From 5719653c806658564729c4371718a55e2c66b78b Mon Sep 17 00:00:00 2001 From: Splunk GitDeploy for Splunk Date: Thu, 26 Mar 2026 23:49:16 +0100 Subject: [PATCH] test_push_configxml Pushed by: admin License: RB02Z2FI66TO (Trial) Timestamp: 2026-03-26T23:49:16.182698 --- apps/bitwarden_event_logs/VERSION | 2 - apps/bitwarden_event_logs/app.manifest | 54 - .../default/collections.conf | 6 - .../views/authentication_events_dashboard.xml | 147 - .../default/data/ui/views/configuration.xml | 3 +- .../views/organization_events_dashboard.xml | 126 - .../data/ui/views/setup_page_dashboard.xml | 13 - .../ui/views/vault_item_events_dashboard.xml | 127 - apps/bitwarden_event_logs/default/inputs.conf | 6 - apps/bitwarden_event_logs/default/macros.conf | 2 - apps/bitwarden_event_logs/default/props.conf | 138 - apps/bitwarden_event_logs/default/script.conf | 4 - apps/bitwarden_event_logs/default/server.conf | 3 - .../lib/PySocks-1.7.1.dist-info/INSTALLER | 1 - .../lib/PySocks-1.7.1.dist-info/LICENSE | 22 - .../lib/PySocks-1.7.1.dist-info/METADATA | 321 - .../lib/PySocks-1.7.1.dist-info/RECORD | 9 - .../lib/PySocks-1.7.1.dist-info/REQUESTED | 0 .../lib/PySocks-1.7.1.dist-info/WHEEL | 5 - .../lib/__pycache__/six.cpython-39.pyc | Bin 27564 -> 0 bytes .../lib/__pycache__/socks.cpython-39.pyc | Bin 21388 -> 0 bytes apps/bitwarden_event_logs/lib/bin/normalizer | 6 - .../certifi-2025.11.12.dist-info/INSTALLER | 1 - .../lib/certifi-2025.11.12.dist-info/METADATA | 78 - .../lib/certifi-2025.11.12.dist-info/RECORD | 12 - .../certifi-2025.11.12.dist-info/REQUESTED | 0 .../lib/certifi-2025.11.12.dist-info/WHEEL | 5 - .../licenses/LICENSE | 20 - .../lib/certifi/__init__.py | 4 - .../lib/certifi/__main__.py | 12 - .../__pycache__/__init__.cpython-39.pyc | Bin 276 -> 0 bytes .../certifi/__pycache__/core.cpython-39.pyc | Bin 1385 -> 0 bytes .../lib/certifi/cacert.pem | 4678 --------- apps/bitwarden_event_logs/lib/certifi/core.py | 83 - .../bitwarden_event_logs/lib/certifi/py.typed | 0 .../INSTALLER | 1 - .../METADATA | 764 -- .../charset_normalizer-3.4.4.dist-info/RECORD | 24 - .../REQUESTED | 0 .../charset_normalizer-3.4.4.dist-info/WHEEL | 7 - .../licenses/LICENSE | 21 - .../lib/charset_normalizer/__init__.py | 48 - .../lib/charset_normalizer/__main__.py | 6 - .../__pycache__/__init__.cpython-39.pyc | Bin 1618 -> 0 bytes .../__pycache__/api.cpython-39.pyc | Bin 11637 -> 0 bytes .../__pycache__/cd.cpython-39.pyc | Bin 9605 -> 0 bytes .../__pycache__/constant.cpython-39.pyc | Bin 27335 -> 0 bytes .../__pycache__/legacy.cpython-39.pyc | Bin 2258 -> 0 bytes .../__pycache__/md.cpython-39.pyc | Bin 16835 -> 0 bytes .../__pycache__/models.cpython-39.pyc | Bin 11958 -> 0 bytes .../__pycache__/utils.cpython-39.pyc | Bin 9110 -> 0 bytes .../__pycache__/version.cpython-39.pyc | Bin 309 -> 0 bytes .../lib/charset_normalizer/api.py | 669 -- .../lib/charset_normalizer/cd.py | 395 - .../lib/charset_normalizer/cli/__init__.py | 8 - .../lib/charset_normalizer/cli/__main__.py | 381 - .../lib/charset_normalizer/constant.py | 2015 ---- .../lib/charset_normalizer/legacy.py | 80 - .../md.cpython-39-aarch64-linux-gnu.so | Bin 201304 -> 0 bytes .../lib/charset_normalizer/md.py | 635 -- .../md__mypyc.cpython-39-aarch64-linux-gnu.so | Bin 324120 -> 0 bytes .../lib/charset_normalizer/models.py | 360 - .../lib/charset_normalizer/py.typed | 0 .../lib/charset_normalizer/utils.py | 414 - .../lib/charset_normalizer/version.py | 8 - .../lib/dateutil/__init__.py | 24 - .../__pycache__/__init__.cpython-39.pyc | Bin 931 -> 0 bytes .../__pycache__/_common.cpython-39.pyc | Bin 1413 -> 0 bytes .../__pycache__/_version.cpython-39.pyc | Bin 265 -> 0 bytes .../__pycache__/relativedelta.cpython-39.pyc | Bin 14792 -> 0 bytes .../lib/dateutil/_common.py | 43 - .../lib/dateutil/_version.py | 4 - .../lib/dateutil/easter.py | 89 - .../lib/dateutil/parser/__init__.py | 61 - .../__pycache__/__init__.cpython-39.pyc | Bin 2057 -> 0 bytes .../parser/__pycache__/_parser.cpython-39.pyc | Bin 40552 -> 0 bytes .../__pycache__/isoparser.cpython-39.pyc | Bin 11288 -> 0 bytes .../lib/dateutil/parser/_parser.py | 1613 --- .../lib/dateutil/parser/isoparser.py | 416 - .../lib/dateutil/relativedelta.py | 599 -- .../lib/dateutil/rrule.py | 1737 ---- .../lib/dateutil/tz/__init__.py | 12 - .../tz/__pycache__/__init__.cpython-39.pyc | Bin 655 -> 0 bytes .../tz/__pycache__/_common.cpython-39.pyc | Bin 10751 -> 0 bytes .../tz/__pycache__/_factories.cpython-39.pyc | Bin 2904 -> 0 bytes .../dateutil/tz/__pycache__/tz.cpython-39.pyc | Bin 44914 -> 0 bytes .../tz/__pycache__/win.cpython-39.pyc | Bin 11393 -> 0 bytes .../lib/dateutil/tz/_common.py | 419 - .../lib/dateutil/tz/_factories.py | 80 - .../lib/dateutil/tz/tz.py | 1849 ---- .../lib/dateutil/tz/win.py | 370 - .../lib/dateutil/tzwin.py | 2 - .../lib/dateutil/utils.py | 71 - .../lib/dateutil/zoneinfo/__init__.py | 167 - .../lib/dateutil/zoneinfo/rebuild.py | 75 - .../lib/defusedxml-0.7.1.dist-info/INSTALLER | 1 - .../lib/defusedxml-0.7.1.dist-info/LICENSE | 49 - .../lib/defusedxml-0.7.1.dist-info/METADATA | 978 -- .../lib/defusedxml-0.7.1.dist-info/RECORD | 18 - .../lib/defusedxml-0.7.1.dist-info/REQUESTED | 0 .../lib/defusedxml-0.7.1.dist-info/WHEEL | 6 - .../lib/defusedxml/ElementTree.py | 154 - .../lib/defusedxml/__init__.py | 67 - .../lib/defusedxml/cElementTree.py | 62 - .../lib/defusedxml/common.py | 129 - .../lib/defusedxml/expatbuilder.py | 107 - .../lib/defusedxml/expatreader.py | 61 - .../lib/defusedxml/lxml.py | 153 - .../lib/defusedxml/minidom.py | 63 - .../lib/defusedxml/pulldom.py | 41 - .../lib/defusedxml/sax.py | 60 - .../lib/defusedxml/xmlrpc.py | 153 - .../lib/deprecation-2.1.0.dist-info/INSTALLER | 1 - .../lib/deprecation-2.1.0.dist-info/LICENSE | 201 - .../lib/deprecation-2.1.0.dist-info/METADATA | 114 - .../lib/deprecation-2.1.0.dist-info/RECORD | 8 - .../lib/deprecation-2.1.0.dist-info/REQUESTED | 0 .../lib/deprecation-2.1.0.dist-info/WHEEL | 6 - apps/bitwarden_event_logs/lib/deprecation.py | 290 - .../lib/idna-3.11.dist-info/INSTALLER | 1 - .../lib/idna-3.11.dist-info/METADATA | 209 - .../lib/idna-3.11.dist-info/RECORD | 15 - .../lib/idna-3.11.dist-info/REQUESTED | 0 .../lib/idna-3.11.dist-info/WHEEL | 4 - .../idna-3.11.dist-info/licenses/LICENSE.md | 31 - .../bitwarden_event_logs/lib/idna/__init__.py | 45 - .../idna/__pycache__/__init__.cpython-39.pyc | Bin 824 -> 0 bytes .../lib/idna/__pycache__/core.cpython-39.pyc | Bin 9849 -> 0 bytes .../idna/__pycache__/idnadata.cpython-39.pyc | Bin 42757 -> 0 bytes .../idna/__pycache__/intranges.cpython-39.pyc | Bin 1969 -> 0 bytes .../__pycache__/package_data.cpython-39.pyc | Bin 184 -> 0 bytes apps/bitwarden_event_logs/lib/idna/codec.py | 122 - apps/bitwarden_event_logs/lib/idna/compat.py | 15 - apps/bitwarden_event_logs/lib/idna/core.py | 437 - .../bitwarden_event_logs/lib/idna/idnadata.py | 4309 -------- .../lib/idna/intranges.py | 57 - .../lib/idna/package_data.py | 1 - apps/bitwarden_event_logs/lib/idna/py.typed | 0 .../lib/idna/uts46data.py | 8841 ----------------- .../lib/packaging-26.0.dist-info/INSTALLER | 1 - .../lib/packaging-26.0.dist-info/METADATA | 107 - .../lib/packaging-26.0.dist-info/RECORD | 26 - .../lib/packaging-26.0.dist-info/REQUESTED | 0 .../lib/packaging-26.0.dist-info/WHEEL | 4 - .../packaging-26.0.dist-info/licenses/LICENSE | 3 - .../licenses/LICENSE.APACHE | 177 - .../licenses/LICENSE.BSD | 23 - .../lib/packaging/__init__.py | 15 - .../lib/packaging/_elffile.py | 108 - .../lib/packaging/_manylinux.py | 262 - .../lib/packaging/_musllinux.py | 85 - .../lib/packaging/_parser.py | 365 - .../lib/packaging/_structures.py | 69 - .../lib/packaging/_tokenizer.py | 193 - .../lib/packaging/licenses/__init__.py | 147 - .../lib/packaging/licenses/_spdx.py | 799 -- .../lib/packaging/markers.py | 388 - .../lib/packaging/metadata.py | 978 -- .../lib/packaging/py.typed | 0 .../lib/packaging/pylock.py | 635 -- .../lib/packaging/requirements.py | 86 - .../lib/packaging/specifiers.py | 1068 -- .../lib/packaging/tags.py | 651 -- .../lib/packaging/utils.py | 158 - .../lib/packaging/version.py | 792 -- .../INSTALLER | 1 - .../LICENSE | 54 - .../METADATA | 204 - .../RECORD | 27 - .../REQUESTED | 0 .../WHEEL | 6 - .../zip-safe | 1 - .../lib/requests-2.32.5.dist-info/INSTALLER | 1 - .../lib/requests-2.32.5.dist-info/METADATA | 133 - .../lib/requests-2.32.5.dist-info/RECORD | 25 - .../lib/requests-2.32.5.dist-info/REQUESTED | 0 .../lib/requests-2.32.5.dist-info/WHEEL | 5 - .../licenses/LICENSE | 175 - .../lib/requests/__init__.py | 184 - .../__pycache__/__init__.cpython-39.pyc | Bin 3888 -> 0 bytes .../__pycache__/__version__.cpython-39.pyc | Bin 522 -> 0 bytes .../_internal_utils.cpython-39.pyc | Bin 1597 -> 0 bytes .../__pycache__/adapters.cpython-39.pyc | Bin 21654 -> 0 bytes .../requests/__pycache__/api.cpython-39.pyc | Bin 6763 -> 0 bytes .../requests/__pycache__/auth.cpython-39.pyc | Bin 8328 -> 0 bytes .../requests/__pycache__/certs.cpython-39.pyc | Bin 593 -> 0 bytes .../__pycache__/compat.cpython-39.pyc | Bin 1922 -> 0 bytes .../__pycache__/cookies.cpython-39.pyc | Bin 18820 -> 0 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 6591 -> 0 bytes .../requests/__pycache__/hooks.cpython-39.pyc | Bin 962 -> 0 bytes .../__pycache__/models.cpython-39.pyc | Bin 24331 -> 0 bytes .../__pycache__/packages.cpython-39.pyc | Bin 589 -> 0 bytes .../__pycache__/sessions.cpython-39.pyc | Bin 19761 -> 0 bytes .../__pycache__/status_codes.cpython-39.pyc | Bin 4278 -> 0 bytes .../__pycache__/structures.cpython-39.pyc | Bin 4419 -> 0 bytes .../requests/__pycache__/utils.cpython-39.pyc | Bin 24390 -> 0 bytes .../lib/requests/__version__.py | 14 - .../lib/requests/_internal_utils.py | 50 - .../lib/requests/adapters.py | 696 -- apps/bitwarden_event_logs/lib/requests/api.py | 157 - .../bitwarden_event_logs/lib/requests/auth.py | 314 - .../lib/requests/certs.py | 17 - .../lib/requests/compat.py | 106 - .../lib/requests/cookies.py | 561 -- .../lib/requests/exceptions.py | 151 - .../bitwarden_event_logs/lib/requests/help.py | 134 - .../lib/requests/hooks.py | 33 - .../lib/requests/models.py | 1039 -- .../lib/requests/packages.py | 23 - .../lib/requests/sessions.py | 831 -- .../lib/requests/status_codes.py | 128 - .../lib/requests/structures.py | 99 - .../lib/requests/utils.py | 1086 -- .../lib/six-1.17.0.dist-info/INSTALLER | 1 - .../lib/six-1.17.0.dist-info/LICENSE | 18 - .../lib/six-1.17.0.dist-info/METADATA | 43 - .../lib/six-1.17.0.dist-info/RECORD | 8 - .../lib/six-1.17.0.dist-info/REQUESTED | 0 .../lib/six-1.17.0.dist-info/WHEEL | 6 - apps/bitwarden_event_logs/lib/six.py | 1003 -- apps/bitwarden_event_logs/lib/socks.py | 847 -- apps/bitwarden_event_logs/lib/sockshandler.py | 111 - .../lib/solnlib-7.0.0.dist-info/INSTALLER | 1 - .../lib/solnlib-7.0.0.dist-info/LICENSE | 201 - .../lib/solnlib-7.0.0.dist-info/METADATA | 28 - .../lib/solnlib-7.0.0.dist-info/RECORD | 40 - .../lib/solnlib-7.0.0.dist-info/REQUESTED | 0 .../lib/solnlib-7.0.0.dist-info/WHEEL | 4 - .../lib/solnlib/__init__.py | 59 - .../__pycache__/__init__.cpython-39.pyc | Bin 661 -> 0 bytes .../__pycache__/_settings.cpython-39.pyc | Bin 267 -> 0 bytes .../solnlib/__pycache__/_utils.cpython-39.pyc | Bin 2212 -> 0 bytes .../solnlib/__pycache__/acl.cpython-39.pyc | Bin 5123 -> 0 bytes .../bulletin_rest_client.cpython-39.pyc | Bin 5829 -> 0 bytes .../__pycache__/conf_manager.cpython-39.pyc | Bin 16546 -> 0 bytes .../__pycache__/credentials.cpython-39.pyc | Bin 9601 -> 0 bytes .../__pycache__/file_monitor.cpython-39.pyc | Bin 3908 -> 0 bytes .../__pycache__/hec_config.cpython-39.pyc | Bin 4956 -> 0 bytes .../solnlib/__pycache__/log.cpython-39.pyc | Bin 10353 -> 0 bytes .../__pycache__/net_utils.cpython-39.pyc | Bin 3483 -> 0 bytes .../orphan_process_monitor.cpython-39.pyc | Bin 3272 -> 0 bytes .../__pycache__/pattern.cpython-39.pyc | Bin 998 -> 0 bytes .../__pycache__/server_info.cpython-39.pyc | Bin 8744 -> 0 bytes .../soln_exceptions.cpython-39.pyc | Bin 998 -> 0 bytes .../splunk_rest_client.cpython-39.pyc | Bin 6143 -> 0 bytes .../__pycache__/splunkenv.cpython-39.pyc | Bin 11001 -> 0 bytes .../__pycache__/time_parser.cpython-39.pyc | Bin 4485 -> 0 bytes .../__pycache__/timer_queue.cpython-39.pyc | Bin 9983 -> 0 bytes .../__pycache__/user_access.cpython-39.pyc | Bin 25975 -> 0 bytes .../solnlib/__pycache__/utils.cpython-39.pyc | Bin 5390 -> 0 bytes .../lib/solnlib/_settings.py | 18 - .../lib/solnlib/_utils.py | 79 - apps/bitwarden_event_logs/lib/solnlib/acl.py | 182 - .../lib/solnlib/alerts_rest_client.py | 258 - .../lib/solnlib/bulletin_rest_client.py | 151 - .../solnlib/concurrent/concurrent_executor.py | 102 - .../lib/solnlib/concurrent/process_pool.py | 75 - .../lib/solnlib/concurrent/thread_pool.py | 347 - .../lib/solnlib/conf_manager.py | 617 -- .../lib/solnlib/credentials.py | 337 - .../lib/solnlib/file_monitor.py | 133 - .../lib/solnlib/hec_config.py | 182 - apps/bitwarden_event_logs/lib/solnlib/log.py | 373 - .../lib/solnlib/modular_input/__init__.py | 38 - .../lib/solnlib/modular_input/checkpointer.py | 263 - .../lib/solnlib/modular_input/event.py | 255 - .../lib/solnlib/modular_input/event_writer.py | 510 - .../lib/solnlib/modular_input/modinput.py | 161 - .../solnlib/modular_input/modular_input.py | 509 - .../lib/solnlib/net_utils.py | 155 - .../lib/solnlib/orphan_process_monitor.py | 120 - .../lib/solnlib/pattern.py | 40 - apps/bitwarden_event_logs/lib/solnlib/rest.py | 95 - .../lib/solnlib/schedule/job.py | 122 - .../lib/solnlib/schedule/scheduler.py | 162 - .../lib/solnlib/server_info.py | 308 - .../lib/solnlib/soln_exceptions.py | 37 - .../lib/solnlib/splunk_rest_client.py | 245 - .../lib/solnlib/splunkenv.py | 414 - .../lib/solnlib/time_parser.py | 151 - .../lib/solnlib/timer_queue.py | 324 - .../lib/solnlib/user_access.py | 949 -- .../bitwarden_event_logs/lib/solnlib/utils.py | 221 - .../INSTALLER | 1 - .../sortedcontainers-2.4.0.dist-info/LICENSE | 13 - .../sortedcontainers-2.4.0.dist-info/METADATA | 264 - .../sortedcontainers-2.4.0.dist-info/RECORD | 11 - .../REQUESTED | 0 .../sortedcontainers-2.4.0.dist-info/WHEEL | 6 - .../lib/sortedcontainers/__init__.py | 74 - .../__pycache__/__init__.cpython-39.pyc | Bin 2266 -> 0 bytes .../__pycache__/sorteddict.cpython-39.pyc | Bin 23129 -> 0 bytes .../__pycache__/sortedlist.cpython-39.pyc | Bin 60403 -> 0 bytes .../__pycache__/sortedset.cpython-39.pyc | Bin 19739 -> 0 bytes .../lib/sortedcontainers/sorteddict.py | 812 -- .../lib/sortedcontainers/sortedlist.py | 2646 ----- .../lib/sortedcontainers/sortedset.py | 733 -- .../lib/splunk_sdk-2.1.1.dist-info/INSTALLER | 1 - .../lib/splunk_sdk-2.1.1.dist-info/METADATA | 24 - .../lib/splunk_sdk-2.1.1.dist-info/RECORD | 33 - .../lib/splunk_sdk-2.1.1.dist-info/REQUESTED | 0 .../lib/splunk_sdk-2.1.1.dist-info/WHEEL | 5 - .../lib/splunklib/__init__.py | 36 - .../__pycache__/__init__.cpython-39.pyc | Bin 712 -> 0 bytes .../__pycache__/binding.cpython-39.pyc | Bin 52842 -> 0 bytes .../__pycache__/client.cpython-39.pyc | Bin 140621 -> 0 bytes .../splunklib/__pycache__/data.cpython-39.pyc | Bin 7051 -> 0 bytes .../lib/splunklib/binding.py | 1577 --- .../lib/splunklib/client.py | 4156 -------- .../lib/splunklib/data.py | 281 - .../lib/splunklib/modularinput/__init__.py | 13 - .../lib/splunklib/modularinput/argument.py | 108 - .../lib/splunklib/modularinput/event.py | 124 - .../splunklib/modularinput/event_writer.py | 104 - .../modularinput/input_definition.py | 57 - .../lib/splunklib/modularinput/scheme.py | 82 - .../lib/splunklib/modularinput/script.py | 170 - .../lib/splunklib/modularinput/utils.py | 78 - .../modularinput/validation_definition.py | 83 - .../lib/splunklib/results.py | 341 - .../lib/splunklib/searchcommands/__init__.py | 157 - .../splunklib/searchcommands/decorators.py | 483 - .../splunklib/searchcommands/environment.py | 132 - .../searchcommands/eventing_command.py | 160 - .../searchcommands/external_search_command.py | 253 - .../searchcommands/generating_command.py | 410 - .../lib/splunklib/searchcommands/internals.py | 835 -- .../searchcommands/reporting_command.py | 313 - .../searchcommands/search_command.py | 1263 --- .../searchcommands/streaming_command.py | 220 - .../splunklib/searchcommands/validators.py | 461 - .../bitwarden_event_logs/lib/splunklib/six.py | 1065 -- .../lib/splunklib/utils.py | 47 - .../splunktaucclib-8.0.0.dist-info/INSTALLER | 1 - .../splunktaucclib-8.0.0.dist-info/LICENSE | 201 - .../splunktaucclib-8.0.0.dist-info/METADATA | 20 - .../lib/splunktaucclib-8.0.0.dist-info/RECORD | 43 - .../splunktaucclib-8.0.0.dist-info/REQUESTED | 0 .../lib/splunktaucclib-8.0.0.dist-info/WHEEL | 4 - .../lib/splunktaucclib/__init__.py | 17 - .../lib/splunktaucclib/alert_actions_base.py | 220 - .../lib/splunktaucclib/cim_actions.py | 1038 -- .../lib/splunktaucclib/common/__init__.py | 67 - .../lib/splunktaucclib/common/log.py | 53 - .../splunktaucclib/common/xml_dom_parser.py | 63 - .../lib/splunktaucclib/config.py | 421 - .../splunktaucclib/global_config/__init__.py | 80 - .../global_config/configuration.py | 389 - .../splunktaucclib/global_config/schema.py | 86 - .../lib/splunktaucclib/legacy/__init__.py | 15 - .../lib/splunktaucclib/legacy/credentials.py | 389 - .../lib/splunktaucclib/legacy/rest.py | 98 - .../lib/splunktaucclib/legacy/util.py | 144 - .../modinput_wrapper/__init__.py | 15 - .../modinput_wrapper/base_modinput.py | 591 -- .../splunktaucclib/rest_handler/__init__.py | 19 - .../rest_handler/admin_external.py | 222 - .../rest_handler/base_hook_mixin.py | 48 - .../rest_handler/credentials.py | 453 - .../lib/splunktaucclib/rest_handler/eai.py | 73 - .../rest_handler/endpoint/__init__.py | 219 - .../rest_handler/endpoint/converter.py | 312 - .../rest_handler/endpoint/field.py | 70 - .../rest_handler/endpoint/validator.py | 485 - .../lib/splunktaucclib/rest_handler/entity.py | 32 - .../lib/splunktaucclib/rest_handler/error.py | 60 - .../splunktaucclib/rest_handler/error_ctl.py | 158 - .../splunktaucclib/rest_handler/handler.py | 526 - .../splunktaucclib/rest_handler/normaliser.py | 109 - .../lib/splunktaucclib/rest_handler/schema.py | 56 - .../lib/splunktaucclib/rest_handler/util.py | 154 - .../splunktaucclib/splunk_aoblib/__init__.py | 15 - .../splunk_aoblib/rest_helper.py | 68 - .../splunk_aoblib/rest_migration.py | 227 - .../splunk_aoblib/setup_util.py | 397 - .../splunktaucclib/splunk_aoblib/utility.py | 35 - .../lib/urllib3-1.26.20.dist-info/INSTALLER | 1 - .../lib/urllib3-1.26.20.dist-info/METADATA | 1517 --- .../lib/urllib3-1.26.20.dist-info/RECORD | 46 - .../lib/urllib3-1.26.20.dist-info/REQUESTED | 0 .../lib/urllib3-1.26.20.dist-info/WHEEL | 6 - .../lib/urllib3/__init__.py | 102 - .../__pycache__/__init__.cpython-39.pyc | Bin 2479 -> 0 bytes .../__pycache__/_collections.cpython-39.pyc | Bin 11247 -> 0 bytes .../__pycache__/_version.cpython-39.pyc | Bin 186 -> 0 bytes .../__pycache__/connection.cpython-39.pyc | Bin 13715 -> 0 bytes .../__pycache__/connectionpool.cpython-39.pyc | Bin 25915 -> 0 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 11618 -> 0 bytes .../urllib3/__pycache__/fields.cpython-39.pyc | Bin 8133 -> 0 bytes .../__pycache__/filepost.cpython-39.pyc | Bin 2734 -> 0 bytes .../__pycache__/poolmanager.cpython-39.pyc | Bin 15100 -> 0 bytes .../__pycache__/request.cpython-39.pyc | Bin 6358 -> 0 bytes .../__pycache__/response.cpython-39.pyc | Bin 22466 -> 0 bytes .../lib/urllib3/_collections.py | 355 - .../lib/urllib3/_version.py | 2 - .../lib/urllib3/connection.py | 572 -- .../lib/urllib3/connectionpool.py | 1140 --- .../lib/urllib3/contrib/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 171 -> 0 bytes .../_appengine_environ.cpython-39.pyc | Bin 1391 -> 0 bytes .../contrib/__pycache__/socks.cpython-39.pyc | Bin 5607 -> 0 bytes .../lib/urllib3/contrib/_appengine_environ.py | 36 - .../contrib/_securetransport/__init__.py | 0 .../contrib/_securetransport/bindings.py | 519 - .../contrib/_securetransport/low_level.py | 397 - .../lib/urllib3/contrib/appengine.py | 314 - .../lib/urllib3/contrib/ntlmpool.py | 130 - .../lib/urllib3/contrib/pyopenssl.py | 518 - .../lib/urllib3/contrib/securetransport.py | 920 -- .../lib/urllib3/contrib/socks.py | 216 - .../lib/urllib3/exceptions.py | 323 - .../lib/urllib3/fields.py | 274 - .../lib/urllib3/filepost.py | 98 - .../lib/urllib3/packages/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 172 -> 0 bytes .../packages/__pycache__/six.cpython-39.pyc | Bin 27555 -> 0 bytes .../urllib3/packages/backports/__init__.py | 0 .../urllib3/packages/backports/makefile.py | 51 - .../packages/backports/weakref_finalize.py | 155 - .../lib/urllib3/packages/six.py | 1076 -- .../lib/urllib3/poolmanager.py | 540 - .../lib/urllib3/request.py | 191 - .../lib/urllib3/response.py | 885 -- .../lib/urllib3/util/__init__.py | 49 - .../util/__pycache__/__init__.cpython-39.pyc | Bin 1081 -> 0 bytes .../__pycache__/connection.cpython-39.pyc | Bin 3413 -> 0 bytes .../util/__pycache__/proxy.cpython-39.pyc | Bin 1317 -> 0 bytes .../util/__pycache__/queue.cpython-39.pyc | Bin 1036 -> 0 bytes .../util/__pycache__/request.cpython-39.pyc | Bin 3474 -> 0 bytes .../util/__pycache__/response.cpython-39.pyc | Bin 2321 -> 0 bytes .../util/__pycache__/retry.cpython-39.pyc | Bin 16270 -> 0 bytes .../util/__pycache__/ssl_.cpython-39.pyc | Bin 11531 -> 0 bytes .../ssl_match_hostname.cpython-39.pyc | Bin 3235 -> 0 bytes .../__pycache__/ssltransport.cpython-39.pyc | Bin 7451 -> 0 bytes .../util/__pycache__/timeout.cpython-39.pyc | Bin 9122 -> 0 bytes .../util/__pycache__/url.cpython-39.pyc | Bin 10651 -> 0 bytes .../util/__pycache__/wait.cpython-39.pyc | Bin 3104 -> 0 bytes .../lib/urllib3/util/connection.py | 149 - .../lib/urllib3/util/proxy.py | 57 - .../lib/urllib3/util/queue.py | 22 - .../lib/urllib3/util/request.py | 146 - .../lib/urllib3/util/response.py | 107 - .../lib/urllib3/util/retry.py | 622 -- .../lib/urllib3/util/ssl_.py | 504 - .../lib/urllib3/util/ssl_match_hostname.py | 159 - .../lib/urllib3/util/ssltransport.py | 221 - .../lib/urllib3/util/timeout.py | 271 - .../lib/urllib3/util/url.py | 435 - .../lib/urllib3/util/wait.py | 152 - apps/bitwarden_event_logs/local/inputs.conf | 1 - .../bitwarden_event_logs/local/passwords.conf | 2 - apps/bitwarden_event_logs/local/script.conf | 4 - apps/bitwarden_event_logs/test.conf | 0 453 files changed, 1 insertion(+), 98471 deletions(-) delete mode 100755 apps/bitwarden_event_logs/VERSION delete mode 100755 apps/bitwarden_event_logs/app.manifest delete mode 100755 apps/bitwarden_event_logs/default/collections.conf delete mode 100755 apps/bitwarden_event_logs/default/data/ui/views/authentication_events_dashboard.xml delete mode 100755 apps/bitwarden_event_logs/default/data/ui/views/organization_events_dashboard.xml delete mode 100755 apps/bitwarden_event_logs/default/data/ui/views/setup_page_dashboard.xml delete mode 100755 apps/bitwarden_event_logs/default/data/ui/views/vault_item_events_dashboard.xml delete mode 100755 apps/bitwarden_event_logs/default/inputs.conf delete mode 100755 apps/bitwarden_event_logs/default/macros.conf delete mode 100755 apps/bitwarden_event_logs/default/props.conf delete mode 100755 apps/bitwarden_event_logs/default/script.conf delete mode 100755 apps/bitwarden_event_logs/default/server.conf delete mode 100755 apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/INSTALLER delete mode 100755 apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/LICENSE delete mode 100755 apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/METADATA delete mode 100755 apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/RECORD delete mode 100755 apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/REQUESTED delete mode 100755 apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/WHEEL delete mode 100755 apps/bitwarden_event_logs/lib/__pycache__/six.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/__pycache__/socks.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/bin/normalizer delete mode 100755 apps/bitwarden_event_logs/lib/certifi-2025.11.12.dist-info/INSTALLER delete mode 100755 apps/bitwarden_event_logs/lib/certifi-2025.11.12.dist-info/METADATA delete mode 100755 apps/bitwarden_event_logs/lib/certifi-2025.11.12.dist-info/RECORD delete mode 100755 apps/bitwarden_event_logs/lib/certifi-2025.11.12.dist-info/REQUESTED delete mode 100755 apps/bitwarden_event_logs/lib/certifi-2025.11.12.dist-info/WHEEL delete mode 100755 apps/bitwarden_event_logs/lib/certifi-2025.11.12.dist-info/licenses/LICENSE delete mode 100755 apps/bitwarden_event_logs/lib/certifi/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/certifi/__main__.py delete mode 100755 apps/bitwarden_event_logs/lib/certifi/__pycache__/__init__.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/certifi/__pycache__/core.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/certifi/cacert.pem delete mode 100755 apps/bitwarden_event_logs/lib/certifi/core.py delete mode 100755 apps/bitwarden_event_logs/lib/certifi/py.typed delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/INSTALLER delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/METADATA delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/RECORD delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/REQUESTED delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/WHEEL delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/licenses/LICENSE delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/__main__.py delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/__pycache__/__init__.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/__pycache__/api.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/__pycache__/cd.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/__pycache__/constant.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/__pycache__/legacy.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/__pycache__/md.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/__pycache__/models.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/__pycache__/utils.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/__pycache__/version.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/api.py delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/cd.py delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/cli/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/cli/__main__.py delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/constant.py delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/legacy.py delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/md.cpython-39-aarch64-linux-gnu.so delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/md.py delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/md__mypyc.cpython-39-aarch64-linux-gnu.so delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/models.py delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/py.typed delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/utils.py delete mode 100755 apps/bitwarden_event_logs/lib/charset_normalizer/version.py delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/__pycache__/__init__.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/__pycache__/_common.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/__pycache__/_version.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/__pycache__/relativedelta.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/_common.py delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/_version.py delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/easter.py delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/parser/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/parser/__pycache__/__init__.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/parser/__pycache__/_parser.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/parser/__pycache__/isoparser.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/parser/_parser.py delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/parser/isoparser.py delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/relativedelta.py delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/rrule.py delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/tz/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/tz/__pycache__/__init__.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/tz/__pycache__/_common.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/tz/__pycache__/_factories.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/tz/__pycache__/tz.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/tz/__pycache__/win.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/tz/_common.py delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/tz/_factories.py delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/tz/tz.py delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/tz/win.py delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/tzwin.py delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/utils.py delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/zoneinfo/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/dateutil/zoneinfo/rebuild.py delete mode 100755 apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/INSTALLER delete mode 100755 apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/LICENSE delete mode 100755 apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/METADATA delete mode 100755 apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/RECORD delete mode 100755 apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/REQUESTED delete mode 100755 apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/WHEEL delete mode 100755 apps/bitwarden_event_logs/lib/defusedxml/ElementTree.py delete mode 100755 apps/bitwarden_event_logs/lib/defusedxml/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/defusedxml/cElementTree.py delete mode 100755 apps/bitwarden_event_logs/lib/defusedxml/common.py delete mode 100755 apps/bitwarden_event_logs/lib/defusedxml/expatbuilder.py delete mode 100755 apps/bitwarden_event_logs/lib/defusedxml/expatreader.py delete mode 100755 apps/bitwarden_event_logs/lib/defusedxml/lxml.py delete mode 100755 apps/bitwarden_event_logs/lib/defusedxml/minidom.py delete mode 100755 apps/bitwarden_event_logs/lib/defusedxml/pulldom.py delete mode 100755 apps/bitwarden_event_logs/lib/defusedxml/sax.py delete mode 100755 apps/bitwarden_event_logs/lib/defusedxml/xmlrpc.py delete mode 100755 apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/INSTALLER delete mode 100755 apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/LICENSE delete mode 100755 apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/METADATA delete mode 100755 apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/RECORD delete mode 100755 apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/REQUESTED delete mode 100755 apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/WHEEL delete mode 100755 apps/bitwarden_event_logs/lib/deprecation.py delete mode 100755 apps/bitwarden_event_logs/lib/idna-3.11.dist-info/INSTALLER delete mode 100755 apps/bitwarden_event_logs/lib/idna-3.11.dist-info/METADATA delete mode 100755 apps/bitwarden_event_logs/lib/idna-3.11.dist-info/RECORD delete mode 100755 apps/bitwarden_event_logs/lib/idna-3.11.dist-info/REQUESTED delete mode 100755 apps/bitwarden_event_logs/lib/idna-3.11.dist-info/WHEEL delete mode 100755 apps/bitwarden_event_logs/lib/idna-3.11.dist-info/licenses/LICENSE.md delete mode 100755 apps/bitwarden_event_logs/lib/idna/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/idna/__pycache__/__init__.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/idna/__pycache__/core.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/idna/__pycache__/idnadata.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/idna/__pycache__/intranges.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/idna/__pycache__/package_data.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/idna/codec.py delete mode 100755 apps/bitwarden_event_logs/lib/idna/compat.py delete mode 100755 apps/bitwarden_event_logs/lib/idna/core.py delete mode 100755 apps/bitwarden_event_logs/lib/idna/idnadata.py delete mode 100755 apps/bitwarden_event_logs/lib/idna/intranges.py delete mode 100755 apps/bitwarden_event_logs/lib/idna/package_data.py delete mode 100755 apps/bitwarden_event_logs/lib/idna/py.typed delete mode 100755 apps/bitwarden_event_logs/lib/idna/uts46data.py delete mode 100755 apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/INSTALLER delete mode 100755 apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/METADATA delete mode 100755 apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/RECORD delete mode 100755 apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/REQUESTED delete mode 100755 apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/WHEEL delete mode 100755 apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/licenses/LICENSE delete mode 100755 apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/licenses/LICENSE.APACHE delete mode 100755 apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/licenses/LICENSE.BSD delete mode 100755 apps/bitwarden_event_logs/lib/packaging/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/packaging/_elffile.py delete mode 100755 apps/bitwarden_event_logs/lib/packaging/_manylinux.py delete mode 100755 apps/bitwarden_event_logs/lib/packaging/_musllinux.py delete mode 100755 apps/bitwarden_event_logs/lib/packaging/_parser.py delete mode 100755 apps/bitwarden_event_logs/lib/packaging/_structures.py delete mode 100755 apps/bitwarden_event_logs/lib/packaging/_tokenizer.py delete mode 100755 apps/bitwarden_event_logs/lib/packaging/licenses/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/packaging/licenses/_spdx.py delete mode 100755 apps/bitwarden_event_logs/lib/packaging/markers.py delete mode 100755 apps/bitwarden_event_logs/lib/packaging/metadata.py delete mode 100755 apps/bitwarden_event_logs/lib/packaging/py.typed delete mode 100755 apps/bitwarden_event_logs/lib/packaging/pylock.py delete mode 100755 apps/bitwarden_event_logs/lib/packaging/requirements.py delete mode 100755 apps/bitwarden_event_logs/lib/packaging/specifiers.py delete mode 100755 apps/bitwarden_event_logs/lib/packaging/tags.py delete mode 100755 apps/bitwarden_event_logs/lib/packaging/utils.py delete mode 100755 apps/bitwarden_event_logs/lib/packaging/version.py delete mode 100755 apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/INSTALLER delete mode 100755 apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/LICENSE delete mode 100755 apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/METADATA delete mode 100755 apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/RECORD delete mode 100755 apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/REQUESTED delete mode 100755 apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/WHEEL delete mode 100755 apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/zip-safe delete mode 100755 apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/INSTALLER delete mode 100755 apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/METADATA delete mode 100755 apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/RECORD delete mode 100755 apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/REQUESTED delete mode 100755 apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/WHEEL delete mode 100755 apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/licenses/LICENSE delete mode 100755 apps/bitwarden_event_logs/lib/requests/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/requests/__pycache__/__init__.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/requests/__pycache__/__version__.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/requests/__pycache__/_internal_utils.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/requests/__pycache__/adapters.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/requests/__pycache__/api.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/requests/__pycache__/auth.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/requests/__pycache__/certs.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/requests/__pycache__/compat.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/requests/__pycache__/cookies.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/requests/__pycache__/exceptions.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/requests/__pycache__/hooks.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/requests/__pycache__/models.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/requests/__pycache__/packages.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/requests/__pycache__/sessions.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/requests/__pycache__/status_codes.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/requests/__pycache__/structures.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/requests/__pycache__/utils.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/requests/__version__.py delete mode 100755 apps/bitwarden_event_logs/lib/requests/_internal_utils.py delete mode 100755 apps/bitwarden_event_logs/lib/requests/adapters.py delete mode 100755 apps/bitwarden_event_logs/lib/requests/api.py delete mode 100755 apps/bitwarden_event_logs/lib/requests/auth.py delete mode 100755 apps/bitwarden_event_logs/lib/requests/certs.py delete mode 100755 apps/bitwarden_event_logs/lib/requests/compat.py delete mode 100755 apps/bitwarden_event_logs/lib/requests/cookies.py delete mode 100755 apps/bitwarden_event_logs/lib/requests/exceptions.py delete mode 100755 apps/bitwarden_event_logs/lib/requests/help.py delete mode 100755 apps/bitwarden_event_logs/lib/requests/hooks.py delete mode 100755 apps/bitwarden_event_logs/lib/requests/models.py delete mode 100755 apps/bitwarden_event_logs/lib/requests/packages.py delete mode 100755 apps/bitwarden_event_logs/lib/requests/sessions.py delete mode 100755 apps/bitwarden_event_logs/lib/requests/status_codes.py delete mode 100755 apps/bitwarden_event_logs/lib/requests/structures.py delete mode 100755 apps/bitwarden_event_logs/lib/requests/utils.py delete mode 100755 apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/INSTALLER delete mode 100755 apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/LICENSE delete mode 100755 apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/METADATA delete mode 100755 apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/RECORD delete mode 100755 apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/REQUESTED delete mode 100755 apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/WHEEL delete mode 100755 apps/bitwarden_event_logs/lib/six.py delete mode 100755 apps/bitwarden_event_logs/lib/socks.py delete mode 100755 apps/bitwarden_event_logs/lib/sockshandler.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/INSTALLER delete mode 100755 apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/LICENSE delete mode 100755 apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/METADATA delete mode 100755 apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/RECORD delete mode 100755 apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/REQUESTED delete mode 100755 apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/WHEEL delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__pycache__/__init__.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__pycache__/_settings.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__pycache__/_utils.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__pycache__/acl.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__pycache__/bulletin_rest_client.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__pycache__/conf_manager.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__pycache__/credentials.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__pycache__/file_monitor.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__pycache__/hec_config.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__pycache__/log.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__pycache__/net_utils.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__pycache__/orphan_process_monitor.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__pycache__/pattern.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__pycache__/server_info.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__pycache__/soln_exceptions.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__pycache__/splunk_rest_client.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__pycache__/splunkenv.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__pycache__/time_parser.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__pycache__/timer_queue.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__pycache__/user_access.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/__pycache__/utils.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/_settings.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/_utils.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/acl.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/alerts_rest_client.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/bulletin_rest_client.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/concurrent/concurrent_executor.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/concurrent/process_pool.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/concurrent/thread_pool.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/conf_manager.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/credentials.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/file_monitor.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/hec_config.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/log.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/modular_input/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/modular_input/checkpointer.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/modular_input/event.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/modular_input/event_writer.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/modular_input/modinput.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/modular_input/modular_input.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/net_utils.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/orphan_process_monitor.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/pattern.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/rest.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/schedule/job.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/schedule/scheduler.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/server_info.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/soln_exceptions.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/splunk_rest_client.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/splunkenv.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/time_parser.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/timer_queue.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/user_access.py delete mode 100755 apps/bitwarden_event_logs/lib/solnlib/utils.py delete mode 100755 apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/INSTALLER delete mode 100755 apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/LICENSE delete mode 100755 apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/METADATA delete mode 100755 apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/RECORD delete mode 100755 apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/REQUESTED delete mode 100755 apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/WHEEL delete mode 100755 apps/bitwarden_event_logs/lib/sortedcontainers/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/sortedcontainers/__pycache__/__init__.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/sortedcontainers/__pycache__/sorteddict.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/sortedcontainers/__pycache__/sortedlist.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/sortedcontainers/__pycache__/sortedset.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/sortedcontainers/sorteddict.py delete mode 100755 apps/bitwarden_event_logs/lib/sortedcontainers/sortedlist.py delete mode 100755 apps/bitwarden_event_logs/lib/sortedcontainers/sortedset.py delete mode 100755 apps/bitwarden_event_logs/lib/splunk_sdk-2.1.1.dist-info/INSTALLER delete mode 100755 apps/bitwarden_event_logs/lib/splunk_sdk-2.1.1.dist-info/METADATA delete mode 100755 apps/bitwarden_event_logs/lib/splunk_sdk-2.1.1.dist-info/RECORD delete mode 100755 apps/bitwarden_event_logs/lib/splunk_sdk-2.1.1.dist-info/REQUESTED delete mode 100755 apps/bitwarden_event_logs/lib/splunk_sdk-2.1.1.dist-info/WHEEL delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/__pycache__/__init__.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/__pycache__/binding.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/__pycache__/client.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/__pycache__/data.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/binding.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/client.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/data.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/modularinput/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/modularinput/argument.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/modularinput/event.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/modularinput/event_writer.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/modularinput/input_definition.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/modularinput/scheme.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/modularinput/script.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/modularinput/utils.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/modularinput/validation_definition.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/results.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/searchcommands/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/searchcommands/decorators.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/searchcommands/environment.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/searchcommands/eventing_command.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/searchcommands/external_search_command.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/searchcommands/generating_command.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/searchcommands/internals.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/searchcommands/reporting_command.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/searchcommands/search_command.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/searchcommands/streaming_command.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/searchcommands/validators.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/six.py delete mode 100755 apps/bitwarden_event_logs/lib/splunklib/utils.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib-8.0.0.dist-info/INSTALLER delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib-8.0.0.dist-info/LICENSE delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib-8.0.0.dist-info/METADATA delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib-8.0.0.dist-info/RECORD delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib-8.0.0.dist-info/REQUESTED delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib-8.0.0.dist-info/WHEEL delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/alert_actions_base.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/cim_actions.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/common/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/common/log.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/common/xml_dom_parser.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/config.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/global_config/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/global_config/configuration.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/global_config/schema.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/legacy/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/legacy/credentials.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/legacy/rest.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/legacy/util.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/modinput_wrapper/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/modinput_wrapper/base_modinput.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/rest_handler/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/rest_handler/admin_external.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/rest_handler/base_hook_mixin.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/rest_handler/credentials.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/rest_handler/eai.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/rest_handler/endpoint/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/rest_handler/endpoint/converter.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/rest_handler/endpoint/field.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/rest_handler/endpoint/validator.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/rest_handler/entity.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/rest_handler/error.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/rest_handler/error_ctl.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/rest_handler/handler.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/rest_handler/normaliser.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/rest_handler/schema.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/rest_handler/util.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/splunk_aoblib/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/splunk_aoblib/rest_helper.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/splunk_aoblib/rest_migration.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/splunk_aoblib/setup_util.py delete mode 100755 apps/bitwarden_event_logs/lib/splunktaucclib/splunk_aoblib/utility.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3-1.26.20.dist-info/INSTALLER delete mode 100755 apps/bitwarden_event_logs/lib/urllib3-1.26.20.dist-info/METADATA delete mode 100755 apps/bitwarden_event_logs/lib/urllib3-1.26.20.dist-info/RECORD delete mode 100755 apps/bitwarden_event_logs/lib/urllib3-1.26.20.dist-info/REQUESTED delete mode 100755 apps/bitwarden_event_logs/lib/urllib3-1.26.20.dist-info/WHEEL delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/__pycache__/__init__.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/__pycache__/_collections.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/__pycache__/_version.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/__pycache__/connection.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/__pycache__/connectionpool.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/__pycache__/exceptions.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/__pycache__/fields.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/__pycache__/filepost.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/__pycache__/poolmanager.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/__pycache__/request.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/__pycache__/response.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/_collections.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/_version.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/connection.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/connectionpool.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/contrib/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/contrib/__pycache__/__init__.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/contrib/__pycache__/_appengine_environ.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/contrib/__pycache__/socks.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/contrib/_appengine_environ.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/contrib/_securetransport/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/contrib/_securetransport/bindings.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/contrib/_securetransport/low_level.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/contrib/appengine.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/contrib/ntlmpool.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/contrib/pyopenssl.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/contrib/securetransport.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/contrib/socks.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/exceptions.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/fields.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/filepost.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/packages/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/packages/__pycache__/__init__.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/packages/__pycache__/six.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/packages/backports/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/packages/backports/makefile.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/packages/backports/weakref_finalize.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/packages/six.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/poolmanager.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/request.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/response.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/__init__.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/__init__.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/connection.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/proxy.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/queue.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/request.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/response.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/retry.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/ssl_.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/ssl_match_hostname.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/ssltransport.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/timeout.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/url.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/wait.cpython-39.pyc delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/connection.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/proxy.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/queue.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/request.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/response.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/retry.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/ssl_.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/ssl_match_hostname.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/ssltransport.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/timeout.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/url.py delete mode 100755 apps/bitwarden_event_logs/lib/urllib3/util/wait.py delete mode 100755 apps/bitwarden_event_logs/local/inputs.conf delete mode 100755 apps/bitwarden_event_logs/local/passwords.conf delete mode 100755 apps/bitwarden_event_logs/local/script.conf delete mode 100755 apps/bitwarden_event_logs/test.conf diff --git a/apps/bitwarden_event_logs/VERSION b/apps/bitwarden_event_logs/VERSION deleted file mode 100755 index dd36387e..00000000 --- a/apps/bitwarden_event_logs/VERSION +++ /dev/null @@ -1,2 +0,0 @@ -2.1.0 -2.1.0 \ No newline at end of file diff --git a/apps/bitwarden_event_logs/app.manifest b/apps/bitwarden_event_logs/app.manifest deleted file mode 100755 index a9d6a137..00000000 --- a/apps/bitwarden_event_logs/app.manifest +++ /dev/null @@ -1,54 +0,0 @@ -{ - "dependencies": null, - "incompatibleApps": null, - "info": { - "author": [ - { - "company": "Bitwarden Inc.", - "email": "support@bitwarden.com", - "name": "Bitwarden Inc." - } - ], - "classification": { - "categories": [], - "developmentStatus": null, - "intendedAudience": null - }, - "commonInformationModels": null, - "description": "Bitwarden event logs integration into Splunk.", - "id": { - "group": null, - "name": "bitwarden_event_logs", - "version": "2.1.0" - }, - "license": { - "name": null, - "text": null, - "uri": null - }, - "privacyPolicy": { - "name": null, - "text": null, - "uri": null - }, - "releaseDate": null, - "releaseNotes": { - "name": "README", - "text": "README.txt", - "uri": "" - }, - "title": "Bitwarden Event Logs" - }, - "inputGroups": null, - "platformRequirements": null, - "schemaVersion": "2.0.0", - "supportedDeployments": [ - "_standalone", - "_distributed" - ], - "targetWorkloads": [ - "_search_heads", - "_indexers" - ], - "tasks": null -} \ No newline at end of file diff --git a/apps/bitwarden_event_logs/default/collections.conf b/apps/bitwarden_event_logs/default/collections.conf deleted file mode 100755 index 14acf53b..00000000 --- a/apps/bitwarden_event_logs/default/collections.conf +++ /dev/null @@ -1,6 +0,0 @@ -[eventsapi] -enforceTypes = true -field.next_request.start = string -field.next_request.end = string -field.next_request.continuation_token = string -field.last_log_date = string diff --git a/apps/bitwarden_event_logs/default/data/ui/views/authentication_events_dashboard.xml b/apps/bitwarden_event_logs/default/data/ui/views/authentication_events_dashboard.xml deleted file mode 100755 index 86879de7..00000000 --- a/apps/bitwarden_event_logs/default/data/ui/views/authentication_events_dashboard.xml +++ /dev/null @@ -1,147 +0,0 @@ -
- -
- - - - -14h@h - now - - - - - User Email - User ID - User Email - User Name - -
- - - - Successful Log In Attempts - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" type=1000 | iplocation ipAddress | lookup geo_countries longitude as lon, latitude as lat | stats count by Country | geom geo_countries featureIdField=Country - $timeframe.earliest$ - $timeframe.latest$ - - - - - - - - - - - - - - - Authentication Events by Device - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" (type=1000 OR type=1001 OR type=1002 OR type=1003 OR type=1004 OR type=1005 OR type=1006 OR type=1008 OR type=1009) | timechart count by deviceName - $timeframe.earliest$ - $timeframe.latest$ - - - - - - - - - Authentication Events by Type - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" (type=1000 OR type=1001 OR type=1002 OR type=1003 OR type=1004 OR type=1005 OR type=1006 OR type=1008 OR type=1009) | timechart count by typeName - $timeframe.earliest$ - $timeframe.latest$ - - - - - - - - - - Authentication Events by Device - - Device - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" (type=1000 OR type=1001 OR type=1002 OR type=1003 OR type=1004 OR type=1005 OR type=1006 OR type=1008 OR type=1009) | stats count by deviceName - -24h@h - now - - - - - - - - Authentication Events by Type - - Type - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" (type=1000 OR type=1001 OR type=1002 OR type=1003 OR type=1004 OR type=1005 OR type=1006 OR type=1008 OR type=1009) | stats count by typeName - $timeframe.earliest$ - $timeframe.latest$ - - - - - - - - - - - Top Failed Log In Attempts - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" (type=1005 OR type=1006) | stats count by $top_users_by$ | sort - count - $timeframe.earliest$ - $timeframe.latest$ - - - - - - - - - - - - - - - Top Successful Log In Attempts - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" type=1000 | stats count by $top_users_by$ | sort - count - $timeframe.earliest$ - $timeframe.latest$ - - - - - - - - - - - - - - Latest Authentication Events - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" (type=1000 OR type=1001 OR type=1002 OR type=1003 OR type=1004 OR type=1005 OR type=1006 OR type=1008 OR type=1009) - - $timeframe.earliest$ - $timeframe.latest$ - - - - - -
\ No newline at end of file diff --git a/apps/bitwarden_event_logs/default/data/ui/views/configuration.xml b/apps/bitwarden_event_logs/default/data/ui/views/configuration.xml index 627e945f..32def83d 100755 --- a/apps/bitwarden_event_logs/default/data/ui/views/configuration.xml +++ b/apps/bitwarden_event_logs/default/data/ui/views/configuration.xml @@ -1,5 +1,4 @@ - -#test \ No newline at end of file + \ No newline at end of file diff --git a/apps/bitwarden_event_logs/default/data/ui/views/organization_events_dashboard.xml b/apps/bitwarden_event_logs/default/data/ui/views/organization_events_dashboard.xml deleted file mode 100755 index 220bb918..00000000 --- a/apps/bitwarden_event_logs/default/data/ui/views/organization_events_dashboard.xml +++ /dev/null @@ -1,126 +0,0 @@ -
- -
- - - - -24h@h - now - - - - - User Email - User ID - User Email - User Name - -
- - - - Organization Events - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" =1300 AND type<1800)]]> | iplocation ipAddress | lookup geo_countries longitude as lon, latitude as lat | stats count by Country | geom geo_countries featureIdField=Country - $timeframe.earliest$ - $timeframe.latest$ - - - - - - - - - - - - - - - - Organization Events by Device - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" =1300 AND type<1800)]]> | timechart count by deviceName - $timeframe.earliest$ - $timeframe.latest$ - - - - - - - - - Organization Events by Type - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" =1300 AND type<1800)]]> | timechart count by typeName - $timeframe.earliest$ - $timeframe.latest$> - - - - - - - - - Organization Events by Device - - Device - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" =1300 AND type<1800)]]> | stats count by deviceName - $timeframe.earliest$ - $timeframe.latest$ - - - - - - - - Organization Events by Type - - Type - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" =1300 AND type<1800)]]> | stats count by typeName - $timeframe.earliest$ - $timeframe.latest$ - - - - - - - - - - - Top Organization Event Users - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" =1300 AND type<1800)]]> | stats count by $top_users_by$ | sort - count - $timeframe.earliest$ - $timeframe.latest$ - - - - - - - - - - - - - Latest Organization Events - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" =1300 AND type<1800)]]> - $timeframe.earliest$ - $timeframe.latest$ - - - - - -
\ No newline at end of file diff --git a/apps/bitwarden_event_logs/default/data/ui/views/setup_page_dashboard.xml b/apps/bitwarden_event_logs/default/data/ui/views/setup_page_dashboard.xml deleted file mode 100755 index 891a4104..00000000 --- a/apps/bitwarden_event_logs/default/data/ui/views/setup_page_dashboard.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - -
- -
-
-
diff --git a/apps/bitwarden_event_logs/default/data/ui/views/vault_item_events_dashboard.xml b/apps/bitwarden_event_logs/default/data/ui/views/vault_item_events_dashboard.xml deleted file mode 100755 index aa71eaae..00000000 --- a/apps/bitwarden_event_logs/default/data/ui/views/vault_item_events_dashboard.xml +++ /dev/null @@ -1,127 +0,0 @@ -
- -
- - - - -24h@h - now - - - - - User Email - User ID - User Email - User Name - -
- - - - Vault Item Events - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" =1100 AND type<1200)]]> | iplocation ipAddress | lookup geo_countries longitude as lon, latitude as lat | stats count by Country | geom geo_countries featureIdField=Country - $timeframe.earliest$ - $timeframe.latest$ - - - - - - - - - - - - - - - - Vault Item Events by Device - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" =1100 AND type<1200)]]> | timechart count by deviceName - $timeframe.earliest$ - $timeframe.latest$ - - - - - - - - - Vault Item Events by Type - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" =1100 AND type<1200)]]> | timechart count by typeName - $timeframe.earliest$ - $timeframe.latest$> - - - - - - - - - Vault Item Events by Device - - Device - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" =1100 AND type<1200)]]> | stats count by deviceName - $timeframe.earliest$ - $timeframe.latest$ - - - - - - - - Vault Item Events by Type - - Type - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" =1100 AND type<1200)]]> | stats count by typeName - $timeframe.earliest$ - $timeframe.latest$ - - - - - - - - - - - Top Vault Item Event Users - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" =1100 AND type<1200)]]> | stats count by $top_users_by$ | sort - count - $timeframe.earliest$ - $timeframe.latest$ - - - - - - - - - - - - - Latest Vault Item Events - - `bitwarden_event_logs_index` sourcetype="bitwarden:events" =1100 AND type<1200)]]> - - $timeframe.earliest$ - $timeframe.latest$ - - - - - -
\ No newline at end of file diff --git a/apps/bitwarden_event_logs/default/inputs.conf b/apps/bitwarden_event_logs/default/inputs.conf deleted file mode 100755 index 4c92bb09..00000000 --- a/apps/bitwarden_event_logs/default/inputs.conf +++ /dev/null @@ -1,6 +0,0 @@ -[script://$SPLUNK_HOME/etc/apps/bitwarden_event_logs/bin/bitwarden_event_logs.py] -interval = 60 -sourcetype = bitwarden:events -index = main -passAuth = splunk-system-user -python.version = python3 diff --git a/apps/bitwarden_event_logs/default/macros.conf b/apps/bitwarden_event_logs/default/macros.conf deleted file mode 100755 index d46034ba..00000000 --- a/apps/bitwarden_event_logs/default/macros.conf +++ /dev/null @@ -1,2 +0,0 @@ -[bitwarden_event_logs_index] -definition=index=* \ No newline at end of file diff --git a/apps/bitwarden_event_logs/default/props.conf b/apps/bitwarden_event_logs/default/props.conf deleted file mode 100755 index c492648e..00000000 --- a/apps/bitwarden_event_logs/default/props.conf +++ /dev/null @@ -1,138 +0,0 @@ -[bitwarden:events] -LINE_BREAKER = ([\r\n]) -SHOULD_LINEMERGE = false -TRUNCATE = 5000 -KV_MODE = json -FIELDALIAS-alias_1 = ipAddress AS src -FIELDALIAS-alias_2 = date AS timestamp -EVAL-typeName = coalesce(case(\ - type==1000,"User_LoggedIn",\ - type==1001,"User_ChangedPassword",\ - type==1002,"User_Updated2fa",\ - type==1003,"User_Disabled2fa",\ - type==1004,"User_Recovered2fa",\ - type==1005,"User_FailedLogIn",\ - type==1006,"User_FailedLogIn2fa",\ - type==1007,"User_ClientExportedVault",\ - type==1008,"User_UpdatedTempPassword",\ - type==1009,"User_MigratedKeyToKeyConnector",\ - type==1010,"User_RequestedDeviceApproval",\ - type==1011,"User_TdeOffboardingPasswordSet",\ - type==1100,"Cipher_Created",\ - type==1101,"Cipher_Updated",\ - type==1102,"Cipher_Deleted",\ - type==1103,"Cipher_AttachmentCreated",\ - type==1104,"Cipher_AttachmentDeleted",\ - type==1105,"Cipher_Shared",\ - type==1106,"Cipher_UpdatedCollections",\ - type==1107,"Cipher_ClientViewed",\ - type==1108,"Cipher_ClientToggledPasswordVisible",\ - type==1109,"Cipher_ClientToggledHiddenFieldVisible",\ - type==1110,"Cipher_ClientToggledCardCodeVisible",\ - type==1111,"Cipher_ClientCopiedPassword",\ - type==1112,"Cipher_ClientCopiedHiddenField",\ - type==1113,"Cipher_ClientCopiedCardCode",\ - type==1114,"Cipher_ClientAutofilled",\ - type==1115,"Cipher_SoftDeleted",\ - type==1116,"Cipher_Restored",\ - type==1117,"Cipher_ClientToggledCardNumberVisible",\ - type==1300,"Collection_Created",\ - type==1301,"Collection_Updated",\ - type==1302,"Collection_Deleted",\ - type==1400,"Group_Created",\ - type==1401,"Group_Updated",\ - type==1402,"Group_Deleted",\ - type==1500,"OrganizationUser_Invited",\ - type==1501,"OrganizationUser_Confirmed",\ - type==1502,"OrganizationUser_Updated",\ - type==1503,"OrganizationUser_Removed",\ - type==1504,"OrganizationUser_UpdatedGroups",\ - type==1505,"OrganizationUser_UnlinkedSso",\ - type==1506,"OrganizationUser_ResetPassword_Enroll",\ - type==1507,"OrganizationUser_ResetPassword_Withdraw",\ - type==1508,"OrganizationUser_AdminResetPassword",\ - type==1509,"OrganizationUser_ResetSsoLink",\ - type==1510,"OrganizationUser_FirstSsoLogin",\ - type==1511,"OrganizationUser_Revoked",\ - type==1512,"OrganizationUser_Restored",\ - type==1513,"OrganizationUser_ApprovedAuthRequest",\ - type==1514,"OrganizationUser_RejectedAuthRequest",\ - type==1515,"OrganizationUser_Deleted",\ - type==1516,"OrganizationUser_Left",\ - type==1517,"OrganizationUser_AutomaticallyConfirmed",\ - type==1600,"Organization_Updated",\ - type==1601,"Organization_PurgedVault",\ - type==1602,"Organization_ClientExportedVault",\ - type==1603,"Organization_VaultAccessed",\ - type==1604,"Organization_EnabledSso",\ - type==1605,"Organization_DisabledSso",\ - type==1606,"Organization_EnabledKeyConnector",\ - type==1607,"Organization_DisabledKeyConnector",\ - type==1608,"Organization_SponsorshipsSynced",\ - type==1609,"Organization_CollectionManagement_Updated",\ - type==1610,"Organization_CollectionManagement_LimitCollectionCreationEnabled",\ - type==1611,"Organization_CollectionManagement_LimitCollectionCreationDisabled",\ - type==1612,"Organization_CollectionManagement_LimitCollectionDeletionEnabled",\ - type==1613,"Organization_CollectionManagement_LimitCollectionDeletionDisabled",\ - type==1614,"Organization_CollectionManagement_LimitItemDeletionEnabled",\ - type==1615,"Organization_CollectionManagement_LimitItemDeletionDisabled",\ - type==1616,"Organization_CollectionManagement_AllowAdminAccessToAllCollectionItemsEnabled",\ - type==1617,"Organization_CollectionManagement_AllowAdminAccessToAllCollectionItemsDisabled",\ - type==1620,"Organization_AutoConfirmEnabled_Admin",\ - type==1621,"Organization_AutoConfirmDisabled_Admin",\ - type==1622,"Organization_AutoConfirmEnabled_Portal",\ - type==1623,"Organization_AutoConfirmDisabled_Portal",\ - type==1700,"Policy_Updated",\ - type==1800,"ProviderUser_Invited",\ - type==1801,"ProviderUser_Confirmed",\ - type==1802,"ProviderUser_Updated",\ - type==1803,"ProviderUser_Removed",\ - type==1900,"ProviderOrganization_Created",\ - type==1901,"ProviderOrganization_Added",\ - type==1902,"ProviderOrganization_Removed",\ - type==1903,"ProviderOrganization_VaultAccessed",\ - type==2000,"OrganizationDomain_Added",\ - type==2001,"OrganizationDomain_Removed",\ - type==2002,"OrganizationDomain_Verified",\ - type==2003,"OrganizationDomain_NotVerified",\ - type==2100,"Secret_Retrieved",\ - type==2101,"Secret_Created",\ - type==2102,"Secret_Edited",\ - type==2103,"Secret_Deleted",\ - type==2104,"Secret_Permanently_Deleted",\ - type==2105,"Secret_Restored",\ - type==2200,"Project_Retrieved",\ - type==2201,"Project_Created",\ - type==2202,"Project_Edited",\ - type==2203,"Project_Deleted"\ - ), type) -EVAL-deviceName = coalesce(case(device==0,"Android",\ - device==1,"iOS",\ - device==2,"Chrome Extension",\ - device==3,"Firefox Extension",\ - device==4,"Opera Extension",\ - device==5,"Edge Extension",\ - device==6,"Windows Desktop",\ - device==7,"macOS Desktop",\ - device==8,"Linux Desktop",\ - device==9,"Chrome Browser",\ - device==10,"Firefox Browser",\ - device==11,"Opera Browser",\ - device==12,"Edge Browser",\ - device==13,"IEBrowser",\ - device==14,"Unknown Browser",\ - device==15,"Android Amazon",\ - device==16,"UWP",\ - device==17,"Safari Browser",\ - device==18,"Vivaldi Browser",\ - device==19,"Vivaldi Extension",\ - device==20,"Safari Extension",\ - device==21,"SDK",\ - device==22,"Server",\ - device==23,"Windows CLI",\ - device==24,"MacOs CLI",\ - device==25,"Linux CLI",\ - device==26,"DuckDuckGo"\ - ), device) -TIME_PREFIX = "date":" -TIME_FORMAT = %Y-%m-%dT%H:%M:%S.%6N%Z diff --git a/apps/bitwarden_event_logs/default/script.conf b/apps/bitwarden_event_logs/default/script.conf deleted file mode 100755 index 53e6133d..00000000 --- a/apps/bitwarden_event_logs/default/script.conf +++ /dev/null @@ -1,4 +0,0 @@ -[config] -apiUrl=https://api.bitwarden.com -identityUrl=https://identity.bitwarden.com -loggingLevel=INFO diff --git a/apps/bitwarden_event_logs/default/server.conf b/apps/bitwarden_event_logs/default/server.conf deleted file mode 100755 index d1193e0e..00000000 --- a/apps/bitwarden_event_logs/default/server.conf +++ /dev/null @@ -1,3 +0,0 @@ -[shclustering] -conf_replication_include.script = true - diff --git a/apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/INSTALLER b/apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/INSTALLER deleted file mode 100755 index a1b589e3..00000000 --- a/apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/LICENSE b/apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/LICENSE deleted file mode 100755 index 04b6b1f3..00000000 --- a/apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -Copyright 2006 Dan-Haim. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. -3. Neither the name of Dan Haim nor the names of his contributors may be used - to endorse or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -EVENT SHALL DAN HAIM OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE. diff --git a/apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/METADATA b/apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/METADATA deleted file mode 100755 index ae2ae341..00000000 --- a/apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/METADATA +++ /dev/null @@ -1,321 +0,0 @@ -Metadata-Version: 2.1 -Name: PySocks -Version: 1.7.1 -Summary: A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information. -Home-page: https://github.com/Anorov/PySocks -Author: Anorov -Author-email: anorov.vorona@gmail.com -License: BSD -Keywords: socks,proxy -Platform: UNKNOWN -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* -Description-Content-Type: text/markdown - -PySocks -======= - -PySocks lets you send traffic through SOCKS and HTTP proxy servers. It is a modern fork of [SocksiPy](http://socksipy.sourceforge.net/) with bug fixes and extra features. - -Acts as a drop-in replacement to the socket module. Seamlessly configure SOCKS proxies for any socket object by calling `socket_object.set_proxy()`. - ----------------- - -Features -======== - -* SOCKS proxy client for Python 2.7 and 3.4+ -* TCP supported -* UDP mostly supported (issues may occur in some edge cases) -* HTTP proxy client included but not supported or recommended (you should use urllib2's or requests' own HTTP proxy interface) -* urllib2 handler included. `pip install` / `setup.py install` will automatically install the `sockshandler` module. - -Installation -============ - - pip install PySocks - -Or download the tarball / `git clone` and... - - python setup.py install - -These will install both the `socks` and `sockshandler` modules. - -Alternatively, include just `socks.py` in your project. - --------------------------------------------- - -*Warning:* PySocks/SocksiPy only supports HTTP proxies that use CONNECT tunneling. Certain HTTP proxies may not work with this library. If you wish to use HTTP (not SOCKS) proxies, it is recommended that you rely on your HTTP client's native proxy support (`proxies` dict for `requests`, or `urllib2.ProxyHandler` for `urllib2`) instead. - --------------------------------------------- - -Usage -===== - -## socks.socksocket ## - - import socks - - s = socks.socksocket() # Same API as socket.socket in the standard lib - - s.set_proxy(socks.SOCKS5, "localhost") # SOCKS4 and SOCKS5 use port 1080 by default - # Or - s.set_proxy(socks.SOCKS4, "localhost", 4444) - # Or - s.set_proxy(socks.HTTP, "5.5.5.5", 8888) - - # Can be treated identical to a regular socket object - s.connect(("www.somesite.com", 80)) - s.sendall("GET / HTTP/1.1 ...") - print s.recv(4096) - -## Monkeypatching ## - -To monkeypatch the entire standard library with a single default proxy: - - import urllib2 - import socket - import socks - - socks.set_default_proxy(socks.SOCKS5, "localhost") - socket.socket = socks.socksocket - - urllib2.urlopen("http://www.somesite.com/") # All requests will pass through the SOCKS proxy - -Note that monkeypatching may not work for all standard modules or for all third party modules, and generally isn't recommended. Monkeypatching is usually an anti-pattern in Python. - -## urllib2 Handler ## - -Example use case with the `sockshandler` urllib2 handler. Note that you must import both `socks` and `sockshandler`, as the handler is its own module separate from PySocks. The module is included in the PyPI package. - - import urllib2 - import socks - from sockshandler import SocksiPyHandler - - opener = urllib2.build_opener(SocksiPyHandler(socks.SOCKS5, "127.0.0.1", 9050)) - print opener.open("http://www.somesite.com/") # All requests made by the opener will pass through the SOCKS proxy - --------------------------------------------- - -Original SocksiPy README attached below, amended to reflect API changes. - --------------------------------------------- - -SocksiPy - -A Python SOCKS module. - -(C) 2006 Dan-Haim. All rights reserved. - -See LICENSE file for details. - - -*WHAT IS A SOCKS PROXY?* - -A SOCKS proxy is a proxy server at the TCP level. In other words, it acts as -a tunnel, relaying all traffic going through it without modifying it. -SOCKS proxies can be used to relay traffic using any network protocol that -uses TCP. - -*WHAT IS SOCKSIPY?* - -This Python module allows you to create TCP connections through a SOCKS -proxy without any special effort. -It also supports relaying UDP packets with a SOCKS5 proxy. - -*PROXY COMPATIBILITY* - -SocksiPy is compatible with three different types of proxies: - -1. SOCKS Version 4 (SOCKS4), including the SOCKS4a extension. -2. SOCKS Version 5 (SOCKS5). -3. HTTP Proxies which support tunneling using the CONNECT method. - -*SYSTEM REQUIREMENTS* - -Being written in Python, SocksiPy can run on any platform that has a Python -interpreter and TCP/IP support. -This module has been tested with Python 2.3 and should work with greater versions -just as well. - - -INSTALLATION -------------- - -Simply copy the file "socks.py" to your Python's `lib/site-packages` directory, -and you're ready to go. [Editor's note: it is better to use `python setup.py install` for PySocks] - - -USAGE ------- - -First load the socks module with the command: - - >>> import socks - >>> - -The socks module provides a class called `socksocket`, which is the base to all of the module's functionality. - -The `socksocket` object has the same initialization parameters as the normal socket -object to ensure maximal compatibility, however it should be noted that `socksocket` will only function with family being `AF_INET` and -type being either `SOCK_STREAM` or `SOCK_DGRAM`. -Generally, it is best to initialize the `socksocket` object with no parameters - - >>> s = socks.socksocket() - >>> - -The `socksocket` object has an interface which is very similiar to socket's (in fact -the `socksocket` class is derived from socket) with a few extra methods. -To select the proxy server you would like to use, use the `set_proxy` method, whose -syntax is: - - set_proxy(proxy_type, addr[, port[, rdns[, username[, password]]]]) - -Explanation of the parameters: - -`proxy_type` - The type of the proxy server. This can be one of three possible -choices: `PROXY_TYPE_SOCKS4`, `PROXY_TYPE_SOCKS5` and `PROXY_TYPE_HTTP` for SOCKS4, -SOCKS5 and HTTP servers respectively. `SOCKS4`, `SOCKS5`, and `HTTP` are all aliases, respectively. - -`addr` - The IP address or DNS name of the proxy server. - -`port` - The port of the proxy server. Defaults to 1080 for socks and 8080 for http. - -`rdns` - This is a boolean flag than modifies the behavior regarding DNS resolving. -If it is set to True, DNS resolving will be preformed remotely, on the server. -If it is set to False, DNS resolving will be preformed locally. Please note that -setting this to True with SOCKS4 servers actually use an extension to the protocol, -called SOCKS4a, which may not be supported on all servers (SOCKS5 and http servers -always support DNS). The default is True. - -`username` - For SOCKS5 servers, this allows simple username / password authentication -with the server. For SOCKS4 servers, this parameter will be sent as the userid. -This parameter is ignored if an HTTP server is being used. If it is not provided, -authentication will not be used (servers may accept unauthenticated requests). - -`password` - This parameter is valid only for SOCKS5 servers and specifies the -respective password for the username provided. - -Example of usage: - - >>> s.set_proxy(socks.SOCKS5, "socks.example.com") # uses default port 1080 - >>> s.set_proxy(socks.SOCKS4, "socks.test.com", 1081) - -After the set_proxy method has been called, simply call the connect method with the -traditional parameters to establish a connection through the proxy: - - >>> s.connect(("www.sourceforge.net", 80)) - >>> - -Connection will take a bit longer to allow negotiation with the proxy server. -Please note that calling connect without calling `set_proxy` earlier will connect -without a proxy (just like a regular socket). - -Errors: Any errors in the connection process will trigger exceptions. The exception -may either be generated by the underlying socket layer or may be custom module -exceptions, whose details follow: - -class `ProxyError` - This is a base exception class. It is not raised directly but -rather all other exception classes raised by this module are derived from it. -This allows an easy way to catch all proxy-related errors. It descends from `IOError`. - -All `ProxyError` exceptions have an attribute `socket_err`, which will contain either a -caught `socket.error` exception, or `None` if there wasn't any. - -class `GeneralProxyError` - When thrown, it indicates a problem which does not fall -into another category. - -* `Sent invalid data` - This error means that unexpected data has been received from -the server. The most common reason is that the server specified as the proxy is -not really a SOCKS4/SOCKS5/HTTP proxy, or maybe the proxy type specified is wrong. - -* `Connection closed unexpectedly` - The proxy server unexpectedly closed the connection. -This may indicate that the proxy server is experiencing network or software problems. - -* `Bad proxy type` - This will be raised if the type of the proxy supplied to the -set_proxy function was not one of `SOCKS4`/`SOCKS5`/`HTTP`. - -* `Bad input` - This will be raised if the `connect()` method is called with bad input -parameters. - -class `SOCKS5AuthError` - This indicates that the connection through a SOCKS5 server -failed due to an authentication problem. - -* `Authentication is required` - This will happen if you use a SOCKS5 server which -requires authentication without providing a username / password at all. - -* `All offered authentication methods were rejected` - This will happen if the proxy -requires a special authentication method which is not supported by this module. - -* `Unknown username or invalid password` - Self descriptive. - -class `SOCKS5Error` - This will be raised for SOCKS5 errors which are not related to -authentication. -The parameter is a tuple containing a code, as given by the server, -and a description of the -error. The possible errors, according to the RFC, are: - -* `0x01` - General SOCKS server failure - If for any reason the proxy server is unable to -fulfill your request (internal server error). -* `0x02` - connection not allowed by ruleset - If the address you're trying to connect to -is blacklisted on the server or requires authentication. -* `0x03` - Network unreachable - The target could not be contacted. A router on the network -had replied with a destination net unreachable error. -* `0x04` - Host unreachable - The target could not be contacted. A router on the network -had replied with a destination host unreachable error. -* `0x05` - Connection refused - The target server has actively refused the connection -(the requested port is closed). -* `0x06` - TTL expired - The TTL value of the SYN packet from the proxy to the target server -has expired. This usually means that there are network problems causing the packet -to be caught in a router-to-router "ping-pong". -* `0x07` - Command not supported - For instance if the server does not support UDP. -* `0x08` - Address type not supported - The client has provided an invalid address type. -When using this module, this error should not occur. - -class `SOCKS4Error` - This will be raised for SOCKS4 errors. The parameter is a tuple -containing a code and a description of the error, as given by the server. The -possible error, according to the specification are: - -* `0x5B` - Request rejected or failed - Will be raised in the event of an failure for any -reason other then the two mentioned next. -* `0x5C` - request rejected because SOCKS server cannot connect to identd on the client - -The Socks server had tried an ident lookup on your computer and has failed. In this -case you should run an identd server and/or configure your firewall to allow incoming -connections to local port 113 from the remote server. -* `0x5D` - request rejected because the client program and identd report different user-ids - -The Socks server had performed an ident lookup on your computer and has received a -different userid than the one you have provided. Change your userid (through the -username parameter of the set_proxy method) to match and try again. - -class `HTTPError` - This will be raised for HTTP errors. The message will contain -the HTTP status code and provided error message. - -After establishing the connection, the object behaves like a standard socket. - -Methods like `makefile()` and `settimeout()` should behave just like regular sockets. -Call the `close()` method to close the connection. - -In addition to the `socksocket` class, an additional function worth mentioning is the -`set_default_proxy` function. The parameters are the same as the `set_proxy` method. -This function will set default proxy settings for newly created `socksocket` objects, -in which the proxy settings haven't been changed via the `set_proxy` method. -This is quite useful if you wish to force 3rd party modules to use a SOCKS proxy, -by overriding the socket object. -For example: - - >>> socks.set_default_proxy(socks.SOCKS5, "socks.example.com") - >>> socket.socket = socks.socksocket - >>> urllib.urlopen("http://www.sourceforge.net/") - - -PROBLEMS ---------- - -Please open a GitHub issue at https://github.com/Anorov/PySocks - - diff --git a/apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/RECORD b/apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/RECORD deleted file mode 100755 index 9b176216..00000000 --- a/apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/RECORD +++ /dev/null @@ -1,9 +0,0 @@ -PySocks-1.7.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -PySocks-1.7.1.dist-info/LICENSE,sha256=cCfiFOAU63i3rcwc7aWspxOnn8T2oMUsnaWz5wfm_-k,1401 -PySocks-1.7.1.dist-info/METADATA,sha256=zbQMizjPOOP4DhEiEX24XXjNrYuIxF9UGUpN0uFDB6Y,13235 -PySocks-1.7.1.dist-info/RECORD,, -PySocks-1.7.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -PySocks-1.7.1.dist-info/WHEEL,sha256=t_MpApv386-8PVts2R6wsTifdIn0vbUDTVv61IbqFC8,92 -PySocks-1.7.1.dist-info/top_level.txt,sha256=TKSOIfCFBoK9EY8FBYbYqC3PWd3--G15ph9n8-QHPDk,19 -socks.py,sha256=xOYn27t9IGrbTBzWsUUuPa0YBuplgiUykzkOB5V5iFY,31086 -sockshandler.py,sha256=2SYGj-pwt1kjgLoZAmyeaEXCeZDWRmfVS_QG6kErGtY,3966 diff --git a/apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/REQUESTED b/apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/REQUESTED deleted file mode 100755 index e69de29b..00000000 diff --git a/apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/WHEEL b/apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/WHEEL deleted file mode 100755 index 129a6735..00000000 --- a/apps/bitwarden_event_logs/lib/PySocks-1.7.1.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.33.3) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/apps/bitwarden_event_logs/lib/__pycache__/six.cpython-39.pyc b/apps/bitwarden_event_logs/lib/__pycache__/six.cpython-39.pyc deleted file mode 100755 index 5c0a607214ccb6d3e1fe5d271f7ff9fa326fdc86..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27564 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 diff --git a/apps/bitwarden_event_logs/lib/__pycache__/socks.cpython-39.pyc b/apps/bitwarden_event_logs/lib/__pycache__/socks.cpython-39.pyc deleted file mode 100755 index a3e73f98c418deb78306e201c8d8cd2da30e27ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21388 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.7 -License-File: LICENSE -Dynamic: author -Dynamic: author-email -Dynamic: classifier -Dynamic: description -Dynamic: home-page -Dynamic: license -Dynamic: license-file -Dynamic: project-url -Dynamic: requires-python -Dynamic: summary - -Certifi: Python SSL Certificates -================================ - -Certifi provides Mozilla's carefully curated collection of Root Certificates for -validating the trustworthiness of SSL certificates while verifying the identity -of TLS hosts. It has been extracted from the `Requests`_ project. - -Installation ------------- - -``certifi`` is available on PyPI. Simply install it with ``pip``:: - - $ pip install certifi - -Usage ------ - -To reference the installed certificate authority (CA) bundle, you can use the -built-in function:: - - >>> import certifi - - >>> certifi.where() - '/usr/local/lib/python3.7/site-packages/certifi/cacert.pem' - -Or from the command line:: - - $ python -m certifi - /usr/local/lib/python3.7/site-packages/certifi/cacert.pem - -Enjoy! - -.. _`Requests`: https://requests.readthedocs.io/en/master/ - -Addition/Removal of Certificates --------------------------------- - -Certifi does not support any addition/removal or other modification of the -CA trust store content. This project is intended to provide a reliable and -highly portable root of trust to python deployments. Look to upstream projects -for methods to use alternate trust. diff --git a/apps/bitwarden_event_logs/lib/certifi-2025.11.12.dist-info/RECORD b/apps/bitwarden_event_logs/lib/certifi-2025.11.12.dist-info/RECORD deleted file mode 100755 index 6980ff43..00000000 --- a/apps/bitwarden_event_logs/lib/certifi-2025.11.12.dist-info/RECORD +++ /dev/null @@ -1,12 +0,0 @@ -certifi-2025.11.12.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -certifi-2025.11.12.dist-info/METADATA,sha256=_JprGu_1lWSdHlruRBKcorXnrfvBDhvX_6KRr8HQbLc,2475 -certifi-2025.11.12.dist-info/RECORD,, -certifi-2025.11.12.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -certifi-2025.11.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91 -certifi-2025.11.12.dist-info/licenses/LICENSE,sha256=6TcW2mucDVpKHfYP5pWzcPBpVgPSH2-D8FPkLPwQyvc,989 -certifi-2025.11.12.dist-info/top_level.txt,sha256=KMu4vUCfsjLrkPbSNdgdekS-pVJzBAJFO__nI8NF6-U,8 -certifi/__init__.py,sha256=1BRSxNMnZW7CZ2oJtYWLoJgfHfcB9i273exwiPwfjJM,94 -certifi/__main__.py,sha256=xBBoj905TUWBLRGANOcf7oi6e-3dMP4cEoG9OyMs11g,243 -certifi/cacert.pem,sha256=oa1dZD4hxDtb7XTH4IkdzbWPavUcis4eTwINZUqlKhY,283932 -certifi/core.py,sha256=XFXycndG5pf37ayeF8N32HUuDafsyhkVMbO4BAPWHa0,3394 -certifi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/apps/bitwarden_event_logs/lib/certifi-2025.11.12.dist-info/REQUESTED b/apps/bitwarden_event_logs/lib/certifi-2025.11.12.dist-info/REQUESTED deleted file mode 100755 index e69de29b..00000000 diff --git a/apps/bitwarden_event_logs/lib/certifi-2025.11.12.dist-info/WHEEL b/apps/bitwarden_event_logs/lib/certifi-2025.11.12.dist-info/WHEEL deleted file mode 100755 index e7fa31b6..00000000 --- a/apps/bitwarden_event_logs/lib/certifi-2025.11.12.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (80.9.0) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/apps/bitwarden_event_logs/lib/certifi-2025.11.12.dist-info/licenses/LICENSE b/apps/bitwarden_event_logs/lib/certifi-2025.11.12.dist-info/licenses/LICENSE deleted file mode 100755 index 62b076cd..00000000 --- a/apps/bitwarden_event_logs/lib/certifi-2025.11.12.dist-info/licenses/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -This package contains a modified version of ca-bundle.crt: - -ca-bundle.crt -- Bundle of CA Root Certificates - -This is a bundle of X.509 certificates of public Certificate Authorities -(CA). These were automatically extracted from Mozilla's root certificates -file (certdata.txt). This file can be found in the mozilla source tree: -https://hg.mozilla.org/mozilla-central/file/tip/security/nss/lib/ckfw/builtins/certdata.txt -It contains the certificates in PEM format and therefore -can be directly used with curl / libcurl / php_curl, or with -an Apache+mod_ssl webserver for SSL client authentication. -Just configure this file as the SSLCACertificateFile.# - -***** BEGIN LICENSE BLOCK ***** -This Source Code Form is subject to the terms of the Mozilla Public License, -v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain -one at http://mozilla.org/MPL/2.0/. - -***** END LICENSE BLOCK ***** -@(#) $RCSfile: certdata.txt,v $ $Revision: 1.80 $ $Date: 2011/11/03 15:11:58 $ diff --git a/apps/bitwarden_event_logs/lib/certifi/__init__.py b/apps/bitwarden_event_logs/lib/certifi/__init__.py deleted file mode 100755 index f11f5ae4..00000000 --- a/apps/bitwarden_event_logs/lib/certifi/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .core import contents, where - -__all__ = ["contents", "where"] -__version__ = "2025.11.12" diff --git a/apps/bitwarden_event_logs/lib/certifi/__main__.py b/apps/bitwarden_event_logs/lib/certifi/__main__.py deleted file mode 100755 index 8945b5da..00000000 --- a/apps/bitwarden_event_logs/lib/certifi/__main__.py +++ /dev/null @@ -1,12 +0,0 @@ -import argparse - -from certifi import contents, where - -parser = argparse.ArgumentParser() -parser.add_argument("-c", "--contents", action="store_true") -args = parser.parse_args() - -if args.contents: - print(contents()) -else: - print(where()) diff --git a/apps/bitwarden_event_logs/lib/certifi/__pycache__/__init__.cpython-39.pyc b/apps/bitwarden_event_logs/lib/certifi/__pycache__/__init__.cpython-39.pyc deleted file mode 100755 index 1dcf7dc714998ce5b762956398eb5c8ae4bb2fed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 276 zcmYk0zfQw25XNmMp;1vK9s!9di%lZ2A;ieU!j^$k8k=I_*p}_Kg4cnCSMtilD==~1 z`bqaY-5-5-(`K_KsH@K}{fzmi6sKZX++)N`gd~z0ve1z(Y-E&3CaWDAD_M`VY@SGW z_DgZCW4WE3P0FU2o8zkrDiohkAwJf_^>D-cp7+CJ*W`M3r{lZ=n2Z6)=Ku#4qIMSG zn}k&{DENC2&La0$$?}79L|t4-1)v4ZZ#ff497Qr|6`tw8T*%vPe*{p zk2vi+6pEEhaU~jF@_rSiP(lUoxe8VEXIKWBpNA@b5y3H@Crz}7%XpEL31>=FDb9h; zdjC`n(0{nIGBreN)IP?j5k`&6$zobgZ$yp%7d6>NDRIbV)8BPgYjEbo%$IMnKdwfW z9i17QFFLhqbPn3BptZR>)46G_tD2^Arfc)2tG7yblGhd8G+*jP-a&4h&Fijpx^;G* zT_6qYV8(sCy{abr(-EPukJH{o;nd@7}W zZJd-J7*KRCKK@j6OIO%svua-y+SNt1T-xH;xK|Y@-Aeru6Ov7LN}FvlpBIfeE_M_Y zbq5-G@YDY0Eg)ID*HmmPP%sV&%9*&J3SSI+-6xfmC#KOpB57}c3(>6y_ZSt2fL08? zbKD8V-|}^!#0J+Ybdd|!@rI$!T(VBxV8c23AF>a@flqcxe$1|ko@IV?$;q!zkA4A4 z6eug7o_D5QR_+WaQy*9det?u=MLd;Aa3_o None: - _CACERT_CTX.__exit__(None, None, None) # type: ignore[union-attr] - - -if sys.version_info >= (3, 11): - - from importlib.resources import as_file, files - - _CACERT_CTX = None - _CACERT_PATH = None - - def where() -> str: - # This is slightly terrible, but we want to delay extracting the file - # in cases where we're inside of a zipimport situation until someone - # actually calls where(), but we don't want to re-extract the file - # on every call of where(), so we'll do it once then store it in a - # global variable. - global _CACERT_CTX - global _CACERT_PATH - if _CACERT_PATH is None: - # This is slightly janky, the importlib.resources API wants you to - # manage the cleanup of this file, so it doesn't actually return a - # path, it returns a context manager that will give you the path - # when you enter it and will do any cleanup when you leave it. In - # the common case of not needing a temporary file, it will just - # return the file system location and the __exit__() is a no-op. - # - # We also have to hold onto the actual context manager, because - # it will do the cleanup whenever it gets garbage collected, so - # we will also store that at the global level as well. - _CACERT_CTX = as_file(files("certifi").joinpath("cacert.pem")) - _CACERT_PATH = str(_CACERT_CTX.__enter__()) - atexit.register(exit_cacert_ctx) - - return _CACERT_PATH - - def contents() -> str: - return files("certifi").joinpath("cacert.pem").read_text(encoding="ascii") - -else: - - from importlib.resources import path as get_path, read_text - - _CACERT_CTX = None - _CACERT_PATH = None - - def where() -> str: - # This is slightly terrible, but we want to delay extracting the - # file in cases where we're inside of a zipimport situation until - # someone actually calls where(), but we don't want to re-extract - # the file on every call of where(), so we'll do it once then store - # it in a global variable. - global _CACERT_CTX - global _CACERT_PATH - if _CACERT_PATH is None: - # This is slightly janky, the importlib.resources API wants you - # to manage the cleanup of this file, so it doesn't actually - # return a path, it returns a context manager that will give - # you the path when you enter it and will do any cleanup when - # you leave it. In the common case of not needing a temporary - # file, it will just return the file system location and the - # __exit__() is a no-op. - # - # We also have to hold onto the actual context manager, because - # it will do the cleanup whenever it gets garbage collected, so - # we will also store that at the global level as well. - _CACERT_CTX = get_path("certifi", "cacert.pem") - _CACERT_PATH = str(_CACERT_CTX.__enter__()) - atexit.register(exit_cacert_ctx) - - return _CACERT_PATH - - def contents() -> str: - return read_text("certifi", "cacert.pem", encoding="ascii") diff --git a/apps/bitwarden_event_logs/lib/certifi/py.typed b/apps/bitwarden_event_logs/lib/certifi/py.typed deleted file mode 100755 index e69de29b..00000000 diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/INSTALLER b/apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/INSTALLER deleted file mode 100755 index a1b589e3..00000000 --- a/apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/METADATA b/apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/METADATA deleted file mode 100755 index 8d32edcc..00000000 --- a/apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/METADATA +++ /dev/null @@ -1,764 +0,0 @@ -Metadata-Version: 2.4 -Name: charset-normalizer -Version: 3.4.4 -Summary: The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet. -Author-email: "Ahmed R. TAHRI" -Maintainer-email: "Ahmed R. TAHRI" -License: MIT -Project-URL: Changelog, https://github.com/jawah/charset_normalizer/blob/master/CHANGELOG.md -Project-URL: Documentation, https://charset-normalizer.readthedocs.io/ -Project-URL: Code, https://github.com/jawah/charset_normalizer -Project-URL: Issue tracker, https://github.com/jawah/charset_normalizer/issues -Keywords: encoding,charset,charset-detector,detector,normalization,unicode,chardet,detect -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -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 :: 3 :: Only -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Text Processing :: Linguistic -Classifier: Topic :: Utilities -Classifier: Typing :: Typed -Requires-Python: >=3.7 -Description-Content-Type: text/markdown -License-File: LICENSE -Provides-Extra: unicode-backport -Dynamic: license-file - -

Charset Detection, for Everyone 👋

- -

- The Real First Universal Charset Detector
- - - - - Download Count Total - - - - -

-

- Featured Packages
- - Static Badge - - - Static Badge - -

-

- In other language (unofficial port - by the community)
- - Static Badge - -

- -> A library that helps you read text from an unknown charset encoding.
Motivated by `chardet`, -> I'm trying to resolve the issue by taking a new approach. -> All IANA character set names for which the Python core library provides codecs are supported. - -

- >>>>> 👉 Try Me Online Now, Then Adopt Me 👈 <<<<< -

- -This project offers you an alternative to **Universal Charset Encoding Detector**, also known as **Chardet**. - -| Feature | [Chardet](https://github.com/chardet/chardet) | Charset Normalizer | [cChardet](https://github.com/PyYoshi/cChardet) | -|--------------------------------------------------|:---------------------------------------------:|:--------------------------------------------------------------------------------------------------:|:-----------------------------------------------:| -| `Fast` | ❌ | ✅ | ✅ | -| `Universal**` | ❌ | ✅ | ❌ | -| `Reliable` **without** distinguishable standards | ❌ | ✅ | ✅ | -| `Reliable` **with** distinguishable standards | ✅ | ✅ | ✅ | -| `License` | LGPL-2.1
_restrictive_ | MIT | MPL-1.1
_restrictive_ | -| `Native Python` | ✅ | ✅ | ❌ | -| `Detect spoken language` | ❌ | ✅ | N/A | -| `UnicodeDecodeError Safety` | ❌ | ✅ | ❌ | -| `Whl Size (min)` | 193.6 kB | 42 kB | ~200 kB | -| `Supported Encoding` | 33 | 🎉 [99](https://charset-normalizer.readthedocs.io/en/latest/user/support.html#supported-encodings) | 40 | - -

-Reading Normalized TextCat Reading Text -

- -*\*\* : They are clearly using specific code for a specific encoding even if covering most of used one*
- -## ⚡ Performance - -This package offer better performance than its counterpart Chardet. Here are some numbers. - -| Package | Accuracy | Mean per file (ms) | File per sec (est) | -|-----------------------------------------------|:--------:|:------------------:|:------------------:| -| [chardet](https://github.com/chardet/chardet) | 86 % | 63 ms | 16 file/sec | -| charset-normalizer | **98 %** | **10 ms** | 100 file/sec | - -| Package | 99th percentile | 95th percentile | 50th percentile | -|-----------------------------------------------|:---------------:|:---------------:|:---------------:| -| [chardet](https://github.com/chardet/chardet) | 265 ms | 71 ms | 7 ms | -| charset-normalizer | 100 ms | 50 ms | 5 ms | - -_updated as of december 2024 using CPython 3.12_ - -Chardet's performance on larger file (1MB+) are very poor. Expect huge difference on large payload. - -> Stats are generated using 400+ files using default parameters. More details on used files, see GHA workflows. -> And yes, these results might change at any time. The dataset can be updated to include more files. -> The actual delays heavily depends on your CPU capabilities. The factors should remain the same. -> Keep in mind that the stats are generous and that Chardet accuracy vs our is measured using Chardet initial capability -> (e.g. Supported Encoding) Challenge-them if you want. - -## ✨ Installation - -Using pip: - -```sh -pip install charset-normalizer -U -``` - -## 🚀 Basic Usage - -### CLI -This package comes with a CLI. - -``` -usage: normalizer [-h] [-v] [-a] [-n] [-m] [-r] [-f] [-t THRESHOLD] - file [file ...] - -The Real First Universal Charset Detector. Discover originating encoding used -on text file. Normalize text to unicode. - -positional arguments: - files File(s) to be analysed - -optional arguments: - -h, --help show this help message and exit - -v, --verbose Display complementary information about file if any. - Stdout will contain logs about the detection process. - -a, --with-alternative - Output complementary possibilities if any. Top-level - JSON WILL be a list. - -n, --normalize Permit to normalize input file. If not set, program - does not write anything. - -m, --minimal Only output the charset detected to STDOUT. Disabling - JSON output. - -r, --replace Replace file when trying to normalize it instead of - creating a new one. - -f, --force Replace file without asking if you are sure, use this - flag with caution. - -t THRESHOLD, --threshold THRESHOLD - Define a custom maximum amount of chaos allowed in - decoded content. 0. <= chaos <= 1. - --version Show version information and exit. -``` - -```bash -normalizer ./data/sample.1.fr.srt -``` - -or - -```bash -python -m charset_normalizer ./data/sample.1.fr.srt -``` - -🎉 Since version 1.4.0 the CLI produce easily usable stdout result in JSON format. - -```json -{ - "path": "/home/default/projects/charset_normalizer/data/sample.1.fr.srt", - "encoding": "cp1252", - "encoding_aliases": [ - "1252", - "windows_1252" - ], - "alternative_encodings": [ - "cp1254", - "cp1256", - "cp1258", - "iso8859_14", - "iso8859_15", - "iso8859_16", - "iso8859_3", - "iso8859_9", - "latin_1", - "mbcs" - ], - "language": "French", - "alphabets": [ - "Basic Latin", - "Latin-1 Supplement" - ], - "has_sig_or_bom": false, - "chaos": 0.149, - "coherence": 97.152, - "unicode_path": null, - "is_preferred": true -} -``` - -### Python -*Just print out normalized text* -```python -from charset_normalizer import from_path - -results = from_path('./my_subtitle.srt') - -print(str(results.best())) -``` - -*Upgrade your code without effort* -```python -from charset_normalizer import detect -``` - -The above code will behave the same as **chardet**. We ensure that we offer the best (reasonable) BC result possible. - -See the docs for advanced usage : [readthedocs.io](https://charset-normalizer.readthedocs.io/en/latest/) - -## 😇 Why - -When I started using Chardet, I noticed that it was not suited to my expectations, and I wanted to propose a -reliable alternative using a completely different method. Also! I never back down on a good challenge! - -I **don't care** about the **originating charset** encoding, because **two different tables** can -produce **two identical rendered string.** -What I want is to get readable text, the best I can. - -In a way, **I'm brute forcing text decoding.** How cool is that ? 😎 - -Don't confuse package **ftfy** with charset-normalizer or chardet. ftfy goal is to repair Unicode string whereas charset-normalizer to convert raw file in unknown encoding to unicode. - -## 🍰 How - - - Discard all charset encoding table that could not fit the binary content. - - Measure noise, or the mess once opened (by chunks) with a corresponding charset encoding. - - Extract matches with the lowest mess detected. - - Additionally, we measure coherence / probe for a language. - -**Wait a minute**, what is noise/mess and coherence according to **YOU ?** - -*Noise :* I opened hundred of text files, **written by humans**, with the wrong encoding table. **I observed**, then -**I established** some ground rules about **what is obvious** when **it seems like** a mess (aka. defining noise in rendered text). - I know that my interpretation of what is noise is probably incomplete, feel free to contribute in order to - improve or rewrite it. - -*Coherence :* For each language there is on earth, we have computed ranked letter appearance occurrences (the best we can). So I thought -that intel is worth something here. So I use those records against decoded text to check if I can detect intelligent design. - -## ⚡ Known limitations - - - Language detection is unreliable when text contains two or more languages sharing identical letters. (eg. HTML (english tags) + Turkish content (Sharing Latin characters)) - - Every charset detector heavily depends on sufficient content. In common cases, do not bother run detection on very tiny content. - -## ⚠️ About Python EOLs - -**If you are running:** - -- Python >=2.7,<3.5: Unsupported -- Python 3.5: charset-normalizer < 2.1 -- Python 3.6: charset-normalizer < 3.1 -- Python 3.7: charset-normalizer < 4.0 - -Upgrade your Python interpreter as soon as possible. - -## 👤 Contributing - -Contributions, issues and feature requests are very much welcome.
-Feel free to check [issues page](https://github.com/ousret/charset_normalizer/issues) if you want to contribute. - -## 📝 License - -Copyright © [Ahmed TAHRI @Ousret](https://github.com/Ousret).
-This project is [MIT](https://github.com/Ousret/charset_normalizer/blob/master/LICENSE) licensed. - -Characters frequencies used in this project © 2012 [Denny Vrandečić](http://simia.net/letters/) - -## 💼 For Enterprise - -Professional support for charset-normalizer is available as part of the [Tidelift -Subscription][1]. Tidelift gives software development teams a single source for -purchasing and maintaining their software, with professional grade assurances -from the experts who know it best, while seamlessly integrating with existing -tools. - -[1]: https://tidelift.com/subscription/pkg/pypi-charset-normalizer?utm_source=pypi-charset-normalizer&utm_medium=readme - -[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/7297/badge)](https://www.bestpractices.dev/projects/7297) - -# Changelog -All notable changes to charset-normalizer will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - -## [3.4.4](https://github.com/Ousret/charset_normalizer/compare/3.4.2...3.4.4) (2025-10-13) - -### Changed -- Bound `setuptools` to a specific constraint `setuptools>=68,<=81`. -- Raised upper bound of mypyc for the optional pre-built extension to v1.18.2 - -### Removed -- `setuptools-scm` as a build dependency. - -### Misc -- Enforced hashes in `dev-requirements.txt` and created `ci-requirements.txt` for security purposes. -- Additional pre-built wheels for riscv64, s390x, and armv7l architectures. -- Restore ` multiple.intoto.jsonl` in GitHub releases in addition to individual attestation file per wheel. - -## [3.4.3](https://github.com/Ousret/charset_normalizer/compare/3.4.2...3.4.3) (2025-08-09) - -### Changed -- mypy(c) is no longer a required dependency at build time if `CHARSET_NORMALIZER_USE_MYPYC` isn't set to `1`. (#595) (#583) -- automatically lower confidence on small bytes samples that are not Unicode in `detect` output legacy function. (#391) - -### Added -- Custom build backend to overcome inability to mark mypy as an optional dependency in the build phase. -- Support for Python 3.14 - -### Fixed -- sdist archive contained useless directories. -- automatically fallback on valid UTF-16 or UTF-32 even if the md says it's noisy. (#633) - -### Misc -- SBOM are automatically published to the relevant GitHub release to comply with regulatory changes. - Each published wheel comes with its SBOM. We choose CycloneDX as the format. -- Prebuilt optimized wheel are no longer distributed by default for CPython 3.7 due to a change in cibuildwheel. - -## [3.4.2](https://github.com/Ousret/charset_normalizer/compare/3.4.1...3.4.2) (2025-05-02) - -### Fixed -- Addressed the DeprecationWarning in our CLI regarding `argparse.FileType` by backporting the target class into the package. (#591) -- Improved the overall reliability of the detector with CJK Ideographs. (#605) (#587) - -### Changed -- Optional mypyc compilation upgraded to version 1.15 for Python >= 3.8 - -## [3.4.1](https://github.com/Ousret/charset_normalizer/compare/3.4.0...3.4.1) (2024-12-24) - -### Changed -- Project metadata are now stored using `pyproject.toml` instead of `setup.cfg` using setuptools as the build backend. -- Enforce annotation delayed loading for a simpler and consistent types in the project. -- Optional mypyc compilation upgraded to version 1.14 for Python >= 3.8 - -### Added -- pre-commit configuration. -- noxfile. - -### Removed -- `build-requirements.txt` as per using `pyproject.toml` native build configuration. -- `bin/integration.py` and `bin/serve.py` in favor of downstream integration test (see noxfile). -- `setup.cfg` in favor of `pyproject.toml` metadata configuration. -- Unused `utils.range_scan` function. - -### Fixed -- Converting content to Unicode bytes may insert `utf_8` instead of preferred `utf-8`. (#572) -- Deprecation warning "'count' is passed as positional argument" when converting to Unicode bytes on Python 3.13+ - -## [3.4.0](https://github.com/Ousret/charset_normalizer/compare/3.3.2...3.4.0) (2024-10-08) - -### Added -- Argument `--no-preemptive` in the CLI to prevent the detector to search for hints. -- Support for Python 3.13 (#512) - -### Fixed -- Relax the TypeError exception thrown when trying to compare a CharsetMatch with anything else than a CharsetMatch. -- Improved the general reliability of the detector based on user feedbacks. (#520) (#509) (#498) (#407) (#537) -- Declared charset in content (preemptive detection) not changed when converting to utf-8 bytes. (#381) - -## [3.3.2](https://github.com/Ousret/charset_normalizer/compare/3.3.1...3.3.2) (2023-10-31) - -### Fixed -- Unintentional memory usage regression when using large payload that match several encoding (#376) -- Regression on some detection case showcased in the documentation (#371) - -### Added -- Noise (md) probe that identify malformed arabic representation due to the presence of letters in isolated form (credit to my wife) - -## [3.3.1](https://github.com/Ousret/charset_normalizer/compare/3.3.0...3.3.1) (2023-10-22) - -### Changed -- Optional mypyc compilation upgraded to version 1.6.1 for Python >= 3.8 -- Improved the general detection reliability based on reports from the community - -## [3.3.0](https://github.com/Ousret/charset_normalizer/compare/3.2.0...3.3.0) (2023-09-30) - -### Added -- Allow to execute the CLI (e.g. normalizer) through `python -m charset_normalizer.cli` or `python -m charset_normalizer` -- Support for 9 forgotten encoding that are supported by Python but unlisted in `encoding.aliases` as they have no alias (#323) - -### Removed -- (internal) Redundant utils.is_ascii function and unused function is_private_use_only -- (internal) charset_normalizer.assets is moved inside charset_normalizer.constant - -### Changed -- (internal) Unicode code blocks in constants are updated using the latest v15.0.0 definition to improve detection -- Optional mypyc compilation upgraded to version 1.5.1 for Python >= 3.8 - -### Fixed -- Unable to properly sort CharsetMatch when both chaos/noise and coherence were close due to an unreachable condition in \_\_lt\_\_ (#350) - -## [3.2.0](https://github.com/Ousret/charset_normalizer/compare/3.1.0...3.2.0) (2023-06-07) - -### Changed -- Typehint for function `from_path` no longer enforce `PathLike` as its first argument -- Minor improvement over the global detection reliability - -### Added -- Introduce function `is_binary` that relies on main capabilities, and optimized to detect binaries -- Propagate `enable_fallback` argument throughout `from_bytes`, `from_path`, and `from_fp` that allow a deeper control over the detection (default True) -- Explicit support for Python 3.12 - -### Fixed -- Edge case detection failure where a file would contain 'very-long' camel cased word (Issue #289) - -## [3.1.0](https://github.com/Ousret/charset_normalizer/compare/3.0.1...3.1.0) (2023-03-06) - -### Added -- Argument `should_rename_legacy` for legacy function `detect` and disregard any new arguments without errors (PR #262) - -### Removed -- Support for Python 3.6 (PR #260) - -### Changed -- Optional speedup provided by mypy/c 1.0.1 - -## [3.0.1](https://github.com/Ousret/charset_normalizer/compare/3.0.0...3.0.1) (2022-11-18) - -### Fixed -- Multi-bytes cutter/chunk generator did not always cut correctly (PR #233) - -### Changed -- Speedup provided by mypy/c 0.990 on Python >= 3.7 - -## [3.0.0](https://github.com/Ousret/charset_normalizer/compare/2.1.1...3.0.0) (2022-10-20) - -### Added -- Extend the capability of explain=True when cp_isolation contains at most two entries (min one), will log in details of the Mess-detector results -- Support for alternative language frequency set in charset_normalizer.assets.FREQUENCIES -- Add parameter `language_threshold` in `from_bytes`, `from_path` and `from_fp` to adjust the minimum expected coherence ratio -- `normalizer --version` now specify if current version provide extra speedup (meaning mypyc compilation whl) - -### Changed -- Build with static metadata using 'build' frontend -- Make the language detection stricter -- Optional: Module `md.py` can be compiled using Mypyc to provide an extra speedup up to 4x faster than v2.1 - -### Fixed -- CLI with opt --normalize fail when using full path for files -- TooManyAccentuatedPlugin induce false positive on the mess detection when too few alpha character have been fed to it -- Sphinx warnings when generating the documentation - -### Removed -- Coherence detector no longer return 'Simple English' instead return 'English' -- Coherence detector no longer return 'Classical Chinese' instead return 'Chinese' -- Breaking: Method `first()` and `best()` from CharsetMatch -- UTF-7 will no longer appear as "detected" without a recognized SIG/mark (is unreliable/conflict with ASCII) -- Breaking: Class aliases CharsetDetector, CharsetDoctor, CharsetNormalizerMatch and CharsetNormalizerMatches -- Breaking: Top-level function `normalize` -- Breaking: Properties `chaos_secondary_pass`, `coherence_non_latin` and `w_counter` from CharsetMatch -- Support for the backport `unicodedata2` - -## [3.0.0rc1](https://github.com/Ousret/charset_normalizer/compare/3.0.0b2...3.0.0rc1) (2022-10-18) - -### Added -- Extend the capability of explain=True when cp_isolation contains at most two entries (min one), will log in details of the Mess-detector results -- Support for alternative language frequency set in charset_normalizer.assets.FREQUENCIES -- Add parameter `language_threshold` in `from_bytes`, `from_path` and `from_fp` to adjust the minimum expected coherence ratio - -### Changed -- Build with static metadata using 'build' frontend -- Make the language detection stricter - -### Fixed -- CLI with opt --normalize fail when using full path for files -- TooManyAccentuatedPlugin induce false positive on the mess detection when too few alpha character have been fed to it - -### Removed -- Coherence detector no longer return 'Simple English' instead return 'English' -- Coherence detector no longer return 'Classical Chinese' instead return 'Chinese' - -## [3.0.0b2](https://github.com/Ousret/charset_normalizer/compare/3.0.0b1...3.0.0b2) (2022-08-21) - -### Added -- `normalizer --version` now specify if current version provide extra speedup (meaning mypyc compilation whl) - -### Removed -- Breaking: Method `first()` and `best()` from CharsetMatch -- UTF-7 will no longer appear as "detected" without a recognized SIG/mark (is unreliable/conflict with ASCII) - -### Fixed -- Sphinx warnings when generating the documentation - -## [3.0.0b1](https://github.com/Ousret/charset_normalizer/compare/2.1.0...3.0.0b1) (2022-08-15) - -### Changed -- Optional: Module `md.py` can be compiled using Mypyc to provide an extra speedup up to 4x faster than v2.1 - -### Removed -- Breaking: Class aliases CharsetDetector, CharsetDoctor, CharsetNormalizerMatch and CharsetNormalizerMatches -- Breaking: Top-level function `normalize` -- Breaking: Properties `chaos_secondary_pass`, `coherence_non_latin` and `w_counter` from CharsetMatch -- Support for the backport `unicodedata2` - -## [2.1.1](https://github.com/Ousret/charset_normalizer/compare/2.1.0...2.1.1) (2022-08-19) - -### Deprecated -- Function `normalize` scheduled for removal in 3.0 - -### Changed -- Removed useless call to decode in fn is_unprintable (#206) - -### Fixed -- Third-party library (i18n xgettext) crashing not recognizing utf_8 (PEP 263) with underscore from [@aleksandernovikov](https://github.com/aleksandernovikov) (#204) - -## [2.1.0](https://github.com/Ousret/charset_normalizer/compare/2.0.12...2.1.0) (2022-06-19) - -### Added -- Output the Unicode table version when running the CLI with `--version` (PR #194) - -### Changed -- Re-use decoded buffer for single byte character sets from [@nijel](https://github.com/nijel) (PR #175) -- Fixing some performance bottlenecks from [@deedy5](https://github.com/deedy5) (PR #183) - -### Fixed -- Workaround potential bug in cpython with Zero Width No-Break Space located in Arabic Presentation Forms-B, Unicode 1.1 not acknowledged as space (PR #175) -- CLI default threshold aligned with the API threshold from [@oleksandr-kuzmenko](https://github.com/oleksandr-kuzmenko) (PR #181) - -### Removed -- Support for Python 3.5 (PR #192) - -### Deprecated -- Use of backport unicodedata from `unicodedata2` as Python is quickly catching up, scheduled for removal in 3.0 (PR #194) - -## [2.0.12](https://github.com/Ousret/charset_normalizer/compare/2.0.11...2.0.12) (2022-02-12) - -### Fixed -- ASCII miss-detection on rare cases (PR #170) - -## [2.0.11](https://github.com/Ousret/charset_normalizer/compare/2.0.10...2.0.11) (2022-01-30) - -### Added -- Explicit support for Python 3.11 (PR #164) - -### Changed -- The logging behavior have been completely reviewed, now using only TRACE and DEBUG levels (PR #163 #165) - -## [2.0.10](https://github.com/Ousret/charset_normalizer/compare/2.0.9...2.0.10) (2022-01-04) - -### Fixed -- Fallback match entries might lead to UnicodeDecodeError for large bytes sequence (PR #154) - -### Changed -- Skipping the language-detection (CD) on ASCII (PR #155) - -## [2.0.9](https://github.com/Ousret/charset_normalizer/compare/2.0.8...2.0.9) (2021-12-03) - -### Changed -- Moderating the logging impact (since 2.0.8) for specific environments (PR #147) - -### Fixed -- Wrong logging level applied when setting kwarg `explain` to True (PR #146) - -## [2.0.8](https://github.com/Ousret/charset_normalizer/compare/2.0.7...2.0.8) (2021-11-24) -### Changed -- Improvement over Vietnamese detection (PR #126) -- MD improvement on trailing data and long foreign (non-pure latin) data (PR #124) -- Efficiency improvements in cd/alphabet_languages from [@adbar](https://github.com/adbar) (PR #122) -- call sum() without an intermediary list following PEP 289 recommendations from [@adbar](https://github.com/adbar) (PR #129) -- Code style as refactored by Sourcery-AI (PR #131) -- Minor adjustment on the MD around european words (PR #133) -- Remove and replace SRTs from assets / tests (PR #139) -- Initialize the library logger with a `NullHandler` by default from [@nmaynes](https://github.com/nmaynes) (PR #135) -- Setting kwarg `explain` to True will add provisionally (bounded to function lifespan) a specific stream handler (PR #135) - -### Fixed -- Fix large (misleading) sequence giving UnicodeDecodeError (PR #137) -- Avoid using too insignificant chunk (PR #137) - -### Added -- Add and expose function `set_logging_handler` to configure a specific StreamHandler from [@nmaynes](https://github.com/nmaynes) (PR #135) -- Add `CHANGELOG.md` entries, format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) (PR #141) - -## [2.0.7](https://github.com/Ousret/charset_normalizer/compare/2.0.6...2.0.7) (2021-10-11) -### Added -- Add support for Kazakh (Cyrillic) language detection (PR #109) - -### Changed -- Further, improve inferring the language from a given single-byte code page (PR #112) -- Vainly trying to leverage PEP263 when PEP3120 is not supported (PR #116) -- Refactoring for potential performance improvements in loops from [@adbar](https://github.com/adbar) (PR #113) -- Various detection improvement (MD+CD) (PR #117) - -### Removed -- Remove redundant logging entry about detected language(s) (PR #115) - -### Fixed -- Fix a minor inconsistency between Python 3.5 and other versions regarding language detection (PR #117 #102) - -## [2.0.6](https://github.com/Ousret/charset_normalizer/compare/2.0.5...2.0.6) (2021-09-18) -### Fixed -- Unforeseen regression with the loss of the backward-compatibility with some older minor of Python 3.5.x (PR #100) -- Fix CLI crash when using --minimal output in certain cases (PR #103) - -### Changed -- Minor improvement to the detection efficiency (less than 1%) (PR #106 #101) - -## [2.0.5](https://github.com/Ousret/charset_normalizer/compare/2.0.4...2.0.5) (2021-09-14) -### Changed -- The project now comply with: flake8, mypy, isort and black to ensure a better overall quality (PR #81) -- The BC-support with v1.x was improved, the old staticmethods are restored (PR #82) -- The Unicode detection is slightly improved (PR #93) -- Add syntax sugar \_\_bool\_\_ for results CharsetMatches list-container (PR #91) - -### Removed -- The project no longer raise warning on tiny content given for detection, will be simply logged as warning instead (PR #92) - -### Fixed -- In some rare case, the chunks extractor could cut in the middle of a multi-byte character and could mislead the mess detection (PR #95) -- Some rare 'space' characters could trip up the UnprintablePlugin/Mess detection (PR #96) -- The MANIFEST.in was not exhaustive (PR #78) - -## [2.0.4](https://github.com/Ousret/charset_normalizer/compare/2.0.3...2.0.4) (2021-07-30) -### Fixed -- The CLI no longer raise an unexpected exception when no encoding has been found (PR #70) -- Fix accessing the 'alphabets' property when the payload contains surrogate characters (PR #68) -- The logger could mislead (explain=True) on detected languages and the impact of one MBCS match (PR #72) -- Submatch factoring could be wrong in rare edge cases (PR #72) -- Multiple files given to the CLI were ignored when publishing results to STDOUT. (After the first path) (PR #72) -- Fix line endings from CRLF to LF for certain project files (PR #67) - -### Changed -- Adjust the MD to lower the sensitivity, thus improving the global detection reliability (PR #69 #76) -- Allow fallback on specified encoding if any (PR #71) - -## [2.0.3](https://github.com/Ousret/charset_normalizer/compare/2.0.2...2.0.3) (2021-07-16) -### Changed -- Part of the detection mechanism has been improved to be less sensitive, resulting in more accurate detection results. Especially ASCII. (PR #63) -- According to the community wishes, the detection will fall back on ASCII or UTF-8 in a last-resort case. (PR #64) - -## [2.0.2](https://github.com/Ousret/charset_normalizer/compare/2.0.1...2.0.2) (2021-07-15) -### Fixed -- Empty/Too small JSON payload miss-detection fixed. Report from [@tseaver](https://github.com/tseaver) (PR #59) - -### Changed -- Don't inject unicodedata2 into sys.modules from [@akx](https://github.com/akx) (PR #57) - -## [2.0.1](https://github.com/Ousret/charset_normalizer/compare/2.0.0...2.0.1) (2021-07-13) -### Fixed -- Make it work where there isn't a filesystem available, dropping assets frequencies.json. Report from [@sethmlarson](https://github.com/sethmlarson). (PR #55) -- Using explain=False permanently disable the verbose output in the current runtime (PR #47) -- One log entry (language target preemptive) was not show in logs when using explain=True (PR #47) -- Fix undesired exception (ValueError) on getitem of instance CharsetMatches (PR #52) - -### Changed -- Public function normalize default args values were not aligned with from_bytes (PR #53) - -### Added -- You may now use charset aliases in cp_isolation and cp_exclusion arguments (PR #47) - -## [2.0.0](https://github.com/Ousret/charset_normalizer/compare/1.4.1...2.0.0) (2021-07-02) -### Changed -- 4x to 5 times faster than the previous 1.4.0 release. At least 2x faster than Chardet. -- Accent has been made on UTF-8 detection, should perform rather instantaneous. -- The backward compatibility with Chardet has been greatly improved. The legacy detect function returns an identical charset name whenever possible. -- The detection mechanism has been slightly improved, now Turkish content is detected correctly (most of the time) -- The program has been rewritten to ease the readability and maintainability. (+Using static typing)+ -- utf_7 detection has been reinstated. - -### Removed -- This package no longer require anything when used with Python 3.5 (Dropped cached_property) -- Removed support for these languages: Catalan, Esperanto, Kazakh, Baque, Volapük, Azeri, Galician, Nynorsk, Macedonian, and Serbocroatian. -- The exception hook on UnicodeDecodeError has been removed. - -### Deprecated -- Methods coherence_non_latin, w_counter, chaos_secondary_pass of the class CharsetMatch are now deprecated and scheduled for removal in v3.0 - -### Fixed -- The CLI output used the relative path of the file(s). Should be absolute. - -## [1.4.1](https://github.com/Ousret/charset_normalizer/compare/1.4.0...1.4.1) (2021-05-28) -### Fixed -- Logger configuration/usage no longer conflict with others (PR #44) - -## [1.4.0](https://github.com/Ousret/charset_normalizer/compare/1.3.9...1.4.0) (2021-05-21) -### Removed -- Using standard logging instead of using the package loguru. -- Dropping nose test framework in favor of the maintained pytest. -- Choose to not use dragonmapper package to help with gibberish Chinese/CJK text. -- Require cached_property only for Python 3.5 due to constraint. Dropping for every other interpreter version. -- Stop support for UTF-7 that does not contain a SIG. -- Dropping PrettyTable, replaced with pure JSON output in CLI. - -### Fixed -- BOM marker in a CharsetNormalizerMatch instance could be False in rare cases even if obviously present. Due to the sub-match factoring process. -- Not searching properly for the BOM when trying utf32/16 parent codec. - -### Changed -- Improving the package final size by compressing frequencies.json. -- Huge improvement over the larges payload. - -### Added -- CLI now produces JSON consumable output. -- Return ASCII if given sequences fit. Given reasonable confidence. - -## [1.3.9](https://github.com/Ousret/charset_normalizer/compare/1.3.8...1.3.9) (2021-05-13) - -### Fixed -- In some very rare cases, you may end up getting encode/decode errors due to a bad bytes payload (PR #40) - -## [1.3.8](https://github.com/Ousret/charset_normalizer/compare/1.3.7...1.3.8) (2021-05-12) - -### Fixed -- Empty given payload for detection may cause an exception if trying to access the `alphabets` property. (PR #39) - -## [1.3.7](https://github.com/Ousret/charset_normalizer/compare/1.3.6...1.3.7) (2021-05-12) - -### Fixed -- The legacy detect function should return UTF-8-SIG if sig is present in the payload. (PR #38) - -## [1.3.6](https://github.com/Ousret/charset_normalizer/compare/1.3.5...1.3.6) (2021-02-09) - -### Changed -- Amend the previous release to allow prettytable 2.0 (PR #35) - -## [1.3.5](https://github.com/Ousret/charset_normalizer/compare/1.3.4...1.3.5) (2021-02-08) - -### Fixed -- Fix error while using the package with a python pre-release interpreter (PR #33) - -### Changed -- Dependencies refactoring, constraints revised. - -### Added -- Add python 3.9 and 3.10 to the supported interpreters - -MIT License - -Copyright (c) 2025 TAHRI Ahmed R. - -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/apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/RECORD b/apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/RECORD deleted file mode 100755 index 98db0d97..00000000 --- a/apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/RECORD +++ /dev/null @@ -1,24 +0,0 @@ -../../bin/normalizer,sha256=9v9CiM1SMj9hAM5YRMeRilB5D2s9HWSQ_qInrU2KpQU,253 -charset_normalizer-3.4.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -charset_normalizer-3.4.4.dist-info/METADATA,sha256=jVuUFBti8dav19YLvWissTihVdF2ozUY4KKMw7jdkBQ,37303 -charset_normalizer-3.4.4.dist-info/RECORD,, -charset_normalizer-3.4.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -charset_normalizer-3.4.4.dist-info/WHEEL,sha256=ylJURW79exB2C-59Cgd1o0-VkgYeH2GaNb8U43WRbwg,187 -charset_normalizer-3.4.4.dist-info/entry_points.txt,sha256=ADSTKrkXZ3hhdOVFi6DcUEHQRS0xfxDIE_pEz4wLIXA,65 -charset_normalizer-3.4.4.dist-info/licenses/LICENSE,sha256=bQ1Bv-FwrGx9wkjJpj4lTQ-0WmDVCoJX0K-SxuJJuIc,1071 -charset_normalizer-3.4.4.dist-info/top_level.txt,sha256=7ASyzePr8_xuZWJsnqJjIBtyV8vhEo0wBCv1MPRRi3Q,19 -charset_normalizer/__init__.py,sha256=OKRxRv2Zhnqk00tqkN0c1BtJjm165fWXLydE52IKuHc,1590 -charset_normalizer/__main__.py,sha256=yzYxMR-IhKRHYwcSlavEv8oGdwxsR89mr2X09qXGdps,109 -charset_normalizer/api.py,sha256=V07i8aVeCD8T2fSia3C-fn0i9t8qQguEBhsqszg32Ns,22668 -charset_normalizer/cd.py,sha256=WKTo1HDb-H9HfCDc3Bfwq5jzS25Ziy9SE2a74SgTq88,12522 -charset_normalizer/cli/__init__.py,sha256=D8I86lFk2-py45JvqxniTirSj_sFyE6sjaY_0-G1shc,136 -charset_normalizer/cli/__main__.py,sha256=dMaXG6IJXRvqq8z2tig7Qb83-BpWTln55ooiku5_uvg,12646 -charset_normalizer/constant.py,sha256=7UVY4ldYhmQMHUdgQ_sgZmzcQ0xxYxpBunqSZ-XJZ8U,42713 -charset_normalizer/legacy.py,sha256=sYBzSpzsRrg_wF4LP536pG64BItw7Tqtc3SMQAHvFLM,2731 -charset_normalizer/md.cpython-39-aarch64-linux-gnu.so,sha256=Fl5b5yFpdjEkjaOr3bUxLjAmBYD37YTHfMCVs6SBh6A,201304 -charset_normalizer/md.py,sha256=-_oN3h3_X99nkFfqamD3yu45DC_wfk5odH0Tr_CQiXs,20145 -charset_normalizer/md__mypyc.cpython-39-aarch64-linux-gnu.so,sha256=nAx9ZddHDN7KxsVUxkP8Qen7djjHdO1_0_bP_aqC34A,324120 -charset_normalizer/models.py,sha256=lKXhOnIPtiakbK3i__J9wpOfzx3JDTKj7Dn3Rg0VaRI,12394 -charset_normalizer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -charset_normalizer/utils.py,sha256=sTejPgrdlNsKNucZfJCxJ95lMTLA0ShHLLE3n5wpT9Q,12170 -charset_normalizer/version.py,sha256=nKE4qBNk5WA4LIJ_yIH_aSDfvtsyizkWMg-PUG-UZVk,115 diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/REQUESTED b/apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/REQUESTED deleted file mode 100755 index e69de29b..00000000 diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/WHEEL b/apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/WHEEL deleted file mode 100755 index 739423f0..00000000 --- a/apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/WHEEL +++ /dev/null @@ -1,7 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (80.9.0) -Root-Is-Purelib: false -Tag: cp39-cp39-manylinux_2_17_aarch64 -Tag: cp39-cp39-manylinux2014_aarch64 -Tag: cp39-cp39-manylinux_2_28_aarch64 - diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/licenses/LICENSE b/apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/licenses/LICENSE deleted file mode 100755 index 9725772c..00000000 --- a/apps/bitwarden_event_logs/lib/charset_normalizer-3.4.4.dist-info/licenses/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2025 TAHRI Ahmed R. - -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/apps/bitwarden_event_logs/lib/charset_normalizer/__init__.py b/apps/bitwarden_event_logs/lib/charset_normalizer/__init__.py deleted file mode 100755 index 0d3a3799..00000000 --- a/apps/bitwarden_event_logs/lib/charset_normalizer/__init__.py +++ /dev/null @@ -1,48 +0,0 @@ -""" -Charset-Normalizer -~~~~~~~~~~~~~~ -The Real First Universal Charset Detector. -A library that helps you read text from an unknown charset encoding. -Motivated by chardet, This package is trying to resolve the issue by taking a new approach. -All IANA character set names for which the Python core library provides codecs are supported. - -Basic usage: - >>> from charset_normalizer import from_bytes - >>> results = from_bytes('Bсеки човек има право на образование. Oбразованието!'.encode('utf_8')) - >>> best_guess = results.best() - >>> str(best_guess) - 'Bсеки човек има право на образование. Oбразованието!' - -Others methods and usages are available - see the full documentation -at . -:copyright: (c) 2021 by Ahmed TAHRI -:license: MIT, see LICENSE for more details. -""" - -from __future__ import annotations - -import logging - -from .api import from_bytes, from_fp, from_path, is_binary -from .legacy import detect -from .models import CharsetMatch, CharsetMatches -from .utils import set_logging_handler -from .version import VERSION, __version__ - -__all__ = ( - "from_fp", - "from_path", - "from_bytes", - "is_binary", - "detect", - "CharsetMatch", - "CharsetMatches", - "__version__", - "VERSION", - "set_logging_handler", -) - -# Attach a NullHandler to the top level logger by default -# https://docs.python.org/3.3/howto/logging.html#configuring-logging-for-a-library - -logging.getLogger("charset_normalizer").addHandler(logging.NullHandler()) diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer/__main__.py b/apps/bitwarden_event_logs/lib/charset_normalizer/__main__.py deleted file mode 100755 index e0e76f7b..00000000 --- a/apps/bitwarden_event_logs/lib/charset_normalizer/__main__.py +++ /dev/null @@ -1,6 +0,0 @@ -from __future__ import annotations - -from .cli import cli_detect - -if __name__ == "__main__": - cli_detect() diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer/__pycache__/__init__.cpython-39.pyc b/apps/bitwarden_event_logs/lib/charset_normalizer/__pycache__/__init__.cpython-39.pyc deleted file mode 100755 index 964808624594b0e02cf7607ae5da8da9ea3eb087..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1618 zcmcIkOK%)S5T1Q_AG7PkaR?xBDa66MQZfTMAhZe=PB4n4c#UksVJ@xSnckh2dmifU zS+g7vL`eLNSa9KHkRmJu<}Zw|bLEu3z=`Uajct@%8EHyY)zw{JJ?ZMIZQ=Lck3aDb zTbA{=5w3qt6h5)6n))7@!1Ao17Su&8s(baC;TxjiHG*caBAU^Pw-U9yR@C;|QOE1x zc{6B>U;fY)mts)j<^xs^lp}XSFA<1yj#(_w~n&y-DZtvZv?%Q+Os>z zy?J7t)KK3zu!jBYX~VMYM3icuMpW>xnY6$8&u1To4E7ln@EMm%!xu51GpSHs zUWHGYX1-3OV{d`r6G`O)bVxOXOr#1HNd}V905tnb!&D{_&=|7#C{B)J@GJkoVm}FZ zJag>bMDsb-EP%sWTBaQc>&|egjVqJ zwX?y-!;A0o=lSpX8C?93pXa|C0rIo_kNgz!zb?MbPf>Rc`JYBU&wnX3zm=A!s65M` zJFxdU^~KZt{M`+w%s(4!WO_kqO=@bnU(7Q8?J5d zVY#J(h+&litVA4Cd9R8>=adUN5e)8Ofvet_W?1|n@w14O8`%H9&L{gw#y!iOiN)?BShucFU*=4)`}jD(LJ?UJ9S zMYm*9s>7no6`AlDCk|g|;hQ*!!+OzQj^-}aepuYNE-{6cx3TGoNM#yc+_*T&- z1oKZwVG}aVbS4=gvTp3#OE5))ro8fU<<$5qT7u1}zbO3{tqA8&s4M@)N~STJmGkxz zdxgg!GoGms=(#&%`UwWkWMR`FcwEI^^v2j6OW|irzJqs4^3Q>rq}o-fz3 zlsn=2n96{~gw3&;Oe&2sb{)sX4fvYQaS7ou*MvCfqUbz8{Fz{zrpjs^N%Ky>-l@M` M>(|!IUvIAc3vk~&V*mgE diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer/__pycache__/api.cpython-39.pyc b/apps/bitwarden_event_logs/lib/charset_normalizer/__pycache__/api.cpython-39.pyc deleted file mode 100755 index 567d4f39f4e4d56a2e10a8f9dcd93e1af6c30ed7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11637 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 diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer/__pycache__/constant.cpython-39.pyc b/apps/bitwarden_event_logs/lib/charset_normalizer/__pycache__/constant.cpython-39.pyc deleted file mode 100755 index 0c276ac2390ed54f9d5ce46893f22a368ba17acc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27335 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 P+Ep~;xUkL?t;2r-bOeVB diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer/__pycache__/md.cpython-39.pyc b/apps/bitwarden_event_logs/lib/charset_normalizer/__pycache__/md.cpython-39.pyc deleted file mode 100755 index 40aa659ea2289e14ca04896cf364ae19c24333e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16835 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 B5TXD8 diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer/__pycache__/utils.cpython-39.pyc b/apps/bitwarden_event_logs/lib/charset_normalizer/__pycache__/utils.cpython-39.pyc deleted file mode 100755 index c7aded9cffc3621733473909b625e16a16a3a0f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9110 zcmcIpUu@h)dgqYbzgo$PEK9bNI6TKrypAn7PR?=CoG)u>Wp6C49Ifm)oA_92hmvS< zm#ZPyj#LW0MDF!^Xs!X;zO=bk>ZJt?6n!pG6b1U+m!gM0@U7^R`_cjh+Q7YY`um2v zt3PYEha%kthr{`XGv9pwzmHabe^$Zo#h-rZ{AgTJ{+S+j|8jVEM^TtRttkpuICGUc zt1?D?)m5viylYiW-u0@EyXGe9$!b#0(cM%%T}{h=!p+pP)vW9%-M)IRnv?yM+g~53 z4#S3nXS$mZCea3i> z_w#`-RDOUD-q$x-^~f$rPC}mHLlSay7v!LX4D&-0^6V~1yxIsKm5}FpAcy%833~(|iUaWj>2p=jBMh9J$Wl!^mrV&era{ z&MS)=U~l4@=QqC4`Az;lR`@nIv2Tvw0;C`Ri?|N*4|w&9EdP*ygc%pOT2b=9`jGT2 z&!Rr7(eMH*aJ+_J#`93AQ4@CEZUmM)Y1ce%3yh{L*Rg!tM>pe&mRYlE%QlTKIt^~Q zE{$KY8@8|lkLEu+({cl6VJ)zGmwU))=TtOMEKkf%mdaPmnZn$)N;Fa`lnZ9%#`Wv7 zbMwVXvr?KVO&8{(7v_rQ_1Q|Lba}dHcHb$LCrcBB`O<7T8og02VWpxuS14a8R?Lan znaibeaWZ@yX3LX>xewwg=C$Gn@6XOn;?4P~i`f2FX{Io37N)0X+e;q88Z)zH zbK>1==ET*)Tw!9qI9H(q*R79zXWfo8KM;|=;CXIdjWV@mOIWqQ7Ew~zK}$4hbjZ$6 zMJJ7Gf=n4lXMG@0wpgfavkmsX(iqm2KwUe|6#9PvGgnd0!N7&=uF_oKTKlfI?}_$3 z*}kXR_jLQ7p*vWdEl;gy-!IKyHLlDRZWi19!o-dF;(AVwPZTFh)6<3Z0r@mpDir66 zl~QH>KnyL-P28B7nl9dIzdB!UNa9X}K-CD1;{-sA3odi2Atg*M!p~xqG09=P>r@`ncB&#{H(-YOIXgL2cY> zHvRDhC%9(`ZZ}N(E~L_Qy(JoKjE{|tyUxNmk>0lhv*C$4q;%aDYgJ7?R<-0WR6Y4~HIe);HJN;*rjlQYW0<*z zk?=x_j!QCf44oY|hRh!dLH3YsshokaYM=#rsB-lPeA1@638v=ta>NWlqVaGLO+Fz; zajW)oC|bSg`3{jIN`M4T5NWk#5ot~%$ZL{j)of>)&7%a7*q3yRo9D4m)DO+|?%M4$ zpT&?b9juyVI@8sxDo$c-Jqy`2LZj?8>^;Y$qtOv}9WPWiA)^5fwXIAlw-0}$Zm1iO z$WXhhh`~@*A+Z%@b&zB>udZLq8u-oGfpEYrBUrYL8?iVT!fGtphPSxr)Evt-NHB~= z;nfYx@SVnzYa3l98k4J(^ID{Nf{T8j6eXO%uKRgCCPjaDu8A2@RxWPRn$-*jO!?eH zowj7g(2H0#rcNTQ4ze@@b@CY7Lyt zMAU`(ZA@Xj&8@a@GI1JXd+;IdbUAb$4?6vu0cglSWGd%r-mjT^>LjP!N8O1R=)xMLS=Q>WBpp$yrO53mW0(@hNG1n$j9bVyox3FOHLoqgu2U@ z>wx>@Vw4VHNxXYd?1~QAu29)hwiSOcgu{X03`@a*qQ`Ej?RL)K0D4<&E|Gm`(Hj5y z*IzthZ$DzMJYtvTBUZgcgH`pqhpUE*Lv>S*?>DWQEd@)^hMmKlLx2Baazt~x2``EO z;vOcnPeVk9VqU_?p3{&G?Vg5gX?z;AY+E6wKVW4zW+35u_u!V%X*HX+c)+Ac@85n9 z+t(1_n0GDVSPQQG386q7Ra7y7cF*QC-u37I7po9y2rQ&cX#2zJz>YeR72A(rlJL8; z@&jzV*G8y5cMeML2Arii2M_5Z%!}|OQSY7#l!0K9IzHse- z#cBm{#|p6egC}5h^_#~kc@;tDjp?UTimb^pd|%7`@(CD~zIlxF`KyJ})A4~On8C8; z{KXTndG{N~CQ2fBzFeAkIzB`nsQrRd`^zU_H2ptf^g5uwyhrxV@3y?+8ok_Ks?5Se z7blIW*}0jgV>#Nz(sXUsw<93lL}U3diXT|B_llK~T# zso57D9Kg2FdlQb-tJl58-(gP2ES2e9Rh**N4x!m&1Zc~4ILCYnJ_6-8l|{xGSMR3- zHi@uKMx!p5p&=c3e#y><_Q z7tE`~M;=3lq?xyukQaX36qo)1%l$hpW&JEpwI>#X>4yj8mRK8m+x2Re>t7n{zK1O| ztFa~q@Tz!^8o9dc$t{jPeYwBK^bWmlpugv^d)Pu~$3BF!mg2n>dy-C{t1B9faZLsk zdU@&*I~u#fzxo?6;X8Ocf=z4+#Ovfgp2$s#iCT^BoPQ+b=v0c9W7$p`@^f(uBM%jW zf?mp?eeog&;8D}Qz1p%HH9P9_ZA;Xa&2>cZG4F|SonHzay5P5%zl9D3BLUk~{eeLJ zlm%KG90q!jAm1M(L!_FU>Te)Zxs&25*SP+fx`{9!Gc{C65-X~>N&!r$Aq6OJ=%K!q z-d46UJjqkr>V_U^&0M)L75LK5eDSI@SAqcS#)e}fEFI7nNCBd8jFtb1EX

b;vL>Iy0A1Xglx7i&eyPH&4($?RT zth{LpXK~GNe8cK+thS3`G+d_|kwMHdAjif6;xCS#;RFVif@F#)*SeI!&QYcnESj&K z2OXmXbuV^D7VNnD>ID4gSIeRTbdac1%tOv^E_n43A}F~|5EP}HEK5Sbvus!upI{86`*qx&EYoBzC&)Y! z8>LLk`bGH?0Yr*u-f1sGPlE_*5=^hkcoE#9bpdC zlIuT#_5KuPMyO?Cy@aX^3X{IHl-JjSWoTNrX12?g%EHX4vKPpSQW5tQZSrHn0)Aov z&HD2<8szPaz%x+yjEg?<6~soy%a7R0C~+;*Y=@ezaTG`J2r1a4(fic6)KC_?pYa(f zBZSl$BCK?mDp@hOOjRcdTCBprK>}~jfXgC_XkOb zHjyZjHtaWZq*>DNv5w(`PQ^Zx!82;od$NrLhaUtdlADONZlB&r!PlmiunjLU__Ql#S#n_0rE0$1>$5pd^1{4L3o>2w_qKLMRWvrXC&P;bSy#H)Kh~ zuTc|HqoIisaczbaP|$MujQAn-e+x}D)Ap*SwbNaw>JG;MwF}XqovgHO1qe85UaJvA z%&GPV9&D`{ufX>+tsnC4J9WoJHXW>ehZskOQi0La)gd+nT?O-0u#gQ?-R*Y(t4W=q zS@=<(4j|QmYEUv_R=lB*44iTV(I`m7)_*Jw)ufHeJG`=OIEzMb&od}kFq#(Xjh4U_ z7_Mz0GPro|Bx;Qktug6hdcQUp9O`M@vwVZXR#YS4l4LX@H==sxA{DhzGBfs^v2FdB z=mWAaJzXmIkOt0S=gs$sG!$AWNtmw3f>P;7mfQB0wAb?|Pa%#8oVuO&QMx^O%0+$E zB{6S5*KOa&f%CARRM&-a5ycUo(7dRhauq9(#;UOts{+xt^CLCrs=`}%=vLj}EKm|c zL7UMSOppx!bo5bW+m_>)f#8m2baMlI-btZQzl7i{$}HKzH2SE*Ac761V}vS!u&w%4 zxE6TSCR2Ib&1 z=Odyxxh5q+LWnqsi9ezh$pEY$?S8}}XM9GM$m8~QJ{x5^zRs7XDoVjHVhcaY)|TtR-=bc2n5lQaJX zGv%y}zOXO0gl^R6drd|Bj47Y9)nltaYoGWn5>)y?_K^I4Y+0on>Y7(!0Pv6; z*2AyygF4wXlWeG1q=v*Le?a^(^`+n1qTV(&e?rYqsrizcKc(i+sQED(e7PZ6sp69% ziHzOg)m*Iw&1~m()sYy0QfPqhBvPi#+VV=o?(~TF?)NYU>-i3eH&siOJ{jcGcONO{ zBOkC53`(v4bZm`Rs1ariQ*Y zhzrzQq=x7utGlv@Dz4DuRccDqP*g3ZsiA_7pr9-k0xBfPBwZ#@vETiK#y+Ly2h@;5 zi;cNnDo@SE7KUiLi-$6vlm(3R+i_-b3CvS`^4>WZCi>?(e>JC{=u5MqXK-b4_2Ww8 t8fae`-09NW_s;YFyPk{ZWQPsR93C7R)Q6uL8Xn52kQ4P6BO}Ux{x^f0JzoF- diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer/__pycache__/version.cpython-39.pyc b/apps/bitwarden_event_logs/lib/charset_normalizer/__pycache__/version.cpython-39.pyc deleted file mode 100755 index ec02afaa16985abfe81ac7f7df8c71293a42ded7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 309 zcmYjKJ5B>J6twpj*bs>$AgH#n3sNFPlY%BliA1r@le0Ep;k7N>8z83vLBo~YQgH<; zyeI`z%zN`bGi)}iQNr2#hxDm_jp9G@B(4y`{xmflwTNS$Dcommh7r%(T;#6>FWzul z@^X!uYIj^Oo~`lXWGkGPM%Uk?UxVY6X>9@vY36t3Ihm7r#AMl2aRR_i2M}w3yCDot z0JtoD#VNqvCT&Y^l?-umyI8HSZWB yLI+UhejI8_2vu@Jy9egH2%wGYnUXu<=su3vmw0f|8$Kv;iBpWP5*5W!R^VSF7FMMI diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer/api.py b/apps/bitwarden_event_logs/lib/charset_normalizer/api.py deleted file mode 100755 index ebd96390..00000000 --- a/apps/bitwarden_event_logs/lib/charset_normalizer/api.py +++ /dev/null @@ -1,669 +0,0 @@ -from __future__ import annotations - -import logging -from os import PathLike -from typing import BinaryIO - -from .cd import ( - coherence_ratio, - encoding_languages, - mb_encoding_languages, - merge_coherence_ratios, -) -from .constant import IANA_SUPPORTED, TOO_BIG_SEQUENCE, TOO_SMALL_SEQUENCE, TRACE -from .md import mess_ratio -from .models import CharsetMatch, CharsetMatches -from .utils import ( - any_specified_encoding, - cut_sequence_chunks, - iana_name, - identify_sig_or_bom, - is_cp_similar, - is_multi_byte_encoding, - should_strip_sig_or_bom, -) - -logger = logging.getLogger("charset_normalizer") -explain_handler = logging.StreamHandler() -explain_handler.setFormatter( - logging.Formatter("%(asctime)s | %(levelname)s | %(message)s") -) - - -def from_bytes( - sequences: bytes | bytearray, - steps: int = 5, - chunk_size: int = 512, - threshold: float = 0.2, - cp_isolation: list[str] | None = None, - cp_exclusion: list[str] | None = None, - preemptive_behaviour: bool = True, - explain: bool = False, - language_threshold: float = 0.1, - enable_fallback: bool = True, -) -> CharsetMatches: - """ - Given a raw bytes sequence, return the best possibles charset usable to render str objects. - If there is no results, it is a strong indicator that the source is binary/not text. - By default, the process will extract 5 blocks of 512o each to assess the mess and coherence of a given sequence. - And will give up a particular code page after 20% of measured mess. Those criteria are customizable at will. - - The preemptive behavior DOES NOT replace the traditional detection workflow, it prioritize a particular code page - but never take it for granted. Can improve the performance. - - You may want to focus your attention to some code page or/and not others, use cp_isolation and cp_exclusion for that - purpose. - - This function will strip the SIG in the payload/sequence every time except on UTF-16, UTF-32. - By default the library does not setup any handler other than the NullHandler, if you choose to set the 'explain' - toggle to True it will alter the logger configuration to add a StreamHandler that is suitable for debugging. - Custom logging format and handler can be set manually. - """ - - if not isinstance(sequences, (bytearray, bytes)): - raise TypeError( - "Expected object of type bytes or bytearray, got: {}".format( - type(sequences) - ) - ) - - if explain: - previous_logger_level: int = logger.level - logger.addHandler(explain_handler) - logger.setLevel(TRACE) - - length: int = len(sequences) - - if length == 0: - logger.debug("Encoding detection on empty bytes, assuming utf_8 intention.") - if explain: # Defensive: ensure exit path clean handler - logger.removeHandler(explain_handler) - logger.setLevel(previous_logger_level or logging.WARNING) - return CharsetMatches([CharsetMatch(sequences, "utf_8", 0.0, False, [], "")]) - - if cp_isolation is not None: - logger.log( - TRACE, - "cp_isolation is set. use this flag for debugging purpose. " - "limited list of encoding allowed : %s.", - ", ".join(cp_isolation), - ) - cp_isolation = [iana_name(cp, False) for cp in cp_isolation] - else: - cp_isolation = [] - - if cp_exclusion is not None: - logger.log( - TRACE, - "cp_exclusion is set. use this flag for debugging purpose. " - "limited list of encoding excluded : %s.", - ", ".join(cp_exclusion), - ) - cp_exclusion = [iana_name(cp, False) for cp in cp_exclusion] - else: - cp_exclusion = [] - - if length <= (chunk_size * steps): - logger.log( - TRACE, - "override steps (%i) and chunk_size (%i) as content does not fit (%i byte(s) given) parameters.", - steps, - chunk_size, - length, - ) - steps = 1 - chunk_size = length - - if steps > 1 and length / steps < chunk_size: - chunk_size = int(length / steps) - - is_too_small_sequence: bool = len(sequences) < TOO_SMALL_SEQUENCE - is_too_large_sequence: bool = len(sequences) >= TOO_BIG_SEQUENCE - - if is_too_small_sequence: - logger.log( - TRACE, - "Trying to detect encoding from a tiny portion of ({}) byte(s).".format( - length - ), - ) - elif is_too_large_sequence: - logger.log( - TRACE, - "Using lazy str decoding because the payload is quite large, ({}) byte(s).".format( - length - ), - ) - - prioritized_encodings: list[str] = [] - - specified_encoding: str | None = ( - any_specified_encoding(sequences) if preemptive_behaviour else None - ) - - if specified_encoding is not None: - prioritized_encodings.append(specified_encoding) - logger.log( - TRACE, - "Detected declarative mark in sequence. Priority +1 given for %s.", - specified_encoding, - ) - - tested: set[str] = set() - tested_but_hard_failure: list[str] = [] - tested_but_soft_failure: list[str] = [] - - fallback_ascii: CharsetMatch | None = None - fallback_u8: CharsetMatch | None = None - fallback_specified: CharsetMatch | None = None - - results: CharsetMatches = CharsetMatches() - - early_stop_results: CharsetMatches = CharsetMatches() - - sig_encoding, sig_payload = identify_sig_or_bom(sequences) - - if sig_encoding is not None: - prioritized_encodings.append(sig_encoding) - logger.log( - TRACE, - "Detected a SIG or BOM mark on first %i byte(s). Priority +1 given for %s.", - len(sig_payload), - sig_encoding, - ) - - prioritized_encodings.append("ascii") - - if "utf_8" not in prioritized_encodings: - prioritized_encodings.append("utf_8") - - for encoding_iana in prioritized_encodings + IANA_SUPPORTED: - if cp_isolation and encoding_iana not in cp_isolation: - continue - - if cp_exclusion and encoding_iana in cp_exclusion: - continue - - if encoding_iana in tested: - continue - - tested.add(encoding_iana) - - decoded_payload: str | None = None - bom_or_sig_available: bool = sig_encoding == encoding_iana - strip_sig_or_bom: bool = bom_or_sig_available and should_strip_sig_or_bom( - encoding_iana - ) - - if encoding_iana in {"utf_16", "utf_32"} and not bom_or_sig_available: - logger.log( - TRACE, - "Encoding %s won't be tested as-is because it require a BOM. Will try some sub-encoder LE/BE.", - encoding_iana, - ) - continue - if encoding_iana in {"utf_7"} and not bom_or_sig_available: - logger.log( - TRACE, - "Encoding %s won't be tested as-is because detection is unreliable without BOM/SIG.", - encoding_iana, - ) - continue - - try: - is_multi_byte_decoder: bool = is_multi_byte_encoding(encoding_iana) - except (ModuleNotFoundError, ImportError): - logger.log( - TRACE, - "Encoding %s does not provide an IncrementalDecoder", - encoding_iana, - ) - continue - - try: - if is_too_large_sequence and is_multi_byte_decoder is False: - str( - ( - sequences[: int(50e4)] - if strip_sig_or_bom is False - else sequences[len(sig_payload) : int(50e4)] - ), - encoding=encoding_iana, - ) - else: - decoded_payload = str( - ( - sequences - if strip_sig_or_bom is False - else sequences[len(sig_payload) :] - ), - encoding=encoding_iana, - ) - except (UnicodeDecodeError, LookupError) as e: - if not isinstance(e, LookupError): - logger.log( - TRACE, - "Code page %s does not fit given bytes sequence at ALL. %s", - encoding_iana, - str(e), - ) - tested_but_hard_failure.append(encoding_iana) - continue - - similar_soft_failure_test: bool = False - - for encoding_soft_failed in tested_but_soft_failure: - if is_cp_similar(encoding_iana, encoding_soft_failed): - similar_soft_failure_test = True - break - - if similar_soft_failure_test: - logger.log( - TRACE, - "%s is deemed too similar to code page %s and was consider unsuited already. Continuing!", - encoding_iana, - encoding_soft_failed, - ) - continue - - r_ = range( - 0 if not bom_or_sig_available else len(sig_payload), - length, - int(length / steps), - ) - - multi_byte_bonus: bool = ( - is_multi_byte_decoder - and decoded_payload is not None - and len(decoded_payload) < length - ) - - if multi_byte_bonus: - logger.log( - TRACE, - "Code page %s is a multi byte encoding table and it appear that at least one character " - "was encoded using n-bytes.", - encoding_iana, - ) - - max_chunk_gave_up: int = int(len(r_) / 4) - - max_chunk_gave_up = max(max_chunk_gave_up, 2) - early_stop_count: int = 0 - lazy_str_hard_failure = False - - md_chunks: list[str] = [] - md_ratios = [] - - try: - for chunk in cut_sequence_chunks( - sequences, - encoding_iana, - r_, - chunk_size, - bom_or_sig_available, - strip_sig_or_bom, - sig_payload, - is_multi_byte_decoder, - decoded_payload, - ): - md_chunks.append(chunk) - - md_ratios.append( - mess_ratio( - chunk, - threshold, - explain is True and 1 <= len(cp_isolation) <= 2, - ) - ) - - if md_ratios[-1] >= threshold: - early_stop_count += 1 - - if (early_stop_count >= max_chunk_gave_up) or ( - bom_or_sig_available and strip_sig_or_bom is False - ): - break - except ( - UnicodeDecodeError - ) as e: # Lazy str loading may have missed something there - logger.log( - TRACE, - "LazyStr Loading: After MD chunk decode, code page %s does not fit given bytes sequence at ALL. %s", - encoding_iana, - str(e), - ) - early_stop_count = max_chunk_gave_up - lazy_str_hard_failure = True - - # We might want to check the sequence again with the whole content - # Only if initial MD tests passes - if ( - not lazy_str_hard_failure - and is_too_large_sequence - and not is_multi_byte_decoder - ): - try: - sequences[int(50e3) :].decode(encoding_iana, errors="strict") - except UnicodeDecodeError as e: - logger.log( - TRACE, - "LazyStr Loading: After final lookup, code page %s does not fit given bytes sequence at ALL. %s", - encoding_iana, - str(e), - ) - tested_but_hard_failure.append(encoding_iana) - continue - - mean_mess_ratio: float = sum(md_ratios) / len(md_ratios) if md_ratios else 0.0 - if mean_mess_ratio >= threshold or early_stop_count >= max_chunk_gave_up: - tested_but_soft_failure.append(encoding_iana) - logger.log( - TRACE, - "%s was excluded because of initial chaos probing. Gave up %i time(s). " - "Computed mean chaos is %f %%.", - encoding_iana, - early_stop_count, - round(mean_mess_ratio * 100, ndigits=3), - ) - # Preparing those fallbacks in case we got nothing. - if ( - enable_fallback - and encoding_iana - in ["ascii", "utf_8", specified_encoding, "utf_16", "utf_32"] - and not lazy_str_hard_failure - ): - fallback_entry = CharsetMatch( - sequences, - encoding_iana, - threshold, - bom_or_sig_available, - [], - decoded_payload, - preemptive_declaration=specified_encoding, - ) - if encoding_iana == specified_encoding: - fallback_specified = fallback_entry - elif encoding_iana == "ascii": - fallback_ascii = fallback_entry - else: - fallback_u8 = fallback_entry - continue - - logger.log( - TRACE, - "%s passed initial chaos probing. Mean measured chaos is %f %%", - encoding_iana, - round(mean_mess_ratio * 100, ndigits=3), - ) - - if not is_multi_byte_decoder: - target_languages: list[str] = encoding_languages(encoding_iana) - else: - target_languages = mb_encoding_languages(encoding_iana) - - if target_languages: - logger.log( - TRACE, - "{} should target any language(s) of {}".format( - encoding_iana, str(target_languages) - ), - ) - - cd_ratios = [] - - # We shall skip the CD when its about ASCII - # Most of the time its not relevant to run "language-detection" on it. - if encoding_iana != "ascii": - for chunk in md_chunks: - chunk_languages = coherence_ratio( - chunk, - language_threshold, - ",".join(target_languages) if target_languages else None, - ) - - cd_ratios.append(chunk_languages) - - cd_ratios_merged = merge_coherence_ratios(cd_ratios) - - if cd_ratios_merged: - logger.log( - TRACE, - "We detected language {} using {}".format( - cd_ratios_merged, encoding_iana - ), - ) - - current_match = CharsetMatch( - sequences, - encoding_iana, - mean_mess_ratio, - bom_or_sig_available, - cd_ratios_merged, - ( - decoded_payload - if ( - is_too_large_sequence is False - or encoding_iana in [specified_encoding, "ascii", "utf_8"] - ) - else None - ), - preemptive_declaration=specified_encoding, - ) - - results.append(current_match) - - if ( - encoding_iana in [specified_encoding, "ascii", "utf_8"] - and mean_mess_ratio < 0.1 - ): - # If md says nothing to worry about, then... stop immediately! - if mean_mess_ratio == 0.0: - logger.debug( - "Encoding detection: %s is most likely the one.", - current_match.encoding, - ) - if explain: # Defensive: ensure exit path clean handler - logger.removeHandler(explain_handler) - logger.setLevel(previous_logger_level) - return CharsetMatches([current_match]) - - early_stop_results.append(current_match) - - if ( - len(early_stop_results) - and (specified_encoding is None or specified_encoding in tested) - and "ascii" in tested - and "utf_8" in tested - ): - probable_result: CharsetMatch = early_stop_results.best() # type: ignore[assignment] - logger.debug( - "Encoding detection: %s is most likely the one.", - probable_result.encoding, - ) - if explain: # Defensive: ensure exit path clean handler - logger.removeHandler(explain_handler) - logger.setLevel(previous_logger_level) - - return CharsetMatches([probable_result]) - - if encoding_iana == sig_encoding: - logger.debug( - "Encoding detection: %s is most likely the one as we detected a BOM or SIG within " - "the beginning of the sequence.", - encoding_iana, - ) - if explain: # Defensive: ensure exit path clean handler - logger.removeHandler(explain_handler) - logger.setLevel(previous_logger_level) - return CharsetMatches([results[encoding_iana]]) - - if len(results) == 0: - if fallback_u8 or fallback_ascii or fallback_specified: - logger.log( - TRACE, - "Nothing got out of the detection process. Using ASCII/UTF-8/Specified fallback.", - ) - - if fallback_specified: - logger.debug( - "Encoding detection: %s will be used as a fallback match", - fallback_specified.encoding, - ) - results.append(fallback_specified) - elif ( - (fallback_u8 and fallback_ascii is None) - or ( - fallback_u8 - and fallback_ascii - and fallback_u8.fingerprint != fallback_ascii.fingerprint - ) - or (fallback_u8 is not None) - ): - logger.debug("Encoding detection: utf_8 will be used as a fallback match") - results.append(fallback_u8) - elif fallback_ascii: - logger.debug("Encoding detection: ascii will be used as a fallback match") - results.append(fallback_ascii) - - if results: - logger.debug( - "Encoding detection: Found %s as plausible (best-candidate) for content. With %i alternatives.", - results.best().encoding, # type: ignore - len(results) - 1, - ) - else: - logger.debug("Encoding detection: Unable to determine any suitable charset.") - - if explain: - logger.removeHandler(explain_handler) - logger.setLevel(previous_logger_level) - - return results - - -def from_fp( - fp: BinaryIO, - steps: int = 5, - chunk_size: int = 512, - threshold: float = 0.20, - cp_isolation: list[str] | None = None, - cp_exclusion: list[str] | None = None, - preemptive_behaviour: bool = True, - explain: bool = False, - language_threshold: float = 0.1, - enable_fallback: bool = True, -) -> CharsetMatches: - """ - Same thing than the function from_bytes but using a file pointer that is already ready. - Will not close the file pointer. - """ - return from_bytes( - fp.read(), - steps, - chunk_size, - threshold, - cp_isolation, - cp_exclusion, - preemptive_behaviour, - explain, - language_threshold, - enable_fallback, - ) - - -def from_path( - path: str | bytes | PathLike, # type: ignore[type-arg] - steps: int = 5, - chunk_size: int = 512, - threshold: float = 0.20, - cp_isolation: list[str] | None = None, - cp_exclusion: list[str] | None = None, - preemptive_behaviour: bool = True, - explain: bool = False, - language_threshold: float = 0.1, - enable_fallback: bool = True, -) -> CharsetMatches: - """ - Same thing than the function from_bytes but with one extra step. Opening and reading given file path in binary mode. - Can raise IOError. - """ - with open(path, "rb") as fp: - return from_fp( - fp, - steps, - chunk_size, - threshold, - cp_isolation, - cp_exclusion, - preemptive_behaviour, - explain, - language_threshold, - enable_fallback, - ) - - -def is_binary( - fp_or_path_or_payload: PathLike | str | BinaryIO | bytes, # type: ignore[type-arg] - steps: int = 5, - chunk_size: int = 512, - threshold: float = 0.20, - cp_isolation: list[str] | None = None, - cp_exclusion: list[str] | None = None, - preemptive_behaviour: bool = True, - explain: bool = False, - language_threshold: float = 0.1, - enable_fallback: bool = False, -) -> bool: - """ - Detect if the given input (file, bytes, or path) points to a binary file. aka. not a string. - Based on the same main heuristic algorithms and default kwargs at the sole exception that fallbacks match - are disabled to be stricter around ASCII-compatible but unlikely to be a string. - """ - if isinstance(fp_or_path_or_payload, (str, PathLike)): - guesses = from_path( - fp_or_path_or_payload, - steps=steps, - chunk_size=chunk_size, - threshold=threshold, - cp_isolation=cp_isolation, - cp_exclusion=cp_exclusion, - preemptive_behaviour=preemptive_behaviour, - explain=explain, - language_threshold=language_threshold, - enable_fallback=enable_fallback, - ) - elif isinstance( - fp_or_path_or_payload, - ( - bytes, - bytearray, - ), - ): - guesses = from_bytes( - fp_or_path_or_payload, - steps=steps, - chunk_size=chunk_size, - threshold=threshold, - cp_isolation=cp_isolation, - cp_exclusion=cp_exclusion, - preemptive_behaviour=preemptive_behaviour, - explain=explain, - language_threshold=language_threshold, - enable_fallback=enable_fallback, - ) - else: - guesses = from_fp( - fp_or_path_or_payload, - steps=steps, - chunk_size=chunk_size, - threshold=threshold, - cp_isolation=cp_isolation, - cp_exclusion=cp_exclusion, - preemptive_behaviour=preemptive_behaviour, - explain=explain, - language_threshold=language_threshold, - enable_fallback=enable_fallback, - ) - - return not guesses diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer/cd.py b/apps/bitwarden_event_logs/lib/charset_normalizer/cd.py deleted file mode 100755 index 71a3ed51..00000000 --- a/apps/bitwarden_event_logs/lib/charset_normalizer/cd.py +++ /dev/null @@ -1,395 +0,0 @@ -from __future__ import annotations - -import importlib -from codecs import IncrementalDecoder -from collections import Counter -from functools import lru_cache -from typing import Counter as TypeCounter - -from .constant import ( - FREQUENCIES, - KO_NAMES, - LANGUAGE_SUPPORTED_COUNT, - TOO_SMALL_SEQUENCE, - ZH_NAMES, -) -from .md import is_suspiciously_successive_range -from .models import CoherenceMatches -from .utils import ( - is_accentuated, - is_latin, - is_multi_byte_encoding, - is_unicode_range_secondary, - unicode_range, -) - - -def encoding_unicode_range(iana_name: str) -> list[str]: - """ - Return associated unicode ranges in a single byte code page. - """ - if is_multi_byte_encoding(iana_name): - raise OSError("Function not supported on multi-byte code page") - - decoder = importlib.import_module(f"encodings.{iana_name}").IncrementalDecoder - - p: IncrementalDecoder = decoder(errors="ignore") - seen_ranges: dict[str, int] = {} - character_count: int = 0 - - for i in range(0x40, 0xFF): - chunk: str = p.decode(bytes([i])) - - if chunk: - character_range: str | None = unicode_range(chunk) - - if character_range is None: - continue - - if is_unicode_range_secondary(character_range) is False: - if character_range not in seen_ranges: - seen_ranges[character_range] = 0 - seen_ranges[character_range] += 1 - character_count += 1 - - return sorted( - [ - character_range - for character_range in seen_ranges - if seen_ranges[character_range] / character_count >= 0.15 - ] - ) - - -def unicode_range_languages(primary_range: str) -> list[str]: - """ - Return inferred languages used with a unicode range. - """ - languages: list[str] = [] - - for language, characters in FREQUENCIES.items(): - for character in characters: - if unicode_range(character) == primary_range: - languages.append(language) - break - - return languages - - -@lru_cache() -def encoding_languages(iana_name: str) -> list[str]: - """ - Single-byte encoding language association. Some code page are heavily linked to particular language(s). - This function does the correspondence. - """ - unicode_ranges: list[str] = encoding_unicode_range(iana_name) - primary_range: str | None = None - - for specified_range in unicode_ranges: - if "Latin" not in specified_range: - primary_range = specified_range - break - - if primary_range is None: - return ["Latin Based"] - - return unicode_range_languages(primary_range) - - -@lru_cache() -def mb_encoding_languages(iana_name: str) -> list[str]: - """ - Multi-byte encoding language association. Some code page are heavily linked to particular language(s). - This function does the correspondence. - """ - if ( - iana_name.startswith("shift_") - or iana_name.startswith("iso2022_jp") - or iana_name.startswith("euc_j") - or iana_name == "cp932" - ): - return ["Japanese"] - if iana_name.startswith("gb") or iana_name in ZH_NAMES: - return ["Chinese"] - if iana_name.startswith("iso2022_kr") or iana_name in KO_NAMES: - return ["Korean"] - - return [] - - -@lru_cache(maxsize=LANGUAGE_SUPPORTED_COUNT) -def get_target_features(language: str) -> tuple[bool, bool]: - """ - Determine main aspects from a supported language if it contains accents and if is pure Latin. - """ - target_have_accents: bool = False - target_pure_latin: bool = True - - for character in FREQUENCIES[language]: - if not target_have_accents and is_accentuated(character): - target_have_accents = True - if target_pure_latin and is_latin(character) is False: - target_pure_latin = False - - return target_have_accents, target_pure_latin - - -def alphabet_languages( - characters: list[str], ignore_non_latin: bool = False -) -> list[str]: - """ - Return associated languages associated to given characters. - """ - languages: list[tuple[str, float]] = [] - - source_have_accents = any(is_accentuated(character) for character in characters) - - for language, language_characters in FREQUENCIES.items(): - target_have_accents, target_pure_latin = get_target_features(language) - - if ignore_non_latin and target_pure_latin is False: - continue - - if target_have_accents is False and source_have_accents: - continue - - character_count: int = len(language_characters) - - character_match_count: int = len( - [c for c in language_characters if c in characters] - ) - - ratio: float = character_match_count / character_count - - if ratio >= 0.2: - languages.append((language, ratio)) - - languages = sorted(languages, key=lambda x: x[1], reverse=True) - - return [compatible_language[0] for compatible_language in languages] - - -def characters_popularity_compare( - language: str, ordered_characters: list[str] -) -> float: - """ - Determine if a ordered characters list (by occurrence from most appearance to rarest) match a particular language. - The result is a ratio between 0. (absolutely no correspondence) and 1. (near perfect fit). - Beware that is function is not strict on the match in order to ease the detection. (Meaning close match is 1.) - """ - if language not in FREQUENCIES: - raise ValueError(f"{language} not available") - - character_approved_count: int = 0 - FREQUENCIES_language_set = set(FREQUENCIES[language]) - - ordered_characters_count: int = len(ordered_characters) - target_language_characters_count: int = len(FREQUENCIES[language]) - - large_alphabet: bool = target_language_characters_count > 26 - - for character, character_rank in zip( - ordered_characters, range(0, ordered_characters_count) - ): - if character not in FREQUENCIES_language_set: - continue - - character_rank_in_language: int = FREQUENCIES[language].index(character) - expected_projection_ratio: float = ( - target_language_characters_count / ordered_characters_count - ) - character_rank_projection: int = int(character_rank * expected_projection_ratio) - - if ( - large_alphabet is False - and abs(character_rank_projection - character_rank_in_language) > 4 - ): - continue - - if ( - large_alphabet is True - and abs(character_rank_projection - character_rank_in_language) - < target_language_characters_count / 3 - ): - character_approved_count += 1 - continue - - characters_before_source: list[str] = FREQUENCIES[language][ - 0:character_rank_in_language - ] - characters_after_source: list[str] = FREQUENCIES[language][ - character_rank_in_language: - ] - characters_before: list[str] = ordered_characters[0:character_rank] - characters_after: list[str] = ordered_characters[character_rank:] - - before_match_count: int = len( - set(characters_before) & set(characters_before_source) - ) - - after_match_count: int = len( - set(characters_after) & set(characters_after_source) - ) - - if len(characters_before_source) == 0 and before_match_count <= 4: - character_approved_count += 1 - continue - - if len(characters_after_source) == 0 and after_match_count <= 4: - character_approved_count += 1 - continue - - if ( - before_match_count / len(characters_before_source) >= 0.4 - or after_match_count / len(characters_after_source) >= 0.4 - ): - character_approved_count += 1 - continue - - return character_approved_count / len(ordered_characters) - - -def alpha_unicode_split(decoded_sequence: str) -> list[str]: - """ - Given a decoded text sequence, return a list of str. Unicode range / alphabet separation. - Ex. a text containing English/Latin with a bit a Hebrew will return two items in the resulting list; - One containing the latin letters and the other hebrew. - """ - layers: dict[str, str] = {} - - for character in decoded_sequence: - if character.isalpha() is False: - continue - - character_range: str | None = unicode_range(character) - - if character_range is None: - continue - - layer_target_range: str | None = None - - for discovered_range in layers: - if ( - is_suspiciously_successive_range(discovered_range, character_range) - is False - ): - layer_target_range = discovered_range - break - - if layer_target_range is None: - layer_target_range = character_range - - if layer_target_range not in layers: - layers[layer_target_range] = character.lower() - continue - - layers[layer_target_range] += character.lower() - - return list(layers.values()) - - -def merge_coherence_ratios(results: list[CoherenceMatches]) -> CoherenceMatches: - """ - This function merge results previously given by the function coherence_ratio. - The return type is the same as coherence_ratio. - """ - per_language_ratios: dict[str, list[float]] = {} - for result in results: - for sub_result in result: - language, ratio = sub_result - if language not in per_language_ratios: - per_language_ratios[language] = [ratio] - continue - per_language_ratios[language].append(ratio) - - merge = [ - ( - language, - round( - sum(per_language_ratios[language]) / len(per_language_ratios[language]), - 4, - ), - ) - for language in per_language_ratios - ] - - return sorted(merge, key=lambda x: x[1], reverse=True) - - -def filter_alt_coherence_matches(results: CoherenceMatches) -> CoherenceMatches: - """ - We shall NOT return "English—" in CoherenceMatches because it is an alternative - of "English". This function only keeps the best match and remove the em-dash in it. - """ - index_results: dict[str, list[float]] = dict() - - for result in results: - language, ratio = result - no_em_name: str = language.replace("—", "") - - if no_em_name not in index_results: - index_results[no_em_name] = [] - - index_results[no_em_name].append(ratio) - - if any(len(index_results[e]) > 1 for e in index_results): - filtered_results: CoherenceMatches = [] - - for language in index_results: - filtered_results.append((language, max(index_results[language]))) - - return filtered_results - - return results - - -@lru_cache(maxsize=2048) -def coherence_ratio( - decoded_sequence: str, threshold: float = 0.1, lg_inclusion: str | None = None -) -> CoherenceMatches: - """ - Detect ANY language that can be identified in given sequence. The sequence will be analysed by layers. - A layer = Character extraction by alphabets/ranges. - """ - - results: list[tuple[str, float]] = [] - ignore_non_latin: bool = False - - sufficient_match_count: int = 0 - - lg_inclusion_list = lg_inclusion.split(",") if lg_inclusion is not None else [] - if "Latin Based" in lg_inclusion_list: - ignore_non_latin = True - lg_inclusion_list.remove("Latin Based") - - for layer in alpha_unicode_split(decoded_sequence): - sequence_frequencies: TypeCounter[str] = Counter(layer) - most_common = sequence_frequencies.most_common() - - character_count: int = sum(o for c, o in most_common) - - if character_count <= TOO_SMALL_SEQUENCE: - continue - - popular_character_ordered: list[str] = [c for c, o in most_common] - - for language in lg_inclusion_list or alphabet_languages( - popular_character_ordered, ignore_non_latin - ): - ratio: float = characters_popularity_compare( - language, popular_character_ordered - ) - - if ratio < threshold: - continue - elif ratio >= 0.8: - sufficient_match_count += 1 - - results.append((language, round(ratio, 4))) - - if sufficient_match_count >= 3: - break - - return sorted( - filter_alt_coherence_matches(results), key=lambda x: x[1], reverse=True - ) diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer/cli/__init__.py b/apps/bitwarden_event_logs/lib/charset_normalizer/cli/__init__.py deleted file mode 100755 index 543a5a4d..00000000 --- a/apps/bitwarden_event_logs/lib/charset_normalizer/cli/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import annotations - -from .__main__ import cli_detect, query_yes_no - -__all__ = ( - "cli_detect", - "query_yes_no", -) diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer/cli/__main__.py b/apps/bitwarden_event_logs/lib/charset_normalizer/cli/__main__.py deleted file mode 100755 index cb64156a..00000000 --- a/apps/bitwarden_event_logs/lib/charset_normalizer/cli/__main__.py +++ /dev/null @@ -1,381 +0,0 @@ -from __future__ import annotations - -import argparse -import sys -import typing -from json import dumps -from os.path import abspath, basename, dirname, join, realpath -from platform import python_version -from unicodedata import unidata_version - -import charset_normalizer.md as md_module -from charset_normalizer import from_fp -from charset_normalizer.models import CliDetectionResult -from charset_normalizer.version import __version__ - - -def query_yes_no(question: str, default: str = "yes") -> bool: - """Ask a yes/no question via input() and return their answer. - - "question" is a string that is presented to the user. - "default" is the presumed answer if the user just hits . - It must be "yes" (the default), "no" or None (meaning - an answer is required of the user). - - The "answer" return value is True for "yes" or False for "no". - - Credit goes to (c) https://stackoverflow.com/questions/3041986/apt-command-line-interface-like-yes-no-input - """ - valid = {"yes": True, "y": True, "ye": True, "no": False, "n": False} - if default is None: - prompt = " [y/n] " - elif default == "yes": - prompt = " [Y/n] " - elif default == "no": - prompt = " [y/N] " - else: - raise ValueError("invalid default answer: '%s'" % default) - - while True: - sys.stdout.write(question + prompt) - choice = input().lower() - if default is not None and choice == "": - return valid[default] - elif choice in valid: - return valid[choice] - else: - sys.stdout.write("Please respond with 'yes' or 'no' (or 'y' or 'n').\n") - - -class FileType: - """Factory for creating file object types - - Instances of FileType are typically passed as type= arguments to the - ArgumentParser add_argument() method. - - Keyword Arguments: - - mode -- A string indicating how the file is to be opened. Accepts the - same values as the builtin open() function. - - bufsize -- The file's desired buffer size. Accepts the same values as - the builtin open() function. - - encoding -- The file's encoding. Accepts the same values as the - builtin open() function. - - errors -- A string indicating how encoding and decoding errors are to - be handled. Accepts the same value as the builtin open() function. - - Backported from CPython 3.12 - """ - - def __init__( - self, - mode: str = "r", - bufsize: int = -1, - encoding: str | None = None, - errors: str | None = None, - ): - self._mode = mode - self._bufsize = bufsize - self._encoding = encoding - self._errors = errors - - def __call__(self, string: str) -> typing.IO: # type: ignore[type-arg] - # the special argument "-" means sys.std{in,out} - if string == "-": - if "r" in self._mode: - return sys.stdin.buffer if "b" in self._mode else sys.stdin - elif any(c in self._mode for c in "wax"): - return sys.stdout.buffer if "b" in self._mode else sys.stdout - else: - msg = f'argument "-" with mode {self._mode}' - raise ValueError(msg) - - # all other arguments are used as file names - try: - return open(string, self._mode, self._bufsize, self._encoding, self._errors) - except OSError as e: - message = f"can't open '{string}': {e}" - raise argparse.ArgumentTypeError(message) - - def __repr__(self) -> str: - args = self._mode, self._bufsize - kwargs = [("encoding", self._encoding), ("errors", self._errors)] - args_str = ", ".join( - [repr(arg) for arg in args if arg != -1] - + [f"{kw}={arg!r}" for kw, arg in kwargs if arg is not None] - ) - return f"{type(self).__name__}({args_str})" - - -def cli_detect(argv: list[str] | None = None) -> int: - """ - CLI assistant using ARGV and ArgumentParser - :param argv: - :return: 0 if everything is fine, anything else equal trouble - """ - parser = argparse.ArgumentParser( - description="The Real First Universal Charset Detector. " - "Discover originating encoding used on text file. " - "Normalize text to unicode." - ) - - parser.add_argument( - "files", type=FileType("rb"), nargs="+", help="File(s) to be analysed" - ) - parser.add_argument( - "-v", - "--verbose", - action="store_true", - default=False, - dest="verbose", - help="Display complementary information about file if any. " - "Stdout will contain logs about the detection process.", - ) - parser.add_argument( - "-a", - "--with-alternative", - action="store_true", - default=False, - dest="alternatives", - help="Output complementary possibilities if any. Top-level JSON WILL be a list.", - ) - parser.add_argument( - "-n", - "--normalize", - action="store_true", - default=False, - dest="normalize", - help="Permit to normalize input file. If not set, program does not write anything.", - ) - parser.add_argument( - "-m", - "--minimal", - action="store_true", - default=False, - dest="minimal", - help="Only output the charset detected to STDOUT. Disabling JSON output.", - ) - parser.add_argument( - "-r", - "--replace", - action="store_true", - default=False, - dest="replace", - help="Replace file when trying to normalize it instead of creating a new one.", - ) - parser.add_argument( - "-f", - "--force", - action="store_true", - default=False, - dest="force", - help="Replace file without asking if you are sure, use this flag with caution.", - ) - parser.add_argument( - "-i", - "--no-preemptive", - action="store_true", - default=False, - dest="no_preemptive", - help="Disable looking at a charset declaration to hint the detector.", - ) - parser.add_argument( - "-t", - "--threshold", - action="store", - default=0.2, - type=float, - dest="threshold", - help="Define a custom maximum amount of noise allowed in decoded content. 0. <= noise <= 1.", - ) - parser.add_argument( - "--version", - action="version", - version="Charset-Normalizer {} - Python {} - Unicode {} - SpeedUp {}".format( - __version__, - python_version(), - unidata_version, - "OFF" if md_module.__file__.lower().endswith(".py") else "ON", - ), - help="Show version information and exit.", - ) - - args = parser.parse_args(argv) - - if args.replace is True and args.normalize is False: - if args.files: - for my_file in args.files: - my_file.close() - print("Use --replace in addition of --normalize only.", file=sys.stderr) - return 1 - - if args.force is True and args.replace is False: - if args.files: - for my_file in args.files: - my_file.close() - print("Use --force in addition of --replace only.", file=sys.stderr) - return 1 - - if args.threshold < 0.0 or args.threshold > 1.0: - if args.files: - for my_file in args.files: - my_file.close() - print("--threshold VALUE should be between 0. AND 1.", file=sys.stderr) - return 1 - - x_ = [] - - for my_file in args.files: - matches = from_fp( - my_file, - threshold=args.threshold, - explain=args.verbose, - preemptive_behaviour=args.no_preemptive is False, - ) - - best_guess = matches.best() - - if best_guess is None: - print( - 'Unable to identify originating encoding for "{}". {}'.format( - my_file.name, - ( - "Maybe try increasing maximum amount of chaos." - if args.threshold < 1.0 - else "" - ), - ), - file=sys.stderr, - ) - x_.append( - CliDetectionResult( - abspath(my_file.name), - None, - [], - [], - "Unknown", - [], - False, - 1.0, - 0.0, - None, - True, - ) - ) - else: - x_.append( - CliDetectionResult( - abspath(my_file.name), - best_guess.encoding, - best_guess.encoding_aliases, - [ - cp - for cp in best_guess.could_be_from_charset - if cp != best_guess.encoding - ], - best_guess.language, - best_guess.alphabets, - best_guess.bom, - best_guess.percent_chaos, - best_guess.percent_coherence, - None, - True, - ) - ) - - if len(matches) > 1 and args.alternatives: - for el in matches: - if el != best_guess: - x_.append( - CliDetectionResult( - abspath(my_file.name), - el.encoding, - el.encoding_aliases, - [ - cp - for cp in el.could_be_from_charset - if cp != el.encoding - ], - el.language, - el.alphabets, - el.bom, - el.percent_chaos, - el.percent_coherence, - None, - False, - ) - ) - - if args.normalize is True: - if best_guess.encoding.startswith("utf") is True: - print( - '"{}" file does not need to be normalized, as it already came from unicode.'.format( - my_file.name - ), - file=sys.stderr, - ) - if my_file.closed is False: - my_file.close() - continue - - dir_path = dirname(realpath(my_file.name)) - file_name = basename(realpath(my_file.name)) - - o_: list[str] = file_name.split(".") - - if args.replace is False: - o_.insert(-1, best_guess.encoding) - if my_file.closed is False: - my_file.close() - elif ( - args.force is False - and query_yes_no( - 'Are you sure to normalize "{}" by replacing it ?'.format( - my_file.name - ), - "no", - ) - is False - ): - if my_file.closed is False: - my_file.close() - continue - - try: - x_[0].unicode_path = join(dir_path, ".".join(o_)) - - with open(x_[0].unicode_path, "wb") as fp: - fp.write(best_guess.output()) - except OSError as e: - print(str(e), file=sys.stderr) - if my_file.closed is False: - my_file.close() - return 2 - - if my_file.closed is False: - my_file.close() - - if args.minimal is False: - print( - dumps( - [el.__dict__ for el in x_] if len(x_) > 1 else x_[0].__dict__, - ensure_ascii=True, - indent=4, - ) - ) - else: - for my_file in args.files: - print( - ", ".join( - [ - el.encoding or "undefined" - for el in x_ - if el.path == abspath(my_file.name) - ] - ) - ) - - return 0 - - -if __name__ == "__main__": - cli_detect() diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer/constant.py b/apps/bitwarden_event_logs/lib/charset_normalizer/constant.py deleted file mode 100755 index cc71a019..00000000 --- a/apps/bitwarden_event_logs/lib/charset_normalizer/constant.py +++ /dev/null @@ -1,2015 +0,0 @@ -from __future__ import annotations - -from codecs import BOM_UTF8, BOM_UTF16_BE, BOM_UTF16_LE, BOM_UTF32_BE, BOM_UTF32_LE -from encodings.aliases import aliases -from re import IGNORECASE -from re import compile as re_compile - -# Contain for each eligible encoding a list of/item bytes SIG/BOM -ENCODING_MARKS: dict[str, bytes | list[bytes]] = { - "utf_8": BOM_UTF8, - "utf_7": [ - b"\x2b\x2f\x76\x38", - b"\x2b\x2f\x76\x39", - b"\x2b\x2f\x76\x2b", - b"\x2b\x2f\x76\x2f", - b"\x2b\x2f\x76\x38\x2d", - ], - "gb18030": b"\x84\x31\x95\x33", - "utf_32": [BOM_UTF32_BE, BOM_UTF32_LE], - "utf_16": [BOM_UTF16_BE, BOM_UTF16_LE], -} - -TOO_SMALL_SEQUENCE: int = 32 -TOO_BIG_SEQUENCE: int = int(10e6) - -UTF8_MAXIMAL_ALLOCATION: int = 1_112_064 - -# Up-to-date Unicode ucd/15.0.0 -UNICODE_RANGES_COMBINED: dict[str, range] = { - "Control character": range(32), - "Basic Latin": range(32, 128), - "Latin-1 Supplement": range(128, 256), - "Latin Extended-A": range(256, 384), - "Latin Extended-B": range(384, 592), - "IPA Extensions": range(592, 688), - "Spacing Modifier Letters": range(688, 768), - "Combining Diacritical Marks": range(768, 880), - "Greek and Coptic": range(880, 1024), - "Cyrillic": range(1024, 1280), - "Cyrillic Supplement": range(1280, 1328), - "Armenian": range(1328, 1424), - "Hebrew": range(1424, 1536), - "Arabic": range(1536, 1792), - "Syriac": range(1792, 1872), - "Arabic Supplement": range(1872, 1920), - "Thaana": range(1920, 1984), - "NKo": range(1984, 2048), - "Samaritan": range(2048, 2112), - "Mandaic": range(2112, 2144), - "Syriac Supplement": range(2144, 2160), - "Arabic Extended-B": range(2160, 2208), - "Arabic Extended-A": range(2208, 2304), - "Devanagari": range(2304, 2432), - "Bengali": range(2432, 2560), - "Gurmukhi": range(2560, 2688), - "Gujarati": range(2688, 2816), - "Oriya": range(2816, 2944), - "Tamil": range(2944, 3072), - "Telugu": range(3072, 3200), - "Kannada": range(3200, 3328), - "Malayalam": range(3328, 3456), - "Sinhala": range(3456, 3584), - "Thai": range(3584, 3712), - "Lao": range(3712, 3840), - "Tibetan": range(3840, 4096), - "Myanmar": range(4096, 4256), - "Georgian": range(4256, 4352), - "Hangul Jamo": range(4352, 4608), - "Ethiopic": range(4608, 4992), - "Ethiopic Supplement": range(4992, 5024), - "Cherokee": range(5024, 5120), - "Unified Canadian Aboriginal Syllabics": range(5120, 5760), - "Ogham": range(5760, 5792), - "Runic": range(5792, 5888), - "Tagalog": range(5888, 5920), - "Hanunoo": range(5920, 5952), - "Buhid": range(5952, 5984), - "Tagbanwa": range(5984, 6016), - "Khmer": range(6016, 6144), - "Mongolian": range(6144, 6320), - "Unified Canadian Aboriginal Syllabics Extended": range(6320, 6400), - "Limbu": range(6400, 6480), - "Tai Le": range(6480, 6528), - "New Tai Lue": range(6528, 6624), - "Khmer Symbols": range(6624, 6656), - "Buginese": range(6656, 6688), - "Tai Tham": range(6688, 6832), - "Combining Diacritical Marks Extended": range(6832, 6912), - "Balinese": range(6912, 7040), - "Sundanese": range(7040, 7104), - "Batak": range(7104, 7168), - "Lepcha": range(7168, 7248), - "Ol Chiki": range(7248, 7296), - "Cyrillic Extended-C": range(7296, 7312), - "Georgian Extended": range(7312, 7360), - "Sundanese Supplement": range(7360, 7376), - "Vedic Extensions": range(7376, 7424), - "Phonetic Extensions": range(7424, 7552), - "Phonetic Extensions Supplement": range(7552, 7616), - "Combining Diacritical Marks Supplement": range(7616, 7680), - "Latin Extended Additional": range(7680, 7936), - "Greek Extended": range(7936, 8192), - "General Punctuation": range(8192, 8304), - "Superscripts and Subscripts": range(8304, 8352), - "Currency Symbols": range(8352, 8400), - "Combining Diacritical Marks for Symbols": range(8400, 8448), - "Letterlike Symbols": range(8448, 8528), - "Number Forms": range(8528, 8592), - "Arrows": range(8592, 8704), - "Mathematical Operators": range(8704, 8960), - "Miscellaneous Technical": range(8960, 9216), - "Control Pictures": range(9216, 9280), - "Optical Character Recognition": range(9280, 9312), - "Enclosed Alphanumerics": range(9312, 9472), - "Box Drawing": range(9472, 9600), - "Block Elements": range(9600, 9632), - "Geometric Shapes": range(9632, 9728), - "Miscellaneous Symbols": range(9728, 9984), - "Dingbats": range(9984, 10176), - "Miscellaneous Mathematical Symbols-A": range(10176, 10224), - "Supplemental Arrows-A": range(10224, 10240), - "Braille Patterns": range(10240, 10496), - "Supplemental Arrows-B": range(10496, 10624), - "Miscellaneous Mathematical Symbols-B": range(10624, 10752), - "Supplemental Mathematical Operators": range(10752, 11008), - "Miscellaneous Symbols and Arrows": range(11008, 11264), - "Glagolitic": range(11264, 11360), - "Latin Extended-C": range(11360, 11392), - "Coptic": range(11392, 11520), - "Georgian Supplement": range(11520, 11568), - "Tifinagh": range(11568, 11648), - "Ethiopic Extended": range(11648, 11744), - "Cyrillic Extended-A": range(11744, 11776), - "Supplemental Punctuation": range(11776, 11904), - "CJK Radicals Supplement": range(11904, 12032), - "Kangxi Radicals": range(12032, 12256), - "Ideographic Description Characters": range(12272, 12288), - "CJK Symbols and Punctuation": range(12288, 12352), - "Hiragana": range(12352, 12448), - "Katakana": range(12448, 12544), - "Bopomofo": range(12544, 12592), - "Hangul Compatibility Jamo": range(12592, 12688), - "Kanbun": range(12688, 12704), - "Bopomofo Extended": range(12704, 12736), - "CJK Strokes": range(12736, 12784), - "Katakana Phonetic Extensions": range(12784, 12800), - "Enclosed CJK Letters and Months": range(12800, 13056), - "CJK Compatibility": range(13056, 13312), - "CJK Unified Ideographs Extension A": range(13312, 19904), - "Yijing Hexagram Symbols": range(19904, 19968), - "CJK Unified Ideographs": range(19968, 40960), - "Yi Syllables": range(40960, 42128), - "Yi Radicals": range(42128, 42192), - "Lisu": range(42192, 42240), - "Vai": range(42240, 42560), - "Cyrillic Extended-B": range(42560, 42656), - "Bamum": range(42656, 42752), - "Modifier Tone Letters": range(42752, 42784), - "Latin Extended-D": range(42784, 43008), - "Syloti Nagri": range(43008, 43056), - "Common Indic Number Forms": range(43056, 43072), - "Phags-pa": range(43072, 43136), - "Saurashtra": range(43136, 43232), - "Devanagari Extended": range(43232, 43264), - "Kayah Li": range(43264, 43312), - "Rejang": range(43312, 43360), - "Hangul Jamo Extended-A": range(43360, 43392), - "Javanese": range(43392, 43488), - "Myanmar Extended-B": range(43488, 43520), - "Cham": range(43520, 43616), - "Myanmar Extended-A": range(43616, 43648), - "Tai Viet": range(43648, 43744), - "Meetei Mayek Extensions": range(43744, 43776), - "Ethiopic Extended-A": range(43776, 43824), - "Latin Extended-E": range(43824, 43888), - "Cherokee Supplement": range(43888, 43968), - "Meetei Mayek": range(43968, 44032), - "Hangul Syllables": range(44032, 55216), - "Hangul Jamo Extended-B": range(55216, 55296), - "High Surrogates": range(55296, 56192), - "High Private Use Surrogates": range(56192, 56320), - "Low Surrogates": range(56320, 57344), - "Private Use Area": range(57344, 63744), - "CJK Compatibility Ideographs": range(63744, 64256), - "Alphabetic Presentation Forms": range(64256, 64336), - "Arabic Presentation Forms-A": range(64336, 65024), - "Variation Selectors": range(65024, 65040), - "Vertical Forms": range(65040, 65056), - "Combining Half Marks": range(65056, 65072), - "CJK Compatibility Forms": range(65072, 65104), - "Small Form Variants": range(65104, 65136), - "Arabic Presentation Forms-B": range(65136, 65280), - "Halfwidth and Fullwidth Forms": range(65280, 65520), - "Specials": range(65520, 65536), - "Linear B Syllabary": range(65536, 65664), - "Linear B Ideograms": range(65664, 65792), - "Aegean Numbers": range(65792, 65856), - "Ancient Greek Numbers": range(65856, 65936), - "Ancient Symbols": range(65936, 66000), - "Phaistos Disc": range(66000, 66048), - "Lycian": range(66176, 66208), - "Carian": range(66208, 66272), - "Coptic Epact Numbers": range(66272, 66304), - "Old Italic": range(66304, 66352), - "Gothic": range(66352, 66384), - "Old Permic": range(66384, 66432), - "Ugaritic": range(66432, 66464), - "Old Persian": range(66464, 66528), - "Deseret": range(66560, 66640), - "Shavian": range(66640, 66688), - "Osmanya": range(66688, 66736), - "Osage": range(66736, 66816), - "Elbasan": range(66816, 66864), - "Caucasian Albanian": range(66864, 66928), - "Vithkuqi": range(66928, 67008), - "Linear A": range(67072, 67456), - "Latin Extended-F": range(67456, 67520), - "Cypriot Syllabary": range(67584, 67648), - "Imperial Aramaic": range(67648, 67680), - "Palmyrene": range(67680, 67712), - "Nabataean": range(67712, 67760), - "Hatran": range(67808, 67840), - "Phoenician": range(67840, 67872), - "Lydian": range(67872, 67904), - "Meroitic Hieroglyphs": range(67968, 68000), - "Meroitic Cursive": range(68000, 68096), - "Kharoshthi": range(68096, 68192), - "Old South Arabian": range(68192, 68224), - "Old North Arabian": range(68224, 68256), - "Manichaean": range(68288, 68352), - "Avestan": range(68352, 68416), - "Inscriptional Parthian": range(68416, 68448), - "Inscriptional Pahlavi": range(68448, 68480), - "Psalter Pahlavi": range(68480, 68528), - "Old Turkic": range(68608, 68688), - "Old Hungarian": range(68736, 68864), - "Hanifi Rohingya": range(68864, 68928), - "Rumi Numeral Symbols": range(69216, 69248), - "Yezidi": range(69248, 69312), - "Arabic Extended-C": range(69312, 69376), - "Old Sogdian": range(69376, 69424), - "Sogdian": range(69424, 69488), - "Old Uyghur": range(69488, 69552), - "Chorasmian": range(69552, 69600), - "Elymaic": range(69600, 69632), - "Brahmi": range(69632, 69760), - "Kaithi": range(69760, 69840), - "Sora Sompeng": range(69840, 69888), - "Chakma": range(69888, 69968), - "Mahajani": range(69968, 70016), - "Sharada": range(70016, 70112), - "Sinhala Archaic Numbers": range(70112, 70144), - "Khojki": range(70144, 70224), - "Multani": range(70272, 70320), - "Khudawadi": range(70320, 70400), - "Grantha": range(70400, 70528), - "Newa": range(70656, 70784), - "Tirhuta": range(70784, 70880), - "Siddham": range(71040, 71168), - "Modi": range(71168, 71264), - "Mongolian Supplement": range(71264, 71296), - "Takri": range(71296, 71376), - "Ahom": range(71424, 71504), - "Dogra": range(71680, 71760), - "Warang Citi": range(71840, 71936), - "Dives Akuru": range(71936, 72032), - "Nandinagari": range(72096, 72192), - "Zanabazar Square": range(72192, 72272), - "Soyombo": range(72272, 72368), - "Unified Canadian Aboriginal Syllabics Extended-A": range(72368, 72384), - "Pau Cin Hau": range(72384, 72448), - "Devanagari Extended-A": range(72448, 72544), - "Bhaiksuki": range(72704, 72816), - "Marchen": range(72816, 72896), - "Masaram Gondi": range(72960, 73056), - "Gunjala Gondi": range(73056, 73136), - "Makasar": range(73440, 73472), - "Kawi": range(73472, 73568), - "Lisu Supplement": range(73648, 73664), - "Tamil Supplement": range(73664, 73728), - "Cuneiform": range(73728, 74752), - "Cuneiform Numbers and Punctuation": range(74752, 74880), - "Early Dynastic Cuneiform": range(74880, 75088), - "Cypro-Minoan": range(77712, 77824), - "Egyptian Hieroglyphs": range(77824, 78896), - "Egyptian Hieroglyph Format Controls": range(78896, 78944), - "Anatolian Hieroglyphs": range(82944, 83584), - "Bamum Supplement": range(92160, 92736), - "Mro": range(92736, 92784), - "Tangsa": range(92784, 92880), - "Bassa Vah": range(92880, 92928), - "Pahawh Hmong": range(92928, 93072), - "Medefaidrin": range(93760, 93856), - "Miao": range(93952, 94112), - "Ideographic Symbols and Punctuation": range(94176, 94208), - "Tangut": range(94208, 100352), - "Tangut Components": range(100352, 101120), - "Khitan Small Script": range(101120, 101632), - "Tangut Supplement": range(101632, 101760), - "Kana Extended-B": range(110576, 110592), - "Kana Supplement": range(110592, 110848), - "Kana Extended-A": range(110848, 110896), - "Small Kana Extension": range(110896, 110960), - "Nushu": range(110960, 111360), - "Duployan": range(113664, 113824), - "Shorthand Format Controls": range(113824, 113840), - "Znamenny Musical Notation": range(118528, 118736), - "Byzantine Musical Symbols": range(118784, 119040), - "Musical Symbols": range(119040, 119296), - "Ancient Greek Musical Notation": range(119296, 119376), - "Kaktovik Numerals": range(119488, 119520), - "Mayan Numerals": range(119520, 119552), - "Tai Xuan Jing Symbols": range(119552, 119648), - "Counting Rod Numerals": range(119648, 119680), - "Mathematical Alphanumeric Symbols": range(119808, 120832), - "Sutton SignWriting": range(120832, 121520), - "Latin Extended-G": range(122624, 122880), - "Glagolitic Supplement": range(122880, 122928), - "Cyrillic Extended-D": range(122928, 123024), - "Nyiakeng Puachue Hmong": range(123136, 123216), - "Toto": range(123536, 123584), - "Wancho": range(123584, 123648), - "Nag Mundari": range(124112, 124160), - "Ethiopic Extended-B": range(124896, 124928), - "Mende Kikakui": range(124928, 125152), - "Adlam": range(125184, 125280), - "Indic Siyaq Numbers": range(126064, 126144), - "Ottoman Siyaq Numbers": range(126208, 126288), - "Arabic Mathematical Alphabetic Symbols": range(126464, 126720), - "Mahjong Tiles": range(126976, 127024), - "Domino Tiles": range(127024, 127136), - "Playing Cards": range(127136, 127232), - "Enclosed Alphanumeric Supplement": range(127232, 127488), - "Enclosed Ideographic Supplement": range(127488, 127744), - "Miscellaneous Symbols and Pictographs": range(127744, 128512), - "Emoticons range(Emoji)": range(128512, 128592), - "Ornamental Dingbats": range(128592, 128640), - "Transport and Map Symbols": range(128640, 128768), - "Alchemical Symbols": range(128768, 128896), - "Geometric Shapes Extended": range(128896, 129024), - "Supplemental Arrows-C": range(129024, 129280), - "Supplemental Symbols and Pictographs": range(129280, 129536), - "Chess Symbols": range(129536, 129648), - "Symbols and Pictographs Extended-A": range(129648, 129792), - "Symbols for Legacy Computing": range(129792, 130048), - "CJK Unified Ideographs Extension B": range(131072, 173792), - "CJK Unified Ideographs Extension C": range(173824, 177984), - "CJK Unified Ideographs Extension D": range(177984, 178208), - "CJK Unified Ideographs Extension E": range(178208, 183984), - "CJK Unified Ideographs Extension F": range(183984, 191472), - "CJK Compatibility Ideographs Supplement": range(194560, 195104), - "CJK Unified Ideographs Extension G": range(196608, 201552), - "CJK Unified Ideographs Extension H": range(201552, 205744), - "Tags": range(917504, 917632), - "Variation Selectors Supplement": range(917760, 918000), - "Supplementary Private Use Area-A": range(983040, 1048576), - "Supplementary Private Use Area-B": range(1048576, 1114112), -} - - -UNICODE_SECONDARY_RANGE_KEYWORD: list[str] = [ - "Supplement", - "Extended", - "Extensions", - "Modifier", - "Marks", - "Punctuation", - "Symbols", - "Forms", - "Operators", - "Miscellaneous", - "Drawing", - "Block", - "Shapes", - "Supplemental", - "Tags", -] - -RE_POSSIBLE_ENCODING_INDICATION = re_compile( - r"(?:(?:encoding)|(?:charset)|(?:coding))(?:[\:= ]{1,10})(?:[\"\']?)([a-zA-Z0-9\-_]+)(?:[\"\']?)", - IGNORECASE, -) - -IANA_NO_ALIASES = [ - "cp720", - "cp737", - "cp856", - "cp874", - "cp875", - "cp1006", - "koi8_r", - "koi8_t", - "koi8_u", -] - -IANA_SUPPORTED: list[str] = sorted( - filter( - lambda x: x.endswith("_codec") is False - and x not in {"rot_13", "tactis", "mbcs"}, - list(set(aliases.values())) + IANA_NO_ALIASES, - ) -) - -IANA_SUPPORTED_COUNT: int = len(IANA_SUPPORTED) - -# pre-computed code page that are similar using the function cp_similarity. -IANA_SUPPORTED_SIMILAR: dict[str, list[str]] = { - "cp037": ["cp1026", "cp1140", "cp273", "cp500"], - "cp1026": ["cp037", "cp1140", "cp273", "cp500"], - "cp1125": ["cp866"], - "cp1140": ["cp037", "cp1026", "cp273", "cp500"], - "cp1250": ["iso8859_2"], - "cp1251": ["kz1048", "ptcp154"], - "cp1252": ["iso8859_15", "iso8859_9", "latin_1"], - "cp1253": ["iso8859_7"], - "cp1254": ["iso8859_15", "iso8859_9", "latin_1"], - "cp1257": ["iso8859_13"], - "cp273": ["cp037", "cp1026", "cp1140", "cp500"], - "cp437": ["cp850", "cp858", "cp860", "cp861", "cp862", "cp863", "cp865"], - "cp500": ["cp037", "cp1026", "cp1140", "cp273"], - "cp850": ["cp437", "cp857", "cp858", "cp865"], - "cp857": ["cp850", "cp858", "cp865"], - "cp858": ["cp437", "cp850", "cp857", "cp865"], - "cp860": ["cp437", "cp861", "cp862", "cp863", "cp865"], - "cp861": ["cp437", "cp860", "cp862", "cp863", "cp865"], - "cp862": ["cp437", "cp860", "cp861", "cp863", "cp865"], - "cp863": ["cp437", "cp860", "cp861", "cp862", "cp865"], - "cp865": ["cp437", "cp850", "cp857", "cp858", "cp860", "cp861", "cp862", "cp863"], - "cp866": ["cp1125"], - "iso8859_10": ["iso8859_14", "iso8859_15", "iso8859_4", "iso8859_9", "latin_1"], - "iso8859_11": ["tis_620"], - "iso8859_13": ["cp1257"], - "iso8859_14": [ - "iso8859_10", - "iso8859_15", - "iso8859_16", - "iso8859_3", - "iso8859_9", - "latin_1", - ], - "iso8859_15": [ - "cp1252", - "cp1254", - "iso8859_10", - "iso8859_14", - "iso8859_16", - "iso8859_3", - "iso8859_9", - "latin_1", - ], - "iso8859_16": [ - "iso8859_14", - "iso8859_15", - "iso8859_2", - "iso8859_3", - "iso8859_9", - "latin_1", - ], - "iso8859_2": ["cp1250", "iso8859_16", "iso8859_4"], - "iso8859_3": ["iso8859_14", "iso8859_15", "iso8859_16", "iso8859_9", "latin_1"], - "iso8859_4": ["iso8859_10", "iso8859_2", "iso8859_9", "latin_1"], - "iso8859_7": ["cp1253"], - "iso8859_9": [ - "cp1252", - "cp1254", - "cp1258", - "iso8859_10", - "iso8859_14", - "iso8859_15", - "iso8859_16", - "iso8859_3", - "iso8859_4", - "latin_1", - ], - "kz1048": ["cp1251", "ptcp154"], - "latin_1": [ - "cp1252", - "cp1254", - "cp1258", - "iso8859_10", - "iso8859_14", - "iso8859_15", - "iso8859_16", - "iso8859_3", - "iso8859_4", - "iso8859_9", - ], - "mac_iceland": ["mac_roman", "mac_turkish"], - "mac_roman": ["mac_iceland", "mac_turkish"], - "mac_turkish": ["mac_iceland", "mac_roman"], - "ptcp154": ["cp1251", "kz1048"], - "tis_620": ["iso8859_11"], -} - - -CHARDET_CORRESPONDENCE: dict[str, str] = { - "iso2022_kr": "ISO-2022-KR", - "iso2022_jp": "ISO-2022-JP", - "euc_kr": "EUC-KR", - "tis_620": "TIS-620", - "utf_32": "UTF-32", - "euc_jp": "EUC-JP", - "koi8_r": "KOI8-R", - "iso8859_1": "ISO-8859-1", - "iso8859_2": "ISO-8859-2", - "iso8859_5": "ISO-8859-5", - "iso8859_6": "ISO-8859-6", - "iso8859_7": "ISO-8859-7", - "iso8859_8": "ISO-8859-8", - "utf_16": "UTF-16", - "cp855": "IBM855", - "mac_cyrillic": "MacCyrillic", - "gb2312": "GB2312", - "gb18030": "GB18030", - "cp932": "CP932", - "cp866": "IBM866", - "utf_8": "utf-8", - "utf_8_sig": "UTF-8-SIG", - "shift_jis": "SHIFT_JIS", - "big5": "Big5", - "cp1250": "windows-1250", - "cp1251": "windows-1251", - "cp1252": "Windows-1252", - "cp1253": "windows-1253", - "cp1255": "windows-1255", - "cp1256": "windows-1256", - "cp1254": "Windows-1254", - "cp949": "CP949", -} - - -COMMON_SAFE_ASCII_CHARACTERS: set[str] = { - "<", - ">", - "=", - ":", - "/", - "&", - ";", - "{", - "}", - "[", - "]", - ",", - "|", - '"', - "-", - "(", - ")", -} - -# Sample character sets — replace with full lists if needed -COMMON_CHINESE_CHARACTERS = "的一是在不了有和人这中大为上个国我以要他时来用们生到作地于出就分对成会可主发年动同工也能下过子说产种面而方后多定行学法所民得经十三之进着等部度家电力里如水化高自二理起小物现实加量都两体制机当使点从业本去把性好应开它合还因由其些然前外天政四日那社义事平形相全表间样与关各重新线内数正心反你明看原又么利比或但质气第向道命此变条只没结解问意建月公无系军很情者最立代想已通并提直题党程展五果料象员革位入常文总次品式活设及管特件长求老头基资边流路级少图山统接知较将组见计别她手角期根论运农指几九区强放决西被干做必战先回则任取据处队南给色光门即保治北造百规热领七海口东导器压志世金增争济阶油思术极交受联什认六共权收证改清己美再采转更单风切打白教速花带安场身车例真务具万每目至达走积示议声报斗完类八离华名确才科张信马节话米整空元况今集温传土许步群广石记需段研界拉林律叫且究观越织装影算低持音众书布复容儿须际商非验连断深难近矿千周委素技备半办青省列习响约支般史感劳便团往酸历市克何除消构府太准精值号率族维划选标写存候毛亲快效斯院查江型眼王按格养易置派层片始却专状育厂京识适属圆包火住调满县局照参红细引听该铁价严龙飞" - -COMMON_JAPANESE_CHARACTERS = "日一国年大十二本中長出三時行見月分後前生五間上東四今金九入学高円子外八六下来気小七山話女北午百書先名川千水半男西電校語土木聞食車何南万毎白天母火右読友左休父雨" - -COMMON_KOREAN_CHARACTERS = "一二三四五六七八九十百千萬上下左右中人女子大小山川日月火水木金土父母天地國名年時文校學生" - -# Combine all into a set -COMMON_CJK_CHARACTERS = set( - "".join( - [ - COMMON_CHINESE_CHARACTERS, - COMMON_JAPANESE_CHARACTERS, - COMMON_KOREAN_CHARACTERS, - ] - ) -) - -KO_NAMES: set[str] = {"johab", "cp949", "euc_kr"} -ZH_NAMES: set[str] = {"big5", "cp950", "big5hkscs", "hz"} - -# Logging LEVEL below DEBUG -TRACE: int = 5 - - -# Language label that contain the em dash "—" -# character are to be considered alternative seq to origin -FREQUENCIES: dict[str, list[str]] = { - "English": [ - "e", - "a", - "t", - "i", - "o", - "n", - "s", - "r", - "h", - "l", - "d", - "c", - "u", - "m", - "f", - "p", - "g", - "w", - "y", - "b", - "v", - "k", - "x", - "j", - "z", - "q", - ], - "English—": [ - "e", - "a", - "t", - "i", - "o", - "n", - "s", - "r", - "h", - "l", - "d", - "c", - "m", - "u", - "f", - "p", - "g", - "w", - "b", - "y", - "v", - "k", - "j", - "x", - "z", - "q", - ], - "German": [ - "e", - "n", - "i", - "r", - "s", - "t", - "a", - "d", - "h", - "u", - "l", - "g", - "o", - "c", - "m", - "b", - "f", - "k", - "w", - "z", - "p", - "v", - "ü", - "ä", - "ö", - "j", - ], - "French": [ - "e", - "a", - "s", - "n", - "i", - "t", - "r", - "l", - "u", - "o", - "d", - "c", - "p", - "m", - "é", - "v", - "g", - "f", - "b", - "h", - "q", - "à", - "x", - "è", - "y", - "j", - ], - "Dutch": [ - "e", - "n", - "a", - "i", - "r", - "t", - "o", - "d", - "s", - "l", - "g", - "h", - "v", - "m", - "u", - "k", - "c", - "p", - "b", - "w", - "j", - "z", - "f", - "y", - "x", - "ë", - ], - "Italian": [ - "e", - "i", - "a", - "o", - "n", - "l", - "t", - "r", - "s", - "c", - "d", - "u", - "p", - "m", - "g", - "v", - "f", - "b", - "z", - "h", - "q", - "è", - "à", - "k", - "y", - "ò", - ], - "Polish": [ - "a", - "i", - "o", - "e", - "n", - "r", - "z", - "w", - "s", - "c", - "t", - "k", - "y", - "d", - "p", - "m", - "u", - "l", - "j", - "ł", - "g", - "b", - "h", - "ą", - "ę", - "ó", - ], - "Spanish": [ - "e", - "a", - "o", - "n", - "s", - "r", - "i", - "l", - "d", - "t", - "c", - "u", - "m", - "p", - "b", - "g", - "v", - "f", - "y", - "ó", - "h", - "q", - "í", - "j", - "z", - "á", - ], - "Russian": [ - "о", - "а", - "е", - "и", - "н", - "с", - "т", - "р", - "в", - "л", - "к", - "м", - "д", - "п", - "у", - "г", - "я", - "ы", - "з", - "б", - "й", - "ь", - "ч", - "х", - "ж", - "ц", - ], - # Jap-Kanji - "Japanese": [ - "人", - "一", - "大", - "亅", - "丁", - "丨", - "竹", - "笑", - "口", - "日", - "今", - "二", - "彳", - "行", - "十", - "土", - "丶", - "寸", - "寺", - "時", - "乙", - "丿", - "乂", - "气", - "気", - "冂", - "巾", - "亠", - "市", - "目", - "儿", - "見", - "八", - "小", - "凵", - "県", - "月", - "彐", - "門", - "間", - "木", - "東", - "山", - "出", - "本", - "中", - "刀", - "分", - "耳", - "又", - "取", - "最", - "言", - "田", - "心", - "思", - "刂", - "前", - "京", - "尹", - "事", - "生", - "厶", - "云", - "会", - "未", - "来", - "白", - "冫", - "楽", - "灬", - "馬", - "尸", - "尺", - "駅", - "明", - "耂", - "者", - "了", - "阝", - "都", - "高", - "卜", - "占", - "厂", - "广", - "店", - "子", - "申", - "奄", - "亻", - "俺", - "上", - "方", - "冖", - "学", - "衣", - "艮", - "食", - "自", - ], - # Jap-Katakana - "Japanese—": [ - "ー", - "ン", - "ス", - "・", - "ル", - "ト", - "リ", - "イ", - "ア", - "ラ", - "ッ", - "ク", - "ド", - "シ", - "レ", - "ジ", - "タ", - "フ", - "ロ", - "カ", - "テ", - "マ", - "ィ", - "グ", - "バ", - "ム", - "プ", - "オ", - "コ", - "デ", - "ニ", - "ウ", - "メ", - "サ", - "ビ", - "ナ", - "ブ", - "ャ", - "エ", - "ュ", - "チ", - "キ", - "ズ", - "ダ", - "パ", - "ミ", - "ェ", - "ョ", - "ハ", - "セ", - "ベ", - "ガ", - "モ", - "ツ", - "ネ", - "ボ", - "ソ", - "ノ", - "ァ", - "ヴ", - "ワ", - "ポ", - "ペ", - "ピ", - "ケ", - "ゴ", - "ギ", - "ザ", - "ホ", - "ゲ", - "ォ", - "ヤ", - "ヒ", - "ユ", - "ヨ", - "ヘ", - "ゼ", - "ヌ", - "ゥ", - "ゾ", - "ヶ", - "ヂ", - "ヲ", - "ヅ", - "ヵ", - "ヱ", - "ヰ", - "ヮ", - "ヽ", - "゠", - "ヾ", - "ヷ", - "ヿ", - "ヸ", - "ヹ", - "ヺ", - ], - # Jap-Hiragana - "Japanese——": [ - "の", - "に", - "る", - "た", - "と", - "は", - "し", - "い", - "を", - "で", - "て", - "が", - "な", - "れ", - "か", - "ら", - "さ", - "っ", - "り", - "す", - "あ", - "も", - "こ", - "ま", - "う", - "く", - "よ", - "き", - "ん", - "め", - "お", - "け", - "そ", - "つ", - "だ", - "や", - "え", - "ど", - "わ", - "ち", - "み", - "せ", - "じ", - "ば", - "へ", - "び", - "ず", - "ろ", - "ほ", - "げ", - "む", - "べ", - "ひ", - "ょ", - "ゆ", - "ぶ", - "ご", - "ゃ", - "ね", - "ふ", - "ぐ", - "ぎ", - "ぼ", - "ゅ", - "づ", - "ざ", - "ぞ", - "ぬ", - "ぜ", - "ぱ", - "ぽ", - "ぷ", - "ぴ", - "ぃ", - "ぁ", - "ぇ", - "ぺ", - "ゞ", - "ぢ", - "ぉ", - "ぅ", - "ゐ", - "ゝ", - "ゑ", - "゛", - "゜", - "ゎ", - "ゔ", - "゚", - "ゟ", - "゙", - "ゕ", - "ゖ", - ], - "Portuguese": [ - "a", - "e", - "o", - "s", - "i", - "r", - "d", - "n", - "t", - "m", - "u", - "c", - "l", - "p", - "g", - "v", - "b", - "f", - "h", - "ã", - "q", - "é", - "ç", - "á", - "z", - "í", - ], - "Swedish": [ - "e", - "a", - "n", - "r", - "t", - "s", - "i", - "l", - "d", - "o", - "m", - "k", - "g", - "v", - "h", - "f", - "u", - "p", - "ä", - "c", - "b", - "ö", - "å", - "y", - "j", - "x", - ], - "Chinese": [ - "的", - "一", - "是", - "不", - "了", - "在", - "人", - "有", - "我", - "他", - "这", - "个", - "们", - "中", - "来", - "上", - "大", - "为", - "和", - "国", - "地", - "到", - "以", - "说", - "时", - "要", - "就", - "出", - "会", - "可", - "也", - "你", - "对", - "生", - "能", - "而", - "子", - "那", - "得", - "于", - "着", - "下", - "自", - "之", - "年", - "过", - "发", - "后", - "作", - "里", - "用", - "道", - "行", - "所", - "然", - "家", - "种", - "事", - "成", - "方", - "多", - "经", - "么", - "去", - "法", - "学", - "如", - "都", - "同", - "现", - "当", - "没", - "动", - "面", - "起", - "看", - "定", - "天", - "分", - "还", - "进", - "好", - "小", - "部", - "其", - "些", - "主", - "样", - "理", - "心", - "她", - "本", - "前", - "开", - "但", - "因", - "只", - "从", - "想", - "实", - ], - "Ukrainian": [ - "о", - "а", - "н", - "і", - "и", - "р", - "в", - "т", - "е", - "с", - "к", - "л", - "у", - "д", - "м", - "п", - "з", - "я", - "ь", - "б", - "г", - "й", - "ч", - "х", - "ц", - "ї", - ], - "Norwegian": [ - "e", - "r", - "n", - "t", - "a", - "s", - "i", - "o", - "l", - "d", - "g", - "k", - "m", - "v", - "f", - "p", - "u", - "b", - "h", - "å", - "y", - "j", - "ø", - "c", - "æ", - "w", - ], - "Finnish": [ - "a", - "i", - "n", - "t", - "e", - "s", - "l", - "o", - "u", - "k", - "ä", - "m", - "r", - "v", - "j", - "h", - "p", - "y", - "d", - "ö", - "g", - "c", - "b", - "f", - "w", - "z", - ], - "Vietnamese": [ - "n", - "h", - "t", - "i", - "c", - "g", - "a", - "o", - "u", - "m", - "l", - "r", - "à", - "đ", - "s", - "e", - "v", - "p", - "b", - "y", - "ư", - "d", - "á", - "k", - "ộ", - "ế", - ], - "Czech": [ - "o", - "e", - "a", - "n", - "t", - "s", - "i", - "l", - "v", - "r", - "k", - "d", - "u", - "m", - "p", - "í", - "c", - "h", - "z", - "á", - "y", - "j", - "b", - "ě", - "é", - "ř", - ], - "Hungarian": [ - "e", - "a", - "t", - "l", - "s", - "n", - "k", - "r", - "i", - "o", - "z", - "á", - "é", - "g", - "m", - "b", - "y", - "v", - "d", - "h", - "u", - "p", - "j", - "ö", - "f", - "c", - ], - "Korean": [ - "이", - "다", - "에", - "의", - "는", - "로", - "하", - "을", - "가", - "고", - "지", - "서", - "한", - "은", - "기", - "으", - "년", - "대", - "사", - "시", - "를", - "리", - "도", - "인", - "스", - "일", - ], - "Indonesian": [ - "a", - "n", - "e", - "i", - "r", - "t", - "u", - "s", - "d", - "k", - "m", - "l", - "g", - "p", - "b", - "o", - "h", - "y", - "j", - "c", - "w", - "f", - "v", - "z", - "x", - "q", - ], - "Turkish": [ - "a", - "e", - "i", - "n", - "r", - "l", - "ı", - "k", - "d", - "t", - "s", - "m", - "y", - "u", - "o", - "b", - "ü", - "ş", - "v", - "g", - "z", - "h", - "c", - "p", - "ç", - "ğ", - ], - "Romanian": [ - "e", - "i", - "a", - "r", - "n", - "t", - "u", - "l", - "o", - "c", - "s", - "d", - "p", - "m", - "ă", - "f", - "v", - "î", - "g", - "b", - "ș", - "ț", - "z", - "h", - "â", - "j", - ], - "Farsi": [ - "ا", - "ی", - "ر", - "د", - "ن", - "ه", - "و", - "م", - "ت", - "ب", - "س", - "ل", - "ک", - "ش", - "ز", - "ف", - "گ", - "ع", - "خ", - "ق", - "ج", - "آ", - "پ", - "ح", - "ط", - "ص", - ], - "Arabic": [ - "ا", - "ل", - "ي", - "م", - "و", - "ن", - "ر", - "ت", - "ب", - "ة", - "ع", - "د", - "س", - "ف", - "ه", - "ك", - "ق", - "أ", - "ح", - "ج", - "ش", - "ط", - "ص", - "ى", - "خ", - "إ", - ], - "Danish": [ - "e", - "r", - "n", - "t", - "a", - "i", - "s", - "d", - "l", - "o", - "g", - "m", - "k", - "f", - "v", - "u", - "b", - "h", - "p", - "å", - "y", - "ø", - "æ", - "c", - "j", - "w", - ], - "Serbian": [ - "а", - "и", - "о", - "е", - "н", - "р", - "с", - "у", - "т", - "к", - "ј", - "в", - "д", - "м", - "п", - "л", - "г", - "з", - "б", - "a", - "i", - "e", - "o", - "n", - "ц", - "ш", - ], - "Lithuanian": [ - "i", - "a", - "s", - "o", - "r", - "e", - "t", - "n", - "u", - "k", - "m", - "l", - "p", - "v", - "d", - "j", - "g", - "ė", - "b", - "y", - "ų", - "š", - "ž", - "c", - "ą", - "į", - ], - "Slovene": [ - "e", - "a", - "i", - "o", - "n", - "r", - "s", - "l", - "t", - "j", - "v", - "k", - "d", - "p", - "m", - "u", - "z", - "b", - "g", - "h", - "č", - "c", - "š", - "ž", - "f", - "y", - ], - "Slovak": [ - "o", - "a", - "e", - "n", - "i", - "r", - "v", - "t", - "s", - "l", - "k", - "d", - "m", - "p", - "u", - "c", - "h", - "j", - "b", - "z", - "á", - "y", - "ý", - "í", - "č", - "é", - ], - "Hebrew": [ - "י", - "ו", - "ה", - "ל", - "ר", - "ב", - "ת", - "מ", - "א", - "ש", - "נ", - "ע", - "ם", - "ד", - "ק", - "ח", - "פ", - "ס", - "כ", - "ג", - "ט", - "צ", - "ן", - "ז", - "ך", - ], - "Bulgarian": [ - "а", - "и", - "о", - "е", - "н", - "т", - "р", - "с", - "в", - "л", - "к", - "д", - "п", - "м", - "з", - "г", - "я", - "ъ", - "у", - "б", - "ч", - "ц", - "й", - "ж", - "щ", - "х", - ], - "Croatian": [ - "a", - "i", - "o", - "e", - "n", - "r", - "j", - "s", - "t", - "u", - "k", - "l", - "v", - "d", - "m", - "p", - "g", - "z", - "b", - "c", - "č", - "h", - "š", - "ž", - "ć", - "f", - ], - "Hindi": [ - "क", - "र", - "स", - "न", - "त", - "म", - "ह", - "प", - "य", - "ल", - "व", - "ज", - "द", - "ग", - "ब", - "श", - "ट", - "अ", - "ए", - "थ", - "भ", - "ड", - "च", - "ध", - "ष", - "इ", - ], - "Estonian": [ - "a", - "i", - "e", - "s", - "t", - "l", - "u", - "n", - "o", - "k", - "r", - "d", - "m", - "v", - "g", - "p", - "j", - "h", - "ä", - "b", - "õ", - "ü", - "f", - "c", - "ö", - "y", - ], - "Thai": [ - "า", - "น", - "ร", - "อ", - "ก", - "เ", - "ง", - "ม", - "ย", - "ล", - "ว", - "ด", - "ท", - "ส", - "ต", - "ะ", - "ป", - "บ", - "ค", - "ห", - "แ", - "จ", - "พ", - "ช", - "ข", - "ใ", - ], - "Greek": [ - "α", - "τ", - "ο", - "ι", - "ε", - "ν", - "ρ", - "σ", - "κ", - "η", - "π", - "ς", - "υ", - "μ", - "λ", - "ί", - "ό", - "ά", - "γ", - "έ", - "δ", - "ή", - "ω", - "χ", - "θ", - "ύ", - ], - "Tamil": [ - "க", - "த", - "ப", - "ட", - "ர", - "ம", - "ல", - "ன", - "வ", - "ற", - "ய", - "ள", - "ச", - "ந", - "இ", - "ண", - "அ", - "ஆ", - "ழ", - "ங", - "எ", - "உ", - "ஒ", - "ஸ", - ], - "Kazakh": [ - "а", - "ы", - "е", - "н", - "т", - "р", - "л", - "і", - "д", - "с", - "м", - "қ", - "к", - "о", - "б", - "и", - "у", - "ғ", - "ж", - "ң", - "з", - "ш", - "й", - "п", - "г", - "ө", - ], -} - -LANGUAGE_SUPPORTED_COUNT: int = len(FREQUENCIES) diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer/legacy.py b/apps/bitwarden_event_logs/lib/charset_normalizer/legacy.py deleted file mode 100755 index 360a3107..00000000 --- a/apps/bitwarden_event_logs/lib/charset_normalizer/legacy.py +++ /dev/null @@ -1,80 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any -from warnings import warn - -from .api import from_bytes -from .constant import CHARDET_CORRESPONDENCE, TOO_SMALL_SEQUENCE - -# TODO: remove this check when dropping Python 3.7 support -if TYPE_CHECKING: - from typing_extensions import TypedDict - - class ResultDict(TypedDict): - encoding: str | None - language: str - confidence: float | None - - -def detect( - byte_str: bytes, should_rename_legacy: bool = False, **kwargs: Any -) -> ResultDict: - """ - chardet legacy method - Detect the encoding of the given byte string. It should be mostly backward-compatible. - Encoding name will match Chardet own writing whenever possible. (Not on encoding name unsupported by it) - This function is deprecated and should be used to migrate your project easily, consult the documentation for - further information. Not planned for removal. - - :param byte_str: The byte sequence to examine. - :param should_rename_legacy: Should we rename legacy encodings - to their more modern equivalents? - """ - if len(kwargs): - warn( - f"charset-normalizer disregard arguments '{','.join(list(kwargs.keys()))}' in legacy function detect()" - ) - - if not isinstance(byte_str, (bytearray, bytes)): - raise TypeError( # pragma: nocover - f"Expected object of type bytes or bytearray, got: {type(byte_str)}" - ) - - if isinstance(byte_str, bytearray): - byte_str = bytes(byte_str) - - r = from_bytes(byte_str).best() - - encoding = r.encoding if r is not None else None - language = r.language if r is not None and r.language != "Unknown" else "" - confidence = 1.0 - r.chaos if r is not None else None - - # automatically lower confidence - # on small bytes samples. - # https://github.com/jawah/charset_normalizer/issues/391 - if ( - confidence is not None - and confidence >= 0.9 - and encoding - not in { - "utf_8", - "ascii", - } - and r.bom is False # type: ignore[union-attr] - and len(byte_str) < TOO_SMALL_SEQUENCE - ): - confidence -= 0.2 - - # Note: CharsetNormalizer does not return 'UTF-8-SIG' as the sig get stripped in the detection/normalization process - # but chardet does return 'utf-8-sig' and it is a valid codec name. - if r is not None and encoding == "utf_8" and r.bom: - encoding += "_sig" - - if should_rename_legacy is False and encoding in CHARDET_CORRESPONDENCE: - encoding = CHARDET_CORRESPONDENCE[encoding] - - return { - "encoding": encoding, - "language": language, - "confidence": confidence, - } diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer/md.cpython-39-aarch64-linux-gnu.so b/apps/bitwarden_event_logs/lib/charset_normalizer/md.cpython-39-aarch64-linux-gnu.so deleted file mode 100755 index 821ffe28f0c706da57b8b1f7dbdad22cde761b0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 201304 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 OA6OODuSygon&w{?Y2k(d diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer/md.py b/apps/bitwarden_event_logs/lib/charset_normalizer/md.py deleted file mode 100755 index 12ce024b..00000000 --- a/apps/bitwarden_event_logs/lib/charset_normalizer/md.py +++ /dev/null @@ -1,635 +0,0 @@ -from __future__ import annotations - -from functools import lru_cache -from logging import getLogger - -from .constant import ( - COMMON_SAFE_ASCII_CHARACTERS, - TRACE, - UNICODE_SECONDARY_RANGE_KEYWORD, -) -from .utils import ( - is_accentuated, - is_arabic, - is_arabic_isolated_form, - is_case_variable, - is_cjk, - is_emoticon, - is_hangul, - is_hiragana, - is_katakana, - is_latin, - is_punctuation, - is_separator, - is_symbol, - is_thai, - is_unprintable, - remove_accent, - unicode_range, - is_cjk_uncommon, -) - - -class MessDetectorPlugin: - """ - Base abstract class used for mess detection plugins. - All detectors MUST extend and implement given methods. - """ - - def eligible(self, character: str) -> bool: - """ - Determine if given character should be fed in. - """ - raise NotImplementedError # pragma: nocover - - def feed(self, character: str) -> None: - """ - The main routine to be executed upon character. - Insert the logic in witch the text would be considered chaotic. - """ - raise NotImplementedError # pragma: nocover - - def reset(self) -> None: # pragma: no cover - """ - Permit to reset the plugin to the initial state. - """ - raise NotImplementedError - - @property - def ratio(self) -> float: - """ - Compute the chaos ratio based on what your feed() has seen. - Must NOT be lower than 0.; No restriction gt 0. - """ - raise NotImplementedError # pragma: nocover - - -class TooManySymbolOrPunctuationPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._punctuation_count: int = 0 - self._symbol_count: int = 0 - self._character_count: int = 0 - - self._last_printable_char: str | None = None - self._frenzy_symbol_in_word: bool = False - - def eligible(self, character: str) -> bool: - return character.isprintable() - - def feed(self, character: str) -> None: - self._character_count += 1 - - if ( - character != self._last_printable_char - and character not in COMMON_SAFE_ASCII_CHARACTERS - ): - if is_punctuation(character): - self._punctuation_count += 1 - elif ( - character.isdigit() is False - and is_symbol(character) - and is_emoticon(character) is False - ): - self._symbol_count += 2 - - self._last_printable_char = character - - def reset(self) -> None: # Abstract - self._punctuation_count = 0 - self._character_count = 0 - self._symbol_count = 0 - - @property - def ratio(self) -> float: - if self._character_count == 0: - return 0.0 - - ratio_of_punctuation: float = ( - self._punctuation_count + self._symbol_count - ) / self._character_count - - return ratio_of_punctuation if ratio_of_punctuation >= 0.3 else 0.0 - - -class TooManyAccentuatedPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._character_count: int = 0 - self._accentuated_count: int = 0 - - def eligible(self, character: str) -> bool: - return character.isalpha() - - def feed(self, character: str) -> None: - self._character_count += 1 - - if is_accentuated(character): - self._accentuated_count += 1 - - def reset(self) -> None: # Abstract - self._character_count = 0 - self._accentuated_count = 0 - - @property - def ratio(self) -> float: - if self._character_count < 8: - return 0.0 - - ratio_of_accentuation: float = self._accentuated_count / self._character_count - return ratio_of_accentuation if ratio_of_accentuation >= 0.35 else 0.0 - - -class UnprintablePlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._unprintable_count: int = 0 - self._character_count: int = 0 - - def eligible(self, character: str) -> bool: - return True - - def feed(self, character: str) -> None: - if is_unprintable(character): - self._unprintable_count += 1 - self._character_count += 1 - - def reset(self) -> None: # Abstract - self._unprintable_count = 0 - - @property - def ratio(self) -> float: - if self._character_count == 0: - return 0.0 - - return (self._unprintable_count * 8) / self._character_count - - -class SuspiciousDuplicateAccentPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._successive_count: int = 0 - self._character_count: int = 0 - - self._last_latin_character: str | None = None - - def eligible(self, character: str) -> bool: - return character.isalpha() and is_latin(character) - - def feed(self, character: str) -> None: - self._character_count += 1 - if ( - self._last_latin_character is not None - and is_accentuated(character) - and is_accentuated(self._last_latin_character) - ): - if character.isupper() and self._last_latin_character.isupper(): - self._successive_count += 1 - # Worse if its the same char duplicated with different accent. - if remove_accent(character) == remove_accent(self._last_latin_character): - self._successive_count += 1 - self._last_latin_character = character - - def reset(self) -> None: # Abstract - self._successive_count = 0 - self._character_count = 0 - self._last_latin_character = None - - @property - def ratio(self) -> float: - if self._character_count == 0: - return 0.0 - - return (self._successive_count * 2) / self._character_count - - -class SuspiciousRange(MessDetectorPlugin): - def __init__(self) -> None: - self._suspicious_successive_range_count: int = 0 - self._character_count: int = 0 - self._last_printable_seen: str | None = None - - def eligible(self, character: str) -> bool: - return character.isprintable() - - def feed(self, character: str) -> None: - self._character_count += 1 - - if ( - character.isspace() - or is_punctuation(character) - or character in COMMON_SAFE_ASCII_CHARACTERS - ): - self._last_printable_seen = None - return - - if self._last_printable_seen is None: - self._last_printable_seen = character - return - - unicode_range_a: str | None = unicode_range(self._last_printable_seen) - unicode_range_b: str | None = unicode_range(character) - - if is_suspiciously_successive_range(unicode_range_a, unicode_range_b): - self._suspicious_successive_range_count += 1 - - self._last_printable_seen = character - - def reset(self) -> None: # Abstract - self._character_count = 0 - self._suspicious_successive_range_count = 0 - self._last_printable_seen = None - - @property - def ratio(self) -> float: - if self._character_count <= 13: - return 0.0 - - ratio_of_suspicious_range_usage: float = ( - self._suspicious_successive_range_count * 2 - ) / self._character_count - - return ratio_of_suspicious_range_usage - - -class SuperWeirdWordPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._word_count: int = 0 - self._bad_word_count: int = 0 - self._foreign_long_count: int = 0 - - self._is_current_word_bad: bool = False - self._foreign_long_watch: bool = False - - self._character_count: int = 0 - self._bad_character_count: int = 0 - - self._buffer: str = "" - self._buffer_accent_count: int = 0 - self._buffer_glyph_count: int = 0 - - def eligible(self, character: str) -> bool: - return True - - def feed(self, character: str) -> None: - if character.isalpha(): - self._buffer += character - if is_accentuated(character): - self._buffer_accent_count += 1 - if ( - self._foreign_long_watch is False - and (is_latin(character) is False or is_accentuated(character)) - and is_cjk(character) is False - and is_hangul(character) is False - and is_katakana(character) is False - and is_hiragana(character) is False - and is_thai(character) is False - ): - self._foreign_long_watch = True - if ( - is_cjk(character) - or is_hangul(character) - or is_katakana(character) - or is_hiragana(character) - or is_thai(character) - ): - self._buffer_glyph_count += 1 - return - if not self._buffer: - return - if ( - character.isspace() or is_punctuation(character) or is_separator(character) - ) and self._buffer: - self._word_count += 1 - buffer_length: int = len(self._buffer) - - self._character_count += buffer_length - - if buffer_length >= 4: - if self._buffer_accent_count / buffer_length >= 0.5: - self._is_current_word_bad = True - # Word/Buffer ending with an upper case accentuated letter are so rare, - # that we will consider them all as suspicious. Same weight as foreign_long suspicious. - elif ( - is_accentuated(self._buffer[-1]) - and self._buffer[-1].isupper() - and all(_.isupper() for _ in self._buffer) is False - ): - self._foreign_long_count += 1 - self._is_current_word_bad = True - elif self._buffer_glyph_count == 1: - self._is_current_word_bad = True - self._foreign_long_count += 1 - if buffer_length >= 24 and self._foreign_long_watch: - camel_case_dst = [ - i - for c, i in zip(self._buffer, range(0, buffer_length)) - if c.isupper() - ] - probable_camel_cased: bool = False - - if camel_case_dst and (len(camel_case_dst) / buffer_length <= 0.3): - probable_camel_cased = True - - if not probable_camel_cased: - self._foreign_long_count += 1 - self._is_current_word_bad = True - - if self._is_current_word_bad: - self._bad_word_count += 1 - self._bad_character_count += len(self._buffer) - self._is_current_word_bad = False - - self._foreign_long_watch = False - self._buffer = "" - self._buffer_accent_count = 0 - self._buffer_glyph_count = 0 - elif ( - character not in {"<", ">", "-", "=", "~", "|", "_"} - and character.isdigit() is False - and is_symbol(character) - ): - self._is_current_word_bad = True - self._buffer += character - - def reset(self) -> None: # Abstract - self._buffer = "" - self._is_current_word_bad = False - self._foreign_long_watch = False - self._bad_word_count = 0 - self._word_count = 0 - self._character_count = 0 - self._bad_character_count = 0 - self._foreign_long_count = 0 - - @property - def ratio(self) -> float: - if self._word_count <= 10 and self._foreign_long_count == 0: - return 0.0 - - return self._bad_character_count / self._character_count - - -class CjkUncommonPlugin(MessDetectorPlugin): - """ - Detect messy CJK text that probably means nothing. - """ - - def __init__(self) -> None: - self._character_count: int = 0 - self._uncommon_count: int = 0 - - def eligible(self, character: str) -> bool: - return is_cjk(character) - - def feed(self, character: str) -> None: - self._character_count += 1 - - if is_cjk_uncommon(character): - self._uncommon_count += 1 - return - - def reset(self) -> None: # Abstract - self._character_count = 0 - self._uncommon_count = 0 - - @property - def ratio(self) -> float: - if self._character_count < 8: - return 0.0 - - uncommon_form_usage: float = self._uncommon_count / self._character_count - - # we can be pretty sure it's garbage when uncommon characters are widely - # used. otherwise it could just be traditional chinese for example. - return uncommon_form_usage / 10 if uncommon_form_usage > 0.5 else 0.0 - - -class ArchaicUpperLowerPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._buf: bool = False - - self._character_count_since_last_sep: int = 0 - - self._successive_upper_lower_count: int = 0 - self._successive_upper_lower_count_final: int = 0 - - self._character_count: int = 0 - - self._last_alpha_seen: str | None = None - self._current_ascii_only: bool = True - - def eligible(self, character: str) -> bool: - return True - - def feed(self, character: str) -> None: - is_concerned = character.isalpha() and is_case_variable(character) - chunk_sep = is_concerned is False - - if chunk_sep and self._character_count_since_last_sep > 0: - if ( - self._character_count_since_last_sep <= 64 - and character.isdigit() is False - and self._current_ascii_only is False - ): - self._successive_upper_lower_count_final += ( - self._successive_upper_lower_count - ) - - self._successive_upper_lower_count = 0 - self._character_count_since_last_sep = 0 - self._last_alpha_seen = None - self._buf = False - self._character_count += 1 - self._current_ascii_only = True - - return - - if self._current_ascii_only is True and character.isascii() is False: - self._current_ascii_only = False - - if self._last_alpha_seen is not None: - if (character.isupper() and self._last_alpha_seen.islower()) or ( - character.islower() and self._last_alpha_seen.isupper() - ): - if self._buf is True: - self._successive_upper_lower_count += 2 - self._buf = False - else: - self._buf = True - else: - self._buf = False - - self._character_count += 1 - self._character_count_since_last_sep += 1 - self._last_alpha_seen = character - - def reset(self) -> None: # Abstract - self._character_count = 0 - self._character_count_since_last_sep = 0 - self._successive_upper_lower_count = 0 - self._successive_upper_lower_count_final = 0 - self._last_alpha_seen = None - self._buf = False - self._current_ascii_only = True - - @property - def ratio(self) -> float: - if self._character_count == 0: - return 0.0 - - return self._successive_upper_lower_count_final / self._character_count - - -class ArabicIsolatedFormPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._character_count: int = 0 - self._isolated_form_count: int = 0 - - def reset(self) -> None: # Abstract - self._character_count = 0 - self._isolated_form_count = 0 - - def eligible(self, character: str) -> bool: - return is_arabic(character) - - def feed(self, character: str) -> None: - self._character_count += 1 - - if is_arabic_isolated_form(character): - self._isolated_form_count += 1 - - @property - def ratio(self) -> float: - if self._character_count < 8: - return 0.0 - - isolated_form_usage: float = self._isolated_form_count / self._character_count - - return isolated_form_usage - - -@lru_cache(maxsize=1024) -def is_suspiciously_successive_range( - unicode_range_a: str | None, unicode_range_b: str | None -) -> bool: - """ - Determine if two Unicode range seen next to each other can be considered as suspicious. - """ - if unicode_range_a is None or unicode_range_b is None: - return True - - if unicode_range_a == unicode_range_b: - return False - - if "Latin" in unicode_range_a and "Latin" in unicode_range_b: - return False - - if "Emoticons" in unicode_range_a or "Emoticons" in unicode_range_b: - return False - - # Latin characters can be accompanied with a combining diacritical mark - # eg. Vietnamese. - if ("Latin" in unicode_range_a or "Latin" in unicode_range_b) and ( - "Combining" in unicode_range_a or "Combining" in unicode_range_b - ): - return False - - keywords_range_a, keywords_range_b = ( - unicode_range_a.split(" "), - unicode_range_b.split(" "), - ) - - for el in keywords_range_a: - if el in UNICODE_SECONDARY_RANGE_KEYWORD: - continue - if el in keywords_range_b: - return False - - # Japanese Exception - range_a_jp_chars, range_b_jp_chars = ( - unicode_range_a - in ( - "Hiragana", - "Katakana", - ), - unicode_range_b in ("Hiragana", "Katakana"), - ) - if (range_a_jp_chars or range_b_jp_chars) and ( - "CJK" in unicode_range_a or "CJK" in unicode_range_b - ): - return False - if range_a_jp_chars and range_b_jp_chars: - return False - - if "Hangul" in unicode_range_a or "Hangul" in unicode_range_b: - if "CJK" in unicode_range_a or "CJK" in unicode_range_b: - return False - if unicode_range_a == "Basic Latin" or unicode_range_b == "Basic Latin": - return False - - # Chinese/Japanese use dedicated range for punctuation and/or separators. - if ("CJK" in unicode_range_a or "CJK" in unicode_range_b) or ( - unicode_range_a in ["Katakana", "Hiragana"] - and unicode_range_b in ["Katakana", "Hiragana"] - ): - if "Punctuation" in unicode_range_a or "Punctuation" in unicode_range_b: - return False - if "Forms" in unicode_range_a or "Forms" in unicode_range_b: - return False - if unicode_range_a == "Basic Latin" or unicode_range_b == "Basic Latin": - return False - - return True - - -@lru_cache(maxsize=2048) -def mess_ratio( - decoded_sequence: str, maximum_threshold: float = 0.2, debug: bool = False -) -> float: - """ - Compute a mess ratio given a decoded bytes sequence. The maximum threshold does stop the computation earlier. - """ - - detectors: list[MessDetectorPlugin] = [ - md_class() for md_class in MessDetectorPlugin.__subclasses__() - ] - - length: int = len(decoded_sequence) + 1 - - mean_mess_ratio: float = 0.0 - - if length < 512: - intermediary_mean_mess_ratio_calc: int = 32 - elif length <= 1024: - intermediary_mean_mess_ratio_calc = 64 - else: - intermediary_mean_mess_ratio_calc = 128 - - for character, index in zip(decoded_sequence + "\n", range(length)): - for detector in detectors: - if detector.eligible(character): - detector.feed(character) - - if ( - index > 0 and index % intermediary_mean_mess_ratio_calc == 0 - ) or index == length - 1: - mean_mess_ratio = sum(dt.ratio for dt in detectors) - - if mean_mess_ratio >= maximum_threshold: - break - - if debug: - logger = getLogger("charset_normalizer") - - logger.log( - TRACE, - "Mess-detector extended-analysis start. " - f"intermediary_mean_mess_ratio_calc={intermediary_mean_mess_ratio_calc} mean_mess_ratio={mean_mess_ratio} " - f"maximum_threshold={maximum_threshold}", - ) - - if len(decoded_sequence) > 16: - logger.log(TRACE, f"Starting with: {decoded_sequence[:16]}") - logger.log(TRACE, f"Ending with: {decoded_sequence[-16::]}") - - for dt in detectors: - logger.log(TRACE, f"{dt.__class__}: {dt.ratio}") - - return round(mean_mess_ratio, 3) diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer/md__mypyc.cpython-39-aarch64-linux-gnu.so b/apps/bitwarden_event_logs/lib/charset_normalizer/md__mypyc.cpython-39-aarch64-linux-gnu.so deleted file mode 100755 index a9018a177f880fd0c12794135b2b8cb0f3e9d878..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 324120 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

7p!pfh&vM}xFn{zPG++F-k@39OHNH~|;}b4= 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 Z bool: - if not isinstance(other, CharsetMatch): - if isinstance(other, str): - return iana_name(other) == self.encoding - return False - return self.encoding == other.encoding and self.fingerprint == other.fingerprint - - def __lt__(self, other: object) -> bool: - """ - Implemented to make sorted available upon CharsetMatches items. - """ - if not isinstance(other, CharsetMatch): - raise ValueError - - chaos_difference: float = abs(self.chaos - other.chaos) - coherence_difference: float = abs(self.coherence - other.coherence) - - # Below 1% difference --> Use Coherence - if chaos_difference < 0.01 and coherence_difference > 0.02: - return self.coherence > other.coherence - elif chaos_difference < 0.01 and coherence_difference <= 0.02: - # When having a difficult decision, use the result that decoded as many multi-byte as possible. - # preserve RAM usage! - if len(self._payload) >= TOO_BIG_SEQUENCE: - return self.chaos < other.chaos - return self.multi_byte_usage > other.multi_byte_usage - - return self.chaos < other.chaos - - @property - def multi_byte_usage(self) -> float: - return 1.0 - (len(str(self)) / len(self.raw)) - - def __str__(self) -> str: - # Lazy Str Loading - if self._string is None: - self._string = str(self._payload, self._encoding, "strict") - return self._string - - def __repr__(self) -> str: - return f"" - - def add_submatch(self, other: CharsetMatch) -> None: - if not isinstance(other, CharsetMatch) or other == self: - raise ValueError( - "Unable to add instance <{}> as a submatch of a CharsetMatch".format( - other.__class__ - ) - ) - - other._string = None # Unload RAM usage; dirty trick. - self._leaves.append(other) - - @property - def encoding(self) -> str: - return self._encoding - - @property - def encoding_aliases(self) -> list[str]: - """ - Encoding name are known by many name, using this could help when searching for IBM855 when it's listed as CP855. - """ - also_known_as: list[str] = [] - for u, p in aliases.items(): - if self.encoding == u: - also_known_as.append(p) - elif self.encoding == p: - also_known_as.append(u) - return also_known_as - - @property - def bom(self) -> bool: - return self._has_sig_or_bom - - @property - def byte_order_mark(self) -> bool: - return self._has_sig_or_bom - - @property - def languages(self) -> list[str]: - """ - Return the complete list of possible languages found in decoded sequence. - Usually not really useful. Returned list may be empty even if 'language' property return something != 'Unknown'. - """ - return [e[0] for e in self._languages] - - @property - def language(self) -> str: - """ - Most probable language found in decoded sequence. If none were detected or inferred, the property will return - "Unknown". - """ - if not self._languages: - # Trying to infer the language based on the given encoding - # Its either English or we should not pronounce ourselves in certain cases. - if "ascii" in self.could_be_from_charset: - return "English" - - # doing it there to avoid circular import - from charset_normalizer.cd import encoding_languages, mb_encoding_languages - - languages = ( - mb_encoding_languages(self.encoding) - if is_multi_byte_encoding(self.encoding) - else encoding_languages(self.encoding) - ) - - if len(languages) == 0 or "Latin Based" in languages: - return "Unknown" - - return languages[0] - - return self._languages[0][0] - - @property - def chaos(self) -> float: - return self._mean_mess_ratio - - @property - def coherence(self) -> float: - if not self._languages: - return 0.0 - return self._languages[0][1] - - @property - def percent_chaos(self) -> float: - return round(self.chaos * 100, ndigits=3) - - @property - def percent_coherence(self) -> float: - return round(self.coherence * 100, ndigits=3) - - @property - def raw(self) -> bytes: - """ - Original untouched bytes. - """ - return self._payload - - @property - def submatch(self) -> list[CharsetMatch]: - return self._leaves - - @property - def has_submatch(self) -> bool: - return len(self._leaves) > 0 - - @property - def alphabets(self) -> list[str]: - if self._unicode_ranges is not None: - return self._unicode_ranges - # list detected ranges - detected_ranges: list[str | None] = [unicode_range(char) for char in str(self)] - # filter and sort - self._unicode_ranges = sorted(list({r for r in detected_ranges if r})) - return self._unicode_ranges - - @property - def could_be_from_charset(self) -> list[str]: - """ - The complete list of encoding that output the exact SAME str result and therefore could be the originating - encoding. - This list does include the encoding available in property 'encoding'. - """ - return [self._encoding] + [m.encoding for m in self._leaves] - - def output(self, encoding: str = "utf_8") -> bytes: - """ - Method to get re-encoded bytes payload using given target encoding. Default to UTF-8. - Any errors will be simply ignored by the encoder NOT replaced. - """ - if self._output_encoding is None or self._output_encoding != encoding: - self._output_encoding = encoding - decoded_string = str(self) - if ( - self._preemptive_declaration is not None - and self._preemptive_declaration.lower() - not in ["utf-8", "utf8", "utf_8"] - ): - patched_header = sub( - RE_POSSIBLE_ENCODING_INDICATION, - lambda m: m.string[m.span()[0] : m.span()[1]].replace( - m.groups()[0], - iana_name(self._output_encoding).replace("_", "-"), # type: ignore[arg-type] - ), - decoded_string[:8192], - count=1, - ) - - decoded_string = patched_header + decoded_string[8192:] - - self._output_payload = decoded_string.encode(encoding, "replace") - - return self._output_payload # type: ignore - - @property - def fingerprint(self) -> str: - """ - Retrieve the unique SHA256 computed using the transformed (re-encoded) payload. Not the original one. - """ - return sha256(self.output()).hexdigest() - - -class CharsetMatches: - """ - Container with every CharsetMatch items ordered by default from most probable to the less one. - Act like a list(iterable) but does not implements all related methods. - """ - - def __init__(self, results: list[CharsetMatch] | None = None): - self._results: list[CharsetMatch] = sorted(results) if results else [] - - def __iter__(self) -> Iterator[CharsetMatch]: - yield from self._results - - def __getitem__(self, item: int | str) -> CharsetMatch: - """ - Retrieve a single item either by its position or encoding name (alias may be used here). - Raise KeyError upon invalid index or encoding not present in results. - """ - if isinstance(item, int): - return self._results[item] - if isinstance(item, str): - item = iana_name(item, False) - for result in self._results: - if item in result.could_be_from_charset: - return result - raise KeyError - - def __len__(self) -> int: - return len(self._results) - - def __bool__(self) -> bool: - return len(self._results) > 0 - - def append(self, item: CharsetMatch) -> None: - """ - Insert a single match. Will be inserted accordingly to preserve sort. - Can be inserted as a submatch. - """ - if not isinstance(item, CharsetMatch): - raise ValueError( - "Cannot append instance '{}' to CharsetMatches".format( - str(item.__class__) - ) - ) - # We should disable the submatch factoring when the input file is too heavy (conserve RAM usage) - if len(item.raw) < TOO_BIG_SEQUENCE: - for match in self._results: - if match.fingerprint == item.fingerprint and match.chaos == item.chaos: - match.add_submatch(item) - return - self._results.append(item) - self._results = sorted(self._results) - - def best(self) -> CharsetMatch | None: - """ - Simply return the first match. Strict equivalent to matches[0]. - """ - if not self._results: - return None - return self._results[0] - - def first(self) -> CharsetMatch | None: - """ - Redundant method, call the method best(). Kept for BC reasons. - """ - return self.best() - - -CoherenceMatch = Tuple[str, float] -CoherenceMatches = List[CoherenceMatch] - - -class CliDetectionResult: - def __init__( - self, - path: str, - encoding: str | None, - encoding_aliases: list[str], - alternative_encodings: list[str], - language: str, - alphabets: list[str], - has_sig_or_bom: bool, - chaos: float, - coherence: float, - unicode_path: str | None, - is_preferred: bool, - ): - self.path: str = path - self.unicode_path: str | None = unicode_path - self.encoding: str | None = encoding - self.encoding_aliases: list[str] = encoding_aliases - self.alternative_encodings: list[str] = alternative_encodings - self.language: str = language - self.alphabets: list[str] = alphabets - self.has_sig_or_bom: bool = has_sig_or_bom - self.chaos: float = chaos - self.coherence: float = coherence - self.is_preferred: bool = is_preferred - - @property - def __dict__(self) -> dict[str, Any]: # type: ignore - return { - "path": self.path, - "encoding": self.encoding, - "encoding_aliases": self.encoding_aliases, - "alternative_encodings": self.alternative_encodings, - "language": self.language, - "alphabets": self.alphabets, - "has_sig_or_bom": self.has_sig_or_bom, - "chaos": self.chaos, - "coherence": self.coherence, - "unicode_path": self.unicode_path, - "is_preferred": self.is_preferred, - } - - def to_json(self) -> str: - return dumps(self.__dict__, ensure_ascii=True, indent=4) diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer/py.typed b/apps/bitwarden_event_logs/lib/charset_normalizer/py.typed deleted file mode 100755 index e69de29b..00000000 diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer/utils.py b/apps/bitwarden_event_logs/lib/charset_normalizer/utils.py deleted file mode 100755 index 6bf0384c..00000000 --- a/apps/bitwarden_event_logs/lib/charset_normalizer/utils.py +++ /dev/null @@ -1,414 +0,0 @@ -from __future__ import annotations - -import importlib -import logging -import unicodedata -from codecs import IncrementalDecoder -from encodings.aliases import aliases -from functools import lru_cache -from re import findall -from typing import Generator - -from _multibytecodec import ( # type: ignore[import-not-found,import] - MultibyteIncrementalDecoder, -) - -from .constant import ( - ENCODING_MARKS, - IANA_SUPPORTED_SIMILAR, - RE_POSSIBLE_ENCODING_INDICATION, - UNICODE_RANGES_COMBINED, - UNICODE_SECONDARY_RANGE_KEYWORD, - UTF8_MAXIMAL_ALLOCATION, - COMMON_CJK_CHARACTERS, -) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_accentuated(character: str) -> bool: - try: - description: str = unicodedata.name(character) - except ValueError: # Defensive: unicode database outdated? - return False - return ( - "WITH GRAVE" in description - or "WITH ACUTE" in description - or "WITH CEDILLA" in description - or "WITH DIAERESIS" in description - or "WITH CIRCUMFLEX" in description - or "WITH TILDE" in description - or "WITH MACRON" in description - or "WITH RING ABOVE" in description - ) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def remove_accent(character: str) -> str: - decomposed: str = unicodedata.decomposition(character) - if not decomposed: - return character - - codes: list[str] = decomposed.split(" ") - - return chr(int(codes[0], 16)) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def unicode_range(character: str) -> str | None: - """ - Retrieve the Unicode range official name from a single character. - """ - character_ord: int = ord(character) - - for range_name, ord_range in UNICODE_RANGES_COMBINED.items(): - if character_ord in ord_range: - return range_name - - return None - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_latin(character: str) -> bool: - try: - description: str = unicodedata.name(character) - except ValueError: # Defensive: unicode database outdated? - return False - return "LATIN" in description - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_punctuation(character: str) -> bool: - character_category: str = unicodedata.category(character) - - if "P" in character_category: - return True - - character_range: str | None = unicode_range(character) - - if character_range is None: - return False - - return "Punctuation" in character_range - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_symbol(character: str) -> bool: - character_category: str = unicodedata.category(character) - - if "S" in character_category or "N" in character_category: - return True - - character_range: str | None = unicode_range(character) - - if character_range is None: - return False - - return "Forms" in character_range and character_category != "Lo" - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_emoticon(character: str) -> bool: - character_range: str | None = unicode_range(character) - - if character_range is None: - return False - - return "Emoticons" in character_range or "Pictographs" in character_range - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_separator(character: str) -> bool: - if character.isspace() or character in {"|", "+", "<", ">"}: - return True - - character_category: str = unicodedata.category(character) - - return "Z" in character_category or character_category in {"Po", "Pd", "Pc"} - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_case_variable(character: str) -> bool: - return character.islower() != character.isupper() - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_cjk(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: # Defensive: unicode database outdated? - return False - - return "CJK" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_hiragana(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: # Defensive: unicode database outdated? - return False - - return "HIRAGANA" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_katakana(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: # Defensive: unicode database outdated? - return False - - return "KATAKANA" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_hangul(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: # Defensive: unicode database outdated? - return False - - return "HANGUL" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_thai(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: # Defensive: unicode database outdated? - return False - - return "THAI" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_arabic(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: # Defensive: unicode database outdated? - return False - - return "ARABIC" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_arabic_isolated_form(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: # Defensive: unicode database outdated? - return False - - return "ARABIC" in character_name and "ISOLATED FORM" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_cjk_uncommon(character: str) -> bool: - return character not in COMMON_CJK_CHARACTERS - - -@lru_cache(maxsize=len(UNICODE_RANGES_COMBINED)) -def is_unicode_range_secondary(range_name: str) -> bool: - return any(keyword in range_name for keyword in UNICODE_SECONDARY_RANGE_KEYWORD) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_unprintable(character: str) -> bool: - return ( - character.isspace() is False # includes \n \t \r \v - and character.isprintable() is False - and character != "\x1a" # Why? Its the ASCII substitute character. - and character != "\ufeff" # bug discovered in Python, - # Zero Width No-Break Space located in Arabic Presentation Forms-B, Unicode 1.1 not acknowledged as space. - ) - - -def any_specified_encoding(sequence: bytes, search_zone: int = 8192) -> str | None: - """ - Extract using ASCII-only decoder any specified encoding in the first n-bytes. - """ - if not isinstance(sequence, bytes): - raise TypeError - - seq_len: int = len(sequence) - - results: list[str] = findall( - RE_POSSIBLE_ENCODING_INDICATION, - sequence[: min(seq_len, search_zone)].decode("ascii", errors="ignore"), - ) - - if len(results) == 0: - return None - - for specified_encoding in results: - specified_encoding = specified_encoding.lower().replace("-", "_") - - encoding_alias: str - encoding_iana: str - - for encoding_alias, encoding_iana in aliases.items(): - if encoding_alias == specified_encoding: - return encoding_iana - if encoding_iana == specified_encoding: - return encoding_iana - - return None - - -@lru_cache(maxsize=128) -def is_multi_byte_encoding(name: str) -> bool: - """ - Verify is a specific encoding is a multi byte one based on it IANA name - """ - return name in { - "utf_8", - "utf_8_sig", - "utf_16", - "utf_16_be", - "utf_16_le", - "utf_32", - "utf_32_le", - "utf_32_be", - "utf_7", - } or issubclass( - importlib.import_module(f"encodings.{name}").IncrementalDecoder, - MultibyteIncrementalDecoder, - ) - - -def identify_sig_or_bom(sequence: bytes) -> tuple[str | None, bytes]: - """ - Identify and extract SIG/BOM in given sequence. - """ - - for iana_encoding in ENCODING_MARKS: - marks: bytes | list[bytes] = ENCODING_MARKS[iana_encoding] - - if isinstance(marks, bytes): - marks = [marks] - - for mark in marks: - if sequence.startswith(mark): - return iana_encoding, mark - - return None, b"" - - -def should_strip_sig_or_bom(iana_encoding: str) -> bool: - return iana_encoding not in {"utf_16", "utf_32"} - - -def iana_name(cp_name: str, strict: bool = True) -> str: - """Returns the Python normalized encoding name (Not the IANA official name).""" - cp_name = cp_name.lower().replace("-", "_") - - encoding_alias: str - encoding_iana: str - - for encoding_alias, encoding_iana in aliases.items(): - if cp_name in [encoding_alias, encoding_iana]: - return encoding_iana - - if strict: - raise ValueError(f"Unable to retrieve IANA for '{cp_name}'") - - return cp_name - - -def cp_similarity(iana_name_a: str, iana_name_b: str) -> float: - if is_multi_byte_encoding(iana_name_a) or is_multi_byte_encoding(iana_name_b): - return 0.0 - - decoder_a = importlib.import_module(f"encodings.{iana_name_a}").IncrementalDecoder - decoder_b = importlib.import_module(f"encodings.{iana_name_b}").IncrementalDecoder - - id_a: IncrementalDecoder = decoder_a(errors="ignore") - id_b: IncrementalDecoder = decoder_b(errors="ignore") - - character_match_count: int = 0 - - for i in range(255): - to_be_decoded: bytes = bytes([i]) - if id_a.decode(to_be_decoded) == id_b.decode(to_be_decoded): - character_match_count += 1 - - return character_match_count / 254 - - -def is_cp_similar(iana_name_a: str, iana_name_b: str) -> bool: - """ - Determine if two code page are at least 80% similar. IANA_SUPPORTED_SIMILAR dict was generated using - the function cp_similarity. - """ - return ( - iana_name_a in IANA_SUPPORTED_SIMILAR - and iana_name_b in IANA_SUPPORTED_SIMILAR[iana_name_a] - ) - - -def set_logging_handler( - name: str = "charset_normalizer", - level: int = logging.INFO, - format_string: str = "%(asctime)s | %(levelname)s | %(message)s", -) -> None: - logger = logging.getLogger(name) - logger.setLevel(level) - - handler = logging.StreamHandler() - handler.setFormatter(logging.Formatter(format_string)) - logger.addHandler(handler) - - -def cut_sequence_chunks( - sequences: bytes, - encoding_iana: str, - offsets: range, - chunk_size: int, - bom_or_sig_available: bool, - strip_sig_or_bom: bool, - sig_payload: bytes, - is_multi_byte_decoder: bool, - decoded_payload: str | None = None, -) -> Generator[str, None, None]: - if decoded_payload and is_multi_byte_decoder is False: - for i in offsets: - chunk = decoded_payload[i : i + chunk_size] - if not chunk: - break - yield chunk - else: - for i in offsets: - chunk_end = i + chunk_size - if chunk_end > len(sequences) + 8: - continue - - cut_sequence = sequences[i : i + chunk_size] - - if bom_or_sig_available and strip_sig_or_bom is False: - cut_sequence = sig_payload + cut_sequence - - chunk = cut_sequence.decode( - encoding_iana, - errors="ignore" if is_multi_byte_decoder else "strict", - ) - - # multi-byte bad cutting detector and adjustment - # not the cleanest way to perform that fix but clever enough for now. - if is_multi_byte_decoder and i > 0: - chunk_partial_size_chk: int = min(chunk_size, 16) - - if ( - decoded_payload - and chunk[:chunk_partial_size_chk] not in decoded_payload - ): - for j in range(i, i - 4, -1): - cut_sequence = sequences[j:chunk_end] - - if bom_or_sig_available and strip_sig_or_bom is False: - cut_sequence = sig_payload + cut_sequence - - chunk = cut_sequence.decode(encoding_iana, errors="ignore") - - if chunk[:chunk_partial_size_chk] in decoded_payload: - break - - yield chunk diff --git a/apps/bitwarden_event_logs/lib/charset_normalizer/version.py b/apps/bitwarden_event_logs/lib/charset_normalizer/version.py deleted file mode 100755 index c843e533..00000000 --- a/apps/bitwarden_event_logs/lib/charset_normalizer/version.py +++ /dev/null @@ -1,8 +0,0 @@ -""" -Expose version -""" - -from __future__ import annotations - -__version__ = "3.4.4" -VERSION = __version__.split(".") diff --git a/apps/bitwarden_event_logs/lib/dateutil/__init__.py b/apps/bitwarden_event_logs/lib/dateutil/__init__.py deleted file mode 100755 index a2c19c06..00000000 --- a/apps/bitwarden_event_logs/lib/dateutil/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -import sys - -try: - from ._version import version as __version__ -except ImportError: - __version__ = 'unknown' - -__all__ = ['easter', 'parser', 'relativedelta', 'rrule', 'tz', - 'utils', 'zoneinfo'] - -def __getattr__(name): - import importlib - - if name in __all__: - return importlib.import_module("." + name, __name__) - raise AttributeError( - "module {!r} has not attribute {!r}".format(__name__, name) - ) - - -def __dir__(): - # __dir__ should include all the lazy-importable modules as well. - return [x for x in globals() if x not in sys.modules] + __all__ diff --git a/apps/bitwarden_event_logs/lib/dateutil/__pycache__/__init__.cpython-39.pyc b/apps/bitwarden_event_logs/lib/dateutil/__pycache__/__init__.cpython-39.pyc deleted file mode 100755 index 8bc7d1e052f7dfbca11ff10c2c2f1d66fcdf9fb9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 931 zcmZuv!EO^V5FL9r*=^cXrJxqUCBP|{Y!4i(3IrS~p`bzQ^Z}a2~@-|QT3a$w!L+E!d z5fKh93EDpK$y^v)YUPuedav}6>L+MTgJ?!@YF4Q zYvxL6^K_w=DAl1J2h?l9zMzHr4AnkH!wCloCu9kZ%)#`QfKK{ZOjV&K)Gc8=7`Z#2m6)gP&%myyM_cL9&%vKVa z%7hzdMOE2sRJxWr-+ zwhabIN>j2yDCzt$o9K^2@TLStYc}uT2HB)%;NTRV!zpoOJFpcU{4;_WbBCz*9-0X( z;mszFfH-(Z=ETHH@`ZkghfR;zcVv&PPa2{u`gE}4$;X<3#@s>}-`SO=bz?oP_U@wp z3l$dk8RI2(G}x7TTu8fzo9iq@N+wc|Fl@rpzX2G?H8m>Es|MJ@hz-pGHR0C%)ZE4; zPubOLH4(aj#Va2txw>DEy&hvzAy8N!|8K2yx9M>&)UoSmQc98iHPWGJly?3Cx5VM( diff --git a/apps/bitwarden_event_logs/lib/dateutil/__pycache__/_common.cpython-39.pyc b/apps/bitwarden_event_logs/lib/dateutil/__pycache__/_common.cpython-39.pyc deleted file mode 100755 index 8102f5b5b77b4c2241212ac44f69f80932f73800..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1413 zcmZuxO;6iE5ZzrniQ^_IRZ&IhCH<~dDekE!R23m8m!?tyq2{u3ybG8(cGz`_h};wW zkXGfwU)pQ`MGsYH>;wp~(eBRfOg!(+JUeJMT>|6z@AK%~BIFMqt`8dyA7SbxSOn>j zPef3`&WK==o?1O7tS#c%lh^KAIvl1+J4}UakF^x-C}|JJaTbkY*&e21982ACLwL;G zfTU%D4KO6>QAolBB-v*@OWYThu+M1E78OxFBfW~K2?zG7s0$bNnz$z#usfnDTCmpz z12_w&b}Z#U1Sf_j-4IjE2HFhf8ce+aOHOmbNzPz})LZ0&b}yKs;87UoS;p2f-n+WF zAgT(EzngwK%6vVF$H~B#S?C9&k@ovhb{r@n6E45YB;#?qkC%zx>G*Nf_eGG&aTdis z4~u$rMkmJMJW8UB^Ey0NW5o%r(#gV|R z`U@(8#1vBZtb&!Pa~{Tl)|?l_JfpK=DZ0fjnoNssm-qAD+6xf zb0D_Bw*toG8RE++V6^ly*c5C!uvB{&y1hkqNwR2@jGerv1b^R(+jk%|=&04**aqyE zZ85FYEK^Z`oXK^iQUxtT0;ZB?2U6XnP~}`6asCh@bORPb8C?Pr4eHQI>#EEc6k?9q zKpV?ekU+I$P*?yCh23MP0D4+fV;=8X ztC*Ln#TPb*LJnAC*48oyQoDnCA~}BwE?vlHjxf6B2xG`4;a6}Fg{mHtAGlM!m;-46 zs>0JAdjtSbThJe`X4vO1hHdW{w!04drePaj4cl6UxihJ~)NfwC6`n%PPUU8(z6lq^ zrsPO*{sIA)6>{Nan3r1_;?b_>qCAse2t16-dHIP$3-w<#6;B%f;82ITwo`Q+n57=O@QJ8#F47S4R@45W46{-RcRbBF OD<#GcD6OI+cI7{P(-%|# diff --git a/apps/bitwarden_event_logs/lib/dateutil/__pycache__/_version.cpython-39.pyc b/apps/bitwarden_event_logs/lib/dateutil/__pycache__/_version.cpython-39.pyc deleted file mode 100755 index 5dddbd7ed4ee3710b03a69fb86cf3428bac618bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 265 zcmYe~<>g`k0_DX^GM54A#~=4rN9OB1B6{3vyE9 zoXjNsl*E$M(vr*^eV7CE3My}L*yQG?l;)(`F#}C6205355dirL BNQ3|Y diff --git a/apps/bitwarden_event_logs/lib/dateutil/__pycache__/relativedelta.cpython-39.pyc b/apps/bitwarden_event_logs/lib/dateutil/__pycache__/relativedelta.cpython-39.pyc deleted file mode 100755 index 6676c8bab7fd18e3d1a01468905d80f49575f250..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14792 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 diff --git a/apps/bitwarden_event_logs/lib/dateutil/_common.py b/apps/bitwarden_event_logs/lib/dateutil/_common.py deleted file mode 100755 index 4eb2659b..00000000 --- a/apps/bitwarden_event_logs/lib/dateutil/_common.py +++ /dev/null @@ -1,43 +0,0 @@ -""" -Common code used in multiple modules. -""" - - -class weekday(object): - __slots__ = ["weekday", "n"] - - def __init__(self, weekday, n=None): - self.weekday = weekday - self.n = n - - def __call__(self, n): - if n == self.n: - return self - else: - return self.__class__(self.weekday, n) - - def __eq__(self, other): - try: - if self.weekday != other.weekday or self.n != other.n: - return False - except AttributeError: - return False - return True - - def __hash__(self): - return hash(( - self.weekday, - self.n, - )) - - def __ne__(self, other): - return not (self == other) - - def __repr__(self): - s = ("MO", "TU", "WE", "TH", "FR", "SA", "SU")[self.weekday] - if not self.n: - return s - else: - return "%s(%+d)" % (s, self.n) - -# vim:ts=4:sw=4:et diff --git a/apps/bitwarden_event_logs/lib/dateutil/_version.py b/apps/bitwarden_event_logs/lib/dateutil/_version.py deleted file mode 100755 index ddda9809..00000000 --- a/apps/bitwarden_event_logs/lib/dateutil/_version.py +++ /dev/null @@ -1,4 +0,0 @@ -# file generated by setuptools_scm -# don't change, don't track in version control -__version__ = version = '2.9.0.post0' -__version_tuple__ = version_tuple = (2, 9, 0) diff --git a/apps/bitwarden_event_logs/lib/dateutil/easter.py b/apps/bitwarden_event_logs/lib/dateutil/easter.py deleted file mode 100755 index f74d1f74..00000000 --- a/apps/bitwarden_event_logs/lib/dateutil/easter.py +++ /dev/null @@ -1,89 +0,0 @@ -# -*- coding: utf-8 -*- -""" -This module offers a generic Easter computing method for any given year, using -Western, Orthodox or Julian algorithms. -""" - -import datetime - -__all__ = ["easter", "EASTER_JULIAN", "EASTER_ORTHODOX", "EASTER_WESTERN"] - -EASTER_JULIAN = 1 -EASTER_ORTHODOX = 2 -EASTER_WESTERN = 3 - - -def easter(year, method=EASTER_WESTERN): - """ - This method was ported from the work done by GM Arts, - on top of the algorithm by Claus Tondering, which was - based in part on the algorithm of Ouding (1940), as - quoted in "Explanatory Supplement to the Astronomical - Almanac", P. Kenneth Seidelmann, editor. - - This algorithm implements three different Easter - calculation methods: - - 1. Original calculation in Julian calendar, valid in - dates after 326 AD - 2. Original method, with date converted to Gregorian - calendar, valid in years 1583 to 4099 - 3. Revised method, in Gregorian calendar, valid in - years 1583 to 4099 as well - - These methods are represented by the constants: - - * ``EASTER_JULIAN = 1`` - * ``EASTER_ORTHODOX = 2`` - * ``EASTER_WESTERN = 3`` - - The default method is method 3. - - More about the algorithm may be found at: - - `GM Arts: Easter Algorithms `_ - - and - - `The Calendar FAQ: Easter `_ - - """ - - if not (1 <= method <= 3): - raise ValueError("invalid method") - - # g - Golden year - 1 - # c - Century - # h - (23 - Epact) mod 30 - # i - Number of days from March 21 to Paschal Full Moon - # j - Weekday for PFM (0=Sunday, etc) - # p - Number of days from March 21 to Sunday on or before PFM - # (-6 to 28 methods 1 & 3, to 56 for method 2) - # e - Extra days to add for method 2 (converting Julian - # date to Gregorian date) - - y = year - g = y % 19 - e = 0 - if method < 3: - # Old method - i = (19*g + 15) % 30 - j = (y + y//4 + i) % 7 - if method == 2: - # Extra dates to convert Julian to Gregorian date - e = 10 - if y > 1600: - e = e + y//100 - 16 - (y//100 - 16)//4 - else: - # New method - c = y//100 - h = (c - c//4 - (8*c + 13)//25 + 19*g + 15) % 30 - i = h - (h//28)*(1 - (h//28)*(29//(h + 1))*((21 - g)//11)) - j = (y + y//4 + i + 2 - c + c//4) % 7 - - # p can be from -6 to 56 corresponding to dates 22 March to 23 May - # (later dates apply to method 2, although 23 May never actually occurs) - p = i - j + e - d = 1 + (p + 27 + (p + 6)//40) % 31 - m = 3 + (p + 26)//30 - return datetime.date(int(y), int(m), int(d)) diff --git a/apps/bitwarden_event_logs/lib/dateutil/parser/__init__.py b/apps/bitwarden_event_logs/lib/dateutil/parser/__init__.py deleted file mode 100755 index d174b0e4..00000000 --- a/apps/bitwarden_event_logs/lib/dateutil/parser/__init__.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- -from ._parser import parse, parser, parserinfo, ParserError -from ._parser import DEFAULTPARSER, DEFAULTTZPARSER -from ._parser import UnknownTimezoneWarning - -from ._parser import __doc__ - -from .isoparser import isoparser, isoparse - -__all__ = ['parse', 'parser', 'parserinfo', - 'isoparse', 'isoparser', - 'ParserError', - 'UnknownTimezoneWarning'] - - -### -# Deprecate portions of the private interface so that downstream code that -# is improperly relying on it is given *some* notice. - - -def __deprecated_private_func(f): - from functools import wraps - import warnings - - msg = ('{name} is a private function and may break without warning, ' - 'it will be moved and or renamed in future versions.') - msg = msg.format(name=f.__name__) - - @wraps(f) - def deprecated_func(*args, **kwargs): - warnings.warn(msg, DeprecationWarning) - return f(*args, **kwargs) - - return deprecated_func - -def __deprecate_private_class(c): - import warnings - - msg = ('{name} is a private class and may break without warning, ' - 'it will be moved and or renamed in future versions.') - msg = msg.format(name=c.__name__) - - class private_class(c): - __doc__ = c.__doc__ - - def __init__(self, *args, **kwargs): - warnings.warn(msg, DeprecationWarning) - super(private_class, self).__init__(*args, **kwargs) - - private_class.__name__ = c.__name__ - - return private_class - - -from ._parser import _timelex, _resultbase -from ._parser import _tzparser, _parsetz - -_timelex = __deprecate_private_class(_timelex) -_tzparser = __deprecate_private_class(_tzparser) -_resultbase = __deprecate_private_class(_resultbase) -_parsetz = __deprecated_private_func(_parsetz) diff --git a/apps/bitwarden_event_logs/lib/dateutil/parser/__pycache__/__init__.cpython-39.pyc b/apps/bitwarden_event_logs/lib/dateutil/parser/__pycache__/__init__.cpython-39.pyc deleted file mode 100755 index 26b0b2fe7196555a85733e0171763780f3f4901e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2057 zcmb_dOK%)S5bo}oot=HJ9SBJfFenJI5Oxtaq$o;I3>Rcsu_L6pw8q=L%h>Z+x@T>& ztWUPI>jRwl0X%ZC#4qYA5>oyGC#rgOW1GhTi5X2jx@)Sds=w-vTCF;P=k71hf-LBQjT})C-kE<&PuQG6O{&{3z!8CPE5|CD=vn@47Nzneh;9LL31`Lqa*%bf28G01U}1p6{b^CKy&eXar} z(WwaH3|tjcC4(cWG&t1#3u;`P9AX&j2MdFlz6GDY17yj5BuiS*pBN=eGAA*a)93Vi zx=SwTHWb*!5X|OdIZ<*LVW{hUfVoJGweb$YWUMb7Lr2C*_1O5Uu=NG`V`q?0ih-Ua z)9i2{i{T)eO!QzsF2F{}jLRdL6+FquIG7Fk{Xr7%4+PX^TExkq+}i=?aTXVx_b1Ox zN33Fzf@AsKz*I0N!kTpUF<|3zepO~`*T0|S!zj@Y`d19lL9b$Jb|3OQ(W(WG)GZ*! z8|5mE3gdH*bp*UQ>L$)wlh;wYjpS7%SW9#JUl&Ik-3CHvllshMZRXKUSK&TfgCfEb zL29A+5CjW9a_$Ib;RldA04O5-J|`vg7S0F&DJo~ar~n|HbA~oc#^D1XdfTcAerCx( z;cu8k8u0kPgHK_u*N_LgMILhf9uP@F=&X?a06i5tkP2Hk3l~y>-aGR{cLC;M1rl&( zA6=;{U+<|6{c=;U4PPrMmhvU$-vwpqTw=a}BF)JuJ8wkhh#2)YkUo4RfB_q` zPyTDJ{g>f*>P=j+&Uu=PX#$8fP0qibMoEcZRa-A_=MS$aTZzJ^A6?zu|ZM K8(j(##rIDy?E`uM diff --git a/apps/bitwarden_event_logs/lib/dateutil/parser/__pycache__/_parser.cpython-39.pyc b/apps/bitwarden_event_logs/lib/dateutil/parser/__pycache__/_parser.cpython-39.pyc deleted file mode 100755 index 75c55d8733c1b3d54cfe3c447382b31e13c1b462..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40552 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 CWiXZi diff --git a/apps/bitwarden_event_logs/lib/dateutil/parser/__pycache__/isoparser.cpython-39.pyc b/apps/bitwarden_event_logs/lib/dateutil/parser/__pycache__/isoparser.cpython-39.pyc deleted file mode 100755 index 69aa4c43855dcb1928793b8887af0fa2ed142e29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11288 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 -- `W3C Date and Time Formats `_ -- `Time Formats (Planetary Rings Node) `_ -- `CPAN ParseDate module - `_ -- `Java SimpleDateFormat Class - `_ -""" -from __future__ import unicode_literals - -import datetime -import re -import string -import time -import warnings - -from calendar import monthrange -from io import StringIO - -import six -from six import integer_types, text_type - -from decimal import Decimal - -from warnings import warn - -from .. import relativedelta -from .. import tz - -__all__ = ["parse", "parserinfo", "ParserError"] - - -# TODO: pandas.core.tools.datetimes imports this explicitly. Might be worth -# making public and/or figuring out if there is something we can -# take off their plate. -class _timelex(object): - # Fractional seconds are sometimes split by a comma - _split_decimal = re.compile("([.,])") - - def __init__(self, instream): - if isinstance(instream, (bytes, bytearray)): - instream = instream.decode() - - if isinstance(instream, text_type): - instream = StringIO(instream) - elif getattr(instream, 'read', None) is None: - raise TypeError('Parser must be a string or character stream, not ' - '{itype}'.format(itype=instream.__class__.__name__)) - - self.instream = instream - self.charstack = [] - self.tokenstack = [] - self.eof = False - - def get_token(self): - """ - This function breaks the time string into lexical units (tokens), which - can be parsed by the parser. Lexical units are demarcated by changes in - the character set, so any continuous string of letters is considered - one unit, any continuous string of numbers is considered one unit. - - The main complication arises from the fact that dots ('.') can be used - both as separators (e.g. "Sep.20.2009") or decimal points (e.g. - "4:30:21.447"). As such, it is necessary to read the full context of - any dot-separated strings before breaking it into tokens; as such, this - function maintains a "token stack", for when the ambiguous context - demands that multiple tokens be parsed at once. - """ - if self.tokenstack: - return self.tokenstack.pop(0) - - seenletters = False - token = None - state = None - - while not self.eof: - # We only realize that we've reached the end of a token when we - # find a character that's not part of the current token - since - # that character may be part of the next token, it's stored in the - # charstack. - if self.charstack: - nextchar = self.charstack.pop(0) - else: - nextchar = self.instream.read(1) - while nextchar == '\x00': - nextchar = self.instream.read(1) - - if not nextchar: - self.eof = True - break - elif not state: - # First character of the token - determines if we're starting - # to parse a word, a number or something else. - token = nextchar - if self.isword(nextchar): - state = 'a' - elif self.isnum(nextchar): - state = '0' - elif self.isspace(nextchar): - token = ' ' - break # emit token - else: - break # emit token - elif state == 'a': - # If we've already started reading a word, we keep reading - # letters until we find something that's not part of a word. - seenletters = True - if self.isword(nextchar): - token += nextchar - elif nextchar == '.': - token += nextchar - state = 'a.' - else: - self.charstack.append(nextchar) - break # emit token - elif state == '0': - # If we've already started reading a number, we keep reading - # numbers until we find something that doesn't fit. - if self.isnum(nextchar): - token += nextchar - elif nextchar == '.' or (nextchar == ',' and len(token) >= 2): - token += nextchar - state = '0.' - else: - self.charstack.append(nextchar) - break # emit token - elif state == 'a.': - # If we've seen some letters and a dot separator, continue - # parsing, and the tokens will be broken up later. - seenletters = True - if nextchar == '.' or self.isword(nextchar): - token += nextchar - elif self.isnum(nextchar) and token[-1] == '.': - token += nextchar - state = '0.' - else: - self.charstack.append(nextchar) - break # emit token - elif state == '0.': - # If we've seen at least one dot separator, keep going, we'll - # break up the tokens later. - if nextchar == '.' or self.isnum(nextchar): - token += nextchar - elif self.isword(nextchar) and token[-1] == '.': - token += nextchar - state = 'a.' - else: - self.charstack.append(nextchar) - break # emit token - - if (state in ('a.', '0.') and (seenletters or token.count('.') > 1 or - token[-1] in '.,')): - l = self._split_decimal.split(token) - token = l[0] - for tok in l[1:]: - if tok: - self.tokenstack.append(tok) - - if state == '0.' and token.count('.') == 0: - token = token.replace(',', '.') - - return token - - def __iter__(self): - return self - - def __next__(self): - token = self.get_token() - if token is None: - raise StopIteration - - return token - - def next(self): - return self.__next__() # Python 2.x support - - @classmethod - def split(cls, s): - return list(cls(s)) - - @classmethod - def isword(cls, nextchar): - """ Whether or not the next character is part of a word """ - return nextchar.isalpha() - - @classmethod - def isnum(cls, nextchar): - """ Whether the next character is part of a number """ - return nextchar.isdigit() - - @classmethod - def isspace(cls, nextchar): - """ Whether the next character is whitespace """ - return nextchar.isspace() - - -class _resultbase(object): - - def __init__(self): - for attr in self.__slots__: - setattr(self, attr, None) - - def _repr(self, classname): - l = [] - for attr in self.__slots__: - value = getattr(self, attr) - if value is not None: - l.append("%s=%s" % (attr, repr(value))) - return "%s(%s)" % (classname, ", ".join(l)) - - def __len__(self): - return (sum(getattr(self, attr) is not None - for attr in self.__slots__)) - - def __repr__(self): - return self._repr(self.__class__.__name__) - - -class parserinfo(object): - """ - Class which handles what inputs are accepted. Subclass this to customize - the language and acceptable values for each parameter. - - :param dayfirst: - Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the day (``True``) or month (``False``). If - ``yearfirst`` is set to ``True``, this distinguishes between YDM - and YMD. Default is ``False``. - - :param yearfirst: - Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the year. If ``True``, the first number is taken - to be the year, otherwise the last number is taken to be the year. - Default is ``False``. - """ - - # m from a.m/p.m, t from ISO T separator - JUMP = [" ", ".", ",", ";", "-", "/", "'", - "at", "on", "and", "ad", "m", "t", "of", - "st", "nd", "rd", "th"] - - WEEKDAYS = [("Mon", "Monday"), - ("Tue", "Tuesday"), # TODO: "Tues" - ("Wed", "Wednesday"), - ("Thu", "Thursday"), # TODO: "Thurs" - ("Fri", "Friday"), - ("Sat", "Saturday"), - ("Sun", "Sunday")] - MONTHS = [("Jan", "January"), - ("Feb", "February"), # TODO: "Febr" - ("Mar", "March"), - ("Apr", "April"), - ("May", "May"), - ("Jun", "June"), - ("Jul", "July"), - ("Aug", "August"), - ("Sep", "Sept", "September"), - ("Oct", "October"), - ("Nov", "November"), - ("Dec", "December")] - HMS = [("h", "hour", "hours"), - ("m", "minute", "minutes"), - ("s", "second", "seconds")] - AMPM = [("am", "a"), - ("pm", "p")] - UTCZONE = ["UTC", "GMT", "Z", "z"] - PERTAIN = ["of"] - TZOFFSET = {} - # TODO: ERA = ["AD", "BC", "CE", "BCE", "Stardate", - # "Anno Domini", "Year of Our Lord"] - - def __init__(self, dayfirst=False, yearfirst=False): - self._jump = self._convert(self.JUMP) - self._weekdays = self._convert(self.WEEKDAYS) - self._months = self._convert(self.MONTHS) - self._hms = self._convert(self.HMS) - self._ampm = self._convert(self.AMPM) - self._utczone = self._convert(self.UTCZONE) - self._pertain = self._convert(self.PERTAIN) - - self.dayfirst = dayfirst - self.yearfirst = yearfirst - - self._year = time.localtime().tm_year - self._century = self._year // 100 * 100 - - def _convert(self, lst): - dct = {} - for i, v in enumerate(lst): - if isinstance(v, tuple): - for v in v: - dct[v.lower()] = i - else: - dct[v.lower()] = i - return dct - - def jump(self, name): - return name.lower() in self._jump - - def weekday(self, name): - try: - return self._weekdays[name.lower()] - except KeyError: - pass - return None - - def month(self, name): - try: - return self._months[name.lower()] + 1 - except KeyError: - pass - return None - - def hms(self, name): - try: - return self._hms[name.lower()] - except KeyError: - return None - - def ampm(self, name): - try: - return self._ampm[name.lower()] - except KeyError: - return None - - def pertain(self, name): - return name.lower() in self._pertain - - def utczone(self, name): - return name.lower() in self._utczone - - def tzoffset(self, name): - if name in self._utczone: - return 0 - - return self.TZOFFSET.get(name) - - def convertyear(self, year, century_specified=False): - """ - Converts two-digit years to year within [-50, 49] - range of self._year (current local time) - """ - - # Function contract is that the year is always positive - assert year >= 0 - - if year < 100 and not century_specified: - # assume current century to start - year += self._century - - if year >= self._year + 50: # if too far in future - year -= 100 - elif year < self._year - 50: # if too far in past - year += 100 - - return year - - def validate(self, res): - # move to info - if res.year is not None: - res.year = self.convertyear(res.year, res.century_specified) - - if ((res.tzoffset == 0 and not res.tzname) or - (res.tzname == 'Z' or res.tzname == 'z')): - res.tzname = "UTC" - res.tzoffset = 0 - elif res.tzoffset != 0 and res.tzname and self.utczone(res.tzname): - res.tzoffset = 0 - return True - - -class _ymd(list): - def __init__(self, *args, **kwargs): - super(self.__class__, self).__init__(*args, **kwargs) - self.century_specified = False - self.dstridx = None - self.mstridx = None - self.ystridx = None - - @property - def has_year(self): - return self.ystridx is not None - - @property - def has_month(self): - return self.mstridx is not None - - @property - def has_day(self): - return self.dstridx is not None - - def could_be_day(self, value): - if self.has_day: - return False - elif not self.has_month: - return 1 <= value <= 31 - elif not self.has_year: - # Be permissive, assume leap year - month = self[self.mstridx] - return 1 <= value <= monthrange(2000, month)[1] - else: - month = self[self.mstridx] - year = self[self.ystridx] - return 1 <= value <= monthrange(year, month)[1] - - def append(self, val, label=None): - if hasattr(val, '__len__'): - if val.isdigit() and len(val) > 2: - self.century_specified = True - if label not in [None, 'Y']: # pragma: no cover - raise ValueError(label) - label = 'Y' - elif val > 100: - self.century_specified = True - if label not in [None, 'Y']: # pragma: no cover - raise ValueError(label) - label = 'Y' - - super(self.__class__, self).append(int(val)) - - if label == 'M': - if self.has_month: - raise ValueError('Month is already set') - self.mstridx = len(self) - 1 - elif label == 'D': - if self.has_day: - raise ValueError('Day is already set') - self.dstridx = len(self) - 1 - elif label == 'Y': - if self.has_year: - raise ValueError('Year is already set') - self.ystridx = len(self) - 1 - - def _resolve_from_stridxs(self, strids): - """ - Try to resolve the identities of year/month/day elements using - ystridx, mstridx, and dstridx, if enough of these are specified. - """ - if len(self) == 3 and len(strids) == 2: - # we can back out the remaining stridx value - missing = [x for x in range(3) if x not in strids.values()] - key = [x for x in ['y', 'm', 'd'] if x not in strids] - assert len(missing) == len(key) == 1 - key = key[0] - val = missing[0] - strids[key] = val - - assert len(self) == len(strids) # otherwise this should not be called - out = {key: self[strids[key]] for key in strids} - return (out.get('y'), out.get('m'), out.get('d')) - - def resolve_ymd(self, yearfirst, dayfirst): - len_ymd = len(self) - year, month, day = (None, None, None) - - strids = (('y', self.ystridx), - ('m', self.mstridx), - ('d', self.dstridx)) - - strids = {key: val for key, val in strids if val is not None} - if (len(self) == len(strids) > 0 or - (len(self) == 3 and len(strids) == 2)): - return self._resolve_from_stridxs(strids) - - mstridx = self.mstridx - - if len_ymd > 3: - raise ValueError("More than three YMD values") - elif len_ymd == 1 or (mstridx is not None and len_ymd == 2): - # One member, or two members with a month string - if mstridx is not None: - month = self[mstridx] - # since mstridx is 0 or 1, self[mstridx-1] always - # looks up the other element - other = self[mstridx - 1] - else: - other = self[0] - - if len_ymd > 1 or mstridx is None: - if other > 31: - year = other - else: - day = other - - elif len_ymd == 2: - # Two members with numbers - if self[0] > 31: - # 99-01 - year, month = self - elif self[1] > 31: - # 01-99 - month, year = self - elif dayfirst and self[1] <= 12: - # 13-01 - day, month = self - else: - # 01-13 - month, day = self - - elif len_ymd == 3: - # Three members - if mstridx == 0: - if self[1] > 31: - # Apr-2003-25 - month, year, day = self - else: - month, day, year = self - elif mstridx == 1: - if self[0] > 31 or (yearfirst and self[2] <= 31): - # 99-Jan-01 - year, month, day = self - else: - # 01-Jan-01 - # Give precedence to day-first, since - # two-digit years is usually hand-written. - day, month, year = self - - elif mstridx == 2: - # WTF!? - if self[1] > 31: - # 01-99-Jan - day, year, month = self - else: - # 99-01-Jan - year, day, month = self - - else: - if (self[0] > 31 or - self.ystridx == 0 or - (yearfirst and self[1] <= 12 and self[2] <= 31)): - # 99-01-01 - if dayfirst and self[2] <= 12: - year, day, month = self - else: - year, month, day = self - elif self[0] > 12 or (dayfirst and self[1] <= 12): - # 13-01-01 - day, month, year = self - else: - # 01-13-01 - month, day, year = self - - return year, month, day - - -class parser(object): - def __init__(self, info=None): - self.info = info or parserinfo() - - def parse(self, timestr, default=None, - ignoretz=False, tzinfos=None, **kwargs): - """ - Parse the date/time string into a :class:`datetime.datetime` object. - - :param timestr: - Any date/time string using the supported formats. - - :param default: - The default datetime object, if this is a datetime object and not - ``None``, elements specified in ``timestr`` replace elements in the - default object. - - :param ignoretz: - If set ``True``, time zones in parsed strings are ignored and a - naive :class:`datetime.datetime` object is returned. - - :param tzinfos: - Additional time zone names / aliases which may be present in the - string. This argument maps time zone names (and optionally offsets - from those time zones) to time zones. This parameter can be a - dictionary with timezone aliases mapping time zone names to time - zones or a function taking two parameters (``tzname`` and - ``tzoffset``) and returning a time zone. - - The timezones to which the names are mapped can be an integer - offset from UTC in seconds or a :class:`tzinfo` object. - - .. doctest:: - :options: +NORMALIZE_WHITESPACE - - >>> from dateutil.parser import parse - >>> from dateutil.tz import gettz - >>> tzinfos = {"BRST": -7200, "CST": gettz("America/Chicago")} - >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos) - datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -7200)) - >>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos) - datetime.datetime(2012, 1, 19, 17, 21, - tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago')) - - This parameter is ignored if ``ignoretz`` is set. - - :param \\*\\*kwargs: - Keyword arguments as passed to ``_parse()``. - - :return: - Returns a :class:`datetime.datetime` object or, if the - ``fuzzy_with_tokens`` option is ``True``, returns a tuple, the - first element being a :class:`datetime.datetime` object, the second - a tuple containing the fuzzy tokens. - - :raises ParserError: - Raised for invalid or unknown string format, if the provided - :class:`tzinfo` is not in a valid format, or if an invalid date - would be created. - - :raises TypeError: - Raised for non-string or character stream input. - - :raises OverflowError: - Raised if the parsed date exceeds the largest valid C integer on - your system. - """ - - if default is None: - default = datetime.datetime.now().replace(hour=0, minute=0, - second=0, microsecond=0) - - res, skipped_tokens = self._parse(timestr, **kwargs) - - if res is None: - raise ParserError("Unknown string format: %s", timestr) - - if len(res) == 0: - raise ParserError("String does not contain a date: %s", timestr) - - try: - ret = self._build_naive(res, default) - except ValueError as e: - six.raise_from(ParserError(str(e) + ": %s", timestr), e) - - if not ignoretz: - ret = self._build_tzaware(ret, res, tzinfos) - - if kwargs.get('fuzzy_with_tokens', False): - return ret, skipped_tokens - else: - return ret - - class _result(_resultbase): - __slots__ = ["year", "month", "day", "weekday", - "hour", "minute", "second", "microsecond", - "tzname", "tzoffset", "ampm","any_unused_tokens"] - - def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False, - fuzzy_with_tokens=False): - """ - Private method which performs the heavy lifting of parsing, called from - ``parse()``, which passes on its ``kwargs`` to this function. - - :param timestr: - The string to parse. - - :param dayfirst: - Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the day (``True``) or month (``False``). If - ``yearfirst`` is set to ``True``, this distinguishes between YDM - and YMD. If set to ``None``, this value is retrieved from the - current :class:`parserinfo` object (which itself defaults to - ``False``). - - :param yearfirst: - Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the year. If ``True``, the first number is taken - to be the year, otherwise the last number is taken to be the year. - If this is set to ``None``, the value is retrieved from the current - :class:`parserinfo` object (which itself defaults to ``False``). - - :param fuzzy: - Whether to allow fuzzy parsing, allowing for string like "Today is - January 1, 2047 at 8:21:00AM". - - :param fuzzy_with_tokens: - If ``True``, ``fuzzy`` is automatically set to True, and the parser - will return a tuple where the first element is the parsed - :class:`datetime.datetime` datetimestamp and the second element is - a tuple containing the portions of the string which were ignored: - - .. doctest:: - - >>> from dateutil.parser import parse - >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True) - (datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at ')) - - """ - if fuzzy_with_tokens: - fuzzy = True - - info = self.info - - if dayfirst is None: - dayfirst = info.dayfirst - - if yearfirst is None: - yearfirst = info.yearfirst - - res = self._result() - l = _timelex.split(timestr) # Splits the timestr into tokens - - skipped_idxs = [] - - # year/month/day list - ymd = _ymd() - - len_l = len(l) - i = 0 - try: - while i < len_l: - - # Check if it's a number - value_repr = l[i] - try: - value = float(value_repr) - except ValueError: - value = None - - if value is not None: - # Numeric token - i = self._parse_numeric_token(l, i, info, ymd, res, fuzzy) - - # Check weekday - elif info.weekday(l[i]) is not None: - value = info.weekday(l[i]) - res.weekday = value - - # Check month name - elif info.month(l[i]) is not None: - value = info.month(l[i]) - ymd.append(value, 'M') - - if i + 1 < len_l: - if l[i + 1] in ('-', '/'): - # Jan-01[-99] - sep = l[i + 1] - ymd.append(l[i + 2]) - - if i + 3 < len_l and l[i + 3] == sep: - # Jan-01-99 - ymd.append(l[i + 4]) - i += 2 - - i += 2 - - elif (i + 4 < len_l and l[i + 1] == l[i + 3] == ' ' and - info.pertain(l[i + 2])): - # Jan of 01 - # In this case, 01 is clearly year - if l[i + 4].isdigit(): - # Convert it here to become unambiguous - value = int(l[i + 4]) - year = str(info.convertyear(value)) - ymd.append(year, 'Y') - else: - # Wrong guess - pass - # TODO: not hit in tests - i += 4 - - # Check am/pm - elif info.ampm(l[i]) is not None: - value = info.ampm(l[i]) - val_is_ampm = self._ampm_valid(res.hour, res.ampm, fuzzy) - - if val_is_ampm: - res.hour = self._adjust_ampm(res.hour, value) - res.ampm = value - - elif fuzzy: - skipped_idxs.append(i) - - # Check for a timezone name - elif self._could_be_tzname(res.hour, res.tzname, res.tzoffset, l[i]): - res.tzname = l[i] - res.tzoffset = info.tzoffset(res.tzname) - - # Check for something like GMT+3, or BRST+3. Notice - # that it doesn't mean "I am 3 hours after GMT", but - # "my time +3 is GMT". If found, we reverse the - # logic so that timezone parsing code will get it - # right. - if i + 1 < len_l and l[i + 1] in ('+', '-'): - l[i + 1] = ('+', '-')[l[i + 1] == '+'] - res.tzoffset = None - if info.utczone(res.tzname): - # With something like GMT+3, the timezone - # is *not* GMT. - res.tzname = None - - # Check for a numbered timezone - elif res.hour is not None and l[i] in ('+', '-'): - signal = (-1, 1)[l[i] == '+'] - len_li = len(l[i + 1]) - - # TODO: check that l[i + 1] is integer? - if len_li == 4: - # -0300 - hour_offset = int(l[i + 1][:2]) - min_offset = int(l[i + 1][2:]) - elif i + 2 < len_l and l[i + 2] == ':': - # -03:00 - hour_offset = int(l[i + 1]) - min_offset = int(l[i + 3]) # TODO: Check that l[i+3] is minute-like? - i += 2 - elif len_li <= 2: - # -[0]3 - hour_offset = int(l[i + 1][:2]) - min_offset = 0 - else: - raise ValueError(timestr) - - res.tzoffset = signal * (hour_offset * 3600 + min_offset * 60) - - # Look for a timezone name between parenthesis - if (i + 5 < len_l and - info.jump(l[i + 2]) and l[i + 3] == '(' and - l[i + 5] == ')' and - 3 <= len(l[i + 4]) and - self._could_be_tzname(res.hour, res.tzname, - None, l[i + 4])): - # -0300 (BRST) - res.tzname = l[i + 4] - i += 4 - - i += 1 - - # Check jumps - elif not (info.jump(l[i]) or fuzzy): - raise ValueError(timestr) - - else: - skipped_idxs.append(i) - i += 1 - - # Process year/month/day - year, month, day = ymd.resolve_ymd(yearfirst, dayfirst) - - res.century_specified = ymd.century_specified - res.year = year - res.month = month - res.day = day - - except (IndexError, ValueError): - return None, None - - if not info.validate(res): - return None, None - - if fuzzy_with_tokens: - skipped_tokens = self._recombine_skipped(l, skipped_idxs) - return res, tuple(skipped_tokens) - else: - return res, None - - def _parse_numeric_token(self, tokens, idx, info, ymd, res, fuzzy): - # Token is a number - value_repr = tokens[idx] - try: - value = self._to_decimal(value_repr) - except Exception as e: - six.raise_from(ValueError('Unknown numeric token'), e) - - len_li = len(value_repr) - - len_l = len(tokens) - - if (len(ymd) == 3 and len_li in (2, 4) and - res.hour is None and - (idx + 1 >= len_l or - (tokens[idx + 1] != ':' and - info.hms(tokens[idx + 1]) is None))): - # 19990101T23[59] - s = tokens[idx] - res.hour = int(s[:2]) - - if len_li == 4: - res.minute = int(s[2:]) - - elif len_li == 6 or (len_li > 6 and tokens[idx].find('.') == 6): - # YYMMDD or HHMMSS[.ss] - s = tokens[idx] - - if not ymd and '.' not in tokens[idx]: - ymd.append(s[:2]) - ymd.append(s[2:4]) - ymd.append(s[4:]) - else: - # 19990101T235959[.59] - - # TODO: Check if res attributes already set. - res.hour = int(s[:2]) - res.minute = int(s[2:4]) - res.second, res.microsecond = self._parsems(s[4:]) - - elif len_li in (8, 12, 14): - # YYYYMMDD - s = tokens[idx] - ymd.append(s[:4], 'Y') - ymd.append(s[4:6]) - ymd.append(s[6:8]) - - if len_li > 8: - res.hour = int(s[8:10]) - res.minute = int(s[10:12]) - - if len_li > 12: - res.second = int(s[12:]) - - elif self._find_hms_idx(idx, tokens, info, allow_jump=True) is not None: - # HH[ ]h or MM[ ]m or SS[.ss][ ]s - hms_idx = self._find_hms_idx(idx, tokens, info, allow_jump=True) - (idx, hms) = self._parse_hms(idx, tokens, info, hms_idx) - if hms is not None: - # TODO: checking that hour/minute/second are not - # already set? - self._assign_hms(res, value_repr, hms) - - elif idx + 2 < len_l and tokens[idx + 1] == ':': - # HH:MM[:SS[.ss]] - res.hour = int(value) - value = self._to_decimal(tokens[idx + 2]) # TODO: try/except for this? - (res.minute, res.second) = self._parse_min_sec(value) - - if idx + 4 < len_l and tokens[idx + 3] == ':': - res.second, res.microsecond = self._parsems(tokens[idx + 4]) - - idx += 2 - - idx += 2 - - elif idx + 1 < len_l and tokens[idx + 1] in ('-', '/', '.'): - sep = tokens[idx + 1] - ymd.append(value_repr) - - if idx + 2 < len_l and not info.jump(tokens[idx + 2]): - if tokens[idx + 2].isdigit(): - # 01-01[-01] - ymd.append(tokens[idx + 2]) - else: - # 01-Jan[-01] - value = info.month(tokens[idx + 2]) - - if value is not None: - ymd.append(value, 'M') - else: - raise ValueError() - - if idx + 3 < len_l and tokens[idx + 3] == sep: - # We have three members - value = info.month(tokens[idx + 4]) - - if value is not None: - ymd.append(value, 'M') - else: - ymd.append(tokens[idx + 4]) - idx += 2 - - idx += 1 - idx += 1 - - elif idx + 1 >= len_l or info.jump(tokens[idx + 1]): - if idx + 2 < len_l and info.ampm(tokens[idx + 2]) is not None: - # 12 am - hour = int(value) - res.hour = self._adjust_ampm(hour, info.ampm(tokens[idx + 2])) - idx += 1 - else: - # Year, month or day - ymd.append(value) - idx += 1 - - elif info.ampm(tokens[idx + 1]) is not None and (0 <= value < 24): - # 12am - hour = int(value) - res.hour = self._adjust_ampm(hour, info.ampm(tokens[idx + 1])) - idx += 1 - - elif ymd.could_be_day(value): - ymd.append(value) - - elif not fuzzy: - raise ValueError() - - return idx - - def _find_hms_idx(self, idx, tokens, info, allow_jump): - len_l = len(tokens) - - if idx+1 < len_l and info.hms(tokens[idx+1]) is not None: - # There is an "h", "m", or "s" label following this token. We take - # assign the upcoming label to the current token. - # e.g. the "12" in 12h" - hms_idx = idx + 1 - - elif (allow_jump and idx+2 < len_l and tokens[idx+1] == ' ' and - info.hms(tokens[idx+2]) is not None): - # There is a space and then an "h", "m", or "s" label. - # e.g. the "12" in "12 h" - hms_idx = idx + 2 - - elif idx > 0 and info.hms(tokens[idx-1]) is not None: - # There is a "h", "m", or "s" preceding this token. Since neither - # of the previous cases was hit, there is no label following this - # token, so we use the previous label. - # e.g. the "04" in "12h04" - hms_idx = idx-1 - - elif (1 < idx == len_l-1 and tokens[idx-1] == ' ' and - info.hms(tokens[idx-2]) is not None): - # If we are looking at the final token, we allow for a - # backward-looking check to skip over a space. - # TODO: Are we sure this is the right condition here? - hms_idx = idx - 2 - - else: - hms_idx = None - - return hms_idx - - def _assign_hms(self, res, value_repr, hms): - # See GH issue #427, fixing float rounding - value = self._to_decimal(value_repr) - - if hms == 0: - # Hour - res.hour = int(value) - if value % 1: - res.minute = int(60*(value % 1)) - - elif hms == 1: - (res.minute, res.second) = self._parse_min_sec(value) - - elif hms == 2: - (res.second, res.microsecond) = self._parsems(value_repr) - - def _could_be_tzname(self, hour, tzname, tzoffset, token): - return (hour is not None and - tzname is None and - tzoffset is None and - len(token) <= 5 and - (all(x in string.ascii_uppercase for x in token) - or token in self.info.UTCZONE)) - - def _ampm_valid(self, hour, ampm, fuzzy): - """ - For fuzzy parsing, 'a' or 'am' (both valid English words) - may erroneously trigger the AM/PM flag. Deal with that - here. - """ - val_is_ampm = True - - # If there's already an AM/PM flag, this one isn't one. - if fuzzy and ampm is not None: - val_is_ampm = False - - # If AM/PM is found and hour is not, raise a ValueError - if hour is None: - if fuzzy: - val_is_ampm = False - else: - raise ValueError('No hour specified with AM or PM flag.') - elif not 0 <= hour <= 12: - # If AM/PM is found, it's a 12 hour clock, so raise - # an error for invalid range - if fuzzy: - val_is_ampm = False - else: - raise ValueError('Invalid hour specified for 12-hour clock.') - - return val_is_ampm - - def _adjust_ampm(self, hour, ampm): - if hour < 12 and ampm == 1: - hour += 12 - elif hour == 12 and ampm == 0: - hour = 0 - return hour - - def _parse_min_sec(self, value): - # TODO: Every usage of this function sets res.second to the return - # value. Are there any cases where second will be returned as None and - # we *don't* want to set res.second = None? - minute = int(value) - second = None - - sec_remainder = value % 1 - if sec_remainder: - second = int(60 * sec_remainder) - return (minute, second) - - def _parse_hms(self, idx, tokens, info, hms_idx): - # TODO: Is this going to admit a lot of false-positives for when we - # just happen to have digits and "h", "m" or "s" characters in non-date - # text? I guess hex hashes won't have that problem, but there's plenty - # of random junk out there. - if hms_idx is None: - hms = None - new_idx = idx - elif hms_idx > idx: - hms = info.hms(tokens[hms_idx]) - new_idx = hms_idx - else: - # Looking backwards, increment one. - hms = info.hms(tokens[hms_idx]) + 1 - new_idx = idx - - return (new_idx, hms) - - # ------------------------------------------------------------------ - # Handling for individual tokens. These are kept as methods instead - # of functions for the sake of customizability via subclassing. - - def _parsems(self, value): - """Parse a I[.F] seconds value into (seconds, microseconds).""" - if "." not in value: - return int(value), 0 - else: - i, f = value.split(".") - return int(i), int(f.ljust(6, "0")[:6]) - - def _to_decimal(self, val): - try: - decimal_value = Decimal(val) - # See GH 662, edge case, infinite value should not be converted - # via `_to_decimal` - if not decimal_value.is_finite(): - raise ValueError("Converted decimal value is infinite or NaN") - except Exception as e: - msg = "Could not convert %s to decimal" % val - six.raise_from(ValueError(msg), e) - else: - return decimal_value - - # ------------------------------------------------------------------ - # Post-Parsing construction of datetime output. These are kept as - # methods instead of functions for the sake of customizability via - # subclassing. - - def _build_tzinfo(self, tzinfos, tzname, tzoffset): - if callable(tzinfos): - tzdata = tzinfos(tzname, tzoffset) - else: - tzdata = tzinfos.get(tzname) - # handle case where tzinfo is paased an options that returns None - # eg tzinfos = {'BRST' : None} - if isinstance(tzdata, datetime.tzinfo) or tzdata is None: - tzinfo = tzdata - elif isinstance(tzdata, text_type): - tzinfo = tz.tzstr(tzdata) - elif isinstance(tzdata, integer_types): - tzinfo = tz.tzoffset(tzname, tzdata) - else: - raise TypeError("Offset must be tzinfo subclass, tz string, " - "or int offset.") - return tzinfo - - def _build_tzaware(self, naive, res, tzinfos): - if (callable(tzinfos) or (tzinfos and res.tzname in tzinfos)): - tzinfo = self._build_tzinfo(tzinfos, res.tzname, res.tzoffset) - aware = naive.replace(tzinfo=tzinfo) - aware = self._assign_tzname(aware, res.tzname) - - elif res.tzname and res.tzname in time.tzname: - aware = naive.replace(tzinfo=tz.tzlocal()) - - # Handle ambiguous local datetime - aware = self._assign_tzname(aware, res.tzname) - - # This is mostly relevant for winter GMT zones parsed in the UK - if (aware.tzname() != res.tzname and - res.tzname in self.info.UTCZONE): - aware = aware.replace(tzinfo=tz.UTC) - - elif res.tzoffset == 0: - aware = naive.replace(tzinfo=tz.UTC) - - elif res.tzoffset: - aware = naive.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset)) - - elif not res.tzname and not res.tzoffset: - # i.e. no timezone information was found. - aware = naive - - elif res.tzname: - # tz-like string was parsed but we don't know what to do - # with it - warnings.warn("tzname {tzname} identified but not understood. " - "Pass `tzinfos` argument in order to correctly " - "return a timezone-aware datetime. In a future " - "version, this will raise an " - "exception.".format(tzname=res.tzname), - category=UnknownTimezoneWarning) - aware = naive - - return aware - - def _build_naive(self, res, default): - repl = {} - for attr in ("year", "month", "day", "hour", - "minute", "second", "microsecond"): - value = getattr(res, attr) - if value is not None: - repl[attr] = value - - if 'day' not in repl: - # If the default day exceeds the last day of the month, fall back - # to the end of the month. - cyear = default.year if res.year is None else res.year - cmonth = default.month if res.month is None else res.month - cday = default.day if res.day is None else res.day - - if cday > monthrange(cyear, cmonth)[1]: - repl['day'] = monthrange(cyear, cmonth)[1] - - naive = default.replace(**repl) - - if res.weekday is not None and not res.day: - naive = naive + relativedelta.relativedelta(weekday=res.weekday) - - return naive - - def _assign_tzname(self, dt, tzname): - if dt.tzname() != tzname: - new_dt = tz.enfold(dt, fold=1) - if new_dt.tzname() == tzname: - return new_dt - - return dt - - def _recombine_skipped(self, tokens, skipped_idxs): - """ - >>> tokens = ["foo", " ", "bar", " ", "19June2000", "baz"] - >>> skipped_idxs = [0, 1, 2, 5] - >>> _recombine_skipped(tokens, skipped_idxs) - ["foo bar", "baz"] - """ - skipped_tokens = [] - for i, idx in enumerate(sorted(skipped_idxs)): - if i > 0 and idx - 1 == skipped_idxs[i - 1]: - skipped_tokens[-1] = skipped_tokens[-1] + tokens[idx] - else: - skipped_tokens.append(tokens[idx]) - - return skipped_tokens - - -DEFAULTPARSER = parser() - - -def parse(timestr, parserinfo=None, **kwargs): - """ - - Parse a string in one of the supported formats, using the - ``parserinfo`` parameters. - - :param timestr: - A string containing a date/time stamp. - - :param parserinfo: - A :class:`parserinfo` object containing parameters for the parser. - If ``None``, the default arguments to the :class:`parserinfo` - constructor are used. - - The ``**kwargs`` parameter takes the following keyword arguments: - - :param default: - The default datetime object, if this is a datetime object and not - ``None``, elements specified in ``timestr`` replace elements in the - default object. - - :param ignoretz: - If set ``True``, time zones in parsed strings are ignored and a naive - :class:`datetime` object is returned. - - :param tzinfos: - Additional time zone names / aliases which may be present in the - string. This argument maps time zone names (and optionally offsets - from those time zones) to time zones. This parameter can be a - dictionary with timezone aliases mapping time zone names to time - zones or a function taking two parameters (``tzname`` and - ``tzoffset``) and returning a time zone. - - The timezones to which the names are mapped can be an integer - offset from UTC in seconds or a :class:`tzinfo` object. - - .. doctest:: - :options: +NORMALIZE_WHITESPACE - - >>> from dateutil.parser import parse - >>> from dateutil.tz import gettz - >>> tzinfos = {"BRST": -7200, "CST": gettz("America/Chicago")} - >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos) - datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -7200)) - >>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos) - datetime.datetime(2012, 1, 19, 17, 21, - tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago')) - - This parameter is ignored if ``ignoretz`` is set. - - :param dayfirst: - Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the day (``True``) or month (``False``). If - ``yearfirst`` is set to ``True``, this distinguishes between YDM and - YMD. If set to ``None``, this value is retrieved from the current - :class:`parserinfo` object (which itself defaults to ``False``). - - :param yearfirst: - Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the year. If ``True``, the first number is taken to - be the year, otherwise the last number is taken to be the year. If - this is set to ``None``, the value is retrieved from the current - :class:`parserinfo` object (which itself defaults to ``False``). - - :param fuzzy: - Whether to allow fuzzy parsing, allowing for string like "Today is - January 1, 2047 at 8:21:00AM". - - :param fuzzy_with_tokens: - If ``True``, ``fuzzy`` is automatically set to True, and the parser - will return a tuple where the first element is the parsed - :class:`datetime.datetime` datetimestamp and the second element is - a tuple containing the portions of the string which were ignored: - - .. doctest:: - - >>> from dateutil.parser import parse - >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True) - (datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at ')) - - :return: - Returns a :class:`datetime.datetime` object or, if the - ``fuzzy_with_tokens`` option is ``True``, returns a tuple, the - first element being a :class:`datetime.datetime` object, the second - a tuple containing the fuzzy tokens. - - :raises ParserError: - Raised for invalid or unknown string formats, if the provided - :class:`tzinfo` is not in a valid format, or if an invalid date would - be created. - - :raises OverflowError: - Raised if the parsed date exceeds the largest valid C integer on - your system. - """ - if parserinfo: - return parser(parserinfo).parse(timestr, **kwargs) - else: - return DEFAULTPARSER.parse(timestr, **kwargs) - - -class _tzparser(object): - - class _result(_resultbase): - - __slots__ = ["stdabbr", "stdoffset", "dstabbr", "dstoffset", - "start", "end"] - - class _attr(_resultbase): - __slots__ = ["month", "week", "weekday", - "yday", "jyday", "day", "time"] - - def __repr__(self): - return self._repr("") - - def __init__(self): - _resultbase.__init__(self) - self.start = self._attr() - self.end = self._attr() - - def parse(self, tzstr): - res = self._result() - l = [x for x in re.split(r'([,:.]|[a-zA-Z]+|[0-9]+)',tzstr) if x] - used_idxs = list() - try: - - len_l = len(l) - - i = 0 - while i < len_l: - # BRST+3[BRDT[+2]] - j = i - while j < len_l and not [x for x in l[j] - if x in "0123456789:,-+"]: - j += 1 - if j != i: - if not res.stdabbr: - offattr = "stdoffset" - res.stdabbr = "".join(l[i:j]) - else: - offattr = "dstoffset" - res.dstabbr = "".join(l[i:j]) - - for ii in range(j): - used_idxs.append(ii) - i = j - if (i < len_l and (l[i] in ('+', '-') or l[i][0] in - "0123456789")): - if l[i] in ('+', '-'): - # Yes, that's right. See the TZ variable - # documentation. - signal = (1, -1)[l[i] == '+'] - used_idxs.append(i) - i += 1 - else: - signal = -1 - len_li = len(l[i]) - if len_li == 4: - # -0300 - setattr(res, offattr, (int(l[i][:2]) * 3600 + - int(l[i][2:]) * 60) * signal) - elif i + 1 < len_l and l[i + 1] == ':': - # -03:00 - setattr(res, offattr, - (int(l[i]) * 3600 + - int(l[i + 2]) * 60) * signal) - used_idxs.append(i) - i += 2 - elif len_li <= 2: - # -[0]3 - setattr(res, offattr, - int(l[i][:2]) * 3600 * signal) - else: - return None - used_idxs.append(i) - i += 1 - if res.dstabbr: - break - else: - break - - - if i < len_l: - for j in range(i, len_l): - if l[j] == ';': - l[j] = ',' - - assert l[i] == ',' - - i += 1 - - if i >= len_l: - pass - elif (8 <= l.count(',') <= 9 and - not [y for x in l[i:] if x != ',' - for y in x if y not in "0123456789+-"]): - # GMT0BST,3,0,30,3600,10,0,26,7200[,3600] - for x in (res.start, res.end): - x.month = int(l[i]) - used_idxs.append(i) - i += 2 - if l[i] == '-': - value = int(l[i + 1]) * -1 - used_idxs.append(i) - i += 1 - else: - value = int(l[i]) - used_idxs.append(i) - i += 2 - if value: - x.week = value - x.weekday = (int(l[i]) - 1) % 7 - else: - x.day = int(l[i]) - used_idxs.append(i) - i += 2 - x.time = int(l[i]) - used_idxs.append(i) - i += 2 - if i < len_l: - if l[i] in ('-', '+'): - signal = (-1, 1)[l[i] == "+"] - used_idxs.append(i) - i += 1 - else: - signal = 1 - used_idxs.append(i) - res.dstoffset = (res.stdoffset + int(l[i]) * signal) - - # This was a made-up format that is not in normal use - warn(('Parsed time zone "%s"' % tzstr) + - 'is in a non-standard dateutil-specific format, which ' + - 'is now deprecated; support for parsing this format ' + - 'will be removed in future versions. It is recommended ' + - 'that you switch to a standard format like the GNU ' + - 'TZ variable format.', tz.DeprecatedTzFormatWarning) - elif (l.count(',') == 2 and l[i:].count('/') <= 2 and - not [y for x in l[i:] if x not in (',', '/', 'J', 'M', - '.', '-', ':') - for y in x if y not in "0123456789"]): - for x in (res.start, res.end): - if l[i] == 'J': - # non-leap year day (1 based) - used_idxs.append(i) - i += 1 - x.jyday = int(l[i]) - elif l[i] == 'M': - # month[-.]week[-.]weekday - used_idxs.append(i) - i += 1 - x.month = int(l[i]) - used_idxs.append(i) - i += 1 - assert l[i] in ('-', '.') - used_idxs.append(i) - i += 1 - x.week = int(l[i]) - if x.week == 5: - x.week = -1 - used_idxs.append(i) - i += 1 - assert l[i] in ('-', '.') - used_idxs.append(i) - i += 1 - x.weekday = (int(l[i]) - 1) % 7 - else: - # year day (zero based) - x.yday = int(l[i]) + 1 - - used_idxs.append(i) - i += 1 - - if i < len_l and l[i] == '/': - used_idxs.append(i) - i += 1 - # start time - len_li = len(l[i]) - if len_li == 4: - # -0300 - x.time = (int(l[i][:2]) * 3600 + - int(l[i][2:]) * 60) - elif i + 1 < len_l and l[i + 1] == ':': - # -03:00 - x.time = int(l[i]) * 3600 + int(l[i + 2]) * 60 - used_idxs.append(i) - i += 2 - if i + 1 < len_l and l[i + 1] == ':': - used_idxs.append(i) - i += 2 - x.time += int(l[i]) - elif len_li <= 2: - # -[0]3 - x.time = (int(l[i][:2]) * 3600) - else: - return None - used_idxs.append(i) - i += 1 - - assert i == len_l or l[i] == ',' - - i += 1 - - assert i >= len_l - - except (IndexError, ValueError, AssertionError): - return None - - unused_idxs = set(range(len_l)).difference(used_idxs) - res.any_unused_tokens = not {l[n] for n in unused_idxs}.issubset({",",":"}) - return res - - -DEFAULTTZPARSER = _tzparser() - - -def _parsetz(tzstr): - return DEFAULTTZPARSER.parse(tzstr) - - -class ParserError(ValueError): - """Exception subclass used for any failure to parse a datetime string. - - This is a subclass of :py:exc:`ValueError`, and should be raised any time - earlier versions of ``dateutil`` would have raised ``ValueError``. - - .. versionadded:: 2.8.1 - """ - def __str__(self): - try: - return self.args[0] % self.args[1:] - except (TypeError, IndexError): - return super(ParserError, self).__str__() - - def __repr__(self): - args = ", ".join("'%s'" % arg for arg in self.args) - return "%s(%s)" % (self.__class__.__name__, args) - - -class UnknownTimezoneWarning(RuntimeWarning): - """Raised when the parser finds a timezone it cannot parse into a tzinfo. - - .. versionadded:: 2.7.0 - """ -# vim:ts=4:sw=4:et diff --git a/apps/bitwarden_event_logs/lib/dateutil/parser/isoparser.py b/apps/bitwarden_event_logs/lib/dateutil/parser/isoparser.py deleted file mode 100755 index 7060087d..00000000 --- a/apps/bitwarden_event_logs/lib/dateutil/parser/isoparser.py +++ /dev/null @@ -1,416 +0,0 @@ -# -*- coding: utf-8 -*- -""" -This module offers a parser for ISO-8601 strings - -It is intended to support all valid date, time and datetime formats per the -ISO-8601 specification. - -..versionadded:: 2.7.0 -""" -from datetime import datetime, timedelta, time, date -import calendar -from dateutil import tz - -from functools import wraps - -import re -import six - -__all__ = ["isoparse", "isoparser"] - - -def _takes_ascii(f): - @wraps(f) - def func(self, str_in, *args, **kwargs): - # If it's a stream, read the whole thing - str_in = getattr(str_in, 'read', lambda: str_in)() - - # If it's unicode, turn it into bytes, since ISO-8601 only covers ASCII - if isinstance(str_in, six.text_type): - # ASCII is the same in UTF-8 - try: - str_in = str_in.encode('ascii') - except UnicodeEncodeError as e: - msg = 'ISO-8601 strings should contain only ASCII characters' - six.raise_from(ValueError(msg), e) - - return f(self, str_in, *args, **kwargs) - - return func - - -class isoparser(object): - def __init__(self, sep=None): - """ - :param sep: - A single character that separates date and time portions. If - ``None``, the parser will accept any single character. - For strict ISO-8601 adherence, pass ``'T'``. - """ - if sep is not None: - if (len(sep) != 1 or ord(sep) >= 128 or sep in '0123456789'): - raise ValueError('Separator must be a single, non-numeric ' + - 'ASCII character') - - sep = sep.encode('ascii') - - self._sep = sep - - @_takes_ascii - def isoparse(self, dt_str): - """ - Parse an ISO-8601 datetime string into a :class:`datetime.datetime`. - - An ISO-8601 datetime string consists of a date portion, followed - optionally by a time portion - the date and time portions are separated - by a single character separator, which is ``T`` in the official - standard. Incomplete date formats (such as ``YYYY-MM``) may *not* be - combined with a time portion. - - Supported date formats are: - - Common: - - - ``YYYY`` - - ``YYYY-MM`` - - ``YYYY-MM-DD`` or ``YYYYMMDD`` - - Uncommon: - - - ``YYYY-Www`` or ``YYYYWww`` - ISO week (day defaults to 0) - - ``YYYY-Www-D`` or ``YYYYWwwD`` - ISO week and day - - The ISO week and day numbering follows the same logic as - :func:`datetime.date.isocalendar`. - - Supported time formats are: - - - ``hh`` - - ``hh:mm`` or ``hhmm`` - - ``hh:mm:ss`` or ``hhmmss`` - - ``hh:mm:ss.ssssss`` (Up to 6 sub-second digits) - - Midnight is a special case for `hh`, as the standard supports both - 00:00 and 24:00 as a representation. The decimal separator can be - either a dot or a comma. - - - .. caution:: - - Support for fractional components other than seconds is part of the - ISO-8601 standard, but is not currently implemented in this parser. - - Supported time zone offset formats are: - - - `Z` (UTC) - - `±HH:MM` - - `±HHMM` - - `±HH` - - Offsets will be represented as :class:`dateutil.tz.tzoffset` objects, - with the exception of UTC, which will be represented as - :class:`dateutil.tz.tzutc`. Time zone offsets equivalent to UTC (such - as `+00:00`) will also be represented as :class:`dateutil.tz.tzutc`. - - :param dt_str: - A string or stream containing only an ISO-8601 datetime string - - :return: - Returns a :class:`datetime.datetime` representing the string. - Unspecified components default to their lowest value. - - .. warning:: - - As of version 2.7.0, the strictness of the parser should not be - considered a stable part of the contract. Any valid ISO-8601 string - that parses correctly with the default settings will continue to - parse correctly in future versions, but invalid strings that - currently fail (e.g. ``2017-01-01T00:00+00:00:00``) are not - guaranteed to continue failing in future versions if they encode - a valid date. - - .. versionadded:: 2.7.0 - """ - components, pos = self._parse_isodate(dt_str) - - if len(dt_str) > pos: - if self._sep is None or dt_str[pos:pos + 1] == self._sep: - components += self._parse_isotime(dt_str[pos + 1:]) - else: - raise ValueError('String contains unknown ISO components') - - if len(components) > 3 and components[3] == 24: - components[3] = 0 - return datetime(*components) + timedelta(days=1) - - return datetime(*components) - - @_takes_ascii - def parse_isodate(self, datestr): - """ - Parse the date portion of an ISO string. - - :param datestr: - The string portion of an ISO string, without a separator - - :return: - Returns a :class:`datetime.date` object - """ - components, pos = self._parse_isodate(datestr) - if pos < len(datestr): - raise ValueError('String contains unknown ISO ' + - 'components: {!r}'.format(datestr.decode('ascii'))) - return date(*components) - - @_takes_ascii - def parse_isotime(self, timestr): - """ - Parse the time portion of an ISO string. - - :param timestr: - The time portion of an ISO string, without a separator - - :return: - Returns a :class:`datetime.time` object - """ - components = self._parse_isotime(timestr) - if components[0] == 24: - components[0] = 0 - return time(*components) - - @_takes_ascii - def parse_tzstr(self, tzstr, zero_as_utc=True): - """ - Parse a valid ISO time zone string. - - See :func:`isoparser.isoparse` for details on supported formats. - - :param tzstr: - A string representing an ISO time zone offset - - :param zero_as_utc: - Whether to return :class:`dateutil.tz.tzutc` for zero-offset zones - - :return: - Returns :class:`dateutil.tz.tzoffset` for offsets and - :class:`dateutil.tz.tzutc` for ``Z`` and (if ``zero_as_utc`` is - specified) offsets equivalent to UTC. - """ - return self._parse_tzstr(tzstr, zero_as_utc=zero_as_utc) - - # Constants - _DATE_SEP = b'-' - _TIME_SEP = b':' - _FRACTION_REGEX = re.compile(b'[\\.,]([0-9]+)') - - def _parse_isodate(self, dt_str): - try: - return self._parse_isodate_common(dt_str) - except ValueError: - return self._parse_isodate_uncommon(dt_str) - - def _parse_isodate_common(self, dt_str): - len_str = len(dt_str) - components = [1, 1, 1] - - if len_str < 4: - raise ValueError('ISO string too short') - - # Year - components[0] = int(dt_str[0:4]) - pos = 4 - if pos >= len_str: - return components, pos - - has_sep = dt_str[pos:pos + 1] == self._DATE_SEP - if has_sep: - pos += 1 - - # Month - if len_str - pos < 2: - raise ValueError('Invalid common month') - - components[1] = int(dt_str[pos:pos + 2]) - pos += 2 - - if pos >= len_str: - if has_sep: - return components, pos - else: - raise ValueError('Invalid ISO format') - - if has_sep: - if dt_str[pos:pos + 1] != self._DATE_SEP: - raise ValueError('Invalid separator in ISO string') - pos += 1 - - # Day - if len_str - pos < 2: - raise ValueError('Invalid common day') - components[2] = int(dt_str[pos:pos + 2]) - return components, pos + 2 - - def _parse_isodate_uncommon(self, dt_str): - if len(dt_str) < 4: - raise ValueError('ISO string too short') - - # All ISO formats start with the year - year = int(dt_str[0:4]) - - has_sep = dt_str[4:5] == self._DATE_SEP - - pos = 4 + has_sep # Skip '-' if it's there - if dt_str[pos:pos + 1] == b'W': - # YYYY-?Www-?D? - pos += 1 - weekno = int(dt_str[pos:pos + 2]) - pos += 2 - - dayno = 1 - if len(dt_str) > pos: - if (dt_str[pos:pos + 1] == self._DATE_SEP) != has_sep: - raise ValueError('Inconsistent use of dash separator') - - pos += has_sep - - dayno = int(dt_str[pos:pos + 1]) - pos += 1 - - base_date = self._calculate_weekdate(year, weekno, dayno) - else: - # YYYYDDD or YYYY-DDD - if len(dt_str) - pos < 3: - raise ValueError('Invalid ordinal day') - - ordinal_day = int(dt_str[pos:pos + 3]) - pos += 3 - - if ordinal_day < 1 or ordinal_day > (365 + calendar.isleap(year)): - raise ValueError('Invalid ordinal day' + - ' {} for year {}'.format(ordinal_day, year)) - - base_date = date(year, 1, 1) + timedelta(days=ordinal_day - 1) - - components = [base_date.year, base_date.month, base_date.day] - return components, pos - - def _calculate_weekdate(self, year, week, day): - """ - Calculate the day of corresponding to the ISO year-week-day calendar. - - This function is effectively the inverse of - :func:`datetime.date.isocalendar`. - - :param year: - The year in the ISO calendar - - :param week: - The week in the ISO calendar - range is [1, 53] - - :param day: - The day in the ISO calendar - range is [1 (MON), 7 (SUN)] - - :return: - Returns a :class:`datetime.date` - """ - if not 0 < week < 54: - raise ValueError('Invalid week: {}'.format(week)) - - if not 0 < day < 8: # Range is 1-7 - raise ValueError('Invalid weekday: {}'.format(day)) - - # Get week 1 for the specific year: - jan_4 = date(year, 1, 4) # Week 1 always has January 4th in it - week_1 = jan_4 - timedelta(days=jan_4.isocalendar()[2] - 1) - - # Now add the specific number of weeks and days to get what we want - week_offset = (week - 1) * 7 + (day - 1) - return week_1 + timedelta(days=week_offset) - - def _parse_isotime(self, timestr): - len_str = len(timestr) - components = [0, 0, 0, 0, None] - pos = 0 - comp = -1 - - if len_str < 2: - raise ValueError('ISO time too short') - - has_sep = False - - while pos < len_str and comp < 5: - comp += 1 - - if timestr[pos:pos + 1] in b'-+Zz': - # Detect time zone boundary - components[-1] = self._parse_tzstr(timestr[pos:]) - pos = len_str - break - - if comp == 1 and timestr[pos:pos+1] == self._TIME_SEP: - has_sep = True - pos += 1 - elif comp == 2 and has_sep: - if timestr[pos:pos+1] != self._TIME_SEP: - raise ValueError('Inconsistent use of colon separator') - pos += 1 - - if comp < 3: - # Hour, minute, second - components[comp] = int(timestr[pos:pos + 2]) - pos += 2 - - if comp == 3: - # Fraction of a second - frac = self._FRACTION_REGEX.match(timestr[pos:]) - if not frac: - continue - - us_str = frac.group(1)[:6] # Truncate to microseconds - components[comp] = int(us_str) * 10**(6 - len(us_str)) - pos += len(frac.group()) - - if pos < len_str: - raise ValueError('Unused components in ISO string') - - if components[0] == 24: - # Standard supports 00:00 and 24:00 as representations of midnight - if any(component != 0 for component in components[1:4]): - raise ValueError('Hour may only be 24 at 24:00:00.000') - - return components - - def _parse_tzstr(self, tzstr, zero_as_utc=True): - if tzstr == b'Z' or tzstr == b'z': - return tz.UTC - - if len(tzstr) not in {3, 5, 6}: - raise ValueError('Time zone offset must be 1, 3, 5 or 6 characters') - - if tzstr[0:1] == b'-': - mult = -1 - elif tzstr[0:1] == b'+': - mult = 1 - else: - raise ValueError('Time zone offset requires sign') - - hours = int(tzstr[1:3]) - if len(tzstr) == 3: - minutes = 0 - else: - minutes = int(tzstr[(4 if tzstr[3:4] == self._TIME_SEP else 3):]) - - if zero_as_utc and hours == 0 and minutes == 0: - return tz.UTC - else: - if minutes > 59: - raise ValueError('Invalid minutes in time zone offset') - - if hours > 23: - raise ValueError('Invalid hours in time zone offset') - - return tz.tzoffset(None, mult * (hours * 60 + minutes) * 60) - - -DEFAULT_ISOPARSER = isoparser() -isoparse = DEFAULT_ISOPARSER.isoparse diff --git a/apps/bitwarden_event_logs/lib/dateutil/relativedelta.py b/apps/bitwarden_event_logs/lib/dateutil/relativedelta.py deleted file mode 100755 index cd323a54..00000000 --- a/apps/bitwarden_event_logs/lib/dateutil/relativedelta.py +++ /dev/null @@ -1,599 +0,0 @@ -# -*- coding: utf-8 -*- -import datetime -import calendar - -import operator -from math import copysign - -from six import integer_types -from warnings import warn - -from ._common import weekday - -MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7)) - -__all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"] - - -class relativedelta(object): - """ - The relativedelta type is designed to be applied to an existing datetime and - can replace specific components of that datetime, or represents an interval - of time. - - It is based on the specification of the excellent work done by M.-A. Lemburg - in his - `mx.DateTime `_ extension. - However, notice that this type does *NOT* implement the same algorithm as - his work. Do *NOT* expect it to behave like mx.DateTime's counterpart. - - There are two different ways to build a relativedelta instance. The - first one is passing it two date/datetime classes:: - - relativedelta(datetime1, datetime2) - - The second one is passing it any number of the following keyword arguments:: - - relativedelta(arg1=x,arg2=y,arg3=z...) - - year, month, day, hour, minute, second, microsecond: - Absolute information (argument is singular); adding or subtracting a - relativedelta with absolute information does not perform an arithmetic - operation, but rather REPLACES the corresponding value in the - original datetime with the value(s) in relativedelta. - - years, months, weeks, days, hours, minutes, seconds, microseconds: - Relative information, may be negative (argument is plural); adding - or subtracting a relativedelta with relative information performs - the corresponding arithmetic operation on the original datetime value - with the information in the relativedelta. - - weekday: - One of the weekday instances (MO, TU, etc) available in the - relativedelta module. These instances may receive a parameter N, - specifying the Nth weekday, which could be positive or negative - (like MO(+1) or MO(-2)). Not specifying it is the same as specifying - +1. You can also use an integer, where 0=MO. This argument is always - relative e.g. if the calculated date is already Monday, using MO(1) - or MO(-1) won't change the day. To effectively make it absolute, use - it in combination with the day argument (e.g. day=1, MO(1) for first - Monday of the month). - - leapdays: - Will add given days to the date found, if year is a leap - year, and the date found is post 28 of february. - - yearday, nlyearday: - Set the yearday or the non-leap year day (jump leap days). - These are converted to day/month/leapdays information. - - There are relative and absolute forms of the keyword - arguments. The plural is relative, and the singular is - absolute. For each argument in the order below, the absolute form - is applied first (by setting each attribute to that value) and - then the relative form (by adding the value to the attribute). - - The order of attributes considered when this relativedelta is - added to a datetime is: - - 1. Year - 2. Month - 3. Day - 4. Hours - 5. Minutes - 6. Seconds - 7. Microseconds - - Finally, weekday is applied, using the rule described above. - - For example - - >>> from datetime import datetime - >>> from dateutil.relativedelta import relativedelta, MO - >>> dt = datetime(2018, 4, 9, 13, 37, 0) - >>> delta = relativedelta(hours=25, day=1, weekday=MO(1)) - >>> dt + delta - datetime.datetime(2018, 4, 2, 14, 37) - - First, the day is set to 1 (the first of the month), then 25 hours - are added, to get to the 2nd day and 14th hour, finally the - weekday is applied, but since the 2nd is already a Monday there is - no effect. - - """ - - def __init__(self, dt1=None, dt2=None, - years=0, months=0, days=0, leapdays=0, weeks=0, - hours=0, minutes=0, seconds=0, microseconds=0, - year=None, month=None, day=None, weekday=None, - yearday=None, nlyearday=None, - hour=None, minute=None, second=None, microsecond=None): - - if dt1 and dt2: - # datetime is a subclass of date. So both must be date - if not (isinstance(dt1, datetime.date) and - isinstance(dt2, datetime.date)): - raise TypeError("relativedelta only diffs datetime/date") - - # We allow two dates, or two datetimes, so we coerce them to be - # of the same type - if (isinstance(dt1, datetime.datetime) != - isinstance(dt2, datetime.datetime)): - if not isinstance(dt1, datetime.datetime): - dt1 = datetime.datetime.fromordinal(dt1.toordinal()) - elif not isinstance(dt2, datetime.datetime): - dt2 = datetime.datetime.fromordinal(dt2.toordinal()) - - self.years = 0 - self.months = 0 - self.days = 0 - self.leapdays = 0 - self.hours = 0 - self.minutes = 0 - self.seconds = 0 - self.microseconds = 0 - self.year = None - self.month = None - self.day = None - self.weekday = None - self.hour = None - self.minute = None - self.second = None - self.microsecond = None - self._has_time = 0 - - # Get year / month delta between the two - months = (dt1.year - dt2.year) * 12 + (dt1.month - dt2.month) - self._set_months(months) - - # Remove the year/month delta so the timedelta is just well-defined - # time units (seconds, days and microseconds) - dtm = self.__radd__(dt2) - - # If we've overshot our target, make an adjustment - if dt1 < dt2: - compare = operator.gt - increment = 1 - else: - compare = operator.lt - increment = -1 - - while compare(dt1, dtm): - months += increment - self._set_months(months) - dtm = self.__radd__(dt2) - - # Get the timedelta between the "months-adjusted" date and dt1 - delta = dt1 - dtm - self.seconds = delta.seconds + delta.days * 86400 - self.microseconds = delta.microseconds - else: - # Check for non-integer values in integer-only quantities - if any(x is not None and x != int(x) for x in (years, months)): - raise ValueError("Non-integer years and months are " - "ambiguous and not currently supported.") - - # Relative information - self.years = int(years) - self.months = int(months) - self.days = days + weeks * 7 - self.leapdays = leapdays - self.hours = hours - self.minutes = minutes - self.seconds = seconds - self.microseconds = microseconds - - # Absolute information - self.year = year - self.month = month - self.day = day - self.hour = hour - self.minute = minute - self.second = second - self.microsecond = microsecond - - if any(x is not None and int(x) != x - for x in (year, month, day, hour, - minute, second, microsecond)): - # For now we'll deprecate floats - later it'll be an error. - warn("Non-integer value passed as absolute information. " + - "This is not a well-defined condition and will raise " + - "errors in future versions.", DeprecationWarning) - - if isinstance(weekday, integer_types): - self.weekday = weekdays[weekday] - else: - self.weekday = weekday - - yday = 0 - if nlyearday: - yday = nlyearday - elif yearday: - yday = yearday - if yearday > 59: - self.leapdays = -1 - if yday: - ydayidx = [31, 59, 90, 120, 151, 181, 212, - 243, 273, 304, 334, 366] - for idx, ydays in enumerate(ydayidx): - if yday <= ydays: - self.month = idx+1 - if idx == 0: - self.day = yday - else: - self.day = yday-ydayidx[idx-1] - break - else: - raise ValueError("invalid year day (%d)" % yday) - - self._fix() - - def _fix(self): - if abs(self.microseconds) > 999999: - s = _sign(self.microseconds) - div, mod = divmod(self.microseconds * s, 1000000) - self.microseconds = mod * s - self.seconds += div * s - if abs(self.seconds) > 59: - s = _sign(self.seconds) - div, mod = divmod(self.seconds * s, 60) - self.seconds = mod * s - self.minutes += div * s - if abs(self.minutes) > 59: - s = _sign(self.minutes) - div, mod = divmod(self.minutes * s, 60) - self.minutes = mod * s - self.hours += div * s - if abs(self.hours) > 23: - s = _sign(self.hours) - div, mod = divmod(self.hours * s, 24) - self.hours = mod * s - self.days += div * s - if abs(self.months) > 11: - s = _sign(self.months) - div, mod = divmod(self.months * s, 12) - self.months = mod * s - self.years += div * s - if (self.hours or self.minutes or self.seconds or self.microseconds - or self.hour is not None or self.minute is not None or - self.second is not None or self.microsecond is not None): - self._has_time = 1 - else: - self._has_time = 0 - - @property - def weeks(self): - return int(self.days / 7.0) - - @weeks.setter - def weeks(self, value): - self.days = self.days - (self.weeks * 7) + value * 7 - - def _set_months(self, months): - self.months = months - if abs(self.months) > 11: - s = _sign(self.months) - div, mod = divmod(self.months * s, 12) - self.months = mod * s - self.years = div * s - else: - self.years = 0 - - def normalized(self): - """ - Return a version of this object represented entirely using integer - values for the relative attributes. - - >>> relativedelta(days=1.5, hours=2).normalized() - relativedelta(days=+1, hours=+14) - - :return: - Returns a :class:`dateutil.relativedelta.relativedelta` object. - """ - # Cascade remainders down (rounding each to roughly nearest microsecond) - days = int(self.days) - - hours_f = round(self.hours + 24 * (self.days - days), 11) - hours = int(hours_f) - - minutes_f = round(self.minutes + 60 * (hours_f - hours), 10) - minutes = int(minutes_f) - - seconds_f = round(self.seconds + 60 * (minutes_f - minutes), 8) - seconds = int(seconds_f) - - microseconds = round(self.microseconds + 1e6 * (seconds_f - seconds)) - - # Constructor carries overflow back up with call to _fix() - return self.__class__(years=self.years, months=self.months, - days=days, hours=hours, minutes=minutes, - seconds=seconds, microseconds=microseconds, - leapdays=self.leapdays, year=self.year, - month=self.month, day=self.day, - weekday=self.weekday, hour=self.hour, - minute=self.minute, second=self.second, - microsecond=self.microsecond) - - def __add__(self, other): - if isinstance(other, relativedelta): - return self.__class__(years=other.years + self.years, - months=other.months + self.months, - days=other.days + self.days, - hours=other.hours + self.hours, - minutes=other.minutes + self.minutes, - seconds=other.seconds + self.seconds, - microseconds=(other.microseconds + - self.microseconds), - leapdays=other.leapdays or self.leapdays, - year=(other.year if other.year is not None - else self.year), - month=(other.month if other.month is not None - else self.month), - day=(other.day if other.day is not None - else self.day), - weekday=(other.weekday if other.weekday is not None - else self.weekday), - hour=(other.hour if other.hour is not None - else self.hour), - minute=(other.minute if other.minute is not None - else self.minute), - second=(other.second if other.second is not None - else self.second), - microsecond=(other.microsecond if other.microsecond - is not None else - self.microsecond)) - if isinstance(other, datetime.timedelta): - return self.__class__(years=self.years, - months=self.months, - days=self.days + other.days, - hours=self.hours, - minutes=self.minutes, - seconds=self.seconds + other.seconds, - microseconds=self.microseconds + other.microseconds, - leapdays=self.leapdays, - year=self.year, - month=self.month, - day=self.day, - weekday=self.weekday, - hour=self.hour, - minute=self.minute, - second=self.second, - microsecond=self.microsecond) - if not isinstance(other, datetime.date): - return NotImplemented - elif self._has_time and not isinstance(other, datetime.datetime): - other = datetime.datetime.fromordinal(other.toordinal()) - year = (self.year or other.year)+self.years - month = self.month or other.month - if self.months: - assert 1 <= abs(self.months) <= 12 - month += self.months - if month > 12: - year += 1 - month -= 12 - elif month < 1: - year -= 1 - month += 12 - day = min(calendar.monthrange(year, month)[1], - self.day or other.day) - repl = {"year": year, "month": month, "day": day} - for attr in ["hour", "minute", "second", "microsecond"]: - value = getattr(self, attr) - if value is not None: - repl[attr] = value - days = self.days - if self.leapdays and month > 2 and calendar.isleap(year): - days += self.leapdays - ret = (other.replace(**repl) - + datetime.timedelta(days=days, - hours=self.hours, - minutes=self.minutes, - seconds=self.seconds, - microseconds=self.microseconds)) - if self.weekday: - weekday, nth = self.weekday.weekday, self.weekday.n or 1 - jumpdays = (abs(nth) - 1) * 7 - if nth > 0: - jumpdays += (7 - ret.weekday() + weekday) % 7 - else: - jumpdays += (ret.weekday() - weekday) % 7 - jumpdays *= -1 - ret += datetime.timedelta(days=jumpdays) - return ret - - def __radd__(self, other): - return self.__add__(other) - - def __rsub__(self, other): - return self.__neg__().__radd__(other) - - def __sub__(self, other): - if not isinstance(other, relativedelta): - return NotImplemented # In case the other object defines __rsub__ - return self.__class__(years=self.years - other.years, - months=self.months - other.months, - days=self.days - other.days, - hours=self.hours - other.hours, - minutes=self.minutes - other.minutes, - seconds=self.seconds - other.seconds, - microseconds=self.microseconds - other.microseconds, - leapdays=self.leapdays or other.leapdays, - year=(self.year if self.year is not None - else other.year), - month=(self.month if self.month is not None else - other.month), - day=(self.day if self.day is not None else - other.day), - weekday=(self.weekday if self.weekday is not None else - other.weekday), - hour=(self.hour if self.hour is not None else - other.hour), - minute=(self.minute if self.minute is not None else - other.minute), - second=(self.second if self.second is not None else - other.second), - microsecond=(self.microsecond if self.microsecond - is not None else - other.microsecond)) - - def __abs__(self): - return self.__class__(years=abs(self.years), - months=abs(self.months), - days=abs(self.days), - hours=abs(self.hours), - minutes=abs(self.minutes), - seconds=abs(self.seconds), - microseconds=abs(self.microseconds), - leapdays=self.leapdays, - year=self.year, - month=self.month, - day=self.day, - weekday=self.weekday, - hour=self.hour, - minute=self.minute, - second=self.second, - microsecond=self.microsecond) - - def __neg__(self): - return self.__class__(years=-self.years, - months=-self.months, - days=-self.days, - hours=-self.hours, - minutes=-self.minutes, - seconds=-self.seconds, - microseconds=-self.microseconds, - leapdays=self.leapdays, - year=self.year, - month=self.month, - day=self.day, - weekday=self.weekday, - hour=self.hour, - minute=self.minute, - second=self.second, - microsecond=self.microsecond) - - def __bool__(self): - return not (not self.years and - not self.months and - not self.days and - not self.hours and - not self.minutes and - not self.seconds and - not self.microseconds and - not self.leapdays and - self.year is None and - self.month is None and - self.day is None and - self.weekday is None and - self.hour is None and - self.minute is None and - self.second is None and - self.microsecond is None) - # Compatibility with Python 2.x - __nonzero__ = __bool__ - - def __mul__(self, other): - try: - f = float(other) - except TypeError: - return NotImplemented - - return self.__class__(years=int(self.years * f), - months=int(self.months * f), - days=int(self.days * f), - hours=int(self.hours * f), - minutes=int(self.minutes * f), - seconds=int(self.seconds * f), - microseconds=int(self.microseconds * f), - leapdays=self.leapdays, - year=self.year, - month=self.month, - day=self.day, - weekday=self.weekday, - hour=self.hour, - minute=self.minute, - second=self.second, - microsecond=self.microsecond) - - __rmul__ = __mul__ - - def __eq__(self, other): - if not isinstance(other, relativedelta): - return NotImplemented - if self.weekday or other.weekday: - if not self.weekday or not other.weekday: - return False - if self.weekday.weekday != other.weekday.weekday: - return False - n1, n2 = self.weekday.n, other.weekday.n - if n1 != n2 and not ((not n1 or n1 == 1) and (not n2 or n2 == 1)): - return False - return (self.years == other.years and - self.months == other.months and - self.days == other.days and - self.hours == other.hours and - self.minutes == other.minutes and - self.seconds == other.seconds and - self.microseconds == other.microseconds and - self.leapdays == other.leapdays and - self.year == other.year and - self.month == other.month and - self.day == other.day and - self.hour == other.hour and - self.minute == other.minute and - self.second == other.second and - self.microsecond == other.microsecond) - - def __hash__(self): - return hash(( - self.weekday, - self.years, - self.months, - self.days, - self.hours, - self.minutes, - self.seconds, - self.microseconds, - self.leapdays, - self.year, - self.month, - self.day, - self.hour, - self.minute, - self.second, - self.microsecond, - )) - - def __ne__(self, other): - return not self.__eq__(other) - - def __div__(self, other): - try: - reciprocal = 1 / float(other) - except TypeError: - return NotImplemented - - return self.__mul__(reciprocal) - - __truediv__ = __div__ - - def __repr__(self): - l = [] - for attr in ["years", "months", "days", "leapdays", - "hours", "minutes", "seconds", "microseconds"]: - value = getattr(self, attr) - if value: - l.append("{attr}={value:+g}".format(attr=attr, value=value)) - for attr in ["year", "month", "day", "weekday", - "hour", "minute", "second", "microsecond"]: - value = getattr(self, attr) - if value is not None: - l.append("{attr}={value}".format(attr=attr, value=repr(value))) - return "{classname}({attrs})".format(classname=self.__class__.__name__, - attrs=", ".join(l)) - - -def _sign(x): - return int(copysign(1, x)) - -# vim:ts=4:sw=4:et diff --git a/apps/bitwarden_event_logs/lib/dateutil/rrule.py b/apps/bitwarden_event_logs/lib/dateutil/rrule.py deleted file mode 100755 index 571a0d2b..00000000 --- a/apps/bitwarden_event_logs/lib/dateutil/rrule.py +++ /dev/null @@ -1,1737 +0,0 @@ -# -*- coding: utf-8 -*- -""" -The rrule module offers a small, complete, and very fast, implementation of -the recurrence rules documented in the -`iCalendar RFC `_, -including support for caching of results. -""" -import calendar -import datetime -import heapq -import itertools -import re -import sys -from functools import wraps -# For warning about deprecation of until and count -from warnings import warn - -from six import advance_iterator, integer_types - -from six.moves import _thread, range - -from ._common import weekday as weekdaybase - -try: - from math import gcd -except ImportError: - from fractions import gcd - -__all__ = ["rrule", "rruleset", "rrulestr", - "YEARLY", "MONTHLY", "WEEKLY", "DAILY", - "HOURLY", "MINUTELY", "SECONDLY", - "MO", "TU", "WE", "TH", "FR", "SA", "SU"] - -# Every mask is 7 days longer to handle cross-year weekly periods. -M366MASK = tuple([1]*31+[2]*29+[3]*31+[4]*30+[5]*31+[6]*30 + - [7]*31+[8]*31+[9]*30+[10]*31+[11]*30+[12]*31+[1]*7) -M365MASK = list(M366MASK) -M29, M30, M31 = list(range(1, 30)), list(range(1, 31)), list(range(1, 32)) -MDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7]) -MDAY365MASK = list(MDAY366MASK) -M29, M30, M31 = list(range(-29, 0)), list(range(-30, 0)), list(range(-31, 0)) -NMDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7]) -NMDAY365MASK = list(NMDAY366MASK) -M366RANGE = (0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366) -M365RANGE = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365) -WDAYMASK = [0, 1, 2, 3, 4, 5, 6]*55 -del M29, M30, M31, M365MASK[59], MDAY365MASK[59], NMDAY365MASK[31] -MDAY365MASK = tuple(MDAY365MASK) -M365MASK = tuple(M365MASK) - -FREQNAMES = ['YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY', 'HOURLY', 'MINUTELY', 'SECONDLY'] - -(YEARLY, - MONTHLY, - WEEKLY, - DAILY, - HOURLY, - MINUTELY, - SECONDLY) = list(range(7)) - -# Imported on demand. -easter = None -parser = None - - -class weekday(weekdaybase): - """ - This version of weekday does not allow n = 0. - """ - def __init__(self, wkday, n=None): - if n == 0: - raise ValueError("Can't create weekday with n==0") - - super(weekday, self).__init__(wkday, n) - - -MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7)) - - -def _invalidates_cache(f): - """ - Decorator for rruleset methods which may invalidate the - cached length. - """ - @wraps(f) - def inner_func(self, *args, **kwargs): - rv = f(self, *args, **kwargs) - self._invalidate_cache() - return rv - - return inner_func - - -class rrulebase(object): - def __init__(self, cache=False): - if cache: - self._cache = [] - self._cache_lock = _thread.allocate_lock() - self._invalidate_cache() - else: - self._cache = None - self._cache_complete = False - self._len = None - - def __iter__(self): - if self._cache_complete: - return iter(self._cache) - elif self._cache is None: - return self._iter() - else: - return self._iter_cached() - - def _invalidate_cache(self): - if self._cache is not None: - self._cache = [] - self._cache_complete = False - self._cache_gen = self._iter() - - if self._cache_lock.locked(): - self._cache_lock.release() - - self._len = None - - def _iter_cached(self): - i = 0 - gen = self._cache_gen - cache = self._cache - acquire = self._cache_lock.acquire - release = self._cache_lock.release - while gen: - if i == len(cache): - acquire() - if self._cache_complete: - break - try: - for j in range(10): - cache.append(advance_iterator(gen)) - except StopIteration: - self._cache_gen = gen = None - self._cache_complete = True - break - release() - yield cache[i] - i += 1 - while i < self._len: - yield cache[i] - i += 1 - - def __getitem__(self, item): - if self._cache_complete: - return self._cache[item] - elif isinstance(item, slice): - if item.step and item.step < 0: - return list(iter(self))[item] - else: - return list(itertools.islice(self, - item.start or 0, - item.stop or sys.maxsize, - item.step or 1)) - elif item >= 0: - gen = iter(self) - try: - for i in range(item+1): - res = advance_iterator(gen) - except StopIteration: - raise IndexError - return res - else: - return list(iter(self))[item] - - def __contains__(self, item): - if self._cache_complete: - return item in self._cache - else: - for i in self: - if i == item: - return True - elif i > item: - return False - return False - - # __len__() introduces a large performance penalty. - def count(self): - """ Returns the number of recurrences in this set. It will have go - through the whole recurrence, if this hasn't been done before. """ - if self._len is None: - for x in self: - pass - return self._len - - def before(self, dt, inc=False): - """ Returns the last recurrence before the given datetime instance. The - inc keyword defines what happens if dt is an occurrence. With - inc=True, if dt itself is an occurrence, it will be returned. """ - if self._cache_complete: - gen = self._cache - else: - gen = self - last = None - if inc: - for i in gen: - if i > dt: - break - last = i - else: - for i in gen: - if i >= dt: - break - last = i - return last - - def after(self, dt, inc=False): - """ Returns the first recurrence after the given datetime instance. The - inc keyword defines what happens if dt is an occurrence. With - inc=True, if dt itself is an occurrence, it will be returned. """ - if self._cache_complete: - gen = self._cache - else: - gen = self - if inc: - for i in gen: - if i >= dt: - return i - else: - for i in gen: - if i > dt: - return i - return None - - def xafter(self, dt, count=None, inc=False): - """ - Generator which yields up to `count` recurrences after the given - datetime instance, equivalent to `after`. - - :param dt: - The datetime at which to start generating recurrences. - - :param count: - The maximum number of recurrences to generate. If `None` (default), - dates are generated until the recurrence rule is exhausted. - - :param inc: - If `dt` is an instance of the rule and `inc` is `True`, it is - included in the output. - - :yields: Yields a sequence of `datetime` objects. - """ - - if self._cache_complete: - gen = self._cache - else: - gen = self - - # Select the comparison function - if inc: - comp = lambda dc, dtc: dc >= dtc - else: - comp = lambda dc, dtc: dc > dtc - - # Generate dates - n = 0 - for d in gen: - if comp(d, dt): - if count is not None: - n += 1 - if n > count: - break - - yield d - - def between(self, after, before, inc=False, count=1): - """ Returns all the occurrences of the rrule between after and before. - The inc keyword defines what happens if after and/or before are - themselves occurrences. With inc=True, they will be included in the - list, if they are found in the recurrence set. """ - if self._cache_complete: - gen = self._cache - else: - gen = self - started = False - l = [] - if inc: - for i in gen: - if i > before: - break - elif not started: - if i >= after: - started = True - l.append(i) - else: - l.append(i) - else: - for i in gen: - if i >= before: - break - elif not started: - if i > after: - started = True - l.append(i) - else: - l.append(i) - return l - - -class rrule(rrulebase): - """ - That's the base of the rrule operation. It accepts all the keywords - defined in the RFC as its constructor parameters (except byday, - which was renamed to byweekday) and more. The constructor prototype is:: - - rrule(freq) - - Where freq must be one of YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, - or SECONDLY. - - .. note:: - Per RFC section 3.3.10, recurrence instances falling on invalid dates - and times are ignored rather than coerced: - - Recurrence rules may generate recurrence instances with an invalid - date (e.g., February 30) or nonexistent local time (e.g., 1:30 AM - on a day where the local time is moved forward by an hour at 1:00 - AM). Such recurrence instances MUST be ignored and MUST NOT be - counted as part of the recurrence set. - - This can lead to possibly surprising behavior when, for example, the - start date occurs at the end of the month: - - >>> from dateutil.rrule import rrule, MONTHLY - >>> from datetime import datetime - >>> start_date = datetime(2014, 12, 31) - >>> list(rrule(freq=MONTHLY, count=4, dtstart=start_date)) - ... # doctest: +NORMALIZE_WHITESPACE - [datetime.datetime(2014, 12, 31, 0, 0), - datetime.datetime(2015, 1, 31, 0, 0), - datetime.datetime(2015, 3, 31, 0, 0), - datetime.datetime(2015, 5, 31, 0, 0)] - - Additionally, it supports the following keyword arguments: - - :param dtstart: - The recurrence start. Besides being the base for the recurrence, - missing parameters in the final recurrence instances will also be - extracted from this date. If not given, datetime.now() will be used - instead. - :param interval: - The interval between each freq iteration. For example, when using - YEARLY, an interval of 2 means once every two years, but with HOURLY, - it means once every two hours. The default interval is 1. - :param wkst: - The week start day. Must be one of the MO, TU, WE constants, or an - integer, specifying the first day of the week. This will affect - recurrences based on weekly periods. The default week start is got - from calendar.firstweekday(), and may be modified by - calendar.setfirstweekday(). - :param count: - If given, this determines how many occurrences will be generated. - - .. note:: - As of version 2.5.0, the use of the keyword ``until`` in conjunction - with ``count`` is deprecated, to make sure ``dateutil`` is fully - compliant with `RFC-5545 Sec. 3.3.10 `_. Therefore, ``until`` and ``count`` - **must not** occur in the same call to ``rrule``. - :param until: - If given, this must be a datetime instance specifying the upper-bound - limit of the recurrence. The last recurrence in the rule is the greatest - datetime that is less than or equal to the value specified in the - ``until`` parameter. - - .. note:: - As of version 2.5.0, the use of the keyword ``until`` in conjunction - with ``count`` is deprecated, to make sure ``dateutil`` is fully - compliant with `RFC-5545 Sec. 3.3.10 `_. Therefore, ``until`` and ``count`` - **must not** occur in the same call to ``rrule``. - :param bysetpos: - If given, it must be either an integer, or a sequence of integers, - positive or negative. Each given integer will specify an occurrence - number, corresponding to the nth occurrence of the rule inside the - frequency period. For example, a bysetpos of -1 if combined with a - MONTHLY frequency, and a byweekday of (MO, TU, WE, TH, FR), will - result in the last work day of every month. - :param bymonth: - If given, it must be either an integer, or a sequence of integers, - meaning the months to apply the recurrence to. - :param bymonthday: - If given, it must be either an integer, or a sequence of integers, - meaning the month days to apply the recurrence to. - :param byyearday: - If given, it must be either an integer, or a sequence of integers, - meaning the year days to apply the recurrence to. - :param byeaster: - If given, it must be either an integer, or a sequence of integers, - positive or negative. Each integer will define an offset from the - Easter Sunday. Passing the offset 0 to byeaster will yield the Easter - Sunday itself. This is an extension to the RFC specification. - :param byweekno: - If given, it must be either an integer, or a sequence of integers, - meaning the week numbers to apply the recurrence to. Week numbers - have the meaning described in ISO8601, that is, the first week of - the year is that containing at least four days of the new year. - :param byweekday: - If given, it must be either an integer (0 == MO), a sequence of - integers, one of the weekday constants (MO, TU, etc), or a sequence - of these constants. When given, these variables will define the - weekdays where the recurrence will be applied. It's also possible to - use an argument n for the weekday instances, which will mean the nth - occurrence of this weekday in the period. For example, with MONTHLY, - or with YEARLY and BYMONTH, using FR(+1) in byweekday will specify the - first friday of the month where the recurrence happens. Notice that in - the RFC documentation, this is specified as BYDAY, but was renamed to - avoid the ambiguity of that keyword. - :param byhour: - If given, it must be either an integer, or a sequence of integers, - meaning the hours to apply the recurrence to. - :param byminute: - If given, it must be either an integer, or a sequence of integers, - meaning the minutes to apply the recurrence to. - :param bysecond: - If given, it must be either an integer, or a sequence of integers, - meaning the seconds to apply the recurrence to. - :param cache: - If given, it must be a boolean value specifying to enable or disable - caching of results. If you will use the same rrule instance multiple - times, enabling caching will improve the performance considerably. - """ - def __init__(self, freq, dtstart=None, - interval=1, wkst=None, count=None, until=None, bysetpos=None, - bymonth=None, bymonthday=None, byyearday=None, byeaster=None, - byweekno=None, byweekday=None, - byhour=None, byminute=None, bysecond=None, - cache=False): - super(rrule, self).__init__(cache) - global easter - if not dtstart: - if until and until.tzinfo: - dtstart = datetime.datetime.now(tz=until.tzinfo).replace(microsecond=0) - else: - dtstart = datetime.datetime.now().replace(microsecond=0) - elif not isinstance(dtstart, datetime.datetime): - dtstart = datetime.datetime.fromordinal(dtstart.toordinal()) - else: - dtstart = dtstart.replace(microsecond=0) - self._dtstart = dtstart - self._tzinfo = dtstart.tzinfo - self._freq = freq - self._interval = interval - self._count = count - - # Cache the original byxxx rules, if they are provided, as the _byxxx - # attributes do not necessarily map to the inputs, and this can be - # a problem in generating the strings. Only store things if they've - # been supplied (the string retrieval will just use .get()) - self._original_rule = {} - - if until and not isinstance(until, datetime.datetime): - until = datetime.datetime.fromordinal(until.toordinal()) - self._until = until - - if self._dtstart and self._until: - if (self._dtstart.tzinfo is not None) != (self._until.tzinfo is not None): - # According to RFC5545 Section 3.3.10: - # https://tools.ietf.org/html/rfc5545#section-3.3.10 - # - # > If the "DTSTART" property is specified as a date with UTC - # > time or a date with local time and time zone reference, - # > then the UNTIL rule part MUST be specified as a date with - # > UTC time. - raise ValueError( - 'RRULE UNTIL values must be specified in UTC when DTSTART ' - 'is timezone-aware' - ) - - if count is not None and until: - warn("Using both 'count' and 'until' is inconsistent with RFC 5545" - " and has been deprecated in dateutil. Future versions will " - "raise an error.", DeprecationWarning) - - if wkst is None: - self._wkst = calendar.firstweekday() - elif isinstance(wkst, integer_types): - self._wkst = wkst - else: - self._wkst = wkst.weekday - - if bysetpos is None: - self._bysetpos = None - elif isinstance(bysetpos, integer_types): - if bysetpos == 0 or not (-366 <= bysetpos <= 366): - raise ValueError("bysetpos must be between 1 and 366, " - "or between -366 and -1") - self._bysetpos = (bysetpos,) - else: - self._bysetpos = tuple(bysetpos) - for pos in self._bysetpos: - if pos == 0 or not (-366 <= pos <= 366): - raise ValueError("bysetpos must be between 1 and 366, " - "or between -366 and -1") - - if self._bysetpos: - self._original_rule['bysetpos'] = self._bysetpos - - if (byweekno is None and byyearday is None and bymonthday is None and - byweekday is None and byeaster is None): - if freq == YEARLY: - if bymonth is None: - bymonth = dtstart.month - self._original_rule['bymonth'] = None - bymonthday = dtstart.day - self._original_rule['bymonthday'] = None - elif freq == MONTHLY: - bymonthday = dtstart.day - self._original_rule['bymonthday'] = None - elif freq == WEEKLY: - byweekday = dtstart.weekday() - self._original_rule['byweekday'] = None - - # bymonth - if bymonth is None: - self._bymonth = None - else: - if isinstance(bymonth, integer_types): - bymonth = (bymonth,) - - self._bymonth = tuple(sorted(set(bymonth))) - - if 'bymonth' not in self._original_rule: - self._original_rule['bymonth'] = self._bymonth - - # byyearday - if byyearday is None: - self._byyearday = None - else: - if isinstance(byyearday, integer_types): - byyearday = (byyearday,) - - self._byyearday = tuple(sorted(set(byyearday))) - self._original_rule['byyearday'] = self._byyearday - - # byeaster - if byeaster is not None: - if not easter: - from dateutil import easter - if isinstance(byeaster, integer_types): - self._byeaster = (byeaster,) - else: - self._byeaster = tuple(sorted(byeaster)) - - self._original_rule['byeaster'] = self._byeaster - else: - self._byeaster = None - - # bymonthday - if bymonthday is None: - self._bymonthday = () - self._bynmonthday = () - else: - if isinstance(bymonthday, integer_types): - bymonthday = (bymonthday,) - - bymonthday = set(bymonthday) # Ensure it's unique - - self._bymonthday = tuple(sorted(x for x in bymonthday if x > 0)) - self._bynmonthday = tuple(sorted(x for x in bymonthday if x < 0)) - - # Storing positive numbers first, then negative numbers - if 'bymonthday' not in self._original_rule: - self._original_rule['bymonthday'] = tuple( - itertools.chain(self._bymonthday, self._bynmonthday)) - - # byweekno - if byweekno is None: - self._byweekno = None - else: - if isinstance(byweekno, integer_types): - byweekno = (byweekno,) - - self._byweekno = tuple(sorted(set(byweekno))) - - self._original_rule['byweekno'] = self._byweekno - - # byweekday / bynweekday - if byweekday is None: - self._byweekday = None - self._bynweekday = None - else: - # If it's one of the valid non-sequence types, convert to a - # single-element sequence before the iterator that builds the - # byweekday set. - if isinstance(byweekday, integer_types) or hasattr(byweekday, "n"): - byweekday = (byweekday,) - - self._byweekday = set() - self._bynweekday = set() - for wday in byweekday: - if isinstance(wday, integer_types): - self._byweekday.add(wday) - elif not wday.n or freq > MONTHLY: - self._byweekday.add(wday.weekday) - else: - self._bynweekday.add((wday.weekday, wday.n)) - - if not self._byweekday: - self._byweekday = None - elif not self._bynweekday: - self._bynweekday = None - - if self._byweekday is not None: - self._byweekday = tuple(sorted(self._byweekday)) - orig_byweekday = [weekday(x) for x in self._byweekday] - else: - orig_byweekday = () - - if self._bynweekday is not None: - self._bynweekday = tuple(sorted(self._bynweekday)) - orig_bynweekday = [weekday(*x) for x in self._bynweekday] - else: - orig_bynweekday = () - - if 'byweekday' not in self._original_rule: - self._original_rule['byweekday'] = tuple(itertools.chain( - orig_byweekday, orig_bynweekday)) - - # byhour - if byhour is None: - if freq < HOURLY: - self._byhour = {dtstart.hour} - else: - self._byhour = None - else: - if isinstance(byhour, integer_types): - byhour = (byhour,) - - if freq == HOURLY: - self._byhour = self.__construct_byset(start=dtstart.hour, - byxxx=byhour, - base=24) - else: - self._byhour = set(byhour) - - self._byhour = tuple(sorted(self._byhour)) - self._original_rule['byhour'] = self._byhour - - # byminute - if byminute is None: - if freq < MINUTELY: - self._byminute = {dtstart.minute} - else: - self._byminute = None - else: - if isinstance(byminute, integer_types): - byminute = (byminute,) - - if freq == MINUTELY: - self._byminute = self.__construct_byset(start=dtstart.minute, - byxxx=byminute, - base=60) - else: - self._byminute = set(byminute) - - self._byminute = tuple(sorted(self._byminute)) - self._original_rule['byminute'] = self._byminute - - # bysecond - if bysecond is None: - if freq < SECONDLY: - self._bysecond = ((dtstart.second,)) - else: - self._bysecond = None - else: - if isinstance(bysecond, integer_types): - bysecond = (bysecond,) - - self._bysecond = set(bysecond) - - if freq == SECONDLY: - self._bysecond = self.__construct_byset(start=dtstart.second, - byxxx=bysecond, - base=60) - else: - self._bysecond = set(bysecond) - - self._bysecond = tuple(sorted(self._bysecond)) - self._original_rule['bysecond'] = self._bysecond - - if self._freq >= HOURLY: - self._timeset = None - else: - self._timeset = [] - for hour in self._byhour: - for minute in self._byminute: - for second in self._bysecond: - self._timeset.append( - datetime.time(hour, minute, second, - tzinfo=self._tzinfo)) - self._timeset.sort() - self._timeset = tuple(self._timeset) - - def __str__(self): - """ - Output a string that would generate this RRULE if passed to rrulestr. - This is mostly compatible with RFC5545, except for the - dateutil-specific extension BYEASTER. - """ - - output = [] - h, m, s = [None] * 3 - if self._dtstart: - output.append(self._dtstart.strftime('DTSTART:%Y%m%dT%H%M%S')) - h, m, s = self._dtstart.timetuple()[3:6] - - parts = ['FREQ=' + FREQNAMES[self._freq]] - if self._interval != 1: - parts.append('INTERVAL=' + str(self._interval)) - - if self._wkst: - parts.append('WKST=' + repr(weekday(self._wkst))[0:2]) - - if self._count is not None: - parts.append('COUNT=' + str(self._count)) - - if self._until: - parts.append(self._until.strftime('UNTIL=%Y%m%dT%H%M%S')) - - if self._original_rule.get('byweekday') is not None: - # The str() method on weekday objects doesn't generate - # RFC5545-compliant strings, so we should modify that. - original_rule = dict(self._original_rule) - wday_strings = [] - for wday in original_rule['byweekday']: - if wday.n: - wday_strings.append('{n:+d}{wday}'.format( - n=wday.n, - wday=repr(wday)[0:2])) - else: - wday_strings.append(repr(wday)) - - original_rule['byweekday'] = wday_strings - else: - original_rule = self._original_rule - - partfmt = '{name}={vals}' - for name, key in [('BYSETPOS', 'bysetpos'), - ('BYMONTH', 'bymonth'), - ('BYMONTHDAY', 'bymonthday'), - ('BYYEARDAY', 'byyearday'), - ('BYWEEKNO', 'byweekno'), - ('BYDAY', 'byweekday'), - ('BYHOUR', 'byhour'), - ('BYMINUTE', 'byminute'), - ('BYSECOND', 'bysecond'), - ('BYEASTER', 'byeaster')]: - value = original_rule.get(key) - if value: - parts.append(partfmt.format(name=name, vals=(','.join(str(v) - for v in value)))) - - output.append('RRULE:' + ';'.join(parts)) - return '\n'.join(output) - - def replace(self, **kwargs): - """Return new rrule with same attributes except for those attributes given new - values by whichever keyword arguments are specified.""" - new_kwargs = {"interval": self._interval, - "count": self._count, - "dtstart": self._dtstart, - "freq": self._freq, - "until": self._until, - "wkst": self._wkst, - "cache": False if self._cache is None else True } - new_kwargs.update(self._original_rule) - new_kwargs.update(kwargs) - return rrule(**new_kwargs) - - def _iter(self): - year, month, day, hour, minute, second, weekday, yearday, _ = \ - self._dtstart.timetuple() - - # Some local variables to speed things up a bit - freq = self._freq - interval = self._interval - wkst = self._wkst - until = self._until - bymonth = self._bymonth - byweekno = self._byweekno - byyearday = self._byyearday - byweekday = self._byweekday - byeaster = self._byeaster - bymonthday = self._bymonthday - bynmonthday = self._bynmonthday - bysetpos = self._bysetpos - byhour = self._byhour - byminute = self._byminute - bysecond = self._bysecond - - ii = _iterinfo(self) - ii.rebuild(year, month) - - getdayset = {YEARLY: ii.ydayset, - MONTHLY: ii.mdayset, - WEEKLY: ii.wdayset, - DAILY: ii.ddayset, - HOURLY: ii.ddayset, - MINUTELY: ii.ddayset, - SECONDLY: ii.ddayset}[freq] - - if freq < HOURLY: - timeset = self._timeset - else: - gettimeset = {HOURLY: ii.htimeset, - MINUTELY: ii.mtimeset, - SECONDLY: ii.stimeset}[freq] - if ((freq >= HOURLY and - self._byhour and hour not in self._byhour) or - (freq >= MINUTELY and - self._byminute and minute not in self._byminute) or - (freq >= SECONDLY and - self._bysecond and second not in self._bysecond)): - timeset = () - else: - timeset = gettimeset(hour, minute, second) - - total = 0 - count = self._count - while True: - # Get dayset with the right frequency - dayset, start, end = getdayset(year, month, day) - - # Do the "hard" work ;-) - filtered = False - for i in dayset[start:end]: - if ((bymonth and ii.mmask[i] not in bymonth) or - (byweekno and not ii.wnomask[i]) or - (byweekday and ii.wdaymask[i] not in byweekday) or - (ii.nwdaymask and not ii.nwdaymask[i]) or - (byeaster and not ii.eastermask[i]) or - ((bymonthday or bynmonthday) and - ii.mdaymask[i] not in bymonthday and - ii.nmdaymask[i] not in bynmonthday) or - (byyearday and - ((i < ii.yearlen and i+1 not in byyearday and - -ii.yearlen+i not in byyearday) or - (i >= ii.yearlen and i+1-ii.yearlen not in byyearday and - -ii.nextyearlen+i-ii.yearlen not in byyearday)))): - dayset[i] = None - filtered = True - - # Output results - if bysetpos and timeset: - poslist = [] - for pos in bysetpos: - if pos < 0: - daypos, timepos = divmod(pos, len(timeset)) - else: - daypos, timepos = divmod(pos-1, len(timeset)) - try: - i = [x for x in dayset[start:end] - if x is not None][daypos] - time = timeset[timepos] - except IndexError: - pass - else: - date = datetime.date.fromordinal(ii.yearordinal+i) - res = datetime.datetime.combine(date, time) - if res not in poslist: - poslist.append(res) - poslist.sort() - for res in poslist: - if until and res > until: - self._len = total - return - elif res >= self._dtstart: - if count is not None: - count -= 1 - if count < 0: - self._len = total - return - total += 1 - yield res - else: - for i in dayset[start:end]: - if i is not None: - date = datetime.date.fromordinal(ii.yearordinal + i) - for time in timeset: - res = datetime.datetime.combine(date, time) - if until and res > until: - self._len = total - return - elif res >= self._dtstart: - if count is not None: - count -= 1 - if count < 0: - self._len = total - return - - total += 1 - yield res - - # Handle frequency and interval - fixday = False - if freq == YEARLY: - year += interval - if year > datetime.MAXYEAR: - self._len = total - return - ii.rebuild(year, month) - elif freq == MONTHLY: - month += interval - if month > 12: - div, mod = divmod(month, 12) - month = mod - year += div - if month == 0: - month = 12 - year -= 1 - if year > datetime.MAXYEAR: - self._len = total - return - ii.rebuild(year, month) - elif freq == WEEKLY: - if wkst > weekday: - day += -(weekday+1+(6-wkst))+self._interval*7 - else: - day += -(weekday-wkst)+self._interval*7 - weekday = wkst - fixday = True - elif freq == DAILY: - day += interval - fixday = True - elif freq == HOURLY: - if filtered: - # Jump to one iteration before next day - hour += ((23-hour)//interval)*interval - - if byhour: - ndays, hour = self.__mod_distance(value=hour, - byxxx=self._byhour, - base=24) - else: - ndays, hour = divmod(hour+interval, 24) - - if ndays: - day += ndays - fixday = True - - timeset = gettimeset(hour, minute, second) - elif freq == MINUTELY: - if filtered: - # Jump to one iteration before next day - minute += ((1439-(hour*60+minute))//interval)*interval - - valid = False - rep_rate = (24*60) - for j in range(rep_rate // gcd(interval, rep_rate)): - if byminute: - nhours, minute = \ - self.__mod_distance(value=minute, - byxxx=self._byminute, - base=60) - else: - nhours, minute = divmod(minute+interval, 60) - - div, hour = divmod(hour+nhours, 24) - if div: - day += div - fixday = True - filtered = False - - if not byhour or hour in byhour: - valid = True - break - - if not valid: - raise ValueError('Invalid combination of interval and ' + - 'byhour resulting in empty rule.') - - timeset = gettimeset(hour, minute, second) - elif freq == SECONDLY: - if filtered: - # Jump to one iteration before next day - second += (((86399 - (hour * 3600 + minute * 60 + second)) - // interval) * interval) - - rep_rate = (24 * 3600) - valid = False - for j in range(0, rep_rate // gcd(interval, rep_rate)): - if bysecond: - nminutes, second = \ - self.__mod_distance(value=second, - byxxx=self._bysecond, - base=60) - else: - nminutes, second = divmod(second+interval, 60) - - div, minute = divmod(minute+nminutes, 60) - if div: - hour += div - div, hour = divmod(hour, 24) - if div: - day += div - fixday = True - - if ((not byhour or hour in byhour) and - (not byminute or minute in byminute) and - (not bysecond or second in bysecond)): - valid = True - break - - if not valid: - raise ValueError('Invalid combination of interval, ' + - 'byhour and byminute resulting in empty' + - ' rule.') - - timeset = gettimeset(hour, minute, second) - - if fixday and day > 28: - daysinmonth = calendar.monthrange(year, month)[1] - if day > daysinmonth: - while day > daysinmonth: - day -= daysinmonth - month += 1 - if month == 13: - month = 1 - year += 1 - if year > datetime.MAXYEAR: - self._len = total - return - daysinmonth = calendar.monthrange(year, month)[1] - ii.rebuild(year, month) - - def __construct_byset(self, start, byxxx, base): - """ - If a `BYXXX` sequence is passed to the constructor at the same level as - `FREQ` (e.g. `FREQ=HOURLY,BYHOUR={2,4,7},INTERVAL=3`), there are some - specifications which cannot be reached given some starting conditions. - - This occurs whenever the interval is not coprime with the base of a - given unit and the difference between the starting position and the - ending position is not coprime with the greatest common denominator - between the interval and the base. For example, with a FREQ of hourly - starting at 17:00 and an interval of 4, the only valid values for - BYHOUR would be {21, 1, 5, 9, 13, 17}, because 4 and 24 are not - coprime. - - :param start: - Specifies the starting position. - :param byxxx: - An iterable containing the list of allowed values. - :param base: - The largest allowable value for the specified frequency (e.g. - 24 hours, 60 minutes). - - This does not preserve the type of the iterable, returning a set, since - the values should be unique and the order is irrelevant, this will - speed up later lookups. - - In the event of an empty set, raises a :exception:`ValueError`, as this - results in an empty rrule. - """ - - cset = set() - - # Support a single byxxx value. - if isinstance(byxxx, integer_types): - byxxx = (byxxx, ) - - for num in byxxx: - i_gcd = gcd(self._interval, base) - # Use divmod rather than % because we need to wrap negative nums. - if i_gcd == 1 or divmod(num - start, i_gcd)[1] == 0: - cset.add(num) - - if len(cset) == 0: - raise ValueError("Invalid rrule byxxx generates an empty set.") - - return cset - - def __mod_distance(self, value, byxxx, base): - """ - Calculates the next value in a sequence where the `FREQ` parameter is - specified along with a `BYXXX` parameter at the same "level" - (e.g. `HOURLY` specified with `BYHOUR`). - - :param value: - The old value of the component. - :param byxxx: - The `BYXXX` set, which should have been generated by - `rrule._construct_byset`, or something else which checks that a - valid rule is present. - :param base: - The largest allowable value for the specified frequency (e.g. - 24 hours, 60 minutes). - - If a valid value is not found after `base` iterations (the maximum - number before the sequence would start to repeat), this raises a - :exception:`ValueError`, as no valid values were found. - - This returns a tuple of `divmod(n*interval, base)`, where `n` is the - smallest number of `interval` repetitions until the next specified - value in `byxxx` is found. - """ - accumulator = 0 - for ii in range(1, base + 1): - # Using divmod() over % to account for negative intervals - div, value = divmod(value + self._interval, base) - accumulator += div - if value in byxxx: - return (accumulator, value) - - -class _iterinfo(object): - __slots__ = ["rrule", "lastyear", "lastmonth", - "yearlen", "nextyearlen", "yearordinal", "yearweekday", - "mmask", "mrange", "mdaymask", "nmdaymask", - "wdaymask", "wnomask", "nwdaymask", "eastermask"] - - def __init__(self, rrule): - for attr in self.__slots__: - setattr(self, attr, None) - self.rrule = rrule - - def rebuild(self, year, month): - # Every mask is 7 days longer to handle cross-year weekly periods. - rr = self.rrule - if year != self.lastyear: - self.yearlen = 365 + calendar.isleap(year) - self.nextyearlen = 365 + calendar.isleap(year + 1) - firstyday = datetime.date(year, 1, 1) - self.yearordinal = firstyday.toordinal() - self.yearweekday = firstyday.weekday() - - wday = datetime.date(year, 1, 1).weekday() - if self.yearlen == 365: - self.mmask = M365MASK - self.mdaymask = MDAY365MASK - self.nmdaymask = NMDAY365MASK - self.wdaymask = WDAYMASK[wday:] - self.mrange = M365RANGE - else: - self.mmask = M366MASK - self.mdaymask = MDAY366MASK - self.nmdaymask = NMDAY366MASK - self.wdaymask = WDAYMASK[wday:] - self.mrange = M366RANGE - - if not rr._byweekno: - self.wnomask = None - else: - self.wnomask = [0]*(self.yearlen+7) - # no1wkst = firstwkst = self.wdaymask.index(rr._wkst) - no1wkst = firstwkst = (7-self.yearweekday+rr._wkst) % 7 - if no1wkst >= 4: - no1wkst = 0 - # Number of days in the year, plus the days we got - # from last year. - wyearlen = self.yearlen+(self.yearweekday-rr._wkst) % 7 - else: - # Number of days in the year, minus the days we - # left in last year. - wyearlen = self.yearlen-no1wkst - div, mod = divmod(wyearlen, 7) - numweeks = div+mod//4 - for n in rr._byweekno: - if n < 0: - n += numweeks+1 - if not (0 < n <= numweeks): - continue - if n > 1: - i = no1wkst+(n-1)*7 - if no1wkst != firstwkst: - i -= 7-firstwkst - else: - i = no1wkst - for j in range(7): - self.wnomask[i] = 1 - i += 1 - if self.wdaymask[i] == rr._wkst: - break - if 1 in rr._byweekno: - # Check week number 1 of next year as well - # TODO: Check -numweeks for next year. - i = no1wkst+numweeks*7 - if no1wkst != firstwkst: - i -= 7-firstwkst - if i < self.yearlen: - # If week starts in next year, we - # don't care about it. - for j in range(7): - self.wnomask[i] = 1 - i += 1 - if self.wdaymask[i] == rr._wkst: - break - if no1wkst: - # Check last week number of last year as - # well. If no1wkst is 0, either the year - # started on week start, or week number 1 - # got days from last year, so there are no - # days from last year's last week number in - # this year. - if -1 not in rr._byweekno: - lyearweekday = datetime.date(year-1, 1, 1).weekday() - lno1wkst = (7-lyearweekday+rr._wkst) % 7 - lyearlen = 365+calendar.isleap(year-1) - if lno1wkst >= 4: - lno1wkst = 0 - lnumweeks = 52+(lyearlen + - (lyearweekday-rr._wkst) % 7) % 7//4 - else: - lnumweeks = 52+(self.yearlen-no1wkst) % 7//4 - else: - lnumweeks = -1 - if lnumweeks in rr._byweekno: - for i in range(no1wkst): - self.wnomask[i] = 1 - - if (rr._bynweekday and (month != self.lastmonth or - year != self.lastyear)): - ranges = [] - if rr._freq == YEARLY: - if rr._bymonth: - for month in rr._bymonth: - ranges.append(self.mrange[month-1:month+1]) - else: - ranges = [(0, self.yearlen)] - elif rr._freq == MONTHLY: - ranges = [self.mrange[month-1:month+1]] - if ranges: - # Weekly frequency won't get here, so we may not - # care about cross-year weekly periods. - self.nwdaymask = [0]*self.yearlen - for first, last in ranges: - last -= 1 - for wday, n in rr._bynweekday: - if n < 0: - i = last+(n+1)*7 - i -= (self.wdaymask[i]-wday) % 7 - else: - i = first+(n-1)*7 - i += (7-self.wdaymask[i]+wday) % 7 - if first <= i <= last: - self.nwdaymask[i] = 1 - - if rr._byeaster: - self.eastermask = [0]*(self.yearlen+7) - eyday = easter.easter(year).toordinal()-self.yearordinal - for offset in rr._byeaster: - self.eastermask[eyday+offset] = 1 - - self.lastyear = year - self.lastmonth = month - - def ydayset(self, year, month, day): - return list(range(self.yearlen)), 0, self.yearlen - - def mdayset(self, year, month, day): - dset = [None]*self.yearlen - start, end = self.mrange[month-1:month+1] - for i in range(start, end): - dset[i] = i - return dset, start, end - - def wdayset(self, year, month, day): - # We need to handle cross-year weeks here. - dset = [None]*(self.yearlen+7) - i = datetime.date(year, month, day).toordinal()-self.yearordinal - start = i - for j in range(7): - dset[i] = i - i += 1 - # if (not (0 <= i < self.yearlen) or - # self.wdaymask[i] == self.rrule._wkst): - # This will cross the year boundary, if necessary. - if self.wdaymask[i] == self.rrule._wkst: - break - return dset, start, i - - def ddayset(self, year, month, day): - dset = [None] * self.yearlen - i = datetime.date(year, month, day).toordinal() - self.yearordinal - dset[i] = i - return dset, i, i + 1 - - def htimeset(self, hour, minute, second): - tset = [] - rr = self.rrule - for minute in rr._byminute: - for second in rr._bysecond: - tset.append(datetime.time(hour, minute, second, - tzinfo=rr._tzinfo)) - tset.sort() - return tset - - def mtimeset(self, hour, minute, second): - tset = [] - rr = self.rrule - for second in rr._bysecond: - tset.append(datetime.time(hour, minute, second, tzinfo=rr._tzinfo)) - tset.sort() - return tset - - def stimeset(self, hour, minute, second): - return (datetime.time(hour, minute, second, - tzinfo=self.rrule._tzinfo),) - - -class rruleset(rrulebase): - """ The rruleset type allows more complex recurrence setups, mixing - multiple rules, dates, exclusion rules, and exclusion dates. The type - constructor takes the following keyword arguments: - - :param cache: If True, caching of results will be enabled, improving - performance of multiple queries considerably. """ - - class _genitem(object): - def __init__(self, genlist, gen): - try: - self.dt = advance_iterator(gen) - genlist.append(self) - except StopIteration: - pass - self.genlist = genlist - self.gen = gen - - def __next__(self): - try: - self.dt = advance_iterator(self.gen) - except StopIteration: - if self.genlist[0] is self: - heapq.heappop(self.genlist) - else: - self.genlist.remove(self) - heapq.heapify(self.genlist) - - next = __next__ - - def __lt__(self, other): - return self.dt < other.dt - - def __gt__(self, other): - return self.dt > other.dt - - def __eq__(self, other): - return self.dt == other.dt - - def __ne__(self, other): - return self.dt != other.dt - - def __init__(self, cache=False): - super(rruleset, self).__init__(cache) - self._rrule = [] - self._rdate = [] - self._exrule = [] - self._exdate = [] - - @_invalidates_cache - def rrule(self, rrule): - """ Include the given :py:class:`rrule` instance in the recurrence set - generation. """ - self._rrule.append(rrule) - - @_invalidates_cache - def rdate(self, rdate): - """ Include the given :py:class:`datetime` instance in the recurrence - set generation. """ - self._rdate.append(rdate) - - @_invalidates_cache - def exrule(self, exrule): - """ Include the given rrule instance in the recurrence set exclusion - list. Dates which are part of the given recurrence rules will not - be generated, even if some inclusive rrule or rdate matches them. - """ - self._exrule.append(exrule) - - @_invalidates_cache - def exdate(self, exdate): - """ Include the given datetime instance in the recurrence set - exclusion list. Dates included that way will not be generated, - even if some inclusive rrule or rdate matches them. """ - self._exdate.append(exdate) - - def _iter(self): - rlist = [] - self._rdate.sort() - self._genitem(rlist, iter(self._rdate)) - for gen in [iter(x) for x in self._rrule]: - self._genitem(rlist, gen) - exlist = [] - self._exdate.sort() - self._genitem(exlist, iter(self._exdate)) - for gen in [iter(x) for x in self._exrule]: - self._genitem(exlist, gen) - lastdt = None - total = 0 - heapq.heapify(rlist) - heapq.heapify(exlist) - while rlist: - ritem = rlist[0] - if not lastdt or lastdt != ritem.dt: - while exlist and exlist[0] < ritem: - exitem = exlist[0] - advance_iterator(exitem) - if exlist and exlist[0] is exitem: - heapq.heapreplace(exlist, exitem) - if not exlist or ritem != exlist[0]: - total += 1 - yield ritem.dt - lastdt = ritem.dt - advance_iterator(ritem) - if rlist and rlist[0] is ritem: - heapq.heapreplace(rlist, ritem) - self._len = total - - - - -class _rrulestr(object): - """ Parses a string representation of a recurrence rule or set of - recurrence rules. - - :param s: - Required, a string defining one or more recurrence rules. - - :param dtstart: - If given, used as the default recurrence start if not specified in the - rule string. - - :param cache: - If set ``True`` caching of results will be enabled, improving - performance of multiple queries considerably. - - :param unfold: - If set ``True`` indicates that a rule string is split over more - than one line and should be joined before processing. - - :param forceset: - If set ``True`` forces a :class:`dateutil.rrule.rruleset` to - be returned. - - :param compatible: - If set ``True`` forces ``unfold`` and ``forceset`` to be ``True``. - - :param ignoretz: - If set ``True``, time zones in parsed strings are ignored and a naive - :class:`datetime.datetime` object is returned. - - :param tzids: - If given, a callable or mapping used to retrieve a - :class:`datetime.tzinfo` from a string representation. - Defaults to :func:`dateutil.tz.gettz`. - - :param tzinfos: - Additional time zone names / aliases which may be present in a string - representation. See :func:`dateutil.parser.parse` for more - information. - - :return: - Returns a :class:`dateutil.rrule.rruleset` or - :class:`dateutil.rrule.rrule` - """ - - _freq_map = {"YEARLY": YEARLY, - "MONTHLY": MONTHLY, - "WEEKLY": WEEKLY, - "DAILY": DAILY, - "HOURLY": HOURLY, - "MINUTELY": MINUTELY, - "SECONDLY": SECONDLY} - - _weekday_map = {"MO": 0, "TU": 1, "WE": 2, "TH": 3, - "FR": 4, "SA": 5, "SU": 6} - - def _handle_int(self, rrkwargs, name, value, **kwargs): - rrkwargs[name.lower()] = int(value) - - def _handle_int_list(self, rrkwargs, name, value, **kwargs): - rrkwargs[name.lower()] = [int(x) for x in value.split(',')] - - _handle_INTERVAL = _handle_int - _handle_COUNT = _handle_int - _handle_BYSETPOS = _handle_int_list - _handle_BYMONTH = _handle_int_list - _handle_BYMONTHDAY = _handle_int_list - _handle_BYYEARDAY = _handle_int_list - _handle_BYEASTER = _handle_int_list - _handle_BYWEEKNO = _handle_int_list - _handle_BYHOUR = _handle_int_list - _handle_BYMINUTE = _handle_int_list - _handle_BYSECOND = _handle_int_list - - def _handle_FREQ(self, rrkwargs, name, value, **kwargs): - rrkwargs["freq"] = self._freq_map[value] - - def _handle_UNTIL(self, rrkwargs, name, value, **kwargs): - global parser - if not parser: - from dateutil import parser - try: - rrkwargs["until"] = parser.parse(value, - ignoretz=kwargs.get("ignoretz"), - tzinfos=kwargs.get("tzinfos")) - except ValueError: - raise ValueError("invalid until date") - - def _handle_WKST(self, rrkwargs, name, value, **kwargs): - rrkwargs["wkst"] = self._weekday_map[value] - - def _handle_BYWEEKDAY(self, rrkwargs, name, value, **kwargs): - """ - Two ways to specify this: +1MO or MO(+1) - """ - l = [] - for wday in value.split(','): - if '(' in wday: - # If it's of the form TH(+1), etc. - splt = wday.split('(') - w = splt[0] - n = int(splt[1][:-1]) - elif len(wday): - # If it's of the form +1MO - for i in range(len(wday)): - if wday[i] not in '+-0123456789': - break - n = wday[:i] or None - w = wday[i:] - if n: - n = int(n) - else: - raise ValueError("Invalid (empty) BYDAY specification.") - - l.append(weekdays[self._weekday_map[w]](n)) - rrkwargs["byweekday"] = l - - _handle_BYDAY = _handle_BYWEEKDAY - - def _parse_rfc_rrule(self, line, - dtstart=None, - cache=False, - ignoretz=False, - tzinfos=None): - if line.find(':') != -1: - name, value = line.split(':') - if name != "RRULE": - raise ValueError("unknown parameter name") - else: - value = line - rrkwargs = {} - for pair in value.split(';'): - name, value = pair.split('=') - name = name.upper() - value = value.upper() - try: - getattr(self, "_handle_"+name)(rrkwargs, name, value, - ignoretz=ignoretz, - tzinfos=tzinfos) - except AttributeError: - raise ValueError("unknown parameter '%s'" % name) - except (KeyError, ValueError): - raise ValueError("invalid '%s': %s" % (name, value)) - return rrule(dtstart=dtstart, cache=cache, **rrkwargs) - - def _parse_date_value(self, date_value, parms, rule_tzids, - ignoretz, tzids, tzinfos): - global parser - if not parser: - from dateutil import parser - - datevals = [] - value_found = False - TZID = None - - for parm in parms: - if parm.startswith("TZID="): - try: - tzkey = rule_tzids[parm.split('TZID=')[-1]] - except KeyError: - continue - if tzids is None: - from . import tz - tzlookup = tz.gettz - elif callable(tzids): - tzlookup = tzids - else: - tzlookup = getattr(tzids, 'get', None) - if tzlookup is None: - msg = ('tzids must be a callable, mapping, or None, ' - 'not %s' % tzids) - raise ValueError(msg) - - TZID = tzlookup(tzkey) - continue - - # RFC 5445 3.8.2.4: The VALUE parameter is optional, but may be found - # only once. - if parm not in {"VALUE=DATE-TIME", "VALUE=DATE"}: - raise ValueError("unsupported parm: " + parm) - else: - if value_found: - msg = ("Duplicate value parameter found in: " + parm) - raise ValueError(msg) - value_found = True - - for datestr in date_value.split(','): - date = parser.parse(datestr, ignoretz=ignoretz, tzinfos=tzinfos) - if TZID is not None: - if date.tzinfo is None: - date = date.replace(tzinfo=TZID) - else: - raise ValueError('DTSTART/EXDATE specifies multiple timezone') - datevals.append(date) - - return datevals - - def _parse_rfc(self, s, - dtstart=None, - cache=False, - unfold=False, - forceset=False, - compatible=False, - ignoretz=False, - tzids=None, - tzinfos=None): - global parser - if compatible: - forceset = True - unfold = True - - TZID_NAMES = dict(map( - lambda x: (x.upper(), x), - re.findall('TZID=(?P[^:]+):', s) - )) - s = s.upper() - if not s.strip(): - raise ValueError("empty string") - if unfold: - lines = s.splitlines() - i = 0 - while i < len(lines): - line = lines[i].rstrip() - if not line: - del lines[i] - elif i > 0 and line[0] == " ": - lines[i-1] += line[1:] - del lines[i] - else: - i += 1 - else: - lines = s.split() - if (not forceset and len(lines) == 1 and (s.find(':') == -1 or - s.startswith('RRULE:'))): - return self._parse_rfc_rrule(lines[0], cache=cache, - dtstart=dtstart, ignoretz=ignoretz, - tzinfos=tzinfos) - else: - rrulevals = [] - rdatevals = [] - exrulevals = [] - exdatevals = [] - for line in lines: - if not line: - continue - if line.find(':') == -1: - name = "RRULE" - value = line - else: - name, value = line.split(':', 1) - parms = name.split(';') - if not parms: - raise ValueError("empty property name") - name = parms[0] - parms = parms[1:] - if name == "RRULE": - for parm in parms: - raise ValueError("unsupported RRULE parm: "+parm) - rrulevals.append(value) - elif name == "RDATE": - for parm in parms: - if parm != "VALUE=DATE-TIME": - raise ValueError("unsupported RDATE parm: "+parm) - rdatevals.append(value) - elif name == "EXRULE": - for parm in parms: - raise ValueError("unsupported EXRULE parm: "+parm) - exrulevals.append(value) - elif name == "EXDATE": - exdatevals.extend( - self._parse_date_value(value, parms, - TZID_NAMES, ignoretz, - tzids, tzinfos) - ) - elif name == "DTSTART": - dtvals = self._parse_date_value(value, parms, TZID_NAMES, - ignoretz, tzids, tzinfos) - if len(dtvals) != 1: - raise ValueError("Multiple DTSTART values specified:" + - value) - dtstart = dtvals[0] - else: - raise ValueError("unsupported property: "+name) - if (forceset or len(rrulevals) > 1 or rdatevals - or exrulevals or exdatevals): - if not parser and (rdatevals or exdatevals): - from dateutil import parser - rset = rruleset(cache=cache) - for value in rrulevals: - rset.rrule(self._parse_rfc_rrule(value, dtstart=dtstart, - ignoretz=ignoretz, - tzinfos=tzinfos)) - for value in rdatevals: - for datestr in value.split(','): - rset.rdate(parser.parse(datestr, - ignoretz=ignoretz, - tzinfos=tzinfos)) - for value in exrulevals: - rset.exrule(self._parse_rfc_rrule(value, dtstart=dtstart, - ignoretz=ignoretz, - tzinfos=tzinfos)) - for value in exdatevals: - rset.exdate(value) - if compatible and dtstart: - rset.rdate(dtstart) - return rset - else: - return self._parse_rfc_rrule(rrulevals[0], - dtstart=dtstart, - cache=cache, - ignoretz=ignoretz, - tzinfos=tzinfos) - - def __call__(self, s, **kwargs): - return self._parse_rfc(s, **kwargs) - - -rrulestr = _rrulestr() - -# vim:ts=4:sw=4:et diff --git a/apps/bitwarden_event_logs/lib/dateutil/tz/__init__.py b/apps/bitwarden_event_logs/lib/dateutil/tz/__init__.py deleted file mode 100755 index af1352c4..00000000 --- a/apps/bitwarden_event_logs/lib/dateutil/tz/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -from .tz import * -from .tz import __doc__ - -__all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange", - "tzstr", "tzical", "tzwin", "tzwinlocal", "gettz", - "enfold", "datetime_ambiguous", "datetime_exists", - "resolve_imaginary", "UTC", "DeprecatedTzFormatWarning"] - - -class DeprecatedTzFormatWarning(Warning): - """Warning raised when time zones are parsed from deprecated formats.""" diff --git a/apps/bitwarden_event_logs/lib/dateutil/tz/__pycache__/__init__.cpython-39.pyc b/apps/bitwarden_event_logs/lib/dateutil/tz/__pycache__/__init__.cpython-39.pyc deleted file mode 100755 index a9c558fd5b568390abeed93e3c3991a7ca043b2c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 655 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 DB#3eZ diff --git a/apps/bitwarden_event_logs/lib/dateutil/tz/__pycache__/win.cpython-39.pyc b/apps/bitwarden_event_logs/lib/dateutil/tz/__pycache__/win.cpython-39.pyc deleted file mode 100755 index 6f6f9c2df3cb0138e34335797f694a32f321ea15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11393 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 diff --git a/apps/bitwarden_event_logs/lib/dateutil/tz/_common.py b/apps/bitwarden_event_logs/lib/dateutil/tz/_common.py deleted file mode 100755 index e6ac1183..00000000 --- a/apps/bitwarden_event_logs/lib/dateutil/tz/_common.py +++ /dev/null @@ -1,419 +0,0 @@ -from six import PY2 - -from functools import wraps - -from datetime import datetime, timedelta, tzinfo - - -ZERO = timedelta(0) - -__all__ = ['tzname_in_python2', 'enfold'] - - -def tzname_in_python2(namefunc): - """Change unicode output into bytestrings in Python 2 - - tzname() API changed in Python 3. It used to return bytes, but was changed - to unicode strings - """ - if PY2: - @wraps(namefunc) - def adjust_encoding(*args, **kwargs): - name = namefunc(*args, **kwargs) - if name is not None: - name = name.encode() - - return name - - return adjust_encoding - else: - return namefunc - - -# The following is adapted from Alexander Belopolsky's tz library -# https://github.com/abalkin/tz -if hasattr(datetime, 'fold'): - # This is the pre-python 3.6 fold situation - def enfold(dt, fold=1): - """ - Provides a unified interface for assigning the ``fold`` attribute to - datetimes both before and after the implementation of PEP-495. - - :param fold: - The value for the ``fold`` attribute in the returned datetime. This - should be either 0 or 1. - - :return: - Returns an object for which ``getattr(dt, 'fold', 0)`` returns - ``fold`` for all versions of Python. In versions prior to - Python 3.6, this is a ``_DatetimeWithFold`` object, which is a - subclass of :py:class:`datetime.datetime` with the ``fold`` - attribute added, if ``fold`` is 1. - - .. versionadded:: 2.6.0 - """ - return dt.replace(fold=fold) - -else: - class _DatetimeWithFold(datetime): - """ - This is a class designed to provide a PEP 495-compliant interface for - Python versions before 3.6. It is used only for dates in a fold, so - the ``fold`` attribute is fixed at ``1``. - - .. versionadded:: 2.6.0 - """ - __slots__ = () - - def replace(self, *args, **kwargs): - """ - Return a datetime with the same attributes, except for those - attributes given new values by whichever keyword arguments are - specified. Note that tzinfo=None can be specified to create a naive - datetime from an aware datetime with no conversion of date and time - data. - - This is reimplemented in ``_DatetimeWithFold`` because pypy3 will - return a ``datetime.datetime`` even if ``fold`` is unchanged. - """ - argnames = ( - 'year', 'month', 'day', 'hour', 'minute', 'second', - 'microsecond', 'tzinfo' - ) - - for arg, argname in zip(args, argnames): - if argname in kwargs: - raise TypeError('Duplicate argument: {}'.format(argname)) - - kwargs[argname] = arg - - for argname in argnames: - if argname not in kwargs: - kwargs[argname] = getattr(self, argname) - - dt_class = self.__class__ if kwargs.get('fold', 1) else datetime - - return dt_class(**kwargs) - - @property - def fold(self): - return 1 - - def enfold(dt, fold=1): - """ - Provides a unified interface for assigning the ``fold`` attribute to - datetimes both before and after the implementation of PEP-495. - - :param fold: - The value for the ``fold`` attribute in the returned datetime. This - should be either 0 or 1. - - :return: - Returns an object for which ``getattr(dt, 'fold', 0)`` returns - ``fold`` for all versions of Python. In versions prior to - Python 3.6, this is a ``_DatetimeWithFold`` object, which is a - subclass of :py:class:`datetime.datetime` with the ``fold`` - attribute added, if ``fold`` is 1. - - .. versionadded:: 2.6.0 - """ - if getattr(dt, 'fold', 0) == fold: - return dt - - args = dt.timetuple()[:6] - args += (dt.microsecond, dt.tzinfo) - - if fold: - return _DatetimeWithFold(*args) - else: - return datetime(*args) - - -def _validate_fromutc_inputs(f): - """ - The CPython version of ``fromutc`` checks that the input is a ``datetime`` - object and that ``self`` is attached as its ``tzinfo``. - """ - @wraps(f) - def fromutc(self, dt): - if not isinstance(dt, datetime): - raise TypeError("fromutc() requires a datetime argument") - if dt.tzinfo is not self: - raise ValueError("dt.tzinfo is not self") - - return f(self, dt) - - return fromutc - - -class _tzinfo(tzinfo): - """ - Base class for all ``dateutil`` ``tzinfo`` objects. - """ - - def is_ambiguous(self, dt): - """ - Whether or not the "wall time" of a given datetime is ambiguous in this - zone. - - :param dt: - A :py:class:`datetime.datetime`, naive or time zone aware. - - - :return: - Returns ``True`` if ambiguous, ``False`` otherwise. - - .. versionadded:: 2.6.0 - """ - - dt = dt.replace(tzinfo=self) - - wall_0 = enfold(dt, fold=0) - wall_1 = enfold(dt, fold=1) - - same_offset = wall_0.utcoffset() == wall_1.utcoffset() - same_dt = wall_0.replace(tzinfo=None) == wall_1.replace(tzinfo=None) - - return same_dt and not same_offset - - def _fold_status(self, dt_utc, dt_wall): - """ - Determine the fold status of a "wall" datetime, given a representation - of the same datetime as a (naive) UTC datetime. This is calculated based - on the assumption that ``dt.utcoffset() - dt.dst()`` is constant for all - datetimes, and that this offset is the actual number of hours separating - ``dt_utc`` and ``dt_wall``. - - :param dt_utc: - Representation of the datetime as UTC - - :param dt_wall: - Representation of the datetime as "wall time". This parameter must - either have a `fold` attribute or have a fold-naive - :class:`datetime.tzinfo` attached, otherwise the calculation may - fail. - """ - if self.is_ambiguous(dt_wall): - delta_wall = dt_wall - dt_utc - _fold = int(delta_wall == (dt_utc.utcoffset() - dt_utc.dst())) - else: - _fold = 0 - - return _fold - - def _fold(self, dt): - return getattr(dt, 'fold', 0) - - def _fromutc(self, dt): - """ - Given a timezone-aware datetime in a given timezone, calculates a - timezone-aware datetime in a new timezone. - - Since this is the one time that we *know* we have an unambiguous - datetime object, we take this opportunity to determine whether the - datetime is ambiguous and in a "fold" state (e.g. if it's the first - occurrence, chronologically, of the ambiguous datetime). - - :param dt: - A timezone-aware :class:`datetime.datetime` object. - """ - - # Re-implement the algorithm from Python's datetime.py - dtoff = dt.utcoffset() - if dtoff is None: - raise ValueError("fromutc() requires a non-None utcoffset() " - "result") - - # The original datetime.py code assumes that `dst()` defaults to - # zero during ambiguous times. PEP 495 inverts this presumption, so - # for pre-PEP 495 versions of python, we need to tweak the algorithm. - dtdst = dt.dst() - if dtdst is None: - raise ValueError("fromutc() requires a non-None dst() result") - delta = dtoff - dtdst - - dt += delta - # Set fold=1 so we can default to being in the fold for - # ambiguous dates. - dtdst = enfold(dt, fold=1).dst() - if dtdst is None: - raise ValueError("fromutc(): dt.dst gave inconsistent " - "results; cannot convert") - return dt + dtdst - - @_validate_fromutc_inputs - def fromutc(self, dt): - """ - Given a timezone-aware datetime in a given timezone, calculates a - timezone-aware datetime in a new timezone. - - Since this is the one time that we *know* we have an unambiguous - datetime object, we take this opportunity to determine whether the - datetime is ambiguous and in a "fold" state (e.g. if it's the first - occurrence, chronologically, of the ambiguous datetime). - - :param dt: - A timezone-aware :class:`datetime.datetime` object. - """ - dt_wall = self._fromutc(dt) - - # Calculate the fold status given the two datetimes. - _fold = self._fold_status(dt, dt_wall) - - # Set the default fold value for ambiguous dates - return enfold(dt_wall, fold=_fold) - - -class tzrangebase(_tzinfo): - """ - This is an abstract base class for time zones represented by an annual - transition into and out of DST. Child classes should implement the following - methods: - - * ``__init__(self, *args, **kwargs)`` - * ``transitions(self, year)`` - this is expected to return a tuple of - datetimes representing the DST on and off transitions in standard - time. - - A fully initialized ``tzrangebase`` subclass should also provide the - following attributes: - * ``hasdst``: Boolean whether or not the zone uses DST. - * ``_dst_offset`` / ``_std_offset``: :class:`datetime.timedelta` objects - representing the respective UTC offsets. - * ``_dst_abbr`` / ``_std_abbr``: Strings representing the timezone short - abbreviations in DST and STD, respectively. - * ``_hasdst``: Whether or not the zone has DST. - - .. versionadded:: 2.6.0 - """ - def __init__(self): - raise NotImplementedError('tzrangebase is an abstract base class') - - def utcoffset(self, dt): - isdst = self._isdst(dt) - - if isdst is None: - return None - elif isdst: - return self._dst_offset - else: - return self._std_offset - - def dst(self, dt): - isdst = self._isdst(dt) - - if isdst is None: - return None - elif isdst: - return self._dst_base_offset - else: - return ZERO - - @tzname_in_python2 - def tzname(self, dt): - if self._isdst(dt): - return self._dst_abbr - else: - return self._std_abbr - - def fromutc(self, dt): - """ Given a datetime in UTC, return local time """ - if not isinstance(dt, datetime): - raise TypeError("fromutc() requires a datetime argument") - - if dt.tzinfo is not self: - raise ValueError("dt.tzinfo is not self") - - # Get transitions - if there are none, fixed offset - transitions = self.transitions(dt.year) - if transitions is None: - return dt + self.utcoffset(dt) - - # Get the transition times in UTC - dston, dstoff = transitions - - dston -= self._std_offset - dstoff -= self._std_offset - - utc_transitions = (dston, dstoff) - dt_utc = dt.replace(tzinfo=None) - - isdst = self._naive_isdst(dt_utc, utc_transitions) - - if isdst: - dt_wall = dt + self._dst_offset - else: - dt_wall = dt + self._std_offset - - _fold = int(not isdst and self.is_ambiguous(dt_wall)) - - return enfold(dt_wall, fold=_fold) - - def is_ambiguous(self, dt): - """ - Whether or not the "wall time" of a given datetime is ambiguous in this - zone. - - :param dt: - A :py:class:`datetime.datetime`, naive or time zone aware. - - - :return: - Returns ``True`` if ambiguous, ``False`` otherwise. - - .. versionadded:: 2.6.0 - """ - if not self.hasdst: - return False - - start, end = self.transitions(dt.year) - - dt = dt.replace(tzinfo=None) - return (end <= dt < end + self._dst_base_offset) - - def _isdst(self, dt): - if not self.hasdst: - return False - elif dt is None: - return None - - transitions = self.transitions(dt.year) - - if transitions is None: - return False - - dt = dt.replace(tzinfo=None) - - isdst = self._naive_isdst(dt, transitions) - - # Handle ambiguous dates - if not isdst and self.is_ambiguous(dt): - return not self._fold(dt) - else: - return isdst - - def _naive_isdst(self, dt, transitions): - dston, dstoff = transitions - - dt = dt.replace(tzinfo=None) - - if dston < dstoff: - isdst = dston <= dt < dstoff - else: - isdst = not dstoff <= dt < dston - - return isdst - - @property - def _dst_base_offset(self): - return self._dst_offset - self._std_offset - - __hash__ = None - - def __ne__(self, other): - return not (self == other) - - def __repr__(self): - return "%s(...)" % self.__class__.__name__ - - __reduce__ = object.__reduce__ diff --git a/apps/bitwarden_event_logs/lib/dateutil/tz/_factories.py b/apps/bitwarden_event_logs/lib/dateutil/tz/_factories.py deleted file mode 100755 index f8a65891..00000000 --- a/apps/bitwarden_event_logs/lib/dateutil/tz/_factories.py +++ /dev/null @@ -1,80 +0,0 @@ -from datetime import timedelta -import weakref -from collections import OrderedDict - -from six.moves import _thread - - -class _TzSingleton(type): - def __init__(cls, *args, **kwargs): - cls.__instance = None - super(_TzSingleton, cls).__init__(*args, **kwargs) - - def __call__(cls): - if cls.__instance is None: - cls.__instance = super(_TzSingleton, cls).__call__() - return cls.__instance - - -class _TzFactory(type): - def instance(cls, *args, **kwargs): - """Alternate constructor that returns a fresh instance""" - return type.__call__(cls, *args, **kwargs) - - -class _TzOffsetFactory(_TzFactory): - def __init__(cls, *args, **kwargs): - cls.__instances = weakref.WeakValueDictionary() - cls.__strong_cache = OrderedDict() - cls.__strong_cache_size = 8 - - cls._cache_lock = _thread.allocate_lock() - - def __call__(cls, name, offset): - if isinstance(offset, timedelta): - key = (name, offset.total_seconds()) - else: - key = (name, offset) - - instance = cls.__instances.get(key, None) - if instance is None: - instance = cls.__instances.setdefault(key, - cls.instance(name, offset)) - - # This lock may not be necessary in Python 3. See GH issue #901 - with cls._cache_lock: - cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance) - - # Remove an item if the strong cache is overpopulated - if len(cls.__strong_cache) > cls.__strong_cache_size: - cls.__strong_cache.popitem(last=False) - - return instance - - -class _TzStrFactory(_TzFactory): - def __init__(cls, *args, **kwargs): - cls.__instances = weakref.WeakValueDictionary() - cls.__strong_cache = OrderedDict() - cls.__strong_cache_size = 8 - - cls.__cache_lock = _thread.allocate_lock() - - def __call__(cls, s, posix_offset=False): - key = (s, posix_offset) - instance = cls.__instances.get(key, None) - - if instance is None: - instance = cls.__instances.setdefault(key, - cls.instance(s, posix_offset)) - - # This lock may not be necessary in Python 3. See GH issue #901 - with cls.__cache_lock: - cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance) - - # Remove an item if the strong cache is overpopulated - if len(cls.__strong_cache) > cls.__strong_cache_size: - cls.__strong_cache.popitem(last=False) - - return instance - diff --git a/apps/bitwarden_event_logs/lib/dateutil/tz/tz.py b/apps/bitwarden_event_logs/lib/dateutil/tz/tz.py deleted file mode 100755 index 61759144..00000000 --- a/apps/bitwarden_event_logs/lib/dateutil/tz/tz.py +++ /dev/null @@ -1,1849 +0,0 @@ -# -*- coding: utf-8 -*- -""" -This module offers timezone implementations subclassing the abstract -:py:class:`datetime.tzinfo` type. There are classes to handle tzfile format -files (usually are in :file:`/etc/localtime`, :file:`/usr/share/zoneinfo`, -etc), TZ environment string (in all known formats), given ranges (with help -from relative deltas), local machine timezone, fixed offset timezone, and UTC -timezone. -""" -import datetime -import struct -import time -import sys -import os -import bisect -import weakref -from collections import OrderedDict - -import six -from six import string_types -from six.moves import _thread -from ._common import tzname_in_python2, _tzinfo -from ._common import tzrangebase, enfold -from ._common import _validate_fromutc_inputs - -from ._factories import _TzSingleton, _TzOffsetFactory -from ._factories import _TzStrFactory -try: - from .win import tzwin, tzwinlocal -except ImportError: - tzwin = tzwinlocal = None - -# For warning about rounding tzinfo -from warnings import warn - -ZERO = datetime.timedelta(0) -EPOCH = datetime.datetime(1970, 1, 1, 0, 0) -EPOCHORDINAL = EPOCH.toordinal() - - -@six.add_metaclass(_TzSingleton) -class tzutc(datetime.tzinfo): - """ - This is a tzinfo object that represents the UTC time zone. - - **Examples:** - - .. doctest:: - - >>> from datetime import * - >>> from dateutil.tz import * - - >>> datetime.now() - datetime.datetime(2003, 9, 27, 9, 40, 1, 521290) - - >>> datetime.now(tzutc()) - datetime.datetime(2003, 9, 27, 12, 40, 12, 156379, tzinfo=tzutc()) - - >>> datetime.now(tzutc()).tzname() - 'UTC' - - .. versionchanged:: 2.7.0 - ``tzutc()`` is now a singleton, so the result of ``tzutc()`` will - always return the same object. - - .. doctest:: - - >>> from dateutil.tz import tzutc, UTC - >>> tzutc() is tzutc() - True - >>> tzutc() is UTC - True - """ - def utcoffset(self, dt): - return ZERO - - def dst(self, dt): - return ZERO - - @tzname_in_python2 - def tzname(self, dt): - return "UTC" - - def is_ambiguous(self, dt): - """ - Whether or not the "wall time" of a given datetime is ambiguous in this - zone. - - :param dt: - A :py:class:`datetime.datetime`, naive or time zone aware. - - - :return: - Returns ``True`` if ambiguous, ``False`` otherwise. - - .. versionadded:: 2.6.0 - """ - return False - - @_validate_fromutc_inputs - def fromutc(self, dt): - """ - Fast track version of fromutc() returns the original ``dt`` object for - any valid :py:class:`datetime.datetime` object. - """ - return dt - - def __eq__(self, other): - if not isinstance(other, (tzutc, tzoffset)): - return NotImplemented - - return (isinstance(other, tzutc) or - (isinstance(other, tzoffset) and other._offset == ZERO)) - - __hash__ = None - - def __ne__(self, other): - return not (self == other) - - def __repr__(self): - return "%s()" % self.__class__.__name__ - - __reduce__ = object.__reduce__ - - -#: Convenience constant providing a :class:`tzutc()` instance -#: -#: .. versionadded:: 2.7.0 -UTC = tzutc() - - -@six.add_metaclass(_TzOffsetFactory) -class tzoffset(datetime.tzinfo): - """ - A simple class for representing a fixed offset from UTC. - - :param name: - The timezone name, to be returned when ``tzname()`` is called. - :param offset: - The time zone offset in seconds, or (since version 2.6.0, represented - as a :py:class:`datetime.timedelta` object). - """ - def __init__(self, name, offset): - self._name = name - - try: - # Allow a timedelta - offset = offset.total_seconds() - except (TypeError, AttributeError): - pass - - self._offset = datetime.timedelta(seconds=_get_supported_offset(offset)) - - def utcoffset(self, dt): - return self._offset - - def dst(self, dt): - return ZERO - - @tzname_in_python2 - def tzname(self, dt): - return self._name - - @_validate_fromutc_inputs - def fromutc(self, dt): - return dt + self._offset - - def is_ambiguous(self, dt): - """ - Whether or not the "wall time" of a given datetime is ambiguous in this - zone. - - :param dt: - A :py:class:`datetime.datetime`, naive or time zone aware. - :return: - Returns ``True`` if ambiguous, ``False`` otherwise. - - .. versionadded:: 2.6.0 - """ - return False - - def __eq__(self, other): - if not isinstance(other, tzoffset): - return NotImplemented - - return self._offset == other._offset - - __hash__ = None - - def __ne__(self, other): - return not (self == other) - - def __repr__(self): - return "%s(%s, %s)" % (self.__class__.__name__, - repr(self._name), - int(self._offset.total_seconds())) - - __reduce__ = object.__reduce__ - - -class tzlocal(_tzinfo): - """ - A :class:`tzinfo` subclass built around the ``time`` timezone functions. - """ - def __init__(self): - super(tzlocal, self).__init__() - - self._std_offset = datetime.timedelta(seconds=-time.timezone) - if time.daylight: - self._dst_offset = datetime.timedelta(seconds=-time.altzone) - else: - self._dst_offset = self._std_offset - - self._dst_saved = self._dst_offset - self._std_offset - self._hasdst = bool(self._dst_saved) - self._tznames = tuple(time.tzname) - - def utcoffset(self, dt): - if dt is None and self._hasdst: - return None - - if self._isdst(dt): - return self._dst_offset - else: - return self._std_offset - - def dst(self, dt): - if dt is None and self._hasdst: - return None - - if self._isdst(dt): - return self._dst_offset - self._std_offset - else: - return ZERO - - @tzname_in_python2 - def tzname(self, dt): - return self._tznames[self._isdst(dt)] - - def is_ambiguous(self, dt): - """ - Whether or not the "wall time" of a given datetime is ambiguous in this - zone. - - :param dt: - A :py:class:`datetime.datetime`, naive or time zone aware. - - - :return: - Returns ``True`` if ambiguous, ``False`` otherwise. - - .. versionadded:: 2.6.0 - """ - naive_dst = self._naive_is_dst(dt) - return (not naive_dst and - (naive_dst != self._naive_is_dst(dt - self._dst_saved))) - - def _naive_is_dst(self, dt): - timestamp = _datetime_to_timestamp(dt) - return time.localtime(timestamp + time.timezone).tm_isdst - - def _isdst(self, dt, fold_naive=True): - # We can't use mktime here. It is unstable when deciding if - # the hour near to a change is DST or not. - # - # timestamp = time.mktime((dt.year, dt.month, dt.day, dt.hour, - # dt.minute, dt.second, dt.weekday(), 0, -1)) - # return time.localtime(timestamp).tm_isdst - # - # The code above yields the following result: - # - # >>> import tz, datetime - # >>> t = tz.tzlocal() - # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() - # 'BRDT' - # >>> datetime.datetime(2003,2,16,0,tzinfo=t).tzname() - # 'BRST' - # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() - # 'BRST' - # >>> datetime.datetime(2003,2,15,22,tzinfo=t).tzname() - # 'BRDT' - # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() - # 'BRDT' - # - # Here is a more stable implementation: - # - if not self._hasdst: - return False - - # Check for ambiguous times: - dstval = self._naive_is_dst(dt) - fold = getattr(dt, 'fold', None) - - if self.is_ambiguous(dt): - if fold is not None: - return not self._fold(dt) - else: - return True - - return dstval - - def __eq__(self, other): - if isinstance(other, tzlocal): - return (self._std_offset == other._std_offset and - self._dst_offset == other._dst_offset) - elif isinstance(other, tzutc): - return (not self._hasdst and - self._tznames[0] in {'UTC', 'GMT'} and - self._std_offset == ZERO) - elif isinstance(other, tzoffset): - return (not self._hasdst and - self._tznames[0] == other._name and - self._std_offset == other._offset) - else: - return NotImplemented - - __hash__ = None - - def __ne__(self, other): - return not (self == other) - - def __repr__(self): - return "%s()" % self.__class__.__name__ - - __reduce__ = object.__reduce__ - - -class _ttinfo(object): - __slots__ = ["offset", "delta", "isdst", "abbr", - "isstd", "isgmt", "dstoffset"] - - def __init__(self): - for attr in self.__slots__: - setattr(self, attr, None) - - def __repr__(self): - l = [] - for attr in self.__slots__: - value = getattr(self, attr) - if value is not None: - l.append("%s=%s" % (attr, repr(value))) - return "%s(%s)" % (self.__class__.__name__, ", ".join(l)) - - def __eq__(self, other): - if not isinstance(other, _ttinfo): - return NotImplemented - - return (self.offset == other.offset and - self.delta == other.delta and - self.isdst == other.isdst and - self.abbr == other.abbr and - self.isstd == other.isstd and - self.isgmt == other.isgmt and - self.dstoffset == other.dstoffset) - - __hash__ = None - - def __ne__(self, other): - return not (self == other) - - def __getstate__(self): - state = {} - for name in self.__slots__: - state[name] = getattr(self, name, None) - return state - - def __setstate__(self, state): - for name in self.__slots__: - if name in state: - setattr(self, name, state[name]) - - -class _tzfile(object): - """ - Lightweight class for holding the relevant transition and time zone - information read from binary tzfiles. - """ - attrs = ['trans_list', 'trans_list_utc', 'trans_idx', 'ttinfo_list', - 'ttinfo_std', 'ttinfo_dst', 'ttinfo_before', 'ttinfo_first'] - - def __init__(self, **kwargs): - for attr in self.attrs: - setattr(self, attr, kwargs.get(attr, None)) - - -class tzfile(_tzinfo): - """ - This is a ``tzinfo`` subclass that allows one to use the ``tzfile(5)`` - format timezone files to extract current and historical zone information. - - :param fileobj: - This can be an opened file stream or a file name that the time zone - information can be read from. - - :param filename: - This is an optional parameter specifying the source of the time zone - information in the event that ``fileobj`` is a file object. If omitted - and ``fileobj`` is a file stream, this parameter will be set either to - ``fileobj``'s ``name`` attribute or to ``repr(fileobj)``. - - See `Sources for Time Zone and Daylight Saving Time Data - `_ for more information. - Time zone files can be compiled from the `IANA Time Zone database files - `_ with the `zic time zone compiler - `_ - - .. note:: - - Only construct a ``tzfile`` directly if you have a specific timezone - file on disk that you want to read into a Python ``tzinfo`` object. - If you want to get a ``tzfile`` representing a specific IANA zone, - (e.g. ``'America/New_York'``), you should call - :func:`dateutil.tz.gettz` with the zone identifier. - - - **Examples:** - - Using the US Eastern time zone as an example, we can see that a ``tzfile`` - provides time zone information for the standard Daylight Saving offsets: - - .. testsetup:: tzfile - - from dateutil.tz import gettz - from datetime import datetime - - .. doctest:: tzfile - - >>> NYC = gettz('America/New_York') - >>> NYC - tzfile('/usr/share/zoneinfo/America/New_York') - - >>> print(datetime(2016, 1, 3, tzinfo=NYC)) # EST - 2016-01-03 00:00:00-05:00 - - >>> print(datetime(2016, 7, 7, tzinfo=NYC)) # EDT - 2016-07-07 00:00:00-04:00 - - - The ``tzfile`` structure contains a fully history of the time zone, - so historical dates will also have the right offsets. For example, before - the adoption of the UTC standards, New York used local solar mean time: - - .. doctest:: tzfile - - >>> print(datetime(1901, 4, 12, tzinfo=NYC)) # LMT - 1901-04-12 00:00:00-04:56 - - And during World War II, New York was on "Eastern War Time", which was a - state of permanent daylight saving time: - - .. doctest:: tzfile - - >>> print(datetime(1944, 2, 7, tzinfo=NYC)) # EWT - 1944-02-07 00:00:00-04:00 - - """ - - def __init__(self, fileobj, filename=None): - super(tzfile, self).__init__() - - file_opened_here = False - if isinstance(fileobj, string_types): - self._filename = fileobj - fileobj = open(fileobj, 'rb') - file_opened_here = True - elif filename is not None: - self._filename = filename - elif hasattr(fileobj, "name"): - self._filename = fileobj.name - else: - self._filename = repr(fileobj) - - if fileobj is not None: - if not file_opened_here: - fileobj = _nullcontext(fileobj) - - with fileobj as file_stream: - tzobj = self._read_tzfile(file_stream) - - self._set_tzdata(tzobj) - - def _set_tzdata(self, tzobj): - """ Set the time zone data of this object from a _tzfile object """ - # Copy the relevant attributes over as private attributes - for attr in _tzfile.attrs: - setattr(self, '_' + attr, getattr(tzobj, attr)) - - def _read_tzfile(self, fileobj): - out = _tzfile() - - # From tzfile(5): - # - # The time zone information files used by tzset(3) - # begin with the magic characters "TZif" to identify - # them as time zone information files, followed by - # sixteen bytes reserved for future use, followed by - # six four-byte values of type long, written in a - # ``standard'' byte order (the high-order byte - # of the value is written first). - if fileobj.read(4).decode() != "TZif": - raise ValueError("magic not found") - - fileobj.read(16) - - ( - # The number of UTC/local indicators stored in the file. - ttisgmtcnt, - - # The number of standard/wall indicators stored in the file. - ttisstdcnt, - - # The number of leap seconds for which data is - # stored in the file. - leapcnt, - - # The number of "transition times" for which data - # is stored in the file. - timecnt, - - # The number of "local time types" for which data - # is stored in the file (must not be zero). - typecnt, - - # The number of characters of "time zone - # abbreviation strings" stored in the file. - charcnt, - - ) = struct.unpack(">6l", fileobj.read(24)) - - # The above header is followed by tzh_timecnt four-byte - # values of type long, sorted in ascending order. - # These values are written in ``standard'' byte order. - # Each is used as a transition time (as returned by - # time(2)) at which the rules for computing local time - # change. - - if timecnt: - out.trans_list_utc = list(struct.unpack(">%dl" % timecnt, - fileobj.read(timecnt*4))) - else: - out.trans_list_utc = [] - - # Next come tzh_timecnt one-byte values of type unsigned - # char; each one tells which of the different types of - # ``local time'' types described in the file is associated - # with the same-indexed transition time. These values - # serve as indices into an array of ttinfo structures that - # appears next in the file. - - if timecnt: - out.trans_idx = struct.unpack(">%dB" % timecnt, - fileobj.read(timecnt)) - else: - out.trans_idx = [] - - # Each ttinfo structure is written as a four-byte value - # for tt_gmtoff of type long, in a standard byte - # order, followed by a one-byte value for tt_isdst - # and a one-byte value for tt_abbrind. In each - # structure, tt_gmtoff gives the number of - # seconds to be added to UTC, tt_isdst tells whether - # tm_isdst should be set by localtime(3), and - # tt_abbrind serves as an index into the array of - # time zone abbreviation characters that follow the - # ttinfo structure(s) in the file. - - ttinfo = [] - - for i in range(typecnt): - ttinfo.append(struct.unpack(">lbb", fileobj.read(6))) - - abbr = fileobj.read(charcnt).decode() - - # Then there are tzh_leapcnt pairs of four-byte - # values, written in standard byte order; the - # first value of each pair gives the time (as - # returned by time(2)) at which a leap second - # occurs; the second gives the total number of - # leap seconds to be applied after the given time. - # The pairs of values are sorted in ascending order - # by time. - - # Not used, for now (but seek for correct file position) - if leapcnt: - fileobj.seek(leapcnt * 8, os.SEEK_CUR) - - # Then there are tzh_ttisstdcnt standard/wall - # indicators, each stored as a one-byte value; - # they tell whether the transition times associated - # with local time types were specified as standard - # time or wall clock time, and are used when - # a time zone file is used in handling POSIX-style - # time zone environment variables. - - if ttisstdcnt: - isstd = struct.unpack(">%db" % ttisstdcnt, - fileobj.read(ttisstdcnt)) - - # Finally, there are tzh_ttisgmtcnt UTC/local - # indicators, each stored as a one-byte value; - # they tell whether the transition times associated - # with local time types were specified as UTC or - # local time, and are used when a time zone file - # is used in handling POSIX-style time zone envi- - # ronment variables. - - if ttisgmtcnt: - isgmt = struct.unpack(">%db" % ttisgmtcnt, - fileobj.read(ttisgmtcnt)) - - # Build ttinfo list - out.ttinfo_list = [] - for i in range(typecnt): - gmtoff, isdst, abbrind = ttinfo[i] - gmtoff = _get_supported_offset(gmtoff) - tti = _ttinfo() - tti.offset = gmtoff - tti.dstoffset = datetime.timedelta(0) - tti.delta = datetime.timedelta(seconds=gmtoff) - tti.isdst = isdst - tti.abbr = abbr[abbrind:abbr.find('\x00', abbrind)] - tti.isstd = (ttisstdcnt > i and isstd[i] != 0) - tti.isgmt = (ttisgmtcnt > i and isgmt[i] != 0) - out.ttinfo_list.append(tti) - - # Replace ttinfo indexes for ttinfo objects. - out.trans_idx = [out.ttinfo_list[idx] for idx in out.trans_idx] - - # Set standard, dst, and before ttinfos. before will be - # used when a given time is before any transitions, - # and will be set to the first non-dst ttinfo, or to - # the first dst, if all of them are dst. - out.ttinfo_std = None - out.ttinfo_dst = None - out.ttinfo_before = None - if out.ttinfo_list: - if not out.trans_list_utc: - out.ttinfo_std = out.ttinfo_first = out.ttinfo_list[0] - else: - for i in range(timecnt-1, -1, -1): - tti = out.trans_idx[i] - if not out.ttinfo_std and not tti.isdst: - out.ttinfo_std = tti - elif not out.ttinfo_dst and tti.isdst: - out.ttinfo_dst = tti - - if out.ttinfo_std and out.ttinfo_dst: - break - else: - if out.ttinfo_dst and not out.ttinfo_std: - out.ttinfo_std = out.ttinfo_dst - - for tti in out.ttinfo_list: - if not tti.isdst: - out.ttinfo_before = tti - break - else: - out.ttinfo_before = out.ttinfo_list[0] - - # Now fix transition times to become relative to wall time. - # - # I'm not sure about this. In my tests, the tz source file - # is setup to wall time, and in the binary file isstd and - # isgmt are off, so it should be in wall time. OTOH, it's - # always in gmt time. Let me know if you have comments - # about this. - lastdst = None - lastoffset = None - lastdstoffset = None - lastbaseoffset = None - out.trans_list = [] - - for i, tti in enumerate(out.trans_idx): - offset = tti.offset - dstoffset = 0 - - if lastdst is not None: - if tti.isdst: - if not lastdst: - dstoffset = offset - lastoffset - - if not dstoffset and lastdstoffset: - dstoffset = lastdstoffset - - tti.dstoffset = datetime.timedelta(seconds=dstoffset) - lastdstoffset = dstoffset - - # If a time zone changes its base offset during a DST transition, - # then you need to adjust by the previous base offset to get the - # transition time in local time. Otherwise you use the current - # base offset. Ideally, I would have some mathematical proof of - # why this is true, but I haven't really thought about it enough. - baseoffset = offset - dstoffset - adjustment = baseoffset - if (lastbaseoffset is not None and baseoffset != lastbaseoffset - and tti.isdst != lastdst): - # The base DST has changed - adjustment = lastbaseoffset - - lastdst = tti.isdst - lastoffset = offset - lastbaseoffset = baseoffset - - out.trans_list.append(out.trans_list_utc[i] + adjustment) - - out.trans_idx = tuple(out.trans_idx) - out.trans_list = tuple(out.trans_list) - out.trans_list_utc = tuple(out.trans_list_utc) - - return out - - def _find_last_transition(self, dt, in_utc=False): - # If there's no list, there are no transitions to find - if not self._trans_list: - return None - - timestamp = _datetime_to_timestamp(dt) - - # Find where the timestamp fits in the transition list - if the - # timestamp is a transition time, it's part of the "after" period. - trans_list = self._trans_list_utc if in_utc else self._trans_list - idx = bisect.bisect_right(trans_list, timestamp) - - # We want to know when the previous transition was, so subtract off 1 - return idx - 1 - - def _get_ttinfo(self, idx): - # For no list or after the last transition, default to _ttinfo_std - if idx is None or (idx + 1) >= len(self._trans_list): - return self._ttinfo_std - - # If there is a list and the time is before it, return _ttinfo_before - if idx < 0: - return self._ttinfo_before - - return self._trans_idx[idx] - - def _find_ttinfo(self, dt): - idx = self._resolve_ambiguous_time(dt) - - return self._get_ttinfo(idx) - - def fromutc(self, dt): - """ - The ``tzfile`` implementation of :py:func:`datetime.tzinfo.fromutc`. - - :param dt: - A :py:class:`datetime.datetime` object. - - :raises TypeError: - Raised if ``dt`` is not a :py:class:`datetime.datetime` object. - - :raises ValueError: - Raised if this is called with a ``dt`` which does not have this - ``tzinfo`` attached. - - :return: - Returns a :py:class:`datetime.datetime` object representing the - wall time in ``self``'s time zone. - """ - # These isinstance checks are in datetime.tzinfo, so we'll preserve - # them, even if we don't care about duck typing. - if not isinstance(dt, datetime.datetime): - raise TypeError("fromutc() requires a datetime argument") - - if dt.tzinfo is not self: - raise ValueError("dt.tzinfo is not self") - - # First treat UTC as wall time and get the transition we're in. - idx = self._find_last_transition(dt, in_utc=True) - tti = self._get_ttinfo(idx) - - dt_out = dt + datetime.timedelta(seconds=tti.offset) - - fold = self.is_ambiguous(dt_out, idx=idx) - - return enfold(dt_out, fold=int(fold)) - - def is_ambiguous(self, dt, idx=None): - """ - Whether or not the "wall time" of a given datetime is ambiguous in this - zone. - - :param dt: - A :py:class:`datetime.datetime`, naive or time zone aware. - - - :return: - Returns ``True`` if ambiguous, ``False`` otherwise. - - .. versionadded:: 2.6.0 - """ - if idx is None: - idx = self._find_last_transition(dt) - - # Calculate the difference in offsets from current to previous - timestamp = _datetime_to_timestamp(dt) - tti = self._get_ttinfo(idx) - - if idx is None or idx <= 0: - return False - - od = self._get_ttinfo(idx - 1).offset - tti.offset - tt = self._trans_list[idx] # Transition time - - return timestamp < tt + od - - def _resolve_ambiguous_time(self, dt): - idx = self._find_last_transition(dt) - - # If we have no transitions, return the index - _fold = self._fold(dt) - if idx is None or idx == 0: - return idx - - # If it's ambiguous and we're in a fold, shift to a different index. - idx_offset = int(not _fold and self.is_ambiguous(dt, idx)) - - return idx - idx_offset - - def utcoffset(self, dt): - if dt is None: - return None - - if not self._ttinfo_std: - return ZERO - - return self._find_ttinfo(dt).delta - - def dst(self, dt): - if dt is None: - return None - - if not self._ttinfo_dst: - return ZERO - - tti = self._find_ttinfo(dt) - - if not tti.isdst: - return ZERO - - # The documentation says that utcoffset()-dst() must - # be constant for every dt. - return tti.dstoffset - - @tzname_in_python2 - def tzname(self, dt): - if not self._ttinfo_std or dt is None: - return None - return self._find_ttinfo(dt).abbr - - def __eq__(self, other): - if not isinstance(other, tzfile): - return NotImplemented - return (self._trans_list == other._trans_list and - self._trans_idx == other._trans_idx and - self._ttinfo_list == other._ttinfo_list) - - __hash__ = None - - def __ne__(self, other): - return not (self == other) - - def __repr__(self): - return "%s(%s)" % (self.__class__.__name__, repr(self._filename)) - - def __reduce__(self): - return self.__reduce_ex__(None) - - def __reduce_ex__(self, protocol): - return (self.__class__, (None, self._filename), self.__dict__) - - -class tzrange(tzrangebase): - """ - The ``tzrange`` object is a time zone specified by a set of offsets and - abbreviations, equivalent to the way the ``TZ`` variable can be specified - in POSIX-like systems, but using Python delta objects to specify DST - start, end and offsets. - - :param stdabbr: - The abbreviation for standard time (e.g. ``'EST'``). - - :param stdoffset: - An integer or :class:`datetime.timedelta` object or equivalent - specifying the base offset from UTC. - - If unspecified, +00:00 is used. - - :param dstabbr: - The abbreviation for DST / "Summer" time (e.g. ``'EDT'``). - - If specified, with no other DST information, DST is assumed to occur - and the default behavior or ``dstoffset``, ``start`` and ``end`` is - used. If unspecified and no other DST information is specified, it - is assumed that this zone has no DST. - - If this is unspecified and other DST information is *is* specified, - DST occurs in the zone but the time zone abbreviation is left - unchanged. - - :param dstoffset: - A an integer or :class:`datetime.timedelta` object or equivalent - specifying the UTC offset during DST. If unspecified and any other DST - information is specified, it is assumed to be the STD offset +1 hour. - - :param start: - A :class:`relativedelta.relativedelta` object or equivalent specifying - the time and time of year that daylight savings time starts. To - specify, for example, that DST starts at 2AM on the 2nd Sunday in - March, pass: - - ``relativedelta(hours=2, month=3, day=1, weekday=SU(+2))`` - - If unspecified and any other DST information is specified, the default - value is 2 AM on the first Sunday in April. - - :param end: - A :class:`relativedelta.relativedelta` object or equivalent - representing the time and time of year that daylight savings time - ends, with the same specification method as in ``start``. One note is - that this should point to the first time in the *standard* zone, so if - a transition occurs at 2AM in the DST zone and the clocks are set back - 1 hour to 1AM, set the ``hours`` parameter to +1. - - - **Examples:** - - .. testsetup:: tzrange - - from dateutil.tz import tzrange, tzstr - - .. doctest:: tzrange - - >>> tzstr('EST5EDT') == tzrange("EST", -18000, "EDT") - True - - >>> from dateutil.relativedelta import * - >>> range1 = tzrange("EST", -18000, "EDT") - >>> range2 = tzrange("EST", -18000, "EDT", -14400, - ... relativedelta(hours=+2, month=4, day=1, - ... weekday=SU(+1)), - ... relativedelta(hours=+1, month=10, day=31, - ... weekday=SU(-1))) - >>> tzstr('EST5EDT') == range1 == range2 - True - - """ - def __init__(self, stdabbr, stdoffset=None, - dstabbr=None, dstoffset=None, - start=None, end=None): - - global relativedelta - from dateutil import relativedelta - - self._std_abbr = stdabbr - self._dst_abbr = dstabbr - - try: - stdoffset = stdoffset.total_seconds() - except (TypeError, AttributeError): - pass - - try: - dstoffset = dstoffset.total_seconds() - except (TypeError, AttributeError): - pass - - if stdoffset is not None: - self._std_offset = datetime.timedelta(seconds=stdoffset) - else: - self._std_offset = ZERO - - if dstoffset is not None: - self._dst_offset = datetime.timedelta(seconds=dstoffset) - elif dstabbr and stdoffset is not None: - self._dst_offset = self._std_offset + datetime.timedelta(hours=+1) - else: - self._dst_offset = ZERO - - if dstabbr and start is None: - self._start_delta = relativedelta.relativedelta( - hours=+2, month=4, day=1, weekday=relativedelta.SU(+1)) - else: - self._start_delta = start - - if dstabbr and end is None: - self._end_delta = relativedelta.relativedelta( - hours=+1, month=10, day=31, weekday=relativedelta.SU(-1)) - else: - self._end_delta = end - - self._dst_base_offset_ = self._dst_offset - self._std_offset - self.hasdst = bool(self._start_delta) - - def transitions(self, year): - """ - For a given year, get the DST on and off transition times, expressed - always on the standard time side. For zones with no transitions, this - function returns ``None``. - - :param year: - The year whose transitions you would like to query. - - :return: - Returns a :class:`tuple` of :class:`datetime.datetime` objects, - ``(dston, dstoff)`` for zones with an annual DST transition, or - ``None`` for fixed offset zones. - """ - if not self.hasdst: - return None - - base_year = datetime.datetime(year, 1, 1) - - start = base_year + self._start_delta - end = base_year + self._end_delta - - return (start, end) - - def __eq__(self, other): - if not isinstance(other, tzrange): - return NotImplemented - - return (self._std_abbr == other._std_abbr and - self._dst_abbr == other._dst_abbr and - self._std_offset == other._std_offset and - self._dst_offset == other._dst_offset and - self._start_delta == other._start_delta and - self._end_delta == other._end_delta) - - @property - def _dst_base_offset(self): - return self._dst_base_offset_ - - -@six.add_metaclass(_TzStrFactory) -class tzstr(tzrange): - """ - ``tzstr`` objects are time zone objects specified by a time-zone string as - it would be passed to a ``TZ`` variable on POSIX-style systems (see - the `GNU C Library: TZ Variable`_ for more details). - - There is one notable exception, which is that POSIX-style time zones use an - inverted offset format, so normally ``GMT+3`` would be parsed as an offset - 3 hours *behind* GMT. The ``tzstr`` time zone object will parse this as an - offset 3 hours *ahead* of GMT. If you would like to maintain the POSIX - behavior, pass a ``True`` value to ``posix_offset``. - - The :class:`tzrange` object provides the same functionality, but is - specified using :class:`relativedelta.relativedelta` objects. rather than - strings. - - :param s: - A time zone string in ``TZ`` variable format. This can be a - :class:`bytes` (2.x: :class:`str`), :class:`str` (2.x: - :class:`unicode`) or a stream emitting unicode characters - (e.g. :class:`StringIO`). - - :param posix_offset: - Optional. If set to ``True``, interpret strings such as ``GMT+3`` or - ``UTC+3`` as being 3 hours *behind* UTC rather than ahead, per the - POSIX standard. - - .. caution:: - - Prior to version 2.7.0, this function also supported time zones - in the format: - - * ``EST5EDT,4,0,6,7200,10,0,26,7200,3600`` - * ``EST5EDT,4,1,0,7200,10,-1,0,7200,3600`` - - This format is non-standard and has been deprecated; this function - will raise a :class:`DeprecatedTZFormatWarning` until - support is removed in a future version. - - .. _`GNU C Library: TZ Variable`: - https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html - """ - def __init__(self, s, posix_offset=False): - global parser - from dateutil.parser import _parser as parser - - self._s = s - - res = parser._parsetz(s) - if res is None or res.any_unused_tokens: - raise ValueError("unknown string format") - - # Here we break the compatibility with the TZ variable handling. - # GMT-3 actually *means* the timezone -3. - if res.stdabbr in ("GMT", "UTC") and not posix_offset: - res.stdoffset *= -1 - - # We must initialize it first, since _delta() needs - # _std_offset and _dst_offset set. Use False in start/end - # to avoid building it two times. - tzrange.__init__(self, res.stdabbr, res.stdoffset, - res.dstabbr, res.dstoffset, - start=False, end=False) - - if not res.dstabbr: - self._start_delta = None - self._end_delta = None - else: - self._start_delta = self._delta(res.start) - if self._start_delta: - self._end_delta = self._delta(res.end, isend=1) - - self.hasdst = bool(self._start_delta) - - def _delta(self, x, isend=0): - from dateutil import relativedelta - kwargs = {} - if x.month is not None: - kwargs["month"] = x.month - if x.weekday is not None: - kwargs["weekday"] = relativedelta.weekday(x.weekday, x.week) - if x.week > 0: - kwargs["day"] = 1 - else: - kwargs["day"] = 31 - elif x.day: - kwargs["day"] = x.day - elif x.yday is not None: - kwargs["yearday"] = x.yday - elif x.jyday is not None: - kwargs["nlyearday"] = x.jyday - if not kwargs: - # Default is to start on first sunday of april, and end - # on last sunday of october. - if not isend: - kwargs["month"] = 4 - kwargs["day"] = 1 - kwargs["weekday"] = relativedelta.SU(+1) - else: - kwargs["month"] = 10 - kwargs["day"] = 31 - kwargs["weekday"] = relativedelta.SU(-1) - if x.time is not None: - kwargs["seconds"] = x.time - else: - # Default is 2AM. - kwargs["seconds"] = 7200 - if isend: - # Convert to standard time, to follow the documented way - # of working with the extra hour. See the documentation - # of the tzinfo class. - delta = self._dst_offset - self._std_offset - kwargs["seconds"] -= delta.seconds + delta.days * 86400 - return relativedelta.relativedelta(**kwargs) - - def __repr__(self): - return "%s(%s)" % (self.__class__.__name__, repr(self._s)) - - -class _tzicalvtzcomp(object): - def __init__(self, tzoffsetfrom, tzoffsetto, isdst, - tzname=None, rrule=None): - self.tzoffsetfrom = datetime.timedelta(seconds=tzoffsetfrom) - self.tzoffsetto = datetime.timedelta(seconds=tzoffsetto) - self.tzoffsetdiff = self.tzoffsetto - self.tzoffsetfrom - self.isdst = isdst - self.tzname = tzname - self.rrule = rrule - - -class _tzicalvtz(_tzinfo): - def __init__(self, tzid, comps=[]): - super(_tzicalvtz, self).__init__() - - self._tzid = tzid - self._comps = comps - self._cachedate = [] - self._cachecomp = [] - self._cache_lock = _thread.allocate_lock() - - def _find_comp(self, dt): - if len(self._comps) == 1: - return self._comps[0] - - dt = dt.replace(tzinfo=None) - - try: - with self._cache_lock: - return self._cachecomp[self._cachedate.index( - (dt, self._fold(dt)))] - except ValueError: - pass - - lastcompdt = None - lastcomp = None - - for comp in self._comps: - compdt = self._find_compdt(comp, dt) - - if compdt and (not lastcompdt or lastcompdt < compdt): - lastcompdt = compdt - lastcomp = comp - - if not lastcomp: - # RFC says nothing about what to do when a given - # time is before the first onset date. We'll look for the - # first standard component, or the first component, if - # none is found. - for comp in self._comps: - if not comp.isdst: - lastcomp = comp - break - else: - lastcomp = comp[0] - - with self._cache_lock: - self._cachedate.insert(0, (dt, self._fold(dt))) - self._cachecomp.insert(0, lastcomp) - - if len(self._cachedate) > 10: - self._cachedate.pop() - self._cachecomp.pop() - - return lastcomp - - def _find_compdt(self, comp, dt): - if comp.tzoffsetdiff < ZERO and self._fold(dt): - dt -= comp.tzoffsetdiff - - compdt = comp.rrule.before(dt, inc=True) - - return compdt - - def utcoffset(self, dt): - if dt is None: - return None - - return self._find_comp(dt).tzoffsetto - - def dst(self, dt): - comp = self._find_comp(dt) - if comp.isdst: - return comp.tzoffsetdiff - else: - return ZERO - - @tzname_in_python2 - def tzname(self, dt): - return self._find_comp(dt).tzname - - def __repr__(self): - return "" % repr(self._tzid) - - __reduce__ = object.__reduce__ - - -class tzical(object): - """ - This object is designed to parse an iCalendar-style ``VTIMEZONE`` structure - as set out in `RFC 5545`_ Section 4.6.5 into one or more `tzinfo` objects. - - :param `fileobj`: - A file or stream in iCalendar format, which should be UTF-8 encoded - with CRLF endings. - - .. _`RFC 5545`: https://tools.ietf.org/html/rfc5545 - """ - def __init__(self, fileobj): - global rrule - from dateutil import rrule - - if isinstance(fileobj, string_types): - self._s = fileobj - # ical should be encoded in UTF-8 with CRLF - fileobj = open(fileobj, 'r') - else: - self._s = getattr(fileobj, 'name', repr(fileobj)) - fileobj = _nullcontext(fileobj) - - self._vtz = {} - - with fileobj as fobj: - self._parse_rfc(fobj.read()) - - def keys(self): - """ - Retrieves the available time zones as a list. - """ - return list(self._vtz.keys()) - - def get(self, tzid=None): - """ - Retrieve a :py:class:`datetime.tzinfo` object by its ``tzid``. - - :param tzid: - If there is exactly one time zone available, omitting ``tzid`` - or passing :py:const:`None` value returns it. Otherwise a valid - key (which can be retrieved from :func:`keys`) is required. - - :raises ValueError: - Raised if ``tzid`` is not specified but there are either more - or fewer than 1 zone defined. - - :returns: - Returns either a :py:class:`datetime.tzinfo` object representing - the relevant time zone or :py:const:`None` if the ``tzid`` was - not found. - """ - if tzid is None: - if len(self._vtz) == 0: - raise ValueError("no timezones defined") - elif len(self._vtz) > 1: - raise ValueError("more than one timezone available") - tzid = next(iter(self._vtz)) - - return self._vtz.get(tzid) - - def _parse_offset(self, s): - s = s.strip() - if not s: - raise ValueError("empty offset") - if s[0] in ('+', '-'): - signal = (-1, +1)[s[0] == '+'] - s = s[1:] - else: - signal = +1 - if len(s) == 4: - return (int(s[:2]) * 3600 + int(s[2:]) * 60) * signal - elif len(s) == 6: - return (int(s[:2]) * 3600 + int(s[2:4]) * 60 + int(s[4:])) * signal - else: - raise ValueError("invalid offset: " + s) - - def _parse_rfc(self, s): - lines = s.splitlines() - if not lines: - raise ValueError("empty string") - - # Unfold - i = 0 - while i < len(lines): - line = lines[i].rstrip() - if not line: - del lines[i] - elif i > 0 and line[0] == " ": - lines[i-1] += line[1:] - del lines[i] - else: - i += 1 - - tzid = None - comps = [] - invtz = False - comptype = None - for line in lines: - if not line: - continue - name, value = line.split(':', 1) - parms = name.split(';') - if not parms: - raise ValueError("empty property name") - name = parms[0].upper() - parms = parms[1:] - if invtz: - if name == "BEGIN": - if value in ("STANDARD", "DAYLIGHT"): - # Process component - pass - else: - raise ValueError("unknown component: "+value) - comptype = value - founddtstart = False - tzoffsetfrom = None - tzoffsetto = None - rrulelines = [] - tzname = None - elif name == "END": - if value == "VTIMEZONE": - if comptype: - raise ValueError("component not closed: "+comptype) - if not tzid: - raise ValueError("mandatory TZID not found") - if not comps: - raise ValueError( - "at least one component is needed") - # Process vtimezone - self._vtz[tzid] = _tzicalvtz(tzid, comps) - invtz = False - elif value == comptype: - if not founddtstart: - raise ValueError("mandatory DTSTART not found") - if tzoffsetfrom is None: - raise ValueError( - "mandatory TZOFFSETFROM not found") - if tzoffsetto is None: - raise ValueError( - "mandatory TZOFFSETFROM not found") - # Process component - rr = None - if rrulelines: - rr = rrule.rrulestr("\n".join(rrulelines), - compatible=True, - ignoretz=True, - cache=True) - comp = _tzicalvtzcomp(tzoffsetfrom, tzoffsetto, - (comptype == "DAYLIGHT"), - tzname, rr) - comps.append(comp) - comptype = None - else: - raise ValueError("invalid component end: "+value) - elif comptype: - if name == "DTSTART": - # DTSTART in VTIMEZONE takes a subset of valid RRULE - # values under RFC 5545. - for parm in parms: - if parm != 'VALUE=DATE-TIME': - msg = ('Unsupported DTSTART param in ' + - 'VTIMEZONE: ' + parm) - raise ValueError(msg) - rrulelines.append(line) - founddtstart = True - elif name in ("RRULE", "RDATE", "EXRULE", "EXDATE"): - rrulelines.append(line) - elif name == "TZOFFSETFROM": - if parms: - raise ValueError( - "unsupported %s parm: %s " % (name, parms[0])) - tzoffsetfrom = self._parse_offset(value) - elif name == "TZOFFSETTO": - if parms: - raise ValueError( - "unsupported TZOFFSETTO parm: "+parms[0]) - tzoffsetto = self._parse_offset(value) - elif name == "TZNAME": - if parms: - raise ValueError( - "unsupported TZNAME parm: "+parms[0]) - tzname = value - elif name == "COMMENT": - pass - else: - raise ValueError("unsupported property: "+name) - else: - if name == "TZID": - if parms: - raise ValueError( - "unsupported TZID parm: "+parms[0]) - tzid = value - elif name in ("TZURL", "LAST-MODIFIED", "COMMENT"): - pass - else: - raise ValueError("unsupported property: "+name) - elif name == "BEGIN" and value == "VTIMEZONE": - tzid = None - comps = [] - invtz = True - - def __repr__(self): - return "%s(%s)" % (self.__class__.__name__, repr(self._s)) - - -if sys.platform != "win32": - TZFILES = ["/etc/localtime", "localtime"] - TZPATHS = ["/usr/share/zoneinfo", - "/usr/lib/zoneinfo", - "/usr/share/lib/zoneinfo", - "/etc/zoneinfo"] -else: - TZFILES = [] - TZPATHS = [] - - -def __get_gettz(): - tzlocal_classes = (tzlocal,) - if tzwinlocal is not None: - tzlocal_classes += (tzwinlocal,) - - class GettzFunc(object): - """ - Retrieve a time zone object from a string representation - - This function is intended to retrieve the :py:class:`tzinfo` subclass - that best represents the time zone that would be used if a POSIX - `TZ variable`_ were set to the same value. - - If no argument or an empty string is passed to ``gettz``, local time - is returned: - - .. code-block:: python3 - - >>> gettz() - tzfile('/etc/localtime') - - This function is also the preferred way to map IANA tz database keys - to :class:`tzfile` objects: - - .. code-block:: python3 - - >>> gettz('Pacific/Kiritimati') - tzfile('/usr/share/zoneinfo/Pacific/Kiritimati') - - On Windows, the standard is extended to include the Windows-specific - zone names provided by the operating system: - - .. code-block:: python3 - - >>> gettz('Egypt Standard Time') - tzwin('Egypt Standard Time') - - Passing a GNU ``TZ`` style string time zone specification returns a - :class:`tzstr` object: - - .. code-block:: python3 - - >>> gettz('AEST-10AEDT-11,M10.1.0/2,M4.1.0/3') - tzstr('AEST-10AEDT-11,M10.1.0/2,M4.1.0/3') - - :param name: - A time zone name (IANA, or, on Windows, Windows keys), location of - a ``tzfile(5)`` zoneinfo file or ``TZ`` variable style time zone - specifier. An empty string, no argument or ``None`` is interpreted - as local time. - - :return: - Returns an instance of one of ``dateutil``'s :py:class:`tzinfo` - subclasses. - - .. versionchanged:: 2.7.0 - - After version 2.7.0, any two calls to ``gettz`` using the same - input strings will return the same object: - - .. code-block:: python3 - - >>> tz.gettz('America/Chicago') is tz.gettz('America/Chicago') - True - - In addition to improving performance, this ensures that - `"same zone" semantics`_ are used for datetimes in the same zone. - - - .. _`TZ variable`: - https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html - - .. _`"same zone" semantics`: - https://blog.ganssle.io/articles/2018/02/aware-datetime-arithmetic.html - """ - def __init__(self): - - self.__instances = weakref.WeakValueDictionary() - self.__strong_cache_size = 8 - self.__strong_cache = OrderedDict() - self._cache_lock = _thread.allocate_lock() - - def __call__(self, name=None): - with self._cache_lock: - rv = self.__instances.get(name, None) - - if rv is None: - rv = self.nocache(name=name) - if not (name is None - or isinstance(rv, tzlocal_classes) - or rv is None): - # tzlocal is slightly more complicated than the other - # time zone providers because it depends on environment - # at construction time, so don't cache that. - # - # We also cannot store weak references to None, so we - # will also not store that. - self.__instances[name] = rv - else: - # No need for strong caching, return immediately - return rv - - self.__strong_cache[name] = self.__strong_cache.pop(name, rv) - - if len(self.__strong_cache) > self.__strong_cache_size: - self.__strong_cache.popitem(last=False) - - return rv - - def set_cache_size(self, size): - with self._cache_lock: - self.__strong_cache_size = size - while len(self.__strong_cache) > size: - self.__strong_cache.popitem(last=False) - - def cache_clear(self): - with self._cache_lock: - self.__instances = weakref.WeakValueDictionary() - self.__strong_cache.clear() - - @staticmethod - def nocache(name=None): - """A non-cached version of gettz""" - tz = None - if not name: - try: - name = os.environ["TZ"] - except KeyError: - pass - if name is None or name in ("", ":"): - for filepath in TZFILES: - if not os.path.isabs(filepath): - filename = filepath - for path in TZPATHS: - filepath = os.path.join(path, filename) - if os.path.isfile(filepath): - break - else: - continue - if os.path.isfile(filepath): - try: - tz = tzfile(filepath) - break - except (IOError, OSError, ValueError): - pass - else: - tz = tzlocal() - else: - try: - if name.startswith(":"): - name = name[1:] - except TypeError as e: - if isinstance(name, bytes): - new_msg = "gettz argument should be str, not bytes" - six.raise_from(TypeError(new_msg), e) - else: - raise - if os.path.isabs(name): - if os.path.isfile(name): - tz = tzfile(name) - else: - tz = None - else: - for path in TZPATHS: - filepath = os.path.join(path, name) - if not os.path.isfile(filepath): - filepath = filepath.replace(' ', '_') - if not os.path.isfile(filepath): - continue - try: - tz = tzfile(filepath) - break - except (IOError, OSError, ValueError): - pass - else: - tz = None - if tzwin is not None: - try: - tz = tzwin(name) - except (WindowsError, UnicodeEncodeError): - # UnicodeEncodeError is for Python 2.7 compat - tz = None - - if not tz: - from dateutil.zoneinfo import get_zonefile_instance - tz = get_zonefile_instance().get(name) - - if not tz: - for c in name: - # name is not a tzstr unless it has at least - # one offset. For short values of "name", an - # explicit for loop seems to be the fastest way - # To determine if a string contains a digit - if c in "0123456789": - try: - tz = tzstr(name) - except ValueError: - pass - break - else: - if name in ("GMT", "UTC"): - tz = UTC - elif name in time.tzname: - tz = tzlocal() - return tz - - return GettzFunc() - - -gettz = __get_gettz() -del __get_gettz - - -def datetime_exists(dt, tz=None): - """ - Given a datetime and a time zone, determine whether or not a given datetime - would fall in a gap. - - :param dt: - A :class:`datetime.datetime` (whose time zone will be ignored if ``tz`` - is provided.) - - :param tz: - A :class:`datetime.tzinfo` with support for the ``fold`` attribute. If - ``None`` or not provided, the datetime's own time zone will be used. - - :return: - Returns a boolean value whether or not the "wall time" exists in - ``tz``. - - .. versionadded:: 2.7.0 - """ - if tz is None: - if dt.tzinfo is None: - raise ValueError('Datetime is naive and no time zone provided.') - tz = dt.tzinfo - - dt = dt.replace(tzinfo=None) - - # This is essentially a test of whether or not the datetime can survive - # a round trip to UTC. - dt_rt = dt.replace(tzinfo=tz).astimezone(UTC).astimezone(tz) - dt_rt = dt_rt.replace(tzinfo=None) - - return dt == dt_rt - - -def datetime_ambiguous(dt, tz=None): - """ - Given a datetime and a time zone, determine whether or not a given datetime - is ambiguous (i.e if there are two times differentiated only by their DST - status). - - :param dt: - A :class:`datetime.datetime` (whose time zone will be ignored if ``tz`` - is provided.) - - :param tz: - A :class:`datetime.tzinfo` with support for the ``fold`` attribute. If - ``None`` or not provided, the datetime's own time zone will be used. - - :return: - Returns a boolean value whether or not the "wall time" is ambiguous in - ``tz``. - - .. versionadded:: 2.6.0 - """ - if tz is None: - if dt.tzinfo is None: - raise ValueError('Datetime is naive and no time zone provided.') - - tz = dt.tzinfo - - # If a time zone defines its own "is_ambiguous" function, we'll use that. - is_ambiguous_fn = getattr(tz, 'is_ambiguous', None) - if is_ambiguous_fn is not None: - try: - return tz.is_ambiguous(dt) - except Exception: - pass - - # If it doesn't come out and tell us it's ambiguous, we'll just check if - # the fold attribute has any effect on this particular date and time. - dt = dt.replace(tzinfo=tz) - wall_0 = enfold(dt, fold=0) - wall_1 = enfold(dt, fold=1) - - same_offset = wall_0.utcoffset() == wall_1.utcoffset() - same_dst = wall_0.dst() == wall_1.dst() - - return not (same_offset and same_dst) - - -def resolve_imaginary(dt): - """ - Given a datetime that may be imaginary, return an existing datetime. - - This function assumes that an imaginary datetime represents what the - wall time would be in a zone had the offset transition not occurred, so - it will always fall forward by the transition's change in offset. - - .. doctest:: - - >>> from dateutil import tz - >>> from datetime import datetime - >>> NYC = tz.gettz('America/New_York') - >>> print(tz.resolve_imaginary(datetime(2017, 3, 12, 2, 30, tzinfo=NYC))) - 2017-03-12 03:30:00-04:00 - - >>> KIR = tz.gettz('Pacific/Kiritimati') - >>> print(tz.resolve_imaginary(datetime(1995, 1, 1, 12, 30, tzinfo=KIR))) - 1995-01-02 12:30:00+14:00 - - As a note, :func:`datetime.astimezone` is guaranteed to produce a valid, - existing datetime, so a round-trip to and from UTC is sufficient to get - an extant datetime, however, this generally "falls back" to an earlier time - rather than falling forward to the STD side (though no guarantees are made - about this behavior). - - :param dt: - A :class:`datetime.datetime` which may or may not exist. - - :return: - Returns an existing :class:`datetime.datetime`. If ``dt`` was not - imaginary, the datetime returned is guaranteed to be the same object - passed to the function. - - .. versionadded:: 2.7.0 - """ - if dt.tzinfo is not None and not datetime_exists(dt): - - curr_offset = (dt + datetime.timedelta(hours=24)).utcoffset() - old_offset = (dt - datetime.timedelta(hours=24)).utcoffset() - - dt += curr_offset - old_offset - - return dt - - -def _datetime_to_timestamp(dt): - """ - Convert a :class:`datetime.datetime` object to an epoch timestamp in - seconds since January 1, 1970, ignoring the time zone. - """ - return (dt.replace(tzinfo=None) - EPOCH).total_seconds() - - -if sys.version_info >= (3, 6): - def _get_supported_offset(second_offset): - return second_offset -else: - def _get_supported_offset(second_offset): - # For python pre-3.6, round to full-minutes if that's not the case. - # Python's datetime doesn't accept sub-minute timezones. Check - # http://python.org/sf/1447945 or https://bugs.python.org/issue5288 - # for some information. - old_offset = second_offset - calculated_offset = 60 * ((second_offset + 30) // 60) - return calculated_offset - - -try: - # Python 3.7 feature - from contextlib import nullcontext as _nullcontext -except ImportError: - class _nullcontext(object): - """ - Class for wrapping contexts so that they are passed through in a - with statement. - """ - def __init__(self, context): - self.context = context - - def __enter__(self): - return self.context - - def __exit__(*args, **kwargs): - pass - -# vim:ts=4:sw=4:et diff --git a/apps/bitwarden_event_logs/lib/dateutil/tz/win.py b/apps/bitwarden_event_logs/lib/dateutil/tz/win.py deleted file mode 100755 index cde07ba7..00000000 --- a/apps/bitwarden_event_logs/lib/dateutil/tz/win.py +++ /dev/null @@ -1,370 +0,0 @@ -# -*- coding: utf-8 -*- -""" -This module provides an interface to the native time zone data on Windows, -including :py:class:`datetime.tzinfo` implementations. - -Attempting to import this module on a non-Windows platform will raise an -:py:obj:`ImportError`. -""" -# This code was originally contributed by Jeffrey Harris. -import datetime -import struct - -from six.moves import winreg -from six import text_type - -try: - import ctypes - from ctypes import wintypes -except ValueError: - # ValueError is raised on non-Windows systems for some horrible reason. - raise ImportError("Running tzwin on non-Windows system") - -from ._common import tzrangebase - -__all__ = ["tzwin", "tzwinlocal", "tzres"] - -ONEWEEK = datetime.timedelta(7) - -TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones" -TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones" -TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation" - - -def _settzkeyname(): - handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) - try: - winreg.OpenKey(handle, TZKEYNAMENT).Close() - TZKEYNAME = TZKEYNAMENT - except WindowsError: - TZKEYNAME = TZKEYNAME9X - handle.Close() - return TZKEYNAME - - -TZKEYNAME = _settzkeyname() - - -class tzres(object): - """ - Class for accessing ``tzres.dll``, which contains timezone name related - resources. - - .. versionadded:: 2.5.0 - """ - p_wchar = ctypes.POINTER(wintypes.WCHAR) # Pointer to a wide char - - def __init__(self, tzres_loc='tzres.dll'): - # Load the user32 DLL so we can load strings from tzres - user32 = ctypes.WinDLL('user32') - - # Specify the LoadStringW function - user32.LoadStringW.argtypes = (wintypes.HINSTANCE, - wintypes.UINT, - wintypes.LPWSTR, - ctypes.c_int) - - self.LoadStringW = user32.LoadStringW - self._tzres = ctypes.WinDLL(tzres_loc) - self.tzres_loc = tzres_loc - - def load_name(self, offset): - """ - Load a timezone name from a DLL offset (integer). - - >>> from dateutil.tzwin import tzres - >>> tzr = tzres() - >>> print(tzr.load_name(112)) - 'Eastern Standard Time' - - :param offset: - A positive integer value referring to a string from the tzres dll. - - .. note:: - - Offsets found in the registry are generally of the form - ``@tzres.dll,-114``. The offset in this case is 114, not -114. - - """ - resource = self.p_wchar() - lpBuffer = ctypes.cast(ctypes.byref(resource), wintypes.LPWSTR) - nchar = self.LoadStringW(self._tzres._handle, offset, lpBuffer, 0) - return resource[:nchar] - - def name_from_string(self, tzname_str): - """ - Parse strings as returned from the Windows registry into the time zone - name as defined in the registry. - - >>> from dateutil.tzwin import tzres - >>> tzr = tzres() - >>> print(tzr.name_from_string('@tzres.dll,-251')) - 'Dateline Daylight Time' - >>> print(tzr.name_from_string('Eastern Standard Time')) - 'Eastern Standard Time' - - :param tzname_str: - A timezone name string as returned from a Windows registry key. - - :return: - Returns the localized timezone string from tzres.dll if the string - is of the form `@tzres.dll,-offset`, else returns the input string. - """ - if not tzname_str.startswith('@'): - return tzname_str - - name_splt = tzname_str.split(',-') - try: - offset = int(name_splt[1]) - except: - raise ValueError("Malformed timezone string.") - - return self.load_name(offset) - - -class tzwinbase(tzrangebase): - """tzinfo class based on win32's timezones available in the registry.""" - def __init__(self): - raise NotImplementedError('tzwinbase is an abstract base class') - - def __eq__(self, other): - # Compare on all relevant dimensions, including name. - if not isinstance(other, tzwinbase): - return NotImplemented - - return (self._std_offset == other._std_offset and - self._dst_offset == other._dst_offset and - self._stddayofweek == other._stddayofweek and - self._dstdayofweek == other._dstdayofweek and - self._stdweeknumber == other._stdweeknumber and - self._dstweeknumber == other._dstweeknumber and - self._stdhour == other._stdhour and - self._dsthour == other._dsthour and - self._stdminute == other._stdminute and - self._dstminute == other._dstminute and - self._std_abbr == other._std_abbr and - self._dst_abbr == other._dst_abbr) - - @staticmethod - def list(): - """Return a list of all time zones known to the system.""" - with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: - with winreg.OpenKey(handle, TZKEYNAME) as tzkey: - result = [winreg.EnumKey(tzkey, i) - for i in range(winreg.QueryInfoKey(tzkey)[0])] - return result - - def display(self): - """ - Return the display name of the time zone. - """ - return self._display - - def transitions(self, year): - """ - For a given year, get the DST on and off transition times, expressed - always on the standard time side. For zones with no transitions, this - function returns ``None``. - - :param year: - The year whose transitions you would like to query. - - :return: - Returns a :class:`tuple` of :class:`datetime.datetime` objects, - ``(dston, dstoff)`` for zones with an annual DST transition, or - ``None`` for fixed offset zones. - """ - - if not self.hasdst: - return None - - dston = picknthweekday(year, self._dstmonth, self._dstdayofweek, - self._dsthour, self._dstminute, - self._dstweeknumber) - - dstoff = picknthweekday(year, self._stdmonth, self._stddayofweek, - self._stdhour, self._stdminute, - self._stdweeknumber) - - # Ambiguous dates default to the STD side - dstoff -= self._dst_base_offset - - return dston, dstoff - - def _get_hasdst(self): - return self._dstmonth != 0 - - @property - def _dst_base_offset(self): - return self._dst_base_offset_ - - -class tzwin(tzwinbase): - """ - Time zone object created from the zone info in the Windows registry - - These are similar to :py:class:`dateutil.tz.tzrange` objects in that - the time zone data is provided in the format of a single offset rule - for either 0 or 2 time zone transitions per year. - - :param: name - The name of a Windows time zone key, e.g. "Eastern Standard Time". - The full list of keys can be retrieved with :func:`tzwin.list`. - """ - - def __init__(self, name): - self._name = name - - with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: - tzkeyname = text_type("{kn}\\{name}").format(kn=TZKEYNAME, name=name) - with winreg.OpenKey(handle, tzkeyname) as tzkey: - keydict = valuestodict(tzkey) - - self._std_abbr = keydict["Std"] - self._dst_abbr = keydict["Dlt"] - - self._display = keydict["Display"] - - # See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm - tup = struct.unpack("=3l16h", keydict["TZI"]) - stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1 - dstoffset = stdoffset-tup[2] # + DaylightBias * -1 - self._std_offset = datetime.timedelta(minutes=stdoffset) - self._dst_offset = datetime.timedelta(minutes=dstoffset) - - # for the meaning see the win32 TIME_ZONE_INFORMATION structure docs - # http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx - (self._stdmonth, - self._stddayofweek, # Sunday = 0 - self._stdweeknumber, # Last = 5 - self._stdhour, - self._stdminute) = tup[4:9] - - (self._dstmonth, - self._dstdayofweek, # Sunday = 0 - self._dstweeknumber, # Last = 5 - self._dsthour, - self._dstminute) = tup[12:17] - - self._dst_base_offset_ = self._dst_offset - self._std_offset - self.hasdst = self._get_hasdst() - - def __repr__(self): - return "tzwin(%s)" % repr(self._name) - - def __reduce__(self): - return (self.__class__, (self._name,)) - - -class tzwinlocal(tzwinbase): - """ - Class representing the local time zone information in the Windows registry - - While :class:`dateutil.tz.tzlocal` makes system calls (via the :mod:`time` - module) to retrieve time zone information, ``tzwinlocal`` retrieves the - rules directly from the Windows registry and creates an object like - :class:`dateutil.tz.tzwin`. - - Because Windows does not have an equivalent of :func:`time.tzset`, on - Windows, :class:`dateutil.tz.tzlocal` instances will always reflect the - time zone settings *at the time that the process was started*, meaning - changes to the machine's time zone settings during the run of a program - on Windows will **not** be reflected by :class:`dateutil.tz.tzlocal`. - Because ``tzwinlocal`` reads the registry directly, it is unaffected by - this issue. - """ - def __init__(self): - with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: - with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey: - keydict = valuestodict(tzlocalkey) - - self._std_abbr = keydict["StandardName"] - self._dst_abbr = keydict["DaylightName"] - - try: - tzkeyname = text_type('{kn}\\{sn}').format(kn=TZKEYNAME, - sn=self._std_abbr) - with winreg.OpenKey(handle, tzkeyname) as tzkey: - _keydict = valuestodict(tzkey) - self._display = _keydict["Display"] - except OSError: - self._display = None - - stdoffset = -keydict["Bias"]-keydict["StandardBias"] - dstoffset = stdoffset-keydict["DaylightBias"] - - self._std_offset = datetime.timedelta(minutes=stdoffset) - self._dst_offset = datetime.timedelta(minutes=dstoffset) - - # For reasons unclear, in this particular key, the day of week has been - # moved to the END of the SYSTEMTIME structure. - tup = struct.unpack("=8h", keydict["StandardStart"]) - - (self._stdmonth, - self._stdweeknumber, # Last = 5 - self._stdhour, - self._stdminute) = tup[1:5] - - self._stddayofweek = tup[7] - - tup = struct.unpack("=8h", keydict["DaylightStart"]) - - (self._dstmonth, - self._dstweeknumber, # Last = 5 - self._dsthour, - self._dstminute) = tup[1:5] - - self._dstdayofweek = tup[7] - - self._dst_base_offset_ = self._dst_offset - self._std_offset - self.hasdst = self._get_hasdst() - - def __repr__(self): - return "tzwinlocal()" - - def __str__(self): - # str will return the standard name, not the daylight name. - return "tzwinlocal(%s)" % repr(self._std_abbr) - - def __reduce__(self): - return (self.__class__, ()) - - -def picknthweekday(year, month, dayofweek, hour, minute, whichweek): - """ dayofweek == 0 means Sunday, whichweek 5 means last instance """ - first = datetime.datetime(year, month, 1, hour, minute) - - # This will work if dayofweek is ISO weekday (1-7) or Microsoft-style (0-6), - # Because 7 % 7 = 0 - weekdayone = first.replace(day=((dayofweek - first.isoweekday()) % 7) + 1) - wd = weekdayone + ((whichweek - 1) * ONEWEEK) - if (wd.month != month): - wd -= ONEWEEK - - return wd - - -def valuestodict(key): - """Convert a registry key's values to a dictionary.""" - dout = {} - size = winreg.QueryInfoKey(key)[1] - tz_res = None - - for i in range(size): - key_name, value, dtype = winreg.EnumValue(key, i) - if dtype == winreg.REG_DWORD or dtype == winreg.REG_DWORD_LITTLE_ENDIAN: - # If it's a DWORD (32-bit integer), it's stored as unsigned - convert - # that to a proper signed integer - if value & (1 << 31): - value = value - (1 << 32) - elif dtype == winreg.REG_SZ: - # If it's a reference to the tzres DLL, load the actual string - if value.startswith('@tzres'): - tz_res = tz_res or tzres() - value = tz_res.name_from_string(value) - - value = value.rstrip('\x00') # Remove trailing nulls - - dout[key_name] = value - - return dout diff --git a/apps/bitwarden_event_logs/lib/dateutil/tzwin.py b/apps/bitwarden_event_logs/lib/dateutil/tzwin.py deleted file mode 100755 index cebc673e..00000000 --- a/apps/bitwarden_event_logs/lib/dateutil/tzwin.py +++ /dev/null @@ -1,2 +0,0 @@ -# tzwin has moved to dateutil.tz.win -from .tz.win import * diff --git a/apps/bitwarden_event_logs/lib/dateutil/utils.py b/apps/bitwarden_event_logs/lib/dateutil/utils.py deleted file mode 100755 index dd2d245a..00000000 --- a/apps/bitwarden_event_logs/lib/dateutil/utils.py +++ /dev/null @@ -1,71 +0,0 @@ -# -*- coding: utf-8 -*- -""" -This module offers general convenience and utility functions for dealing with -datetimes. - -.. versionadded:: 2.7.0 -""" -from __future__ import unicode_literals - -from datetime import datetime, time - - -def today(tzinfo=None): - """ - Returns a :py:class:`datetime` representing the current day at midnight - - :param tzinfo: - The time zone to attach (also used to determine the current day). - - :return: - A :py:class:`datetime.datetime` object representing the current day - at midnight. - """ - - dt = datetime.now(tzinfo) - return datetime.combine(dt.date(), time(0, tzinfo=tzinfo)) - - -def default_tzinfo(dt, tzinfo): - """ - Sets the ``tzinfo`` parameter on naive datetimes only - - This is useful for example when you are provided a datetime that may have - either an implicit or explicit time zone, such as when parsing a time zone - string. - - .. doctest:: - - >>> from dateutil.tz import tzoffset - >>> from dateutil.parser import parse - >>> from dateutil.utils import default_tzinfo - >>> dflt_tz = tzoffset("EST", -18000) - >>> print(default_tzinfo(parse('2014-01-01 12:30 UTC'), dflt_tz)) - 2014-01-01 12:30:00+00:00 - >>> print(default_tzinfo(parse('2014-01-01 12:30'), dflt_tz)) - 2014-01-01 12:30:00-05:00 - - :param dt: - The datetime on which to replace the time zone - - :param tzinfo: - The :py:class:`datetime.tzinfo` subclass instance to assign to - ``dt`` if (and only if) it is naive. - - :return: - Returns an aware :py:class:`datetime.datetime`. - """ - if dt.tzinfo is not None: - return dt - else: - return dt.replace(tzinfo=tzinfo) - - -def within_delta(dt1, dt2, delta): - """ - Useful for comparing two datetimes that may have a negligible difference - to be considered equal. - """ - delta = abs(delta) - difference = dt1 - dt2 - return -delta <= difference <= delta diff --git a/apps/bitwarden_event_logs/lib/dateutil/zoneinfo/__init__.py b/apps/bitwarden_event_logs/lib/dateutil/zoneinfo/__init__.py deleted file mode 100755 index 34f11ad6..00000000 --- a/apps/bitwarden_event_logs/lib/dateutil/zoneinfo/__init__.py +++ /dev/null @@ -1,167 +0,0 @@ -# -*- coding: utf-8 -*- -import warnings -import json - -from tarfile import TarFile -from pkgutil import get_data -from io import BytesIO - -from dateutil.tz import tzfile as _tzfile - -__all__ = ["get_zonefile_instance", "gettz", "gettz_db_metadata"] - -ZONEFILENAME = "dateutil-zoneinfo.tar.gz" -METADATA_FN = 'METADATA' - - -class tzfile(_tzfile): - def __reduce__(self): - return (gettz, (self._filename,)) - - -def getzoneinfofile_stream(): - try: - return BytesIO(get_data(__name__, ZONEFILENAME)) - except IOError as e: # TODO switch to FileNotFoundError? - warnings.warn("I/O error({0}): {1}".format(e.errno, e.strerror)) - return None - - -class ZoneInfoFile(object): - def __init__(self, zonefile_stream=None): - if zonefile_stream is not None: - with TarFile.open(fileobj=zonefile_stream) as tf: - self.zones = {zf.name: tzfile(tf.extractfile(zf), filename=zf.name) - for zf in tf.getmembers() - if zf.isfile() and zf.name != METADATA_FN} - # deal with links: They'll point to their parent object. Less - # waste of memory - links = {zl.name: self.zones[zl.linkname] - for zl in tf.getmembers() if - zl.islnk() or zl.issym()} - self.zones.update(links) - try: - metadata_json = tf.extractfile(tf.getmember(METADATA_FN)) - metadata_str = metadata_json.read().decode('UTF-8') - self.metadata = json.loads(metadata_str) - except KeyError: - # no metadata in tar file - self.metadata = None - else: - self.zones = {} - self.metadata = None - - def get(self, name, default=None): - """ - Wrapper for :func:`ZoneInfoFile.zones.get`. This is a convenience method - for retrieving zones from the zone dictionary. - - :param name: - The name of the zone to retrieve. (Generally IANA zone names) - - :param default: - The value to return in the event of a missing key. - - .. versionadded:: 2.6.0 - - """ - return self.zones.get(name, default) - - -# The current API has gettz as a module function, although in fact it taps into -# a stateful class. So as a workaround for now, without changing the API, we -# will create a new "global" class instance the first time a user requests a -# timezone. Ugly, but adheres to the api. -# -# TODO: Remove after deprecation period. -_CLASS_ZONE_INSTANCE = [] - - -def get_zonefile_instance(new_instance=False): - """ - This is a convenience function which provides a :class:`ZoneInfoFile` - instance using the data provided by the ``dateutil`` package. By default, it - caches a single instance of the ZoneInfoFile object and returns that. - - :param new_instance: - If ``True``, a new instance of :class:`ZoneInfoFile` is instantiated and - used as the cached instance for the next call. Otherwise, new instances - are created only as necessary. - - :return: - Returns a :class:`ZoneInfoFile` object. - - .. versionadded:: 2.6 - """ - if new_instance: - zif = None - else: - zif = getattr(get_zonefile_instance, '_cached_instance', None) - - if zif is None: - zif = ZoneInfoFile(getzoneinfofile_stream()) - - get_zonefile_instance._cached_instance = zif - - return zif - - -def gettz(name): - """ - This retrieves a time zone from the local zoneinfo tarball that is packaged - with dateutil. - - :param name: - An IANA-style time zone name, as found in the zoneinfo file. - - :return: - Returns a :class:`dateutil.tz.tzfile` time zone object. - - .. warning:: - It is generally inadvisable to use this function, and it is only - provided for API compatibility with earlier versions. This is *not* - equivalent to ``dateutil.tz.gettz()``, which selects an appropriate - time zone based on the inputs, favoring system zoneinfo. This is ONLY - for accessing the dateutil-specific zoneinfo (which may be out of - date compared to the system zoneinfo). - - .. deprecated:: 2.6 - If you need to use a specific zoneinfofile over the system zoneinfo, - instantiate a :class:`dateutil.zoneinfo.ZoneInfoFile` object and call - :func:`dateutil.zoneinfo.ZoneInfoFile.get(name)` instead. - - Use :func:`get_zonefile_instance` to retrieve an instance of the - dateutil-provided zoneinfo. - """ - warnings.warn("zoneinfo.gettz() will be removed in future versions, " - "to use the dateutil-provided zoneinfo files, instantiate a " - "ZoneInfoFile object and use ZoneInfoFile.zones.get() " - "instead. See the documentation for details.", - DeprecationWarning) - - if len(_CLASS_ZONE_INSTANCE) == 0: - _CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream())) - return _CLASS_ZONE_INSTANCE[0].zones.get(name) - - -def gettz_db_metadata(): - """ Get the zonefile metadata - - See `zonefile_metadata`_ - - :returns: - A dictionary with the database metadata - - .. deprecated:: 2.6 - See deprecation warning in :func:`zoneinfo.gettz`. To get metadata, - query the attribute ``zoneinfo.ZoneInfoFile.metadata``. - """ - warnings.warn("zoneinfo.gettz_db_metadata() will be removed in future " - "versions, to use the dateutil-provided zoneinfo files, " - "ZoneInfoFile object and query the 'metadata' attribute " - "instead. See the documentation for details.", - DeprecationWarning) - - if len(_CLASS_ZONE_INSTANCE) == 0: - _CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream())) - return _CLASS_ZONE_INSTANCE[0].metadata diff --git a/apps/bitwarden_event_logs/lib/dateutil/zoneinfo/rebuild.py b/apps/bitwarden_event_logs/lib/dateutil/zoneinfo/rebuild.py deleted file mode 100755 index 684c6586..00000000 --- a/apps/bitwarden_event_logs/lib/dateutil/zoneinfo/rebuild.py +++ /dev/null @@ -1,75 +0,0 @@ -import logging -import os -import tempfile -import shutil -import json -from subprocess import check_call, check_output -from tarfile import TarFile - -from dateutil.zoneinfo import METADATA_FN, ZONEFILENAME - - -def rebuild(filename, tag=None, format="gz", zonegroups=[], metadata=None): - """Rebuild the internal timezone info in dateutil/zoneinfo/zoneinfo*tar* - - filename is the timezone tarball from ``ftp.iana.org/tz``. - - """ - tmpdir = tempfile.mkdtemp() - zonedir = os.path.join(tmpdir, "zoneinfo") - moduledir = os.path.dirname(__file__) - try: - with TarFile.open(filename) as tf: - for name in zonegroups: - tf.extract(name, tmpdir) - filepaths = [os.path.join(tmpdir, n) for n in zonegroups] - - _run_zic(zonedir, filepaths) - - # write metadata file - with open(os.path.join(zonedir, METADATA_FN), 'w') as f: - json.dump(metadata, f, indent=4, sort_keys=True) - target = os.path.join(moduledir, ZONEFILENAME) - with TarFile.open(target, "w:%s" % format) as tf: - for entry in os.listdir(zonedir): - entrypath = os.path.join(zonedir, entry) - tf.add(entrypath, entry) - finally: - shutil.rmtree(tmpdir) - - -def _run_zic(zonedir, filepaths): - """Calls the ``zic`` compiler in a compatible way to get a "fat" binary. - - Recent versions of ``zic`` default to ``-b slim``, while older versions - don't even have the ``-b`` option (but default to "fat" binaries). The - current version of dateutil does not support Version 2+ TZif files, which - causes problems when used in conjunction with "slim" binaries, so this - function is used to ensure that we always get a "fat" binary. - """ - - try: - help_text = check_output(["zic", "--help"]) - except OSError as e: - _print_on_nosuchfile(e) - raise - - if b"-b " in help_text: - bloat_args = ["-b", "fat"] - else: - bloat_args = [] - - check_call(["zic"] + bloat_args + ["-d", zonedir] + filepaths) - - -def _print_on_nosuchfile(e): - """Print helpful troubleshooting message - - e is an exception raised by subprocess.check_call() - - """ - if e.errno == 2: - logging.error( - "Could not find zic. Perhaps you need to install " - "libc-bin or some other package that provides it, " - "or it's not in your PATH?") diff --git a/apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/INSTALLER b/apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/INSTALLER deleted file mode 100755 index a1b589e3..00000000 --- a/apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/LICENSE b/apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/LICENSE deleted file mode 100755 index 311690c6..00000000 --- a/apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/LICENSE +++ /dev/null @@ -1,49 +0,0 @@ -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 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. - diff --git a/apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/METADATA b/apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/METADATA deleted file mode 100755 index f916e891..00000000 --- a/apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/METADATA +++ /dev/null @@ -1,978 +0,0 @@ -Metadata-Version: 2.1 -Name: defusedxml -Version: 0.7.1 -Summary: XML bomb protection for Python stdlib modules -Home-page: https://github.com/tiran/defusedxml -Author: Christian Heimes -Author-email: christian@python.org -Maintainer: Christian Heimes -Maintainer-email: christian@python.org -License: PSFL -Download-URL: https://pypi.python.org/pypi/defusedxml -Keywords: xml bomb DoS -Platform: all -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: Python Software Foundation License -Classifier: Natural Language :: English -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Topic :: Text Processing :: Markup :: XML -Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* - -=================================================== -defusedxml -- defusing XML bombs and other exploits -=================================================== - -.. image:: https://img.shields.io/pypi/v/defusedxml.svg - :target: https://pypi.org/project/defusedxml/ - :alt: Latest Version - -.. image:: https://img.shields.io/pypi/pyversions/defusedxml.svg - :target: https://pypi.org/project/defusedxml/ - :alt: Supported Python versions - -.. image:: https://travis-ci.org/tiran/defusedxml.svg?branch=master - :target: https://travis-ci.org/tiran/defusedxml - :alt: Travis CI - -.. image:: https://codecov.io/github/tiran/defusedxml/coverage.svg?branch=master - :target: https://codecov.io/github/tiran/defusedxml?branch=master - :alt: codecov - -.. image:: https://img.shields.io/pypi/dm/defusedxml.svg - :target: https://pypistats.org/packages/defusedxml - :alt: PyPI downloads - -.. image:: https://img.shields.io/badge/code%20style-black-000000.svg - :target: https://github.com/psf/black - :alt: Code style: black - -.. - - "It's just XML, what could probably go wrong?" - -Christian Heimes - -Synopsis -======== - -The results of an attack on a vulnerable XML library can be fairly dramatic. -With just a few hundred **Bytes** of XML data an attacker can occupy several -**Gigabytes** of memory within **seconds**. An attacker can also keep -CPUs busy for a long time with a small to medium size request. Under some -circumstances it is even possible to access local files on your -server, to circumvent a firewall, or to abuse services to rebound attacks to -third parties. - -The attacks use and abuse less common features of XML and its parsers. The -majority of developers are unacquainted with features such as processing -instructions and entity expansions that XML inherited from SGML. At best -they know about ```` from experience with HTML but they are not -aware that a document type definition (DTD) can generate an HTTP request -or load a file from the file system. - -None of the issues is new. They have been known for a long time. Billion -laughs was first reported in 2003. Nevertheless some XML libraries and -applications are still vulnerable and even heavy users of XML are -surprised by these features. It's hard to say whom to blame for the -situation. It's too short sighted to shift all blame on XML parsers and -XML libraries for using insecure default settings. After all they -properly implement XML specifications. Application developers must not rely -that a library is always configured for security and potential harmful data -by default. - - -.. contents:: Table of Contents - :depth: 2 - - -Attack vectors -============== - -billion laughs / exponential entity expansion ---------------------------------------------- - -The `Billion Laughs`_ attack -- also known as exponential entity expansion -- -uses multiple levels of nested entities. The original example uses 9 levels -of 10 expansions in each level to expand the string ``lol`` to a string of -3 * 10 :sup:`9` bytes, hence the name "billion laughs". The resulting string -occupies 3 GB (2.79 GiB) of memory; intermediate strings require additional -memory. Because most parsers don't cache the intermediate step for every -expansion it is repeated over and over again. It increases the CPU load even -more. - -An XML document of just a few hundred bytes can disrupt all services on a -machine within seconds. - -Example XML:: - - - - - - ]> - &d; - - -quadratic blowup entity expansion ---------------------------------- - -A quadratic blowup attack is similar to a `Billion Laughs`_ attack; it abuses -entity expansion, too. Instead of nested entities it repeats one large entity -with a couple of thousand chars over and over again. The attack isn't as -efficient as the exponential case but it avoids triggering countermeasures of -parsers against heavily nested entities. Some parsers limit the depth and -breadth of a single entity but not the total amount of expanded text -throughout an entire XML document. - -A medium-sized XML document with a couple of hundred kilobytes can require a -couple of hundred MB to several GB of memory. When the attack is combined -with some level of nested expansion an attacker is able to achieve a higher -ratio of success. - -:: - - - ]> - &a;&a;&a;... repeat - - -external entity expansion (remote) ----------------------------------- - -Entity declarations can contain more than just text for replacement. They can -also point to external resources by public identifiers or system identifiers. -System identifiers are standard URIs. When the URI is a URL (e.g. a -``http://`` locator) some parsers download the resource from the remote -location and embed them into the XML document verbatim. - -Simple example of a parsed external entity:: - - - ]> - - -The case of parsed external entities works only for valid XML content. The -XML standard also supports unparsed external entities with a -``NData declaration``. - -External entity expansion opens the door to plenty of exploits. An attacker -can abuse a vulnerable XML library and application to rebound and forward -network requests with the IP address of the server. It highly depends -on the parser and the application what kind of exploit is possible. For -example: - -* An attacker can circumvent firewalls and gain access to restricted - resources as all the requests are made from an internal and trustworthy - IP address, not from the outside. -* An attacker can abuse a service to attack, spy on or DoS your servers but - also third party services. The attack is disguised with the IP address of - the server and the attacker is able to utilize the high bandwidth of a big - machine. -* An attacker can exhaust additional resources on the machine, e.g. with - requests to a service that doesn't respond or responds with very large - files. -* An attacker may gain knowledge, when, how often and from which IP address - an XML document is accessed. -* An attacker could send mail from inside your network if the URL handler - supports ``smtp://`` URIs. - - -external entity expansion (local file) --------------------------------------- - -External entities with references to local files are a sub-case of external -entity expansion. It's listed as an extra attack because it deserves extra -attention. Some XML libraries such as lxml disable network access by default -but still allow entity expansion with local file access by default. Local -files are either referenced with a ``file://`` URL or by a file path (either -relative or absolute). - -An attacker may be able to access and download all files that can be read by -the application process. This may include critical configuration files, too. - -:: - - - ]> - - - -DTD retrieval -------------- - -This case is similar to external entity expansion, too. Some XML libraries -like Python's xml.dom.pulldom retrieve document type definitions from remote -or local locations. Several attack scenarios from the external entity case -apply to this issue as well. - -:: - - - - - - text - - - -Python XML Libraries -==================== - -.. csv-table:: vulnerabilities and features - :header: "kind", "sax", "etree", "minidom", "pulldom", "xmlrpc", "lxml", "genshi" - :widths: 24, 7, 8, 8, 7, 8, 8, 8 - :stub-columns: 0 - - "billion laughs", "**True**", "**True**", "**True**", "**True**", "**True**", "False (1)", "False (5)" - "quadratic blowup", "**True**", "**True**", "**True**", "**True**", "**True**", "**True**", "False (5)" - "external entity expansion (remote)", "**True**", "False (3)", "False (4)", "**True**", "false", "False (1)", "False (5)" - "external entity expansion (local file)", "**True**", "False (3)", "False (4)", "**True**", "false", "**True**", "False (5)" - "DTD retrieval", "**True**", "False", "False", "**True**", "false", "False (1)", "False" - "gzip bomb", "False", "False", "False", "False", "**True**", "**partly** (2)", "False" - "xpath support (7)", "False", "False", "False", "False", "False", "**True**", "False" - "xsl(t) support (7)", "False", "False", "False", "False", "False", "**True**", "False" - "xinclude support (7)", "False", "**True** (6)", "False", "False", "False", "**True** (6)", "**True**" - "C library", "expat", "expat", "expat", "expat", "expat", "libxml2", "expat" - -1. Lxml is protected against billion laughs attacks and doesn't do network - lookups by default. -2. libxml2 and lxml are not directly vulnerable to gzip decompression bombs - but they don't protect you against them either. -3. xml.etree doesn't expand entities and raises a ParserError when an entity - occurs. -4. minidom doesn't expand entities and simply returns the unexpanded entity - verbatim. -5. genshi.input of genshi 0.6 doesn't support entity expansion and raises a - ParserError when an entity occurs. -6. Library has (limited) XInclude support but requires an additional step to - process inclusion. -7. These are features but they may introduce exploitable holes, see - `Other things to consider`_ - - -Settings in standard library ----------------------------- - - -xml.sax.handler Features -........................ - -feature_external_ges (http://xml.org/sax/features/external-general-entities) - disables external entity expansion - -feature_external_pes (http://xml.org/sax/features/external-parameter-entities) - the option is ignored and doesn't modify any functionality - -DOM xml.dom.xmlbuilder.Options -.............................. - -external_parameter_entities - ignored - -external_general_entities - ignored - -external_dtd_subset - ignored - -entities - unsure - - -defusedxml -========== - -The `defusedxml package`_ (`defusedxml on PyPI`_) -contains several Python-only workarounds and fixes -for denial of service and other vulnerabilities in Python's XML libraries. -In order to benefit from the protection you just have to import and use the -listed functions / classes from the right defusedxml module instead of the -original module. Merely `defusedxml.xmlrpc`_ is implemented as monkey patch. - -Instead of:: - - >>> from xml.etree.ElementTree import parse - >>> et = parse(xmlfile) - -alter code to:: - - >>> from defusedxml.ElementTree import parse - >>> et = parse(xmlfile) - -Additionally the package has an **untested** function to monkey patch -all stdlib modules with ``defusedxml.defuse_stdlib()``. - -All functions and parser classes accept three additional keyword arguments. -They return either the same objects as the original functions or compatible -subclasses. - -forbid_dtd (default: False) - disallow XML with a ```` processing instruction and raise a - *DTDForbidden* exception when a DTD processing instruction is found. - -forbid_entities (default: True) - disallow XML with ```` declarations inside the DTD and raise an - *EntitiesForbidden* exception when an entity is declared. - -forbid_external (default: True) - disallow any access to remote or local resources in external entities - or DTD and raising an *ExternalReferenceForbidden* exception when a DTD - or entity references an external resource. - - -defusedxml (package) --------------------- - -DefusedXmlException, DTDForbidden, EntitiesForbidden, -ExternalReferenceForbidden, NotSupportedError - -defuse_stdlib() (*experimental*) - - -defusedxml.cElementTree ------------------------ - -**NOTE** ``defusedxml.cElementTree`` is deprecated and will be removed in a -future release. Import from ``defusedxml.ElementTree`` instead. - -parse(), iterparse(), fromstring(), XMLParser - - -defusedxml.ElementTree ------------------------ - -parse(), iterparse(), fromstring(), XMLParser - - -defusedxml.expatreader ----------------------- - -create_parser(), DefusedExpatParser - - -defusedxml.sax --------------- - -parse(), parseString(), make_parser() - - -defusedxml.expatbuilder ------------------------ - -parse(), parseString(), DefusedExpatBuilder, DefusedExpatBuilderNS - - -defusedxml.minidom ------------------- - -parse(), parseString() - - -defusedxml.pulldom ------------------- - -parse(), parseString() - - -defusedxml.xmlrpc ------------------ - -The fix is implemented as monkey patch for the stdlib's xmlrpc package (3.x) -or xmlrpclib module (2.x). The function `monkey_patch()` enables the fixes, -`unmonkey_patch()` removes the patch and puts the code in its former state. - -The monkey patch protects against XML related attacks as well as -decompression bombs and excessively large requests or responses. The default -setting is 30 MB for requests, responses and gzip decompression. You can -modify the default by changing the module variable `MAX_DATA`. A value of -`-1` disables the limit. - - -defusedxml.lxml ---------------- - -**DEPRECATED** The module is deprecated and will be removed in a future -release. - -The module acts as an *example* how you could protect code that uses -lxml.etree. It implements a custom Element class that filters out -Entity instances, a custom parser factory and a thread local storage for -parser instances. It also has a check_docinfo() function which inspects -a tree for internal or external DTDs and entity declarations. In order to -check for entities lxml > 3.0 is required. - -parse(), fromstring() -RestrictedElement, GlobalParserTLS, getDefaultParser(), check_docinfo() - - -defusedexpat -============ - -The `defusedexpat package`_ (`defusedexpat on PyPI`_) -comes with binary extensions and a -`modified expat`_ library instead of the standard `expat parser`_. It's -basically a stand-alone version of the patches for Python's standard -library C extensions. - -Modifications in expat ----------------------- - -new definitions:: - - XML_BOMB_PROTECTION - XML_DEFAULT_MAX_ENTITY_INDIRECTIONS - XML_DEFAULT_MAX_ENTITY_EXPANSIONS - XML_DEFAULT_RESET_DTD - -new XML_FeatureEnum members:: - - XML_FEATURE_MAX_ENTITY_INDIRECTIONS - XML_FEATURE_MAX_ENTITY_EXPANSIONS - XML_FEATURE_IGNORE_DTD - -new XML_Error members:: - - XML_ERROR_ENTITY_INDIRECTIONS - XML_ERROR_ENTITY_EXPANSION - -new API functions:: - - int XML_GetFeature(XML_Parser parser, - enum XML_FeatureEnum feature, - long *value); - int XML_SetFeature(XML_Parser parser, - enum XML_FeatureEnum feature, - long value); - int XML_GetFeatureDefault(enum XML_FeatureEnum feature, - long *value); - int XML_SetFeatureDefault(enum XML_FeatureEnum feature, - long value); - -XML_FEATURE_MAX_ENTITY_INDIRECTIONS - Limit the amount of indirections that are allowed to occur during the - expansion of a nested entity. A counter starts when an entity reference - is encountered. It resets after the entity is fully expanded. The limit - protects the parser against exponential entity expansion attacks (aka - billion laughs attack). When the limit is exceeded the parser stops and - fails with `XML_ERROR_ENTITY_INDIRECTIONS`. - A value of 0 disables the protection. - - Supported range - 0 .. UINT_MAX - Default - 40 - -XML_FEATURE_MAX_ENTITY_EXPANSIONS - Limit the total length of all entity expansions throughout the entire - document. The lengths of all entities are accumulated in a parser variable. - The setting protects against quadratic blowup attacks (lots of expansions - of a large entity declaration). When the sum of all entities exceeds - the limit, the parser stops and fails with `XML_ERROR_ENTITY_EXPANSION`. - A value of 0 disables the protection. - - Supported range - 0 .. UINT_MAX - Default - 8 MiB - -XML_FEATURE_RESET_DTD - Reset all DTD information after the block has been parsed. When - the flag is set (default: false) all DTD information after the - endDoctypeDeclHandler has been called. The flag can be set inside the - endDoctypeDeclHandler. Without DTD information any entity reference in - the document body leads to `XML_ERROR_UNDEFINED_ENTITY`. - - Supported range - 0, 1 - Default - 0 - - -How to avoid XML vulnerabilities -================================ - -Best practices --------------- - -* Don't allow DTDs -* Don't expand entities -* Don't resolve externals -* Limit parse depth -* Limit total input size -* Limit parse time -* Favor a SAX or iterparse-like parser for potential large data -* Validate and properly quote arguments to XSL transformations and - XPath queries -* Don't use XPath expression from untrusted sources -* Don't apply XSL transformations that come untrusted sources - -(based on Brad Hill's `Attacking XML Security`_) - - -Other things to consider -======================== - -XML, XML parsers and processing libraries have more features and possible -issue that could lead to DoS vulnerabilities or security exploits in -applications. I have compiled an incomplete list of theoretical issues that -need further research and more attention. The list is deliberately pessimistic -and a bit paranoid, too. It contains things that might go wrong under daffy -circumstances. - - -attribute blowup / hash collision attack ----------------------------------------- - -XML parsers may use an algorithm with quadratic runtime O(n :sup:`2`) to -handle attributes and namespaces. If it uses hash tables (dictionaries) to -store attributes and namespaces the implementation may be vulnerable to -hash collision attacks, thus reducing the performance to O(n :sup:`2`) again. -In either case an attacker is able to forge a denial of service attack with -an XML document that contains thousands upon thousands of attributes in -a single node. - -I haven't researched yet if expat, pyexpat or libxml2 are vulnerable. - - -decompression bomb ------------------- - -The issue of decompression bombs (aka `ZIP bomb`_) apply to all XML libraries -that can parse compressed XML stream like gzipped HTTP streams or LZMA-ed -files. For an attacker it can reduce the amount of transmitted data by three -magnitudes or more. Gzip is able to compress 1 GiB zeros to roughly 1 MB, -lzma is even better:: - - $ dd if=/dev/zero bs=1M count=1024 | gzip > zeros.gz - $ dd if=/dev/zero bs=1M count=1024 | lzma -z > zeros.xy - $ ls -sh zeros.* - 1020K zeros.gz - 148K zeros.xy - -None of Python's standard XML libraries decompress streams except for -``xmlrpclib``. The module is vulnerable -to decompression bombs. - -lxml can load and process compressed data through libxml2 transparently. -libxml2 can handle even very large blobs of compressed data efficiently -without using too much memory. But it doesn't protect applications from -decompression bombs. A carefully written SAX or iterparse-like approach can -be safe. - - -Processing Instruction ----------------------- - -`PI`_'s like:: - - - -may impose more threats for XML processing. It depends if and how a -processor handles processing instructions. The issue of URL retrieval with -network or local file access apply to processing instructions, too. - - -Other DTD features ------------------- - -`DTD`_ has more features like ````. I haven't researched how -these features may be a security threat. - - -XPath ------ - -XPath statements may introduce DoS vulnerabilities. Code should never execute -queries from untrusted sources. An attacker may also be able to create an XML -document that makes certain XPath queries costly or resource hungry. - - -XPath injection attacks ------------------------ - -XPath injeciton attacks pretty much work like SQL injection attacks. -Arguments to XPath queries must be quoted and validated properly, especially -when they are taken from the user. The page `Avoid the dangers of XPath injection`_ -list some ramifications of XPath injections. - -Python's standard library doesn't have XPath support. Lxml supports -parameterized XPath queries which does proper quoting. You just have to use -its xpath() method correctly:: - - # DON'T - >>> tree.xpath("/tag[@id='%s']" % value) - - # instead do - >>> tree.xpath("/tag[@id=$tagid]", tagid=name) - - -XInclude --------- - -`XML Inclusion`_ is another way to load and include external files:: - - - - - -This feature should be disabled when XML files from an untrusted source are -processed. Some Python XML libraries and libxml2 support XInclude but don't -have an option to sandbox inclusion and limit it to allowed directories. - - -XMLSchema location ------------------- - -A validating XML parser may download schema files from the information in a -``xsi:schemaLocation`` attribute. - -:: - - - - - -XSL Transformation ------------------- - -You should keep in mind that XSLT is a Turing complete language. Never -process XSLT code from unknown or untrusted source! XSLT processors may -allow you to interact with external resources in ways you can't even imagine. -Some processors even support extensions that allow read/write access to file -system, access to JRE objects or scripting with Jython. - -Example from `Attacking XML Security`_ for Xalan-J:: - - - - - - - - - - - -Related CVEs -============ - -CVE-2013-1664 - Unrestricted entity expansion induces DoS vulnerabilities in Python XML - libraries (XML bomb) - -CVE-2013-1665 - External entity expansion in Python XML libraries inflicts potential - security flaws and DoS vulnerabilities - - -Other languages / frameworks -============================= - -Several other programming languages and frameworks are vulnerable as well. A -couple of them are affected by the fact that libxml2 up to 2.9.0 has no -protection against quadratic blowup attacks. Most of them have potential -dangerous default settings for entity expansion and external entities, too. - -Perl ----- - -Perl's XML::Simple is vulnerable to quadratic entity expansion and external -entity expansion (both local and remote). - - -Ruby ----- - -Ruby's REXML document parser is vulnerable to entity expansion attacks -(both quadratic and exponential) but it doesn't do external entity -expansion by default. In order to counteract entity expansion you have to -disable the feature:: - - REXML::Document.entity_expansion_limit = 0 - -libxml-ruby and hpricot don't expand entities in their default configuration. - - -PHP ---- - -PHP's SimpleXML API is vulnerable to quadratic entity expansion and loads -entities from local and remote resources. The option ``LIBXML_NONET`` disables -network access but still allows local file access. ``LIBXML_NOENT`` seems to -have no effect on entity expansion in PHP 5.4.6. - - -C# / .NET / Mono ----------------- - -Information in `XML DoS and Defenses (MSDN)`_ suggest that .NET is -vulnerable with its default settings. The article contains code snippets -how to create a secure XML reader:: - - XmlReaderSettings settings = new XmlReaderSettings(); - settings.ProhibitDtd = false; - settings.MaxCharactersFromEntities = 1024; - settings.XmlResolver = null; - XmlReader reader = XmlReader.Create(stream, settings); - - -Java ----- - -Untested. The documentation of Xerces and its `Xerces SecurityMananger`_ -sounds like Xerces is also vulnerable to billion laugh attacks with its -default settings. It also does entity resolving when an -``org.xml.sax.EntityResolver`` is configured. I'm not yet sure about the -default setting here. - -Java specialists suggest to have a custom builder factory:: - - DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); - builderFactory.setXIncludeAware(False); - builderFactory.setExpandEntityReferences(False); - builderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, True); - # either - builderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", True); - # or if you need DTDs - builderFactory.setFeature("http://xml.org/sax/features/external-general-entities", False); - builderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", False); - builderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", False); - builderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", False); - - -TODO -==== - -* DOM: Use xml.dom.xmlbuilder options for entity handling -* SAX: take feature_external_ges and feature_external_pes (?) into account -* test experimental monkey patching of stdlib modules -* improve documentation - - -License -======= - -Copyright (c) 2013-2017 by Christian Heimes - -Licensed to PSF under a Contributor Agreement. - -See https://www.python.org/psf/license for licensing details. - - -Acknowledgements -================ - -Brett Cannon (Python Core developer) - review and code cleanup - -Antoine Pitrou (Python Core developer) - code review - -Aaron Patterson, Ben Murphy and Michael Koziarski (Ruby community) - Many thanks to Aaron, Ben and Michael from the Ruby community for their - report and assistance. - -Thierry Carrez (OpenStack) - Many thanks to Thierry for his report to the Python Security Response - Team on behalf of the OpenStack security team. - -Carl Meyer (Django) - Many thanks to Carl for his report to PSRT on behalf of the Django security - team. - -Daniel Veillard (libxml2) - Many thanks to Daniel for his insight and assistance with libxml2. - -semantics GmbH (https://www.semantics.de/) - Many thanks to my employer semantics for letting me work on the issue - during working hours as part of semantics's open source initiative. - - -References -========== - -* `XML DoS and Defenses (MSDN)`_ -* `Billion Laughs`_ on Wikipedia -* `ZIP bomb`_ on Wikipedia -* `Configure SAX parsers for secure processing`_ -* `Testing for XML Injection`_ - -.. _defusedxml package: https://github.com/tiran/defusedxml -.. _defusedxml on PyPI: https://pypi.python.org/pypi/defusedxml -.. _defusedexpat package: https://github.com/tiran/defusedexpat -.. _defusedexpat on PyPI: https://pypi.python.org/pypi/defusedexpat -.. _modified expat: https://github.com/tiran/expat -.. _expat parser: http://expat.sourceforge.net/ -.. _Attacking XML Security: https://www.isecpartners.com/media/12976/iSEC-HILL-Attacking-XML-Security-bh07.pdf -.. _Billion Laughs: https://en.wikipedia.org/wiki/Billion_laughs -.. _XML DoS and Defenses (MSDN): https://msdn.microsoft.com/en-us/magazine/ee335713.aspx -.. _ZIP bomb: https://en.wikipedia.org/wiki/Zip_bomb -.. _DTD: https://en.wikipedia.org/wiki/Document_Type_Definition -.. _PI: https://en.wikipedia.org/wiki/Processing_Instruction -.. _Avoid the dangers of XPath injection: http://www.ibm.com/developerworks/xml/library/x-xpathinjection/index.html -.. _Configure SAX parsers for secure processing: http://www.ibm.com/developerworks/xml/library/x-tipcfsx/index.html -.. _Testing for XML Injection: https://www.owasp.org/index.php/Testing_for_XML_Injection_(OWASP-DV-008) -.. _Xerces SecurityMananger: https://xerces.apache.org/xerces2-j/javadocs/xerces2/org/apache/xerces/util/SecurityManager.html -.. _XML Inclusion: https://www.w3.org/TR/xinclude/#include_element - -Changelog -========= - -defusedxml 0.7.1 ---------------------- - -*Release date: 08-Mar-2021* - -- Fix regression ``defusedxml.ElementTree.ParseError`` (#63) - The ``ParseError`` exception is now the same class object as - ``xml.etree.ElementTree.ParseError`` again. - - -defusedxml 0.7.0 ----------------- - -*Release date: 4-Mar-2021* - -- No changes - - -defusedxml 0.7.0rc2 -------------------- - -*Release date: 12-Jan-2021* - -- Re-add and deprecate ``defusedxml.cElementTree`` -- Use GitHub Actions instead of TravisCI -- Restore ``ElementTree`` attribute of ``xml.etree`` module after patching - -defusedxml 0.7.0rc1 -------------------- - -*Release date: 04-May-2020* - -- Add support for Python 3.9 -- ``defusedxml.cElementTree`` is not available with Python 3.9. -- Python 2 is deprecate. Support for Python 2 will be removed in 0.8.0. - - -defusedxml 0.6.0 ----------------- - -*Release date: 17-Apr-2019* - -- Increase test coverage. -- Add badges to README. - - -defusedxml 0.6.0rc1 -------------------- - -*Release date: 14-Apr-2019* - -- Test on Python 3.7 stable and 3.8-dev -- Drop support for Python 3.4 -- No longer pass *html* argument to XMLParse. It has been deprecated and - ignored for a long time. The DefusedXMLParser still takes a html argument. - A deprecation warning is issued when the argument is False and a TypeError - when it's True. -- defusedxml now fails early when pyexpat stdlib module is not available or - broken. -- defusedxml.ElementTree.__all__ now lists ParseError as public attribute. -- The defusedxml.ElementTree and defusedxml.cElementTree modules had a typo - and used XMLParse instead of XMLParser as an alias for DefusedXMLParser. - Both the old and fixed name are now available. - - -defusedxml 0.5.0 ----------------- - -*Release date: 07-Feb-2017* - -- No changes - - -defusedxml 0.5.0.rc1 --------------------- - -*Release date: 28-Jan-2017* - -- Add compatibility with Python 3.6 -- Drop support for Python 2.6, 3.1, 3.2, 3.3 -- Fix lxml tests (XMLSyntaxError: Detected an entity reference loop) - - -defusedxml 0.4.1 ----------------- - -*Release date: 28-Mar-2013* - -- Add more demo exploits, e.g. python_external.py and Xalan XSLT demos. -- Improved documentation. - - -defusedxml 0.4 --------------- - -*Release date: 25-Feb-2013* - -- As per http://seclists.org/oss-sec/2013/q1/340 please REJECT - CVE-2013-0278, CVE-2013-0279 and CVE-2013-0280 and use CVE-2013-1664, - CVE-2013-1665 for OpenStack/etc. -- Add missing parser_list argument to sax.make_parser(). The argument is - ignored, though. (thanks to Florian Apolloner) -- Add demo exploit for external entity attack on Python's SAX parser, XML-RPC - and WebDAV. - - -defusedxml 0.3 --------------- - -*Release date: 19-Feb-2013* - -- Improve documentation - - -defusedxml 0.2 --------------- - -*Release date: 15-Feb-2013* - -- Rename ExternalEntitiesForbidden to ExternalReferenceForbidden -- Rename defusedxml.lxml.check_dtd() to check_docinfo() -- Unify argument names in callbacks -- Add arguments and formatted representation to exceptions -- Add forbid_external argument to all functions and classes -- More tests -- LOTS of documentation -- Add example code for other languages (Ruby, Perl, PHP) and parsers (Genshi) -- Add protection against XML and gzip attacks to xmlrpclib - -defusedxml 0.1 --------------- - -*Release date: 08-Feb-2013* - -- Initial and internal release for PSRT review - - diff --git a/apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/RECORD b/apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/RECORD deleted file mode 100755 index 6c3d73e3..00000000 --- a/apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/RECORD +++ /dev/null @@ -1,18 +0,0 @@ -defusedxml-0.7.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -defusedxml-0.7.1.dist-info/LICENSE,sha256=uAzp2oxCofkQeWJ_u-K_JyEK4Qig_-Xwd9WwjgdsJMg,2409 -defusedxml-0.7.1.dist-info/METADATA,sha256=Np0872SHDa-En7pxHLjQWn7-PI2asPdjrcNAef43i7E,32518 -defusedxml-0.7.1.dist-info/RECORD,, -defusedxml-0.7.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -defusedxml-0.7.1.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110 -defusedxml-0.7.1.dist-info/top_level.txt,sha256=QGHa90F50pVKhWSFlERI0jtSKtqDiGyfeZX7dQNZAAw,11 -defusedxml/ElementTree.py,sha256=GLSqpCz58oXGPGyzf_HylsPS9_dcGVP5SN4dK7yvyPw,4640 -defusedxml/__init__.py,sha256=RczeaVJG64p2Fgy1jlCzbuRdchEPnEaCBrxgk8JJ_pM,1444 -defusedxml/cElementTree.py,sha256=PpaKMh3rU29sY8amAK4fzHQKl8gcAYD0h1LCoW62Rtk,1449 -defusedxml/common.py,sha256=3d26jNW4fNXzgjWhvUfs83Afiz5EVxFDupQbugkSMZc,4036 -defusedxml/expatbuilder.py,sha256=b4Q05vsBMJ5StkiTFf4my2rGGo1gZyEl_hC5MeFTOAA,3732 -defusedxml/expatreader.py,sha256=KOpSrwkSvj5SGOY9pTXOM26Dnz00rsJt33WueVvzpvc,2196 -defusedxml/lxml.py,sha256=HW-LFKdrfMRzHdi0Vcucq4-n8yz7v_OQwEQWFg1JQYA,4940 -defusedxml/minidom.py,sha256=3QcgygVwJqcWDQ3IZ2iol8zsH4cx3BRX70SPcd0bG2g,1884 -defusedxml/pulldom.py,sha256=DYj2D2lc7xoxZ38gfzujXmdznd8ovzDqGFXqyXbtxjk,1170 -defusedxml/sax.py,sha256=-SF08Msc2mWEYAMw62pJ5FMwWccOctFSnQwDLYLLlVE,1477 -defusedxml/xmlrpc.py,sha256=7rZQey3tqXcc1hrrM3RprOICU6fiFny9B9l4nmTioxA,5364 diff --git a/apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/REQUESTED b/apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/REQUESTED deleted file mode 100755 index e69de29b..00000000 diff --git a/apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/WHEEL b/apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/WHEEL deleted file mode 100755 index ef99c6cf..00000000 --- a/apps/bitwarden_event_logs/lib/defusedxml-0.7.1.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.34.2) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/apps/bitwarden_event_logs/lib/defusedxml/ElementTree.py b/apps/bitwarden_event_logs/lib/defusedxml/ElementTree.py deleted file mode 100755 index 5ba765f1..00000000 --- a/apps/bitwarden_event_logs/lib/defusedxml/ElementTree.py +++ /dev/null @@ -1,154 +0,0 @@ -# defusedxml -# -# Copyright (c) 2013 by Christian Heimes -# Licensed to PSF under a Contributor Agreement. -# See https://www.python.org/psf/license for licensing details. -"""Defused xml.etree.ElementTree facade -""" -from __future__ import print_function, absolute_import - -import sys -import warnings -from xml.etree.ElementTree import ParseError -from xml.etree.ElementTree import TreeBuilder as _TreeBuilder -from xml.etree.ElementTree import parse as _parse -from xml.etree.ElementTree import tostring - -from .common import PY3 - -if PY3: - import importlib -else: - from xml.etree.ElementTree import XMLParser as _XMLParser - from xml.etree.ElementTree import iterparse as _iterparse - - -from .common import ( - DTDForbidden, - EntitiesForbidden, - ExternalReferenceForbidden, - _generate_etree_functions, -) - -__origin__ = "xml.etree.ElementTree" - - -def _get_py3_cls(): - """Python 3.3 hides the pure Python code but defusedxml requires it. - - The code is based on test.support.import_fresh_module(). - """ - pymodname = "xml.etree.ElementTree" - cmodname = "_elementtree" - - pymod = sys.modules.pop(pymodname, None) - cmod = sys.modules.pop(cmodname, None) - - sys.modules[cmodname] = None - try: - pure_pymod = importlib.import_module(pymodname) - finally: - # restore module - sys.modules[pymodname] = pymod - if cmod is not None: - sys.modules[cmodname] = cmod - else: - sys.modules.pop(cmodname, None) - # restore attribute on original package - etree_pkg = sys.modules["xml.etree"] - if pymod is not None: - etree_pkg.ElementTree = pymod - elif hasattr(etree_pkg, "ElementTree"): - del etree_pkg.ElementTree - - _XMLParser = pure_pymod.XMLParser - _iterparse = pure_pymod.iterparse - # patch pure module to use ParseError from C extension - pure_pymod.ParseError = ParseError - - return _XMLParser, _iterparse - - -if PY3: - _XMLParser, _iterparse = _get_py3_cls() - - -_sentinel = object() - - -class DefusedXMLParser(_XMLParser): - def __init__( - self, - html=_sentinel, - target=None, - encoding=None, - forbid_dtd=False, - forbid_entities=True, - forbid_external=True, - ): - # Python 2.x old style class - _XMLParser.__init__(self, target=target, encoding=encoding) - if html is not _sentinel: - # the 'html' argument has been deprecated and ignored in all - # supported versions of Python. Python 3.8 finally removed it. - if html: - raise TypeError("'html=True' is no longer supported.") - else: - warnings.warn( - "'html' keyword argument is no longer supported. Pass " - "in arguments as keyword arguments.", - category=DeprecationWarning, - ) - - self.forbid_dtd = forbid_dtd - self.forbid_entities = forbid_entities - self.forbid_external = forbid_external - if PY3: - parser = self.parser - else: - parser = self._parser - if self.forbid_dtd: - parser.StartDoctypeDeclHandler = self.defused_start_doctype_decl - if self.forbid_entities: - parser.EntityDeclHandler = self.defused_entity_decl - parser.UnparsedEntityDeclHandler = self.defused_unparsed_entity_decl - if self.forbid_external: - parser.ExternalEntityRefHandler = self.defused_external_entity_ref_handler - - def defused_start_doctype_decl(self, name, sysid, pubid, has_internal_subset): - raise DTDForbidden(name, sysid, pubid) - - def defused_entity_decl( - self, name, is_parameter_entity, value, base, sysid, pubid, notation_name - ): - raise EntitiesForbidden(name, value, base, sysid, pubid, notation_name) - - def defused_unparsed_entity_decl(self, name, base, sysid, pubid, notation_name): - # expat 1.2 - raise EntitiesForbidden(name, None, base, sysid, pubid, notation_name) # pragma: no cover - - def defused_external_entity_ref_handler(self, context, base, sysid, pubid): - raise ExternalReferenceForbidden(context, base, sysid, pubid) - - -# aliases -# XMLParse is a typo, keep it for backwards compatibility -XMLTreeBuilder = XMLParse = XMLParser = DefusedXMLParser - -parse, iterparse, fromstring = _generate_etree_functions( - DefusedXMLParser, _TreeBuilder, _parse, _iterparse -) -XML = fromstring - - -__all__ = [ - "ParseError", - "XML", - "XMLParse", - "XMLParser", - "XMLTreeBuilder", - "fromstring", - "iterparse", - "parse", - "tostring", -] diff --git a/apps/bitwarden_event_logs/lib/defusedxml/__init__.py b/apps/bitwarden_event_logs/lib/defusedxml/__init__.py deleted file mode 100755 index 4b5a2300..00000000 --- a/apps/bitwarden_event_logs/lib/defusedxml/__init__.py +++ /dev/null @@ -1,67 +0,0 @@ -# defusedxml -# -# Copyright (c) 2013 by Christian Heimes -# Licensed to PSF under a Contributor Agreement. -# See https://www.python.org/psf/license for licensing details. -"""Defuse XML bomb denial of service vulnerabilities -""" -from __future__ import print_function, absolute_import - -import warnings - -from .common import ( - DefusedXmlException, - DTDForbidden, - EntitiesForbidden, - ExternalReferenceForbidden, - NotSupportedError, - _apply_defusing, -) - - -def defuse_stdlib(): - """Monkey patch and defuse all stdlib packages - - :warning: The monkey patch is an EXPERIMETNAL feature. - """ - defused = {} - - with warnings.catch_warnings(): - from . import cElementTree - from . import ElementTree - from . import minidom - from . import pulldom - from . import sax - from . import expatbuilder - from . import expatreader - from . import xmlrpc - - xmlrpc.monkey_patch() - defused[xmlrpc] = None - - defused_mods = [ - cElementTree, - ElementTree, - minidom, - pulldom, - sax, - expatbuilder, - expatreader, - ] - - for defused_mod in defused_mods: - stdlib_mod = _apply_defusing(defused_mod) - defused[defused_mod] = stdlib_mod - - return defused - - -__version__ = "0.7.1" - -__all__ = [ - "DefusedXmlException", - "DTDForbidden", - "EntitiesForbidden", - "ExternalReferenceForbidden", - "NotSupportedError", -] diff --git a/apps/bitwarden_event_logs/lib/defusedxml/cElementTree.py b/apps/bitwarden_event_logs/lib/defusedxml/cElementTree.py deleted file mode 100755 index 84670c68..00000000 --- a/apps/bitwarden_event_logs/lib/defusedxml/cElementTree.py +++ /dev/null @@ -1,62 +0,0 @@ -# defusedxml -# -# Copyright (c) 2013 by Christian Heimes -# Licensed to PSF under a Contributor Agreement. -# See https://www.python.org/psf/license for licensing details. -"""Defused xml.etree.cElementTree -""" -from __future__ import absolute_import - -import warnings - -from .common import _generate_etree_functions - -from xml.etree.cElementTree import TreeBuilder as _TreeBuilder -from xml.etree.cElementTree import parse as _parse -from xml.etree.cElementTree import tostring - -# iterparse from ElementTree! -from xml.etree.ElementTree import iterparse as _iterparse - -# This module is an alias for ElementTree just like xml.etree.cElementTree -from .ElementTree import ( - XML, - XMLParse, - XMLParser, - XMLTreeBuilder, - fromstring, - iterparse, - parse, - tostring, - DefusedXMLParser, - ParseError, -) - -__origin__ = "xml.etree.cElementTree" - - -warnings.warn( - "defusedxml.cElementTree is deprecated, import from defusedxml.ElementTree instead.", - category=DeprecationWarning, - stacklevel=2, -) - -# XMLParse is a typo, keep it for backwards compatibility -XMLTreeBuilder = XMLParse = XMLParser = DefusedXMLParser - -parse, iterparse, fromstring = _generate_etree_functions( - DefusedXMLParser, _TreeBuilder, _parse, _iterparse -) -XML = fromstring - -__all__ = [ - "ParseError", - "XML", - "XMLParse", - "XMLParser", - "XMLTreeBuilder", - "fromstring", - "iterparse", - "parse", - "tostring", -] diff --git a/apps/bitwarden_event_logs/lib/defusedxml/common.py b/apps/bitwarden_event_logs/lib/defusedxml/common.py deleted file mode 100755 index 5ceda1fb..00000000 --- a/apps/bitwarden_event_logs/lib/defusedxml/common.py +++ /dev/null @@ -1,129 +0,0 @@ -# defusedxml -# -# Copyright (c) 2013 by Christian Heimes -# Licensed to PSF under a Contributor Agreement. -# See https://www.python.org/psf/license for licensing details. -"""Common constants, exceptions and helpe functions -""" -import sys -import xml.parsers.expat - -PY3 = sys.version_info[0] == 3 - -# Fail early when pyexpat is not installed correctly -if not hasattr(xml.parsers.expat, "ParserCreate"): - raise ImportError("pyexpat") # pragma: no cover - - -class DefusedXmlException(ValueError): - """Base exception""" - - def __repr__(self): - return str(self) - - -class DTDForbidden(DefusedXmlException): - """Document type definition is forbidden""" - - def __init__(self, name, sysid, pubid): - super(DTDForbidden, self).__init__() - self.name = name - self.sysid = sysid - self.pubid = pubid - - def __str__(self): - tpl = "DTDForbidden(name='{}', system_id={!r}, public_id={!r})" - return tpl.format(self.name, self.sysid, self.pubid) - - -class EntitiesForbidden(DefusedXmlException): - """Entity definition is forbidden""" - - def __init__(self, name, value, base, sysid, pubid, notation_name): - super(EntitiesForbidden, self).__init__() - self.name = name - self.value = value - self.base = base - self.sysid = sysid - self.pubid = pubid - self.notation_name = notation_name - - def __str__(self): - tpl = "EntitiesForbidden(name='{}', system_id={!r}, public_id={!r})" - return tpl.format(self.name, self.sysid, self.pubid) - - -class ExternalReferenceForbidden(DefusedXmlException): - """Resolving an external reference is forbidden""" - - def __init__(self, context, base, sysid, pubid): - super(ExternalReferenceForbidden, self).__init__() - self.context = context - self.base = base - self.sysid = sysid - self.pubid = pubid - - def __str__(self): - tpl = "ExternalReferenceForbidden(system_id='{}', public_id={})" - return tpl.format(self.sysid, self.pubid) - - -class NotSupportedError(DefusedXmlException): - """The operation is not supported""" - - -def _apply_defusing(defused_mod): - assert defused_mod is sys.modules[defused_mod.__name__] - stdlib_name = defused_mod.__origin__ - __import__(stdlib_name, {}, {}, ["*"]) - stdlib_mod = sys.modules[stdlib_name] - stdlib_names = set(dir(stdlib_mod)) - for name, obj in vars(defused_mod).items(): - if name.startswith("_") or name not in stdlib_names: - continue - setattr(stdlib_mod, name, obj) - return stdlib_mod - - -def _generate_etree_functions(DefusedXMLParser, _TreeBuilder, _parse, _iterparse): - """Factory for functions needed by etree, dependent on whether - cElementTree or ElementTree is used.""" - - def parse(source, parser=None, forbid_dtd=False, forbid_entities=True, forbid_external=True): - if parser is None: - parser = DefusedXMLParser( - target=_TreeBuilder(), - forbid_dtd=forbid_dtd, - forbid_entities=forbid_entities, - forbid_external=forbid_external, - ) - return _parse(source, parser) - - def iterparse( - source, - events=None, - parser=None, - forbid_dtd=False, - forbid_entities=True, - forbid_external=True, - ): - if parser is None: - parser = DefusedXMLParser( - target=_TreeBuilder(), - forbid_dtd=forbid_dtd, - forbid_entities=forbid_entities, - forbid_external=forbid_external, - ) - return _iterparse(source, events, parser) - - def fromstring(text, forbid_dtd=False, forbid_entities=True, forbid_external=True): - parser = DefusedXMLParser( - target=_TreeBuilder(), - forbid_dtd=forbid_dtd, - forbid_entities=forbid_entities, - forbid_external=forbid_external, - ) - parser.feed(text) - return parser.close() - - return parse, iterparse, fromstring diff --git a/apps/bitwarden_event_logs/lib/defusedxml/expatbuilder.py b/apps/bitwarden_event_logs/lib/defusedxml/expatbuilder.py deleted file mode 100755 index 7bfc57e4..00000000 --- a/apps/bitwarden_event_logs/lib/defusedxml/expatbuilder.py +++ /dev/null @@ -1,107 +0,0 @@ -# defusedxml -# -# Copyright (c) 2013 by Christian Heimes -# Licensed to PSF under a Contributor Agreement. -# See https://www.python.org/psf/license for licensing details. -"""Defused xml.dom.expatbuilder -""" -from __future__ import print_function, absolute_import - -from xml.dom.expatbuilder import ExpatBuilder as _ExpatBuilder -from xml.dom.expatbuilder import Namespaces as _Namespaces - -from .common import DTDForbidden, EntitiesForbidden, ExternalReferenceForbidden - -__origin__ = "xml.dom.expatbuilder" - - -class DefusedExpatBuilder(_ExpatBuilder): - """Defused document builder""" - - def __init__( - self, options=None, forbid_dtd=False, forbid_entities=True, forbid_external=True - ): - _ExpatBuilder.__init__(self, options) - self.forbid_dtd = forbid_dtd - self.forbid_entities = forbid_entities - self.forbid_external = forbid_external - - def defused_start_doctype_decl(self, name, sysid, pubid, has_internal_subset): - raise DTDForbidden(name, sysid, pubid) - - def defused_entity_decl( - self, name, is_parameter_entity, value, base, sysid, pubid, notation_name - ): - raise EntitiesForbidden(name, value, base, sysid, pubid, notation_name) - - def defused_unparsed_entity_decl(self, name, base, sysid, pubid, notation_name): - # expat 1.2 - raise EntitiesForbidden(name, None, base, sysid, pubid, notation_name) # pragma: no cover - - def defused_external_entity_ref_handler(self, context, base, sysid, pubid): - raise ExternalReferenceForbidden(context, base, sysid, pubid) - - def install(self, parser): - _ExpatBuilder.install(self, parser) - - if self.forbid_dtd: - parser.StartDoctypeDeclHandler = self.defused_start_doctype_decl - if self.forbid_entities: - # if self._options.entities: - parser.EntityDeclHandler = self.defused_entity_decl - parser.UnparsedEntityDeclHandler = self.defused_unparsed_entity_decl - if self.forbid_external: - parser.ExternalEntityRefHandler = self.defused_external_entity_ref_handler - - -class DefusedExpatBuilderNS(_Namespaces, DefusedExpatBuilder): - """Defused document builder that supports namespaces.""" - - def install(self, parser): - DefusedExpatBuilder.install(self, parser) - if self._options.namespace_declarations: - parser.StartNamespaceDeclHandler = self.start_namespace_decl_handler - - def reset(self): - DefusedExpatBuilder.reset(self) - self._initNamespaces() - - -def parse(file, namespaces=True, forbid_dtd=False, forbid_entities=True, forbid_external=True): - """Parse a document, returning the resulting Document node. - - 'file' may be either a file name or an open file object. - """ - if namespaces: - build_builder = DefusedExpatBuilderNS - else: - build_builder = DefusedExpatBuilder - builder = build_builder( - forbid_dtd=forbid_dtd, forbid_entities=forbid_entities, forbid_external=forbid_external - ) - - if isinstance(file, str): - fp = open(file, "rb") - try: - result = builder.parseFile(fp) - finally: - fp.close() - else: - result = builder.parseFile(file) - return result - - -def parseString( - string, namespaces=True, forbid_dtd=False, forbid_entities=True, forbid_external=True -): - """Parse a document from a string, returning the resulting - Document node. - """ - if namespaces: - build_builder = DefusedExpatBuilderNS - else: - build_builder = DefusedExpatBuilder - builder = build_builder( - forbid_dtd=forbid_dtd, forbid_entities=forbid_entities, forbid_external=forbid_external - ) - return builder.parseString(string) diff --git a/apps/bitwarden_event_logs/lib/defusedxml/expatreader.py b/apps/bitwarden_event_logs/lib/defusedxml/expatreader.py deleted file mode 100755 index 890e1d16..00000000 --- a/apps/bitwarden_event_logs/lib/defusedxml/expatreader.py +++ /dev/null @@ -1,61 +0,0 @@ -# defusedxml -# -# Copyright (c) 2013 by Christian Heimes -# Licensed to PSF under a Contributor Agreement. -# See https://www.python.org/psf/license for licensing details. -"""Defused xml.sax.expatreader -""" -from __future__ import print_function, absolute_import - -from xml.sax.expatreader import ExpatParser as _ExpatParser - -from .common import DTDForbidden, EntitiesForbidden, ExternalReferenceForbidden - -__origin__ = "xml.sax.expatreader" - - -class DefusedExpatParser(_ExpatParser): - """Defused SAX driver for the pyexpat C module.""" - - def __init__( - self, - namespaceHandling=0, - bufsize=2 ** 16 - 20, - forbid_dtd=False, - forbid_entities=True, - forbid_external=True, - ): - _ExpatParser.__init__(self, namespaceHandling, bufsize) - self.forbid_dtd = forbid_dtd - self.forbid_entities = forbid_entities - self.forbid_external = forbid_external - - def defused_start_doctype_decl(self, name, sysid, pubid, has_internal_subset): - raise DTDForbidden(name, sysid, pubid) - - def defused_entity_decl( - self, name, is_parameter_entity, value, base, sysid, pubid, notation_name - ): - raise EntitiesForbidden(name, value, base, sysid, pubid, notation_name) - - def defused_unparsed_entity_decl(self, name, base, sysid, pubid, notation_name): - # expat 1.2 - raise EntitiesForbidden(name, None, base, sysid, pubid, notation_name) # pragma: no cover - - def defused_external_entity_ref_handler(self, context, base, sysid, pubid): - raise ExternalReferenceForbidden(context, base, sysid, pubid) - - def reset(self): - _ExpatParser.reset(self) - parser = self._parser - if self.forbid_dtd: - parser.StartDoctypeDeclHandler = self.defused_start_doctype_decl - if self.forbid_entities: - parser.EntityDeclHandler = self.defused_entity_decl - parser.UnparsedEntityDeclHandler = self.defused_unparsed_entity_decl - if self.forbid_external: - parser.ExternalEntityRefHandler = self.defused_external_entity_ref_handler - - -def create_parser(*args, **kwargs): - return DefusedExpatParser(*args, **kwargs) diff --git a/apps/bitwarden_event_logs/lib/defusedxml/lxml.py b/apps/bitwarden_event_logs/lib/defusedxml/lxml.py deleted file mode 100755 index 99d5be93..00000000 --- a/apps/bitwarden_event_logs/lib/defusedxml/lxml.py +++ /dev/null @@ -1,153 +0,0 @@ -# defusedxml -# -# Copyright (c) 2013 by Christian Heimes -# Licensed to PSF under a Contributor Agreement. -# See https://www.python.org/psf/license for licensing details. -"""DEPRECATED Example code for lxml.etree protection - -The code has NO protection against decompression bombs. -""" -from __future__ import print_function, absolute_import - -import threading -import warnings - -from lxml import etree as _etree - -from .common import DTDForbidden, EntitiesForbidden, NotSupportedError - -LXML3 = _etree.LXML_VERSION[0] >= 3 - -__origin__ = "lxml.etree" - -tostring = _etree.tostring - - -warnings.warn( - "defusedxml.lxml is no longer supported and will be removed in a future release.", - category=DeprecationWarning, - stacklevel=2, -) - - -class RestrictedElement(_etree.ElementBase): - """A restricted Element class that filters out instances of some classes""" - - __slots__ = () - # blacklist = (etree._Entity, etree._ProcessingInstruction, etree._Comment) - blacklist = _etree._Entity - - def _filter(self, iterator): - blacklist = self.blacklist - for child in iterator: - if isinstance(child, blacklist): - continue - yield child - - def __iter__(self): - iterator = super(RestrictedElement, self).__iter__() - return self._filter(iterator) - - def iterchildren(self, tag=None, reversed=False): - iterator = super(RestrictedElement, self).iterchildren(tag=tag, reversed=reversed) - return self._filter(iterator) - - def iter(self, tag=None, *tags): - iterator = super(RestrictedElement, self).iter(tag=tag, *tags) - return self._filter(iterator) - - def iterdescendants(self, tag=None, *tags): - iterator = super(RestrictedElement, self).iterdescendants(tag=tag, *tags) - return self._filter(iterator) - - def itersiblings(self, tag=None, preceding=False): - iterator = super(RestrictedElement, self).itersiblings(tag=tag, preceding=preceding) - return self._filter(iterator) - - def getchildren(self): - iterator = super(RestrictedElement, self).__iter__() - return list(self._filter(iterator)) - - def getiterator(self, tag=None): - iterator = super(RestrictedElement, self).getiterator(tag) - return self._filter(iterator) - - -class GlobalParserTLS(threading.local): - """Thread local context for custom parser instances""" - - parser_config = { - "resolve_entities": False, - # 'remove_comments': True, - # 'remove_pis': True, - } - - element_class = RestrictedElement - - def createDefaultParser(self): - parser = _etree.XMLParser(**self.parser_config) - element_class = self.element_class - if self.element_class is not None: - lookup = _etree.ElementDefaultClassLookup(element=element_class) - parser.set_element_class_lookup(lookup) - return parser - - def setDefaultParser(self, parser): - self._default_parser = parser - - def getDefaultParser(self): - parser = getattr(self, "_default_parser", None) - if parser is None: - parser = self.createDefaultParser() - self.setDefaultParser(parser) - return parser - - -_parser_tls = GlobalParserTLS() -getDefaultParser = _parser_tls.getDefaultParser - - -def check_docinfo(elementtree, forbid_dtd=False, forbid_entities=True): - """Check docinfo of an element tree for DTD and entity declarations - - The check for entity declarations needs lxml 3 or newer. lxml 2.x does - not support dtd.iterentities(). - """ - docinfo = elementtree.docinfo - if docinfo.doctype: - if forbid_dtd: - raise DTDForbidden(docinfo.doctype, docinfo.system_url, docinfo.public_id) - if forbid_entities and not LXML3: - # lxml < 3 has no iterentities() - raise NotSupportedError("Unable to check for entity declarations " "in lxml 2.x") - - if forbid_entities: - for dtd in docinfo.internalDTD, docinfo.externalDTD: - if dtd is None: - continue - for entity in dtd.iterentities(): - raise EntitiesForbidden(entity.name, entity.content, None, None, None, None) - - -def parse(source, parser=None, base_url=None, forbid_dtd=False, forbid_entities=True): - if parser is None: - parser = getDefaultParser() - elementtree = _etree.parse(source, parser, base_url=base_url) - check_docinfo(elementtree, forbid_dtd, forbid_entities) - return elementtree - - -def fromstring(text, parser=None, base_url=None, forbid_dtd=False, forbid_entities=True): - if parser is None: - parser = getDefaultParser() - rootelement = _etree.fromstring(text, parser, base_url=base_url) - elementtree = rootelement.getroottree() - check_docinfo(elementtree, forbid_dtd, forbid_entities) - return rootelement - - -XML = fromstring - - -def iterparse(*args, **kwargs): - raise NotSupportedError("defused lxml.etree.iterparse not available") diff --git a/apps/bitwarden_event_logs/lib/defusedxml/minidom.py b/apps/bitwarden_event_logs/lib/defusedxml/minidom.py deleted file mode 100755 index 78033b6c..00000000 --- a/apps/bitwarden_event_logs/lib/defusedxml/minidom.py +++ /dev/null @@ -1,63 +0,0 @@ -# defusedxml -# -# Copyright (c) 2013 by Christian Heimes -# Licensed to PSF under a Contributor Agreement. -# See https://www.python.org/psf/license for licensing details. -"""Defused xml.dom.minidom -""" -from __future__ import print_function, absolute_import - -from xml.dom.minidom import _do_pulldom_parse -from . import expatbuilder as _expatbuilder -from . import pulldom as _pulldom - -__origin__ = "xml.dom.minidom" - - -def parse( - file, parser=None, bufsize=None, forbid_dtd=False, forbid_entities=True, forbid_external=True -): - """Parse a file into a DOM by filename or file object.""" - if parser is None and not bufsize: - return _expatbuilder.parse( - file, - forbid_dtd=forbid_dtd, - forbid_entities=forbid_entities, - forbid_external=forbid_external, - ) - else: - return _do_pulldom_parse( - _pulldom.parse, - (file,), - { - "parser": parser, - "bufsize": bufsize, - "forbid_dtd": forbid_dtd, - "forbid_entities": forbid_entities, - "forbid_external": forbid_external, - }, - ) - - -def parseString( - string, parser=None, forbid_dtd=False, forbid_entities=True, forbid_external=True -): - """Parse a file into a DOM from a string.""" - if parser is None: - return _expatbuilder.parseString( - string, - forbid_dtd=forbid_dtd, - forbid_entities=forbid_entities, - forbid_external=forbid_external, - ) - else: - return _do_pulldom_parse( - _pulldom.parseString, - (string,), - { - "parser": parser, - "forbid_dtd": forbid_dtd, - "forbid_entities": forbid_entities, - "forbid_external": forbid_external, - }, - ) diff --git a/apps/bitwarden_event_logs/lib/defusedxml/pulldom.py b/apps/bitwarden_event_logs/lib/defusedxml/pulldom.py deleted file mode 100755 index e3b10a46..00000000 --- a/apps/bitwarden_event_logs/lib/defusedxml/pulldom.py +++ /dev/null @@ -1,41 +0,0 @@ -# defusedxml -# -# Copyright (c) 2013 by Christian Heimes -# Licensed to PSF under a Contributor Agreement. -# See https://www.python.org/psf/license for licensing details. -"""Defused xml.dom.pulldom -""" -from __future__ import print_function, absolute_import - -from xml.dom.pulldom import parse as _parse -from xml.dom.pulldom import parseString as _parseString -from .sax import make_parser - -__origin__ = "xml.dom.pulldom" - - -def parse( - stream_or_string, - parser=None, - bufsize=None, - forbid_dtd=False, - forbid_entities=True, - forbid_external=True, -): - if parser is None: - parser = make_parser() - parser.forbid_dtd = forbid_dtd - parser.forbid_entities = forbid_entities - parser.forbid_external = forbid_external - return _parse(stream_or_string, parser, bufsize) - - -def parseString( - string, parser=None, forbid_dtd=False, forbid_entities=True, forbid_external=True -): - if parser is None: - parser = make_parser() - parser.forbid_dtd = forbid_dtd - parser.forbid_entities = forbid_entities - parser.forbid_external = forbid_external - return _parseString(string, parser) diff --git a/apps/bitwarden_event_logs/lib/defusedxml/sax.py b/apps/bitwarden_event_logs/lib/defusedxml/sax.py deleted file mode 100755 index b2786f74..00000000 --- a/apps/bitwarden_event_logs/lib/defusedxml/sax.py +++ /dev/null @@ -1,60 +0,0 @@ -# defusedxml -# -# Copyright (c) 2013 by Christian Heimes -# Licensed to PSF under a Contributor Agreement. -# See https://www.python.org/psf/license for licensing details. -"""Defused xml.sax -""" -from __future__ import print_function, absolute_import - -from xml.sax import InputSource as _InputSource -from xml.sax import ErrorHandler as _ErrorHandler - -from . import expatreader - -__origin__ = "xml.sax" - - -def parse( - source, - handler, - errorHandler=_ErrorHandler(), - forbid_dtd=False, - forbid_entities=True, - forbid_external=True, -): - parser = make_parser() - parser.setContentHandler(handler) - parser.setErrorHandler(errorHandler) - parser.forbid_dtd = forbid_dtd - parser.forbid_entities = forbid_entities - parser.forbid_external = forbid_external - parser.parse(source) - - -def parseString( - string, - handler, - errorHandler=_ErrorHandler(), - forbid_dtd=False, - forbid_entities=True, - forbid_external=True, -): - from io import BytesIO - - if errorHandler is None: - errorHandler = _ErrorHandler() - parser = make_parser() - parser.setContentHandler(handler) - parser.setErrorHandler(errorHandler) - parser.forbid_dtd = forbid_dtd - parser.forbid_entities = forbid_entities - parser.forbid_external = forbid_external - - inpsrc = _InputSource() - inpsrc.setByteStream(BytesIO(string)) - parser.parse(inpsrc) - - -def make_parser(parser_list=[]): - return expatreader.create_parser() diff --git a/apps/bitwarden_event_logs/lib/defusedxml/xmlrpc.py b/apps/bitwarden_event_logs/lib/defusedxml/xmlrpc.py deleted file mode 100755 index fbc674da..00000000 --- a/apps/bitwarden_event_logs/lib/defusedxml/xmlrpc.py +++ /dev/null @@ -1,153 +0,0 @@ -# defusedxml -# -# Copyright (c) 2013 by Christian Heimes -# Licensed to PSF under a Contributor Agreement. -# See https://www.python.org/psf/license for licensing details. -"""Defused xmlrpclib - -Also defuses gzip bomb -""" -from __future__ import print_function, absolute_import - -import io - -from .common import DTDForbidden, EntitiesForbidden, ExternalReferenceForbidden, PY3 - -if PY3: - __origin__ = "xmlrpc.client" - from xmlrpc.client import ExpatParser - from xmlrpc import client as xmlrpc_client - from xmlrpc import server as xmlrpc_server - from xmlrpc.client import gzip_decode as _orig_gzip_decode - from xmlrpc.client import GzipDecodedResponse as _OrigGzipDecodedResponse -else: - __origin__ = "xmlrpclib" - from xmlrpclib import ExpatParser - import xmlrpclib as xmlrpc_client - - xmlrpc_server = None - from xmlrpclib import gzip_decode as _orig_gzip_decode - from xmlrpclib import GzipDecodedResponse as _OrigGzipDecodedResponse - -try: - import gzip -except ImportError: # pragma: no cover - gzip = None - - -# Limit maximum request size to prevent resource exhaustion DoS -# Also used to limit maximum amount of gzip decoded data in order to prevent -# decompression bombs -# A value of -1 or smaller disables the limit -MAX_DATA = 30 * 1024 * 1024 # 30 MB - - -def defused_gzip_decode(data, limit=None): - """gzip encoded data -> unencoded data - - Decode data using the gzip content encoding as described in RFC 1952 - """ - if not gzip: # pragma: no cover - raise NotImplementedError - if limit is None: - limit = MAX_DATA - f = io.BytesIO(data) - gzf = gzip.GzipFile(mode="rb", fileobj=f) - try: - if limit < 0: # no limit - decoded = gzf.read() - else: - decoded = gzf.read(limit + 1) - except IOError: # pragma: no cover - raise ValueError("invalid data") - f.close() - gzf.close() - if limit >= 0 and len(decoded) > limit: - raise ValueError("max gzipped payload length exceeded") - return decoded - - -class DefusedGzipDecodedResponse(gzip.GzipFile if gzip else object): - """a file-like object to decode a response encoded with the gzip - method, as described in RFC 1952. - """ - - def __init__(self, response, limit=None): - # response doesn't support tell() and read(), required by - # GzipFile - if not gzip: # pragma: no cover - raise NotImplementedError - self.limit = limit = limit if limit is not None else MAX_DATA - if limit < 0: # no limit - data = response.read() - self.readlength = None - else: - data = response.read(limit + 1) - self.readlength = 0 - if limit >= 0 and len(data) > limit: - raise ValueError("max payload length exceeded") - self.stringio = io.BytesIO(data) - gzip.GzipFile.__init__(self, mode="rb", fileobj=self.stringio) - - def read(self, n): - if self.limit >= 0: - left = self.limit - self.readlength - n = min(n, left + 1) - data = gzip.GzipFile.read(self, n) - self.readlength += len(data) - if self.readlength > self.limit: - raise ValueError("max payload length exceeded") - return data - else: - return gzip.GzipFile.read(self, n) - - def close(self): - gzip.GzipFile.close(self) - self.stringio.close() - - -class DefusedExpatParser(ExpatParser): - def __init__(self, target, forbid_dtd=False, forbid_entities=True, forbid_external=True): - ExpatParser.__init__(self, target) - self.forbid_dtd = forbid_dtd - self.forbid_entities = forbid_entities - self.forbid_external = forbid_external - parser = self._parser - if self.forbid_dtd: - parser.StartDoctypeDeclHandler = self.defused_start_doctype_decl - if self.forbid_entities: - parser.EntityDeclHandler = self.defused_entity_decl - parser.UnparsedEntityDeclHandler = self.defused_unparsed_entity_decl - if self.forbid_external: - parser.ExternalEntityRefHandler = self.defused_external_entity_ref_handler - - def defused_start_doctype_decl(self, name, sysid, pubid, has_internal_subset): - raise DTDForbidden(name, sysid, pubid) - - def defused_entity_decl( - self, name, is_parameter_entity, value, base, sysid, pubid, notation_name - ): - raise EntitiesForbidden(name, value, base, sysid, pubid, notation_name) - - def defused_unparsed_entity_decl(self, name, base, sysid, pubid, notation_name): - # expat 1.2 - raise EntitiesForbidden(name, None, base, sysid, pubid, notation_name) # pragma: no cover - - def defused_external_entity_ref_handler(self, context, base, sysid, pubid): - raise ExternalReferenceForbidden(context, base, sysid, pubid) - - -def monkey_patch(): - xmlrpc_client.FastParser = DefusedExpatParser - xmlrpc_client.GzipDecodedResponse = DefusedGzipDecodedResponse - xmlrpc_client.gzip_decode = defused_gzip_decode - if xmlrpc_server: - xmlrpc_server.gzip_decode = defused_gzip_decode - - -def unmonkey_patch(): - xmlrpc_client.FastParser = None - xmlrpc_client.GzipDecodedResponse = _OrigGzipDecodedResponse - xmlrpc_client.gzip_decode = _orig_gzip_decode - if xmlrpc_server: - xmlrpc_server.gzip_decode = _orig_gzip_decode diff --git a/apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/INSTALLER b/apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/INSTALLER deleted file mode 100755 index a1b589e3..00000000 --- a/apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/LICENSE b/apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/LICENSE deleted file mode 100755 index 8dada3ed..00000000 --- a/apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/METADATA b/apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/METADATA deleted file mode 100755 index 1d44ff8c..00000000 --- a/apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/METADATA +++ /dev/null @@ -1,114 +0,0 @@ -Metadata-Version: 2.1 -Name: deprecation -Version: 2.1.0 -Summary: A library to handle automated deprecations -Home-page: http://deprecation.readthedocs.io/ -Author: Brian Curtin -Author-email: brian@python.org -Maintainer: Brian Curtin -Maintainer-email: brian@python.org -License: Apache 2 -Project-URL: Documentation, http://deprecation.readthedocs.io/en/latest/ -Project-URL: Source, https://github.com/briancurtin/deprecation -Project-URL: Bug Tracker, https://github.com/briancurtin/deprecation/issues -Keywords: deprecation -Platform: UNKNOWN -Classifier: Development Status :: 5 - Production/Stable -Classifier: License :: OSI Approved :: Apache Software License -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Requires-Dist: packaging - -deprecation -=========== - -.. image:: https://readthedocs.org/projects/deprecation/badge/?version=latest - :target: http://deprecation.readthedocs.io/en/latest/ - :alt: Documentation Status - -.. image:: https://travis-ci.org/briancurtin/deprecation.svg?branch=master - :target: https://travis-ci.org/briancurtin/deprecation - -.. image:: https://codecov.io/gh/briancurtin/deprecation/branch/master/graph/badge.svg - :target: https://codecov.io/gh/briancurtin/deprecation - -The ``deprecation`` library provides a ``deprecated`` decorator and a -``fail_if_not_removed`` decorator for your tests. Together, the two -enable the automation of several things: - -1. The docstring of a deprecated method gets the deprecation details - appended to the end of it. If you generate your API docs direct - from your source, you don't need to worry about writing your own - notification. You also don't need to worry about forgetting to - write it. It's done for you. -2. Rather than having code live on forever because you only deprecated - it but never actually moved on from it, you can have your tests - tell you when it's time to remove the code. The ``@deprecated`` - decorator can be told when it's time to entirely remove the code, - which causes ``@fail_if_not_removed`` to raise an ``AssertionError``, - causing either your unittest or py.test tests to fail. - -See http://deprecation.readthedocs.io/ for the full documentation. - -Installation -============ - - :: - - pip install deprecation - -Usage -===== - - :: - - import deprecation - - @deprecation.deprecated(deprecated_in="1.0", removed_in="2.0", - current_version=__version__, - details="Use the bar function instead") - def foo(): - """Do some stuff""" - return 1 - -...but doesn't Python ignore ``DeprecationWarning``? -==================================================== - -Yes, by default since 2.7—and for good reason [#]_ —and this works fine -with that. - -1. It often makes sense for you to run your tests with a ``-W`` flag or - the ``PYTHONWARNINGS`` environment variable so you catch warnings - in development and handle them appropriately. The warnings raised by - this library show up there, as they're subclasses of the built-in - ``DeprecationWarning``. See the `Command Line - `_ - and `Environment Variable - `_ - documentation for more details. -2. Even if you don't enable those things, the behavior of this library - remains the same. The docstrings will still be updated and the tests - will still fail when they need to. You'll get the benefits regardless - of what Python cares about ``DeprecationWarning``. - ----- - -.. [#] Exposing application users to ``DeprecationWarning``\s that are - emitted by lower-level code needlessly involves end-users in - "how things are done." It often leads to users raising issues - about warnings they're presented, which on one hand is done - rightfully so, as it's been presented to them as some sort of - issue to resolve. However, at the same time, the warning could - be well known and planned for. From either side, loud - ``DeprecationWarning``\s can be seen as noise that isn't - necessary outside of development. - - diff --git a/apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/RECORD b/apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/RECORD deleted file mode 100755 index 6865e23f..00000000 --- a/apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/RECORD +++ /dev/null @@ -1,8 +0,0 @@ -deprecation-2.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -deprecation-2.1.0.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357 -deprecation-2.1.0.dist-info/METADATA,sha256=AmlhU86t_lQQh858jSMZMZpoYM6gfnnLSKKe5DgGU88,4604 -deprecation-2.1.0.dist-info/RECORD,, -deprecation-2.1.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -deprecation-2.1.0.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110 -deprecation-2.1.0.dist-info/top_level.txt,sha256=ztDh9pLraFksBgQ6iXWE_OnukoBC6_jkPQ4Y6p7771o,12 -deprecation.py,sha256=Z8qPVC-janAf7Ppoc1wYTiTWvGSdSjErOV1jDhnHN0Y,12895 diff --git a/apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/REQUESTED b/apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/REQUESTED deleted file mode 100755 index e69de29b..00000000 diff --git a/apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/WHEEL b/apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/WHEEL deleted file mode 100755 index ef99c6cf..00000000 --- a/apps/bitwarden_event_logs/lib/deprecation-2.1.0.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.34.2) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/apps/bitwarden_event_logs/lib/deprecation.py b/apps/bitwarden_event_logs/lib/deprecation.py deleted file mode 100755 index 0217b589..00000000 --- a/apps/bitwarden_event_logs/lib/deprecation.py +++ /dev/null @@ -1,290 +0,0 @@ -# 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 collections -import functools -import textwrap -import warnings - -from packaging import version -from datetime import date - -__version__ = "2.1.0" - -# This is mostly here so automodule docs are ordered more ideally. -__all__ = ["deprecated", "message_location", "fail_if_not_removed", - "DeprecatedWarning", "UnsupportedWarning"] - -#: Location where the details are added to a deprecated docstring -#: -#: When set to ``"bottom"``, the details are appended to the end. -#: When set to ``"top"``, the details are inserted between the -#: summary line and docstring contents. -message_location = "bottom" - - -class DeprecatedWarning(DeprecationWarning): - """A warning class for deprecated methods - - This is a specialization of the built-in :class:`DeprecationWarning`, - adding parameters that allow us to get information into the __str__ - that ends up being sent through the :mod:`warnings` system. - The attributes aren't able to be retrieved after the warning gets - raised and passed through the system as only the class--not the - instance--and message are what gets preserved. - - :param function: The function being deprecated. - :param deprecated_in: The version that ``function`` is deprecated in - :param removed_in: The version or :class:`datetime.date` specifying - when ``function`` gets removed. - :param details: Optional details about the deprecation. Most often - this will include directions on what to use instead - of the now deprecated code. - """ - - def __init__(self, function, deprecated_in, removed_in, details=""): - # NOTE: The docstring only works for this class if it appears up - # near the class name, not here inside __init__. I think it has - # to do with being an exception class. - self.function = function - self.deprecated_in = deprecated_in - self.removed_in = removed_in - self.details = details - super(DeprecatedWarning, self).__init__(function, deprecated_in, - removed_in, details) - - def __str__(self): - # Use a defaultdict to give us the empty string - # when a part isn't included. - parts = collections.defaultdict(str) - parts["function"] = self.function - - if self.deprecated_in: - parts["deprecated"] = " as of %s" % self.deprecated_in - if self.removed_in: - parts["removed"] = " and will be removed {} {}".format("on" if isinstance(self.removed_in, date) else "in", - self.removed_in) - if any([self.deprecated_in, self.removed_in, self.details]): - parts["period"] = "." - if self.details: - parts["details"] = " %s" % self.details - - return ("%(function)s is deprecated%(deprecated)s%(removed)s" - "%(period)s%(details)s" % (parts)) - - -class UnsupportedWarning(DeprecatedWarning): - """A warning class for methods to be removed - - This is a subclass of :class:`~deprecation.DeprecatedWarning` and is used - to output a proper message about a function being unsupported. - Additionally, the :func:`~deprecation.fail_if_not_removed` decorator - will handle this warning and cause any tests to fail if the system - under test uses code that raises this warning. - """ - - def __str__(self): - parts = collections.defaultdict(str) - parts["function"] = self.function - parts["removed"] = self.removed_in - - if self.details: - parts["details"] = " %s" % self.details - - return ("%(function)s is unsupported as of %(removed)s." - "%(details)s" % (parts)) - - -def deprecated(deprecated_in=None, removed_in=None, current_version=None, - details=""): - """Decorate a function to signify its deprecation - - This function wraps a method that will soon be removed and does two things: - * The docstring of the method will be modified to include a notice - about deprecation, e.g., "Deprecated since 0.9.11. Use foo instead." - * Raises a :class:`~deprecation.DeprecatedWarning` - via the :mod:`warnings` module, which is a subclass of the built-in - :class:`DeprecationWarning`. Note that built-in - :class:`DeprecationWarning`s are ignored by default, so for users - to be informed of said warnings they will need to enable them--see - the :mod:`warnings` module documentation for more details. - - :param deprecated_in: The version at which the decorated method is - considered deprecated. This will usually be the - next version to be released when the decorator is - added. The default is **None**, which effectively - means immediate deprecation. If this is not - specified, then the `removed_in` and - `current_version` arguments are ignored. - :param removed_in: The version or :class:`datetime.date` when the decorated - method will be removed. The default is **None**, - specifying that the function is not currently planned - to be removed. - Note: This parameter cannot be set to a value if - `deprecated_in=None`. - :param current_version: The source of version information for the - currently running code. This will usually be - a `__version__` attribute on your library. - The default is `None`. - When `current_version=None` the automation to - determine if the wrapped function is actually - in a period of deprecation or time for removal - does not work, causing a - :class:`~deprecation.DeprecatedWarning` - to be raised in all cases. - :param details: Extra details to be added to the method docstring and - warning. For example, the details may point users to - a replacement method, such as "Use the foo_bar - method instead". By default there are no details. - """ - # You can't just jump to removal. It's weird, unfair, and also makes - # building up the docstring weird. - if deprecated_in is None and removed_in is not None: - raise TypeError("Cannot set removed_in to a value " - "without also setting deprecated_in") - - # Only warn when it's appropriate. There may be cases when it makes sense - # to add this decorator before a formal deprecation period begins. - # In CPython, PendingDeprecatedWarning gets used in that period, - # so perhaps mimick that at some point. - is_deprecated = False - is_unsupported = False - - # StrictVersion won't take a None or a "", so make whatever goes to it - # is at least *something*. Compare versions only if removed_in is not - # of type datetime.date - if isinstance(removed_in, date): - if date.today() >= removed_in: - is_unsupported = True - else: - is_deprecated = True - elif current_version: - current_version = version.parse(current_version) - - if (removed_in - and current_version >= version.parse(removed_in)): - is_unsupported = True - elif (deprecated_in - and current_version >= version.parse(deprecated_in)): - is_deprecated = True - else: - # If we can't actually calculate that we're in a period of - # deprecation...well, they used the decorator, so it's deprecated. - # This will cover the case of someone just using - # @deprecated("1.0") without the other advantages. - is_deprecated = True - - should_warn = any([is_deprecated, is_unsupported]) - - def _function_wrapper(function): - if should_warn: - # Everything *should* have a docstring, but just in case... - existing_docstring = function.__doc__ or "" - - # The various parts of this decorator being optional makes for - # a number of ways the deprecation notice could go. The following - # makes for a nicely constructed sentence with or without any - # of the parts. - - # If removed_in is a date, use "removed on" - # If removed_in is a version, use "removed in" - parts = { - "deprecated_in": - " %s" % deprecated_in if deprecated_in else "", - "removed_in": - "\n This will be removed {} {}.".format("on" if isinstance(removed_in, date) else "in", - removed_in) if removed_in else "", - "details": - " %s" % details if details else ""} - - deprecation_note = (".. deprecated::{deprecated_in}" - "{removed_in}{details}".format(**parts)) - - # default location for insertion of deprecation note - loc = 1 - - # split docstring at first occurrence of newline - string_list = existing_docstring.split("\n", 1) - - if len(string_list) > 1: - # With a multi-line docstring, when we modify - # existing_docstring to add our deprecation_note, - # if we're not careful we'll interfere with the - # indentation levels of the contents below the - # first line, or as PEP 257 calls it, the summary - # line. Since the summary line can start on the - # same line as the """, dedenting the whole thing - # won't help. Split the summary and contents up, - # dedent the contents independently, then join - # summary, dedent'ed contents, and our - # deprecation_note. - - # in-place dedent docstring content - string_list[1] = textwrap.dedent(string_list[1]) - - # we need another newline - string_list.insert(loc, "\n") - - # change the message_location if we add to end of docstring - # do this always if not "top" - if message_location != "top": - loc = 3 - - # insert deprecation note and dual newline - string_list.insert(loc, deprecation_note) - string_list.insert(loc, "\n\n") - - function.__doc__ = "".join(string_list) - - @functools.wraps(function) - def _inner(*args, **kwargs): - if should_warn: - if is_unsupported: - cls = UnsupportedWarning - else: - cls = DeprecatedWarning - - the_warning = cls(function.__name__, deprecated_in, - removed_in, details) - warnings.warn(the_warning, category=DeprecationWarning, - stacklevel=2) - - return function(*args, **kwargs) - return _inner - return _function_wrapper - - -def fail_if_not_removed(method): - """Decorate a test method to track removal of deprecated code - - This decorator catches :class:`~deprecation.UnsupportedWarning` - warnings that occur during testing and causes unittests to fail, - making it easier to keep track of when code should be removed. - - :raises: :class:`AssertionError` if an - :class:`~deprecation.UnsupportedWarning` - is raised while running the test method. - """ - # NOTE(briancurtin): Unless this is named test_inner, nose won't work - # properly. See Issue #32. - @functools.wraps(method) - def test_inner(*args, **kwargs): - with warnings.catch_warnings(record=True) as caught_warnings: - warnings.simplefilter("always") - rv = method(*args, **kwargs) - - for warning in caught_warnings: - if warning.category == UnsupportedWarning: - raise AssertionError( - ("%s uses a function that should be removed: %s" % - (method, str(warning.message)))) - return rv - return test_inner diff --git a/apps/bitwarden_event_logs/lib/idna-3.11.dist-info/INSTALLER b/apps/bitwarden_event_logs/lib/idna-3.11.dist-info/INSTALLER deleted file mode 100755 index a1b589e3..00000000 --- a/apps/bitwarden_event_logs/lib/idna-3.11.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/apps/bitwarden_event_logs/lib/idna-3.11.dist-info/METADATA b/apps/bitwarden_event_logs/lib/idna-3.11.dist-info/METADATA deleted file mode 100755 index 7a4a4b7a..00000000 --- a/apps/bitwarden_event_logs/lib/idna-3.11.dist-info/METADATA +++ /dev/null @@ -1,209 +0,0 @@ -Metadata-Version: 2.4 -Name: idna -Version: 3.11 -Summary: Internationalized Domain Names in Applications (IDNA) -Author-email: Kim Davies -Requires-Python: >=3.8 -Description-Content-Type: text/x-rst -License-Expression: BSD-3-Clause -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: Intended Audience :: System Administrators -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.8 -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: Topic :: Internet :: Name Service (DNS) -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: Utilities -License-File: LICENSE.md -Requires-Dist: ruff >= 0.6.2 ; extra == "all" -Requires-Dist: mypy >= 1.11.2 ; extra == "all" -Requires-Dist: pytest >= 8.3.2 ; extra == "all" -Requires-Dist: flake8 >= 7.1.1 ; extra == "all" -Project-URL: Changelog, https://github.com/kjd/idna/blob/master/HISTORY.rst -Project-URL: Issue tracker, https://github.com/kjd/idna/issues -Project-URL: Source, https://github.com/kjd/idna -Provides-Extra: all - -Internationalized Domain Names in Applications (IDNA) -===================================================== - -Support for `Internationalized Domain Names in -Applications (IDNA) `_ -and `Unicode IDNA Compatibility Processing -`_. - -The latest versions of these standards supplied here provide -more comprehensive language coverage and reduce the potential of -allowing domains with known security vulnerabilities. This library -is a suitable replacement for the “encodings.idna” -module that comes with the Python standard library, but which -only supports an older superseded IDNA specification from 2003. - -Basic functions are simply executed: - -.. code-block:: pycon - - >>> import idna - >>> idna.encode('ドメイン.テスト') - b'xn--eckwd4c7c.xn--zckzah' - >>> print(idna.decode('xn--eckwd4c7c.xn--zckzah')) - ドメイン.テスト - - -Installation ------------- - -This package is available for installation from PyPI via the -typical mechanisms, such as: - -.. code-block:: bash - - $ python3 -m pip install idna - - -Usage ------ - -For typical usage, the ``encode`` and ``decode`` functions will take a -domain name argument and perform a conversion to ASCII compatible encoding -(known as A-labels), or to Unicode strings (known as U-labels) -respectively. - -.. code-block:: pycon - - >>> import idna - >>> idna.encode('ドメイン.テスト') - b'xn--eckwd4c7c.xn--zckzah' - >>> print(idna.decode('xn--eckwd4c7c.xn--zckzah')) - ドメイン.テスト - -Conversions can be applied at a per-label basis using the ``ulabel`` or -``alabel`` functions if necessary: - -.. code-block:: pycon - - >>> idna.alabel('测试') - b'xn--0zwm56d' - - -Compatibility Mapping (UTS #46) -+++++++++++++++++++++++++++++++ - -This library provides support for `Unicode IDNA Compatibility -Processing `_ which normalizes input from -different potential ways a user may input a domain prior to performing the IDNA -conversion operations. This functionality, known as a -`mapping `_, is considered by the -specification to be a local user-interface issue distinct from IDNA -conversion functionality. - -For example, “Königsgäßchen” is not a permissible label as *LATIN -CAPITAL LETTER K* is not allowed (nor are capital letters in general). -UTS 46 will convert this into lower case prior to applying the IDNA -conversion. - -.. code-block:: pycon - - >>> import idna - >>> idna.encode('Königsgäßchen') - ... - idna.core.InvalidCodepoint: Codepoint U+004B at position 1 of 'Königsgäßchen' not allowed - >>> idna.encode('Königsgäßchen', uts46=True) - b'xn--knigsgchen-b4a3dun' - >>> print(idna.decode('xn--knigsgchen-b4a3dun')) - königsgäßchen - - -Exceptions ----------- - -All errors raised during the conversion following the specification -should raise an exception derived from the ``idna.IDNAError`` base -class. - -More specific exceptions that may be generated as ``idna.IDNABidiError`` -when the error reflects an illegal combination of left-to-right and -right-to-left characters in a label; ``idna.InvalidCodepoint`` when -a specific codepoint is an illegal character in an IDN label (i.e. -INVALID); and ``idna.InvalidCodepointContext`` when the codepoint is -illegal based on its position in the string (i.e. it is CONTEXTO or CONTEXTJ -but the contextual requirements are not satisfied.) - -Building and Diagnostics ------------------------- - -The IDNA and UTS 46 functionality relies upon pre-calculated lookup -tables for performance. These tables are derived from computing against -eligibility criteria in the respective standards using the command-line -script ``tools/idna-data``. - -This tool will fetch relevant codepoint data from the Unicode repository -and perform the required calculations to identify eligibility. There are -three main modes: - -* ``idna-data make-libdata``. Generates ``idnadata.py`` and - ``uts46data.py``, the pre-calculated lookup tables used for IDNA and - UTS 46 conversions. Implementers who wish to track this library against - a different Unicode version may use this tool to manually generate a - different version of the ``idnadata.py`` and ``uts46data.py`` files. - -* ``idna-data make-table``. Generate a table of the IDNA disposition - (e.g. PVALID, CONTEXTJ, CONTEXTO) in the format found in Appendix - B.1 of RFC 5892 and the pre-computed tables published by `IANA - `_. - -* ``idna-data U+0061``. Prints debugging output on the various - properties associated with an individual Unicode codepoint (in this - case, U+0061), that are used to assess the IDNA and UTS 46 status of a - codepoint. This is helpful in debugging or analysis. - -The tool accepts a number of arguments, described using ``idna-data --h``. Most notably, the ``--version`` argument allows the specification -of the version of Unicode to be used in computing the table data. For -example, ``idna-data --version 9.0.0 make-libdata`` will generate -library data against Unicode 9.0.0. - - -Additional Notes ----------------- - -* **Packages**. The latest tagged release version is published in the - `Python Package Index `_. - -* **Version support**. This library supports Python 3.8 and higher. - As this library serves as a low-level toolkit for a variety of - applications, many of which strive for broad compatibility with older - Python versions, there is no rush to remove older interpreter support. - Support for older versions are likely to be removed from new releases - as automated tests can no longer easily be run, i.e. once the Python - version is officially end-of-life. - -* **Testing**. The library has a test suite based on each rule of the - IDNA specification, as well as tests that are provided as part of the - Unicode Technical Standard 46, `Unicode IDNA Compatibility Processing - `_. - -* **Emoji**. It is an occasional request to support emoji domains in - this library. Encoding of symbols like emoji is expressly prohibited by - the technical standard IDNA 2008 and emoji domains are broadly phased - out across the domain industry due to associated security risks. For - now, applications that need to support these non-compliant labels - may wish to consider trying the encode/decode operation in this library - first, and then falling back to using `encodings.idna`. See `the Github - project `_ for more discussion. - -* **Transitional processing**. Unicode 16.0.0 removed transitional - processing so the `transitional` argument for the encode() method - no longer has any effect and will be removed at a later date. - diff --git a/apps/bitwarden_event_logs/lib/idna-3.11.dist-info/RECORD b/apps/bitwarden_event_logs/lib/idna-3.11.dist-info/RECORD deleted file mode 100755 index 151b8df6..00000000 --- a/apps/bitwarden_event_logs/lib/idna-3.11.dist-info/RECORD +++ /dev/null @@ -1,15 +0,0 @@ -idna-3.11.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -idna-3.11.dist-info/METADATA,sha256=fCwSww9SuiN8TIHllFSASUQCW55hAs8dzKnr9RaEEbA,8378 -idna-3.11.dist-info/RECORD,, -idna-3.11.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -idna-3.11.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82 -idna-3.11.dist-info/licenses/LICENSE.md,sha256=t6M2q_OwThgOwGXN0W5wXQeeHMehT5EKpukYfza5zYc,1541 -idna/__init__.py,sha256=MPqNDLZbXqGaNdXxAFhiqFPKEQXju2jNQhCey6-5eJM,868 -idna/codec.py,sha256=M2SGWN7cs_6B32QmKTyTN6xQGZeYQgQ2wiX3_DR6loE,3438 -idna/compat.py,sha256=RzLy6QQCdl9784aFhb2EX9EKGCJjg0P3PilGdeXXcx8,316 -idna/core.py,sha256=P26_XVycuMTZ1R2mNK1ZREVzM5mvTzdabBXfyZVU1Lc,13246 -idna/idnadata.py,sha256=SG8jhaGE53iiD6B49pt2pwTv_UvClciWE-N54oR2p4U,79623 -idna/intranges.py,sha256=amUtkdhYcQG8Zr-CoMM_kVRacxkivC1WgxN1b63KKdU,1898 -idna/package_data.py,sha256=_CUavOxobnbyNG2FLyHoN8QHP3QM9W1tKuw7eq9QwBk,21 -idna/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -idna/uts46data.py,sha256=H9J35VkD0F9L9mKOqjeNGd2A-Va6FlPoz6Jz4K7h-ps,243725 diff --git a/apps/bitwarden_event_logs/lib/idna-3.11.dist-info/REQUESTED b/apps/bitwarden_event_logs/lib/idna-3.11.dist-info/REQUESTED deleted file mode 100755 index e69de29b..00000000 diff --git a/apps/bitwarden_event_logs/lib/idna-3.11.dist-info/WHEEL b/apps/bitwarden_event_logs/lib/idna-3.11.dist-info/WHEEL deleted file mode 100755 index d8b9936d..00000000 --- a/apps/bitwarden_event_logs/lib/idna-3.11.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: flit 3.12.0 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/apps/bitwarden_event_logs/lib/idna-3.11.dist-info/licenses/LICENSE.md b/apps/bitwarden_event_logs/lib/idna-3.11.dist-info/licenses/LICENSE.md deleted file mode 100755 index 256ba90c..00000000 --- a/apps/bitwarden_event_logs/lib/idna-3.11.dist-info/licenses/LICENSE.md +++ /dev/null @@ -1,31 +0,0 @@ -BSD 3-Clause License - -Copyright (c) 2013-2025, Kim Davies and contributors. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/apps/bitwarden_event_logs/lib/idna/__init__.py b/apps/bitwarden_event_logs/lib/idna/__init__.py deleted file mode 100755 index cfdc030a..00000000 --- a/apps/bitwarden_event_logs/lib/idna/__init__.py +++ /dev/null @@ -1,45 +0,0 @@ -from .core import ( - IDNABidiError, - IDNAError, - InvalidCodepoint, - InvalidCodepointContext, - alabel, - check_bidi, - check_hyphen_ok, - check_initial_combiner, - check_label, - check_nfc, - decode, - encode, - ulabel, - uts46_remap, - valid_contextj, - valid_contexto, - valid_label_length, - valid_string_length, -) -from .intranges import intranges_contain -from .package_data import __version__ - -__all__ = [ - "__version__", - "IDNABidiError", - "IDNAError", - "InvalidCodepoint", - "InvalidCodepointContext", - "alabel", - "check_bidi", - "check_hyphen_ok", - "check_initial_combiner", - "check_label", - "check_nfc", - "decode", - "encode", - "intranges_contain", - "ulabel", - "uts46_remap", - "valid_contextj", - "valid_contexto", - "valid_label_length", - "valid_string_length", -] diff --git a/apps/bitwarden_event_logs/lib/idna/__pycache__/__init__.cpython-39.pyc b/apps/bitwarden_event_logs/lib/idna/__pycache__/__init__.cpython-39.pyc deleted file mode 100755 index d3b7fcc0fd4463293f176b56b7cd9dc0c351fbd7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 824 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 diff --git a/apps/bitwarden_event_logs/lib/idna/__pycache__/idnadata.cpython-39.pyc b/apps/bitwarden_event_logs/lib/idna/__pycache__/idnadata.cpython-39.pyc deleted file mode 100755 index f8c193beb793f978bff40d16aa8385f891ea6960..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42757 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 F{{W=Xu=oG~ diff --git a/apps/bitwarden_event_logs/lib/idna/__pycache__/intranges.cpython-39.pyc b/apps/bitwarden_event_logs/lib/idna/__pycache__/intranges.cpython-39.pyc deleted file mode 100755 index 002e7284d5b88aadc90af9a2dcfc77c1e429e487..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1969 zcmZuy%Wm8@6eacYWNfE)lEyY_phN-`R$8@}dXag4r3KvMS8nlq*A!n>wLro|u zjsp!+z%9^^$Ra_z=`U&9m4Bg|^jylXJ|u`kUfvh)IhT_zE_NB7+rRyu{W!9cUpPtL0yqoVeye%!cJFu&se;iUg3;&zsX)IEmDzZ zr4_|UWZJ5+GUcY2q*94$O2Uo(VyZ?}o*%_fEhlN73tdh2l%d_c&|_gLU3Re;7P?f! z%EIj>U&CTDt*nBvQB$KzrEO}nLX)}OS7KC{Nq1wTw5hRoH-*yjZoDbl^*y(~7mHr6 z7x%>TeacsthX_ulFDg|!SGNw+QoItU?z@?G+l9%-nND+ez=yYxld#!DiSIV@V!UzN z0BsqZgR@s4Pp1fFx%qLm9^9x8Y z3v2J4Z-Y9NK5RnCNq>bMd+#}i-pqfoZlQC`0dA0ff<&L5)%t(%6(|;1qk|50w|<%~-rHA<*g>Com!&?-s= zoG$Df>PaTH?nAsq;9~(sh_V1sS+DDyJ$31|uJo{wO41R^Opo&mrkzqoY;B1z?unXw zAw^wS(zOI>*>m1bKHxbaFbxfH)I?=rtddRW>d^SP(oK|3r*P3MCMXg@ChcD6JjIcr zjvmanh6(WmG;wJTx_JuL9Gc9khG&DtVPyfp0-lmk_j|p5p6&HBsnb46+eYe5kDBY}t7K$~iOXaG0m_>gSf|4S zyi0gjxyNttbsl-<8g^p830?6UAEz`SL_?2EP9K`n=lD$yYg}0mz}K%BIOgq~2*}(~ zV$WQs`HeGylbU5(lHo0UrE|^Q8hyo08}M>N(cH)90#b=6;6oa{M{Gp5mcL^@hCC;& z4{tua@lWG(LuYNHlA#L?+@!j|?K8;Fl32kU#}81s#9SqV|D2vWn*esnyvBsOTk>OX z=2`v@eO-I+y_t_b?5&DdGCr5F5;4|*+LZ9IK50;V$e;u z+fH|Tj^u_;^n5!G9Ry9Zm*J+i&3yZt5nM2_%D<4=pS2}lub zl8yIm!;^p4eEEWm_%E<&ray;^mWp1NFZdl$aFqw%0tiXeG@nwq`DQLjWHC$g`k0_DX^GDU&(V-N=!FakLaKwQiLBvKfn7*ZI688n%ySd8@y4gEA3Z*j-R zm!%dJXXfX{$FF24Vgf1y6Tdw5^9xG!iwkl}^Ro3*OOo{y3kr($lQK)n6N^$(^Wsy> zQu9jUbMn(cf_eISdipt;N&1;7d5QW3iOJcC>8bH4i6x181(mlrY;yBcN^?@}K(6@= G#0&r#YcIzD diff --git a/apps/bitwarden_event_logs/lib/idna/codec.py b/apps/bitwarden_event_logs/lib/idna/codec.py deleted file mode 100755 index cbc2e4ff..00000000 --- a/apps/bitwarden_event_logs/lib/idna/codec.py +++ /dev/null @@ -1,122 +0,0 @@ -import codecs -import re -from typing import Any, Optional, Tuple - -from .core import IDNAError, alabel, decode, encode, ulabel - -_unicode_dots_re = re.compile("[\u002e\u3002\uff0e\uff61]") - - -class Codec(codecs.Codec): - def encode(self, data: str, errors: str = "strict") -> Tuple[bytes, int]: - if errors != "strict": - raise IDNAError('Unsupported error handling "{}"'.format(errors)) - - if not data: - return b"", 0 - - return encode(data), len(data) - - def decode(self, data: bytes, errors: str = "strict") -> Tuple[str, int]: - if errors != "strict": - raise IDNAError('Unsupported error handling "{}"'.format(errors)) - - if not data: - return "", 0 - - return decode(data), len(data) - - -class IncrementalEncoder(codecs.BufferedIncrementalEncoder): - def _buffer_encode(self, data: str, errors: str, final: bool) -> Tuple[bytes, int]: - if errors != "strict": - raise IDNAError('Unsupported error handling "{}"'.format(errors)) - - if not data: - return b"", 0 - - labels = _unicode_dots_re.split(data) - trailing_dot = b"" - if labels: - if not labels[-1]: - trailing_dot = b"." - del labels[-1] - elif not final: - # Keep potentially unfinished label until the next call - del labels[-1] - if labels: - trailing_dot = b"." - - result = [] - size = 0 - for label in labels: - result.append(alabel(label)) - if size: - size += 1 - size += len(label) - - # Join with U+002E - result_bytes = b".".join(result) + trailing_dot - size += len(trailing_dot) - return result_bytes, size - - -class IncrementalDecoder(codecs.BufferedIncrementalDecoder): - def _buffer_decode(self, data: Any, errors: str, final: bool) -> Tuple[str, int]: - if errors != "strict": - raise IDNAError('Unsupported error handling "{}"'.format(errors)) - - if not data: - return ("", 0) - - if not isinstance(data, str): - data = str(data, "ascii") - - labels = _unicode_dots_re.split(data) - trailing_dot = "" - if labels: - if not labels[-1]: - trailing_dot = "." - del labels[-1] - elif not final: - # Keep potentially unfinished label until the next call - del labels[-1] - if labels: - trailing_dot = "." - - result = [] - size = 0 - for label in labels: - result.append(ulabel(label)) - if size: - size += 1 - size += len(label) - - result_str = ".".join(result) + trailing_dot - size += len(trailing_dot) - return (result_str, size) - - -class StreamWriter(Codec, codecs.StreamWriter): - pass - - -class StreamReader(Codec, codecs.StreamReader): - pass - - -def search_function(name: str) -> Optional[codecs.CodecInfo]: - if name != "idna2008": - return None - return codecs.CodecInfo( - name=name, - encode=Codec().encode, - decode=Codec().decode, # type: ignore - incrementalencoder=IncrementalEncoder, - incrementaldecoder=IncrementalDecoder, - streamwriter=StreamWriter, - streamreader=StreamReader, - ) - - -codecs.register(search_function) diff --git a/apps/bitwarden_event_logs/lib/idna/compat.py b/apps/bitwarden_event_logs/lib/idna/compat.py deleted file mode 100755 index 1df9f2a7..00000000 --- a/apps/bitwarden_event_logs/lib/idna/compat.py +++ /dev/null @@ -1,15 +0,0 @@ -from typing import Any, Union - -from .core import decode, encode - - -def ToASCII(label: str) -> bytes: - return encode(label) - - -def ToUnicode(label: Union[bytes, bytearray]) -> str: - return decode(label) - - -def nameprep(s: Any) -> None: - raise NotImplementedError("IDNA 2008 does not utilise nameprep protocol") diff --git a/apps/bitwarden_event_logs/lib/idna/core.py b/apps/bitwarden_event_logs/lib/idna/core.py deleted file mode 100755 index 8177bf7a..00000000 --- a/apps/bitwarden_event_logs/lib/idna/core.py +++ /dev/null @@ -1,437 +0,0 @@ -import bisect -import re -import unicodedata -from typing import Optional, Union - -from . import idnadata -from .intranges import intranges_contain - -_virama_combining_class = 9 -_alabel_prefix = b"xn--" -_unicode_dots_re = re.compile("[\u002e\u3002\uff0e\uff61]") - - -class IDNAError(UnicodeError): - """Base exception for all IDNA-encoding related problems""" - - pass - - -class IDNABidiError(IDNAError): - """Exception when bidirectional requirements are not satisfied""" - - pass - - -class InvalidCodepoint(IDNAError): - """Exception when a disallowed or unallocated codepoint is used""" - - pass - - -class InvalidCodepointContext(IDNAError): - """Exception when the codepoint is not valid in the context it is used""" - - pass - - -def _combining_class(cp: int) -> int: - v = unicodedata.combining(chr(cp)) - if v == 0: - if not unicodedata.name(chr(cp)): - raise ValueError("Unknown character in unicodedata") - return v - - -def _is_script(cp: str, script: str) -> bool: - return intranges_contain(ord(cp), idnadata.scripts[script]) - - -def _punycode(s: str) -> bytes: - return s.encode("punycode") - - -def _unot(s: int) -> str: - return "U+{:04X}".format(s) - - -def valid_label_length(label: Union[bytes, str]) -> bool: - if len(label) > 63: - return False - return True - - -def valid_string_length(label: Union[bytes, str], trailing_dot: bool) -> bool: - if len(label) > (254 if trailing_dot else 253): - return False - return True - - -def check_bidi(label: str, check_ltr: bool = False) -> bool: - # Bidi rules should only be applied if string contains RTL characters - bidi_label = False - for idx, cp in enumerate(label, 1): - direction = unicodedata.bidirectional(cp) - if direction == "": - # String likely comes from a newer version of Unicode - raise IDNABidiError("Unknown directionality in label {} at position {}".format(repr(label), idx)) - if direction in ["R", "AL", "AN"]: - bidi_label = True - if not bidi_label and not check_ltr: - return True - - # Bidi rule 1 - direction = unicodedata.bidirectional(label[0]) - if direction in ["R", "AL"]: - rtl = True - elif direction == "L": - rtl = False - else: - raise IDNABidiError("First codepoint in label {} must be directionality L, R or AL".format(repr(label))) - - valid_ending = False - number_type: Optional[str] = None - for idx, cp in enumerate(label, 1): - direction = unicodedata.bidirectional(cp) - - if rtl: - # Bidi rule 2 - if direction not in [ - "R", - "AL", - "AN", - "EN", - "ES", - "CS", - "ET", - "ON", - "BN", - "NSM", - ]: - raise IDNABidiError("Invalid direction for codepoint at position {} in a right-to-left label".format(idx)) - # Bidi rule 3 - if direction in ["R", "AL", "EN", "AN"]: - valid_ending = True - elif direction != "NSM": - valid_ending = False - # Bidi rule 4 - if direction in ["AN", "EN"]: - if not number_type: - number_type = direction - else: - if number_type != direction: - raise IDNABidiError("Can not mix numeral types in a right-to-left label") - else: - # Bidi rule 5 - if direction not in ["L", "EN", "ES", "CS", "ET", "ON", "BN", "NSM"]: - raise IDNABidiError("Invalid direction for codepoint at position {} in a left-to-right label".format(idx)) - # Bidi rule 6 - if direction in ["L", "EN"]: - valid_ending = True - elif direction != "NSM": - valid_ending = False - - if not valid_ending: - raise IDNABidiError("Label ends with illegal codepoint directionality") - - return True - - -def check_initial_combiner(label: str) -> bool: - if unicodedata.category(label[0])[0] == "M": - raise IDNAError("Label begins with an illegal combining character") - return True - - -def check_hyphen_ok(label: str) -> bool: - if label[2:4] == "--": - raise IDNAError("Label has disallowed hyphens in 3rd and 4th position") - if label[0] == "-" or label[-1] == "-": - raise IDNAError("Label must not start or end with a hyphen") - return True - - -def check_nfc(label: str) -> None: - if unicodedata.normalize("NFC", label) != label: - raise IDNAError("Label must be in Normalization Form C") - - -def valid_contextj(label: str, pos: int) -> bool: - cp_value = ord(label[pos]) - - if cp_value == 0x200C: - if pos > 0: - if _combining_class(ord(label[pos - 1])) == _virama_combining_class: - return True - - ok = False - for i in range(pos - 1, -1, -1): - joining_type = idnadata.joining_types.get(ord(label[i])) - if joining_type == ord("T"): - continue - elif joining_type in [ord("L"), ord("D")]: - ok = True - break - else: - break - - if not ok: - return False - - ok = False - for i in range(pos + 1, len(label)): - joining_type = idnadata.joining_types.get(ord(label[i])) - if joining_type == ord("T"): - continue - elif joining_type in [ord("R"), ord("D")]: - ok = True - break - else: - break - return ok - - if cp_value == 0x200D: - if pos > 0: - if _combining_class(ord(label[pos - 1])) == _virama_combining_class: - return True - return False - - else: - return False - - -def valid_contexto(label: str, pos: int, exception: bool = False) -> bool: - cp_value = ord(label[pos]) - - if cp_value == 0x00B7: - if 0 < pos < len(label) - 1: - if ord(label[pos - 1]) == 0x006C and ord(label[pos + 1]) == 0x006C: - return True - return False - - elif cp_value == 0x0375: - if pos < len(label) - 1 and len(label) > 1: - return _is_script(label[pos + 1], "Greek") - return False - - elif cp_value == 0x05F3 or cp_value == 0x05F4: - if pos > 0: - return _is_script(label[pos - 1], "Hebrew") - return False - - elif cp_value == 0x30FB: - for cp in label: - if cp == "\u30fb": - continue - if _is_script(cp, "Hiragana") or _is_script(cp, "Katakana") or _is_script(cp, "Han"): - return True - return False - - elif 0x660 <= cp_value <= 0x669: - for cp in label: - if 0x6F0 <= ord(cp) <= 0x06F9: - return False - return True - - elif 0x6F0 <= cp_value <= 0x6F9: - for cp in label: - if 0x660 <= ord(cp) <= 0x0669: - return False - return True - - return False - - -def check_label(label: Union[str, bytes, bytearray]) -> None: - if isinstance(label, (bytes, bytearray)): - label = label.decode("utf-8") - if len(label) == 0: - raise IDNAError("Empty Label") - - check_nfc(label) - check_hyphen_ok(label) - check_initial_combiner(label) - - for pos, cp in enumerate(label): - cp_value = ord(cp) - if intranges_contain(cp_value, idnadata.codepoint_classes["PVALID"]): - continue - elif intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTJ"]): - try: - if not valid_contextj(label, pos): - raise InvalidCodepointContext( - "Joiner {} not allowed at position {} in {}".format(_unot(cp_value), pos + 1, repr(label)) - ) - except ValueError: - raise IDNAError( - "Unknown codepoint adjacent to joiner {} at position {} in {}".format( - _unot(cp_value), pos + 1, repr(label) - ) - ) - elif intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTO"]): - if not valid_contexto(label, pos): - raise InvalidCodepointContext( - "Codepoint {} not allowed at position {} in {}".format(_unot(cp_value), pos + 1, repr(label)) - ) - else: - raise InvalidCodepoint( - "Codepoint {} at position {} of {} not allowed".format(_unot(cp_value), pos + 1, repr(label)) - ) - - check_bidi(label) - - -def alabel(label: str) -> bytes: - try: - label_bytes = label.encode("ascii") - ulabel(label_bytes) - if not valid_label_length(label_bytes): - raise IDNAError("Label too long") - return label_bytes - except UnicodeEncodeError: - pass - - check_label(label) - label_bytes = _alabel_prefix + _punycode(label) - - if not valid_label_length(label_bytes): - raise IDNAError("Label too long") - - return label_bytes - - -def ulabel(label: Union[str, bytes, bytearray]) -> str: - if not isinstance(label, (bytes, bytearray)): - try: - label_bytes = label.encode("ascii") - except UnicodeEncodeError: - check_label(label) - return label - else: - label_bytes = bytes(label) - - label_bytes = label_bytes.lower() - if label_bytes.startswith(_alabel_prefix): - label_bytes = label_bytes[len(_alabel_prefix) :] - if not label_bytes: - raise IDNAError("Malformed A-label, no Punycode eligible content found") - if label_bytes.decode("ascii")[-1] == "-": - raise IDNAError("A-label must not end with a hyphen") - else: - check_label(label_bytes) - return label_bytes.decode("ascii") - - try: - label = label_bytes.decode("punycode") - except UnicodeError: - raise IDNAError("Invalid A-label") - check_label(label) - return label - - -def uts46_remap(domain: str, std3_rules: bool = True, transitional: bool = False) -> str: - """Re-map the characters in the string according to UTS46 processing.""" - from .uts46data import uts46data - - output = "" - - for pos, char in enumerate(domain): - code_point = ord(char) - try: - uts46row = uts46data[code_point if code_point < 256 else bisect.bisect_left(uts46data, (code_point, "Z")) - 1] - status = uts46row[1] - replacement: Optional[str] = None - if len(uts46row) == 3: - replacement = uts46row[2] - if ( - status == "V" - or (status == "D" and not transitional) - or (status == "3" and not std3_rules and replacement is None) - ): - output += char - elif replacement is not None and ( - status == "M" or (status == "3" and not std3_rules) or (status == "D" and transitional) - ): - output += replacement - elif status != "I": - raise IndexError() - except IndexError: - raise InvalidCodepoint( - "Codepoint {} not allowed at position {} in {}".format(_unot(code_point), pos + 1, repr(domain)) - ) - - return unicodedata.normalize("NFC", output) - - -def encode( - s: Union[str, bytes, bytearray], - strict: bool = False, - uts46: bool = False, - std3_rules: bool = False, - transitional: bool = False, -) -> bytes: - if not isinstance(s, str): - try: - s = str(s, "ascii") - except UnicodeDecodeError: - raise IDNAError("should pass a unicode string to the function rather than a byte string.") - if uts46: - s = uts46_remap(s, std3_rules, transitional) - trailing_dot = False - result = [] - if strict: - labels = s.split(".") - else: - labels = _unicode_dots_re.split(s) - if not labels or labels == [""]: - raise IDNAError("Empty domain") - if labels[-1] == "": - del labels[-1] - trailing_dot = True - for label in labels: - s = alabel(label) - if s: - result.append(s) - else: - raise IDNAError("Empty label") - if trailing_dot: - result.append(b"") - s = b".".join(result) - if not valid_string_length(s, trailing_dot): - raise IDNAError("Domain too long") - return s - - -def decode( - s: Union[str, bytes, bytearray], - strict: bool = False, - uts46: bool = False, - std3_rules: bool = False, -) -> str: - try: - if not isinstance(s, str): - s = str(s, "ascii") - except UnicodeDecodeError: - raise IDNAError("Invalid ASCII in A-label") - if uts46: - s = uts46_remap(s, std3_rules, False) - trailing_dot = False - result = [] - if not strict: - labels = _unicode_dots_re.split(s) - else: - labels = s.split(".") - if not labels or labels == [""]: - raise IDNAError("Empty domain") - if not labels[-1]: - del labels[-1] - trailing_dot = True - for label in labels: - s = ulabel(label) - if s: - result.append(s) - else: - raise IDNAError("Empty label") - if trailing_dot: - result.append("") - return ".".join(result) diff --git a/apps/bitwarden_event_logs/lib/idna/idnadata.py b/apps/bitwarden_event_logs/lib/idna/idnadata.py deleted file mode 100755 index ded47cae..00000000 --- a/apps/bitwarden_event_logs/lib/idna/idnadata.py +++ /dev/null @@ -1,4309 +0,0 @@ -# This file is automatically generated by tools/idna-data - -__version__ = "16.0.0" - -scripts = { - "Greek": ( - 0x37000000374, - 0x37500000378, - 0x37A0000037E, - 0x37F00000380, - 0x38400000385, - 0x38600000387, - 0x3880000038B, - 0x38C0000038D, - 0x38E000003A2, - 0x3A3000003E2, - 0x3F000000400, - 0x1D2600001D2B, - 0x1D5D00001D62, - 0x1D6600001D6B, - 0x1DBF00001DC0, - 0x1F0000001F16, - 0x1F1800001F1E, - 0x1F2000001F46, - 0x1F4800001F4E, - 0x1F5000001F58, - 0x1F5900001F5A, - 0x1F5B00001F5C, - 0x1F5D00001F5E, - 0x1F5F00001F7E, - 0x1F8000001FB5, - 0x1FB600001FC5, - 0x1FC600001FD4, - 0x1FD600001FDC, - 0x1FDD00001FF0, - 0x1FF200001FF5, - 0x1FF600001FFF, - 0x212600002127, - 0xAB650000AB66, - 0x101400001018F, - 0x101A0000101A1, - 0x1D2000001D246, - ), - "Han": ( - 0x2E8000002E9A, - 0x2E9B00002EF4, - 0x2F0000002FD6, - 0x300500003006, - 0x300700003008, - 0x30210000302A, - 0x30380000303C, - 0x340000004DC0, - 0x4E000000A000, - 0xF9000000FA6E, - 0xFA700000FADA, - 0x16FE200016FE4, - 0x16FF000016FF2, - 0x200000002A6E0, - 0x2A7000002B73A, - 0x2B7400002B81E, - 0x2B8200002CEA2, - 0x2CEB00002EBE1, - 0x2EBF00002EE5E, - 0x2F8000002FA1E, - 0x300000003134B, - 0x31350000323B0, - ), - "Hebrew": ( - 0x591000005C8, - 0x5D0000005EB, - 0x5EF000005F5, - 0xFB1D0000FB37, - 0xFB380000FB3D, - 0xFB3E0000FB3F, - 0xFB400000FB42, - 0xFB430000FB45, - 0xFB460000FB50, - ), - "Hiragana": ( - 0x304100003097, - 0x309D000030A0, - 0x1B0010001B120, - 0x1B1320001B133, - 0x1B1500001B153, - 0x1F2000001F201, - ), - "Katakana": ( - 0x30A1000030FB, - 0x30FD00003100, - 0x31F000003200, - 0x32D0000032FF, - 0x330000003358, - 0xFF660000FF70, - 0xFF710000FF9E, - 0x1AFF00001AFF4, - 0x1AFF50001AFFC, - 0x1AFFD0001AFFF, - 0x1B0000001B001, - 0x1B1200001B123, - 0x1B1550001B156, - 0x1B1640001B168, - ), -} -joining_types = { - 0xAD: 84, - 0x300: 84, - 0x301: 84, - 0x302: 84, - 0x303: 84, - 0x304: 84, - 0x305: 84, - 0x306: 84, - 0x307: 84, - 0x308: 84, - 0x309: 84, - 0x30A: 84, - 0x30B: 84, - 0x30C: 84, - 0x30D: 84, - 0x30E: 84, - 0x30F: 84, - 0x310: 84, - 0x311: 84, - 0x312: 84, - 0x313: 84, - 0x314: 84, - 0x315: 84, - 0x316: 84, - 0x317: 84, - 0x318: 84, - 0x319: 84, - 0x31A: 84, - 0x31B: 84, - 0x31C: 84, - 0x31D: 84, - 0x31E: 84, - 0x31F: 84, - 0x320: 84, - 0x321: 84, - 0x322: 84, - 0x323: 84, - 0x324: 84, - 0x325: 84, - 0x326: 84, - 0x327: 84, - 0x328: 84, - 0x329: 84, - 0x32A: 84, - 0x32B: 84, - 0x32C: 84, - 0x32D: 84, - 0x32E: 84, - 0x32F: 84, - 0x330: 84, - 0x331: 84, - 0x332: 84, - 0x333: 84, - 0x334: 84, - 0x335: 84, - 0x336: 84, - 0x337: 84, - 0x338: 84, - 0x339: 84, - 0x33A: 84, - 0x33B: 84, - 0x33C: 84, - 0x33D: 84, - 0x33E: 84, - 0x33F: 84, - 0x340: 84, - 0x341: 84, - 0x342: 84, - 0x343: 84, - 0x344: 84, - 0x345: 84, - 0x346: 84, - 0x347: 84, - 0x348: 84, - 0x349: 84, - 0x34A: 84, - 0x34B: 84, - 0x34C: 84, - 0x34D: 84, - 0x34E: 84, - 0x34F: 84, - 0x350: 84, - 0x351: 84, - 0x352: 84, - 0x353: 84, - 0x354: 84, - 0x355: 84, - 0x356: 84, - 0x357: 84, - 0x358: 84, - 0x359: 84, - 0x35A: 84, - 0x35B: 84, - 0x35C: 84, - 0x35D: 84, - 0x35E: 84, - 0x35F: 84, - 0x360: 84, - 0x361: 84, - 0x362: 84, - 0x363: 84, - 0x364: 84, - 0x365: 84, - 0x366: 84, - 0x367: 84, - 0x368: 84, - 0x369: 84, - 0x36A: 84, - 0x36B: 84, - 0x36C: 84, - 0x36D: 84, - 0x36E: 84, - 0x36F: 84, - 0x483: 84, - 0x484: 84, - 0x485: 84, - 0x486: 84, - 0x487: 84, - 0x488: 84, - 0x489: 84, - 0x591: 84, - 0x592: 84, - 0x593: 84, - 0x594: 84, - 0x595: 84, - 0x596: 84, - 0x597: 84, - 0x598: 84, - 0x599: 84, - 0x59A: 84, - 0x59B: 84, - 0x59C: 84, - 0x59D: 84, - 0x59E: 84, - 0x59F: 84, - 0x5A0: 84, - 0x5A1: 84, - 0x5A2: 84, - 0x5A3: 84, - 0x5A4: 84, - 0x5A5: 84, - 0x5A6: 84, - 0x5A7: 84, - 0x5A8: 84, - 0x5A9: 84, - 0x5AA: 84, - 0x5AB: 84, - 0x5AC: 84, - 0x5AD: 84, - 0x5AE: 84, - 0x5AF: 84, - 0x5B0: 84, - 0x5B1: 84, - 0x5B2: 84, - 0x5B3: 84, - 0x5B4: 84, - 0x5B5: 84, - 0x5B6: 84, - 0x5B7: 84, - 0x5B8: 84, - 0x5B9: 84, - 0x5BA: 84, - 0x5BB: 84, - 0x5BC: 84, - 0x5BD: 84, - 0x5BF: 84, - 0x5C1: 84, - 0x5C2: 84, - 0x5C4: 84, - 0x5C5: 84, - 0x5C7: 84, - 0x610: 84, - 0x611: 84, - 0x612: 84, - 0x613: 84, - 0x614: 84, - 0x615: 84, - 0x616: 84, - 0x617: 84, - 0x618: 84, - 0x619: 84, - 0x61A: 84, - 0x61C: 84, - 0x620: 68, - 0x622: 82, - 0x623: 82, - 0x624: 82, - 0x625: 82, - 0x626: 68, - 0x627: 82, - 0x628: 68, - 0x629: 82, - 0x62A: 68, - 0x62B: 68, - 0x62C: 68, - 0x62D: 68, - 0x62E: 68, - 0x62F: 82, - 0x630: 82, - 0x631: 82, - 0x632: 82, - 0x633: 68, - 0x634: 68, - 0x635: 68, - 0x636: 68, - 0x637: 68, - 0x638: 68, - 0x639: 68, - 0x63A: 68, - 0x63B: 68, - 0x63C: 68, - 0x63D: 68, - 0x63E: 68, - 0x63F: 68, - 0x640: 67, - 0x641: 68, - 0x642: 68, - 0x643: 68, - 0x644: 68, - 0x645: 68, - 0x646: 68, - 0x647: 68, - 0x648: 82, - 0x649: 68, - 0x64A: 68, - 0x64B: 84, - 0x64C: 84, - 0x64D: 84, - 0x64E: 84, - 0x64F: 84, - 0x650: 84, - 0x651: 84, - 0x652: 84, - 0x653: 84, - 0x654: 84, - 0x655: 84, - 0x656: 84, - 0x657: 84, - 0x658: 84, - 0x659: 84, - 0x65A: 84, - 0x65B: 84, - 0x65C: 84, - 0x65D: 84, - 0x65E: 84, - 0x65F: 84, - 0x66E: 68, - 0x66F: 68, - 0x670: 84, - 0x671: 82, - 0x672: 82, - 0x673: 82, - 0x675: 82, - 0x676: 82, - 0x677: 82, - 0x678: 68, - 0x679: 68, - 0x67A: 68, - 0x67B: 68, - 0x67C: 68, - 0x67D: 68, - 0x67E: 68, - 0x67F: 68, - 0x680: 68, - 0x681: 68, - 0x682: 68, - 0x683: 68, - 0x684: 68, - 0x685: 68, - 0x686: 68, - 0x687: 68, - 0x688: 82, - 0x689: 82, - 0x68A: 82, - 0x68B: 82, - 0x68C: 82, - 0x68D: 82, - 0x68E: 82, - 0x68F: 82, - 0x690: 82, - 0x691: 82, - 0x692: 82, - 0x693: 82, - 0x694: 82, - 0x695: 82, - 0x696: 82, - 0x697: 82, - 0x698: 82, - 0x699: 82, - 0x69A: 68, - 0x69B: 68, - 0x69C: 68, - 0x69D: 68, - 0x69E: 68, - 0x69F: 68, - 0x6A0: 68, - 0x6A1: 68, - 0x6A2: 68, - 0x6A3: 68, - 0x6A4: 68, - 0x6A5: 68, - 0x6A6: 68, - 0x6A7: 68, - 0x6A8: 68, - 0x6A9: 68, - 0x6AA: 68, - 0x6AB: 68, - 0x6AC: 68, - 0x6AD: 68, - 0x6AE: 68, - 0x6AF: 68, - 0x6B0: 68, - 0x6B1: 68, - 0x6B2: 68, - 0x6B3: 68, - 0x6B4: 68, - 0x6B5: 68, - 0x6B6: 68, - 0x6B7: 68, - 0x6B8: 68, - 0x6B9: 68, - 0x6BA: 68, - 0x6BB: 68, - 0x6BC: 68, - 0x6BD: 68, - 0x6BE: 68, - 0x6BF: 68, - 0x6C0: 82, - 0x6C1: 68, - 0x6C2: 68, - 0x6C3: 82, - 0x6C4: 82, - 0x6C5: 82, - 0x6C6: 82, - 0x6C7: 82, - 0x6C8: 82, - 0x6C9: 82, - 0x6CA: 82, - 0x6CB: 82, - 0x6CC: 68, - 0x6CD: 82, - 0x6CE: 68, - 0x6CF: 82, - 0x6D0: 68, - 0x6D1: 68, - 0x6D2: 82, - 0x6D3: 82, - 0x6D5: 82, - 0x6D6: 84, - 0x6D7: 84, - 0x6D8: 84, - 0x6D9: 84, - 0x6DA: 84, - 0x6DB: 84, - 0x6DC: 84, - 0x6DF: 84, - 0x6E0: 84, - 0x6E1: 84, - 0x6E2: 84, - 0x6E3: 84, - 0x6E4: 84, - 0x6E7: 84, - 0x6E8: 84, - 0x6EA: 84, - 0x6EB: 84, - 0x6EC: 84, - 0x6ED: 84, - 0x6EE: 82, - 0x6EF: 82, - 0x6FA: 68, - 0x6FB: 68, - 0x6FC: 68, - 0x6FF: 68, - 0x70F: 84, - 0x710: 82, - 0x711: 84, - 0x712: 68, - 0x713: 68, - 0x714: 68, - 0x715: 82, - 0x716: 82, - 0x717: 82, - 0x718: 82, - 0x719: 82, - 0x71A: 68, - 0x71B: 68, - 0x71C: 68, - 0x71D: 68, - 0x71E: 82, - 0x71F: 68, - 0x720: 68, - 0x721: 68, - 0x722: 68, - 0x723: 68, - 0x724: 68, - 0x725: 68, - 0x726: 68, - 0x727: 68, - 0x728: 82, - 0x729: 68, - 0x72A: 82, - 0x72B: 68, - 0x72C: 82, - 0x72D: 68, - 0x72E: 68, - 0x72F: 82, - 0x730: 84, - 0x731: 84, - 0x732: 84, - 0x733: 84, - 0x734: 84, - 0x735: 84, - 0x736: 84, - 0x737: 84, - 0x738: 84, - 0x739: 84, - 0x73A: 84, - 0x73B: 84, - 0x73C: 84, - 0x73D: 84, - 0x73E: 84, - 0x73F: 84, - 0x740: 84, - 0x741: 84, - 0x742: 84, - 0x743: 84, - 0x744: 84, - 0x745: 84, - 0x746: 84, - 0x747: 84, - 0x748: 84, - 0x749: 84, - 0x74A: 84, - 0x74D: 82, - 0x74E: 68, - 0x74F: 68, - 0x750: 68, - 0x751: 68, - 0x752: 68, - 0x753: 68, - 0x754: 68, - 0x755: 68, - 0x756: 68, - 0x757: 68, - 0x758: 68, - 0x759: 82, - 0x75A: 82, - 0x75B: 82, - 0x75C: 68, - 0x75D: 68, - 0x75E: 68, - 0x75F: 68, - 0x760: 68, - 0x761: 68, - 0x762: 68, - 0x763: 68, - 0x764: 68, - 0x765: 68, - 0x766: 68, - 0x767: 68, - 0x768: 68, - 0x769: 68, - 0x76A: 68, - 0x76B: 82, - 0x76C: 82, - 0x76D: 68, - 0x76E: 68, - 0x76F: 68, - 0x770: 68, - 0x771: 82, - 0x772: 68, - 0x773: 82, - 0x774: 82, - 0x775: 68, - 0x776: 68, - 0x777: 68, - 0x778: 82, - 0x779: 82, - 0x77A: 68, - 0x77B: 68, - 0x77C: 68, - 0x77D: 68, - 0x77E: 68, - 0x77F: 68, - 0x7A6: 84, - 0x7A7: 84, - 0x7A8: 84, - 0x7A9: 84, - 0x7AA: 84, - 0x7AB: 84, - 0x7AC: 84, - 0x7AD: 84, - 0x7AE: 84, - 0x7AF: 84, - 0x7B0: 84, - 0x7CA: 68, - 0x7CB: 68, - 0x7CC: 68, - 0x7CD: 68, - 0x7CE: 68, - 0x7CF: 68, - 0x7D0: 68, - 0x7D1: 68, - 0x7D2: 68, - 0x7D3: 68, - 0x7D4: 68, - 0x7D5: 68, - 0x7D6: 68, - 0x7D7: 68, - 0x7D8: 68, - 0x7D9: 68, - 0x7DA: 68, - 0x7DB: 68, - 0x7DC: 68, - 0x7DD: 68, - 0x7DE: 68, - 0x7DF: 68, - 0x7E0: 68, - 0x7E1: 68, - 0x7E2: 68, - 0x7E3: 68, - 0x7E4: 68, - 0x7E5: 68, - 0x7E6: 68, - 0x7E7: 68, - 0x7E8: 68, - 0x7E9: 68, - 0x7EA: 68, - 0x7EB: 84, - 0x7EC: 84, - 0x7ED: 84, - 0x7EE: 84, - 0x7EF: 84, - 0x7F0: 84, - 0x7F1: 84, - 0x7F2: 84, - 0x7F3: 84, - 0x7FA: 67, - 0x7FD: 84, - 0x816: 84, - 0x817: 84, - 0x818: 84, - 0x819: 84, - 0x81B: 84, - 0x81C: 84, - 0x81D: 84, - 0x81E: 84, - 0x81F: 84, - 0x820: 84, - 0x821: 84, - 0x822: 84, - 0x823: 84, - 0x825: 84, - 0x826: 84, - 0x827: 84, - 0x829: 84, - 0x82A: 84, - 0x82B: 84, - 0x82C: 84, - 0x82D: 84, - 0x840: 82, - 0x841: 68, - 0x842: 68, - 0x843: 68, - 0x844: 68, - 0x845: 68, - 0x846: 82, - 0x847: 82, - 0x848: 68, - 0x849: 82, - 0x84A: 68, - 0x84B: 68, - 0x84C: 68, - 0x84D: 68, - 0x84E: 68, - 0x84F: 68, - 0x850: 68, - 0x851: 68, - 0x852: 68, - 0x853: 68, - 0x854: 82, - 0x855: 68, - 0x856: 82, - 0x857: 82, - 0x858: 82, - 0x859: 84, - 0x85A: 84, - 0x85B: 84, - 0x860: 68, - 0x862: 68, - 0x863: 68, - 0x864: 68, - 0x865: 68, - 0x867: 82, - 0x868: 68, - 0x869: 82, - 0x86A: 82, - 0x870: 82, - 0x871: 82, - 0x872: 82, - 0x873: 82, - 0x874: 82, - 0x875: 82, - 0x876: 82, - 0x877: 82, - 0x878: 82, - 0x879: 82, - 0x87A: 82, - 0x87B: 82, - 0x87C: 82, - 0x87D: 82, - 0x87E: 82, - 0x87F: 82, - 0x880: 82, - 0x881: 82, - 0x882: 82, - 0x883: 67, - 0x884: 67, - 0x885: 67, - 0x886: 68, - 0x889: 68, - 0x88A: 68, - 0x88B: 68, - 0x88C: 68, - 0x88D: 68, - 0x88E: 82, - 0x897: 84, - 0x898: 84, - 0x899: 84, - 0x89A: 84, - 0x89B: 84, - 0x89C: 84, - 0x89D: 84, - 0x89E: 84, - 0x89F: 84, - 0x8A0: 68, - 0x8A1: 68, - 0x8A2: 68, - 0x8A3: 68, - 0x8A4: 68, - 0x8A5: 68, - 0x8A6: 68, - 0x8A7: 68, - 0x8A8: 68, - 0x8A9: 68, - 0x8AA: 82, - 0x8AB: 82, - 0x8AC: 82, - 0x8AE: 82, - 0x8AF: 68, - 0x8B0: 68, - 0x8B1: 82, - 0x8B2: 82, - 0x8B3: 68, - 0x8B4: 68, - 0x8B5: 68, - 0x8B6: 68, - 0x8B7: 68, - 0x8B8: 68, - 0x8B9: 82, - 0x8BA: 68, - 0x8BB: 68, - 0x8BC: 68, - 0x8BD: 68, - 0x8BE: 68, - 0x8BF: 68, - 0x8C0: 68, - 0x8C1: 68, - 0x8C2: 68, - 0x8C3: 68, - 0x8C4: 68, - 0x8C5: 68, - 0x8C6: 68, - 0x8C7: 68, - 0x8C8: 68, - 0x8CA: 84, - 0x8CB: 84, - 0x8CC: 84, - 0x8CD: 84, - 0x8CE: 84, - 0x8CF: 84, - 0x8D0: 84, - 0x8D1: 84, - 0x8D2: 84, - 0x8D3: 84, - 0x8D4: 84, - 0x8D5: 84, - 0x8D6: 84, - 0x8D7: 84, - 0x8D8: 84, - 0x8D9: 84, - 0x8DA: 84, - 0x8DB: 84, - 0x8DC: 84, - 0x8DD: 84, - 0x8DE: 84, - 0x8DF: 84, - 0x8E0: 84, - 0x8E1: 84, - 0x8E3: 84, - 0x8E4: 84, - 0x8E5: 84, - 0x8E6: 84, - 0x8E7: 84, - 0x8E8: 84, - 0x8E9: 84, - 0x8EA: 84, - 0x8EB: 84, - 0x8EC: 84, - 0x8ED: 84, - 0x8EE: 84, - 0x8EF: 84, - 0x8F0: 84, - 0x8F1: 84, - 0x8F2: 84, - 0x8F3: 84, - 0x8F4: 84, - 0x8F5: 84, - 0x8F6: 84, - 0x8F7: 84, - 0x8F8: 84, - 0x8F9: 84, - 0x8FA: 84, - 0x8FB: 84, - 0x8FC: 84, - 0x8FD: 84, - 0x8FE: 84, - 0x8FF: 84, - 0x900: 84, - 0x901: 84, - 0x902: 84, - 0x93A: 84, - 0x93C: 84, - 0x941: 84, - 0x942: 84, - 0x943: 84, - 0x944: 84, - 0x945: 84, - 0x946: 84, - 0x947: 84, - 0x948: 84, - 0x94D: 84, - 0x951: 84, - 0x952: 84, - 0x953: 84, - 0x954: 84, - 0x955: 84, - 0x956: 84, - 0x957: 84, - 0x962: 84, - 0x963: 84, - 0x981: 84, - 0x9BC: 84, - 0x9C1: 84, - 0x9C2: 84, - 0x9C3: 84, - 0x9C4: 84, - 0x9CD: 84, - 0x9E2: 84, - 0x9E3: 84, - 0x9FE: 84, - 0xA01: 84, - 0xA02: 84, - 0xA3C: 84, - 0xA41: 84, - 0xA42: 84, - 0xA47: 84, - 0xA48: 84, - 0xA4B: 84, - 0xA4C: 84, - 0xA4D: 84, - 0xA51: 84, - 0xA70: 84, - 0xA71: 84, - 0xA75: 84, - 0xA81: 84, - 0xA82: 84, - 0xABC: 84, - 0xAC1: 84, - 0xAC2: 84, - 0xAC3: 84, - 0xAC4: 84, - 0xAC5: 84, - 0xAC7: 84, - 0xAC8: 84, - 0xACD: 84, - 0xAE2: 84, - 0xAE3: 84, - 0xAFA: 84, - 0xAFB: 84, - 0xAFC: 84, - 0xAFD: 84, - 0xAFE: 84, - 0xAFF: 84, - 0xB01: 84, - 0xB3C: 84, - 0xB3F: 84, - 0xB41: 84, - 0xB42: 84, - 0xB43: 84, - 0xB44: 84, - 0xB4D: 84, - 0xB55: 84, - 0xB56: 84, - 0xB62: 84, - 0xB63: 84, - 0xB82: 84, - 0xBC0: 84, - 0xBCD: 84, - 0xC00: 84, - 0xC04: 84, - 0xC3C: 84, - 0xC3E: 84, - 0xC3F: 84, - 0xC40: 84, - 0xC46: 84, - 0xC47: 84, - 0xC48: 84, - 0xC4A: 84, - 0xC4B: 84, - 0xC4C: 84, - 0xC4D: 84, - 0xC55: 84, - 0xC56: 84, - 0xC62: 84, - 0xC63: 84, - 0xC81: 84, - 0xCBC: 84, - 0xCBF: 84, - 0xCC6: 84, - 0xCCC: 84, - 0xCCD: 84, - 0xCE2: 84, - 0xCE3: 84, - 0xD00: 84, - 0xD01: 84, - 0xD3B: 84, - 0xD3C: 84, - 0xD41: 84, - 0xD42: 84, - 0xD43: 84, - 0xD44: 84, - 0xD4D: 84, - 0xD62: 84, - 0xD63: 84, - 0xD81: 84, - 0xDCA: 84, - 0xDD2: 84, - 0xDD3: 84, - 0xDD4: 84, - 0xDD6: 84, - 0xE31: 84, - 0xE34: 84, - 0xE35: 84, - 0xE36: 84, - 0xE37: 84, - 0xE38: 84, - 0xE39: 84, - 0xE3A: 84, - 0xE47: 84, - 0xE48: 84, - 0xE49: 84, - 0xE4A: 84, - 0xE4B: 84, - 0xE4C: 84, - 0xE4D: 84, - 0xE4E: 84, - 0xEB1: 84, - 0xEB4: 84, - 0xEB5: 84, - 0xEB6: 84, - 0xEB7: 84, - 0xEB8: 84, - 0xEB9: 84, - 0xEBA: 84, - 0xEBB: 84, - 0xEBC: 84, - 0xEC8: 84, - 0xEC9: 84, - 0xECA: 84, - 0xECB: 84, - 0xECC: 84, - 0xECD: 84, - 0xECE: 84, - 0xF18: 84, - 0xF19: 84, - 0xF35: 84, - 0xF37: 84, - 0xF39: 84, - 0xF71: 84, - 0xF72: 84, - 0xF73: 84, - 0xF74: 84, - 0xF75: 84, - 0xF76: 84, - 0xF77: 84, - 0xF78: 84, - 0xF79: 84, - 0xF7A: 84, - 0xF7B: 84, - 0xF7C: 84, - 0xF7D: 84, - 0xF7E: 84, - 0xF80: 84, - 0xF81: 84, - 0xF82: 84, - 0xF83: 84, - 0xF84: 84, - 0xF86: 84, - 0xF87: 84, - 0xF8D: 84, - 0xF8E: 84, - 0xF8F: 84, - 0xF90: 84, - 0xF91: 84, - 0xF92: 84, - 0xF93: 84, - 0xF94: 84, - 0xF95: 84, - 0xF96: 84, - 0xF97: 84, - 0xF99: 84, - 0xF9A: 84, - 0xF9B: 84, - 0xF9C: 84, - 0xF9D: 84, - 0xF9E: 84, - 0xF9F: 84, - 0xFA0: 84, - 0xFA1: 84, - 0xFA2: 84, - 0xFA3: 84, - 0xFA4: 84, - 0xFA5: 84, - 0xFA6: 84, - 0xFA7: 84, - 0xFA8: 84, - 0xFA9: 84, - 0xFAA: 84, - 0xFAB: 84, - 0xFAC: 84, - 0xFAD: 84, - 0xFAE: 84, - 0xFAF: 84, - 0xFB0: 84, - 0xFB1: 84, - 0xFB2: 84, - 0xFB3: 84, - 0xFB4: 84, - 0xFB5: 84, - 0xFB6: 84, - 0xFB7: 84, - 0xFB8: 84, - 0xFB9: 84, - 0xFBA: 84, - 0xFBB: 84, - 0xFBC: 84, - 0xFC6: 84, - 0x102D: 84, - 0x102E: 84, - 0x102F: 84, - 0x1030: 84, - 0x1032: 84, - 0x1033: 84, - 0x1034: 84, - 0x1035: 84, - 0x1036: 84, - 0x1037: 84, - 0x1039: 84, - 0x103A: 84, - 0x103D: 84, - 0x103E: 84, - 0x1058: 84, - 0x1059: 84, - 0x105E: 84, - 0x105F: 84, - 0x1060: 84, - 0x1071: 84, - 0x1072: 84, - 0x1073: 84, - 0x1074: 84, - 0x1082: 84, - 0x1085: 84, - 0x1086: 84, - 0x108D: 84, - 0x109D: 84, - 0x135D: 84, - 0x135E: 84, - 0x135F: 84, - 0x1712: 84, - 0x1713: 84, - 0x1714: 84, - 0x1732: 84, - 0x1733: 84, - 0x1752: 84, - 0x1753: 84, - 0x1772: 84, - 0x1773: 84, - 0x17B4: 84, - 0x17B5: 84, - 0x17B7: 84, - 0x17B8: 84, - 0x17B9: 84, - 0x17BA: 84, - 0x17BB: 84, - 0x17BC: 84, - 0x17BD: 84, - 0x17C6: 84, - 0x17C9: 84, - 0x17CA: 84, - 0x17CB: 84, - 0x17CC: 84, - 0x17CD: 84, - 0x17CE: 84, - 0x17CF: 84, - 0x17D0: 84, - 0x17D1: 84, - 0x17D2: 84, - 0x17D3: 84, - 0x17DD: 84, - 0x1807: 68, - 0x180A: 67, - 0x180B: 84, - 0x180C: 84, - 0x180D: 84, - 0x180F: 84, - 0x1820: 68, - 0x1821: 68, - 0x1822: 68, - 0x1823: 68, - 0x1824: 68, - 0x1825: 68, - 0x1826: 68, - 0x1827: 68, - 0x1828: 68, - 0x1829: 68, - 0x182A: 68, - 0x182B: 68, - 0x182C: 68, - 0x182D: 68, - 0x182E: 68, - 0x182F: 68, - 0x1830: 68, - 0x1831: 68, - 0x1832: 68, - 0x1833: 68, - 0x1834: 68, - 0x1835: 68, - 0x1836: 68, - 0x1837: 68, - 0x1838: 68, - 0x1839: 68, - 0x183A: 68, - 0x183B: 68, - 0x183C: 68, - 0x183D: 68, - 0x183E: 68, - 0x183F: 68, - 0x1840: 68, - 0x1841: 68, - 0x1842: 68, - 0x1843: 68, - 0x1844: 68, - 0x1845: 68, - 0x1846: 68, - 0x1847: 68, - 0x1848: 68, - 0x1849: 68, - 0x184A: 68, - 0x184B: 68, - 0x184C: 68, - 0x184D: 68, - 0x184E: 68, - 0x184F: 68, - 0x1850: 68, - 0x1851: 68, - 0x1852: 68, - 0x1853: 68, - 0x1854: 68, - 0x1855: 68, - 0x1856: 68, - 0x1857: 68, - 0x1858: 68, - 0x1859: 68, - 0x185A: 68, - 0x185B: 68, - 0x185C: 68, - 0x185D: 68, - 0x185E: 68, - 0x185F: 68, - 0x1860: 68, - 0x1861: 68, - 0x1862: 68, - 0x1863: 68, - 0x1864: 68, - 0x1865: 68, - 0x1866: 68, - 0x1867: 68, - 0x1868: 68, - 0x1869: 68, - 0x186A: 68, - 0x186B: 68, - 0x186C: 68, - 0x186D: 68, - 0x186E: 68, - 0x186F: 68, - 0x1870: 68, - 0x1871: 68, - 0x1872: 68, - 0x1873: 68, - 0x1874: 68, - 0x1875: 68, - 0x1876: 68, - 0x1877: 68, - 0x1878: 68, - 0x1885: 84, - 0x1886: 84, - 0x1887: 68, - 0x1888: 68, - 0x1889: 68, - 0x188A: 68, - 0x188B: 68, - 0x188C: 68, - 0x188D: 68, - 0x188E: 68, - 0x188F: 68, - 0x1890: 68, - 0x1891: 68, - 0x1892: 68, - 0x1893: 68, - 0x1894: 68, - 0x1895: 68, - 0x1896: 68, - 0x1897: 68, - 0x1898: 68, - 0x1899: 68, - 0x189A: 68, - 0x189B: 68, - 0x189C: 68, - 0x189D: 68, - 0x189E: 68, - 0x189F: 68, - 0x18A0: 68, - 0x18A1: 68, - 0x18A2: 68, - 0x18A3: 68, - 0x18A4: 68, - 0x18A5: 68, - 0x18A6: 68, - 0x18A7: 68, - 0x18A8: 68, - 0x18A9: 84, - 0x18AA: 68, - 0x1920: 84, - 0x1921: 84, - 0x1922: 84, - 0x1927: 84, - 0x1928: 84, - 0x1932: 84, - 0x1939: 84, - 0x193A: 84, - 0x193B: 84, - 0x1A17: 84, - 0x1A18: 84, - 0x1A1B: 84, - 0x1A56: 84, - 0x1A58: 84, - 0x1A59: 84, - 0x1A5A: 84, - 0x1A5B: 84, - 0x1A5C: 84, - 0x1A5D: 84, - 0x1A5E: 84, - 0x1A60: 84, - 0x1A62: 84, - 0x1A65: 84, - 0x1A66: 84, - 0x1A67: 84, - 0x1A68: 84, - 0x1A69: 84, - 0x1A6A: 84, - 0x1A6B: 84, - 0x1A6C: 84, - 0x1A73: 84, - 0x1A74: 84, - 0x1A75: 84, - 0x1A76: 84, - 0x1A77: 84, - 0x1A78: 84, - 0x1A79: 84, - 0x1A7A: 84, - 0x1A7B: 84, - 0x1A7C: 84, - 0x1A7F: 84, - 0x1AB0: 84, - 0x1AB1: 84, - 0x1AB2: 84, - 0x1AB3: 84, - 0x1AB4: 84, - 0x1AB5: 84, - 0x1AB6: 84, - 0x1AB7: 84, - 0x1AB8: 84, - 0x1AB9: 84, - 0x1ABA: 84, - 0x1ABB: 84, - 0x1ABC: 84, - 0x1ABD: 84, - 0x1ABE: 84, - 0x1ABF: 84, - 0x1AC0: 84, - 0x1AC1: 84, - 0x1AC2: 84, - 0x1AC3: 84, - 0x1AC4: 84, - 0x1AC5: 84, - 0x1AC6: 84, - 0x1AC7: 84, - 0x1AC8: 84, - 0x1AC9: 84, - 0x1ACA: 84, - 0x1ACB: 84, - 0x1ACC: 84, - 0x1ACD: 84, - 0x1ACE: 84, - 0x1B00: 84, - 0x1B01: 84, - 0x1B02: 84, - 0x1B03: 84, - 0x1B34: 84, - 0x1B36: 84, - 0x1B37: 84, - 0x1B38: 84, - 0x1B39: 84, - 0x1B3A: 84, - 0x1B3C: 84, - 0x1B42: 84, - 0x1B6B: 84, - 0x1B6C: 84, - 0x1B6D: 84, - 0x1B6E: 84, - 0x1B6F: 84, - 0x1B70: 84, - 0x1B71: 84, - 0x1B72: 84, - 0x1B73: 84, - 0x1B80: 84, - 0x1B81: 84, - 0x1BA2: 84, - 0x1BA3: 84, - 0x1BA4: 84, - 0x1BA5: 84, - 0x1BA8: 84, - 0x1BA9: 84, - 0x1BAB: 84, - 0x1BAC: 84, - 0x1BAD: 84, - 0x1BE6: 84, - 0x1BE8: 84, - 0x1BE9: 84, - 0x1BED: 84, - 0x1BEF: 84, - 0x1BF0: 84, - 0x1BF1: 84, - 0x1C2C: 84, - 0x1C2D: 84, - 0x1C2E: 84, - 0x1C2F: 84, - 0x1C30: 84, - 0x1C31: 84, - 0x1C32: 84, - 0x1C33: 84, - 0x1C36: 84, - 0x1C37: 84, - 0x1CD0: 84, - 0x1CD1: 84, - 0x1CD2: 84, - 0x1CD4: 84, - 0x1CD5: 84, - 0x1CD6: 84, - 0x1CD7: 84, - 0x1CD8: 84, - 0x1CD9: 84, - 0x1CDA: 84, - 0x1CDB: 84, - 0x1CDC: 84, - 0x1CDD: 84, - 0x1CDE: 84, - 0x1CDF: 84, - 0x1CE0: 84, - 0x1CE2: 84, - 0x1CE3: 84, - 0x1CE4: 84, - 0x1CE5: 84, - 0x1CE6: 84, - 0x1CE7: 84, - 0x1CE8: 84, - 0x1CED: 84, - 0x1CF4: 84, - 0x1CF8: 84, - 0x1CF9: 84, - 0x1DC0: 84, - 0x1DC1: 84, - 0x1DC2: 84, - 0x1DC3: 84, - 0x1DC4: 84, - 0x1DC5: 84, - 0x1DC6: 84, - 0x1DC7: 84, - 0x1DC8: 84, - 0x1DC9: 84, - 0x1DCA: 84, - 0x1DCB: 84, - 0x1DCC: 84, - 0x1DCD: 84, - 0x1DCE: 84, - 0x1DCF: 84, - 0x1DD0: 84, - 0x1DD1: 84, - 0x1DD2: 84, - 0x1DD3: 84, - 0x1DD4: 84, - 0x1DD5: 84, - 0x1DD6: 84, - 0x1DD7: 84, - 0x1DD8: 84, - 0x1DD9: 84, - 0x1DDA: 84, - 0x1DDB: 84, - 0x1DDC: 84, - 0x1DDD: 84, - 0x1DDE: 84, - 0x1DDF: 84, - 0x1DE0: 84, - 0x1DE1: 84, - 0x1DE2: 84, - 0x1DE3: 84, - 0x1DE4: 84, - 0x1DE5: 84, - 0x1DE6: 84, - 0x1DE7: 84, - 0x1DE8: 84, - 0x1DE9: 84, - 0x1DEA: 84, - 0x1DEB: 84, - 0x1DEC: 84, - 0x1DED: 84, - 0x1DEE: 84, - 0x1DEF: 84, - 0x1DF0: 84, - 0x1DF1: 84, - 0x1DF2: 84, - 0x1DF3: 84, - 0x1DF4: 84, - 0x1DF5: 84, - 0x1DF6: 84, - 0x1DF7: 84, - 0x1DF8: 84, - 0x1DF9: 84, - 0x1DFA: 84, - 0x1DFB: 84, - 0x1DFC: 84, - 0x1DFD: 84, - 0x1DFE: 84, - 0x1DFF: 84, - 0x200B: 84, - 0x200D: 67, - 0x200E: 84, - 0x200F: 84, - 0x202A: 84, - 0x202B: 84, - 0x202C: 84, - 0x202D: 84, - 0x202E: 84, - 0x2060: 84, - 0x2061: 84, - 0x2062: 84, - 0x2063: 84, - 0x2064: 84, - 0x206A: 84, - 0x206B: 84, - 0x206C: 84, - 0x206D: 84, - 0x206E: 84, - 0x206F: 84, - 0x20D0: 84, - 0x20D1: 84, - 0x20D2: 84, - 0x20D3: 84, - 0x20D4: 84, - 0x20D5: 84, - 0x20D6: 84, - 0x20D7: 84, - 0x20D8: 84, - 0x20D9: 84, - 0x20DA: 84, - 0x20DB: 84, - 0x20DC: 84, - 0x20DD: 84, - 0x20DE: 84, - 0x20DF: 84, - 0x20E0: 84, - 0x20E1: 84, - 0x20E2: 84, - 0x20E3: 84, - 0x20E4: 84, - 0x20E5: 84, - 0x20E6: 84, - 0x20E7: 84, - 0x20E8: 84, - 0x20E9: 84, - 0x20EA: 84, - 0x20EB: 84, - 0x20EC: 84, - 0x20ED: 84, - 0x20EE: 84, - 0x20EF: 84, - 0x20F0: 84, - 0x2CEF: 84, - 0x2CF0: 84, - 0x2CF1: 84, - 0x2D7F: 84, - 0x2DE0: 84, - 0x2DE1: 84, - 0x2DE2: 84, - 0x2DE3: 84, - 0x2DE4: 84, - 0x2DE5: 84, - 0x2DE6: 84, - 0x2DE7: 84, - 0x2DE8: 84, - 0x2DE9: 84, - 0x2DEA: 84, - 0x2DEB: 84, - 0x2DEC: 84, - 0x2DED: 84, - 0x2DEE: 84, - 0x2DEF: 84, - 0x2DF0: 84, - 0x2DF1: 84, - 0x2DF2: 84, - 0x2DF3: 84, - 0x2DF4: 84, - 0x2DF5: 84, - 0x2DF6: 84, - 0x2DF7: 84, - 0x2DF8: 84, - 0x2DF9: 84, - 0x2DFA: 84, - 0x2DFB: 84, - 0x2DFC: 84, - 0x2DFD: 84, - 0x2DFE: 84, - 0x2DFF: 84, - 0x302A: 84, - 0x302B: 84, - 0x302C: 84, - 0x302D: 84, - 0x3099: 84, - 0x309A: 84, - 0xA66F: 84, - 0xA670: 84, - 0xA671: 84, - 0xA672: 84, - 0xA674: 84, - 0xA675: 84, - 0xA676: 84, - 0xA677: 84, - 0xA678: 84, - 0xA679: 84, - 0xA67A: 84, - 0xA67B: 84, - 0xA67C: 84, - 0xA67D: 84, - 0xA69E: 84, - 0xA69F: 84, - 0xA6F0: 84, - 0xA6F1: 84, - 0xA802: 84, - 0xA806: 84, - 0xA80B: 84, - 0xA825: 84, - 0xA826: 84, - 0xA82C: 84, - 0xA840: 68, - 0xA841: 68, - 0xA842: 68, - 0xA843: 68, - 0xA844: 68, - 0xA845: 68, - 0xA846: 68, - 0xA847: 68, - 0xA848: 68, - 0xA849: 68, - 0xA84A: 68, - 0xA84B: 68, - 0xA84C: 68, - 0xA84D: 68, - 0xA84E: 68, - 0xA84F: 68, - 0xA850: 68, - 0xA851: 68, - 0xA852: 68, - 0xA853: 68, - 0xA854: 68, - 0xA855: 68, - 0xA856: 68, - 0xA857: 68, - 0xA858: 68, - 0xA859: 68, - 0xA85A: 68, - 0xA85B: 68, - 0xA85C: 68, - 0xA85D: 68, - 0xA85E: 68, - 0xA85F: 68, - 0xA860: 68, - 0xA861: 68, - 0xA862: 68, - 0xA863: 68, - 0xA864: 68, - 0xA865: 68, - 0xA866: 68, - 0xA867: 68, - 0xA868: 68, - 0xA869: 68, - 0xA86A: 68, - 0xA86B: 68, - 0xA86C: 68, - 0xA86D: 68, - 0xA86E: 68, - 0xA86F: 68, - 0xA870: 68, - 0xA871: 68, - 0xA872: 76, - 0xA8C4: 84, - 0xA8C5: 84, - 0xA8E0: 84, - 0xA8E1: 84, - 0xA8E2: 84, - 0xA8E3: 84, - 0xA8E4: 84, - 0xA8E5: 84, - 0xA8E6: 84, - 0xA8E7: 84, - 0xA8E8: 84, - 0xA8E9: 84, - 0xA8EA: 84, - 0xA8EB: 84, - 0xA8EC: 84, - 0xA8ED: 84, - 0xA8EE: 84, - 0xA8EF: 84, - 0xA8F0: 84, - 0xA8F1: 84, - 0xA8FF: 84, - 0xA926: 84, - 0xA927: 84, - 0xA928: 84, - 0xA929: 84, - 0xA92A: 84, - 0xA92B: 84, - 0xA92C: 84, - 0xA92D: 84, - 0xA947: 84, - 0xA948: 84, - 0xA949: 84, - 0xA94A: 84, - 0xA94B: 84, - 0xA94C: 84, - 0xA94D: 84, - 0xA94E: 84, - 0xA94F: 84, - 0xA950: 84, - 0xA951: 84, - 0xA980: 84, - 0xA981: 84, - 0xA982: 84, - 0xA9B3: 84, - 0xA9B6: 84, - 0xA9B7: 84, - 0xA9B8: 84, - 0xA9B9: 84, - 0xA9BC: 84, - 0xA9BD: 84, - 0xA9E5: 84, - 0xAA29: 84, - 0xAA2A: 84, - 0xAA2B: 84, - 0xAA2C: 84, - 0xAA2D: 84, - 0xAA2E: 84, - 0xAA31: 84, - 0xAA32: 84, - 0xAA35: 84, - 0xAA36: 84, - 0xAA43: 84, - 0xAA4C: 84, - 0xAA7C: 84, - 0xAAB0: 84, - 0xAAB2: 84, - 0xAAB3: 84, - 0xAAB4: 84, - 0xAAB7: 84, - 0xAAB8: 84, - 0xAABE: 84, - 0xAABF: 84, - 0xAAC1: 84, - 0xAAEC: 84, - 0xAAED: 84, - 0xAAF6: 84, - 0xABE5: 84, - 0xABE8: 84, - 0xABED: 84, - 0xFB1E: 84, - 0xFE00: 84, - 0xFE01: 84, - 0xFE02: 84, - 0xFE03: 84, - 0xFE04: 84, - 0xFE05: 84, - 0xFE06: 84, - 0xFE07: 84, - 0xFE08: 84, - 0xFE09: 84, - 0xFE0A: 84, - 0xFE0B: 84, - 0xFE0C: 84, - 0xFE0D: 84, - 0xFE0E: 84, - 0xFE0F: 84, - 0xFE20: 84, - 0xFE21: 84, - 0xFE22: 84, - 0xFE23: 84, - 0xFE24: 84, - 0xFE25: 84, - 0xFE26: 84, - 0xFE27: 84, - 0xFE28: 84, - 0xFE29: 84, - 0xFE2A: 84, - 0xFE2B: 84, - 0xFE2C: 84, - 0xFE2D: 84, - 0xFE2E: 84, - 0xFE2F: 84, - 0xFEFF: 84, - 0xFFF9: 84, - 0xFFFA: 84, - 0xFFFB: 84, - 0x101FD: 84, - 0x102E0: 84, - 0x10376: 84, - 0x10377: 84, - 0x10378: 84, - 0x10379: 84, - 0x1037A: 84, - 0x10A01: 84, - 0x10A02: 84, - 0x10A03: 84, - 0x10A05: 84, - 0x10A06: 84, - 0x10A0C: 84, - 0x10A0D: 84, - 0x10A0E: 84, - 0x10A0F: 84, - 0x10A38: 84, - 0x10A39: 84, - 0x10A3A: 84, - 0x10A3F: 84, - 0x10AC0: 68, - 0x10AC1: 68, - 0x10AC2: 68, - 0x10AC3: 68, - 0x10AC4: 68, - 0x10AC5: 82, - 0x10AC7: 82, - 0x10AC9: 82, - 0x10ACA: 82, - 0x10ACD: 76, - 0x10ACE: 82, - 0x10ACF: 82, - 0x10AD0: 82, - 0x10AD1: 82, - 0x10AD2: 82, - 0x10AD3: 68, - 0x10AD4: 68, - 0x10AD5: 68, - 0x10AD6: 68, - 0x10AD7: 76, - 0x10AD8: 68, - 0x10AD9: 68, - 0x10ADA: 68, - 0x10ADB: 68, - 0x10ADC: 68, - 0x10ADD: 82, - 0x10ADE: 68, - 0x10ADF: 68, - 0x10AE0: 68, - 0x10AE1: 82, - 0x10AE4: 82, - 0x10AE5: 84, - 0x10AE6: 84, - 0x10AEB: 68, - 0x10AEC: 68, - 0x10AED: 68, - 0x10AEE: 68, - 0x10AEF: 82, - 0x10B80: 68, - 0x10B81: 82, - 0x10B82: 68, - 0x10B83: 82, - 0x10B84: 82, - 0x10B85: 82, - 0x10B86: 68, - 0x10B87: 68, - 0x10B88: 68, - 0x10B89: 82, - 0x10B8A: 68, - 0x10B8B: 68, - 0x10B8C: 82, - 0x10B8D: 68, - 0x10B8E: 82, - 0x10B8F: 82, - 0x10B90: 68, - 0x10B91: 82, - 0x10BA9: 82, - 0x10BAA: 82, - 0x10BAB: 82, - 0x10BAC: 82, - 0x10BAD: 68, - 0x10BAE: 68, - 0x10D00: 76, - 0x10D01: 68, - 0x10D02: 68, - 0x10D03: 68, - 0x10D04: 68, - 0x10D05: 68, - 0x10D06: 68, - 0x10D07: 68, - 0x10D08: 68, - 0x10D09: 68, - 0x10D0A: 68, - 0x10D0B: 68, - 0x10D0C: 68, - 0x10D0D: 68, - 0x10D0E: 68, - 0x10D0F: 68, - 0x10D10: 68, - 0x10D11: 68, - 0x10D12: 68, - 0x10D13: 68, - 0x10D14: 68, - 0x10D15: 68, - 0x10D16: 68, - 0x10D17: 68, - 0x10D18: 68, - 0x10D19: 68, - 0x10D1A: 68, - 0x10D1B: 68, - 0x10D1C: 68, - 0x10D1D: 68, - 0x10D1E: 68, - 0x10D1F: 68, - 0x10D20: 68, - 0x10D21: 68, - 0x10D22: 82, - 0x10D23: 68, - 0x10D24: 84, - 0x10D25: 84, - 0x10D26: 84, - 0x10D27: 84, - 0x10D69: 84, - 0x10D6A: 84, - 0x10D6B: 84, - 0x10D6C: 84, - 0x10D6D: 84, - 0x10EAB: 84, - 0x10EAC: 84, - 0x10EC2: 82, - 0x10EC3: 68, - 0x10EC4: 68, - 0x10EFC: 84, - 0x10EFD: 84, - 0x10EFE: 84, - 0x10EFF: 84, - 0x10F30: 68, - 0x10F31: 68, - 0x10F32: 68, - 0x10F33: 82, - 0x10F34: 68, - 0x10F35: 68, - 0x10F36: 68, - 0x10F37: 68, - 0x10F38: 68, - 0x10F39: 68, - 0x10F3A: 68, - 0x10F3B: 68, - 0x10F3C: 68, - 0x10F3D: 68, - 0x10F3E: 68, - 0x10F3F: 68, - 0x10F40: 68, - 0x10F41: 68, - 0x10F42: 68, - 0x10F43: 68, - 0x10F44: 68, - 0x10F46: 84, - 0x10F47: 84, - 0x10F48: 84, - 0x10F49: 84, - 0x10F4A: 84, - 0x10F4B: 84, - 0x10F4C: 84, - 0x10F4D: 84, - 0x10F4E: 84, - 0x10F4F: 84, - 0x10F50: 84, - 0x10F51: 68, - 0x10F52: 68, - 0x10F53: 68, - 0x10F54: 82, - 0x10F70: 68, - 0x10F71: 68, - 0x10F72: 68, - 0x10F73: 68, - 0x10F74: 82, - 0x10F75: 82, - 0x10F76: 68, - 0x10F77: 68, - 0x10F78: 68, - 0x10F79: 68, - 0x10F7A: 68, - 0x10F7B: 68, - 0x10F7C: 68, - 0x10F7D: 68, - 0x10F7E: 68, - 0x10F7F: 68, - 0x10F80: 68, - 0x10F81: 68, - 0x10F82: 84, - 0x10F83: 84, - 0x10F84: 84, - 0x10F85: 84, - 0x10FB0: 68, - 0x10FB2: 68, - 0x10FB3: 68, - 0x10FB4: 82, - 0x10FB5: 82, - 0x10FB6: 82, - 0x10FB8: 68, - 0x10FB9: 82, - 0x10FBA: 82, - 0x10FBB: 68, - 0x10FBC: 68, - 0x10FBD: 82, - 0x10FBE: 68, - 0x10FBF: 68, - 0x10FC1: 68, - 0x10FC2: 82, - 0x10FC3: 82, - 0x10FC4: 68, - 0x10FC9: 82, - 0x10FCA: 68, - 0x10FCB: 76, - 0x11001: 84, - 0x11038: 84, - 0x11039: 84, - 0x1103A: 84, - 0x1103B: 84, - 0x1103C: 84, - 0x1103D: 84, - 0x1103E: 84, - 0x1103F: 84, - 0x11040: 84, - 0x11041: 84, - 0x11042: 84, - 0x11043: 84, - 0x11044: 84, - 0x11045: 84, - 0x11046: 84, - 0x11070: 84, - 0x11073: 84, - 0x11074: 84, - 0x1107F: 84, - 0x11080: 84, - 0x11081: 84, - 0x110B3: 84, - 0x110B4: 84, - 0x110B5: 84, - 0x110B6: 84, - 0x110B9: 84, - 0x110BA: 84, - 0x110C2: 84, - 0x11100: 84, - 0x11101: 84, - 0x11102: 84, - 0x11127: 84, - 0x11128: 84, - 0x11129: 84, - 0x1112A: 84, - 0x1112B: 84, - 0x1112D: 84, - 0x1112E: 84, - 0x1112F: 84, - 0x11130: 84, - 0x11131: 84, - 0x11132: 84, - 0x11133: 84, - 0x11134: 84, - 0x11173: 84, - 0x11180: 84, - 0x11181: 84, - 0x111B6: 84, - 0x111B7: 84, - 0x111B8: 84, - 0x111B9: 84, - 0x111BA: 84, - 0x111BB: 84, - 0x111BC: 84, - 0x111BD: 84, - 0x111BE: 84, - 0x111C9: 84, - 0x111CA: 84, - 0x111CB: 84, - 0x111CC: 84, - 0x111CF: 84, - 0x1122F: 84, - 0x11230: 84, - 0x11231: 84, - 0x11234: 84, - 0x11236: 84, - 0x11237: 84, - 0x1123E: 84, - 0x11241: 84, - 0x112DF: 84, - 0x112E3: 84, - 0x112E4: 84, - 0x112E5: 84, - 0x112E6: 84, - 0x112E7: 84, - 0x112E8: 84, - 0x112E9: 84, - 0x112EA: 84, - 0x11300: 84, - 0x11301: 84, - 0x1133B: 84, - 0x1133C: 84, - 0x11340: 84, - 0x11366: 84, - 0x11367: 84, - 0x11368: 84, - 0x11369: 84, - 0x1136A: 84, - 0x1136B: 84, - 0x1136C: 84, - 0x11370: 84, - 0x11371: 84, - 0x11372: 84, - 0x11373: 84, - 0x11374: 84, - 0x113BB: 84, - 0x113BC: 84, - 0x113BD: 84, - 0x113BE: 84, - 0x113BF: 84, - 0x113C0: 84, - 0x113CE: 84, - 0x113D0: 84, - 0x113D2: 84, - 0x113E1: 84, - 0x113E2: 84, - 0x11438: 84, - 0x11439: 84, - 0x1143A: 84, - 0x1143B: 84, - 0x1143C: 84, - 0x1143D: 84, - 0x1143E: 84, - 0x1143F: 84, - 0x11442: 84, - 0x11443: 84, - 0x11444: 84, - 0x11446: 84, - 0x1145E: 84, - 0x114B3: 84, - 0x114B4: 84, - 0x114B5: 84, - 0x114B6: 84, - 0x114B7: 84, - 0x114B8: 84, - 0x114BA: 84, - 0x114BF: 84, - 0x114C0: 84, - 0x114C2: 84, - 0x114C3: 84, - 0x115B2: 84, - 0x115B3: 84, - 0x115B4: 84, - 0x115B5: 84, - 0x115BC: 84, - 0x115BD: 84, - 0x115BF: 84, - 0x115C0: 84, - 0x115DC: 84, - 0x115DD: 84, - 0x11633: 84, - 0x11634: 84, - 0x11635: 84, - 0x11636: 84, - 0x11637: 84, - 0x11638: 84, - 0x11639: 84, - 0x1163A: 84, - 0x1163D: 84, - 0x1163F: 84, - 0x11640: 84, - 0x116AB: 84, - 0x116AD: 84, - 0x116B0: 84, - 0x116B1: 84, - 0x116B2: 84, - 0x116B3: 84, - 0x116B4: 84, - 0x116B5: 84, - 0x116B7: 84, - 0x1171D: 84, - 0x1171F: 84, - 0x11722: 84, - 0x11723: 84, - 0x11724: 84, - 0x11725: 84, - 0x11727: 84, - 0x11728: 84, - 0x11729: 84, - 0x1172A: 84, - 0x1172B: 84, - 0x1182F: 84, - 0x11830: 84, - 0x11831: 84, - 0x11832: 84, - 0x11833: 84, - 0x11834: 84, - 0x11835: 84, - 0x11836: 84, - 0x11837: 84, - 0x11839: 84, - 0x1183A: 84, - 0x1193B: 84, - 0x1193C: 84, - 0x1193E: 84, - 0x11943: 84, - 0x119D4: 84, - 0x119D5: 84, - 0x119D6: 84, - 0x119D7: 84, - 0x119DA: 84, - 0x119DB: 84, - 0x119E0: 84, - 0x11A01: 84, - 0x11A02: 84, - 0x11A03: 84, - 0x11A04: 84, - 0x11A05: 84, - 0x11A06: 84, - 0x11A07: 84, - 0x11A08: 84, - 0x11A09: 84, - 0x11A0A: 84, - 0x11A33: 84, - 0x11A34: 84, - 0x11A35: 84, - 0x11A36: 84, - 0x11A37: 84, - 0x11A38: 84, - 0x11A3B: 84, - 0x11A3C: 84, - 0x11A3D: 84, - 0x11A3E: 84, - 0x11A47: 84, - 0x11A51: 84, - 0x11A52: 84, - 0x11A53: 84, - 0x11A54: 84, - 0x11A55: 84, - 0x11A56: 84, - 0x11A59: 84, - 0x11A5A: 84, - 0x11A5B: 84, - 0x11A8A: 84, - 0x11A8B: 84, - 0x11A8C: 84, - 0x11A8D: 84, - 0x11A8E: 84, - 0x11A8F: 84, - 0x11A90: 84, - 0x11A91: 84, - 0x11A92: 84, - 0x11A93: 84, - 0x11A94: 84, - 0x11A95: 84, - 0x11A96: 84, - 0x11A98: 84, - 0x11A99: 84, - 0x11C30: 84, - 0x11C31: 84, - 0x11C32: 84, - 0x11C33: 84, - 0x11C34: 84, - 0x11C35: 84, - 0x11C36: 84, - 0x11C38: 84, - 0x11C39: 84, - 0x11C3A: 84, - 0x11C3B: 84, - 0x11C3C: 84, - 0x11C3D: 84, - 0x11C3F: 84, - 0x11C92: 84, - 0x11C93: 84, - 0x11C94: 84, - 0x11C95: 84, - 0x11C96: 84, - 0x11C97: 84, - 0x11C98: 84, - 0x11C99: 84, - 0x11C9A: 84, - 0x11C9B: 84, - 0x11C9C: 84, - 0x11C9D: 84, - 0x11C9E: 84, - 0x11C9F: 84, - 0x11CA0: 84, - 0x11CA1: 84, - 0x11CA2: 84, - 0x11CA3: 84, - 0x11CA4: 84, - 0x11CA5: 84, - 0x11CA6: 84, - 0x11CA7: 84, - 0x11CAA: 84, - 0x11CAB: 84, - 0x11CAC: 84, - 0x11CAD: 84, - 0x11CAE: 84, - 0x11CAF: 84, - 0x11CB0: 84, - 0x11CB2: 84, - 0x11CB3: 84, - 0x11CB5: 84, - 0x11CB6: 84, - 0x11D31: 84, - 0x11D32: 84, - 0x11D33: 84, - 0x11D34: 84, - 0x11D35: 84, - 0x11D36: 84, - 0x11D3A: 84, - 0x11D3C: 84, - 0x11D3D: 84, - 0x11D3F: 84, - 0x11D40: 84, - 0x11D41: 84, - 0x11D42: 84, - 0x11D43: 84, - 0x11D44: 84, - 0x11D45: 84, - 0x11D47: 84, - 0x11D90: 84, - 0x11D91: 84, - 0x11D95: 84, - 0x11D97: 84, - 0x11EF3: 84, - 0x11EF4: 84, - 0x11F00: 84, - 0x11F01: 84, - 0x11F36: 84, - 0x11F37: 84, - 0x11F38: 84, - 0x11F39: 84, - 0x11F3A: 84, - 0x11F40: 84, - 0x11F42: 84, - 0x11F5A: 84, - 0x13430: 84, - 0x13431: 84, - 0x13432: 84, - 0x13433: 84, - 0x13434: 84, - 0x13435: 84, - 0x13436: 84, - 0x13437: 84, - 0x13438: 84, - 0x13439: 84, - 0x1343A: 84, - 0x1343B: 84, - 0x1343C: 84, - 0x1343D: 84, - 0x1343E: 84, - 0x1343F: 84, - 0x13440: 84, - 0x13447: 84, - 0x13448: 84, - 0x13449: 84, - 0x1344A: 84, - 0x1344B: 84, - 0x1344C: 84, - 0x1344D: 84, - 0x1344E: 84, - 0x1344F: 84, - 0x13450: 84, - 0x13451: 84, - 0x13452: 84, - 0x13453: 84, - 0x13454: 84, - 0x13455: 84, - 0x1611E: 84, - 0x1611F: 84, - 0x16120: 84, - 0x16121: 84, - 0x16122: 84, - 0x16123: 84, - 0x16124: 84, - 0x16125: 84, - 0x16126: 84, - 0x16127: 84, - 0x16128: 84, - 0x16129: 84, - 0x1612D: 84, - 0x1612E: 84, - 0x1612F: 84, - 0x16AF0: 84, - 0x16AF1: 84, - 0x16AF2: 84, - 0x16AF3: 84, - 0x16AF4: 84, - 0x16B30: 84, - 0x16B31: 84, - 0x16B32: 84, - 0x16B33: 84, - 0x16B34: 84, - 0x16B35: 84, - 0x16B36: 84, - 0x16F4F: 84, - 0x16F8F: 84, - 0x16F90: 84, - 0x16F91: 84, - 0x16F92: 84, - 0x16FE4: 84, - 0x1BC9D: 84, - 0x1BC9E: 84, - 0x1BCA0: 84, - 0x1BCA1: 84, - 0x1BCA2: 84, - 0x1BCA3: 84, - 0x1CF00: 84, - 0x1CF01: 84, - 0x1CF02: 84, - 0x1CF03: 84, - 0x1CF04: 84, - 0x1CF05: 84, - 0x1CF06: 84, - 0x1CF07: 84, - 0x1CF08: 84, - 0x1CF09: 84, - 0x1CF0A: 84, - 0x1CF0B: 84, - 0x1CF0C: 84, - 0x1CF0D: 84, - 0x1CF0E: 84, - 0x1CF0F: 84, - 0x1CF10: 84, - 0x1CF11: 84, - 0x1CF12: 84, - 0x1CF13: 84, - 0x1CF14: 84, - 0x1CF15: 84, - 0x1CF16: 84, - 0x1CF17: 84, - 0x1CF18: 84, - 0x1CF19: 84, - 0x1CF1A: 84, - 0x1CF1B: 84, - 0x1CF1C: 84, - 0x1CF1D: 84, - 0x1CF1E: 84, - 0x1CF1F: 84, - 0x1CF20: 84, - 0x1CF21: 84, - 0x1CF22: 84, - 0x1CF23: 84, - 0x1CF24: 84, - 0x1CF25: 84, - 0x1CF26: 84, - 0x1CF27: 84, - 0x1CF28: 84, - 0x1CF29: 84, - 0x1CF2A: 84, - 0x1CF2B: 84, - 0x1CF2C: 84, - 0x1CF2D: 84, - 0x1CF30: 84, - 0x1CF31: 84, - 0x1CF32: 84, - 0x1CF33: 84, - 0x1CF34: 84, - 0x1CF35: 84, - 0x1CF36: 84, - 0x1CF37: 84, - 0x1CF38: 84, - 0x1CF39: 84, - 0x1CF3A: 84, - 0x1CF3B: 84, - 0x1CF3C: 84, - 0x1CF3D: 84, - 0x1CF3E: 84, - 0x1CF3F: 84, - 0x1CF40: 84, - 0x1CF41: 84, - 0x1CF42: 84, - 0x1CF43: 84, - 0x1CF44: 84, - 0x1CF45: 84, - 0x1CF46: 84, - 0x1D167: 84, - 0x1D168: 84, - 0x1D169: 84, - 0x1D173: 84, - 0x1D174: 84, - 0x1D175: 84, - 0x1D176: 84, - 0x1D177: 84, - 0x1D178: 84, - 0x1D179: 84, - 0x1D17A: 84, - 0x1D17B: 84, - 0x1D17C: 84, - 0x1D17D: 84, - 0x1D17E: 84, - 0x1D17F: 84, - 0x1D180: 84, - 0x1D181: 84, - 0x1D182: 84, - 0x1D185: 84, - 0x1D186: 84, - 0x1D187: 84, - 0x1D188: 84, - 0x1D189: 84, - 0x1D18A: 84, - 0x1D18B: 84, - 0x1D1AA: 84, - 0x1D1AB: 84, - 0x1D1AC: 84, - 0x1D1AD: 84, - 0x1D242: 84, - 0x1D243: 84, - 0x1D244: 84, - 0x1DA00: 84, - 0x1DA01: 84, - 0x1DA02: 84, - 0x1DA03: 84, - 0x1DA04: 84, - 0x1DA05: 84, - 0x1DA06: 84, - 0x1DA07: 84, - 0x1DA08: 84, - 0x1DA09: 84, - 0x1DA0A: 84, - 0x1DA0B: 84, - 0x1DA0C: 84, - 0x1DA0D: 84, - 0x1DA0E: 84, - 0x1DA0F: 84, - 0x1DA10: 84, - 0x1DA11: 84, - 0x1DA12: 84, - 0x1DA13: 84, - 0x1DA14: 84, - 0x1DA15: 84, - 0x1DA16: 84, - 0x1DA17: 84, - 0x1DA18: 84, - 0x1DA19: 84, - 0x1DA1A: 84, - 0x1DA1B: 84, - 0x1DA1C: 84, - 0x1DA1D: 84, - 0x1DA1E: 84, - 0x1DA1F: 84, - 0x1DA20: 84, - 0x1DA21: 84, - 0x1DA22: 84, - 0x1DA23: 84, - 0x1DA24: 84, - 0x1DA25: 84, - 0x1DA26: 84, - 0x1DA27: 84, - 0x1DA28: 84, - 0x1DA29: 84, - 0x1DA2A: 84, - 0x1DA2B: 84, - 0x1DA2C: 84, - 0x1DA2D: 84, - 0x1DA2E: 84, - 0x1DA2F: 84, - 0x1DA30: 84, - 0x1DA31: 84, - 0x1DA32: 84, - 0x1DA33: 84, - 0x1DA34: 84, - 0x1DA35: 84, - 0x1DA36: 84, - 0x1DA3B: 84, - 0x1DA3C: 84, - 0x1DA3D: 84, - 0x1DA3E: 84, - 0x1DA3F: 84, - 0x1DA40: 84, - 0x1DA41: 84, - 0x1DA42: 84, - 0x1DA43: 84, - 0x1DA44: 84, - 0x1DA45: 84, - 0x1DA46: 84, - 0x1DA47: 84, - 0x1DA48: 84, - 0x1DA49: 84, - 0x1DA4A: 84, - 0x1DA4B: 84, - 0x1DA4C: 84, - 0x1DA4D: 84, - 0x1DA4E: 84, - 0x1DA4F: 84, - 0x1DA50: 84, - 0x1DA51: 84, - 0x1DA52: 84, - 0x1DA53: 84, - 0x1DA54: 84, - 0x1DA55: 84, - 0x1DA56: 84, - 0x1DA57: 84, - 0x1DA58: 84, - 0x1DA59: 84, - 0x1DA5A: 84, - 0x1DA5B: 84, - 0x1DA5C: 84, - 0x1DA5D: 84, - 0x1DA5E: 84, - 0x1DA5F: 84, - 0x1DA60: 84, - 0x1DA61: 84, - 0x1DA62: 84, - 0x1DA63: 84, - 0x1DA64: 84, - 0x1DA65: 84, - 0x1DA66: 84, - 0x1DA67: 84, - 0x1DA68: 84, - 0x1DA69: 84, - 0x1DA6A: 84, - 0x1DA6B: 84, - 0x1DA6C: 84, - 0x1DA75: 84, - 0x1DA84: 84, - 0x1DA9B: 84, - 0x1DA9C: 84, - 0x1DA9D: 84, - 0x1DA9E: 84, - 0x1DA9F: 84, - 0x1DAA1: 84, - 0x1DAA2: 84, - 0x1DAA3: 84, - 0x1DAA4: 84, - 0x1DAA5: 84, - 0x1DAA6: 84, - 0x1DAA7: 84, - 0x1DAA8: 84, - 0x1DAA9: 84, - 0x1DAAA: 84, - 0x1DAAB: 84, - 0x1DAAC: 84, - 0x1DAAD: 84, - 0x1DAAE: 84, - 0x1DAAF: 84, - 0x1E000: 84, - 0x1E001: 84, - 0x1E002: 84, - 0x1E003: 84, - 0x1E004: 84, - 0x1E005: 84, - 0x1E006: 84, - 0x1E008: 84, - 0x1E009: 84, - 0x1E00A: 84, - 0x1E00B: 84, - 0x1E00C: 84, - 0x1E00D: 84, - 0x1E00E: 84, - 0x1E00F: 84, - 0x1E010: 84, - 0x1E011: 84, - 0x1E012: 84, - 0x1E013: 84, - 0x1E014: 84, - 0x1E015: 84, - 0x1E016: 84, - 0x1E017: 84, - 0x1E018: 84, - 0x1E01B: 84, - 0x1E01C: 84, - 0x1E01D: 84, - 0x1E01E: 84, - 0x1E01F: 84, - 0x1E020: 84, - 0x1E021: 84, - 0x1E023: 84, - 0x1E024: 84, - 0x1E026: 84, - 0x1E027: 84, - 0x1E028: 84, - 0x1E029: 84, - 0x1E02A: 84, - 0x1E08F: 84, - 0x1E130: 84, - 0x1E131: 84, - 0x1E132: 84, - 0x1E133: 84, - 0x1E134: 84, - 0x1E135: 84, - 0x1E136: 84, - 0x1E2AE: 84, - 0x1E2EC: 84, - 0x1E2ED: 84, - 0x1E2EE: 84, - 0x1E2EF: 84, - 0x1E4EC: 84, - 0x1E4ED: 84, - 0x1E4EE: 84, - 0x1E4EF: 84, - 0x1E5EE: 84, - 0x1E5EF: 84, - 0x1E8D0: 84, - 0x1E8D1: 84, - 0x1E8D2: 84, - 0x1E8D3: 84, - 0x1E8D4: 84, - 0x1E8D5: 84, - 0x1E8D6: 84, - 0x1E900: 68, - 0x1E901: 68, - 0x1E902: 68, - 0x1E903: 68, - 0x1E904: 68, - 0x1E905: 68, - 0x1E906: 68, - 0x1E907: 68, - 0x1E908: 68, - 0x1E909: 68, - 0x1E90A: 68, - 0x1E90B: 68, - 0x1E90C: 68, - 0x1E90D: 68, - 0x1E90E: 68, - 0x1E90F: 68, - 0x1E910: 68, - 0x1E911: 68, - 0x1E912: 68, - 0x1E913: 68, - 0x1E914: 68, - 0x1E915: 68, - 0x1E916: 68, - 0x1E917: 68, - 0x1E918: 68, - 0x1E919: 68, - 0x1E91A: 68, - 0x1E91B: 68, - 0x1E91C: 68, - 0x1E91D: 68, - 0x1E91E: 68, - 0x1E91F: 68, - 0x1E920: 68, - 0x1E921: 68, - 0x1E922: 68, - 0x1E923: 68, - 0x1E924: 68, - 0x1E925: 68, - 0x1E926: 68, - 0x1E927: 68, - 0x1E928: 68, - 0x1E929: 68, - 0x1E92A: 68, - 0x1E92B: 68, - 0x1E92C: 68, - 0x1E92D: 68, - 0x1E92E: 68, - 0x1E92F: 68, - 0x1E930: 68, - 0x1E931: 68, - 0x1E932: 68, - 0x1E933: 68, - 0x1E934: 68, - 0x1E935: 68, - 0x1E936: 68, - 0x1E937: 68, - 0x1E938: 68, - 0x1E939: 68, - 0x1E93A: 68, - 0x1E93B: 68, - 0x1E93C: 68, - 0x1E93D: 68, - 0x1E93E: 68, - 0x1E93F: 68, - 0x1E940: 68, - 0x1E941: 68, - 0x1E942: 68, - 0x1E943: 68, - 0x1E944: 84, - 0x1E945: 84, - 0x1E946: 84, - 0x1E947: 84, - 0x1E948: 84, - 0x1E949: 84, - 0x1E94A: 84, - 0x1E94B: 84, - 0xE0001: 84, - 0xE0020: 84, - 0xE0021: 84, - 0xE0022: 84, - 0xE0023: 84, - 0xE0024: 84, - 0xE0025: 84, - 0xE0026: 84, - 0xE0027: 84, - 0xE0028: 84, - 0xE0029: 84, - 0xE002A: 84, - 0xE002B: 84, - 0xE002C: 84, - 0xE002D: 84, - 0xE002E: 84, - 0xE002F: 84, - 0xE0030: 84, - 0xE0031: 84, - 0xE0032: 84, - 0xE0033: 84, - 0xE0034: 84, - 0xE0035: 84, - 0xE0036: 84, - 0xE0037: 84, - 0xE0038: 84, - 0xE0039: 84, - 0xE003A: 84, - 0xE003B: 84, - 0xE003C: 84, - 0xE003D: 84, - 0xE003E: 84, - 0xE003F: 84, - 0xE0040: 84, - 0xE0041: 84, - 0xE0042: 84, - 0xE0043: 84, - 0xE0044: 84, - 0xE0045: 84, - 0xE0046: 84, - 0xE0047: 84, - 0xE0048: 84, - 0xE0049: 84, - 0xE004A: 84, - 0xE004B: 84, - 0xE004C: 84, - 0xE004D: 84, - 0xE004E: 84, - 0xE004F: 84, - 0xE0050: 84, - 0xE0051: 84, - 0xE0052: 84, - 0xE0053: 84, - 0xE0054: 84, - 0xE0055: 84, - 0xE0056: 84, - 0xE0057: 84, - 0xE0058: 84, - 0xE0059: 84, - 0xE005A: 84, - 0xE005B: 84, - 0xE005C: 84, - 0xE005D: 84, - 0xE005E: 84, - 0xE005F: 84, - 0xE0060: 84, - 0xE0061: 84, - 0xE0062: 84, - 0xE0063: 84, - 0xE0064: 84, - 0xE0065: 84, - 0xE0066: 84, - 0xE0067: 84, - 0xE0068: 84, - 0xE0069: 84, - 0xE006A: 84, - 0xE006B: 84, - 0xE006C: 84, - 0xE006D: 84, - 0xE006E: 84, - 0xE006F: 84, - 0xE0070: 84, - 0xE0071: 84, - 0xE0072: 84, - 0xE0073: 84, - 0xE0074: 84, - 0xE0075: 84, - 0xE0076: 84, - 0xE0077: 84, - 0xE0078: 84, - 0xE0079: 84, - 0xE007A: 84, - 0xE007B: 84, - 0xE007C: 84, - 0xE007D: 84, - 0xE007E: 84, - 0xE007F: 84, - 0xE0100: 84, - 0xE0101: 84, - 0xE0102: 84, - 0xE0103: 84, - 0xE0104: 84, - 0xE0105: 84, - 0xE0106: 84, - 0xE0107: 84, - 0xE0108: 84, - 0xE0109: 84, - 0xE010A: 84, - 0xE010B: 84, - 0xE010C: 84, - 0xE010D: 84, - 0xE010E: 84, - 0xE010F: 84, - 0xE0110: 84, - 0xE0111: 84, - 0xE0112: 84, - 0xE0113: 84, - 0xE0114: 84, - 0xE0115: 84, - 0xE0116: 84, - 0xE0117: 84, - 0xE0118: 84, - 0xE0119: 84, - 0xE011A: 84, - 0xE011B: 84, - 0xE011C: 84, - 0xE011D: 84, - 0xE011E: 84, - 0xE011F: 84, - 0xE0120: 84, - 0xE0121: 84, - 0xE0122: 84, - 0xE0123: 84, - 0xE0124: 84, - 0xE0125: 84, - 0xE0126: 84, - 0xE0127: 84, - 0xE0128: 84, - 0xE0129: 84, - 0xE012A: 84, - 0xE012B: 84, - 0xE012C: 84, - 0xE012D: 84, - 0xE012E: 84, - 0xE012F: 84, - 0xE0130: 84, - 0xE0131: 84, - 0xE0132: 84, - 0xE0133: 84, - 0xE0134: 84, - 0xE0135: 84, - 0xE0136: 84, - 0xE0137: 84, - 0xE0138: 84, - 0xE0139: 84, - 0xE013A: 84, - 0xE013B: 84, - 0xE013C: 84, - 0xE013D: 84, - 0xE013E: 84, - 0xE013F: 84, - 0xE0140: 84, - 0xE0141: 84, - 0xE0142: 84, - 0xE0143: 84, - 0xE0144: 84, - 0xE0145: 84, - 0xE0146: 84, - 0xE0147: 84, - 0xE0148: 84, - 0xE0149: 84, - 0xE014A: 84, - 0xE014B: 84, - 0xE014C: 84, - 0xE014D: 84, - 0xE014E: 84, - 0xE014F: 84, - 0xE0150: 84, - 0xE0151: 84, - 0xE0152: 84, - 0xE0153: 84, - 0xE0154: 84, - 0xE0155: 84, - 0xE0156: 84, - 0xE0157: 84, - 0xE0158: 84, - 0xE0159: 84, - 0xE015A: 84, - 0xE015B: 84, - 0xE015C: 84, - 0xE015D: 84, - 0xE015E: 84, - 0xE015F: 84, - 0xE0160: 84, - 0xE0161: 84, - 0xE0162: 84, - 0xE0163: 84, - 0xE0164: 84, - 0xE0165: 84, - 0xE0166: 84, - 0xE0167: 84, - 0xE0168: 84, - 0xE0169: 84, - 0xE016A: 84, - 0xE016B: 84, - 0xE016C: 84, - 0xE016D: 84, - 0xE016E: 84, - 0xE016F: 84, - 0xE0170: 84, - 0xE0171: 84, - 0xE0172: 84, - 0xE0173: 84, - 0xE0174: 84, - 0xE0175: 84, - 0xE0176: 84, - 0xE0177: 84, - 0xE0178: 84, - 0xE0179: 84, - 0xE017A: 84, - 0xE017B: 84, - 0xE017C: 84, - 0xE017D: 84, - 0xE017E: 84, - 0xE017F: 84, - 0xE0180: 84, - 0xE0181: 84, - 0xE0182: 84, - 0xE0183: 84, - 0xE0184: 84, - 0xE0185: 84, - 0xE0186: 84, - 0xE0187: 84, - 0xE0188: 84, - 0xE0189: 84, - 0xE018A: 84, - 0xE018B: 84, - 0xE018C: 84, - 0xE018D: 84, - 0xE018E: 84, - 0xE018F: 84, - 0xE0190: 84, - 0xE0191: 84, - 0xE0192: 84, - 0xE0193: 84, - 0xE0194: 84, - 0xE0195: 84, - 0xE0196: 84, - 0xE0197: 84, - 0xE0198: 84, - 0xE0199: 84, - 0xE019A: 84, - 0xE019B: 84, - 0xE019C: 84, - 0xE019D: 84, - 0xE019E: 84, - 0xE019F: 84, - 0xE01A0: 84, - 0xE01A1: 84, - 0xE01A2: 84, - 0xE01A3: 84, - 0xE01A4: 84, - 0xE01A5: 84, - 0xE01A6: 84, - 0xE01A7: 84, - 0xE01A8: 84, - 0xE01A9: 84, - 0xE01AA: 84, - 0xE01AB: 84, - 0xE01AC: 84, - 0xE01AD: 84, - 0xE01AE: 84, - 0xE01AF: 84, - 0xE01B0: 84, - 0xE01B1: 84, - 0xE01B2: 84, - 0xE01B3: 84, - 0xE01B4: 84, - 0xE01B5: 84, - 0xE01B6: 84, - 0xE01B7: 84, - 0xE01B8: 84, - 0xE01B9: 84, - 0xE01BA: 84, - 0xE01BB: 84, - 0xE01BC: 84, - 0xE01BD: 84, - 0xE01BE: 84, - 0xE01BF: 84, - 0xE01C0: 84, - 0xE01C1: 84, - 0xE01C2: 84, - 0xE01C3: 84, - 0xE01C4: 84, - 0xE01C5: 84, - 0xE01C6: 84, - 0xE01C7: 84, - 0xE01C8: 84, - 0xE01C9: 84, - 0xE01CA: 84, - 0xE01CB: 84, - 0xE01CC: 84, - 0xE01CD: 84, - 0xE01CE: 84, - 0xE01CF: 84, - 0xE01D0: 84, - 0xE01D1: 84, - 0xE01D2: 84, - 0xE01D3: 84, - 0xE01D4: 84, - 0xE01D5: 84, - 0xE01D6: 84, - 0xE01D7: 84, - 0xE01D8: 84, - 0xE01D9: 84, - 0xE01DA: 84, - 0xE01DB: 84, - 0xE01DC: 84, - 0xE01DD: 84, - 0xE01DE: 84, - 0xE01DF: 84, - 0xE01E0: 84, - 0xE01E1: 84, - 0xE01E2: 84, - 0xE01E3: 84, - 0xE01E4: 84, - 0xE01E5: 84, - 0xE01E6: 84, - 0xE01E7: 84, - 0xE01E8: 84, - 0xE01E9: 84, - 0xE01EA: 84, - 0xE01EB: 84, - 0xE01EC: 84, - 0xE01ED: 84, - 0xE01EE: 84, - 0xE01EF: 84, -} -codepoint_classes = { - "PVALID": ( - 0x2D0000002E, - 0x300000003A, - 0x610000007B, - 0xDF000000F7, - 0xF800000100, - 0x10100000102, - 0x10300000104, - 0x10500000106, - 0x10700000108, - 0x1090000010A, - 0x10B0000010C, - 0x10D0000010E, - 0x10F00000110, - 0x11100000112, - 0x11300000114, - 0x11500000116, - 0x11700000118, - 0x1190000011A, - 0x11B0000011C, - 0x11D0000011E, - 0x11F00000120, - 0x12100000122, - 0x12300000124, - 0x12500000126, - 0x12700000128, - 0x1290000012A, - 0x12B0000012C, - 0x12D0000012E, - 0x12F00000130, - 0x13100000132, - 0x13500000136, - 0x13700000139, - 0x13A0000013B, - 0x13C0000013D, - 0x13E0000013F, - 0x14200000143, - 0x14400000145, - 0x14600000147, - 0x14800000149, - 0x14B0000014C, - 0x14D0000014E, - 0x14F00000150, - 0x15100000152, - 0x15300000154, - 0x15500000156, - 0x15700000158, - 0x1590000015A, - 0x15B0000015C, - 0x15D0000015E, - 0x15F00000160, - 0x16100000162, - 0x16300000164, - 0x16500000166, - 0x16700000168, - 0x1690000016A, - 0x16B0000016C, - 0x16D0000016E, - 0x16F00000170, - 0x17100000172, - 0x17300000174, - 0x17500000176, - 0x17700000178, - 0x17A0000017B, - 0x17C0000017D, - 0x17E0000017F, - 0x18000000181, - 0x18300000184, - 0x18500000186, - 0x18800000189, - 0x18C0000018E, - 0x19200000193, - 0x19500000196, - 0x1990000019C, - 0x19E0000019F, - 0x1A1000001A2, - 0x1A3000001A4, - 0x1A5000001A6, - 0x1A8000001A9, - 0x1AA000001AC, - 0x1AD000001AE, - 0x1B0000001B1, - 0x1B4000001B5, - 0x1B6000001B7, - 0x1B9000001BC, - 0x1BD000001C4, - 0x1CE000001CF, - 0x1D0000001D1, - 0x1D2000001D3, - 0x1D4000001D5, - 0x1D6000001D7, - 0x1D8000001D9, - 0x1DA000001DB, - 0x1DC000001DE, - 0x1DF000001E0, - 0x1E1000001E2, - 0x1E3000001E4, - 0x1E5000001E6, - 0x1E7000001E8, - 0x1E9000001EA, - 0x1EB000001EC, - 0x1ED000001EE, - 0x1EF000001F1, - 0x1F5000001F6, - 0x1F9000001FA, - 0x1FB000001FC, - 0x1FD000001FE, - 0x1FF00000200, - 0x20100000202, - 0x20300000204, - 0x20500000206, - 0x20700000208, - 0x2090000020A, - 0x20B0000020C, - 0x20D0000020E, - 0x20F00000210, - 0x21100000212, - 0x21300000214, - 0x21500000216, - 0x21700000218, - 0x2190000021A, - 0x21B0000021C, - 0x21D0000021E, - 0x21F00000220, - 0x22100000222, - 0x22300000224, - 0x22500000226, - 0x22700000228, - 0x2290000022A, - 0x22B0000022C, - 0x22D0000022E, - 0x22F00000230, - 0x23100000232, - 0x2330000023A, - 0x23C0000023D, - 0x23F00000241, - 0x24200000243, - 0x24700000248, - 0x2490000024A, - 0x24B0000024C, - 0x24D0000024E, - 0x24F000002B0, - 0x2B9000002C2, - 0x2C6000002D2, - 0x2EC000002ED, - 0x2EE000002EF, - 0x30000000340, - 0x34200000343, - 0x3460000034F, - 0x35000000370, - 0x37100000372, - 0x37300000374, - 0x37700000378, - 0x37B0000037E, - 0x39000000391, - 0x3AC000003CF, - 0x3D7000003D8, - 0x3D9000003DA, - 0x3DB000003DC, - 0x3DD000003DE, - 0x3DF000003E0, - 0x3E1000003E2, - 0x3E3000003E4, - 0x3E5000003E6, - 0x3E7000003E8, - 0x3E9000003EA, - 0x3EB000003EC, - 0x3ED000003EE, - 0x3EF000003F0, - 0x3F3000003F4, - 0x3F8000003F9, - 0x3FB000003FD, - 0x43000000460, - 0x46100000462, - 0x46300000464, - 0x46500000466, - 0x46700000468, - 0x4690000046A, - 0x46B0000046C, - 0x46D0000046E, - 0x46F00000470, - 0x47100000472, - 0x47300000474, - 0x47500000476, - 0x47700000478, - 0x4790000047A, - 0x47B0000047C, - 0x47D0000047E, - 0x47F00000480, - 0x48100000482, - 0x48300000488, - 0x48B0000048C, - 0x48D0000048E, - 0x48F00000490, - 0x49100000492, - 0x49300000494, - 0x49500000496, - 0x49700000498, - 0x4990000049A, - 0x49B0000049C, - 0x49D0000049E, - 0x49F000004A0, - 0x4A1000004A2, - 0x4A3000004A4, - 0x4A5000004A6, - 0x4A7000004A8, - 0x4A9000004AA, - 0x4AB000004AC, - 0x4AD000004AE, - 0x4AF000004B0, - 0x4B1000004B2, - 0x4B3000004B4, - 0x4B5000004B6, - 0x4B7000004B8, - 0x4B9000004BA, - 0x4BB000004BC, - 0x4BD000004BE, - 0x4BF000004C0, - 0x4C2000004C3, - 0x4C4000004C5, - 0x4C6000004C7, - 0x4C8000004C9, - 0x4CA000004CB, - 0x4CC000004CD, - 0x4CE000004D0, - 0x4D1000004D2, - 0x4D3000004D4, - 0x4D5000004D6, - 0x4D7000004D8, - 0x4D9000004DA, - 0x4DB000004DC, - 0x4DD000004DE, - 0x4DF000004E0, - 0x4E1000004E2, - 0x4E3000004E4, - 0x4E5000004E6, - 0x4E7000004E8, - 0x4E9000004EA, - 0x4EB000004EC, - 0x4ED000004EE, - 0x4EF000004F0, - 0x4F1000004F2, - 0x4F3000004F4, - 0x4F5000004F6, - 0x4F7000004F8, - 0x4F9000004FA, - 0x4FB000004FC, - 0x4FD000004FE, - 0x4FF00000500, - 0x50100000502, - 0x50300000504, - 0x50500000506, - 0x50700000508, - 0x5090000050A, - 0x50B0000050C, - 0x50D0000050E, - 0x50F00000510, - 0x51100000512, - 0x51300000514, - 0x51500000516, - 0x51700000518, - 0x5190000051A, - 0x51B0000051C, - 0x51D0000051E, - 0x51F00000520, - 0x52100000522, - 0x52300000524, - 0x52500000526, - 0x52700000528, - 0x5290000052A, - 0x52B0000052C, - 0x52D0000052E, - 0x52F00000530, - 0x5590000055A, - 0x56000000587, - 0x58800000589, - 0x591000005BE, - 0x5BF000005C0, - 0x5C1000005C3, - 0x5C4000005C6, - 0x5C7000005C8, - 0x5D0000005EB, - 0x5EF000005F3, - 0x6100000061B, - 0x62000000640, - 0x64100000660, - 0x66E00000675, - 0x679000006D4, - 0x6D5000006DD, - 0x6DF000006E9, - 0x6EA000006F0, - 0x6FA00000700, - 0x7100000074B, - 0x74D000007B2, - 0x7C0000007F6, - 0x7FD000007FE, - 0x8000000082E, - 0x8400000085C, - 0x8600000086B, - 0x87000000888, - 0x8890000088F, - 0x897000008E2, - 0x8E300000958, - 0x96000000964, - 0x96600000970, - 0x97100000984, - 0x9850000098D, - 0x98F00000991, - 0x993000009A9, - 0x9AA000009B1, - 0x9B2000009B3, - 0x9B6000009BA, - 0x9BC000009C5, - 0x9C7000009C9, - 0x9CB000009CF, - 0x9D7000009D8, - 0x9E0000009E4, - 0x9E6000009F2, - 0x9FC000009FD, - 0x9FE000009FF, - 0xA0100000A04, - 0xA0500000A0B, - 0xA0F00000A11, - 0xA1300000A29, - 0xA2A00000A31, - 0xA3200000A33, - 0xA3500000A36, - 0xA3800000A3A, - 0xA3C00000A3D, - 0xA3E00000A43, - 0xA4700000A49, - 0xA4B00000A4E, - 0xA5100000A52, - 0xA5C00000A5D, - 0xA6600000A76, - 0xA8100000A84, - 0xA8500000A8E, - 0xA8F00000A92, - 0xA9300000AA9, - 0xAAA00000AB1, - 0xAB200000AB4, - 0xAB500000ABA, - 0xABC00000AC6, - 0xAC700000ACA, - 0xACB00000ACE, - 0xAD000000AD1, - 0xAE000000AE4, - 0xAE600000AF0, - 0xAF900000B00, - 0xB0100000B04, - 0xB0500000B0D, - 0xB0F00000B11, - 0xB1300000B29, - 0xB2A00000B31, - 0xB3200000B34, - 0xB3500000B3A, - 0xB3C00000B45, - 0xB4700000B49, - 0xB4B00000B4E, - 0xB5500000B58, - 0xB5F00000B64, - 0xB6600000B70, - 0xB7100000B72, - 0xB8200000B84, - 0xB8500000B8B, - 0xB8E00000B91, - 0xB9200000B96, - 0xB9900000B9B, - 0xB9C00000B9D, - 0xB9E00000BA0, - 0xBA300000BA5, - 0xBA800000BAB, - 0xBAE00000BBA, - 0xBBE00000BC3, - 0xBC600000BC9, - 0xBCA00000BCE, - 0xBD000000BD1, - 0xBD700000BD8, - 0xBE600000BF0, - 0xC0000000C0D, - 0xC0E00000C11, - 0xC1200000C29, - 0xC2A00000C3A, - 0xC3C00000C45, - 0xC4600000C49, - 0xC4A00000C4E, - 0xC5500000C57, - 0xC5800000C5B, - 0xC5D00000C5E, - 0xC6000000C64, - 0xC6600000C70, - 0xC8000000C84, - 0xC8500000C8D, - 0xC8E00000C91, - 0xC9200000CA9, - 0xCAA00000CB4, - 0xCB500000CBA, - 0xCBC00000CC5, - 0xCC600000CC9, - 0xCCA00000CCE, - 0xCD500000CD7, - 0xCDD00000CDF, - 0xCE000000CE4, - 0xCE600000CF0, - 0xCF100000CF4, - 0xD0000000D0D, - 0xD0E00000D11, - 0xD1200000D45, - 0xD4600000D49, - 0xD4A00000D4F, - 0xD5400000D58, - 0xD5F00000D64, - 0xD6600000D70, - 0xD7A00000D80, - 0xD8100000D84, - 0xD8500000D97, - 0xD9A00000DB2, - 0xDB300000DBC, - 0xDBD00000DBE, - 0xDC000000DC7, - 0xDCA00000DCB, - 0xDCF00000DD5, - 0xDD600000DD7, - 0xDD800000DE0, - 0xDE600000DF0, - 0xDF200000DF4, - 0xE0100000E33, - 0xE3400000E3B, - 0xE4000000E4F, - 0xE5000000E5A, - 0xE8100000E83, - 0xE8400000E85, - 0xE8600000E8B, - 0xE8C00000EA4, - 0xEA500000EA6, - 0xEA700000EB3, - 0xEB400000EBE, - 0xEC000000EC5, - 0xEC600000EC7, - 0xEC800000ECF, - 0xED000000EDA, - 0xEDE00000EE0, - 0xF0000000F01, - 0xF0B00000F0C, - 0xF1800000F1A, - 0xF2000000F2A, - 0xF3500000F36, - 0xF3700000F38, - 0xF3900000F3A, - 0xF3E00000F43, - 0xF4400000F48, - 0xF4900000F4D, - 0xF4E00000F52, - 0xF5300000F57, - 0xF5800000F5C, - 0xF5D00000F69, - 0xF6A00000F6D, - 0xF7100000F73, - 0xF7400000F75, - 0xF7A00000F81, - 0xF8200000F85, - 0xF8600000F93, - 0xF9400000F98, - 0xF9900000F9D, - 0xF9E00000FA2, - 0xFA300000FA7, - 0xFA800000FAC, - 0xFAD00000FB9, - 0xFBA00000FBD, - 0xFC600000FC7, - 0x10000000104A, - 0x10500000109E, - 0x10D0000010FB, - 0x10FD00001100, - 0x120000001249, - 0x124A0000124E, - 0x125000001257, - 0x125800001259, - 0x125A0000125E, - 0x126000001289, - 0x128A0000128E, - 0x1290000012B1, - 0x12B2000012B6, - 0x12B8000012BF, - 0x12C0000012C1, - 0x12C2000012C6, - 0x12C8000012D7, - 0x12D800001311, - 0x131200001316, - 0x13180000135B, - 0x135D00001360, - 0x138000001390, - 0x13A0000013F6, - 0x14010000166D, - 0x166F00001680, - 0x16810000169B, - 0x16A0000016EB, - 0x16F1000016F9, - 0x170000001716, - 0x171F00001735, - 0x174000001754, - 0x17600000176D, - 0x176E00001771, - 0x177200001774, - 0x1780000017B4, - 0x17B6000017D4, - 0x17D7000017D8, - 0x17DC000017DE, - 0x17E0000017EA, - 0x18100000181A, - 0x182000001879, - 0x1880000018AB, - 0x18B0000018F6, - 0x19000000191F, - 0x19200000192C, - 0x19300000193C, - 0x19460000196E, - 0x197000001975, - 0x1980000019AC, - 0x19B0000019CA, - 0x19D0000019DA, - 0x1A0000001A1C, - 0x1A2000001A5F, - 0x1A6000001A7D, - 0x1A7F00001A8A, - 0x1A9000001A9A, - 0x1AA700001AA8, - 0x1AB000001ABE, - 0x1ABF00001ACF, - 0x1B0000001B4D, - 0x1B5000001B5A, - 0x1B6B00001B74, - 0x1B8000001BF4, - 0x1C0000001C38, - 0x1C4000001C4A, - 0x1C4D00001C7E, - 0x1C8A00001C8B, - 0x1CD000001CD3, - 0x1CD400001CFB, - 0x1D0000001D2C, - 0x1D2F00001D30, - 0x1D3B00001D3C, - 0x1D4E00001D4F, - 0x1D6B00001D78, - 0x1D7900001D9B, - 0x1DC000001E00, - 0x1E0100001E02, - 0x1E0300001E04, - 0x1E0500001E06, - 0x1E0700001E08, - 0x1E0900001E0A, - 0x1E0B00001E0C, - 0x1E0D00001E0E, - 0x1E0F00001E10, - 0x1E1100001E12, - 0x1E1300001E14, - 0x1E1500001E16, - 0x1E1700001E18, - 0x1E1900001E1A, - 0x1E1B00001E1C, - 0x1E1D00001E1E, - 0x1E1F00001E20, - 0x1E2100001E22, - 0x1E2300001E24, - 0x1E2500001E26, - 0x1E2700001E28, - 0x1E2900001E2A, - 0x1E2B00001E2C, - 0x1E2D00001E2E, - 0x1E2F00001E30, - 0x1E3100001E32, - 0x1E3300001E34, - 0x1E3500001E36, - 0x1E3700001E38, - 0x1E3900001E3A, - 0x1E3B00001E3C, - 0x1E3D00001E3E, - 0x1E3F00001E40, - 0x1E4100001E42, - 0x1E4300001E44, - 0x1E4500001E46, - 0x1E4700001E48, - 0x1E4900001E4A, - 0x1E4B00001E4C, - 0x1E4D00001E4E, - 0x1E4F00001E50, - 0x1E5100001E52, - 0x1E5300001E54, - 0x1E5500001E56, - 0x1E5700001E58, - 0x1E5900001E5A, - 0x1E5B00001E5C, - 0x1E5D00001E5E, - 0x1E5F00001E60, - 0x1E6100001E62, - 0x1E6300001E64, - 0x1E6500001E66, - 0x1E6700001E68, - 0x1E6900001E6A, - 0x1E6B00001E6C, - 0x1E6D00001E6E, - 0x1E6F00001E70, - 0x1E7100001E72, - 0x1E7300001E74, - 0x1E7500001E76, - 0x1E7700001E78, - 0x1E7900001E7A, - 0x1E7B00001E7C, - 0x1E7D00001E7E, - 0x1E7F00001E80, - 0x1E8100001E82, - 0x1E8300001E84, - 0x1E8500001E86, - 0x1E8700001E88, - 0x1E8900001E8A, - 0x1E8B00001E8C, - 0x1E8D00001E8E, - 0x1E8F00001E90, - 0x1E9100001E92, - 0x1E9300001E94, - 0x1E9500001E9A, - 0x1E9C00001E9E, - 0x1E9F00001EA0, - 0x1EA100001EA2, - 0x1EA300001EA4, - 0x1EA500001EA6, - 0x1EA700001EA8, - 0x1EA900001EAA, - 0x1EAB00001EAC, - 0x1EAD00001EAE, - 0x1EAF00001EB0, - 0x1EB100001EB2, - 0x1EB300001EB4, - 0x1EB500001EB6, - 0x1EB700001EB8, - 0x1EB900001EBA, - 0x1EBB00001EBC, - 0x1EBD00001EBE, - 0x1EBF00001EC0, - 0x1EC100001EC2, - 0x1EC300001EC4, - 0x1EC500001EC6, - 0x1EC700001EC8, - 0x1EC900001ECA, - 0x1ECB00001ECC, - 0x1ECD00001ECE, - 0x1ECF00001ED0, - 0x1ED100001ED2, - 0x1ED300001ED4, - 0x1ED500001ED6, - 0x1ED700001ED8, - 0x1ED900001EDA, - 0x1EDB00001EDC, - 0x1EDD00001EDE, - 0x1EDF00001EE0, - 0x1EE100001EE2, - 0x1EE300001EE4, - 0x1EE500001EE6, - 0x1EE700001EE8, - 0x1EE900001EEA, - 0x1EEB00001EEC, - 0x1EED00001EEE, - 0x1EEF00001EF0, - 0x1EF100001EF2, - 0x1EF300001EF4, - 0x1EF500001EF6, - 0x1EF700001EF8, - 0x1EF900001EFA, - 0x1EFB00001EFC, - 0x1EFD00001EFE, - 0x1EFF00001F08, - 0x1F1000001F16, - 0x1F2000001F28, - 0x1F3000001F38, - 0x1F4000001F46, - 0x1F5000001F58, - 0x1F6000001F68, - 0x1F7000001F71, - 0x1F7200001F73, - 0x1F7400001F75, - 0x1F7600001F77, - 0x1F7800001F79, - 0x1F7A00001F7B, - 0x1F7C00001F7D, - 0x1FB000001FB2, - 0x1FB600001FB7, - 0x1FC600001FC7, - 0x1FD000001FD3, - 0x1FD600001FD8, - 0x1FE000001FE3, - 0x1FE400001FE8, - 0x1FF600001FF7, - 0x214E0000214F, - 0x218400002185, - 0x2C3000002C60, - 0x2C6100002C62, - 0x2C6500002C67, - 0x2C6800002C69, - 0x2C6A00002C6B, - 0x2C6C00002C6D, - 0x2C7100002C72, - 0x2C7300002C75, - 0x2C7600002C7C, - 0x2C8100002C82, - 0x2C8300002C84, - 0x2C8500002C86, - 0x2C8700002C88, - 0x2C8900002C8A, - 0x2C8B00002C8C, - 0x2C8D00002C8E, - 0x2C8F00002C90, - 0x2C9100002C92, - 0x2C9300002C94, - 0x2C9500002C96, - 0x2C9700002C98, - 0x2C9900002C9A, - 0x2C9B00002C9C, - 0x2C9D00002C9E, - 0x2C9F00002CA0, - 0x2CA100002CA2, - 0x2CA300002CA4, - 0x2CA500002CA6, - 0x2CA700002CA8, - 0x2CA900002CAA, - 0x2CAB00002CAC, - 0x2CAD00002CAE, - 0x2CAF00002CB0, - 0x2CB100002CB2, - 0x2CB300002CB4, - 0x2CB500002CB6, - 0x2CB700002CB8, - 0x2CB900002CBA, - 0x2CBB00002CBC, - 0x2CBD00002CBE, - 0x2CBF00002CC0, - 0x2CC100002CC2, - 0x2CC300002CC4, - 0x2CC500002CC6, - 0x2CC700002CC8, - 0x2CC900002CCA, - 0x2CCB00002CCC, - 0x2CCD00002CCE, - 0x2CCF00002CD0, - 0x2CD100002CD2, - 0x2CD300002CD4, - 0x2CD500002CD6, - 0x2CD700002CD8, - 0x2CD900002CDA, - 0x2CDB00002CDC, - 0x2CDD00002CDE, - 0x2CDF00002CE0, - 0x2CE100002CE2, - 0x2CE300002CE5, - 0x2CEC00002CED, - 0x2CEE00002CF2, - 0x2CF300002CF4, - 0x2D0000002D26, - 0x2D2700002D28, - 0x2D2D00002D2E, - 0x2D3000002D68, - 0x2D7F00002D97, - 0x2DA000002DA7, - 0x2DA800002DAF, - 0x2DB000002DB7, - 0x2DB800002DBF, - 0x2DC000002DC7, - 0x2DC800002DCF, - 0x2DD000002DD7, - 0x2DD800002DDF, - 0x2DE000002E00, - 0x2E2F00002E30, - 0x300500003008, - 0x302A0000302E, - 0x303C0000303D, - 0x304100003097, - 0x30990000309B, - 0x309D0000309F, - 0x30A1000030FB, - 0x30FC000030FF, - 0x310500003130, - 0x31A0000031C0, - 0x31F000003200, - 0x340000004DC0, - 0x4E000000A48D, - 0xA4D00000A4FE, - 0xA5000000A60D, - 0xA6100000A62C, - 0xA6410000A642, - 0xA6430000A644, - 0xA6450000A646, - 0xA6470000A648, - 0xA6490000A64A, - 0xA64B0000A64C, - 0xA64D0000A64E, - 0xA64F0000A650, - 0xA6510000A652, - 0xA6530000A654, - 0xA6550000A656, - 0xA6570000A658, - 0xA6590000A65A, - 0xA65B0000A65C, - 0xA65D0000A65E, - 0xA65F0000A660, - 0xA6610000A662, - 0xA6630000A664, - 0xA6650000A666, - 0xA6670000A668, - 0xA6690000A66A, - 0xA66B0000A66C, - 0xA66D0000A670, - 0xA6740000A67E, - 0xA67F0000A680, - 0xA6810000A682, - 0xA6830000A684, - 0xA6850000A686, - 0xA6870000A688, - 0xA6890000A68A, - 0xA68B0000A68C, - 0xA68D0000A68E, - 0xA68F0000A690, - 0xA6910000A692, - 0xA6930000A694, - 0xA6950000A696, - 0xA6970000A698, - 0xA6990000A69A, - 0xA69B0000A69C, - 0xA69E0000A6E6, - 0xA6F00000A6F2, - 0xA7170000A720, - 0xA7230000A724, - 0xA7250000A726, - 0xA7270000A728, - 0xA7290000A72A, - 0xA72B0000A72C, - 0xA72D0000A72E, - 0xA72F0000A732, - 0xA7330000A734, - 0xA7350000A736, - 0xA7370000A738, - 0xA7390000A73A, - 0xA73B0000A73C, - 0xA73D0000A73E, - 0xA73F0000A740, - 0xA7410000A742, - 0xA7430000A744, - 0xA7450000A746, - 0xA7470000A748, - 0xA7490000A74A, - 0xA74B0000A74C, - 0xA74D0000A74E, - 0xA74F0000A750, - 0xA7510000A752, - 0xA7530000A754, - 0xA7550000A756, - 0xA7570000A758, - 0xA7590000A75A, - 0xA75B0000A75C, - 0xA75D0000A75E, - 0xA75F0000A760, - 0xA7610000A762, - 0xA7630000A764, - 0xA7650000A766, - 0xA7670000A768, - 0xA7690000A76A, - 0xA76B0000A76C, - 0xA76D0000A76E, - 0xA76F0000A770, - 0xA7710000A779, - 0xA77A0000A77B, - 0xA77C0000A77D, - 0xA77F0000A780, - 0xA7810000A782, - 0xA7830000A784, - 0xA7850000A786, - 0xA7870000A789, - 0xA78C0000A78D, - 0xA78E0000A790, - 0xA7910000A792, - 0xA7930000A796, - 0xA7970000A798, - 0xA7990000A79A, - 0xA79B0000A79C, - 0xA79D0000A79E, - 0xA79F0000A7A0, - 0xA7A10000A7A2, - 0xA7A30000A7A4, - 0xA7A50000A7A6, - 0xA7A70000A7A8, - 0xA7A90000A7AA, - 0xA7AF0000A7B0, - 0xA7B50000A7B6, - 0xA7B70000A7B8, - 0xA7B90000A7BA, - 0xA7BB0000A7BC, - 0xA7BD0000A7BE, - 0xA7BF0000A7C0, - 0xA7C10000A7C2, - 0xA7C30000A7C4, - 0xA7C80000A7C9, - 0xA7CA0000A7CB, - 0xA7CD0000A7CE, - 0xA7D10000A7D2, - 0xA7D30000A7D4, - 0xA7D50000A7D6, - 0xA7D70000A7D8, - 0xA7D90000A7DA, - 0xA7DB0000A7DC, - 0xA7F60000A7F8, - 0xA7FA0000A828, - 0xA82C0000A82D, - 0xA8400000A874, - 0xA8800000A8C6, - 0xA8D00000A8DA, - 0xA8E00000A8F8, - 0xA8FB0000A8FC, - 0xA8FD0000A92E, - 0xA9300000A954, - 0xA9800000A9C1, - 0xA9CF0000A9DA, - 0xA9E00000A9FF, - 0xAA000000AA37, - 0xAA400000AA4E, - 0xAA500000AA5A, - 0xAA600000AA77, - 0xAA7A0000AAC3, - 0xAADB0000AADE, - 0xAAE00000AAF0, - 0xAAF20000AAF7, - 0xAB010000AB07, - 0xAB090000AB0F, - 0xAB110000AB17, - 0xAB200000AB27, - 0xAB280000AB2F, - 0xAB300000AB5B, - 0xAB600000AB69, - 0xABC00000ABEB, - 0xABEC0000ABEE, - 0xABF00000ABFA, - 0xAC000000D7A4, - 0xFA0E0000FA10, - 0xFA110000FA12, - 0xFA130000FA15, - 0xFA1F0000FA20, - 0xFA210000FA22, - 0xFA230000FA25, - 0xFA270000FA2A, - 0xFB1E0000FB1F, - 0xFE200000FE30, - 0xFE730000FE74, - 0x100000001000C, - 0x1000D00010027, - 0x100280001003B, - 0x1003C0001003E, - 0x1003F0001004E, - 0x100500001005E, - 0x10080000100FB, - 0x101FD000101FE, - 0x102800001029D, - 0x102A0000102D1, - 0x102E0000102E1, - 0x1030000010320, - 0x1032D00010341, - 0x103420001034A, - 0x103500001037B, - 0x103800001039E, - 0x103A0000103C4, - 0x103C8000103D0, - 0x104280001049E, - 0x104A0000104AA, - 0x104D8000104FC, - 0x1050000010528, - 0x1053000010564, - 0x10597000105A2, - 0x105A3000105B2, - 0x105B3000105BA, - 0x105BB000105BD, - 0x105C0000105F4, - 0x1060000010737, - 0x1074000010756, - 0x1076000010768, - 0x1078000010781, - 0x1080000010806, - 0x1080800010809, - 0x1080A00010836, - 0x1083700010839, - 0x1083C0001083D, - 0x1083F00010856, - 0x1086000010877, - 0x108800001089F, - 0x108E0000108F3, - 0x108F4000108F6, - 0x1090000010916, - 0x109200001093A, - 0x10980000109B8, - 0x109BE000109C0, - 0x10A0000010A04, - 0x10A0500010A07, - 0x10A0C00010A14, - 0x10A1500010A18, - 0x10A1900010A36, - 0x10A3800010A3B, - 0x10A3F00010A40, - 0x10A6000010A7D, - 0x10A8000010A9D, - 0x10AC000010AC8, - 0x10AC900010AE7, - 0x10B0000010B36, - 0x10B4000010B56, - 0x10B6000010B73, - 0x10B8000010B92, - 0x10C0000010C49, - 0x10CC000010CF3, - 0x10D0000010D28, - 0x10D3000010D3A, - 0x10D4000010D50, - 0x10D6900010D6E, - 0x10D6F00010D86, - 0x10E8000010EAA, - 0x10EAB00010EAD, - 0x10EB000010EB2, - 0x10EC200010EC5, - 0x10EFC00010F1D, - 0x10F2700010F28, - 0x10F3000010F51, - 0x10F7000010F86, - 0x10FB000010FC5, - 0x10FE000010FF7, - 0x1100000011047, - 0x1106600011076, - 0x1107F000110BB, - 0x110C2000110C3, - 0x110D0000110E9, - 0x110F0000110FA, - 0x1110000011135, - 0x1113600011140, - 0x1114400011148, - 0x1115000011174, - 0x1117600011177, - 0x11180000111C5, - 0x111C9000111CD, - 0x111CE000111DB, - 0x111DC000111DD, - 0x1120000011212, - 0x1121300011238, - 0x1123E00011242, - 0x1128000011287, - 0x1128800011289, - 0x1128A0001128E, - 0x1128F0001129E, - 0x1129F000112A9, - 0x112B0000112EB, - 0x112F0000112FA, - 0x1130000011304, - 0x113050001130D, - 0x1130F00011311, - 0x1131300011329, - 0x1132A00011331, - 0x1133200011334, - 0x113350001133A, - 0x1133B00011345, - 0x1134700011349, - 0x1134B0001134E, - 0x1135000011351, - 0x1135700011358, - 0x1135D00011364, - 0x113660001136D, - 0x1137000011375, - 0x113800001138A, - 0x1138B0001138C, - 0x1138E0001138F, - 0x11390000113B6, - 0x113B7000113C1, - 0x113C2000113C3, - 0x113C5000113C6, - 0x113C7000113CB, - 0x113CC000113D4, - 0x113E1000113E3, - 0x114000001144B, - 0x114500001145A, - 0x1145E00011462, - 0x11480000114C6, - 0x114C7000114C8, - 0x114D0000114DA, - 0x11580000115B6, - 0x115B8000115C1, - 0x115D8000115DE, - 0x1160000011641, - 0x1164400011645, - 0x116500001165A, - 0x11680000116B9, - 0x116C0000116CA, - 0x116D0000116E4, - 0x117000001171B, - 0x1171D0001172C, - 0x117300001173A, - 0x1174000011747, - 0x118000001183B, - 0x118C0000118EA, - 0x118FF00011907, - 0x119090001190A, - 0x1190C00011914, - 0x1191500011917, - 0x1191800011936, - 0x1193700011939, - 0x1193B00011944, - 0x119500001195A, - 0x119A0000119A8, - 0x119AA000119D8, - 0x119DA000119E2, - 0x119E3000119E5, - 0x11A0000011A3F, - 0x11A4700011A48, - 0x11A5000011A9A, - 0x11A9D00011A9E, - 0x11AB000011AF9, - 0x11BC000011BE1, - 0x11BF000011BFA, - 0x11C0000011C09, - 0x11C0A00011C37, - 0x11C3800011C41, - 0x11C5000011C5A, - 0x11C7200011C90, - 0x11C9200011CA8, - 0x11CA900011CB7, - 0x11D0000011D07, - 0x11D0800011D0A, - 0x11D0B00011D37, - 0x11D3A00011D3B, - 0x11D3C00011D3E, - 0x11D3F00011D48, - 0x11D5000011D5A, - 0x11D6000011D66, - 0x11D6700011D69, - 0x11D6A00011D8F, - 0x11D9000011D92, - 0x11D9300011D99, - 0x11DA000011DAA, - 0x11EE000011EF7, - 0x11F0000011F11, - 0x11F1200011F3B, - 0x11F3E00011F43, - 0x11F5000011F5B, - 0x11FB000011FB1, - 0x120000001239A, - 0x1248000012544, - 0x12F9000012FF1, - 0x1300000013430, - 0x1344000013456, - 0x13460000143FB, - 0x1440000014647, - 0x161000001613A, - 0x1680000016A39, - 0x16A4000016A5F, - 0x16A6000016A6A, - 0x16A7000016ABF, - 0x16AC000016ACA, - 0x16AD000016AEE, - 0x16AF000016AF5, - 0x16B0000016B37, - 0x16B4000016B44, - 0x16B5000016B5A, - 0x16B6300016B78, - 0x16B7D00016B90, - 0x16D4000016D6D, - 0x16D7000016D7A, - 0x16E6000016E80, - 0x16F0000016F4B, - 0x16F4F00016F88, - 0x16F8F00016FA0, - 0x16FE000016FE2, - 0x16FE300016FE5, - 0x16FF000016FF2, - 0x17000000187F8, - 0x1880000018CD6, - 0x18CFF00018D09, - 0x1AFF00001AFF4, - 0x1AFF50001AFFC, - 0x1AFFD0001AFFF, - 0x1B0000001B123, - 0x1B1320001B133, - 0x1B1500001B153, - 0x1B1550001B156, - 0x1B1640001B168, - 0x1B1700001B2FC, - 0x1BC000001BC6B, - 0x1BC700001BC7D, - 0x1BC800001BC89, - 0x1BC900001BC9A, - 0x1BC9D0001BC9F, - 0x1CCF00001CCFA, - 0x1CF000001CF2E, - 0x1CF300001CF47, - 0x1DA000001DA37, - 0x1DA3B0001DA6D, - 0x1DA750001DA76, - 0x1DA840001DA85, - 0x1DA9B0001DAA0, - 0x1DAA10001DAB0, - 0x1DF000001DF1F, - 0x1DF250001DF2B, - 0x1E0000001E007, - 0x1E0080001E019, - 0x1E01B0001E022, - 0x1E0230001E025, - 0x1E0260001E02B, - 0x1E08F0001E090, - 0x1E1000001E12D, - 0x1E1300001E13E, - 0x1E1400001E14A, - 0x1E14E0001E14F, - 0x1E2900001E2AF, - 0x1E2C00001E2FA, - 0x1E4D00001E4FA, - 0x1E5D00001E5FB, - 0x1E7E00001E7E7, - 0x1E7E80001E7EC, - 0x1E7ED0001E7EF, - 0x1E7F00001E7FF, - 0x1E8000001E8C5, - 0x1E8D00001E8D7, - 0x1E9220001E94C, - 0x1E9500001E95A, - 0x200000002A6E0, - 0x2A7000002B73A, - 0x2B7400002B81E, - 0x2B8200002CEA2, - 0x2CEB00002EBE1, - 0x2EBF00002EE5E, - 0x300000003134B, - 0x31350000323B0, - ), - "CONTEXTJ": (0x200C0000200E,), - "CONTEXTO": ( - 0xB7000000B8, - 0x37500000376, - 0x5F3000005F5, - 0x6600000066A, - 0x6F0000006FA, - 0x30FB000030FC, - ), -} diff --git a/apps/bitwarden_event_logs/lib/idna/intranges.py b/apps/bitwarden_event_logs/lib/idna/intranges.py deleted file mode 100755 index 7bfaa8d8..00000000 --- a/apps/bitwarden_event_logs/lib/idna/intranges.py +++ /dev/null @@ -1,57 +0,0 @@ -""" -Given a list of integers, made up of (hopefully) a small number of long runs -of consecutive integers, compute a representation of the form -((start1, end1), (start2, end2) ...). Then answer the question "was x present -in the original list?" in time O(log(# runs)). -""" - -import bisect -from typing import List, Tuple - - -def intranges_from_list(list_: List[int]) -> Tuple[int, ...]: - """Represent a list of integers as a sequence of ranges: - ((start_0, end_0), (start_1, end_1), ...), such that the original - integers are exactly those x such that start_i <= x < end_i for some i. - - Ranges are encoded as single integers (start << 32 | end), not as tuples. - """ - - sorted_list = sorted(list_) - ranges = [] - last_write = -1 - for i in range(len(sorted_list)): - if i + 1 < len(sorted_list): - if sorted_list[i] == sorted_list[i + 1] - 1: - continue - current_range = sorted_list[last_write + 1 : i + 1] - ranges.append(_encode_range(current_range[0], current_range[-1] + 1)) - last_write = i - - return tuple(ranges) - - -def _encode_range(start: int, end: int) -> int: - return (start << 32) | end - - -def _decode_range(r: int) -> Tuple[int, int]: - return (r >> 32), (r & ((1 << 32) - 1)) - - -def intranges_contain(int_: int, ranges: Tuple[int, ...]) -> bool: - """Determine if `int_` falls into one of the ranges in `ranges`.""" - tuple_ = _encode_range(int_, 0) - pos = bisect.bisect_left(ranges, tuple_) - # we could be immediately ahead of a tuple (start, end) - # with start < int_ <= end - if pos > 0: - left, right = _decode_range(ranges[pos - 1]) - if left <= int_ < right: - return True - # or we could be immediately behind a tuple (int_, end) - if pos < len(ranges): - left, _ = _decode_range(ranges[pos]) - if left == int_: - return True - return False diff --git a/apps/bitwarden_event_logs/lib/idna/package_data.py b/apps/bitwarden_event_logs/lib/idna/package_data.py deleted file mode 100755 index 7272c8d9..00000000 --- a/apps/bitwarden_event_logs/lib/idna/package_data.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = "3.11" diff --git a/apps/bitwarden_event_logs/lib/idna/py.typed b/apps/bitwarden_event_logs/lib/idna/py.typed deleted file mode 100755 index e69de29b..00000000 diff --git a/apps/bitwarden_event_logs/lib/idna/uts46data.py b/apps/bitwarden_event_logs/lib/idna/uts46data.py deleted file mode 100755 index 4610b71d..00000000 --- a/apps/bitwarden_event_logs/lib/idna/uts46data.py +++ /dev/null @@ -1,8841 +0,0 @@ -# This file is automatically generated by tools/idna-data -# vim: set fileencoding=utf-8 : - -from typing import List, Tuple, Union - -"""IDNA Mapping Table from UTS46.""" - - -__version__ = "16.0.0" - - -def _seg_0() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x0, "V"), - (0x1, "V"), - (0x2, "V"), - (0x3, "V"), - (0x4, "V"), - (0x5, "V"), - (0x6, "V"), - (0x7, "V"), - (0x8, "V"), - (0x9, "V"), - (0xA, "V"), - (0xB, "V"), - (0xC, "V"), - (0xD, "V"), - (0xE, "V"), - (0xF, "V"), - (0x10, "V"), - (0x11, "V"), - (0x12, "V"), - (0x13, "V"), - (0x14, "V"), - (0x15, "V"), - (0x16, "V"), - (0x17, "V"), - (0x18, "V"), - (0x19, "V"), - (0x1A, "V"), - (0x1B, "V"), - (0x1C, "V"), - (0x1D, "V"), - (0x1E, "V"), - (0x1F, "V"), - (0x20, "V"), - (0x21, "V"), - (0x22, "V"), - (0x23, "V"), - (0x24, "V"), - (0x25, "V"), - (0x26, "V"), - (0x27, "V"), - (0x28, "V"), - (0x29, "V"), - (0x2A, "V"), - (0x2B, "V"), - (0x2C, "V"), - (0x2D, "V"), - (0x2E, "V"), - (0x2F, "V"), - (0x30, "V"), - (0x31, "V"), - (0x32, "V"), - (0x33, "V"), - (0x34, "V"), - (0x35, "V"), - (0x36, "V"), - (0x37, "V"), - (0x38, "V"), - (0x39, "V"), - (0x3A, "V"), - (0x3B, "V"), - (0x3C, "V"), - (0x3D, "V"), - (0x3E, "V"), - (0x3F, "V"), - (0x40, "V"), - (0x41, "M", "a"), - (0x42, "M", "b"), - (0x43, "M", "c"), - (0x44, "M", "d"), - (0x45, "M", "e"), - (0x46, "M", "f"), - (0x47, "M", "g"), - (0x48, "M", "h"), - (0x49, "M", "i"), - (0x4A, "M", "j"), - (0x4B, "M", "k"), - (0x4C, "M", "l"), - (0x4D, "M", "m"), - (0x4E, "M", "n"), - (0x4F, "M", "o"), - (0x50, "M", "p"), - (0x51, "M", "q"), - (0x52, "M", "r"), - (0x53, "M", "s"), - (0x54, "M", "t"), - (0x55, "M", "u"), - (0x56, "M", "v"), - (0x57, "M", "w"), - (0x58, "M", "x"), - (0x59, "M", "y"), - (0x5A, "M", "z"), - (0x5B, "V"), - (0x5C, "V"), - (0x5D, "V"), - (0x5E, "V"), - (0x5F, "V"), - (0x60, "V"), - (0x61, "V"), - (0x62, "V"), - (0x63, "V"), - ] - - -def _seg_1() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x64, "V"), - (0x65, "V"), - (0x66, "V"), - (0x67, "V"), - (0x68, "V"), - (0x69, "V"), - (0x6A, "V"), - (0x6B, "V"), - (0x6C, "V"), - (0x6D, "V"), - (0x6E, "V"), - (0x6F, "V"), - (0x70, "V"), - (0x71, "V"), - (0x72, "V"), - (0x73, "V"), - (0x74, "V"), - (0x75, "V"), - (0x76, "V"), - (0x77, "V"), - (0x78, "V"), - (0x79, "V"), - (0x7A, "V"), - (0x7B, "V"), - (0x7C, "V"), - (0x7D, "V"), - (0x7E, "V"), - (0x7F, "V"), - (0x80, "X"), - (0x81, "X"), - (0x82, "X"), - (0x83, "X"), - (0x84, "X"), - (0x85, "X"), - (0x86, "X"), - (0x87, "X"), - (0x88, "X"), - (0x89, "X"), - (0x8A, "X"), - (0x8B, "X"), - (0x8C, "X"), - (0x8D, "X"), - (0x8E, "X"), - (0x8F, "X"), - (0x90, "X"), - (0x91, "X"), - (0x92, "X"), - (0x93, "X"), - (0x94, "X"), - (0x95, "X"), - (0x96, "X"), - (0x97, "X"), - (0x98, "X"), - (0x99, "X"), - (0x9A, "X"), - (0x9B, "X"), - (0x9C, "X"), - (0x9D, "X"), - (0x9E, "X"), - (0x9F, "X"), - (0xA0, "M", " "), - (0xA1, "V"), - (0xA2, "V"), - (0xA3, "V"), - (0xA4, "V"), - (0xA5, "V"), - (0xA6, "V"), - (0xA7, "V"), - (0xA8, "M", " ̈"), - (0xA9, "V"), - (0xAA, "M", "a"), - (0xAB, "V"), - (0xAC, "V"), - (0xAD, "I"), - (0xAE, "V"), - (0xAF, "M", " ̄"), - (0xB0, "V"), - (0xB1, "V"), - (0xB2, "M", "2"), - (0xB3, "M", "3"), - (0xB4, "M", " ́"), - (0xB5, "M", "μ"), - (0xB6, "V"), - (0xB7, "V"), - (0xB8, "M", " ̧"), - (0xB9, "M", "1"), - (0xBA, "M", "o"), - (0xBB, "V"), - (0xBC, "M", "1⁄4"), - (0xBD, "M", "1⁄2"), - (0xBE, "M", "3⁄4"), - (0xBF, "V"), - (0xC0, "M", "à"), - (0xC1, "M", "á"), - (0xC2, "M", "â"), - (0xC3, "M", "ã"), - (0xC4, "M", "ä"), - (0xC5, "M", "å"), - (0xC6, "M", "æ"), - (0xC7, "M", "ç"), - ] - - -def _seg_2() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xC8, "M", "è"), - (0xC9, "M", "é"), - (0xCA, "M", "ê"), - (0xCB, "M", "ë"), - (0xCC, "M", "ì"), - (0xCD, "M", "í"), - (0xCE, "M", "î"), - (0xCF, "M", "ï"), - (0xD0, "M", "ð"), - (0xD1, "M", "ñ"), - (0xD2, "M", "ò"), - (0xD3, "M", "ó"), - (0xD4, "M", "ô"), - (0xD5, "M", "õ"), - (0xD6, "M", "ö"), - (0xD7, "V"), - (0xD8, "M", "ø"), - (0xD9, "M", "ù"), - (0xDA, "M", "ú"), - (0xDB, "M", "û"), - (0xDC, "M", "ü"), - (0xDD, "M", "ý"), - (0xDE, "M", "þ"), - (0xDF, "D", "ss"), - (0xE0, "V"), - (0xE1, "V"), - (0xE2, "V"), - (0xE3, "V"), - (0xE4, "V"), - (0xE5, "V"), - (0xE6, "V"), - (0xE7, "V"), - (0xE8, "V"), - (0xE9, "V"), - (0xEA, "V"), - (0xEB, "V"), - (0xEC, "V"), - (0xED, "V"), - (0xEE, "V"), - (0xEF, "V"), - (0xF0, "V"), - (0xF1, "V"), - (0xF2, "V"), - (0xF3, "V"), - (0xF4, "V"), - (0xF5, "V"), - (0xF6, "V"), - (0xF7, "V"), - (0xF8, "V"), - (0xF9, "V"), - (0xFA, "V"), - (0xFB, "V"), - (0xFC, "V"), - (0xFD, "V"), - (0xFE, "V"), - (0xFF, "V"), - (0x100, "M", "ā"), - (0x101, "V"), - (0x102, "M", "ă"), - (0x103, "V"), - (0x104, "M", "ą"), - (0x105, "V"), - (0x106, "M", "ć"), - (0x107, "V"), - (0x108, "M", "ĉ"), - (0x109, "V"), - (0x10A, "M", "ċ"), - (0x10B, "V"), - (0x10C, "M", "č"), - (0x10D, "V"), - (0x10E, "M", "ď"), - (0x10F, "V"), - (0x110, "M", "đ"), - (0x111, "V"), - (0x112, "M", "ē"), - (0x113, "V"), - (0x114, "M", "ĕ"), - (0x115, "V"), - (0x116, "M", "ė"), - (0x117, "V"), - (0x118, "M", "ę"), - (0x119, "V"), - (0x11A, "M", "ě"), - (0x11B, "V"), - (0x11C, "M", "ĝ"), - (0x11D, "V"), - (0x11E, "M", "ğ"), - (0x11F, "V"), - (0x120, "M", "ġ"), - (0x121, "V"), - (0x122, "M", "ģ"), - (0x123, "V"), - (0x124, "M", "ĥ"), - (0x125, "V"), - (0x126, "M", "ħ"), - (0x127, "V"), - (0x128, "M", "ĩ"), - (0x129, "V"), - (0x12A, "M", "ī"), - (0x12B, "V"), - ] - - -def _seg_3() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x12C, "M", "ĭ"), - (0x12D, "V"), - (0x12E, "M", "į"), - (0x12F, "V"), - (0x130, "M", "i̇"), - (0x131, "V"), - (0x132, "M", "ij"), - (0x134, "M", "ĵ"), - (0x135, "V"), - (0x136, "M", "ķ"), - (0x137, "V"), - (0x139, "M", "ĺ"), - (0x13A, "V"), - (0x13B, "M", "ļ"), - (0x13C, "V"), - (0x13D, "M", "ľ"), - (0x13E, "V"), - (0x13F, "M", "l·"), - (0x141, "M", "ł"), - (0x142, "V"), - (0x143, "M", "ń"), - (0x144, "V"), - (0x145, "M", "ņ"), - (0x146, "V"), - (0x147, "M", "ň"), - (0x148, "V"), - (0x149, "M", "ʼn"), - (0x14A, "M", "ŋ"), - (0x14B, "V"), - (0x14C, "M", "ō"), - (0x14D, "V"), - (0x14E, "M", "ŏ"), - (0x14F, "V"), - (0x150, "M", "ő"), - (0x151, "V"), - (0x152, "M", "œ"), - (0x153, "V"), - (0x154, "M", "ŕ"), - (0x155, "V"), - (0x156, "M", "ŗ"), - (0x157, "V"), - (0x158, "M", "ř"), - (0x159, "V"), - (0x15A, "M", "ś"), - (0x15B, "V"), - (0x15C, "M", "ŝ"), - (0x15D, "V"), - (0x15E, "M", "ş"), - (0x15F, "V"), - (0x160, "M", "š"), - (0x161, "V"), - (0x162, "M", "ţ"), - (0x163, "V"), - (0x164, "M", "ť"), - (0x165, "V"), - (0x166, "M", "ŧ"), - (0x167, "V"), - (0x168, "M", "ũ"), - (0x169, "V"), - (0x16A, "M", "ū"), - (0x16B, "V"), - (0x16C, "M", "ŭ"), - (0x16D, "V"), - (0x16E, "M", "ů"), - (0x16F, "V"), - (0x170, "M", "ű"), - (0x171, "V"), - (0x172, "M", "ų"), - (0x173, "V"), - (0x174, "M", "ŵ"), - (0x175, "V"), - (0x176, "M", "ŷ"), - (0x177, "V"), - (0x178, "M", "ÿ"), - (0x179, "M", "ź"), - (0x17A, "V"), - (0x17B, "M", "ż"), - (0x17C, "V"), - (0x17D, "M", "ž"), - (0x17E, "V"), - (0x17F, "M", "s"), - (0x180, "V"), - (0x181, "M", "ɓ"), - (0x182, "M", "ƃ"), - (0x183, "V"), - (0x184, "M", "ƅ"), - (0x185, "V"), - (0x186, "M", "ɔ"), - (0x187, "M", "ƈ"), - (0x188, "V"), - (0x189, "M", "ɖ"), - (0x18A, "M", "ɗ"), - (0x18B, "M", "ƌ"), - (0x18C, "V"), - (0x18E, "M", "ǝ"), - (0x18F, "M", "ə"), - (0x190, "M", "ɛ"), - (0x191, "M", "ƒ"), - (0x192, "V"), - (0x193, "M", "ɠ"), - ] - - -def _seg_4() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x194, "M", "ɣ"), - (0x195, "V"), - (0x196, "M", "ɩ"), - (0x197, "M", "ɨ"), - (0x198, "M", "ƙ"), - (0x199, "V"), - (0x19C, "M", "ɯ"), - (0x19D, "M", "ɲ"), - (0x19E, "V"), - (0x19F, "M", "ɵ"), - (0x1A0, "M", "ơ"), - (0x1A1, "V"), - (0x1A2, "M", "ƣ"), - (0x1A3, "V"), - (0x1A4, "M", "ƥ"), - (0x1A5, "V"), - (0x1A6, "M", "ʀ"), - (0x1A7, "M", "ƨ"), - (0x1A8, "V"), - (0x1A9, "M", "ʃ"), - (0x1AA, "V"), - (0x1AC, "M", "ƭ"), - (0x1AD, "V"), - (0x1AE, "M", "ʈ"), - (0x1AF, "M", "ư"), - (0x1B0, "V"), - (0x1B1, "M", "ʊ"), - (0x1B2, "M", "ʋ"), - (0x1B3, "M", "ƴ"), - (0x1B4, "V"), - (0x1B5, "M", "ƶ"), - (0x1B6, "V"), - (0x1B7, "M", "ʒ"), - (0x1B8, "M", "ƹ"), - (0x1B9, "V"), - (0x1BC, "M", "ƽ"), - (0x1BD, "V"), - (0x1C4, "M", "dž"), - (0x1C7, "M", "lj"), - (0x1CA, "M", "nj"), - (0x1CD, "M", "ǎ"), - (0x1CE, "V"), - (0x1CF, "M", "ǐ"), - (0x1D0, "V"), - (0x1D1, "M", "ǒ"), - (0x1D2, "V"), - (0x1D3, "M", "ǔ"), - (0x1D4, "V"), - (0x1D5, "M", "ǖ"), - (0x1D6, "V"), - (0x1D7, "M", "ǘ"), - (0x1D8, "V"), - (0x1D9, "M", "ǚ"), - (0x1DA, "V"), - (0x1DB, "M", "ǜ"), - (0x1DC, "V"), - (0x1DE, "M", "ǟ"), - (0x1DF, "V"), - (0x1E0, "M", "ǡ"), - (0x1E1, "V"), - (0x1E2, "M", "ǣ"), - (0x1E3, "V"), - (0x1E4, "M", "ǥ"), - (0x1E5, "V"), - (0x1E6, "M", "ǧ"), - (0x1E7, "V"), - (0x1E8, "M", "ǩ"), - (0x1E9, "V"), - (0x1EA, "M", "ǫ"), - (0x1EB, "V"), - (0x1EC, "M", "ǭ"), - (0x1ED, "V"), - (0x1EE, "M", "ǯ"), - (0x1EF, "V"), - (0x1F1, "M", "dz"), - (0x1F4, "M", "ǵ"), - (0x1F5, "V"), - (0x1F6, "M", "ƕ"), - (0x1F7, "M", "ƿ"), - (0x1F8, "M", "ǹ"), - (0x1F9, "V"), - (0x1FA, "M", "ǻ"), - (0x1FB, "V"), - (0x1FC, "M", "ǽ"), - (0x1FD, "V"), - (0x1FE, "M", "ǿ"), - (0x1FF, "V"), - (0x200, "M", "ȁ"), - (0x201, "V"), - (0x202, "M", "ȃ"), - (0x203, "V"), - (0x204, "M", "ȅ"), - (0x205, "V"), - (0x206, "M", "ȇ"), - (0x207, "V"), - (0x208, "M", "ȉ"), - (0x209, "V"), - (0x20A, "M", "ȋ"), - (0x20B, "V"), - (0x20C, "M", "ȍ"), - ] - - -def _seg_5() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x20D, "V"), - (0x20E, "M", "ȏ"), - (0x20F, "V"), - (0x210, "M", "ȑ"), - (0x211, "V"), - (0x212, "M", "ȓ"), - (0x213, "V"), - (0x214, "M", "ȕ"), - (0x215, "V"), - (0x216, "M", "ȗ"), - (0x217, "V"), - (0x218, "M", "ș"), - (0x219, "V"), - (0x21A, "M", "ț"), - (0x21B, "V"), - (0x21C, "M", "ȝ"), - (0x21D, "V"), - (0x21E, "M", "ȟ"), - (0x21F, "V"), - (0x220, "M", "ƞ"), - (0x221, "V"), - (0x222, "M", "ȣ"), - (0x223, "V"), - (0x224, "M", "ȥ"), - (0x225, "V"), - (0x226, "M", "ȧ"), - (0x227, "V"), - (0x228, "M", "ȩ"), - (0x229, "V"), - (0x22A, "M", "ȫ"), - (0x22B, "V"), - (0x22C, "M", "ȭ"), - (0x22D, "V"), - (0x22E, "M", "ȯ"), - (0x22F, "V"), - (0x230, "M", "ȱ"), - (0x231, "V"), - (0x232, "M", "ȳ"), - (0x233, "V"), - (0x23A, "M", "ⱥ"), - (0x23B, "M", "ȼ"), - (0x23C, "V"), - (0x23D, "M", "ƚ"), - (0x23E, "M", "ⱦ"), - (0x23F, "V"), - (0x241, "M", "ɂ"), - (0x242, "V"), - (0x243, "M", "ƀ"), - (0x244, "M", "ʉ"), - (0x245, "M", "ʌ"), - (0x246, "M", "ɇ"), - (0x247, "V"), - (0x248, "M", "ɉ"), - (0x249, "V"), - (0x24A, "M", "ɋ"), - (0x24B, "V"), - (0x24C, "M", "ɍ"), - (0x24D, "V"), - (0x24E, "M", "ɏ"), - (0x24F, "V"), - (0x2B0, "M", "h"), - (0x2B1, "M", "ɦ"), - (0x2B2, "M", "j"), - (0x2B3, "M", "r"), - (0x2B4, "M", "ɹ"), - (0x2B5, "M", "ɻ"), - (0x2B6, "M", "ʁ"), - (0x2B7, "M", "w"), - (0x2B8, "M", "y"), - (0x2B9, "V"), - (0x2D8, "M", " ̆"), - (0x2D9, "M", " ̇"), - (0x2DA, "M", " ̊"), - (0x2DB, "M", " ̨"), - (0x2DC, "M", " ̃"), - (0x2DD, "M", " ̋"), - (0x2DE, "V"), - (0x2E0, "M", "ɣ"), - (0x2E1, "M", "l"), - (0x2E2, "M", "s"), - (0x2E3, "M", "x"), - (0x2E4, "M", "ʕ"), - (0x2E5, "V"), - (0x340, "M", "̀"), - (0x341, "M", "́"), - (0x342, "V"), - (0x343, "M", "̓"), - (0x344, "M", "̈́"), - (0x345, "M", "ι"), - (0x346, "V"), - (0x34F, "I"), - (0x350, "V"), - (0x370, "M", "ͱ"), - (0x371, "V"), - (0x372, "M", "ͳ"), - (0x373, "V"), - (0x374, "M", "ʹ"), - (0x375, "V"), - (0x376, "M", "ͷ"), - (0x377, "V"), - ] - - -def _seg_6() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x378, "X"), - (0x37A, "M", " ι"), - (0x37B, "V"), - (0x37E, "M", ";"), - (0x37F, "M", "ϳ"), - (0x380, "X"), - (0x384, "M", " ́"), - (0x385, "M", " ̈́"), - (0x386, "M", "ά"), - (0x387, "M", "·"), - (0x388, "M", "έ"), - (0x389, "M", "ή"), - (0x38A, "M", "ί"), - (0x38B, "X"), - (0x38C, "M", "ό"), - (0x38D, "X"), - (0x38E, "M", "ύ"), - (0x38F, "M", "ώ"), - (0x390, "V"), - (0x391, "M", "α"), - (0x392, "M", "β"), - (0x393, "M", "γ"), - (0x394, "M", "δ"), - (0x395, "M", "ε"), - (0x396, "M", "ζ"), - (0x397, "M", "η"), - (0x398, "M", "θ"), - (0x399, "M", "ι"), - (0x39A, "M", "κ"), - (0x39B, "M", "λ"), - (0x39C, "M", "μ"), - (0x39D, "M", "ν"), - (0x39E, "M", "ξ"), - (0x39F, "M", "ο"), - (0x3A0, "M", "π"), - (0x3A1, "M", "ρ"), - (0x3A2, "X"), - (0x3A3, "M", "σ"), - (0x3A4, "M", "τ"), - (0x3A5, "M", "υ"), - (0x3A6, "M", "φ"), - (0x3A7, "M", "χ"), - (0x3A8, "M", "ψ"), - (0x3A9, "M", "ω"), - (0x3AA, "M", "ϊ"), - (0x3AB, "M", "ϋ"), - (0x3AC, "V"), - (0x3C2, "D", "σ"), - (0x3C3, "V"), - (0x3CF, "M", "ϗ"), - (0x3D0, "M", "β"), - (0x3D1, "M", "θ"), - (0x3D2, "M", "υ"), - (0x3D3, "M", "ύ"), - (0x3D4, "M", "ϋ"), - (0x3D5, "M", "φ"), - (0x3D6, "M", "π"), - (0x3D7, "V"), - (0x3D8, "M", "ϙ"), - (0x3D9, "V"), - (0x3DA, "M", "ϛ"), - (0x3DB, "V"), - (0x3DC, "M", "ϝ"), - (0x3DD, "V"), - (0x3DE, "M", "ϟ"), - (0x3DF, "V"), - (0x3E0, "M", "ϡ"), - (0x3E1, "V"), - (0x3E2, "M", "ϣ"), - (0x3E3, "V"), - (0x3E4, "M", "ϥ"), - (0x3E5, "V"), - (0x3E6, "M", "ϧ"), - (0x3E7, "V"), - (0x3E8, "M", "ϩ"), - (0x3E9, "V"), - (0x3EA, "M", "ϫ"), - (0x3EB, "V"), - (0x3EC, "M", "ϭ"), - (0x3ED, "V"), - (0x3EE, "M", "ϯ"), - (0x3EF, "V"), - (0x3F0, "M", "κ"), - (0x3F1, "M", "ρ"), - (0x3F2, "M", "σ"), - (0x3F3, "V"), - (0x3F4, "M", "θ"), - (0x3F5, "M", "ε"), - (0x3F6, "V"), - (0x3F7, "M", "ϸ"), - (0x3F8, "V"), - (0x3F9, "M", "σ"), - (0x3FA, "M", "ϻ"), - (0x3FB, "V"), - (0x3FD, "M", "ͻ"), - (0x3FE, "M", "ͼ"), - (0x3FF, "M", "ͽ"), - (0x400, "M", "ѐ"), - (0x401, "M", "ё"), - (0x402, "M", "ђ"), - ] - - -def _seg_7() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x403, "M", "ѓ"), - (0x404, "M", "є"), - (0x405, "M", "ѕ"), - (0x406, "M", "і"), - (0x407, "M", "ї"), - (0x408, "M", "ј"), - (0x409, "M", "љ"), - (0x40A, "M", "њ"), - (0x40B, "M", "ћ"), - (0x40C, "M", "ќ"), - (0x40D, "M", "ѝ"), - (0x40E, "M", "ў"), - (0x40F, "M", "џ"), - (0x410, "M", "а"), - (0x411, "M", "б"), - (0x412, "M", "в"), - (0x413, "M", "г"), - (0x414, "M", "д"), - (0x415, "M", "е"), - (0x416, "M", "ж"), - (0x417, "M", "з"), - (0x418, "M", "и"), - (0x419, "M", "й"), - (0x41A, "M", "к"), - (0x41B, "M", "л"), - (0x41C, "M", "м"), - (0x41D, "M", "н"), - (0x41E, "M", "о"), - (0x41F, "M", "п"), - (0x420, "M", "р"), - (0x421, "M", "с"), - (0x422, "M", "т"), - (0x423, "M", "у"), - (0x424, "M", "ф"), - (0x425, "M", "х"), - (0x426, "M", "ц"), - (0x427, "M", "ч"), - (0x428, "M", "ш"), - (0x429, "M", "щ"), - (0x42A, "M", "ъ"), - (0x42B, "M", "ы"), - (0x42C, "M", "ь"), - (0x42D, "M", "э"), - (0x42E, "M", "ю"), - (0x42F, "M", "я"), - (0x430, "V"), - (0x460, "M", "ѡ"), - (0x461, "V"), - (0x462, "M", "ѣ"), - (0x463, "V"), - (0x464, "M", "ѥ"), - (0x465, "V"), - (0x466, "M", "ѧ"), - (0x467, "V"), - (0x468, "M", "ѩ"), - (0x469, "V"), - (0x46A, "M", "ѫ"), - (0x46B, "V"), - (0x46C, "M", "ѭ"), - (0x46D, "V"), - (0x46E, "M", "ѯ"), - (0x46F, "V"), - (0x470, "M", "ѱ"), - (0x471, "V"), - (0x472, "M", "ѳ"), - (0x473, "V"), - (0x474, "M", "ѵ"), - (0x475, "V"), - (0x476, "M", "ѷ"), - (0x477, "V"), - (0x478, "M", "ѹ"), - (0x479, "V"), - (0x47A, "M", "ѻ"), - (0x47B, "V"), - (0x47C, "M", "ѽ"), - (0x47D, "V"), - (0x47E, "M", "ѿ"), - (0x47F, "V"), - (0x480, "M", "ҁ"), - (0x481, "V"), - (0x48A, "M", "ҋ"), - (0x48B, "V"), - (0x48C, "M", "ҍ"), - (0x48D, "V"), - (0x48E, "M", "ҏ"), - (0x48F, "V"), - (0x490, "M", "ґ"), - (0x491, "V"), - (0x492, "M", "ғ"), - (0x493, "V"), - (0x494, "M", "ҕ"), - (0x495, "V"), - (0x496, "M", "җ"), - (0x497, "V"), - (0x498, "M", "ҙ"), - (0x499, "V"), - (0x49A, "M", "қ"), - (0x49B, "V"), - (0x49C, "M", "ҝ"), - (0x49D, "V"), - ] - - -def _seg_8() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x49E, "M", "ҟ"), - (0x49F, "V"), - (0x4A0, "M", "ҡ"), - (0x4A1, "V"), - (0x4A2, "M", "ң"), - (0x4A3, "V"), - (0x4A4, "M", "ҥ"), - (0x4A5, "V"), - (0x4A6, "M", "ҧ"), - (0x4A7, "V"), - (0x4A8, "M", "ҩ"), - (0x4A9, "V"), - (0x4AA, "M", "ҫ"), - (0x4AB, "V"), - (0x4AC, "M", "ҭ"), - (0x4AD, "V"), - (0x4AE, "M", "ү"), - (0x4AF, "V"), - (0x4B0, "M", "ұ"), - (0x4B1, "V"), - (0x4B2, "M", "ҳ"), - (0x4B3, "V"), - (0x4B4, "M", "ҵ"), - (0x4B5, "V"), - (0x4B6, "M", "ҷ"), - (0x4B7, "V"), - (0x4B8, "M", "ҹ"), - (0x4B9, "V"), - (0x4BA, "M", "һ"), - (0x4BB, "V"), - (0x4BC, "M", "ҽ"), - (0x4BD, "V"), - (0x4BE, "M", "ҿ"), - (0x4BF, "V"), - (0x4C0, "M", "ӏ"), - (0x4C1, "M", "ӂ"), - (0x4C2, "V"), - (0x4C3, "M", "ӄ"), - (0x4C4, "V"), - (0x4C5, "M", "ӆ"), - (0x4C6, "V"), - (0x4C7, "M", "ӈ"), - (0x4C8, "V"), - (0x4C9, "M", "ӊ"), - (0x4CA, "V"), - (0x4CB, "M", "ӌ"), - (0x4CC, "V"), - (0x4CD, "M", "ӎ"), - (0x4CE, "V"), - (0x4D0, "M", "ӑ"), - (0x4D1, "V"), - (0x4D2, "M", "ӓ"), - (0x4D3, "V"), - (0x4D4, "M", "ӕ"), - (0x4D5, "V"), - (0x4D6, "M", "ӗ"), - (0x4D7, "V"), - (0x4D8, "M", "ә"), - (0x4D9, "V"), - (0x4DA, "M", "ӛ"), - (0x4DB, "V"), - (0x4DC, "M", "ӝ"), - (0x4DD, "V"), - (0x4DE, "M", "ӟ"), - (0x4DF, "V"), - (0x4E0, "M", "ӡ"), - (0x4E1, "V"), - (0x4E2, "M", "ӣ"), - (0x4E3, "V"), - (0x4E4, "M", "ӥ"), - (0x4E5, "V"), - (0x4E6, "M", "ӧ"), - (0x4E7, "V"), - (0x4E8, "M", "ө"), - (0x4E9, "V"), - (0x4EA, "M", "ӫ"), - (0x4EB, "V"), - (0x4EC, "M", "ӭ"), - (0x4ED, "V"), - (0x4EE, "M", "ӯ"), - (0x4EF, "V"), - (0x4F0, "M", "ӱ"), - (0x4F1, "V"), - (0x4F2, "M", "ӳ"), - (0x4F3, "V"), - (0x4F4, "M", "ӵ"), - (0x4F5, "V"), - (0x4F6, "M", "ӷ"), - (0x4F7, "V"), - (0x4F8, "M", "ӹ"), - (0x4F9, "V"), - (0x4FA, "M", "ӻ"), - (0x4FB, "V"), - (0x4FC, "M", "ӽ"), - (0x4FD, "V"), - (0x4FE, "M", "ӿ"), - (0x4FF, "V"), - (0x500, "M", "ԁ"), - (0x501, "V"), - (0x502, "M", "ԃ"), - ] - - -def _seg_9() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x503, "V"), - (0x504, "M", "ԅ"), - (0x505, "V"), - (0x506, "M", "ԇ"), - (0x507, "V"), - (0x508, "M", "ԉ"), - (0x509, "V"), - (0x50A, "M", "ԋ"), - (0x50B, "V"), - (0x50C, "M", "ԍ"), - (0x50D, "V"), - (0x50E, "M", "ԏ"), - (0x50F, "V"), - (0x510, "M", "ԑ"), - (0x511, "V"), - (0x512, "M", "ԓ"), - (0x513, "V"), - (0x514, "M", "ԕ"), - (0x515, "V"), - (0x516, "M", "ԗ"), - (0x517, "V"), - (0x518, "M", "ԙ"), - (0x519, "V"), - (0x51A, "M", "ԛ"), - (0x51B, "V"), - (0x51C, "M", "ԝ"), - (0x51D, "V"), - (0x51E, "M", "ԟ"), - (0x51F, "V"), - (0x520, "M", "ԡ"), - (0x521, "V"), - (0x522, "M", "ԣ"), - (0x523, "V"), - (0x524, "M", "ԥ"), - (0x525, "V"), - (0x526, "M", "ԧ"), - (0x527, "V"), - (0x528, "M", "ԩ"), - (0x529, "V"), - (0x52A, "M", "ԫ"), - (0x52B, "V"), - (0x52C, "M", "ԭ"), - (0x52D, "V"), - (0x52E, "M", "ԯ"), - (0x52F, "V"), - (0x530, "X"), - (0x531, "M", "ա"), - (0x532, "M", "բ"), - (0x533, "M", "գ"), - (0x534, "M", "դ"), - (0x535, "M", "ե"), - (0x536, "M", "զ"), - (0x537, "M", "է"), - (0x538, "M", "ը"), - (0x539, "M", "թ"), - (0x53A, "M", "ժ"), - (0x53B, "M", "ի"), - (0x53C, "M", "լ"), - (0x53D, "M", "խ"), - (0x53E, "M", "ծ"), - (0x53F, "M", "կ"), - (0x540, "M", "հ"), - (0x541, "M", "ձ"), - (0x542, "M", "ղ"), - (0x543, "M", "ճ"), - (0x544, "M", "մ"), - (0x545, "M", "յ"), - (0x546, "M", "ն"), - (0x547, "M", "շ"), - (0x548, "M", "ո"), - (0x549, "M", "չ"), - (0x54A, "M", "պ"), - (0x54B, "M", "ջ"), - (0x54C, "M", "ռ"), - (0x54D, "M", "ս"), - (0x54E, "M", "վ"), - (0x54F, "M", "տ"), - (0x550, "M", "ր"), - (0x551, "M", "ց"), - (0x552, "M", "ւ"), - (0x553, "M", "փ"), - (0x554, "M", "ք"), - (0x555, "M", "օ"), - (0x556, "M", "ֆ"), - (0x557, "X"), - (0x559, "V"), - (0x587, "M", "եւ"), - (0x588, "V"), - (0x58B, "X"), - (0x58D, "V"), - (0x590, "X"), - (0x591, "V"), - (0x5C8, "X"), - (0x5D0, "V"), - (0x5EB, "X"), - (0x5EF, "V"), - (0x5F5, "X"), - (0x606, "V"), - (0x61C, "X"), - (0x61D, "V"), - ] - - -def _seg_10() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x675, "M", "اٴ"), - (0x676, "M", "وٴ"), - (0x677, "M", "ۇٴ"), - (0x678, "M", "يٴ"), - (0x679, "V"), - (0x6DD, "X"), - (0x6DE, "V"), - (0x70E, "X"), - (0x710, "V"), - (0x74B, "X"), - (0x74D, "V"), - (0x7B2, "X"), - (0x7C0, "V"), - (0x7FB, "X"), - (0x7FD, "V"), - (0x82E, "X"), - (0x830, "V"), - (0x83F, "X"), - (0x840, "V"), - (0x85C, "X"), - (0x85E, "V"), - (0x85F, "X"), - (0x860, "V"), - (0x86B, "X"), - (0x870, "V"), - (0x88F, "X"), - (0x897, "V"), - (0x8E2, "X"), - (0x8E3, "V"), - (0x958, "M", "क़"), - (0x959, "M", "ख़"), - (0x95A, "M", "ग़"), - (0x95B, "M", "ज़"), - (0x95C, "M", "ड़"), - (0x95D, "M", "ढ़"), - (0x95E, "M", "फ़"), - (0x95F, "M", "य़"), - (0x960, "V"), - (0x984, "X"), - (0x985, "V"), - (0x98D, "X"), - (0x98F, "V"), - (0x991, "X"), - (0x993, "V"), - (0x9A9, "X"), - (0x9AA, "V"), - (0x9B1, "X"), - (0x9B2, "V"), - (0x9B3, "X"), - (0x9B6, "V"), - (0x9BA, "X"), - (0x9BC, "V"), - (0x9C5, "X"), - (0x9C7, "V"), - (0x9C9, "X"), - (0x9CB, "V"), - (0x9CF, "X"), - (0x9D7, "V"), - (0x9D8, "X"), - (0x9DC, "M", "ড়"), - (0x9DD, "M", "ঢ়"), - (0x9DE, "X"), - (0x9DF, "M", "য়"), - (0x9E0, "V"), - (0x9E4, "X"), - (0x9E6, "V"), - (0x9FF, "X"), - (0xA01, "V"), - (0xA04, "X"), - (0xA05, "V"), - (0xA0B, "X"), - (0xA0F, "V"), - (0xA11, "X"), - (0xA13, "V"), - (0xA29, "X"), - (0xA2A, "V"), - (0xA31, "X"), - (0xA32, "V"), - (0xA33, "M", "ਲ਼"), - (0xA34, "X"), - (0xA35, "V"), - (0xA36, "M", "ਸ਼"), - (0xA37, "X"), - (0xA38, "V"), - (0xA3A, "X"), - (0xA3C, "V"), - (0xA3D, "X"), - (0xA3E, "V"), - (0xA43, "X"), - (0xA47, "V"), - (0xA49, "X"), - (0xA4B, "V"), - (0xA4E, "X"), - (0xA51, "V"), - (0xA52, "X"), - (0xA59, "M", "ਖ਼"), - (0xA5A, "M", "ਗ਼"), - (0xA5B, "M", "ਜ਼"), - (0xA5C, "V"), - (0xA5D, "X"), - ] - - -def _seg_11() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xA5E, "M", "ਫ਼"), - (0xA5F, "X"), - (0xA66, "V"), - (0xA77, "X"), - (0xA81, "V"), - (0xA84, "X"), - (0xA85, "V"), - (0xA8E, "X"), - (0xA8F, "V"), - (0xA92, "X"), - (0xA93, "V"), - (0xAA9, "X"), - (0xAAA, "V"), - (0xAB1, "X"), - (0xAB2, "V"), - (0xAB4, "X"), - (0xAB5, "V"), - (0xABA, "X"), - (0xABC, "V"), - (0xAC6, "X"), - (0xAC7, "V"), - (0xACA, "X"), - (0xACB, "V"), - (0xACE, "X"), - (0xAD0, "V"), - (0xAD1, "X"), - (0xAE0, "V"), - (0xAE4, "X"), - (0xAE6, "V"), - (0xAF2, "X"), - (0xAF9, "V"), - (0xB00, "X"), - (0xB01, "V"), - (0xB04, "X"), - (0xB05, "V"), - (0xB0D, "X"), - (0xB0F, "V"), - (0xB11, "X"), - (0xB13, "V"), - (0xB29, "X"), - (0xB2A, "V"), - (0xB31, "X"), - (0xB32, "V"), - (0xB34, "X"), - (0xB35, "V"), - (0xB3A, "X"), - (0xB3C, "V"), - (0xB45, "X"), - (0xB47, "V"), - (0xB49, "X"), - (0xB4B, "V"), - (0xB4E, "X"), - (0xB55, "V"), - (0xB58, "X"), - (0xB5C, "M", "ଡ଼"), - (0xB5D, "M", "ଢ଼"), - (0xB5E, "X"), - (0xB5F, "V"), - (0xB64, "X"), - (0xB66, "V"), - (0xB78, "X"), - (0xB82, "V"), - (0xB84, "X"), - (0xB85, "V"), - (0xB8B, "X"), - (0xB8E, "V"), - (0xB91, "X"), - (0xB92, "V"), - (0xB96, "X"), - (0xB99, "V"), - (0xB9B, "X"), - (0xB9C, "V"), - (0xB9D, "X"), - (0xB9E, "V"), - (0xBA0, "X"), - (0xBA3, "V"), - (0xBA5, "X"), - (0xBA8, "V"), - (0xBAB, "X"), - (0xBAE, "V"), - (0xBBA, "X"), - (0xBBE, "V"), - (0xBC3, "X"), - (0xBC6, "V"), - (0xBC9, "X"), - (0xBCA, "V"), - (0xBCE, "X"), - (0xBD0, "V"), - (0xBD1, "X"), - (0xBD7, "V"), - (0xBD8, "X"), - (0xBE6, "V"), - (0xBFB, "X"), - (0xC00, "V"), - (0xC0D, "X"), - (0xC0E, "V"), - (0xC11, "X"), - (0xC12, "V"), - (0xC29, "X"), - (0xC2A, "V"), - ] - - -def _seg_12() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xC3A, "X"), - (0xC3C, "V"), - (0xC45, "X"), - (0xC46, "V"), - (0xC49, "X"), - (0xC4A, "V"), - (0xC4E, "X"), - (0xC55, "V"), - (0xC57, "X"), - (0xC58, "V"), - (0xC5B, "X"), - (0xC5D, "V"), - (0xC5E, "X"), - (0xC60, "V"), - (0xC64, "X"), - (0xC66, "V"), - (0xC70, "X"), - (0xC77, "V"), - (0xC8D, "X"), - (0xC8E, "V"), - (0xC91, "X"), - (0xC92, "V"), - (0xCA9, "X"), - (0xCAA, "V"), - (0xCB4, "X"), - (0xCB5, "V"), - (0xCBA, "X"), - (0xCBC, "V"), - (0xCC5, "X"), - (0xCC6, "V"), - (0xCC9, "X"), - (0xCCA, "V"), - (0xCCE, "X"), - (0xCD5, "V"), - (0xCD7, "X"), - (0xCDD, "V"), - (0xCDF, "X"), - (0xCE0, "V"), - (0xCE4, "X"), - (0xCE6, "V"), - (0xCF0, "X"), - (0xCF1, "V"), - (0xCF4, "X"), - (0xD00, "V"), - (0xD0D, "X"), - (0xD0E, "V"), - (0xD11, "X"), - (0xD12, "V"), - (0xD45, "X"), - (0xD46, "V"), - (0xD49, "X"), - (0xD4A, "V"), - (0xD50, "X"), - (0xD54, "V"), - (0xD64, "X"), - (0xD66, "V"), - (0xD80, "X"), - (0xD81, "V"), - (0xD84, "X"), - (0xD85, "V"), - (0xD97, "X"), - (0xD9A, "V"), - (0xDB2, "X"), - (0xDB3, "V"), - (0xDBC, "X"), - (0xDBD, "V"), - (0xDBE, "X"), - (0xDC0, "V"), - (0xDC7, "X"), - (0xDCA, "V"), - (0xDCB, "X"), - (0xDCF, "V"), - (0xDD5, "X"), - (0xDD6, "V"), - (0xDD7, "X"), - (0xDD8, "V"), - (0xDE0, "X"), - (0xDE6, "V"), - (0xDF0, "X"), - (0xDF2, "V"), - (0xDF5, "X"), - (0xE01, "V"), - (0xE33, "M", "ํา"), - (0xE34, "V"), - (0xE3B, "X"), - (0xE3F, "V"), - (0xE5C, "X"), - (0xE81, "V"), - (0xE83, "X"), - (0xE84, "V"), - (0xE85, "X"), - (0xE86, "V"), - (0xE8B, "X"), - (0xE8C, "V"), - (0xEA4, "X"), - (0xEA5, "V"), - (0xEA6, "X"), - (0xEA7, "V"), - (0xEB3, "M", "ໍາ"), - (0xEB4, "V"), - ] - - -def _seg_13() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xEBE, "X"), - (0xEC0, "V"), - (0xEC5, "X"), - (0xEC6, "V"), - (0xEC7, "X"), - (0xEC8, "V"), - (0xECF, "X"), - (0xED0, "V"), - (0xEDA, "X"), - (0xEDC, "M", "ຫນ"), - (0xEDD, "M", "ຫມ"), - (0xEDE, "V"), - (0xEE0, "X"), - (0xF00, "V"), - (0xF0C, "M", "་"), - (0xF0D, "V"), - (0xF43, "M", "གྷ"), - (0xF44, "V"), - (0xF48, "X"), - (0xF49, "V"), - (0xF4D, "M", "ཌྷ"), - (0xF4E, "V"), - (0xF52, "M", "དྷ"), - (0xF53, "V"), - (0xF57, "M", "བྷ"), - (0xF58, "V"), - (0xF5C, "M", "ཛྷ"), - (0xF5D, "V"), - (0xF69, "M", "ཀྵ"), - (0xF6A, "V"), - (0xF6D, "X"), - (0xF71, "V"), - (0xF73, "M", "ཱི"), - (0xF74, "V"), - (0xF75, "M", "ཱུ"), - (0xF76, "M", "ྲྀ"), - (0xF77, "M", "ྲཱྀ"), - (0xF78, "M", "ླྀ"), - (0xF79, "M", "ླཱྀ"), - (0xF7A, "V"), - (0xF81, "M", "ཱྀ"), - (0xF82, "V"), - (0xF93, "M", "ྒྷ"), - (0xF94, "V"), - (0xF98, "X"), - (0xF99, "V"), - (0xF9D, "M", "ྜྷ"), - (0xF9E, "V"), - (0xFA2, "M", "ྡྷ"), - (0xFA3, "V"), - (0xFA7, "M", "ྦྷ"), - (0xFA8, "V"), - (0xFAC, "M", "ྫྷ"), - (0xFAD, "V"), - (0xFB9, "M", "ྐྵ"), - (0xFBA, "V"), - (0xFBD, "X"), - (0xFBE, "V"), - (0xFCD, "X"), - (0xFCE, "V"), - (0xFDB, "X"), - (0x1000, "V"), - (0x10A0, "M", "ⴀ"), - (0x10A1, "M", "ⴁ"), - (0x10A2, "M", "ⴂ"), - (0x10A3, "M", "ⴃ"), - (0x10A4, "M", "ⴄ"), - (0x10A5, "M", "ⴅ"), - (0x10A6, "M", "ⴆ"), - (0x10A7, "M", "ⴇ"), - (0x10A8, "M", "ⴈ"), - (0x10A9, "M", "ⴉ"), - (0x10AA, "M", "ⴊ"), - (0x10AB, "M", "ⴋ"), - (0x10AC, "M", "ⴌ"), - (0x10AD, "M", "ⴍ"), - (0x10AE, "M", "ⴎ"), - (0x10AF, "M", "ⴏ"), - (0x10B0, "M", "ⴐ"), - (0x10B1, "M", "ⴑ"), - (0x10B2, "M", "ⴒ"), - (0x10B3, "M", "ⴓ"), - (0x10B4, "M", "ⴔ"), - (0x10B5, "M", "ⴕ"), - (0x10B6, "M", "ⴖ"), - (0x10B7, "M", "ⴗ"), - (0x10B8, "M", "ⴘ"), - (0x10B9, "M", "ⴙ"), - (0x10BA, "M", "ⴚ"), - (0x10BB, "M", "ⴛ"), - (0x10BC, "M", "ⴜ"), - (0x10BD, "M", "ⴝ"), - (0x10BE, "M", "ⴞ"), - (0x10BF, "M", "ⴟ"), - (0x10C0, "M", "ⴠ"), - (0x10C1, "M", "ⴡ"), - (0x10C2, "M", "ⴢ"), - (0x10C3, "M", "ⴣ"), - (0x10C4, "M", "ⴤ"), - (0x10C5, "M", "ⴥ"), - ] - - -def _seg_14() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x10C6, "X"), - (0x10C7, "M", "ⴧ"), - (0x10C8, "X"), - (0x10CD, "M", "ⴭ"), - (0x10CE, "X"), - (0x10D0, "V"), - (0x10FC, "M", "ნ"), - (0x10FD, "V"), - (0x115F, "I"), - (0x1161, "V"), - (0x1249, "X"), - (0x124A, "V"), - (0x124E, "X"), - (0x1250, "V"), - (0x1257, "X"), - (0x1258, "V"), - (0x1259, "X"), - (0x125A, "V"), - (0x125E, "X"), - (0x1260, "V"), - (0x1289, "X"), - (0x128A, "V"), - (0x128E, "X"), - (0x1290, "V"), - (0x12B1, "X"), - (0x12B2, "V"), - (0x12B6, "X"), - (0x12B8, "V"), - (0x12BF, "X"), - (0x12C0, "V"), - (0x12C1, "X"), - (0x12C2, "V"), - (0x12C6, "X"), - (0x12C8, "V"), - (0x12D7, "X"), - (0x12D8, "V"), - (0x1311, "X"), - (0x1312, "V"), - (0x1316, "X"), - (0x1318, "V"), - (0x135B, "X"), - (0x135D, "V"), - (0x137D, "X"), - (0x1380, "V"), - (0x139A, "X"), - (0x13A0, "V"), - (0x13F6, "X"), - (0x13F8, "M", "Ᏸ"), - (0x13F9, "M", "Ᏹ"), - (0x13FA, "M", "Ᏺ"), - (0x13FB, "M", "Ᏻ"), - (0x13FC, "M", "Ᏼ"), - (0x13FD, "M", "Ᏽ"), - (0x13FE, "X"), - (0x1400, "V"), - (0x1680, "X"), - (0x1681, "V"), - (0x169D, "X"), - (0x16A0, "V"), - (0x16F9, "X"), - (0x1700, "V"), - (0x1716, "X"), - (0x171F, "V"), - (0x1737, "X"), - (0x1740, "V"), - (0x1754, "X"), - (0x1760, "V"), - (0x176D, "X"), - (0x176E, "V"), - (0x1771, "X"), - (0x1772, "V"), - (0x1774, "X"), - (0x1780, "V"), - (0x17B4, "I"), - (0x17B6, "V"), - (0x17DE, "X"), - (0x17E0, "V"), - (0x17EA, "X"), - (0x17F0, "V"), - (0x17FA, "X"), - (0x1800, "V"), - (0x180B, "I"), - (0x1810, "V"), - (0x181A, "X"), - (0x1820, "V"), - (0x1879, "X"), - (0x1880, "V"), - (0x18AB, "X"), - (0x18B0, "V"), - (0x18F6, "X"), - (0x1900, "V"), - (0x191F, "X"), - (0x1920, "V"), - (0x192C, "X"), - (0x1930, "V"), - (0x193C, "X"), - (0x1940, "V"), - (0x1941, "X"), - (0x1944, "V"), - (0x196E, "X"), - ] - - -def _seg_15() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1970, "V"), - (0x1975, "X"), - (0x1980, "V"), - (0x19AC, "X"), - (0x19B0, "V"), - (0x19CA, "X"), - (0x19D0, "V"), - (0x19DB, "X"), - (0x19DE, "V"), - (0x1A1C, "X"), - (0x1A1E, "V"), - (0x1A5F, "X"), - (0x1A60, "V"), - (0x1A7D, "X"), - (0x1A7F, "V"), - (0x1A8A, "X"), - (0x1A90, "V"), - (0x1A9A, "X"), - (0x1AA0, "V"), - (0x1AAE, "X"), - (0x1AB0, "V"), - (0x1ACF, "X"), - (0x1B00, "V"), - (0x1B4D, "X"), - (0x1B4E, "V"), - (0x1BF4, "X"), - (0x1BFC, "V"), - (0x1C38, "X"), - (0x1C3B, "V"), - (0x1C4A, "X"), - (0x1C4D, "V"), - (0x1C80, "M", "в"), - (0x1C81, "M", "д"), - (0x1C82, "M", "о"), - (0x1C83, "M", "с"), - (0x1C84, "M", "т"), - (0x1C86, "M", "ъ"), - (0x1C87, "M", "ѣ"), - (0x1C88, "M", "ꙋ"), - (0x1C89, "M", "ᲊ"), - (0x1C8A, "V"), - (0x1C8B, "X"), - (0x1C90, "M", "ა"), - (0x1C91, "M", "ბ"), - (0x1C92, "M", "გ"), - (0x1C93, "M", "დ"), - (0x1C94, "M", "ე"), - (0x1C95, "M", "ვ"), - (0x1C96, "M", "ზ"), - (0x1C97, "M", "თ"), - (0x1C98, "M", "ი"), - (0x1C99, "M", "კ"), - (0x1C9A, "M", "ლ"), - (0x1C9B, "M", "მ"), - (0x1C9C, "M", "ნ"), - (0x1C9D, "M", "ო"), - (0x1C9E, "M", "პ"), - (0x1C9F, "M", "ჟ"), - (0x1CA0, "M", "რ"), - (0x1CA1, "M", "ს"), - (0x1CA2, "M", "ტ"), - (0x1CA3, "M", "უ"), - (0x1CA4, "M", "ფ"), - (0x1CA5, "M", "ქ"), - (0x1CA6, "M", "ღ"), - (0x1CA7, "M", "ყ"), - (0x1CA8, "M", "შ"), - (0x1CA9, "M", "ჩ"), - (0x1CAA, "M", "ც"), - (0x1CAB, "M", "ძ"), - (0x1CAC, "M", "წ"), - (0x1CAD, "M", "ჭ"), - (0x1CAE, "M", "ხ"), - (0x1CAF, "M", "ჯ"), - (0x1CB0, "M", "ჰ"), - (0x1CB1, "M", "ჱ"), - (0x1CB2, "M", "ჲ"), - (0x1CB3, "M", "ჳ"), - (0x1CB4, "M", "ჴ"), - (0x1CB5, "M", "ჵ"), - (0x1CB6, "M", "ჶ"), - (0x1CB7, "M", "ჷ"), - (0x1CB8, "M", "ჸ"), - (0x1CB9, "M", "ჹ"), - (0x1CBA, "M", "ჺ"), - (0x1CBB, "X"), - (0x1CBD, "M", "ჽ"), - (0x1CBE, "M", "ჾ"), - (0x1CBF, "M", "ჿ"), - (0x1CC0, "V"), - (0x1CC8, "X"), - (0x1CD0, "V"), - (0x1CFB, "X"), - (0x1D00, "V"), - (0x1D2C, "M", "a"), - (0x1D2D, "M", "æ"), - (0x1D2E, "M", "b"), - (0x1D2F, "V"), - (0x1D30, "M", "d"), - (0x1D31, "M", "e"), - ] - - -def _seg_16() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D32, "M", "ǝ"), - (0x1D33, "M", "g"), - (0x1D34, "M", "h"), - (0x1D35, "M", "i"), - (0x1D36, "M", "j"), - (0x1D37, "M", "k"), - (0x1D38, "M", "l"), - (0x1D39, "M", "m"), - (0x1D3A, "M", "n"), - (0x1D3B, "V"), - (0x1D3C, "M", "o"), - (0x1D3D, "M", "ȣ"), - (0x1D3E, "M", "p"), - (0x1D3F, "M", "r"), - (0x1D40, "M", "t"), - (0x1D41, "M", "u"), - (0x1D42, "M", "w"), - (0x1D43, "M", "a"), - (0x1D44, "M", "ɐ"), - (0x1D45, "M", "ɑ"), - (0x1D46, "M", "ᴂ"), - (0x1D47, "M", "b"), - (0x1D48, "M", "d"), - (0x1D49, "M", "e"), - (0x1D4A, "M", "ə"), - (0x1D4B, "M", "ɛ"), - (0x1D4C, "M", "ɜ"), - (0x1D4D, "M", "g"), - (0x1D4E, "V"), - (0x1D4F, "M", "k"), - (0x1D50, "M", "m"), - (0x1D51, "M", "ŋ"), - (0x1D52, "M", "o"), - (0x1D53, "M", "ɔ"), - (0x1D54, "M", "ᴖ"), - (0x1D55, "M", "ᴗ"), - (0x1D56, "M", "p"), - (0x1D57, "M", "t"), - (0x1D58, "M", "u"), - (0x1D59, "M", "ᴝ"), - (0x1D5A, "M", "ɯ"), - (0x1D5B, "M", "v"), - (0x1D5C, "M", "ᴥ"), - (0x1D5D, "M", "β"), - (0x1D5E, "M", "γ"), - (0x1D5F, "M", "δ"), - (0x1D60, "M", "φ"), - (0x1D61, "M", "χ"), - (0x1D62, "M", "i"), - (0x1D63, "M", "r"), - (0x1D64, "M", "u"), - (0x1D65, "M", "v"), - (0x1D66, "M", "β"), - (0x1D67, "M", "γ"), - (0x1D68, "M", "ρ"), - (0x1D69, "M", "φ"), - (0x1D6A, "M", "χ"), - (0x1D6B, "V"), - (0x1D78, "M", "н"), - (0x1D79, "V"), - (0x1D9B, "M", "ɒ"), - (0x1D9C, "M", "c"), - (0x1D9D, "M", "ɕ"), - (0x1D9E, "M", "ð"), - (0x1D9F, "M", "ɜ"), - (0x1DA0, "M", "f"), - (0x1DA1, "M", "ɟ"), - (0x1DA2, "M", "ɡ"), - (0x1DA3, "M", "ɥ"), - (0x1DA4, "M", "ɨ"), - (0x1DA5, "M", "ɩ"), - (0x1DA6, "M", "ɪ"), - (0x1DA7, "M", "ᵻ"), - (0x1DA8, "M", "ʝ"), - (0x1DA9, "M", "ɭ"), - (0x1DAA, "M", "ᶅ"), - (0x1DAB, "M", "ʟ"), - (0x1DAC, "M", "ɱ"), - (0x1DAD, "M", "ɰ"), - (0x1DAE, "M", "ɲ"), - (0x1DAF, "M", "ɳ"), - (0x1DB0, "M", "ɴ"), - (0x1DB1, "M", "ɵ"), - (0x1DB2, "M", "ɸ"), - (0x1DB3, "M", "ʂ"), - (0x1DB4, "M", "ʃ"), - (0x1DB5, "M", "ƫ"), - (0x1DB6, "M", "ʉ"), - (0x1DB7, "M", "ʊ"), - (0x1DB8, "M", "ᴜ"), - (0x1DB9, "M", "ʋ"), - (0x1DBA, "M", "ʌ"), - (0x1DBB, "M", "z"), - (0x1DBC, "M", "ʐ"), - (0x1DBD, "M", "ʑ"), - (0x1DBE, "M", "ʒ"), - (0x1DBF, "M", "θ"), - (0x1DC0, "V"), - (0x1E00, "M", "ḁ"), - (0x1E01, "V"), - ] - - -def _seg_17() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1E02, "M", "ḃ"), - (0x1E03, "V"), - (0x1E04, "M", "ḅ"), - (0x1E05, "V"), - (0x1E06, "M", "ḇ"), - (0x1E07, "V"), - (0x1E08, "M", "ḉ"), - (0x1E09, "V"), - (0x1E0A, "M", "ḋ"), - (0x1E0B, "V"), - (0x1E0C, "M", "ḍ"), - (0x1E0D, "V"), - (0x1E0E, "M", "ḏ"), - (0x1E0F, "V"), - (0x1E10, "M", "ḑ"), - (0x1E11, "V"), - (0x1E12, "M", "ḓ"), - (0x1E13, "V"), - (0x1E14, "M", "ḕ"), - (0x1E15, "V"), - (0x1E16, "M", "ḗ"), - (0x1E17, "V"), - (0x1E18, "M", "ḙ"), - (0x1E19, "V"), - (0x1E1A, "M", "ḛ"), - (0x1E1B, "V"), - (0x1E1C, "M", "ḝ"), - (0x1E1D, "V"), - (0x1E1E, "M", "ḟ"), - (0x1E1F, "V"), - (0x1E20, "M", "ḡ"), - (0x1E21, "V"), - (0x1E22, "M", "ḣ"), - (0x1E23, "V"), - (0x1E24, "M", "ḥ"), - (0x1E25, "V"), - (0x1E26, "M", "ḧ"), - (0x1E27, "V"), - (0x1E28, "M", "ḩ"), - (0x1E29, "V"), - (0x1E2A, "M", "ḫ"), - (0x1E2B, "V"), - (0x1E2C, "M", "ḭ"), - (0x1E2D, "V"), - (0x1E2E, "M", "ḯ"), - (0x1E2F, "V"), - (0x1E30, "M", "ḱ"), - (0x1E31, "V"), - (0x1E32, "M", "ḳ"), - (0x1E33, "V"), - (0x1E34, "M", "ḵ"), - (0x1E35, "V"), - (0x1E36, "M", "ḷ"), - (0x1E37, "V"), - (0x1E38, "M", "ḹ"), - (0x1E39, "V"), - (0x1E3A, "M", "ḻ"), - (0x1E3B, "V"), - (0x1E3C, "M", "ḽ"), - (0x1E3D, "V"), - (0x1E3E, "M", "ḿ"), - (0x1E3F, "V"), - (0x1E40, "M", "ṁ"), - (0x1E41, "V"), - (0x1E42, "M", "ṃ"), - (0x1E43, "V"), - (0x1E44, "M", "ṅ"), - (0x1E45, "V"), - (0x1E46, "M", "ṇ"), - (0x1E47, "V"), - (0x1E48, "M", "ṉ"), - (0x1E49, "V"), - (0x1E4A, "M", "ṋ"), - (0x1E4B, "V"), - (0x1E4C, "M", "ṍ"), - (0x1E4D, "V"), - (0x1E4E, "M", "ṏ"), - (0x1E4F, "V"), - (0x1E50, "M", "ṑ"), - (0x1E51, "V"), - (0x1E52, "M", "ṓ"), - (0x1E53, "V"), - (0x1E54, "M", "ṕ"), - (0x1E55, "V"), - (0x1E56, "M", "ṗ"), - (0x1E57, "V"), - (0x1E58, "M", "ṙ"), - (0x1E59, "V"), - (0x1E5A, "M", "ṛ"), - (0x1E5B, "V"), - (0x1E5C, "M", "ṝ"), - (0x1E5D, "V"), - (0x1E5E, "M", "ṟ"), - (0x1E5F, "V"), - (0x1E60, "M", "ṡ"), - (0x1E61, "V"), - (0x1E62, "M", "ṣ"), - (0x1E63, "V"), - (0x1E64, "M", "ṥ"), - (0x1E65, "V"), - ] - - -def _seg_18() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1E66, "M", "ṧ"), - (0x1E67, "V"), - (0x1E68, "M", "ṩ"), - (0x1E69, "V"), - (0x1E6A, "M", "ṫ"), - (0x1E6B, "V"), - (0x1E6C, "M", "ṭ"), - (0x1E6D, "V"), - (0x1E6E, "M", "ṯ"), - (0x1E6F, "V"), - (0x1E70, "M", "ṱ"), - (0x1E71, "V"), - (0x1E72, "M", "ṳ"), - (0x1E73, "V"), - (0x1E74, "M", "ṵ"), - (0x1E75, "V"), - (0x1E76, "M", "ṷ"), - (0x1E77, "V"), - (0x1E78, "M", "ṹ"), - (0x1E79, "V"), - (0x1E7A, "M", "ṻ"), - (0x1E7B, "V"), - (0x1E7C, "M", "ṽ"), - (0x1E7D, "V"), - (0x1E7E, "M", "ṿ"), - (0x1E7F, "V"), - (0x1E80, "M", "ẁ"), - (0x1E81, "V"), - (0x1E82, "M", "ẃ"), - (0x1E83, "V"), - (0x1E84, "M", "ẅ"), - (0x1E85, "V"), - (0x1E86, "M", "ẇ"), - (0x1E87, "V"), - (0x1E88, "M", "ẉ"), - (0x1E89, "V"), - (0x1E8A, "M", "ẋ"), - (0x1E8B, "V"), - (0x1E8C, "M", "ẍ"), - (0x1E8D, "V"), - (0x1E8E, "M", "ẏ"), - (0x1E8F, "V"), - (0x1E90, "M", "ẑ"), - (0x1E91, "V"), - (0x1E92, "M", "ẓ"), - (0x1E93, "V"), - (0x1E94, "M", "ẕ"), - (0x1E95, "V"), - (0x1E9A, "M", "aʾ"), - (0x1E9B, "M", "ṡ"), - (0x1E9C, "V"), - (0x1E9E, "M", "ß"), - (0x1E9F, "V"), - (0x1EA0, "M", "ạ"), - (0x1EA1, "V"), - (0x1EA2, "M", "ả"), - (0x1EA3, "V"), - (0x1EA4, "M", "ấ"), - (0x1EA5, "V"), - (0x1EA6, "M", "ầ"), - (0x1EA7, "V"), - (0x1EA8, "M", "ẩ"), - (0x1EA9, "V"), - (0x1EAA, "M", "ẫ"), - (0x1EAB, "V"), - (0x1EAC, "M", "ậ"), - (0x1EAD, "V"), - (0x1EAE, "M", "ắ"), - (0x1EAF, "V"), - (0x1EB0, "M", "ằ"), - (0x1EB1, "V"), - (0x1EB2, "M", "ẳ"), - (0x1EB3, "V"), - (0x1EB4, "M", "ẵ"), - (0x1EB5, "V"), - (0x1EB6, "M", "ặ"), - (0x1EB7, "V"), - (0x1EB8, "M", "ẹ"), - (0x1EB9, "V"), - (0x1EBA, "M", "ẻ"), - (0x1EBB, "V"), - (0x1EBC, "M", "ẽ"), - (0x1EBD, "V"), - (0x1EBE, "M", "ế"), - (0x1EBF, "V"), - (0x1EC0, "M", "ề"), - (0x1EC1, "V"), - (0x1EC2, "M", "ể"), - (0x1EC3, "V"), - (0x1EC4, "M", "ễ"), - (0x1EC5, "V"), - (0x1EC6, "M", "ệ"), - (0x1EC7, "V"), - (0x1EC8, "M", "ỉ"), - (0x1EC9, "V"), - (0x1ECA, "M", "ị"), - (0x1ECB, "V"), - (0x1ECC, "M", "ọ"), - (0x1ECD, "V"), - (0x1ECE, "M", "ỏ"), - ] - - -def _seg_19() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1ECF, "V"), - (0x1ED0, "M", "ố"), - (0x1ED1, "V"), - (0x1ED2, "M", "ồ"), - (0x1ED3, "V"), - (0x1ED4, "M", "ổ"), - (0x1ED5, "V"), - (0x1ED6, "M", "ỗ"), - (0x1ED7, "V"), - (0x1ED8, "M", "ộ"), - (0x1ED9, "V"), - (0x1EDA, "M", "ớ"), - (0x1EDB, "V"), - (0x1EDC, "M", "ờ"), - (0x1EDD, "V"), - (0x1EDE, "M", "ở"), - (0x1EDF, "V"), - (0x1EE0, "M", "ỡ"), - (0x1EE1, "V"), - (0x1EE2, "M", "ợ"), - (0x1EE3, "V"), - (0x1EE4, "M", "ụ"), - (0x1EE5, "V"), - (0x1EE6, "M", "ủ"), - (0x1EE7, "V"), - (0x1EE8, "M", "ứ"), - (0x1EE9, "V"), - (0x1EEA, "M", "ừ"), - (0x1EEB, "V"), - (0x1EEC, "M", "ử"), - (0x1EED, "V"), - (0x1EEE, "M", "ữ"), - (0x1EEF, "V"), - (0x1EF0, "M", "ự"), - (0x1EF1, "V"), - (0x1EF2, "M", "ỳ"), - (0x1EF3, "V"), - (0x1EF4, "M", "ỵ"), - (0x1EF5, "V"), - (0x1EF6, "M", "ỷ"), - (0x1EF7, "V"), - (0x1EF8, "M", "ỹ"), - (0x1EF9, "V"), - (0x1EFA, "M", "ỻ"), - (0x1EFB, "V"), - (0x1EFC, "M", "ỽ"), - (0x1EFD, "V"), - (0x1EFE, "M", "ỿ"), - (0x1EFF, "V"), - (0x1F08, "M", "ἀ"), - (0x1F09, "M", "ἁ"), - (0x1F0A, "M", "ἂ"), - (0x1F0B, "M", "ἃ"), - (0x1F0C, "M", "ἄ"), - (0x1F0D, "M", "ἅ"), - (0x1F0E, "M", "ἆ"), - (0x1F0F, "M", "ἇ"), - (0x1F10, "V"), - (0x1F16, "X"), - (0x1F18, "M", "ἐ"), - (0x1F19, "M", "ἑ"), - (0x1F1A, "M", "ἒ"), - (0x1F1B, "M", "ἓ"), - (0x1F1C, "M", "ἔ"), - (0x1F1D, "M", "ἕ"), - (0x1F1E, "X"), - (0x1F20, "V"), - (0x1F28, "M", "ἠ"), - (0x1F29, "M", "ἡ"), - (0x1F2A, "M", "ἢ"), - (0x1F2B, "M", "ἣ"), - (0x1F2C, "M", "ἤ"), - (0x1F2D, "M", "ἥ"), - (0x1F2E, "M", "ἦ"), - (0x1F2F, "M", "ἧ"), - (0x1F30, "V"), - (0x1F38, "M", "ἰ"), - (0x1F39, "M", "ἱ"), - (0x1F3A, "M", "ἲ"), - (0x1F3B, "M", "ἳ"), - (0x1F3C, "M", "ἴ"), - (0x1F3D, "M", "ἵ"), - (0x1F3E, "M", "ἶ"), - (0x1F3F, "M", "ἷ"), - (0x1F40, "V"), - (0x1F46, "X"), - (0x1F48, "M", "ὀ"), - (0x1F49, "M", "ὁ"), - (0x1F4A, "M", "ὂ"), - (0x1F4B, "M", "ὃ"), - (0x1F4C, "M", "ὄ"), - (0x1F4D, "M", "ὅ"), - (0x1F4E, "X"), - (0x1F50, "V"), - (0x1F58, "X"), - (0x1F59, "M", "ὑ"), - (0x1F5A, "X"), - (0x1F5B, "M", "ὓ"), - (0x1F5C, "X"), - (0x1F5D, "M", "ὕ"), - ] - - -def _seg_20() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1F5E, "X"), - (0x1F5F, "M", "ὗ"), - (0x1F60, "V"), - (0x1F68, "M", "ὠ"), - (0x1F69, "M", "ὡ"), - (0x1F6A, "M", "ὢ"), - (0x1F6B, "M", "ὣ"), - (0x1F6C, "M", "ὤ"), - (0x1F6D, "M", "ὥ"), - (0x1F6E, "M", "ὦ"), - (0x1F6F, "M", "ὧ"), - (0x1F70, "V"), - (0x1F71, "M", "ά"), - (0x1F72, "V"), - (0x1F73, "M", "έ"), - (0x1F74, "V"), - (0x1F75, "M", "ή"), - (0x1F76, "V"), - (0x1F77, "M", "ί"), - (0x1F78, "V"), - (0x1F79, "M", "ό"), - (0x1F7A, "V"), - (0x1F7B, "M", "ύ"), - (0x1F7C, "V"), - (0x1F7D, "M", "ώ"), - (0x1F7E, "X"), - (0x1F80, "M", "ἀι"), - (0x1F81, "M", "ἁι"), - (0x1F82, "M", "ἂι"), - (0x1F83, "M", "ἃι"), - (0x1F84, "M", "ἄι"), - (0x1F85, "M", "ἅι"), - (0x1F86, "M", "ἆι"), - (0x1F87, "M", "ἇι"), - (0x1F88, "M", "ἀι"), - (0x1F89, "M", "ἁι"), - (0x1F8A, "M", "ἂι"), - (0x1F8B, "M", "ἃι"), - (0x1F8C, "M", "ἄι"), - (0x1F8D, "M", "ἅι"), - (0x1F8E, "M", "ἆι"), - (0x1F8F, "M", "ἇι"), - (0x1F90, "M", "ἠι"), - (0x1F91, "M", "ἡι"), - (0x1F92, "M", "ἢι"), - (0x1F93, "M", "ἣι"), - (0x1F94, "M", "ἤι"), - (0x1F95, "M", "ἥι"), - (0x1F96, "M", "ἦι"), - (0x1F97, "M", "ἧι"), - (0x1F98, "M", "ἠι"), - (0x1F99, "M", "ἡι"), - (0x1F9A, "M", "ἢι"), - (0x1F9B, "M", "ἣι"), - (0x1F9C, "M", "ἤι"), - (0x1F9D, "M", "ἥι"), - (0x1F9E, "M", "ἦι"), - (0x1F9F, "M", "ἧι"), - (0x1FA0, "M", "ὠι"), - (0x1FA1, "M", "ὡι"), - (0x1FA2, "M", "ὢι"), - (0x1FA3, "M", "ὣι"), - (0x1FA4, "M", "ὤι"), - (0x1FA5, "M", "ὥι"), - (0x1FA6, "M", "ὦι"), - (0x1FA7, "M", "ὧι"), - (0x1FA8, "M", "ὠι"), - (0x1FA9, "M", "ὡι"), - (0x1FAA, "M", "ὢι"), - (0x1FAB, "M", "ὣι"), - (0x1FAC, "M", "ὤι"), - (0x1FAD, "M", "ὥι"), - (0x1FAE, "M", "ὦι"), - (0x1FAF, "M", "ὧι"), - (0x1FB0, "V"), - (0x1FB2, "M", "ὰι"), - (0x1FB3, "M", "αι"), - (0x1FB4, "M", "άι"), - (0x1FB5, "X"), - (0x1FB6, "V"), - (0x1FB7, "M", "ᾶι"), - (0x1FB8, "M", "ᾰ"), - (0x1FB9, "M", "ᾱ"), - (0x1FBA, "M", "ὰ"), - (0x1FBB, "M", "ά"), - (0x1FBC, "M", "αι"), - (0x1FBD, "M", " ̓"), - (0x1FBE, "M", "ι"), - (0x1FBF, "M", " ̓"), - (0x1FC0, "M", " ͂"), - (0x1FC1, "M", " ̈͂"), - (0x1FC2, "M", "ὴι"), - (0x1FC3, "M", "ηι"), - (0x1FC4, "M", "ήι"), - (0x1FC5, "X"), - (0x1FC6, "V"), - (0x1FC7, "M", "ῆι"), - (0x1FC8, "M", "ὲ"), - (0x1FC9, "M", "έ"), - (0x1FCA, "M", "ὴ"), - ] - - -def _seg_21() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1FCB, "M", "ή"), - (0x1FCC, "M", "ηι"), - (0x1FCD, "M", " ̓̀"), - (0x1FCE, "M", " ̓́"), - (0x1FCF, "M", " ̓͂"), - (0x1FD0, "V"), - (0x1FD3, "M", "ΐ"), - (0x1FD4, "X"), - (0x1FD6, "V"), - (0x1FD8, "M", "ῐ"), - (0x1FD9, "M", "ῑ"), - (0x1FDA, "M", "ὶ"), - (0x1FDB, "M", "ί"), - (0x1FDC, "X"), - (0x1FDD, "M", " ̔̀"), - (0x1FDE, "M", " ̔́"), - (0x1FDF, "M", " ̔͂"), - (0x1FE0, "V"), - (0x1FE3, "M", "ΰ"), - (0x1FE4, "V"), - (0x1FE8, "M", "ῠ"), - (0x1FE9, "M", "ῡ"), - (0x1FEA, "M", "ὺ"), - (0x1FEB, "M", "ύ"), - (0x1FEC, "M", "ῥ"), - (0x1FED, "M", " ̈̀"), - (0x1FEE, "M", " ̈́"), - (0x1FEF, "M", "`"), - (0x1FF0, "X"), - (0x1FF2, "M", "ὼι"), - (0x1FF3, "M", "ωι"), - (0x1FF4, "M", "ώι"), - (0x1FF5, "X"), - (0x1FF6, "V"), - (0x1FF7, "M", "ῶι"), - (0x1FF8, "M", "ὸ"), - (0x1FF9, "M", "ό"), - (0x1FFA, "M", "ὼ"), - (0x1FFB, "M", "ώ"), - (0x1FFC, "M", "ωι"), - (0x1FFD, "M", " ́"), - (0x1FFE, "M", " ̔"), - (0x1FFF, "X"), - (0x2000, "M", " "), - (0x200B, "I"), - (0x200C, "D", ""), - (0x200E, "X"), - (0x2010, "V"), - (0x2011, "M", "‐"), - (0x2012, "V"), - (0x2017, "M", " ̳"), - (0x2018, "V"), - (0x2024, "X"), - (0x2027, "V"), - (0x2028, "X"), - (0x202F, "M", " "), - (0x2030, "V"), - (0x2033, "M", "′′"), - (0x2034, "M", "′′′"), - (0x2035, "V"), - (0x2036, "M", "‵‵"), - (0x2037, "M", "‵‵‵"), - (0x2038, "V"), - (0x203C, "M", "!!"), - (0x203D, "V"), - (0x203E, "M", " ̅"), - (0x203F, "V"), - (0x2047, "M", "??"), - (0x2048, "M", "?!"), - (0x2049, "M", "!?"), - (0x204A, "V"), - (0x2057, "M", "′′′′"), - (0x2058, "V"), - (0x205F, "M", " "), - (0x2060, "I"), - (0x2065, "X"), - (0x206A, "I"), - (0x2070, "M", "0"), - (0x2071, "M", "i"), - (0x2072, "X"), - (0x2074, "M", "4"), - (0x2075, "M", "5"), - (0x2076, "M", "6"), - (0x2077, "M", "7"), - (0x2078, "M", "8"), - (0x2079, "M", "9"), - (0x207A, "M", "+"), - (0x207B, "M", "−"), - (0x207C, "M", "="), - (0x207D, "M", "("), - (0x207E, "M", ")"), - (0x207F, "M", "n"), - (0x2080, "M", "0"), - (0x2081, "M", "1"), - (0x2082, "M", "2"), - (0x2083, "M", "3"), - (0x2084, "M", "4"), - (0x2085, "M", "5"), - (0x2086, "M", "6"), - (0x2087, "M", "7"), - ] - - -def _seg_22() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2088, "M", "8"), - (0x2089, "M", "9"), - (0x208A, "M", "+"), - (0x208B, "M", "−"), - (0x208C, "M", "="), - (0x208D, "M", "("), - (0x208E, "M", ")"), - (0x208F, "X"), - (0x2090, "M", "a"), - (0x2091, "M", "e"), - (0x2092, "M", "o"), - (0x2093, "M", "x"), - (0x2094, "M", "ə"), - (0x2095, "M", "h"), - (0x2096, "M", "k"), - (0x2097, "M", "l"), - (0x2098, "M", "m"), - (0x2099, "M", "n"), - (0x209A, "M", "p"), - (0x209B, "M", "s"), - (0x209C, "M", "t"), - (0x209D, "X"), - (0x20A0, "V"), - (0x20A8, "M", "rs"), - (0x20A9, "V"), - (0x20C1, "X"), - (0x20D0, "V"), - (0x20F1, "X"), - (0x2100, "M", "a/c"), - (0x2101, "M", "a/s"), - (0x2102, "M", "c"), - (0x2103, "M", "°c"), - (0x2104, "V"), - (0x2105, "M", "c/o"), - (0x2106, "M", "c/u"), - (0x2107, "M", "ɛ"), - (0x2108, "V"), - (0x2109, "M", "°f"), - (0x210A, "M", "g"), - (0x210B, "M", "h"), - (0x210F, "M", "ħ"), - (0x2110, "M", "i"), - (0x2112, "M", "l"), - (0x2114, "V"), - (0x2115, "M", "n"), - (0x2116, "M", "no"), - (0x2117, "V"), - (0x2119, "M", "p"), - (0x211A, "M", "q"), - (0x211B, "M", "r"), - (0x211E, "V"), - (0x2120, "M", "sm"), - (0x2121, "M", "tel"), - (0x2122, "M", "tm"), - (0x2123, "V"), - (0x2124, "M", "z"), - (0x2125, "V"), - (0x2126, "M", "ω"), - (0x2127, "V"), - (0x2128, "M", "z"), - (0x2129, "V"), - (0x212A, "M", "k"), - (0x212B, "M", "å"), - (0x212C, "M", "b"), - (0x212D, "M", "c"), - (0x212E, "V"), - (0x212F, "M", "e"), - (0x2131, "M", "f"), - (0x2132, "M", "ⅎ"), - (0x2133, "M", "m"), - (0x2134, "M", "o"), - (0x2135, "M", "א"), - (0x2136, "M", "ב"), - (0x2137, "M", "ג"), - (0x2138, "M", "ד"), - (0x2139, "M", "i"), - (0x213A, "V"), - (0x213B, "M", "fax"), - (0x213C, "M", "π"), - (0x213D, "M", "γ"), - (0x213F, "M", "π"), - (0x2140, "M", "∑"), - (0x2141, "V"), - (0x2145, "M", "d"), - (0x2147, "M", "e"), - (0x2148, "M", "i"), - (0x2149, "M", "j"), - (0x214A, "V"), - (0x2150, "M", "1⁄7"), - (0x2151, "M", "1⁄9"), - (0x2152, "M", "1⁄10"), - (0x2153, "M", "1⁄3"), - (0x2154, "M", "2⁄3"), - (0x2155, "M", "1⁄5"), - (0x2156, "M", "2⁄5"), - (0x2157, "M", "3⁄5"), - (0x2158, "M", "4⁄5"), - (0x2159, "M", "1⁄6"), - (0x215A, "M", "5⁄6"), - (0x215B, "M", "1⁄8"), - ] - - -def _seg_23() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x215C, "M", "3⁄8"), - (0x215D, "M", "5⁄8"), - (0x215E, "M", "7⁄8"), - (0x215F, "M", "1⁄"), - (0x2160, "M", "i"), - (0x2161, "M", "ii"), - (0x2162, "M", "iii"), - (0x2163, "M", "iv"), - (0x2164, "M", "v"), - (0x2165, "M", "vi"), - (0x2166, "M", "vii"), - (0x2167, "M", "viii"), - (0x2168, "M", "ix"), - (0x2169, "M", "x"), - (0x216A, "M", "xi"), - (0x216B, "M", "xii"), - (0x216C, "M", "l"), - (0x216D, "M", "c"), - (0x216E, "M", "d"), - (0x216F, "M", "m"), - (0x2170, "M", "i"), - (0x2171, "M", "ii"), - (0x2172, "M", "iii"), - (0x2173, "M", "iv"), - (0x2174, "M", "v"), - (0x2175, "M", "vi"), - (0x2176, "M", "vii"), - (0x2177, "M", "viii"), - (0x2178, "M", "ix"), - (0x2179, "M", "x"), - (0x217A, "M", "xi"), - (0x217B, "M", "xii"), - (0x217C, "M", "l"), - (0x217D, "M", "c"), - (0x217E, "M", "d"), - (0x217F, "M", "m"), - (0x2180, "V"), - (0x2183, "M", "ↄ"), - (0x2184, "V"), - (0x2189, "M", "0⁄3"), - (0x218A, "V"), - (0x218C, "X"), - (0x2190, "V"), - (0x222C, "M", "∫∫"), - (0x222D, "M", "∫∫∫"), - (0x222E, "V"), - (0x222F, "M", "∮∮"), - (0x2230, "M", "∮∮∮"), - (0x2231, "V"), - (0x2329, "M", "〈"), - (0x232A, "M", "〉"), - (0x232B, "V"), - (0x242A, "X"), - (0x2440, "V"), - (0x244B, "X"), - (0x2460, "M", "1"), - (0x2461, "M", "2"), - (0x2462, "M", "3"), - (0x2463, "M", "4"), - (0x2464, "M", "5"), - (0x2465, "M", "6"), - (0x2466, "M", "7"), - (0x2467, "M", "8"), - (0x2468, "M", "9"), - (0x2469, "M", "10"), - (0x246A, "M", "11"), - (0x246B, "M", "12"), - (0x246C, "M", "13"), - (0x246D, "M", "14"), - (0x246E, "M", "15"), - (0x246F, "M", "16"), - (0x2470, "M", "17"), - (0x2471, "M", "18"), - (0x2472, "M", "19"), - (0x2473, "M", "20"), - (0x2474, "M", "(1)"), - (0x2475, "M", "(2)"), - (0x2476, "M", "(3)"), - (0x2477, "M", "(4)"), - (0x2478, "M", "(5)"), - (0x2479, "M", "(6)"), - (0x247A, "M", "(7)"), - (0x247B, "M", "(8)"), - (0x247C, "M", "(9)"), - (0x247D, "M", "(10)"), - (0x247E, "M", "(11)"), - (0x247F, "M", "(12)"), - (0x2480, "M", "(13)"), - (0x2481, "M", "(14)"), - (0x2482, "M", "(15)"), - (0x2483, "M", "(16)"), - (0x2484, "M", "(17)"), - (0x2485, "M", "(18)"), - (0x2486, "M", "(19)"), - (0x2487, "M", "(20)"), - (0x2488, "X"), - (0x249C, "M", "(a)"), - (0x249D, "M", "(b)"), - (0x249E, "M", "(c)"), - (0x249F, "M", "(d)"), - ] - - -def _seg_24() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x24A0, "M", "(e)"), - (0x24A1, "M", "(f)"), - (0x24A2, "M", "(g)"), - (0x24A3, "M", "(h)"), - (0x24A4, "M", "(i)"), - (0x24A5, "M", "(j)"), - (0x24A6, "M", "(k)"), - (0x24A7, "M", "(l)"), - (0x24A8, "M", "(m)"), - (0x24A9, "M", "(n)"), - (0x24AA, "M", "(o)"), - (0x24AB, "M", "(p)"), - (0x24AC, "M", "(q)"), - (0x24AD, "M", "(r)"), - (0x24AE, "M", "(s)"), - (0x24AF, "M", "(t)"), - (0x24B0, "M", "(u)"), - (0x24B1, "M", "(v)"), - (0x24B2, "M", "(w)"), - (0x24B3, "M", "(x)"), - (0x24B4, "M", "(y)"), - (0x24B5, "M", "(z)"), - (0x24B6, "M", "a"), - (0x24B7, "M", "b"), - (0x24B8, "M", "c"), - (0x24B9, "M", "d"), - (0x24BA, "M", "e"), - (0x24BB, "M", "f"), - (0x24BC, "M", "g"), - (0x24BD, "M", "h"), - (0x24BE, "M", "i"), - (0x24BF, "M", "j"), - (0x24C0, "M", "k"), - (0x24C1, "M", "l"), - (0x24C2, "M", "m"), - (0x24C3, "M", "n"), - (0x24C4, "M", "o"), - (0x24C5, "M", "p"), - (0x24C6, "M", "q"), - (0x24C7, "M", "r"), - (0x24C8, "M", "s"), - (0x24C9, "M", "t"), - (0x24CA, "M", "u"), - (0x24CB, "M", "v"), - (0x24CC, "M", "w"), - (0x24CD, "M", "x"), - (0x24CE, "M", "y"), - (0x24CF, "M", "z"), - (0x24D0, "M", "a"), - (0x24D1, "M", "b"), - (0x24D2, "M", "c"), - (0x24D3, "M", "d"), - (0x24D4, "M", "e"), - (0x24D5, "M", "f"), - (0x24D6, "M", "g"), - (0x24D7, "M", "h"), - (0x24D8, "M", "i"), - (0x24D9, "M", "j"), - (0x24DA, "M", "k"), - (0x24DB, "M", "l"), - (0x24DC, "M", "m"), - (0x24DD, "M", "n"), - (0x24DE, "M", "o"), - (0x24DF, "M", "p"), - (0x24E0, "M", "q"), - (0x24E1, "M", "r"), - (0x24E2, "M", "s"), - (0x24E3, "M", "t"), - (0x24E4, "M", "u"), - (0x24E5, "M", "v"), - (0x24E6, "M", "w"), - (0x24E7, "M", "x"), - (0x24E8, "M", "y"), - (0x24E9, "M", "z"), - (0x24EA, "M", "0"), - (0x24EB, "V"), - (0x2A0C, "M", "∫∫∫∫"), - (0x2A0D, "V"), - (0x2A74, "M", "::="), - (0x2A75, "M", "=="), - (0x2A76, "M", "==="), - (0x2A77, "V"), - (0x2ADC, "M", "⫝̸"), - (0x2ADD, "V"), - (0x2B74, "X"), - (0x2B76, "V"), - (0x2B96, "X"), - (0x2B97, "V"), - (0x2C00, "M", "ⰰ"), - (0x2C01, "M", "ⰱ"), - (0x2C02, "M", "ⰲ"), - (0x2C03, "M", "ⰳ"), - (0x2C04, "M", "ⰴ"), - (0x2C05, "M", "ⰵ"), - (0x2C06, "M", "ⰶ"), - (0x2C07, "M", "ⰷ"), - (0x2C08, "M", "ⰸ"), - (0x2C09, "M", "ⰹ"), - (0x2C0A, "M", "ⰺ"), - (0x2C0B, "M", "ⰻ"), - ] - - -def _seg_25() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2C0C, "M", "ⰼ"), - (0x2C0D, "M", "ⰽ"), - (0x2C0E, "M", "ⰾ"), - (0x2C0F, "M", "ⰿ"), - (0x2C10, "M", "ⱀ"), - (0x2C11, "M", "ⱁ"), - (0x2C12, "M", "ⱂ"), - (0x2C13, "M", "ⱃ"), - (0x2C14, "M", "ⱄ"), - (0x2C15, "M", "ⱅ"), - (0x2C16, "M", "ⱆ"), - (0x2C17, "M", "ⱇ"), - (0x2C18, "M", "ⱈ"), - (0x2C19, "M", "ⱉ"), - (0x2C1A, "M", "ⱊ"), - (0x2C1B, "M", "ⱋ"), - (0x2C1C, "M", "ⱌ"), - (0x2C1D, "M", "ⱍ"), - (0x2C1E, "M", "ⱎ"), - (0x2C1F, "M", "ⱏ"), - (0x2C20, "M", "ⱐ"), - (0x2C21, "M", "ⱑ"), - (0x2C22, "M", "ⱒ"), - (0x2C23, "M", "ⱓ"), - (0x2C24, "M", "ⱔ"), - (0x2C25, "M", "ⱕ"), - (0x2C26, "M", "ⱖ"), - (0x2C27, "M", "ⱗ"), - (0x2C28, "M", "ⱘ"), - (0x2C29, "M", "ⱙ"), - (0x2C2A, "M", "ⱚ"), - (0x2C2B, "M", "ⱛ"), - (0x2C2C, "M", "ⱜ"), - (0x2C2D, "M", "ⱝ"), - (0x2C2E, "M", "ⱞ"), - (0x2C2F, "M", "ⱟ"), - (0x2C30, "V"), - (0x2C60, "M", "ⱡ"), - (0x2C61, "V"), - (0x2C62, "M", "ɫ"), - (0x2C63, "M", "ᵽ"), - (0x2C64, "M", "ɽ"), - (0x2C65, "V"), - (0x2C67, "M", "ⱨ"), - (0x2C68, "V"), - (0x2C69, "M", "ⱪ"), - (0x2C6A, "V"), - (0x2C6B, "M", "ⱬ"), - (0x2C6C, "V"), - (0x2C6D, "M", "ɑ"), - (0x2C6E, "M", "ɱ"), - (0x2C6F, "M", "ɐ"), - (0x2C70, "M", "ɒ"), - (0x2C71, "V"), - (0x2C72, "M", "ⱳ"), - (0x2C73, "V"), - (0x2C75, "M", "ⱶ"), - (0x2C76, "V"), - (0x2C7C, "M", "j"), - (0x2C7D, "M", "v"), - (0x2C7E, "M", "ȿ"), - (0x2C7F, "M", "ɀ"), - (0x2C80, "M", "ⲁ"), - (0x2C81, "V"), - (0x2C82, "M", "ⲃ"), - (0x2C83, "V"), - (0x2C84, "M", "ⲅ"), - (0x2C85, "V"), - (0x2C86, "M", "ⲇ"), - (0x2C87, "V"), - (0x2C88, "M", "ⲉ"), - (0x2C89, "V"), - (0x2C8A, "M", "ⲋ"), - (0x2C8B, "V"), - (0x2C8C, "M", "ⲍ"), - (0x2C8D, "V"), - (0x2C8E, "M", "ⲏ"), - (0x2C8F, "V"), - (0x2C90, "M", "ⲑ"), - (0x2C91, "V"), - (0x2C92, "M", "ⲓ"), - (0x2C93, "V"), - (0x2C94, "M", "ⲕ"), - (0x2C95, "V"), - (0x2C96, "M", "ⲗ"), - (0x2C97, "V"), - (0x2C98, "M", "ⲙ"), - (0x2C99, "V"), - (0x2C9A, "M", "ⲛ"), - (0x2C9B, "V"), - (0x2C9C, "M", "ⲝ"), - (0x2C9D, "V"), - (0x2C9E, "M", "ⲟ"), - (0x2C9F, "V"), - (0x2CA0, "M", "ⲡ"), - (0x2CA1, "V"), - (0x2CA2, "M", "ⲣ"), - (0x2CA3, "V"), - (0x2CA4, "M", "ⲥ"), - (0x2CA5, "V"), - ] - - -def _seg_26() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2CA6, "M", "ⲧ"), - (0x2CA7, "V"), - (0x2CA8, "M", "ⲩ"), - (0x2CA9, "V"), - (0x2CAA, "M", "ⲫ"), - (0x2CAB, "V"), - (0x2CAC, "M", "ⲭ"), - (0x2CAD, "V"), - (0x2CAE, "M", "ⲯ"), - (0x2CAF, "V"), - (0x2CB0, "M", "ⲱ"), - (0x2CB1, "V"), - (0x2CB2, "M", "ⲳ"), - (0x2CB3, "V"), - (0x2CB4, "M", "ⲵ"), - (0x2CB5, "V"), - (0x2CB6, "M", "ⲷ"), - (0x2CB7, "V"), - (0x2CB8, "M", "ⲹ"), - (0x2CB9, "V"), - (0x2CBA, "M", "ⲻ"), - (0x2CBB, "V"), - (0x2CBC, "M", "ⲽ"), - (0x2CBD, "V"), - (0x2CBE, "M", "ⲿ"), - (0x2CBF, "V"), - (0x2CC0, "M", "ⳁ"), - (0x2CC1, "V"), - (0x2CC2, "M", "ⳃ"), - (0x2CC3, "V"), - (0x2CC4, "M", "ⳅ"), - (0x2CC5, "V"), - (0x2CC6, "M", "ⳇ"), - (0x2CC7, "V"), - (0x2CC8, "M", "ⳉ"), - (0x2CC9, "V"), - (0x2CCA, "M", "ⳋ"), - (0x2CCB, "V"), - (0x2CCC, "M", "ⳍ"), - (0x2CCD, "V"), - (0x2CCE, "M", "ⳏ"), - (0x2CCF, "V"), - (0x2CD0, "M", "ⳑ"), - (0x2CD1, "V"), - (0x2CD2, "M", "ⳓ"), - (0x2CD3, "V"), - (0x2CD4, "M", "ⳕ"), - (0x2CD5, "V"), - (0x2CD6, "M", "ⳗ"), - (0x2CD7, "V"), - (0x2CD8, "M", "ⳙ"), - (0x2CD9, "V"), - (0x2CDA, "M", "ⳛ"), - (0x2CDB, "V"), - (0x2CDC, "M", "ⳝ"), - (0x2CDD, "V"), - (0x2CDE, "M", "ⳟ"), - (0x2CDF, "V"), - (0x2CE0, "M", "ⳡ"), - (0x2CE1, "V"), - (0x2CE2, "M", "ⳣ"), - (0x2CE3, "V"), - (0x2CEB, "M", "ⳬ"), - (0x2CEC, "V"), - (0x2CED, "M", "ⳮ"), - (0x2CEE, "V"), - (0x2CF2, "M", "ⳳ"), - (0x2CF3, "V"), - (0x2CF4, "X"), - (0x2CF9, "V"), - (0x2D26, "X"), - (0x2D27, "V"), - (0x2D28, "X"), - (0x2D2D, "V"), - (0x2D2E, "X"), - (0x2D30, "V"), - (0x2D68, "X"), - (0x2D6F, "M", "ⵡ"), - (0x2D70, "V"), - (0x2D71, "X"), - (0x2D7F, "V"), - (0x2D97, "X"), - (0x2DA0, "V"), - (0x2DA7, "X"), - (0x2DA8, "V"), - (0x2DAF, "X"), - (0x2DB0, "V"), - (0x2DB7, "X"), - (0x2DB8, "V"), - (0x2DBF, "X"), - (0x2DC0, "V"), - (0x2DC7, "X"), - (0x2DC8, "V"), - (0x2DCF, "X"), - (0x2DD0, "V"), - (0x2DD7, "X"), - (0x2DD8, "V"), - (0x2DDF, "X"), - (0x2DE0, "V"), - (0x2E5E, "X"), - ] - - -def _seg_27() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2E80, "V"), - (0x2E9A, "X"), - (0x2E9B, "V"), - (0x2E9F, "M", "母"), - (0x2EA0, "V"), - (0x2EF3, "M", "龟"), - (0x2EF4, "X"), - (0x2F00, "M", "一"), - (0x2F01, "M", "丨"), - (0x2F02, "M", "丶"), - (0x2F03, "M", "丿"), - (0x2F04, "M", "乙"), - (0x2F05, "M", "亅"), - (0x2F06, "M", "二"), - (0x2F07, "M", "亠"), - (0x2F08, "M", "人"), - (0x2F09, "M", "儿"), - (0x2F0A, "M", "入"), - (0x2F0B, "M", "八"), - (0x2F0C, "M", "冂"), - (0x2F0D, "M", "冖"), - (0x2F0E, "M", "冫"), - (0x2F0F, "M", "几"), - (0x2F10, "M", "凵"), - (0x2F11, "M", "刀"), - (0x2F12, "M", "力"), - (0x2F13, "M", "勹"), - (0x2F14, "M", "匕"), - (0x2F15, "M", "匚"), - (0x2F16, "M", "匸"), - (0x2F17, "M", "十"), - (0x2F18, "M", "卜"), - (0x2F19, "M", "卩"), - (0x2F1A, "M", "厂"), - (0x2F1B, "M", "厶"), - (0x2F1C, "M", "又"), - (0x2F1D, "M", "口"), - (0x2F1E, "M", "囗"), - (0x2F1F, "M", "土"), - (0x2F20, "M", "士"), - (0x2F21, "M", "夂"), - (0x2F22, "M", "夊"), - (0x2F23, "M", "夕"), - (0x2F24, "M", "大"), - (0x2F25, "M", "女"), - (0x2F26, "M", "子"), - (0x2F27, "M", "宀"), - (0x2F28, "M", "寸"), - (0x2F29, "M", "小"), - (0x2F2A, "M", "尢"), - (0x2F2B, "M", "尸"), - (0x2F2C, "M", "屮"), - (0x2F2D, "M", "山"), - (0x2F2E, "M", "巛"), - (0x2F2F, "M", "工"), - (0x2F30, "M", "己"), - (0x2F31, "M", "巾"), - (0x2F32, "M", "干"), - (0x2F33, "M", "幺"), - (0x2F34, "M", "广"), - (0x2F35, "M", "廴"), - (0x2F36, "M", "廾"), - (0x2F37, "M", "弋"), - (0x2F38, "M", "弓"), - (0x2F39, "M", "彐"), - (0x2F3A, "M", "彡"), - (0x2F3B, "M", "彳"), - (0x2F3C, "M", "心"), - (0x2F3D, "M", "戈"), - (0x2F3E, "M", "戶"), - (0x2F3F, "M", "手"), - (0x2F40, "M", "支"), - (0x2F41, "M", "攴"), - (0x2F42, "M", "文"), - (0x2F43, "M", "斗"), - (0x2F44, "M", "斤"), - (0x2F45, "M", "方"), - (0x2F46, "M", "无"), - (0x2F47, "M", "日"), - (0x2F48, "M", "曰"), - (0x2F49, "M", "月"), - (0x2F4A, "M", "木"), - (0x2F4B, "M", "欠"), - (0x2F4C, "M", "止"), - (0x2F4D, "M", "歹"), - (0x2F4E, "M", "殳"), - (0x2F4F, "M", "毋"), - (0x2F50, "M", "比"), - (0x2F51, "M", "毛"), - (0x2F52, "M", "氏"), - (0x2F53, "M", "气"), - (0x2F54, "M", "水"), - (0x2F55, "M", "火"), - (0x2F56, "M", "爪"), - (0x2F57, "M", "父"), - (0x2F58, "M", "爻"), - (0x2F59, "M", "爿"), - (0x2F5A, "M", "片"), - (0x2F5B, "M", "牙"), - (0x2F5C, "M", "牛"), - ] - - -def _seg_28() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F5D, "M", "犬"), - (0x2F5E, "M", "玄"), - (0x2F5F, "M", "玉"), - (0x2F60, "M", "瓜"), - (0x2F61, "M", "瓦"), - (0x2F62, "M", "甘"), - (0x2F63, "M", "生"), - (0x2F64, "M", "用"), - (0x2F65, "M", "田"), - (0x2F66, "M", "疋"), - (0x2F67, "M", "疒"), - (0x2F68, "M", "癶"), - (0x2F69, "M", "白"), - (0x2F6A, "M", "皮"), - (0x2F6B, "M", "皿"), - (0x2F6C, "M", "目"), - (0x2F6D, "M", "矛"), - (0x2F6E, "M", "矢"), - (0x2F6F, "M", "石"), - (0x2F70, "M", "示"), - (0x2F71, "M", "禸"), - (0x2F72, "M", "禾"), - (0x2F73, "M", "穴"), - (0x2F74, "M", "立"), - (0x2F75, "M", "竹"), - (0x2F76, "M", "米"), - (0x2F77, "M", "糸"), - (0x2F78, "M", "缶"), - (0x2F79, "M", "网"), - (0x2F7A, "M", "羊"), - (0x2F7B, "M", "羽"), - (0x2F7C, "M", "老"), - (0x2F7D, "M", "而"), - (0x2F7E, "M", "耒"), - (0x2F7F, "M", "耳"), - (0x2F80, "M", "聿"), - (0x2F81, "M", "肉"), - (0x2F82, "M", "臣"), - (0x2F83, "M", "自"), - (0x2F84, "M", "至"), - (0x2F85, "M", "臼"), - (0x2F86, "M", "舌"), - (0x2F87, "M", "舛"), - (0x2F88, "M", "舟"), - (0x2F89, "M", "艮"), - (0x2F8A, "M", "色"), - (0x2F8B, "M", "艸"), - (0x2F8C, "M", "虍"), - (0x2F8D, "M", "虫"), - (0x2F8E, "M", "血"), - (0x2F8F, "M", "行"), - (0x2F90, "M", "衣"), - (0x2F91, "M", "襾"), - (0x2F92, "M", "見"), - (0x2F93, "M", "角"), - (0x2F94, "M", "言"), - (0x2F95, "M", "谷"), - (0x2F96, "M", "豆"), - (0x2F97, "M", "豕"), - (0x2F98, "M", "豸"), - (0x2F99, "M", "貝"), - (0x2F9A, "M", "赤"), - (0x2F9B, "M", "走"), - (0x2F9C, "M", "足"), - (0x2F9D, "M", "身"), - (0x2F9E, "M", "車"), - (0x2F9F, "M", "辛"), - (0x2FA0, "M", "辰"), - (0x2FA1, "M", "辵"), - (0x2FA2, "M", "邑"), - (0x2FA3, "M", "酉"), - (0x2FA4, "M", "釆"), - (0x2FA5, "M", "里"), - (0x2FA6, "M", "金"), - (0x2FA7, "M", "長"), - (0x2FA8, "M", "門"), - (0x2FA9, "M", "阜"), - (0x2FAA, "M", "隶"), - (0x2FAB, "M", "隹"), - (0x2FAC, "M", "雨"), - (0x2FAD, "M", "靑"), - (0x2FAE, "M", "非"), - (0x2FAF, "M", "面"), - (0x2FB0, "M", "革"), - (0x2FB1, "M", "韋"), - (0x2FB2, "M", "韭"), - (0x2FB3, "M", "音"), - (0x2FB4, "M", "頁"), - (0x2FB5, "M", "風"), - (0x2FB6, "M", "飛"), - (0x2FB7, "M", "食"), - (0x2FB8, "M", "首"), - (0x2FB9, "M", "香"), - (0x2FBA, "M", "馬"), - (0x2FBB, "M", "骨"), - (0x2FBC, "M", "高"), - (0x2FBD, "M", "髟"), - (0x2FBE, "M", "鬥"), - (0x2FBF, "M", "鬯"), - (0x2FC0, "M", "鬲"), - ] - - -def _seg_29() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2FC1, "M", "鬼"), - (0x2FC2, "M", "魚"), - (0x2FC3, "M", "鳥"), - (0x2FC4, "M", "鹵"), - (0x2FC5, "M", "鹿"), - (0x2FC6, "M", "麥"), - (0x2FC7, "M", "麻"), - (0x2FC8, "M", "黃"), - (0x2FC9, "M", "黍"), - (0x2FCA, "M", "黑"), - (0x2FCB, "M", "黹"), - (0x2FCC, "M", "黽"), - (0x2FCD, "M", "鼎"), - (0x2FCE, "M", "鼓"), - (0x2FCF, "M", "鼠"), - (0x2FD0, "M", "鼻"), - (0x2FD1, "M", "齊"), - (0x2FD2, "M", "齒"), - (0x2FD3, "M", "龍"), - (0x2FD4, "M", "龜"), - (0x2FD5, "M", "龠"), - (0x2FD6, "X"), - (0x3000, "M", " "), - (0x3001, "V"), - (0x3002, "M", "."), - (0x3003, "V"), - (0x3036, "M", "〒"), - (0x3037, "V"), - (0x3038, "M", "十"), - (0x3039, "M", "卄"), - (0x303A, "M", "卅"), - (0x303B, "V"), - (0x3040, "X"), - (0x3041, "V"), - (0x3097, "X"), - (0x3099, "V"), - (0x309B, "M", " ゙"), - (0x309C, "M", " ゚"), - (0x309D, "V"), - (0x309F, "M", "より"), - (0x30A0, "V"), - (0x30FF, "M", "コト"), - (0x3100, "X"), - (0x3105, "V"), - (0x3130, "X"), - (0x3131, "M", "ᄀ"), - (0x3132, "M", "ᄁ"), - (0x3133, "M", "ᆪ"), - (0x3134, "M", "ᄂ"), - (0x3135, "M", "ᆬ"), - (0x3136, "M", "ᆭ"), - (0x3137, "M", "ᄃ"), - (0x3138, "M", "ᄄ"), - (0x3139, "M", "ᄅ"), - (0x313A, "M", "ᆰ"), - (0x313B, "M", "ᆱ"), - (0x313C, "M", "ᆲ"), - (0x313D, "M", "ᆳ"), - (0x313E, "M", "ᆴ"), - (0x313F, "M", "ᆵ"), - (0x3140, "M", "ᄚ"), - (0x3141, "M", "ᄆ"), - (0x3142, "M", "ᄇ"), - (0x3143, "M", "ᄈ"), - (0x3144, "M", "ᄡ"), - (0x3145, "M", "ᄉ"), - (0x3146, "M", "ᄊ"), - (0x3147, "M", "ᄋ"), - (0x3148, "M", "ᄌ"), - (0x3149, "M", "ᄍ"), - (0x314A, "M", "ᄎ"), - (0x314B, "M", "ᄏ"), - (0x314C, "M", "ᄐ"), - (0x314D, "M", "ᄑ"), - (0x314E, "M", "ᄒ"), - (0x314F, "M", "ᅡ"), - (0x3150, "M", "ᅢ"), - (0x3151, "M", "ᅣ"), - (0x3152, "M", "ᅤ"), - (0x3153, "M", "ᅥ"), - (0x3154, "M", "ᅦ"), - (0x3155, "M", "ᅧ"), - (0x3156, "M", "ᅨ"), - (0x3157, "M", "ᅩ"), - (0x3158, "M", "ᅪ"), - (0x3159, "M", "ᅫ"), - (0x315A, "M", "ᅬ"), - (0x315B, "M", "ᅭ"), - (0x315C, "M", "ᅮ"), - (0x315D, "M", "ᅯ"), - (0x315E, "M", "ᅰ"), - (0x315F, "M", "ᅱ"), - (0x3160, "M", "ᅲ"), - (0x3161, "M", "ᅳ"), - (0x3162, "M", "ᅴ"), - (0x3163, "M", "ᅵ"), - (0x3164, "I"), - (0x3165, "M", "ᄔ"), - (0x3166, "M", "ᄕ"), - (0x3167, "M", "ᇇ"), - ] - - -def _seg_30() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x3168, "M", "ᇈ"), - (0x3169, "M", "ᇌ"), - (0x316A, "M", "ᇎ"), - (0x316B, "M", "ᇓ"), - (0x316C, "M", "ᇗ"), - (0x316D, "M", "ᇙ"), - (0x316E, "M", "ᄜ"), - (0x316F, "M", "ᇝ"), - (0x3170, "M", "ᇟ"), - (0x3171, "M", "ᄝ"), - (0x3172, "M", "ᄞ"), - (0x3173, "M", "ᄠ"), - (0x3174, "M", "ᄢ"), - (0x3175, "M", "ᄣ"), - (0x3176, "M", "ᄧ"), - (0x3177, "M", "ᄩ"), - (0x3178, "M", "ᄫ"), - (0x3179, "M", "ᄬ"), - (0x317A, "M", "ᄭ"), - (0x317B, "M", "ᄮ"), - (0x317C, "M", "ᄯ"), - (0x317D, "M", "ᄲ"), - (0x317E, "M", "ᄶ"), - (0x317F, "M", "ᅀ"), - (0x3180, "M", "ᅇ"), - (0x3181, "M", "ᅌ"), - (0x3182, "M", "ᇱ"), - (0x3183, "M", "ᇲ"), - (0x3184, "M", "ᅗ"), - (0x3185, "M", "ᅘ"), - (0x3186, "M", "ᅙ"), - (0x3187, "M", "ᆄ"), - (0x3188, "M", "ᆅ"), - (0x3189, "M", "ᆈ"), - (0x318A, "M", "ᆑ"), - (0x318B, "M", "ᆒ"), - (0x318C, "M", "ᆔ"), - (0x318D, "M", "ᆞ"), - (0x318E, "M", "ᆡ"), - (0x318F, "X"), - (0x3190, "V"), - (0x3192, "M", "一"), - (0x3193, "M", "二"), - (0x3194, "M", "三"), - (0x3195, "M", "四"), - (0x3196, "M", "上"), - (0x3197, "M", "中"), - (0x3198, "M", "下"), - (0x3199, "M", "甲"), - (0x319A, "M", "乙"), - (0x319B, "M", "丙"), - (0x319C, "M", "丁"), - (0x319D, "M", "天"), - (0x319E, "M", "地"), - (0x319F, "M", "人"), - (0x31A0, "V"), - (0x31E6, "X"), - (0x31F0, "V"), - (0x3200, "M", "(ᄀ)"), - (0x3201, "M", "(ᄂ)"), - (0x3202, "M", "(ᄃ)"), - (0x3203, "M", "(ᄅ)"), - (0x3204, "M", "(ᄆ)"), - (0x3205, "M", "(ᄇ)"), - (0x3206, "M", "(ᄉ)"), - (0x3207, "M", "(ᄋ)"), - (0x3208, "M", "(ᄌ)"), - (0x3209, "M", "(ᄎ)"), - (0x320A, "M", "(ᄏ)"), - (0x320B, "M", "(ᄐ)"), - (0x320C, "M", "(ᄑ)"), - (0x320D, "M", "(ᄒ)"), - (0x320E, "M", "(가)"), - (0x320F, "M", "(나)"), - (0x3210, "M", "(다)"), - (0x3211, "M", "(라)"), - (0x3212, "M", "(마)"), - (0x3213, "M", "(바)"), - (0x3214, "M", "(사)"), - (0x3215, "M", "(아)"), - (0x3216, "M", "(자)"), - (0x3217, "M", "(차)"), - (0x3218, "M", "(카)"), - (0x3219, "M", "(타)"), - (0x321A, "M", "(파)"), - (0x321B, "M", "(하)"), - (0x321C, "M", "(주)"), - (0x321D, "M", "(오전)"), - (0x321E, "M", "(오후)"), - (0x321F, "X"), - (0x3220, "M", "(一)"), - (0x3221, "M", "(二)"), - (0x3222, "M", "(三)"), - (0x3223, "M", "(四)"), - (0x3224, "M", "(五)"), - (0x3225, "M", "(六)"), - (0x3226, "M", "(七)"), - (0x3227, "M", "(八)"), - (0x3228, "M", "(九)"), - (0x3229, "M", "(十)"), - ] - - -def _seg_31() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x322A, "M", "(月)"), - (0x322B, "M", "(火)"), - (0x322C, "M", "(水)"), - (0x322D, "M", "(木)"), - (0x322E, "M", "(金)"), - (0x322F, "M", "(土)"), - (0x3230, "M", "(日)"), - (0x3231, "M", "(株)"), - (0x3232, "M", "(有)"), - (0x3233, "M", "(社)"), - (0x3234, "M", "(名)"), - (0x3235, "M", "(特)"), - (0x3236, "M", "(財)"), - (0x3237, "M", "(祝)"), - (0x3238, "M", "(労)"), - (0x3239, "M", "(代)"), - (0x323A, "M", "(呼)"), - (0x323B, "M", "(学)"), - (0x323C, "M", "(監)"), - (0x323D, "M", "(企)"), - (0x323E, "M", "(資)"), - (0x323F, "M", "(協)"), - (0x3240, "M", "(祭)"), - (0x3241, "M", "(休)"), - (0x3242, "M", "(自)"), - (0x3243, "M", "(至)"), - (0x3244, "M", "問"), - (0x3245, "M", "幼"), - (0x3246, "M", "文"), - (0x3247, "M", "箏"), - (0x3248, "V"), - (0x3250, "M", "pte"), - (0x3251, "M", "21"), - (0x3252, "M", "22"), - (0x3253, "M", "23"), - (0x3254, "M", "24"), - (0x3255, "M", "25"), - (0x3256, "M", "26"), - (0x3257, "M", "27"), - (0x3258, "M", "28"), - (0x3259, "M", "29"), - (0x325A, "M", "30"), - (0x325B, "M", "31"), - (0x325C, "M", "32"), - (0x325D, "M", "33"), - (0x325E, "M", "34"), - (0x325F, "M", "35"), - (0x3260, "M", "ᄀ"), - (0x3261, "M", "ᄂ"), - (0x3262, "M", "ᄃ"), - (0x3263, "M", "ᄅ"), - (0x3264, "M", "ᄆ"), - (0x3265, "M", "ᄇ"), - (0x3266, "M", "ᄉ"), - (0x3267, "M", "ᄋ"), - (0x3268, "M", "ᄌ"), - (0x3269, "M", "ᄎ"), - (0x326A, "M", "ᄏ"), - (0x326B, "M", "ᄐ"), - (0x326C, "M", "ᄑ"), - (0x326D, "M", "ᄒ"), - (0x326E, "M", "가"), - (0x326F, "M", "나"), - (0x3270, "M", "다"), - (0x3271, "M", "라"), - (0x3272, "M", "마"), - (0x3273, "M", "바"), - (0x3274, "M", "사"), - (0x3275, "M", "아"), - (0x3276, "M", "자"), - (0x3277, "M", "차"), - (0x3278, "M", "카"), - (0x3279, "M", "타"), - (0x327A, "M", "파"), - (0x327B, "M", "하"), - (0x327C, "M", "참고"), - (0x327D, "M", "주의"), - (0x327E, "M", "우"), - (0x327F, "V"), - (0x3280, "M", "一"), - (0x3281, "M", "二"), - (0x3282, "M", "三"), - (0x3283, "M", "四"), - (0x3284, "M", "五"), - (0x3285, "M", "六"), - (0x3286, "M", "七"), - (0x3287, "M", "八"), - (0x3288, "M", "九"), - (0x3289, "M", "十"), - (0x328A, "M", "月"), - (0x328B, "M", "火"), - (0x328C, "M", "水"), - (0x328D, "M", "木"), - (0x328E, "M", "金"), - (0x328F, "M", "土"), - (0x3290, "M", "日"), - (0x3291, "M", "株"), - (0x3292, "M", "有"), - (0x3293, "M", "社"), - (0x3294, "M", "名"), - ] - - -def _seg_32() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x3295, "M", "特"), - (0x3296, "M", "財"), - (0x3297, "M", "祝"), - (0x3298, "M", "労"), - (0x3299, "M", "秘"), - (0x329A, "M", "男"), - (0x329B, "M", "女"), - (0x329C, "M", "適"), - (0x329D, "M", "優"), - (0x329E, "M", "印"), - (0x329F, "M", "注"), - (0x32A0, "M", "項"), - (0x32A1, "M", "休"), - (0x32A2, "M", "写"), - (0x32A3, "M", "正"), - (0x32A4, "M", "上"), - (0x32A5, "M", "中"), - (0x32A6, "M", "下"), - (0x32A7, "M", "左"), - (0x32A8, "M", "右"), - (0x32A9, "M", "医"), - (0x32AA, "M", "宗"), - (0x32AB, "M", "学"), - (0x32AC, "M", "監"), - (0x32AD, "M", "企"), - (0x32AE, "M", "資"), - (0x32AF, "M", "協"), - (0x32B0, "M", "夜"), - (0x32B1, "M", "36"), - (0x32B2, "M", "37"), - (0x32B3, "M", "38"), - (0x32B4, "M", "39"), - (0x32B5, "M", "40"), - (0x32B6, "M", "41"), - (0x32B7, "M", "42"), - (0x32B8, "M", "43"), - (0x32B9, "M", "44"), - (0x32BA, "M", "45"), - (0x32BB, "M", "46"), - (0x32BC, "M", "47"), - (0x32BD, "M", "48"), - (0x32BE, "M", "49"), - (0x32BF, "M", "50"), - (0x32C0, "M", "1月"), - (0x32C1, "M", "2月"), - (0x32C2, "M", "3月"), - (0x32C3, "M", "4月"), - (0x32C4, "M", "5月"), - (0x32C5, "M", "6月"), - (0x32C6, "M", "7月"), - (0x32C7, "M", "8月"), - (0x32C8, "M", "9月"), - (0x32C9, "M", "10月"), - (0x32CA, "M", "11月"), - (0x32CB, "M", "12月"), - (0x32CC, "M", "hg"), - (0x32CD, "M", "erg"), - (0x32CE, "M", "ev"), - (0x32CF, "M", "ltd"), - (0x32D0, "M", "ア"), - (0x32D1, "M", "イ"), - (0x32D2, "M", "ウ"), - (0x32D3, "M", "エ"), - (0x32D4, "M", "オ"), - (0x32D5, "M", "カ"), - (0x32D6, "M", "キ"), - (0x32D7, "M", "ク"), - (0x32D8, "M", "ケ"), - (0x32D9, "M", "コ"), - (0x32DA, "M", "サ"), - (0x32DB, "M", "シ"), - (0x32DC, "M", "ス"), - (0x32DD, "M", "セ"), - (0x32DE, "M", "ソ"), - (0x32DF, "M", "タ"), - (0x32E0, "M", "チ"), - (0x32E1, "M", "ツ"), - (0x32E2, "M", "テ"), - (0x32E3, "M", "ト"), - (0x32E4, "M", "ナ"), - (0x32E5, "M", "ニ"), - (0x32E6, "M", "ヌ"), - (0x32E7, "M", "ネ"), - (0x32E8, "M", "ノ"), - (0x32E9, "M", "ハ"), - (0x32EA, "M", "ヒ"), - (0x32EB, "M", "フ"), - (0x32EC, "M", "ヘ"), - (0x32ED, "M", "ホ"), - (0x32EE, "M", "マ"), - (0x32EF, "M", "ミ"), - (0x32F0, "M", "ム"), - (0x32F1, "M", "メ"), - (0x32F2, "M", "モ"), - (0x32F3, "M", "ヤ"), - (0x32F4, "M", "ユ"), - (0x32F5, "M", "ヨ"), - (0x32F6, "M", "ラ"), - (0x32F7, "M", "リ"), - (0x32F8, "M", "ル"), - ] - - -def _seg_33() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x32F9, "M", "レ"), - (0x32FA, "M", "ロ"), - (0x32FB, "M", "ワ"), - (0x32FC, "M", "ヰ"), - (0x32FD, "M", "ヱ"), - (0x32FE, "M", "ヲ"), - (0x32FF, "M", "令和"), - (0x3300, "M", "アパート"), - (0x3301, "M", "アルファ"), - (0x3302, "M", "アンペア"), - (0x3303, "M", "アール"), - (0x3304, "M", "イニング"), - (0x3305, "M", "インチ"), - (0x3306, "M", "ウォン"), - (0x3307, "M", "エスクード"), - (0x3308, "M", "エーカー"), - (0x3309, "M", "オンス"), - (0x330A, "M", "オーム"), - (0x330B, "M", "カイリ"), - (0x330C, "M", "カラット"), - (0x330D, "M", "カロリー"), - (0x330E, "M", "ガロン"), - (0x330F, "M", "ガンマ"), - (0x3310, "M", "ギガ"), - (0x3311, "M", "ギニー"), - (0x3312, "M", "キュリー"), - (0x3313, "M", "ギルダー"), - (0x3314, "M", "キロ"), - (0x3315, "M", "キログラム"), - (0x3316, "M", "キロメートル"), - (0x3317, "M", "キロワット"), - (0x3318, "M", "グラム"), - (0x3319, "M", "グラムトン"), - (0x331A, "M", "クルゼイロ"), - (0x331B, "M", "クローネ"), - (0x331C, "M", "ケース"), - (0x331D, "M", "コルナ"), - (0x331E, "M", "コーポ"), - (0x331F, "M", "サイクル"), - (0x3320, "M", "サンチーム"), - (0x3321, "M", "シリング"), - (0x3322, "M", "センチ"), - (0x3323, "M", "セント"), - (0x3324, "M", "ダース"), - (0x3325, "M", "デシ"), - (0x3326, "M", "ドル"), - (0x3327, "M", "トン"), - (0x3328, "M", "ナノ"), - (0x3329, "M", "ノット"), - (0x332A, "M", "ハイツ"), - (0x332B, "M", "パーセント"), - (0x332C, "M", "パーツ"), - (0x332D, "M", "バーレル"), - (0x332E, "M", "ピアストル"), - (0x332F, "M", "ピクル"), - (0x3330, "M", "ピコ"), - (0x3331, "M", "ビル"), - (0x3332, "M", "ファラッド"), - (0x3333, "M", "フィート"), - (0x3334, "M", "ブッシェル"), - (0x3335, "M", "フラン"), - (0x3336, "M", "ヘクタール"), - (0x3337, "M", "ペソ"), - (0x3338, "M", "ペニヒ"), - (0x3339, "M", "ヘルツ"), - (0x333A, "M", "ペンス"), - (0x333B, "M", "ページ"), - (0x333C, "M", "ベータ"), - (0x333D, "M", "ポイント"), - (0x333E, "M", "ボルト"), - (0x333F, "M", "ホン"), - (0x3340, "M", "ポンド"), - (0x3341, "M", "ホール"), - (0x3342, "M", "ホーン"), - (0x3343, "M", "マイクロ"), - (0x3344, "M", "マイル"), - (0x3345, "M", "マッハ"), - (0x3346, "M", "マルク"), - (0x3347, "M", "マンション"), - (0x3348, "M", "ミクロン"), - (0x3349, "M", "ミリ"), - (0x334A, "M", "ミリバール"), - (0x334B, "M", "メガ"), - (0x334C, "M", "メガトン"), - (0x334D, "M", "メートル"), - (0x334E, "M", "ヤード"), - (0x334F, "M", "ヤール"), - (0x3350, "M", "ユアン"), - (0x3351, "M", "リットル"), - (0x3352, "M", "リラ"), - (0x3353, "M", "ルピー"), - (0x3354, "M", "ルーブル"), - (0x3355, "M", "レム"), - (0x3356, "M", "レントゲン"), - (0x3357, "M", "ワット"), - (0x3358, "M", "0点"), - (0x3359, "M", "1点"), - (0x335A, "M", "2点"), - (0x335B, "M", "3点"), - (0x335C, "M", "4点"), - ] - - -def _seg_34() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x335D, "M", "5点"), - (0x335E, "M", "6点"), - (0x335F, "M", "7点"), - (0x3360, "M", "8点"), - (0x3361, "M", "9点"), - (0x3362, "M", "10点"), - (0x3363, "M", "11点"), - (0x3364, "M", "12点"), - (0x3365, "M", "13点"), - (0x3366, "M", "14点"), - (0x3367, "M", "15点"), - (0x3368, "M", "16点"), - (0x3369, "M", "17点"), - (0x336A, "M", "18点"), - (0x336B, "M", "19点"), - (0x336C, "M", "20点"), - (0x336D, "M", "21点"), - (0x336E, "M", "22点"), - (0x336F, "M", "23点"), - (0x3370, "M", "24点"), - (0x3371, "M", "hpa"), - (0x3372, "M", "da"), - (0x3373, "M", "au"), - (0x3374, "M", "bar"), - (0x3375, "M", "ov"), - (0x3376, "M", "pc"), - (0x3377, "M", "dm"), - (0x3378, "M", "dm2"), - (0x3379, "M", "dm3"), - (0x337A, "M", "iu"), - (0x337B, "M", "平成"), - (0x337C, "M", "昭和"), - (0x337D, "M", "大正"), - (0x337E, "M", "明治"), - (0x337F, "M", "株式会社"), - (0x3380, "M", "pa"), - (0x3381, "M", "na"), - (0x3382, "M", "μa"), - (0x3383, "M", "ma"), - (0x3384, "M", "ka"), - (0x3385, "M", "kb"), - (0x3386, "M", "mb"), - (0x3387, "M", "gb"), - (0x3388, "M", "cal"), - (0x3389, "M", "kcal"), - (0x338A, "M", "pf"), - (0x338B, "M", "nf"), - (0x338C, "M", "μf"), - (0x338D, "M", "μg"), - (0x338E, "M", "mg"), - (0x338F, "M", "kg"), - (0x3390, "M", "hz"), - (0x3391, "M", "khz"), - (0x3392, "M", "mhz"), - (0x3393, "M", "ghz"), - (0x3394, "M", "thz"), - (0x3395, "M", "μl"), - (0x3396, "M", "ml"), - (0x3397, "M", "dl"), - (0x3398, "M", "kl"), - (0x3399, "M", "fm"), - (0x339A, "M", "nm"), - (0x339B, "M", "μm"), - (0x339C, "M", "mm"), - (0x339D, "M", "cm"), - (0x339E, "M", "km"), - (0x339F, "M", "mm2"), - (0x33A0, "M", "cm2"), - (0x33A1, "M", "m2"), - (0x33A2, "M", "km2"), - (0x33A3, "M", "mm3"), - (0x33A4, "M", "cm3"), - (0x33A5, "M", "m3"), - (0x33A6, "M", "km3"), - (0x33A7, "M", "m∕s"), - (0x33A8, "M", "m∕s2"), - (0x33A9, "M", "pa"), - (0x33AA, "M", "kpa"), - (0x33AB, "M", "mpa"), - (0x33AC, "M", "gpa"), - (0x33AD, "M", "rad"), - (0x33AE, "M", "rad∕s"), - (0x33AF, "M", "rad∕s2"), - (0x33B0, "M", "ps"), - (0x33B1, "M", "ns"), - (0x33B2, "M", "μs"), - (0x33B3, "M", "ms"), - (0x33B4, "M", "pv"), - (0x33B5, "M", "nv"), - (0x33B6, "M", "μv"), - (0x33B7, "M", "mv"), - (0x33B8, "M", "kv"), - (0x33B9, "M", "mv"), - (0x33BA, "M", "pw"), - (0x33BB, "M", "nw"), - (0x33BC, "M", "μw"), - (0x33BD, "M", "mw"), - (0x33BE, "M", "kw"), - (0x33BF, "M", "mw"), - (0x33C0, "M", "kω"), - ] - - -def _seg_35() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x33C1, "M", "mω"), - (0x33C2, "X"), - (0x33C3, "M", "bq"), - (0x33C4, "M", "cc"), - (0x33C5, "M", "cd"), - (0x33C6, "M", "c∕kg"), - (0x33C7, "X"), - (0x33C8, "M", "db"), - (0x33C9, "M", "gy"), - (0x33CA, "M", "ha"), - (0x33CB, "M", "hp"), - (0x33CC, "M", "in"), - (0x33CD, "M", "kk"), - (0x33CE, "M", "km"), - (0x33CF, "M", "kt"), - (0x33D0, "M", "lm"), - (0x33D1, "M", "ln"), - (0x33D2, "M", "log"), - (0x33D3, "M", "lx"), - (0x33D4, "M", "mb"), - (0x33D5, "M", "mil"), - (0x33D6, "M", "mol"), - (0x33D7, "M", "ph"), - (0x33D8, "X"), - (0x33D9, "M", "ppm"), - (0x33DA, "M", "pr"), - (0x33DB, "M", "sr"), - (0x33DC, "M", "sv"), - (0x33DD, "M", "wb"), - (0x33DE, "M", "v∕m"), - (0x33DF, "M", "a∕m"), - (0x33E0, "M", "1日"), - (0x33E1, "M", "2日"), - (0x33E2, "M", "3日"), - (0x33E3, "M", "4日"), - (0x33E4, "M", "5日"), - (0x33E5, "M", "6日"), - (0x33E6, "M", "7日"), - (0x33E7, "M", "8日"), - (0x33E8, "M", "9日"), - (0x33E9, "M", "10日"), - (0x33EA, "M", "11日"), - (0x33EB, "M", "12日"), - (0x33EC, "M", "13日"), - (0x33ED, "M", "14日"), - (0x33EE, "M", "15日"), - (0x33EF, "M", "16日"), - (0x33F0, "M", "17日"), - (0x33F1, "M", "18日"), - (0x33F2, "M", "19日"), - (0x33F3, "M", "20日"), - (0x33F4, "M", "21日"), - (0x33F5, "M", "22日"), - (0x33F6, "M", "23日"), - (0x33F7, "M", "24日"), - (0x33F8, "M", "25日"), - (0x33F9, "M", "26日"), - (0x33FA, "M", "27日"), - (0x33FB, "M", "28日"), - (0x33FC, "M", "29日"), - (0x33FD, "M", "30日"), - (0x33FE, "M", "31日"), - (0x33FF, "M", "gal"), - (0x3400, "V"), - (0xA48D, "X"), - (0xA490, "V"), - (0xA4C7, "X"), - (0xA4D0, "V"), - (0xA62C, "X"), - (0xA640, "M", "ꙁ"), - (0xA641, "V"), - (0xA642, "M", "ꙃ"), - (0xA643, "V"), - (0xA644, "M", "ꙅ"), - (0xA645, "V"), - (0xA646, "M", "ꙇ"), - (0xA647, "V"), - (0xA648, "M", "ꙉ"), - (0xA649, "V"), - (0xA64A, "M", "ꙋ"), - (0xA64B, "V"), - (0xA64C, "M", "ꙍ"), - (0xA64D, "V"), - (0xA64E, "M", "ꙏ"), - (0xA64F, "V"), - (0xA650, "M", "ꙑ"), - (0xA651, "V"), - (0xA652, "M", "ꙓ"), - (0xA653, "V"), - (0xA654, "M", "ꙕ"), - (0xA655, "V"), - (0xA656, "M", "ꙗ"), - (0xA657, "V"), - (0xA658, "M", "ꙙ"), - (0xA659, "V"), - (0xA65A, "M", "ꙛ"), - (0xA65B, "V"), - (0xA65C, "M", "ꙝ"), - (0xA65D, "V"), - (0xA65E, "M", "ꙟ"), - ] - - -def _seg_36() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xA65F, "V"), - (0xA660, "M", "ꙡ"), - (0xA661, "V"), - (0xA662, "M", "ꙣ"), - (0xA663, "V"), - (0xA664, "M", "ꙥ"), - (0xA665, "V"), - (0xA666, "M", "ꙧ"), - (0xA667, "V"), - (0xA668, "M", "ꙩ"), - (0xA669, "V"), - (0xA66A, "M", "ꙫ"), - (0xA66B, "V"), - (0xA66C, "M", "ꙭ"), - (0xA66D, "V"), - (0xA680, "M", "ꚁ"), - (0xA681, "V"), - (0xA682, "M", "ꚃ"), - (0xA683, "V"), - (0xA684, "M", "ꚅ"), - (0xA685, "V"), - (0xA686, "M", "ꚇ"), - (0xA687, "V"), - (0xA688, "M", "ꚉ"), - (0xA689, "V"), - (0xA68A, "M", "ꚋ"), - (0xA68B, "V"), - (0xA68C, "M", "ꚍ"), - (0xA68D, "V"), - (0xA68E, "M", "ꚏ"), - (0xA68F, "V"), - (0xA690, "M", "ꚑ"), - (0xA691, "V"), - (0xA692, "M", "ꚓ"), - (0xA693, "V"), - (0xA694, "M", "ꚕ"), - (0xA695, "V"), - (0xA696, "M", "ꚗ"), - (0xA697, "V"), - (0xA698, "M", "ꚙ"), - (0xA699, "V"), - (0xA69A, "M", "ꚛ"), - (0xA69B, "V"), - (0xA69C, "M", "ъ"), - (0xA69D, "M", "ь"), - (0xA69E, "V"), - (0xA6F8, "X"), - (0xA700, "V"), - (0xA722, "M", "ꜣ"), - (0xA723, "V"), - (0xA724, "M", "ꜥ"), - (0xA725, "V"), - (0xA726, "M", "ꜧ"), - (0xA727, "V"), - (0xA728, "M", "ꜩ"), - (0xA729, "V"), - (0xA72A, "M", "ꜫ"), - (0xA72B, "V"), - (0xA72C, "M", "ꜭ"), - (0xA72D, "V"), - (0xA72E, "M", "ꜯ"), - (0xA72F, "V"), - (0xA732, "M", "ꜳ"), - (0xA733, "V"), - (0xA734, "M", "ꜵ"), - (0xA735, "V"), - (0xA736, "M", "ꜷ"), - (0xA737, "V"), - (0xA738, "M", "ꜹ"), - (0xA739, "V"), - (0xA73A, "M", "ꜻ"), - (0xA73B, "V"), - (0xA73C, "M", "ꜽ"), - (0xA73D, "V"), - (0xA73E, "M", "ꜿ"), - (0xA73F, "V"), - (0xA740, "M", "ꝁ"), - (0xA741, "V"), - (0xA742, "M", "ꝃ"), - (0xA743, "V"), - (0xA744, "M", "ꝅ"), - (0xA745, "V"), - (0xA746, "M", "ꝇ"), - (0xA747, "V"), - (0xA748, "M", "ꝉ"), - (0xA749, "V"), - (0xA74A, "M", "ꝋ"), - (0xA74B, "V"), - (0xA74C, "M", "ꝍ"), - (0xA74D, "V"), - (0xA74E, "M", "ꝏ"), - (0xA74F, "V"), - (0xA750, "M", "ꝑ"), - (0xA751, "V"), - (0xA752, "M", "ꝓ"), - (0xA753, "V"), - (0xA754, "M", "ꝕ"), - (0xA755, "V"), - (0xA756, "M", "ꝗ"), - (0xA757, "V"), - ] - - -def _seg_37() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xA758, "M", "ꝙ"), - (0xA759, "V"), - (0xA75A, "M", "ꝛ"), - (0xA75B, "V"), - (0xA75C, "M", "ꝝ"), - (0xA75D, "V"), - (0xA75E, "M", "ꝟ"), - (0xA75F, "V"), - (0xA760, "M", "ꝡ"), - (0xA761, "V"), - (0xA762, "M", "ꝣ"), - (0xA763, "V"), - (0xA764, "M", "ꝥ"), - (0xA765, "V"), - (0xA766, "M", "ꝧ"), - (0xA767, "V"), - (0xA768, "M", "ꝩ"), - (0xA769, "V"), - (0xA76A, "M", "ꝫ"), - (0xA76B, "V"), - (0xA76C, "M", "ꝭ"), - (0xA76D, "V"), - (0xA76E, "M", "ꝯ"), - (0xA76F, "V"), - (0xA770, "M", "ꝯ"), - (0xA771, "V"), - (0xA779, "M", "ꝺ"), - (0xA77A, "V"), - (0xA77B, "M", "ꝼ"), - (0xA77C, "V"), - (0xA77D, "M", "ᵹ"), - (0xA77E, "M", "ꝿ"), - (0xA77F, "V"), - (0xA780, "M", "ꞁ"), - (0xA781, "V"), - (0xA782, "M", "ꞃ"), - (0xA783, "V"), - (0xA784, "M", "ꞅ"), - (0xA785, "V"), - (0xA786, "M", "ꞇ"), - (0xA787, "V"), - (0xA78B, "M", "ꞌ"), - (0xA78C, "V"), - (0xA78D, "M", "ɥ"), - (0xA78E, "V"), - (0xA790, "M", "ꞑ"), - (0xA791, "V"), - (0xA792, "M", "ꞓ"), - (0xA793, "V"), - (0xA796, "M", "ꞗ"), - (0xA797, "V"), - (0xA798, "M", "ꞙ"), - (0xA799, "V"), - (0xA79A, "M", "ꞛ"), - (0xA79B, "V"), - (0xA79C, "M", "ꞝ"), - (0xA79D, "V"), - (0xA79E, "M", "ꞟ"), - (0xA79F, "V"), - (0xA7A0, "M", "ꞡ"), - (0xA7A1, "V"), - (0xA7A2, "M", "ꞣ"), - (0xA7A3, "V"), - (0xA7A4, "M", "ꞥ"), - (0xA7A5, "V"), - (0xA7A6, "M", "ꞧ"), - (0xA7A7, "V"), - (0xA7A8, "M", "ꞩ"), - (0xA7A9, "V"), - (0xA7AA, "M", "ɦ"), - (0xA7AB, "M", "ɜ"), - (0xA7AC, "M", "ɡ"), - (0xA7AD, "M", "ɬ"), - (0xA7AE, "M", "ɪ"), - (0xA7AF, "V"), - (0xA7B0, "M", "ʞ"), - (0xA7B1, "M", "ʇ"), - (0xA7B2, "M", "ʝ"), - (0xA7B3, "M", "ꭓ"), - (0xA7B4, "M", "ꞵ"), - (0xA7B5, "V"), - (0xA7B6, "M", "ꞷ"), - (0xA7B7, "V"), - (0xA7B8, "M", "ꞹ"), - (0xA7B9, "V"), - (0xA7BA, "M", "ꞻ"), - (0xA7BB, "V"), - (0xA7BC, "M", "ꞽ"), - (0xA7BD, "V"), - (0xA7BE, "M", "ꞿ"), - (0xA7BF, "V"), - (0xA7C0, "M", "ꟁ"), - (0xA7C1, "V"), - (0xA7C2, "M", "ꟃ"), - (0xA7C3, "V"), - (0xA7C4, "M", "ꞔ"), - (0xA7C5, "M", "ʂ"), - (0xA7C6, "M", "ᶎ"), - (0xA7C7, "M", "ꟈ"), - (0xA7C8, "V"), - ] - - -def _seg_38() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xA7C9, "M", "ꟊ"), - (0xA7CA, "V"), - (0xA7CB, "M", "ɤ"), - (0xA7CC, "M", "ꟍ"), - (0xA7CD, "V"), - (0xA7CE, "X"), - (0xA7D0, "M", "ꟑ"), - (0xA7D1, "V"), - (0xA7D2, "X"), - (0xA7D3, "V"), - (0xA7D4, "X"), - (0xA7D5, "V"), - (0xA7D6, "M", "ꟗ"), - (0xA7D7, "V"), - (0xA7D8, "M", "ꟙ"), - (0xA7D9, "V"), - (0xA7DA, "M", "ꟛ"), - (0xA7DB, "V"), - (0xA7DC, "M", "ƛ"), - (0xA7DD, "X"), - (0xA7F2, "M", "c"), - (0xA7F3, "M", "f"), - (0xA7F4, "M", "q"), - (0xA7F5, "M", "ꟶ"), - (0xA7F6, "V"), - (0xA7F8, "M", "ħ"), - (0xA7F9, "M", "œ"), - (0xA7FA, "V"), - (0xA82D, "X"), - (0xA830, "V"), - (0xA83A, "X"), - (0xA840, "V"), - (0xA878, "X"), - (0xA880, "V"), - (0xA8C6, "X"), - (0xA8CE, "V"), - (0xA8DA, "X"), - (0xA8E0, "V"), - (0xA954, "X"), - (0xA95F, "V"), - (0xA97D, "X"), - (0xA980, "V"), - (0xA9CE, "X"), - (0xA9CF, "V"), - (0xA9DA, "X"), - (0xA9DE, "V"), - (0xA9FF, "X"), - (0xAA00, "V"), - (0xAA37, "X"), - (0xAA40, "V"), - (0xAA4E, "X"), - (0xAA50, "V"), - (0xAA5A, "X"), - (0xAA5C, "V"), - (0xAAC3, "X"), - (0xAADB, "V"), - (0xAAF7, "X"), - (0xAB01, "V"), - (0xAB07, "X"), - (0xAB09, "V"), - (0xAB0F, "X"), - (0xAB11, "V"), - (0xAB17, "X"), - (0xAB20, "V"), - (0xAB27, "X"), - (0xAB28, "V"), - (0xAB2F, "X"), - (0xAB30, "V"), - (0xAB5C, "M", "ꜧ"), - (0xAB5D, "M", "ꬷ"), - (0xAB5E, "M", "ɫ"), - (0xAB5F, "M", "ꭒ"), - (0xAB60, "V"), - (0xAB69, "M", "ʍ"), - (0xAB6A, "V"), - (0xAB6C, "X"), - (0xAB70, "M", "Ꭰ"), - (0xAB71, "M", "Ꭱ"), - (0xAB72, "M", "Ꭲ"), - (0xAB73, "M", "Ꭳ"), - (0xAB74, "M", "Ꭴ"), - (0xAB75, "M", "Ꭵ"), - (0xAB76, "M", "Ꭶ"), - (0xAB77, "M", "Ꭷ"), - (0xAB78, "M", "Ꭸ"), - (0xAB79, "M", "Ꭹ"), - (0xAB7A, "M", "Ꭺ"), - (0xAB7B, "M", "Ꭻ"), - (0xAB7C, "M", "Ꭼ"), - (0xAB7D, "M", "Ꭽ"), - (0xAB7E, "M", "Ꭾ"), - (0xAB7F, "M", "Ꭿ"), - (0xAB80, "M", "Ꮀ"), - (0xAB81, "M", "Ꮁ"), - (0xAB82, "M", "Ꮂ"), - (0xAB83, "M", "Ꮃ"), - (0xAB84, "M", "Ꮄ"), - (0xAB85, "M", "Ꮅ"), - (0xAB86, "M", "Ꮆ"), - (0xAB87, "M", "Ꮇ"), - ] - - -def _seg_39() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xAB88, "M", "Ꮈ"), - (0xAB89, "M", "Ꮉ"), - (0xAB8A, "M", "Ꮊ"), - (0xAB8B, "M", "Ꮋ"), - (0xAB8C, "M", "Ꮌ"), - (0xAB8D, "M", "Ꮍ"), - (0xAB8E, "M", "Ꮎ"), - (0xAB8F, "M", "Ꮏ"), - (0xAB90, "M", "Ꮐ"), - (0xAB91, "M", "Ꮑ"), - (0xAB92, "M", "Ꮒ"), - (0xAB93, "M", "Ꮓ"), - (0xAB94, "M", "Ꮔ"), - (0xAB95, "M", "Ꮕ"), - (0xAB96, "M", "Ꮖ"), - (0xAB97, "M", "Ꮗ"), - (0xAB98, "M", "Ꮘ"), - (0xAB99, "M", "Ꮙ"), - (0xAB9A, "M", "Ꮚ"), - (0xAB9B, "M", "Ꮛ"), - (0xAB9C, "M", "Ꮜ"), - (0xAB9D, "M", "Ꮝ"), - (0xAB9E, "M", "Ꮞ"), - (0xAB9F, "M", "Ꮟ"), - (0xABA0, "M", "Ꮠ"), - (0xABA1, "M", "Ꮡ"), - (0xABA2, "M", "Ꮢ"), - (0xABA3, "M", "Ꮣ"), - (0xABA4, "M", "Ꮤ"), - (0xABA5, "M", "Ꮥ"), - (0xABA6, "M", "Ꮦ"), - (0xABA7, "M", "Ꮧ"), - (0xABA8, "M", "Ꮨ"), - (0xABA9, "M", "Ꮩ"), - (0xABAA, "M", "Ꮪ"), - (0xABAB, "M", "Ꮫ"), - (0xABAC, "M", "Ꮬ"), - (0xABAD, "M", "Ꮭ"), - (0xABAE, "M", "Ꮮ"), - (0xABAF, "M", "Ꮯ"), - (0xABB0, "M", "Ꮰ"), - (0xABB1, "M", "Ꮱ"), - (0xABB2, "M", "Ꮲ"), - (0xABB3, "M", "Ꮳ"), - (0xABB4, "M", "Ꮴ"), - (0xABB5, "M", "Ꮵ"), - (0xABB6, "M", "Ꮶ"), - (0xABB7, "M", "Ꮷ"), - (0xABB8, "M", "Ꮸ"), - (0xABB9, "M", "Ꮹ"), - (0xABBA, "M", "Ꮺ"), - (0xABBB, "M", "Ꮻ"), - (0xABBC, "M", "Ꮼ"), - (0xABBD, "M", "Ꮽ"), - (0xABBE, "M", "Ꮾ"), - (0xABBF, "M", "Ꮿ"), - (0xABC0, "V"), - (0xABEE, "X"), - (0xABF0, "V"), - (0xABFA, "X"), - (0xAC00, "V"), - (0xD7A4, "X"), - (0xD7B0, "V"), - (0xD7C7, "X"), - (0xD7CB, "V"), - (0xD7FC, "X"), - (0xF900, "M", "豈"), - (0xF901, "M", "更"), - (0xF902, "M", "車"), - (0xF903, "M", "賈"), - (0xF904, "M", "滑"), - (0xF905, "M", "串"), - (0xF906, "M", "句"), - (0xF907, "M", "龜"), - (0xF909, "M", "契"), - (0xF90A, "M", "金"), - (0xF90B, "M", "喇"), - (0xF90C, "M", "奈"), - (0xF90D, "M", "懶"), - (0xF90E, "M", "癩"), - (0xF90F, "M", "羅"), - (0xF910, "M", "蘿"), - (0xF911, "M", "螺"), - (0xF912, "M", "裸"), - (0xF913, "M", "邏"), - (0xF914, "M", "樂"), - (0xF915, "M", "洛"), - (0xF916, "M", "烙"), - (0xF917, "M", "珞"), - (0xF918, "M", "落"), - (0xF919, "M", "酪"), - (0xF91A, "M", "駱"), - (0xF91B, "M", "亂"), - (0xF91C, "M", "卵"), - (0xF91D, "M", "欄"), - (0xF91E, "M", "爛"), - (0xF91F, "M", "蘭"), - (0xF920, "M", "鸞"), - (0xF921, "M", "嵐"), - (0xF922, "M", "濫"), - ] - - -def _seg_40() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xF923, "M", "藍"), - (0xF924, "M", "襤"), - (0xF925, "M", "拉"), - (0xF926, "M", "臘"), - (0xF927, "M", "蠟"), - (0xF928, "M", "廊"), - (0xF929, "M", "朗"), - (0xF92A, "M", "浪"), - (0xF92B, "M", "狼"), - (0xF92C, "M", "郎"), - (0xF92D, "M", "來"), - (0xF92E, "M", "冷"), - (0xF92F, "M", "勞"), - (0xF930, "M", "擄"), - (0xF931, "M", "櫓"), - (0xF932, "M", "爐"), - (0xF933, "M", "盧"), - (0xF934, "M", "老"), - (0xF935, "M", "蘆"), - (0xF936, "M", "虜"), - (0xF937, "M", "路"), - (0xF938, "M", "露"), - (0xF939, "M", "魯"), - (0xF93A, "M", "鷺"), - (0xF93B, "M", "碌"), - (0xF93C, "M", "祿"), - (0xF93D, "M", "綠"), - (0xF93E, "M", "菉"), - (0xF93F, "M", "錄"), - (0xF940, "M", "鹿"), - (0xF941, "M", "論"), - (0xF942, "M", "壟"), - (0xF943, "M", "弄"), - (0xF944, "M", "籠"), - (0xF945, "M", "聾"), - (0xF946, "M", "牢"), - (0xF947, "M", "磊"), - (0xF948, "M", "賂"), - (0xF949, "M", "雷"), - (0xF94A, "M", "壘"), - (0xF94B, "M", "屢"), - (0xF94C, "M", "樓"), - (0xF94D, "M", "淚"), - (0xF94E, "M", "漏"), - (0xF94F, "M", "累"), - (0xF950, "M", "縷"), - (0xF951, "M", "陋"), - (0xF952, "M", "勒"), - (0xF953, "M", "肋"), - (0xF954, "M", "凜"), - (0xF955, "M", "凌"), - (0xF956, "M", "稜"), - (0xF957, "M", "綾"), - (0xF958, "M", "菱"), - (0xF959, "M", "陵"), - (0xF95A, "M", "讀"), - (0xF95B, "M", "拏"), - (0xF95C, "M", "樂"), - (0xF95D, "M", "諾"), - (0xF95E, "M", "丹"), - (0xF95F, "M", "寧"), - (0xF960, "M", "怒"), - (0xF961, "M", "率"), - (0xF962, "M", "異"), - (0xF963, "M", "北"), - (0xF964, "M", "磻"), - (0xF965, "M", "便"), - (0xF966, "M", "復"), - (0xF967, "M", "不"), - (0xF968, "M", "泌"), - (0xF969, "M", "數"), - (0xF96A, "M", "索"), - (0xF96B, "M", "參"), - (0xF96C, "M", "塞"), - (0xF96D, "M", "省"), - (0xF96E, "M", "葉"), - (0xF96F, "M", "說"), - (0xF970, "M", "殺"), - (0xF971, "M", "辰"), - (0xF972, "M", "沈"), - (0xF973, "M", "拾"), - (0xF974, "M", "若"), - (0xF975, "M", "掠"), - (0xF976, "M", "略"), - (0xF977, "M", "亮"), - (0xF978, "M", "兩"), - (0xF979, "M", "凉"), - (0xF97A, "M", "梁"), - (0xF97B, "M", "糧"), - (0xF97C, "M", "良"), - (0xF97D, "M", "諒"), - (0xF97E, "M", "量"), - (0xF97F, "M", "勵"), - (0xF980, "M", "呂"), - (0xF981, "M", "女"), - (0xF982, "M", "廬"), - (0xF983, "M", "旅"), - (0xF984, "M", "濾"), - (0xF985, "M", "礪"), - (0xF986, "M", "閭"), - ] - - -def _seg_41() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xF987, "M", "驪"), - (0xF988, "M", "麗"), - (0xF989, "M", "黎"), - (0xF98A, "M", "力"), - (0xF98B, "M", "曆"), - (0xF98C, "M", "歷"), - (0xF98D, "M", "轢"), - (0xF98E, "M", "年"), - (0xF98F, "M", "憐"), - (0xF990, "M", "戀"), - (0xF991, "M", "撚"), - (0xF992, "M", "漣"), - (0xF993, "M", "煉"), - (0xF994, "M", "璉"), - (0xF995, "M", "秊"), - (0xF996, "M", "練"), - (0xF997, "M", "聯"), - (0xF998, "M", "輦"), - (0xF999, "M", "蓮"), - (0xF99A, "M", "連"), - (0xF99B, "M", "鍊"), - (0xF99C, "M", "列"), - (0xF99D, "M", "劣"), - (0xF99E, "M", "咽"), - (0xF99F, "M", "烈"), - (0xF9A0, "M", "裂"), - (0xF9A1, "M", "說"), - (0xF9A2, "M", "廉"), - (0xF9A3, "M", "念"), - (0xF9A4, "M", "捻"), - (0xF9A5, "M", "殮"), - (0xF9A6, "M", "簾"), - (0xF9A7, "M", "獵"), - (0xF9A8, "M", "令"), - (0xF9A9, "M", "囹"), - (0xF9AA, "M", "寧"), - (0xF9AB, "M", "嶺"), - (0xF9AC, "M", "怜"), - (0xF9AD, "M", "玲"), - (0xF9AE, "M", "瑩"), - (0xF9AF, "M", "羚"), - (0xF9B0, "M", "聆"), - (0xF9B1, "M", "鈴"), - (0xF9B2, "M", "零"), - (0xF9B3, "M", "靈"), - (0xF9B4, "M", "領"), - (0xF9B5, "M", "例"), - (0xF9B6, "M", "禮"), - (0xF9B7, "M", "醴"), - (0xF9B8, "M", "隸"), - (0xF9B9, "M", "惡"), - (0xF9BA, "M", "了"), - (0xF9BB, "M", "僚"), - (0xF9BC, "M", "寮"), - (0xF9BD, "M", "尿"), - (0xF9BE, "M", "料"), - (0xF9BF, "M", "樂"), - (0xF9C0, "M", "燎"), - (0xF9C1, "M", "療"), - (0xF9C2, "M", "蓼"), - (0xF9C3, "M", "遼"), - (0xF9C4, "M", "龍"), - (0xF9C5, "M", "暈"), - (0xF9C6, "M", "阮"), - (0xF9C7, "M", "劉"), - (0xF9C8, "M", "杻"), - (0xF9C9, "M", "柳"), - (0xF9CA, "M", "流"), - (0xF9CB, "M", "溜"), - (0xF9CC, "M", "琉"), - (0xF9CD, "M", "留"), - (0xF9CE, "M", "硫"), - (0xF9CF, "M", "紐"), - (0xF9D0, "M", "類"), - (0xF9D1, "M", "六"), - (0xF9D2, "M", "戮"), - (0xF9D3, "M", "陸"), - (0xF9D4, "M", "倫"), - (0xF9D5, "M", "崙"), - (0xF9D6, "M", "淪"), - (0xF9D7, "M", "輪"), - (0xF9D8, "M", "律"), - (0xF9D9, "M", "慄"), - (0xF9DA, "M", "栗"), - (0xF9DB, "M", "率"), - (0xF9DC, "M", "隆"), - (0xF9DD, "M", "利"), - (0xF9DE, "M", "吏"), - (0xF9DF, "M", "履"), - (0xF9E0, "M", "易"), - (0xF9E1, "M", "李"), - (0xF9E2, "M", "梨"), - (0xF9E3, "M", "泥"), - (0xF9E4, "M", "理"), - (0xF9E5, "M", "痢"), - (0xF9E6, "M", "罹"), - (0xF9E7, "M", "裏"), - (0xF9E8, "M", "裡"), - (0xF9E9, "M", "里"), - (0xF9EA, "M", "離"), - ] - - -def _seg_42() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xF9EB, "M", "匿"), - (0xF9EC, "M", "溺"), - (0xF9ED, "M", "吝"), - (0xF9EE, "M", "燐"), - (0xF9EF, "M", "璘"), - (0xF9F0, "M", "藺"), - (0xF9F1, "M", "隣"), - (0xF9F2, "M", "鱗"), - (0xF9F3, "M", "麟"), - (0xF9F4, "M", "林"), - (0xF9F5, "M", "淋"), - (0xF9F6, "M", "臨"), - (0xF9F7, "M", "立"), - (0xF9F8, "M", "笠"), - (0xF9F9, "M", "粒"), - (0xF9FA, "M", "狀"), - (0xF9FB, "M", "炙"), - (0xF9FC, "M", "識"), - (0xF9FD, "M", "什"), - (0xF9FE, "M", "茶"), - (0xF9FF, "M", "刺"), - (0xFA00, "M", "切"), - (0xFA01, "M", "度"), - (0xFA02, "M", "拓"), - (0xFA03, "M", "糖"), - (0xFA04, "M", "宅"), - (0xFA05, "M", "洞"), - (0xFA06, "M", "暴"), - (0xFA07, "M", "輻"), - (0xFA08, "M", "行"), - (0xFA09, "M", "降"), - (0xFA0A, "M", "見"), - (0xFA0B, "M", "廓"), - (0xFA0C, "M", "兀"), - (0xFA0D, "M", "嗀"), - (0xFA0E, "V"), - (0xFA10, "M", "塚"), - (0xFA11, "V"), - (0xFA12, "M", "晴"), - (0xFA13, "V"), - (0xFA15, "M", "凞"), - (0xFA16, "M", "猪"), - (0xFA17, "M", "益"), - (0xFA18, "M", "礼"), - (0xFA19, "M", "神"), - (0xFA1A, "M", "祥"), - (0xFA1B, "M", "福"), - (0xFA1C, "M", "靖"), - (0xFA1D, "M", "精"), - (0xFA1E, "M", "羽"), - (0xFA1F, "V"), - (0xFA20, "M", "蘒"), - (0xFA21, "V"), - (0xFA22, "M", "諸"), - (0xFA23, "V"), - (0xFA25, "M", "逸"), - (0xFA26, "M", "都"), - (0xFA27, "V"), - (0xFA2A, "M", "飯"), - (0xFA2B, "M", "飼"), - (0xFA2C, "M", "館"), - (0xFA2D, "M", "鶴"), - (0xFA2E, "M", "郞"), - (0xFA2F, "M", "隷"), - (0xFA30, "M", "侮"), - (0xFA31, "M", "僧"), - (0xFA32, "M", "免"), - (0xFA33, "M", "勉"), - (0xFA34, "M", "勤"), - (0xFA35, "M", "卑"), - (0xFA36, "M", "喝"), - (0xFA37, "M", "嘆"), - (0xFA38, "M", "器"), - (0xFA39, "M", "塀"), - (0xFA3A, "M", "墨"), - (0xFA3B, "M", "層"), - (0xFA3C, "M", "屮"), - (0xFA3D, "M", "悔"), - (0xFA3E, "M", "慨"), - (0xFA3F, "M", "憎"), - (0xFA40, "M", "懲"), - (0xFA41, "M", "敏"), - (0xFA42, "M", "既"), - (0xFA43, "M", "暑"), - (0xFA44, "M", "梅"), - (0xFA45, "M", "海"), - (0xFA46, "M", "渚"), - (0xFA47, "M", "漢"), - (0xFA48, "M", "煮"), - (0xFA49, "M", "爫"), - (0xFA4A, "M", "琢"), - (0xFA4B, "M", "碑"), - (0xFA4C, "M", "社"), - (0xFA4D, "M", "祉"), - (0xFA4E, "M", "祈"), - (0xFA4F, "M", "祐"), - (0xFA50, "M", "祖"), - (0xFA51, "M", "祝"), - (0xFA52, "M", "禍"), - (0xFA53, "M", "禎"), - ] - - -def _seg_43() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFA54, "M", "穀"), - (0xFA55, "M", "突"), - (0xFA56, "M", "節"), - (0xFA57, "M", "練"), - (0xFA58, "M", "縉"), - (0xFA59, "M", "繁"), - (0xFA5A, "M", "署"), - (0xFA5B, "M", "者"), - (0xFA5C, "M", "臭"), - (0xFA5D, "M", "艹"), - (0xFA5F, "M", "著"), - (0xFA60, "M", "褐"), - (0xFA61, "M", "視"), - (0xFA62, "M", "謁"), - (0xFA63, "M", "謹"), - (0xFA64, "M", "賓"), - (0xFA65, "M", "贈"), - (0xFA66, "M", "辶"), - (0xFA67, "M", "逸"), - (0xFA68, "M", "難"), - (0xFA69, "M", "響"), - (0xFA6A, "M", "頻"), - (0xFA6B, "M", "恵"), - (0xFA6C, "M", "𤋮"), - (0xFA6D, "M", "舘"), - (0xFA6E, "X"), - (0xFA70, "M", "並"), - (0xFA71, "M", "况"), - (0xFA72, "M", "全"), - (0xFA73, "M", "侀"), - (0xFA74, "M", "充"), - (0xFA75, "M", "冀"), - (0xFA76, "M", "勇"), - (0xFA77, "M", "勺"), - (0xFA78, "M", "喝"), - (0xFA79, "M", "啕"), - (0xFA7A, "M", "喙"), - (0xFA7B, "M", "嗢"), - (0xFA7C, "M", "塚"), - (0xFA7D, "M", "墳"), - (0xFA7E, "M", "奄"), - (0xFA7F, "M", "奔"), - (0xFA80, "M", "婢"), - (0xFA81, "M", "嬨"), - (0xFA82, "M", "廒"), - (0xFA83, "M", "廙"), - (0xFA84, "M", "彩"), - (0xFA85, "M", "徭"), - (0xFA86, "M", "惘"), - (0xFA87, "M", "慎"), - (0xFA88, "M", "愈"), - (0xFA89, "M", "憎"), - (0xFA8A, "M", "慠"), - (0xFA8B, "M", "懲"), - (0xFA8C, "M", "戴"), - (0xFA8D, "M", "揄"), - (0xFA8E, "M", "搜"), - (0xFA8F, "M", "摒"), - (0xFA90, "M", "敖"), - (0xFA91, "M", "晴"), - (0xFA92, "M", "朗"), - (0xFA93, "M", "望"), - (0xFA94, "M", "杖"), - (0xFA95, "M", "歹"), - (0xFA96, "M", "殺"), - (0xFA97, "M", "流"), - (0xFA98, "M", "滛"), - (0xFA99, "M", "滋"), - (0xFA9A, "M", "漢"), - (0xFA9B, "M", "瀞"), - (0xFA9C, "M", "煮"), - (0xFA9D, "M", "瞧"), - (0xFA9E, "M", "爵"), - (0xFA9F, "M", "犯"), - (0xFAA0, "M", "猪"), - (0xFAA1, "M", "瑱"), - (0xFAA2, "M", "甆"), - (0xFAA3, "M", "画"), - (0xFAA4, "M", "瘝"), - (0xFAA5, "M", "瘟"), - (0xFAA6, "M", "益"), - (0xFAA7, "M", "盛"), - (0xFAA8, "M", "直"), - (0xFAA9, "M", "睊"), - (0xFAAA, "M", "着"), - (0xFAAB, "M", "磌"), - (0xFAAC, "M", "窱"), - (0xFAAD, "M", "節"), - (0xFAAE, "M", "类"), - (0xFAAF, "M", "絛"), - (0xFAB0, "M", "練"), - (0xFAB1, "M", "缾"), - (0xFAB2, "M", "者"), - (0xFAB3, "M", "荒"), - (0xFAB4, "M", "華"), - (0xFAB5, "M", "蝹"), - (0xFAB6, "M", "襁"), - (0xFAB7, "M", "覆"), - (0xFAB8, "M", "視"), - (0xFAB9, "M", "調"), - ] - - -def _seg_44() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFABA, "M", "諸"), - (0xFABB, "M", "請"), - (0xFABC, "M", "謁"), - (0xFABD, "M", "諾"), - (0xFABE, "M", "諭"), - (0xFABF, "M", "謹"), - (0xFAC0, "M", "變"), - (0xFAC1, "M", "贈"), - (0xFAC2, "M", "輸"), - (0xFAC3, "M", "遲"), - (0xFAC4, "M", "醙"), - (0xFAC5, "M", "鉶"), - (0xFAC6, "M", "陼"), - (0xFAC7, "M", "難"), - (0xFAC8, "M", "靖"), - (0xFAC9, "M", "韛"), - (0xFACA, "M", "響"), - (0xFACB, "M", "頋"), - (0xFACC, "M", "頻"), - (0xFACD, "M", "鬒"), - (0xFACE, "M", "龜"), - (0xFACF, "M", "𢡊"), - (0xFAD0, "M", "𢡄"), - (0xFAD1, "M", "𣏕"), - (0xFAD2, "M", "㮝"), - (0xFAD3, "M", "䀘"), - (0xFAD4, "M", "䀹"), - (0xFAD5, "M", "𥉉"), - (0xFAD6, "M", "𥳐"), - (0xFAD7, "M", "𧻓"), - (0xFAD8, "M", "齃"), - (0xFAD9, "M", "龎"), - (0xFADA, "X"), - (0xFB00, "M", "ff"), - (0xFB01, "M", "fi"), - (0xFB02, "M", "fl"), - (0xFB03, "M", "ffi"), - (0xFB04, "M", "ffl"), - (0xFB05, "M", "st"), - (0xFB07, "X"), - (0xFB13, "M", "մն"), - (0xFB14, "M", "մե"), - (0xFB15, "M", "մի"), - (0xFB16, "M", "վն"), - (0xFB17, "M", "մխ"), - (0xFB18, "X"), - (0xFB1D, "M", "יִ"), - (0xFB1E, "V"), - (0xFB1F, "M", "ײַ"), - (0xFB20, "M", "ע"), - (0xFB21, "M", "א"), - (0xFB22, "M", "ד"), - (0xFB23, "M", "ה"), - (0xFB24, "M", "כ"), - (0xFB25, "M", "ל"), - (0xFB26, "M", "ם"), - (0xFB27, "M", "ר"), - (0xFB28, "M", "ת"), - (0xFB29, "M", "+"), - (0xFB2A, "M", "שׁ"), - (0xFB2B, "M", "שׂ"), - (0xFB2C, "M", "שּׁ"), - (0xFB2D, "M", "שּׂ"), - (0xFB2E, "M", "אַ"), - (0xFB2F, "M", "אָ"), - (0xFB30, "M", "אּ"), - (0xFB31, "M", "בּ"), - (0xFB32, "M", "גּ"), - (0xFB33, "M", "דּ"), - (0xFB34, "M", "הּ"), - (0xFB35, "M", "וּ"), - (0xFB36, "M", "זּ"), - (0xFB37, "X"), - (0xFB38, "M", "טּ"), - (0xFB39, "M", "יּ"), - (0xFB3A, "M", "ךּ"), - (0xFB3B, "M", "כּ"), - (0xFB3C, "M", "לּ"), - (0xFB3D, "X"), - (0xFB3E, "M", "מּ"), - (0xFB3F, "X"), - (0xFB40, "M", "נּ"), - (0xFB41, "M", "סּ"), - (0xFB42, "X"), - (0xFB43, "M", "ףּ"), - (0xFB44, "M", "פּ"), - (0xFB45, "X"), - (0xFB46, "M", "צּ"), - (0xFB47, "M", "קּ"), - (0xFB48, "M", "רּ"), - (0xFB49, "M", "שּ"), - (0xFB4A, "M", "תּ"), - (0xFB4B, "M", "וֹ"), - (0xFB4C, "M", "בֿ"), - (0xFB4D, "M", "כֿ"), - (0xFB4E, "M", "פֿ"), - (0xFB4F, "M", "אל"), - (0xFB50, "M", "ٱ"), - (0xFB52, "M", "ٻ"), - (0xFB56, "M", "پ"), - ] - - -def _seg_45() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFB5A, "M", "ڀ"), - (0xFB5E, "M", "ٺ"), - (0xFB62, "M", "ٿ"), - (0xFB66, "M", "ٹ"), - (0xFB6A, "M", "ڤ"), - (0xFB6E, "M", "ڦ"), - (0xFB72, "M", "ڄ"), - (0xFB76, "M", "ڃ"), - (0xFB7A, "M", "چ"), - (0xFB7E, "M", "ڇ"), - (0xFB82, "M", "ڍ"), - (0xFB84, "M", "ڌ"), - (0xFB86, "M", "ڎ"), - (0xFB88, "M", "ڈ"), - (0xFB8A, "M", "ژ"), - (0xFB8C, "M", "ڑ"), - (0xFB8E, "M", "ک"), - (0xFB92, "M", "گ"), - (0xFB96, "M", "ڳ"), - (0xFB9A, "M", "ڱ"), - (0xFB9E, "M", "ں"), - (0xFBA0, "M", "ڻ"), - (0xFBA4, "M", "ۀ"), - (0xFBA6, "M", "ہ"), - (0xFBAA, "M", "ھ"), - (0xFBAE, "M", "ے"), - (0xFBB0, "M", "ۓ"), - (0xFBB2, "V"), - (0xFBC3, "X"), - (0xFBD3, "M", "ڭ"), - (0xFBD7, "M", "ۇ"), - (0xFBD9, "M", "ۆ"), - (0xFBDB, "M", "ۈ"), - (0xFBDD, "M", "ۇٴ"), - (0xFBDE, "M", "ۋ"), - (0xFBE0, "M", "ۅ"), - (0xFBE2, "M", "ۉ"), - (0xFBE4, "M", "ې"), - (0xFBE8, "M", "ى"), - (0xFBEA, "M", "ئا"), - (0xFBEC, "M", "ئە"), - (0xFBEE, "M", "ئو"), - (0xFBF0, "M", "ئۇ"), - (0xFBF2, "M", "ئۆ"), - (0xFBF4, "M", "ئۈ"), - (0xFBF6, "M", "ئې"), - (0xFBF9, "M", "ئى"), - (0xFBFC, "M", "ی"), - (0xFC00, "M", "ئج"), - (0xFC01, "M", "ئح"), - (0xFC02, "M", "ئم"), - (0xFC03, "M", "ئى"), - (0xFC04, "M", "ئي"), - (0xFC05, "M", "بج"), - (0xFC06, "M", "بح"), - (0xFC07, "M", "بخ"), - (0xFC08, "M", "بم"), - (0xFC09, "M", "بى"), - (0xFC0A, "M", "بي"), - (0xFC0B, "M", "تج"), - (0xFC0C, "M", "تح"), - (0xFC0D, "M", "تخ"), - (0xFC0E, "M", "تم"), - (0xFC0F, "M", "تى"), - (0xFC10, "M", "تي"), - (0xFC11, "M", "ثج"), - (0xFC12, "M", "ثم"), - (0xFC13, "M", "ثى"), - (0xFC14, "M", "ثي"), - (0xFC15, "M", "جح"), - (0xFC16, "M", "جم"), - (0xFC17, "M", "حج"), - (0xFC18, "M", "حم"), - (0xFC19, "M", "خج"), - (0xFC1A, "M", "خح"), - (0xFC1B, "M", "خم"), - (0xFC1C, "M", "سج"), - (0xFC1D, "M", "سح"), - (0xFC1E, "M", "سخ"), - (0xFC1F, "M", "سم"), - (0xFC20, "M", "صح"), - (0xFC21, "M", "صم"), - (0xFC22, "M", "ضج"), - (0xFC23, "M", "ضح"), - (0xFC24, "M", "ضخ"), - (0xFC25, "M", "ضم"), - (0xFC26, "M", "طح"), - (0xFC27, "M", "طم"), - (0xFC28, "M", "ظم"), - (0xFC29, "M", "عج"), - (0xFC2A, "M", "عم"), - (0xFC2B, "M", "غج"), - (0xFC2C, "M", "غم"), - (0xFC2D, "M", "فج"), - (0xFC2E, "M", "فح"), - (0xFC2F, "M", "فخ"), - (0xFC30, "M", "فم"), - (0xFC31, "M", "فى"), - (0xFC32, "M", "في"), - (0xFC33, "M", "قح"), - ] - - -def _seg_46() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFC34, "M", "قم"), - (0xFC35, "M", "قى"), - (0xFC36, "M", "قي"), - (0xFC37, "M", "كا"), - (0xFC38, "M", "كج"), - (0xFC39, "M", "كح"), - (0xFC3A, "M", "كخ"), - (0xFC3B, "M", "كل"), - (0xFC3C, "M", "كم"), - (0xFC3D, "M", "كى"), - (0xFC3E, "M", "كي"), - (0xFC3F, "M", "لج"), - (0xFC40, "M", "لح"), - (0xFC41, "M", "لخ"), - (0xFC42, "M", "لم"), - (0xFC43, "M", "لى"), - (0xFC44, "M", "لي"), - (0xFC45, "M", "مج"), - (0xFC46, "M", "مح"), - (0xFC47, "M", "مخ"), - (0xFC48, "M", "مم"), - (0xFC49, "M", "مى"), - (0xFC4A, "M", "مي"), - (0xFC4B, "M", "نج"), - (0xFC4C, "M", "نح"), - (0xFC4D, "M", "نخ"), - (0xFC4E, "M", "نم"), - (0xFC4F, "M", "نى"), - (0xFC50, "M", "ني"), - (0xFC51, "M", "هج"), - (0xFC52, "M", "هم"), - (0xFC53, "M", "هى"), - (0xFC54, "M", "هي"), - (0xFC55, "M", "يج"), - (0xFC56, "M", "يح"), - (0xFC57, "M", "يخ"), - (0xFC58, "M", "يم"), - (0xFC59, "M", "يى"), - (0xFC5A, "M", "يي"), - (0xFC5B, "M", "ذٰ"), - (0xFC5C, "M", "رٰ"), - (0xFC5D, "M", "ىٰ"), - (0xFC5E, "M", " ٌّ"), - (0xFC5F, "M", " ٍّ"), - (0xFC60, "M", " َّ"), - (0xFC61, "M", " ُّ"), - (0xFC62, "M", " ِّ"), - (0xFC63, "M", " ّٰ"), - (0xFC64, "M", "ئر"), - (0xFC65, "M", "ئز"), - (0xFC66, "M", "ئم"), - (0xFC67, "M", "ئن"), - (0xFC68, "M", "ئى"), - (0xFC69, "M", "ئي"), - (0xFC6A, "M", "بر"), - (0xFC6B, "M", "بز"), - (0xFC6C, "M", "بم"), - (0xFC6D, "M", "بن"), - (0xFC6E, "M", "بى"), - (0xFC6F, "M", "بي"), - (0xFC70, "M", "تر"), - (0xFC71, "M", "تز"), - (0xFC72, "M", "تم"), - (0xFC73, "M", "تن"), - (0xFC74, "M", "تى"), - (0xFC75, "M", "تي"), - (0xFC76, "M", "ثر"), - (0xFC77, "M", "ثز"), - (0xFC78, "M", "ثم"), - (0xFC79, "M", "ثن"), - (0xFC7A, "M", "ثى"), - (0xFC7B, "M", "ثي"), - (0xFC7C, "M", "فى"), - (0xFC7D, "M", "في"), - (0xFC7E, "M", "قى"), - (0xFC7F, "M", "قي"), - (0xFC80, "M", "كا"), - (0xFC81, "M", "كل"), - (0xFC82, "M", "كم"), - (0xFC83, "M", "كى"), - (0xFC84, "M", "كي"), - (0xFC85, "M", "لم"), - (0xFC86, "M", "لى"), - (0xFC87, "M", "لي"), - (0xFC88, "M", "ما"), - (0xFC89, "M", "مم"), - (0xFC8A, "M", "نر"), - (0xFC8B, "M", "نز"), - (0xFC8C, "M", "نم"), - (0xFC8D, "M", "نن"), - (0xFC8E, "M", "نى"), - (0xFC8F, "M", "ني"), - (0xFC90, "M", "ىٰ"), - (0xFC91, "M", "ير"), - (0xFC92, "M", "يز"), - (0xFC93, "M", "يم"), - (0xFC94, "M", "ين"), - (0xFC95, "M", "يى"), - (0xFC96, "M", "يي"), - (0xFC97, "M", "ئج"), - ] - - -def _seg_47() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFC98, "M", "ئح"), - (0xFC99, "M", "ئخ"), - (0xFC9A, "M", "ئم"), - (0xFC9B, "M", "ئه"), - (0xFC9C, "M", "بج"), - (0xFC9D, "M", "بح"), - (0xFC9E, "M", "بخ"), - (0xFC9F, "M", "بم"), - (0xFCA0, "M", "به"), - (0xFCA1, "M", "تج"), - (0xFCA2, "M", "تح"), - (0xFCA3, "M", "تخ"), - (0xFCA4, "M", "تم"), - (0xFCA5, "M", "ته"), - (0xFCA6, "M", "ثم"), - (0xFCA7, "M", "جح"), - (0xFCA8, "M", "جم"), - (0xFCA9, "M", "حج"), - (0xFCAA, "M", "حم"), - (0xFCAB, "M", "خج"), - (0xFCAC, "M", "خم"), - (0xFCAD, "M", "سج"), - (0xFCAE, "M", "سح"), - (0xFCAF, "M", "سخ"), - (0xFCB0, "M", "سم"), - (0xFCB1, "M", "صح"), - (0xFCB2, "M", "صخ"), - (0xFCB3, "M", "صم"), - (0xFCB4, "M", "ضج"), - (0xFCB5, "M", "ضح"), - (0xFCB6, "M", "ضخ"), - (0xFCB7, "M", "ضم"), - (0xFCB8, "M", "طح"), - (0xFCB9, "M", "ظم"), - (0xFCBA, "M", "عج"), - (0xFCBB, "M", "عم"), - (0xFCBC, "M", "غج"), - (0xFCBD, "M", "غم"), - (0xFCBE, "M", "فج"), - (0xFCBF, "M", "فح"), - (0xFCC0, "M", "فخ"), - (0xFCC1, "M", "فم"), - (0xFCC2, "M", "قح"), - (0xFCC3, "M", "قم"), - (0xFCC4, "M", "كج"), - (0xFCC5, "M", "كح"), - (0xFCC6, "M", "كخ"), - (0xFCC7, "M", "كل"), - (0xFCC8, "M", "كم"), - (0xFCC9, "M", "لج"), - (0xFCCA, "M", "لح"), - (0xFCCB, "M", "لخ"), - (0xFCCC, "M", "لم"), - (0xFCCD, "M", "له"), - (0xFCCE, "M", "مج"), - (0xFCCF, "M", "مح"), - (0xFCD0, "M", "مخ"), - (0xFCD1, "M", "مم"), - (0xFCD2, "M", "نج"), - (0xFCD3, "M", "نح"), - (0xFCD4, "M", "نخ"), - (0xFCD5, "M", "نم"), - (0xFCD6, "M", "نه"), - (0xFCD7, "M", "هج"), - (0xFCD8, "M", "هم"), - (0xFCD9, "M", "هٰ"), - (0xFCDA, "M", "يج"), - (0xFCDB, "M", "يح"), - (0xFCDC, "M", "يخ"), - (0xFCDD, "M", "يم"), - (0xFCDE, "M", "يه"), - (0xFCDF, "M", "ئم"), - (0xFCE0, "M", "ئه"), - (0xFCE1, "M", "بم"), - (0xFCE2, "M", "به"), - (0xFCE3, "M", "تم"), - (0xFCE4, "M", "ته"), - (0xFCE5, "M", "ثم"), - (0xFCE6, "M", "ثه"), - (0xFCE7, "M", "سم"), - (0xFCE8, "M", "سه"), - (0xFCE9, "M", "شم"), - (0xFCEA, "M", "شه"), - (0xFCEB, "M", "كل"), - (0xFCEC, "M", "كم"), - (0xFCED, "M", "لم"), - (0xFCEE, "M", "نم"), - (0xFCEF, "M", "نه"), - (0xFCF0, "M", "يم"), - (0xFCF1, "M", "يه"), - (0xFCF2, "M", "ـَّ"), - (0xFCF3, "M", "ـُّ"), - (0xFCF4, "M", "ـِّ"), - (0xFCF5, "M", "طى"), - (0xFCF6, "M", "طي"), - (0xFCF7, "M", "عى"), - (0xFCF8, "M", "عي"), - (0xFCF9, "M", "غى"), - (0xFCFA, "M", "غي"), - (0xFCFB, "M", "سى"), - ] - - -def _seg_48() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFCFC, "M", "سي"), - (0xFCFD, "M", "شى"), - (0xFCFE, "M", "شي"), - (0xFCFF, "M", "حى"), - (0xFD00, "M", "حي"), - (0xFD01, "M", "جى"), - (0xFD02, "M", "جي"), - (0xFD03, "M", "خى"), - (0xFD04, "M", "خي"), - (0xFD05, "M", "صى"), - (0xFD06, "M", "صي"), - (0xFD07, "M", "ضى"), - (0xFD08, "M", "ضي"), - (0xFD09, "M", "شج"), - (0xFD0A, "M", "شح"), - (0xFD0B, "M", "شخ"), - (0xFD0C, "M", "شم"), - (0xFD0D, "M", "شر"), - (0xFD0E, "M", "سر"), - (0xFD0F, "M", "صر"), - (0xFD10, "M", "ضر"), - (0xFD11, "M", "طى"), - (0xFD12, "M", "طي"), - (0xFD13, "M", "عى"), - (0xFD14, "M", "عي"), - (0xFD15, "M", "غى"), - (0xFD16, "M", "غي"), - (0xFD17, "M", "سى"), - (0xFD18, "M", "سي"), - (0xFD19, "M", "شى"), - (0xFD1A, "M", "شي"), - (0xFD1B, "M", "حى"), - (0xFD1C, "M", "حي"), - (0xFD1D, "M", "جى"), - (0xFD1E, "M", "جي"), - (0xFD1F, "M", "خى"), - (0xFD20, "M", "خي"), - (0xFD21, "M", "صى"), - (0xFD22, "M", "صي"), - (0xFD23, "M", "ضى"), - (0xFD24, "M", "ضي"), - (0xFD25, "M", "شج"), - (0xFD26, "M", "شح"), - (0xFD27, "M", "شخ"), - (0xFD28, "M", "شم"), - (0xFD29, "M", "شر"), - (0xFD2A, "M", "سر"), - (0xFD2B, "M", "صر"), - (0xFD2C, "M", "ضر"), - (0xFD2D, "M", "شج"), - (0xFD2E, "M", "شح"), - (0xFD2F, "M", "شخ"), - (0xFD30, "M", "شم"), - (0xFD31, "M", "سه"), - (0xFD32, "M", "شه"), - (0xFD33, "M", "طم"), - (0xFD34, "M", "سج"), - (0xFD35, "M", "سح"), - (0xFD36, "M", "سخ"), - (0xFD37, "M", "شج"), - (0xFD38, "M", "شح"), - (0xFD39, "M", "شخ"), - (0xFD3A, "M", "طم"), - (0xFD3B, "M", "ظم"), - (0xFD3C, "M", "اً"), - (0xFD3E, "V"), - (0xFD50, "M", "تجم"), - (0xFD51, "M", "تحج"), - (0xFD53, "M", "تحم"), - (0xFD54, "M", "تخم"), - (0xFD55, "M", "تمج"), - (0xFD56, "M", "تمح"), - (0xFD57, "M", "تمخ"), - (0xFD58, "M", "جمح"), - (0xFD5A, "M", "حمي"), - (0xFD5B, "M", "حمى"), - (0xFD5C, "M", "سحج"), - (0xFD5D, "M", "سجح"), - (0xFD5E, "M", "سجى"), - (0xFD5F, "M", "سمح"), - (0xFD61, "M", "سمج"), - (0xFD62, "M", "سمم"), - (0xFD64, "M", "صحح"), - (0xFD66, "M", "صمم"), - (0xFD67, "M", "شحم"), - (0xFD69, "M", "شجي"), - (0xFD6A, "M", "شمخ"), - (0xFD6C, "M", "شمم"), - (0xFD6E, "M", "ضحى"), - (0xFD6F, "M", "ضخم"), - (0xFD71, "M", "طمح"), - (0xFD73, "M", "طمم"), - (0xFD74, "M", "طمي"), - (0xFD75, "M", "عجم"), - (0xFD76, "M", "عمم"), - (0xFD78, "M", "عمى"), - (0xFD79, "M", "غمم"), - (0xFD7A, "M", "غمي"), - (0xFD7B, "M", "غمى"), - (0xFD7C, "M", "فخم"), - ] - - -def _seg_49() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFD7E, "M", "قمح"), - (0xFD7F, "M", "قمم"), - (0xFD80, "M", "لحم"), - (0xFD81, "M", "لحي"), - (0xFD82, "M", "لحى"), - (0xFD83, "M", "لجج"), - (0xFD85, "M", "لخم"), - (0xFD87, "M", "لمح"), - (0xFD89, "M", "محج"), - (0xFD8A, "M", "محم"), - (0xFD8B, "M", "محي"), - (0xFD8C, "M", "مجح"), - (0xFD8D, "M", "مجم"), - (0xFD8E, "M", "مخج"), - (0xFD8F, "M", "مخم"), - (0xFD90, "X"), - (0xFD92, "M", "مجخ"), - (0xFD93, "M", "همج"), - (0xFD94, "M", "همم"), - (0xFD95, "M", "نحم"), - (0xFD96, "M", "نحى"), - (0xFD97, "M", "نجم"), - (0xFD99, "M", "نجى"), - (0xFD9A, "M", "نمي"), - (0xFD9B, "M", "نمى"), - (0xFD9C, "M", "يمم"), - (0xFD9E, "M", "بخي"), - (0xFD9F, "M", "تجي"), - (0xFDA0, "M", "تجى"), - (0xFDA1, "M", "تخي"), - (0xFDA2, "M", "تخى"), - (0xFDA3, "M", "تمي"), - (0xFDA4, "M", "تمى"), - (0xFDA5, "M", "جمي"), - (0xFDA6, "M", "جحى"), - (0xFDA7, "M", "جمى"), - (0xFDA8, "M", "سخى"), - (0xFDA9, "M", "صحي"), - (0xFDAA, "M", "شحي"), - (0xFDAB, "M", "ضحي"), - (0xFDAC, "M", "لجي"), - (0xFDAD, "M", "لمي"), - (0xFDAE, "M", "يحي"), - (0xFDAF, "M", "يجي"), - (0xFDB0, "M", "يمي"), - (0xFDB1, "M", "ممي"), - (0xFDB2, "M", "قمي"), - (0xFDB3, "M", "نحي"), - (0xFDB4, "M", "قمح"), - (0xFDB5, "M", "لحم"), - (0xFDB6, "M", "عمي"), - (0xFDB7, "M", "كمي"), - (0xFDB8, "M", "نجح"), - (0xFDB9, "M", "مخي"), - (0xFDBA, "M", "لجم"), - (0xFDBB, "M", "كمم"), - (0xFDBC, "M", "لجم"), - (0xFDBD, "M", "نجح"), - (0xFDBE, "M", "جحي"), - (0xFDBF, "M", "حجي"), - (0xFDC0, "M", "مجي"), - (0xFDC1, "M", "فمي"), - (0xFDC2, "M", "بحي"), - (0xFDC3, "M", "كمم"), - (0xFDC4, "M", "عجم"), - (0xFDC5, "M", "صمم"), - (0xFDC6, "M", "سخي"), - (0xFDC7, "M", "نجي"), - (0xFDC8, "X"), - (0xFDCF, "V"), - (0xFDD0, "X"), - (0xFDF0, "M", "صلے"), - (0xFDF1, "M", "قلے"), - (0xFDF2, "M", "الله"), - (0xFDF3, "M", "اكبر"), - (0xFDF4, "M", "محمد"), - (0xFDF5, "M", "صلعم"), - (0xFDF6, "M", "رسول"), - (0xFDF7, "M", "عليه"), - (0xFDF8, "M", "وسلم"), - (0xFDF9, "M", "صلى"), - (0xFDFA, "M", "صلى الله عليه وسلم"), - (0xFDFB, "M", "جل جلاله"), - (0xFDFC, "M", "ریال"), - (0xFDFD, "V"), - (0xFE00, "I"), - (0xFE10, "M", ","), - (0xFE11, "M", "、"), - (0xFE12, "X"), - (0xFE13, "M", ":"), - (0xFE14, "M", ";"), - (0xFE15, "M", "!"), - (0xFE16, "M", "?"), - (0xFE17, "M", "〖"), - (0xFE18, "M", "〗"), - (0xFE19, "X"), - (0xFE20, "V"), - (0xFE30, "X"), - (0xFE31, "M", "—"), - (0xFE32, "M", "–"), - ] - - -def _seg_50() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFE33, "M", "_"), - (0xFE35, "M", "("), - (0xFE36, "M", ")"), - (0xFE37, "M", "{"), - (0xFE38, "M", "}"), - (0xFE39, "M", "〔"), - (0xFE3A, "M", "〕"), - (0xFE3B, "M", "【"), - (0xFE3C, "M", "】"), - (0xFE3D, "M", "《"), - (0xFE3E, "M", "》"), - (0xFE3F, "M", "〈"), - (0xFE40, "M", "〉"), - (0xFE41, "M", "「"), - (0xFE42, "M", "」"), - (0xFE43, "M", "『"), - (0xFE44, "M", "』"), - (0xFE45, "V"), - (0xFE47, "M", "["), - (0xFE48, "M", "]"), - (0xFE49, "M", " ̅"), - (0xFE4D, "M", "_"), - (0xFE50, "M", ","), - (0xFE51, "M", "、"), - (0xFE52, "X"), - (0xFE54, "M", ";"), - (0xFE55, "M", ":"), - (0xFE56, "M", "?"), - (0xFE57, "M", "!"), - (0xFE58, "M", "—"), - (0xFE59, "M", "("), - (0xFE5A, "M", ")"), - (0xFE5B, "M", "{"), - (0xFE5C, "M", "}"), - (0xFE5D, "M", "〔"), - (0xFE5E, "M", "〕"), - (0xFE5F, "M", "#"), - (0xFE60, "M", "&"), - (0xFE61, "M", "*"), - (0xFE62, "M", "+"), - (0xFE63, "M", "-"), - (0xFE64, "M", "<"), - (0xFE65, "M", ">"), - (0xFE66, "M", "="), - (0xFE67, "X"), - (0xFE68, "M", "\\"), - (0xFE69, "M", "$"), - (0xFE6A, "M", "%"), - (0xFE6B, "M", "@"), - (0xFE6C, "X"), - (0xFE70, "M", " ً"), - (0xFE71, "M", "ـً"), - (0xFE72, "M", " ٌ"), - (0xFE73, "V"), - (0xFE74, "M", " ٍ"), - (0xFE75, "X"), - (0xFE76, "M", " َ"), - (0xFE77, "M", "ـَ"), - (0xFE78, "M", " ُ"), - (0xFE79, "M", "ـُ"), - (0xFE7A, "M", " ِ"), - (0xFE7B, "M", "ـِ"), - (0xFE7C, "M", " ّ"), - (0xFE7D, "M", "ـّ"), - (0xFE7E, "M", " ْ"), - (0xFE7F, "M", "ـْ"), - (0xFE80, "M", "ء"), - (0xFE81, "M", "آ"), - (0xFE83, "M", "أ"), - (0xFE85, "M", "ؤ"), - (0xFE87, "M", "إ"), - (0xFE89, "M", "ئ"), - (0xFE8D, "M", "ا"), - (0xFE8F, "M", "ب"), - (0xFE93, "M", "ة"), - (0xFE95, "M", "ت"), - (0xFE99, "M", "ث"), - (0xFE9D, "M", "ج"), - (0xFEA1, "M", "ح"), - (0xFEA5, "M", "خ"), - (0xFEA9, "M", "د"), - (0xFEAB, "M", "ذ"), - (0xFEAD, "M", "ر"), - (0xFEAF, "M", "ز"), - (0xFEB1, "M", "س"), - (0xFEB5, "M", "ش"), - (0xFEB9, "M", "ص"), - (0xFEBD, "M", "ض"), - (0xFEC1, "M", "ط"), - (0xFEC5, "M", "ظ"), - (0xFEC9, "M", "ع"), - (0xFECD, "M", "غ"), - (0xFED1, "M", "ف"), - (0xFED5, "M", "ق"), - (0xFED9, "M", "ك"), - (0xFEDD, "M", "ل"), - (0xFEE1, "M", "م"), - (0xFEE5, "M", "ن"), - (0xFEE9, "M", "ه"), - (0xFEED, "M", "و"), - ] - - -def _seg_51() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFEEF, "M", "ى"), - (0xFEF1, "M", "ي"), - (0xFEF5, "M", "لآ"), - (0xFEF7, "M", "لأ"), - (0xFEF9, "M", "لإ"), - (0xFEFB, "M", "لا"), - (0xFEFD, "X"), - (0xFEFF, "I"), - (0xFF00, "X"), - (0xFF01, "M", "!"), - (0xFF02, "M", '"'), - (0xFF03, "M", "#"), - (0xFF04, "M", "$"), - (0xFF05, "M", "%"), - (0xFF06, "M", "&"), - (0xFF07, "M", "'"), - (0xFF08, "M", "("), - (0xFF09, "M", ")"), - (0xFF0A, "M", "*"), - (0xFF0B, "M", "+"), - (0xFF0C, "M", ","), - (0xFF0D, "M", "-"), - (0xFF0E, "M", "."), - (0xFF0F, "M", "/"), - (0xFF10, "M", "0"), - (0xFF11, "M", "1"), - (0xFF12, "M", "2"), - (0xFF13, "M", "3"), - (0xFF14, "M", "4"), - (0xFF15, "M", "5"), - (0xFF16, "M", "6"), - (0xFF17, "M", "7"), - (0xFF18, "M", "8"), - (0xFF19, "M", "9"), - (0xFF1A, "M", ":"), - (0xFF1B, "M", ";"), - (0xFF1C, "M", "<"), - (0xFF1D, "M", "="), - (0xFF1E, "M", ">"), - (0xFF1F, "M", "?"), - (0xFF20, "M", "@"), - (0xFF21, "M", "a"), - (0xFF22, "M", "b"), - (0xFF23, "M", "c"), - (0xFF24, "M", "d"), - (0xFF25, "M", "e"), - (0xFF26, "M", "f"), - (0xFF27, "M", "g"), - (0xFF28, "M", "h"), - (0xFF29, "M", "i"), - (0xFF2A, "M", "j"), - (0xFF2B, "M", "k"), - (0xFF2C, "M", "l"), - (0xFF2D, "M", "m"), - (0xFF2E, "M", "n"), - (0xFF2F, "M", "o"), - (0xFF30, "M", "p"), - (0xFF31, "M", "q"), - (0xFF32, "M", "r"), - (0xFF33, "M", "s"), - (0xFF34, "M", "t"), - (0xFF35, "M", "u"), - (0xFF36, "M", "v"), - (0xFF37, "M", "w"), - (0xFF38, "M", "x"), - (0xFF39, "M", "y"), - (0xFF3A, "M", "z"), - (0xFF3B, "M", "["), - (0xFF3C, "M", "\\"), - (0xFF3D, "M", "]"), - (0xFF3E, "M", "^"), - (0xFF3F, "M", "_"), - (0xFF40, "M", "`"), - (0xFF41, "M", "a"), - (0xFF42, "M", "b"), - (0xFF43, "M", "c"), - (0xFF44, "M", "d"), - (0xFF45, "M", "e"), - (0xFF46, "M", "f"), - (0xFF47, "M", "g"), - (0xFF48, "M", "h"), - (0xFF49, "M", "i"), - (0xFF4A, "M", "j"), - (0xFF4B, "M", "k"), - (0xFF4C, "M", "l"), - (0xFF4D, "M", "m"), - (0xFF4E, "M", "n"), - (0xFF4F, "M", "o"), - (0xFF50, "M", "p"), - (0xFF51, "M", "q"), - (0xFF52, "M", "r"), - (0xFF53, "M", "s"), - (0xFF54, "M", "t"), - (0xFF55, "M", "u"), - (0xFF56, "M", "v"), - (0xFF57, "M", "w"), - (0xFF58, "M", "x"), - (0xFF59, "M", "y"), - (0xFF5A, "M", "z"), - (0xFF5B, "M", "{"), - ] - - -def _seg_52() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFF5C, "M", "|"), - (0xFF5D, "M", "}"), - (0xFF5E, "M", "~"), - (0xFF5F, "M", "⦅"), - (0xFF60, "M", "⦆"), - (0xFF61, "M", "."), - (0xFF62, "M", "「"), - (0xFF63, "M", "」"), - (0xFF64, "M", "、"), - (0xFF65, "M", "・"), - (0xFF66, "M", "ヲ"), - (0xFF67, "M", "ァ"), - (0xFF68, "M", "ィ"), - (0xFF69, "M", "ゥ"), - (0xFF6A, "M", "ェ"), - (0xFF6B, "M", "ォ"), - (0xFF6C, "M", "ャ"), - (0xFF6D, "M", "ュ"), - (0xFF6E, "M", "ョ"), - (0xFF6F, "M", "ッ"), - (0xFF70, "M", "ー"), - (0xFF71, "M", "ア"), - (0xFF72, "M", "イ"), - (0xFF73, "M", "ウ"), - (0xFF74, "M", "エ"), - (0xFF75, "M", "オ"), - (0xFF76, "M", "カ"), - (0xFF77, "M", "キ"), - (0xFF78, "M", "ク"), - (0xFF79, "M", "ケ"), - (0xFF7A, "M", "コ"), - (0xFF7B, "M", "サ"), - (0xFF7C, "M", "シ"), - (0xFF7D, "M", "ス"), - (0xFF7E, "M", "セ"), - (0xFF7F, "M", "ソ"), - (0xFF80, "M", "タ"), - (0xFF81, "M", "チ"), - (0xFF82, "M", "ツ"), - (0xFF83, "M", "テ"), - (0xFF84, "M", "ト"), - (0xFF85, "M", "ナ"), - (0xFF86, "M", "ニ"), - (0xFF87, "M", "ヌ"), - (0xFF88, "M", "ネ"), - (0xFF89, "M", "ノ"), - (0xFF8A, "M", "ハ"), - (0xFF8B, "M", "ヒ"), - (0xFF8C, "M", "フ"), - (0xFF8D, "M", "ヘ"), - (0xFF8E, "M", "ホ"), - (0xFF8F, "M", "マ"), - (0xFF90, "M", "ミ"), - (0xFF91, "M", "ム"), - (0xFF92, "M", "メ"), - (0xFF93, "M", "モ"), - (0xFF94, "M", "ヤ"), - (0xFF95, "M", "ユ"), - (0xFF96, "M", "ヨ"), - (0xFF97, "M", "ラ"), - (0xFF98, "M", "リ"), - (0xFF99, "M", "ル"), - (0xFF9A, "M", "レ"), - (0xFF9B, "M", "ロ"), - (0xFF9C, "M", "ワ"), - (0xFF9D, "M", "ン"), - (0xFF9E, "M", "゙"), - (0xFF9F, "M", "゚"), - (0xFFA0, "I"), - (0xFFA1, "M", "ᄀ"), - (0xFFA2, "M", "ᄁ"), - (0xFFA3, "M", "ᆪ"), - (0xFFA4, "M", "ᄂ"), - (0xFFA5, "M", "ᆬ"), - (0xFFA6, "M", "ᆭ"), - (0xFFA7, "M", "ᄃ"), - (0xFFA8, "M", "ᄄ"), - (0xFFA9, "M", "ᄅ"), - (0xFFAA, "M", "ᆰ"), - (0xFFAB, "M", "ᆱ"), - (0xFFAC, "M", "ᆲ"), - (0xFFAD, "M", "ᆳ"), - (0xFFAE, "M", "ᆴ"), - (0xFFAF, "M", "ᆵ"), - (0xFFB0, "M", "ᄚ"), - (0xFFB1, "M", "ᄆ"), - (0xFFB2, "M", "ᄇ"), - (0xFFB3, "M", "ᄈ"), - (0xFFB4, "M", "ᄡ"), - (0xFFB5, "M", "ᄉ"), - (0xFFB6, "M", "ᄊ"), - (0xFFB7, "M", "ᄋ"), - (0xFFB8, "M", "ᄌ"), - (0xFFB9, "M", "ᄍ"), - (0xFFBA, "M", "ᄎ"), - (0xFFBB, "M", "ᄏ"), - (0xFFBC, "M", "ᄐ"), - (0xFFBD, "M", "ᄑ"), - (0xFFBE, "M", "ᄒ"), - (0xFFBF, "X"), - ] - - -def _seg_53() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFFC2, "M", "ᅡ"), - (0xFFC3, "M", "ᅢ"), - (0xFFC4, "M", "ᅣ"), - (0xFFC5, "M", "ᅤ"), - (0xFFC6, "M", "ᅥ"), - (0xFFC7, "M", "ᅦ"), - (0xFFC8, "X"), - (0xFFCA, "M", "ᅧ"), - (0xFFCB, "M", "ᅨ"), - (0xFFCC, "M", "ᅩ"), - (0xFFCD, "M", "ᅪ"), - (0xFFCE, "M", "ᅫ"), - (0xFFCF, "M", "ᅬ"), - (0xFFD0, "X"), - (0xFFD2, "M", "ᅭ"), - (0xFFD3, "M", "ᅮ"), - (0xFFD4, "M", "ᅯ"), - (0xFFD5, "M", "ᅰ"), - (0xFFD6, "M", "ᅱ"), - (0xFFD7, "M", "ᅲ"), - (0xFFD8, "X"), - (0xFFDA, "M", "ᅳ"), - (0xFFDB, "M", "ᅴ"), - (0xFFDC, "M", "ᅵ"), - (0xFFDD, "X"), - (0xFFE0, "M", "¢"), - (0xFFE1, "M", "£"), - (0xFFE2, "M", "¬"), - (0xFFE3, "M", " ̄"), - (0xFFE4, "M", "¦"), - (0xFFE5, "M", "¥"), - (0xFFE6, "M", "₩"), - (0xFFE7, "X"), - (0xFFE8, "M", "│"), - (0xFFE9, "M", "←"), - (0xFFEA, "M", "↑"), - (0xFFEB, "M", "→"), - (0xFFEC, "M", "↓"), - (0xFFED, "M", "■"), - (0xFFEE, "M", "○"), - (0xFFEF, "X"), - (0x10000, "V"), - (0x1000C, "X"), - (0x1000D, "V"), - (0x10027, "X"), - (0x10028, "V"), - (0x1003B, "X"), - (0x1003C, "V"), - (0x1003E, "X"), - (0x1003F, "V"), - (0x1004E, "X"), - (0x10050, "V"), - (0x1005E, "X"), - (0x10080, "V"), - (0x100FB, "X"), - (0x10100, "V"), - (0x10103, "X"), - (0x10107, "V"), - (0x10134, "X"), - (0x10137, "V"), - (0x1018F, "X"), - (0x10190, "V"), - (0x1019D, "X"), - (0x101A0, "V"), - (0x101A1, "X"), - (0x101D0, "V"), - (0x101FE, "X"), - (0x10280, "V"), - (0x1029D, "X"), - (0x102A0, "V"), - (0x102D1, "X"), - (0x102E0, "V"), - (0x102FC, "X"), - (0x10300, "V"), - (0x10324, "X"), - (0x1032D, "V"), - (0x1034B, "X"), - (0x10350, "V"), - (0x1037B, "X"), - (0x10380, "V"), - (0x1039E, "X"), - (0x1039F, "V"), - (0x103C4, "X"), - (0x103C8, "V"), - (0x103D6, "X"), - (0x10400, "M", "𐐨"), - (0x10401, "M", "𐐩"), - (0x10402, "M", "𐐪"), - (0x10403, "M", "𐐫"), - (0x10404, "M", "𐐬"), - (0x10405, "M", "𐐭"), - (0x10406, "M", "𐐮"), - (0x10407, "M", "𐐯"), - (0x10408, "M", "𐐰"), - (0x10409, "M", "𐐱"), - (0x1040A, "M", "𐐲"), - (0x1040B, "M", "𐐳"), - (0x1040C, "M", "𐐴"), - (0x1040D, "M", "𐐵"), - (0x1040E, "M", "𐐶"), - ] - - -def _seg_54() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1040F, "M", "𐐷"), - (0x10410, "M", "𐐸"), - (0x10411, "M", "𐐹"), - (0x10412, "M", "𐐺"), - (0x10413, "M", "𐐻"), - (0x10414, "M", "𐐼"), - (0x10415, "M", "𐐽"), - (0x10416, "M", "𐐾"), - (0x10417, "M", "𐐿"), - (0x10418, "M", "𐑀"), - (0x10419, "M", "𐑁"), - (0x1041A, "M", "𐑂"), - (0x1041B, "M", "𐑃"), - (0x1041C, "M", "𐑄"), - (0x1041D, "M", "𐑅"), - (0x1041E, "M", "𐑆"), - (0x1041F, "M", "𐑇"), - (0x10420, "M", "𐑈"), - (0x10421, "M", "𐑉"), - (0x10422, "M", "𐑊"), - (0x10423, "M", "𐑋"), - (0x10424, "M", "𐑌"), - (0x10425, "M", "𐑍"), - (0x10426, "M", "𐑎"), - (0x10427, "M", "𐑏"), - (0x10428, "V"), - (0x1049E, "X"), - (0x104A0, "V"), - (0x104AA, "X"), - (0x104B0, "M", "𐓘"), - (0x104B1, "M", "𐓙"), - (0x104B2, "M", "𐓚"), - (0x104B3, "M", "𐓛"), - (0x104B4, "M", "𐓜"), - (0x104B5, "M", "𐓝"), - (0x104B6, "M", "𐓞"), - (0x104B7, "M", "𐓟"), - (0x104B8, "M", "𐓠"), - (0x104B9, "M", "𐓡"), - (0x104BA, "M", "𐓢"), - (0x104BB, "M", "𐓣"), - (0x104BC, "M", "𐓤"), - (0x104BD, "M", "𐓥"), - (0x104BE, "M", "𐓦"), - (0x104BF, "M", "𐓧"), - (0x104C0, "M", "𐓨"), - (0x104C1, "M", "𐓩"), - (0x104C2, "M", "𐓪"), - (0x104C3, "M", "𐓫"), - (0x104C4, "M", "𐓬"), - (0x104C5, "M", "𐓭"), - (0x104C6, "M", "𐓮"), - (0x104C7, "M", "𐓯"), - (0x104C8, "M", "𐓰"), - (0x104C9, "M", "𐓱"), - (0x104CA, "M", "𐓲"), - (0x104CB, "M", "𐓳"), - (0x104CC, "M", "𐓴"), - (0x104CD, "M", "𐓵"), - (0x104CE, "M", "𐓶"), - (0x104CF, "M", "𐓷"), - (0x104D0, "M", "𐓸"), - (0x104D1, "M", "𐓹"), - (0x104D2, "M", "𐓺"), - (0x104D3, "M", "𐓻"), - (0x104D4, "X"), - (0x104D8, "V"), - (0x104FC, "X"), - (0x10500, "V"), - (0x10528, "X"), - (0x10530, "V"), - (0x10564, "X"), - (0x1056F, "V"), - (0x10570, "M", "𐖗"), - (0x10571, "M", "𐖘"), - (0x10572, "M", "𐖙"), - (0x10573, "M", "𐖚"), - (0x10574, "M", "𐖛"), - (0x10575, "M", "𐖜"), - (0x10576, "M", "𐖝"), - (0x10577, "M", "𐖞"), - (0x10578, "M", "𐖟"), - (0x10579, "M", "𐖠"), - (0x1057A, "M", "𐖡"), - (0x1057B, "X"), - (0x1057C, "M", "𐖣"), - (0x1057D, "M", "𐖤"), - (0x1057E, "M", "𐖥"), - (0x1057F, "M", "𐖦"), - (0x10580, "M", "𐖧"), - (0x10581, "M", "𐖨"), - (0x10582, "M", "𐖩"), - (0x10583, "M", "𐖪"), - (0x10584, "M", "𐖫"), - (0x10585, "M", "𐖬"), - (0x10586, "M", "𐖭"), - (0x10587, "M", "𐖮"), - (0x10588, "M", "𐖯"), - (0x10589, "M", "𐖰"), - (0x1058A, "M", "𐖱"), - ] - - -def _seg_55() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1058B, "X"), - (0x1058C, "M", "𐖳"), - (0x1058D, "M", "𐖴"), - (0x1058E, "M", "𐖵"), - (0x1058F, "M", "𐖶"), - (0x10590, "M", "𐖷"), - (0x10591, "M", "𐖸"), - (0x10592, "M", "𐖹"), - (0x10593, "X"), - (0x10594, "M", "𐖻"), - (0x10595, "M", "𐖼"), - (0x10596, "X"), - (0x10597, "V"), - (0x105A2, "X"), - (0x105A3, "V"), - (0x105B2, "X"), - (0x105B3, "V"), - (0x105BA, "X"), - (0x105BB, "V"), - (0x105BD, "X"), - (0x105C0, "V"), - (0x105F4, "X"), - (0x10600, "V"), - (0x10737, "X"), - (0x10740, "V"), - (0x10756, "X"), - (0x10760, "V"), - (0x10768, "X"), - (0x10780, "V"), - (0x10781, "M", "ː"), - (0x10782, "M", "ˑ"), - (0x10783, "M", "æ"), - (0x10784, "M", "ʙ"), - (0x10785, "M", "ɓ"), - (0x10786, "X"), - (0x10787, "M", "ʣ"), - (0x10788, "M", "ꭦ"), - (0x10789, "M", "ʥ"), - (0x1078A, "M", "ʤ"), - (0x1078B, "M", "ɖ"), - (0x1078C, "M", "ɗ"), - (0x1078D, "M", "ᶑ"), - (0x1078E, "M", "ɘ"), - (0x1078F, "M", "ɞ"), - (0x10790, "M", "ʩ"), - (0x10791, "M", "ɤ"), - (0x10792, "M", "ɢ"), - (0x10793, "M", "ɠ"), - (0x10794, "M", "ʛ"), - (0x10795, "M", "ħ"), - (0x10796, "M", "ʜ"), - (0x10797, "M", "ɧ"), - (0x10798, "M", "ʄ"), - (0x10799, "M", "ʪ"), - (0x1079A, "M", "ʫ"), - (0x1079B, "M", "ɬ"), - (0x1079C, "M", "𝼄"), - (0x1079D, "M", "ꞎ"), - (0x1079E, "M", "ɮ"), - (0x1079F, "M", "𝼅"), - (0x107A0, "M", "ʎ"), - (0x107A1, "M", "𝼆"), - (0x107A2, "M", "ø"), - (0x107A3, "M", "ɶ"), - (0x107A4, "M", "ɷ"), - (0x107A5, "M", "q"), - (0x107A6, "M", "ɺ"), - (0x107A7, "M", "𝼈"), - (0x107A8, "M", "ɽ"), - (0x107A9, "M", "ɾ"), - (0x107AA, "M", "ʀ"), - (0x107AB, "M", "ʨ"), - (0x107AC, "M", "ʦ"), - (0x107AD, "M", "ꭧ"), - (0x107AE, "M", "ʧ"), - (0x107AF, "M", "ʈ"), - (0x107B0, "M", "ⱱ"), - (0x107B1, "X"), - (0x107B2, "M", "ʏ"), - (0x107B3, "M", "ʡ"), - (0x107B4, "M", "ʢ"), - (0x107B5, "M", "ʘ"), - (0x107B6, "M", "ǀ"), - (0x107B7, "M", "ǁ"), - (0x107B8, "M", "ǂ"), - (0x107B9, "M", "𝼊"), - (0x107BA, "M", "𝼞"), - (0x107BB, "X"), - (0x10800, "V"), - (0x10806, "X"), - (0x10808, "V"), - (0x10809, "X"), - (0x1080A, "V"), - (0x10836, "X"), - (0x10837, "V"), - (0x10839, "X"), - (0x1083C, "V"), - (0x1083D, "X"), - (0x1083F, "V"), - (0x10856, "X"), - ] - - -def _seg_56() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x10857, "V"), - (0x1089F, "X"), - (0x108A7, "V"), - (0x108B0, "X"), - (0x108E0, "V"), - (0x108F3, "X"), - (0x108F4, "V"), - (0x108F6, "X"), - (0x108FB, "V"), - (0x1091C, "X"), - (0x1091F, "V"), - (0x1093A, "X"), - (0x1093F, "V"), - (0x10940, "X"), - (0x10980, "V"), - (0x109B8, "X"), - (0x109BC, "V"), - (0x109D0, "X"), - (0x109D2, "V"), - (0x10A04, "X"), - (0x10A05, "V"), - (0x10A07, "X"), - (0x10A0C, "V"), - (0x10A14, "X"), - (0x10A15, "V"), - (0x10A18, "X"), - (0x10A19, "V"), - (0x10A36, "X"), - (0x10A38, "V"), - (0x10A3B, "X"), - (0x10A3F, "V"), - (0x10A49, "X"), - (0x10A50, "V"), - (0x10A59, "X"), - (0x10A60, "V"), - (0x10AA0, "X"), - (0x10AC0, "V"), - (0x10AE7, "X"), - (0x10AEB, "V"), - (0x10AF7, "X"), - (0x10B00, "V"), - (0x10B36, "X"), - (0x10B39, "V"), - (0x10B56, "X"), - (0x10B58, "V"), - (0x10B73, "X"), - (0x10B78, "V"), - (0x10B92, "X"), - (0x10B99, "V"), - (0x10B9D, "X"), - (0x10BA9, "V"), - (0x10BB0, "X"), - (0x10C00, "V"), - (0x10C49, "X"), - (0x10C80, "M", "𐳀"), - (0x10C81, "M", "𐳁"), - (0x10C82, "M", "𐳂"), - (0x10C83, "M", "𐳃"), - (0x10C84, "M", "𐳄"), - (0x10C85, "M", "𐳅"), - (0x10C86, "M", "𐳆"), - (0x10C87, "M", "𐳇"), - (0x10C88, "M", "𐳈"), - (0x10C89, "M", "𐳉"), - (0x10C8A, "M", "𐳊"), - (0x10C8B, "M", "𐳋"), - (0x10C8C, "M", "𐳌"), - (0x10C8D, "M", "𐳍"), - (0x10C8E, "M", "𐳎"), - (0x10C8F, "M", "𐳏"), - (0x10C90, "M", "𐳐"), - (0x10C91, "M", "𐳑"), - (0x10C92, "M", "𐳒"), - (0x10C93, "M", "𐳓"), - (0x10C94, "M", "𐳔"), - (0x10C95, "M", "𐳕"), - (0x10C96, "M", "𐳖"), - (0x10C97, "M", "𐳗"), - (0x10C98, "M", "𐳘"), - (0x10C99, "M", "𐳙"), - (0x10C9A, "M", "𐳚"), - (0x10C9B, "M", "𐳛"), - (0x10C9C, "M", "𐳜"), - (0x10C9D, "M", "𐳝"), - (0x10C9E, "M", "𐳞"), - (0x10C9F, "M", "𐳟"), - (0x10CA0, "M", "𐳠"), - (0x10CA1, "M", "𐳡"), - (0x10CA2, "M", "𐳢"), - (0x10CA3, "M", "𐳣"), - (0x10CA4, "M", "𐳤"), - (0x10CA5, "M", "𐳥"), - (0x10CA6, "M", "𐳦"), - (0x10CA7, "M", "𐳧"), - (0x10CA8, "M", "𐳨"), - (0x10CA9, "M", "𐳩"), - (0x10CAA, "M", "𐳪"), - (0x10CAB, "M", "𐳫"), - (0x10CAC, "M", "𐳬"), - (0x10CAD, "M", "𐳭"), - ] - - -def _seg_57() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x10CAE, "M", "𐳮"), - (0x10CAF, "M", "𐳯"), - (0x10CB0, "M", "𐳰"), - (0x10CB1, "M", "𐳱"), - (0x10CB2, "M", "𐳲"), - (0x10CB3, "X"), - (0x10CC0, "V"), - (0x10CF3, "X"), - (0x10CFA, "V"), - (0x10D28, "X"), - (0x10D30, "V"), - (0x10D3A, "X"), - (0x10D40, "V"), - (0x10D50, "M", "𐵰"), - (0x10D51, "M", "𐵱"), - (0x10D52, "M", "𐵲"), - (0x10D53, "M", "𐵳"), - (0x10D54, "M", "𐵴"), - (0x10D55, "M", "𐵵"), - (0x10D56, "M", "𐵶"), - (0x10D57, "M", "𐵷"), - (0x10D58, "M", "𐵸"), - (0x10D59, "M", "𐵹"), - (0x10D5A, "M", "𐵺"), - (0x10D5B, "M", "𐵻"), - (0x10D5C, "M", "𐵼"), - (0x10D5D, "M", "𐵽"), - (0x10D5E, "M", "𐵾"), - (0x10D5F, "M", "𐵿"), - (0x10D60, "M", "𐶀"), - (0x10D61, "M", "𐶁"), - (0x10D62, "M", "𐶂"), - (0x10D63, "M", "𐶃"), - (0x10D64, "M", "𐶄"), - (0x10D65, "M", "𐶅"), - (0x10D66, "X"), - (0x10D69, "V"), - (0x10D86, "X"), - (0x10D8E, "V"), - (0x10D90, "X"), - (0x10E60, "V"), - (0x10E7F, "X"), - (0x10E80, "V"), - (0x10EAA, "X"), - (0x10EAB, "V"), - (0x10EAE, "X"), - (0x10EB0, "V"), - (0x10EB2, "X"), - (0x10EC2, "V"), - (0x10EC5, "X"), - (0x10EFC, "V"), - (0x10F28, "X"), - (0x10F30, "V"), - (0x10F5A, "X"), - (0x10F70, "V"), - (0x10F8A, "X"), - (0x10FB0, "V"), - (0x10FCC, "X"), - (0x10FE0, "V"), - (0x10FF7, "X"), - (0x11000, "V"), - (0x1104E, "X"), - (0x11052, "V"), - (0x11076, "X"), - (0x1107F, "V"), - (0x110BD, "X"), - (0x110BE, "V"), - (0x110C3, "X"), - (0x110D0, "V"), - (0x110E9, "X"), - (0x110F0, "V"), - (0x110FA, "X"), - (0x11100, "V"), - (0x11135, "X"), - (0x11136, "V"), - (0x11148, "X"), - (0x11150, "V"), - (0x11177, "X"), - (0x11180, "V"), - (0x111E0, "X"), - (0x111E1, "V"), - (0x111F5, "X"), - (0x11200, "V"), - (0x11212, "X"), - (0x11213, "V"), - (0x11242, "X"), - (0x11280, "V"), - (0x11287, "X"), - (0x11288, "V"), - (0x11289, "X"), - (0x1128A, "V"), - (0x1128E, "X"), - (0x1128F, "V"), - (0x1129E, "X"), - (0x1129F, "V"), - (0x112AA, "X"), - (0x112B0, "V"), - (0x112EB, "X"), - (0x112F0, "V"), - (0x112FA, "X"), - ] - - -def _seg_58() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x11300, "V"), - (0x11304, "X"), - (0x11305, "V"), - (0x1130D, "X"), - (0x1130F, "V"), - (0x11311, "X"), - (0x11313, "V"), - (0x11329, "X"), - (0x1132A, "V"), - (0x11331, "X"), - (0x11332, "V"), - (0x11334, "X"), - (0x11335, "V"), - (0x1133A, "X"), - (0x1133B, "V"), - (0x11345, "X"), - (0x11347, "V"), - (0x11349, "X"), - (0x1134B, "V"), - (0x1134E, "X"), - (0x11350, "V"), - (0x11351, "X"), - (0x11357, "V"), - (0x11358, "X"), - (0x1135D, "V"), - (0x11364, "X"), - (0x11366, "V"), - (0x1136D, "X"), - (0x11370, "V"), - (0x11375, "X"), - (0x11380, "V"), - (0x1138A, "X"), - (0x1138B, "V"), - (0x1138C, "X"), - (0x1138E, "V"), - (0x1138F, "X"), - (0x11390, "V"), - (0x113B6, "X"), - (0x113B7, "V"), - (0x113C1, "X"), - (0x113C2, "V"), - (0x113C3, "X"), - (0x113C5, "V"), - (0x113C6, "X"), - (0x113C7, "V"), - (0x113CB, "X"), - (0x113CC, "V"), - (0x113D6, "X"), - (0x113D7, "V"), - (0x113D9, "X"), - (0x113E1, "V"), - (0x113E3, "X"), - (0x11400, "V"), - (0x1145C, "X"), - (0x1145D, "V"), - (0x11462, "X"), - (0x11480, "V"), - (0x114C8, "X"), - (0x114D0, "V"), - (0x114DA, "X"), - (0x11580, "V"), - (0x115B6, "X"), - (0x115B8, "V"), - (0x115DE, "X"), - (0x11600, "V"), - (0x11645, "X"), - (0x11650, "V"), - (0x1165A, "X"), - (0x11660, "V"), - (0x1166D, "X"), - (0x11680, "V"), - (0x116BA, "X"), - (0x116C0, "V"), - (0x116CA, "X"), - (0x116D0, "V"), - (0x116E4, "X"), - (0x11700, "V"), - (0x1171B, "X"), - (0x1171D, "V"), - (0x1172C, "X"), - (0x11730, "V"), - (0x11747, "X"), - (0x11800, "V"), - (0x1183C, "X"), - (0x118A0, "M", "𑣀"), - (0x118A1, "M", "𑣁"), - (0x118A2, "M", "𑣂"), - (0x118A3, "M", "𑣃"), - (0x118A4, "M", "𑣄"), - (0x118A5, "M", "𑣅"), - (0x118A6, "M", "𑣆"), - (0x118A7, "M", "𑣇"), - (0x118A8, "M", "𑣈"), - (0x118A9, "M", "𑣉"), - (0x118AA, "M", "𑣊"), - (0x118AB, "M", "𑣋"), - (0x118AC, "M", "𑣌"), - (0x118AD, "M", "𑣍"), - (0x118AE, "M", "𑣎"), - (0x118AF, "M", "𑣏"), - ] - - -def _seg_59() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x118B0, "M", "𑣐"), - (0x118B1, "M", "𑣑"), - (0x118B2, "M", "𑣒"), - (0x118B3, "M", "𑣓"), - (0x118B4, "M", "𑣔"), - (0x118B5, "M", "𑣕"), - (0x118B6, "M", "𑣖"), - (0x118B7, "M", "𑣗"), - (0x118B8, "M", "𑣘"), - (0x118B9, "M", "𑣙"), - (0x118BA, "M", "𑣚"), - (0x118BB, "M", "𑣛"), - (0x118BC, "M", "𑣜"), - (0x118BD, "M", "𑣝"), - (0x118BE, "M", "𑣞"), - (0x118BF, "M", "𑣟"), - (0x118C0, "V"), - (0x118F3, "X"), - (0x118FF, "V"), - (0x11907, "X"), - (0x11909, "V"), - (0x1190A, "X"), - (0x1190C, "V"), - (0x11914, "X"), - (0x11915, "V"), - (0x11917, "X"), - (0x11918, "V"), - (0x11936, "X"), - (0x11937, "V"), - (0x11939, "X"), - (0x1193B, "V"), - (0x11947, "X"), - (0x11950, "V"), - (0x1195A, "X"), - (0x119A0, "V"), - (0x119A8, "X"), - (0x119AA, "V"), - (0x119D8, "X"), - (0x119DA, "V"), - (0x119E5, "X"), - (0x11A00, "V"), - (0x11A48, "X"), - (0x11A50, "V"), - (0x11AA3, "X"), - (0x11AB0, "V"), - (0x11AF9, "X"), - (0x11B00, "V"), - (0x11B0A, "X"), - (0x11BC0, "V"), - (0x11BE2, "X"), - (0x11BF0, "V"), - (0x11BFA, "X"), - (0x11C00, "V"), - (0x11C09, "X"), - (0x11C0A, "V"), - (0x11C37, "X"), - (0x11C38, "V"), - (0x11C46, "X"), - (0x11C50, "V"), - (0x11C6D, "X"), - (0x11C70, "V"), - (0x11C90, "X"), - (0x11C92, "V"), - (0x11CA8, "X"), - (0x11CA9, "V"), - (0x11CB7, "X"), - (0x11D00, "V"), - (0x11D07, "X"), - (0x11D08, "V"), - (0x11D0A, "X"), - (0x11D0B, "V"), - (0x11D37, "X"), - (0x11D3A, "V"), - (0x11D3B, "X"), - (0x11D3C, "V"), - (0x11D3E, "X"), - (0x11D3F, "V"), - (0x11D48, "X"), - (0x11D50, "V"), - (0x11D5A, "X"), - (0x11D60, "V"), - (0x11D66, "X"), - (0x11D67, "V"), - (0x11D69, "X"), - (0x11D6A, "V"), - (0x11D8F, "X"), - (0x11D90, "V"), - (0x11D92, "X"), - (0x11D93, "V"), - (0x11D99, "X"), - (0x11DA0, "V"), - (0x11DAA, "X"), - (0x11EE0, "V"), - (0x11EF9, "X"), - (0x11F00, "V"), - (0x11F11, "X"), - (0x11F12, "V"), - (0x11F3B, "X"), - (0x11F3E, "V"), - (0x11F5B, "X"), - ] - - -def _seg_60() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x11FB0, "V"), - (0x11FB1, "X"), - (0x11FC0, "V"), - (0x11FF2, "X"), - (0x11FFF, "V"), - (0x1239A, "X"), - (0x12400, "V"), - (0x1246F, "X"), - (0x12470, "V"), - (0x12475, "X"), - (0x12480, "V"), - (0x12544, "X"), - (0x12F90, "V"), - (0x12FF3, "X"), - (0x13000, "V"), - (0x13430, "X"), - (0x13440, "V"), - (0x13456, "X"), - (0x13460, "V"), - (0x143FB, "X"), - (0x14400, "V"), - (0x14647, "X"), - (0x16100, "V"), - (0x1613A, "X"), - (0x16800, "V"), - (0x16A39, "X"), - (0x16A40, "V"), - (0x16A5F, "X"), - (0x16A60, "V"), - (0x16A6A, "X"), - (0x16A6E, "V"), - (0x16ABF, "X"), - (0x16AC0, "V"), - (0x16ACA, "X"), - (0x16AD0, "V"), - (0x16AEE, "X"), - (0x16AF0, "V"), - (0x16AF6, "X"), - (0x16B00, "V"), - (0x16B46, "X"), - (0x16B50, "V"), - (0x16B5A, "X"), - (0x16B5B, "V"), - (0x16B62, "X"), - (0x16B63, "V"), - (0x16B78, "X"), - (0x16B7D, "V"), - (0x16B90, "X"), - (0x16D40, "V"), - (0x16D7A, "X"), - (0x16E40, "M", "𖹠"), - (0x16E41, "M", "𖹡"), - (0x16E42, "M", "𖹢"), - (0x16E43, "M", "𖹣"), - (0x16E44, "M", "𖹤"), - (0x16E45, "M", "𖹥"), - (0x16E46, "M", "𖹦"), - (0x16E47, "M", "𖹧"), - (0x16E48, "M", "𖹨"), - (0x16E49, "M", "𖹩"), - (0x16E4A, "M", "𖹪"), - (0x16E4B, "M", "𖹫"), - (0x16E4C, "M", "𖹬"), - (0x16E4D, "M", "𖹭"), - (0x16E4E, "M", "𖹮"), - (0x16E4F, "M", "𖹯"), - (0x16E50, "M", "𖹰"), - (0x16E51, "M", "𖹱"), - (0x16E52, "M", "𖹲"), - (0x16E53, "M", "𖹳"), - (0x16E54, "M", "𖹴"), - (0x16E55, "M", "𖹵"), - (0x16E56, "M", "𖹶"), - (0x16E57, "M", "𖹷"), - (0x16E58, "M", "𖹸"), - (0x16E59, "M", "𖹹"), - (0x16E5A, "M", "𖹺"), - (0x16E5B, "M", "𖹻"), - (0x16E5C, "M", "𖹼"), - (0x16E5D, "M", "𖹽"), - (0x16E5E, "M", "𖹾"), - (0x16E5F, "M", "𖹿"), - (0x16E60, "V"), - (0x16E9B, "X"), - (0x16F00, "V"), - (0x16F4B, "X"), - (0x16F4F, "V"), - (0x16F88, "X"), - (0x16F8F, "V"), - (0x16FA0, "X"), - (0x16FE0, "V"), - (0x16FE5, "X"), - (0x16FF0, "V"), - (0x16FF2, "X"), - (0x17000, "V"), - (0x187F8, "X"), - (0x18800, "V"), - (0x18CD6, "X"), - (0x18CFF, "V"), - (0x18D09, "X"), - ] - - -def _seg_61() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1AFF0, "V"), - (0x1AFF4, "X"), - (0x1AFF5, "V"), - (0x1AFFC, "X"), - (0x1AFFD, "V"), - (0x1AFFF, "X"), - (0x1B000, "V"), - (0x1B123, "X"), - (0x1B132, "V"), - (0x1B133, "X"), - (0x1B150, "V"), - (0x1B153, "X"), - (0x1B155, "V"), - (0x1B156, "X"), - (0x1B164, "V"), - (0x1B168, "X"), - (0x1B170, "V"), - (0x1B2FC, "X"), - (0x1BC00, "V"), - (0x1BC6B, "X"), - (0x1BC70, "V"), - (0x1BC7D, "X"), - (0x1BC80, "V"), - (0x1BC89, "X"), - (0x1BC90, "V"), - (0x1BC9A, "X"), - (0x1BC9C, "V"), - (0x1BCA0, "I"), - (0x1BCA4, "X"), - (0x1CC00, "V"), - (0x1CCD6, "M", "a"), - (0x1CCD7, "M", "b"), - (0x1CCD8, "M", "c"), - (0x1CCD9, "M", "d"), - (0x1CCDA, "M", "e"), - (0x1CCDB, "M", "f"), - (0x1CCDC, "M", "g"), - (0x1CCDD, "M", "h"), - (0x1CCDE, "M", "i"), - (0x1CCDF, "M", "j"), - (0x1CCE0, "M", "k"), - (0x1CCE1, "M", "l"), - (0x1CCE2, "M", "m"), - (0x1CCE3, "M", "n"), - (0x1CCE4, "M", "o"), - (0x1CCE5, "M", "p"), - (0x1CCE6, "M", "q"), - (0x1CCE7, "M", "r"), - (0x1CCE8, "M", "s"), - (0x1CCE9, "M", "t"), - (0x1CCEA, "M", "u"), - (0x1CCEB, "M", "v"), - (0x1CCEC, "M", "w"), - (0x1CCED, "M", "x"), - (0x1CCEE, "M", "y"), - (0x1CCEF, "M", "z"), - (0x1CCF0, "M", "0"), - (0x1CCF1, "M", "1"), - (0x1CCF2, "M", "2"), - (0x1CCF3, "M", "3"), - (0x1CCF4, "M", "4"), - (0x1CCF5, "M", "5"), - (0x1CCF6, "M", "6"), - (0x1CCF7, "M", "7"), - (0x1CCF8, "M", "8"), - (0x1CCF9, "M", "9"), - (0x1CCFA, "X"), - (0x1CD00, "V"), - (0x1CEB4, "X"), - (0x1CF00, "V"), - (0x1CF2E, "X"), - (0x1CF30, "V"), - (0x1CF47, "X"), - (0x1CF50, "V"), - (0x1CFC4, "X"), - (0x1D000, "V"), - (0x1D0F6, "X"), - (0x1D100, "V"), - (0x1D127, "X"), - (0x1D129, "V"), - (0x1D15E, "M", "𝅗𝅥"), - (0x1D15F, "M", "𝅘𝅥"), - (0x1D160, "M", "𝅘𝅥𝅮"), - (0x1D161, "M", "𝅘𝅥𝅯"), - (0x1D162, "M", "𝅘𝅥𝅰"), - (0x1D163, "M", "𝅘𝅥𝅱"), - (0x1D164, "M", "𝅘𝅥𝅲"), - (0x1D165, "V"), - (0x1D173, "I"), - (0x1D17B, "V"), - (0x1D1BB, "M", "𝆹𝅥"), - (0x1D1BC, "M", "𝆺𝅥"), - (0x1D1BD, "M", "𝆹𝅥𝅮"), - (0x1D1BE, "M", "𝆺𝅥𝅮"), - (0x1D1BF, "M", "𝆹𝅥𝅯"), - (0x1D1C0, "M", "𝆺𝅥𝅯"), - (0x1D1C1, "V"), - (0x1D1EB, "X"), - (0x1D200, "V"), - (0x1D246, "X"), - ] - - -def _seg_62() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D2C0, "V"), - (0x1D2D4, "X"), - (0x1D2E0, "V"), - (0x1D2F4, "X"), - (0x1D300, "V"), - (0x1D357, "X"), - (0x1D360, "V"), - (0x1D379, "X"), - (0x1D400, "M", "a"), - (0x1D401, "M", "b"), - (0x1D402, "M", "c"), - (0x1D403, "M", "d"), - (0x1D404, "M", "e"), - (0x1D405, "M", "f"), - (0x1D406, "M", "g"), - (0x1D407, "M", "h"), - (0x1D408, "M", "i"), - (0x1D409, "M", "j"), - (0x1D40A, "M", "k"), - (0x1D40B, "M", "l"), - (0x1D40C, "M", "m"), - (0x1D40D, "M", "n"), - (0x1D40E, "M", "o"), - (0x1D40F, "M", "p"), - (0x1D410, "M", "q"), - (0x1D411, "M", "r"), - (0x1D412, "M", "s"), - (0x1D413, "M", "t"), - (0x1D414, "M", "u"), - (0x1D415, "M", "v"), - (0x1D416, "M", "w"), - (0x1D417, "M", "x"), - (0x1D418, "M", "y"), - (0x1D419, "M", "z"), - (0x1D41A, "M", "a"), - (0x1D41B, "M", "b"), - (0x1D41C, "M", "c"), - (0x1D41D, "M", "d"), - (0x1D41E, "M", "e"), - (0x1D41F, "M", "f"), - (0x1D420, "M", "g"), - (0x1D421, "M", "h"), - (0x1D422, "M", "i"), - (0x1D423, "M", "j"), - (0x1D424, "M", "k"), - (0x1D425, "M", "l"), - (0x1D426, "M", "m"), - (0x1D427, "M", "n"), - (0x1D428, "M", "o"), - (0x1D429, "M", "p"), - (0x1D42A, "M", "q"), - (0x1D42B, "M", "r"), - (0x1D42C, "M", "s"), - (0x1D42D, "M", "t"), - (0x1D42E, "M", "u"), - (0x1D42F, "M", "v"), - (0x1D430, "M", "w"), - (0x1D431, "M", "x"), - (0x1D432, "M", "y"), - (0x1D433, "M", "z"), - (0x1D434, "M", "a"), - (0x1D435, "M", "b"), - (0x1D436, "M", "c"), - (0x1D437, "M", "d"), - (0x1D438, "M", "e"), - (0x1D439, "M", "f"), - (0x1D43A, "M", "g"), - (0x1D43B, "M", "h"), - (0x1D43C, "M", "i"), - (0x1D43D, "M", "j"), - (0x1D43E, "M", "k"), - (0x1D43F, "M", "l"), - (0x1D440, "M", "m"), - (0x1D441, "M", "n"), - (0x1D442, "M", "o"), - (0x1D443, "M", "p"), - (0x1D444, "M", "q"), - (0x1D445, "M", "r"), - (0x1D446, "M", "s"), - (0x1D447, "M", "t"), - (0x1D448, "M", "u"), - (0x1D449, "M", "v"), - (0x1D44A, "M", "w"), - (0x1D44B, "M", "x"), - (0x1D44C, "M", "y"), - (0x1D44D, "M", "z"), - (0x1D44E, "M", "a"), - (0x1D44F, "M", "b"), - (0x1D450, "M", "c"), - (0x1D451, "M", "d"), - (0x1D452, "M", "e"), - (0x1D453, "M", "f"), - (0x1D454, "M", "g"), - (0x1D455, "X"), - (0x1D456, "M", "i"), - (0x1D457, "M", "j"), - (0x1D458, "M", "k"), - (0x1D459, "M", "l"), - (0x1D45A, "M", "m"), - (0x1D45B, "M", "n"), - ] - - -def _seg_63() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D45C, "M", "o"), - (0x1D45D, "M", "p"), - (0x1D45E, "M", "q"), - (0x1D45F, "M", "r"), - (0x1D460, "M", "s"), - (0x1D461, "M", "t"), - (0x1D462, "M", "u"), - (0x1D463, "M", "v"), - (0x1D464, "M", "w"), - (0x1D465, "M", "x"), - (0x1D466, "M", "y"), - (0x1D467, "M", "z"), - (0x1D468, "M", "a"), - (0x1D469, "M", "b"), - (0x1D46A, "M", "c"), - (0x1D46B, "M", "d"), - (0x1D46C, "M", "e"), - (0x1D46D, "M", "f"), - (0x1D46E, "M", "g"), - (0x1D46F, "M", "h"), - (0x1D470, "M", "i"), - (0x1D471, "M", "j"), - (0x1D472, "M", "k"), - (0x1D473, "M", "l"), - (0x1D474, "M", "m"), - (0x1D475, "M", "n"), - (0x1D476, "M", "o"), - (0x1D477, "M", "p"), - (0x1D478, "M", "q"), - (0x1D479, "M", "r"), - (0x1D47A, "M", "s"), - (0x1D47B, "M", "t"), - (0x1D47C, "M", "u"), - (0x1D47D, "M", "v"), - (0x1D47E, "M", "w"), - (0x1D47F, "M", "x"), - (0x1D480, "M", "y"), - (0x1D481, "M", "z"), - (0x1D482, "M", "a"), - (0x1D483, "M", "b"), - (0x1D484, "M", "c"), - (0x1D485, "M", "d"), - (0x1D486, "M", "e"), - (0x1D487, "M", "f"), - (0x1D488, "M", "g"), - (0x1D489, "M", "h"), - (0x1D48A, "M", "i"), - (0x1D48B, "M", "j"), - (0x1D48C, "M", "k"), - (0x1D48D, "M", "l"), - (0x1D48E, "M", "m"), - (0x1D48F, "M", "n"), - (0x1D490, "M", "o"), - (0x1D491, "M", "p"), - (0x1D492, "M", "q"), - (0x1D493, "M", "r"), - (0x1D494, "M", "s"), - (0x1D495, "M", "t"), - (0x1D496, "M", "u"), - (0x1D497, "M", "v"), - (0x1D498, "M", "w"), - (0x1D499, "M", "x"), - (0x1D49A, "M", "y"), - (0x1D49B, "M", "z"), - (0x1D49C, "M", "a"), - (0x1D49D, "X"), - (0x1D49E, "M", "c"), - (0x1D49F, "M", "d"), - (0x1D4A0, "X"), - (0x1D4A2, "M", "g"), - (0x1D4A3, "X"), - (0x1D4A5, "M", "j"), - (0x1D4A6, "M", "k"), - (0x1D4A7, "X"), - (0x1D4A9, "M", "n"), - (0x1D4AA, "M", "o"), - (0x1D4AB, "M", "p"), - (0x1D4AC, "M", "q"), - (0x1D4AD, "X"), - (0x1D4AE, "M", "s"), - (0x1D4AF, "M", "t"), - (0x1D4B0, "M", "u"), - (0x1D4B1, "M", "v"), - (0x1D4B2, "M", "w"), - (0x1D4B3, "M", "x"), - (0x1D4B4, "M", "y"), - (0x1D4B5, "M", "z"), - (0x1D4B6, "M", "a"), - (0x1D4B7, "M", "b"), - (0x1D4B8, "M", "c"), - (0x1D4B9, "M", "d"), - (0x1D4BA, "X"), - (0x1D4BB, "M", "f"), - (0x1D4BC, "X"), - (0x1D4BD, "M", "h"), - (0x1D4BE, "M", "i"), - (0x1D4BF, "M", "j"), - (0x1D4C0, "M", "k"), - (0x1D4C1, "M", "l"), - (0x1D4C2, "M", "m"), - ] - - -def _seg_64() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D4C3, "M", "n"), - (0x1D4C4, "X"), - (0x1D4C5, "M", "p"), - (0x1D4C6, "M", "q"), - (0x1D4C7, "M", "r"), - (0x1D4C8, "M", "s"), - (0x1D4C9, "M", "t"), - (0x1D4CA, "M", "u"), - (0x1D4CB, "M", "v"), - (0x1D4CC, "M", "w"), - (0x1D4CD, "M", "x"), - (0x1D4CE, "M", "y"), - (0x1D4CF, "M", "z"), - (0x1D4D0, "M", "a"), - (0x1D4D1, "M", "b"), - (0x1D4D2, "M", "c"), - (0x1D4D3, "M", "d"), - (0x1D4D4, "M", "e"), - (0x1D4D5, "M", "f"), - (0x1D4D6, "M", "g"), - (0x1D4D7, "M", "h"), - (0x1D4D8, "M", "i"), - (0x1D4D9, "M", "j"), - (0x1D4DA, "M", "k"), - (0x1D4DB, "M", "l"), - (0x1D4DC, "M", "m"), - (0x1D4DD, "M", "n"), - (0x1D4DE, "M", "o"), - (0x1D4DF, "M", "p"), - (0x1D4E0, "M", "q"), - (0x1D4E1, "M", "r"), - (0x1D4E2, "M", "s"), - (0x1D4E3, "M", "t"), - (0x1D4E4, "M", "u"), - (0x1D4E5, "M", "v"), - (0x1D4E6, "M", "w"), - (0x1D4E7, "M", "x"), - (0x1D4E8, "M", "y"), - (0x1D4E9, "M", "z"), - (0x1D4EA, "M", "a"), - (0x1D4EB, "M", "b"), - (0x1D4EC, "M", "c"), - (0x1D4ED, "M", "d"), - (0x1D4EE, "M", "e"), - (0x1D4EF, "M", "f"), - (0x1D4F0, "M", "g"), - (0x1D4F1, "M", "h"), - (0x1D4F2, "M", "i"), - (0x1D4F3, "M", "j"), - (0x1D4F4, "M", "k"), - (0x1D4F5, "M", "l"), - (0x1D4F6, "M", "m"), - (0x1D4F7, "M", "n"), - (0x1D4F8, "M", "o"), - (0x1D4F9, "M", "p"), - (0x1D4FA, "M", "q"), - (0x1D4FB, "M", "r"), - (0x1D4FC, "M", "s"), - (0x1D4FD, "M", "t"), - (0x1D4FE, "M", "u"), - (0x1D4FF, "M", "v"), - (0x1D500, "M", "w"), - (0x1D501, "M", "x"), - (0x1D502, "M", "y"), - (0x1D503, "M", "z"), - (0x1D504, "M", "a"), - (0x1D505, "M", "b"), - (0x1D506, "X"), - (0x1D507, "M", "d"), - (0x1D508, "M", "e"), - (0x1D509, "M", "f"), - (0x1D50A, "M", "g"), - (0x1D50B, "X"), - (0x1D50D, "M", "j"), - (0x1D50E, "M", "k"), - (0x1D50F, "M", "l"), - (0x1D510, "M", "m"), - (0x1D511, "M", "n"), - (0x1D512, "M", "o"), - (0x1D513, "M", "p"), - (0x1D514, "M", "q"), - (0x1D515, "X"), - (0x1D516, "M", "s"), - (0x1D517, "M", "t"), - (0x1D518, "M", "u"), - (0x1D519, "M", "v"), - (0x1D51A, "M", "w"), - (0x1D51B, "M", "x"), - (0x1D51C, "M", "y"), - (0x1D51D, "X"), - (0x1D51E, "M", "a"), - (0x1D51F, "M", "b"), - (0x1D520, "M", "c"), - (0x1D521, "M", "d"), - (0x1D522, "M", "e"), - (0x1D523, "M", "f"), - (0x1D524, "M", "g"), - (0x1D525, "M", "h"), - (0x1D526, "M", "i"), - (0x1D527, "M", "j"), - ] - - -def _seg_65() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D528, "M", "k"), - (0x1D529, "M", "l"), - (0x1D52A, "M", "m"), - (0x1D52B, "M", "n"), - (0x1D52C, "M", "o"), - (0x1D52D, "M", "p"), - (0x1D52E, "M", "q"), - (0x1D52F, "M", "r"), - (0x1D530, "M", "s"), - (0x1D531, "M", "t"), - (0x1D532, "M", "u"), - (0x1D533, "M", "v"), - (0x1D534, "M", "w"), - (0x1D535, "M", "x"), - (0x1D536, "M", "y"), - (0x1D537, "M", "z"), - (0x1D538, "M", "a"), - (0x1D539, "M", "b"), - (0x1D53A, "X"), - (0x1D53B, "M", "d"), - (0x1D53C, "M", "e"), - (0x1D53D, "M", "f"), - (0x1D53E, "M", "g"), - (0x1D53F, "X"), - (0x1D540, "M", "i"), - (0x1D541, "M", "j"), - (0x1D542, "M", "k"), - (0x1D543, "M", "l"), - (0x1D544, "M", "m"), - (0x1D545, "X"), - (0x1D546, "M", "o"), - (0x1D547, "X"), - (0x1D54A, "M", "s"), - (0x1D54B, "M", "t"), - (0x1D54C, "M", "u"), - (0x1D54D, "M", "v"), - (0x1D54E, "M", "w"), - (0x1D54F, "M", "x"), - (0x1D550, "M", "y"), - (0x1D551, "X"), - (0x1D552, "M", "a"), - (0x1D553, "M", "b"), - (0x1D554, "M", "c"), - (0x1D555, "M", "d"), - (0x1D556, "M", "e"), - (0x1D557, "M", "f"), - (0x1D558, "M", "g"), - (0x1D559, "M", "h"), - (0x1D55A, "M", "i"), - (0x1D55B, "M", "j"), - (0x1D55C, "M", "k"), - (0x1D55D, "M", "l"), - (0x1D55E, "M", "m"), - (0x1D55F, "M", "n"), - (0x1D560, "M", "o"), - (0x1D561, "M", "p"), - (0x1D562, "M", "q"), - (0x1D563, "M", "r"), - (0x1D564, "M", "s"), - (0x1D565, "M", "t"), - (0x1D566, "M", "u"), - (0x1D567, "M", "v"), - (0x1D568, "M", "w"), - (0x1D569, "M", "x"), - (0x1D56A, "M", "y"), - (0x1D56B, "M", "z"), - (0x1D56C, "M", "a"), - (0x1D56D, "M", "b"), - (0x1D56E, "M", "c"), - (0x1D56F, "M", "d"), - (0x1D570, "M", "e"), - (0x1D571, "M", "f"), - (0x1D572, "M", "g"), - (0x1D573, "M", "h"), - (0x1D574, "M", "i"), - (0x1D575, "M", "j"), - (0x1D576, "M", "k"), - (0x1D577, "M", "l"), - (0x1D578, "M", "m"), - (0x1D579, "M", "n"), - (0x1D57A, "M", "o"), - (0x1D57B, "M", "p"), - (0x1D57C, "M", "q"), - (0x1D57D, "M", "r"), - (0x1D57E, "M", "s"), - (0x1D57F, "M", "t"), - (0x1D580, "M", "u"), - (0x1D581, "M", "v"), - (0x1D582, "M", "w"), - (0x1D583, "M", "x"), - (0x1D584, "M", "y"), - (0x1D585, "M", "z"), - (0x1D586, "M", "a"), - (0x1D587, "M", "b"), - (0x1D588, "M", "c"), - (0x1D589, "M", "d"), - (0x1D58A, "M", "e"), - (0x1D58B, "M", "f"), - (0x1D58C, "M", "g"), - (0x1D58D, "M", "h"), - ] - - -def _seg_66() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D58E, "M", "i"), - (0x1D58F, "M", "j"), - (0x1D590, "M", "k"), - (0x1D591, "M", "l"), - (0x1D592, "M", "m"), - (0x1D593, "M", "n"), - (0x1D594, "M", "o"), - (0x1D595, "M", "p"), - (0x1D596, "M", "q"), - (0x1D597, "M", "r"), - (0x1D598, "M", "s"), - (0x1D599, "M", "t"), - (0x1D59A, "M", "u"), - (0x1D59B, "M", "v"), - (0x1D59C, "M", "w"), - (0x1D59D, "M", "x"), - (0x1D59E, "M", "y"), - (0x1D59F, "M", "z"), - (0x1D5A0, "M", "a"), - (0x1D5A1, "M", "b"), - (0x1D5A2, "M", "c"), - (0x1D5A3, "M", "d"), - (0x1D5A4, "M", "e"), - (0x1D5A5, "M", "f"), - (0x1D5A6, "M", "g"), - (0x1D5A7, "M", "h"), - (0x1D5A8, "M", "i"), - (0x1D5A9, "M", "j"), - (0x1D5AA, "M", "k"), - (0x1D5AB, "M", "l"), - (0x1D5AC, "M", "m"), - (0x1D5AD, "M", "n"), - (0x1D5AE, "M", "o"), - (0x1D5AF, "M", "p"), - (0x1D5B0, "M", "q"), - (0x1D5B1, "M", "r"), - (0x1D5B2, "M", "s"), - (0x1D5B3, "M", "t"), - (0x1D5B4, "M", "u"), - (0x1D5B5, "M", "v"), - (0x1D5B6, "M", "w"), - (0x1D5B7, "M", "x"), - (0x1D5B8, "M", "y"), - (0x1D5B9, "M", "z"), - (0x1D5BA, "M", "a"), - (0x1D5BB, "M", "b"), - (0x1D5BC, "M", "c"), - (0x1D5BD, "M", "d"), - (0x1D5BE, "M", "e"), - (0x1D5BF, "M", "f"), - (0x1D5C0, "M", "g"), - (0x1D5C1, "M", "h"), - (0x1D5C2, "M", "i"), - (0x1D5C3, "M", "j"), - (0x1D5C4, "M", "k"), - (0x1D5C5, "M", "l"), - (0x1D5C6, "M", "m"), - (0x1D5C7, "M", "n"), - (0x1D5C8, "M", "o"), - (0x1D5C9, "M", "p"), - (0x1D5CA, "M", "q"), - (0x1D5CB, "M", "r"), - (0x1D5CC, "M", "s"), - (0x1D5CD, "M", "t"), - (0x1D5CE, "M", "u"), - (0x1D5CF, "M", "v"), - (0x1D5D0, "M", "w"), - (0x1D5D1, "M", "x"), - (0x1D5D2, "M", "y"), - (0x1D5D3, "M", "z"), - (0x1D5D4, "M", "a"), - (0x1D5D5, "M", "b"), - (0x1D5D6, "M", "c"), - (0x1D5D7, "M", "d"), - (0x1D5D8, "M", "e"), - (0x1D5D9, "M", "f"), - (0x1D5DA, "M", "g"), - (0x1D5DB, "M", "h"), - (0x1D5DC, "M", "i"), - (0x1D5DD, "M", "j"), - (0x1D5DE, "M", "k"), - (0x1D5DF, "M", "l"), - (0x1D5E0, "M", "m"), - (0x1D5E1, "M", "n"), - (0x1D5E2, "M", "o"), - (0x1D5E3, "M", "p"), - (0x1D5E4, "M", "q"), - (0x1D5E5, "M", "r"), - (0x1D5E6, "M", "s"), - (0x1D5E7, "M", "t"), - (0x1D5E8, "M", "u"), - (0x1D5E9, "M", "v"), - (0x1D5EA, "M", "w"), - (0x1D5EB, "M", "x"), - (0x1D5EC, "M", "y"), - (0x1D5ED, "M", "z"), - (0x1D5EE, "M", "a"), - (0x1D5EF, "M", "b"), - (0x1D5F0, "M", "c"), - (0x1D5F1, "M", "d"), - ] - - -def _seg_67() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D5F2, "M", "e"), - (0x1D5F3, "M", "f"), - (0x1D5F4, "M", "g"), - (0x1D5F5, "M", "h"), - (0x1D5F6, "M", "i"), - (0x1D5F7, "M", "j"), - (0x1D5F8, "M", "k"), - (0x1D5F9, "M", "l"), - (0x1D5FA, "M", "m"), - (0x1D5FB, "M", "n"), - (0x1D5FC, "M", "o"), - (0x1D5FD, "M", "p"), - (0x1D5FE, "M", "q"), - (0x1D5FF, "M", "r"), - (0x1D600, "M", "s"), - (0x1D601, "M", "t"), - (0x1D602, "M", "u"), - (0x1D603, "M", "v"), - (0x1D604, "M", "w"), - (0x1D605, "M", "x"), - (0x1D606, "M", "y"), - (0x1D607, "M", "z"), - (0x1D608, "M", "a"), - (0x1D609, "M", "b"), - (0x1D60A, "M", "c"), - (0x1D60B, "M", "d"), - (0x1D60C, "M", "e"), - (0x1D60D, "M", "f"), - (0x1D60E, "M", "g"), - (0x1D60F, "M", "h"), - (0x1D610, "M", "i"), - (0x1D611, "M", "j"), - (0x1D612, "M", "k"), - (0x1D613, "M", "l"), - (0x1D614, "M", "m"), - (0x1D615, "M", "n"), - (0x1D616, "M", "o"), - (0x1D617, "M", "p"), - (0x1D618, "M", "q"), - (0x1D619, "M", "r"), - (0x1D61A, "M", "s"), - (0x1D61B, "M", "t"), - (0x1D61C, "M", "u"), - (0x1D61D, "M", "v"), - (0x1D61E, "M", "w"), - (0x1D61F, "M", "x"), - (0x1D620, "M", "y"), - (0x1D621, "M", "z"), - (0x1D622, "M", "a"), - (0x1D623, "M", "b"), - (0x1D624, "M", "c"), - (0x1D625, "M", "d"), - (0x1D626, "M", "e"), - (0x1D627, "M", "f"), - (0x1D628, "M", "g"), - (0x1D629, "M", "h"), - (0x1D62A, "M", "i"), - (0x1D62B, "M", "j"), - (0x1D62C, "M", "k"), - (0x1D62D, "M", "l"), - (0x1D62E, "M", "m"), - (0x1D62F, "M", "n"), - (0x1D630, "M", "o"), - (0x1D631, "M", "p"), - (0x1D632, "M", "q"), - (0x1D633, "M", "r"), - (0x1D634, "M", "s"), - (0x1D635, "M", "t"), - (0x1D636, "M", "u"), - (0x1D637, "M", "v"), - (0x1D638, "M", "w"), - (0x1D639, "M", "x"), - (0x1D63A, "M", "y"), - (0x1D63B, "M", "z"), - (0x1D63C, "M", "a"), - (0x1D63D, "M", "b"), - (0x1D63E, "M", "c"), - (0x1D63F, "M", "d"), - (0x1D640, "M", "e"), - (0x1D641, "M", "f"), - (0x1D642, "M", "g"), - (0x1D643, "M", "h"), - (0x1D644, "M", "i"), - (0x1D645, "M", "j"), - (0x1D646, "M", "k"), - (0x1D647, "M", "l"), - (0x1D648, "M", "m"), - (0x1D649, "M", "n"), - (0x1D64A, "M", "o"), - (0x1D64B, "M", "p"), - (0x1D64C, "M", "q"), - (0x1D64D, "M", "r"), - (0x1D64E, "M", "s"), - (0x1D64F, "M", "t"), - (0x1D650, "M", "u"), - (0x1D651, "M", "v"), - (0x1D652, "M", "w"), - (0x1D653, "M", "x"), - (0x1D654, "M", "y"), - (0x1D655, "M", "z"), - ] - - -def _seg_68() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D656, "M", "a"), - (0x1D657, "M", "b"), - (0x1D658, "M", "c"), - (0x1D659, "M", "d"), - (0x1D65A, "M", "e"), - (0x1D65B, "M", "f"), - (0x1D65C, "M", "g"), - (0x1D65D, "M", "h"), - (0x1D65E, "M", "i"), - (0x1D65F, "M", "j"), - (0x1D660, "M", "k"), - (0x1D661, "M", "l"), - (0x1D662, "M", "m"), - (0x1D663, "M", "n"), - (0x1D664, "M", "o"), - (0x1D665, "M", "p"), - (0x1D666, "M", "q"), - (0x1D667, "M", "r"), - (0x1D668, "M", "s"), - (0x1D669, "M", "t"), - (0x1D66A, "M", "u"), - (0x1D66B, "M", "v"), - (0x1D66C, "M", "w"), - (0x1D66D, "M", "x"), - (0x1D66E, "M", "y"), - (0x1D66F, "M", "z"), - (0x1D670, "M", "a"), - (0x1D671, "M", "b"), - (0x1D672, "M", "c"), - (0x1D673, "M", "d"), - (0x1D674, "M", "e"), - (0x1D675, "M", "f"), - (0x1D676, "M", "g"), - (0x1D677, "M", "h"), - (0x1D678, "M", "i"), - (0x1D679, "M", "j"), - (0x1D67A, "M", "k"), - (0x1D67B, "M", "l"), - (0x1D67C, "M", "m"), - (0x1D67D, "M", "n"), - (0x1D67E, "M", "o"), - (0x1D67F, "M", "p"), - (0x1D680, "M", "q"), - (0x1D681, "M", "r"), - (0x1D682, "M", "s"), - (0x1D683, "M", "t"), - (0x1D684, "M", "u"), - (0x1D685, "M", "v"), - (0x1D686, "M", "w"), - (0x1D687, "M", "x"), - (0x1D688, "M", "y"), - (0x1D689, "M", "z"), - (0x1D68A, "M", "a"), - (0x1D68B, "M", "b"), - (0x1D68C, "M", "c"), - (0x1D68D, "M", "d"), - (0x1D68E, "M", "e"), - (0x1D68F, "M", "f"), - (0x1D690, "M", "g"), - (0x1D691, "M", "h"), - (0x1D692, "M", "i"), - (0x1D693, "M", "j"), - (0x1D694, "M", "k"), - (0x1D695, "M", "l"), - (0x1D696, "M", "m"), - (0x1D697, "M", "n"), - (0x1D698, "M", "o"), - (0x1D699, "M", "p"), - (0x1D69A, "M", "q"), - (0x1D69B, "M", "r"), - (0x1D69C, "M", "s"), - (0x1D69D, "M", "t"), - (0x1D69E, "M", "u"), - (0x1D69F, "M", "v"), - (0x1D6A0, "M", "w"), - (0x1D6A1, "M", "x"), - (0x1D6A2, "M", "y"), - (0x1D6A3, "M", "z"), - (0x1D6A4, "M", "ı"), - (0x1D6A5, "M", "ȷ"), - (0x1D6A6, "X"), - (0x1D6A8, "M", "α"), - (0x1D6A9, "M", "β"), - (0x1D6AA, "M", "γ"), - (0x1D6AB, "M", "δ"), - (0x1D6AC, "M", "ε"), - (0x1D6AD, "M", "ζ"), - (0x1D6AE, "M", "η"), - (0x1D6AF, "M", "θ"), - (0x1D6B0, "M", "ι"), - (0x1D6B1, "M", "κ"), - (0x1D6B2, "M", "λ"), - (0x1D6B3, "M", "μ"), - (0x1D6B4, "M", "ν"), - (0x1D6B5, "M", "ξ"), - (0x1D6B6, "M", "ο"), - (0x1D6B7, "M", "π"), - (0x1D6B8, "M", "ρ"), - (0x1D6B9, "M", "θ"), - (0x1D6BA, "M", "σ"), - ] - - -def _seg_69() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D6BB, "M", "τ"), - (0x1D6BC, "M", "υ"), - (0x1D6BD, "M", "φ"), - (0x1D6BE, "M", "χ"), - (0x1D6BF, "M", "ψ"), - (0x1D6C0, "M", "ω"), - (0x1D6C1, "M", "∇"), - (0x1D6C2, "M", "α"), - (0x1D6C3, "M", "β"), - (0x1D6C4, "M", "γ"), - (0x1D6C5, "M", "δ"), - (0x1D6C6, "M", "ε"), - (0x1D6C7, "M", "ζ"), - (0x1D6C8, "M", "η"), - (0x1D6C9, "M", "θ"), - (0x1D6CA, "M", "ι"), - (0x1D6CB, "M", "κ"), - (0x1D6CC, "M", "λ"), - (0x1D6CD, "M", "μ"), - (0x1D6CE, "M", "ν"), - (0x1D6CF, "M", "ξ"), - (0x1D6D0, "M", "ο"), - (0x1D6D1, "M", "π"), - (0x1D6D2, "M", "ρ"), - (0x1D6D3, "M", "σ"), - (0x1D6D5, "M", "τ"), - (0x1D6D6, "M", "υ"), - (0x1D6D7, "M", "φ"), - (0x1D6D8, "M", "χ"), - (0x1D6D9, "M", "ψ"), - (0x1D6DA, "M", "ω"), - (0x1D6DB, "M", "∂"), - (0x1D6DC, "M", "ε"), - (0x1D6DD, "M", "θ"), - (0x1D6DE, "M", "κ"), - (0x1D6DF, "M", "φ"), - (0x1D6E0, "M", "ρ"), - (0x1D6E1, "M", "π"), - (0x1D6E2, "M", "α"), - (0x1D6E3, "M", "β"), - (0x1D6E4, "M", "γ"), - (0x1D6E5, "M", "δ"), - (0x1D6E6, "M", "ε"), - (0x1D6E7, "M", "ζ"), - (0x1D6E8, "M", "η"), - (0x1D6E9, "M", "θ"), - (0x1D6EA, "M", "ι"), - (0x1D6EB, "M", "κ"), - (0x1D6EC, "M", "λ"), - (0x1D6ED, "M", "μ"), - (0x1D6EE, "M", "ν"), - (0x1D6EF, "M", "ξ"), - (0x1D6F0, "M", "ο"), - (0x1D6F1, "M", "π"), - (0x1D6F2, "M", "ρ"), - (0x1D6F3, "M", "θ"), - (0x1D6F4, "M", "σ"), - (0x1D6F5, "M", "τ"), - (0x1D6F6, "M", "υ"), - (0x1D6F7, "M", "φ"), - (0x1D6F8, "M", "χ"), - (0x1D6F9, "M", "ψ"), - (0x1D6FA, "M", "ω"), - (0x1D6FB, "M", "∇"), - (0x1D6FC, "M", "α"), - (0x1D6FD, "M", "β"), - (0x1D6FE, "M", "γ"), - (0x1D6FF, "M", "δ"), - (0x1D700, "M", "ε"), - (0x1D701, "M", "ζ"), - (0x1D702, "M", "η"), - (0x1D703, "M", "θ"), - (0x1D704, "M", "ι"), - (0x1D705, "M", "κ"), - (0x1D706, "M", "λ"), - (0x1D707, "M", "μ"), - (0x1D708, "M", "ν"), - (0x1D709, "M", "ξ"), - (0x1D70A, "M", "ο"), - (0x1D70B, "M", "π"), - (0x1D70C, "M", "ρ"), - (0x1D70D, "M", "σ"), - (0x1D70F, "M", "τ"), - (0x1D710, "M", "υ"), - (0x1D711, "M", "φ"), - (0x1D712, "M", "χ"), - (0x1D713, "M", "ψ"), - (0x1D714, "M", "ω"), - (0x1D715, "M", "∂"), - (0x1D716, "M", "ε"), - (0x1D717, "M", "θ"), - (0x1D718, "M", "κ"), - (0x1D719, "M", "φ"), - (0x1D71A, "M", "ρ"), - (0x1D71B, "M", "π"), - (0x1D71C, "M", "α"), - (0x1D71D, "M", "β"), - (0x1D71E, "M", "γ"), - (0x1D71F, "M", "δ"), - (0x1D720, "M", "ε"), - ] - - -def _seg_70() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D721, "M", "ζ"), - (0x1D722, "M", "η"), - (0x1D723, "M", "θ"), - (0x1D724, "M", "ι"), - (0x1D725, "M", "κ"), - (0x1D726, "M", "λ"), - (0x1D727, "M", "μ"), - (0x1D728, "M", "ν"), - (0x1D729, "M", "ξ"), - (0x1D72A, "M", "ο"), - (0x1D72B, "M", "π"), - (0x1D72C, "M", "ρ"), - (0x1D72D, "M", "θ"), - (0x1D72E, "M", "σ"), - (0x1D72F, "M", "τ"), - (0x1D730, "M", "υ"), - (0x1D731, "M", "φ"), - (0x1D732, "M", "χ"), - (0x1D733, "M", "ψ"), - (0x1D734, "M", "ω"), - (0x1D735, "M", "∇"), - (0x1D736, "M", "α"), - (0x1D737, "M", "β"), - (0x1D738, "M", "γ"), - (0x1D739, "M", "δ"), - (0x1D73A, "M", "ε"), - (0x1D73B, "M", "ζ"), - (0x1D73C, "M", "η"), - (0x1D73D, "M", "θ"), - (0x1D73E, "M", "ι"), - (0x1D73F, "M", "κ"), - (0x1D740, "M", "λ"), - (0x1D741, "M", "μ"), - (0x1D742, "M", "ν"), - (0x1D743, "M", "ξ"), - (0x1D744, "M", "ο"), - (0x1D745, "M", "π"), - (0x1D746, "M", "ρ"), - (0x1D747, "M", "σ"), - (0x1D749, "M", "τ"), - (0x1D74A, "M", "υ"), - (0x1D74B, "M", "φ"), - (0x1D74C, "M", "χ"), - (0x1D74D, "M", "ψ"), - (0x1D74E, "M", "ω"), - (0x1D74F, "M", "∂"), - (0x1D750, "M", "ε"), - (0x1D751, "M", "θ"), - (0x1D752, "M", "κ"), - (0x1D753, "M", "φ"), - (0x1D754, "M", "ρ"), - (0x1D755, "M", "π"), - (0x1D756, "M", "α"), - (0x1D757, "M", "β"), - (0x1D758, "M", "γ"), - (0x1D759, "M", "δ"), - (0x1D75A, "M", "ε"), - (0x1D75B, "M", "ζ"), - (0x1D75C, "M", "η"), - (0x1D75D, "M", "θ"), - (0x1D75E, "M", "ι"), - (0x1D75F, "M", "κ"), - (0x1D760, "M", "λ"), - (0x1D761, "M", "μ"), - (0x1D762, "M", "ν"), - (0x1D763, "M", "ξ"), - (0x1D764, "M", "ο"), - (0x1D765, "M", "π"), - (0x1D766, "M", "ρ"), - (0x1D767, "M", "θ"), - (0x1D768, "M", "σ"), - (0x1D769, "M", "τ"), - (0x1D76A, "M", "υ"), - (0x1D76B, "M", "φ"), - (0x1D76C, "M", "χ"), - (0x1D76D, "M", "ψ"), - (0x1D76E, "M", "ω"), - (0x1D76F, "M", "∇"), - (0x1D770, "M", "α"), - (0x1D771, "M", "β"), - (0x1D772, "M", "γ"), - (0x1D773, "M", "δ"), - (0x1D774, "M", "ε"), - (0x1D775, "M", "ζ"), - (0x1D776, "M", "η"), - (0x1D777, "M", "θ"), - (0x1D778, "M", "ι"), - (0x1D779, "M", "κ"), - (0x1D77A, "M", "λ"), - (0x1D77B, "M", "μ"), - (0x1D77C, "M", "ν"), - (0x1D77D, "M", "ξ"), - (0x1D77E, "M", "ο"), - (0x1D77F, "M", "π"), - (0x1D780, "M", "ρ"), - (0x1D781, "M", "σ"), - (0x1D783, "M", "τ"), - (0x1D784, "M", "υ"), - (0x1D785, "M", "φ"), - (0x1D786, "M", "χ"), - ] - - -def _seg_71() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D787, "M", "ψ"), - (0x1D788, "M", "ω"), - (0x1D789, "M", "∂"), - (0x1D78A, "M", "ε"), - (0x1D78B, "M", "θ"), - (0x1D78C, "M", "κ"), - (0x1D78D, "M", "φ"), - (0x1D78E, "M", "ρ"), - (0x1D78F, "M", "π"), - (0x1D790, "M", "α"), - (0x1D791, "M", "β"), - (0x1D792, "M", "γ"), - (0x1D793, "M", "δ"), - (0x1D794, "M", "ε"), - (0x1D795, "M", "ζ"), - (0x1D796, "M", "η"), - (0x1D797, "M", "θ"), - (0x1D798, "M", "ι"), - (0x1D799, "M", "κ"), - (0x1D79A, "M", "λ"), - (0x1D79B, "M", "μ"), - (0x1D79C, "M", "ν"), - (0x1D79D, "M", "ξ"), - (0x1D79E, "M", "ο"), - (0x1D79F, "M", "π"), - (0x1D7A0, "M", "ρ"), - (0x1D7A1, "M", "θ"), - (0x1D7A2, "M", "σ"), - (0x1D7A3, "M", "τ"), - (0x1D7A4, "M", "υ"), - (0x1D7A5, "M", "φ"), - (0x1D7A6, "M", "χ"), - (0x1D7A7, "M", "ψ"), - (0x1D7A8, "M", "ω"), - (0x1D7A9, "M", "∇"), - (0x1D7AA, "M", "α"), - (0x1D7AB, "M", "β"), - (0x1D7AC, "M", "γ"), - (0x1D7AD, "M", "δ"), - (0x1D7AE, "M", "ε"), - (0x1D7AF, "M", "ζ"), - (0x1D7B0, "M", "η"), - (0x1D7B1, "M", "θ"), - (0x1D7B2, "M", "ι"), - (0x1D7B3, "M", "κ"), - (0x1D7B4, "M", "λ"), - (0x1D7B5, "M", "μ"), - (0x1D7B6, "M", "ν"), - (0x1D7B7, "M", "ξ"), - (0x1D7B8, "M", "ο"), - (0x1D7B9, "M", "π"), - (0x1D7BA, "M", "ρ"), - (0x1D7BB, "M", "σ"), - (0x1D7BD, "M", "τ"), - (0x1D7BE, "M", "υ"), - (0x1D7BF, "M", "φ"), - (0x1D7C0, "M", "χ"), - (0x1D7C1, "M", "ψ"), - (0x1D7C2, "M", "ω"), - (0x1D7C3, "M", "∂"), - (0x1D7C4, "M", "ε"), - (0x1D7C5, "M", "θ"), - (0x1D7C6, "M", "κ"), - (0x1D7C7, "M", "φ"), - (0x1D7C8, "M", "ρ"), - (0x1D7C9, "M", "π"), - (0x1D7CA, "M", "ϝ"), - (0x1D7CC, "X"), - (0x1D7CE, "M", "0"), - (0x1D7CF, "M", "1"), - (0x1D7D0, "M", "2"), - (0x1D7D1, "M", "3"), - (0x1D7D2, "M", "4"), - (0x1D7D3, "M", "5"), - (0x1D7D4, "M", "6"), - (0x1D7D5, "M", "7"), - (0x1D7D6, "M", "8"), - (0x1D7D7, "M", "9"), - (0x1D7D8, "M", "0"), - (0x1D7D9, "M", "1"), - (0x1D7DA, "M", "2"), - (0x1D7DB, "M", "3"), - (0x1D7DC, "M", "4"), - (0x1D7DD, "M", "5"), - (0x1D7DE, "M", "6"), - (0x1D7DF, "M", "7"), - (0x1D7E0, "M", "8"), - (0x1D7E1, "M", "9"), - (0x1D7E2, "M", "0"), - (0x1D7E3, "M", "1"), - (0x1D7E4, "M", "2"), - (0x1D7E5, "M", "3"), - (0x1D7E6, "M", "4"), - (0x1D7E7, "M", "5"), - (0x1D7E8, "M", "6"), - (0x1D7E9, "M", "7"), - (0x1D7EA, "M", "8"), - (0x1D7EB, "M", "9"), - (0x1D7EC, "M", "0"), - (0x1D7ED, "M", "1"), - ] - - -def _seg_72() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D7EE, "M", "2"), - (0x1D7EF, "M", "3"), - (0x1D7F0, "M", "4"), - (0x1D7F1, "M", "5"), - (0x1D7F2, "M", "6"), - (0x1D7F3, "M", "7"), - (0x1D7F4, "M", "8"), - (0x1D7F5, "M", "9"), - (0x1D7F6, "M", "0"), - (0x1D7F7, "M", "1"), - (0x1D7F8, "M", "2"), - (0x1D7F9, "M", "3"), - (0x1D7FA, "M", "4"), - (0x1D7FB, "M", "5"), - (0x1D7FC, "M", "6"), - (0x1D7FD, "M", "7"), - (0x1D7FE, "M", "8"), - (0x1D7FF, "M", "9"), - (0x1D800, "V"), - (0x1DA8C, "X"), - (0x1DA9B, "V"), - (0x1DAA0, "X"), - (0x1DAA1, "V"), - (0x1DAB0, "X"), - (0x1DF00, "V"), - (0x1DF1F, "X"), - (0x1DF25, "V"), - (0x1DF2B, "X"), - (0x1E000, "V"), - (0x1E007, "X"), - (0x1E008, "V"), - (0x1E019, "X"), - (0x1E01B, "V"), - (0x1E022, "X"), - (0x1E023, "V"), - (0x1E025, "X"), - (0x1E026, "V"), - (0x1E02B, "X"), - (0x1E030, "M", "а"), - (0x1E031, "M", "б"), - (0x1E032, "M", "в"), - (0x1E033, "M", "г"), - (0x1E034, "M", "д"), - (0x1E035, "M", "е"), - (0x1E036, "M", "ж"), - (0x1E037, "M", "з"), - (0x1E038, "M", "и"), - (0x1E039, "M", "к"), - (0x1E03A, "M", "л"), - (0x1E03B, "M", "м"), - (0x1E03C, "M", "о"), - (0x1E03D, "M", "п"), - (0x1E03E, "M", "р"), - (0x1E03F, "M", "с"), - (0x1E040, "M", "т"), - (0x1E041, "M", "у"), - (0x1E042, "M", "ф"), - (0x1E043, "M", "х"), - (0x1E044, "M", "ц"), - (0x1E045, "M", "ч"), - (0x1E046, "M", "ш"), - (0x1E047, "M", "ы"), - (0x1E048, "M", "э"), - (0x1E049, "M", "ю"), - (0x1E04A, "M", "ꚉ"), - (0x1E04B, "M", "ә"), - (0x1E04C, "M", "і"), - (0x1E04D, "M", "ј"), - (0x1E04E, "M", "ө"), - (0x1E04F, "M", "ү"), - (0x1E050, "M", "ӏ"), - (0x1E051, "M", "а"), - (0x1E052, "M", "б"), - (0x1E053, "M", "в"), - (0x1E054, "M", "г"), - (0x1E055, "M", "д"), - (0x1E056, "M", "е"), - (0x1E057, "M", "ж"), - (0x1E058, "M", "з"), - (0x1E059, "M", "и"), - (0x1E05A, "M", "к"), - (0x1E05B, "M", "л"), - (0x1E05C, "M", "о"), - (0x1E05D, "M", "п"), - (0x1E05E, "M", "с"), - (0x1E05F, "M", "у"), - (0x1E060, "M", "ф"), - (0x1E061, "M", "х"), - (0x1E062, "M", "ц"), - (0x1E063, "M", "ч"), - (0x1E064, "M", "ш"), - (0x1E065, "M", "ъ"), - (0x1E066, "M", "ы"), - (0x1E067, "M", "ґ"), - (0x1E068, "M", "і"), - (0x1E069, "M", "ѕ"), - (0x1E06A, "M", "џ"), - (0x1E06B, "M", "ҫ"), - (0x1E06C, "M", "ꙑ"), - (0x1E06D, "M", "ұ"), - ] - - -def _seg_73() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1E06E, "X"), - (0x1E08F, "V"), - (0x1E090, "X"), - (0x1E100, "V"), - (0x1E12D, "X"), - (0x1E130, "V"), - (0x1E13E, "X"), - (0x1E140, "V"), - (0x1E14A, "X"), - (0x1E14E, "V"), - (0x1E150, "X"), - (0x1E290, "V"), - (0x1E2AF, "X"), - (0x1E2C0, "V"), - (0x1E2FA, "X"), - (0x1E2FF, "V"), - (0x1E300, "X"), - (0x1E4D0, "V"), - (0x1E4FA, "X"), - (0x1E5D0, "V"), - (0x1E5FB, "X"), - (0x1E5FF, "V"), - (0x1E600, "X"), - (0x1E7E0, "V"), - (0x1E7E7, "X"), - (0x1E7E8, "V"), - (0x1E7EC, "X"), - (0x1E7ED, "V"), - (0x1E7EF, "X"), - (0x1E7F0, "V"), - (0x1E7FF, "X"), - (0x1E800, "V"), - (0x1E8C5, "X"), - (0x1E8C7, "V"), - (0x1E8D7, "X"), - (0x1E900, "M", "𞤢"), - (0x1E901, "M", "𞤣"), - (0x1E902, "M", "𞤤"), - (0x1E903, "M", "𞤥"), - (0x1E904, "M", "𞤦"), - (0x1E905, "M", "𞤧"), - (0x1E906, "M", "𞤨"), - (0x1E907, "M", "𞤩"), - (0x1E908, "M", "𞤪"), - (0x1E909, "M", "𞤫"), - (0x1E90A, "M", "𞤬"), - (0x1E90B, "M", "𞤭"), - (0x1E90C, "M", "𞤮"), - (0x1E90D, "M", "𞤯"), - (0x1E90E, "M", "𞤰"), - (0x1E90F, "M", "𞤱"), - (0x1E910, "M", "𞤲"), - (0x1E911, "M", "𞤳"), - (0x1E912, "M", "𞤴"), - (0x1E913, "M", "𞤵"), - (0x1E914, "M", "𞤶"), - (0x1E915, "M", "𞤷"), - (0x1E916, "M", "𞤸"), - (0x1E917, "M", "𞤹"), - (0x1E918, "M", "𞤺"), - (0x1E919, "M", "𞤻"), - (0x1E91A, "M", "𞤼"), - (0x1E91B, "M", "𞤽"), - (0x1E91C, "M", "𞤾"), - (0x1E91D, "M", "𞤿"), - (0x1E91E, "M", "𞥀"), - (0x1E91F, "M", "𞥁"), - (0x1E920, "M", "𞥂"), - (0x1E921, "M", "𞥃"), - (0x1E922, "V"), - (0x1E94C, "X"), - (0x1E950, "V"), - (0x1E95A, "X"), - (0x1E95E, "V"), - (0x1E960, "X"), - (0x1EC71, "V"), - (0x1ECB5, "X"), - (0x1ED01, "V"), - (0x1ED3E, "X"), - (0x1EE00, "M", "ا"), - (0x1EE01, "M", "ب"), - (0x1EE02, "M", "ج"), - (0x1EE03, "M", "د"), - (0x1EE04, "X"), - (0x1EE05, "M", "و"), - (0x1EE06, "M", "ز"), - (0x1EE07, "M", "ح"), - (0x1EE08, "M", "ط"), - (0x1EE09, "M", "ي"), - (0x1EE0A, "M", "ك"), - (0x1EE0B, "M", "ل"), - (0x1EE0C, "M", "م"), - (0x1EE0D, "M", "ن"), - (0x1EE0E, "M", "س"), - (0x1EE0F, "M", "ع"), - (0x1EE10, "M", "ف"), - (0x1EE11, "M", "ص"), - (0x1EE12, "M", "ق"), - (0x1EE13, "M", "ر"), - (0x1EE14, "M", "ش"), - ] - - -def _seg_74() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1EE15, "M", "ت"), - (0x1EE16, "M", "ث"), - (0x1EE17, "M", "خ"), - (0x1EE18, "M", "ذ"), - (0x1EE19, "M", "ض"), - (0x1EE1A, "M", "ظ"), - (0x1EE1B, "M", "غ"), - (0x1EE1C, "M", "ٮ"), - (0x1EE1D, "M", "ں"), - (0x1EE1E, "M", "ڡ"), - (0x1EE1F, "M", "ٯ"), - (0x1EE20, "X"), - (0x1EE21, "M", "ب"), - (0x1EE22, "M", "ج"), - (0x1EE23, "X"), - (0x1EE24, "M", "ه"), - (0x1EE25, "X"), - (0x1EE27, "M", "ح"), - (0x1EE28, "X"), - (0x1EE29, "M", "ي"), - (0x1EE2A, "M", "ك"), - (0x1EE2B, "M", "ل"), - (0x1EE2C, "M", "م"), - (0x1EE2D, "M", "ن"), - (0x1EE2E, "M", "س"), - (0x1EE2F, "M", "ع"), - (0x1EE30, "M", "ف"), - (0x1EE31, "M", "ص"), - (0x1EE32, "M", "ق"), - (0x1EE33, "X"), - (0x1EE34, "M", "ش"), - (0x1EE35, "M", "ت"), - (0x1EE36, "M", "ث"), - (0x1EE37, "M", "خ"), - (0x1EE38, "X"), - (0x1EE39, "M", "ض"), - (0x1EE3A, "X"), - (0x1EE3B, "M", "غ"), - (0x1EE3C, "X"), - (0x1EE42, "M", "ج"), - (0x1EE43, "X"), - (0x1EE47, "M", "ح"), - (0x1EE48, "X"), - (0x1EE49, "M", "ي"), - (0x1EE4A, "X"), - (0x1EE4B, "M", "ل"), - (0x1EE4C, "X"), - (0x1EE4D, "M", "ن"), - (0x1EE4E, "M", "س"), - (0x1EE4F, "M", "ع"), - (0x1EE50, "X"), - (0x1EE51, "M", "ص"), - (0x1EE52, "M", "ق"), - (0x1EE53, "X"), - (0x1EE54, "M", "ش"), - (0x1EE55, "X"), - (0x1EE57, "M", "خ"), - (0x1EE58, "X"), - (0x1EE59, "M", "ض"), - (0x1EE5A, "X"), - (0x1EE5B, "M", "غ"), - (0x1EE5C, "X"), - (0x1EE5D, "M", "ں"), - (0x1EE5E, "X"), - (0x1EE5F, "M", "ٯ"), - (0x1EE60, "X"), - (0x1EE61, "M", "ب"), - (0x1EE62, "M", "ج"), - (0x1EE63, "X"), - (0x1EE64, "M", "ه"), - (0x1EE65, "X"), - (0x1EE67, "M", "ح"), - (0x1EE68, "M", "ط"), - (0x1EE69, "M", "ي"), - (0x1EE6A, "M", "ك"), - (0x1EE6B, "X"), - (0x1EE6C, "M", "م"), - (0x1EE6D, "M", "ن"), - (0x1EE6E, "M", "س"), - (0x1EE6F, "M", "ع"), - (0x1EE70, "M", "ف"), - (0x1EE71, "M", "ص"), - (0x1EE72, "M", "ق"), - (0x1EE73, "X"), - (0x1EE74, "M", "ش"), - (0x1EE75, "M", "ت"), - (0x1EE76, "M", "ث"), - (0x1EE77, "M", "خ"), - (0x1EE78, "X"), - (0x1EE79, "M", "ض"), - (0x1EE7A, "M", "ظ"), - (0x1EE7B, "M", "غ"), - (0x1EE7C, "M", "ٮ"), - (0x1EE7D, "X"), - (0x1EE7E, "M", "ڡ"), - (0x1EE7F, "X"), - (0x1EE80, "M", "ا"), - (0x1EE81, "M", "ب"), - (0x1EE82, "M", "ج"), - (0x1EE83, "M", "د"), - ] - - -def _seg_75() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1EE84, "M", "ه"), - (0x1EE85, "M", "و"), - (0x1EE86, "M", "ز"), - (0x1EE87, "M", "ح"), - (0x1EE88, "M", "ط"), - (0x1EE89, "M", "ي"), - (0x1EE8A, "X"), - (0x1EE8B, "M", "ل"), - (0x1EE8C, "M", "م"), - (0x1EE8D, "M", "ن"), - (0x1EE8E, "M", "س"), - (0x1EE8F, "M", "ع"), - (0x1EE90, "M", "ف"), - (0x1EE91, "M", "ص"), - (0x1EE92, "M", "ق"), - (0x1EE93, "M", "ر"), - (0x1EE94, "M", "ش"), - (0x1EE95, "M", "ت"), - (0x1EE96, "M", "ث"), - (0x1EE97, "M", "خ"), - (0x1EE98, "M", "ذ"), - (0x1EE99, "M", "ض"), - (0x1EE9A, "M", "ظ"), - (0x1EE9B, "M", "غ"), - (0x1EE9C, "X"), - (0x1EEA1, "M", "ب"), - (0x1EEA2, "M", "ج"), - (0x1EEA3, "M", "د"), - (0x1EEA4, "X"), - (0x1EEA5, "M", "و"), - (0x1EEA6, "M", "ز"), - (0x1EEA7, "M", "ح"), - (0x1EEA8, "M", "ط"), - (0x1EEA9, "M", "ي"), - (0x1EEAA, "X"), - (0x1EEAB, "M", "ل"), - (0x1EEAC, "M", "م"), - (0x1EEAD, "M", "ن"), - (0x1EEAE, "M", "س"), - (0x1EEAF, "M", "ع"), - (0x1EEB0, "M", "ف"), - (0x1EEB1, "M", "ص"), - (0x1EEB2, "M", "ق"), - (0x1EEB3, "M", "ر"), - (0x1EEB4, "M", "ش"), - (0x1EEB5, "M", "ت"), - (0x1EEB6, "M", "ث"), - (0x1EEB7, "M", "خ"), - (0x1EEB8, "M", "ذ"), - (0x1EEB9, "M", "ض"), - (0x1EEBA, "M", "ظ"), - (0x1EEBB, "M", "غ"), - (0x1EEBC, "X"), - (0x1EEF0, "V"), - (0x1EEF2, "X"), - (0x1F000, "V"), - (0x1F02C, "X"), - (0x1F030, "V"), - (0x1F094, "X"), - (0x1F0A0, "V"), - (0x1F0AF, "X"), - (0x1F0B1, "V"), - (0x1F0C0, "X"), - (0x1F0C1, "V"), - (0x1F0D0, "X"), - (0x1F0D1, "V"), - (0x1F0F6, "X"), - (0x1F101, "M", "0,"), - (0x1F102, "M", "1,"), - (0x1F103, "M", "2,"), - (0x1F104, "M", "3,"), - (0x1F105, "M", "4,"), - (0x1F106, "M", "5,"), - (0x1F107, "M", "6,"), - (0x1F108, "M", "7,"), - (0x1F109, "M", "8,"), - (0x1F10A, "M", "9,"), - (0x1F10B, "V"), - (0x1F110, "M", "(a)"), - (0x1F111, "M", "(b)"), - (0x1F112, "M", "(c)"), - (0x1F113, "M", "(d)"), - (0x1F114, "M", "(e)"), - (0x1F115, "M", "(f)"), - (0x1F116, "M", "(g)"), - (0x1F117, "M", "(h)"), - (0x1F118, "M", "(i)"), - (0x1F119, "M", "(j)"), - (0x1F11A, "M", "(k)"), - (0x1F11B, "M", "(l)"), - (0x1F11C, "M", "(m)"), - (0x1F11D, "M", "(n)"), - (0x1F11E, "M", "(o)"), - (0x1F11F, "M", "(p)"), - (0x1F120, "M", "(q)"), - (0x1F121, "M", "(r)"), - (0x1F122, "M", "(s)"), - (0x1F123, "M", "(t)"), - (0x1F124, "M", "(u)"), - (0x1F125, "M", "(v)"), - ] - - -def _seg_76() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1F126, "M", "(w)"), - (0x1F127, "M", "(x)"), - (0x1F128, "M", "(y)"), - (0x1F129, "M", "(z)"), - (0x1F12A, "M", "〔s〕"), - (0x1F12B, "M", "c"), - (0x1F12C, "M", "r"), - (0x1F12D, "M", "cd"), - (0x1F12E, "M", "wz"), - (0x1F12F, "V"), - (0x1F130, "M", "a"), - (0x1F131, "M", "b"), - (0x1F132, "M", "c"), - (0x1F133, "M", "d"), - (0x1F134, "M", "e"), - (0x1F135, "M", "f"), - (0x1F136, "M", "g"), - (0x1F137, "M", "h"), - (0x1F138, "M", "i"), - (0x1F139, "M", "j"), - (0x1F13A, "M", "k"), - (0x1F13B, "M", "l"), - (0x1F13C, "M", "m"), - (0x1F13D, "M", "n"), - (0x1F13E, "M", "o"), - (0x1F13F, "M", "p"), - (0x1F140, "M", "q"), - (0x1F141, "M", "r"), - (0x1F142, "M", "s"), - (0x1F143, "M", "t"), - (0x1F144, "M", "u"), - (0x1F145, "M", "v"), - (0x1F146, "M", "w"), - (0x1F147, "M", "x"), - (0x1F148, "M", "y"), - (0x1F149, "M", "z"), - (0x1F14A, "M", "hv"), - (0x1F14B, "M", "mv"), - (0x1F14C, "M", "sd"), - (0x1F14D, "M", "ss"), - (0x1F14E, "M", "ppv"), - (0x1F14F, "M", "wc"), - (0x1F150, "V"), - (0x1F16A, "M", "mc"), - (0x1F16B, "M", "md"), - (0x1F16C, "M", "mr"), - (0x1F16D, "V"), - (0x1F190, "M", "dj"), - (0x1F191, "V"), - (0x1F1AE, "X"), - (0x1F1E6, "V"), - (0x1F200, "M", "ほか"), - (0x1F201, "M", "ココ"), - (0x1F202, "M", "サ"), - (0x1F203, "X"), - (0x1F210, "M", "手"), - (0x1F211, "M", "字"), - (0x1F212, "M", "双"), - (0x1F213, "M", "デ"), - (0x1F214, "M", "二"), - (0x1F215, "M", "多"), - (0x1F216, "M", "解"), - (0x1F217, "M", "天"), - (0x1F218, "M", "交"), - (0x1F219, "M", "映"), - (0x1F21A, "M", "無"), - (0x1F21B, "M", "料"), - (0x1F21C, "M", "前"), - (0x1F21D, "M", "後"), - (0x1F21E, "M", "再"), - (0x1F21F, "M", "新"), - (0x1F220, "M", "初"), - (0x1F221, "M", "終"), - (0x1F222, "M", "生"), - (0x1F223, "M", "販"), - (0x1F224, "M", "声"), - (0x1F225, "M", "吹"), - (0x1F226, "M", "演"), - (0x1F227, "M", "投"), - (0x1F228, "M", "捕"), - (0x1F229, "M", "一"), - (0x1F22A, "M", "三"), - (0x1F22B, "M", "遊"), - (0x1F22C, "M", "左"), - (0x1F22D, "M", "中"), - (0x1F22E, "M", "右"), - (0x1F22F, "M", "指"), - (0x1F230, "M", "走"), - (0x1F231, "M", "打"), - (0x1F232, "M", "禁"), - (0x1F233, "M", "空"), - (0x1F234, "M", "合"), - (0x1F235, "M", "満"), - (0x1F236, "M", "有"), - (0x1F237, "M", "月"), - (0x1F238, "M", "申"), - (0x1F239, "M", "割"), - (0x1F23A, "M", "営"), - (0x1F23B, "M", "配"), - (0x1F23C, "X"), - ] - - -def _seg_77() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1F240, "M", "〔本〕"), - (0x1F241, "M", "〔三〕"), - (0x1F242, "M", "〔二〕"), - (0x1F243, "M", "〔安〕"), - (0x1F244, "M", "〔点〕"), - (0x1F245, "M", "〔打〕"), - (0x1F246, "M", "〔盗〕"), - (0x1F247, "M", "〔勝〕"), - (0x1F248, "M", "〔敗〕"), - (0x1F249, "X"), - (0x1F250, "M", "得"), - (0x1F251, "M", "可"), - (0x1F252, "X"), - (0x1F260, "V"), - (0x1F266, "X"), - (0x1F300, "V"), - (0x1F6D8, "X"), - (0x1F6DC, "V"), - (0x1F6ED, "X"), - (0x1F6F0, "V"), - (0x1F6FD, "X"), - (0x1F700, "V"), - (0x1F777, "X"), - (0x1F77B, "V"), - (0x1F7DA, "X"), - (0x1F7E0, "V"), - (0x1F7EC, "X"), - (0x1F7F0, "V"), - (0x1F7F1, "X"), - (0x1F800, "V"), - (0x1F80C, "X"), - (0x1F810, "V"), - (0x1F848, "X"), - (0x1F850, "V"), - (0x1F85A, "X"), - (0x1F860, "V"), - (0x1F888, "X"), - (0x1F890, "V"), - (0x1F8AE, "X"), - (0x1F8B0, "V"), - (0x1F8BC, "X"), - (0x1F8C0, "V"), - (0x1F8C2, "X"), - (0x1F900, "V"), - (0x1FA54, "X"), - (0x1FA60, "V"), - (0x1FA6E, "X"), - (0x1FA70, "V"), - (0x1FA7D, "X"), - (0x1FA80, "V"), - (0x1FA8A, "X"), - (0x1FA8F, "V"), - (0x1FAC7, "X"), - (0x1FACE, "V"), - (0x1FADD, "X"), - (0x1FADF, "V"), - (0x1FAEA, "X"), - (0x1FAF0, "V"), - (0x1FAF9, "X"), - (0x1FB00, "V"), - (0x1FB93, "X"), - (0x1FB94, "V"), - (0x1FBF0, "M", "0"), - (0x1FBF1, "M", "1"), - (0x1FBF2, "M", "2"), - (0x1FBF3, "M", "3"), - (0x1FBF4, "M", "4"), - (0x1FBF5, "M", "5"), - (0x1FBF6, "M", "6"), - (0x1FBF7, "M", "7"), - (0x1FBF8, "M", "8"), - (0x1FBF9, "M", "9"), - (0x1FBFA, "X"), - (0x20000, "V"), - (0x2A6E0, "X"), - (0x2A700, "V"), - (0x2B73A, "X"), - (0x2B740, "V"), - (0x2B81E, "X"), - (0x2B820, "V"), - (0x2CEA2, "X"), - (0x2CEB0, "V"), - (0x2EBE1, "X"), - (0x2EBF0, "V"), - (0x2EE5E, "X"), - (0x2F800, "M", "丽"), - (0x2F801, "M", "丸"), - (0x2F802, "M", "乁"), - (0x2F803, "M", "𠄢"), - (0x2F804, "M", "你"), - (0x2F805, "M", "侮"), - (0x2F806, "M", "侻"), - (0x2F807, "M", "倂"), - (0x2F808, "M", "偺"), - (0x2F809, "M", "備"), - (0x2F80A, "M", "僧"), - (0x2F80B, "M", "像"), - (0x2F80C, "M", "㒞"), - (0x2F80D, "M", "𠘺"), - (0x2F80E, "M", "免"), - ] - - -def _seg_78() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F80F, "M", "兔"), - (0x2F810, "M", "兤"), - (0x2F811, "M", "具"), - (0x2F812, "M", "𠔜"), - (0x2F813, "M", "㒹"), - (0x2F814, "M", "內"), - (0x2F815, "M", "再"), - (0x2F816, "M", "𠕋"), - (0x2F817, "M", "冗"), - (0x2F818, "M", "冤"), - (0x2F819, "M", "仌"), - (0x2F81A, "M", "冬"), - (0x2F81B, "M", "况"), - (0x2F81C, "M", "𩇟"), - (0x2F81D, "M", "凵"), - (0x2F81E, "M", "刃"), - (0x2F81F, "M", "㓟"), - (0x2F820, "M", "刻"), - (0x2F821, "M", "剆"), - (0x2F822, "M", "割"), - (0x2F823, "M", "剷"), - (0x2F824, "M", "㔕"), - (0x2F825, "M", "勇"), - (0x2F826, "M", "勉"), - (0x2F827, "M", "勤"), - (0x2F828, "M", "勺"), - (0x2F829, "M", "包"), - (0x2F82A, "M", "匆"), - (0x2F82B, "M", "北"), - (0x2F82C, "M", "卉"), - (0x2F82D, "M", "卑"), - (0x2F82E, "M", "博"), - (0x2F82F, "M", "即"), - (0x2F830, "M", "卽"), - (0x2F831, "M", "卿"), - (0x2F834, "M", "𠨬"), - (0x2F835, "M", "灰"), - (0x2F836, "M", "及"), - (0x2F837, "M", "叟"), - (0x2F838, "M", "𠭣"), - (0x2F839, "M", "叫"), - (0x2F83A, "M", "叱"), - (0x2F83B, "M", "吆"), - (0x2F83C, "M", "咞"), - (0x2F83D, "M", "吸"), - (0x2F83E, "M", "呈"), - (0x2F83F, "M", "周"), - (0x2F840, "M", "咢"), - (0x2F841, "M", "哶"), - (0x2F842, "M", "唐"), - (0x2F843, "M", "啓"), - (0x2F844, "M", "啣"), - (0x2F845, "M", "善"), - (0x2F847, "M", "喙"), - (0x2F848, "M", "喫"), - (0x2F849, "M", "喳"), - (0x2F84A, "M", "嗂"), - (0x2F84B, "M", "圖"), - (0x2F84C, "M", "嘆"), - (0x2F84D, "M", "圗"), - (0x2F84E, "M", "噑"), - (0x2F84F, "M", "噴"), - (0x2F850, "M", "切"), - (0x2F851, "M", "壮"), - (0x2F852, "M", "城"), - (0x2F853, "M", "埴"), - (0x2F854, "M", "堍"), - (0x2F855, "M", "型"), - (0x2F856, "M", "堲"), - (0x2F857, "M", "報"), - (0x2F858, "M", "墬"), - (0x2F859, "M", "𡓤"), - (0x2F85A, "M", "売"), - (0x2F85B, "M", "壷"), - (0x2F85C, "M", "夆"), - (0x2F85D, "M", "多"), - (0x2F85E, "M", "夢"), - (0x2F85F, "M", "奢"), - (0x2F860, "M", "𡚨"), - (0x2F861, "M", "𡛪"), - (0x2F862, "M", "姬"), - (0x2F863, "M", "娛"), - (0x2F864, "M", "娧"), - (0x2F865, "M", "姘"), - (0x2F866, "M", "婦"), - (0x2F867, "M", "㛮"), - (0x2F868, "M", "㛼"), - (0x2F869, "M", "嬈"), - (0x2F86A, "M", "嬾"), - (0x2F86C, "M", "𡧈"), - (0x2F86D, "M", "寃"), - (0x2F86E, "M", "寘"), - (0x2F86F, "M", "寧"), - (0x2F870, "M", "寳"), - (0x2F871, "M", "𡬘"), - (0x2F872, "M", "寿"), - (0x2F873, "M", "将"), - (0x2F874, "M", "当"), - (0x2F875, "M", "尢"), - (0x2F876, "M", "㞁"), - ] - - -def _seg_79() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F877, "M", "屠"), - (0x2F878, "M", "屮"), - (0x2F879, "M", "峀"), - (0x2F87A, "M", "岍"), - (0x2F87B, "M", "𡷤"), - (0x2F87C, "M", "嵃"), - (0x2F87D, "M", "𡷦"), - (0x2F87E, "M", "嵮"), - (0x2F87F, "M", "嵫"), - (0x2F880, "M", "嵼"), - (0x2F881, "M", "巡"), - (0x2F882, "M", "巢"), - (0x2F883, "M", "㠯"), - (0x2F884, "M", "巽"), - (0x2F885, "M", "帨"), - (0x2F886, "M", "帽"), - (0x2F887, "M", "幩"), - (0x2F888, "M", "㡢"), - (0x2F889, "M", "𢆃"), - (0x2F88A, "M", "㡼"), - (0x2F88B, "M", "庰"), - (0x2F88C, "M", "庳"), - (0x2F88D, "M", "庶"), - (0x2F88E, "M", "廊"), - (0x2F88F, "M", "𪎒"), - (0x2F890, "M", "廾"), - (0x2F891, "M", "𢌱"), - (0x2F893, "M", "舁"), - (0x2F894, "M", "弢"), - (0x2F896, "M", "㣇"), - (0x2F897, "M", "𣊸"), - (0x2F898, "M", "𦇚"), - (0x2F899, "M", "形"), - (0x2F89A, "M", "彫"), - (0x2F89B, "M", "㣣"), - (0x2F89C, "M", "徚"), - (0x2F89D, "M", "忍"), - (0x2F89E, "M", "志"), - (0x2F89F, "M", "忹"), - (0x2F8A0, "M", "悁"), - (0x2F8A1, "M", "㤺"), - (0x2F8A2, "M", "㤜"), - (0x2F8A3, "M", "悔"), - (0x2F8A4, "M", "𢛔"), - (0x2F8A5, "M", "惇"), - (0x2F8A6, "M", "慈"), - (0x2F8A7, "M", "慌"), - (0x2F8A8, "M", "慎"), - (0x2F8A9, "M", "慌"), - (0x2F8AA, "M", "慺"), - (0x2F8AB, "M", "憎"), - (0x2F8AC, "M", "憲"), - (0x2F8AD, "M", "憤"), - (0x2F8AE, "M", "憯"), - (0x2F8AF, "M", "懞"), - (0x2F8B0, "M", "懲"), - (0x2F8B1, "M", "懶"), - (0x2F8B2, "M", "成"), - (0x2F8B3, "M", "戛"), - (0x2F8B4, "M", "扝"), - (0x2F8B5, "M", "抱"), - (0x2F8B6, "M", "拔"), - (0x2F8B7, "M", "捐"), - (0x2F8B8, "M", "𢬌"), - (0x2F8B9, "M", "挽"), - (0x2F8BA, "M", "拼"), - (0x2F8BB, "M", "捨"), - (0x2F8BC, "M", "掃"), - (0x2F8BD, "M", "揤"), - (0x2F8BE, "M", "𢯱"), - (0x2F8BF, "M", "搢"), - (0x2F8C0, "M", "揅"), - (0x2F8C1, "M", "掩"), - (0x2F8C2, "M", "㨮"), - (0x2F8C3, "M", "摩"), - (0x2F8C4, "M", "摾"), - (0x2F8C5, "M", "撝"), - (0x2F8C6, "M", "摷"), - (0x2F8C7, "M", "㩬"), - (0x2F8C8, "M", "敏"), - (0x2F8C9, "M", "敬"), - (0x2F8CA, "M", "𣀊"), - (0x2F8CB, "M", "旣"), - (0x2F8CC, "M", "書"), - (0x2F8CD, "M", "晉"), - (0x2F8CE, "M", "㬙"), - (0x2F8CF, "M", "暑"), - (0x2F8D0, "M", "㬈"), - (0x2F8D1, "M", "㫤"), - (0x2F8D2, "M", "冒"), - (0x2F8D3, "M", "冕"), - (0x2F8D4, "M", "最"), - (0x2F8D5, "M", "暜"), - (0x2F8D6, "M", "肭"), - (0x2F8D7, "M", "䏙"), - (0x2F8D8, "M", "朗"), - (0x2F8D9, "M", "望"), - (0x2F8DA, "M", "朡"), - (0x2F8DB, "M", "杞"), - (0x2F8DC, "M", "杓"), - ] - - -def _seg_80() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F8DD, "M", "𣏃"), - (0x2F8DE, "M", "㭉"), - (0x2F8DF, "M", "柺"), - (0x2F8E0, "M", "枅"), - (0x2F8E1, "M", "桒"), - (0x2F8E2, "M", "梅"), - (0x2F8E3, "M", "𣑭"), - (0x2F8E4, "M", "梎"), - (0x2F8E5, "M", "栟"), - (0x2F8E6, "M", "椔"), - (0x2F8E7, "M", "㮝"), - (0x2F8E8, "M", "楂"), - (0x2F8E9, "M", "榣"), - (0x2F8EA, "M", "槪"), - (0x2F8EB, "M", "檨"), - (0x2F8EC, "M", "𣚣"), - (0x2F8ED, "M", "櫛"), - (0x2F8EE, "M", "㰘"), - (0x2F8EF, "M", "次"), - (0x2F8F0, "M", "𣢧"), - (0x2F8F1, "M", "歔"), - (0x2F8F2, "M", "㱎"), - (0x2F8F3, "M", "歲"), - (0x2F8F4, "M", "殟"), - (0x2F8F5, "M", "殺"), - (0x2F8F6, "M", "殻"), - (0x2F8F7, "M", "𣪍"), - (0x2F8F8, "M", "𡴋"), - (0x2F8F9, "M", "𣫺"), - (0x2F8FA, "M", "汎"), - (0x2F8FB, "M", "𣲼"), - (0x2F8FC, "M", "沿"), - (0x2F8FD, "M", "泍"), - (0x2F8FE, "M", "汧"), - (0x2F8FF, "M", "洖"), - (0x2F900, "M", "派"), - (0x2F901, "M", "海"), - (0x2F902, "M", "流"), - (0x2F903, "M", "浩"), - (0x2F904, "M", "浸"), - (0x2F905, "M", "涅"), - (0x2F906, "M", "𣴞"), - (0x2F907, "M", "洴"), - (0x2F908, "M", "港"), - (0x2F909, "M", "湮"), - (0x2F90A, "M", "㴳"), - (0x2F90B, "M", "滋"), - (0x2F90C, "M", "滇"), - (0x2F90D, "M", "𣻑"), - (0x2F90E, "M", "淹"), - (0x2F90F, "M", "潮"), - (0x2F910, "M", "𣽞"), - (0x2F911, "M", "𣾎"), - (0x2F912, "M", "濆"), - (0x2F913, "M", "瀹"), - (0x2F914, "M", "瀞"), - (0x2F915, "M", "瀛"), - (0x2F916, "M", "㶖"), - (0x2F917, "M", "灊"), - (0x2F918, "M", "災"), - (0x2F919, "M", "灷"), - (0x2F91A, "M", "炭"), - (0x2F91B, "M", "𠔥"), - (0x2F91C, "M", "煅"), - (0x2F91D, "M", "𤉣"), - (0x2F91E, "M", "熜"), - (0x2F91F, "M", "𤎫"), - (0x2F920, "M", "爨"), - (0x2F921, "M", "爵"), - (0x2F922, "M", "牐"), - (0x2F923, "M", "𤘈"), - (0x2F924, "M", "犀"), - (0x2F925, "M", "犕"), - (0x2F926, "M", "𤜵"), - (0x2F927, "M", "𤠔"), - (0x2F928, "M", "獺"), - (0x2F929, "M", "王"), - (0x2F92A, "M", "㺬"), - (0x2F92B, "M", "玥"), - (0x2F92C, "M", "㺸"), - (0x2F92E, "M", "瑇"), - (0x2F92F, "M", "瑜"), - (0x2F930, "M", "瑱"), - (0x2F931, "M", "璅"), - (0x2F932, "M", "瓊"), - (0x2F933, "M", "㼛"), - (0x2F934, "M", "甤"), - (0x2F935, "M", "𤰶"), - (0x2F936, "M", "甾"), - (0x2F937, "M", "𤲒"), - (0x2F938, "M", "異"), - (0x2F939, "M", "𢆟"), - (0x2F93A, "M", "瘐"), - (0x2F93B, "M", "𤾡"), - (0x2F93C, "M", "𤾸"), - (0x2F93D, "M", "𥁄"), - (0x2F93E, "M", "㿼"), - (0x2F93F, "M", "䀈"), - (0x2F940, "M", "直"), - (0x2F941, "M", "𥃳"), - ] - - -def _seg_81() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F942, "M", "𥃲"), - (0x2F943, "M", "𥄙"), - (0x2F944, "M", "𥄳"), - (0x2F945, "M", "眞"), - (0x2F946, "M", "真"), - (0x2F948, "M", "睊"), - (0x2F949, "M", "䀹"), - (0x2F94A, "M", "瞋"), - (0x2F94B, "M", "䁆"), - (0x2F94C, "M", "䂖"), - (0x2F94D, "M", "𥐝"), - (0x2F94E, "M", "硎"), - (0x2F94F, "M", "碌"), - (0x2F950, "M", "磌"), - (0x2F951, "M", "䃣"), - (0x2F952, "M", "𥘦"), - (0x2F953, "M", "祖"), - (0x2F954, "M", "𥚚"), - (0x2F955, "M", "𥛅"), - (0x2F956, "M", "福"), - (0x2F957, "M", "秫"), - (0x2F958, "M", "䄯"), - (0x2F959, "M", "穀"), - (0x2F95A, "M", "穊"), - (0x2F95B, "M", "穏"), - (0x2F95C, "M", "𥥼"), - (0x2F95D, "M", "𥪧"), - (0x2F95F, "M", "竮"), - (0x2F960, "M", "䈂"), - (0x2F961, "M", "𥮫"), - (0x2F962, "M", "篆"), - (0x2F963, "M", "築"), - (0x2F964, "M", "䈧"), - (0x2F965, "M", "𥲀"), - (0x2F966, "M", "糒"), - (0x2F967, "M", "䊠"), - (0x2F968, "M", "糨"), - (0x2F969, "M", "糣"), - (0x2F96A, "M", "紀"), - (0x2F96B, "M", "𥾆"), - (0x2F96C, "M", "絣"), - (0x2F96D, "M", "䌁"), - (0x2F96E, "M", "緇"), - (0x2F96F, "M", "縂"), - (0x2F970, "M", "繅"), - (0x2F971, "M", "䌴"), - (0x2F972, "M", "𦈨"), - (0x2F973, "M", "𦉇"), - (0x2F974, "M", "䍙"), - (0x2F975, "M", "𦋙"), - (0x2F976, "M", "罺"), - (0x2F977, "M", "𦌾"), - (0x2F978, "M", "羕"), - (0x2F979, "M", "翺"), - (0x2F97A, "M", "者"), - (0x2F97B, "M", "𦓚"), - (0x2F97C, "M", "𦔣"), - (0x2F97D, "M", "聠"), - (0x2F97E, "M", "𦖨"), - (0x2F97F, "M", "聰"), - (0x2F980, "M", "𣍟"), - (0x2F981, "M", "䏕"), - (0x2F982, "M", "育"), - (0x2F983, "M", "脃"), - (0x2F984, "M", "䐋"), - (0x2F985, "M", "脾"), - (0x2F986, "M", "媵"), - (0x2F987, "M", "𦞧"), - (0x2F988, "M", "𦞵"), - (0x2F989, "M", "𣎓"), - (0x2F98A, "M", "𣎜"), - (0x2F98B, "M", "舁"), - (0x2F98C, "M", "舄"), - (0x2F98D, "M", "辞"), - (0x2F98E, "M", "䑫"), - (0x2F98F, "M", "芑"), - (0x2F990, "M", "芋"), - (0x2F991, "M", "芝"), - (0x2F992, "M", "劳"), - (0x2F993, "M", "花"), - (0x2F994, "M", "芳"), - (0x2F995, "M", "芽"), - (0x2F996, "M", "苦"), - (0x2F997, "M", "𦬼"), - (0x2F998, "M", "若"), - (0x2F999, "M", "茝"), - (0x2F99A, "M", "荣"), - (0x2F99B, "M", "莭"), - (0x2F99C, "M", "茣"), - (0x2F99D, "M", "莽"), - (0x2F99E, "M", "菧"), - (0x2F99F, "M", "著"), - (0x2F9A0, "M", "荓"), - (0x2F9A1, "M", "菊"), - (0x2F9A2, "M", "菌"), - (0x2F9A3, "M", "菜"), - (0x2F9A4, "M", "𦰶"), - (0x2F9A5, "M", "𦵫"), - (0x2F9A6, "M", "𦳕"), - (0x2F9A7, "M", "䔫"), - ] - - -def _seg_82() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F9A8, "M", "蓱"), - (0x2F9A9, "M", "蓳"), - (0x2F9AA, "M", "蔖"), - (0x2F9AB, "M", "𧏊"), - (0x2F9AC, "M", "蕤"), - (0x2F9AD, "M", "𦼬"), - (0x2F9AE, "M", "䕝"), - (0x2F9AF, "M", "䕡"), - (0x2F9B0, "M", "𦾱"), - (0x2F9B1, "M", "𧃒"), - (0x2F9B2, "M", "䕫"), - (0x2F9B3, "M", "虐"), - (0x2F9B4, "M", "虜"), - (0x2F9B5, "M", "虧"), - (0x2F9B6, "M", "虩"), - (0x2F9B7, "M", "蚩"), - (0x2F9B8, "M", "蚈"), - (0x2F9B9, "M", "蜎"), - (0x2F9BA, "M", "蛢"), - (0x2F9BB, "M", "蝹"), - (0x2F9BC, "M", "蜨"), - (0x2F9BD, "M", "蝫"), - (0x2F9BE, "M", "螆"), - (0x2F9BF, "M", "䗗"), - (0x2F9C0, "M", "蟡"), - (0x2F9C1, "M", "蠁"), - (0x2F9C2, "M", "䗹"), - (0x2F9C3, "M", "衠"), - (0x2F9C4, "M", "衣"), - (0x2F9C5, "M", "𧙧"), - (0x2F9C6, "M", "裗"), - (0x2F9C7, "M", "裞"), - (0x2F9C8, "M", "䘵"), - (0x2F9C9, "M", "裺"), - (0x2F9CA, "M", "㒻"), - (0x2F9CB, "M", "𧢮"), - (0x2F9CC, "M", "𧥦"), - (0x2F9CD, "M", "䚾"), - (0x2F9CE, "M", "䛇"), - (0x2F9CF, "M", "誠"), - (0x2F9D0, "M", "諭"), - (0x2F9D1, "M", "變"), - (0x2F9D2, "M", "豕"), - (0x2F9D3, "M", "𧲨"), - (0x2F9D4, "M", "貫"), - (0x2F9D5, "M", "賁"), - (0x2F9D6, "M", "贛"), - (0x2F9D7, "M", "起"), - (0x2F9D8, "M", "𧼯"), - (0x2F9D9, "M", "𠠄"), - (0x2F9DA, "M", "跋"), - (0x2F9DB, "M", "趼"), - (0x2F9DC, "M", "跰"), - (0x2F9DD, "M", "𠣞"), - (0x2F9DE, "M", "軔"), - (0x2F9DF, "M", "輸"), - (0x2F9E0, "M", "𨗒"), - (0x2F9E1, "M", "𨗭"), - (0x2F9E2, "M", "邔"), - (0x2F9E3, "M", "郱"), - (0x2F9E4, "M", "鄑"), - (0x2F9E5, "M", "𨜮"), - (0x2F9E6, "M", "鄛"), - (0x2F9E7, "M", "鈸"), - (0x2F9E8, "M", "鋗"), - (0x2F9E9, "M", "鋘"), - (0x2F9EA, "M", "鉼"), - (0x2F9EB, "M", "鏹"), - (0x2F9EC, "M", "鐕"), - (0x2F9ED, "M", "𨯺"), - (0x2F9EE, "M", "開"), - (0x2F9EF, "M", "䦕"), - (0x2F9F0, "M", "閷"), - (0x2F9F1, "M", "𨵷"), - (0x2F9F2, "M", "䧦"), - (0x2F9F3, "M", "雃"), - (0x2F9F4, "M", "嶲"), - (0x2F9F5, "M", "霣"), - (0x2F9F6, "M", "𩅅"), - (0x2F9F7, "M", "𩈚"), - (0x2F9F8, "M", "䩮"), - (0x2F9F9, "M", "䩶"), - (0x2F9FA, "M", "韠"), - (0x2F9FB, "M", "𩐊"), - (0x2F9FC, "M", "䪲"), - (0x2F9FD, "M", "𩒖"), - (0x2F9FE, "M", "頋"), - (0x2FA00, "M", "頩"), - (0x2FA01, "M", "𩖶"), - (0x2FA02, "M", "飢"), - (0x2FA03, "M", "䬳"), - (0x2FA04, "M", "餩"), - (0x2FA05, "M", "馧"), - (0x2FA06, "M", "駂"), - (0x2FA07, "M", "駾"), - (0x2FA08, "M", "䯎"), - (0x2FA09, "M", "𩬰"), - (0x2FA0A, "M", "鬒"), - (0x2FA0B, "M", "鱀"), - (0x2FA0C, "M", "鳽"), - ] - - -def _seg_83() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2FA0D, "M", "䳎"), - (0x2FA0E, "M", "䳭"), - (0x2FA0F, "M", "鵧"), - (0x2FA10, "M", "𪃎"), - (0x2FA11, "M", "䳸"), - (0x2FA12, "M", "𪄅"), - (0x2FA13, "M", "𪈎"), - (0x2FA14, "M", "𪊑"), - (0x2FA15, "M", "麻"), - (0x2FA16, "M", "䵖"), - (0x2FA17, "M", "黹"), - (0x2FA18, "M", "黾"), - (0x2FA19, "M", "鼅"), - (0x2FA1A, "M", "鼏"), - (0x2FA1B, "M", "鼖"), - (0x2FA1C, "M", "鼻"), - (0x2FA1D, "M", "𪘀"), - (0x2FA1E, "X"), - (0x30000, "V"), - (0x3134B, "X"), - (0x31350, "V"), - (0x323B0, "X"), - (0xE0100, "I"), - (0xE01F0, "X"), - ] - - -uts46data = tuple( - _seg_0() - + _seg_1() - + _seg_2() - + _seg_3() - + _seg_4() - + _seg_5() - + _seg_6() - + _seg_7() - + _seg_8() - + _seg_9() - + _seg_10() - + _seg_11() - + _seg_12() - + _seg_13() - + _seg_14() - + _seg_15() - + _seg_16() - + _seg_17() - + _seg_18() - + _seg_19() - + _seg_20() - + _seg_21() - + _seg_22() - + _seg_23() - + _seg_24() - + _seg_25() - + _seg_26() - + _seg_27() - + _seg_28() - + _seg_29() - + _seg_30() - + _seg_31() - + _seg_32() - + _seg_33() - + _seg_34() - + _seg_35() - + _seg_36() - + _seg_37() - + _seg_38() - + _seg_39() - + _seg_40() - + _seg_41() - + _seg_42() - + _seg_43() - + _seg_44() - + _seg_45() - + _seg_46() - + _seg_47() - + _seg_48() - + _seg_49() - + _seg_50() - + _seg_51() - + _seg_52() - + _seg_53() - + _seg_54() - + _seg_55() - + _seg_56() - + _seg_57() - + _seg_58() - + _seg_59() - + _seg_60() - + _seg_61() - + _seg_62() - + _seg_63() - + _seg_64() - + _seg_65() - + _seg_66() - + _seg_67() - + _seg_68() - + _seg_69() - + _seg_70() - + _seg_71() - + _seg_72() - + _seg_73() - + _seg_74() - + _seg_75() - + _seg_76() - + _seg_77() - + _seg_78() - + _seg_79() - + _seg_80() - + _seg_81() - + _seg_82() - + _seg_83() -) # type: Tuple[Union[Tuple[int, str], Tuple[int, str, str]], ...] diff --git a/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/INSTALLER b/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/INSTALLER deleted file mode 100755 index a1b589e3..00000000 --- a/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/METADATA b/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/METADATA deleted file mode 100755 index 3200e601..00000000 --- a/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/METADATA +++ /dev/null @@ -1,107 +0,0 @@ -Metadata-Version: 2.4 -Name: packaging -Version: 26.0 -Summary: Core utilities for Python packages -Author-email: Donald Stufft -Requires-Python: >=3.8 -Description-Content-Type: text/x-rst -License-Expression: Apache-2.0 OR BSD-2-Clause -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.8 -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 -License-File: LICENSE -License-File: LICENSE.APACHE -License-File: LICENSE.BSD -Project-URL: Documentation, https://packaging.pypa.io/ -Project-URL: Source, https://github.com/pypa/packaging - -packaging -========= - -.. start-intro - -Reusable core utilities for various Python Packaging -`interoperability specifications `_. - -This library provides utilities that implement the interoperability -specifications which have clearly one correct behaviour (eg: :pep:`440`) -or benefit greatly from having a single shared implementation (eg: :pep:`425`). - -.. end-intro - -The ``packaging`` project includes the following: version handling, specifiers, -markers, requirements, tags, metadata, lockfiles, utilities. - -Documentation -------------- - -The `documentation`_ provides information and the API for the following: - -- Version Handling -- Specifiers -- Markers -- Requirements -- Tags -- Metadata -- Lockfiles -- Utilities - -Installation ------------- - -Use ``pip`` to install these utilities:: - - pip install packaging - -The ``packaging`` library uses calendar-based versioning (``YY.N``). - -Discussion ----------- - -If you run into bugs, you can file them in our `issue tracker`_. - -You can also join ``#pypa`` on Freenode to ask questions or get involved. - - -.. _`documentation`: https://packaging.pypa.io/ -.. _`issue tracker`: https://github.com/pypa/packaging/issues - - -Code of Conduct ---------------- - -Everyone interacting in the packaging project's codebases, issue trackers, chat -rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_. - -.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md - -Contributing ------------- - -The ``CONTRIBUTING.rst`` file outlines how to contribute to this project as -well as how to report a potential security issue. The documentation for this -project also covers information about `project development`_ and `security`_. - -.. _`project development`: https://packaging.pypa.io/en/latest/development/ -.. _`security`: https://packaging.pypa.io/en/latest/security/ - -Project History ---------------- - -Please review the ``CHANGELOG.rst`` file or the `Changelog documentation`_ for -recent changes and project history. - -.. _`Changelog documentation`: https://packaging.pypa.io/en/latest/changelog/ - diff --git a/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/RECORD b/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/RECORD deleted file mode 100755 index 5e18df3d..00000000 --- a/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/RECORD +++ /dev/null @@ -1,26 +0,0 @@ -packaging-26.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -packaging-26.0.dist-info/METADATA,sha256=M2K7fWom2iliuo2qpHhc0LrKwhq6kIoRlcyPWVgKJlo,3309 -packaging-26.0.dist-info/RECORD,, -packaging-26.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -packaging-26.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82 -packaging-26.0.dist-info/licenses/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197 -packaging-26.0.dist-info/licenses/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174 -packaging-26.0.dist-info/licenses/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344 -packaging/__init__.py,sha256=y4lVbpeBzCGk-IPDw5BGBZ_b0P3ukEEJZAbGYc6Ey8c,494 -packaging/_elffile.py,sha256=-sKkptYqzYw2-x3QByJa5mB4rfPWu1pxkZHRx1WAFCY,3211 -packaging/_manylinux.py,sha256=Hf6nB0cOrayEs96-p3oIXAgGnFquv20DO5l-o2_Xnv0,9559 -packaging/_musllinux.py,sha256=Z6swjH3MA7XS3qXnmMN7QPhqP3fnoYI0eQ18e9-HgAE,2707 -packaging/_parser.py,sha256=U_DajsEx2VoC_F46fSVV3hDKNCWoQYkPkasO3dld0ig,10518 -packaging/_structures.py,sha256=Hn49Ta8zV9Wo8GiCL8Nl2ARZY983Un3pruZGVNldPwE,1514 -packaging/_tokenizer.py,sha256=M8EwNIdXeL9NMFuFrQtiOKwjka_xFx8KjRQnfE8O_z8,5421 -packaging/licenses/__init__.py,sha256=TwXLHZCXwSgdFwRLPxW602T6mSieunSFHM6fp8pgW78,5819 -packaging/licenses/_spdx.py,sha256=WW7DXiyg68up_YND_wpRYlr1SHhiV4FfJLQffghhMxQ,51122 -packaging/markers.py,sha256=ZX-cLvW1S3cZcEc0fHI4z7zSx5U2T19yMpDP_mE-CYw,12771 -packaging/metadata.py,sha256=CWVZpN_HfoYMSSDuCP7igOvGgqA9AOmpW8f3qTisfnc,39360 -packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -packaging/pylock.py,sha256=-R1uNfJ4PaLto7Mg62YsGOHgvskuiIEqPwxOywl42Jk,22537 -packaging/requirements.py,sha256=PMCAWD8aNMnVD-6uZMedhBuAVX2573eZ4yPBLXmz04I,2870 -packaging/specifiers.py,sha256=EPNPimY_zFivthv1vdjZYz5IqkKGsnKR2yKh-EVyvZw,40797 -packaging/tags.py,sha256=cXLV1pJD3UtJlDg7Wz3zrfdQhRZqr8jumSAKKAAd2xE,22856 -packaging/utils.py,sha256=N4c6oZzFJy6klTZ3AnkNz7sSkJesuFWPp68LA3B5dAo,5040 -packaging/version.py,sha256=7XWlL2IDYLwDYC0ht6cFEhapLwLWbmyo4rb7sEFj0x8,23272 diff --git a/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/REQUESTED b/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/REQUESTED deleted file mode 100755 index e69de29b..00000000 diff --git a/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/WHEEL b/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/WHEEL deleted file mode 100755 index d8b9936d..00000000 --- a/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: flit 3.12.0 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/licenses/LICENSE b/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/licenses/LICENSE deleted file mode 100755 index 6f62d44e..00000000 --- a/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/licenses/LICENSE +++ /dev/null @@ -1,3 +0,0 @@ -This software is made available under the terms of *either* of the licenses -found in LICENSE.APACHE or LICENSE.BSD. Contributions to this software is made -under the terms of *both* these licenses. diff --git a/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/licenses/LICENSE.APACHE b/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/licenses/LICENSE.APACHE deleted file mode 100755 index f433b1a5..00000000 --- a/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/licenses/LICENSE.APACHE +++ /dev/null @@ -1,177 +0,0 @@ - - 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 diff --git a/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/licenses/LICENSE.BSD b/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/licenses/LICENSE.BSD deleted file mode 100755 index 42ce7b75..00000000 --- a/apps/bitwarden_event_logs/lib/packaging-26.0.dist-info/licenses/LICENSE.BSD +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) Donald Stufft and individual contributors. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/apps/bitwarden_event_logs/lib/packaging/__init__.py b/apps/bitwarden_event_logs/lib/packaging/__init__.py deleted file mode 100755 index 21695a74..00000000 --- a/apps/bitwarden_event_logs/lib/packaging/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -__title__ = "packaging" -__summary__ = "Core utilities for Python packages" -__uri__ = "https://github.com/pypa/packaging" - -__version__ = "26.0" - -__author__ = "Donald Stufft and individual contributors" -__email__ = "donald@stufft.io" - -__license__ = "BSD-2-Clause or Apache-2.0" -__copyright__ = f"2014 {__author__}" diff --git a/apps/bitwarden_event_logs/lib/packaging/_elffile.py b/apps/bitwarden_event_logs/lib/packaging/_elffile.py deleted file mode 100755 index 497b0645..00000000 --- a/apps/bitwarden_event_logs/lib/packaging/_elffile.py +++ /dev/null @@ -1,108 +0,0 @@ -""" -ELF file parser. - -This provides a class ``ELFFile`` that parses an ELF executable in a similar -interface to ``ZipFile``. Only the read interface is implemented. - -ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html -""" - -from __future__ import annotations - -import enum -import os -import struct -from typing import IO - - -class ELFInvalid(ValueError): - pass - - -class EIClass(enum.IntEnum): - C32 = 1 - C64 = 2 - - -class EIData(enum.IntEnum): - Lsb = 1 - Msb = 2 - - -class EMachine(enum.IntEnum): - I386 = 3 - S390 = 22 - Arm = 40 - X8664 = 62 - AArc64 = 183 - - -class ELFFile: - """ - Representation of an ELF executable. - """ - - def __init__(self, f: IO[bytes]) -> None: - self._f = f - - try: - ident = self._read("16B") - except struct.error as e: - raise ELFInvalid("unable to parse identification") from e - magic = bytes(ident[:4]) - if magic != b"\x7fELF": - raise ELFInvalid(f"invalid magic: {magic!r}") - - self.capacity = ident[4] # Format for program header (bitness). - self.encoding = ident[5] # Data structure encoding (endianness). - - try: - # e_fmt: Format for program header. - # p_fmt: Format for section header. - # p_idx: Indexes to find p_type, p_offset, and p_filesz. - e_fmt, self._p_fmt, self._p_idx = { - (1, 1): ("HHIIIIIHHH", ">IIIIIIII", (0, 1, 4)), # 32-bit MSB. - (2, 1): ("HHIQQQIHHH", ">IIQQQQQQ", (0, 2, 5)), # 64-bit MSB. - }[(self.capacity, self.encoding)] - except KeyError as e: - raise ELFInvalid( - f"unrecognized capacity ({self.capacity}) or encoding ({self.encoding})" - ) from e - - try: - ( - _, - self.machine, # Architecture type. - _, - _, - self._e_phoff, # Offset of program header. - _, - self.flags, # Processor-specific flags. - _, - self._e_phentsize, # Size of section. - self._e_phnum, # Number of sections. - ) = self._read(e_fmt) - except struct.error as e: - raise ELFInvalid("unable to parse machine and section information") from e - - def _read(self, fmt: str) -> tuple[int, ...]: - return struct.unpack(fmt, self._f.read(struct.calcsize(fmt))) - - @property - def interpreter(self) -> str | None: - """ - The path recorded in the ``PT_INTERP`` section header. - """ - for index in range(self._e_phnum): - self._f.seek(self._e_phoff + self._e_phentsize * index) - try: - data = self._read(self._p_fmt) - except struct.error: - continue - if data[self._p_idx[0]] != 3: # Not PT_INTERP. - continue - self._f.seek(data[self._p_idx[1]]) - return os.fsdecode(self._f.read(data[self._p_idx[2]])).strip("\0") - return None diff --git a/apps/bitwarden_event_logs/lib/packaging/_manylinux.py b/apps/bitwarden_event_logs/lib/packaging/_manylinux.py deleted file mode 100755 index 0e79e8a8..00000000 --- a/apps/bitwarden_event_logs/lib/packaging/_manylinux.py +++ /dev/null @@ -1,262 +0,0 @@ -from __future__ import annotations - -import collections -import contextlib -import functools -import os -import re -import sys -import warnings -from typing import Generator, Iterator, NamedTuple, Sequence - -from ._elffile import EIClass, EIData, ELFFile, EMachine - -EF_ARM_ABIMASK = 0xFF000000 -EF_ARM_ABI_VER5 = 0x05000000 -EF_ARM_ABI_FLOAT_HARD = 0x00000400 - -_ALLOWED_ARCHS = { - "x86_64", - "aarch64", - "ppc64", - "ppc64le", - "s390x", - "loongarch64", - "riscv64", -} - - -# `os.PathLike` not a generic type until Python 3.9, so sticking with `str` -# as the type for `path` until then. -@contextlib.contextmanager -def _parse_elf(path: str) -> Generator[ELFFile | None, None, None]: - try: - with open(path, "rb") as f: - yield ELFFile(f) - except (OSError, TypeError, ValueError): - yield None - - -def _is_linux_armhf(executable: str) -> bool: - # hard-float ABI can be detected from the ELF header of the running - # process - # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf - with _parse_elf(executable) as f: - return ( - f is not None - and f.capacity == EIClass.C32 - and f.encoding == EIData.Lsb - and f.machine == EMachine.Arm - and f.flags & EF_ARM_ABIMASK == EF_ARM_ABI_VER5 - and f.flags & EF_ARM_ABI_FLOAT_HARD == EF_ARM_ABI_FLOAT_HARD - ) - - -def _is_linux_i686(executable: str) -> bool: - with _parse_elf(executable) as f: - return ( - f is not None - and f.capacity == EIClass.C32 - and f.encoding == EIData.Lsb - and f.machine == EMachine.I386 - ) - - -def _have_compatible_abi(executable: str, archs: Sequence[str]) -> bool: - if "armv7l" in archs: - return _is_linux_armhf(executable) - if "i686" in archs: - return _is_linux_i686(executable) - return any(arch in _ALLOWED_ARCHS for arch in archs) - - -# If glibc ever changes its major version, we need to know what the last -# minor version was, so we can build the complete list of all versions. -# For now, guess what the highest minor version might be, assume it will -# be 50 for testing. Once this actually happens, update the dictionary -# with the actual value. -_LAST_GLIBC_MINOR: dict[int, int] = collections.defaultdict(lambda: 50) - - -class _GLibCVersion(NamedTuple): - major: int - minor: int - - -def _glibc_version_string_confstr() -> str | None: - """ - Primary implementation of glibc_version_string using os.confstr. - """ - # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely - # to be broken or missing. This strategy is used in the standard library - # platform module. - # https://github.com/python/cpython/blob/fcf1d003bf4f0100c/Lib/platform.py#L175-L183 - try: - # Should be a string like "glibc 2.17". - version_string: str | None = os.confstr("CS_GNU_LIBC_VERSION") - assert version_string is not None - _, version = version_string.rsplit() - except (AssertionError, AttributeError, OSError, ValueError): - # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... - return None - return version - - -def _glibc_version_string_ctypes() -> str | None: - """ - Fallback implementation of glibc_version_string using ctypes. - """ - try: - import ctypes # noqa: PLC0415 - except ImportError: - return None - - # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen - # manpage says, "If filename is NULL, then the returned handle is for the - # main program". This way we can let the linker do the work to figure out - # which libc our process is actually using. - # - # We must also handle the special case where the executable is not a - # dynamically linked executable. This can occur when using musl libc, - # for example. In this situation, dlopen() will error, leading to an - # OSError. Interestingly, at least in the case of musl, there is no - # errno set on the OSError. The single string argument used to construct - # OSError comes from libc itself and is therefore not portable to - # hard code here. In any case, failure to call dlopen() means we - # can proceed, so we bail on our attempt. - try: - process_namespace = ctypes.CDLL(None) - except OSError: - return None - - try: - gnu_get_libc_version = process_namespace.gnu_get_libc_version - except AttributeError: - # Symbol doesn't exist -> therefore, we are not linked to - # glibc. - return None - - # Call gnu_get_libc_version, which returns a string like "2.5" - gnu_get_libc_version.restype = ctypes.c_char_p - version_str: str = gnu_get_libc_version() - # py2 / py3 compatibility: - if not isinstance(version_str, str): - version_str = version_str.decode("ascii") - - return version_str - - -def _glibc_version_string() -> str | None: - """Returns glibc version string, or None if not using glibc.""" - return _glibc_version_string_confstr() or _glibc_version_string_ctypes() - - -def _parse_glibc_version(version_str: str) -> _GLibCVersion: - """Parse glibc version. - - We use a regexp instead of str.split because we want to discard any - random junk that might come after the minor version -- this might happen - in patched/forked versions of glibc (e.g. Linaro's version of glibc - uses version strings like "2.20-2014.11"). See gh-3588. - """ - m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str) - if not m: - warnings.warn( - f"Expected glibc version with 2 components major.minor, got: {version_str}", - RuntimeWarning, - stacklevel=2, - ) - return _GLibCVersion(-1, -1) - return _GLibCVersion(int(m.group("major")), int(m.group("minor"))) - - -@functools.lru_cache -def _get_glibc_version() -> _GLibCVersion: - version_str = _glibc_version_string() - if version_str is None: - return _GLibCVersion(-1, -1) - return _parse_glibc_version(version_str) - - -# From PEP 513, PEP 600 -def _is_compatible(arch: str, version: _GLibCVersion) -> bool: - sys_glibc = _get_glibc_version() - if sys_glibc < version: - return False - # Check for presence of _manylinux module. - try: - import _manylinux # noqa: PLC0415 - except ImportError: - return True - if hasattr(_manylinux, "manylinux_compatible"): - result = _manylinux.manylinux_compatible(version[0], version[1], arch) - if result is not None: - return bool(result) - return True - if version == _GLibCVersion(2, 5) and hasattr(_manylinux, "manylinux1_compatible"): - return bool(_manylinux.manylinux1_compatible) - if version == _GLibCVersion(2, 12) and hasattr( - _manylinux, "manylinux2010_compatible" - ): - return bool(_manylinux.manylinux2010_compatible) - if version == _GLibCVersion(2, 17) and hasattr( - _manylinux, "manylinux2014_compatible" - ): - return bool(_manylinux.manylinux2014_compatible) - return True - - -_LEGACY_MANYLINUX_MAP: dict[_GLibCVersion, str] = { - # CentOS 7 w/ glibc 2.17 (PEP 599) - _GLibCVersion(2, 17): "manylinux2014", - # CentOS 6 w/ glibc 2.12 (PEP 571) - _GLibCVersion(2, 12): "manylinux2010", - # CentOS 5 w/ glibc 2.5 (PEP 513) - _GLibCVersion(2, 5): "manylinux1", -} - - -def platform_tags(archs: Sequence[str]) -> Iterator[str]: - """Generate manylinux tags compatible to the current platform. - - :param archs: Sequence of compatible architectures. - The first one shall be the closest to the actual architecture and be the part of - platform tag after the ``linux_`` prefix, e.g. ``x86_64``. - The ``linux_`` prefix is assumed as a prerequisite for the current platform to - be manylinux-compatible. - - :returns: An iterator of compatible manylinux tags. - """ - if not _have_compatible_abi(sys.executable, archs): - return - # Oldest glibc to be supported regardless of architecture is (2, 17). - too_old_glibc2 = _GLibCVersion(2, 16) - if set(archs) & {"x86_64", "i686"}: - # On x86/i686 also oldest glibc to be supported is (2, 5). - too_old_glibc2 = _GLibCVersion(2, 4) - current_glibc = _GLibCVersion(*_get_glibc_version()) - glibc_max_list = [current_glibc] - # We can assume compatibility across glibc major versions. - # https://sourceware.org/bugzilla/show_bug.cgi?id=24636 - # - # Build a list of maximum glibc versions so that we can - # output the canonical list of all glibc from current_glibc - # down to too_old_glibc2, including all intermediary versions. - for glibc_major in range(current_glibc.major - 1, 1, -1): - glibc_minor = _LAST_GLIBC_MINOR[glibc_major] - glibc_max_list.append(_GLibCVersion(glibc_major, glibc_minor)) - for arch in archs: - for glibc_max in glibc_max_list: - if glibc_max.major == too_old_glibc2.major: - min_minor = too_old_glibc2.minor - else: - # For other glibc major versions oldest supported is (x, 0). - min_minor = -1 - for glibc_minor in range(glibc_max.minor, min_minor, -1): - glibc_version = _GLibCVersion(glibc_max.major, glibc_minor) - if _is_compatible(arch, glibc_version): - yield "manylinux_{}_{}_{}".format(*glibc_version, arch) - - # Handle the legacy manylinux1, manylinux2010, manylinux2014 tags. - if legacy_tag := _LEGACY_MANYLINUX_MAP.get(glibc_version): - yield f"{legacy_tag}_{arch}" diff --git a/apps/bitwarden_event_logs/lib/packaging/_musllinux.py b/apps/bitwarden_event_logs/lib/packaging/_musllinux.py deleted file mode 100755 index 4e8116a7..00000000 --- a/apps/bitwarden_event_logs/lib/packaging/_musllinux.py +++ /dev/null @@ -1,85 +0,0 @@ -"""PEP 656 support. - -This module implements logic to detect if the currently running Python is -linked against musl, and what musl version is used. -""" - -from __future__ import annotations - -import functools -import re -import subprocess -import sys -from typing import Iterator, NamedTuple, Sequence - -from ._elffile import ELFFile - - -class _MuslVersion(NamedTuple): - major: int - minor: int - - -def _parse_musl_version(output: str) -> _MuslVersion | None: - lines = [n for n in (n.strip() for n in output.splitlines()) if n] - if len(lines) < 2 or lines[0][:4] != "musl": - return None - m = re.match(r"Version (\d+)\.(\d+)", lines[1]) - if not m: - return None - return _MuslVersion(major=int(m.group(1)), minor=int(m.group(2))) - - -@functools.lru_cache -def _get_musl_version(executable: str) -> _MuslVersion | None: - """Detect currently-running musl runtime version. - - This is done by checking the specified executable's dynamic linking - information, and invoking the loader to parse its output for a version - string. If the loader is musl, the output would be something like:: - - musl libc (x86_64) - Version 1.2.2 - Dynamic Program Loader - """ - try: - with open(executable, "rb") as f: - ld = ELFFile(f).interpreter - except (OSError, TypeError, ValueError): - return None - if ld is None or "musl" not in ld: - return None - proc = subprocess.run([ld], check=False, stderr=subprocess.PIPE, text=True) - return _parse_musl_version(proc.stderr) - - -def platform_tags(archs: Sequence[str]) -> Iterator[str]: - """Generate musllinux tags compatible to the current platform. - - :param archs: Sequence of compatible architectures. - The first one shall be the closest to the actual architecture and be the part of - platform tag after the ``linux_`` prefix, e.g. ``x86_64``. - The ``linux_`` prefix is assumed as a prerequisite for the current platform to - be musllinux-compatible. - - :returns: An iterator of compatible musllinux tags. - """ - sys_musl = _get_musl_version(sys.executable) - if sys_musl is None: # Python not dynamically linked against musl. - return - for arch in archs: - for minor in range(sys_musl.minor, -1, -1): - yield f"musllinux_{sys_musl.major}_{minor}_{arch}" - - -if __name__ == "__main__": # pragma: no cover - import sysconfig - - plat = sysconfig.get_platform() - assert plat.startswith("linux-"), "not linux" - - print("plat:", plat) - print("musl:", _get_musl_version(sys.executable)) - print("tags:", end=" ") - for t in platform_tags(re.sub(r"[.-]", "_", plat.split("-", 1)[-1])): - print(t, end="\n ") diff --git a/apps/bitwarden_event_logs/lib/packaging/_parser.py b/apps/bitwarden_event_logs/lib/packaging/_parser.py deleted file mode 100755 index f6c1f5cd..00000000 --- a/apps/bitwarden_event_logs/lib/packaging/_parser.py +++ /dev/null @@ -1,365 +0,0 @@ -"""Handwritten parser of dependency specifiers. - -The docstring for each __parse_* function contains EBNF-inspired grammar representing -the implementation. -""" - -from __future__ import annotations - -import ast -from typing import List, Literal, NamedTuple, Sequence, Tuple, Union - -from ._tokenizer import DEFAULT_RULES, Tokenizer - - -class Node: - __slots__ = ("value",) - - def __init__(self, value: str) -> None: - self.value = value - - def __str__(self) -> str: - return self.value - - def __repr__(self) -> str: - return f"<{self.__class__.__name__}({self.value!r})>" - - def serialize(self) -> str: - raise NotImplementedError - - -class Variable(Node): - __slots__ = () - - def serialize(self) -> str: - return str(self) - - -class Value(Node): - __slots__ = () - - def serialize(self) -> str: - return f'"{self}"' - - -class Op(Node): - __slots__ = () - - def serialize(self) -> str: - return str(self) - - -MarkerLogical = Literal["and", "or"] -MarkerVar = Union[Variable, Value] -MarkerItem = Tuple[MarkerVar, Op, MarkerVar] -MarkerAtom = Union[MarkerItem, Sequence["MarkerAtom"]] -MarkerList = List[Union["MarkerList", MarkerAtom, MarkerLogical]] - - -class ParsedRequirement(NamedTuple): - name: str - url: str - extras: list[str] - specifier: str - marker: MarkerList | None - - -# -------------------------------------------------------------------------------------- -# Recursive descent parser for dependency specifier -# -------------------------------------------------------------------------------------- -def parse_requirement(source: str) -> ParsedRequirement: - return _parse_requirement(Tokenizer(source, rules=DEFAULT_RULES)) - - -def _parse_requirement(tokenizer: Tokenizer) -> ParsedRequirement: - """ - requirement = WS? IDENTIFIER WS? extras WS? requirement_details - """ - tokenizer.consume("WS") - - name_token = tokenizer.expect( - "IDENTIFIER", expected="package name at the start of dependency specifier" - ) - name = name_token.text - tokenizer.consume("WS") - - extras = _parse_extras(tokenizer) - tokenizer.consume("WS") - - url, specifier, marker = _parse_requirement_details(tokenizer) - tokenizer.expect("END", expected="end of dependency specifier") - - return ParsedRequirement(name, url, extras, specifier, marker) - - -def _parse_requirement_details( - tokenizer: Tokenizer, -) -> tuple[str, str, MarkerList | None]: - """ - requirement_details = AT URL (WS requirement_marker?)? - | specifier WS? (requirement_marker)? - """ - - specifier = "" - url = "" - marker = None - - if tokenizer.check("AT"): - tokenizer.read() - tokenizer.consume("WS") - - url_start = tokenizer.position - url = tokenizer.expect("URL", expected="URL after @").text - if tokenizer.check("END", peek=True): - return (url, specifier, marker) - - tokenizer.expect("WS", expected="whitespace after URL") - - # The input might end after whitespace. - if tokenizer.check("END", peek=True): - return (url, specifier, marker) - - marker = _parse_requirement_marker( - tokenizer, - span_start=url_start, - expected="semicolon (after URL and whitespace)", - ) - else: - specifier_start = tokenizer.position - specifier = _parse_specifier(tokenizer) - tokenizer.consume("WS") - - if tokenizer.check("END", peek=True): - return (url, specifier, marker) - - marker = _parse_requirement_marker( - tokenizer, - span_start=specifier_start, - expected=( - "comma (within version specifier), semicolon (after version specifier)" - if specifier - else "semicolon (after name with no version specifier)" - ), - ) - - return (url, specifier, marker) - - -def _parse_requirement_marker( - tokenizer: Tokenizer, *, span_start: int, expected: str -) -> MarkerList: - """ - requirement_marker = SEMICOLON marker WS? - """ - - if not tokenizer.check("SEMICOLON"): - tokenizer.raise_syntax_error( - f"Expected {expected} or end", - span_start=span_start, - span_end=None, - ) - tokenizer.read() - - marker = _parse_marker(tokenizer) - tokenizer.consume("WS") - - return marker - - -def _parse_extras(tokenizer: Tokenizer) -> list[str]: - """ - extras = (LEFT_BRACKET wsp* extras_list? wsp* RIGHT_BRACKET)? - """ - if not tokenizer.check("LEFT_BRACKET", peek=True): - return [] - - with tokenizer.enclosing_tokens( - "LEFT_BRACKET", - "RIGHT_BRACKET", - around="extras", - ): - tokenizer.consume("WS") - extras = _parse_extras_list(tokenizer) - tokenizer.consume("WS") - - return extras - - -def _parse_extras_list(tokenizer: Tokenizer) -> list[str]: - """ - extras_list = identifier (wsp* ',' wsp* identifier)* - """ - extras: list[str] = [] - - if not tokenizer.check("IDENTIFIER"): - return extras - - extras.append(tokenizer.read().text) - - while True: - tokenizer.consume("WS") - if tokenizer.check("IDENTIFIER", peek=True): - tokenizer.raise_syntax_error("Expected comma between extra names") - elif not tokenizer.check("COMMA"): - break - - tokenizer.read() - tokenizer.consume("WS") - - extra_token = tokenizer.expect("IDENTIFIER", expected="extra name after comma") - extras.append(extra_token.text) - - return extras - - -def _parse_specifier(tokenizer: Tokenizer) -> str: - """ - specifier = LEFT_PARENTHESIS WS? version_many WS? RIGHT_PARENTHESIS - | WS? version_many WS? - """ - with tokenizer.enclosing_tokens( - "LEFT_PARENTHESIS", - "RIGHT_PARENTHESIS", - around="version specifier", - ): - tokenizer.consume("WS") - parsed_specifiers = _parse_version_many(tokenizer) - tokenizer.consume("WS") - - return parsed_specifiers - - -def _parse_version_many(tokenizer: Tokenizer) -> str: - """ - version_many = (SPECIFIER (WS? COMMA WS? SPECIFIER)*)? - """ - parsed_specifiers = "" - while tokenizer.check("SPECIFIER"): - span_start = tokenizer.position - parsed_specifiers += tokenizer.read().text - if tokenizer.check("VERSION_PREFIX_TRAIL", peek=True): - tokenizer.raise_syntax_error( - ".* suffix can only be used with `==` or `!=` operators", - span_start=span_start, - span_end=tokenizer.position + 1, - ) - if tokenizer.check("VERSION_LOCAL_LABEL_TRAIL", peek=True): - tokenizer.raise_syntax_error( - "Local version label can only be used with `==` or `!=` operators", - span_start=span_start, - span_end=tokenizer.position, - ) - tokenizer.consume("WS") - if not tokenizer.check("COMMA"): - break - parsed_specifiers += tokenizer.read().text - tokenizer.consume("WS") - - return parsed_specifiers - - -# -------------------------------------------------------------------------------------- -# Recursive descent parser for marker expression -# -------------------------------------------------------------------------------------- -def parse_marker(source: str) -> MarkerList: - return _parse_full_marker(Tokenizer(source, rules=DEFAULT_RULES)) - - -def _parse_full_marker(tokenizer: Tokenizer) -> MarkerList: - retval = _parse_marker(tokenizer) - tokenizer.expect("END", expected="end of marker expression") - return retval - - -def _parse_marker(tokenizer: Tokenizer) -> MarkerList: - """ - marker = marker_atom (BOOLOP marker_atom)+ - """ - expression = [_parse_marker_atom(tokenizer)] - while tokenizer.check("BOOLOP"): - token = tokenizer.read() - expr_right = _parse_marker_atom(tokenizer) - expression.extend((token.text, expr_right)) - return expression - - -def _parse_marker_atom(tokenizer: Tokenizer) -> MarkerAtom: - """ - marker_atom = WS? LEFT_PARENTHESIS WS? marker WS? RIGHT_PARENTHESIS WS? - | WS? marker_item WS? - """ - - tokenizer.consume("WS") - if tokenizer.check("LEFT_PARENTHESIS", peek=True): - with tokenizer.enclosing_tokens( - "LEFT_PARENTHESIS", - "RIGHT_PARENTHESIS", - around="marker expression", - ): - tokenizer.consume("WS") - marker: MarkerAtom = _parse_marker(tokenizer) - tokenizer.consume("WS") - else: - marker = _parse_marker_item(tokenizer) - tokenizer.consume("WS") - return marker - - -def _parse_marker_item(tokenizer: Tokenizer) -> MarkerItem: - """ - marker_item = WS? marker_var WS? marker_op WS? marker_var WS? - """ - tokenizer.consume("WS") - marker_var_left = _parse_marker_var(tokenizer) - tokenizer.consume("WS") - marker_op = _parse_marker_op(tokenizer) - tokenizer.consume("WS") - marker_var_right = _parse_marker_var(tokenizer) - tokenizer.consume("WS") - return (marker_var_left, marker_op, marker_var_right) - - -def _parse_marker_var(tokenizer: Tokenizer) -> MarkerVar: # noqa: RET503 - """ - marker_var = VARIABLE | QUOTED_STRING - """ - if tokenizer.check("VARIABLE"): - return process_env_var(tokenizer.read().text.replace(".", "_")) - elif tokenizer.check("QUOTED_STRING"): - return process_python_str(tokenizer.read().text) - else: - tokenizer.raise_syntax_error( - message="Expected a marker variable or quoted string" - ) - - -def process_env_var(env_var: str) -> Variable: - if env_var in ("platform_python_implementation", "python_implementation"): - return Variable("platform_python_implementation") - else: - return Variable(env_var) - - -def process_python_str(python_str: str) -> Value: - value = ast.literal_eval(python_str) - return Value(str(value)) - - -def _parse_marker_op(tokenizer: Tokenizer) -> Op: - """ - marker_op = IN | NOT IN | OP - """ - if tokenizer.check("IN"): - tokenizer.read() - return Op("in") - elif tokenizer.check("NOT"): - tokenizer.read() - tokenizer.expect("WS", expected="whitespace after 'not'") - tokenizer.expect("IN", expected="'in' after 'not'") - return Op("not in") - elif tokenizer.check("OP"): - return Op(tokenizer.read().text) - else: - return tokenizer.raise_syntax_error( - "Expected marker operator, one of <=, <, !=, ==, >=, >, ~=, ===, in, not in" - ) diff --git a/apps/bitwarden_event_logs/lib/packaging/_structures.py b/apps/bitwarden_event_logs/lib/packaging/_structures.py deleted file mode 100755 index 225e2eee..00000000 --- a/apps/bitwarden_event_logs/lib/packaging/_structures.py +++ /dev/null @@ -1,69 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - - -@typing.final -class InfinityType: - __slots__ = () - - def __repr__(self) -> str: - return "Infinity" - - def __hash__(self) -> int: - return hash(repr(self)) - - def __lt__(self, other: object) -> bool: - return False - - def __le__(self, other: object) -> bool: - return False - - def __eq__(self, other: object) -> bool: - return isinstance(other, self.__class__) - - def __gt__(self, other: object) -> bool: - return True - - def __ge__(self, other: object) -> bool: - return True - - def __neg__(self: object) -> "NegativeInfinityType": - return NegativeInfinity - - -Infinity = InfinityType() - - -@typing.final -class NegativeInfinityType: - __slots__ = () - - def __repr__(self) -> str: - return "-Infinity" - - def __hash__(self) -> int: - return hash(repr(self)) - - def __lt__(self, other: object) -> bool: - return True - - def __le__(self, other: object) -> bool: - return True - - def __eq__(self, other: object) -> bool: - return isinstance(other, self.__class__) - - def __gt__(self, other: object) -> bool: - return False - - def __ge__(self, other: object) -> bool: - return False - - def __neg__(self: object) -> InfinityType: - return Infinity - - -NegativeInfinity = NegativeInfinityType() diff --git a/apps/bitwarden_event_logs/lib/packaging/_tokenizer.py b/apps/bitwarden_event_logs/lib/packaging/_tokenizer.py deleted file mode 100755 index e6d20dd3..00000000 --- a/apps/bitwarden_event_logs/lib/packaging/_tokenizer.py +++ /dev/null @@ -1,193 +0,0 @@ -from __future__ import annotations - -import contextlib -import re -from dataclasses import dataclass -from typing import Generator, Mapping, NoReturn - -from .specifiers import Specifier - - -@dataclass -class Token: - name: str - text: str - position: int - - -class ParserSyntaxError(Exception): - """The provided source text could not be parsed correctly.""" - - def __init__( - self, - message: str, - *, - source: str, - span: tuple[int, int], - ) -> None: - self.span = span - self.message = message - self.source = source - - super().__init__() - - def __str__(self) -> str: - marker = " " * self.span[0] + "~" * (self.span[1] - self.span[0]) + "^" - return f"{self.message}\n {self.source}\n {marker}" - - -DEFAULT_RULES: dict[str, re.Pattern[str]] = { - "LEFT_PARENTHESIS": re.compile(r"\("), - "RIGHT_PARENTHESIS": re.compile(r"\)"), - "LEFT_BRACKET": re.compile(r"\["), - "RIGHT_BRACKET": re.compile(r"\]"), - "SEMICOLON": re.compile(r";"), - "COMMA": re.compile(r","), - "QUOTED_STRING": re.compile( - r""" - ( - ('[^']*') - | - ("[^"]*") - ) - """, - re.VERBOSE, - ), - "OP": re.compile(r"(===|==|~=|!=|<=|>=|<|>)"), - "BOOLOP": re.compile(r"\b(or|and)\b"), - "IN": re.compile(r"\bin\b"), - "NOT": re.compile(r"\bnot\b"), - "VARIABLE": re.compile( - r""" - \b( - python_version - |python_full_version - |os[._]name - |sys[._]platform - |platform_(release|system) - |platform[._](version|machine|python_implementation) - |python_implementation - |implementation_(name|version) - |extras? - |dependency_groups - )\b - """, - re.VERBOSE, - ), - "SPECIFIER": re.compile( - Specifier._operator_regex_str + Specifier._version_regex_str, - re.VERBOSE | re.IGNORECASE, - ), - "AT": re.compile(r"\@"), - "URL": re.compile(r"[^ \t]+"), - "IDENTIFIER": re.compile(r"\b[a-zA-Z0-9][a-zA-Z0-9._-]*\b"), - "VERSION_PREFIX_TRAIL": re.compile(r"\.\*"), - "VERSION_LOCAL_LABEL_TRAIL": re.compile(r"\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*"), - "WS": re.compile(r"[ \t]+"), - "END": re.compile(r"$"), -} - - -class Tokenizer: - """Context-sensitive token parsing. - - Provides methods to examine the input stream to check whether the next token - matches. - """ - - def __init__( - self, - source: str, - *, - rules: Mapping[str, re.Pattern[str]], - ) -> None: - self.source = source - self.rules = rules - self.next_token: Token | None = None - self.position = 0 - - def consume(self, name: str) -> None: - """Move beyond provided token name, if at current position.""" - if self.check(name): - self.read() - - def check(self, name: str, *, peek: bool = False) -> bool: - """Check whether the next token has the provided name. - - By default, if the check succeeds, the token *must* be read before - another check. If `peek` is set to `True`, the token is not loaded and - would need to be checked again. - """ - assert self.next_token is None, ( - f"Cannot check for {name!r}, already have {self.next_token!r}" - ) - assert name in self.rules, f"Unknown token name: {name!r}" - - expression = self.rules[name] - - match = expression.match(self.source, self.position) - if match is None: - return False - if not peek: - self.next_token = Token(name, match[0], self.position) - return True - - def expect(self, name: str, *, expected: str) -> Token: - """Expect a certain token name next, failing with a syntax error otherwise. - - The token is *not* read. - """ - if not self.check(name): - raise self.raise_syntax_error(f"Expected {expected}") - return self.read() - - def read(self) -> Token: - """Consume the next token and return it.""" - token = self.next_token - assert token is not None - - self.position += len(token.text) - self.next_token = None - - return token - - def raise_syntax_error( - self, - message: str, - *, - span_start: int | None = None, - span_end: int | None = None, - ) -> NoReturn: - """Raise ParserSyntaxError at the given position.""" - span = ( - self.position if span_start is None else span_start, - self.position if span_end is None else span_end, - ) - raise ParserSyntaxError( - message, - source=self.source, - span=span, - ) - - @contextlib.contextmanager - def enclosing_tokens( - self, open_token: str, close_token: str, *, around: str - ) -> Generator[None, None, None]: - if self.check(open_token): - open_position = self.position - self.read() - else: - open_position = None - - yield - - if open_position is None: - return - - if not self.check(close_token): - self.raise_syntax_error( - f"Expected matching {close_token} for {open_token}, after {around}", - span_start=open_position, - ) - - self.read() diff --git a/apps/bitwarden_event_logs/lib/packaging/licenses/__init__.py b/apps/bitwarden_event_logs/lib/packaging/licenses/__init__.py deleted file mode 100755 index 335b275f..00000000 --- a/apps/bitwarden_event_logs/lib/packaging/licenses/__init__.py +++ /dev/null @@ -1,147 +0,0 @@ -####################################################################################### -# -# Adapted from: -# https://github.com/pypa/hatch/blob/5352e44/backend/src/hatchling/licenses/parse.py -# -# MIT License -# -# Copyright (c) 2017-present Ofek Lev -# -# 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. -# -# -# With additional allowance of arbitrary `LicenseRef-` identifiers, not just -# `LicenseRef-Public-Domain` and `LicenseRef-Proprietary`. -# -####################################################################################### -from __future__ import annotations - -import re -from typing import NewType, cast - -from ._spdx import EXCEPTIONS, LICENSES - -__all__ = [ - "InvalidLicenseExpression", - "NormalizedLicenseExpression", - "canonicalize_license_expression", -] - -license_ref_allowed = re.compile("^[A-Za-z0-9.-]*$") - -NormalizedLicenseExpression = NewType("NormalizedLicenseExpression", str) - - -class InvalidLicenseExpression(ValueError): - """Raised when a license-expression string is invalid - - >>> canonicalize_license_expression("invalid") - Traceback (most recent call last): - ... - packaging.licenses.InvalidLicenseExpression: Invalid license expression: 'invalid' - """ - - -def canonicalize_license_expression( - raw_license_expression: str, -) -> NormalizedLicenseExpression: - if not raw_license_expression: - message = f"Invalid license expression: {raw_license_expression!r}" - raise InvalidLicenseExpression(message) - - # Pad any parentheses so tokenization can be achieved by merely splitting on - # whitespace. - license_expression = raw_license_expression.replace("(", " ( ").replace(")", " ) ") - licenseref_prefix = "LicenseRef-" - license_refs = { - ref.lower(): "LicenseRef-" + ref[len(licenseref_prefix) :] - for ref in license_expression.split() - if ref.lower().startswith(licenseref_prefix.lower()) - } - - # Normalize to lower case so we can look up licenses/exceptions - # and so boolean operators are Python-compatible. - license_expression = license_expression.lower() - - tokens = license_expression.split() - - # Rather than implementing a parenthesis/boolean logic parser, create an - # expression that Python can parse. Everything that is not involved with the - # grammar itself is replaced with the placeholder `False` and the resultant - # expression should become a valid Python expression. - python_tokens = [] - for token in tokens: - if token not in {"or", "and", "with", "(", ")"}: - python_tokens.append("False") - elif token == "with": - python_tokens.append("or") - elif ( - token == "(" - and python_tokens - and python_tokens[-1] not in {"or", "and", "("} - ) or (token == ")" and python_tokens and python_tokens[-1] == "("): - message = f"Invalid license expression: {raw_license_expression!r}" - raise InvalidLicenseExpression(message) - else: - python_tokens.append(token) - - python_expression = " ".join(python_tokens) - try: - compile(python_expression, "", "eval") - except SyntaxError: - message = f"Invalid license expression: {raw_license_expression!r}" - raise InvalidLicenseExpression(message) from None - - # Take a final pass to check for unknown licenses/exceptions. - normalized_tokens = [] - for token in tokens: - if token in {"or", "and", "with", "(", ")"}: - normalized_tokens.append(token.upper()) - continue - - if normalized_tokens and normalized_tokens[-1] == "WITH": - if token not in EXCEPTIONS: - message = f"Unknown license exception: {token!r}" - raise InvalidLicenseExpression(message) - - normalized_tokens.append(EXCEPTIONS[token]["id"]) - else: - if token.endswith("+"): - final_token = token[:-1] - suffix = "+" - else: - final_token = token - suffix = "" - - if final_token.startswith("licenseref-"): - if not license_ref_allowed.match(final_token): - message = f"Invalid licenseref: {final_token!r}" - raise InvalidLicenseExpression(message) - normalized_tokens.append(license_refs[final_token] + suffix) - else: - if final_token not in LICENSES: - message = f"Unknown license: {final_token!r}" - raise InvalidLicenseExpression(message) - normalized_tokens.append(LICENSES[final_token]["id"] + suffix) - - normalized_expression = " ".join(normalized_tokens) - - return cast( - "NormalizedLicenseExpression", - normalized_expression.replace("( ", "(").replace(" )", ")"), - ) diff --git a/apps/bitwarden_event_logs/lib/packaging/licenses/_spdx.py b/apps/bitwarden_event_logs/lib/packaging/licenses/_spdx.py deleted file mode 100755 index a277af28..00000000 --- a/apps/bitwarden_event_logs/lib/packaging/licenses/_spdx.py +++ /dev/null @@ -1,799 +0,0 @@ - -from __future__ import annotations - -from typing import TypedDict - -class SPDXLicense(TypedDict): - id: str - deprecated: bool - -class SPDXException(TypedDict): - id: str - deprecated: bool - - -VERSION = '3.27.0' - -LICENSES: dict[str, SPDXLicense] = { - '0bsd': {'id': '0BSD', 'deprecated': False}, - '3d-slicer-1.0': {'id': '3D-Slicer-1.0', 'deprecated': False}, - 'aal': {'id': 'AAL', 'deprecated': False}, - 'abstyles': {'id': 'Abstyles', 'deprecated': False}, - 'adacore-doc': {'id': 'AdaCore-doc', 'deprecated': False}, - 'adobe-2006': {'id': 'Adobe-2006', 'deprecated': False}, - 'adobe-display-postscript': {'id': 'Adobe-Display-PostScript', 'deprecated': False}, - 'adobe-glyph': {'id': 'Adobe-Glyph', 'deprecated': False}, - 'adobe-utopia': {'id': 'Adobe-Utopia', 'deprecated': False}, - 'adsl': {'id': 'ADSL', 'deprecated': False}, - 'afl-1.1': {'id': 'AFL-1.1', 'deprecated': False}, - 'afl-1.2': {'id': 'AFL-1.2', 'deprecated': False}, - 'afl-2.0': {'id': 'AFL-2.0', 'deprecated': False}, - 'afl-2.1': {'id': 'AFL-2.1', 'deprecated': False}, - 'afl-3.0': {'id': 'AFL-3.0', 'deprecated': False}, - 'afmparse': {'id': 'Afmparse', 'deprecated': False}, - 'agpl-1.0': {'id': 'AGPL-1.0', 'deprecated': True}, - 'agpl-1.0-only': {'id': 'AGPL-1.0-only', 'deprecated': False}, - 'agpl-1.0-or-later': {'id': 'AGPL-1.0-or-later', 'deprecated': False}, - 'agpl-3.0': {'id': 'AGPL-3.0', 'deprecated': True}, - 'agpl-3.0-only': {'id': 'AGPL-3.0-only', 'deprecated': False}, - 'agpl-3.0-or-later': {'id': 'AGPL-3.0-or-later', 'deprecated': False}, - 'aladdin': {'id': 'Aladdin', 'deprecated': False}, - 'amd-newlib': {'id': 'AMD-newlib', 'deprecated': False}, - 'amdplpa': {'id': 'AMDPLPA', 'deprecated': False}, - 'aml': {'id': 'AML', 'deprecated': False}, - 'aml-glslang': {'id': 'AML-glslang', 'deprecated': False}, - 'ampas': {'id': 'AMPAS', 'deprecated': False}, - 'antlr-pd': {'id': 'ANTLR-PD', 'deprecated': False}, - 'antlr-pd-fallback': {'id': 'ANTLR-PD-fallback', 'deprecated': False}, - 'any-osi': {'id': 'any-OSI', 'deprecated': False}, - 'any-osi-perl-modules': {'id': 'any-OSI-perl-modules', 'deprecated': False}, - 'apache-1.0': {'id': 'Apache-1.0', 'deprecated': False}, - 'apache-1.1': {'id': 'Apache-1.1', 'deprecated': False}, - 'apache-2.0': {'id': 'Apache-2.0', 'deprecated': False}, - 'apafml': {'id': 'APAFML', 'deprecated': False}, - 'apl-1.0': {'id': 'APL-1.0', 'deprecated': False}, - 'app-s2p': {'id': 'App-s2p', 'deprecated': False}, - 'apsl-1.0': {'id': 'APSL-1.0', 'deprecated': False}, - 'apsl-1.1': {'id': 'APSL-1.1', 'deprecated': False}, - 'apsl-1.2': {'id': 'APSL-1.2', 'deprecated': False}, - 'apsl-2.0': {'id': 'APSL-2.0', 'deprecated': False}, - 'arphic-1999': {'id': 'Arphic-1999', 'deprecated': False}, - 'artistic-1.0': {'id': 'Artistic-1.0', 'deprecated': False}, - 'artistic-1.0-cl8': {'id': 'Artistic-1.0-cl8', 'deprecated': False}, - 'artistic-1.0-perl': {'id': 'Artistic-1.0-Perl', 'deprecated': False}, - 'artistic-2.0': {'id': 'Artistic-2.0', 'deprecated': False}, - 'artistic-dist': {'id': 'Artistic-dist', 'deprecated': False}, - 'aspell-ru': {'id': 'Aspell-RU', 'deprecated': False}, - 'aswf-digital-assets-1.0': {'id': 'ASWF-Digital-Assets-1.0', 'deprecated': False}, - 'aswf-digital-assets-1.1': {'id': 'ASWF-Digital-Assets-1.1', 'deprecated': False}, - 'baekmuk': {'id': 'Baekmuk', 'deprecated': False}, - 'bahyph': {'id': 'Bahyph', 'deprecated': False}, - 'barr': {'id': 'Barr', 'deprecated': False}, - 'bcrypt-solar-designer': {'id': 'bcrypt-Solar-Designer', 'deprecated': False}, - 'beerware': {'id': 'Beerware', 'deprecated': False}, - 'bitstream-charter': {'id': 'Bitstream-Charter', 'deprecated': False}, - 'bitstream-vera': {'id': 'Bitstream-Vera', 'deprecated': False}, - 'bittorrent-1.0': {'id': 'BitTorrent-1.0', 'deprecated': False}, - 'bittorrent-1.1': {'id': 'BitTorrent-1.1', 'deprecated': False}, - 'blessing': {'id': 'blessing', 'deprecated': False}, - 'blueoak-1.0.0': {'id': 'BlueOak-1.0.0', 'deprecated': False}, - 'boehm-gc': {'id': 'Boehm-GC', 'deprecated': False}, - 'boehm-gc-without-fee': {'id': 'Boehm-GC-without-fee', 'deprecated': False}, - 'borceux': {'id': 'Borceux', 'deprecated': False}, - 'brian-gladman-2-clause': {'id': 'Brian-Gladman-2-Clause', 'deprecated': False}, - 'brian-gladman-3-clause': {'id': 'Brian-Gladman-3-Clause', 'deprecated': False}, - 'bsd-1-clause': {'id': 'BSD-1-Clause', 'deprecated': False}, - 'bsd-2-clause': {'id': 'BSD-2-Clause', 'deprecated': False}, - 'bsd-2-clause-darwin': {'id': 'BSD-2-Clause-Darwin', 'deprecated': False}, - 'bsd-2-clause-first-lines': {'id': 'BSD-2-Clause-first-lines', 'deprecated': False}, - 'bsd-2-clause-freebsd': {'id': 'BSD-2-Clause-FreeBSD', 'deprecated': True}, - 'bsd-2-clause-netbsd': {'id': 'BSD-2-Clause-NetBSD', 'deprecated': True}, - 'bsd-2-clause-patent': {'id': 'BSD-2-Clause-Patent', 'deprecated': False}, - 'bsd-2-clause-pkgconf-disclaimer': {'id': 'BSD-2-Clause-pkgconf-disclaimer', 'deprecated': False}, - 'bsd-2-clause-views': {'id': 'BSD-2-Clause-Views', 'deprecated': False}, - 'bsd-3-clause': {'id': 'BSD-3-Clause', 'deprecated': False}, - 'bsd-3-clause-acpica': {'id': 'BSD-3-Clause-acpica', 'deprecated': False}, - 'bsd-3-clause-attribution': {'id': 'BSD-3-Clause-Attribution', 'deprecated': False}, - 'bsd-3-clause-clear': {'id': 'BSD-3-Clause-Clear', 'deprecated': False}, - 'bsd-3-clause-flex': {'id': 'BSD-3-Clause-flex', 'deprecated': False}, - 'bsd-3-clause-hp': {'id': 'BSD-3-Clause-HP', 'deprecated': False}, - 'bsd-3-clause-lbnl': {'id': 'BSD-3-Clause-LBNL', 'deprecated': False}, - 'bsd-3-clause-modification': {'id': 'BSD-3-Clause-Modification', 'deprecated': False}, - 'bsd-3-clause-no-military-license': {'id': 'BSD-3-Clause-No-Military-License', 'deprecated': False}, - 'bsd-3-clause-no-nuclear-license': {'id': 'BSD-3-Clause-No-Nuclear-License', 'deprecated': False}, - 'bsd-3-clause-no-nuclear-license-2014': {'id': 'BSD-3-Clause-No-Nuclear-License-2014', 'deprecated': False}, - 'bsd-3-clause-no-nuclear-warranty': {'id': 'BSD-3-Clause-No-Nuclear-Warranty', 'deprecated': False}, - 'bsd-3-clause-open-mpi': {'id': 'BSD-3-Clause-Open-MPI', 'deprecated': False}, - 'bsd-3-clause-sun': {'id': 'BSD-3-Clause-Sun', 'deprecated': False}, - 'bsd-4-clause': {'id': 'BSD-4-Clause', 'deprecated': False}, - 'bsd-4-clause-shortened': {'id': 'BSD-4-Clause-Shortened', 'deprecated': False}, - 'bsd-4-clause-uc': {'id': 'BSD-4-Clause-UC', 'deprecated': False}, - 'bsd-4.3reno': {'id': 'BSD-4.3RENO', 'deprecated': False}, - 'bsd-4.3tahoe': {'id': 'BSD-4.3TAHOE', 'deprecated': False}, - 'bsd-advertising-acknowledgement': {'id': 'BSD-Advertising-Acknowledgement', 'deprecated': False}, - 'bsd-attribution-hpnd-disclaimer': {'id': 'BSD-Attribution-HPND-disclaimer', 'deprecated': False}, - 'bsd-inferno-nettverk': {'id': 'BSD-Inferno-Nettverk', 'deprecated': False}, - 'bsd-protection': {'id': 'BSD-Protection', 'deprecated': False}, - 'bsd-source-beginning-file': {'id': 'BSD-Source-beginning-file', 'deprecated': False}, - 'bsd-source-code': {'id': 'BSD-Source-Code', 'deprecated': False}, - 'bsd-systemics': {'id': 'BSD-Systemics', 'deprecated': False}, - 'bsd-systemics-w3works': {'id': 'BSD-Systemics-W3Works', 'deprecated': False}, - 'bsl-1.0': {'id': 'BSL-1.0', 'deprecated': False}, - 'busl-1.1': {'id': 'BUSL-1.1', 'deprecated': False}, - 'bzip2-1.0.5': {'id': 'bzip2-1.0.5', 'deprecated': True}, - 'bzip2-1.0.6': {'id': 'bzip2-1.0.6', 'deprecated': False}, - 'c-uda-1.0': {'id': 'C-UDA-1.0', 'deprecated': False}, - 'cal-1.0': {'id': 'CAL-1.0', 'deprecated': False}, - 'cal-1.0-combined-work-exception': {'id': 'CAL-1.0-Combined-Work-Exception', 'deprecated': False}, - 'caldera': {'id': 'Caldera', 'deprecated': False}, - 'caldera-no-preamble': {'id': 'Caldera-no-preamble', 'deprecated': False}, - 'catharon': {'id': 'Catharon', 'deprecated': False}, - 'catosl-1.1': {'id': 'CATOSL-1.1', 'deprecated': False}, - 'cc-by-1.0': {'id': 'CC-BY-1.0', 'deprecated': False}, - 'cc-by-2.0': {'id': 'CC-BY-2.0', 'deprecated': False}, - 'cc-by-2.5': {'id': 'CC-BY-2.5', 'deprecated': False}, - 'cc-by-2.5-au': {'id': 'CC-BY-2.5-AU', 'deprecated': False}, - 'cc-by-3.0': {'id': 'CC-BY-3.0', 'deprecated': False}, - 'cc-by-3.0-at': {'id': 'CC-BY-3.0-AT', 'deprecated': False}, - 'cc-by-3.0-au': {'id': 'CC-BY-3.0-AU', 'deprecated': False}, - 'cc-by-3.0-de': {'id': 'CC-BY-3.0-DE', 'deprecated': False}, - 'cc-by-3.0-igo': {'id': 'CC-BY-3.0-IGO', 'deprecated': False}, - 'cc-by-3.0-nl': {'id': 'CC-BY-3.0-NL', 'deprecated': False}, - 'cc-by-3.0-us': {'id': 'CC-BY-3.0-US', 'deprecated': False}, - 'cc-by-4.0': {'id': 'CC-BY-4.0', 'deprecated': False}, - 'cc-by-nc-1.0': {'id': 'CC-BY-NC-1.0', 'deprecated': False}, - 'cc-by-nc-2.0': {'id': 'CC-BY-NC-2.0', 'deprecated': False}, - 'cc-by-nc-2.5': {'id': 'CC-BY-NC-2.5', 'deprecated': False}, - 'cc-by-nc-3.0': {'id': 'CC-BY-NC-3.0', 'deprecated': False}, - 'cc-by-nc-3.0-de': {'id': 'CC-BY-NC-3.0-DE', 'deprecated': False}, - 'cc-by-nc-4.0': {'id': 'CC-BY-NC-4.0', 'deprecated': False}, - 'cc-by-nc-nd-1.0': {'id': 'CC-BY-NC-ND-1.0', 'deprecated': False}, - 'cc-by-nc-nd-2.0': {'id': 'CC-BY-NC-ND-2.0', 'deprecated': False}, - 'cc-by-nc-nd-2.5': {'id': 'CC-BY-NC-ND-2.5', 'deprecated': False}, - 'cc-by-nc-nd-3.0': {'id': 'CC-BY-NC-ND-3.0', 'deprecated': False}, - 'cc-by-nc-nd-3.0-de': {'id': 'CC-BY-NC-ND-3.0-DE', 'deprecated': False}, - 'cc-by-nc-nd-3.0-igo': {'id': 'CC-BY-NC-ND-3.0-IGO', 'deprecated': False}, - 'cc-by-nc-nd-4.0': {'id': 'CC-BY-NC-ND-4.0', 'deprecated': False}, - 'cc-by-nc-sa-1.0': {'id': 'CC-BY-NC-SA-1.0', 'deprecated': False}, - 'cc-by-nc-sa-2.0': {'id': 'CC-BY-NC-SA-2.0', 'deprecated': False}, - 'cc-by-nc-sa-2.0-de': {'id': 'CC-BY-NC-SA-2.0-DE', 'deprecated': False}, - 'cc-by-nc-sa-2.0-fr': {'id': 'CC-BY-NC-SA-2.0-FR', 'deprecated': False}, - 'cc-by-nc-sa-2.0-uk': {'id': 'CC-BY-NC-SA-2.0-UK', 'deprecated': False}, - 'cc-by-nc-sa-2.5': {'id': 'CC-BY-NC-SA-2.5', 'deprecated': False}, - 'cc-by-nc-sa-3.0': {'id': 'CC-BY-NC-SA-3.0', 'deprecated': False}, - 'cc-by-nc-sa-3.0-de': {'id': 'CC-BY-NC-SA-3.0-DE', 'deprecated': False}, - 'cc-by-nc-sa-3.0-igo': {'id': 'CC-BY-NC-SA-3.0-IGO', 'deprecated': False}, - 'cc-by-nc-sa-4.0': {'id': 'CC-BY-NC-SA-4.0', 'deprecated': False}, - 'cc-by-nd-1.0': {'id': 'CC-BY-ND-1.0', 'deprecated': False}, - 'cc-by-nd-2.0': {'id': 'CC-BY-ND-2.0', 'deprecated': False}, - 'cc-by-nd-2.5': {'id': 'CC-BY-ND-2.5', 'deprecated': False}, - 'cc-by-nd-3.0': {'id': 'CC-BY-ND-3.0', 'deprecated': False}, - 'cc-by-nd-3.0-de': {'id': 'CC-BY-ND-3.0-DE', 'deprecated': False}, - 'cc-by-nd-4.0': {'id': 'CC-BY-ND-4.0', 'deprecated': False}, - 'cc-by-sa-1.0': {'id': 'CC-BY-SA-1.0', 'deprecated': False}, - 'cc-by-sa-2.0': {'id': 'CC-BY-SA-2.0', 'deprecated': False}, - 'cc-by-sa-2.0-uk': {'id': 'CC-BY-SA-2.0-UK', 'deprecated': False}, - 'cc-by-sa-2.1-jp': {'id': 'CC-BY-SA-2.1-JP', 'deprecated': False}, - 'cc-by-sa-2.5': {'id': 'CC-BY-SA-2.5', 'deprecated': False}, - 'cc-by-sa-3.0': {'id': 'CC-BY-SA-3.0', 'deprecated': False}, - 'cc-by-sa-3.0-at': {'id': 'CC-BY-SA-3.0-AT', 'deprecated': False}, - 'cc-by-sa-3.0-de': {'id': 'CC-BY-SA-3.0-DE', 'deprecated': False}, - 'cc-by-sa-3.0-igo': {'id': 'CC-BY-SA-3.0-IGO', 'deprecated': False}, - 'cc-by-sa-4.0': {'id': 'CC-BY-SA-4.0', 'deprecated': False}, - 'cc-pddc': {'id': 'CC-PDDC', 'deprecated': False}, - 'cc-pdm-1.0': {'id': 'CC-PDM-1.0', 'deprecated': False}, - 'cc-sa-1.0': {'id': 'CC-SA-1.0', 'deprecated': False}, - 'cc0-1.0': {'id': 'CC0-1.0', 'deprecated': False}, - 'cddl-1.0': {'id': 'CDDL-1.0', 'deprecated': False}, - 'cddl-1.1': {'id': 'CDDL-1.1', 'deprecated': False}, - 'cdl-1.0': {'id': 'CDL-1.0', 'deprecated': False}, - 'cdla-permissive-1.0': {'id': 'CDLA-Permissive-1.0', 'deprecated': False}, - 'cdla-permissive-2.0': {'id': 'CDLA-Permissive-2.0', 'deprecated': False}, - 'cdla-sharing-1.0': {'id': 'CDLA-Sharing-1.0', 'deprecated': False}, - 'cecill-1.0': {'id': 'CECILL-1.0', 'deprecated': False}, - 'cecill-1.1': {'id': 'CECILL-1.1', 'deprecated': False}, - 'cecill-2.0': {'id': 'CECILL-2.0', 'deprecated': False}, - 'cecill-2.1': {'id': 'CECILL-2.1', 'deprecated': False}, - 'cecill-b': {'id': 'CECILL-B', 'deprecated': False}, - 'cecill-c': {'id': 'CECILL-C', 'deprecated': False}, - 'cern-ohl-1.1': {'id': 'CERN-OHL-1.1', 'deprecated': False}, - 'cern-ohl-1.2': {'id': 'CERN-OHL-1.2', 'deprecated': False}, - 'cern-ohl-p-2.0': {'id': 'CERN-OHL-P-2.0', 'deprecated': False}, - 'cern-ohl-s-2.0': {'id': 'CERN-OHL-S-2.0', 'deprecated': False}, - 'cern-ohl-w-2.0': {'id': 'CERN-OHL-W-2.0', 'deprecated': False}, - 'cfitsio': {'id': 'CFITSIO', 'deprecated': False}, - 'check-cvs': {'id': 'check-cvs', 'deprecated': False}, - 'checkmk': {'id': 'checkmk', 'deprecated': False}, - 'clartistic': {'id': 'ClArtistic', 'deprecated': False}, - 'clips': {'id': 'Clips', 'deprecated': False}, - 'cmu-mach': {'id': 'CMU-Mach', 'deprecated': False}, - 'cmu-mach-nodoc': {'id': 'CMU-Mach-nodoc', 'deprecated': False}, - 'cnri-jython': {'id': 'CNRI-Jython', 'deprecated': False}, - 'cnri-python': {'id': 'CNRI-Python', 'deprecated': False}, - 'cnri-python-gpl-compatible': {'id': 'CNRI-Python-GPL-Compatible', 'deprecated': False}, - 'coil-1.0': {'id': 'COIL-1.0', 'deprecated': False}, - 'community-spec-1.0': {'id': 'Community-Spec-1.0', 'deprecated': False}, - 'condor-1.1': {'id': 'Condor-1.1', 'deprecated': False}, - 'copyleft-next-0.3.0': {'id': 'copyleft-next-0.3.0', 'deprecated': False}, - 'copyleft-next-0.3.1': {'id': 'copyleft-next-0.3.1', 'deprecated': False}, - 'cornell-lossless-jpeg': {'id': 'Cornell-Lossless-JPEG', 'deprecated': False}, - 'cpal-1.0': {'id': 'CPAL-1.0', 'deprecated': False}, - 'cpl-1.0': {'id': 'CPL-1.0', 'deprecated': False}, - 'cpol-1.02': {'id': 'CPOL-1.02', 'deprecated': False}, - 'cronyx': {'id': 'Cronyx', 'deprecated': False}, - 'crossword': {'id': 'Crossword', 'deprecated': False}, - 'cryptoswift': {'id': 'CryptoSwift', 'deprecated': False}, - 'crystalstacker': {'id': 'CrystalStacker', 'deprecated': False}, - 'cua-opl-1.0': {'id': 'CUA-OPL-1.0', 'deprecated': False}, - 'cube': {'id': 'Cube', 'deprecated': False}, - 'curl': {'id': 'curl', 'deprecated': False}, - 'cve-tou': {'id': 'cve-tou', 'deprecated': False}, - 'd-fsl-1.0': {'id': 'D-FSL-1.0', 'deprecated': False}, - 'dec-3-clause': {'id': 'DEC-3-Clause', 'deprecated': False}, - 'diffmark': {'id': 'diffmark', 'deprecated': False}, - 'dl-de-by-2.0': {'id': 'DL-DE-BY-2.0', 'deprecated': False}, - 'dl-de-zero-2.0': {'id': 'DL-DE-ZERO-2.0', 'deprecated': False}, - 'doc': {'id': 'DOC', 'deprecated': False}, - 'docbook-dtd': {'id': 'DocBook-DTD', 'deprecated': False}, - 'docbook-schema': {'id': 'DocBook-Schema', 'deprecated': False}, - 'docbook-stylesheet': {'id': 'DocBook-Stylesheet', 'deprecated': False}, - 'docbook-xml': {'id': 'DocBook-XML', 'deprecated': False}, - 'dotseqn': {'id': 'Dotseqn', 'deprecated': False}, - 'drl-1.0': {'id': 'DRL-1.0', 'deprecated': False}, - 'drl-1.1': {'id': 'DRL-1.1', 'deprecated': False}, - 'dsdp': {'id': 'DSDP', 'deprecated': False}, - 'dtoa': {'id': 'dtoa', 'deprecated': False}, - 'dvipdfm': {'id': 'dvipdfm', 'deprecated': False}, - 'ecl-1.0': {'id': 'ECL-1.0', 'deprecated': False}, - 'ecl-2.0': {'id': 'ECL-2.0', 'deprecated': False}, - 'ecos-2.0': {'id': 'eCos-2.0', 'deprecated': True}, - 'efl-1.0': {'id': 'EFL-1.0', 'deprecated': False}, - 'efl-2.0': {'id': 'EFL-2.0', 'deprecated': False}, - 'egenix': {'id': 'eGenix', 'deprecated': False}, - 'elastic-2.0': {'id': 'Elastic-2.0', 'deprecated': False}, - 'entessa': {'id': 'Entessa', 'deprecated': False}, - 'epics': {'id': 'EPICS', 'deprecated': False}, - 'epl-1.0': {'id': 'EPL-1.0', 'deprecated': False}, - 'epl-2.0': {'id': 'EPL-2.0', 'deprecated': False}, - 'erlpl-1.1': {'id': 'ErlPL-1.1', 'deprecated': False}, - 'etalab-2.0': {'id': 'etalab-2.0', 'deprecated': False}, - 'eudatagrid': {'id': 'EUDatagrid', 'deprecated': False}, - 'eupl-1.0': {'id': 'EUPL-1.0', 'deprecated': False}, - 'eupl-1.1': {'id': 'EUPL-1.1', 'deprecated': False}, - 'eupl-1.2': {'id': 'EUPL-1.2', 'deprecated': False}, - 'eurosym': {'id': 'Eurosym', 'deprecated': False}, - 'fair': {'id': 'Fair', 'deprecated': False}, - 'fbm': {'id': 'FBM', 'deprecated': False}, - 'fdk-aac': {'id': 'FDK-AAC', 'deprecated': False}, - 'ferguson-twofish': {'id': 'Ferguson-Twofish', 'deprecated': False}, - 'frameworx-1.0': {'id': 'Frameworx-1.0', 'deprecated': False}, - 'freebsd-doc': {'id': 'FreeBSD-DOC', 'deprecated': False}, - 'freeimage': {'id': 'FreeImage', 'deprecated': False}, - 'fsfap': {'id': 'FSFAP', 'deprecated': False}, - 'fsfap-no-warranty-disclaimer': {'id': 'FSFAP-no-warranty-disclaimer', 'deprecated': False}, - 'fsful': {'id': 'FSFUL', 'deprecated': False}, - 'fsfullr': {'id': 'FSFULLR', 'deprecated': False}, - 'fsfullrsd': {'id': 'FSFULLRSD', 'deprecated': False}, - 'fsfullrwd': {'id': 'FSFULLRWD', 'deprecated': False}, - 'fsl-1.1-alv2': {'id': 'FSL-1.1-ALv2', 'deprecated': False}, - 'fsl-1.1-mit': {'id': 'FSL-1.1-MIT', 'deprecated': False}, - 'ftl': {'id': 'FTL', 'deprecated': False}, - 'furuseth': {'id': 'Furuseth', 'deprecated': False}, - 'fwlw': {'id': 'fwlw', 'deprecated': False}, - 'game-programming-gems': {'id': 'Game-Programming-Gems', 'deprecated': False}, - 'gcr-docs': {'id': 'GCR-docs', 'deprecated': False}, - 'gd': {'id': 'GD', 'deprecated': False}, - 'generic-xts': {'id': 'generic-xts', 'deprecated': False}, - 'gfdl-1.1': {'id': 'GFDL-1.1', 'deprecated': True}, - 'gfdl-1.1-invariants-only': {'id': 'GFDL-1.1-invariants-only', 'deprecated': False}, - 'gfdl-1.1-invariants-or-later': {'id': 'GFDL-1.1-invariants-or-later', 'deprecated': False}, - 'gfdl-1.1-no-invariants-only': {'id': 'GFDL-1.1-no-invariants-only', 'deprecated': False}, - 'gfdl-1.1-no-invariants-or-later': {'id': 'GFDL-1.1-no-invariants-or-later', 'deprecated': False}, - 'gfdl-1.1-only': {'id': 'GFDL-1.1-only', 'deprecated': False}, - 'gfdl-1.1-or-later': {'id': 'GFDL-1.1-or-later', 'deprecated': False}, - 'gfdl-1.2': {'id': 'GFDL-1.2', 'deprecated': True}, - 'gfdl-1.2-invariants-only': {'id': 'GFDL-1.2-invariants-only', 'deprecated': False}, - 'gfdl-1.2-invariants-or-later': {'id': 'GFDL-1.2-invariants-or-later', 'deprecated': False}, - 'gfdl-1.2-no-invariants-only': {'id': 'GFDL-1.2-no-invariants-only', 'deprecated': False}, - 'gfdl-1.2-no-invariants-or-later': {'id': 'GFDL-1.2-no-invariants-or-later', 'deprecated': False}, - 'gfdl-1.2-only': {'id': 'GFDL-1.2-only', 'deprecated': False}, - 'gfdl-1.2-or-later': {'id': 'GFDL-1.2-or-later', 'deprecated': False}, - 'gfdl-1.3': {'id': 'GFDL-1.3', 'deprecated': True}, - 'gfdl-1.3-invariants-only': {'id': 'GFDL-1.3-invariants-only', 'deprecated': False}, - 'gfdl-1.3-invariants-or-later': {'id': 'GFDL-1.3-invariants-or-later', 'deprecated': False}, - 'gfdl-1.3-no-invariants-only': {'id': 'GFDL-1.3-no-invariants-only', 'deprecated': False}, - 'gfdl-1.3-no-invariants-or-later': {'id': 'GFDL-1.3-no-invariants-or-later', 'deprecated': False}, - 'gfdl-1.3-only': {'id': 'GFDL-1.3-only', 'deprecated': False}, - 'gfdl-1.3-or-later': {'id': 'GFDL-1.3-or-later', 'deprecated': False}, - 'giftware': {'id': 'Giftware', 'deprecated': False}, - 'gl2ps': {'id': 'GL2PS', 'deprecated': False}, - 'glide': {'id': 'Glide', 'deprecated': False}, - 'glulxe': {'id': 'Glulxe', 'deprecated': False}, - 'glwtpl': {'id': 'GLWTPL', 'deprecated': False}, - 'gnuplot': {'id': 'gnuplot', 'deprecated': False}, - 'gpl-1.0': {'id': 'GPL-1.0', 'deprecated': True}, - 'gpl-1.0+': {'id': 'GPL-1.0+', 'deprecated': True}, - 'gpl-1.0-only': {'id': 'GPL-1.0-only', 'deprecated': False}, - 'gpl-1.0-or-later': {'id': 'GPL-1.0-or-later', 'deprecated': False}, - 'gpl-2.0': {'id': 'GPL-2.0', 'deprecated': True}, - 'gpl-2.0+': {'id': 'GPL-2.0+', 'deprecated': True}, - 'gpl-2.0-only': {'id': 'GPL-2.0-only', 'deprecated': False}, - 'gpl-2.0-or-later': {'id': 'GPL-2.0-or-later', 'deprecated': False}, - 'gpl-2.0-with-autoconf-exception': {'id': 'GPL-2.0-with-autoconf-exception', 'deprecated': True}, - 'gpl-2.0-with-bison-exception': {'id': 'GPL-2.0-with-bison-exception', 'deprecated': True}, - 'gpl-2.0-with-classpath-exception': {'id': 'GPL-2.0-with-classpath-exception', 'deprecated': True}, - 'gpl-2.0-with-font-exception': {'id': 'GPL-2.0-with-font-exception', 'deprecated': True}, - 'gpl-2.0-with-gcc-exception': {'id': 'GPL-2.0-with-GCC-exception', 'deprecated': True}, - 'gpl-3.0': {'id': 'GPL-3.0', 'deprecated': True}, - 'gpl-3.0+': {'id': 'GPL-3.0+', 'deprecated': True}, - 'gpl-3.0-only': {'id': 'GPL-3.0-only', 'deprecated': False}, - 'gpl-3.0-or-later': {'id': 'GPL-3.0-or-later', 'deprecated': False}, - 'gpl-3.0-with-autoconf-exception': {'id': 'GPL-3.0-with-autoconf-exception', 'deprecated': True}, - 'gpl-3.0-with-gcc-exception': {'id': 'GPL-3.0-with-GCC-exception', 'deprecated': True}, - 'graphics-gems': {'id': 'Graphics-Gems', 'deprecated': False}, - 'gsoap-1.3b': {'id': 'gSOAP-1.3b', 'deprecated': False}, - 'gtkbook': {'id': 'gtkbook', 'deprecated': False}, - 'gutmann': {'id': 'Gutmann', 'deprecated': False}, - 'haskellreport': {'id': 'HaskellReport', 'deprecated': False}, - 'hdf5': {'id': 'HDF5', 'deprecated': False}, - 'hdparm': {'id': 'hdparm', 'deprecated': False}, - 'hidapi': {'id': 'HIDAPI', 'deprecated': False}, - 'hippocratic-2.1': {'id': 'Hippocratic-2.1', 'deprecated': False}, - 'hp-1986': {'id': 'HP-1986', 'deprecated': False}, - 'hp-1989': {'id': 'HP-1989', 'deprecated': False}, - 'hpnd': {'id': 'HPND', 'deprecated': False}, - 'hpnd-dec': {'id': 'HPND-DEC', 'deprecated': False}, - 'hpnd-doc': {'id': 'HPND-doc', 'deprecated': False}, - 'hpnd-doc-sell': {'id': 'HPND-doc-sell', 'deprecated': False}, - 'hpnd-export-us': {'id': 'HPND-export-US', 'deprecated': False}, - 'hpnd-export-us-acknowledgement': {'id': 'HPND-export-US-acknowledgement', 'deprecated': False}, - 'hpnd-export-us-modify': {'id': 'HPND-export-US-modify', 'deprecated': False}, - 'hpnd-export2-us': {'id': 'HPND-export2-US', 'deprecated': False}, - 'hpnd-fenneberg-livingston': {'id': 'HPND-Fenneberg-Livingston', 'deprecated': False}, - 'hpnd-inria-imag': {'id': 'HPND-INRIA-IMAG', 'deprecated': False}, - 'hpnd-intel': {'id': 'HPND-Intel', 'deprecated': False}, - 'hpnd-kevlin-henney': {'id': 'HPND-Kevlin-Henney', 'deprecated': False}, - 'hpnd-markus-kuhn': {'id': 'HPND-Markus-Kuhn', 'deprecated': False}, - 'hpnd-merchantability-variant': {'id': 'HPND-merchantability-variant', 'deprecated': False}, - 'hpnd-mit-disclaimer': {'id': 'HPND-MIT-disclaimer', 'deprecated': False}, - 'hpnd-netrek': {'id': 'HPND-Netrek', 'deprecated': False}, - 'hpnd-pbmplus': {'id': 'HPND-Pbmplus', 'deprecated': False}, - 'hpnd-sell-mit-disclaimer-xserver': {'id': 'HPND-sell-MIT-disclaimer-xserver', 'deprecated': False}, - 'hpnd-sell-regexpr': {'id': 'HPND-sell-regexpr', 'deprecated': False}, - 'hpnd-sell-variant': {'id': 'HPND-sell-variant', 'deprecated': False}, - 'hpnd-sell-variant-mit-disclaimer': {'id': 'HPND-sell-variant-MIT-disclaimer', 'deprecated': False}, - 'hpnd-sell-variant-mit-disclaimer-rev': {'id': 'HPND-sell-variant-MIT-disclaimer-rev', 'deprecated': False}, - 'hpnd-uc': {'id': 'HPND-UC', 'deprecated': False}, - 'hpnd-uc-export-us': {'id': 'HPND-UC-export-US', 'deprecated': False}, - 'htmltidy': {'id': 'HTMLTIDY', 'deprecated': False}, - 'ibm-pibs': {'id': 'IBM-pibs', 'deprecated': False}, - 'icu': {'id': 'ICU', 'deprecated': False}, - 'iec-code-components-eula': {'id': 'IEC-Code-Components-EULA', 'deprecated': False}, - 'ijg': {'id': 'IJG', 'deprecated': False}, - 'ijg-short': {'id': 'IJG-short', 'deprecated': False}, - 'imagemagick': {'id': 'ImageMagick', 'deprecated': False}, - 'imatix': {'id': 'iMatix', 'deprecated': False}, - 'imlib2': {'id': 'Imlib2', 'deprecated': False}, - 'info-zip': {'id': 'Info-ZIP', 'deprecated': False}, - 'inner-net-2.0': {'id': 'Inner-Net-2.0', 'deprecated': False}, - 'innosetup': {'id': 'InnoSetup', 'deprecated': False}, - 'intel': {'id': 'Intel', 'deprecated': False}, - 'intel-acpi': {'id': 'Intel-ACPI', 'deprecated': False}, - 'interbase-1.0': {'id': 'Interbase-1.0', 'deprecated': False}, - 'ipa': {'id': 'IPA', 'deprecated': False}, - 'ipl-1.0': {'id': 'IPL-1.0', 'deprecated': False}, - 'isc': {'id': 'ISC', 'deprecated': False}, - 'isc-veillard': {'id': 'ISC-Veillard', 'deprecated': False}, - 'jam': {'id': 'Jam', 'deprecated': False}, - 'jasper-2.0': {'id': 'JasPer-2.0', 'deprecated': False}, - 'jove': {'id': 'jove', 'deprecated': False}, - 'jpl-image': {'id': 'JPL-image', 'deprecated': False}, - 'jpnic': {'id': 'JPNIC', 'deprecated': False}, - 'json': {'id': 'JSON', 'deprecated': False}, - 'kastrup': {'id': 'Kastrup', 'deprecated': False}, - 'kazlib': {'id': 'Kazlib', 'deprecated': False}, - 'knuth-ctan': {'id': 'Knuth-CTAN', 'deprecated': False}, - 'lal-1.2': {'id': 'LAL-1.2', 'deprecated': False}, - 'lal-1.3': {'id': 'LAL-1.3', 'deprecated': False}, - 'latex2e': {'id': 'Latex2e', 'deprecated': False}, - 'latex2e-translated-notice': {'id': 'Latex2e-translated-notice', 'deprecated': False}, - 'leptonica': {'id': 'Leptonica', 'deprecated': False}, - 'lgpl-2.0': {'id': 'LGPL-2.0', 'deprecated': True}, - 'lgpl-2.0+': {'id': 'LGPL-2.0+', 'deprecated': True}, - 'lgpl-2.0-only': {'id': 'LGPL-2.0-only', 'deprecated': False}, - 'lgpl-2.0-or-later': {'id': 'LGPL-2.0-or-later', 'deprecated': False}, - 'lgpl-2.1': {'id': 'LGPL-2.1', 'deprecated': True}, - 'lgpl-2.1+': {'id': 'LGPL-2.1+', 'deprecated': True}, - 'lgpl-2.1-only': {'id': 'LGPL-2.1-only', 'deprecated': False}, - 'lgpl-2.1-or-later': {'id': 'LGPL-2.1-or-later', 'deprecated': False}, - 'lgpl-3.0': {'id': 'LGPL-3.0', 'deprecated': True}, - 'lgpl-3.0+': {'id': 'LGPL-3.0+', 'deprecated': True}, - 'lgpl-3.0-only': {'id': 'LGPL-3.0-only', 'deprecated': False}, - 'lgpl-3.0-or-later': {'id': 'LGPL-3.0-or-later', 'deprecated': False}, - 'lgpllr': {'id': 'LGPLLR', 'deprecated': False}, - 'libpng': {'id': 'Libpng', 'deprecated': False}, - 'libpng-1.6.35': {'id': 'libpng-1.6.35', 'deprecated': False}, - 'libpng-2.0': {'id': 'libpng-2.0', 'deprecated': False}, - 'libselinux-1.0': {'id': 'libselinux-1.0', 'deprecated': False}, - 'libtiff': {'id': 'libtiff', 'deprecated': False}, - 'libutil-david-nugent': {'id': 'libutil-David-Nugent', 'deprecated': False}, - 'liliq-p-1.1': {'id': 'LiLiQ-P-1.1', 'deprecated': False}, - 'liliq-r-1.1': {'id': 'LiLiQ-R-1.1', 'deprecated': False}, - 'liliq-rplus-1.1': {'id': 'LiLiQ-Rplus-1.1', 'deprecated': False}, - 'linux-man-pages-1-para': {'id': 'Linux-man-pages-1-para', 'deprecated': False}, - 'linux-man-pages-copyleft': {'id': 'Linux-man-pages-copyleft', 'deprecated': False}, - 'linux-man-pages-copyleft-2-para': {'id': 'Linux-man-pages-copyleft-2-para', 'deprecated': False}, - 'linux-man-pages-copyleft-var': {'id': 'Linux-man-pages-copyleft-var', 'deprecated': False}, - 'linux-openib': {'id': 'Linux-OpenIB', 'deprecated': False}, - 'loop': {'id': 'LOOP', 'deprecated': False}, - 'lpd-document': {'id': 'LPD-document', 'deprecated': False}, - 'lpl-1.0': {'id': 'LPL-1.0', 'deprecated': False}, - 'lpl-1.02': {'id': 'LPL-1.02', 'deprecated': False}, - 'lppl-1.0': {'id': 'LPPL-1.0', 'deprecated': False}, - 'lppl-1.1': {'id': 'LPPL-1.1', 'deprecated': False}, - 'lppl-1.2': {'id': 'LPPL-1.2', 'deprecated': False}, - 'lppl-1.3a': {'id': 'LPPL-1.3a', 'deprecated': False}, - 'lppl-1.3c': {'id': 'LPPL-1.3c', 'deprecated': False}, - 'lsof': {'id': 'lsof', 'deprecated': False}, - 'lucida-bitmap-fonts': {'id': 'Lucida-Bitmap-Fonts', 'deprecated': False}, - 'lzma-sdk-9.11-to-9.20': {'id': 'LZMA-SDK-9.11-to-9.20', 'deprecated': False}, - 'lzma-sdk-9.22': {'id': 'LZMA-SDK-9.22', 'deprecated': False}, - 'mackerras-3-clause': {'id': 'Mackerras-3-Clause', 'deprecated': False}, - 'mackerras-3-clause-acknowledgment': {'id': 'Mackerras-3-Clause-acknowledgment', 'deprecated': False}, - 'magaz': {'id': 'magaz', 'deprecated': False}, - 'mailprio': {'id': 'mailprio', 'deprecated': False}, - 'makeindex': {'id': 'MakeIndex', 'deprecated': False}, - 'man2html': {'id': 'man2html', 'deprecated': False}, - 'martin-birgmeier': {'id': 'Martin-Birgmeier', 'deprecated': False}, - 'mcphee-slideshow': {'id': 'McPhee-slideshow', 'deprecated': False}, - 'metamail': {'id': 'metamail', 'deprecated': False}, - 'minpack': {'id': 'Minpack', 'deprecated': False}, - 'mips': {'id': 'MIPS', 'deprecated': False}, - 'miros': {'id': 'MirOS', 'deprecated': False}, - 'mit': {'id': 'MIT', 'deprecated': False}, - 'mit-0': {'id': 'MIT-0', 'deprecated': False}, - 'mit-advertising': {'id': 'MIT-advertising', 'deprecated': False}, - 'mit-click': {'id': 'MIT-Click', 'deprecated': False}, - 'mit-cmu': {'id': 'MIT-CMU', 'deprecated': False}, - 'mit-enna': {'id': 'MIT-enna', 'deprecated': False}, - 'mit-feh': {'id': 'MIT-feh', 'deprecated': False}, - 'mit-festival': {'id': 'MIT-Festival', 'deprecated': False}, - 'mit-khronos-old': {'id': 'MIT-Khronos-old', 'deprecated': False}, - 'mit-modern-variant': {'id': 'MIT-Modern-Variant', 'deprecated': False}, - 'mit-open-group': {'id': 'MIT-open-group', 'deprecated': False}, - 'mit-testregex': {'id': 'MIT-testregex', 'deprecated': False}, - 'mit-wu': {'id': 'MIT-Wu', 'deprecated': False}, - 'mitnfa': {'id': 'MITNFA', 'deprecated': False}, - 'mmixware': {'id': 'MMIXware', 'deprecated': False}, - 'motosoto': {'id': 'Motosoto', 'deprecated': False}, - 'mpeg-ssg': {'id': 'MPEG-SSG', 'deprecated': False}, - 'mpi-permissive': {'id': 'mpi-permissive', 'deprecated': False}, - 'mpich2': {'id': 'mpich2', 'deprecated': False}, - 'mpl-1.0': {'id': 'MPL-1.0', 'deprecated': False}, - 'mpl-1.1': {'id': 'MPL-1.1', 'deprecated': False}, - 'mpl-2.0': {'id': 'MPL-2.0', 'deprecated': False}, - 'mpl-2.0-no-copyleft-exception': {'id': 'MPL-2.0-no-copyleft-exception', 'deprecated': False}, - 'mplus': {'id': 'mplus', 'deprecated': False}, - 'ms-lpl': {'id': 'MS-LPL', 'deprecated': False}, - 'ms-pl': {'id': 'MS-PL', 'deprecated': False}, - 'ms-rl': {'id': 'MS-RL', 'deprecated': False}, - 'mtll': {'id': 'MTLL', 'deprecated': False}, - 'mulanpsl-1.0': {'id': 'MulanPSL-1.0', 'deprecated': False}, - 'mulanpsl-2.0': {'id': 'MulanPSL-2.0', 'deprecated': False}, - 'multics': {'id': 'Multics', 'deprecated': False}, - 'mup': {'id': 'Mup', 'deprecated': False}, - 'naist-2003': {'id': 'NAIST-2003', 'deprecated': False}, - 'nasa-1.3': {'id': 'NASA-1.3', 'deprecated': False}, - 'naumen': {'id': 'Naumen', 'deprecated': False}, - 'nbpl-1.0': {'id': 'NBPL-1.0', 'deprecated': False}, - 'ncbi-pd': {'id': 'NCBI-PD', 'deprecated': False}, - 'ncgl-uk-2.0': {'id': 'NCGL-UK-2.0', 'deprecated': False}, - 'ncl': {'id': 'NCL', 'deprecated': False}, - 'ncsa': {'id': 'NCSA', 'deprecated': False}, - 'net-snmp': {'id': 'Net-SNMP', 'deprecated': True}, - 'netcdf': {'id': 'NetCDF', 'deprecated': False}, - 'newsletr': {'id': 'Newsletr', 'deprecated': False}, - 'ngpl': {'id': 'NGPL', 'deprecated': False}, - 'ngrep': {'id': 'ngrep', 'deprecated': False}, - 'nicta-1.0': {'id': 'NICTA-1.0', 'deprecated': False}, - 'nist-pd': {'id': 'NIST-PD', 'deprecated': False}, - 'nist-pd-fallback': {'id': 'NIST-PD-fallback', 'deprecated': False}, - 'nist-software': {'id': 'NIST-Software', 'deprecated': False}, - 'nlod-1.0': {'id': 'NLOD-1.0', 'deprecated': False}, - 'nlod-2.0': {'id': 'NLOD-2.0', 'deprecated': False}, - 'nlpl': {'id': 'NLPL', 'deprecated': False}, - 'nokia': {'id': 'Nokia', 'deprecated': False}, - 'nosl': {'id': 'NOSL', 'deprecated': False}, - 'noweb': {'id': 'Noweb', 'deprecated': False}, - 'npl-1.0': {'id': 'NPL-1.0', 'deprecated': False}, - 'npl-1.1': {'id': 'NPL-1.1', 'deprecated': False}, - 'nposl-3.0': {'id': 'NPOSL-3.0', 'deprecated': False}, - 'nrl': {'id': 'NRL', 'deprecated': False}, - 'ntia-pd': {'id': 'NTIA-PD', 'deprecated': False}, - 'ntp': {'id': 'NTP', 'deprecated': False}, - 'ntp-0': {'id': 'NTP-0', 'deprecated': False}, - 'nunit': {'id': 'Nunit', 'deprecated': True}, - 'o-uda-1.0': {'id': 'O-UDA-1.0', 'deprecated': False}, - 'oar': {'id': 'OAR', 'deprecated': False}, - 'occt-pl': {'id': 'OCCT-PL', 'deprecated': False}, - 'oclc-2.0': {'id': 'OCLC-2.0', 'deprecated': False}, - 'odbl-1.0': {'id': 'ODbL-1.0', 'deprecated': False}, - 'odc-by-1.0': {'id': 'ODC-By-1.0', 'deprecated': False}, - 'offis': {'id': 'OFFIS', 'deprecated': False}, - 'ofl-1.0': {'id': 'OFL-1.0', 'deprecated': False}, - 'ofl-1.0-no-rfn': {'id': 'OFL-1.0-no-RFN', 'deprecated': False}, - 'ofl-1.0-rfn': {'id': 'OFL-1.0-RFN', 'deprecated': False}, - 'ofl-1.1': {'id': 'OFL-1.1', 'deprecated': False}, - 'ofl-1.1-no-rfn': {'id': 'OFL-1.1-no-RFN', 'deprecated': False}, - 'ofl-1.1-rfn': {'id': 'OFL-1.1-RFN', 'deprecated': False}, - 'ogc-1.0': {'id': 'OGC-1.0', 'deprecated': False}, - 'ogdl-taiwan-1.0': {'id': 'OGDL-Taiwan-1.0', 'deprecated': False}, - 'ogl-canada-2.0': {'id': 'OGL-Canada-2.0', 'deprecated': False}, - 'ogl-uk-1.0': {'id': 'OGL-UK-1.0', 'deprecated': False}, - 'ogl-uk-2.0': {'id': 'OGL-UK-2.0', 'deprecated': False}, - 'ogl-uk-3.0': {'id': 'OGL-UK-3.0', 'deprecated': False}, - 'ogtsl': {'id': 'OGTSL', 'deprecated': False}, - 'oldap-1.1': {'id': 'OLDAP-1.1', 'deprecated': False}, - 'oldap-1.2': {'id': 'OLDAP-1.2', 'deprecated': False}, - 'oldap-1.3': {'id': 'OLDAP-1.3', 'deprecated': False}, - 'oldap-1.4': {'id': 'OLDAP-1.4', 'deprecated': False}, - 'oldap-2.0': {'id': 'OLDAP-2.0', 'deprecated': False}, - 'oldap-2.0.1': {'id': 'OLDAP-2.0.1', 'deprecated': False}, - 'oldap-2.1': {'id': 'OLDAP-2.1', 'deprecated': False}, - 'oldap-2.2': {'id': 'OLDAP-2.2', 'deprecated': False}, - 'oldap-2.2.1': {'id': 'OLDAP-2.2.1', 'deprecated': False}, - 'oldap-2.2.2': {'id': 'OLDAP-2.2.2', 'deprecated': False}, - 'oldap-2.3': {'id': 'OLDAP-2.3', 'deprecated': False}, - 'oldap-2.4': {'id': 'OLDAP-2.4', 'deprecated': False}, - 'oldap-2.5': {'id': 'OLDAP-2.5', 'deprecated': False}, - 'oldap-2.6': {'id': 'OLDAP-2.6', 'deprecated': False}, - 'oldap-2.7': {'id': 'OLDAP-2.7', 'deprecated': False}, - 'oldap-2.8': {'id': 'OLDAP-2.8', 'deprecated': False}, - 'olfl-1.3': {'id': 'OLFL-1.3', 'deprecated': False}, - 'oml': {'id': 'OML', 'deprecated': False}, - 'openpbs-2.3': {'id': 'OpenPBS-2.3', 'deprecated': False}, - 'openssl': {'id': 'OpenSSL', 'deprecated': False}, - 'openssl-standalone': {'id': 'OpenSSL-standalone', 'deprecated': False}, - 'openvision': {'id': 'OpenVision', 'deprecated': False}, - 'opl-1.0': {'id': 'OPL-1.0', 'deprecated': False}, - 'opl-uk-3.0': {'id': 'OPL-UK-3.0', 'deprecated': False}, - 'opubl-1.0': {'id': 'OPUBL-1.0', 'deprecated': False}, - 'oset-pl-2.1': {'id': 'OSET-PL-2.1', 'deprecated': False}, - 'osl-1.0': {'id': 'OSL-1.0', 'deprecated': False}, - 'osl-1.1': {'id': 'OSL-1.1', 'deprecated': False}, - 'osl-2.0': {'id': 'OSL-2.0', 'deprecated': False}, - 'osl-2.1': {'id': 'OSL-2.1', 'deprecated': False}, - 'osl-3.0': {'id': 'OSL-3.0', 'deprecated': False}, - 'padl': {'id': 'PADL', 'deprecated': False}, - 'parity-6.0.0': {'id': 'Parity-6.0.0', 'deprecated': False}, - 'parity-7.0.0': {'id': 'Parity-7.0.0', 'deprecated': False}, - 'pddl-1.0': {'id': 'PDDL-1.0', 'deprecated': False}, - 'php-3.0': {'id': 'PHP-3.0', 'deprecated': False}, - 'php-3.01': {'id': 'PHP-3.01', 'deprecated': False}, - 'pixar': {'id': 'Pixar', 'deprecated': False}, - 'pkgconf': {'id': 'pkgconf', 'deprecated': False}, - 'plexus': {'id': 'Plexus', 'deprecated': False}, - 'pnmstitch': {'id': 'pnmstitch', 'deprecated': False}, - 'polyform-noncommercial-1.0.0': {'id': 'PolyForm-Noncommercial-1.0.0', 'deprecated': False}, - 'polyform-small-business-1.0.0': {'id': 'PolyForm-Small-Business-1.0.0', 'deprecated': False}, - 'postgresql': {'id': 'PostgreSQL', 'deprecated': False}, - 'ppl': {'id': 'PPL', 'deprecated': False}, - 'psf-2.0': {'id': 'PSF-2.0', 'deprecated': False}, - 'psfrag': {'id': 'psfrag', 'deprecated': False}, - 'psutils': {'id': 'psutils', 'deprecated': False}, - 'python-2.0': {'id': 'Python-2.0', 'deprecated': False}, - 'python-2.0.1': {'id': 'Python-2.0.1', 'deprecated': False}, - 'python-ldap': {'id': 'python-ldap', 'deprecated': False}, - 'qhull': {'id': 'Qhull', 'deprecated': False}, - 'qpl-1.0': {'id': 'QPL-1.0', 'deprecated': False}, - 'qpl-1.0-inria-2004': {'id': 'QPL-1.0-INRIA-2004', 'deprecated': False}, - 'radvd': {'id': 'radvd', 'deprecated': False}, - 'rdisc': {'id': 'Rdisc', 'deprecated': False}, - 'rhecos-1.1': {'id': 'RHeCos-1.1', 'deprecated': False}, - 'rpl-1.1': {'id': 'RPL-1.1', 'deprecated': False}, - 'rpl-1.5': {'id': 'RPL-1.5', 'deprecated': False}, - 'rpsl-1.0': {'id': 'RPSL-1.0', 'deprecated': False}, - 'rsa-md': {'id': 'RSA-MD', 'deprecated': False}, - 'rscpl': {'id': 'RSCPL', 'deprecated': False}, - 'ruby': {'id': 'Ruby', 'deprecated': False}, - 'ruby-pty': {'id': 'Ruby-pty', 'deprecated': False}, - 'sax-pd': {'id': 'SAX-PD', 'deprecated': False}, - 'sax-pd-2.0': {'id': 'SAX-PD-2.0', 'deprecated': False}, - 'saxpath': {'id': 'Saxpath', 'deprecated': False}, - 'scea': {'id': 'SCEA', 'deprecated': False}, - 'schemereport': {'id': 'SchemeReport', 'deprecated': False}, - 'sendmail': {'id': 'Sendmail', 'deprecated': False}, - 'sendmail-8.23': {'id': 'Sendmail-8.23', 'deprecated': False}, - 'sendmail-open-source-1.1': {'id': 'Sendmail-Open-Source-1.1', 'deprecated': False}, - 'sgi-b-1.0': {'id': 'SGI-B-1.0', 'deprecated': False}, - 'sgi-b-1.1': {'id': 'SGI-B-1.1', 'deprecated': False}, - 'sgi-b-2.0': {'id': 'SGI-B-2.0', 'deprecated': False}, - 'sgi-opengl': {'id': 'SGI-OpenGL', 'deprecated': False}, - 'sgp4': {'id': 'SGP4', 'deprecated': False}, - 'shl-0.5': {'id': 'SHL-0.5', 'deprecated': False}, - 'shl-0.51': {'id': 'SHL-0.51', 'deprecated': False}, - 'simpl-2.0': {'id': 'SimPL-2.0', 'deprecated': False}, - 'sissl': {'id': 'SISSL', 'deprecated': False}, - 'sissl-1.2': {'id': 'SISSL-1.2', 'deprecated': False}, - 'sl': {'id': 'SL', 'deprecated': False}, - 'sleepycat': {'id': 'Sleepycat', 'deprecated': False}, - 'smail-gpl': {'id': 'SMAIL-GPL', 'deprecated': False}, - 'smlnj': {'id': 'SMLNJ', 'deprecated': False}, - 'smppl': {'id': 'SMPPL', 'deprecated': False}, - 'snia': {'id': 'SNIA', 'deprecated': False}, - 'snprintf': {'id': 'snprintf', 'deprecated': False}, - 'sofa': {'id': 'SOFA', 'deprecated': False}, - 'softsurfer': {'id': 'softSurfer', 'deprecated': False}, - 'soundex': {'id': 'Soundex', 'deprecated': False}, - 'spencer-86': {'id': 'Spencer-86', 'deprecated': False}, - 'spencer-94': {'id': 'Spencer-94', 'deprecated': False}, - 'spencer-99': {'id': 'Spencer-99', 'deprecated': False}, - 'spl-1.0': {'id': 'SPL-1.0', 'deprecated': False}, - 'ssh-keyscan': {'id': 'ssh-keyscan', 'deprecated': False}, - 'ssh-openssh': {'id': 'SSH-OpenSSH', 'deprecated': False}, - 'ssh-short': {'id': 'SSH-short', 'deprecated': False}, - 'ssleay-standalone': {'id': 'SSLeay-standalone', 'deprecated': False}, - 'sspl-1.0': {'id': 'SSPL-1.0', 'deprecated': False}, - 'standardml-nj': {'id': 'StandardML-NJ', 'deprecated': True}, - 'sugarcrm-1.1.3': {'id': 'SugarCRM-1.1.3', 'deprecated': False}, - 'sul-1.0': {'id': 'SUL-1.0', 'deprecated': False}, - 'sun-ppp': {'id': 'Sun-PPP', 'deprecated': False}, - 'sun-ppp-2000': {'id': 'Sun-PPP-2000', 'deprecated': False}, - 'sunpro': {'id': 'SunPro', 'deprecated': False}, - 'swl': {'id': 'SWL', 'deprecated': False}, - 'swrule': {'id': 'swrule', 'deprecated': False}, - 'symlinks': {'id': 'Symlinks', 'deprecated': False}, - 'tapr-ohl-1.0': {'id': 'TAPR-OHL-1.0', 'deprecated': False}, - 'tcl': {'id': 'TCL', 'deprecated': False}, - 'tcp-wrappers': {'id': 'TCP-wrappers', 'deprecated': False}, - 'termreadkey': {'id': 'TermReadKey', 'deprecated': False}, - 'tgppl-1.0': {'id': 'TGPPL-1.0', 'deprecated': False}, - 'thirdeye': {'id': 'ThirdEye', 'deprecated': False}, - 'threeparttable': {'id': 'threeparttable', 'deprecated': False}, - 'tmate': {'id': 'TMate', 'deprecated': False}, - 'torque-1.1': {'id': 'TORQUE-1.1', 'deprecated': False}, - 'tosl': {'id': 'TOSL', 'deprecated': False}, - 'tpdl': {'id': 'TPDL', 'deprecated': False}, - 'tpl-1.0': {'id': 'TPL-1.0', 'deprecated': False}, - 'trustedqsl': {'id': 'TrustedQSL', 'deprecated': False}, - 'ttwl': {'id': 'TTWL', 'deprecated': False}, - 'ttyp0': {'id': 'TTYP0', 'deprecated': False}, - 'tu-berlin-1.0': {'id': 'TU-Berlin-1.0', 'deprecated': False}, - 'tu-berlin-2.0': {'id': 'TU-Berlin-2.0', 'deprecated': False}, - 'ubuntu-font-1.0': {'id': 'Ubuntu-font-1.0', 'deprecated': False}, - 'ucar': {'id': 'UCAR', 'deprecated': False}, - 'ucl-1.0': {'id': 'UCL-1.0', 'deprecated': False}, - 'ulem': {'id': 'ulem', 'deprecated': False}, - 'umich-merit': {'id': 'UMich-Merit', 'deprecated': False}, - 'unicode-3.0': {'id': 'Unicode-3.0', 'deprecated': False}, - 'unicode-dfs-2015': {'id': 'Unicode-DFS-2015', 'deprecated': False}, - 'unicode-dfs-2016': {'id': 'Unicode-DFS-2016', 'deprecated': False}, - 'unicode-tou': {'id': 'Unicode-TOU', 'deprecated': False}, - 'unixcrypt': {'id': 'UnixCrypt', 'deprecated': False}, - 'unlicense': {'id': 'Unlicense', 'deprecated': False}, - 'unlicense-libtelnet': {'id': 'Unlicense-libtelnet', 'deprecated': False}, - 'unlicense-libwhirlpool': {'id': 'Unlicense-libwhirlpool', 'deprecated': False}, - 'upl-1.0': {'id': 'UPL-1.0', 'deprecated': False}, - 'urt-rle': {'id': 'URT-RLE', 'deprecated': False}, - 'vim': {'id': 'Vim', 'deprecated': False}, - 'vostrom': {'id': 'VOSTROM', 'deprecated': False}, - 'vsl-1.0': {'id': 'VSL-1.0', 'deprecated': False}, - 'w3c': {'id': 'W3C', 'deprecated': False}, - 'w3c-19980720': {'id': 'W3C-19980720', 'deprecated': False}, - 'w3c-20150513': {'id': 'W3C-20150513', 'deprecated': False}, - 'w3m': {'id': 'w3m', 'deprecated': False}, - 'watcom-1.0': {'id': 'Watcom-1.0', 'deprecated': False}, - 'widget-workshop': {'id': 'Widget-Workshop', 'deprecated': False}, - 'wsuipa': {'id': 'Wsuipa', 'deprecated': False}, - 'wtfpl': {'id': 'WTFPL', 'deprecated': False}, - 'wwl': {'id': 'wwl', 'deprecated': False}, - 'wxwindows': {'id': 'wxWindows', 'deprecated': True}, - 'x11': {'id': 'X11', 'deprecated': False}, - 'x11-distribute-modifications-variant': {'id': 'X11-distribute-modifications-variant', 'deprecated': False}, - 'x11-swapped': {'id': 'X11-swapped', 'deprecated': False}, - 'xdebug-1.03': {'id': 'Xdebug-1.03', 'deprecated': False}, - 'xerox': {'id': 'Xerox', 'deprecated': False}, - 'xfig': {'id': 'Xfig', 'deprecated': False}, - 'xfree86-1.1': {'id': 'XFree86-1.1', 'deprecated': False}, - 'xinetd': {'id': 'xinetd', 'deprecated': False}, - 'xkeyboard-config-zinoviev': {'id': 'xkeyboard-config-Zinoviev', 'deprecated': False}, - 'xlock': {'id': 'xlock', 'deprecated': False}, - 'xnet': {'id': 'Xnet', 'deprecated': False}, - 'xpp': {'id': 'xpp', 'deprecated': False}, - 'xskat': {'id': 'XSkat', 'deprecated': False}, - 'xzoom': {'id': 'xzoom', 'deprecated': False}, - 'ypl-1.0': {'id': 'YPL-1.0', 'deprecated': False}, - 'ypl-1.1': {'id': 'YPL-1.1', 'deprecated': False}, - 'zed': {'id': 'Zed', 'deprecated': False}, - 'zeeff': {'id': 'Zeeff', 'deprecated': False}, - 'zend-2.0': {'id': 'Zend-2.0', 'deprecated': False}, - 'zimbra-1.3': {'id': 'Zimbra-1.3', 'deprecated': False}, - 'zimbra-1.4': {'id': 'Zimbra-1.4', 'deprecated': False}, - 'zlib': {'id': 'Zlib', 'deprecated': False}, - 'zlib-acknowledgement': {'id': 'zlib-acknowledgement', 'deprecated': False}, - 'zpl-1.1': {'id': 'ZPL-1.1', 'deprecated': False}, - 'zpl-2.0': {'id': 'ZPL-2.0', 'deprecated': False}, - 'zpl-2.1': {'id': 'ZPL-2.1', 'deprecated': False}, -} - -EXCEPTIONS: dict[str, SPDXException] = { - '389-exception': {'id': '389-exception', 'deprecated': False}, - 'asterisk-exception': {'id': 'Asterisk-exception', 'deprecated': False}, - 'asterisk-linking-protocols-exception': {'id': 'Asterisk-linking-protocols-exception', 'deprecated': False}, - 'autoconf-exception-2.0': {'id': 'Autoconf-exception-2.0', 'deprecated': False}, - 'autoconf-exception-3.0': {'id': 'Autoconf-exception-3.0', 'deprecated': False}, - 'autoconf-exception-generic': {'id': 'Autoconf-exception-generic', 'deprecated': False}, - 'autoconf-exception-generic-3.0': {'id': 'Autoconf-exception-generic-3.0', 'deprecated': False}, - 'autoconf-exception-macro': {'id': 'Autoconf-exception-macro', 'deprecated': False}, - 'bison-exception-1.24': {'id': 'Bison-exception-1.24', 'deprecated': False}, - 'bison-exception-2.2': {'id': 'Bison-exception-2.2', 'deprecated': False}, - 'bootloader-exception': {'id': 'Bootloader-exception', 'deprecated': False}, - 'cgal-linking-exception': {'id': 'CGAL-linking-exception', 'deprecated': False}, - 'classpath-exception-2.0': {'id': 'Classpath-exception-2.0', 'deprecated': False}, - 'clisp-exception-2.0': {'id': 'CLISP-exception-2.0', 'deprecated': False}, - 'cryptsetup-openssl-exception': {'id': 'cryptsetup-OpenSSL-exception', 'deprecated': False}, - 'digia-qt-lgpl-exception-1.1': {'id': 'Digia-Qt-LGPL-exception-1.1', 'deprecated': False}, - 'digirule-foss-exception': {'id': 'DigiRule-FOSS-exception', 'deprecated': False}, - 'ecos-exception-2.0': {'id': 'eCos-exception-2.0', 'deprecated': False}, - 'erlang-otp-linking-exception': {'id': 'erlang-otp-linking-exception', 'deprecated': False}, - 'fawkes-runtime-exception': {'id': 'Fawkes-Runtime-exception', 'deprecated': False}, - 'fltk-exception': {'id': 'FLTK-exception', 'deprecated': False}, - 'fmt-exception': {'id': 'fmt-exception', 'deprecated': False}, - 'font-exception-2.0': {'id': 'Font-exception-2.0', 'deprecated': False}, - 'freertos-exception-2.0': {'id': 'freertos-exception-2.0', 'deprecated': False}, - 'gcc-exception-2.0': {'id': 'GCC-exception-2.0', 'deprecated': False}, - 'gcc-exception-2.0-note': {'id': 'GCC-exception-2.0-note', 'deprecated': False}, - 'gcc-exception-3.1': {'id': 'GCC-exception-3.1', 'deprecated': False}, - 'gmsh-exception': {'id': 'Gmsh-exception', 'deprecated': False}, - 'gnat-exception': {'id': 'GNAT-exception', 'deprecated': False}, - 'gnome-examples-exception': {'id': 'GNOME-examples-exception', 'deprecated': False}, - 'gnu-compiler-exception': {'id': 'GNU-compiler-exception', 'deprecated': False}, - 'gnu-javamail-exception': {'id': 'gnu-javamail-exception', 'deprecated': False}, - 'gpl-3.0-389-ds-base-exception': {'id': 'GPL-3.0-389-ds-base-exception', 'deprecated': False}, - 'gpl-3.0-interface-exception': {'id': 'GPL-3.0-interface-exception', 'deprecated': False}, - 'gpl-3.0-linking-exception': {'id': 'GPL-3.0-linking-exception', 'deprecated': False}, - 'gpl-3.0-linking-source-exception': {'id': 'GPL-3.0-linking-source-exception', 'deprecated': False}, - 'gpl-cc-1.0': {'id': 'GPL-CC-1.0', 'deprecated': False}, - 'gstreamer-exception-2005': {'id': 'GStreamer-exception-2005', 'deprecated': False}, - 'gstreamer-exception-2008': {'id': 'GStreamer-exception-2008', 'deprecated': False}, - 'harbour-exception': {'id': 'harbour-exception', 'deprecated': False}, - 'i2p-gpl-java-exception': {'id': 'i2p-gpl-java-exception', 'deprecated': False}, - 'independent-modules-exception': {'id': 'Independent-modules-exception', 'deprecated': False}, - 'kicad-libraries-exception': {'id': 'KiCad-libraries-exception', 'deprecated': False}, - 'lgpl-3.0-linking-exception': {'id': 'LGPL-3.0-linking-exception', 'deprecated': False}, - 'libpri-openh323-exception': {'id': 'libpri-OpenH323-exception', 'deprecated': False}, - 'libtool-exception': {'id': 'Libtool-exception', 'deprecated': False}, - 'linux-syscall-note': {'id': 'Linux-syscall-note', 'deprecated': False}, - 'llgpl': {'id': 'LLGPL', 'deprecated': False}, - 'llvm-exception': {'id': 'LLVM-exception', 'deprecated': False}, - 'lzma-exception': {'id': 'LZMA-exception', 'deprecated': False}, - 'mif-exception': {'id': 'mif-exception', 'deprecated': False}, - 'mxml-exception': {'id': 'mxml-exception', 'deprecated': False}, - 'nokia-qt-exception-1.1': {'id': 'Nokia-Qt-exception-1.1', 'deprecated': True}, - 'ocaml-lgpl-linking-exception': {'id': 'OCaml-LGPL-linking-exception', 'deprecated': False}, - 'occt-exception-1.0': {'id': 'OCCT-exception-1.0', 'deprecated': False}, - 'openjdk-assembly-exception-1.0': {'id': 'OpenJDK-assembly-exception-1.0', 'deprecated': False}, - 'openvpn-openssl-exception': {'id': 'openvpn-openssl-exception', 'deprecated': False}, - 'pcre2-exception': {'id': 'PCRE2-exception', 'deprecated': False}, - 'polyparse-exception': {'id': 'polyparse-exception', 'deprecated': False}, - 'ps-or-pdf-font-exception-20170817': {'id': 'PS-or-PDF-font-exception-20170817', 'deprecated': False}, - 'qpl-1.0-inria-2004-exception': {'id': 'QPL-1.0-INRIA-2004-exception', 'deprecated': False}, - 'qt-gpl-exception-1.0': {'id': 'Qt-GPL-exception-1.0', 'deprecated': False}, - 'qt-lgpl-exception-1.1': {'id': 'Qt-LGPL-exception-1.1', 'deprecated': False}, - 'qwt-exception-1.0': {'id': 'Qwt-exception-1.0', 'deprecated': False}, - 'romic-exception': {'id': 'romic-exception', 'deprecated': False}, - 'rrdtool-floss-exception-2.0': {'id': 'RRDtool-FLOSS-exception-2.0', 'deprecated': False}, - 'sane-exception': {'id': 'SANE-exception', 'deprecated': False}, - 'shl-2.0': {'id': 'SHL-2.0', 'deprecated': False}, - 'shl-2.1': {'id': 'SHL-2.1', 'deprecated': False}, - 'stunnel-exception': {'id': 'stunnel-exception', 'deprecated': False}, - 'swi-exception': {'id': 'SWI-exception', 'deprecated': False}, - 'swift-exception': {'id': 'Swift-exception', 'deprecated': False}, - 'texinfo-exception': {'id': 'Texinfo-exception', 'deprecated': False}, - 'u-boot-exception-2.0': {'id': 'u-boot-exception-2.0', 'deprecated': False}, - 'ubdl-exception': {'id': 'UBDL-exception', 'deprecated': False}, - 'universal-foss-exception-1.0': {'id': 'Universal-FOSS-exception-1.0', 'deprecated': False}, - 'vsftpd-openssl-exception': {'id': 'vsftpd-openssl-exception', 'deprecated': False}, - 'wxwindows-exception-3.1': {'id': 'WxWindows-exception-3.1', 'deprecated': False}, - 'x11vnc-openssl-exception': {'id': 'x11vnc-openssl-exception', 'deprecated': False}, -} diff --git a/apps/bitwarden_event_logs/lib/packaging/markers.py b/apps/bitwarden_event_logs/lib/packaging/markers.py deleted file mode 100755 index ca3706fe..00000000 --- a/apps/bitwarden_event_logs/lib/packaging/markers.py +++ /dev/null @@ -1,388 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import operator -import os -import platform -import sys -from typing import AbstractSet, Callable, Literal, Mapping, TypedDict, Union, cast - -from ._parser import MarkerAtom, MarkerList, Op, Value, Variable -from ._parser import parse_marker as _parse_marker -from ._tokenizer import ParserSyntaxError -from .specifiers import InvalidSpecifier, Specifier -from .utils import canonicalize_name - -__all__ = [ - "Environment", - "EvaluateContext", - "InvalidMarker", - "Marker", - "UndefinedComparison", - "UndefinedEnvironmentName", - "default_environment", -] - -Operator = Callable[[str, Union[str, AbstractSet[str]]], bool] -EvaluateContext = Literal["metadata", "lock_file", "requirement"] -MARKERS_ALLOWING_SET = {"extras", "dependency_groups"} -MARKERS_REQUIRING_VERSION = { - "implementation_version", - "platform_release", - "python_full_version", - "python_version", -} - - -class InvalidMarker(ValueError): - """ - An invalid marker was found, users should refer to PEP 508. - """ - - -class UndefinedComparison(ValueError): - """ - An invalid operation was attempted on a value that doesn't support it. - """ - - -class UndefinedEnvironmentName(ValueError): - """ - A name was attempted to be used that does not exist inside of the - environment. - """ - - -class Environment(TypedDict): - implementation_name: str - """The implementation's identifier, e.g. ``'cpython'``.""" - - implementation_version: str - """ - The implementation's version, e.g. ``'3.13.0a2'`` for CPython 3.13.0a2, or - ``'7.3.13'`` for PyPy3.10 v7.3.13. - """ - - os_name: str - """ - The value of :py:data:`os.name`. The name of the operating system dependent module - imported, e.g. ``'posix'``. - """ - - platform_machine: str - """ - Returns the machine type, e.g. ``'i386'``. - - An empty string if the value cannot be determined. - """ - - platform_release: str - """ - The system's release, e.g. ``'2.2.0'`` or ``'NT'``. - - An empty string if the value cannot be determined. - """ - - platform_system: str - """ - The system/OS name, e.g. ``'Linux'``, ``'Windows'`` or ``'Java'``. - - An empty string if the value cannot be determined. - """ - - platform_version: str - """ - The system's release version, e.g. ``'#3 on degas'``. - - An empty string if the value cannot be determined. - """ - - python_full_version: str - """ - The Python version as string ``'major.minor.patchlevel'``. - - Note that unlike the Python :py:data:`sys.version`, this value will always include - the patchlevel (it defaults to 0). - """ - - platform_python_implementation: str - """ - A string identifying the Python implementation, e.g. ``'CPython'``. - """ - - python_version: str - """The Python version as string ``'major.minor'``.""" - - sys_platform: str - """ - This string contains a platform identifier that can be used to append - platform-specific components to :py:data:`sys.path`, for instance. - - For Unix systems, except on Linux and AIX, this is the lowercased OS name as - returned by ``uname -s`` with the first part of the version as returned by - ``uname -r`` appended, e.g. ``'sunos5'`` or ``'freebsd8'``, at the time when Python - was built. - """ - - -def _normalize_extras( - result: MarkerList | MarkerAtom | str, -) -> MarkerList | MarkerAtom | str: - if not isinstance(result, tuple): - return result - - lhs, op, rhs = result - if isinstance(lhs, Variable) and lhs.value == "extra": - normalized_extra = canonicalize_name(rhs.value) - rhs = Value(normalized_extra) - elif isinstance(rhs, Variable) and rhs.value == "extra": - normalized_extra = canonicalize_name(lhs.value) - lhs = Value(normalized_extra) - return lhs, op, rhs - - -def _normalize_extra_values(results: MarkerList) -> MarkerList: - """ - Normalize extra values. - """ - - return [_normalize_extras(r) for r in results] - - -def _format_marker( - marker: list[str] | MarkerAtom | str, first: bool | None = True -) -> str: - assert isinstance(marker, (list, tuple, str)) - - # Sometimes we have a structure like [[...]] which is a single item list - # where the single item is itself it's own list. In that case we want skip - # the rest of this function so that we don't get extraneous () on the - # outside. - if ( - isinstance(marker, list) - and len(marker) == 1 - and isinstance(marker[0], (list, tuple)) - ): - return _format_marker(marker[0]) - - if isinstance(marker, list): - inner = (_format_marker(m, first=False) for m in marker) - if first: - return " ".join(inner) - else: - return "(" + " ".join(inner) + ")" - elif isinstance(marker, tuple): - return " ".join([m.serialize() for m in marker]) - else: - return marker - - -_operators: dict[str, Operator] = { - "in": lambda lhs, rhs: lhs in rhs, - "not in": lambda lhs, rhs: lhs not in rhs, - "<": lambda _lhs, _rhs: False, - "<=": operator.eq, - "==": operator.eq, - "!=": operator.ne, - ">=": operator.eq, - ">": lambda _lhs, _rhs: False, -} - - -def _eval_op(lhs: str, op: Op, rhs: str | AbstractSet[str], *, key: str) -> bool: - op_str = op.serialize() - if key in MARKERS_REQUIRING_VERSION: - try: - spec = Specifier(f"{op_str}{rhs}") - except InvalidSpecifier: - pass - else: - return spec.contains(lhs, prereleases=True) - - oper: Operator | None = _operators.get(op_str) - if oper is None: - raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.") - - return oper(lhs, rhs) - - -def _normalize( - lhs: str, rhs: str | AbstractSet[str], key: str -) -> tuple[str, str | AbstractSet[str]]: - # PEP 685 - Comparison of extra names for optional distribution dependencies - # https://peps.python.org/pep-0685/ - # > When comparing extra names, tools MUST normalize the names being - # > compared using the semantics outlined in PEP 503 for names - if key == "extra": - assert isinstance(rhs, str), "extra value must be a string" - # Both sides are normalized at this point already - return (lhs, rhs) - if key in MARKERS_ALLOWING_SET: - if isinstance(rhs, str): # pragma: no cover - return (canonicalize_name(lhs), canonicalize_name(rhs)) - else: - return (canonicalize_name(lhs), {canonicalize_name(v) for v in rhs}) - - # other environment markers don't have such standards - return lhs, rhs - - -def _evaluate_markers( - markers: MarkerList, environment: dict[str, str | AbstractSet[str]] -) -> bool: - groups: list[list[bool]] = [[]] - - for marker in markers: - if isinstance(marker, list): - groups[-1].append(_evaluate_markers(marker, environment)) - elif isinstance(marker, tuple): - lhs, op, rhs = marker - - if isinstance(lhs, Variable): - environment_key = lhs.value - lhs_value = environment[environment_key] - rhs_value = rhs.value - else: - lhs_value = lhs.value - environment_key = rhs.value - rhs_value = environment[environment_key] - - assert isinstance(lhs_value, str), "lhs must be a string" - lhs_value, rhs_value = _normalize(lhs_value, rhs_value, key=environment_key) - groups[-1].append(_eval_op(lhs_value, op, rhs_value, key=environment_key)) - elif marker == "or": - groups.append([]) - elif marker == "and": - pass - else: # pragma: nocover - raise TypeError(f"Unexpected marker {marker!r}") - - return any(all(item) for item in groups) - - -def format_full_version(info: sys._version_info) -> str: - version = f"{info.major}.{info.minor}.{info.micro}" - kind = info.releaselevel - if kind != "final": - version += kind[0] + str(info.serial) - return version - - -def default_environment() -> Environment: - iver = format_full_version(sys.implementation.version) - implementation_name = sys.implementation.name - return { - "implementation_name": implementation_name, - "implementation_version": iver, - "os_name": os.name, - "platform_machine": platform.machine(), - "platform_release": platform.release(), - "platform_system": platform.system(), - "platform_version": platform.version(), - "python_full_version": platform.python_version(), - "platform_python_implementation": platform.python_implementation(), - "python_version": ".".join(platform.python_version_tuple()[:2]), - "sys_platform": sys.platform, - } - - -class Marker: - def __init__(self, marker: str) -> None: - # Note: We create a Marker object without calling this constructor in - # packaging.requirements.Requirement. If any additional logic is - # added here, make sure to mirror/adapt Requirement. - - # If this fails and throws an error, the repr still expects _markers to - # be defined. - self._markers: MarkerList = [] - - try: - self._markers = _normalize_extra_values(_parse_marker(marker)) - # The attribute `_markers` can be described in terms of a recursive type: - # MarkerList = List[Union[Tuple[Node, ...], str, MarkerList]] - # - # For example, the following expression: - # python_version > "3.6" or (python_version == "3.6" and os_name == "unix") - # - # is parsed into: - # [ - # (, ')>, ), - # 'and', - # [ - # (, , ), - # 'or', - # (, , ) - # ] - # ] - except ParserSyntaxError as e: - raise InvalidMarker(str(e)) from e - - def __str__(self) -> str: - return _format_marker(self._markers) - - def __repr__(self) -> str: - return f"<{self.__class__.__name__}('{self}')>" - - def __hash__(self) -> int: - return hash(str(self)) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Marker): - return NotImplemented - - return str(self) == str(other) - - def evaluate( - self, - environment: Mapping[str, str | AbstractSet[str]] | None = None, - context: EvaluateContext = "metadata", - ) -> bool: - """Evaluate a marker. - - Return the boolean from evaluating the given marker against the - environment. environment is an optional argument to override all or - part of the determined environment. The *context* parameter specifies what - context the markers are being evaluated for, which influences what markers - are considered valid. Acceptable values are "metadata" (for core metadata; - default), "lock_file", and "requirement" (i.e. all other situations). - - The environment is determined from the current Python process. - """ - current_environment = cast( - "dict[str, str | AbstractSet[str]]", default_environment() - ) - if context == "lock_file": - current_environment.update( - extras=frozenset(), dependency_groups=frozenset() - ) - elif context == "metadata": - current_environment["extra"] = "" - - if environment is not None: - current_environment.update(environment) - if "extra" in current_environment: - # The API used to allow setting extra to None. We need to handle - # this case for backwards compatibility. Also skip running - # normalize name if extra is empty. - extra = cast("str | None", current_environment["extra"]) - current_environment["extra"] = canonicalize_name(extra) if extra else "" - - return _evaluate_markers( - self._markers, _repair_python_full_version(current_environment) - ) - - -def _repair_python_full_version( - env: dict[str, str | AbstractSet[str]], -) -> dict[str, str | AbstractSet[str]]: - """ - Work around platform.python_version() returning something that is not PEP 440 - compliant for non-tagged Python builds. - """ - python_full_version = cast("str", env["python_full_version"]) - if python_full_version.endswith("+"): - env["python_full_version"] = f"{python_full_version}local" - return env diff --git a/apps/bitwarden_event_logs/lib/packaging/metadata.py b/apps/bitwarden_event_logs/lib/packaging/metadata.py deleted file mode 100755 index 253f6b1b..00000000 --- a/apps/bitwarden_event_logs/lib/packaging/metadata.py +++ /dev/null @@ -1,978 +0,0 @@ -from __future__ import annotations - -import email.feedparser -import email.header -import email.message -import email.parser -import email.policy -import keyword -import pathlib -import sys -import typing -from typing import ( - Any, - Callable, - Generic, - Literal, - TypedDict, - cast, -) - -from . import licenses, requirements, specifiers, utils -from . import version as version_module - -if typing.TYPE_CHECKING: - from .licenses import NormalizedLicenseExpression - -T = typing.TypeVar("T") - - -if sys.version_info >= (3, 11): # pragma: no cover - ExceptionGroup = ExceptionGroup # noqa: F821 -else: # pragma: no cover - - class ExceptionGroup(Exception): - """A minimal implementation of :external:exc:`ExceptionGroup` from Python 3.11. - - If :external:exc:`ExceptionGroup` is already defined by Python itself, - that version is used instead. - """ - - message: str - exceptions: list[Exception] - - def __init__(self, message: str, exceptions: list[Exception]) -> None: - self.message = message - self.exceptions = exceptions - - def __repr__(self) -> str: - return f"{self.__class__.__name__}({self.message!r}, {self.exceptions!r})" - - -class InvalidMetadata(ValueError): - """A metadata field contains invalid data.""" - - field: str - """The name of the field that contains invalid data.""" - - def __init__(self, field: str, message: str) -> None: - self.field = field - super().__init__(message) - - -# The RawMetadata class attempts to make as few assumptions about the underlying -# serialization formats as possible. The idea is that as long as a serialization -# formats offer some very basic primitives in *some* way then we can support -# serializing to and from that format. -class RawMetadata(TypedDict, total=False): - """A dictionary of raw core metadata. - - Each field in core metadata maps to a key of this dictionary (when data is - provided). The key is lower-case and underscores are used instead of dashes - compared to the equivalent core metadata field. Any core metadata field that - can be specified multiple times or can hold multiple values in a single - field have a key with a plural name. See :class:`Metadata` whose attributes - match the keys of this dictionary. - - Core metadata fields that can be specified multiple times are stored as a - list or dict depending on which is appropriate for the field. Any fields - which hold multiple values in a single field are stored as a list. - - """ - - # Metadata 1.0 - PEP 241 - metadata_version: str - name: str - version: str - platforms: list[str] - summary: str - description: str - keywords: list[str] - home_page: str - author: str - author_email: str - license: str - - # Metadata 1.1 - PEP 314 - supported_platforms: list[str] - download_url: str - classifiers: list[str] - requires: list[str] - provides: list[str] - obsoletes: list[str] - - # Metadata 1.2 - PEP 345 - maintainer: str - maintainer_email: str - requires_dist: list[str] - provides_dist: list[str] - obsoletes_dist: list[str] - requires_python: str - requires_external: list[str] - project_urls: dict[str, str] - - # Metadata 2.0 - # PEP 426 attempted to completely revamp the metadata format - # but got stuck without ever being able to build consensus on - # it and ultimately ended up withdrawn. - # - # However, a number of tools had started emitting METADATA with - # `2.0` Metadata-Version, so for historical reasons, this version - # was skipped. - - # Metadata 2.1 - PEP 566 - description_content_type: str - provides_extra: list[str] - - # Metadata 2.2 - PEP 643 - dynamic: list[str] - - # Metadata 2.3 - PEP 685 - # No new fields were added in PEP 685, just some edge case were - # tightened up to provide better interoperability. - - # Metadata 2.4 - PEP 639 - license_expression: str - license_files: list[str] - - # Metadata 2.5 - PEP 794 - import_names: list[str] - import_namespaces: list[str] - - -# 'keywords' is special as it's a string in the core metadata spec, but we -# represent it as a list. -_STRING_FIELDS = { - "author", - "author_email", - "description", - "description_content_type", - "download_url", - "home_page", - "license", - "license_expression", - "maintainer", - "maintainer_email", - "metadata_version", - "name", - "requires_python", - "summary", - "version", -} - -_LIST_FIELDS = { - "classifiers", - "dynamic", - "license_files", - "obsoletes", - "obsoletes_dist", - "platforms", - "provides", - "provides_dist", - "provides_extra", - "requires", - "requires_dist", - "requires_external", - "supported_platforms", - "import_names", - "import_namespaces", -} - -_DICT_FIELDS = { - "project_urls", -} - - -def _parse_keywords(data: str) -> list[str]: - """Split a string of comma-separated keywords into a list of keywords.""" - return [k.strip() for k in data.split(",")] - - -def _parse_project_urls(data: list[str]) -> dict[str, str]: - """Parse a list of label/URL string pairings separated by a comma.""" - urls = {} - for pair in data: - # Our logic is slightly tricky here as we want to try and do - # *something* reasonable with malformed data. - # - # The main thing that we have to worry about, is data that does - # not have a ',' at all to split the label from the Value. There - # isn't a singular right answer here, and we will fail validation - # later on (if the caller is validating) so it doesn't *really* - # matter, but since the missing value has to be an empty str - # and our return value is dict[str, str], if we let the key - # be the missing value, then they'd have multiple '' values that - # overwrite each other in a accumulating dict. - # - # The other potential issue is that it's possible to have the - # same label multiple times in the metadata, with no solid "right" - # answer with what to do in that case. As such, we'll do the only - # thing we can, which is treat the field as unparsable and add it - # to our list of unparsed fields. - # - # TODO: The spec doesn't say anything about if the keys should be - # considered case sensitive or not... logically they should - # be case-preserving and case-insensitive, but doing that - # would open up more cases where we might have duplicate - # entries. - label, _, url = (s.strip() for s in pair.partition(",")) - - if label in urls: - # The label already exists in our set of urls, so this field - # is unparsable, and we can just add the whole thing to our - # unparsable data and stop processing it. - raise KeyError("duplicate labels in project urls") - urls[label] = url - - return urls - - -def _get_payload(msg: email.message.Message, source: bytes | str) -> str: - """Get the body of the message.""" - # If our source is a str, then our caller has managed encodings for us, - # and we don't need to deal with it. - if isinstance(source, str): - payload = msg.get_payload() - assert isinstance(payload, str) - return payload - # If our source is a bytes, then we're managing the encoding and we need - # to deal with it. - else: - bpayload = msg.get_payload(decode=True) - assert isinstance(bpayload, bytes) - try: - return bpayload.decode("utf8", "strict") - except UnicodeDecodeError as exc: - raise ValueError("payload in an invalid encoding") from exc - - -# The various parse_FORMAT functions here are intended to be as lenient as -# possible in their parsing, while still returning a correctly typed -# RawMetadata. -# -# To aid in this, we also generally want to do as little touching of the -# data as possible, except where there are possibly some historic holdovers -# that make valid data awkward to work with. -# -# While this is a lower level, intermediate format than our ``Metadata`` -# class, some light touch ups can make a massive difference in usability. - -# Map METADATA fields to RawMetadata. -_EMAIL_TO_RAW_MAPPING = { - "author": "author", - "author-email": "author_email", - "classifier": "classifiers", - "description": "description", - "description-content-type": "description_content_type", - "download-url": "download_url", - "dynamic": "dynamic", - "home-page": "home_page", - "import-name": "import_names", - "import-namespace": "import_namespaces", - "keywords": "keywords", - "license": "license", - "license-expression": "license_expression", - "license-file": "license_files", - "maintainer": "maintainer", - "maintainer-email": "maintainer_email", - "metadata-version": "metadata_version", - "name": "name", - "obsoletes": "obsoletes", - "obsoletes-dist": "obsoletes_dist", - "platform": "platforms", - "project-url": "project_urls", - "provides": "provides", - "provides-dist": "provides_dist", - "provides-extra": "provides_extra", - "requires": "requires", - "requires-dist": "requires_dist", - "requires-external": "requires_external", - "requires-python": "requires_python", - "summary": "summary", - "supported-platform": "supported_platforms", - "version": "version", -} -_RAW_TO_EMAIL_MAPPING = {raw: email for email, raw in _EMAIL_TO_RAW_MAPPING.items()} - - -# This class is for writing RFC822 messages -class RFC822Policy(email.policy.EmailPolicy): - """ - This is :class:`email.policy.EmailPolicy`, but with a simple ``header_store_parse`` - implementation that handles multi-line values, and some nice defaults. - """ - - utf8 = True - mangle_from_ = False - max_line_length = 0 - - def header_store_parse(self, name: str, value: str) -> tuple[str, str]: - size = len(name) + 2 - value = value.replace("\n", "\n" + " " * size) - return (name, value) - - -# This class is for writing RFC822 messages -class RFC822Message(email.message.EmailMessage): - """ - This is :class:`email.message.EmailMessage` with two small changes: it defaults to - our `RFC822Policy`, and it correctly writes unicode when being called - with `bytes()`. - """ - - def __init__(self) -> None: - super().__init__(policy=RFC822Policy()) - - def as_bytes( - self, unixfrom: bool = False, policy: email.policy.Policy | None = None - ) -> bytes: - """ - Return the bytes representation of the message. - - This handles unicode encoding. - """ - return self.as_string(unixfrom, policy=policy).encode("utf-8") - - -def parse_email(data: bytes | str) -> tuple[RawMetadata, dict[str, list[str]]]: - """Parse a distribution's metadata stored as email headers (e.g. from ``METADATA``). - - This function returns a two-item tuple of dicts. The first dict is of - recognized fields from the core metadata specification. Fields that can be - parsed and translated into Python's built-in types are converted - appropriately. All other fields are left as-is. Fields that are allowed to - appear multiple times are stored as lists. - - The second dict contains all other fields from the metadata. This includes - any unrecognized fields. It also includes any fields which are expected to - be parsed into a built-in type but were not formatted appropriately. Finally, - any fields that are expected to appear only once but are repeated are - included in this dict. - - """ - raw: dict[str, str | list[str] | dict[str, str]] = {} - unparsed: dict[str, list[str]] = {} - - if isinstance(data, str): - parsed = email.parser.Parser(policy=email.policy.compat32).parsestr(data) - else: - parsed = email.parser.BytesParser(policy=email.policy.compat32).parsebytes(data) - - # We have to wrap parsed.keys() in a set, because in the case of multiple - # values for a key (a list), the key will appear multiple times in the - # list of keys, but we're avoiding that by using get_all(). - for name_with_case in frozenset(parsed.keys()): - # Header names in RFC are case insensitive, so we'll normalize to all - # lower case to make comparisons easier. - name = name_with_case.lower() - - # We use get_all() here, even for fields that aren't multiple use, - # because otherwise someone could have e.g. two Name fields, and we - # would just silently ignore it rather than doing something about it. - headers = parsed.get_all(name) or [] - - # The way the email module works when parsing bytes is that it - # unconditionally decodes the bytes as ascii using the surrogateescape - # handler. When you pull that data back out (such as with get_all() ), - # it looks to see if the str has any surrogate escapes, and if it does - # it wraps it in a Header object instead of returning the string. - # - # As such, we'll look for those Header objects, and fix up the encoding. - value = [] - # Flag if we have run into any issues processing the headers, thus - # signalling that the data belongs in 'unparsed'. - valid_encoding = True - for h in headers: - # It's unclear if this can return more types than just a Header or - # a str, so we'll just assert here to make sure. - assert isinstance(h, (email.header.Header, str)) - - # If it's a header object, we need to do our little dance to get - # the real data out of it. In cases where there is invalid data - # we're going to end up with mojibake, but there's no obvious, good - # way around that without reimplementing parts of the Header object - # ourselves. - # - # That should be fine since, if mojibacked happens, this key is - # going into the unparsed dict anyways. - if isinstance(h, email.header.Header): - # The Header object stores it's data as chunks, and each chunk - # can be independently encoded, so we'll need to check each - # of them. - chunks: list[tuple[bytes, str | None]] = [] - for binary, _encoding in email.header.decode_header(h): - try: - binary.decode("utf8", "strict") - except UnicodeDecodeError: - # Enable mojibake. - encoding = "latin1" - valid_encoding = False - else: - encoding = "utf8" - chunks.append((binary, encoding)) - - # Turn our chunks back into a Header object, then let that - # Header object do the right thing to turn them into a - # string for us. - value.append(str(email.header.make_header(chunks))) - # This is already a string, so just add it. - else: - value.append(h) - - # We've processed all of our values to get them into a list of str, - # but we may have mojibake data, in which case this is an unparsed - # field. - if not valid_encoding: - unparsed[name] = value - continue - - raw_name = _EMAIL_TO_RAW_MAPPING.get(name) - if raw_name is None: - # This is a bit of a weird situation, we've encountered a key that - # we don't know what it means, so we don't know whether it's meant - # to be a list or not. - # - # Since we can't really tell one way or another, we'll just leave it - # as a list, even though it may be a single item list, because that's - # what makes the most sense for email headers. - unparsed[name] = value - continue - - # If this is one of our string fields, then we'll check to see if our - # value is a list of a single item. If it is then we'll assume that - # it was emitted as a single string, and unwrap the str from inside - # the list. - # - # If it's any other kind of data, then we haven't the faintest clue - # what we should parse it as, and we have to just add it to our list - # of unparsed stuff. - if raw_name in _STRING_FIELDS and len(value) == 1: - raw[raw_name] = value[0] - # If this is import_names, we need to special case the empty field - # case, which converts to an empty list instead of None. We can't let - # the empty case slip through, as it will fail validation. - elif raw_name == "import_names" and value == [""]: - raw[raw_name] = [] - # If this is one of our list of string fields, then we can just assign - # the value, since email *only* has strings, and our get_all() call - # above ensures that this is a list. - elif raw_name in _LIST_FIELDS: - raw[raw_name] = value - # Special Case: Keywords - # The keywords field is implemented in the metadata spec as a str, - # but it conceptually is a list of strings, and is serialized using - # ", ".join(keywords), so we'll do some light data massaging to turn - # this into what it logically is. - elif raw_name == "keywords" and len(value) == 1: - raw[raw_name] = _parse_keywords(value[0]) - # Special Case: Project-URL - # The project urls is implemented in the metadata spec as a list of - # specially-formatted strings that represent a key and a value, which - # is fundamentally a mapping, however the email format doesn't support - # mappings in a sane way, so it was crammed into a list of strings - # instead. - # - # We will do a little light data massaging to turn this into a map as - # it logically should be. - elif raw_name == "project_urls": - try: - raw[raw_name] = _parse_project_urls(value) - except KeyError: - unparsed[name] = value - # Nothing that we've done has managed to parse this, so it'll just - # throw it in our unparsable data and move on. - else: - unparsed[name] = value - - # We need to support getting the Description from the message payload in - # addition to getting it from the the headers. This does mean, though, there - # is the possibility of it being set both ways, in which case we put both - # in 'unparsed' since we don't know which is right. - try: - payload = _get_payload(parsed, data) - except ValueError: - unparsed.setdefault("description", []).append( - parsed.get_payload(decode=isinstance(data, bytes)) # type: ignore[call-overload] - ) - else: - if payload: - # Check to see if we've already got a description, if so then both - # it, and this body move to unparsable. - if "description" in raw: - description_header = cast("str", raw.pop("description")) - unparsed.setdefault("description", []).extend( - [description_header, payload] - ) - elif "description" in unparsed: - unparsed["description"].append(payload) - else: - raw["description"] = payload - - # We need to cast our `raw` to a metadata, because a TypedDict only support - # literal key names, but we're computing our key names on purpose, but the - # way this function is implemented, our `TypedDict` can only have valid key - # names. - return cast("RawMetadata", raw), unparsed - - -_NOT_FOUND = object() - - -# Keep the two values in sync. -_VALID_METADATA_VERSIONS = ["1.0", "1.1", "1.2", "2.1", "2.2", "2.3", "2.4", "2.5"] -_MetadataVersion = Literal["1.0", "1.1", "1.2", "2.1", "2.2", "2.3", "2.4", "2.5"] - -_REQUIRED_ATTRS = frozenset(["metadata_version", "name", "version"]) - - -class _Validator(Generic[T]): - """Validate a metadata field. - - All _process_*() methods correspond to a core metadata field. The method is - called with the field's raw value. If the raw value is valid it is returned - in its "enriched" form (e.g. ``version.Version`` for the ``Version`` field). - If the raw value is invalid, :exc:`InvalidMetadata` is raised (with a cause - as appropriate). - """ - - name: str - raw_name: str - added: _MetadataVersion - - def __init__( - self, - *, - added: _MetadataVersion = "1.0", - ) -> None: - self.added = added - - def __set_name__(self, _owner: Metadata, name: str) -> None: - self.name = name - self.raw_name = _RAW_TO_EMAIL_MAPPING[name] - - def __get__(self, instance: Metadata, _owner: type[Metadata]) -> T: - # With Python 3.8, the caching can be replaced with functools.cached_property(). - # No need to check the cache as attribute lookup will resolve into the - # instance's __dict__ before __get__ is called. - cache = instance.__dict__ - value = instance._raw.get(self.name) - - # To make the _process_* methods easier, we'll check if the value is None - # and if this field is NOT a required attribute, and if both of those - # things are true, we'll skip the the converter. This will mean that the - # converters never have to deal with the None union. - if self.name in _REQUIRED_ATTRS or value is not None: - try: - converter: Callable[[Any], T] = getattr(self, f"_process_{self.name}") - except AttributeError: - pass - else: - value = converter(value) - - cache[self.name] = value - try: - del instance._raw[self.name] # type: ignore[misc] - except KeyError: - pass - - return cast("T", value) - - def _invalid_metadata( - self, msg: str, cause: Exception | None = None - ) -> InvalidMetadata: - exc = InvalidMetadata( - self.raw_name, msg.format_map({"field": repr(self.raw_name)}) - ) - exc.__cause__ = cause - return exc - - def _process_metadata_version(self, value: str) -> _MetadataVersion: - # Implicitly makes Metadata-Version required. - if value not in _VALID_METADATA_VERSIONS: - raise self._invalid_metadata(f"{value!r} is not a valid metadata version") - return cast("_MetadataVersion", value) - - def _process_name(self, value: str) -> str: - if not value: - raise self._invalid_metadata("{field} is a required field") - # Validate the name as a side-effect. - try: - utils.canonicalize_name(value, validate=True) - except utils.InvalidName as exc: - raise self._invalid_metadata( - f"{value!r} is invalid for {{field}}", cause=exc - ) from exc - else: - return value - - def _process_version(self, value: str) -> version_module.Version: - if not value: - raise self._invalid_metadata("{field} is a required field") - try: - return version_module.parse(value) - except version_module.InvalidVersion as exc: - raise self._invalid_metadata( - f"{value!r} is invalid for {{field}}", cause=exc - ) from exc - - def _process_summary(self, value: str) -> str: - """Check the field contains no newlines.""" - if "\n" in value: - raise self._invalid_metadata("{field} must be a single line") - return value - - def _process_description_content_type(self, value: str) -> str: - content_types = {"text/plain", "text/x-rst", "text/markdown"} - message = email.message.EmailMessage() - message["content-type"] = value - - content_type, parameters = ( - # Defaults to `text/plain` if parsing failed. - message.get_content_type().lower(), - message["content-type"].params, - ) - # Check if content-type is valid or defaulted to `text/plain` and thus was - # not parseable. - if content_type not in content_types or content_type not in value.lower(): - raise self._invalid_metadata( - f"{{field}} must be one of {list(content_types)}, not {value!r}" - ) - - charset = parameters.get("charset", "UTF-8") - if charset != "UTF-8": - raise self._invalid_metadata( - f"{{field}} can only specify the UTF-8 charset, not {list(charset)}" - ) - - markdown_variants = {"GFM", "CommonMark"} - variant = parameters.get("variant", "GFM") # Use an acceptable default. - if content_type == "text/markdown" and variant not in markdown_variants: - raise self._invalid_metadata( - f"valid Markdown variants for {{field}} are {list(markdown_variants)}, " - f"not {variant!r}", - ) - return value - - def _process_dynamic(self, value: list[str]) -> list[str]: - for dynamic_field in map(str.lower, value): - if dynamic_field in {"name", "version", "metadata-version"}: - raise self._invalid_metadata( - f"{dynamic_field!r} is not allowed as a dynamic field" - ) - elif dynamic_field not in _EMAIL_TO_RAW_MAPPING: - raise self._invalid_metadata( - f"{dynamic_field!r} is not a valid dynamic field" - ) - return list(map(str.lower, value)) - - def _process_provides_extra( - self, - value: list[str], - ) -> list[utils.NormalizedName]: - normalized_names = [] - try: - for name in value: - normalized_names.append(utils.canonicalize_name(name, validate=True)) - except utils.InvalidName as exc: - raise self._invalid_metadata( - f"{name!r} is invalid for {{field}}", cause=exc - ) from exc - else: - return normalized_names - - def _process_requires_python(self, value: str) -> specifiers.SpecifierSet: - try: - return specifiers.SpecifierSet(value) - except specifiers.InvalidSpecifier as exc: - raise self._invalid_metadata( - f"{value!r} is invalid for {{field}}", cause=exc - ) from exc - - def _process_requires_dist( - self, - value: list[str], - ) -> list[requirements.Requirement]: - reqs = [] - try: - for req in value: - reqs.append(requirements.Requirement(req)) - except requirements.InvalidRequirement as exc: - raise self._invalid_metadata( - f"{req!r} is invalid for {{field}}", cause=exc - ) from exc - else: - return reqs - - def _process_license_expression(self, value: str) -> NormalizedLicenseExpression: - try: - return licenses.canonicalize_license_expression(value) - except ValueError as exc: - raise self._invalid_metadata( - f"{value!r} is invalid for {{field}}", cause=exc - ) from exc - - def _process_license_files(self, value: list[str]) -> list[str]: - paths = [] - for path in value: - if ".." in path: - raise self._invalid_metadata( - f"{path!r} is invalid for {{field}}, " - "parent directory indicators are not allowed" - ) - if "*" in path: - raise self._invalid_metadata( - f"{path!r} is invalid for {{field}}, paths must be resolved" - ) - if ( - pathlib.PurePosixPath(path).is_absolute() - or pathlib.PureWindowsPath(path).is_absolute() - ): - raise self._invalid_metadata( - f"{path!r} is invalid for {{field}}, paths must be relative" - ) - if pathlib.PureWindowsPath(path).as_posix() != path: - raise self._invalid_metadata( - f"{path!r} is invalid for {{field}}, paths must use '/' delimiter" - ) - paths.append(path) - return paths - - def _process_import_names(self, value: list[str]) -> list[str]: - for import_name in value: - name, semicolon, private = import_name.partition(";") - name = name.rstrip() - for identifier in name.split("."): - if not identifier.isidentifier(): - raise self._invalid_metadata( - f"{name!r} is invalid for {{field}}; " - f"{identifier!r} is not a valid identifier" - ) - elif keyword.iskeyword(identifier): - raise self._invalid_metadata( - f"{name!r} is invalid for {{field}}; " - f"{identifier!r} is a keyword" - ) - if semicolon and private.lstrip() != "private": - raise self._invalid_metadata( - f"{import_name!r} is invalid for {{field}}; " - "the only valid option is 'private'" - ) - return value - - _process_import_namespaces = _process_import_names - - -class Metadata: - """Representation of distribution metadata. - - Compared to :class:`RawMetadata`, this class provides objects representing - metadata fields instead of only using built-in types. Any invalid metadata - will cause :exc:`InvalidMetadata` to be raised (with a - :py:attr:`~BaseException.__cause__` attribute as appropriate). - """ - - _raw: RawMetadata - - @classmethod - def from_raw(cls, data: RawMetadata, *, validate: bool = True) -> Metadata: - """Create an instance from :class:`RawMetadata`. - - If *validate* is true, all metadata will be validated. All exceptions - related to validation will be gathered and raised as an :class:`ExceptionGroup`. - """ - ins = cls() - ins._raw = data.copy() # Mutations occur due to caching enriched values. - - if validate: - exceptions: list[Exception] = [] - try: - metadata_version = ins.metadata_version - metadata_age = _VALID_METADATA_VERSIONS.index(metadata_version) - except InvalidMetadata as metadata_version_exc: - exceptions.append(metadata_version_exc) - metadata_version = None - - # Make sure to check for the fields that are present, the required - # fields (so their absence can be reported). - fields_to_check = frozenset(ins._raw) | _REQUIRED_ATTRS - # Remove fields that have already been checked. - fields_to_check -= {"metadata_version"} - - for key in fields_to_check: - try: - if metadata_version: - # Can't use getattr() as that triggers descriptor protocol which - # will fail due to no value for the instance argument. - try: - field_metadata_version = cls.__dict__[key].added - except KeyError: - exc = InvalidMetadata(key, f"unrecognized field: {key!r}") - exceptions.append(exc) - continue - field_age = _VALID_METADATA_VERSIONS.index( - field_metadata_version - ) - if field_age > metadata_age: - field = _RAW_TO_EMAIL_MAPPING[key] - exc = InvalidMetadata( - field, - f"{field} introduced in metadata version " - f"{field_metadata_version}, not {metadata_version}", - ) - exceptions.append(exc) - continue - getattr(ins, key) - except InvalidMetadata as exc: - exceptions.append(exc) - - if exceptions: - raise ExceptionGroup("invalid metadata", exceptions) - - return ins - - @classmethod - def from_email(cls, data: bytes | str, *, validate: bool = True) -> Metadata: - """Parse metadata from email headers. - - If *validate* is true, the metadata will be validated. All exceptions - related to validation will be gathered and raised as an :class:`ExceptionGroup`. - """ - raw, unparsed = parse_email(data) - - if validate: - exceptions: list[Exception] = [] - for unparsed_key in unparsed: - if unparsed_key in _EMAIL_TO_RAW_MAPPING: - message = f"{unparsed_key!r} has invalid data" - else: - message = f"unrecognized field: {unparsed_key!r}" - exceptions.append(InvalidMetadata(unparsed_key, message)) - - if exceptions: - raise ExceptionGroup("unparsed", exceptions) - - try: - return cls.from_raw(raw, validate=validate) - except ExceptionGroup as exc_group: - raise ExceptionGroup( - "invalid or unparsed metadata", exc_group.exceptions - ) from None - - metadata_version: _Validator[_MetadataVersion] = _Validator() - """:external:ref:`core-metadata-metadata-version` - (required; validated to be a valid metadata version)""" - # `name` is not normalized/typed to NormalizedName so as to provide access to - # the original/raw name. - name: _Validator[str] = _Validator() - """:external:ref:`core-metadata-name` - (required; validated using :func:`~packaging.utils.canonicalize_name` and its - *validate* parameter)""" - version: _Validator[version_module.Version] = _Validator() - """:external:ref:`core-metadata-version` (required)""" - dynamic: _Validator[list[str] | None] = _Validator( - added="2.2", - ) - """:external:ref:`core-metadata-dynamic` - (validated against core metadata field names and lowercased)""" - platforms: _Validator[list[str] | None] = _Validator() - """:external:ref:`core-metadata-platform`""" - supported_platforms: _Validator[list[str] | None] = _Validator(added="1.1") - """:external:ref:`core-metadata-supported-platform`""" - summary: _Validator[str | None] = _Validator() - """:external:ref:`core-metadata-summary` (validated to contain no newlines)""" - description: _Validator[str | None] = _Validator() # TODO 2.1: can be in body - """:external:ref:`core-metadata-description`""" - description_content_type: _Validator[str | None] = _Validator(added="2.1") - """:external:ref:`core-metadata-description-content-type` (validated)""" - keywords: _Validator[list[str] | None] = _Validator() - """:external:ref:`core-metadata-keywords`""" - home_page: _Validator[str | None] = _Validator() - """:external:ref:`core-metadata-home-page`""" - download_url: _Validator[str | None] = _Validator(added="1.1") - """:external:ref:`core-metadata-download-url`""" - author: _Validator[str | None] = _Validator() - """:external:ref:`core-metadata-author`""" - author_email: _Validator[str | None] = _Validator() - """:external:ref:`core-metadata-author-email`""" - maintainer: _Validator[str | None] = _Validator(added="1.2") - """:external:ref:`core-metadata-maintainer`""" - maintainer_email: _Validator[str | None] = _Validator(added="1.2") - """:external:ref:`core-metadata-maintainer-email`""" - license: _Validator[str | None] = _Validator() - """:external:ref:`core-metadata-license`""" - license_expression: _Validator[NormalizedLicenseExpression | None] = _Validator( - added="2.4" - ) - """:external:ref:`core-metadata-license-expression`""" - license_files: _Validator[list[str] | None] = _Validator(added="2.4") - """:external:ref:`core-metadata-license-file`""" - classifiers: _Validator[list[str] | None] = _Validator(added="1.1") - """:external:ref:`core-metadata-classifier`""" - requires_dist: _Validator[list[requirements.Requirement] | None] = _Validator( - added="1.2" - ) - """:external:ref:`core-metadata-requires-dist`""" - requires_python: _Validator[specifiers.SpecifierSet | None] = _Validator( - added="1.2" - ) - """:external:ref:`core-metadata-requires-python`""" - # Because `Requires-External` allows for non-PEP 440 version specifiers, we - # don't do any processing on the values. - requires_external: _Validator[list[str] | None] = _Validator(added="1.2") - """:external:ref:`core-metadata-requires-external`""" - project_urls: _Validator[dict[str, str] | None] = _Validator(added="1.2") - """:external:ref:`core-metadata-project-url`""" - # PEP 685 lets us raise an error if an extra doesn't pass `Name` validation - # regardless of metadata version. - provides_extra: _Validator[list[utils.NormalizedName] | None] = _Validator( - added="2.1", - ) - """:external:ref:`core-metadata-provides-extra`""" - provides_dist: _Validator[list[str] | None] = _Validator(added="1.2") - """:external:ref:`core-metadata-provides-dist`""" - obsoletes_dist: _Validator[list[str] | None] = _Validator(added="1.2") - """:external:ref:`core-metadata-obsoletes-dist`""" - import_names: _Validator[list[str] | None] = _Validator(added="2.5") - """:external:ref:`core-metadata-import-name`""" - import_namespaces: _Validator[list[str] | None] = _Validator(added="2.5") - """:external:ref:`core-metadata-import-namespace`""" - requires: _Validator[list[str] | None] = _Validator(added="1.1") - """``Requires`` (deprecated)""" - provides: _Validator[list[str] | None] = _Validator(added="1.1") - """``Provides`` (deprecated)""" - obsoletes: _Validator[list[str] | None] = _Validator(added="1.1") - """``Obsoletes`` (deprecated)""" - - def as_rfc822(self) -> RFC822Message: - """ - Return an RFC822 message with the metadata. - """ - message = RFC822Message() - self._write_metadata(message) - return message - - def _write_metadata(self, message: RFC822Message) -> None: - """ - Return an RFC822 message with the metadata. - """ - for name, validator in self.__class__.__dict__.items(): - if isinstance(validator, _Validator) and name != "description": - value = getattr(self, name) - email_name = _RAW_TO_EMAIL_MAPPING[name] - if value is not None: - if email_name == "project-url": - for label, url in value.items(): - message[email_name] = f"{label}, {url}" - elif email_name == "keywords": - message[email_name] = ",".join(value) - elif email_name == "import-name" and value == []: - message[email_name] = "" - elif isinstance(value, list): - for item in value: - message[email_name] = str(item) - else: - message[email_name] = str(value) - - # The description is a special case because it is in the body of the message. - if self.description is not None: - message.set_payload(self.description) diff --git a/apps/bitwarden_event_logs/lib/packaging/py.typed b/apps/bitwarden_event_logs/lib/packaging/py.typed deleted file mode 100755 index e69de29b..00000000 diff --git a/apps/bitwarden_event_logs/lib/packaging/pylock.py b/apps/bitwarden_event_logs/lib/packaging/pylock.py deleted file mode 100755 index a564f152..00000000 --- a/apps/bitwarden_event_logs/lib/packaging/pylock.py +++ /dev/null @@ -1,635 +0,0 @@ -from __future__ import annotations - -import dataclasses -import logging -import re -from collections.abc import Mapping, Sequence -from dataclasses import dataclass -from datetime import datetime -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Protocol, - TypeVar, -) - -from .markers import Marker -from .specifiers import SpecifierSet -from .utils import NormalizedName, is_normalized_name -from .version import Version - -if TYPE_CHECKING: # pragma: no cover - from pathlib import Path - - from typing_extensions import Self - -_logger = logging.getLogger(__name__) - -__all__ = [ - "Package", - "PackageArchive", - "PackageDirectory", - "PackageSdist", - "PackageVcs", - "PackageWheel", - "Pylock", - "PylockUnsupportedVersionError", - "PylockValidationError", - "is_valid_pylock_path", -] - -_T = TypeVar("_T") -_T2 = TypeVar("_T2") - - -class _FromMappingProtocol(Protocol): # pragma: no cover - @classmethod - def _from_dict(cls, d: Mapping[str, Any]) -> Self: ... - - -_FromMappingProtocolT = TypeVar("_FromMappingProtocolT", bound=_FromMappingProtocol) - - -_PYLOCK_FILE_NAME_RE = re.compile(r"^pylock\.([^.]+)\.toml$") - - -def is_valid_pylock_path(path: Path) -> bool: - """Check if the given path is a valid pylock file path.""" - return path.name == "pylock.toml" or bool(_PYLOCK_FILE_NAME_RE.match(path.name)) - - -def _toml_key(key: str) -> str: - return key.replace("_", "-") - - -def _toml_value(key: str, value: Any) -> Any: # noqa: ANN401 - if isinstance(value, (Version, Marker, SpecifierSet)): - return str(value) - if isinstance(value, Sequence) and key == "environments": - return [str(v) for v in value] - return value - - -def _toml_dict_factory(data: list[tuple[str, Any]]) -> dict[str, Any]: - return { - _toml_key(key): _toml_value(key, value) - for key, value in data - if value is not None - } - - -def _get(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T | None: - """Get a value from the dictionary and verify it's the expected type.""" - if (value := d.get(key)) is None: - return None - if not isinstance(value, expected_type): - raise PylockValidationError( - f"Unexpected type {type(value).__name__} " - f"(expected {expected_type.__name__})", - context=key, - ) - return value - - -def _get_required(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T: - """Get a required value from the dictionary and verify it's the expected type.""" - if (value := _get(d, expected_type, key)) is None: - raise _PylockRequiredKeyError(key) - return value - - -def _get_sequence( - d: Mapping[str, Any], expected_item_type: type[_T], key: str -) -> Sequence[_T] | None: - """Get a list value from the dictionary and verify it's the expected items type.""" - if (value := _get(d, Sequence, key)) is None: # type: ignore[type-abstract] - return None - if isinstance(value, (str, bytes)): - # special case: str and bytes are Sequences, but we want to reject it - raise PylockValidationError( - f"Unexpected type {type(value).__name__} (expected Sequence)", - context=key, - ) - for i, item in enumerate(value): - if not isinstance(item, expected_item_type): - raise PylockValidationError( - f"Unexpected type {type(item).__name__} " - f"(expected {expected_item_type.__name__})", - context=f"{key}[{i}]", - ) - return value - - -def _get_as( - d: Mapping[str, Any], - expected_type: type[_T], - target_type: Callable[[_T], _T2], - key: str, -) -> _T2 | None: - """Get a value from the dictionary, verify it's the expected type, - and convert to the target type. - - This assumes the target_type constructor accepts the value. - """ - if (value := _get(d, expected_type, key)) is None: - return None - try: - return target_type(value) - except Exception as e: - raise PylockValidationError(e, context=key) from e - - -def _get_required_as( - d: Mapping[str, Any], - expected_type: type[_T], - target_type: Callable[[_T], _T2], - key: str, -) -> _T2: - """Get a required value from the dict, verify it's the expected type, - and convert to the target type.""" - if (value := _get_as(d, expected_type, target_type, key)) is None: - raise _PylockRequiredKeyError(key) - return value - - -def _get_sequence_as( - d: Mapping[str, Any], - expected_item_type: type[_T], - target_item_type: Callable[[_T], _T2], - key: str, -) -> list[_T2] | None: - """Get list value from dictionary and verify expected items type.""" - if (value := _get_sequence(d, expected_item_type, key)) is None: - return None - result = [] - try: - for item in value: - typed_item = target_item_type(item) - result.append(typed_item) - except Exception as e: - raise PylockValidationError(e, context=f"{key}[{len(result)}]") from e - return result - - -def _get_object( - d: Mapping[str, Any], target_type: type[_FromMappingProtocolT], key: str -) -> _FromMappingProtocolT | None: - """Get a dictionary value from the dictionary and convert it to a dataclass.""" - if (value := _get(d, Mapping, key)) is None: # type: ignore[type-abstract] - return None - try: - return target_type._from_dict(value) - except Exception as e: - raise PylockValidationError(e, context=key) from e - - -def _get_sequence_of_objects( - d: Mapping[str, Any], target_item_type: type[_FromMappingProtocolT], key: str -) -> list[_FromMappingProtocolT] | None: - """Get a list value from the dictionary and convert its items to a dataclass.""" - if (value := _get_sequence(d, Mapping, key)) is None: # type: ignore[type-abstract] - return None - result: list[_FromMappingProtocolT] = [] - try: - for item in value: - typed_item = target_item_type._from_dict(item) - result.append(typed_item) - except Exception as e: - raise PylockValidationError(e, context=f"{key}[{len(result)}]") from e - return result - - -def _get_required_sequence_of_objects( - d: Mapping[str, Any], target_item_type: type[_FromMappingProtocolT], key: str -) -> Sequence[_FromMappingProtocolT]: - """Get a required list value from the dictionary and convert its items to a - dataclass.""" - if (result := _get_sequence_of_objects(d, target_item_type, key)) is None: - raise _PylockRequiredKeyError(key) - return result - - -def _validate_normalized_name(name: str) -> NormalizedName: - """Validate that a string is a NormalizedName.""" - if not is_normalized_name(name): - raise PylockValidationError(f"Name {name!r} is not normalized") - return NormalizedName(name) - - -def _validate_path_url(path: str | None, url: str | None) -> None: - if not path and not url: - raise PylockValidationError("path or url must be provided") - - -def _validate_hashes(hashes: Mapping[str, Any]) -> Mapping[str, Any]: - if not hashes: - raise PylockValidationError("At least one hash must be provided") - if not all(isinstance(hash_val, str) for hash_val in hashes.values()): - raise PylockValidationError("Hash values must be strings") - return hashes - - -class PylockValidationError(Exception): - """Raised when when input data is not spec-compliant.""" - - context: str | None = None - message: str - - def __init__( - self, - cause: str | Exception, - *, - context: str | None = None, - ) -> None: - if isinstance(cause, PylockValidationError): - if cause.context: - self.context = ( - f"{context}.{cause.context}" if context else cause.context - ) - else: - self.context = context - self.message = cause.message - else: - self.context = context - self.message = str(cause) - - def __str__(self) -> str: - if self.context: - return f"{self.message} in {self.context!r}" - return self.message - - -class _PylockRequiredKeyError(PylockValidationError): - def __init__(self, key: str) -> None: - super().__init__("Missing required value", context=key) - - -class PylockUnsupportedVersionError(PylockValidationError): - """Raised when encountering an unsupported `lock_version`.""" - - -@dataclass(frozen=True, init=False) -class PackageVcs: - type: str - url: str | None = None - path: str | None = None - requested_revision: str | None = None - commit_id: str # type: ignore[misc] - subdirectory: str | None = None - - def __init__( - self, - *, - type: str, - url: str | None = None, - path: str | None = None, - requested_revision: str | None = None, - commit_id: str, - subdirectory: str | None = None, - ) -> None: - # In Python 3.10+ make dataclass kw_only=True and remove __init__ - object.__setattr__(self, "type", type) - object.__setattr__(self, "url", url) - object.__setattr__(self, "path", path) - object.__setattr__(self, "requested_revision", requested_revision) - object.__setattr__(self, "commit_id", commit_id) - object.__setattr__(self, "subdirectory", subdirectory) - - @classmethod - def _from_dict(cls, d: Mapping[str, Any]) -> Self: - package_vcs = cls( - type=_get_required(d, str, "type"), - url=_get(d, str, "url"), - path=_get(d, str, "path"), - requested_revision=_get(d, str, "requested-revision"), - commit_id=_get_required(d, str, "commit-id"), - subdirectory=_get(d, str, "subdirectory"), - ) - _validate_path_url(package_vcs.path, package_vcs.url) - return package_vcs - - -@dataclass(frozen=True, init=False) -class PackageDirectory: - path: str - editable: bool | None = None - subdirectory: str | None = None - - def __init__( - self, - *, - path: str, - editable: bool | None = None, - subdirectory: str | None = None, - ) -> None: - # In Python 3.10+ make dataclass kw_only=True and remove __init__ - object.__setattr__(self, "path", path) - object.__setattr__(self, "editable", editable) - object.__setattr__(self, "subdirectory", subdirectory) - - @classmethod - def _from_dict(cls, d: Mapping[str, Any]) -> Self: - return cls( - path=_get_required(d, str, "path"), - editable=_get(d, bool, "editable"), - subdirectory=_get(d, str, "subdirectory"), - ) - - -@dataclass(frozen=True, init=False) -class PackageArchive: - url: str | None = None - path: str | None = None - size: int | None = None - upload_time: datetime | None = None - hashes: Mapping[str, str] # type: ignore[misc] - subdirectory: str | None = None - - def __init__( - self, - *, - url: str | None = None, - path: str | None = None, - size: int | None = None, - upload_time: datetime | None = None, - hashes: Mapping[str, str], - subdirectory: str | None = None, - ) -> None: - # In Python 3.10+ make dataclass kw_only=True and remove __init__ - object.__setattr__(self, "url", url) - object.__setattr__(self, "path", path) - object.__setattr__(self, "size", size) - object.__setattr__(self, "upload_time", upload_time) - object.__setattr__(self, "hashes", hashes) - object.__setattr__(self, "subdirectory", subdirectory) - - @classmethod - def _from_dict(cls, d: Mapping[str, Any]) -> Self: - package_archive = cls( - url=_get(d, str, "url"), - path=_get(d, str, "path"), - size=_get(d, int, "size"), - upload_time=_get(d, datetime, "upload-time"), - hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"), # type: ignore[type-abstract] - subdirectory=_get(d, str, "subdirectory"), - ) - _validate_path_url(package_archive.path, package_archive.url) - return package_archive - - -@dataclass(frozen=True, init=False) -class PackageSdist: - name: str | None = None - upload_time: datetime | None = None - url: str | None = None - path: str | None = None - size: int | None = None - hashes: Mapping[str, str] # type: ignore[misc] - - def __init__( - self, - *, - name: str | None = None, - upload_time: datetime | None = None, - url: str | None = None, - path: str | None = None, - size: int | None = None, - hashes: Mapping[str, str], - ) -> None: - # In Python 3.10+ make dataclass kw_only=True and remove __init__ - object.__setattr__(self, "name", name) - object.__setattr__(self, "upload_time", upload_time) - object.__setattr__(self, "url", url) - object.__setattr__(self, "path", path) - object.__setattr__(self, "size", size) - object.__setattr__(self, "hashes", hashes) - - @classmethod - def _from_dict(cls, d: Mapping[str, Any]) -> Self: - package_sdist = cls( - name=_get(d, str, "name"), - upload_time=_get(d, datetime, "upload-time"), - url=_get(d, str, "url"), - path=_get(d, str, "path"), - size=_get(d, int, "size"), - hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"), # type: ignore[type-abstract] - ) - _validate_path_url(package_sdist.path, package_sdist.url) - return package_sdist - - -@dataclass(frozen=True, init=False) -class PackageWheel: - name: str | None = None - upload_time: datetime | None = None - url: str | None = None - path: str | None = None - size: int | None = None - hashes: Mapping[str, str] # type: ignore[misc] - - def __init__( - self, - *, - name: str | None = None, - upload_time: datetime | None = None, - url: str | None = None, - path: str | None = None, - size: int | None = None, - hashes: Mapping[str, str], - ) -> None: - # In Python 3.10+ make dataclass kw_only=True and remove __init__ - object.__setattr__(self, "name", name) - object.__setattr__(self, "upload_time", upload_time) - object.__setattr__(self, "url", url) - object.__setattr__(self, "path", path) - object.__setattr__(self, "size", size) - object.__setattr__(self, "hashes", hashes) - - @classmethod - def _from_dict(cls, d: Mapping[str, Any]) -> Self: - package_wheel = cls( - name=_get(d, str, "name"), - upload_time=_get(d, datetime, "upload-time"), - url=_get(d, str, "url"), - path=_get(d, str, "path"), - size=_get(d, int, "size"), - hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"), # type: ignore[type-abstract] - ) - _validate_path_url(package_wheel.path, package_wheel.url) - return package_wheel - - -@dataclass(frozen=True, init=False) -class Package: - name: NormalizedName - version: Version | None = None - marker: Marker | None = None - requires_python: SpecifierSet | None = None - dependencies: Sequence[Mapping[str, Any]] | None = None - vcs: PackageVcs | None = None - directory: PackageDirectory | None = None - archive: PackageArchive | None = None - index: str | None = None - sdist: PackageSdist | None = None - wheels: Sequence[PackageWheel] | None = None - attestation_identities: Sequence[Mapping[str, Any]] | None = None - tool: Mapping[str, Any] | None = None - - def __init__( - self, - *, - name: NormalizedName, - version: Version | None = None, - marker: Marker | None = None, - requires_python: SpecifierSet | None = None, - dependencies: Sequence[Mapping[str, Any]] | None = None, - vcs: PackageVcs | None = None, - directory: PackageDirectory | None = None, - archive: PackageArchive | None = None, - index: str | None = None, - sdist: PackageSdist | None = None, - wheels: Sequence[PackageWheel] | None = None, - attestation_identities: Sequence[Mapping[str, Any]] | None = None, - tool: Mapping[str, Any] | None = None, - ) -> None: - # In Python 3.10+ make dataclass kw_only=True and remove __init__ - object.__setattr__(self, "name", name) - object.__setattr__(self, "version", version) - object.__setattr__(self, "marker", marker) - object.__setattr__(self, "requires_python", requires_python) - object.__setattr__(self, "dependencies", dependencies) - object.__setattr__(self, "vcs", vcs) - object.__setattr__(self, "directory", directory) - object.__setattr__(self, "archive", archive) - object.__setattr__(self, "index", index) - object.__setattr__(self, "sdist", sdist) - object.__setattr__(self, "wheels", wheels) - object.__setattr__(self, "attestation_identities", attestation_identities) - object.__setattr__(self, "tool", tool) - - @classmethod - def _from_dict(cls, d: Mapping[str, Any]) -> Self: - package = cls( - name=_get_required_as(d, str, _validate_normalized_name, "name"), - version=_get_as(d, str, Version, "version"), - requires_python=_get_as(d, str, SpecifierSet, "requires-python"), - dependencies=_get_sequence(d, Mapping, "dependencies"), # type: ignore[type-abstract] - marker=_get_as(d, str, Marker, "marker"), - vcs=_get_object(d, PackageVcs, "vcs"), - directory=_get_object(d, PackageDirectory, "directory"), - archive=_get_object(d, PackageArchive, "archive"), - index=_get(d, str, "index"), - sdist=_get_object(d, PackageSdist, "sdist"), - wheels=_get_sequence_of_objects(d, PackageWheel, "wheels"), - attestation_identities=_get_sequence(d, Mapping, "attestation-identities"), # type: ignore[type-abstract] - tool=_get(d, Mapping, "tool"), # type: ignore[type-abstract] - ) - distributions = bool(package.sdist) + len(package.wheels or []) - direct_urls = ( - bool(package.vcs) + bool(package.directory) + bool(package.archive) - ) - if distributions > 0 and direct_urls > 0: - raise PylockValidationError( - "None of vcs, directory, archive must be set if sdist or wheels are set" - ) - if distributions == 0 and direct_urls != 1: - raise PylockValidationError( - "Exactly one of vcs, directory, archive must be set " - "if sdist and wheels are not set" - ) - try: - for i, attestation_identity in enumerate( # noqa: B007 - package.attestation_identities or [] - ): - _get_required(attestation_identity, str, "kind") - except Exception as e: - raise PylockValidationError( - e, context=f"attestation-identities[{i}]" - ) from e - return package - - @property - def is_direct(self) -> bool: - return not (self.sdist or self.wheels) - - -@dataclass(frozen=True, init=False) -class Pylock: - """A class representing a pylock file.""" - - lock_version: Version - environments: Sequence[Marker] | None = None - requires_python: SpecifierSet | None = None - extras: Sequence[NormalizedName] | None = None - dependency_groups: Sequence[str] | None = None - default_groups: Sequence[str] | None = None - created_by: str # type: ignore[misc] - packages: Sequence[Package] # type: ignore[misc] - tool: Mapping[str, Any] | None = None - - def __init__( - self, - *, - lock_version: Version, - environments: Sequence[Marker] | None = None, - requires_python: SpecifierSet | None = None, - extras: Sequence[NormalizedName] | None = None, - dependency_groups: Sequence[str] | None = None, - default_groups: Sequence[str] | None = None, - created_by: str, - packages: Sequence[Package], - tool: Mapping[str, Any] | None = None, - ) -> None: - # In Python 3.10+ make dataclass kw_only=True and remove __init__ - object.__setattr__(self, "lock_version", lock_version) - object.__setattr__(self, "environments", environments) - object.__setattr__(self, "requires_python", requires_python) - object.__setattr__(self, "extras", extras) - object.__setattr__(self, "dependency_groups", dependency_groups) - object.__setattr__(self, "default_groups", default_groups) - object.__setattr__(self, "created_by", created_by) - object.__setattr__(self, "packages", packages) - object.__setattr__(self, "tool", tool) - - @classmethod - def _from_dict(cls, d: Mapping[str, Any]) -> Self: - pylock = cls( - lock_version=_get_required_as(d, str, Version, "lock-version"), - environments=_get_sequence_as(d, str, Marker, "environments"), - extras=_get_sequence_as(d, str, _validate_normalized_name, "extras"), - dependency_groups=_get_sequence(d, str, "dependency-groups"), - default_groups=_get_sequence(d, str, "default-groups"), - created_by=_get_required(d, str, "created-by"), - requires_python=_get_as(d, str, SpecifierSet, "requires-python"), - packages=_get_required_sequence_of_objects(d, Package, "packages"), - tool=_get(d, Mapping, "tool"), # type: ignore[type-abstract] - ) - if not Version("1") <= pylock.lock_version < Version("2"): - raise PylockUnsupportedVersionError( - f"pylock version {pylock.lock_version} is not supported" - ) - if pylock.lock_version > Version("1.0"): - _logger.warning( - "pylock minor version %s is not supported", pylock.lock_version - ) - return pylock - - @classmethod - def from_dict(cls, d: Mapping[str, Any], /) -> Self: - """Create and validate a Pylock instance from a TOML dictionary. - - Raises :class:`PylockValidationError` if the input data is not - spec-compliant. - """ - return cls._from_dict(d) - - def to_dict(self) -> Mapping[str, Any]: - """Convert the Pylock instance to a TOML dictionary.""" - return dataclasses.asdict(self, dict_factory=_toml_dict_factory) - - def validate(self) -> None: - """Validate the Pylock instance against the specification. - - Raises :class:`PylockValidationError` otherwise.""" - self.from_dict(self.to_dict()) diff --git a/apps/bitwarden_event_logs/lib/packaging/requirements.py b/apps/bitwarden_event_logs/lib/packaging/requirements.py deleted file mode 100755 index 3079be69..00000000 --- a/apps/bitwarden_event_logs/lib/packaging/requirements.py +++ /dev/null @@ -1,86 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import annotations - -from typing import Iterator - -from ._parser import parse_requirement as _parse_requirement -from ._tokenizer import ParserSyntaxError -from .markers import Marker, _normalize_extra_values -from .specifiers import SpecifierSet -from .utils import canonicalize_name - - -class InvalidRequirement(ValueError): - """ - An invalid requirement was found, users should refer to PEP 508. - """ - - -class Requirement: - """Parse a requirement. - - Parse a given requirement string into its parts, such as name, specifier, - URL, and extras. Raises InvalidRequirement on a badly-formed requirement - string. - """ - - # TODO: Can we test whether something is contained within a requirement? - # If so how do we do that? Do we need to test against the _name_ of - # the thing as well as the version? What about the markers? - # TODO: Can we normalize the name and extra name? - - def __init__(self, requirement_string: str) -> None: - try: - parsed = _parse_requirement(requirement_string) - except ParserSyntaxError as e: - raise InvalidRequirement(str(e)) from e - - self.name: str = parsed.name - self.url: str | None = parsed.url or None - self.extras: set[str] = set(parsed.extras or []) - self.specifier: SpecifierSet = SpecifierSet(parsed.specifier) - self.marker: Marker | None = None - if parsed.marker is not None: - self.marker = Marker.__new__(Marker) - self.marker._markers = _normalize_extra_values(parsed.marker) - - def _iter_parts(self, name: str) -> Iterator[str]: - yield name - - if self.extras: - formatted_extras = ",".join(sorted(self.extras)) - yield f"[{formatted_extras}]" - - if self.specifier: - yield str(self.specifier) - - if self.url: - yield f" @ {self.url}" - if self.marker: - yield " " - - if self.marker: - yield f"; {self.marker}" - - def __str__(self) -> str: - return "".join(self._iter_parts(self.name)) - - def __repr__(self) -> str: - return f"<{self.__class__.__name__}('{self}')>" - - def __hash__(self) -> int: - return hash(tuple(self._iter_parts(canonicalize_name(self.name)))) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Requirement): - return NotImplemented - - return ( - canonicalize_name(self.name) == canonicalize_name(other.name) - and self.extras == other.extras - and self.specifier == other.specifier - and self.url == other.url - and self.marker == other.marker - ) diff --git a/apps/bitwarden_event_logs/lib/packaging/specifiers.py b/apps/bitwarden_event_logs/lib/packaging/specifiers.py deleted file mode 100755 index 5d26b0d1..00000000 --- a/apps/bitwarden_event_logs/lib/packaging/specifiers.py +++ /dev/null @@ -1,1068 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -""" -.. testsetup:: - - from packaging.specifiers import Specifier, SpecifierSet, InvalidSpecifier - from packaging.version import Version -""" - -from __future__ import annotations - -import abc -import itertools -import re -from typing import Callable, Final, Iterable, Iterator, TypeVar, Union - -from .utils import canonicalize_version -from .version import InvalidVersion, Version - -UnparsedVersion = Union[Version, str] -UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion) -CallableOperator = Callable[[Version, str], bool] - - -def _coerce_version(version: UnparsedVersion) -> Version | None: - if not isinstance(version, Version): - try: - version = Version(version) - except InvalidVersion: - return None - return version - - -def _public_version(version: Version) -> Version: - return version.__replace__(local=None) - - -def _base_version(version: Version) -> Version: - return version.__replace__(pre=None, post=None, dev=None, local=None) - - -class InvalidSpecifier(ValueError): - """ - Raised when attempting to create a :class:`Specifier` with a specifier - string that is invalid. - - >>> Specifier("lolwat") - Traceback (most recent call last): - ... - packaging.specifiers.InvalidSpecifier: Invalid specifier: 'lolwat' - """ - - -class BaseSpecifier(metaclass=abc.ABCMeta): - __slots__ = () - __match_args__ = ("_str",) - - @property - def _str(self) -> str: - """Internal property for match_args""" - return str(self) - - @abc.abstractmethod - def __str__(self) -> str: - """ - Returns the str representation of this Specifier-like object. This - should be representative of the Specifier itself. - """ - - @abc.abstractmethod - def __hash__(self) -> int: - """ - Returns a hash value for this Specifier-like object. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Returns a boolean representing whether or not the two Specifier-like - objects are equal. - - :param other: The other object to check against. - """ - - @property - @abc.abstractmethod - def prereleases(self) -> bool | None: - """Whether or not pre-releases as a whole are allowed. - - This can be set to either ``True`` or ``False`` to explicitly enable or disable - prereleases or it can be set to ``None`` (the default) to use default semantics. - """ - - @prereleases.setter # noqa: B027 - def prereleases(self, value: bool) -> None: - """Setter for :attr:`prereleases`. - - :param value: The value to set. - """ - - @abc.abstractmethod - def contains(self, item: str, prereleases: bool | None = None) -> bool: - """ - Determines if the given item is contained within this specifier. - """ - - @abc.abstractmethod - def filter( - self, iterable: Iterable[UnparsedVersionVar], prereleases: bool | None = None - ) -> Iterator[UnparsedVersionVar]: - """ - Takes an iterable of items and filters them so that only items which - are contained within this specifier are allowed in it. - """ - - -class Specifier(BaseSpecifier): - """This class abstracts handling of version specifiers. - - .. tip:: - - It is generally not required to instantiate this manually. You should instead - prefer to work with :class:`SpecifierSet` instead, which can parse - comma-separated version specifiers (which is what package metadata contains). - """ - - __slots__ = ("_prereleases", "_spec", "_spec_version") - - _operator_regex_str = r""" - (?P(~=|==|!=|<=|>=|<|>|===)) - """ - _version_regex_str = r""" - (?P - (?: - # The identity operators allow for an escape hatch that will - # do an exact string match of the version you wish to install. - # This will not be parsed by PEP 440 and we cannot determine - # any semantic meaning from it. This operator is discouraged - # but included entirely as an escape hatch. - (?<====) # Only match for the identity operator - \s* - [^\s;)]* # The arbitrary version can be just about anything, - # we match everything except for whitespace, a - # semi-colon for marker support, and a closing paren - # since versions can be enclosed in them. - ) - | - (?: - # The (non)equality operators allow for wild card and local - # versions to be specified so we have to define these two - # operators separately to enable that. - (?<===|!=) # Only match for equals and not equals - - \s* - v? - (?:[0-9]+!)? # epoch - [0-9]+(?:\.[0-9]+)* # release - - # You cannot use a wild card and a pre-release, post-release, a dev or - # local version together so group them with a | and make them optional. - (?: - \.\* # Wild card syntax of .* - | - (?: # pre release - [-_\.]? - (alpha|beta|preview|pre|a|b|c|rc) - [-_\.]? - [0-9]* - )? - (?: # post release - (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) - )? - (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release - (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local - )? - ) - | - (?: - # The compatible operator requires at least two digits in the - # release segment. - (?<=~=) # Only match for the compatible operator - - \s* - v? - (?:[0-9]+!)? # epoch - [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) - (?: # pre release - [-_\.]? - (alpha|beta|preview|pre|a|b|c|rc) - [-_\.]? - [0-9]* - )? - (?: # post release - (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) - )? - (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release - ) - | - (?: - # All other operators only allow a sub set of what the - # (non)equality operators do. Specifically they do not allow - # local versions to be specified nor do they allow the prefix - # matching wild cards. - (?=": "greater_than_equal", - "<": "less_than", - ">": "greater_than", - "===": "arbitrary", - } - - def __init__(self, spec: str = "", prereleases: bool | None = None) -> None: - """Initialize a Specifier instance. - - :param spec: - The string representation of a specifier which will be parsed and - normalized before use. - :param prereleases: - This tells the specifier if it should accept prerelease versions if - applicable or not. The default of ``None`` will autodetect it from the - given specifiers. - :raises InvalidSpecifier: - If the given specifier is invalid (i.e. bad syntax). - """ - match = self._regex.fullmatch(spec) - if not match: - raise InvalidSpecifier(f"Invalid specifier: {spec!r}") - - self._spec: tuple[str, str] = ( - match.group("operator").strip(), - match.group("version").strip(), - ) - - # Store whether or not this Specifier should accept prereleases - self._prereleases = prereleases - - # Specifier version cache - self._spec_version: tuple[str, Version] | None = None - - def _get_spec_version(self, version: str) -> Version | None: - """One element cache, as only one spec Version is needed per Specifier.""" - if self._spec_version is not None and self._spec_version[0] == version: - return self._spec_version[1] - - version_specifier = _coerce_version(version) - if version_specifier is None: - return None - - self._spec_version = (version, version_specifier) - return version_specifier - - def _require_spec_version(self, version: str) -> Version: - """Get spec version, asserting it's valid (not for === operator). - - This method should only be called for operators where version - strings are guaranteed to be valid PEP 440 versions (not ===). - """ - spec_version = self._get_spec_version(version) - assert spec_version is not None - return spec_version - - @property - def prereleases(self) -> bool | None: - # If there is an explicit prereleases set for this, then we'll just - # blindly use that. - if self._prereleases is not None: - return self._prereleases - - # Only the "!=" operator does not imply prereleases when - # the version in the specifier is a prerelease. - operator, version_str = self._spec - if operator != "!=": - # The == specifier with trailing .* cannot include prereleases - # e.g. "==1.0a1.*" is not valid. - if operator == "==" and version_str.endswith(".*"): - return False - - # "===" can have arbitrary string versions, so we cannot parse - # those, we take prereleases as unknown (None) for those. - version = self._get_spec_version(version_str) - if version is None: - return None - - # For all other operators, use the check if spec Version - # object implies pre-releases. - if version.is_prerelease: - return True - - return False - - @prereleases.setter - def prereleases(self, value: bool | None) -> None: - self._prereleases = value - - @property - def operator(self) -> str: - """The operator of this specifier. - - >>> Specifier("==1.2.3").operator - '==' - """ - return self._spec[0] - - @property - def version(self) -> str: - """The version of this specifier. - - >>> Specifier("==1.2.3").version - '1.2.3' - """ - return self._spec[1] - - def __repr__(self) -> str: - """A representation of the Specifier that shows all internal state. - - >>> Specifier('>=1.0.0') - =1.0.0')> - >>> Specifier('>=1.0.0', prereleases=False) - =1.0.0', prereleases=False)> - >>> Specifier('>=1.0.0', prereleases=True) - =1.0.0', prereleases=True)> - """ - pre = ( - f", prereleases={self.prereleases!r}" - if self._prereleases is not None - else "" - ) - - return f"<{self.__class__.__name__}({str(self)!r}{pre})>" - - def __str__(self) -> str: - """A string representation of the Specifier that can be round-tripped. - - >>> str(Specifier('>=1.0.0')) - '>=1.0.0' - >>> str(Specifier('>=1.0.0', prereleases=False)) - '>=1.0.0' - """ - return "{}{}".format(*self._spec) - - @property - def _canonical_spec(self) -> tuple[str, str]: - operator, version = self._spec - if operator == "===" or version.endswith(".*"): - return operator, version - - spec_version = self._require_spec_version(version) - - canonical_version = canonicalize_version( - spec_version, strip_trailing_zero=(operator != "~=") - ) - - return operator, canonical_version - - def __hash__(self) -> int: - return hash(self._canonical_spec) - - def __eq__(self, other: object) -> bool: - """Whether or not the two Specifier-like objects are equal. - - :param other: The other object to check against. - - The value of :attr:`prereleases` is ignored. - - >>> Specifier("==1.2.3") == Specifier("== 1.2.3.0") - True - >>> (Specifier("==1.2.3", prereleases=False) == - ... Specifier("==1.2.3", prereleases=True)) - True - >>> Specifier("==1.2.3") == "==1.2.3" - True - >>> Specifier("==1.2.3") == Specifier("==1.2.4") - False - >>> Specifier("==1.2.3") == Specifier("~=1.2.3") - False - """ - if isinstance(other, str): - try: - other = self.__class__(str(other)) - except InvalidSpecifier: - return NotImplemented - elif not isinstance(other, self.__class__): - return NotImplemented - - return self._canonical_spec == other._canonical_spec - - def _get_operator(self, op: str) -> CallableOperator: - operator_callable: CallableOperator = getattr( - self, f"_compare_{self._operators[op]}" - ) - return operator_callable - - def _compare_compatible(self, prospective: Version, spec: str) -> bool: - # Compatible releases have an equivalent combination of >= and ==. That - # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to - # implement this in terms of the other specifiers instead of - # implementing it ourselves. The only thing we need to do is construct - # the other specifiers. - - # We want everything but the last item in the version, but we want to - # ignore suffix segments. - prefix = _version_join( - list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1] - ) - - # Add the prefix notation to the end of our string - prefix += ".*" - - return self._get_operator(">=")(prospective, spec) and self._get_operator("==")( - prospective, prefix - ) - - def _compare_equal(self, prospective: Version, spec: str) -> bool: - # We need special logic to handle prefix matching - if spec.endswith(".*"): - # In the case of prefix matching we want to ignore local segment. - normalized_prospective = canonicalize_version( - _public_version(prospective), strip_trailing_zero=False - ) - # Get the normalized version string ignoring the trailing .* - normalized_spec = canonicalize_version(spec[:-2], strip_trailing_zero=False) - # Split the spec out by bangs and dots, and pretend that there is - # an implicit dot in between a release segment and a pre-release segment. - split_spec = _version_split(normalized_spec) - - # Split the prospective version out by bangs and dots, and pretend - # that there is an implicit dot in between a release segment and - # a pre-release segment. - split_prospective = _version_split(normalized_prospective) - - # 0-pad the prospective version before shortening it to get the correct - # shortened version. - padded_prospective, _ = _pad_version(split_prospective, split_spec) - - # Shorten the prospective version to be the same length as the spec - # so that we can determine if the specifier is a prefix of the - # prospective version or not. - shortened_prospective = padded_prospective[: len(split_spec)] - - return shortened_prospective == split_spec - else: - # Convert our spec string into a Version - spec_version = self._require_spec_version(spec) - - # If the specifier does not have a local segment, then we want to - # act as if the prospective version also does not have a local - # segment. - if not spec_version.local: - prospective = _public_version(prospective) - - return prospective == spec_version - - def _compare_not_equal(self, prospective: Version, spec: str) -> bool: - return not self._compare_equal(prospective, spec) - - def _compare_less_than_equal(self, prospective: Version, spec: str) -> bool: - # NB: Local version identifiers are NOT permitted in the version - # specifier, so local version labels can be universally removed from - # the prospective version. - return _public_version(prospective) <= self._require_spec_version(spec) - - def _compare_greater_than_equal(self, prospective: Version, spec: str) -> bool: - # NB: Local version identifiers are NOT permitted in the version - # specifier, so local version labels can be universally removed from - # the prospective version. - return _public_version(prospective) >= self._require_spec_version(spec) - - def _compare_less_than(self, prospective: Version, spec_str: str) -> bool: - # Convert our spec to a Version instance, since we'll want to work with - # it as a version. - spec = self._require_spec_version(spec_str) - - # Check to see if the prospective version is less than the spec - # version. If it's not we can short circuit and just return False now - # instead of doing extra unneeded work. - if not prospective < spec: - return False - - # This special case is here so that, unless the specifier itself - # includes is a pre-release version, that we do not accept pre-release - # versions for the version mentioned in the specifier (e.g. <3.1 should - # not match 3.1.dev0, but should match 3.0.dev0). - if ( - not spec.is_prerelease - and prospective.is_prerelease - and _base_version(prospective) == _base_version(spec) - ): - return False - - # If we've gotten to here, it means that prospective version is both - # less than the spec version *and* it's not a pre-release of the same - # version in the spec. - return True - - def _compare_greater_than(self, prospective: Version, spec_str: str) -> bool: - # Convert our spec to a Version instance, since we'll want to work with - # it as a version. - spec = self._require_spec_version(spec_str) - - # Check to see if the prospective version is greater than the spec - # version. If it's not we can short circuit and just return False now - # instead of doing extra unneeded work. - if not prospective > spec: - return False - - # This special case is here so that, unless the specifier itself - # includes is a post-release version, that we do not accept - # post-release versions for the version mentioned in the specifier - # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). - if ( - not spec.is_postrelease - and prospective.is_postrelease - and _base_version(prospective) == _base_version(spec) - ): - return False - - # Ensure that we do not allow a local version of the version mentioned - # in the specifier, which is technically greater than, to match. - if prospective.local is not None and _base_version( - prospective - ) == _base_version(spec): - return False - - # If we've gotten to here, it means that prospective version is both - # greater than the spec version *and* it's not a pre-release of the - # same version in the spec. - return True - - def _compare_arbitrary(self, prospective: Version | str, spec: str) -> bool: - return str(prospective).lower() == str(spec).lower() - - def __contains__(self, item: str | Version) -> bool: - """Return whether or not the item is contained in this specifier. - - :param item: The item to check for. - - This is used for the ``in`` operator and behaves the same as - :meth:`contains` with no ``prereleases`` argument passed. - - >>> "1.2.3" in Specifier(">=1.2.3") - True - >>> Version("1.2.3") in Specifier(">=1.2.3") - True - >>> "1.0.0" in Specifier(">=1.2.3") - False - >>> "1.3.0a1" in Specifier(">=1.2.3") - True - >>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True) - True - """ - return self.contains(item) - - def contains(self, item: UnparsedVersion, prereleases: bool | None = None) -> bool: - """Return whether or not the item is contained in this specifier. - - :param item: - The item to check for, which can be a version string or a - :class:`Version` instance. - :param prereleases: - Whether or not to match prereleases with this Specifier. If set to - ``None`` (the default), it will follow the recommendation from - :pep:`440` and match prereleases, as there are no other versions. - - >>> Specifier(">=1.2.3").contains("1.2.3") - True - >>> Specifier(">=1.2.3").contains(Version("1.2.3")) - True - >>> Specifier(">=1.2.3").contains("1.0.0") - False - >>> Specifier(">=1.2.3").contains("1.3.0a1") - True - >>> Specifier(">=1.2.3", prereleases=False).contains("1.3.0a1") - False - >>> Specifier(">=1.2.3").contains("1.3.0a1") - True - """ - - return bool(list(self.filter([item], prereleases=prereleases))) - - def filter( - self, iterable: Iterable[UnparsedVersionVar], prereleases: bool | None = None - ) -> Iterator[UnparsedVersionVar]: - """Filter items in the given iterable, that match the specifier. - - :param iterable: - An iterable that can contain version strings and :class:`Version` instances. - The items in the iterable will be filtered according to the specifier. - :param prereleases: - Whether or not to allow prereleases in the returned iterator. If set to - ``None`` (the default), it will follow the recommendation from :pep:`440` - and match prereleases if there are no other versions. - - >>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) - ['1.3'] - >>> list(Specifier(">=1.2.3").filter(["1.2", "1.2.3", "1.3", Version("1.4")])) - ['1.2.3', '1.3', ] - >>> list(Specifier(">=1.2.3").filter(["1.2", "1.5a1"])) - ['1.5a1'] - >>> list(Specifier(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) - ['1.3', '1.5a1'] - >>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) - ['1.3', '1.5a1'] - """ - prereleases_versions = [] - found_non_prereleases = False - - # Determine if to include prereleases by default - include_prereleases = ( - prereleases if prereleases is not None else self.prereleases - ) - - # Get the matching operator - operator_callable = self._get_operator(self.operator) - - # Filter versions - for version in iterable: - parsed_version = _coerce_version(version) - if parsed_version is None: - # === operator can match arbitrary (non-version) strings - if self.operator == "===" and self._compare_arbitrary( - version, self.version - ): - yield version - elif operator_callable(parsed_version, self.version): - # If it's not a prerelease or prereleases are allowed, yield it directly - if not parsed_version.is_prerelease or include_prereleases: - found_non_prereleases = True - yield version - # Otherwise collect prereleases for potential later use - elif prereleases is None and self._prereleases is not False: - prereleases_versions.append(version) - - # If no non-prereleases were found and prereleases weren't - # explicitly forbidden, yield the collected prereleases - if ( - not found_non_prereleases - and prereleases is None - and self._prereleases is not False - ): - yield from prereleases_versions - - -_prefix_regex = re.compile(r"([0-9]+)((?:a|b|c|rc)[0-9]+)") - - -def _version_split(version: str) -> list[str]: - """Split version into components. - - The split components are intended for version comparison. The logic does - not attempt to retain the original version string, so joining the - components back with :func:`_version_join` may not produce the original - version string. - """ - result: list[str] = [] - - epoch, _, rest = version.rpartition("!") - result.append(epoch or "0") - - for item in rest.split("."): - match = _prefix_regex.fullmatch(item) - if match: - result.extend(match.groups()) - else: - result.append(item) - return result - - -def _version_join(components: list[str]) -> str: - """Join split version components into a version string. - - This function assumes the input came from :func:`_version_split`, where the - first component must be the epoch (either empty or numeric), and all other - components numeric. - """ - epoch, *rest = components - return f"{epoch}!{'.'.join(rest)}" - - -def _is_not_suffix(segment: str) -> bool: - return not any( - segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post") - ) - - -def _pad_version(left: list[str], right: list[str]) -> tuple[list[str], list[str]]: - left_split, right_split = [], [] - - # Get the release segment of our versions - left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) - right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) - - # Get the rest of our versions - left_split.append(left[len(left_split[0]) :]) - right_split.append(right[len(right_split[0]) :]) - - # Insert our padding - left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0]))) - right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0]))) - - return ( - list(itertools.chain.from_iterable(left_split)), - list(itertools.chain.from_iterable(right_split)), - ) - - -class SpecifierSet(BaseSpecifier): - """This class abstracts handling of a set of version specifiers. - - It can be passed a single specifier (``>=3.0``), a comma-separated list of - specifiers (``>=3.0,!=3.1``), or no specifier at all. - """ - - __slots__ = ("_prereleases", "_specs") - - def __init__( - self, - specifiers: str | Iterable[Specifier] = "", - prereleases: bool | None = None, - ) -> None: - """Initialize a SpecifierSet instance. - - :param specifiers: - The string representation of a specifier or a comma-separated list of - specifiers which will be parsed and normalized before use. - May also be an iterable of ``Specifier`` instances, which will be used - as is. - :param prereleases: - This tells the SpecifierSet if it should accept prerelease versions if - applicable or not. The default of ``None`` will autodetect it from the - given specifiers. - - :raises InvalidSpecifier: - If the given ``specifiers`` are not parseable than this exception will be - raised. - """ - - if isinstance(specifiers, str): - # Split on `,` to break each individual specifier into its own item, and - # strip each item to remove leading/trailing whitespace. - split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] - - # Make each individual specifier a Specifier and save in a frozen set - # for later. - self._specs = frozenset(map(Specifier, split_specifiers)) - else: - # Save the supplied specifiers in a frozen set. - self._specs = frozenset(specifiers) - - # Store our prereleases value so we can use it later to determine if - # we accept prereleases or not. - self._prereleases = prereleases - - @property - def prereleases(self) -> bool | None: - # If we have been given an explicit prerelease modifier, then we'll - # pass that through here. - if self._prereleases is not None: - return self._prereleases - - # If we don't have any specifiers, and we don't have a forced value, - # then we'll just return None since we don't know if this should have - # pre-releases or not. - if not self._specs: - return None - - # Otherwise we'll see if any of the given specifiers accept - # prereleases, if any of them do we'll return True, otherwise False. - if any(s.prereleases for s in self._specs): - return True - - return None - - @prereleases.setter - def prereleases(self, value: bool | None) -> None: - self._prereleases = value - - def __repr__(self) -> str: - """A representation of the specifier set that shows all internal state. - - Note that the ordering of the individual specifiers within the set may not - match the input string. - - >>> SpecifierSet('>=1.0.0,!=2.0.0') - =1.0.0')> - >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=False) - =1.0.0', prereleases=False)> - >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=True) - =1.0.0', prereleases=True)> - """ - pre = ( - f", prereleases={self.prereleases!r}" - if self._prereleases is not None - else "" - ) - - return f"" - - def __str__(self) -> str: - """A string representation of the specifier set that can be round-tripped. - - Note that the ordering of the individual specifiers within the set may not - match the input string. - - >>> str(SpecifierSet(">=1.0.0,!=1.0.1")) - '!=1.0.1,>=1.0.0' - >>> str(SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False)) - '!=1.0.1,>=1.0.0' - """ - return ",".join(sorted(str(s) for s in self._specs)) - - def __hash__(self) -> int: - return hash(self._specs) - - def __and__(self, other: SpecifierSet | str) -> SpecifierSet: - """Return a SpecifierSet which is a combination of the two sets. - - :param other: The other object to combine with. - - >>> SpecifierSet(">=1.0.0,!=1.0.1") & '<=2.0.0,!=2.0.1' - =1.0.0')> - >>> SpecifierSet(">=1.0.0,!=1.0.1") & SpecifierSet('<=2.0.0,!=2.0.1') - =1.0.0')> - """ - if isinstance(other, str): - other = SpecifierSet(other) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - specifier = SpecifierSet() - specifier._specs = frozenset(self._specs | other._specs) - - if self._prereleases is None and other._prereleases is not None: - specifier._prereleases = other._prereleases - elif ( - self._prereleases is not None and other._prereleases is None - ) or self._prereleases == other._prereleases: - specifier._prereleases = self._prereleases - else: - raise ValueError( - "Cannot combine SpecifierSets with True and False prerelease overrides." - ) - - return specifier - - def __eq__(self, other: object) -> bool: - """Whether or not the two SpecifierSet-like objects are equal. - - :param other: The other object to check against. - - The value of :attr:`prereleases` is ignored. - - >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.1") - True - >>> (SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False) == - ... SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True)) - True - >>> SpecifierSet(">=1.0.0,!=1.0.1") == ">=1.0.0,!=1.0.1" - True - >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0") - False - >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.2") - False - """ - if isinstance(other, (str, Specifier)): - other = SpecifierSet(str(other)) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - return self._specs == other._specs - - def __len__(self) -> int: - """Returns the number of specifiers in this specifier set.""" - return len(self._specs) - - def __iter__(self) -> Iterator[Specifier]: - """ - Returns an iterator over all the underlying :class:`Specifier` instances - in this specifier set. - - >>> sorted(SpecifierSet(">=1.0.0,!=1.0.1"), key=str) - [, =1.0.0')>] - """ - return iter(self._specs) - - def __contains__(self, item: UnparsedVersion) -> bool: - """Return whether or not the item is contained in this specifier. - - :param item: The item to check for. - - This is used for the ``in`` operator and behaves the same as - :meth:`contains` with no ``prereleases`` argument passed. - - >>> "1.2.3" in SpecifierSet(">=1.0.0,!=1.0.1") - True - >>> Version("1.2.3") in SpecifierSet(">=1.0.0,!=1.0.1") - True - >>> "1.0.1" in SpecifierSet(">=1.0.0,!=1.0.1") - False - >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1") - True - >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True) - True - """ - return self.contains(item) - - def contains( - self, - item: UnparsedVersion, - prereleases: bool | None = None, - installed: bool | None = None, - ) -> bool: - """Return whether or not the item is contained in this SpecifierSet. - - :param item: - The item to check for, which can be a version string or a - :class:`Version` instance. - :param prereleases: - Whether or not to match prereleases with this SpecifierSet. If set to - ``None`` (the default), it will follow the recommendation from :pep:`440` - and match prereleases, as there are no other versions. - :param installed: - Whether or not the item is installed. If set to ``True``, it will - accept prerelease versions even if the specifier does not allow them. - - >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.2.3") - True - >>> SpecifierSet(">=1.0.0,!=1.0.1").contains(Version("1.2.3")) - True - >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.0.1") - False - >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1") - True - >>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False).contains("1.3.0a1") - False - >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True) - True - """ - version = _coerce_version(item) - - if version is not None and installed and version.is_prerelease: - prereleases = True - - check_item = item if version is None else version - return bool(list(self.filter([check_item], prereleases=prereleases))) - - def filter( - self, iterable: Iterable[UnparsedVersionVar], prereleases: bool | None = None - ) -> Iterator[UnparsedVersionVar]: - """Filter items in the given iterable, that match the specifiers in this set. - - :param iterable: - An iterable that can contain version strings and :class:`Version` instances. - The items in the iterable will be filtered according to the specifier. - :param prereleases: - Whether or not to allow prereleases in the returned iterator. If set to - ``None`` (the default), it will follow the recommendation from :pep:`440` - and match prereleases if there are no other versions. - - >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) - ['1.3'] - >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", Version("1.4")])) - ['1.3', ] - >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.5a1"])) - ['1.5a1'] - >>> list(SpecifierSet(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) - ['1.3', '1.5a1'] - >>> list(SpecifierSet(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) - ['1.3', '1.5a1'] - - An "empty" SpecifierSet will filter items based on the presence of prerelease - versions in the set. - - >>> list(SpecifierSet("").filter(["1.3", "1.5a1"])) - ['1.3'] - >>> list(SpecifierSet("").filter(["1.5a1"])) - ['1.5a1'] - >>> list(SpecifierSet("", prereleases=True).filter(["1.3", "1.5a1"])) - ['1.3', '1.5a1'] - >>> list(SpecifierSet("").filter(["1.3", "1.5a1"], prereleases=True)) - ['1.3', '1.5a1'] - """ - # Determine if we're forcing a prerelease or not, if we're not forcing - # one for this particular filter call, then we'll use whatever the - # SpecifierSet thinks for whether or not we should support prereleases. - if prereleases is None and self.prereleases is not None: - prereleases = self.prereleases - - # If we have any specifiers, then we want to wrap our iterable in the - # filter method for each one, this will act as a logical AND amongst - # each specifier. - if self._specs: - # When prereleases is None, we need to let all versions through - # the individual filters, then decide about prereleases at the end - # based on whether any non-prereleases matched ALL specs. - for spec in self._specs: - iterable = spec.filter( - iterable, prereleases=True if prereleases is None else prereleases - ) - - if prereleases is not None: - # If we have a forced prereleases value, - # we can immediately return the iterator. - return iter(iterable) - else: - # Handle empty SpecifierSet cases where prereleases is not None. - if prereleases is True: - return iter(iterable) - - if prereleases is False: - return ( - item - for item in iterable - if (version := _coerce_version(item)) is None - or not version.is_prerelease - ) - - # Finally if prereleases is None, apply PEP 440 logic: - # exclude prereleases unless there are no final releases that matched. - filtered_items: list[UnparsedVersionVar] = [] - found_prereleases: list[UnparsedVersionVar] = [] - found_final_release = False - - for item in iterable: - parsed_version = _coerce_version(item) - # Arbitrary strings are always included as it is not - # possible to determine if they are prereleases, - # and they have already passed all specifiers. - if parsed_version is None: - filtered_items.append(item) - found_prereleases.append(item) - elif parsed_version.is_prerelease: - found_prereleases.append(item) - else: - filtered_items.append(item) - found_final_release = True - - return iter(filtered_items if found_final_release else found_prereleases) diff --git a/apps/bitwarden_event_logs/lib/packaging/tags.py b/apps/bitwarden_event_logs/lib/packaging/tags.py deleted file mode 100755 index 5ef27c89..00000000 --- a/apps/bitwarden_event_logs/lib/packaging/tags.py +++ /dev/null @@ -1,651 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import logging -import platform -import re -import struct -import subprocess -import sys -import sysconfig -from importlib.machinery import EXTENSION_SUFFIXES -from typing import ( - Any, - Iterable, - Iterator, - Sequence, - Tuple, - cast, -) - -from . import _manylinux, _musllinux - -logger = logging.getLogger(__name__) - -PythonVersion = Sequence[int] -AppleVersion = Tuple[int, int] - -INTERPRETER_SHORT_NAMES: dict[str, str] = { - "python": "py", # Generic. - "cpython": "cp", - "pypy": "pp", - "ironpython": "ip", - "jython": "jy", -} - - -_32_BIT_INTERPRETER = struct.calcsize("P") == 4 - - -class Tag: - """ - A representation of the tag triple for a wheel. - - Instances are considered immutable and thus are hashable. Equality checking - is also supported. - """ - - __slots__ = ["_abi", "_hash", "_interpreter", "_platform"] - - def __init__(self, interpreter: str, abi: str, platform: str) -> None: - self._interpreter = interpreter.lower() - self._abi = abi.lower() - self._platform = platform.lower() - # The __hash__ of every single element in a Set[Tag] will be evaluated each time - # that a set calls its `.disjoint()` method, which may be called hundreds of - # times when scanning a page of links for packages with tags matching that - # Set[Tag]. Pre-computing the value here produces significant speedups for - # downstream consumers. - self._hash = hash((self._interpreter, self._abi, self._platform)) - - @property - def interpreter(self) -> str: - return self._interpreter - - @property - def abi(self) -> str: - return self._abi - - @property - def platform(self) -> str: - return self._platform - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Tag): - return NotImplemented - - return ( - (self._hash == other._hash) # Short-circuit ASAP for perf reasons. - and (self._platform == other._platform) - and (self._abi == other._abi) - and (self._interpreter == other._interpreter) - ) - - def __hash__(self) -> int: - return self._hash - - def __str__(self) -> str: - return f"{self._interpreter}-{self._abi}-{self._platform}" - - def __repr__(self) -> str: - return f"<{self} @ {id(self)}>" - - def __setstate__(self, state: tuple[None, dict[str, Any]]) -> None: - # The cached _hash is wrong when unpickling. - _, slots = state - for k, v in slots.items(): - setattr(self, k, v) - self._hash = hash((self._interpreter, self._abi, self._platform)) - - -def parse_tag(tag: str) -> frozenset[Tag]: - """ - Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. - - Returning a set is required due to the possibility that the tag is a - compressed tag set. - """ - tags = set() - interpreters, abis, platforms = tag.split("-") - for interpreter in interpreters.split("."): - for abi in abis.split("."): - for platform_ in platforms.split("."): - tags.add(Tag(interpreter, abi, platform_)) - return frozenset(tags) - - -def _get_config_var(name: str, warn: bool = False) -> int | str | None: - value: int | str | None = sysconfig.get_config_var(name) - if value is None and warn: - logger.debug( - "Config variable '%s' is unset, Python ABI tag may be incorrect", name - ) - return value - - -def _normalize_string(string: str) -> str: - return string.replace(".", "_").replace("-", "_").replace(" ", "_") - - -def _is_threaded_cpython(abis: list[str]) -> bool: - """ - Determine if the ABI corresponds to a threaded (`--disable-gil`) build. - - The threaded builds are indicated by a "t" in the abiflags. - """ - if len(abis) == 0: - return False - # expect e.g., cp313 - m = re.match(r"cp\d+(.*)", abis[0]) - if not m: - return False - abiflags = m.group(1) - return "t" in abiflags - - -def _abi3_applies(python_version: PythonVersion, threading: bool) -> bool: - """ - Determine if the Python version supports abi3. - - PEP 384 was first implemented in Python 3.2. The threaded (`--disable-gil`) - builds do not support abi3. - """ - return len(python_version) > 1 and tuple(python_version) >= (3, 2) and not threading - - -def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> list[str]: - py_version = tuple(py_version) # To allow for version comparison. - abis = [] - version = _version_nodot(py_version[:2]) - threading = debug = pymalloc = ucs4 = "" - with_debug = _get_config_var("Py_DEBUG", warn) - has_refcount = hasattr(sys, "gettotalrefcount") - # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled - # extension modules is the best option. - # https://github.com/pypa/pip/issues/3383#issuecomment-173267692 - has_ext = "_d.pyd" in EXTENSION_SUFFIXES - if with_debug or (with_debug is None and (has_refcount or has_ext)): - debug = "d" - if py_version >= (3, 13) and _get_config_var("Py_GIL_DISABLED", warn): - threading = "t" - if py_version < (3, 8): - with_pymalloc = _get_config_var("WITH_PYMALLOC", warn) - if with_pymalloc or with_pymalloc is None: - pymalloc = "m" - if py_version < (3, 3): - unicode_size = _get_config_var("Py_UNICODE_SIZE", warn) - if unicode_size == 4 or ( - unicode_size is None and sys.maxunicode == 0x10FFFF - ): - ucs4 = "u" - elif debug: - # Debug builds can also load "normal" extension modules. - # We can also assume no UCS-4 or pymalloc requirement. - abis.append(f"cp{version}{threading}") - abis.insert(0, f"cp{version}{threading}{debug}{pymalloc}{ucs4}") - return abis - - -def cpython_tags( - python_version: PythonVersion | None = None, - abis: Iterable[str] | None = None, - platforms: Iterable[str] | None = None, - *, - warn: bool = False, -) -> Iterator[Tag]: - """ - Yields the tags for a CPython interpreter. - - The tags consist of: - - cp-- - - cp-abi3- - - cp-none- - - cp-abi3- # Older Python versions down to 3.2. - - If python_version only specifies a major version then user-provided ABIs and - the 'none' ABItag will be used. - - If 'abi3' or 'none' are specified in 'abis' then they will be yielded at - their normal position and not at the beginning. - """ - if not python_version: - python_version = sys.version_info[:2] - - interpreter = f"cp{_version_nodot(python_version[:2])}" - - if abis is None: - abis = _cpython_abis(python_version, warn) if len(python_version) > 1 else [] - abis = list(abis) - # 'abi3' and 'none' are explicitly handled later. - for explicit_abi in ("abi3", "none"): - try: - abis.remove(explicit_abi) - except ValueError: # noqa: PERF203 - pass - - platforms = list(platforms or platform_tags()) - for abi in abis: - for platform_ in platforms: - yield Tag(interpreter, abi, platform_) - - threading = _is_threaded_cpython(abis) - use_abi3 = _abi3_applies(python_version, threading) - if use_abi3: - yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms) - yield from (Tag(interpreter, "none", platform_) for platform_ in platforms) - - if use_abi3: - for minor_version in range(python_version[1] - 1, 1, -1): - for platform_ in platforms: - version = _version_nodot((python_version[0], minor_version)) - interpreter = f"cp{version}" - yield Tag(interpreter, "abi3", platform_) - - -def _generic_abi() -> list[str]: - """ - Return the ABI tag based on EXT_SUFFIX. - """ - # The following are examples of `EXT_SUFFIX`. - # We want to keep the parts which are related to the ABI and remove the - # parts which are related to the platform: - # - linux: '.cpython-310-x86_64-linux-gnu.so' => cp310 - # - mac: '.cpython-310-darwin.so' => cp310 - # - win: '.cp310-win_amd64.pyd' => cp310 - # - win: '.pyd' => cp37 (uses _cpython_abis()) - # - pypy: '.pypy38-pp73-x86_64-linux-gnu.so' => pypy38_pp73 - # - graalpy: '.graalpy-38-native-x86_64-darwin.dylib' - # => graalpy_38_native - - ext_suffix = _get_config_var("EXT_SUFFIX", warn=True) - if not isinstance(ext_suffix, str) or ext_suffix[0] != ".": - raise SystemError("invalid sysconfig.get_config_var('EXT_SUFFIX')") - parts = ext_suffix.split(".") - if len(parts) < 3: - # CPython3.7 and earlier uses ".pyd" on Windows. - return _cpython_abis(sys.version_info[:2]) - soabi = parts[1] - if soabi.startswith("cpython"): - # non-windows - abi = "cp" + soabi.split("-")[1] - elif soabi.startswith("cp"): - # windows - abi = soabi.split("-")[0] - elif soabi.startswith("pypy"): - abi = "-".join(soabi.split("-")[:2]) - elif soabi.startswith("graalpy"): - abi = "-".join(soabi.split("-")[:3]) - elif soabi: - # pyston, ironpython, others? - abi = soabi - else: - return [] - return [_normalize_string(abi)] - - -def generic_tags( - interpreter: str | None = None, - abis: Iterable[str] | None = None, - platforms: Iterable[str] | None = None, - *, - warn: bool = False, -) -> Iterator[Tag]: - """ - Yields the tags for a generic interpreter. - - The tags consist of: - - -- - - The "none" ABI will be added if it was not explicitly provided. - """ - if not interpreter: - interp_name = interpreter_name() - interp_version = interpreter_version(warn=warn) - interpreter = f"{interp_name}{interp_version}" - abis = _generic_abi() if abis is None else list(abis) - platforms = list(platforms or platform_tags()) - if "none" not in abis: - abis.append("none") - for abi in abis: - for platform_ in platforms: - yield Tag(interpreter, abi, platform_) - - -def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]: - """ - Yields Python versions in descending order. - - After the latest version, the major-only version will be yielded, and then - all previous versions of that major version. - """ - if len(py_version) > 1: - yield f"py{_version_nodot(py_version[:2])}" - yield f"py{py_version[0]}" - if len(py_version) > 1: - for minor in range(py_version[1] - 1, -1, -1): - yield f"py{_version_nodot((py_version[0], minor))}" - - -def compatible_tags( - python_version: PythonVersion | None = None, - interpreter: str | None = None, - platforms: Iterable[str] | None = None, -) -> Iterator[Tag]: - """ - Yields the sequence of tags that are compatible with a specific version of Python. - - The tags consist of: - - py*-none- - - -none-any # ... if `interpreter` is provided. - - py*-none-any - """ - if not python_version: - python_version = sys.version_info[:2] - platforms = list(platforms or platform_tags()) - for version in _py_interpreter_range(python_version): - for platform_ in platforms: - yield Tag(version, "none", platform_) - if interpreter: - yield Tag(interpreter, "none", "any") - for version in _py_interpreter_range(python_version): - yield Tag(version, "none", "any") - - -def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str: - if not is_32bit: - return arch - - if arch.startswith("ppc"): - return "ppc" - - return "i386" - - -def _mac_binary_formats(version: AppleVersion, cpu_arch: str) -> list[str]: - formats = [cpu_arch] - if cpu_arch == "x86_64": - if version < (10, 4): - return [] - formats.extend(["intel", "fat64", "fat32"]) - - elif cpu_arch == "i386": - if version < (10, 4): - return [] - formats.extend(["intel", "fat32", "fat"]) - - elif cpu_arch == "ppc64": - # TODO: Need to care about 32-bit PPC for ppc64 through 10.2? - if version > (10, 5) or version < (10, 4): - return [] - formats.append("fat64") - - elif cpu_arch == "ppc": - if version > (10, 6): - return [] - formats.extend(["fat32", "fat"]) - - if cpu_arch in {"arm64", "x86_64"}: - formats.append("universal2") - - if cpu_arch in {"x86_64", "i386", "ppc64", "ppc", "intel"}: - formats.append("universal") - - return formats - - -def mac_platforms( - version: AppleVersion | None = None, arch: str | None = None -) -> Iterator[str]: - """ - Yields the platform tags for a macOS system. - - The `version` parameter is a two-item tuple specifying the macOS version to - generate platform tags for. The `arch` parameter is the CPU architecture to - generate platform tags for. Both parameters default to the appropriate value - for the current system. - """ - version_str, _, cpu_arch = platform.mac_ver() - if version is None: - version = cast("AppleVersion", tuple(map(int, version_str.split(".")[:2]))) - if version == (10, 16): - # When built against an older macOS SDK, Python will report macOS 10.16 - # instead of the real version. - version_str = subprocess.run( - [ - sys.executable, - "-sS", - "-c", - "import platform; print(platform.mac_ver()[0])", - ], - check=True, - env={"SYSTEM_VERSION_COMPAT": "0"}, - stdout=subprocess.PIPE, - text=True, - ).stdout - version = cast("AppleVersion", tuple(map(int, version_str.split(".")[:2]))) - - if arch is None: - arch = _mac_arch(cpu_arch) - - if (10, 0) <= version < (11, 0): - # Prior to Mac OS 11, each yearly release of Mac OS bumped the - # "minor" version number. The major version was always 10. - major_version = 10 - for minor_version in range(version[1], -1, -1): - compat_version = major_version, minor_version - binary_formats = _mac_binary_formats(compat_version, arch) - for binary_format in binary_formats: - yield f"macosx_{major_version}_{minor_version}_{binary_format}" - - if version >= (11, 0): - # Starting with Mac OS 11, each yearly release bumps the major version - # number. The minor versions are now the midyear updates. - minor_version = 0 - for major_version in range(version[0], 10, -1): - compat_version = major_version, minor_version - binary_formats = _mac_binary_formats(compat_version, arch) - for binary_format in binary_formats: - yield f"macosx_{major_version}_{minor_version}_{binary_format}" - - if version >= (11, 0): - # Mac OS 11 on x86_64 is compatible with binaries from previous releases. - # Arm64 support was introduced in 11.0, so no Arm binaries from previous - # releases exist. - # - # However, the "universal2" binary format can have a - # macOS version earlier than 11.0 when the x86_64 part of the binary supports - # that version of macOS. - major_version = 10 - if arch == "x86_64": - for minor_version in range(16, 3, -1): - compat_version = major_version, minor_version - binary_formats = _mac_binary_formats(compat_version, arch) - for binary_format in binary_formats: - yield f"macosx_{major_version}_{minor_version}_{binary_format}" - else: - for minor_version in range(16, 3, -1): - compat_version = major_version, minor_version - binary_format = "universal2" - yield f"macosx_{major_version}_{minor_version}_{binary_format}" - - -def ios_platforms( - version: AppleVersion | None = None, multiarch: str | None = None -) -> Iterator[str]: - """ - Yields the platform tags for an iOS system. - - :param version: A two-item tuple specifying the iOS version to generate - platform tags for. Defaults to the current iOS version. - :param multiarch: The CPU architecture+ABI to generate platform tags for - - (the value used by `sys.implementation._multiarch` e.g., - `arm64_iphoneos` or `x84_64_iphonesimulator`). Defaults to the current - multiarch value. - """ - if version is None: - # if iOS is the current platform, ios_ver *must* be defined. However, - # it won't exist for CPython versions before 3.13, which causes a mypy - # error. - _, release, _, _ = platform.ios_ver() # type: ignore[attr-defined, unused-ignore] - version = cast("AppleVersion", tuple(map(int, release.split(".")[:2]))) - - if multiarch is None: - multiarch = sys.implementation._multiarch - multiarch = multiarch.replace("-", "_") - - ios_platform_template = "ios_{major}_{minor}_{multiarch}" - - # Consider any iOS major.minor version from the version requested, down to - # 12.0. 12.0 is the first iOS version that is known to have enough features - # to support CPython. Consider every possible minor release up to X.9. There - # highest the minor has ever gone is 8 (14.8 and 15.8) but having some extra - # candidates that won't ever match doesn't really hurt, and it saves us from - # having to keep an explicit list of known iOS versions in the code. Return - # the results descending order of version number. - - # If the requested major version is less than 12, there won't be any matches. - if version[0] < 12: - return - - # Consider the actual X.Y version that was requested. - yield ios_platform_template.format( - major=version[0], minor=version[1], multiarch=multiarch - ) - - # Consider every minor version from X.0 to the minor version prior to the - # version requested by the platform. - for minor in range(version[1] - 1, -1, -1): - yield ios_platform_template.format( - major=version[0], minor=minor, multiarch=multiarch - ) - - for major in range(version[0] - 1, 11, -1): - for minor in range(9, -1, -1): - yield ios_platform_template.format( - major=major, minor=minor, multiarch=multiarch - ) - - -def android_platforms( - api_level: int | None = None, abi: str | None = None -) -> Iterator[str]: - """ - Yields the :attr:`~Tag.platform` tags for Android. If this function is invoked on - non-Android platforms, the ``api_level`` and ``abi`` arguments are required. - - :param int api_level: The maximum `API level - `__ to return. Defaults - to the current system's version, as returned by ``platform.android_ver``. - :param str abi: The `Android ABI `__, - e.g. ``arm64_v8a``. Defaults to the current system's ABI , as returned by - ``sysconfig.get_platform``. Hyphens and periods will be replaced with - underscores. - """ - if platform.system() != "Android" and (api_level is None or abi is None): - raise TypeError( - "on non-Android platforms, the api_level and abi arguments are required" - ) - - if api_level is None: - # Python 3.13 was the first version to return platform.system() == "Android", - # and also the first version to define platform.android_ver(). - api_level = platform.android_ver().api_level # type: ignore[attr-defined] - - if abi is None: - abi = sysconfig.get_platform().split("-")[-1] - abi = _normalize_string(abi) - - # 16 is the minimum API level known to have enough features to support CPython - # without major patching. Yield every API level from the maximum down to the - # minimum, inclusive. - min_api_level = 16 - for ver in range(api_level, min_api_level - 1, -1): - yield f"android_{ver}_{abi}" - - -def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]: - linux = _normalize_string(sysconfig.get_platform()) - if not linux.startswith("linux_"): - # we should never be here, just yield the sysconfig one and return - yield linux - return - if is_32bit: - if linux == "linux_x86_64": - linux = "linux_i686" - elif linux == "linux_aarch64": - linux = "linux_armv8l" - _, arch = linux.split("_", 1) - archs = {"armv8l": ["armv8l", "armv7l"]}.get(arch, [arch]) - yield from _manylinux.platform_tags(archs) - yield from _musllinux.platform_tags(archs) - for arch in archs: - yield f"linux_{arch}" - - -def _generic_platforms() -> Iterator[str]: - yield _normalize_string(sysconfig.get_platform()) - - -def platform_tags() -> Iterator[str]: - """ - Provides the platform tags for this installation. - """ - if platform.system() == "Darwin": - return mac_platforms() - elif platform.system() == "iOS": - return ios_platforms() - elif platform.system() == "Android": - return android_platforms() - elif platform.system() == "Linux": - return _linux_platforms() - else: - return _generic_platforms() - - -def interpreter_name() -> str: - """ - Returns the name of the running interpreter. - - Some implementations have a reserved, two-letter abbreviation which will - be returned when appropriate. - """ - name = sys.implementation.name - return INTERPRETER_SHORT_NAMES.get(name) or name - - -def interpreter_version(*, warn: bool = False) -> str: - """ - Returns the version of the running interpreter. - """ - version = _get_config_var("py_version_nodot", warn=warn) - return str(version) if version else _version_nodot(sys.version_info[:2]) - - -def _version_nodot(version: PythonVersion) -> str: - return "".join(map(str, version)) - - -def sys_tags(*, warn: bool = False) -> Iterator[Tag]: - """ - Returns the sequence of tag triples for the running interpreter. - - The order of the sequence corresponds to priority order for the - interpreter, from most to least important. - """ - - interp_name = interpreter_name() - if interp_name == "cp": - yield from cpython_tags(warn=warn) - else: - yield from generic_tags() - - if interp_name == "pp": - interp = "pp3" - elif interp_name == "cp": - interp = "cp" + interpreter_version(warn=warn) - else: - interp = None - yield from compatible_tags(interpreter=interp) diff --git a/apps/bitwarden_event_logs/lib/packaging/utils.py b/apps/bitwarden_event_logs/lib/packaging/utils.py deleted file mode 100755 index c41c8137..00000000 --- a/apps/bitwarden_event_logs/lib/packaging/utils.py +++ /dev/null @@ -1,158 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import re -from typing import NewType, Tuple, Union, cast - -from .tags import Tag, parse_tag -from .version import InvalidVersion, Version, _TrimmedRelease - -BuildTag = Union[Tuple[()], Tuple[int, str]] -NormalizedName = NewType("NormalizedName", str) - - -class InvalidName(ValueError): - """ - An invalid distribution name; users should refer to the packaging user guide. - """ - - -class InvalidWheelFilename(ValueError): - """ - An invalid wheel filename was found, users should refer to PEP 427. - """ - - -class InvalidSdistFilename(ValueError): - """ - An invalid sdist filename was found, users should refer to the packaging user guide. - """ - - -# Core metadata spec for `Name` -_validate_regex = re.compile(r"[A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9]", re.IGNORECASE) -_normalized_regex = re.compile(r"[a-z0-9]|[a-z0-9]([a-z0-9-](?!--))*[a-z0-9]") -# PEP 427: The build number must start with a digit. -_build_tag_regex = re.compile(r"(\d+)(.*)") - - -def canonicalize_name(name: str, *, validate: bool = False) -> NormalizedName: - if validate and not _validate_regex.fullmatch(name): - raise InvalidName(f"name is invalid: {name!r}") - # Ensure all ``.`` and ``_`` are ``-`` - # Emulates ``re.sub(r"[-_.]+", "-", name).lower()`` from PEP 503 - # Much faster than re, and even faster than str.translate - value = name.lower().replace("_", "-").replace(".", "-") - # Condense repeats (faster than regex) - while "--" in value: - value = value.replace("--", "-") - return cast("NormalizedName", value) - - -def is_normalized_name(name: str) -> bool: - return _normalized_regex.fullmatch(name) is not None - - -def canonicalize_version( - version: Version | str, *, strip_trailing_zero: bool = True -) -> str: - """ - Return a canonical form of a version as a string. - - >>> canonicalize_version('1.0.1') - '1.0.1' - - Per PEP 625, versions may have multiple canonical forms, differing - only by trailing zeros. - - >>> canonicalize_version('1.0.0') - '1' - >>> canonicalize_version('1.0.0', strip_trailing_zero=False) - '1.0.0' - - Invalid versions are returned unaltered. - - >>> canonicalize_version('foo bar baz') - 'foo bar baz' - """ - if isinstance(version, str): - try: - version = Version(version) - except InvalidVersion: - return str(version) - return str(_TrimmedRelease(version) if strip_trailing_zero else version) - - -def parse_wheel_filename( - filename: str, -) -> tuple[NormalizedName, Version, BuildTag, frozenset[Tag]]: - if not filename.endswith(".whl"): - raise InvalidWheelFilename( - f"Invalid wheel filename (extension must be '.whl'): {filename!r}" - ) - - filename = filename[:-4] - dashes = filename.count("-") - if dashes not in (4, 5): - raise InvalidWheelFilename( - f"Invalid wheel filename (wrong number of parts): {filename!r}" - ) - - parts = filename.split("-", dashes - 2) - name_part = parts[0] - # See PEP 427 for the rules on escaping the project name. - if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None: - raise InvalidWheelFilename(f"Invalid project name: {filename!r}") - name = canonicalize_name(name_part) - - try: - version = Version(parts[1]) - except InvalidVersion as e: - raise InvalidWheelFilename( - f"Invalid wheel filename (invalid version): {filename!r}" - ) from e - - if dashes == 5: - build_part = parts[2] - build_match = _build_tag_regex.match(build_part) - if build_match is None: - raise InvalidWheelFilename( - f"Invalid build number: {build_part} in {filename!r}" - ) - build = cast("BuildTag", (int(build_match.group(1)), build_match.group(2))) - else: - build = () - tags = parse_tag(parts[-1]) - return (name, version, build, tags) - - -def parse_sdist_filename(filename: str) -> tuple[NormalizedName, Version]: - if filename.endswith(".tar.gz"): - file_stem = filename[: -len(".tar.gz")] - elif filename.endswith(".zip"): - file_stem = filename[: -len(".zip")] - else: - raise InvalidSdistFilename( - f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):" - f" {filename!r}" - ) - - # We are requiring a PEP 440 version, which cannot contain dashes, - # so we split on the last dash. - name_part, sep, version_part = file_stem.rpartition("-") - if not sep: - raise InvalidSdistFilename(f"Invalid sdist filename: {filename!r}") - - name = canonicalize_name(name_part) - - try: - version = Version(version_part) - except InvalidVersion as e: - raise InvalidSdistFilename( - f"Invalid sdist filename (invalid version): {filename!r}" - ) from e - - return (name, version) diff --git a/apps/bitwarden_event_logs/lib/packaging/version.py b/apps/bitwarden_event_logs/lib/packaging/version.py deleted file mode 100755 index 1206c462..00000000 --- a/apps/bitwarden_event_logs/lib/packaging/version.py +++ /dev/null @@ -1,792 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -""" -.. testsetup:: - - from packaging.version import parse, Version -""" - -from __future__ import annotations - -import re -import sys -import typing -from typing import ( - Any, - Callable, - Literal, - NamedTuple, - SupportsInt, - Tuple, - TypedDict, - Union, -) - -from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType - -if typing.TYPE_CHECKING: - from typing_extensions import Self, Unpack - -if sys.version_info >= (3, 13): # pragma: no cover - from warnings import deprecated as _deprecated -elif typing.TYPE_CHECKING: - from typing_extensions import deprecated as _deprecated -else: # pragma: no cover - import functools - import warnings - - def _deprecated(message: str) -> object: - def decorator(func: object) -> object: - @functools.wraps(func) - def wrapper(*args: object, **kwargs: object) -> object: - warnings.warn( - message, - category=DeprecationWarning, - stacklevel=2, - ) - return func(*args, **kwargs) - - return wrapper - - return decorator - - -_LETTER_NORMALIZATION = { - "alpha": "a", - "beta": "b", - "c": "rc", - "pre": "rc", - "preview": "rc", - "rev": "post", - "r": "post", -} - -__all__ = ["VERSION_PATTERN", "InvalidVersion", "Version", "parse"] - -LocalType = Tuple[Union[int, str], ...] - -CmpPrePostDevType = Union[InfinityType, NegativeInfinityType, Tuple[str, int]] -CmpLocalType = Union[ - NegativeInfinityType, - Tuple[Union[Tuple[int, str], Tuple[NegativeInfinityType, Union[int, str]]], ...], -] -CmpKey = Tuple[ - int, - Tuple[int, ...], - CmpPrePostDevType, - CmpPrePostDevType, - CmpPrePostDevType, - CmpLocalType, -] -VersionComparisonMethod = Callable[[CmpKey, CmpKey], bool] - - -class _VersionReplace(TypedDict, total=False): - epoch: int | None - release: tuple[int, ...] | None - pre: tuple[Literal["a", "b", "rc"], int] | None - post: int | None - dev: int | None - local: str | None - - -def parse(version: str) -> Version: - """Parse the given version string. - - >>> parse('1.0.dev1') - - - :param version: The version string to parse. - :raises InvalidVersion: When the version string is not a valid version. - """ - return Version(version) - - -class InvalidVersion(ValueError): - """Raised when a version string is not a valid version. - - >>> Version("invalid") - Traceback (most recent call last): - ... - packaging.version.InvalidVersion: Invalid version: 'invalid' - """ - - -class _BaseVersion: - __slots__ = () - - # This can also be a normal member (see the packaging_legacy package); - # we are just requiring it to be readable. Actually defining a property - # has runtime effect on subclasses, so it's typing only. - if typing.TYPE_CHECKING: - - @property - def _key(self) -> tuple[Any, ...]: ... - - def __hash__(self) -> int: - return hash(self._key) - - # Please keep the duplicated `isinstance` check - # in the six comparisons hereunder - # unless you find a way to avoid adding overhead function calls. - def __lt__(self, other: _BaseVersion) -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key < other._key - - def __le__(self, other: _BaseVersion) -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key <= other._key - - def __eq__(self, other: object) -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key == other._key - - def __ge__(self, other: _BaseVersion) -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key >= other._key - - def __gt__(self, other: _BaseVersion) -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key > other._key - - def __ne__(self, other: object) -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key != other._key - - -# Deliberately not anchored to the start and end of the string, to make it -# easier for 3rd party code to reuse - -# Note that ++ doesn't behave identically on CPython and PyPy, so not using it here -_VERSION_PATTERN = r""" - v?+ # optional leading v - (?: - (?:(?P[0-9]+)!)?+ # epoch - (?P[0-9]+(?:\.[0-9]+)*+) # release segment - (?P

                                          # pre-release
-            [._-]?+
-            (?Palpha|a|beta|b|preview|pre|c|rc)
-            [._-]?+
-            (?P[0-9]+)?
-        )?+
-        (?P                                         # post release
-            (?:-(?P[0-9]+))
-            |
-            (?:
-                [._-]?
-                (?Ppost|rev|r)
-                [._-]?
-                (?P[0-9]+)?
-            )
-        )?+
-        (?P                                          # dev release
-            [._-]?+
-            (?Pdev)
-            [._-]?+
-            (?P[0-9]+)?
-        )?+
-    )
-    (?:\+
-        (?P                                        # local version
-            [a-z0-9]+
-            (?:[._-][a-z0-9]+)*+
-        )
-    )?+
-"""
-
-_VERSION_PATTERN_OLD = _VERSION_PATTERN.replace("*+", "*").replace("?+", "?")
-
-# Possessive qualifiers were added in Python 3.11.
-# CPython 3.11.0-3.11.4 had a bug: https://github.com/python/cpython/pull/107795
-# Older PyPy also had a bug.
-VERSION_PATTERN = (
-    _VERSION_PATTERN_OLD
-    if (sys.implementation.name == "cpython" and sys.version_info < (3, 11, 5))
-    or (sys.implementation.name == "pypy" and sys.version_info < (3, 11, 13))
-    or sys.version_info < (3, 11)
-    else _VERSION_PATTERN
-)
-"""
-A string containing the regular expression used to match a valid version.
-
-The pattern is not anchored at either end, and is intended for embedding in larger
-expressions (for example, matching a version number as part of a file name). The
-regular expression should be compiled with the ``re.VERBOSE`` and ``re.IGNORECASE``
-flags set.
-
-:meta hide-value:
-"""
-
-
-# Validation pattern for local version in replace()
-_LOCAL_PATTERN = re.compile(r"[a-z0-9]+(?:[._-][a-z0-9]+)*", re.IGNORECASE)
-
-
-def _validate_epoch(value: object, /) -> int:
-    epoch = value or 0
-    if isinstance(epoch, int) and epoch >= 0:
-        return epoch
-    msg = f"epoch must be non-negative integer, got {epoch}"
-    raise InvalidVersion(msg)
-
-
-def _validate_release(value: object, /) -> tuple[int, ...]:
-    release = (0,) if value is None else value
-    if (
-        isinstance(release, tuple)
-        and len(release) > 0
-        and all(isinstance(i, int) and i >= 0 for i in release)
-    ):
-        return release
-    msg = f"release must be a non-empty tuple of non-negative integers, got {release}"
-    raise InvalidVersion(msg)
-
-
-def _validate_pre(value: object, /) -> tuple[Literal["a", "b", "rc"], int] | None:
-    if value is None:
-        return value
-    if (
-        isinstance(value, tuple)
-        and len(value) == 2
-        and value[0] in ("a", "b", "rc")
-        and isinstance(value[1], int)
-        and value[1] >= 0
-    ):
-        return value
-    msg = f"pre must be a tuple of ('a'|'b'|'rc', non-negative int), got {value}"
-    raise InvalidVersion(msg)
-
-
-def _validate_post(value: object, /) -> tuple[Literal["post"], int] | None:
-    if value is None:
-        return value
-    if isinstance(value, int) and value >= 0:
-        return ("post", value)
-    msg = f"post must be non-negative integer, got {value}"
-    raise InvalidVersion(msg)
-
-
-def _validate_dev(value: object, /) -> tuple[Literal["dev"], int] | None:
-    if value is None:
-        return value
-    if isinstance(value, int) and value >= 0:
-        return ("dev", value)
-    msg = f"dev must be non-negative integer, got {value}"
-    raise InvalidVersion(msg)
-
-
-def _validate_local(value: object, /) -> LocalType | None:
-    if value is None:
-        return value
-    if isinstance(value, str) and _LOCAL_PATTERN.fullmatch(value):
-        return _parse_local_version(value)
-    msg = f"local must be a valid version string, got {value!r}"
-    raise InvalidVersion(msg)
-
-
-# Backward compatibility for internals before 26.0. Do not use.
-class _Version(NamedTuple):
-    epoch: int
-    release: tuple[int, ...]
-    dev: tuple[str, int] | None
-    pre: tuple[str, int] | None
-    post: tuple[str, int] | None
-    local: LocalType | None
-
-
-class Version(_BaseVersion):
-    """This class abstracts handling of a project's versions.
-
-    A :class:`Version` instance is comparison aware and can be compared and
-    sorted using the standard Python interfaces.
-
-    >>> v1 = Version("1.0a5")
-    >>> v2 = Version("1.0")
-    >>> v1
-    
-    >>> v2
-    
-    >>> v1 < v2
-    True
-    >>> v1 == v2
-    False
-    >>> v1 > v2
-    False
-    >>> v1 >= v2
-    False
-    >>> v1 <= v2
-    True
-    """
-
-    __slots__ = ("_dev", "_epoch", "_key_cache", "_local", "_post", "_pre", "_release")
-    __match_args__ = ("_str",)
-
-    _regex = re.compile(r"\s*" + VERSION_PATTERN + r"\s*", re.VERBOSE | re.IGNORECASE)
-
-    _epoch: int
-    _release: tuple[int, ...]
-    _dev: tuple[str, int] | None
-    _pre: tuple[str, int] | None
-    _post: tuple[str, int] | None
-    _local: LocalType | None
-
-    _key_cache: CmpKey | None
-
-    def __init__(self, version: str) -> None:
-        """Initialize a Version object.
-
-        :param version:
-            The string representation of a version which will be parsed and normalized
-            before use.
-        :raises InvalidVersion:
-            If the ``version`` does not conform to PEP 440 in any way then this
-            exception will be raised.
-        """
-        # Validate the version and parse it into pieces
-        match = self._regex.fullmatch(version)
-        if not match:
-            raise InvalidVersion(f"Invalid version: {version!r}")
-        self._epoch = int(match.group("epoch")) if match.group("epoch") else 0
-        self._release = tuple(map(int, match.group("release").split(".")))
-        self._pre = _parse_letter_version(match.group("pre_l"), match.group("pre_n"))
-        self._post = _parse_letter_version(
-            match.group("post_l"), match.group("post_n1") or match.group("post_n2")
-        )
-        self._dev = _parse_letter_version(match.group("dev_l"), match.group("dev_n"))
-        self._local = _parse_local_version(match.group("local"))
-
-        # Key which will be used for sorting
-        self._key_cache = None
-
-    def __replace__(self, **kwargs: Unpack[_VersionReplace]) -> Self:
-        epoch = _validate_epoch(kwargs["epoch"]) if "epoch" in kwargs else self._epoch
-        release = (
-            _validate_release(kwargs["release"])
-            if "release" in kwargs
-            else self._release
-        )
-        pre = _validate_pre(kwargs["pre"]) if "pre" in kwargs else self._pre
-        post = _validate_post(kwargs["post"]) if "post" in kwargs else self._post
-        dev = _validate_dev(kwargs["dev"]) if "dev" in kwargs else self._dev
-        local = _validate_local(kwargs["local"]) if "local" in kwargs else self._local
-
-        if (
-            epoch == self._epoch
-            and release == self._release
-            and pre == self._pre
-            and post == self._post
-            and dev == self._dev
-            and local == self._local
-        ):
-            return self
-
-        new_version = self.__class__.__new__(self.__class__)
-        new_version._key_cache = None
-        new_version._epoch = epoch
-        new_version._release = release
-        new_version._pre = pre
-        new_version._post = post
-        new_version._dev = dev
-        new_version._local = local
-
-        return new_version
-
-    @property
-    def _key(self) -> CmpKey:
-        if self._key_cache is None:
-            self._key_cache = _cmpkey(
-                self._epoch,
-                self._release,
-                self._pre,
-                self._post,
-                self._dev,
-                self._local,
-            )
-        return self._key_cache
-
-    @property
-    @_deprecated("Version._version is private and will be removed soon")
-    def _version(self) -> _Version:
-        return _Version(
-            self._epoch, self._release, self._dev, self._pre, self._post, self._local
-        )
-
-    @_version.setter
-    @_deprecated("Version._version is private and will be removed soon")
-    def _version(self, value: _Version) -> None:
-        self._epoch = value.epoch
-        self._release = value.release
-        self._dev = value.dev
-        self._pre = value.pre
-        self._post = value.post
-        self._local = value.local
-        self._key_cache = None
-
-    def __repr__(self) -> str:
-        """A representation of the Version that shows all internal state.
-
-        >>> Version('1.0.0')
-        
-        """
-        return f""
-
-    def __str__(self) -> str:
-        """A string representation of the version that can be round-tripped.
-
-        >>> str(Version("1.0a5"))
-        '1.0a5'
-        """
-        # This is a hot function, so not calling self.base_version
-        version = ".".join(map(str, self.release))
-
-        # Epoch
-        if self.epoch:
-            version = f"{self.epoch}!{version}"
-
-        # Pre-release
-        if self.pre is not None:
-            version += "".join(map(str, self.pre))
-
-        # Post-release
-        if self.post is not None:
-            version += f".post{self.post}"
-
-        # Development release
-        if self.dev is not None:
-            version += f".dev{self.dev}"
-
-        # Local version segment
-        if self.local is not None:
-            version += f"+{self.local}"
-
-        return version
-
-    @property
-    def _str(self) -> str:
-        """Internal property for match_args"""
-        return str(self)
-
-    @property
-    def epoch(self) -> int:
-        """The epoch of the version.
-
-        >>> Version("2.0.0").epoch
-        0
-        >>> Version("1!2.0.0").epoch
-        1
-        """
-        return self._epoch
-
-    @property
-    def release(self) -> tuple[int, ...]:
-        """The components of the "release" segment of the version.
-
-        >>> Version("1.2.3").release
-        (1, 2, 3)
-        >>> Version("2.0.0").release
-        (2, 0, 0)
-        >>> Version("1!2.0.0.post0").release
-        (2, 0, 0)
-
-        Includes trailing zeroes but not the epoch or any pre-release / development /
-        post-release suffixes.
-        """
-        return self._release
-
-    @property
-    def pre(self) -> tuple[str, int] | None:
-        """The pre-release segment of the version.
-
-        >>> print(Version("1.2.3").pre)
-        None
-        >>> Version("1.2.3a1").pre
-        ('a', 1)
-        >>> Version("1.2.3b1").pre
-        ('b', 1)
-        >>> Version("1.2.3rc1").pre
-        ('rc', 1)
-        """
-        return self._pre
-
-    @property
-    def post(self) -> int | None:
-        """The post-release number of the version.
-
-        >>> print(Version("1.2.3").post)
-        None
-        >>> Version("1.2.3.post1").post
-        1
-        """
-        return self._post[1] if self._post else None
-
-    @property
-    def dev(self) -> int | None:
-        """The development number of the version.
-
-        >>> print(Version("1.2.3").dev)
-        None
-        >>> Version("1.2.3.dev1").dev
-        1
-        """
-        return self._dev[1] if self._dev else None
-
-    @property
-    def local(self) -> str | None:
-        """The local version segment of the version.
-
-        >>> print(Version("1.2.3").local)
-        None
-        >>> Version("1.2.3+abc").local
-        'abc'
-        """
-        if self._local:
-            return ".".join(str(x) for x in self._local)
-        else:
-            return None
-
-    @property
-    def public(self) -> str:
-        """The public portion of the version.
-
-        >>> Version("1.2.3").public
-        '1.2.3'
-        >>> Version("1.2.3+abc").public
-        '1.2.3'
-        >>> Version("1!1.2.3dev1+abc").public
-        '1!1.2.3.dev1'
-        """
-        return str(self).split("+", 1)[0]
-
-    @property
-    def base_version(self) -> str:
-        """The "base version" of the version.
-
-        >>> Version("1.2.3").base_version
-        '1.2.3'
-        >>> Version("1.2.3+abc").base_version
-        '1.2.3'
-        >>> Version("1!1.2.3dev1+abc").base_version
-        '1!1.2.3'
-
-        The "base version" is the public version of the project without any pre or post
-        release markers.
-        """
-        release_segment = ".".join(map(str, self.release))
-        return f"{self.epoch}!{release_segment}" if self.epoch else release_segment
-
-    @property
-    def is_prerelease(self) -> bool:
-        """Whether this version is a pre-release.
-
-        >>> Version("1.2.3").is_prerelease
-        False
-        >>> Version("1.2.3a1").is_prerelease
-        True
-        >>> Version("1.2.3b1").is_prerelease
-        True
-        >>> Version("1.2.3rc1").is_prerelease
-        True
-        >>> Version("1.2.3dev1").is_prerelease
-        True
-        """
-        return self.dev is not None or self.pre is not None
-
-    @property
-    def is_postrelease(self) -> bool:
-        """Whether this version is a post-release.
-
-        >>> Version("1.2.3").is_postrelease
-        False
-        >>> Version("1.2.3.post1").is_postrelease
-        True
-        """
-        return self.post is not None
-
-    @property
-    def is_devrelease(self) -> bool:
-        """Whether this version is a development release.
-
-        >>> Version("1.2.3").is_devrelease
-        False
-        >>> Version("1.2.3.dev1").is_devrelease
-        True
-        """
-        return self.dev is not None
-
-    @property
-    def major(self) -> int:
-        """The first item of :attr:`release` or ``0`` if unavailable.
-
-        >>> Version("1.2.3").major
-        1
-        """
-        return self.release[0] if len(self.release) >= 1 else 0
-
-    @property
-    def minor(self) -> int:
-        """The second item of :attr:`release` or ``0`` if unavailable.
-
-        >>> Version("1.2.3").minor
-        2
-        >>> Version("1").minor
-        0
-        """
-        return self.release[1] if len(self.release) >= 2 else 0
-
-    @property
-    def micro(self) -> int:
-        """The third item of :attr:`release` or ``0`` if unavailable.
-
-        >>> Version("1.2.3").micro
-        3
-        >>> Version("1").micro
-        0
-        """
-        return self.release[2] if len(self.release) >= 3 else 0
-
-
-class _TrimmedRelease(Version):
-    __slots__ = ()
-
-    def __init__(self, version: str | Version) -> None:
-        if isinstance(version, Version):
-            self._epoch = version._epoch
-            self._release = version._release
-            self._dev = version._dev
-            self._pre = version._pre
-            self._post = version._post
-            self._local = version._local
-            self._key_cache = version._key_cache
-            return
-        super().__init__(version)  # pragma: no cover
-
-    @property
-    def release(self) -> tuple[int, ...]:
-        """
-        Release segment without any trailing zeros.
-
-        >>> _TrimmedRelease('1.0.0').release
-        (1,)
-        >>> _TrimmedRelease('0.0').release
-        (0,)
-        """
-        # This leaves one 0.
-        rel = super().release
-        len_release = len(rel)
-        i = len_release
-        while i > 1 and rel[i - 1] == 0:
-            i -= 1
-        return rel if i == len_release else rel[:i]
-
-
-def _parse_letter_version(
-    letter: str | None, number: str | bytes | SupportsInt | None
-) -> tuple[str, int] | None:
-    if letter:
-        # We normalize any letters to their lower case form
-        letter = letter.lower()
-
-        # We consider some words to be alternate spellings of other words and
-        # in those cases we want to normalize the spellings to our preferred
-        # spelling.
-        letter = _LETTER_NORMALIZATION.get(letter, letter)
-
-        # We consider there to be an implicit 0 in a pre-release if there is
-        # not a numeral associated with it.
-        return letter, int(number or 0)
-
-    if number:
-        # We assume if we are given a number, but we are not given a letter
-        # then this is using the implicit post release syntax (e.g. 1.0-1)
-        return "post", int(number)
-
-    return None
-
-
-_local_version_separators = re.compile(r"[\._-]")
-
-
-def _parse_local_version(local: str | None) -> LocalType | None:
-    """
-    Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
-    """
-    if local is not None:
-        return tuple(
-            part.lower() if not part.isdigit() else int(part)
-            for part in _local_version_separators.split(local)
-        )
-    return None
-
-
-def _cmpkey(
-    epoch: int,
-    release: tuple[int, ...],
-    pre: tuple[str, int] | None,
-    post: tuple[str, int] | None,
-    dev: tuple[str, int] | None,
-    local: LocalType | None,
-) -> CmpKey:
-    # When we compare a release version, we want to compare it with all of the
-    # trailing zeros removed. We will use this for our sorting key.
-    len_release = len(release)
-    i = len_release
-    while i and release[i - 1] == 0:
-        i -= 1
-    _release = release if i == len_release else release[:i]
-
-    # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
-    # We'll do this by abusing the pre segment, but we _only_ want to do this
-    # if there is not a pre or a post segment. If we have one of those then
-    # the normal sorting rules will handle this case correctly.
-    if pre is None and post is None and dev is not None:
-        _pre: CmpPrePostDevType = NegativeInfinity
-    # Versions without a pre-release (except as noted above) should sort after
-    # those with one.
-    elif pre is None:
-        _pre = Infinity
-    else:
-        _pre = pre
-
-    # Versions without a post segment should sort before those with one.
-    if post is None:
-        _post: CmpPrePostDevType = NegativeInfinity
-
-    else:
-        _post = post
-
-    # Versions without a development segment should sort after those with one.
-    if dev is None:
-        _dev: CmpPrePostDevType = Infinity
-
-    else:
-        _dev = dev
-
-    if local is None:
-        # Versions without a local segment should sort before those with one.
-        _local: CmpLocalType = NegativeInfinity
-    else:
-        # Versions with a local segment need that segment parsed to implement
-        # the sorting rules in PEP440.
-        # - Alpha numeric segments sort before numeric segments
-        # - Alpha numeric segments sort lexicographically
-        # - Numeric segments sort numerically
-        # - Shorter versions sort before longer versions when the prefixes
-        #   match exactly
-        _local = tuple(
-            (i, "") if isinstance(i, int) else (NegativeInfinity, i) for i in local
-        )
-
-    return epoch, _release, _pre, _post, _dev, _local
diff --git a/apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/INSTALLER b/apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/INSTALLER
deleted file mode 100755
index a1b589e3..00000000
--- a/apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/INSTALLER
+++ /dev/null
@@ -1 +0,0 @@
-pip
diff --git a/apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/LICENSE b/apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/LICENSE
deleted file mode 100755
index 1e65815c..00000000
--- a/apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/LICENSE
+++ /dev/null
@@ -1,54 +0,0 @@
-Copyright 2017- Paul Ganssle 
-Copyright 2017- dateutil contributors (see AUTHORS file)
-
-   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.
-
-The above license applies to all contributions after 2017-12-01, as well as
-all contributions that have been re-licensed (see AUTHORS file for the list of
-contributors who have re-licensed their code).
---------------------------------------------------------------------------------
-dateutil - Extensions to the standard Python datetime module.
-
-Copyright (c) 2003-2011 - Gustavo Niemeyer 
-Copyright (c) 2012-2014 - Tomi Pieviläinen 
-Copyright (c) 2014-2016 - Yaron de Leeuw 
-Copyright (c) 2015-     - Paul Ganssle 
-Copyright (c) 2015-     - dateutil contributors (see AUTHORS file)
-
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-    * Redistributions of source code must retain the above copyright notice,
-      this list of conditions and the following disclaimer.
-    * Redistributions in binary form must reproduce the above copyright notice,
-      this list of conditions and the following disclaimer in the documentation
-      and/or other materials provided with the distribution.
-    * Neither the name of the copyright holder nor the names of its
-      contributors may be used to endorse or promote products derived from
-      this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
-CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-The above BSD License Applies to all code, even that also covered by Apache 2.0.
\ No newline at end of file
diff --git a/apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/METADATA b/apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/METADATA
deleted file mode 100755
index 577f2bf2..00000000
--- a/apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/METADATA
+++ /dev/null
@@ -1,204 +0,0 @@
-Metadata-Version: 2.1
-Name: python-dateutil
-Version: 2.9.0.post0
-Summary: Extensions to the standard Python datetime module
-Home-page: https://github.com/dateutil/dateutil
-Author: Gustavo Niemeyer
-Author-email: gustavo@niemeyer.net
-Maintainer: Paul Ganssle
-Maintainer-email: dateutil@python.org
-License: Dual License
-Project-URL: Documentation, https://dateutil.readthedocs.io/en/stable/
-Project-URL: Source, https://github.com/dateutil/dateutil
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: BSD License
-Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.3
-Classifier: Programming Language :: Python :: 3.4
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-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: Topic :: Software Development :: Libraries
-Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,>=2.7
-Description-Content-Type: text/x-rst
-License-File: LICENSE
-Requires-Dist: six >=1.5
-
-dateutil - powerful extensions to datetime
-==========================================
-
-|pypi| |support| |licence|
-
-|gitter| |readthedocs|
-
-|travis| |appveyor| |pipelines| |coverage|
-
-.. |pypi| image:: https://img.shields.io/pypi/v/python-dateutil.svg?style=flat-square
-    :target: https://pypi.org/project/python-dateutil/
-    :alt: pypi version
-
-.. |support| image:: https://img.shields.io/pypi/pyversions/python-dateutil.svg?style=flat-square
-    :target: https://pypi.org/project/python-dateutil/
-    :alt: supported Python version
-
-.. |travis| image:: https://img.shields.io/travis/dateutil/dateutil/master.svg?style=flat-square&label=Travis%20Build
-    :target: https://travis-ci.org/dateutil/dateutil
-    :alt: travis build status
-
-.. |appveyor| image:: https://img.shields.io/appveyor/ci/dateutil/dateutil/master.svg?style=flat-square&logo=appveyor
-    :target: https://ci.appveyor.com/project/dateutil/dateutil
-    :alt: appveyor build status
-
-.. |pipelines| image:: https://dev.azure.com/pythondateutilazure/dateutil/_apis/build/status/dateutil.dateutil?branchName=master
-    :target: https://dev.azure.com/pythondateutilazure/dateutil/_build/latest?definitionId=1&branchName=master
-    :alt: azure pipelines build status
-
-.. |coverage| image:: https://codecov.io/gh/dateutil/dateutil/branch/master/graphs/badge.svg?branch=master
-    :target: https://codecov.io/gh/dateutil/dateutil?branch=master
-    :alt: Code coverage
-
-.. |gitter| image:: https://badges.gitter.im/dateutil/dateutil.svg
-   :alt: Join the chat at https://gitter.im/dateutil/dateutil
-   :target: https://gitter.im/dateutil/dateutil
-
-.. |licence| image:: https://img.shields.io/pypi/l/python-dateutil.svg?style=flat-square
-    :target: https://pypi.org/project/python-dateutil/
-    :alt: licence
-
-.. |readthedocs| image:: https://img.shields.io/readthedocs/dateutil/latest.svg?style=flat-square&label=Read%20the%20Docs
-   :alt: Read the documentation at https://dateutil.readthedocs.io/en/latest/
-   :target: https://dateutil.readthedocs.io/en/latest/
-
-The `dateutil` module provides powerful extensions to
-the standard `datetime` module, available in Python.
-
-Installation
-============
-`dateutil` can be installed from PyPI using `pip` (note that the package name is
-different from the importable name)::
-
-    pip install python-dateutil
-
-Download
-========
-dateutil is available on PyPI
-https://pypi.org/project/python-dateutil/
-
-The documentation is hosted at:
-https://dateutil.readthedocs.io/en/stable/
-
-Code
-====
-The code and issue tracker are hosted on GitHub:
-https://github.com/dateutil/dateutil/
-
-Features
-========
-
-* Computing of relative deltas (next month, next year,
-  next Monday, last week of month, etc);
-* Computing of relative deltas between two given
-  date and/or datetime objects;
-* Computing of dates based on very flexible recurrence rules,
-  using a superset of the `iCalendar `_
-  specification. Parsing of RFC strings is supported as well.
-* Generic parsing of dates in almost any string format;
-* Timezone (tzinfo) implementations for tzfile(5) format
-  files (/etc/localtime, /usr/share/zoneinfo, etc), TZ
-  environment string (in all known formats), iCalendar
-  format files, given ranges (with help from relative deltas),
-  local machine timezone, fixed offset timezone, UTC timezone,
-  and Windows registry-based time zones.
-* Internal up-to-date world timezone information based on
-  Olson's database.
-* Computing of Easter Sunday dates for any given year,
-  using Western, Orthodox or Julian algorithms;
-* A comprehensive test suite.
-
-Quick example
-=============
-Here's a snapshot, just to give an idea about the power of the
-package. For more examples, look at the documentation.
-
-Suppose you want to know how much time is left, in
-years/months/days/etc, before the next easter happening on a
-year with a Friday 13th in August, and you want to get today's
-date out of the "date" unix system command. Here is the code:
-
-.. code-block:: python3
-
-    >>> from dateutil.relativedelta import *
-    >>> from dateutil.easter import *
-    >>> from dateutil.rrule import *
-    >>> from dateutil.parser import *
-    >>> from datetime import *
-    >>> now = parse("Sat Oct 11 17:13:46 UTC 2003")
-    >>> today = now.date()
-    >>> year = rrule(YEARLY,dtstart=now,bymonth=8,bymonthday=13,byweekday=FR)[0].year
-    >>> rdelta = relativedelta(easter(year), today)
-    >>> print("Today is: %s" % today)
-    Today is: 2003-10-11
-    >>> print("Year with next Aug 13th on a Friday is: %s" % year)
-    Year with next Aug 13th on a Friday is: 2004
-    >>> print("How far is the Easter of that year: %s" % rdelta)
-    How far is the Easter of that year: relativedelta(months=+6)
-    >>> print("And the Easter of that year is: %s" % (today+rdelta))
-    And the Easter of that year is: 2004-04-11
-
-Being exactly 6 months ahead was **really** a coincidence :)
-
-Contributing
-============
-
-We welcome many types of contributions - bug reports, pull requests (code, infrastructure or documentation fixes). For more information about how to contribute to the project, see the ``CONTRIBUTING.md`` file in the repository.
-
-
-Author
-======
-The dateutil module was written by Gustavo Niemeyer 
-in 2003.
-
-It is maintained by:
-
-* Gustavo Niemeyer  2003-2011
-* Tomi Pieviläinen  2012-2014
-* Yaron de Leeuw  2014-2016
-* Paul Ganssle  2015-
-
-Starting with version 2.4.1 and running until 2.8.2, all source and binary
-distributions will be signed by a PGP key that has, at the very least, been
-signed by the key which made the previous release. A table of release signing
-keys can be found below:
-
-===========  ============================
-Releases     Signing key fingerprint
-===========  ============================
-2.4.1-2.8.2  `6B49 ACBA DCF6 BD1C A206 67AB CD54 FCE3 D964 BEFB`_
-===========  ============================
-
-New releases *may* have signed tags, but binary and source distributions
-uploaded to PyPI will no longer have GPG signatures attached.
-
-Contact
-=======
-Our mailing list is available at `dateutil@python.org `_. As it is hosted by the PSF, it is subject to the `PSF code of
-conduct `_.
-
-License
-=======
-
-All contributions after December 1, 2017 released under dual license - either `Apache 2.0 License `_ or the `BSD 3-Clause License `_. Contributions before December 1, 2017 - except those those explicitly relicensed - are released only under the BSD 3-Clause License.
-
-
-.. _6B49 ACBA DCF6 BD1C A206 67AB CD54 FCE3 D964 BEFB:
-   https://pgp.mit.edu/pks/lookup?op=vindex&search=0xCD54FCE3D964BEFB
diff --git a/apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/RECORD b/apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/RECORD
deleted file mode 100755
index 20958cfa..00000000
--- a/apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/RECORD
+++ /dev/null
@@ -1,27 +0,0 @@
-dateutil/__init__.py,sha256=Mqam67WO9IkTmUFyI66vS6IoSXTp9G388DadH2LCMLY,620
-dateutil/_common.py,sha256=77w0yytkrxlYbSn--lDVPUMabUXRR9I3lBv_vQRUqUY,932
-dateutil/_version.py,sha256=BV031OxDDAmy58neUg5yyqLkLaqIw7ibK9As3jiMib0,166
-dateutil/easter.py,sha256=dyBi-lKvimH1u_k6p7Z0JJK72QhqVtVBsqByvpEPKvc,2678
-dateutil/parser/__init__.py,sha256=wWk6GFuxTpjoggCGtgkceJoti4pVjl4_fHQXpNOaSYg,1766
-dateutil/parser/_parser.py,sha256=7klDdyicksQB_Xgl-3UAmBwzCYor1AIZqklIcT6dH_8,58796
-dateutil/parser/isoparser.py,sha256=8Fy999bnCd1frSdOYuOraWfJTtd5W7qQ51NwNuH_hXM,13233
-dateutil/relativedelta.py,sha256=IY_mglMjoZbYfrvloTY2ce02aiVjPIkiZfqgNTZRfuA,24903
-dateutil/rrule.py,sha256=KJzKlaCd1jEbu4A38ZltslaoAUh9nSbdbOFdjp70Kew,66557
-dateutil/tz/__init__.py,sha256=F-Mz13v6jYseklQf9Te9J6nzcLDmq47gORa61K35_FA,444
-dateutil/tz/_common.py,sha256=cgzDTANsOXvEc86cYF77EsliuSab8Puwpsl5-bX3_S4,12977
-dateutil/tz/_factories.py,sha256=unb6XQNXrPMveksTCU-Ag8jmVZs4SojoPUcAHpWnrvU,2569
-dateutil/tz/tz.py,sha256=EUnEdMfeThXiY6l4sh9yBabZ63_POzy01zSsh9thn1o,62855
-dateutil/tz/win.py,sha256=xJszWgSwE1xPx_HJj4ZkepyukC_hNy016WMcXhbRaB8,12935
-dateutil/tzwin.py,sha256=7Ar4vdQCnnM0mKR3MUjbIKsZrBVfHgdwsJZc_mGYRew,59
-dateutil/utils.py,sha256=dKCchEw8eObi0loGTx91unBxm_7UGlU3v_FjFMdqwYM,1965
-dateutil/zoneinfo/__init__.py,sha256=KYg0pthCMjcp5MXSEiBJn3nMjZeNZav7rlJw5-tz1S4,5889
-dateutil/zoneinfo/dateutil-zoneinfo.tar.gz,sha256=0-pS57bpaN4NiE3xKIGTWW-pW4A9tPkqGCeac5gARHU,156400
-dateutil/zoneinfo/rebuild.py,sha256=MiqYzCIHvNbMH-LdRYLv-4T0EIA7hDKt5GLR0IRTLdI,2392
-python_dateutil-2.9.0.post0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-python_dateutil-2.9.0.post0.dist-info/LICENSE,sha256=ugD1Gg2SgjtaHN4n2LW50jIeZ-2NqbwWPv-W1eF-V34,2889
-python_dateutil-2.9.0.post0.dist-info/METADATA,sha256=qdQ22jIr6AgzL5jYgyWZjofLaTpniplp_rTPrXKabpM,8354
-python_dateutil-2.9.0.post0.dist-info/RECORD,,
-python_dateutil-2.9.0.post0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-python_dateutil-2.9.0.post0.dist-info/WHEEL,sha256=-G_t0oGuE7UD0DrSpVZnq1hHMBV9DD2XkS5v7XpmTnk,110
-python_dateutil-2.9.0.post0.dist-info/top_level.txt,sha256=4tjdWkhRZvF7LA_BYe_L9gB2w_p2a-z5y6ArjaRkot8,9
-python_dateutil-2.9.0.post0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
diff --git a/apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/REQUESTED b/apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/REQUESTED
deleted file mode 100755
index e69de29b..00000000
diff --git a/apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/WHEEL b/apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/WHEEL
deleted file mode 100755
index 4724c457..00000000
--- a/apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/WHEEL
+++ /dev/null
@@ -1,6 +0,0 @@
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.42.0)
-Root-Is-Purelib: true
-Tag: py2-none-any
-Tag: py3-none-any
-
diff --git a/apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/zip-safe b/apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/zip-safe
deleted file mode 100755
index 8b137891..00000000
--- a/apps/bitwarden_event_logs/lib/python_dateutil-2.9.0.post0.dist-info/zip-safe
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/INSTALLER b/apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/INSTALLER
deleted file mode 100755
index a1b589e3..00000000
--- a/apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/INSTALLER
+++ /dev/null
@@ -1 +0,0 @@
-pip
diff --git a/apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/METADATA b/apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/METADATA
deleted file mode 100755
index b31773e3..00000000
--- a/apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/METADATA
+++ /dev/null
@@ -1,133 +0,0 @@
-Metadata-Version: 2.4
-Name: requests
-Version: 2.32.5
-Summary: Python HTTP for Humans.
-Home-page: https://requests.readthedocs.io
-Author: Kenneth Reitz
-Author-email: me@kennethreitz.org
-License: Apache-2.0
-Project-URL: Documentation, https://requests.readthedocs.io
-Project-URL: Source, https://github.com/psf/requests
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Environment :: Web Environment
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Natural Language :: English
-Classifier: Operating System :: OS Independent
-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: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Programming Language :: Python :: Implementation :: PyPy
-Classifier: Topic :: Internet :: WWW/HTTP
-Classifier: Topic :: Software Development :: Libraries
-Requires-Python: >=3.9
-Description-Content-Type: text/markdown
-License-File: LICENSE
-Requires-Dist: charset_normalizer<4,>=2
-Requires-Dist: idna<4,>=2.5
-Requires-Dist: urllib3<3,>=1.21.1
-Requires-Dist: certifi>=2017.4.17
-Provides-Extra: security
-Provides-Extra: socks
-Requires-Dist: PySocks!=1.5.7,>=1.5.6; extra == "socks"
-Provides-Extra: use-chardet-on-py3
-Requires-Dist: chardet<6,>=3.0.2; extra == "use-chardet-on-py3"
-Dynamic: author
-Dynamic: author-email
-Dynamic: classifier
-Dynamic: description
-Dynamic: description-content-type
-Dynamic: home-page
-Dynamic: license
-Dynamic: license-file
-Dynamic: project-url
-Dynamic: provides-extra
-Dynamic: requires-dist
-Dynamic: requires-python
-Dynamic: summary
-
-# Requests
-
-**Requests** is a simple, yet elegant, HTTP library.
-
-```python
->>> import requests
->>> r = requests.get('https://httpbin.org/basic-auth/user/pass', auth=('user', 'pass'))
->>> r.status_code
-200
->>> r.headers['content-type']
-'application/json; charset=utf8'
->>> r.encoding
-'utf-8'
->>> r.text
-'{"authenticated": true, ...'
->>> r.json()
-{'authenticated': True, ...}
-```
-
-Requests allows you to send HTTP/1.1 requests extremely easily. There’s no need to manually add query strings to your URLs, or to form-encode your `PUT` & `POST` data — but nowadays, just use the `json` method!
-
-Requests is one of the most downloaded Python packages today, pulling in around `30M downloads / week`— according to GitHub, Requests is currently [depended upon](https://github.com/psf/requests/network/dependents?package_id=UGFja2FnZS01NzA4OTExNg%3D%3D) by `1,000,000+` repositories. You may certainly put your trust in this code.
-
-[![Downloads](https://static.pepy.tech/badge/requests/month)](https://pepy.tech/project/requests)
-[![Supported Versions](https://img.shields.io/pypi/pyversions/requests.svg)](https://pypi.org/project/requests)
-[![Contributors](https://img.shields.io/github/contributors/psf/requests.svg)](https://github.com/psf/requests/graphs/contributors)
-
-## Installing Requests and Supported Versions
-
-Requests is available on PyPI:
-
-```console
-$ python -m pip install requests
-```
-
-Requests officially supports Python 3.9+.
-
-## Supported Features & Best–Practices
-
-Requests is ready for the demands of building robust and reliable HTTP–speaking applications, for the needs of today.
-
-- Keep-Alive & Connection Pooling
-- International Domains and URLs
-- Sessions with Cookie Persistence
-- Browser-style TLS/SSL Verification
-- Basic & Digest Authentication
-- Familiar `dict`–like Cookies
-- Automatic Content Decompression and Decoding
-- Multi-part File Uploads
-- SOCKS Proxy Support
-- Connection Timeouts
-- Streaming Downloads
-- Automatic honoring of `.netrc`
-- Chunked HTTP Requests
-
-## API Reference and User Guide available on [Read the Docs](https://requests.readthedocs.io)
-
-[![Read the Docs](https://raw.githubusercontent.com/psf/requests/main/ext/ss.png)](https://requests.readthedocs.io)
-
-## Cloning the repository
-
-When cloning the Requests repository, you may need to add the `-c
-fetch.fsck.badTimezone=ignore` flag to avoid an error about a bad commit timestamp (see
-[this issue](https://github.com/psf/requests/issues/2690) for more background):
-
-```shell
-git clone -c fetch.fsck.badTimezone=ignore https://github.com/psf/requests.git
-```
-
-You can also apply this setting to your global Git config:
-
-```shell
-git config --global fetch.fsck.badTimezone ignore
-```
-
----
-
-[![Kenneth Reitz](https://raw.githubusercontent.com/psf/requests/main/ext/kr.png)](https://kennethreitz.org) [![Python Software Foundation](https://raw.githubusercontent.com/psf/requests/main/ext/psf.png)](https://www.python.org/psf)
diff --git a/apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/RECORD b/apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/RECORD
deleted file mode 100755
index c7e57418..00000000
--- a/apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/RECORD
+++ /dev/null
@@ -1,25 +0,0 @@
-requests-2.32.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-requests-2.32.5.dist-info/METADATA,sha256=ZbWgjagfSRVRPnYJZf8Ut1GPZbe7Pv4NqzZLvMTUDLA,4945
-requests-2.32.5.dist-info/RECORD,,
-requests-2.32.5.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-requests-2.32.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
-requests-2.32.5.dist-info/licenses/LICENSE,sha256=CeipvOyAZxBGUsFoaFqwkx54aPnIKEtm9a5u2uXxEws,10142
-requests-2.32.5.dist-info/top_level.txt,sha256=fMSVmHfb5rbGOo6xv-O_tUX6j-WyixssE-SnwcDRxNQ,9
-requests/__init__.py,sha256=4xaAERmPDIBPsa2PsjpU9r06yooK-2mZKHTZAhWRWts,5072
-requests/__version__.py,sha256=QKDceK8K_ujqwDDc3oYrR0odOBYgKVOQQ5vFap_G_cg,435
-requests/_internal_utils.py,sha256=nMQymr4hs32TqVo5AbCrmcJEhvPUh7xXlluyqwslLiQ,1495
-requests/adapters.py,sha256=8nX113gbb123aUtx2ETkAN_6IsYX-M2fRoLGluTEcRk,26285
-requests/api.py,sha256=_Zb9Oa7tzVIizTKwFrPjDEY9ejtm_OnSRERnADxGsQs,6449
-requests/auth.py,sha256=kF75tqnLctZ9Mf_hm9TZIj4cQWnN5uxRz8oWsx5wmR0,10186
-requests/certs.py,sha256=Z9Sb410Anv6jUFTyss0jFFhU6xst8ctELqfy8Ev23gw,429
-requests/compat.py,sha256=J7sIjR6XoDGp5JTVzOxkK5fSoUVUa_Pjc7iRZhAWGmI,2142
-requests/cookies.py,sha256=bNi-iqEj4NPZ00-ob-rHvzkvObzN3lEpgw3g6paS3Xw,18590
-requests/exceptions.py,sha256=jJPS1UWATs86ShVUaLorTiJb1SaGuoNEWgICJep-VkY,4260
-requests/help.py,sha256=gPX5d_H7Xd88aDABejhqGgl9B1VFRTt5BmiYvL3PzIQ,3875
-requests/hooks.py,sha256=CiuysiHA39V5UfcCBXFIx83IrDpuwfN9RcTUgv28ftQ,733
-requests/models.py,sha256=MjZdZ4k7tnw-1nz5PKShjmPmqyk0L6DciwnFngb_Vk4,35510
-requests/packages.py,sha256=_g0gZ681UyAlKHRjH6kanbaoxx2eAb6qzcXiODyTIoc,904
-requests/sessions.py,sha256=Cl1dpEnOfwrzzPbku-emepNeN4Rt_0_58Iy2x-JGTm8,30503
-requests/status_codes.py,sha256=iJUAeA25baTdw-6PfD0eF4qhpINDJRJI-yaMqxs4LEI,4322
-requests/structures.py,sha256=-IbmhVz06S-5aPSZuUthZ6-6D9XOjRuTXHOabY041XM,2912
-requests/utils.py,sha256=WqU86rZ3wvhC-tQjWcjtH_HEKZwWB3iWCZV6SW5DEdQ,33213
diff --git a/apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/REQUESTED b/apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/REQUESTED
deleted file mode 100755
index e69de29b..00000000
diff --git a/apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/WHEEL b/apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/WHEEL
deleted file mode 100755
index e7fa31b6..00000000
--- a/apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/WHEEL
+++ /dev/null
@@ -1,5 +0,0 @@
-Wheel-Version: 1.0
-Generator: setuptools (80.9.0)
-Root-Is-Purelib: true
-Tag: py3-none-any
-
diff --git a/apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/licenses/LICENSE b/apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/licenses/LICENSE
deleted file mode 100755
index 67db8588..00000000
--- a/apps/bitwarden_event_logs/lib/requests-2.32.5.dist-info/licenses/LICENSE
+++ /dev/null
@@ -1,175 +0,0 @@
-
-                                 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/apps/bitwarden_event_logs/lib/requests/__init__.py b/apps/bitwarden_event_logs/lib/requests/__init__.py
deleted file mode 100755
index 051cda13..00000000
--- a/apps/bitwarden_event_logs/lib/requests/__init__.py
+++ /dev/null
@@ -1,184 +0,0 @@
-#   __
-#  /__)  _  _     _   _ _/   _
-# / (   (- (/ (/ (- _)  /  _)
-#          /
-
-"""
-Requests HTTP Library
-~~~~~~~~~~~~~~~~~~~~~
-
-Requests is an HTTP library, written in Python, for human beings.
-Basic GET usage:
-
-   >>> import requests
-   >>> r = requests.get('https://www.python.org')
-   >>> r.status_code
-   200
-   >>> b'Python is a programming language' in r.content
-   True
-
-... or POST:
-
-   >>> payload = dict(key1='value1', key2='value2')
-   >>> r = requests.post('https://httpbin.org/post', data=payload)
-   >>> print(r.text)
-   {
-     ...
-     "form": {
-       "key1": "value1",
-       "key2": "value2"
-     },
-     ...
-   }
-
-The other HTTP methods are supported - see `requests.api`. Full documentation
-is at .
-
-:copyright: (c) 2017 by Kenneth Reitz.
-:license: Apache 2.0, see LICENSE for more details.
-"""
-
-import warnings
-
-import urllib3
-
-from .exceptions import RequestsDependencyWarning
-
-try:
-    from charset_normalizer import __version__ as charset_normalizer_version
-except ImportError:
-    charset_normalizer_version = None
-
-try:
-    from chardet import __version__ as chardet_version
-except ImportError:
-    chardet_version = None
-
-
-def check_compatibility(urllib3_version, chardet_version, charset_normalizer_version):
-    urllib3_version = urllib3_version.split(".")
-    assert urllib3_version != ["dev"]  # Verify urllib3 isn't installed from git.
-
-    # Sometimes, urllib3 only reports its version as 16.1.
-    if len(urllib3_version) == 2:
-        urllib3_version.append("0")
-
-    # Check urllib3 for compatibility.
-    major, minor, patch = urllib3_version  # noqa: F811
-    major, minor, patch = int(major), int(minor), int(patch)
-    # urllib3 >= 1.21.1
-    assert major >= 1
-    if major == 1:
-        assert minor >= 21
-
-    # Check charset_normalizer for compatibility.
-    if chardet_version:
-        major, minor, patch = chardet_version.split(".")[:3]
-        major, minor, patch = int(major), int(minor), int(patch)
-        # chardet_version >= 3.0.2, < 6.0.0
-        assert (3, 0, 2) <= (major, minor, patch) < (6, 0, 0)
-    elif charset_normalizer_version:
-        major, minor, patch = charset_normalizer_version.split(".")[:3]
-        major, minor, patch = int(major), int(minor), int(patch)
-        # charset_normalizer >= 2.0.0 < 4.0.0
-        assert (2, 0, 0) <= (major, minor, patch) < (4, 0, 0)
-    else:
-        warnings.warn(
-            "Unable to find acceptable character detection dependency "
-            "(chardet or charset_normalizer).",
-            RequestsDependencyWarning,
-        )
-
-
-def _check_cryptography(cryptography_version):
-    # cryptography < 1.3.4
-    try:
-        cryptography_version = list(map(int, cryptography_version.split(".")))
-    except ValueError:
-        return
-
-    if cryptography_version < [1, 3, 4]:
-        warning = "Old version of cryptography ({}) may cause slowdown.".format(
-            cryptography_version
-        )
-        warnings.warn(warning, RequestsDependencyWarning)
-
-
-# Check imported dependencies for compatibility.
-try:
-    check_compatibility(
-        urllib3.__version__, chardet_version, charset_normalizer_version
-    )
-except (AssertionError, ValueError):
-    warnings.warn(
-        "urllib3 ({}) or chardet ({})/charset_normalizer ({}) doesn't match a supported "
-        "version!".format(
-            urllib3.__version__, chardet_version, charset_normalizer_version
-        ),
-        RequestsDependencyWarning,
-    )
-
-# Attempt to enable urllib3's fallback for SNI support
-# if the standard library doesn't support SNI or the
-# 'ssl' library isn't available.
-try:
-    try:
-        import ssl
-    except ImportError:
-        ssl = None
-
-    if not getattr(ssl, "HAS_SNI", False):
-        from urllib3.contrib import pyopenssl
-
-        pyopenssl.inject_into_urllib3()
-
-        # Check cryptography version
-        from cryptography import __version__ as cryptography_version
-
-        _check_cryptography(cryptography_version)
-except ImportError:
-    pass
-
-# urllib3's DependencyWarnings should be silenced.
-from urllib3.exceptions import DependencyWarning
-
-warnings.simplefilter("ignore", DependencyWarning)
-
-# Set default logging handler to avoid "No handler found" warnings.
-import logging
-from logging import NullHandler
-
-from . import packages, utils
-from .__version__ import (
-    __author__,
-    __author_email__,
-    __build__,
-    __cake__,
-    __copyright__,
-    __description__,
-    __license__,
-    __title__,
-    __url__,
-    __version__,
-)
-from .api import delete, get, head, options, patch, post, put, request
-from .exceptions import (
-    ConnectionError,
-    ConnectTimeout,
-    FileModeWarning,
-    HTTPError,
-    JSONDecodeError,
-    ReadTimeout,
-    RequestException,
-    Timeout,
-    TooManyRedirects,
-    URLRequired,
-)
-from .models import PreparedRequest, Request, Response
-from .sessions import Session, session
-from .status_codes import codes
-
-logging.getLogger(__name__).addHandler(NullHandler())
-
-# FileModeWarnings go off per the default.
-warnings.simplefilter("default", FileModeWarning, append=True)
diff --git a/apps/bitwarden_event_logs/lib/requests/__pycache__/__init__.cpython-39.pyc b/apps/bitwarden_event_logs/lib/requests/__pycache__/__init__.cpython-39.pyc
deleted file mode 100755
index 3411266f3ed1f3168d3cfd21651f002444a319c3..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 3888
zcmbVPOLN?~5hloai5fjEKV*AtSoT^PTjt2}YgcQ_Px-O&$}USA*P@cp)Buub5hMd*
ztR1bCLzEm-<)0uYx#VBulv65|Gq0&imH)z~l1(?s8BfYNq(ap+fJOu8Mt6S=c|$|C
z0pHo*{oeoUA;b6^4*EZwy#v4GxoH^OUpSCOP?)bP
z!WPwiD_iNn2}5Fd-R%4^UoFVN2)7SLyK?U=QF><*qd56zJM~QSF*erI@9({H$8sq#
zF2?vEukTxLNpcG0hk`?Lf=v)ZOdK43hqj=dM?n9hz^jX+`y|t50_}ZEI#=@@3o8g8
z4vxu5HVLxi;5Z)jxxg0W7wn5n{%~+p-eR}p
zB3qQV+3k#XB)B8*vb*vgyO)iR2KVIy_5kcWaj+yFz9R>Z#L0c*9o&u}uaEO%;wqoy
z$A1I9T~+kUsImy*uVI1pZ!7DPpB8>OM-mKEZtVV
z)*_^SNS}A~W)#lS^+?gpwgk~N;fEWEW8d=d5{kU7zP-sQ7@8Z75Gl(2NFNxX}iMrcr(6w4xA9(YS6^wS{dvjzhti=g(GFKj<8Lognf!sL1`6
zp4k$e%eSWActKlSo}Q&JoG*s+ed_fa6h}#)MQGWYAEG%;oPj;Kr@dRnF+B@o<%fDk
zIa++HGv;@wC#9fc-kE}Xms1P0;8Qw?bS^|
zBfTk9?sF-?zZ~vR37WKH^qk=I5>14luevUDyx4!`(1-0Hpgd}|C0wYd{V25Y_BFlP
z^<-~>5*~*`Ksa&yXwk9lg;o@Il)tg57wAl@0lvI^gRXVxk3<-P4)lfa^^RjN1b$0|
ziCCa_W3L6e&O39nnKnj
zE%6HQbjBmYhPV~eOi@qsNP`Ev;Pi2H`YuKAXL
zzUKe2xBZ`c!#?Nv;FbZHzR=shu1g1HUe}cjkqyA4q2-h~sl~;c_kc}CO6C-{?++uv
zi*Hy8tD-`$scOb}k50%LnJ=x3-_LlDF8aXO`R41;TMGo$5ncB~PQ6x3#5!ZOHa+FF
zG{iZ?iD+T8La!SU5P~odGYAFPO@wws$TctUcZ6y;=tFb6^Jmccrh+bEy&lj
zww=dQl9sTiGIJ_yq^$yg&@0^tOGk<`dW_>ApVXTxOYe20(lTHeHnFE$n}0CWF);Z1
zC(S6Mqf>URafuRT=h%;KB3q2&&^@SDyu(7O736b=p#$SYuI$H#>CZ
zyS)aLUWc~4b^*j
zP(&&QEgIDPIGA!d9*HEJ)?hG%5cT?Cy||w*M@?kTsIx<^@RkS2F`%%
zuC@JuL$?O->um|>4MDG48DKc#x?ChJ<;R$@zyQQ9N&*aE2kq+s*Hv8?ZpU>MLPDdK
zR=5Zt(Nhba7HMe{>Q7pQtbt7ubreL>5*FgrirYG^7O4RmkKB)-{$%iw`+K-xtol+!
zAUpEV55!mS0C|}weL8^8t6X;E%av!#4+K6>a>n2bIJdiUv?xSN-y$f1B5AcZwHifV
zd12>;;J$*>lXURw7f(?hhP+{=BhQtHp(gWUkF?tD)Lw`r2BQRcpt>RwtYv96$vfbr
zGHR2gRW8=OcA!@qM2+ALJ;ob@dJH#)p`2EW*h+2D)i23Hn~i@QSlK1330yb$BzxXv
z_Ofb$a0>qEF)~cZ0yiDLnm)
zRgGyqI(P%s7XK)Dw7!24dY0pa`T@K*tSu1E}*jXQ*jt
z)C`ga5=1|B5y>o)OGq3fO(b(jE(2i}yoA&|4zD1&isTv+3^8>b$qgi*A^98$f~)!h
z$xS4;kSrp(jpPoJyGZTzV8@shc&c4mdVUD%e_3L(EY!SD%631ny{#TmJ?s
CxG-)2

diff --git a/apps/bitwarden_event_logs/lib/requests/__pycache__/__version__.cpython-39.pyc b/apps/bitwarden_event_logs/lib/requests/__pycache__/__version__.cpython-39.pyc
deleted file mode 100755
index f066b98ed08c455de4b446dade20ae1c8b1f0182..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 522
zcmZ9J&2AGh5XYS~ADc}hP&xF-N}M85y9(k`p(=<=rBKyQ`$HaV6T;LHgxrBz`pkN)!;k2Pb<(I_R+UVQzgZd!!=XoANU8JWiIUq%On
z6KL@kw0Rr0_!e|{2fDlqJ>G*p??b{981Mn4AIW&QNgRHv&<9`do~}b-bar%hcAA|V
zmmO70sXeWq7a>@Gz}Wp!>QF)`5KQi=GIei4Urp$1)t$6SJvv6MF%;PcRH3e)EpfWI
zBd!6|xOttvwK6YoZ$kI$-FL>WothUR`=_$nj>Fu3{+|8(a(SIa@qV0c20{cCN)%%A
zLTgx4a@4-Ld?ZAE
z-&PEh7|KOVToKo_^~e1>Va5jLZCU9B!;mv+t!Fb8E~Eq00#~SmD9yZqno-J1HG6nO
a_NYs0*PGVYm6oo#;BP|JQFP38k6<

diff --git a/apps/bitwarden_event_logs/lib/requests/__pycache__/_internal_utils.cpython-39.pyc b/apps/bitwarden_event_logs/lib/requests/__pycache__/_internal_utils.cpython-39.pyc
deleted file mode 100755
index 9df0b62997663701261ca55867e4ec84db1e6765..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1597
zcmZuxUvC>l5Z^tY|MU_kP9H!m5?YB0VT5edrz%2-&=?4!A+l3YuW7F5+i|?~ym##G
z#n^IrfPRov@WKmEd<#AR(!KK3ufPLhc5U}h=$>wFW_EUF_xGEl^?xi%Oyu$s|n^eLIB|tBHUVTc#s?%zy)tpvGt?sl2Y7MAC
zW8{TbpbB;T2CxAEY{C`cg-yQ21HSQuhFg4-H=mGjn|lM&-k$#+DEPhrZFDCR(twh2
z8Wl#QI{5LG5d=pnI~N=@v(v)N*rfjCc_(>n+QBo#i5xzoDBHB$7#1Qm
zLPpvsy@S5f`=`4n`hk_|L_T=`#(d|0F1>97PX_l-S22sz8}sc;jbFrCvLJ^3?5Wm*JoaR;AQ#?(KBbSng$0++|NLB>gJdzo{Rw%-vf+1-m=BnO
zvsm#Iv}W0enOP3jX9|T-#;D~AG)NO$a4{laI2^>QJVK%gOk%(}tdlR(fJ`!OYsCmK
zhm|oMg94XELV{&uHS4hBvA}ZL@}=3{;P$}*E2P_$$x>toOQ@;P&~Xdfl`;@oNNr-7
zfU-c8HCF&|s~u7B4)G{892PvDd>VrZT%piGA)7UKx-N3HOoN>imy=z*e0I2~3O-
zHi1AtXCjl>&v+9|jBu@xO~$c`6I`P^fx~k*N5{mH$BNY%b}uI~8B4~HJnFDJg*I#m
z>j0O*c7JE?9BkwSPq`eyH|gZ=1>-rjz%A06)9=|%ls^y$6hUU=>G
zbA#i4_|~ganB(q?b(eBJpTvdb8|zB0?B4O${Xt~?Z@$KFH&IrHGXq*R@nGf7YL_q8
wx~_AlehDyp2b4pt@zEsXMG7C=Uewo7`1OXj=>>=vK3;=1JfHgi`n90>AA@blbN~PV

diff --git a/apps/bitwarden_event_logs/lib/requests/__pycache__/adapters.cpython-39.pyc b/apps/bitwarden_event_logs/lib/requests/__pycache__/adapters.cpython-39.pyc
deleted file mode 100755
index 9a94f4144dc4c0769ed2ad370072605955504e71..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 21654
zcmeHvZHye}ec$Zt>+S9xkK~CWDM}hqFXUR}9!<-Ve4%Kajz^0Tb!3hwE81Azo7>Q`k_TX6exlsm=8vQ00sOt
zMS(<4%Km=;XJ&SGkCYX~K;yP|m}j4v=b7jA|Nj3!V^2;N4g5X%4?b%C?z&<832%~r
z1>C%h%llEvFdU<1I4Q?$rD~@9rfX^W&D1jTo2_N>n{MUW`C7hRs1;JIpTYe^ttj~{
z@}*i?@;T%uYm@D%+EjaAZC`u3HZ9Ndc)q`OK=K9T57rL0XKFKYKY{y)YKJ6WY(3mQ
zTsz!8Qad8|rPk5*Bei3aFC+hG?NP~3wjOIgUV9w*DQ92HYClnXLh{qCX&(-Eq
zMt>SBbg1bsc9
z5%l=w+Luw|XzgX^k=neIs=eZvwFT#x^QiNf^SEOKmcv%9p68-w*K*wTX2&hs9mi_pZMMLc
zI&Q;nb~|3R_+tX$QpF7REws9x>s;-2TZ>9{Rd8@Y*IQ|}-EPm9hm}+?@#@OT)#zsO
zQs<`KYC5mFw&SWwCOB}p+pyX6SM65M)eTL)Zojka`f5kt9$0d3g-xSgSmmnf`rSs@
zHF_!H!8`f|jBR=u?I*_(OfI{af5+2Z;Dy4qYnSz?%%H?cyl!{w4OazI+ia=6uDb1d
zPqk1rAGXBFY}?9n!{=EUR@mO7cl5cSgDTJ^qV)`3(baK$p?onEMA;{lhs2Be&D&I#>?9yqf|A
zUT}C@x!Z2Xse2s1TW_w{JFe@vPEhvT7C^5qdlF1y4FRCa-ryo*toyHBS$OT5EJL_`
z4NNHhGm+zU1(#Pw;)CklPQ90M3}BVH)Iafh)AKMKfIa)i=&&2sdRJM}u;ul(i8R$p
zNuFtxDW7gi@_GZ_bK5C)Sx_i^@
z`1MwIgAY5i)#_}kxi%YN^lXHZ)$JX1678MG<#F6bO700zR1uB>kij4kZdZf=my+f-lk
z^IL^GD7~BVRtBj-*2$bRIt9sP&l!UWJjvnLbc|j~eT4T+zlirf^h*QtcFM_LM;@hx
zBL-?tNX??;OVW-RKIKTtgMw53Fuj`@WCq!FQt8R1U~*w`d8NL*_)BkGT3)R3eY$J}v6ViG=FZ?;@@koSe_E(j8CE9oGoaU{rW
zc0E7H5IifB>JY1?fgb7{bJI{?5GdauFN-PJGE4z)EaC_?-7itHDx;dN+85L=y3`Rzw
zUStbI7M+}8TZdT(nDB-8od$Ro=*eZ2dQ~JwE|bcniuli&nN%T_L#~`Eng!$wsYA$5
zWeTQ=I_6*Ivg$RwF+u>?1Sux}JdWQrYCwJpNwxH^8S4NeC;ff_LQ=`eJB9aC5R)dH
zBG`KF*8rEbJhrtQlwJWvoY!o(e{==xln<3?b29e1)$vx(jXgNOYOT2>z+g9ib-YPHt82ICtdk(GVEuNhV!gTP
zLY`3+RdlI{H(}o*^W3i?UyCFoExp>kWwpBK8cp4@o4$~J%1WNAK+B{DtV}#6&epMg
z8v_P|aIKBzO%A-beaf;u%eK~AUE4opfp^%}oO0LaR!;()DL7eS_oqhM!ql9oq+*>5
z>v&iZLC3^mfCfW?i>4s`up6lsHyQ4Nrotl=U2MvVVc@`4*Y2FMAk*F2Y&JHn2H_Y}
zC*G-U%fn;6ytiCSw-mR}=?_=uglDZ>zGjhlHP@Sv;M{QL*c7bFeY$A(JReJk#Ube#
zZMKW23-i`muj4>)#eP5V3u?KZf500Fn{RhQHX2YZ#t7^%)SDD3}6JP@slQ_zFe>0=-I8XBww#P-3F)|X_~4r`4uF=WJvcKZmSj1J{in1tB`LfvWFYEJW3CR
zRK_f4jC;j{^1OjZBY!dZ|2;fTlK&0EF@KP%W!xchcGCOyhS7dfWl$AehKL=V@ogc?h{_Xmf|0hmqUw
z9CnVN)&b|J^9X(qQX9nY3^hXhKIEJbjWAU^B&_FQ(C3puiON&RUhLn5q(lf^>or^c
zX&^BRt*}(cSIAGRnmAw8h(Y4C;dWeQw@zz>2Yjk^yWz?^%aIBQRmB6qiyBCR00l+&
z0A*0Hk=E6tZWz4jVj?5=Yf
z6g?8kt;JXL>}}{F8oQINwe5GjX5ZB<;ilQ{wXI=`iOz(1*f&92J2D@(JklQChR_3A
zNdm+@-P>GPY`GXrVw5pm^ftS_mSfpIaCaMw8srB=*ylQvSW%Jbb~Mdew;?+^)`g{O
z;CS8Zy=@QrK(}$-_2br(gQxO8ypK2!-swP*5ylu2K;ai)g~Ap$Z7*)E4eg$M1%#8^
zsX1!ZdS!?91-sV*pNzrI>0+dvt`DjTmEY21jawRy!>d}C)~%gx55vG@HXwcVTDFRK
zmqi$m-tdY8&6SWWU=p{bc^+7s?#YhmIJi|?Y$G8|fVr)%E^{=i5xj9pwjqthZHe`S
zay7X4$j%Dft+lYab3u>Zv{GLG
zwmC5BW~c0@?xY9lyD8o^p)S^;I3mqL6*TVTDQH9SGwOu_>rX&UGwMYsl_fki2N@^x
z?X(ufE7>JcyDmb{&QK}?GtbJnAp7r&%pS{&E6bM_uc-qVky=9%6vVUv8mGu~f=ROQ
zoqDM4swLJF-sUjpGJze}2&P5U=URkSD_Ic|g6Ta=R!**%bXqIQBN?3%jwrlQ*djao
zeO%rQ5+h$oO`BzCOgXb?BE@fiCb8SGy0Axp;KIw|vmN1ySoZe|-!it03&w5g4e6zk
zm#BBO^3TnCWE$o70+)n?Nc_VYpP_LsH>zs!%2$YLPGP8OHRu$wE
zf(Pdl6wgrvYjoS&=l>(_{|7Fke`<6JXa=gS4^g9f4mfAGyz|xJYv>4S6x*g6HI!>4
zCw)LB88-1B&_SeG9ez+h4N<)~7W~n4L0DbD6<_l2OR2WHcOQ
zWkr-ijUWqaYtJ2*<}hot6E56;LOoGXjWmH|IyD7VyL_)dwgST!MhrNdeGb=aY=tVj
z;2h^^)Kde@xNiFCbw0(+oz#cvLFS{>cZio6F>R;qLs-yD(0-e+3-`ea?3guQ1&)Mf
zy^yz43DGugs3~11BBJCT6e1u^fHM#+MKb`+b-)ryyTKI63}f=`zOzl^wHBJ713~ZU
zz}mMa(nToH#FZmyBmolExrhYE_V^?zJ-?b5aHPP*mfg=Pi*WW~V<8oNknE7)B@Y%w
z63q!cnX_IY+tO>%>0%w-B-_HyfYsYKAxqe6tx!Blv>}8^+k)n-MH;v*n5}Mg6=W2(
z(W6!bhRj8WR6rW2H(XdYciM`#nvL=g)~*SNSHdW~ARo?IH34NyAey7lFXl|KQHAuh
zR!Xdb+Qm!qrus$ZglY~+C$Z^|acR=IPm@a+mH7XcnT+`t{bPyEuZ~mj5t=|sNGpqA
z#CMS)Mr;{34RwgWgVYgYH{BV4I=~oo2bQS2DfM;G1{ga*Co(X7W(KMC6wQjLKco-l
zhcH(XcQSZiw*;f)a|7cJ44ijiDD`3Z#CxPB!?)5OX7;`{NP~!H?Eef^5n>L^J42Ks
z4+}R!kb~kIEFHiOVQ&er{&~einTJLk$r2nP5ONg~q-Sg$hN^y~5D2sXwBW9#q8{*v
z>v@&$?ge)kjl|S8rz?cJFr-3?cA&XvtE$qpY20Z(RPm@D=Si9L33CzkVsKT!+o=Pf^u-9*I$=8F&g8@bjJt129d%`2RSQH~;)V
zu^2y>r$5Q$%#Wuh`i~}Ycu(siLMT~U8rK3Y?=O%H%sU3q6(T6mwKGVoj$`g-!P3(3
zBpe^4kct!1l;2Y4fRkel$K+E;Fy?<0t>dH5w#^(o}&tw`1Pbx_JDQh(nd
zwKb*wC>#Uw=;eZ2*UD7Z}wP;{<_UYnZS_?x6^1Lbnx424HmXzwW69
zEfsDuaLE6QtgH-LZu*{kVOtPr#E!u*IG
zSKEbYQN%jL`XUqXF+)6M{TId>Cb=CmRHMfhq{7k0`m+ys5hBceU#Lv0
zm)YxkOpY^of(hw`q9PFF+;^G?*9h`gu8ApFd%zZ1P7U%c0UI7v1W{WQ|-BmB$YH`avS#|_bo+pQDCQ&*L6WT;vLrXSRxd2@cw?Gk2%#WT5S*>qWYB
zw~Ea9lc+yM?Sfhre$(}R(6Z9?X_0ZV_P@YlyaIbEJX~e
zoHh%%7u(FyB$kc7zBHn7k6_v~+C->?XxEVYQ@3Gbsif?GI1xg9tX;hZIz~fCbFGId
zYds9c_K5E#W#HkI$EAH5kAX+9>xvXR5|KjFvny)%>CmkN2B-;=B}Ry$_mc`aE6Zcr
z;(0^D0?LlK0vApLvJZ!uHloph8|afdB4;6Qc`=xvBGCswI#dW~KAeEI1;G6w8ca0V
zg;*1pMn`6;8JTih!ttroQj1M!uvCJH?dZemh<=AbwgcaQRU3e+*+ui8EUX_dw06xua@64_DI*qy2u2-d_
zyDA|z*pDujS>0*^REPaC0w~dSJXuQsD!vWpE{nwK7*9Mq*=SikiZyX#cI%c6Xwth)
z$4T6n6)ktzlC9$i038r+v80$7?PYK(Nz(vIi9AZQ>~#~3p@(gdIiSIi->h`S8KdBo
zCu(EL*mL3mTu6vc6Bv(dZK#0h2wy%W20VEXcMvKSq!l>7zcsMFv1xZU
zFo=k9002Ayv4^d4s@BD>iraGEvFWk|oUF?*uo)N}+hEfm8;BEt<&aw#$_YTAD!@O4
zen1mJvVw7UxFgGgyw-j2$+!M$iNVT
z85s@2DPc=D?VHVRPbJ7!%zh>EYAzll$GU<>6O0~_S!jX;hGa0>uE(iWG8L`hGG1Ot%CIH1ig1>`kCGiMQo^az&#C{`q>X|yFq43c#t
zW^e;};g9
zh+LBG#KYQQ<3qXubPuJ8m_`j@r(#OfqBdM>2TmgHl1ksWZ26QH4nd|vgw>8zEUtLE
zw1C&6#0i^?VVvoo*(+Io^2;NZurk_UoXr9*iO{9Exdlb*Htb)0n6&S}N(I#|b2kn1
zb#6D0lM-h#hM(S9P8qv6XVO0mgEka4IJnIBQg0e>A?{|k@NS_4%NBpn7-tY|ix^y3
z%^Ydfht&*Lw28sQR^EHZFE~&T--8hyk-e~%?G^{ct>QYJY`KqUOUpM?G>h++I!7S_
zm+oISC_ymJ**DUW$y)@4y)hufLIRPN7(?nNh(I$!#K(sFCsy#!ssu*vrx>K}=Z1>4
z*2Fe`2KUo_$vAN*BIRS0`+nMBs2PJCieLerPwb5Tk^vZdd5NZxKO^!#O7cfW@fpd#
z6_fmt)`rw_VJtpz2m%R@0K<7k-cN7fF%)iWSW+D`V{$OXl(#s`ViF%~_|WIvOa
zm{1K@e;Y|Klc0hU%E4weAubQYHnsKbExskx_%?Hwn9z|Jq+FZ{5?>G&LHkR7orPo0
zjK_KSCD9+H?YiIcBK_?D9@+ertYA9=xh$9vtcIX4+z>yOR1c{%VemT@}&%boG
z`h~OAvk8$}`$az$lc1F!f&sNMJz%Zq#^C?c!xAE+V8jo8_Qt@(%tCqsaMh+d44Zb26pV-0L3>-J4-J^CMep+1gpt5s`3t9wbPh?G*mix@{F&=70WZX((c#!tf4
z9+t{){x$<}m_Txe!s|Rn_-^
zdg?;Yh8kTEcCGpJ7PPCi-tLI#7RHYq^7+Q=2*
z3h(bv0CH7kFhb>F5FS$bK5jxWUQ=_3@H}j{#p7)0?r}JkQ`h0rhJc;}-M`>7Y7M1#
zGI9(re+Pm(BTn#EZnrQf;2xeC|GO;2y+|#grpnI#@ng+(v*nf@G=sC9E2VQRwzIXwKrI
zXc0r(r`Lgi6gGr__(MY$1Pqd@s&xgeika>dWz3MEOB^*v$1X)A9MdAj(HoC#FE98M
zp=skhAa3n`Ize0*3Ga!`|7++5qDx0jehPhHxa32a9+QTx`7zF*wQC@y3Keali6bJN
zn7m?nFo7qLvk2l@JahGT0K^y7yGZ-y?Ci&>*~&yh79^?D#}8j-?mZ+m1j0b-0I5F+H9@pl)*I5mbA03BrYA>_n>g4^+8!NLtnC*8qAtNU`fzd-HW!>S
zC`l|&jGe3uv2{t9dL^AefzJVk##wy{qo{|Yc$Sch3w(YQLKAgHAe3PNgwh3E9$7ff
zcbjBC67VQ2M;%9C078Rrn+zA=xofZq(=MQ!p&&jgVRBZ(b&+M_Y603qOc4Wj0_q}`
zAOt29*s)x2Xo~9tI4S3W+IHwzh;8rM-$@_f(Dv8pLS$%Y*B{L2=$Ye@rcYUr73GL3
zJQEN)z=ixC^kRukj>gs;I%F2EEG;cAtPEM0FrtJvWshd@H=LWuD#T#`l)mD%37Ir!
zC=&1|s|DQfcmw+fuTsrEJr8?$S2g=$4u_ERBmheBq(T_>t8Vjw1Oa@5xoIJ6<(miAQI}9w`D%>>6#%>YOGimv_LC`-uplP>KLqQ5D8
zRskbm&p03{{}|^!)GRv1ZfI^SejgupK<*4gR{tR0_+Q})eg~r|Y|Z!&-9|g=gYfPU@4OHEhli^hmK>IP|HMDC
zr{t)V{CoeAJtfDaCCAx$UP>$}`GNn$aMZBE@SWpQ@`wJD
zi4yZYeB1~|`TN1*Q-HR~yClXX`y1*1|EU1b`JdlU1yJOUxZH~Vnm`zq6%*M|cn_`g
zNqf)Fr;m1gVq!#3PHP_W0)^GT2_F%Nw46{z@T$w>A#q@LLqqiMfdB9q%vbTN-eeNT
ziAt?LKOM#Rq|=whM~kC&h;0)Cz1l$a{&e`2sOW&N)wiqGRm3}6wiSK40>TUqL{{P|
z^393pQx6dKVffSEqY&8=p#^jyN1w8Y#3BzdT{>*5da+UpayqC(RHPuY)^&D-T{D_a
z2iOGD7zE>4`C^?``CuQceE7bGqwVD#KbX=nd+|NKLnNoEWhF4tO?-;Ubt+G*yIdLC
zNwhcMH<%N1*Y7gNj{>NF&O{`JdFC!63CbacT(f%sn(y;b9>~+hXQI?UW34qNw5q6Y
zF*(eHM}O1@Oh79QMa#N6!h}v#^%xQydlfN8hXhmnzzIdTiuFB33I%BbUW__L5&g8p
zr;qaKR6X3XSFtJ3w7d)1PAq9BtaUjj!R25
z6f99%;zW&u^iCA+71Qucm2ipQ#qbdyjOAsnhL+DgU?UkC;Sk_
zVf8f(SiQ~U>rCoQdPsul=veR7D_1UGyHs0LTYS7f`S_K~__&xp!h4f1{w*dvDJJeC
zC6L6$SHI0f;OrkW$In=(A$0vJZ@GqQmB~;PUFPj6CN`4a#3ctP5wCr;Pz+b-CsT(C
z>B1CzJx2@A6kaSmT|8U-e6dm}7OcWl;gE4p-mQE^rWWMGpfd?WNBpcD@egwP8(!)M
z=pi@|a)7G)PQ#V2>UaWi8e(ZbP=CloK(8_E9XDY
A!~g&Q

diff --git a/apps/bitwarden_event_logs/lib/requests/__pycache__/api.cpython-39.pyc b/apps/bitwarden_event_logs/lib/requests/__pycache__/api.cpython-39.pyc
deleted file mode 100755
index 8f4f46a835422f7c0e61c0cdc3c7ef3d29fa209b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 6763
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

diff --git a/apps/bitwarden_event_logs/lib/requests/__pycache__/cookies.cpython-39.pyc b/apps/bitwarden_event_logs/lib/requests/__pycache__/cookies.cpython-39.pyc
deleted file mode 100755
index 0dc4e591ec7b7579f7ff78b1c5165feb3ef30b7b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 18820
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
av&B871GD+L&l{gNO2xg!6UF;V2mc$+=jvhr

diff --git a/apps/bitwarden_event_logs/lib/requests/__pycache__/exceptions.cpython-39.pyc b/apps/bitwarden_event_logs/lib/requests/__pycache__/exceptions.cpython-39.pyc
deleted file mode 100755
index 6717503db10b30865263cb705bf6a6a458004c2e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 6591
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

diff --git a/apps/bitwarden_event_logs/lib/requests/__pycache__/hooks.cpython-39.pyc b/apps/bitwarden_event_logs/lib/requests/__pycache__/hooks.cpython-39.pyc
deleted file mode 100755
index 36c29ddbd138497f7259fdd0b5135edb0492db6b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 962
zcmY*Y&u`N(6t?rLnbBY%#1R=GA(cbIiNiEaASQ$~v?01vr73k|x3!ukne7gBO1W%j
zF8l)^cHzjs6NwXl0yj=Pd!5!j>3eoQe}2jP?6A#Ef*_v$xRk#=g#Orv^#BgN0_ew(
zIO-#g$GDHV$NdY`_jtfV7=0e~P`7jTizsn27ux7#QdLt;KHmyLjwVvmS;gmtptY(_
zB^R2SiJ&>F*-#eJNFYX)a^%NVG3ts@z0gL?5<*^|GFh-8)Nl*igbW5s=(;Mk7!0-v
zg*bwu*Bl*-QYdBwrz2I(D5I;&32EbNQ4S5>3fuY{fW8ZfL(3P)pgnYsp;iAN$7^CM
z4FElWegH|L1|5?nZtxg=^-h8w`iYcPb-mV0UbLY&6{T^EOaI=cRc%sT7xQwO3X`X-
zu5~(;<`Yv~l$q0KMK!i$nI=hE$YFZzcItdc>P1U-xXevn&FbAPc->zBI(x#!h|LR=
zUCDNeDrbe>O>VSZ+=lNC5BGaFpL8_~VSl@MYyUlnk8+Ynw*7~YkQZZu
z13-jhyakA`x)1z8H)uOq#;ZKbR1Adk(k-o(wl*tQKi;|eZFcRw)7IwTPUOd(|F+Bc
AWdHyG

diff --git a/apps/bitwarden_event_logs/lib/requests/__pycache__/models.cpython-39.pyc b/apps/bitwarden_event_logs/lib/requests/__pycache__/models.cpython-39.pyc
deleted file mode 100755
index b57b1482a90e7b74a0daa4ab9b3d6d95ad0c515a..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 24331
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
H4>83*GIOG2

diff --git a/apps/bitwarden_event_logs/lib/requests/__pycache__/sessions.cpython-39.pyc b/apps/bitwarden_event_logs/lib/requests/__pycache__/sessions.cpython-39.pyc
deleted file mode 100755
index 7651183691347cf6a34d30f8dcfc0fd104d49bd6..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 19761
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
CQzV%H

diff --git a/apps/bitwarden_event_logs/lib/requests/__pycache__/structures.cpython-39.pyc b/apps/bitwarden_event_logs/lib/requests/__pycache__/structures.cpython-39.pyc
deleted file mode 100755
index e7505041e9fa4dcb50844aaf899001bd52159068..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 4419
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 "(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"""
-
-    def __init__(self):
-        super().__init__()
-
-    def send(
-        self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None
-    ):
-        """Sends PreparedRequest object. Returns Response object.
-
-        :param request: The :class:`PreparedRequest ` being sent.
-        :param stream: (optional) Whether to stream the request content.
-        :param timeout: (optional) How long 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
-        :param verify: (optional) 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 to be trusted.
-        :param proxies: (optional) The proxies dictionary to apply to the request.
-        """
-        raise NotImplementedError
-
-    def close(self):
-        """Cleans up adapter specific items."""
-        raise NotImplementedError
-
-
-class HTTPAdapter(BaseAdapter):
-    """The built-in HTTP Adapter for urllib3.
-
-    Provides a general-case interface for Requests sessions to contact HTTP and
-    HTTPS urls by implementing the Transport Adapter interface. This class will
-    usually be created by the :class:`Session ` class under the
-    covers.
-
-    :param pool_connections: The number of urllib3 connection pools to cache.
-    :param pool_maxsize: The maximum number of connections to save in the pool.
-    :param max_retries: The maximum number of retries each connection
-        should attempt. Note, this applies only to failed DNS lookups, socket
-        connections and connection timeouts, never to requests where data has
-        made it to the server. By default, Requests does not retry failed
-        connections. If you need granular control over the conditions under
-        which we retry a request, import urllib3's ``Retry`` class and pass
-        that instead.
-    :param pool_block: Whether the connection pool should block for connections.
-
-    Usage::
-
-      >>> import requests
-      >>> s = requests.Session()
-      >>> a = requests.adapters.HTTPAdapter(max_retries=3)
-      >>> s.mount('http://', a)
-    """
-
-    __attrs__ = [
-        "max_retries",
-        "config",
-        "_pool_connections",
-        "_pool_maxsize",
-        "_pool_block",
-    ]
-
-    def __init__(
-        self,
-        pool_connections=DEFAULT_POOLSIZE,
-        pool_maxsize=DEFAULT_POOLSIZE,
-        max_retries=DEFAULT_RETRIES,
-        pool_block=DEFAULT_POOLBLOCK,
-    ):
-        if max_retries == DEFAULT_RETRIES:
-            self.max_retries = Retry(0, read=False)
-        else:
-            self.max_retries = Retry.from_int(max_retries)
-        self.config = {}
-        self.proxy_manager = {}
-
-        super().__init__()
-
-        self._pool_connections = pool_connections
-        self._pool_maxsize = pool_maxsize
-        self._pool_block = pool_block
-
-        self.init_poolmanager(pool_connections, pool_maxsize, block=pool_block)
-
-    def __getstate__(self):
-        return {attr: getattr(self, attr, None) for attr in self.__attrs__}
-
-    def __setstate__(self, state):
-        # Can't handle by adding 'proxy_manager' to self.__attrs__ because
-        # self.poolmanager uses a lambda function, which isn't pickleable.
-        self.proxy_manager = {}
-        self.config = {}
-
-        for attr, value in state.items():
-            setattr(self, attr, value)
-
-        self.init_poolmanager(
-            self._pool_connections, self._pool_maxsize, block=self._pool_block
-        )
-
-    def init_poolmanager(
-        self, connections, maxsize, block=DEFAULT_POOLBLOCK, **pool_kwargs
-    ):
-        """Initializes a urllib3 PoolManager.
-
-        This method should not be called from user code, and is only
-        exposed for use when subclassing the
-        :class:`HTTPAdapter `.
-
-        :param connections: The number of urllib3 connection pools to cache.
-        :param maxsize: The maximum number of connections to save in the pool.
-        :param block: Block when no free connections are available.
-        :param pool_kwargs: Extra keyword arguments used to initialize the Pool Manager.
-        """
-        # save these values for pickling
-        self._pool_connections = connections
-        self._pool_maxsize = maxsize
-        self._pool_block = block
-
-        self.poolmanager = PoolManager(
-            num_pools=connections,
-            maxsize=maxsize,
-            block=block,
-            **pool_kwargs,
-        )
-
-    def proxy_manager_for(self, proxy, **proxy_kwargs):
-        """Return urllib3 ProxyManager for the given proxy.
-
-        This method should not be called from user code, and is only
-        exposed for use when subclassing the
-        :class:`HTTPAdapter `.
-
-        :param proxy: The proxy to return a urllib3 ProxyManager for.
-        :param proxy_kwargs: Extra keyword arguments used to configure the Proxy Manager.
-        :returns: ProxyManager
-        :rtype: urllib3.ProxyManager
-        """
-        if proxy in self.proxy_manager:
-            manager = self.proxy_manager[proxy]
-        elif proxy.lower().startswith("socks"):
-            username, password = get_auth_from_url(proxy)
-            manager = self.proxy_manager[proxy] = SOCKSProxyManager(
-                proxy,
-                username=username,
-                password=password,
-                num_pools=self._pool_connections,
-                maxsize=self._pool_maxsize,
-                block=self._pool_block,
-                **proxy_kwargs,
-            )
-        else:
-            proxy_headers = self.proxy_headers(proxy)
-            manager = self.proxy_manager[proxy] = proxy_from_url(
-                proxy,
-                proxy_headers=proxy_headers,
-                num_pools=self._pool_connections,
-                maxsize=self._pool_maxsize,
-                block=self._pool_block,
-                **proxy_kwargs,
-            )
-
-        return manager
-
-    def cert_verify(self, conn, url, verify, cert):
-        """Verify a SSL certificate. This method should not be called from user
-        code, and is only exposed for use when subclassing the
-        :class:`HTTPAdapter `.
-
-        :param conn: The urllib3 connection object associated with the cert.
-        :param url: The requested URL.
-        :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: The SSL certificate to verify.
-        """
-        if url.lower().startswith("https") and verify:
-            cert_loc = None
-
-            # Allow self-specified cert location.
-            if verify is not True:
-                cert_loc = verify
-
-            if not cert_loc:
-                cert_loc = extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH)
-
-            if not cert_loc or not os.path.exists(cert_loc):
-                raise OSError(
-                    f"Could not find a suitable TLS CA certificate bundle, "
-                    f"invalid path: {cert_loc}"
-                )
-
-            conn.cert_reqs = "CERT_REQUIRED"
-
-            if not os.path.isdir(cert_loc):
-                conn.ca_certs = cert_loc
-            else:
-                conn.ca_cert_dir = cert_loc
-        else:
-            conn.cert_reqs = "CERT_NONE"
-            conn.ca_certs = None
-            conn.ca_cert_dir = None
-
-        if cert:
-            if not isinstance(cert, basestring):
-                conn.cert_file = cert[0]
-                conn.key_file = cert[1]
-            else:
-                conn.cert_file = cert
-                conn.key_file = None
-            if conn.cert_file and not os.path.exists(conn.cert_file):
-                raise OSError(
-                    f"Could not find the TLS certificate file, "
-                    f"invalid path: {conn.cert_file}"
-                )
-            if conn.key_file and not os.path.exists(conn.key_file):
-                raise OSError(
-                    f"Could not find the TLS key file, invalid path: {conn.key_file}"
-                )
-
-    def build_response(self, req, resp):
-        """Builds a :class:`Response ` object from a urllib3
-        response. This should not be called from user code, and is only exposed
-        for use when subclassing the
-        :class:`HTTPAdapter `
-
-        :param req: The :class:`PreparedRequest ` used to generate the response.
-        :param resp: The urllib3 response object.
-        :rtype: requests.Response
-        """
-        response = Response()
-
-        # Fallback to None if there's no status_code, for whatever reason.
-        response.status_code = getattr(resp, "status", None)
-
-        # Make headers case-insensitive.
-        response.headers = CaseInsensitiveDict(getattr(resp, "headers", {}))
-
-        # Set encoding.
-        response.encoding = get_encoding_from_headers(response.headers)
-        response.raw = resp
-        response.reason = response.raw.reason
-
-        if isinstance(req.url, bytes):
-            response.url = req.url.decode("utf-8")
-        else:
-            response.url = req.url
-
-        # Add new cookies from the server.
-        extract_cookies_to_jar(response.cookies, req, resp)
-
-        # Give the Response some context.
-        response.request = req
-        response.connection = self
-
-        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):
-        """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 `.
-
-        :param url: The URL to connect to.
-        :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:
-            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_url(url)
-        else:
-            # Only scheme should be lower case
-            parsed = urlparse(url)
-            url = parsed.geturl()
-            conn = self.poolmanager.connection_from_url(url)
-
-        return conn
-
-    def close(self):
-        """Disposes of any internal state.
-
-        Currently, this closes the PoolManager and any active ProxyManager,
-        which closes any pooled connections.
-        """
-        self.poolmanager.clear()
-        for proxy in self.proxy_manager.values():
-            proxy.clear()
-
-    def request_url(self, request, proxies):
-        """Obtain the url to use when making the final request.
-
-        If the message is being sent through a HTTP proxy, the full URL has to
-        be used. Otherwise, we should only use the path portion of the URL.
-
-        This should not be called from user code, and is only exposed for use
-        when subclassing the
-        :class:`HTTPAdapter `.
-
-        :param request: The :class:`PreparedRequest ` being sent.
-        :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs.
-        :rtype: str
-        """
-        proxy = select_proxy(request.url, proxies)
-        scheme = urlparse(request.url).scheme
-
-        is_proxied_http_request = proxy and scheme != "https"
-        using_socks_proxy = False
-        if proxy:
-            proxy_scheme = urlparse(proxy).scheme.lower()
-            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)
-
-        return url
-
-    def add_headers(self, request, **kwargs):
-        """Add any headers needed by the connection. As of v2.0 this does
-        nothing by default, but is left for overriding by users that subclass
-        the :class:`HTTPAdapter `.
-
-        This should not be called from user code, and is only exposed for use
-        when subclassing the
-        :class:`HTTPAdapter `.
-
-        :param request: The :class:`PreparedRequest ` to add headers to.
-        :param kwargs: The keyword arguments from the call to send().
-        """
-        pass
-
-    def proxy_headers(self, proxy):
-        """Returns a dictionary of the headers to add to any request sent
-        through a proxy. This works with urllib3 magic to ensure that they are
-        correctly sent to the proxy, rather than in a tunnelled request if
-        CONNECT is being used.
-
-        This should not be called from user code, and is only exposed for use
-        when subclassing the
-        :class:`HTTPAdapter `.
-
-        :param proxy: The url of the proxy being used for this request.
-        :rtype: dict
-        """
-        headers = {}
-        username, password = get_auth_from_url(proxy)
-
-        if username:
-            headers["Proxy-Authorization"] = _basic_auth_str(username, password)
-
-        return headers
-
-    def send(
-        self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None
-    ):
-        """Sends PreparedRequest object. Returns Response object.
-
-        :param request: The :class:`PreparedRequest ` being sent.
-        :param stream: (optional) Whether to stream the request content.
-        :param timeout: (optional) How long 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 or urllib3 Timeout object
-        :param verify: (optional) 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 to be trusted.
-        :param proxies: (optional) The proxies dictionary to apply to the request.
-        :rtype: requests.Response
-        """
-
-        try:
-            conn = self.get_connection_with_tls_context(
-                request, verify, proxies=proxies, cert=cert
-            )
-        except LocationValueError as e:
-            raise InvalidURL(e, request=request)
-
-        self.cert_verify(conn, request.url, verify, cert)
-        url = self.request_url(request, proxies)
-        self.add_headers(
-            request,
-            stream=stream,
-            timeout=timeout,
-            verify=verify,
-            cert=cert,
-            proxies=proxies,
-        )
-
-        chunked = not (request.body is None or "Content-Length" in request.headers)
-
-        if isinstance(timeout, tuple):
-            try:
-                connect, read = timeout
-                timeout = TimeoutSauce(connect=connect, read=read)
-            except ValueError:
-                raise ValueError(
-                    f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, "
-                    f"or a single float to set both timeouts to the same value."
-                )
-        elif isinstance(timeout, TimeoutSauce):
-            pass
-        else:
-            timeout = TimeoutSauce(connect=timeout, read=timeout)
-
-        try:
-            resp = conn.urlopen(
-                method=request.method,
-                url=url,
-                body=request.body,
-                headers=request.headers,
-                redirect=False,
-                assert_same_host=False,
-                preload_content=False,
-                decode_content=False,
-                retries=self.max_retries,
-                timeout=timeout,
-                chunked=chunked,
-            )
-
-        except (ProtocolError, OSError) as err:
-            raise ConnectionError(err, request=request)
-
-        except MaxRetryError as e:
-            if isinstance(e.reason, ConnectTimeoutError):
-                # TODO: Remove this in 3.0.0: see #2811
-                if not isinstance(e.reason, NewConnectionError):
-                    raise ConnectTimeout(e, request=request)
-
-            if isinstance(e.reason, ResponseError):
-                raise RetryError(e, request=request)
-
-            if isinstance(e.reason, _ProxyError):
-                raise ProxyError(e, request=request)
-
-            if isinstance(e.reason, _SSLError):
-                # This branch is for urllib3 v1.22 and later.
-                raise SSLError(e, request=request)
-
-            raise ConnectionError(e, request=request)
-
-        except ClosedPoolError as e:
-            raise ConnectionError(e, request=request)
-
-        except _ProxyError as e:
-            raise ProxyError(e)
-
-        except (_SSLError, _HTTPError) as e:
-            if isinstance(e, _SSLError):
-                # This branch is for urllib3 versions earlier than v1.22
-                raise SSLError(e, request=request)
-            elif isinstance(e, ReadTimeoutError):
-                raise ReadTimeout(e, request=request)
-            elif isinstance(e, _InvalidHeader):
-                raise InvalidHeader(e, request=request)
-            else:
-                raise
-
-        return self.build_response(request, resp)
diff --git a/apps/bitwarden_event_logs/lib/requests/api.py b/apps/bitwarden_event_logs/lib/requests/api.py
deleted file mode 100755
index 59607445..00000000
--- a/apps/bitwarden_event_logs/lib/requests/api.py
+++ /dev/null
@@ -1,157 +0,0 @@
-"""
-requests.api
-~~~~~~~~~~~~
-
-This module implements the Requests API.
-
-:copyright: (c) 2012 by Kenneth Reitz.
-:license: Apache2, see LICENSE for more details.
-"""
-
-from . import sessions
-
-
-def request(method, url, **kwargs):
-    """Constructs and sends a :class:`Request `.
-
-    :param method: method for the new :class:`Request` object: ``GET``, ``OPTIONS``, ``HEAD``, ``POST``, ``PUT``, ``PATCH``, or ``DELETE``.
-    :param url: URL for the new :class:`Request` object.
-    :param params: (optional) Dictionary, list of tuples or bytes to send
-        in the query string for the :class:`Request`.
-    :param data: (optional) Dictionary, list of tuples, bytes, or file-like
-        object to send in the body of the :class:`Request`.
-    :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
-    :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.
-    :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
-        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.
-    :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
-    :param allow_redirects: (optional) Boolean. Enable/disable GET/OPTIONS/POST/PUT/PATCH/DELETE/HEAD redirection. Defaults to ``True``.
-    :type allow_redirects: bool
-    :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
-    :param verify: (optional) 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. Defaults to ``True``.
-    :param stream: (optional) if ``False``, the response content will be immediately downloaded.
-    :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
-    :return: :class:`Response ` object
-    :rtype: requests.Response
-
-    Usage::
-
-      >>> import requests
-      >>> req = requests.request('GET', 'https://httpbin.org/get')
-      >>> req
-      
-    """
-
-    # By using the 'with' statement we are sure the session is closed, thus we
-    # avoid leaving sockets open which can trigger a ResourceWarning in some
-    # cases, and look like a memory leak in others.
-    with sessions.Session() as session:
-        return session.request(method=method, url=url, **kwargs)
-
-
-def get(url, params=None, **kwargs):
-    r"""Sends a GET request.
-
-    :param url: URL for the new :class:`Request` object.
-    :param params: (optional) Dictionary, list of tuples or bytes to send
-        in the query string for the :class:`Request`.
-    :param \*\*kwargs: Optional arguments that ``request`` takes.
-    :return: :class:`Response ` object
-    :rtype: requests.Response
-    """
-
-    return request("get", url, params=params, **kwargs)
-
-
-def options(url, **kwargs):
-    r"""Sends an OPTIONS request.
-
-    :param url: URL for the new :class:`Request` object.
-    :param \*\*kwargs: Optional arguments that ``request`` takes.
-    :return: :class:`Response ` object
-    :rtype: requests.Response
-    """
-
-    return request("options", url, **kwargs)
-
-
-def head(url, **kwargs):
-    r"""Sends a HEAD request.
-
-    :param url: URL for the new :class:`Request` object.
-    :param \*\*kwargs: Optional arguments that ``request`` takes. If
-        `allow_redirects` is not provided, it will be set to `False` (as
-        opposed to the default :meth:`request` behavior).
-    :return: :class:`Response ` object
-    :rtype: requests.Response
-    """
-
-    kwargs.setdefault("allow_redirects", False)
-    return request("head", url, **kwargs)
-
-
-def post(url, data=None, json=None, **kwargs):
-    r"""Sends a POST request.
-
-    :param url: URL for the new :class:`Request` object.
-    :param data: (optional) Dictionary, list of tuples, bytes, or file-like
-        object to send in the body of the :class:`Request`.
-    :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
-    :param \*\*kwargs: Optional arguments that ``request`` takes.
-    :return: :class:`Response ` object
-    :rtype: requests.Response
-    """
-
-    return request("post", url, data=data, json=json, **kwargs)
-
-
-def put(url, data=None, **kwargs):
-    r"""Sends a PUT request.
-
-    :param url: URL for the new :class:`Request` object.
-    :param data: (optional) Dictionary, list of tuples, bytes, or file-like
-        object to send in the body of the :class:`Request`.
-    :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
-    :param \*\*kwargs: Optional arguments that ``request`` takes.
-    :return: :class:`Response ` object
-    :rtype: requests.Response
-    """
-
-    return request("put", url, data=data, **kwargs)
-
-
-def patch(url, data=None, **kwargs):
-    r"""Sends a PATCH request.
-
-    :param url: URL for the new :class:`Request` object.
-    :param data: (optional) Dictionary, list of tuples, bytes, or file-like
-        object to send in the body of the :class:`Request`.
-    :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
-    :param \*\*kwargs: Optional arguments that ``request`` takes.
-    :return: :class:`Response ` object
-    :rtype: requests.Response
-    """
-
-    return request("patch", url, data=data, **kwargs)
-
-
-def delete(url, **kwargs):
-    r"""Sends a DELETE request.
-
-    :param url: URL for the new :class:`Request` object.
-    :param \*\*kwargs: Optional arguments that ``request`` takes.
-    :return: :class:`Response ` object
-    :rtype: requests.Response
-    """
-
-    return request("delete", url, **kwargs)
diff --git a/apps/bitwarden_event_logs/lib/requests/auth.py b/apps/bitwarden_event_logs/lib/requests/auth.py
deleted file mode 100755
index 4a7ce6dc..00000000
--- a/apps/bitwarden_event_logs/lib/requests/auth.py
+++ /dev/null
@@ -1,314 +0,0 @@
-"""
-requests.auth
-~~~~~~~~~~~~~
-
-This module contains the authentication handlers for Requests.
-"""
-
-import hashlib
-import os
-import re
-import threading
-import time
-import warnings
-from base64 import b64encode
-
-from ._internal_utils import to_native_string
-from .compat import basestring, str, urlparse
-from .cookies import extract_cookies_to_jar
-from .utils import parse_dict_header
-
-CONTENT_TYPE_FORM_URLENCODED = "application/x-www-form-urlencoded"
-CONTENT_TYPE_MULTI_PART = "multipart/form-data"
-
-
-def _basic_auth_str(username, password):
-    """Returns a Basic Auth string."""
-
-    # "I want us to put a big-ol' comment on top of it that
-    # says that this behaviour is dumb but we need to preserve
-    # it because people are relying on it."
-    #    - Lukasa
-    #
-    # These are here solely to maintain backwards compatibility
-    # for things like ints. This will be removed in 3.0.0.
-    if not isinstance(username, basestring):
-        warnings.warn(
-            "Non-string usernames will no longer be supported in Requests "
-            "3.0.0. Please convert the object you've passed in ({!r}) to "
-            "a string or bytes object in the near future to avoid "
-            "problems.".format(username),
-            category=DeprecationWarning,
-        )
-        username = str(username)
-
-    if not isinstance(password, basestring):
-        warnings.warn(
-            "Non-string passwords will no longer be supported in Requests "
-            "3.0.0. Please convert the object you've passed in ({!r}) to "
-            "a string or bytes object in the near future to avoid "
-            "problems.".format(type(password)),
-            category=DeprecationWarning,
-        )
-        password = str(password)
-    # -- End Removal --
-
-    if isinstance(username, str):
-        username = username.encode("latin1")
-
-    if isinstance(password, str):
-        password = password.encode("latin1")
-
-    authstr = "Basic " + to_native_string(
-        b64encode(b":".join((username, password))).strip()
-    )
-
-    return authstr
-
-
-class AuthBase:
-    """Base class that all auth implementations derive from"""
-
-    def __call__(self, r):
-        raise NotImplementedError("Auth hooks must be callable.")
-
-
-class HTTPBasicAuth(AuthBase):
-    """Attaches HTTP Basic Authentication to the given Request object."""
-
-    def __init__(self, username, password):
-        self.username = username
-        self.password = password
-
-    def __eq__(self, other):
-        return all(
-            [
-                self.username == getattr(other, "username", None),
-                self.password == getattr(other, "password", None),
-            ]
-        )
-
-    def __ne__(self, other):
-        return not self == other
-
-    def __call__(self, r):
-        r.headers["Authorization"] = _basic_auth_str(self.username, self.password)
-        return r
-
-
-class HTTPProxyAuth(HTTPBasicAuth):
-    """Attaches HTTP Proxy Authentication to a given Request object."""
-
-    def __call__(self, r):
-        r.headers["Proxy-Authorization"] = _basic_auth_str(self.username, self.password)
-        return r
-
-
-class HTTPDigestAuth(AuthBase):
-    """Attaches HTTP Digest Authentication to the given Request object."""
-
-    def __init__(self, username, password):
-        self.username = username
-        self.password = password
-        # Keep state in per-thread local storage
-        self._thread_local = threading.local()
-
-    def init_per_thread_state(self):
-        # Ensure state is initialized just once per-thread
-        if not hasattr(self._thread_local, "init"):
-            self._thread_local.init = True
-            self._thread_local.last_nonce = ""
-            self._thread_local.nonce_count = 0
-            self._thread_local.chal = {}
-            self._thread_local.pos = None
-            self._thread_local.num_401_calls = None
-
-    def build_digest_header(self, method, url):
-        """
-        :rtype: str
-        """
-
-        realm = self._thread_local.chal["realm"]
-        nonce = self._thread_local.chal["nonce"]
-        qop = self._thread_local.chal.get("qop")
-        algorithm = self._thread_local.chal.get("algorithm")
-        opaque = self._thread_local.chal.get("opaque")
-        hash_utf8 = None
-
-        if algorithm is None:
-            _algorithm = "MD5"
-        else:
-            _algorithm = algorithm.upper()
-        # lambdas assume digest modules are imported at the top level
-        if _algorithm == "MD5" or _algorithm == "MD5-SESS":
-
-            def md5_utf8(x):
-                if isinstance(x, str):
-                    x = x.encode("utf-8")
-                return hashlib.md5(x).hexdigest()
-
-            hash_utf8 = md5_utf8
-        elif _algorithm == "SHA":
-
-            def sha_utf8(x):
-                if isinstance(x, str):
-                    x = x.encode("utf-8")
-                return hashlib.sha1(x).hexdigest()
-
-            hash_utf8 = sha_utf8
-        elif _algorithm == "SHA-256":
-
-            def sha256_utf8(x):
-                if isinstance(x, str):
-                    x = x.encode("utf-8")
-                return hashlib.sha256(x).hexdigest()
-
-            hash_utf8 = sha256_utf8
-        elif _algorithm == "SHA-512":
-
-            def sha512_utf8(x):
-                if isinstance(x, str):
-                    x = x.encode("utf-8")
-                return hashlib.sha512(x).hexdigest()
-
-            hash_utf8 = sha512_utf8
-
-        KD = lambda s, d: hash_utf8(f"{s}:{d}")  # noqa:E731
-
-        if hash_utf8 is None:
-            return None
-
-        # XXX not implemented yet
-        entdig = None
-        p_parsed = urlparse(url)
-        #: path is request-uri defined in RFC 2616 which should not be empty
-        path = p_parsed.path or "/"
-        if p_parsed.query:
-            path += f"?{p_parsed.query}"
-
-        A1 = f"{self.username}:{realm}:{self.password}"
-        A2 = f"{method}:{path}"
-
-        HA1 = hash_utf8(A1)
-        HA2 = hash_utf8(A2)
-
-        if nonce == self._thread_local.last_nonce:
-            self._thread_local.nonce_count += 1
-        else:
-            self._thread_local.nonce_count = 1
-        ncvalue = f"{self._thread_local.nonce_count:08x}"
-        s = str(self._thread_local.nonce_count).encode("utf-8")
-        s += nonce.encode("utf-8")
-        s += time.ctime().encode("utf-8")
-        s += os.urandom(8)
-
-        cnonce = hashlib.sha1(s).hexdigest()[:16]
-        if _algorithm == "MD5-SESS":
-            HA1 = hash_utf8(f"{HA1}:{nonce}:{cnonce}")
-
-        if not qop:
-            respdig = KD(HA1, f"{nonce}:{HA2}")
-        elif qop == "auth" or "auth" in qop.split(","):
-            noncebit = f"{nonce}:{ncvalue}:{cnonce}:auth:{HA2}"
-            respdig = KD(HA1, noncebit)
-        else:
-            # XXX handle auth-int.
-            return None
-
-        self._thread_local.last_nonce = nonce
-
-        # XXX should the partial digests be encoded too?
-        base = (
-            f'username="{self.username}", realm="{realm}", nonce="{nonce}", '
-            f'uri="{path}", response="{respdig}"'
-        )
-        if opaque:
-            base += f', opaque="{opaque}"'
-        if algorithm:
-            base += f', algorithm="{algorithm}"'
-        if entdig:
-            base += f', digest="{entdig}"'
-        if qop:
-            base += f', qop="auth", nc={ncvalue}, cnonce="{cnonce}"'
-
-        return f"Digest {base}"
-
-    def handle_redirect(self, r, **kwargs):
-        """Reset num_401_calls counter on redirects."""
-        if r.is_redirect:
-            self._thread_local.num_401_calls = 1
-
-    def handle_401(self, r, **kwargs):
-        """
-        Takes the given response and tries digest-auth, if needed.
-
-        :rtype: requests.Response
-        """
-
-        # If response is not 4xx, do not auth
-        # See https://github.com/psf/requests/issues/3772
-        if not 400 <= r.status_code < 500:
-            self._thread_local.num_401_calls = 1
-            return r
-
-        if self._thread_local.pos is not None:
-            # Rewind the file position indicator of the body to where
-            # it was to resend the request.
-            r.request.body.seek(self._thread_local.pos)
-        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))
-
-            # Consume content and release the original connection
-            # to allow our new request to reuse the same one.
-            r.content
-            r.close()
-            prep = r.request.copy()
-            extract_cookies_to_jar(prep._cookies, r.request, r.raw)
-            prep.prepare_cookies(prep._cookies)
-
-            prep.headers["Authorization"] = self.build_digest_header(
-                prep.method, prep.url
-            )
-            _r = r.connection.send(prep, **kwargs)
-            _r.history.append(r)
-            _r.request = prep
-
-            return _r
-
-        self._thread_local.num_401_calls = 1
-        return r
-
-    def __call__(self, r):
-        # Initialize per-thread state, if needed
-        self.init_per_thread_state()
-        # If we have a saved nonce, skip the 401
-        if self._thread_local.last_nonce:
-            r.headers["Authorization"] = self.build_digest_header(r.method, r.url)
-        try:
-            self._thread_local.pos = r.body.tell()
-        except AttributeError:
-            # In the case of HTTPDigestAuth being reused and the body of
-            # the previous request was a file-like object, pos has the
-            # file position of the previous body. Ensure it's set to
-            # None.
-            self._thread_local.pos = None
-        r.register_hook("response", self.handle_401)
-        r.register_hook("response", self.handle_redirect)
-        self._thread_local.num_401_calls = 1
-
-        return r
-
-    def __eq__(self, other):
-        return all(
-            [
-                self.username == getattr(other, "username", None),
-                self.password == getattr(other, "password", None),
-            ]
-        )
-
-    def __ne__(self, other):
-        return not self == other
diff --git a/apps/bitwarden_event_logs/lib/requests/certs.py b/apps/bitwarden_event_logs/lib/requests/certs.py
deleted file mode 100755
index be422c3e..00000000
--- a/apps/bitwarden_event_logs/lib/requests/certs.py
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env python
-
-"""
-requests.certs
-~~~~~~~~~~~~~~
-
-This module returns the preferred default CA certificate bundle. There is
-only one — the one from the certifi package.
-
-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.
-"""
-from certifi import where
-
-if __name__ == "__main__":
-    print(where())
diff --git a/apps/bitwarden_event_logs/lib/requests/compat.py b/apps/bitwarden_event_logs/lib/requests/compat.py
deleted file mode 100755
index 7f9d7543..00000000
--- a/apps/bitwarden_event_logs/lib/requests/compat.py
+++ /dev/null
@@ -1,106 +0,0 @@
-"""
-requests.compat
-~~~~~~~~~~~~~~~
-
-This module previously handled import compatibility issues
-between Python 2 and Python 3. It remains for backwards
-compatibility until the next major version.
-"""
-
-import importlib
-import sys
-
-# -------
-# urllib3
-# -------
-from 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
-    for lib in ("chardet", "charset_normalizer"):
-        if chardet is None:
-            try:
-                chardet = importlib.import_module(lib)
-            except ImportError:
-                pass
-    return chardet
-
-
-chardet = _resolve_char_detection()
-
-# -------
-# Pythons
-# -------
-
-# Syntax sugar.
-_ver = sys.version_info
-
-#: Python 2.x?
-is_py2 = _ver[0] == 2
-
-#: Python 3.x?
-is_py3 = _ver[0] == 3
-
-# json/simplejson module import resolution
-has_simplejson = False
-try:
-    import simplejson as json
-
-    has_simplejson = True
-except ImportError:
-    import json
-
-if has_simplejson:
-    from simplejson import JSONDecodeError
-else:
-    from json import JSONDecodeError
-
-# Keep OrderedDict for backwards compatibility.
-from collections import OrderedDict
-from collections.abc import Callable, Mapping, MutableMapping
-from http import cookiejar as cookielib
-from http.cookies import Morsel
-from io import StringIO
-
-# --------------
-# Legacy Imports
-# --------------
-from urllib.parse import (
-    quote,
-    quote_plus,
-    unquote,
-    unquote_plus,
-    urldefrag,
-    urlencode,
-    urljoin,
-    urlparse,
-    urlsplit,
-    urlunparse,
-)
-from urllib.request import (
-    getproxies,
-    getproxies_environment,
-    parse_http_list,
-    proxy_bypass,
-    proxy_bypass_environment,
-)
-
-builtin_str = str
-str = str
-bytes = bytes
-basestring = (str, bytes)
-numeric_types = (int, float)
-integer_types = (int,)
diff --git a/apps/bitwarden_event_logs/lib/requests/cookies.py b/apps/bitwarden_event_logs/lib/requests/cookies.py
deleted file mode 100755
index f69d0cda..00000000
--- a/apps/bitwarden_event_logs/lib/requests/cookies.py
+++ /dev/null
@@ -1,561 +0,0 @@
-"""
-requests.cookies
-~~~~~~~~~~~~~~~~
-
-Compatibility code to be able to use `http.cookiejar.CookieJar` with requests.
-
-requests.utils imports from here, so be careful with imports.
-"""
-
-import calendar
-import copy
-import time
-
-from ._internal_utils import to_native_string
-from .compat import Morsel, MutableMapping, cookielib, urlparse, urlunparse
-
-try:
-    import threading
-except ImportError:
-    import dummy_threading as threading
-
-
-class MockRequest:
-    """Wraps a `requests.Request` to mimic a `urllib2.Request`.
-
-    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.
-
-    The original request object is read-only. The client is responsible for collecting
-    the new headers via `get_new_headers()` and interpreting them appropriately. You
-    probably want `get_cookie_header`, defined below.
-    """
-
-    def __init__(self, request):
-        self._r = request
-        self._new_headers = {}
-        self.type = urlparse(self._r.url).scheme
-
-    def get_type(self):
-        return self.type
-
-    def get_host(self):
-        return urlparse(self._r.url).netloc
-
-    def get_origin_req_host(self):
-        return self.get_host()
-
-    def get_full_url(self):
-        # Only return the response's URL if the user hadn't set the Host
-        # header
-        if not self._r.headers.get("Host"):
-            return self._r.url
-        # If they did set it, retrieve it and reconstruct the expected domain
-        host = to_native_string(self._r.headers["Host"], encoding="utf-8")
-        parsed = urlparse(self._r.url)
-        # Reconstruct the URL as we expect it
-        return urlunparse(
-            [
-                parsed.scheme,
-                host,
-                parsed.path,
-                parsed.params,
-                parsed.query,
-                parsed.fragment,
-            ]
-        )
-
-    def is_unverifiable(self):
-        return True
-
-    def has_header(self, name):
-        return name in self._r.headers or name in self._new_headers
-
-    def get_header(self, name, default=None):
-        return self._r.headers.get(name, self._new_headers.get(name, default))
-
-    def add_header(self, key, val):
-        """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()"
-        )
-
-    def add_unredirected_header(self, name, value):
-        self._new_headers[name] = value
-
-    def get_new_headers(self):
-        return self._new_headers
-
-    @property
-    def unverifiable(self):
-        return self.is_unverifiable()
-
-    @property
-    def origin_req_host(self):
-        return self.get_origin_req_host()
-
-    @property
-    def host(self):
-        return self.get_host()
-
-
-class MockResponse:
-    """Wraps a `httplib.HTTPMessage` to mimic a `urllib.addinfourl`.
-
-    ...what? Basically, expose the parsed HTTP headers from the server response
-    the way `http.cookiejar` expects to see them.
-    """
-
-    def __init__(self, headers):
-        """Make a MockResponse for `cookiejar` to read.
-
-        :param headers: a httplib.HTTPMessage or analogous carrying the headers
-        """
-        self._headers = headers
-
-    def info(self):
-        return self._headers
-
-    def getheaders(self, name):
-        self._headers.getheaders(name)
-
-
-def extract_cookies_to_jar(jar, request, response):
-    """Extract the cookies from the response into a CookieJar.
-
-    :param jar: http.cookiejar.CookieJar (not necessarily a RequestsCookieJar)
-    :param request: our own requests.Request object
-    :param response: urllib3.HTTPResponse object
-    """
-    if not (hasattr(response, "_original_response") and response._original_response):
-        return
-    # the _original_response field is the wrapped httplib.HTTPResponse object,
-    req = MockRequest(request)
-    # pull out the HTTPMessage with the headers and put it in the mock:
-    res = MockResponse(response._original_response.msg)
-    jar.extract_cookies(res, req)
-
-
-def get_cookie_header(jar, request):
-    """
-    Produce an appropriate Cookie header string to be sent with `request`, or None.
-
-    :rtype: str
-    """
-    r = MockRequest(request)
-    jar.add_cookie_header(r)
-    return r.get_new_headers().get("Cookie")
-
-
-def remove_cookie_by_name(cookiejar, name, domain=None, path=None):
-    """Unsets a cookie by name, by default over all domains and paths.
-
-    Wraps CookieJar.clear(), is O(n).
-    """
-    clearables = []
-    for cookie in cookiejar:
-        if cookie.name != name:
-            continue
-        if domain is not None and domain != cookie.domain:
-            continue
-        if path is not None and path != cookie.path:
-            continue
-        clearables.append((cookie.domain, cookie.path, cookie.name))
-
-    for domain, path, name in clearables:
-        cookiejar.clear(domain, path, name)
-
-
-class CookieConflictError(RuntimeError):
-    """There are two cookies that meet the criteria specified in the cookie jar.
-    Use .get and .set and include domain and path args in order to be more specific.
-    """
-
-
-class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
-    """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
-    don't specify one, since some clients may expect response.cookies and
-    session.cookies to support dict operations.
-
-    Requests does not use the dict interface internally; it's just for
-    compatibility with external client code. All requests code should work
-    out of the box with externally provided instances of ``CookieJar``, e.g.
-    ``LWPCookieJar`` and ``FileCookieJar``.
-
-    Unlike a regular CookieJar, this class is pickleable.
-
-    .. warning:: dictionary operations that are normally O(1) may be O(n).
-    """
-
-    def get(self, name, default=None, domain=None, path=None):
-        """Dict-like get() that also supports optional domain and path args in
-        order to resolve naming collisions from using one cookie jar over
-        multiple domains.
-
-        .. warning:: operation is O(n), not O(1).
-        """
-        try:
-            return self._find_no_duplicates(name, domain, path)
-        except KeyError:
-            return default
-
-    def set(self, name, value, **kwargs):
-        """Dict-like set() that also supports optional domain and path args in
-        order to resolve naming collisions from using one cookie jar over
-        multiple domains.
-        """
-        # support client code that unsets cookies by assignment of a None value:
-        if value is None:
-            remove_cookie_by_name(
-                self, name, domain=kwargs.get("domain"), path=kwargs.get("path")
-            )
-            return
-
-        if isinstance(value, Morsel):
-            c = morsel_to_cookie(value)
-        else:
-            c = create_cookie(name, value, **kwargs)
-        self.set_cookie(c)
-        return c
-
-    def iterkeys(self):
-        """Dict-like iterkeys() that returns an iterator of names of cookies
-        from the jar.
-
-        .. seealso:: itervalues() and iteritems().
-        """
-        for cookie in iter(self):
-            yield cookie.name
-
-    def keys(self):
-        """Dict-like keys() that returns a list of names of cookies from the
-        jar.
-
-        .. seealso:: values() and items().
-        """
-        return list(self.iterkeys())
-
-    def itervalues(self):
-        """Dict-like itervalues() that returns an iterator of values of cookies
-        from the jar.
-
-        .. seealso:: iterkeys() and iteritems().
-        """
-        for cookie in iter(self):
-            yield cookie.value
-
-    def values(self):
-        """Dict-like values() that returns a list of values of cookies from the
-        jar.
-
-        .. seealso:: keys() and items().
-        """
-        return list(self.itervalues())
-
-    def iteritems(self):
-        """Dict-like iteritems() that returns an iterator of name-value tuples
-        from the jar.
-
-        .. seealso:: iterkeys() and itervalues().
-        """
-        for cookie in iter(self):
-            yield cookie.name, cookie.value
-
-    def items(self):
-        """Dict-like items() that returns a list of name-value tuples from the
-        jar. Allows client-code to call ``dict(RequestsCookieJar)`` and get a
-        vanilla python dict of key value pairs.
-
-        .. seealso:: keys() and values().
-        """
-        return list(self.iteritems())
-
-    def list_domains(self):
-        """Utility method to list all the domains in the jar."""
-        domains = []
-        for cookie in iter(self):
-            if cookie.domain not in domains:
-                domains.append(cookie.domain)
-        return domains
-
-    def list_paths(self):
-        """Utility method to list all the paths in the jar."""
-        paths = []
-        for cookie in iter(self):
-            if cookie.path not in paths:
-                paths.append(cookie.path)
-        return paths
-
-    def multiple_domains(self):
-        """Returns True if there are multiple domains in the jar.
-        Returns False otherwise.
-
-        :rtype: bool
-        """
-        domains = []
-        for cookie in iter(self):
-            if cookie.domain is not None and cookie.domain in domains:
-                return True
-            domains.append(cookie.domain)
-        return False  # there is only one domain in jar
-
-    def get_dict(self, domain=None, path=None):
-        """Takes as an argument an optional domain and path and returns a plain
-        old Python dict of name-value pairs of cookies that meet the
-        requirements.
-
-        :rtype: dict
-        """
-        dictionary = {}
-        for cookie in iter(self):
-            if (domain is None or cookie.domain == domain) and (
-                path is None or cookie.path == path
-            ):
-                dictionary[cookie.name] = cookie.value
-        return dictionary
-
-    def __contains__(self, name):
-        try:
-            return super().__contains__(name)
-        except CookieConflictError:
-            return True
-
-    def __getitem__(self, name):
-        """Dict-like __getitem__() for compatibility with client code. Throws
-        exception if there are more than one cookie with name. In that case,
-        use the more explicit get() method instead.
-
-        .. warning:: operation is O(n), not O(1).
-        """
-        return self._find_no_duplicates(name)
-
-    def __setitem__(self, name, value):
-        """Dict-like __setitem__ for compatibility with client code. Throws
-        exception if there is already a cookie of that name in the jar. In that
-        case, use the more explicit set() method instead.
-        """
-        self.set(name, value)
-
-    def __delitem__(self, name):
-        """Deletes a cookie given a name. Wraps ``http.cookiejar.CookieJar``'s
-        ``remove_cookie_by_name()``.
-        """
-        remove_cookie_by_name(self, name)
-
-    def set_cookie(self, cookie, *args, **kwargs):
-        if (
-            hasattr(cookie.value, "startswith")
-            and cookie.value.startswith('"')
-            and cookie.value.endswith('"')
-        ):
-            cookie.value = cookie.value.replace('\\"', "")
-        return super().set_cookie(cookie, *args, **kwargs)
-
-    def update(self, other):
-        """Updates this jar with cookies from another CookieJar or dict-like"""
-        if isinstance(other, cookielib.CookieJar):
-            for cookie in other:
-                self.set_cookie(copy.copy(cookie))
-        else:
-            super().update(other)
-
-    def _find(self, name, domain=None, path=None):
-        """Requests uses this method internally to get cookie values.
-
-        If there are conflicting cookies, _find arbitrarily chooses one.
-        See _find_no_duplicates if you want an exception thrown if there are
-        conflicting cookies.
-
-        :param name: a string containing name of cookie
-        :param domain: (optional) string containing domain of cookie
-        :param path: (optional) string containing path of cookie
-        :return: cookie.value
-        """
-        for cookie in iter(self):
-            if cookie.name == name:
-                if domain is None or cookie.domain == domain:
-                    if path is None or cookie.path == path:
-                        return cookie.value
-
-        raise KeyError(f"name={name!r}, domain={domain!r}, path={path!r}")
-
-    def _find_no_duplicates(self, name, domain=None, path=None):
-        """Both ``__get_item__`` and ``get`` call this function: it's never
-        used elsewhere in Requests.
-
-        :param name: a string containing name of cookie
-        :param domain: (optional) string containing domain of cookie
-        :param path: (optional) string containing path of cookie
-        :raises KeyError: if cookie is not found
-        :raises CookieConflictError: if there are multiple cookies
-            that match name and optionally domain and path
-        :return: cookie.value
-        """
-        toReturn = None
-        for cookie in iter(self):
-            if cookie.name == name:
-                if domain is None or cookie.domain == domain:
-                    if path is None or cookie.path == path:
-                        if toReturn is not None:
-                            # if there are multiple cookies that meet passed in criteria
-                            raise CookieConflictError(
-                                f"There are multiple cookies with name, {name!r}"
-                            )
-                        # we will eventually return this as long as no cookie conflict
-                        toReturn = cookie.value
-
-        if toReturn:
-            return toReturn
-        raise KeyError(f"name={name!r}, domain={domain!r}, path={path!r}")
-
-    def __getstate__(self):
-        """Unlike a normal CookieJar, this class is pickleable."""
-        state = self.__dict__.copy()
-        # remove the unpickleable RLock object
-        state.pop("_cookies_lock")
-        return state
-
-    def __setstate__(self, state):
-        """Unlike a normal CookieJar, this class is pickleable."""
-        self.__dict__.update(state)
-        if "_cookies_lock" not in self.__dict__:
-            self._cookies_lock = threading.RLock()
-
-    def copy(self):
-        """Return a copy of this RequestsCookieJar."""
-        new_cj = RequestsCookieJar()
-        new_cj.set_policy(self.get_policy())
-        new_cj.update(self)
-        return new_cj
-
-    def get_policy(self):
-        """Return the CookiePolicy instance used."""
-        return self._policy
-
-
-def _copy_cookie_jar(jar):
-    if jar is None:
-        return None
-
-    if hasattr(jar, "copy"):
-        # We're dealing with an instance of RequestsCookieJar
-        return jar.copy()
-    # We're dealing with a generic CookieJar instance
-    new_jar = copy.copy(jar)
-    new_jar.clear()
-    for cookie in jar:
-        new_jar.set_cookie(copy.copy(cookie))
-    return new_jar
-
-
-def create_cookie(name, value, **kwargs):
-    """Make a cookie from underspecified parameters.
-
-    By default, the pair of `name` and `value` will be set for the domain ''
-    and sent on every request (this is sometimes called a "supercookie").
-    """
-    result = {
-        "version": 0,
-        "name": name,
-        "value": value,
-        "port": None,
-        "domain": "",
-        "path": "/",
-        "secure": False,
-        "expires": None,
-        "discard": True,
-        "comment": None,
-        "comment_url": None,
-        "rest": {"HttpOnly": None},
-        "rfc2109": False,
-    }
-
-    badargs = set(kwargs) - set(result)
-    if badargs:
-        raise TypeError(
-            f"create_cookie() got unexpected keyword arguments: {list(badargs)}"
-        )
-
-    result.update(kwargs)
-    result["port_specified"] = bool(result["port"])
-    result["domain_specified"] = bool(result["domain"])
-    result["domain_initial_dot"] = result["domain"].startswith(".")
-    result["path_specified"] = bool(result["path"])
-
-    return cookielib.Cookie(**result)
-
-
-def morsel_to_cookie(morsel):
-    """Convert a Morsel object into a Cookie containing the one k/v pair."""
-
-    expires = None
-    if morsel["max-age"]:
-        try:
-            expires = int(time.time() + int(morsel["max-age"]))
-        except ValueError:
-            raise TypeError(f"max-age: {morsel['max-age']} must be integer")
-    elif morsel["expires"]:
-        time_template = "%a, %d-%b-%Y %H:%M:%S GMT"
-        expires = calendar.timegm(time.strptime(morsel["expires"], time_template))
-    return create_cookie(
-        comment=morsel["comment"],
-        comment_url=bool(morsel["comment"]),
-        discard=False,
-        domain=morsel["domain"],
-        expires=expires,
-        name=morsel.key,
-        path=morsel["path"],
-        port=None,
-        rest={"HttpOnly": morsel["httponly"]},
-        rfc2109=False,
-        secure=bool(morsel["secure"]),
-        value=morsel.value,
-        version=morsel["version"] or 0,
-    )
-
-
-def cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True):
-    """Returns a CookieJar from a key/value dictionary.
-
-    :param cookie_dict: Dict of key/values to insert into CookieJar.
-    :param cookiejar: (optional) A cookiejar to add the cookies to.
-    :param overwrite: (optional) If False, will not replace cookies
-        already in the jar with new ones.
-    :rtype: CookieJar
-    """
-    if cookiejar is None:
-        cookiejar = RequestsCookieJar()
-
-    if cookie_dict is not None:
-        names_from_jar = [cookie.name for cookie in cookiejar]
-        for name in cookie_dict:
-            if overwrite or (name not in names_from_jar):
-                cookiejar.set_cookie(create_cookie(name, cookie_dict[name]))
-
-    return cookiejar
-
-
-def merge_cookies(cookiejar, cookies):
-    """Add cookies to cookiejar and returns a merged CookieJar.
-
-    :param cookiejar: CookieJar object to add the cookies to.
-    :param cookies: Dictionary or CookieJar object to be added.
-    :rtype: CookieJar
-    """
-    if not isinstance(cookiejar, cookielib.CookieJar):
-        raise ValueError("You can only merge into CookieJar")
-
-    if isinstance(cookies, dict):
-        cookiejar = cookiejar_from_dict(cookies, cookiejar=cookiejar, overwrite=False)
-    elif isinstance(cookies, cookielib.CookieJar):
-        try:
-            cookiejar.update(cookies)
-        except AttributeError:
-            for cookie_in_jar in cookies:
-                cookiejar.set_cookie(cookie_in_jar)
-
-    return cookiejar
diff --git a/apps/bitwarden_event_logs/lib/requests/exceptions.py b/apps/bitwarden_event_logs/lib/requests/exceptions.py
deleted file mode 100755
index 83986b48..00000000
--- a/apps/bitwarden_event_logs/lib/requests/exceptions.py
+++ /dev/null
@@ -1,151 +0,0 @@
-"""
-requests.exceptions
-~~~~~~~~~~~~~~~~~~~
-
-This module contains the set of Requests' exceptions.
-"""
-from urllib3.exceptions import HTTPError as BaseHTTPError
-
-from .compat import JSONDecodeError as CompatJSONDecodeError
-
-
-class RequestException(IOError):
-    """There was an ambiguous exception that occurred while handling your
-    request.
-    """
-
-    def __init__(self, *args, **kwargs):
-        """Initialize RequestException with `request` and `response` objects."""
-        response = kwargs.pop("response", None)
-        self.response = response
-        self.request = kwargs.pop("request", None)
-        if response is not None and not self.request and hasattr(response, "request"):
-            self.request = self.response.request
-        super().__init__(*args, **kwargs)
-
-
-class InvalidJSONError(RequestException):
-    """A JSON error occurred."""
-
-
-class JSONDecodeError(InvalidJSONError, CompatJSONDecodeError):
-    """Couldn't decode the text into json"""
-
-    def __init__(self, *args, **kwargs):
-        """
-        Construct the JSONDecodeError instance first with all
-        args. Then use it's args to construct the IOError so that
-        the json specific args aren't used as IOError specific args
-        and the error message from JSONDecodeError is preserved.
-        """
-        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."""
-
-
-class ConnectionError(RequestException):
-    """A Connection error occurred."""
-
-
-class ProxyError(ConnectionError):
-    """A proxy error occurred."""
-
-
-class SSLError(ConnectionError):
-    """An SSL error occurred."""
-
-
-class Timeout(RequestException):
-    """The request timed out.
-
-    Catching this error will catch both
-    :exc:`~requests.exceptions.ConnectTimeout` and
-    :exc:`~requests.exceptions.ReadTimeout` errors.
-    """
-
-
-class ConnectTimeout(ConnectionError, Timeout):
-    """The request timed out while trying to connect to the remote server.
-
-    Requests that produced this error are safe to retry.
-    """
-
-
-class ReadTimeout(Timeout):
-    """The server did not send any data in the allotted amount of time."""
-
-
-class URLRequired(RequestException):
-    """A valid URL is required to make a request."""
-
-
-class TooManyRedirects(RequestException):
-    """Too many redirects."""
-
-
-class MissingSchema(RequestException, ValueError):
-    """The URL scheme (e.g. http or https) is missing."""
-
-
-class InvalidSchema(RequestException, ValueError):
-    """The URL scheme provided is either invalid or unsupported."""
-
-
-class InvalidURL(RequestException, ValueError):
-    """The URL provided was somehow invalid."""
-
-
-class InvalidHeader(RequestException, ValueError):
-    """The header value provided was somehow invalid."""
-
-
-class InvalidProxyURL(InvalidURL):
-    """The proxy URL provided is invalid."""
-
-
-class ChunkedEncodingError(RequestException):
-    """The server declared chunked encoding but sent an invalid chunk."""
-
-
-class ContentDecodingError(RequestException, BaseHTTPError):
-    """Failed to decode response content."""
-
-
-class StreamConsumedError(RequestException, TypeError):
-    """The content for this response was already consumed."""
-
-
-class RetryError(RequestException):
-    """Custom retries logic failed"""
-
-
-class UnrewindableBodyError(RequestException):
-    """Requests encountered an error when trying to rewind a body."""
-
-
-# Warnings
-
-
-class RequestsWarning(Warning):
-    """Base warning for Requests."""
-
-
-class FileModeWarning(RequestsWarning, DeprecationWarning):
-    """A file was opened in text mode, but Requests determined its binary length."""
-
-
-class RequestsDependencyWarning(RequestsWarning):
-    """An imported dependency doesn't match the expected version range."""
diff --git a/apps/bitwarden_event_logs/lib/requests/help.py b/apps/bitwarden_event_logs/lib/requests/help.py
deleted file mode 100755
index 8fbcd656..00000000
--- a/apps/bitwarden_event_logs/lib/requests/help.py
+++ /dev/null
@@ -1,134 +0,0 @@
-"""Module containing bug report helper(s)."""
-
-import json
-import platform
-import ssl
-import sys
-
-import idna
-import urllib3
-
-from . import __version__ as requests_version
-
-try:
-    import charset_normalizer
-except ImportError:
-    charset_normalizer = None
-
-try:
-    import chardet
-except ImportError:
-    chardet = None
-
-try:
-    from urllib3.contrib import pyopenssl
-except ImportError:
-    pyopenssl = None
-    OpenSSL = None
-    cryptography = None
-else:
-    import cryptography
-    import OpenSSL
-
-
-def _implementation():
-    """Return a dict with the Python implementation and version.
-
-    Provide both the name and the version of the Python implementation
-    currently running. For example, on CPython 3.10.3 it will return
-    {'name': 'CPython', 'version': '3.10.3'}.
-
-    This function works best on CPython and PyPy: in particular, it probably
-    doesn't work for Jython or IronPython. Future investigation should be done
-    to work out the correct shape of the code for those platforms.
-    """
-    implementation = platform.python_implementation()
-
-    if implementation == "CPython":
-        implementation_version = platform.python_version()
-    elif implementation == "PyPy":
-        implementation_version = "{}.{}.{}".format(
-            sys.pypy_version_info.major,
-            sys.pypy_version_info.minor,
-            sys.pypy_version_info.micro,
-        )
-        if sys.pypy_version_info.releaselevel != "final":
-            implementation_version = "".join(
-                [implementation_version, sys.pypy_version_info.releaselevel]
-            )
-    elif implementation == "Jython":
-        implementation_version = platform.python_version()  # Complete Guess
-    elif implementation == "IronPython":
-        implementation_version = platform.python_version()  # Complete Guess
-    else:
-        implementation_version = "Unknown"
-
-    return {"name": implementation, "version": implementation_version}
-
-
-def info():
-    """Generate information for a bug report."""
-    try:
-        platform_info = {
-            "system": platform.system(),
-            "release": platform.release(),
-        }
-    except OSError:
-        platform_info = {
-            "system": "Unknown",
-            "release": "Unknown",
-        }
-
-    implementation_info = _implementation()
-    urllib3_info = {"version": urllib3.__version__}
-    charset_normalizer_info = {"version": None}
-    chardet_info = {"version": None}
-    if charset_normalizer:
-        charset_normalizer_info = {"version": charset_normalizer.__version__}
-    if chardet:
-        chardet_info = {"version": chardet.__version__}
-
-    pyopenssl_info = {
-        "version": None,
-        "openssl_version": "",
-    }
-    if OpenSSL:
-        pyopenssl_info = {
-            "version": OpenSSL.__version__,
-            "openssl_version": f"{OpenSSL.SSL.OPENSSL_VERSION_NUMBER:x}",
-        }
-    cryptography_info = {
-        "version": getattr(cryptography, "__version__", ""),
-    }
-    idna_info = {
-        "version": getattr(idna, "__version__", ""),
-    }
-
-    system_ssl = ssl.OPENSSL_VERSION_NUMBER
-    system_ssl_info = {"version": f"{system_ssl:x}" if system_ssl is not None else ""}
-
-    return {
-        "platform": platform_info,
-        "implementation": implementation_info,
-        "system_ssl": system_ssl_info,
-        "using_pyopenssl": pyopenssl is not None,
-        "using_charset_normalizer": chardet is None,
-        "pyOpenSSL": pyopenssl_info,
-        "urllib3": urllib3_info,
-        "chardet": chardet_info,
-        "charset_normalizer": charset_normalizer_info,
-        "cryptography": cryptography_info,
-        "idna": idna_info,
-        "requests": {
-            "version": requests_version,
-        },
-    }
-
-
-def main():
-    """Pretty-print the bug information as JSON."""
-    print(json.dumps(info(), sort_keys=True, indent=2))
-
-
-if __name__ == "__main__":
-    main()
diff --git a/apps/bitwarden_event_logs/lib/requests/hooks.py b/apps/bitwarden_event_logs/lib/requests/hooks.py
deleted file mode 100755
index d181ba2e..00000000
--- a/apps/bitwarden_event_logs/lib/requests/hooks.py
+++ /dev/null
@@ -1,33 +0,0 @@
-"""
-requests.hooks
-~~~~~~~~~~~~~~
-
-This module provides the capabilities for the Requests hooks system.
-
-Available hooks:
-
-``response``:
-    The response generated from a Request.
-"""
-HOOKS = ["response"]
-
-
-def default_hooks():
-    return {event: [] for event in HOOKS}
-
-
-# TODO: response is the only one
-
-
-def dispatch_hook(key, hooks, hook_data, **kwargs):
-    """Dispatches a hook dictionary on a given piece of data."""
-    hooks = hooks or {}
-    hooks = hooks.get(key)
-    if hooks:
-        if hasattr(hooks, "__call__"):
-            hooks = [hooks]
-        for hook in hooks:
-            _hook_data = hook(hook_data, **kwargs)
-            if _hook_data is not None:
-                hook_data = _hook_data
-    return hook_data
diff --git a/apps/bitwarden_event_logs/lib/requests/models.py b/apps/bitwarden_event_logs/lib/requests/models.py
deleted file mode 100755
index c4b25fa0..00000000
--- a/apps/bitwarden_event_logs/lib/requests/models.py
+++ /dev/null
@@ -1,1039 +0,0 @@
-"""
-requests.models
-~~~~~~~~~~~~~~~
-
-This module contains the primary objects that power Requests.
-"""
-
-import datetime
-
-# Import encoding now, to avoid implicit import later.
-# Implicit import within threads may cause LookupError when standard library is in a ZIP,
-# such as in Embedded Python. See https://github.com/psf/requests/issues/3578.
-import encodings.idna  # noqa: F401
-from io import UnsupportedOperation
-
-from urllib3.exceptions import (
-    DecodeError,
-    LocationParseError,
-    ProtocolError,
-    ReadTimeoutError,
-    SSLError,
-)
-from urllib3.fields import RequestField
-from urllib3.filepost import encode_multipart_formdata
-from urllib3.util import parse_url
-
-from ._internal_utils import to_native_string, unicode_is_ascii
-from .auth import HTTPBasicAuth
-from .compat import (
-    Callable,
-    JSONDecodeError,
-    Mapping,
-    basestring,
-    builtin_str,
-    chardet,
-    cookielib,
-)
-from .compat import json as complexjson
-from .compat import urlencode, urlsplit, urlunparse
-from .cookies import _copy_cookie_jar, cookiejar_from_dict, get_cookie_header
-from .exceptions import (
-    ChunkedEncodingError,
-    ConnectionError,
-    ContentDecodingError,
-    HTTPError,
-    InvalidJSONError,
-    InvalidURL,
-)
-from .exceptions import JSONDecodeError as RequestsJSONDecodeError
-from .exceptions import MissingSchema
-from .exceptions import SSLError as RequestsSSLError
-from .exceptions import StreamConsumedError
-from .hooks import default_hooks
-from .status_codes import codes
-from .structures import CaseInsensitiveDict
-from .utils import (
-    check_header_validity,
-    get_auth_from_url,
-    guess_filename,
-    guess_json_utf,
-    iter_slices,
-    parse_header_links,
-    requote_uri,
-    stream_decode_response_unicode,
-    super_len,
-    to_key_val_list,
-)
-
-#: The set of HTTP status codes that indicate an automatically
-#: processable redirect.
-REDIRECT_STATI = (
-    codes.moved,  # 301
-    codes.found,  # 302
-    codes.other,  # 303
-    codes.temporary_redirect,  # 307
-    codes.permanent_redirect,  # 308
-)
-
-DEFAULT_REDIRECT_LIMIT = 30
-CONTENT_CHUNK_SIZE = 10 * 1024
-ITER_CHUNK_SIZE = 512
-
-
-class RequestEncodingMixin:
-    @property
-    def path_url(self):
-        """Build the path URL to use."""
-
-        url = []
-
-        p = urlsplit(self.url)
-
-        path = p.path
-        if not path:
-            path = "/"
-
-        url.append(path)
-
-        query = p.query
-        if query:
-            url.append("?")
-            url.append(query)
-
-        return "".join(url)
-
-    @staticmethod
-    def _encode_params(data):
-        """Encode parameters in a piece of data.
-
-        Will successfully encode parameters when passed as a dict or a list of
-        2-tuples. Order is retained if data is a list of 2-tuples but arbitrary
-        if parameters are supplied as a dict.
-        """
-
-        if isinstance(data, (str, bytes)):
-            return data
-        elif hasattr(data, "read"):
-            return data
-        elif hasattr(data, "__iter__"):
-            result = []
-            for k, vs in to_key_val_list(data):
-                if isinstance(vs, basestring) or not hasattr(vs, "__iter__"):
-                    vs = [vs]
-                for v in vs:
-                    if v is not None:
-                        result.append(
-                            (
-                                k.encode("utf-8") if isinstance(k, str) else k,
-                                v.encode("utf-8") if isinstance(v, str) else v,
-                            )
-                        )
-            return urlencode(result, doseq=True)
-        else:
-            return data
-
-    @staticmethod
-    def _encode_files(files, data):
-        """Build the body for a multipart/form-data request.
-
-        Will successfully encode files when passed as a dict or a list of
-        tuples. Order is retained if data is a list of tuples but arbitrary
-        if parameters are supplied as a dict.
-        The tuples may be 2-tuples (filename, fileobj), 3-tuples (filename, fileobj, contentype)
-        or 4-tuples (filename, fileobj, contentype, custom_headers).
-        """
-        if not files:
-            raise ValueError("Files must be provided.")
-        elif isinstance(data, basestring):
-            raise ValueError("Data must not be a string.")
-
-        new_fields = []
-        fields = to_key_val_list(data or {})
-        files = to_key_val_list(files or {})
-
-        for field, val in fields:
-            if isinstance(val, basestring) or not hasattr(val, "__iter__"):
-                val = [val]
-            for v in val:
-                if v is not None:
-                    # Don't call str() on bytestrings: in Py3 it all goes wrong.
-                    if not isinstance(v, bytes):
-                        v = str(v)
-
-                    new_fields.append(
-                        (
-                            field.decode("utf-8")
-                            if isinstance(field, bytes)
-                            else field,
-                            v.encode("utf-8") if isinstance(v, str) else v,
-                        )
-                    )
-
-        for k, v in files:
-            # support for explicit filename
-            ft = None
-            fh = None
-            if isinstance(v, (tuple, list)):
-                if len(v) == 2:
-                    fn, fp = v
-                elif len(v) == 3:
-                    fn, fp, ft = v
-                else:
-                    fn, fp, ft, fh = v
-            else:
-                fn = guess_filename(v) or k
-                fp = v
-
-            if isinstance(fp, (str, bytes, bytearray)):
-                fdata = fp
-            elif hasattr(fp, "read"):
-                fdata = fp.read()
-            elif fp is None:
-                continue
-            else:
-                fdata = fp
-
-            rf = RequestField(name=k, data=fdata, filename=fn, headers=fh)
-            rf.make_multipart(content_type=ft)
-            new_fields.append(rf)
-
-        body, content_type = encode_multipart_formdata(new_fields)
-
-        return body, content_type
-
-
-class RequestHooksMixin:
-    def register_hook(self, event, hook):
-        """Properly register a hook."""
-
-        if event not in self.hooks:
-            raise ValueError(f'Unsupported event specified, with event name "{event}"')
-
-        if isinstance(hook, Callable):
-            self.hooks[event].append(hook)
-        elif hasattr(hook, "__iter__"):
-            self.hooks[event].extend(h for h in hook if isinstance(h, Callable))
-
-    def deregister_hook(self, event, hook):
-        """Deregister a previously registered hook.
-        Returns True if the hook existed, False if not.
-        """
-
-        try:
-            self.hooks[event].remove(hook)
-            return True
-        except ValueError:
-            return False
-
-
-class Request(RequestHooksMixin):
-    """A user-created :class:`Request ` object.
-
-    Used to prepare a :class:`PreparedRequest `, which is sent to the server.
-
-    :param method: HTTP method to use.
-    :param url: URL to send.
-    :param headers: dictionary of headers to send.
-    :param files: dictionary of {filename: fileobject} files to multipart upload.
-    :param data: the body to attach to the request. If a dictionary or
-        list of tuples ``[(key, value)]`` is provided, form-encoding will
-        take place.
-    :param json: json for the body to attach to the request (if files or data is not specified).
-    :param params: URL parameters to append to the URL. If a dictionary or
-        list of tuples ``[(key, value)]`` is provided, form-encoding will
-        take place.
-    :param auth: Auth handler or (user, pass) tuple.
-    :param cookies: dictionary or CookieJar of cookies to attach to this request.
-    :param hooks: dictionary of callback hooks, for internal usage.
-
-    Usage::
-
-      >>> import requests
-      >>> req = requests.Request('GET', 'https://httpbin.org/get')
-      >>> req.prepare()
-      
-    """
-
-    def __init__(
-        self,
-        method=None,
-        url=None,
-        headers=None,
-        files=None,
-        data=None,
-        params=None,
-        auth=None,
-        cookies=None,
-        hooks=None,
-        json=None,
-    ):
-        # Default empty dicts for dict params.
-        data = [] if data is None else data
-        files = [] if files is None else files
-        headers = {} if headers is None else headers
-        params = {} if params is None else params
-        hooks = {} if hooks is None else hooks
-
-        self.hooks = default_hooks()
-        for k, v in list(hooks.items()):
-            self.register_hook(event=k, hook=v)
-
-        self.method = method
-        self.url = url
-        self.headers = headers
-        self.files = files
-        self.data = data
-        self.json = json
-        self.params = params
-        self.auth = auth
-        self.cookies = cookies
-
-    def __repr__(self):
-        return f""
-
-    def prepare(self):
-        """Constructs a :class:`PreparedRequest ` for transmission and returns it."""
-        p = PreparedRequest()
-        p.prepare(
-            method=self.method,
-            url=self.url,
-            headers=self.headers,
-            files=self.files,
-            data=self.data,
-            json=self.json,
-            params=self.params,
-            auth=self.auth,
-            cookies=self.cookies,
-            hooks=self.hooks,
-        )
-        return p
-
-
-class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
-    """The fully mutable :class:`PreparedRequest ` object,
-    containing the exact bytes that will be sent to the server.
-
-    Instances are generated from a :class:`Request ` object, and
-    should not be instantiated manually; doing so may produce undesirable
-    effects.
-
-    Usage::
-
-      >>> import requests
-      >>> req = requests.Request('GET', 'https://httpbin.org/get')
-      >>> r = req.prepare()
-      >>> r
-      
-
-      >>> s = requests.Session()
-      >>> s.send(r)
-      
-    """
-
-    def __init__(self):
-        #: HTTP verb to send to the server.
-        self.method = None
-        #: HTTP URL to send the request to.
-        self.url = None
-        #: dictionary of HTTP headers.
-        self.headers = None
-        # The `CookieJar` used to create the Cookie header will be stored here
-        # after prepare_cookies is called
-        self._cookies = None
-        #: request body to send to the server.
-        self.body = None
-        #: dictionary of callback hooks, for internal usage.
-        self.hooks = default_hooks()
-        #: integer denoting starting position of a readable file-like body.
-        self._body_position = None
-
-    def prepare(
-        self,
-        method=None,
-        url=None,
-        headers=None,
-        files=None,
-        data=None,
-        params=None,
-        auth=None,
-        cookies=None,
-        hooks=None,
-        json=None,
-    ):
-        """Prepares the entire request with the given parameters."""
-
-        self.prepare_method(method)
-        self.prepare_url(url, params)
-        self.prepare_headers(headers)
-        self.prepare_cookies(cookies)
-        self.prepare_body(data, files, json)
-        self.prepare_auth(auth, url)
-
-        # Note that prepare_auth must be last to enable authentication schemes
-        # such as OAuth to work on a fully prepared request.
-
-        # This MUST go after prepare_auth. Authenticators could add a hook
-        self.prepare_hooks(hooks)
-
-    def __repr__(self):
-        return f""
-
-    def copy(self):
-        p = PreparedRequest()
-        p.method = self.method
-        p.url = self.url
-        p.headers = self.headers.copy() if self.headers is not None else None
-        p._cookies = _copy_cookie_jar(self._cookies)
-        p.body = self.body
-        p.hooks = self.hooks
-        p._body_position = self._body_position
-        return p
-
-    def prepare_method(self, method):
-        """Prepares the given HTTP method."""
-        self.method = method
-        if self.method is not None:
-            self.method = to_native_string(self.method.upper())
-
-    @staticmethod
-    def _get_idna_encoded_host(host):
-        import idna
-
-        try:
-            host = idna.encode(host, uts46=True).decode("utf-8")
-        except idna.IDNAError:
-            raise UnicodeError
-        return host
-
-    def prepare_url(self, url, params):
-        """Prepares the given HTTP URL."""
-        #: Accept objects that have string representations.
-        #: We're unable to blindly call unicode/str functions
-        #: as this will include the bytestring indicator (b'')
-        #: on python 3.x.
-        #: https://github.com/psf/requests/pull/2238
-        if isinstance(url, bytes):
-            url = url.decode("utf8")
-        else:
-            url = str(url)
-
-        # Remove leading whitespaces from url
-        url = url.lstrip()
-
-        # Don't do any URL preparation for non-HTTP schemes like `mailto`,
-        # `data` etc to work around exceptions from `url_parse`, which
-        # handles RFC 3986 only.
-        if ":" in url and not url.lower().startswith("http"):
-            self.url = url
-            return
-
-        # Support for unicode domain names and paths.
-        try:
-            scheme, auth, host, port, path, query, fragment = parse_url(url)
-        except LocationParseError as e:
-            raise InvalidURL(*e.args)
-
-        if not scheme:
-            raise MissingSchema(
-                f"Invalid URL {url!r}: No scheme supplied. "
-                f"Perhaps you meant https://{url}?"
-            )
-
-        if not host:
-            raise InvalidURL(f"Invalid URL {url!r}: No host supplied")
-
-        # In general, we want to try IDNA encoding the hostname if the string contains
-        # non-ASCII characters. This allows users to automatically get the correct IDNA
-        # behaviour. For strings containing only ASCII characters, we need to also verify
-        # it doesn't start with a wildcard (*), before allowing the unencoded hostname.
-        if not unicode_is_ascii(host):
-            try:
-                host = self._get_idna_encoded_host(host)
-            except UnicodeError:
-                raise InvalidURL("URL has an invalid label.")
-        elif host.startswith(("*", ".")):
-            raise InvalidURL("URL has an invalid label.")
-
-        # Carefully reconstruct the network location
-        netloc = auth or ""
-        if netloc:
-            netloc += "@"
-        netloc += host
-        if port:
-            netloc += f":{port}"
-
-        # Bare domains aren't valid URLs.
-        if not path:
-            path = "/"
-
-        if isinstance(params, (str, bytes)):
-            params = to_native_string(params)
-
-        enc_params = self._encode_params(params)
-        if enc_params:
-            if query:
-                query = f"{query}&{enc_params}"
-            else:
-                query = enc_params
-
-        url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragment]))
-        self.url = url
-
-    def prepare_headers(self, headers):
-        """Prepares the given HTTP headers."""
-
-        self.headers = CaseInsensitiveDict()
-        if headers:
-            for header in headers.items():
-                # Raise exception on invalid header value.
-                check_header_validity(header)
-                name, value = header
-                self.headers[to_native_string(name)] = value
-
-    def prepare_body(self, data, files, json=None):
-        """Prepares the given HTTP body data."""
-
-        # Check if file, fo, generator, iterator.
-        # If not, run through normal process.
-
-        # Nottin' on you.
-        body = None
-        content_type = None
-
-        if not data and json is not None:
-            # urllib3 requires a bytes-like body. Python 2's json.dumps
-            # provides this natively, but Python 3 gives a Unicode string.
-            content_type = "application/json"
-
-            try:
-                body = complexjson.dumps(json, allow_nan=False)
-            except ValueError as ve:
-                raise InvalidJSONError(ve, request=self)
-
-            if not isinstance(body, bytes):
-                body = body.encode("utf-8")
-
-        is_stream = all(
-            [
-                hasattr(data, "__iter__"),
-                not isinstance(data, (basestring, list, tuple, Mapping)),
-            ]
-        )
-
-        if is_stream:
-            try:
-                length = super_len(data)
-            except (TypeError, AttributeError, UnsupportedOperation):
-                length = None
-
-            body = data
-
-            if getattr(body, "tell", None) is not None:
-                # Record the current file position before reading.
-                # This will allow us to rewind a file in the event
-                # of a redirect.
-                try:
-                    self._body_position = body.tell()
-                except OSError:
-                    # This differentiates from None, allowing us to catch
-                    # a failed `tell()` later when trying to rewind the body
-                    self._body_position = object()
-
-            if files:
-                raise NotImplementedError(
-                    "Streamed bodies and files are mutually exclusive."
-                )
-
-            if length:
-                self.headers["Content-Length"] = builtin_str(length)
-            else:
-                self.headers["Transfer-Encoding"] = "chunked"
-        else:
-            # Multi-part file uploads.
-            if files:
-                (body, content_type) = self._encode_files(files, data)
-            else:
-                if data:
-                    body = self._encode_params(data)
-                    if isinstance(data, basestring) or hasattr(data, "read"):
-                        content_type = None
-                    else:
-                        content_type = "application/x-www-form-urlencoded"
-
-            self.prepare_content_length(body)
-
-            # Add content-type if it wasn't explicitly provided.
-            if content_type and ("content-type" not in self.headers):
-                self.headers["Content-Type"] = content_type
-
-        self.body = body
-
-    def prepare_content_length(self, body):
-        """Prepare Content-Length header based on request method and body"""
-        if body is not None:
-            length = super_len(body)
-            if length:
-                # If length exists, set it. Otherwise, we fallback
-                # to Transfer-Encoding: chunked.
-                self.headers["Content-Length"] = builtin_str(length)
-        elif (
-            self.method not in ("GET", "HEAD")
-            and self.headers.get("Content-Length") is None
-        ):
-            # Set Content-Length to 0 for methods that can have a body
-            # but don't provide one. (i.e. not GET or HEAD)
-            self.headers["Content-Length"] = "0"
-
-    def prepare_auth(self, auth, url=""):
-        """Prepares the given HTTP auth data."""
-
-        # If no Auth is explicitly provided, extract it from the URL first.
-        if auth is None:
-            url_auth = get_auth_from_url(self.url)
-            auth = url_auth if any(url_auth) else None
-
-        if auth:
-            if isinstance(auth, tuple) and len(auth) == 2:
-                # special-case basic HTTP auth
-                auth = HTTPBasicAuth(*auth)
-
-            # Allow auth to make its changes.
-            r = auth(self)
-
-            # Update self to reflect the auth changes.
-            self.__dict__.update(r.__dict__)
-
-            # Recompute Content-Length
-            self.prepare_content_length(self.body)
-
-    def prepare_cookies(self, cookies):
-        """Prepares the given HTTP cookie data.
-
-        This function eventually generates a ``Cookie`` header from the
-        given cookies using cookielib. Due to cookielib's design, the header
-        will not be regenerated if it already exists, meaning this function
-        can only be called once for the life of the
-        :class:`PreparedRequest ` object. Any subsequent calls
-        to ``prepare_cookies`` will have no actual effect, unless the "Cookie"
-        header is removed beforehand.
-        """
-        if isinstance(cookies, cookielib.CookieJar):
-            self._cookies = cookies
-        else:
-            self._cookies = cookiejar_from_dict(cookies)
-
-        cookie_header = get_cookie_header(self._cookies, self)
-        if cookie_header is not None:
-            self.headers["Cookie"] = cookie_header
-
-    def prepare_hooks(self, hooks):
-        """Prepares the given hooks."""
-        # hooks can be passed as None to the prepare method and to this
-        # method. To prevent iterating over None, simply use an empty list
-        # if hooks is False-y
-        hooks = hooks or []
-        for event in hooks:
-            self.register_hook(event, hooks[event])
-
-
-class Response:
-    """The :class:`Response ` object, which contains a
-    server's response to an HTTP request.
-    """
-
-    __attrs__ = [
-        "_content",
-        "status_code",
-        "headers",
-        "url",
-        "history",
-        "encoding",
-        "reason",
-        "cookies",
-        "elapsed",
-        "request",
-    ]
-
-    def __init__(self):
-        self._content = False
-        self._content_consumed = False
-        self._next = None
-
-        #: Integer Code of responded HTTP Status, e.g. 404 or 200.
-        self.status_code = None
-
-        #: Case-insensitive Dictionary of Response Headers.
-        #: For example, ``headers['content-encoding']`` will return the
-        #: value of a ``'Content-Encoding'`` response header.
-        self.headers = CaseInsensitiveDict()
-
-        #: File-like object representation of response (for advanced usage).
-        #: Use of ``raw`` requires that ``stream=True`` be set on the request.
-        #: This requirement does not apply for use internally to Requests.
-        self.raw = None
-
-        #: Final URL location of Response.
-        self.url = None
-
-        #: Encoding to decode with when accessing r.text.
-        self.encoding = None
-
-        #: A list of :class:`Response ` objects from
-        #: the history of the Request. Any redirect responses will end
-        #: up here. The list is sorted from the oldest to the most recent request.
-        self.history = []
-
-        #: Textual reason of responded HTTP Status, e.g. "Not Found" or "OK".
-        self.reason = None
-
-        #: A CookieJar of Cookies the server sent back.
-        self.cookies = cookiejar_from_dict({})
-
-        #: The amount of time elapsed between sending the request
-        #: and the arrival of the response (as a timedelta).
-        #: This property specifically measures the time taken between sending
-        #: the first byte of the request and finishing parsing the headers. It
-        #: is therefore unaffected by consuming the response content or the
-        #: value of the ``stream`` keyword argument.
-        self.elapsed = datetime.timedelta(0)
-
-        #: The :class:`PreparedRequest ` object to which this
-        #: is a response.
-        self.request = None
-
-    def __enter__(self):
-        return self
-
-    def __exit__(self, *args):
-        self.close()
-
-    def __getstate__(self):
-        # Consume everything; accessing the content attribute makes
-        # sure the content has been fully read.
-        if not self._content_consumed:
-            self.content
-
-        return {attr: getattr(self, attr, None) for attr in self.__attrs__}
-
-    def __setstate__(self, state):
-        for name, value in state.items():
-            setattr(self, name, value)
-
-        # pickled objects do not have .raw
-        setattr(self, "_content_consumed", True)
-        setattr(self, "raw", None)
-
-    def __repr__(self):
-        return f""
-
-    def __bool__(self):
-        """Returns True if :attr:`status_code` is less than 400.
-
-        This attribute checks if the status code of the response is between
-        400 and 600 to see if there was a client error or a server error. If
-        the status code, is between 200 and 400, this will return True. This
-        is **not** a check to see if the response code is ``200 OK``.
-        """
-        return self.ok
-
-    def __nonzero__(self):
-        """Returns True if :attr:`status_code` is less than 400.
-
-        This attribute checks if the status code of the response is between
-        400 and 600 to see if there was a client error or a server error. If
-        the status code, is between 200 and 400, this will return True. This
-        is **not** a check to see if the response code is ``200 OK``.
-        """
-        return self.ok
-
-    def __iter__(self):
-        """Allows you to use a response as an iterator."""
-        return self.iter_content(128)
-
-    @property
-    def ok(self):
-        """Returns True if :attr:`status_code` is less than 400, False if not.
-
-        This attribute checks if the status code of the response is between
-        400 and 600 to see if there was a client error or a server error. If
-        the status code is between 200 and 400, this will return True. This
-        is **not** a check to see if the response code is ``200 OK``.
-        """
-        try:
-            self.raise_for_status()
-        except HTTPError:
-            return False
-        return True
-
-    @property
-    def is_redirect(self):
-        """True if this Response is a well-formed HTTP redirect that could have
-        been processed automatically (by :meth:`Session.resolve_redirects`).
-        """
-        return "location" in self.headers and self.status_code in REDIRECT_STATI
-
-    @property
-    def is_permanent_redirect(self):
-        """True if this Response one of the permanent versions of redirect."""
-        return "location" in self.headers and self.status_code in (
-            codes.moved_permanently,
-            codes.permanent_redirect,
-        )
-
-    @property
-    def next(self):
-        """Returns a PreparedRequest for the next request in a redirect chain, if there is one."""
-        return self._next
-
-    @property
-    def apparent_encoding(self):
-        """The apparent encoding, provided by the charset_normalizer or chardet libraries."""
-        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
-        request, this avoids reading the content at once into memory for
-        large responses.  The chunk size is the number of bytes it should
-        read into memory.  This is not necessarily the length of each item
-        returned as decoding can take place.
-
-        chunk_size must be of type int or None. A value of None will
-        function differently depending on the value of `stream`.
-        stream=True will read data as it arrives in whatever size the
-        chunks are received. If stream=False, data is returned as
-        a single chunk.
-
-        If decode_unicode is True, content will be decoded using the best
-        available encoding based on the response.
-        """
-
-        def generate():
-            # Special case for urllib3.
-            if hasattr(self.raw, "stream"):
-                try:
-                    yield from self.raw.stream(chunk_size, decode_content=True)
-                except ProtocolError as e:
-                    raise ChunkedEncodingError(e)
-                except DecodeError as e:
-                    raise ContentDecodingError(e)
-                except ReadTimeoutError as e:
-                    raise ConnectionError(e)
-                except SSLError as e:
-                    raise RequestsSSLError(e)
-            else:
-                # Standard file-like object.
-                while True:
-                    chunk = self.raw.read(chunk_size)
-                    if not chunk:
-                        break
-                    yield chunk
-
-            self._content_consumed = True
-
-        if self._content_consumed and isinstance(self._content, bool):
-            raise StreamConsumedError()
-        elif chunk_size is not None and not isinstance(chunk_size, int):
-            raise TypeError(
-                f"chunk_size must be an int, it is instead a {type(chunk_size)}."
-            )
-        # simulate reading small chunks of the content
-        reused_chunks = iter_slices(self._content, chunk_size)
-
-        stream_chunks = generate()
-
-        chunks = reused_chunks if self._content_consumed else stream_chunks
-
-        if decode_unicode:
-            chunks = stream_decode_response_unicode(chunks, self)
-
-        return chunks
-
-    def iter_lines(
-        self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=False, delimiter=None
-    ):
-        """Iterates over the response data, one line at a time.  When
-        stream=True is set on the request, this avoids reading the
-        content at once into memory for large responses.
-
-        .. note:: This method is not reentrant safe.
-        """
-
-        pending = None
-
-        for chunk in self.iter_content(
-            chunk_size=chunk_size, decode_unicode=decode_unicode
-        ):
-            if pending is not None:
-                chunk = pending + chunk
-
-            if delimiter:
-                lines = chunk.split(delimiter)
-            else:
-                lines = chunk.splitlines()
-
-            if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]:
-                pending = lines.pop()
-            else:
-                pending = None
-
-            yield from lines
-
-        if pending is not None:
-            yield pending
-
-    @property
-    def content(self):
-        """Content of the response, in bytes."""
-
-        if self._content is False:
-            # Read the contents.
-            if self._content_consumed:
-                raise RuntimeError("The content for this response was already consumed")
-
-            if self.status_code == 0 or self.raw is None:
-                self._content = None
-            else:
-                self._content = b"".join(self.iter_content(CONTENT_CHUNK_SIZE)) or b""
-
-        self._content_consumed = True
-        # don't need to release the connection; that's been handled by urllib3
-        # since we exhausted the data.
-        return self._content
-
-    @property
-    def text(self):
-        """Content of the response, in unicode.
-
-        If Response.encoding is None, encoding will be guessed using
-        ``charset_normalizer`` or ``chardet``.
-
-        The encoding of the response content is determined based solely on HTTP
-        headers, following RFC 2616 to the letter. If you can take advantage of
-        non-HTTP knowledge to make a better guess at the encoding, you should
-        set ``r.encoding`` appropriately before accessing this property.
-        """
-
-        # Try charset from content-type
-        content = None
-        encoding = self.encoding
-
-        if not self.content:
-            return ""
-
-        # Fallback to auto-detected encoding.
-        if self.encoding is None:
-            encoding = self.apparent_encoding
-
-        # Decode unicode from given encoding.
-        try:
-            content = str(self.content, encoding, errors="replace")
-        except (LookupError, TypeError):
-            # A LookupError is raised if the encoding was not found which could
-            # indicate a misspelling or similar mistake.
-            #
-            # A TypeError can be raised if encoding is None
-            #
-            # So we try blindly encoding.
-            content = str(self.content, errors="replace")
-
-        return content
-
-    def json(self, **kwargs):
-        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
-            contain valid json.
-        """
-
-        if not self.encoding and self.content and len(self.content) > 3:
-            # No encoding set. JSON RFC 4627 section 3 states we should expect
-            # UTF-8, -16 or -32. Detect which one to use; If the detection or
-            # decoding fails, fall back to `self.text` (using charset_normalizer to make
-            # a best guess).
-            encoding = guess_json_utf(self.content)
-            if encoding is not None:
-                try:
-                    return complexjson.loads(self.content.decode(encoding), **kwargs)
-                except UnicodeDecodeError:
-                    # Wrong UTF codec detected; usually because it's not UTF-8
-                    # but some other 8-bit codec.  This is an RFC violation,
-                    # and the server didn't bother to tell us what codec *was*
-                    # used.
-                    pass
-                except JSONDecodeError as e:
-                    raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)
-
-        try:
-            return complexjson.loads(self.text, **kwargs)
-        except JSONDecodeError as e:
-            # Catch JSON-related errors and raise as requests.JSONDecodeError
-            # This aliases json.JSONDecodeError and simplejson.JSONDecodeError
-            raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)
-
-    @property
-    def links(self):
-        """Returns the parsed header links of the response, if any."""
-
-        header = self.headers.get("link")
-
-        resolved_links = {}
-
-        if header:
-            links = parse_header_links(header)
-
-            for link in links:
-                key = link.get("rel") or link.get("url")
-                resolved_links[key] = link
-
-        return resolved_links
-
-    def raise_for_status(self):
-        """Raises :class:`HTTPError`, if one occurred."""
-
-        http_error_msg = ""
-        if isinstance(self.reason, bytes):
-            # We attempt to decode utf-8 first because some servers
-            # choose to localize their reason strings. If the string
-            # isn't utf-8, we fall back to iso-8859-1 for all other
-            # encodings. (See PR #3538)
-            try:
-                reason = self.reason.decode("utf-8")
-            except UnicodeDecodeError:
-                reason = self.reason.decode("iso-8859-1")
-        else:
-            reason = self.reason
-
-        if 400 <= self.status_code < 500:
-            http_error_msg = (
-                f"{self.status_code} Client Error: {reason} for url: {self.url}"
-            )
-
-        elif 500 <= self.status_code < 600:
-            http_error_msg = (
-                f"{self.status_code} Server Error: {reason} for url: {self.url}"
-            )
-
-        if http_error_msg:
-            raise HTTPError(http_error_msg, response=self)
-
-    def close(self):
-        """Releases the connection back to the pool. Once this method has been
-        called the underlying ``raw`` object must not be accessed again.
-
-        *Note: Should not normally need to be called explicitly.*
-        """
-        if not self._content_consumed:
-            self.raw.close()
-
-        release_conn = getattr(self.raw, "release_conn", None)
-        if release_conn is not None:
-            release_conn()
diff --git a/apps/bitwarden_event_logs/lib/requests/packages.py b/apps/bitwarden_event_logs/lib/requests/packages.py
deleted file mode 100755
index 5ab3d8e2..00000000
--- a/apps/bitwarden_event_logs/lib/requests/packages.py
+++ /dev/null
@@ -1,23 +0,0 @@
-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"):
-    locals()[package] = __import__(package)
-    # This traversal is apparently necessary such that the identities are
-    # preserved (requests.packages.urllib3.* is urllib3.*)
-    for mod in list(sys.modules):
-        if mod == package or mod.startswith(f"{package}."):
-            sys.modules[f"requests.packages.{mod}"] = sys.modules[mod]
-
-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/apps/bitwarden_event_logs/lib/requests/sessions.py b/apps/bitwarden_event_logs/lib/requests/sessions.py
deleted file mode 100755
index 731550de..00000000
--- a/apps/bitwarden_event_logs/lib/requests/sessions.py
+++ /dev/null
@@ -1,831 +0,0 @@
-"""
-requests.sessions
-~~~~~~~~~~~~~~~~~
-
-This module provides a Session object to manage and persist settings across
-requests (cookies, auth, proxies).
-"""
-import os
-import sys
-import time
-from collections import OrderedDict
-from datetime import timedelta
-
-from ._internal_utils import to_native_string
-from .adapters import HTTPAdapter
-from .auth import _basic_auth_str
-from .compat import Mapping, cookielib, urljoin, urlparse
-from .cookies import (
-    RequestsCookieJar,
-    cookiejar_from_dict,
-    extract_cookies_to_jar,
-    merge_cookies,
-)
-from .exceptions import (
-    ChunkedEncodingError,
-    ContentDecodingError,
-    InvalidSchema,
-    TooManyRedirects,
-)
-from .hooks import default_hooks, dispatch_hook
-
-# formerly defined here, reexposed here for backward compatibility
-from .models import (  # noqa: F401
-    DEFAULT_REDIRECT_LIMIT,
-    REDIRECT_STATI,
-    PreparedRequest,
-    Request,
-)
-from .status_codes import codes
-from .structures import CaseInsensitiveDict
-from .utils import (  # noqa: F401
-    DEFAULT_PORTS,
-    default_headers,
-    get_auth_from_url,
-    get_environ_proxies,
-    get_netrc_auth,
-    requote_uri,
-    resolve_proxies,
-    rewind_body,
-    should_bypass_proxies,
-    to_key_val_list,
-)
-
-# Preferred clock, based on which one is more accurate on a given system.
-if sys.platform == "win32":
-    preferred_clock = time.perf_counter
-else:
-    preferred_clock = time.time
-
-
-def merge_setting(request_setting, session_setting, dict_class=OrderedDict):
-    """Determines appropriate setting for a given request, taking into account
-    the explicit setting on that request, and the setting in the session. If a
-    setting is a dictionary, they will be merged together using `dict_class`
-    """
-
-    if session_setting is None:
-        return request_setting
-
-    if request_setting is None:
-        return session_setting
-
-    # Bypass if not a dictionary (e.g. verify)
-    if not (
-        isinstance(session_setting, Mapping) and isinstance(request_setting, Mapping)
-    ):
-        return request_setting
-
-    merged_setting = dict_class(to_key_val_list(session_setting))
-    merged_setting.update(to_key_val_list(request_setting))
-
-    # Remove keys that are set to None. Extract keys first to avoid altering
-    # the dictionary during iteration.
-    none_keys = [k for (k, v) in merged_setting.items() if v is None]
-    for key in none_keys:
-        del merged_setting[key]
-
-    return merged_setting
-
-
-def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict):
-    """Properly merges both requests and session hooks.
-
-    This is necessary because when request_hooks == {'response': []}, the
-    merge breaks Session hooks entirely.
-    """
-    if session_hooks is None or session_hooks.get("response") == []:
-        return request_hooks
-
-    if request_hooks is None or request_hooks.get("response") == []:
-        return session_hooks
-
-    return merge_setting(request_hooks, session_hooks, dict_class)
-
-
-class SessionRedirectMixin:
-    def get_redirect_target(self, resp):
-        """Receives a Response. Returns a redirect URI or ``None``"""
-        # Due to the nature of how requests processes redirects this method will
-        # be called at least once upon the original response and at least twice
-        # on each subsequent redirect response (if any).
-        # If a custom mixin is used to handle this logic, it may be advantageous
-        # to cache the redirect location onto the response object as a private
-        # attribute.
-        if resp.is_redirect:
-            location = resp.headers["location"]
-            # Currently the underlying http module on py3 decode headers
-            # in latin1, but empirical evidence suggests that latin1 is very
-            # rarely used with non-ASCII characters in HTTP headers.
-            # It is more likely to get UTF8 header rather than latin1.
-            # This causes incorrect handling of UTF8 encoded location headers.
-            # To solve this, we re-encode the location in latin1.
-            location = location.encode("latin1")
-            return to_native_string(location, "utf8")
-        return None
-
-    def should_strip_auth(self, old_url, new_url):
-        """Decide whether Authorization header should be removed when redirecting"""
-        old_parsed = urlparse(old_url)
-        new_parsed = urlparse(new_url)
-        if old_parsed.hostname != new_parsed.hostname:
-            return True
-        # Special case: allow http -> https redirect when using the standard
-        # ports. This isn't specified by RFC 7235, but is kept to avoid
-        # breaking backwards compatibility with older versions of requests
-        # that allowed any redirects on the same host.
-        if (
-            old_parsed.scheme == "http"
-            and old_parsed.port in (80, None)
-            and new_parsed.scheme == "https"
-            and new_parsed.port in (443, None)
-        ):
-            return False
-
-        # Handle default port usage corresponding to scheme.
-        changed_port = old_parsed.port != new_parsed.port
-        changed_scheme = old_parsed.scheme != new_parsed.scheme
-        default_port = (DEFAULT_PORTS.get(old_parsed.scheme, None), None)
-        if (
-            not changed_scheme
-            and old_parsed.port in default_port
-            and new_parsed.port in default_port
-        ):
-            return False
-
-        # Standard case: root URI must match
-        return changed_port or changed_scheme
-
-    def resolve_redirects(
-        self,
-        resp,
-        req,
-        stream=False,
-        timeout=None,
-        verify=True,
-        cert=None,
-        proxies=None,
-        yield_requests=False,
-        **adapter_kwargs,
-    ):
-        """Receives a Response. Returns a generator of Responses or Requests."""
-
-        hist = []  # keep track of history
-
-        url = self.get_redirect_target(resp)
-        previous_fragment = urlparse(req.url).fragment
-        while url:
-            prepared_request = req.copy()
-
-            # Update history and keep track of redirects.
-            # resp.history must ignore the original request in this loop
-            hist.append(resp)
-            resp.history = hist[1:]
-
-            try:
-                resp.content  # Consume socket so it can be released
-            except (ChunkedEncodingError, ContentDecodingError, RuntimeError):
-                resp.raw.read(decode_content=False)
-
-            if len(resp.history) >= self.max_redirects:
-                raise TooManyRedirects(
-                    f"Exceeded {self.max_redirects} redirects.", response=resp
-                )
-
-            # Release the connection back into the pool.
-            resp.close()
-
-            # Handle redirection without scheme (see: RFC 1808 Section 4)
-            if url.startswith("//"):
-                parsed_rurl = urlparse(resp.url)
-                url = ":".join([to_native_string(parsed_rurl.scheme), url])
-
-            # Normalize url case and attach previous fragment if needed (RFC 7231 7.1.2)
-            parsed = urlparse(url)
-            if parsed.fragment == "" and previous_fragment:
-                parsed = parsed._replace(fragment=previous_fragment)
-            elif parsed.fragment:
-                previous_fragment = parsed.fragment
-            url = parsed.geturl()
-
-            # Facilitate relative 'location' headers, as allowed by RFC 7231.
-            # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource')
-            # Compliant with RFC3986, we percent encode the url.
-            if not parsed.netloc:
-                url = urljoin(resp.url, requote_uri(url))
-            else:
-                url = requote_uri(url)
-
-            prepared_request.url = to_native_string(url)
-
-            self.rebuild_method(prepared_request, resp)
-
-            # https://github.com/psf/requests/issues/1084
-            if resp.status_code not in (
-                codes.temporary_redirect,
-                codes.permanent_redirect,
-            ):
-                # https://github.com/psf/requests/issues/3490
-                purged_headers = ("Content-Length", "Content-Type", "Transfer-Encoding")
-                for header in purged_headers:
-                    prepared_request.headers.pop(header, None)
-                prepared_request.body = None
-
-            headers = prepared_request.headers
-            headers.pop("Cookie", None)
-
-            # Extract any cookies sent on the response to the cookiejar
-            # in the new request. Because we've mutated our copied prepared
-            # request, use the old one that we haven't yet touched.
-            extract_cookies_to_jar(prepared_request._cookies, req, resp.raw)
-            merge_cookies(prepared_request._cookies, self.cookies)
-            prepared_request.prepare_cookies(prepared_request._cookies)
-
-            # Rebuild auth and proxy information.
-            proxies = self.rebuild_proxies(prepared_request, proxies)
-            self.rebuild_auth(prepared_request, resp)
-
-            # A failed tell() sets `_body_position` to `object()`. This non-None
-            # value ensures `rewindable` will be True, allowing us to raise an
-            # UnrewindableBodyError, instead of hanging the connection.
-            rewindable = prepared_request._body_position is not None and (
-                "Content-Length" in headers or "Transfer-Encoding" in headers
-            )
-
-            # Attempt to rewind consumed file-like object.
-            if rewindable:
-                rewind_body(prepared_request)
-
-            # Override the original request.
-            req = prepared_request
-
-            if yield_requests:
-                yield req
-            else:
-                resp = self.send(
-                    req,
-                    stream=stream,
-                    timeout=timeout,
-                    verify=verify,
-                    cert=cert,
-                    proxies=proxies,
-                    allow_redirects=False,
-                    **adapter_kwargs,
-                )
-
-                extract_cookies_to_jar(self.cookies, prepared_request, resp.raw)
-
-                # extract redirect url, if any, for the next loop
-                url = self.get_redirect_target(resp)
-                yield resp
-
-    def rebuild_auth(self, prepared_request, response):
-        """When being redirected we may want to strip authentication from the
-        request to avoid leaking credentials. This method intelligently removes
-        and reapplies authentication where possible to avoid credential loss.
-        """
-        headers = prepared_request.headers
-        url = prepared_request.url
-
-        if "Authorization" in headers and self.should_strip_auth(
-            response.request.url, url
-        ):
-            # If we get redirected to a new host, we should strip out any
-            # authentication headers.
-            del headers["Authorization"]
-
-        # .netrc might have more auth for us on our new host.
-        new_auth = get_netrc_auth(url) if self.trust_env else None
-        if new_auth is not None:
-            prepared_request.prepare_auth(new_auth)
-
-    def rebuild_proxies(self, prepared_request, proxies):
-        """This method re-evaluates the proxy configuration by considering the
-        environment variables. If we are redirected to a URL covered by
-        NO_PROXY, we strip the proxy configuration. Otherwise, we set missing
-        proxy keys for this URL (in case they were stripped by a previous
-        redirect).
-
-        This method also replaces the Proxy-Authorization header where
-        necessary.
-
-        :rtype: dict
-        """
-        headers = prepared_request.headers
-        scheme = urlparse(prepared_request.url).scheme
-        new_proxies = resolve_proxies(prepared_request, proxies, self.trust_env)
-
-        if "Proxy-Authorization" in headers:
-            del headers["Proxy-Authorization"]
-
-        try:
-            username, password = get_auth_from_url(new_proxies[scheme])
-        except KeyError:
-            username, password = None, None
-
-        # 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:
-            headers["Proxy-Authorization"] = _basic_auth_str(username, password)
-
-        return new_proxies
-
-    def rebuild_method(self, prepared_request, response):
-        """When being redirected we may want to change the method of the request
-        based on certain specs or browser behavior.
-        """
-        method = prepared_request.method
-
-        # https://tools.ietf.org/html/rfc7231#section-6.4.4
-        if response.status_code == codes.see_other and method != "HEAD":
-            method = "GET"
-
-        # Do what the browsers do, despite standards...
-        # First, turn 302s into GETs.
-        if response.status_code == codes.found and method != "HEAD":
-            method = "GET"
-
-        # Second, if a POST is responded to with a 301, turn it into a GET.
-        # This bizarre behaviour is explained in Issue 1704.
-        if response.status_code == codes.moved and method == "POST":
-            method = "GET"
-
-        prepared_request.method = method
-
-
-class Session(SessionRedirectMixin):
-    """A Requests session.
-
-    Provides cookie persistence, connection-pooling, and configuration.
-
-    Basic Usage::
-
-      >>> import requests
-      >>> s = requests.Session()
-      >>> s.get('https://httpbin.org/get')
-      
-
-    Or as a context manager::
-
-      >>> with requests.Session() as s:
-      ...     s.get('https://httpbin.org/get')
-      
-    """
-
-    __attrs__ = [
-        "headers",
-        "cookies",
-        "auth",
-        "proxies",
-        "hooks",
-        "params",
-        "verify",
-        "cert",
-        "adapters",
-        "stream",
-        "trust_env",
-        "max_redirects",
-    ]
-
-    def __init__(self):
-        #: A case-insensitive dictionary of headers to be sent on each
-        #: :class:`Request ` sent from this
-        #: :class:`Session `.
-        self.headers = default_headers()
-
-        #: Default Authentication tuple or object to attach to
-        #: :class:`Request `.
-        self.auth = None
-
-        #: Dictionary mapping protocol or protocol and host to the URL of the proxy
-        #: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to
-        #: be used on each :class:`Request `.
-        self.proxies = {}
-
-        #: Event-handling hooks.
-        self.hooks = default_hooks()
-
-        #: Dictionary of querystring data to attach to each
-        #: :class:`Request `. The dictionary values may be lists for
-        #: representing multivalued query parameters.
-        self.params = {}
-
-        #: Stream response content default.
-        self.stream = False
-
-        #: SSL Verification default.
-        #: Defaults to `True`, requiring requests to verify the TLS certificate at the
-        #: remote end.
-        #: If verify is set to `False`, requests will accept any TLS certificate
-        #: presented by the server, and will ignore hostname mismatches and/or
-        #: expired certificates, which will make your application vulnerable to
-        #: man-in-the-middle (MitM) attacks.
-        #: Only set this to `False` for testing.
-        self.verify = True
-
-        #: SSL client certificate default, if String, path to ssl client
-        #: cert file (.pem). If Tuple, ('cert', 'key') pair.
-        self.cert = None
-
-        #: Maximum number of redirects allowed. If the request exceeds this
-        #: limit, a :class:`TooManyRedirects` exception is raised.
-        #: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is
-        #: 30.
-        self.max_redirects = DEFAULT_REDIRECT_LIMIT
-
-        #: Trust environment settings for proxy configuration, default
-        #: authentication and similar.
-        self.trust_env = True
-
-        #: A CookieJar containing all currently outstanding cookies set on this
-        #: session. By default it is a
-        #: :class:`RequestsCookieJar `, but
-        #: may be any other ``cookielib.CookieJar`` compatible object.
-        self.cookies = cookiejar_from_dict({})
-
-        # Default connection adapters.
-        self.adapters = OrderedDict()
-        self.mount("https://", HTTPAdapter())
-        self.mount("http://", HTTPAdapter())
-
-    def __enter__(self):
-        return self
-
-    def __exit__(self, *args):
-        self.close()
-
-    def prepare_request(self, request):
-        """Constructs a :class:`PreparedRequest ` for
-        transmission and returns it. The :class:`PreparedRequest` has settings
-        merged from the :class:`Request ` instance and those of the
-        :class:`Session`.
-
-        :param request: :class:`Request` instance to prepare with this
-            session's settings.
-        :rtype: requests.PreparedRequest
-        """
-        cookies = request.cookies or {}
-
-        # Bootstrap CookieJar.
-        if not isinstance(cookies, cookielib.CookieJar):
-            cookies = cookiejar_from_dict(cookies)
-
-        # Merge with session cookies
-        merged_cookies = merge_cookies(
-            merge_cookies(RequestsCookieJar(), self.cookies), cookies
-        )
-
-        # Set environment's basic authentication if not explicitly set.
-        auth = request.auth
-        if self.trust_env and not auth and not self.auth:
-            auth = get_netrc_auth(request.url)
-
-        p = PreparedRequest()
-        p.prepare(
-            method=request.method.upper(),
-            url=request.url,
-            files=request.files,
-            data=request.data,
-            json=request.json,
-            headers=merge_setting(
-                request.headers, self.headers, dict_class=CaseInsensitiveDict
-            ),
-            params=merge_setting(request.params, self.params),
-            auth=merge_setting(auth, self.auth),
-            cookies=merged_cookies,
-            hooks=merge_hooks(request.hooks, self.hooks),
-        )
-        return p
-
-    def request(
-        self,
-        method,
-        url,
-        params=None,
-        data=None,
-        headers=None,
-        cookies=None,
-        files=None,
-        auth=None,
-        timeout=None,
-        allow_redirects=True,
-        proxies=None,
-        hooks=None,
-        stream=None,
-        verify=None,
-        cert=None,
-        json=None,
-    ):
-        """Constructs a :class:`Request `, prepares it and sends it.
-        Returns :class:`Response ` object.
-
-        :param method: method for the new :class:`Request` object.
-        :param url: URL for the new :class:`Request` object.
-        :param params: (optional) Dictionary or bytes to be sent in the query
-            string for the :class:`Request`.
-        :param data: (optional) Dictionary, list of tuples, bytes, or file-like
-            object to send in the body of the :class:`Request`.
-        :param json: (optional) json to send in the body of the
-            :class:`Request`.
-        :param headers: (optional) Dictionary of HTTP Headers to send with the
-            :class:`Request`.
-        :param cookies: (optional) Dict or CookieJar object to send with the
-            :class:`Request`.
-        :param files: (optional) Dictionary of ``'filename': file-like-objects``
-            for multipart encoding upload.
-        :param auth: (optional) Auth tuple or callable to enable
-            Basic/Digest/Custom HTTP Auth.
-        :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
-        :param allow_redirects: (optional) Set to True by default.
-        :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
-            the server's TLS certificate, or a string, in which case it must be a path
-            to a CA bundle to use. Defaults to ``True``. When set to
-            ``False``, requests will accept any TLS certificate presented by
-            the server, and will ignore hostname mismatches and/or expired
-            certificates, which will make your application vulnerable to
-            man-in-the-middle (MitM) attacks. Setting verify to ``False``
-            may be useful during local development or testing.
-        :param cert: (optional) if String, path to ssl client cert file (.pem).
-            If Tuple, ('cert', 'key') pair.
-        :rtype: requests.Response
-        """
-        # Create the Request.
-        req = Request(
-            method=method.upper(),
-            url=url,
-            headers=headers,
-            files=files,
-            data=data or {},
-            json=json,
-            params=params or {},
-            auth=auth,
-            cookies=cookies,
-            hooks=hooks,
-        )
-        prep = self.prepare_request(req)
-
-        proxies = proxies or {}
-
-        settings = self.merge_environment_settings(
-            prep.url, proxies, stream, verify, cert
-        )
-
-        # Send the request.
-        send_kwargs = {
-            "timeout": timeout,
-            "allow_redirects": allow_redirects,
-        }
-        send_kwargs.update(settings)
-        resp = self.send(prep, **send_kwargs)
-
-        return resp
-
-    def get(self, url, **kwargs):
-        r"""Sends a GET request. Returns :class:`Response` object.
-
-        :param url: URL for the new :class:`Request` object.
-        :param \*\*kwargs: Optional arguments that ``request`` takes.
-        :rtype: requests.Response
-        """
-
-        kwargs.setdefault("allow_redirects", True)
-        return self.request("GET", url, **kwargs)
-
-    def options(self, url, **kwargs):
-        r"""Sends a OPTIONS request. Returns :class:`Response` object.
-
-        :param url: URL for the new :class:`Request` object.
-        :param \*\*kwargs: Optional arguments that ``request`` takes.
-        :rtype: requests.Response
-        """
-
-        kwargs.setdefault("allow_redirects", True)
-        return self.request("OPTIONS", url, **kwargs)
-
-    def head(self, url, **kwargs):
-        r"""Sends a HEAD request. Returns :class:`Response` object.
-
-        :param url: URL for the new :class:`Request` object.
-        :param \*\*kwargs: Optional arguments that ``request`` takes.
-        :rtype: requests.Response
-        """
-
-        kwargs.setdefault("allow_redirects", False)
-        return self.request("HEAD", url, **kwargs)
-
-    def post(self, url, data=None, json=None, **kwargs):
-        r"""Sends a POST request. Returns :class:`Response` object.
-
-        :param url: URL for the new :class:`Request` object.
-        :param data: (optional) Dictionary, list of tuples, bytes, or file-like
-            object to send in the body of the :class:`Request`.
-        :param json: (optional) json to send in the body of the :class:`Request`.
-        :param \*\*kwargs: Optional arguments that ``request`` takes.
-        :rtype: requests.Response
-        """
-
-        return self.request("POST", url, data=data, json=json, **kwargs)
-
-    def put(self, url, data=None, **kwargs):
-        r"""Sends a PUT request. Returns :class:`Response` object.
-
-        :param url: URL for the new :class:`Request` object.
-        :param data: (optional) Dictionary, list of tuples, bytes, or file-like
-            object to send in the body of the :class:`Request`.
-        :param \*\*kwargs: Optional arguments that ``request`` takes.
-        :rtype: requests.Response
-        """
-
-        return self.request("PUT", url, data=data, **kwargs)
-
-    def patch(self, url, data=None, **kwargs):
-        r"""Sends a PATCH request. Returns :class:`Response` object.
-
-        :param url: URL for the new :class:`Request` object.
-        :param data: (optional) Dictionary, list of tuples, bytes, or file-like
-            object to send in the body of the :class:`Request`.
-        :param \*\*kwargs: Optional arguments that ``request`` takes.
-        :rtype: requests.Response
-        """
-
-        return self.request("PATCH", url, data=data, **kwargs)
-
-    def delete(self, url, **kwargs):
-        r"""Sends a DELETE request. Returns :class:`Response` object.
-
-        :param url: URL for the new :class:`Request` object.
-        :param \*\*kwargs: Optional arguments that ``request`` takes.
-        :rtype: requests.Response
-        """
-
-        return self.request("DELETE", url, **kwargs)
-
-    def send(self, request, **kwargs):
-        """Send a given PreparedRequest.
-
-        :rtype: requests.Response
-        """
-        # Set defaults that the hooks can utilize to ensure they always have
-        # the correct parameters to reproduce the previous request.
-        kwargs.setdefault("stream", self.stream)
-        kwargs.setdefault("verify", self.verify)
-        kwargs.setdefault("cert", self.cert)
-        if "proxies" not in kwargs:
-            kwargs["proxies"] = resolve_proxies(request, self.proxies, self.trust_env)
-
-        # It's possible that users might accidentally send a Request object.
-        # Guard against that specific failure case.
-        if isinstance(request, Request):
-            raise ValueError("You can only send PreparedRequests.")
-
-        # Set up variables needed for resolve_redirects and dispatching of hooks
-        allow_redirects = kwargs.pop("allow_redirects", True)
-        stream = kwargs.get("stream")
-        hooks = request.hooks
-
-        # Get the appropriate adapter to use
-        adapter = self.get_adapter(url=request.url)
-
-        # Start time (approximately) of the request
-        start = preferred_clock()
-
-        # Send the request
-        r = adapter.send(request, **kwargs)
-
-        # Total elapsed time of the request (approximately)
-        elapsed = preferred_clock() - start
-        r.elapsed = timedelta(seconds=elapsed)
-
-        # Response manipulation hooks
-        r = dispatch_hook("response", hooks, r, **kwargs)
-
-        # 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)
-
-        extract_cookies_to_jar(self.cookies, request, r.raw)
-
-        # Resolve redirects if allowed.
-        if allow_redirects:
-            # Redirect resolving generator.
-            gen = self.resolve_redirects(r, request, **kwargs)
-            history = [resp for resp in gen]
-        else:
-            history = []
-
-        # Shuffle things around if there's history.
-        if history:
-            # Insert the first (original) request at the start
-            history.insert(0, r)
-            # Get the last request made
-            r = history.pop()
-            r.history = history
-
-        # If redirects aren't being followed, store the response on the Request for Response.next().
-        if not allow_redirects:
-            try:
-                r._next = next(
-                    self.resolve_redirects(r, request, yield_requests=True, **kwargs)
-                )
-            except StopIteration:
-                pass
-
-        if not stream:
-            r.content
-
-        return r
-
-    def merge_environment_settings(self, url, proxies, stream, verify, cert):
-        """
-        Check the environment and merge it with some settings.
-
-        :rtype: dict
-        """
-        # Gather clues from the surrounding environment.
-        if self.trust_env:
-            # 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():
-                proxies.setdefault(k, v)
-
-            # Look for requests environment configuration
-            # and be compatible with cURL.
-            if verify is True or verify is None:
-                verify = (
-                    os.environ.get("REQUESTS_CA_BUNDLE")
-                    or os.environ.get("CURL_CA_BUNDLE")
-                    or verify
-                )
-
-        # Merge all the kwargs.
-        proxies = merge_setting(proxies, self.proxies)
-        stream = merge_setting(stream, self.stream)
-        verify = merge_setting(verify, self.verify)
-        cert = merge_setting(cert, self.cert)
-
-        return {"proxies": proxies, "stream": stream, "verify": verify, "cert": cert}
-
-    def get_adapter(self, url):
-        """
-        Returns the appropriate connection adapter for the given URL.
-
-        :rtype: requests.adapters.BaseAdapter
-        """
-        for prefix, adapter in self.adapters.items():
-            if url.lower().startswith(prefix.lower()):
-                return adapter
-
-        # Nothing matches :-/
-        raise InvalidSchema(f"No connection adapters were found for {url!r}")
-
-    def close(self):
-        """Closes all adapters and as such the session"""
-        for v in self.adapters.values():
-            v.close()
-
-    def mount(self, prefix, adapter):
-        """Registers a connection adapter to a prefix.
-
-        Adapters are sorted in descending order by prefix length.
-        """
-        self.adapters[prefix] = adapter
-        keys_to_move = [k for k in self.adapters if len(k) < len(prefix)]
-
-        for key in keys_to_move:
-            self.adapters[key] = self.adapters.pop(key)
-
-    def __getstate__(self):
-        state = {attr: getattr(self, attr, None) for attr in self.__attrs__}
-        return state
-
-    def __setstate__(self, state):
-        for attr, value in state.items():
-            setattr(self, attr, value)
-
-
-def session():
-    """
-    Returns a :class:`Session` for context-management.
-
-    .. deprecated:: 1.0.0
-
-        This method has been deprecated since version 1.0.0 and is only kept for
-        backwards compatibility. New code should use :class:`~requests.sessions.Session`
-        to create a session. This may be removed at a future date.
-
-    :rtype: Session
-    """
-    return Session()
diff --git a/apps/bitwarden_event_logs/lib/requests/status_codes.py b/apps/bitwarden_event_logs/lib/requests/status_codes.py
deleted file mode 100755
index c7945a2f..00000000
--- a/apps/bitwarden_event_logs/lib/requests/status_codes.py
+++ /dev/null
@@ -1,128 +0,0 @@
-r"""
-The ``codes`` object defines a mapping from common names for HTTP statuses
-to their numerical codes, accessible either as attributes or as dictionary
-items.
-
-Example::
-
-    >>> import requests
-    >>> requests.codes['temporary_redirect']
-    307
-    >>> requests.codes.teapot
-    418
-    >>> requests.codes['\o/']
-    200
-
-Some codes have multiple names, and both upper- and lower-case versions of
-the names are allowed. For example, ``codes.ok``, ``codes.OK``, and
-``codes.okay`` all correspond to the HTTP status code 200.
-"""
-
-from .structures import LookupDict
-
-_codes = {
-    # Informational.
-    100: ("continue",),
-    101: ("switching_protocols",),
-    102: ("processing", "early-hints"),
-    103: ("checkpoint",),
-    122: ("uri_too_long", "request_uri_too_long"),
-    200: ("ok", "okay", "all_ok", "all_okay", "all_good", "\\o/", "✓"),
-    201: ("created",),
-    202: ("accepted",),
-    203: ("non_authoritative_info", "non_authoritative_information"),
-    204: ("no_content",),
-    205: ("reset_content", "reset"),
-    206: ("partial_content", "partial"),
-    207: ("multi_status", "multiple_status", "multi_stati", "multiple_stati"),
-    208: ("already_reported",),
-    226: ("im_used",),
-    # Redirection.
-    300: ("multiple_choices",),
-    301: ("moved_permanently", "moved", "\\o-"),
-    302: ("found",),
-    303: ("see_other", "other"),
-    304: ("not_modified",),
-    305: ("use_proxy",),
-    306: ("switch_proxy",),
-    307: ("temporary_redirect", "temporary_moved", "temporary"),
-    308: (
-        "permanent_redirect",
-        "resume_incomplete",
-        "resume",
-    ),  # "resume" and "resume_incomplete" to be removed in 3.0
-    # Client Error.
-    400: ("bad_request", "bad"),
-    401: ("unauthorized",),
-    402: ("payment_required", "payment"),
-    403: ("forbidden",),
-    404: ("not_found", "-o-"),
-    405: ("method_not_allowed", "not_allowed"),
-    406: ("not_acceptable",),
-    407: ("proxy_authentication_required", "proxy_auth", "proxy_authentication"),
-    408: ("request_timeout", "timeout"),
-    409: ("conflict",),
-    410: ("gone",),
-    411: ("length_required",),
-    412: ("precondition_failed", "precondition"),
-    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",
-        "requested_range",
-        "range_not_satisfiable",
-    ),
-    417: ("expectation_failed",),
-    418: ("im_a_teapot", "teapot", "i_am_a_teapot"),
-    421: ("misdirected_request",),
-    422: ("unprocessable_entity", "unprocessable", "unprocessable_content"),
-    423: ("locked",),
-    424: ("failed_dependency", "dependency"),
-    425: ("unordered_collection", "unordered", "too_early"),
-    426: ("upgrade_required", "upgrade"),
-    428: ("precondition_required", "precondition"),
-    429: ("too_many_requests", "too_many"),
-    431: ("header_fields_too_large", "fields_too_large"),
-    444: ("no_response", "none"),
-    449: ("retry_with", "retry"),
-    450: ("blocked_by_windows_parental_controls", "parental_controls"),
-    451: ("unavailable_for_legal_reasons", "legal_reasons"),
-    499: ("client_closed_request",),
-    # Server Error.
-    500: ("internal_server_error", "server_error", "/o\\", "✗"),
-    501: ("not_implemented",),
-    502: ("bad_gateway",),
-    503: ("service_unavailable", "unavailable"),
-    504: ("gateway_timeout",),
-    505: ("http_version_not_supported", "http_version"),
-    506: ("variant_also_negotiates",),
-    507: ("insufficient_storage",),
-    509: ("bandwidth_limit_exceeded", "bandwidth"),
-    510: ("not_extended",),
-    511: ("network_authentication_required", "network_auth", "network_authentication"),
-}
-
-codes = LookupDict(name="status_codes")
-
-
-def _init():
-    for code, titles in _codes.items():
-        for title in titles:
-            setattr(codes, title, code)
-            if not title.startswith(("\\", "/")):
-                setattr(codes, title.upper(), code)
-
-    def doc(code):
-        names = ", ".join(f"``{n}``" for n in _codes[code])
-        return "* %d: %s" % (code, names)
-
-    global __doc__
-    __doc__ = (
-        __doc__ + "\n" + "\n".join(doc(code) for code in sorted(_codes))
-        if __doc__ is not None
-        else None
-    )
-
-
-_init()
diff --git a/apps/bitwarden_event_logs/lib/requests/structures.py b/apps/bitwarden_event_logs/lib/requests/structures.py
deleted file mode 100755
index 188e13e4..00000000
--- a/apps/bitwarden_event_logs/lib/requests/structures.py
+++ /dev/null
@@ -1,99 +0,0 @@
-"""
-requests.structures
-~~~~~~~~~~~~~~~~~~~
-
-Data structures that power Requests.
-"""
-
-from collections import OrderedDict
-
-from .compat import Mapping, MutableMapping
-
-
-class CaseInsensitiveDict(MutableMapping):
-    """A case-insensitive ``dict``-like object.
-
-    Implements all methods and operations of
-    ``MutableMapping`` as well as dict's ``copy``. Also
-    provides ``lower_items``.
-
-    All keys are expected to be strings. The structure remembers the
-    case of the last key to be set, and ``iter(instance)``,
-    ``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()``
-    will contain case-sensitive keys. However, querying and contains
-    testing is case insensitive::
-
-        cid = CaseInsensitiveDict()
-        cid['Accept'] = 'application/json'
-        cid['aCCEPT'] == 'application/json'  # True
-        list(cid) == ['Accept']  # True
-
-    For example, ``headers['content-encoding']`` will return the
-    value of a ``'Content-Encoding'`` response header, regardless
-    of how the header name was originally stored.
-
-    If the constructor, ``.update``, or equality comparison
-    operations are given keys that have equal ``.lower()``s, the
-    behavior is undefined.
-    """
-
-    def __init__(self, data=None, **kwargs):
-        self._store = OrderedDict()
-        if data is None:
-            data = {}
-        self.update(data, **kwargs)
-
-    def __setitem__(self, key, value):
-        # Use the lowercased key for lookups, but store the actual
-        # key alongside the value.
-        self._store[key.lower()] = (key, value)
-
-    def __getitem__(self, key):
-        return self._store[key.lower()][1]
-
-    def __delitem__(self, key):
-        del self._store[key.lower()]
-
-    def __iter__(self):
-        return (casedkey for casedkey, mappedvalue in self._store.values())
-
-    def __len__(self):
-        return len(self._store)
-
-    def lower_items(self):
-        """Like iteritems(), but with all lowercase keys."""
-        return ((lowerkey, keyval[1]) for (lowerkey, keyval) in self._store.items())
-
-    def __eq__(self, other):
-        if isinstance(other, Mapping):
-            other = CaseInsensitiveDict(other)
-        else:
-            return NotImplemented
-        # Compare insensitively
-        return dict(self.lower_items()) == dict(other.lower_items())
-
-    # Copy is required
-    def copy(self):
-        return CaseInsensitiveDict(self._store.values())
-
-    def __repr__(self):
-        return str(dict(self.items()))
-
-
-class LookupDict(dict):
-    """Dictionary lookup object."""
-
-    def __init__(self, name=None):
-        self.name = name
-        super().__init__()
-
-    def __repr__(self):
-        return f""
-
-    def __getitem__(self, key):
-        # We allow fall-through here, so values default to None
-
-        return self.__dict__.get(key, None)
-
-    def get(self, key, default=None):
-        return self.__dict__.get(key, default)
diff --git a/apps/bitwarden_event_logs/lib/requests/utils.py b/apps/bitwarden_event_logs/lib/requests/utils.py
deleted file mode 100755
index 8ab55852..00000000
--- a/apps/bitwarden_event_logs/lib/requests/utils.py
+++ /dev/null
@@ -1,1086 +0,0 @@
-"""
-requests.utils
-~~~~~~~~~~~~~~
-
-This module provides utility functions that are used within Requests
-that are also useful for external consumption.
-"""
-
-import codecs
-import contextlib
-import io
-import os
-import re
-import socket
-import struct
-import sys
-import tempfile
-import warnings
-import zipfile
-from collections import OrderedDict
-
-from urllib3.util import make_headers, parse_url
-
-from . import certs
-from .__version__ import __version__
-
-# to_native_string is unused here, but imported here for backwards compatibility
-from ._internal_utils import (  # noqa: F401
-    _HEADER_VALIDATORS_BYTE,
-    _HEADER_VALIDATORS_STR,
-    HEADER_VALIDATORS,
-    to_native_string,
-)
-from .compat import (
-    Mapping,
-    basestring,
-    bytes,
-    getproxies,
-    getproxies_environment,
-    integer_types,
-    is_urllib3_1,
-)
-from .compat import parse_http_list as _parse_list_header
-from .compat import (
-    proxy_bypass,
-    proxy_bypass_environment,
-    quote,
-    str,
-    unquote,
-    urlparse,
-    urlunparse,
-)
-from .cookies import cookiejar_from_dict
-from .exceptions import (
-    FileModeWarning,
-    InvalidHeader,
-    InvalidURL,
-    UnrewindableBodyError,
-)
-from .structures import CaseInsensitiveDict
-
-NETRC_FILES = (".netrc", "_netrc")
-
-DEFAULT_CA_BUNDLE_PATH = certs.where()
-
-DEFAULT_PORTS = {"http": 80, "https": 443}
-
-# Ensure that ', ' is used to preserve previous delimiter behavior.
-DEFAULT_ACCEPT_ENCODING = ", ".join(
-    re.split(r",\s*", make_headers(accept_encoding=True)["accept-encoding"])
-)
-
-
-if sys.platform == "win32":
-    # provide a proxy_bypass version on Windows without DNS lookups
-
-    def proxy_bypass_registry(host):
-        try:
-            import winreg
-        except ImportError:
-            return False
-
-        try:
-            internetSettings = winreg.OpenKey(
-                winreg.HKEY_CURRENT_USER,
-                r"Software\Microsoft\Windows\CurrentVersion\Internet Settings",
-            )
-            # ProxyEnable could be REG_SZ or REG_DWORD, normalizing it
-            proxyEnable = int(winreg.QueryValueEx(internetSettings, "ProxyEnable")[0])
-            # ProxyOverride is almost always a string
-            proxyOverride = winreg.QueryValueEx(internetSettings, "ProxyOverride")[0]
-        except (OSError, ValueError):
-            return False
-        if not proxyEnable or not proxyOverride:
-            return False
-
-        # make a check value list from the registry entry: replace the
-        # '' 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 == "":
-                if "." not in host:
-                    return True
-            test = test.replace(".", r"\.")  # mask dots
-            test = test.replace("*", r".*")  # change glob sequence
-            test = test.replace("?", r".")  # change glob char
-            if re.match(test, host, re.I):
-                return True
-        return False
-
-    def proxy_bypass(host):  # noqa
-        """Return True, if the host should be bypassed.
-
-        Checks proxy settings gathered from the environment, if specified,
-        or the registry.
-        """
-        if getproxies_environment():
-            return proxy_bypass_environment(host)
-        else:
-            return proxy_bypass_registry(host)
-
-
-def dict_to_sequence(d):
-    """Returns an internal sequence dictionary update."""
-
-    if hasattr(d, "items"):
-        d = d.items()
-
-    return d
-
-
-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)
-
-    elif hasattr(o, "len"):
-        total_length = o.len
-
-    elif hasattr(o, "fileno"):
-        try:
-            fileno = o.fileno()
-        except (io.UnsupportedOperation, AttributeError):
-            # AttributeError is a surprising exception, seeing as how we've just checked
-            # that `hasattr(o, 'fileno')`.  It happens for objects obtained via
-            # `Tarfile.extractfile()`, per issue 5229.
-            pass
-        else:
-            total_length = os.fstat(fileno).st_size
-
-            # Having used fstat to determine the file length, we need to
-            # confirm that this file was opened up in binary mode.
-            if "b" not in o.mode:
-                warnings.warn(
-                    (
-                        "Requests has determined the content-length for this "
-                        "request using the binary size of the file: however, the "
-                        "file has been opened in text mode (i.e. without the 'b' "
-                        "flag in the mode). This may lead to an incorrect "
-                        "content-length. In Requests 3.0, support will be removed "
-                        "for files in text mode."
-                    ),
-                    FileModeWarning,
-                )
-
-    if hasattr(o, "tell"):
-        try:
-            current_position = o.tell()
-        except OSError:
-            # This can happen in some weird situations, such as when the file
-            # is actually a special file descriptor like stdin. In this
-            # instance, we don't know what the length is, so set it to zero and
-            # let requests chunk it instead.
-            if total_length is not None:
-                current_position = total_length
-        else:
-            if hasattr(o, "seek") and total_length is None:
-                # StringIO and BytesIO have seek but no usable fileno
-                try:
-                    # seek to end of file
-                    o.seek(0, 2)
-                    total_length = o.tell()
-
-                    # seek back to current position to support
-                    # partially read file-like objects
-                    o.seek(current_position or 0)
-                except OSError:
-                    total_length = 0
-
-    if total_length is None:
-        total_length = 0
-
-    return max(0, total_length - current_position)
-
-
-def get_netrc_auth(url, raise_errors=False):
-    """Returns the Requests tuple auth for a given url from netrc."""
-
-    netrc_file = os.environ.get("NETRC")
-    if netrc_file is not None:
-        netrc_locations = (netrc_file,)
-    else:
-        netrc_locations = (f"~/{f}" for f in NETRC_FILES)
-
-    try:
-        from netrc import NetrcParseError, netrc
-
-        netrc_path = None
-
-        for f in netrc_locations:
-            loc = os.path.expanduser(f)
-            if os.path.exists(loc):
-                netrc_path = loc
-                break
-
-        # Abort early if there isn't one.
-        if netrc_path is None:
-            return
-
-        ri = urlparse(url)
-        host = ri.hostname
-
-        try:
-            _netrc = netrc(netrc_path).authenticators(host)
-            if _netrc:
-                # Return with login / password
-                login_i = 0 if _netrc[0] else 1
-                return (_netrc[login_i], _netrc[2])
-        except (NetrcParseError, OSError):
-            # If there was a parsing error or a permissions issue reading the file,
-            # we'll just skip netrc auth unless explicitly asked to raise errors.
-            if raise_errors:
-                raise
-
-    # App Engine hackiness.
-    except (ImportError, AttributeError):
-        pass
-
-
-def guess_filename(obj):
-    """Tries to guess the filename of the given object."""
-    name = getattr(obj, "name", None)
-    if name and isinstance(name, basestring) and name[0] != "<" and name[-1] != ">":
-        return os.path.basename(name)
-
-
-def extract_zipped_paths(path):
-    """Replace nonexistent paths that look like they refer to a member of a zip
-    archive with the location of an extracted copy of the target, or else
-    just return the provided path unchanged.
-    """
-    if os.path.exists(path):
-        # this is already a valid path, no need to do anything further
-        return path
-
-    # find the first valid part of the provided path and treat that as a zip archive
-    # assume the rest of the path is the name of a member in the archive
-    archive, member = os.path.split(path)
-    while archive and not os.path.exists(archive):
-        archive, prefix = os.path.split(archive)
-        if not prefix:
-            # If we don't check for an empty prefix after the split (in other words, archive remains unchanged after the split),
-            # we _can_ end up in an infinite loop on a rare corner case affecting a small number of users
-            break
-        member = "/".join([prefix, member])
-
-    if not zipfile.is_zipfile(archive):
-        return path
-
-    zip_file = zipfile.ZipFile(archive)
-    if member not in zip_file.namelist():
-        return path
-
-    # we have a valid zip archive and a valid member of that archive
-    tmp = tempfile.gettempdir()
-    extracted_path = os.path.join(tmp, member.split("/")[-1])
-    if not os.path.exists(extracted_path):
-        # use read + write to avoid the creating nested folders, we only want the file, avoids mkdir racing condition
-        with atomic_open(extracted_path) as file_handler:
-            file_handler.write(zip_file.read(member))
-    return extracted_path
-
-
-@contextlib.contextmanager
-def atomic_open(filename):
-    """Write a file to the disk in an atomic fashion"""
-    tmp_descriptor, tmp_name = tempfile.mkstemp(dir=os.path.dirname(filename))
-    try:
-        with os.fdopen(tmp_descriptor, "wb") as tmp_handler:
-            yield tmp_handler
-        os.replace(tmp_name, filename)
-    except BaseException:
-        os.remove(tmp_name)
-        raise
-
-
-def from_key_val_list(value):
-    """Take an object and test to see if it can be represented as a
-    dictionary. Unless it can not be represented as such, return an
-    OrderedDict, e.g.,
-
-    ::
-
-        >>> from_key_val_list([('key', 'val')])
-        OrderedDict([('key', 'val')])
-        >>> from_key_val_list('string')
-        Traceback (most recent call last):
-        ...
-        ValueError: cannot encode objects that are not 2-tuples
-        >>> from_key_val_list({'key': 'val'})
-        OrderedDict([('key', 'val')])
-
-    :rtype: OrderedDict
-    """
-    if value is None:
-        return None
-
-    if isinstance(value, (str, bytes, bool, int)):
-        raise ValueError("cannot encode objects that are not 2-tuples")
-
-    return OrderedDict(value)
-
-
-def to_key_val_list(value):
-    """Take an object and test to see if it can be represented as a
-    dictionary. If it can be, return a list of tuples, e.g.,
-
-    ::
-
-        >>> to_key_val_list([('key', 'val')])
-        [('key', 'val')]
-        >>> to_key_val_list({'key': 'val'})
-        [('key', 'val')]
-        >>> to_key_val_list('string')
-        Traceback (most recent call last):
-        ...
-        ValueError: cannot encode objects that are not 2-tuples
-
-    :rtype: list
-    """
-    if value is None:
-        return None
-
-    if isinstance(value, (str, bytes, bool, int)):
-        raise ValueError("cannot encode objects that are not 2-tuples")
-
-    if isinstance(value, Mapping):
-        value = value.items()
-
-    return list(value)
-
-
-# From mitsuhiko/werkzeug (used with permission).
-def parse_list_header(value):
-    """Parse lists as described by RFC 2068 Section 2.
-
-    In particular, parse comma-separated lists where the elements of
-    the list may include quoted-strings.  A quoted-string could
-    contain a comma.  A non-quoted string could have quotes in the
-    middle.  Quotes are removed automatically after parsing.
-
-    It basically works like :func:`parse_set_header` just that items
-    may appear multiple times and case sensitivity is preserved.
-
-    The return value is a standard :class:`list`:
-
-    >>> parse_list_header('token, "quoted value"')
-    ['token', 'quoted value']
-
-    To create a header from the :class:`list` again, use the
-    :func:`dump_header` function.
-
-    :param value: a string with a list header.
-    :return: :class:`list`
-    :rtype: list
-    """
-    result = []
-    for item in _parse_list_header(value):
-        if item[:1] == item[-1:] == '"':
-            item = unquote_header_value(item[1:-1])
-        result.append(item)
-    return result
-
-
-# From mitsuhiko/werkzeug (used with permission).
-def parse_dict_header(value):
-    """Parse lists of key, value pairs as described by RFC 2068 Section 2 and
-    convert them into a python dict:
-
-    >>> d = parse_dict_header('foo="is a fish", bar="as well"')
-    >>> type(d) is dict
-    True
-    >>> sorted(d.items())
-    [('bar', 'as well'), ('foo', 'is a fish')]
-
-    If there is no value for a key it will be `None`:
-
-    >>> parse_dict_header('key_without_value')
-    {'key_without_value': None}
-
-    To create a header from the :class:`dict` again, use the
-    :func:`dump_header` function.
-
-    :param value: a string with a dict header.
-    :return: :class:`dict`
-    :rtype: dict
-    """
-    result = {}
-    for item in _parse_list_header(value):
-        if "=" not in item:
-            result[item] = None
-            continue
-        name, value = item.split("=", 1)
-        if value[:1] == value[-1:] == '"':
-            value = unquote_header_value(value[1:-1])
-        result[name] = value
-    return result
-
-
-# From mitsuhiko/werkzeug (used with permission).
-def unquote_header_value(value, is_filename=False):
-    r"""Unquotes a header value.  (Reversal of :func:`quote_header_value`).
-    This does not use the real unquoting but what browsers are actually
-    using for quoting.
-
-    :param value: the header value to unquote.
-    :rtype: str
-    """
-    if value and value[0] == value[-1] == '"':
-        # this is not the real unquoting, but fixing this so that the
-        # RFC is met will result in bugs with internet explorer and
-        # probably some other browsers as well.  IE for example is
-        # uploading files with "C:\foo\bar.txt" as filename
-        value = value[1:-1]
-
-        # if this is a filename and the starting characters look like
-        # a UNC path, then just return the value without quotes.  Using the
-        # replace sequence below on a UNC path has the effect of turning
-        # the leading double slash into a single slash and then
-        # _fix_ie_filename() doesn't work correctly.  See #458.
-        if not is_filename or value[:2] != "\\\\":
-            return value.replace("\\\\", "\\").replace('\\"', '"')
-    return value
-
-
-def dict_from_cookiejar(cj):
-    """Returns a key/value dictionary from a CookieJar.
-
-    :param cj: CookieJar object to extract cookies from.
-    :rtype: dict
-    """
-
-    cookie_dict = {cookie.name: cookie.value for cookie in cj}
-    return cookie_dict
-
-
-def add_dict_to_cookiejar(cj, cookie_dict):
-    """Returns a CookieJar from a key/value dictionary.
-
-    :param cj: CookieJar to insert cookies into.
-    :param cookie_dict: Dict of key/values to insert into CookieJar.
-    :rtype: CookieJar
-    """
-
-    return cookiejar_from_dict(cookie_dict, cj)
-
-
-def get_encodings_from_content(content):
-    """Returns encodings from given content string.
-
-    :param content: bytestring to extract encodings from.
-    """
-    warnings.warn(
-        (
-            "In requests 3.0, get_encodings_from_content will be removed. For "
-            "more information, please see the discussion on issue #2266. (This"
-            " warning should only appear once.)"
-        ),
-        DeprecationWarning,
-    )
-
-    charset_re = re.compile(r']', flags=re.I)
-    pragma_re = re.compile(r']', flags=re.I)
-    xml_re = re.compile(r'^<\?xml.*?encoding=["\']*(.+?)["\'>]')
-
-    return (
-        charset_re.findall(content)
-        + pragma_re.findall(content)
-        + xml_re.findall(content)
-    )
-
-
-def _parse_content_type_header(header):
-    """Returns content type and parameters from given header
-
-    :param header: string
-    :return: tuple containing content type and dictionary of
-         parameters
-    """
-
-    tokens = header.split(";")
-    content_type, params = tokens[0].strip(), tokens[1:]
-    params_dict = {}
-    items_to_strip = "\"' "
-
-    for param in params:
-        param = param.strip()
-        if param:
-            key, value = param, True
-            index_of_equals = param.find("=")
-            if index_of_equals != -1:
-                key = param[:index_of_equals].strip(items_to_strip)
-                value = param[index_of_equals + 1 :].strip(items_to_strip)
-            params_dict[key.lower()] = value
-    return content_type, params_dict
-
-
-def get_encoding_from_headers(headers):
-    """Returns encodings from given HTTP Header Dict.
-
-    :param headers: dictionary to extract encoding from.
-    :rtype: str
-    """
-
-    content_type = headers.get("content-type")
-
-    if not content_type:
-        return None
-
-    content_type, params = _parse_content_type_header(content_type)
-
-    if "charset" in params:
-        return params["charset"].strip("'\"")
-
-    if "text" in content_type:
-        return "ISO-8859-1"
-
-    if "application/json" in content_type:
-        # Assume UTF-8 based on RFC 4627: https://www.ietf.org/rfc/rfc4627.txt since the charset was unset
-        return "utf-8"
-
-
-def stream_decode_response_unicode(iterator, r):
-    """Stream decodes an iterator."""
-
-    if r.encoding is None:
-        yield from iterator
-        return
-
-    decoder = codecs.getincrementaldecoder(r.encoding)(errors="replace")
-    for chunk in iterator:
-        rv = decoder.decode(chunk)
-        if rv:
-            yield rv
-    rv = decoder.decode(b"", final=True)
-    if rv:
-        yield rv
-
-
-def iter_slices(string, slice_length):
-    """Iterate over slices of a string."""
-    pos = 0
-    if slice_length is None or slice_length <= 0:
-        slice_length = len(string)
-    while pos < len(string):
-        yield string[pos : pos + slice_length]
-        pos += slice_length
-
-
-def get_unicode_from_response(r):
-    """Returns the requested content back in unicode.
-
-    :param r: Response object to get unicode content from.
-
-    Tried:
-
-    1. charset from content-type
-    2. fall back and replace all unicode characters
-
-    :rtype: str
-    """
-    warnings.warn(
-        (
-            "In requests 3.0, get_unicode_from_response will be removed. For "
-            "more information, please see the discussion on issue #2266. (This"
-            " warning should only appear once.)"
-        ),
-        DeprecationWarning,
-    )
-
-    tried_encodings = []
-
-    # Try charset from content-type
-    encoding = get_encoding_from_headers(r.headers)
-
-    if encoding:
-        try:
-            return str(r.content, encoding)
-        except UnicodeError:
-            tried_encodings.append(encoding)
-
-    # Fall back:
-    try:
-        return str(r.content, encoding, errors="replace")
-    except TypeError:
-        return r.content
-
-
-# The unreserved URI characters (RFC 3986)
-UNRESERVED_SET = frozenset(
-    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789-._~"
-)
-
-
-def unquote_unreserved(uri):
-    """Un-escape any percent-escape sequences in a URI that are unreserved
-    characters. This leaves all reserved, illegal and non-ASCII bytes encoded.
-
-    :rtype: str
-    """
-    parts = uri.split("%")
-    for i in range(1, len(parts)):
-        h = parts[i][0:2]
-        if len(h) == 2 and h.isalnum():
-            try:
-                c = chr(int(h, 16))
-            except ValueError:
-                raise InvalidURL(f"Invalid percent-escape sequence: '{h}'")
-
-            if c in UNRESERVED_SET:
-                parts[i] = c + parts[i][2:]
-            else:
-                parts[i] = f"%{parts[i]}"
-        else:
-            parts[i] = f"%{parts[i]}"
-    return "".join(parts)
-
-
-def requote_uri(uri):
-    """Re-quote the given URI.
-
-    This function passes the given URI through an unquote/quote cycle to
-    ensure that it is fully and consistently quoted.
-
-    :rtype: str
-    """
-    safe_with_percent = "!#$%&'()*+,/:;=?@[]~"
-    safe_without_percent = "!#$&'()*+,/:;=?@[]~"
-    try:
-        # Unquote only the unreserved characters
-        # Then quote only illegal characters (do not quote reserved,
-        # unreserved, or '%')
-        return quote(unquote_unreserved(uri), safe=safe_with_percent)
-    except InvalidURL:
-        # We couldn't unquote the given URI, so let's try quoting it, but
-        # there may be unquoted '%'s in the URI. We need to make sure they're
-        # properly quoted so they do not cause issues elsewhere.
-        return quote(uri, safe=safe_without_percent)
-
-
-def address_in_network(ip, net):
-    """This function allows you to check if an IP belongs to a network subnet
-
-    Example: returns True if ip = 192.168.1.1 and net = 192.168.1.0/24
-             returns False if ip = 192.168.1.1 and net = 192.168.100.0/24
-
-    :rtype: bool
-    """
-    ipaddr = struct.unpack("=L", socket.inet_aton(ip))[0]
-    netaddr, bits = net.split("/")
-    netmask = struct.unpack("=L", socket.inet_aton(dotted_netmask(int(bits))))[0]
-    network = struct.unpack("=L", socket.inet_aton(netaddr))[0] & netmask
-    return (ipaddr & netmask) == (network & netmask)
-
-
-def dotted_netmask(mask):
-    """Converts mask from /xx format to xxx.xxx.xxx.xxx
-
-    Example: if mask is 24 function returns 255.255.255.0
-
-    :rtype: str
-    """
-    bits = 0xFFFFFFFF ^ (1 << 32 - mask) - 1
-    return socket.inet_ntoa(struct.pack(">I", bits))
-
-
-def is_ipv4_address(string_ip):
-    """
-    :rtype: bool
-    """
-    try:
-        socket.inet_aton(string_ip)
-    except OSError:
-        return False
-    return True
-
-
-def is_valid_cidr(string_network):
-    """
-    Very simple check of the cidr format in no_proxy variable.
-
-    :rtype: bool
-    """
-    if string_network.count("/") == 1:
-        try:
-            mask = int(string_network.split("/")[1])
-        except ValueError:
-            return False
-
-        if mask < 1 or mask > 32:
-            return False
-
-        try:
-            socket.inet_aton(string_network.split("/")[0])
-        except OSError:
-            return False
-    else:
-        return False
-    return True
-
-
-@contextlib.contextmanager
-def set_environ(env_name, value):
-    """Set the environment variable 'env_name' to 'value'
-
-    Save previous value, yield, and then restore the previous value stored in
-    the environment variable 'env_name'.
-
-    If 'value' is None, do nothing"""
-    value_changed = value is not None
-    if value_changed:
-        old_value = os.environ.get(env_name)
-        os.environ[env_name] = value
-    try:
-        yield
-    finally:
-        if value_changed:
-            if old_value is None:
-                del os.environ[env_name]
-            else:
-                os.environ[env_name] = old_value
-
-
-def should_bypass_proxies(url, no_proxy):
-    """
-    Returns whether we should bypass proxies or not.
-
-    :rtype: bool
-    """
-
-    # Prioritize lowercase environment variables over uppercase
-    # to keep a consistent behaviour with other http projects (curl, wget).
-    def get_proxy(key):
-        return os.environ.get(key) or os.environ.get(key.upper())
-
-    # First check whether no_proxy is defined. If it is, check that the URL
-    # we're getting isn't in the no_proxy list.
-    no_proxy_arg = no_proxy
-    if no_proxy is None:
-        no_proxy = get_proxy("no_proxy")
-    parsed = urlparse(url)
-
-    if parsed.hostname is None:
-        # URLs don't always have hostnames, e.g. file:/// urls.
-        return True
-
-    if no_proxy:
-        # We need to check whether we match here. We need to see if we match
-        # the end of the hostname, both with and without the port.
-        no_proxy = (host for host in no_proxy.replace(" ", "").split(",") if host)
-
-        if is_ipv4_address(parsed.hostname):
-            for proxy_ip in no_proxy:
-                if is_valid_cidr(proxy_ip):
-                    if address_in_network(parsed.hostname, proxy_ip):
-                        return True
-                elif parsed.hostname == proxy_ip:
-                    # If no_proxy ip was defined in plain IP notation instead of cidr notation &
-                    # matches the IP of the index
-                    return True
-        else:
-            host_with_port = parsed.hostname
-            if parsed.port:
-                host_with_port += f":{parsed.port}"
-
-            for host in no_proxy:
-                if parsed.hostname.endswith(host) or host_with_port.endswith(host):
-                    # The URL does match something in no_proxy, so we don't want
-                    # to apply the proxies on this URL.
-                    return True
-
-    with set_environ("no_proxy", no_proxy_arg):
-        # parsed.hostname can be `None` in cases such as a file URI.
-        try:
-            bypass = proxy_bypass(parsed.hostname)
-        except (TypeError, socket.gaierror):
-            bypass = False
-
-    if bypass:
-        return True
-
-    return False
-
-
-def get_environ_proxies(url, no_proxy=None):
-    """
-    Return a dict of environment proxies.
-
-    :rtype: dict
-    """
-    if should_bypass_proxies(url, no_proxy=no_proxy):
-        return {}
-    else:
-        return getproxies()
-
-
-def select_proxy(url, proxies):
-    """Select a proxy for the url, if applicable.
-
-    :param url: The url being for the request
-    :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs
-    """
-    proxies = proxies or {}
-    urlparts = urlparse(url)
-    if urlparts.hostname is None:
-        return proxies.get(urlparts.scheme, proxies.get("all"))
-
-    proxy_keys = [
-        urlparts.scheme + "://" + urlparts.hostname,
-        urlparts.scheme,
-        "all://" + urlparts.hostname,
-        "all",
-    ]
-    proxy = None
-    for proxy_key in proxy_keys:
-        if proxy_key in proxies:
-            proxy = proxies[proxy_key]
-            break
-
-    return proxy
-
-
-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 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
-    :param trust_env: Boolean declaring whether to trust environment configs
-
-    :rtype: dict
-    """
-    proxies = proxies if proxies is not None else {}
-    url = request.url
-    scheme = urlparse(url).scheme
-    no_proxy = proxies.get("no_proxy")
-    new_proxies = proxies.copy()
-
-    if trust_env and not should_bypass_proxies(url, no_proxy=no_proxy):
-        environ_proxies = get_environ_proxies(url, no_proxy=no_proxy)
-
-        proxy = environ_proxies.get(scheme, environ_proxies.get("all"))
-
-        if proxy:
-            new_proxies.setdefault(scheme, proxy)
-    return new_proxies
-
-
-def default_user_agent(name="python-requests"):
-    """
-    Return a string representing the default user agent.
-
-    :rtype: str
-    """
-    return f"{name}/{__version__}"
-
-
-def default_headers():
-    """
-    :rtype: requests.structures.CaseInsensitiveDict
-    """
-    return CaseInsensitiveDict(
-        {
-            "User-Agent": default_user_agent(),
-            "Accept-Encoding": DEFAULT_ACCEPT_ENCODING,
-            "Accept": "*/*",
-            "Connection": "keep-alive",
-        }
-    )
-
-
-def parse_header_links(value):
-    """Return a list of parsed link headers proxies.
-
-    i.e. Link: ; rel=front; type="image/jpeg",; rel=back;type="image/jpeg"
-
-    :rtype: list
-    """
-
-    links = []
-
-    replace_chars = " '\""
-
-    value = value.strip(replace_chars)
-    if not value:
-        return links
-
-    for val in re.split(", *<", value):
-        try:
-            url, params = val.split(";", 1)
-        except ValueError:
-            url, params = val, ""
-
-        link = {"url": url.strip("<> '\"")}
-
-        for param in params.split(";"):
-            try:
-                key, value = param.split("=")
-            except ValueError:
-                break
-
-            link[key.strip(replace_chars)] = value.strip(replace_chars)
-
-        links.append(link)
-
-    return links
-
-
-# Null bytes; no need to recreate these on each call to guess_json_utf
-_null = "\x00".encode("ascii")  # encoding to ASCII for Python 3
-_null2 = _null * 2
-_null3 = _null * 3
-
-
-def guess_json_utf(data):
-    """
-    :rtype: str
-    """
-    # JSON always starts with two ASCII characters, so detection is as
-    # easy as counting the nulls and from their location and count
-    # determine the encoding. Also detect a BOM, if present.
-    sample = data[:4]
-    if sample in (codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE):
-        return "utf-32"  # BOM included
-    if sample[:3] == codecs.BOM_UTF8:
-        return "utf-8-sig"  # BOM included, MS style (discouraged)
-    if sample[:2] in (codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE):
-        return "utf-16"  # BOM included
-    nullcount = sample.count(_null)
-    if nullcount == 0:
-        return "utf-8"
-    if nullcount == 2:
-        if sample[::2] == _null2:  # 1st and 3rd are null
-            return "utf-16-be"
-        if sample[1::2] == _null2:  # 2nd and 4th are null
-            return "utf-16-le"
-        # Did not detect 2 valid UTF-16 ascii-range characters
-    if nullcount == 3:
-        if sample[:3] == _null3:
-            return "utf-32-be"
-        if sample[1:] == _null3:
-            return "utf-32-le"
-        # Did not detect a valid UTF-32 ascii-range character
-    return None
-
-
-def prepend_scheme_if_needed(url, new_scheme):
-    """Given a URL that may or may not have a scheme, prepend the given scheme.
-    Does not replace a present scheme with the one provided as an argument.
-
-    :rtype: str
-    """
-    parsed = parse_url(url)
-    scheme, auth, host, port, path, query, fragment = parsed
-
-    # A defect in urlparse determines that there isn't a netloc present in some
-    # urls. We previously assumed parsing was overly cautious, and swapped the
-    # netloc and path. Due to a lack of tests on the original defect, this is
-    # maintained with parse_url for backwards compatibility.
-    netloc = parsed.netloc
-    if not netloc:
-        netloc, path = path, netloc
-
-    if auth:
-        # parse_url doesn't provide the netloc with auth
-        # so we'll add it ourselves.
-        netloc = "@".join([auth, netloc])
-    if scheme is None:
-        scheme = new_scheme
-    if path is None:
-        path = ""
-
-    return urlunparse((scheme, netloc, path, "", query, fragment))
-
-
-def get_auth_from_url(url):
-    """Given a url with authentication components, extract them into a tuple of
-    username,password.
-
-    :rtype: (str,str)
-    """
-    parsed = urlparse(url)
-
-    try:
-        auth = (unquote(parsed.username), unquote(parsed.password))
-    except (AttributeError, TypeError):
-        auth = ("", "")
-
-    return auth
-
-
-def check_header_validity(header):
-    """Verifies that header parts don't contain leading whitespace
-    reserved characters, or return characters.
-
-    :param header: tuple, in the format (name, value).
-    """
-    name, value = header
-    _validate_header_part(header, name, 0)
-    _validate_header_part(header, value, 1)
-
-
-def _validate_header_part(header, header_part, header_validator_index):
-    if isinstance(header_part, str):
-        validator = _HEADER_VALIDATORS_STR[header_validator_index]
-    elif isinstance(header_part, bytes):
-        validator = _HEADER_VALIDATORS_BYTE[header_validator_index]
-    else:
-        raise InvalidHeader(
-            f"Header part ({header_part!r}) from {header} "
-            f"must be of type str or bytes, not {type(header_part)}"
-        )
-
-    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"character(s) in header {header_kind}: {header_part!r}"
-        )
-
-
-def urldefragauth(url):
-    """
-    Given a url remove the fragment and the authentication part.
-
-    :rtype: str
-    """
-    scheme, netloc, path, params, query, fragment = urlparse(url)
-
-    # see func:`prepend_scheme_if_needed`
-    if not netloc:
-        netloc, path = path, netloc
-
-    netloc = netloc.rsplit("@", 1)[-1]
-
-    return urlunparse((scheme, netloc, path, params, query, ""))
-
-
-def rewind_body(prepared_request):
-    """Move file pointer back to its recorded starting position
-    so it can be read again on redirect.
-    """
-    body_seek = getattr(prepared_request.body, "seek", None)
-    if body_seek is not None and isinstance(
-        prepared_request._body_position, integer_types
-    ):
-        try:
-            body_seek(prepared_request._body_position)
-        except OSError:
-            raise UnrewindableBodyError(
-                "An error occurred when rewinding request body for redirect."
-            )
-    else:
-        raise UnrewindableBodyError("Unable to rewind request body for redirect.")
diff --git a/apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/INSTALLER b/apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/INSTALLER
deleted file mode 100755
index a1b589e3..00000000
--- a/apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/INSTALLER
+++ /dev/null
@@ -1 +0,0 @@
-pip
diff --git a/apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/LICENSE b/apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/LICENSE
deleted file mode 100755
index 1cc22a5a..00000000
--- a/apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/LICENSE
+++ /dev/null
@@ -1,18 +0,0 @@
-Copyright (c) 2010-2024 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.
diff --git a/apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/METADATA b/apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/METADATA
deleted file mode 100755
index cfde03c2..00000000
--- a/apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/METADATA
+++ /dev/null
@@ -1,43 +0,0 @@
-Metadata-Version: 2.1
-Name: six
-Version: 1.17.0
-Summary: Python 2 and 3 compatibility utilities
-Home-page: https://github.com/benjaminp/six
-Author: Benjamin Peterson
-Author-email: benjamin@python.org
-License: MIT
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 3
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Topic :: Software Development :: Libraries
-Classifier: Topic :: Utilities
-Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*
-License-File: LICENSE
-
-.. image:: https://img.shields.io/pypi/v/six.svg
-   :target: https://pypi.org/project/six/
-   :alt: six on PyPI
-
-.. image:: https://readthedocs.org/projects/six/badge/?version=latest
-   :target: https://six.readthedocs.io/
-   :alt: six's documentation on Read the Docs
-
-.. image:: https://img.shields.io/badge/license-MIT-green.svg
-   :target: https://github.com/benjaminp/six/blob/master/LICENSE
-   :alt: MIT License badge
-
-Six is a Python 2 and 3 compatibility library.  It provides utility functions
-for smoothing over the differences between the Python versions with the goal of
-writing Python code that is compatible on both Python versions.  See the
-documentation for more information on what is provided.
-
-Six supports Python 2.7 and 3.3+.  It is contained in only one Python
-file, so it can be easily copied into your project. (The copyright and license
-notice must be retained.)
-
-Online documentation is at https://six.readthedocs.io/.
-
-Bugs can be reported to https://github.com/benjaminp/six.  The code can also
-be found there.
diff --git a/apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/RECORD b/apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/RECORD
deleted file mode 100755
index 72d425d7..00000000
--- a/apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/RECORD
+++ /dev/null
@@ -1,8 +0,0 @@
-six-1.17.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-six-1.17.0.dist-info/LICENSE,sha256=Q3W6IOK5xsTnytKUCmKP2Q6VzD1Q7pKq51VxXYuh-9A,1066
-six-1.17.0.dist-info/METADATA,sha256=ViBCB4wnUlSfbYp8htvF3XCAiKe-bYBnLsewcQC3JGg,1658
-six-1.17.0.dist-info/RECORD,,
-six-1.17.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-six-1.17.0.dist-info/WHEEL,sha256=pxeNX5JdtCe58PUSYP9upmc7jdRPgvT0Gm9kb1SHlVw,109
-six-1.17.0.dist-info/top_level.txt,sha256=_iVH_iYEtEXnD8nYGQYpYFUvkUW9sEO1GYbkeKSAais,4
-six.py,sha256=xRyR9wPT1LNpbJI8tf7CE-BeddkhU5O--sfy-mo5BN8,34703
diff --git a/apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/REQUESTED b/apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/REQUESTED
deleted file mode 100755
index e69de29b..00000000
diff --git a/apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/WHEEL b/apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/WHEEL
deleted file mode 100755
index 104f3874..00000000
--- a/apps/bitwarden_event_logs/lib/six-1.17.0.dist-info/WHEEL
+++ /dev/null
@@ -1,6 +0,0 @@
-Wheel-Version: 1.0
-Generator: setuptools (75.6.0)
-Root-Is-Purelib: true
-Tag: py2-none-any
-Tag: py3-none-any
-
diff --git a/apps/bitwarden_event_logs/lib/six.py b/apps/bitwarden_event_logs/lib/six.py
deleted file mode 100755
index 3de5969b..00000000
--- a/apps/bitwarden_event_logs/lib/six.py
+++ /dev/null
@@ -1,1003 +0,0 @@
-# Copyright (c) 2010-2024 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.17.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", "IterableUserDict", "UserDict"),
-    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("proxy_bypass", "urllib", "urllib.request"),
-    MovedAttribute("parse_http_list", "urllib2", "urllib.request"),
-    MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"),
-]
-if sys.version_info[:2] < (3, 14):
-    _urllib_request_moved_attributes.extend(
-        [
-            MovedAttribute("URLopener", "urllib", "urllib.request"),
-            MovedAttribute("FancyURLopener", "urllib", "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/apps/bitwarden_event_logs/lib/socks.py b/apps/bitwarden_event_logs/lib/socks.py
deleted file mode 100755
index 83b1435d..00000000
--- a/apps/bitwarden_event_logs/lib/socks.py
+++ /dev/null
@@ -1,847 +0,0 @@
-from base64 import b64encode
-try:
-    from collections.abc import Callable
-except ImportError:
-    from collections import Callable
-from errno import EOPNOTSUPP, EINVAL, EAGAIN
-import functools
-from io import BytesIO
-import logging
-import os
-from os import SEEK_CUR
-import socket
-import struct
-import sys
-
-__version__ = "1.7.1"
-
-
-if os.name == "nt" and sys.version_info < (3, 0):
-    try:
-        import win_inet_pton
-    except ImportError:
-        raise ImportError(
-            "To run PySocks on Windows you must install win_inet_pton")
-
-log = logging.getLogger(__name__)
-
-PROXY_TYPE_SOCKS4 = SOCKS4 = 1
-PROXY_TYPE_SOCKS5 = SOCKS5 = 2
-PROXY_TYPE_HTTP = HTTP = 3
-
-PROXY_TYPES = {"SOCKS4": SOCKS4, "SOCKS5": SOCKS5, "HTTP": HTTP}
-PRINTABLE_PROXY_TYPES = dict(zip(PROXY_TYPES.values(), PROXY_TYPES.keys()))
-
-_orgsocket = _orig_socket = socket.socket
-
-
-def set_self_blocking(function):
-
-    @functools.wraps(function)
-    def wrapper(*args, **kwargs):
-        self = args[0]
-        try:
-            _is_blocking = self.gettimeout()
-            if _is_blocking == 0:
-                self.setblocking(True)
-            return function(*args, **kwargs)
-        except Exception as e:
-            raise
-        finally:
-            # set orgin blocking
-            if _is_blocking == 0:
-                self.setblocking(False)
-    return wrapper
-
-
-class ProxyError(IOError):
-    """Socket_err contains original socket.error exception."""
-    def __init__(self, msg, socket_err=None):
-        self.msg = msg
-        self.socket_err = socket_err
-
-        if socket_err:
-            self.msg += ": {}".format(socket_err)
-
-    def __str__(self):
-        return self.msg
-
-
-class GeneralProxyError(ProxyError):
-    pass
-
-
-class ProxyConnectionError(ProxyError):
-    pass
-
-
-class SOCKS5AuthError(ProxyError):
-    pass
-
-
-class SOCKS5Error(ProxyError):
-    pass
-
-
-class SOCKS4Error(ProxyError):
-    pass
-
-
-class HTTPError(ProxyError):
-    pass
-
-SOCKS4_ERRORS = {
-    0x5B: "Request rejected or failed",
-    0x5C: ("Request rejected because SOCKS server cannot connect to identd on"
-           " the client"),
-    0x5D: ("Request rejected because the client program and identd report"
-           " different user-ids")
-}
-
-SOCKS5_ERRORS = {
-    0x01: "General SOCKS server failure",
-    0x02: "Connection not allowed by ruleset",
-    0x03: "Network unreachable",
-    0x04: "Host unreachable",
-    0x05: "Connection refused",
-    0x06: "TTL expired",
-    0x07: "Command not supported, or protocol error",
-    0x08: "Address type not supported"
-}
-
-DEFAULT_PORTS = {SOCKS4: 1080, SOCKS5: 1080, HTTP: 8080}
-
-
-def set_default_proxy(proxy_type=None, addr=None, port=None, rdns=True,
-                      username=None, password=None):
-    """Sets a default proxy.
-
-    All further socksocket objects will use the default unless explicitly
-    changed. All parameters are as for socket.set_proxy()."""
-    socksocket.default_proxy = (proxy_type, addr, port, rdns,
-                                username.encode() if username else None,
-                                password.encode() if password else None)
-
-
-def setdefaultproxy(*args, **kwargs):
-    if "proxytype" in kwargs:
-        kwargs["proxy_type"] = kwargs.pop("proxytype")
-    return set_default_proxy(*args, **kwargs)
-
-
-def get_default_proxy():
-    """Returns the default proxy, set by set_default_proxy."""
-    return socksocket.default_proxy
-
-getdefaultproxy = get_default_proxy
-
-
-def wrap_module(module):
-    """Attempts to replace a module's socket library with a SOCKS socket.
-
-    Must set a default proxy using set_default_proxy(...) first. This will
-    only work on modules that import socket directly into the namespace;
-    most of the Python Standard Library falls into this category."""
-    if socksocket.default_proxy:
-        module.socket.socket = socksocket
-    else:
-        raise GeneralProxyError("No default proxy specified")
-
-wrapmodule = wrap_module
-
-
-def create_connection(dest_pair,
-                      timeout=None, source_address=None,
-                      proxy_type=None, proxy_addr=None,
-                      proxy_port=None, proxy_rdns=True,
-                      proxy_username=None, proxy_password=None,
-                      socket_options=None):
-    """create_connection(dest_pair, *[, timeout], **proxy_args) -> socket object
-
-    Like socket.create_connection(), but connects to proxy
-    before returning the socket object.
-
-    dest_pair - 2-tuple of (IP/hostname, port).
-    **proxy_args - Same args passed to socksocket.set_proxy() if present.
-    timeout - Optional socket timeout value, in seconds.
-    source_address - tuple (host, port) for the socket to bind to as its source
-    address before connecting (only for compatibility)
-    """
-    # Remove IPv6 brackets on the remote address and proxy address.
-    remote_host, remote_port = dest_pair
-    if remote_host.startswith("["):
-        remote_host = remote_host.strip("[]")
-    if proxy_addr and proxy_addr.startswith("["):
-        proxy_addr = proxy_addr.strip("[]")
-
-    err = None
-
-    # Allow the SOCKS proxy to be on IPv4 or IPv6 addresses.
-    for r in socket.getaddrinfo(proxy_addr, proxy_port, 0, socket.SOCK_STREAM):
-        family, socket_type, proto, canonname, sa = r
-        sock = None
-        try:
-            sock = socksocket(family, socket_type, proto)
-
-            if socket_options:
-                for opt in socket_options:
-                    sock.setsockopt(*opt)
-
-            if isinstance(timeout, (int, float)):
-                sock.settimeout(timeout)
-
-            if proxy_type:
-                sock.set_proxy(proxy_type, proxy_addr, proxy_port, proxy_rdns,
-                               proxy_username, proxy_password)
-            if source_address:
-                sock.bind(source_address)
-
-            sock.connect((remote_host, remote_port))
-            return sock
-
-        except (socket.error, ProxyError) as e:
-            err = e
-            if sock:
-                sock.close()
-                sock = None
-
-    if err:
-        raise err
-
-    raise socket.error("gai returned empty list.")
-
-
-class _BaseSocket(socket.socket):
-    """Allows Python 2 delegated methods such as send() to be overridden."""
-    def __init__(self, *pos, **kw):
-        _orig_socket.__init__(self, *pos, **kw)
-
-        self._savedmethods = dict()
-        for name in self._savenames:
-            self._savedmethods[name] = getattr(self, name)
-            delattr(self, name)  # Allows normal overriding mechanism to work
-
-    _savenames = list()
-
-
-def _makemethod(name):
-    return lambda self, *pos, **kw: self._savedmethods[name](*pos, **kw)
-for name in ("sendto", "send", "recvfrom", "recv"):
-    method = getattr(_BaseSocket, name, None)
-
-    # Determine if the method is not defined the usual way
-    # as a function in the class.
-    # Python 2 uses __slots__, so there are descriptors for each method,
-    # but they are not functions.
-    if not isinstance(method, Callable):
-        _BaseSocket._savenames.append(name)
-        setattr(_BaseSocket, name, _makemethod(name))
-
-
-class socksocket(_BaseSocket):
-    """socksocket([family[, type[, proto]]]) -> socket object
-
-    Open a SOCKS enabled socket. The parameters are the same as
-    those of the standard socket init. In order for SOCKS to work,
-    you must specify family=AF_INET and proto=0.
-    The "type" argument must be either SOCK_STREAM or SOCK_DGRAM.
-    """
-
-    default_proxy = None
-
-    def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM,
-                 proto=0, *args, **kwargs):
-        if type not in (socket.SOCK_STREAM, socket.SOCK_DGRAM):
-            msg = "Socket type must be stream or datagram, not {!r}"
-            raise ValueError(msg.format(type))
-
-        super(socksocket, self).__init__(family, type, proto, *args, **kwargs)
-        self._proxyconn = None  # TCP connection to keep UDP relay alive
-
-        if self.default_proxy:
-            self.proxy = self.default_proxy
-        else:
-            self.proxy = (None, None, None, None, None, None)
-        self.proxy_sockname = None
-        self.proxy_peername = None
-
-        self._timeout = None
-
-    def _readall(self, file, count):
-        """Receive EXACTLY the number of bytes requested from the file object.
-
-        Blocks until the required number of bytes have been received."""
-        data = b""
-        while len(data) < count:
-            d = file.read(count - len(data))
-            if not d:
-                raise GeneralProxyError("Connection closed unexpectedly")
-            data += d
-        return data
-
-    def settimeout(self, timeout):
-        self._timeout = timeout
-        try:
-            # test if we're connected, if so apply timeout
-            peer = self.get_proxy_peername()
-            super(socksocket, self).settimeout(self._timeout)
-        except socket.error:
-            pass
-
-    def gettimeout(self):
-        return self._timeout
-
-    def setblocking(self, v):
-        if v:
-            self.settimeout(None)
-        else:
-            self.settimeout(0.0)
-
-    def set_proxy(self, proxy_type=None, addr=None, port=None, rdns=True,
-                  username=None, password=None):
-        """ Sets the proxy to be used.
-
-        proxy_type -  The type of the proxy to be used. Three types
-                        are supported: PROXY_TYPE_SOCKS4 (including socks4a),
-                        PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP
-        addr -        The address of the server (IP or DNS).
-        port -        The port of the server. Defaults to 1080 for SOCKS
-                        servers and 8080 for HTTP proxy servers.
-        rdns -        Should DNS queries be performed on the remote side
-                       (rather than the local side). The default is True.
-                       Note: This has no effect with SOCKS4 servers.
-        username -    Username to authenticate with to the server.
-                       The default is no authentication.
-        password -    Password to authenticate with to the server.
-                       Only relevant when username is also provided."""
-        self.proxy = (proxy_type, addr, port, rdns,
-                      username.encode() if username else None,
-                      password.encode() if password else None)
-
-    def setproxy(self, *args, **kwargs):
-        if "proxytype" in kwargs:
-            kwargs["proxy_type"] = kwargs.pop("proxytype")
-        return self.set_proxy(*args, **kwargs)
-
-    def bind(self, *pos, **kw):
-        """Implements proxy connection for UDP sockets.
-
-        Happens during the bind() phase."""
-        (proxy_type, proxy_addr, proxy_port, rdns, username,
-         password) = self.proxy
-        if not proxy_type or self.type != socket.SOCK_DGRAM:
-            return _orig_socket.bind(self, *pos, **kw)
-
-        if self._proxyconn:
-            raise socket.error(EINVAL, "Socket already bound to an address")
-        if proxy_type != SOCKS5:
-            msg = "UDP only supported by SOCKS5 proxy type"
-            raise socket.error(EOPNOTSUPP, msg)
-        super(socksocket, self).bind(*pos, **kw)
-
-        # Need to specify actual local port because
-        # some relays drop packets if a port of zero is specified.
-        # Avoid specifying host address in case of NAT though.
-        _, port = self.getsockname()
-        dst = ("0", port)
-
-        self._proxyconn = _orig_socket()
-        proxy = self._proxy_addr()
-        self._proxyconn.connect(proxy)
-
-        UDP_ASSOCIATE = b"\x03"
-        _, relay = self._SOCKS5_request(self._proxyconn, UDP_ASSOCIATE, dst)
-
-        # The relay is most likely on the same host as the SOCKS proxy,
-        # but some proxies return a private IP address (10.x.y.z)
-        host, _ = proxy
-        _, port = relay
-        super(socksocket, self).connect((host, port))
-        super(socksocket, self).settimeout(self._timeout)
-        self.proxy_sockname = ("0.0.0.0", 0)  # Unknown
-
-    def sendto(self, bytes, *args, **kwargs):
-        if self.type != socket.SOCK_DGRAM:
-            return super(socksocket, self).sendto(bytes, *args, **kwargs)
-        if not self._proxyconn:
-            self.bind(("", 0))
-
-        address = args[-1]
-        flags = args[:-1]
-
-        header = BytesIO()
-        RSV = b"\x00\x00"
-        header.write(RSV)
-        STANDALONE = b"\x00"
-        header.write(STANDALONE)
-        self._write_SOCKS5_address(address, header)
-
-        sent = super(socksocket, self).send(header.getvalue() + bytes, *flags,
-                                            **kwargs)
-        return sent - header.tell()
-
-    def send(self, bytes, flags=0, **kwargs):
-        if self.type == socket.SOCK_DGRAM:
-            return self.sendto(bytes, flags, self.proxy_peername, **kwargs)
-        else:
-            return super(socksocket, self).send(bytes, flags, **kwargs)
-
-    def recvfrom(self, bufsize, flags=0):
-        if self.type != socket.SOCK_DGRAM:
-            return super(socksocket, self).recvfrom(bufsize, flags)
-        if not self._proxyconn:
-            self.bind(("", 0))
-
-        buf = BytesIO(super(socksocket, self).recv(bufsize + 1024, flags))
-        buf.seek(2, SEEK_CUR)
-        frag = buf.read(1)
-        if ord(frag):
-            raise NotImplementedError("Received UDP packet fragment")
-        fromhost, fromport = self._read_SOCKS5_address(buf)
-
-        if self.proxy_peername:
-            peerhost, peerport = self.proxy_peername
-            if fromhost != peerhost or peerport not in (0, fromport):
-                raise socket.error(EAGAIN, "Packet filtered")
-
-        return (buf.read(bufsize), (fromhost, fromport))
-
-    def recv(self, *pos, **kw):
-        bytes, _ = self.recvfrom(*pos, **kw)
-        return bytes
-
-    def close(self):
-        if self._proxyconn:
-            self._proxyconn.close()
-        return super(socksocket, self).close()
-
-    def get_proxy_sockname(self):
-        """Returns the bound IP address and port number at the proxy."""
-        return self.proxy_sockname
-
-    getproxysockname = get_proxy_sockname
-
-    def get_proxy_peername(self):
-        """
-        Returns the IP and port number of the proxy.
-        """
-        return self.getpeername()
-
-    getproxypeername = get_proxy_peername
-
-    def get_peername(self):
-        """Returns the IP address and port number of the destination machine.
-
-        Note: get_proxy_peername returns the proxy."""
-        return self.proxy_peername
-
-    getpeername = get_peername
-
-    def _negotiate_SOCKS5(self, *dest_addr):
-        """Negotiates a stream connection through a SOCKS5 server."""
-        CONNECT = b"\x01"
-        self.proxy_peername, self.proxy_sockname = self._SOCKS5_request(
-            self, CONNECT, dest_addr)
-
-    def _SOCKS5_request(self, conn, cmd, dst):
-        """
-        Send SOCKS5 request with given command (CMD field) and
-        address (DST field). Returns resolved DST address that was used.
-        """
-        proxy_type, addr, port, rdns, username, password = self.proxy
-
-        writer = conn.makefile("wb")
-        reader = conn.makefile("rb", 0)  # buffering=0 renamed in Python 3
-        try:
-            # First we'll send the authentication packages we support.
-            if username and password:
-                # The username/password details were supplied to the
-                # set_proxy method so we support the USERNAME/PASSWORD
-                # authentication (in addition to the standard none).
-                writer.write(b"\x05\x02\x00\x02")
-            else:
-                # No username/password were entered, therefore we
-                # only support connections with no authentication.
-                writer.write(b"\x05\x01\x00")
-
-            # We'll receive the server's response to determine which
-            # method was selected
-            writer.flush()
-            chosen_auth = self._readall(reader, 2)
-
-            if chosen_auth[0:1] != b"\x05":
-                # Note: string[i:i+1] is used because indexing of a bytestring
-                # via bytestring[i] yields an integer in Python 3
-                raise GeneralProxyError(
-                    "SOCKS5 proxy server sent invalid data")
-
-            # Check the chosen authentication method
-
-            if chosen_auth[1:2] == b"\x02":
-                # Okay, we need to perform a basic username/password
-                # authentication.
-                if not (username and password):
-                    # Although we said we don't support authentication, the
-                    # server may still request basic username/password
-                    # authentication
-                    raise SOCKS5AuthError("No username/password supplied. "
-                                          "Server requested username/password"
-                                          " authentication")
-
-                writer.write(b"\x01" + chr(len(username)).encode()
-                             + username
-                             + chr(len(password)).encode()
-                             + password)
-                writer.flush()
-                auth_status = self._readall(reader, 2)
-                if auth_status[0:1] != b"\x01":
-                    # Bad response
-                    raise GeneralProxyError(
-                        "SOCKS5 proxy server sent invalid data")
-                if auth_status[1:2] != b"\x00":
-                    # Authentication failed
-                    raise SOCKS5AuthError("SOCKS5 authentication failed")
-
-                # Otherwise, authentication succeeded
-
-            # No authentication is required if 0x00
-            elif chosen_auth[1:2] != b"\x00":
-                # Reaching here is always bad
-                if chosen_auth[1:2] == b"\xFF":
-                    raise SOCKS5AuthError(
-                        "All offered SOCKS5 authentication methods were"
-                        " rejected")
-                else:
-                    raise GeneralProxyError(
-                        "SOCKS5 proxy server sent invalid data")
-
-            # Now we can request the actual connection
-            writer.write(b"\x05" + cmd + b"\x00")
-            resolved = self._write_SOCKS5_address(dst, writer)
-            writer.flush()
-
-            # Get the response
-            resp = self._readall(reader, 3)
-            if resp[0:1] != b"\x05":
-                raise GeneralProxyError(
-                    "SOCKS5 proxy server sent invalid data")
-
-            status = ord(resp[1:2])
-            if status != 0x00:
-                # Connection failed: server returned an error
-                error = SOCKS5_ERRORS.get(status, "Unknown error")
-                raise SOCKS5Error("{:#04x}: {}".format(status, error))
-
-            # Get the bound address/port
-            bnd = self._read_SOCKS5_address(reader)
-
-            super(socksocket, self).settimeout(self._timeout)
-            return (resolved, bnd)
-        finally:
-            reader.close()
-            writer.close()
-
-    def _write_SOCKS5_address(self, addr, file):
-        """
-        Return the host and port packed for the SOCKS5 protocol,
-        and the resolved address as a tuple object.
-        """
-        host, port = addr
-        proxy_type, _, _, rdns, username, password = self.proxy
-        family_to_byte = {socket.AF_INET: b"\x01", socket.AF_INET6: b"\x04"}
-
-        # If the given destination address is an IP address, we'll
-        # use the IP address request even if remote resolving was specified.
-        # Detect whether the address is IPv4/6 directly.
-        for family in (socket.AF_INET, socket.AF_INET6):
-            try:
-                addr_bytes = socket.inet_pton(family, host)
-                file.write(family_to_byte[family] + addr_bytes)
-                host = socket.inet_ntop(family, addr_bytes)
-                file.write(struct.pack(">H", port))
-                return host, port
-            except socket.error:
-                continue
-
-        # Well it's not an IP number, so it's probably a DNS name.
-        if rdns:
-            # Resolve remotely
-            host_bytes = host.encode("idna")
-            file.write(b"\x03" + chr(len(host_bytes)).encode() + host_bytes)
-        else:
-            # Resolve locally
-            addresses = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
-                                           socket.SOCK_STREAM,
-                                           socket.IPPROTO_TCP,
-                                           socket.AI_ADDRCONFIG)
-            # We can't really work out what IP is reachable, so just pick the
-            # first.
-            target_addr = addresses[0]
-            family = target_addr[0]
-            host = target_addr[4][0]
-
-            addr_bytes = socket.inet_pton(family, host)
-            file.write(family_to_byte[family] + addr_bytes)
-            host = socket.inet_ntop(family, addr_bytes)
-        file.write(struct.pack(">H", port))
-        return host, port
-
-    def _read_SOCKS5_address(self, file):
-        atyp = self._readall(file, 1)
-        if atyp == b"\x01":
-            addr = socket.inet_ntoa(self._readall(file, 4))
-        elif atyp == b"\x03":
-            length = self._readall(file, 1)
-            addr = self._readall(file, ord(length))
-        elif atyp == b"\x04":
-            addr = socket.inet_ntop(socket.AF_INET6, self._readall(file, 16))
-        else:
-            raise GeneralProxyError("SOCKS5 proxy server sent invalid data")
-
-        port = struct.unpack(">H", self._readall(file, 2))[0]
-        return addr, port
-
-    def _negotiate_SOCKS4(self, dest_addr, dest_port):
-        """Negotiates a connection through a SOCKS4 server."""
-        proxy_type, addr, port, rdns, username, password = self.proxy
-
-        writer = self.makefile("wb")
-        reader = self.makefile("rb", 0)  # buffering=0 renamed in Python 3
-        try:
-            # Check if the destination address provided is an IP address
-            remote_resolve = False
-            try:
-                addr_bytes = socket.inet_aton(dest_addr)
-            except socket.error:
-                # It's a DNS name. Check where it should be resolved.
-                if rdns:
-                    addr_bytes = b"\x00\x00\x00\x01"
-                    remote_resolve = True
-                else:
-                    addr_bytes = socket.inet_aton(
-                        socket.gethostbyname(dest_addr))
-
-            # Construct the request packet
-            writer.write(struct.pack(">BBH", 0x04, 0x01, dest_port))
-            writer.write(addr_bytes)
-
-            # The username parameter is considered userid for SOCKS4
-            if username:
-                writer.write(username)
-            writer.write(b"\x00")
-
-            # DNS name if remote resolving is required
-            # NOTE: This is actually an extension to the SOCKS4 protocol
-            # called SOCKS4A and may not be supported in all cases.
-            if remote_resolve:
-                writer.write(dest_addr.encode("idna") + b"\x00")
-            writer.flush()
-
-            # Get the response from the server
-            resp = self._readall(reader, 8)
-            if resp[0:1] != b"\x00":
-                # Bad data
-                raise GeneralProxyError(
-                    "SOCKS4 proxy server sent invalid data")
-
-            status = ord(resp[1:2])
-            if status != 0x5A:
-                # Connection failed: server returned an error
-                error = SOCKS4_ERRORS.get(status, "Unknown error")
-                raise SOCKS4Error("{:#04x}: {}".format(status, error))
-
-            # Get the bound address/port
-            self.proxy_sockname = (socket.inet_ntoa(resp[4:]),
-                                   struct.unpack(">H", resp[2:4])[0])
-            if remote_resolve:
-                self.proxy_peername = socket.inet_ntoa(addr_bytes), dest_port
-            else:
-                self.proxy_peername = dest_addr, dest_port
-        finally:
-            reader.close()
-            writer.close()
-
-    def _negotiate_HTTP(self, dest_addr, dest_port):
-        """Negotiates a connection through an HTTP server.
-
-        NOTE: This currently only supports HTTP CONNECT-style proxies."""
-        proxy_type, addr, port, rdns, username, password = self.proxy
-
-        # If we need to resolve locally, we do this now
-        addr = dest_addr if rdns else socket.gethostbyname(dest_addr)
-
-        http_headers = [
-            (b"CONNECT " + addr.encode("idna") + b":"
-             + str(dest_port).encode() + b" HTTP/1.1"),
-            b"Host: " + dest_addr.encode("idna")
-        ]
-
-        if username and password:
-            http_headers.append(b"Proxy-Authorization: basic "
-                                + b64encode(username + b":" + password))
-
-        http_headers.append(b"\r\n")
-
-        self.sendall(b"\r\n".join(http_headers))
-
-        # We just need the first line to check if the connection was successful
-        fobj = self.makefile()
-        status_line = fobj.readline()
-        fobj.close()
-
-        if not status_line:
-            raise GeneralProxyError("Connection closed unexpectedly")
-
-        try:
-            proto, status_code, status_msg = status_line.split(" ", 2)
-        except ValueError:
-            raise GeneralProxyError("HTTP proxy server sent invalid response")
-
-        if not proto.startswith("HTTP/"):
-            raise GeneralProxyError(
-                "Proxy server does not appear to be an HTTP proxy")
-
-        try:
-            status_code = int(status_code)
-        except ValueError:
-            raise HTTPError(
-                "HTTP proxy server did not return a valid HTTP status")
-
-        if status_code != 200:
-            error = "{}: {}".format(status_code, status_msg)
-            if status_code in (400, 403, 405):
-                # It's likely that the HTTP proxy server does not support the
-                # CONNECT tunneling method
-                error += ("\n[*] Note: The HTTP proxy server may not be"
-                          " supported by PySocks (must be a CONNECT tunnel"
-                          " proxy)")
-            raise HTTPError(error)
-
-        self.proxy_sockname = (b"0.0.0.0", 0)
-        self.proxy_peername = addr, dest_port
-
-    _proxy_negotiators = {
-                           SOCKS4: _negotiate_SOCKS4,
-                           SOCKS5: _negotiate_SOCKS5,
-                           HTTP: _negotiate_HTTP
-                         }
-
-    @set_self_blocking
-    def connect(self, dest_pair, catch_errors=None):
-        """
-        Connects to the specified destination through a proxy.
-        Uses the same API as socket's connect().
-        To select the proxy server, use set_proxy().
-
-        dest_pair - 2-tuple of (IP/hostname, port).
-        """
-        if len(dest_pair) != 2 or dest_pair[0].startswith("["):
-            # Probably IPv6, not supported -- raise an error, and hope
-            # Happy Eyeballs (RFC6555) makes sure at least the IPv4
-            # connection works...
-            raise socket.error("PySocks doesn't support IPv6: %s"
-                               % str(dest_pair))
-
-        dest_addr, dest_port = dest_pair
-
-        if self.type == socket.SOCK_DGRAM:
-            if not self._proxyconn:
-                self.bind(("", 0))
-            dest_addr = socket.gethostbyname(dest_addr)
-
-            # If the host address is INADDR_ANY or similar, reset the peer
-            # address so that packets are received from any peer
-            if dest_addr == "0.0.0.0" and not dest_port:
-                self.proxy_peername = None
-            else:
-                self.proxy_peername = (dest_addr, dest_port)
-            return
-
-        (proxy_type, proxy_addr, proxy_port, rdns, username,
-         password) = self.proxy
-
-        # Do a minimal input check first
-        if (not isinstance(dest_pair, (list, tuple))
-                or len(dest_pair) != 2
-                or not dest_addr
-                or not isinstance(dest_port, int)):
-            # Inputs failed, raise an error
-            raise GeneralProxyError(
-                "Invalid destination-connection (host, port) pair")
-
-        # We set the timeout here so that we don't hang in connection or during
-        # negotiation.
-        super(socksocket, self).settimeout(self._timeout)
-
-        if proxy_type is None:
-            # Treat like regular socket object
-            self.proxy_peername = dest_pair
-            super(socksocket, self).settimeout(self._timeout)
-            super(socksocket, self).connect((dest_addr, dest_port))
-            return
-
-        proxy_addr = self._proxy_addr()
-
-        try:
-            # Initial connection to proxy server.
-            super(socksocket, self).connect(proxy_addr)
-
-        except socket.error as error:
-            # Error while connecting to proxy
-            self.close()
-            if not catch_errors:
-                proxy_addr, proxy_port = proxy_addr
-                proxy_server = "{}:{}".format(proxy_addr, proxy_port)
-                printable_type = PRINTABLE_PROXY_TYPES[proxy_type]
-
-                msg = "Error connecting to {} proxy {}".format(printable_type,
-                                                                    proxy_server)
-                log.debug("%s due to: %s", msg, error)
-                raise ProxyConnectionError(msg, error)
-            else:
-                raise error
-
-        else:
-            # Connected to proxy server, now negotiate
-            try:
-                # Calls negotiate_{SOCKS4, SOCKS5, HTTP}
-                negotiate = self._proxy_negotiators[proxy_type]
-                negotiate(self, dest_addr, dest_port)
-            except socket.error as error:
-                if not catch_errors:
-                    # Wrap socket errors
-                    self.close()
-                    raise GeneralProxyError("Socket error", error)
-                else:
-                    raise error
-            except ProxyError:
-                # Protocol error while negotiating with proxy
-                self.close()
-                raise
-                
-    @set_self_blocking
-    def connect_ex(self, dest_pair):
-        """ https://docs.python.org/3/library/socket.html#socket.socket.connect_ex
-        Like connect(address), but return an error indicator instead of raising an exception for errors returned by the C-level connect() call (other problems, such as "host not found" can still raise exceptions).
-        """
-        try:
-            self.connect(dest_pair, catch_errors=True)
-            return 0
-        except OSError as e:
-            # If the error is numeric (socket errors are numeric), then return number as 
-            # connect_ex expects. Otherwise raise the error again (socket timeout for example)
-            if e.errno:
-                return e.errno
-            else:
-                raise
-
-    def _proxy_addr(self):
-        """
-        Return proxy address to connect to as tuple object
-        """
-        (proxy_type, proxy_addr, proxy_port, rdns, username,
-         password) = self.proxy
-        proxy_port = proxy_port or DEFAULT_PORTS.get(proxy_type)
-        if not proxy_port:
-            raise GeneralProxyError("Invalid proxy type")
-        return proxy_addr, proxy_port
diff --git a/apps/bitwarden_event_logs/lib/sockshandler.py b/apps/bitwarden_event_logs/lib/sockshandler.py
deleted file mode 100755
index 6a2ed81c..00000000
--- a/apps/bitwarden_event_logs/lib/sockshandler.py
+++ /dev/null
@@ -1,111 +0,0 @@
-#!/usr/bin/env python
-"""
-SocksiPy + urllib2 handler
-
-version: 0.3
-author: e
-
-This module provides a Handler which you can use with urllib2 to allow it to tunnel your connection through a socks.sockssocket socket, with out monkey patching the original socket...
-"""
-import socket
-import ssl
-
-try:
-    import urllib2
-    import httplib
-except ImportError: # Python 3
-    import urllib.request as urllib2
-    import http.client as httplib
-
-import socks # $ pip install PySocks
-
-def merge_dict(a, b):
-    d = a.copy()
-    d.update(b)
-    return d
-
-def is_ip(s):
-    try:
-        if ':' in s:
-            socket.inet_pton(socket.AF_INET6, s)
-        elif '.' in s:
-            socket.inet_aton(s)
-        else:
-            return False
-    except:
-        return False
-    else:
-        return True
-
-socks4_no_rdns = set()
-
-class SocksiPyConnection(httplib.HTTPConnection):
-    def __init__(self, proxytype, proxyaddr, proxyport=None, rdns=True, username=None, password=None, *args, **kwargs):
-        self.proxyargs = (proxytype, proxyaddr, proxyport, rdns, username, password)
-        httplib.HTTPConnection.__init__(self, *args, **kwargs)
-
-    def connect(self):
-        (proxytype, proxyaddr, proxyport, rdns, username, password) = self.proxyargs
-        rdns = rdns and proxyaddr not in socks4_no_rdns
-        while True:
-            try:
-                sock = socks.create_connection(
-                    (self.host, self.port), self.timeout, None,
-                    proxytype, proxyaddr, proxyport, rdns, username, password,
-                    ((socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),))
-                break
-            except socks.SOCKS4Error as e:
-                if rdns and "0x5b" in str(e) and not is_ip(self.host):
-                    # Maybe a SOCKS4 server that doesn't support remote resolving
-                    # Let's try again
-                    rdns = False
-                    socks4_no_rdns.add(proxyaddr)
-                else:
-                    raise
-        self.sock = sock
-
-class SocksiPyConnectionS(httplib.HTTPSConnection):
-    def __init__(self, proxytype, proxyaddr, proxyport=None, rdns=True, username=None, password=None, *args, **kwargs):
-        self.proxyargs = (proxytype, proxyaddr, proxyport, rdns, username, password)
-        httplib.HTTPSConnection.__init__(self, *args, **kwargs)
-
-    def connect(self):
-        SocksiPyConnection.connect(self)
-        self.sock = self._context.wrap_socket(self.sock, server_hostname=self.host)
-        if not self._context.check_hostname and self._check_hostname:
-            try:
-                ssl.match_hostname(self.sock.getpeercert(), self.host)
-            except Exception:
-                self.sock.shutdown(socket.SHUT_RDWR)
-                self.sock.close()
-                raise
-
-class SocksiPyHandler(urllib2.HTTPHandler, urllib2.HTTPSHandler):
-    def __init__(self, *args, **kwargs):
-        self.args = args
-        self.kw = kwargs
-        urllib2.HTTPHandler.__init__(self)
-
-    def http_open(self, req):
-        def build(host, port=None, timeout=0, **kwargs):
-            kw = merge_dict(self.kw, kwargs)
-            conn = SocksiPyConnection(*self.args, host=host, port=port, timeout=timeout, **kw)
-            return conn
-        return self.do_open(build, req)
-
-    def https_open(self, req):
-        def build(host, port=None, timeout=0, **kwargs):
-            kw = merge_dict(self.kw, kwargs)
-            conn = SocksiPyConnectionS(*self.args, host=host, port=port, timeout=timeout, **kw)
-            return conn
-        return self.do_open(build, req)
-
-if __name__ == "__main__":
-    import sys
-    try:
-        port = int(sys.argv[1])
-    except (ValueError, IndexError):
-        port = 9050
-    opener = urllib2.build_opener(SocksiPyHandler(socks.PROXY_TYPE_SOCKS5, "localhost", port))
-    print("HTTP: " + opener.open("http://httpbin.org/ip").read().decode())
-    print("HTTPS: " + opener.open("https://httpbin.org/ip").read().decode())
diff --git a/apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/INSTALLER b/apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/INSTALLER
deleted file mode 100755
index a1b589e3..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/INSTALLER
+++ /dev/null
@@ -1 +0,0 @@
-pip
diff --git a/apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/LICENSE b/apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/LICENSE
deleted file mode 100755
index d13065d5..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
-                                 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 2021 Splunk Inc.
-
-   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/apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/METADATA b/apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/METADATA
deleted file mode 100755
index 542ab03f..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/METADATA
+++ /dev/null
@@ -1,28 +0,0 @@
-Metadata-Version: 2.1
-Name: solnlib
-Version: 7.0.0
-Summary: The Splunk Software Development Kit for Splunk Solutions
-Home-page: https://github.com/splunk/addonfactory-solutions-library-python
-License: Apache-2.0
-Keywords: splunk,ucc
-Author: Splunk
-Author-email: addonfactory@splunk.com
-Requires-Python: >=3.7,<3.14
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-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: Topic :: Software Development :: Code Generators
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
-Requires-Dist: defusedxml (>=0.7)
-Requires-Dist: sortedcontainers (>=2)
-Requires-Dist: splunk-sdk (>=2.0.2)
-Project-URL: Repository, https://github.com/splunk/addonfactory-solutions-library-python
diff --git a/apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/RECORD b/apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/RECORD
deleted file mode 100755
index 1cceb35c..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/RECORD
+++ /dev/null
@@ -1,40 +0,0 @@
-solnlib-7.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-solnlib-7.0.0.dist-info/LICENSE,sha256=Xvvd894DEl8lUHPEeFU-Ya18RW7Hc2IlPTZS_bE3Hgs,11341
-solnlib-7.0.0.dist-info/METADATA,sha256=K6kaGCMmzvwPa45GhHTWfT5d-ckfTwYYqS3Kg2NxCkM,1254
-solnlib-7.0.0.dist-info/RECORD,,
-solnlib-7.0.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-solnlib-7.0.0.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
-solnlib/__init__.py,sha256=I4q1InhCUhAyPjqhRQCbgZ9Ya3CndtnjJyTkRtfep10,1302
-solnlib/_settings.py,sha256=GIIg9GB8AKSfIDq1J3Qszq_RzUCdPxNsGsMmSBFtd0A,678
-solnlib/_utils.py,sha256=3f4ggQuURl8VkCh517w_hnhtYDfNW-Fm5bPxMPyy2yA,2739
-solnlib/acl.py,sha256=RY99GZcF8VUAOc9p7oWpYssidttDuapXok5-ZG2jg3o,5905
-solnlib/alerts_rest_client.py,sha256=4jc9gF0tID3aeNxfaCSDMwOPo-VMz8zlxqoBjUhM9X4,8681
-solnlib/bulletin_rest_client.py,sha256=ZLMHnN2bIw0TB2FXyf-2Y-I6EQsK-V3vPCj5sNdG4uE,5942
-solnlib/concurrent/concurrent_executor.py,sha256=H1uYRz82WZ3Fpswq_1jmJ0UGPgNy4EbzfZlgoTU5jIY,3485
-solnlib/concurrent/process_pool.py,sha256=aci4XRKKuwvx5Xu4pa4cqqgRgMyKw6NqtzTzieAz9Cs,2192
-solnlib/concurrent/thread_pool.py,sha256=tFuNjndBihMApgZzDXePDzauptGT9B2Pm4ffE7Z3e68,10466
-solnlib/conf_manager.py,sha256=EtJcvTCtLxFj1uKeLryj76R5VnXTLsV_DCTMyhLevpU,19344
-solnlib/credentials.py,sha256=0Pbn-Cth6rTpHyBtL5uFL7gC2zz2A-525O1nGJ2XpoA,12098
-solnlib/file_monitor.py,sha256=QeMAm5anPy7jf7M22mOvsCnn1gD7-IHQ_R-036sIo9w,3978
-solnlib/hec_config.py,sha256=DeCkxqazb0G1fNhcjfEdc9cTZBHTN8pRMLUFkcjPs50,5054
-solnlib/log.py,sha256=w3AGpL8VjHW2FP3V-bOGym0zm2iAAiG2_ciD95WIqSc,11933
-solnlib/modular_input/__init__.py,sha256=2ZaLwmHL7DQSxdvlpr_DIBWRRPzmP1UaKdE8Ixrm-Rc,1190
-solnlib/modular_input/checkpointer.py,sha256=dpiueA_QsFj3lpKCKtNomGLqEkpVm-LD7R5UuOwt3Y4,8692
-solnlib/modular_input/event.py,sha256=YNbIVpHom8Ya6TmY_NsBjej5_tqChmGAM1WQqq-iKik,7873
-solnlib/modular_input/event_writer.py,sha256=nNLh48zyHQtDyK6VOBvx2SP97ctPjrhvp2RiobL6wes,17148
-solnlib/modular_input/modinput.py,sha256=C4ctMCqLylbv0IYC-dyFdowmAckJFtaQA4fnTa8cpVs,5150
-solnlib/modular_input/modular_input.py,sha256=QuFR0O5Fbf89re-FG4i67p2inxn9aZIDZ-hYIJDYR6c,18048
-solnlib/net_utils.py,sha256=LkWGYcRgj-B7wpP8WIAgGl6yFuDAwnNXVyTnG6D-rEE,4063
-solnlib/orphan_process_monitor.py,sha256=vK_Lz8VIj59EvHcr475m4ZI29jlLtkZaPVqtG3xSMoM,3218
-solnlib/pattern.py,sha256=lS_jzA3efz1XZlgCZ0kbPbOniPgC7hy-aVXQJnjIbOE,1162
-solnlib/rest.py,sha256=KISfYwRchp_LyM5Hq3LZP6PqR9SzUSiIpqwY4tAEphU,2945
-solnlib/schedule/job.py,sha256=gzGf-EVztTQtUqUvpKyZCo_UHWTYXudPSeG4l1U-jVk,3060
-solnlib/schedule/scheduler.py,sha256=LNcub_Hvm_E6X7phxlfitO2Lq0IrqDsHvNSqU0CHjOE,4719
-solnlib/server_info.py,sha256=wJFZuyDI-A9bR-qFA5BdKgIvlk2x5AepOMjEJ3-dCR8,9319
-solnlib/soln_exceptions.py,sha256=TXUAVom2F5xqU0fspKG-cq5vCxFnJila_xbXMlWkAAI,1019
-solnlib/splunk_rest_client.py,sha256=7yiCtyM6oOhPnu4gFR_lhaWV9hmZBtKCgijRL0hyEKY,7809
-solnlib/splunkenv.py,sha256=E8bfYOFVQOl5BRyE05wQbZNTimFrqlIbWFRJsknVYws,12475
-solnlib/time_parser.py,sha256=vQ90V20ZhpFTt52YfIeWiNLXvUIvyQgK3fL2gv67jE8,4668
-solnlib/timer_queue.py,sha256=ehmAH2zgTzpixiXcnC4Cn7X3B1vqBIu4u4pQ4NaA5rM,9972
-solnlib/user_access.py,sha256=paouy4pV4svGe4dS6XEXidPdvBg8llkwT_59hqOPsxM,29175
-solnlib/utils.py,sha256=oNxr6oU0mR1cIO6j7TyCjb4KkPE-6RuqfM62ABT6xn4,5887
diff --git a/apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/REQUESTED b/apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/REQUESTED
deleted file mode 100755
index e69de29b..00000000
diff --git a/apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/WHEEL b/apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/WHEEL
deleted file mode 100755
index 258a6ff3..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib-7.0.0.dist-info/WHEEL
+++ /dev/null
@@ -1,4 +0,0 @@
-Wheel-Version: 1.0
-Generator: poetry-core 1.6.1
-Root-Is-Purelib: true
-Tag: py3-none-any
diff --git a/apps/bitwarden_event_logs/lib/solnlib/__init__.py b/apps/bitwarden_event_logs/lib/solnlib/__init__.py
deleted file mode 100755
index 32596d9b..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/__init__.py
+++ /dev/null
@@ -1,59 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""The Splunk Software Development Kit for Solutions."""
-
-from . import (
-    acl,
-    bulletin_rest_client,
-    conf_manager,
-    credentials,
-    file_monitor,
-    hec_config,
-    log,
-    net_utils,
-    orphan_process_monitor,
-    pattern,
-    server_info,
-    splunk_rest_client,
-    splunkenv,
-    time_parser,
-    timer_queue,
-    user_access,
-    utils,
-)
-
-__all__ = [
-    "acl",
-    "bulletin_rest_client",
-    "conf_manager",
-    "credentials",
-    "file_monitor",
-    "hec_config",
-    "log",
-    "net_utils",
-    "orphan_process_monitor",
-    "pattern",
-    "server_info",
-    "splunk_rest_client",
-    "splunkenv",
-    "time_parser",
-    "timer_queue",
-    "user_access",
-    "utils",
-]
-
-__version__ = "7.0.0"
diff --git a/apps/bitwarden_event_logs/lib/solnlib/__pycache__/__init__.cpython-39.pyc b/apps/bitwarden_event_logs/lib/solnlib/__pycache__/__init__.cpython-39.pyc
deleted file mode 100755
index e87c91d8f217d2a10293f1f8faf32be2da3ac61b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 661
zcmY+ByRH*45QaCq+1xgF!l8{sm1g&d<}?UVP#}S5Lgi*~cAdn+V=uNhi1Ro=!z;O^
z;uWayoJa^P?PtyGjOX(6*=&-;^Yq(yd6Or}Pcr^JX*6EO&wYq!5|%W*CRII5S&A9U
zu+RFKvm6I(fI~LK5gXx{jd8*zSg-=8Y>G2B!#SJdf-P{#mbhXoT(fmAIVDZ{wZ|Sc
z8QV1deX`5%pS?ee?Y>o={IZp?cj-w3_WqPdRW%uxz;YVT923IU0X>h
ze5!s{e1GYCjVTLq4nurKk`_(QjMmG+^p2vYJw?$W?WSYIrorGXatd+U`af8ckK@Jd
OU+TN-saol+&gvfl3Qp$$

diff --git a/apps/bitwarden_event_logs/lib/solnlib/__pycache__/_utils.cpython-39.pyc b/apps/bitwarden_event_logs/lib/solnlib/__pycache__/_utils.cpython-39.pyc
deleted file mode 100755
index ad522533993039f9632a4d3ec731db7cba5540e3..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 2212
zcmaJ?L5~|X6t-t3lgT96OU`*<>qKwKZ?-=V$x9_r3QVRjVO__U>=b
z`5!R)%T7+VDoj3yPyGUfqBf$KItgxL_`8WSa@+36YkQ+YyD;+GKDO&#QXB>CU{q?C
z@_8W%N9A@oANxsVRBcx=VpSFlHhP#Ak1#E>&Ab=b=6S?krar9Hy3To&uXsRRRvMh5
z!PAhIX!t|nF>arx&MvA~W|#N+T#->qCkZ2CnI3S;$VBr*5#5h8iKJ!ogGe)?Q=-yD
zBz(W|H`s=D>Yj10h^g_e@mQPS##r-IM2RWf7BHwgrnt`q<)UZ&IN?m_IyR@(IGKoH
zM>3^5D{etDEvK*?vWGFt&r$WzY{qGlu-G2k5z&a5vSLcv*^o_*8;!@NkRA#qjj!T9
z0|Bp}Ds8-RDz)*uoF!D5Vw?)i9s&bynMe@_R{LeMa9KdN;ZuJC(P)4(oH<=5b2Be1
zWIn}Dy)1Z+pWuNrFKPFgM;+>B=vQ}+X0@y|C}fCwM;N28k+|t0?GK96Cm78`jAoa!
za1hYK(3N)|p{z*#36}k=C>)OFMc7-)EVp>%*xlbC>d(XPLlG|iTWAY`ObF)(M|fV&
z%Gc1nbMs188I&_LbmTJ_RUCxTE_(1xS1FwK6QbeI?qmE2Qy(BMM>{S;UouVBz9cl#
z(M7_$fH4DrAp!(oJBj-zg+3y&WPrOOsbGyTB+#zN-ed$os#ZR=to5-uL@jc4g&DD!
zk+e&;?~q-c!c72DJRGMS@Q{tw5hp-xk=;B#NEF8aHN@PIiM1pk-Xd4VK@jy?iBdkxdD;9CsBD%ack&(?TMY^dxcDqeWvPjD%rgY=0y}g@X
zNSVqOu|vY_m{8vBLP!Zsx-uP+OIw#N=Et*#(P#`M=98_D$zh*~6pz;
zBoY)NQxk|oI?d}3lLri@4?`uIMP>o%Mm%A(QQwfZMw!|o8E%2GSMvgDio*kCGs!scxoWa+Trg1jR=Tu;1TNB%
zo+zlhc?ugZ;Y!OltdwVy-vq}Vt5;%&M=COYf@f~1qAJ99{@q`c-VX4;NXRaofm<(fgN0f9%9F-;rE;h4q#kyq!q3gc5JJk
zF~v@Yrg5hu-vqYt^>hruCLNeasFg^1mK5w-X<-1Umb+LMEwi}PGRLT0J$@5&m1W89
zecOt;;Bn(ZsWWG{@8sq0>Z&qc11oZ+a%Y_&7wpd9)5U}QnT;*g0OC0TKIhcHpo&{5
VK)qk|e;qK`c0cbMKppJ;^FNXdbyffX

diff --git a/apps/bitwarden_event_logs/lib/solnlib/__pycache__/acl.cpython-39.pyc b/apps/bitwarden_event_logs/lib/solnlib/__pycache__/acl.cpython-39.pyc
deleted file mode 100755
index c65174b8d6530c2c43111a62eff058f5a18136fa..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 5123
zcmdT|OOM;u73NDXN;4jh?X-3uzI7fmO2<-AAVn~647(E}HIm4NJ#J7H1&5=1rJ+ob
z>Lul|R8AJQw=S~is_E}VTf_DGpTF=L7n-_LgxzpQ5%G7O^xf^@b#vSyXhM}vuWxyX8Bex*UX`v
z<9R>dD>RF$UGPgiyJ@3cqrsBPRsX1K&x_6#B!Kcz~b8dmgp1D
z)JE?UH*h;bwutTgMcl67AFY504W`kW2G^UWQpS`843ExFc`@mFk>G51NK%fO)*4kS
zEjmu%_Jrf4w&P^Vqg`>F-wj-UJX3HS9=06kFPdBeUqzm8)x&;V&$OwFxK(%i{iwd{
z#rv-0B5=e#sOI=#hX#XstycHF-Fg)I0o~n}U+WK*m_H^p=*Ww@WgJnogo!J(q7Sm5
z=(4b;znsW(>q&w9;8Ctw5Jgey+8v!29_v_>=fx)mFLC>k^-yn?=V$o|#iJrBSXF+K
z;}yR2D2G)`+=N%G$RZx=z6*21ZmgG`M$NVvu50(*Uf&ndrs`qf^5x6S>nZmb>sf=I
z%#B!t`}`{E_t^Vtyf!otARTLkKO=@wXUA%iP1I^GqI9flIQvpD=B=T|3ObQ$H;=g{X35Ddf>&L>w6;v^334l
zlO*M?%Fdt%mz^D_Qh843O_m)9b6bD|;6Zp-1hoYtL90!6wclqHIV^0ed(;PfEUr_E
zZ?biS5RTjRtLz4uVqwPyt6PfX;?W*68r!r%o-$mT~F
z(4Z3iC7bD}j%-%`9}IfCLcV1C_>4E%jd%~rqucg6g9)O-nI{G(uw)rn&J0ks<}xXsO1i>Va}zby$!gYwZLOWyWhHm`JFhS|}+$tDfk50`w+++_x-xd?7kE#az-i`OtJQBe`1}%fQ
zjW5DRN|;;tM*+y_T%vWA>r-NUY;=u7^Qm@Ve5wVPEG@RWx#6#L?Epz~bRjXjd2Ze{
z<$DjbxWKJMe`q|=Kh=JXqj=O9aq|
zBBsD07iING$a@}wpmDqY`yJ)XI|pdN{nedR**SQ5aM8L=b`zA?5_c(lB-DEX!S(G=
z$sm|Q^p>*N3t@YeajylRnWS;>o)hO5Wy2SD%%$EDZlok|xUT_b{!RHtpfqoK%
z;D3zkxV<{8Pk1OSMRFisFGctas;8-erB*iz19(?C9K`)WOgUXV`y&qU`I+mP9!vzh
zAEL0?WN${bMw$ca$)Us&4Y=I$tG4{UY9qU(7QuIF5!OqSMoYik-oANF%8-CNcH=>$
zvbdx)p62|}XG?XEE5U09$A%G-7cmw~4!bw{-^mF=(ZtI4@jM4I3%s`H6
zPH|JQncB1bla}W)kNgqXv~t93e3fyZhRzjenCOS79G~hDQ!O|oHO0xAiDdF`iP8Tno?@TX@J#!XhIU!C%a^p#Izk@-p6GGDTWD(@
z)=;*iy5+IiMF0Z`N&tc@0D^qi{#+lf0}4Ij}#o
z0S4->z@T!tl#~DlCjkVj_@7c>aN6C1f$yjqVuA;+3%JnrCi8w}s*t-6{JB;xBSbAX0SzCrZr9o>TB}AV?hW}7TyaLiG
zoq?tR(}NoOis>Taw-{zBE(a5rBbHf
zM;hcuc;ek=ym)dLnGwN#Vpf)oAba)&>Pwh-I7`PQmlA!UAZUpF@gZgazfchzU)0Qk
zdw~;W?dBM?FqVe!7QI9MN-ce44?&xR7<>n368^Gz0kM`PgePfhm
zLglEfz)sSyuwLDLR5%~#7LtdaJl>_p1km_<;
zga{Da*fD35h@X66{K386$n|my+KnK
zL7GJhNdoLf-|~i+&D*&m+s4u6|z|
z5OwbNRpop|U;RPFQnUY@n|)qAp(M)6vwS@4BRi<4HcQ#J8+?t)j!u(QIZ1qwLmfAW
zJwH+sr}+8t()4pu<_7dSew^58@ly67=`y`esV`KPQPe9ia0RqzuH^p>9zs)w

diff --git a/apps/bitwarden_event_logs/lib/solnlib/__pycache__/bulletin_rest_client.cpython-39.pyc b/apps/bitwarden_event_logs/lib/solnlib/__pycache__/bulletin_rest_client.cpython-39.pyc
deleted file mode 100755
index 0ddaab6120f101a55cb1239ce19380acd81b6455..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 5829
zcmd5=-)|eo5x!siA&Hh{C2{K}O*c*&nTYmQCVPUN<{n%BzJa^~KR
z^0hqons%*l-Qqd!-M6@Ru;yJ?m4|9}LmY^U?89d+W{z7$IP^(YjvE<0)QMq7tT
z97L)2(@^R4bpC25ii8g14UAhc!|Ehq^vL7pW1Q;uXoOX>xm9zxU30lp^SBF5a?r%%
zIcSoDCV5dXzaq~+^mu_6?|Zc=k>8!>Q+%r7;#pqIh>|FrvCdd@b;jb;7&*gB_j9!q
z`~;u9Z`Ee`Nj``Fll&B)$N!u-B})92hcQW6f8b1b5N`M-p1
zRd-bqAHl`OydUFK-$rv3ml}Kn{c_#cGYt+gu55}uAwzwTdSTp1Qg1(yahelSCUU);
zPm8`E2QA_I>9p^+65fgEy5#$JIzcpe;;pU!_(odz?8?Ua+WPf0x4N-$W8;Y>PeL2h
ze7#jo+Pc~ggsRZ>Doj$hUCz$W*x`*-EK|ZAWpWsv%
z(e&(|vEEfALGdl~HMRt&MSgWHnYs+LW(VP@&^?
z7;~01*xI$q*bHk8^SYJ?Oeg)R@P3@=9U-$B*nX&Y7I^N&?waw^{^4vvuVfGAGkUef)i-RxQe>
zy$J6mcBS3!FE>m`)2nt+7voX~WVOU@P{2?aHo|5{nzW!yi1gK>$kBrUJd_|`1(ku&
zI_&H$cXLLHY^X%A+l)bqQ3U}f0w5YNiRjLQ=U^|RJtU{$ZEJLB{#Ms$ENr283
zoN5mZ_?Z~oXHFI&^7kPN$H5+b*xvBVar7+F|k
z3sjEKzbQx-4&#}opaWdDFB2q>Y(65=W{7%Xk$oCO9kEK4OdlM`mqDlm9~UzkQ&?T3
z0Wx9&P9A-Ng%7Uopj=b^9E|h?ha_w-M6M=x41w-zcG#|DL3_|&+bn?WJyZr9(J`S6
zy|qOdBp7fMQ#(l~;>d?t)<@!{kcyBs2~4FWl4n&h;@vToKl*$DcoKSgKUugnZZ%B=
z@K8Y{@%tuNh}MS|9}tePmM^kTwzh7vOXttCt5>eEjn$w3g6dmww=PTt|3YZ9Nitg?
z(-aSkYW~lm`K_0Al3D8G-Z5P-?-sTJw)o#Wz-R^JDN+dffVaZ<<-n&`|BqlNx~E)}
z<`AnLA>RO`AnFC0v{g&JcA|88>SwE)n^&%{Zu+b1*KXceTi=qD
zHIb~(kl@#;A@JpQsd9pjO4$#vRqW`~Olq`H2_HTxxG+%J)9DYJfQ{l)#l?2&fep3NQZ
zK5%xuN4dw=7mm(9D)gMkHnOGHty~xSNL4$iOu>EFvk)g{xvuiHT}?gO3O}*anWUrJ
z9Zj24Q7+1J(53GmgRl$O4O)m+_)orv$7wD~0EML4w(ATF$(E+U~x23|=F{$G~#%?hq`BE`S7>&^jFO037#`2XJhiL-}DFaNO=&
z1P;oBEW?-`1!bJ=!TPEgv*aQKmJ24bBssp1g$b1^-IDF{JPmUCkwxyBGNgmqifAkcar3O?cHe%E#iMT>F`MFKg+yg$AB~Ymh>^!ODk!su?q0Y?yS<~Eo#%&z
z`~fCF**F;ld9!a6nb{XGYSQf4{*cV-2c#)^iky7xk{BP4?_7B)bVtT@@^N<1_0aGi
z;YP#b_iSx8v5?pa*@0A5v_l}X=%VgHvD^E=dUlFo0Ogbo;nWo
zOxyXVr4NhwVxfp+Ql3f+gD-ZH;$EJndGo*%<*7W>&M+`6Bew9(gvV);7MJ^98!wZ^
OhJQ--oLzMC?!N(Ls990~

diff --git a/apps/bitwarden_event_logs/lib/solnlib/__pycache__/conf_manager.cpython-39.pyc b/apps/bitwarden_event_logs/lib/solnlib/__pycache__/conf_manager.cpython-39.pyc
deleted file mode 100755
index 9f34346c9bf84f99bb6cf50ac1f0d88c7cb430b2..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 16546
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

diff --git a/apps/bitwarden_event_logs/lib/solnlib/__pycache__/log.cpython-39.pyc b/apps/bitwarden_event_logs/lib/solnlib/__pycache__/log.cpython-39.pyc
deleted file mode 100755
index 0423eabb58990fcadca467fc489abc99c5bd1b0f..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 10353
zcmb_i&2t>bb)T>O!~$3X;HSPuq$pq|YC$WKVk0Drlt@Y}(@=_3B6%a#Xt3Q_46r-1
z>X{{h%_6QcS+?v%aruywsTAnw!hax@b8fk%Dz~|%QaR|H9Aw62=J(#r&h7%B;&NqB
z)988K{krFUzc=enPnR`ZXa3{r1Q*0C&X0=P5a-+=sTzjfhX;eDXjcGjRMWH>@nQhE+zu2yJjx>(w
zT1yk9U+axIsc+7U@&jE=iOPMevGA@Yrp3&CP0Yxn_sqsIJkN?Mo~v?J9Qnd*9LI_i
z>pHEI6?sxluNx;dc}mPZFvYxBxSwl0!(%7K(FcY&CXNH^vty7G;v^u?iPz*&`8?L2
z6wf@c#Ixc#tb9Qho|1|f{aQ_VVUlR3X7vMZacp3Lg
z4~pWfcp13Qic`W62IjoHZt={cn87E&=@%aq#FD6C-lA9@o(yMuxX!G;&woj~;3((bC@jwfVjw}Y0w-t(K07x-@5i#!?Dza}QH*7Ug0byeiKZFH@h
zL368S#MV`>8O7!mf9Gp3UBgJ}w&%CnG75Y=EOgv0>4e>O&)?bzI@0O7(MHXTXRwbW
z{YWZDZhKK&xf!&sZ8v3?_9f)4@R1(pF{>q2lQ0i2106HH!zwyI)z(?S?iY9UfsR)*
z-8Zn{s%!?zjRIw_<4zFSQ84j>HlM(DePPon>gBSHi|Hb=<#)Yy+ipo;0-Lm5Ac*W>
z-4=4K*CK4^>~34SA%=voBhZwQ1b%zRzPllP+ly?_S;^0Oo{}QnNz;CciTsH9dn&*Ie6nJJ@P$$)!t|?8{g;M#&5vjS0DKcXo)KOSREPG(wjMZi6E@
zvEN#%@w!6^Jr5d(b4=);>gxvS@`%){O$=lg@x{XZ0`3qNLi=3bgQ9<~ZR!sVUHe=c
zXr4CEztaC!-!*v57>*4{D9zP=`D~bQMT&lj`rwNc5VRZD>r;r+T;Qqf{0MmErYi`a8??T*RkqLkrtWG#m7(U%mGBpS^p?Ott_^i|)wwrubI-
ze|2`r4dJvpvc^8lVT>2gF7balI^bPT00LLnlwnwdvF7J!LrGVxCR5hEw!|Fzmn>Sr
z1FT6hjEA=8OFzKkfqixf1PQ^pTIgNg-qpX(fzNRvos9AuNWx2Z+TGQ{))Z03^o{OXL~S1|-jr
zoG38lCx8?fk{@V$g@@2^QH1?1xqpYF-Igp#c6$0$21y`pdLjG1ExB_yP{M}Y9eS||
z>B)qQy_uaoo+FRs!bp=xOiVIKjI9Fr)NEFL0vEA?#V+%V8Ds+8YT(0Bj*BfYJMjG-
zH%g9?^d}a53KPu-B1ugre|iuL3sHiEyAwEuQhQ_d`umehJMOl#wgY8ZK};c`=G$JU
z*WouN0fRB(}vtbkN
zkm3g=BiHlKjnVwgNlZldR7Vp6VC9N^i?~5=1(f*5_-rEj#Mu^8IbQ_H$3*0iz=n8|
z%F>Gc{r#d-w{J^nw^PZ=B+7QzK#tLI<5DIcaVe94xIE%pT%tbnr%}pC->8g}tFds{
zE3ejy@mwYeqkTrO(T4Ncys@1}6SE_XA10QxeU_xXAV>&j7AjfRB
zL-ic&$C$U|xM6C{+<#0)=6N*Qk)mGKpLX}FH~0C836n)WHIMrp+~G%PB5hB9s3F|e
z_KbnHX+A_a7ipW;W^RDE01%j!&4Mt#uy)OXxmU#e5+LP=`a@ku0Jt|bz#8sV0H3D5
z&^Q2$b@ve>JGcuO7eTqVNq^$0gkJBE$k%4#0?16BA}+zn;OLOlc-l!FjzjXVmT=6t
zxPj0f4`DoiD~Q|(ORi(^y#%UW#vHXs6Dy$Ebv6~qNa9k~RWH$K8IgUuCN5&;P4ZUi
zEQS!%yh2DQ!itN;eA+y&Q0t_d@8gfEVeT&OkitIADC!kGuN(NYbnCbI{2chyKhFF(
z$oD3Bj^RaYp;(mJqR;^;HFb*GU$Iv~YkX<#>VA2kbKfAAZR+ZEYIhC)T)!F_NTAJM
z>-X^dq2`~mw8-4~ScgZ@wa5|(g?_C6Q1gxRT9n(&?-d5{O)Ucq2V=po+-j^leT$-9
z1mq?nir7Ry&L`Hr-@=QGEIXNf+37|*S}m0h)frGgou-C}5*J}8<_T_Y*mf^T*9t#0h~}E3ZRK|%qdExBJCdgb!0=DW3cG!
zt2I-Z7>jeT#my~bIv^w@J47U5g|fZA?{t(3dA=7p&K_2!xj|9yPbY^-$B<)OC8T*c
zgP!sy1c>4lq805L{wo?1onQPT^b-0#fUf?GQ?^Y5;mI2qL$Y>K`w(v;bPIXjJyVCy
zM&_RNxyBYC$Emm>%=^WGv6;V$*coC4K?(OFb2GnL_*_HSqHPv&mqZTM!5Ub5Wnh>B
zq>@?(V{dw3v2`%^W&odM>yRH9P8Gt(r?{XEAgq(;_>LC-^LZ9qUHc&gG*k*GxUYit
z?l=^sG3{a>RlYzDzZrD8m;MQ8{|mR)Kf{8XDiN49N}5Nr)j7;muc5hKJEE@9
z11YJxP7P(}>UA`+>H0hB4SIZ&n#|Cvx9IVYsUgd*zDv#bs3B_}myjED-4?i5%c*w>
zOuEC?y)l!TbE;Q?{pwF>gzd!-sF%Ff=lKXP$kJ%P)wibRL*ssQju|X)T2`i5|X32+S~2aUMs2602Jb@%%`ANV7&S#640Qu8+KpPDp3K
z+f)z<8O9UC%2P~aqOVtNLoZL`UMN~~XL$w8O$kSF`AqPg9D|jSnmQ?gUj()ZrLh1Y
z#bKqP7ZN(kljN@`CPhR`9*aX8ifaw14T_e6j9ae%*1KE*ZfC{uVJ#V!#^xHL?fEu#
ze~oviqB9cD`eWUmu&AWNbOhox2j*;1k9~~DY!{IgqBLmmue2}Cp;djXUu2r$1*u{X
z>szsYXN-zWz*UU=CvNpVn*MVT)ugO>m<6RUg9s4kP+0AR>Js`0hgjMi!a-z<3u$R%
z-%d%6#jpGCn3e<`)(Ks$J+49o((OfDuAK-w$g5;*ASsAoy{HNzj0eU&
zA~{8tR{y)VyRzx6d(8thCkGxz_!dFtg&xu!!2;}9M-7GsrFdk-d6z$Bs15A13P(f-
zRV`{plE}7ue)80e;cLtbsW7WS1P!`>XZ=of@cak8&|Ba7LK0sg4t
z+Gr1bfg_VIfHnV(sRxhm1NuSDicPuQjEn2NcH8NMt+E2B^08
zXkZ=#>t3se?>pI8zxtwZcBx-O^UIYyax$eTn>YtYRYJ*
zi%d5C&JaTDz{~Bp;5NB_6I&Yre8NK8Yf2w4)1!K#ldNnKt%p|`odRyAJ>gHwh9_XT
zV8b4AVQ{zTS0fYz*^uPEG%0yQ5wL)epy0#2FsF(G3zhEtZfTHHtApI2B#MZq^Qtk(
zqhGq9MToKoC04{T6miP^B{cS1MHhU4mU@0-^oV2-WVR9^pe>Y_5vF30eR!g;NL&I%
zNuF@SjkN&ZV$zWHkT@8;g(q3DuL$uPa3_x=e?2MpiRZo!u}*^FLsiPY5!t?!D7C}+
zBaVjYc2kpWe|jOYTg13aE6dC2BjLTYuQqtYN3M#OXrUrT>H{NW-}P8+BrMxDoW)_$<#&j~0Qm9c*9_hW|l;a#qCioIw9*GV+D@2v-
zn@Q)@U213-wL?vxn!luGfTmF%Ib20mhJ6N8{RprV$cMw1(a}y8^(tIi`H7W>YkPub
zXfnUaPgy8((9LUVg~YMzC`J(4No&plLz8UN@FKgF*(%C`;3xFNN8Gb^3!I+dcOMk!
z;8RBd;f}?T7Q21--*CdG1zVZN2>)Pffo%HP(C?%%+(>vckF{d6Ld>X?qo^?J+LcW}7?1h?4`-)RF!e!t+#_i%pPezl=v%@dJ`^
z3ih?5GrA%OQY=QbPjYWysL2y)GOj7yS!hZNDZ;sjL>Wo4ZldC}YwhNC@mWp15>aA}
zsArIWXzmvL3Z9W-4{{F;NVp-ip03^=7``F&-6Hag+@OHWqBJmhcVvGy{iH8|PU5@mU3NAxFlI0(Co{NID2M_cP)isJBjxsG_6gk0F(-8Wp38
zRJE!r8m*ZhtitfEQbWC3l9F-`S|$B};jO1tmoP2_VV!E*2-{)I8pe9*ED1m`guniV)lSii{L;YW{VS^zS5vWBdvM^$7e5fj*w_
z4NC~++>f*uttUvupHzx-Q**OiYkdsYkvNXWl{s3$eDV>oqHwOTk?IPIl4Yxi3B#_N*tLN|E-Y7VX{pSKyOJ(C
z-m!a2$70Al)M?YA=zow02?+}LU%Hn*_{q-&@{m5b-|UG$q}T?EhQ#jP%+Bo2&VDmH
zL8Vf*@Vofc*WtfTS=I|8CVwu7pQEM!L?^6;vLYlpo?Q!app@~oD~jcP6@td`9J8*u1f87br^*@lxgiHY`0dks*-6EJ(11b
zB-L@ymf86yK@^HW%VyfzmF;|{*-4c4c~*MR(P0t?QRe<8#s^qVAcV>aO6snPTa1=H
z`q>yuXdj@ZyXdslx1KSz(L1Nv0qnNRIhJN0+xyGRqW|x$_&vjM
z_CFkP&Ki!pzdqvdCHQ~M(gnevu|MGsa9X?fpGRun(}{OD+E6pr
zt@h5`k8^(58BO`cY&uMvhTjZ3Ss_hYds1hW9jVDBTl?lBOFKa*X}7Gfn~y~uwq?@Q
zS$S-VZ>s_>G;<7OSo681PJ{mT{$kS6i)kn7#(RrWw-$p=CtchM^(TQ6GH%KzaAGq`
zc8D1-)@qAUxU~ob#x%xKHwmF>t+TH<*83q^I)~1xlo)4KR$#N-Icf#&@MCx*jS=47
z2cfM4;6Ym-^Fuyhy=tEw*nR83IRx^K@aB%uMsWfx*}#v}`vPY*QII2N{o2Tn{}UjL
zw-HOjyFH1BN2)75fC1NOL~}IU4SGr*4C|`r}q|Werth)@|2;KBNfE|7UaEt*
z7o;OA=EqL!4}H*I&JWZ-GS0tb*1hO$he&r`($Z3!1@5l;g^Vki6_F}BVT6zMI}d;H
zVC_!k8h2-Isucok_O`~j)zx|Q`rL#}!wox4_0)t*-n|2)D#C3YEv=%n$`0dG&Nohl
zFh9f1EM?zel2%0s9m785U3!-&=cZfe`pAlVT+I-w^SmqUJxBdWIBPWLio&1qridbB
z-UnR}F3|&I-~%p7l!p)b-z@EXVQn)pCCZo$mc*93|tl`sS25
zG`~I;rmXpY2T9L;T(4i*eDv}a4u1FLEBsb7R_nbxf9uB!{(Qae-+Fv|p|K$rHUYe!
zT^-Edx`ByH$2>^c!$G@>W^zv7+N=)PU#$bH&kor-UZg*Afmh0Bt3FfbAcia9wWW+b
zea;%iAc~Stq!{06Hg#i1#`0-L-Q0ulztODTrBMnVI-_rtKOxKz?7Z-&L+PDJBQYEi8*|RIRq>=
z%hY?Ap;w$9_}9bgSW4|(poB`#xC5;1S;?H>dn|T1+%tT_CsHbsDN-sR=exb96JVj;
z-i#BPt$WmeFYr(l9N{Z~1F_39JB+oOrDHSo6AULpF{_NcM;648NXZ3OnX?PV*fjogOMAd}MZtg@=1e{LH!tce=J__IYhX!xV=ku!b;g7Q+Askt0P
z>6}LGOm$lyFE#i}8nxgvmM(#B8$LJud)n?pF1-i7Gph64-ix6#(<2{U&T|Wuc5esS
z+gv+*Qs_*1vNHAJv0OEjc{^C&Qp`~eXe#U@lM|IgK2RtL^7?I9&&>|&xm=+)Go;DD
zF-4~Mj=2^Ca1-1HuWAvlKXJ8+?&j}kDS_0QuCfx60yV#y6`M_ww3^L42Tfs~Sjarh
zW>(Pq9XzXwvZbO&C$n+&reMpQtt5%edzEiKPcjrSxmC2~gc&ApnUqz!(N07+k~fK!
YQqFT=N{N?v)jsQ0FI3OXRjZZ%0xz>su>b%7

diff --git a/apps/bitwarden_event_logs/lib/solnlib/__pycache__/orphan_process_monitor.cpython-39.pyc b/apps/bitwarden_event_logs/lib/solnlib/__pycache__/orphan_process_monitor.cpython-39.pyc
deleted file mode 100755
index b183cfe34dd3ecdabf4c6b89158100d885495ff0..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 3272
zcmb_eO>f*p7@qOkUhn3EQWaWCTe&TuZiTXlQ&lQd6ckheY1Kv`t%NLRXOej1*v{D7
zkkxW(uDw(oIV6HhPy7>pfUlhT7dY{}Gizseo0bbB%{w0+&%7Vc`^);9bS5?VH}5>u`Dl`
zrH{9?FZu=+?llag-NY$>tl`8+cC>j3jXK`3yw6}t<{*O^2
z(u2I7{M3)~AT_Rta=k|wAB2awzZS;1^ut8>#g0@G>-;436Dcu~`Xa?NrY3HSl5Ib&
zcVE%_XeajJ#Lck3+qa;6Rj5yO6*d0^qF@8Wb=Okog*9e@m9z_X+&E#>A9F~!3Q@C1
z;|GXR6voj==Kfh>nfQTx@s-*h?#ZOc?`m~uib|!?UH?{^6;YamamS}vlbYIX6L(CI
zVT_?aODic9hq~(>r?k?%w3Bd8mQIjmktmznve2DYP#w{+N;{YFmU8gZOQx34{PRN>
z$9D599wxh8S@gSMmgU{es5l6fkVzn)Lgye(w`nrzuB>$9XtSHAaYC)pEtu#QRH)4k
zOD_l_I41~h;FR2hU~ZdxR-2Emp6RJ-J+SU7SqxRuC*!I@eL|cL(iL>-AT3i0joH95
zLE5FzG)BMb#HjNL*f6?IMPv<_JeUKZKN#kPZ_YQEoBH9gR%T5@!kx?
zuVITZJ7y>B7e40dGJL1yF+X9PjXC_sM}HarMO&0_wHr5OKivb?U-RXIx2>Zs5{>j4
z?*7&==@Z=RWIz^!07a}1%cP1A|4+|%kjyY!EkK}8o-P|o=El>M3C7)vn!qL$^aZ93
zblV;Wc=_o1nLeNXr^DAfEkZvfV-S?>09hf1G4&UNV1F3KlP}F65NSUM6a`1ULmJte
zX&M_vFVLjc>r@aRH0^$fj#kX$wtH@Id8v3j{lE!af!UxrCffIe4F@=*Ki^35lUsHQr~gUvZ(;W8@;appV)@Fjnfu{E
z
z@f$9v*0n~LXA-|sUGFlZH|bnM?gj?X6ErIbWB{MKMtTn{VTs0bPA^0au`5rCG<%ck
zYw9xgK~$SXv?tSHu|8vpJxHTuLTNpZ2J}jtvkDhKPS}|?iwqlm@CLKe>@};12XrFQ
z(~+!Fcd8hu^ZR!SolejA*X*ZDxK{8zzs=b`E1Y9j@Dtd&e`OypXV`148;hDIAXl-U
zBDyOb6(-wKeMEhmj76McDYJ^Oc`#>|^072S%?Wo57;AIOT1E@oW*=l#$flr<7okz4
zt=_}m>U|OyNn9d9p)4B!z2;^`xKOu9_#{qIqk9_n=n+wgr6TJsi6aN|KC7
zx}X_7Br=lp3T`U8jI&tAQ!+{}(&LYXWo0dwTCv8|XN6Rj*}7CLugkKo*wQM=8sVHW
zl^y%tX;Ce-a&?sh{ym9TH~Sh4MKTK2p=K6elR1Uz@l`KNf}O|?*qv*VkgN+lx`sl^
z#jg!EE0q&0*TULyzt2EEd=X`%m3PxSAE@*
zKZj}ExYRa!S)HcJ<*8^Io6ZXNSs1A*uFh2DxULuItkUr~)x|8ewXX2H+V8kIH^~1v
zat}?>NA%*!W@h6x#~k;y7VH#*AHuOOLAB%rx?B>o@8~5tiCS8aHoBqTD0CkFPKk*i
za1gqL;?pK!qYW}9!>g#c)Qo_g3(+

diff --git a/apps/bitwarden_event_logs/lib/solnlib/__pycache__/server_info.cpython-39.pyc b/apps/bitwarden_event_logs/lib/solnlib/__pycache__/server_info.cpython-39.pyc
deleted file mode 100755
index 36da991c65573c06c9ffad06a330084c95f87e3c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 8744
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
L2o(O%HlP1L(@4ow

diff --git a/apps/bitwarden_event_logs/lib/solnlib/__pycache__/soln_exceptions.cpython-39.pyc b/apps/bitwarden_event_logs/lib/solnlib/__pycache__/soln_exceptions.cpython-39.pyc
deleted file mode 100755
index f8c36808e6f6400fa5fe693c4338a87b535f0b81..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 998
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

diff --git a/apps/bitwarden_event_logs/lib/solnlib/__pycache__/splunkenv.cpython-39.pyc b/apps/bitwarden_event_logs/lib/solnlib/__pycache__/splunkenv.cpython-39.pyc
deleted file mode 100755
index ea5da717981bf40ab5227af691c13df64817c5f2..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 11001
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
G(tiQLt&em7

diff --git a/apps/bitwarden_event_logs/lib/solnlib/__pycache__/timer_queue.cpython-39.pyc b/apps/bitwarden_event_logs/lib/solnlib/__pycache__/timer_queue.cpython-39.pyc
deleted file mode 100755
index 6cc60a7f98d85920611ccde36ddc81d19361c8d8..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 9983
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

diff --git a/apps/bitwarden_event_logs/lib/solnlib/__pycache__/user_access.cpython-39.pyc b/apps/bitwarden_event_logs/lib/solnlib/__pycache__/user_access.cpython-39.pyc
deleted file mode 100755
index 85e781d91eedac1556b650b18e65a05dd18301b1..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 25975
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
U$^eyF-`cQ~`F%V3-o)vD0fz&TZvX%Q

diff --git a/apps/bitwarden_event_logs/lib/solnlib/_settings.py b/apps/bitwarden_event_logs/lib/solnlib/_settings.py
deleted file mode 100755
index 1f3501de..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/_settings.py
+++ /dev/null
@@ -1,18 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-"""This module provide settings that can be used to disable/switch features."""
-
-use_btool = False
diff --git a/apps/bitwarden_event_logs/lib/solnlib/_utils.py b/apps/bitwarden_event_logs/lib/solnlib/_utils.py
deleted file mode 100755
index 3b98aac8..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/_utils.py
+++ /dev/null
@@ -1,79 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-"""This module provide utils that are private to solnlib."""
-
-import re
-from typing import Any, Dict, Optional, Union
-
-from splunklib import binding, client
-
-from solnlib import splunk_rest_client
-from solnlib.utils import retry
-
-
-@retry(exceptions=[binding.HTTPError])
-def get_collection_data(
-    collection_name: str,
-    session_key: str,
-    app: str,
-    owner: Optional[str] = None,
-    scheme: Optional[str] = None,
-    host: Optional[str] = None,
-    port: Optional[Union[str, int]] = None,
-    fields: Optional[Dict] = None,
-    **context: Any,
-) -> client.KVStoreCollectionData:
-    """Get collection data, if there is no such collection - creates one.
-
-    Arguments:
-        collection_name: Collection name of KV Store checkpointer.
-        session_key: Splunk access token.
-        app: App name of namespace.
-        owner: Owner of namespace, default is `nobody`.
-        scheme: The access scheme, default is None.
-        host: The host name, default is None.
-        port: The port number, default is None.
-        fields: Fields used to initialize the collection if it's missing.
-        context: Other configurations for Splunk rest client.
-
-    Raises:
-        binding.HTTPError: HTTP error different from 404, for example 503 when
-            KV Store is initializing and not ready to serve requests.
-        KeyError: KV Store did not get collection_name.
-
-    Returns:
-        KV Store collections data instance.
-    """
-    kvstore = splunk_rest_client.SplunkRestClient(
-        session_key, app, owner=owner, scheme=scheme, host=host, port=port, **context
-    ).kvstore
-
-    collection_name = re.sub(r"[^\w]+", "_", collection_name)
-    try:
-        kvstore.get(name=collection_name)
-    except binding.HTTPError as e:
-        if e.status != 404:
-            raise
-
-        fields = fields if fields is not None else {}
-        kvstore.create(collection_name, fields=fields)
-
-    collections = kvstore.list(search=collection_name)
-    for collection in collections:
-        if collection.name == collection_name:
-            return collection.data
-    else:
-        raise KeyError(f"Get collection data: {collection_name} failed.")
diff --git a/apps/bitwarden_event_logs/lib/solnlib/acl.py b/apps/bitwarden_event_logs/lib/solnlib/acl.py
deleted file mode 100755
index ce6f6eaa..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/acl.py
+++ /dev/null
@@ -1,182 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""This module contains interfaces that support CRUD operations on ACL."""
-
-import json
-from typing import List
-
-from splunklib import binding
-
-from . import splunk_rest_client as rest_client
-from .utils import retry
-
-__all__ = ["ACLException", "ACLManager"]
-
-
-class ACLException(Exception):
-    """Exception raised by ACLManager."""
-
-    pass
-
-
-class ACLManager:
-    """ACL manager.
-
-    Examples:
-       >>> import solnlib.acl as sacl
-       >>> saclm = sacl.ACLManager(session_key, 'Splunk_TA_test')
-       >>> saclm.get('data/transforms/extractions')
-       >>> saclm.update('data/transforms/extractions/_acl',
-                        perms_read=['*'], perms_write=['*'])
-    """
-
-    def __init__(
-        self,
-        session_key: str,
-        app: str,
-        owner: str = "nobody",
-        scheme: str = None,
-        host: str = None,
-        port: int = None,
-        **context: dict
-    ):
-        """Initializes ACLManager.
-
-        Arguments:
-            session_key: Splunk access token.
-            app: App name of namespace.
-            owner: (optional) Owner of namespace, default is `nobody`.
-            scheme: (optional) The access scheme, default is None.
-            host: (optional) The host name, default is None.
-            port: (optional) The port number, default is None.
-            context: Other configurations for Splunk rest client.
-        """
-        self._rest_client = rest_client.SplunkRestClient(
-            session_key,
-            app,
-            owner=owner,
-            scheme=scheme,
-            host=host,
-            port=port,
-            **context
-        )
-
-    @retry(exceptions=[binding.HTTPError])
-    def get(self, path: str) -> dict:
-        """Get ACL of  /servicesNS/{`owner`}/{`app`}/{`path`}.
-
-        Arguments:
-            path: Path of ACL relative to /servicesNS/{`owner`}/{`app`}
-
-        Returns:
-            A dict contains ACL.
-
-        Raises:
-            ACLException: If `path` is invalid.
-
-        Examples:
-           >>> aclm = acl.ACLManager(session_key, 'Splunk_TA_test')
-           >>> perms = aclm.get('data/transforms/extractions/_acl')
-        """
-
-        try:
-            content = self._rest_client.get(path, output_mode="json").body.read()
-        except binding.HTTPError as e:
-            if e.status != 404:
-                raise
-
-            raise ACLException("Invalid endpoint: %s.", path)
-
-        return json.loads(content)["entry"][0]["acl"]
-
-    @retry(exceptions=[binding.HTTPError])
-    def update(
-        self,
-        path: str,
-        owner: str = None,
-        perms_read: List = None,
-        perms_write: List = None,
-    ) -> dict:
-        """Update ACL of /servicesNS/{`owner`}/{`app`}/{`path`}.
-
-        If the ACL is per-entity (ends in /acl), owner can be reassigned. If
-        the acl is endpoint-level (ends in _acl), owner will be ignored. The
-        'sharing' setting is always retrieved from the current.
-
-        Arguments:
-            path: Path of ACL relative to /servicesNS/{owner}/{app}. MUST
-                end with /acl or /_acl indicating whether the permission is applied
-                at the per-entity level or endpoint level respectively.
-            owner: (optional) New owner of ACL, default is `nobody`.
-            perms_read: (optional) List of roles (['*'] for all roles). If
-                unspecified we will POST with current (if available) perms.read,
-                default is None.
-            perms_write: (optional) List of roles (['*'] for all roles). If
-                unspecified we will POST with current (if available) perms.write,
-                default is None.
-
-        Returns:
-            A dict contains ACL after update.
-
-        Raises:
-            ACLException: If `path` is invalid.
-
-        Examples:
-           >>> aclm = acl.ACLManager(session_key, 'Splunk_TA_test')
-           >>> perms = aclm.update('data/transforms/extractions/_acl',
-                                   perms_read=['admin'], perms_write=['admin'])
-        """
-
-        if not path.endswith("/acl") and not path.endswith("/_acl"):
-            raise ACLException(
-                "Invalid endpoint: %s, must end with /acl or /_acl." % path
-            )
-
-        curr_acl = self.get(path)
-
-        postargs = {}
-        if perms_read:
-            postargs["perms.read"] = ",".join(perms_read)
-        else:
-            curr_read = curr_acl["perms"].get("read", [])
-            if curr_read:
-                postargs["perms.read"] = ",".join(curr_read)
-
-        if perms_write:
-            postargs["perms.write"] = ",".join(perms_write)
-        else:
-            curr_write = curr_acl["perms"].get("write", [])
-            if curr_write:
-                postargs["perms.write"] = ",".join(curr_write)
-
-        if path.endswith("/acl"):
-            # Allow ownership to be reset only at entity level.
-            postargs["owner"] = owner or curr_acl["owner"]
-
-        postargs["sharing"] = curr_acl["sharing"]
-
-        try:
-            content = self._rest_client.post(
-                path, body=binding._encode(**postargs), output_mode="json"
-            ).body.read()
-        except binding.HTTPError as e:
-            if e.status != 404:
-                raise
-
-            raise ACLException("Invalid endpoint: %s.", path)
-
-        return json.loads(content)["entry"][0]["acl"]
diff --git a/apps/bitwarden_event_logs/lib/solnlib/alerts_rest_client.py b/apps/bitwarden_event_logs/lib/solnlib/alerts_rest_client.py
deleted file mode 100755
index 430aacfe..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/alerts_rest_client.py
+++ /dev/null
@@ -1,258 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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 json
-from enum import Enum
-from typing import Tuple, Union, Optional
-
-from solnlib import splunk_rest_client as rest_client
-
-
-class AlertType(Enum):
-    CUSTOM = "custom"
-    NUMBER_OF_EVENTS = "number of events"
-    NUMBER_OF_HOSTS = "number of hosts"
-    NUMBER_OF_SOURCES = "number of sources"
-
-
-class AlertSeverity(Enum):
-    DEBUG = 1
-    INFO = 2
-    WARN = 3
-    ERROR = 4
-    SEVERE = 5
-    FATAL = 6
-
-
-class AlertComparator(Enum):
-    GREATER_THAN = "greater than"
-    LESS_THAN = "less than"
-    EQUAL_TO = "equal to"
-    RISES_BY = "rises by"
-    DROPS_BY = "drops by"
-    RISES_BY_PERC = "rises by perc"
-    DROPS_BY_PERC = "drops by perc"
-
-
-class AlertsRestClient:
-    """REST client for handling alerts."""
-
-    ENDPOINT = "/servicesNS/{owner}/{app}/saved/searches"
-    headers = [("Content-Type", "application/json")]
-
-    def __init__(
-        self,
-        session_key: str,
-        app: str,
-        owner: str = "nobody",
-        **context: dict,
-    ):
-        """Initializes AlertsRestClient.
-
-        Arguments:
-            session_key: Splunk access token.
-            app: App name of namespace.
-            context: Other configurations for Splunk rest client.
-        """
-        self.session_key = session_key
-        self.app = app
-
-        self._rest_client = rest_client.SplunkRestClient(
-            self.session_key,
-            app=self.app,
-            owner=owner,
-            **context,
-        )
-
-        self.endpoint = self.ENDPOINT.format(owner=owner, app=app)
-
-    def create_search_alert(
-        self,
-        name: str,
-        search: str,
-        *,
-        disabled: bool = True,
-        description: str = "",
-        alert_type: AlertType = AlertType.NUMBER_OF_EVENTS,
-        alert_condition: str = "",
-        alert_comparator: AlertComparator = AlertComparator.GREATER_THAN,
-        alert_threshold: Union[int, float, str] = 0,
-        time_window: Tuple[str, str] = ("-15m", "now"),
-        alert_severity: AlertSeverity = AlertSeverity.WARN,
-        cron_schedule: str = "* * * * *",
-        expires: Union[int, str] = "24h",
-        **kwargs,
-    ):
-        """Creates a search alert in Splunk.
-
-        Arguments:
-            name: Name of the alert.
-            search: Search query for the alert.
-            disabled: Whether the alert is disabled. Default is True.
-            description: Description of the alert.
-            alert_type: Type of the alert (see AlertType). If it equals to CUSTOM, Splunk executes a check in
-                alert_condition. Otherwise, alert_comparator and alert_threshold are used.
-            alert_condition: Condition for the alert.
-            alert_comparator: Comparator for the alert. Default is GREATER_THAN.
-            alert_threshold: Threshold for the alert. Default is 0.
-            time_window: Time window for the alert. Tuple of earliest and latest time. Default is ("-15m", "now").
-            alert_severity: Severity level of the alert. Default is WARN.
-            cron_schedule: Cron schedule for the alert. Default is "* * * * *".
-            expires: Expiration time for the alert (i.e. how long you can access the result of triggered alert).
-                Default is "24h".
-            kwargs: Additional parameters for the alert. See Splunk documentation for more details.
-        """
-        params = {
-            "output_mode": "json",
-            "name": name,
-            "search": search,
-            "description": description,
-            "alert_type": alert_type.value,
-            "alert_condition": alert_condition,
-            "alert_comparator": alert_comparator.value,
-            "alert_threshold": alert_threshold,
-            "alert.severity": str(alert_severity.value),
-            "is_scheduled": "1",
-            "cron_schedule": cron_schedule,
-            "dispatch.earliest_time": time_window[0],
-            "dispatch.latest_time": time_window[1],
-            "alert.digest_mode": "1",
-            "alert.expires": str(expires),
-            "disabled": "1" if disabled else "0",
-            "realtime_schedule": "1",
-        }
-
-        params.update(kwargs)
-
-        self._rest_client.post(self.endpoint, body=params, headers=self.headers)
-
-    def delete_search_alert(self, name: str):
-        """Deletes a search alert in Splunk.
-
-        Arguments:
-            name: Name of the alert to delete.
-        """
-        self._rest_client.delete(f"{self.endpoint}/{name}")
-
-    def get_search_alert(self, name: str):
-        """Retrieves a specific search alert from Splunk.
-
-        Arguments:
-            name: Name of the alert to retrieve.
-
-        Returns:
-            A dictionary containing the alert details.
-        """
-        response = (
-            self._rest_client.get(f"{self.endpoint}/{name}", output_mode="json")
-            .body.read()
-            .decode("utf-8")
-        )
-
-        return json.loads(response)
-
-    def get_all_search_alerts(self):
-        """Retrieves all search alerts from Splunk.
-
-        Returns:
-            A dictionary containing all search alerts.
-        """
-        response = (
-            self._rest_client.get(self.endpoint, output_mode="json")
-            .body.read()
-            .decode("utf-8")
-        )
-
-        return json.loads(response)
-
-    def update_search_alert(
-        self,
-        name: str,
-        *,
-        search: Optional[str] = None,
-        disabled: Optional[bool] = None,
-        description: Optional[str] = None,
-        alert_type: Optional[AlertType] = None,
-        alert_condition: Optional[str] = None,
-        alert_comparator: Optional[AlertComparator] = None,
-        alert_threshold: Optional[Union[int, float, str]] = None,
-        time_window: Optional[Tuple[str, str]] = None,
-        alert_severity: Optional[AlertSeverity] = None,
-        cron_schedule: Optional[str] = None,
-        expires: Optional[Union[int, str]] = None,
-        **kwargs,
-    ):
-        """Updates a search alert in Splunk.
-
-        Arguments:
-            name: Name of the alert to update.
-            search: Search query for the alert.
-            disabled: Whether the alert is disabled.
-            description: Description of the alert.
-            alert_type: Type of the alert (see AlertType). If it equals to CUSTOM, Splunk executes a check in
-                alert_condition. Otherwise, alert_comparator and alert_threshold are used.
-            alert_condition: Condition for the alert.
-            alert_comparator: Comparator for the alert.
-            alert_threshold: Threshold for the alert.
-            time_window: Time window for the alert. Tuple of earliest and latest time.
-            alert_severity: Severity level of the alert.
-            cron_schedule: Cron schedule for the alert.
-            expires: Expiration time for the alert.
-            kwargs: Additional parameters for the alert. See Splunk documentation for more details.
-        """
-        params = {
-            "output_mode": "json",
-        }
-
-        if search:
-            params["search"] = search
-
-        if disabled is not None:
-            params["disabled"] = "1" if disabled else "0"
-
-        if description:
-            params["description"] = description
-
-        if alert_type:
-            params["alert_type"] = alert_type.value
-
-        if alert_condition:
-            params["alert_condition"] = alert_condition
-
-        if alert_comparator:
-            params["alert_comparator"] = alert_comparator.value
-
-        if alert_threshold:
-            params["alert_threshold"] = str(alert_threshold)
-
-        if time_window:
-            params["dispatch.earliest_time"] = time_window[0]
-            params["dispatch.latest_time"] = time_window[1]
-
-        if alert_severity:
-            params["alert.severity"] = str(alert_severity.value)
-
-        if cron_schedule:
-            params["is_scheduled"] = "1"
-            params["cron_schedule"] = cron_schedule
-
-        if expires:
-            params["alert.expires"] = str(expires)
-
-        params.update(kwargs)
-
-        self._rest_client.post(
-            f"{self.endpoint}/{name}", body=params, headers=self.headers
-        )
diff --git a/apps/bitwarden_event_logs/lib/solnlib/bulletin_rest_client.py b/apps/bitwarden_event_logs/lib/solnlib/bulletin_rest_client.py
deleted file mode 100755
index 1fb786f7..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/bulletin_rest_client.py
+++ /dev/null
@@ -1,151 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-from solnlib import splunk_rest_client as rest_client
-from typing import Optional, List
-import json
-
-__all__ = ["BulletinRestClient"]
-
-
-class BulletinRestClient:
-    """REST client for handling Bulletin messages."""
-
-    MESSAGES_ENDPOINT = "/services/messages"
-
-    headers = [("Content-Type", "application/json")]
-
-    class Severity:
-        INFO = "info"
-        WARNING = "warn"
-        ERROR = "error"
-
-    def __init__(
-        self,
-        message_name: str,
-        session_key: str,
-        app: str,
-        **context: dict,
-    ):
-        """Initializes BulletinRestClient.
-            When creating a new bulletin message, you must provide a name, which is a kind of ID.
-            If you try to create another message with the same name (ID), the API will not add another message
-            to the bulletin, but it will overwrite the existing one. Similar behaviour applies to deletion.
-            To delete a message, you must indicate the name (ID) of the message.
-            To provide better and easier control over bulletin messages, this client works in such a way
-            that there is one instance responsible for handling one specific message.
-            If you need to add another message to bulletin create another instance
-            with a different 'message_name'
-            e.g.
-            msg_1 = BulletinRestClient("message_1", "")
-            msg_2 = BulletinRestClient("message_2", "")
-
-        Arguments:
-            message_name: Name of the message in the Splunk's bulletin.
-            session_key: Splunk access token.
-            app: App name of namespace.
-            context: Other configurations for Splunk rest client.
-        """
-
-        self.message_name = message_name
-        self.session_key = session_key
-        self.app = app
-
-        self._rest_client = rest_client.SplunkRestClient(
-            self.session_key, app=self.app, **context
-        )
-
-    def create_message(
-        self,
-        msg: str,
-        severity: Severity = Severity.WARNING,
-        capabilities: Optional[List[str]] = None,
-        roles: Optional[List] = None,
-    ):
-        """Creates a message in the Splunk's bulletin. Calling this method
-        multiple times for the same instance will overwrite existing message.
-
-        Arguments:
-            msg: The message which will be displayed in the Splunk's bulletin
-            severity: Severity level of the message. It has to be one of: 'info', 'warn', 'error'.
-                If wrong severity is given, ValueError will be raised.
-            capabilities: One or more capabilities that users must have to view the message.
-                Capability names are validated.
-                This argument should be provided as a list of string/s e.g. capabilities=['one', 'two'].
-                If a non-existent capability is used, HTTP 400 BAD REQUEST exception will be raised.
-                If argument is not a List[str] ValueError will be raised.
-            roles: One or more roles that users must have to view the message. Role names are validated.
-                This argument should be provided as a list of string/s e.g. roles=['user', 'admin'].
-                If a non-existent role is used, HTTP 400 BAD REQUEST exception will be raised.
-                If argument is not a List[str] ValueError will be raised.
-        """
-        body = {
-            "name": self.message_name,
-            "value": msg,
-            "severity": severity,
-            "capability": [],
-            "role": [],
-        }
-
-        if severity not in (
-            self.Severity.INFO,
-            self.Severity.WARNING,
-            self.Severity.ERROR,
-        ):
-            raise ValueError(
-                "Severity must be one of ("
-                "'BulletinRestClient.Severity.INFO', "
-                "'BulletinRestClient.Severity.WARNING', "
-                "'BulletinRestClient.Severity.ERROR'"
-                ")."
-            )
-
-        if capabilities:
-            body["capability"] = self._validate_and_get_body_value(
-                capabilities, "Capabilities must be a list of strings."
-            )
-
-        if roles:
-            body["role"] = self._validate_and_get_body_value(
-                roles, "Roles must be a list of strings."
-            )
-
-        self._rest_client.post(self.MESSAGES_ENDPOINT, body=body, headers=self.headers)
-
-    def get_message(self):
-        """Get specific message created by this instance."""
-        endpoint = f"{self.MESSAGES_ENDPOINT}/{self.message_name}"
-        response = self._rest_client.get(endpoint, output_mode="json").body.read()
-        return json.loads(response)
-
-    def get_all_messages(self):
-        """Get all messages in the bulletin."""
-        response = self._rest_client.get(
-            self.MESSAGES_ENDPOINT, output_mode="json"
-        ).body.read()
-        return json.loads(response)
-
-    def delete_message(self):
-        """Delete specific message created by this instance."""
-        endpoint = f"{self.MESSAGES_ENDPOINT}/{self.message_name}"
-        self._rest_client.delete(endpoint)
-
-    @staticmethod
-    def _validate_and_get_body_value(arg, error_msg) -> List:
-        if type(arg) is list and (all(isinstance(el, str) for el in arg)):
-            return [el for el in arg]
-        else:
-            raise ValueError(error_msg)
diff --git a/apps/bitwarden_event_logs/lib/solnlib/concurrent/concurrent_executor.py b/apps/bitwarden_event_logs/lib/solnlib/concurrent/concurrent_executor.py
deleted file mode 100755
index 98dafc2a..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/concurrent/concurrent_executor.py
+++ /dev/null
@@ -1,102 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""Concurrent executor provides concurrent executing function either in a
-thread pool or a process pool."""
-
-import solnlib.concurrent.process_pool as pp
-import solnlib.concurrent.thread_pool as tp
-
-
-class ConcurrentExecutor:
-    def __init__(self, config):
-        """
-        :param config: dict like object, contains thread_min_size (int),
-                       thread_max_size (int), daemonize_thread (bool),
-                       process_size (int)
-        """
-
-        self._io_executor = tp.ThreadPool(
-            config.get("thread_min_size", 0),
-            config.get("thread_max_size", 0),
-            config.get("task_queue_size", 1024),
-            config.get("daemonize_thread", True),
-        )
-        self._compute_executor = None
-        if config.get("process_size", 0):
-            self._compute_executor = pp.ProcessPool(config.get("process_size", 0))
-
-    def start(self):
-        self._io_executor.start()
-
-    def tear_down(self):
-        self._io_executor.tear_down()
-        if self._compute_executor is not None:
-            self._compute_executor.tear_down()
-
-    def run_io_func_sync(self, func, args=(), kwargs=None):
-        """
-        :param func: callable
-        :param args: free params
-        :param kwargs: named params
-        :return whatever the func returns
-        """
-
-        return self._io_executor.apply(func, args, kwargs)
-
-    def run_io_func_async(self, func, args=(), kwargs=None, callback=None):
-        """
-        :param func: callable
-        :param args: free params
-        :param kwargs: named params
-        :calllback: when func is done and without exception, call the callback
-        :return whatever the func returns
-        """
-
-        return self._io_executor.apply_async(func, args, kwargs, callback)
-
-    def enqueue_io_funcs(self, funcs, block=True):
-        """run jobs in a fire and forget way, no result will be handled over to
-        clients.
-
-        :param funcs: tuple/list-like or generator like object, func shall be
-                      callable
-        """
-
-        return self._io_executor.enqueue_funcs(funcs, block)
-
-    def run_compute_func_sync(self, func, args=(), kwargs={}):
-        """
-        :param func: callable
-        :param args: free params
-        :param kwargs: named params
-        :return whatever the func returns
-        """
-
-        assert self._compute_executor is not None
-        return self._compute_executor.apply(func, args, kwargs)
-
-    def run_compute_func_async(self, func, args=(), kwargs={}, callback=None):
-        """
-        :param func: callable
-        :param args: free params
-        :param kwargs: named params
-        :calllback: when func is done and without exception, call the callback
-        :return whatever the func returns
-        """
-
-        assert self._compute_executor is not None
-        return self._compute_executor.apply_async(func, args, kwargs, callback)
diff --git a/apps/bitwarden_event_logs/lib/solnlib/concurrent/process_pool.py b/apps/bitwarden_event_logs/lib/solnlib/concurrent/process_pool.py
deleted file mode 100755
index e900bfe4..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/concurrent/process_pool.py
+++ /dev/null
@@ -1,75 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""A wrapper of multiprocessing.pool."""
-
-import multiprocessing
-
-import logging
-
-
-class ProcessPool:
-    """A simple wrapper of multiprocessing.pool."""
-
-    def __init__(self, size=0, maxtasksperchild=10000):
-        if size <= 0:
-            size = multiprocessing.cpu_count()
-        self.size = size
-        self._pool = multiprocessing.Pool(
-            processes=size, maxtasksperchild=maxtasksperchild
-        )
-        self._stopped = False
-
-    def tear_down(self):
-        """Tear down the pool."""
-
-        if self._stopped:
-            logging.info("ProcessPool has already stopped.")
-            return
-        self._stopped = True
-
-        self._pool.close()
-        self._pool.join()
-        logging.info("ProcessPool stopped.")
-
-    def apply(self, func, args=(), kwargs={}):
-        """
-        :param func: callable
-        :param args: free params
-        :param kwargs: named params
-        :return whatever the func returns
-        """
-
-        if self._stopped:
-            logging.info("ProcessPool has already stopped.")
-            return None
-
-        return self._pool.apply(func, args, kwargs)
-
-    def apply_async(self, func, args=(), kwargs={}, callback=None):
-        """
-        :param func: callable
-        :param args: free params
-        :param kwargs: named params
-        :callback: when func is done without exception, call this callack
-        :return whatever the func returns
-        """
-
-        if self._stopped:
-            logging.info("ProcessPool has already stopped.")
-            return None
-
-        return self._pool.apply_async(func, args, kwargs, callback)
diff --git a/apps/bitwarden_event_logs/lib/solnlib/concurrent/thread_pool.py b/apps/bitwarden_event_logs/lib/solnlib/concurrent/thread_pool.py
deleted file mode 100755
index 212e5ad4..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/concurrent/thread_pool.py
+++ /dev/null
@@ -1,347 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""A simple thread pool implementation."""
-
-import multiprocessing
-import queue
-import threading
-import traceback
-from time import time
-import logging
-
-
-class ThreadPool:
-    """A simple thread pool implementation."""
-
-    _high_watermark = 0.2
-    _resize_window = 10
-
-    def __init__(self, min_size=1, max_size=128, task_queue_size=1024, daemon=True):
-        assert task_queue_size
-
-        if not min_size or min_size <= 0:
-            min_size = multiprocessing.cpu_count()
-
-        if not max_size or max_size <= 0:
-            max_size = multiprocessing.cpu_count() * 8
-
-        self._min_size = min_size
-        self._max_size = max_size
-        self._daemon = daemon
-
-        self._work_queue = queue.Queue(task_queue_size)
-        self._thrs = []
-        for _ in range(min_size):
-            thr = threading.Thread(target=self._run)
-            self._thrs.append(thr)
-        self._admin_queue = queue.Queue()
-        self._admin_thr = threading.Thread(target=self._do_admin)
-        self._last_resize_time = time()
-        self._last_size = min_size
-        self._lock = threading.Lock()
-        self._occupied_threads = 0
-        self._count_lock = threading.Lock()
-        self._started = False
-
-    def start(self):
-        """Start threads in the pool."""
-
-        with self._lock:
-            if self._started:
-                return
-            self._started = True
-
-            for thr in self._thrs:
-                thr.daemon = self._daemon
-                thr.start()
-
-            self._admin_thr.start()
-        logging.info("ThreadPool started.")
-
-    def tear_down(self):
-        """Tear down thread pool."""
-
-        with self._lock:
-            if not self._started:
-                return
-            self._started = False
-
-            for thr in self._thrs:
-                self._work_queue.put(None, block=True)
-
-            self._admin_queue.put(None)
-
-            if not self._daemon:
-                logging.info("Wait for threads to stop.")
-                for thr in self._thrs:
-                    thr.join()
-            self._admin_thr.join()
-
-        logging.info("ThreadPool stopped.")
-
-    def enqueue_funcs(self, funcs, block=True):
-        """run jobs in a fire and forget way, no result will be handled over to
-        clients.
-
-        :param funcs: tuple/list-like or generator like object, func shall be
-                      callable
-        """
-
-        if not self._started:
-            logging.info("ThreadPool has already stopped.")
-            return
-
-        for func in funcs:
-            self._work_queue.put(func, block)
-
-    def apply_async(self, func, args=(), kwargs=None, callback=None):
-        """
-        :param func: callable
-        :param args: free params
-        :param kwargs: named params
-        :callback: when func is done and without exception, call the callback
-        :return AsyncResult, clients can poll or wait the result through it
-        """
-
-        if not self._started:
-            logging.info("ThreadPool has already stopped.")
-            return None
-
-        res = AsyncResult(func, args, kwargs, callback)
-        self._work_queue.put(res)
-        return res
-
-    def apply(self, func, args=(), kwargs=None):
-        """
-        :param func: callable
-        :param args: free params
-        :param kwargs: named params
-        :return whatever the func returns
-        """
-
-        if not self._started:
-            logging.info("ThreadPool has already stopped.")
-            return None
-
-        res = self.apply_async(func, args, kwargs)
-        return res.get()
-
-    def size(self):
-        return self._last_size
-
-    def resize(self, new_size):
-        """Resize the pool size, spawn or destroy threads if necessary."""
-
-        if new_size <= 0:
-            return
-
-        if self._lock.locked() or not self._started:
-            logging.info(
-                "Try to resize thread pool during the tear " "down process, do nothing"
-            )
-            return
-
-        with self._lock:
-            self._remove_exited_threads_with_lock()
-            size = self._last_size
-            self._last_size = new_size
-            if new_size > size:
-                for _ in range(new_size - size):
-                    thr = threading.Thread(target=self._run)
-                    thr.daemon = self._daemon
-                    thr.start()
-                    self._thrs.append(thr)
-            elif new_size < size:
-                for _ in range(size - new_size):
-                    self._work_queue.put(None)
-        logging.info("Finished ThreadPool resizing. New size=%d", new_size)
-
-    def _remove_exited_threads_with_lock(self):
-        """Join the exited threads last time when resize was called."""
-
-        joined_thrs = set()
-        for thr in self._thrs:
-            if not thr.is_alive():
-                try:
-                    if not thr.daemon:
-                        thr.join(timeout=0.5)
-                    joined_thrs.add(thr.ident)
-                except RuntimeError:
-                    pass
-
-        if joined_thrs:
-            live_thrs = []
-            for thr in self._thrs:
-                if thr.ident not in joined_thrs:
-                    live_thrs.append(thr)
-            self._thrs = live_thrs
-
-    def _do_resize_according_to_loads(self):
-        if (
-            self._last_resize_time
-            and time() - self._last_resize_time < self._resize_window
-        ):
-            return
-
-        thr_size = self._last_size
-        free_thrs = thr_size - self._occupied_threads
-        work_size = self._work_queue.qsize()
-
-        logging.debug(
-            "current_thr_size=%s, free_thrs=%s, work_size=%s",
-            thr_size,
-            free_thrs,
-            work_size,
-        )
-        if work_size and work_size > free_thrs:
-            if thr_size < self._max_size:
-                thr_size = min(thr_size * 2, self._max_size)
-                self.resize(thr_size)
-        elif free_thrs > 0:
-            free = free_thrs * 1.0
-            if free / thr_size >= self._high_watermark and free_thrs >= 2:
-                # 20 % thrs are idle, tear down half of the idle ones
-                thr_size = thr_size - int(free_thrs // 2)
-                if thr_size > self._min_size:
-                    self.resize(thr_size)
-        self._last_resize_time = time()
-
-    def _do_admin(self):
-        admin_q = self._admin_queue
-        resize_win = self._resize_window
-        while 1:
-            try:
-                wakup = admin_q.get(timeout=resize_win + 1)
-            except queue.Empty:
-                self._do_resize_according_to_loads()
-                continue
-
-            if wakup is None:
-                break
-            else:
-                self._do_resize_according_to_loads()
-        logging.info(
-            "ThreadPool admin thread=%s stopped.", threading.current_thread().getName()
-        )
-
-    def _run(self):
-        """Threads callback func, run forever to handle jobs from the job
-        queue."""
-
-        work_queue = self._work_queue
-        count_lock = self._count_lock
-        while 1:
-            logging.debug("Going to get job")
-            func = work_queue.get()
-            if func is None:
-                break
-
-            if not self._started:
-                break
-
-            logging.debug("Going to exec job")
-            with count_lock:
-                self._occupied_threads += 1
-
-            try:
-                func()
-            except Exception:
-                logging.error(traceback.format_exc())
-
-            with count_lock:
-                self._occupied_threads -= 1
-
-            logging.debug("Done with exec job")
-            logging.info("Thread work_queue_size=%d", work_queue.qsize())
-
-        logging.debug("Worker thread %s stopped.", threading.current_thread().getName())
-
-
-class AsyncResult:
-    def __init__(self, func, args, kwargs, callback):
-        self._func = func
-        self._args = args
-        self._kwargs = kwargs
-        self._callback = callback
-        self._q = queue.Queue()
-
-    def __call__(self):
-        try:
-            if self._args and self._kwargs:
-                res = self._func(*self._args, **self._kwargs)
-            elif self._args:
-                res = self._func(*self._args)
-            elif self._kwargs:
-                res = self._func(**self._kwargs)
-            else:
-                res = self._func()
-        except Exception as e:
-            self._q.put(e)
-            return
-        else:
-            self._q.put(res)
-
-        if self._callback is not None:
-            self._callback()
-
-    def get(self, timeout=None):
-        """Return the result when it arrives.
-
-        If timeout is not None and the result does not arrive within
-        timeout seconds then multiprocessing.TimeoutError is raised. If
-        the remote call raised an exception then that exception will be
-        reraised by get().
-        """
-
-        try:
-            res = self._q.get(timeout=timeout)
-        except queue.Empty:
-            raise multiprocessing.TimeoutError("Timed out")
-
-        if isinstance(res, Exception):
-            raise res
-        return res
-
-    def wait(self, timeout=None):
-        """Wait until the result is available or until timeout seconds pass."""
-
-        try:
-            res = self._q.get(timeout=timeout)
-        except queue.Empty:
-            pass
-        else:
-            self._q.put(res)
-
-    def ready(self):
-        """Return whether the call has completed."""
-
-        return len(self._q)
-
-    def successful(self):
-        """Return whether the call completed without raising an exception.
-
-        Will raise AssertionError if the result is not ready.
-        """
-
-        if not self.ready():
-            raise AssertionError("Function is not ready")
-        res = self._q.get()
-        self._q.put(res)
-
-        if isinstance(res, Exception):
-            return False
-        return True
diff --git a/apps/bitwarden_event_logs/lib/solnlib/conf_manager.py b/apps/bitwarden_event_logs/lib/solnlib/conf_manager.py
deleted file mode 100755
index 203164b4..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/conf_manager.py
+++ /dev/null
@@ -1,617 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""This module contains simple interfaces for Splunk config file management,
-you can update/get/delete stanzas and encrypt/decrypt some fields of stanza
-automatically."""
-
-import json
-import logging
-import traceback
-from typing import List, Union, Dict, NoReturn
-
-from splunklib import binding, client
-
-from . import splunk_rest_client as rest_client
-from .credentials import CredentialManager, CredentialNotExistException
-from .utils import retry
-from .net_utils import is_valid_port, is_valid_hostname
-from .soln_exceptions import (
-    ConfManagerException,
-    ConfStanzaNotExistException,
-    InvalidPortError,
-    InvalidHostnameError,
-)
-
-__all__ = [
-    "ConfFile",
-    "ConfManager",
-]
-
-
-class ConfFile:
-    """Configuration file."""
-
-    ENCRYPTED_TOKEN = "******"
-
-    reserved_keys = ("userName", "appName")
-
-    def __init__(
-        self,
-        name: str,
-        conf: client.ConfigurationFile,
-        session_key: str,
-        app: str,
-        owner: str = "nobody",
-        scheme: str = None,
-        host: str = None,
-        port: int = None,
-        realm: str = None,
-        **context: dict,
-    ):
-        """Initializes ConfFile.
-
-        Arguments:
-            name: Configuration file name.
-            conf: Configuration file object.
-            session_key: Splunk access token.
-            app: App name of namespace.
-            owner: (optional) Owner of namespace, default is `nobody`.
-            scheme: (optional) The access scheme, default is None.
-            host: (optional) The host name, default is None.
-            port: (optional) The port number, default is None.
-            realm: (optional) Realm of credential, default is None.
-            context: Other configurations for Splunk rest client.
-        """
-        self._name = name
-        self._conf = conf
-        self._session_key = session_key
-        self._app = app
-        self._owner = owner
-        self._scheme = scheme
-        self._host = host
-        self._port = port
-        self._context = context
-        self._cred_manager = None
-        # 'realm' is set to provided 'realm' argument otherwise as default
-        # behaviour it is set to 'APP_NAME'.
-        if realm is None:
-            self._realm = self._app
-        else:
-            self._realm = realm
-
-    @property
-    @retry(exceptions=[binding.HTTPError])
-    def _cred_mgr(self):
-        if self._cred_manager is None:
-            self._cred_manager = CredentialManager(
-                self._session_key,
-                self._app,
-                owner=self._owner,
-                realm=self._realm,
-                scheme=self._scheme,
-                host=self._host,
-                port=self._port,
-                **self._context,
-            )
-
-        return self._cred_manager
-
-    def _filter_stanza(self, stanza):
-        for k in self.reserved_keys:
-            if k in stanza:
-                del stanza[k]
-
-        return stanza
-
-    def _encrypt_stanza(self, stanza_name, stanza, encrypt_keys):
-        if not encrypt_keys:
-            return stanza
-
-        encrypt_stanza_keys = [k for k in encrypt_keys if k in stanza]
-        encrypt_fields = {key: stanza[key] for key in encrypt_stanza_keys}
-        if not encrypt_fields:
-            return stanza
-        self._cred_mgr.set_password(stanza_name, json.dumps(encrypt_fields))
-
-        for key in encrypt_stanza_keys:
-            stanza[key] = self.ENCRYPTED_TOKEN
-
-        return stanza
-
-    def _decrypt_stanza(self, stanza_name, encrypted_stanza):
-        encrypted_keys = [
-            key
-            for key in encrypted_stanza
-            if encrypted_stanza[key] == self.ENCRYPTED_TOKEN
-        ]
-        if encrypted_keys:
-            encrypted_fields = json.loads(self._cred_mgr.get_password(stanza_name))
-            for key in encrypted_keys:
-                encrypted_stanza[key] = encrypted_fields[key]
-
-        return encrypted_stanza
-
-    def _delete_stanza_creds(self, stanza_name):
-        self._cred_mgr.delete_password(stanza_name)
-
-    @retry(exceptions=[binding.HTTPError])
-    def stanza_exist(self, stanza_name: str) -> bool:
-        """Check whether stanza exists.
-
-        Arguments:
-            stanza_name: Stanza name.
-
-        Returns:
-            True if stanza exists else False.
-
-        Examples:
-           >>> from solnlib import conf_manager
-           >>> cfm = conf_manager.ConfManager(session_key,
-                                              'Splunk_TA_test')
-           >>> conf = cfm.get_conf('test')
-           >>> conf.stanza_exist('test_stanza')
-        """
-
-        try:
-            self._conf.list(name=stanza_name)[0]
-        except binding.HTTPError as e:
-            if e.status != 404:
-                raise
-
-            return False
-
-        return True
-
-    @retry(exceptions=[binding.HTTPError])
-    def get(self, stanza_name: str, only_current_app: bool = False) -> dict:
-        """Get stanza from configuration file.
-
-        Result is like:
-
-            {
-                'disabled': '0',
-                'eai:appName': 'solnlib_demo',
-                'eai:userName': 'nobody',
-                'k1': '1',
-                'k2': '2'
-            }
-
-        Arguments:
-            stanza_name: Stanza name.
-            only_current_app: Only include current app.
-
-        Returns:
-            Stanza.
-
-        Raises:
-            ConfStanzaNotExistException: If stanza does not exist.
-
-        Examples:
-           >>> from solnlib import conf_manager
-           >>> cfm = conf_manager.ConfManager(session_key,
-                                              'Splunk_TA_test')
-           >>> conf = cfm.get_conf('test')
-           >>> conf.get('test_stanza')
-        """
-
-        try:
-            if only_current_app:
-                stanza_mgrs = self._conf.list(
-                    search="eai:acl.app={} name={}".format(
-                        self._app, stanza_name.replace("=", r"\=")
-                    )
-                )
-            else:
-                stanza_mgrs = self._conf.list(name=stanza_name)
-        except binding.HTTPError as e:
-            if e.status != 404:
-                raise
-
-            raise ConfStanzaNotExistException(
-                f"Stanza: {stanza_name} does not exist in {self._name}.conf"
-            )
-
-        if len(stanza_mgrs) == 0:
-            raise ConfStanzaNotExistException(
-                f"Stanza: {stanza_name} does not exist in {self._name}.conf"
-            )
-
-        stanza = self._decrypt_stanza(stanza_mgrs[0].name, stanza_mgrs[0].content)
-        stanza["eai:access"] = stanza_mgrs[0].access
-        stanza["eai:appName"] = stanza_mgrs[0].access.app
-        return stanza
-
-    @retry(exceptions=[binding.HTTPError])
-    def get_all(self, only_current_app: bool = False) -> dict:
-        """Get all stanzas from configuration file.
-
-        Result is like:
-
-            {
-                'test':
-                    {
-                        'disabled': '0',
-                        'eai:appName': 'solnlib_demo',
-                        'eai:userName': 'nobody',
-                        'k1': '1',
-                        'k2': '2'
-                    }
-            }
-
-        Arguments:
-            only_current_app: Only include current app.
-
-        Returns:
-            Dict of stanzas.
-
-        Examples:
-           >>> from solnlib import conf_manager
-           >>> cfm = conf_manager.ConfManager(session_key,
-                                              'Splunk_TA_test')
-           >>> conf = cfm.get_conf('test')
-           >>> conf.get_all()
-        """
-
-        if only_current_app:
-            stanza_mgrs = self._conf.list(search=f"eai:acl.app={self._app}")
-        else:
-            stanza_mgrs = self._conf.list()
-        res = {}
-        for stanza_mgr in stanza_mgrs:
-            name = stanza_mgr.name
-            key_values = self._decrypt_stanza(name, stanza_mgr.content)
-            key_values["eai:access"] = stanza_mgr.access
-            key_values["eai:appName"] = stanza_mgr.access.app
-            res[name] = key_values
-        return res
-
-    @retry(exceptions=[binding.HTTPError])
-    def update(self, stanza_name: str, stanza: dict, encrypt_keys: List[str] = None):
-        """Update stanza.
-
-        It will try to encrypt the credential automatically fist if
-        encrypt_keys are not None else keep stanza untouched.
-
-        Arguments:
-            stanza_name: Stanza name.
-            stanza: Stanza to update.
-            encrypt_keys: Field names to encrypt.
-
-        Examples:
-           >>> from solnlib import conf_manager
-           >>> cfm = conf_manager.ConfManager(session_key,
-                                              'Splunk_TA_test')
-           >>> conf = cfm.get_conf('test')
-           >>> conf.update('test_stanza', {'k1': 1, 'k2': 2}, ['k1'])
-        """
-
-        stanza = self._filter_stanza(stanza)
-        encrypted_stanza = self._encrypt_stanza(stanza_name, stanza, encrypt_keys)
-
-        try:
-            stanza_mgr = self._conf.list(name=stanza_name)[0]
-        except binding.HTTPError as e:
-            if e.status != 404:
-                raise
-
-            stanza_mgr = self._conf.create(stanza_name)
-
-        stanza_mgr.submit(encrypted_stanza)
-
-    @retry(exceptions=[binding.HTTPError])
-    def delete(self, stanza_name: str):
-        """Delete stanza.
-
-        Arguments:
-            stanza_name: Stanza name to delete.
-
-        Raises:
-            ConfStanzaNotExistException: If stanza does not exist.
-
-        Examples:
-           >>> from solnlib import conf_manager
-           >>> cfm = conf_manager.ConfManager(session_key,
-                                              'Splunk_TA_test')
-           >>> conf = cfm.get_conf('test')
-           >>> conf.delete('test_stanza')
-        """
-
-        try:
-            self._cred_mgr.delete_password(stanza_name)
-        except CredentialNotExistException:
-            pass
-
-        try:
-            self._conf.delete(stanza_name)
-        except KeyError:
-            logging.error(
-                "Delete stanza: %s error: %s.", stanza_name, traceback.format_exc()
-            )
-            raise ConfStanzaNotExistException(
-                f"Stanza: {stanza_name} does not exist in {self._name}.conf"
-            )
-
-    @retry(exceptions=[binding.HTTPError])
-    def reload(self):
-        """Reload configuration file.
-
-        Examples:
-           >>> from solnlib import conf_manager
-           >>> cfm = conf_manager.ConfManager(session_key,
-                                              'Splunk_TA_test')
-           >>> conf = cfm.get_conf('test')
-           >>> conf.reload()
-        """
-
-        self._conf.get("_reload")
-
-
-class ConfManager:
-    """Configuration file manager.
-
-    Examples:
-
-        >>> from solnlib import conf_manager
-        >>> cfm = conf_manager.ConfManager(session_key,
-                                          'Splunk_TA_test')
-
-    Examples:
-        If stanza in passwords.conf is formatted as below:
-
-        `credential:__REST_CREDENTIAL__#Splunk_TA_test#configs/conf-CONF_FILENAME:STANZA_NAME``splunk_cred_sep``1:`
-
-        >>> from solnlib import conf_manager
-        >>> cfm = conf_manager.ConfManager(
-                session_key,
-                'Splunk_TA_test',
-                realm='__REST_CREDENTIAL__#Splunk_TA_test#configs/conf-CONF_FILENAME'
-            )
-    """
-
-    def __init__(
-        self,
-        session_key: str,
-        app: str,
-        owner: str = "nobody",
-        scheme: str = None,
-        host: str = None,
-        port: int = None,
-        realm: str = None,
-        **context: dict,
-    ):
-        """Initializes ConfManager.
-
-        Arguments:
-            session_key: Splunk access token.
-            app: App name of namespace.
-            owner: (optional) Owner of namespace, default is `nobody`.
-            scheme: (optional) The access scheme, default is None.
-            host: (optional) The host name, default is None.
-            port: (optional) The port number, default is None.
-            realm: (optional) Realm of credential, default is None.
-            context: Other configurations for Splunk rest client.
-        """
-        self._session_key = session_key
-        self._app = app
-        self._owner = owner
-        self._scheme = scheme
-        self._host = host
-        self._port = port
-        self._context = context
-        self._rest_client = rest_client.SplunkRestClient(
-            self._session_key,
-            self._app,
-            owner=self._owner,
-            scheme=self._scheme,
-            host=self._host,
-            port=self._port,
-            **self._context,
-        )
-        self._confs = None
-        self._realm = realm
-
-    @retry(exceptions=[binding.HTTPError])
-    def get_conf(self, name: str, refresh: bool = False) -> ConfFile:
-        """Get conf file.
-
-        Arguments:
-            name: Conf file name.
-            refresh: (optional) Flag to refresh conf file list, default is False.
-
-        Returns:
-            Conf file object.
-
-        Raises:
-            ConfManagerException: If `conf_file` does not exist.
-        """
-
-        if self._confs is None or refresh:
-            # Fix bug that can't pass `-` as app name.
-            curr_app = self._rest_client.namespace.app
-            self._rest_client.namespace.app = "dummy"
-            self._confs = self._rest_client.confs
-            self._rest_client.namespace.app = curr_app
-
-        try:
-            conf = self._confs[name]
-        except KeyError:
-            raise ConfManagerException(f"Config file: {name} does not exist.")
-
-        return ConfFile(
-            name,
-            conf,
-            self._session_key,
-            self._app,
-            self._owner,
-            self._scheme,
-            self._host,
-            self._port,
-            self._realm,
-            **self._context,
-        )
-
-    @retry(exceptions=[binding.HTTPError])
-    def create_conf(self, name: str) -> ConfFile:
-        """Create conf file.
-
-        Arguments:
-            name: Conf file name.
-
-        Returns:
-            Conf file object.
-        """
-
-        if self._confs is None:
-            self._confs = self._rest_client.confs
-
-        conf = self._confs.create(name)
-        return ConfFile(
-            name,
-            conf,
-            self._session_key,
-            self._app,
-            self._owner,
-            self._scheme,
-            self._host,
-            self._port,
-            self._realm,
-            **self._context,
-        )
-
-
-def get_log_level(
-    *,
-    logger: logging.Logger,
-    session_key: str,
-    app_name: str,
-    conf_name: str,
-    log_stanza: str = "logging",
-    log_level_field: str = "loglevel",
-    default_log_level: str = "INFO",
-) -> str:
-    """This function returns the log level for the addon from configuration
-    file.
-
-    Arguments:
-        logger: Logger.
-        session_key: Splunk access token.
-        app_name: Add-on name.
-        conf_name: Configuration file name where logging stanza is.
-        log_stanza: Logging stanza to define `log_level_field` and its value.
-        log_level_field: Logging level field name under logging stanza.
-        default_log_level: Default log level to return in case of errors.
-
-    Returns:
-        Log level defined under `logging.log_level_field` field in `conf_name`
-        file. In case of any error, `default_log_level` will be returned.
-
-    Examples:
-        >>> from solnlib import conf_manager
-        >>> log_level = conf_manager.get_log_level(
-        >>>     logger,
-        >>>     "session_key",
-        >>>     "ADDON_NAME",
-        >>>     "splunk_ta_addon_settings",
-        >>> )
-    """
-    try:
-        cfm = ConfManager(
-            session_key,
-            app_name,
-            realm=f"__REST_CREDENTIAL__#{app_name}#configs/conf-{conf_name}",
-        )
-        conf = cfm.get_conf(conf_name)
-    except ConfManagerException:
-        logger.error(
-            f"Failed to fetch configuration file {conf_name}, "
-            f"taking {default_log_level} as log level."
-        )
-        return default_log_level
-    try:
-        logging_details = conf.get(log_stanza)
-        return logging_details.get(log_level_field, default_log_level)
-    except ConfStanzaNotExistException:
-        logger.error(
-            f'"logging" stanza does not exist under {conf_name}, '
-            f"taking {default_log_level} as log level."
-        )
-        return default_log_level
-
-
-def get_proxy_dict(
-    logger: logging.Logger,
-    session_key: str,
-    app_name: str,
-    conf_name: str,
-    proxy_stanza: str = "proxy",
-    **kwargs,
-) -> Union[Dict[str, str], NoReturn]:
-    """This function returns the proxy settings for the addon from
-    configuration file.
-
-    Arguments:
-        logger: Logger.
-        session_key: Splunk access token.
-        app_name: Add-on name.
-        conf_name: Configuration file name where logging stanza is.
-        proxy_stanza: Proxy stanza that would contain the Proxy details
-    Returns:
-        A dictionary is returned with stanza details present in the file.
-        The keys related to `eai` are removed before returning.
-
-    Examples:
-        >>> from solnlib import conf_manager
-        >>> proxy_details = conf_manager.get_proxy_dict(
-        >>>     logger,
-        >>>     "session_key",
-        >>>     "ADDON_NAME",
-        >>>     "splunk_ta_addon_settings",
-        >>> )
-    """
-    proxy_dict = {}
-    try:
-        cfm = ConfManager(
-            session_key,
-            app_name,
-            realm=f"__REST_CREDENTIAL__#{app_name}#configs/conf-{conf_name}",
-        )
-        conf = cfm.get_conf(conf_name)
-    except Exception:
-        raise ConfManagerException(f"Failed to fetch configuration file '{conf_name}'.")
-    else:
-        try:
-            proxy_dict = conf.get(proxy_stanza)
-        except Exception:
-            raise ConfStanzaNotExistException(
-                f"Failed to fetch '{proxy_stanza}' from the configuration file '{conf_name}'. "
-            )
-        else:
-            # remove the other fields that are added by ConfFile class
-            proxy_dict.pop("disabled", None)
-            proxy_dict.pop("eai:access", None)
-            proxy_dict.pop("eai:appName", None)
-            proxy_dict.pop("eai:userName", None)
-
-            if "proxy_port" in kwargs:
-                if not is_valid_port(proxy_dict.get(kwargs["proxy_port"])):
-                    logger.error("Invalid proxy port provided.")
-                    raise InvalidPortError("The provided port is not valid.")
-            if "proxy_host" in kwargs:
-                if not is_valid_hostname(proxy_dict.get(kwargs["proxy_host"])):
-                    logger.error("Invalid proxy host provided.")
-                    raise InvalidHostnameError("The provided hostname is not valid.")
-    return proxy_dict
diff --git a/apps/bitwarden_event_logs/lib/solnlib/credentials.py b/apps/bitwarden_event_logs/lib/solnlib/credentials.py
deleted file mode 100755
index 29ee1ff6..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/credentials.py
+++ /dev/null
@@ -1,337 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""This module contains Splunk credential related interfaces."""
-
-import re
-import warnings
-from typing import Dict, List
-
-from splunklib import binding, client
-
-from . import splunk_rest_client as rest_client
-from .utils import retry
-
-__all__ = [
-    "CredentialException",
-    "CredentialNotExistException",
-    "CredentialManager",
-]
-
-
-class CredentialException(Exception):
-    """General exception regarding credentials."""
-
-    pass
-
-
-class CredentialNotExistException(Exception):
-    """Exception is raised when credentials do not exist."""
-
-    pass
-
-
-class CredentialManager:
-    """Credential manager.
-
-    Examples:
-       >>> from solnlib import credentials
-       >>> cm = credentials.CredentialManager(session_key,
-                                              'Splunk_TA_test',
-                                              realm='realm_test')
-    """
-
-    # Splunk can only encrypt string with length <=255
-    SPLUNK_CRED_LEN_LIMIT = 255
-
-    # Splunk credential separator
-    SEP = "``splunk_cred_sep``"
-
-    # Splunk credential end mark
-    END_MARK = (
-        "``splunk_cred_sep``S``splunk_cred_sep``P``splunk_cred_sep``L``splunk_cred_sep``"
-        "U``splunk_cred_sep``N``splunk_cred_sep``K``splunk_cred_sep``"
-    )
-
-    def __init__(
-        self,
-        session_key: str,
-        app: str,
-        owner: str = "nobody",
-        realm: str = None,
-        scheme: str = None,
-        host: str = None,
-        port: int = None,
-        **context: dict,
-    ):
-        """Initializes CredentialManager.
-
-        Arguments:
-            session_key: Splunk access token.
-            app: App name of namespace.
-            owner: (optional) Owner of namespace, default is `nobody`.
-            realm: (optional) Realm of credential, default is None.
-            scheme: (optional) The access scheme, default is None.
-            host: (optional) The host name, default is None.
-            port: (optional) The port number, default is None.
-            context: Other configurations for Splunk rest client.
-        """
-        self._realm = realm
-        self.service = rest_client.SplunkRestClient(
-            session_key,
-            app,
-            owner=owner,
-            scheme=scheme,
-            host=host,
-            port=port,
-            **context,
-        )
-        self._storage_passwords = self.service.storage_passwords
-
-    @retry(exceptions=[binding.HTTPError])
-    def get_password(self, user: str) -> str:
-        """Get password.
-
-        Arguments:
-            user: User name.
-
-        Returns:
-            Clear user password.
-
-        Raises:
-            CredentialNotExistException: If password for 'realm:user' doesn't exist.
-
-        Examples:
-           >>> from solnlib import credentials
-           >>> cm = credentials.CredentialManager(session_key,
-                                                  'Splunk_TA_test',
-                                                  realm='realm_test')
-           >>> cm.get_password('testuser2')
-        """
-        if self._realm is not None:
-            passwords = self.get_clear_passwords_in_realm()
-        else:
-            passwords = self.get_clear_passwords()
-        for password in passwords:
-            if password["username"] == user and password["realm"] == self._realm:
-                return password["clear_password"]
-
-        raise CredentialNotExistException(
-            f"Failed to get password of realm={self._realm}, user={user}."
-        )
-
-    @retry(exceptions=[binding.HTTPError])
-    def set_password(self, user: str, password: str):
-        """Set password.
-
-        Arguments:
-            user: User name.
-            password: User password.
-
-        Examples:
-           >>> from solnlib import credentials
-           >>> cm = credentials.CredentialManager(session_key,
-                                                  'Splunk_TA_test',
-                                                  realm='realm_test')
-           >>> cm.set_password('testuser1', 'password1')
-        """
-        length = 0
-        index = 1
-        while length < len(password):
-            curr_str = password[
-                length : length + self.SPLUNK_CRED_LEN_LIMIT  # noqa: E203
-            ]
-            partial_user = self.SEP.join([user, str(index)])
-            self._update_password(partial_user, curr_str)
-            length += self.SPLUNK_CRED_LEN_LIMIT
-            index += 1
-
-        # Append another stanza to mark the end of the password
-        partial_user = self.SEP.join([user, str(index)])
-        self._update_password(partial_user, self.END_MARK)
-
-    @retry(exceptions=[binding.HTTPError])
-    def _update_password(self, user: str, password: str):
-        """Update password.
-
-        Arguments:
-            user: User name.
-            password: User password.
-
-        Examples:
-           >>> from solnlib import credentials
-           >>> cm = credentials.CredentialManager(session_key,
-                                                  'Splunk_TA_test',
-                                                  realm='realm_test')
-           >>> cm._update_password('testuser1', 'password1')
-        """
-        try:
-            self._storage_passwords.create(password, user, self._realm)
-        except binding.HTTPError as ex:
-            if ex.status == 409:
-                if self._realm is not None:
-                    passwords = self.get_raw_passwords_in_realm()
-                else:
-                    passwords = self.get_raw_passwords()
-                for pwd_stanza in passwords:
-                    if pwd_stanza.realm == self._realm and pwd_stanza.username == user:
-                        pwd_stanza.update(password=password)
-                        return
-                raise ValueError(
-                    f"Can not get the password object for realm: {self._realm} user: {user}"
-                )
-            else:
-                raise ex
-
-    @retry(exceptions=[binding.HTTPError])
-    def delete_password(self, user: str):
-        """Delete password.
-
-        Arguments:
-            user: User name.
-
-        Raises:
-             CredentialNotExistException: If password of realm:user doesn't exist.
-
-        Examples:
-           >>> from solnlib import credentials
-           >>> cm = credentials.CredentialManager(session_key,
-                                                  'Splunk_TA_test',
-                                                  realm='realm_test')
-           >>> cm.delete_password('testuser1')
-        """
-        if self._realm is not None:
-            passwords = self.get_raw_passwords_in_realm()
-        else:
-            passwords = self.get_raw_passwords()
-        deleted = False
-        ent_pattern = re.compile(
-            r"({}{}\d+)".format(user.replace("\\", "\\\\"), self.SEP)
-        )
-        for password in passwords:
-            match = (user == password.username) or ent_pattern.match(password.username)
-            if match and password.realm == self._realm:
-                password.delete()
-                deleted = True
-
-        if not deleted:
-            raise CredentialNotExistException(
-                f"Failed to delete password of realm={self._realm}, user={user}"
-            )
-
-    def get_raw_passwords(self) -> List[client.StoragePassword]:
-        """Returns all passwords in the "raw" format."""
-        warnings.warn(
-            "Please pass realm to the CredentialManager, "
-            "so it can utilize get_raw_passwords_in_realm method instead."
-        )
-        return self._storage_passwords.list(count=-1)
-
-    def get_raw_passwords_in_realm(self) -> List[client.StoragePassword]:
-        """Returns all passwords within the realm in the "raw" format."""
-        if self._realm is None:
-            raise ValueError("No realm was specified")
-        return self._storage_passwords.list(count=-1, search=f"realm={self._realm}")
-
-    def get_clear_passwords(self) -> List[Dict[str, str]]:
-        """Returns all passwords in the "clear" format."""
-        warnings.warn(
-            "Please pass realm to the CredentialManager, "
-            "so it can utilize get_clear_passwords_in_realm method instead."
-        )
-        raw_passwords = self.get_raw_passwords()
-        return self._get_clear_passwords(raw_passwords)
-
-    def get_clear_passwords_in_realm(self) -> List[Dict[str, str]]:
-        """Returns all passwords within the realm in the "clear" format."""
-        if self._realm is None:
-            raise ValueError("No realm was specified")
-        raw_passwords = self.get_raw_passwords_in_realm()
-        return self._get_clear_passwords(raw_passwords)
-
-    def _get_all_passwords_in_realm(self) -> List[client.StoragePassword]:
-        warnings.warn(
-            "_get_all_passwords_in_realm is deprecated, "
-            "please use get_raw_passwords_in_realm instead.",
-            stacklevel=2,
-        )
-        if self._realm:
-            all_passwords = self._storage_passwords.list(
-                count=-1, search=f"realm={self._realm}"
-            )
-        else:
-            all_passwords = self._storage_passwords.list(count=-1, search="")
-        return all_passwords
-
-    def _get_clear_passwords(
-        self, passwords: List[client.StoragePassword]
-    ) -> List[Dict[str, str]]:
-        results = {}
-        ptn = re.compile(rf"(.+){self.SEP}(\d+)")
-        for password in passwords:
-            match = ptn.match(password.name)
-            if match:
-                actual_name = match.group(1) + ":"
-                index = int(match.group(2))
-                if actual_name in results:
-                    exist_stanza = results[actual_name]
-                else:
-                    exist_stanza = {}
-                    exist_stanza["name"] = actual_name
-                    exist_stanza["realm"] = password.realm
-                    exist_stanza["username"] = password.username.split(self.SEP)[0]
-                    exist_stanza["clears"] = {}
-                    results[actual_name] = exist_stanza
-
-                exist_stanza["clears"][index] = password.clear_password
-
-        # Backward compatibility
-        # To deal with the password with only one stanza which is generated by the old version.
-        for password in passwords:
-            match = ptn.match(password.name)
-            if (not match) and (password.name not in results):
-                results[password.name] = {
-                    "name": password.name,
-                    "realm": password.realm,
-                    "username": password.username,
-                    "clear_password": password.clear_password,
-                }
-
-        # Merge password by index
-        for name, values in list(results.items()):
-            field_clear = values.get("clears")
-            if field_clear:
-                clear_password = ""
-                for index in sorted(field_clear.keys()):
-                    if field_clear[index] != self.END_MARK:
-                        clear_password += field_clear[index]
-                    else:
-                        break
-                values["clear_password"] = clear_password
-
-                del values["clears"]
-
-        return list(results.values())
-
-    @retry(exceptions=[binding.HTTPError])
-    def _get_all_passwords(self) -> List[Dict[str, str]]:
-        warnings.warn(
-            "_get_all_passwords is deprecated, "
-            "please use get_all_passwords_in_realm instead.",
-            stacklevel=2,
-        )
-        passwords = self._storage_passwords.list(count=-1)
-        return self._get_clear_passwords(passwords)
diff --git a/apps/bitwarden_event_logs/lib/solnlib/file_monitor.py b/apps/bitwarden_event_logs/lib/solnlib/file_monitor.py
deleted file mode 100755
index 971d1024..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/file_monitor.py
+++ /dev/null
@@ -1,133 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""This module contains file monitoring class that can be used to check files
-change periodically and call callback function to handle properly when
-detecting files change."""
-import logging
-import os.path as op
-import threading
-import time
-import traceback
-from typing import Any, Callable, List
-
-__all__ = ["FileChangesChecker", "FileMonitor"]
-
-
-class FileChangesChecker:
-    """Files change checker."""
-
-    def __init__(self, callback: Callable[[List[str]], Any], files: List):
-        """Initializes FileChangesChecker.
-
-        Arguments:
-            callback: Callback function for files change.
-            files: Files to be monitored with full path.
-        """
-        self._callback = callback
-        self._files = files
-
-        self.file_mtimes = {file_name: None for file_name in self._files}
-        for k in self.file_mtimes:
-            try:
-                self.file_mtimes[k] = op.getmtime(k)
-            except OSError:
-                logging.debug(f"Getmtime for {k}, failed: {traceback.format_exc()}")
-
-    def check_changes(self) -> bool:
-        """Check files change.
-
-        If some files are changed and callback function is not None, call
-        callback function to handle files change.
-
-        Returns:
-            True if files changed else False
-        """
-        logging.debug(f"Checking files={self._files}")
-        file_mtimes = self.file_mtimes
-        changed_files = []
-        for f, last_mtime in list(file_mtimes.items()):
-            try:
-                current_mtime = op.getmtime(f)
-                if current_mtime != last_mtime:
-                    file_mtimes[f] = current_mtime
-                    changed_files.append(f)
-                    logging.info(f"Detect {f} has changed", f)
-            except OSError:
-                pass
-        if changed_files:
-            if self._callback:
-                self._callback(changed_files)
-            return True
-        return False
-
-
-class FileMonitor:
-    """Files change monitor.
-
-    Monitor files change in a separated thread and call callback
-    when there is files change.
-
-    Examples:
-      >>> import solnlib.file_monitor as fm
-      >>> fm = fm.FileMonitor(fm_callback, files_list, 5)
-      >>> fm.start()
-    """
-
-    def __init__(
-        self, callback: Callable[[List[str]], Any], files: List, interval: int = 1
-    ):
-        """Initializes FileMonitor.
-
-        Arguments:
-            callback: Callback for handling files change.
-            files: Files to monitor.
-            interval: Interval to check files change.
-        """
-        self._checker = FileChangesChecker(callback, files)
-        self._thr = threading.Thread(target=self._do_monitor)
-        self._thr.daemon = True
-        self._interval = interval
-        self._started = False
-
-    def start(self):
-        """Start file monitor.
-
-        Start a background thread to monitor files change.
-        """
-
-        if self._started:
-            return
-        self._started = True
-
-        self._thr.start()
-
-    def stop(self):
-        """Stop file monitor.
-
-        Stop the background thread to monitor files change.
-        """
-
-        self._started = False
-
-    def _do_monitor(self):
-        while self._started:
-            self._checker.check_changes()
-
-            for _ in range(self._interval):
-                if not self._started:
-                    break
-                time.sleep(1)
diff --git a/apps/bitwarden_event_logs/lib/solnlib/hec_config.py b/apps/bitwarden_event_logs/lib/solnlib/hec_config.py
deleted file mode 100755
index d5c7cd24..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/hec_config.py
+++ /dev/null
@@ -1,182 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-from splunklib import binding
-
-from . import splunk_rest_client as rest_client
-from .utils import retry
-
-__all__ = ["HECConfig"]
-
-
-class HECConfig:
-    """HTTP Event Collector configuration."""
-
-    input_type = "http"
-
-    def __init__(
-        self,
-        session_key: str,
-        scheme: str = None,
-        host: str = None,
-        port: int = None,
-        **context: dict
-    ):
-        """Initializes HECConfig.
-
-        Arguments:
-            session_key: Splunk access token.
-            scheme: (optional) The access scheme, default is None.
-            host: (optional) The host name, default is None.
-            port: (optional) The port number, default is None.
-            context: Other configurations for Splunk rest client.
-        """
-        self._rest_client = rest_client.SplunkRestClient(
-            session_key,
-            "splunk_httpinput",
-            scheme=scheme,
-            host=host,
-            port=port,
-            **context
-        )
-
-    @retry(exceptions=[binding.HTTPError])
-    def get_settings(self) -> dict:
-        """Get http data input global settings.
-
-        Returns:
-            HTTP global settings, for example:
-
-                {
-                    'enableSSL': 1,
-                    'disabled': 0,
-                    'useDeploymentServer': 0,
-                    'port': 8088
-                }
-        """
-
-        return self._do_get_input(self.input_type).content
-
-    @retry(exceptions=[binding.HTTPError])
-    def update_settings(self, settings: dict):
-        """Update http data input global settings.
-
-        Arguments:
-            settings: HTTP global settings.
-        """
-
-        res = self._do_get_input(self.input_type)
-        res.update(**settings)
-
-    @retry(exceptions=[binding.HTTPError])
-    def create_input(self, name: str, stanza: dict) -> dict:
-        """Create http data input.
-
-        Arguments:
-            name: HTTP data input name.
-            stanza: Data input stanza content.
-
-        Returns:
-            Created input.
-
-        Examples:
-           >>> from solnlib.hec_config import HECConfig
-           >>> hec = HECConfig(session_key)
-           >>> hec.create_input('my_hec_data_input',
-                                {'index': 'main', 'sourcetype': 'hec'})
-        """
-
-        res = self._rest_client.inputs.create(name, self.input_type, **stanza)
-        return res.content
-
-    @retry(exceptions=[binding.HTTPError])
-    def update_input(self, name: str, stanza: dict):
-        """Update http data input.
-
-        It will create if the data input doesn't exist.
-
-        Arguments:
-            name: HTTP data input name.
-            stanza: Data input stanza.
-
-        Examples:
-           >>> from solnlib import HEConfig
-           >>> hec = HECConfig(session_key)
-           >>> hec.update_input('my_hec_data_input',
-                                {'index': 'main', 'sourcetype': 'hec2'})
-        """
-
-        res = self._do_get_input(name)
-        if res is None:
-            return self.create_input(name, stanza)
-        res.update(**stanza)
-
-    @retry(exceptions=[binding.HTTPError])
-    def delete_input(self, name: str):
-        """Delete http data input.
-
-        Arguments:
-            name: HTTP data input name.
-        """
-
-        try:
-            self._rest_client.inputs.delete(name, self.input_type)
-        except KeyError:
-            pass
-
-    @retry(exceptions=[binding.HTTPError])
-    def get_input(self, name: str) -> dict:
-        """Get http data input.
-
-        Arguments:
-            name: HTTP event collector data input name.
-
-        Returns:
-            HTTP event collector data input config dict.
-        """
-
-        res = self._do_get_input(name)
-        if res:
-            return res.content
-        else:
-            return None
-
-    def _do_get_input(self, name):
-        try:
-            return self._rest_client.inputs[(name, self.input_type)]
-        except KeyError:
-            return None
-
-    @retry(exceptions=[binding.HTTPError])
-    def get_limits(self) -> dict:
-        """Get HTTP input limits.
-
-        Returns:
-            HTTP input limits.
-        """
-
-        return self._rest_client.confs["limits"]["http_input"].content
-
-    @retry(exceptions=[binding.HTTPError])
-    def set_limits(self, limits: dict):
-        """Set HTTP input limits.
-
-        Arguments:
-            limits: HTTP input limits.
-        """
-
-        res = self._rest_client.confs["limits"]["http_input"]
-        res.submit(limits)
diff --git a/apps/bitwarden_event_logs/lib/solnlib/log.py b/apps/bitwarden_event_logs/lib/solnlib/log.py
deleted file mode 100755
index e1c0511c..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/log.py
+++ /dev/null
@@ -1,373 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""This module provides log functionalities."""
-
-import logging
-import logging.handlers
-import os.path as op
-import traceback
-from functools import partial
-from threading import Lock
-from typing import Dict, Any
-
-from .pattern import Singleton
-from .splunkenv import make_splunkhome_path
-
-__all__ = ["log_enter_exit", "LogException", "Logs"]
-
-
-def log_enter_exit(logger: logging.Logger):
-    """Decorator for logger to log function enter and exit.
-
-    This decorator will generate a lot of debug log, please add this
-    only when it is required.
-
-    Arguments:
-        logger: Logger to decorate.
-
-    Examples:
-        >>> @log_enter_exit
-        >>> def myfunc():
-        >>>     doSomething()
-    """
-
-    def log_decorator(func):
-        def wrapper(*args, **kwargs):
-            logger.debug("%s entered", func.__name__)
-            result = func(*args, **kwargs)
-            logger.debug("%s exited", func.__name__)
-            return result
-
-        return wrapper
-
-    return log_decorator
-
-
-class LogException(Exception):
-    """Exception raised by Logs class."""
-
-    pass
-
-
-class Logs(metaclass=Singleton):
-    """A singleton class that manage all kinds of logger.
-
-    Examples:
-      >>> from solnlib import log
-      >>> log.Logs.set_context(directory='/var/log/test',
-                               namespace='test')
-      >>> logger = log.Logs().get_logger('mymodule')
-      >>> logger.set_level(logging.DEBUG)
-      >>> logger.debug('a debug log')
-    """
-
-    # Normal logger settings
-    _default_directory = None
-    _default_namespace = None
-    _default_log_format = (
-        "%(asctime)s log_level=%(levelname)s pid=%(process)d tid=%(threadName)s "
-        "file=%(filename)s:%(funcName)s:%(lineno)d | %(message)s"
-    )
-    _default_log_level = logging.INFO
-    _default_max_bytes = 25000000
-    _default_backup_count = 5
-
-    # Default root logger settings
-    _default_root_logger_log_file = "solnlib"
-
-    @classmethod
-    def set_context(cls, **context: dict):
-        """Set log context.
-
-        List of keyword arguments:
-
-            directory: Log directory, default is splunk log root directory.
-            namespace: Logger namespace, default is None.
-            log_format: Log format, default is `_default_log_format`.
-            log_level: Log level, default is logging.INFO.
-            max_bytes: The maximum log file size before rollover, default is 25000000.
-            backup_count: The number of log files to retain,default is 5.
-            root_logger_log_file: Root logger log file name, default is 'solnlib'   .
-
-        Arguments:
-            context: Keyword arguments. See list of arguments above.
-        """
-        if "directory" in context:
-            cls._default_directory = context["directory"]
-        if "namespace" in context:
-            cls._default_namespace = context["namespace"]
-        if "log_format" in context:
-            cls._default_log_format = context["log_format"]
-        if "log_level" in context:
-            cls._default_log_level = context["log_level"]
-        if "max_bytes" in context:
-            cls._default_max_bytes = context["max_bytes"]
-        if "backup_count" in context:
-            cls._default_backup_count = context["backup_count"]
-        if "root_logger_log_file" in context:
-            cls._default_root_logger_log_file = context["root_logger_log_file"]
-            cls._reset_root_logger()
-
-    @classmethod
-    def _reset_root_logger(cls):
-        logger = logging.getLogger()
-        log_file = cls._get_log_file(cls._default_root_logger_log_file)
-        file_handler = logging.handlers.RotatingFileHandler(
-            log_file,
-            mode="a",
-            maxBytes=cls._default_max_bytes,
-            backupCount=cls._default_backup_count,
-        )
-        file_handler.setFormatter(logging.Formatter(cls._default_log_format))
-        logger.addHandler(file_handler)
-        logger.setLevel(cls._default_log_level)
-
-    @classmethod
-    def _get_log_file(cls, name):
-        if cls._default_namespace:
-            name = f"{cls._default_namespace}_{name}.log"
-        else:
-            name = f"{name}.log"
-
-        if cls._default_directory:
-            directory = cls._default_directory
-        else:
-            try:
-                directory = make_splunkhome_path(["var", "log", "splunk"])
-            except KeyError:
-                raise LogException(
-                    "Log directory is empty, please set log directory "
-                    'by calling Logs.set_context(directory="/var/log/...").'
-                )
-        log_file = op.sep.join([directory, name])
-
-        return log_file
-
-    def __init__(self):
-        self._lock = Lock()
-        self._loggers = {}
-
-    def get_logger(self, name: str) -> logging.Logger:
-        """Get logger with the name of `name`.
-
-        If logger with the name of `name` exists just return else create a new
-        logger with the name of `name`.
-
-        Arguments:
-            name: Logger name, it will be used as log file name too.
-
-        Returns:
-            A named logger.
-        """
-
-        with self._lock:
-            log_file = self._get_log_file(name)
-            if log_file in self._loggers:
-                return self._loggers[log_file]
-
-            logger = logging.getLogger(log_file)
-            handler_exists = any(
-                [True for h in logger.handlers if h.baseFilename == log_file]
-            )
-            if not handler_exists:
-                file_handler = logging.handlers.RotatingFileHandler(
-                    log_file,
-                    mode="a",
-                    maxBytes=self._default_max_bytes,
-                    backupCount=self._default_backup_count,
-                )
-                file_handler.setFormatter(logging.Formatter(self._default_log_format))
-                logger.addHandler(file_handler)
-                logger.setLevel(self._default_log_level)
-                logger.propagate = False
-
-            self._loggers[log_file] = logger
-            return logger
-
-    def set_level(self, level: int, name: str = None):
-        """Set log level of logger.
-
-        Set log level of all logger if `name` is None else of
-        logger with the name of `name`.
-
-        Arguments:
-            level: Log level to set.
-            name: The name of logger, default is None.
-        """
-
-        with self._lock:
-            if name:
-                log_file = self._get_log_file(name)
-                logger = self._loggers.get(log_file)
-                if logger:
-                    logger.setLevel(level)
-            else:
-                self._default_log_level = level
-                for logger in list(self._loggers.values()):
-                    logger.setLevel(level)
-                logging.getLogger().setLevel(level)
-
-
-def log_event(
-    logger: logging.Logger, key_values: Dict[str, Any], log_level: int = logging.INFO
-):
-    """General function to log any event in key-value format."""
-    message = " ".join([f"{k}={v}" for k, v in key_values.items()])
-    logger.log(log_level, message)
-
-
-def modular_input_start(logger: logging.Logger, modular_input_name: str):
-    """Specific function to log the start of the modular input."""
-    log_event(
-        logger,
-        {
-            "action": "started",
-            "modular_input_name": modular_input_name,
-        },
-    )
-
-
-def modular_input_end(logger: logging.Logger, modular_input_name: str):
-    """Specific function to log the end of the modular input."""
-    log_event(
-        logger,
-        {
-            "action": "ended",
-            "modular_input_name": modular_input_name,
-        },
-    )
-
-
-def _base_error_log(
-    logger,
-    exc: Exception,
-    exe_label,
-    full_msg: bool = True,
-    msg_before: str = None,
-    msg_after: str = None,
-):
-    log_exception(
-        logger,
-        exc,
-        exc_label=exe_label,
-        full_msg=full_msg,
-        msg_before=msg_before,
-        msg_after=msg_after,
-    )
-
-
-log_connection_error = partial(_base_error_log, exe_label="Connection Error")
-log_configuration_error = partial(_base_error_log, exe_label="Configuration Error")
-log_permission_error = partial(_base_error_log, exe_label="Permission Error")
-log_authentication_error = partial(_base_error_log, exe_label="Authentication Error")
-log_server_error = partial(_base_error_log, exe_label="Server Error")
-
-
-def events_ingested(
-    logger: logging.Logger,
-    modular_input_name: str,
-    sourcetype: str,
-    n_events: int,
-    index: str,
-    account: str = None,
-    host: str = None,
-    license_usage_source: str = None,
-):
-    """Specific function to log the basic information of events ingested for
-    the monitoring dashboard.
-
-    Arguments:
-        logger: Add-on logger.
-        modular_input_name: Full name of the modular input. It needs to be in a format `://`.
-            In case of invalid format ValueError is raised.
-        sourcetype: Source type used to write event.
-        n_events: Number of ingested events.
-        index: Index used to write event.
-        license_usage_source: source used to match data with license_usage.log.
-        account: Account used to write event. (optional)
-        host: Host used to write event. (optional)
-    """
-
-    if "://" in modular_input_name:
-        input_name = modular_input_name.split("/")[-1]
-    else:
-        raise ValueError(
-            f"Invalid modular input name: {modular_input_name}. "
-            f"It should be in format ://"
-        )
-
-    result = {
-        "action": "events_ingested",
-        "modular_input_name": license_usage_source
-        if license_usage_source
-        else modular_input_name,
-        "sourcetype_ingested": sourcetype,
-        "n_events": n_events,
-        "event_input": input_name,
-        "event_index": index,
-    }
-
-    if account:
-        result["event_account"] = account
-
-    if host:
-        result["event_host"] = host
-
-    log_event(logger, result)
-
-
-def log_exception(
-    logger: logging.Logger,
-    e: Exception,
-    exc_label: str,
-    full_msg: bool = True,
-    msg_before: str = None,
-    msg_after: str = None,
-    log_level: int = logging.ERROR,
-):
-    """General function to log exceptions.
-
-    Arguments:
-        logger: Add-on logger.
-        e: Exception to log.
-        exc_label: label for the error to categorize it.
-        full_msg: if set to True, full traceback will be logged. Default: True
-        msg_before: custom message before exception traceback. Default: None
-        msg_after: custom message after exception traceback. Default: None
-        log_level: Log level to log exception. Default: ERROR.
-    """
-
-    msg = _get_exception_message(e, full_msg, msg_before, msg_after)
-    logger.log(log_level, f'exc_l="{exc_label}" {msg}')
-
-
-def _get_exception_message(
-    e: Exception,
-    full_msg: bool = True,
-    msg_before: str = None,
-    msg_after: str = None,
-) -> str:
-    exc_type, exc_value, exc_traceback = type(e), e, e.__traceback__
-    if full_msg:
-        error = traceback.format_exception(exc_type, exc_value, exc_traceback)
-    else:
-        error = traceback.format_exception_only(exc_type, exc_value)
-
-    msg_start = msg_before if msg_before is not None else ""
-    msg_mid = "".join(error)
-    msg_end = msg_after if msg_after is not None else ""
-    return f"{msg_start}\n{msg_mid}\n{msg_end}"
diff --git a/apps/bitwarden_event_logs/lib/solnlib/modular_input/__init__.py b/apps/bitwarden_event_logs/lib/solnlib/modular_input/__init__.py
deleted file mode 100755
index 1b6d6eb3..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/modular_input/__init__.py
+++ /dev/null
@@ -1,38 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""Splunk modular input."""
-
-from splunklib.modularinput.argument import Argument
-
-from .checkpointer import CheckpointerException, FileCheckpointer, KVStoreCheckpointer
-from .event import EventException, HECEvent, XMLEvent
-from .event_writer import ClassicEventWriter, HECEventWriter
-from .modular_input import ModularInput, ModularInputException
-
-__all__ = [
-    "EventException",
-    "XMLEvent",
-    "HECEvent",
-    "ClassicEventWriter",
-    "HECEventWriter",
-    "CheckpointerException",
-    "KVStoreCheckpointer",
-    "FileCheckpointer",
-    "Argument",
-    "ModularInputException",
-    "ModularInput",
-]
diff --git a/apps/bitwarden_event_logs/lib/solnlib/modular_input/checkpointer.py b/apps/bitwarden_event_logs/lib/solnlib/modular_input/checkpointer.py
deleted file mode 100755
index 378b2f33..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/modular_input/checkpointer.py
+++ /dev/null
@@ -1,263 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""This module provides two kinds of checkpointer: KVStoreCheckpointer,
-FileCheckpointer for modular input to save checkpoint."""
-
-import base64
-import json
-import logging
-import os
-import os.path as op
-import traceback
-import warnings
-from abc import ABCMeta, abstractmethod
-from typing import Any, Dict, Iterable, Optional
-
-from splunklib import binding
-
-from solnlib import _utils, utils
-
-__all__ = ["CheckpointerException", "KVStoreCheckpointer", "FileCheckpointer"]
-
-
-class CheckpointerException(Exception):
-    pass
-
-
-class Checkpointer(metaclass=ABCMeta):
-    """Base class of checkpointer."""
-
-    @abstractmethod
-    def update(self, key: str, state: Any):
-        """Updates document with an id that equals to `key` and `state` as
-        document data."""
-
-    @abstractmethod
-    def batch_update(self, states: Iterable[Dict[str, Any]]):
-        """Updates multiple documents."""
-
-    @abstractmethod
-    def get(self, key: str) -> dict:
-        """Gets document with an id that equals to `key`."""
-
-    @abstractmethod
-    def delete(self, key: str):
-        """Deletes document with an id that equals to `key`."""
-
-
-class KVStoreCheckpointer(Checkpointer):
-    """KVStore checkpointer.
-
-    Use KVStore to save modular input checkpoint.
-
-    More information about KV Store in Splunk is
-    [here](https://dev.splunk.com/enterprise/docs/developapps/manageknowledge/kvstore/aboutkvstorecollections).
-
-    Examples:
-        >>> from solnlib.modular_input import checkpointer
-        >>> checkpoint = checkpointer.KVStoreCheckpointer(
-                "unique_addon_checkpoints",
-                "session_key",
-                "unique_addon"
-            )
-        >>> checkpoint.update("input_1", {"timestamp": 1638043093})
-        >>> checkpoint.get("input_1")
-        >>> # returns {"timestamp": 1638043093}
-    """
-
-    def __init__(
-        self,
-        collection_name: str,
-        session_key: str,
-        app: str,
-        owner: Optional[str] = "nobody",
-        scheme: Optional[str] = None,
-        host: Optional[str] = None,
-        port: Optional[int] = None,
-        **context: Any,
-    ):
-        """Initializes KVStoreCheckpointer.
-
-        Arguments:
-            collection_name: Collection name of kvstore checkpointer.
-            session_key: Splunk access token.
-            app: App name of namespace.
-            owner: (optional) Owner of namespace, default is `nobody`.
-            scheme: (optional) The access scheme, default is None.
-            host: (optional) The host name, default is None.
-            port: (optional) The port number, default is None.
-            context: Other configurations for Splunk rest client.
-
-        Raises:
-            binding.HTTPError: HTTP error different from 404, for example 503
-                when KV Store is initializing and not ready to serve requests.
-            CheckpointerException: If init KV Store checkpointer failed.
-        """
-        try:
-            if not context.get("pool_connections"):
-                context["pool_connections"] = 5
-            if not context.get("pool_maxsize"):
-                context["pool_maxsize"] = 5
-            self._collection_data = _utils.get_collection_data(
-                collection_name,
-                session_key,
-                app,
-                owner,
-                scheme,
-                host,
-                port,
-                {"state": "string"},
-                **context,
-            )
-        except KeyError:
-            raise CheckpointerException("Get KV Store checkpointer failed.")
-
-    @utils.retry(exceptions=[binding.HTTPError])
-    def update(self, key: str, state: Any) -> None:
-        """Updates document with an id that equals to `key` and `state` as
-        document data.
-
-        Arguments:
-            key: `id` of the document to update.
-            state: Document data to update. It can be integer, string,
-                or a dict, or anything that can be an argument to `json.dumps`.
-
-        Raises:
-            binding.HTTPError: when an error occurred in Splunk, for example,
-                when Splunk is restarting and KV Store is not yet initialized.
-        """
-        record = {"_key": key, "state": json.dumps(state)}
-        self._collection_data.batch_save(record)
-
-    @utils.retry(exceptions=[binding.HTTPError])
-    def batch_update(self, states: Iterable[Dict[str, Any]]) -> None:
-        """Updates multiple documents.
-
-        Arguments:
-            states: Iterable that contains documents to update. Document should
-                be a dict with at least "state" key.
-
-        Raises:
-            binding.HTTPError: when an error occurred in Splunk, for example,
-                when Splunk is restarting and KV Store is not yet initialized.
-        """
-        for state in states:
-            state["state"] = json.dumps(state["state"])
-        self._collection_data.batch_save(*states)
-
-    @utils.retry(exceptions=[binding.HTTPError])
-    def get(self, key: str) -> Optional[Any]:
-        """Gets document with an id that equals to `key`.
-
-        Arguments:
-            key: `id` of the document to get.
-
-        Raises:
-            binding.HTTPError: When an error occurred in Splunk (not 404 code),
-                can be 503 code, when Splunk is restarting and KV Store is not
-                yet initialized.
-
-        Returns:
-            Document data under `key` or `None` in case of no data.
-        """
-        try:
-            record = self._collection_data.query_by_id(key)
-        except binding.HTTPError as e:
-            if e.status != 404:
-                logging.error(f"Get checkpoint failed: {traceback.format_exc()}.")
-                raise
-            return None
-        return json.loads(record["state"])
-
-    @utils.retry(exceptions=[binding.HTTPError])
-    def delete(self, key: str) -> None:
-        """Deletes document with an id that equals to `key`.
-
-        Arguments:
-            key: `id` of the document to delete.
-
-        Raises:
-            binding.HTTPError: When an error occurred in Splunk (not 404 code),
-                can be 503 code, when Splunk is restarting and KV Store is not
-                yet initialized.
-        """
-        try:
-            self._collection_data.delete_by_id(key)
-        except binding.HTTPError as e:
-            if e.status != 404:
-                logging.error(f"Delete checkpoint failed: {traceback.format_exc()}.")
-                raise
-
-
-class FileCheckpointer(Checkpointer):
-    """File checkpointer.
-
-    Use file to save modular input checkpoint.
-
-    Examples:
-        >>> from solnlib.modular_input import checkpointer
-        >>> ck = checkpointer.FileCheckpointer('/opt/splunk/var/...')
-        >>> ck.update(...)
-        >>> ck.get(...)
-    """
-
-    def __init__(self, checkpoint_dir: str):
-        """Initializes FileCheckpointer.
-
-        Arguments:
-            checkpoint_dir: Checkpoint directory.
-        """
-        warnings.warn(
-            "FileCheckpointer is deprecated, please use KVStoreCheckpointer",
-            stacklevel=2,
-        )
-        self._checkpoint_dir = checkpoint_dir
-
-    def encode_key(self, key):
-        return base64.b64encode(key.encode()).decode()
-
-    def update(self, key, state):
-        file_name = op.join(self._checkpoint_dir, self.encode_key(key))
-        with open(file_name + "_new", "w") as fp:
-            json.dump(state, fp)
-
-        if op.exists(file_name):
-            try:
-                os.remove(file_name)
-            except OSError:
-                pass
-
-        os.rename(file_name + "_new", file_name)
-
-    def batch_update(self, states):
-        for state in states:
-            self.update(state["_key"], state["state"])
-
-    def get(self, key):
-        file_name = op.join(self._checkpoint_dir, self.encode_key(key))
-        try:
-            with open(file_name) as fp:
-                return json.load(fp)
-        except (OSError, ValueError):
-            return None
-
-    def delete(self, key):
-        file_name = op.join(self._checkpoint_dir, self.encode_key(key))
-        try:
-            os.remove(file_name)
-        except OSError:
-            pass
diff --git a/apps/bitwarden_event_logs/lib/solnlib/modular_input/event.py b/apps/bitwarden_event_logs/lib/solnlib/modular_input/event.py
deleted file mode 100755
index 916ba660..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/modular_input/event.py
+++ /dev/null
@@ -1,255 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""This module provides Splunk modular input event encapsulation."""
-
-import json
-from typing import List
-from xml.etree import ElementTree as ET  # nosemgrep
-
-import defusedxml.ElementTree as defused_et
-
-__all__ = ["EventException", "XMLEvent", "HECEvent"]
-
-
-class EventException(Exception):
-    pass
-
-
-class Event:
-    """Base class of modular input event."""
-
-    def __init__(
-        self,
-        data: dict,
-        time: float = None,
-        index: str = None,
-        host: str = None,
-        source: str = None,
-        sourcetype: str = None,
-        fields: dict = None,
-        stanza: str = None,
-        unbroken: bool = False,
-        done: bool = False,
-    ):
-        """Modular input event.
-
-        Arguments:
-            data: Event data.
-            time: (optional) Event timestamp, default is None.
-            index: (optional) The index event will be written to, default is None.
-            host: (optional) Event host, default is None.
-            source: (optional) Event source, default is None.
-            sourcetype: (optional) Event sourcetype, default is None.
-            fields: (optional) Event fields, default is None.
-            stanza: (optional) Event stanza name, default is None.
-            unbroken: (optional) Event unbroken flag, default is False.
-            done: (optional) The last unbroken event, default is False.
-
-        Examples:
-           >>> event = Event(
-           >>>     data='This is a test data.',
-           >>>     time=1372274622.493,
-           >>>     index='main',
-           >>>     host='localhost',
-           >>>     source='Splunk',
-           >>>     sourcetype='misc',
-           >>>     fields= {'Cloud':'AWS','region': 'us-west-1'},
-           >>>     stanza='test_scheme://test',
-           >>>     unbroken=True,
-           >>>     done=True)
-        """
-
-        self._data = data
-        self._time = "%.3f" % time if time else None
-        self._index = index
-        self._host = host
-        self._source = source
-        self._sourcetype = sourcetype
-        if fields:
-            self._fields = fields
-        self._stanza = stanza
-        if not unbroken and done:
-            raise EventException('Invalid combination of "unbroken" and "done".')
-        self._unbroken = unbroken
-        self._done = done
-
-    def __str__(self):
-        event = {
-            "data": self._data,
-            "time": float(self._time) if self._time else self._time,
-            "index": self._index,
-            "host": self._host,
-            "source": self._source,
-            "sourcetype": self._sourcetype,
-            "stanza": self._stanza,
-            "unbroken": self._unbroken,
-            "done": self._done,
-        }
-
-        if hasattr(self, "_fields"):
-            event["fields"] = self._fields
-
-        return json.dumps(event)
-
-    @classmethod
-    def format_events(cls, events: List) -> List:
-        """Format events to list of string.
-
-        Arguments:
-            events: List of events to format.
-
-        Returns:
-            List of formatted events string.
-        """
-
-        raise EventException('Unimplemented "format_events".')
-
-
-class XMLEvent(Event):
-    """XML event."""
-
-    def _to_xml(self):
-        _event = ET.Element("event")
-        if self._stanza:
-            _event.set("stanza", self._stanza)
-        if self._unbroken:
-            _event.set("unbroken", str(int(self._unbroken)))
-
-        if self._time:
-            ET.SubElement(_event, "time").text = self._time
-
-        sub_elements = [
-            ("index", self._index),
-            ("host", self._host),
-            ("source", self._source),
-            ("sourcetype", self._sourcetype),
-        ]
-        for node, value in sub_elements:
-            if value:
-                ET.SubElement(_event, node).text = value
-
-        if isinstance(self._data, str):
-            ET.SubElement(_event, "data").text = self._data
-        else:
-            ET.SubElement(_event, "data").text = json.dumps(self._data)
-
-        if self._done:
-            ET.SubElement(_event, "done")
-
-        return _event
-
-    @classmethod
-    def format_events(cls, events: List) -> List:
-        """Format events to list of string.
-
-        Arguments:
-            events: List of events to format.
-
-        Returns:
-            List of formatted events string, example::
-
-                [
-                    '
-                    
-                    
-                    main
-                    localhost
-                    test
-                    test
-                    {"kk": [1, 2, 3]}
-                    
-                    
-                    
-                    
-                    main
-                    localhost
-                    test
-                    test
-                    {"kk": [3, 2, 3]}
-                    
-                    
-                    '
-                ]
-        """
-
-        stream = ET.Element("stream")
-        for event in events:
-            stream.append(event._to_xml())
-
-        return [
-            defused_et.tostring(stream, encoding="utf-8", method="xml").decode("utf-8")
-        ]
-
-
-class HECEvent(Event):
-    """HEC event."""
-
-    max_hec_event_length = 1000000
-
-    def _to_hec(self, event_field):
-        event = {}
-        event[event_field] = self._data
-        if self._time:
-            event["time"] = float(self._time)
-        if self._index:
-            event["index"] = self._index
-        if self._host:
-            event["host"] = self._host
-        if self._source:
-            event["source"] = self._source
-        if self._sourcetype:
-            event["sourcetype"] = self._sourcetype
-        if hasattr(self, "_fields"):
-            event["fields"] = self._fields
-
-        return json.dumps(event, ensure_ascii=False)
-
-    @classmethod
-    def format_events(cls, events: List, event_field: str = "event") -> List:
-        """Format events to list of string.
-
-        Arguments:
-            events: List of events to format.
-            event_field: Event field.
-
-        Returns:
-            List of formatted events string, example::
-
-                [
-                    '{"index": "main", ... "event": {"kk": [1, 2, 3]}}\\n
-                    {"index": "main", ... "event": {"kk": [3, 2, 3]}}',
-                '...'
-                ]
-        """
-
-        size = 0
-        new_events, batched_events = [], []
-        events = [event._to_hec(event_field) for event in events]
-        for event in events:
-            new_length = size + len(event) + len(batched_events) - 1
-            if new_length >= cls.max_hec_event_length:
-                if batched_events:
-                    new_events.append("\n".join(batched_events))
-                del batched_events[:]
-                size = 0
-
-            batched_events.append(event)
-            size = size + len(event)
-        if batched_events:
-            new_events.append("\n".join(batched_events))
-
-        return new_events
diff --git a/apps/bitwarden_event_logs/lib/solnlib/modular_input/event_writer.py b/apps/bitwarden_event_logs/lib/solnlib/modular_input/event_writer.py
deleted file mode 100755
index 06a14ae8..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/modular_input/event_writer.py
+++ /dev/null
@@ -1,510 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""This module provides two kinds of event writers (ClassicEventWriter,
-HECEventWriter) to write Splunk modular input events."""
-
-import logging
-import multiprocessing
-import sys
-import threading
-import time
-import traceback
-import warnings
-from abc import ABCMeta, abstractmethod
-from random import randint
-from typing import List, Union
-
-from splunklib import binding
-
-from .. import splunk_rest_client as rest_client
-from .. import utils
-from ..hec_config import HECConfig
-from ..splunkenv import get_splunkd_access_info, get_scheme_from_hec_settings
-from ..utils import retry
-from .event import HECEvent, XMLEvent
-
-__all__ = ["ClassicEventWriter", "HECEventWriter"]
-
-
-deprecation_msg = (
-    "Function 'create_from_token' is deprecated and incompatible with 'global_settings_schema=True'. "
-    "Use 'create_from_token_with_session_key' instead."
-)
-
-
-class FunctionDeprecated(Exception):
-    pass
-
-
-class EventWriter(metaclass=ABCMeta):
-    """Base class of event writer."""
-
-    description = "EventWriter"
-
-    @abstractmethod
-    def create_event(
-        self,
-        data: dict,
-        time: float = None,
-        index: str = None,
-        host: str = None,
-        source: str = None,
-        sourcetype: str = None,
-        fields: dict = None,
-        stanza: str = None,
-        unbroken: bool = False,
-        done: bool = False,
-    ) -> Union[XMLEvent, HECEvent]:
-        """Create a new event.
-
-        Arguments:
-            data: Event data.
-            time: (optional) Event timestamp, default is None.
-            index: (optional) The index event will be written to, default is None.
-            host: (optional) Event host, default is None.
-            source: (optional) Event source, default is None.
-            sourcetype: (optional) Event sourcetype, default is None.
-            fields: (optional) Event fields, default is None.
-            stanza: (optional) Event stanza name, default is None.
-            unbroken: (optional) Event unbroken flag, default is False.
-                It is only meaningful when for XMLEvent when using ClassicEventWriter.
-            done: (optional) The last unbroken event, default is False.
-                It is only meaningful when for XMLEvent when using ClassicEventWriter.
-
-        Examples:
-           >>> ew = event_writer.HECEventWriter(...)
-           >>> event = ew.create_event(
-           >>>     data='This is a test data.',
-           >>>     time='%.3f' % 1372274622.493,
-           >>>     index='main',
-           >>>     host='localhost',
-           >>>     source='Splunk',
-           >>>     sourcetype='misc',
-           >>>     fields={'accountid': '603514901691', 'Cloud': u'AWS'},
-           >>>     stanza='test_scheme://test',
-           >>>     unbroken=True,
-           >>>     done=True)
-        """
-
-        pass
-
-    @abstractmethod
-    def write_events(self, events: List):
-        """Write events.
-
-        Arguments:
-            events: List of events to write.
-
-        Examples:
-           >>> from solnlib.modular_input import event_writer
-           >>> ew = event_writer.EventWriter(...)
-           >>> ew.write_events([event1, event2])
-        """
-
-        pass
-
-
-class ClassicEventWriter(EventWriter):
-    """Classic event writer.
-
-    Use sys.stdout as the output.
-
-    Examples:
-        >>> from solnlib.modular_input import event_writer
-        >>> ew = event_writer.ClassicEventWriter()
-        >>> ew.write_events([event1, event2])
-    """
-
-    description = "ClassicEventWriter"
-
-    def __init__(self, lock: Union[threading.Lock, multiprocessing.Lock] = None):
-        """Initializes ClassicEventWriter.
-
-        Arguments:
-            lock: (optional) lock to exclusively access stdout.
-                by default, it is None and it will use threading safe lock.
-                if user would like to make the lock multiple-process safe, user should
-                pass in multiprocessing.Lock() instead
-        """
-        if lock is None:
-            self._lock = threading.Lock()
-        else:
-            self._lock = lock
-
-    def create_event(
-        self,
-        data: dict,
-        time: float = None,
-        index: str = None,
-        host: str = None,
-        source: str = None,
-        sourcetype: str = None,
-        fields: dict = None,
-        stanza: str = None,
-        unbroken: bool = False,
-        done: bool = False,
-    ):
-        """Create a new XMLEvent object."""
-
-        return XMLEvent(
-            data,
-            time=time,
-            index=index,
-            host=host,
-            source=source,
-            sourcetype=sourcetype,
-            stanza=stanza,
-            unbroken=unbroken,
-            done=done,
-        )
-
-    def write_events(self, events):
-        if not events:
-            return
-
-        stdout = sys.stdout
-
-        data = "".join([event for event in XMLEvent.format_events(events)])
-        with self._lock:
-            stdout.write(data)
-            stdout.flush()
-
-
-class HECEventWriter(EventWriter):
-    """HEC event writer.
-
-    Use Splunk HEC as the output.
-
-    Examples:
-        >>> from solnlib.modular_input import event_writer
-        >>> ew = event_writer.HECEventWriter(hec_input_name, session_key)
-        >>> ew.write_events([event1, event2])
-    """
-
-    WRITE_EVENT_RETRIES = 5
-    HTTP_INPUT_CONFIG_ENDPOINT = "/servicesNS/nobody/splunk_httpinput/data/inputs/http"
-    HTTP_EVENT_COLLECTOR_ENDPOINT = "/services/collector"
-    TOO_MANY_REQUESTS = 429  # we exceeded rate limit
-    SERVICE_UNAVAILABLE = 503  # remote service is temporary unavailable
-
-    description = "HECEventWriter"
-
-    headers = [("Content-Type", "application/json")]
-
-    def __init__(
-        self,
-        hec_input_name: str,
-        session_key: str,
-        scheme: str = None,
-        host: str = None,
-        port: int = None,
-        hec_uri: str = None,
-        hec_token: str = None,
-        global_settings_schema: bool = True,
-        logger: logging.Logger = None,
-        **context: dict
-    ):
-        """Initializes HECEventWriter.
-
-        Arguments:
-            hec_input_name: Splunk HEC input name.
-            session_key: Splunk access token.
-            scheme: (optional) The access scheme, default is None.
-            host: (optional) The host name, default is None.
-            port: (optional) The port number, default is None.
-            hec_uri: (optional) If hec_uri and hec_token are provided, they will
-                higher precedence than hec_input_name.
-            hec_token: (optional) HEC token.
-            global_settings_schema: (optional) if True, scheme will be set based on HEC global settings, default False.
-            logger: Logger object.
-            context: Other configurations for Splunk rest client.
-        """
-        super().__init__()
-        self._session_key = session_key
-        if logger:
-            self.logger = logger
-        else:
-            self.logger = logging
-
-        if hec_uri and hec_token:
-            scheme, host, hec_port = utils.extract_http_scheme_host_port(hec_uri)
-        else:
-            if not all([scheme, host, port]):
-                scheme, host, port = get_splunkd_access_info(self._session_key)
-            hec_port, hec_token = self._get_hec_config(
-                hec_input_name, session_key, scheme, host, port, **context
-            )
-
-        if global_settings_schema:
-            scheme = get_scheme_from_hec_settings(self._session_key)
-
-        if not context.get("pool_connections"):
-            context["pool_connections"] = 10
-
-        if not context.get("pool_maxsize"):
-            context["pool_maxsize"] = 10
-
-        self._rest_client = rest_client.SplunkRestClient(
-            hec_token, app="-", scheme=scheme, host=host, port=hec_port, **context
-        )
-
-    @staticmethod
-    def create_from_token(
-        hec_uri: str,
-        hec_token: str,
-        global_settings_schema: bool = False,
-        **context: dict
-    ) -> "HECEventWriter":
-        """Given HEC URI and HEC token, create HECEventWriter object. This
-        function simplifies the standalone mode HECEventWriter usage (not in a
-        modinput).
-
-        Arguments:
-            hec_uri: HTTP Event Collector URI, like https://localhost:8088.
-            hec_token: HTTP Event Collector token.
-            global_settings_schema: (optional) if True, scheme will be set based on HEC global settings, default False.
-            context: Other configurations.
-
-        Returns:
-            Created HECEventWriter.
-        """
-
-        warnings.warn(deprecation_msg, DeprecationWarning, stacklevel=2)
-
-        if global_settings_schema:
-            raise FunctionDeprecated(deprecation_msg)
-
-        return HECEventWriter(
-            None,
-            None,
-            None,
-            None,
-            None,
-            hec_uri=hec_uri,
-            hec_token=hec_token,
-            global_settings_schema=global_settings_schema,
-            **context
-        )
-
-    @staticmethod
-    def create_from_input(
-        hec_input_name: str,
-        splunkd_uri: str,
-        session_key: str,
-        global_settings_schema: bool = False,
-        **context: dict
-    ) -> "HECEventWriter":
-        """Given HEC input stanza name, splunkd URI and splunkd session key,
-        create HECEventWriter object. HEC URI and token etc will be discovered
-        from HEC input stanza. When hitting HEC event limit, the underlying
-        code will increase the HEC event limit automatically by calling
-        corresponding REST API against splunkd_uri by using session_key.
-
-        Arguments:
-            hec_input_name: Splunk HEC input name.
-            splunkd_uri: Splunkd URI, like https://localhost:8089
-            session_key: Splunkd access token.
-            global_settings_schema: (optional) if True, scheme will be set based on HEC global settings, default False.
-            context: Other configurations.
-
-        Returns:
-            Created HECEventWriter.
-        """
-
-        scheme, host, port = utils.extract_http_scheme_host_port(splunkd_uri)
-        return HECEventWriter(
-            hec_input_name,
-            session_key,
-            scheme,
-            host,
-            port,
-            global_settings_schema=global_settings_schema,
-            **context
-        )
-
-    @staticmethod
-    def create_from_token_with_session_key(
-        splunkd_uri: str,
-        session_key: str,
-        hec_uri: str,
-        hec_token: str,
-        global_settings_schema: bool = False,
-        **context: dict
-    ) -> "HECEventWriter":
-        """Given Splunkd URI, Splunkd session key, HEC URI and HEC token,
-        create HECEventWriter object. When hitting HEC event limit, the event
-        writer will increase the HEC event limit automatically by calling
-        corresponding REST API against splunkd_uri by using session_key.
-
-        Arguments:
-            splunkd_uri: Splunkd URI, like https://localhost:8089.
-            session_key: Splunkd access token.
-            hec_uri: Http Event Collector URI, like https://localhost:8088.
-            hec_token: Http Event Collector token.
-            global_settings_schema: (optional) if True, scheme will be set based on HEC global settings, default False.
-            context: Other configurations.
-
-        Returns:
-            Created HECEventWriter.
-        """
-
-        scheme, host, port = utils.extract_http_scheme_host_port(splunkd_uri)
-        return HECEventWriter(
-            None,
-            session_key,
-            scheme,
-            host,
-            port,
-            hec_uri=hec_uri,
-            hec_token=hec_token,
-            global_settings_schema=global_settings_schema,
-            **context
-        )
-
-    @retry(exceptions=[binding.HTTPError])
-    def _get_hec_config(
-        self, hec_input_name, session_key, scheme, host, port, **context
-    ):
-        hc = HECConfig(session_key, scheme=scheme, host=host, port=port, **context)
-        settings = hc.get_settings()
-        if utils.is_true(settings.get("disabled")):
-            # Enable HEC input
-            self.logger.info("Enabling HEC")
-            settings["disabled"] = "0"
-            settings["enableSSL"] = context.get("hec_enablessl", "1")
-            settings["port"] = context.get("hec_port", "8088")
-            hc.update_settings(settings)
-
-        hec_input = hc.get_input(hec_input_name)
-        if not hec_input:
-            # Create HEC input
-            self.logger.info("Create HEC datainput, name=%s", hec_input_name)
-            hinput = {
-                "index": context.get("index", "main"),
-            }
-
-            if context.get("sourcetype"):
-                hinput["sourcetype"] = context["sourcetype"]
-
-            if context.get("token"):
-                hinput["token"] = context["token"]
-
-            if context.get("source"):
-                hinput["source"] = context["source"]
-
-            if context.get("host"):
-                hinput["host"] = context["host"]
-
-            hec_input = hc.create_input(hec_input_name, hinput)
-
-        limits = hc.get_limits()
-        HECEvent.max_hec_event_length = int(limits.get("max_content_length", 1000000))
-
-        return settings["port"], hec_input["token"]
-
-    def create_event(
-        self,
-        data: dict,
-        time: float = None,
-        index: str = None,
-        host: str = None,
-        source: str = None,
-        sourcetype: str = None,
-        fields: dict = None,
-        stanza: str = None,
-        unbroken: bool = False,
-        done: bool = False,
-    ) -> HECEvent:
-        """Create a new HECEvent object.
-
-        Arguments:
-            data: Event data.
-            time: (optional) Event timestamp, default is None.
-            index: (optional) The index event will be written to, default is None.
-            host: (optional) Event host, default is None.
-            source: (optional) Event source, default is None.
-            sourcetype: (optional) Event sourcetype, default is None.
-            fields: (optional) Event fields, default is None.
-            stanza: (optional) Event stanza name, default is None.
-            unbroken: (optional) Event unbroken flag, default is False.
-                It is only meaningful when for XMLEvent when using ClassicEventWriter.
-            done: (optional) The last unbroken event, default is False.
-                It is only meaningful when for XMLEvent when using ClassicEventWriter.
-
-        Returns:
-            Created HECEvent.
-        """
-
-        return HECEvent(
-            data,
-            time=time,
-            index=index,
-            host=host,
-            source=source,
-            sourcetype=sourcetype,
-            fields=fields,
-        )
-
-    def write_events(
-        self,
-        events: List,
-        retries: int = WRITE_EVENT_RETRIES,
-        event_field: str = "event",
-    ):
-        """Write events to index in bulk.
-
-        Arguments:
-            events: List of events.
-            retries: Number of retries for writing events to index.
-            event_field: Event field.
-        """
-        if not events:
-            return
-
-        last_ex = None
-        for event in HECEvent.format_events(events, event_field):
-            for i in range(retries):
-                try:
-                    self._rest_client.post(
-                        self.HTTP_EVENT_COLLECTOR_ENDPOINT,
-                        body=event.encode("utf-8"),
-                        headers=self.headers,
-                    )
-                except binding.HTTPError as e:
-                    self.logger.warn(
-                        "Write events through HEC failed. Status=%s", e.status
-                    )
-                    last_ex = e
-                    if e.status in [self.TOO_MANY_REQUESTS, self.SERVICE_UNAVAILABLE]:
-                        # wait time for n retries: 10, 20, 40, 80, 80, 80, 80, ....
-                        sleep_time = min(((2 ** (i + 1)) * 5), 80)
-                        if i < retries - 1:
-                            random_millisecond = randint(0, 1000) / 1000.0
-                            time.sleep(sleep_time + random_millisecond)
-                    else:
-                        raise last_ex
-                else:
-                    break
-            else:
-                # When failed after retry, we reraise the exception
-                # to exit the function to let client handle this situation
-                self.logger.error(
-                    "Write events through HEC failed: %s. status=%s",
-                    traceback.format_exc(),
-                    last_ex.status,
-                )
-                raise last_ex
diff --git a/apps/bitwarden_event_logs/lib/solnlib/modular_input/modinput.py b/apps/bitwarden_event_logs/lib/solnlib/modular_input/modinput.py
deleted file mode 100755
index 0d591923..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/modular_input/modinput.py
+++ /dev/null
@@ -1,161 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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 subprocess
-import sys
-import traceback
-
-import solnlib.splunkenv as sp
-import logging
-
-
-def _parse_modinput_configs(root, outer_block, inner_block):
-    """When user splunkd spawns modinput script to do config check or run.
-
-    
-    
-      localhost.localdomain
-      https://127.0.0.1:8089
-      xxxyyyzzz
-      ckpt_dir
-      
-        
-          60
-            localhost.localdomain
-            snow
-            10
-        
-        ...
-      
-    
-
-    When user create an stanza through data input on WebUI
-
-    
-    
-      localhost.localdomain
-      https://127.0.0.1:8089
-      xxxyyyzzz
-      ckpt_dir
-      
-        60
-        
-        localhost.localdomain
-        snow
-        10
-      
-    
-    """
-
-    confs = root.getElementsByTagName(outer_block)
-    if not confs:
-        logging.error("Invalid config, missing %s section", outer_block)
-        raise Exception(f"Invalid config, missing {outer_block} section")
-
-    configs = []
-    stanzas = confs[0].getElementsByTagName(inner_block)
-    for stanza in stanzas:
-        config = {}
-        stanza_name = stanza.getAttribute("name")
-        if not stanza_name:
-            logging.error("Invalid config, missing name")
-            raise Exception("Invalid config, missing name")
-
-        config["name"] = stanza_name
-        params = stanza.getElementsByTagName("param")
-        for param in params:
-            name = param.getAttribute("name")
-            if (
-                name
-                and param.firstChild
-                and param.firstChild.nodeType == param.firstChild.TEXT_NODE
-            ):
-                config[name] = param.firstChild.data
-        configs.append(config)
-    return configs
-
-
-def parse_modinput_configs(config_str):
-    """
-    @config_str: modinput XML configuration feed by splunkd
-    @return: meta_config and stanza_config
-    """
-
-    import defusedxml.minidom as xdm
-
-    meta_configs = {
-        "server_host": None,
-        "server_uri": None,
-        "session_key": None,
-        "checkpoint_dir": None,
-    }
-    root = xdm.parseString(config_str)
-    doc = root.documentElement
-    for tag in meta_configs.keys():
-        nodes = doc.getElementsByTagName(tag)
-        if not nodes:
-            logging.error("Invalid config, missing %s section", tag)
-            raise Exception("Invalid config, missing %s section", tag)
-
-        if nodes[0].firstChild and nodes[0].firstChild.nodeType == nodes[0].TEXT_NODE:
-            meta_configs[tag] = nodes[0].firstChild.data
-        else:
-            logging.error("Invalid config, expect text ndoe")
-            raise Exception("Invalid config, expect text ndoe")
-
-    if doc.nodeName == "input":
-        configs = _parse_modinput_configs(doc, "configuration", "stanza")
-    else:
-        configs = _parse_modinput_configs(root, "items", "item")
-    return meta_configs, configs
-
-
-def get_modinput_configs_from_cli(modinput, modinput_stanza=None):
-    """
-    @modinput: modinput name
-    @modinput_stanza: modinput stanza name, for multiple instance only
-    """
-
-    assert modinput
-
-    splunkbin = sp.get_splunk_bin()
-    cli = [splunkbin, "cmd", "splunkd", "print-modinput-config", modinput]
-    if modinput_stanza:
-        cli.append(modinput_stanza)
-
-    out, err = subprocess.Popen(
-        cli, stdout=subprocess.PIPE, stderr=subprocess.PIPE
-    ).communicate()
-    if err:
-        logging.error("Failed to get modinput configs with error: %s", err)
-        return None, None
-    else:
-        return parse_modinput_configs(out)
-
-
-def get_modinput_config_str_from_stdin():
-    """Get modinput from stdin which is feed by splunkd."""
-
-    try:
-        return sys.stdin.read(5000)
-    except Exception:
-        logging.error(traceback.format_exc())
-        raise
-
-
-def get_modinput_configs_from_stdin():
-    config_str = get_modinput_config_str_from_stdin()
-    return parse_modinput_configs(config_str)
diff --git a/apps/bitwarden_event_logs/lib/solnlib/modular_input/modular_input.py b/apps/bitwarden_event_logs/lib/solnlib/modular_input/modular_input.py
deleted file mode 100755
index 0889f7a9..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/modular_input/modular_input.py
+++ /dev/null
@@ -1,509 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""This module provides a base class of Splunk modular input."""
-
-import logging
-import sys
-import traceback
-from abc import ABCMeta, abstractmethod
-from typing import Callable, List
-from urllib import parse as urlparse
-from xml.etree import ElementTree as ET  # nosemgrep
-
-import defusedxml.ElementTree as defused_et
-from splunklib import binding
-from splunklib.modularinput.argument import Argument
-from splunklib.modularinput.input_definition import InputDefinition
-from splunklib.modularinput.scheme import Scheme
-from splunklib.modularinput.validation_definition import ValidationDefinition
-
-from .. import utils
-from ..orphan_process_monitor import OrphanProcessMonitor
-from . import checkpointer, event_writer
-
-__all__ = ["ModularInputException", "ModularInput"]
-
-
-class ModularInputException(Exception):
-    """Exception for ModularInput class."""
-
-    pass
-
-
-class ModularInput(metaclass=ABCMeta):
-    """Base class of Splunk modular input.
-
-    It's a base modular input, it should be inherited by sub modular input. For
-    sub modular input, properties: 'app', 'name', 'title' and 'description' must
-    be overriden, also there are some other optional properties can be overriden
-    like: 'use_external_validation', 'use_single_instance', 'use_kvstore_checkpointer'
-    and 'use_hec_event_writer'.
-
-    Notes: If you set 'KVStoreCheckpointer' or 'use_hec_event_writer' to True,
-    you must override the corresponding 'kvstore_checkpointer_collection_name'
-    and 'hec_input_name'.
-
-    Examples:
-
-       >>> class TestModularInput(ModularInput):
-       >>>     app = 'TestApp'
-       >>>     name = 'test_modular_input'
-       >>>     title = 'Test modular input'
-       >>>     description = 'This is a test modular input'
-       >>>     use_external_validation = True
-       >>>     use_single_instance = False
-       >>>     use_kvstore_checkpointer = True
-       >>>     kvstore_checkpointer_collection_name = 'TestCheckpoint'
-       >>>     use_hec_event_writer = True
-       >>>     hec_input_name = 'TestEventWriter'
-       >>>
-       >>>     def extra_arguments(self):
-       >>>         ... .. .
-       >>>
-       >>>     def do_validation(self, parameters):
-       >>>         ... .. .
-       >>>
-       >>>     def do_run(self, inputs):
-       >>>         ... .. .
-       >>>
-       >>> if __name__ == '__main__':
-       >>>     md = TestModularInput()
-       >>>     md.execute()
-    """
-
-    # App name, must be overridden
-    app = None
-    # Modular input name, must be overridden
-    name = None
-    # Modular input scheme title, must be overridden
-    title = None
-    # Modular input scheme description, must be overridden
-    description = None
-    # Modular input scheme use external validation, default is False
-    use_external_validation = False
-    # Modular input scheme use single instance mode, default is False
-    use_single_instance = False
-    # Use kvstore as checkpointer, default is True
-    use_kvstore_checkpointer = True
-    # Collection name of kvstore checkpointer, must be overridden if
-    # use_kvstore_checkpointer is True
-    kvstore_checkpointer_collection_name = None
-    # Use hec event writer
-    use_hec_event_writer = True
-    # Input name of Splunk HEC, must be overridden if use_hec_event_writer
-    # is True
-    hec_input_name = None
-    hec_global_settings_schema = False
-
-    def __init__(self):
-        # Validate properties
-        self._validate_properties()
-        # Modular input state
-        self.should_exit = False
-        # Metadata
-        self.server_host_name = None
-        self.server_uri = None
-        self.server_scheme = None
-        self.server_host = None
-        self.server_port = None
-        self.session_key = None
-        # Modular input config name
-        self.config_name = None
-        # Checkpoint dir
-        self._checkpoint_dir = None
-        # Checkpointer
-        self._checkpointer = None
-        # Orphan process monitor
-        self._orphan_monitor = None
-        # Event writer
-        self._event_writer = None
-
-    def _validate_properties(self):
-        if not all([self.app, self.name, self.title, self.description]):
-            raise ModularInputException(
-                'Attributes: "app", "name", "title", "description" must '
-                "be overriden."
-            )
-
-        if self.use_kvstore_checkpointer:
-            if self.kvstore_checkpointer_collection_name is None:
-                raise ModularInputException(
-                    'Attribute: "kvstore_checkpointer_collection_name" must'
-                    'be overriden if "use_kvstore_checkpointer" is True".'
-                )
-            elif self.kvstore_checkpointer_collection_name.strip() == "":
-                raise ModularInputException(
-                    'Attribute: "kvstore_checkpointer_collection_name" can'
-                    " not be empty."
-                )
-
-        if self.use_hec_event_writer:
-            if self.hec_input_name is None:
-                raise ModularInputException(
-                    'Attribute: "hec_input_name" must be overriden '
-                    'if "use_hec_event_writer" is True.'
-                )
-            elif self.hec_input_name.strip() == "":
-                raise ModularInputException(
-                    'Attribute: "hec_input_name" can not be empty.'
-                )
-
-    @property
-    def checkpointer(self) -> checkpointer.Checkpointer:
-        """Get checkpointer object.
-
-        The checkpointer returned depends on use_kvstore_checkpointer flag,
-        if use_kvstore_checkpointer is true will return an KVStoreCheckpointer
-        object else an FileCheckpointer object.
-
-        Returns:
-            A checkpointer object.
-        """
-
-        if self._checkpointer is not None:
-            return self._checkpointer
-
-        self._checkpointer = self._create_checkpointer()
-        return self._checkpointer
-
-    def _create_checkpointer(self):
-        if self.use_kvstore_checkpointer:
-            checkpointer_name = ":".join(
-                [self.app, self.config_name, self.kvstore_checkpointer_collection_name]
-            )
-            try:
-                return checkpointer.KVStoreCheckpointer(
-                    checkpointer_name,
-                    self.session_key,
-                    self.app,
-                    owner="nobody",
-                    scheme=self.server_scheme,
-                    host=self.server_host,
-                    port=self.server_port,
-                )
-            except binding.HTTPError:
-                logging.error(
-                    "Failed to init kvstore checkpointer: %s.", traceback.format_exc()
-                )
-                raise
-        else:
-            return checkpointer.FileCheckpointer(self._checkpoint_dir)
-
-    @property
-    def event_writer(self) -> event_writer.EventWriter:
-        """Get event writer object.
-
-        The event writer returned depends on use_hec_event_writer flag,
-        if use_hec_event_writer is true will return an HECEventWriter
-        object else an ClassicEventWriter object.
-
-        Returns:
-            Event writer object.
-        """
-
-        if self._event_writer is not None:
-            return self._event_writer
-
-        self._event_writer = self._create_event_writer()
-        return self._event_writer
-
-    def _create_event_writer(self):
-        if self.use_hec_event_writer:
-            hec_input_name = ":".join([self.app, self.hec_input_name])
-            try:
-                return event_writer.HECEventWriter(
-                    hec_input_name,
-                    self.session_key,
-                    scheme=self.server_scheme,
-                    host=self.server_host,
-                    port=self.server_port,
-                    global_settings_schema=self.hec_global_settings_schema,
-                )
-            except binding.HTTPError:
-                logging.error(
-                    "Failed to init HECEventWriter: %s.", traceback.format_exc()
-                )
-                raise
-        else:
-            return event_writer.ClassicEventWriter()
-
-    def _update_metadata(self, metadata):
-        self.server_host_name = metadata["server_host"]
-        splunkd = urlparse.urlsplit(metadata["server_uri"])
-        self.server_uri = splunkd.geturl()
-        self.server_scheme = splunkd.scheme
-        self.server_host = splunkd.hostname
-        self.server_port = splunkd.port
-        self.session_key = metadata["session_key"]
-        self._checkpoint_dir = metadata["checkpoint_dir"]
-
-    def _do_scheme(self):
-        scheme = Scheme(self.title)
-        scheme.description = self.description
-        scheme.use_external_validation = self.use_external_validation
-        scheme.streaming_mode = Scheme.streaming_mode_xml
-        scheme.use_single_instance = self.use_single_instance
-
-        for argument in self.extra_arguments():
-            name = argument["name"]
-            title = argument.get("title", None)
-            description = argument.get("description", None)
-            validation = argument.get("validation", None)
-            data_type = argument.get("data_type", Argument.data_type_string)
-            required_on_edit = argument.get("required_on_edit", False)
-            required_on_create = argument.get("required_on_create", False)
-
-            scheme.add_argument(
-                Argument(
-                    name,
-                    title=title,
-                    description=description,
-                    validation=validation,
-                    data_type=data_type,
-                    required_on_edit=required_on_edit,
-                    required_on_create=required_on_create,
-                )
-            )
-
-        return defused_et.tostring(scheme.to_xml(), encoding="unicode")
-
-    def extra_arguments(self) -> List:
-        """Extra arguments for modular input.
-
-        Default implementation is returning an empty list.
-
-        Returns:
-            List of arguments like::
-
-                [
-                    {
-                        'name': 'arg1',
-                        'title': 'arg1 title',
-                        'description': 'arg1 description',
-                        'validation': 'arg1 validation statement',
-                        'data_type': Argument.data_type_string,
-                        'required_on_edit': False,
-                        'required_on_create': False
-                    },
-                    {...},
-                    {...}
-                ]
-        """
-
-        return []
-
-    def do_validation(self, parameters):
-        """Handles external validation for modular input kinds.
-
-        When Splunk calls a modular input script in validation mode, it will
-        pass in an XML document giving information about the Splunk instance
-        (so you can call back into it if needed) and the name and parameters
-        of the proposed input. If this function does not throw an exception,
-        the validation is assumed to succeed. Otherwise any errors thrown will
-        be turned into a string and logged back to Splunk.
-
-        Arguments:
-            parameters: The parameters of input passed by splunkd.
-
-        Raises:
-            Exception: If validation is failed.
-        """
-
-        pass
-
-    @abstractmethod
-    def do_run(self, inputs: dict):
-        """Runs this modular input.
-
-        Arguments:
-            inputs: Command line arguments passed to this modular input.
-                For single instance mode, inputs like::
-
-                    {
-                    'stanza_name1': {'arg1': 'arg1_value', 'arg2': 'arg2_value', ...}
-                    'stanza_name2': {'arg1': 'arg1_value', 'arg2': 'arg2_value', ...}
-                    'stanza_name3': {'arg1': 'arg1_value', 'arg2': 'arg2_value', ...}
-                    }
-
-                For multiple instance mode, inputs like::
-
-                    {
-                    'stanza_name1': {'arg1': 'arg1_value', 'arg2': 'arg2_value', ...}
-                    }
-        """
-
-        pass
-
-    def register_teardown_handler(self, handler: Callable, *args):
-        """Register teardown signal handler.
-
-        Arguments:
-            handler: Teardown signal handler.
-            args: Arguments to the handler.
-
-        Examples:
-           >>> mi = ModularInput(...)
-           >>> def teardown_handler(arg1, arg2, ...):
-           >>>     ...
-           >>> mi.register_teardown_handler(teardown_handler, arg1, arg2, ...)
-        """
-
-        def _teardown_handler(signum, frame):
-            handler(*args)
-
-        utils.handle_teardown_signals(_teardown_handler)
-
-    def register_orphan_handler(self, handler: Callable, *args):
-        """Register orphan process handler.
-
-        Arguments:
-            handler: Teardown signal handler.
-            args: Arguments to the handler.
-
-        Examples:
-           >>> mi = ModularInput(...)
-           >>> def orphan_handler(arg1, arg2, ...):
-           >>>     ...
-           >>> mi.register_orphan_handler(orphan_handler, arg1, arg2, ...)
-        """
-
-        def _orphan_handler():
-            handler(*args)
-
-        if self._orphan_monitor is None:
-            self._orphan_monitor = OrphanProcessMonitor(_orphan_handler)
-            self._orphan_monitor.start()
-
-    def get_validation_definition(self) -> dict:
-        """Get validation definition.
-
-        This method can be overwritten to get validation definition from
-        other input instead `stdin`.
-
-        Returns:
-            A dict object must contains `metadata` and `parameters`::
-
-                example: {
-                    'metadata': {
-                    'session_key': 'iCKPS0cvmpyeJk...sdaf',
-                    'server_host': 'test-test.com',
-                    'server_uri': 'https://127.0.0.1:8089',
-                    'checkpoint_dir': '/tmp'
-                    },
-                    parameters: {'args1': value1, 'args2': value2}
-                }
-        """
-
-        validation_definition = ValidationDefinition.parse(sys.stdin)
-        return {
-            "metadata": validation_definition.metadata,
-            "parameters": validation_definition.parameters,
-        }
-
-    def get_input_definition(self) -> dict:
-        """Get input definition.
-
-        This method can be overwritten to get input definition from
-        other input instead `stdin`.
-
-        Returns:
-            A dict object must contain `metadata` and `inputs`::
-
-                example: {
-                    'metadata': {
-                    'session_key': 'iCKPS0cvmpyeJk...sdaf',
-                    'server_host': 'test-test.com',
-                    'server_uri': 'https://127.0.0.1:8089',
-                    'checkpoint_dir': '/tmp'
-                    },
-                    inputs: {
-                    'stanza1': {'arg1': value1, 'arg2': value2},
-                    'stanza2': {'arg1': value1, 'arg2': value2}
-                    }
-                }
-        """
-
-        input_definition = InputDefinition.parse(sys.stdin)
-        return {
-            "metadata": input_definition.metadata,
-            "inputs": input_definition.inputs,
-        }
-
-    def execute(self):
-        """Modular input entry.
-
-        Examples:
-           >>> class TestModularInput(ModularInput):
-           >>>         ... .. .
-           >>>
-           >>> if __name__ == '__main__':
-           >>>     md = TestModularInput()
-           >>>     md.execute()
-        """
-
-        if len(sys.argv) == 1:
-            try:
-                input_definition = self.get_input_definition()
-                self._update_metadata(input_definition["metadata"])
-                if self.use_single_instance:
-                    self.config_name = self.name
-                else:
-                    self.config_name = list(input_definition["inputs"].keys())[0]
-                self.do_run(input_definition["inputs"])
-                logging.info("Modular input: %s exit normally.", self.name)
-                return 0
-            except Exception:
-                logging.error(
-                    "Modular input: %s exit with exception: %s.",
-                    self.name,
-                    traceback.format_exc(),
-                )
-                return 1
-            finally:
-                # Stop orphan monitor if any
-                if self._orphan_monitor:
-                    self._orphan_monitor.stop()
-
-        elif str(sys.argv[1]).lower() == "--scheme":
-            sys.stdout.write(self._do_scheme())
-            sys.stdout.flush()
-            return 0
-
-        elif sys.argv[1].lower() == "--validate-arguments":
-            try:
-                validation_definition = self.get_validation_definition()
-                self._update_metadata(validation_definition["metadata"])
-                self.do_validation(validation_definition["parameters"])
-                return 0
-            except Exception as e:
-                logging.error(
-                    "Modular input: %s validate arguments with exception: %s.",
-                    self.name,
-                    traceback.format_exc(),
-                )
-                root = ET.Element("error")
-                ET.SubElement(root, "message").text = str(e)
-                sys.stderr.write(defused_et.tostring(root))
-                sys.stderr.flush()
-                return 1
-        else:
-            logging.error(
-                'Modular input: %s run with invalid arguments: "%s".',
-                self.name,
-                " ".join(sys.argv[1:]),
-            )
-            return 1
diff --git a/apps/bitwarden_event_logs/lib/solnlib/net_utils.py b/apps/bitwarden_event_logs/lib/solnlib/net_utils.py
deleted file mode 100755
index bdc76a4d..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/net_utils.py
+++ /dev/null
@@ -1,155 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""Net utilities."""
-import re
-import socket
-
-__all__ = ["resolve_hostname", "validate_scheme_host_port"]
-
-from typing import Optional, Union
-
-
-def resolve_hostname(addr: str) -> Optional[str]:
-    """Try to resolve an IP to a host name and returns None on common failures.
-
-    Arguments:
-        addr: IP address to resolve.
-
-    Returns:
-        Host name if success else None.
-
-    Raises:
-        ValueError: If `addr` is not a valid address.
-    """
-
-    if is_valid_ip(addr):
-        try:
-            name, _, _ = socket.gethostbyaddr(addr)
-            return name
-        except socket.gaierror:
-            # [Errno 8] nodename nor servname provided, or not known
-            pass
-        except socket.herror:
-            # [Errno 1] Unknown host
-            pass
-        except socket.timeout:
-            # Timeout.
-            pass
-
-        return None
-    else:
-        raise ValueError("Invalid ip address.")
-
-
-def is_valid_ip(addr: str) -> bool:
-    """Validate an IPV4 address.
-
-    Arguments:
-        addr: IP address to validate.
-
-    Returns:
-        True if is valid else False.
-    """
-
-    ip_rx = re.compile(
-        r"""
-        ^(((
-              [0-1]\d{2}                  # matches 000-199
-            | 2[0-4]\d                    # matches 200-249
-            | 25[0-5]                     # matches 250-255
-            | \d{1,2}                     # matches 0-9, 00-99
-        )\.){3})                          # 3 of the preceding stanzas
-        ([0-1]\d{2}|2[0-4]\d|25[0-5]|\d{1,2})$     # final octet
-    """,
-        re.VERBOSE,
-    )
-
-    try:
-        return ip_rx.match(addr.strip())
-    except AttributeError:
-        # Value was not a string
-        return False
-
-
-def is_valid_hostname(hostname: str) -> bool:
-    """Validate a host name.
-
-    Arguments:
-        hostname: host name to validate.
-
-    Returns:
-        True if is valid else False.
-    """
-    # Splunk IPv6 support.
-    # https://docs.splunk.com/Documentation/Splunk/9.0.0/Admin/ConfigureSplunkforIPv6#Change_the_prioritization_of_IPv4_and_IPv6_communications
-    if hostname == "[::1]":
-        return True
-
-    if len(hostname) > 255:
-        return False
-    if hostname[-1:] == ".":
-        hostname = hostname[:-1]
-    allowed = re.compile(r"(?!-)(::)?[A-Z\d-]{1,63}(? bool:
-    """Validate a port.
-
-    Arguments:
-        port: port to validate.
-
-    Returns:
-        True if is valid else False.
-    """
-
-    try:
-        return 0 < int(port) <= 65535
-    except ValueError:
-        return False
-
-
-def is_valid_scheme(scheme: str) -> bool:
-    """Validate a scheme.
-
-    Arguments:
-        scheme: scheme to validate.
-
-    Returns:
-        True if is valid else False.
-    """
-
-    return scheme.lower() in ("http", "https")
-
-
-def validate_scheme_host_port(scheme: str, host: str, port: Union[str, int]):
-    """Validates scheme, host and port.
-
-    Arguments:
-        scheme: scheme to validate.
-        host: hostname to validate.
-        port: port to validate.
-
-    Raises:
-        ValueError: if scheme, host or port are invalid.
-    """
-    if scheme is not None and not is_valid_scheme(scheme):
-        raise ValueError("Invalid scheme")
-    if host is not None and not is_valid_hostname(host):
-        raise ValueError("Invalid host")
-    if port is not None and not is_valid_port(port):
-        raise ValueError("Invalid port")
diff --git a/apps/bitwarden_event_logs/lib/solnlib/orphan_process_monitor.py b/apps/bitwarden_event_logs/lib/solnlib/orphan_process_monitor.py
deleted file mode 100755
index d71543ad..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/orphan_process_monitor.py
+++ /dev/null
@@ -1,120 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""Orphan process monitor."""
-
-import os
-import threading
-import time
-from typing import Callable
-
-__all__ = ["OrphanProcessChecker", "OrphanProcessMonitor"]
-
-
-class OrphanProcessChecker:
-    """Orphan process checker.
-
-    Only work for Linux platform. On Windows platform, is_orphan is
-    always False and there is no need to do this monitoring on Windows.
-    """
-
-    def __init__(self, callback: Callable = None):
-        """Initializes OrphanProcessChecker.
-
-        Arguments:
-            callback: (optional) Callback for orphan process.
-        """
-        if os.name == "nt":
-            self._ppid = 0
-        else:
-            self._ppid = os.getppid()
-        self._callback = callback
-
-    def is_orphan(self) -> bool:
-        """Check process is orphan.
-
-        For windows platform just return False.
-
-        Returns:
-            True for orphan process else False.
-        """
-
-        if os.name == "nt":
-            return False
-        return self._ppid != os.getppid()
-
-    def check_orphan(self) -> bool:
-        """Check if the process becomes orphan.
-
-        If the process becomes orphan then call callback function
-        to handle properly.
-
-        Returns:
-            True for orphan process else False.
-        """
-
-        res = self.is_orphan()
-        if res and self._callback:
-            self._callback()
-        return res
-
-
-class OrphanProcessMonitor:
-    """Orphan process monitor.
-
-    Check if process become orphan in background thread per interval and
-    call callback if process become orphan.
-    """
-
-    def __init__(self, callback: Callable, interval: int = 1):
-        """Initializes OrphanProcessMonitor.
-
-        Arguments:
-            callback: Callback for orphan process monitor.
-            interval: (optional) Interval to monitor.
-        """
-        self._checker = OrphanProcessChecker(callback)
-        self._thr = threading.Thread(target=self._do_monitor)
-        self._thr.daemon = True
-        self._started = False
-        self._interval = interval
-
-    def start(self):
-        """Start orphan process monitor."""
-
-        if self._started:
-            return
-        self._started = True
-
-        self._thr.start()
-
-    def stop(self):
-        """Stop orphan process monitor."""
-
-        joinable = self._started
-        self._started = False
-        if joinable:
-            self._thr.join(timeout=1)
-
-    def _do_monitor(self):
-        while self._started:
-            if self._checker.check_orphan():
-                break
-
-            for _ in range(self._interval):
-                if not self._started:
-                    break
-                time.sleep(1)
diff --git a/apps/bitwarden_event_logs/lib/solnlib/pattern.py b/apps/bitwarden_event_logs/lib/solnlib/pattern.py
deleted file mode 100755
index 8008d2c8..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/pattern.py
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""This module provides some common used patterns."""
-
-__all__ = ["Singleton"]
-
-
-class Singleton(type):
-    """Singleton meta class.
-
-    Examples:
-       >>> class Test(object):
-       >>>     __metaclass__ = Singleton
-       >>>
-       >>>     def __init__(self):
-       >>>         pass
-    """
-
-    def __init__(cls, name, bases, attrs):
-        super().__init__(name, bases, attrs)
-        cls._instance = None
-
-    def __call__(cls, *args, **kwargs):
-        if cls._instance is None:
-            cls._instance = super().__call__(*args, **kwargs)
-        return cls._instance
diff --git a/apps/bitwarden_event_logs/lib/solnlib/rest.py b/apps/bitwarden_event_logs/lib/solnlib/rest.py
deleted file mode 100755
index df13c303..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/rest.py
+++ /dev/null
@@ -1,95 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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 json
-import urllib.parse
-from traceback import format_exc
-from typing import Optional
-
-import requests
-
-import logging
-
-
-def splunkd_request(
-    splunkd_uri,
-    session_key,
-    method="GET",
-    headers=None,
-    data=None,
-    timeout=300,
-    retry=1,
-    verify=False,
-) -> Optional[requests.Response]:
-
-    headers = headers if headers is not None else {}
-    headers["Authorization"] = f"Splunk {session_key}"
-    content_type = headers.get("Content-Type")
-    if not content_type:
-        content_type = headers.get("content-type")
-
-    if not content_type:
-        content_type = "application/x-www-form-urlencoded"
-        headers["Content-Type"] = content_type
-
-    if data is not None:
-        if content_type == "application/json":
-            data = json.dumps(data)
-        else:
-            data = urllib.parse.urlencode(data)
-
-    msg_temp = "Failed to send rest request=%s, errcode=%s, reason=%s"
-    resp = None
-    for _ in range(retry):
-        try:
-            resp = requests.request(
-                method=method,
-                url=splunkd_uri,
-                data=data,
-                headers=headers,
-                timeout=timeout,
-                verify=verify,
-            )
-        except Exception:
-            logging.error(msg_temp, splunkd_uri, "unknown", format_exc())
-        else:
-            if resp.status_code not in (200, 201):
-                if not (method == "GET" and resp.status_code == 404):
-                    logging.debug(
-                        msg_temp, splunkd_uri, resp.status_code, code_to_msg(resp)
-                    )
-            else:
-                return resp
-    else:
-        return resp
-
-
-def code_to_msg(response: requests.Response):
-    code_msg_tbl = {
-        400: f"Request error. reason={response.text}",
-        401: "Authentication failure, invalid access credentials.",
-        402: "In-use license disables this feature.",
-        403: "Insufficient permission.",
-        404: "Requested endpoint does not exist.",
-        409: f"Invalid operation for this endpoint. reason={response.text}",
-        500: f"Unspecified internal server error. reason={response.text}",
-        503: (
-            "Feature is disabled in the configuration file. "
-            "reason={}".format(response.text)
-        ),
-    }
-
-    return code_msg_tbl.get(response.status_code, response.text)
diff --git a/apps/bitwarden_event_logs/lib/solnlib/schedule/job.py b/apps/bitwarden_event_logs/lib/solnlib/schedule/job.py
deleted file mode 100755
index bc1ff6de..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/schedule/job.py
+++ /dev/null
@@ -1,122 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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 threading
-import time
-
-
-class Job:
-    """Timer wraps the callback and timestamp related stuff."""
-
-    _ident = 0
-    _lock = threading.Lock()
-
-    def __init__(self, func, job_props, interval, when=None, job_id=None):
-        """
-        @job_props: dict like object
-        @func: execution function
-        @interval: execution interval
-        @when: seconds from epoch
-        @job_id: a unique id for the job
-        """
-
-        self._props = job_props
-        self._func = func
-        if when is None:
-            self._when = time.time()
-        else:
-            self._when = when
-        self._interval = interval
-
-        if job_id is not None:
-            self._id = job_id
-        else:
-            with Job._lock:
-                self._id = Job._ident + 1
-                Job._ident = Job._ident + 1
-        self._stopped = False
-
-    def ident(self):
-        return self._id
-
-    def get_interval(self):
-        return self._interval
-
-    def set_interval(self, interval):
-        self._interval = interval
-
-    def get_expiration(self):
-        return self._when
-
-    def set_initial_due_time(self, when):
-        if self._when is None:
-            self._when = when
-
-    def update_expiration(self):
-        self._when += self._interval
-
-    def get(self, key, default):
-        return self._props.get(key, default)
-
-    def get_props(self):
-        return self._props
-
-    def set_props(self, props):
-        self._props = props
-
-    def __cmp__(self, other):
-        if other is None:
-            return 1
-
-        self_k = (self.get_expiration(), self.ident())
-        other_k = (other.get_expiration(), other.ident())
-
-        if self_k == other_k:
-            return 0
-        elif self_k < other_k:
-            return -1
-        else:
-            return 1
-
-    def __eq__(self, other):
-        return isinstance(other, Job) and (self.ident() == other.ident())
-
-    def __lt__(self, other):
-        return self.__cmp__(other) == -1
-
-    def __gt__(self, other):
-        return self.__cmp__(other) == 1
-
-    def __ne__(self, other):
-        return not self.__eq__(other)
-
-    def __le__(self, other):
-        return self.__lt__(other) or self.__eq__(other)
-
-    def __ge__(self, other):
-        return self.__gt__(other) or self.__eq__(other)
-
-    def __hash__(self):
-        return self.ident()
-
-    def __call__(self):
-        self._func(self)
-
-    def stop(self):
-        self._stopped = True
-
-    def stopped(self):
-        return self._stopped
diff --git a/apps/bitwarden_event_logs/lib/solnlib/schedule/scheduler.py b/apps/bitwarden_event_logs/lib/solnlib/schedule/scheduler.py
deleted file mode 100755
index 5d53b439..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/schedule/scheduler.py
+++ /dev/null
@@ -1,162 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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 queue
-import random
-import threading
-from time import time
-
-import logging
-
-
-class Scheduler:
-    """A simple scheduler which schedules the periodic or once event."""
-
-    import sortedcontainers as sc
-
-    max_delay_time = 60
-
-    def __init__(self):
-        self._jobs = Scheduler.sc.SortedSet()
-        self._wakeup_q = queue.Queue()
-        self._lock = threading.Lock()
-        self._thr = threading.Thread(target=self._do_jobs)
-        # FIXME: the `daemon` property HAS to be passed in init() call ONLY,
-        # the below attribute setting is of incorrect spelling
-        self._thr.deamon = True
-        self._started = False
-
-    def start(self):
-        """Start the schduler which will start the internal thread for
-        scheduling jobs.
-
-        Please do tear_down when doing cleanup
-        """
-
-        if self._started:
-            logging.info("Scheduler already started.")
-            return
-        self._started = True
-
-        self._thr.start()
-
-    def tear_down(self):
-        """Stop the schduler which will stop the internal thread for scheduling
-        jobs."""
-
-        if not self._started:
-            logging.info("Scheduler already tear down.")
-            return
-
-        self._wakeup_q.put(True)
-
-    def _do_jobs(self):
-        while 1:
-            (sleep_time, jobs) = self.get_ready_jobs()
-            self._do_execution(jobs)
-            try:
-                done = self._wakeup_q.get(timeout=sleep_time)
-            except queue.Empty:
-                pass
-            else:
-                if done:
-                    break
-        self._started = False
-        logging.info("Scheduler exited.")
-
-    def get_ready_jobs(self):
-        """
-        @return: a 2 element tuple. The first element is the next ready
-                 duration. The second element is ready jobs list
-        """
-
-        now = time()
-        ready_jobs = []
-        sleep_time = 1
-
-        with self._lock:
-            job_set = self._jobs
-            total_jobs = len(job_set)
-            for job in job_set:
-                if job.get_expiration() <= now:
-                    ready_jobs.append(job)
-
-            if ready_jobs:
-                del job_set[: len(ready_jobs)]
-
-            for job in ready_jobs:
-                if job.get_interval() != 0 and not job.stopped():
-                    # repeated job, calculate next due time and enqueue
-                    job.update_expiration()
-                    job_set.add(job)
-
-            if job_set:
-                sleep_time = job_set[0].get_expiration() - now
-                if sleep_time < 0:
-                    logging.warn("Scheduler satuation, sleep_time=%s", sleep_time)
-                    sleep_time = 0.1
-
-        if ready_jobs:
-            logging.info(
-                "Get %d ready jobs, next duration is %f, "
-                "and there are %s jobs scheduling",
-                len(ready_jobs),
-                sleep_time,
-                total_jobs,
-            )
-
-        ready_jobs.sort(key=lambda job: job.get("priority", 0), reverse=True)
-        return (sleep_time, ready_jobs)
-
-    def add_jobs(self, jobs):
-        with self._lock:
-            now = time()
-            job_set = self._jobs
-            for job in jobs:
-                delay_time = random.randrange(0, self.max_delay_time)
-                job.set_initial_due_time(now + delay_time)
-                job_set.add(job)
-        self._wakeup()
-
-    def update_jobs(self, jobs):
-        with self._lock:
-            job_set = self._jobs
-            for njob in jobs:
-                job_set.discard(njob)
-                job_set.add(njob)
-        self._wakeup()
-
-    def remove_jobs(self, jobs):
-        with self._lock:
-            job_set = self._jobs
-            for njob in jobs:
-                njob.stop()
-                job_set.discard(njob)
-        self._wakeup()
-
-    def number_of_jobs(self):
-        with self._lock:
-            return len(self._jobs)
-
-    def disable_randomization(self):
-        self.max_delay_time = 1
-
-    def _wakeup(self):
-        self._wakeup_q.put(None)
-
-    def _do_execution(self, jobs):
-        for job in jobs:
-            job()
diff --git a/apps/bitwarden_event_logs/lib/solnlib/server_info.py b/apps/bitwarden_event_logs/lib/solnlib/server_info.py
deleted file mode 100755
index 56f2c006..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/server_info.py
+++ /dev/null
@@ -1,308 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""This module contains Splunk server info related functionalities."""
-
-import os
-import json
-from typing import Any, Dict, Optional
-
-try:
-    from splunk.rest import getWebCertFile, getWebKeyFile
-except (ModuleNotFoundError, ImportError):
-
-    def getWebCertFile():
-        return None
-
-    def getWebKeyFile():
-        return None
-
-
-try:
-    from splunk.rest import is_cert_or_key_encrypted
-except (ModuleNotFoundError, ImportError):
-
-    def is_cert_or_key_encrypted(cert_filename):
-        return False
-
-
-from splunklib import binding
-from solnlib import splunk_rest_client as rest_client
-from solnlib import utils
-from solnlib.splunkenv import get_splunkd_access_info
-
-__all__ = ["ServerInfo", "ServerInfoException"]
-
-
-class ServerInfoException(Exception):
-    """Exception raised by ServerInfo class."""
-
-    pass
-
-
-class ServerInfo:
-    """This class is a wrapper of Splunk server info."""
-
-    SHC_MEMBER_ENDPOINT = "/services/shcluster/member/members"
-    SHC_CAPTAIN_INFO_ENDPOINT = "/services/shcluster/captain/info"
-
-    def __init__(
-        self,
-        session_key: str,
-        scheme: Optional[str] = None,
-        host: Optional[str] = None,
-        port: Optional[int] = None,
-        **context: Any
-    ):
-        """Initializes ServerInfo.
-
-        Arguments:
-            session_key: Splunk access token.
-            scheme: The access scheme, default is None.
-            host: The host name, default is None.
-            port: The port number, default is None.
-            context: Other configurations for Splunk rest client.
-        """
-        is_localhost = False
-        if not all([scheme, host, port]) and os.environ.get("SPLUNK_HOME"):
-            scheme, host, port = get_splunkd_access_info(session_key)
-            is_localhost = (
-                host == "localhost" or host == "127.0.0.1" or host in ("::1", "[::1]")
-            )
-
-        web_key_file = getWebKeyFile()
-        web_cert_file = getWebCertFile()
-        if web_cert_file and (
-            web_key_file is None or not is_cert_or_key_encrypted(web_key_file)
-        ):
-            context["cert_file"] = web_cert_file
-
-            if web_key_file is not None:
-                context["key_file"] = web_key_file
-
-            if all([is_localhost, context.get("verify") is None]):
-                # NOTE: this is specifically for mTLS communication
-                # ONLY if scheme, host, port aren't provided AND user hasn't provided server certificate
-                # we set verify to off (similar to 'rest.simpleRequest' implementation)
-                context["verify"] = False
-
-        self._rest_client = rest_client.SplunkRestClient(
-            session_key, "-", scheme=scheme, host=host, port=port, **context
-        )
-
-    @classmethod
-    def from_server_uri(
-        cls, server_uri: str, session_key: str, **context: Any
-    ) -> "ServerInfo":
-        """Creates ServerInfo class using server_uri and session_key.
-
-        Note: splunktalib uses these parameters to create it's ServerInfo class,
-        so this method should ease the transition from splunktalib to solnlib.
-
-        Arguments:
-            server_uri: splunkd URI.
-            session_key: Splunk access token.
-            context: Other configurations for Splunk rest client.
-
-        Returns:
-            An instance of `ServerInfo`.
-
-        Raises:
-            ValueError: server_uri is in the wrong format.
-        """
-        scheme, host, port = utils.extract_http_scheme_host_port(server_uri)
-        return ServerInfo(
-            session_key,
-            scheme=scheme,
-            host=host,
-            port=port,
-            **context,
-        )
-
-    def to_dict(self) -> Dict:
-        """Returns server information in a form dictionary.
-
-        Note: This method is implemented here to have compatibility with splunktalib's
-        analogue.
-
-        Returns:
-            Server information in a dictionary format.
-        """
-        return self._server_info()
-
-    @utils.retry(exceptions=[binding.HTTPError])
-    def _server_info(self):
-        return self._rest_client.info
-
-    @property
-    def server_name(self) -> str:
-        """Get server name.
-
-        Returns:
-            Server name.
-        """
-        return self._server_info()["serverName"]
-
-    @property
-    def guid(self) -> str:
-        """Get guid for the server.
-
-        Returns:
-            Server GUID.
-        """
-        return self._server_info()["guid"]
-
-    @property
-    def version(self) -> str:
-        """Get Splunk server version.
-
-        Returns:
-            Splunk version.
-        """
-        return self._server_info()["version"]
-
-    def is_captain(self) -> bool:
-        """Check if this server is SHC captain.
-
-        Note during a rolling start of SH members, the captain may be changed
-        from machine to machine. To avoid the race condition, client may need
-        do necessary sleep and then poll `is_captain_ready() == True` and then
-        check `is_captain()`. See `is_captain_ready()` for more details.
-
-        Returns:
-            True if this server is SHC captain else False.
-        """
-
-        return "shc_captain" in self._server_info()["server_roles"]
-
-    def is_cloud_instance(self) -> bool:
-        """Check if this server is a cloud instance.
-
-        Returns:
-            True if this server is a cloud instance else False.
-        """
-
-        try:
-            return self._server_info()["instance_type"] == "cloud"
-        except KeyError:
-            return False
-
-    def is_search_head(self) -> bool:
-        """Check if this server is a search head.
-
-        Returns:
-            True if this server is a search head else False.
-        """
-
-        server_info = self._server_info()
-        for sh in ("search_head", "cluster_search_head"):
-            if sh in server_info["server_roles"]:
-                return True
-
-        return False
-
-    def is_shc_member(self) -> bool:
-        """Check if this server is a SHC member.
-
-        Returns:
-            True if this server is a SHC member else False.
-        """
-
-        server_info = self._server_info()
-        for sh in ("shc_member", "shc_captain"):
-            if sh in server_info["server_roles"]:
-                return True
-
-        return False
-
-    @utils.retry(exceptions=[binding.HTTPError])
-    def get_shc_members(self) -> list:
-        """Get SHC members.
-
-        Raises:
-            ServerInfoException: If this server has no SHC members.
-
-        Returns:
-            List of SHC members [(label, peer_scheme_host_port) ...].
-        """
-        try:
-            content = self._rest_client.get(
-                self.SHC_MEMBER_ENDPOINT, output_mode="json"
-            ).body.read()
-        except binding.HTTPError as e:
-            if e.status != 404 and e.status != 503:
-                raise
-
-            raise ServerInfoException(
-                "This server is not a SHC member and has no SHC members."
-            )
-
-        members = []
-        for member in json.loads(content)["entry"]:
-            content = member["content"]
-            members.append((content["label"], content["peer_scheme_host_port"]))
-
-        return members
-
-    @utils.retry(exceptions=[binding.HTTPError])
-    def is_captain_ready(self) -> bool:
-        """Check if captain is ready.
-
-        Client usually first polls this function until captain is ready
-        and then call is_captain to detect current captain machine
-
-        Returns:
-            True if captain is ready else False.
-
-        Examples:
-            >>> from solnlib import server_info
-            >>> serverinfo = server_info.ServerInfo(session_key)
-            >>> while 1:
-            >>>    if serverinfo.is_captain_ready():
-            >>>        break
-            >>>    time.sleep(2)
-            >>>
-            >>> # If do_stuff can only be executed in SH captain
-            >>> if serverinfo.is_captain():
-            >>>    do_stuff()
-        """
-
-        cap_info = self.captain_info()
-        return utils.is_true(cap_info["service_ready_flag"]) and utils.is_false(
-            cap_info["maintenance_mode"]
-        )
-
-    @utils.retry(exceptions=[binding.HTTPError])
-    def captain_info(self) -> dict:
-        """Get captain information.
-
-        Raises:
-            ServerInfoException: If there is SHC is not enabled.
-
-        Returns:
-            Captain information.
-        """
-
-        try:
-            content = self._rest_client.get(
-                self.SHC_CAPTAIN_INFO_ENDPOINT, output_mode="json"
-            ).body.read()
-        except binding.HTTPError as e:
-            if e.status == 503 and "not available" in str(e):
-                raise ServerInfoException(str(e))
-            raise
-
-        return json.loads(content)["entry"][0]["content"]
diff --git a/apps/bitwarden_event_logs/lib/solnlib/soln_exceptions.py b/apps/bitwarden_event_logs/lib/solnlib/soln_exceptions.py
deleted file mode 100755
index 65d74a64..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/soln_exceptions.py
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-class ConfManagerException(Exception):
-    """Exception raised by ConfManager class."""
-
-    pass
-
-
-class ConfStanzaNotExistException(Exception):
-    """Exception raised by ConfFile class."""
-
-    pass
-
-
-class InvalidPortError(ValueError):
-    """Exception raised when an invalid proxy port is provided."""
-
-    pass
-
-
-class InvalidHostnameError(ValueError):
-    """Exception raised when an invalid proxy hostname is provided."""
-
-    pass
diff --git a/apps/bitwarden_event_logs/lib/solnlib/splunk_rest_client.py b/apps/bitwarden_event_logs/lib/solnlib/splunk_rest_client.py
deleted file mode 100755
index d960a2e3..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/splunk_rest_client.py
+++ /dev/null
@@ -1,245 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""This module proxy all REST call to splunklib SDK, it handles proxy, certs
-etc in this centralized location.
-
-All clients should use SplunkRestProxy to do REST call instead of
-calling splunklib SDK directly in business logic code.
-"""
-
-import logging
-import os
-import sys
-import traceback
-import solnlib
-
-from io import BytesIO
-from urllib.parse import quote
-from urllib3.util.retry import Retry
-from splunklib import binding, client
-
-from .net_utils import validate_scheme_host_port
-from .splunkenv import get_splunkd_access_info
-
-__all__ = ["SplunkRestClient"]
-MAX_REQUEST_RETRIES = 5
-
-
-def _get_proxy_info(context):
-    if not context.get("proxy_hostname") or not context.get("proxy_port"):
-        return None
-
-    user_pass = ""
-    if context.get("proxy_username") and context.get("proxy_password"):
-        username = quote(context["proxy_username"], safe="")
-        password = quote(context["proxy_password"], safe="")
-        user_pass = f"{username}:{password}@"
-
-    proxy = "http://{user_pass}{host}:{port}".format(
-        user_pass=user_pass, host=context["proxy_hostname"], port=context["proxy_port"]
-    )
-    proxies = {
-        "http": proxy,
-        "https": proxy,
-    }
-    return proxies
-
-
-def _request_handler(context):
-    """
-    :param context: Http connection context can contain the following
-        key/values: {
-        'proxy_hostname': string,
-        'proxy_port': int,
-        'proxy_username': string,
-        'proxy_password': string,
-        'key_file': string,
-        'cert_file': string
-        'pool_connections', int,
-        'pool_maxsize', int,
-        }
-    :type content: dict
-    """
-
-    try:
-        import requests
-    except ImportError:
-        # FIXME proxy ?
-        return binding.handler(
-            key_file=context.get("key_file"), cert_file=context.get("cert_file")
-        )
-
-    try:
-        requests.urllib3.disable_warnings()
-    except AttributeError:
-        pass
-
-    proxies = _get_proxy_info(context)
-    verify = context.get("verify", False)
-
-    if context.get("key_file") and context.get("cert_file"):
-        # cert: if tuple, ('cert', 'key') pair as per requests library
-        cert = context["cert_file"], context["key_file"]
-    elif context.get("cert_file"):
-        cert = context["cert_file"]
-    elif context.get("cert"):
-        # as the solnlib uses requests, we need to have a check for 'cert' key as well
-        cert = context["cert"]
-    else:
-        cert = None
-
-    retries = Retry(
-        total=MAX_REQUEST_RETRIES,
-        backoff_factor=0.3,
-        status_forcelist=[500, 502, 503, 504],
-        allowed_methods=["GET", "POST", "PUT", "DELETE"],
-        raise_on_status=False,
-    )
-    if context.get("pool_connections", 0):
-        logging.info("Use HTTP connection pooling")
-        session = requests.Session()
-        adapter = requests.adapters.HTTPAdapter(
-            max_retries=retries,
-            pool_connections=context.get("pool_connections", 10),
-            pool_maxsize=context.get("pool_maxsize", 10),
-        )
-        session.mount("https://", adapter)
-        req_func = session.request
-    else:
-        req_func = requests.request
-
-    def request(url, message, **kwargs):
-        """
-        :param url: URL
-        :type url: string
-        :param message: Can contain following key/values: {
-            'method': 'GET' or 'DELETE', or 'PUT' or 'POST'
-            'headers': [[key, value], [key, value], ...],
-            'body': string
-            }
-        :type message: dict
-        """
-
-        body = message.get("body")
-        headers = {
-            "User-Agent": f"solnlib/{solnlib.__version__} rest-client {sys.platform}",
-            "Accept": "*/*",
-            "Connection": "Keep-Alive",
-        }
-
-        if body:
-            headers["Content-Length"] = str(len(body))
-
-        for key, value in message["headers"]:
-            headers[key] = value
-
-        method = message.get("method", "GET")
-
-        try:
-            resp = req_func(
-                method,
-                url,
-                data=body,
-                headers=headers,
-                stream=False,
-                verify=verify,
-                proxies=proxies,
-                cert=cert,
-                **kwargs,
-            )
-        except Exception:
-            logging.error(
-                "Failed to issue http request=%s to url=%s, error=%s",
-                method,
-                url,
-                traceback.format_exc(),
-            )
-            raise
-
-        return {
-            "status": resp.status_code,
-            "reason": resp.reason,
-            "headers": dict(resp.headers),
-            "body": BytesIO(resp.content),
-        }
-
-    return request
-
-
-class SplunkRestClient(client.Service):
-    """Splunk REST client."""
-
-    def __init__(
-        self,
-        session_key: str,
-        app: str,
-        owner: str = "nobody",
-        scheme: str = None,
-        host: str = None,
-        port: int = None,
-        **context: dict,
-    ):
-        """Initializes SplunkRestClient.
-
-        Arguments `scheme`, `host` and `port` are optional in the Splunk
-        environment (when environment variable SPLUNK_HOME is set). In this
-        situation `get_splunkd_access_info` will be used to set `scheme`,
-        `host` and `port`. In case of using `SplunkRestClient` outside of
-        Splunk environment - `scheme`, `host` and `port` should be provided.
-
-        Arguments:
-            session_key: Splunk access token.
-            app: App name of namespace.
-            owner: Owner of namespace, default is `nobody`.
-            scheme: The access scheme, default is None.
-            host: The host name, default is None.
-            port: The port number, default is None.
-            context: Other configurations, it can contain `proxy_hostname`,
-                `proxy_port`, `proxy_username`, `proxy_password`, then proxy will
-                be accounted and setup, all REST APIs to splunkd will be through
-                the proxy. If `context` contains `key_file`, `cert_file`, then
-                certification will be accounted and setup, all REST APIs to splunkd
-                will use certification. If `context` contains `pool_connections`,
-                `pool_maxsize`, then HTTP connection will be pooled.
-
-        Raises:
-            ValueError: if scheme, host or port are invalid.
-        """
-        # Only do splunkd URI discovery in SPLUNK env (SPLUNK_HOME is set).
-        if not all([scheme, host, port]) and os.environ.get("SPLUNK_HOME"):
-            scheme, host, port = get_splunkd_access_info(session_key)
-        if os.environ.get("SPLUNK_HOME") is None:
-            if not all([scheme, host, port]):
-                raise ValueError(
-                    "scheme, host, port should be provided outside of Splunk environment"
-                )
-
-        validate_scheme_host_port(scheme, host, port)
-        if host == "[::1]":
-            host = "::1"
-
-        handler = _request_handler(context)
-        super().__init__(
-            handler=handler,
-            scheme=scheme,
-            host=host,
-            port=port,
-            token=session_key,
-            app=app,
-            owner=owner,
-            autologin=True,
-        )
diff --git a/apps/bitwarden_event_logs/lib/solnlib/splunkenv.py b/apps/bitwarden_event_logs/lib/solnlib/splunkenv.py
deleted file mode 100755
index 09ce12de..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/splunkenv.py
+++ /dev/null
@@ -1,414 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""Splunk platform related utilities."""
-
-
-import os
-import os.path as op
-import socket
-import subprocess
-import json
-from configparser import ConfigParser
-from io import StringIO
-from typing import List, Optional, Tuple, Union
-import __main__
-from solnlib._settings import use_btool
-from .utils import is_true
-
-try:
-    from splunk.rest import simpleRequest
-except ImportError:
-
-    def simpleRequest(*args, **kwargs):
-        raise ImportError("This module requires Splunk to be installed.")
-
-
-try:
-    from splunk import getSessionKey
-except ImportError:
-
-    def getSessionKey(*args, **kwargs):
-        raise ImportError("This module requires Splunk to be installed.")
-
-
-try:
-    from splunk.clilib.bundle_paths import make_splunkhome_path as msp
-except ImportError:
-
-    def msp(*args, **kwargs):
-        raise ImportError("This module requires Splunk to be installed.")
-
-
-__all__ = [
-    "make_splunkhome_path",
-    "get_splunk_host_info",
-    "get_splunk_bin",
-    "get_splunkd_access_info",
-    "get_scheme_from_hec_settings",
-    "get_splunkd_uri",
-    "get_conf_key_value",
-    "get_conf_stanza",
-    "get_conf_stanzas",
-]
-
-ETC_LEAF = "etc"
-APP_SYSTEM = "system"
-APP_HEC = "splunk_httpinput"
-
-
-class SessionKeyNotFound(Exception):
-    pass
-
-
-def make_splunkhome_path(parts: Union[List, Tuple]) -> str:
-    """Construct absolute path by $SPLUNK_HOME and `parts`.
-
-    Concatenate $SPLUNK_HOME and `parts` to an absolute path.
-    For example, `parts` is ['etc', 'apps', 'Splunk_TA_test'],
-    the return path will be $SPLUNK_HOME/etc/apps/Splunk_TA_test.
-    Note: this function assumed SPLUNK_HOME is in environment varialbes.
-
-    Arguments:
-        parts: Path parts.
-
-    Returns:
-        Absolute path.
-
-    Raises:
-        ValueError: Escape from intended parent directories.
-    """
-    return msp(parts)
-
-
-def get_splunk_host_info(session_key: Optional[str] = None) -> Tuple:
-    """Get splunk host info.
-
-    Arguments:
-        session_key: Needed to make a call to config endpoint. If 'None', solnlib will try to get it from
-            splunk.getSessionKey() and/or __main__ module and if it won't get it, SessionKeyNotFound will be raised.
-    Returns:
-        Tuple of (server_name, host_name).
-    """
-
-    server_name = get_conf_key_value(
-        "server",
-        "general",
-        "serverName",
-        APP_SYSTEM,
-        session_key=session_key,
-    )
-    host_name = socket.gethostname()
-
-    return server_name, host_name
-
-
-def get_splunk_bin() -> str:
-    """Get absolute path of splunk CLI.
-
-    Returns:
-        Absolute path of splunk CLI.
-    """
-
-    if os.name == "nt":
-        splunk_bin = "splunk.exe"
-    else:
-        splunk_bin = "splunk"
-    return make_splunkhome_path(("bin", splunk_bin))
-
-
-def get_splunkd_access_info(session_key: Optional[str] = None) -> Tuple[str, str, int]:
-    """Get splunkd server access info.
-
-    Arguments:
-        session_key: Needed to make a call to config endpoint. If 'None', solnlib will try to get it from
-            splunk.getSessionKey() and/or __main__ module and if it won't get it, SessionKeyNotFound will be raised.
-    Returns:
-        Tuple of (scheme, host, port).
-    """
-    enable_splunkd_ssl = get_conf_key_value(
-        "server",
-        "sslConfig",
-        "enableSplunkdSSL",
-        APP_SYSTEM,
-        session_key=session_key,
-    )
-
-    if is_true(enable_splunkd_ssl):
-        scheme = "https"
-    else:
-        scheme = "http"
-
-    host_port = get_conf_key_value(
-        "web",
-        "settings",
-        "mgmtHostPort",
-        APP_SYSTEM,
-        session_key=session_key,
-    )
-    host_port = host_port.strip()
-    host_port_split_parts = host_port.split(":")
-    host = ":".join(host_port_split_parts[:-1])
-    port = int(host_port_split_parts[-1])
-
-    if "SPLUNK_BINDIP" in os.environ:
-        bindip = os.environ["SPLUNK_BINDIP"]
-        port_idx = bindip.rfind(":")
-        host = bindip[:port_idx] if port_idx > 0 else bindip
-
-    return scheme, host, port
-
-
-def get_scheme_from_hec_settings(session_key: Optional[str] = None) -> str:
-    """Get scheme from HEC global settings.
-
-    Arguments:
-        session_key: Needed to make a call to config endpoint. If 'None', solnlib will try to get it from
-            splunk.getSessionKey() and/or __main__ module and if it won't get it, SessionKeyNotFound will be raised.
-    Returns:
-        scheme (str)
-    """
-    try:
-        ssl_enabled = get_conf_key_value(
-            "inputs",
-            "http",
-            "enableSSL",
-            APP_HEC,
-            session_key=session_key,
-        )
-    except KeyError:
-        raise KeyError(
-            "Cannot get enableSSL setting form conf: 'inputs' and stanza: '[http]'. "
-            "Verify that your Splunk instance has the inputs.conf file with the correct [http] stanza. "
-            "For more information see: "
-            "https://docs.splunk.com/Documentation/Splunk/9.2.0/Data/UseHECusingconffiles"
-        )
-
-    if is_true(ssl_enabled):
-        scheme = "https"
-    else:
-        scheme = "http"
-
-    return scheme
-
-
-def get_splunkd_uri(session_key: Optional[str] = None) -> str:
-    """Get splunkd uri.
-
-    Arguments:
-        session_key: Needed to make a call to config endpoint. If 'None', solnlib will try to get it from
-            splunk.getSessionKey() and/or __main__ module and if it won't get it, SessionKeyNotFound will be raised.
-    Returns:
-        Splunkd uri.
-    """
-
-    if os.environ.get("SPLUNKD_URI"):
-        return os.environ["SPLUNKD_URI"]
-
-    scheme, host, port = get_splunkd_access_info(session_key)
-    return f"{scheme}://{host}:{port}"
-
-
-def get_conf_key_value(
-    conf_name: str,
-    stanza: str,
-    key: str,
-    app_name: str,
-    session_key: Optional[str] = None,
-    user: str = "nobody",
-    raw_output: Optional[bool] = False,
-) -> Union[str, List, dict]:
-    """Get value of `key` of `stanza` in `conf_name`.
-
-    Arguments:
-        conf_name: Config file.
-        stanza: Stanza name.
-        key: Key name in the stanza.
-        app_name: Application name. To make a call to global context use '-' as app_name and set raw_output=True.
-            In that case manual parsing is needed as response may be the list with multiple entries.
-        session_key: Needed to make a call to config endpoint. If 'None', solnlib will try to get it from
-            splunk.getSessionKey() and/or __main__ module and if it won't get it, SessionKeyNotFound will be raised.
-        user: used for set user context in API call. Optional.
-        raw_output: if 'true' full, decoded response in json format will be returned. It should be set to True when
-            app_name is a global context '/-/'. In that case splunk API may return multiple entries.
-
-    Returns:
-        Config value.
-
-    Raises:
-        KeyError: If `stanza` or `key` doesn't exist.
-    """
-
-    if use_btool:
-        app = None if app_name == "-" else app_name
-        stanzas = get_conf_stanzas(conf_name, app)
-        return stanzas[stanza][key]
-
-    stanzas = _get_conf_stanzas_from_splunk_api(
-        conf_name, app_name, session_key=session_key, user=user, stanza=stanza
-    )
-
-    if raw_output:
-        return stanzas
-
-    stanza = stanzas.get("entry")[0].get("content")
-    requested_key = stanza[key]
-    return requested_key
-
-
-def get_conf_stanza(
-    conf_name: str,
-    stanza: str,
-    app_name: str,
-    session_key: Optional[str] = None,
-    user: str = "nobody",
-    raw_output: Optional[bool] = False,
-) -> dict:
-    """Get `stanza` in `conf_name`.
-
-    Arguments:
-        conf_name: Config file.
-        stanza: Stanza name.
-        app_name: Application name. To make a call to global context use '-' as app_name and set raw_output=True.
-            In that case manual parsing is needed as response may be the list with multiple entries.
-        session_key: Needed to make a call to config endpoint. If 'None', solnlib will try to get it from
-            splunk.getSessionKey() and/or __main__ module and if it won't get it, SessionKeyNotFound will be raised.
-        user: used for set user context in API call. Optional.
-        raw_output: if 'true' full, decoded response in json format will be returned. It should be set to True when
-            app_name is a global context '/-/'. In that case splunk API may return multiple entries.
-
-    Returns:
-        Config stanza.
-
-    Raises:
-         KeyError: If stanza doesn't exist.
-    """
-
-    if use_btool:
-        app = None if app_name == "-" else app_name
-        stanzas = get_conf_stanzas(conf_name, app)
-        return stanzas[stanza]
-
-    stanzas = _get_conf_stanzas_from_splunk_api(
-        conf_name, app_name, session_key=session_key, user=user, stanza=stanza
-    )
-
-    if raw_output:
-        return stanzas
-
-    stanza = stanzas.get("entry")[0].get("content")
-    return stanza
-
-
-def get_conf_stanzas(conf_name: str, app_name: Optional[str] = None) -> dict:
-    """Get stanzas of `conf_name`
-
-    Arguments:
-        conf_name: Config file.
-        app_name: Application name. Optional.
-
-    Returns:
-        Config stanzas.
-
-    Examples:
-       >>> stanzas = get_conf_stanzas('server')
-       >>> return: {'serverName': 'testServer', 'sessionTimeout': '1h', ...}
-    """
-
-    if conf_name.endswith(".conf"):
-        conf_name = conf_name[:-5]
-
-    # TODO: dynamically calculate SPLUNK_HOME
-    btool_cli = [
-        op.join(os.environ["SPLUNK_HOME"], "bin", "splunk"),
-        "cmd",
-        "btool",
-        conf_name,
-        "list",
-    ]
-
-    if app_name:
-        btool_cli.append(f"--app={app_name}")
-
-    p = subprocess.Popen(  # nosemgrep: python.lang.security.audit.dangerous-subprocess-use.dangerous-subprocess-use
-        btool_cli, stdout=subprocess.PIPE, stderr=subprocess.PIPE
-    )
-    out, _ = p.communicate()
-
-    if isinstance(out, bytes):
-        out = out.decode()
-
-    parser = ConfigParser(**{"strict": False})
-    parser.optionxform = str
-    parser.read_file(StringIO(out))
-
-    out = {}
-    for section in parser.sections():
-        out[section] = {item[0]: item[1] for item in parser.items(section, raw=True)}
-    return out
-
-
-def _get_conf_stanzas_from_splunk_api(
-    conf_name: str,
-    app_name: str,
-    session_key: Optional[str] = None,
-    user: str = "nobody",
-    stanza: Optional[str] = None,
-) -> dict:
-    """Get stanzas of `conf_name` using splunk API:
-
-    /servicesNS/{user}/{app_name}/configs/conf-{conf_name}/{stanza}
-
-    Arguments:
-        conf_name: Config file.
-        app_name: Application name. To make a call to global context use '-' as app_name and set raw_output=True.
-            In that case manual parsing is needed as response may be the list with multiple entries.
-        session_key: Needed to make a call to config endpoint. If 'None', solnlib will try to get it from
-            splunk.getSessionKey() and/or __main__ module and if it won't get it, SessionKeyNotFound will be raised.
-        user: used for set user context in API call. Optional.
-        stanza: Stanza name. Optional.
-
-    Returns:
-        json response.
-    """
-
-    url = f"/servicesNS/{user}/{app_name}/configs/conf-{conf_name}"
-
-    if stanza:
-        url = url + "/" + stanza
-
-    if not session_key:
-        session_key = getSessionKey()
-
-    if not session_key and hasattr(__main__, "___sessionKey"):
-        session_key = getattr(__main__, "___sessionKey")
-
-    if not session_key:
-        raise SessionKeyNotFound(
-            "Session key is missing. If you are using 'splunkenv' module in your TA, please ensure you are "
-            "providing session_key to it's functions. For more information "
-            "please see: https://splunk.github.io/addonfactory-solutions-library-python/release_7_0_0/"
-        )
-
-    server_response, server_content = simpleRequest(
-        url,
-        sessionKey=session_key,
-        getargs={"output_mode": "json"},
-    )
-
-    result = json.loads(server_content.decode())
-
-    return result
diff --git a/apps/bitwarden_event_logs/lib/solnlib/time_parser.py b/apps/bitwarden_event_logs/lib/solnlib/time_parser.py
deleted file mode 100755
index 64b40ebf..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/time_parser.py
+++ /dev/null
@@ -1,151 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""This module provides interfaces to parse and convert timestamp."""
-
-import datetime
-import json
-from typing import Any
-
-from splunklib import binding
-
-from . import splunk_rest_client as rest_client
-from .utils import retry
-
-__all__ = ["TimeParser"]
-
-
-class InvalidTimeFormatException(Exception):
-    """Exception for invalid time format."""
-
-    pass
-
-
-class TimeParser:
-    """Datetime parser.
-
-    Use splunkd rest to parse datetime.
-
-    Examples:
-       >>> from solnlib import time_parser
-       >>> tp = time_parser.TimeParser(session_key)
-       >>> tp.to_seconds('2011-07-06T21:54:23.000-07:00')
-       >>> tp.to_utc('2011-07-06T21:54:23.000-07:00')
-       >>> tp.to_local('2011-07-06T21:54:23.000-07:00')
-    """
-
-    URL = "/services/search/timeparser"
-
-    def __init__(
-        self,
-        session_key: str,
-        scheme: str = None,
-        host: str = None,
-        port: int = None,
-        **context: Any,
-    ):
-        """Initializes TimeParser.
-
-        Arguments:
-            session_key: Splunk access token.
-            scheme: (optional) The access scheme, default is None.
-            host: (optional) The host name, default is None.
-            port: (optional) The port number, default is None.
-            context: Other configurations for Splunk rest client.
-
-        Raises:
-            ValueError: if scheme, host or port are invalid.
-        """
-        self._rest_client = rest_client.SplunkRestClient(
-            session_key, "-", scheme=scheme, host=host, port=port, **context
-        )
-
-    @retry(exceptions=[binding.HTTPError])
-    def to_seconds(self, time_str: str) -> float:
-        """Parse `time_str` and convert to seconds since epoch.
-
-        Arguments:
-            time_str: ISO8601 format timestamp, example: 2011-07-06T21:54:23.000-07:00.
-
-        Raises:
-            binding.HTTPError: rest client returns an exception (everything
-                else than 400 code).
-            InvalidTimeFormatException: when time format is invalid (rest
-                client returns 400 code).
-
-        Returns:
-            Seconds since epoch.
-        """
-
-        try:
-            response = self._rest_client.get(
-                self.URL, output_mode="json", time=time_str, output_time_format="%s"
-            ).body.read()
-        except binding.HTTPError as e:
-            if e.status != 400:
-                raise
-
-            raise InvalidTimeFormatException(f"Invalid time format: {time_str}.")
-
-        seconds = json.loads(response)[time_str]
-        return float(seconds)
-
-    def to_utc(self, time_str: str) -> datetime.datetime:
-        """Parse `time_str` and convert to UTC timestamp.
-
-        Arguments:
-            time_str: ISO8601 format timestamp, example: 2011-07-06T21:54:23.000-07:00.
-
-        Raises:
-            binding.HTTPError: rest client returns an exception (everything
-                else than 400 code).
-            InvalidTimeFormatException: when time format is invalid (rest
-                client returns 400 code).
-
-        Returns:
-            UTC timestamp.
-        """
-
-        return datetime.datetime.utcfromtimestamp(self.to_seconds(time_str))
-
-    @retry(exceptions=[binding.HTTPError])
-    def to_local(self, time_str: str) -> str:
-        """Parse `time_str` and convert to local timestamp.
-
-        Arguments:
-            time_str: ISO8601 format timestamp, example: 2011-07-06T21:54:23.000-07:00.
-
-        Raises:
-            binding.HTTPError: rest client returns an exception (everything
-                else than 400 code).
-            InvalidTimeFormatException: when time format is invalid (rest
-                client returns 400 code).
-
-        Returns:
-            Local timestamp in ISO8601 format.
-        """
-
-        try:
-            response = self._rest_client.get(
-                self.URL, output_mode="json", time=time_str
-            ).body.read()
-        except binding.HTTPError as e:
-            if e.status != 400:
-                raise
-
-            raise InvalidTimeFormatException(f"Invalid time format: {time_str}.")
-
-        return json.loads(response)[time_str]
diff --git a/apps/bitwarden_event_logs/lib/solnlib/timer_queue.py b/apps/bitwarden_event_logs/lib/solnlib/timer_queue.py
deleted file mode 100755
index 53d7d439..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/timer_queue.py
+++ /dev/null
@@ -1,324 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""A simple thread safe timer queue implementation which has O(logn) time
-complexity."""
-import logging
-import queue as Queue
-import threading
-import traceback
-from time import time
-from typing import Callable, List, Tuple
-
-import sortedcontainers as sc
-
-__all__ = ["Timer", "TimerQueueStruct", "TimerQueue"]
-
-
-class Timer:
-    """Timer wraps the callback and timestamp related attributes."""
-
-    _ident = 0
-    _lock = threading.Lock()
-
-    def __init__(self, callback: Callable, when: int, interval: int, ident: int = None):
-        """Initializes Timer.
-
-        Arguments:
-            callback: Arbitrary callable object.
-            when: The first expiration time, seconds since epoch.
-            interval: Timer interval, if equals 0, one time timer, otherwise
-                the timer will be periodically executed.
-            ident: (optional) Timer identity.
-        """
-        self._callback = callback
-        self.when = when
-        self.interval = interval
-
-        if ident is not None:
-            self.ident = ident
-        else:
-            with Timer._lock:
-                self.ident = Timer._ident + 1
-                Timer._ident = Timer._ident + 1
-
-    def update_expiration(self):
-        self.when += self.interval
-
-    def __hash__(self):
-        return hash(self.ident)
-
-    def __eq__(self, other):
-        return isinstance(other, Timer) and (self.ident == other.ident)
-
-    def __lt__(self, other):
-        return (self.when, self.ident) < (other.when, other.ident)
-
-    def __le__(self, other):
-        return (self.when, self.ident) <= (other.when, other.ident)
-
-    def __gt__(self, other):
-        return (self.when, self.ident) > (other.when, other.ident)
-
-    def __ge__(self, other):
-        return (self.when, self.ident) >= (other.when, other.ident)
-
-    def __call__(self):
-        self._callback()
-
-
-TEARDOWN_SENTINEL = None
-
-
-class TimerQueueStruct:
-    """The underlying data structure for TimerQueue."""
-
-    def __init__(self):
-        self._timers = sc.SortedSet()
-        self._cancelling_timers = {}
-
-    def add_timer(
-        self, callback: Callable, when: int, interval: int, ident: int
-    ) -> Timer:
-        """Add timer to the data structure.
-
-        Arguments:
-            callback: Arbitrary callable object.
-            when: The first expiration time, seconds since epoch.
-            interval: Timer interval, if equals 0, one time timer, otherwise
-                the timer will be periodically executed
-            ident: (optional) Timer identity.
-
-        Returns:
-            A timer object which should not be manipulated directly by
-                clients. Used to delete/update the timer.
-        """
-
-        timer = Timer(callback, when, interval, ident)
-        self._timers.add(timer)
-        return timer
-
-    def remove_timer(self, timer: Timer):
-        """Remove timer from data structure.
-
-        Arguments:
-            timer: Timer object which is returned by ``TimerQueueStruct.add_timer``.
-        """
-
-        try:
-            self._timers.remove(timer)
-        except ValueError:
-            logging.info(
-                "Timer=%s is not in queue, move it to cancelling " "list", timer.ident
-            )
-        else:
-            self._cancelling_timers[timer.ident] = timer
-
-    def get_expired_timers(self) -> Tuple:
-        """Get a list of expired timers.
-
-        Returns:
-            A tuple of ``Timer``, empty list if there is no expired timers.
-        """
-
-        next_expired_time = 0
-        now = time()
-        expired_timers = []
-        for timer in self._timers:
-            if timer.when <= now:
-                expired_timers.append(timer)
-
-        if expired_timers:
-            del self._timers[: len(expired_timers)]
-
-        if self._timers:
-            next_expired_time = self._timers[0].when
-        return next_expired_time, expired_timers
-
-    def reset_timers(self, expired_timers: List[Timer]) -> bool:
-        """Re-add the expired periodical timers to data structure for next
-        round scheduling.
-
-        Arguments:
-            expired_timers: List of expired timers.
-
-        Returns:
-            True if there are timers added, False otherwise.
-        """
-
-        has_new_timer = False
-        cancelling_timers = self._cancelling_timers
-        for timer in expired_timers:
-            if timer.ident in cancelling_timers:
-                continue
-            elif timer.interval:
-                # Repeated timer
-                timer.update_expiration()
-                self._timers.add(timer)
-                has_new_timer = True
-        cancelling_timers.clear()
-        return has_new_timer
-
-    def check_and_execute(self) -> float:
-        """Get expired timers and execute callbacks for the timers.
-
-        Returns:
-            Duration of next expired timer.
-        """
-
-        (next_expired_time, expired_timers) = self.get_expired_timers()
-        for timer in expired_timers:
-            try:
-                timer()
-            except Exception:
-                logging.error(traceback.format_exc())
-
-        self.reset_timers(expired_timers)
-        return _calc_sleep_time(next_expired_time)
-
-
-class TimerQueue:
-    r"""A simple timer queue implementation.
-
-    It runs a separate thread to handle timers Note: to effectively use this
-    timer queue, the timer callback should be short, otherwise it will cause
-    other timers's delay execution. A typical use scenario in production is
-    that the timers are just a simple functions which inject themselvies to
-    a task queue and then they are picked up by a threading/process pool to
-    execute, as shows below:
-
-        Timers --enqueue---> TimerQueue --------expiration-----------
-                                                                    |
-                                                                    |
-                                                                   \|/
-        Threading/Process Pool <---- TaskQueue <--enqueue-- Timers' callback (nonblocking)
-
-    Examples:
-           >>> from solnlib import timer_queue
-           >>> tq = timer_queue.TimerQueue()
-           >>> tq.start()
-           >>> t = tq.add_timer(my_func, time.time(), 10)
-           >>> # do other stuff
-           >>> tq.stop()
-    """
-
-    def __init__(self):
-        self._timers = TimerQueueStruct()
-        self._lock = threading.Lock()
-        self._wakeup_queue = Queue.Queue()
-        self._thr = threading.Thread(target=self._check_and_execute)
-        self._thr.daemon = True
-        self._started = False
-
-    def start(self):
-        """Start the timer queue."""
-
-        if self._started:
-            return
-        self._started = True
-
-        self._thr.start()
-        logging.info("TimerQueue started.")
-
-    def stop(self):
-        """Stop the timer queue."""
-
-        if not self._started:
-            return
-        self._started = True
-
-        self._wakeup(TEARDOWN_SENTINEL)
-        self._thr.join()
-
-    def add_timer(
-        self, callback: Callable, when: int, interval: int, ident: int = None
-    ) -> Timer:
-        """Add timer to the queue.
-
-        Arguments:
-            callback: Arbitrary callable object.
-            when: The first expiration time, seconds since epoch.
-            interval: Timer interval, if equals 0, one time timer, otherwise
-                the timer will be periodically executed
-            ident: (optional) Timer identity.
-
-        Returns:
-            A timer object which should not be manipulated directly by
-                clients. Used to delete/update the timer.
-        """
-
-        with self._lock:
-            timer = self._timers.add_timer(callback, when, interval, ident)
-        self._wakeup()
-        return timer
-
-    def remove_timer(self, timer: Timer):
-        """Remove timer from the queue.
-
-        Arguments:
-            timer: Timer object to remove.
-        """
-
-        with self._lock:
-            self._timers.remove_timer(timer)
-
-    def _check_and_execute(self):
-        wakeup_queue = self._wakeup_queue
-        while 1:
-            (next_expired_time, expired_timers) = self._get_expired_timers()
-            for timer in expired_timers:
-                try:
-                    # Note, please make timer callback effective/short
-                    timer()
-                except Exception:
-                    logging.error(traceback.format_exc())
-
-            self._reset_timers(expired_timers)
-
-            sleep_time = _calc_sleep_time(next_expired_time)
-            try:
-                wakeup = wakeup_queue.get(timeout=sleep_time)
-                if wakeup is TEARDOWN_SENTINEL:
-                    break
-            except Queue.Empty:
-                pass
-        logging.info("TimerQueue stopped.")
-
-    def _get_expired_timers(self):
-        with self._lock:
-            return self._timers.get_expired_timers()
-
-    def _reset_timers(self, expired_timers):
-        with self._lock:
-            has_new_timer = self._timers.reset_timers(expired_timers)
-
-        if has_new_timer:
-            self._wakeup()
-
-    def _wakeup(self, something="not_None"):
-        self._wakeup_queue.put(something)
-
-
-def _calc_sleep_time(next_expired_time):
-    if next_expired_time:
-        now = time()
-        if now < next_expired_time:
-            sleep_time = next_expired_time - now
-        else:
-            sleep_time = 0.1
-    else:
-        sleep_time = 1
-    return sleep_time
diff --git a/apps/bitwarden_event_logs/lib/solnlib/user_access.py b/apps/bitwarden_event_logs/lib/solnlib/user_access.py
deleted file mode 100755
index 29b3d493..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/user_access.py
+++ /dev/null
@@ -1,949 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""Splunk user access control related utilities."""
-
-import json
-from typing import List, Optional
-
-from splunklib import binding
-
-from solnlib import _utils
-from solnlib import splunk_rest_client as rest_client
-from solnlib import utils
-
-__all__ = [
-    "ObjectACLException",
-    "ObjectACL",
-    "ObjectACLManagerException",
-    "ObjectACLManager",
-    "AppCapabilityManagerException",
-    "AppCapabilityManager",
-    "UserAccessException",
-    "check_user_access",
-    "InvalidSessionKeyException",
-    "get_current_username",
-    "UserNotExistException",
-    "get_user_capabilities",
-    "user_is_capable",
-    "get_user_roles",
-]
-
-
-class ObjectACLException(Exception):
-    pass
-
-
-class ObjectACL:
-    """Object ACL record.
-
-    Examples:
-       >>> from solnlib import user_access
-       >>> obj_acl = user_access.ObjectACL(
-       >>>    'test_collection',
-       >>>    '9defa6f510d711e6be16a45e60e34295',
-       >>>    'test_object',
-       >>>    'Splunk_TA_test',
-       >>>    'admin',
-       >>>    {'read': ['*'], 'write': ['admin'], 'delete': ['admin']},
-       >>>    False)
-    """
-
-    OBJ_COLLECTION_KEY = "obj_collection"
-    OBJ_ID_KEY = "obj_id"
-    OBJ_TYPE_KEY = "obj_type"
-    OBJ_APP_KEY = "obj_app"
-    OBJ_OWNER_KEY = "obj_owner"
-    OBJ_PERMS_KEY = "obj_perms"
-    OBJ_PERMS_READ_KEY = "read"
-    OBJ_PERMS_WRITE_KEY = "write"
-    OBJ_PERMS_DELETE_KEY = "delete"
-    OBJ_PERMS_ALLOW_ALL = "*"
-    OBJ_SHARED_BY_INCLUSION_KEY = "obj_shared_by_inclusion"
-
-    def __init__(
-        self,
-        obj_collection: str,
-        obj_id: str,
-        obj_type: str,
-        obj_app: str,
-        obj_owner: str,
-        obj_perms: dict,
-        obj_shared_by_inclusion: bool,
-    ):
-        """Initializes ObjectACL.
-
-        Arguments:
-            obj_collection: Collection where object currently stored.
-            obj_id: ID of this object.
-            obj_type: Type of this object.
-            obj_app: App of this object.
-            obj_owner: Owner of this object.
-            obj_perms: Object perms, like: {'read': ['*'], 'write': ['admin'], 'delete': ['admin']}.
-            obj_shared_by_inclusion: Flag of object is shared by inclusion.
-        """
-        self.obj_collection = obj_collection
-        self.obj_id = obj_id
-        self.obj_type = obj_type
-        self.obj_app = obj_app
-        self.obj_owner = obj_owner
-        self._check_perms(obj_perms)
-        self._obj_perms = obj_perms
-        self.obj_shared_by_inclusion = obj_shared_by_inclusion
-
-    @classmethod
-    def _check_perms(cls, obj_perms):
-        if not isinstance(obj_perms, dict):
-            raise ObjectACLException(
-                "Invalid object acl perms type: %s, should be a dict." % type(obj_perms)
-            )
-
-        if not (
-            cls.OBJ_PERMS_READ_KEY in obj_perms
-            and cls.OBJ_PERMS_WRITE_KEY in obj_perms
-            and cls.OBJ_PERMS_DELETE_KEY in obj_perms
-        ):
-            raise ObjectACLException(
-                "Invalid object acl perms: %s, "
-                "should include read, write and delete perms." % obj_perms
-            )
-
-    @property
-    def obj_perms(self):
-        return self._obj_perms
-
-    @obj_perms.setter
-    def obj_perms(self, obj_perms):
-        self._check_perms(obj_perms)
-        self._obj_perms = obj_perms
-
-    @property
-    def record(self) -> dict:
-        """Get object acl record.
-
-        Returns: Object acl record, like:
-
-            {
-                '_key': 'test_collection-1234',
-                'obj_collection': 'test_collection',
-                'obj_id': '1234',
-                'obj_type': 'test_object',
-                'obj_app': 'Splunk_TA_test',
-                'obj_owner': 'admin',
-                'obj_perms': {'read': ['*'], 'write': ['admin'], 'delete': ['admin']},
-                'obj_shared_by_inclusion': True
-            }
-        """
-
-        return {
-            "_key": self.generate_key(self.obj_collection, self.obj_id),
-            self.OBJ_COLLECTION_KEY: self.obj_collection,
-            self.OBJ_ID_KEY: self.obj_id,
-            self.OBJ_TYPE_KEY: self.obj_type,
-            self.OBJ_APP_KEY: self.obj_app,
-            self.OBJ_OWNER_KEY: self.obj_owner,
-            self.OBJ_PERMS_KEY: self._obj_perms,
-            self.OBJ_SHARED_BY_INCLUSION_KEY: self.obj_shared_by_inclusion,
-        }
-
-    @staticmethod
-    def generate_key(obj_collection: str, obj_id: str) -> str:
-        """Generate object acl record key.
-
-        Arguments:
-            obj_collection: Collection where object currently stored.
-            obj_id: ID of this object.
-
-        Returns:
-            Object acl record key.
-        """
-
-        return "{obj_collection}_{obj_id}".format(
-            obj_collection=obj_collection, obj_id=obj_id
-        )
-
-    @staticmethod
-    def parse(obj_acl_record: dict) -> "ObjectACL":
-        """Parse object acl record and construct a new `ObjectACL` object from
-        it.
-
-        Arguments:
-            obj_acl_record: Object acl record.
-
-        Returns:
-            New `ObjectACL` object.
-        """
-
-        return ObjectACL(
-            obj_acl_record[ObjectACL.OBJ_COLLECTION_KEY],
-            obj_acl_record[ObjectACL.OBJ_ID_KEY],
-            obj_acl_record[ObjectACL.OBJ_TYPE_KEY],
-            obj_acl_record[ObjectACL.OBJ_APP_KEY],
-            obj_acl_record[ObjectACL.OBJ_OWNER_KEY],
-            obj_acl_record[ObjectACL.OBJ_PERMS_KEY],
-            obj_acl_record[ObjectACL.OBJ_SHARED_BY_INCLUSION_KEY],
-        )
-
-    def merge(self, obj_acl: "ObjectACL"):
-        """Merge current object perms with perms of `obj_acl`.
-
-        Arguments:
-            obj_acl: Object acl to merge.
-        """
-
-        for perm_key in self._obj_perms:
-            self._obj_perms[perm_key] = list(
-                set.union(
-                    set(self._obj_perms[perm_key]), set(obj_acl._obj_perms[perm_key])
-                )
-            )
-            if self.OBJ_PERMS_ALLOW_ALL in self._obj_perms[perm_key]:
-                self._obj_perms[perm_key] = [self.OBJ_PERMS_ALLOW_ALL]
-
-    def __str__(self):
-        return json.dumps(self.record)
-
-
-class ObjectACLManagerException(Exception):
-    """Exception for ObjectACLManager."""
-
-    pass
-
-
-class ObjectACLNotExistException(Exception):
-    """Exception for the situation when ACL does not exist."""
-
-    pass
-
-
-class ObjectACLManager:
-    """Object ACL manager.
-
-    Examples:
-       >>> from solnlib import user_access
-       >>> oaclm = user_access.ObjectACLManager(session_key,
-                                                'Splunk_TA_test')
-    """
-
-    def __init__(
-        self,
-        collection_name: str,
-        session_key: str,
-        app: str,
-        owner: Optional[str] = "nobody",
-        scheme: Optional[str] = None,
-        host: Optional[str] = None,
-        port: Optional[int] = None,
-        **context: dict,
-    ):
-        """Initializes ObjectACLManager.
-
-        Arguments:
-            collection_name: Collection name to store object ACL info.
-            session_key: Splunk access token.
-            app: App name of namespace.
-            owner: (optional) Owner of namespace, default is `nobody`.
-            scheme: (optional) The access scheme, default is None.
-            host: (optional) The host name, default is None.
-            port: (optional) The port number, default is None.
-            context: Other configurations for Splunk rest client.
-
-        Raises:
-            ObjectACLManagerException: If init ObjectACLManager failed.
-        """
-        collection_name = "{app}_{collection_name}".format(
-            app=app, collection_name=collection_name
-        )
-        try:
-            self._collection_data = _utils.get_collection_data(
-                collection_name,
-                session_key,
-                app,
-                owner,
-                scheme,
-                host,
-                port,
-                None,
-                **context,
-            )
-        except KeyError:
-            raise ObjectACLManagerException(
-                f"Get object acl collection: {collection_name} fail."
-            )
-
-    @utils.retry(exceptions=[binding.HTTPError])
-    def update_acl(
-        self,
-        obj_collection: str,
-        obj_id: str,
-        obj_type: str,
-        obj_app: str,
-        obj_owner: str,
-        obj_perms: dict,
-        obj_shared_by_inclusion: bool = True,
-        replace_existing: bool = True,
-    ):
-        """Update acl info of object.
-
-        Construct a new object acl info first, if `replace_existing` is True
-        then replace existing acl info else merge new object acl info with the
-        old one and replace the old acl info with merged acl info.
-
-        Arguments:
-            obj_collection: Collection where object currently stored.
-            obj_id: ID of this object.
-            obj_type: Type of this object.
-            obj_app: App of this object.
-            obj_owner: Owner of this object.
-            obj_perms: Object perms, like:
-
-                {
-                    'read': ['*'],
-                    'write': ['admin'],
-                    'delete': ['admin']
-                }.
-            obj_shared_by_inclusion: (optional) Flag of object is shared by
-                inclusion, default is True.
-            replace_existing: (optional) Replace existing acl info flag, True
-                indicates replace old acl info with new one else merge with old
-                acl info, default is True.
-        """
-
-        obj_acl = ObjectACL(
-            obj_collection,
-            obj_id,
-            obj_type,
-            obj_app,
-            obj_owner,
-            obj_perms,
-            obj_shared_by_inclusion,
-        )
-
-        if not replace_existing:
-            try:
-                old_obj_acl = self.get_acl(obj_collection, obj_id)
-            except ObjectACLNotExistException:
-                old_obj_acl = None
-
-            if old_obj_acl:
-                obj_acl.merge(old_obj_acl)
-
-        self._collection_data.batch_save(obj_acl.record)
-
-    @utils.retry(exceptions=[binding.HTTPError])
-    def update_acls(
-        self,
-        obj_collection: str,
-        obj_ids: List[str],
-        obj_type: str,
-        obj_app: str,
-        obj_owner: str,
-        obj_perms: dict,
-        obj_shared_by_inclusion: bool = True,
-        replace_existing: bool = True,
-    ):
-        """Batch update object acl info to all provided `obj_ids`.
-
-        Arguments:
-            obj_collection: Collection where objects currently stored.
-            obj_ids: IDs list of objects.
-            obj_type: Type of this object.
-            obj_app: App of this object.
-            obj_owner: Owner of this object.
-            obj_perms: Object perms, like:
-
-                {
-                    'read': ['*'],
-                    'write': ['admin'],
-                    'delete': ['admin']
-                }.
-            obj_shared_by_inclusion: (optional) Flag of object is shared by
-                inclusion, default is True.
-            replace_existing: (optional) Replace existing acl info flag, True
-                indicates replace old acl info with new one else merge with old acl
-                info, default is True.
-        """
-
-        obj_acl_records = []
-        for obj_id in obj_ids:
-            obj_acl = ObjectACL(
-                obj_collection,
-                obj_id,
-                obj_type,
-                obj_app,
-                obj_owner,
-                obj_perms,
-                obj_shared_by_inclusion,
-            )
-
-            if not replace_existing:
-                try:
-                    old_obj_acl = self.get_acl(obj_collection, obj_id)
-                except ObjectACLNotExistException:
-                    old_obj_acl = None
-
-                if old_obj_acl:
-                    obj_acl.merge(old_obj_acl)
-
-            obj_acl_records.append(obj_acl.record)
-
-        self._collection_data.batch_save(*obj_acl_records)
-
-    @utils.retry(exceptions=[binding.HTTPError])
-    def get_acl(self, obj_collection: str, obj_id: str) -> "ObjectACL":
-        """Get acl info.
-
-        Query object acl info with parameter of the combination of
-        `obj_collection` and `obj_id` from `self.collection_name` and
-        return it.
-
-        Arguments:
-            obj_collection: Collection where object currently stored.
-            obj_id: ID of this object.
-
-        Returns:
-            Object acl info if success else None.
-
-        Raises:
-            ObjectACLNotExistException: If object ACL info does not exist.
-        """
-
-        key = ObjectACL.generate_key(obj_collection, obj_id)
-        try:
-            obj_acl = self._collection_data.query_by_id(key)
-        except binding.HTTPError as e:
-            if e.status != 404:
-                raise
-
-            raise ObjectACLNotExistException(
-                "Object ACL info of {}_{} does not exist.".format(
-                    obj_collection, obj_id
-                )
-            )
-
-        return ObjectACL.parse(obj_acl)
-
-    @utils.retry(exceptions=[binding.HTTPError])
-    def get_acls(self, obj_collection: str, obj_ids: List[str]) -> List[ObjectACL]:
-        """Batch get acl info.
-
-        Query objects acl info with parameter of the combination of
-        `obj_collection` and `obj_ids` from KVStore and return them.
-
-        Arguments:
-            obj_collection: Collection where object currently stored.
-            obj_ids: IDs of objects.
-
-        Returns:
-            List of `ObjectACL` instances.
-        """
-
-        query = json.dumps(
-            {
-                "$or": [
-                    {"_key": ObjectACL.generate_key(obj_collection, obj_id)}
-                    for obj_id in obj_ids
-                ]
-            }
-        )
-        obj_acls = self._collection_data.query(query=query)
-
-        return [ObjectACL.parse(obj_acl) for obj_acl in obj_acls]
-
-    @utils.retry(exceptions=[binding.HTTPError])
-    def delete_acl(self, obj_collection: str, obj_id: str):
-        """Delete acl info.
-
-        Query object acl info with parameter of the combination of
-        `obj_collection` and `obj_ids` from KVStore and delete it.
-
-        Arguments:
-            obj_collection: Collection where object currently stored.
-            obj_id: ID of this object.
-
-        Raises:
-            ObjectACLNotExistException: If object ACL info does not exist.
-        """
-
-        key = ObjectACL.generate_key(obj_collection, obj_id)
-        try:
-            self._collection_data.delete_by_id(key)
-        except binding.HTTPError as e:
-            if e.status != 404:
-                raise
-
-            raise ObjectACLNotExistException(
-                "Object ACL info of {}_{} does not exist.".format(
-                    obj_collection, obj_id
-                )
-            )
-
-    @utils.retry(exceptions=[binding.HTTPError])
-    def delete_acls(self, obj_collection: str, obj_ids: List[str]):
-        """Batch delete acl info.
-
-        Query objects acl info with parameter of the combination of
-        `obj_collection` and `obj_ids` from KVStore and delete them.
-
-        Arguments:
-            obj_collection: Collection where object currently stored.
-            obj_ids: IDs of objects.
-        """
-
-        query = json.dumps(
-            {
-                "$or": [
-                    {"_key": ObjectACL.generate_key(obj_collection, obj_id)}
-                    for obj_id in obj_ids
-                ]
-            }
-        )
-        self._collection_data.delete(query=query)
-
-    @utils.retry(exceptions=[binding.HTTPError])
-    def get_accessible_object_ids(
-        self, user: str, operation: str, obj_collection: str, obj_ids: List[str]
-    ) -> List[str]:
-        """Get accessible IDs of objects from `obj_acls`.
-
-        Arguments:
-            user: User name of current `operation`.
-            operation: User operation, possible option: (read/write/delete).
-            obj_collection: Collection where object currently stored.
-            obj_ids: IDs of objects.
-
-        Returns:
-            List of IDs of accessible objects.
-        """
-
-        obj_acls = self.get_acls(obj_collection, obj_ids)
-        accessible_obj_ids = []
-        for obj_acl in obj_acls:
-            perms = obj_acl.obj_perms[operation]
-            if ObjectACL.OBJ_PERMS_ALLOW_ALL in perms or user in perms:
-                accessible_obj_ids.append(obj_acl.obj_id)
-
-        return accessible_obj_ids
-
-
-class AppCapabilityManagerException(Exception):
-    """Exception for AppCapabilityManager."""
-
-    pass
-
-
-class AppCapabilityNotExistException(Exception):
-    """Exception for the situation when AppCapability does not exist for a
-    specific app."""
-
-    pass
-
-
-class AppCapabilityManager:
-    """App capability manager.
-
-    Examples:
-       >>> from solnlib import user_access
-       >>> acm = user_access.AppCapabilityManager('test_collection',
-                                                  session_key,
-                                                  'Splunk_TA_test')
-       >>> acm.register_capabilities(...)
-       >>> acm.unregister_capabilities(...)
-    """
-
-    def __init__(
-        self,
-        collection_name: str,
-        session_key: str,
-        app: str,
-        owner: str = "nobody",
-        scheme: str = None,
-        host: str = None,
-        port: int = None,
-        **context: dict,
-    ):
-        """Initializes AppCapabilityManager.
-
-        Arguments:
-            collection_name: Collection name to store capabilities.
-            session_key: Splunk access token.
-            app: App name of namespace.
-            owner: (optional) Owner of namespace, default is `nobody`.
-            scheme: (optional) The access scheme, default is None.
-            host: (optional) The host name, default is None.
-            port: (optional) The port number, default is None.
-            context: Other configurations for Splunk rest client.
-
-        Raises:
-            AppCapabilityManagerException: If init AppCapabilityManager failed.
-        """
-        self._app = app
-
-        collection_name = f"{app}_{collection_name}"
-        try:
-            self._collection_data = _utils.get_collection_data(
-                collection_name,
-                session_key,
-                app,
-                owner,
-                scheme,
-                host,
-                port,
-                None,
-                **context,
-            )
-        except KeyError:
-            raise AppCapabilityManagerException(
-                f"Get app capabilities collection: {collection_name} failed."
-            )
-
-    @utils.retry(exceptions=[binding.HTTPError])
-    def register_capabilities(self, capabilities: dict):
-        """Register app capabilities.
-
-        Arguments:
-            capabilities: App capabilities, example:
-
-                {
-                    'object_type1': {
-                        'read': 'read_app_object_type1',
-                        'write': 'write_app_object_type1',
-                        'delete': 'delete_app_object_type1'},
-                        'object_type2': {
-                        'read': 'read_app_object_type2',
-                        'write': 'write_app_object_type2',
-                        'delete': 'delete_app_object_type2'
-                    },
-                    ...
-                }
-        """
-
-        record = {"_key": self._app, "capabilities": capabilities}
-        self._collection_data.batch_save(record)
-
-    @utils.retry(exceptions=[binding.HTTPError])
-    def unregister_capabilities(self):
-        """Unregister app capabilities.
-
-        Raises:
-            AppCapabilityNotExistException: If app capabilities are not registered.
-        """
-
-        try:
-            self._collection_data.delete_by_id(self._app)
-        except binding.HTTPError as e:
-            if e.status != 404:
-                raise
-
-            raise AppCapabilityNotExistException(
-                "App capabilities for %s have not been registered." % self._app
-            )
-
-    @utils.retry(exceptions=[binding.HTTPError])
-    def capabilities_are_registered(self) -> bool:
-        """Check if app capabilities are registered.
-
-        Returns:
-            True if app capabilities are registered else False.
-        """
-
-        try:
-            self._collection_data.query_by_id(self._app)
-        except binding.HTTPError as e:
-            if e.status != 404:
-                raise
-
-            return False
-
-        return True
-
-    @utils.retry(exceptions=[binding.HTTPError])
-    def get_capabilities(self) -> dict:
-        """Get app capabilities.
-
-        Returns:
-            App capabilities.
-
-        Raises:
-             AppCapabilityNotExistException: If app capabilities are not registered.
-        """
-
-        try:
-            record = self._collection_data.query_by_id(self._app)
-        except binding.HTTPError as e:
-            if e.status != 404:
-                raise
-
-            raise AppCapabilityNotExistException(
-                "App capabilities for %s have not been registered." % self._app
-            )
-
-        return record["capabilities"]
-
-
-class UserAccessException(Exception):
-    """Exception for the situation when there is user access exception."""
-
-    pass
-
-
-def check_user_access(
-    session_key: str,
-    capabilities: dict,
-    obj_type: str,
-    operation: str,
-    scheme: str = None,
-    host: str = None,
-    port: int = None,
-    **context: dict,
-):
-    """User access checker.
-
-    It will fetch user capabilities from given `session_key` and check if
-    the capability extracted from `capabilities`, `obj_type` and `operation`
-    is contained, if user capabilities include the extracted capability user
-    access is ok else fail.
-
-    Arguments:
-        session_key: Splunk access token.
-        capabilities: App capabilities, example:
-
-            {
-                'object_type1': {
-                    'read': 'read_app_object_type1',
-                    'write': 'write_app_object_type1',
-                    'delete': 'delete_app_object_type1'},
-                    'object_type2': {
-                    'read': 'read_app_object_type2',
-                    'write': 'write_app_object_type2',
-                    'delete': 'delete_app_object_type2'
-                },
-                ...
-            }
-        obj_type: Object type.
-        operation: User operation, possible option: (read/write/delete).
-        scheme: (optional) The access scheme, default is None.
-        host: (optional) The host name, default is None.
-        port: (optional) The port number, default is None.
-        context: Other configurations for Splunk rest client.
-
-    Raises:
-        UserAccessException: If user access permission is denied.
-
-    Examples:
-       >>> from solnlib.user_access import check_user_access
-       >>> def fun():
-       >>>     check_user_access(
-       >>>         session_key, capabilities, 'test_object', 'read')
-       >>>     ...
-    """
-
-    username = get_current_username(
-        session_key, scheme=scheme, host=host, port=port, **context
-    )
-    capability = capabilities[obj_type][operation]
-    if not user_is_capable(
-        session_key,
-        username,
-        capability,
-        scheme=scheme,
-        host=host,
-        port=port,
-        **context,
-    ):
-        raise UserAccessException(
-            "Permission denied, %s does not have the capability: %s."
-            % (username, capability)
-        )
-
-
-class InvalidSessionKeyException(Exception):
-    """Exception when Splunk session key is invalid."""
-
-    pass
-
-
-@utils.retry(exceptions=[binding.HTTPError])
-def get_current_username(
-    session_key: str,
-    scheme: str = None,
-    host: str = None,
-    port: int = None,
-    **context: dict,
-) -> str:
-    """Get current user name from `session_key`.
-
-    Arguments:
-        session_key: Splunk access token.
-        scheme: (optional) The access scheme, default is None.
-        host: (optional) The host name, default is None.
-        port: (optional) The port number, default is None.
-        context: Other configurations for Splunk rest client.
-
-    Returns:
-        Current user name.
-
-    Raises:
-        InvalidSessionKeyException: If `session_key` is invalid.
-
-    Examples:
-       >>> from solnlib import user_access
-       >>> user_name = user_access.get_current_username(session_key)
-    """
-
-    _rest_client = rest_client.SplunkRestClient(
-        session_key, "-", scheme=scheme, host=host, port=port, **context
-    )
-    try:
-        response = _rest_client.get(
-            "/services/authentication/current-context", output_mode="json"
-        ).body.read()
-    except binding.HTTPError as e:
-        if e.status != 401:
-            raise
-
-        raise InvalidSessionKeyException("Invalid session key.")
-
-    return json.loads(response)["entry"][0]["content"]["username"]
-
-
-class UserNotExistException(Exception):
-    """Exception when user does not exist."""
-
-    pass
-
-
-@utils.retry(exceptions=[binding.HTTPError])
-def get_user_capabilities(
-    session_key: str,
-    username: str,
-    scheme: str = None,
-    host: str = None,
-    port: int = None,
-    **context: dict,
-) -> List[dict]:
-    """Get user capabilities.
-
-    Arguments:
-        session_key: Splunk access token.
-        scheme: (optional) The access scheme, default is None.
-        host: (optional) The host name, default is None.
-        port: (optional) The port number, default is None.
-        context: Other configurations for Splunk rest client.
-
-    Returns:
-        User capabilities.
-
-    Raises:
-        UserNotExistException: If `username` does not exist.
-
-    Examples:
-       >>> from solnlib import user_access
-       >>> user_capabilities = user_access.get_user_capabilities(
-       >>>     session_key, 'test_user')
-    """
-
-    _rest_client = rest_client.SplunkRestClient(
-        session_key, "-", scheme=scheme, host=host, port=port, **context
-    )
-    url = f"/services/authentication/users/{username}"
-    try:
-        response = _rest_client.get(url, output_mode="json").body.read()
-    except binding.HTTPError as e:
-        if e.status != 404:
-            raise
-
-        raise UserNotExistException("User: %s does not exist." % username)
-
-    return json.loads(response)["entry"][0]["content"]["capabilities"]
-
-
-def user_is_capable(
-    session_key: str,
-    username: str,
-    capability: str,
-    scheme: str = None,
-    host: str = None,
-    port: int = None,
-    **context: dict,
-) -> bool:
-    """Check if user is capable for given `capability`.
-
-    Arguments:
-        session_key: Splunk access token.
-        username: (optional) User name of roles to get.
-        capability: The capability we wish to check for.
-        scheme: (optional) The access scheme, default is None.
-        host: (optional) The host name, default is None.
-        port: (optional) The port number, default is None.
-        context: Other configurations for Splunk rest client.
-
-    Returns:
-        True if user is capable else False.
-
-    Raises:
-        UserNotExistException: If `username` does not exist.
-
-    Examples:
-       >>> from solnlib import user_access
-       >>> is_capable = user_access.user_is_capable(
-       >>>     session_key, 'test_user', 'object_read_capability')
-    """
-
-    capabilities = get_user_capabilities(
-        session_key, username, scheme=scheme, host=host, port=port, **context
-    )
-    return capability in capabilities
-
-
-@utils.retry(exceptions=[binding.HTTPError])
-def get_user_roles(
-    session_key: str, username: str, scheme=None, host=None, port=None, **context
-) -> List:
-    """Get user roles.
-
-    Arguments:
-        session_key: Splunk access token.
-        username: (optional) User name of roles to get.
-        scheme: (optional) The access scheme, default is None.
-        host: (optional) The host name, default is None.
-        port: (optional) The port number, default is None.
-        context: Other configurations for Splunk rest client.
-
-    Returns:
-        User roles.
-
-    Raises:
-        UserNotExistException: If `username` does not exist.
-
-    Examples:
-       >>> from solnlib import user_access
-       >>> user_roles = user_access.get_user_roles(session_key, 'test_user')
-    """
-
-    _rest_client = rest_client.SplunkRestClient(
-        session_key, "-", scheme=scheme, host=host, port=port, **context
-    )
-    url = f"/services/authentication/users/{username}"
-    try:
-        response = _rest_client.get(url, output_mode="json").body.read()
-    except binding.HTTPError as e:
-        if e.status != 404:
-            raise
-
-        raise UserNotExistException("User: %s does not exist." % username)
-
-    return json.loads(response)["entry"][0]["content"]["roles"]
diff --git a/apps/bitwarden_event_logs/lib/solnlib/utils.py b/apps/bitwarden_event_logs/lib/solnlib/utils.py
deleted file mode 100755
index b7954208..00000000
--- a/apps/bitwarden_event_logs/lib/solnlib/utils.py
+++ /dev/null
@@ -1,221 +0,0 @@
-#
-# Copyright 2025 Splunk Inc.
-#
-# 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.
-#
-
-"""Common utilities."""
-
-import datetime
-import logging
-import os
-import signal
-import time
-import traceback
-from functools import wraps
-from typing import Any, Callable, List, Tuple, Union
-from urllib import parse as urlparse
-
-__all__ = [
-    "handle_teardown_signals",
-    "datetime_to_seconds",
-    "is_true",
-    "is_false",
-    "retry",
-    "extract_http_scheme_host_port",
-    "remove_http_proxy_env_vars",
-]
-
-
-def remove_http_proxy_env_vars() -> None:
-    """Removes HTTP(s) proxies from environment variables.
-
-    Removes the following environment variables:
-        * http_proxy
-        * https_proxy
-        * HTTP_PROXY
-        * HTTPS_PROXY
-
-    This function can be used in Splunk modular inputs code before starting the
-    ingestion to ensure that no proxy is going to be used when doing requests.
-    In case of proxy is needed, it can be defined in the modular inputs code.
-    """
-    env_vars_to_remove = (
-        "http_proxy",
-        "https_proxy",
-        "HTTP_PROXY",
-        "HTTPS_PROXY",
-    )
-    for env_var in env_vars_to_remove:
-        if env_var in os.environ:
-            del os.environ[env_var]
-
-
-def handle_teardown_signals(callback: Callable):
-    """Register handler for SIGTERM/SIGINT/SIGBREAK signal.
-
-    Catch SIGTERM/SIGINT/SIGBREAK signals, and invoke callback
-    Note: this should be called in main thread since Python only catches
-    signals in main thread.
-
-    Arguments:
-        callback: Callback for tear down signals.
-    """
-
-    signal.signal(signal.SIGTERM, callback)
-    signal.signal(signal.SIGINT, callback)
-
-    if os.name == "nt":
-        signal.signal(signal.SIGBREAK, callback)
-
-
-def datetime_to_seconds(dt: datetime.datetime) -> float:
-    """Convert UTC datetime to seconds since epoch.
-
-    Arguments:
-        dt: Date time.
-
-    Returns:
-        Seconds since epoch.
-    """
-
-    epoch_time = datetime.datetime.utcfromtimestamp(0)
-    return (dt - epoch_time).total_seconds()
-
-
-def is_true(val: Union[str, int]) -> bool:
-    """Decide if `val` is true.
-
-    Arguments:
-        val: Value to check.
-
-    Returns:
-        True or False.
-    """
-
-    value = str(val).strip().upper()
-    if value in ("1", "TRUE", "T", "Y", "YES"):
-        return True
-    return False
-
-
-def is_false(val: Union[str, int]) -> bool:
-    """Decide if `val` is false.
-
-    Arguments:
-        val: Value to check.
-
-    Returns:
-        True or False.
-    """
-
-    value = str(val).strip().upper()
-    if value in ("0", "FALSE", "F", "N", "NO", "NONE", ""):
-        return True
-    return False
-
-
-def retry(
-    retries: int = 3,
-    reraise: bool = True,
-    default_return: Any = None,
-    exceptions: List = None,
-):
-    """A decorator to run function with max `retries` times if there is
-    exception.
-
-    Arguments:
-        retries: (optional) Max retries times, default is 3.
-        reraise: Whether exception should be reraised, default is True.
-        default_return: (optional) Default return value for function
-            run after max retries and reraise is False.
-        exceptions: (optional) List of exceptions that should retry.
-    """
-
-    max_tries = max(retries, 0) + 1
-
-    def do_retry(func):
-        @wraps(func)
-        def wrapper(*args, **kwargs):
-            last_ex = None
-            for i in range(max_tries):
-                try:
-                    return func(*args, **kwargs)
-                except Exception as e:
-                    logging.warning(
-                        "Run function: %s failed: %s.",
-                        func.__name__,
-                        traceback.format_exc(),
-                    )
-                    if not exceptions or any(
-                        isinstance(e, exception) for exception in exceptions
-                    ):
-                        last_ex = e
-                        if i < max_tries - 1:
-                            time.sleep(2**i)
-                    else:
-                        raise
-
-            if reraise:
-                raise last_ex
-            else:
-                return default_return
-
-        return wrapper
-
-    return do_retry
-
-
-def extract_http_scheme_host_port(http_url: str) -> Tuple:
-    """Extract scheme, host and port from a HTTP URL.
-
-    Arguments:
-        http_url: HTTP URL to extract.
-
-    Returns:
-        A tuple of scheme, host and port
-
-    Raises:
-        ValueError: If `http_url` is not in http(s)://hostname:port format.
-    """
-
-    http_info = urlparse.urlparse(http_url)
-    if not http_info.scheme or not http_info.hostname or not http_info.port:
-        raise ValueError(http_url + " is not in http(s)://hostname:port format")
-    return http_info.scheme, http_info.hostname, http_info.port
-
-
-def get_appname_from_path(absolute_path):
-    """Gets name of the app from its path.
-
-    Arguments:
-        absolute_path: path of app
-
-    Returns:
-    """
-    absolute_path = os.path.normpath(absolute_path)
-    parts = absolute_path.split(os.path.sep)
-    parts.reverse()
-    for key in ("apps", "peer-apps", "manager-apps"):
-        try:
-            idx = parts.index(key)
-        except ValueError:
-            continue
-        else:
-            try:
-                if parts[idx + 1] == "etc":
-                    return parts[idx - 1]
-            except IndexError:
-                pass
-            continue
-    return "-"
diff --git a/apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/INSTALLER b/apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/INSTALLER
deleted file mode 100755
index a1b589e3..00000000
--- a/apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/INSTALLER
+++ /dev/null
@@ -1 +0,0 @@
-pip
diff --git a/apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/LICENSE b/apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/LICENSE
deleted file mode 100755
index 668d8ecd..00000000
--- a/apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/LICENSE
+++ /dev/null
@@ -1,13 +0,0 @@
-Copyright 2014-2019 Grant Jenks
-
-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/apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/METADATA b/apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/METADATA
deleted file mode 100755
index dd28af42..00000000
--- a/apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/METADATA
+++ /dev/null
@@ -1,264 +0,0 @@
-Metadata-Version: 2.1
-Name: sortedcontainers
-Version: 2.4.0
-Summary: Sorted Containers -- Sorted List, Sorted Dict, Sorted Set
-Home-page: http://www.grantjenks.com/docs/sortedcontainers/
-Author: Grant Jenks
-Author-email: contact@grantjenks.com
-License: Apache 2.0
-Platform: UNKNOWN
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Natural Language :: English
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.2
-Classifier: Programming Language :: Python :: 3.3
-Classifier: Programming Language :: Python :: 3.4
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Programming Language :: Python :: Implementation :: PyPy
-
-Python Sorted Containers
-========================
-
-`Sorted Containers`_ is an Apache2 licensed `sorted collections library`_,
-written in pure-Python, and fast as C-extensions.
-
-Python's standard library is great until you need a sorted collections
-type. Many will attest that you can get really far without one, but the moment
-you **really need** a sorted list, sorted dict, or sorted set, you're faced
-with a dozen different implementations, most using C-extensions without great
-documentation and benchmarking.
-
-In Python, we can do better. And we can do it in pure-Python!
-
-.. code-block:: python
-
-    >>> from sortedcontainers import SortedList
-    >>> sl = SortedList(['e', 'a', 'c', 'd', 'b'])
-    >>> sl
-    SortedList(['a', 'b', 'c', 'd', 'e'])
-    >>> sl *= 10_000_000
-    >>> sl.count('c')
-    10000000
-    >>> sl[-3:]
-    ['e', 'e', 'e']
-    >>> from sortedcontainers import SortedDict
-    >>> sd = SortedDict({'c': 3, 'a': 1, 'b': 2})
-    >>> sd
-    SortedDict({'a': 1, 'b': 2, 'c': 3})
-    >>> sd.popitem(index=-1)
-    ('c', 3)
-    >>> from sortedcontainers import SortedSet
-    >>> ss = SortedSet('abracadabra')
-    >>> ss
-    SortedSet(['a', 'b', 'c', 'd', 'r'])
-    >>> ss.bisect_left('c')
-    2
-
-All of the operations shown above run in faster than linear time. The above
-demo also takes nearly a gigabyte of memory to run. When the sorted list is
-multiplied by ten million, it stores ten million references to each of "a"
-through "e". Each reference requires eight bytes in the sorted
-container. That's pretty hard to beat as it's the cost of a pointer to each
-object. It's also 66% less overhead than a typical binary tree implementation
-(e.g. Red-Black Tree, AVL-Tree, AA-Tree, Splay-Tree, Treap, etc.) for which
-every node must also store two pointers to children nodes.
-
-`Sorted Containers`_ takes all of the work out of Python sorted collections -
-making your deployment and use of Python easy. There's no need to install a C
-compiler or pre-build and distribute custom extensions. Performance is a
-feature and testing has 100% coverage with unit tests and hours of stress.
-
-.. _`Sorted Containers`: http://www.grantjenks.com/docs/sortedcontainers/
-.. _`sorted collections library`: http://www.grantjenks.com/docs/sortedcontainers/
-
-Testimonials
-------------
-
-**Alex Martelli**, `Fellow of the Python Software Foundation`_
-
-"Good stuff! ... I like the `simple, effective implementation`_ idea of
-splitting the sorted containers into smaller "fragments" to avoid the O(N)
-insertion costs."
-
-**Jeff Knupp**, `author of Writing Idiomatic Python and Python Trainer`_
-
-"That last part, "fast as C-extensions," was difficult to believe. I would need
-some sort of `Performance Comparison`_ to be convinced this is true. The author
-includes this in the docs. It is."
-
-**Kevin Samuel**, `Python and Django Trainer`_
-
-I'm quite amazed, not just by the code quality (it's incredibly readable and
-has more comment than code, wow), but the actual amount of work you put at
-stuff that is *not* code: documentation, benchmarking, implementation
-explanations. Even the git log is clean and the unit tests run out of the box
-on Python 2 and 3.
-
-**Mark Summerfield**, a short plea for `Python Sorted Collections`_
-
-Python's "batteries included" standard library seems to have a battery
-missing. And the argument that "we never had it before" has worn thin. It is
-time that Python offered a full range of collection classes out of the box,
-including sorted ones.
-
-`Sorted Containers`_ is used in popular open source projects such as:
-`Zipline`_, an algorithmic trading library from Quantopian; `Angr`_, a binary
-analysis platform from UC Santa Barbara; `Trio`_, an async I/O library; and
-`Dask Distributed`_, a distributed computation library supported by Continuum
-Analytics.
-
-.. _`Fellow of the Python Software Foundation`: https://en.wikipedia.org/wiki/Alex_Martelli
-.. _`simple, effective implementation`: http://www.grantjenks.com/docs/sortedcontainers/implementation.html
-.. _`author of Writing Idiomatic Python and Python Trainer`: https://jeffknupp.com/
-.. _`Python and Django Trainer`: https://www.elephorm.com/formateur/kevin-samuel
-.. _`Python Sorted Collections`: http://www.qtrac.eu/pysorted.html
-.. _`Zipline`: https://github.com/quantopian/zipline
-.. _`Angr`: https://github.com/angr/angr
-.. _`Trio`: https://github.com/python-trio/trio
-.. _`Dask Distributed`: https://github.com/dask/distributed
-
-Features
---------
-
-- Pure-Python
-- Fully documented
-- Benchmark comparison (alternatives, runtimes, load-factors)
-- 100% test coverage
-- Hours of stress testing
-- Performance matters (often faster than C implementations)
-- Compatible API (nearly identical to older blist and bintrees modules)
-- Feature-rich (e.g. get the five largest keys in a sorted dict: d.keys()[-5:])
-- Pragmatic design (e.g. SortedSet is a Python set with a SortedList index)
-- Developed on Python 3.7
-- Tested on CPython 2.7, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7 and PyPy, PyPy3
-
-.. image:: https://api.travis-ci.org/grantjenks/python-sortedcontainers.svg?branch=master
-   :target: http://www.grantjenks.com/docs/sortedcontainers/
-
-.. image:: https://ci.appveyor.com/api/projects/status/github/grantjenks/python-sortedcontainers?branch=master&svg=true
-   :target: http://www.grantjenks.com/docs/sortedcontainers/
-
-Quickstart
-----------
-
-Installing `Sorted Containers`_ is simple with `pip
-`_::
-
-    $ pip install sortedcontainers
-
-You can access documentation in the interpreter with Python's built-in `help`
-function. The `help` works on modules, classes and methods in `Sorted
-Containers`_.
-
-.. code-block:: python
-
-    >>> import sortedcontainers
-    >>> help(sortedcontainers)
-    >>> from sortedcontainers import SortedDict
-    >>> help(SortedDict)
-    >>> help(SortedDict.popitem)
-
-Documentation
--------------
-
-Complete documentation for `Sorted Containers`_ is available at
-http://www.grantjenks.com/docs/sortedcontainers/
-
-User Guide
-..........
-
-The user guide provides an introduction to `Sorted Containers`_ and extensive
-performance comparisons and analysis.
-
-- `Introduction`_
-- `Performance Comparison`_
-- `Load Factor Performance Comparison`_
-- `Runtime Performance Comparison`_
-- `Simulated Workload Performance Comparison`_
-- `Performance at Scale`_
-
-.. _`Introduction`: http://www.grantjenks.com/docs/sortedcontainers/introduction.html
-.. _`Performance Comparison`: http://www.grantjenks.com/docs/sortedcontainers/performance.html
-.. _`Load Factor Performance Comparison`: http://www.grantjenks.com/docs/sortedcontainers/performance-load.html
-.. _`Runtime Performance Comparison`: http://www.grantjenks.com/docs/sortedcontainers/performance-runtime.html
-.. _`Simulated Workload Performance Comparison`: http://www.grantjenks.com/docs/sortedcontainers/performance-workload.html
-.. _`Performance at Scale`: http://www.grantjenks.com/docs/sortedcontainers/performance-scale.html
-
-Community Guide
-...............
-
-The community guide provides information on the development of `Sorted
-Containers`_ along with support, implementation, and history details.
-
-- `Development and Support`_
-- `Implementation Details`_
-- `Release History`_
-
-.. _`Development and Support`: http://www.grantjenks.com/docs/sortedcontainers/development.html
-.. _`Implementation Details`: http://www.grantjenks.com/docs/sortedcontainers/implementation.html
-.. _`Release History`: http://www.grantjenks.com/docs/sortedcontainers/history.html
-
-API Documentation
-.................
-
-The API documentation provides information on specific functions, classes, and
-modules in the `Sorted Containers`_ package.
-
-- `Sorted List`_
-- `Sorted Dict`_
-- `Sorted Set`_
-
-.. _`Sorted List`: http://www.grantjenks.com/docs/sortedcontainers/sortedlist.html
-.. _`Sorted Dict`: http://www.grantjenks.com/docs/sortedcontainers/sorteddict.html
-.. _`Sorted Set`: http://www.grantjenks.com/docs/sortedcontainers/sortedset.html
-
-Talks
------
-
-- `Python Sorted Collections | PyCon 2016 Talk`_
-- `SF Python Holiday Party 2015 Lightning Talk`_
-- `DjangoCon 2015 Lightning Talk`_
-
-.. _`Python Sorted Collections | PyCon 2016 Talk`: http://www.grantjenks.com/docs/sortedcontainers/pycon-2016-talk.html
-.. _`SF Python Holiday Party 2015 Lightning Talk`: http://www.grantjenks.com/docs/sortedcontainers/sf-python-2015-lightning-talk.html
-.. _`DjangoCon 2015 Lightning Talk`: http://www.grantjenks.com/docs/sortedcontainers/djangocon-2015-lightning-talk.html
-
-Resources
----------
-
-- `Sorted Containers Documentation`_
-- `Sorted Containers at PyPI`_
-- `Sorted Containers at Github`_
-- `Sorted Containers Issue Tracker`_
-
-.. _`Sorted Containers Documentation`: http://www.grantjenks.com/docs/sortedcontainers/
-.. _`Sorted Containers at PyPI`: https://pypi.org/project/sortedcontainers/
-.. _`Sorted Containers at Github`: https://github.com/grantjenks/python-sortedcontainers
-.. _`Sorted Containers Issue Tracker`: https://github.com/grantjenks/python-sortedcontainers/issues
-
-Sorted Containers License
--------------------------
-
-Copyright 2014-2019 Grant Jenks
-
-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/apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/RECORD b/apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/RECORD
deleted file mode 100755
index 6e7b29d3..00000000
--- a/apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/RECORD
+++ /dev/null
@@ -1,11 +0,0 @@
-sortedcontainers-2.4.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-sortedcontainers-2.4.0.dist-info/LICENSE,sha256=HbfK5_zmRS4uYI5AGg-VPgEz5MLXXbafuK6FHSCG9bY,557
-sortedcontainers-2.4.0.dist-info/METADATA,sha256=7t8RUQniFQAekE2giChRvq5QsDwLAWGouedLfP1nlHM,10666
-sortedcontainers-2.4.0.dist-info/RECORD,,
-sortedcontainers-2.4.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-sortedcontainers-2.4.0.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110
-sortedcontainers-2.4.0.dist-info/top_level.txt,sha256=AVs0q-qf-10gs1tkVhfALdebJo4uPuVVBX7YT91riQU,17
-sortedcontainers/__init__.py,sha256=X_X5LuRjve77KwuJQrDUSu0T8559YcoowQbAnjuM7TE,2131
-sortedcontainers/sorteddict.py,sha256=za5LnENZMGPIn_JZEr0S0tDXfEsxJsfEnanh6lLlTZM,22712
-sortedcontainers/sortedlist.py,sha256=rwA1dM8ce09eb2DMjLOfHoikhCx_Rfk5nuE_NSsfr3I,76331
-sortedcontainers/sortedset.py,sha256=5RI2CUJGMm1ig3-dhbOrzDl8aPqDePkoJPQoq0ROS7M,19825
diff --git a/apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/REQUESTED b/apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/REQUESTED
deleted file mode 100755
index e69de29b..00000000
diff --git a/apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/WHEEL b/apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/WHEEL
deleted file mode 100755
index ef99c6cf..00000000
--- a/apps/bitwarden_event_logs/lib/sortedcontainers-2.4.0.dist-info/WHEEL
+++ /dev/null
@@ -1,6 +0,0 @@
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.34.2)
-Root-Is-Purelib: true
-Tag: py2-none-any
-Tag: py3-none-any
-
diff --git a/apps/bitwarden_event_logs/lib/sortedcontainers/__init__.py b/apps/bitwarden_event_logs/lib/sortedcontainers/__init__.py
deleted file mode 100755
index a141dd1d..00000000
--- a/apps/bitwarden_event_logs/lib/sortedcontainers/__init__.py
+++ /dev/null
@@ -1,74 +0,0 @@
-"""Sorted Containers -- Sorted List, Sorted Dict, Sorted Set
-
-Sorted Containers is an Apache2 licensed containers library, written in
-pure-Python, and fast as C-extensions.
-
-Python's standard library is great until you need a sorted collections
-type. Many will attest that you can get really far without one, but the moment
-you **really need** a sorted list, dict, or set, you're faced with a dozen
-different implementations, most using C-extensions without great documentation
-and benchmarking.
-
-In Python, we can do better. And we can do it in pure-Python!
-
-::
-
-    >>> from sortedcontainers import SortedList
-    >>> sl = SortedList(['e', 'a', 'c', 'd', 'b'])
-    >>> sl
-    SortedList(['a', 'b', 'c', 'd', 'e'])
-    >>> sl *= 1000000
-    >>> sl.count('c')
-    1000000
-    >>> sl[-3:]
-    ['e', 'e', 'e']
-    >>> from sortedcontainers import SortedDict
-    >>> sd = SortedDict({'c': 3, 'a': 1, 'b': 2})
-    >>> sd
-    SortedDict({'a': 1, 'b': 2, 'c': 3})
-    >>> sd.popitem(index=-1)
-    ('c', 3)
-    >>> from sortedcontainers import SortedSet
-    >>> ss = SortedSet('abracadabra')
-    >>> ss
-    SortedSet(['a', 'b', 'c', 'd', 'r'])
-    >>> ss.bisect_left('c')
-    2
-
-Sorted Containers takes all of the work out of Python sorted types - making
-your deployment and use of Python easy. There's no need to install a C compiler
-or pre-build and distribute custom extensions. Performance is a feature and
-testing has 100% coverage with unit tests and hours of stress.
-
-:copyright: (c) 2014-2019 by Grant Jenks.
-:license: Apache 2.0, see LICENSE for more details.
-
-"""
-
-
-from .sortedlist import SortedList, SortedKeyList, SortedListWithKey
-from .sortedset import SortedSet
-from .sorteddict import (
-    SortedDict,
-    SortedKeysView,
-    SortedItemsView,
-    SortedValuesView,
-)
-
-__all__ = [
-    'SortedList',
-    'SortedKeyList',
-    'SortedListWithKey',
-    'SortedDict',
-    'SortedKeysView',
-    'SortedItemsView',
-    'SortedValuesView',
-    'SortedSet',
-]
-
-__title__ = 'sortedcontainers'
-__version__ = '2.4.0'
-__build__ = 0x020400
-__author__ = 'Grant Jenks'
-__license__ = 'Apache 2.0'
-__copyright__ = '2014-2019, Grant Jenks'
diff --git a/apps/bitwarden_event_logs/lib/sortedcontainers/__pycache__/__init__.cpython-39.pyc b/apps/bitwarden_event_logs/lib/sortedcontainers/__pycache__/__init__.cpython-39.pyc
deleted file mode 100755
index 4bdaa9a5c3479ade6de5bf3645552835b0d1c76f..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 2266
zcma)7TW=dh6kaDzVsDZ*EfwnHK?pgHd`Z&2V9|&wR47oYqM#yeRW0M4u|3M{>}qDV
zse|}4zzcuLuRQS=c;cH~-$MJ)jdHSka_)1!Z`l5R7~uK*k3Z#QBMAQXAM4KsKEB4U
z{RM&tQNU|_L)FraXd~T>Hq&}kuX)|3+Df;h?ee{@cG5=FNSjeJ4WlsKjdp9nIN)2q
z*P=bX9qsd-=nij0cX=~9;9+!+??(6e-eu6S!hS<@fBZi!ZhTFi^Y3+~0R
z^`^b%QodGu=7Yhhv>)Df61Sb`=n0)1m(N?C?xVq*(qmp*Ss;Qh
zqkKfq9wtkB%a=WS{o$|3KA=Zs+XFf&`yJ5fZ)+{{(z$AR?!(}-w1rO@`B4Z^+Ylm|BAy#qhN`P1Z)ofHE?>aMARN`?oU*`qRJ+{GMlrq2Y9-D@^$W=D=
zuqer;u)@cwU_gS|X^(!I0Cpf2omITyGVDHB#)CLSWck#{@x%@2Flp22@yX*Z#FsRj(zk}8-S33Hg6UvR#Dh6A
z(`oOx11=Ku{NnuCi_2$(D#0))j0@ZX%7_1_hC8UeIh(V*N8fC}TFdXnwEVoc0_M-4
zF{s)@{EQOB6
zTYw-|+1M*}zt`*I0`32Qbp1G%SiU&!<;s7U@q^l4?cq*iw_aQP
F{0ByQw2uG)

diff --git a/apps/bitwarden_event_logs/lib/sortedcontainers/__pycache__/sorteddict.cpython-39.pyc b/apps/bitwarden_event_logs/lib/sortedcontainers/__pycache__/sorteddict.cpython-39.pyc
deleted file mode 100755
index cdc34559f55a4f69e819975cc957688ded018269..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 23129
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
VsXsZs^OrRBf1!AE_sQK~{2zSUWS;;4

diff --git a/apps/bitwarden_event_logs/lib/sortedcontainers/__pycache__/sortedlist.cpython-39.pyc b/apps/bitwarden_event_logs/lib/sortedcontainers/__pycache__/sortedlist.cpython-39.pyc
deleted file mode 100755
index 5dbe1a35bcc91ac14a794c3b34fd0707e7f98fa5..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 60403
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 is an Apache2 licensed Python sorted -collections library, written in pure-Python, and fast as C-extensions. The -:doc:`introduction` is the best way to get started. - -Sorted dict implementations: - -.. currentmodule:: sortedcontainers - -* :class:`SortedDict` -* :class:`SortedKeysView` -* :class:`SortedItemsView` -* :class:`SortedValuesView` - -""" - -import sys -import warnings - -from itertools import chain - -from .sortedlist import SortedList, recursive_repr -from .sortedset import SortedSet - -############################################################################### -# BEGIN Python 2/3 Shims -############################################################################### - -try: - from collections.abc import ( - ItemsView, KeysView, Mapping, ValuesView, Sequence - ) -except ImportError: - from collections import ItemsView, KeysView, Mapping, ValuesView, Sequence - -############################################################################### -# END Python 2/3 Shims -############################################################################### - - -class SortedDict(dict): - """Sorted dict is a sorted mutable mapping. - - Sorted dict keys are maintained in sorted order. The design of sorted dict - is simple: sorted dict inherits from dict to store items and maintains a - sorted list of keys. - - Sorted dict keys must be hashable and comparable. The hash and total - ordering of keys must not change while they are stored in the sorted dict. - - Mutable mapping methods: - - * :func:`SortedDict.__getitem__` (inherited from dict) - * :func:`SortedDict.__setitem__` - * :func:`SortedDict.__delitem__` - * :func:`SortedDict.__iter__` - * :func:`SortedDict.__len__` (inherited from dict) - - Methods for adding items: - - * :func:`SortedDict.setdefault` - * :func:`SortedDict.update` - - Methods for removing items: - - * :func:`SortedDict.clear` - * :func:`SortedDict.pop` - * :func:`SortedDict.popitem` - - Methods for looking up items: - - * :func:`SortedDict.__contains__` (inherited from dict) - * :func:`SortedDict.get` (inherited from dict) - * :func:`SortedDict.peekitem` - - Methods for views: - - * :func:`SortedDict.keys` - * :func:`SortedDict.items` - * :func:`SortedDict.values` - - Methods for miscellany: - - * :func:`SortedDict.copy` - * :func:`SortedDict.fromkeys` - * :func:`SortedDict.__reversed__` - * :func:`SortedDict.__eq__` (inherited from dict) - * :func:`SortedDict.__ne__` (inherited from dict) - * :func:`SortedDict.__repr__` - * :func:`SortedDict._check` - - Sorted list methods available (applies to keys): - - * :func:`SortedList.bisect_left` - * :func:`SortedList.bisect_right` - * :func:`SortedList.count` - * :func:`SortedList.index` - * :func:`SortedList.irange` - * :func:`SortedList.islice` - * :func:`SortedList._reset` - - Additional sorted list methods available, if key-function used: - - * :func:`SortedKeyList.bisect_key_left` - * :func:`SortedKeyList.bisect_key_right` - * :func:`SortedKeyList.irange_key` - - Sorted dicts may only be compared for equality and inequality. - - """ - def __init__(self, *args, **kwargs): - """Initialize sorted dict instance. - - Optional key-function argument defines a callable that, like the `key` - argument to the built-in `sorted` function, extracts a comparison key - from each dictionary key. If no function is specified, the default - compares the dictionary keys directly. The key-function argument must - be provided as a positional argument and must come before all other - arguments. - - Optional iterable argument provides an initial sequence of pairs to - initialize the sorted dict. Each pair in the sequence defines the key - and corresponding value. If a key is seen more than once, the last - value associated with it is stored in the new sorted dict. - - Optional mapping argument provides an initial mapping of items to - initialize the sorted dict. - - If keyword arguments are given, the keywords themselves, with their - associated values, are added as items to the dictionary. If a key is - specified both in the positional argument and as a keyword argument, - the value associated with the keyword is stored in the - sorted dict. - - Sorted dict keys must be hashable, per the requirement for Python's - dictionaries. Keys (or the result of the key-function) must also be - comparable, per the requirement for sorted lists. - - >>> d = {'alpha': 1, 'beta': 2} - >>> SortedDict([('alpha', 1), ('beta', 2)]) == d - True - >>> SortedDict({'alpha': 1, 'beta': 2}) == d - True - >>> SortedDict(alpha=1, beta=2) == d - True - - """ - if args and (args[0] is None or callable(args[0])): - _key = self._key = args[0] - args = args[1:] - else: - _key = self._key = None - - self._list = SortedList(key=_key) - - # Reaching through ``self._list`` repeatedly adds unnecessary overhead - # so cache references to sorted list methods. - - _list = self._list - self._list_add = _list.add - self._list_clear = _list.clear - self._list_iter = _list.__iter__ - self._list_reversed = _list.__reversed__ - self._list_pop = _list.pop - self._list_remove = _list.remove - self._list_update = _list.update - - # Expose some sorted list methods publicly. - - self.bisect_left = _list.bisect_left - self.bisect = _list.bisect_right - self.bisect_right = _list.bisect_right - self.index = _list.index - self.irange = _list.irange - self.islice = _list.islice - self._reset = _list._reset - - if _key is not None: - self.bisect_key_left = _list.bisect_key_left - self.bisect_key_right = _list.bisect_key_right - self.bisect_key = _list.bisect_key - self.irange_key = _list.irange_key - - self._update(*args, **kwargs) - - - @property - def key(self): - """Function used to extract comparison key from keys. - - Sorted dict compares keys directly when the key function is none. - - """ - return self._key - - - @property - def iloc(self): - """Cached reference of sorted keys view. - - Deprecated in version 2 of Sorted Containers. Use - :func:`SortedDict.keys` instead. - - """ - # pylint: disable=attribute-defined-outside-init - try: - return self._iloc - except AttributeError: - warnings.warn( - 'sorted_dict.iloc is deprecated.' - ' Use SortedDict.keys() instead.', - DeprecationWarning, - stacklevel=2, - ) - _iloc = self._iloc = SortedKeysView(self) - return _iloc - - - def clear(self): - - """Remove all items from sorted dict. - - Runtime complexity: `O(n)` - - """ - dict.clear(self) - self._list_clear() - - - def __delitem__(self, key): - """Remove item from sorted dict identified by `key`. - - ``sd.__delitem__(key)`` <==> ``del sd[key]`` - - Runtime complexity: `O(log(n))` -- approximate. - - >>> sd = SortedDict({'a': 1, 'b': 2, 'c': 3}) - >>> del sd['b'] - >>> sd - SortedDict({'a': 1, 'c': 3}) - >>> del sd['z'] - Traceback (most recent call last): - ... - KeyError: 'z' - - :param key: `key` for item lookup - :raises KeyError: if key not found - - """ - dict.__delitem__(self, key) - self._list_remove(key) - - - def __iter__(self): - """Return an iterator over the keys of the sorted dict. - - ``sd.__iter__()`` <==> ``iter(sd)`` - - Iterating the sorted dict while adding or deleting items may raise a - :exc:`RuntimeError` or fail to iterate over all keys. - - """ - return self._list_iter() - - - def __reversed__(self): - """Return a reverse iterator over the keys of the sorted dict. - - ``sd.__reversed__()`` <==> ``reversed(sd)`` - - Iterating the sorted dict while adding or deleting items may raise a - :exc:`RuntimeError` or fail to iterate over all keys. - - """ - return self._list_reversed() - - - def __setitem__(self, key, value): - """Store item in sorted dict with `key` and corresponding `value`. - - ``sd.__setitem__(key, value)`` <==> ``sd[key] = value`` - - Runtime complexity: `O(log(n))` -- approximate. - - >>> sd = SortedDict() - >>> sd['c'] = 3 - >>> sd['a'] = 1 - >>> sd['b'] = 2 - >>> sd - SortedDict({'a': 1, 'b': 2, 'c': 3}) - - :param key: key for item - :param value: value for item - - """ - if key not in self: - self._list_add(key) - dict.__setitem__(self, key, value) - - _setitem = __setitem__ - - - def __or__(self, other): - if not isinstance(other, Mapping): - return NotImplemented - items = chain(self.items(), other.items()) - return self.__class__(self._key, items) - - - def __ror__(self, other): - if not isinstance(other, Mapping): - return NotImplemented - items = chain(other.items(), self.items()) - return self.__class__(self._key, items) - - - def __ior__(self, other): - self._update(other) - return self - - - def copy(self): - """Return a shallow copy of the sorted dict. - - Runtime complexity: `O(n)` - - :return: new sorted dict - - """ - return self.__class__(self._key, self.items()) - - __copy__ = copy - - - @classmethod - def fromkeys(cls, iterable, value=None): - """Return a new sorted dict initailized from `iterable` and `value`. - - Items in the sorted dict have keys from `iterable` and values equal to - `value`. - - Runtime complexity: `O(n*log(n))` - - :return: new sorted dict - - """ - return cls((key, value) for key in iterable) - - - def keys(self): - """Return new sorted keys view of the sorted dict's keys. - - See :class:`SortedKeysView` for details. - - :return: new sorted keys view - - """ - return SortedKeysView(self) - - - def items(self): - """Return new sorted items view of the sorted dict's items. - - See :class:`SortedItemsView` for details. - - :return: new sorted items view - - """ - return SortedItemsView(self) - - - def values(self): - """Return new sorted values view of the sorted dict's values. - - See :class:`SortedValuesView` for details. - - :return: new sorted values view - - """ - return SortedValuesView(self) - - - if sys.hexversion < 0x03000000: - def __make_raise_attributeerror(original, alternate): - # pylint: disable=no-self-argument - message = ( - 'SortedDict.{original}() is not implemented.' - ' Use SortedDict.{alternate}() instead.' - ).format(original=original, alternate=alternate) - def method(self): - # pylint: disable=missing-docstring,unused-argument - raise AttributeError(message) - method.__name__ = original # pylint: disable=non-str-assignment-to-dunder-name - method.__doc__ = message - return property(method) - - iteritems = __make_raise_attributeerror('iteritems', 'items') - iterkeys = __make_raise_attributeerror('iterkeys', 'keys') - itervalues = __make_raise_attributeerror('itervalues', 'values') - viewitems = __make_raise_attributeerror('viewitems', 'items') - viewkeys = __make_raise_attributeerror('viewkeys', 'keys') - viewvalues = __make_raise_attributeerror('viewvalues', 'values') - - - class _NotGiven(object): - # pylint: disable=too-few-public-methods - def __repr__(self): - return '' - - __not_given = _NotGiven() - - def pop(self, key, default=__not_given): - """Remove and return value for item identified by `key`. - - If the `key` is not found then return `default` if given. If `default` - is not given then raise :exc:`KeyError`. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> sd = SortedDict({'a': 1, 'b': 2, 'c': 3}) - >>> sd.pop('c') - 3 - >>> sd.pop('z', 26) - 26 - >>> sd.pop('y') - Traceback (most recent call last): - ... - KeyError: 'y' - - :param key: `key` for item - :param default: `default` value if key not found (optional) - :return: value for item - :raises KeyError: if `key` not found and `default` not given - - """ - if key in self: - self._list_remove(key) - return dict.pop(self, key) - else: - if default is self.__not_given: - raise KeyError(key) - return default - - - def popitem(self, index=-1): - """Remove and return ``(key, value)`` pair at `index` from sorted dict. - - Optional argument `index` defaults to -1, the last item in the sorted - dict. Specify ``index=0`` for the first item in the sorted dict. - - If the sorted dict is empty, raises :exc:`KeyError`. - - If the `index` is out of range, raises :exc:`IndexError`. - - Runtime complexity: `O(log(n))` - - >>> sd = SortedDict({'a': 1, 'b': 2, 'c': 3}) - >>> sd.popitem() - ('c', 3) - >>> sd.popitem(0) - ('a', 1) - >>> sd.popitem(100) - Traceback (most recent call last): - ... - IndexError: list index out of range - - :param int index: `index` of item (default -1) - :return: key and value pair - :raises KeyError: if sorted dict is empty - :raises IndexError: if `index` out of range - - """ - if not self: - raise KeyError('popitem(): dictionary is empty') - - key = self._list_pop(index) - value = dict.pop(self, key) - return (key, value) - - - def peekitem(self, index=-1): - """Return ``(key, value)`` pair at `index` in sorted dict. - - Optional argument `index` defaults to -1, the last item in the sorted - dict. Specify ``index=0`` for the first item in the sorted dict. - - Unlike :func:`SortedDict.popitem`, the sorted dict is not modified. - - If the `index` is out of range, raises :exc:`IndexError`. - - Runtime complexity: `O(log(n))` - - >>> sd = SortedDict({'a': 1, 'b': 2, 'c': 3}) - >>> sd.peekitem() - ('c', 3) - >>> sd.peekitem(0) - ('a', 1) - >>> sd.peekitem(100) - Traceback (most recent call last): - ... - IndexError: list index out of range - - :param int index: index of item (default -1) - :return: key and value pair - :raises IndexError: if `index` out of range - - """ - key = self._list[index] - return key, self[key] - - - def setdefault(self, key, default=None): - """Return value for item identified by `key` in sorted dict. - - If `key` is in the sorted dict then return its value. If `key` is not - in the sorted dict then insert `key` with value `default` and return - `default`. - - Optional argument `default` defaults to none. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> sd = SortedDict() - >>> sd.setdefault('a', 1) - 1 - >>> sd.setdefault('a', 10) - 1 - >>> sd - SortedDict({'a': 1}) - - :param key: key for item - :param default: value for item (default None) - :return: value for item identified by `key` - - """ - if key in self: - return self[key] - dict.__setitem__(self, key, default) - self._list_add(key) - return default - - - def update(self, *args, **kwargs): - """Update sorted dict with items from `args` and `kwargs`. - - Overwrites existing items. - - Optional arguments `args` and `kwargs` may be a mapping, an iterable of - pairs or keyword arguments. See :func:`SortedDict.__init__` for - details. - - :param args: mapping or iterable of pairs - :param kwargs: keyword arguments mapping - - """ - if not self: - dict.update(self, *args, **kwargs) - self._list_update(dict.__iter__(self)) - return - - if not kwargs and len(args) == 1 and isinstance(args[0], dict): - pairs = args[0] - else: - pairs = dict(*args, **kwargs) - - if (10 * len(pairs)) > len(self): - dict.update(self, pairs) - self._list_clear() - self._list_update(dict.__iter__(self)) - else: - for key in pairs: - self._setitem(key, pairs[key]) - - _update = update - - - def __reduce__(self): - """Support for pickle. - - The tricks played with caching references in - :func:`SortedDict.__init__` confuse pickle so customize the reducer. - - """ - items = dict.copy(self) - return (type(self), (self._key, items)) - - - @recursive_repr() - def __repr__(self): - """Return string representation of sorted dict. - - ``sd.__repr__()`` <==> ``repr(sd)`` - - :return: string representation - - """ - _key = self._key - type_name = type(self).__name__ - key_arg = '' if _key is None else '{0!r}, '.format(_key) - item_format = '{0!r}: {1!r}'.format - items = ', '.join(item_format(key, self[key]) for key in self._list) - return '{0}({1}{{{2}}})'.format(type_name, key_arg, items) - - - def _check(self): - """Check invariants of sorted dict. - - Runtime complexity: `O(n)` - - """ - _list = self._list - _list._check() - assert len(self) == len(_list) - assert all(key in self for key in _list) - - -def _view_delitem(self, index): - """Remove item at `index` from sorted dict. - - ``view.__delitem__(index)`` <==> ``del view[index]`` - - Supports slicing. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> sd = SortedDict({'a': 1, 'b': 2, 'c': 3}) - >>> view = sd.keys() - >>> del view[0] - >>> sd - SortedDict({'b': 2, 'c': 3}) - >>> del view[-1] - >>> sd - SortedDict({'b': 2}) - >>> del view[:] - >>> sd - SortedDict({}) - - :param index: integer or slice for indexing - :raises IndexError: if index out of range - - """ - _mapping = self._mapping - _list = _mapping._list - dict_delitem = dict.__delitem__ - if isinstance(index, slice): - keys = _list[index] - del _list[index] - for key in keys: - dict_delitem(_mapping, key) - else: - key = _list.pop(index) - dict_delitem(_mapping, key) - - -class SortedKeysView(KeysView, Sequence): - """Sorted keys view is a dynamic view of the sorted dict's keys. - - When the sorted dict's keys change, the view reflects those changes. - - The keys view implements the set and sequence abstract base classes. - - """ - __slots__ = () - - - @classmethod - def _from_iterable(cls, it): - return SortedSet(it) - - - def __getitem__(self, index): - """Lookup key at `index` in sorted keys views. - - ``skv.__getitem__(index)`` <==> ``skv[index]`` - - Supports slicing. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> sd = SortedDict({'a': 1, 'b': 2, 'c': 3}) - >>> skv = sd.keys() - >>> skv[0] - 'a' - >>> skv[-1] - 'c' - >>> skv[:] - ['a', 'b', 'c'] - >>> skv[100] - Traceback (most recent call last): - ... - IndexError: list index out of range - - :param index: integer or slice for indexing - :return: key or list of keys - :raises IndexError: if index out of range - - """ - return self._mapping._list[index] - - - __delitem__ = _view_delitem - - -class SortedItemsView(ItemsView, Sequence): - """Sorted items view is a dynamic view of the sorted dict's items. - - When the sorted dict's items change, the view reflects those changes. - - The items view implements the set and sequence abstract base classes. - - """ - __slots__ = () - - - @classmethod - def _from_iterable(cls, it): - return SortedSet(it) - - - def __getitem__(self, index): - """Lookup item at `index` in sorted items view. - - ``siv.__getitem__(index)`` <==> ``siv[index]`` - - Supports slicing. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> sd = SortedDict({'a': 1, 'b': 2, 'c': 3}) - >>> siv = sd.items() - >>> siv[0] - ('a', 1) - >>> siv[-1] - ('c', 3) - >>> siv[:] - [('a', 1), ('b', 2), ('c', 3)] - >>> siv[100] - Traceback (most recent call last): - ... - IndexError: list index out of range - - :param index: integer or slice for indexing - :return: item or list of items - :raises IndexError: if index out of range - - """ - _mapping = self._mapping - _mapping_list = _mapping._list - - if isinstance(index, slice): - keys = _mapping_list[index] - return [(key, _mapping[key]) for key in keys] - - key = _mapping_list[index] - return key, _mapping[key] - - - __delitem__ = _view_delitem - - -class SortedValuesView(ValuesView, Sequence): - """Sorted values view is a dynamic view of the sorted dict's values. - - When the sorted dict's values change, the view reflects those changes. - - The values view implements the sequence abstract base class. - - """ - __slots__ = () - - - def __getitem__(self, index): - """Lookup value at `index` in sorted values view. - - ``siv.__getitem__(index)`` <==> ``siv[index]`` - - Supports slicing. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> sd = SortedDict({'a': 1, 'b': 2, 'c': 3}) - >>> svv = sd.values() - >>> svv[0] - 1 - >>> svv[-1] - 3 - >>> svv[:] - [1, 2, 3] - >>> svv[100] - Traceback (most recent call last): - ... - IndexError: list index out of range - - :param index: integer or slice for indexing - :return: value or list of values - :raises IndexError: if index out of range - - """ - _mapping = self._mapping - _mapping_list = _mapping._list - - if isinstance(index, slice): - keys = _mapping_list[index] - return [_mapping[key] for key in keys] - - key = _mapping_list[index] - return _mapping[key] - - - __delitem__ = _view_delitem diff --git a/apps/bitwarden_event_logs/lib/sortedcontainers/sortedlist.py b/apps/bitwarden_event_logs/lib/sortedcontainers/sortedlist.py deleted file mode 100755 index e3b58eb9..00000000 --- a/apps/bitwarden_event_logs/lib/sortedcontainers/sortedlist.py +++ /dev/null @@ -1,2646 +0,0 @@ -"""Sorted List -============== - -:doc:`Sorted Containers` is an Apache2 licensed Python sorted -collections library, written in pure-Python, and fast as C-extensions. The -:doc:`introduction` is the best way to get started. - -Sorted list implementations: - -.. currentmodule:: sortedcontainers - -* :class:`SortedList` -* :class:`SortedKeyList` - -""" -# pylint: disable=too-many-lines -from __future__ import print_function - -import sys -import traceback - -from bisect import bisect_left, bisect_right, insort -from itertools import chain, repeat, starmap -from math import log -from operator import add, eq, ne, gt, ge, lt, le, iadd -from textwrap import dedent - -############################################################################### -# BEGIN Python 2/3 Shims -############################################################################### - -try: - from collections.abc import Sequence, MutableSequence -except ImportError: - from collections import Sequence, MutableSequence - -from functools import wraps -from sys import hexversion - -if hexversion < 0x03000000: - from itertools import imap as map # pylint: disable=redefined-builtin - from itertools import izip as zip # pylint: disable=redefined-builtin - try: - from thread import get_ident - except ImportError: - from dummy_thread import get_ident -else: - from functools import reduce - try: - from _thread import get_ident - except ImportError: - from _dummy_thread import get_ident - - -def recursive_repr(fillvalue='...'): - "Decorator to make a repr function return fillvalue for a recursive call." - # pylint: disable=missing-docstring - # Copied from reprlib in Python 3 - # https://hg.python.org/cpython/file/3.6/Lib/reprlib.py - - def decorating_function(user_function): - repr_running = set() - - @wraps(user_function) - def wrapper(self): - key = id(self), get_ident() - if key in repr_running: - return fillvalue - repr_running.add(key) - try: - result = user_function(self) - finally: - repr_running.discard(key) - return result - - return wrapper - - return decorating_function - -############################################################################### -# END Python 2/3 Shims -############################################################################### - - -class SortedList(MutableSequence): - """Sorted list is a sorted mutable sequence. - - Sorted list values are maintained in sorted order. - - Sorted list values must be comparable. The total ordering of values must - not change while they are stored in the sorted list. - - Methods for adding values: - - * :func:`SortedList.add` - * :func:`SortedList.update` - * :func:`SortedList.__add__` - * :func:`SortedList.__iadd__` - * :func:`SortedList.__mul__` - * :func:`SortedList.__imul__` - - Methods for removing values: - - * :func:`SortedList.clear` - * :func:`SortedList.discard` - * :func:`SortedList.remove` - * :func:`SortedList.pop` - * :func:`SortedList.__delitem__` - - Methods for looking up values: - - * :func:`SortedList.bisect_left` - * :func:`SortedList.bisect_right` - * :func:`SortedList.count` - * :func:`SortedList.index` - * :func:`SortedList.__contains__` - * :func:`SortedList.__getitem__` - - Methods for iterating values: - - * :func:`SortedList.irange` - * :func:`SortedList.islice` - * :func:`SortedList.__iter__` - * :func:`SortedList.__reversed__` - - Methods for miscellany: - - * :func:`SortedList.copy` - * :func:`SortedList.__len__` - * :func:`SortedList.__repr__` - * :func:`SortedList._check` - * :func:`SortedList._reset` - - Sorted lists use lexicographical ordering semantics when compared to other - sequences. - - Some methods of mutable sequences are not supported and will raise - not-implemented error. - - """ - DEFAULT_LOAD_FACTOR = 1000 - - - def __init__(self, iterable=None, key=None): - """Initialize sorted list instance. - - Optional `iterable` argument provides an initial iterable of values to - initialize the sorted list. - - Runtime complexity: `O(n*log(n))` - - >>> sl = SortedList() - >>> sl - SortedList([]) - >>> sl = SortedList([3, 1, 2, 5, 4]) - >>> sl - SortedList([1, 2, 3, 4, 5]) - - :param iterable: initial values (optional) - - """ - assert key is None - self._len = 0 - self._load = self.DEFAULT_LOAD_FACTOR - self._lists = [] - self._maxes = [] - self._index = [] - self._offset = 0 - - if iterable is not None: - self._update(iterable) - - - def __new__(cls, iterable=None, key=None): - """Create new sorted list or sorted-key list instance. - - Optional `key`-function argument will return an instance of subtype - :class:`SortedKeyList`. - - >>> sl = SortedList() - >>> isinstance(sl, SortedList) - True - >>> sl = SortedList(key=lambda x: -x) - >>> isinstance(sl, SortedList) - True - >>> isinstance(sl, SortedKeyList) - True - - :param iterable: initial values (optional) - :param key: function used to extract comparison key (optional) - :return: sorted list or sorted-key list instance - - """ - # pylint: disable=unused-argument - if key is None: - return object.__new__(cls) - else: - if cls is SortedList: - return object.__new__(SortedKeyList) - else: - raise TypeError('inherit SortedKeyList for key argument') - - - @property - def key(self): # pylint: disable=useless-return - """Function used to extract comparison key from values. - - Sorted list compares values directly so the key function is none. - - """ - return None - - - def _reset(self, load): - """Reset sorted list load factor. - - The `load` specifies the load-factor of the list. The default load - factor of 1000 works well for lists from tens to tens-of-millions of - values. Good practice is to use a value that is the cube root of the - list size. With billions of elements, the best load factor depends on - your usage. It's best to leave the load factor at the default until you - start benchmarking. - - See :doc:`implementation` and :doc:`performance-scale` for more - information. - - Runtime complexity: `O(n)` - - :param int load: load-factor for sorted list sublists - - """ - values = reduce(iadd, self._lists, []) - self._clear() - self._load = load - self._update(values) - - - def clear(self): - """Remove all values from sorted list. - - Runtime complexity: `O(n)` - - """ - self._len = 0 - del self._lists[:] - del self._maxes[:] - del self._index[:] - self._offset = 0 - - _clear = clear - - - def add(self, value): - """Add `value` to sorted list. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> sl = SortedList() - >>> sl.add(3) - >>> sl.add(1) - >>> sl.add(2) - >>> sl - SortedList([1, 2, 3]) - - :param value: value to add to sorted list - - """ - _lists = self._lists - _maxes = self._maxes - - if _maxes: - pos = bisect_right(_maxes, value) - - if pos == len(_maxes): - pos -= 1 - _lists[pos].append(value) - _maxes[pos] = value - else: - insort(_lists[pos], value) - - self._expand(pos) - else: - _lists.append([value]) - _maxes.append(value) - - self._len += 1 - - - def _expand(self, pos): - """Split sublists with length greater than double the load-factor. - - Updates the index when the sublist length is less than double the load - level. This requires incrementing the nodes in a traversal from the - leaf node to the root. For an example traversal see - ``SortedList._loc``. - - """ - _load = self._load - _lists = self._lists - _index = self._index - - if len(_lists[pos]) > (_load << 1): - _maxes = self._maxes - - _lists_pos = _lists[pos] - half = _lists_pos[_load:] - del _lists_pos[_load:] - _maxes[pos] = _lists_pos[-1] - - _lists.insert(pos + 1, half) - _maxes.insert(pos + 1, half[-1]) - - del _index[:] - else: - if _index: - child = self._offset + pos - while child: - _index[child] += 1 - child = (child - 1) >> 1 - _index[0] += 1 - - - def update(self, iterable): - """Update sorted list by adding all values from `iterable`. - - Runtime complexity: `O(k*log(n))` -- approximate. - - >>> sl = SortedList() - >>> sl.update([3, 1, 2]) - >>> sl - SortedList([1, 2, 3]) - - :param iterable: iterable of values to add - - """ - _lists = self._lists - _maxes = self._maxes - values = sorted(iterable) - - if _maxes: - if len(values) * 4 >= self._len: - _lists.append(values) - values = reduce(iadd, _lists, []) - values.sort() - self._clear() - else: - _add = self.add - for val in values: - _add(val) - return - - _load = self._load - _lists.extend(values[pos:(pos + _load)] - for pos in range(0, len(values), _load)) - _maxes.extend(sublist[-1] for sublist in _lists) - self._len = len(values) - del self._index[:] - - _update = update - - - def __contains__(self, value): - """Return true if `value` is an element of the sorted list. - - ``sl.__contains__(value)`` <==> ``value in sl`` - - Runtime complexity: `O(log(n))` - - >>> sl = SortedList([1, 2, 3, 4, 5]) - >>> 3 in sl - True - - :param value: search for value in sorted list - :return: true if `value` in sorted list - - """ - _maxes = self._maxes - - if not _maxes: - return False - - pos = bisect_left(_maxes, value) - - if pos == len(_maxes): - return False - - _lists = self._lists - idx = bisect_left(_lists[pos], value) - - return _lists[pos][idx] == value - - - def discard(self, value): - """Remove `value` from sorted list if it is a member. - - If `value` is not a member, do nothing. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> sl = SortedList([1, 2, 3, 4, 5]) - >>> sl.discard(5) - >>> sl.discard(0) - >>> sl == [1, 2, 3, 4] - True - - :param value: `value` to discard from sorted list - - """ - _maxes = self._maxes - - if not _maxes: - return - - pos = bisect_left(_maxes, value) - - if pos == len(_maxes): - return - - _lists = self._lists - idx = bisect_left(_lists[pos], value) - - if _lists[pos][idx] == value: - self._delete(pos, idx) - - - def remove(self, value): - """Remove `value` from sorted list; `value` must be a member. - - If `value` is not a member, raise ValueError. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> sl = SortedList([1, 2, 3, 4, 5]) - >>> sl.remove(5) - >>> sl == [1, 2, 3, 4] - True - >>> sl.remove(0) - Traceback (most recent call last): - ... - ValueError: 0 not in list - - :param value: `value` to remove from sorted list - :raises ValueError: if `value` is not in sorted list - - """ - _maxes = self._maxes - - if not _maxes: - raise ValueError('{0!r} not in list'.format(value)) - - pos = bisect_left(_maxes, value) - - if pos == len(_maxes): - raise ValueError('{0!r} not in list'.format(value)) - - _lists = self._lists - idx = bisect_left(_lists[pos], value) - - if _lists[pos][idx] == value: - self._delete(pos, idx) - else: - raise ValueError('{0!r} not in list'.format(value)) - - - def _delete(self, pos, idx): - """Delete value at the given `(pos, idx)`. - - Combines lists that are less than half the load level. - - Updates the index when the sublist length is more than half the load - level. This requires decrementing the nodes in a traversal from the - leaf node to the root. For an example traversal see - ``SortedList._loc``. - - :param int pos: lists index - :param int idx: sublist index - - """ - _lists = self._lists - _maxes = self._maxes - _index = self._index - - _lists_pos = _lists[pos] - - del _lists_pos[idx] - self._len -= 1 - - len_lists_pos = len(_lists_pos) - - if len_lists_pos > (self._load >> 1): - _maxes[pos] = _lists_pos[-1] - - if _index: - child = self._offset + pos - while child > 0: - _index[child] -= 1 - child = (child - 1) >> 1 - _index[0] -= 1 - elif len(_lists) > 1: - if not pos: - pos += 1 - - prev = pos - 1 - _lists[prev].extend(_lists[pos]) - _maxes[prev] = _lists[prev][-1] - - del _lists[pos] - del _maxes[pos] - del _index[:] - - self._expand(prev) - elif len_lists_pos: - _maxes[pos] = _lists_pos[-1] - else: - del _lists[pos] - del _maxes[pos] - del _index[:] - - - def _loc(self, pos, idx): - """Convert an index pair (lists index, sublist index) into a single - index number that corresponds to the position of the value in the - sorted list. - - Many queries require the index be built. Details of the index are - described in ``SortedList._build_index``. - - Indexing requires traversing the tree from a leaf node to the root. The - parent of each node is easily computable at ``(pos - 1) // 2``. - - Left-child nodes are always at odd indices and right-child nodes are - always at even indices. - - When traversing up from a right-child node, increment the total by the - left-child node. - - The final index is the sum from traversal and the index in the sublist. - - For example, using the index from ``SortedList._build_index``:: - - _index = 14 5 9 3 2 4 5 - _offset = 3 - - Tree:: - - 14 - 5 9 - 3 2 4 5 - - Converting an index pair (2, 3) into a single index involves iterating - like so: - - 1. Starting at the leaf node: offset + alpha = 3 + 2 = 5. We identify - the node as a left-child node. At such nodes, we simply traverse to - the parent. - - 2. At node 9, position 2, we recognize the node as a right-child node - and accumulate the left-child in our total. Total is now 5 and we - traverse to the parent at position 0. - - 3. Iteration ends at the root. - - The index is then the sum of the total and sublist index: 5 + 3 = 8. - - :param int pos: lists index - :param int idx: sublist index - :return: index in sorted list - - """ - if not pos: - return idx - - _index = self._index - - if not _index: - self._build_index() - - total = 0 - - # Increment pos to point in the index to len(self._lists[pos]). - - pos += self._offset - - # Iterate until reaching the root of the index tree at pos = 0. - - while pos: - - # Right-child nodes are at odd indices. At such indices - # account the total below the left child node. - - if not pos & 1: - total += _index[pos - 1] - - # Advance pos to the parent node. - - pos = (pos - 1) >> 1 - - return total + idx - - - def _pos(self, idx): - """Convert an index into an index pair (lists index, sublist index) - that can be used to access the corresponding lists position. - - Many queries require the index be built. Details of the index are - described in ``SortedList._build_index``. - - Indexing requires traversing the tree to a leaf node. Each node has two - children which are easily computable. Given an index, pos, the - left-child is at ``pos * 2 + 1`` and the right-child is at ``pos * 2 + - 2``. - - When the index is less than the left-child, traversal moves to the - left sub-tree. Otherwise, the index is decremented by the left-child - and traversal moves to the right sub-tree. - - At a child node, the indexing pair is computed from the relative - position of the child node as compared with the offset and the remaining - index. - - For example, using the index from ``SortedList._build_index``:: - - _index = 14 5 9 3 2 4 5 - _offset = 3 - - Tree:: - - 14 - 5 9 - 3 2 4 5 - - Indexing position 8 involves iterating like so: - - 1. Starting at the root, position 0, 8 is compared with the left-child - node (5) which it is greater than. When greater the index is - decremented and the position is updated to the right child node. - - 2. At node 9 with index 3, we again compare the index to the left-child - node with value 4. Because the index is the less than the left-child - node, we simply traverse to the left. - - 3. At node 4 with index 3, we recognize that we are at a leaf node and - stop iterating. - - 4. To compute the sublist index, we subtract the offset from the index - of the leaf node: 5 - 3 = 2. To compute the index in the sublist, we - simply use the index remaining from iteration. In this case, 3. - - The final index pair from our example is (2, 3) which corresponds to - index 8 in the sorted list. - - :param int idx: index in sorted list - :return: (lists index, sublist index) pair - - """ - if idx < 0: - last_len = len(self._lists[-1]) - - if (-idx) <= last_len: - return len(self._lists) - 1, last_len + idx - - idx += self._len - - if idx < 0: - raise IndexError('list index out of range') - elif idx >= self._len: - raise IndexError('list index out of range') - - if idx < len(self._lists[0]): - return 0, idx - - _index = self._index - - if not _index: - self._build_index() - - pos = 0 - child = 1 - len_index = len(_index) - - while child < len_index: - index_child = _index[child] - - if idx < index_child: - pos = child - else: - idx -= index_child - pos = child + 1 - - child = (pos << 1) + 1 - - return (pos - self._offset, idx) - - - def _build_index(self): - """Build a positional index for indexing the sorted list. - - Indexes are represented as binary trees in a dense array notation - similar to a binary heap. - - For example, given a lists representation storing integers:: - - 0: [1, 2, 3] - 1: [4, 5] - 2: [6, 7, 8, 9] - 3: [10, 11, 12, 13, 14] - - The first transformation maps the sub-lists by their length. The - first row of the index is the length of the sub-lists:: - - 0: [3, 2, 4, 5] - - Each row after that is the sum of consecutive pairs of the previous - row:: - - 1: [5, 9] - 2: [14] - - Finally, the index is built by concatenating these lists together:: - - _index = [14, 5, 9, 3, 2, 4, 5] - - An offset storing the start of the first row is also stored:: - - _offset = 3 - - When built, the index can be used for efficient indexing into the list. - See the comment and notes on ``SortedList._pos`` for details. - - """ - row0 = list(map(len, self._lists)) - - if len(row0) == 1: - self._index[:] = row0 - self._offset = 0 - return - - head = iter(row0) - tail = iter(head) - row1 = list(starmap(add, zip(head, tail))) - - if len(row0) & 1: - row1.append(row0[-1]) - - if len(row1) == 1: - self._index[:] = row1 + row0 - self._offset = 1 - return - - size = 2 ** (int(log(len(row1) - 1, 2)) + 1) - row1.extend(repeat(0, size - len(row1))) - tree = [row0, row1] - - while len(tree[-1]) > 1: - head = iter(tree[-1]) - tail = iter(head) - row = list(starmap(add, zip(head, tail))) - tree.append(row) - - reduce(iadd, reversed(tree), self._index) - self._offset = size * 2 - 1 - - - def __delitem__(self, index): - """Remove value at `index` from sorted list. - - ``sl.__delitem__(index)`` <==> ``del sl[index]`` - - Supports slicing. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> sl = SortedList('abcde') - >>> del sl[2] - >>> sl - SortedList(['a', 'b', 'd', 'e']) - >>> del sl[:2] - >>> sl - SortedList(['d', 'e']) - - :param index: integer or slice for indexing - :raises IndexError: if index out of range - - """ - if isinstance(index, slice): - start, stop, step = index.indices(self._len) - - if step == 1 and start < stop: - if start == 0 and stop == self._len: - return self._clear() - elif self._len <= 8 * (stop - start): - values = self._getitem(slice(None, start)) - if stop < self._len: - values += self._getitem(slice(stop, None)) - self._clear() - return self._update(values) - - indices = range(start, stop, step) - - # Delete items from greatest index to least so - # that the indices remain valid throughout iteration. - - if step > 0: - indices = reversed(indices) - - _pos, _delete = self._pos, self._delete - - for index in indices: - pos, idx = _pos(index) - _delete(pos, idx) - else: - pos, idx = self._pos(index) - self._delete(pos, idx) - - - def __getitem__(self, index): - """Lookup value at `index` in sorted list. - - ``sl.__getitem__(index)`` <==> ``sl[index]`` - - Supports slicing. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> sl = SortedList('abcde') - >>> sl[1] - 'b' - >>> sl[-1] - 'e' - >>> sl[2:5] - ['c', 'd', 'e'] - - :param index: integer or slice for indexing - :return: value or list of values - :raises IndexError: if index out of range - - """ - _lists = self._lists - - if isinstance(index, slice): - start, stop, step = index.indices(self._len) - - if step == 1 and start < stop: - # Whole slice optimization: start to stop slices the whole - # sorted list. - - if start == 0 and stop == self._len: - return reduce(iadd, self._lists, []) - - start_pos, start_idx = self._pos(start) - start_list = _lists[start_pos] - stop_idx = start_idx + stop - start - - # Small slice optimization: start index and stop index are - # within the start list. - - if len(start_list) >= stop_idx: - return start_list[start_idx:stop_idx] - - if stop == self._len: - stop_pos = len(_lists) - 1 - stop_idx = len(_lists[stop_pos]) - else: - stop_pos, stop_idx = self._pos(stop) - - prefix = _lists[start_pos][start_idx:] - middle = _lists[(start_pos + 1):stop_pos] - result = reduce(iadd, middle, prefix) - result += _lists[stop_pos][:stop_idx] - - return result - - if step == -1 and start > stop: - result = self._getitem(slice(stop + 1, start + 1)) - result.reverse() - return result - - # Return a list because a negative step could - # reverse the order of the items and this could - # be the desired behavior. - - indices = range(start, stop, step) - return list(self._getitem(index) for index in indices) - else: - if self._len: - if index == 0: - return _lists[0][0] - elif index == -1: - return _lists[-1][-1] - else: - raise IndexError('list index out of range') - - if 0 <= index < len(_lists[0]): - return _lists[0][index] - - len_last = len(_lists[-1]) - - if -len_last < index < 0: - return _lists[-1][len_last + index] - - pos, idx = self._pos(index) - return _lists[pos][idx] - - _getitem = __getitem__ - - - def __setitem__(self, index, value): - """Raise not-implemented error. - - ``sl.__setitem__(index, value)`` <==> ``sl[index] = value`` - - :raises NotImplementedError: use ``del sl[index]`` and - ``sl.add(value)`` instead - - """ - message = 'use ``del sl[index]`` and ``sl.add(value)`` instead' - raise NotImplementedError(message) - - - def __iter__(self): - """Return an iterator over the sorted list. - - ``sl.__iter__()`` <==> ``iter(sl)`` - - Iterating the sorted list while adding or deleting values may raise a - :exc:`RuntimeError` or fail to iterate over all values. - - """ - return chain.from_iterable(self._lists) - - - def __reversed__(self): - """Return a reverse iterator over the sorted list. - - ``sl.__reversed__()`` <==> ``reversed(sl)`` - - Iterating the sorted list while adding or deleting values may raise a - :exc:`RuntimeError` or fail to iterate over all values. - - """ - return chain.from_iterable(map(reversed, reversed(self._lists))) - - - def reverse(self): - """Raise not-implemented error. - - Sorted list maintains values in ascending sort order. Values may not be - reversed in-place. - - Use ``reversed(sl)`` for an iterator over values in descending sort - order. - - Implemented to override `MutableSequence.reverse` which provides an - erroneous default implementation. - - :raises NotImplementedError: use ``reversed(sl)`` instead - - """ - raise NotImplementedError('use ``reversed(sl)`` instead') - - - def islice(self, start=None, stop=None, reverse=False): - """Return an iterator that slices sorted list from `start` to `stop`. - - The `start` and `stop` index are treated inclusive and exclusive, - respectively. - - Both `start` and `stop` default to `None` which is automatically - inclusive of the beginning and end of the sorted list. - - When `reverse` is `True` the values are yielded from the iterator in - reverse order; `reverse` defaults to `False`. - - >>> sl = SortedList('abcdefghij') - >>> it = sl.islice(2, 6) - >>> list(it) - ['c', 'd', 'e', 'f'] - - :param int start: start index (inclusive) - :param int stop: stop index (exclusive) - :param bool reverse: yield values in reverse order - :return: iterator - - """ - _len = self._len - - if not _len: - return iter(()) - - start, stop, _ = slice(start, stop).indices(self._len) - - if start >= stop: - return iter(()) - - _pos = self._pos - - min_pos, min_idx = _pos(start) - - if stop == _len: - max_pos = len(self._lists) - 1 - max_idx = len(self._lists[-1]) - else: - max_pos, max_idx = _pos(stop) - - return self._islice(min_pos, min_idx, max_pos, max_idx, reverse) - - - def _islice(self, min_pos, min_idx, max_pos, max_idx, reverse): - """Return an iterator that slices sorted list using two index pairs. - - The index pairs are (min_pos, min_idx) and (max_pos, max_idx), the - first inclusive and the latter exclusive. See `_pos` for details on how - an index is converted to an index pair. - - When `reverse` is `True`, values are yielded from the iterator in - reverse order. - - """ - _lists = self._lists - - if min_pos > max_pos: - return iter(()) - - if min_pos == max_pos: - if reverse: - indices = reversed(range(min_idx, max_idx)) - return map(_lists[min_pos].__getitem__, indices) - - indices = range(min_idx, max_idx) - return map(_lists[min_pos].__getitem__, indices) - - next_pos = min_pos + 1 - - if next_pos == max_pos: - if reverse: - min_indices = range(min_idx, len(_lists[min_pos])) - max_indices = range(max_idx) - return chain( - map(_lists[max_pos].__getitem__, reversed(max_indices)), - map(_lists[min_pos].__getitem__, reversed(min_indices)), - ) - - min_indices = range(min_idx, len(_lists[min_pos])) - max_indices = range(max_idx) - return chain( - map(_lists[min_pos].__getitem__, min_indices), - map(_lists[max_pos].__getitem__, max_indices), - ) - - if reverse: - min_indices = range(min_idx, len(_lists[min_pos])) - sublist_indices = range(next_pos, max_pos) - sublists = map(_lists.__getitem__, reversed(sublist_indices)) - max_indices = range(max_idx) - return chain( - map(_lists[max_pos].__getitem__, reversed(max_indices)), - chain.from_iterable(map(reversed, sublists)), - map(_lists[min_pos].__getitem__, reversed(min_indices)), - ) - - min_indices = range(min_idx, len(_lists[min_pos])) - sublist_indices = range(next_pos, max_pos) - sublists = map(_lists.__getitem__, sublist_indices) - max_indices = range(max_idx) - return chain( - map(_lists[min_pos].__getitem__, min_indices), - chain.from_iterable(sublists), - map(_lists[max_pos].__getitem__, max_indices), - ) - - - def irange(self, minimum=None, maximum=None, inclusive=(True, True), - reverse=False): - """Create an iterator of values between `minimum` and `maximum`. - - Both `minimum` and `maximum` default to `None` which is automatically - inclusive of the beginning and end of the sorted list. - - The argument `inclusive` is a pair of booleans that indicates whether - the minimum and maximum ought to be included in the range, - respectively. The default is ``(True, True)`` such that the range is - inclusive of both minimum and maximum. - - When `reverse` is `True` the values are yielded from the iterator in - reverse order; `reverse` defaults to `False`. - - >>> sl = SortedList('abcdefghij') - >>> it = sl.irange('c', 'f') - >>> list(it) - ['c', 'd', 'e', 'f'] - - :param minimum: minimum value to start iterating - :param maximum: maximum value to stop iterating - :param inclusive: pair of booleans - :param bool reverse: yield values in reverse order - :return: iterator - - """ - _maxes = self._maxes - - if not _maxes: - return iter(()) - - _lists = self._lists - - # Calculate the minimum (pos, idx) pair. By default this location - # will be inclusive in our calculation. - - if minimum is None: - min_pos = 0 - min_idx = 0 - else: - if inclusive[0]: - min_pos = bisect_left(_maxes, minimum) - - if min_pos == len(_maxes): - return iter(()) - - min_idx = bisect_left(_lists[min_pos], minimum) - else: - min_pos = bisect_right(_maxes, minimum) - - if min_pos == len(_maxes): - return iter(()) - - min_idx = bisect_right(_lists[min_pos], minimum) - - # Calculate the maximum (pos, idx) pair. By default this location - # will be exclusive in our calculation. - - if maximum is None: - max_pos = len(_maxes) - 1 - max_idx = len(_lists[max_pos]) - else: - if inclusive[1]: - max_pos = bisect_right(_maxes, maximum) - - if max_pos == len(_maxes): - max_pos -= 1 - max_idx = len(_lists[max_pos]) - else: - max_idx = bisect_right(_lists[max_pos], maximum) - else: - max_pos = bisect_left(_maxes, maximum) - - if max_pos == len(_maxes): - max_pos -= 1 - max_idx = len(_lists[max_pos]) - else: - max_idx = bisect_left(_lists[max_pos], maximum) - - return self._islice(min_pos, min_idx, max_pos, max_idx, reverse) - - - def __len__(self): - """Return the size of the sorted list. - - ``sl.__len__()`` <==> ``len(sl)`` - - :return: size of sorted list - - """ - return self._len - - - def bisect_left(self, value): - """Return an index to insert `value` in the sorted list. - - If the `value` is already present, the insertion point will be before - (to the left of) any existing values. - - Similar to the `bisect` module in the standard library. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> sl = SortedList([10, 11, 12, 13, 14]) - >>> sl.bisect_left(12) - 2 - - :param value: insertion index of value in sorted list - :return: index - - """ - _maxes = self._maxes - - if not _maxes: - return 0 - - pos = bisect_left(_maxes, value) - - if pos == len(_maxes): - return self._len - - idx = bisect_left(self._lists[pos], value) - return self._loc(pos, idx) - - - def bisect_right(self, value): - """Return an index to insert `value` in the sorted list. - - Similar to `bisect_left`, but if `value` is already present, the - insertion point will be after (to the right of) any existing values. - - Similar to the `bisect` module in the standard library. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> sl = SortedList([10, 11, 12, 13, 14]) - >>> sl.bisect_right(12) - 3 - - :param value: insertion index of value in sorted list - :return: index - - """ - _maxes = self._maxes - - if not _maxes: - return 0 - - pos = bisect_right(_maxes, value) - - if pos == len(_maxes): - return self._len - - idx = bisect_right(self._lists[pos], value) - return self._loc(pos, idx) - - bisect = bisect_right - _bisect_right = bisect_right - - - def count(self, value): - """Return number of occurrences of `value` in the sorted list. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> sl = SortedList([1, 2, 2, 3, 3, 3, 4, 4, 4, 4]) - >>> sl.count(3) - 3 - - :param value: value to count in sorted list - :return: count - - """ - _maxes = self._maxes - - if not _maxes: - return 0 - - pos_left = bisect_left(_maxes, value) - - if pos_left == len(_maxes): - return 0 - - _lists = self._lists - idx_left = bisect_left(_lists[pos_left], value) - pos_right = bisect_right(_maxes, value) - - if pos_right == len(_maxes): - return self._len - self._loc(pos_left, idx_left) - - idx_right = bisect_right(_lists[pos_right], value) - - if pos_left == pos_right: - return idx_right - idx_left - - right = self._loc(pos_right, idx_right) - left = self._loc(pos_left, idx_left) - return right - left - - - def copy(self): - """Return a shallow copy of the sorted list. - - Runtime complexity: `O(n)` - - :return: new sorted list - - """ - return self.__class__(self) - - __copy__ = copy - - - def append(self, value): - """Raise not-implemented error. - - Implemented to override `MutableSequence.append` which provides an - erroneous default implementation. - - :raises NotImplementedError: use ``sl.add(value)`` instead - - """ - raise NotImplementedError('use ``sl.add(value)`` instead') - - - def extend(self, values): - """Raise not-implemented error. - - Implemented to override `MutableSequence.extend` which provides an - erroneous default implementation. - - :raises NotImplementedError: use ``sl.update(values)`` instead - - """ - raise NotImplementedError('use ``sl.update(values)`` instead') - - - def insert(self, index, value): - """Raise not-implemented error. - - :raises NotImplementedError: use ``sl.add(value)`` instead - - """ - raise NotImplementedError('use ``sl.add(value)`` instead') - - - def pop(self, index=-1): - """Remove and return value at `index` in sorted list. - - Raise :exc:`IndexError` if the sorted list is empty or index is out of - range. - - Negative indices are supported. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> sl = SortedList('abcde') - >>> sl.pop() - 'e' - >>> sl.pop(2) - 'c' - >>> sl - SortedList(['a', 'b', 'd']) - - :param int index: index of value (default -1) - :return: value - :raises IndexError: if index is out of range - - """ - if not self._len: - raise IndexError('pop index out of range') - - _lists = self._lists - - if index == 0: - val = _lists[0][0] - self._delete(0, 0) - return val - - if index == -1: - pos = len(_lists) - 1 - loc = len(_lists[pos]) - 1 - val = _lists[pos][loc] - self._delete(pos, loc) - return val - - if 0 <= index < len(_lists[0]): - val = _lists[0][index] - self._delete(0, index) - return val - - len_last = len(_lists[-1]) - - if -len_last < index < 0: - pos = len(_lists) - 1 - loc = len_last + index - val = _lists[pos][loc] - self._delete(pos, loc) - return val - - pos, idx = self._pos(index) - val = _lists[pos][idx] - self._delete(pos, idx) - return val - - - def index(self, value, start=None, stop=None): - """Return first index of value in sorted list. - - Raise ValueError if `value` is not present. - - Index must be between `start` and `stop` for the `value` to be - considered present. The default value, None, for `start` and `stop` - indicate the beginning and end of the sorted list. - - Negative indices are supported. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> sl = SortedList('abcde') - >>> sl.index('d') - 3 - >>> sl.index('z') - Traceback (most recent call last): - ... - ValueError: 'z' is not in list - - :param value: value in sorted list - :param int start: start index (default None, start of sorted list) - :param int stop: stop index (default None, end of sorted list) - :return: index of value - :raises ValueError: if value is not present - - """ - _len = self._len - - if not _len: - raise ValueError('{0!r} is not in list'.format(value)) - - if start is None: - start = 0 - if start < 0: - start += _len - if start < 0: - start = 0 - - if stop is None: - stop = _len - if stop < 0: - stop += _len - if stop > _len: - stop = _len - - if stop <= start: - raise ValueError('{0!r} is not in list'.format(value)) - - _maxes = self._maxes - pos_left = bisect_left(_maxes, value) - - if pos_left == len(_maxes): - raise ValueError('{0!r} is not in list'.format(value)) - - _lists = self._lists - idx_left = bisect_left(_lists[pos_left], value) - - if _lists[pos_left][idx_left] != value: - raise ValueError('{0!r} is not in list'.format(value)) - - stop -= 1 - left = self._loc(pos_left, idx_left) - - if start <= left: - if left <= stop: - return left - else: - right = self._bisect_right(value) - 1 - - if start <= right: - return start - - raise ValueError('{0!r} is not in list'.format(value)) - - - def __add__(self, other): - """Return new sorted list containing all values in both sequences. - - ``sl.__add__(other)`` <==> ``sl + other`` - - Values in `other` do not need to be in sorted order. - - Runtime complexity: `O(n*log(n))` - - >>> sl1 = SortedList('bat') - >>> sl2 = SortedList('cat') - >>> sl1 + sl2 - SortedList(['a', 'a', 'b', 'c', 't', 't']) - - :param other: other iterable - :return: new sorted list - - """ - values = reduce(iadd, self._lists, []) - values.extend(other) - return self.__class__(values) - - __radd__ = __add__ - - - def __iadd__(self, other): - """Update sorted list with values from `other`. - - ``sl.__iadd__(other)`` <==> ``sl += other`` - - Values in `other` do not need to be in sorted order. - - Runtime complexity: `O(k*log(n))` -- approximate. - - >>> sl = SortedList('bat') - >>> sl += 'cat' - >>> sl - SortedList(['a', 'a', 'b', 'c', 't', 't']) - - :param other: other iterable - :return: existing sorted list - - """ - self._update(other) - return self - - - def __mul__(self, num): - """Return new sorted list with `num` shallow copies of values. - - ``sl.__mul__(num)`` <==> ``sl * num`` - - Runtime complexity: `O(n*log(n))` - - >>> sl = SortedList('abc') - >>> sl * 3 - SortedList(['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c']) - - :param int num: count of shallow copies - :return: new sorted list - - """ - values = reduce(iadd, self._lists, []) * num - return self.__class__(values) - - __rmul__ = __mul__ - - - def __imul__(self, num): - """Update the sorted list with `num` shallow copies of values. - - ``sl.__imul__(num)`` <==> ``sl *= num`` - - Runtime complexity: `O(n*log(n))` - - >>> sl = SortedList('abc') - >>> sl *= 3 - >>> sl - SortedList(['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c']) - - :param int num: count of shallow copies - :return: existing sorted list - - """ - values = reduce(iadd, self._lists, []) * num - self._clear() - self._update(values) - return self - - - def __make_cmp(seq_op, symbol, doc): - "Make comparator method." - def comparer(self, other): - "Compare method for sorted list and sequence." - if not isinstance(other, Sequence): - return NotImplemented - - self_len = self._len - len_other = len(other) - - if self_len != len_other: - if seq_op is eq: - return False - if seq_op is ne: - return True - - for alpha, beta in zip(self, other): - if alpha != beta: - return seq_op(alpha, beta) - - return seq_op(self_len, len_other) - - seq_op_name = seq_op.__name__ - comparer.__name__ = '__{0}__'.format(seq_op_name) - doc_str = """Return true if and only if sorted list is {0} `other`. - - ``sl.__{1}__(other)`` <==> ``sl {2} other`` - - Comparisons use lexicographical order as with sequences. - - Runtime complexity: `O(n)` - - :param other: `other` sequence - :return: true if sorted list is {0} `other` - - """ - comparer.__doc__ = dedent(doc_str.format(doc, seq_op_name, symbol)) - return comparer - - - __eq__ = __make_cmp(eq, '==', 'equal to') - __ne__ = __make_cmp(ne, '!=', 'not equal to') - __lt__ = __make_cmp(lt, '<', 'less than') - __gt__ = __make_cmp(gt, '>', 'greater than') - __le__ = __make_cmp(le, '<=', 'less than or equal to') - __ge__ = __make_cmp(ge, '>=', 'greater than or equal to') - __make_cmp = staticmethod(__make_cmp) - - - def __reduce__(self): - values = reduce(iadd, self._lists, []) - return (type(self), (values,)) - - - @recursive_repr() - def __repr__(self): - """Return string representation of sorted list. - - ``sl.__repr__()`` <==> ``repr(sl)`` - - :return: string representation - - """ - return '{0}({1!r})'.format(type(self).__name__, list(self)) - - - def _check(self): - """Check invariants of sorted list. - - Runtime complexity: `O(n)` - - """ - try: - assert self._load >= 4 - assert len(self._maxes) == len(self._lists) - assert self._len == sum(len(sublist) for sublist in self._lists) - - # Check all sublists are sorted. - - for sublist in self._lists: - for pos in range(1, len(sublist)): - assert sublist[pos - 1] <= sublist[pos] - - # Check beginning/end of sublists are sorted. - - for pos in range(1, len(self._lists)): - assert self._lists[pos - 1][-1] <= self._lists[pos][0] - - # Check _maxes index is the last value of each sublist. - - for pos in range(len(self._maxes)): - assert self._maxes[pos] == self._lists[pos][-1] - - # Check sublist lengths are less than double load-factor. - - double = self._load << 1 - assert all(len(sublist) <= double for sublist in self._lists) - - # Check sublist lengths are greater than half load-factor for all - # but the last sublist. - - half = self._load >> 1 - for pos in range(0, len(self._lists) - 1): - assert len(self._lists[pos]) >= half - - if self._index: - assert self._len == self._index[0] - assert len(self._index) == self._offset + len(self._lists) - - # Check index leaf nodes equal length of sublists. - - for pos in range(len(self._lists)): - leaf = self._index[self._offset + pos] - assert leaf == len(self._lists[pos]) - - # Check index branch nodes are the sum of their children. - - for pos in range(self._offset): - child = (pos << 1) + 1 - if child >= len(self._index): - assert self._index[pos] == 0 - elif child + 1 == len(self._index): - assert self._index[pos] == self._index[child] - else: - child_sum = self._index[child] + self._index[child + 1] - assert child_sum == self._index[pos] - except: - traceback.print_exc(file=sys.stdout) - print('len', self._len) - print('load', self._load) - print('offset', self._offset) - print('len_index', len(self._index)) - print('index', self._index) - print('len_maxes', len(self._maxes)) - print('maxes', self._maxes) - print('len_lists', len(self._lists)) - print('lists', self._lists) - raise - - -def identity(value): - "Identity function." - return value - - -class SortedKeyList(SortedList): - """Sorted-key list is a subtype of sorted list. - - The sorted-key list maintains values in comparison order based on the - result of a key function applied to every value. - - All the same methods that are available in :class:`SortedList` are also - available in :class:`SortedKeyList`. - - Additional methods provided: - - * :attr:`SortedKeyList.key` - * :func:`SortedKeyList.bisect_key_left` - * :func:`SortedKeyList.bisect_key_right` - * :func:`SortedKeyList.irange_key` - - Some examples below use: - - >>> from operator import neg - >>> neg - - >>> neg(1) - -1 - - """ - def __init__(self, iterable=None, key=identity): - """Initialize sorted-key list instance. - - Optional `iterable` argument provides an initial iterable of values to - initialize the sorted-key list. - - Optional `key` argument defines a callable that, like the `key` - argument to Python's `sorted` function, extracts a comparison key from - each value. The default is the identity function. - - Runtime complexity: `O(n*log(n))` - - >>> from operator import neg - >>> skl = SortedKeyList(key=neg) - >>> skl - SortedKeyList([], key=) - >>> skl = SortedKeyList([3, 1, 2], key=neg) - >>> skl - SortedKeyList([3, 2, 1], key=) - - :param iterable: initial values (optional) - :param key: function used to extract comparison key (optional) - - """ - self._key = key - self._len = 0 - self._load = self.DEFAULT_LOAD_FACTOR - self._lists = [] - self._keys = [] - self._maxes = [] - self._index = [] - self._offset = 0 - - if iterable is not None: - self._update(iterable) - - - def __new__(cls, iterable=None, key=identity): - return object.__new__(cls) - - - @property - def key(self): - "Function used to extract comparison key from values." - return self._key - - - def clear(self): - """Remove all values from sorted-key list. - - Runtime complexity: `O(n)` - - """ - self._len = 0 - del self._lists[:] - del self._keys[:] - del self._maxes[:] - del self._index[:] - - _clear = clear - - - def add(self, value): - """Add `value` to sorted-key list. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> from operator import neg - >>> skl = SortedKeyList(key=neg) - >>> skl.add(3) - >>> skl.add(1) - >>> skl.add(2) - >>> skl - SortedKeyList([3, 2, 1], key=) - - :param value: value to add to sorted-key list - - """ - _lists = self._lists - _keys = self._keys - _maxes = self._maxes - - key = self._key(value) - - if _maxes: - pos = bisect_right(_maxes, key) - - if pos == len(_maxes): - pos -= 1 - _lists[pos].append(value) - _keys[pos].append(key) - _maxes[pos] = key - else: - idx = bisect_right(_keys[pos], key) - _lists[pos].insert(idx, value) - _keys[pos].insert(idx, key) - - self._expand(pos) - else: - _lists.append([value]) - _keys.append([key]) - _maxes.append(key) - - self._len += 1 - - - def _expand(self, pos): - """Split sublists with length greater than double the load-factor. - - Updates the index when the sublist length is less than double the load - level. This requires incrementing the nodes in a traversal from the - leaf node to the root. For an example traversal see - ``SortedList._loc``. - - """ - _lists = self._lists - _keys = self._keys - _index = self._index - - if len(_keys[pos]) > (self._load << 1): - _maxes = self._maxes - _load = self._load - - _lists_pos = _lists[pos] - _keys_pos = _keys[pos] - half = _lists_pos[_load:] - half_keys = _keys_pos[_load:] - del _lists_pos[_load:] - del _keys_pos[_load:] - _maxes[pos] = _keys_pos[-1] - - _lists.insert(pos + 1, half) - _keys.insert(pos + 1, half_keys) - _maxes.insert(pos + 1, half_keys[-1]) - - del _index[:] - else: - if _index: - child = self._offset + pos - while child: - _index[child] += 1 - child = (child - 1) >> 1 - _index[0] += 1 - - - def update(self, iterable): - """Update sorted-key list by adding all values from `iterable`. - - Runtime complexity: `O(k*log(n))` -- approximate. - - >>> from operator import neg - >>> skl = SortedKeyList(key=neg) - >>> skl.update([3, 1, 2]) - >>> skl - SortedKeyList([3, 2, 1], key=) - - :param iterable: iterable of values to add - - """ - _lists = self._lists - _keys = self._keys - _maxes = self._maxes - values = sorted(iterable, key=self._key) - - if _maxes: - if len(values) * 4 >= self._len: - _lists.append(values) - values = reduce(iadd, _lists, []) - values.sort(key=self._key) - self._clear() - else: - _add = self.add - for val in values: - _add(val) - return - - _load = self._load - _lists.extend(values[pos:(pos + _load)] - for pos in range(0, len(values), _load)) - _keys.extend(list(map(self._key, _list)) for _list in _lists) - _maxes.extend(sublist[-1] for sublist in _keys) - self._len = len(values) - del self._index[:] - - _update = update - - - def __contains__(self, value): - """Return true if `value` is an element of the sorted-key list. - - ``skl.__contains__(value)`` <==> ``value in skl`` - - Runtime complexity: `O(log(n))` - - >>> from operator import neg - >>> skl = SortedKeyList([1, 2, 3, 4, 5], key=neg) - >>> 3 in skl - True - - :param value: search for value in sorted-key list - :return: true if `value` in sorted-key list - - """ - _maxes = self._maxes - - if not _maxes: - return False - - key = self._key(value) - pos = bisect_left(_maxes, key) - - if pos == len(_maxes): - return False - - _lists = self._lists - _keys = self._keys - - idx = bisect_left(_keys[pos], key) - - len_keys = len(_keys) - len_sublist = len(_keys[pos]) - - while True: - if _keys[pos][idx] != key: - return False - if _lists[pos][idx] == value: - return True - idx += 1 - if idx == len_sublist: - pos += 1 - if pos == len_keys: - return False - len_sublist = len(_keys[pos]) - idx = 0 - - - def discard(self, value): - """Remove `value` from sorted-key list if it is a member. - - If `value` is not a member, do nothing. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> from operator import neg - >>> skl = SortedKeyList([5, 4, 3, 2, 1], key=neg) - >>> skl.discard(1) - >>> skl.discard(0) - >>> skl == [5, 4, 3, 2] - True - - :param value: `value` to discard from sorted-key list - - """ - _maxes = self._maxes - - if not _maxes: - return - - key = self._key(value) - pos = bisect_left(_maxes, key) - - if pos == len(_maxes): - return - - _lists = self._lists - _keys = self._keys - idx = bisect_left(_keys[pos], key) - len_keys = len(_keys) - len_sublist = len(_keys[pos]) - - while True: - if _keys[pos][idx] != key: - return - if _lists[pos][idx] == value: - self._delete(pos, idx) - return - idx += 1 - if idx == len_sublist: - pos += 1 - if pos == len_keys: - return - len_sublist = len(_keys[pos]) - idx = 0 - - - def remove(self, value): - """Remove `value` from sorted-key list; `value` must be a member. - - If `value` is not a member, raise ValueError. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> from operator import neg - >>> skl = SortedKeyList([1, 2, 3, 4, 5], key=neg) - >>> skl.remove(5) - >>> skl == [4, 3, 2, 1] - True - >>> skl.remove(0) - Traceback (most recent call last): - ... - ValueError: 0 not in list - - :param value: `value` to remove from sorted-key list - :raises ValueError: if `value` is not in sorted-key list - - """ - _maxes = self._maxes - - if not _maxes: - raise ValueError('{0!r} not in list'.format(value)) - - key = self._key(value) - pos = bisect_left(_maxes, key) - - if pos == len(_maxes): - raise ValueError('{0!r} not in list'.format(value)) - - _lists = self._lists - _keys = self._keys - idx = bisect_left(_keys[pos], key) - len_keys = len(_keys) - len_sublist = len(_keys[pos]) - - while True: - if _keys[pos][idx] != key: - raise ValueError('{0!r} not in list'.format(value)) - if _lists[pos][idx] == value: - self._delete(pos, idx) - return - idx += 1 - if idx == len_sublist: - pos += 1 - if pos == len_keys: - raise ValueError('{0!r} not in list'.format(value)) - len_sublist = len(_keys[pos]) - idx = 0 - - - def _delete(self, pos, idx): - """Delete value at the given `(pos, idx)`. - - Combines lists that are less than half the load level. - - Updates the index when the sublist length is more than half the load - level. This requires decrementing the nodes in a traversal from the - leaf node to the root. For an example traversal see - ``SortedList._loc``. - - :param int pos: lists index - :param int idx: sublist index - - """ - _lists = self._lists - _keys = self._keys - _maxes = self._maxes - _index = self._index - keys_pos = _keys[pos] - lists_pos = _lists[pos] - - del keys_pos[idx] - del lists_pos[idx] - self._len -= 1 - - len_keys_pos = len(keys_pos) - - if len_keys_pos > (self._load >> 1): - _maxes[pos] = keys_pos[-1] - - if _index: - child = self._offset + pos - while child > 0: - _index[child] -= 1 - child = (child - 1) >> 1 - _index[0] -= 1 - elif len(_keys) > 1: - if not pos: - pos += 1 - - prev = pos - 1 - _keys[prev].extend(_keys[pos]) - _lists[prev].extend(_lists[pos]) - _maxes[prev] = _keys[prev][-1] - - del _lists[pos] - del _keys[pos] - del _maxes[pos] - del _index[:] - - self._expand(prev) - elif len_keys_pos: - _maxes[pos] = keys_pos[-1] - else: - del _lists[pos] - del _keys[pos] - del _maxes[pos] - del _index[:] - - - def irange(self, minimum=None, maximum=None, inclusive=(True, True), - reverse=False): - """Create an iterator of values between `minimum` and `maximum`. - - Both `minimum` and `maximum` default to `None` which is automatically - inclusive of the beginning and end of the sorted-key list. - - The argument `inclusive` is a pair of booleans that indicates whether - the minimum and maximum ought to be included in the range, - respectively. The default is ``(True, True)`` such that the range is - inclusive of both minimum and maximum. - - When `reverse` is `True` the values are yielded from the iterator in - reverse order; `reverse` defaults to `False`. - - >>> from operator import neg - >>> skl = SortedKeyList([11, 12, 13, 14, 15], key=neg) - >>> it = skl.irange(14.5, 11.5) - >>> list(it) - [14, 13, 12] - - :param minimum: minimum value to start iterating - :param maximum: maximum value to stop iterating - :param inclusive: pair of booleans - :param bool reverse: yield values in reverse order - :return: iterator - - """ - min_key = self._key(minimum) if minimum is not None else None - max_key = self._key(maximum) if maximum is not None else None - return self._irange_key( - min_key=min_key, max_key=max_key, - inclusive=inclusive, reverse=reverse, - ) - - - def irange_key(self, min_key=None, max_key=None, inclusive=(True, True), - reverse=False): - """Create an iterator of values between `min_key` and `max_key`. - - Both `min_key` and `max_key` default to `None` which is automatically - inclusive of the beginning and end of the sorted-key list. - - The argument `inclusive` is a pair of booleans that indicates whether - the minimum and maximum ought to be included in the range, - respectively. The default is ``(True, True)`` such that the range is - inclusive of both minimum and maximum. - - When `reverse` is `True` the values are yielded from the iterator in - reverse order; `reverse` defaults to `False`. - - >>> from operator import neg - >>> skl = SortedKeyList([11, 12, 13, 14, 15], key=neg) - >>> it = skl.irange_key(-14, -12) - >>> list(it) - [14, 13, 12] - - :param min_key: minimum key to start iterating - :param max_key: maximum key to stop iterating - :param inclusive: pair of booleans - :param bool reverse: yield values in reverse order - :return: iterator - - """ - _maxes = self._maxes - - if not _maxes: - return iter(()) - - _keys = self._keys - - # Calculate the minimum (pos, idx) pair. By default this location - # will be inclusive in our calculation. - - if min_key is None: - min_pos = 0 - min_idx = 0 - else: - if inclusive[0]: - min_pos = bisect_left(_maxes, min_key) - - if min_pos == len(_maxes): - return iter(()) - - min_idx = bisect_left(_keys[min_pos], min_key) - else: - min_pos = bisect_right(_maxes, min_key) - - if min_pos == len(_maxes): - return iter(()) - - min_idx = bisect_right(_keys[min_pos], min_key) - - # Calculate the maximum (pos, idx) pair. By default this location - # will be exclusive in our calculation. - - if max_key is None: - max_pos = len(_maxes) - 1 - max_idx = len(_keys[max_pos]) - else: - if inclusive[1]: - max_pos = bisect_right(_maxes, max_key) - - if max_pos == len(_maxes): - max_pos -= 1 - max_idx = len(_keys[max_pos]) - else: - max_idx = bisect_right(_keys[max_pos], max_key) - else: - max_pos = bisect_left(_maxes, max_key) - - if max_pos == len(_maxes): - max_pos -= 1 - max_idx = len(_keys[max_pos]) - else: - max_idx = bisect_left(_keys[max_pos], max_key) - - return self._islice(min_pos, min_idx, max_pos, max_idx, reverse) - - _irange_key = irange_key - - - def bisect_left(self, value): - """Return an index to insert `value` in the sorted-key list. - - If the `value` is already present, the insertion point will be before - (to the left of) any existing values. - - Similar to the `bisect` module in the standard library. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> from operator import neg - >>> skl = SortedKeyList([5, 4, 3, 2, 1], key=neg) - >>> skl.bisect_left(1) - 4 - - :param value: insertion index of value in sorted-key list - :return: index - - """ - return self._bisect_key_left(self._key(value)) - - - def bisect_right(self, value): - """Return an index to insert `value` in the sorted-key list. - - Similar to `bisect_left`, but if `value` is already present, the - insertion point will be after (to the right of) any existing values. - - Similar to the `bisect` module in the standard library. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> from operator import neg - >>> skl = SortedList([5, 4, 3, 2, 1], key=neg) - >>> skl.bisect_right(1) - 5 - - :param value: insertion index of value in sorted-key list - :return: index - - """ - return self._bisect_key_right(self._key(value)) - - bisect = bisect_right - - - def bisect_key_left(self, key): - """Return an index to insert `key` in the sorted-key list. - - If the `key` is already present, the insertion point will be before (to - the left of) any existing keys. - - Similar to the `bisect` module in the standard library. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> from operator import neg - >>> skl = SortedKeyList([5, 4, 3, 2, 1], key=neg) - >>> skl.bisect_key_left(-1) - 4 - - :param key: insertion index of key in sorted-key list - :return: index - - """ - _maxes = self._maxes - - if not _maxes: - return 0 - - pos = bisect_left(_maxes, key) - - if pos == len(_maxes): - return self._len - - idx = bisect_left(self._keys[pos], key) - - return self._loc(pos, idx) - - _bisect_key_left = bisect_key_left - - - def bisect_key_right(self, key): - """Return an index to insert `key` in the sorted-key list. - - Similar to `bisect_key_left`, but if `key` is already present, the - insertion point will be after (to the right of) any existing keys. - - Similar to the `bisect` module in the standard library. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> from operator import neg - >>> skl = SortedList([5, 4, 3, 2, 1], key=neg) - >>> skl.bisect_key_right(-1) - 5 - - :param key: insertion index of key in sorted-key list - :return: index - - """ - _maxes = self._maxes - - if not _maxes: - return 0 - - pos = bisect_right(_maxes, key) - - if pos == len(_maxes): - return self._len - - idx = bisect_right(self._keys[pos], key) - - return self._loc(pos, idx) - - bisect_key = bisect_key_right - _bisect_key_right = bisect_key_right - - - def count(self, value): - """Return number of occurrences of `value` in the sorted-key list. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> from operator import neg - >>> skl = SortedKeyList([4, 4, 4, 4, 3, 3, 3, 2, 2, 1], key=neg) - >>> skl.count(2) - 2 - - :param value: value to count in sorted-key list - :return: count - - """ - _maxes = self._maxes - - if not _maxes: - return 0 - - key = self._key(value) - pos = bisect_left(_maxes, key) - - if pos == len(_maxes): - return 0 - - _lists = self._lists - _keys = self._keys - idx = bisect_left(_keys[pos], key) - total = 0 - len_keys = len(_keys) - len_sublist = len(_keys[pos]) - - while True: - if _keys[pos][idx] != key: - return total - if _lists[pos][idx] == value: - total += 1 - idx += 1 - if idx == len_sublist: - pos += 1 - if pos == len_keys: - return total - len_sublist = len(_keys[pos]) - idx = 0 - - - def copy(self): - """Return a shallow copy of the sorted-key list. - - Runtime complexity: `O(n)` - - :return: new sorted-key list - - """ - return self.__class__(self, key=self._key) - - __copy__ = copy - - - def index(self, value, start=None, stop=None): - """Return first index of value in sorted-key list. - - Raise ValueError if `value` is not present. - - Index must be between `start` and `stop` for the `value` to be - considered present. The default value, None, for `start` and `stop` - indicate the beginning and end of the sorted-key list. - - Negative indices are supported. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> from operator import neg - >>> skl = SortedKeyList([5, 4, 3, 2, 1], key=neg) - >>> skl.index(2) - 3 - >>> skl.index(0) - Traceback (most recent call last): - ... - ValueError: 0 is not in list - - :param value: value in sorted-key list - :param int start: start index (default None, start of sorted-key list) - :param int stop: stop index (default None, end of sorted-key list) - :return: index of value - :raises ValueError: if value is not present - - """ - _len = self._len - - if not _len: - raise ValueError('{0!r} is not in list'.format(value)) - - if start is None: - start = 0 - if start < 0: - start += _len - if start < 0: - start = 0 - - if stop is None: - stop = _len - if stop < 0: - stop += _len - if stop > _len: - stop = _len - - if stop <= start: - raise ValueError('{0!r} is not in list'.format(value)) - - _maxes = self._maxes - key = self._key(value) - pos = bisect_left(_maxes, key) - - if pos == len(_maxes): - raise ValueError('{0!r} is not in list'.format(value)) - - stop -= 1 - _lists = self._lists - _keys = self._keys - idx = bisect_left(_keys[pos], key) - len_keys = len(_keys) - len_sublist = len(_keys[pos]) - - while True: - if _keys[pos][idx] != key: - raise ValueError('{0!r} is not in list'.format(value)) - if _lists[pos][idx] == value: - loc = self._loc(pos, idx) - if start <= loc <= stop: - return loc - elif loc > stop: - break - idx += 1 - if idx == len_sublist: - pos += 1 - if pos == len_keys: - raise ValueError('{0!r} is not in list'.format(value)) - len_sublist = len(_keys[pos]) - idx = 0 - - raise ValueError('{0!r} is not in list'.format(value)) - - - def __add__(self, other): - """Return new sorted-key list containing all values in both sequences. - - ``skl.__add__(other)`` <==> ``skl + other`` - - Values in `other` do not need to be in sorted-key order. - - Runtime complexity: `O(n*log(n))` - - >>> from operator import neg - >>> skl1 = SortedKeyList([5, 4, 3], key=neg) - >>> skl2 = SortedKeyList([2, 1, 0], key=neg) - >>> skl1 + skl2 - SortedKeyList([5, 4, 3, 2, 1, 0], key=) - - :param other: other iterable - :return: new sorted-key list - - """ - values = reduce(iadd, self._lists, []) - values.extend(other) - return self.__class__(values, key=self._key) - - __radd__ = __add__ - - - def __mul__(self, num): - """Return new sorted-key list with `num` shallow copies of values. - - ``skl.__mul__(num)`` <==> ``skl * num`` - - Runtime complexity: `O(n*log(n))` - - >>> from operator import neg - >>> skl = SortedKeyList([3, 2, 1], key=neg) - >>> skl * 2 - SortedKeyList([3, 3, 2, 2, 1, 1], key=) - - :param int num: count of shallow copies - :return: new sorted-key list - - """ - values = reduce(iadd, self._lists, []) * num - return self.__class__(values, key=self._key) - - - def __reduce__(self): - values = reduce(iadd, self._lists, []) - return (type(self), (values, self.key)) - - - @recursive_repr() - def __repr__(self): - """Return string representation of sorted-key list. - - ``skl.__repr__()`` <==> ``repr(skl)`` - - :return: string representation - - """ - type_name = type(self).__name__ - return '{0}({1!r}, key={2!r})'.format(type_name, list(self), self._key) - - - def _check(self): - """Check invariants of sorted-key list. - - Runtime complexity: `O(n)` - - """ - try: - assert self._load >= 4 - assert len(self._maxes) == len(self._lists) == len(self._keys) - assert self._len == sum(len(sublist) for sublist in self._lists) - - # Check all sublists are sorted. - - for sublist in self._keys: - for pos in range(1, len(sublist)): - assert sublist[pos - 1] <= sublist[pos] - - # Check beginning/end of sublists are sorted. - - for pos in range(1, len(self._keys)): - assert self._keys[pos - 1][-1] <= self._keys[pos][0] - - # Check _keys matches _key mapped to _lists. - - for val_sublist, key_sublist in zip(self._lists, self._keys): - assert len(val_sublist) == len(key_sublist) - for val, key in zip(val_sublist, key_sublist): - assert self._key(val) == key - - # Check _maxes index is the last value of each sublist. - - for pos in range(len(self._maxes)): - assert self._maxes[pos] == self._keys[pos][-1] - - # Check sublist lengths are less than double load-factor. - - double = self._load << 1 - assert all(len(sublist) <= double for sublist in self._lists) - - # Check sublist lengths are greater than half load-factor for all - # but the last sublist. - - half = self._load >> 1 - for pos in range(0, len(self._lists) - 1): - assert len(self._lists[pos]) >= half - - if self._index: - assert self._len == self._index[0] - assert len(self._index) == self._offset + len(self._lists) - - # Check index leaf nodes equal length of sublists. - - for pos in range(len(self._lists)): - leaf = self._index[self._offset + pos] - assert leaf == len(self._lists[pos]) - - # Check index branch nodes are the sum of their children. - - for pos in range(self._offset): - child = (pos << 1) + 1 - if child >= len(self._index): - assert self._index[pos] == 0 - elif child + 1 == len(self._index): - assert self._index[pos] == self._index[child] - else: - child_sum = self._index[child] + self._index[child + 1] - assert child_sum == self._index[pos] - except: - traceback.print_exc(file=sys.stdout) - print('len', self._len) - print('load', self._load) - print('offset', self._offset) - print('len_index', len(self._index)) - print('index', self._index) - print('len_maxes', len(self._maxes)) - print('maxes', self._maxes) - print('len_keys', len(self._keys)) - print('keys', self._keys) - print('len_lists', len(self._lists)) - print('lists', self._lists) - raise - - -SortedListWithKey = SortedKeyList diff --git a/apps/bitwarden_event_logs/lib/sortedcontainers/sortedset.py b/apps/bitwarden_event_logs/lib/sortedcontainers/sortedset.py deleted file mode 100755 index be2b8999..00000000 --- a/apps/bitwarden_event_logs/lib/sortedcontainers/sortedset.py +++ /dev/null @@ -1,733 +0,0 @@ -"""Sorted Set -============= - -:doc:`Sorted Containers` is an Apache2 licensed Python sorted -collections library, written in pure-Python, and fast as C-extensions. The -:doc:`introduction` is the best way to get started. - -Sorted set implementations: - -.. currentmodule:: sortedcontainers - -* :class:`SortedSet` - -""" - -from itertools import chain -from operator import eq, ne, gt, ge, lt, le -from textwrap import dedent - -from .sortedlist import SortedList, recursive_repr - -############################################################################### -# BEGIN Python 2/3 Shims -############################################################################### - -try: - from collections.abc import MutableSet, Sequence, Set -except ImportError: - from collections import MutableSet, Sequence, Set - -############################################################################### -# END Python 2/3 Shims -############################################################################### - - -class SortedSet(MutableSet, Sequence): - """Sorted set is a sorted mutable set. - - Sorted set values are maintained in sorted order. The design of sorted set - is simple: sorted set uses a set for set-operations and maintains a sorted - list of values. - - Sorted set values must be hashable and comparable. The hash and total - ordering of values must not change while they are stored in the sorted set. - - Mutable set methods: - - * :func:`SortedSet.__contains__` - * :func:`SortedSet.__iter__` - * :func:`SortedSet.__len__` - * :func:`SortedSet.add` - * :func:`SortedSet.discard` - - Sequence methods: - - * :func:`SortedSet.__getitem__` - * :func:`SortedSet.__delitem__` - * :func:`SortedSet.__reversed__` - - Methods for removing values: - - * :func:`SortedSet.clear` - * :func:`SortedSet.pop` - * :func:`SortedSet.remove` - - Set-operation methods: - - * :func:`SortedSet.difference` - * :func:`SortedSet.difference_update` - * :func:`SortedSet.intersection` - * :func:`SortedSet.intersection_update` - * :func:`SortedSet.symmetric_difference` - * :func:`SortedSet.symmetric_difference_update` - * :func:`SortedSet.union` - * :func:`SortedSet.update` - - Methods for miscellany: - - * :func:`SortedSet.copy` - * :func:`SortedSet.count` - * :func:`SortedSet.__repr__` - * :func:`SortedSet._check` - - Sorted list methods available: - - * :func:`SortedList.bisect_left` - * :func:`SortedList.bisect_right` - * :func:`SortedList.index` - * :func:`SortedList.irange` - * :func:`SortedList.islice` - * :func:`SortedList._reset` - - Additional sorted list methods available, if key-function used: - - * :func:`SortedKeyList.bisect_key_left` - * :func:`SortedKeyList.bisect_key_right` - * :func:`SortedKeyList.irange_key` - - Sorted set comparisons use subset and superset relations. Two sorted sets - are equal if and only if every element of each sorted set is contained in - the other (each is a subset of the other). A sorted set is less than - another sorted set if and only if the first sorted set is a proper subset - of the second sorted set (is a subset, but is not equal). A sorted set is - greater than another sorted set if and only if the first sorted set is a - proper superset of the second sorted set (is a superset, but is not equal). - - """ - def __init__(self, iterable=None, key=None): - """Initialize sorted set instance. - - Optional `iterable` argument provides an initial iterable of values to - initialize the sorted set. - - Optional `key` argument defines a callable that, like the `key` - argument to Python's `sorted` function, extracts a comparison key from - each value. The default, none, compares values directly. - - Runtime complexity: `O(n*log(n))` - - >>> ss = SortedSet([3, 1, 2, 5, 4]) - >>> ss - SortedSet([1, 2, 3, 4, 5]) - >>> from operator import neg - >>> ss = SortedSet([3, 1, 2, 5, 4], neg) - >>> ss - SortedSet([5, 4, 3, 2, 1], key=) - - :param iterable: initial values (optional) - :param key: function used to extract comparison key (optional) - - """ - self._key = key - - # SortedSet._fromset calls SortedSet.__init__ after initializing the - # _set attribute. So only create a new set if the _set attribute is not - # already present. - - if not hasattr(self, '_set'): - self._set = set() - - self._list = SortedList(self._set, key=key) - - # Expose some set methods publicly. - - _set = self._set - self.isdisjoint = _set.isdisjoint - self.issubset = _set.issubset - self.issuperset = _set.issuperset - - # Expose some sorted list methods publicly. - - _list = self._list - self.bisect_left = _list.bisect_left - self.bisect = _list.bisect - self.bisect_right = _list.bisect_right - self.index = _list.index - self.irange = _list.irange - self.islice = _list.islice - self._reset = _list._reset - - if key is not None: - self.bisect_key_left = _list.bisect_key_left - self.bisect_key_right = _list.bisect_key_right - self.bisect_key = _list.bisect_key - self.irange_key = _list.irange_key - - if iterable is not None: - self._update(iterable) - - - @classmethod - def _fromset(cls, values, key=None): - """Initialize sorted set from existing set. - - Used internally by set operations that return a new set. - - """ - sorted_set = object.__new__(cls) - sorted_set._set = values - sorted_set.__init__(key=key) - return sorted_set - - - @property - def key(self): - """Function used to extract comparison key from values. - - Sorted set compares values directly when the key function is none. - - """ - return self._key - - - def __contains__(self, value): - """Return true if `value` is an element of the sorted set. - - ``ss.__contains__(value)`` <==> ``value in ss`` - - Runtime complexity: `O(1)` - - >>> ss = SortedSet([1, 2, 3, 4, 5]) - >>> 3 in ss - True - - :param value: search for value in sorted set - :return: true if `value` in sorted set - - """ - return value in self._set - - - def __getitem__(self, index): - """Lookup value at `index` in sorted set. - - ``ss.__getitem__(index)`` <==> ``ss[index]`` - - Supports slicing. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> ss = SortedSet('abcde') - >>> ss[2] - 'c' - >>> ss[-1] - 'e' - >>> ss[2:5] - ['c', 'd', 'e'] - - :param index: integer or slice for indexing - :return: value or list of values - :raises IndexError: if index out of range - - """ - return self._list[index] - - - def __delitem__(self, index): - """Remove value at `index` from sorted set. - - ``ss.__delitem__(index)`` <==> ``del ss[index]`` - - Supports slicing. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> ss = SortedSet('abcde') - >>> del ss[2] - >>> ss - SortedSet(['a', 'b', 'd', 'e']) - >>> del ss[:2] - >>> ss - SortedSet(['d', 'e']) - - :param index: integer or slice for indexing - :raises IndexError: if index out of range - - """ - _set = self._set - _list = self._list - if isinstance(index, slice): - values = _list[index] - _set.difference_update(values) - else: - value = _list[index] - _set.remove(value) - del _list[index] - - - def __make_cmp(set_op, symbol, doc): - "Make comparator method." - def comparer(self, other): - "Compare method for sorted set and set." - if isinstance(other, SortedSet): - return set_op(self._set, other._set) - elif isinstance(other, Set): - return set_op(self._set, other) - return NotImplemented - - set_op_name = set_op.__name__ - comparer.__name__ = '__{0}__'.format(set_op_name) - doc_str = """Return true if and only if sorted set is {0} `other`. - - ``ss.__{1}__(other)`` <==> ``ss {2} other`` - - Comparisons use subset and superset semantics as with sets. - - Runtime complexity: `O(n)` - - :param other: `other` set - :return: true if sorted set is {0} `other` - - """ - comparer.__doc__ = dedent(doc_str.format(doc, set_op_name, symbol)) - return comparer - - - __eq__ = __make_cmp(eq, '==', 'equal to') - __ne__ = __make_cmp(ne, '!=', 'not equal to') - __lt__ = __make_cmp(lt, '<', 'a proper subset of') - __gt__ = __make_cmp(gt, '>', 'a proper superset of') - __le__ = __make_cmp(le, '<=', 'a subset of') - __ge__ = __make_cmp(ge, '>=', 'a superset of') - __make_cmp = staticmethod(__make_cmp) - - - def __len__(self): - """Return the size of the sorted set. - - ``ss.__len__()`` <==> ``len(ss)`` - - :return: size of sorted set - - """ - return len(self._set) - - - def __iter__(self): - """Return an iterator over the sorted set. - - ``ss.__iter__()`` <==> ``iter(ss)`` - - Iterating the sorted set while adding or deleting values may raise a - :exc:`RuntimeError` or fail to iterate over all values. - - """ - return iter(self._list) - - - def __reversed__(self): - """Return a reverse iterator over the sorted set. - - ``ss.__reversed__()`` <==> ``reversed(ss)`` - - Iterating the sorted set while adding or deleting values may raise a - :exc:`RuntimeError` or fail to iterate over all values. - - """ - return reversed(self._list) - - - def add(self, value): - """Add `value` to sorted set. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> ss = SortedSet() - >>> ss.add(3) - >>> ss.add(1) - >>> ss.add(2) - >>> ss - SortedSet([1, 2, 3]) - - :param value: value to add to sorted set - - """ - _set = self._set - if value not in _set: - _set.add(value) - self._list.add(value) - - _add = add - - - def clear(self): - """Remove all values from sorted set. - - Runtime complexity: `O(n)` - - """ - self._set.clear() - self._list.clear() - - - def copy(self): - """Return a shallow copy of the sorted set. - - Runtime complexity: `O(n)` - - :return: new sorted set - - """ - return self._fromset(set(self._set), key=self._key) - - __copy__ = copy - - - def count(self, value): - """Return number of occurrences of `value` in the sorted set. - - Runtime complexity: `O(1)` - - >>> ss = SortedSet([1, 2, 3, 4, 5]) - >>> ss.count(3) - 1 - - :param value: value to count in sorted set - :return: count - - """ - return 1 if value in self._set else 0 - - - def discard(self, value): - """Remove `value` from sorted set if it is a member. - - If `value` is not a member, do nothing. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> ss = SortedSet([1, 2, 3, 4, 5]) - >>> ss.discard(5) - >>> ss.discard(0) - >>> ss == set([1, 2, 3, 4]) - True - - :param value: `value` to discard from sorted set - - """ - _set = self._set - if value in _set: - _set.remove(value) - self._list.remove(value) - - _discard = discard - - - def pop(self, index=-1): - """Remove and return value at `index` in sorted set. - - Raise :exc:`IndexError` if the sorted set is empty or index is out of - range. - - Negative indices are supported. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> ss = SortedSet('abcde') - >>> ss.pop() - 'e' - >>> ss.pop(2) - 'c' - >>> ss - SortedSet(['a', 'b', 'd']) - - :param int index: index of value (default -1) - :return: value - :raises IndexError: if index is out of range - - """ - # pylint: disable=arguments-differ - value = self._list.pop(index) - self._set.remove(value) - return value - - - def remove(self, value): - """Remove `value` from sorted set; `value` must be a member. - - If `value` is not a member, raise :exc:`KeyError`. - - Runtime complexity: `O(log(n))` -- approximate. - - >>> ss = SortedSet([1, 2, 3, 4, 5]) - >>> ss.remove(5) - >>> ss == set([1, 2, 3, 4]) - True - >>> ss.remove(0) - Traceback (most recent call last): - ... - KeyError: 0 - - :param value: `value` to remove from sorted set - :raises KeyError: if `value` is not in sorted set - - """ - self._set.remove(value) - self._list.remove(value) - - - def difference(self, *iterables): - """Return the difference of two or more sets as a new sorted set. - - The `difference` method also corresponds to operator ``-``. - - ``ss.__sub__(iterable)`` <==> ``ss - iterable`` - - The difference is all values that are in this sorted set but not the - other `iterables`. - - >>> ss = SortedSet([1, 2, 3, 4, 5]) - >>> ss.difference([4, 5, 6, 7]) - SortedSet([1, 2, 3]) - - :param iterables: iterable arguments - :return: new sorted set - - """ - diff = self._set.difference(*iterables) - return self._fromset(diff, key=self._key) - - __sub__ = difference - - - def difference_update(self, *iterables): - """Remove all values of `iterables` from this sorted set. - - The `difference_update` method also corresponds to operator ``-=``. - - ``ss.__isub__(iterable)`` <==> ``ss -= iterable`` - - >>> ss = SortedSet([1, 2, 3, 4, 5]) - >>> _ = ss.difference_update([4, 5, 6, 7]) - >>> ss - SortedSet([1, 2, 3]) - - :param iterables: iterable arguments - :return: itself - - """ - _set = self._set - _list = self._list - values = set(chain(*iterables)) - if (4 * len(values)) > len(_set): - _set.difference_update(values) - _list.clear() - _list.update(_set) - else: - _discard = self._discard - for value in values: - _discard(value) - return self - - __isub__ = difference_update - - - def intersection(self, *iterables): - """Return the intersection of two or more sets as a new sorted set. - - The `intersection` method also corresponds to operator ``&``. - - ``ss.__and__(iterable)`` <==> ``ss & iterable`` - - The intersection is all values that are in this sorted set and each of - the other `iterables`. - - >>> ss = SortedSet([1, 2, 3, 4, 5]) - >>> ss.intersection([4, 5, 6, 7]) - SortedSet([4, 5]) - - :param iterables: iterable arguments - :return: new sorted set - - """ - intersect = self._set.intersection(*iterables) - return self._fromset(intersect, key=self._key) - - __and__ = intersection - __rand__ = __and__ - - - def intersection_update(self, *iterables): - """Update the sorted set with the intersection of `iterables`. - - The `intersection_update` method also corresponds to operator ``&=``. - - ``ss.__iand__(iterable)`` <==> ``ss &= iterable`` - - Keep only values found in itself and all `iterables`. - - >>> ss = SortedSet([1, 2, 3, 4, 5]) - >>> _ = ss.intersection_update([4, 5, 6, 7]) - >>> ss - SortedSet([4, 5]) - - :param iterables: iterable arguments - :return: itself - - """ - _set = self._set - _list = self._list - _set.intersection_update(*iterables) - _list.clear() - _list.update(_set) - return self - - __iand__ = intersection_update - - - def symmetric_difference(self, other): - """Return the symmetric difference with `other` as a new sorted set. - - The `symmetric_difference` method also corresponds to operator ``^``. - - ``ss.__xor__(other)`` <==> ``ss ^ other`` - - The symmetric difference is all values tha are in exactly one of the - sets. - - >>> ss = SortedSet([1, 2, 3, 4, 5]) - >>> ss.symmetric_difference([4, 5, 6, 7]) - SortedSet([1, 2, 3, 6, 7]) - - :param other: `other` iterable - :return: new sorted set - - """ - diff = self._set.symmetric_difference(other) - return self._fromset(diff, key=self._key) - - __xor__ = symmetric_difference - __rxor__ = __xor__ - - - def symmetric_difference_update(self, other): - """Update the sorted set with the symmetric difference with `other`. - - The `symmetric_difference_update` method also corresponds to operator - ``^=``. - - ``ss.__ixor__(other)`` <==> ``ss ^= other`` - - Keep only values found in exactly one of itself and `other`. - - >>> ss = SortedSet([1, 2, 3, 4, 5]) - >>> _ = ss.symmetric_difference_update([4, 5, 6, 7]) - >>> ss - SortedSet([1, 2, 3, 6, 7]) - - :param other: `other` iterable - :return: itself - - """ - _set = self._set - _list = self._list - _set.symmetric_difference_update(other) - _list.clear() - _list.update(_set) - return self - - __ixor__ = symmetric_difference_update - - - def union(self, *iterables): - """Return new sorted set with values from itself and all `iterables`. - - The `union` method also corresponds to operator ``|``. - - ``ss.__or__(iterable)`` <==> ``ss | iterable`` - - >>> ss = SortedSet([1, 2, 3, 4, 5]) - >>> ss.union([4, 5, 6, 7]) - SortedSet([1, 2, 3, 4, 5, 6, 7]) - - :param iterables: iterable arguments - :return: new sorted set - - """ - return self.__class__(chain(iter(self), *iterables), key=self._key) - - __or__ = union - __ror__ = __or__ - - - def update(self, *iterables): - """Update the sorted set adding values from all `iterables`. - - The `update` method also corresponds to operator ``|=``. - - ``ss.__ior__(iterable)`` <==> ``ss |= iterable`` - - >>> ss = SortedSet([1, 2, 3, 4, 5]) - >>> _ = ss.update([4, 5, 6, 7]) - >>> ss - SortedSet([1, 2, 3, 4, 5, 6, 7]) - - :param iterables: iterable arguments - :return: itself - - """ - _set = self._set - _list = self._list - values = set(chain(*iterables)) - if (4 * len(values)) > len(_set): - _list = self._list - _set.update(values) - _list.clear() - _list.update(_set) - else: - _add = self._add - for value in values: - _add(value) - return self - - __ior__ = update - _update = update - - - def __reduce__(self): - """Support for pickle. - - The tricks played with exposing methods in :func:`SortedSet.__init__` - confuse pickle so customize the reducer. - - """ - return (type(self), (self._set, self._key)) - - - @recursive_repr() - def __repr__(self): - """Return string representation of sorted set. - - ``ss.__repr__()`` <==> ``repr(ss)`` - - :return: string representation - - """ - _key = self._key - key = '' if _key is None else ', key={0!r}'.format(_key) - type_name = type(self).__name__ - return '{0}({1!r}{2})'.format(type_name, list(self), key) - - - def _check(self): - """Check invariants of sorted set. - - Runtime complexity: `O(n)` - - """ - _set = self._set - _list = self._list - _list._check() - assert len(_set) == len(_list) - assert all(value in _set for value in _list) diff --git a/apps/bitwarden_event_logs/lib/splunk_sdk-2.1.1.dist-info/INSTALLER b/apps/bitwarden_event_logs/lib/splunk_sdk-2.1.1.dist-info/INSTALLER deleted file mode 100755 index a1b589e3..00000000 --- a/apps/bitwarden_event_logs/lib/splunk_sdk-2.1.1.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/apps/bitwarden_event_logs/lib/splunk_sdk-2.1.1.dist-info/METADATA b/apps/bitwarden_event_logs/lib/splunk_sdk-2.1.1.dist-info/METADATA deleted file mode 100755 index 61191462..00000000 --- a/apps/bitwarden_event_logs/lib/splunk_sdk-2.1.1.dist-info/METADATA +++ /dev/null @@ -1,24 +0,0 @@ -Metadata-Version: 2.4 -Name: splunk-sdk -Version: 2.1.1 -Summary: The Splunk Software Development Kit for Python. -Home-page: http://github.com/splunk/splunk-sdk-python -Author: Splunk, Inc. -Author-email: devinfo@splunk.com -License: http://www.apache.org/licenses/LICENSE-2.0 -Classifier: Programming Language :: Python -Classifier: Development Status :: 6 - Mature -Classifier: Environment :: Other Environment -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: Apache Software License -Classifier: Operating System :: OS Independent -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: Software Development :: Libraries :: Application Frameworks -Requires-Dist: deprecation -Dynamic: author -Dynamic: author-email -Dynamic: classifier -Dynamic: home-page -Dynamic: license -Dynamic: requires-dist -Dynamic: summary diff --git a/apps/bitwarden_event_logs/lib/splunk_sdk-2.1.1.dist-info/RECORD b/apps/bitwarden_event_logs/lib/splunk_sdk-2.1.1.dist-info/RECORD deleted file mode 100755 index 6502ca44..00000000 --- a/apps/bitwarden_event_logs/lib/splunk_sdk-2.1.1.dist-info/RECORD +++ /dev/null @@ -1,33 +0,0 @@ -splunk_sdk-2.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -splunk_sdk-2.1.1.dist-info/METADATA,sha256=MyXVkpgJEVd0UJRmktSvYwREeQovd-CMLD3WbOWzHzs,873 -splunk_sdk-2.1.1.dist-info/RECORD,, -splunk_sdk-2.1.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -splunk_sdk-2.1.1.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91 -splunk_sdk-2.1.1.dist-info/top_level.txt,sha256=taq2YYfOB31tNefwW73s54xytiB_sy8bpbyBAfn0kr8,10 -splunklib/__init__.py,sha256=pjiK7OL2TI19BVzBWu2hM651G3JW1JDUNYE0w_wu7Cw,1251 -splunklib/binding.py,sha256=KyxwcRdMh3RbFwf0qz5QqxUa62LACe-z7bvsTckz7kU,61494 -splunklib/client.py,sha256=F1-vwAGC8hlPGU1TysXECwki3bPfqFok02CPUcJscPA,154488 -splunklib/data.py,sha256=OlS_e7wxBt0k5EaP1kLSybfyrNTCBm3Y0rY8SrTlWbY,8252 -splunklib/modularinput/__init__.py,sha256=wa_qct5JdNcG8VAOnVFPDIjvfSukhMn9c0ApkwT6n8c,401 -splunklib/modularinput/argument.py,sha256=KEAweyWOfteXLcxPZmesoNDfEeT5fW4SBEIWNp-cF8U,4162 -splunklib/modularinput/event.py,sha256=XJ_P6wD2oP00a6uyvGE2QktIcLvKMgFGN4SQ070_YwQ,4433 -splunklib/modularinput/event_writer.py,sha256=A2t-EwevRk54r2HWKh2Cg3hGg-zKXe8ErDYSudm43XE,3664 -splunklib/modularinput/input_definition.py,sha256=8iJ6N7BbO93kHuOC5wVGJIZT_OBuJAt9G_GoUiqDb9I,1778 -splunklib/modularinput/scheme.py,sha256=xZv7a-VJ7NrcLcQT7Yh8LD4MmUWfcLVer52UIna_4aQ,2963 -splunklib/modularinput/script.py,sha256=Rxw7ILvAqoQxfGWD9MKyzm6IHOVxStRVqoIcQfAk9xA,6358 -splunklib/modularinput/utils.py,sha256=RU6yXLcICI6O9jRCGKtBwtBJREdjIUATyPkn-Ttf_zc,2610 -splunklib/modularinput/validation_definition.py,sha256=xtc-rykogtF6pyRRwaI-Ppaq1OTkyJI3JMe8bGHi8SY,2658 -splunklib/results.py,sha256=cI3cq5Pqdz_vdiGM4Bpl92reqJmav7w0qFOy_lgYMzs,12808 -splunklib/searchcommands/__init__.py,sha256=sgIL7b84ywgDuyO_xYoDY2HsSFniUxsR7LWTTs9lpXQ,6244 -splunklib/searchcommands/decorators.py,sha256=aKG0oIoNnKw0HIrhmenqWww-DYp6N1NtzP2s23pnW_E,15913 -splunklib/searchcommands/environment.py,sha256=Sk0Bq-09lqWqGc87BYhcYBE3F-kmpr7zyYPM1PKKhDE,4665 -splunklib/searchcommands/eventing_command.py,sha256=hTfWMdOtr0VEzqKJFdJ7_eF0__rAxfJKLjaNJdP_ad8,5428 -splunklib/searchcommands/external_search_command.py,sha256=OwpO7k8OlrHHHidwwisnEfKpFJpTaLoi4y8PUCHMiq4,8154 -splunklib/searchcommands/generating_command.py,sha256=WxFfgr4flJl2IwVL9j2_8DVIAkSDP2n6j_Q9ICRPYT8,19100 -splunklib/searchcommands/internals.py,sha256=o49_MI14KEgtwx7OJ-KfPc09iPglbRmpr-SS-sjFyfs,27782 -splunklib/searchcommands/reporting_command.py,sha256=aPCABSz0NvRxIX2UdGAZvEGGRV1mitLQf2xlejnveAk,10261 -splunklib/searchcommands/search_command.py,sha256=B4JtDF35WlZLWkEuFPQWTMXaEcPCH72vdAOu5zJAkxY,42090 -splunklib/searchcommands/streaming_command.py,sha256=RJJloaZGd8rQYUUWBLfV156787EzrbrqgPsALjq_KjA,7044 -splunklib/searchcommands/validators.py,sha256=r1jMANPh1IDNax3ClSaVa0SHrGnGHrfKkho1GaM0Mjk,12989 -splunklib/six.py,sha256=AR8CmBFvf0uhnNve3ZW5AHVnL2op8MrCFDb-INKOBxM,34649 -splunklib/utils.py,sha256=PLtvYMmEkap5hir35T9g0NXprvStctO9MmPUntIdUNo,1347 diff --git a/apps/bitwarden_event_logs/lib/splunk_sdk-2.1.1.dist-info/REQUESTED b/apps/bitwarden_event_logs/lib/splunk_sdk-2.1.1.dist-info/REQUESTED deleted file mode 100755 index e69de29b..00000000 diff --git a/apps/bitwarden_event_logs/lib/splunk_sdk-2.1.1.dist-info/WHEEL b/apps/bitwarden_event_logs/lib/splunk_sdk-2.1.1.dist-info/WHEEL deleted file mode 100755 index 1ef55833..00000000 --- a/apps/bitwarden_event_logs/lib/splunk_sdk-2.1.1.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (82.0.0) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/apps/bitwarden_event_logs/lib/splunklib/__init__.py b/apps/bitwarden_event_logs/lib/splunklib/__init__.py deleted file mode 100755 index 3dca1c4c..00000000 --- a/apps/bitwarden_event_logs/lib/splunklib/__init__.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright © 2011-2024 Splunk, Inc. -# -# 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. - -"""Python library for Splunk.""" - -import logging - -DEFAULT_LOG_FORMAT = ( - "%(asctime)s, Level=%(levelname)s, Pid=%(process)s, Logger=%(name)s, File=%(filename)s, " - "Line=%(lineno)s, %(message)s" -) -DEFAULT_DATE_FORMAT = "%Y-%m-%d %H:%M:%S %Z" - - -# To set the logging level of splunklib -# ex. To enable debug logs, call this method with parameter 'logging.DEBUG' -# default logging level is set to 'WARNING' -def setup_logging( - level, log_format=DEFAULT_LOG_FORMAT, date_format=DEFAULT_DATE_FORMAT -): - logging.basicConfig(level=level, format=log_format, datefmt=date_format) - - -__version_info__ = (2, 1, 1) -__version__ = ".".join(map(str, __version_info__)) diff --git a/apps/bitwarden_event_logs/lib/splunklib/__pycache__/__init__.cpython-39.pyc b/apps/bitwarden_event_logs/lib/splunklib/__pycache__/__init__.cpython-39.pyc deleted file mode 100755 index 380acd51031ed6a4dac63d7ab1d632f83a05e3cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 712 zcmYjNO^?$s5KWx4>4z#-uqPy>NGMVxvCX{#M76sGq3(xhIpB+x#%}R$Vn?>Ss8w%p zR^Yu z6y+{4ij&8-*dJ$cC~P^FR?(|VgF=X0ij z+h+`U&LEtD+fyShER};3(iTPC0?N}l{IfcYJ{?8HQAES&T|YYON8>QUnP(O4%--!9 zj^XG-0OMS_YiI4Q5p}C92LO(-}i|uO`^BUN|?!#v@CTp<>pJOC=&!WBn*Kj zmJ~^^m%x&D`SwN#7u*oiE0o@xTd-o{277eKWEDz_K6s(=tG zGeVTz1AcsRIvji)O~~ltEg4>XI2%mxlRbAbn4Ip&ZAW}S$PH7PO93iol90;3l3Y|? uk(QOGjjG!JGst$}2hgw@8ZQe;7dd3z?vU>X{#~o?yZ-=O!NJe~ diff --git a/apps/bitwarden_event_logs/lib/splunklib/__pycache__/binding.cpython-39.pyc b/apps/bitwarden_event_logs/lib/splunklib/__pycache__/binding.cpython-39.pyc deleted file mode 100755 index 4b02f1543473a584d1f53834ff1be612fa85ba03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52842 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 i4;L95zWznMz>K2O}g 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. - -This module handles the wire details of calling the REST API, such as -authentication tokens, prefix paths, URL encoding, and so on. Actual path -segments, ``GET`` and ``POST`` arguments, and the parsing of responses is left -to the user. - -If you want a friendlier interface to the Splunk REST API, use the -:mod:`splunklib.client` module. -""" - -import io -import json -import logging -import socket -import ssl -import time -from base64 import b64encode -from contextlib import contextmanager -from datetime import datetime -from functools import wraps -from io import BytesIO -from urllib import parse -from http import client -from http.cookies import SimpleCookie -from xml.etree.ElementTree import XML, ParseError -from .data import record -from . import __version__ - - -logger = logging.getLogger(__name__) - -__all__ = [ - "AuthenticationError", - "connect", - "Context", - "handler", - "HTTPError", - "UrlEncoded", - "_encode", - "_make_cookie_header", - "_NoAuthenticationToken", - "namespace", -] - -SENSITIVE_KEYS = [ - "Authorization", - "Cookie", - "action.email.auth_password", - "auth", - "auth_password", - "clear_password", - "clientId", - "crc-salt", - "encr_password", - "oldpassword", - "passAuth", - "password", - "session", - "suppressionKey", - "token", -] - -# If you change these, update the docstring -# on _authority as well. -DEFAULT_HOST = "localhost" -DEFAULT_PORT = "8089" -DEFAULT_SCHEME = "https" - - -def _log_duration(f): - @wraps(f) - def new_f(*args, **kwargs): - start_time = datetime.now() - val = f(*args, **kwargs) - end_time = datetime.now() - logger.debug("Operation took %s", end_time - start_time) - return val - - return new_f - - -def mask_sensitive_data(data): - """ - Masked sensitive fields data for logging purpose - """ - if not isinstance(data, dict): - try: - data = json.loads(data) - except Exception as ex: - return data - - # json.loads will return "123"(str) as 123(int), so return the data if it's not 'dict' type - if not isinstance(data, dict): - return data - mdata = {} - for k, v in data.items(): - if k in SENSITIVE_KEYS: - mdata[k] = "******" - else: - mdata[k] = mask_sensitive_data(v) - return mdata - - -def _parse_cookies(cookie_str, dictionary): - """Tries to parse any key-value pairs of cookies in a string, - then updates the the dictionary with any key-value pairs found. - - **Example**:: - - dictionary = {} - _parse_cookies('my=value', dictionary) - # Now the following is True - dictionary['my'] == 'value' - - :param cookie_str: A string containing "key=value" pairs from an HTTP "Set-Cookie" header. - :type cookie_str: ``str`` - :param dictionary: A dictionary to update with any found key-value pairs. - :type dictionary: ``dict`` - """ - parsed_cookie = SimpleCookie(cookie_str) - for cookie in parsed_cookie.values(): - dictionary[cookie.key] = cookie.coded_value - - -def _make_cookie_header(cookies): - """ - Takes a list of 2-tuples of key-value pairs of - cookies, and returns a valid HTTP ``Cookie`` - header. - - **Example**:: - - header = _make_cookie_header([("key", "value"), ("key_2", "value_2")]) - # Now the following is True - header == "key=value; key_2=value_2" - - :param cookies: A list of 2-tuples of cookie key-value pairs. - :type cookies: ``list`` of 2-tuples - :return: ``str` An HTTP header cookie string. - :rtype: ``str`` - """ - return "; ".join(f"{key}={value}" for key, value in cookies) - - -# Singleton values to eschew None -class _NoAuthenticationToken: - """The value stored in a :class:`Context` or :class:`splunklib.client.Service` - class that is not logged in. - - If a ``Context`` or ``Service`` object is created without an authentication - token, and there has not yet been a call to the ``login`` method, the token - field of the ``Context`` or ``Service`` object is set to - ``_NoAuthenticationToken``. - - Likewise, after a ``Context`` or ``Service`` object has been logged out, the - token is set to this value again. - """ - - -class UrlEncoded(str): - """This class marks URL-encoded strings. - It should be considered an SDK-private implementation detail. - - Manually tracking whether strings are URL encoded can be difficult. Avoid - calling ``urllib.quote`` to replace special characters with escapes. When - you receive a URL-encoded string, *do* use ``urllib.unquote`` to replace - escapes with single characters. Then, wrap any string you want to use as a - URL in ``UrlEncoded``. Note that because the ``UrlEncoded`` class is - idempotent, making multiple calls to it is OK. - - ``UrlEncoded`` objects are identical to ``str`` objects (including being - equal if their contents are equal) except when passed to ``UrlEncoded`` - again. - - ``UrlEncoded`` removes the ``str`` type support for interpolating values - with ``%`` (doing that raises a ``TypeError``). There is no reliable way to - encode values this way, so instead, interpolate into a string, quoting by - hand, and call ``UrlEncode`` with ``skip_encode=True``. - - **Example**:: - - import urllib - UrlEncoded(f'{scheme}://{urllib.quote(host)}', skip_encode=True) - - If you append ``str`` strings and ``UrlEncoded`` strings, the result is also - URL encoded. - - **Example**:: - - UrlEncoded('ab c') + 'de f' == UrlEncoded('ab cde f') - 'ab c' + UrlEncoded('de f') == UrlEncoded('ab cde f') - """ - - def __new__(self, val="", skip_encode=False, encode_slash=False): - if isinstance(val, UrlEncoded): - # Don't urllib.quote something already URL encoded. - return val - if skip_encode: - return str.__new__(self, val) - if encode_slash: - return str.__new__(self, parse.quote_plus(val)) - # When subclassing str, just call str.__new__ method - # with your class and the value you want to have in the - # new string. - return str.__new__(self, parse.quote(val)) - - def __add__(self, other): - """self + other - - If *other* is not a ``UrlEncoded``, URL encode it before - adding it. - """ - if isinstance(other, UrlEncoded): - return UrlEncoded(str.__add__(self, other), skip_encode=True) - - return UrlEncoded(str.__add__(self, parse.quote(other)), skip_encode=True) - - def __radd__(self, other): - """other + self - - If *other* is not a ``UrlEncoded``, URL _encode it before - adding it. - """ - if isinstance(other, UrlEncoded): - return UrlEncoded(str.__radd__(self, other), skip_encode=True) - - return UrlEncoded(str.__add__(parse.quote(other), self), skip_encode=True) - - def __mod__(self, fields): - """Interpolation into ``UrlEncoded``s is disabled. - - If you try to write ``UrlEncoded("%s") % "abc", will get a - ``TypeError``. - """ - raise TypeError("Cannot interpolate into a UrlEncoded object.") - - def __repr__(self): - return f"UrlEncoded({repr(parse.unquote(str(self)))})" - - -@contextmanager -def _handle_auth_error(msg): - """Handle re-raising HTTP authentication errors as something clearer. - - If an ``HTTPError`` is raised with status 401 (access denied) in - the body of this context manager, re-raise it as an - ``AuthenticationError`` instead, with *msg* as its message. - - This function adds no round trips to the server. - - :param msg: The message to be raised in ``AuthenticationError``. - :type msg: ``str`` - - **Example**:: - - with _handle_auth_error("Your login failed."): - ... # make an HTTP request - """ - try: - yield - except HTTPError as he: - if he.status == 401: - raise AuthenticationError(msg, he) - else: - raise - - -def _authentication(request_fun): - """Decorator to handle autologin and authentication errors. - - *request_fun* is a function taking no arguments that needs to - be run with this ``Context`` logged into Splunk. - - ``_authentication``'s behavior depends on whether the - ``autologin`` field of ``Context`` is set to ``True`` or - ``False``. If it's ``False``, then ``_authentication`` - aborts if the ``Context`` is not logged in, and raises an - ``AuthenticationError`` if an ``HTTPError`` of status 401 is - raised in *request_fun*. If it's ``True``, then - ``_authentication`` will try at all sensible places to - log in before issuing the request. - - If ``autologin`` is ``False``, ``_authentication`` makes - one roundtrip to the server if the ``Context`` is logged in, - or zero if it is not. If ``autologin`` is ``True``, it's less - deterministic, and may make at most three roundtrips (though - that would be a truly pathological case). - - :param request_fun: A function of no arguments encapsulating - the request to make to the server. - - **Example**:: - - import splunklib.binding as binding - c = binding.connect(..., autologin=True) - c.logout() - def f(): - c.get("/services") - return 42 - print(_authentication(f)) - """ - - @wraps(request_fun) - def wrapper(self, *args, **kwargs): - if self.token is _NoAuthenticationToken and not self.has_cookies(): - # Not yet logged in. - if self.autologin and self.username and self.password: - # This will throw an uncaught - # AuthenticationError if it fails. - self.login() - else: - # Try the request anyway without authentication. - # Most requests will fail. Some will succeed, such as - # 'GET server/info'. - with _handle_auth_error("Request aborted: not logged in."): - return request_fun(self, *args, **kwargs) - try: - # Issue the request - return request_fun(self, *args, **kwargs) - except HTTPError as he: - if he.status == 401 and self.autologin: - # Authentication failed. Try logging in, and then - # rerunning the request. If either step fails, throw - # an AuthenticationError and give up. - with _handle_auth_error("Autologin failed."): - self.login() - with _handle_auth_error( - "Authentication Failed! If session token is used, it seems to have been expired." - ): - return request_fun(self, *args, **kwargs) - elif he.status == 401 and not self.autologin: - raise AuthenticationError( - "Request failed: Session is not logged in.", he - ) - else: - raise - - return wrapper - - -def _authority(scheme=DEFAULT_SCHEME, host=DEFAULT_HOST, port=DEFAULT_PORT): - """Construct a URL authority from the given *scheme*, *host*, and *port*. - - Named in accordance with RFC2396_, which defines URLs as:: - - ://? - - .. _RFC2396: http://www.ietf.org/rfc/rfc2396.txt - - So ``https://localhost:8000/a/b/b?boris=hilda`` would be parsed as:: - - scheme := https - authority := localhost:8000 - path := /a/b/c - query := boris=hilda - - :param scheme: URL scheme (the default is "https") - :type scheme: "http" or "https" - :param host: The host name (the default is "localhost") - :type host: string - :param port: The port number (the default is 8089) - :type port: integer - :return: The URL authority. - :rtype: UrlEncoded (subclass of ``str``) - - **Example**:: - - _authority() == "https://localhost:8089" - - _authority(host="splunk.utopia.net") == "https://splunk.utopia.net:8089" - - _authority(host="2001:0db8:85a3:0000:0000:8a2e:0370:7334") == \ - "https://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8089" - - _authority(scheme="http", host="splunk.utopia.net", port="471") == \ - "http://splunk.utopia.net:471" - - """ - # check if host is an IPv6 address and not enclosed in [ ] - if ":" in host and not (host.startswith("[") and host.endswith("]")): - # IPv6 addresses must be enclosed in [ ] in order to be well - # formed. - host = "[" + host + "]" - return UrlEncoded(f"{scheme}://{host}:{port}", skip_encode=True) - - -# kwargs: sharing, owner, app -def namespace(sharing=None, owner=None, app=None, **kwargs): - """This function constructs a Splunk namespace. - - Every Splunk resource belongs to a namespace. The namespace is specified by - the pair of values ``owner`` and ``app`` and is governed by a ``sharing`` mode. - The possible values for ``sharing`` are: "user", "app", "global" and "system", - which map to the following combinations of ``owner`` and ``app`` values: - - "user" => {owner}, {app} - - "app" => nobody, {app} - - "global" => nobody, {app} - - "system" => nobody, system - - "nobody" is a special user name that basically means no user, and "system" - is the name reserved for system resources. - - "-" is a wildcard that can be used for both ``owner`` and ``app`` values and - refers to all users and all apps, respectively. - - In general, when you specify a namespace you can specify any combination of - these three values and the library will reconcile the triple, overriding the - provided values as appropriate. - - Finally, if no namespacing is specified the library will make use of the - ``/services`` branch of the REST API, which provides a namespaced view of - Splunk resources equivelent to using ``owner={currentUser}`` and - ``app={defaultApp}``. - - The ``namespace`` function returns a representation of the namespace from - reconciling the values you provide. It ignores any keyword arguments other - than ``owner``, ``app``, and ``sharing``, so you can provide ``dicts`` of - configuration information without first having to extract individual keys. - - :param sharing: The sharing mode (the default is "user"). - :type sharing: "system", "global", "app", or "user" - :param owner: The owner context (the default is "None"). - :type owner: ``string`` - :param app: The app context (the default is "None"). - :type app: ``string`` - :returns: A :class:`splunklib.data.Record` containing the reconciled - namespace. - - **Example**:: - - import splunklib.binding as binding - n = binding.namespace(sharing="user", owner="boris", app="search") - n = binding.namespace(sharing="global", app="search") - """ - if sharing in ["system"]: - return record({"sharing": sharing, "owner": "nobody", "app": "system"}) - if sharing in ["global", "app"]: - return record({"sharing": sharing, "owner": "nobody", "app": app}) - if sharing in ["user", None]: - return record({"sharing": sharing, "owner": owner, "app": app}) - raise ValueError("Invalid value for argument: 'sharing'") - - -class Context: - """This class represents a context that encapsulates a splunkd connection. - - The ``Context`` class encapsulates the details of HTTP requests, - authentication, a default namespace, and URL prefixes to simplify access to - the REST API. - - After creating a ``Context`` object, you must call its :meth:`login` - method before you can issue requests to splunkd. Or, use the :func:`connect` - function to create an already-authenticated ``Context`` object. You can - provide a session token explicitly (the same token can be shared by multiple - ``Context`` objects) to provide authentication. - - :param host: The host name (the default is "localhost"). - :type host: ``string`` - :param port: The port number (the default is 8089). - :type port: ``integer`` - :param scheme: The scheme for accessing the service (the default is "https"). - :type scheme: "https" or "http" - :param verify: Enable (True) or disable (False) SSL verification for https connections. - :type verify: ``Boolean`` - :param self_signed_certificate: Specifies if self signed certificate is used - :type self_signed_certificate: ``Boolean`` - :param sharing: The sharing mode for the namespace (the default is "user"). - :type sharing: "global", "system", "app", or "user" - :param owner: The owner context of the namespace (optional, the default is "None"). - :type owner: ``string`` - :param app: The app context of the namespace (optional, the default is "None"). - :type app: ``string`` - :param token: A session token. When provided, you don't need to call :meth:`login`. - :type token: ``string`` - :param cookie: A session cookie. When provided, you don't need to call :meth:`login`. - This parameter is only supported for Splunk 6.2+. - :type cookie: ``string`` - :param username: The Splunk account username, which is used to - authenticate the Splunk instance. - :type username: ``string`` - :param password: The password for the Splunk account. - :type password: ``string`` - :param splunkToken: Splunk authentication token - :type splunkToken: ``string`` - :param headers: List of extra HTTP headers to send (optional). - :type headers: ``list`` of 2-tuples. - :param retires: Number of retries for each HTTP connection (optional, the default is 0). - NOTE THAT THIS MAY INCREASE THE NUMBER OF ROUND TRIP CONNECTIONS TO THE SPLUNK SERVER AND BLOCK THE - CURRENT THREAD WHILE RETRYING. - :type retries: ``int`` - :param retryDelay: How long to wait between connection attempts if `retries` > 0 (optional, defaults to 10s). - :type retryDelay: ``int`` (in seconds) - :param handler: The HTTP request handler (optional). - :returns: A ``Context`` instance. - - **Example**:: - - import splunklib.binding as binding - c = binding.Context(username="boris", password="natasha", ...) - c.login() - # Or equivalently - c = binding.connect(username="boris", password="natasha") - # Or if you already have a session token - c = binding.Context(token="atg232342aa34324a") - # Or if you already have a valid cookie - c = binding.Context(cookie="splunkd_8089=...") - """ - - def __init__(self, handler=None, **kwargs): - self.http = HttpLib( - handler, - kwargs.get("verify", False), - key_file=kwargs.get("key_file"), - cert_file=kwargs.get("cert_file"), - context=kwargs.get("context"), - # Default to False for backward compat - retries=kwargs.get("retries", 0), - retryDelay=kwargs.get("retryDelay", 10), - ) - self.token = kwargs.get("token", _NoAuthenticationToken) - if self.token is None: # In case someone explicitly passes token=None - self.token = _NoAuthenticationToken - self.scheme = kwargs.get("scheme", DEFAULT_SCHEME) - self.host = kwargs.get("host", DEFAULT_HOST) - self.port = int(kwargs.get("port", DEFAULT_PORT)) - self.authority = _authority(self.scheme, self.host, self.port) - self.namespace = namespace(**kwargs) - self.username = kwargs.get("username", "") - self.password = kwargs.get("password", "") - self.basic = kwargs.get("basic", False) - self.bearerToken = kwargs.get("splunkToken", "") - self.autologin = kwargs.get("autologin", False) - self.additional_headers = kwargs.get("headers", []) - self._self_signed_certificate = kwargs.get("self_signed_certificate", True) - - # Store any cookies in the self.http._cookies dict - if "cookie" in kwargs and kwargs["cookie"] not in [ - None, - _NoAuthenticationToken, - ]: - _parse_cookies(kwargs["cookie"], self.http._cookies) - - def get_cookies(self): - """Gets the dictionary of cookies from the ``HttpLib`` member of this instance. - - :return: Dictionary of cookies stored on the ``self.http``. - :rtype: ``dict`` - """ - return self.http._cookies - - def has_cookies(self): - """Returns true if the ``HttpLib`` member of this instance has auth token stored. - - :return: ``True`` if there is auth token present, else ``False`` - :rtype: ``bool`` - """ - auth_token_key = "splunkd_" - return any(auth_token_key in key for key in self.get_cookies().keys()) - - # Shared per-context request headers - @property - def _auth_headers(self): - """Headers required to authenticate a request. - - Assumes your ``Context`` already has a authentication token or - cookie, either provided explicitly or obtained by logging - into the Splunk instance. - - :returns: A list of 2-tuples containing key and value - """ - header = [] - if self.has_cookies(): - return [("Cookie", _make_cookie_header(list(self.get_cookies().items())))] - elif self.basic and (self.username and self.password): - token = f"Basic {b64encode(('%s:%s' % (self.username, self.password)).encode('utf-8')).decode('ascii')}" - elif self.bearerToken: - token = f"Bearer {self.bearerToken}" - elif self.token is _NoAuthenticationToken: - token = [] - else: - # Ensure the token is properly formatted - if self.token.startswith("Splunk "): - token = self.token - else: - token = f"Splunk {self.token}" - if token: - header.append(("Authorization", token)) - if self.get_cookies(): - header.append( - ("Cookie", _make_cookie_header(list(self.get_cookies().items()))) - ) - - return header - - def connect(self): - """Returns an open connection (socket) to the Splunk instance. - - This method is used for writing bulk events to an index or similar tasks - where the overhead of opening a connection multiple times would be - prohibitive. - - :returns: A socket. - - **Example**:: - - import splunklib.binding as binding - c = binding.connect(...) - socket = c.connect() - socket.write("POST %s HTTP/1.1\\r\\n" % "some/path/to/post/to") - socket.write("Host: %s:%s\\r\\n" % (c.host, c.port)) - socket.write("Accept-Encoding: identity\\r\\n") - socket.write("Authorization: %s\\r\\n" % c.token) - socket.write("X-Splunk-Input-Mode: Streaming\\r\\n") - socket.write("\\r\\n") - """ - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - if self.scheme == "https": - context = ssl.create_default_context() - context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 - context.check_hostname = not self._self_signed_certificate - context.verify_mode = ( - ssl.CERT_NONE if self._self_signed_certificate else ssl.CERT_REQUIRED - ) - sock = context.wrap_socket(sock, server_hostname=self.host) - sock.connect((socket.gethostbyname(self.host), self.port)) - return sock - - @_authentication - @_log_duration - def delete(self, path_segment, owner=None, app=None, sharing=None, **query): - """Performs a DELETE operation at the REST path segment with the given - namespace and query. - - This method is named to match the HTTP method. ``delete`` makes at least - one round trip to the server, one additional round trip for each 303 - status returned, and at most two additional round trips if - the ``autologin`` field of :func:`connect` is set to ``True``. - - If *owner*, *app*, and *sharing* are omitted, this method uses the - default :class:`Context` namespace. All other keyword arguments are - included in the URL as query parameters. - - :raises AuthenticationError: Raised when the ``Context`` object is not - logged in. - :raises HTTPError: Raised when an error occurred in a GET operation from - *path_segment*. - :param path_segment: A REST path segment. - :type path_segment: ``string`` - :param owner: The owner context of the namespace (optional). - :type owner: ``string`` - :param app: The app context of the namespace (optional). - :type app: ``string`` - :param sharing: The sharing mode of the namespace (optional). - :type sharing: ``string`` - :param query: All other keyword arguments, which are used as query - parameters. - :type query: ``string`` - :return: The response from the server. - :rtype: ``dict`` with keys ``body``, ``headers``, ``reason``, - and ``status`` - - **Example**:: - - c = binding.connect(...) - c.delete('saved/searches/boris') == \\ - {'body': ...a response reader object..., - 'headers': [('content-length', '1786'), - ('expires', 'Fri, 30 Oct 1998 00:00:00 GMT'), - ('server', 'Splunkd'), - ('connection', 'close'), - ('cache-control', 'no-store, max-age=0, must-revalidate, no-cache'), - ('date', 'Fri, 11 May 2012 16:53:06 GMT'), - ('content-type', 'text/xml; charset=utf-8')], - 'reason': 'OK', - 'status': 200} - c.delete('nonexistant/path') # raises HTTPError - c.logout() - c.delete('apps/local') # raises AuthenticationError - """ - path = self.authority + self._abspath( - path_segment, owner=owner, app=app, sharing=sharing - ) - logger.debug( - "DELETE request to %s (body: %s)", path, mask_sensitive_data(query) - ) - response = self.http.delete(path, self._auth_headers, **query) - return response - - @_authentication - @_log_duration - def get( - self, path_segment, owner=None, app=None, headers=None, sharing=None, **query - ): - """Performs a GET operation from the REST path segment with the given - namespace and query. - - This method is named to match the HTTP method. ``get`` makes at least - one round trip to the server, one additional round trip for each 303 - status returned, and at most two additional round trips if - the ``autologin`` field of :func:`connect` is set to ``True``. - - If *owner*, *app*, and *sharing* are omitted, this method uses the - default :class:`Context` namespace. All other keyword arguments are - included in the URL as query parameters. - - :raises AuthenticationError: Raised when the ``Context`` object is not - logged in. - :raises HTTPError: Raised when an error occurred in a GET operation from - *path_segment*. - :param path_segment: A REST path segment. - :type path_segment: ``string`` - :param owner: The owner context of the namespace (optional). - :type owner: ``string`` - :param app: The app context of the namespace (optional). - :type app: ``string`` - :param headers: List of extra HTTP headers to send (optional). - :type headers: ``list`` of 2-tuples. - :param sharing: The sharing mode of the namespace (optional). - :type sharing: ``string`` - :param query: All other keyword arguments, which are used as query - parameters. - :type query: ``string`` - :return: The response from the server. - :rtype: ``dict`` with keys ``body``, ``headers``, ``reason``, - and ``status`` - - **Example**:: - - c = binding.connect(...) - c.get('apps/local') == \\ - {'body': ...a response reader object..., - 'headers': [('content-length', '26208'), - ('expires', 'Fri, 30 Oct 1998 00:00:00 GMT'), - ('server', 'Splunkd'), - ('connection', 'close'), - ('cache-control', 'no-store, max-age=0, must-revalidate, no-cache'), - ('date', 'Fri, 11 May 2012 16:30:35 GMT'), - ('content-type', 'text/xml; charset=utf-8')], - 'reason': 'OK', - 'status': 200} - c.get('nonexistant/path') # raises HTTPError - c.logout() - c.get('apps/local') # raises AuthenticationError - """ - if headers is None: - headers = [] - - path = self.authority + self._abspath( - path_segment, owner=owner, app=app, sharing=sharing - ) - logger.debug("GET request to %s (body: %s)", path, mask_sensitive_data(query)) - all_headers = headers + self.additional_headers + self._auth_headers - response = self.http.get(path, all_headers, **query) - return response - - @_authentication - @_log_duration - def post( - self, path_segment, owner=None, app=None, sharing=None, headers=None, **query - ): - """Performs a POST operation from the REST path segment with the given - namespace and query. - - This method is named to match the HTTP method. ``post`` makes at least - one round trip to the server, one additional round trip for each 303 - status returned, and at most two additional round trips if - the ``autologin`` field of :func:`connect` is set to ``True``. - - If *owner*, *app*, and *sharing* are omitted, this method uses the - default :class:`Context` namespace. All other keyword arguments are - included in the URL as query parameters. - - Some of Splunk's endpoints, such as ``receivers/simple`` and - ``receivers/stream``, require unstructured data in the POST body - and all metadata passed as GET-style arguments. If you provide - a ``body`` argument to ``post``, it will be used as the POST - body, and all other keyword arguments will be passed as - GET-style arguments in the URL. - - :raises AuthenticationError: Raised when the ``Context`` object is not - logged in. - :raises HTTPError: Raised when an error occurred in a GET operation from - *path_segment*. - :param path_segment: A REST path segment. - :type path_segment: ``string`` - :param owner: The owner context of the namespace (optional). - :type owner: ``string`` - :param app: The app context of the namespace (optional). - :type app: ``string`` - :param sharing: The sharing mode of the namespace (optional). - :type sharing: ``string`` - :param headers: List of extra HTTP headers to send (optional). - :type headers: ``list`` of 2-tuples. - :param query: All other keyword arguments, which are used as query - parameters. - :param body: Parameters to be used in the post body. If specified, - any parameters in the query will be applied to the URL instead of - the body. If a dict is supplied, the key-value pairs will be form - encoded. If a string is supplied, the body will be passed through - in the request unchanged. - :type body: ``dict`` or ``str`` - :return: The response from the server. - :rtype: ``dict`` with keys ``body``, ``headers``, ``reason``, - and ``status`` - - **Example**:: - - c = binding.connect(...) - c.post('saved/searches', name='boris', - search='search * earliest=-1m | head 1') == \\ - {'body': ...a response reader object..., - 'headers': [('content-length', '10455'), - ('expires', 'Fri, 30 Oct 1998 00:00:00 GMT'), - ('server', 'Splunkd'), - ('connection', 'close'), - ('cache-control', 'no-store, max-age=0, must-revalidate, no-cache'), - ('date', 'Fri, 11 May 2012 16:46:06 GMT'), - ('content-type', 'text/xml; charset=utf-8')], - 'reason': 'Created', - 'status': 201} - c.post('nonexistant/path') # raises HTTPError - c.logout() - # raises AuthenticationError: - c.post('saved/searches', name='boris', - search='search * earliest=-1m | head 1') - """ - if headers is None: - headers = [] - - path = self.authority + self._abspath( - path_segment, owner=owner, app=app, sharing=sharing - ) - - logger.debug("POST request to %s (body: %s)", path, mask_sensitive_data(query)) - all_headers = headers + self.additional_headers + self._auth_headers - response = self.http.post(path, all_headers, **query) - return response - - @_authentication - @_log_duration - def request( - self, - path_segment, - method="GET", - headers=None, - body={}, - owner=None, - app=None, - sharing=None, - ): - """Issues an arbitrary HTTP request to the REST path segment. - - This method is named to match ``httplib.request``. This function - makes a single round trip to the server. - - If *owner*, *app*, and *sharing* are omitted, this method uses the - default :class:`Context` namespace. All other keyword arguments are - included in the URL as query parameters. - - :raises AuthenticationError: Raised when the ``Context`` object is not - logged in. - :raises HTTPError: Raised when an error occurred in a GET operation from - *path_segment*. - :param path_segment: A REST path segment. - :type path_segment: ``string`` - :param method: The HTTP method to use (optional). - :type method: ``string`` - :param headers: List of extra HTTP headers to send (optional). - :type headers: ``list`` of 2-tuples. - :param body: Content of the HTTP request (optional). - :type body: ``string`` - :param owner: The owner context of the namespace (optional). - :type owner: ``string`` - :param app: The app context of the namespace (optional). - :type app: ``string`` - :param sharing: The sharing mode of the namespace (optional). - :type sharing: ``string`` - :return: The response from the server. - :rtype: ``dict`` with keys ``body``, ``headers``, ``reason``, - and ``status`` - - **Example**:: - - c = binding.connect(...) - c.request('saved/searches', method='GET') == \\ - {'body': ...a response reader object..., - 'headers': [('content-length', '46722'), - ('expires', 'Fri, 30 Oct 1998 00:00:00 GMT'), - ('server', 'Splunkd'), - ('connection', 'close'), - ('cache-control', 'no-store, max-age=0, must-revalidate, no-cache'), - ('date', 'Fri, 11 May 2012 17:24:19 GMT'), - ('content-type', 'text/xml; charset=utf-8')], - 'reason': 'OK', - 'status': 200} - c.request('nonexistant/path', method='GET') # raises HTTPError - c.logout() - c.get('apps/local') # raises AuthenticationError - """ - if headers is None: - headers = [] - - path = self.authority + self._abspath( - path_segment, owner=owner, app=app, sharing=sharing - ) - - all_headers = headers + self.additional_headers + self._auth_headers - logger.debug( - "%s request to %s (headers: %s, body: %s)", - method, - path, - str(mask_sensitive_data(dict(all_headers))), - mask_sensitive_data(body), - ) - if body: - body = _encode(**body) - - if method == "GET": - path = path + UrlEncoded("?" + body, skip_encode=True) - message = {"method": method, "headers": all_headers} - else: - message = {"method": method, "headers": all_headers, "body": body} - else: - message = {"method": method, "headers": all_headers} - - response = self.http.request(path, message) - - return response - - def login(self): - """Logs into the Splunk instance referred to by the :class:`Context` - object. - - Unless a ``Context`` is created with an explicit authentication token - (probably obtained by logging in from a different ``Context`` object) - you must call :meth:`login` before you can issue requests. - The authentication token obtained from the server is stored in the - ``token`` field of the ``Context`` object. - - :raises AuthenticationError: Raised when login fails. - :returns: The ``Context`` object, so you can chain calls. - - **Example**:: - - import splunklib.binding as binding - c = binding.Context(...).login() - # Then issue requests... - """ - - if self.has_cookies() and (not self.username and not self.password): - # If we were passed session cookie(s), but no username or - # password, then login is a nop, since we're automatically - # logged in. - return - - if self.token is not _NoAuthenticationToken and ( - not self.username and not self.password - ): - # If we were passed a session token, but no username or - # password, then login is a nop, since we're automatically - # logged in. - return - - if self.basic and (self.username and self.password): - # Basic auth mode requested, so this method is a nop as long - # as credentials were passed in. - return - - if self.bearerToken: - # Bearer auth mode requested, so this method is a nop as long - # as authentication token was passed in. - return - # Only try to get a token and updated cookie if username & password are specified - try: - response = self.http.post( - self.authority + self._abspath("/services/auth/login"), - username=self.username, - password=self.password, - headers=self.additional_headers, - cookie="1", - ) # In Splunk 6.2+, passing "cookie=1" will return the "set-cookie" header - - body = response.body.read() - session = XML(body).findtext("./sessionKey") - self.token = f"Splunk {session}" - return self - except HTTPError as he: - if he.status == 401: - raise AuthenticationError("Login failed.", he) - else: - raise - - def logout(self): - """Forgets the current session token, and cookies.""" - self.token = _NoAuthenticationToken - self.http._cookies = {} - return self - - def _abspath(self, path_segment, owner=None, app=None, sharing=None): - """Qualifies *path_segment* into an absolute path for a URL. - - If *path_segment* is already absolute, returns it unchanged. - If *path_segment* is relative, then qualifies it with either - the provided namespace arguments or the ``Context``'s default - namespace. Any forbidden characters in *path_segment* are URL - encoded. This function has no network activity. - - Named to be consistent with RFC2396_. - - .. _RFC2396: http://www.ietf.org/rfc/rfc2396.txt - - :param path_segment: A relative or absolute URL path segment. - :type path_segment: ``string`` - :param owner, app, sharing: Components of a namespace (defaults - to the ``Context``'s namespace if all - three are omitted) - :type owner, app, sharing: ``string`` - :return: A ``UrlEncoded`` (a subclass of ``str``). - :rtype: ``string`` - - **Example**:: - - import splunklib.binding as binding - c = binding.connect(owner='boris', app='search', sharing='user') - c._abspath('/a/b/c') == '/a/b/c' - c._abspath('/a/b c/d') == '/a/b%20c/d' - c._abspath('apps/local/search') == \ - '/servicesNS/boris/search/apps/local/search' - c._abspath('apps/local/search', sharing='system') == \ - '/servicesNS/nobody/system/apps/local/search' - url = c.authority + c._abspath('apps/local/sharing') - """ - skip_encode = isinstance(path_segment, UrlEncoded) - # If path_segment is absolute, escape all forbidden characters - # in it and return it. - if path_segment.startswith("/"): - return UrlEncoded(path_segment, skip_encode=skip_encode) - - # path_segment is relative, so we need a namespace to build an - # absolute path. - if owner or app or sharing: - ns = namespace(owner=owner, app=app, sharing=sharing) - else: - ns = self.namespace - - # If no app or owner are specified, then use the /services - # endpoint. Otherwise, use /servicesNS with the specified - # namespace. If only one of app and owner is specified, use - # '-' for the other. - if ns.app is None and ns.owner is None: - return UrlEncoded(f"/services/{path_segment}", skip_encode=skip_encode) - - oname = "nobody" if ns.owner is None else ns.owner - aname = "system" if ns.app is None else ns.app - path = UrlEncoded( - f"/servicesNS/{oname}/{aname}/{path_segment}", skip_encode=skip_encode - ) - return path - - -def connect(**kwargs): - """This function returns an authenticated :class:`Context` object. - - This function is a shorthand for calling :meth:`Context.login`. - - This function makes one round trip to the server. - - :param host: The host name (the default is "localhost"). - :type host: ``string`` - :param port: The port number (the default is 8089). - :type port: ``integer`` - :param scheme: The scheme for accessing the service (the default is "https"). - :type scheme: "https" or "http" - :param owner: The owner context of the namespace (the default is "None"). - :type owner: ``string`` - :param app: The app context of the namespace (the default is "None"). - :type app: ``string`` - :param sharing: The sharing mode for the namespace (the default is "user"). - :type sharing: "global", "system", "app", or "user" - :param token: The current session token (optional). Session tokens can be - shared across multiple service instances. - :type token: ``string`` - :param cookie: A session cookie. When provided, you don't need to call :meth:`login`. - This parameter is only supported for Splunk 6.2+. - :type cookie: ``string`` - :param username: The Splunk account username, which is used to - authenticate the Splunk instance. - :type username: ``string`` - :param password: The password for the Splunk account. - :type password: ``string`` - :param headers: List of extra HTTP headers to send (optional). - :type headers: ``list`` of 2-tuples. - :param autologin: When ``True``, automatically tries to log in again if the - session terminates. - :type autologin: ``Boolean`` - :return: An initialized :class:`Context` instance. - - **Example**:: - - import splunklib.binding as binding - c = binding.connect(...) - response = c.get("apps/local") - """ - c = Context(**kwargs) - c.login() - return c - - -# Note: the error response schema supports multiple messages but we only -# return the first, although we do return the body so that an exception -# handler that wants to read multiple messages can do so. -class HTTPError(Exception): - """This exception is raised for HTTP responses that return an error.""" - - def __init__(self, response, _message=None): - status = response.status - reason = response.reason - body = response.body.read() - try: - detail = XML(body).findtext("./messages/msg") - except ParseError: - detail = body - detail_formatted = "" if detail is None else f" -- {detail}" - message = f"HTTP {status} {reason}{detail_formatted}" - Exception.__init__(self, _message or message) - self.status = status - self.reason = reason - self.headers = response.headers - self.body = body - self._response = response - - -class AuthenticationError(HTTPError): - """Raised when a login request to Splunk fails. - - If your username was unknown or you provided an incorrect password - in a call to :meth:`Context.login` or :meth:`splunklib.client.Service.login`, - this exception is raised. - """ - - def __init__(self, message, cause): - # Put the body back in the response so that HTTPError's constructor can - # read it again. - cause._response.body = BytesIO(cause.body) - - HTTPError.__init__(self, cause._response, message) - - -# -# The HTTP interface used by the Splunk binding layer abstracts the underlying -# HTTP library using request & response 'messages' which are implemented as -# dictionaries with the following structure: -# -# # HTTP request message (only method required) -# request { -# method : str, -# headers? : [(str, str)*], -# body? : str, -# } -# -# # HTTP response message (all keys present) -# response { -# status : int, -# reason : str, -# headers : [(str, str)*], -# body : file, -# } -# - - -# Encode the given kwargs as a query string. This wrapper will also _encode -# a list value as a sequence of assignments to the corresponding arg name, -# for example an argument such as 'foo=[1,2,3]' will be encoded as -# 'foo=1&foo=2&foo=3'. -def _encode(**kwargs): - items = [] - for key, value in kwargs.items(): - if isinstance(value, list): - items.extend([(key, item) for item in value]) - else: - items.append((key, value)) - return parse.urlencode(items) - - -# Crack the given url into (scheme, host, port, path) -def _spliturl(url): - parsed_url = parse.urlparse(url) - host = parsed_url.hostname - port = parsed_url.port - path = ( - "?".join((parsed_url.path, parsed_url.query)) - if parsed_url.query - else parsed_url.path - ) - # Strip brackets if its an IPv6 address - if host.startswith("[") and host.endswith("]"): - host = host[1:-1] - if port is None: - port = DEFAULT_PORT - return parsed_url.scheme, host, port, path - - -# Given an HTTP request handler, this wrapper objects provides a related -# family of convenience methods built using that handler. -class HttpLib: - """A set of convenient methods for making HTTP calls. - - ``HttpLib`` provides a general :meth:`request` method, and :meth:`delete`, - :meth:`post`, and :meth:`get` methods for the three HTTP methods that Splunk - uses. - - By default, ``HttpLib`` uses Python's built-in ``httplib`` library, - but you can replace it by passing your own handling function to the - constructor for ``HttpLib``. - - The handling function should have the type: - - ``handler(`url`, `request_dict`) -> response_dict`` - - where `url` is the URL to make the request to (including any query and - fragment sections) as a dictionary with the following keys: - - - method: The method for the request, typically ``GET``, ``POST``, or ``DELETE``. - - - headers: A list of pairs specifying the HTTP headers (for example: ``[('key': value), ...]``). - - - body: A string containing the body to send with the request (this string - should default to ''). - - and ``response_dict`` is a dictionary with the following keys: - - - status: An integer containing the HTTP status code (such as 200 or 404). - - - reason: The reason phrase, if any, returned by the server. - - - headers: A list of pairs containing the response headers (for example, ``[('key': value), ...]``). - - - body: A stream-like object supporting ``read(size=None)`` and ``close()`` - methods to get the body of the response. - - The response dictionary is returned directly by ``HttpLib``'s methods with - no further processing. By default, ``HttpLib`` calls the :func:`handler` function - to get a handler function. - - If using the default handler, SSL verification can be disabled by passing verify=False. - """ - - def __init__( - self, - custom_handler=None, - verify=False, - key_file=None, - cert_file=None, - context=None, - retries=0, - retryDelay=10, - ): - if custom_handler is None: - self.handler = handler( - verify=verify, key_file=key_file, cert_file=cert_file, context=context - ) - else: - self.handler = custom_handler - self._cookies = {} - self.retries = retries - self.retryDelay = retryDelay - - def delete(self, url, headers=None, **kwargs): - """Sends a DELETE request to a URL. - - :param url: The URL. - :type url: ``string`` - :param headers: A list of pairs specifying the headers for the HTTP - response (for example, ``[('Content-Type': 'text/cthulhu'), ('Token': 'boris')]``). - :type headers: ``list`` - :param kwargs: Additional keyword arguments (optional). These arguments - are interpreted as the query part of the URL. The order of keyword - arguments is not preserved in the request, but the keywords and - their arguments will be URL encoded. - :type kwargs: ``dict`` - :returns: A dictionary describing the response (see :class:`HttpLib` for - its structure). - :rtype: ``dict`` - """ - if headers is None: - headers = [] - if kwargs: - # url is already a UrlEncoded. We have to manually declare - # the query to be encoded or it will get automatically URL - # encoded by being appended to url. - url = url + UrlEncoded("?" + _encode(**kwargs), skip_encode=True) - message = { - "method": "DELETE", - "headers": headers, - } - return self.request(url, message) - - def get(self, url, headers=None, **kwargs): - """Sends a GET request to a URL. - - :param url: The URL. - :type url: ``string`` - :param headers: A list of pairs specifying the headers for the HTTP - response (for example, ``[('Content-Type': 'text/cthulhu'), ('Token': 'boris')]``). - :type headers: ``list`` - :param kwargs: Additional keyword arguments (optional). These arguments - are interpreted as the query part of the URL. The order of keyword - arguments is not preserved in the request, but the keywords and - their arguments will be URL encoded. - :type kwargs: ``dict`` - :returns: A dictionary describing the response (see :class:`HttpLib` for - its structure). - :rtype: ``dict`` - """ - if headers is None: - headers = [] - if kwargs: - # url is already a UrlEncoded. We have to manually declare - # the query to be encoded or it will get automatically URL - # encoded by being appended to url. - url = url + UrlEncoded("?" + _encode(**kwargs), skip_encode=True) - return self.request(url, {"method": "GET", "headers": headers}) - - def post(self, url, headers=None, **kwargs): - """Sends a POST request to a URL. - - :param url: The URL. - :type url: ``string`` - :param headers: A list of pairs specifying the headers for the HTTP - response (for example, ``[('Content-Type': 'text/cthulhu'), ('Token': 'boris')]``). - :type headers: ``list`` - :param kwargs: Additional keyword arguments (optional). If the argument - is ``body``, the value is used as the body for the request, and the - keywords and their arguments will be URL encoded. If there is no - ``body`` keyword argument, all the keyword arguments are encoded - into the body of the request in the format ``x-www-form-urlencoded``. - :type kwargs: ``dict`` - :returns: A dictionary describing the response (see :class:`HttpLib` for - its structure). - :rtype: ``dict`` - """ - if headers is None: - headers = [] - - # We handle GET-style arguments and an unstructured body. This is here - # to support the receivers/stream endpoint. - if "body" in kwargs: - # We only use application/x-www-form-urlencoded if there is no other - # Content-Type header present. This can happen in cases where we - # send requests as application/json, e.g. for KV Store. - if len([x for x in headers if x[0].lower() == "content-type"]) == 0: - headers.append(("Content-Type", "application/x-www-form-urlencoded")) - - body = kwargs.pop("body") - if isinstance(body, dict): - body = _encode(**body).encode("utf-8") - if len(kwargs) > 0: - url = url + UrlEncoded("?" + _encode(**kwargs), skip_encode=True) - else: - body = _encode(**kwargs).encode("utf-8") - message = {"method": "POST", "headers": headers, "body": body} - return self.request(url, message) - - def request(self, url, message, **kwargs): - """Issues an HTTP request to a URL. - - :param url: The URL. - :type url: ``string`` - :param message: A dictionary with the format as described in - :class:`HttpLib`. - :type message: ``dict`` - :param kwargs: Additional keyword arguments (optional). These arguments - are passed unchanged to the handler. - :type kwargs: ``dict`` - :returns: A dictionary describing the response (see :class:`HttpLib` for - its structure). - :rtype: ``dict`` - """ - while True: - try: - response = self.handler(url, message, **kwargs) - break - except Exception: - if self.retries <= 0: - raise - else: - time.sleep(self.retryDelay) - self.retries -= 1 - response = record(response) - if 400 <= response.status: - raise HTTPError(response) - - # Update the cookie with any HTTP request - # Initially, assume list of 2-tuples - key_value_tuples = response.headers - # If response.headers is a dict, get the key-value pairs as 2-tuples - # this is the case when using urllib2 - if isinstance(response.headers, dict): - key_value_tuples = list(response.headers.items()) - for key, value in key_value_tuples: - if key.lower() == "set-cookie": - _parse_cookies(value, self._cookies) - - return response - - -# Converts an httplib response into a file-like object. -class ResponseReader(io.RawIOBase): - """This class provides a file-like interface for :class:`httplib` responses. - - The ``ResponseReader`` class is intended to be a layer to unify the different - types of HTTP libraries used with this SDK. This class also provides a - preview of the stream and a few useful predicates. - """ - - # For testing, you can use a StringIO as the argument to - # ``ResponseReader`` instead of an ``httplib.HTTPResponse``. It - # will work equally well. - def __init__(self, response, connection=None): - self._response = response - self._connection = connection - self._buffer = b"" - - def __str__(self): - return str(self.read(), "UTF-8") - - @property - def empty(self): - """Indicates whether there is any more data in the response.""" - return self.peek(1) == b"" - - def peek(self, size): - """Nondestructively retrieves a given number of characters. - - The next :meth:`read` operation behaves as though this method was never - called. - - :param size: The number of characters to retrieve. - :type size: ``integer`` - """ - c = self.read(size) - self._buffer = self._buffer + c - return c - - def close(self): - """Closes this response.""" - if self._connection: - self._connection.close() - self._response.close() - - def read(self, size=None): - """Reads a given number of characters from the response. - - :param size: The number of characters to read, or "None" to read the - entire response. - :type size: ``integer`` or "None" - - """ - r = self._buffer - self._buffer = b"" - if size is not None: - size -= len(r) - r = r + self._response.read(size) - return r - - def readable(self): - """Indicates that the response reader is readable.""" - return True - - def readinto(self, byte_array): - """Read data into a byte array, upto the size of the byte array. - - :param byte_array: A byte array/memory view to pour bytes into. - :type byte_array: ``bytearray`` or ``memoryview`` - - """ - max_size = len(byte_array) - data = self.read(max_size) - bytes_read = len(data) - byte_array[:bytes_read] = data - return bytes_read - - -def handler(key_file=None, cert_file=None, timeout=None, verify=False, context=None): - """This class returns an instance of the default HTTP request handler using - the values you provide. - - :param `key_file`: A path to a PEM (Privacy Enhanced Mail) formatted file containing your private key (optional). - :type key_file: ``string`` - :param `cert_file`: A path to a PEM (Privacy Enhanced Mail) formatted file containing a certificate chain file (optional). - :type cert_file: ``string`` - :param `timeout`: The request time-out period, in seconds (optional). - :type timeout: ``integer`` or "None" - :param `verify`: Set to False to disable SSL verification on https connections. - :type verify: ``Boolean`` - :param `context`: The SSLContext that can is used with the HTTPSConnection when verify=True is enabled and context is specified - :type context: ``SSLContext` - """ - - def connect(scheme, host, port): - kwargs = {} - if timeout is not None: - kwargs["timeout"] = timeout - if scheme == "http": - return client.HTTPConnection(host, port, **kwargs) - if scheme == "https": - if key_file is not None: - kwargs["key_file"] = key_file - if cert_file is not None: - kwargs["cert_file"] = cert_file - - if not verify: - kwargs["context"] = ssl._create_unverified_context() # nosemgrep - elif context: - # verify is True in elif branch and context is not None - kwargs["context"] = context - - return client.HTTPSConnection(host, port, **kwargs) - raise ValueError(f"unsupported scheme: {scheme}") - - def request(url, message, **kwargs): - scheme, host, port, path = _spliturl(url) - body = message.get("body", "") - head = { - "Content-Length": str(len(body)), - "Host": host, - "User-Agent": "splunk-sdk-python/%s" % __version__, - "Accept": "*/*", - "Connection": "Close", - } # defaults - for key, value in message["headers"]: - head[key] = value - method = message.get("method", "GET") - - connection = connect(scheme, host, port) - is_keepalive = False - try: - connection.request(method, path, body, head) - if timeout is not None: - connection.sock.settimeout(timeout) - response = connection.getresponse() - is_keepalive = ( - "keep-alive" - in response.getheader("connection", default="close").lower() - ) - finally: - if not is_keepalive: - connection.close() - - return { - "status": response.status, - "reason": response.reason, - "headers": response.getheaders(), - "body": ResponseReader(response, connection if is_keepalive else None), - } - - return request diff --git a/apps/bitwarden_event_logs/lib/splunklib/client.py b/apps/bitwarden_event_logs/lib/splunklib/client.py deleted file mode 100755 index 72cefc26..00000000 --- a/apps/bitwarden_event_logs/lib/splunklib/client.py +++ /dev/null @@ -1,4156 +0,0 @@ -# Copyright © 2011-2024 Splunk, Inc. -# -# 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. -# -# The purpose of this module is to provide a friendlier domain interface to -# various Splunk endpoints. The approach here is to leverage the binding -# layer to capture endpoint context and provide objects and methods that -# offer simplified access their corresponding endpoints. The design avoids -# caching resource state. From the perspective of this module, the 'policy' -# for caching resource state belongs in the application or a higher level -# framework, and its the purpose of this module to provide simplified -# access to that resource state. -# -# A side note, the objects below that provide helper methods for updating eg: -# Entity state, are written so that they may be used in a fluent style. -# - -"""The **splunklib.client** module provides a Pythonic interface to the -`Splunk REST API `_, -allowing you programmatically access Splunk's resources. - -**splunklib.client** wraps a Pythonic layer around the wire-level -binding of the **splunklib.binding** module. The core of the library is the -:class:`Service` class, which encapsulates a connection to the server, and -provides access to the various aspects of Splunk's functionality, which are -exposed via the REST API. Typically you connect to a running Splunk instance -with the :func:`connect` function:: - - import splunklib.client as client - service = client.connect(host='localhost', port=8089, - username='admin', password='...') - assert isinstance(service, client.Service) - -:class:`Service` objects have fields for the various Splunk resources (such as apps, -jobs, saved searches, inputs, and indexes). All of these fields are -:class:`Collection` objects:: - - appcollection = service.apps - my_app = appcollection.create('my_app') - my_app = appcollection['my_app'] - appcollection.delete('my_app') - -The individual elements of the collection, in this case *applications*, -are subclasses of :class:`Entity`. An ``Entity`` object has fields for its -attributes, and methods that are specific to each kind of entity. For example:: - - print(my_app['author']) # Or: print(my_app.author) - my_app.package() # Creates a compressed package of this application -""" - -import contextlib -import datetime -import json -import logging -import re -import socket -from datetime import datetime, timedelta -from time import sleep -from urllib import parse - -from . import data -from .data import record -from .binding import ( - AuthenticationError, - Context, - HTTPError, - UrlEncoded, - _encode, - _make_cookie_header, - _NoAuthenticationToken, - namespace, -) - -logger = logging.getLogger(__name__) - -__all__ = [ - "connect", - "NotSupportedError", - "OperationError", - "IncomparableException", - "Service", - "namespace", - "AuthenticationError", -] - -PATH_APPS = "apps/local/" -PATH_CAPABILITIES = "authorization/capabilities/" -PATH_CONF = "configs/conf-%s/" -PATH_PROPERTIES = "properties/" -PATH_DEPLOYMENT_CLIENTS = "deployment/client/" -PATH_DEPLOYMENT_TENANTS = "deployment/tenants/" -PATH_DEPLOYMENT_SERVERS = "deployment/server/" -PATH_DEPLOYMENT_SERVERCLASSES = "deployment/serverclass/" -PATH_EVENT_TYPES = "saved/eventtypes/" -PATH_FIRED_ALERTS = "alerts/fired_alerts/" -PATH_INDEXES = "data/indexes/" -PATH_INPUTS = "data/inputs/" -PATH_JOBS = "search/jobs/" -PATH_JOBS_V2 = "search/v2/jobs/" -PATH_LOGGER = "/services/server/logger/" -PATH_MACROS = "configs/conf-macros/" -PATH_MESSAGES = "messages/" -PATH_MODULAR_INPUTS = "data/modular-inputs" -PATH_ROLES = "authorization/roles/" -PATH_SAVED_SEARCHES = "saved/searches/" -PATH_STANZA = "configs/conf-%s/%s" # (file, stanza) -PATH_USERS = "authentication/users/" -PATH_RECEIVERS_STREAM = "/services/receivers/stream" -PATH_RECEIVERS_SIMPLE = "/services/receivers/simple" -PATH_STORAGE_PASSWORDS = "storage/passwords" - -XNAMEF_ATOM = "{http://www.w3.org/2005/Atom}%s" -XNAME_ENTRY = XNAMEF_ATOM % "entry" -XNAME_CONTENT = XNAMEF_ATOM % "content" - -MATCH_ENTRY_CONTENT = f"{XNAME_ENTRY}/{XNAME_CONTENT}/*" - - -class IllegalOperationException(Exception): - """Thrown when an operation is not possible on the Splunk instance that a - :class:`Service` object is connected to.""" - - -class IncomparableException(Exception): - """Thrown when trying to compare objects (using ``==``, ``<``, ``>``, and - so on) of a type that doesn't support it.""" - - -class AmbiguousReferenceException(ValueError): - """Thrown when the name used to fetch an entity matches more than one entity.""" - - -class InvalidNameException(Exception): - """Thrown when the specified name contains characters that are not allowed - in Splunk entity names.""" - - -class NoSuchCapability(Exception): - """Thrown when the capability that has been referred to doesn't exist.""" - - -class OperationError(Exception): - """Raised for a failed operation, such as a timeout.""" - - -class NotSupportedError(Exception): - """Raised for operations that are not supported on a given object.""" - - -def _trailing(template, *targets): - """Substring of *template* following all *targets*. - - **Example**:: - - template = "this is a test of the bunnies." - _trailing(template, "is", "est", "the") == " bunnies" - - Each target is matched successively in the string, and the string - remaining after the last target is returned. If one of the targets - fails to match, a ValueError is raised. - - :param template: Template to extract a trailing string from. - :type template: ``string`` - :param targets: Strings to successively match in *template*. - :type targets: list of ``string``s - :return: Trailing string after all targets are matched. - :rtype: ``string`` - :raises ValueError: Raised when one of the targets does not match. - """ - s = template - for t in targets: - n = s.find(t) - if n == -1: - raise ValueError("Target " + t + " not found in template.") - s = s[n + len(t) :] - return s - - -# Filter the given state content record according to the given arg list. -def _filter_content(content, *args): - if len(args) > 0: - return record((k, content[k]) for k in args) - return record( - (k, v) - for k, v in content.items() - if k not in ["eai:acl", "eai:attributes", "type"] - ) - - -# Construct a resource path from the given base path + resource name -def _path(base, name): - if not base.endswith("/"): - base = base + "/" - return base + name - - -# Load an atom record from the body of the given response -# this will ultimately be sent to an xml ElementTree so we -# should use the xmlcharrefreplace option -def _load_atom(response, match=None): - return data.load(response.body.read().decode("utf-8", "xmlcharrefreplace"), match) - - -# Load an array of atom entries from the body of the given response -def _load_atom_entries(response): - r = _load_atom(response) - if "feed" in r: - # Need this to handle a random case in the REST API - if r.feed.get("totalResults") in [0, "0"]: - return [] - entries = r.feed.get("entry", None) - if entries is None: - return None - return entries if isinstance(entries, list) else [entries] - # Unlike most other endpoints, the jobs endpoint does not return - # its state wrapped in another element, but at the top level. - # For example, in XML, it returns ... instead of - # .... - entries = r.get("entry", None) - if entries is None: - return None - return entries if isinstance(entries, list) else [entries] - - -# Load the sid from the body of the given response -def _load_sid(response, output_mode): - if output_mode == "json": - json_obj = json.loads(response.body.read()) - return json_obj.get("sid") - return _load_atom(response).response.sid - - -# Parse the given atom entry record into a generic entity state record -def _parse_atom_entry(entry): - title = entry.get("title", None) - - elink = entry.get("link", []) - elink = elink if isinstance(elink, list) else [elink] - links = record((link.rel, link.href) for link in elink) - - # Retrieve entity content values - content = entry.get("content", {}) - - # Host entry metadata - metadata = _parse_atom_metadata(content) - - # Filter some of the noise out of the content record - content = record( - (k, v) for k, v in content.items() if k not in ["eai:acl", "eai:attributes"] - ) - - if "type" in content: - if isinstance(content["type"], list): - content["type"] = [t for t in content["type"] if t != "text/xml"] - # Unset type if it was only 'text/xml' - if len(content["type"]) == 0: - content.pop("type", None) - # Flatten 1 element list - if len(content["type"]) == 1: - content["type"] = content["type"][0] - else: - content.pop("type", None) - - return record( - { - "title": title, - "links": links, - "access": metadata.access, - "fields": metadata.fields, - "content": content, - "updated": entry.get("updated"), - } - ) - - -# Parse the metadata fields out of the given atom entry content record -def _parse_atom_metadata(content): - # Hoist access metadata - access = content.get("eai:acl", None) - - # Hoist content metadata (and cleanup some naming) - attributes = content.get("eai:attributes", {}) - fields = record( - { - "required": attributes.get("requiredFields", []), - "optional": attributes.get("optionalFields", []), - "wildcard": attributes.get("wildcardFields", []), - } - ) - - return record({"access": access, "fields": fields}) - - -# kwargs: scheme, host, port, app, owner, username, password -def connect(**kwargs): - """This function connects and logs in to a Splunk instance. - - This function is a shorthand for :meth:`Service.login`. - The ``connect`` function makes one round trip to the server (for logging in). - - :param host: The host name (the default is "localhost"). - :type host: ``string`` - :param port: The port number (the default is 8089). - :type port: ``integer`` - :param scheme: The scheme for accessing the service (the default is "https"). - :type scheme: "https" or "http" - :param verify: Enable (True) or disable (False) SSL verification for - https connections. (optional, the default is True) - :type verify: ``Boolean`` - :param `owner`: The owner context of the namespace (optional). - :type owner: ``string`` - :param `app`: The app context of the namespace (optional). - :type app: ``string`` - :param sharing: The sharing mode for the namespace (the default is "user"). - :type sharing: "global", "system", "app", or "user" - :param `token`: The current session token (optional). Session tokens can be - shared across multiple service instances. - :type token: ``string`` - :param cookie: A session cookie. When provided, you don't need to call :meth:`login`. - This parameter is only supported for Splunk 6.2+. - :type cookie: ``string`` - :param autologin: When ``True``, automatically tries to log in again if the - session terminates. - :type autologin: ``boolean`` - :param `username`: The Splunk account username, which is used to - authenticate the Splunk instance. - :type username: ``string`` - :param `password`: The password for the Splunk account. - :type password: ``string`` - :param retires: Number of retries for each HTTP connection (optional, the default is 0). - NOTE THAT THIS MAY INCREASE THE NUMBER OF ROUND TRIP CONNECTIONS TO THE SPLUNK SERVER. - :type retries: ``int`` - :param retryDelay: How long to wait between connection attempts if `retries` > 0 (optional, defaults to 10s). - :type retryDelay: ``int`` (in seconds) - :param `context`: The SSLContext that can be used when setting verify=True (optional) - :type context: ``SSLContext`` - :return: An initialized :class:`Service` connection. - - **Example**:: - - import splunklib.client as client - s = client.connect(...) - a = s.apps["my_app"] - ... - """ - s = Service(**kwargs) - s.login() - return s - - -# In preparation for adding Storm support, we added an -# intermediary class between Service and Context. Storm's -# API is not going to be the same as enterprise Splunk's -# API, so we will derive both Service (for enterprise Splunk) -# and StormService for (Splunk Storm) from _BaseService, and -# put any shared behavior on it. -class _BaseService(Context): - pass - - -class Service(_BaseService): - """A Pythonic binding to Splunk instances. - - A :class:`Service` represents a binding to a Splunk instance on an - HTTP or HTTPS port. It handles the details of authentication, wire - formats, and wraps the REST API endpoints into something more - Pythonic. All of the low-level operations on the instance from - :class:`splunklib.binding.Context` are also available in case you need - to do something beyond what is provided by this class. - - After creating a ``Service`` object, you must call its :meth:`login` - method before you can issue requests to Splunk. - Alternately, use the :func:`connect` function to create an already - authenticated :class:`Service` object, or provide a session token - when creating the :class:`Service` object explicitly (the same - token may be shared by multiple :class:`Service` objects). - - :param host: The host name (the default is "localhost"). - :type host: ``string`` - :param port: The port number (the default is 8089). - :type port: ``integer`` - :param scheme: The scheme for accessing the service (the default is "https"). - :type scheme: "https" or "http" - :param verify: Enable (True) or disable (False) SSL verification for - https connections. (optional, the default is True) - :type verify: ``Boolean`` - :param `owner`: The owner context of the namespace (optional; use "-" for wildcard). - :type owner: ``string`` - :param `app`: The app context of the namespace (optional; use "-" for wildcard). - :type app: ``string`` - :param `token`: The current session token (optional). Session tokens can be - shared across multiple service instances. - :type token: ``string`` - :param cookie: A session cookie. When provided, you don't need to call :meth:`login`. - This parameter is only supported for Splunk 6.2+. - :type cookie: ``string`` - :param `username`: The Splunk account username, which is used to - authenticate the Splunk instance. - :type username: ``string`` - :param `password`: The password, which is used to authenticate the Splunk - instance. - :type password: ``string`` - :param retires: Number of retries for each HTTP connection (optional, the default is 0). - NOTE THAT THIS MAY INCREASE THE NUMBER OF ROUND TRIP CONNECTIONS TO THE SPLUNK SERVER. - :type retries: ``int`` - :param retryDelay: How long to wait between connection attempts if `retries` > 0 (optional, defaults to 10s). - :type retryDelay: ``int`` (in seconds) - :return: A :class:`Service` instance. - - **Example**:: - - import splunklib.client as client - s = client.Service(username="boris", password="natasha", ...) - s.login() - # Or equivalently - s = client.connect(username="boris", password="natasha") - # Or if you already have a session token - s = client.Service(token="atg232342aa34324a") - # Or if you already have a valid cookie - s = client.Service(cookie="splunkd_8089=...") - """ - - def __init__(self, **kwargs): - super().__init__(**kwargs) - self._splunk_version = None - self._kvstore_owner = None - self._instance_type = None - - @property - def apps(self): - """Returns the collection of applications that are installed on this instance of Splunk. - - :return: A :class:`Collection` of :class:`Application` entities. - """ - return Collection(self, PATH_APPS, item=Application) - - @property - def confs(self): - """Returns the collection of configuration files for this Splunk instance. - - :return: A :class:`Configurations` collection of - :class:`ConfigurationFile` entities. - """ - return Configurations(self) - - @property - def capabilities(self): - """Returns the list of system capabilities. - - :return: A ``list`` of capabilities. - """ - response = self.get(PATH_CAPABILITIES) - return _load_atom(response, MATCH_ENTRY_CONTENT).capabilities - - @property - def event_types(self): - """Returns the collection of event types defined in this Splunk instance. - - :return: An :class:`Entity` containing the event types. - """ - return Collection(self, PATH_EVENT_TYPES) - - @property - def fired_alerts(self): - """Returns the collection of alerts that have been fired on the Splunk - instance, grouped by saved search. - - :return: A :class:`Collection` of :class:`AlertGroup` entities. - """ - return Collection(self, PATH_FIRED_ALERTS, item=AlertGroup) - - @property - def indexes(self): - """Returns the collection of indexes for this Splunk instance. - - :return: An :class:`Indexes` collection of :class:`Index` entities. - """ - return Indexes(self, PATH_INDEXES, item=Index) - - @property - def info(self): - """Returns the information about this instance of Splunk. - - :return: The system information, as key-value pairs. - :rtype: ``dict`` - """ - response = self.get("/services/server/info") - return _filter_content(_load_atom(response, MATCH_ENTRY_CONTENT)) - - def input(self, path, kind=None): - """Retrieves an input by path, and optionally kind. - - :return: A :class:`Input` object. - """ - return Input(self, path, kind=kind).refresh() - - @property - def inputs(self): - """Returns the collection of inputs configured on this Splunk instance. - - :return: An :class:`Inputs` collection of :class:`Input` entities. - """ - return Inputs(self) - - def job(self, sid): - """Retrieves a search job by sid. - - :return: A :class:`Job` object. - """ - return Job(self, sid).refresh() - - @property - def jobs(self): - """Returns the collection of current search jobs. - - :return: A :class:`Jobs` collection of :class:`Job` entities. - """ - return Jobs(self) - - @property - def loggers(self): - """Returns the collection of logging level categories and their status. - - :return: A :class:`Loggers` collection of logging levels. - """ - return Loggers(self) - - @property - def messages(self): - """Returns the collection of service messages. - - :return: A :class:`Collection` of :class:`Message` entities. - """ - return Collection(self, PATH_MESSAGES, item=Message) - - @property - def modular_input_kinds(self): - """Returns the collection of the modular input kinds on this Splunk instance. - - :return: A :class:`ReadOnlyCollection` of :class:`ModularInputKind` entities. - """ - if self.splunk_version >= (5,): - return ReadOnlyCollection(self, PATH_MODULAR_INPUTS, item=ModularInputKind) - raise IllegalOperationException( - "Modular inputs are not supported before Splunk version 5." - ) - - @property - def storage_passwords(self): - """Returns the collection of the storage passwords on this Splunk instance. - - :return: A :class:`ReadOnlyCollection` of :class:`StoragePasswords` entities. - """ - return StoragePasswords(self) - - # kwargs: enable_lookups, reload_macros, parse_only, output_mode - def parse(self, query, **kwargs): - """Parses a search query and returns a semantic map of the search. - - :param query: The search query to parse. - :type query: ``string`` - :param kwargs: Arguments to pass to the ``search/parser`` endpoint - (optional). Valid arguments are: - - * "enable_lookups" (``boolean``): If ``True``, performs reverse lookups - to expand the search expression. - - * "output_mode" (``string``): The output format (XML or JSON). - - * "parse_only" (``boolean``): If ``True``, disables the expansion of - search due to evaluation of subsearches, time term expansion, - lookups, tags, eventtypes, and sourcetype alias. - - * "reload_macros" (``boolean``): If ``True``, reloads macro - definitions from macros.conf. - - :type kwargs: ``dict`` - :return: A semantic map of the parsed search query. - """ - if not self.disable_v2_api: - return self.post("search/v2/parser", q=query, **kwargs) - return self.get("search/parser", q=query, **kwargs) - - def restart(self, timeout=None): - """Restarts this Splunk instance. - - The service is unavailable until it has successfully restarted. - - If a *timeout* value is specified, ``restart`` blocks until the service - resumes or the timeout period has been exceeded. Otherwise, ``restart`` returns - immediately. - - :param timeout: A timeout period, in seconds. - :type timeout: ``integer`` - """ - msg = { - "value": "Restart requested by " - + self.username - + "via the Splunk SDK for Python" - } - # This message will be deleted once the server actually restarts. - self.messages.create(name="restart_required", **msg) - result = self.post("/services/server/control/restart") - if timeout is None: - return result - start = datetime.now() - diff = timedelta(seconds=timeout) - while datetime.now() - start < diff: - try: - self.login() - if not self.restart_required: - return result - except Exception as e: - sleep(1) - raise Exception("Operation time out.") - - @property - def restart_required(self): - """Indicates whether splunkd is in a state that requires a restart. - - :return: A ``boolean`` that indicates whether a restart is required. - - """ - response = self.get("messages").body.read() - messages = data.load(response)["feed"] - if "entry" not in messages: - result = False - else: - if isinstance(messages["entry"], dict): - titles = [messages["entry"]["title"]] - else: - titles = [x["title"] for x in messages["entry"]] - result = "restart_required" in titles - return result - - @property - def roles(self): - """Returns the collection of user roles. - - :return: A :class:`Roles` collection of :class:`Role` entities. - """ - return Roles(self) - - def search(self, query, **kwargs): - """Runs a search using a search query and any optional arguments you - provide, and returns a `Job` object representing the search. - - :param query: A search query. - :type query: ``string`` - :param kwargs: Arguments for the search (optional): - - * "output_mode" (``string``): Specifies the output format of the - results. - - * "earliest_time" (``string``): Specifies the earliest time in the - time range to - search. The time string can be a UTC time (with fractional - seconds), a relative time specifier (to now), or a formatted - time string. - - * "latest_time" (``string``): Specifies the latest time in the time - range to - search. The time string can be a UTC time (with fractional - seconds), a relative time specifier (to now), or a formatted - time string. - - * "rf" (``string``): Specifies one or more fields to add to the - search. - - :type kwargs: ``dict`` - :rtype: class:`Job` - :returns: An object representing the created job. - """ - return self.jobs.create(query, **kwargs) - - @property - def saved_searches(self): - """Returns the collection of saved searches. - - :return: A :class:`SavedSearches` collection of :class:`SavedSearch` - entities. - """ - return SavedSearches(self) - - @property - def macros(self): - """Returns the collection of macros. - - :return: A :class:`Macros` collection of :class:`Macro` - entities. - """ - return Macros(self) - - @property - def settings(self): - """Returns the configuration settings for this instance of Splunk. - - :return: A :class:`Settings` object containing configuration settings. - """ - return Settings(self) - - @property - def splunk_version(self): - """Returns the version of the splunkd instance this object is attached - to. - - The version is returned as a tuple of the version components as - integers (for example, `(4,3,3)` or `(5,)`). - - :return: A ``tuple`` of ``integers``. - """ - if self._splunk_version is None: - self._splunk_version = tuple( - int(p) for p in self.info["version"].split(".") - ) - return self._splunk_version - - @property - def splunk_instance(self): - if self._instance_type is None: - splunk_info = self.info - if hasattr(splunk_info, "instance_type"): - self._instance_type = splunk_info["instance_type"] - else: - self._instance_type = "" - return self._instance_type - - @property - def disable_v2_api(self): - if self.splunk_instance.lower() == "cloud": - return self.splunk_version < (9, 0, 2209) - return self.splunk_version < (9, 0, 2) - - @property - def kvstore_owner(self): - """Returns the KVStore owner for this instance of Splunk. - - By default is the kvstore owner is not set, it will return "nobody" - :return: A string with the KVStore owner. - """ - if self._kvstore_owner is None: - self._kvstore_owner = "nobody" - return self._kvstore_owner - - @kvstore_owner.setter - def kvstore_owner(self, value): - """ - kvstore is refreshed, when the owner value is changed - """ - self._kvstore_owner = value - self.kvstore - - @property - def kvstore(self): - """Returns the collection of KV Store collections. - - sets the owner for the namespace, before retrieving the KVStore Collection - - :return: A :class:`KVStoreCollections` collection of :class:`KVStoreCollection` entities. - """ - self.namespace["owner"] = self.kvstore_owner - return KVStoreCollections(self) - - @property - def users(self): - """Returns the collection of users. - - :return: A :class:`Users` collection of :class:`User` entities. - """ - return Users(self) - - -class Endpoint: - """This class represents individual Splunk resources in the Splunk REST API. - - An ``Endpoint`` object represents a URI, such as ``/services/saved/searches``. - This class provides the common functionality of :class:`Collection` and - :class:`Entity` (essentially HTTP GET and POST methods). - """ - - def __init__(self, service, path): - self.service = service - self.path = path - - def get_api_version(self, path): - """Return the API version of the service used in the provided path. - - Args: - path (str): A fully-qualified endpoint path (for example, "/services/search/jobs"). - - Returns: - int: Version of the API (for example, 1) - """ - # Default to v1 if undefined in the path - # For example, "/services/search/jobs" is using API v1 - api_version = 1 - - versionSearch = re.search( - r"(?:servicesNS\/[^/]+\/[^/]+|services)\/[^/]+\/v(\d+)\/", path - ) - if versionSearch: - api_version = int(versionSearch.group(1)) - - return api_version - - def get(self, path_segment="", owner=None, app=None, sharing=None, **query): - """Performs a GET operation on the path segment relative to this endpoint. - - This method is named to match the HTTP method. This method makes at least - one roundtrip to the server, one additional round trip for - each 303 status returned, plus at most two additional round - trips if - the ``autologin`` field of :func:`connect` is set to ``True``. - - If *owner*, *app*, and *sharing* are omitted, this method takes a - default namespace from the :class:`Service` object for this :class:`Endpoint`. - All other keyword arguments are included in the URL as query parameters. - - :raises AuthenticationError: Raised when the ``Service`` is not logged in. - :raises HTTPError: Raised when an error in the request occurs. - :param path_segment: A path segment relative to this endpoint. - :type path_segment: ``string`` - :param owner: The owner context of the namespace (optional). - :type owner: ``string`` - :param app: The app context of the namespace (optional). - :type app: ``string`` - :param sharing: The sharing mode for the namespace (optional). - :type sharing: "global", "system", "app", or "user" - :param query: All other keyword arguments, which are used as query - parameters. - :type query: ``string`` - :return: The response from the server. - :rtype: ``dict`` with keys ``body``, ``headers``, ``reason``, - and ``status`` - - **Example**:: - - import splunklib.client - s = client.service(...) - apps = s.apps - apps.get() == \\ - {'body': ...a response reader object..., - 'headers': [('content-length', '26208'), - ('expires', 'Fri, 30 Oct 1998 00:00:00 GMT'), - ('server', 'Splunkd'), - ('connection', 'close'), - ('cache-control', 'no-store, max-age=0, must-revalidate, no-cache'), - ('date', 'Fri, 11 May 2012 16:30:35 GMT'), - ('content-type', 'text/xml; charset=utf-8')], - 'reason': 'OK', - 'status': 200} - apps.get('nonexistant/path') # raises HTTPError - s.logout() - apps.get() # raises AuthenticationError - """ - # self.path to the Endpoint is relative in the SDK, so passing - # owner, app, sharing, etc. along will produce the correct - # namespace in the final request. - if path_segment.startswith("/"): - path = path_segment - else: - if not self.path.endswith("/") and path_segment != "": - self.path = self.path + "/" - path = self.service._abspath( - self.path + path_segment, owner=owner, app=app, sharing=sharing - ) - # ^-- This was "%s%s" % (self.path, path_segment). - # That doesn't work, because self.path may be UrlEncoded. - - # Get the API version from the path - api_version = self.get_api_version(path) - - # Search API v2+ fallback to v1: - # - In v2+, /results_preview, /events and /results do not support search params. - # - Fallback from v2+ to v1 if Splunk Version is < 9. - # if api_version >= 2 and ('search' in query and path.endswith(tuple(["results_preview", "events", "results"])) or self.service.splunk_version < (9,)): - # path = path.replace(PATH_JOBS_V2, PATH_JOBS) - - if api_version == 1: - if isinstance(path, UrlEncoded): - path = UrlEncoded( - path.replace(PATH_JOBS_V2, PATH_JOBS), skip_encode=True - ) - else: - path = path.replace(PATH_JOBS_V2, PATH_JOBS) - - return self.service.get(path, owner=owner, app=app, sharing=sharing, **query) - - def post(self, path_segment="", owner=None, app=None, sharing=None, **query): - """Performs a POST operation on the path segment relative to this endpoint. - - This method is named to match the HTTP method. This method makes at least - one roundtrip to the server, one additional round trip for - each 303 status returned, plus at most two additional round trips if - the ``autologin`` field of :func:`connect` is set to ``True``. - - If *owner*, *app*, and *sharing* are omitted, this method takes a - default namespace from the :class:`Service` object for this :class:`Endpoint`. - All other keyword arguments are included in the URL as query parameters. - - :raises AuthenticationError: Raised when the ``Service`` is not logged in. - :raises HTTPError: Raised when an error in the request occurs. - :param path_segment: A path segment relative to this endpoint. - :type path_segment: ``string`` - :param owner: The owner context of the namespace (optional). - :type owner: ``string`` - :param app: The app context of the namespace (optional). - :type app: ``string`` - :param sharing: The sharing mode of the namespace (optional). - :type sharing: ``string`` - :param query: All other keyword arguments, which are used as query - parameters. - :type query: ``string`` - :return: The response from the server. - :rtype: ``dict`` with keys ``body``, ``headers``, ``reason``, - and ``status`` - - **Example**:: - - import splunklib.client - s = client.service(...) - apps = s.apps - apps.post(name='boris') == \\ - {'body': ...a response reader object..., - 'headers': [('content-length', '2908'), - ('expires', 'Fri, 30 Oct 1998 00:00:00 GMT'), - ('server', 'Splunkd'), - ('connection', 'close'), - ('cache-control', 'no-store, max-age=0, must-revalidate, no-cache'), - ('date', 'Fri, 11 May 2012 18:34:50 GMT'), - ('content-type', 'text/xml; charset=utf-8')], - 'reason': 'Created', - 'status': 201} - apps.get('nonexistant/path') # raises HTTPError - s.logout() - apps.get() # raises AuthenticationError - """ - if path_segment.startswith("/"): - path = path_segment - else: - if not self.path.endswith("/") and path_segment != "": - self.path = self.path + "/" - path = self.service._abspath( - self.path + path_segment, owner=owner, app=app, sharing=sharing - ) - - # Get the API version from the path - api_version = self.get_api_version(path) - - # Search API v2+ fallback to v1: - # - In v2+, /results_preview, /events and /results do not support search params. - # - Fallback from v2+ to v1 if Splunk Version is < 9. - # if api_version >= 2 and ('search' in query and path.endswith(tuple(["results_preview", "events", "results"])) or self.service.splunk_version < (9,)): - # path = path.replace(PATH_JOBS_V2, PATH_JOBS) - - if api_version == 1: - if isinstance(path, UrlEncoded): - path = UrlEncoded( - path.replace(PATH_JOBS_V2, PATH_JOBS), skip_encode=True - ) - else: - path = path.replace(PATH_JOBS_V2, PATH_JOBS) - - return self.service.post(path, owner=owner, app=app, sharing=sharing, **query) - - -# kwargs: path, app, owner, sharing, state -class Entity(Endpoint): - """This class is a base class for Splunk entities in the REST API, such as - saved searches, jobs, indexes, and inputs. - - ``Entity`` provides the majority of functionality required by entities. - Subclasses only implement the special cases for individual entities. - For example for saved searches, the subclass makes fields like ``action.email``, - ``alert_type``, and ``search`` available. - - An ``Entity`` is addressed like a dictionary, with a few extensions, - so the following all work, for example in saved searches:: - - ent['action.email'] - ent['alert_type'] - ent['search'] - - You can also access the fields as though they were the fields of a Python - object, as in:: - - ent.alert_type - ent.search - - However, because some of the field names are not valid Python identifiers, - the dictionary-like syntax is preferable. - - The state of an :class:`Entity` object is cached, so accessing a field - does not contact the server. If you think the values on the - server have changed, call the :meth:`Entity.refresh` method. - """ - - # Not every endpoint in the API is an Entity or a Collection. For - # example, a saved search at saved/searches/{name} has an additional - # method saved/searches/{name}/scheduled_times, but this isn't an - # entity in its own right. In these cases, subclasses should - # implement a method that uses the get and post methods inherited - # from Endpoint, calls the _load_atom function (it's elsewhere in - # client.py, but not a method of any object) to read the - # information, and returns the extracted data in a Pythonesque form. - # - # The primary use of subclasses of Entity is to handle specially - # named fields in the Entity. If you only need to provide a default - # value for an optional field, subclass Entity and define a - # dictionary ``defaults``. For instance,:: - # - # class Hypothetical(Entity): - # defaults = {'anOptionalField': 'foo', - # 'anotherField': 'bar'} - # - # If you have to do more than provide a default, such as rename or - # actually process values, then define a new method with the - # ``@property`` decorator. - # - # class Hypothetical(Entity): - # @property - # def foobar(self): - # return self.content['foo'] + "-" + self.content["bar"] - - # Subclasses can override defaults the default values for - # optional fields. See above. - defaults = {} - - def __init__(self, service, path, **kwargs): - Endpoint.__init__(self, service, path) - self._state = None - if not kwargs.get("skip_refresh", False): - self.refresh(kwargs.get("state", None)) # "Prefresh" - - def __contains__(self, item): - try: - self[item] - return True - except (KeyError, AttributeError): - return False - - def __eq__(self, other): - """Raises IncomparableException. - - Since Entity objects are snapshots of times on the server, no - simple definition of equality will suffice beyond instance - equality, and instance equality leads to strange situations - such as:: - - import splunklib.client as client - c = client.connect(...) - saved_searches = c.saved_searches - x = saved_searches['asearch'] - - but then ``x != saved_searches['asearch']``. - - whether or not there was a change on the server. Rather than - try to do something fancy, we simply declare that equality is - undefined for Entities. - - Makes no roundtrips to the server. - """ - raise IncomparableException( - f"Equality is undefined for objects of class {self.__class__.__name__}" - ) - - def __getattr__(self, key): - # Called when an attribute was not found by the normal method. In this - # case we try to find it in self.content and then self.defaults. - if key in self.state.content: - return self.state.content[key] - if key in self.defaults: - return self.defaults[key] - raise AttributeError(key) - - def __getitem__(self, key): - # getattr attempts to find a field on the object in the normal way, - # then calls __getattr__ if it cannot. - return getattr(self, key) - - # Load the Atom entry record from the given response - this is a method - # because the "entry" record varies slightly by entity and this allows - # for a subclass to override and handle any special cases. - def _load_atom_entry(self, response): - elem = _load_atom(response, XNAME_ENTRY) - if isinstance(elem, list): - apps = [ele.entry.content.get("eai:appName") for ele in elem] - - raise AmbiguousReferenceException( - f"Fetch from server returned multiple entries for name '{elem[0].entry.title}' in apps {apps}." - ) - return elem.entry - - # Load the entity state record from the given response - def _load_state(self, response): - entry = self._load_atom_entry(response) - return _parse_atom_entry(entry) - - def _run_action(self, path_segment, **kwargs): - """Run a method and return the content Record from the returned XML. - - A method is a relative path from an Entity that is not itself - an Entity. _run_action assumes that the returned XML is an - Atom field containing one Entry, and the contents of Entry is - what should be the return value. This is right in enough cases - to make this method useful. - """ - response = self.get(path_segment, **kwargs) - data = self._load_atom_entry(response) - rec = _parse_atom_entry(data) - return rec.content - - def _proper_namespace(self, owner=None, app=None, sharing=None): - """Produce a namespace sans wildcards for use in entity requests. - - This method tries to fill in the fields of the namespace which are `None` - or wildcard (`'-'`) from the entity's namespace. If that fails, it uses - the service's namespace. - - :param owner: - :param app: - :param sharing: - :return: - """ - if owner is None and app is None and sharing is None: # No namespace provided - if self._state is not None and "access" in self._state: - return ( - self._state.access.owner, - self._state.access.app, - self._state.access.sharing, - ) - return ( - self.service.namespace["owner"], - self.service.namespace["app"], - self.service.namespace["sharing"], - ) - return owner, app, sharing - - def delete(self): - owner, app, sharing = self._proper_namespace() - return self.service.delete(self.path, owner=owner, app=app, sharing=sharing) - - def get(self, path_segment="", owner=None, app=None, sharing=None, **query): - owner, app, sharing = self._proper_namespace(owner, app, sharing) - return super().get(path_segment, owner=owner, app=app, sharing=sharing, **query) - - def post(self, path_segment="", owner=None, app=None, sharing=None, **query): - owner, app, sharing = self._proper_namespace(owner, app, sharing) - return super().post( - path_segment, owner=owner, app=app, sharing=sharing, **query - ) - - def refresh(self, state=None): - """Refreshes the state of this entity. - - If *state* is provided, load it as the new state for this - entity. Otherwise, make a roundtrip to the server (by calling - the :meth:`read` method of ``self``) to fetch an updated state, - plus at most two additional round trips if - the ``autologin`` field of :func:`connect` is set to ``True``. - - :param state: Entity-specific arguments (optional). - :type state: ``dict`` - :raises EntityDeletedException: Raised if the entity no longer exists on - the server. - - **Example**:: - - import splunklib.client as client - s = client.connect(...) - search = s.apps['search'] - search.refresh() - """ - if state is not None: - self._state = state - else: - self._state = self.read(self.get()) - return self - - @property - def access(self): - """Returns the access metadata for this entity. - - :return: A :class:`splunklib.data.Record` object with three keys: - ``owner``, ``app``, and ``sharing``. - """ - return self.state.access - - @property - def content(self): - """Returns the contents of the entity. - - :return: A ``dict`` containing values. - """ - return self.state.content - - def disable(self): - """Disables the entity at this endpoint.""" - self.post("disable") - return self - - def enable(self): - """Enables the entity at this endpoint.""" - self.post("enable") - return self - - @property - def fields(self): - """Returns the content metadata for this entity. - - :return: A :class:`splunklib.data.Record` object with three keys: - ``required``, ``optional``, and ``wildcard``. - """ - return self.state.fields - - @property - def links(self): - """Returns a dictionary of related resources. - - :return: A ``dict`` with keys and corresponding URLs. - """ - return self.state.links - - @property - def name(self): - """Returns the entity name. - - :return: The entity name. - :rtype: ``string`` - """ - return self.state.title - - def read(self, response): - """Reads the current state of the entity from the server.""" - results = self._load_state(response) - # In lower layers of the SDK, we end up trying to URL encode - # text to be dispatched via HTTP. However, these links are already - # URL encoded when they arrive, and we need to mark them as such. - unquoted_links = dict( - (k, UrlEncoded(v, skip_encode=True)) for k, v in results["links"].items() - ) - results["links"] = unquoted_links - return results - - def reload(self): - """Reloads the entity.""" - self.post("_reload") - return self - - def acl_update(self, **kwargs): - """To update Access Control List (ACL) properties for an endpoint. - - :param kwargs: Additional entity-specific arguments (required). - - - "owner" (``string``): The Splunk username, such as "admin". A value of "nobody" means no specific user (required). - - - "sharing" (``string``): A mode that indicates how the resource is shared. The sharing mode can be "user", "app", "global", or "system" (required). - - :type kwargs: ``dict`` - - **Example**:: - - import splunklib.client as client - service = client.connect(...) - saved_search = service.saved_searches["name"] - saved_search.acl_update(sharing="app", owner="nobody", app="search", **{"perms.read": "admin, nobody"}) - """ - if "body" not in kwargs: - kwargs = {"body": kwargs} - - if "sharing" not in kwargs["body"]: - raise ValueError("Required argument 'sharing' is missing.") - if "owner" not in kwargs["body"]: - raise ValueError("Required argument 'owner' is missing.") - - self.post("acl", **kwargs) - self.refresh() - return self - - @property - def state(self): - """Returns the entity's state record. - - :return: A ``dict`` containing fields and metadata for the entity. - """ - if self._state is None: - self.refresh() - return self._state - - def update(self, **kwargs): - """Updates the server with any changes you've made to the current entity - along with any additional arguments you specify. - - **Note**: You cannot update the ``name`` field of an entity. - - Many of the fields in the REST API are not valid Python - identifiers, which means you cannot pass them as keyword - arguments. That is, Python will fail to parse the following:: - - # This fails - x.update(check-new=False, email.to='boris@utopia.net') - - However, you can always explicitly use a dictionary to pass - such keys:: - - # This works - x.update(**{'check-new': False, 'email.to': 'boris@utopia.net'}) - - :param kwargs: Additional entity-specific arguments (optional). - :type kwargs: ``dict`` - - :return: The entity this method is called on. - :rtype: class:`Entity` - """ - # The peculiarity in question: the REST API creates a new - # Entity if we pass name in the dictionary, instead of the - # expected behavior of updating this Entity. Therefore, we - # check for 'name' in kwargs and throw an error if it is - # there. - if "name" in kwargs: - raise IllegalOperationException( - "Cannot update the name of an Entity via the REST API." - ) - self.post(**kwargs) - return self - - -class ReadOnlyCollection(Endpoint): - """This class represents a read-only collection of entities in the Splunk - instance. - """ - - def __init__(self, service, path, item=Entity): - Endpoint.__init__(self, service, path) - self.item = item # Item accessor - self.null_count = -1 - - def __contains__(self, name): - """Is there at least one entry called *name* in this collection? - - Makes a single roundtrip to the server, plus at most two more - if - the ``autologin`` field of :func:`connect` is set to ``True``. - """ - try: - self[name] - return True - except KeyError: - return False - except AmbiguousReferenceException: - return True - - def __getitem__(self, key): - """Fetch an item named *key* from this collection. - - A name is not a unique identifier in a collection. The unique - identifier is a name plus a namespace. For example, there can - be a saved search named ``'mysearch'`` with sharing ``'app'`` - in application ``'search'``, and another with sharing - ``'user'`` with owner ``'boris'`` and application - ``'search'``. If the ``Collection`` is attached to a - ``Service`` that has ``'-'`` (wildcard) as user and app in its - namespace, then both of these may be visible under the same - name. - - Where there is no conflict, ``__getitem__`` will fetch the - entity given just the name. If there is a conflict, and you - pass just a name, it will raise a ``ValueError``. In that - case, add the namespace as a second argument. - - This function makes a single roundtrip to the server, plus at - most two additional round trips if - the ``autologin`` field of :func:`connect` is set to ``True``. - - :param key: The name to fetch, or a tuple (name, namespace). - :return: An :class:`Entity` object. - :raises KeyError: Raised if *key* does not exist. - :raises ValueError: Raised if no namespace is specified and *key* - does not refer to a unique name. - - **Example**:: - - s = client.connect(...) - saved_searches = s.saved_searches - x1 = saved_searches.create( - 'mysearch', 'search * | head 1', - owner='admin', app='search', sharing='app') - x2 = saved_searches.create( - 'mysearch', 'search * | head 1', - owner='admin', app='search', sharing='user') - # Raises ValueError: - saved_searches['mysearch'] - # Fetches x1 - saved_searches[ - 'mysearch', - client.namespace(sharing='app', app='search')] - # Fetches x2 - saved_searches[ - 'mysearch', - client.namespace(sharing='user', owner='boris', app='search')] - """ - try: - if isinstance(key, tuple) and len(key) == 2: - # x[a,b] is translated to x.__getitem__( (a,b) ), so we - # have to extract values out. - key, ns = key - key = UrlEncoded(key, encode_slash=True) - response = self.get(key, owner=ns.owner, app=ns.app) - else: - key = UrlEncoded(key, encode_slash=True) - response = self.get(key) - entries = self._load_list(response) - if len(entries) > 1: - raise AmbiguousReferenceException( - f"Found multiple entities named '{key}'; please specify a namespace." - ) - if len(entries) == 0: - raise KeyError(key) - return entries[0] - except HTTPError as he: - if he.status == 404: # No entity matching key and namespace. - raise KeyError(key) - else: - raise - - def __iter__(self, **kwargs): - """Iterate over the entities in the collection. - - :param kwargs: Additional arguments. - :type kwargs: ``dict`` - :rtype: iterator over entities. - - Implemented to give Collection a listish interface. This - function always makes a roundtrip to the server, plus at most - two additional round trips if - the ``autologin`` field of :func:`connect` is set to ``True``. - - **Example**:: - - import splunklib.client as client - c = client.connect(...) - saved_searches = c.saved_searches - for entity in saved_searches: - print(f"Saved search named {entity.name}") - """ - - for item in self.iter(**kwargs): - yield item - - def __len__(self): - """Enable ``len(...)`` for ``Collection`` objects. - - Implemented for consistency with a listish interface. No - further failure modes beyond those possible for any method on - an Endpoint. - - This function always makes a round trip to the server, plus at - most two additional round trips if - the ``autologin`` field of :func:`connect` is set to ``True``. - - **Example**:: - - import splunklib.client as client - c = client.connect(...) - saved_searches = c.saved_searches - n = len(saved_searches) - """ - return len(self.list()) - - def _entity_path(self, state): - """Calculate the path to an entity to be returned. - - *state* should be the dictionary returned by - :func:`_parse_atom_entry`. :func:`_entity_path` extracts the - link to this entity from *state*, and strips all the namespace - prefixes from it to leave only the relative path of the entity - itself, sans namespace. - - :rtype: ``string`` - :return: an absolute path - """ - # This has been factored out so that it can be easily - # overloaded by Configurations, which has to switch its - # entities' endpoints from its own properties/ to configs/. - raw_path = parse.unquote(state.links.alternate) - if "servicesNS/" in raw_path: - return _trailing(raw_path, "servicesNS/", "/", "/") - if "services/" in raw_path: - return _trailing(raw_path, "services/") - return raw_path - - def _load_list(self, response): - """Converts *response* to a list of entities. - - *response* is assumed to be a :class:`Record` containing an - HTTP response, of the form:: - - {'status': 200, - 'headers': [('content-length', '232642'), - ('expires', 'Fri, 30 Oct 1998 00:00:00 GMT'), - ('server', 'Splunkd'), - ('connection', 'close'), - ('cache-control', 'no-store, max-age=0, must-revalidate, no-cache'), - ('date', 'Tue, 29 May 2012 15:27:08 GMT'), - ('content-type', 'text/xml; charset=utf-8')], - 'reason': 'OK', - 'body': ...a stream implementing .read()...} - - The ``'body'`` key refers to a stream containing an Atom feed, - that is, an XML document with a toplevel element ````, - and within that element one or more ```` elements. - """ - # Some subclasses of Collection have to override this because - # splunkd returns something that doesn't match - # . - entries = _load_atom_entries(response) - if entries is None: - return [] - entities = [] - for entry in entries: - state = _parse_atom_entry(entry) - entity = self.item(self.service, self._entity_path(state), state=state) - entities.append(entity) - - return entities - - def itemmeta(self): - """Returns metadata for members of the collection. - - Makes a single roundtrip to the server, plus two more at most if - the ``autologin`` field of :func:`connect` is set to ``True``. - - :return: A :class:`splunklib.data.Record` object containing the metadata. - - **Example**:: - - import splunklib.client as client - import pprint - s = client.connect(...) - pprint.pprint(s.apps.itemmeta()) - {'access': {'app': 'search', - 'can_change_perms': '1', - 'can_list': '1', - 'can_share_app': '1', - 'can_share_global': '1', - 'can_share_user': '1', - 'can_write': '1', - 'modifiable': '1', - 'owner': 'admin', - 'perms': {'read': ['*'], 'write': ['admin']}, - 'removable': '0', - 'sharing': 'user'}, - 'fields': {'optional': ['author', - 'configured', - 'description', - 'label', - 'manageable', - 'template', - 'visible'], - 'required': ['name'], 'wildcard': []}} - """ - response = self.get("_new") - content = _load_atom(response, MATCH_ENTRY_CONTENT) - return _parse_atom_metadata(content) - - def iter(self, offset=0, count=None, pagesize=None, **kwargs): - """Iterates over the collection. - - This method is equivalent to the :meth:`list` method, but - it returns an iterator and can load a certain number of entities at a - time from the server. - - :param offset: The index of the first entity to return (optional). - :type offset: ``integer`` - :param count: The maximum number of entities to return (optional). - :type count: ``integer`` - :param pagesize: The number of entities to load (optional). - :type pagesize: ``integer`` - :param kwargs: Additional arguments (optional): - - - "search" (``string``): The search query to filter responses. - - - "sort_dir" (``string``): The direction to sort returned items: - "asc" or "desc". - - - "sort_key" (``string``): The field to use for sorting (optional). - - - "sort_mode" (``string``): The collating sequence for sorting - returned items: "auto", "alpha", "alpha_case", or "num". - - :type kwargs: ``dict`` - - **Example**:: - - import splunklib.client as client - s = client.connect(...) - for saved_search in s.saved_searches.iter(pagesize=10): - # Loads 10 saved searches at a time from the - # server. - ... - """ - assert pagesize is None or pagesize > 0 - if count is None: - count = self.null_count - fetched = 0 - while count == self.null_count or fetched < count: - response = self.get(count=pagesize or count, offset=offset, **kwargs) - items = self._load_list(response) - N = len(items) - fetched += N - for item in items: - yield item - if pagesize is None or N < pagesize: - break - offset += N - logger.debug( - "pagesize=%d, fetched=%d, offset=%d, N=%d, kwargs=%s", - pagesize, - fetched, - offset, - N, - kwargs, - ) - - # kwargs: count, offset, search, sort_dir, sort_key, sort_mode - def list(self, count=None, **kwargs): - """Retrieves a list of entities in this collection. - - The entire collection is loaded at once and is returned as a list. This - function makes a single roundtrip to the server, plus at most two more if - the ``autologin`` field of :func:`connect` is set to ``True``. - There is no caching--every call makes at least one round trip. - - :param count: The maximum number of entities to return (optional). - :type count: ``integer`` - :param kwargs: Additional arguments (optional): - - - "offset" (``integer``): The offset of the first item to return. - - - "search" (``string``): The search query to filter responses. - - - "sort_dir" (``string``): The direction to sort returned items: - "asc" or "desc". - - - "sort_key" (``string``): The field to use for sorting (optional). - - - "sort_mode" (``string``): The collating sequence for sorting - returned items: "auto", "alpha", "alpha_case", or "num". - - :type kwargs: ``dict`` - :return: A ``list`` of entities. - """ - # response = self.get(count=count, **kwargs) - # return self._load_list(response) - return list(self.iter(count=count, **kwargs)) - - -class Collection(ReadOnlyCollection): - """A collection of entities. - - Splunk provides a number of different collections of distinct - entity types: applications, saved searches, fired alerts, and a - number of others. Each particular type is available separately - from the Splunk instance, and the entities of that type are - returned in a :class:`Collection`. - - The interface for :class:`Collection` does not quite match either - ``list`` or ``dict`` in Python, because there are enough semantic - mismatches with either to make its behavior surprising. A unique - element in a :class:`Collection` is defined by a string giving its - name plus namespace (although the namespace is optional if the name is - unique). - - **Example**:: - - import splunklib.client as client - service = client.connect(...) - mycollection = service.saved_searches - mysearch = mycollection['my_search', client.namespace(owner='boris', app='natasha', sharing='user')] - # Or if there is only one search visible named 'my_search' - mysearch = mycollection['my_search'] - - Similarly, ``name`` in ``mycollection`` works as you might expect (though - you cannot currently pass a namespace to the ``in`` operator), as does - ``len(mycollection)``. - - However, as an aggregate, :class:`Collection` behaves more like a - list. If you iterate over a :class:`Collection`, you get an - iterator over the entities, not the names and namespaces. - - **Example**:: - - for entity in mycollection: - assert isinstance(entity, client.Entity) - - Use the :meth:`create` and :meth:`delete` methods to create and delete - entities in this collection. To view the access control list and other - metadata of the collection, use the :meth:`ReadOnlyCollection.itemmeta` method. - - :class:`Collection` does no caching. Each call makes at least one - round trip to the server to fetch data. - """ - - def create(self, name, **params): - """Creates a new entity in this collection. - - This function makes either one or two roundtrips to the - server, depending on the type of entities in this - collection, plus at most two more if - the ``autologin`` field of :func:`connect` is set to ``True``. - - :param name: The name of the entity to create. - :type name: ``string`` - :param namespace: A namespace, as created by the :func:`splunklib.binding.namespace` - function (optional). You can also set ``owner``, ``app``, and - ``sharing`` in ``params``. - :type namespace: A :class:`splunklib.data.Record` object with keys ``owner``, ``app``, - and ``sharing``. - :param params: Additional entity-specific arguments (optional). - :type params: ``dict`` - :return: The new entity. - :rtype: A subclass of :class:`Entity`, chosen by :meth:`Collection.self.item`. - - **Example**:: - - import splunklib.client as client - s = client.connect(...) - applications = s.apps - new_app = applications.create("my_fake_app") - """ - if not isinstance(name, str): - raise InvalidNameException(f"{name} is not a valid name for an entity.") - if "namespace" in params: - namespace = params.pop("namespace") - params["owner"] = namespace.owner - params["app"] = namespace.app - params["sharing"] = namespace.sharing - response = self.post(name=name, **params) - atom = _load_atom(response, XNAME_ENTRY) - if atom is None: - # This endpoint doesn't return the content of the new - # item. We have to go fetch it ourselves. - return self[name] - entry = atom.entry - state = _parse_atom_entry(entry) - entity = self.item(self.service, self._entity_path(state), state=state) - return entity - - def delete(self, name, **params): - """Deletes a specified entity from the collection. - - :param name: The name of the entity to delete. - :type name: ``string`` - :return: The collection. - :rtype: ``self`` - - This method is implemented for consistency with the REST API's DELETE - method. - - If there is no *name* entity on the server, a ``KeyError`` is - thrown. This function always makes a roundtrip to the server. - - **Example**:: - - import splunklib.client as client - c = client.connect(...) - saved_searches = c.saved_searches - saved_searches.create('my_saved_search', - 'search * | head 1') - assert 'my_saved_search' in saved_searches - saved_searches.delete('my_saved_search') - assert 'my_saved_search' not in saved_searches - """ - name = UrlEncoded(name, encode_slash=True) - if "namespace" in params: - namespace = params.pop("namespace") - params["owner"] = namespace.owner - params["app"] = namespace.app - params["sharing"] = namespace.sharing - try: - self.service.delete(_path(self.path, name), **params) - except HTTPError as he: - # An HTTPError with status code 404 means that the entity - # has already been deleted, and we reraise it as a - # KeyError. - if he.status == 404: - raise KeyError(f"No such entity {name}") - else: - raise - return self - - def get(self, name="", owner=None, app=None, sharing=None, **query): - """Performs a GET request to the server on the collection. - - If *owner*, *app*, and *sharing* are omitted, this method takes a - default namespace from the :class:`Service` object for this :class:`Endpoint`. - All other keyword arguments are included in the URL as query parameters. - - :raises AuthenticationError: Raised when the ``Service`` is not logged in. - :raises HTTPError: Raised when an error in the request occurs. - :param path_segment: A path segment relative to this endpoint. - :type path_segment: ``string`` - :param owner: The owner context of the namespace (optional). - :type owner: ``string`` - :param app: The app context of the namespace (optional). - :type app: ``string`` - :param sharing: The sharing mode for the namespace (optional). - :type sharing: "global", "system", "app", or "user" - :param query: All other keyword arguments, which are used as query - parameters. - :type query: ``string`` - :return: The response from the server. - :rtype: ``dict`` with keys ``body``, ``headers``, ``reason``, - and ``status`` - - **Example**:: - - import splunklib.client - s = client.service(...) - saved_searches = s.saved_searches - saved_searches.get("my/saved/search") == \\ - {'body': ...a response reader object..., - 'headers': [('content-length', '26208'), - ('expires', 'Fri, 30 Oct 1998 00:00:00 GMT'), - ('server', 'Splunkd'), - ('connection', 'close'), - ('cache-control', 'no-store, max-age=0, must-revalidate, no-cache'), - ('date', 'Fri, 11 May 2012 16:30:35 GMT'), - ('content-type', 'text/xml; charset=utf-8')], - 'reason': 'OK', - 'status': 200} - saved_searches.get('nonexistant/search') # raises HTTPError - s.logout() - saved_searches.get() # raises AuthenticationError - - """ - name = UrlEncoded(name, encode_slash=True) - return super().get(name, owner, app, sharing, **query) - - -class ConfigurationFile(Collection): - """This class contains all of the stanzas from one configuration file.""" - - # __init__'s arguments must match those of an Entity, not a - # Collection, since it is being created as the elements of a - # Configurations, which is a Collection subclass. - def __init__(self, service, path, **kwargs): - Collection.__init__(self, service, path, item=Stanza) - self.name = kwargs["state"]["title"] - - -class Configurations(Collection): - """This class provides access to the configuration files from this Splunk - instance. Retrieve this collection using :meth:`Service.confs`. - - Splunk's configuration is divided into files, and each file into - stanzas. This collection is unusual in that the values in it are - themselves collections of :class:`ConfigurationFile` objects. - """ - - def __init__(self, service): - Collection.__init__(self, service, PATH_PROPERTIES, item=ConfigurationFile) - if self.service.namespace.owner == "-" or self.service.namespace.app == "-": - raise ValueError("Configurations cannot have wildcards in namespace.") - - def __getitem__(self, key): - # The superclass implementation is designed for collections that contain - # entities. This collection (Configurations) contains collections - # (ConfigurationFile). - # - # The configurations endpoint returns multiple entities when we ask for a single file. - # This screws up the default implementation of __getitem__ from Collection, which thinks - # that multiple entities means a name collision, so we have to override it here. - try: - self.get(key) - return ConfigurationFile( - self.service, PATH_CONF % key, state={"title": key} - ) - except HTTPError as he: - if he.status == 404: # No entity matching key - raise KeyError(key) - else: - raise - - def __contains__(self, key): - # configs/conf-{name} never returns a 404. We have to post to properties/{name} - # in order to find out if a configuration exists. - try: - self.get(key) - return True - except HTTPError as he: - if he.status == 404: # No entity matching key - return False - raise - - def create(self, name): - """Creates a configuration file named *name*. - - If there is already a configuration file with that name, - the existing file is returned. - - :param name: The name of the configuration file. - :type name: ``string`` - - :return: The :class:`ConfigurationFile` object. - """ - # This has to be overridden to handle the plumbing of creating - # a ConfigurationFile (which is a Collection) instead of some - # Entity. - if not isinstance(name, str): - raise ValueError(f"Invalid name: {repr(name)}") - response = self.post(__conf=name) - if response.status == 303: - return self[name] - if response.status == 201: - return ConfigurationFile( - self.service, PATH_CONF % name, item=Stanza, state={"title": name} - ) - raise ValueError( - f"Unexpected status code {response.status} returned from creating a stanza" - ) - - def delete(self, key): - """Raises `IllegalOperationException`.""" - raise IllegalOperationException( - "Cannot delete configuration files from the REST API." - ) - - def _entity_path(self, state): - # Overridden to make all the ConfigurationFile objects - # returned refer to the configs/ path instead of the - # properties/ path used by Configrations. - return PATH_CONF % state["title"] - - -class Stanza(Entity): - """This class contains a single configuration stanza.""" - - def submit(self, stanza): - """Adds keys to the current configuration stanza as a - dictionary of key-value pairs. - - :param stanza: A dictionary of key-value pairs for the stanza. - :type stanza: ``dict`` - :return: The :class:`Stanza` object. - """ - body = _encode(**stanza) - self.service.post(self.path, body=body) - return self - - def __len__(self): - # The stanza endpoint returns all the keys at the same level in the XML as the eai information - # and 'disabled', so to get an accurate length, we have to filter those out and have just - # the stanza keys. - return len( - [ - x - for x in self._state.content.keys() - if not x.startswith("eai") and x != "disabled" - ] - ) - - -class StoragePassword(Entity): - """This class contains a storage password.""" - - def __init__(self, service, path, **kwargs): - state = kwargs.get("state", None) - kwargs["skip_refresh"] = kwargs.get("skip_refresh", state is not None) - super().__init__(service, path, **kwargs) - self._state = state - - @property - def clear_password(self): - return self.content.get("clear_password") - - @property - def encrypted_password(self): - return self.content.get("encr_password") - - @property - def realm(self): - return self.content.get("realm") - - @property - def username(self): - return self.content.get("username") - - -class StoragePasswords(Collection): - """This class provides access to the storage passwords from this Splunk - instance. Retrieve this collection using :meth:`Service.storage_passwords`. - """ - - def __init__(self, service): - if service.namespace.owner == "-" or service.namespace.app == "-": - raise ValueError("StoragePasswords cannot have wildcards in namespace.") - super().__init__(service, PATH_STORAGE_PASSWORDS, item=StoragePassword) - - def create(self, password, username, realm=None): - """Creates a storage password. - - A `StoragePassword` can be identified by , or by : if the - optional realm parameter is also provided. - - :param password: The password for the credentials - this is the only part of the credentials that will be stored securely. - :type name: ``string`` - :param username: The username for the credentials. - :type name: ``string`` - :param realm: The credential realm. (optional) - :type name: ``string`` - - :return: The :class:`StoragePassword` object created. - """ - if not isinstance(username, str): - raise ValueError(f"Invalid name: {repr(username)}") - - if realm is None: - response = self.post(password=password, name=username) - else: - response = self.post(password=password, realm=realm, name=username) - - if response.status != 201: - raise ValueError( - f"Unexpected status code {response.status} returned from creating a stanza" - ) - - entries = _load_atom_entries(response) - state = _parse_atom_entry(entries[0]) - storage_password = StoragePassword( - self.service, self._entity_path(state), state=state, skip_refresh=True - ) - - return storage_password - - def delete(self, username, realm=None): - """Delete a storage password by username and/or realm. - - The identifier can be passed in through the username parameter as - or :, but the preferred way is by - passing in the username and realm parameters. - - :param username: The username for the credentials, or : if the realm parameter is omitted. - :type name: ``string`` - :param realm: The credential realm. (optional) - :type name: ``string`` - :return: The `StoragePassword` collection. - :rtype: ``self`` - """ - if realm is None: - # This case makes the username optional, so - # the full name can be passed in as realm. - # Assume it's already encoded. - name = username - else: - # Encode each component separately - name = ( - UrlEncoded(realm, encode_slash=True) - + ":" - + UrlEncoded(username, encode_slash=True) - ) - - # Append the : expected at the end of the name - if name[-1] != ":": - name = name + ":" - return Collection.delete(self, name) - - -class AlertGroup(Entity): - """This class represents a group of fired alerts for a saved search. Access - it using the :meth:`alerts` property.""" - - def __init__(self, service, path, **kwargs): - Entity.__init__(self, service, path, **kwargs) - - def __len__(self): - return self.count - - @property - def alerts(self): - """Returns a collection of triggered alerts. - - :return: A :class:`Collection` of triggered alerts. - """ - return Collection(self.service, self.path) - - @property - def count(self): - """Returns the count of triggered alerts. - - :return: The triggered alert count. - :rtype: ``integer`` - """ - return int(self.content.get("triggered_alert_count", 0)) - - -class Indexes(Collection): - """This class contains the collection of indexes in this Splunk instance. - Retrieve this collection using :meth:`Service.indexes`. - """ - - def get_default(self): - """Returns the name of the default index. - - :return: The name of the default index. - - """ - index = self["_audit"] - return index["defaultDatabase"] - - def delete(self, name): - """Deletes a given index. - - **Note**: This method is only supported in Splunk 5.0 and later. - - :param name: The name of the index to delete. - :type name: ``string`` - """ - if self.service.splunk_version >= (5,): - Collection.delete(self, name) - else: - raise IllegalOperationException( - "Deleting indexes via the REST API is " - "not supported before Splunk version 5." - ) - - -class Index(Entity): - """This class represents an index and provides different operations, such as - cleaning the index, writing to the index, and so forth.""" - - def __init__(self, service, path, **kwargs): - Entity.__init__(self, service, path, **kwargs) - - def attach(self, host=None, source=None, sourcetype=None): - """Opens a stream (a writable socket) for writing events to the index. - - :param host: The host value for events written to the stream. - :type host: ``string`` - :param source: The source value for events written to the stream. - :type source: ``string`` - :param sourcetype: The sourcetype value for events written to the - stream. - :type sourcetype: ``string`` - - :return: A writable socket. - """ - args = {"index": self.name} - if host is not None: - args["host"] = host - if source is not None: - args["source"] = source - if sourcetype is not None: - args["sourcetype"] = sourcetype - path = UrlEncoded( - PATH_RECEIVERS_STREAM + "?" + parse.urlencode(args), skip_encode=True - ) - - cookie_header = ( - self.service.token - if self.service.token is _NoAuthenticationToken - else self.service.token.replace("Splunk ", "") - ) - cookie_or_auth_header = f"Authorization: Splunk {cookie_header}\r\n" - - # If we have cookie(s), use them instead of "Authorization: ..." - if self.service.has_cookies(): - cookie_header = _make_cookie_header(self.service.get_cookies().items()) - cookie_or_auth_header = f"Cookie: {cookie_header}\r\n" - - # Since we need to stream to the index connection, we have to keep - # the connection open and use the Splunk extension headers to note - # the input mode - sock = self.service.connect() - headers = [ - f"POST {str(self.service._abspath(path))} HTTP/1.1\r\n".encode("utf-8"), - f"Host: {self.service.host}:{int(self.service.port)}\r\n".encode("utf-8"), - b"Accept-Encoding: identity\r\n", - cookie_or_auth_header.encode("utf-8"), - b"X-Splunk-Input-Mode: Streaming\r\n", - b"\r\n", - ] - - for h in headers: - sock.write(h) - return sock - - @contextlib.contextmanager - def attached_socket(self, *args, **kwargs): - """Opens a raw socket in a ``with`` block to write data to Splunk. - - The arguments are identical to those for :meth:`attach`. The socket is - automatically closed at the end of the ``with`` block, even if an - exception is raised in the block. - - :param host: The host value for events written to the stream. - :type host: ``string`` - :param source: The source value for events written to the stream. - :type source: ``string`` - :param sourcetype: The sourcetype value for events written to the - stream. - :type sourcetype: ``string`` - - :returns: Nothing. - - **Example**:: - - import splunklib.client as client - s = client.connect(...) - index = s.indexes['some_index'] - with index.attached_socket(sourcetype='test') as sock: - sock.send('Test event\\r\\n') - - """ - try: - sock = self.attach(*args, **kwargs) - yield sock - finally: - sock.shutdown(socket.SHUT_RDWR) - sock.close() - - def clean(self, timeout=60): - """Deletes the contents of the index. - - This method blocks until the index is empty, because it needs to restore - values at the end of the operation. - - :param timeout: The time-out period for the operation, in seconds (the - default is 60). - :type timeout: ``integer`` - - :return: The :class:`Index`. - """ - self.refresh() - - tds = self["maxTotalDataSizeMB"] - ftp = self["frozenTimePeriodInSecs"] - was_disabled_initially = self.disabled - try: - if not was_disabled_initially and self.service.splunk_version < (5,): - # Need to disable the index first on Splunk 4.x, - # but it doesn't work to disable it on 5.0. - self.disable() - self.update(maxTotalDataSizeMB=1, frozenTimePeriodInSecs=1) - self.roll_hot_buckets() - - # Wait until event count goes to 0. - start = datetime.now() - diff = timedelta(seconds=timeout) - while self.content.totalEventCount != "0" and datetime.now() < start + diff: - sleep(1) - self.refresh() - - if self.content.totalEventCount != "0": - raise OperationError( - f"Cleaning index {self.name} took longer than {timeout} seconds; timing out." - ) - finally: - # Restore original values - self.update(maxTotalDataSizeMB=tds, frozenTimePeriodInSecs=ftp) - if not was_disabled_initially and self.service.splunk_version < (5,): - # Re-enable the index if it was originally enabled and we messed with it. - self.enable() - - return self - - def roll_hot_buckets(self): - """Performs rolling hot buckets for this index. - - :return: The :class:`Index`. - """ - self.post("roll-hot-buckets") - return self - - def submit(self, event, host=None, source=None, sourcetype=None): - """Submits a single event to the index using ``HTTP POST``. - - :param event: The event to submit. - :type event: ``string`` - :param `host`: The host value of the event. - :type host: ``string`` - :param `source`: The source value of the event. - :type source: ``string`` - :param `sourcetype`: The sourcetype value of the event. - :type sourcetype: ``string`` - - :return: The :class:`Index`. - """ - args = {"index": self.name} - if host is not None: - args["host"] = host - if source is not None: - args["source"] = source - if sourcetype is not None: - args["sourcetype"] = sourcetype - - self.service.post(PATH_RECEIVERS_SIMPLE, body=event, **args) - return self - - # kwargs: host, host_regex, host_segment, rename-source, sourcetype - def upload(self, filename, **kwargs): - """Uploads a file for immediate indexing. - - **Note**: The file must be locally accessible from the server. - - :param filename: The name of the file to upload. The file can be a - plain, compressed, or archived file. - :type filename: ``string`` - :param kwargs: Additional arguments (optional). For more about the - available parameters, see `Index parameters `_ on Splunk Developer Portal. - :type kwargs: ``dict`` - - :return: The :class:`Index`. - """ - kwargs["index"] = self.name - path = "data/inputs/oneshot" - self.service.post(path, name=filename, **kwargs) - return self - - -class Input(Entity): - """This class represents a Splunk input. This class is the base for all - typed input classes and is also used when the client does not recognize an - input kind. - """ - - def __init__(self, service, path, kind=None, **kwargs): - # kind can be omitted (in which case it is inferred from the path) - # Otherwise, valid values are the paths from data/inputs ("udp", - # "monitor", "tcp/raw"), or two special cases: "tcp" (which is "tcp/raw") - # and "splunktcp" (which is "tcp/cooked"). - Entity.__init__(self, service, path, **kwargs) - if kind is None: - path_segments = path.split("/") - i = path_segments.index("inputs") + 1 - if path_segments[i] == "tcp": - self.kind = path_segments[i] + "/" + path_segments[i + 1] - else: - self.kind = path_segments[i] - else: - self.kind = kind - - # Handle old input kind names. - if self.kind == "tcp": - self.kind = "tcp/raw" - if self.kind == "splunktcp": - self.kind = "tcp/cooked" - - def update(self, **kwargs): - """Updates the server with any changes you've made to the current input - along with any additional arguments you specify. - - :param kwargs: Additional arguments (optional). For more about the - available parameters, see `Input parameters `_ on Splunk Developer Portal. - :type kwargs: ``dict`` - - :return: The input this method was called on. - :rtype: class:`Input` - """ - # UDP and TCP inputs require special handling due to their restrictToHost - # field. For all other inputs kinds, we can dispatch to the superclass method. - if self.kind not in ["tcp", "splunktcp", "tcp/raw", "tcp/cooked", "udp"]: - return super().update(**kwargs) - else: - # The behavior of restrictToHost is inconsistent across input kinds and versions of Splunk. - # In Splunk 4.x, the name of the entity is only the port, independent of the value of - # restrictToHost. In Splunk 5.0 this changed so the name will be of the form :. - # In 5.0 and 5.0.1, if you don't supply the restrictToHost value on every update, it will - # remove the host restriction from the input. As of 5.0.2 you simply can't change restrictToHost - # on an existing input. - - # The logic to handle all these cases: - # - Throw an exception if the user tries to set restrictToHost on an existing input - # for *any* version of Splunk. - # - Set the existing restrictToHost value on the update args internally so we don't - # cause it to change in Splunk 5.0 and 5.0.1. - to_update = kwargs.copy() - - if "restrictToHost" in kwargs: - raise IllegalOperationException( - "Cannot set restrictToHost on an existing input with the SDK." - ) - if "restrictToHost" in self._state.content and self.kind != "udp": - to_update["restrictToHost"] = self._state.content["restrictToHost"] - - # Do the actual update operation. - return super().update(**to_update) - - -# Inputs is a "kinded" collection, which is a heterogenous collection where -# each item is tagged with a kind, that provides a single merged view of all -# input kinds. -class Inputs(Collection): - """This class represents a collection of inputs. The collection is - heterogeneous and each member of the collection contains a *kind* property - that indicates the specific type of input. - Retrieve this collection using :meth:`Service.inputs`.""" - - def __init__(self, service, kindmap=None): - Collection.__init__(self, service, PATH_INPUTS, item=Input) - - def __getitem__(self, key): - # The key needed to retrieve the input needs it's parenthesis to be URL encoded - # based on the REST API for input - # - if isinstance(key, tuple) and len(key) == 2: - # Fetch a single kind - key, kind = key - key = UrlEncoded(key, encode_slash=True) - try: - response = self.get(self.kindpath(kind) + "/" + key) - entries = self._load_list(response) - if len(entries) > 1: - raise AmbiguousReferenceException( - f"Found multiple inputs of kind {kind} named {key}." - ) - if len(entries) == 0: - raise KeyError((key, kind)) - return entries[0] - except HTTPError as he: - if he.status == 404: # No entity matching kind and key - raise KeyError((key, kind)) - else: - raise - else: - # Iterate over all the kinds looking for matches. - kind = None - candidate = None - key = UrlEncoded(key, encode_slash=True) - for kind in self.kinds: - try: - response = self.get(kind + "/" + key) - entries = self._load_list(response) - if len(entries) > 1: - raise AmbiguousReferenceException( - f"Found multiple inputs of kind {kind} named {key}." - ) - if len(entries) == 0: - pass - else: - if ( - candidate is not None - ): # Already found at least one candidate - raise AmbiguousReferenceException( - f"Found multiple inputs named {key}, please specify a kind" - ) - candidate = entries[0] - except HTTPError as he: - if he.status == 404: - pass # Just carry on to the next kind. - else: - raise - if candidate is None: - raise KeyError(key) # Never found a match. - return candidate - - def __contains__(self, key): - if isinstance(key, tuple) and len(key) == 2: - # If we specify a kind, this will shortcut properly - try: - self.__getitem__(key) - return True - except KeyError: - return False - else: - # Without a kind, we want to minimize the number of round trips to the server, so we - # reimplement some of the behavior of __getitem__ in order to be able to stop searching - # on the first hit. - for kind in self.kinds: - try: - response = self.get(self.kindpath(kind) + "/" + key) - entries = self._load_list(response) - if len(entries) > 0: - return True - except HTTPError as he: - if he.status == 404: - pass # Just carry on to the next kind. - else: - raise - return False - - def create(self, name, kind, **kwargs): - """Creates an input of a specific kind in this collection, with any - arguments you specify. - - :param `name`: The input name. - :type name: ``string`` - :param `kind`: The kind of input: - - - "ad": Active Directory - - - "monitor": Files and directories - - - "registry": Windows Registry - - - "script": Scripts - - - "splunktcp": TCP, processed - - - "tcp": TCP, unprocessed - - - "udp": UDP - - - "win-event-log-collections": Windows event log - - - "win-perfmon": Performance monitoring - - - "win-wmi-collections": WMI - - :type kind: ``string`` - :param `kwargs`: Additional arguments (optional). For more about the - available parameters, see `Input parameters `_ on Splunk Developer Portal. - - :type kwargs: ``dict`` - - :return: The new :class:`Input`. - """ - kindpath = self.kindpath(kind) - self.post(kindpath, name=name, **kwargs) - - # If we created an input with restrictToHost set, then - # its path will be :, not just , - # and we have to adjust accordingly. - - # Url encodes the name of the entity. - name = UrlEncoded(name, encode_slash=True) - path = _path( - self.path + kindpath, - f"{kwargs['restrictToHost']}:{name}" - if "restrictToHost" in kwargs - else name, - ) - return Input(self.service, path, kind) - - def delete(self, name, kind=None): - """Removes an input from the collection. - - :param `kind`: The kind of input: - - - "ad": Active Directory - - - "monitor": Files and directories - - - "registry": Windows Registry - - - "script": Scripts - - - "splunktcp": TCP, processed - - - "tcp": TCP, unprocessed - - - "udp": UDP - - - "win-event-log-collections": Windows event log - - - "win-perfmon": Performance monitoring - - - "win-wmi-collections": WMI - - :type kind: ``string`` - :param name: The name of the input to remove. - :type name: ``string`` - - :return: The :class:`Inputs` collection. - """ - if kind is None: - self.service.delete(self[name].path) - else: - self.service.delete(self[name, kind].path) - return self - - def itemmeta(self, kind): - """Returns metadata for the members of a given kind. - - :param `kind`: The kind of input: - - - "ad": Active Directory - - - "monitor": Files and directories - - - "registry": Windows Registry - - - "script": Scripts - - - "splunktcp": TCP, processed - - - "tcp": TCP, unprocessed - - - "udp": UDP - - - "win-event-log-collections": Windows event log - - - "win-perfmon": Performance monitoring - - - "win-wmi-collections": WMI - - :type kind: ``string`` - - :return: The metadata. - :rtype: class:``splunklib.data.Record`` - """ - response = self.get(f"{self._kindmap[kind]}/_new") - content = _load_atom(response, MATCH_ENTRY_CONTENT) - return _parse_atom_metadata(content) - - def _get_kind_list(self, subpath=None): - if subpath is None: - subpath = [] - - kinds = [] - response = self.get("/".join(subpath)) - content = _load_atom_entries(response) - for entry in content: - this_subpath = subpath + [entry.title] - # The "all" endpoint doesn't work yet. - # The "tcp/ssl" endpoint is not a real input collection. - if entry.title == "all" or this_subpath == ["tcp", "ssl"]: - continue - if "create" in [x.rel for x in entry.link]: - path = "/".join(subpath + [entry.title]) - kinds.append(path) - else: - subkinds = self._get_kind_list(subpath + [entry.title]) - kinds.extend(subkinds) - return kinds - - @property - def kinds(self): - """Returns the input kinds on this Splunk instance. - - :return: The list of input kinds. - :rtype: ``list`` - """ - return self._get_kind_list() - - def kindpath(self, kind): - """Returns a path to the resources for a given input kind. - - :param `kind`: The kind of input: - - - "ad": Active Directory - - - "monitor": Files and directories - - - "registry": Windows Registry - - - "script": Scripts - - - "splunktcp": TCP, processed - - - "tcp": TCP, unprocessed - - - "udp": UDP - - - "win-event-log-collections": Windows event log - - - "win-perfmon": Performance monitoring - - - "win-wmi-collections": WMI - - :type kind: ``string`` - - :return: The relative endpoint path. - :rtype: ``string`` - """ - if kind == "tcp": - return UrlEncoded("tcp/raw", skip_encode=True) - if kind == "splunktcp": - return UrlEncoded("tcp/cooked", skip_encode=True) - return UrlEncoded(kind, skip_encode=True) - - def list(self, *kinds, **kwargs): - """Returns a list of inputs that are in the :class:`Inputs` collection. - You can also filter by one or more input kinds. - - This function iterates over all possible inputs, regardless of any arguments you - specify. Because the :class:`Inputs` collection is the union of all the inputs of each - kind, this method implements parameters such as "count", "search", and so - on at the Python level once all the data has been fetched. The exception - is when you specify a single input kind, and then this method makes a single request - with the usual semantics for parameters. - - :param kinds: The input kinds to return (optional). - - - "ad": Active Directory - - - "monitor": Files and directories - - - "registry": Windows Registry - - - "script": Scripts - - - "splunktcp": TCP, processed - - - "tcp": TCP, unprocessed - - - "udp": UDP - - - "win-event-log-collections": Windows event log - - - "win-perfmon": Performance monitoring - - - "win-wmi-collections": WMI - - :type kinds: ``string`` - :param kwargs: Additional arguments (optional): - - - "count" (``integer``): The maximum number of items to return. - - - "offset" (``integer``): The offset of the first item to return. - - - "search" (``string``): The search query to filter responses. - - - "sort_dir" (``string``): The direction to sort returned items: - "asc" or "desc". - - - "sort_key" (``string``): The field to use for sorting (optional). - - - "sort_mode" (``string``): The collating sequence for sorting - returned items: "auto", "alpha", "alpha_case", or "num". - - :type kwargs: ``dict`` - - :return: A list of input kinds. - :rtype: ``list`` - """ - if len(kinds) == 0: - kinds = self.kinds - if len(kinds) == 1: - kind = kinds[0] - logger.debug("Inputs.list taking short circuit branch for single kind.") - path = self.kindpath(kind) - logger.debug("Path for inputs: %s", path) - try: - path = UrlEncoded(path, skip_encode=True) - response = self.get(path, **kwargs) - except HTTPError as he: - if he.status == 404: # No inputs of this kind - return [] - entities = [] - entries = _load_atom_entries(response) - if entries is None: - return [] # No inputs in a collection comes back with no feed or entry in the XML - for entry in entries: - state = _parse_atom_entry(entry) - # Unquote the URL, since all URL encoded in the SDK - # should be of type UrlEncoded, and all str should not - # be URL encoded. - path = parse.unquote(state.links.alternate) - entity = Input(self.service, path, kind, state=state) - entities.append(entity) - return entities - - search = kwargs.get("search", "*") - - entities = [] - for kind in kinds: - response = None - try: - kind = UrlEncoded(kind, skip_encode=True) - response = self.get(self.kindpath(kind), search=search) - except HTTPError as e: - if e.status == 404: - continue # No inputs of this kind - else: - raise - - entries = _load_atom_entries(response) - if entries is None: - continue # No inputs to process - for entry in entries: - state = _parse_atom_entry(entry) - # Unquote the URL, since all URL encoded in the SDK - # should be of type UrlEncoded, and all str should not - # be URL encoded. - path = parse.unquote(state.links.alternate) - entity = Input(self.service, path, kind, state=state) - entities.append(entity) - if "offset" in kwargs: - entities = entities[kwargs["offset"] :] - if "count" in kwargs: - entities = entities[: kwargs["count"]] - if kwargs.get("sort_mode", None) == "alpha": - sort_field = kwargs.get("sort_field", "name") - if sort_field == "name": - f = lambda x: x.name.lower() - else: - f = lambda x: x[sort_field].lower() - entities = sorted(entities, key=f) - if kwargs.get("sort_mode", None) == "alpha_case": - sort_field = kwargs.get("sort_field", "name") - if sort_field == "name": - f = lambda x: x.name - else: - f = lambda x: x[sort_field] - entities = sorted(entities, key=f) - if kwargs.get("sort_dir", "asc") == "desc": - entities = list(reversed(entities)) - return entities - - def __iter__(self, **kwargs): - for item in self.iter(**kwargs): - yield item - - def iter(self, **kwargs): - """Iterates over the collection of inputs. - - :param kwargs: Additional arguments (optional): - - - "count" (``integer``): The maximum number of items to return. - - - "offset" (``integer``): The offset of the first item to return. - - - "search" (``string``): The search query to filter responses. - - - "sort_dir" (``string``): The direction to sort returned items: - "asc" or "desc". - - - "sort_key" (``string``): The field to use for sorting (optional). - - - "sort_mode" (``string``): The collating sequence for sorting - returned items: "auto", "alpha", "alpha_case", or "num". - - :type kwargs: ``dict`` - """ - for item in self.list(**kwargs): - yield item - - def oneshot(self, path, **kwargs): - """Creates a oneshot data input, which is an upload of a single file - for one-time indexing. - - :param path: The path and filename. - :type path: ``string`` - :param kwargs: Additional arguments (optional). For more about the - available parameters, see `Input parameters `_ on Splunk Developer Portal. - :type kwargs: ``dict`` - """ - self.post("oneshot", name=path, **kwargs) - - -class Job(Entity): - """This class represents a search job.""" - - def __init__(self, service, sid, **kwargs): - # Default to v2 in Splunk Version 9+ - path = "{path}{sid}" - # Formatting path based on the Splunk Version - if service.disable_v2_api: - path = path.format(path=PATH_JOBS, sid=sid) - else: - path = path.format(path=PATH_JOBS_V2, sid=sid) - - Entity.__init__(self, service, path, skip_refresh=True, **kwargs) - self.sid = sid - - # The Job entry record is returned at the root of the response - def _load_atom_entry(self, response): - return _load_atom(response).entry - - def cancel(self): - """Stops the current search and deletes the results cache. - - :return: The :class:`Job`. - """ - try: - self.post("control", action="cancel") - except HTTPError as he: - if he.status == 404: - # The job has already been cancelled, so - # cancelling it twice is a nop. - pass - else: - raise - return self - - def disable_preview(self): - """Disables preview for this job. - - :return: The :class:`Job`. - """ - self.post("control", action="disablepreview") - return self - - def enable_preview(self): - """Enables preview for this job. - - **Note**: Enabling preview might slow search considerably. - - :return: The :class:`Job`. - """ - self.post("control", action="enablepreview") - return self - - def events(self, **kwargs): - """Returns a streaming handle to this job's events. - - :param kwargs: Additional parameters (optional). For a list of valid - parameters, see `GET search/jobs/{search_id}/events - `_ - in the REST API documentation. - :type kwargs: ``dict`` - - :return: The ``InputStream`` IO handle to this job's events. - """ - kwargs["segmentation"] = kwargs.get("segmentation", "none") - - # Search API v1(GET) and v2(POST) - if self.service.disable_v2_api: - return self.get("events", **kwargs).body - return self.post("events", **kwargs).body - - def finalize(self): - """Stops the job and provides intermediate results for retrieval. - - :return: The :class:`Job`. - """ - self.post("control", action="finalize") - return self - - def is_done(self): - """Indicates whether this job finished running. - - :return: ``True`` if the job is done, ``False`` if not. - :rtype: ``boolean`` - """ - if not self.is_ready(): - return False - done = self._state.content["isDone"] == "1" - return done - - def is_ready(self): - """Indicates whether this job is ready for querying. - - :return: ``True`` if the job is ready, ``False`` if not. - :rtype: ``boolean`` - - """ - response = self.get() - if response.status == 204: - return False - self._state = self.read(response) - ready = self._state.content["dispatchState"] not in ["QUEUED", "PARSING"] - return ready - - @property - def name(self): - """Returns the name of the search job, which is the search ID (SID). - - :return: The search ID. - :rtype: ``string`` - """ - return self.sid - - def pause(self): - """Suspends the current search. - - :return: The :class:`Job`. - """ - self.post("control", action="pause") - return self - - def results(self, **query_params): - """Returns a streaming handle to this job's search results. To get a nice, Pythonic iterator, pass the handle - to :class:`splunklib.results.JSONResultsReader` along with the query param "output_mode='json'", as in:: - - import splunklib.client as client - import splunklib.results as results - from time import sleep - service = client.connect(...) - job = service.jobs.create("search * | head 5") - while not job.is_done(): - sleep(.2) - rr = results.JSONResultsReader(job.results(output_mode='json')) - for result in rr: - if isinstance(result, results.Message): - # Diagnostic messages may be returned in the results - print(f'{result.type}: {result.message}') - elif isinstance(result, dict): - # Normal events are returned as dicts - print(result) - assert rr.is_preview == False - - Results are not available until the job has finished. If called on - an unfinished job, the result is an empty event set. - - This method makes a single roundtrip - to the server, plus at most two additional round trips if - the ``autologin`` field of :func:`connect` is set to ``True``. - - :param query_params: Additional parameters (optional). For a list of valid - parameters, see `GET search/jobs/{search_id}/results - `_. - :type query_params: ``dict`` - - :return: The ``InputStream`` IO handle to this job's results. - """ - query_params["segmentation"] = query_params.get("segmentation", "none") - - # Search API v1(GET) and v2(POST) - if self.service.disable_v2_api: - return self.get("results", **query_params).body - return self.post("results", **query_params).body - - def preview(self, **query_params): - """Returns a streaming handle to this job's preview search results. - - Unlike :class:`splunklib.results.JSONResultsReader`along with the query param "output_mode='json'", - which requires a job to be finished to return any results, the ``preview`` method returns any results that - have been generated so far, whether the job is running or not. The returned search results are the raw data - from the server. Pass the handle returned to :class:`splunklib.results.JSONResultsReader` to get a nice, - Pythonic iterator over objects, as in:: - - import splunklib.client as client - import splunklib.results as results - service = client.connect(...) - job = service.jobs.create("search * | head 5") - rr = results.JSONResultsReader(job.preview(output_mode='json')) - for result in rr: - if isinstance(result, results.Message): - # Diagnostic messages may be returned in the results - print(f'{result.type}: {result.message}') - elif isinstance(result, dict): - # Normal events are returned as dicts - print(result) - if rr.is_preview: - print("Preview of a running search job.") - else: - print("Job is finished. Results are final.") - - This method makes one roundtrip to the server, plus at most - two more if - the ``autologin`` field of :func:`connect` is set to ``True``. - - :param query_params: Additional parameters (optional). For a list of valid - parameters, see `GET search/jobs/{search_id}/results_preview - `_ - in the REST API documentation. - :type query_params: ``dict`` - - :return: The ``InputStream`` IO handle to this job's preview results. - """ - query_params["segmentation"] = query_params.get("segmentation", "none") - - # Search API v1(GET) and v2(POST) - if self.service.disable_v2_api: - return self.get("results_preview", **query_params).body - return self.post("results_preview", **query_params).body - - def searchlog(self, **kwargs): - """Returns a streaming handle to this job's search log. - - :param `kwargs`: Additional parameters (optional). For a list of valid - parameters, see `GET search/jobs/{search_id}/search.log - `_ - in the REST API documentation. - :type kwargs: ``dict`` - - :return: The ``InputStream`` IO handle to this job's search log. - """ - return self.get("search.log", **kwargs).body - - def set_priority(self, value): - """Sets this job's search priority in the range of 0-10. - - Higher numbers indicate higher priority. Unless splunkd is - running as *root*, you can only decrease the priority of a running job. - - :param `value`: The search priority. - :type value: ``integer`` - - :return: The :class:`Job`. - """ - self.post("control", action="setpriority", priority=value) - return self - - def summary(self, **kwargs): - """Returns a streaming handle to this job's summary. - - :param `kwargs`: Additional parameters (optional). For a list of valid - parameters, see `GET search/jobs/{search_id}/summary - `_ - in the REST API documentation. - :type kwargs: ``dict`` - - :return: The ``InputStream`` IO handle to this job's summary. - """ - return self.get("summary", **kwargs).body - - def timeline(self, **kwargs): - """Returns a streaming handle to this job's timeline results. - - :param `kwargs`: Additional timeline arguments (optional). For a list of valid - parameters, see `GET search/jobs/{search_id}/timeline - `_ - in the REST API documentation. - :type kwargs: ``dict`` - - :return: The ``InputStream`` IO handle to this job's timeline. - """ - return self.get("timeline", **kwargs).body - - def touch(self): - """Extends the expiration time of the search to the current time (now) plus - the time-to-live (ttl) value. - - :return: The :class:`Job`. - """ - self.post("control", action="touch") - return self - - def set_ttl(self, value): - """Set the job's time-to-live (ttl) value, which is the time before the - search job expires and is still available. - - :param `value`: The ttl value, in seconds. - :type value: ``integer`` - - :return: The :class:`Job`. - """ - self.post("control", action="setttl", ttl=value) - return self - - def unpause(self): - """Resumes the current search, if paused. - - :return: The :class:`Job`. - """ - self.post("control", action="unpause") - return self - - -class Jobs(Collection): - """This class represents a collection of search jobs. Retrieve this - collection using :meth:`Service.jobs`.""" - - def __init__(self, service): - # Splunk 9 introduces the v2 endpoint - if not service.disable_v2_api: - path = PATH_JOBS_V2 - else: - path = PATH_JOBS - Collection.__init__(self, service, path, item=Job) - # The count value to say list all the contents of this - # Collection is 0, not -1 as it is on most. - self.null_count = 0 - - def _load_list(self, response): - # Overridden because Job takes a sid instead of a path. - entries = _load_atom_entries(response) - if entries is None: - return [] - entities = [] - for entry in entries: - state = _parse_atom_entry(entry) - entity = self.item(self.service, entry["content"]["sid"], state=state) - entities.append(entity) - return entities - - def create(self, query, **kwargs): - """Creates a search using a search query and any additional parameters - you provide. - - :param query: The search query. - :type query: ``string`` - :param kwargs: Additiona parameters (optional). For a list of available - parameters, see `Search job parameters - `_ - on Splunk Developer Portal. - :type kwargs: ``dict`` - - :return: The :class:`Job`. - """ - if kwargs.get("exec_mode", None) == "oneshot": - raise TypeError( - "Cannot specify exec_mode=oneshot; use the oneshot method instead." - ) - response = self.post(search=query, **kwargs) - sid = _load_sid(response, kwargs.get("output_mode", None)) - return Job(self.service, sid) - - def export(self, query, **params): - """Runs a search and immediately starts streaming preview events. This method returns a streaming handle to - this job's events as an XML document from the server. To parse this stream into usable Python objects, - pass the handle to :class:`splunklib.results.JSONResultsReader` along with the query param - "output_mode='json'":: - - import splunklib.client as client - import splunklib.results as results - service = client.connect(...) - rr = results.JSONResultsReader(service.jobs.export("search * | head 5",output_mode='json')) - for result in rr: - if isinstance(result, results.Message): - # Diagnostic messages may be returned in the results - print(f'{result.type}: {result.message}') - elif isinstance(result, dict): - # Normal events are returned as dicts - print(result) - assert rr.is_preview == False - - Running an export search is more efficient as it streams the results - directly to you, rather than having to write them out to disk and make - them available later. As soon as results are ready, you will receive - them. - - The ``export`` method makes a single roundtrip to the server (as opposed - to two for :meth:`create` followed by :meth:`preview`), plus at most two - more if the ``autologin`` field of :func:`connect` is set to ``True``. - - :raises `ValueError`: Raised for invalid queries. - :param query: The search query. - :type query: ``string`` - :param params: Additional arguments (optional). For a list of valid - parameters, see `GET search/jobs/export - `_ - in the REST API documentation. - :type params: ``dict`` - - :return: The ``InputStream`` IO handle to raw XML returned from the server. - """ - if "exec_mode" in params: - raise TypeError("Cannot specify an exec_mode to export.") - params["segmentation"] = params.get("segmentation", "none") - return self.post(path_segment="export", search=query, **params).body - - def itemmeta(self): - """There is no metadata available for class:``Jobs``. - - Any call to this method raises a class:``NotSupportedError``. - - :raises: class:``NotSupportedError`` - """ - raise NotSupportedError() - - def oneshot(self, query, **params): - """Run a oneshot search and returns a streaming handle to the results. - - The ``InputStream`` object streams fragments from the server. To parse this stream into usable Python - objects, pass the handle to :class:`splunklib.results.JSONResultsReader` along with the query param - "output_mode='json'" :: - - import splunklib.client as client - import splunklib.results as results - service = client.connect(...) - rr = results.JSONResultsReader(service.jobs.oneshot("search * | head 5",output_mode='json')) - for result in rr: - if isinstance(result, results.Message): - # Diagnostic messages may be returned in the results - print(f'{result.type}: {result.message}') - elif isinstance(result, dict): - # Normal events are returned as dicts - print(result) - assert rr.is_preview == False - - The ``oneshot`` method makes a single roundtrip to the server (as opposed - to two for :meth:`create` followed by :meth:`results`), plus at most two more - if the ``autologin`` field of :func:`connect` is set to ``True``. - - :raises ValueError: Raised for invalid queries. - - :param query: The search query. - :type query: ``string`` - :param params: Additional arguments (optional): - - - "output_mode": Specifies the output format of the results (XML, - JSON, or CSV). - - - "earliest_time": Specifies the earliest time in the time range to - search. The time string can be a UTC time (with fractional seconds), - a relative time specifier (to now), or a formatted time string. - - - "latest_time": Specifies the latest time in the time range to - search. The time string can be a UTC time (with fractional seconds), - a relative time specifier (to now), or a formatted time string. - - - "rf": Specifies one or more fields to add to the search. - - :type params: ``dict`` - - :return: The ``InputStream`` IO handle to raw XML returned from the server. - """ - if "exec_mode" in params: - raise TypeError("Cannot specify an exec_mode to oneshot.") - params["segmentation"] = params.get("segmentation", "none") - return self.post(search=query, exec_mode="oneshot", **params).body - - -class Loggers(Collection): - """This class represents a collection of service logging categories. - Retrieve this collection using :meth:`Service.loggers`.""" - - def __init__(self, service): - Collection.__init__(self, service, PATH_LOGGER) - - def itemmeta(self): - """There is no metadata available for class:``Loggers``. - - Any call to this method raises a class:``NotSupportedError``. - - :raises: class:``NotSupportedError`` - """ - raise NotSupportedError() - - -class Message(Entity): - def __init__(self, service, path, **kwargs): - Entity.__init__(self, service, path, **kwargs) - - @property - def value(self): - """Returns the message value. - - :return: The message value. - :rtype: ``string`` - """ - return self[self.name] - - -class ModularInputKind(Entity): - """This class contains the different types of modular inputs. Retrieve this - collection using :meth:`Service.modular_input_kinds`. - """ - - def __contains__(self, name): - args = self.state.content["endpoints"]["args"] - if name in args: - return True - return Entity.__contains__(self, name) - - def __getitem__(self, name): - args = self.state.content["endpoint"]["args"] - if name in args: - return args["item"] - return Entity.__getitem__(self, name) - - @property - def arguments(self): - """A dictionary of all the arguments supported by this modular input kind. - - The keys in the dictionary are the names of the arguments. The values are - another dictionary giving the metadata about that argument. The possible - keys in that dictionary are ``"title"``, ``"description"``, ``"required_on_create``", - ``"required_on_edit"``, ``"data_type"``. Each value is a string. It should be one - of ``"true"`` or ``"false"`` for ``"required_on_create"`` and ``"required_on_edit"``, - and one of ``"boolean"``, ``"string"``, or ``"number``" for ``"data_type"``. - - :return: A dictionary describing the arguments this modular input kind takes. - :rtype: ``dict`` - """ - return self.state.content["endpoint"]["args"] - - def update(self, **kwargs): - """Raises an error. Modular input kinds are read only.""" - raise IllegalOperationException( - "Modular input kinds cannot be updated via the REST API." - ) - - -class SavedSearch(Entity): - """This class represents a saved search.""" - - def __init__(self, service, path, **kwargs): - Entity.__init__(self, service, path, **kwargs) - - def acknowledge(self): - """Acknowledges the suppression of alerts from this saved search and - resumes alerting. - - :return: The :class:`SavedSearch`. - """ - self.post("acknowledge") - return self - - @property - def alert_count(self): - """Returns the number of alerts fired by this saved search. - - :return: The number of alerts fired by this saved search. - :rtype: ``integer`` - """ - return int(self._state.content.get("triggered_alert_count", 0)) - - def dispatch(self, **kwargs): - """Runs the saved search and returns the resulting search job. - - :param `kwargs`: Additional dispatch arguments (optional). For details, - see the `POST saved/searches/{name}/dispatch - `_ - endpoint in the REST API documentation. - :type kwargs: ``dict`` - :return: The :class:`Job`. - """ - response = self.post("dispatch", **kwargs) - sid = _load_sid(response, kwargs.get("output_mode", None)) - return Job(self.service, sid) - - @property - def fired_alerts(self): - """Returns the collection of fired alerts (a fired alert group) - corresponding to this saved search's alerts. - - :raises IllegalOperationException: Raised when the search is not scheduled. - - :return: A collection of fired alerts. - :rtype: :class:`AlertGroup` - """ - if self["is_scheduled"] == "0": - raise IllegalOperationException( - "Unscheduled saved searches have no alerts." - ) - c = Collection( - self.service, - self.service._abspath( - PATH_FIRED_ALERTS + self.name, - owner=self._state.access.owner, - app=self._state.access.app, - sharing=self._state.access.sharing, - ), - item=AlertGroup, - ) - return c - - def history(self, **kwargs): - """Returns a list of search jobs corresponding to this saved search. - - :param `kwargs`: Additional arguments (optional). - :type kwargs: ``dict`` - - :return: A list of :class:`Job` objects. - """ - response = self.get("history", **kwargs) - entries = _load_atom_entries(response) - if entries is None: - return [] - jobs = [] - for entry in entries: - job = Job(self.service, entry.title) - jobs.append(job) - return jobs - - def update(self, search=None, **kwargs): - """Updates the server with any changes you've made to the current saved - search along with any additional arguments you specify. - - :param `search`: The search query (optional). - :type search: ``string`` - :param `kwargs`: Additional arguments (optional). For a list of available - parameters, see `Saved search parameters - `_ - on Splunk Developer Portal. - :type kwargs: ``dict`` - - :return: The :class:`SavedSearch`. - """ - # Updates to a saved search *require* that the search string be - # passed, so we pass the current search string if a value wasn't - # provided by the caller. - if search is None: - search = self.content.search - Entity.update(self, search=search, **kwargs) - return self - - def scheduled_times(self, earliest_time="now", latest_time="+1h"): - """Returns the times when this search is scheduled to run. - - By default this method returns the times in the next hour. For different - time ranges, set *earliest_time* and *latest_time*. For example, - for all times in the last day use "earliest_time=-1d" and - "latest_time=now". - - :param earliest_time: The earliest time. - :type earliest_time: ``string`` - :param latest_time: The latest time. - :type latest_time: ``string`` - - :return: The list of search times. - """ - response = self.get( - "scheduled_times", earliest_time=earliest_time, latest_time=latest_time - ) - data = self._load_atom_entry(response) - rec = _parse_atom_entry(data) - times = [datetime.fromtimestamp(int(t)) for t in rec.content.scheduled_times] - return times - - def suppress(self, expiration): - """Skips any scheduled runs of this search in the next *expiration* - number of seconds. - - :param expiration: The expiration period, in seconds. - :type expiration: ``integer`` - - :return: The :class:`SavedSearch`. - """ - self.post("suppress", expiration=expiration) - return self - - @property - def suppressed(self): - """Returns the number of seconds that this search is blocked from running - (possibly 0). - - :return: The number of seconds. - :rtype: ``integer`` - """ - r = self._run_action("suppress") - if r.suppressed == "1": - return int(r.expiration) - return 0 - - def unsuppress(self): - """Cancels suppression and makes this search run as scheduled. - - :return: The :class:`SavedSearch`. - """ - self.post("suppress", expiration="0") - return self - - -class SavedSearches(Collection): - """This class represents a collection of saved searches. Retrieve this - collection using :meth:`Service.saved_searches`.""" - - def __init__(self, service): - Collection.__init__(self, service, PATH_SAVED_SEARCHES, item=SavedSearch) - - def create(self, name, search, **kwargs): - """Creates a saved search. - - :param name: The name for the saved search. - :type name: ``string`` - :param search: The search query. - :type search: ``string`` - :param kwargs: Additional arguments (optional). For a list of available - parameters, see `Saved search parameters - `_ - on Splunk Developer Portal. - :type kwargs: ``dict`` - :return: The :class:`SavedSearches` collection. - """ - return Collection.create(self, name, search=search, **kwargs) - - -class Macro(Entity): - """This class represents a search macro.""" - - def __init__(self, service, path, **kwargs): - Entity.__init__(self, service, path, **kwargs) - - @property - def args(self): - """Returns the macro arguments. - :return: The macro arguments. - :rtype: ``string`` - """ - return self._state.content.get("args", "") - - @property - def definition(self): - """Returns the macro definition. - :return: The macro definition. - :rtype: ``string`` - """ - return self._state.content.get("definition", "") - - @property - def errormsg(self): - """Returns the validation error message for the macro. - :return: The validation error message for the macro. - :rtype: ``string`` - """ - return self._state.content.get("errormsg", "") - - @property - def iseval(self): - """Returns the eval-based definition status of the macro. - :return: The iseval value for the macro. - :rtype: ``string`` - """ - return self._state.content.get("iseval", "0") - - def update(self, definition=None, **kwargs): - """Updates the server with any changes you've made to the current macro - along with any additional arguments you specify. - :param `definition`: The macro definition (optional). - :type definition: ``string`` - :param `kwargs`: Additional arguments (optional). Available parameters are: - 'disabled', 'iseval', 'validation', and 'errormsg'. - :type kwargs: ``dict`` - :return: The :class:`Macro`. - """ - # Updates to a macro *require* that the definition be - # passed, so we pass the current definition if a value wasn't - # provided by the caller. - if definition is None: - definition = self.content.definition - Entity.update(self, definition=definition, **kwargs) - return self - - @property - def validation(self): - """Returns the validation expression for the macro. - :return: The validation expression for the macro. - :rtype: ``string`` - """ - return self._state.content.get("validation", "") - - -class Macros(Collection): - """This class represents a collection of macros. Retrieve this - collection using :meth:`Service.macros`.""" - - def __init__(self, service): - Collection.__init__(self, service, PATH_MACROS, item=Macro) - - def create(self, name, definition, **kwargs): - """Creates a macro. - :param name: The name for the macro. - :type name: ``string`` - :param definition: The macro definition. - :type definition: ``string`` - :param kwargs: Additional arguments (optional). Available parameters are: - 'disabled', 'iseval', 'validation', and 'errormsg'. - :type kwargs: ``dict`` - :return: The :class:`Macros` collection. - """ - return Collection.create(self, name, definition=definition, **kwargs) - - -class Settings(Entity): - """This class represents configuration settings for a Splunk service. - Retrieve this collection using :meth:`Service.settings`.""" - - def __init__(self, service, **kwargs): - Entity.__init__(self, service, "/services/server/settings", **kwargs) - - # Updates on the settings endpoint are POSTed to server/settings/settings. - def update(self, **kwargs): - """Updates the settings on the server using the arguments you provide. - - :param kwargs: Additional arguments. For a list of valid arguments, see - `POST server/settings/{name} - `_ - in the REST API documentation. - :type kwargs: ``dict`` - :return: The :class:`Settings` collection. - """ - self.service.post("/services/server/settings/settings", **kwargs) - return self - - -class User(Entity): - """This class represents a Splunk user.""" - - @property - def role_entities(self): - """Returns a list of roles assigned to this user. - - :return: The list of roles. - :rtype: ``list`` - """ - all_role_names = [r.name for r in self.service.roles.list()] - return [ - self.service.roles[name] - for name in self.content.roles - if name in all_role_names - ] - - -# Splunk automatically lowercases new user names so we need to match that -# behavior here to ensure that the subsequent member lookup works correctly. -class Users(Collection): - """This class represents the collection of Splunk users for this instance of - Splunk. Retrieve this collection using :meth:`Service.users`. - """ - - def __init__(self, service): - Collection.__init__(self, service, PATH_USERS, item=User) - - def __getitem__(self, key): - return Collection.__getitem__(self, key.lower()) - - def __contains__(self, name): - return Collection.__contains__(self, name.lower()) - - def create(self, username, password, roles, **params): - """Creates a new user. - - This function makes two roundtrips to the server, plus at most - two more if - the ``autologin`` field of :func:`connect` is set to ``True``. - - :param username: The username. - :type username: ``string`` - :param password: The password. - :type password: ``string`` - :param roles: A single role or list of roles for the user. - :type roles: ``string`` or ``list`` - :param params: Additional arguments (optional). For a list of available - parameters, see `User authentication parameters - `_ - on Splunk Developer Portal. - :type params: ``dict`` - - :return: The new user. - :rtype: :class:`User` - - **Example**:: - - import splunklib.client as client - c = client.connect(...) - users = c.users - boris = users.create("boris", "securepassword", roles="user") - hilda = users.create("hilda", "anotherpassword", roles=["user","power"]) - """ - if not isinstance(username, str): - raise ValueError(f"Invalid username: {str(username)}") - username = username.lower() - self.post(name=username, password=password, roles=roles, **params) - # splunkd doesn't return the user in the POST response body, - # so we have to make a second round trip to fetch it. - response = self.get(username) - entry = _load_atom(response, XNAME_ENTRY).entry - state = _parse_atom_entry(entry) - entity = self.item( - self.service, parse.unquote(state.links.alternate), state=state - ) - return entity - - def delete(self, name): - """Deletes the user and returns the resulting collection of users. - - :param name: The name of the user to delete. - :type name: ``string`` - - :return: - :rtype: :class:`Users` - """ - return Collection.delete(self, name.lower()) - - -class Role(Entity): - """This class represents a user role.""" - - def grant(self, *capabilities_to_grant): - """Grants additional capabilities to this role. - - :param capabilities_to_grant: Zero or more capabilities to grant this - role. For a list of capabilities, see - `Capabilities `_ - on Splunk Developer Portal. - :type capabilities_to_grant: ``string`` or ``list`` - :return: The :class:`Role`. - - **Example**:: - - service = client.connect(...) - role = service.roles['somerole'] - role.grant('change_own_password', 'search') - """ - possible_capabilities = self.service.capabilities - for capability in capabilities_to_grant: - if capability not in possible_capabilities: - raise NoSuchCapability(capability) - new_capabilities = self["capabilities"] + list(capabilities_to_grant) - self.post(capabilities=new_capabilities) - return self - - def revoke(self, *capabilities_to_revoke): - """Revokes zero or more capabilities from this role. - - :param capabilities_to_revoke: Zero or more capabilities to grant this - role. For a list of capabilities, see - `Capabilities `_ - on Splunk Developer Portal. - :type capabilities_to_revoke: ``string`` or ``list`` - - :return: The :class:`Role`. - - **Example**:: - - service = client.connect(...) - role = service.roles['somerole'] - role.revoke('change_own_password', 'search') - """ - possible_capabilities = self.service.capabilities - for capability in capabilities_to_revoke: - if capability not in possible_capabilities: - raise NoSuchCapability(capability) - old_capabilities = self["capabilities"] - new_capabilities = [] - for c in old_capabilities: - if c not in capabilities_to_revoke: - new_capabilities.append(c) - if not new_capabilities: - new_capabilities = "" # Empty lists don't get passed in the body, so we have to force an empty argument. - self.post(capabilities=new_capabilities) - return self - - -class Roles(Collection): - """This class represents the collection of roles in the Splunk instance. - Retrieve this collection using :meth:`Service.roles`.""" - - def __init__(self, service): - Collection.__init__(self, service, PATH_ROLES, item=Role) - - def __getitem__(self, key): - return Collection.__getitem__(self, key.lower()) - - def __contains__(self, name): - return Collection.__contains__(self, name.lower()) - - def create(self, name, **params): - """Creates a new role. - - This function makes two roundtrips to the server, plus at most - two more if - the ``autologin`` field of :func:`connect` is set to ``True``. - - :param name: Name for the role. - :type name: ``string`` - :param params: Additional arguments (optional). For a list of available - parameters, see `Roles parameters - `_ - on Splunk Developer Portal. - :type params: ``dict`` - - :return: The new role. - :rtype: :class:`Role` - - **Example**:: - - import splunklib.client as client - c = client.connect(...) - roles = c.roles - paltry = roles.create("paltry", imported_roles="user", defaultApp="search") - """ - if not isinstance(name, str): - raise ValueError(f"Invalid role name: {str(name)}") - name = name.lower() - self.post(name=name, **params) - # splunkd doesn't return the user in the POST response body, - # so we have to make a second round trip to fetch it. - response = self.get(name) - entry = _load_atom(response, XNAME_ENTRY).entry - state = _parse_atom_entry(entry) - entity = self.item( - self.service, parse.unquote(state.links.alternate), state=state - ) - return entity - - def delete(self, name): - """Deletes the role and returns the resulting collection of roles. - - :param name: The name of the role to delete. - :type name: ``string`` - - :rtype: The :class:`Roles` - """ - return Collection.delete(self, name.lower()) - - -class Application(Entity): - """Represents a locally-installed Splunk app.""" - - @property - def setupInfo(self): - """Returns the setup information for the app. - - :return: The setup information. - """ - return self.content.get("eai:setup", None) - - def package(self): - """Creates a compressed package of the app for archiving.""" - return self._run_action("package") - - def updateInfo(self): - """Returns any update information that is available for the app.""" - return self._run_action("update") - - -class KVStoreCollections(Collection): - def __init__(self, service): - Collection.__init__( - self, service, "storage/collections/config", item=KVStoreCollection - ) - - def __getitem__(self, item): - res = Collection.__getitem__(self, item) - for k, v in res.content.items(): - if "accelerated_fields" in k: - res.content[k] = json.loads(v) - return res - - def create(self, name, accelerated_fields={}, fields={}, **kwargs): - """Creates a KV Store Collection. - - :param name: name of collection to create - :type name: ``string`` - :param accelerated_fields: dictionary of accelerated_fields definitions - :type accelerated_fields: ``dict`` - :param fields: dictionary of field definitions - :type fields: ``dict`` - :param kwargs: a dictionary of additional parameters specifying indexes and field definitions - :type kwargs: ``dict`` - - :return: Result of POST request - """ - for k, v in accelerated_fields.items(): - if isinstance(v, dict): - v = json.dumps(v) - kwargs["accelerated_fields." + k] = v - for k, v in fields.items(): - kwargs["field." + k] = v - return self.post(name=name, **kwargs) - - -class KVStoreCollection(Entity): - @property - def data(self): - """Returns data object for this Collection. - - :rtype: :class:`KVStoreCollectionData` - """ - return KVStoreCollectionData(self) - - def update_accelerated_field(self, name, value): - """Changes the definition of a KV Store accelerated_field. - - :param name: name of accelerated_fields to change - :type name: ``string`` - :param value: new accelerated_fields definition - :type value: ``dict`` - - :return: Result of POST request - """ - kwargs = {} - kwargs["accelerated_fields." + name] = ( - json.dumps(value) if isinstance(value, dict) else value - ) - return self.post(**kwargs) - - def update_field(self, name, value): - """Changes the definition of a KV Store field. - - :param name: name of field to change - :type name: ``string`` - :param value: new field definition - :type value: ``string`` - - :return: Result of POST request - """ - kwargs = {} - kwargs["field." + name] = value - return self.post(**kwargs) - - -class KVStoreCollectionData: - """This class represents the data endpoint for a KVStoreCollection. - - Retrieve using :meth:`KVStoreCollection.data` - """ - - JSON_HEADER = [("Content-Type", "application/json")] - - def __init__(self, collection): - self.service = collection.service - self.collection = collection - self.owner, self.app, self.sharing = collection._proper_namespace() - self.path = ( - "storage/collections/data/" - + UrlEncoded(self.collection.name, encode_slash=True) - + "/" - ) - - def _get(self, url, **kwargs): - return self.service.get( - self.path + url, - owner=self.owner, - app=self.app, - sharing=self.sharing, - **kwargs, - ) - - def _post(self, url, **kwargs): - return self.service.post( - self.path + url, - owner=self.owner, - app=self.app, - sharing=self.sharing, - **kwargs, - ) - - def _delete(self, url, **kwargs): - return self.service.delete( - self.path + url, - owner=self.owner, - app=self.app, - sharing=self.sharing, - **kwargs, - ) - - def query(self, **query): - """ - Gets the results of query, with optional parameters sort, limit, skip, and fields. - - :param query: Optional parameters. Valid options are sort, limit, skip, and fields - :type query: ``dict`` - - :return: Array of documents retrieved by query. - :rtype: ``array`` - """ - - for key, value in query.items(): - if isinstance(query[key], dict): - query[key] = json.dumps(value) - - return json.loads(self._get("", **query).body.read().decode("utf-8")) - - def query_by_id(self, id): - """ - Returns object with _id = id. - - :param id: Value for ID. If not a string will be coerced to string. - :type id: ``string`` - - :return: Document with id - :rtype: ``dict`` - """ - return json.loads( - self._get(UrlEncoded(str(id), encode_slash=True)) - .body.read() - .decode("utf-8") - ) - - def insert(self, data): - """ - Inserts item into this collection. An _id field will be generated if not assigned in the data. - - :param data: Document to insert - :type data: ``string`` - - :return: _id of inserted object - :rtype: ``dict`` - """ - if isinstance(data, dict): - data = json.dumps(data) - return json.loads( - self._post("", headers=KVStoreCollectionData.JSON_HEADER, body=data) - .body.read() - .decode("utf-8") - ) - - def delete(self, query=None): - """ - Deletes all data in collection if query is absent. Otherwise, deletes all data matched by query. - - :param query: Query to select documents to delete - :type query: ``string`` - - :return: Result of DELETE request - """ - return self._delete("", **({"query": query}) if query else {}) - - def delete_by_id(self, id): - """ - Deletes document that has _id = id. - - :param id: id of document to delete - :type id: ``string`` - - :return: Result of DELETE request - """ - return self._delete(UrlEncoded(str(id), encode_slash=True)) - - def update(self, id, data): - """ - Replaces document with _id = id with data. - - :param id: _id of document to update - :type id: ``string`` - :param data: the new document to insert - :type data: ``string`` - - :return: id of replaced document - :rtype: ``dict`` - """ - if isinstance(data, dict): - data = json.dumps(data) - return json.loads( - self._post( - UrlEncoded(str(id), encode_slash=True), - headers=KVStoreCollectionData.JSON_HEADER, - body=data, - ) - .body.read() - .decode("utf-8") - ) - - def batch_find(self, *dbqueries): - """ - Returns array of results from queries dbqueries. - - :param dbqueries: Array of individual queries as dictionaries - :type dbqueries: ``array`` of ``dict`` - - :return: Results of each query - :rtype: ``array`` of ``array`` - """ - if len(dbqueries) < 1: - raise Exception("Must have at least one query.") - - data = json.dumps(dbqueries) - - return json.loads( - self._post( - "batch_find", headers=KVStoreCollectionData.JSON_HEADER, body=data - ) - .body.read() - .decode("utf-8") - ) - - def batch_save(self, *documents): - """ - Inserts or updates every document specified in documents. - - :param documents: Array of documents to save as dictionaries - :type documents: ``array`` of ``dict`` - - :return: Results of update operation as overall stats - :rtype: ``dict`` - """ - if len(documents) < 1: - raise Exception("Must have at least one document.") - - data = json.dumps(documents) - - return json.loads( - self._post( - "batch_save", headers=KVStoreCollectionData.JSON_HEADER, body=data - ) - .body.read() - .decode("utf-8") - ) diff --git a/apps/bitwarden_event_logs/lib/splunklib/data.py b/apps/bitwarden_event_logs/lib/splunklib/data.py deleted file mode 100755 index 1f026ed8..00000000 --- a/apps/bitwarden_event_logs/lib/splunklib/data.py +++ /dev/null @@ -1,281 +0,0 @@ -# Copyright © 2011-2024 Splunk, Inc. -# -# 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. - -"""The **splunklib.data** module reads the responses from splunkd in Atom Feed -format, which is the format used by most of the REST API. -""" - -from xml.etree.ElementTree import XML - -__all__ = ["load", "record"] - -# LNAME refers to element names without namespaces; XNAME is the same -# name, but with an XML namespace. -LNAME_DICT = "dict" -LNAME_ITEM = "item" -LNAME_KEY = "key" -LNAME_LIST = "list" - -XNAMEF_REST = "{http://dev.splunk.com/ns/rest}%s" -XNAME_DICT = XNAMEF_REST % LNAME_DICT -XNAME_ITEM = XNAMEF_REST % LNAME_ITEM -XNAME_KEY = XNAMEF_REST % LNAME_KEY -XNAME_LIST = XNAMEF_REST % LNAME_LIST - - -# Some responses don't use namespaces (eg: search/parse) so we look for -# both the extended and local versions of the following names. - - -def isdict(name): - return name in (XNAME_DICT, LNAME_DICT) - - -def isitem(name): - return name in (XNAME_ITEM, LNAME_ITEM) - - -def iskey(name): - return name in (XNAME_KEY, LNAME_KEY) - - -def islist(name): - return name in (XNAME_LIST, LNAME_LIST) - - -def hasattrs(element): - return len(element.attrib) > 0 - - -def localname(xname): - rcurly = xname.find("}") - return xname if rcurly == -1 else xname[rcurly + 1 :] - - -def load(text, match=None): - """This function reads a string that contains the XML of an Atom Feed, then - returns the - data in a native Python structure (a ``dict`` or ``list``). If you also - provide a tag name or path to match, only the matching sub-elements are - loaded. - - :param text: The XML text to load. - :type text: ``string`` - :param match: A tag name or path to match (optional). - :type match: ``string`` - """ - if text is None: - return None - text = text.strip() - if len(text) == 0: - return None - nametable = {"namespaces": [], "names": {}} - - root = XML(text) - items = [root] if match is None else root.findall(match) - count = len(items) - if count == 0: - return None - if count == 1: - return load_root(items[0], nametable) - return [load_root(item, nametable) for item in items] - - -# Load the attributes of the given element. -def load_attrs(element): - if not hasattrs(element): - return None - attrs = record() - for key, value in element.attrib.items(): - attrs[key] = value - return attrs - - -# Parse a element and return a Python dict -def load_dict(element, nametable=None): - value = record() - children = list(element) - for child in children: - assert iskey(child.tag) - name = child.attrib["name"] - value[name] = load_value(child, nametable) - return value - - -# Loads the given elements attrs & value into single merged dict. -def load_elem(element, nametable=None): - name = localname(element.tag) - attrs = load_attrs(element) - value = load_value(element, nametable) - if attrs is None: - return name, value - if value is None: - return name, attrs - # If value is simple, merge into attrs dict using special key - if isinstance(value, str): - attrs["$text"] = value - return name, attrs - # Both attrs & value are complex, so merge the two dicts, resolving collisions. - collision_keys = [] - for key, val in attrs.items(): - if key in value and key in collision_keys: - value[key].append(val) - elif key in value and key not in collision_keys: - value[key] = [value[key], val] - collision_keys.append(key) - else: - value[key] = val - return name, value - - -# Parse a element and return a Python list -def load_list(element, nametable=None): - assert islist(element.tag) - value = [] - children = list(element) - for child in children: - assert isitem(child.tag) - value.append(load_value(child, nametable)) - return value - - -# Load the given root element. -def load_root(element, nametable=None): - tag = element.tag - if isdict(tag): - return load_dict(element, nametable) - if islist(tag): - return load_list(element, nametable) - k, v = load_elem(element, nametable) - return Record.fromkv(k, v) - - -# Load the children of the given element. -def load_value(element, nametable=None): - children = list(element) - count = len(children) - - # No children, assume a simple text value - if count == 0: - text = element.text - if text is None: - return None - - if len(text.strip()) == 0: - return None - return text - - # Look for the special case of a single well-known structure - if count == 1: - child = children[0] - tag = child.tag - if isdict(tag): - return load_dict(child, nametable) - if islist(tag): - return load_list(child, nametable) - - value = record() - for child in children: - name, item = load_elem(child, nametable) - # If we have seen this name before, promote the value to a list - if name in value: - current = value[name] - if not isinstance(current, list): - value[name] = [current] - value[name].append(item) - else: - value[name] = item - - return value - - -# A generic utility that enables "dot" access to dicts -class Record(dict): - """This generic utility class enables dot access to members of a Python - dictionary. - - Any key that is also a valid Python identifier can be retrieved as a field. - So, for an instance of ``Record`` called ``r``, ``r.key`` is equivalent to - ``r['key']``. A key such as ``invalid-key`` or ``invalid.key`` cannot be - retrieved as a field, because ``-`` and ``.`` are not allowed in - identifiers. - - Keys of the form ``a.b.c`` are very natural to write in Python as fields. If - a group of keys shares a prefix ending in ``.``, you can retrieve keys as a - nested dictionary by calling only the prefix. For example, if ``r`` contains - keys ``'foo'``, ``'bar.baz'``, and ``'bar.qux'``, ``r.bar`` returns a record - with the keys ``baz`` and ``qux``. If a key contains multiple ``.``, each - one is placed into a nested dictionary, so you can write ``r.bar.qux`` or - ``r['bar.qux']`` interchangeably. - """ - - sep = "." - - def __call__(self, *args): - if len(args) == 0: - return self - return Record((key, self[key]) for key in args) - - def __getattr__(self, name): - try: - return self[name] - except KeyError: - raise AttributeError(name) - - def __delattr__(self, name): - del self[name] - - def __setattr__(self, name, value): - self[name] = value - - @staticmethod - def fromkv(k, v): - result = record() - result[k] = v - return result - - def __getitem__(self, key): - if key in self: - return dict.__getitem__(self, key) - key += self.sep - result = record() - for k, v in self.items(): - if not k.startswith(key): - continue - suffix = k[len(key) :] - if "." in suffix: - ks = suffix.split(self.sep) - z = result - for x in ks[:-1]: - if x not in z: - z[x] = record() - z = z[x] - z[ks[-1]] = v - else: - result[suffix] = v - if len(result) == 0: - raise KeyError(f"No key or prefix: {key}") - return result - - -def record(value=None): - """This function returns a :class:`Record` instance constructed with an - initial value that you provide. - - :param value: An initial record value. - :type value: ``dict`` - """ - if value is None: - value = {} - return Record(value) diff --git a/apps/bitwarden_event_logs/lib/splunklib/modularinput/__init__.py b/apps/bitwarden_event_logs/lib/splunklib/modularinput/__init__.py deleted file mode 100755 index 987d1f95..00000000 --- a/apps/bitwarden_event_logs/lib/splunklib/modularinput/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -"""The following imports allow these classes to be imported via -the splunklib.modularinput package like so: - -from splunklib.modularinput import * -""" - -from .argument import Argument -from .event import Event -from .event_writer import EventWriter -from .input_definition import InputDefinition -from .scheme import Scheme -from .script import Script -from .validation_definition import ValidationDefinition diff --git a/apps/bitwarden_event_logs/lib/splunklib/modularinput/argument.py b/apps/bitwarden_event_logs/lib/splunklib/modularinput/argument.py deleted file mode 100755 index 99203ca2..00000000 --- a/apps/bitwarden_event_logs/lib/splunklib/modularinput/argument.py +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright © 2011-2024 Splunk, Inc. -# -# 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 xml.etree.ElementTree as ET - - -class Argument: - """Class representing an argument to a modular input kind. - - ``Argument`` is meant to be used with ``Scheme`` to generate an XML - definition of the modular input kind that Splunk understands. - - ``name`` is the only required parameter for the constructor. - - **Example with least parameters**:: - - arg1 = Argument(name="arg1") - - **Example with all parameters**:: - - arg2 = Argument( - name="arg2", - description="This is an argument with lots of parameters", - validation="is_pos_int('some_name')", - data_type=Argument.data_type_number, - required_on_edit=True, - required_on_create=True - ) - """ - - # Constant values, do not change. - # These should be used for setting the value of an Argument object's data_type field. - data_type_boolean = "BOOLEAN" - data_type_number = "NUMBER" - data_type_string = "STRING" - - def __init__( - self, - name, - description=None, - validation=None, - data_type=data_type_string, - required_on_edit=False, - required_on_create=False, - title=None, - ): - """ - :param name: ``string``, identifier for this argument in Splunk. - :param description: ``string``, human-readable description of the argument. - :param validation: ``string`` specifying how the argument should be validated, if using internal validation. - If using external validation, this will be ignored. - :param data_type: ``string``, data type of this field; use the class constants. - "data_type_boolean", "data_type_number", or "data_type_string". - :param required_on_edit: ``Boolean``, whether this arg is required when editing an existing modular input of this kind. - :param required_on_create: ``Boolean``, whether this arg is required when creating a modular input of this kind. - :param title: ``String``, a human-readable title for the argument. - """ - self.name = name - self.description = description - self.validation = validation - self.data_type = data_type - self.required_on_edit = required_on_edit - self.required_on_create = required_on_create - self.title = title - - def add_to_document(self, parent): - """Adds an ``Argument`` object to this ElementTree document. - - Adds an subelement to the parent element, typically - and sets up its subelements with their respective text. - - :param parent: An ``ET.Element`` to be the parent of a new subelement - :returns: An ``ET.Element`` object representing this argument. - """ - arg = ET.SubElement(parent, "arg") - arg.set("name", self.name) - - if self.title is not None: - ET.SubElement(arg, "title").text = self.title - - if self.description is not None: - ET.SubElement(arg, "description").text = self.description - - if self.validation is not None: - ET.SubElement(arg, "validation").text = self.validation - - # add all other subelements to this Argument, represented by (tag, text) - subelements = [ - ("data_type", self.data_type), - ("required_on_edit", self.required_on_edit), - ("required_on_create", self.required_on_create), - ] - - for name, value in subelements: - ET.SubElement(arg, name).text = str(value).lower() - - return arg diff --git a/apps/bitwarden_event_logs/lib/splunklib/modularinput/event.py b/apps/bitwarden_event_logs/lib/splunklib/modularinput/event.py deleted file mode 100755 index 4d243c75..00000000 --- a/apps/bitwarden_event_logs/lib/splunklib/modularinput/event.py +++ /dev/null @@ -1,124 +0,0 @@ -# Copyright © 2011-2024 Splunk, Inc. -# -# 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. - -from io import TextIOBase -import xml.etree.ElementTree as ET - -from ..utils import ensure_str - - -class Event: - """Represents an event or fragment of an event to be written by this modular input to Splunk. - - To write an input to a stream, call the ``write_to`` function, passing in a stream. - """ - - def __init__( - self, - data=None, - stanza=None, - time=None, - host=None, - index=None, - source=None, - sourcetype=None, - done=True, - unbroken=True, - ): - """There are no required parameters for constructing an Event - - **Example with minimal configuration**:: - - my_event = Event( - data="This is a test of my new event.", - stanza="myStanzaName", - time="%.3f" % 1372187084.000 - ) - - **Example with full configuration**:: - - excellent_event = Event( - data="This is a test of my excellent event.", - stanza="excellenceOnly", - time="%.3f" % 1372274622.493, - host="localhost", - index="main", - source="Splunk", - sourcetype="misc", - done=True, - unbroken=True - ) - - :param data: ``string``, the event's text. - :param stanza: ``string``, name of the input this event should be sent to. - :param time: ``float``, time in seconds, including up to 3 decimal places to represent milliseconds. - :param host: ``string``, the event's host, ex: localhost. - :param index: ``string``, the index this event is specified to write to, or None if default index. - :param source: ``string``, the source of this event, or None to have Splunk guess. - :param sourcetype: ``string``, source type currently set on this event, or None to have Splunk guess. - :param done: ``boolean``, is this a complete ``Event``? False if an ``Event`` fragment. - :param unbroken: ``boolean``, Is this event completely encapsulated in this ``Event`` object? - """ - self.data = data - self.done = done - self.host = host - self.index = index - self.source = source - self.sourceType = sourcetype - self.stanza = stanza - self.time = time - self.unbroken = unbroken - - def write_to(self, stream): - """Write an XML representation of self, an ``Event`` object, to the given stream. - - The ``Event`` object will only be written if its data field is defined, - otherwise a ``ValueError`` is raised. - - :param stream: stream to write XML to. - """ - if self.data is None: - raise ValueError( - "Events must have at least the data field set to be written to XML." - ) - - event = ET.Element("event") - if self.stanza is not None: - event.set("stanza", self.stanza) - event.set("unbroken", str(int(self.unbroken))) - - # if a time isn't set, let Splunk guess by not creating a

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 Sn~C1PnTc;Ep3WZ1PX1r$k@)HW diff --git a/apps/bitwarden_event_logs/lib/urllib3/__pycache__/exceptions.cpython-39.pyc b/apps/bitwarden_event_logs/lib/urllib3/__pycache__/exceptions.cpython-39.pyc deleted file mode 100755 index a869cf9d93a82668fdc56469da48a678d66ec157..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11618 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 DcD^kZ diff --git a/apps/bitwarden_event_logs/lib/urllib3/__pycache__/fields.cpython-39.pyc b/apps/bitwarden_event_logs/lib/urllib3/__pycache__/fields.cpython-39.pyc deleted file mode 100755 index d123f9ca71e3e425c9a720b7190c613244fe623a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8133 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 diff --git a/apps/bitwarden_event_logs/lib/urllib3/__pycache__/filepost.cpython-39.pyc b/apps/bitwarden_event_logs/lib/urllib3/__pycache__/filepost.cpython-39.pyc deleted file mode 100755 index 9ff0d66581d2e60098bb7ca480a9872e81c1a289..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2734 zcmbVOTW=gS6t-t(CzHu;l5RtxP%5jXz)IO&E)uGiP!JSEkeUKPNUU1z&W^pwBy+3n z$xRbpTA@Di8xp}w{{g>cUU|whZ#;3%%x;nfkP2SeKK9th$KN^r&U%f8OQ78R<=5cp zEFr(+;CwM*@Cj7)6dF!A4M{}%l%i~eM&BrVvu~EY)wiHG!&+qbZCGRRS~wFqeW#S| za5i%LZYj@%b5Xrtr$m@X4elIMK6{n)=ef)0o{;_muk!}7e=CrnM`^yPaGwW&+x639McIV}jZ1r@NjrZe!3VgMrJfv?qaDdHjptIDlCKLlU)hCfb# z=u8x!kF4S3m^E;f8-U}L_F`WY7AmcrGaG<$P$N}XC`t^n{@hq5(V()TWfpd&yP!c) zE5*`rRdX5E7fswcC_fF7(Ut&{uOguH&=AL^b;B_nz~3?CHJEwM1LzI(KpEM~Ae?&O zI;Y%tLZ*JW3m#a9r;KwssYWf!YlDIkkxqwkkxJnM*1Xqa?{+?@P+t94{J+~2%;P-J zK@ubUF9a%!ylrrf-V78QXR(i)O9K*o_(F>gTLq+r$Cp{A1nc3V-tg}b4VT&OX5eqa zBh;CU)r{ zM(=MRX!4jKcAREJE&-E}L*Qv-4`7b;(9D5Vw8de?6_`7Qw54mg@t6YFm`C<8@C_xo zxixcWX(zYzEO5*N?dBG+Pc<{QRfoB~(_I$sh+Wj1moiG+#MwvJCZ}2FPIGy!8Xdy9 zM~!2W*T$H@Z10=tOZq%Uu4Z0IJ`mkN|M)tF!-@xnLx^k0BJczRi?UD$sV8+8(nr+h zp7z+i)q9^~Hi_{nowm2 z*0kta>vG`^2ICB9Pz(kVk&<)J6gCE;uVe!UMvzo^l&=;ORpF$bzwK=ZRbH@PpAI|l zhiZaSm?Ya-D&It%nEp#3%ZFT^e(qMM)QNpuO+`;6eUV#QO7~I5xOjf7xGv-al Ib(WmwU-h-UbpQYW diff --git a/apps/bitwarden_event_logs/lib/urllib3/__pycache__/poolmanager.cpython-39.pyc b/apps/bitwarden_event_logs/lib/urllib3/__pycache__/poolmanager.cpython-39.pyc deleted file mode 100755 index ae1a42e454323935fc16074e3d13361bee510831..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15100 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 M|IqlMadJBPA9}oxO#lD@ diff --git a/apps/bitwarden_event_logs/lib/urllib3/__pycache__/request.cpython-39.pyc b/apps/bitwarden_event_logs/lib/urllib3/__pycache__/request.cpython-39.pyc deleted file mode 100755 index e9a11d16d1b11c1615aeeb7ec72fda42cd0db050..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6358 zcmb_gTW=f372aK5MM<_|yGh)p%_M0{(WOWrEl@y6QX7sEyNzVQR#22Euv~J6JH9g%vfHZZA%`+J zbvbnDiIH>aLOD`!?3}p%koAM{IPh6Gn_xgNU^Ew#iDgseR6ZFOeJyA@a{D9w4SkZ0 z9=O8eOnZkUhXIR}NOgEJ!KVYa?|9ylfjhl?j1GRgS~riH`&{J@9iD!HByD1F?%oIv;S@S zJ0#)`*@cwJY!fM3M;_S>4wz1-I~3qMc}OQ7&2=X)9X@)31suLlr+Y{DA3Q_-T;6&KaU@G}dcmn+ z;WWs@1D?jbiLj+brH{!aRCt$ zmy_6tD~}G39zHsFa?rOhBKy%b9rtmlYdFo#N!c~#h7A-2AeaXLrb@bmh7}i;@CI@N z1AlKw8vplq+rcDktBDu+Cv6e-+sM&7p(7nJ_( zbiJ*~EUwzN3v=4`_edA5qbu!h4O-|QIU0SA z8;tne{MdYga5yep{YF=fqVpG&xjZEIkuRtQscVs91P_;MgN}GgD1^(?85#_7Y29uD ziQO*iIZ7by6DaI<6EovEl9;CnNhxtO0>t9Kg95NrzH~7HFd*|uHCLi2kOH1?;Cg2a z2ZV$JW55&dV;BtfQYIQ6^ayx zNH63G#!=dHteA0%E`pZ;Nv+ZvlH<;FBz%Ud2n;|}BsCPEpR1MHGd1#ZfoW#8zVuGR ze=`>V0H)MTOb|>#HeiWU;bmW^!GuCfy3?<-o=LWS?7?vfk>4%>2#Y;l+$r7Rmr|0LsC(1Vw`#vCu6;8Rjry? zG>czZIEu)Za&h_Ysq9HbtYi;?3<0Xno(H%(i+_3*$!Yn1kf*s_tD57DfR-Oe&jzQ#U z%8{-xk1@|hqCUkUnhYqu@cKbweDg<*VscfECg80?60*n_acWea=`;B)ypO9uUFkgS zHu)0XzK=s!9-~w>%U0E_Sw(ZpI)CNyD)OhwF9p|130Q}i+*2Rp=CY?;1D>kFKRsTU zSAchSLX#J%NX$#}F8OO-=A~a-KesN{E^7X@us|Np%ytR@SUDF%T-a%03=km z7({GQY7!P0x?`$YLC|6kfY_%(YT`R~W~>J>Ct1l|2y%x$=FF={V$nzUqHwfSFP{Ln zQR1i-N^6wk!TWjH&512l#}hGYpE_PdbxuOIV)qW-&L^a|w7@VzSJ-0r^lJ4@QjoXl zr4O3iWYqgM+5W@*J?+W{ONPN#Y3oClCFkcuNJLUY9pKwLRKU(0;$XWe;ifO>m-bL^ zvR~wiB`+AO4}ze+%j!Kx))%#K@fOsDRK$>N8cNr#@GPu1Sv_^W?j+M!^~M!4;0Z66 zfq9l>5Q*Ea=K~t(4;{YUxJn8&t$az$xfTNk4OJ2`Jx4#QXHuS4C0kbVx9*P7Ik%4| zVtA$AKB91(t$t;-i#LgBFInzlkqH_&;Co~*#k!;Y-(+BT=+YNYXd<>v&KNxV@nFWyLV-KH$O)Q!n+Vwk|8wsA6cG-&~4shZT26~QryZ*G*zruC09 zD9wg-4V32m)qhQAH1|cbT|78AIzY#o9LZgcT4PEX+m36tJq~!}(e;LHe}GOUeN(Y* z9`tQnegj&`EjrQq<;!#;qNNpIS`2mXlKM-%=SQYgWQM9)dm#F6NV>y#Gh4gXyiZ%A*{^^L67dMfYUfV-s)h64=HW~r z_HaO{AsH%f{ns(qITQao{BCi&>YdA zT5ix5ep`j4NBi{kov{mf)Q0X1BW!oJM&a1Q7LWWk?B6KpREdj@9ICi{f(@LZS_&Aw z7lBZub=me89{6@dq4w^QUS` zx^Fy3F+YgHNFwGX#R~SzXDY5zK_pV8vp%j)oc;;wo07EK(Rn$$ZDDV{H3m#8Vu5f& zLJ2iW#K;-FmbjdFPG>?~IQ-e`aV7Do(#pqcSPtzVK?73iD(&Oy8_6ecQGDxk*gy-q SVd9@vE^PhM_)gh+ar2){cqpm> diff --git a/apps/bitwarden_event_logs/lib/urllib3/__pycache__/response.cpython-39.pyc b/apps/bitwarden_event_logs/lib/urllib3/__pycache__/response.cpython-39.pyc deleted file mode 100755 index fb473a04211681251865815431a417865b31bbc4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22466 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 self._maxsize: - _key, evicted_value = self._container.popitem(last=False) - - if self.dispose_func and evicted_value is not _Null: - self.dispose_func(evicted_value) - - def __delitem__(self, key): - with self.lock: - value = self._container.pop(key) - - if self.dispose_func: - self.dispose_func(value) - - def __len__(self): - with self.lock: - return len(self._container) - - def __iter__(self): - raise NotImplementedError( - "Iteration over this class is unlikely to be threadsafe." - ) - - def clear(self): - with self.lock: - # Copy pointers to all values, then wipe the mapping - values = list(itervalues(self._container)) - self._container.clear() - - if self.dispose_func: - for value in values: - self.dispose_func(value) - - def keys(self): - with self.lock: - return list(iterkeys(self._container)) - - -class HTTPHeaderDict(MutableMapping): - """ - :param headers: - An iterable of field-value pairs. Must not contain multiple field names - when compared case-insensitively. - - :param kwargs: - Additional field-value pairs to pass in to ``dict.update``. - - A ``dict`` like container for storing HTTP Headers. - - Field names are stored and compared case-insensitively in compliance with - RFC 7230. Iteration provides the first case-sensitive key seen for each - case-insensitive pair. - - Using ``__setitem__`` syntax overwrites fields that compare equal - case-insensitively in order to maintain ``dict``'s api. For fields that - compare equal, instead create a new ``HTTPHeaderDict`` and use ``.add`` - in a loop. - - If multiple fields that are equal case-insensitively are passed to the - constructor or ``.update``, the behavior is undefined and some will be - lost. - - >>> headers = HTTPHeaderDict() - >>> headers.add('Set-Cookie', 'foo=bar') - >>> headers.add('set-cookie', 'baz=quxx') - >>> headers['content-length'] = '7' - >>> headers['SET-cookie'] - 'foo=bar, baz=quxx' - >>> headers['Content-Length'] - '7' - """ - - def __init__(self, headers=None, **kwargs): - super(HTTPHeaderDict, self).__init__() - self._container = OrderedDict() - if headers is not None: - if isinstance(headers, HTTPHeaderDict): - self._copy_from(headers) - else: - self.extend(headers) - if kwargs: - self.extend(kwargs) - - def __setitem__(self, key, val): - self._container[key.lower()] = [key, val] - return self._container[key.lower()] - - def __getitem__(self, key): - val = self._container[key.lower()] - return ", ".join(val[1:]) - - def __delitem__(self, key): - del self._container[key.lower()] - - def __contains__(self, key): - return key.lower() in self._container - - def __eq__(self, other): - if not isinstance(other, Mapping) and not hasattr(other, "keys"): - return False - if not isinstance(other, type(self)): - other = type(self)(other) - return dict((k.lower(), v) for k, v in self.itermerged()) == dict( - (k.lower(), v) for k, v in other.itermerged() - ) - - def __ne__(self, other): - return not self.__eq__(other) - - if six.PY2: # Python 2 - iterkeys = MutableMapping.iterkeys - itervalues = MutableMapping.itervalues - - __marker = object() - - def __len__(self): - return len(self._container) - - def __iter__(self): - # Only provide the originally cased names - for vals in self._container.values(): - yield vals[0] - - def pop(self, key, default=__marker): - """D.pop(k[,d]) -> v, remove specified key and return the corresponding value. - If key is not found, d is returned if given, otherwise KeyError is raised. - """ - # Using the MutableMapping function directly fails due to the private marker. - # Using ordinary dict.pop would expose the internal structures. - # So let's reinvent the wheel. - try: - value = self[key] - except KeyError: - if default is self.__marker: - raise - return default - else: - del self[key] - return value - - def discard(self, key): - try: - del self[key] - except KeyError: - pass - - def add(self, key, val): - """Adds a (name, value) pair, doesn't overwrite the value if it already - exists. - - >>> headers = HTTPHeaderDict(foo='bar') - >>> headers.add('Foo', 'baz') - >>> headers['foo'] - 'bar, baz' - """ - key_lower = key.lower() - new_vals = [key, val] - # Keep the common case aka no item present as fast as possible - vals = self._container.setdefault(key_lower, new_vals) - if new_vals is not vals: - vals.append(val) - - def extend(self, *args, **kwargs): - """Generic import function for any type of header-like object. - Adapted version of MutableMapping.update in order to insert items - with self.add instead of self.__setitem__ - """ - if len(args) > 1: - raise TypeError( - "extend() takes at most 1 positional " - "arguments ({0} given)".format(len(args)) - ) - other = args[0] if len(args) >= 1 else () - - if isinstance(other, HTTPHeaderDict): - for key, val in other.iteritems(): - self.add(key, val) - elif isinstance(other, Mapping): - for key in other: - self.add(key, other[key]) - elif hasattr(other, "keys"): - for key in other.keys(): - self.add(key, other[key]) - else: - for key, value in other: - self.add(key, value) - - for key, value in kwargs.items(): - self.add(key, value) - - def getlist(self, key, default=__marker): - """Returns a list of all the values for the named field. Returns an - empty list if the key doesn't exist.""" - try: - vals = self._container[key.lower()] - except KeyError: - if default is self.__marker: - return [] - return default - else: - return vals[1:] - - def _prepare_for_method_change(self): - """ - Remove content-specific header fields before changing the request - method to GET or HEAD according to RFC 9110, Section 15.4. - """ - content_specific_headers = [ - "Content-Encoding", - "Content-Language", - "Content-Location", - "Content-Type", - "Content-Length", - "Digest", - "Last-Modified", - ] - for header in content_specific_headers: - self.discard(header) - return self - - # Backwards compatibility for httplib - getheaders = getlist - getallmatchingheaders = getlist - iget = getlist - - # Backwards compatibility for http.cookiejar - get_all = getlist - - def __repr__(self): - return "%s(%s)" % (type(self).__name__, dict(self.itermerged())) - - def _copy_from(self, other): - for key in other: - val = other.getlist(key) - if isinstance(val, list): - # Don't need to convert tuples - val = list(val) - self._container[key.lower()] = [key] + val - - def copy(self): - clone = type(self)() - clone._copy_from(self) - return clone - - def iteritems(self): - """Iterate over all header lines, including duplicate ones.""" - for key in self: - vals = self._container[key.lower()] - for val in vals[1:]: - yield vals[0], val - - def itermerged(self): - """Iterate over all headers, merging duplicate ones together.""" - for key in self: - val = self._container[key.lower()] - yield val[0], ", ".join(val[1:]) - - def items(self): - return list(self.iteritems()) - - @classmethod - def from_httplib(cls, message): # Python 2 - """Read headers from a Python 2 httplib message object.""" - # python2.7 does not expose a proper API for exporting multiheaders - # efficiently. This function re-reads raw lines from the message - # object and extracts the multiheaders properly. - obs_fold_continued_leaders = (" ", "\t") - headers = [] - - for line in message.headers: - if line.startswith(obs_fold_continued_leaders): - if not headers: - # We received a header line that starts with OWS as described - # in RFC-7230 S3.2.4. This indicates a multiline header, but - # there exists no previous header to which we can attach it. - raise InvalidHeader( - "Header continuation with no previous header: %s" % line - ) - else: - key, value = headers[-1] - headers[-1] = (key, value + " " + line.strip()) - continue - - key, value = line.split(":", 1) - headers.append((key, value.strip())) - - return cls(headers) diff --git a/apps/bitwarden_event_logs/lib/urllib3/_version.py b/apps/bitwarden_event_logs/lib/urllib3/_version.py deleted file mode 100755 index d49df2a0..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/_version.py +++ /dev/null @@ -1,2 +0,0 @@ -# This file is protected via CODEOWNERS -__version__ = "1.26.20" diff --git a/apps/bitwarden_event_logs/lib/urllib3/connection.py b/apps/bitwarden_event_logs/lib/urllib3/connection.py deleted file mode 100755 index de35b63d..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/connection.py +++ /dev/null @@ -1,572 +0,0 @@ -from __future__ import absolute_import - -import datetime -import logging -import os -import re -import socket -import warnings -from socket import error as SocketError -from socket import timeout as SocketTimeout - -from .packages import six -from .packages.six.moves.http_client import HTTPConnection as _HTTPConnection -from .packages.six.moves.http_client import HTTPException # noqa: F401 -from .util.proxy import create_proxy_ssl_context - -try: # Compiled with SSL? - import ssl - - BaseSSLError = ssl.SSLError -except (ImportError, AttributeError): # Platform-specific: No SSL. - ssl = None - - class BaseSSLError(BaseException): - pass - - -try: - # Python 3: not a no-op, we're adding this to the namespace so it can be imported. - ConnectionError = ConnectionError -except NameError: - # Python 2 - class ConnectionError(Exception): - pass - - -try: # Python 3: - # Not a no-op, we're adding this to the namespace so it can be imported. - BrokenPipeError = BrokenPipeError -except NameError: # Python 2: - - class BrokenPipeError(Exception): - pass - - -from ._collections import HTTPHeaderDict # noqa (historical, removed in v2) -from ._version import __version__ -from .exceptions import ( - ConnectTimeoutError, - NewConnectionError, - SubjectAltNameWarning, - SystemTimeWarning, -) -from .util import SKIP_HEADER, SKIPPABLE_HEADERS, connection -from .util.ssl_ import ( - assert_fingerprint, - create_urllib3_context, - is_ipaddress, - resolve_cert_reqs, - resolve_ssl_version, - ssl_wrap_socket, -) -from .util.ssl_match_hostname import CertificateError, match_hostname - -log = logging.getLogger(__name__) - -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(2024, 1, 1) - -_CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]") - - -class HTTPConnection(_HTTPConnection, object): - """ - Based on :class:`http.client.HTTPConnection` but provides an extra constructor - backwards-compatibility layer between older and newer Pythons. - - Additional keyword parameters are used to configure attributes of the connection. - Accepted parameters include: - - - ``strict``: See the documentation on :class:`urllib3.connectionpool.HTTPConnectionPool` - - ``source_address``: Set the source address for the current connection. - - ``socket_options``: Set specific options on the underlying socket. If not specified, then - defaults are loaded from ``HTTPConnection.default_socket_options`` which includes disabling - Nagle's algorithm (sets TCP_NODELAY to 1) unless the connection is behind a proxy. - - For example, if you wish to enable TCP Keep Alive in addition to the defaults, - you might pass: - - .. code-block:: python - - HTTPConnection.default_socket_options + [ - (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1), - ] - - Or you may want to disable the defaults by passing an empty list (e.g., ``[]``). - """ - - default_port = port_by_scheme["http"] - - #: Disable Nagle's algorithm by default. - #: ``[(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]`` - default_socket_options = [(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)] - - #: Whether this connection verifies the host's certificate. - is_verified = False - - #: Whether this proxy connection (if used) verifies the proxy host's - #: certificate. - proxy_is_verified = None - - def __init__(self, *args, **kw): - if not six.PY2: - kw.pop("strict", None) - - # Pre-set source_address. - self.source_address = kw.get("source_address") - - #: The socket options provided by the user. If no options are - #: provided, we use the default options. - self.socket_options = kw.pop("socket_options", self.default_socket_options) - - # Proxy options provided by the user. - self.proxy = kw.pop("proxy", None) - self.proxy_config = kw.pop("proxy_config", None) - - _HTTPConnection.__init__(self, *args, **kw) - - @property - def host(self): - """ - Getter method to remove any trailing dots that indicate the hostname is an FQDN. - - In general, SSL certificates don't include the trailing dot indicating a - fully-qualified domain name, and thus, they don't validate properly when - checked against a domain name that includes the dot. In addition, some - servers may not expect to receive the trailing dot when provided. - - However, the hostname with trailing dot is critical to DNS resolution; doing a - lookup with the trailing dot will properly only resolve the appropriate FQDN, - whereas a lookup without a trailing dot will search the system's search domain - list. Thus, it's important to keep the original host around for use only in - those cases where it's appropriate (i.e., when doing DNS lookup to establish the - actual TCP connection across which we're going to send HTTP requests). - """ - return self._dns_host.rstrip(".") - - @host.setter - def host(self, value): - """ - Setter for the `host` property. - - We assume that only urllib3 uses the _dns_host attribute; httplib itself - only uses `host`, and it seems reasonable that other libraries follow suit. - """ - self._dns_host = value - - def _new_conn(self): - """Establish a socket connection and set nodelay settings on it. - - :return: New socket connection. - """ - extra_kw = {} - if self.source_address: - extra_kw["source_address"] = self.source_address - - if self.socket_options: - extra_kw["socket_options"] = self.socket_options - - try: - conn = connection.create_connection( - (self._dns_host, self.port), self.timeout, **extra_kw - ) - - except SocketTimeout: - raise ConnectTimeoutError( - self, - "Connection to %s timed out. (connect timeout=%s)" - % (self.host, self.timeout), - ) - - except SocketError as e: - raise NewConnectionError( - self, "Failed to establish a new connection: %s" % e - ) - - return conn - - def _is_using_tunnel(self): - # Google App Engine's httplib does not define _tunnel_host - return getattr(self, "_tunnel_host", None) - - def _prepare_conn(self, conn): - self.sock = conn - if self._is_using_tunnel(): - # TODO: Fix tunnel so it doesn't depend on self.sock state. - self._tunnel() - # Mark this connection as not reusable - self.auto_open = 0 - - def connect(self): - conn = self._new_conn() - self._prepare_conn(conn) - - def putrequest(self, method, url, *args, **kwargs): - """ """ - # Empty docstring because the indentation of CPython's implementation - # is broken but we don't want this method in our documentation. - match = _CONTAINS_CONTROL_CHAR_RE.search(method) - if match: - raise ValueError( - "Method cannot contain non-token characters %r (found at least %r)" - % (method, match.group()) - ) - - return _HTTPConnection.putrequest(self, method, url, *args, **kwargs) - - def putheader(self, header, *values): - """ """ - if not any(isinstance(v, str) and v == SKIP_HEADER for v in values): - _HTTPConnection.putheader(self, header, *values) - elif six.ensure_str(header.lower()) not in SKIPPABLE_HEADERS: - raise ValueError( - "urllib3.util.SKIP_HEADER only supports '%s'" - % ("', '".join(map(str.title, sorted(SKIPPABLE_HEADERS))),) - ) - - def request(self, method, url, body=None, headers=None): - # Update the inner socket's timeout value to send the request. - # This only triggers if the connection is re-used. - if getattr(self, "sock", None) is not None: - self.sock.settimeout(self.timeout) - - if headers is None: - headers = {} - else: - # Avoid modifying the headers passed into .request() - headers = headers.copy() - if "user-agent" not in (six.ensure_str(k.lower()) for k in headers): - headers["User-Agent"] = _get_default_user_agent() - super(HTTPConnection, self).request(method, url, body=body, headers=headers) - - def request_chunked(self, method, url, body=None, headers=None): - """ - Alternative to the common request method, which sends the - body with chunked encoding and not as one block - """ - headers = headers or {} - header_keys = set([six.ensure_str(k.lower()) for k in headers]) - skip_accept_encoding = "accept-encoding" in header_keys - skip_host = "host" in header_keys - self.putrequest( - method, url, skip_accept_encoding=skip_accept_encoding, skip_host=skip_host - ) - if "user-agent" not in header_keys: - self.putheader("User-Agent", _get_default_user_agent()) - for header, value in headers.items(): - self.putheader(header, value) - if "transfer-encoding" not in header_keys: - self.putheader("Transfer-Encoding", "chunked") - self.endheaders() - - if body is not None: - stringish_types = six.string_types + (bytes,) - if isinstance(body, stringish_types): - body = (body,) - for chunk in body: - if not chunk: - continue - if not isinstance(chunk, bytes): - chunk = chunk.encode("utf8") - len_str = hex(len(chunk))[2:] - to_send = bytearray(len_str.encode()) - to_send += b"\r\n" - to_send += chunk - to_send += b"\r\n" - self.send(to_send) - - # After the if clause, to always have a closed body - self.send(b"0\r\n\r\n") - - -class HTTPSConnection(HTTPConnection): - """ - Many of the parameters to this constructor are passed to the underlying SSL - socket by means of :py:func:`urllib3.util.ssl_wrap_socket`. - """ - - default_port = port_by_scheme["https"] - - cert_reqs = None - ca_certs = None - ca_cert_dir = None - ca_cert_data = None - ssl_version = None - assert_fingerprint = None - tls_in_tls_required = False - - def __init__( - self, - host, - port=None, - key_file=None, - cert_file=None, - key_password=None, - strict=None, - timeout=socket._GLOBAL_DEFAULT_TIMEOUT, - ssl_context=None, - server_hostname=None, - **kw - ): - - HTTPConnection.__init__(self, host, port, strict=strict, timeout=timeout, **kw) - - self.key_file = key_file - self.cert_file = cert_file - self.key_password = key_password - self.ssl_context = ssl_context - self.server_hostname = server_hostname - - # Required property for Google AppEngine 1.9.0 which otherwise causes - # HTTPS requests to go out as HTTP. (See Issue #356) - self._protocol = "https" - - def set_cert( - self, - key_file=None, - cert_file=None, - cert_reqs=None, - key_password=None, - ca_certs=None, - assert_hostname=None, - assert_fingerprint=None, - ca_cert_dir=None, - ca_cert_data=None, - ): - """ - This method should only be called once, before the connection is used. - """ - # If cert_reqs is not provided we'll assume CERT_REQUIRED unless we also - # have an SSLContext object in which case we'll use its verify_mode. - if cert_reqs is None: - if self.ssl_context is not None: - cert_reqs = self.ssl_context.verify_mode - else: - cert_reqs = resolve_cert_reqs(None) - - self.key_file = key_file - self.cert_file = cert_file - self.cert_reqs = cert_reqs - self.key_password = key_password - self.assert_hostname = assert_hostname - self.assert_fingerprint = assert_fingerprint - self.ca_certs = ca_certs and os.path.expanduser(ca_certs) - self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir) - self.ca_cert_data = ca_cert_data - - def connect(self): - # Add certificate verification - self.sock = conn = self._new_conn() - hostname = self.host - tls_in_tls = False - - if self._is_using_tunnel(): - if self.tls_in_tls_required: - self.sock = conn = self._connect_tls_proxy(hostname, conn) - tls_in_tls = True - - # Calls self._set_hostport(), so self.host is - # self._tunnel_host below. - self._tunnel() - # Mark this connection as not reusable - self.auto_open = 0 - - # Override the host with the one we're requesting data from. - hostname = self._tunnel_host - - server_hostname = hostname - if self.server_hostname is not None: - server_hostname = self.server_hostname - - is_time_off = datetime.date.today() < RECENT_DATE - if is_time_off: - warnings.warn( - ( - "System time is way off (before {0}). This will probably " - "lead to SSL verification errors" - ).format(RECENT_DATE), - SystemTimeWarning, - ) - - # Wrap socket using verification with the root certs in - # trusted_root_certs - default_ssl_context = False - if self.ssl_context is None: - default_ssl_context = True - self.ssl_context = create_urllib3_context( - ssl_version=resolve_ssl_version(self.ssl_version), - cert_reqs=resolve_cert_reqs(self.cert_reqs), - ) - - context = self.ssl_context - context.verify_mode = resolve_cert_reqs(self.cert_reqs) - - # Try to load OS default certs if none are given. - # Works well on Windows (requires Python3.4+) - if ( - not self.ca_certs - and not self.ca_cert_dir - and not self.ca_cert_data - and default_ssl_context - and hasattr(context, "load_default_certs") - ): - context.load_default_certs() - - self.sock = ssl_wrap_socket( - sock=conn, - keyfile=self.key_file, - certfile=self.cert_file, - key_password=self.key_password, - ca_certs=self.ca_certs, - ca_cert_dir=self.ca_cert_dir, - ca_cert_data=self.ca_cert_data, - server_hostname=server_hostname, - ssl_context=context, - tls_in_tls=tls_in_tls, - ) - - # If we're using all defaults and the connection - # is TLSv1 or TLSv1.1 we throw a DeprecationWarning - # for the host. - if ( - default_ssl_context - 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 " - "'%s' with '%s' can be enabled by explicitly opting-in " - "with 'ssl_version'" % (self.host, self.sock.version()), - DeprecationWarning, - ) - - if self.assert_fingerprint: - assert_fingerprint( - self.sock.getpeercert(binary_form=True), self.assert_fingerprint - ) - elif ( - context.verify_mode != ssl.CERT_NONE - and not getattr(context, "check_hostname", False) - and self.assert_hostname is not False - ): - # While urllib3 attempts to always turn off hostname matching from - # the TLS library, this cannot always be done. So we check whether - # the TLS Library still thinks it's matching hostnames. - cert = self.sock.getpeercert() - if not cert.get("subjectAltName", ()): - warnings.warn( - ( - "Certificate for {0} has no `subjectAltName`, falling back to check for a " - "`commonName` for now. This feature is being removed by major browsers and " - "deprecated by RFC 2818. (See https://github.com/urllib3/urllib3/issues/497 " - "for details.)".format(hostname) - ), - SubjectAltNameWarning, - ) - _match_hostname(cert, self.assert_hostname or server_hostname) - - self.is_verified = ( - context.verify_mode == ssl.CERT_REQUIRED - or self.assert_fingerprint is not None - ) - - def _connect_tls_proxy(self, hostname, conn): - """ - Establish a TLS connection to the proxy using the provided SSL context. - """ - proxy_config = self.proxy_config - ssl_context = proxy_config.ssl_context - if ssl_context: - # If the user provided a proxy context, we assume CA and client - # certificates have already been set - return ssl_wrap_socket( - sock=conn, - server_hostname=hostname, - ssl_context=ssl_context, - ) - - ssl_context = create_proxy_ssl_context( - self.ssl_version, - self.cert_reqs, - self.ca_certs, - self.ca_cert_dir, - self.ca_cert_data, - ) - - # If no cert was provided, use only the default options for server - # certificate validation - socket = ssl_wrap_socket( - sock=conn, - ca_certs=self.ca_certs, - ca_cert_dir=self.ca_cert_dir, - ca_cert_data=self.ca_cert_data, - server_hostname=hostname, - ssl_context=ssl_context, - ) - - if ssl_context.verify_mode != ssl.CERT_NONE and not getattr( - ssl_context, "check_hostname", False - ): - # While urllib3 attempts to always turn off hostname matching from - # the TLS library, this cannot always be done. So we check whether - # the TLS Library still thinks it's matching hostnames. - cert = socket.getpeercert() - if not cert.get("subjectAltName", ()): - warnings.warn( - ( - "Certificate for {0} has no `subjectAltName`, falling back to check for a " - "`commonName` for now. This feature is being removed by major browsers and " - "deprecated by RFC 2818. (See https://github.com/urllib3/urllib3/issues/497 " - "for details.)".format(hostname) - ), - SubjectAltNameWarning, - ) - _match_hostname(cert, hostname) - - self.proxy_is_verified = ssl_context.verify_mode == ssl.CERT_REQUIRED - return socket - - -def _match_hostname(cert, asserted_hostname): - # Our upstream implementation of ssl.match_hostname() - # only applies this normalization to IP addresses so it doesn't - # match DNS SANs so we do the same thing! - stripped_hostname = asserted_hostname.strip("u[]") - if is_ipaddress(stripped_hostname): - asserted_hostname = stripped_hostname - - try: - match_hostname(cert, asserted_hostname) - except CertificateError as e: - log.warning( - "Certificate did not match expected hostname: %s. Certificate: %s", - asserted_hostname, - cert, - ) - # Add cert to exception and reraise so client code can inspect - # the cert when catching the exception, if they want to - e._peer_cert = cert - raise - - -def _get_default_user_agent(): - return "python-urllib3/%s" % __version__ - - -class DummyConnection(object): - """Used to detect a failed ConnectionCls import.""" - - pass - - -if not ssl: - HTTPSConnection = DummyConnection # noqa: F811 - - -VerifiedHTTPSConnection = HTTPSConnection diff --git a/apps/bitwarden_event_logs/lib/urllib3/connectionpool.py b/apps/bitwarden_event_logs/lib/urllib3/connectionpool.py deleted file mode 100755 index 0872ed77..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/connectionpool.py +++ /dev/null @@ -1,1140 +0,0 @@ -from __future__ import absolute_import - -import errno -import logging -import re -import socket -import sys -import warnings -from socket import error as SocketError -from socket import timeout as SocketTimeout - -from ._collections import HTTPHeaderDict -from .connection import ( - BaseSSLError, - BrokenPipeError, - DummyConnection, - HTTPConnection, - HTTPException, - HTTPSConnection, - VerifiedHTTPSConnection, - port_by_scheme, -) -from .exceptions import ( - ClosedPoolError, - EmptyPoolError, - HeaderParsingError, - HostChangedError, - InsecureRequestWarning, - LocationValueError, - MaxRetryError, - NewConnectionError, - ProtocolError, - ProxyError, - ReadTimeoutError, - SSLError, - TimeoutError, -) -from .packages import six -from .packages.six.moves import queue -from .request import RequestMethods -from .response import HTTPResponse -from .util.connection import is_connection_dropped -from .util.proxy import connection_requires_http_tunnel -from .util.queue import LifoQueue -from .util.request import set_file_position -from .util.response import assert_header_parsing -from .util.retry import Retry -from .util.ssl_match_hostname import CertificateError -from .util.timeout import Timeout -from .util.url import Url, _encode_target -from .util.url import _normalize_host as normalize_host -from .util.url import get_host, parse_url - -try: # Platform-specific: Python 3 - import weakref - - weakref_finalize = weakref.finalize -except AttributeError: # Platform-specific: Python 2 - from .packages.backports.weakref_finalize import weakref_finalize - -xrange = six.moves.xrange - -log = logging.getLogger(__name__) - -_Default = object() - - -# Pool objects -class ConnectionPool(object): - """ - Base class for all connection pools, such as - :class:`.HTTPConnectionPool` and :class:`.HTTPSConnectionPool`. - - .. note:: - ConnectionPool.urlopen() does not normalize or percent-encode target URIs - which is useful if your target server doesn't support percent-encoded - target URIs. - """ - - scheme = None - QueueCls = LifoQueue - - def __init__(self, host, port=None): - if not host: - raise LocationValueError("No host specified.") - - self.host = _normalize_host(host, scheme=self.scheme) - self._proxy_host = host.lower() - self.port = port - - def __str__(self): - return "%s(host=%r, port=%r)" % (type(self).__name__, self.host, self.port) - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.close() - # Return False to re-raise any potential exceptions - return False - - def close(self): - """ - Close all pooled connections and disable the pool. - """ - pass - - -# This is taken from http://hg.python.org/cpython/file/7aaba721ebc0/Lib/socket.py#l252 -_blocking_errnos = {errno.EAGAIN, errno.EWOULDBLOCK} - - -class HTTPConnectionPool(ConnectionPool, RequestMethods): - """ - Thread-safe connection pool for one host. - - :param host: - Host used for this HTTP Connection (e.g. "localhost"), passed into - :class:`http.client.HTTPConnection`. - - :param port: - Port used for this HTTP Connection (None is equivalent to 80), passed - into :class:`http.client.HTTPConnection`. - - :param strict: - Causes BadStatusLine to be raised if the status line can't be parsed - as a valid HTTP/1.0 or 1.1 status line, passed into - :class:`http.client.HTTPConnection`. - - .. note:: - Only works in Python 2. This parameter is ignored in Python 3. - - :param timeout: - Socket timeout in seconds for each individual connection. This can - be a float or integer, which sets the timeout for the HTTP request, - or an instance of :class:`urllib3.util.Timeout` which gives you more - fine-grained control over request timeouts. After the constructor has - been parsed, this is always a `urllib3.util.Timeout` object. - - :param maxsize: - Number of connections to save that can be reused. More than 1 is useful - in multithreaded situations. If ``block`` is set to False, more - connections will be created but they will not be saved once they've - been used. - - :param block: - If set to True, no more than ``maxsize`` connections will be used at - a time. When no free connections are available, the call will block - until a connection has been released. This is a useful side effect for - particular multithreaded situations where one does not want to use more - than maxsize connections per host to prevent flooding. - - :param headers: - Headers to include with all requests, unless other headers are given - explicitly. - - :param retries: - Retry configuration to use by default with requests in this pool. - - :param _proxy: - Parsed proxy URL, should not be used directly, instead, see - :class:`urllib3.ProxyManager` - - :param _proxy_headers: - A dictionary with proxy headers, should not be used directly, - instead, see :class:`urllib3.ProxyManager` - - :param \\**conn_kw: - Additional parameters are used to create fresh :class:`urllib3.connection.HTTPConnection`, - :class:`urllib3.connection.HTTPSConnection` instances. - """ - - scheme = "http" - ConnectionCls = HTTPConnection - ResponseCls = HTTPResponse - - def __init__( - self, - host, - port=None, - strict=False, - timeout=Timeout.DEFAULT_TIMEOUT, - maxsize=1, - block=False, - headers=None, - retries=None, - _proxy=None, - _proxy_headers=None, - _proxy_config=None, - **conn_kw - ): - ConnectionPool.__init__(self, host, port) - RequestMethods.__init__(self, headers) - - self.strict = strict - - if not isinstance(timeout, Timeout): - timeout = Timeout.from_float(timeout) - - if retries is None: - retries = Retry.DEFAULT - - self.timeout = timeout - self.retries = retries - - self.pool = self.QueueCls(maxsize) - self.block = block - - self.proxy = _proxy - self.proxy_headers = _proxy_headers or {} - self.proxy_config = _proxy_config - - # Fill the queue up so that doing get() on it will block properly - for _ in xrange(maxsize): - self.pool.put(None) - - # These are mostly for testing and debugging purposes. - self.num_connections = 0 - self.num_requests = 0 - self.conn_kw = conn_kw - - if self.proxy: - # Enable Nagle's algorithm for proxies, to avoid packet fragmentation. - # We cannot know if the user has added default socket options, so we cannot replace the - # list. - self.conn_kw.setdefault("socket_options", []) - - self.conn_kw["proxy"] = self.proxy - self.conn_kw["proxy_config"] = self.proxy_config - - # Do not pass 'self' as callback to 'finalize'. - # Then the 'finalize' would keep an endless living (leak) to self. - # By just passing a reference to the pool allows the garbage collector - # to free self if nobody else has a reference to it. - pool = self.pool - - # Close all the HTTPConnections in the pool before the - # HTTPConnectionPool object is garbage collected. - weakref_finalize(self, _close_pool_connections, pool) - - def _new_conn(self): - """ - Return a fresh :class:`HTTPConnection`. - """ - self.num_connections += 1 - log.debug( - "Starting new HTTP connection (%d): %s:%s", - self.num_connections, - self.host, - self.port or "80", - ) - - conn = self.ConnectionCls( - host=self.host, - port=self.port, - timeout=self.timeout.connect_timeout, - strict=self.strict, - **self.conn_kw - ) - return conn - - def _get_conn(self, timeout=None): - """ - Get a connection. Will return a pooled connection if one is available. - - If no connections are available and :prop:`.block` is ``False``, then a - fresh connection is returned. - - :param timeout: - Seconds to wait before giving up and raising - :class:`urllib3.exceptions.EmptyPoolError` if the pool is empty and - :prop:`.block` is ``True``. - """ - conn = None - try: - conn = self.pool.get(block=self.block, timeout=timeout) - - except AttributeError: # self.pool is None - raise ClosedPoolError(self, "Pool is closed.") - - except queue.Empty: - if self.block: - raise EmptyPoolError( - self, - "Pool reached maximum size and no more connections are allowed.", - ) - pass # Oh well, we'll create a new connection then - - # If this is a persistent connection, check if it got disconnected - if conn and is_connection_dropped(conn): - log.debug("Resetting dropped connection: %s", self.host) - conn.close() - if getattr(conn, "auto_open", 1) == 0: - # This is a proxied connection that has been mutated by - # http.client._tunnel() and cannot be reused (since it would - # attempt to bypass the proxy) - conn = None - - return conn or self._new_conn() - - def _put_conn(self, conn): - """ - Put a connection back into the pool. - - :param conn: - Connection object for the current host and port as returned by - :meth:`._new_conn` or :meth:`._get_conn`. - - If the pool is already full, the connection is closed and discarded - because we exceeded maxsize. If connections are discarded frequently, - then maxsize should be increased. - - If the pool is closed, then the connection will be closed and discarded. - """ - try: - self.pool.put(conn, block=False) - return # Everything is dandy, done. - except AttributeError: - # self.pool is None. - pass - except queue.Full: - # This should never happen if self.block == True - log.warning( - "Connection pool is full, discarding connection: %s. Connection pool size: %s", - self.host, - self.pool.qsize(), - ) - # Connection never got put back into the pool, close it. - if conn: - conn.close() - - def _validate_conn(self, conn): - """ - Called right before a request is made, after the socket is created. - """ - pass - - def _prepare_proxy(self, conn): - # Nothing to do for HTTP connections. - pass - - def _get_timeout(self, timeout): - """Helper that always returns a :class:`urllib3.util.Timeout`""" - if timeout is _Default: - return self.timeout.clone() - - if isinstance(timeout, Timeout): - return timeout.clone() - else: - # User passed us an int/float. This is for backwards compatibility, - # can be removed later - return Timeout.from_float(timeout) - - def _raise_timeout(self, err, url, timeout_value): - """Is the error actually a timeout? Will raise a ReadTimeout or pass""" - - if isinstance(err, SocketTimeout): - raise ReadTimeoutError( - self, url, "Read timed out. (read timeout=%s)" % timeout_value - ) - - # See the above comment about EAGAIN in Python 3. In Python 2 we have - # to specifically catch it and throw the timeout error - if hasattr(err, "errno") and err.errno in _blocking_errnos: - raise ReadTimeoutError( - self, url, "Read timed out. (read timeout=%s)" % timeout_value - ) - - # Catch possible read timeouts thrown as SSL errors. If not the - # case, rethrow the original. We need to do this because of: - # http://bugs.python.org/issue10272 - if "timed out" in str(err) or "did not complete (read)" in str( - err - ): # Python < 2.7.4 - raise ReadTimeoutError( - self, url, "Read timed out. (read timeout=%s)" % timeout_value - ) - - def _make_request( - self, conn, method, url, timeout=_Default, chunked=False, **httplib_request_kw - ): - """ - Perform a request on a given urllib connection object taken from our - pool. - - :param conn: - a connection from one of our connection pools - - :param timeout: - Socket timeout in seconds for the request. This can be a - float or integer, which will set the same timeout value for - the socket connect and the socket read, or an instance of - :class:`urllib3.util.Timeout`, which gives you more fine-grained - control over your timeouts. - """ - self.num_requests += 1 - - timeout_obj = self._get_timeout(timeout) - timeout_obj.start_connect() - conn.timeout = Timeout.resolve_default_timeout(timeout_obj.connect_timeout) - - # Trigger any extra validation we need to do. - try: - self._validate_conn(conn) - except (SocketTimeout, BaseSSLError) as e: - # Py2 raises this as a BaseSSLError, Py3 raises it as socket timeout. - self._raise_timeout(err=e, url=url, timeout_value=conn.timeout) - raise - - # conn.request() calls http.client.*.request, not the method in - # urllib3.request. It also calls makefile (recv) on the socket. - try: - if chunked: - conn.request_chunked(method, url, **httplib_request_kw) - else: - conn.request(method, url, **httplib_request_kw) - - # We are swallowing BrokenPipeError (errno.EPIPE) since the server is - # legitimately able to close the connection after sending a valid response. - # With this behaviour, the received response is still readable. - except BrokenPipeError: - # Python 3 - pass - except IOError as e: - # Python 2 and macOS/Linux - # 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 - - # Reset the timeout for the recv() on the socket - read_timeout = timeout_obj.read_timeout - - # App Engine doesn't have a sock attr - if getattr(conn, "sock", None): - # In Python 3 socket.py will catch EAGAIN and return None when you - # try and read into the file pointer created by http.client, which - # instead raises a BadStatusLine exception. Instead of catching - # the exception and assuming all BadStatusLine exceptions are read - # timeouts, check for a zero timeout before making the request. - if read_timeout == 0: - raise ReadTimeoutError( - self, url, "Read timed out. (read timeout=%s)" % read_timeout - ) - if read_timeout is Timeout.DEFAULT_TIMEOUT: - conn.sock.settimeout(socket.getdefaulttimeout()) - else: # None or a value - conn.sock.settimeout(read_timeout) - - # Receive the response from the server - try: - try: - # Python 2.7, use buffering of HTTP responses - httplib_response = conn.getresponse(buffering=True) - except TypeError: - # Python 3 - try: - httplib_response = conn.getresponse() - except BaseException as e: - # Remove the TypeError from the exception chain in - # Python 3 (including for exceptions like SystemExit). - # Otherwise it looks like a bug in the code. - six.raise_from(e, None) - except (SocketTimeout, BaseSSLError, SocketError) as e: - self._raise_timeout(err=e, url=url, timeout_value=read_timeout) - raise - - # AppEngine doesn't have a version attr. - http_version = getattr(conn, "_http_vsn_str", "HTTP/?") - log.debug( - '%s://%s:%s "%s %s %s" %s %s', - self.scheme, - self.host, - self.port, - method, - url, - http_version, - httplib_response.status, - httplib_response.length, - ) - - try: - assert_header_parsing(httplib_response.msg) - except (HeaderParsingError, TypeError) as hpe: # Platform-specific: Python 3 - log.warning( - "Failed to parse headers (url=%s): %s", - self._absolute_url(url), - hpe, - exc_info=True, - ) - - return httplib_response - - def _absolute_url(self, path): - return Url(scheme=self.scheme, host=self.host, port=self.port, path=path).url - - def close(self): - """ - Close all pooled connections and disable the pool. - """ - if self.pool is None: - return - # Disable access to the pool - old_pool, self.pool = self.pool, None - - # Close all the HTTPConnections in the pool. - _close_pool_connections(old_pool) - - def is_same_host(self, url): - """ - Check if the given ``url`` is a member of the same host as this - connection pool. - """ - if url.startswith("/"): - return True - - # TODO: Add optional support for socket.gethostbyname checking. - scheme, host, port = get_host(url) - if host is not None: - host = _normalize_host(host, scheme=scheme) - - # Use explicit default port for comparison when none is given - if self.port and not port: - port = port_by_scheme.get(scheme) - elif not self.port and port == port_by_scheme.get(scheme): - port = None - - return (scheme, host, port) == (self.scheme, self.host, self.port) - - def urlopen( - self, - method, - url, - body=None, - headers=None, - retries=None, - redirect=True, - assert_same_host=True, - timeout=_Default, - pool_timeout=None, - release_conn=None, - chunked=False, - body_pos=None, - **response_kw - ): - """ - Get a connection from the pool and perform an HTTP request. This is the - lowest level call for making a request, so you'll need to specify all - the raw details. - - .. note:: - - More commonly, it's appropriate to use a convenience method provided - by :class:`.RequestMethods`, such as :meth:`request`. - - .. note:: - - `release_conn` will only behave as expected if - `preload_content=False` because we want to make - `preload_content=False` the default behaviour someday soon without - breaking backwards compatibility. - - :param method: - HTTP request method (such as GET, POST, PUT, etc.) - - :param url: - The URL to perform the request on. - - :param body: - Data to send in the request body, either :class:`str`, :class:`bytes`, - an iterable of :class:`str`/:class:`bytes`, or a file-like object. - - :param headers: - Dictionary of custom headers to send, such as User-Agent, - If-None-Match, etc. If None, pool headers are used. If provided, - these headers completely replace any pool-specific headers. - - :param retries: - Configure the number of retries to allow before raising a - :class:`~urllib3.exceptions.MaxRetryError` exception. - - Pass ``None`` to retry until you receive a response. Pass a - :class:`~urllib3.util.retry.Retry` object for fine-grained control - over different types of retries. - Pass an integer number to retry connection errors that many times, - but no other types of errors. Pass zero to never retry. - - If ``False``, then retries are disabled and any exception is raised - immediately. Also, instead of raising a MaxRetryError on redirects, - the redirect response will be returned. - - :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. - - :param redirect: - If True, automatically handle redirects (status codes 301, 302, - 303, 307, 308). Each redirect counts as a retry. Disabling retries - will disable redirect, too. - - :param assert_same_host: - If ``True``, will make sure that the host of the pool requests is - consistent else will raise HostChangedError. When ``False``, you can - use the pool on an HTTP proxy and request foreign hosts. - - :param timeout: - If specified, overrides the default timeout for this one - request. It may be a float (in seconds) or an instance of - :class:`urllib3.util.Timeout`. - - :param pool_timeout: - If set and the pool is set to block=True, then this method will - block for ``pool_timeout`` seconds and raise EmptyPoolError if no - connection is available within the time period. - - :param release_conn: - If False, then the urlopen call will not release the connection - back into the pool once a response is received (but will release if - you read the entire contents of the response such as when - `preload_content=True`). This is useful if you're not preloading - the response's content immediately. You will need to call - ``r.release_conn()`` on the response ``r`` to return the connection - back into the pool. If None, it takes the value of - ``response_kw.get('preload_content', True)``. - - :param chunked: - If True, urllib3 will send the body using chunked transfer - encoding. Otherwise, urllib3 will send the body using the standard - content-length form. Defaults to False. - - :param int body_pos: - Position to seek to in file-like body in the event of a retry or - redirect. Typically this won't need to be set because urllib3 will - auto-populate the value when needed. - - :param \\**response_kw: - Additional parameters are passed to - :meth:`urllib3.response.HTTPResponse.from_httplib` - """ - - parsed_url = parse_url(url) - destination_scheme = parsed_url.scheme - - if headers is None: - headers = self.headers - - if not isinstance(retries, Retry): - retries = Retry.from_int(retries, redirect=redirect, default=self.retries) - - if release_conn is None: - release_conn = response_kw.get("preload_content", True) - - # Check host - if assert_same_host and not self.is_same_host(url): - raise HostChangedError(self, url, retries) - - # Ensure that the URL we're connecting to is properly encoded - if url.startswith("/"): - url = six.ensure_str(_encode_target(url)) - else: - url = six.ensure_str(parsed_url.url) - - conn = None - - # Track whether `conn` needs to be released before - # returning/raising/recursing. Update this variable if necessary, and - # leave `release_conn` constant throughout the function. That way, if - # the function recurses, the original value of `release_conn` will be - # passed down into the recursive call, and its value will be respected. - # - # See issue #651 [1] for details. - # - # [1] - release_this_conn = release_conn - - http_tunnel_required = connection_requires_http_tunnel( - self.proxy, self.proxy_config, destination_scheme - ) - - # Merge the proxy headers. Only done when not using HTTP CONNECT. We - # have to copy the headers dict so we can safely change it without those - # changes being reflected in anyone else's copy. - if not http_tunnel_required: - headers = headers.copy() - headers.update(self.proxy_headers) - - # Must keep the exception bound to a separate variable or else Python 3 - # complains about UnboundLocalError. - err = None - - # Keep track of whether we cleanly exited the except block. This - # ensures we do proper cleanup in finally. - clean_exit = False - - # Rewind body position, if needed. Record current position - # for future rewinds in the event of a redirect/retry. - body_pos = set_file_position(body, body_pos) - - try: - # Request a connection from the queue. - timeout_obj = self._get_timeout(timeout) - conn = self._get_conn(timeout=pool_timeout) - - conn.timeout = timeout_obj.connect_timeout - - is_new_proxy_conn = self.proxy is not None and not getattr( - conn, "sock", None - ) - if is_new_proxy_conn and http_tunnel_required: - self._prepare_proxy(conn) - - # Make the request on the httplib connection object. - httplib_response = self._make_request( - conn, - method, - url, - timeout=timeout_obj, - body=body, - headers=headers, - chunked=chunked, - ) - - # If we're going to release the connection in ``finally:``, then - # the response doesn't need to know about the connection. Otherwise - # it will also try to release it and we'll have a double-release - # mess. - response_conn = conn if not release_conn else None - - # Pass method to Response for length checking - response_kw["request_method"] = method - - # Import httplib's response into our own wrapper object - response = self.ResponseCls.from_httplib( - httplib_response, - pool=self, - connection=response_conn, - retries=retries, - **response_kw - ) - - # Everything went great! - clean_exit = True - - except EmptyPoolError: - # Didn't get a connection from the pool, no need to clean up - clean_exit = True - release_this_conn = False - raise - - except ( - TimeoutError, - HTTPException, - SocketError, - ProtocolError, - BaseSSLError, - SSLError, - CertificateError, - ) as e: - # Discard the connection for these exceptions. It will be - # replaced during the next _get_conn() call. - clean_exit = False - - def _is_ssl_error_message_from_http_proxy(ssl_error): - # We're trying to detect the message 'WRONG_VERSION_NUMBER' but - # SSLErrors are kinda all over the place when it comes to the message, - # 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 - or "record layer failure" in message - ) - - # Try to detect a common user error with proxies which is to - # set an HTTP proxy to be HTTPS when it should be 'http://' - # (ie {'http': 'http://proxy', 'https': 'https://proxy'}) - # Instead we add a nice error message and point to a URL. - if ( - isinstance(e, BaseSSLError) - and self.proxy - and _is_ssl_error_message_from_http_proxy(e) - and conn.proxy - and conn.proxy.scheme == "https" - ): - e = ProxyError( - "Your proxy appears to only use HTTP and not HTTPS, " - "try changing your proxy URL to be HTTP. See: " - "https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html" - "#https-proxy-error-http-proxy", - SSLError(e), - ) - elif isinstance(e, (BaseSSLError, CertificateError)): - e = SSLError(e) - elif isinstance(e, (SocketError, NewConnectionError)) and self.proxy: - e = ProxyError("Cannot connect to proxy.", e) - elif isinstance(e, (SocketError, HTTPException)): - e = ProtocolError("Connection aborted.", e) - - retries = retries.increment( - method, url, error=e, _pool=self, _stacktrace=sys.exc_info()[2] - ) - retries.sleep() - - # Keep track of the error for the retry warning. - err = e - - finally: - if not clean_exit: - # We hit some kind of exception, handled or otherwise. We need - # to throw the connection away unless explicitly told not to. - # Close the connection, set the variable to None, and make sure - # we put the None back in the pool to avoid leaking it. - conn = conn and conn.close() - release_this_conn = True - - if release_this_conn: - # Put the connection back to be reused. If the connection is - # expired then it will be None, which will get replaced with a - # fresh connection during _get_conn. - self._put_conn(conn) - - if not conn: - # Try again - log.warning( - "Retrying (%r) after connection broken by '%r': %s", retries, err, url - ) - return self.urlopen( - method, - url, - body, - headers, - retries, - redirect, - assert_same_host, - timeout=timeout, - pool_timeout=pool_timeout, - release_conn=release_conn, - chunked=chunked, - body_pos=body_pos, - **response_kw - ) - - # Handle redirect? - redirect_location = redirect and response.get_redirect_location() - if redirect_location: - if response.status == 303: - # Change the method according to RFC 9110, Section 15.4.4. - method = "GET" - # And lose the body not to transfer anything sensitive. - body = None - headers = HTTPHeaderDict(headers)._prepare_for_method_change() - - try: - retries = retries.increment(method, url, response=response, _pool=self) - except MaxRetryError: - if retries.raise_on_redirect: - response.drain_conn() - raise - return response - - response.drain_conn() - retries.sleep_for_retry(response) - log.debug("Redirecting %s -> %s", url, redirect_location) - return self.urlopen( - method, - redirect_location, - body, - headers, - retries=retries, - redirect=redirect, - assert_same_host=assert_same_host, - timeout=timeout, - pool_timeout=pool_timeout, - release_conn=release_conn, - chunked=chunked, - body_pos=body_pos, - **response_kw - ) - - # Check if we should retry the HTTP response. - has_retry_after = bool(response.headers.get("Retry-After")) - if retries.is_retry(method, response.status, has_retry_after): - try: - retries = retries.increment(method, url, response=response, _pool=self) - except MaxRetryError: - if retries.raise_on_status: - response.drain_conn() - raise - return response - - response.drain_conn() - retries.sleep(response) - log.debug("Retry: %s", url) - return self.urlopen( - method, - url, - body, - headers, - retries=retries, - redirect=redirect, - assert_same_host=assert_same_host, - timeout=timeout, - pool_timeout=pool_timeout, - release_conn=release_conn, - chunked=chunked, - body_pos=body_pos, - **response_kw - ) - - return response - - -class HTTPSConnectionPool(HTTPConnectionPool): - """ - Same as :class:`.HTTPConnectionPool`, but HTTPS. - - :class:`.HTTPSConnection` uses one of ``assert_fingerprint``, - ``assert_hostname`` and ``host`` in this order to verify connections. - If ``assert_hostname`` is False, no verification is done. - - The ``key_file``, ``cert_file``, ``cert_reqs``, ``ca_certs``, - ``ca_cert_dir``, ``ssl_version``, ``key_password`` are only used if :mod:`ssl` - is available and are fed into :meth:`urllib3.util.ssl_wrap_socket` to upgrade - the connection socket into an SSL socket. - """ - - scheme = "https" - ConnectionCls = HTTPSConnection - - def __init__( - self, - host, - port=None, - strict=False, - timeout=Timeout.DEFAULT_TIMEOUT, - maxsize=1, - block=False, - headers=None, - retries=None, - _proxy=None, - _proxy_headers=None, - key_file=None, - cert_file=None, - cert_reqs=None, - key_password=None, - ca_certs=None, - ssl_version=None, - assert_hostname=None, - assert_fingerprint=None, - ca_cert_dir=None, - **conn_kw - ): - - HTTPConnectionPool.__init__( - self, - host, - port, - strict, - timeout, - maxsize, - block, - headers, - retries, - _proxy, - _proxy_headers, - **conn_kw - ) - - self.key_file = key_file - self.cert_file = cert_file - self.cert_reqs = cert_reqs - self.key_password = key_password - self.ca_certs = ca_certs - self.ca_cert_dir = ca_cert_dir - self.ssl_version = ssl_version - self.assert_hostname = assert_hostname - self.assert_fingerprint = assert_fingerprint - - def _prepare_conn(self, conn): - """ - Prepare the ``connection`` for :meth:`urllib3.util.ssl_wrap_socket` - and establish the tunnel if proxy is used. - """ - - if isinstance(conn, VerifiedHTTPSConnection): - conn.set_cert( - key_file=self.key_file, - key_password=self.key_password, - cert_file=self.cert_file, - cert_reqs=self.cert_reqs, - ca_certs=self.ca_certs, - ca_cert_dir=self.ca_cert_dir, - assert_hostname=self.assert_hostname, - assert_fingerprint=self.assert_fingerprint, - ) - conn.ssl_version = self.ssl_version - return conn - - def _prepare_proxy(self, conn): - """ - Establishes a tunnel connection through HTTP CONNECT. - - Tunnel connection is established early because otherwise httplib would - improperly set Host: header to proxy's IP:port. - """ - - conn.set_tunnel(self._proxy_host, self.port, self.proxy_headers) - - if self.proxy.scheme == "https": - conn.tls_in_tls_required = True - - conn.connect() - - def _new_conn(self): - """ - Return a fresh :class:`http.client.HTTPSConnection`. - """ - self.num_connections += 1 - log.debug( - "Starting new HTTPS connection (%d): %s:%s", - self.num_connections, - self.host, - self.port or "443", - ) - - if not self.ConnectionCls or self.ConnectionCls is DummyConnection: - raise SSLError( - "Can't connect to HTTPS URL because the SSL module is not available." - ) - - actual_host = self.host - actual_port = self.port - if self.proxy is not None: - actual_host = self.proxy.host - actual_port = self.proxy.port - - conn = self.ConnectionCls( - host=actual_host, - port=actual_port, - timeout=self.timeout.connect_timeout, - strict=self.strict, - cert_file=self.cert_file, - key_file=self.key_file, - key_password=self.key_password, - **self.conn_kw - ) - - return self._prepare_conn(conn) - - def _validate_conn(self, conn): - """ - Called right before a request is made, after the socket is created. - """ - super(HTTPSConnectionPool, self)._validate_conn(conn) - - # Force connect early to allow us to validate the connection. - if not getattr(conn, "sock", None): # AppEngine might not have `.sock` - conn.connect() - - if not conn.is_verified: - warnings.warn( - ( - "Unverified HTTPS request is being made to host '%s'. " - "Adding certificate verification is strongly advised. See: " - "https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html" - "#ssl-warnings" % conn.host - ), - InsecureRequestWarning, - ) - - if getattr(conn, "proxy_is_verified", None) is False: - warnings.warn( - ( - "Unverified HTTPS connection done to an HTTPS proxy. " - "Adding certificate verification is strongly advised. See: " - "https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html" - "#ssl-warnings" - ), - InsecureRequestWarning, - ) - - -def connection_from_url(url, **kw): - """ - Given a url, return an :class:`.ConnectionPool` instance of its host. - - This is a shortcut for not having to parse out the scheme, host, and port - of the url before creating an :class:`.ConnectionPool` instance. - - :param url: - Absolute URL string that must include the scheme. Port is optional. - - :param \\**kw: - Passes additional parameters to the constructor of the appropriate - :class:`.ConnectionPool`. Useful for specifying things like - timeout, maxsize, headers, etc. - - Example:: - - >>> conn = connection_from_url('http://google.com/') - >>> r = conn.request('GET', '/') - """ - scheme, host, port = get_host(url) - port = port or port_by_scheme.get(scheme, 80) - if scheme == "https": - return HTTPSConnectionPool(host, port=port, **kw) - else: - return HTTPConnectionPool(host, port=port, **kw) - - -def _normalize_host(host, scheme): - """ - Normalize hosts for comparisons and use with sockets. - """ - - host = normalize_host(host, scheme) - - # httplib doesn't like it when we include brackets in IPv6 addresses - # Specifically, if we include brackets but also pass the port then - # httplib crazily doubles up the square brackets on the Host header. - # Instead, we need to make sure we never pass ``None`` as the port. - # However, for backward compatibility reasons we can't actually - # *assert* that. See http://bugs.python.org/issue28539 - if host.startswith("[") and host.endswith("]"): - host = host[1:-1] - return host - - -def _close_pool_connections(pool): - """Drains a queue of connections and closes each one.""" - try: - while True: - conn = pool.get(block=False) - if conn: - conn.close() - except queue.Empty: - pass # Done. diff --git a/apps/bitwarden_event_logs/lib/urllib3/contrib/__init__.py b/apps/bitwarden_event_logs/lib/urllib3/contrib/__init__.py deleted file mode 100755 index e69de29b..00000000 diff --git a/apps/bitwarden_event_logs/lib/urllib3/contrib/__pycache__/__init__.cpython-39.pyc b/apps/bitwarden_event_logs/lib/urllib3/contrib/__pycache__/__init__.cpython-39.pyc deleted file mode 100755 index e168e68870e68a5799cd0cdf2a298f86e9019d44..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 171 zcmYe~<>g`k0_DX^GC}lX5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;x_OettoTesMug zXP5S!!NMd`^BkNH9-dPftH5GfBU+CnG>u jl@tN_@$s2?nI-Y@dIgoYIBatBQ%ZAE?Lant24V&Po4G4M diff --git a/apps/bitwarden_event_logs/lib/urllib3/contrib/__pycache__/_appengine_environ.cpython-39.pyc b/apps/bitwarden_event_logs/lib/urllib3/contrib/__pycache__/_appengine_environ.cpython-39.pyc deleted file mode 100755 index 6b251b0bcfe18925328d09b4a877e77a105d47b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1391 zcmb_cPfrvv6z}|DcNic7AtWZmG;%N`W+u^t#uy{YLO8gRWz}RalVMtR3bXB`?JU9- zKL|uGd}JgBeTh24Nho3yY0=DlBkzxO6xSO^ic^{?O9cMqW-W-;AdSiFX= zp2I*;4-tpBh0}A0NBl3S=aL!H0PB$^3BdYfmV{tu$Q+pm+aT^f+E_Sy6Lxc^@Ia8N zptzJ`$OwfsO}WBaU_v#`G}bxAn`Mbxyw5nrlnTuwKFqLHwPAPp{scq zG&)CRsGV~JQJwaNTQ4(}6e3HDBrQv7-PTL62N1|)>k3I9KDWcKUMxx-tFoy0aZGg< z!J8v^*_H8RLF>e#g+BtHGa~~1+*f8s*>p(zJTISNAztqasE3h2r79q^&q0F+84#UN468E{#gVx?J87}x2w zpU3#1(wJ%M&1hX^IWDLIAXEV*nPx-!7%MUMZ@ip}3aeaH1>ui0=9ChX3|UUIV|*wi zc#Et%MJdj;F4c=T&I(bHs4qmnpiw3UMxK$dF%g-Hm4r*>!DuS+}mrlcXr#Y zr1PQO-F?^U1?8#E1%L8v!?XO?u26MztkxtUMza}^pkA4lFBubP5^p49Q<`eJBcn+z zE8_#05J#gUXNbg_u@^3JiFfH@3&KW$0 zjI-q(tIkv@$8y1(-W1t{Q|ZCM8uZ(^%>Er*H`Q)D5kr`JHn&AfNwZW_5^diOVIx}Y zR_|1OG8hijLlfE-p(__*zR|3kNdlRaBqN4RWm;V=Qx>khcsr5}61*J!;$9h`_Lt=Q Mffx8e7&L;$Uks{GbN~PV diff --git a/apps/bitwarden_event_logs/lib/urllib3/contrib/__pycache__/socks.cpython-39.pyc b/apps/bitwarden_event_logs/lib/urllib3/contrib/__pycache__/socks.cpython-39.pyc deleted file mode 100755 index c56fc147cce325fa922c7540a1ce57d972903ead..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5607 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 diff --git a/apps/bitwarden_event_logs/lib/urllib3/contrib/_appengine_environ.py b/apps/bitwarden_event_logs/lib/urllib3/contrib/_appengine_environ.py deleted file mode 100755 index 8765b907..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/contrib/_appengine_environ.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -This module provides means to detect the App Engine environment. -""" - -import os - - -def is_appengine(): - return is_local_appengine() or is_prod_appengine() - - -def is_appengine_sandbox(): - """Reports if the app is running in the first generation sandbox. - - The second generation runtimes are technically still in a sandbox, but it - is much less restrictive, so generally you shouldn't need to check for it. - see https://cloud.google.com/appengine/docs/standard/runtimes - """ - return is_appengine() and os.environ["APPENGINE_RUNTIME"] == "python27" - - -def is_local_appengine(): - return "APPENGINE_RUNTIME" in os.environ and os.environ.get( - "SERVER_SOFTWARE", "" - ).startswith("Development/") - - -def is_prod_appengine(): - return "APPENGINE_RUNTIME" in os.environ and os.environ.get( - "SERVER_SOFTWARE", "" - ).startswith("Google App Engine/") - - -def is_prod_appengine_mvms(): - """Deprecated.""" - return False diff --git a/apps/bitwarden_event_logs/lib/urllib3/contrib/_securetransport/__init__.py b/apps/bitwarden_event_logs/lib/urllib3/contrib/_securetransport/__init__.py deleted file mode 100755 index e69de29b..00000000 diff --git a/apps/bitwarden_event_logs/lib/urllib3/contrib/_securetransport/bindings.py b/apps/bitwarden_event_logs/lib/urllib3/contrib/_securetransport/bindings.py deleted file mode 100755 index 264d564d..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/contrib/_securetransport/bindings.py +++ /dev/null @@ -1,519 +0,0 @@ -""" -This module uses ctypes to bind a whole bunch of functions and constants from -SecureTransport. The goal here is to provide the low-level API to -SecureTransport. These are essentially the C-level functions and constants, and -they're pretty gross to work with. - -This code is a bastardised version of the code found in Will Bond's oscrypto -library. An enormous debt is owed to him for blazing this trail for us. For -that reason, this code should be considered to be covered both by urllib3's -license and by oscrypto's: - - Copyright (c) 2015-2016 Will Bond - - 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 absolute_import - -import platform -from ctypes import ( - CDLL, - CFUNCTYPE, - POINTER, - c_bool, - c_byte, - c_char_p, - c_int32, - c_long, - c_size_t, - c_uint32, - c_ulong, - c_void_p, -) -from ctypes.util import find_library - -from ...packages.six import raise_from - -if platform.system() != "Darwin": - raise ImportError("Only macOS is supported") - -version = platform.mac_ver()[0] -version_info = tuple(map(int, version.split("."))) -if version_info < (10, 8): - raise OSError( - "Only OS X 10.8 and newer are supported, not %s.%s" - % (version_info[0], version_info[1]) - ) - - -def load_cdll(name, macos10_16_path): - """Loads a CDLL by name, falling back to known path on 10.16+""" - try: - # Big Sur is technically 11 but we use 10.16 due to the Big Sur - # beta being labeled as 10.16. - if version_info >= (10, 16): - path = macos10_16_path - else: - path = find_library(name) - if not path: - raise OSError # Caught and reraised as 'ImportError' - return CDLL(path, use_errno=True) - except OSError: - raise_from(ImportError("The library %s failed to load" % name), None) - - -Security = load_cdll( - "Security", "/System/Library/Frameworks/Security.framework/Security" -) -CoreFoundation = load_cdll( - "CoreFoundation", - "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", -) - - -Boolean = c_bool -CFIndex = c_long -CFStringEncoding = c_uint32 -CFData = c_void_p -CFString = c_void_p -CFArray = c_void_p -CFMutableArray = c_void_p -CFDictionary = c_void_p -CFError = c_void_p -CFType = c_void_p -CFTypeID = c_ulong - -CFTypeRef = POINTER(CFType) -CFAllocatorRef = c_void_p - -OSStatus = c_int32 - -CFDataRef = POINTER(CFData) -CFStringRef = POINTER(CFString) -CFArrayRef = POINTER(CFArray) -CFMutableArrayRef = POINTER(CFMutableArray) -CFDictionaryRef = POINTER(CFDictionary) -CFArrayCallBacks = c_void_p -CFDictionaryKeyCallBacks = c_void_p -CFDictionaryValueCallBacks = c_void_p - -SecCertificateRef = POINTER(c_void_p) -SecExternalFormat = c_uint32 -SecExternalItemType = c_uint32 -SecIdentityRef = POINTER(c_void_p) -SecItemImportExportFlags = c_uint32 -SecItemImportExportKeyParameters = c_void_p -SecKeychainRef = POINTER(c_void_p) -SSLProtocol = c_uint32 -SSLCipherSuite = c_uint32 -SSLContextRef = POINTER(c_void_p) -SecTrustRef = POINTER(c_void_p) -SSLConnectionRef = c_uint32 -SecTrustResultType = c_uint32 -SecTrustOptionFlags = c_uint32 -SSLProtocolSide = c_uint32 -SSLConnectionType = c_uint32 -SSLSessionOption = c_uint32 - - -try: - Security.SecItemImport.argtypes = [ - CFDataRef, - CFStringRef, - POINTER(SecExternalFormat), - POINTER(SecExternalItemType), - SecItemImportExportFlags, - POINTER(SecItemImportExportKeyParameters), - SecKeychainRef, - POINTER(CFArrayRef), - ] - Security.SecItemImport.restype = OSStatus - - Security.SecCertificateGetTypeID.argtypes = [] - Security.SecCertificateGetTypeID.restype = CFTypeID - - Security.SecIdentityGetTypeID.argtypes = [] - Security.SecIdentityGetTypeID.restype = CFTypeID - - Security.SecKeyGetTypeID.argtypes = [] - Security.SecKeyGetTypeID.restype = CFTypeID - - Security.SecCertificateCreateWithData.argtypes = [CFAllocatorRef, CFDataRef] - Security.SecCertificateCreateWithData.restype = SecCertificateRef - - Security.SecCertificateCopyData.argtypes = [SecCertificateRef] - Security.SecCertificateCopyData.restype = CFDataRef - - Security.SecCopyErrorMessageString.argtypes = [OSStatus, c_void_p] - Security.SecCopyErrorMessageString.restype = CFStringRef - - Security.SecIdentityCreateWithCertificate.argtypes = [ - CFTypeRef, - SecCertificateRef, - POINTER(SecIdentityRef), - ] - Security.SecIdentityCreateWithCertificate.restype = OSStatus - - Security.SecKeychainCreate.argtypes = [ - c_char_p, - c_uint32, - c_void_p, - Boolean, - c_void_p, - POINTER(SecKeychainRef), - ] - Security.SecKeychainCreate.restype = OSStatus - - Security.SecKeychainDelete.argtypes = [SecKeychainRef] - Security.SecKeychainDelete.restype = OSStatus - - Security.SecPKCS12Import.argtypes = [ - CFDataRef, - CFDictionaryRef, - POINTER(CFArrayRef), - ] - Security.SecPKCS12Import.restype = OSStatus - - SSLReadFunc = CFUNCTYPE(OSStatus, SSLConnectionRef, c_void_p, POINTER(c_size_t)) - SSLWriteFunc = CFUNCTYPE( - OSStatus, SSLConnectionRef, POINTER(c_byte), POINTER(c_size_t) - ) - - Security.SSLSetIOFuncs.argtypes = [SSLContextRef, SSLReadFunc, SSLWriteFunc] - Security.SSLSetIOFuncs.restype = OSStatus - - Security.SSLSetPeerID.argtypes = [SSLContextRef, c_char_p, c_size_t] - Security.SSLSetPeerID.restype = OSStatus - - Security.SSLSetCertificate.argtypes = [SSLContextRef, CFArrayRef] - Security.SSLSetCertificate.restype = OSStatus - - Security.SSLSetCertificateAuthorities.argtypes = [SSLContextRef, CFTypeRef, Boolean] - Security.SSLSetCertificateAuthorities.restype = OSStatus - - Security.SSLSetConnection.argtypes = [SSLContextRef, SSLConnectionRef] - Security.SSLSetConnection.restype = OSStatus - - Security.SSLSetPeerDomainName.argtypes = [SSLContextRef, c_char_p, c_size_t] - Security.SSLSetPeerDomainName.restype = OSStatus - - Security.SSLHandshake.argtypes = [SSLContextRef] - Security.SSLHandshake.restype = OSStatus - - Security.SSLRead.argtypes = [SSLContextRef, c_char_p, c_size_t, POINTER(c_size_t)] - Security.SSLRead.restype = OSStatus - - Security.SSLWrite.argtypes = [SSLContextRef, c_char_p, c_size_t, POINTER(c_size_t)] - Security.SSLWrite.restype = OSStatus - - Security.SSLClose.argtypes = [SSLContextRef] - Security.SSLClose.restype = OSStatus - - Security.SSLGetNumberSupportedCiphers.argtypes = [SSLContextRef, POINTER(c_size_t)] - Security.SSLGetNumberSupportedCiphers.restype = OSStatus - - Security.SSLGetSupportedCiphers.argtypes = [ - SSLContextRef, - POINTER(SSLCipherSuite), - POINTER(c_size_t), - ] - Security.SSLGetSupportedCiphers.restype = OSStatus - - Security.SSLSetEnabledCiphers.argtypes = [ - SSLContextRef, - POINTER(SSLCipherSuite), - c_size_t, - ] - Security.SSLSetEnabledCiphers.restype = OSStatus - - Security.SSLGetNumberEnabledCiphers.argtype = [SSLContextRef, POINTER(c_size_t)] - Security.SSLGetNumberEnabledCiphers.restype = OSStatus - - Security.SSLGetEnabledCiphers.argtypes = [ - SSLContextRef, - POINTER(SSLCipherSuite), - POINTER(c_size_t), - ] - Security.SSLGetEnabledCiphers.restype = OSStatus - - Security.SSLGetNegotiatedCipher.argtypes = [SSLContextRef, POINTER(SSLCipherSuite)] - Security.SSLGetNegotiatedCipher.restype = OSStatus - - Security.SSLGetNegotiatedProtocolVersion.argtypes = [ - SSLContextRef, - POINTER(SSLProtocol), - ] - Security.SSLGetNegotiatedProtocolVersion.restype = OSStatus - - Security.SSLCopyPeerTrust.argtypes = [SSLContextRef, POINTER(SecTrustRef)] - Security.SSLCopyPeerTrust.restype = OSStatus - - Security.SecTrustSetAnchorCertificates.argtypes = [SecTrustRef, CFArrayRef] - Security.SecTrustSetAnchorCertificates.restype = OSStatus - - Security.SecTrustSetAnchorCertificatesOnly.argstypes = [SecTrustRef, Boolean] - Security.SecTrustSetAnchorCertificatesOnly.restype = OSStatus - - Security.SecTrustEvaluate.argtypes = [SecTrustRef, POINTER(SecTrustResultType)] - Security.SecTrustEvaluate.restype = OSStatus - - Security.SecTrustGetCertificateCount.argtypes = [SecTrustRef] - Security.SecTrustGetCertificateCount.restype = CFIndex - - Security.SecTrustGetCertificateAtIndex.argtypes = [SecTrustRef, CFIndex] - Security.SecTrustGetCertificateAtIndex.restype = SecCertificateRef - - Security.SSLCreateContext.argtypes = [ - CFAllocatorRef, - SSLProtocolSide, - SSLConnectionType, - ] - Security.SSLCreateContext.restype = SSLContextRef - - Security.SSLSetSessionOption.argtypes = [SSLContextRef, SSLSessionOption, Boolean] - Security.SSLSetSessionOption.restype = OSStatus - - Security.SSLSetProtocolVersionMin.argtypes = [SSLContextRef, SSLProtocol] - Security.SSLSetProtocolVersionMin.restype = OSStatus - - Security.SSLSetProtocolVersionMax.argtypes = [SSLContextRef, SSLProtocol] - Security.SSLSetProtocolVersionMax.restype = OSStatus - - try: - Security.SSLSetALPNProtocols.argtypes = [SSLContextRef, CFArrayRef] - Security.SSLSetALPNProtocols.restype = OSStatus - except AttributeError: - # Supported only in 10.12+ - pass - - Security.SecCopyErrorMessageString.argtypes = [OSStatus, c_void_p] - Security.SecCopyErrorMessageString.restype = CFStringRef - - Security.SSLReadFunc = SSLReadFunc - Security.SSLWriteFunc = SSLWriteFunc - Security.SSLContextRef = SSLContextRef - Security.SSLProtocol = SSLProtocol - Security.SSLCipherSuite = SSLCipherSuite - Security.SecIdentityRef = SecIdentityRef - Security.SecKeychainRef = SecKeychainRef - Security.SecTrustRef = SecTrustRef - Security.SecTrustResultType = SecTrustResultType - Security.SecExternalFormat = SecExternalFormat - Security.OSStatus = OSStatus - - Security.kSecImportExportPassphrase = CFStringRef.in_dll( - Security, "kSecImportExportPassphrase" - ) - Security.kSecImportItemIdentity = CFStringRef.in_dll( - Security, "kSecImportItemIdentity" - ) - - # CoreFoundation time! - CoreFoundation.CFRetain.argtypes = [CFTypeRef] - CoreFoundation.CFRetain.restype = CFTypeRef - - CoreFoundation.CFRelease.argtypes = [CFTypeRef] - CoreFoundation.CFRelease.restype = None - - CoreFoundation.CFGetTypeID.argtypes = [CFTypeRef] - CoreFoundation.CFGetTypeID.restype = CFTypeID - - CoreFoundation.CFStringCreateWithCString.argtypes = [ - CFAllocatorRef, - c_char_p, - CFStringEncoding, - ] - CoreFoundation.CFStringCreateWithCString.restype = CFStringRef - - CoreFoundation.CFStringGetCStringPtr.argtypes = [CFStringRef, CFStringEncoding] - CoreFoundation.CFStringGetCStringPtr.restype = c_char_p - - CoreFoundation.CFStringGetCString.argtypes = [ - CFStringRef, - c_char_p, - CFIndex, - CFStringEncoding, - ] - CoreFoundation.CFStringGetCString.restype = c_bool - - CoreFoundation.CFDataCreate.argtypes = [CFAllocatorRef, c_char_p, CFIndex] - CoreFoundation.CFDataCreate.restype = CFDataRef - - CoreFoundation.CFDataGetLength.argtypes = [CFDataRef] - CoreFoundation.CFDataGetLength.restype = CFIndex - - CoreFoundation.CFDataGetBytePtr.argtypes = [CFDataRef] - CoreFoundation.CFDataGetBytePtr.restype = c_void_p - - CoreFoundation.CFDictionaryCreate.argtypes = [ - CFAllocatorRef, - POINTER(CFTypeRef), - POINTER(CFTypeRef), - CFIndex, - CFDictionaryKeyCallBacks, - CFDictionaryValueCallBacks, - ] - CoreFoundation.CFDictionaryCreate.restype = CFDictionaryRef - - CoreFoundation.CFDictionaryGetValue.argtypes = [CFDictionaryRef, CFTypeRef] - CoreFoundation.CFDictionaryGetValue.restype = CFTypeRef - - CoreFoundation.CFArrayCreate.argtypes = [ - CFAllocatorRef, - POINTER(CFTypeRef), - CFIndex, - CFArrayCallBacks, - ] - CoreFoundation.CFArrayCreate.restype = CFArrayRef - - CoreFoundation.CFArrayCreateMutable.argtypes = [ - CFAllocatorRef, - CFIndex, - CFArrayCallBacks, - ] - CoreFoundation.CFArrayCreateMutable.restype = CFMutableArrayRef - - CoreFoundation.CFArrayAppendValue.argtypes = [CFMutableArrayRef, c_void_p] - CoreFoundation.CFArrayAppendValue.restype = None - - CoreFoundation.CFArrayGetCount.argtypes = [CFArrayRef] - CoreFoundation.CFArrayGetCount.restype = CFIndex - - CoreFoundation.CFArrayGetValueAtIndex.argtypes = [CFArrayRef, CFIndex] - CoreFoundation.CFArrayGetValueAtIndex.restype = c_void_p - - CoreFoundation.kCFAllocatorDefault = CFAllocatorRef.in_dll( - CoreFoundation, "kCFAllocatorDefault" - ) - CoreFoundation.kCFTypeArrayCallBacks = c_void_p.in_dll( - CoreFoundation, "kCFTypeArrayCallBacks" - ) - CoreFoundation.kCFTypeDictionaryKeyCallBacks = c_void_p.in_dll( - CoreFoundation, "kCFTypeDictionaryKeyCallBacks" - ) - CoreFoundation.kCFTypeDictionaryValueCallBacks = c_void_p.in_dll( - CoreFoundation, "kCFTypeDictionaryValueCallBacks" - ) - - CoreFoundation.CFTypeRef = CFTypeRef - CoreFoundation.CFArrayRef = CFArrayRef - CoreFoundation.CFStringRef = CFStringRef - CoreFoundation.CFDictionaryRef = CFDictionaryRef - -except (AttributeError): - raise ImportError("Error initializing ctypes") - - -class CFConst(object): - """ - A class object that acts as essentially a namespace for CoreFoundation - constants. - """ - - kCFStringEncodingUTF8 = CFStringEncoding(0x08000100) - - -class SecurityConst(object): - """ - A class object that acts as essentially a namespace for Security constants. - """ - - kSSLSessionOptionBreakOnServerAuth = 0 - - kSSLProtocol2 = 1 - kSSLProtocol3 = 2 - kTLSProtocol1 = 4 - kTLSProtocol11 = 7 - kTLSProtocol12 = 8 - # SecureTransport does not support TLS 1.3 even if there's a constant for it - kTLSProtocol13 = 10 - kTLSProtocolMaxSupported = 999 - - kSSLClientSide = 1 - kSSLStreamType = 0 - - kSecFormatPEMSequence = 10 - - kSecTrustResultInvalid = 0 - kSecTrustResultProceed = 1 - # This gap is present on purpose: this was kSecTrustResultConfirm, which - # is deprecated. - kSecTrustResultDeny = 3 - kSecTrustResultUnspecified = 4 - kSecTrustResultRecoverableTrustFailure = 5 - kSecTrustResultFatalTrustFailure = 6 - kSecTrustResultOtherError = 7 - - errSSLProtocol = -9800 - errSSLWouldBlock = -9803 - errSSLClosedGraceful = -9805 - errSSLClosedNoNotify = -9816 - errSSLClosedAbort = -9806 - - errSSLXCertChainInvalid = -9807 - errSSLCrypto = -9809 - errSSLInternal = -9810 - errSSLCertExpired = -9814 - errSSLCertNotYetValid = -9815 - errSSLUnknownRootCert = -9812 - errSSLNoRootCert = -9813 - errSSLHostNameMismatch = -9843 - errSSLPeerHandshakeFail = -9824 - errSSLPeerUserCancelled = -9839 - errSSLWeakPeerEphemeralDHKey = -9850 - errSSLServerAuthCompleted = -9841 - errSSLRecordOverflow = -9847 - - errSecVerifyFailed = -67808 - errSecNoTrustSettings = -25263 - errSecItemNotFound = -25300 - errSecInvalidTrustSettings = -25262 - - # Cipher suites. We only pick the ones our default cipher string allows. - # Source: https://developer.apple.com/documentation/security/1550981-ssl_cipher_suite_values - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030 - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA9 - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA8 - TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F - TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024 - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028 - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014 - TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B - TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039 - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023 - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027 - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009 - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013 - TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067 - TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033 - TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D - TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C - TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D - TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C - TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035 - TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F - TLS_AES_128_GCM_SHA256 = 0x1301 - TLS_AES_256_GCM_SHA384 = 0x1302 - TLS_AES_128_CCM_8_SHA256 = 0x1305 - TLS_AES_128_CCM_SHA256 = 0x1304 diff --git a/apps/bitwarden_event_logs/lib/urllib3/contrib/_securetransport/low_level.py b/apps/bitwarden_event_logs/lib/urllib3/contrib/_securetransport/low_level.py deleted file mode 100755 index fa0b245d..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/contrib/_securetransport/low_level.py +++ /dev/null @@ -1,397 +0,0 @@ -""" -Low-level helpers for the SecureTransport bindings. - -These are Python functions that are not directly related to the high-level APIs -but are necessary to get them to work. They include a whole bunch of low-level -CoreFoundation messing about and memory management. The concerns in this module -are almost entirely about trying to avoid memory leaks and providing -appropriate and useful assistance to the higher-level code. -""" -import base64 -import ctypes -import itertools -import os -import re -import ssl -import struct -import tempfile - -from .bindings import CFConst, CoreFoundation, Security - -# This regular expression is used to grab PEM data out of a PEM bundle. -_PEM_CERTS_RE = re.compile( - b"-----BEGIN CERTIFICATE-----\n(.*?)\n-----END CERTIFICATE-----", re.DOTALL -) - - -def _cf_data_from_bytes(bytestring): - """ - Given a bytestring, create a CFData object from it. This CFData object must - be CFReleased by the caller. - """ - return CoreFoundation.CFDataCreate( - CoreFoundation.kCFAllocatorDefault, bytestring, len(bytestring) - ) - - -def _cf_dictionary_from_tuples(tuples): - """ - Given a list of Python tuples, create an associated CFDictionary. - """ - dictionary_size = len(tuples) - - # We need to get the dictionary keys and values out in the same order. - keys = (t[0] for t in tuples) - values = (t[1] for t in tuples) - cf_keys = (CoreFoundation.CFTypeRef * dictionary_size)(*keys) - cf_values = (CoreFoundation.CFTypeRef * dictionary_size)(*values) - - return CoreFoundation.CFDictionaryCreate( - CoreFoundation.kCFAllocatorDefault, - cf_keys, - cf_values, - dictionary_size, - CoreFoundation.kCFTypeDictionaryKeyCallBacks, - CoreFoundation.kCFTypeDictionaryValueCallBacks, - ) - - -def _cfstr(py_bstr): - """ - Given a Python binary data, create a CFString. - The string must be CFReleased by the caller. - """ - c_str = ctypes.c_char_p(py_bstr) - cf_str = CoreFoundation.CFStringCreateWithCString( - CoreFoundation.kCFAllocatorDefault, - c_str, - CFConst.kCFStringEncodingUTF8, - ) - return cf_str - - -def _create_cfstring_array(lst): - """ - Given a list of Python binary data, create an associated CFMutableArray. - The array must be CFReleased by the caller. - - Raises an ssl.SSLError on failure. - """ - cf_arr = None - try: - cf_arr = CoreFoundation.CFArrayCreateMutable( - CoreFoundation.kCFAllocatorDefault, - 0, - ctypes.byref(CoreFoundation.kCFTypeArrayCallBacks), - ) - if not cf_arr: - raise MemoryError("Unable to allocate memory!") - for item in lst: - cf_str = _cfstr(item) - if not cf_str: - raise MemoryError("Unable to allocate memory!") - try: - CoreFoundation.CFArrayAppendValue(cf_arr, cf_str) - finally: - CoreFoundation.CFRelease(cf_str) - except BaseException as e: - if cf_arr: - CoreFoundation.CFRelease(cf_arr) - raise ssl.SSLError("Unable to allocate array: %s" % (e,)) - return cf_arr - - -def _cf_string_to_unicode(value): - """ - Creates a Unicode string from a CFString object. Used entirely for error - reporting. - - Yes, it annoys me quite a lot that this function is this complex. - """ - value_as_void_p = ctypes.cast(value, ctypes.POINTER(ctypes.c_void_p)) - - string = CoreFoundation.CFStringGetCStringPtr( - value_as_void_p, CFConst.kCFStringEncodingUTF8 - ) - if string is None: - buffer = ctypes.create_string_buffer(1024) - result = CoreFoundation.CFStringGetCString( - value_as_void_p, buffer, 1024, CFConst.kCFStringEncodingUTF8 - ) - if not result: - raise OSError("Error copying C string from CFStringRef") - string = buffer.value - if string is not None: - string = string.decode("utf-8") - return string - - -def _assert_no_error(error, exception_class=None): - """ - Checks the return code and throws an exception if there is an error to - report - """ - if error == 0: - return - - cf_error_string = Security.SecCopyErrorMessageString(error, None) - output = _cf_string_to_unicode(cf_error_string) - CoreFoundation.CFRelease(cf_error_string) - - if output is None or output == u"": - output = u"OSStatus %s" % error - - if exception_class is None: - exception_class = ssl.SSLError - - raise exception_class(output) - - -def _cert_array_from_pem(pem_bundle): - """ - Given a bundle of certs in PEM format, turns them into a CFArray of certs - that can be used to validate a cert chain. - """ - # Normalize the PEM bundle's line endings. - pem_bundle = pem_bundle.replace(b"\r\n", b"\n") - - der_certs = [ - base64.b64decode(match.group(1)) for match in _PEM_CERTS_RE.finditer(pem_bundle) - ] - if not der_certs: - raise ssl.SSLError("No root certificates specified") - - cert_array = CoreFoundation.CFArrayCreateMutable( - CoreFoundation.kCFAllocatorDefault, - 0, - ctypes.byref(CoreFoundation.kCFTypeArrayCallBacks), - ) - if not cert_array: - raise ssl.SSLError("Unable to allocate memory!") - - try: - for der_bytes in der_certs: - certdata = _cf_data_from_bytes(der_bytes) - if not certdata: - raise ssl.SSLError("Unable to allocate memory!") - cert = Security.SecCertificateCreateWithData( - CoreFoundation.kCFAllocatorDefault, certdata - ) - CoreFoundation.CFRelease(certdata) - if not cert: - raise ssl.SSLError("Unable to build cert object!") - - CoreFoundation.CFArrayAppendValue(cert_array, cert) - CoreFoundation.CFRelease(cert) - except Exception: - # We need to free the array before the exception bubbles further. - # We only want to do that if an error occurs: otherwise, the caller - # should free. - CoreFoundation.CFRelease(cert_array) - raise - - return cert_array - - -def _is_cert(item): - """ - Returns True if a given CFTypeRef is a certificate. - """ - expected = Security.SecCertificateGetTypeID() - return CoreFoundation.CFGetTypeID(item) == expected - - -def _is_identity(item): - """ - Returns True if a given CFTypeRef is an identity. - """ - expected = Security.SecIdentityGetTypeID() - return CoreFoundation.CFGetTypeID(item) == expected - - -def _temporary_keychain(): - """ - This function creates a temporary Mac keychain that we can use to work with - credentials. This keychain uses a one-time password and a temporary file to - store the data. We expect to have one keychain per socket. The returned - SecKeychainRef must be freed by the caller, including calling - SecKeychainDelete. - - Returns a tuple of the SecKeychainRef and the path to the temporary - directory that contains it. - """ - # Unfortunately, SecKeychainCreate requires a path to a keychain. This - # means we cannot use mkstemp to use a generic temporary file. Instead, - # we're going to create a temporary directory and a filename to use there. - # This filename will be 8 random bytes expanded into base64. We also need - # some random bytes to password-protect the keychain we're creating, so we - # ask for 40 random bytes. - random_bytes = os.urandom(40) - filename = base64.b16encode(random_bytes[:8]).decode("utf-8") - password = base64.b16encode(random_bytes[8:]) # Must be valid UTF-8 - tempdirectory = tempfile.mkdtemp() - - keychain_path = os.path.join(tempdirectory, filename).encode("utf-8") - - # We now want to create the keychain itself. - keychain = Security.SecKeychainRef() - status = Security.SecKeychainCreate( - keychain_path, len(password), password, False, None, ctypes.byref(keychain) - ) - _assert_no_error(status) - - # Having created the keychain, we want to pass it off to the caller. - return keychain, tempdirectory - - -def _load_items_from_file(keychain, path): - """ - Given a single file, loads all the trust objects from it into arrays and - the keychain. - Returns a tuple of lists: the first list is a list of identities, the - second a list of certs. - """ - certificates = [] - identities = [] - result_array = None - - with open(path, "rb") as f: - raw_filedata = f.read() - - try: - filedata = CoreFoundation.CFDataCreate( - CoreFoundation.kCFAllocatorDefault, raw_filedata, len(raw_filedata) - ) - result_array = CoreFoundation.CFArrayRef() - result = Security.SecItemImport( - filedata, # cert data - None, # Filename, leaving it out for now - None, # What the type of the file is, we don't care - None, # what's in the file, we don't care - 0, # import flags - None, # key params, can include passphrase in the future - keychain, # The keychain to insert into - ctypes.byref(result_array), # Results - ) - _assert_no_error(result) - - # A CFArray is not very useful to us as an intermediary - # representation, so we are going to extract the objects we want - # and then free the array. We don't need to keep hold of keys: the - # keychain already has them! - result_count = CoreFoundation.CFArrayGetCount(result_array) - for index in range(result_count): - item = CoreFoundation.CFArrayGetValueAtIndex(result_array, index) - item = ctypes.cast(item, CoreFoundation.CFTypeRef) - - if _is_cert(item): - CoreFoundation.CFRetain(item) - certificates.append(item) - elif _is_identity(item): - CoreFoundation.CFRetain(item) - identities.append(item) - finally: - if result_array: - CoreFoundation.CFRelease(result_array) - - CoreFoundation.CFRelease(filedata) - - return (identities, certificates) - - -def _load_client_cert_chain(keychain, *paths): - """ - Load certificates and maybe keys from a number of files. Has the end goal - of returning a CFArray containing one SecIdentityRef, and then zero or more - SecCertificateRef objects, suitable for use as a client certificate trust - chain. - """ - # Ok, the strategy. - # - # This relies on knowing that macOS will not give you a SecIdentityRef - # unless you have imported a key into a keychain. This is a somewhat - # artificial limitation of macOS (for example, it doesn't necessarily - # affect iOS), but there is nothing inside Security.framework that lets you - # get a SecIdentityRef without having a key in a keychain. - # - # So the policy here is we take all the files and iterate them in order. - # Each one will use SecItemImport to have one or more objects loaded from - # it. We will also point at a keychain that macOS can use to work with the - # private key. - # - # Once we have all the objects, we'll check what we actually have. If we - # already have a SecIdentityRef in hand, fab: we'll use that. Otherwise, - # we'll take the first certificate (which we assume to be our leaf) and - # ask the keychain to give us a SecIdentityRef with that cert's associated - # key. - # - # We'll then return a CFArray containing the trust chain: one - # SecIdentityRef and then zero-or-more SecCertificateRef objects. The - # responsibility for freeing this CFArray will be with the caller. This - # CFArray must remain alive for the entire connection, so in practice it - # will be stored with a single SSLSocket, along with the reference to the - # keychain. - certificates = [] - identities = [] - - # Filter out bad paths. - paths = (path for path in paths if path) - - try: - for file_path in paths: - new_identities, new_certs = _load_items_from_file(keychain, file_path) - identities.extend(new_identities) - certificates.extend(new_certs) - - # Ok, we have everything. The question is: do we have an identity? If - # not, we want to grab one from the first cert we have. - if not identities: - new_identity = Security.SecIdentityRef() - status = Security.SecIdentityCreateWithCertificate( - keychain, certificates[0], ctypes.byref(new_identity) - ) - _assert_no_error(status) - identities.append(new_identity) - - # We now want to release the original certificate, as we no longer - # need it. - CoreFoundation.CFRelease(certificates.pop(0)) - - # We now need to build a new CFArray that holds the trust chain. - trust_chain = CoreFoundation.CFArrayCreateMutable( - CoreFoundation.kCFAllocatorDefault, - 0, - ctypes.byref(CoreFoundation.kCFTypeArrayCallBacks), - ) - for item in itertools.chain(identities, certificates): - # ArrayAppendValue does a CFRetain on the item. That's fine, - # because the finally block will release our other refs to them. - CoreFoundation.CFArrayAppendValue(trust_chain, item) - - return trust_chain - finally: - for obj in itertools.chain(identities, certificates): - CoreFoundation.CFRelease(obj) - - -TLS_PROTOCOL_VERSIONS = { - "SSLv2": (0, 2), - "SSLv3": (3, 0), - "TLSv1": (3, 1), - "TLSv1.1": (3, 2), - "TLSv1.2": (3, 3), -} - - -def _build_tls_unknown_ca_alert(version): - """ - Builds a TLS alert record for an unknown CA. - """ - ver_maj, ver_min = TLS_PROTOCOL_VERSIONS[version] - severity_fatal = 0x02 - description_unknown_ca = 0x30 - msg = struct.pack(">BB", severity_fatal, description_unknown_ca) - msg_len = len(msg) - record_type_alert = 0x15 - record = struct.pack(">BBBH", record_type_alert, ver_maj, ver_min, msg_len) + msg - return record diff --git a/apps/bitwarden_event_logs/lib/urllib3/contrib/appengine.py b/apps/bitwarden_event_logs/lib/urllib3/contrib/appengine.py deleted file mode 100755 index a5a6d910..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/contrib/appengine.py +++ /dev/null @@ -1,314 +0,0 @@ -""" -This module provides a pool manager that uses Google App Engine's -`URLFetch Service `_. - -Example usage:: - - from urllib3 import PoolManager - from urllib3.contrib.appengine import AppEngineManager, is_appengine_sandbox - - if is_appengine_sandbox(): - # AppEngineManager uses AppEngine's URLFetch API behind the scenes - http = AppEngineManager() - else: - # PoolManager uses a socket-level API behind the scenes - http = PoolManager() - - r = http.request('GET', 'https://google.com/') - -There are `limitations `_ to the URLFetch service and it may not be -the best choice for your application. There are three options for using -urllib3 on Google App Engine: - -1. You can use :class:`AppEngineManager` with URLFetch. URLFetch is - cost-effective in many circumstances as long as your usage is within the - limitations. -2. You can use a normal :class:`~urllib3.PoolManager` by enabling sockets. - Sockets also have `limitations and restrictions - `_ and have a lower free quota than URLFetch. - To use sockets, be sure to specify the following in your ``app.yaml``:: - - env_variables: - GAE_USE_SOCKETS_HTTPLIB : 'true' - -3. If you are using `App Engine Flexible -`_, you can use the standard -:class:`PoolManager` without any configuration or special environment variables. -""" - -from __future__ import absolute_import - -import io -import logging -import warnings - -from ..exceptions import ( - HTTPError, - HTTPWarning, - MaxRetryError, - ProtocolError, - SSLError, - TimeoutError, -) -from ..packages.six.moves.urllib.parse import urljoin -from ..request import RequestMethods -from ..response import HTTPResponse -from ..util.retry import Retry -from ..util.timeout import Timeout -from . import _appengine_environ - -try: - from google.appengine.api import urlfetch -except ImportError: - urlfetch = None - - -log = logging.getLogger(__name__) - - -class AppEnginePlatformWarning(HTTPWarning): - pass - - -class AppEnginePlatformError(HTTPError): - pass - - -class AppEngineManager(RequestMethods): - """ - Connection manager for Google App Engine sandbox applications. - - This manager uses the URLFetch service directly instead of using the - emulated httplib, and is subject to URLFetch limitations as described in - the App Engine documentation `here - `_. - - Notably it will raise an :class:`AppEnginePlatformError` if: - * URLFetch is not available. - * If you attempt to use this on App Engine Flexible, as full socket - support is available. - * If a request size is more than 10 megabytes. - * If a response size is more than 32 megabytes. - * If you use an unsupported request method such as OPTIONS. - - Beyond those cases, it will raise normal urllib3 errors. - """ - - def __init__( - self, - headers=None, - retries=None, - validate_certificate=True, - urlfetch_retries=True, - ): - if not urlfetch: - raise AppEnginePlatformError( - "URLFetch is not available in this environment." - ) - - warnings.warn( - "urllib3 is using URLFetch on Google App Engine sandbox instead " - "of sockets. To use sockets directly instead of URLFetch see " - "https://urllib3.readthedocs.io/en/1.26.x/reference/urllib3.contrib.html.", - AppEnginePlatformWarning, - ) - - RequestMethods.__init__(self, headers) - self.validate_certificate = validate_certificate - self.urlfetch_retries = urlfetch_retries - - self.retries = retries or Retry.DEFAULT - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - # Return False to re-raise any potential exceptions - return False - - def urlopen( - self, - method, - url, - body=None, - headers=None, - retries=None, - redirect=True, - timeout=Timeout.DEFAULT_TIMEOUT, - **response_kw - ): - - retries = self._get_retries(retries, redirect) - - try: - follow_redirects = redirect and retries.redirect != 0 and retries.total - response = urlfetch.fetch( - url, - payload=body, - method=method, - headers=headers or {}, - allow_truncated=False, - follow_redirects=self.urlfetch_retries and follow_redirects, - deadline=self._get_absolute_timeout(timeout), - validate_certificate=self.validate_certificate, - ) - except urlfetch.DeadlineExceededError as e: - raise TimeoutError(self, e) - - except urlfetch.InvalidURLError as e: - if "too large" in str(e): - raise AppEnginePlatformError( - "URLFetch request too large, URLFetch only " - "supports requests up to 10mb in size.", - e, - ) - raise ProtocolError(e) - - except urlfetch.DownloadError as e: - if "Too many redirects" in str(e): - raise MaxRetryError(self, url, reason=e) - raise ProtocolError(e) - - except urlfetch.ResponseTooLargeError as e: - raise AppEnginePlatformError( - "URLFetch response too large, URLFetch only supports" - "responses up to 32mb in size.", - e, - ) - - except urlfetch.SSLCertificateError as e: - raise SSLError(e) - - except urlfetch.InvalidMethodError as e: - raise AppEnginePlatformError( - "URLFetch does not support method: %s" % method, e - ) - - http_response = self._urlfetch_response_to_http_response( - response, retries=retries, **response_kw - ) - - # Handle redirect? - redirect_location = redirect and http_response.get_redirect_location() - if redirect_location: - # Check for redirect response - if self.urlfetch_retries and retries.raise_on_redirect: - raise MaxRetryError(self, url, "too many redirects") - else: - if http_response.status == 303: - method = "GET" - - try: - retries = retries.increment( - method, url, response=http_response, _pool=self - ) - except MaxRetryError: - if retries.raise_on_redirect: - raise MaxRetryError(self, url, "too many redirects") - return http_response - - retries.sleep_for_retry(http_response) - log.debug("Redirecting %s -> %s", url, redirect_location) - redirect_url = urljoin(url, redirect_location) - return self.urlopen( - method, - redirect_url, - body, - headers, - retries=retries, - redirect=redirect, - timeout=timeout, - **response_kw - ) - - # Check if we should retry the HTTP response. - has_retry_after = bool(http_response.headers.get("Retry-After")) - if retries.is_retry(method, http_response.status, has_retry_after): - retries = retries.increment(method, url, response=http_response, _pool=self) - log.debug("Retry: %s", url) - retries.sleep(http_response) - return self.urlopen( - method, - url, - body=body, - headers=headers, - retries=retries, - redirect=redirect, - timeout=timeout, - **response_kw - ) - - return http_response - - def _urlfetch_response_to_http_response(self, urlfetch_resp, **response_kw): - - if is_prod_appengine(): - # Production GAE handles deflate encoding automatically, but does - # not remove the encoding header. - content_encoding = urlfetch_resp.headers.get("content-encoding") - - if content_encoding == "deflate": - del urlfetch_resp.headers["content-encoding"] - - transfer_encoding = urlfetch_resp.headers.get("transfer-encoding") - # We have a full response's content, - # so let's make sure we don't report ourselves as chunked data. - if transfer_encoding == "chunked": - encodings = transfer_encoding.split(",") - encodings.remove("chunked") - urlfetch_resp.headers["transfer-encoding"] = ",".join(encodings) - - original_response = HTTPResponse( - # In order for decoding to work, we must present the content as - # a file-like object. - body=io.BytesIO(urlfetch_resp.content), - msg=urlfetch_resp.header_msg, - headers=urlfetch_resp.headers, - status=urlfetch_resp.status_code, - **response_kw - ) - - return HTTPResponse( - body=io.BytesIO(urlfetch_resp.content), - headers=urlfetch_resp.headers, - status=urlfetch_resp.status_code, - original_response=original_response, - **response_kw - ) - - def _get_absolute_timeout(self, timeout): - if timeout is Timeout.DEFAULT_TIMEOUT: - return None # Defer to URLFetch's default. - if isinstance(timeout, Timeout): - if timeout._read is not None or timeout._connect is not None: - warnings.warn( - "URLFetch does not support granular timeout settings, " - "reverting to total or default URLFetch timeout.", - AppEnginePlatformWarning, - ) - return timeout.total - return timeout - - def _get_retries(self, retries, redirect): - if not isinstance(retries, Retry): - retries = Retry.from_int(retries, redirect=redirect, default=self.retries) - - if retries.connect or retries.read or retries.redirect: - warnings.warn( - "URLFetch only supports total retries and does not " - "recognize connect, read, or redirect retry parameters.", - AppEnginePlatformWarning, - ) - - return retries - - -# Alias methods from _appengine_environ to maintain public API interface. - -is_appengine = _appengine_environ.is_appengine -is_appengine_sandbox = _appengine_environ.is_appengine_sandbox -is_local_appengine = _appengine_environ.is_local_appengine -is_prod_appengine = _appengine_environ.is_prod_appengine -is_prod_appengine_mvms = _appengine_environ.is_prod_appengine_mvms diff --git a/apps/bitwarden_event_logs/lib/urllib3/contrib/ntlmpool.py b/apps/bitwarden_event_logs/lib/urllib3/contrib/ntlmpool.py deleted file mode 100755 index 47166575..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/contrib/ntlmpool.py +++ /dev/null @@ -1,130 +0,0 @@ -""" -NTLM authenticating pool, contributed by erikcederstran - -Issue #10, see: http://code.google.com/p/urllib3/issues/detail?id=10 -""" -from __future__ import absolute_import - -import warnings -from logging import getLogger - -from ntlm import ntlm - -from .. import HTTPSConnectionPool -from ..packages.six.moves.http_client import HTTPSConnection - -warnings.warn( - "The 'urllib3.contrib.ntlmpool' module is deprecated and will be removed " - "in urllib3 v2.0 release, urllib3 is not able to support it properly due " - "to reasons listed in issue: https://github.com/urllib3/urllib3/issues/2282. " - "If you are a user of this module please comment in the mentioned issue.", - DeprecationWarning, -) - -log = getLogger(__name__) - - -class NTLMConnectionPool(HTTPSConnectionPool): - """ - Implements an NTLM authentication version of an urllib3 connection pool - """ - - scheme = "https" - - def __init__(self, user, pw, authurl, *args, **kwargs): - """ - authurl is a random URL on the server that is protected by NTLM. - user is the Windows user, probably in the DOMAIN\\username format. - pw is the password for the user. - """ - super(NTLMConnectionPool, self).__init__(*args, **kwargs) - self.authurl = authurl - self.rawuser = user - user_parts = user.split("\\", 1) - self.domain = user_parts[0].upper() - self.user = user_parts[1] - self.pw = pw - - def _new_conn(self): - # Performs the NTLM handshake that secures the connection. The socket - # must be kept open while requests are performed. - self.num_connections += 1 - log.debug( - "Starting NTLM HTTPS connection no. %d: https://%s%s", - self.num_connections, - self.host, - self.authurl, - ) - - headers = {"Connection": "Keep-Alive"} - req_header = "Authorization" - resp_header = "www-authenticate" - - conn = HTTPSConnection(host=self.host, port=self.port) - - # Send negotiation message - headers[req_header] = "NTLM %s" % ntlm.create_NTLM_NEGOTIATE_MESSAGE( - self.rawuser - ) - log.debug("Request headers: %s", headers) - conn.request("GET", self.authurl, None, headers) - res = conn.getresponse() - reshdr = dict(res.headers) - log.debug("Response status: %s %s", res.status, res.reason) - log.debug("Response headers: %s", reshdr) - log.debug("Response data: %s [...]", res.read(100)) - - # Remove the reference to the socket, so that it can not be closed by - # the response object (we want to keep the socket open) - res.fp = None - - # Server should respond with a challenge message - auth_header_values = reshdr[resp_header].split(", ") - auth_header_value = None - for s in auth_header_values: - if s[:5] == "NTLM ": - auth_header_value = s[5:] - if auth_header_value is None: - raise Exception( - "Unexpected %s response header: %s" % (resp_header, reshdr[resp_header]) - ) - - # Send authentication message - ServerChallenge, NegotiateFlags = ntlm.parse_NTLM_CHALLENGE_MESSAGE( - auth_header_value - ) - auth_msg = ntlm.create_NTLM_AUTHENTICATE_MESSAGE( - ServerChallenge, self.user, self.domain, self.pw, NegotiateFlags - ) - headers[req_header] = "NTLM %s" % auth_msg - log.debug("Request headers: %s", headers) - conn.request("GET", self.authurl, None, headers) - res = conn.getresponse() - log.debug("Response status: %s %s", res.status, res.reason) - log.debug("Response headers: %s", dict(res.headers)) - log.debug("Response data: %s [...]", res.read()[:100]) - if res.status != 200: - if res.status == 401: - raise Exception("Server rejected request: wrong username or password") - raise Exception("Wrong server response: %s %s" % (res.status, res.reason)) - - res.fp = None - log.debug("Connection established") - return conn - - def urlopen( - self, - method, - url, - body=None, - headers=None, - retries=3, - redirect=True, - assert_same_host=True, - ): - if headers is None: - headers = {} - headers["Connection"] = "Keep-Alive" - return super(NTLMConnectionPool, self).urlopen( - method, url, body, headers, retries, redirect, assert_same_host - ) diff --git a/apps/bitwarden_event_logs/lib/urllib3/contrib/pyopenssl.py b/apps/bitwarden_event_logs/lib/urllib3/contrib/pyopenssl.py deleted file mode 100755 index 1ed214b1..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/contrib/pyopenssl.py +++ /dev/null @@ -1,518 +0,0 @@ -""" -TLS with SNI_-support for Python 2. Follow these instructions if you would -like to verify TLS certificates in Python 2. Note, the default libraries do -*not* do certificate checking; you need to do additional work to validate -certificates yourself. - -This needs the following packages installed: - -* `pyOpenSSL`_ (tested with 16.0.0) -* `cryptography`_ (minimum 1.3.4, from pyopenssl) -* `idna`_ (minimum 2.0, from cryptography) - -However, pyopenssl depends on cryptography, which depends on idna, so while we -use all three directly here we end up having relatively few packages required. - -You can install them with the following command: - -.. code-block:: bash - - $ python -m pip install pyopenssl cryptography idna - -To activate certificate checking, call -:func:`~urllib3.contrib.pyopenssl.inject_into_urllib3` from your Python code -before you begin making HTTP requests. This can be done in a ``sitecustomize`` -module, or at any other time before your application begins using ``urllib3``, -like this: - -.. code-block:: python - - try: - import urllib3.contrib.pyopenssl - urllib3.contrib.pyopenssl.inject_into_urllib3() - except ImportError: - pass - -Now you can use :mod:`urllib3` as you normally would, and it will support SNI -when the required modules are installed. - -Activating this module also has the positive side effect of disabling SSL/TLS -compression in Python 2 (see `CRIME attack`_). - -.. _sni: https://en.wikipedia.org/wiki/Server_Name_Indication -.. _crime attack: https://en.wikipedia.org/wiki/CRIME_(security_exploit) -.. _pyopenssl: https://www.pyopenssl.org -.. _cryptography: https://cryptography.io -.. _idna: https://github.com/kjd/idna -""" -from __future__ import absolute_import - -import OpenSSL.crypto -import OpenSSL.SSL -from cryptography import x509 -from cryptography.hazmat.backends.openssl import backend as openssl_backend - -try: - from cryptography.x509 import UnsupportedExtension -except ImportError: - # UnsupportedExtension is gone in cryptography >= 2.1.0 - class UnsupportedExtension(Exception): - pass - - -from io import BytesIO -from socket import error as SocketError -from socket import timeout - -try: # Platform-specific: Python 2 - from socket import _fileobject -except ImportError: # Platform-specific: Python 3 - _fileobject = None - from ..packages.backports.makefile import backport_makefile - -import logging -import ssl -import sys -import warnings - -from .. import util -from ..packages import six -from ..util.ssl_ import PROTOCOL_TLS_CLIENT - -warnings.warn( - "'urllib3.contrib.pyopenssl' module is deprecated and will be removed " - "in a future release of urllib3 2.x. Read more in this issue: " - "https://github.com/urllib3/urllib3/issues/2680", - category=DeprecationWarning, - stacklevel=2, -) - -__all__ = ["inject_into_urllib3", "extract_from_urllib3"] - -# SNI always works. -HAS_SNI = True - -# Map from urllib3 to PyOpenSSL compatible parameter-values. -_openssl_versions = { - util.PROTOCOL_TLS: OpenSSL.SSL.SSLv23_METHOD, - PROTOCOL_TLS_CLIENT: OpenSSL.SSL.SSLv23_METHOD, - ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD, -} - -if hasattr(ssl, "PROTOCOL_SSLv3") and hasattr(OpenSSL.SSL, "SSLv3_METHOD"): - _openssl_versions[ssl.PROTOCOL_SSLv3] = OpenSSL.SSL.SSLv3_METHOD - -if hasattr(ssl, "PROTOCOL_TLSv1_1") and hasattr(OpenSSL.SSL, "TLSv1_1_METHOD"): - _openssl_versions[ssl.PROTOCOL_TLSv1_1] = OpenSSL.SSL.TLSv1_1_METHOD - -if hasattr(ssl, "PROTOCOL_TLSv1_2") and hasattr(OpenSSL.SSL, "TLSv1_2_METHOD"): - _openssl_versions[ssl.PROTOCOL_TLSv1_2] = OpenSSL.SSL.TLSv1_2_METHOD - - -_stdlib_to_openssl_verify = { - ssl.CERT_NONE: OpenSSL.SSL.VERIFY_NONE, - ssl.CERT_OPTIONAL: OpenSSL.SSL.VERIFY_PEER, - ssl.CERT_REQUIRED: OpenSSL.SSL.VERIFY_PEER - + OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT, -} -_openssl_to_stdlib_verify = dict((v, k) for k, v in _stdlib_to_openssl_verify.items()) - -# OpenSSL will only write 16K at a time -SSL_WRITE_BLOCKSIZE = 16384 - -orig_util_HAS_SNI = util.HAS_SNI -orig_util_SSLContext = util.ssl_.SSLContext - - -log = logging.getLogger(__name__) - - -def inject_into_urllib3(): - "Monkey-patch urllib3 with PyOpenSSL-backed SSL-support." - - _validate_dependencies_met() - - util.SSLContext = PyOpenSSLContext - util.ssl_.SSLContext = PyOpenSSLContext - util.HAS_SNI = HAS_SNI - util.ssl_.HAS_SNI = HAS_SNI - util.IS_PYOPENSSL = True - util.ssl_.IS_PYOPENSSL = True - - -def extract_from_urllib3(): - "Undo monkey-patching by :func:`inject_into_urllib3`." - - util.SSLContext = orig_util_SSLContext - util.ssl_.SSLContext = orig_util_SSLContext - util.HAS_SNI = orig_util_HAS_SNI - util.ssl_.HAS_SNI = orig_util_HAS_SNI - util.IS_PYOPENSSL = False - util.ssl_.IS_PYOPENSSL = False - - -def _validate_dependencies_met(): - """ - Verifies that PyOpenSSL's package-level dependencies have been met. - Throws `ImportError` if they are not met. - """ - # Method added in `cryptography==1.1`; not available in older versions - from cryptography.x509.extensions import Extensions - - if getattr(Extensions, "get_extension_for_class", None) is None: - raise ImportError( - "'cryptography' module missing required functionality. " - "Try upgrading to v1.3.4 or newer." - ) - - # pyOpenSSL 0.14 and above use cryptography for OpenSSL bindings. The _x509 - # attribute is only present on those versions. - from OpenSSL.crypto import X509 - - x509 = X509() - if getattr(x509, "_x509", None) is None: - raise ImportError( - "'pyOpenSSL' module missing required functionality. " - "Try upgrading to v0.14 or newer." - ) - - -def _dnsname_to_stdlib(name): - """ - Converts a dNSName SubjectAlternativeName field to the form used by the - standard library on the given Python version. - - Cryptography produces a dNSName as a unicode string that was idna-decoded - from ASCII bytes. We need to idna-encode that string to get it back, and - then on Python 3 we also need to convert to unicode via UTF-8 (the stdlib - uses PyUnicode_FromStringAndSize on it, which decodes via UTF-8). - - If the name cannot be idna-encoded then we return None signalling that - the name given should be skipped. - """ - - def idna_encode(name): - """ - Borrowed wholesale from the Python Cryptography Project. It turns out - that we can't just safely call `idna.encode`: it can explode for - wildcard names. This avoids that problem. - """ - import idna - - try: - for prefix in [u"*.", u"."]: - if name.startswith(prefix): - name = name[len(prefix) :] - return prefix.encode("ascii") + idna.encode(name) - return idna.encode(name) - except idna.core.IDNAError: - return None - - # Don't send IPv6 addresses through the IDNA encoder. - if ":" in name: - return name - - name = idna_encode(name) - if name is None: - return None - elif sys.version_info >= (3, 0): - name = name.decode("utf-8") - return name - - -def get_subj_alt_name(peer_cert): - """ - Given an PyOpenSSL certificate, provides all the subject alternative names. - """ - # Pass the cert to cryptography, which has much better APIs for this. - if hasattr(peer_cert, "to_cryptography"): - cert = peer_cert.to_cryptography() - else: - der = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, peer_cert) - cert = x509.load_der_x509_certificate(der, openssl_backend) - - # We want to find the SAN extension. Ask Cryptography to locate it (it's - # faster than looping in Python) - try: - ext = cert.extensions.get_extension_for_class(x509.SubjectAlternativeName).value - except x509.ExtensionNotFound: - # No such extension, return the empty list. - return [] - except ( - x509.DuplicateExtension, - UnsupportedExtension, - x509.UnsupportedGeneralNameType, - UnicodeError, - ) as e: - # A problem has been found with the quality of the certificate. Assume - # no SAN field is present. - log.warning( - "A problem was encountered with the certificate that prevented " - "urllib3 from finding the SubjectAlternativeName field. This can " - "affect certificate validation. The error was %s", - e, - ) - return [] - - # We want to return dNSName and iPAddress fields. We need to cast the IPs - # back to strings because the match_hostname function wants them as - # strings. - # Sadly the DNS names need to be idna encoded and then, on Python 3, UTF-8 - # decoded. This is pretty frustrating, but that's what the standard library - # does with certificates, and so we need to attempt to do the same. - # We also want to skip over names which cannot be idna encoded. - names = [ - ("DNS", name) - for name in map(_dnsname_to_stdlib, ext.get_values_for_type(x509.DNSName)) - if name is not None - ] - names.extend( - ("IP Address", str(name)) for name in ext.get_values_for_type(x509.IPAddress) - ) - - return names - - -class WrappedSocket(object): - """API-compatibility wrapper for Python OpenSSL's Connection-class. - - Note: _makefile_refs, _drop() and _reuse() are needed for the garbage - collector of pypy. - """ - - def __init__(self, connection, socket, suppress_ragged_eofs=True): - self.connection = connection - self.socket = socket - self.suppress_ragged_eofs = suppress_ragged_eofs - self._makefile_refs = 0 - self._closed = False - - def fileno(self): - return self.socket.fileno() - - # Copy-pasted from Python 3.5 source code - def _decref_socketios(self): - if self._makefile_refs > 0: - self._makefile_refs -= 1 - if self._closed: - self.close() - - def recv(self, *args, **kwargs): - try: - data = self.connection.recv(*args, **kwargs) - except OpenSSL.SSL.SysCallError as e: - if self.suppress_ragged_eofs and e.args == (-1, "Unexpected EOF"): - return b"" - else: - raise SocketError(str(e)) - except OpenSSL.SSL.ZeroReturnError: - if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: - return b"" - else: - raise - except OpenSSL.SSL.WantReadError: - if not util.wait_for_read(self.socket, self.socket.gettimeout()): - raise timeout("The read operation timed out") - else: - return self.recv(*args, **kwargs) - - # TLS 1.3 post-handshake authentication - except OpenSSL.SSL.Error as e: - raise ssl.SSLError("read error: %r" % e) - else: - return data - - def recv_into(self, *args, **kwargs): - try: - return self.connection.recv_into(*args, **kwargs) - except OpenSSL.SSL.SysCallError as e: - if self.suppress_ragged_eofs and e.args == (-1, "Unexpected EOF"): - return 0 - else: - raise SocketError(str(e)) - except OpenSSL.SSL.ZeroReturnError: - if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: - return 0 - else: - raise - except OpenSSL.SSL.WantReadError: - if not util.wait_for_read(self.socket, self.socket.gettimeout()): - raise timeout("The read operation timed out") - else: - return self.recv_into(*args, **kwargs) - - # TLS 1.3 post-handshake authentication - except OpenSSL.SSL.Error as e: - raise ssl.SSLError("read error: %r" % e) - - def settimeout(self, timeout): - return self.socket.settimeout(timeout) - - def _send_until_done(self, data): - while True: - try: - return self.connection.send(data) - except OpenSSL.SSL.WantWriteError: - if not util.wait_for_write(self.socket, self.socket.gettimeout()): - raise timeout() - continue - except OpenSSL.SSL.SysCallError as e: - raise SocketError(str(e)) - - def sendall(self, data): - total_sent = 0 - while total_sent < len(data): - sent = self._send_until_done( - data[total_sent : total_sent + SSL_WRITE_BLOCKSIZE] - ) - total_sent += sent - - def shutdown(self): - # FIXME rethrow compatible exceptions should we ever use this - self.connection.shutdown() - - def close(self): - if self._makefile_refs < 1: - try: - self._closed = True - return self.connection.close() - except OpenSSL.SSL.Error: - return - else: - self._makefile_refs -= 1 - - def getpeercert(self, binary_form=False): - x509 = self.connection.get_peer_certificate() - - if not x509: - return x509 - - if binary_form: - return OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, x509) - - return { - "subject": ((("commonName", x509.get_subject().CN),),), - "subjectAltName": get_subj_alt_name(x509), - } - - def version(self): - return self.connection.get_protocol_version_name() - - def _reuse(self): - self._makefile_refs += 1 - - def _drop(self): - if self._makefile_refs < 1: - self.close() - else: - self._makefile_refs -= 1 - - -if _fileobject: # Platform-specific: Python 2 - - def makefile(self, mode, bufsize=-1): - self._makefile_refs += 1 - return _fileobject(self, mode, bufsize, close=True) - -else: # Platform-specific: Python 3 - makefile = backport_makefile - -WrappedSocket.makefile = makefile - - -class PyOpenSSLContext(object): - """ - I am a wrapper class for the PyOpenSSL ``Context`` object. I am responsible - for translating the interface of the standard library ``SSLContext`` object - to calls into PyOpenSSL. - """ - - def __init__(self, protocol): - self.protocol = _openssl_versions[protocol] - self._ctx = OpenSSL.SSL.Context(self.protocol) - self._options = 0 - self.check_hostname = False - - @property - def options(self): - return self._options - - @options.setter - def options(self, value): - self._options = value - self._ctx.set_options(value) - - @property - def verify_mode(self): - return _openssl_to_stdlib_verify[self._ctx.get_verify_mode()] - - @verify_mode.setter - def verify_mode(self, value): - self._ctx.set_verify(_stdlib_to_openssl_verify[value], _verify_callback) - - def set_default_verify_paths(self): - self._ctx.set_default_verify_paths() - - def set_ciphers(self, ciphers): - if isinstance(ciphers, six.text_type): - ciphers = ciphers.encode("utf-8") - self._ctx.set_cipher_list(ciphers) - - def load_verify_locations(self, cafile=None, capath=None, cadata=None): - if cafile is not None: - cafile = cafile.encode("utf-8") - if capath is not None: - capath = capath.encode("utf-8") - try: - self._ctx.load_verify_locations(cafile, capath) - if cadata is not None: - self._ctx.load_verify_locations(BytesIO(cadata)) - except OpenSSL.SSL.Error as e: - raise ssl.SSLError("unable to load trusted certificates: %r" % e) - - def load_cert_chain(self, certfile, keyfile=None, password=None): - self._ctx.use_certificate_chain_file(certfile) - if password is not None: - if not isinstance(password, six.binary_type): - password = password.encode("utf-8") - self._ctx.set_passwd_cb(lambda *_: password) - self._ctx.use_privatekey_file(keyfile or certfile) - - def set_alpn_protocols(self, protocols): - protocols = [six.ensure_binary(p) for p in protocols] - return self._ctx.set_alpn_protos(protocols) - - def wrap_socket( - self, - sock, - server_side=False, - do_handshake_on_connect=True, - suppress_ragged_eofs=True, - server_hostname=None, - ): - cnx = OpenSSL.SSL.Connection(self._ctx, sock) - - if isinstance(server_hostname, six.text_type): # Platform-specific: Python 3 - server_hostname = server_hostname.encode("utf-8") - - if server_hostname is not None: - cnx.set_tlsext_host_name(server_hostname) - - cnx.set_connect_state() - - while True: - try: - cnx.do_handshake() - except OpenSSL.SSL.WantReadError: - if not util.wait_for_read(sock, sock.gettimeout()): - raise timeout("select timed out") - continue - except OpenSSL.SSL.Error as e: - raise ssl.SSLError("bad handshake: %r" % e) - break - - return WrappedSocket(cnx, sock) - - -def _verify_callback(cnx, x509, err_no, err_depth, return_code): - return err_no == 0 diff --git a/apps/bitwarden_event_logs/lib/urllib3/contrib/securetransport.py b/apps/bitwarden_event_logs/lib/urllib3/contrib/securetransport.py deleted file mode 100755 index e311c0c8..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/contrib/securetransport.py +++ /dev/null @@ -1,920 +0,0 @@ -""" -SecureTranport support for urllib3 via ctypes. - -This makes platform-native TLS available to urllib3 users on macOS without the -use of a compiler. This is an important feature because the Python Package -Index is moving to become a TLSv1.2-or-higher server, and the default OpenSSL -that ships with macOS is not capable of doing TLSv1.2. The only way to resolve -this is to give macOS users an alternative solution to the problem, and that -solution is to use SecureTransport. - -We use ctypes here because this solution must not require a compiler. That's -because pip is not allowed to require a compiler either. - -This is not intended to be a seriously long-term solution to this problem. -The hope is that PEP 543 will eventually solve this issue for us, at which -point we can retire this contrib module. But in the short term, we need to -solve the impending tire fire that is Python on Mac without this kind of -contrib module. So...here we are. - -To use this module, simply import and inject it:: - - import urllib3.contrib.securetransport - urllib3.contrib.securetransport.inject_into_urllib3() - -Happy TLSing! - -This code is a bastardised version of the code found in Will Bond's oscrypto -library. An enormous debt is owed to him for blazing this trail for us. For -that reason, this code should be considered to be covered both by urllib3's -license and by oscrypto's: - -.. code-block:: - - Copyright (c) 2015-2016 Will Bond - - 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 absolute_import - -import contextlib -import ctypes -import errno -import os.path -import shutil -import socket -import ssl -import struct -import threading -import weakref - -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 ( - _assert_no_error, - _build_tls_unknown_ca_alert, - _cert_array_from_pem, - _create_cfstring_array, - _load_client_cert_chain, - _temporary_keychain, -) - -try: # Platform-specific: Python 2 - from socket import _fileobject -except ImportError: # Platform-specific: Python 3 - _fileobject = None - from ..packages.backports.makefile import backport_makefile - -__all__ = ["inject_into_urllib3", "extract_from_urllib3"] - -# SNI always works -HAS_SNI = True - -orig_util_HAS_SNI = util.HAS_SNI -orig_util_SSLContext = util.ssl_.SSLContext - -# This dictionary is used by the read callback to obtain a handle to the -# calling wrapped socket. This is a pretty silly approach, but for now it'll -# do. I feel like I should be able to smuggle a handle to the wrapped socket -# directly in the SSLConnectionRef, but for now this approach will work I -# guess. -# -# We need to lock around this structure for inserts, but we don't do it for -# reads/writes in the callbacks. The reasoning here goes as follows: -# -# 1. It is not possible to call into the callbacks before the dictionary is -# populated, so once in the callback the id must be in the dictionary. -# 2. The callbacks don't mutate the dictionary, they only read from it, and -# so cannot conflict with any of the insertions. -# -# This is good: if we had to lock in the callbacks we'd drastically slow down -# the performance of this code. -_connection_refs = weakref.WeakValueDictionary() -_connection_ref_lock = threading.Lock() - -# Limit writes to 16kB. This is OpenSSL's limit, but we'll cargo-cult it over -# for no better reason than we need *a* limit, and this one is right there. -SSL_WRITE_BLOCKSIZE = 16384 - -# This is our equivalent of util.ssl_.DEFAULT_CIPHERS, but expanded out to -# individual cipher suites. We need to do this because this is how -# SecureTransport wants them. -CIPHER_SUITES = [ - SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - SecurityConst.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - SecurityConst.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - SecurityConst.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, - SecurityConst.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, - SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, - SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, - SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - SecurityConst.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, - SecurityConst.TLS_DHE_RSA_WITH_AES_256_CBC_SHA, - SecurityConst.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, - SecurityConst.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, - SecurityConst.TLS_AES_256_GCM_SHA384, - SecurityConst.TLS_AES_128_GCM_SHA256, - SecurityConst.TLS_RSA_WITH_AES_256_GCM_SHA384, - SecurityConst.TLS_RSA_WITH_AES_128_GCM_SHA256, - SecurityConst.TLS_AES_128_CCM_8_SHA256, - SecurityConst.TLS_AES_128_CCM_SHA256, - SecurityConst.TLS_RSA_WITH_AES_256_CBC_SHA256, - SecurityConst.TLS_RSA_WITH_AES_128_CBC_SHA256, - SecurityConst.TLS_RSA_WITH_AES_256_CBC_SHA, - SecurityConst.TLS_RSA_WITH_AES_128_CBC_SHA, -] - -# Basically this is simple: for PROTOCOL_SSLv23 we turn it into a low of -# TLSv1 and a high of TLSv1.2. For everything else, we pin to that version. -# TLSv1 to 1.2 are supported on macOS 10.8+ -_protocol_to_min_max = { - util.PROTOCOL_TLS: (SecurityConst.kTLSProtocol1, SecurityConst.kTLSProtocol12), - PROTOCOL_TLS_CLIENT: (SecurityConst.kTLSProtocol1, SecurityConst.kTLSProtocol12), -} - -if hasattr(ssl, "PROTOCOL_SSLv2"): - _protocol_to_min_max[ssl.PROTOCOL_SSLv2] = ( - SecurityConst.kSSLProtocol2, - SecurityConst.kSSLProtocol2, - ) -if hasattr(ssl, "PROTOCOL_SSLv3"): - _protocol_to_min_max[ssl.PROTOCOL_SSLv3] = ( - SecurityConst.kSSLProtocol3, - SecurityConst.kSSLProtocol3, - ) -if hasattr(ssl, "PROTOCOL_TLSv1"): - _protocol_to_min_max[ssl.PROTOCOL_TLSv1] = ( - SecurityConst.kTLSProtocol1, - SecurityConst.kTLSProtocol1, - ) -if hasattr(ssl, "PROTOCOL_TLSv1_1"): - _protocol_to_min_max[ssl.PROTOCOL_TLSv1_1] = ( - SecurityConst.kTLSProtocol11, - SecurityConst.kTLSProtocol11, - ) -if hasattr(ssl, "PROTOCOL_TLSv1_2"): - _protocol_to_min_max[ssl.PROTOCOL_TLSv1_2] = ( - SecurityConst.kTLSProtocol12, - SecurityConst.kTLSProtocol12, - ) - - -def inject_into_urllib3(): - """ - Monkey-patch urllib3 with SecureTransport-backed SSL-support. - """ - util.SSLContext = SecureTransportContext - util.ssl_.SSLContext = SecureTransportContext - util.HAS_SNI = HAS_SNI - util.ssl_.HAS_SNI = HAS_SNI - util.IS_SECURETRANSPORT = True - util.ssl_.IS_SECURETRANSPORT = True - - -def extract_from_urllib3(): - """ - Undo monkey-patching by :func:`inject_into_urllib3`. - """ - util.SSLContext = orig_util_SSLContext - util.ssl_.SSLContext = orig_util_SSLContext - util.HAS_SNI = orig_util_HAS_SNI - util.ssl_.HAS_SNI = orig_util_HAS_SNI - util.IS_SECURETRANSPORT = False - util.ssl_.IS_SECURETRANSPORT = False - - -def _read_callback(connection_id, data_buffer, data_length_pointer): - """ - SecureTransport read callback. This is called by ST to request that data - be returned from the socket. - """ - wrapped_socket = None - try: - wrapped_socket = _connection_refs.get(connection_id) - if wrapped_socket is None: - return SecurityConst.errSSLInternal - base_socket = wrapped_socket.socket - - requested_length = data_length_pointer[0] - - timeout = wrapped_socket.gettimeout() - error = None - read_count = 0 - - try: - while read_count < requested_length: - if timeout is None or timeout >= 0: - if not util.wait_for_read(base_socket, timeout): - raise socket.error(errno.EAGAIN, "timed out") - - remaining = requested_length - read_count - buffer = (ctypes.c_char * remaining).from_address( - data_buffer + read_count - ) - chunk_size = base_socket.recv_into(buffer, remaining) - read_count += chunk_size - if not chunk_size: - if not read_count: - return SecurityConst.errSSLClosedGraceful - break - except (socket.error) as e: - error = e.errno - - if error is not None and error != errno.EAGAIN: - data_length_pointer[0] = read_count - if error == errno.ECONNRESET or error == errno.EPIPE: - return SecurityConst.errSSLClosedAbort - raise - - data_length_pointer[0] = read_count - - if read_count != requested_length: - return SecurityConst.errSSLWouldBlock - - return 0 - except Exception as e: - if wrapped_socket is not None: - wrapped_socket._exception = e - return SecurityConst.errSSLInternal - - -def _write_callback(connection_id, data_buffer, data_length_pointer): - """ - SecureTransport write callback. This is called by ST to request that data - actually be sent on the network. - """ - wrapped_socket = None - try: - wrapped_socket = _connection_refs.get(connection_id) - if wrapped_socket is None: - return SecurityConst.errSSLInternal - base_socket = wrapped_socket.socket - - bytes_to_write = data_length_pointer[0] - data = ctypes.string_at(data_buffer, bytes_to_write) - - timeout = wrapped_socket.gettimeout() - error = None - sent = 0 - - try: - while sent < bytes_to_write: - if timeout is None or timeout >= 0: - if not util.wait_for_write(base_socket, timeout): - raise socket.error(errno.EAGAIN, "timed out") - chunk_sent = base_socket.send(data) - sent += chunk_sent - - # This has some needless copying here, but I'm not sure there's - # much value in optimising this data path. - data = data[chunk_sent:] - except (socket.error) as e: - error = e.errno - - if error is not None and error != errno.EAGAIN: - data_length_pointer[0] = sent - if error == errno.ECONNRESET or error == errno.EPIPE: - return SecurityConst.errSSLClosedAbort - raise - - data_length_pointer[0] = sent - - if sent != bytes_to_write: - return SecurityConst.errSSLWouldBlock - - return 0 - except Exception as e: - if wrapped_socket is not None: - wrapped_socket._exception = e - return SecurityConst.errSSLInternal - - -# We need to keep these two objects references alive: if they get GC'd while -# in use then SecureTransport could attempt to call a function that is in freed -# memory. That would be...uh...bad. Yeah, that's the word. Bad. -_read_callback_pointer = Security.SSLReadFunc(_read_callback) -_write_callback_pointer = Security.SSLWriteFunc(_write_callback) - - -class WrappedSocket(object): - """ - API-compatibility wrapper for Python's OpenSSL wrapped socket object. - - Note: _makefile_refs, _drop(), and _reuse() are needed for the garbage - collector of PyPy. - """ - - def __init__(self, socket): - self.socket = socket - self.context = None - self._makefile_refs = 0 - self._closed = False - self._exception = None - self._keychain = None - self._keychain_dir = None - self._client_cert_chain = None - - # We save off the previously-configured timeout and then set it to - # zero. This is done because we use select and friends to handle the - # timeouts, but if we leave the timeout set on the lower socket then - # Python will "kindly" call select on that socket again for us. Avoid - # that by forcing the timeout to zero. - self._timeout = self.socket.gettimeout() - self.socket.settimeout(0) - - @contextlib.contextmanager - def _raise_on_error(self): - """ - A context manager that can be used to wrap calls that do I/O from - SecureTransport. If any of the I/O callbacks hit an exception, this - context manager will correctly propagate the exception after the fact. - This avoids silently swallowing those exceptions. - - It also correctly forces the socket closed. - """ - self._exception = None - - # We explicitly don't catch around this yield because in the unlikely - # event that an exception was hit in the block we don't want to swallow - # it. - yield - if self._exception is not None: - exception, self._exception = self._exception, None - self.close() - raise exception - - def _set_ciphers(self): - """ - Sets up the allowed ciphers. By default this matches the set in - util.ssl_.DEFAULT_CIPHERS, at least as supported by macOS. This is done - custom and doesn't allow changing at this time, mostly because parsing - OpenSSL cipher strings is going to be a freaking nightmare. - """ - ciphers = (Security.SSLCipherSuite * len(CIPHER_SUITES))(*CIPHER_SUITES) - result = Security.SSLSetEnabledCiphers( - self.context, ciphers, len(CIPHER_SUITES) - ) - _assert_no_error(result) - - def _set_alpn_protocols(self, protocols): - """ - Sets up the ALPN protocols on the context. - """ - if not protocols: - return - protocols_arr = _create_cfstring_array(protocols) - try: - result = Security.SSLSetALPNProtocols(self.context, protocols_arr) - _assert_no_error(result) - finally: - CoreFoundation.CFRelease(protocols_arr) - - def _custom_validate(self, verify, trust_bundle): - """ - Called when we have set custom validation. We do this in two cases: - first, when cert validation is entirely disabled; and second, when - using a custom trust DB. - Raises an SSLError if the connection is not trusted. - """ - # If we disabled cert validation, just say: cool. - if not verify: - return - - successes = ( - SecurityConst.kSecTrustResultUnspecified, - SecurityConst.kSecTrustResultProceed, - ) - try: - trust_result = self._evaluate_trust(trust_bundle) - if trust_result in successes: - return - reason = "error code: %d" % (trust_result,) - except Exception as e: - # Do not trust on error - reason = "exception: %r" % (e,) - - # SecureTransport does not send an alert nor shuts down the connection. - rec = _build_tls_unknown_ca_alert(self.version()) - self.socket.sendall(rec) - # close the connection immediately - # l_onoff = 1, activate linger - # l_linger = 0, linger for 0 seoncds - opts = struct.pack("ii", 1, 0) - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, opts) - self.close() - raise ssl.SSLError("certificate verify failed, %s" % reason) - - def _evaluate_trust(self, trust_bundle): - # We want data in memory, so load it up. - if os.path.isfile(trust_bundle): - with open(trust_bundle, "rb") as f: - trust_bundle = f.read() - - cert_array = None - trust = Security.SecTrustRef() - - try: - # Get a CFArray that contains the certs we want. - cert_array = _cert_array_from_pem(trust_bundle) - - # Ok, now the hard part. We want to get the SecTrustRef that ST has - # created for this connection, shove our CAs into it, tell ST to - # ignore everything else it knows, and then ask if it can build a - # chain. This is a buuuunch of code. - result = Security.SSLCopyPeerTrust(self.context, ctypes.byref(trust)) - _assert_no_error(result) - if not trust: - raise ssl.SSLError("Failed to copy trust reference") - - result = Security.SecTrustSetAnchorCertificates(trust, cert_array) - _assert_no_error(result) - - result = Security.SecTrustSetAnchorCertificatesOnly(trust, True) - _assert_no_error(result) - - trust_result = Security.SecTrustResultType() - result = Security.SecTrustEvaluate(trust, ctypes.byref(trust_result)) - _assert_no_error(result) - finally: - if trust: - CoreFoundation.CFRelease(trust) - - if cert_array is not None: - CoreFoundation.CFRelease(cert_array) - - return trust_result.value - - def handshake( - self, - server_hostname, - verify, - trust_bundle, - min_version, - max_version, - client_cert, - client_key, - client_key_passphrase, - alpn_protocols, - ): - """ - Actually performs the TLS handshake. This is run automatically by - wrapped socket, and shouldn't be needed in user code. - """ - # First, we do the initial bits of connection setup. We need to create - # a context, set its I/O funcs, and set the connection reference. - self.context = Security.SSLCreateContext( - None, SecurityConst.kSSLClientSide, SecurityConst.kSSLStreamType - ) - result = Security.SSLSetIOFuncs( - self.context, _read_callback_pointer, _write_callback_pointer - ) - _assert_no_error(result) - - # Here we need to compute the handle to use. We do this by taking the - # id of self modulo 2**31 - 1. If this is already in the dictionary, we - # just keep incrementing by one until we find a free space. - with _connection_ref_lock: - handle = id(self) % 2147483647 - while handle in _connection_refs: - handle = (handle + 1) % 2147483647 - _connection_refs[handle] = self - - result = Security.SSLSetConnection(self.context, handle) - _assert_no_error(result) - - # If we have a server hostname, we should set that too. - if server_hostname: - if not isinstance(server_hostname, bytes): - server_hostname = server_hostname.encode("utf-8") - - result = Security.SSLSetPeerDomainName( - self.context, server_hostname, len(server_hostname) - ) - _assert_no_error(result) - - # Setup the ciphers. - self._set_ciphers() - - # Setup the ALPN protocols. - self._set_alpn_protocols(alpn_protocols) - - # Set the minimum and maximum TLS versions. - result = Security.SSLSetProtocolVersionMin(self.context, min_version) - _assert_no_error(result) - - result = Security.SSLSetProtocolVersionMax(self.context, max_version) - _assert_no_error(result) - - # If there's a trust DB, we need to use it. We do that by telling - # SecureTransport to break on server auth. We also do that if we don't - # want to validate the certs at all: we just won't actually do any - # authing in that case. - if not verify or trust_bundle is not None: - result = Security.SSLSetSessionOption( - self.context, SecurityConst.kSSLSessionOptionBreakOnServerAuth, True - ) - _assert_no_error(result) - - # If there's a client cert, we need to use it. - if client_cert: - self._keychain, self._keychain_dir = _temporary_keychain() - self._client_cert_chain = _load_client_cert_chain( - self._keychain, client_cert, client_key - ) - result = Security.SSLSetCertificate(self.context, self._client_cert_chain) - _assert_no_error(result) - - while True: - with self._raise_on_error(): - result = Security.SSLHandshake(self.context) - - if result == SecurityConst.errSSLWouldBlock: - raise socket.timeout("handshake timed out") - elif result == SecurityConst.errSSLServerAuthCompleted: - self._custom_validate(verify, trust_bundle) - continue - else: - _assert_no_error(result) - break - - def fileno(self): - return self.socket.fileno() - - # Copy-pasted from Python 3.5 source code - def _decref_socketios(self): - if self._makefile_refs > 0: - self._makefile_refs -= 1 - if self._closed: - self.close() - - def recv(self, bufsiz): - buffer = ctypes.create_string_buffer(bufsiz) - bytes_read = self.recv_into(buffer, bufsiz) - data = buffer[:bytes_read] - return data - - def recv_into(self, buffer, nbytes=None): - # Read short on EOF. - if self._closed: - return 0 - - if nbytes is None: - nbytes = len(buffer) - - buffer = (ctypes.c_char * nbytes).from_buffer(buffer) - processed_bytes = ctypes.c_size_t(0) - - with self._raise_on_error(): - result = Security.SSLRead( - self.context, buffer, nbytes, ctypes.byref(processed_bytes) - ) - - # There are some result codes that we want to treat as "not always - # errors". Specifically, those are errSSLWouldBlock, - # errSSLClosedGraceful, and errSSLClosedNoNotify. - if result == SecurityConst.errSSLWouldBlock: - # If we didn't process any bytes, then this was just a time out. - # However, we can get errSSLWouldBlock in situations when we *did* - # read some data, and in those cases we should just read "short" - # and return. - if processed_bytes.value == 0: - # Timed out, no data read. - raise socket.timeout("recv timed out") - elif result in ( - SecurityConst.errSSLClosedGraceful, - SecurityConst.errSSLClosedNoNotify, - ): - # The remote peer has closed this connection. We should do so as - # well. Note that we don't actually return here because in - # principle this could actually be fired along with return data. - # It's unlikely though. - self.close() - else: - _assert_no_error(result) - - # Ok, we read and probably succeeded. We should return whatever data - # was actually read. - return processed_bytes.value - - def settimeout(self, timeout): - self._timeout = timeout - - def gettimeout(self): - return self._timeout - - def send(self, data): - processed_bytes = ctypes.c_size_t(0) - - with self._raise_on_error(): - result = Security.SSLWrite( - self.context, data, len(data), ctypes.byref(processed_bytes) - ) - - if result == SecurityConst.errSSLWouldBlock and processed_bytes.value == 0: - # Timed out - raise socket.timeout("send timed out") - else: - _assert_no_error(result) - - # We sent, and probably succeeded. Tell them how much we sent. - return processed_bytes.value - - def sendall(self, data): - total_sent = 0 - while total_sent < len(data): - sent = self.send(data[total_sent : total_sent + SSL_WRITE_BLOCKSIZE]) - total_sent += sent - - def shutdown(self): - with self._raise_on_error(): - Security.SSLClose(self.context) - - def close(self): - # TODO: should I do clean shutdown here? Do I have to? - if self._makefile_refs < 1: - self._closed = True - if self.context: - CoreFoundation.CFRelease(self.context) - self.context = None - if self._client_cert_chain: - CoreFoundation.CFRelease(self._client_cert_chain) - self._client_cert_chain = None - if self._keychain: - Security.SecKeychainDelete(self._keychain) - CoreFoundation.CFRelease(self._keychain) - shutil.rmtree(self._keychain_dir) - self._keychain = self._keychain_dir = None - return self.socket.close() - else: - self._makefile_refs -= 1 - - def getpeercert(self, binary_form=False): - # Urgh, annoying. - # - # Here's how we do this: - # - # 1. Call SSLCopyPeerTrust to get hold of the trust object for this - # connection. - # 2. Call SecTrustGetCertificateAtIndex for index 0 to get the leaf. - # 3. To get the CN, call SecCertificateCopyCommonName and process that - # string so that it's of the appropriate type. - # 4. To get the SAN, we need to do something a bit more complex: - # a. Call SecCertificateCopyValues to get the data, requesting - # kSecOIDSubjectAltName. - # b. Mess about with this dictionary to try to get the SANs out. - # - # This is gross. Really gross. It's going to be a few hundred LoC extra - # just to repeat something that SecureTransport can *already do*. So my - # operating assumption at this time is that what we want to do is - # instead to just flag to urllib3 that it shouldn't do its own hostname - # validation when using SecureTransport. - if not binary_form: - raise ValueError("SecureTransport only supports dumping binary certs") - trust = Security.SecTrustRef() - certdata = None - der_bytes = None - - try: - # Grab the trust store. - result = Security.SSLCopyPeerTrust(self.context, ctypes.byref(trust)) - _assert_no_error(result) - if not trust: - # Probably we haven't done the handshake yet. No biggie. - return None - - cert_count = Security.SecTrustGetCertificateCount(trust) - if not cert_count: - # Also a case that might happen if we haven't handshaked. - # Handshook? Handshaken? - return None - - leaf = Security.SecTrustGetCertificateAtIndex(trust, 0) - assert leaf - - # Ok, now we want the DER bytes. - certdata = Security.SecCertificateCopyData(leaf) - assert certdata - - data_length = CoreFoundation.CFDataGetLength(certdata) - data_buffer = CoreFoundation.CFDataGetBytePtr(certdata) - der_bytes = ctypes.string_at(data_buffer, data_length) - finally: - if certdata: - CoreFoundation.CFRelease(certdata) - if trust: - CoreFoundation.CFRelease(trust) - - return der_bytes - - def version(self): - protocol = Security.SSLProtocol() - result = Security.SSLGetNegotiatedProtocolVersion( - self.context, ctypes.byref(protocol) - ) - _assert_no_error(result) - if protocol.value == SecurityConst.kTLSProtocol13: - raise ssl.SSLError("SecureTransport does not support TLS 1.3") - elif protocol.value == SecurityConst.kTLSProtocol12: - return "TLSv1.2" - elif protocol.value == SecurityConst.kTLSProtocol11: - return "TLSv1.1" - elif protocol.value == SecurityConst.kTLSProtocol1: - return "TLSv1" - elif protocol.value == SecurityConst.kSSLProtocol3: - return "SSLv3" - elif protocol.value == SecurityConst.kSSLProtocol2: - return "SSLv2" - else: - raise ssl.SSLError("Unknown TLS version: %r" % protocol) - - def _reuse(self): - self._makefile_refs += 1 - - def _drop(self): - if self._makefile_refs < 1: - self.close() - else: - self._makefile_refs -= 1 - - -if _fileobject: # Platform-specific: Python 2 - - def makefile(self, mode, bufsize=-1): - self._makefile_refs += 1 - return _fileobject(self, mode, bufsize, close=True) - -else: # Platform-specific: Python 3 - - def makefile(self, mode="r", buffering=None, *args, **kwargs): - # We disable buffering with SecureTransport because it conflicts with - # the buffering that ST does internally (see issue #1153 for more). - buffering = 0 - return backport_makefile(self, mode, buffering, *args, **kwargs) - - -WrappedSocket.makefile = makefile - - -class SecureTransportContext(object): - """ - I am a wrapper class for the SecureTransport library, to translate the - interface of the standard library ``SSLContext`` object to calls into - SecureTransport. - """ - - def __init__(self, protocol): - self._min_version, self._max_version = _protocol_to_min_max[protocol] - self._options = 0 - self._verify = False - self._trust_bundle = None - self._client_cert = None - self._client_key = None - self._client_key_passphrase = None - self._alpn_protocols = None - - @property - def check_hostname(self): - """ - SecureTransport cannot have its hostname checking disabled. For more, - see the comment on getpeercert() in this file. - """ - return True - - @check_hostname.setter - def check_hostname(self, value): - """ - SecureTransport cannot have its hostname checking disabled. For more, - see the comment on getpeercert() in this file. - """ - pass - - @property - def options(self): - # TODO: Well, crap. - # - # So this is the bit of the code that is the most likely to cause us - # trouble. Essentially we need to enumerate all of the SSL options that - # users might want to use and try to see if we can sensibly translate - # them, or whether we should just ignore them. - return self._options - - @options.setter - def options(self, value): - # TODO: Update in line with above. - self._options = value - - @property - def verify_mode(self): - return ssl.CERT_REQUIRED if self._verify else ssl.CERT_NONE - - @verify_mode.setter - def verify_mode(self, value): - self._verify = True if value == ssl.CERT_REQUIRED else False - - def set_default_verify_paths(self): - # So, this has to do something a bit weird. Specifically, what it does - # is nothing. - # - # This means that, if we had previously had load_verify_locations - # called, this does not undo that. We need to do that because it turns - # out that the rest of the urllib3 code will attempt to load the - # default verify paths if it hasn't been told about any paths, even if - # the context itself was sometime earlier. We resolve that by just - # ignoring it. - pass - - def load_default_certs(self): - return self.set_default_verify_paths() - - def set_ciphers(self, ciphers): - # For now, we just require the default cipher string. - if ciphers != util.ssl_.DEFAULT_CIPHERS: - raise ValueError("SecureTransport doesn't support custom cipher strings") - - def load_verify_locations(self, cafile=None, capath=None, cadata=None): - # OK, we only really support cadata and cafile. - if capath is not None: - raise ValueError("SecureTransport does not support cert directories") - - # Raise if cafile does not exist. - if cafile is not None: - with open(cafile): - pass - - self._trust_bundle = cafile or cadata - - def load_cert_chain(self, certfile, keyfile=None, password=None): - self._client_cert = certfile - self._client_key = keyfile - self._client_cert_passphrase = password - - def set_alpn_protocols(self, protocols): - """ - Sets the ALPN protocols that will later be set on the context. - - Raises a NotImplementedError if ALPN is not supported. - """ - if not hasattr(Security, "SSLSetALPNProtocols"): - raise NotImplementedError( - "SecureTransport supports ALPN only in macOS 10.12+" - ) - self._alpn_protocols = [six.ensure_binary(p) for p in protocols] - - def wrap_socket( - self, - sock, - server_side=False, - do_handshake_on_connect=True, - suppress_ragged_eofs=True, - server_hostname=None, - ): - # So, what do we do here? Firstly, we assert some properties. This is a - # stripped down shim, so there is some functionality we don't support. - # See PEP 543 for the real deal. - assert not server_side - assert do_handshake_on_connect - assert suppress_ragged_eofs - - # Ok, we're good to go. Now we want to create the wrapped socket object - # and store it in the appropriate place. - wrapped_socket = WrappedSocket(sock) - - # Now we can handshake - wrapped_socket.handshake( - server_hostname, - self._verify, - self._trust_bundle, - self._min_version, - self._max_version, - self._client_cert, - self._client_key, - self._client_key_passphrase, - self._alpn_protocols, - ) - return wrapped_socket diff --git a/apps/bitwarden_event_logs/lib/urllib3/contrib/socks.py b/apps/bitwarden_event_logs/lib/urllib3/contrib/socks.py deleted file mode 100755 index c326e80d..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/contrib/socks.py +++ /dev/null @@ -1,216 +0,0 @@ -# -*- coding: utf-8 -*- -""" -This module contains provisional support for SOCKS proxies from within -urllib3. This module supports SOCKS4, SOCKS4A (an extension of SOCKS4), and -SOCKS5. To enable its functionality, either install PySocks or install this -module with the ``socks`` extra. - -The SOCKS implementation supports the full range of urllib3 features. It also -supports the following SOCKS features: - -- SOCKS4A (``proxy_url='socks4a://...``) -- SOCKS4 (``proxy_url='socks4://...``) -- SOCKS5 with remote DNS (``proxy_url='socks5h://...``) -- SOCKS5 with local DNS (``proxy_url='socks5://...``) -- Usernames and passwords for the SOCKS proxy - -.. note:: - It is recommended to use ``socks5h://`` or ``socks4a://`` schemes in - your ``proxy_url`` to ensure that DNS resolution is done from the remote - server instead of client-side when connecting to a domain name. - -SOCKS4 supports IPv4 and domain names with the SOCKS4A extension. SOCKS5 -supports IPv4, IPv6, and domain names. - -When connecting to a SOCKS4 proxy the ``username`` portion of the ``proxy_url`` -will be sent as the ``userid`` section of the SOCKS request: - -.. code-block:: python - - proxy_url="socks4a://@proxy-host" - -When connecting to a SOCKS5 proxy the ``username`` and ``password`` portion -of the ``proxy_url`` will be sent as the username/password to authenticate -with the proxy: - -.. code-block:: python - - proxy_url="socks5h://:@proxy-host" - -""" -from __future__ import absolute_import - -try: - import socks -except ImportError: - import warnings - - from ..exceptions import DependencyWarning - - warnings.warn( - ( - "SOCKS support in urllib3 requires the installation of optional " - "dependencies: specifically, PySocks. For more information, see " - "https://urllib3.readthedocs.io/en/1.26.x/contrib.html#socks-proxies" - ), - DependencyWarning, - ) - raise - -from socket import error as SocketError -from socket import timeout as SocketTimeout - -from ..connection import HTTPConnection, HTTPSConnection -from ..connectionpool import HTTPConnectionPool, HTTPSConnectionPool -from ..exceptions import ConnectTimeoutError, NewConnectionError -from ..poolmanager import PoolManager -from ..util.url import parse_url - -try: - import ssl -except ImportError: - ssl = None - - -class SOCKSConnection(HTTPConnection): - """ - A plain-text HTTP connection that connects via a SOCKS proxy. - """ - - def __init__(self, *args, **kwargs): - self._socks_options = kwargs.pop("_socks_options") - super(SOCKSConnection, self).__init__(*args, **kwargs) - - def _new_conn(self): - """ - Establish a new connection via the SOCKS proxy. - """ - extra_kw = {} - if self.source_address: - extra_kw["source_address"] = self.source_address - - if self.socket_options: - extra_kw["socket_options"] = self.socket_options - - try: - conn = socks.create_connection( - (self.host, self.port), - proxy_type=self._socks_options["socks_version"], - proxy_addr=self._socks_options["proxy_host"], - proxy_port=self._socks_options["proxy_port"], - proxy_username=self._socks_options["username"], - proxy_password=self._socks_options["password"], - proxy_rdns=self._socks_options["rdns"], - timeout=self.timeout, - **extra_kw - ) - - except SocketTimeout: - raise ConnectTimeoutError( - self, - "Connection to %s timed out. (connect timeout=%s)" - % (self.host, self.timeout), - ) - - except socks.ProxyError as e: - # This is fragile as hell, but it seems to be the only way to raise - # useful errors here. - if e.socket_err: - error = e.socket_err - if isinstance(error, SocketTimeout): - raise ConnectTimeoutError( - self, - "Connection to %s timed out. (connect timeout=%s)" - % (self.host, self.timeout), - ) - else: - raise NewConnectionError( - self, "Failed to establish a new connection: %s" % error - ) - else: - raise NewConnectionError( - self, "Failed to establish a new connection: %s" % e - ) - - except SocketError as e: # Defensive: PySocks should catch all these. - raise NewConnectionError( - self, "Failed to establish a new connection: %s" % e - ) - - return conn - - -# We don't need to duplicate the Verified/Unverified distinction from -# urllib3/connection.py here because the HTTPSConnection will already have been -# correctly set to either the Verified or Unverified form by that module. This -# means the SOCKSHTTPSConnection will automatically be the correct type. -class SOCKSHTTPSConnection(SOCKSConnection, HTTPSConnection): - pass - - -class SOCKSHTTPConnectionPool(HTTPConnectionPool): - ConnectionCls = SOCKSConnection - - -class SOCKSHTTPSConnectionPool(HTTPSConnectionPool): - ConnectionCls = SOCKSHTTPSConnection - - -class SOCKSProxyManager(PoolManager): - """ - A version of the urllib3 ProxyManager that routes connections via the - defined SOCKS proxy. - """ - - pool_classes_by_scheme = { - "http": SOCKSHTTPConnectionPool, - "https": SOCKSHTTPSConnectionPool, - } - - def __init__( - self, - proxy_url, - username=None, - password=None, - num_pools=10, - headers=None, - **connection_pool_kw - ): - parsed = parse_url(proxy_url) - - if username is None and password is None and parsed.auth is not None: - split = parsed.auth.split(":") - if len(split) == 2: - username, password = split - if parsed.scheme == "socks5": - socks_version = socks.PROXY_TYPE_SOCKS5 - rdns = False - elif parsed.scheme == "socks5h": - socks_version = socks.PROXY_TYPE_SOCKS5 - rdns = True - elif parsed.scheme == "socks4": - socks_version = socks.PROXY_TYPE_SOCKS4 - rdns = False - elif parsed.scheme == "socks4a": - socks_version = socks.PROXY_TYPE_SOCKS4 - rdns = True - else: - raise ValueError("Unable to determine SOCKS version from %s" % proxy_url) - - self.proxy_url = proxy_url - - socks_options = { - "socks_version": socks_version, - "proxy_host": parsed.host, - "proxy_port": parsed.port, - "username": username, - "password": password, - "rdns": rdns, - } - connection_pool_kw["_socks_options"] = socks_options - - super(SOCKSProxyManager, self).__init__( - num_pools, headers, **connection_pool_kw - ) - - self.pool_classes_by_scheme = SOCKSProxyManager.pool_classes_by_scheme diff --git a/apps/bitwarden_event_logs/lib/urllib3/exceptions.py b/apps/bitwarden_event_logs/lib/urllib3/exceptions.py deleted file mode 100755 index cba6f3f5..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/exceptions.py +++ /dev/null @@ -1,323 +0,0 @@ -from __future__ import absolute_import - -from .packages.six.moves.http_client import IncompleteRead as httplib_IncompleteRead - -# Base Exceptions - - -class HTTPError(Exception): - """Base exception used by this module.""" - - pass - - -class HTTPWarning(Warning): - """Base warning used by this module.""" - - pass - - -class PoolError(HTTPError): - """Base exception for errors caused within a pool.""" - - def __init__(self, pool, message): - self.pool = pool - HTTPError.__init__(self, "%s: %s" % (pool, message)) - - def __reduce__(self): - # For pickling purposes. - return self.__class__, (None, None) - - -class RequestError(PoolError): - """Base exception for PoolErrors that have associated URLs.""" - - def __init__(self, pool, url, message): - self.url = url - PoolError.__init__(self, pool, message) - - def __reduce__(self): - # For pickling purposes. - return self.__class__, (None, self.url, None) - - -class SSLError(HTTPError): - """Raised when SSL certificate fails in an HTTPS connection.""" - - pass - - -class ProxyError(HTTPError): - """Raised when the connection to a proxy fails.""" - - def __init__(self, message, error, *args): - super(ProxyError, self).__init__(message, error, *args) - self.original_error = error - - -class DecodeError(HTTPError): - """Raised when automatic decoding based on Content-Type fails.""" - - pass - - -class ProtocolError(HTTPError): - """Raised when something unexpected happens mid-request/response.""" - - pass - - -#: Renamed to ProtocolError but aliased for backwards compatibility. -ConnectionError = ProtocolError - - -# Leaf Exceptions - - -class MaxRetryError(RequestError): - """Raised when the maximum number of retries is exceeded. - - :param pool: The connection pool - :type pool: :class:`~urllib3.connectionpool.HTTPConnectionPool` - :param string url: The requested Url - :param exceptions.Exception reason: The underlying error - - """ - - def __init__(self, pool, url, reason=None): - self.reason = reason - - message = "Max retries exceeded with url: %s (Caused by %r)" % (url, reason) - - RequestError.__init__(self, pool, url, message) - - -class HostChangedError(RequestError): - """Raised when an existing pool gets a request for a foreign host.""" - - def __init__(self, pool, url, retries=3): - message = "Tried to open a foreign host with url: %s" % url - RequestError.__init__(self, pool, url, message) - self.retries = retries - - -class TimeoutStateError(HTTPError): - """Raised when passing an invalid state to a timeout""" - - pass - - -class TimeoutError(HTTPError): - """Raised when a socket timeout error occurs. - - Catching this error will catch both :exc:`ReadTimeoutErrors - ` and :exc:`ConnectTimeoutErrors `. - """ - - pass - - -class ReadTimeoutError(TimeoutError, RequestError): - """Raised when a socket timeout occurs while receiving data from a server""" - - pass - - -# This timeout error does not have a URL attached and needs to inherit from the -# base HTTPError -class ConnectTimeoutError(TimeoutError): - """Raised when a socket timeout occurs while connecting to a server""" - - pass - - -class NewConnectionError(ConnectTimeoutError, PoolError): - """Raised when we fail to establish a new connection. Usually ECONNREFUSED.""" - - pass - - -class EmptyPoolError(PoolError): - """Raised when a pool runs out of connections and no more are allowed.""" - - pass - - -class ClosedPoolError(PoolError): - """Raised when a request enters a pool after the pool has been closed.""" - - pass - - -class LocationValueError(ValueError, HTTPError): - """Raised when there is something wrong with a given URL input.""" - - pass - - -class LocationParseError(LocationValueError): - """Raised when get_host or similar fails to parse the URL input.""" - - def __init__(self, location): - message = "Failed to parse: %s" % location - HTTPError.__init__(self, message) - - self.location = location - - -class URLSchemeUnknown(LocationValueError): - """Raised when a URL input has an unsupported scheme.""" - - def __init__(self, scheme): - message = "Not supported URL scheme %s" % scheme - super(URLSchemeUnknown, self).__init__(message) - - self.scheme = scheme - - -class ResponseError(HTTPError): - """Used as a container for an error reason supplied in a MaxRetryError.""" - - GENERIC_ERROR = "too many error responses" - SPECIFIC_ERROR = "too many {status_code} error responses" - - -class SecurityWarning(HTTPWarning): - """Warned when performing security reducing actions""" - - pass - - -class SubjectAltNameWarning(SecurityWarning): - """Warned when connecting to a host with a certificate missing a SAN.""" - - pass - - -class InsecureRequestWarning(SecurityWarning): - """Warned when making an unverified HTTPS request.""" - - pass - - -class SystemTimeWarning(SecurityWarning): - """Warned when system time is suspected to be wrong""" - - pass - - -class InsecurePlatformWarning(SecurityWarning): - """Warned when certain TLS/SSL configuration is not available on a platform.""" - - pass - - -class SNIMissingWarning(HTTPWarning): - """Warned when making a HTTPS request without SNI available.""" - - pass - - -class DependencyWarning(HTTPWarning): - """ - Warned when an attempt is made to import a module with missing optional - dependencies. - """ - - pass - - -class ResponseNotChunked(ProtocolError, ValueError): - """Response needs to be chunked in order to read it as chunks.""" - - pass - - -class BodyNotHttplibCompatible(HTTPError): - """ - Body should be :class:`http.client.HTTPResponse` like - (have an fp attribute which returns raw chunks) for read_chunked(). - """ - - pass - - -class IncompleteRead(HTTPError, httplib_IncompleteRead): - """ - Response length doesn't match expected Content-Length - - Subclass of :class:`http.client.IncompleteRead` to allow int value - for ``partial`` to avoid creating large objects on streamed reads. - """ - - def __init__(self, partial, expected): - super(IncompleteRead, self).__init__(partial, expected) - - def __repr__(self): - return "IncompleteRead(%i bytes read, %i more expected)" % ( - self.partial, - self.expected, - ) - - -class InvalidChunkLength(HTTPError, httplib_IncompleteRead): - """Invalid chunk length in a chunked response.""" - - def __init__(self, response, length): - super(InvalidChunkLength, self).__init__( - response.tell(), response.length_remaining - ) - self.response = response - self.length = length - - def __repr__(self): - return "InvalidChunkLength(got length %r, %i bytes read)" % ( - self.length, - self.partial, - ) - - -class InvalidHeader(HTTPError): - """The header provided was somehow invalid.""" - - pass - - -class ProxySchemeUnknown(AssertionError, URLSchemeUnknown): - """ProxyManager does not support the supplied scheme""" - - # TODO(t-8ch): Stop inheriting from AssertionError in v2.0. - - def __init__(self, scheme): - # 'localhost' is here because our URL parser parses - # localhost:8080 -> scheme=localhost, remove if we fix this. - if scheme == "localhost": - scheme = None - if scheme is None: - message = "Proxy URL had no scheme, should start with http:// or https://" - else: - message = ( - "Proxy URL had unsupported scheme %s, should use http:// or https://" - % scheme - ) - super(ProxySchemeUnknown, self).__init__(message) - - -class ProxySchemeUnsupported(ValueError): - """Fetching HTTPS resources through HTTPS proxies is unsupported""" - - pass - - -class HeaderParsingError(HTTPError): - """Raised by assert_header_parsing, but we convert it to a log.warning statement.""" - - def __init__(self, defects, unparsed_data): - message = "%s, unparsed data: %r" % (defects or "Unknown", unparsed_data) - super(HeaderParsingError, self).__init__(message) - - -class UnrewindableBodyError(HTTPError): - """urllib3 encountered an error when trying to rewind a body""" - - pass diff --git a/apps/bitwarden_event_logs/lib/urllib3/fields.py b/apps/bitwarden_event_logs/lib/urllib3/fields.py deleted file mode 100755 index 9d630f49..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/fields.py +++ /dev/null @@ -1,274 +0,0 @@ -from __future__ import absolute_import - -import email.utils -import mimetypes -import re - -from .packages import six - - -def guess_content_type(filename, default="application/octet-stream"): - """ - Guess the "Content-Type" of a file. - - :param filename: - The filename to guess the "Content-Type" of using :mod:`mimetypes`. - :param default: - If no "Content-Type" can be guessed, default to `default`. - """ - if filename: - return mimetypes.guess_type(filename)[0] or default - return default - - -def format_header_param_rfc2231(name, value): - """ - Helper function to format and quote a single header parameter using the - strategy defined in RFC 2231. - - Particularly useful for header parameters which might contain - non-ASCII values, like file names. This follows - `RFC 2388 Section 4.4 `_. - - :param name: - The name of the parameter, a string expected to be ASCII only. - :param value: - The value of the parameter, provided as ``bytes`` or `str``. - :ret: - An RFC-2231-formatted unicode string. - """ - if isinstance(value, six.binary_type): - value = value.decode("utf-8") - - if not any(ch in value for ch in '"\\\r\n'): - result = u'%s="%s"' % (name, value) - try: - result.encode("ascii") - except (UnicodeEncodeError, UnicodeDecodeError): - pass - else: - return result - - if six.PY2: # Python 2: - value = value.encode("utf-8") - - # encode_rfc2231 accepts an encoded string and returns an ascii-encoded - # string in Python 2 but accepts and returns unicode strings in Python 3 - value = email.utils.encode_rfc2231(value, "utf-8") - value = "%s*=%s" % (name, value) - - if six.PY2: # Python 2: - value = value.decode("utf-8") - - return value - - -_HTML5_REPLACEMENTS = { - u"\u0022": u"%22", - # Replace "\" with "\\". - u"\u005C": u"\u005C\u005C", -} - -# All control characters from 0x00 to 0x1F *except* 0x1B. -_HTML5_REPLACEMENTS.update( - { - six.unichr(cc): u"%{:02X}".format(cc) - for cc in range(0x00, 0x1F + 1) - if cc not in (0x1B,) - } -) - - -def _replace_multiple(value, needles_and_replacements): - def replacer(match): - return needles_and_replacements[match.group(0)] - - pattern = re.compile( - r"|".join([re.escape(needle) for needle in needles_and_replacements.keys()]) - ) - - result = pattern.sub(replacer, value) - - return result - - -def format_header_param_html5(name, value): - """ - Helper function to format and quote a single header parameter using the - HTML5 strategy. - - Particularly useful for header parameters which might contain - non-ASCII values, like file names. This follows the `HTML5 Working Draft - Section 4.10.22.7`_ and matches the behavior of curl and modern browsers. - - .. _HTML5 Working Draft Section 4.10.22.7: - https://w3c.github.io/html/sec-forms.html#multipart-form-data - - :param name: - The name of the parameter, a string expected to be ASCII only. - :param value: - The value of the parameter, provided as ``bytes`` or `str``. - :ret: - A unicode string, stripped of troublesome characters. - """ - if isinstance(value, six.binary_type): - value = value.decode("utf-8") - - value = _replace_multiple(value, _HTML5_REPLACEMENTS) - - return u'%s="%s"' % (name, value) - - -# For backwards-compatibility. -format_header_param = format_header_param_html5 - - -class RequestField(object): - """ - A data container for request body parameters. - - :param name: - The name of this request field. Must be unicode. - :param data: - The data/value body. - :param filename: - An optional filename of the request field. Must be unicode. - :param headers: - An optional dict-like object of headers to initially use for the field. - :param header_formatter: - An optional callable that is used to encode and format the headers. By - default, this is :func:`format_header_param_html5`. - """ - - def __init__( - self, - name, - data, - filename=None, - headers=None, - header_formatter=format_header_param_html5, - ): - self._name = name - self._filename = filename - self.data = data - self.headers = {} - if headers: - self.headers = dict(headers) - self.header_formatter = header_formatter - - @classmethod - def from_tuples(cls, fieldname, value, header_formatter=format_header_param_html5): - """ - A :class:`~urllib3.fields.RequestField` factory from old-style tuple parameters. - - Supports constructing :class:`~urllib3.fields.RequestField` from - parameter of key/value strings AND key/filetuple. A filetuple is a - (filename, data, MIME type) tuple where the MIME type is optional. - For example:: - - 'foo': 'bar', - 'fakefile': ('foofile.txt', 'contents of foofile'), - 'realfile': ('barfile.txt', open('realfile').read()), - 'typedfile': ('bazfile.bin', open('bazfile').read(), 'image/jpeg'), - 'nonamefile': 'contents of nonamefile field', - - Field names and filenames must be unicode. - """ - if isinstance(value, tuple): - if len(value) == 3: - filename, data, content_type = value - else: - filename, data = value - content_type = guess_content_type(filename) - else: - filename = None - content_type = None - data = value - - request_param = cls( - fieldname, data, filename=filename, header_formatter=header_formatter - ) - request_param.make_multipart(content_type=content_type) - - return request_param - - def _render_part(self, name, value): - """ - Overridable helper function to format a single header parameter. By - default, this calls ``self.header_formatter``. - - :param name: - The name of the parameter, a string expected to be ASCII only. - :param value: - The value of the parameter, provided as a unicode string. - """ - - return self.header_formatter(name, value) - - def _render_parts(self, header_parts): - """ - Helper function to format and quote a single header. - - Useful for single headers that are composed of multiple items. E.g., - 'Content-Disposition' fields. - - :param header_parts: - A sequence of (k, v) tuples or a :class:`dict` of (k, v) to format - as `k1="v1"; k2="v2"; ...`. - """ - parts = [] - iterable = header_parts - if isinstance(header_parts, dict): - iterable = header_parts.items() - - for name, value in iterable: - if value is not None: - parts.append(self._render_part(name, value)) - - return u"; ".join(parts) - - def render_headers(self): - """ - Renders the headers for this request field. - """ - lines = [] - - sort_keys = ["Content-Disposition", "Content-Type", "Content-Location"] - for sort_key in sort_keys: - if self.headers.get(sort_key, False): - lines.append(u"%s: %s" % (sort_key, self.headers[sort_key])) - - for header_name, header_value in self.headers.items(): - if header_name not in sort_keys: - if header_value: - lines.append(u"%s: %s" % (header_name, header_value)) - - lines.append(u"\r\n") - return u"\r\n".join(lines) - - def make_multipart( - self, content_disposition=None, content_type=None, content_location=None - ): - """ - Makes this request field into a multipart request field. - - This method overrides "Content-Disposition", "Content-Type" and - "Content-Location" headers to the request parameter. - - :param content_type: - The 'Content-Type' of the request body. - :param content_location: - The 'Content-Location' of the request body. - - """ - self.headers["Content-Disposition"] = content_disposition or u"form-data" - self.headers["Content-Disposition"] += u"; ".join( - [ - u"", - self._render_parts( - ((u"name", self._name), (u"filename", self._filename)) - ), - ] - ) - self.headers["Content-Type"] = content_type - self.headers["Content-Location"] = content_location diff --git a/apps/bitwarden_event_logs/lib/urllib3/filepost.py b/apps/bitwarden_event_logs/lib/urllib3/filepost.py deleted file mode 100755 index 36c9252c..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/filepost.py +++ /dev/null @@ -1,98 +0,0 @@ -from __future__ import absolute_import - -import binascii -import codecs -import os -from io import BytesIO - -from .fields import RequestField -from .packages import six -from .packages.six import b - -writer = codecs.lookup("utf-8")[3] - - -def choose_boundary(): - """ - Our embarrassingly-simple replacement for mimetools.choose_boundary. - """ - boundary = binascii.hexlify(os.urandom(16)) - if not six.PY2: - boundary = boundary.decode("ascii") - return boundary - - -def iter_field_objects(fields): - """ - Iterate over fields. - - Supports list of (k, v) tuples and dicts, and lists of - :class:`~urllib3.fields.RequestField`. - - """ - if isinstance(fields, dict): - i = six.iteritems(fields) - else: - i = iter(fields) - - for field in i: - if isinstance(field, RequestField): - yield field - else: - yield RequestField.from_tuples(*field) - - -def iter_fields(fields): - """ - .. deprecated:: 1.6 - - Iterate over fields. - - The addition of :class:`~urllib3.fields.RequestField` makes this function - obsolete. Instead, use :func:`iter_field_objects`, which returns - :class:`~urllib3.fields.RequestField` objects. - - Supports list of (k, v) tuples and dicts. - """ - if isinstance(fields, dict): - return ((k, v) for k, v in six.iteritems(fields)) - - return ((k, v) for k, v in fields) - - -def encode_multipart_formdata(fields, boundary=None): - """ - Encode a dictionary of ``fields`` using the multipart/form-data MIME format. - - :param fields: - Dictionary of fields or list of (key, :class:`~urllib3.fields.RequestField`). - - :param boundary: - If not specified, then a random boundary will be generated using - :func:`urllib3.filepost.choose_boundary`. - """ - body = BytesIO() - if boundary is None: - boundary = choose_boundary() - - for field in iter_field_objects(fields): - body.write(b("--%s\r\n" % (boundary))) - - writer(body).write(field.render_headers()) - data = field.data - - if isinstance(data, int): - data = str(data) # Backwards compatibility - - if isinstance(data, six.text_type): - writer(body).write(data) - else: - body.write(data) - - body.write(b"\r\n") - - body.write(b("--%s--\r\n" % (boundary))) - - content_type = str("multipart/form-data; boundary=%s" % boundary) - - return body.getvalue(), content_type diff --git a/apps/bitwarden_event_logs/lib/urllib3/packages/__init__.py b/apps/bitwarden_event_logs/lib/urllib3/packages/__init__.py deleted file mode 100755 index e69de29b..00000000 diff --git a/apps/bitwarden_event_logs/lib/urllib3/packages/__pycache__/__init__.cpython-39.pyc b/apps/bitwarden_event_logs/lib/urllib3/packages/__pycache__/__init__.cpython-39.pyc deleted file mode 100755 index c665f84549123e239ae1248be08252d59324bfd4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 172 zcmYe~<>g`k0_DX^GC}lX5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;x_uettoTesMug zXP5S!!NMd`^BkNH9-dPftH5GfBU+ClY*@ lXD6no7VF2yXXa&=#K-FuRNmsS$<0qG%}KQbS@ju+834yhD~|vG diff --git a/apps/bitwarden_event_logs/lib/urllib3/packages/__pycache__/six.cpython-39.pyc b/apps/bitwarden_event_logs/lib/urllib3/packages/__pycache__/six.cpython-39.pyc deleted file mode 100755 index cdbc19b12af4ea2339f02864c26bc99f1b8e55cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27555 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" % (type(self).__name__, id(self)) - else: - return "<%s object at %#x; for %r at %#x>" % ( - type(self).__name__, - id(self), - type(obj).__name__, - id(obj), - ) - - @classmethod - def _select_for_exit(cls): - # Return live finalizers marked for exit, oldest first - L = [(f, i) for (f, i) in cls._registry.items() if i.atexit] - L.sort(key=lambda item: item[1].index) - return [f for (f, i) in L] - - @classmethod - def _exitfunc(cls): - # At shutdown invoke finalizers for which atexit is true. - # This is called once all other non-daemonic threads have been - # joined. - reenable_gc = False - try: - if cls._registry: - import gc - - if gc.isenabled(): - reenable_gc = True - gc.disable() - pending = None - while True: - if pending is None or weakref_finalize._dirty: - pending = cls._select_for_exit() - weakref_finalize._dirty = False - if not pending: - break - f = pending.pop() - try: - # gc is disabled, so (assuming no daemonic - # threads) the following is the only line in - # this function which might trigger creation - # of a new finalizer - f() - except Exception: - sys.excepthook(*sys.exc_info()) - assert f not in cls._registry - finally: - # prevent any more finalizers from executing during shutdown - weakref_finalize._shutdown = True - if reenable_gc: - gc.enable() diff --git a/apps/bitwarden_event_logs/lib/urllib3/packages/six.py b/apps/bitwarden_event_logs/lib/urllib3/packages/six.py deleted file mode 100755 index f099a3dc..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/packages/six.py +++ /dev/null @@ -1,1076 +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/apps/bitwarden_event_logs/lib/urllib3/poolmanager.py b/apps/bitwarden_event_logs/lib/urllib3/poolmanager.py deleted file mode 100755 index fb51bf7d..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/poolmanager.py +++ /dev/null @@ -1,540 +0,0 @@ -from __future__ import absolute_import - -import collections -import functools -import logging - -from ._collections import HTTPHeaderDict, RecentlyUsedContainer -from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool, port_by_scheme -from .exceptions import ( - LocationValueError, - MaxRetryError, - ProxySchemeUnknown, - ProxySchemeUnsupported, - URLSchemeUnknown, -) -from .packages import six -from .packages.six.moves.urllib.parse import urljoin -from .request import RequestMethods -from .util.proxy import connection_requires_http_tunnel -from .util.retry import Retry -from .util.url import parse_url - -__all__ = ["PoolManager", "ProxyManager", "proxy_from_url"] - - -log = logging.getLogger(__name__) - -SSL_KEYWORDS = ( - "key_file", - "cert_file", - "cert_reqs", - "ca_certs", - "ssl_version", - "ca_cert_dir", - "ssl_context", - "key_password", - "server_hostname", -) - -# All known keyword arguments that could be provided to the pool manager, its -# pools, or the underlying connections. This is used to construct a pool key. -_key_fields = ( - "key_scheme", # str - "key_host", # str - "key_port", # int - "key_timeout", # int or float or Timeout - "key_retries", # int or Retry - "key_strict", # bool - "key_block", # bool - "key_source_address", # str - "key_key_file", # str - "key_key_password", # str - "key_cert_file", # str - "key_cert_reqs", # str - "key_ca_certs", # str - "key_ssl_version", # str - "key_ca_cert_dir", # str - "key_ssl_context", # instance of ssl.SSLContext or urllib3.util.ssl_.SSLContext - "key_maxsize", # int - "key_headers", # dict - "key__proxy", # parsed proxy url - "key__proxy_headers", # dict - "key__proxy_config", # class - "key_socket_options", # list of (level (int), optname (int), value (int or str)) tuples - "key__socks_options", # dict - "key_assert_hostname", # bool or string - "key_assert_fingerprint", # str - "key_server_hostname", # str -) - -#: The namedtuple class used to construct keys for the connection pool. -#: All custom key schemes should include the fields in this key at a minimum. -PoolKey = collections.namedtuple("PoolKey", _key_fields) - -_proxy_config_fields = ("ssl_context", "use_forwarding_for_https") -ProxyConfig = collections.namedtuple("ProxyConfig", _proxy_config_fields) - - -def _default_key_normalizer(key_class, request_context): - """ - Create a pool key out of a request context dictionary. - - According to RFC 3986, both the scheme and host are case-insensitive. - Therefore, this function normalizes both before constructing the pool - key for an HTTPS request. If you wish to change this behaviour, provide - alternate callables to ``key_fn_by_scheme``. - - :param key_class: - The class to use when constructing the key. This should be a namedtuple - with the ``scheme`` and ``host`` keys at a minimum. - :type key_class: namedtuple - :param request_context: - A dictionary-like object that contain the context for a request. - :type request_context: dict - - :return: A namedtuple that can be used as a connection pool key. - :rtype: PoolKey - """ - # Since we mutate the dictionary, make a copy first - context = request_context.copy() - context["scheme"] = context["scheme"].lower() - context["host"] = context["host"].lower() - - # These are both dictionaries and need to be transformed into frozensets - for key in ("headers", "_proxy_headers", "_socks_options"): - if key in context and context[key] is not None: - context[key] = frozenset(context[key].items()) - - # The socket_options key may be a list and needs to be transformed into a - # tuple. - socket_opts = context.get("socket_options") - if socket_opts is not None: - context["socket_options"] = tuple(socket_opts) - - # Map the kwargs to the names in the namedtuple - this is necessary since - # namedtuples can't have fields starting with '_'. - for key in list(context.keys()): - context["key_" + key] = context.pop(key) - - # Default to ``None`` for keys missing from the context - for field in key_class._fields: - if field not in context: - context[field] = None - - return key_class(**context) - - -#: A dictionary that maps a scheme to a callable that creates a pool key. -#: This can be used to alter the way pool keys are constructed, if desired. -#: Each PoolManager makes a copy of this dictionary so they can be configured -#: globally here, or individually on the instance. -key_fn_by_scheme = { - "http": functools.partial(_default_key_normalizer, PoolKey), - "https": functools.partial(_default_key_normalizer, PoolKey), -} - -pool_classes_by_scheme = {"http": HTTPConnectionPool, "https": HTTPSConnectionPool} - - -class PoolManager(RequestMethods): - """ - Allows for arbitrary requests while transparently keeping track of - necessary connection pools for you. - - :param num_pools: - Number of connection pools to cache before discarding the least - recently used pool. - - :param headers: - Headers to include with all requests, unless other headers are given - explicitly. - - :param \\**connection_pool_kw: - Additional parameters are used to create fresh - :class:`urllib3.connectionpool.ConnectionPool` instances. - - Example:: - - >>> manager = PoolManager(num_pools=2) - >>> r = manager.request('GET', 'http://google.com/') - >>> r = manager.request('GET', 'http://google.com/mail') - >>> r = manager.request('GET', 'http://yahoo.com/') - >>> len(manager.pools) - 2 - - """ - - proxy = None - proxy_config = None - - def __init__(self, num_pools=10, headers=None, **connection_pool_kw): - RequestMethods.__init__(self, headers) - self.connection_pool_kw = connection_pool_kw - self.pools = RecentlyUsedContainer(num_pools) - - # Locally set the pool classes and keys so other PoolManagers can - # override them. - self.pool_classes_by_scheme = pool_classes_by_scheme - self.key_fn_by_scheme = key_fn_by_scheme.copy() - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.clear() - # Return False to re-raise any potential exceptions - return False - - def _new_pool(self, scheme, host, port, request_context=None): - """ - Create a new :class:`urllib3.connectionpool.ConnectionPool` based on host, port, scheme, and - any additional pool keyword arguments. - - If ``request_context`` is provided, it is provided as keyword arguments - to the pool class used. This method is used to actually create the - connection pools handed out by :meth:`connection_from_url` and - companion methods. It is intended to be overridden for customization. - """ - pool_cls = self.pool_classes_by_scheme[scheme] - if request_context is None: - request_context = self.connection_pool_kw.copy() - - # Although the context has everything necessary to create the pool, - # this function has historically only used the scheme, host, and port - # in the positional args. When an API change is acceptable these can - # be removed. - for key in ("scheme", "host", "port"): - request_context.pop(key, None) - - if scheme == "http": - for kw in SSL_KEYWORDS: - request_context.pop(kw, None) - - return pool_cls(host, port, **request_context) - - def clear(self): - """ - Empty our store of pools and direct them all to close. - - This will not affect in-flight connections, but they will not be - re-used after completion. - """ - self.pools.clear() - - def connection_from_host(self, host, port=None, scheme="http", pool_kwargs=None): - """ - Get a :class:`urllib3.connectionpool.ConnectionPool` based on the host, port, and scheme. - - If ``port`` isn't given, it will be derived from the ``scheme`` using - ``urllib3.connectionpool.port_by_scheme``. If ``pool_kwargs`` is - provided, it is merged with the instance's ``connection_pool_kw`` - variable and used to create the new connection pool, if one is - needed. - """ - - if not host: - raise LocationValueError("No host specified.") - - request_context = self._merge_pool_kwargs(pool_kwargs) - request_context["scheme"] = scheme or "http" - if not port: - port = port_by_scheme.get(request_context["scheme"].lower(), 80) - request_context["port"] = port - request_context["host"] = host - - return self.connection_from_context(request_context) - - def connection_from_context(self, request_context): - """ - Get a :class:`urllib3.connectionpool.ConnectionPool` based on the request context. - - ``request_context`` must at least contain the ``scheme`` key and its - value must be a key in ``key_fn_by_scheme`` instance variable. - """ - scheme = request_context["scheme"].lower() - pool_key_constructor = self.key_fn_by_scheme.get(scheme) - if not pool_key_constructor: - raise URLSchemeUnknown(scheme) - pool_key = pool_key_constructor(request_context) - - return self.connection_from_pool_key(pool_key, request_context=request_context) - - def connection_from_pool_key(self, pool_key, request_context=None): - """ - Get a :class:`urllib3.connectionpool.ConnectionPool` based on the provided pool key. - - ``pool_key`` should be a namedtuple that only contains immutable - objects. At a minimum it must have the ``scheme``, ``host``, and - ``port`` fields. - """ - with self.pools.lock: - # If the scheme, host, or port doesn't match existing open - # connections, open a new ConnectionPool. - pool = self.pools.get(pool_key) - if pool: - return pool - - # Make a fresh ConnectionPool of the desired type - scheme = request_context["scheme"] - host = request_context["host"] - port = request_context["port"] - pool = self._new_pool(scheme, host, port, request_context=request_context) - self.pools[pool_key] = pool - - return pool - - def connection_from_url(self, url, pool_kwargs=None): - """ - Similar to :func:`urllib3.connectionpool.connection_from_url`. - - If ``pool_kwargs`` is not provided and a new pool needs to be - constructed, ``self.connection_pool_kw`` is used to initialize - the :class:`urllib3.connectionpool.ConnectionPool`. If ``pool_kwargs`` - is provided, it is used instead. Note that if a new pool does not - need to be created for the request, the provided ``pool_kwargs`` are - not used. - """ - u = parse_url(url) - return self.connection_from_host( - u.host, port=u.port, scheme=u.scheme, pool_kwargs=pool_kwargs - ) - - def _merge_pool_kwargs(self, override): - """ - Merge a dictionary of override values for self.connection_pool_kw. - - This does not modify self.connection_pool_kw and returns a new dict. - Any keys in the override dictionary with a value of ``None`` are - removed from the merged dictionary. - """ - base_pool_kwargs = self.connection_pool_kw.copy() - if override: - for key, value in override.items(): - if value is None: - try: - del base_pool_kwargs[key] - except KeyError: - pass - else: - base_pool_kwargs[key] = value - return base_pool_kwargs - - def _proxy_requires_url_absolute_form(self, parsed_url): - """ - Indicates if the proxy requires the complete destination URL in the - request. Normally this is only needed when not using an HTTP CONNECT - tunnel. - """ - if self.proxy is None: - return False - - return not connection_requires_http_tunnel( - self.proxy, self.proxy_config, parsed_url.scheme - ) - - def _validate_proxy_scheme_url_selection(self, url_scheme): - """ - Validates that were not attempting to do TLS in TLS connections on - Python2 or with unsupported SSL implementations. - """ - if self.proxy is None or url_scheme != "https": - return - - if self.proxy.scheme != "https": - return - - if six.PY2 and not self.proxy_config.use_forwarding_for_https: - raise ProxySchemeUnsupported( - "Contacting HTTPS destinations through HTTPS proxies " - "'via CONNECT tunnels' is not supported in Python 2" - ) - - def urlopen(self, method, url, redirect=True, **kw): - """ - Same as :meth:`urllib3.HTTPConnectionPool.urlopen` - with custom cross-host redirect logic and only sends the request-uri - portion of the ``url``. - - The given ``url`` parameter must be absolute, such that an appropriate - :class:`urllib3.connectionpool.ConnectionPool` can be chosen for it. - """ - u = parse_url(url) - self._validate_proxy_scheme_url_selection(u.scheme) - - conn = self.connection_from_host(u.host, port=u.port, scheme=u.scheme) - - kw["assert_same_host"] = False - kw["redirect"] = False - - if "headers" not in kw: - kw["headers"] = self.headers.copy() - - if self._proxy_requires_url_absolute_form(u): - response = conn.urlopen(method, url, **kw) - else: - response = conn.urlopen(method, u.request_uri, **kw) - - redirect_location = redirect and response.get_redirect_location() - if not redirect_location: - return response - - # Support relative URLs for redirecting. - redirect_location = urljoin(url, redirect_location) - - if response.status == 303: - # Change the method according to RFC 9110, Section 15.4.4. - method = "GET" - # And lose the body not to transfer anything sensitive. - kw["body"] = None - kw["headers"] = HTTPHeaderDict(kw["headers"])._prepare_for_method_change() - - retries = kw.get("retries") - if not isinstance(retries, Retry): - retries = Retry.from_int(retries, redirect=redirect) - - # Strip headers marked as unsafe to forward to the redirected location. - # Check remove_headers_on_redirect to avoid a potential network call within - # conn.is_same_host() which may use socket.gethostbyname() in the future. - if retries.remove_headers_on_redirect and not conn.is_same_host( - redirect_location - ): - headers = list(six.iterkeys(kw["headers"])) - for header in headers: - if header.lower() in retries.remove_headers_on_redirect: - kw["headers"].pop(header, None) - - try: - retries = retries.increment(method, url, response=response, _pool=conn) - except MaxRetryError: - if retries.raise_on_redirect: - response.drain_conn() - raise - return response - - kw["retries"] = retries - kw["redirect"] = redirect - - log.info("Redirecting %s -> %s", url, redirect_location) - - response.drain_conn() - return self.urlopen(method, redirect_location, **kw) - - -class ProxyManager(PoolManager): - """ - Behaves just like :class:`PoolManager`, but sends all requests through - the defined proxy, using the CONNECT method for HTTPS URLs. - - :param proxy_url: - The URL of the proxy to be used. - - :param proxy_headers: - A dictionary containing headers that will be sent to the proxy. In case - of HTTP they are being sent with each request, while in the - HTTPS/CONNECT case they are sent only once. Could be used for proxy - authentication. - - :param proxy_ssl_context: - The proxy SSL context is used to establish the TLS connection to the - proxy when using HTTPS proxies. - - :param use_forwarding_for_https: - (Defaults to False) If set to True will forward requests to the HTTPS - proxy to be made on behalf of the client instead of creating a TLS - tunnel via the CONNECT method. **Enabling this flag means that request - and response headers and content will be visible from the HTTPS proxy** - whereas tunneling keeps request and response headers and content - private. IP address, target hostname, SNI, and port are always visible - to an HTTPS proxy even when this flag is disabled. - - Example: - >>> proxy = urllib3.ProxyManager('http://localhost:3128/') - >>> r1 = proxy.request('GET', 'http://google.com/') - >>> r2 = proxy.request('GET', 'http://httpbin.org/') - >>> len(proxy.pools) - 1 - >>> r3 = proxy.request('GET', 'https://httpbin.org/') - >>> r4 = proxy.request('GET', 'https://twitter.com/') - >>> len(proxy.pools) - 3 - - """ - - def __init__( - self, - proxy_url, - num_pools=10, - headers=None, - proxy_headers=None, - proxy_ssl_context=None, - use_forwarding_for_https=False, - **connection_pool_kw - ): - - if isinstance(proxy_url, HTTPConnectionPool): - proxy_url = "%s://%s:%i" % ( - proxy_url.scheme, - proxy_url.host, - proxy_url.port, - ) - proxy = parse_url(proxy_url) - - if proxy.scheme not in ("http", "https"): - raise ProxySchemeUnknown(proxy.scheme) - - if not proxy.port: - port = port_by_scheme.get(proxy.scheme, 80) - proxy = proxy._replace(port=port) - - self.proxy = proxy - self.proxy_headers = proxy_headers or {} - self.proxy_ssl_context = proxy_ssl_context - self.proxy_config = ProxyConfig(proxy_ssl_context, use_forwarding_for_https) - - connection_pool_kw["_proxy"] = self.proxy - connection_pool_kw["_proxy_headers"] = self.proxy_headers - connection_pool_kw["_proxy_config"] = self.proxy_config - - super(ProxyManager, self).__init__(num_pools, headers, **connection_pool_kw) - - def connection_from_host(self, host, port=None, scheme="http", pool_kwargs=None): - if scheme == "https": - return super(ProxyManager, self).connection_from_host( - host, port, scheme, pool_kwargs=pool_kwargs - ) - - return super(ProxyManager, self).connection_from_host( - self.proxy.host, self.proxy.port, self.proxy.scheme, pool_kwargs=pool_kwargs - ) - - def _set_proxy_headers(self, url, headers=None): - """ - Sets headers needed by proxies: specifically, the Accept and Host - headers. Only sets headers not provided by the user. - """ - headers_ = {"Accept": "*/*"} - - netloc = parse_url(url).netloc - if netloc: - headers_["Host"] = netloc - - if headers: - headers_.update(headers) - return headers_ - - def urlopen(self, method, url, redirect=True, **kw): - "Same as HTTP(S)ConnectionPool.urlopen, ``url`` must be absolute." - u = parse_url(url) - if not connection_requires_http_tunnel(self.proxy, self.proxy_config, u.scheme): - # For connections using HTTP CONNECT, httplib sets the necessary - # headers on the CONNECT to the proxy. If we're not using CONNECT, - # we'll definitely need to set 'Host' at the very least. - headers = kw.get("headers", self.headers) - kw["headers"] = self._set_proxy_headers(url, headers) - - return super(ProxyManager, self).urlopen(method, url, redirect=redirect, **kw) - - -def proxy_from_url(url, **kw): - return ProxyManager(proxy_url=url, **kw) diff --git a/apps/bitwarden_event_logs/lib/urllib3/request.py b/apps/bitwarden_event_logs/lib/urllib3/request.py deleted file mode 100755 index 3b4cf999..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/request.py +++ /dev/null @@ -1,191 +0,0 @@ -from __future__ import absolute_import - -import sys - -from .filepost import encode_multipart_formdata -from .packages import six -from .packages.six.moves.urllib.parse import urlencode - -__all__ = ["RequestMethods"] - - -class RequestMethods(object): - """ - Convenience mixin for classes who implement a :meth:`urlopen` method, such - as :class:`urllib3.HTTPConnectionPool` and - :class:`urllib3.PoolManager`. - - Provides behavior for making common types of HTTP request methods and - decides which type of request field encoding to use. - - Specifically, - - :meth:`.request_encode_url` is for sending requests whose fields are - encoded in the URL (such as GET, HEAD, DELETE). - - :meth:`.request_encode_body` is for sending requests whose fields are - encoded in the *body* of the request using multipart or www-form-urlencoded - (such as for POST, PUT, PATCH). - - :meth:`.request` is for making any kind of request, it will look up the - appropriate encoding format and use one of the above two methods to make - the request. - - Initializer parameters: - - :param headers: - Headers to include with all requests, unless other headers are given - explicitly. - """ - - _encode_url_methods = {"DELETE", "GET", "HEAD", "OPTIONS"} - - def __init__(self, headers=None): - self.headers = headers or {} - - def urlopen( - self, - method, - url, - body=None, - headers=None, - encode_multipart=True, - multipart_boundary=None, - **kw - ): # Abstract - raise NotImplementedError( - "Classes extending RequestMethods must implement " - "their own ``urlopen`` method." - ) - - def request(self, method, url, fields=None, headers=None, **urlopen_kw): - """ - Make a request using :meth:`urlopen` with the appropriate encoding of - ``fields`` based on the ``method`` used. - - This is a convenience method that requires the least amount of manual - effort. It can be used in most situations, while still having the - option to drop down to more specific methods when necessary, such as - :meth:`request_encode_url`, :meth:`request_encode_body`, - or even the lowest level :meth:`urlopen`. - """ - method = method.upper() - - urlopen_kw["request_url"] = url - - if method in self._encode_url_methods: - return self.request_encode_url( - method, url, fields=fields, headers=headers, **urlopen_kw - ) - else: - return self.request_encode_body( - method, url, fields=fields, headers=headers, **urlopen_kw - ) - - def request_encode_url(self, method, url, fields=None, headers=None, **urlopen_kw): - """ - Make a request using :meth:`urlopen` with the ``fields`` encoded in - the url. This is useful for request methods like GET, HEAD, DELETE, etc. - """ - if headers is None: - headers = self.headers - - extra_kw = {"headers": headers} - extra_kw.update(urlopen_kw) - - if fields: - url += "?" + urlencode(fields) - - return self.urlopen(method, url, **extra_kw) - - def request_encode_body( - self, - method, - url, - fields=None, - headers=None, - encode_multipart=True, - multipart_boundary=None, - **urlopen_kw - ): - """ - Make a request using :meth:`urlopen` with the ``fields`` encoded in - the body. This is useful for request methods like POST, PUT, PATCH, etc. - - When ``encode_multipart=True`` (default), then - :func:`urllib3.encode_multipart_formdata` is used to encode - the payload with the appropriate content type. Otherwise - :func:`urllib.parse.urlencode` is used with the - 'application/x-www-form-urlencoded' content type. - - Multipart encoding must be used when posting files, and it's reasonably - safe to use it in other times too. However, it may break request - signing, such as with OAuth. - - Supports an optional ``fields`` parameter of key/value strings AND - key/filetuple. A filetuple is a (filename, data, MIME type) tuple where - the MIME type is optional. For example:: - - fields = { - 'foo': 'bar', - 'fakefile': ('foofile.txt', 'contents of foofile'), - 'realfile': ('barfile.txt', open('realfile').read()), - 'typedfile': ('bazfile.bin', open('bazfile').read(), - 'image/jpeg'), - 'nonamefile': 'contents of nonamefile field', - } - - When uploading a file, providing a filename (the first parameter of the - tuple) is optional but recommended to best mimic behavior of browsers. - - Note that if ``headers`` are supplied, the 'Content-Type' header will - be overwritten because it depends on the dynamic random boundary string - which is used to compose the body of the request. The random boundary - string can be explicitly set with the ``multipart_boundary`` parameter. - """ - if headers is None: - headers = self.headers - - extra_kw = {"headers": {}} - - if fields: - if "body" in urlopen_kw: - raise TypeError( - "request got values for both 'fields' and 'body', can only specify one." - ) - - if encode_multipart: - body, content_type = encode_multipart_formdata( - fields, boundary=multipart_boundary - ) - else: - body, content_type = ( - urlencode(fields), - "application/x-www-form-urlencoded", - ) - - extra_kw["body"] = body - extra_kw["headers"] = {"Content-Type": content_type} - - extra_kw["headers"].update(headers) - extra_kw.update(urlopen_kw) - - return self.urlopen(method, url, **extra_kw) - - -if not six.PY2: - - class RequestModule(sys.modules[__name__].__class__): - def __call__(self, *args, **kwargs): - """ - If user tries to call this module directly urllib3 v2.x style raise an error to the user - suggesting they may need urllib3 v2 - """ - raise TypeError( - "'module' object is not callable\n" - "urllib3.request() method is not supported in this release, " - "upgrade to urllib3 v2 to use it\n" - "see https://urllib3.readthedocs.io/en/stable/v2-migration-guide.html" - ) - - sys.modules[__name__].__class__ = RequestModule diff --git a/apps/bitwarden_event_logs/lib/urllib3/response.py b/apps/bitwarden_event_logs/lib/urllib3/response.py deleted file mode 100755 index 0bd13d40..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/response.py +++ /dev/null @@ -1,885 +0,0 @@ -from __future__ import absolute_import - -import io -import logging -import sys -import warnings -import zlib -from contextlib import contextmanager -from socket import error as SocketError -from socket import timeout as SocketTimeout - -try: - try: - import brotlicffi as brotli - except ImportError: - import brotli -except ImportError: - brotli = None - -from . import util -from ._collections import HTTPHeaderDict -from .connection import BaseSSLError, HTTPException -from .exceptions import ( - BodyNotHttplibCompatible, - DecodeError, - HTTPError, - IncompleteRead, - InvalidChunkLength, - InvalidHeader, - ProtocolError, - ReadTimeoutError, - ResponseNotChunked, - SSLError, -) -from .packages import six -from .util.response import is_fp_closed, is_response_to_head - -log = logging.getLogger(__name__) - - -class DeflateDecoder(object): - def __init__(self): - self._first_try = True - self._data = b"" - self._obj = zlib.decompressobj() - - def __getattr__(self, name): - return getattr(self._obj, name) - - def decompress(self, data): - if not data: - return data - - if not self._first_try: - return self._obj.decompress(data) - - self._data += data - try: - decompressed = self._obj.decompress(data) - if decompressed: - self._first_try = False - self._data = None - return decompressed - except zlib.error: - self._first_try = False - self._obj = zlib.decompressobj(-zlib.MAX_WBITS) - try: - return self.decompress(self._data) - finally: - self._data = None - - -class GzipDecoderState(object): - - FIRST_MEMBER = 0 - OTHER_MEMBERS = 1 - SWALLOW_DATA = 2 - - -class GzipDecoder(object): - def __init__(self): - self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS) - self._state = GzipDecoderState.FIRST_MEMBER - - def __getattr__(self, name): - return getattr(self._obj, name) - - def decompress(self, data): - ret = bytearray() - if self._state == GzipDecoderState.SWALLOW_DATA or not data: - return bytes(ret) - while True: - try: - ret += self._obj.decompress(data) - except zlib.error: - previous_state = self._state - # Ignore data after the first error - self._state = GzipDecoderState.SWALLOW_DATA - if previous_state == GzipDecoderState.OTHER_MEMBERS: - # Allow trailing garbage acceptable in other gzip clients - return bytes(ret) - raise - data = self._obj.unused_data - if not data: - return bytes(ret) - self._state = GzipDecoderState.OTHER_MEMBERS - self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS) - - -if brotli is not None: - - class BrotliDecoder(object): - # 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): - self._obj = brotli.Decompressor() - if hasattr(self._obj, "decompress"): - self.decompress = self._obj.decompress - else: - self.decompress = self._obj.process - - def flush(self): - if hasattr(self._obj, "flush"): - return self._obj.flush() - return b"" - - -class MultiDecoder(object): - """ - From RFC7231: - If one or more encodings have been applied to a representation, the - sender that applied the encodings MUST generate a Content-Encoding - header field that lists the content codings in the order in which - they were applied. - """ - - def __init__(self, modes): - self._decoders = [_get_decoder(m.strip()) for m in modes.split(",")] - - def flush(self): - return self._decoders[0].flush() - - def decompress(self, data): - for d in reversed(self._decoders): - data = d.decompress(data) - return data - - -def _get_decoder(mode): - if "," in mode: - return MultiDecoder(mode) - - if mode == "gzip": - return GzipDecoder() - - if brotli is not None and mode == "br": - return BrotliDecoder() - - return DeflateDecoder() - - -class HTTPResponse(io.IOBase): - """ - HTTP Response container. - - Backwards-compatible with :class:`http.client.HTTPResponse` but the response ``body`` is - loaded and decoded on-demand when the ``data`` property is accessed. This - class is also compatible with the Python standard library's :mod:`io` - module, and can hence be treated as a readable object in the context of that - framework. - - Extra parameters for behaviour not present in :class:`http.client.HTTPResponse`: - - :param preload_content: - If True, the response's body will be preloaded during construction. - - :param decode_content: - If True, will attempt to decode the body based on the - 'content-encoding' header. - - :param original_response: - When this HTTPResponse wrapper is generated from an :class:`http.client.HTTPResponse` - object, it's convenient to include the original for debug purposes. It's - otherwise unused. - - :param retries: - The retries contains the last :class:`~urllib3.util.retry.Retry` that - was used during the request. - - :param enforce_content_length: - Enforce content length checking. Body returned by server must match - value of Content-Length header, if present. Otherwise, raise error. - """ - - CONTENT_DECODERS = ["gzip", "deflate"] - if brotli is not None: - CONTENT_DECODERS += ["br"] - REDIRECT_STATUSES = [301, 302, 303, 307, 308] - - def __init__( - self, - body="", - headers=None, - status=0, - version=0, - reason=None, - strict=0, - preload_content=True, - decode_content=True, - original_response=None, - pool=None, - connection=None, - msg=None, - retries=None, - enforce_content_length=False, - request_method=None, - request_url=None, - auto_close=True, - ): - - if isinstance(headers, HTTPHeaderDict): - self.headers = headers - else: - self.headers = HTTPHeaderDict(headers) - self.status = status - self.version = version - self.reason = reason - self.strict = strict - self.decode_content = decode_content - self.retries = retries - self.enforce_content_length = enforce_content_length - self.auto_close = auto_close - - self._decoder = None - self._body = None - self._fp = None - self._original_response = original_response - self._fp_bytes_read = 0 - self.msg = msg - self._request_url = request_url - - if body and isinstance(body, (six.string_types, bytes)): - self._body = body - - self._pool = pool - self._connection = connection - - if hasattr(body, "read"): - self._fp = body - - # Are we using the chunked-style of transfer encoding? - self.chunked = False - self.chunk_left = None - tr_enc = self.headers.get("transfer-encoding", "").lower() - # Don't incur the penalty of creating a list and then discarding it - encodings = (enc.strip() for enc in tr_enc.split(",")) - if "chunked" in encodings: - self.chunked = True - - # Determine length of response - self.length_remaining = self._init_length(request_method) - - # If requested, preload the body. - if preload_content and not self._body: - self._body = self.read(decode_content=decode_content) - - def get_redirect_location(self): - """ - Should we redirect and where to? - - :returns: Truthy redirect location string if we got a redirect status - code and valid location. ``None`` if redirect status and no - location. ``False`` if not a redirect status code. - """ - if self.status in self.REDIRECT_STATUSES: - return self.headers.get("location") - - return False - - def release_conn(self): - if not self._pool or not self._connection: - return - - self._pool._put_conn(self._connection) - self._connection = None - - def drain_conn(self): - """ - Read and discard any remaining HTTP response data in the response connection. - - Unread data in the HTTPResponse connection blocks the connection from being released back to the pool. - """ - try: - self.read() - except (HTTPError, SocketError, BaseSSLError, HTTPException): - pass - - @property - def data(self): - # For backwards-compat with earlier urllib3 0.4 and earlier. - if self._body: - return self._body - - if self._fp: - return self.read(cache_content=True) - - @property - def connection(self): - return self._connection - - def isclosed(self): - return is_fp_closed(self._fp) - - def tell(self): - """ - Obtain the number of bytes pulled over the wire so far. May differ from - the amount of content returned by :meth:``urllib3.response.HTTPResponse.read`` - if bytes are encoded on the wire (e.g, compressed). - """ - return self._fp_bytes_read - - def _init_length(self, request_method): - """ - Set initial length value for Response content if available. - """ - length = self.headers.get("content-length") - - if length is not None: - if self.chunked: - # This Response will fail with an IncompleteRead if it can't be - # received as chunked. This method falls back to attempt reading - # the response before raising an exception. - log.warning( - "Received response with both Content-Length and " - "Transfer-Encoding set. This is expressly forbidden " - "by RFC 7230 sec 3.3.2. Ignoring Content-Length and " - "attempting to process response as Transfer-Encoding: " - "chunked." - ) - return None - - try: - # RFC 7230 section 3.3.2 specifies multiple content lengths can - # be sent in a single Content-Length header - # (e.g. Content-Length: 42, 42). This line ensures the values - # are all valid ints and that as long as the `set` length is 1, - # all values are the same. Otherwise, the header is invalid. - lengths = set([int(val) for val in length.split(",")]) - if len(lengths) > 1: - raise InvalidHeader( - "Content-Length contained multiple " - "unmatching values (%s)" % length - ) - length = lengths.pop() - except ValueError: - length = None - else: - if length < 0: - length = None - - # Convert status to int for comparison - # In some cases, httplib returns a status of "_UNKNOWN" - try: - status = int(self.status) - except ValueError: - status = 0 - - # Check for responses that shouldn't include a body - if status in (204, 304) or 100 <= status < 200 or request_method == "HEAD": - length = 0 - - return length - - def _init_decoder(self): - """ - Set-up the _decoder attribute if necessary. - """ - # Note: content-encoding value should be case-insensitive, per RFC 7230 - # Section 3.2 - content_encoding = self.headers.get("content-encoding", "").lower() - if self._decoder is None: - if content_encoding in self.CONTENT_DECODERS: - self._decoder = _get_decoder(content_encoding) - elif "," in content_encoding: - encodings = [ - e.strip() - for e in content_encoding.split(",") - if e.strip() in self.CONTENT_DECODERS - ] - if len(encodings): - self._decoder = _get_decoder(content_encoding) - - DECODER_ERROR_CLASSES = (IOError, zlib.error) - if brotli is not None: - DECODER_ERROR_CLASSES += (brotli.error,) - - def _decode(self, data, decode_content, flush_decoder): - """ - Decode the data passed in and potentially flush the decoder. - """ - if not decode_content: - return data - - try: - if self._decoder: - data = self._decoder.decompress(data) - except self.DECODER_ERROR_CLASSES as e: - content_encoding = self.headers.get("content-encoding", "").lower() - raise DecodeError( - "Received response with content-encoding: %s, but " - "failed to decode it." % content_encoding, - e, - ) - if flush_decoder: - data += self._flush_decoder() - - return data - - def _flush_decoder(self): - """ - Flushes the decoder. Should only be called if the decoder is actually - being used. - """ - if self._decoder: - buf = self._decoder.decompress(b"") - return buf + self._decoder.flush() - - return b"" - - @contextmanager - def _error_catcher(self): - """ - Catch low-level python exceptions, instead re-raising urllib3 - variants, so that low-level exceptions are not leaked in the - high-level api. - - On exit, release the connection back to the pool. - """ - clean_exit = False - - try: - try: - yield - - except SocketTimeout: - # FIXME: Ideally we'd like to include the url in the ReadTimeoutError but - # there is yet no clean way to get at it from this context. - raise ReadTimeoutError(self._pool, None, "Read timed out.") - - except BaseSSLError as e: - # FIXME: Is there a better way to differentiate between SSLErrors? - if "read operation timed out" not in str(e): - # SSL errors related to framing/MAC get wrapped and reraised here - raise SSLError(e) - - raise ReadTimeoutError(self._pool, None, "Read timed out.") - - except (HTTPException, SocketError) as e: - # This includes IncompleteRead. - raise ProtocolError("Connection broken: %r" % e, e) - - # If no exception is thrown, we should avoid cleaning up - # unnecessarily. - clean_exit = True - finally: - # If we didn't terminate cleanly, we need to throw away our - # connection. - if not clean_exit: - # The response may not be closed but we're not going to use it - # anymore so close it now to ensure that the connection is - # released back to the pool. - if self._original_response: - self._original_response.close() - - # Closing the response may not actually be sufficient to close - # everything, so if we have a hold of the connection close that - # too. - if self._connection: - self._connection.close() - - # If we hold the original response but it's closed now, we should - # return the connection back to the pool. - if self._original_response and self._original_response.isclosed(): - self.release_conn() - - def _fp_read(self, amt): - """ - Read a response with the thought that reading the number of bytes - larger than can fit in a 32-bit int at a time via SSL in some - known cases leads to an overflow error that has to be prevented - if `amt` or `self.length_remaining` indicate that a problem may - happen. - - The known cases: - * 3.8 <= CPython < 3.9.7 because of a bug - https://github.com/urllib3/urllib3/issues/2513#issuecomment-1152559900. - * urllib3 injected with pyOpenSSL-backed SSL-support. - * CPython < 3.10 only when `amt` does not fit 32-bit int. - """ - assert self._fp - c_int_max = 2 ** 31 - 1 - if ( - ( - (amt and amt > c_int_max) - or (self.length_remaining and self.length_remaining > c_int_max) - ) - and not util.IS_SECURETRANSPORT - and (util.IS_PYOPENSSL or sys.version_info < (3, 10)) - ): - buffer = io.BytesIO() - # Besides `max_chunk_amt` being a maximum chunk size, it - # affects memory overhead of reading a response by this - # method in CPython. - # `c_int_max` equal to 2 GiB - 1 byte is the actual maximum - # chunk size that does not lead to an overflow error, but - # 256 MiB is a compromise. - max_chunk_amt = 2 ** 28 - while amt is None or amt != 0: - if amt is not None: - chunk_amt = min(amt, max_chunk_amt) - amt -= chunk_amt - else: - chunk_amt = max_chunk_amt - data = self._fp.read(chunk_amt) - if not data: - break - buffer.write(data) - del data # to reduce peak memory usage by `max_chunk_amt`. - return buffer.getvalue() - else: - # StringIO doesn't like amt=None - return self._fp.read(amt) if amt is not None else self._fp.read() - - def read(self, amt=None, decode_content=None, cache_content=False): - """ - Similar to :meth:`http.client.HTTPResponse.read`, but with two additional - parameters: ``decode_content`` and ``cache_content``. - - :param amt: - How much of the content to read. If specified, caching is skipped - because it doesn't make sense to cache partial content as the full - response. - - :param decode_content: - If True, will attempt to decode the body based on the - 'content-encoding' header. - - :param cache_content: - If True, will save the returned data such that the same result is - returned despite of the state of the underlying file object. This - is useful if you want the ``.data`` property to continue working - after having ``.read()`` the file object. (Overridden if ``amt`` is - set.) - """ - self._init_decoder() - if decode_content is None: - decode_content = self.decode_content - - if self._fp is None: - return - - flush_decoder = False - fp_closed = getattr(self._fp, "closed", False) - - with self._error_catcher(): - data = self._fp_read(amt) if not fp_closed else b"" - if amt is None: - flush_decoder = True - else: - cache_content = False - if ( - amt != 0 and not data - ): # Platform-specific: Buggy versions of Python. - # Close the connection when no data is returned - # - # This is redundant to what httplib/http.client _should_ - # already do. However, versions of python released before - # December 15, 2012 (http://bugs.python.org/issue16298) do - # not properly close the connection in all cases. There is - # no harm in redundantly calling close. - self._fp.close() - flush_decoder = True - if self.enforce_content_length and self.length_remaining not in ( - 0, - None, - ): - # This is an edge case that httplib failed to cover due - # to concerns of backward compatibility. We're - # addressing it here to make sure IncompleteRead is - # raised during streaming, so all calls with incorrect - # Content-Length are caught. - raise IncompleteRead(self._fp_bytes_read, self.length_remaining) - - if data: - self._fp_bytes_read += len(data) - if self.length_remaining is not None: - self.length_remaining -= len(data) - - data = self._decode(data, decode_content, flush_decoder) - - if cache_content: - self._body = data - - return data - - def stream(self, amt=2 ** 16, decode_content=None): - """ - A generator wrapper for the read() method. A call will block until - ``amt`` bytes have been read from the connection or until the - connection is closed. - - :param amt: - How much of the content to read. The generator will return up to - much data per iteration, but may return less. This is particularly - likely when using compressed data. However, the empty string will - never be returned. - - :param decode_content: - If True, will attempt to decode the body based on the - 'content-encoding' header. - """ - if self.chunked and self.supports_chunked_reads(): - for line in self.read_chunked(amt, decode_content=decode_content): - yield line - else: - while not is_fp_closed(self._fp): - data = self.read(amt=amt, decode_content=decode_content) - - if data: - yield data - - @classmethod - def from_httplib(ResponseCls, r, **response_kw): - """ - Given an :class:`http.client.HTTPResponse` instance ``r``, return a - corresponding :class:`urllib3.response.HTTPResponse` object. - - Remaining parameters are passed to the HTTPResponse constructor, along - with ``original_response=r``. - """ - headers = r.msg - - if not isinstance(headers, HTTPHeaderDict): - if six.PY2: - # Python 2.7 - headers = HTTPHeaderDict.from_httplib(headers) - else: - headers = HTTPHeaderDict(headers.items()) - - # HTTPResponse objects in Python 3 don't have a .strict attribute - strict = getattr(r, "strict", 0) - resp = ResponseCls( - body=r, - headers=headers, - status=r.status, - version=r.version, - reason=r.reason, - strict=strict, - original_response=r, - **response_kw - ) - return resp - - # Backwards-compatibility methods for http.client.HTTPResponse - def getheaders(self): - warnings.warn( - "HTTPResponse.getheaders() is deprecated and will be removed " - "in urllib3 v2.1.0. Instead access HTTPResponse.headers directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return self.headers - - def getheader(self, name, default=None): - warnings.warn( - "HTTPResponse.getheader() is deprecated and will be removed " - "in urllib3 v2.1.0. Instead use HTTPResponse.headers.get(name, default).", - category=DeprecationWarning, - stacklevel=2, - ) - return self.headers.get(name, default) - - # Backwards compatibility for http.cookiejar - def info(self): - return self.headers - - # Overrides from io.IOBase - def close(self): - if not self.closed: - self._fp.close() - - if self._connection: - self._connection.close() - - if not self.auto_close: - io.IOBase.close(self) - - @property - def closed(self): - if not self.auto_close: - return io.IOBase.closed.__get__(self) - elif self._fp is None: - return True - elif hasattr(self._fp, "isclosed"): - return self._fp.isclosed() - elif hasattr(self._fp, "closed"): - return self._fp.closed - else: - return True - - def fileno(self): - if self._fp is None: - raise IOError("HTTPResponse has no file to get a fileno from") - elif hasattr(self._fp, "fileno"): - return self._fp.fileno() - else: - raise IOError( - "The file-like object this HTTPResponse is wrapped " - "around has no file descriptor" - ) - - def flush(self): - if ( - self._fp is not None - and hasattr(self._fp, "flush") - and not getattr(self._fp, "closed", False) - ): - return self._fp.flush() - - def readable(self): - # This method is required for `io` module compatibility. - return True - - def readinto(self, b): - # This method is required for `io` module compatibility. - temp = self.read(len(b)) - if len(temp) == 0: - return 0 - else: - b[: len(temp)] = temp - return len(temp) - - def supports_chunked_reads(self): - """ - Checks if the underlying file-like object looks like a - :class:`http.client.HTTPResponse` object. We do this by testing for - the fp attribute. If it is present we assume it returns raw chunks as - processed by read_chunked(). - """ - return hasattr(self._fp, "fp") - - def _update_chunk_length(self): - # First, we'll figure out length of a chunk and then - # we'll try to read it from socket. - if self.chunk_left is not None: - return - line = self._fp.fp.readline() - line = line.split(b";", 1)[0] - try: - self.chunk_left = int(line, 16) - except ValueError: - # Invalid chunked protocol response, abort. - self.close() - raise InvalidChunkLength(self, line) - - def _handle_chunk(self, amt): - returned_chunk = None - if amt is None: - chunk = self._fp._safe_read(self.chunk_left) - returned_chunk = chunk - self._fp._safe_read(2) # Toss the CRLF at the end of the chunk. - self.chunk_left = None - elif amt < self.chunk_left: - value = self._fp._safe_read(amt) - self.chunk_left = self.chunk_left - amt - returned_chunk = value - elif amt == self.chunk_left: - value = self._fp._safe_read(amt) - self._fp._safe_read(2) # Toss the CRLF at the end of the chunk. - self.chunk_left = None - returned_chunk = value - else: # amt > self.chunk_left - returned_chunk = self._fp._safe_read(self.chunk_left) - self._fp._safe_read(2) # Toss the CRLF at the end of the chunk. - self.chunk_left = None - return returned_chunk - - def read_chunked(self, amt=None, decode_content=None): - """ - Similar to :meth:`HTTPResponse.read`, but with an additional - parameter: ``decode_content``. - - :param amt: - How much of the content to read. If specified, caching is skipped - because it doesn't make sense to cache partial content as the full - response. - - :param decode_content: - If True, will attempt to decode the body based on the - 'content-encoding' header. - """ - self._init_decoder() - # FIXME: Rewrite this method and make it a class with a better structured logic. - if not self.chunked: - raise ResponseNotChunked( - "Response is not chunked. " - "Header 'transfer-encoding: chunked' is missing." - ) - if not self.supports_chunked_reads(): - raise BodyNotHttplibCompatible( - "Body should be http.client.HTTPResponse like. " - "It should have have an fp attribute which returns raw chunks." - ) - - with self._error_catcher(): - # Don't bother reading the body of a HEAD request. - if self._original_response and is_response_to_head(self._original_response): - self._original_response.close() - return - - # If a response is already read and closed - # then return immediately. - if self._fp.fp is None: - return - - while True: - self._update_chunk_length() - if self.chunk_left == 0: - break - chunk = self._handle_chunk(amt) - decoded = self._decode( - chunk, decode_content=decode_content, flush_decoder=False - ) - if decoded: - yield decoded - - if decode_content: - # On CPython and PyPy, we should never need to flush the - # decoder. However, on Jython we *might* need to, so - # lets defensively do it anyway. - decoded = self._flush_decoder() - if decoded: # Platform-specific: Jython. - yield decoded - - # Chunk content ends with \r\n: discard it. - while True: - line = self._fp.fp.readline() - if not line: - # Some sites may not end with '\r\n'. - break - if line == b"\r\n": - break - - # We read everything; close the "file". - if self._original_response: - self._original_response.close() - - def geturl(self): - """ - Returns the URL that was the source of this response. - If the request that generated this response redirected, this method - will return the final redirect location. - """ - if self.retries is not None and len(self.retries.history): - return self.retries.history[-1].redirect_location - else: - return self._request_url - - def __iter__(self): - buffer = [] - for chunk in self.stream(decode_content=True): - if b"\n" in chunk: - chunk = chunk.split(b"\n") - yield b"".join(buffer) + chunk[0] + b"\n" - for x in chunk[1:-1]: - yield x + b"\n" - if chunk[-1]: - buffer = [chunk[-1]] - else: - buffer = [] - else: - buffer.append(chunk) - if buffer: - yield b"".join(buffer) diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/__init__.py b/apps/bitwarden_event_logs/lib/urllib3/util/__init__.py deleted file mode 100755 index 4547fc52..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/util/__init__.py +++ /dev/null @@ -1,49 +0,0 @@ -from __future__ import absolute_import - -# For backwards compatibility, provide imports that used to be here. -from .connection import is_connection_dropped -from .request import SKIP_HEADER, SKIPPABLE_HEADERS, make_headers -from .response import is_fp_closed -from .retry import Retry -from .ssl_ import ( - ALPN_PROTOCOLS, - HAS_SNI, - IS_PYOPENSSL, - IS_SECURETRANSPORT, - PROTOCOL_TLS, - SSLContext, - assert_fingerprint, - resolve_cert_reqs, - resolve_ssl_version, - ssl_wrap_socket, -) -from .timeout import Timeout, current_time -from .url import Url, get_host, parse_url, split_first -from .wait import wait_for_read, wait_for_write - -__all__ = ( - "HAS_SNI", - "IS_PYOPENSSL", - "IS_SECURETRANSPORT", - "SSLContext", - "PROTOCOL_TLS", - "ALPN_PROTOCOLS", - "Retry", - "Timeout", - "Url", - "assert_fingerprint", - "current_time", - "is_connection_dropped", - "is_fp_closed", - "get_host", - "parse_url", - "make_headers", - "resolve_cert_reqs", - "resolve_ssl_version", - "split_first", - "ssl_wrap_socket", - "wait_for_read", - "wait_for_write", - "SKIP_HEADER", - "SKIPPABLE_HEADERS", -) diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/__init__.cpython-39.pyc b/apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/__init__.cpython-39.pyc deleted file mode 100755 index 3b417c94d86b379cb2d8939d80eb6127beaf2b1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1081 zcmZ9L%W~5&6o&2GoQrc$+Vlo39Twiy%&=t`Ldi@!gd`KE3$)R=vD$hn*+!BZy5><} z!=9(nmQ`PY6*>+j49?_>&avfl{{JX`y_-r(MH=3(91he7W@#s&+Q#SK&3BDnIK)BEaZ2h9E1TSeU^nOzn=S1>* zr>Yvm@oa);^XX!GIvsn;IvaY}o182A+{3ev)7fa^d1KYlkvBTMn2#3o;l!Iw=L=;& z?!pVbv8o?CjU!1uOV#m(AY9_BFuEo@;bA0In-eYZj^HKS;pCH0-N%#=6yNDawMNy1 zV8#6ei+FiUq?1$DB4i{^rLvbP=OmI?>V#8J`3p`}`I<<46AP&-iO&VWsZQ2JLPKbP z3wT3);SreUBDio2qV>etmG`M7nw!X>{unxpe&`5$EV;)UX@?5I7lgZ4Sx^;2HSbt?3uRyys%s5z-;Ay13}dt#Q*>R diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/connection.cpython-39.pyc b/apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/connection.cpython-39.pyc deleted file mode 100755 index 529ed412ef75445a86734d0b4efa3131e0e3e2b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3413 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 R;&03JfSe7;;nF2?_z&@@b|wG- diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/queue.cpython-39.pyc b/apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/queue.cpython-39.pyc deleted file mode 100755 index 5dca7cd924a80bbb5b7bb925b266c33aa7c1fd69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1036 zcmZ`&&2G~`5Z)i#anhu%3OoTPUu*6D*v_t9pvtX%kXCT% zEA5q2uN*lsvr!_eg^^}5Kh}IR-)xH6Y(hYu|M*$`riA>$U?>3uhoJTy7)d16Bqx$d zuIYm1jG`ZCz6kPw62;C!llrfseDt2kP)658Mk+Xqu6Q0xdQ6hhJ#->R_Y7=89k2-mNR9z&;>I7V zdGpz~M!b!nkNOtW?t-uUYsgxkY0DN2~+rsq>JVP3Z7E_>B~zO`d6Jl6@A@XB{w7btnd1-Azb`=y~-P8 P(IR^>o6;$ZSj_(d8OhjN diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/request.cpython-39.pyc b/apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/request.cpython-39.pyc deleted file mode 100755 index f45533b57620ed8810a54b983d3fdcd64e6b164f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3474 zcmbtX&2QVt73Yu?CDU^Ju}QW~zXo0usur^3qCvVac7xcCw{dGY#yZ=gJd_!6Mi$r9 zkeM0Akt(C;deKAwfF62E+Fp9^U&HGH1@>R?q3N5UWXawwdRQ79J_E%cLvQBv&kq2U#iS6mCD2>eA086C31& zaW$%+jN+_CSuY`q9gdtN(Tcn+jeYjDK~;3;1d)Ri8GPDj47kn)zBUj z8jqQ2Fkv8ACYS_Y((tOj;WY6sJWO+ZMmVS?^QaH(uXI|^hN_?Pja(#2-2GW+v;AQG znk~9S#!*Oe)t}wCn$TezV) zOoF0?8?$Vq*JcbF=T{8ACc}-eF@ntBcO$WB#@C{uWPOQRLe~tT%gV@~iB2$s=Jj!(HZ&m{%XPJhANd~{lAA!l;r7;43=AgmKO_vRp zuK7+;otXEZmbeY~T^@AOOm$?I&8Z(ZB|kMHU=aTiWR zy7WySO)tP}iq8=cV?^*+AcW?j=fol#qpnHZ7{|G7yv0<{WbJ9 zm<1tdm0obynn3?#3O$7y|2e?_f^S-U9Zq?+M={5Luh1 z6t{5o^x9{sUTxAIo-g zL3|h7Ml9(;_mbkC9=@lCiXOfXL*f6LBsnV`+tnz-77V2Uy5ZpBJFxK# zUim{9ti?K7D;*j}`)%E+nP0C0Iq0p~+w>02)4W^wVOWM^7z&+h3J)?WdvM>R<-%vL zboUGiK^j?udPbsCXo6&0#;;7)HYyd1bB%Yg&=cv1afrHNRnH%7?;Pw+y7FVO3b#Ps z7O6_&sMm{wJ7Las>lF@CnnRG5OU2^8`LDo05_pqIuXH_wnMsx=V2x69py=!_Oz77> rESWw;FeNu(sN3*|Ry^0FV*m&uUjtT0o?Wv&*Zk8{M(d^jq_*~7y0Y4W diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/response.cpython-39.pyc b/apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/response.cpython-39.pyc deleted file mode 100755 index 512e65479a07f215e017e9786e44efdaf3aceda0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2321 zcmZuyPj4JG6t`z)cPEogTT%fPpkgReSwUwjRYIt$Dnc8S9vY%16j+I_yfdC%H=aMW zJ!v*fE~E!89QX!};KCQ+!_1XaKL8gFg=f!hvT51X*l+y&=lQ+g?>BU1B|z}I^4stH z&lQCJGB1li2VTB_M{Pk9qZkX6;sM6Sc7&6<12^>s9=yA;Ct7KH(6)9>_ zJVS#Z_Twe!m*XH_hQ1qjw^4891?-A?xIRxuDi@_@L!K77)IF#E;C?AIFR0Y_vOOv} z&GbW_3#xgZ-DL@jbp7tO2KSeFnZ;C2H*@`!F&pO#6FKa>y1{77e2>u8)Vm769dp7PQMIAh(qHM+?)h@*J2a@L?m;c=F=Y{U8 zLX_EVpXsPii$e8BTtB5UX4#OzUG-4pV>6ib!>}*-s9#C}-Hm>!x#&x#iab*+ET(mr zt6^fwXcDnxD)tUE$amZTcX8Ws9KY$^uI=Ro@SZ7m3kJ+%%l$JL8dXr_$Ig+XE^8>> zb5FM_bcBzwDV1(l0Q=ZIat|BZvo?$zx|Jgz0aQB2NIj~YBXsD&UY_>#QUbey&x1Kw z7I7I07XabY+kiz|q&20O)C-^xf=n!`6`9aI zMn()EqRJ>@N+qQbQvztP*f20y-`A2xu%2ZR@B?bhV!{j&6iH;B5~on41YK)lg_2HgiAvi$TQO0a_41hMVL_)Nr|}t-7%6GPvki~B49yr&^21wz0jQ(K+A$SYUWI>k zo_O00)_ZOpaF86OX%;c*!7AxO1Ip%v*Xwq3s-;EKoCjwe+!9>rC{K&q58z!HaRwY? z2mAkeFAsXuLSficA7Ztl7TywhM1{Hy#(Z_pk#Cp{SIVrx!Z4nNE>~etzHJYj zrS;YOzWKiw`WTid0u4If#j6gG*YU9D`atP(7_2U&^5i?3xKm({-orVGYMHMiuG&^>N*E~qu8&K=E3}k`B|rUKXJ`9obbdRl1mO7 z!QY|Nk-5Ykb4odcs^&Sa|_W&Fh!ZKDGJ&+z)-JGz~1bw)WL9=l)98`IFyE6gDy)c z7h%I=OB73)Iylt=i?MpOpwTXfw!QrCoQ1OMr$eN-%9hi V6wJsgQe9}e&IN-p*MHyt>_4n+e$oH{ diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/retry.cpython-39.pyc b/apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/retry.cpython-39.pyc deleted file mode 100755 index 32b82ecf4bf6b59d24f744d436777b55708c30ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16270 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 diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/ssl_match_hostname.cpython-39.pyc b/apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/ssl_match_hostname.cpython-39.pyc deleted file mode 100755 index 66b4d3499621ac78d47c99514bd9b8b5c258a64b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3235 zcmZ`*&u<*J6&`YCc1Nq#S`}Q!4g!>L;KZxQyZ+HQhS3HQY$b5v%C6)%?Ka7DG~`Mn z&CZNTuI*indWc;hmjXHE(38~W(tn_TK>iG#3iJ|Rd+f#iIHOf$qn!ac9P*Lx@$tRy zW3;f)a`3$Ux9^fao^hOi*`Je7fS-?0ltjgyfx`)>DH)LJO9yn?u3GNE{mjYEc#iNU z*NJ0aP6zJqoQz&}xI6Gaak$6*=MMKp9IYgXd%r@HyUk?hNMmJa6OM;tRZk z?>wiQPIqzos|P!RjUpZIggd#?Su_$CyKFelVx8m}8_Imd?oad%+Sht`SC~*rWIBmb z_GCw7Y^;)On{961?Y+Prob_q*TJQbd<=&Op{+@gYem+7`ZB)X+CY=H01ZGgPcuPo~ z43jw0Vol0iu6JD%grT)33{5KxM>!v-w%rcHN8>21NB-`}23D)8|9sZZ3*A>mI?nd` zLdX56C{%wd(N7}DMHY(3Fg#52ZQGgkd%b>|Z1u-7#phaotdq2_R2shOjb1U46e@md zEw_z;RR1sRc48OR?6F5}jk$%Q{*7we*>_89*Y2@&4sc|LWZx@2?w)h@eSC3bf1+qN zO8W!ZzZRjoZ!_r}u;7aja_lAN>RK zTfEH|4(3bu5H|778;)-7wh!t4Tse2JP(ltTd1|`zYa2{9CZagbCATe|vyG2$vFlf^ zzQ4khs6*>|@8`X1tro+xqjhn!-`9Dbs$L@Wu$Rm2{*E4{eL0Nnw6~9DU8-lXxP^HGtc!g#xRl9|?>PuA~ktle7OTr++Za^04pJTt*84+jgQ=xKPo zlYv)6S{c8vY6d|nhI)jIHFHvoq9o(6W$swmctaU&8y8c4Oc2?Gqf95{nX0m<(s~Yk zY6X?!djzl7A^~Yr)IKGxmmYc-{lIOL)Bcis)(yxKeT&GSK|Z$5pFOtiqg28F4jLLE zugNni&uhBta3<&{VR?sD(X?mos5F#dX%ys%kh{7A(~Vj4=zB-X%$KxA=l;|IR9 zkf6H0X(fOgcsheaT}YyuDARr{qyk)pNjA()bDSlZD7wVB`PT01`=1YW9v+F@gJNZK z)*+{9hsgKPifxk59&4G0Qf=!^G_-R-G_`qo;2t{2SX$DO*lg^Q>DOydqgdB)YU4g1 zvOD)##JRMfsd{y^&r*SjrwFY$(dmToT&V0_&A1pQnP5@IqI5f#2&NGm=91~1L{+JB zah5(;dLQ(zvP~#nYTKuvw)5!sC%>)xG>CER$PG^SX-2y#v2y5@$DUJ4xssmnZ|F3Y}b_iiq;<<{ZqF}_Zi`Hdmdyy*7Lko z5)(g6W5n%F=o~iIZTRE1ckllrByCkK{7tXsb z6lu0>k#Lz^1SWD}Q-s^#0t~Q7F)8#|X6V|QR51if-^}AZp?ljx7eYuo`C_+Qhqq8z zQn2M2SS|Nhh4EK!T)Bbp<;Q?uttCO-ibYaDUutoaudpq|?aAz@hO->D#IY!JZQDr& zb@se|QD6y7j}irWdtZr?bSy=W-OHF7VbU zEI-K^E3e&;SSAFBC9;0+o>L*4oB{@ZS9gckZpP-nD{N z$Hv-kKE1QCc6;4))Oc$boVc3mb);gIeZ~b(OGM)Ysn+K)IypRblW7yJ6owfZ{TiGxXDO>>_ zv?n{xc=wcPJ&e+EeTkEnSL7vIS!6ZsPithZZg{4%s)0fXgId_Qh_i}VZkE=ooLkj%uCCMaP%k zEi9onP&t7|D4`%eN|!A4feZuEK=DW$F9?t{PJA2gB58vb?MMeN=8~nC^ku{AkTcY- zBrOnjNp(%>woNk(c^-$M{0X|{ySB1!GV^yvMJ{#ioB1%Daat%purijFYVTNAR^F)= oKC!RrCR}lR@QeDo{Gp{~brzL}jRXx^ZJ|XychPHI51K*yzpvb7?*IS* diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/ssltransport.cpython-39.pyc b/apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/ssltransport.cpython-39.pyc deleted file mode 100755 index 4339e2833f9c24fdc2697defae14e8d35c5a5cbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7451 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 diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/url.cpython-39.pyc b/apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/url.cpython-39.pyc deleted file mode 100755 index 04965578816393c0a56143e0db8a801c25bbf7b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10651 zcmbtaTWlLwdY&^gB!?6w+p;aiPBxj?mL)n8CEpZ>8(Wbj`O@05SF)1qST;T48A+5z zBzjImrDZOK zT(``1IqF&uo@ij&@4YJH<~s zZ#hStHO>5wjgr{Ysw1{SV_DcM8&1j3XlUg7l`uxBH(NV2@X#Jvo*cO`HuAxv=NIa=w;vBp z7?Ue11mhmRK*M<-6B2qh06%N%MJ z>7!XXkvlzU#i#o(Bb*2^Q<1F z!SiBe^HM#NQ9})-mQJ4;RUE+kUMP_1OvrD_pE}+?NOKd`OoCAF>?)Dkq*XQ}z@CDS$`5ov-HO<+M9_&X{+x=Qg zYr>M+H*Vg#{X4&V=kDyi``?+H zf3WcJC!c=TvX=_nSzcK!uC2QprE=w|@cioLXV123W9KhiymWc|{VN}2^NSmhB&jgpOwlk9Q2oiV`S~(sdRvU+Gi+SbeI`D_@gv#qwTt$@^Lj)$CZvo{6Jq zER&bFM#~j{)T_AF()y_57e?($#T#8J`k&c?JEgp{2|3BTZgFX}DqOrS zj#mAmOCQTyivI}4)VXmqlgnoUb0NB8`B@Sa5)&rAoH+x^ii-+(lYPW z)WwH2Ju|WRsP-;3dc62>@zFqz#O6TbbG3IL4~@Q7kRV4BXI`?$FWH%Ie)a0rtCwsD zloubJsbwAy4S0e4M>ENdotqd6#)60EA@7lhjX|KK6kh-T9ve=r>)kVF2Zu7}-W&e* z)t3wh_|Z!?8nTH3ZBFMe(fsfpBjYF*;|f$Dvox++IzRjaWtou@e37(bPMpVi;sunE zl|bI$Ntrj0Pw_69Pa>b@-7=rzJ^T>hw(AE3=V?Ihkx*L@ZtWjaCVtblZ=0Skss+C) z9CNuWOyN|7daStEl{@Ah8`#Z3)Dah^dJ_*gT#)3-BV}9DbUKq!j$LR z!uLKal4fzspE)9odDV&|#2GXQ6Zw3}`7EE$7@<~hJwekENlIu^qKlF=CEb+tAo-ew z$$Y-x+MbusM>6^b)eRweh{vNwN~(t$@-M{>zp8cND6`Fx_J9Kc1w9fR$l3~&H3jo$ zwU2J#keBSjI*GV9LJ|v&x>R(F{+3CrF>!YC5VPhd&LACBvEZ0)aoxdD+I}{}Cs|8NsVUZ@)(+8fI$G>m5p8>WMQ`DQRwRhwumZ|2t9>{M z`XYv^4b3|KXJxT&x=q`rV=0laKx)X0sl^7o+yKY4CHF`^8V3FSbB~uCAG89RHXXg9 z#o1w2OVOmGsy*{1rX&E8C-5_50%b8#XLYr%)%AL;93zH_HH8Xg7;84X0%Uy8K=a3?B29K;Mo|(FkI)d=u?rvRzz${}}D!LA*&TAA>>q-0r05fjO_fWVj_^(5N9-Qp!6q(FwHrKGEqW&56WNZYw_2{c+}tRG zxgiX$<9VhiS4(^Z{8EX8powPLPB?q88o?6{89^Yh3*(p@_7>pGHM<2i3p4q{1hRvm_Zx*qdXqrUyX|)Kj1t^%j6%-ymn#-M*65VOzz5@=4;(79H4Pgh-i7oClM=$3m+Ul*FAX4+NlK*oEtCL(k-JZz#bprN#^aqrq8#mI zx@NF6v;aNyceweMWoj+407(a(J!1YEjqo*tt0hMezYa^F#xVbU{;z0w!fJ)Jl#VRV zBxa*uraLsCh;UT-e3;7TH_E)~Qa+u}Kdstsvj+5dxqxD*I?pZpr0Ph)g+@h`p*;O9 zk;SJNqvSj#w0MCSf)aK&wPV3?-Ij)wrtCRtB6TIH-Z?xTX%d6!ab1TR-2MBno?exu zbUaBsaXf=kT^Hoi(1(76nQg;*6CZ#-SYvf9VAIN@u{tb+27qjb!Fm9Qtt;Zp(=-5( zRTb|NYy~Pw2eG;u=yk1KGfQBJnok3SHaJ_>NCiMCsN~wi){p?onf%hIHWRb|1BWKL z3|!60E`#&y@LbM3pS^H764#rsj7osJNYzOT3r1VfcZ9v<0^XL<(JgLJfj4Zz(g&~r z0tu3qi|{vK^8xteAnk@(b1pLQpqH9mfC4@&KWHNAxncWowfQdTCZOD2mM|zu4pYm{ zfrx|&7?wj<0ZI}MTRJ*gO$&`Wu!tZ*PCGB>J2bdlE{`tRVq(dzk!VkhUhM!NSxvg~#Ztb=pIJ%D$ik5EP(#r^ z7t}J22BX zDIX6E*b(ukp9XQn0^@!mh#Zb(%yg!x~Sk=CQz({4BsUX zxhk^dFF?y`!jP>GqVr!b2D%bs!OFX9*rHw3V<0uOfGHt zkQHHXg)w25R-90SRTFAZ>>=2j_8ncG`A&(;cvTpyEyI2hH)+IFm~0=0)or`vbTwBK z8gd*QiFKs8|Iw;Z;#o-;@p&5AvwDkOeoq63TSuZff>i6MLsqnO>R=g^<}tnf8-4uO z?kWj~N9st?p?%a;4086$(9^63GS&+zBb^<81-lzan>+&6?n zFWG>U`RpE!Bt;ibv>z7*?w2fuVf6RB)O&g!rT_w(|Z~#R*eQP=Zku9-F;4 zcX#s6ZEN!Sotgan)UBDjGxK5h?fV~(=O?G9=PZDgxJ7eJS@&jV^0%i6z2Y8Vs(@Pr zuVR-Z2TM5(4=>El&CJiteLORrpSm?UH!sN23=<%Ci3k<&B`AuQNkoAj;w&ox)Ik(} zJQ^n^BS)bINw5+%=oW0)el9<>Z!dbWOA)X@U8ljvq`q}OA}YRh`@ZJluqf;>s!;23GHhQ-H&l- zYYq+1lD^<+ba}{nPjNhrL9mWx?$IC zc`+vMydsuQ3A>0;>#`_sh!`#PJ|)DDR+5NC*JEm^lkX+!2RB2kq3k?y*4xM^tPct) zjXxNKO#KzPNqyic@Y}1DCfn}eCOQ;)rto;zkhCm8QaM0Bpd&)5K+J7eg2<_U3^^bs zY)2+?z%=A#Oeex3t#<4F9@8m`G|MoYiCF83gOErW?t|@<4}sWC6h(#~5p5vZH3+`I z(P_r|;$iZ^OX2YMVpw$9w-n+5<$}Q;tPE5*)odYQn-z_&HESh2^25o!?!0m22ZnwN{YlYDOd>L zSH5DkJ`y@AZN#vx@G4tjIr1l~N(FvpBfg{fiA@E@Gt7zT8yl^Ozl1U}IP8H%t!~tl z^;BR~Hn~bhHE5DCOf=PcS3MmV;-^7E{M1JqzY7Ot1Zl3rzW6(|PnS|WCTUR1v<%Aq z90qn8hIty{y0nfZtuyh@Jh9HapO*j)emA1%>~rS#1YH5tXuT^)e1*typi}A}B9p(a zV>I1Aj9M7|C^v%`r6`U0M^GA*rQ<;yrQ;}#`@JZQ1D1vWOGo`<%j#Eb>q!$}jyL)+ zDj*C>R*eIk=9Mw1&99jEMHFmAE+ExgWuW&t*Xw!-!AZ=bAIkpt+8f)Th8;M*cH&1$ zDUSEaA1U(gg>G4ldN(kq8*5=m;gTTjLYl@KYe5>5X`GynFB9Za*e7w&(nn}{=?KaU zw{B1D2u8S>lQI9?lPtnEPfQZl=+YbaCx}3gILo+}A+vC`qMYzW0lAOJab91OX2(1hRJb5CP z3v`e3C+rQ14p4wIC&9L3#WKYE+Kr3c^ZN)mVCaK)9f*raS3L))&OHHn zxuPe{kzW&okZtH*>$i!6;v|QnJFlxU@QJtqSu|G%cVnk0qM-g&iYFioWA^G`GoCtZ zB8<5kRHc|j$LPBx5SZ);R&R226hSAw5W=au{jD|>}RJFxdul4~api&`2fb_V$>n4l16@mC6z;hewU(m&K z(+U;+$bZeOmU~LE{|LjS70@asYq3GtE#rTz_I7)pcg=v7gNvque1*)L(G6cRvL#8k z2o#5LLO?i9v#=WLG z(r}3p>2xyUq|YL@DM#*L*mGlU^5$Lgv!}=e4O1V}MXXf!Vb7!wqXRbx(YLe|#P_JH zOI?%Xr%NMQ+@-=2l1z^bvcgO$R}s1(9+#2oFiHQBrYvwV*r5nXhgsdbmO5RKkxmO4 zg1W?Vd1C9x_?7omKcsP_0rmsRMOIjQpYPJ{b5tmOx*AvEsxbrU8|)ZT%8}39i-&Y- zTqVQTfLUT7N73#S8N+ybpm+P?adr%CbRBo3pq18C7&7$NeE+xj6O6M*Be$$eX~dB4 zNRA&g6D}FoSmiMEFq}XbHL!g5JA|Fg#rvJ-P$P zQjT^9CMyNFLsr>uVHmJ?q#N|tv_s^5QA|PnDRp8pR+G6*f%fGpUEL!dPD=?@;e=g< zvg>5(YQP_QR&XlBeV*hhMB8xzY_B+;fSUwqv?AOChy!4KSrr#NWTYml$E$O^JyNLFiz^p_ATSi{KX4YBFJI1y7HU0 zD9Zo4A9Jk`Ps?4Gm`f2qo=4+cXGkTN&YJM z3j43mO-{|+nVFx@cl1Z?Q)=7OA%ZmiA5s_1b9~`b^`ZHN>-p)KJGbvf0w%sh>8|{V zKcdewluT1X5*&%B6jm~-rRf))Hyv2V+3tEG#)pxoa1G}hi9nj76<}+;b@J`VlT%}< zmwJ){wml>Q$947=*!>v3Q($@eJIQ)g6)cXw|B1(rQ6Ao(L4G0Y(fS@4r&Fhlw~cp< JUL$4L{|m29kv9MU diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/wait.cpython-39.pyc b/apps/bitwarden_event_logs/lib/urllib3/util/__pycache__/wait.cpython-39.pyc deleted file mode 100755 index e5621041d1e2d6964ec31704bb839556af827df9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3104 zcmc&$&2JpH6(>0#J6h{wEi1NcE6y}+0&JUT1*ex5X^g+6rGNiTzRyk znH7gCS;A~kL_$9f>8a?ku-aVwzw|GdYcBP_kW+t;BPrIRr(7};$@fSu`S^Rk_sDo| zuF3HI_}^bA58q(y8&Y2X0w^D&6#qdbndB+!)h+LFtBamMD^lmc?YW$twxBtwci{JY z&URS#$YrW=vdJ0!Va&ytM`NF}OsugzCcS_0Uho?xec3o?vY}?qoL*Cj{aZ2^disxY zMmEn`Z#E0$ty+=~<(!;9=e;?(A{S%}(!9Ja7cpx=-jPdaTk;Kg_ngJI<(u-JT*iz= z`AFWE4!bmh_>xCOJ@Tr@G(GcjA+6REb&7 z^;X^=D)U6^Tvv0)=!*uqj%^!&?eO}?p?r)|%%W0^5CnQ;jnE#ty4Wp< zqAWg8QB*af=pdKll;qhcIvmGoJ@S(2TbOODZ=ZJZk?9npbes)4%Je(&XjF7|6LTDE zsj^5Nsmw%azDJ!|XLYrcCcB-nPSJeO8Ji^SkkzZB6KldenI}*M-0E%d%7ONZ%vMe@ z@{PS*bP!MNUjKwp0=Y5Miw2B5;}elJ4L?KgJvI@i&DxS3w#8g#gmL!WfjIdGXA=kf z+$^1aPm24@`1_5aqd)tSWsgfizh7n(j+Mob>46j^*Xs|3K9U&Q0cX)VUpR*=ugi7c z8e3zBD-yf5oo?kM*+7%O+d<_G(mXbraI6{|TQzn3LZzy2Dob=lLk(JwiHM=>^h)b97f#xccnkb`@xtK+va(DAQZ8s~oK$KRljY`ihQ^V-%{xj7d z_WWbpT7Y}Lrg>1cW!ABIaTgUKPe{{WlZxNsEv|orksG-C z=pa83ABx|jn#LX6bAkEWrEB=U*nk&+`I^-n(hPs$Ua-Hizpxh`xb9vE&i>2>TsqP{ z4Ynv@9LA2Ilk~J9i4b)eqCWAE(7ftCzoO2l^p? zVtrcqpKW~l>C&$vKN%b-3`XlyH*+8*L{VH^98W7#nM_C@$w0U10 zj+3J}MK%uOqc}<9-Bh*fB>Ph6dsw0Jhe+Cc#WeAjvN1rO0`7peq`eu}@1wq97kPFD z&8DA$*d$VL>!i0`PHdGfrkMkdKK`}C@2kCZN;;eiB( zP@1!si6wDOPzo2zJL>{XX1!**J%r76!H4xr@*_$N#k;8JszO#10&zFX7e(N-9^l#F S1^1eFnoEHj%moY0#{U4E_o!q5 diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/connection.py b/apps/bitwarden_event_logs/lib/urllib3/util/connection.py deleted file mode 100755 index 6af1138f..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/util/connection.py +++ /dev/null @@ -1,149 +0,0 @@ -from __future__ import absolute_import - -import socket - -from ..contrib import _appengine_environ -from ..exceptions import LocationParseError -from ..packages import six -from .wait import NoWayToWaitForSocketError, wait_for_read - - -def is_connection_dropped(conn): # Platform-specific - """ - Returns True if the connection is dropped and should be closed. - - :param conn: - :class:`http.client.HTTPConnection` object. - - Note: For platforms like AppEngine, this will always return ``False`` to - let the platform handle connection recycling transparently for us. - """ - sock = getattr(conn, "sock", False) - if sock is False: # Platform-specific: AppEngine - return False - if sock is None: # Connection already closed (such as by httplib). - return True - try: - # Returns True if readable, which here means it's been dropped - return wait_for_read(sock, timeout=0.0) - except NoWayToWaitForSocketError: # Platform-specific: AppEngine - return False - - -# This function is copied from socket.py in the Python 2.7 standard -# library test suite. Added to its signature is only `socket_options`. -# One additional modification is that we avoid binding to IPv6 servers -# discovered in DNS if the system doesn't have IPv6 functionality. -def create_connection( - address, - timeout=socket._GLOBAL_DEFAULT_TIMEOUT, - source_address=None, - socket_options=None, -): - """Connect to *address* and return the socket object. - - Convenience function. Connect to *address* (a 2-tuple ``(host, - port)``) and return the socket object. Passing the optional - *timeout* parameter will set the timeout on the socket instance - before attempting to connect. If no *timeout* is supplied, the - global default timeout setting returned by :func:`socket.getdefaulttimeout` - is used. If *source_address* is set it must be a tuple of (host, port) - for the socket to bind as a source address before making the connection. - An host of '' or port 0 tells the OS to use the default. - """ - - host, port = address - if host.startswith("["): - host = host.strip("[]") - err = None - - # Using the value from allowed_gai_family() in the context of getaddrinfo lets - # us select whether to work with IPv4 DNS records, IPv6 records, or both. - # The original create_connection function always returns all records. - family = allowed_gai_family() - - try: - host.encode("idna") - except UnicodeError: - return six.raise_from( - LocationParseError(u"'%s', label empty or too long" % host), None - ) - - for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): - af, socktype, proto, canonname, sa = res - sock = None - try: - sock = socket.socket(af, socktype, proto) - - # If provided, set socket level options before connecting. - _set_socket_options(sock, socket_options) - - if timeout is not socket._GLOBAL_DEFAULT_TIMEOUT: - sock.settimeout(timeout) - if source_address: - sock.bind(source_address) - sock.connect(sa) - return sock - - except socket.error as e: - err = e - if sock is not None: - sock.close() - sock = None - - if err is not None: - raise err - - raise socket.error("getaddrinfo returns an empty list") - - -def _set_socket_options(sock, options): - if options is None: - return - - for opt in options: - sock.setsockopt(*opt) - - -def allowed_gai_family(): - """This function is designed to work in the context of - getaddrinfo, where family=socket.AF_UNSPEC is the default and - will perform a DNS search for both IPv6 and IPv4 records.""" - - family = socket.AF_INET - if HAS_IPV6: - family = socket.AF_UNSPEC - return family - - -def _has_ipv6(host): - """Returns True if the system can bind an IPv6 address.""" - sock = None - has_ipv6 = False - - # App Engine doesn't support IPV6 sockets and actually has a quota on the - # number of sockets that can be used, so just early out here instead of - # creating a socket needlessly. - # See https://github.com/urllib3/urllib3/issues/1446 - if _appengine_environ.is_appengine_sandbox(): - return False - - if socket.has_ipv6: - # has_ipv6 returns true if cPython was compiled with IPv6 support. - # It does not tell us if the system has IPv6 support enabled. To - # determine that we must bind to an IPv6 address. - # https://github.com/urllib3/urllib3/pull/611 - # https://bugs.python.org/issue658327 - try: - sock = socket.socket(socket.AF_INET6) - sock.bind((host, 0)) - has_ipv6 = True - except Exception: - pass - - if sock: - sock.close() - return has_ipv6 - - -HAS_IPV6 = _has_ipv6("::1") diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/proxy.py b/apps/bitwarden_event_logs/lib/urllib3/util/proxy.py deleted file mode 100755 index 2199cc7b..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/util/proxy.py +++ /dev/null @@ -1,57 +0,0 @@ -from .ssl_ import create_urllib3_context, resolve_cert_reqs, resolve_ssl_version - - -def connection_requires_http_tunnel( - proxy_url=None, proxy_config=None, destination_scheme=None -): - """ - Returns True if the connection requires an HTTP CONNECT through the proxy. - - :param URL proxy_url: - URL of the proxy. - :param ProxyConfig proxy_config: - Proxy configuration from poolmanager.py - :param str destination_scheme: - The scheme of the destination. (i.e https, http, etc) - """ - # If we're not using a proxy, no way to use a tunnel. - if proxy_url is None: - return False - - # HTTP destinations never require tunneling, we always forward. - if destination_scheme == "http": - return False - - # Support for forwarding with HTTPS proxies and HTTPS destinations. - if ( - proxy_url.scheme == "https" - and proxy_config - and proxy_config.use_forwarding_for_https - ): - return False - - # Otherwise always use a tunnel. - return True - - -def create_proxy_ssl_context( - ssl_version, cert_reqs, ca_certs=None, ca_cert_dir=None, ca_cert_data=None -): - """ - Generates a default proxy ssl context if one hasn't been provided by the - user. - """ - ssl_context = create_urllib3_context( - ssl_version=resolve_ssl_version(ssl_version), - cert_reqs=resolve_cert_reqs(cert_reqs), - ) - - if ( - not ca_certs - and not ca_cert_dir - and not ca_cert_data - and hasattr(ssl_context, "load_default_certs") - ): - ssl_context.load_default_certs() - - return ssl_context diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/queue.py b/apps/bitwarden_event_logs/lib/urllib3/util/queue.py deleted file mode 100755 index 41784104..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/util/queue.py +++ /dev/null @@ -1,22 +0,0 @@ -import collections - -from ..packages import six -from ..packages.six.moves import queue - -if six.PY2: - # Queue is imported for side effects on MS Windows. See issue #229. - import Queue as _unused_module_Queue # noqa: F401 - - -class LifoQueue(queue.Queue): - def _init(self, _): - self.queue = collections.deque() - - def _qsize(self, len=len): - return len(self.queue) - - def _put(self, item): - self.queue.append(item) - - def _get(self): - return self.queue.pop() diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/request.py b/apps/bitwarden_event_logs/lib/urllib3/util/request.py deleted file mode 100755 index b574b081..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/util/request.py +++ /dev/null @@ -1,146 +0,0 @@ -from __future__ import absolute_import - -from base64 import b64encode - -from ..exceptions import UnrewindableBodyError -from ..packages.six import b, integer_types - -# Pass as a value within ``headers`` to skip -# emitting some HTTP headers that are added automatically. -# The only headers that are supported are ``Accept-Encoding``, -# ``Host``, and ``User-Agent``. -SKIP_HEADER = "@@@SKIP_HEADER@@@" -SKIPPABLE_HEADERS = frozenset(["accept-encoding", "host", "user-agent"]) - -ACCEPT_ENCODING = "gzip,deflate" -try: - try: - import brotlicffi as _unused_module_brotli # noqa: F401 - except ImportError: - import brotli as _unused_module_brotli # noqa: F401 -except ImportError: - pass -else: - ACCEPT_ENCODING += ",br" - -_FAILEDTELL = object() - - -def make_headers( - keep_alive=None, - accept_encoding=None, - user_agent=None, - basic_auth=None, - proxy_basic_auth=None, - disable_cache=None, -): - """ - Shortcuts for generating request headers. - - :param keep_alive: - If ``True``, adds 'connection: keep-alive' header. - - :param accept_encoding: - Can be a boolean, list, or string. - ``True`` translates to 'gzip,deflate'. - List will get joined by comma. - String will be used as provided. - - :param user_agent: - String representing the user-agent you want, such as - "python-urllib3/0.6" - - :param basic_auth: - Colon-separated username:password string for 'authorization: basic ...' - auth header. - - :param proxy_basic_auth: - Colon-separated username:password string for 'proxy-authorization: basic ...' - auth header. - - :param disable_cache: - If ``True``, adds 'cache-control: no-cache' header. - - Example:: - - >>> make_headers(keep_alive=True, user_agent="Batman/1.0") - {'connection': 'keep-alive', 'user-agent': 'Batman/1.0'} - >>> make_headers(accept_encoding=True) - {'accept-encoding': 'gzip,deflate'} - """ - headers = {} - if accept_encoding: - if isinstance(accept_encoding, str): - pass - elif isinstance(accept_encoding, list): - accept_encoding = ",".join(accept_encoding) - else: - accept_encoding = ACCEPT_ENCODING - headers["accept-encoding"] = accept_encoding - - if user_agent: - headers["user-agent"] = user_agent - - if keep_alive: - headers["connection"] = "keep-alive" - - if basic_auth: - headers["authorization"] = "Basic " + b64encode(b(basic_auth)).decode("utf-8") - - if proxy_basic_auth: - headers["proxy-authorization"] = "Basic " + b64encode( - b(proxy_basic_auth) - ).decode("utf-8") - - if disable_cache: - headers["cache-control"] = "no-cache" - - return headers - - -def set_file_position(body, pos): - """ - If a position is provided, move file to that point. - Otherwise, we'll attempt to record a position for future use. - """ - if pos is not None: - rewind_body(body, pos) - elif getattr(body, "tell", None) is not None: - try: - pos = body.tell() - except (IOError, OSError): - # This differentiates from None, allowing us to catch - # a failed `tell()` later when trying to rewind the body. - pos = _FAILEDTELL - - return pos - - -def rewind_body(body, body_pos): - """ - Attempt to rewind body to a certain position. - Primarily used for request redirects and retries. - - :param body: - File-like object that supports seek. - - :param int pos: - Position to seek to in file. - """ - body_seek = getattr(body, "seek", None) - if body_seek is not None and isinstance(body_pos, integer_types): - try: - body_seek(body_pos) - except (IOError, OSError): - raise UnrewindableBodyError( - "An error occurred when rewinding request body for redirect/retry." - ) - elif body_pos is _FAILEDTELL: - raise UnrewindableBodyError( - "Unable to record file position for rewinding " - "request body during a redirect/retry." - ) - else: - raise ValueError( - "body_pos must be of type integer, instead it was %s." % type(body_pos) - ) diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/response.py b/apps/bitwarden_event_logs/lib/urllib3/util/response.py deleted file mode 100755 index 5ea609cc..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/util/response.py +++ /dev/null @@ -1,107 +0,0 @@ -from __future__ import absolute_import - -from email.errors import MultipartInvariantViolationDefect, StartBoundaryNotFoundDefect - -from ..exceptions import HeaderParsingError -from ..packages.six.moves import http_client as httplib - - -def is_fp_closed(obj): - """ - Checks whether a given file-like object is closed. - - :param obj: - The file-like object to check. - """ - - try: - # Check `isclosed()` first, in case Python3 doesn't set `closed`. - # GH Issue #928 - return obj.isclosed() - except AttributeError: - pass - - try: - # Check via the official file-like-object way. - return obj.closed - except AttributeError: - pass - - try: - # Check if the object is a container for another file-like object that - # gets released on exhaustion (e.g. HTTPResponse). - return obj.fp is None - except AttributeError: - pass - - raise ValueError("Unable to determine whether fp is closed.") - - -def assert_header_parsing(headers): - """ - Asserts whether all headers have been successfully parsed. - Extracts encountered errors from the result of parsing headers. - - Only works on Python 3. - - :param http.client.HTTPMessage headers: Headers to verify. - - :raises urllib3.exceptions.HeaderParsingError: - If parsing errors are found. - """ - - # This will fail silently if we pass in the wrong kind of parameter. - # To make debugging easier add an explicit check. - if not isinstance(headers, httplib.HTTPMessage): - raise TypeError("expected httplib.Message, got {0}.".format(type(headers))) - - defects = getattr(headers, "defects", None) - get_payload = getattr(headers, "get_payload", None) - - unparsed_data = None - if get_payload: - # get_payload is actually email.message.Message.get_payload; - # we're only interested in the result if it's not a multipart message - if not headers.is_multipart(): - payload = get_payload() - - if isinstance(payload, (bytes, str)): - unparsed_data = payload - if defects: - # httplib is assuming a response body is available - # when parsing headers even when httplib only sends - # header data to parse_headers() This results in - # defects on multipart responses in particular. - # See: https://github.com/urllib3/urllib3/issues/800 - - # So we ignore the following defects: - # - StartBoundaryNotFoundDefect: - # The claimed start boundary was never found. - # - MultipartInvariantViolationDefect: - # A message claimed to be a multipart but no subparts were found. - defects = [ - defect - for defect in defects - if not isinstance( - defect, (StartBoundaryNotFoundDefect, MultipartInvariantViolationDefect) - ) - ] - - if defects or unparsed_data: - raise HeaderParsingError(defects=defects, unparsed_data=unparsed_data) - - -def is_response_to_head(response): - """ - Checks whether the request of a response has been a HEAD-request. - Handles the quirks of AppEngine. - - :param http.client.HTTPResponse response: - Response to check if the originating request - used 'HEAD' as a method. - """ - # FIXME: Can we do this somehow without accessing private httplib _method? - method = response._method - if isinstance(method, int): # Platform-specific: Appengine - return method == 3 - return method.upper() == "HEAD" diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/retry.py b/apps/bitwarden_event_logs/lib/urllib3/util/retry.py deleted file mode 100755 index 9a1e90d0..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/util/retry.py +++ /dev/null @@ -1,622 +0,0 @@ -from __future__ import absolute_import - -import email -import logging -import re -import time -import warnings -from collections import namedtuple -from itertools import takewhile - -from ..exceptions import ( - ConnectTimeoutError, - InvalidHeader, - MaxRetryError, - ProtocolError, - ProxyError, - ReadTimeoutError, - ResponseError, -) -from ..packages import six - -log = logging.getLogger(__name__) - - -# Data structure for representing the metadata of requests that result in a retry. -RequestHistory = namedtuple( - "RequestHistory", ["method", "url", "error", "status", "redirect_location"] -) - - -# TODO: In v2 we can remove this sentinel and metaclass with deprecated options. -_Default = object() - - -class _RetryMeta(type): - @property - def DEFAULT_METHOD_WHITELIST(cls): - warnings.warn( - "Using 'Retry.DEFAULT_METHOD_WHITELIST' is deprecated and " - "will be removed in v2.0. Use 'Retry.DEFAULT_ALLOWED_METHODS' instead", - DeprecationWarning, - ) - return cls.DEFAULT_ALLOWED_METHODS - - @DEFAULT_METHOD_WHITELIST.setter - def DEFAULT_METHOD_WHITELIST(cls, value): - warnings.warn( - "Using 'Retry.DEFAULT_METHOD_WHITELIST' is deprecated and " - "will be removed in v2.0. Use 'Retry.DEFAULT_ALLOWED_METHODS' instead", - DeprecationWarning, - ) - cls.DEFAULT_ALLOWED_METHODS = value - - @property - def DEFAULT_REDIRECT_HEADERS_BLACKLIST(cls): - warnings.warn( - "Using 'Retry.DEFAULT_REDIRECT_HEADERS_BLACKLIST' is deprecated and " - "will be removed in v2.0. Use 'Retry.DEFAULT_REMOVE_HEADERS_ON_REDIRECT' instead", - DeprecationWarning, - ) - return cls.DEFAULT_REMOVE_HEADERS_ON_REDIRECT - - @DEFAULT_REDIRECT_HEADERS_BLACKLIST.setter - def DEFAULT_REDIRECT_HEADERS_BLACKLIST(cls, value): - warnings.warn( - "Using 'Retry.DEFAULT_REDIRECT_HEADERS_BLACKLIST' is deprecated and " - "will be removed in v2.0. Use 'Retry.DEFAULT_REMOVE_HEADERS_ON_REDIRECT' instead", - DeprecationWarning, - ) - cls.DEFAULT_REMOVE_HEADERS_ON_REDIRECT = value - - @property - def BACKOFF_MAX(cls): - warnings.warn( - "Using 'Retry.BACKOFF_MAX' is deprecated and " - "will be removed in v2.0. Use 'Retry.DEFAULT_BACKOFF_MAX' instead", - DeprecationWarning, - ) - return cls.DEFAULT_BACKOFF_MAX - - @BACKOFF_MAX.setter - def BACKOFF_MAX(cls, value): - warnings.warn( - "Using 'Retry.BACKOFF_MAX' is deprecated and " - "will be removed in v2.0. Use 'Retry.DEFAULT_BACKOFF_MAX' instead", - DeprecationWarning, - ) - cls.DEFAULT_BACKOFF_MAX = value - - -@six.add_metaclass(_RetryMeta) -class Retry(object): - """Retry configuration. - - Each retry attempt will create a new Retry object with updated values, so - they can be safely reused. - - Retries can be defined as a default for a pool:: - - retries = Retry(connect=5, read=2, redirect=5) - http = PoolManager(retries=retries) - response = http.request('GET', 'http://example.com/') - - Or per-request (which overrides the default for the pool):: - - response = http.request('GET', 'http://example.com/', retries=Retry(10)) - - Retries can be disabled by passing ``False``:: - - response = http.request('GET', 'http://example.com/', retries=False) - - Errors will be wrapped in :class:`~urllib3.exceptions.MaxRetryError` unless - retries are disabled, in which case the causing exception will be raised. - - :param int total: - Total number of retries to allow. Takes precedence over other counts. - - Set to ``None`` to remove this constraint and fall back on other - counts. - - Set to ``0`` to fail on the first retry. - - Set to ``False`` to disable and imply ``raise_on_redirect=False``. - - :param int connect: - How many connection-related errors to retry on. - - These are errors raised before the request is sent to the remote server, - which we assume has not triggered the server to process the request. - - Set to ``0`` to fail on the first retry of this type. - - :param int read: - How many times to retry on read errors. - - These errors are raised after the request was sent to the server, so the - request may have side-effects. - - Set to ``0`` to fail on the first retry of this type. - - :param int redirect: - How many redirects to perform. Limit this to avoid infinite redirect - loops. - - A redirect is a HTTP response with a status code 301, 302, 303, 307 or - 308. - - Set to ``0`` to fail on the first retry of this type. - - Set to ``False`` to disable and imply ``raise_on_redirect=False``. - - :param int status: - How many times to retry on bad status codes. - - These are retries made on responses, where status code matches - ``status_forcelist``. - - Set to ``0`` to fail on the first retry of this type. - - :param int other: - How many times to retry on other errors. - - Other errors are errors that are not connect, read, redirect or status errors. - These errors might be raised after the request was sent to the server, so the - request might have side-effects. - - Set to ``0`` to fail on the first retry of this type. - - If ``total`` is not set, it's a good idea to set this to 0 to account - for unexpected edge cases and avoid infinite retry loops. - - :param iterable allowed_methods: - Set of uppercased HTTP method verbs that we should retry on. - - By default, we only retry on methods which are considered to be - idempotent (multiple requests with the same parameters end with the - same state). See :attr:`Retry.DEFAULT_ALLOWED_METHODS`. - - Set to a ``False`` value to retry on any verb. - - .. warning:: - - Previously this parameter was named ``method_whitelist``, that - usage is deprecated in v1.26.0 and will be removed in v2.0. - - :param iterable status_forcelist: - A set of integer HTTP status codes that we should force a retry on. - A retry is initiated if the request method is in ``allowed_methods`` - and the response status code is in ``status_forcelist``. - - By default, this is disabled with ``None``. - - :param float backoff_factor: - A backoff factor to apply between attempts after the second try - (most errors are resolved immediately by a second try without a - delay). urllib3 will sleep for:: - - {backoff factor} * (2 ** ({number of total retries} - 1)) - - seconds. If the backoff_factor is 0.1, then :func:`.sleep` will sleep - for [0.0s, 0.2s, 0.4s, ...] between retries. It will never be longer - than :attr:`Retry.DEFAULT_BACKOFF_MAX`. - - By default, backoff is disabled (set to 0). - - :param bool raise_on_redirect: Whether, if the number of redirects is - exhausted, to raise a MaxRetryError, or to return a response with a - response code in the 3xx range. - - :param bool raise_on_status: Similar meaning to ``raise_on_redirect``: - whether we should raise an exception, or return a response, - if status falls in ``status_forcelist`` range and retries have - been exhausted. - - :param tuple history: The history of the request encountered during - each call to :meth:`~Retry.increment`. The list is in the order - the requests occurred. Each list item is of class :class:`RequestHistory`. - - :param bool respect_retry_after_header: - Whether to respect Retry-After header on status codes defined as - :attr:`Retry.RETRY_AFTER_STATUS_CODES` or not. - - :param iterable remove_headers_on_redirect: - Sequence of headers to remove from the request when a response - indicating a redirect is returned before firing off the redirected - request. - """ - - #: Default methods to be used for ``allowed_methods`` - DEFAULT_ALLOWED_METHODS = frozenset( - ["HEAD", "GET", "PUT", "DELETE", "OPTIONS", "TRACE"] - ) - - #: Default status codes to be used for ``status_forcelist`` - RETRY_AFTER_STATUS_CODES = frozenset([413, 429, 503]) - - #: Default headers to be used for ``remove_headers_on_redirect`` - DEFAULT_REMOVE_HEADERS_ON_REDIRECT = frozenset( - ["Cookie", "Authorization", "Proxy-Authorization"] - ) - - #: Maximum backoff time. - DEFAULT_BACKOFF_MAX = 120 - - def __init__( - self, - total=10, - connect=None, - read=None, - redirect=None, - status=None, - other=None, - allowed_methods=_Default, - status_forcelist=None, - backoff_factor=0, - raise_on_redirect=True, - raise_on_status=True, - history=None, - respect_retry_after_header=True, - remove_headers_on_redirect=_Default, - # TODO: Deprecated, remove in v2.0 - method_whitelist=_Default, - ): - - if method_whitelist is not _Default: - if allowed_methods is not _Default: - raise ValueError( - "Using both 'allowed_methods' and " - "'method_whitelist' together is not allowed. " - "Instead only use 'allowed_methods'" - ) - warnings.warn( - "Using 'method_whitelist' with Retry is deprecated and " - "will be removed in v2.0. Use 'allowed_methods' instead", - DeprecationWarning, - stacklevel=2, - ) - allowed_methods = method_whitelist - if allowed_methods is _Default: - allowed_methods = self.DEFAULT_ALLOWED_METHODS - if remove_headers_on_redirect is _Default: - remove_headers_on_redirect = self.DEFAULT_REMOVE_HEADERS_ON_REDIRECT - - self.total = total - self.connect = connect - self.read = read - self.status = status - self.other = other - - if redirect is False or total is False: - redirect = 0 - raise_on_redirect = False - - self.redirect = redirect - self.status_forcelist = status_forcelist or set() - self.allowed_methods = allowed_methods - self.backoff_factor = backoff_factor - self.raise_on_redirect = raise_on_redirect - self.raise_on_status = raise_on_status - self.history = history or tuple() - self.respect_retry_after_header = respect_retry_after_header - self.remove_headers_on_redirect = frozenset( - [h.lower() for h in remove_headers_on_redirect] - ) - - def new(self, **kw): - params = dict( - total=self.total, - connect=self.connect, - read=self.read, - redirect=self.redirect, - status=self.status, - other=self.other, - status_forcelist=self.status_forcelist, - backoff_factor=self.backoff_factor, - raise_on_redirect=self.raise_on_redirect, - raise_on_status=self.raise_on_status, - history=self.history, - remove_headers_on_redirect=self.remove_headers_on_redirect, - respect_retry_after_header=self.respect_retry_after_header, - ) - - # TODO: If already given in **kw we use what's given to us - # If not given we need to figure out what to pass. We decide - # based on whether our class has the 'method_whitelist' property - # and if so we pass the deprecated 'method_whitelist' otherwise - # we use 'allowed_methods'. Remove in v2.0 - if "method_whitelist" not in kw and "allowed_methods" not in kw: - if "method_whitelist" in self.__dict__: - warnings.warn( - "Using 'method_whitelist' with Retry is deprecated and " - "will be removed in v2.0. Use 'allowed_methods' instead", - DeprecationWarning, - ) - params["method_whitelist"] = self.allowed_methods - else: - params["allowed_methods"] = self.allowed_methods - - params.update(kw) - return type(self)(**params) - - @classmethod - def from_int(cls, retries, redirect=True, default=None): - """Backwards-compatibility for the old retries format.""" - if retries is None: - retries = default if default is not None else cls.DEFAULT - - if isinstance(retries, Retry): - return retries - - redirect = bool(redirect) and None - new_retries = cls(retries, redirect=redirect) - log.debug("Converted retries value: %r -> %r", retries, new_retries) - return new_retries - - def get_backoff_time(self): - """Formula for computing the current backoff - - :rtype: float - """ - # We want to consider only the last consecutive errors sequence (Ignore redirects). - consecutive_errors_len = len( - list( - takewhile(lambda x: x.redirect_location is None, reversed(self.history)) - ) - ) - if consecutive_errors_len <= 1: - return 0 - - backoff_value = self.backoff_factor * (2 ** (consecutive_errors_len - 1)) - return min(self.DEFAULT_BACKOFF_MAX, backoff_value) - - def parse_retry_after(self, retry_after): - # Whitespace: https://tools.ietf.org/html/rfc7230#section-3.2.4 - if re.match(r"^\s*[0-9]+\s*$", retry_after): - seconds = int(retry_after) - else: - retry_date_tuple = email.utils.parsedate_tz(retry_after) - if retry_date_tuple is None: - raise InvalidHeader("Invalid Retry-After header: %s" % retry_after) - if retry_date_tuple[9] is None: # Python 2 - # Assume UTC if no timezone was specified - # On Python2.7, parsedate_tz returns None for a timezone offset - # instead of 0 if no timezone is given, where mktime_tz treats - # a None timezone offset as local time. - retry_date_tuple = retry_date_tuple[:9] + (0,) + retry_date_tuple[10:] - - retry_date = email.utils.mktime_tz(retry_date_tuple) - seconds = retry_date - time.time() - - if seconds < 0: - seconds = 0 - - return seconds - - def get_retry_after(self, response): - """Get the value of Retry-After in seconds.""" - - retry_after = response.headers.get("Retry-After") - - if retry_after is None: - return None - - return self.parse_retry_after(retry_after) - - def sleep_for_retry(self, response=None): - retry_after = self.get_retry_after(response) - if retry_after: - time.sleep(retry_after) - return True - - return False - - def _sleep_backoff(self): - backoff = self.get_backoff_time() - if backoff <= 0: - return - time.sleep(backoff) - - def sleep(self, response=None): - """Sleep between retry attempts. - - This method will respect a server's ``Retry-After`` response header - and sleep the duration of the time requested. If that is not present, it - will use an exponential backoff. By default, the backoff factor is 0 and - this method will return immediately. - """ - - if self.respect_retry_after_header and response: - slept = self.sleep_for_retry(response) - if slept: - return - - self._sleep_backoff() - - def _is_connection_error(self, err): - """Errors when we're fairly sure that the server did not receive the - request, so it should be safe to retry. - """ - if isinstance(err, ProxyError): - err = err.original_error - return isinstance(err, ConnectTimeoutError) - - def _is_read_error(self, err): - """Errors that occur after the request has been started, so we should - assume that the server began processing it. - """ - return isinstance(err, (ReadTimeoutError, ProtocolError)) - - def _is_method_retryable(self, method): - """Checks if a given HTTP method should be retried upon, depending if - it is included in the allowed_methods - """ - # TODO: For now favor if the Retry implementation sets its own method_whitelist - # property outside of our constructor to avoid breaking custom implementations. - if "method_whitelist" in self.__dict__: - warnings.warn( - "Using 'method_whitelist' with Retry is deprecated and " - "will be removed in v2.0. Use 'allowed_methods' instead", - DeprecationWarning, - ) - allowed_methods = self.method_whitelist - else: - allowed_methods = self.allowed_methods - - if allowed_methods and method.upper() not in allowed_methods: - return False - return True - - def is_retry(self, method, status_code, has_retry_after=False): - """Is this method/status code retryable? (Based on allowlists and control - variables such as the number of total retries to allow, whether to - respect the Retry-After header, whether this header is present, and - whether the returned status code is on the list of status codes to - be retried upon on the presence of the aforementioned header) - """ - if not self._is_method_retryable(method): - return False - - if self.status_forcelist and status_code in self.status_forcelist: - return True - - return ( - self.total - and self.respect_retry_after_header - and has_retry_after - and (status_code in self.RETRY_AFTER_STATUS_CODES) - ) - - def is_exhausted(self): - """Are we out of retries?""" - retry_counts = ( - self.total, - self.connect, - self.read, - self.redirect, - self.status, - self.other, - ) - retry_counts = list(filter(None, retry_counts)) - if not retry_counts: - return False - - return min(retry_counts) < 0 - - def increment( - self, - method=None, - url=None, - response=None, - error=None, - _pool=None, - _stacktrace=None, - ): - """Return a new Retry object with incremented retry counters. - - :param response: A response object, or None, if the server did not - return a response. - :type response: :class:`~urllib3.response.HTTPResponse` - :param Exception error: An error encountered during the request, or - None if the response was received successfully. - - :return: A new ``Retry`` object. - """ - if self.total is False and error: - # Disabled, indicate to re-raise the error. - raise six.reraise(type(error), error, _stacktrace) - - total = self.total - if total is not None: - total -= 1 - - connect = self.connect - read = self.read - redirect = self.redirect - status_count = self.status - other = self.other - cause = "unknown" - status = None - redirect_location = None - - if error and self._is_connection_error(error): - # Connect retry? - if connect is False: - raise six.reraise(type(error), error, _stacktrace) - elif connect is not None: - connect -= 1 - - elif error and self._is_read_error(error): - # Read retry? - if read is False or not self._is_method_retryable(method): - raise six.reraise(type(error), error, _stacktrace) - elif read is not None: - read -= 1 - - elif error: - # Other retry? - if other is not None: - other -= 1 - - elif response and response.get_redirect_location(): - # Redirect retry? - if redirect is not None: - redirect -= 1 - cause = "too many redirects" - redirect_location = response.get_redirect_location() - status = response.status - - else: - # Incrementing because of a server error like a 500 in - # status_forcelist and the given method is in the allowed_methods - cause = ResponseError.GENERIC_ERROR - if response and response.status: - if status_count is not None: - status_count -= 1 - cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) - status = response.status - - history = self.history + ( - RequestHistory(method, url, error, status, redirect_location), - ) - - new_retry = self.new( - total=total, - connect=connect, - read=read, - redirect=redirect, - status=status_count, - other=other, - history=history, - ) - - if new_retry.is_exhausted(): - raise MaxRetryError(_pool, url, error or ResponseError(cause)) - - log.debug("Incremented Retry for (url='%s'): %r", url, new_retry) - - return new_retry - - def __repr__(self): - return ( - "{cls.__name__}(total={self.total}, connect={self.connect}, " - "read={self.read}, redirect={self.redirect}, status={self.status})" - ).format(cls=type(self), self=self) - - def __getattr__(self, item): - if item == "method_whitelist": - # TODO: Remove this deprecated alias in v2.0 - warnings.warn( - "Using 'method_whitelist' with Retry is deprecated and " - "will be removed in v2.0. Use 'allowed_methods' instead", - DeprecationWarning, - ) - return self.allowed_methods - try: - return getattr(super(Retry, self), item) - except AttributeError: - return getattr(Retry, item) - - -# For backwards compatibility (equivalent to pre-v1.9): -Retry.DEFAULT = Retry(3) diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/ssl_.py b/apps/bitwarden_event_logs/lib/urllib3/util/ssl_.py deleted file mode 100755 index f26809c7..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/util/ssl_.py +++ /dev/null @@ -1,504 +0,0 @@ -from __future__ import absolute_import - -import hashlib -import hmac -import os -import sys -import warnings -from binascii import hexlify, unhexlify - -from ..exceptions import ( - InsecurePlatformWarning, - ProxySchemeUnsupported, - SNIMissingWarning, - SSLError, -) -from ..packages import six -from .url import BRACELESS_IPV6_ADDRZ_RE, IPV4_RE - -SSLContext = None -SSLTransport = None -HAS_SNI = False -IS_PYOPENSSL = False -IS_SECURETRANSPORT = False -ALPN_PROTOCOLS = ["http/1.1"] - -# Maps the length of a digest to a possible hash function producing this digest -HASHFUNC_MAP = { - length: getattr(hashlib, algorithm, None) - for length, algorithm in ((32, "md5"), (40, "sha1"), (64, "sha256")) -} - - -def _const_compare_digest_backport(a, b): - """ - Compare two digests of equal length in constant time. - - The digests must be of type str/bytes. - Returns True if the digests match, and False otherwise. - """ - result = abs(len(a) - len(b)) - for left, right in zip(bytearray(a), bytearray(b)): - result |= left ^ right - return result == 0 - - -_const_compare_digest = getattr(hmac, "compare_digest", _const_compare_digest_backport) - -try: # Test for SSL features - import ssl - from ssl import CERT_REQUIRED, wrap_socket -except ImportError: - pass - -try: - from ssl import HAS_SNI # Has SNI? -except ImportError: - pass - -try: - from .ssltransport import SSLTransport -except ImportError: - pass - - -try: # Platform-specific: Python 3.6 - from ssl import PROTOCOL_TLS - - PROTOCOL_SSLv23 = PROTOCOL_TLS -except ImportError: - try: - from ssl import PROTOCOL_SSLv23 as PROTOCOL_TLS - - PROTOCOL_SSLv23 = PROTOCOL_TLS - except ImportError: - PROTOCOL_SSLv23 = PROTOCOL_TLS = 2 - -try: - from ssl import PROTOCOL_TLS_CLIENT -except ImportError: - PROTOCOL_TLS_CLIENT = PROTOCOL_TLS - - -try: - from ssl import OP_NO_COMPRESSION, OP_NO_SSLv2, OP_NO_SSLv3 -except ImportError: - OP_NO_SSLv2, OP_NO_SSLv3 = 0x1000000, 0x2000000 - OP_NO_COMPRESSION = 0x20000 - - -try: # OP_NO_TICKET was added in Python 3.6 - from ssl import OP_NO_TICKET -except ImportError: - OP_NO_TICKET = 0x4000 - - -# A secure default. -# Sources for more information on TLS ciphers: -# -# - https://wiki.mozilla.org/Security/Server_Side_TLS -# - https://www.ssllabs.com/projects/best-practices/index.html -# - https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/ -# -# The general intent is: -# - prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE), -# - prefer ECDHE over DHE for better performance, -# - prefer any AES-GCM and ChaCha20 over any AES-CBC for better performance and -# security, -# - prefer AES-GCM over ChaCha20 because hardware-accelerated AES is common, -# - disable NULL authentication, MD5 MACs, DSS, and other -# insecure ciphers for security reasons. -# - NOTE: TLS 1.3 cipher suites are managed through a different interface -# not exposed by CPython (yet!) and are enabled by default if they're available. -DEFAULT_CIPHERS = ":".join( - [ - "ECDHE+AESGCM", - "ECDHE+CHACHA20", - "DHE+AESGCM", - "DHE+CHACHA20", - "ECDH+AESGCM", - "DH+AESGCM", - "ECDH+AES", - "DH+AES", - "RSA+AESGCM", - "RSA+AES", - "!aNULL", - "!eNULL", - "!MD5", - "!DSS", - ] -) - -try: - from ssl import SSLContext # Modern SSL? -except ImportError: - - class SSLContext(object): # Platform-specific: Python 2 - def __init__(self, protocol_version): - self.protocol = protocol_version - # Use default values from a real SSLContext - self.check_hostname = False - self.verify_mode = ssl.CERT_NONE - self.ca_certs = None - self.options = 0 - self.certfile = None - self.keyfile = None - self.ciphers = None - - def load_cert_chain(self, certfile, keyfile): - self.certfile = certfile - self.keyfile = keyfile - - def load_verify_locations(self, cafile=None, capath=None, cadata=None): - self.ca_certs = cafile - - if capath is not None: - raise SSLError("CA directories not supported in older Pythons") - - if cadata is not None: - raise SSLError("CA data not supported in older Pythons") - - def set_ciphers(self, cipher_suite): - self.ciphers = cipher_suite - - def wrap_socket(self, socket, server_hostname=None, server_side=False): - warnings.warn( - "A true SSLContext object is not available. This prevents " - "urllib3 from configuring SSL appropriately and may cause " - "certain SSL connections to fail. You can upgrade to a newer " - "version of Python to solve this. For more information, see " - "https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html" - "#ssl-warnings", - InsecurePlatformWarning, - ) - kwargs = { - "keyfile": self.keyfile, - "certfile": self.certfile, - "ca_certs": self.ca_certs, - "cert_reqs": self.verify_mode, - "ssl_version": self.protocol, - "server_side": server_side, - } - return wrap_socket(socket, ciphers=self.ciphers, **kwargs) - - -def assert_fingerprint(cert, fingerprint): - """ - Checks if given fingerprint matches the supplied certificate. - - :param cert: - Certificate as bytes object. - :param fingerprint: - Fingerprint as string of hexdigits, can be interspersed by colons. - """ - - fingerprint = fingerprint.replace(":", "").lower() - digest_length = len(fingerprint) - 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()) - - cert_digest = hashfunc(cert).digest() - - if not _const_compare_digest(cert_digest, fingerprint_bytes): - raise SSLError( - 'Fingerprints did not match. Expected "{0}", got "{1}".'.format( - fingerprint, hexlify(cert_digest) - ) - ) - - -def resolve_cert_reqs(candidate): - """ - Resolves the argument to a numeric constant, which can be passed to - the wrap_socket function/method from the ssl module. - Defaults to :data:`ssl.CERT_REQUIRED`. - If given a string it is assumed to be the name of the constant in the - :mod:`ssl` module or its abbreviation. - (So you can specify `REQUIRED` instead of `CERT_REQUIRED`. - If it's neither `None` nor a string we assume it is already the numeric - constant which can directly be passed to wrap_socket. - """ - if candidate is None: - return CERT_REQUIRED - - if isinstance(candidate, str): - res = getattr(ssl, candidate, None) - if res is None: - res = getattr(ssl, "CERT_" + candidate) - return res - - return candidate - - -def resolve_ssl_version(candidate): - """ - like resolve_cert_reqs - """ - if candidate is None: - return PROTOCOL_TLS - - if isinstance(candidate, str): - res = getattr(ssl, candidate, None) - if res is None: - res = getattr(ssl, "PROTOCOL_" + candidate) - return res - - return candidate - - -def create_urllib3_context( - ssl_version=None, cert_reqs=None, options=None, ciphers=None -): - """All arguments have the same meaning as ``ssl_wrap_socket``. - - By default, this function does a lot of the same work that - ``ssl.create_default_context`` does on Python 3.4+. It: - - - Disables SSLv2, SSLv3, and compression - - Sets a restricted set of server ciphers - - If you wish to enable SSLv3, you can do:: - - from urllib3.util import ssl_ - context = ssl_.create_urllib3_context() - context.options &= ~ssl_.OP_NO_SSLv3 - - You can do the same to enable compression (substituting ``COMPRESSION`` - for ``SSLv3`` in the last line above). - - :param ssl_version: - The desired protocol version to use. This will default to - PROTOCOL_SSLv23 which will negotiate the highest protocol that both - the server and your installation of OpenSSL support. - :param cert_reqs: - Whether to require the certificate verification. This defaults to - ``ssl.CERT_REQUIRED``. - :param options: - Specific OpenSSL options. These default to ``ssl.OP_NO_SSLv2``, - ``ssl.OP_NO_SSLv3``, ``ssl.OP_NO_COMPRESSION``, and ``ssl.OP_NO_TICKET``. - :param ciphers: - Which cipher suites to allow the server to select. - :returns: - Constructed SSLContext object with specified options - :rtype: SSLContext - """ - # PROTOCOL_TLS is deprecated in Python 3.10 - if not ssl_version or ssl_version == PROTOCOL_TLS: - ssl_version = PROTOCOL_TLS_CLIENT - - context = SSLContext(ssl_version) - - context.set_ciphers(ciphers or DEFAULT_CIPHERS) - - # Setting the default here, as we may have no ssl module on import - cert_reqs = ssl.CERT_REQUIRED if cert_reqs is None else cert_reqs - - if options is None: - options = 0 - # SSLv2 is easily broken and is considered harmful and dangerous - options |= OP_NO_SSLv2 - # SSLv3 has several problems and is now dangerous - options |= OP_NO_SSLv3 - # Disable compression to prevent CRIME attacks for OpenSSL 1.0+ - # (issue #309) - options |= OP_NO_COMPRESSION - # TLSv1.2 only. Unless set explicitly, do not request tickets. - # This may save some bandwidth on wire, and although the ticket is encrypted, - # there is a risk associated with it being on wire, - # if the server is not rotating its ticketing keys properly. - options |= OP_NO_TICKET - - context.options |= options - - # Enable post-handshake authentication for TLS 1.3, see GH #1634. PHA is - # necessary for conditional client cert authentication with TLS 1.3. - # The attribute is None for OpenSSL <= 1.1.0 or does not exist in older - # versions of Python. We only enable on Python 3.7.4+ or if certificate - # verification is enabled to work around Python issue #37428 - # See: https://bugs.python.org/issue37428 - if (cert_reqs == ssl.CERT_REQUIRED or sys.version_info >= (3, 7, 4)) and getattr( - context, "post_handshake_auth", None - ) is not None: - context.post_handshake_auth = True - - def disable_check_hostname(): - if ( - getattr(context, "check_hostname", None) is not None - ): # Platform-specific: Python 3.2 - # We do our own verification, including fingerprints and alternative - # hostnames. So disable it here - context.check_hostname = False - - # The order of the below lines setting verify_mode and check_hostname - # matter due to safe-guards SSLContext has to prevent an SSLContext with - # check_hostname=True, verify_mode=NONE/OPTIONAL. This is made even more - # complex because we don't know whether PROTOCOL_TLS_CLIENT will be used - # or not so we don't know the initial state of the freshly created SSLContext. - if cert_reqs == ssl.CERT_REQUIRED: - context.verify_mode = cert_reqs - disable_check_hostname() - else: - disable_check_hostname() - context.verify_mode = cert_reqs - - # Enable logging of TLS session keys via defacto standard environment variable - # 'SSLKEYLOGFILE', if the feature is available (Python 3.8+). Skip empty values. - if hasattr(context, "keylog_filename"): - sslkeylogfile = os.environ.get("SSLKEYLOGFILE") - if sslkeylogfile: - context.keylog_filename = sslkeylogfile - - return context - - -def ssl_wrap_socket( - sock, - keyfile=None, - certfile=None, - cert_reqs=None, - ca_certs=None, - server_hostname=None, - ssl_version=None, - ciphers=None, - ssl_context=None, - ca_cert_dir=None, - key_password=None, - ca_cert_data=None, - tls_in_tls=False, -): - """ - All arguments except for server_hostname, ssl_context, and ca_cert_dir have - the same meaning as they do when using :func:`ssl.wrap_socket`. - - :param server_hostname: - When SNI is supported, the expected hostname of the certificate - :param ssl_context: - A pre-made :class:`SSLContext` object. If none is provided, one will - be created using :func:`create_urllib3_context`. - :param ciphers: - A string of ciphers we wish the client to support. - :param ca_cert_dir: - A directory containing CA certificates in multiple separate files, as - supported by OpenSSL's -CApath flag or the capath argument to - SSLContext.load_verify_locations(). - :param key_password: - Optional password if the keyfile is encrypted. - :param ca_cert_data: - Optional string containing CA certificates in PEM format suitable for - passing as the cadata parameter to SSLContext.load_verify_locations() - :param tls_in_tls: - Use SSLTransport to wrap the existing socket. - """ - context = ssl_context - if context is None: - # Note: This branch of code and all the variables in it are no longer - # used by urllib3 itself. We should consider deprecating and removing - # this code. - context = create_urllib3_context(ssl_version, cert_reqs, ciphers=ciphers) - - if ca_certs or ca_cert_dir or ca_cert_data: - try: - context.load_verify_locations(ca_certs, ca_cert_dir, ca_cert_data) - except (IOError, OSError) as e: - raise SSLError(e) - - elif ssl_context is None and hasattr(context, "load_default_certs"): - # try to load OS default certs; works well on Windows (require Python3.4+) - context.load_default_certs() - - # Attempt to detect if we get the goofy behavior of the - # keyfile being encrypted and OpenSSL asking for the - # passphrase via the terminal and instead error out. - if keyfile and key_password is None and _is_key_file_encrypted(keyfile): - raise SSLError("Client private key is encrypted, password is required") - - if certfile: - if key_password is None: - context.load_cert_chain(certfile, keyfile) - else: - context.load_cert_chain(certfile, keyfile, key_password) - - try: - if hasattr(context, "set_alpn_protocols"): - context.set_alpn_protocols(ALPN_PROTOCOLS) - except NotImplementedError: # Defensive: in CI, we always have set_alpn_protocols - pass - - # If we detect server_hostname is an IP address then the SNI - # extension should not be used according to RFC3546 Section 3.1 - use_sni_hostname = server_hostname and not is_ipaddress(server_hostname) - # SecureTransport uses server_hostname in certificate verification. - send_sni = (use_sni_hostname and HAS_SNI) or ( - IS_SECURETRANSPORT and server_hostname - ) - # Do not warn the user if server_hostname is an invalid SNI hostname. - if not HAS_SNI and use_sni_hostname: - warnings.warn( - "An HTTPS request has been made, but the SNI (Server Name " - "Indication) extension to TLS is not available on this platform. " - "This may cause the server to present an incorrect TLS " - "certificate, which can cause validation failures. You can upgrade to " - "a newer version of Python to solve this. For more information, see " - "https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html" - "#ssl-warnings", - SNIMissingWarning, - ) - - if send_sni: - ssl_sock = _ssl_wrap_socket_impl( - sock, context, tls_in_tls, server_hostname=server_hostname - ) - else: - ssl_sock = _ssl_wrap_socket_impl(sock, context, tls_in_tls) - return ssl_sock - - -def is_ipaddress(hostname): - """Detects whether the hostname given is an IPv4 or IPv6 address. - Also detects IPv6 addresses with Zone IDs. - - :param str hostname: Hostname to examine. - :return: True if the hostname is an IP address, False otherwise. - """ - if not six.PY2 and isinstance(hostname, bytes): - # IDN A-label bytes are ASCII compatible. - hostname = hostname.decode("ascii") - return bool(IPV4_RE.match(hostname) or BRACELESS_IPV6_ADDRZ_RE.match(hostname)) - - -def _is_key_file_encrypted(key_file): - """Detects if a key file is encrypted or not.""" - with open(key_file, "r") as f: - for line in f: - # Look for Proc-Type: 4,ENCRYPTED - if "ENCRYPTED" in line: - return True - - return False - - -def _ssl_wrap_socket_impl(sock, ssl_context, tls_in_tls, server_hostname=None): - if tls_in_tls: - if not SSLTransport: - # Import error, ssl is not available. - raise ProxySchemeUnsupported( - "TLS in TLS requires support for the 'ssl' module" - ) - - SSLTransport._validate_ssl_context_for_tls_in_tls(ssl_context) - return SSLTransport(sock, ssl_context, server_hostname) - - if server_hostname: - return ssl_context.wrap_socket(sock, server_hostname=server_hostname) - else: - return ssl_context.wrap_socket(sock) diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/ssl_match_hostname.py b/apps/bitwarden_event_logs/lib/urllib3/util/ssl_match_hostname.py deleted file mode 100755 index 1dd950c4..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/util/ssl_match_hostname.py +++ /dev/null @@ -1,159 +0,0 @@ -"""The match_hostname() function from Python 3.3.3, essential when using SSL.""" - -# Note: This file is under the PSF license as the code comes from the python -# stdlib. http://docs.python.org/3/license.html - -import re -import sys - -# ipaddress has been backported to 2.6+ in pypi. If it is installed on the -# system, use it to handle IPAddress ServerAltnames (this was added in -# python-3.5) otherwise only do DNS matching. This allows -# util.ssl_match_hostname to continue to be used in Python 2.7. -try: - import ipaddress -except ImportError: - ipaddress = None - -__version__ = "3.5.0.1" - - -class CertificateError(ValueError): - pass - - -def _dnsname_match(dn, hostname, max_wildcards=1): - """Matching according to RFC 6125, section 6.4.3 - - http://tools.ietf.org/html/rfc6125#section-6.4.3 - """ - pats = [] - if not dn: - return False - - # Ported from python3-syntax: - # leftmost, *remainder = dn.split(r'.') - parts = dn.split(r".") - leftmost = parts[0] - remainder = parts[1:] - - wildcards = leftmost.count("*") - if wildcards > max_wildcards: - # Issue #17980: avoid denials of service by refusing more - # than one wildcard per fragment. A survey of established - # policy among SSL implementations showed it to be a - # reasonable choice. - raise CertificateError( - "too many wildcards in certificate DNS name: " + repr(dn) - ) - - # speed up common case w/o wildcards - if not wildcards: - return dn.lower() == hostname.lower() - - # RFC 6125, section 6.4.3, subitem 1. - # The client SHOULD NOT attempt to match a presented identifier in which - # the wildcard character comprises a label other than the left-most label. - if leftmost == "*": - # When '*' is a fragment by itself, it matches a non-empty dotless - # fragment. - pats.append("[^.]+") - elif leftmost.startswith("xn--") or hostname.startswith("xn--"): - # RFC 6125, section 6.4.3, subitem 3. - # The client SHOULD NOT attempt to match a presented identifier - # where the wildcard character is embedded within an A-label or - # U-label of an internationalized domain name. - pats.append(re.escape(leftmost)) - else: - # Otherwise, '*' matches any dotless string, e.g. www* - pats.append(re.escape(leftmost).replace(r"\*", "[^.]*")) - - # add the remaining fragments, ignore any wildcards - for frag in remainder: - pats.append(re.escape(frag)) - - pat = re.compile(r"\A" + r"\.".join(pats) + r"\Z", re.IGNORECASE) - return pat.match(hostname) - - -def _to_unicode(obj): - if isinstance(obj, str) and sys.version_info < (3,): - # ignored flake8 # F821 to support python 2.7 function - obj = unicode(obj, encoding="ascii", errors="strict") # noqa: F821 - return obj - - -def _ipaddress_match(ipname, host_ip): - """Exact matching of IP addresses. - - RFC 6125 explicitly doesn't define an algorithm for this - (section 1.7.2 - "Out of Scope"). - """ - # OpenSSL may add a trailing newline to a subjectAltName's IP address - # Divergence from upstream: ipaddress can't handle byte str - ip = ipaddress.ip_address(_to_unicode(ipname).rstrip()) - return ip == host_ip - - -def match_hostname(cert, hostname): - """Verify that *cert* (in decoded format as returned by - SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 - rules are followed, but IP addresses are not accepted for *hostname*. - - CertificateError is raised on failure. On success, the function - returns nothing. - """ - if not cert: - raise ValueError( - "empty or no certificate, match_hostname needs a " - "SSL socket or SSL context with either " - "CERT_OPTIONAL or CERT_REQUIRED" - ) - try: - # Divergence from upstream: ipaddress can't handle byte str - host_ip = ipaddress.ip_address(_to_unicode(hostname)) - except (UnicodeError, ValueError): - # ValueError: Not an IP address (common case) - # UnicodeError: Divergence from upstream: Have to deal with ipaddress not taking - # byte strings. addresses should be all ascii, so we consider it not - # an ipaddress in this case - host_ip = None - except AttributeError: - # Divergence from upstream: Make ipaddress library optional - if ipaddress is None: - host_ip = None - else: # Defensive - raise - dnsnames = [] - san = cert.get("subjectAltName", ()) - for key, value in san: - if key == "DNS": - if host_ip is None and _dnsname_match(value, hostname): - return - dnsnames.append(value) - elif key == "IP Address": - if host_ip is not None and _ipaddress_match(value, host_ip): - return - dnsnames.append(value) - if not dnsnames: - # The subject is only checked when there is no dNSName entry - # in subjectAltName - for sub in cert.get("subject", ()): - for key, value in sub: - # XXX according to RFC 2818, the most specific Common Name - # must be used. - if key == "commonName": - if _dnsname_match(value, hostname): - return - dnsnames.append(value) - if len(dnsnames) > 1: - raise CertificateError( - "hostname %r " - "doesn't match either of %s" % (hostname, ", ".join(map(repr, dnsnames))) - ) - elif len(dnsnames) == 1: - raise CertificateError("hostname %r doesn't match %r" % (hostname, dnsnames[0])) - else: - raise CertificateError( - "no appropriate commonName or subjectAltName fields were found" - ) diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/ssltransport.py b/apps/bitwarden_event_logs/lib/urllib3/util/ssltransport.py deleted file mode 100755 index 4a7105d1..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/util/ssltransport.py +++ /dev/null @@ -1,221 +0,0 @@ -import io -import socket -import ssl - -from ..exceptions import ProxySchemeUnsupported -from ..packages import six - -SSL_BLOCKSIZE = 16384 - - -class SSLTransport: - """ - The SSLTransport wraps an existing socket and establishes an SSL connection. - - Contrary to Python's implementation of SSLSocket, it allows you to chain - multiple TLS connections together. It's particularly useful if you need to - implement TLS within TLS. - - The class supports most of the socket API operations. - """ - - @staticmethod - def _validate_ssl_context_for_tls_in_tls(ssl_context): - """ - Raises a ProxySchemeUnsupported if the provided ssl_context can't be used - for TLS in TLS. - - The only requirement is that the ssl_context provides the 'wrap_bio' - methods. - """ - - if not hasattr(ssl_context, "wrap_bio"): - if six.PY2: - raise ProxySchemeUnsupported( - "TLS in TLS requires SSLContext.wrap_bio() which isn't " - "supported on Python 2" - ) - else: - raise ProxySchemeUnsupported( - "TLS in TLS requires SSLContext.wrap_bio() which isn't " - "available on non-native SSLContext" - ) - - def __init__( - self, socket, ssl_context, server_hostname=None, suppress_ragged_eofs=True - ): - """ - Create an SSLTransport around socket using the provided ssl_context. - """ - self.incoming = ssl.MemoryBIO() - self.outgoing = ssl.MemoryBIO() - - self.suppress_ragged_eofs = suppress_ragged_eofs - self.socket = socket - - self.sslobj = ssl_context.wrap_bio( - self.incoming, self.outgoing, server_hostname=server_hostname - ) - - # Perform initial handshake. - self._ssl_io_loop(self.sslobj.do_handshake) - - def __enter__(self): - return self - - def __exit__(self, *_): - self.close() - - def fileno(self): - return self.socket.fileno() - - def read(self, len=1024, buffer=None): - return self._wrap_ssl_read(len, buffer) - - def recv(self, len=1024, flags=0): - if flags != 0: - raise ValueError("non-zero flags not allowed in calls to recv") - return self._wrap_ssl_read(len) - - def recv_into(self, buffer, nbytes=None, flags=0): - if flags != 0: - raise ValueError("non-zero flags not allowed in calls to recv_into") - if buffer and (nbytes is None): - nbytes = len(buffer) - elif nbytes is None: - nbytes = 1024 - return self.read(nbytes, buffer) - - def sendall(self, data, flags=0): - if flags != 0: - raise ValueError("non-zero flags not allowed in calls to sendall") - count = 0 - with memoryview(data) as view, view.cast("B") as byte_view: - amount = len(byte_view) - while count < amount: - v = self.send(byte_view[count:]) - count += v - - def send(self, data, flags=0): - if flags != 0: - raise ValueError("non-zero flags not allowed in calls to send") - response = self._ssl_io_loop(self.sslobj.write, data) - return response - - def makefile( - self, mode="r", buffering=None, encoding=None, errors=None, newline=None - ): - """ - Python's httpclient uses makefile and buffered io when reading HTTP - messages and we need to support it. - - This is unfortunately a copy and paste of socket.py makefile with small - changes to point to the socket directly. - """ - if not set(mode) <= {"r", "w", "b"}: - raise ValueError("invalid mode %r (only r, w, b allowed)" % (mode,)) - - writing = "w" in mode - reading = "r" in mode or not writing - assert reading or writing - binary = "b" in mode - rawmode = "" - if reading: - rawmode += "r" - if writing: - rawmode += "w" - raw = socket.SocketIO(self, rawmode) - self.socket._io_refs += 1 - if buffering is None: - buffering = -1 - if buffering < 0: - buffering = io.DEFAULT_BUFFER_SIZE - if buffering == 0: - if not binary: - raise ValueError("unbuffered streams must be binary") - return raw - if reading and writing: - buffer = io.BufferedRWPair(raw, raw, buffering) - elif reading: - buffer = io.BufferedReader(raw, buffering) - else: - assert writing - buffer = io.BufferedWriter(raw, buffering) - if binary: - return buffer - text = io.TextIOWrapper(buffer, encoding, errors, newline) - text.mode = mode - return text - - def unwrap(self): - self._ssl_io_loop(self.sslobj.unwrap) - - def close(self): - self.socket.close() - - def getpeercert(self, binary_form=False): - return self.sslobj.getpeercert(binary_form) - - def version(self): - return self.sslobj.version() - - def cipher(self): - return self.sslobj.cipher() - - def selected_alpn_protocol(self): - return self.sslobj.selected_alpn_protocol() - - def selected_npn_protocol(self): - return self.sslobj.selected_npn_protocol() - - def shared_ciphers(self): - return self.sslobj.shared_ciphers() - - def compression(self): - return self.sslobj.compression() - - def settimeout(self, value): - self.socket.settimeout(value) - - def gettimeout(self): - return self.socket.gettimeout() - - def _decref_socketios(self): - self.socket._decref_socketios() - - def _wrap_ssl_read(self, len, buffer=None): - try: - return self._ssl_io_loop(self.sslobj.read, len, buffer) - except ssl.SSLError as e: - if e.errno == ssl.SSL_ERROR_EOF and self.suppress_ragged_eofs: - return 0 # eof, return 0. - else: - raise - - def _ssl_io_loop(self, func, *args): - """Performs an I/O loop between incoming/outgoing and the socket.""" - should_loop = True - ret = None - - while should_loop: - errno = None - try: - ret = func(*args) - except ssl.SSLError as e: - if e.errno not in (ssl.SSL_ERROR_WANT_READ, ssl.SSL_ERROR_WANT_WRITE): - # WANT_READ, and WANT_WRITE are expected, others are not. - raise e - errno = e.errno - - buf = self.outgoing.read() - self.socket.sendall(buf) - - if errno is None: - should_loop = False - elif errno == ssl.SSL_ERROR_WANT_READ: - buf = self.socket.recv(SSL_BLOCKSIZE) - if buf: - self.incoming.write(buf) - else: - self.incoming.write_eof() - return ret diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/timeout.py b/apps/bitwarden_event_logs/lib/urllib3/util/timeout.py deleted file mode 100755 index 78e18a62..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/util/timeout.py +++ /dev/null @@ -1,271 +0,0 @@ -from __future__ import absolute_import - -import time - -# The default socket timeout, used by httplib to indicate that no timeout was; specified by the user -from socket import _GLOBAL_DEFAULT_TIMEOUT, getdefaulttimeout - -from ..exceptions import TimeoutStateError - -# A sentinel value to indicate that no timeout was specified by the user in -# urllib3 -_Default = object() - - -# Use time.monotonic if available. -current_time = getattr(time, "monotonic", time.time) - - -class Timeout(object): - """Timeout configuration. - - Timeouts can be defined as a default for a pool: - - .. code-block:: python - - timeout = Timeout(connect=2.0, read=7.0) - http = PoolManager(timeout=timeout) - response = http.request('GET', 'http://example.com/') - - Or per-request (which overrides the default for the pool): - - .. code-block:: python - - response = http.request('GET', 'http://example.com/', timeout=Timeout(10)) - - Timeouts can be disabled by setting all the parameters to ``None``: - - .. code-block:: python - - no_timeout = Timeout(connect=None, read=None) - response = http.request('GET', 'http://example.com/, timeout=no_timeout) - - - :param total: - This combines the connect and read timeouts into one; the read timeout - will be set to the time leftover from the connect attempt. In the - event that both a connect timeout and a total are specified, or a read - timeout and a total are specified, the shorter timeout will be applied. - - Defaults to None. - - :type total: int, float, or None - - :param connect: - The maximum amount of time (in seconds) to wait for a connection - attempt to a server to succeed. Omitting the parameter will default the - connect timeout to the system default, probably `the global default - timeout in socket.py - `_. - None will set an infinite timeout for connection attempts. - - :type connect: int, float, or None - - :param read: - The maximum amount of time (in seconds) to wait between consecutive - read operations for a response from the server. Omitting the parameter - will default the read timeout to the system default, probably `the - global default timeout in socket.py - `_. - None will set an infinite timeout. - - :type read: int, float, or None - - .. note:: - - Many factors can affect the total amount of time for urllib3 to return - an HTTP response. - - For example, Python's DNS resolver does not obey the timeout specified - on the socket. Other factors that can affect total request time include - high CPU load, high swap, the program running at a low priority level, - or other behaviors. - - In addition, the read and total timeouts only measure the time between - read operations on the socket connecting the client and the server, - not the total amount of time for the request to return a complete - response. For most requests, the timeout is raised because the server - has not sent the first byte in the specified time. This is not always - the case; if a server streams one byte every fifteen seconds, a timeout - of 20 seconds will not trigger, even though the request will take - several minutes to complete. - - If your goal is to cut off any request after a set amount of wall clock - time, consider having a second "watcher" thread to cut off a slow - request. - """ - - #: A sentinel object representing the default timeout value - DEFAULT_TIMEOUT = _GLOBAL_DEFAULT_TIMEOUT - - def __init__(self, total=None, connect=_Default, read=_Default): - self._connect = self._validate_timeout(connect, "connect") - self._read = self._validate_timeout(read, "read") - self.total = self._validate_timeout(total, "total") - self._start_connect = None - - def __repr__(self): - return "%s(connect=%r, read=%r, total=%r)" % ( - type(self).__name__, - self._connect, - self._read, - self.total, - ) - - # __str__ provided for backwards compatibility - __str__ = __repr__ - - @classmethod - def resolve_default_timeout(cls, timeout): - return getdefaulttimeout() if timeout is cls.DEFAULT_TIMEOUT else timeout - - @classmethod - def _validate_timeout(cls, value, name): - """Check that a timeout attribute is valid. - - :param value: The timeout value to validate - :param name: The name of the timeout attribute to validate. This is - used to specify in error messages. - :return: The validated and casted version of the given value. - :raises ValueError: If it is a numeric value less than or equal to - zero, or the type is not an integer, float, or None. - """ - if value is _Default: - return cls.DEFAULT_TIMEOUT - - if value is None or value is cls.DEFAULT_TIMEOUT: - return value - - if isinstance(value, bool): - raise ValueError( - "Timeout cannot be a boolean value. It must " - "be an int, float or None." - ) - try: - float(value) - except (TypeError, ValueError): - raise ValueError( - "Timeout value %s was %s, but it must be an " - "int, float or None." % (name, value) - ) - - try: - if value <= 0: - raise ValueError( - "Attempted to set %s timeout to %s, but the " - "timeout cannot be set to a value less " - "than or equal to 0." % (name, value) - ) - except TypeError: - # Python 3 - raise ValueError( - "Timeout value %s was %s, but it must be an " - "int, float or None." % (name, value) - ) - - return value - - @classmethod - def from_float(cls, timeout): - """Create a new Timeout from a legacy timeout value. - - The timeout value used by httplib.py sets the same timeout on the - connect(), and recv() socket requests. This creates a :class:`Timeout` - object that sets the individual timeouts to the ``timeout`` value - passed to this function. - - :param timeout: The legacy timeout value. - :type timeout: integer, float, sentinel default object, or None - :return: Timeout object - :rtype: :class:`Timeout` - """ - return Timeout(read=timeout, connect=timeout) - - def clone(self): - """Create a copy of the timeout object - - Timeout properties are stored per-pool but each request needs a fresh - Timeout object to ensure each one has its own start/stop configured. - - :return: a copy of the timeout object - :rtype: :class:`Timeout` - """ - # We can't use copy.deepcopy because that will also create a new object - # for _GLOBAL_DEFAULT_TIMEOUT, which socket.py uses as a sentinel to - # detect the user default. - return Timeout(connect=self._connect, read=self._read, total=self.total) - - def start_connect(self): - """Start the timeout clock, used during a connect() attempt - - :raises urllib3.exceptions.TimeoutStateError: if you attempt - to start a timer that has been started already. - """ - if self._start_connect is not None: - raise TimeoutStateError("Timeout timer has already been started.") - self._start_connect = current_time() - return self._start_connect - - def get_connect_duration(self): - """Gets the time elapsed since the call to :meth:`start_connect`. - - :return: Elapsed time in seconds. - :rtype: float - :raises urllib3.exceptions.TimeoutStateError: if you attempt - to get duration for a timer that hasn't been started. - """ - if self._start_connect is None: - raise TimeoutStateError( - "Can't get connect duration for timer that has not started." - ) - return current_time() - self._start_connect - - @property - def connect_timeout(self): - """Get the value to use when setting a connection timeout. - - This will be a positive float or integer, the value None - (never timeout), or the default system timeout. - - :return: Connect timeout. - :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None - """ - if self.total is None: - return self._connect - - if self._connect is None or self._connect is self.DEFAULT_TIMEOUT: - return self.total - - return min(self._connect, self.total) - - @property - def read_timeout(self): - """Get the value for the read timeout. - - This assumes some time has elapsed in the connection timeout and - computes the read timeout appropriately. - - If self.total is set, the read timeout is dependent on the amount of - time taken by the connect timeout. If the connection time has not been - established, a :exc:`~urllib3.exceptions.TimeoutStateError` will be - raised. - - :return: Value to use for the read timeout. - :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None - :raises urllib3.exceptions.TimeoutStateError: If :meth:`start_connect` - has not yet been called on this object. - """ - if ( - self.total is not None - and self.total is not self.DEFAULT_TIMEOUT - and self._read is not None - and self._read is not self.DEFAULT_TIMEOUT - ): - # In case the connect timeout has not yet been established. - if self._start_connect is None: - return self._read - return max(0, min(self.total - self.get_connect_duration(), self._read)) - elif self.total is not None and self.total is not self.DEFAULT_TIMEOUT: - return max(0, self.total - self.get_connect_duration()) - else: - return self._read diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/url.py b/apps/bitwarden_event_logs/lib/urllib3/util/url.py deleted file mode 100755 index e5682d3b..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/util/url.py +++ /dev/null @@ -1,435 +0,0 @@ -from __future__ import absolute_import - -import re -from collections import namedtuple - -from ..exceptions import LocationParseError -from ..packages import six - -url_attrs = ["scheme", "auth", "host", "port", "path", "query", "fragment"] - -# We only want to normalize urls with an HTTP(S) scheme. -# urllib3 infers URLs without a scheme (None) to be http. -NORMALIZABLE_SCHEMES = ("http", "https", None) - -# Almost all of these patterns were derived from the -# 'rfc3986' module: https://github.com/python-hyper/rfc3986 -PERCENT_RE = re.compile(r"%[a-fA-F0-9]{2}") -SCHEME_RE = re.compile(r"^(?:[a-zA-Z][a-zA-Z0-9+-]*:|/)") -URI_RE = re.compile( - r"^(?:([a-zA-Z][a-zA-Z0-9+.-]*):)?" - r"(?://([^\\/?#]*))?" - r"([^?#]*)" - r"(?:\?([^#]*))?" - r"(?:#(.*))?$", - re.UNICODE | re.DOTALL, -) - -IPV4_PAT = r"(?:[0-9]{1,3}\.){3}[0-9]{1,3}" -HEX_PAT = "[0-9A-Fa-f]{1,4}" -LS32_PAT = "(?:{hex}:{hex}|{ipv4})".format(hex=HEX_PAT, ipv4=IPV4_PAT) -_subs = {"hex": HEX_PAT, "ls32": LS32_PAT} -_variations = [ - # 6( h16 ":" ) ls32 - "(?:%(hex)s:){6}%(ls32)s", - # "::" 5( h16 ":" ) ls32 - "::(?:%(hex)s:){5}%(ls32)s", - # [ h16 ] "::" 4( h16 ":" ) ls32 - "(?:%(hex)s)?::(?:%(hex)s:){4}%(ls32)s", - # [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 - "(?:(?:%(hex)s:)?%(hex)s)?::(?:%(hex)s:){3}%(ls32)s", - # [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 - "(?:(?:%(hex)s:){0,2}%(hex)s)?::(?:%(hex)s:){2}%(ls32)s", - # [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 - "(?:(?:%(hex)s:){0,3}%(hex)s)?::%(hex)s:%(ls32)s", - # [ *4( h16 ":" ) h16 ] "::" ls32 - "(?:(?:%(hex)s:){0,4}%(hex)s)?::%(ls32)s", - # [ *5( h16 ":" ) h16 ] "::" h16 - "(?:(?:%(hex)s:){0,5}%(hex)s)?::%(hex)s", - # [ *6( h16 ":" ) h16 ] "::" - "(?:(?:%(hex)s:){0,6}%(hex)s)?::", -] - -UNRESERVED_PAT = r"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._\-~" -IPV6_PAT = "(?:" + "|".join([x % _subs for x in _variations]) + ")" -ZONE_ID_PAT = "(?:%25|%)(?:[" + UNRESERVED_PAT + "]|%[a-fA-F0-9]{2})+" -IPV6_ADDRZ_PAT = r"\[" + IPV6_PAT + r"(?:" + ZONE_ID_PAT + r")?\]" -REG_NAME_PAT = r"(?:[^\[\]%:/?#]|%[a-fA-F0-9]{2})*" -TARGET_RE = re.compile(r"^(/[^?#]*)(?:\?([^#]*))?(?:#.*)?$") - -IPV4_RE = re.compile("^" + IPV4_PAT + "$") -IPV6_RE = re.compile("^" + IPV6_PAT + "$") -IPV6_ADDRZ_RE = re.compile("^" + IPV6_ADDRZ_PAT + "$") -BRACELESS_IPV6_ADDRZ_RE = re.compile("^" + IPV6_ADDRZ_PAT[2:-2] + "$") -ZONE_ID_RE = re.compile("(" + ZONE_ID_PAT + r")\]$") - -_HOST_PORT_PAT = ("^(%s|%s|%s)(?::0*?(|0|[1-9][0-9]{0,4}))?$") % ( - REG_NAME_PAT, - IPV4_PAT, - IPV6_ADDRZ_PAT, -) -_HOST_PORT_RE = re.compile(_HOST_PORT_PAT, re.UNICODE | re.DOTALL) - -UNRESERVED_CHARS = set( - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-~" -) -SUB_DELIM_CHARS = set("!$&'()*+,;=") -USERINFO_CHARS = UNRESERVED_CHARS | SUB_DELIM_CHARS | {":"} -PATH_CHARS = USERINFO_CHARS | {"@", "/"} -QUERY_CHARS = FRAGMENT_CHARS = PATH_CHARS | {"?"} - - -class Url(namedtuple("Url", url_attrs)): - """ - Data structure for representing an HTTP URL. Used as a return value for - :func:`parse_url`. Both the scheme and host are normalized as they are - both case-insensitive according to RFC 3986. - """ - - __slots__ = () - - def __new__( - cls, - scheme=None, - auth=None, - host=None, - port=None, - path=None, - query=None, - fragment=None, - ): - if path and not path.startswith("/"): - path = "/" + path - if scheme is not None: - scheme = scheme.lower() - return super(Url, cls).__new__( - cls, scheme, auth, host, port, path, query, fragment - ) - - @property - def hostname(self): - """For backwards-compatibility with urlparse. We're nice like that.""" - return self.host - - @property - def request_uri(self): - """Absolute path including the query string.""" - uri = self.path or "/" - - if self.query is not None: - uri += "?" + self.query - - return uri - - @property - def netloc(self): - """Network location including host and port""" - if self.port: - return "%s:%d" % (self.host, self.port) - return self.host - - @property - def url(self): - """ - Convert self into a url - - This function should more or less round-trip with :func:`.parse_url`. The - returned url may not be exactly the same as the url inputted to - :func:`.parse_url`, but it should be equivalent by the RFC (e.g., urls - with a blank port will have : removed). - - Example: :: - - >>> U = parse_url('http://google.com/mail/') - >>> U.url - 'http://google.com/mail/' - >>> Url('http', 'username:password', 'host.com', 80, - ... '/path', 'query', 'fragment').url - 'http://username:password@host.com:80/path?query#fragment' - """ - scheme, auth, host, port, path, query, fragment = self - url = u"" - - # We use "is not None" we want things to happen with empty strings (or 0 port) - if scheme is not None: - url += scheme + u"://" - if auth is not None: - url += auth + u"@" - if host is not None: - url += host - if port is not None: - url += u":" + str(port) - if path is not None: - url += path - if query is not None: - url += u"?" + query - if fragment is not None: - url += u"#" + fragment - - return url - - def __str__(self): - return self.url - - -def split_first(s, delims): - """ - .. deprecated:: 1.25 - - Given a string and an iterable of delimiters, split on the first found - delimiter. Return two split parts and the matched delimiter. - - If not found, then the first part is the full input string. - - Example:: - - >>> split_first('foo/bar?baz', '?/=') - ('foo', 'bar?baz', '/') - >>> split_first('foo/bar?baz', '123') - ('foo/bar?baz', '', None) - - Scales linearly with number of delims. Not ideal for large number of delims. - """ - min_idx = None - min_delim = None - for d in delims: - idx = s.find(d) - if idx < 0: - continue - - if min_idx is None or idx < min_idx: - min_idx = idx - min_delim = d - - if min_idx is None or min_idx < 0: - return s, "", None - - return s[:min_idx], s[min_idx + 1 :], min_delim - - -def _encode_invalid_chars(component, allowed_chars, encoding="utf-8"): - """Percent-encodes a URI component without reapplying - onto an already percent-encoded component. - """ - if component is None: - return component - - component = six.ensure_text(component) - - # Normalize existing percent-encoded bytes. - # Try to see if the component we're encoding is already percent-encoded - # so we can skip all '%' characters but still encode all others. - component, percent_encodings = PERCENT_RE.subn( - lambda match: match.group(0).upper(), component - ) - - uri_bytes = component.encode("utf-8", "surrogatepass") - is_percent_encoded = percent_encodings == uri_bytes.count(b"%") - encoded_component = bytearray() - - for i in range(0, len(uri_bytes)): - # Will return a single character bytestring on both Python 2 & 3 - byte = uri_bytes[i : i + 1] - byte_ord = ord(byte) - if (is_percent_encoded and byte == b"%") or ( - byte_ord < 128 and byte.decode() in allowed_chars - ): - encoded_component += byte - continue - encoded_component.extend(b"%" + (hex(byte_ord)[2:].encode().zfill(2).upper())) - - return encoded_component.decode(encoding) - - -def _remove_path_dot_segments(path): - # See http://tools.ietf.org/html/rfc3986#section-5.2.4 for pseudo-code - segments = path.split("/") # Turn the path into a list of segments - output = [] # Initialize the variable to use to store output - - for segment in segments: - # '.' is the current directory, so ignore it, it is superfluous - if segment == ".": - continue - # Anything other than '..', should be appended to the output - elif segment != "..": - output.append(segment) - # In this case segment == '..', if we can, we should pop the last - # element - elif output: - output.pop() - - # If the path starts with '/' and the output is empty or the first string - # is non-empty - if path.startswith("/") and (not output or output[0]): - output.insert(0, "") - - # If the path starts with '/.' or '/..' ensure we add one more empty - # string to add a trailing '/' - if path.endswith(("/.", "/..")): - output.append("") - - return "/".join(output) - - -def _normalize_host(host, scheme): - if host: - if isinstance(host, six.binary_type): - host = six.ensure_str(host) - - if scheme in NORMALIZABLE_SCHEMES: - is_ipv6 = IPV6_ADDRZ_RE.match(host) - if is_ipv6: - # IPv6 hosts of the form 'a::b%zone' are encoded in a URL as - # such per RFC 6874: 'a::b%25zone'. Unquote the ZoneID - # separator as necessary to return a valid RFC 4007 scoped IP. - match = ZONE_ID_RE.search(host) - if match: - start, end = match.span(1) - zone_id = host[start:end] - - if zone_id.startswith("%25") and zone_id != "%25": - zone_id = zone_id[3:] - else: - zone_id = zone_id[1:] - zone_id = "%" + _encode_invalid_chars(zone_id, UNRESERVED_CHARS) - return host[:start].lower() + zone_id + host[end:] - else: - return host.lower() - elif not IPV4_RE.match(host): - return six.ensure_str( - b".".join([_idna_encode(label) for label in host.split(".")]) - ) - return host - - -def _idna_encode(name): - if name and any(ord(x) >= 128 for x in name): - try: - import idna - except ImportError: - six.raise_from( - LocationParseError("Unable to parse URL without the 'idna' module"), - None, - ) - try: - return idna.encode(name.lower(), strict=True, std3_rules=True) - except idna.IDNAError: - six.raise_from( - LocationParseError(u"Name '%s' is not a valid IDNA label" % name), None - ) - return name.lower().encode("ascii") - - -def _encode_target(target): - """Percent-encodes a request target so that there are no invalid characters""" - path, query = TARGET_RE.match(target).groups() - target = _encode_invalid_chars(path, PATH_CHARS) - query = _encode_invalid_chars(query, QUERY_CHARS) - if query is not None: - target += "?" + query - return target - - -def parse_url(url): - """ - Given a url, return a parsed :class:`.Url` namedtuple. Best-effort is - performed to parse incomplete urls. Fields not provided will be None. - This parser is RFC 3986 and RFC 6874 compliant. - - The parser logic and helper functions are based heavily on - work done in the ``rfc3986`` module. - - :param str url: URL to parse into a :class:`.Url` namedtuple. - - Partly backwards-compatible with :mod:`urlparse`. - - Example:: - - >>> parse_url('http://google.com/mail/') - Url(scheme='http', host='google.com', port=None, path='/mail/', ...) - >>> parse_url('google.com:80') - Url(scheme=None, host='google.com', port=80, path=None, ...) - >>> parse_url('/foo?bar') - Url(scheme=None, host=None, port=None, path='/foo', query='bar', ...) - """ - if not url: - # Empty - return Url() - - source_url = url - if not SCHEME_RE.search(url): - url = "//" + url - - try: - scheme, authority, path, query, fragment = URI_RE.match(url).groups() - normalize_uri = scheme is None or scheme.lower() in NORMALIZABLE_SCHEMES - - if scheme: - scheme = scheme.lower() - - if authority: - auth, _, host_port = authority.rpartition("@") - auth = auth or None - host, port = _HOST_PORT_RE.match(host_port).groups() - if auth and normalize_uri: - auth = _encode_invalid_chars(auth, USERINFO_CHARS) - if port == "": - port = None - else: - auth, host, port = None, None, None - - if port is not None: - port = int(port) - if not (0 <= port <= 65535): - raise LocationParseError(url) - - host = _normalize_host(host, scheme) - - if normalize_uri and path: - path = _remove_path_dot_segments(path) - path = _encode_invalid_chars(path, PATH_CHARS) - if normalize_uri and query: - query = _encode_invalid_chars(query, QUERY_CHARS) - if normalize_uri and fragment: - fragment = _encode_invalid_chars(fragment, FRAGMENT_CHARS) - - except (ValueError, AttributeError): - return six.raise_from(LocationParseError(source_url), None) - - # For the sake of backwards compatibility we put empty - # string values for path if there are any defined values - # beyond the path in the URL. - # TODO: Remove this when we break backwards compatibility. - if not path: - if query is not None or fragment is not None: - path = "" - else: - path = None - - # Ensure that each part of the URL is a `str` for - # backwards compatibility. - if isinstance(url, six.text_type): - ensure_func = six.ensure_text - else: - ensure_func = six.ensure_str - - def ensure_type(x): - return x if x is None else ensure_func(x) - - return Url( - scheme=ensure_type(scheme), - auth=ensure_type(auth), - host=ensure_type(host), - port=port, - path=ensure_type(path), - query=ensure_type(query), - fragment=ensure_type(fragment), - ) - - -def get_host(url): - """ - Deprecated. Use :func:`parse_url` instead. - """ - p = parse_url(url) - return p.scheme or "http", p.hostname, p.port diff --git a/apps/bitwarden_event_logs/lib/urllib3/util/wait.py b/apps/bitwarden_event_logs/lib/urllib3/util/wait.py deleted file mode 100755 index 21b4590b..00000000 --- a/apps/bitwarden_event_logs/lib/urllib3/util/wait.py +++ /dev/null @@ -1,152 +0,0 @@ -import errno -import select -import sys -from functools import partial - -try: - from time import monotonic -except ImportError: - from time import time as monotonic - -__all__ = ["NoWayToWaitForSocketError", "wait_for_read", "wait_for_write"] - - -class NoWayToWaitForSocketError(Exception): - pass - - -# How should we wait on sockets? -# -# There are two types of APIs you can use for waiting on sockets: the fancy -# modern stateful APIs like epoll/kqueue, and the older stateless APIs like -# select/poll. The stateful APIs are more efficient when you have a lots of -# sockets to keep track of, because you can set them up once and then use them -# lots of times. But we only ever want to wait on a single socket at a time -# and don't want to keep track of state, so the stateless APIs are actually -# more efficient. So we want to use select() or poll(). -# -# Now, how do we choose between select() and poll()? On traditional Unixes, -# select() has a strange calling convention that makes it slow, or fail -# altogether, for high-numbered file descriptors. The point of poll() is to fix -# that, so on Unixes, we prefer poll(). -# -# On Windows, there is no poll() (or at least Python doesn't provide a wrapper -# for it), but that's OK, because on Windows, select() doesn't have this -# strange calling convention; plain select() works fine. -# -# So: on Windows we use select(), and everywhere else we use poll(). We also -# fall back to select() in case poll() is somehow broken or missing. - -if sys.version_info >= (3, 5): - # Modern Python, that retries syscalls by default - def _retry_on_intr(fn, timeout): - return fn(timeout) - -else: - # Old and broken Pythons. - def _retry_on_intr(fn, timeout): - if timeout is None: - deadline = float("inf") - else: - deadline = monotonic() + timeout - - while True: - try: - return fn(timeout) - # OSError for 3 <= pyver < 3.5, select.error for pyver <= 2.7 - except (OSError, select.error) as e: - # 'e.args[0]' incantation works for both OSError and select.error - if e.args[0] != errno.EINTR: - raise - else: - timeout = deadline - monotonic() - if timeout < 0: - timeout = 0 - if timeout == float("inf"): - timeout = None - continue - - -def select_wait_for_socket(sock, read=False, write=False, timeout=None): - if not read and not write: - raise RuntimeError("must specify at least one of read=True, write=True") - rcheck = [] - wcheck = [] - if read: - rcheck.append(sock) - if write: - wcheck.append(sock) - # When doing a non-blocking connect, most systems signal success by - # marking the socket writable. Windows, though, signals success by marked - # it as "exceptional". We paper over the difference by checking the write - # sockets for both conditions. (The stdlib selectors module does the same - # thing.) - fn = partial(select.select, rcheck, wcheck, wcheck) - rready, wready, xready = _retry_on_intr(fn, timeout) - return bool(rready or wready or xready) - - -def poll_wait_for_socket(sock, read=False, write=False, timeout=None): - if not read and not write: - raise RuntimeError("must specify at least one of read=True, write=True") - mask = 0 - if read: - mask |= select.POLLIN - if write: - mask |= select.POLLOUT - poll_obj = select.poll() - poll_obj.register(sock, mask) - - # For some reason, poll() takes timeout in milliseconds - def do_poll(t): - if t is not None: - t *= 1000 - return poll_obj.poll(t) - - return bool(_retry_on_intr(do_poll, timeout)) - - -def null_wait_for_socket(*args, **kwargs): - raise NoWayToWaitForSocketError("no select-equivalent available") - - -def _have_working_poll(): - # Apparently some systems have a select.poll that fails as soon as you try - # to use it, either due to strange configuration or broken monkeypatching - # from libraries like eventlet/greenlet. - try: - poll_obj = select.poll() - _retry_on_intr(poll_obj.poll, 0) - except (AttributeError, OSError): - return False - else: - return True - - -def wait_for_socket(*args, **kwargs): - # We delay choosing which implementation to use until the first time we're - # called. We could do it at import time, but then we might make the wrong - # decision if someone goes wild with monkeypatching select.poll after - # we're imported. - global wait_for_socket - if _have_working_poll(): - wait_for_socket = poll_wait_for_socket - elif hasattr(select, "select"): - wait_for_socket = select_wait_for_socket - else: # Platform-specific: Appengine. - wait_for_socket = null_wait_for_socket - return wait_for_socket(*args, **kwargs) - - -def wait_for_read(sock, timeout=None): - """Waits for reading to be available on a given socket. - Returns True if the socket is readable, or False if the timeout expired. - """ - return wait_for_socket(sock, read=True, timeout=timeout) - - -def wait_for_write(sock, timeout=None): - """Waits for writing to be available on a given socket. - Returns True if the socket is readable, or False if the timeout expired. - """ - return wait_for_socket(sock, write=True, timeout=timeout) diff --git a/apps/bitwarden_event_logs/local/inputs.conf b/apps/bitwarden_event_logs/local/inputs.conf deleted file mode 100755 index 3fae0716..00000000 --- a/apps/bitwarden_event_logs/local/inputs.conf +++ /dev/null @@ -1 +0,0 @@ -[script://$SPLUNK_HOME/etc/apps/bitwarden_event_logs/bin/bitwarden_event_logs.py] diff --git a/apps/bitwarden_event_logs/local/passwords.conf b/apps/bitwarden_event_logs/local/passwords.conf deleted file mode 100755 index 12495d35..00000000 --- a/apps/bitwarden_event_logs/local/passwords.conf +++ /dev/null @@ -1,2 +0,0 @@ -[credential:bitwarden_event_logs_realm:api_key:] -password = $7$Rb2EkeaDOjqhDQXvFUVFPP2yvOi6A9kc1kWarDBtdrSHkkgfVo/9Mq52q/GG3YSf2R0/iSZfViqr5dpPiP1kXXXU08/UhktWIh1mLx42mgdYK2DazEZJlTWNTyE6b81GaXpOQJ5NMtuH+BrNOgfJGA== diff --git a/apps/bitwarden_event_logs/local/script.conf b/apps/bitwarden_event_logs/local/script.conf deleted file mode 100755 index 5dba6f07..00000000 --- a/apps/bitwarden_event_logs/local/script.conf +++ /dev/null @@ -1,4 +0,0 @@ -[config] -apiUrl = https://private-vault.jp-engineering.fr/api -identityUrl = https://private-vault.jp-engineering.fr/identity -startDate = 2026-02-27 diff --git a/apps/bitwarden_event_logs/test.conf b/apps/bitwarden_event_logs/test.conf deleted file mode 100755 index e69de29b..00000000