From c2257d3a869dc6a7a4723521ec8666b2dddeeae4 Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Thu, 19 Mar 2026 13:53:55 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=ED=8F=AC=ED=8A=B8=20=EC=B6=A9=EB=8F=8C?= =?UTF-8?q?=20=ED=9A=8C=ED=94=BC=20=E2=80=94=20note=5Fbridge=208098,=20int?= =?UTF-8?q?ent=5Fservice=208099?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Jellyfin(8096), OrbStack(8097) 포트 충돌으로 변경. Co-Authored-By: Claude Opus 4.6 (1M context) --- .env.example | 4 +- .venv/bin/Activate.ps1 | 241 + .venv/bin/activate | 66 + .venv/bin/activate.csh | 25 + .venv/bin/activate.fish | 64 + .venv/bin/icalendar | 8 + .venv/bin/normalizer | 8 + .venv/bin/pip | 8 + .venv/bin/pip3 | 8 + .venv/bin/pip3.9 | 8 + .venv/bin/python | 1 + .venv/bin/python3 | 1 + .venv/bin/python3.9 | 1 + .venv/bin/x-wr-timezone | 8 + ...bd2c585b0f4821__mypyc.cpython-39-darwin.so | Bin 0 -> 716632 bytes .../site-packages/_distutils_hack/__init__.py | 128 + .../site-packages/_distutils_hack/override.py | 1 + .../python3.9/site-packages/_yaml/__init__.py | 33 + .../INSTALLER | 1 + .../aiohappyeyeballs-2.6.1.dist-info/LICENSE | 279 + .../aiohappyeyeballs-2.6.1.dist-info/METADATA | 123 + .../aiohappyeyeballs-2.6.1.dist-info/RECORD | 16 + .../aiohappyeyeballs-2.6.1.dist-info/WHEEL | 4 + .../aiohappyeyeballs/__init__.py | 14 + .../aiohappyeyeballs/_staggered.py | 207 + .../site-packages/aiohappyeyeballs/impl.py | 259 + .../site-packages/aiohappyeyeballs/py.typed | 0 .../site-packages/aiohappyeyeballs/types.py | 17 + .../site-packages/aiohappyeyeballs/utils.py | 97 + .../aiohttp-3.13.3.dist-info/INSTALLER | 1 + .../aiohttp-3.13.3.dist-info/METADATA | 262 + .../aiohttp-3.13.3.dist-info/RECORD | 139 + .../aiohttp-3.13.3.dist-info/REQUESTED | 0 .../aiohttp-3.13.3.dist-info/WHEEL | 6 + .../licenses/LICENSE.txt | 13 + .../licenses/vendor/llhttp/LICENSE | 22 + .../aiohttp-3.13.3.dist-info/top_level.txt | 1 + .../aiohttp/.hash/_cparser.pxd.hash | 1 + .../aiohttp/.hash/_find_header.pxd.hash | 1 + .../aiohttp/.hash/_http_parser.pyx.hash | 1 + .../aiohttp/.hash/_http_writer.pyx.hash | 1 + .../site-packages/aiohttp/.hash/hdrs.py.hash | 1 + .../site-packages/aiohttp/__init__.py | 278 + .../site-packages/aiohttp/_cookie_helpers.py | 338 + .../site-packages/aiohttp/_cparser.pxd | 158 + .../site-packages/aiohttp/_find_header.pxd | 2 + .../site-packages/aiohttp/_headers.pxi | 83 + .../aiohttp/_http_parser.cpython-39-darwin.so | Bin 0 -> 435440 bytes .../site-packages/aiohttp/_http_parser.pyx | 835 ++ .../aiohttp/_http_writer.cpython-39-darwin.so | Bin 0 -> 92416 bytes .../site-packages/aiohttp/_http_writer.pyx | 162 + .../aiohttp/_websocket/.hash/mask.pxd.hash | 1 + .../aiohttp/_websocket/.hash/mask.pyx.hash | 1 + .../_websocket/.hash/reader_c.pxd.hash | 1 + .../aiohttp/_websocket/__init__.py | 1 + .../aiohttp/_websocket/helpers.py | 147 + .../_websocket/mask.cpython-39-darwin.so | Bin 0 -> 85880 bytes .../site-packages/aiohttp/_websocket/mask.pxd | 3 + .../site-packages/aiohttp/_websocket/mask.pyx | 48 + .../aiohttp/_websocket/models.py | 84 + .../aiohttp/_websocket/reader.py | 31 + .../_websocket/reader_c.cpython-39-darwin.so | Bin 0 -> 256144 bytes .../aiohttp/_websocket/reader_c.pxd | 110 + .../aiohttp/_websocket/reader_c.py | 478 + .../aiohttp/_websocket/reader_py.py | 478 + .../aiohttp/_websocket/writer.py | 262 + .../python3.9/site-packages/aiohttp/abc.py | 268 + .../site-packages/aiohttp/base_protocol.py | 100 + .../python3.9/site-packages/aiohttp/client.py | 1635 +++ .../aiohttp/client_exceptions.py | 421 + .../aiohttp/client_middleware_digest_auth.py | 480 + .../aiohttp/client_middlewares.py | 55 + .../site-packages/aiohttp/client_proto.py | 359 + .../site-packages/aiohttp/client_reqrep.py | 1536 +++ .../site-packages/aiohttp/client_ws.py | 428 + .../aiohttp/compression_utils.py | 348 + .../site-packages/aiohttp/connector.py | 1842 ++++ .../site-packages/aiohttp/cookiejar.py | 522 + .../site-packages/aiohttp/formdata.py | 179 + .../python3.9/site-packages/aiohttp/hdrs.py | 121 + .../site-packages/aiohttp/helpers.py | 986 ++ .../python3.9/site-packages/aiohttp/http.py | 72 + .../site-packages/aiohttp/http_exceptions.py | 116 + .../site-packages/aiohttp/http_parser.py | 1086 ++ .../site-packages/aiohttp/http_websocket.py | 36 + .../site-packages/aiohttp/http_writer.py | 378 + .../python3.9/site-packages/aiohttp/log.py | 8 + .../site-packages/aiohttp/multipart.py | 1152 +++ .../site-packages/aiohttp/payload.py | 1120 +++ .../site-packages/aiohttp/payload_streamer.py | 78 + .../python3.9/site-packages/aiohttp/py.typed | 1 + .../site-packages/aiohttp/pytest_plugin.py | 444 + .../site-packages/aiohttp/resolver.py | 274 + .../site-packages/aiohttp/streams.py | 758 ++ .../site-packages/aiohttp/tcp_helpers.py | 37 + .../site-packages/aiohttp/test_utils.py | 774 ++ .../site-packages/aiohttp/tracing.py | 455 + .../site-packages/aiohttp/typedefs.py | 69 + .../python3.9/site-packages/aiohttp/web.py | 592 ++ .../site-packages/aiohttp/web_app.py | 620 ++ .../site-packages/aiohttp/web_exceptions.py | 452 + .../site-packages/aiohttp/web_fileresponse.py | 418 + .../site-packages/aiohttp/web_log.py | 216 + .../site-packages/aiohttp/web_middlewares.py | 121 + .../site-packages/aiohttp/web_protocol.py | 792 ++ .../site-packages/aiohttp/web_request.py | 914 ++ .../site-packages/aiohttp/web_response.py | 856 ++ .../site-packages/aiohttp/web_routedef.py | 214 + .../site-packages/aiohttp/web_runner.py | 399 + .../site-packages/aiohttp/web_server.py | 84 + .../aiohttp/web_urldispatcher.py | 1305 +++ .../python3.9/site-packages/aiohttp/web_ws.py | 631 ++ .../python3.9/site-packages/aiohttp/worker.py | 255 + .../aiosignal-1.4.0.dist-info/INSTALLER | 1 + .../aiosignal-1.4.0.dist-info/METADATA | 112 + .../aiosignal-1.4.0.dist-info/RECORD | 9 + .../aiosignal-1.4.0.dist-info/WHEEL | 5 + .../licenses/LICENSE | 201 + .../aiosignal-1.4.0.dist-info/top_level.txt | 1 + .../site-packages/aiosignal/__init__.py | 59 + .../site-packages/aiosignal/py.typed | 0 .../async_timeout-5.0.1.dist-info/INSTALLER | 1 + .../async_timeout-5.0.1.dist-info/LICENSE | 13 + .../async_timeout-5.0.1.dist-info/METADATA | 163 + .../async_timeout-5.0.1.dist-info/RECORD | 10 + .../async_timeout-5.0.1.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../async_timeout-5.0.1.dist-info/zip-safe | 1 + .../site-packages/async_timeout/__init__.py | 276 + .../site-packages/async_timeout/py.typed | 1 + .../python3.9/site-packages/attr/__init__.py | 104 + .../python3.9/site-packages/attr/__init__.pyi | 389 + .../lib/python3.9/site-packages/attr/_cmp.py | 160 + .../lib/python3.9/site-packages/attr/_cmp.pyi | 13 + .../python3.9/site-packages/attr/_compat.py | 99 + .../python3.9/site-packages/attr/_config.py | 31 + .../python3.9/site-packages/attr/_funcs.py | 497 + .../lib/python3.9/site-packages/attr/_make.py | 3362 +++++++ .../python3.9/site-packages/attr/_next_gen.py | 674 ++ .../site-packages/attr/_typing_compat.pyi | 15 + .../site-packages/attr/_version_info.py | 89 + .../site-packages/attr/_version_info.pyi | 9 + .../site-packages/attr/converters.py | 162 + .../site-packages/attr/converters.pyi | 19 + .../site-packages/attr/exceptions.py | 95 + .../site-packages/attr/exceptions.pyi | 17 + .../python3.9/site-packages/attr/filters.py | 72 + .../python3.9/site-packages/attr/filters.pyi | 6 + .../lib/python3.9/site-packages/attr/py.typed | 0 .../python3.9/site-packages/attr/setters.py | 79 + .../python3.9/site-packages/attr/setters.pyi | 20 + .../site-packages/attr/validators.py | 748 ++ .../site-packages/attr/validators.pyi | 140 + .../attrs-25.4.0.dist-info/INSTALLER | 1 + .../attrs-25.4.0.dist-info/METADATA | 235 + .../attrs-25.4.0.dist-info/RECORD | 55 + .../attrs-25.4.0.dist-info/WHEEL | 4 + .../attrs-25.4.0.dist-info/licenses/LICENSE | 21 + .../python3.9/site-packages/attrs/__init__.py | 72 + .../site-packages/attrs/__init__.pyi | 314 + .../site-packages/attrs/converters.py | 3 + .../site-packages/attrs/exceptions.py | 3 + .../python3.9/site-packages/attrs/filters.py | 3 + .../python3.9/site-packages/attrs/py.typed | 0 .../python3.9/site-packages/attrs/setters.py | 3 + .../site-packages/attrs/validators.py | 3 + .../caldav-2.2.6.dist-info/INSTALLER | 1 + .../caldav-2.2.6.dist-info/METADATA | 64 + .../caldav-2.2.6.dist-info/RECORD | 56 + .../caldav-2.2.6.dist-info/REQUESTED | 0 .../caldav-2.2.6.dist-info/WHEEL | 5 + .../licenses/COPYING.APACHE | 202 + .../licenses/COPYING.GPL | 674 ++ .../site-packages/caldav/__init__.py | 33 + .../site-packages/caldav/_version.py | 34 + .../caldav/calendarobjectresource.py | 1664 ++++ .../site-packages/caldav/collection.py | 1549 +++ .../caldav/compatibility_hints.py | 1175 +++ .../python3.9/site-packages/caldav/config.py | 125 + .../site-packages/caldav/davclient.py | 1311 +++ .../site-packages/caldav/davobject.py | 430 + .../site-packages/caldav/discovery.py | 561 ++ .../site-packages/caldav/elements/__init__.py | 1 + .../site-packages/caldav/elements/base.py | 106 + .../site-packages/caldav/elements/cdav.py | 223 + .../site-packages/caldav/elements/dav.py | 115 + .../site-packages/caldav/elements/ical.py | 14 + .../site-packages/caldav/lib/__init__.py | 0 .../site-packages/caldav/lib/debug.py | 16 + .../site-packages/caldav/lib/error.py | 151 + .../site-packages/caldav/lib/namespace.py | 24 + .../caldav/lib/python_utilities.py | 41 + .../python3.9/site-packages/caldav/lib/url.py | 213 + .../site-packages/caldav/lib/vcal.py | 264 + .../python3.9/site-packages/caldav/objects.py | 17 + .../python3.9/site-packages/caldav/py.typed | 0 .../site-packages/caldav/requests.py | 19 + .../python3.9/site-packages/caldav/search.py | 783 ++ .../INSTALLER | 1 + .../METADATA | 798 ++ .../charset_normalizer-3.4.6.dist-info/RECORD | 36 + .../charset_normalizer-3.4.6.dist-info/WHEEL | 6 + .../entry_points.txt | 2 + .../licenses/LICENSE | 21 + .../top_level.txt | 2 + .../charset_normalizer/__init__.py | 48 + .../charset_normalizer/__main__.py | 6 + .../site-packages/charset_normalizer/api.py | 974 ++ .../cd.cpython-39-darwin.so | Bin 0 -> 66592 bytes .../site-packages/charset_normalizer/cd.py | 454 + .../charset_normalizer/cli/__init__.py | 8 + .../charset_normalizer/cli/__main__.py | 362 + .../charset_normalizer/constant.py | 2050 ++++ .../charset_normalizer/legacy.py | 79 + .../md.cpython-39-darwin.so | Bin 0 -> 66592 bytes .../site-packages/charset_normalizer/md.py | 936 ++ .../charset_normalizer/models.py | 359 + .../site-packages/charset_normalizer/py.typed | 0 .../site-packages/charset_normalizer/utils.py | 422 + .../charset_normalizer/version.py | 8 + .../click-8.1.8.dist-info/INSTALLER | 1 + .../click-8.1.8.dist-info/LICENSE.txt | 28 + .../click-8.1.8.dist-info/METADATA | 74 + .../click-8.1.8.dist-info/RECORD | 38 + .../site-packages/click-8.1.8.dist-info/WHEEL | 4 + .../python3.9/site-packages/click/__init__.py | 75 + .../python3.9/site-packages/click/_compat.py | 623 ++ .../site-packages/click/_termui_impl.py | 788 ++ .../site-packages/click/_textwrap.py | 49 + .../site-packages/click/_winconsole.py | 279 + .../lib/python3.9/site-packages/click/core.py | 3047 ++++++ .../site-packages/click/decorators.py | 562 ++ .../site-packages/click/exceptions.py | 296 + .../site-packages/click/formatting.py | 301 + .../python3.9/site-packages/click/globals.py | 67 + .../python3.9/site-packages/click/parser.py | 531 + .../python3.9/site-packages/click/py.typed | 0 .../site-packages/click/shell_completion.py | 603 ++ .../python3.9/site-packages/click/termui.py | 784 ++ .../python3.9/site-packages/click/testing.py | 483 + .../python3.9/site-packages/click/types.py | 1093 ++ .../python3.9/site-packages/click/utils.py | 624 ++ .../site-packages/dateutil/__init__.py | 24 + .../site-packages/dateutil/_common.py | 43 + .../site-packages/dateutil/_version.py | 4 + .../site-packages/dateutil/easter.py | 89 + .../site-packages/dateutil/parser/__init__.py | 61 + .../site-packages/dateutil/parser/_parser.py | 1613 +++ .../dateutil/parser/isoparser.py | 416 + .../site-packages/dateutil/relativedelta.py | 599 ++ .../python3.9/site-packages/dateutil/rrule.py | 1737 ++++ .../site-packages/dateutil/tz/__init__.py | 12 + .../site-packages/dateutil/tz/_common.py | 419 + .../site-packages/dateutil/tz/_factories.py | 80 + .../python3.9/site-packages/dateutil/tz/tz.py | 1849 ++++ .../site-packages/dateutil/tz/win.py | 370 + .../python3.9/site-packages/dateutil/tzwin.py | 2 + .../python3.9/site-packages/dateutil/utils.py | 71 + .../dateutil/zoneinfo/__init__.py | 167 + .../zoneinfo/dateutil-zoneinfo.tar.gz | Bin 0 -> 156400 bytes .../dateutil/zoneinfo/rebuild.py | 75 + .../site-packages/distutils-precedence.pth | 1 + .../python3.9/site-packages/dns/__init__.py | 70 + .../site-packages/dns/_asyncbackend.py | 100 + .../site-packages/dns/_asyncio_backend.py | 275 + .venv/lib/python3.9/site-packages/dns/_ddr.py | 154 + .../python3.9/site-packages/dns/_features.py | 95 + .../site-packages/dns/_immutable_ctx.py | 76 + .../site-packages/dns/_trio_backend.py | 253 + .../site-packages/dns/asyncbackend.py | 101 + .../python3.9/site-packages/dns/asyncquery.py | 913 ++ .../site-packages/dns/asyncresolver.py | 475 + .../lib/python3.9/site-packages/dns/dnssec.py | 1247 +++ .../site-packages/dns/dnssecalgs/__init__.py | 121 + .../site-packages/dns/dnssecalgs/base.py | 89 + .../dns/dnssecalgs/cryptography.py | 68 + .../site-packages/dns/dnssecalgs/dsa.py | 106 + .../site-packages/dns/dnssecalgs/ecdsa.py | 97 + .../site-packages/dns/dnssecalgs/eddsa.py | 70 + .../site-packages/dns/dnssecalgs/rsa.py | 124 + .../site-packages/dns/dnssectypes.py | 71 + .venv/lib/python3.9/site-packages/dns/e164.py | 116 + .venv/lib/python3.9/site-packages/dns/edns.py | 572 ++ .../python3.9/site-packages/dns/entropy.py | 130 + .venv/lib/python3.9/site-packages/dns/enum.py | 116 + .../python3.9/site-packages/dns/exception.py | 169 + .../lib/python3.9/site-packages/dns/flags.py | 123 + .../lib/python3.9/site-packages/dns/grange.py | 72 + .../python3.9/site-packages/dns/immutable.py | 68 + .venv/lib/python3.9/site-packages/dns/inet.py | 197 + .venv/lib/python3.9/site-packages/dns/ipv4.py | 77 + .venv/lib/python3.9/site-packages/dns/ipv6.py | 217 + .../python3.9/site-packages/dns/message.py | 1933 ++++ .venv/lib/python3.9/site-packages/dns/name.py | 1284 +++ .../python3.9/site-packages/dns/namedict.py | 109 + .../python3.9/site-packages/dns/nameserver.py | 363 + .venv/lib/python3.9/site-packages/dns/node.py | 359 + .../lib/python3.9/site-packages/dns/opcode.py | 117 + .../lib/python3.9/site-packages/dns/py.typed | 0 .../lib/python3.9/site-packages/dns/query.py | 1665 ++++ .../site-packages/dns/quic/__init__.py | 80 + .../site-packages/dns/quic/_asyncio.py | 267 + .../site-packages/dns/quic/_common.py | 339 + .../python3.9/site-packages/dns/quic/_sync.py | 295 + .../python3.9/site-packages/dns/quic/_trio.py | 246 + .../lib/python3.9/site-packages/dns/rcode.py | 168 + .../lib/python3.9/site-packages/dns/rdata.py | 911 ++ .../python3.9/site-packages/dns/rdataclass.py | 118 + .../python3.9/site-packages/dns/rdataset.py | 512 + .../python3.9/site-packages/dns/rdatatype.py | 336 + .../site-packages/dns/rdtypes/ANY/AFSDB.py | 45 + .../site-packages/dns/rdtypes/ANY/AMTRELAY.py | 91 + .../site-packages/dns/rdtypes/ANY/AVC.py | 26 + .../site-packages/dns/rdtypes/ANY/CAA.py | 71 + .../site-packages/dns/rdtypes/ANY/CDNSKEY.py | 33 + .../site-packages/dns/rdtypes/ANY/CDS.py | 29 + .../site-packages/dns/rdtypes/ANY/CERT.py | 116 + .../site-packages/dns/rdtypes/ANY/CNAME.py | 28 + .../site-packages/dns/rdtypes/ANY/CSYNC.py | 68 + .../site-packages/dns/rdtypes/ANY/DLV.py | 24 + .../site-packages/dns/rdtypes/ANY/DNAME.py | 27 + .../site-packages/dns/rdtypes/ANY/DNSKEY.py | 33 + .../site-packages/dns/rdtypes/ANY/DS.py | 24 + .../site-packages/dns/rdtypes/ANY/EUI48.py | 30 + .../site-packages/dns/rdtypes/ANY/EUI64.py | 30 + .../site-packages/dns/rdtypes/ANY/GPOS.py | 126 + .../site-packages/dns/rdtypes/ANY/HINFO.py | 64 + .../site-packages/dns/rdtypes/ANY/HIP.py | 85 + .../site-packages/dns/rdtypes/ANY/ISDN.py | 78 + .../site-packages/dns/rdtypes/ANY/L32.py | 41 + .../site-packages/dns/rdtypes/ANY/L64.py | 47 + .../site-packages/dns/rdtypes/ANY/LOC.py | 353 + .../site-packages/dns/rdtypes/ANY/LP.py | 42 + .../site-packages/dns/rdtypes/ANY/MX.py | 24 + .../site-packages/dns/rdtypes/ANY/NID.py | 47 + .../site-packages/dns/rdtypes/ANY/NINFO.py | 26 + .../site-packages/dns/rdtypes/ANY/NS.py | 24 + .../site-packages/dns/rdtypes/ANY/NSEC.py | 67 + .../site-packages/dns/rdtypes/ANY/NSEC3.py | 126 + .../dns/rdtypes/ANY/NSEC3PARAM.py | 69 + .../dns/rdtypes/ANY/OPENPGPKEY.py | 53 + .../site-packages/dns/rdtypes/ANY/OPT.py | 77 + .../site-packages/dns/rdtypes/ANY/PTR.py | 24 + .../site-packages/dns/rdtypes/ANY/RESINFO.py | 24 + .../site-packages/dns/rdtypes/ANY/RP.py | 58 + .../site-packages/dns/rdtypes/ANY/RRSIG.py | 157 + .../site-packages/dns/rdtypes/ANY/RT.py | 24 + .../site-packages/dns/rdtypes/ANY/SMIMEA.py | 9 + .../site-packages/dns/rdtypes/ANY/SOA.py | 86 + .../site-packages/dns/rdtypes/ANY/SPF.py | 26 + .../site-packages/dns/rdtypes/ANY/SSHFP.py | 68 + .../site-packages/dns/rdtypes/ANY/TKEY.py | 142 + .../site-packages/dns/rdtypes/ANY/TLSA.py | 9 + .../site-packages/dns/rdtypes/ANY/TSIG.py | 160 + .../site-packages/dns/rdtypes/ANY/TXT.py | 24 + .../site-packages/dns/rdtypes/ANY/URI.py | 79 + .../site-packages/dns/rdtypes/ANY/WALLET.py | 9 + .../site-packages/dns/rdtypes/ANY/X25.py | 57 + .../site-packages/dns/rdtypes/ANY/ZONEMD.py | 66 + .../site-packages/dns/rdtypes/ANY/__init__.py | 70 + .../site-packages/dns/rdtypes/CH/A.py | 59 + .../site-packages/dns/rdtypes/CH/__init__.py | 22 + .../site-packages/dns/rdtypes/IN/A.py | 51 + .../site-packages/dns/rdtypes/IN/AAAA.py | 51 + .../site-packages/dns/rdtypes/IN/APL.py | 150 + .../site-packages/dns/rdtypes/IN/DHCID.py | 54 + .../site-packages/dns/rdtypes/IN/HTTPS.py | 9 + .../site-packages/dns/rdtypes/IN/IPSECKEY.py | 91 + .../site-packages/dns/rdtypes/IN/KX.py | 24 + .../site-packages/dns/rdtypes/IN/NAPTR.py | 110 + .../site-packages/dns/rdtypes/IN/NSAP.py | 60 + .../site-packages/dns/rdtypes/IN/NSAP_PTR.py | 24 + .../site-packages/dns/rdtypes/IN/PX.py | 73 + .../site-packages/dns/rdtypes/IN/SRV.py | 75 + .../site-packages/dns/rdtypes/IN/SVCB.py | 9 + .../site-packages/dns/rdtypes/IN/WKS.py | 100 + .../site-packages/dns/rdtypes/IN/__init__.py | 35 + .../site-packages/dns/rdtypes/__init__.py | 33 + .../site-packages/dns/rdtypes/dnskeybase.py | 87 + .../site-packages/dns/rdtypes/dsbase.py | 85 + .../site-packages/dns/rdtypes/euibase.py | 70 + .../site-packages/dns/rdtypes/mxbase.py | 87 + .../site-packages/dns/rdtypes/nsbase.py | 63 + .../site-packages/dns/rdtypes/svcbbase.py | 585 ++ .../site-packages/dns/rdtypes/tlsabase.py | 71 + .../site-packages/dns/rdtypes/txtbase.py | 106 + .../site-packages/dns/rdtypes/util.py | 257 + .../python3.9/site-packages/dns/renderer.py | 346 + .../python3.9/site-packages/dns/resolver.py | 2053 ++++ .../site-packages/dns/reversename.py | 105 + .../lib/python3.9/site-packages/dns/rrset.py | 285 + .../lib/python3.9/site-packages/dns/serial.py | 118 + .venv/lib/python3.9/site-packages/dns/set.py | 308 + .../python3.9/site-packages/dns/tokenizer.py | 708 ++ .../site-packages/dns/transaction.py | 649 ++ .venv/lib/python3.9/site-packages/dns/tsig.py | 352 + .../site-packages/dns/tsigkeyring.py | 68 + .venv/lib/python3.9/site-packages/dns/ttl.py | 92 + .../lib/python3.9/site-packages/dns/update.py | 386 + .../python3.9/site-packages/dns/version.py | 58 + .../python3.9/site-packages/dns/versioned.py | 318 + .../python3.9/site-packages/dns/win32util.py | 242 + .venv/lib/python3.9/site-packages/dns/wire.py | 89 + .venv/lib/python3.9/site-packages/dns/xfr.py | 343 + .venv/lib/python3.9/site-packages/dns/zone.py | 1434 +++ .../python3.9/site-packages/dns/zonefile.py | 744 ++ .../python3.9/site-packages/dns/zonetypes.py | 37 + .../dnspython-2.7.0.dist-info/INSTALLER | 1 + .../dnspython-2.7.0.dist-info/METADATA | 149 + .../dnspython-2.7.0.dist-info/RECORD | 294 + .../dnspython-2.7.0.dist-info/WHEEL | 4 + .../licenses/LICENSE | 35 + .../frozenlist-1.8.0.dist-info/INSTALLER | 1 + .../frozenlist-1.8.0.dist-info/METADATA | 672 ++ .../frozenlist-1.8.0.dist-info/RECORD | 12 + .../frozenlist-1.8.0.dist-info/WHEEL | 6 + .../licenses/LICENSE | 201 + .../frozenlist-1.8.0.dist-info/top_level.txt | 1 + .../site-packages/frozenlist/__init__.py | 86 + .../site-packages/frozenlist/__init__.pyi | 47 + .../_frozenlist.cpython-39-darwin.so | Bin 0 -> 144176 bytes .../site-packages/frozenlist/_frozenlist.pyx | 148 + .../site-packages/frozenlist/py.typed | 1 + .../h11-0.16.0.dist-info/INSTALLER | 1 + .../h11-0.16.0.dist-info/METADATA | 202 + .../site-packages/h11-0.16.0.dist-info/RECORD | 29 + .../site-packages/h11-0.16.0.dist-info/WHEEL | 5 + .../h11-0.16.0.dist-info/licenses/LICENSE.txt | 22 + .../h11-0.16.0.dist-info/top_level.txt | 1 + .../python3.9/site-packages/h11/__init__.py | 62 + .../lib/python3.9/site-packages/h11/_abnf.py | 132 + .../site-packages/h11/_connection.py | 659 ++ .../python3.9/site-packages/h11/_events.py | 369 + .../python3.9/site-packages/h11/_headers.py | 282 + .../python3.9/site-packages/h11/_readers.py | 250 + .../site-packages/h11/_receivebuffer.py | 153 + .../lib/python3.9/site-packages/h11/_state.py | 365 + .../lib/python3.9/site-packages/h11/_util.py | 135 + .../python3.9/site-packages/h11/_version.py | 16 + .../python3.9/site-packages/h11/_writers.py | 145 + .../lib/python3.9/site-packages/h11/py.typed | 1 + .../icalendar-6.3.2.dist-info/INSTALLER | 1 + .../icalendar-6.3.2.dist-info/METADATA | 234 + .../icalendar-6.3.2.dist-info/RECORD | 305 + .../icalendar-6.3.2.dist-info/WHEEL | 4 + .../entry_points.txt | 2 + .../licenses/LICENSE.rst | 26 + .../site-packages/icalendar/__init__.py | 128 + .../site-packages/icalendar/_version.py | 34 + .../site-packages/icalendar/alarms.py | 378 + .../python3.9/site-packages/icalendar/attr.py | 700 ++ .../python3.9/site-packages/icalendar/cal.py | 2201 ++++ .../site-packages/icalendar/caselessdict.py | 108 + .../python3.9/site-packages/icalendar/cli.py | 105 + .../site-packages/icalendar/enums.py | 129 + .../site-packages/icalendar/error.py | 67 + .../site-packages/icalendar/param.py | 454 + .../site-packages/icalendar/parser.py | 482 + .../site-packages/icalendar/parser_tools.py | 63 + .../python3.9/site-packages/icalendar/prop.py | 2044 ++++ .../site-packages/icalendar/tests/__init__.py | 0 .../rfc_5545_absolute_alarm_example.ics | 7 + .../icalendar/tests/alarms/rfc_5545_end.ics | 8 + .../icalendar/tests/alarms/start_date.ics | 8 + .../icalendar/tests/attr/__init__.py | 1 + .../icalendar/tests/attr/test_alarm.py | 54 + .../icalendar/tests/attr/test_component.py | 92 + .../icalendar/tests/attr/test_exdates.py | 62 + .../icalendar/tests/attr/test_rdate.py | 111 + .../icalendar/tests/attr/test_rrule.py | 45 + .../tests/calendars/alarm_etar_future.ics | 235 + .../calendars/alarm_etar_notification.ics | 235 + .../alarm_etar_notification_clicked.ics | 225 + .../calendars/alarm_google_acknowledged.ics | 60 + .../tests/calendars/alarm_google_future.ics | 60 + .../calendars/alarm_thunderbird_2_future.ics | 624 ++ ...derbird_2_notification_5_min_postponed.ics | 626 ++ ...otification_5_min_postponed_and_closed.ics | 625 ++ ...fication_5_min_postponed_and_popped_up.ics | 626 ++ ...m_thunderbird_2_notification_popped_up.ics | 624 ++ .../calendars/alarm_thunderbird_closed.ics | 625 ++ .../calendars/alarm_thunderbird_future.ics | 624 ++ .../alarm_thunderbird_snoozed_until_1457.ics | 626 ++ .../tests/calendars/america_new_york.ics | 61 + .../america_new_york_forward_reference.ics | 61 + .../tests/calendars/big_bad_calendar.ics | 41 + .../tests/calendars/bom_calendar.ics | 2 + .../icalendar/tests/calendars/broken_ical.ics | 7 + .../tests/calendars/calendar_with_unicode.ics | 7 + .../created_calendar_with_unicode_fields.ics | 23 + .../icalendar/tests/calendars/example.ics | 40 + .../calendars/issue_104_broken_calendar.ics | 14 + .../issue_156_RDATE_with_PERIOD_TZID_khal.ics | 25 + ...ssue_156_RDATE_with_PERIOD_TZID_khal_2.ics | 55 + .../calendars/issue_165_missing_event.ics | 28 + .../calendars/issue_168_expected_output.ics | 7 + .../tests/calendars/issue_168_input.ics | 8 + ...omponent_with_invalid_name_represented.ics | 2 + ...ue_178_custom_component_contains_other.ics | 7 + ...ssue_178_custom_component_inside_other.ics | 5 + .../tests/calendars/issue_218_bad_tzid.ics | 24 + ..._to_parse_timezone_with_non_ascii_tzid.ics | 23 + ...eriods_in_freebusy_multiple_freebusies.ics | 21 + ...tiple_periods_in_freebusy_one_freebusy.ics | 14 + .../calendars/issue_322_expected_calendar.ics | 6 + .../issue_348_exception_parsing_value.ics | 32 + .../icalendar/tests/calendars/issue_350.ics | 36 + .../issue_466_convert_tzid_with_slash.ics | 12 + .../issue_466_respect_unique_timezone.ics | 29 + ...sue_526_calendar_with_different_events.ics | 18 + .../issue_526_calendar_with_event_subset.ics | 11 + .../issue_526_calendar_with_events.ics | 18 + ...sue_526_calendar_with_shuffeled_events.ics | 18 + .../issue_722_missing_VTIMEZONE_custom.ics | 5 + .../calendars/issue_722_missing_timezones.ics | 20 + ...ssue_722_timezone_transition_ambiguity.ics | 44 + .../tests/calendars/issue_798_freebusy.ics | 12 + .../tests/calendars/issue_798_related_to.ics | 8 + .../calendars/issue_836_do_not_quote_tzid.ics | 27 + .../multiple_calendar_components.ics | 80 + .../tests/calendars/pacific_fiji.ics | 52 + .../tests/calendars/parsing_error.ics | 21 + .../calendars/parsing_error_in_UTC_offset.ics | 11 + .../tests/calendars/period_with_timezone.ics | 32 + .../calendars/pr_480_summary_with_colon.ics | 7 + .../tests/calendars/property_params.ics | 21 + .../calendars/rfc_5545_RDATE_example.ics | 33 + .../icalendar/tests/calendars/rfc_6868.ics | 6 + .../icalendar/tests/calendars/rfc_7529.ics | 29 + .../tests/calendars/small_bad_calendar.ics | 3 + .../icalendar/tests/calendars/time.ics | 3 + .../tests/calendars/timezone_rdate.ics | 55 + .../tests/calendars/timezone_same_start.ics | 27 + .../timezone_same_start_and_offset.ics | 23 + .../icalendar/tests/calendars/timezoned.ics | 36 + .../icalendar/tests/calendars/x_location.ics | 48 + .../site-packages/icalendar/tests/conftest.py | 358 + .../events/event_with_escaped_character1.ics | 3 + .../events/event_with_escaped_character2.ics | 3 + .../events/event_with_escaped_character3.ics | 3 + .../events/event_with_escaped_character4.ics | 3 + .../events/event_with_escaped_characters.ics | 3 + .../tests/events/event_with_recurrence.ics | 7 + ..._recurrence_exdates_on_different_lines.ics | 12 + .../tests/events/event_with_rsvp.ics | 3 + .../events/event_with_unicode_fields.ics | 9 + .../events/event_with_unicode_organizer.ics | 3 + ...00_transformed_doctests_into_unittests.ics | 3 + ...alendar_chokes_on_umlauts_in_organizer.ics | 14 + .../events/issue_104_mark_events_broken.ics | 10 + .../issue_112_missing_tzinfo_on_exdate.ics | 20 + .../events/issue_156_RDATE_with_PERIOD.ics | 7 + .../issue_156_RDATE_with_PERIOD_list.ics | 8 + .../issue_157_removes_trailing_semicolon.ics | 4 + ...ue_184_broken_representation_of_period.ics | 6 + .../tests/events/issue_464_invalid_rdate.ics | 7 + .../issue_53_description_parsed_properly.ics | 19 + .../issue_64_event_with_ascii_summary.ics | 3 + .../issue_64_event_with_non_ascii_summary.ics | 3 + .../issue_70_rrule_causes_attribute_error.ics | 12 + .../tests/events/issue_82_expected_output.ics | 3 + .../tests/events/rfc_9074_example_1.ics | 16 + .../tests/events/rfc_9074_example_2.ics | 25 + .../tests/events/rfc_9074_example_3.ics | 25 + .../tests/events/rfc_9074_example_4.ics | 26 + .../events/rfc_9074_example_proximity.ics | 14 + .../icalendar/tests/fuzzed/__init__.py | 25 + ..._from_downloaded_clusterfuzz_test_cases.sh | 45 + .../tests/fuzzed/test_fuzzed_calendars.py | 12 + .../tests/hypothesis/test_fuzzing.py | 34 + .../icalendar/tests/prop/__init__.py | 1 + .../icalendar/tests/prop/test_constructors.py | 120 + .../tests/prop/test_identity_and_equality.py | 70 + .../tests/prop/test_property_values.py | 19 + .../icalendar/tests/prop/test_unit.py | 363 + .../icalendar/tests/prop/test_vBinary.py | 45 + .../icalendar/tests/prop/test_vBoolean.py | 22 + .../icalendar/tests/prop/test_vCalAddress.py | 57 + .../icalendar/tests/prop/test_vDDDTypes.py | 37 + .../icalendar/tests/prop/test_vDatetime.py | 47 + .../icalendar/tests/prop/test_vPeriod.py | 63 + .../icalendar/tests/prop/test_vWeekday.py | 27 + .../prop/test_windows_to_olson_mapping.py | 22 + .../icalendar/tests/test_bom_calendar.py | 4 + .../icalendar/tests/test_cli_tool.py | 112 + .../tests/test_components_break_on_bad_ics.py | 48 + .../icalendar/tests/test_create_release.sh | 32 + .../icalendar/tests/test_encoding.py | 98 + .../icalendar/tests/test_equality.py | 188 + .../icalendar/tests/test_examples.py | 72 + .../icalendar/tests/test_icalendar.py | 304 + .../icalendar/tests/test_issue_116.py | 28 + .../tests/test_issue_165_missing_event.py | 9 + ...68_parsing_invalid_calendars_no_warning.py | 16 + .../tests/test_issue_218_parse_calendar.py | 32 + .../icalendar/tests/test_issue_27_period.py | 19 + .../test_issue_301_add_rrule_as_string.py | 14 + .../test_issue_318_skip_default_parameters.py | 32 + ...aracters_split_into_multiple_categories.py | 10 + .../tests/test_issue_336_dateutil_timezone.py | 30 + .../test_issue_348_exception_parsing_value.py | 23 + .../icalendar/tests/test_issue_350.py | 9 + .../test_issue_500_vboolean_for_parameter.py | 10 + ...test_issue_557_encode_native_parameters.py | 151 + .../test_issue_662_component_properties.py | 711 ++ .../test_issue_716_alarm_time_computation.py | 438 + .../tests/test_issue_720_uid_property.py | 12 + .../test_issue_722_generate_vtimezone.py | 437 + .../test_issue_798_property_parameters.py | 199 + .../icalendar/tests/test_issue_802.py | 54 + .../icalendar/tests/test_issue_828.py | 162 + .../tests/test_issue_836_do_not_quote_tzid.py | 80 + .../icalendar/tests/test_multiple.py | 33 + .../icalendar/tests/test_oss_fuzz_errors.py | 20 + .../icalendar/tests/test_parsing.py | 257 + .../icalendar/tests/test_period.py | 112 + .../icalendar/tests/test_property_params.py | 145 + .../tests/test_pytz_zoneinfo_integration.py | 104 + .../icalendar/tests/test_recurrence.py | 130 + .../icalendar/tests/test_rfc_6868.py | 108 + .../icalendar/tests/test_rfc_7529.py | 86 + .../icalendar/tests/test_rfc_7986.py | 191 + .../tests/test_rfc_7986_categories.py | 71 + .../icalendar/tests/test_rfc_9074.py | 44 + .../icalendar/tests/test_time.py | 30 + .../tests/test_timezone_identification.py | 32 + .../icalendar/tests/test_timezoned.py | 452 + .../icalendar/tests/test_unit_cal.py | 489 + .../icalendar/tests/test_unit_caselessdict.py | 127 + .../icalendar/tests/test_unit_parser_tools.py | 33 + .../icalendar/tests/test_unit_tools.py | 88 + .../icalendar/tests/test_with_doctest.py | 106 + .../icalendar/tests/timezone_ids.py | 599 ++ .../timezones/issue_237_brazilia_standard.ics | 17 + .../issue_53_tzid_parsed_properly.ics | 19 + ...parse_error_on_utc_offset_with_seconds.ics | 10 + .../tests/timezones/pacific_fiji.ics | 42 + .../icalendar/timezone/__init__.py | 27 + .../timezone/equivalent_timezone_ids.py | 148 + .../equivalent_timezone_ids_result.py | 3120 ++++++ .../icalendar/timezone/provider.py | 57 + .../site-packages/icalendar/timezone/pytz.py | 72 + .../site-packages/icalendar/timezone/tzid.py | 112 + .../site-packages/icalendar/timezone/tzp.py | 146 + .../icalendar/timezone/windows_to_olson.py | 119 + .../icalendar/timezone/zoneinfo.py | 160 + .../site-packages/icalendar/tools.py | 113 + .../site-packages/icalendar/version.py | 14 + .../INSTALLER | 1 + .../METADATA | 172 + .../icalendar_searcher-1.0.3.dist-info/RECORD | 15 + .../icalendar_searcher-1.0.3.dist-info/WHEEL | 4 + .../licenses/LICENSE.md | 660 ++ .../icalendar_searcher/__init__.py | 21 + .../icalendar_searcher/collation.py | 232 + .../icalendar_searcher/filters.py | 456 + .../icalendar_searcher/searcher.py | 886 ++ .../site-packages/icalendar_searcher/utils.py | 58 + .../idna-3.11.dist-info/INSTALLER | 1 + .../idna-3.11.dist-info/METADATA | 209 + .../site-packages/idna-3.11.dist-info/RECORD | 22 + .../site-packages/idna-3.11.dist-info/WHEEL | 4 + .../idna-3.11.dist-info/licenses/LICENSE.md | 31 + .../python3.9/site-packages/idna/__init__.py | 45 + .../lib/python3.9/site-packages/idna/codec.py | 122 + .../python3.9/site-packages/idna/compat.py | 15 + .../lib/python3.9/site-packages/idna/core.py | 437 + .../python3.9/site-packages/idna/idnadata.py | 4309 ++++++++ .../python3.9/site-packages/idna/intranges.py | 57 + .../site-packages/idna/package_data.py | 1 + .../lib/python3.9/site-packages/idna/py.typed | 0 .../python3.9/site-packages/idna/uts46data.py | 8841 +++++++++++++++++ .../jh2-5.0.10.dist-info/INSTALLER | 1 + .../jh2-5.0.10.dist-info/METADATA | 106 + .../site-packages/jh2-5.0.10.dist-info/RECORD | 54 + .../site-packages/jh2-5.0.10.dist-info/WHEEL | 4 + .../jh2-5.0.10.dist-info/licenses/LICENSE | 21 + .../python3.9/site-packages/jh2/__init__.py | 10 + .../site-packages/jh2/_hazmat.abi3.so | Bin 0 -> 1361504 bytes .../python3.9/site-packages/jh2/_hazmat.pyi | 21 + .../lib/python3.9/site-packages/jh2/config.py | 203 + .../python3.9/site-packages/jh2/connection.py | 2131 ++++ .../lib/python3.9/site-packages/jh2/errors.py | 78 + .../lib/python3.9/site-packages/jh2/events.py | 651 ++ .../python3.9/site-packages/jh2/exceptions.py | 205 + .../site-packages/jh2/frame_buffer.py | 158 + .../site-packages/jh2/hpack/__init__.py | 30 + .../site-packages/jh2/hpack/exceptions.py | 55 + .../site-packages/jh2/hpack/hpack.py | 610 ++ .../site-packages/jh2/hpack/huffman.py | 66 + .../jh2/hpack/huffman_constants.py | 530 + .../site-packages/jh2/hpack/huffman_table.py | 4486 +++++++++ .../site-packages/jh2/hpack/struct.py | 42 + .../site-packages/jh2/hpack/table.py | 237 + .../site-packages/jh2/hyperframe/__init__.py | 6 + .../jh2/hyperframe/exceptions.py | 72 + .../site-packages/jh2/hyperframe/flags.py | 54 + .../site-packages/jh2/hyperframe/frame.py | 1000 ++ .../site-packages/jh2/hyperframe/py.typed | 0 .../python3.9/site-packages/jh2/settings.py | 338 + .../lib/python3.9/site-packages/jh2/stream.py | 1474 +++ .../python3.9/site-packages/jh2/utilities.py | 694 ++ .../python3.9/site-packages/jh2/windows.py | 139 + .../lxml-6.0.2.dist-info/INSTALLER | 1 + .../lxml-6.0.2.dist-info/METADATA | 103 + .../site-packages/lxml-6.0.2.dist-info/RECORD | 204 + .../site-packages/lxml-6.0.2.dist-info/WHEEL | 6 + .../lxml-6.0.2.dist-info/licenses/LICENSE.txt | 31 + .../licenses/LICENSES.txt | 29 + .../lxml-6.0.2.dist-info/top_level.txt | 1 + .../site-packages/lxml/ElementInclude.py | 244 + .../python3.9/site-packages/lxml/__init__.py | 22 + .../lxml/_elementpath.cpython-39-darwin.so | Bin 0 -> 497776 bytes .../site-packages/lxml/_elementpath.py | 343 + .../site-packages/lxml/apihelpers.pxi | 1801 ++++ .../lxml/builder.cpython-39-darwin.so | Bin 0 -> 291248 bytes .../python3.9/site-packages/lxml/builder.py | 243 + .../site-packages/lxml/classlookup.pxi | 580 ++ .../python3.9/site-packages/lxml/cleanup.pxi | 215 + .../python3.9/site-packages/lxml/cssselect.py | 101 + .../python3.9/site-packages/lxml/debug.pxi | 36 + .../site-packages/lxml/docloader.pxi | 178 + .../site-packages/lxml/doctestcompare.py | 488 + .../lib/python3.9/site-packages/lxml/dtd.pxi | 479 + .../lxml/etree.cpython-39-darwin.so | Bin 0 -> 9667432 bytes .../lib/python3.9/site-packages/lxml/etree.h | 244 + .../python3.9/site-packages/lxml/etree.pyx | 3853 +++++++ .../python3.9/site-packages/lxml/etree_api.h | 204 + .../site-packages/lxml/extensions.pxi | 830 ++ .../site-packages/lxml/html/ElementSoup.py | 10 + .../site-packages/lxml/html/__init__.py | 1927 ++++ .../site-packages/lxml/html/_diffcommand.py | 86 + .../lxml/html/_difflib.cpython-39-darwin.so | Bin 0 -> 1121904 bytes .../site-packages/lxml/html/_difflib.py | 2106 ++++ .../site-packages/lxml/html/_html5builder.py | 100 + .../site-packages/lxml/html/_setmixin.py | 56 + .../site-packages/lxml/html/builder.py | 173 + .../site-packages/lxml/html/clean.py | 21 + .../python3.9/site-packages/lxml/html/defs.py | 135 + .../lxml/html/diff.cpython-39-darwin.so | Bin 0 -> 732312 bytes .../python3.9/site-packages/lxml/html/diff.py | 972 ++ .../site-packages/lxml/html/formfill.py | 299 + .../site-packages/lxml/html/html5parser.py | 260 + .../site-packages/lxml/html/soupparser.py | 314 + .../site-packages/lxml/html/usedoctest.py | 13 + .../site-packages/lxml/includes/__init__.pxd | 0 .../site-packages/lxml/includes/__init__.py | 0 .../site-packages/lxml/includes/c14n.pxd | 25 + .../site-packages/lxml/includes/config.pxd | 3 + .../site-packages/lxml/includes/dtdvalid.pxd | 18 + .../site-packages/lxml/includes/etree_defs.h | 390 + .../lxml/includes/etreepublic.pxd | 237 + .../lxml/includes/extlibs/__init__.py | 0 .../lxml/includes/extlibs/libcharset.h | 45 + .../lxml/includes/extlibs/localcharset.h | 137 + .../lxml/includes/extlibs/zconf.h | 543 + .../lxml/includes/extlibs/zlib.h | 1938 ++++ .../lxml/includes/htmlparser.pxd | 56 + .../lxml/includes/libexslt/__init__.py | 0 .../lxml/includes/libexslt/exslt.h | 108 + .../lxml/includes/libexslt/exsltconfig.h | 70 + .../lxml/includes/libexslt/exsltexports.h | 63 + .../lxml/includes/libxml/HTMLparser.h | 339 + .../lxml/includes/libxml/HTMLtree.h | 148 + .../site-packages/lxml/includes/libxml/SAX.h | 18 + .../site-packages/lxml/includes/libxml/SAX2.h | 170 + .../lxml/includes/libxml/__init__.py | 0 .../site-packages/lxml/includes/libxml/c14n.h | 115 + .../lxml/includes/libxml/catalog.h | 183 + .../lxml/includes/libxml/chvalid.h | 230 + .../lxml/includes/libxml/debugXML.h | 79 + .../site-packages/lxml/includes/libxml/dict.h | 82 + .../lxml/includes/libxml/encoding.h | 307 + .../lxml/includes/libxml/entities.h | 147 + .../lxml/includes/libxml/globals.h | 25 + .../site-packages/lxml/includes/libxml/hash.h | 251 + .../site-packages/lxml/includes/libxml/list.h | 137 + .../lxml/includes/libxml/nanoftp.h | 16 + .../lxml/includes/libxml/nanohttp.h | 98 + .../lxml/includes/libxml/parser.h | 1633 +++ .../lxml/includes/libxml/parserInternals.h | 591 ++ .../lxml/includes/libxml/relaxng.h | 224 + .../lxml/includes/libxml/schemasInternals.h | 959 ++ .../lxml/includes/libxml/schematron.h | 143 + .../lxml/includes/libxml/threads.h | 81 + .../site-packages/lxml/includes/libxml/tree.h | 1326 +++ .../site-packages/lxml/includes/libxml/uri.h | 106 + .../lxml/includes/libxml/valid.h | 482 + .../lxml/includes/libxml/xinclude.h | 141 + .../lxml/includes/libxml/xlink.h | 193 + .../lxml/includes/libxml/xmlIO.h | 419 + .../lxml/includes/libxml/xmlautomata.h | 163 + .../lxml/includes/libxml/xmlerror.h | 962 ++ .../lxml/includes/libxml/xmlexports.h | 96 + .../lxml/includes/libxml/xmlmemory.h | 188 + .../lxml/includes/libxml/xmlmodule.h | 61 + .../lxml/includes/libxml/xmlreader.h | 444 + .../lxml/includes/libxml/xmlregexp.h | 116 + .../lxml/includes/libxml/xmlsave.h | 111 + .../lxml/includes/libxml/xmlschemas.h | 254 + .../lxml/includes/libxml/xmlschemastypes.h | 152 + .../lxml/includes/libxml/xmlstring.h | 140 + .../lxml/includes/libxml/xmlunicode.h | 15 + .../lxml/includes/libxml/xmlversion.h | 332 + .../lxml/includes/libxml/xmlwriter.h | 489 + .../lxml/includes/libxml/xpath.h | 569 ++ .../lxml/includes/libxml/xpathInternals.h | 639 ++ .../lxml/includes/libxml/xpointer.h | 48 + .../lxml/includes/libxslt/__init__.py | 0 .../lxml/includes/libxslt/attributes.h | 39 + .../lxml/includes/libxslt/documents.h | 93 + .../lxml/includes/libxslt/extensions.h | 262 + .../lxml/includes/libxslt/extra.h | 72 + .../lxml/includes/libxslt/functions.h | 78 + .../lxml/includes/libxslt/imports.h | 75 + .../lxml/includes/libxslt/keys.h | 53 + .../lxml/includes/libxslt/namespaces.h | 68 + .../lxml/includes/libxslt/numbersInternals.h | 73 + .../lxml/includes/libxslt/pattern.h | 84 + .../lxml/includes/libxslt/preproc.h | 43 + .../lxml/includes/libxslt/security.h | 104 + .../lxml/includes/libxslt/templates.h | 77 + .../lxml/includes/libxslt/transform.h | 207 + .../lxml/includes/libxslt/variables.h | 118 + .../lxml/includes/libxslt/xslt.h | 110 + .../lxml/includes/libxslt/xsltInternals.h | 1995 ++++ .../lxml/includes/libxslt/xsltconfig.h | 146 + .../lxml/includes/libxslt/xsltexports.h | 64 + .../lxml/includes/libxslt/xsltlocale.h | 44 + .../lxml/includes/libxslt/xsltutils.h | 343 + .../lxml/includes/lxml-version.h | 3 + .../site-packages/lxml/includes/relaxng.pxd | 64 + .../lxml/includes/schematron.pxd | 34 + .../site-packages/lxml/includes/tree.pxd | 492 + .../site-packages/lxml/includes/uri.pxd | 5 + .../site-packages/lxml/includes/xinclude.pxd | 22 + .../site-packages/lxml/includes/xmlerror.pxd | 860 ++ .../site-packages/lxml/includes/xmlparser.pxd | 318 + .../site-packages/lxml/includes/xmlschema.pxd | 35 + .../site-packages/lxml/includes/xpath.pxd | 136 + .../site-packages/lxml/includes/xslt.pxd | 190 + .../lxml/isoschematron/__init__.py | 348 + .../resources/rng/iso-schematron.rng | 709 ++ .../resources/xsl/RNG2Schtrn.xsl | 75 + .../resources/xsl/XSD2Schtrn.xsl | 77 + .../iso_abstract_expand.xsl | 313 + .../iso-schematron-xslt1/iso_dsdl_include.xsl | 1160 +++ .../iso_schematron_message.xsl | 55 + .../iso_schematron_skeleton_for_xslt1.xsl | 1796 ++++ .../iso_svrl_for_xslt1.xsl | 588 ++ .../xsl/iso-schematron-xslt1/readme.txt | 84 + .../site-packages/lxml/iterparse.pxi | 438 + .../python3.9/site-packages/lxml/lxml.etree.h | 244 + .../site-packages/lxml/lxml.etree_api.h | 204 + .../site-packages/lxml/nsclasses.pxi | 281 + .../lxml/objectify.cpython-39-darwin.so | Bin 0 -> 5235008 bytes .../site-packages/lxml/objectify.pyx | 2149 ++++ .../site-packages/lxml/objectpath.pxi | 332 + .../python3.9/site-packages/lxml/parser.pxi | 2071 ++++ .../site-packages/lxml/parsertarget.pxi | 180 + .../python3.9/site-packages/lxml/proxy.pxi | 622 ++ .../site-packages/lxml/public-api.pxi | 178 + .../site-packages/lxml/pyclasslookup.py | 3 + .../site-packages/lxml/readonlytree.pxi | 565 ++ .../python3.9/site-packages/lxml/relaxng.pxi | 165 + .../lxml/sax.cpython-39-darwin.so | Bin 0 -> 416008 bytes .venv/lib/python3.9/site-packages/lxml/sax.py | 285 + .../site-packages/lxml/saxparser.pxi | 875 ++ .../site-packages/lxml/schematron.pxi | 173 + .../site-packages/lxml/serializer.pxi | 1849 ++++ .../site-packages/lxml/usedoctest.py | 13 + .../python3.9/site-packages/lxml/xinclude.pxi | 67 + .../python3.9/site-packages/lxml/xmlerror.pxi | 1662 ++++ .../python3.9/site-packages/lxml/xmlid.pxi | 179 + .../site-packages/lxml/xmlschema.pxi | 215 + .../python3.9/site-packages/lxml/xpath.pxi | 487 + .../lib/python3.9/site-packages/lxml/xslt.pxi | 957 ++ .../python3.9/site-packages/lxml/xsltext.pxi | 242 + .../multidict-6.7.1.dist-info/INSTALLER | 1 + .../multidict-6.7.1.dist-info/METADATA | 149 + .../multidict-6.7.1.dist-info/RECORD | 16 + .../multidict-6.7.1.dist-info/WHEEL | 6 + .../licenses/LICENSE | 13 + .../multidict-6.7.1.dist-info/top_level.txt | 1 + .../site-packages/multidict/__init__.py | 60 + .../python3.9/site-packages/multidict/_abc.py | 73 + .../site-packages/multidict/_compat.py | 15 + .../multidict/_multidict.cpython-39-darwin.so | Bin 0 -> 121792 bytes .../site-packages/multidict/_multidict_py.py | 1242 +++ .../site-packages/multidict/py.typed | 1 + .../niquests-3.18.2.dist-info/INSTALLER | 1 + .../niquests-3.18.2.dist-info/METADATA | 284 + .../niquests-3.18.2.dist-info/RECORD | 110 + .../niquests-3.18.2.dist-info/WHEEL | 4 + .../licenses/LICENSE | 175 + .../site-packages/niquests/__init__.py | 187 + .../site-packages/niquests/__version__.py | 19 + .../site-packages/niquests/_async.py | 14 + .../site-packages/niquests/_compat.py | 94 + .../site-packages/niquests/_constant.py | 25 + .../site-packages/niquests/_typing.py | 14 + .../niquests/_vendor/__init__.py | 0 .../niquests/_vendor/kiss_headers/LICENSE | 21 + .../niquests/_vendor/kiss_headers/__init__.py | 157 + .../niquests/_vendor/kiss_headers/api.py | 209 + .../niquests/_vendor/kiss_headers/builder.py | 1563 +++ .../niquests/_vendor/kiss_headers/models.py | 1450 +++ .../niquests/_vendor/kiss_headers/py.typed | 0 .../_vendor/kiss_headers/serializer.py | 64 + .../_vendor/kiss_headers/structures.py | 95 + .../niquests/_vendor/kiss_headers/utils.py | 487 + .../niquests/_vendor/kiss_headers/version.py | 8 + .../site-packages/niquests/adapters.py | 2410 +++++ .../python3.9/site-packages/niquests/api.py | 638 ++ .../site-packages/niquests/async_api.py | 975 ++ .../site-packages/niquests/async_session.py | 1623 +++ .../python3.9/site-packages/niquests/auth.py | 429 + .../site-packages/niquests/cookies.py | 592 ++ .../site-packages/niquests/exceptions.py | 155 + .../niquests/extensions/__init__.py | 4 + .../niquests/extensions/pyodide/__init__.py | 394 + .../extensions/pyodide/_async/__init__.py | 451 + .../extensions/pyodide/_async/_sse.py | 152 + .../niquests/extensions/pyodide/_async/_ws.py | 124 + .../niquests/extensions/pyodide/_sse.py | 151 + .../niquests/extensions/pyodide/_ws.py | 174 + .../extensions/revocation/__init__.py | 19 + .../extensions/revocation/_crl/__init__.py | 374 + .../revocation/_crl/_async/__init__.py | 405 + .../extensions/revocation/_ocsp/__init__.py | 477 + .../revocation/_ocsp/_async/__init__.py | 492 + .../niquests/extensions/sgi/__init__.py | 325 + .../extensions/sgi/_async/__init__.py | 821 ++ .../niquests/extensions/sgi/_async/_sse.py | 175 + .../niquests/extensions/sgi/_async/_ws.py | 98 + .../niquests/extensions/sgi/_sse.py | 175 + .../niquests/extensions/sgi/_ws.py | 67 + .../extensions/unixsocket/__init__.py | 100 + .../extensions/unixsocket/_async/__init__.py | 101 + .../python3.9/site-packages/niquests/help.py | 227 + .../python3.9/site-packages/niquests/hooks.py | 436 + .../site-packages/niquests/models.py | 2006 ++++ .../site-packages/niquests/packages.py | 122 + .../python3.9/site-packages/niquests/py.typed | 0 .../site-packages/niquests/sessions.py | 1956 ++++ .../site-packages/niquests/status_codes.py | 126 + .../site-packages/niquests/structures.py | 283 + .../site-packages/niquests/typing.py | 206 + .../python3.9/site-packages/niquests/utils.py | 1475 +++ .../pip-21.2.4.dist-info/INSTALLER | 1 + .../pip-21.2.4.dist-info/LICENSE.txt | 20 + .../pip-21.2.4.dist-info/METADATA | 92 + .../site-packages/pip-21.2.4.dist-info/RECORD | 795 ++ .../pip-21.2.4.dist-info/REQUESTED | 0 .../site-packages/pip-21.2.4.dist-info/WHEEL | 5 + .../pip-21.2.4.dist-info/entry_points.txt | 5 + .../pip-21.2.4.dist-info/top_level.txt | 1 + .../python3.9/site-packages/pip/__init__.py | 13 + .../python3.9/site-packages/pip/__main__.py | 31 + .../site-packages/pip/_internal/__init__.py | 19 + .../site-packages/pip/_internal/build_env.py | 294 + .../site-packages/pip/_internal/cache.py | 287 + .../pip/_internal/cli/__init__.py | 4 + .../pip/_internal/cli/autocompletion.py | 163 + .../pip/_internal/cli/base_command.py | 214 + .../pip/_internal/cli/cmdoptions.py | 1009 ++ .../pip/_internal/cli/command_context.py | 27 + .../site-packages/pip/_internal/cli/main.py | 70 + .../pip/_internal/cli/main_parser.py | 87 + .../site-packages/pip/_internal/cli/parser.py | 292 + .../pip/_internal/cli/progress_bars.py | 250 + .../pip/_internal/cli/req_command.py | 453 + .../pip/_internal/cli/spinners.py | 157 + .../pip/_internal/cli/status_codes.py | 6 + .../pip/_internal/commands/__init__.py | 112 + .../pip/_internal/commands/cache.py | 216 + .../pip/_internal/commands/check.py | 47 + .../pip/_internal/commands/completion.py | 91 + .../pip/_internal/commands/configuration.py | 266 + .../pip/_internal/commands/debug.py | 204 + .../pip/_internal/commands/download.py | 139 + .../pip/_internal/commands/freeze.py | 84 + .../pip/_internal/commands/hash.py | 55 + .../pip/_internal/commands/help.py | 41 + .../pip/_internal/commands/index.py | 139 + .../pip/_internal/commands/install.py | 750 ++ .../pip/_internal/commands/list.py | 337 + .../pip/_internal/commands/search.py | 164 + .../pip/_internal/commands/show.py | 234 + .../pip/_internal/commands/uninstall.py | 100 + .../pip/_internal/commands/wheel.py | 176 + .../pip/_internal/configuration.py | 403 + .../pip/_internal/distributions/__init__.py | 21 + .../pip/_internal/distributions/base.py | 38 + .../pip/_internal/distributions/installed.py | 22 + .../pip/_internal/distributions/sdist.py | 95 + .../pip/_internal/distributions/wheel.py | 34 + .../site-packages/pip/_internal/exceptions.py | 397 + .../pip/_internal/index/__init__.py | 2 + .../pip/_internal/index/collector.py | 534 + .../pip/_internal/index/package_finder.py | 982 ++ .../pip/_internal/index/sources.py | 224 + .../pip/_internal/locations/__init__.py | 408 + .../pip/_internal/locations/_distutils.py | 169 + .../pip/_internal/locations/_sysconfig.py | 219 + .../pip/_internal/locations/base.py | 52 + .../site-packages/pip/_internal/main.py | 13 + .../pip/_internal/metadata/__init__.py | 48 + .../pip/_internal/metadata/base.py | 242 + .../pip/_internal/metadata/pkg_resources.py | 153 + .../pip/_internal/models/__init__.py | 2 + .../pip/_internal/models/candidate.py | 31 + .../pip/_internal/models/direct_url.py | 220 + .../pip/_internal/models/format_control.py | 84 + .../pip/_internal/models/index.py | 32 + .../pip/_internal/models/link.py | 288 + .../pip/_internal/models/scheme.py | 31 + .../pip/_internal/models/search_scope.py | 126 + .../pip/_internal/models/selection_prefs.py | 46 + .../pip/_internal/models/target_python.py | 111 + .../pip/_internal/models/wheel.py | 92 + .../pip/_internal/network/__init__.py | 2 + .../pip/_internal/network/auth.py | 316 + .../pip/_internal/network/cache.py | 69 + .../pip/_internal/network/download.py | 184 + .../pip/_internal/network/lazy_wheel.py | 210 + .../pip/_internal/network/session.py | 454 + .../pip/_internal/network/utils.py | 96 + .../pip/_internal/network/xmlrpc.py | 60 + .../pip/_internal/operations/__init__.py | 0 .../_internal/operations/build/__init__.py | 0 .../_internal/operations/build/metadata.py | 35 + .../operations/build/metadata_legacy.py | 74 + .../pip/_internal/operations/build/wheel.py | 38 + .../operations/build/wheel_legacy.py | 110 + .../pip/_internal/operations/check.py | 153 + .../pip/_internal/operations/freeze.py | 277 + .../_internal/operations/install/__init__.py | 2 + .../operations/install/editable_legacy.py | 47 + .../_internal/operations/install/legacy.py | 132 + .../pip/_internal/operations/install/wheel.py | 803 ++ .../pip/_internal/operations/prepare.py | 655 ++ .../site-packages/pip/_internal/pyproject.py | 183 + .../pip/_internal/req/__init__.py | 94 + .../pip/_internal/req/constructors.py | 474 + .../pip/_internal/req/req_file.py | 528 + .../pip/_internal/req/req_install.py | 846 ++ .../pip/_internal/req/req_set.py | 190 + .../pip/_internal/req/req_tracker.py | 130 + .../pip/_internal/req/req_uninstall.py | 629 ++ .../pip/_internal/resolution/__init__.py | 0 .../pip/_internal/resolution/base.py | 18 + .../_internal/resolution/legacy/__init__.py | 0 .../_internal/resolution/legacy/resolver.py | 453 + .../resolution/resolvelib/__init__.py | 0 .../_internal/resolution/resolvelib/base.py | 144 + .../resolution/resolvelib/candidates.py | 555 ++ .../resolution/resolvelib/factory.py | 700 ++ .../resolution/resolvelib/found_candidates.py | 142 + .../resolution/resolvelib/provider.py | 197 + .../resolution/resolvelib/reporter.py | 69 + .../resolution/resolvelib/requirements.py | 166 + .../resolution/resolvelib/resolver.py | 272 + .../pip/_internal/self_outdated_check.py | 187 + .../pip/_internal/utils/__init__.py | 0 .../site-packages/pip/_internal/utils/_log.py | 38 + .../pip/_internal/utils/appdirs.py | 35 + .../pip/_internal/utils/compat.py | 63 + .../pip/_internal/utils/compatibility_tags.py | 168 + .../pip/_internal/utils/datetime.py | 11 + .../pip/_internal/utils/deprecation.py | 104 + .../pip/_internal/utils/direct_url_helpers.py | 79 + .../pip/_internal/utils/distutils_args.py | 42 + .../pip/_internal/utils/encoding.py | 36 + .../pip/_internal/utils/entrypoints.py | 27 + .../pip/_internal/utils/filesystem.py | 182 + .../pip/_internal/utils/filetypes.py | 28 + .../pip/_internal/utils/glibc.py | 92 + .../pip/_internal/utils/hashes.py | 165 + .../_internal/utils/inject_securetransport.py | 36 + .../pip/_internal/utils/logging.py | 391 + .../site-packages/pip/_internal/utils/misc.py | 828 ++ .../pip/_internal/utils/models.py | 47 + .../pip/_internal/utils/packaging.py | 89 + .../pip/_internal/utils/parallel.py | 101 + .../pip/_internal/utils/pkg_resources.py | 40 + .../pip/_internal/utils/setuptools_build.py | 173 + .../pip/_internal/utils/subprocess.py | 281 + .../pip/_internal/utils/temp_dir.py | 260 + .../pip/_internal/utils/unpacking.py | 267 + .../site-packages/pip/_internal/utils/urls.py | 65 + .../pip/_internal/utils/virtualenv.py | 111 + .../pip/_internal/utils/wheel.py | 189 + .../pip/_internal/vcs/__init__.py | 15 + .../site-packages/pip/_internal/vcs/bazaar.py | 96 + .../site-packages/pip/_internal/vcs/git.py | 506 + .../pip/_internal/vcs/mercurial.py | 158 + .../pip/_internal/vcs/subversion.py | 329 + .../pip/_internal/vcs/versioncontrol.py | 722 ++ .../pip/_internal/wheel_builder.py | 360 + .../site-packages/pip/_vendor/__init__.py | 111 + .../site-packages/pip/_vendor/appdirs.py | 633 ++ .../pip/_vendor/cachecontrol/__init__.py | 11 + .../pip/_vendor/cachecontrol/_cmd.py | 57 + .../pip/_vendor/cachecontrol/adapter.py | 133 + .../pip/_vendor/cachecontrol/cache.py | 39 + .../_vendor/cachecontrol/caches/__init__.py | 2 + .../_vendor/cachecontrol/caches/file_cache.py | 146 + .../cachecontrol/caches/redis_cache.py | 33 + .../pip/_vendor/cachecontrol/compat.py | 29 + .../pip/_vendor/cachecontrol/controller.py | 376 + .../pip/_vendor/cachecontrol/filewrapper.py | 80 + .../pip/_vendor/cachecontrol/heuristics.py | 135 + .../pip/_vendor/cachecontrol/serialize.py | 188 + .../pip/_vendor/cachecontrol/wrapper.py | 29 + .../pip/_vendor/certifi/__init__.py | 3 + .../pip/_vendor/certifi/__main__.py | 12 + .../pip/_vendor/certifi/cacert.pem | 4257 ++++++++ .../site-packages/pip/_vendor/certifi/core.py | 76 + .../pip/_vendor/chardet/__init__.py | 83 + .../pip/_vendor/chardet/big5freq.py | 386 + .../pip/_vendor/chardet/big5prober.py | 47 + .../pip/_vendor/chardet/chardistribution.py | 233 + .../pip/_vendor/chardet/charsetgroupprober.py | 107 + .../pip/_vendor/chardet/charsetprober.py | 145 + .../pip/_vendor/chardet/cli/__init__.py | 1 + .../pip/_vendor/chardet/cli/chardetect.py | 84 + .../pip/_vendor/chardet/codingstatemachine.py | 88 + .../pip/_vendor/chardet/compat.py | 36 + .../pip/_vendor/chardet/cp949prober.py | 49 + .../pip/_vendor/chardet/enums.py | 76 + .../pip/_vendor/chardet/escprober.py | 101 + .../pip/_vendor/chardet/escsm.py | 246 + .../pip/_vendor/chardet/eucjpprober.py | 92 + .../pip/_vendor/chardet/euckrfreq.py | 195 + .../pip/_vendor/chardet/euckrprober.py | 47 + .../pip/_vendor/chardet/euctwfreq.py | 387 + .../pip/_vendor/chardet/euctwprober.py | 46 + .../pip/_vendor/chardet/gb2312freq.py | 283 + .../pip/_vendor/chardet/gb2312prober.py | 46 + .../pip/_vendor/chardet/hebrewprober.py | 292 + .../pip/_vendor/chardet/jisfreq.py | 325 + .../pip/_vendor/chardet/jpcntx.py | 233 + .../pip/_vendor/chardet/langbulgarianmodel.py | 4650 +++++++++ .../pip/_vendor/chardet/langgreekmodel.py | 4398 ++++++++ .../pip/_vendor/chardet/langhebrewmodel.py | 4383 ++++++++ .../pip/_vendor/chardet/langhungarianmodel.py | 4650 +++++++++ .../pip/_vendor/chardet/langrussianmodel.py | 5718 +++++++++++ .../pip/_vendor/chardet/langthaimodel.py | 4383 ++++++++ .../pip/_vendor/chardet/langturkishmodel.py | 4383 ++++++++ .../pip/_vendor/chardet/latin1prober.py | 145 + .../pip/_vendor/chardet/mbcharsetprober.py | 91 + .../pip/_vendor/chardet/mbcsgroupprober.py | 54 + .../pip/_vendor/chardet/mbcssm.py | 572 ++ .../pip/_vendor/chardet/metadata/__init__.py | 0 .../pip/_vendor/chardet/metadata/languages.py | 310 + .../pip/_vendor/chardet/sbcharsetprober.py | 145 + .../pip/_vendor/chardet/sbcsgroupprober.py | 83 + .../pip/_vendor/chardet/sjisprober.py | 92 + .../pip/_vendor/chardet/universaldetector.py | 286 + .../pip/_vendor/chardet/utf8prober.py | 82 + .../pip/_vendor/chardet/version.py | 9 + .../pip/_vendor/colorama/__init__.py | 6 + .../pip/_vendor/colorama/ansi.py | 102 + .../pip/_vendor/colorama/ansitowin32.py | 258 + .../pip/_vendor/colorama/initialise.py | 80 + .../pip/_vendor/colorama/win32.py | 152 + .../pip/_vendor/colorama/winterm.py | 169 + .../pip/_vendor/distlib/__init__.py | 23 + .../pip/_vendor/distlib/_backport/__init__.py | 6 + .../pip/_vendor/distlib/_backport/misc.py | 41 + .../pip/_vendor/distlib/_backport/shutil.py | 764 ++ .../_vendor/distlib/_backport/sysconfig.cfg | 84 + .../_vendor/distlib/_backport/sysconfig.py | 786 ++ .../pip/_vendor/distlib/_backport/tarfile.py | 2607 +++++ .../pip/_vendor/distlib/compat.py | 1120 +++ .../pip/_vendor/distlib/database.py | 1339 +++ .../pip/_vendor/distlib/index.py | 509 + .../pip/_vendor/distlib/locators.py | 1300 +++ .../pip/_vendor/distlib/manifest.py | 393 + .../pip/_vendor/distlib/markers.py | 130 + .../pip/_vendor/distlib/metadata.py | 1058 ++ .../pip/_vendor/distlib/resources.py | 358 + .../pip/_vendor/distlib/scripts.py | 423 + .../site-packages/pip/_vendor/distlib/t32.exe | Bin 0 -> 96768 bytes .../site-packages/pip/_vendor/distlib/t64.exe | Bin 0 -> 105984 bytes .../site-packages/pip/_vendor/distlib/util.py | 1965 ++++ .../pip/_vendor/distlib/version.py | 739 ++ .../site-packages/pip/_vendor/distlib/w32.exe | Bin 0 -> 90112 bytes .../site-packages/pip/_vendor/distlib/w64.exe | Bin 0 -> 99840 bytes .../pip/_vendor/distlib/wheel.py | 1056 ++ .../site-packages/pip/_vendor/distro.py | 1230 +++ .../pip/_vendor/html5lib/__init__.py | 35 + .../pip/_vendor/html5lib/_ihatexml.py | 289 + .../pip/_vendor/html5lib/_inputstream.py | 918 ++ .../pip/_vendor/html5lib/_tokenizer.py | 1735 ++++ .../pip/_vendor/html5lib/_trie/__init__.py | 5 + .../pip/_vendor/html5lib/_trie/_base.py | 40 + .../pip/_vendor/html5lib/_trie/py.py | 67 + .../pip/_vendor/html5lib/_utils.py | 159 + .../pip/_vendor/html5lib/constants.py | 2946 ++++++ .../pip/_vendor/html5lib/filters/__init__.py | 0 .../filters/alphabeticalattributes.py | 29 + .../pip/_vendor/html5lib/filters/base.py | 12 + .../html5lib/filters/inject_meta_charset.py | 73 + .../pip/_vendor/html5lib/filters/lint.py | 93 + .../_vendor/html5lib/filters/optionaltags.py | 207 + .../pip/_vendor/html5lib/filters/sanitizer.py | 916 ++ .../_vendor/html5lib/filters/whitespace.py | 38 + .../pip/_vendor/html5lib/html5parser.py | 2795 ++++++ .../pip/_vendor/html5lib/serializer.py | 409 + .../_vendor/html5lib/treeadapters/__init__.py | 30 + .../_vendor/html5lib/treeadapters/genshi.py | 54 + .../pip/_vendor/html5lib/treeadapters/sax.py | 50 + .../_vendor/html5lib/treebuilders/__init__.py | 88 + .../pip/_vendor/html5lib/treebuilders/base.py | 417 + .../pip/_vendor/html5lib/treebuilders/dom.py | 239 + .../_vendor/html5lib/treebuilders/etree.py | 343 + .../html5lib/treebuilders/etree_lxml.py | 392 + .../_vendor/html5lib/treewalkers/__init__.py | 154 + .../pip/_vendor/html5lib/treewalkers/base.py | 252 + .../pip/_vendor/html5lib/treewalkers/dom.py | 43 + .../pip/_vendor/html5lib/treewalkers/etree.py | 131 + .../html5lib/treewalkers/etree_lxml.py | 215 + .../_vendor/html5lib/treewalkers/genshi.py | 69 + .../pip/_vendor/idna/__init__.py | 44 + .../site-packages/pip/_vendor/idna/codec.py | 117 + .../site-packages/pip/_vendor/idna/compat.py | 16 + .../site-packages/pip/_vendor/idna/core.py | 409 + .../pip/_vendor/idna/idnadata.py | 2050 ++++ .../pip/_vendor/idna/intranges.py | 58 + .../pip/_vendor/idna/package_data.py | 2 + .../pip/_vendor/idna/uts46data.py | 8438 ++++++++++++++++ .../pip/_vendor/msgpack/__init__.py | 54 + .../pip/_vendor/msgpack/_version.py | 1 + .../pip/_vendor/msgpack/exceptions.py | 48 + .../site-packages/pip/_vendor/msgpack/ext.py | 193 + .../pip/_vendor/msgpack/fallback.py | 1087 ++ .../pip/_vendor/packaging/__about__.py | 26 + .../pip/_vendor/packaging/__init__.py | 25 + .../pip/_vendor/packaging/_manylinux.py | 301 + .../pip/_vendor/packaging/_musllinux.py | 136 + .../pip/_vendor/packaging/_structures.py | 67 + .../pip/_vendor/packaging/markers.py | 304 + .../pip/_vendor/packaging/requirements.py | 146 + .../pip/_vendor/packaging/specifiers.py | 828 ++ .../pip/_vendor/packaging/tags.py | 484 + .../pip/_vendor/packaging/utils.py | 136 + .../pip/_vendor/packaging/version.py | 504 + .../pip/_vendor/pep517/__init__.py | 6 + .../site-packages/pip/_vendor/pep517/build.py | 127 + .../site-packages/pip/_vendor/pep517/check.py | 207 + .../pip/_vendor/pep517/colorlog.py | 115 + .../pip/_vendor/pep517/compat.py | 42 + .../pip/_vendor/pep517/dirtools.py | 44 + .../pip/_vendor/pep517/envbuild.py | 171 + .../pip/_vendor/pep517/in_process/__init__.py | 17 + .../_vendor/pep517/in_process/_in_process.py | 349 + .../site-packages/pip/_vendor/pep517/meta.py | 92 + .../pip/_vendor/pep517/wrappers.py | 371 + .../pip/_vendor/pkg_resources/__init__.py | 3296 ++++++ .../pip/_vendor/pkg_resources/py31compat.py | 23 + .../pip/_vendor/progress/__init__.py | 177 + .../site-packages/pip/_vendor/progress/bar.py | 91 + .../pip/_vendor/progress/counter.py | 41 + .../pip/_vendor/progress/spinner.py | 43 + .../site-packages/pip/_vendor/pyparsing.py | 7107 +++++++++++++ .../pip/_vendor/requests/__init__.py | 154 + .../pip/_vendor/requests/__version__.py | 14 + .../pip/_vendor/requests/_internal_utils.py | 42 + .../pip/_vendor/requests/adapters.py | 533 + .../site-packages/pip/_vendor/requests/api.py | 159 + .../pip/_vendor/requests/auth.py | 305 + .../pip/_vendor/requests/certs.py | 18 + .../pip/_vendor/requests/compat.py | 76 + .../pip/_vendor/requests/cookies.py | 549 + .../pip/_vendor/requests/exceptions.py | 127 + .../pip/_vendor/requests/help.py | 132 + .../pip/_vendor/requests/hooks.py | 34 + .../pip/_vendor/requests/models.py | 966 ++ .../pip/_vendor/requests/packages.py | 16 + .../pip/_vendor/requests/sessions.py | 781 ++ .../pip/_vendor/requests/status_codes.py | 123 + .../pip/_vendor/requests/structures.py | 105 + .../pip/_vendor/requests/utils.py | 1013 ++ .../pip/_vendor/resolvelib/__init__.py | 26 + .../pip/_vendor/resolvelib/compat/__init__.py | 0 .../resolvelib/compat/collections_abc.py | 6 + .../pip/_vendor/resolvelib/providers.py | 124 + .../pip/_vendor/resolvelib/reporters.py | 37 + .../pip/_vendor/resolvelib/resolvers.py | 473 + .../pip/_vendor/resolvelib/structs.py | 165 + .../site-packages/pip/_vendor/six.py | 998 ++ .../pip/_vendor/tenacity/__init__.py | 517 + .../pip/_vendor/tenacity/_asyncio.py | 92 + .../pip/_vendor/tenacity/_utils.py | 68 + .../pip/_vendor/tenacity/after.py | 46 + .../pip/_vendor/tenacity/before.py | 41 + .../pip/_vendor/tenacity/before_sleep.py | 58 + .../site-packages/pip/_vendor/tenacity/nap.py | 43 + .../pip/_vendor/tenacity/retry.py | 213 + .../pip/_vendor/tenacity/stop.py | 96 + .../pip/_vendor/tenacity/tornadoweb.py | 59 + .../pip/_vendor/tenacity/wait.py | 191 + .../pip/_vendor/tomli/__init__.py | 6 + .../pip/_vendor/tomli/_parser.py | 703 ++ .../site-packages/pip/_vendor/tomli/_re.py | 83 + .../pip/_vendor/urllib3/__init__.py | 85 + .../pip/_vendor/urllib3/_collections.py | 337 + .../pip/_vendor/urllib3/_version.py | 2 + .../pip/_vendor/urllib3/connection.py | 539 + .../pip/_vendor/urllib3/connectionpool.py | 1067 ++ .../pip/_vendor/urllib3/contrib/__init__.py | 0 .../urllib3/contrib/_appengine_environ.py | 36 + .../contrib/_securetransport/__init__.py | 0 .../contrib/_securetransport/bindings.py | 519 + .../contrib/_securetransport/low_level.py | 396 + .../pip/_vendor/urllib3/contrib/appengine.py | 314 + .../pip/_vendor/urllib3/contrib/ntlmpool.py | 130 + .../pip/_vendor/urllib3/contrib/pyopenssl.py | 511 + .../urllib3/contrib/securetransport.py | 922 ++ .../pip/_vendor/urllib3/contrib/socks.py | 216 + .../pip/_vendor/urllib3/exceptions.py | 323 + .../pip/_vendor/urllib3/fields.py | 274 + .../pip/_vendor/urllib3/filepost.py | 98 + .../pip/_vendor/urllib3/packages/__init__.py | 5 + .../urllib3/packages/backports/__init__.py | 0 .../urllib3/packages/backports/makefile.py | 51 + .../pip/_vendor/urllib3/packages/six.py | 1077 ++ .../packages/ssl_match_hostname/__init__.py | 24 + .../ssl_match_hostname/_implementation.py | 160 + .../pip/_vendor/urllib3/poolmanager.py | 536 + .../pip/_vendor/urllib3/request.py | 170 + .../pip/_vendor/urllib3/response.py | 821 ++ .../pip/_vendor/urllib3/util/__init__.py | 49 + .../pip/_vendor/urllib3/util/connection.py | 150 + .../pip/_vendor/urllib3/util/proxy.py | 56 + .../pip/_vendor/urllib3/util/queue.py | 22 + .../pip/_vendor/urllib3/util/request.py | 143 + .../pip/_vendor/urllib3/util/response.py | 107 + .../pip/_vendor/urllib3/util/retry.py | 602 ++ .../pip/_vendor/urllib3/util/ssl_.py | 495 + .../pip/_vendor/urllib3/util/ssltransport.py | 221 + .../pip/_vendor/urllib3/util/timeout.py | 268 + .../pip/_vendor/urllib3/util/url.py | 432 + .../pip/_vendor/urllib3/util/wait.py | 153 + .../site-packages/pip/_vendor/vendor.txt | 22 + .../pip/_vendor/webencodings/__init__.py | 342 + .../pip/_vendor/webencodings/labels.py | 231 + .../pip/_vendor/webencodings/mklabels.py | 59 + .../pip/_vendor/webencodings/tests.py | 153 + .../_vendor/webencodings/x_user_defined.py | 325 + .../lib/python3.9/site-packages/pip/py.typed | 4 + .../site-packages/pkg_resources/__init__.py | 3288 ++++++ .../pkg_resources/_vendor/__init__.py | 0 .../pkg_resources/_vendor/appdirs.py | 608 ++ .../_vendor/packaging/__about__.py | 27 + .../_vendor/packaging/__init__.py | 26 + .../_vendor/packaging/_compat.py | 38 + .../_vendor/packaging/_structures.py | 86 + .../_vendor/packaging/_typing.py | 48 + .../_vendor/packaging/markers.py | 328 + .../_vendor/packaging/requirements.py | 145 + .../_vendor/packaging/specifiers.py | 863 ++ .../pkg_resources/_vendor/packaging/tags.py | 751 ++ .../pkg_resources/_vendor/packaging/utils.py | 65 + .../_vendor/packaging/version.py | 535 + .../pkg_resources/_vendor/pyparsing.py | 5742 +++++++++++ .../pkg_resources/extern/__init__.py | 73 + .../data/my-test-package-source/setup.py | 6 + .../propcache-0.4.1.dist-info/INSTALLER | 1 + .../propcache-0.4.1.dist-info/METADATA | 443 + .../propcache-0.4.1.dist-info/RECORD | 18 + .../propcache-0.4.1.dist-info/WHEEL | 6 + .../licenses/LICENSE | 202 + .../propcache-0.4.1.dist-info/licenses/NOTICE | 13 + .../propcache-0.4.1.dist-info/top_level.txt | 1 + .../site-packages/propcache/__init__.py | 32 + .../site-packages/propcache/_helpers.py | 39 + .../propcache/_helpers_c.cpython-39-darwin.so | Bin 0 -> 140976 bytes .../site-packages/propcache/_helpers_c.pyx | 103 + .../site-packages/propcache/_helpers_py.py | 62 + .../python3.9/site-packages/propcache/api.py | 8 + .../site-packages/propcache/py.typed | 1 + .../INSTALLER | 1 + .../LICENSE | 54 + .../METADATA | 204 + .../RECORD | 44 + .../WHEEL | 6 + .../top_level.txt | 1 + .../zip-safe | 1 + .../pyyaml-6.0.3.dist-info/INSTALLER | 1 + .../pyyaml-6.0.3.dist-info/METADATA | 59 + .../pyyaml-6.0.3.dist-info/RECORD | 43 + .../pyyaml-6.0.3.dist-info/WHEEL | 6 + .../pyyaml-6.0.3.dist-info/licenses/LICENSE | 20 + .../pyyaml-6.0.3.dist-info/top_level.txt | 2 + .../qh3-1.6.0.dist-info/INSTALLER | 1 + .../qh3-1.6.0.dist-info/METADATA | 128 + .../site-packages/qh3-1.6.0.dist-info/RECORD | 53 + .../site-packages/qh3-1.6.0.dist-info/WHEEL | 6 + .../qh3-1.6.0.dist-info/licenses/LICENSE | 25 + .../sboms/qh3.cyclonedx.json | 6099 ++++++++++++ .../python3.9/site-packages/qh3/__init__.py | 42 + .../python3.9/site-packages/qh3/_compat.py | 7 + .../site-packages/qh3/_hazmat.abi3.so | Bin 0 -> 11125792 bytes .../python3.9/site-packages/qh3/_hazmat.pyi | 361 + .../site-packages/qh3/asyncio/__init__.py | 3 + .../site-packages/qh3/asyncio/client.py | 104 + .../site-packages/qh3/asyncio/protocol.py | 254 + .../site-packages/qh3/asyncio/server.py | 217 + .../site-packages/qh3/h3/__init__.py | 0 .../site-packages/qh3/h3/connection.py | 1215 +++ .../python3.9/site-packages/qh3/h3/events.py | 116 + .../site-packages/qh3/h3/exceptions.py | 13 + .../lib/python3.9/site-packages/qh3/py.typed | 1 + .../site-packages/qh3/quic/__init__.py | 0 .../site-packages/qh3/quic/configuration.py | 191 + .../site-packages/qh3/quic/connection.py | 3804 +++++++ .../site-packages/qh3/quic/crypto.py | 284 + .../site-packages/qh3/quic/events.py | 127 + .../site-packages/qh3/quic/logger.py | 333 + .../site-packages/qh3/quic/packet.py | 628 ++ .../site-packages/qh3/quic/packet_builder.py | 437 + .../site-packages/qh3/quic/recovery.py | 503 + .../python3.9/site-packages/qh3/quic/retry.py | 39 + .../site-packages/qh3/quic/stream.py | 380 + .venv/lib/python3.9/site-packages/qh3/tls.py | 2278 +++++ .../INSTALLER | 1 + .../METADATA | 85 + .../RECORD | 297 + .../WHEEL | 4 + .../licenses/LICENSE | 165 + .../recurring_ical_events/.gitignore | 1 + .../recurring_ical_events/__init__.py | 113 + .../adapters/__init__.py | 15 + .../recurring_ical_events/adapters/alarm.py | 22 + .../adapters/component.py | 255 + .../recurring_ical_events/adapters/event.py | 60 + .../recurring_ical_events/adapters/journal.py | 39 + .../recurring_ical_events/adapters/todo.py | 80 + .../recurring_ical_events/constants.py | 29 + .../recurring_ical_events/errors.py | 54 + .../recurring_ical_events/examples.py | 29 + .../recurring_ical_events/occurrence.py | 212 + .../recurring_ical_events/pages.py | 151 + .../recurring_ical_events/query.py | 368 + .../selection/__init__.py | 13 + .../recurring_ical_events/selection/alarm.py | 93 + .../recurring_ical_events/selection/all.py | 62 + .../recurring_ical_events/selection/base.py | 33 + .../recurring_ical_events/selection/name.py | 100 + .../recurring_ical_events/series/__init__.py | 15 + .../recurring_ical_events/series/alarm.py | 132 + .../recurring_ical_events/series/rrule.py | 480 + .../recurring_ical_events/test/__init__.py | 0 .../test/calendars/Germany.ics | 3666 +++++++ .../test/calendars/Germany_Holidays.ics | 484 + .../calendars/after_many_events_in_order.ics | 695 ++ .../alarm_15_min_before_event_snoozed.ics | 622 ++ .../calendars/alarm_1_week_before_event.ics | 626 ++ .../test/calendars/alarm_absolute.ics | 619 ++ .../test/calendars/alarm_absolute_edited.ics | 636 ++ .../test/calendars/alarm_absolute_repeat.ics | 621 ++ .../alarm_around_event_boundaries.ics | 637 ++ .../calendars/alarm_at_start_of_event.ics | 160 + .../calendars/alarm_of_repeated_event.ics | 624 ++ ...g_and_acknowledged_at_2024_11_27_16_27.ics | 622 ++ .../calendars/alarm_removed_and_moved.ics | 722 ++ .../test/calendars/alarm_several_in_one.ics | 636 ++ .../calendars/alarms_at_the_same_time.ics | 675 ++ .../alarms_different_in_same_event.ics | 636 ++ .../bad_rrule_missing_until_event.ics | 17 + .../test/calendars/date_exclude.txt | 20 + .../test/calendars/daylight_saving_time.ics | 1069 ++ .../test/calendars/discourse_no_dtend.ics | 123 + .../test/calendars/duplicated_rrule.ics | 16 + .../test/calendars/duration.ics | 21 + .../test/calendars/duration_edited.ics | 53 + .../calendars/each_week_but_one_deleted.ics | 39 + .../calendars/each_week_but_two_deleted.ics | 40 + .../test/calendars/end_before_start_event.ics | 34 + .../test/calendars/event_10_times.ics | 34 + .../test/calendars/fablab_cottbus.ics | 997 ++ .../issue_107_omitting_last_event.ics | 24 + .../calendars/issue_113_period_in_rdate.ics | 32 + .../issue_113_period_rdate_duration.ics | 8 + .../issue_117_until_before_dtstart.ics | 17 + .../calendars/issue_128_only_first_event.ics | 17 + .../issue_132_swapped_start_and_end.ics | 32 + .../test/calendars/issue_148_edge_case_1.ics | 43 + .../test/calendars/issue_148_edge_case_2.ics | 42 + .../issue_148_exdate_and_rdate_unedited.ics | 17 + .../issue_148_exdate_and_rdate_updated.ics | 32 + .../calendars/issue_148_ignored_exdate.ics | 29 + .../issue_151_macos_linux_difference.ics | 65 + .../issue_151_macos_linux_difference2.ics | 65 + .../calendars/issue_15_duplicated_events.ics | 17 + .../issue_163_deleted_modification.ics | 44 + .../calendars/issue_164_duplicated_event.ics | 43 + .../issue_173_only_modifications_error.ics | 8841 +++++++++++++++++ .../test/calendars/issue_179_example.ics | 12 + .../calendars/issue_186_invalid_trigger.ics | 638 ++ .../test/calendars/issue_18_cancel_status.ics | 50 + .../issue_201_mixed_datetime_and_date.ics | 14 + .../test/calendars/issue_201_test_matrix.ics | 152 + .../calendars/issue_20_exdate_ignored.ics | 122 + .../issue_223_one_event_with_sequence.ics | 45 + .../test/calendars/issue_223_thunderbird.ics | 643 ++ ...urrence_id_is_not_identical_to_dtstart.ics | 10 + .../test/calendars/issue_27_t1.ics | 42 + .../test/calendars/issue_27_t2.ics | 42 + .../issue_28_rrule_with_UTC_endinginZ.ics | 141 + .../issue_36_recurrence_ID_format.ics | 49 + .../test/calendars/issue_4.ics | 36 + .../test/calendars/issue_44_double_event.ics | 22 + .../issue_48_daylight_aware_repeats.ics | 43 + .../test/calendars/issue_48_dst.ics | 233 + .../test/calendars/issue_4_rrule_until.ics | 17 + .../test/calendars/issue_4_weidenrinde.ics | 22 + .../calendars/issue_61_time_zone_error.ics | 221 + .../test/calendars/issue_62_moved_event.ics | 59 + .../test/calendars/issue_62_moved_event_2.ics | 79 + .../calendars/issue_75_range_parameter.ics | 41 + ..._x_wr_timezone_without_time_zone_in_dt.ics | 15 + .../calendars/issue_97_simple_journal.ics | 15 + .../test/calendars/issue_97_simple_todo.ics | 15 + .../calendars/issue_97_todo_nodtstart.ics | 14 + .../test/calendars/machbar_16_feb_2019.ics | 1069 ++ .../test/calendars/multiple_rrule.ics | 16 + .../test/calendars/no_events.ics | 7 + .../test/calendars/one_day_event.ics | 34 + .../one_day_event_repeat_every_day.ics | 37 + .../test/calendars/one_event.ics | 34 + .../one_event_repeat_every_3_days.ics | 37 + .../test/calendars/rdate.ics | 24 + .../test/calendars/rdate2.ics | 10 + .../calendars/rdate_falls_on_rrule_until.ics | 123 + .../calendars/rdate_hackerpublicradio.ics | 26 + .../calendars/recurrence_sequence_number.ics | 32 + .../recurring_events_changed_duration.ics | 105 + .../test/calendars/recurring_events_moved.ics | 91 + .../same_event_recurring_at_same_time.ics | 668 ++ .../several_events_at_the_same_time.ics | 115 + .../test/calendars/subcomponents.ics | 30 + .../test/calendars/three_events.ics | 37 + .../calendars/three_events_one_edited.ics | 53 + .../x_wr_timezone_simple_events_issue_59.ics | 36 + .../test/calendars/zero_size_event.ics | 33 + .../recurring_ical_events/test/conftest.py | 210 + .../recurring_ical_events/test/py.py | 13 + .../recurring_ical_events/test/test_after.py | 72 + .../test/test_at_function.py | 28 + .../test/test_bad_rrule_format.py | 8 + .../test/test_convert_inputs.py | 22 + .../recurring_ical_events/test/test_count.py | 19 + .../test/test_daylight_saving_time.py | 28 + .../test/test_deleted_entries.py | 8 + .../test/test_duration.py | 56 + .../test/test_end_before_start_event.py | 28 + .../test/test_event_values_and_edits.py | 68 + .../test/test_example_function.py | 23 + .../test/test_examples.py | 123 + .../test/test_extend_classes.py | 130 + .../test/test_issue_101_select_components.py | 54 + .../test_issue_107_omitting_last_event.py | 20 + .../test/test_issue_113_period_in_rdate.py | 45 + .../test_issue_117_until_before_dtstart.py | 9 + .../test/test_issue_128_only_first_event.py | 33 + .../test_issue_132_swapped_start_and_end.py | 37 + .../test/test_issue_139_no_duration.py | 11 + ...e_148_ignored_exdate_in_higher_sequence.py | 115 + .../test/test_issue_15.py | 24 + .../test_issue_151_macos_linux_difference.py | 32 + .../test_issue_163_deleted_modification.py | 10 + .../test/test_issue_164_duplicated_event.py | 15 + ...st_issue_173_only_modification_included.py | 16 + .../test/test_issue_179_span_in_event.py | 24 + .../test/test_issue_186_alarms.py | 331 + ...est_issue_186_icalendar_alarm_interface.py | 31 + .../test/test_issue_18_cancel_status.py | 19 + .../test/test_issue_201_incompatible_dates.py | 135 + .../test/test_issue_20_exdate_ignored.py | 81 + .../test/test_issue_211_pagination.py | 212 + .../test_issue_219_recurrence_id_in_events.py | 175 + .../test/test_issue_223_sequence_number.py | 60 + ...currence_id_is_not_identical_to_dtstart.py | 33 + .../test/test_issue_27.py | 39 + .../test/test_issue_28_timezone_with_z.py | 22 + .../test_issue_36_recurrence_id_format.py | 22 + .../test/test_issue_4.py | 43 + .../test_issue_44_day_event_reported_twice.py | 33 + .../test/test_issue_48_daylight.py | 40 + .../test/test_issue_48_dst.py | 41 + .../test/test_issue_61.py | 24 + .../test/test_issue_62_moved_event.py | 57 + .../test/test_issue_6_copy_subcomponents.py | 13 + .../test/test_issue_75_range_parameter.py | 249 + ...st_issue_7_datetime_and_date_start_stop.py | 34 + ...ssue_86_x_wr_timezone_but_no_tzid_in_dt.py | 16 + ..._97_simple_recurrent_todos_and_journals.py | 48 + .../test/test_keep_recurrence_attributes.py | 38 + .../test/test_multiple_rrule.py | 11 + .../test/test_occurrence.py | 249 + .../test/test_properties.py | 33 + .../recurring_ical_events/test/test_rdate.py | 97 + .../recurring_ical_events/test/test_readme.py | 25 + .../test/test_recurrence_sequence_number.py | 9 + .../test/test_repeated_properties.py | 3 + .../test/test_repetitions_do_not_change.py | 43 + .../test/test_simple_recurrent_events.py | 44 + .../test/test_single_events.py | 26 + .../test/test_skip_bad_events.py | 22 + .../test/test_time_arguments.py | 28 + .../test/test_time_span_contains_event.py | 228 + .../test/test_time_zones_differ.py | 38 + .../test/test_timedelta_for_between.py | 27 + .../test/test_util_functions.py | 28 + .../test/test_with_doctest.py | 61 + .../test/test_x_wr_timezone.py | 69 + .../test/test_zero_size_events.py | 38 + .../test/test_zoneinfo_issue_57.py | 92 + .../recurring_ical_events/types.py | 29 + .../recurring_ical_events/util.py | 251 + .../recurring_ical_events/version.py | 16 + .../setuptools-58.0.4.dist-info/INSTALLER | 1 + .../setuptools-58.0.4.dist-info/LICENSE | 19 + .../setuptools-58.0.4.dist-info/METADATA | 119 + .../setuptools-58.0.4.dist-info/RECORD | 296 + .../setuptools-58.0.4.dist-info/REQUESTED | 0 .../setuptools-58.0.4.dist-info/WHEEL | 5 + .../entry_points.txt | 56 + .../setuptools-58.0.4.dist-info/top_level.txt | 3 + .../site-packages/setuptools/__init__.py | 242 + .../setuptools/_deprecation_warning.py | 7 + .../setuptools/_distutils/__init__.py | 15 + .../setuptools/_distutils/_msvccompiler.py | 561 ++ .../setuptools/_distutils/archive_util.py | 256 + .../setuptools/_distutils/bcppcompiler.py | 393 + .../setuptools/_distutils/ccompiler.py | 1123 +++ .../setuptools/_distutils/cmd.py | 403 + .../setuptools/_distutils/command/__init__.py | 31 + .../setuptools/_distutils/command/bdist.py | 143 + .../_distutils/command/bdist_dumb.py | 123 + .../_distutils/command/bdist_msi.py | 749 ++ .../_distutils/command/bdist_rpm.py | 579 ++ .../_distutils/command/bdist_wininst.py | 377 + .../setuptools/_distutils/command/build.py | 157 + .../_distutils/command/build_clib.py | 209 + .../_distutils/command/build_ext.py | 757 ++ .../setuptools/_distutils/command/build_py.py | 392 + .../_distutils/command/build_scripts.py | 152 + .../setuptools/_distutils/command/check.py | 148 + .../setuptools/_distutils/command/clean.py | 76 + .../setuptools/_distutils/command/config.py | 344 + .../setuptools/_distutils/command/install.py | 677 ++ .../_distutils/command/install_data.py | 79 + .../_distutils/command/install_egg_info.py | 77 + .../_distutils/command/install_headers.py | 47 + .../_distutils/command/install_lib.py | 217 + .../_distutils/command/install_scripts.py | 60 + .../_distutils/command/py37compat.py | 30 + .../setuptools/_distutils/command/register.py | 304 + .../setuptools/_distutils/command/sdist.py | 494 + .../setuptools/_distutils/command/upload.py | 214 + .../setuptools/_distutils/config.py | 130 + .../setuptools/_distutils/core.py | 234 + .../setuptools/_distutils/cygwinccompiler.py | 414 + .../setuptools/_distutils/debug.py | 5 + .../setuptools/_distutils/dep_util.py | 92 + .../setuptools/_distutils/dir_util.py | 210 + .../setuptools/_distutils/dist.py | 1257 +++ .../setuptools/_distutils/errors.py | 97 + .../setuptools/_distutils/extension.py | 240 + .../setuptools/_distutils/fancy_getopt.py | 457 + .../setuptools/_distutils/file_util.py | 238 + .../setuptools/_distutils/filelist.py | 355 + .../setuptools/_distutils/log.py | 77 + .../setuptools/_distutils/msvc9compiler.py | 788 ++ .../setuptools/_distutils/msvccompiler.py | 643 ++ .../setuptools/_distutils/py35compat.py | 19 + .../setuptools/_distutils/py38compat.py | 7 + .../setuptools/_distutils/spawn.py | 106 + .../setuptools/_distutils/sysconfig.py | 573 ++ .../setuptools/_distutils/text_file.py | 286 + .../setuptools/_distutils/unixccompiler.py | 332 + .../setuptools/_distutils/util.py | 535 + .../setuptools/_distutils/version.py | 347 + .../setuptools/_distutils/versionpredicate.py | 166 + .../site-packages/setuptools/_imp.py | 82 + .../setuptools/_vendor/__init__.py | 0 .../_vendor/more_itertools/__init__.py | 4 + .../setuptools/_vendor/more_itertools/more.py | 3825 +++++++ .../_vendor/more_itertools/recipes.py | 620 ++ .../setuptools/_vendor/ordered_set.py | 488 + .../setuptools/_vendor/packaging/__about__.py | 27 + .../setuptools/_vendor/packaging/__init__.py | 26 + .../setuptools/_vendor/packaging/_compat.py | 38 + .../_vendor/packaging/_structures.py | 86 + .../setuptools/_vendor/packaging/_typing.py | 48 + .../setuptools/_vendor/packaging/markers.py | 328 + .../_vendor/packaging/requirements.py | 145 + .../_vendor/packaging/specifiers.py | 863 ++ .../setuptools/_vendor/packaging/tags.py | 751 ++ .../setuptools/_vendor/packaging/utils.py | 65 + .../setuptools/_vendor/packaging/version.py | 535 + .../setuptools/_vendor/pyparsing.py | 5742 +++++++++++ .../site-packages/setuptools/archive_util.py | 205 + .../site-packages/setuptools/build_meta.py | 281 + .../site-packages/setuptools/cli-32.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/cli-64.exe | Bin 0 -> 74752 bytes .../site-packages/setuptools/cli.exe | Bin 0 -> 65536 bytes .../setuptools/command/__init__.py | 8 + .../site-packages/setuptools/command/alias.py | 78 + .../setuptools/command/bdist_egg.py | 456 + .../setuptools/command/bdist_rpm.py | 31 + .../setuptools/command/build_clib.py | 101 + .../setuptools/command/build_ext.py | 328 + .../setuptools/command/build_py.py | 232 + .../setuptools/command/develop.py | 193 + .../setuptools/command/dist_info.py | 36 + .../setuptools/command/easy_install.py | 2290 +++++ .../setuptools/command/egg_info.py | 734 ++ .../setuptools/command/install.py | 125 + .../setuptools/command/install_egg_info.py | 62 + .../setuptools/command/install_lib.py | 122 + .../setuptools/command/install_scripts.py | 69 + .../setuptools/command/launcher manifest.xml | 15 + .../setuptools/command/py36compat.py | 134 + .../setuptools/command/register.py | 18 + .../setuptools/command/rotate.py | 64 + .../setuptools/command/saveopts.py | 22 + .../site-packages/setuptools/command/sdist.py | 189 + .../setuptools/command/setopt.py | 149 + .../site-packages/setuptools/command/test.py | 252 + .../setuptools/command/upload.py | 17 + .../setuptools/command/upload_docs.py | 202 + .../site-packages/setuptools/config.py | 749 ++ .../site-packages/setuptools/dep_util.py | 25 + .../site-packages/setuptools/depends.py | 175 + .../site-packages/setuptools/dist.py | 1150 +++ .../site-packages/setuptools/errors.py | 16 + .../site-packages/setuptools/extension.py | 55 + .../setuptools/extern/__init__.py | 73 + .../site-packages/setuptools/glob.py | 167 + .../site-packages/setuptools/gui-32.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/gui-64.exe | Bin 0 -> 75264 bytes .../site-packages/setuptools/gui.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/installer.py | 97 + .../site-packages/setuptools/launch.py | 36 + .../site-packages/setuptools/monkey.py | 177 + .../site-packages/setuptools/msvc.py | 1805 ++++ .../site-packages/setuptools/namespaces.py | 107 + .../site-packages/setuptools/package_index.py | 1119 +++ .../site-packages/setuptools/py34compat.py | 13 + .../site-packages/setuptools/sandbox.py | 530 + .../setuptools/script (dev).tmpl | 6 + .../site-packages/setuptools/script.tmpl | 3 + .../site-packages/setuptools/unicode_utils.py | 42 + .../site-packages/setuptools/version.py | 6 + .../site-packages/setuptools/wheel.py | 213 + .../setuptools/windows_support.py | 29 + .../six-1.17.0.dist-info/INSTALLER | 1 + .../six-1.17.0.dist-info/LICENSE | 18 + .../six-1.17.0.dist-info/METADATA | 43 + .../site-packages/six-1.17.0.dist-info/RECORD | 8 + .../site-packages/six-1.17.0.dist-info/WHEEL | 6 + .../six-1.17.0.dist-info/top_level.txt | 1 + .venv/lib/python3.9/site-packages/six.py | 1003 ++ .../INSTALLER | 1 + .../METADATA | 72 + .../typing_extensions-4.15.0.dist-info/RECORD | 7 + .../typing_extensions-4.15.0.dist-info/WHEEL | 4 + .../licenses/LICENSE | 279 + .../site-packages/typing_extensions.py | 4317 ++++++++ .../tzdata-2025.3.dist-info/INSTALLER | 1 + .../tzdata-2025.3.dist-info/METADATA | 33 + .../tzdata-2025.3.dist-info/RECORD | 656 ++ .../tzdata-2025.3.dist-info/WHEEL | 6 + .../tzdata-2025.3.dist-info/licenses/LICENSE | 15 + .../licenses/licenses/LICENSE_APACHE | 201 + .../tzdata-2025.3.dist-info/top_level.txt | 1 + .../site-packages/tzdata/__init__.py | 6 + .../tzdata/zoneinfo/Africa/Abidjan | Bin 0 -> 130 bytes .../tzdata/zoneinfo/Africa/Accra | Bin 0 -> 130 bytes .../tzdata/zoneinfo/Africa/Addis_Ababa | Bin 0 -> 191 bytes .../tzdata/zoneinfo/Africa/Algiers | Bin 0 -> 470 bytes .../tzdata/zoneinfo/Africa/Asmara | Bin 0 -> 191 bytes .../tzdata/zoneinfo/Africa/Asmera | Bin 0 -> 191 bytes .../tzdata/zoneinfo/Africa/Bamako | Bin 0 -> 130 bytes .../tzdata/zoneinfo/Africa/Bangui | Bin 0 -> 180 bytes .../tzdata/zoneinfo/Africa/Banjul | Bin 0 -> 130 bytes .../tzdata/zoneinfo/Africa/Bissau | Bin 0 -> 149 bytes .../tzdata/zoneinfo/Africa/Blantyre | Bin 0 -> 131 bytes .../tzdata/zoneinfo/Africa/Brazzaville | Bin 0 -> 180 bytes .../tzdata/zoneinfo/Africa/Bujumbura | Bin 0 -> 131 bytes .../tzdata/zoneinfo/Africa/Cairo | Bin 0 -> 1309 bytes .../tzdata/zoneinfo/Africa/Casablanca | Bin 0 -> 1919 bytes .../tzdata/zoneinfo/Africa/Ceuta | Bin 0 -> 562 bytes .../tzdata/zoneinfo/Africa/Conakry | Bin 0 -> 130 bytes .../tzdata/zoneinfo/Africa/Dakar | Bin 0 -> 130 bytes .../tzdata/zoneinfo/Africa/Dar_es_Salaam | Bin 0 -> 191 bytes .../tzdata/zoneinfo/Africa/Djibouti | Bin 0 -> 191 bytes .../tzdata/zoneinfo/Africa/Douala | Bin 0 -> 180 bytes .../tzdata/zoneinfo/Africa/El_Aaiun | Bin 0 -> 1830 bytes .../tzdata/zoneinfo/Africa/Freetown | Bin 0 -> 130 bytes .../tzdata/zoneinfo/Africa/Gaborone | Bin 0 -> 131 bytes .../tzdata/zoneinfo/Africa/Harare | Bin 0 -> 131 bytes .../tzdata/zoneinfo/Africa/Johannesburg | Bin 0 -> 190 bytes .../site-packages/tzdata/zoneinfo/Africa/Juba | Bin 0 -> 458 bytes .../tzdata/zoneinfo/Africa/Kampala | Bin 0 -> 191 bytes .../tzdata/zoneinfo/Africa/Khartoum | Bin 0 -> 458 bytes .../tzdata/zoneinfo/Africa/Kigali | Bin 0 -> 131 bytes .../tzdata/zoneinfo/Africa/Kinshasa | Bin 0 -> 180 bytes .../tzdata/zoneinfo/Africa/Lagos | Bin 0 -> 180 bytes .../tzdata/zoneinfo/Africa/Libreville | Bin 0 -> 180 bytes .../site-packages/tzdata/zoneinfo/Africa/Lome | Bin 0 -> 130 bytes .../tzdata/zoneinfo/Africa/Luanda | Bin 0 -> 180 bytes .../tzdata/zoneinfo/Africa/Lubumbashi | Bin 0 -> 131 bytes .../tzdata/zoneinfo/Africa/Lusaka | Bin 0 -> 131 bytes .../tzdata/zoneinfo/Africa/Malabo | Bin 0 -> 180 bytes .../tzdata/zoneinfo/Africa/Maputo | Bin 0 -> 131 bytes .../tzdata/zoneinfo/Africa/Maseru | Bin 0 -> 190 bytes .../tzdata/zoneinfo/Africa/Mbabane | Bin 0 -> 190 bytes .../tzdata/zoneinfo/Africa/Mogadishu | Bin 0 -> 191 bytes .../tzdata/zoneinfo/Africa/Monrovia | Bin 0 -> 164 bytes .../tzdata/zoneinfo/Africa/Nairobi | Bin 0 -> 191 bytes .../tzdata/zoneinfo/Africa/Ndjamena | Bin 0 -> 160 bytes .../tzdata/zoneinfo/Africa/Niamey | Bin 0 -> 180 bytes .../tzdata/zoneinfo/Africa/Nouakchott | Bin 0 -> 130 bytes .../tzdata/zoneinfo/Africa/Ouagadougou | Bin 0 -> 130 bytes .../tzdata/zoneinfo/Africa/Porto-Novo | Bin 0 -> 180 bytes .../tzdata/zoneinfo/Africa/Sao_Tome | Bin 0 -> 173 bytes .../tzdata/zoneinfo/Africa/Timbuktu | Bin 0 -> 130 bytes .../tzdata/zoneinfo/Africa/Tripoli | Bin 0 -> 431 bytes .../tzdata/zoneinfo/Africa/Tunis | Bin 0 -> 449 bytes .../tzdata/zoneinfo/Africa/Windhoek | Bin 0 -> 638 bytes .../tzdata/zoneinfo/Africa/__init__.py | 0 .../tzdata/zoneinfo/America/Adak | Bin 0 -> 969 bytes .../tzdata/zoneinfo/America/Anchorage | Bin 0 -> 977 bytes .../tzdata/zoneinfo/America/Anguilla | Bin 0 -> 177 bytes .../tzdata/zoneinfo/America/Antigua | Bin 0 -> 177 bytes .../tzdata/zoneinfo/America/Araguaina | Bin 0 -> 592 bytes .../zoneinfo/America/Argentina/Buenos_Aires | Bin 0 -> 708 bytes .../zoneinfo/America/Argentina/Catamarca | Bin 0 -> 708 bytes .../zoneinfo/America/Argentina/ComodRivadavia | Bin 0 -> 708 bytes .../tzdata/zoneinfo/America/Argentina/Cordoba | Bin 0 -> 708 bytes .../tzdata/zoneinfo/America/Argentina/Jujuy | Bin 0 -> 690 bytes .../zoneinfo/America/Argentina/La_Rioja | Bin 0 -> 717 bytes .../tzdata/zoneinfo/America/Argentina/Mendoza | Bin 0 -> 708 bytes .../zoneinfo/America/Argentina/Rio_Gallegos | Bin 0 -> 708 bytes .../tzdata/zoneinfo/America/Argentina/Salta | Bin 0 -> 690 bytes .../zoneinfo/America/Argentina/San_Juan | Bin 0 -> 717 bytes .../zoneinfo/America/Argentina/San_Luis | Bin 0 -> 717 bytes .../tzdata/zoneinfo/America/Argentina/Tucuman | Bin 0 -> 726 bytes .../tzdata/zoneinfo/America/Argentina/Ushuaia | Bin 0 -> 708 bytes .../zoneinfo/America/Argentina/__init__.py | 0 .../tzdata/zoneinfo/America/Aruba | Bin 0 -> 177 bytes .../tzdata/zoneinfo/America/Asuncion | Bin 0 -> 1085 bytes .../tzdata/zoneinfo/America/Atikokan | Bin 0 -> 149 bytes .../tzdata/zoneinfo/America/Atka | Bin 0 -> 969 bytes .../tzdata/zoneinfo/America/Bahia | Bin 0 -> 682 bytes .../tzdata/zoneinfo/America/Bahia_Banderas | Bin 0 -> 700 bytes .../tzdata/zoneinfo/America/Barbados | Bin 0 -> 278 bytes .../tzdata/zoneinfo/America/Belem | Bin 0 -> 394 bytes .../tzdata/zoneinfo/America/Belize | Bin 0 -> 1045 bytes .../tzdata/zoneinfo/America/Blanc-Sablon | Bin 0 -> 177 bytes .../tzdata/zoneinfo/America/Boa_Vista | Bin 0 -> 430 bytes .../tzdata/zoneinfo/America/Bogota | Bin 0 -> 179 bytes .../tzdata/zoneinfo/America/Boise | Bin 0 -> 999 bytes .../tzdata/zoneinfo/America/Buenos_Aires | Bin 0 -> 708 bytes .../tzdata/zoneinfo/America/Cambridge_Bay | Bin 0 -> 883 bytes .../tzdata/zoneinfo/America/Campo_Grande | Bin 0 -> 952 bytes .../tzdata/zoneinfo/America/Cancun | Bin 0 -> 538 bytes .../tzdata/zoneinfo/America/Caracas | Bin 0 -> 190 bytes .../tzdata/zoneinfo/America/Catamarca | Bin 0 -> 708 bytes .../tzdata/zoneinfo/America/Cayenne | Bin 0 -> 151 bytes .../tzdata/zoneinfo/America/Cayman | Bin 0 -> 149 bytes .../tzdata/zoneinfo/America/Chicago | Bin 0 -> 1754 bytes .../tzdata/zoneinfo/America/Chihuahua | Bin 0 -> 691 bytes .../tzdata/zoneinfo/America/Ciudad_Juarez | Bin 0 -> 718 bytes .../tzdata/zoneinfo/America/Coral_Harbour | Bin 0 -> 149 bytes .../tzdata/zoneinfo/America/Cordoba | Bin 0 -> 708 bytes .../tzdata/zoneinfo/America/Costa_Rica | Bin 0 -> 232 bytes .../tzdata/zoneinfo/America/Coyhaique | Bin 0 -> 1362 bytes .../tzdata/zoneinfo/America/Creston | Bin 0 -> 240 bytes .../tzdata/zoneinfo/America/Cuiaba | Bin 0 -> 934 bytes .../tzdata/zoneinfo/America/Curacao | Bin 0 -> 177 bytes .../tzdata/zoneinfo/America/Danmarkshavn | Bin 0 -> 447 bytes .../tzdata/zoneinfo/America/Dawson | Bin 0 -> 1029 bytes .../tzdata/zoneinfo/America/Dawson_Creek | Bin 0 -> 683 bytes .../tzdata/zoneinfo/America/Denver | Bin 0 -> 1042 bytes .../tzdata/zoneinfo/America/Detroit | Bin 0 -> 899 bytes .../tzdata/zoneinfo/America/Dominica | Bin 0 -> 177 bytes .../tzdata/zoneinfo/America/Edmonton | Bin 0 -> 970 bytes .../tzdata/zoneinfo/America/Eirunepe | Bin 0 -> 436 bytes .../tzdata/zoneinfo/America/El_Salvador | Bin 0 -> 176 bytes .../tzdata/zoneinfo/America/Ensenada | Bin 0 -> 1367 bytes .../tzdata/zoneinfo/America/Fort_Nelson | Bin 0 -> 1448 bytes .../tzdata/zoneinfo/America/Fort_Wayne | Bin 0 -> 531 bytes .../tzdata/zoneinfo/America/Fortaleza | Bin 0 -> 484 bytes .../tzdata/zoneinfo/America/Glace_Bay | Bin 0 -> 880 bytes .../tzdata/zoneinfo/America/Godthab | Bin 0 -> 965 bytes .../tzdata/zoneinfo/America/Goose_Bay | Bin 0 -> 1580 bytes .../tzdata/zoneinfo/America/Grand_Turk | Bin 0 -> 853 bytes .../tzdata/zoneinfo/America/Grenada | Bin 0 -> 177 bytes .../tzdata/zoneinfo/America/Guadeloupe | Bin 0 -> 177 bytes .../tzdata/zoneinfo/America/Guatemala | Bin 0 -> 212 bytes .../tzdata/zoneinfo/America/Guayaquil | Bin 0 -> 179 bytes .../tzdata/zoneinfo/America/Guyana | Bin 0 -> 181 bytes .../tzdata/zoneinfo/America/Halifax | Bin 0 -> 1672 bytes .../tzdata/zoneinfo/America/Havana | Bin 0 -> 1117 bytes .../tzdata/zoneinfo/America/Hermosillo | Bin 0 -> 258 bytes .../zoneinfo/America/Indiana/Indianapolis | Bin 0 -> 531 bytes .../tzdata/zoneinfo/America/Indiana/Knox | Bin 0 -> 1016 bytes .../tzdata/zoneinfo/America/Indiana/Marengo | Bin 0 -> 567 bytes .../zoneinfo/America/Indiana/Petersburg | Bin 0 -> 683 bytes .../tzdata/zoneinfo/America/Indiana/Tell_City | Bin 0 -> 522 bytes .../tzdata/zoneinfo/America/Indiana/Vevay | Bin 0 -> 369 bytes .../tzdata/zoneinfo/America/Indiana/Vincennes | Bin 0 -> 558 bytes .../tzdata/zoneinfo/America/Indiana/Winamac | Bin 0 -> 603 bytes .../zoneinfo/America/Indiana/__init__.py | 0 .../tzdata/zoneinfo/America/Indianapolis | Bin 0 -> 531 bytes .../tzdata/zoneinfo/America/Inuvik | Bin 0 -> 817 bytes .../tzdata/zoneinfo/America/Iqaluit | Bin 0 -> 855 bytes .../tzdata/zoneinfo/America/Jamaica | Bin 0 -> 339 bytes .../tzdata/zoneinfo/America/Jujuy | Bin 0 -> 690 bytes .../tzdata/zoneinfo/America/Juneau | Bin 0 -> 966 bytes .../zoneinfo/America/Kentucky/Louisville | Bin 0 -> 1242 bytes .../zoneinfo/America/Kentucky/Monticello | Bin 0 -> 972 bytes .../zoneinfo/America/Kentucky/__init__.py | 0 .../tzdata/zoneinfo/America/Knox_IN | Bin 0 -> 1016 bytes .../tzdata/zoneinfo/America/Kralendijk | Bin 0 -> 177 bytes .../tzdata/zoneinfo/America/La_Paz | Bin 0 -> 170 bytes .../tzdata/zoneinfo/America/Lima | Bin 0 -> 283 bytes .../tzdata/zoneinfo/America/Los_Angeles | Bin 0 -> 1294 bytes .../tzdata/zoneinfo/America/Louisville | Bin 0 -> 1242 bytes .../tzdata/zoneinfo/America/Lower_Princes | Bin 0 -> 177 bytes .../tzdata/zoneinfo/America/Maceio | Bin 0 -> 502 bytes .../tzdata/zoneinfo/America/Managua | Bin 0 -> 295 bytes .../tzdata/zoneinfo/America/Manaus | Bin 0 -> 412 bytes .../tzdata/zoneinfo/America/Marigot | Bin 0 -> 177 bytes .../tzdata/zoneinfo/America/Martinique | Bin 0 -> 178 bytes .../tzdata/zoneinfo/America/Matamoros | Bin 0 -> 437 bytes .../tzdata/zoneinfo/America/Mazatlan | Bin 0 -> 690 bytes .../tzdata/zoneinfo/America/Mendoza | Bin 0 -> 708 bytes .../tzdata/zoneinfo/America/Menominee | Bin 0 -> 917 bytes .../tzdata/zoneinfo/America/Merida | Bin 0 -> 654 bytes .../tzdata/zoneinfo/America/Metlakatla | Bin 0 -> 586 bytes .../tzdata/zoneinfo/America/Mexico_City | Bin 0 -> 773 bytes .../tzdata/zoneinfo/America/Miquelon | Bin 0 -> 550 bytes .../tzdata/zoneinfo/America/Moncton | Bin 0 -> 1493 bytes .../tzdata/zoneinfo/America/Monterrey | Bin 0 -> 709 bytes .../tzdata/zoneinfo/America/Montevideo | Bin 0 -> 969 bytes .../tzdata/zoneinfo/America/Montreal | Bin 0 -> 1717 bytes .../tzdata/zoneinfo/America/Montserrat | Bin 0 -> 177 bytes .../tzdata/zoneinfo/America/Nassau | Bin 0 -> 1717 bytes .../tzdata/zoneinfo/America/New_York | Bin 0 -> 1744 bytes .../tzdata/zoneinfo/America/Nipigon | Bin 0 -> 1717 bytes .../tzdata/zoneinfo/America/Nome | Bin 0 -> 975 bytes .../tzdata/zoneinfo/America/Noronha | Bin 0 -> 484 bytes .../zoneinfo/America/North_Dakota/Beulah | Bin 0 -> 1043 bytes .../zoneinfo/America/North_Dakota/Center | Bin 0 -> 990 bytes .../zoneinfo/America/North_Dakota/New_Salem | Bin 0 -> 990 bytes .../zoneinfo/America/North_Dakota/__init__.py | 0 .../tzdata/zoneinfo/America/Nuuk | Bin 0 -> 965 bytes .../tzdata/zoneinfo/America/Ojinaga | Bin 0 -> 718 bytes .../tzdata/zoneinfo/America/Panama | Bin 0 -> 149 bytes .../tzdata/zoneinfo/America/Pangnirtung | Bin 0 -> 855 bytes .../tzdata/zoneinfo/America/Paramaribo | Bin 0 -> 187 bytes .../tzdata/zoneinfo/America/Phoenix | Bin 0 -> 240 bytes .../tzdata/zoneinfo/America/Port-au-Prince | Bin 0 -> 565 bytes .../tzdata/zoneinfo/America/Port_of_Spain | Bin 0 -> 177 bytes .../tzdata/zoneinfo/America/Porto_Acre | Bin 0 -> 418 bytes .../tzdata/zoneinfo/America/Porto_Velho | Bin 0 -> 394 bytes .../tzdata/zoneinfo/America/Puerto_Rico | Bin 0 -> 177 bytes .../tzdata/zoneinfo/America/Punta_Arenas | Bin 0 -> 1218 bytes .../tzdata/zoneinfo/America/Rainy_River | Bin 0 -> 1294 bytes .../tzdata/zoneinfo/America/Rankin_Inlet | Bin 0 -> 807 bytes .../tzdata/zoneinfo/America/Recife | Bin 0 -> 484 bytes .../tzdata/zoneinfo/America/Regina | Bin 0 -> 638 bytes .../tzdata/zoneinfo/America/Resolute | Bin 0 -> 807 bytes .../tzdata/zoneinfo/America/Rio_Branco | Bin 0 -> 418 bytes .../tzdata/zoneinfo/America/Rosario | Bin 0 -> 708 bytes .../tzdata/zoneinfo/America/Santa_Isabel | Bin 0 -> 1367 bytes .../tzdata/zoneinfo/America/Santarem | Bin 0 -> 409 bytes .../tzdata/zoneinfo/America/Santiago | Bin 0 -> 1354 bytes .../tzdata/zoneinfo/America/Santo_Domingo | Bin 0 -> 317 bytes .../tzdata/zoneinfo/America/Sao_Paulo | Bin 0 -> 952 bytes .../tzdata/zoneinfo/America/Scoresbysund | Bin 0 -> 984 bytes .../tzdata/zoneinfo/America/Shiprock | Bin 0 -> 1042 bytes .../tzdata/zoneinfo/America/Sitka | Bin 0 -> 956 bytes .../tzdata/zoneinfo/America/St_Barthelemy | Bin 0 -> 177 bytes .../tzdata/zoneinfo/America/St_Johns | Bin 0 -> 1878 bytes .../tzdata/zoneinfo/America/St_Kitts | Bin 0 -> 177 bytes .../tzdata/zoneinfo/America/St_Lucia | Bin 0 -> 177 bytes .../tzdata/zoneinfo/America/St_Thomas | Bin 0 -> 177 bytes .../tzdata/zoneinfo/America/St_Vincent | Bin 0 -> 177 bytes .../tzdata/zoneinfo/America/Swift_Current | Bin 0 -> 368 bytes .../tzdata/zoneinfo/America/Tegucigalpa | Bin 0 -> 194 bytes .../tzdata/zoneinfo/America/Thule | Bin 0 -> 455 bytes .../tzdata/zoneinfo/America/Thunder_Bay | Bin 0 -> 1717 bytes .../tzdata/zoneinfo/America/Tijuana | Bin 0 -> 1367 bytes .../tzdata/zoneinfo/America/Toronto | Bin 0 -> 1717 bytes .../tzdata/zoneinfo/America/Tortola | Bin 0 -> 177 bytes .../tzdata/zoneinfo/America/Vancouver | Bin 0 -> 1330 bytes .../tzdata/zoneinfo/America/Virgin | Bin 0 -> 177 bytes .../tzdata/zoneinfo/America/Whitehorse | Bin 0 -> 1029 bytes .../tzdata/zoneinfo/America/Winnipeg | Bin 0 -> 1294 bytes .../tzdata/zoneinfo/America/Yakutat | Bin 0 -> 946 bytes .../tzdata/zoneinfo/America/Yellowknife | Bin 0 -> 970 bytes .../tzdata/zoneinfo/America/__init__.py | 0 .../tzdata/zoneinfo/Antarctica/Casey | Bin 0 -> 287 bytes .../tzdata/zoneinfo/Antarctica/Davis | Bin 0 -> 197 bytes .../tzdata/zoneinfo/Antarctica/DumontDUrville | Bin 0 -> 154 bytes .../tzdata/zoneinfo/Antarctica/Macquarie | Bin 0 -> 976 bytes .../tzdata/zoneinfo/Antarctica/Mawson | Bin 0 -> 152 bytes .../tzdata/zoneinfo/Antarctica/McMurdo | Bin 0 -> 1043 bytes .../tzdata/zoneinfo/Antarctica/Palmer | Bin 0 -> 887 bytes .../tzdata/zoneinfo/Antarctica/Rothera | Bin 0 -> 132 bytes .../tzdata/zoneinfo/Antarctica/South_Pole | Bin 0 -> 1043 bytes .../tzdata/zoneinfo/Antarctica/Syowa | Bin 0 -> 133 bytes .../tzdata/zoneinfo/Antarctica/Troll | Bin 0 -> 158 bytes .../tzdata/zoneinfo/Antarctica/Vostok | Bin 0 -> 170 bytes .../tzdata/zoneinfo/Antarctica/__init__.py | 0 .../tzdata/zoneinfo/Arctic/Longyearbyen | Bin 0 -> 705 bytes .../tzdata/zoneinfo/Arctic/__init__.py | 0 .../site-packages/tzdata/zoneinfo/Asia/Aden | Bin 0 -> 133 bytes .../site-packages/tzdata/zoneinfo/Asia/Almaty | Bin 0 -> 618 bytes .../site-packages/tzdata/zoneinfo/Asia/Amman | Bin 0 -> 928 bytes .../site-packages/tzdata/zoneinfo/Asia/Anadyr | Bin 0 -> 743 bytes .../site-packages/tzdata/zoneinfo/Asia/Aqtau | Bin 0 -> 606 bytes .../site-packages/tzdata/zoneinfo/Asia/Aqtobe | Bin 0 -> 615 bytes .../tzdata/zoneinfo/Asia/Ashgabat | Bin 0 -> 375 bytes .../tzdata/zoneinfo/Asia/Ashkhabad | Bin 0 -> 375 bytes .../site-packages/tzdata/zoneinfo/Asia/Atyrau | Bin 0 -> 616 bytes .../tzdata/zoneinfo/Asia/Baghdad | Bin 0 -> 630 bytes .../tzdata/zoneinfo/Asia/Bahrain | Bin 0 -> 152 bytes .../site-packages/tzdata/zoneinfo/Asia/Baku | Bin 0 -> 744 bytes .../tzdata/zoneinfo/Asia/Bangkok | Bin 0 -> 152 bytes .../tzdata/zoneinfo/Asia/Barnaul | Bin 0 -> 753 bytes .../site-packages/tzdata/zoneinfo/Asia/Beirut | Bin 0 -> 732 bytes .../tzdata/zoneinfo/Asia/Bishkek | Bin 0 -> 618 bytes .../site-packages/tzdata/zoneinfo/Asia/Brunei | Bin 0 -> 320 bytes .../tzdata/zoneinfo/Asia/Calcutta | Bin 0 -> 220 bytes .../site-packages/tzdata/zoneinfo/Asia/Chita | Bin 0 -> 750 bytes .../tzdata/zoneinfo/Asia/Choibalsan | Bin 0 -> 594 bytes .../tzdata/zoneinfo/Asia/Chongqing | Bin 0 -> 393 bytes .../tzdata/zoneinfo/Asia/Chungking | Bin 0 -> 393 bytes .../tzdata/zoneinfo/Asia/Colombo | Bin 0 -> 247 bytes .../site-packages/tzdata/zoneinfo/Asia/Dacca | Bin 0 -> 231 bytes .../tzdata/zoneinfo/Asia/Damascus | Bin 0 -> 1234 bytes .../site-packages/tzdata/zoneinfo/Asia/Dhaka | Bin 0 -> 231 bytes .../site-packages/tzdata/zoneinfo/Asia/Dili | Bin 0 -> 170 bytes .../site-packages/tzdata/zoneinfo/Asia/Dubai | Bin 0 -> 133 bytes .../tzdata/zoneinfo/Asia/Dushanbe | Bin 0 -> 366 bytes .../tzdata/zoneinfo/Asia/Famagusta | Bin 0 -> 940 bytes .../site-packages/tzdata/zoneinfo/Asia/Gaza | Bin 0 -> 2950 bytes .../site-packages/tzdata/zoneinfo/Asia/Harbin | Bin 0 -> 393 bytes .../site-packages/tzdata/zoneinfo/Asia/Hebron | Bin 0 -> 2968 bytes .../tzdata/zoneinfo/Asia/Ho_Chi_Minh | Bin 0 -> 236 bytes .../tzdata/zoneinfo/Asia/Hong_Kong | Bin 0 -> 775 bytes .../site-packages/tzdata/zoneinfo/Asia/Hovd | Bin 0 -> 594 bytes .../tzdata/zoneinfo/Asia/Irkutsk | Bin 0 -> 760 bytes .../tzdata/zoneinfo/Asia/Istanbul | Bin 0 -> 1200 bytes .../tzdata/zoneinfo/Asia/Jakarta | Bin 0 -> 248 bytes .../tzdata/zoneinfo/Asia/Jayapura | Bin 0 -> 171 bytes .../tzdata/zoneinfo/Asia/Jerusalem | Bin 0 -> 1074 bytes .../site-packages/tzdata/zoneinfo/Asia/Kabul | Bin 0 -> 159 bytes .../tzdata/zoneinfo/Asia/Kamchatka | Bin 0 -> 727 bytes .../tzdata/zoneinfo/Asia/Karachi | Bin 0 -> 266 bytes .../tzdata/zoneinfo/Asia/Kashgar | Bin 0 -> 133 bytes .../tzdata/zoneinfo/Asia/Kathmandu | Bin 0 -> 161 bytes .../tzdata/zoneinfo/Asia/Katmandu | Bin 0 -> 161 bytes .../tzdata/zoneinfo/Asia/Khandyga | Bin 0 -> 775 bytes .../tzdata/zoneinfo/Asia/Kolkata | Bin 0 -> 220 bytes .../tzdata/zoneinfo/Asia/Krasnoyarsk | Bin 0 -> 741 bytes .../tzdata/zoneinfo/Asia/Kuala_Lumpur | Bin 0 -> 256 bytes .../tzdata/zoneinfo/Asia/Kuching | Bin 0 -> 320 bytes .../site-packages/tzdata/zoneinfo/Asia/Kuwait | Bin 0 -> 133 bytes .../site-packages/tzdata/zoneinfo/Asia/Macao | Bin 0 -> 791 bytes .../site-packages/tzdata/zoneinfo/Asia/Macau | Bin 0 -> 791 bytes .../tzdata/zoneinfo/Asia/Magadan | Bin 0 -> 751 bytes .../tzdata/zoneinfo/Asia/Makassar | Bin 0 -> 190 bytes .../site-packages/tzdata/zoneinfo/Asia/Manila | Bin 0 -> 274 bytes .../site-packages/tzdata/zoneinfo/Asia/Muscat | Bin 0 -> 133 bytes .../tzdata/zoneinfo/Asia/Nicosia | Bin 0 -> 597 bytes .../tzdata/zoneinfo/Asia/Novokuznetsk | Bin 0 -> 726 bytes .../tzdata/zoneinfo/Asia/Novosibirsk | Bin 0 -> 753 bytes .../site-packages/tzdata/zoneinfo/Asia/Omsk | Bin 0 -> 741 bytes .../site-packages/tzdata/zoneinfo/Asia/Oral | Bin 0 -> 625 bytes .../tzdata/zoneinfo/Asia/Phnom_Penh | Bin 0 -> 152 bytes .../tzdata/zoneinfo/Asia/Pontianak | Bin 0 -> 247 bytes .../tzdata/zoneinfo/Asia/Pyongyang | Bin 0 -> 183 bytes .../site-packages/tzdata/zoneinfo/Asia/Qatar | Bin 0 -> 152 bytes .../tzdata/zoneinfo/Asia/Qostanay | Bin 0 -> 624 bytes .../tzdata/zoneinfo/Asia/Qyzylorda | Bin 0 -> 624 bytes .../tzdata/zoneinfo/Asia/Rangoon | Bin 0 -> 187 bytes .../site-packages/tzdata/zoneinfo/Asia/Riyadh | Bin 0 -> 133 bytes .../site-packages/tzdata/zoneinfo/Asia/Saigon | Bin 0 -> 236 bytes .../tzdata/zoneinfo/Asia/Sakhalin | Bin 0 -> 755 bytes .../tzdata/zoneinfo/Asia/Samarkand | Bin 0 -> 366 bytes .../site-packages/tzdata/zoneinfo/Asia/Seoul | Bin 0 -> 415 bytes .../tzdata/zoneinfo/Asia/Shanghai | Bin 0 -> 393 bytes .../tzdata/zoneinfo/Asia/Singapore | Bin 0 -> 256 bytes .../tzdata/zoneinfo/Asia/Srednekolymsk | Bin 0 -> 742 bytes .../site-packages/tzdata/zoneinfo/Asia/Taipei | Bin 0 -> 511 bytes .../tzdata/zoneinfo/Asia/Tashkent | Bin 0 -> 366 bytes .../tzdata/zoneinfo/Asia/Tbilisi | Bin 0 -> 629 bytes .../site-packages/tzdata/zoneinfo/Asia/Tehran | Bin 0 -> 812 bytes .../tzdata/zoneinfo/Asia/Tel_Aviv | Bin 0 -> 1074 bytes .../site-packages/tzdata/zoneinfo/Asia/Thimbu | Bin 0 -> 154 bytes .../tzdata/zoneinfo/Asia/Thimphu | Bin 0 -> 154 bytes .../site-packages/tzdata/zoneinfo/Asia/Tokyo | Bin 0 -> 213 bytes .../site-packages/tzdata/zoneinfo/Asia/Tomsk | Bin 0 -> 753 bytes .../tzdata/zoneinfo/Asia/Ujung_Pandang | Bin 0 -> 190 bytes .../tzdata/zoneinfo/Asia/Ulaanbaatar | Bin 0 -> 594 bytes .../tzdata/zoneinfo/Asia/Ulan_Bator | Bin 0 -> 594 bytes .../site-packages/tzdata/zoneinfo/Asia/Urumqi | Bin 0 -> 133 bytes .../tzdata/zoneinfo/Asia/Ust-Nera | Bin 0 -> 771 bytes .../tzdata/zoneinfo/Asia/Vientiane | Bin 0 -> 152 bytes .../tzdata/zoneinfo/Asia/Vladivostok | Bin 0 -> 742 bytes .../tzdata/zoneinfo/Asia/Yakutsk | Bin 0 -> 741 bytes .../site-packages/tzdata/zoneinfo/Asia/Yangon | Bin 0 -> 187 bytes .../tzdata/zoneinfo/Asia/Yekaterinburg | Bin 0 -> 760 bytes .../tzdata/zoneinfo/Asia/Yerevan | Bin 0 -> 708 bytes .../tzdata/zoneinfo/Asia/__init__.py | 0 .../tzdata/zoneinfo/Atlantic/Azores | Bin 0 -> 1401 bytes .../tzdata/zoneinfo/Atlantic/Bermuda | Bin 0 -> 1024 bytes .../tzdata/zoneinfo/Atlantic/Canary | Bin 0 -> 478 bytes .../tzdata/zoneinfo/Atlantic/Cape_Verde | Bin 0 -> 175 bytes .../tzdata/zoneinfo/Atlantic/Faeroe | Bin 0 -> 441 bytes .../tzdata/zoneinfo/Atlantic/Faroe | Bin 0 -> 441 bytes .../tzdata/zoneinfo/Atlantic/Jan_Mayen | Bin 0 -> 705 bytes .../tzdata/zoneinfo/Atlantic/Madeira | Bin 0 -> 1372 bytes .../tzdata/zoneinfo/Atlantic/Reykjavik | Bin 0 -> 130 bytes .../tzdata/zoneinfo/Atlantic/South_Georgia | Bin 0 -> 132 bytes .../tzdata/zoneinfo/Atlantic/St_Helena | Bin 0 -> 130 bytes .../tzdata/zoneinfo/Atlantic/Stanley | Bin 0 -> 789 bytes .../tzdata/zoneinfo/Atlantic/__init__.py | 0 .../tzdata/zoneinfo/Australia/ACT | Bin 0 -> 904 bytes .../tzdata/zoneinfo/Australia/Adelaide | Bin 0 -> 921 bytes .../tzdata/zoneinfo/Australia/Brisbane | Bin 0 -> 289 bytes .../tzdata/zoneinfo/Australia/Broken_Hill | Bin 0 -> 941 bytes .../tzdata/zoneinfo/Australia/Canberra | Bin 0 -> 904 bytes .../tzdata/zoneinfo/Australia/Currie | Bin 0 -> 1003 bytes .../tzdata/zoneinfo/Australia/Darwin | Bin 0 -> 234 bytes .../tzdata/zoneinfo/Australia/Eucla | Bin 0 -> 314 bytes .../tzdata/zoneinfo/Australia/Hobart | Bin 0 -> 1003 bytes .../tzdata/zoneinfo/Australia/LHI | Bin 0 -> 692 bytes .../tzdata/zoneinfo/Australia/Lindeman | Bin 0 -> 325 bytes .../tzdata/zoneinfo/Australia/Lord_Howe | Bin 0 -> 692 bytes .../tzdata/zoneinfo/Australia/Melbourne | Bin 0 -> 904 bytes .../tzdata/zoneinfo/Australia/NSW | Bin 0 -> 904 bytes .../tzdata/zoneinfo/Australia/North | Bin 0 -> 234 bytes .../tzdata/zoneinfo/Australia/Perth | Bin 0 -> 306 bytes .../tzdata/zoneinfo/Australia/Queensland | Bin 0 -> 289 bytes .../tzdata/zoneinfo/Australia/South | Bin 0 -> 921 bytes .../tzdata/zoneinfo/Australia/Sydney | Bin 0 -> 904 bytes .../tzdata/zoneinfo/Australia/Tasmania | Bin 0 -> 1003 bytes .../tzdata/zoneinfo/Australia/Victoria | Bin 0 -> 904 bytes .../tzdata/zoneinfo/Australia/West | Bin 0 -> 306 bytes .../tzdata/zoneinfo/Australia/Yancowinna | Bin 0 -> 941 bytes .../tzdata/zoneinfo/Australia/__init__.py | 0 .../site-packages/tzdata/zoneinfo/Brazil/Acre | Bin 0 -> 418 bytes .../tzdata/zoneinfo/Brazil/DeNoronha | Bin 0 -> 484 bytes .../site-packages/tzdata/zoneinfo/Brazil/East | Bin 0 -> 952 bytes .../site-packages/tzdata/zoneinfo/Brazil/West | Bin 0 -> 412 bytes .../tzdata/zoneinfo/Brazil/__init__.py | 0 .../site-packages/tzdata/zoneinfo/CET | Bin 0 -> 1103 bytes .../site-packages/tzdata/zoneinfo/CST6CDT | Bin 0 -> 1754 bytes .../tzdata/zoneinfo/Canada/Atlantic | Bin 0 -> 1672 bytes .../tzdata/zoneinfo/Canada/Central | Bin 0 -> 1294 bytes .../tzdata/zoneinfo/Canada/Eastern | Bin 0 -> 1717 bytes .../tzdata/zoneinfo/Canada/Mountain | Bin 0 -> 970 bytes .../tzdata/zoneinfo/Canada/Newfoundland | Bin 0 -> 1878 bytes .../tzdata/zoneinfo/Canada/Pacific | Bin 0 -> 1330 bytes .../tzdata/zoneinfo/Canada/Saskatchewan | Bin 0 -> 638 bytes .../tzdata/zoneinfo/Canada/Yukon | Bin 0 -> 1029 bytes .../tzdata/zoneinfo/Canada/__init__.py | 0 .../tzdata/zoneinfo/Chile/Continental | Bin 0 -> 1354 bytes .../tzdata/zoneinfo/Chile/EasterIsland | Bin 0 -> 1174 bytes .../tzdata/zoneinfo/Chile/__init__.py | 0 .../site-packages/tzdata/zoneinfo/Cuba | Bin 0 -> 1117 bytes .../site-packages/tzdata/zoneinfo/EET | Bin 0 -> 682 bytes .../site-packages/tzdata/zoneinfo/EST | Bin 0 -> 149 bytes .../site-packages/tzdata/zoneinfo/EST5EDT | Bin 0 -> 1744 bytes .../site-packages/tzdata/zoneinfo/Egypt | Bin 0 -> 1309 bytes .../site-packages/tzdata/zoneinfo/Eire | Bin 0 -> 1496 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT | Bin 0 -> 111 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT+0 | Bin 0 -> 111 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT+1 | Bin 0 -> 113 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT+10 | Bin 0 -> 114 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT+11 | Bin 0 -> 114 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT+12 | Bin 0 -> 114 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT+2 | Bin 0 -> 113 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT+3 | Bin 0 -> 113 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT+4 | Bin 0 -> 113 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT+5 | Bin 0 -> 113 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT+6 | Bin 0 -> 113 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT+7 | Bin 0 -> 113 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT+8 | Bin 0 -> 113 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT+9 | Bin 0 -> 113 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT-0 | Bin 0 -> 111 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT-1 | Bin 0 -> 114 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT-10 | Bin 0 -> 115 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT-11 | Bin 0 -> 115 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT-12 | Bin 0 -> 115 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT-13 | Bin 0 -> 115 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT-14 | Bin 0 -> 115 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT-2 | Bin 0 -> 114 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT-3 | Bin 0 -> 114 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT-4 | Bin 0 -> 114 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT-5 | Bin 0 -> 114 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT-6 | Bin 0 -> 114 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT-7 | Bin 0 -> 114 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT-8 | Bin 0 -> 114 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT-9 | Bin 0 -> 114 bytes .../site-packages/tzdata/zoneinfo/Etc/GMT0 | Bin 0 -> 111 bytes .../tzdata/zoneinfo/Etc/Greenwich | Bin 0 -> 111 bytes .../site-packages/tzdata/zoneinfo/Etc/UCT | Bin 0 -> 111 bytes .../site-packages/tzdata/zoneinfo/Etc/UTC | Bin 0 -> 111 bytes .../tzdata/zoneinfo/Etc/Universal | Bin 0 -> 111 bytes .../site-packages/tzdata/zoneinfo/Etc/Zulu | Bin 0 -> 111 bytes .../tzdata/zoneinfo/Etc/__init__.py | 0 .../tzdata/zoneinfo/Europe/Amsterdam | Bin 0 -> 1103 bytes .../tzdata/zoneinfo/Europe/Andorra | Bin 0 -> 389 bytes .../tzdata/zoneinfo/Europe/Astrakhan | Bin 0 -> 726 bytes .../tzdata/zoneinfo/Europe/Athens | Bin 0 -> 682 bytes .../tzdata/zoneinfo/Europe/Belfast | Bin 0 -> 1599 bytes .../tzdata/zoneinfo/Europe/Belgrade | Bin 0 -> 478 bytes .../tzdata/zoneinfo/Europe/Berlin | Bin 0 -> 705 bytes .../tzdata/zoneinfo/Europe/Bratislava | Bin 0 -> 723 bytes .../tzdata/zoneinfo/Europe/Brussels | Bin 0 -> 1103 bytes .../tzdata/zoneinfo/Europe/Bucharest | Bin 0 -> 661 bytes .../tzdata/zoneinfo/Europe/Budapest | Bin 0 -> 766 bytes .../tzdata/zoneinfo/Europe/Busingen | Bin 0 -> 497 bytes .../tzdata/zoneinfo/Europe/Chisinau | Bin 0 -> 755 bytes .../tzdata/zoneinfo/Europe/Copenhagen | Bin 0 -> 705 bytes .../tzdata/zoneinfo/Europe/Dublin | Bin 0 -> 1496 bytes .../tzdata/zoneinfo/Europe/Gibraltar | Bin 0 -> 1220 bytes .../tzdata/zoneinfo/Europe/Guernsey | Bin 0 -> 1599 bytes .../tzdata/zoneinfo/Europe/Helsinki | Bin 0 -> 481 bytes .../tzdata/zoneinfo/Europe/Isle_of_Man | Bin 0 -> 1599 bytes .../tzdata/zoneinfo/Europe/Istanbul | Bin 0 -> 1200 bytes .../tzdata/zoneinfo/Europe/Jersey | Bin 0 -> 1599 bytes .../tzdata/zoneinfo/Europe/Kaliningrad | Bin 0 -> 904 bytes .../site-packages/tzdata/zoneinfo/Europe/Kiev | Bin 0 -> 558 bytes .../tzdata/zoneinfo/Europe/Kirov | Bin 0 -> 735 bytes .../site-packages/tzdata/zoneinfo/Europe/Kyiv | Bin 0 -> 558 bytes .../tzdata/zoneinfo/Europe/Lisbon | Bin 0 -> 1463 bytes .../tzdata/zoneinfo/Europe/Ljubljana | Bin 0 -> 478 bytes .../tzdata/zoneinfo/Europe/London | Bin 0 -> 1599 bytes .../tzdata/zoneinfo/Europe/Luxembourg | Bin 0 -> 1103 bytes .../tzdata/zoneinfo/Europe/Madrid | Bin 0 -> 897 bytes .../tzdata/zoneinfo/Europe/Malta | Bin 0 -> 928 bytes .../tzdata/zoneinfo/Europe/Mariehamn | Bin 0 -> 481 bytes .../tzdata/zoneinfo/Europe/Minsk | Bin 0 -> 808 bytes .../tzdata/zoneinfo/Europe/Monaco | Bin 0 -> 1105 bytes .../tzdata/zoneinfo/Europe/Moscow | Bin 0 -> 908 bytes .../tzdata/zoneinfo/Europe/Nicosia | Bin 0 -> 597 bytes .../site-packages/tzdata/zoneinfo/Europe/Oslo | Bin 0 -> 705 bytes .../tzdata/zoneinfo/Europe/Paris | Bin 0 -> 1105 bytes .../tzdata/zoneinfo/Europe/Podgorica | Bin 0 -> 478 bytes .../tzdata/zoneinfo/Europe/Prague | Bin 0 -> 723 bytes .../site-packages/tzdata/zoneinfo/Europe/Riga | Bin 0 -> 694 bytes .../site-packages/tzdata/zoneinfo/Europe/Rome | Bin 0 -> 947 bytes .../tzdata/zoneinfo/Europe/Samara | Bin 0 -> 732 bytes .../tzdata/zoneinfo/Europe/San_Marino | Bin 0 -> 947 bytes .../tzdata/zoneinfo/Europe/Sarajevo | Bin 0 -> 478 bytes .../tzdata/zoneinfo/Europe/Saratov | Bin 0 -> 726 bytes .../tzdata/zoneinfo/Europe/Simferopol | Bin 0 -> 865 bytes .../tzdata/zoneinfo/Europe/Skopje | Bin 0 -> 478 bytes .../tzdata/zoneinfo/Europe/Sofia | Bin 0 -> 592 bytes .../tzdata/zoneinfo/Europe/Stockholm | Bin 0 -> 705 bytes .../tzdata/zoneinfo/Europe/Tallinn | Bin 0 -> 675 bytes .../tzdata/zoneinfo/Europe/Tirane | Bin 0 -> 604 bytes .../tzdata/zoneinfo/Europe/Tiraspol | Bin 0 -> 755 bytes .../tzdata/zoneinfo/Europe/Ulyanovsk | Bin 0 -> 760 bytes .../tzdata/zoneinfo/Europe/Uzhgorod | Bin 0 -> 558 bytes .../tzdata/zoneinfo/Europe/Vaduz | Bin 0 -> 497 bytes .../tzdata/zoneinfo/Europe/Vatican | Bin 0 -> 947 bytes .../tzdata/zoneinfo/Europe/Vienna | Bin 0 -> 658 bytes .../tzdata/zoneinfo/Europe/Vilnius | Bin 0 -> 676 bytes .../tzdata/zoneinfo/Europe/Volgograd | Bin 0 -> 753 bytes .../tzdata/zoneinfo/Europe/Warsaw | Bin 0 -> 923 bytes .../tzdata/zoneinfo/Europe/Zagreb | Bin 0 -> 478 bytes .../tzdata/zoneinfo/Europe/Zaporozhye | Bin 0 -> 558 bytes .../tzdata/zoneinfo/Europe/Zurich | Bin 0 -> 497 bytes .../tzdata/zoneinfo/Europe/__init__.py | 0 .../site-packages/tzdata/zoneinfo/Factory | Bin 0 -> 113 bytes .../site-packages/tzdata/zoneinfo/GB | Bin 0 -> 1599 bytes .../site-packages/tzdata/zoneinfo/GB-Eire | Bin 0 -> 1599 bytes .../site-packages/tzdata/zoneinfo/GMT | Bin 0 -> 111 bytes .../site-packages/tzdata/zoneinfo/GMT+0 | Bin 0 -> 111 bytes .../site-packages/tzdata/zoneinfo/GMT-0 | Bin 0 -> 111 bytes .../site-packages/tzdata/zoneinfo/GMT0 | Bin 0 -> 111 bytes .../site-packages/tzdata/zoneinfo/Greenwich | Bin 0 -> 111 bytes .../site-packages/tzdata/zoneinfo/HST | Bin 0 -> 221 bytes .../site-packages/tzdata/zoneinfo/Hongkong | Bin 0 -> 775 bytes .../site-packages/tzdata/zoneinfo/Iceland | Bin 0 -> 130 bytes .../tzdata/zoneinfo/Indian/Antananarivo | Bin 0 -> 191 bytes .../tzdata/zoneinfo/Indian/Chagos | Bin 0 -> 152 bytes .../tzdata/zoneinfo/Indian/Christmas | Bin 0 -> 152 bytes .../tzdata/zoneinfo/Indian/Cocos | Bin 0 -> 187 bytes .../tzdata/zoneinfo/Indian/Comoro | Bin 0 -> 191 bytes .../tzdata/zoneinfo/Indian/Kerguelen | Bin 0 -> 152 bytes .../site-packages/tzdata/zoneinfo/Indian/Mahe | Bin 0 -> 133 bytes .../tzdata/zoneinfo/Indian/Maldives | Bin 0 -> 152 bytes .../tzdata/zoneinfo/Indian/Mauritius | Bin 0 -> 179 bytes .../tzdata/zoneinfo/Indian/Mayotte | Bin 0 -> 191 bytes .../tzdata/zoneinfo/Indian/Reunion | Bin 0 -> 133 bytes .../tzdata/zoneinfo/Indian/__init__.py | 0 .../site-packages/tzdata/zoneinfo/Iran | Bin 0 -> 812 bytes .../site-packages/tzdata/zoneinfo/Israel | Bin 0 -> 1074 bytes .../site-packages/tzdata/zoneinfo/Jamaica | Bin 0 -> 339 bytes .../site-packages/tzdata/zoneinfo/Japan | Bin 0 -> 213 bytes .../site-packages/tzdata/zoneinfo/Kwajalein | Bin 0 -> 219 bytes .../site-packages/tzdata/zoneinfo/Libya | Bin 0 -> 431 bytes .../site-packages/tzdata/zoneinfo/MET | Bin 0 -> 1103 bytes .../site-packages/tzdata/zoneinfo/MST | Bin 0 -> 240 bytes .../site-packages/tzdata/zoneinfo/MST7MDT | Bin 0 -> 1042 bytes .../tzdata/zoneinfo/Mexico/BajaNorte | Bin 0 -> 1367 bytes .../tzdata/zoneinfo/Mexico/BajaSur | Bin 0 -> 690 bytes .../tzdata/zoneinfo/Mexico/General | Bin 0 -> 773 bytes .../tzdata/zoneinfo/Mexico/__init__.py | 0 .../site-packages/tzdata/zoneinfo/NZ | Bin 0 -> 1043 bytes .../site-packages/tzdata/zoneinfo/NZ-CHAT | Bin 0 -> 808 bytes .../site-packages/tzdata/zoneinfo/Navajo | Bin 0 -> 1042 bytes .../site-packages/tzdata/zoneinfo/PRC | Bin 0 -> 393 bytes .../site-packages/tzdata/zoneinfo/PST8PDT | Bin 0 -> 1294 bytes .../tzdata/zoneinfo/Pacific/Apia | Bin 0 -> 407 bytes .../tzdata/zoneinfo/Pacific/Auckland | Bin 0 -> 1043 bytes .../tzdata/zoneinfo/Pacific/Bougainville | Bin 0 -> 201 bytes .../tzdata/zoneinfo/Pacific/Chatham | Bin 0 -> 808 bytes .../tzdata/zoneinfo/Pacific/Chuuk | Bin 0 -> 154 bytes .../tzdata/zoneinfo/Pacific/Easter | Bin 0 -> 1174 bytes .../tzdata/zoneinfo/Pacific/Efate | Bin 0 -> 342 bytes .../tzdata/zoneinfo/Pacific/Enderbury | Bin 0 -> 172 bytes .../tzdata/zoneinfo/Pacific/Fakaofo | Bin 0 -> 153 bytes .../tzdata/zoneinfo/Pacific/Fiji | Bin 0 -> 396 bytes .../tzdata/zoneinfo/Pacific/Funafuti | Bin 0 -> 134 bytes .../tzdata/zoneinfo/Pacific/Galapagos | Bin 0 -> 175 bytes .../tzdata/zoneinfo/Pacific/Gambier | Bin 0 -> 132 bytes .../tzdata/zoneinfo/Pacific/Guadalcanal | Bin 0 -> 134 bytes .../tzdata/zoneinfo/Pacific/Guam | Bin 0 -> 350 bytes .../tzdata/zoneinfo/Pacific/Honolulu | Bin 0 -> 221 bytes .../tzdata/zoneinfo/Pacific/Johnston | Bin 0 -> 221 bytes .../tzdata/zoneinfo/Pacific/Kanton | Bin 0 -> 172 bytes .../tzdata/zoneinfo/Pacific/Kiritimati | Bin 0 -> 174 bytes .../tzdata/zoneinfo/Pacific/Kosrae | Bin 0 -> 242 bytes .../tzdata/zoneinfo/Pacific/Kwajalein | Bin 0 -> 219 bytes .../tzdata/zoneinfo/Pacific/Majuro | Bin 0 -> 134 bytes .../tzdata/zoneinfo/Pacific/Marquesas | Bin 0 -> 139 bytes .../tzdata/zoneinfo/Pacific/Midway | Bin 0 -> 146 bytes .../tzdata/zoneinfo/Pacific/Nauru | Bin 0 -> 183 bytes .../tzdata/zoneinfo/Pacific/Niue | Bin 0 -> 154 bytes .../tzdata/zoneinfo/Pacific/Norfolk | Bin 0 -> 237 bytes .../tzdata/zoneinfo/Pacific/Noumea | Bin 0 -> 198 bytes .../tzdata/zoneinfo/Pacific/Pago_Pago | Bin 0 -> 146 bytes .../tzdata/zoneinfo/Pacific/Palau | Bin 0 -> 148 bytes .../tzdata/zoneinfo/Pacific/Pitcairn | Bin 0 -> 153 bytes .../tzdata/zoneinfo/Pacific/Pohnpei | Bin 0 -> 134 bytes .../tzdata/zoneinfo/Pacific/Ponape | Bin 0 -> 134 bytes .../tzdata/zoneinfo/Pacific/Port_Moresby | Bin 0 -> 154 bytes .../tzdata/zoneinfo/Pacific/Rarotonga | Bin 0 -> 406 bytes .../tzdata/zoneinfo/Pacific/Saipan | Bin 0 -> 350 bytes .../tzdata/zoneinfo/Pacific/Samoa | Bin 0 -> 146 bytes .../tzdata/zoneinfo/Pacific/Tahiti | Bin 0 -> 133 bytes .../tzdata/zoneinfo/Pacific/Tarawa | Bin 0 -> 134 bytes .../tzdata/zoneinfo/Pacific/Tongatapu | Bin 0 -> 237 bytes .../tzdata/zoneinfo/Pacific/Truk | Bin 0 -> 154 bytes .../tzdata/zoneinfo/Pacific/Wake | Bin 0 -> 134 bytes .../tzdata/zoneinfo/Pacific/Wallis | Bin 0 -> 134 bytes .../site-packages/tzdata/zoneinfo/Pacific/Yap | Bin 0 -> 154 bytes .../tzdata/zoneinfo/Pacific/__init__.py | 0 .../site-packages/tzdata/zoneinfo/Poland | Bin 0 -> 923 bytes .../site-packages/tzdata/zoneinfo/Portugal | Bin 0 -> 1463 bytes .../site-packages/tzdata/zoneinfo/ROC | Bin 0 -> 511 bytes .../site-packages/tzdata/zoneinfo/ROK | Bin 0 -> 415 bytes .../site-packages/tzdata/zoneinfo/Singapore | Bin 0 -> 256 bytes .../site-packages/tzdata/zoneinfo/Turkey | Bin 0 -> 1200 bytes .../site-packages/tzdata/zoneinfo/UCT | Bin 0 -> 111 bytes .../site-packages/tzdata/zoneinfo/US/Alaska | Bin 0 -> 977 bytes .../site-packages/tzdata/zoneinfo/US/Aleutian | Bin 0 -> 969 bytes .../site-packages/tzdata/zoneinfo/US/Arizona | Bin 0 -> 240 bytes .../site-packages/tzdata/zoneinfo/US/Central | Bin 0 -> 1754 bytes .../tzdata/zoneinfo/US/East-Indiana | Bin 0 -> 531 bytes .../site-packages/tzdata/zoneinfo/US/Eastern | Bin 0 -> 1744 bytes .../site-packages/tzdata/zoneinfo/US/Hawaii | Bin 0 -> 221 bytes .../tzdata/zoneinfo/US/Indiana-Starke | Bin 0 -> 1016 bytes .../site-packages/tzdata/zoneinfo/US/Michigan | Bin 0 -> 899 bytes .../site-packages/tzdata/zoneinfo/US/Mountain | Bin 0 -> 1042 bytes .../site-packages/tzdata/zoneinfo/US/Pacific | Bin 0 -> 1294 bytes .../site-packages/tzdata/zoneinfo/US/Samoa | Bin 0 -> 146 bytes .../tzdata/zoneinfo/US/__init__.py | 0 .../site-packages/tzdata/zoneinfo/UTC | Bin 0 -> 111 bytes .../site-packages/tzdata/zoneinfo/Universal | Bin 0 -> 111 bytes .../site-packages/tzdata/zoneinfo/W-SU | Bin 0 -> 908 bytes .../site-packages/tzdata/zoneinfo/WET | Bin 0 -> 1463 bytes .../site-packages/tzdata/zoneinfo/Zulu | Bin 0 -> 111 bytes .../site-packages/tzdata/zoneinfo/__init__.py | 0 .../site-packages/tzdata/zoneinfo/iso3166.tab | 279 + .../site-packages/tzdata/zoneinfo/leapseconds | 86 + .../site-packages/tzdata/zoneinfo/tzdata.zi | 4298 ++++++++ .../site-packages/tzdata/zoneinfo/zone.tab | 448 + .../tzdata/zoneinfo/zone1970.tab | 375 + .../site-packages/tzdata/zoneinfo/zonenow.tab | 296 + .../lib/python3.9/site-packages/tzdata/zones | 598 ++ .../site-packages/urllib3/__init__.py | 159 + .../site-packages/urllib3/_async/__init__.py | 0 .../urllib3/_async/connection.py | 1096 ++ .../urllib3/_async/connectionpool.py | 2454 +++++ .../urllib3/_async/poolmanager.py | 1016 ++ .../site-packages/urllib3/_async/response.py | 539 + .../site-packages/urllib3/_collections.py | 440 + .../site-packages/urllib3/_constant.py | 255 + .../site-packages/urllib3/_request_methods.py | 642 ++ .../site-packages/urllib3/_typing.py | 94 + .../site-packages/urllib3/_version.py | 4 + .../site-packages/urllib3/backend/__init__.py | 21 + .../urllib3/backend/_async/__init__.py | 10 + .../urllib3/backend/_async/_base.py | 330 + .../urllib3/backend/_async/hface.py | 1830 ++++ .../site-packages/urllib3/backend/_base.py | 690 ++ .../site-packages/urllib3/backend/hface.py | 1955 ++++ .../site-packages/urllib3/connection.py | 1130 +++ .../site-packages/urllib3/connectionpool.py | 2409 +++++ .../site-packages/urllib3/contrib/__init__.py | 0 .../urllib3/contrib/_socks_legacy.py | 237 + .../urllib3/contrib/_socks_override.py | 173 + .../urllib3/contrib/emscripten/__init__.py | 27 + .../urllib3/contrib/hface/__init__.py | 39 + .../urllib3/contrib/hface/_configuration.py | 59 + .../urllib3/contrib/hface/_stream_matrix.py | 151 + .../urllib3/contrib/hface/_typing.py | 25 + .../urllib3/contrib/hface/events/__init__.py | 43 + .../urllib3/contrib/hface/events/_events.py | 202 + .../contrib/hface/protocols/__init__.py | 37 + .../contrib/hface/protocols/_factories.py | 90 + .../contrib/hface/protocols/_protocols.py | 358 + .../contrib/hface/protocols/http1/__init__.py | 21 + .../contrib/hface/protocols/http1/_h11.py | 347 + .../contrib/hface/protocols/http2/__init__.py | 21 + .../contrib/hface/protocols/http2/_h2.py | 312 + .../contrib/hface/protocols/http3/__init__.py | 21 + .../contrib/hface/protocols/http3/_qh3.py | 592 ++ .../urllib3/contrib/hface/py.typed | 0 .../urllib3/contrib/imcc/__init__.py | 59 + .../urllib3/contrib/imcc/_ctypes.py | 376 + .../urllib3/contrib/imcc/_shm.py | 122 + .../urllib3/contrib/pyopenssl.py | 28 + .../urllib3/contrib/resolver/__init__.py | 12 + .../contrib/resolver/_async/__init__.py | 11 + .../contrib/resolver/_async/doh/__init__.py | 21 + .../contrib/resolver/_async/doh/_urllib3.py | 656 ++ .../contrib/resolver/_async/doq/__init__.py | 15 + .../contrib/resolver/_async/doq/_qh3.py | 557 ++ .../contrib/resolver/_async/dot/__init__.py | 19 + .../contrib/resolver/_async/dot/_ssl.py | 197 + .../contrib/resolver/_async/dou/__init__.py | 17 + .../contrib/resolver/_async/dou/_socket.py | 431 + .../contrib/resolver/_async/factories.py | 243 + .../resolver/_async/in_memory/__init__.py | 5 + .../resolver/_async/in_memory/_dict.py | 186 + .../contrib/resolver/_async/null/__init__.py | 105 + .../contrib/resolver/_async/protocols.py | 375 + .../resolver/_async/system/__init__.py | 5 + .../contrib/resolver/_async/system/_socket.py | 66 + .../urllib3/contrib/resolver/doh/__init__.py | 21 + .../urllib3/contrib/resolver/doh/_urllib3.py | 641 ++ .../urllib3/contrib/resolver/doq/__init__.py | 15 + .../urllib3/contrib/resolver/doq/_qh3.py | 541 + .../urllib3/contrib/resolver/dot/__init__.py | 19 + .../urllib3/contrib/resolver/dot/_ssl.py | 156 + .../urllib3/contrib/resolver/dou/__init__.py | 17 + .../urllib3/contrib/resolver/dou/_socket.py | 415 + .../urllib3/contrib/resolver/factories.py | 230 + .../contrib/resolver/in_memory/__init__.py | 5 + .../contrib/resolver/in_memory/_dict.py | 192 + .../urllib3/contrib/resolver/null/__init__.py | 104 + .../urllib3/contrib/resolver/protocols.py | 655 ++ .../contrib/resolver/system/__init__.py | 5 + .../contrib/resolver/system/_socket.py | 65 + .../urllib3/contrib/resolver/utils.py | 322 + .../site-packages/urllib3/contrib/socks.py | 453 + .../urllib3/contrib/ssa/__init__.py | 520 + .../site-packages/urllib3/contrib/ssa/_gro.py | 640 ++ .../urllib3/contrib/ssa/_timeout.py | 142 + .../urllib3/contrib/webextensions/__init__.py | 60 + .../contrib/webextensions/_async/__init__.py | 51 + .../contrib/webextensions/_async/protocol.py | 188 + .../contrib/webextensions/_async/raw.py | 57 + .../contrib/webextensions/_async/sse.py | 132 + .../contrib/webextensions/_async/ws.py | 238 + .../urllib3/contrib/webextensions/protocol.py | 189 + .../urllib3/contrib/webextensions/raw.py | 58 + .../urllib3/contrib/webextensions/sse.py | 185 + .../urllib3/contrib/webextensions/ws.py | 247 + .../site-packages/urllib3/exceptions.py | 373 + .../python3.9/site-packages/urllib3/fields.py | 283 + .../site-packages/urllib3/filepost.py | 82 + .../site-packages/urllib3/http2/__init__.py | 23 + .../site-packages/urllib3/poolmanager.py | 1172 +++ .../python3.9/site-packages/urllib3/py.typed | 2 + .../site-packages/urllib3/response.py | 1091 ++ .../site-packages/urllib3/util/__init__.py | 41 + .../urllib3/util/_async/__init__.py | 0 .../site-packages/urllib3/util/_async/ssl_.py | 186 + .../urllib3/util/_async/traffic_police.py | 1217 +++ .../site-packages/urllib3/util/connection.py | 118 + .../site-packages/urllib3/util/proxy.py | 43 + .../site-packages/urllib3/util/request.py | 375 + .../site-packages/urllib3/util/response.py | 183 + .../site-packages/urllib3/util/retry.py | 574 ++ .../urllib3/util/socket_state.py | 243 + .../site-packages/urllib3/util/ssl_.py | 864 ++ .../urllib3/util/ssl_match_hostname.py | 159 + .../urllib3/util/ssltransport.py | 242 + .../site-packages/urllib3/util/timeout.py | 280 + .../urllib3/util/traffic_police.py | 1039 ++ .../site-packages/urllib3/util/url.py | 502 + .../site-packages/urllib3/util/util.py | 57 + .../site-packages/urllib3/util/wait.py | 124 + .../INSTALLER | 1 + .../METADATA | 432 + .../urllib3_future-2.17.902.dist-info/RECORD | 462 + .../urllib3_future-2.17.902.dist-info/WHEEL | 4 + .../licenses/LICENSE.txt | 21 + .../site-packages/urllib3_future.pth | 1 + .../site-packages/urllib3_future/__init__.py | 159 + .../urllib3_future/_async/__init__.py | 0 .../urllib3_future/_async/connection.py | 1096 ++ .../urllib3_future/_async/connectionpool.py | 2454 +++++ .../urllib3_future/_async/poolmanager.py | 1016 ++ .../urllib3_future/_async/response.py | 539 + .../urllib3_future/_collections.py | 440 + .../site-packages/urllib3_future/_constant.py | 255 + .../urllib3_future/_request_methods.py | 642 ++ .../site-packages/urllib3_future/_typing.py | 94 + .../site-packages/urllib3_future/_version.py | 4 + .../urllib3_future/backend/__init__.py | 21 + .../urllib3_future/backend/_async/__init__.py | 10 + .../urllib3_future/backend/_async/_base.py | 330 + .../urllib3_future/backend/_async/hface.py | 1830 ++++ .../urllib3_future/backend/_base.py | 690 ++ .../urllib3_future/backend/hface.py | 1955 ++++ .../urllib3_future/connection.py | 1130 +++ .../urllib3_future/connectionpool.py | 2409 +++++ .../urllib3_future/contrib/__init__.py | 0 .../urllib3_future/contrib/_socks_legacy.py | 237 + .../urllib3_future/contrib/_socks_override.py | 173 + .../contrib/emscripten/__init__.py | 27 + .../urllib3_future/contrib/hface/__init__.py | 39 + .../contrib/hface/_configuration.py | 59 + .../contrib/hface/_stream_matrix.py | 151 + .../urllib3_future/contrib/hface/_typing.py | 25 + .../contrib/hface/events/__init__.py | 43 + .../contrib/hface/events/_events.py | 202 + .../contrib/hface/protocols/__init__.py | 37 + .../contrib/hface/protocols/_factories.py | 90 + .../contrib/hface/protocols/_protocols.py | 358 + .../contrib/hface/protocols/http1/__init__.py | 21 + .../contrib/hface/protocols/http1/_h11.py | 347 + .../contrib/hface/protocols/http2/__init__.py | 21 + .../contrib/hface/protocols/http2/_h2.py | 312 + .../contrib/hface/protocols/http3/__init__.py | 21 + .../contrib/hface/protocols/http3/_qh3.py | 592 ++ .../urllib3_future/contrib/hface/py.typed | 0 .../urllib3_future/contrib/imcc/__init__.py | 59 + .../urllib3_future/contrib/imcc/_ctypes.py | 376 + .../urllib3_future/contrib/imcc/_shm.py | 122 + .../urllib3_future/contrib/pyopenssl.py | 28 + .../contrib/resolver/__init__.py | 12 + .../contrib/resolver/_async/__init__.py | 11 + .../contrib/resolver/_async/doh/__init__.py | 21 + .../contrib/resolver/_async/doh/_urllib3.py | 656 ++ .../contrib/resolver/_async/doq/__init__.py | 15 + .../contrib/resolver/_async/doq/_qh3.py | 557 ++ .../contrib/resolver/_async/dot/__init__.py | 19 + .../contrib/resolver/_async/dot/_ssl.py | 197 + .../contrib/resolver/_async/dou/__init__.py | 17 + .../contrib/resolver/_async/dou/_socket.py | 431 + .../contrib/resolver/_async/factories.py | 243 + .../resolver/_async/in_memory/__init__.py | 5 + .../resolver/_async/in_memory/_dict.py | 186 + .../contrib/resolver/_async/null/__init__.py | 105 + .../contrib/resolver/_async/protocols.py | 375 + .../resolver/_async/system/__init__.py | 5 + .../contrib/resolver/_async/system/_socket.py | 66 + .../contrib/resolver/doh/__init__.py | 21 + .../contrib/resolver/doh/_urllib3.py | 641 ++ .../contrib/resolver/doq/__init__.py | 15 + .../contrib/resolver/doq/_qh3.py | 541 + .../contrib/resolver/dot/__init__.py | 19 + .../contrib/resolver/dot/_ssl.py | 156 + .../contrib/resolver/dou/__init__.py | 17 + .../contrib/resolver/dou/_socket.py | 415 + .../contrib/resolver/factories.py | 230 + .../contrib/resolver/in_memory/__init__.py | 5 + .../contrib/resolver/in_memory/_dict.py | 192 + .../contrib/resolver/null/__init__.py | 104 + .../contrib/resolver/protocols.py | 655 ++ .../contrib/resolver/system/__init__.py | 5 + .../contrib/resolver/system/_socket.py | 65 + .../urllib3_future/contrib/resolver/utils.py | 322 + .../urllib3_future/contrib/socks.py | 453 + .../urllib3_future/contrib/ssa/__init__.py | 520 + .../urllib3_future/contrib/ssa/_gro.py | 640 ++ .../urllib3_future/contrib/ssa/_timeout.py | 142 + .../contrib/webextensions/__init__.py | 60 + .../contrib/webextensions/_async/__init__.py | 51 + .../contrib/webextensions/_async/protocol.py | 188 + .../contrib/webextensions/_async/raw.py | 57 + .../contrib/webextensions/_async/sse.py | 132 + .../contrib/webextensions/_async/ws.py | 238 + .../contrib/webextensions/protocol.py | 189 + .../contrib/webextensions/raw.py | 58 + .../contrib/webextensions/sse.py | 185 + .../contrib/webextensions/ws.py | 247 + .../urllib3_future/exceptions.py | 373 + .../site-packages/urllib3_future/fields.py | 283 + .../site-packages/urllib3_future/filepost.py | 82 + .../urllib3_future/http2/__init__.py | 23 + .../urllib3_future/poolmanager.py | 1172 +++ .../site-packages/urllib3_future/py.typed | 2 + .../site-packages/urllib3_future/response.py | 1091 ++ .../urllib3_future/util/__init__.py | 41 + .../urllib3_future/util/_async/__init__.py | 0 .../urllib3_future/util/_async/ssl_.py | 186 + .../util/_async/traffic_police.py | 1217 +++ .../urllib3_future/util/connection.py | 118 + .../urllib3_future/util/proxy.py | 43 + .../urllib3_future/util/request.py | 375 + .../urllib3_future/util/response.py | 183 + .../urllib3_future/util/retry.py | 574 ++ .../urllib3_future/util/socket_state.py | 243 + .../site-packages/urllib3_future/util/ssl_.py | 864 ++ .../urllib3_future/util/ssl_match_hostname.py | 159 + .../urllib3_future/util/ssltransport.py | 242 + .../urllib3_future/util/timeout.py | 280 + .../urllib3_future/util/traffic_police.py | 1039 ++ .../site-packages/urllib3_future/util/url.py | 502 + .../site-packages/urllib3_future/util/util.py | 57 + .../site-packages/urllib3_future/util/wait.py | 124 + .../wassima-2.0.5.dist-info/INSTALLER | 1 + .../wassima-2.0.5.dist-info/METADATA | 116 + .../wassima-2.0.5.dist-info/RECORD | 20 + .../wassima-2.0.5.dist-info/WHEEL | 4 + .../wassima-2.0.5.dist-info/licenses/LICENSE | 21 + .../site-packages/wassima/__init__.py | 113 + .../site-packages/wassima/_os/__init__.py | 30 + .../site-packages/wassima/_os/_embed.py | 4054 ++++++++ .../site-packages/wassima/_os/_linux.py | 95 + .../site-packages/wassima/_os/_macos.py | 142 + .../site-packages/wassima/_os/_windows.py | 40 + .../site-packages/wassima/_version.py | 4 + .../python3.9/site-packages/wassima/py.typed | 0 .../x_wr_timezone-2.0.1.dist-info/INSTALLER | 1 + .../x_wr_timezone-2.0.1.dist-info/LICENSE | 165 + .../x_wr_timezone-2.0.1.dist-info/METADATA | 345 + .../x_wr_timezone-2.0.1.dist-info/RECORD | 11 + .../x_wr_timezone-2.0.1.dist-info/WHEEL | 5 + .../entry_points.txt | 2 + .../top_level.txt | 1 + .../x_wr_timezone-2.0.1.dist-info/zip-safe | 1 + .../python3.9/site-packages/x_wr_timezone.py | 239 + .../python3.9/site-packages/yaml/__init__.py | 390 + .../yaml/_yaml.cpython-39-darwin.so | Bin 0 -> 382552 bytes .../python3.9/site-packages/yaml/composer.py | 139 + .../site-packages/yaml/constructor.py | 748 ++ .../lib/python3.9/site-packages/yaml/cyaml.py | 101 + .../python3.9/site-packages/yaml/dumper.py | 62 + .../python3.9/site-packages/yaml/emitter.py | 1137 +++ .../lib/python3.9/site-packages/yaml/error.py | 75 + .../python3.9/site-packages/yaml/events.py | 86 + .../python3.9/site-packages/yaml/loader.py | 63 + .../lib/python3.9/site-packages/yaml/nodes.py | 49 + .../python3.9/site-packages/yaml/parser.py | 589 ++ .../python3.9/site-packages/yaml/reader.py | 185 + .../site-packages/yaml/representer.py | 389 + .../python3.9/site-packages/yaml/resolver.py | 227 + .../python3.9/site-packages/yaml/scanner.py | 1435 +++ .../site-packages/yaml/serializer.py | 111 + .../python3.9/site-packages/yaml/tokens.py | 104 + .../yarl-1.22.0.dist-info/INSTALLER | 1 + .../yarl-1.22.0.dist-info/METADATA | 2478 +++++ .../yarl-1.22.0.dist-info/RECORD | 26 + .../site-packages/yarl-1.22.0.dist-info/WHEEL | 6 + .../yarl-1.22.0.dist-info/licenses/LICENSE | 202 + .../yarl-1.22.0.dist-info/licenses/NOTICE | 13 + .../yarl-1.22.0.dist-info/top_level.txt | 1 + .../python3.9/site-packages/yarl/__init__.py | 14 + .../python3.9/site-packages/yarl/_parse.py | 203 + .../lib/python3.9/site-packages/yarl/_path.py | 41 + .../python3.9/site-packages/yarl/_query.py | 121 + .../python3.9/site-packages/yarl/_quoters.py | 33 + .../python3.9/site-packages/yarl/_quoting.py | 19 + .../yarl/_quoting_c.cpython-39-darwin.so | Bin 0 -> 164480 bytes .../site-packages/yarl/_quoting_c.pyx | 451 + .../site-packages/yarl/_quoting_py.py | 213 + .../lib/python3.9/site-packages/yarl/_url.py | 1622 +++ .../lib/python3.9/site-packages/yarl/py.typed | 1 + .venv/pyvenv.cfg | 3 + CLAUDE.md | 8 +- com.syn-chat-bot.intent-service.plist | 2 +- com.syn-chat-bot.note-bridge.plist | 2 +- intent_service.py | 2 +- note_bridge.py | 2 +- 2709 files changed, 619549 insertions(+), 10 deletions(-) create mode 100644 .venv/bin/Activate.ps1 create mode 100644 .venv/bin/activate create mode 100644 .venv/bin/activate.csh create mode 100644 .venv/bin/activate.fish create mode 100755 .venv/bin/icalendar create mode 100755 .venv/bin/normalizer create mode 100755 .venv/bin/pip create mode 100755 .venv/bin/pip3 create mode 100755 .venv/bin/pip3.9 create mode 120000 .venv/bin/python create mode 120000 .venv/bin/python3 create mode 120000 .venv/bin/python3.9 create mode 100755 .venv/bin/x-wr-timezone create mode 100755 .venv/lib/python3.9/site-packages/81d243bd2c585b0f4821__mypyc.cpython-39-darwin.so create mode 100644 .venv/lib/python3.9/site-packages/_distutils_hack/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/_distutils_hack/override.py create mode 100644 .venv/lib/python3.9/site-packages/_yaml/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/aiohappyeyeballs-2.6.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/aiohappyeyeballs-2.6.1.dist-info/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/aiohappyeyeballs-2.6.1.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/aiohappyeyeballs-2.6.1.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/aiohappyeyeballs-2.6.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/aiohappyeyeballs/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/aiohappyeyeballs/_staggered.py create mode 100644 .venv/lib/python3.9/site-packages/aiohappyeyeballs/impl.py create mode 100644 .venv/lib/python3.9/site-packages/aiohappyeyeballs/py.typed create mode 100644 .venv/lib/python3.9/site-packages/aiohappyeyeballs/types.py create mode 100644 .venv/lib/python3.9/site-packages/aiohappyeyeballs/utils.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp-3.13.3.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/aiohttp-3.13.3.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/aiohttp-3.13.3.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/aiohttp-3.13.3.dist-info/REQUESTED create mode 100644 .venv/lib/python3.9/site-packages/aiohttp-3.13.3.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/aiohttp-3.13.3.dist-info/licenses/LICENSE.txt create mode 100644 .venv/lib/python3.9/site-packages/aiohttp-3.13.3.dist-info/licenses/vendor/llhttp/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/aiohttp-3.13.3.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/.hash/_cparser.pxd.hash create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/.hash/_find_header.pxd.hash create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/.hash/_http_parser.pyx.hash create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/.hash/_http_writer.pyx.hash create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/.hash/hdrs.py.hash create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/_cookie_helpers.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/_cparser.pxd create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/_find_header.pxd create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/_headers.pxi create mode 100755 .venv/lib/python3.9/site-packages/aiohttp/_http_parser.cpython-39-darwin.so create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/_http_parser.pyx create mode 100755 .venv/lib/python3.9/site-packages/aiohttp/_http_writer.cpython-39-darwin.so create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/_http_writer.pyx create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/_websocket/.hash/mask.pxd.hash create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/_websocket/.hash/mask.pyx.hash create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/_websocket/.hash/reader_c.pxd.hash create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/_websocket/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/_websocket/helpers.py create mode 100755 .venv/lib/python3.9/site-packages/aiohttp/_websocket/mask.cpython-39-darwin.so create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/_websocket/mask.pxd create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/_websocket/mask.pyx create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/_websocket/models.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/_websocket/reader.py create mode 100755 .venv/lib/python3.9/site-packages/aiohttp/_websocket/reader_c.cpython-39-darwin.so create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/_websocket/reader_c.pxd create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/_websocket/reader_c.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/_websocket/reader_py.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/_websocket/writer.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/abc.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/base_protocol.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/client.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/client_exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/client_middleware_digest_auth.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/client_middlewares.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/client_proto.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/client_reqrep.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/client_ws.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/compression_utils.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/connector.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/cookiejar.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/formdata.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/hdrs.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/helpers.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/http.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/http_exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/http_parser.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/http_websocket.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/http_writer.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/log.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/multipart.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/payload.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/payload_streamer.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/py.typed create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/pytest_plugin.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/resolver.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/streams.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/tcp_helpers.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/test_utils.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/tracing.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/typedefs.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/web.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/web_app.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/web_exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/web_fileresponse.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/web_log.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/web_middlewares.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/web_protocol.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/web_request.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/web_response.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/web_routedef.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/web_runner.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/web_server.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/web_urldispatcher.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/web_ws.py create mode 100644 .venv/lib/python3.9/site-packages/aiohttp/worker.py create mode 100644 .venv/lib/python3.9/site-packages/aiosignal-1.4.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/aiosignal-1.4.0.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/aiosignal-1.4.0.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/aiosignal-1.4.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/aiosignal-1.4.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/aiosignal-1.4.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/aiosignal/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/aiosignal/py.typed create mode 100644 .venv/lib/python3.9/site-packages/async_timeout-5.0.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/async_timeout-5.0.1.dist-info/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/async_timeout-5.0.1.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/async_timeout-5.0.1.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/async_timeout-5.0.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/async_timeout-5.0.1.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/async_timeout-5.0.1.dist-info/zip-safe create mode 100644 .venv/lib/python3.9/site-packages/async_timeout/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/async_timeout/py.typed create mode 100644 .venv/lib/python3.9/site-packages/attr/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/attr/__init__.pyi create mode 100644 .venv/lib/python3.9/site-packages/attr/_cmp.py create mode 100644 .venv/lib/python3.9/site-packages/attr/_cmp.pyi create mode 100644 .venv/lib/python3.9/site-packages/attr/_compat.py create mode 100644 .venv/lib/python3.9/site-packages/attr/_config.py create mode 100644 .venv/lib/python3.9/site-packages/attr/_funcs.py create mode 100644 .venv/lib/python3.9/site-packages/attr/_make.py create mode 100644 .venv/lib/python3.9/site-packages/attr/_next_gen.py create mode 100644 .venv/lib/python3.9/site-packages/attr/_typing_compat.pyi create mode 100644 .venv/lib/python3.9/site-packages/attr/_version_info.py create mode 100644 .venv/lib/python3.9/site-packages/attr/_version_info.pyi create mode 100644 .venv/lib/python3.9/site-packages/attr/converters.py create mode 100644 .venv/lib/python3.9/site-packages/attr/converters.pyi create mode 100644 .venv/lib/python3.9/site-packages/attr/exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/attr/exceptions.pyi create mode 100644 .venv/lib/python3.9/site-packages/attr/filters.py create mode 100644 .venv/lib/python3.9/site-packages/attr/filters.pyi create mode 100644 .venv/lib/python3.9/site-packages/attr/py.typed create mode 100644 .venv/lib/python3.9/site-packages/attr/setters.py create mode 100644 .venv/lib/python3.9/site-packages/attr/setters.pyi create mode 100644 .venv/lib/python3.9/site-packages/attr/validators.py create mode 100644 .venv/lib/python3.9/site-packages/attr/validators.pyi create mode 100644 .venv/lib/python3.9/site-packages/attrs-25.4.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/attrs-25.4.0.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/attrs-25.4.0.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/attrs-25.4.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/attrs-25.4.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/attrs/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/attrs/__init__.pyi create mode 100644 .venv/lib/python3.9/site-packages/attrs/converters.py create mode 100644 .venv/lib/python3.9/site-packages/attrs/exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/attrs/filters.py create mode 100644 .venv/lib/python3.9/site-packages/attrs/py.typed create mode 100644 .venv/lib/python3.9/site-packages/attrs/setters.py create mode 100644 .venv/lib/python3.9/site-packages/attrs/validators.py create mode 100644 .venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/REQUESTED create mode 100644 .venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/licenses/COPYING.APACHE create mode 100644 .venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/licenses/COPYING.GPL create mode 100644 .venv/lib/python3.9/site-packages/caldav/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/_version.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/calendarobjectresource.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/collection.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/compatibility_hints.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/config.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/davclient.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/davobject.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/discovery.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/elements/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/elements/base.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/elements/cdav.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/elements/dav.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/elements/ical.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/lib/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/lib/debug.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/lib/error.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/lib/namespace.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/lib/python_utilities.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/lib/url.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/lib/vcal.py create mode 100755 .venv/lib/python3.9/site-packages/caldav/objects.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/py.typed create mode 100644 .venv/lib/python3.9/site-packages/caldav/requests.py create mode 100644 .venv/lib/python3.9/site-packages/caldav/search.py create mode 100644 .venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/charset_normalizer/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/charset_normalizer/__main__.py create mode 100644 .venv/lib/python3.9/site-packages/charset_normalizer/api.py create mode 100755 .venv/lib/python3.9/site-packages/charset_normalizer/cd.cpython-39-darwin.so create mode 100644 .venv/lib/python3.9/site-packages/charset_normalizer/cd.py create mode 100644 .venv/lib/python3.9/site-packages/charset_normalizer/cli/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/charset_normalizer/cli/__main__.py create mode 100644 .venv/lib/python3.9/site-packages/charset_normalizer/constant.py create mode 100644 .venv/lib/python3.9/site-packages/charset_normalizer/legacy.py create mode 100755 .venv/lib/python3.9/site-packages/charset_normalizer/md.cpython-39-darwin.so create mode 100644 .venv/lib/python3.9/site-packages/charset_normalizer/md.py create mode 100644 .venv/lib/python3.9/site-packages/charset_normalizer/models.py create mode 100644 .venv/lib/python3.9/site-packages/charset_normalizer/py.typed create mode 100644 .venv/lib/python3.9/site-packages/charset_normalizer/utils.py create mode 100644 .venv/lib/python3.9/site-packages/charset_normalizer/version.py create mode 100644 .venv/lib/python3.9/site-packages/click-8.1.8.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/click-8.1.8.dist-info/LICENSE.txt create mode 100644 .venv/lib/python3.9/site-packages/click-8.1.8.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/click-8.1.8.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/click-8.1.8.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/click/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/click/_compat.py create mode 100644 .venv/lib/python3.9/site-packages/click/_termui_impl.py create mode 100644 .venv/lib/python3.9/site-packages/click/_textwrap.py create mode 100644 .venv/lib/python3.9/site-packages/click/_winconsole.py create mode 100644 .venv/lib/python3.9/site-packages/click/core.py create mode 100644 .venv/lib/python3.9/site-packages/click/decorators.py create mode 100644 .venv/lib/python3.9/site-packages/click/exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/click/formatting.py create mode 100644 .venv/lib/python3.9/site-packages/click/globals.py create mode 100644 .venv/lib/python3.9/site-packages/click/parser.py create mode 100644 .venv/lib/python3.9/site-packages/click/py.typed create mode 100644 .venv/lib/python3.9/site-packages/click/shell_completion.py create mode 100644 .venv/lib/python3.9/site-packages/click/termui.py create mode 100644 .venv/lib/python3.9/site-packages/click/testing.py create mode 100644 .venv/lib/python3.9/site-packages/click/types.py create mode 100644 .venv/lib/python3.9/site-packages/click/utils.py create mode 100644 .venv/lib/python3.9/site-packages/dateutil/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/dateutil/_common.py create mode 100644 .venv/lib/python3.9/site-packages/dateutil/_version.py create mode 100644 .venv/lib/python3.9/site-packages/dateutil/easter.py create mode 100644 .venv/lib/python3.9/site-packages/dateutil/parser/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/dateutil/parser/_parser.py create mode 100644 .venv/lib/python3.9/site-packages/dateutil/parser/isoparser.py create mode 100644 .venv/lib/python3.9/site-packages/dateutil/relativedelta.py create mode 100644 .venv/lib/python3.9/site-packages/dateutil/rrule.py create mode 100644 .venv/lib/python3.9/site-packages/dateutil/tz/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/dateutil/tz/_common.py create mode 100644 .venv/lib/python3.9/site-packages/dateutil/tz/_factories.py create mode 100644 .venv/lib/python3.9/site-packages/dateutil/tz/tz.py create mode 100644 .venv/lib/python3.9/site-packages/dateutil/tz/win.py create mode 100644 .venv/lib/python3.9/site-packages/dateutil/tzwin.py create mode 100644 .venv/lib/python3.9/site-packages/dateutil/utils.py create mode 100644 .venv/lib/python3.9/site-packages/dateutil/zoneinfo/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz create mode 100644 .venv/lib/python3.9/site-packages/dateutil/zoneinfo/rebuild.py create mode 100644 .venv/lib/python3.9/site-packages/distutils-precedence.pth create mode 100644 .venv/lib/python3.9/site-packages/dns/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/dns/_asyncbackend.py create mode 100644 .venv/lib/python3.9/site-packages/dns/_asyncio_backend.py create mode 100644 .venv/lib/python3.9/site-packages/dns/_ddr.py create mode 100644 .venv/lib/python3.9/site-packages/dns/_features.py create mode 100644 .venv/lib/python3.9/site-packages/dns/_immutable_ctx.py create mode 100644 .venv/lib/python3.9/site-packages/dns/_trio_backend.py create mode 100644 .venv/lib/python3.9/site-packages/dns/asyncbackend.py create mode 100644 .venv/lib/python3.9/site-packages/dns/asyncquery.py create mode 100644 .venv/lib/python3.9/site-packages/dns/asyncresolver.py create mode 100644 .venv/lib/python3.9/site-packages/dns/dnssec.py create mode 100644 .venv/lib/python3.9/site-packages/dns/dnssecalgs/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/dns/dnssecalgs/base.py create mode 100644 .venv/lib/python3.9/site-packages/dns/dnssecalgs/cryptography.py create mode 100644 .venv/lib/python3.9/site-packages/dns/dnssecalgs/dsa.py create mode 100644 .venv/lib/python3.9/site-packages/dns/dnssecalgs/ecdsa.py create mode 100644 .venv/lib/python3.9/site-packages/dns/dnssecalgs/eddsa.py create mode 100644 .venv/lib/python3.9/site-packages/dns/dnssecalgs/rsa.py create mode 100644 .venv/lib/python3.9/site-packages/dns/dnssectypes.py create mode 100644 .venv/lib/python3.9/site-packages/dns/e164.py create mode 100644 .venv/lib/python3.9/site-packages/dns/edns.py create mode 100644 .venv/lib/python3.9/site-packages/dns/entropy.py create mode 100644 .venv/lib/python3.9/site-packages/dns/enum.py create mode 100644 .venv/lib/python3.9/site-packages/dns/exception.py create mode 100644 .venv/lib/python3.9/site-packages/dns/flags.py create mode 100644 .venv/lib/python3.9/site-packages/dns/grange.py create mode 100644 .venv/lib/python3.9/site-packages/dns/immutable.py create mode 100644 .venv/lib/python3.9/site-packages/dns/inet.py create mode 100644 .venv/lib/python3.9/site-packages/dns/ipv4.py create mode 100644 .venv/lib/python3.9/site-packages/dns/ipv6.py create mode 100644 .venv/lib/python3.9/site-packages/dns/message.py create mode 100644 .venv/lib/python3.9/site-packages/dns/name.py create mode 100644 .venv/lib/python3.9/site-packages/dns/namedict.py create mode 100644 .venv/lib/python3.9/site-packages/dns/nameserver.py create mode 100644 .venv/lib/python3.9/site-packages/dns/node.py create mode 100644 .venv/lib/python3.9/site-packages/dns/opcode.py create mode 100644 .venv/lib/python3.9/site-packages/dns/py.typed create mode 100644 .venv/lib/python3.9/site-packages/dns/query.py create mode 100644 .venv/lib/python3.9/site-packages/dns/quic/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/dns/quic/_asyncio.py create mode 100644 .venv/lib/python3.9/site-packages/dns/quic/_common.py create mode 100644 .venv/lib/python3.9/site-packages/dns/quic/_sync.py create mode 100644 .venv/lib/python3.9/site-packages/dns/quic/_trio.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rcode.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdata.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdataclass.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdataset.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdatatype.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/AFSDB.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/AMTRELAY.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/AVC.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CAA.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CDNSKEY.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CDS.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CERT.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CNAME.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CSYNC.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/DLV.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/DNAME.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/DNSKEY.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/DS.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/EUI48.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/EUI64.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/GPOS.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/HINFO.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/HIP.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/ISDN.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/L32.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/L64.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/LOC.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/LP.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/MX.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NID.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NINFO.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NS.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NSEC.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NSEC3.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NSEC3PARAM.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/OPENPGPKEY.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/OPT.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/PTR.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/RESINFO.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/RP.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/RRSIG.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/RT.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/SMIMEA.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/SOA.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/SPF.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/SSHFP.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/TKEY.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/TLSA.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/TSIG.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/TXT.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/URI.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/WALLET.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/X25.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/ZONEMD.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/ANY/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/CH/A.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/CH/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/IN/A.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/IN/AAAA.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/IN/APL.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/IN/DHCID.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/IN/HTTPS.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/IN/IPSECKEY.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/IN/KX.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/IN/NAPTR.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/IN/NSAP.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/IN/NSAP_PTR.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/IN/PX.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/IN/SRV.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/IN/SVCB.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/IN/WKS.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/IN/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/dnskeybase.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/dsbase.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/euibase.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/mxbase.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/nsbase.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/svcbbase.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/tlsabase.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/txtbase.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rdtypes/util.py create mode 100644 .venv/lib/python3.9/site-packages/dns/renderer.py create mode 100644 .venv/lib/python3.9/site-packages/dns/resolver.py create mode 100644 .venv/lib/python3.9/site-packages/dns/reversename.py create mode 100644 .venv/lib/python3.9/site-packages/dns/rrset.py create mode 100644 .venv/lib/python3.9/site-packages/dns/serial.py create mode 100644 .venv/lib/python3.9/site-packages/dns/set.py create mode 100644 .venv/lib/python3.9/site-packages/dns/tokenizer.py create mode 100644 .venv/lib/python3.9/site-packages/dns/transaction.py create mode 100644 .venv/lib/python3.9/site-packages/dns/tsig.py create mode 100644 .venv/lib/python3.9/site-packages/dns/tsigkeyring.py create mode 100644 .venv/lib/python3.9/site-packages/dns/ttl.py create mode 100644 .venv/lib/python3.9/site-packages/dns/update.py create mode 100644 .venv/lib/python3.9/site-packages/dns/version.py create mode 100644 .venv/lib/python3.9/site-packages/dns/versioned.py create mode 100644 .venv/lib/python3.9/site-packages/dns/win32util.py create mode 100644 .venv/lib/python3.9/site-packages/dns/wire.py create mode 100644 .venv/lib/python3.9/site-packages/dns/xfr.py create mode 100644 .venv/lib/python3.9/site-packages/dns/zone.py create mode 100644 .venv/lib/python3.9/site-packages/dns/zonefile.py create mode 100644 .venv/lib/python3.9/site-packages/dns/zonetypes.py create mode 100644 .venv/lib/python3.9/site-packages/dnspython-2.7.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/dnspython-2.7.0.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/dnspython-2.7.0.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/dnspython-2.7.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/dnspython-2.7.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/frozenlist/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/frozenlist/__init__.pyi create mode 100755 .venv/lib/python3.9/site-packages/frozenlist/_frozenlist.cpython-39-darwin.so create mode 100644 .venv/lib/python3.9/site-packages/frozenlist/_frozenlist.pyx create mode 100644 .venv/lib/python3.9/site-packages/frozenlist/py.typed create mode 100644 .venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/licenses/LICENSE.txt create mode 100644 .venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/h11/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/h11/_abnf.py create mode 100644 .venv/lib/python3.9/site-packages/h11/_connection.py create mode 100644 .venv/lib/python3.9/site-packages/h11/_events.py create mode 100644 .venv/lib/python3.9/site-packages/h11/_headers.py create mode 100644 .venv/lib/python3.9/site-packages/h11/_readers.py create mode 100644 .venv/lib/python3.9/site-packages/h11/_receivebuffer.py create mode 100644 .venv/lib/python3.9/site-packages/h11/_state.py create mode 100644 .venv/lib/python3.9/site-packages/h11/_util.py create mode 100644 .venv/lib/python3.9/site-packages/h11/_version.py create mode 100644 .venv/lib/python3.9/site-packages/h11/_writers.py create mode 100644 .venv/lib/python3.9/site-packages/h11/py.typed create mode 100644 .venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/licenses/LICENSE.rst create mode 100644 .venv/lib/python3.9/site-packages/icalendar/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/_version.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/alarms.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/attr.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/cal.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/caselessdict.py create mode 100755 .venv/lib/python3.9/site-packages/icalendar/cli.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/enums.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/error.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/param.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/parser.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/parser_tools.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/prop.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/alarms/rfc_5545_absolute_alarm_example.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/alarms/rfc_5545_end.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/alarms/start_date.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/attr/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/attr/test_alarm.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/attr/test_component.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/attr/test_exdates.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/attr/test_rdate.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/attr/test_rrule.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_etar_future.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_etar_notification.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_etar_notification_clicked.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_google_acknowledged.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_google_future.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_2_future.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_2_notification_5_min_postponed.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_2_notification_5_min_postponed_and_closed.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_2_notification_5_min_postponed_and_popped_up.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_2_notification_popped_up.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_closed.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_future.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_snoozed_until_1457.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/america_new_york.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/america_new_york_forward_reference.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/big_bad_calendar.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/bom_calendar.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/broken_ical.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/calendar_with_unicode.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/created_calendar_with_unicode_fields.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/example.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_104_broken_calendar.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_156_RDATE_with_PERIOD_TZID_khal.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_156_RDATE_with_PERIOD_TZID_khal_2.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_165_missing_event.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_168_expected_output.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_168_input.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_178_component_with_invalid_name_represented.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_178_custom_component_contains_other.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_178_custom_component_inside_other.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_218_bad_tzid.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_237_fail_to_parse_timezone_with_non_ascii_tzid.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_27_multiple_periods_in_freebusy_multiple_freebusies.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_27_multiple_periods_in_freebusy_one_freebusy.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_322_expected_calendar.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_348_exception_parsing_value.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_350.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_466_convert_tzid_with_slash.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_466_respect_unique_timezone.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_526_calendar_with_different_events.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_526_calendar_with_event_subset.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_526_calendar_with_events.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_526_calendar_with_shuffeled_events.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_722_missing_VTIMEZONE_custom.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_722_missing_timezones.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_722_timezone_transition_ambiguity.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_798_freebusy.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_798_related_to.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_836_do_not_quote_tzid.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/multiple_calendar_components.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/pacific_fiji.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/parsing_error.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/parsing_error_in_UTC_offset.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/period_with_timezone.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/pr_480_summary_with_colon.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/property_params.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/rfc_5545_RDATE_example.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/rfc_6868.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/rfc_7529.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/small_bad_calendar.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/time.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/timezone_rdate.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/timezone_same_start.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/timezone_same_start_and_offset.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/timezoned.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/calendars/x_location.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/conftest.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_escaped_character1.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_escaped_character2.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_escaped_character3.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_escaped_character4.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_escaped_characters.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_recurrence.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_recurrence_exdates_on_different_lines.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_rsvp.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_unicode_fields.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_unicode_organizer.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/issue_100_transformed_doctests_into_unittests.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/issue_101_icalendar_chokes_on_umlauts_in_organizer.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/issue_104_mark_events_broken.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/issue_112_missing_tzinfo_on_exdate.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/issue_156_RDATE_with_PERIOD.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/issue_156_RDATE_with_PERIOD_list.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/issue_157_removes_trailing_semicolon.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/issue_184_broken_representation_of_period.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/issue_464_invalid_rdate.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/issue_53_description_parsed_properly.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/issue_64_event_with_ascii_summary.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/issue_64_event_with_non_ascii_summary.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/issue_70_rrule_causes_attribute_error.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/issue_82_expected_output.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/rfc_9074_example_1.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/rfc_9074_example_2.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/rfc_9074_example_3.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/rfc_9074_example_4.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/events/rfc_9074_example_proximity.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/fuzzed/__init__.py create mode 100755 .venv/lib/python3.9/site-packages/icalendar/tests/fuzzed/generate_python_test_cases_from_downloaded_clusterfuzz_test_cases.sh create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/fuzzed/test_fuzzed_calendars.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/hypothesis/test_fuzzing.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/prop/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/prop/test_constructors.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/prop/test_identity_and_equality.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/prop/test_property_values.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/prop/test_unit.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vBinary.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vBoolean.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vCalAddress.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vDDDTypes.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vDatetime.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vPeriod.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vWeekday.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/prop/test_windows_to_olson_mapping.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_bom_calendar.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_cli_tool.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_components_break_on_bad_ics.py create mode 100755 .venv/lib/python3.9/site-packages/icalendar/tests/test_create_release.sh create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_encoding.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_equality.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_examples.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_icalendar.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_issue_116.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_issue_165_missing_event.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_issue_168_parsing_invalid_calendars_no_warning.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_issue_218_parse_calendar.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_issue_27_period.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_issue_301_add_rrule_as_string.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_issue_318_skip_default_parameters.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_issue_322_single_strings_characters_split_into_multiple_categories.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_issue_336_dateutil_timezone.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_issue_348_exception_parsing_value.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_issue_350.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_issue_500_vboolean_for_parameter.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_issue_557_encode_native_parameters.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_issue_662_component_properties.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_issue_716_alarm_time_computation.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_issue_720_uid_property.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_issue_722_generate_vtimezone.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_issue_798_property_parameters.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_issue_802.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_issue_828.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_issue_836_do_not_quote_tzid.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_multiple.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_oss_fuzz_errors.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_parsing.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_period.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_property_params.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_pytz_zoneinfo_integration.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_recurrence.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_6868.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_7529.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_7986.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_7986_categories.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_9074.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_time.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_timezone_identification.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_timezoned.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_unit_cal.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_unit_caselessdict.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_unit_parser_tools.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_unit_tools.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/test_with_doctest.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/timezone_ids.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/timezones/issue_237_brazilia_standard.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/timezones/issue_53_tzid_parsed_properly.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/timezones/issue_55_parse_error_on_utc_offset_with_seconds.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tests/timezones/pacific_fiji.ics create mode 100644 .venv/lib/python3.9/site-packages/icalendar/timezone/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/timezone/equivalent_timezone_ids.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/timezone/equivalent_timezone_ids_result.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/timezone/provider.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/timezone/pytz.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/timezone/tzid.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/timezone/tzp.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/timezone/windows_to_olson.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/timezone/zoneinfo.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/tools.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar/version.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar_searcher-1.0.3.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/icalendar_searcher-1.0.3.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/icalendar_searcher-1.0.3.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/icalendar_searcher-1.0.3.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/icalendar_searcher-1.0.3.dist-info/licenses/LICENSE.md create mode 100644 .venv/lib/python3.9/site-packages/icalendar_searcher/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar_searcher/collation.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar_searcher/filters.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar_searcher/searcher.py create mode 100644 .venv/lib/python3.9/site-packages/icalendar_searcher/utils.py create mode 100644 .venv/lib/python3.9/site-packages/idna-3.11.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/idna-3.11.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/idna-3.11.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/idna-3.11.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/idna-3.11.dist-info/licenses/LICENSE.md create mode 100644 .venv/lib/python3.9/site-packages/idna/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/idna/codec.py create mode 100644 .venv/lib/python3.9/site-packages/idna/compat.py create mode 100644 .venv/lib/python3.9/site-packages/idna/core.py create mode 100644 .venv/lib/python3.9/site-packages/idna/idnadata.py create mode 100644 .venv/lib/python3.9/site-packages/idna/intranges.py create mode 100644 .venv/lib/python3.9/site-packages/idna/package_data.py create mode 100644 .venv/lib/python3.9/site-packages/idna/py.typed create mode 100644 .venv/lib/python3.9/site-packages/idna/uts46data.py create mode 100644 .venv/lib/python3.9/site-packages/jh2-5.0.10.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/jh2-5.0.10.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/jh2-5.0.10.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/jh2-5.0.10.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/jh2-5.0.10.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/jh2/__init__.py create mode 100755 .venv/lib/python3.9/site-packages/jh2/_hazmat.abi3.so create mode 100644 .venv/lib/python3.9/site-packages/jh2/_hazmat.pyi create mode 100644 .venv/lib/python3.9/site-packages/jh2/config.py create mode 100644 .venv/lib/python3.9/site-packages/jh2/connection.py create mode 100644 .venv/lib/python3.9/site-packages/jh2/errors.py create mode 100644 .venv/lib/python3.9/site-packages/jh2/events.py create mode 100644 .venv/lib/python3.9/site-packages/jh2/exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/jh2/frame_buffer.py create mode 100644 .venv/lib/python3.9/site-packages/jh2/hpack/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/jh2/hpack/exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/jh2/hpack/hpack.py create mode 100644 .venv/lib/python3.9/site-packages/jh2/hpack/huffman.py create mode 100644 .venv/lib/python3.9/site-packages/jh2/hpack/huffman_constants.py create mode 100644 .venv/lib/python3.9/site-packages/jh2/hpack/huffman_table.py create mode 100644 .venv/lib/python3.9/site-packages/jh2/hpack/struct.py create mode 100644 .venv/lib/python3.9/site-packages/jh2/hpack/table.py create mode 100644 .venv/lib/python3.9/site-packages/jh2/hyperframe/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/jh2/hyperframe/exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/jh2/hyperframe/flags.py create mode 100644 .venv/lib/python3.9/site-packages/jh2/hyperframe/frame.py create mode 100644 .venv/lib/python3.9/site-packages/jh2/hyperframe/py.typed create mode 100644 .venv/lib/python3.9/site-packages/jh2/settings.py create mode 100644 .venv/lib/python3.9/site-packages/jh2/stream.py create mode 100644 .venv/lib/python3.9/site-packages/jh2/utilities.py create mode 100644 .venv/lib/python3.9/site-packages/jh2/windows.py create mode 100644 .venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/licenses/LICENSE.txt create mode 100644 .venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/licenses/LICENSES.txt create mode 100644 .venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/lxml/ElementInclude.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/__init__.py create mode 100755 .venv/lib/python3.9/site-packages/lxml/_elementpath.cpython-39-darwin.so create mode 100644 .venv/lib/python3.9/site-packages/lxml/_elementpath.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/apihelpers.pxi create mode 100755 .venv/lib/python3.9/site-packages/lxml/builder.cpython-39-darwin.so create mode 100644 .venv/lib/python3.9/site-packages/lxml/builder.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/classlookup.pxi create mode 100644 .venv/lib/python3.9/site-packages/lxml/cleanup.pxi create mode 100644 .venv/lib/python3.9/site-packages/lxml/cssselect.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/debug.pxi create mode 100644 .venv/lib/python3.9/site-packages/lxml/docloader.pxi create mode 100644 .venv/lib/python3.9/site-packages/lxml/doctestcompare.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/dtd.pxi create mode 100755 .venv/lib/python3.9/site-packages/lxml/etree.cpython-39-darwin.so create mode 100644 .venv/lib/python3.9/site-packages/lxml/etree.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/etree.pyx create mode 100644 .venv/lib/python3.9/site-packages/lxml/etree_api.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/extensions.pxi create mode 100644 .venv/lib/python3.9/site-packages/lxml/html/ElementSoup.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/html/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/html/_diffcommand.py create mode 100755 .venv/lib/python3.9/site-packages/lxml/html/_difflib.cpython-39-darwin.so create mode 100644 .venv/lib/python3.9/site-packages/lxml/html/_difflib.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/html/_html5builder.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/html/_setmixin.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/html/builder.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/html/clean.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/html/defs.py create mode 100755 .venv/lib/python3.9/site-packages/lxml/html/diff.cpython-39-darwin.so create mode 100644 .venv/lib/python3.9/site-packages/lxml/html/diff.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/html/formfill.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/html/html5parser.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/html/soupparser.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/html/usedoctest.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/__init__.pxd create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/c14n.pxd create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/config.pxd create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/dtdvalid.pxd create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/etree_defs.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/etreepublic.pxd create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/extlibs/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/extlibs/libcharset.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/extlibs/localcharset.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/extlibs/zconf.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/extlibs/zlib.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/htmlparser.pxd create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libexslt/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libexslt/exslt.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libexslt/exsltconfig.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libexslt/exsltexports.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/HTMLparser.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/HTMLtree.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/SAX.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/SAX2.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/c14n.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/catalog.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/chvalid.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/debugXML.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/dict.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/encoding.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/entities.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/globals.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/hash.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/list.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/nanoftp.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/nanohttp.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/parser.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/parserInternals.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/relaxng.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/schemasInternals.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/schematron.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/threads.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/tree.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/uri.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/valid.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/xinclude.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/xlink.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/xmlIO.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/xmlautomata.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/xmlerror.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/xmlexports.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/xmlmemory.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/xmlmodule.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/xmlreader.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/xmlregexp.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/xmlsave.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/xmlschemas.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/xmlschemastypes.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/xmlstring.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/xmlunicode.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/xmlversion.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/xmlwriter.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/xpath.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/xpathInternals.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxml/xpointer.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/attributes.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/documents.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/extensions.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/extra.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/functions.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/imports.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/keys.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/namespaces.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/numbersInternals.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/pattern.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/preproc.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/security.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/templates.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/transform.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/variables.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/xslt.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/xsltInternals.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/xsltconfig.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/xsltexports.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/xsltlocale.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/libxslt/xsltutils.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/lxml-version.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/relaxng.pxd create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/schematron.pxd create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/tree.pxd create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/uri.pxd create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/xinclude.pxd create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/xmlerror.pxd create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/xmlparser.pxd create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/xmlschema.pxd create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/xpath.pxd create mode 100644 .venv/lib/python3.9/site-packages/lxml/includes/xslt.pxd create mode 100644 .venv/lib/python3.9/site-packages/lxml/isoschematron/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/isoschematron/resources/rng/iso-schematron.rng create mode 100644 .venv/lib/python3.9/site-packages/lxml/isoschematron/resources/xsl/RNG2Schtrn.xsl create mode 100644 .venv/lib/python3.9/site-packages/lxml/isoschematron/resources/xsl/XSD2Schtrn.xsl create mode 100644 .venv/lib/python3.9/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_abstract_expand.xsl create mode 100644 .venv/lib/python3.9/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_dsdl_include.xsl create mode 100644 .venv/lib/python3.9/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_message.xsl create mode 100644 .venv/lib/python3.9/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_skeleton_for_xslt1.xsl create mode 100644 .venv/lib/python3.9/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_svrl_for_xslt1.xsl create mode 100644 .venv/lib/python3.9/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/readme.txt create mode 100644 .venv/lib/python3.9/site-packages/lxml/iterparse.pxi create mode 100644 .venv/lib/python3.9/site-packages/lxml/lxml.etree.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/lxml.etree_api.h create mode 100644 .venv/lib/python3.9/site-packages/lxml/nsclasses.pxi create mode 100755 .venv/lib/python3.9/site-packages/lxml/objectify.cpython-39-darwin.so create mode 100644 .venv/lib/python3.9/site-packages/lxml/objectify.pyx create mode 100644 .venv/lib/python3.9/site-packages/lxml/objectpath.pxi create mode 100644 .venv/lib/python3.9/site-packages/lxml/parser.pxi create mode 100644 .venv/lib/python3.9/site-packages/lxml/parsertarget.pxi create mode 100644 .venv/lib/python3.9/site-packages/lxml/proxy.pxi create mode 100644 .venv/lib/python3.9/site-packages/lxml/public-api.pxi create mode 100644 .venv/lib/python3.9/site-packages/lxml/pyclasslookup.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/readonlytree.pxi create mode 100644 .venv/lib/python3.9/site-packages/lxml/relaxng.pxi create mode 100755 .venv/lib/python3.9/site-packages/lxml/sax.cpython-39-darwin.so create mode 100644 .venv/lib/python3.9/site-packages/lxml/sax.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/saxparser.pxi create mode 100644 .venv/lib/python3.9/site-packages/lxml/schematron.pxi create mode 100644 .venv/lib/python3.9/site-packages/lxml/serializer.pxi create mode 100644 .venv/lib/python3.9/site-packages/lxml/usedoctest.py create mode 100644 .venv/lib/python3.9/site-packages/lxml/xinclude.pxi create mode 100644 .venv/lib/python3.9/site-packages/lxml/xmlerror.pxi create mode 100644 .venv/lib/python3.9/site-packages/lxml/xmlid.pxi create mode 100644 .venv/lib/python3.9/site-packages/lxml/xmlschema.pxi create mode 100644 .venv/lib/python3.9/site-packages/lxml/xpath.pxi create mode 100644 .venv/lib/python3.9/site-packages/lxml/xslt.pxi create mode 100644 .venv/lib/python3.9/site-packages/lxml/xsltext.pxi create mode 100644 .venv/lib/python3.9/site-packages/multidict-6.7.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/multidict-6.7.1.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/multidict-6.7.1.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/multidict-6.7.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/multidict-6.7.1.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/multidict-6.7.1.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/multidict/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/multidict/_abc.py create mode 100644 .venv/lib/python3.9/site-packages/multidict/_compat.py create mode 100755 .venv/lib/python3.9/site-packages/multidict/_multidict.cpython-39-darwin.so create mode 100644 .venv/lib/python3.9/site-packages/multidict/_multidict_py.py create mode 100644 .venv/lib/python3.9/site-packages/multidict/py.typed create mode 100644 .venv/lib/python3.9/site-packages/niquests-3.18.2.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/niquests-3.18.2.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/niquests-3.18.2.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/niquests-3.18.2.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/niquests-3.18.2.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/niquests/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/__version__.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/_async.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/_compat.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/_constant.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/_typing.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/_vendor/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/_vendor/kiss_headers/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/niquests/_vendor/kiss_headers/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/_vendor/kiss_headers/api.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/_vendor/kiss_headers/builder.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/_vendor/kiss_headers/models.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/_vendor/kiss_headers/py.typed create mode 100644 .venv/lib/python3.9/site-packages/niquests/_vendor/kiss_headers/serializer.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/_vendor/kiss_headers/structures.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/_vendor/kiss_headers/utils.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/_vendor/kiss_headers/version.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/adapters.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/api.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/async_api.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/async_session.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/auth.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/cookies.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/extensions/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/extensions/pyodide/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/extensions/pyodide/_async/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/extensions/pyodide/_async/_sse.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/extensions/pyodide/_async/_ws.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/extensions/pyodide/_sse.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/extensions/pyodide/_ws.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/extensions/revocation/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/extensions/revocation/_crl/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/extensions/revocation/_crl/_async/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/extensions/revocation/_ocsp/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/extensions/revocation/_ocsp/_async/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/extensions/sgi/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/extensions/sgi/_async/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/extensions/sgi/_async/_sse.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/extensions/sgi/_async/_ws.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/extensions/sgi/_sse.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/extensions/sgi/_ws.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/extensions/unixsocket/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/extensions/unixsocket/_async/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/help.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/hooks.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/models.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/packages.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/py.typed create mode 100644 .venv/lib/python3.9/site-packages/niquests/sessions.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/status_codes.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/structures.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/typing.py create mode 100644 .venv/lib/python3.9/site-packages/niquests/utils.py create mode 100644 .venv/lib/python3.9/site-packages/pip-21.2.4.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/pip-21.2.4.dist-info/LICENSE.txt create mode 100644 .venv/lib/python3.9/site-packages/pip-21.2.4.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/pip-21.2.4.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/pip-21.2.4.dist-info/REQUESTED create mode 100644 .venv/lib/python3.9/site-packages/pip-21.2.4.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/pip-21.2.4.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.9/site-packages/pip-21.2.4.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/pip/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/__main__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/build_env.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cache.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/autocompletion.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/base_command.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/cmdoptions.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/command_context.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/main.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/main_parser.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/parser.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/progress_bars.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/req_command.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/spinners.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/cli/status_codes.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/cache.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/check.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/completion.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/configuration.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/debug.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/download.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/freeze.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/hash.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/help.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/index.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/install.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/list.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/search.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/show.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/uninstall.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/commands/wheel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/configuration.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/distributions/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/distributions/base.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/distributions/installed.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/distributions/sdist.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/distributions/wheel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/index/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/index/collector.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/index/package_finder.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/index/sources.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/locations/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/locations/_distutils.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/locations/_sysconfig.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/locations/base.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/main.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/metadata/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/metadata/base.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/metadata/pkg_resources.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/candidate.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/direct_url.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/format_control.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/index.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/link.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/scheme.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/search_scope.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/selection_prefs.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/target_python.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/models/wheel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/auth.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/cache.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/download.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/lazy_wheel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/session.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/utils.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/network/xmlrpc.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/build/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/build/metadata.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/build/metadata_legacy.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/build/wheel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/build/wheel_legacy.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/check.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/freeze.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/install/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/install/editable_legacy.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/install/legacy.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/install/wheel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/operations/prepare.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/pyproject.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/req/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/req/constructors.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/req/req_file.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/req/req_install.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/req/req_set.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/req/req_tracker.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/req/req_uninstall.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/base.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/legacy/resolver.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/base.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/candidates.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/factory.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/provider.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/reporter.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/requirements.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/resolver.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/self_outdated_check.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/_log.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/appdirs.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/compat.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/compatibility_tags.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/datetime.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/deprecation.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/direct_url_helpers.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/distutils_args.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/encoding.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/entrypoints.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/filesystem.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/filetypes.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/glibc.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/hashes.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/inject_securetransport.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/logging.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/misc.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/models.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/packaging.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/parallel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/pkg_resources.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/setuptools_build.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/subprocess.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/temp_dir.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/unpacking.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/urls.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/virtualenv.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/utils/wheel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/vcs/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/vcs/bazaar.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/vcs/git.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/vcs/mercurial.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/vcs/subversion.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/vcs/versioncontrol.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_internal/wheel_builder.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/appdirs.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/_cmd.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/adapter.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/cache.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/compat.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/controller.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/filewrapper.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/heuristics.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/serialize.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/cachecontrol/wrapper.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/certifi/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/certifi/__main__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/certifi/cacert.pem create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/certifi/core.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/big5freq.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/big5prober.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/chardistribution.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/charsetgroupprober.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/charsetprober.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/cli/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/cli/chardetect.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/codingstatemachine.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/compat.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/cp949prober.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/enums.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/escprober.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/escsm.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/eucjpprober.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/euckrfreq.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/euckrprober.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/euctwfreq.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/euctwprober.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/gb2312freq.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/gb2312prober.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/hebrewprober.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/jisfreq.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/jpcntx.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/langbulgarianmodel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/langgreekmodel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/langhebrewmodel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/langhungarianmodel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/langrussianmodel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/langthaimodel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/langturkishmodel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/latin1prober.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/mbcharsetprober.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/mbcsgroupprober.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/mbcssm.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/metadata/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/metadata/languages.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/sbcharsetprober.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/sbcsgroupprober.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/sjisprober.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/universaldetector.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/utf8prober.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/chardet/version.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/colorama/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/colorama/ansi.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/colorama/ansitowin32.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/colorama/initialise.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/colorama/win32.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/colorama/winterm.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/misc.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/shutil.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/sysconfig.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/_backport/tarfile.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/compat.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/database.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/index.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/locators.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/manifest.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/markers.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/metadata.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/resources.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/scripts.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/t32.exe create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/t64.exe create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/util.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/version.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/w32.exe create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/w64.exe create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distlib/wheel.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/distro.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/_ihatexml.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/_inputstream.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/_tokenizer.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/_base.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/_trie/py.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/_utils.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/constants.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/base.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/lint.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/optionaltags.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/sanitizer.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/filters/whitespace.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/html5parser.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/serializer.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treeadapters/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treeadapters/genshi.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treeadapters/sax.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/base.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/dom.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/etree.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/base.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/dom.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/etree.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/html5lib/treewalkers/genshi.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/idna/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/idna/codec.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/idna/compat.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/idna/core.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/idna/idnadata.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/idna/intranges.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/idna/package_data.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/idna/uts46data.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/msgpack/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/msgpack/_version.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/msgpack/exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/msgpack/ext.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/msgpack/fallback.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/packaging/__about__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/packaging/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/packaging/_manylinux.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/packaging/_musllinux.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/packaging/_structures.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/packaging/markers.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/packaging/requirements.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/packaging/specifiers.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/packaging/tags.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/packaging/utils.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/packaging/version.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/pep517/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/pep517/build.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/pep517/check.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/pep517/colorlog.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/pep517/compat.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/pep517/dirtools.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/pep517/envbuild.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/pep517/in_process/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/pep517/in_process/_in_process.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/pep517/meta.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/pep517/wrappers.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/pkg_resources/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/pkg_resources/py31compat.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/progress/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/progress/bar.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/progress/counter.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/progress/spinner.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/pyparsing.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/requests/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/requests/__version__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/requests/_internal_utils.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/requests/adapters.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/requests/api.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/requests/auth.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/requests/certs.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/requests/compat.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/requests/cookies.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/requests/exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/requests/help.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/requests/hooks.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/requests/models.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/requests/packages.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/requests/sessions.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/requests/status_codes.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/requests/structures.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/requests/utils.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/resolvelib/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/resolvelib/compat/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/resolvelib/providers.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/resolvelib/reporters.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/resolvelib/resolvers.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/resolvelib/structs.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/six.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/tenacity/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/tenacity/_asyncio.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/tenacity/_utils.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/tenacity/after.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/tenacity/before.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/tenacity/before_sleep.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/tenacity/nap.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/tenacity/retry.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/tenacity/stop.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/tenacity/tornadoweb.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/tenacity/wait.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/tomli/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/tomli/_parser.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/tomli/_re.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/_collections.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/_version.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/connection.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/connectionpool.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/appengine.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/securetransport.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/socks.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/fields.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/filepost.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/six.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/poolmanager.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/request.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/response.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/connection.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/proxy.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/queue.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/request.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/response.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/retry.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/ssl_.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/ssltransport.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/timeout.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/url.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/urllib3/util/wait.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/vendor.txt create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/webencodings/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/webencodings/labels.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/webencodings/mklabels.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/webencodings/tests.py create mode 100644 .venv/lib/python3.9/site-packages/pip/_vendor/webencodings/x_user_defined.py create mode 100644 .venv/lib/python3.9/site-packages/pip/py.typed create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/appdirs.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__about__.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_compat.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_structures.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_typing.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/markers.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/requirements.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/specifiers.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/tags.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/utils.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/version.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/_vendor/pyparsing.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/extern/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/pkg_resources/tests/data/my-test-package-source/setup.py create mode 100644 .venv/lib/python3.9/site-packages/propcache-0.4.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/propcache-0.4.1.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/propcache-0.4.1.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/propcache-0.4.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/propcache-0.4.1.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/propcache-0.4.1.dist-info/licenses/NOTICE create mode 100644 .venv/lib/python3.9/site-packages/propcache-0.4.1.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/propcache/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/propcache/_helpers.py create mode 100755 .venv/lib/python3.9/site-packages/propcache/_helpers_c.cpython-39-darwin.so create mode 100644 .venv/lib/python3.9/site-packages/propcache/_helpers_c.pyx create mode 100644 .venv/lib/python3.9/site-packages/propcache/_helpers_py.py create mode 100644 .venv/lib/python3.9/site-packages/propcache/api.py create mode 100644 .venv/lib/python3.9/site-packages/propcache/py.typed create mode 100644 .venv/lib/python3.9/site-packages/python_dateutil-2.9.0.post0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/python_dateutil-2.9.0.post0.dist-info/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/python_dateutil-2.9.0.post0.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/python_dateutil-2.9.0.post0.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/python_dateutil-2.9.0.post0.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/python_dateutil-2.9.0.post0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/python_dateutil-2.9.0.post0.dist-info/zip-safe create mode 100644 .venv/lib/python3.9/site-packages/pyyaml-6.0.3.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/pyyaml-6.0.3.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/pyyaml-6.0.3.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/pyyaml-6.0.3.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/pyyaml-6.0.3.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/pyyaml-6.0.3.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/qh3-1.6.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/qh3-1.6.0.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/qh3-1.6.0.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/qh3-1.6.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/qh3-1.6.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/qh3-1.6.0.dist-info/sboms/qh3.cyclonedx.json create mode 100644 .venv/lib/python3.9/site-packages/qh3/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/qh3/_compat.py create mode 100755 .venv/lib/python3.9/site-packages/qh3/_hazmat.abi3.so create mode 100644 .venv/lib/python3.9/site-packages/qh3/_hazmat.pyi create mode 100644 .venv/lib/python3.9/site-packages/qh3/asyncio/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/qh3/asyncio/client.py create mode 100644 .venv/lib/python3.9/site-packages/qh3/asyncio/protocol.py create mode 100644 .venv/lib/python3.9/site-packages/qh3/asyncio/server.py create mode 100644 .venv/lib/python3.9/site-packages/qh3/h3/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/qh3/h3/connection.py create mode 100644 .venv/lib/python3.9/site-packages/qh3/h3/events.py create mode 100644 .venv/lib/python3.9/site-packages/qh3/h3/exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/qh3/py.typed create mode 100644 .venv/lib/python3.9/site-packages/qh3/quic/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/qh3/quic/configuration.py create mode 100644 .venv/lib/python3.9/site-packages/qh3/quic/connection.py create mode 100644 .venv/lib/python3.9/site-packages/qh3/quic/crypto.py create mode 100644 .venv/lib/python3.9/site-packages/qh3/quic/events.py create mode 100644 .venv/lib/python3.9/site-packages/qh3/quic/logger.py create mode 100644 .venv/lib/python3.9/site-packages/qh3/quic/packet.py create mode 100644 .venv/lib/python3.9/site-packages/qh3/quic/packet_builder.py create mode 100644 .venv/lib/python3.9/site-packages/qh3/quic/recovery.py create mode 100644 .venv/lib/python3.9/site-packages/qh3/quic/retry.py create mode 100644 .venv/lib/python3.9/site-packages/qh3/quic/stream.py create mode 100644 .venv/lib/python3.9/site-packages/qh3/tls.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events-3.8.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events-3.8.1.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events-3.8.1.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events-3.8.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events-3.8.1.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/.gitignore create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/adapters/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/adapters/alarm.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/adapters/component.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/adapters/event.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/adapters/journal.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/adapters/todo.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/constants.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/errors.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/examples.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/occurrence.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/pages.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/query.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/selection/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/selection/alarm.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/selection/all.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/selection/base.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/selection/name.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/series/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/series/alarm.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/series/rrule.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/Germany.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/Germany_Holidays.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/after_many_events_in_order.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/alarm_15_min_before_event_snoozed.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/alarm_1_week_before_event.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/alarm_absolute.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/alarm_absolute_edited.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/alarm_absolute_repeat.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/alarm_around_event_boundaries.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/alarm_at_start_of_event.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/alarm_of_repeated_event.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/alarm_recurring_and_acknowledged_at_2024_11_27_16_27.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/alarm_removed_and_moved.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/alarm_several_in_one.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/alarms_at_the_same_time.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/alarms_different_in_same_event.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/bad_rrule_missing_until_event.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/date_exclude.txt create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/daylight_saving_time.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/discourse_no_dtend.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/duplicated_rrule.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/duration.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/duration_edited.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/each_week_but_one_deleted.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/each_week_but_two_deleted.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/end_before_start_event.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/event_10_times.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/fablab_cottbus.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_107_omitting_last_event.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_113_period_in_rdate.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_113_period_rdate_duration.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_117_until_before_dtstart.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_128_only_first_event.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_132_swapped_start_and_end.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_148_edge_case_1.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_148_edge_case_2.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_148_exdate_and_rdate_unedited.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_148_exdate_and_rdate_updated.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_148_ignored_exdate.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_151_macos_linux_difference.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_151_macos_linux_difference2.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_15_duplicated_events.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_163_deleted_modification.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_164_duplicated_event.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_173_only_modifications_error.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_179_example.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_186_invalid_trigger.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_18_cancel_status.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_201_mixed_datetime_and_date.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_201_test_matrix.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_20_exdate_ignored.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_223_one_event_with_sequence.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_223_thunderbird.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_243_recurrence_id_is_not_identical_to_dtstart.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_27_t1.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_27_t2.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_28_rrule_with_UTC_endinginZ.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_36_recurrence_ID_format.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_4.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_44_double_event.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_48_daylight_aware_repeats.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_48_dst.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_4_rrule_until.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_4_weidenrinde.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_61_time_zone_error.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_62_moved_event.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_62_moved_event_2.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_75_range_parameter.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_86_x_wr_timezone_without_time_zone_in_dt.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_97_simple_journal.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_97_simple_todo.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/issue_97_todo_nodtstart.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/machbar_16_feb_2019.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/multiple_rrule.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/no_events.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/one_day_event.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/one_day_event_repeat_every_day.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/one_event.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/one_event_repeat_every_3_days.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/rdate.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/rdate2.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/rdate_falls_on_rrule_until.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/rdate_hackerpublicradio.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/recurrence_sequence_number.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/recurring_events_changed_duration.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/recurring_events_moved.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/same_event_recurring_at_same_time.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/several_events_at_the_same_time.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/subcomponents.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/three_events.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/three_events_one_edited.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/x_wr_timezone_simple_events_issue_59.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/calendars/zero_size_event.ics create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/conftest.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/py.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_after.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_at_function.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_bad_rrule_format.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_convert_inputs.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_count.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_daylight_saving_time.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_deleted_entries.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_duration.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_end_before_start_event.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_event_values_and_edits.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_example_function.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_examples.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_extend_classes.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_101_select_components.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_107_omitting_last_event.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_113_period_in_rdate.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_117_until_before_dtstart.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_128_only_first_event.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_132_swapped_start_and_end.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_139_no_duration.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_148_ignored_exdate_in_higher_sequence.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_15.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_151_macos_linux_difference.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_163_deleted_modification.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_164_duplicated_event.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_173_only_modification_included.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_179_span_in_event.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_186_alarms.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_186_icalendar_alarm_interface.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_18_cancel_status.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_201_incompatible_dates.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_20_exdate_ignored.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_211_pagination.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_219_recurrence_id_in_events.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_223_sequence_number.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_243_recurrence_id_is_not_identical_to_dtstart.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_27.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_28_timezone_with_z.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_36_recurrence_id_format.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_4.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_44_day_event_reported_twice.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_48_daylight.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_48_dst.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_61.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_62_moved_event.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_6_copy_subcomponents.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_75_range_parameter.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_7_datetime_and_date_start_stop.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_86_x_wr_timezone_but_no_tzid_in_dt.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_issue_97_simple_recurrent_todos_and_journals.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_keep_recurrence_attributes.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_multiple_rrule.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_occurrence.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_properties.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_rdate.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_readme.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_recurrence_sequence_number.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_repeated_properties.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_repetitions_do_not_change.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_simple_recurrent_events.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_single_events.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_skip_bad_events.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_time_arguments.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_time_span_contains_event.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_time_zones_differ.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_timedelta_for_between.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_util_functions.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_with_doctest.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_x_wr_timezone.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_zero_size_events.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/test/test_zoneinfo_issue_57.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/types.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/util.py create mode 100644 .venv/lib/python3.9/site-packages/recurring_ical_events/version.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/REQUESTED create mode 100644 .venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.9/site-packages/setuptools-58.0.4.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/setuptools/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_deprecation_warning.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/_msvccompiler.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/archive_util.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/bcppcompiler.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/ccompiler.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/cmd.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/bdist.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/bdist_dumb.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/bdist_msi.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/bdist_rpm.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/bdist_wininst.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/build.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/build_clib.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/build_ext.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/build_py.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/build_scripts.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/check.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/clean.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/config.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/install.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/install_data.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/install_egg_info.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/install_headers.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/install_lib.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/install_scripts.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/py37compat.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/register.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/sdist.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/command/upload.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/config.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/core.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/cygwinccompiler.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/debug.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/dep_util.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/dir_util.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/dist.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/errors.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/extension.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/fancy_getopt.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/file_util.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/filelist.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/log.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/msvc9compiler.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/msvccompiler.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/py35compat.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/py38compat.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/spawn.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/sysconfig.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/text_file.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/unixccompiler.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/util.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/version.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_distutils/versionpredicate.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_imp.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/more_itertools/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/more_itertools/more.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/more_itertools/recipes.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/ordered_set.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__about__.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/_compat.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/_structures.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/_typing.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/markers.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/requirements.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/specifiers.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/tags.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/utils.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/packaging/version.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/_vendor/pyparsing.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/archive_util.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/build_meta.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/cli-32.exe create mode 100644 .venv/lib/python3.9/site-packages/setuptools/cli-64.exe create mode 100644 .venv/lib/python3.9/site-packages/setuptools/cli.exe create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/alias.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/bdist_egg.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/bdist_rpm.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/build_clib.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/build_ext.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/build_py.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/develop.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/dist_info.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/easy_install.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/egg_info.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/install.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/install_egg_info.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/install_lib.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/install_scripts.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/launcher manifest.xml create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/py36compat.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/register.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/rotate.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/saveopts.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/sdist.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/setopt.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/test.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/upload.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/command/upload_docs.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/config.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/dep_util.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/depends.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/dist.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/errors.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/extension.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/extern/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/glob.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/gui-32.exe create mode 100644 .venv/lib/python3.9/site-packages/setuptools/gui-64.exe create mode 100644 .venv/lib/python3.9/site-packages/setuptools/gui.exe create mode 100644 .venv/lib/python3.9/site-packages/setuptools/installer.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/launch.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/monkey.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/msvc.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/namespaces.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/package_index.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/py34compat.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/sandbox.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/script (dev).tmpl create mode 100644 .venv/lib/python3.9/site-packages/setuptools/script.tmpl create mode 100644 .venv/lib/python3.9/site-packages/setuptools/unicode_utils.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/version.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/wheel.py create mode 100644 .venv/lib/python3.9/site-packages/setuptools/windows_support.py create mode 100644 .venv/lib/python3.9/site-packages/six-1.17.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/six-1.17.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/six-1.17.0.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/six-1.17.0.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/six-1.17.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/six-1.17.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/six.py create mode 100644 .venv/lib/python3.9/site-packages/typing_extensions-4.15.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/typing_extensions-4.15.0.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/typing_extensions-4.15.0.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/typing_extensions-4.15.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/typing_extensions-4.15.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/typing_extensions.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata-2025.3.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/tzdata-2025.3.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/tzdata-2025.3.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/tzdata-2025.3.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/tzdata-2025.3.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/tzdata-2025.3.dist-info/licenses/licenses/LICENSE_APACHE create mode 100644 .venv/lib/python3.9/site-packages/tzdata-2025.3.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/tzdata/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Abidjan create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Accra create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Addis_Ababa create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Algiers create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Asmara create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Asmera create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Bamako create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Bangui create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Banjul create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Bissau create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Blantyre create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Brazzaville create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Bujumbura create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Cairo create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Casablanca create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Ceuta create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Conakry create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Dakar create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Dar_es_Salaam create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Djibouti create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Douala create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/El_Aaiun create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Freetown create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Gaborone create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Harare create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Johannesburg create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Juba create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Kampala create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Khartoum create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Kigali create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Kinshasa create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Lagos create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Libreville create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Lome create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Luanda create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Lubumbashi create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Lusaka create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Malabo create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Maputo create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Maseru create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Mbabane create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Mogadishu create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Monrovia create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Nairobi create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Ndjamena create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Niamey create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Nouakchott create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Ouagadougou create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Porto-Novo create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Sao_Tome create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Timbuktu create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Tripoli create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Tunis create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/Windhoek create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Africa/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Adak create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Anchorage create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Anguilla create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Antigua create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Araguaina create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Argentina/Buenos_Aires create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Argentina/Catamarca create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Argentina/ComodRivadavia create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Argentina/Cordoba create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Argentina/Jujuy create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Argentina/La_Rioja create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Argentina/Mendoza create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Argentina/Rio_Gallegos create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Argentina/Salta create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Argentina/San_Juan create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Argentina/San_Luis create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Argentina/Tucuman create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Argentina/Ushuaia create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Argentina/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Aruba create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Asuncion create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Atikokan create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Atka create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Bahia create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Bahia_Banderas create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Barbados create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Belem create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Belize create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Blanc-Sablon create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Boa_Vista create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Bogota create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Boise create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Buenos_Aires create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Cambridge_Bay create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Campo_Grande create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Cancun create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Caracas create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Catamarca create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Cayenne create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Cayman create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Chicago create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Chihuahua create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Ciudad_Juarez create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Coral_Harbour create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Cordoba create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Costa_Rica create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Coyhaique create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Creston create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Cuiaba create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Curacao create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Danmarkshavn create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Dawson create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Dawson_Creek create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Denver create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Detroit create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Dominica create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Edmonton create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Eirunepe create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/El_Salvador create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Ensenada create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Fort_Nelson create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Fort_Wayne create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Fortaleza create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Glace_Bay create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Godthab create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Goose_Bay create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Grand_Turk create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Grenada create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Guadeloupe create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Guatemala create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Guayaquil create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Guyana create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Halifax create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Havana create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Hermosillo create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Indiana/Indianapolis create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Indiana/Knox create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Indiana/Marengo create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Indiana/Petersburg create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Indiana/Tell_City create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Indiana/Vevay create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Indiana/Vincennes create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Indiana/Winamac create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Indiana/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Indianapolis create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Inuvik create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Iqaluit create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Jamaica create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Jujuy create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Juneau create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Kentucky/Louisville create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Kentucky/Monticello create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Kentucky/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Knox_IN create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Kralendijk create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/La_Paz create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Lima create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Los_Angeles create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Louisville create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Lower_Princes create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Maceio create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Managua create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Manaus create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Marigot create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Martinique create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Matamoros create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Mazatlan create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Mendoza create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Menominee create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Merida create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Metlakatla create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Mexico_City create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Miquelon create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Moncton create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Monterrey create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Montevideo create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Montreal create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Montserrat create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Nassau create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/New_York create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Nipigon create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Nome create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Noronha create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/North_Dakota/Beulah create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/North_Dakota/Center create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/North_Dakota/New_Salem create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/North_Dakota/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Nuuk create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Ojinaga create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Panama create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Pangnirtung create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Paramaribo create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Phoenix create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Port-au-Prince create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Port_of_Spain create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Porto_Acre create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Porto_Velho create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Puerto_Rico create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Punta_Arenas create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Rainy_River create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Rankin_Inlet create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Recife create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Regina create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Resolute create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Rio_Branco create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Rosario create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Santa_Isabel create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Santarem create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Santiago create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Santo_Domingo create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Sao_Paulo create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Scoresbysund create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Shiprock create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Sitka create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/St_Barthelemy create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/St_Johns create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/St_Kitts create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/St_Lucia create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/St_Thomas create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/St_Vincent create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Swift_Current create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Tegucigalpa create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Thule create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Thunder_Bay create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Tijuana create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Toronto create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Tortola create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Vancouver create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Virgin create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Whitehorse create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Winnipeg create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Yakutat create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/Yellowknife create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/America/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Antarctica/Casey create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Antarctica/Davis create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Antarctica/DumontDUrville create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Antarctica/Macquarie create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Antarctica/Mawson create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Antarctica/McMurdo create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Antarctica/Palmer create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Antarctica/Rothera create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Antarctica/South_Pole create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Antarctica/Syowa create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Antarctica/Troll create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Antarctica/Vostok create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Antarctica/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Arctic/Longyearbyen create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Arctic/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Aden create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Almaty create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Amman create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Anadyr create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Aqtau create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Aqtobe create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Ashgabat create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Ashkhabad create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Atyrau create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Baghdad create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Bahrain create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Baku create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Bangkok create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Barnaul create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Beirut create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Bishkek create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Brunei create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Calcutta create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Chita create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Choibalsan create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Chongqing create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Chungking create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Colombo create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Dacca create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Damascus create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Dhaka create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Dili create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Dubai create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Dushanbe create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Famagusta create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Gaza create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Harbin create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Hebron create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Ho_Chi_Minh create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Hong_Kong create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Hovd create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Irkutsk create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Istanbul create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Jakarta create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Jayapura create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Jerusalem create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Kabul create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Kamchatka create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Karachi create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Kashgar create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Kathmandu create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Katmandu create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Khandyga create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Kolkata create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Krasnoyarsk create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Kuala_Lumpur create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Kuching create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Kuwait create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Macao create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Macau create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Magadan create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Makassar create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Manila create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Muscat create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Nicosia create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Novokuznetsk create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Novosibirsk create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Omsk create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Oral create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Phnom_Penh create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Pontianak create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Pyongyang create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Qatar create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Qostanay create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Qyzylorda create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Rangoon create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Riyadh create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Saigon create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Sakhalin create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Samarkand create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Seoul create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Shanghai create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Singapore create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Srednekolymsk create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Taipei create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Tashkent create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Tbilisi create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Tehran create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Tel_Aviv create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Thimbu create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Thimphu create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Tokyo create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Tomsk create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Ujung_Pandang create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Ulaanbaatar create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Ulan_Bator create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Urumqi create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Ust-Nera create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Vientiane create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Vladivostok create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Yakutsk create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Yangon create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Yekaterinburg create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/Yerevan create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Asia/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Atlantic/Azores create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Atlantic/Bermuda create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Atlantic/Canary create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Atlantic/Cape_Verde create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Atlantic/Faeroe create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Atlantic/Faroe create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Atlantic/Jan_Mayen create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Atlantic/Madeira create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Atlantic/Reykjavik create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Atlantic/South_Georgia create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Atlantic/St_Helena create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Atlantic/Stanley create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Atlantic/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/ACT create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/Adelaide create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/Brisbane create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/Broken_Hill create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/Canberra create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/Currie create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/Darwin create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/Eucla create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/Hobart create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/LHI create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/Lindeman create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/Lord_Howe create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/Melbourne create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/NSW create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/North create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/Perth create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/Queensland create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/South create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/Sydney create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/Tasmania create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/Victoria create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/West create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/Yancowinna create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Australia/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Brazil/Acre create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Brazil/DeNoronha create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Brazil/East create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Brazil/West create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Brazil/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/CET create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/CST6CDT create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Canada/Atlantic create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Canada/Central create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Canada/Eastern create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Canada/Mountain create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Canada/Newfoundland create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Canada/Pacific create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Canada/Saskatchewan create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Canada/Yukon create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Canada/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Chile/Continental create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Chile/EasterIsland create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Chile/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Cuba create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/EET create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/EST create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/EST5EDT create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Egypt create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Eire create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT+0 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT+1 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT+10 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT+11 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT+12 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT+2 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT+3 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT+4 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT+5 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT+6 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT+7 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT+8 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT+9 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT-0 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT-1 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT-10 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT-11 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT-12 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT-13 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT-14 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT-2 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT-3 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT-4 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT-5 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT-6 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT-7 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT-8 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT-9 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/GMT0 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/Greenwich create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/UCT create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/UTC create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/Universal create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/Zulu create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Etc/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Amsterdam create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Andorra create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Astrakhan create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Athens create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Belfast create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Belgrade create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Berlin create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Bratislava create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Brussels create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Bucharest create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Budapest create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Busingen create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Chisinau create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Copenhagen create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Dublin create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Gibraltar create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Guernsey create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Helsinki create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Isle_of_Man create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Istanbul create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Jersey create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Kaliningrad create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Kiev create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Kirov create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Kyiv create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Lisbon create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Ljubljana create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/London create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Luxembourg create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Madrid create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Malta create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Mariehamn create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Minsk create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Monaco create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Moscow create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Nicosia create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Oslo create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Paris create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Podgorica create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Prague create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Riga create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Rome create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Samara create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/San_Marino create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Sarajevo create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Saratov create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Simferopol create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Skopje create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Sofia create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Stockholm create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Tallinn create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Tirane create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Tiraspol create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Ulyanovsk create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Uzhgorod create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Vaduz create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Vatican create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Vienna create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Vilnius create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Volgograd create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Warsaw create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Zagreb create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Zaporozhye create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/Zurich create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Europe/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Factory create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/GB create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/GB-Eire create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/GMT create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/GMT+0 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/GMT-0 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/GMT0 create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Greenwich create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/HST create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Hongkong create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Iceland create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Indian/Antananarivo create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Indian/Chagos create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Indian/Christmas create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Indian/Cocos create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Indian/Comoro create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Indian/Kerguelen create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Indian/Mahe create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Indian/Maldives create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Indian/Mauritius create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Indian/Mayotte create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Indian/Reunion create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Indian/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Iran create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Israel create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Jamaica create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Japan create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Kwajalein create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Libya create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/MET create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/MST create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/MST7MDT create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Mexico/BajaNorte create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Mexico/BajaSur create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Mexico/General create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Mexico/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/NZ create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/NZ-CHAT create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Navajo create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/PRC create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/PST8PDT create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Apia create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Auckland create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Bougainville create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Chatham create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Chuuk create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Easter create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Efate create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Enderbury create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Fakaofo create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Fiji create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Funafuti create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Galapagos create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Gambier create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Guadalcanal create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Guam create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Honolulu create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Johnston create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Kanton create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Kiritimati create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Kosrae create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Kwajalein create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Majuro create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Marquesas create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Midway create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Nauru create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Niue create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Norfolk create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Noumea create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Pago_Pago create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Palau create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Pitcairn create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Pohnpei create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Ponape create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Port_Moresby create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Rarotonga create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Saipan create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Samoa create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Tahiti create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Tarawa create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Tongatapu create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Truk create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Wake create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Wallis create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/Yap create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Pacific/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Poland create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Portugal create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/ROC create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/ROK create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Singapore create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Turkey create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/UCT create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/US/Alaska create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/US/Aleutian create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/US/Arizona create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/US/Central create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/US/East-Indiana create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/US/Eastern create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/US/Hawaii create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/US/Indiana-Starke create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/US/Michigan create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/US/Mountain create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/US/Pacific create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/US/Samoa create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/US/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/UTC create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Universal create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/W-SU create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/WET create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/Zulu create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/iso3166.tab create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/leapseconds create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/tzdata.zi create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/zone.tab create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/zone1970.tab create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zoneinfo/zonenow.tab create mode 100644 .venv/lib/python3.9/site-packages/tzdata/zones create mode 100644 .venv/lib/python3.9/site-packages/urllib3/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/_async/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/_async/connection.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/_async/connectionpool.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/_async/poolmanager.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/_async/response.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/_collections.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/_constant.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/_request_methods.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/_typing.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/_version.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/backend/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/backend/_async/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/backend/_async/_base.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/backend/_async/hface.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/backend/_base.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/backend/hface.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/connection.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/connectionpool.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/_socks_legacy.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/_socks_override.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/emscripten/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/hface/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/hface/_configuration.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/hface/_stream_matrix.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/hface/_typing.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/hface/events/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/hface/events/_events.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/hface/protocols/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/hface/protocols/_factories.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/hface/protocols/_protocols.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/hface/protocols/http1/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/hface/protocols/http1/_h11.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/hface/protocols/http2/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/hface/protocols/http2/_h2.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/hface/protocols/http3/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/hface/protocols/http3/_qh3.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/hface/py.typed create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/imcc/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/imcc/_ctypes.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/imcc/_shm.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/pyopenssl.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/_async/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/_async/doh/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/_async/doh/_urllib3.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/_async/doq/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/_async/doq/_qh3.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/_async/dot/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/_async/dot/_ssl.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/_async/dou/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/_async/dou/_socket.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/_async/factories.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/_async/in_memory/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/_async/in_memory/_dict.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/_async/null/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/_async/protocols.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/_async/system/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/_async/system/_socket.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/doh/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/doh/_urllib3.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/doq/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/doq/_qh3.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/dot/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/dot/_ssl.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/dou/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/dou/_socket.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/factories.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/in_memory/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/in_memory/_dict.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/null/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/protocols.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/system/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/system/_socket.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/resolver/utils.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/socks.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/ssa/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/ssa/_gro.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/ssa/_timeout.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/webextensions/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/webextensions/_async/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/webextensions/_async/protocol.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/webextensions/_async/raw.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/webextensions/_async/sse.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/webextensions/_async/ws.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/webextensions/protocol.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/webextensions/raw.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/webextensions/sse.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/contrib/webextensions/ws.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/fields.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/filepost.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/http2/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/poolmanager.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/py.typed create mode 100644 .venv/lib/python3.9/site-packages/urllib3/response.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/util/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/util/_async/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/util/_async/ssl_.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/util/_async/traffic_police.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/util/connection.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/util/proxy.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/util/request.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/util/response.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/util/retry.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/util/socket_state.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/util/ssl_.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/util/ssl_match_hostname.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/util/ssltransport.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/util/timeout.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/util/traffic_police.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/util/url.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/util/util.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3/util/wait.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future-2.17.902.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future-2.17.902.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future-2.17.902.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future-2.17.902.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future-2.17.902.dist-info/licenses/LICENSE.txt create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future.pth create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/_async/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/_async/connection.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/_async/connectionpool.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/_async/poolmanager.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/_async/response.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/_collections.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/_constant.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/_request_methods.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/_typing.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/_version.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/backend/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/backend/_async/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/backend/_async/_base.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/backend/_async/hface.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/backend/_base.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/backend/hface.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/connection.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/connectionpool.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/_socks_legacy.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/_socks_override.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/emscripten/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/hface/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/hface/_configuration.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/hface/_stream_matrix.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/hface/_typing.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/hface/events/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/hface/events/_events.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/hface/protocols/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/hface/protocols/_factories.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/hface/protocols/_protocols.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/hface/protocols/http1/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/hface/protocols/http1/_h11.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/hface/protocols/http2/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/hface/protocols/http2/_h2.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/hface/protocols/http3/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/hface/protocols/http3/_qh3.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/hface/py.typed create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/imcc/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/imcc/_ctypes.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/imcc/_shm.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/pyopenssl.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/_async/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/_async/doh/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/_async/doh/_urllib3.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/_async/doq/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/_async/doq/_qh3.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/_async/dot/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/_async/dot/_ssl.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/_async/dou/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/_async/dou/_socket.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/_async/factories.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/_async/in_memory/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/_async/in_memory/_dict.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/_async/null/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/_async/protocols.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/_async/system/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/_async/system/_socket.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/doh/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/doh/_urllib3.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/doq/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/doq/_qh3.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/dot/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/dot/_ssl.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/dou/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/dou/_socket.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/factories.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/in_memory/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/in_memory/_dict.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/null/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/protocols.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/system/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/system/_socket.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/resolver/utils.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/socks.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/ssa/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/ssa/_gro.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/ssa/_timeout.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/webextensions/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/webextensions/_async/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/webextensions/_async/protocol.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/webextensions/_async/raw.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/webextensions/_async/sse.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/webextensions/_async/ws.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/webextensions/protocol.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/webextensions/raw.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/webextensions/sse.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/contrib/webextensions/ws.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/fields.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/filepost.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/http2/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/poolmanager.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/py.typed create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/response.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/util/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/util/_async/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/util/_async/ssl_.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/util/_async/traffic_police.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/util/connection.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/util/proxy.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/util/request.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/util/response.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/util/retry.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/util/socket_state.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/util/ssl_.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/util/ssl_match_hostname.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/util/ssltransport.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/util/timeout.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/util/traffic_police.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/util/url.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/util/util.py create mode 100644 .venv/lib/python3.9/site-packages/urllib3_future/util/wait.py create mode 100644 .venv/lib/python3.9/site-packages/wassima-2.0.5.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/wassima-2.0.5.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/wassima-2.0.5.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/wassima-2.0.5.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/wassima-2.0.5.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/wassima/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/wassima/_os/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/wassima/_os/_embed.py create mode 100644 .venv/lib/python3.9/site-packages/wassima/_os/_linux.py create mode 100644 .venv/lib/python3.9/site-packages/wassima/_os/_macos.py create mode 100644 .venv/lib/python3.9/site-packages/wassima/_os/_windows.py create mode 100644 .venv/lib/python3.9/site-packages/wassima/_version.py create mode 100644 .venv/lib/python3.9/site-packages/wassima/py.typed create mode 100644 .venv/lib/python3.9/site-packages/x_wr_timezone-2.0.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/x_wr_timezone-2.0.1.dist-info/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/x_wr_timezone-2.0.1.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/x_wr_timezone-2.0.1.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/x_wr_timezone-2.0.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/x_wr_timezone-2.0.1.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.9/site-packages/x_wr_timezone-2.0.1.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/x_wr_timezone-2.0.1.dist-info/zip-safe create mode 100644 .venv/lib/python3.9/site-packages/x_wr_timezone.py create mode 100644 .venv/lib/python3.9/site-packages/yaml/__init__.py create mode 100755 .venv/lib/python3.9/site-packages/yaml/_yaml.cpython-39-darwin.so create mode 100644 .venv/lib/python3.9/site-packages/yaml/composer.py create mode 100644 .venv/lib/python3.9/site-packages/yaml/constructor.py create mode 100644 .venv/lib/python3.9/site-packages/yaml/cyaml.py create mode 100644 .venv/lib/python3.9/site-packages/yaml/dumper.py create mode 100644 .venv/lib/python3.9/site-packages/yaml/emitter.py create mode 100644 .venv/lib/python3.9/site-packages/yaml/error.py create mode 100644 .venv/lib/python3.9/site-packages/yaml/events.py create mode 100644 .venv/lib/python3.9/site-packages/yaml/loader.py create mode 100644 .venv/lib/python3.9/site-packages/yaml/nodes.py create mode 100644 .venv/lib/python3.9/site-packages/yaml/parser.py create mode 100644 .venv/lib/python3.9/site-packages/yaml/reader.py create mode 100644 .venv/lib/python3.9/site-packages/yaml/representer.py create mode 100644 .venv/lib/python3.9/site-packages/yaml/resolver.py create mode 100644 .venv/lib/python3.9/site-packages/yaml/scanner.py create mode 100644 .venv/lib/python3.9/site-packages/yaml/serializer.py create mode 100644 .venv/lib/python3.9/site-packages/yaml/tokens.py create mode 100644 .venv/lib/python3.9/site-packages/yarl-1.22.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/yarl-1.22.0.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/yarl-1.22.0.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/yarl-1.22.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/yarl-1.22.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/yarl-1.22.0.dist-info/licenses/NOTICE create mode 100644 .venv/lib/python3.9/site-packages/yarl-1.22.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/yarl/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/yarl/_parse.py create mode 100644 .venv/lib/python3.9/site-packages/yarl/_path.py create mode 100644 .venv/lib/python3.9/site-packages/yarl/_query.py create mode 100644 .venv/lib/python3.9/site-packages/yarl/_quoters.py create mode 100644 .venv/lib/python3.9/site-packages/yarl/_quoting.py create mode 100755 .venv/lib/python3.9/site-packages/yarl/_quoting_c.cpython-39-darwin.so create mode 100644 .venv/lib/python3.9/site-packages/yarl/_quoting_c.pyx create mode 100644 .venv/lib/python3.9/site-packages/yarl/_quoting_py.py create mode 100644 .venv/lib/python3.9/site-packages/yarl/_url.py create mode 100644 .venv/lib/python3.9/site-packages/yarl/py.typed create mode 100644 .venv/pyvenv.cfg diff --git a/.env.example b/.env.example index 452acf5..5cf1d71 100644 --- a/.env.example +++ b/.env.example @@ -73,5 +73,5 @@ CALDAV_BRIDGE_URL=http://host.docker.internal:8092 DEVONTHINK_BRIDGE_URL=http://host.docker.internal:8093 MAIL_BRIDGE_URL=http://host.docker.internal:8094 KB_WRITER_URL=http://host.docker.internal:8095 -NOTE_BRIDGE_URL=http://host.docker.internal:8096 -INTENT_SERVICE_URL=http://host.docker.internal:8097 +NOTE_BRIDGE_URL=http://host.docker.internal:8098 +INTENT_SERVICE_URL=http://host.docker.internal:8099 diff --git a/.venv/bin/Activate.ps1 b/.venv/bin/Activate.ps1 new file mode 100644 index 0000000..2fb3852 --- /dev/null +++ b/.venv/bin/Activate.ps1 @@ -0,0 +1,241 @@ +<# +.Synopsis +Activate a Python virtual environment for the current PowerShell session. + +.Description +Pushes the python executable for a virtual environment to the front of the +$Env:PATH environment variable and sets the prompt to signify that you are +in a Python virtual environment. Makes use of the command line switches as +well as the `pyvenv.cfg` file values present in the virtual environment. + +.Parameter VenvDir +Path to the directory that contains the virtual environment to activate. The +default value for this is the parent of the directory that the Activate.ps1 +script is located within. + +.Parameter Prompt +The prompt prefix to display when this virtual environment is activated. By +default, this prompt is the name of the virtual environment folder (VenvDir) +surrounded by parentheses and followed by a single space (ie. '(.venv) '). + +.Example +Activate.ps1 +Activates the Python virtual environment that contains the Activate.ps1 script. + +.Example +Activate.ps1 -Verbose +Activates the Python virtual environment that contains the Activate.ps1 script, +and shows extra information about the activation as it executes. + +.Example +Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv +Activates the Python virtual environment located in the specified location. + +.Example +Activate.ps1 -Prompt "MyPython" +Activates the Python virtual environment that contains the Activate.ps1 script, +and prefixes the current prompt with the specified string (surrounded in +parentheses) while the virtual environment is active. + +.Notes +On Windows, it may be required to enable this Activate.ps1 script by setting the +execution policy for the user. You can do this by issuing the following PowerShell +command: + +PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + +For more information on Execution Policies: +https://go.microsoft.com/fwlink/?LinkID=135170 + +#> +Param( + [Parameter(Mandatory = $false)] + [String] + $VenvDir, + [Parameter(Mandatory = $false)] + [String] + $Prompt +) + +<# Function declarations --------------------------------------------------- #> + +<# +.Synopsis +Remove all shell session elements added by the Activate script, including the +addition of the virtual environment's Python executable from the beginning of +the PATH variable. + +.Parameter NonDestructive +If present, do not remove this function from the global namespace for the +session. + +#> +function global:deactivate ([switch]$NonDestructive) { + # Revert to original values + + # The prior prompt: + if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { + Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt + Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT + } + + # The prior PYTHONHOME: + if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { + Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME + Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME + } + + # The prior PATH: + if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { + Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH + Remove-Item -Path Env:_OLD_VIRTUAL_PATH + } + + # Just remove the VIRTUAL_ENV altogether: + if (Test-Path -Path Env:VIRTUAL_ENV) { + Remove-Item -Path env:VIRTUAL_ENV + } + + # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: + if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { + Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force + } + + # Leave deactivate function in the global namespace if requested: + if (-not $NonDestructive) { + Remove-Item -Path function:deactivate + } +} + +<# +.Description +Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the +given folder, and returns them in a map. + +For each line in the pyvenv.cfg file, if that line can be parsed into exactly +two strings separated by `=` (with any amount of whitespace surrounding the =) +then it is considered a `key = value` line. The left hand string is the key, +the right hand is the value. + +If the value starts with a `'` or a `"` then the first and last character is +stripped from the value before being captured. + +.Parameter ConfigDir +Path to the directory that contains the `pyvenv.cfg` file. +#> +function Get-PyVenvConfig( + [String] + $ConfigDir +) { + Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" + + # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). + $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue + + # An empty map will be returned if no config file is found. + $pyvenvConfig = @{ } + + if ($pyvenvConfigPath) { + + Write-Verbose "File exists, parse `key = value` lines" + $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath + + $pyvenvConfigContent | ForEach-Object { + $keyval = $PSItem -split "\s*=\s*", 2 + if ($keyval[0] -and $keyval[1]) { + $val = $keyval[1] + + # Remove extraneous quotations around a string value. + if ("'""".Contains($val.Substring(0, 1))) { + $val = $val.Substring(1, $val.Length - 2) + } + + $pyvenvConfig[$keyval[0]] = $val + Write-Verbose "Adding Key: '$($keyval[0])'='$val'" + } + } + } + return $pyvenvConfig +} + + +<# Begin Activate script --------------------------------------------------- #> + +# Determine the containing directory of this script +$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +$VenvExecDir = Get-Item -Path $VenvExecPath + +Write-Verbose "Activation script is located in path: '$VenvExecPath'" +Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" +Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" + +# Set values required in priority: CmdLine, ConfigFile, Default +# First, get the location of the virtual environment, it might not be +# VenvExecDir if specified on the command line. +if ($VenvDir) { + Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" +} +else { + Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." + $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") + Write-Verbose "VenvDir=$VenvDir" +} + +# Next, read the `pyvenv.cfg` file to determine any required value such +# as `prompt`. +$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir + +# Next, set the prompt from the command line, or the config file, or +# just use the name of the virtual environment folder. +if ($Prompt) { + Write-Verbose "Prompt specified as argument, using '$Prompt'" +} +else { + Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" + if ($pyvenvCfg -and $pyvenvCfg['prompt']) { + Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" + $Prompt = $pyvenvCfg['prompt']; + } + else { + Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virutal environment)" + Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" + $Prompt = Split-Path -Path $venvDir -Leaf + } +} + +Write-Verbose "Prompt = '$Prompt'" +Write-Verbose "VenvDir='$VenvDir'" + +# Deactivate any currently active virtual environment, but leave the +# deactivate function in place. +deactivate -nondestructive + +# Now set the environment variable VIRTUAL_ENV, used by many tools to determine +# that there is an activated venv. +$env:VIRTUAL_ENV = $VenvDir + +if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { + + Write-Verbose "Setting prompt to '$Prompt'" + + # Set the prompt to include the env name + # Make sure _OLD_VIRTUAL_PROMPT is global + function global:_OLD_VIRTUAL_PROMPT { "" } + Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT + New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt + + function global:prompt { + Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " + _OLD_VIRTUAL_PROMPT + } +} + +# Clear PYTHONHOME +if (Test-Path -Path Env:PYTHONHOME) { + Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME + Remove-Item -Path Env:PYTHONHOME +} + +# Add the venv to the PATH +Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH +$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" diff --git a/.venv/bin/activate b/.venv/bin/activate new file mode 100644 index 0000000..9cd6e7f --- /dev/null +++ b/.venv/bin/activate @@ -0,0 +1,66 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then + PATH="${_OLD_VIRTUAL_PATH:-}" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then + PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r 2> /dev/null + fi + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + if [ ! "${1:-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV="/Users/hyungiahn/Documents/code/syn-chat-bot/.venv" +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + PS1="(.venv) ${PS1:-}" + export PS1 +fi + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r 2> /dev/null +fi diff --git a/.venv/bin/activate.csh b/.venv/bin/activate.csh new file mode 100644 index 0000000..5c570de --- /dev/null +++ b/.venv/bin/activate.csh @@ -0,0 +1,25 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/Users/hyungiahn/Documents/code/syn-chat-bot/.venv" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/bin:$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + set prompt = "(.venv) $prompt" +endif + +alias pydoc python -m pydoc + +rehash diff --git a/.venv/bin/activate.fish b/.venv/bin/activate.fish new file mode 100644 index 0000000..fca0ffd --- /dev/null +++ b/.venv/bin/activate.fish @@ -0,0 +1,64 @@ +# This file must be used with "source /bin/activate.fish" *from fish* +# (https://fishshell.com/); you cannot run it directly. + +function deactivate -d "Exit virtual environment and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + functions -e fish_prompt + set -e _OLD_FISH_PROMPT_OVERRIDE + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + + set -e VIRTUAL_ENV + if test "$argv[1]" != "nondestructive" + # Self-destruct! + functions -e deactivate + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV "/Users/hyungiahn/Documents/code/syn-chat-bot/.venv" + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/bin" $PATH + +# Unset PYTHONHOME if set. +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # Save the current fish_prompt function as the function _old_fish_prompt. + functions -c fish_prompt _old_fish_prompt + + # With the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command. + set -l old_status $status + + # Output the venv prompt; color taken from the blue of the Python logo. + printf "%s%s%s" (set_color 4B8BBE) "(.venv) " (set_color normal) + + # Restore the return status of the previous command. + echo "exit $old_status" | . + # Output the original/"old" prompt. + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" +end diff --git a/.venv/bin/icalendar b/.venv/bin/icalendar new file mode 100755 index 0000000..1305ea6 --- /dev/null +++ b/.venv/bin/icalendar @@ -0,0 +1,8 @@ +#!/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from icalendar.cli import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/normalizer b/.venv/bin/normalizer new file mode 100755 index 0000000..b467ebc --- /dev/null +++ b/.venv/bin/normalizer @@ -0,0 +1,8 @@ +#!/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from charset_normalizer.cli import cli_detect +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(cli_detect()) diff --git a/.venv/bin/pip b/.venv/bin/pip new file mode 100755 index 0000000..c8cac8d --- /dev/null +++ b/.venv/bin/pip @@ -0,0 +1,8 @@ +#!/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/pip3 b/.venv/bin/pip3 new file mode 100755 index 0000000..c8cac8d --- /dev/null +++ b/.venv/bin/pip3 @@ -0,0 +1,8 @@ +#!/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/pip3.9 b/.venv/bin/pip3.9 new file mode 100755 index 0000000..c8cac8d --- /dev/null +++ b/.venv/bin/pip3.9 @@ -0,0 +1,8 @@ +#!/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/python b/.venv/bin/python new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/.venv/bin/python @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/.venv/bin/python3 b/.venv/bin/python3 new file mode 120000 index 0000000..f25545f --- /dev/null +++ b/.venv/bin/python3 @@ -0,0 +1 @@ +/Library/Developer/CommandLineTools/usr/bin/python3 \ No newline at end of file diff --git a/.venv/bin/python3.9 b/.venv/bin/python3.9 new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/.venv/bin/python3.9 @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/.venv/bin/x-wr-timezone b/.venv/bin/x-wr-timezone new file mode 100755 index 0000000..b811491 --- /dev/null +++ b/.venv/bin/x-wr-timezone @@ -0,0 +1,8 @@ +#!/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from x_wr_timezone import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/lib/python3.9/site-packages/81d243bd2c585b0f4821__mypyc.cpython-39-darwin.so b/.venv/lib/python3.9/site-packages/81d243bd2c585b0f4821__mypyc.cpython-39-darwin.so new file mode 100755 index 0000000000000000000000000000000000000000..fe8f7778fe4d11bcc272d82c21154c0e3f1df36b GIT binary patch literal 716632 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 z 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
SNrxa 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�Y@)xEeizs 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 (3, 6) else ImportError + raise exc("No module named '_yaml'") +else: + from yaml._yaml import * + import warnings + warnings.warn( + 'The _yaml extension module is now located at yaml._yaml' + ' and its location is subject to change. To use the' + ' LibYAML-based parser and emitter, import from `yaml`:' + ' `from yaml import CLoader as Loader, CDumper as Dumper`.', + DeprecationWarning + ) + del warnings + # Don't `del yaml` here because yaml is actually an existing + # namespace member of _yaml. + +__name__ = '_yaml' +# If the module is top-level (i.e. not a part of any specific package) +# then the attribute should be set to ''. +# https://docs.python.org/3.8/library/types.html +__package__ = '' diff --git a/.venv/lib/python3.9/site-packages/aiohappyeyeballs-2.6.1.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/aiohappyeyeballs-2.6.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohappyeyeballs-2.6.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/aiohappyeyeballs-2.6.1.dist-info/LICENSE b/.venv/lib/python3.9/site-packages/aiohappyeyeballs-2.6.1.dist-info/LICENSE new file mode 100644 index 0000000..f26bcf4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohappyeyeballs-2.6.1.dist-info/LICENSE @@ -0,0 +1,279 @@ +A. HISTORY OF THE SOFTWARE +========================== + +Python was created in the early 1990s by Guido van Rossum at Stichting +Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands +as a successor of a language called ABC. Guido remains Python's +principal author, although it includes many contributions from others. + +In 1995, Guido continued his work on Python at the Corporation for +National Research Initiatives (CNRI, see https://www.cnri.reston.va.us) +in Reston, Virginia where he released several versions of the +software. + +In May 2000, Guido and the Python core development team moved to +BeOpen.com to form the BeOpen PythonLabs team. In October of the same +year, the PythonLabs team moved to Digital Creations, which became +Zope Corporation. In 2001, the Python Software Foundation (PSF, see +https://www.python.org/psf/) was formed, a non-profit organization +created specifically to own Python-related Intellectual Property. +Zope Corporation was a sponsoring member of the PSF. + +All Python releases are Open Source (see https://opensource.org for +the Open Source Definition). Historically, most, but not all, Python +releases have also been GPL-compatible; the table below summarizes +the various releases. + + Release Derived Year Owner GPL- + from compatible? (1) + + 0.9.0 thru 1.2 1991-1995 CWI yes + 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes + 1.6 1.5.2 2000 CNRI no + 2.0 1.6 2000 BeOpen.com no + 1.6.1 1.6 2001 CNRI yes (2) + 2.1 2.0+1.6.1 2001 PSF no + 2.0.1 2.0+1.6.1 2001 PSF yes + 2.1.1 2.1+2.0.1 2001 PSF yes + 2.1.2 2.1.1 2002 PSF yes + 2.1.3 2.1.2 2002 PSF yes + 2.2 and above 2.1.1 2001-now PSF yes + +Footnotes: + +(1) GPL-compatible doesn't mean that we're distributing Python under + the GPL. All Python licenses, unlike the GPL, let you distribute + a modified version without making your changes open source. The + GPL-compatible licenses make it possible to combine Python with + other software that is released under the GPL; the others don't. + +(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, + because its license has a choice of law clause. According to + CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 + is "not incompatible" with the GPL. + +Thanks to the many outside volunteers who have worked under Guido's +direction to make these releases possible. + + +B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON +=============================================================== + +Python software and documentation are licensed under the +Python Software Foundation License Version 2. + +Starting with Python 3.8.6, examples, recipes, and other code in +the documentation are dual licensed under the PSF License Version 2 +and the Zero-Clause BSD license. + +Some software incorporated into Python is under different licenses. +The licenses are listed with code falling under that license. + + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation; +All Rights Reserved" are retained in Python alone or in any derivative version +prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 +------------------------------------------- + +BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 + +1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an +office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the +Individual or Organization ("Licensee") accessing and otherwise using +this software in source or binary form and its associated +documentation ("the Software"). + +2. Subject to the terms and conditions of this BeOpen Python License +Agreement, BeOpen hereby grants Licensee a non-exclusive, +royalty-free, world-wide license to reproduce, analyze, test, perform +and/or display publicly, prepare derivative works, distribute, and +otherwise use the Software alone or in any derivative version, +provided, however, that the BeOpen Python License is retained in the +Software, alone or in any derivative version prepared by Licensee. + +3. BeOpen is making the Software available to Licensee on an "AS IS" +basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE +SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS +AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY +DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +5. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +6. This License Agreement shall be governed by and interpreted in all +respects by the law of the State of California, excluding conflict of +law provisions. Nothing in this License Agreement shall be deemed to +create any relationship of agency, partnership, or joint venture +between BeOpen and Licensee. This License Agreement does not grant +permission to use BeOpen trademarks or trade names in a trademark +sense to endorse or promote products or services of Licensee, or any +third party. As an exception, the "BeOpen Python" logos available at +http://www.pythonlabs.com/logos.html may be used according to the +permissions granted on that web page. + +7. By copying, installing or otherwise using the software, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 +--------------------------------------- + +1. This LICENSE AGREEMENT is between the Corporation for National +Research Initiatives, having an office at 1895 Preston White Drive, +Reston, VA 20191 ("CNRI"), and the Individual or Organization +("Licensee") accessing and otherwise using Python 1.6.1 software in +source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, CNRI +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 1.6.1 +alone or in any derivative version, provided, however, that CNRI's +License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) +1995-2001 Corporation for National Research Initiatives; All Rights +Reserved" are retained in Python 1.6.1 alone or in any derivative +version prepared by Licensee. Alternately, in lieu of CNRI's License +Agreement, Licensee may substitute the following text (omitting the +quotes): "Python 1.6.1 is made available subject to the terms and +conditions in CNRI's License Agreement. This Agreement together with +Python 1.6.1 may be located on the internet using the following +unique, persistent identifier (known as a handle): 1895.22/1013. This +Agreement may also be obtained from a proxy server on the internet +using the following URL: http://hdl.handle.net/1895.22/1013". + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 1.6.1 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 1.6.1. + +4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" +basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. This License Agreement shall be governed by the federal +intellectual property law of the United States, including without +limitation the federal copyright law, and, to the extent such +U.S. federal law does not apply, by the law of the Commonwealth of +Virginia, excluding Virginia's conflict of law provisions. +Notwithstanding the foregoing, with regard to derivative works based +on Python 1.6.1 that incorporate non-separable material that was +previously distributed under the GNU General Public License (GPL), the +law of the Commonwealth of Virginia shall govern this License +Agreement only as to issues arising under or with respect to +Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this +License Agreement shall be deemed to create any relationship of +agency, partnership, or joint venture between CNRI and Licensee. This +License Agreement does not grant permission to use CNRI trademarks or +trade name in a trademark sense to endorse or promote products or +services of Licensee, or any third party. + +8. By clicking on the "ACCEPT" button where indicated, or by copying, +installing or otherwise using Python 1.6.1, Licensee agrees to be +bound by the terms and conditions of this License Agreement. + + ACCEPT + + +CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 +-------------------------------------------------- + +Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, +The Netherlands. All rights reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION +---------------------------------------------------------------------- + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/.venv/lib/python3.9/site-packages/aiohappyeyeballs-2.6.1.dist-info/METADATA b/.venv/lib/python3.9/site-packages/aiohappyeyeballs-2.6.1.dist-info/METADATA new file mode 100644 index 0000000..c632040 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohappyeyeballs-2.6.1.dist-info/METADATA @@ -0,0 +1,123 @@ +Metadata-Version: 2.3 +Name: aiohappyeyeballs +Version: 2.6.1 +Summary: Happy Eyeballs for asyncio +License: PSF-2.0 +Author: J. Nick Koston +Author-email: nick@koston.org +Requires-Python: >=3.9 +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Natural Language :: English +Classifier: Operating System :: OS Independent +Classifier: Topic :: Software Development :: Libraries +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: License :: OSI Approved :: Python Software Foundation License +Project-URL: Bug Tracker, https://github.com/aio-libs/aiohappyeyeballs/issues +Project-URL: Changelog, https://github.com/aio-libs/aiohappyeyeballs/blob/main/CHANGELOG.md +Project-URL: Documentation, https://aiohappyeyeballs.readthedocs.io +Project-URL: Repository, https://github.com/aio-libs/aiohappyeyeballs +Description-Content-Type: text/markdown + +# aiohappyeyeballs + +

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

+

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

+

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

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

En7G~jCBNVB*x5on0 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 csvh7U%)I i).decode('ascii')) + + +cdef inline str http_method_str(int i): + if i < METHODS_COUNT: + return _http_method[i] + else: + return "" + +cdef inline object find_header(bytes raw_header): + cdef Py_ssize_t size + cdef char *buf + cdef int idx + PyBytes_AsStringAndSize(raw_header, &buf, &size) + idx = _find_header.find_header(buf, size) + if idx == -1: + return raw_header.decode('utf-8', 'surrogateescape') + return headers[idx] + + +@cython.freelist(DEFAULT_FREELIST_SIZE) +cdef class RawRequestMessage: + cdef readonly str method + cdef readonly str path + cdef readonly object version # HttpVersion + cdef readonly object headers # CIMultiDict + cdef readonly object raw_headers # tuple + cdef readonly object should_close + cdef readonly object compression + cdef readonly object upgrade + cdef readonly object chunked + cdef readonly object url # yarl.URL + + def __init__(self, method, path, version, headers, raw_headers, + should_close, compression, upgrade, chunked, url): + self.method = method + self.path = path + self.version = version + self.headers = headers + self.raw_headers = raw_headers + self.should_close = should_close + self.compression = compression + self.upgrade = upgrade + self.chunked = chunked + self.url = url + + def __repr__(self): + info = [] + info.append(("method", self.method)) + info.append(("path", self.path)) + info.append(("version", self.version)) + info.append(("headers", self.headers)) + info.append(("raw_headers", self.raw_headers)) + info.append(("should_close", self.should_close)) + info.append(("compression", self.compression)) + info.append(("upgrade", self.upgrade)) + info.append(("chunked", self.chunked)) + info.append(("url", self.url)) + sinfo = ', '.join(name + '=' + repr(val) for name, val in info) + return '' + + def _replace(self, **dct): + cdef RawRequestMessage ret + ret = _new_request_message(self.method, + self.path, + self.version, + self.headers, + self.raw_headers, + self.should_close, + self.compression, + self.upgrade, + self.chunked, + self.url) + if "method" in dct: + ret.method = dct["method"] + if "path" in dct: + ret.path = dct["path"] + if "version" in dct: + ret.version = dct["version"] + if "headers" in dct: + ret.headers = dct["headers"] + if "raw_headers" in dct: + ret.raw_headers = dct["raw_headers"] + if "should_close" in dct: + ret.should_close = dct["should_close"] + if "compression" in dct: + ret.compression = dct["compression"] + if "upgrade" in dct: + ret.upgrade = dct["upgrade"] + if "chunked" in dct: + ret.chunked = dct["chunked"] + if "url" in dct: + ret.url = dct["url"] + return ret + +cdef _new_request_message(str method, + str path, + object version, + object headers, + object raw_headers, + bint should_close, + object compression, + bint upgrade, + bint chunked, + object url): + cdef RawRequestMessage ret + ret = RawRequestMessage.__new__(RawRequestMessage) + ret.method = method + ret.path = path + ret.version = version + ret.headers = headers + ret.raw_headers = raw_headers + ret.should_close = should_close + ret.compression = compression + ret.upgrade = upgrade + ret.chunked = chunked + ret.url = url + return ret + + +@cython.freelist(DEFAULT_FREELIST_SIZE) +cdef class RawResponseMessage: + cdef readonly object version # HttpVersion + cdef readonly int code + cdef readonly str reason + cdef readonly object headers # CIMultiDict + cdef readonly object raw_headers # tuple + cdef readonly object should_close + cdef readonly object compression + cdef readonly object upgrade + cdef readonly object chunked + + def __init__(self, version, code, reason, headers, raw_headers, + should_close, compression, upgrade, chunked): + self.version = version + self.code = code + self.reason = reason + self.headers = headers + self.raw_headers = raw_headers + self.should_close = should_close + self.compression = compression + self.upgrade = upgrade + self.chunked = chunked + + def __repr__(self): + info = [] + info.append(("version", self.version)) + info.append(("code", self.code)) + info.append(("reason", self.reason)) + info.append(("headers", self.headers)) + info.append(("raw_headers", self.raw_headers)) + info.append(("should_close", self.should_close)) + info.append(("compression", self.compression)) + info.append(("upgrade", self.upgrade)) + info.append(("chunked", self.chunked)) + sinfo = ', '.join(name + '=' + repr(val) for name, val in info) + return '' + + +cdef _new_response_message(object version, + int code, + str reason, + object headers, + object raw_headers, + bint should_close, + object compression, + bint upgrade, + bint chunked): + cdef RawResponseMessage ret + ret = RawResponseMessage.__new__(RawResponseMessage) + ret.version = version + ret.code = code + ret.reason = reason + ret.headers = headers + ret.raw_headers = raw_headers + ret.should_close = should_close + ret.compression = compression + ret.upgrade = upgrade + ret.chunked = chunked + return ret + + +@cython.internal +cdef class HttpParser: + + cdef: + cparser.llhttp_t* _cparser + cparser.llhttp_settings_t* _csettings + + bytes _raw_name + object _name + bytes _raw_value + bint _has_value + + object _protocol + object _loop + object _timer + + size_t _max_line_size + size_t _max_field_size + size_t _max_headers + bint _response_with_body + bint _read_until_eof + + bint _started + object _url + bytearray _buf + str _path + str _reason + list _headers + list _raw_headers + bint _upgraded + list _messages + object _payload + bint _payload_error + object _payload_exception + object _last_error + bint _auto_decompress + int _limit + + str _content_encoding + + Py_buffer py_buf + + def __cinit__(self): + self._cparser = \ + PyMem_Malloc(sizeof(cparser.llhttp_t)) + if self._cparser is NULL: + raise MemoryError() + + self._csettings = \ + PyMem_Malloc(sizeof(cparser.llhttp_settings_t)) + if self._csettings is NULL: + raise MemoryError() + + def __dealloc__(self): + PyMem_Free(self._cparser) + PyMem_Free(self._csettings) + + cdef _init( + self, cparser.llhttp_type mode, + object protocol, object loop, int limit, + object timer=None, + size_t max_line_size=8190, size_t max_headers=32768, + size_t max_field_size=8190, payload_exception=None, + bint response_with_body=True, bint read_until_eof=False, + bint auto_decompress=True, + ): + cparser.llhttp_settings_init(self._csettings) + cparser.llhttp_init(self._cparser, mode, self._csettings) + self._cparser.data = self + self._cparser.content_length = 0 + + self._protocol = protocol + self._loop = loop + self._timer = timer + + self._buf = bytearray() + self._payload = None + self._payload_error = 0 + self._payload_exception = payload_exception + self._messages = [] + + self._raw_name = EMPTY_BYTES + self._raw_value = EMPTY_BYTES + self._has_value = False + + self._max_line_size = max_line_size + self._max_headers = max_headers + self._max_field_size = max_field_size + self._response_with_body = response_with_body + self._read_until_eof = read_until_eof + self._upgraded = False + self._auto_decompress = auto_decompress + self._content_encoding = None + + self._csettings.on_url = cb_on_url + self._csettings.on_status = cb_on_status + self._csettings.on_header_field = cb_on_header_field + self._csettings.on_header_value = cb_on_header_value + self._csettings.on_headers_complete = cb_on_headers_complete + self._csettings.on_body = cb_on_body + self._csettings.on_message_begin = cb_on_message_begin + self._csettings.on_message_complete = cb_on_message_complete + self._csettings.on_chunk_header = cb_on_chunk_header + self._csettings.on_chunk_complete = cb_on_chunk_complete + + self._last_error = None + self._limit = limit + + cdef _process_header(self): + cdef str value + if self._raw_name is not EMPTY_BYTES: + name = find_header(self._raw_name) + value = self._raw_value.decode('utf-8', 'surrogateescape') + + self._headers.append((name, value)) + + if name is CONTENT_ENCODING: + self._content_encoding = value + + self._has_value = False + self._raw_headers.append((self._raw_name, self._raw_value)) + self._raw_name = EMPTY_BYTES + self._raw_value = EMPTY_BYTES + + cdef _on_header_field(self, char* at, size_t length): + if self._has_value: + self._process_header() + + if self._raw_name is EMPTY_BYTES: + self._raw_name = at[:length] + else: + self._raw_name += at[:length] + + cdef _on_header_value(self, char* at, size_t length): + if self._raw_value is EMPTY_BYTES: + self._raw_value = at[:length] + else: + self._raw_value += at[:length] + self._has_value = True + + cdef _on_headers_complete(self): + self._process_header() + + should_close = not cparser.llhttp_should_keep_alive(self._cparser) + upgrade = self._cparser.upgrade + chunked = self._cparser.flags & cparser.F_CHUNKED + + raw_headers = tuple(self._raw_headers) + headers = CIMultiDictProxy(CIMultiDict(self._headers)) + + if self._cparser.type == cparser.HTTP_REQUEST: + h_upg = headers.get("upgrade", "") + allowed = upgrade and h_upg.isascii() and h_upg.lower() in ALLOWED_UPGRADES + if allowed or self._cparser.method == cparser.HTTP_CONNECT: + self._upgraded = True + else: + if upgrade and self._cparser.status_code == 101: + self._upgraded = True + + # do not support old websocket spec + if SEC_WEBSOCKET_KEY1 in headers: + raise InvalidHeader(SEC_WEBSOCKET_KEY1) + + encoding = None + enc = self._content_encoding + if enc is not None: + self._content_encoding = None + if enc.isascii() and enc.lower() in {"gzip", "deflate", "br", "zstd"}: + encoding = enc + + if self._cparser.type == cparser.HTTP_REQUEST: + method = http_method_str(self._cparser.method) + msg = _new_request_message( + method, self._path, + self.http_version(), headers, raw_headers, + should_close, encoding, upgrade, chunked, self._url) + else: + msg = _new_response_message( + self.http_version(), self._cparser.status_code, self._reason, + headers, raw_headers, should_close, encoding, + upgrade, chunked) + + if ( + ULLONG_MAX > self._cparser.content_length > 0 or chunked or + self._cparser.method == cparser.HTTP_CONNECT or + (self._cparser.status_code >= 199 and + self._cparser.content_length == 0 and + self._read_until_eof) + ): + payload = StreamReader( + self._protocol, timer=self._timer, loop=self._loop, + limit=self._limit) + else: + payload = EMPTY_PAYLOAD + + self._payload = payload + if encoding is not None and self._auto_decompress: + self._payload = DeflateBuffer(payload, encoding) + + if not self._response_with_body: + payload = EMPTY_PAYLOAD + + self._messages.append((msg, payload)) + + cdef _on_message_complete(self): + self._payload.feed_eof() + self._payload = None + + cdef _on_chunk_header(self): + self._payload.begin_http_chunk_receiving() + + cdef _on_chunk_complete(self): + self._payload.end_http_chunk_receiving() + + cdef object _on_status_complete(self): + pass + + cdef inline http_version(self): + cdef cparser.llhttp_t* parser = self._cparser + + if parser.http_major == 1: + if parser.http_minor == 0: + return HttpVersion10 + elif parser.http_minor == 1: + return HttpVersion11 + + return HttpVersion(parser.http_major, parser.http_minor) + + ### Public API ### + + def feed_eof(self): + cdef bytes desc + + if self._payload is not None: + if self._cparser.flags & cparser.F_CHUNKED: + raise TransferEncodingError( + "Not enough data to satisfy transfer length header.") + elif self._cparser.flags & cparser.F_CONTENT_LENGTH: + raise ContentLengthError( + "Not enough data to satisfy content length header.") + elif cparser.llhttp_get_errno(self._cparser) != cparser.HPE_OK: + desc = cparser.llhttp_get_error_reason(self._cparser) + raise PayloadEncodingError(desc.decode('latin-1')) + else: + self._payload.feed_eof() + elif self._started: + self._on_headers_complete() + if self._messages: + return self._messages[-1][0] + + def feed_data(self, data): + cdef: + size_t data_len + size_t nb + cdef cparser.llhttp_errno_t errno + + PyObject_GetBuffer(data, &self.py_buf, PyBUF_SIMPLE) + data_len = self.py_buf.len + + errno = cparser.llhttp_execute( + self._cparser, + self.py_buf.buf, + data_len) + + if errno is cparser.HPE_PAUSED_UPGRADE: + cparser.llhttp_resume_after_upgrade(self._cparser) + + nb = cparser.llhttp_get_error_pos(self._cparser) - self.py_buf.buf + + PyBuffer_Release(&self.py_buf) + + if errno not in (cparser.HPE_OK, cparser.HPE_PAUSED_UPGRADE): + if self._payload_error == 0: + if self._last_error is not None: + ex = self._last_error + self._last_error = None + else: + after = cparser.llhttp_get_error_pos(self._cparser) + before = data[:after - self.py_buf.buf] + after_b = after.split(b"\r\n", 1)[0] + before = before.rsplit(b"\r\n", 1)[-1] + data = before + after_b + pointer = " " * (len(repr(before))-1) + "^" + ex = parser_error_from_errno(self._cparser, data, pointer) + self._payload = None + raise ex + + if self._messages: + messages = self._messages + self._messages = [] + else: + messages = () + + if self._upgraded: + return messages, True, data[nb:] + else: + return messages, False, b"" + + def set_upgraded(self, val): + self._upgraded = val + + +cdef class HttpRequestParser(HttpParser): + + def __init__( + self, protocol, loop, int limit, timer=None, + size_t max_line_size=8190, size_t max_headers=32768, + size_t max_field_size=8190, payload_exception=None, + bint response_with_body=True, bint read_until_eof=False, + bint auto_decompress=True, + ): + self._init(cparser.HTTP_REQUEST, protocol, loop, limit, timer, + max_line_size, max_headers, max_field_size, + payload_exception, response_with_body, read_until_eof, + auto_decompress) + + cdef object _on_status_complete(self): + cdef int idx1, idx2 + if not self._buf: + return + self._path = self._buf.decode('utf-8', 'surrogateescape') + try: + idx3 = len(self._path) + if self._cparser.method == cparser.HTTP_CONNECT: + # authority-form, + # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.3 + self._url = URL.build(authority=self._path, encoded=True) + elif idx3 > 1 and self._path[0] == '/': + # origin-form, + # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.1 + idx1 = self._path.find("?") + if idx1 == -1: + query = "" + idx2 = self._path.find("#") + if idx2 == -1: + path = self._path + fragment = "" + else: + path = self._path[0: idx2] + fragment = self._path[idx2+1:] + + else: + path = self._path[0:idx1] + idx1 += 1 + idx2 = self._path.find("#", idx1+1) + if idx2 == -1: + query = self._path[idx1:] + fragment = "" + else: + query = self._path[idx1: idx2] + fragment = self._path[idx2+1:] + + self._url = URL.build( + path=path, + query_string=query, + fragment=fragment, + encoded=True, + ) + else: + # absolute-form for proxy maybe, + # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.2 + self._url = URL(self._path, encoded=True) + finally: + PyByteArray_Resize(self._buf, 0) + + +cdef class HttpResponseParser(HttpParser): + + def __init__( + self, protocol, loop, int limit, timer=None, + size_t max_line_size=8190, size_t max_headers=32768, + size_t max_field_size=8190, payload_exception=None, + bint response_with_body=True, bint read_until_eof=False, + bint auto_decompress=True + ): + self._init(cparser.HTTP_RESPONSE, protocol, loop, limit, timer, + max_line_size, max_headers, max_field_size, + payload_exception, response_with_body, read_until_eof, + auto_decompress) + # Use strict parsing on dev mode, so users are warned about broken servers. + if not DEBUG: + cparser.llhttp_set_lenient_headers(self._cparser, 1) + cparser.llhttp_set_lenient_optional_cr_before_lf(self._cparser, 1) + cparser.llhttp_set_lenient_spaces_after_chunk_size(self._cparser, 1) + + cdef object _on_status_complete(self): + if self._buf: + self._reason = self._buf.decode('utf-8', 'surrogateescape') + PyByteArray_Resize(self._buf, 0) + else: + self._reason = self._reason or '' + +cdef int cb_on_message_begin(cparser.llhttp_t* parser) except -1: + cdef HttpParser pyparser = parser.data + + pyparser._started = True + pyparser._headers = [] + pyparser._raw_headers = [] + PyByteArray_Resize(pyparser._buf, 0) + pyparser._path = None + pyparser._reason = None + return 0 + + +cdef int cb_on_url(cparser.llhttp_t* parser, + const char *at, size_t length) except -1: + cdef HttpParser pyparser = parser.data + try: + if length > pyparser._max_line_size: + raise LineTooLong( + 'Status line is too long', pyparser._max_line_size, length) + extend(pyparser._buf, at, length) + except BaseException as ex: + pyparser._last_error = ex + return -1 + else: + return 0 + + +cdef int cb_on_status(cparser.llhttp_t* parser, + const char *at, size_t length) except -1: + cdef HttpParser pyparser = parser.data + cdef str reason + try: + if length > pyparser._max_line_size: + raise LineTooLong( + 'Status line is too long', pyparser._max_line_size, length) + extend(pyparser._buf, at, length) + except BaseException as ex: + pyparser._last_error = ex + return -1 + else: + return 0 + + +cdef int cb_on_header_field(cparser.llhttp_t* parser, + const char *at, size_t length) except -1: + cdef HttpParser pyparser = parser.data + cdef Py_ssize_t size + try: + pyparser._on_status_complete() + size = len(pyparser._raw_name) + length + if size > pyparser._max_field_size: + raise LineTooLong( + 'Header name is too long', pyparser._max_field_size, size) + pyparser._on_header_field(at, length) + except BaseException as ex: + pyparser._last_error = ex + return -1 + else: + return 0 + + +cdef int cb_on_header_value(cparser.llhttp_t* parser, + const char *at, size_t length) except -1: + cdef HttpParser pyparser = parser.data + cdef Py_ssize_t size + try: + size = len(pyparser._raw_value) + length + if size > pyparser._max_field_size: + raise LineTooLong( + 'Header value is too long', pyparser._max_field_size, size) + pyparser._on_header_value(at, length) + except BaseException as ex: + pyparser._last_error = ex + return -1 + else: + return 0 + + +cdef int cb_on_headers_complete(cparser.llhttp_t* parser) except -1: + cdef HttpParser pyparser = parser.data + try: + pyparser._on_status_complete() + pyparser._on_headers_complete() + except BaseException as exc: + pyparser._last_error = exc + return -1 + else: + if pyparser._upgraded or pyparser._cparser.method == cparser.HTTP_CONNECT: + return 2 + else: + return 0 + + +cdef int cb_on_body(cparser.llhttp_t* parser, + const char *at, size_t length) except -1: + cdef HttpParser pyparser = parser.data + cdef bytes body = at[:length] + try: + pyparser._payload.feed_data(body, length) + except BaseException as underlying_exc: + reraised_exc = underlying_exc + if pyparser._payload_exception is not None: + reraised_exc = pyparser._payload_exception(str(underlying_exc)) + + set_exception(pyparser._payload, reraised_exc, underlying_exc) + + pyparser._payload_error = 1 + return -1 + else: + return 0 + + +cdef int cb_on_message_complete(cparser.llhttp_t* parser) except -1: + cdef HttpParser pyparser = parser.data + try: + pyparser._started = False + pyparser._on_message_complete() + except BaseException as exc: + pyparser._last_error = exc + return -1 + else: + return 0 + + +cdef int cb_on_chunk_header(cparser.llhttp_t* parser) except -1: + cdef HttpParser pyparser = parser.data + try: + pyparser._on_chunk_header() + except BaseException as exc: + pyparser._last_error = exc + return -1 + else: + return 0 + + +cdef int cb_on_chunk_complete(cparser.llhttp_t* parser) except -1: + cdef HttpParser pyparser = parser.data + try: + pyparser._on_chunk_complete() + except BaseException as exc: + pyparser._last_error = exc + return -1 + else: + return 0 + + +cdef parser_error_from_errno(cparser.llhttp_t* parser, data, pointer): + cdef cparser.llhttp_errno_t errno = cparser.llhttp_get_errno(parser) + cdef bytes desc = cparser.llhttp_get_error_reason(parser) + + err_msg = "{}:\n\n {!r}\n {}".format(desc.decode("latin-1"), data, pointer) + + if errno in {cparser.HPE_CB_MESSAGE_BEGIN, + cparser.HPE_CB_HEADERS_COMPLETE, + cparser.HPE_CB_MESSAGE_COMPLETE, + cparser.HPE_CB_CHUNK_HEADER, + cparser.HPE_CB_CHUNK_COMPLETE, + cparser.HPE_INVALID_CONSTANT, + cparser.HPE_INVALID_HEADER_TOKEN, + cparser.HPE_INVALID_CONTENT_LENGTH, + cparser.HPE_INVALID_CHUNK_SIZE, + cparser.HPE_INVALID_EOF_STATE, + cparser.HPE_INVALID_TRANSFER_ENCODING}: + return BadHttpMessage(err_msg) + elif errno == cparser.HPE_INVALID_METHOD: + return BadHttpMethod(error=err_msg) + elif errno in {cparser.HPE_INVALID_STATUS, + cparser.HPE_INVALID_VERSION}: + return BadStatusLine(error=err_msg) + elif errno == cparser.HPE_INVALID_URL: + return InvalidURLError(err_msg) + + return BadHttpMessage(err_msg) diff --git a/.venv/lib/python3.9/site-packages/aiohttp/_http_writer.cpython-39-darwin.so b/.venv/lib/python3.9/site-packages/aiohttp/_http_writer.cpython-39-darwin.so new file mode 100755 index 0000000000000000000000000000000000000000..c1ed806b334e2fb57254d158fa4f379c8e87e1c6 GIT binary patch literal 92416 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 V@biq-*Z;XWUW$zDn!e6p_z#wFcT4~P literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/aiohttp/_http_writer.pyx b/.venv/lib/python3.9/site-packages/aiohttp/_http_writer.pyx new file mode 100644 index 0000000..7989c18 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/_http_writer.pyx @@ -0,0 +1,162 @@ +from cpython.bytes cimport PyBytes_FromStringAndSize +from cpython.exc cimport PyErr_NoMemory +from cpython.mem cimport PyMem_Free, PyMem_Malloc, PyMem_Realloc +from cpython.object cimport PyObject_Str +from libc.stdint cimport uint8_t, uint64_t +from libc.string cimport memcpy + +from multidict import istr + +DEF BUF_SIZE = 16 * 1024 # 16KiB + +cdef object _istr = istr + + +# ----------------- writer --------------------------- + +cdef struct Writer: + char *buf + Py_ssize_t size + Py_ssize_t pos + bint heap_allocated + +cdef inline void _init_writer(Writer* writer, char *buf): + writer.buf = buf + writer.size = BUF_SIZE + writer.pos = 0 + writer.heap_allocated = 0 + + +cdef inline void _release_writer(Writer* writer): + if writer.heap_allocated: + PyMem_Free(writer.buf) + + +cdef inline int _write_byte(Writer* writer, uint8_t ch): + cdef char * buf + cdef Py_ssize_t size + + if writer.pos == writer.size: + # reallocate + size = writer.size + BUF_SIZE + if not writer.heap_allocated: + buf = PyMem_Malloc(size) + if buf == NULL: + PyErr_NoMemory() + return -1 + memcpy(buf, writer.buf, writer.size) + else: + buf = PyMem_Realloc(writer.buf, size) + if buf == NULL: + PyErr_NoMemory() + return -1 + writer.buf = buf + writer.size = size + writer.heap_allocated = 1 + writer.buf[writer.pos] = ch + writer.pos += 1 + return 0 + + +cdef inline int _write_utf8(Writer* writer, Py_UCS4 symbol): + cdef uint64_t utf = symbol + + if utf < 0x80: + return _write_byte(writer, utf) + elif utf < 0x800: + if _write_byte(writer, (0xc0 | (utf >> 6))) < 0: + return -1 + return _write_byte(writer, (0x80 | (utf & 0x3f))) + elif 0xD800 <= utf <= 0xDFFF: + # surogate pair, ignored + return 0 + elif utf < 0x10000: + if _write_byte(writer, (0xe0 | (utf >> 12))) < 0: + return -1 + if _write_byte(writer, (0x80 | ((utf >> 6) & 0x3f))) < 0: + return -1 + return _write_byte(writer, (0x80 | (utf & 0x3f))) + elif utf > 0x10FFFF: + # symbol is too large + return 0 + else: + if _write_byte(writer, (0xf0 | (utf >> 18))) < 0: + return -1 + if _write_byte(writer, + (0x80 | ((utf >> 12) & 0x3f))) < 0: + return -1 + if _write_byte(writer, + (0x80 | ((utf >> 6) & 0x3f))) < 0: + return -1 + return _write_byte(writer, (0x80 | (utf & 0x3f))) + + +cdef inline int _write_str(Writer* writer, str s): + cdef Py_UCS4 ch + for ch in s: + if _write_utf8(writer, ch) < 0: + return -1 + + +cdef inline int _write_str_raise_on_nlcr(Writer* writer, object s): + cdef Py_UCS4 ch + cdef str out_str + if type(s) is str: + out_str = s + elif type(s) is _istr: + out_str = PyObject_Str(s) + elif not isinstance(s, str): + raise TypeError("Cannot serialize non-str key {!r}".format(s)) + else: + out_str = str(s) + + for ch in out_str: + if ch == 0x0D or ch == 0x0A: + raise ValueError( + "Newline or carriage return detected in headers. " + "Potential header injection attack." + ) + if _write_utf8(writer, ch) < 0: + return -1 + + +# --------------- _serialize_headers ---------------------- + +def _serialize_headers(str status_line, headers): + cdef Writer writer + cdef object key + cdef object val + cdef char buf[BUF_SIZE] + + _init_writer(&writer, buf) + + try: + if _write_str(&writer, status_line) < 0: + raise + if _write_byte(&writer, b'\r') < 0: + raise + if _write_byte(&writer, b'\n') < 0: + raise + + for key, val in headers.items(): + if _write_str_raise_on_nlcr(&writer, key) < 0: + raise + if _write_byte(&writer, b':') < 0: + raise + if _write_byte(&writer, b' ') < 0: + raise + if _write_str_raise_on_nlcr(&writer, val) < 0: + raise + if _write_byte(&writer, b'\r') < 0: + raise + if _write_byte(&writer, b'\n') < 0: + raise + + if _write_byte(&writer, b'\r') < 0: + raise + if _write_byte(&writer, b'\n') < 0: + raise + + return PyBytes_FromStringAndSize(writer.buf, writer.pos) + finally: + _release_writer(&writer) diff --git a/.venv/lib/python3.9/site-packages/aiohttp/_websocket/.hash/mask.pxd.hash b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/.hash/mask.pxd.hash new file mode 100644 index 0000000..f782cc2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/.hash/mask.pxd.hash @@ -0,0 +1 @@ +b01999d409b29bd916e067bc963d5f2d9ee63cfc9ae0bccb769910131417bf93 /Users/runner/work/aiohttp/aiohttp/aiohttp/_websocket/mask.pxd diff --git a/.venv/lib/python3.9/site-packages/aiohttp/_websocket/.hash/mask.pyx.hash b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/.hash/mask.pyx.hash new file mode 100644 index 0000000..cfb49b8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/.hash/mask.pyx.hash @@ -0,0 +1 @@ +0478ceb55d0ed30ef1a7da742cd003449bc69a07cf9fdb06789bd2b347cbfffe /Users/runner/work/aiohttp/aiohttp/aiohttp/_websocket/mask.pyx diff --git a/.venv/lib/python3.9/site-packages/aiohttp/_websocket/.hash/reader_c.pxd.hash b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/.hash/reader_c.pxd.hash new file mode 100644 index 0000000..e24cce4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/.hash/reader_c.pxd.hash @@ -0,0 +1 @@ +9e5fe78ed0ebce5414d2b8e01868d90c1facc20b84d2d5ff6c23e86e44a155ae /Users/runner/work/aiohttp/aiohttp/aiohttp/_websocket/reader_c.pxd diff --git a/.venv/lib/python3.9/site-packages/aiohttp/_websocket/__init__.py b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/__init__.py new file mode 100644 index 0000000..836257cc --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/__init__.py @@ -0,0 +1 @@ +"""WebSocket protocol versions 13 and 8.""" diff --git a/.venv/lib/python3.9/site-packages/aiohttp/_websocket/helpers.py b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/helpers.py new file mode 100644 index 0000000..0bb58df --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/helpers.py @@ -0,0 +1,147 @@ +"""Helpers for WebSocket protocol versions 13 and 8.""" + +import functools +import re +from struct import Struct +from typing import TYPE_CHECKING, Final, List, Optional, Pattern, Tuple + +from ..helpers import NO_EXTENSIONS +from .models import WSHandshakeError + +UNPACK_LEN3 = Struct("!Q").unpack_from +UNPACK_CLOSE_CODE = Struct("!H").unpack +PACK_LEN1 = Struct("!BB").pack +PACK_LEN2 = Struct("!BBH").pack +PACK_LEN3 = Struct("!BBQ").pack +PACK_CLOSE_CODE = Struct("!H").pack +PACK_RANDBITS = Struct("!L").pack +MSG_SIZE: Final[int] = 2**14 +MASK_LEN: Final[int] = 4 + +WS_KEY: Final[bytes] = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + + +# Used by _websocket_mask_python +@functools.lru_cache +def _xor_table() -> List[bytes]: + return [bytes(a ^ b for a in range(256)) for b in range(256)] + + +def _websocket_mask_python(mask: bytes, data: bytearray) -> None: + """Websocket masking function. + + `mask` is a `bytes` object of length 4; `data` is a `bytearray` + object of any length. The contents of `data` are masked with `mask`, + as specified in section 5.3 of RFC 6455. + + Note that this function mutates the `data` argument. + + This pure-python implementation may be replaced by an optimized + version when available. + + """ + assert isinstance(data, bytearray), data + assert len(mask) == 4, mask + + if data: + _XOR_TABLE = _xor_table() + a, b, c, d = (_XOR_TABLE[n] for n in mask) + data[::4] = data[::4].translate(a) + data[1::4] = data[1::4].translate(b) + data[2::4] = data[2::4].translate(c) + data[3::4] = data[3::4].translate(d) + + +if TYPE_CHECKING or NO_EXTENSIONS: # pragma: no cover + websocket_mask = _websocket_mask_python +else: + try: + from .mask import _websocket_mask_cython # type: ignore[import-not-found] + + websocket_mask = _websocket_mask_cython + except ImportError: # pragma: no cover + websocket_mask = _websocket_mask_python + + +_WS_EXT_RE: Final[Pattern[str]] = re.compile( + r"^(?:;\s*(?:" + r"(server_no_context_takeover)|" + r"(client_no_context_takeover)|" + r"(server_max_window_bits(?:=(\d+))?)|" + r"(client_max_window_bits(?:=(\d+))?)))*$" +) + +_WS_EXT_RE_SPLIT: Final[Pattern[str]] = re.compile(r"permessage-deflate([^,]+)?") + + +def ws_ext_parse(extstr: Optional[str], isserver: bool = False) -> Tuple[int, bool]: + if not extstr: + return 0, False + + compress = 0 + notakeover = False + for ext in _WS_EXT_RE_SPLIT.finditer(extstr): + defext = ext.group(1) + # Return compress = 15 when get `permessage-deflate` + if not defext: + compress = 15 + break + match = _WS_EXT_RE.match(defext) + if match: + compress = 15 + if isserver: + # Server never fail to detect compress handshake. + # Server does not need to send max wbit to client + if match.group(4): + compress = int(match.group(4)) + # Group3 must match if group4 matches + # Compress wbit 8 does not support in zlib + # If compress level not support, + # CONTINUE to next extension + if compress > 15 or compress < 9: + compress = 0 + continue + if match.group(1): + notakeover = True + # Ignore regex group 5 & 6 for client_max_window_bits + break + else: + if match.group(6): + compress = int(match.group(6)) + # Group5 must match if group6 matches + # Compress wbit 8 does not support in zlib + # If compress level not support, + # FAIL the parse progress + if compress > 15 or compress < 9: + raise WSHandshakeError("Invalid window size") + if match.group(2): + notakeover = True + # Ignore regex group 5 & 6 for client_max_window_bits + break + # Return Fail if client side and not match + elif not isserver: + raise WSHandshakeError("Extension for deflate not supported" + ext.group(1)) + + return compress, notakeover + + +def ws_ext_gen( + compress: int = 15, isserver: bool = False, server_notakeover: bool = False +) -> str: + # client_notakeover=False not used for server + # compress wbit 8 does not support in zlib + if compress < 9 or compress > 15: + raise ValueError( + "Compress wbits must between 9 and 15, zlib does not support wbits=8" + ) + enabledext = ["permessage-deflate"] + if not isserver: + enabledext.append("client_max_window_bits") + + if compress < 15: + enabledext.append("server_max_window_bits=" + str(compress)) + if server_notakeover: + enabledext.append("server_no_context_takeover") + # if client_notakeover: + # enabledext.append('client_no_context_takeover') + return "; ".join(enabledext) diff --git a/.venv/lib/python3.9/site-packages/aiohttp/_websocket/mask.cpython-39-darwin.so b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/mask.cpython-39-darwin.so new file mode 100755 index 0000000000000000000000000000000000000000..5cbb2c0cb88b83ce3a943e0e8e0f89b32ad85b73 GIT binary patch literal 85880 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 a{;K=c>eG%zL-KwXIbL&P@f}-zLi|4i*}hN! literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/aiohttp/_websocket/mask.pxd b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/mask.pxd new file mode 100644 index 0000000..90983de --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/mask.pxd @@ -0,0 +1,3 @@ +"""Cython declarations for websocket masking.""" + +cpdef void _websocket_mask_cython(bytes mask, bytearray data) diff --git a/.venv/lib/python3.9/site-packages/aiohttp/_websocket/mask.pyx b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/mask.pyx new file mode 100644 index 0000000..2d956c8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/mask.pyx @@ -0,0 +1,48 @@ +from cpython cimport PyBytes_AsString + + +#from cpython cimport PyByteArray_AsString # cython still not exports that +cdef extern from "Python.h": + char* PyByteArray_AsString(bytearray ba) except NULL + +from libc.stdint cimport uint32_t, uint64_t, uintmax_t + + +cpdef void _websocket_mask_cython(bytes mask, bytearray data): + """Note, this function mutates its `data` argument + """ + cdef: + Py_ssize_t data_len, i + # bit operations on signed integers are implementation-specific + unsigned char * in_buf + const unsigned char * mask_buf + uint32_t uint32_msk + uint64_t uint64_msk + + assert len(mask) == 4 + + data_len = len(data) + in_buf = PyByteArray_AsString(data) + mask_buf = PyBytes_AsString(mask) + uint32_msk = (mask_buf)[0] + + # TODO: align in_data ptr to achieve even faster speeds + # does it need in python ?! malloc() always aligns to sizeof(long) bytes + + if sizeof(size_t) >= 8: + uint64_msk = uint32_msk + uint64_msk = (uint64_msk << 32) | uint32_msk + + while data_len >= 8: + (in_buf)[0] ^= uint64_msk + in_buf += 8 + data_len -= 8 + + + while data_len >= 4: + (in_buf)[0] ^= uint32_msk + in_buf += 4 + data_len -= 4 + + for i in range(0, data_len): + in_buf[i] ^= mask_buf[i] diff --git a/.venv/lib/python3.9/site-packages/aiohttp/_websocket/models.py b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/models.py new file mode 100644 index 0000000..7e89b96 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/models.py @@ -0,0 +1,84 @@ +"""Models for WebSocket protocol versions 13 and 8.""" + +import json +from enum import IntEnum +from typing import Any, Callable, Final, NamedTuple, Optional, cast + +WS_DEFLATE_TRAILING: Final[bytes] = bytes([0x00, 0x00, 0xFF, 0xFF]) + + +class WSCloseCode(IntEnum): + OK = 1000 + GOING_AWAY = 1001 + PROTOCOL_ERROR = 1002 + UNSUPPORTED_DATA = 1003 + ABNORMAL_CLOSURE = 1006 + INVALID_TEXT = 1007 + POLICY_VIOLATION = 1008 + MESSAGE_TOO_BIG = 1009 + MANDATORY_EXTENSION = 1010 + INTERNAL_ERROR = 1011 + SERVICE_RESTART = 1012 + TRY_AGAIN_LATER = 1013 + BAD_GATEWAY = 1014 + + +class WSMsgType(IntEnum): + # websocket spec types + CONTINUATION = 0x0 + TEXT = 0x1 + BINARY = 0x2 + PING = 0x9 + PONG = 0xA + CLOSE = 0x8 + + # aiohttp specific types + CLOSING = 0x100 + CLOSED = 0x101 + ERROR = 0x102 + + text = TEXT + binary = BINARY + ping = PING + pong = PONG + close = CLOSE + closing = CLOSING + closed = CLOSED + error = ERROR + + +class WSMessage(NamedTuple): + type: WSMsgType + # To type correctly, this would need some kind of tagged union for each type. + data: Any + extra: Optional[str] + + def json(self, *, loads: Callable[[Any], Any] = json.loads) -> Any: + """Return parsed JSON data. + + .. versionadded:: 0.22 + """ + return loads(self.data) + + +# Constructing the tuple directly to avoid the overhead of +# the lambda and arg processing since NamedTuples are constructed +# with a run time built lambda +# https://github.com/python/cpython/blob/d83fcf8371f2f33c7797bc8f5423a8bca8c46e5c/Lib/collections/__init__.py#L441 +WS_CLOSED_MESSAGE = tuple.__new__(WSMessage, (WSMsgType.CLOSED, None, None)) +WS_CLOSING_MESSAGE = tuple.__new__(WSMessage, (WSMsgType.CLOSING, None, None)) + + +class WebSocketError(Exception): + """WebSocket protocol parser error.""" + + def __init__(self, code: int, message: str) -> None: + self.code = code + super().__init__(code, message) + + def __str__(self) -> str: + return cast(str, self.args[1]) + + +class WSHandshakeError(Exception): + """WebSocket protocol handshake error.""" diff --git a/.venv/lib/python3.9/site-packages/aiohttp/_websocket/reader.py b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/reader.py new file mode 100644 index 0000000..23f3226 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/reader.py @@ -0,0 +1,31 @@ +"""Reader for WebSocket protocol versions 13 and 8.""" + +from typing import TYPE_CHECKING + +from ..helpers import NO_EXTENSIONS + +if TYPE_CHECKING or NO_EXTENSIONS: # pragma: no cover + from .reader_py import ( + WebSocketDataQueue as WebSocketDataQueuePython, + WebSocketReader as WebSocketReaderPython, + ) + + WebSocketReader = WebSocketReaderPython + WebSocketDataQueue = WebSocketDataQueuePython +else: + try: + from .reader_c import ( # type: ignore[import-not-found] + WebSocketDataQueue as WebSocketDataQueueCython, + WebSocketReader as WebSocketReaderCython, + ) + + WebSocketReader = WebSocketReaderCython + WebSocketDataQueue = WebSocketDataQueueCython + except ImportError: # pragma: no cover + from .reader_py import ( + WebSocketDataQueue as WebSocketDataQueuePython, + WebSocketReader as WebSocketReaderPython, + ) + + WebSocketReader = WebSocketReaderPython + WebSocketDataQueue = WebSocketDataQueuePython diff --git a/.venv/lib/python3.9/site-packages/aiohttp/_websocket/reader_c.cpython-39-darwin.so b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/reader_c.cpython-39-darwin.so new file mode 100755 index 0000000000000000000000000000000000000000..9c7200609dae9e56b6f29e9497e205b46495fa29 GIT binary patch literal 256144 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 TtzAwS+PBmBCHH=AtpffZfWjMh literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/aiohttp/_websocket/reader_c.pxd b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/reader_c.pxd new file mode 100644 index 0000000..a7620d8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/reader_c.pxd @@ -0,0 +1,110 @@ +import cython + +from .mask cimport _websocket_mask_cython as websocket_mask + + +cdef unsigned int READ_HEADER +cdef unsigned int READ_PAYLOAD_LENGTH +cdef unsigned int READ_PAYLOAD_MASK +cdef unsigned int READ_PAYLOAD + +cdef int OP_CODE_NOT_SET +cdef int OP_CODE_CONTINUATION +cdef int OP_CODE_TEXT +cdef int OP_CODE_BINARY +cdef int OP_CODE_CLOSE +cdef int OP_CODE_PING +cdef int OP_CODE_PONG + +cdef int COMPRESSED_NOT_SET +cdef int COMPRESSED_FALSE +cdef int COMPRESSED_TRUE + +cdef object UNPACK_LEN3 +cdef object UNPACK_CLOSE_CODE +cdef object TUPLE_NEW + +cdef object WSMsgType +cdef object WSMessage + +cdef object WS_MSG_TYPE_TEXT +cdef object WS_MSG_TYPE_BINARY + +cdef set ALLOWED_CLOSE_CODES +cdef set MESSAGE_TYPES_WITH_CONTENT + +cdef tuple EMPTY_FRAME +cdef tuple EMPTY_FRAME_ERROR + +cdef class WebSocketDataQueue: + + cdef unsigned int _size + cdef public object _protocol + cdef unsigned int _limit + cdef object _loop + cdef bint _eof + cdef object _waiter + cdef object _exception + cdef public object _buffer + cdef object _get_buffer + cdef object _put_buffer + + cdef void _release_waiter(self) + + cpdef void feed_data(self, object data, unsigned int size) + + @cython.locals(size="unsigned int") + cdef _read_from_buffer(self) + +cdef class WebSocketReader: + + cdef WebSocketDataQueue queue + cdef unsigned int _max_msg_size + + cdef Exception _exc + cdef bytearray _partial + cdef unsigned int _state + + cdef int _opcode + cdef bint _frame_fin + cdef int _frame_opcode + cdef list _payload_fragments + cdef Py_ssize_t _frame_payload_len + + cdef bytes _tail + cdef bint _has_mask + cdef bytes _frame_mask + cdef Py_ssize_t _payload_bytes_to_read + cdef unsigned int _payload_len_flag + cdef int _compressed + cdef object _decompressobj + cdef bint _compress + + cpdef tuple feed_data(self, object data) + + @cython.locals( + is_continuation=bint, + fin=bint, + has_partial=bint, + payload_merged=bytes, + ) + cpdef void _handle_frame(self, bint fin, int opcode, object payload, int compressed) except * + + @cython.locals( + start_pos=Py_ssize_t, + data_len=Py_ssize_t, + length=Py_ssize_t, + chunk_size=Py_ssize_t, + chunk_len=Py_ssize_t, + data_len=Py_ssize_t, + data_cstr="const unsigned char *", + first_byte="unsigned char", + second_byte="unsigned char", + f_start_pos=Py_ssize_t, + f_end_pos=Py_ssize_t, + has_mask=bint, + fin=bint, + had_fragments=Py_ssize_t, + payload_bytearray=bytearray, + ) + cpdef void _feed_data(self, bytes data) except * diff --git a/.venv/lib/python3.9/site-packages/aiohttp/_websocket/reader_c.py b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/reader_c.py new file mode 100644 index 0000000..5166d7e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/reader_c.py @@ -0,0 +1,478 @@ +"""Reader for WebSocket protocol versions 13 and 8.""" + +import asyncio +import builtins +from collections import deque +from typing import Deque, Final, Optional, Set, Tuple, Union + +from ..base_protocol import BaseProtocol +from ..compression_utils import ZLibDecompressor +from ..helpers import _EXC_SENTINEL, set_exception +from ..streams import EofStream +from .helpers import UNPACK_CLOSE_CODE, UNPACK_LEN3, websocket_mask +from .models import ( + WS_DEFLATE_TRAILING, + WebSocketError, + WSCloseCode, + WSMessage, + WSMsgType, +) + +ALLOWED_CLOSE_CODES: Final[Set[int]] = {int(i) for i in WSCloseCode} + +# States for the reader, used to parse the WebSocket frame +# integer values are used so they can be cythonized +READ_HEADER = 1 +READ_PAYLOAD_LENGTH = 2 +READ_PAYLOAD_MASK = 3 +READ_PAYLOAD = 4 + +WS_MSG_TYPE_BINARY = WSMsgType.BINARY +WS_MSG_TYPE_TEXT = WSMsgType.TEXT + +# WSMsgType values unpacked so they can by cythonized to ints +OP_CODE_NOT_SET = -1 +OP_CODE_CONTINUATION = WSMsgType.CONTINUATION.value +OP_CODE_TEXT = WSMsgType.TEXT.value +OP_CODE_BINARY = WSMsgType.BINARY.value +OP_CODE_CLOSE = WSMsgType.CLOSE.value +OP_CODE_PING = WSMsgType.PING.value +OP_CODE_PONG = WSMsgType.PONG.value + +EMPTY_FRAME_ERROR = (True, b"") +EMPTY_FRAME = (False, b"") + +COMPRESSED_NOT_SET = -1 +COMPRESSED_FALSE = 0 +COMPRESSED_TRUE = 1 + +TUPLE_NEW = tuple.__new__ + +cython_int = int # Typed to int in Python, but cython with use a signed int in the pxd + + +class WebSocketDataQueue: + """WebSocketDataQueue resumes and pauses an underlying stream. + + It is a destination for WebSocket data. + """ + + def __init__( + self, protocol: BaseProtocol, limit: int, *, loop: asyncio.AbstractEventLoop + ) -> None: + self._size = 0 + self._protocol = protocol + self._limit = limit * 2 + self._loop = loop + self._eof = False + self._waiter: Optional[asyncio.Future[None]] = None + self._exception: Union[BaseException, None] = None + self._buffer: Deque[Tuple[WSMessage, int]] = deque() + self._get_buffer = self._buffer.popleft + self._put_buffer = self._buffer.append + + def is_eof(self) -> bool: + return self._eof + + def exception(self) -> Optional[BaseException]: + return self._exception + + def set_exception( + self, + exc: BaseException, + exc_cause: builtins.BaseException = _EXC_SENTINEL, + ) -> None: + self._eof = True + self._exception = exc + if (waiter := self._waiter) is not None: + self._waiter = None + set_exception(waiter, exc, exc_cause) + + def _release_waiter(self) -> None: + if (waiter := self._waiter) is None: + return + self._waiter = None + if not waiter.done(): + waiter.set_result(None) + + def feed_eof(self) -> None: + self._eof = True + self._release_waiter() + self._exception = None # Break cyclic references + + def feed_data(self, data: "WSMessage", size: "cython_int") -> None: + self._size += size + self._put_buffer((data, size)) + self._release_waiter() + if self._size > self._limit and not self._protocol._reading_paused: + self._protocol.pause_reading() + + async def read(self) -> WSMessage: + if not self._buffer and not self._eof: + assert not self._waiter + self._waiter = self._loop.create_future() + try: + await self._waiter + except (asyncio.CancelledError, asyncio.TimeoutError): + self._waiter = None + raise + return self._read_from_buffer() + + def _read_from_buffer(self) -> WSMessage: + if self._buffer: + data, size = self._get_buffer() + self._size -= size + if self._size < self._limit and self._protocol._reading_paused: + self._protocol.resume_reading() + return data + if self._exception is not None: + raise self._exception + raise EofStream + + +class WebSocketReader: + def __init__( + self, queue: WebSocketDataQueue, max_msg_size: int, compress: bool = True + ) -> None: + self.queue = queue + self._max_msg_size = max_msg_size + + self._exc: Optional[Exception] = None + self._partial = bytearray() + self._state = READ_HEADER + + self._opcode: int = OP_CODE_NOT_SET + self._frame_fin = False + self._frame_opcode: int = OP_CODE_NOT_SET + self._payload_fragments: list[bytes] = [] + self._frame_payload_len = 0 + + self._tail: bytes = b"" + self._has_mask = False + self._frame_mask: Optional[bytes] = None + self._payload_bytes_to_read = 0 + self._payload_len_flag = 0 + self._compressed: int = COMPRESSED_NOT_SET + self._decompressobj: Optional[ZLibDecompressor] = None + self._compress = compress + + def feed_eof(self) -> None: + self.queue.feed_eof() + + # data can be bytearray on Windows because proactor event loop uses bytearray + # and asyncio types this to Union[bytes, bytearray, memoryview] so we need + # coerce data to bytes if it is not + def feed_data( + self, data: Union[bytes, bytearray, memoryview] + ) -> Tuple[bool, bytes]: + if type(data) is not bytes: + data = bytes(data) + + if self._exc is not None: + return True, data + + try: + self._feed_data(data) + except Exception as exc: + self._exc = exc + set_exception(self.queue, exc) + return EMPTY_FRAME_ERROR + + return EMPTY_FRAME + + def _handle_frame( + self, + fin: bool, + opcode: Union[int, cython_int], # Union intended: Cython pxd uses C int + payload: Union[bytes, bytearray], + compressed: Union[int, cython_int], # Union intended: Cython pxd uses C int + ) -> None: + msg: WSMessage + if opcode in {OP_CODE_TEXT, OP_CODE_BINARY, OP_CODE_CONTINUATION}: + # Validate continuation frames before processing + if opcode == OP_CODE_CONTINUATION and self._opcode == OP_CODE_NOT_SET: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Continuation frame for non started message", + ) + + # load text/binary + if not fin: + # got partial frame payload + if opcode != OP_CODE_CONTINUATION: + self._opcode = opcode + self._partial += payload + if self._max_msg_size and len(self._partial) >= self._max_msg_size: + raise WebSocketError( + WSCloseCode.MESSAGE_TOO_BIG, + f"Message size {len(self._partial)} " + f"exceeds limit {self._max_msg_size}", + ) + return + + has_partial = bool(self._partial) + if opcode == OP_CODE_CONTINUATION: + opcode = self._opcode + self._opcode = OP_CODE_NOT_SET + # previous frame was non finished + # we should get continuation opcode + elif has_partial: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "The opcode in non-fin frame is expected " + f"to be zero, got {opcode!r}", + ) + + assembled_payload: Union[bytes, bytearray] + if has_partial: + assembled_payload = self._partial + payload + self._partial.clear() + else: + assembled_payload = payload + + if self._max_msg_size and len(assembled_payload) >= self._max_msg_size: + raise WebSocketError( + WSCloseCode.MESSAGE_TOO_BIG, + f"Message size {len(assembled_payload)} " + f"exceeds limit {self._max_msg_size}", + ) + + # Decompress process must to be done after all packets + # received. + if compressed: + if not self._decompressobj: + self._decompressobj = ZLibDecompressor(suppress_deflate_header=True) + # XXX: It's possible that the zlib backend (isal is known to + # do this, maybe others too?) will return max_length bytes, + # but internally buffer more data such that the payload is + # >max_length, so we return one extra byte and if we're able + # to do that, then the message is too big. + payload_merged = self._decompressobj.decompress_sync( + assembled_payload + WS_DEFLATE_TRAILING, + ( + self._max_msg_size + 1 + if self._max_msg_size + else self._max_msg_size + ), + ) + if self._max_msg_size and len(payload_merged) > self._max_msg_size: + raise WebSocketError( + WSCloseCode.MESSAGE_TOO_BIG, + f"Decompressed message exceeds size limit {self._max_msg_size}", + ) + elif type(assembled_payload) is bytes: + payload_merged = assembled_payload + else: + payload_merged = bytes(assembled_payload) + + if opcode == OP_CODE_TEXT: + try: + text = payload_merged.decode("utf-8") + except UnicodeDecodeError as exc: + raise WebSocketError( + WSCloseCode.INVALID_TEXT, "Invalid UTF-8 text message" + ) from exc + + # XXX: The Text and Binary messages here can be a performance + # bottleneck, so we use tuple.__new__ to improve performance. + # This is not type safe, but many tests should fail in + # test_client_ws_functional.py if this is wrong. + self.queue.feed_data( + TUPLE_NEW(WSMessage, (WS_MSG_TYPE_TEXT, text, "")), + len(payload_merged), + ) + else: + self.queue.feed_data( + TUPLE_NEW(WSMessage, (WS_MSG_TYPE_BINARY, payload_merged, "")), + len(payload_merged), + ) + elif opcode == OP_CODE_CLOSE: + if len(payload) >= 2: + close_code = UNPACK_CLOSE_CODE(payload[:2])[0] + if close_code < 3000 and close_code not in ALLOWED_CLOSE_CODES: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + f"Invalid close code: {close_code}", + ) + try: + close_message = payload[2:].decode("utf-8") + except UnicodeDecodeError as exc: + raise WebSocketError( + WSCloseCode.INVALID_TEXT, "Invalid UTF-8 text message" + ) from exc + msg = TUPLE_NEW(WSMessage, (WSMsgType.CLOSE, close_code, close_message)) + elif payload: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + f"Invalid close frame: {fin} {opcode} {payload!r}", + ) + else: + msg = TUPLE_NEW(WSMessage, (WSMsgType.CLOSE, 0, "")) + + self.queue.feed_data(msg, 0) + elif opcode == OP_CODE_PING: + msg = TUPLE_NEW(WSMessage, (WSMsgType.PING, payload, "")) + self.queue.feed_data(msg, len(payload)) + elif opcode == OP_CODE_PONG: + msg = TUPLE_NEW(WSMessage, (WSMsgType.PONG, payload, "")) + self.queue.feed_data(msg, len(payload)) + else: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, f"Unexpected opcode={opcode!r}" + ) + + def _feed_data(self, data: bytes) -> None: + """Return the next frame from the socket.""" + if self._tail: + data, self._tail = self._tail + data, b"" + + start_pos: int = 0 + data_len = len(data) + data_cstr = data + + while True: + # read header + if self._state == READ_HEADER: + if data_len - start_pos < 2: + break + first_byte = data_cstr[start_pos] + second_byte = data_cstr[start_pos + 1] + start_pos += 2 + + fin = (first_byte >> 7) & 1 + rsv1 = (first_byte >> 6) & 1 + rsv2 = (first_byte >> 5) & 1 + rsv3 = (first_byte >> 4) & 1 + opcode = first_byte & 0xF + + # frame-fin = %x0 ; more frames of this message follow + # / %x1 ; final frame of this message + # frame-rsv1 = %x0 ; + # 1 bit, MUST be 0 unless negotiated otherwise + # frame-rsv2 = %x0 ; + # 1 bit, MUST be 0 unless negotiated otherwise + # frame-rsv3 = %x0 ; + # 1 bit, MUST be 0 unless negotiated otherwise + # + # Remove rsv1 from this test for deflate development + if rsv2 or rsv3 or (rsv1 and not self._compress): + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Received frame with non-zero reserved bits", + ) + + if opcode > 0x7 and fin == 0: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Received fragmented control frame", + ) + + has_mask = (second_byte >> 7) & 1 + length = second_byte & 0x7F + + # Control frames MUST have a payload + # length of 125 bytes or less + if opcode > 0x7 and length > 125: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Control frame payload cannot be larger than 125 bytes", + ) + + # Set compress status if last package is FIN + # OR set compress status if this is first fragment + # Raise error if not first fragment with rsv1 = 0x1 + if self._frame_fin or self._compressed == COMPRESSED_NOT_SET: + self._compressed = COMPRESSED_TRUE if rsv1 else COMPRESSED_FALSE + elif rsv1: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Received frame with non-zero reserved bits", + ) + + self._frame_fin = bool(fin) + self._frame_opcode = opcode + self._has_mask = bool(has_mask) + self._payload_len_flag = length + self._state = READ_PAYLOAD_LENGTH + + # read payload length + if self._state == READ_PAYLOAD_LENGTH: + len_flag = self._payload_len_flag + if len_flag == 126: + if data_len - start_pos < 2: + break + first_byte = data_cstr[start_pos] + second_byte = data_cstr[start_pos + 1] + start_pos += 2 + self._payload_bytes_to_read = first_byte << 8 | second_byte + elif len_flag > 126: + if data_len - start_pos < 8: + break + self._payload_bytes_to_read = UNPACK_LEN3(data, start_pos)[0] + start_pos += 8 + else: + self._payload_bytes_to_read = len_flag + + self._state = READ_PAYLOAD_MASK if self._has_mask else READ_PAYLOAD + + # read payload mask + if self._state == READ_PAYLOAD_MASK: + if data_len - start_pos < 4: + break + self._frame_mask = data_cstr[start_pos : start_pos + 4] + start_pos += 4 + self._state = READ_PAYLOAD + + if self._state == READ_PAYLOAD: + chunk_len = data_len - start_pos + if self._payload_bytes_to_read >= chunk_len: + f_end_pos = data_len + self._payload_bytes_to_read -= chunk_len + else: + f_end_pos = start_pos + self._payload_bytes_to_read + self._payload_bytes_to_read = 0 + + had_fragments = self._frame_payload_len + self._frame_payload_len += f_end_pos - start_pos + f_start_pos = start_pos + start_pos = f_end_pos + + if self._payload_bytes_to_read != 0: + # If we don't have a complete frame, we need to save the + # data for the next call to feed_data. + self._payload_fragments.append(data_cstr[f_start_pos:f_end_pos]) + break + + payload: Union[bytes, bytearray] + if had_fragments: + # We have to join the payload fragments get the payload + self._payload_fragments.append(data_cstr[f_start_pos:f_end_pos]) + if self._has_mask: + assert self._frame_mask is not None + payload_bytearray = bytearray(b"".join(self._payload_fragments)) + websocket_mask(self._frame_mask, payload_bytearray) + payload = payload_bytearray + else: + payload = b"".join(self._payload_fragments) + self._payload_fragments.clear() + elif self._has_mask: + assert self._frame_mask is not None + payload_bytearray = data_cstr[f_start_pos:f_end_pos] # type: ignore[assignment] + if type(payload_bytearray) is not bytearray: # pragma: no branch + # Cython will do the conversion for us + # but we need to do it for Python and we + # will always get here in Python + payload_bytearray = bytearray(payload_bytearray) + websocket_mask(self._frame_mask, payload_bytearray) + payload = payload_bytearray + else: + payload = data_cstr[f_start_pos:f_end_pos] + + self._handle_frame( + self._frame_fin, self._frame_opcode, payload, self._compressed + ) + self._frame_payload_len = 0 + self._state = READ_HEADER + + # XXX: Cython needs slices to be bounded, so we can't omit the slice end here. + self._tail = data_cstr[start_pos:data_len] if start_pos < data_len else b"" diff --git a/.venv/lib/python3.9/site-packages/aiohttp/_websocket/reader_py.py b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/reader_py.py new file mode 100644 index 0000000..5166d7e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/reader_py.py @@ -0,0 +1,478 @@ +"""Reader for WebSocket protocol versions 13 and 8.""" + +import asyncio +import builtins +from collections import deque +from typing import Deque, Final, Optional, Set, Tuple, Union + +from ..base_protocol import BaseProtocol +from ..compression_utils import ZLibDecompressor +from ..helpers import _EXC_SENTINEL, set_exception +from ..streams import EofStream +from .helpers import UNPACK_CLOSE_CODE, UNPACK_LEN3, websocket_mask +from .models import ( + WS_DEFLATE_TRAILING, + WebSocketError, + WSCloseCode, + WSMessage, + WSMsgType, +) + +ALLOWED_CLOSE_CODES: Final[Set[int]] = {int(i) for i in WSCloseCode} + +# States for the reader, used to parse the WebSocket frame +# integer values are used so they can be cythonized +READ_HEADER = 1 +READ_PAYLOAD_LENGTH = 2 +READ_PAYLOAD_MASK = 3 +READ_PAYLOAD = 4 + +WS_MSG_TYPE_BINARY = WSMsgType.BINARY +WS_MSG_TYPE_TEXT = WSMsgType.TEXT + +# WSMsgType values unpacked so they can by cythonized to ints +OP_CODE_NOT_SET = -1 +OP_CODE_CONTINUATION = WSMsgType.CONTINUATION.value +OP_CODE_TEXT = WSMsgType.TEXT.value +OP_CODE_BINARY = WSMsgType.BINARY.value +OP_CODE_CLOSE = WSMsgType.CLOSE.value +OP_CODE_PING = WSMsgType.PING.value +OP_CODE_PONG = WSMsgType.PONG.value + +EMPTY_FRAME_ERROR = (True, b"") +EMPTY_FRAME = (False, b"") + +COMPRESSED_NOT_SET = -1 +COMPRESSED_FALSE = 0 +COMPRESSED_TRUE = 1 + +TUPLE_NEW = tuple.__new__ + +cython_int = int # Typed to int in Python, but cython with use a signed int in the pxd + + +class WebSocketDataQueue: + """WebSocketDataQueue resumes and pauses an underlying stream. + + It is a destination for WebSocket data. + """ + + def __init__( + self, protocol: BaseProtocol, limit: int, *, loop: asyncio.AbstractEventLoop + ) -> None: + self._size = 0 + self._protocol = protocol + self._limit = limit * 2 + self._loop = loop + self._eof = False + self._waiter: Optional[asyncio.Future[None]] = None + self._exception: Union[BaseException, None] = None + self._buffer: Deque[Tuple[WSMessage, int]] = deque() + self._get_buffer = self._buffer.popleft + self._put_buffer = self._buffer.append + + def is_eof(self) -> bool: + return self._eof + + def exception(self) -> Optional[BaseException]: + return self._exception + + def set_exception( + self, + exc: BaseException, + exc_cause: builtins.BaseException = _EXC_SENTINEL, + ) -> None: + self._eof = True + self._exception = exc + if (waiter := self._waiter) is not None: + self._waiter = None + set_exception(waiter, exc, exc_cause) + + def _release_waiter(self) -> None: + if (waiter := self._waiter) is None: + return + self._waiter = None + if not waiter.done(): + waiter.set_result(None) + + def feed_eof(self) -> None: + self._eof = True + self._release_waiter() + self._exception = None # Break cyclic references + + def feed_data(self, data: "WSMessage", size: "cython_int") -> None: + self._size += size + self._put_buffer((data, size)) + self._release_waiter() + if self._size > self._limit and not self._protocol._reading_paused: + self._protocol.pause_reading() + + async def read(self) -> WSMessage: + if not self._buffer and not self._eof: + assert not self._waiter + self._waiter = self._loop.create_future() + try: + await self._waiter + except (asyncio.CancelledError, asyncio.TimeoutError): + self._waiter = None + raise + return self._read_from_buffer() + + def _read_from_buffer(self) -> WSMessage: + if self._buffer: + data, size = self._get_buffer() + self._size -= size + if self._size < self._limit and self._protocol._reading_paused: + self._protocol.resume_reading() + return data + if self._exception is not None: + raise self._exception + raise EofStream + + +class WebSocketReader: + def __init__( + self, queue: WebSocketDataQueue, max_msg_size: int, compress: bool = True + ) -> None: + self.queue = queue + self._max_msg_size = max_msg_size + + self._exc: Optional[Exception] = None + self._partial = bytearray() + self._state = READ_HEADER + + self._opcode: int = OP_CODE_NOT_SET + self._frame_fin = False + self._frame_opcode: int = OP_CODE_NOT_SET + self._payload_fragments: list[bytes] = [] + self._frame_payload_len = 0 + + self._tail: bytes = b"" + self._has_mask = False + self._frame_mask: Optional[bytes] = None + self._payload_bytes_to_read = 0 + self._payload_len_flag = 0 + self._compressed: int = COMPRESSED_NOT_SET + self._decompressobj: Optional[ZLibDecompressor] = None + self._compress = compress + + def feed_eof(self) -> None: + self.queue.feed_eof() + + # data can be bytearray on Windows because proactor event loop uses bytearray + # and asyncio types this to Union[bytes, bytearray, memoryview] so we need + # coerce data to bytes if it is not + def feed_data( + self, data: Union[bytes, bytearray, memoryview] + ) -> Tuple[bool, bytes]: + if type(data) is not bytes: + data = bytes(data) + + if self._exc is not None: + return True, data + + try: + self._feed_data(data) + except Exception as exc: + self._exc = exc + set_exception(self.queue, exc) + return EMPTY_FRAME_ERROR + + return EMPTY_FRAME + + def _handle_frame( + self, + fin: bool, + opcode: Union[int, cython_int], # Union intended: Cython pxd uses C int + payload: Union[bytes, bytearray], + compressed: Union[int, cython_int], # Union intended: Cython pxd uses C int + ) -> None: + msg: WSMessage + if opcode in {OP_CODE_TEXT, OP_CODE_BINARY, OP_CODE_CONTINUATION}: + # Validate continuation frames before processing + if opcode == OP_CODE_CONTINUATION and self._opcode == OP_CODE_NOT_SET: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Continuation frame for non started message", + ) + + # load text/binary + if not fin: + # got partial frame payload + if opcode != OP_CODE_CONTINUATION: + self._opcode = opcode + self._partial += payload + if self._max_msg_size and len(self._partial) >= self._max_msg_size: + raise WebSocketError( + WSCloseCode.MESSAGE_TOO_BIG, + f"Message size {len(self._partial)} " + f"exceeds limit {self._max_msg_size}", + ) + return + + has_partial = bool(self._partial) + if opcode == OP_CODE_CONTINUATION: + opcode = self._opcode + self._opcode = OP_CODE_NOT_SET + # previous frame was non finished + # we should get continuation opcode + elif has_partial: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "The opcode in non-fin frame is expected " + f"to be zero, got {opcode!r}", + ) + + assembled_payload: Union[bytes, bytearray] + if has_partial: + assembled_payload = self._partial + payload + self._partial.clear() + else: + assembled_payload = payload + + if self._max_msg_size and len(assembled_payload) >= self._max_msg_size: + raise WebSocketError( + WSCloseCode.MESSAGE_TOO_BIG, + f"Message size {len(assembled_payload)} " + f"exceeds limit {self._max_msg_size}", + ) + + # Decompress process must to be done after all packets + # received. + if compressed: + if not self._decompressobj: + self._decompressobj = ZLibDecompressor(suppress_deflate_header=True) + # XXX: It's possible that the zlib backend (isal is known to + # do this, maybe others too?) will return max_length bytes, + # but internally buffer more data such that the payload is + # >max_length, so we return one extra byte and if we're able + # to do that, then the message is too big. + payload_merged = self._decompressobj.decompress_sync( + assembled_payload + WS_DEFLATE_TRAILING, + ( + self._max_msg_size + 1 + if self._max_msg_size + else self._max_msg_size + ), + ) + if self._max_msg_size and len(payload_merged) > self._max_msg_size: + raise WebSocketError( + WSCloseCode.MESSAGE_TOO_BIG, + f"Decompressed message exceeds size limit {self._max_msg_size}", + ) + elif type(assembled_payload) is bytes: + payload_merged = assembled_payload + else: + payload_merged = bytes(assembled_payload) + + if opcode == OP_CODE_TEXT: + try: + text = payload_merged.decode("utf-8") + except UnicodeDecodeError as exc: + raise WebSocketError( + WSCloseCode.INVALID_TEXT, "Invalid UTF-8 text message" + ) from exc + + # XXX: The Text and Binary messages here can be a performance + # bottleneck, so we use tuple.__new__ to improve performance. + # This is not type safe, but many tests should fail in + # test_client_ws_functional.py if this is wrong. + self.queue.feed_data( + TUPLE_NEW(WSMessage, (WS_MSG_TYPE_TEXT, text, "")), + len(payload_merged), + ) + else: + self.queue.feed_data( + TUPLE_NEW(WSMessage, (WS_MSG_TYPE_BINARY, payload_merged, "")), + len(payload_merged), + ) + elif opcode == OP_CODE_CLOSE: + if len(payload) >= 2: + close_code = UNPACK_CLOSE_CODE(payload[:2])[0] + if close_code < 3000 and close_code not in ALLOWED_CLOSE_CODES: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + f"Invalid close code: {close_code}", + ) + try: + close_message = payload[2:].decode("utf-8") + except UnicodeDecodeError as exc: + raise WebSocketError( + WSCloseCode.INVALID_TEXT, "Invalid UTF-8 text message" + ) from exc + msg = TUPLE_NEW(WSMessage, (WSMsgType.CLOSE, close_code, close_message)) + elif payload: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + f"Invalid close frame: {fin} {opcode} {payload!r}", + ) + else: + msg = TUPLE_NEW(WSMessage, (WSMsgType.CLOSE, 0, "")) + + self.queue.feed_data(msg, 0) + elif opcode == OP_CODE_PING: + msg = TUPLE_NEW(WSMessage, (WSMsgType.PING, payload, "")) + self.queue.feed_data(msg, len(payload)) + elif opcode == OP_CODE_PONG: + msg = TUPLE_NEW(WSMessage, (WSMsgType.PONG, payload, "")) + self.queue.feed_data(msg, len(payload)) + else: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, f"Unexpected opcode={opcode!r}" + ) + + def _feed_data(self, data: bytes) -> None: + """Return the next frame from the socket.""" + if self._tail: + data, self._tail = self._tail + data, b"" + + start_pos: int = 0 + data_len = len(data) + data_cstr = data + + while True: + # read header + if self._state == READ_HEADER: + if data_len - start_pos < 2: + break + first_byte = data_cstr[start_pos] + second_byte = data_cstr[start_pos + 1] + start_pos += 2 + + fin = (first_byte >> 7) & 1 + rsv1 = (first_byte >> 6) & 1 + rsv2 = (first_byte >> 5) & 1 + rsv3 = (first_byte >> 4) & 1 + opcode = first_byte & 0xF + + # frame-fin = %x0 ; more frames of this message follow + # / %x1 ; final frame of this message + # frame-rsv1 = %x0 ; + # 1 bit, MUST be 0 unless negotiated otherwise + # frame-rsv2 = %x0 ; + # 1 bit, MUST be 0 unless negotiated otherwise + # frame-rsv3 = %x0 ; + # 1 bit, MUST be 0 unless negotiated otherwise + # + # Remove rsv1 from this test for deflate development + if rsv2 or rsv3 or (rsv1 and not self._compress): + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Received frame with non-zero reserved bits", + ) + + if opcode > 0x7 and fin == 0: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Received fragmented control frame", + ) + + has_mask = (second_byte >> 7) & 1 + length = second_byte & 0x7F + + # Control frames MUST have a payload + # length of 125 bytes or less + if opcode > 0x7 and length > 125: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Control frame payload cannot be larger than 125 bytes", + ) + + # Set compress status if last package is FIN + # OR set compress status if this is first fragment + # Raise error if not first fragment with rsv1 = 0x1 + if self._frame_fin or self._compressed == COMPRESSED_NOT_SET: + self._compressed = COMPRESSED_TRUE if rsv1 else COMPRESSED_FALSE + elif rsv1: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Received frame with non-zero reserved bits", + ) + + self._frame_fin = bool(fin) + self._frame_opcode = opcode + self._has_mask = bool(has_mask) + self._payload_len_flag = length + self._state = READ_PAYLOAD_LENGTH + + # read payload length + if self._state == READ_PAYLOAD_LENGTH: + len_flag = self._payload_len_flag + if len_flag == 126: + if data_len - start_pos < 2: + break + first_byte = data_cstr[start_pos] + second_byte = data_cstr[start_pos + 1] + start_pos += 2 + self._payload_bytes_to_read = first_byte << 8 | second_byte + elif len_flag > 126: + if data_len - start_pos < 8: + break + self._payload_bytes_to_read = UNPACK_LEN3(data, start_pos)[0] + start_pos += 8 + else: + self._payload_bytes_to_read = len_flag + + self._state = READ_PAYLOAD_MASK if self._has_mask else READ_PAYLOAD + + # read payload mask + if self._state == READ_PAYLOAD_MASK: + if data_len - start_pos < 4: + break + self._frame_mask = data_cstr[start_pos : start_pos + 4] + start_pos += 4 + self._state = READ_PAYLOAD + + if self._state == READ_PAYLOAD: + chunk_len = data_len - start_pos + if self._payload_bytes_to_read >= chunk_len: + f_end_pos = data_len + self._payload_bytes_to_read -= chunk_len + else: + f_end_pos = start_pos + self._payload_bytes_to_read + self._payload_bytes_to_read = 0 + + had_fragments = self._frame_payload_len + self._frame_payload_len += f_end_pos - start_pos + f_start_pos = start_pos + start_pos = f_end_pos + + if self._payload_bytes_to_read != 0: + # If we don't have a complete frame, we need to save the + # data for the next call to feed_data. + self._payload_fragments.append(data_cstr[f_start_pos:f_end_pos]) + break + + payload: Union[bytes, bytearray] + if had_fragments: + # We have to join the payload fragments get the payload + self._payload_fragments.append(data_cstr[f_start_pos:f_end_pos]) + if self._has_mask: + assert self._frame_mask is not None + payload_bytearray = bytearray(b"".join(self._payload_fragments)) + websocket_mask(self._frame_mask, payload_bytearray) + payload = payload_bytearray + else: + payload = b"".join(self._payload_fragments) + self._payload_fragments.clear() + elif self._has_mask: + assert self._frame_mask is not None + payload_bytearray = data_cstr[f_start_pos:f_end_pos] # type: ignore[assignment] + if type(payload_bytearray) is not bytearray: # pragma: no branch + # Cython will do the conversion for us + # but we need to do it for Python and we + # will always get here in Python + payload_bytearray = bytearray(payload_bytearray) + websocket_mask(self._frame_mask, payload_bytearray) + payload = payload_bytearray + else: + payload = data_cstr[f_start_pos:f_end_pos] + + self._handle_frame( + self._frame_fin, self._frame_opcode, payload, self._compressed + ) + self._frame_payload_len = 0 + self._state = READ_HEADER + + # XXX: Cython needs slices to be bounded, so we can't omit the slice end here. + self._tail = data_cstr[start_pos:data_len] if start_pos < data_len else b"" diff --git a/.venv/lib/python3.9/site-packages/aiohttp/_websocket/writer.py b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/writer.py new file mode 100644 index 0000000..9604202 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/_websocket/writer.py @@ -0,0 +1,262 @@ +"""WebSocket protocol versions 13 and 8.""" + +import asyncio +import random +import sys +from functools import partial +from typing import Final, Optional, Set, Union + +from ..base_protocol import BaseProtocol +from ..client_exceptions import ClientConnectionResetError +from ..compression_utils import ZLibBackend, ZLibCompressor +from .helpers import ( + MASK_LEN, + MSG_SIZE, + PACK_CLOSE_CODE, + PACK_LEN1, + PACK_LEN2, + PACK_LEN3, + PACK_RANDBITS, + websocket_mask, +) +from .models import WS_DEFLATE_TRAILING, WSMsgType + +DEFAULT_LIMIT: Final[int] = 2**16 + +# WebSocket opcode boundary: opcodes 0-7 are data frames, 8-15 are control frames +# Control frames (ping, pong, close) are never compressed +WS_CONTROL_FRAME_OPCODE: Final[int] = 8 + +# For websockets, keeping latency low is extremely important as implementations +# generally expect to be able to send and receive messages quickly. We use a +# larger chunk size to reduce the number of executor calls and avoid task +# creation overhead, since both are significant sources of latency when chunks +# are small. A size of 16KiB was chosen as a balance between avoiding task +# overhead and not blocking the event loop too long with synchronous compression. + +WEBSOCKET_MAX_SYNC_CHUNK_SIZE = 16 * 1024 + + +class WebSocketWriter: + """WebSocket writer. + + The writer is responsible for sending messages to the client. It is + created by the protocol when a connection is established. The writer + should avoid implementing any application logic and should only be + concerned with the low-level details of the WebSocket protocol. + """ + + def __init__( + self, + protocol: BaseProtocol, + transport: asyncio.Transport, + *, + use_mask: bool = False, + limit: int = DEFAULT_LIMIT, + random: random.Random = random.Random(), + compress: int = 0, + notakeover: bool = False, + ) -> None: + """Initialize a WebSocket writer.""" + self.protocol = protocol + self.transport = transport + self.use_mask = use_mask + self.get_random_bits = partial(random.getrandbits, 32) + self.compress = compress + self.notakeover = notakeover + self._closing = False + self._limit = limit + self._output_size = 0 + self._compressobj: Optional[ZLibCompressor] = None + self._send_lock = asyncio.Lock() + self._background_tasks: Set[asyncio.Task[None]] = set() + + async def send_frame( + self, message: bytes, opcode: int, compress: Optional[int] = None + ) -> None: + """Send a frame over the websocket with message as its payload.""" + if self._closing and not (opcode & WSMsgType.CLOSE): + raise ClientConnectionResetError("Cannot write to closing transport") + + if not (compress or self.compress) or opcode >= WS_CONTROL_FRAME_OPCODE: + # Non-compressed frames don't need lock or shield + self._write_websocket_frame(message, opcode, 0) + elif len(message) <= WEBSOCKET_MAX_SYNC_CHUNK_SIZE: + # Small compressed payloads - compress synchronously in event loop + # We need the lock even though sync compression has no await points. + # This prevents small frames from interleaving with large frames that + # compress in the executor, avoiding compressor state corruption. + async with self._send_lock: + self._send_compressed_frame_sync(message, opcode, compress) + else: + # Large compressed frames need shield to prevent corruption + # For large compressed frames, the entire compress+send + # operation must be atomic. If cancelled after compression but + # before send, the compressor state would be advanced but data + # not sent, corrupting subsequent frames. + # Create a task to shield from cancellation + # The lock is acquired inside the shielded task so the entire + # operation (lock + compress + send) completes atomically. + # Use eager_start on Python 3.12+ to avoid scheduling overhead + loop = asyncio.get_running_loop() + coro = self._send_compressed_frame_async_locked(message, opcode, compress) + if sys.version_info >= (3, 12): + send_task = asyncio.Task(coro, loop=loop, eager_start=True) + else: + send_task = loop.create_task(coro) + # Keep a strong reference to prevent garbage collection + self._background_tasks.add(send_task) + send_task.add_done_callback(self._background_tasks.discard) + await asyncio.shield(send_task) + + # It is safe to return control to the event loop when using compression + # after this point as we have already sent or buffered all the data. + # Once we have written output_size up to the limit, we call the + # drain helper which waits for the transport to be ready to accept + # more data. This is a flow control mechanism to prevent the buffer + # from growing too large. The drain helper will return right away + # if the writer is not paused. + if self._output_size > self._limit: + self._output_size = 0 + if self.protocol._paused: + await self.protocol._drain_helper() + + def _write_websocket_frame(self, message: bytes, opcode: int, rsv: int) -> None: + """ + Write a websocket frame to the transport. + + This method handles frame header construction, masking, and writing to transport. + It does not handle compression or flow control - those are the responsibility + of the caller. + """ + msg_length = len(message) + + use_mask = self.use_mask + mask_bit = 0x80 if use_mask else 0 + + # Depending on the message length, the header is assembled differently. + # The first byte is reserved for the opcode and the RSV bits. + first_byte = 0x80 | rsv | opcode + if msg_length < 126: + header = PACK_LEN1(first_byte, msg_length | mask_bit) + header_len = 2 + elif msg_length < 65536: + header = PACK_LEN2(first_byte, 126 | mask_bit, msg_length) + header_len = 4 + else: + header = PACK_LEN3(first_byte, 127 | mask_bit, msg_length) + header_len = 10 + + if self.transport.is_closing(): + raise ClientConnectionResetError("Cannot write to closing transport") + + # https://datatracker.ietf.org/doc/html/rfc6455#section-5.3 + # If we are using a mask, we need to generate it randomly + # and apply it to the message before sending it. A mask is + # a 32-bit value that is applied to the message using a + # bitwise XOR operation. It is used to prevent certain types + # of attacks on the websocket protocol. The mask is only used + # when aiohttp is acting as a client. Servers do not use a mask. + if use_mask: + mask = PACK_RANDBITS(self.get_random_bits()) + message = bytearray(message) + websocket_mask(mask, message) + self.transport.write(header + mask + message) + self._output_size += MASK_LEN + elif msg_length > MSG_SIZE: + self.transport.write(header) + self.transport.write(message) + else: + self.transport.write(header + message) + + self._output_size += header_len + msg_length + + def _get_compressor(self, compress: Optional[int]) -> ZLibCompressor: + """Get or create a compressor object for the given compression level.""" + if compress: + # Do not set self._compress if compressing is for this frame + return ZLibCompressor( + level=ZLibBackend.Z_BEST_SPEED, + wbits=-compress, + max_sync_chunk_size=WEBSOCKET_MAX_SYNC_CHUNK_SIZE, + ) + if not self._compressobj: + self._compressobj = ZLibCompressor( + level=ZLibBackend.Z_BEST_SPEED, + wbits=-self.compress, + max_sync_chunk_size=WEBSOCKET_MAX_SYNC_CHUNK_SIZE, + ) + return self._compressobj + + def _send_compressed_frame_sync( + self, message: bytes, opcode: int, compress: Optional[int] + ) -> None: + """ + Synchronous send for small compressed frames. + + This is used for small compressed payloads that compress synchronously in the event loop. + Since there are no await points, this is inherently cancellation-safe. + """ + # RSV are the reserved bits in the frame header. They are used to + # indicate that the frame is using an extension. + # https://datatracker.ietf.org/doc/html/rfc6455#section-5.2 + compressobj = self._get_compressor(compress) + # (0x40) RSV1 is set for compressed frames + # https://datatracker.ietf.org/doc/html/rfc7692#section-7.2.3.1 + self._write_websocket_frame( + ( + compressobj.compress_sync(message) + + compressobj.flush( + ZLibBackend.Z_FULL_FLUSH + if self.notakeover + else ZLibBackend.Z_SYNC_FLUSH + ) + ).removesuffix(WS_DEFLATE_TRAILING), + opcode, + 0x40, + ) + + async def _send_compressed_frame_async_locked( + self, message: bytes, opcode: int, compress: Optional[int] + ) -> None: + """ + Async send for large compressed frames with lock. + + Acquires the lock and compresses large payloads asynchronously in + the executor. The lock is held for the entire operation to ensure + the compressor state is not corrupted by concurrent sends. + + MUST be run shielded from cancellation. If cancelled after + compression but before sending, the compressor state would be + advanced but data not sent, corrupting subsequent frames. + """ + async with self._send_lock: + # RSV are the reserved bits in the frame header. They are used to + # indicate that the frame is using an extension. + # https://datatracker.ietf.org/doc/html/rfc6455#section-5.2 + compressobj = self._get_compressor(compress) + # (0x40) RSV1 is set for compressed frames + # https://datatracker.ietf.org/doc/html/rfc7692#section-7.2.3.1 + self._write_websocket_frame( + ( + await compressobj.compress(message) + + compressobj.flush( + ZLibBackend.Z_FULL_FLUSH + if self.notakeover + else ZLibBackend.Z_SYNC_FLUSH + ) + ).removesuffix(WS_DEFLATE_TRAILING), + opcode, + 0x40, + ) + + async def close(self, code: int = 1000, message: Union[bytes, str] = b"") -> None: + """Close the websocket, sending the specified code and message.""" + if isinstance(message, str): + message = message.encode("utf-8") + try: + await self.send_frame( + PACK_CLOSE_CODE(code) + message, opcode=WSMsgType.CLOSE + ) + finally: + self._closing = True diff --git a/.venv/lib/python3.9/site-packages/aiohttp/abc.py b/.venv/lib/python3.9/site-packages/aiohttp/abc.py new file mode 100644 index 0000000..faf0957 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/abc.py @@ -0,0 +1,268 @@ +import asyncio +import logging +import socket +from abc import ABC, abstractmethod +from collections.abc import Sized +from http.cookies import BaseCookie, Morsel +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Dict, + Generator, + Iterable, + List, + Optional, + Sequence, + Tuple, + TypedDict, + Union, +) + +from multidict import CIMultiDict +from yarl import URL + +from ._cookie_helpers import parse_set_cookie_headers +from .typedefs import LooseCookies + +if TYPE_CHECKING: + from .web_app import Application + from .web_exceptions import HTTPException + from .web_request import BaseRequest, Request + from .web_response import StreamResponse +else: + BaseRequest = Request = Application = StreamResponse = None + HTTPException = None + + +class AbstractRouter(ABC): + def __init__(self) -> None: + self._frozen = False + + def post_init(self, app: Application) -> None: + """Post init stage. + + Not an abstract method for sake of backward compatibility, + but if the router wants to be aware of the application + it can override this. + """ + + @property + def frozen(self) -> bool: + return self._frozen + + def freeze(self) -> None: + """Freeze router.""" + self._frozen = True + + @abstractmethod + async def resolve(self, request: Request) -> "AbstractMatchInfo": + """Return MATCH_INFO for given request""" + + +class AbstractMatchInfo(ABC): + + __slots__ = () + + @property # pragma: no branch + @abstractmethod + def handler(self) -> Callable[[Request], Awaitable[StreamResponse]]: + """Execute matched request handler""" + + @property + @abstractmethod + def expect_handler( + self, + ) -> Callable[[Request], Awaitable[Optional[StreamResponse]]]: + """Expect handler for 100-continue processing""" + + @property # pragma: no branch + @abstractmethod + def http_exception(self) -> Optional[HTTPException]: + """HTTPException instance raised on router's resolving, or None""" + + @abstractmethod # pragma: no branch + def get_info(self) -> Dict[str, Any]: + """Return a dict with additional info useful for introspection""" + + @property # pragma: no branch + @abstractmethod + def apps(self) -> Tuple[Application, ...]: + """Stack of nested applications. + + Top level application is left-most element. + + """ + + @abstractmethod + def add_app(self, app: Application) -> None: + """Add application to the nested apps stack.""" + + @abstractmethod + def freeze(self) -> None: + """Freeze the match info. + + The method is called after route resolution. + + After the call .add_app() is forbidden. + + """ + + +class AbstractView(ABC): + """Abstract class based view.""" + + def __init__(self, request: Request) -> None: + self._request = request + + @property + def request(self) -> Request: + """Request instance.""" + return self._request + + @abstractmethod + def __await__(self) -> Generator[None, None, StreamResponse]: + """Execute the view handler.""" + + +class ResolveResult(TypedDict): + """Resolve result. + + This is the result returned from an AbstractResolver's + resolve method. + + :param hostname: The hostname that was provided. + :param host: The IP address that was resolved. + :param port: The port that was resolved. + :param family: The address family that was resolved. + :param proto: The protocol that was resolved. + :param flags: The flags that were resolved. + """ + + hostname: str + host: str + port: int + family: int + proto: int + flags: int + + +class AbstractResolver(ABC): + """Abstract DNS resolver.""" + + @abstractmethod + async def resolve( + self, host: str, port: int = 0, family: socket.AddressFamily = socket.AF_INET + ) -> List[ResolveResult]: + """Return IP address for given hostname""" + + @abstractmethod + async def close(self) -> None: + """Release resolver""" + + +if TYPE_CHECKING: + IterableBase = Iterable[Morsel[str]] +else: + IterableBase = Iterable + + +ClearCookiePredicate = Callable[["Morsel[str]"], bool] + + +class AbstractCookieJar(Sized, IterableBase): + """Abstract Cookie Jar.""" + + def __init__(self, *, loop: Optional[asyncio.AbstractEventLoop] = None) -> None: + self._loop = loop or asyncio.get_running_loop() + + @property + @abstractmethod + def quote_cookie(self) -> bool: + """Return True if cookies should be quoted.""" + + @abstractmethod + def clear(self, predicate: Optional[ClearCookiePredicate] = None) -> None: + """Clear all cookies if no predicate is passed.""" + + @abstractmethod + def clear_domain(self, domain: str) -> None: + """Clear all cookies for domain and all subdomains.""" + + @abstractmethod + def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> None: + """Update cookies.""" + + def update_cookies_from_headers( + self, headers: Sequence[str], response_url: URL + ) -> None: + """Update cookies from raw Set-Cookie headers.""" + if headers and (cookies_to_update := parse_set_cookie_headers(headers)): + self.update_cookies(cookies_to_update, response_url) + + @abstractmethod + def filter_cookies(self, request_url: URL) -> "BaseCookie[str]": + """Return the jar's cookies filtered by their attributes.""" + + +class AbstractStreamWriter(ABC): + """Abstract stream writer.""" + + buffer_size: int = 0 + output_size: int = 0 + length: Optional[int] = 0 + + @abstractmethod + async def write(self, chunk: Union[bytes, bytearray, memoryview]) -> None: + """Write chunk into stream.""" + + @abstractmethod + async def write_eof(self, chunk: bytes = b"") -> None: + """Write last chunk.""" + + @abstractmethod + async def drain(self) -> None: + """Flush the write buffer.""" + + @abstractmethod + def enable_compression( + self, encoding: str = "deflate", strategy: Optional[int] = None + ) -> None: + """Enable HTTP body compression""" + + @abstractmethod + def enable_chunking(self) -> None: + """Enable HTTP chunked mode""" + + @abstractmethod + async def write_headers( + self, status_line: str, headers: "CIMultiDict[str]" + ) -> None: + """Write HTTP headers""" + + def send_headers(self) -> None: + """Force sending buffered headers if not already sent. + + Required only if write_headers() buffers headers instead of sending immediately. + For backwards compatibility, this method does nothing by default. + """ + + +class AbstractAccessLogger(ABC): + """Abstract writer to access log.""" + + __slots__ = ("logger", "log_format") + + def __init__(self, logger: logging.Logger, log_format: str) -> None: + self.logger = logger + self.log_format = log_format + + @abstractmethod + def log(self, request: BaseRequest, response: StreamResponse, time: float) -> None: + """Emit log to logger.""" + + @property + def enabled(self) -> bool: + """Check if logger is enabled.""" + return True diff --git a/.venv/lib/python3.9/site-packages/aiohttp/base_protocol.py b/.venv/lib/python3.9/site-packages/aiohttp/base_protocol.py new file mode 100644 index 0000000..b0a67ed --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/base_protocol.py @@ -0,0 +1,100 @@ +import asyncio +from typing import Optional, cast + +from .client_exceptions import ClientConnectionResetError +from .helpers import set_exception +from .tcp_helpers import tcp_nodelay + + +class BaseProtocol(asyncio.Protocol): + __slots__ = ( + "_loop", + "_paused", + "_drain_waiter", + "_connection_lost", + "_reading_paused", + "transport", + ) + + def __init__(self, loop: asyncio.AbstractEventLoop) -> None: + self._loop: asyncio.AbstractEventLoop = loop + self._paused = False + self._drain_waiter: Optional[asyncio.Future[None]] = None + self._reading_paused = False + + self.transport: Optional[asyncio.Transport] = None + + @property + def connected(self) -> bool: + """Return True if the connection is open.""" + return self.transport is not None + + @property + def writing_paused(self) -> bool: + return self._paused + + def pause_writing(self) -> None: + assert not self._paused + self._paused = True + + def resume_writing(self) -> None: + assert self._paused + self._paused = False + + waiter = self._drain_waiter + if waiter is not None: + self._drain_waiter = None + if not waiter.done(): + waiter.set_result(None) + + def pause_reading(self) -> None: + if not self._reading_paused and self.transport is not None: + try: + self.transport.pause_reading() + except (AttributeError, NotImplementedError, RuntimeError): + pass + self._reading_paused = True + + def resume_reading(self) -> None: + if self._reading_paused and self.transport is not None: + try: + self.transport.resume_reading() + except (AttributeError, NotImplementedError, RuntimeError): + pass + self._reading_paused = False + + def connection_made(self, transport: asyncio.BaseTransport) -> None: + tr = cast(asyncio.Transport, transport) + tcp_nodelay(tr, True) + self.transport = tr + + def connection_lost(self, exc: Optional[BaseException]) -> None: + # Wake up the writer if currently paused. + self.transport = None + if not self._paused: + return + waiter = self._drain_waiter + if waiter is None: + return + self._drain_waiter = None + if waiter.done(): + return + if exc is None: + waiter.set_result(None) + else: + set_exception( + waiter, + ConnectionError("Connection lost"), + exc, + ) + + async def _drain_helper(self) -> None: + if self.transport is None: + raise ClientConnectionResetError("Connection lost") + if not self._paused: + return + waiter = self._drain_waiter + if waiter is None: + waiter = self._loop.create_future() + self._drain_waiter = waiter + await asyncio.shield(waiter) diff --git a/.venv/lib/python3.9/site-packages/aiohttp/client.py b/.venv/lib/python3.9/site-packages/aiohttp/client.py new file mode 100644 index 0000000..bc4ee17 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/client.py @@ -0,0 +1,1635 @@ +"""HTTP Client for asyncio.""" + +import asyncio +import base64 +import hashlib +import json +import os +import sys +import traceback +import warnings +from contextlib import suppress +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Coroutine, + Final, + FrozenSet, + Generator, + Generic, + Iterable, + List, + Mapping, + Optional, + Sequence, + Set, + Tuple, + Type, + TypedDict, + TypeVar, + Union, +) + +import attr +from multidict import CIMultiDict, MultiDict, MultiDictProxy, istr +from yarl import URL + +from . import hdrs, http, payload +from ._websocket.reader import WebSocketDataQueue +from .abc import AbstractCookieJar +from .client_exceptions import ( + ClientConnectionError, + ClientConnectionResetError, + ClientConnectorCertificateError, + ClientConnectorDNSError, + ClientConnectorError, + ClientConnectorSSLError, + ClientError, + ClientHttpProxyError, + ClientOSError, + ClientPayloadError, + ClientProxyConnectionError, + ClientResponseError, + ClientSSLError, + ConnectionTimeoutError, + ContentTypeError, + InvalidURL, + InvalidUrlClientError, + InvalidUrlRedirectClientError, + NonHttpUrlClientError, + NonHttpUrlRedirectClientError, + RedirectClientError, + ServerConnectionError, + ServerDisconnectedError, + ServerFingerprintMismatch, + ServerTimeoutError, + SocketTimeoutError, + TooManyRedirects, + WSMessageTypeError, + WSServerHandshakeError, +) +from .client_middlewares import ClientMiddlewareType, build_client_middlewares +from .client_reqrep import ( + ClientRequest as ClientRequest, + ClientResponse as ClientResponse, + Fingerprint as Fingerprint, + RequestInfo as RequestInfo, + _merge_ssl_params, +) +from .client_ws import ( + DEFAULT_WS_CLIENT_TIMEOUT, + ClientWebSocketResponse as ClientWebSocketResponse, + ClientWSTimeout as ClientWSTimeout, +) +from .connector import ( + HTTP_AND_EMPTY_SCHEMA_SET, + BaseConnector as BaseConnector, + NamedPipeConnector as NamedPipeConnector, + TCPConnector as TCPConnector, + UnixConnector as UnixConnector, +) +from .cookiejar import CookieJar +from .helpers import ( + _SENTINEL, + DEBUG, + EMPTY_BODY_METHODS, + BasicAuth, + TimeoutHandle, + basicauth_from_netrc, + get_env_proxy_for_url, + netrc_from_env, + sentinel, + strip_auth_from_url, +) +from .http import WS_KEY, HttpVersion, WebSocketReader, WebSocketWriter +from .http_websocket import WSHandshakeError, ws_ext_gen, ws_ext_parse +from .tracing import Trace, TraceConfig +from .typedefs import JSONEncoder, LooseCookies, LooseHeaders, Query, StrOrURL + +__all__ = ( + # client_exceptions + "ClientConnectionError", + "ClientConnectionResetError", + "ClientConnectorCertificateError", + "ClientConnectorDNSError", + "ClientConnectorError", + "ClientConnectorSSLError", + "ClientError", + "ClientHttpProxyError", + "ClientOSError", + "ClientPayloadError", + "ClientProxyConnectionError", + "ClientResponseError", + "ClientSSLError", + "ConnectionTimeoutError", + "ContentTypeError", + "InvalidURL", + "InvalidUrlClientError", + "RedirectClientError", + "NonHttpUrlClientError", + "InvalidUrlRedirectClientError", + "NonHttpUrlRedirectClientError", + "ServerConnectionError", + "ServerDisconnectedError", + "ServerFingerprintMismatch", + "ServerTimeoutError", + "SocketTimeoutError", + "TooManyRedirects", + "WSServerHandshakeError", + # client_reqrep + "ClientRequest", + "ClientResponse", + "Fingerprint", + "RequestInfo", + # connector + "BaseConnector", + "TCPConnector", + "UnixConnector", + "NamedPipeConnector", + # client_ws + "ClientWebSocketResponse", + # client + "ClientSession", + "ClientTimeout", + "ClientWSTimeout", + "request", + "WSMessageTypeError", +) + + +if TYPE_CHECKING: + from ssl import SSLContext +else: + SSLContext = None + +if sys.version_info >= (3, 11) and TYPE_CHECKING: + from typing import Unpack + + +class _RequestOptions(TypedDict, total=False): + params: Query + data: Any + json: Any + cookies: Union[LooseCookies, None] + headers: Union[LooseHeaders, None] + skip_auto_headers: Union[Iterable[str], None] + auth: Union[BasicAuth, None] + allow_redirects: bool + max_redirects: int + compress: Union[str, bool, None] + chunked: Union[bool, None] + expect100: bool + raise_for_status: Union[None, bool, Callable[[ClientResponse], Awaitable[None]]] + read_until_eof: bool + proxy: Union[StrOrURL, None] + proxy_auth: Union[BasicAuth, None] + timeout: "Union[ClientTimeout, _SENTINEL, None]" + ssl: Union[SSLContext, bool, Fingerprint] + server_hostname: Union[str, None] + proxy_headers: Union[LooseHeaders, None] + trace_request_ctx: Union[Mapping[str, Any], None] + read_bufsize: Union[int, None] + auto_decompress: Union[bool, None] + max_line_size: Union[int, None] + max_field_size: Union[int, None] + middlewares: Optional[Sequence[ClientMiddlewareType]] + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class ClientTimeout: + total: Optional[float] = None + connect: Optional[float] = None + sock_read: Optional[float] = None + sock_connect: Optional[float] = None + ceil_threshold: float = 5 + + # pool_queue_timeout: Optional[float] = None + # dns_resolution_timeout: Optional[float] = None + # socket_connect_timeout: Optional[float] = None + # connection_acquiring_timeout: Optional[float] = None + # new_connection_timeout: Optional[float] = None + # http_header_timeout: Optional[float] = None + # response_body_timeout: Optional[float] = None + + # to create a timeout specific for a single request, either + # - create a completely new one to overwrite the default + # - or use http://www.attrs.org/en/stable/api.html#attr.evolve + # to overwrite the defaults + + +# 5 Minute default read timeout +DEFAULT_TIMEOUT: Final[ClientTimeout] = ClientTimeout(total=5 * 60, sock_connect=30) + +# https://www.rfc-editor.org/rfc/rfc9110#section-9.2.2 +IDEMPOTENT_METHODS = frozenset({"GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE"}) + +_RetType = TypeVar("_RetType", ClientResponse, ClientWebSocketResponse) +_CharsetResolver = Callable[[ClientResponse, bytes], str] + + +class ClientSession: + """First-class interface for making HTTP requests.""" + + ATTRS = frozenset( + [ + "_base_url", + "_base_url_origin", + "_source_traceback", + "_connector", + "_loop", + "_cookie_jar", + "_connector_owner", + "_default_auth", + "_version", + "_json_serialize", + "_requote_redirect_url", + "_timeout", + "_raise_for_status", + "_auto_decompress", + "_trust_env", + "_default_headers", + "_skip_auto_headers", + "_request_class", + "_response_class", + "_ws_response_class", + "_trace_configs", + "_read_bufsize", + "_max_line_size", + "_max_field_size", + "_resolve_charset", + "_default_proxy", + "_default_proxy_auth", + "_retry_connection", + "_middlewares", + "requote_redirect_url", + ] + ) + + _source_traceback: Optional[traceback.StackSummary] = None + _connector: Optional[BaseConnector] = None + + def __init__( + self, + base_url: Optional[StrOrURL] = None, + *, + connector: Optional[BaseConnector] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + cookies: Optional[LooseCookies] = None, + headers: Optional[LooseHeaders] = None, + proxy: Optional[StrOrURL] = None, + proxy_auth: Optional[BasicAuth] = None, + skip_auto_headers: Optional[Iterable[str]] = None, + auth: Optional[BasicAuth] = None, + json_serialize: JSONEncoder = json.dumps, + request_class: Type[ClientRequest] = ClientRequest, + response_class: Type[ClientResponse] = ClientResponse, + ws_response_class: Type[ClientWebSocketResponse] = ClientWebSocketResponse, + version: HttpVersion = http.HttpVersion11, + cookie_jar: Optional[AbstractCookieJar] = None, + connector_owner: bool = True, + raise_for_status: Union[ + bool, Callable[[ClientResponse], Awaitable[None]] + ] = False, + read_timeout: Union[float, _SENTINEL] = sentinel, + conn_timeout: Optional[float] = None, + timeout: Union[object, ClientTimeout] = sentinel, + auto_decompress: bool = True, + trust_env: bool = False, + requote_redirect_url: bool = True, + trace_configs: Optional[List[TraceConfig]] = None, + read_bufsize: int = 2**16, + max_line_size: int = 8190, + max_field_size: int = 8190, + fallback_charset_resolver: _CharsetResolver = lambda r, b: "utf-8", + middlewares: Sequence[ClientMiddlewareType] = (), + ssl_shutdown_timeout: Union[_SENTINEL, None, float] = sentinel, + ) -> None: + # We initialise _connector to None immediately, as it's referenced in __del__() + # and could cause issues if an exception occurs during initialisation. + self._connector: Optional[BaseConnector] = None + + if loop is None: + if connector is not None: + loop = connector._loop + + loop = loop or asyncio.get_running_loop() + + if base_url is None or isinstance(base_url, URL): + self._base_url: Optional[URL] = base_url + self._base_url_origin = None if base_url is None else base_url.origin() + else: + self._base_url = URL(base_url) + self._base_url_origin = self._base_url.origin() + assert self._base_url.absolute, "Only absolute URLs are supported" + if self._base_url is not None and not self._base_url.path.endswith("/"): + raise ValueError("base_url must have a trailing '/'") + + if timeout is sentinel or timeout is None: + self._timeout = DEFAULT_TIMEOUT + if read_timeout is not sentinel: + warnings.warn( + "read_timeout is deprecated, use timeout argument instead", + DeprecationWarning, + stacklevel=2, + ) + self._timeout = attr.evolve(self._timeout, total=read_timeout) + if conn_timeout is not None: + self._timeout = attr.evolve(self._timeout, connect=conn_timeout) + warnings.warn( + "conn_timeout is deprecated, use timeout argument instead", + DeprecationWarning, + stacklevel=2, + ) + else: + if not isinstance(timeout, ClientTimeout): + raise ValueError( + f"timeout parameter cannot be of {type(timeout)} type, " + "please use 'timeout=ClientTimeout(...)'", + ) + self._timeout = timeout + if read_timeout is not sentinel: + raise ValueError( + "read_timeout and timeout parameters " + "conflict, please setup " + "timeout.read" + ) + if conn_timeout is not None: + raise ValueError( + "conn_timeout and timeout parameters " + "conflict, please setup " + "timeout.connect" + ) + + if ssl_shutdown_timeout is not sentinel: + warnings.warn( + "The ssl_shutdown_timeout parameter is deprecated and will be removed in aiohttp 4.0", + DeprecationWarning, + stacklevel=2, + ) + + if connector is None: + connector = TCPConnector( + loop=loop, ssl_shutdown_timeout=ssl_shutdown_timeout + ) + + if connector._loop is not loop: + raise RuntimeError("Session and connector has to use same event loop") + + self._loop = loop + + if loop.get_debug(): + self._source_traceback = traceback.extract_stack(sys._getframe(1)) + + if cookie_jar is None: + cookie_jar = CookieJar(loop=loop) + self._cookie_jar = cookie_jar + + if cookies: + self._cookie_jar.update_cookies(cookies) + + self._connector = connector + self._connector_owner = connector_owner + self._default_auth = auth + self._version = version + self._json_serialize = json_serialize + self._raise_for_status = raise_for_status + self._auto_decompress = auto_decompress + self._trust_env = trust_env + self._requote_redirect_url = requote_redirect_url + self._read_bufsize = read_bufsize + self._max_line_size = max_line_size + self._max_field_size = max_field_size + + # Convert to list of tuples + if headers: + real_headers: CIMultiDict[str] = CIMultiDict(headers) + else: + real_headers = CIMultiDict() + self._default_headers: CIMultiDict[str] = real_headers + if skip_auto_headers is not None: + self._skip_auto_headers = frozenset(istr(i) for i in skip_auto_headers) + else: + self._skip_auto_headers = frozenset() + + self._request_class = request_class + self._response_class = response_class + self._ws_response_class = ws_response_class + + self._trace_configs = trace_configs or [] + for trace_config in self._trace_configs: + trace_config.freeze() + + self._resolve_charset = fallback_charset_resolver + + self._default_proxy = proxy + self._default_proxy_auth = proxy_auth + self._retry_connection: bool = True + self._middlewares = middlewares + + def __init_subclass__(cls: Type["ClientSession"]) -> None: + warnings.warn( + "Inheritance class {} from ClientSession " + "is discouraged".format(cls.__name__), + DeprecationWarning, + stacklevel=2, + ) + + if DEBUG: + + def __setattr__(self, name: str, val: Any) -> None: + if name not in self.ATTRS: + warnings.warn( + "Setting custom ClientSession.{} attribute " + "is discouraged".format(name), + DeprecationWarning, + stacklevel=2, + ) + super().__setattr__(name, val) + + def __del__(self, _warnings: Any = warnings) -> None: + if not self.closed: + kwargs = {"source": self} + _warnings.warn( + f"Unclosed client session {self!r}", ResourceWarning, **kwargs + ) + context = {"client_session": self, "message": "Unclosed client session"} + if self._source_traceback is not None: + context["source_traceback"] = self._source_traceback + self._loop.call_exception_handler(context) + + if sys.version_info >= (3, 11) and TYPE_CHECKING: + + def request( + self, + method: str, + url: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> "_RequestContextManager": ... + + else: + + def request( + self, method: str, url: StrOrURL, **kwargs: Any + ) -> "_RequestContextManager": + """Perform HTTP request.""" + return _RequestContextManager(self._request(method, url, **kwargs)) + + def _build_url(self, str_or_url: StrOrURL) -> URL: + url = URL(str_or_url) + if self._base_url and not url.absolute: + return self._base_url.join(url) + return url + + async def _request( + self, + method: str, + str_or_url: StrOrURL, + *, + params: Query = None, + data: Any = None, + json: Any = None, + cookies: Optional[LooseCookies] = None, + headers: Optional[LooseHeaders] = None, + skip_auto_headers: Optional[Iterable[str]] = None, + auth: Optional[BasicAuth] = None, + allow_redirects: bool = True, + max_redirects: int = 10, + compress: Union[str, bool, None] = None, + chunked: Optional[bool] = None, + expect100: bool = False, + raise_for_status: Union[ + None, bool, Callable[[ClientResponse], Awaitable[None]] + ] = None, + read_until_eof: bool = True, + proxy: Optional[StrOrURL] = None, + proxy_auth: Optional[BasicAuth] = None, + timeout: Union[ClientTimeout, _SENTINEL] = sentinel, + verify_ssl: Optional[bool] = None, + fingerprint: Optional[bytes] = None, + ssl_context: Optional[SSLContext] = None, + ssl: Union[SSLContext, bool, Fingerprint] = True, + server_hostname: Optional[str] = None, + proxy_headers: Optional[LooseHeaders] = None, + trace_request_ctx: Optional[Mapping[str, Any]] = None, + read_bufsize: Optional[int] = None, + auto_decompress: Optional[bool] = None, + max_line_size: Optional[int] = None, + max_field_size: Optional[int] = None, + middlewares: Optional[Sequence[ClientMiddlewareType]] = None, + ) -> ClientResponse: + + # NOTE: timeout clamps existing connect and read timeouts. We cannot + # set the default to None because we need to detect if the user wants + # to use the existing timeouts by setting timeout to None. + + if self.closed: + raise RuntimeError("Session is closed") + + ssl = _merge_ssl_params(ssl, verify_ssl, ssl_context, fingerprint) + + if data is not None and json is not None: + raise ValueError( + "data and json parameters can not be used at the same time" + ) + elif json is not None: + data = payload.JsonPayload(json, dumps=self._json_serialize) + + if not isinstance(chunked, bool) and chunked is not None: + warnings.warn("Chunk size is deprecated #1615", DeprecationWarning) + + redirects = 0 + history: List[ClientResponse] = [] + version = self._version + params = params or {} + + # Merge with default headers and transform to CIMultiDict + headers = self._prepare_headers(headers) + + try: + url = self._build_url(str_or_url) + except ValueError as e: + raise InvalidUrlClientError(str_or_url) from e + + assert self._connector is not None + if url.scheme not in self._connector.allowed_protocol_schema_set: + raise NonHttpUrlClientError(url) + + skip_headers: Optional[Iterable[istr]] + if skip_auto_headers is not None: + skip_headers = { + istr(i) for i in skip_auto_headers + } | self._skip_auto_headers + elif self._skip_auto_headers: + skip_headers = self._skip_auto_headers + else: + skip_headers = None + + if proxy is None: + proxy = self._default_proxy + if proxy_auth is None: + proxy_auth = self._default_proxy_auth + + if proxy is None: + proxy_headers = None + else: + proxy_headers = self._prepare_headers(proxy_headers) + try: + proxy = URL(proxy) + except ValueError as e: + raise InvalidURL(proxy) from e + + if timeout is sentinel: + real_timeout: ClientTimeout = self._timeout + else: + if not isinstance(timeout, ClientTimeout): + real_timeout = ClientTimeout(total=timeout) + else: + real_timeout = timeout + # timeout is cumulative for all request operations + # (request, redirects, responses, data consuming) + tm = TimeoutHandle( + self._loop, real_timeout.total, ceil_threshold=real_timeout.ceil_threshold + ) + handle = tm.start() + + if read_bufsize is None: + read_bufsize = self._read_bufsize + + if auto_decompress is None: + auto_decompress = self._auto_decompress + + if max_line_size is None: + max_line_size = self._max_line_size + + if max_field_size is None: + max_field_size = self._max_field_size + + traces = [ + Trace( + self, + trace_config, + trace_config.trace_config_ctx(trace_request_ctx=trace_request_ctx), + ) + for trace_config in self._trace_configs + ] + + for trace in traces: + await trace.send_request_start(method, url.update_query(params), headers) + + timer = tm.timer() + try: + with timer: + # https://www.rfc-editor.org/rfc/rfc9112.html#name-retrying-requests + retry_persistent_connection = ( + self._retry_connection and method in IDEMPOTENT_METHODS + ) + while True: + url, auth_from_url = strip_auth_from_url(url) + if not url.raw_host: + # NOTE: Bail early, otherwise, causes `InvalidURL` through + # NOTE: `self._request_class()` below. + err_exc_cls = ( + InvalidUrlRedirectClientError + if redirects + else InvalidUrlClientError + ) + raise err_exc_cls(url) + # If `auth` was passed for an already authenticated URL, + # disallow only if this is the initial URL; this is to avoid issues + # with sketchy redirects that are not the caller's responsibility + if not history and (auth and auth_from_url): + raise ValueError( + "Cannot combine AUTH argument with " + "credentials encoded in URL" + ) + + # Override the auth with the one from the URL only if we + # have no auth, or if we got an auth from a redirect URL + if auth is None or (history and auth_from_url is not None): + auth = auth_from_url + + if ( + auth is None + and self._default_auth + and ( + not self._base_url or self._base_url_origin == url.origin() + ) + ): + auth = self._default_auth + + # Try netrc if auth is still None and trust_env is enabled. + if auth is None and self._trust_env and url.host is not None: + auth = await self._loop.run_in_executor( + None, self._get_netrc_auth, url.host + ) + + # It would be confusing if we support explicit + # Authorization header with auth argument + if ( + headers is not None + and auth is not None + and hdrs.AUTHORIZATION in headers + ): + raise ValueError( + "Cannot combine AUTHORIZATION header " + "with AUTH argument or credentials " + "encoded in URL" + ) + + all_cookies = self._cookie_jar.filter_cookies(url) + + if cookies is not None: + tmp_cookie_jar = CookieJar( + quote_cookie=self._cookie_jar.quote_cookie + ) + tmp_cookie_jar.update_cookies(cookies) + req_cookies = tmp_cookie_jar.filter_cookies(url) + if req_cookies: + all_cookies.load(req_cookies) + + proxy_: Optional[URL] = None + if proxy is not None: + proxy_ = URL(proxy) + elif self._trust_env: + with suppress(LookupError): + proxy_, proxy_auth = await asyncio.to_thread( + get_env_proxy_for_url, url + ) + + req = self._request_class( + method, + url, + params=params, + headers=headers, + skip_auto_headers=skip_headers, + data=data, + cookies=all_cookies, + auth=auth, + version=version, + compress=compress, + chunked=chunked, + expect100=expect100, + loop=self._loop, + response_class=self._response_class, + proxy=proxy_, + proxy_auth=proxy_auth, + timer=timer, + session=self, + ssl=ssl if ssl is not None else True, + server_hostname=server_hostname, + proxy_headers=proxy_headers, + traces=traces, + trust_env=self.trust_env, + ) + + async def _connect_and_send_request( + req: ClientRequest, + ) -> ClientResponse: + # connection timeout + assert self._connector is not None + try: + conn = await self._connector.connect( + req, traces=traces, timeout=real_timeout + ) + except asyncio.TimeoutError as exc: + raise ConnectionTimeoutError( + f"Connection timeout to host {req.url}" + ) from exc + + assert conn.protocol is not None + conn.protocol.set_response_params( + timer=timer, + skip_payload=req.method in EMPTY_BODY_METHODS, + read_until_eof=read_until_eof, + auto_decompress=auto_decompress, + read_timeout=real_timeout.sock_read, + read_bufsize=read_bufsize, + timeout_ceil_threshold=self._connector._timeout_ceil_threshold, + max_line_size=max_line_size, + max_field_size=max_field_size, + ) + try: + resp = await req.send(conn) + try: + await resp.start(conn) + except BaseException: + resp.close() + raise + except BaseException: + conn.close() + raise + return resp + + # Apply middleware (if any) - per-request middleware overrides session middleware + effective_middlewares = ( + self._middlewares if middlewares is None else middlewares + ) + + if effective_middlewares: + handler = build_client_middlewares( + _connect_and_send_request, effective_middlewares + ) + else: + handler = _connect_and_send_request + + try: + resp = await handler(req) + # Client connector errors should not be retried + except ( + ConnectionTimeoutError, + ClientConnectorError, + ClientConnectorCertificateError, + ClientConnectorSSLError, + ): + raise + except (ClientOSError, ServerDisconnectedError): + if retry_persistent_connection: + retry_persistent_connection = False + continue + raise + except ClientError: + raise + except OSError as exc: + if exc.errno is None and isinstance(exc, asyncio.TimeoutError): + raise + raise ClientOSError(*exc.args) from exc + + # Update cookies from raw headers to preserve duplicates + if resp._raw_cookie_headers: + self._cookie_jar.update_cookies_from_headers( + resp._raw_cookie_headers, resp.url + ) + + # redirects + if resp.status in (301, 302, 303, 307, 308) and allow_redirects: + + for trace in traces: + await trace.send_request_redirect( + method, url.update_query(params), headers, resp + ) + + redirects += 1 + history.append(resp) + if max_redirects and redirects >= max_redirects: + if req._body is not None: + await req._body.close() + resp.close() + raise TooManyRedirects( + history[0].request_info, tuple(history) + ) + + # For 301 and 302, mimic IE, now changed in RFC + # https://github.com/kennethreitz/requests/pull/269 + if (resp.status == 303 and resp.method != hdrs.METH_HEAD) or ( + resp.status in (301, 302) and resp.method == hdrs.METH_POST + ): + method = hdrs.METH_GET + data = None + if headers.get(hdrs.CONTENT_LENGTH): + headers.pop(hdrs.CONTENT_LENGTH) + else: + # For 307/308, always preserve the request body + # For 301/302 with non-POST methods, preserve the request body + # https://www.rfc-editor.org/rfc/rfc9110#section-15.4.3-3.1 + # Use the existing payload to avoid recreating it from a potentially consumed file + data = req._body + + r_url = resp.headers.get(hdrs.LOCATION) or resp.headers.get( + hdrs.URI + ) + if r_url is None: + # see github.com/aio-libs/aiohttp/issues/2022 + break + else: + # reading from correct redirection + # response is forbidden + resp.release() + + try: + parsed_redirect_url = URL( + r_url, encoded=not self._requote_redirect_url + ) + except ValueError as e: + if req._body is not None: + await req._body.close() + resp.close() + raise InvalidUrlRedirectClientError( + r_url, + "Server attempted redirecting to a location that does not look like a URL", + ) from e + + scheme = parsed_redirect_url.scheme + if scheme not in HTTP_AND_EMPTY_SCHEMA_SET: + if req._body is not None: + await req._body.close() + resp.close() + raise NonHttpUrlRedirectClientError(r_url) + elif not scheme: + parsed_redirect_url = url.join(parsed_redirect_url) + + try: + redirect_origin = parsed_redirect_url.origin() + except ValueError as origin_val_err: + if req._body is not None: + await req._body.close() + resp.close() + raise InvalidUrlRedirectClientError( + parsed_redirect_url, + "Invalid redirect URL origin", + ) from origin_val_err + + if url.origin() != redirect_origin: + auth = None + headers.pop(hdrs.AUTHORIZATION, None) + + url = parsed_redirect_url + params = {} + resp.release() + continue + + break + + if req._body is not None: + await req._body.close() + # check response status + if raise_for_status is None: + raise_for_status = self._raise_for_status + + if raise_for_status is None: + pass + elif callable(raise_for_status): + await raise_for_status(resp) + elif raise_for_status: + resp.raise_for_status() + + # register connection + if handle is not None: + if resp.connection is not None: + resp.connection.add_callback(handle.cancel) + else: + handle.cancel() + + resp._history = tuple(history) + + for trace in traces: + await trace.send_request_end( + method, url.update_query(params), headers, resp + ) + return resp + + except BaseException as e: + # cleanup timer + tm.close() + if handle: + handle.cancel() + handle = None + + for trace in traces: + await trace.send_request_exception( + method, url.update_query(params), headers, e + ) + raise + + def ws_connect( + self, + url: StrOrURL, + *, + method: str = hdrs.METH_GET, + protocols: Iterable[str] = (), + timeout: Union[ClientWSTimeout, _SENTINEL] = sentinel, + receive_timeout: Optional[float] = None, + autoclose: bool = True, + autoping: bool = True, + heartbeat: Optional[float] = None, + auth: Optional[BasicAuth] = None, + origin: Optional[str] = None, + params: Query = None, + headers: Optional[LooseHeaders] = None, + proxy: Optional[StrOrURL] = None, + proxy_auth: Optional[BasicAuth] = None, + ssl: Union[SSLContext, bool, Fingerprint] = True, + verify_ssl: Optional[bool] = None, + fingerprint: Optional[bytes] = None, + ssl_context: Optional[SSLContext] = None, + server_hostname: Optional[str] = None, + proxy_headers: Optional[LooseHeaders] = None, + compress: int = 0, + max_msg_size: int = 4 * 1024 * 1024, + ) -> "_WSRequestContextManager": + """Initiate websocket connection.""" + return _WSRequestContextManager( + self._ws_connect( + url, + method=method, + protocols=protocols, + timeout=timeout, + receive_timeout=receive_timeout, + autoclose=autoclose, + autoping=autoping, + heartbeat=heartbeat, + auth=auth, + origin=origin, + params=params, + headers=headers, + proxy=proxy, + proxy_auth=proxy_auth, + ssl=ssl, + verify_ssl=verify_ssl, + fingerprint=fingerprint, + ssl_context=ssl_context, + server_hostname=server_hostname, + proxy_headers=proxy_headers, + compress=compress, + max_msg_size=max_msg_size, + ) + ) + + async def _ws_connect( + self, + url: StrOrURL, + *, + method: str = hdrs.METH_GET, + protocols: Iterable[str] = (), + timeout: Union[ClientWSTimeout, _SENTINEL] = sentinel, + receive_timeout: Optional[float] = None, + autoclose: bool = True, + autoping: bool = True, + heartbeat: Optional[float] = None, + auth: Optional[BasicAuth] = None, + origin: Optional[str] = None, + params: Query = None, + headers: Optional[LooseHeaders] = None, + proxy: Optional[StrOrURL] = None, + proxy_auth: Optional[BasicAuth] = None, + ssl: Union[SSLContext, bool, Fingerprint] = True, + verify_ssl: Optional[bool] = None, + fingerprint: Optional[bytes] = None, + ssl_context: Optional[SSLContext] = None, + server_hostname: Optional[str] = None, + proxy_headers: Optional[LooseHeaders] = None, + compress: int = 0, + max_msg_size: int = 4 * 1024 * 1024, + ) -> ClientWebSocketResponse: + if timeout is not sentinel: + if isinstance(timeout, ClientWSTimeout): + ws_timeout = timeout + else: + warnings.warn( + "parameter 'timeout' of type 'float' " + "is deprecated, please use " + "'timeout=ClientWSTimeout(ws_close=...)'", + DeprecationWarning, + stacklevel=2, + ) + ws_timeout = ClientWSTimeout(ws_close=timeout) + else: + ws_timeout = DEFAULT_WS_CLIENT_TIMEOUT + if receive_timeout is not None: + warnings.warn( + "float parameter 'receive_timeout' " + "is deprecated, please use parameter " + "'timeout=ClientWSTimeout(ws_receive=...)'", + DeprecationWarning, + stacklevel=2, + ) + ws_timeout = attr.evolve(ws_timeout, ws_receive=receive_timeout) + + if headers is None: + real_headers: CIMultiDict[str] = CIMultiDict() + else: + real_headers = CIMultiDict(headers) + + default_headers = { + hdrs.UPGRADE: "websocket", + hdrs.CONNECTION: "Upgrade", + hdrs.SEC_WEBSOCKET_VERSION: "13", + } + + for key, value in default_headers.items(): + real_headers.setdefault(key, value) + + sec_key = base64.b64encode(os.urandom(16)) + real_headers[hdrs.SEC_WEBSOCKET_KEY] = sec_key.decode() + + if protocols: + real_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = ",".join(protocols) + if origin is not None: + real_headers[hdrs.ORIGIN] = origin + if compress: + extstr = ws_ext_gen(compress=compress) + real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr + + # For the sake of backward compatibility, if user passes in None, convert it to True + if ssl is None: + warnings.warn( + "ssl=None is deprecated, please use ssl=True", + DeprecationWarning, + stacklevel=2, + ) + ssl = True + ssl = _merge_ssl_params(ssl, verify_ssl, ssl_context, fingerprint) + + # send request + resp = await self.request( + method, + url, + params=params, + headers=real_headers, + read_until_eof=False, + auth=auth, + proxy=proxy, + proxy_auth=proxy_auth, + ssl=ssl, + server_hostname=server_hostname, + proxy_headers=proxy_headers, + ) + + try: + # check handshake + if resp.status != 101: + raise WSServerHandshakeError( + resp.request_info, + resp.history, + message="Invalid response status", + status=resp.status, + headers=resp.headers, + ) + + if resp.headers.get(hdrs.UPGRADE, "").lower() != "websocket": + raise WSServerHandshakeError( + resp.request_info, + resp.history, + message="Invalid upgrade header", + status=resp.status, + headers=resp.headers, + ) + + if resp.headers.get(hdrs.CONNECTION, "").lower() != "upgrade": + raise WSServerHandshakeError( + resp.request_info, + resp.history, + message="Invalid connection header", + status=resp.status, + headers=resp.headers, + ) + + # key calculation + r_key = resp.headers.get(hdrs.SEC_WEBSOCKET_ACCEPT, "") + match = base64.b64encode(hashlib.sha1(sec_key + WS_KEY).digest()).decode() + if r_key != match: + raise WSServerHandshakeError( + resp.request_info, + resp.history, + message="Invalid challenge response", + status=resp.status, + headers=resp.headers, + ) + + # websocket protocol + protocol = None + if protocols and hdrs.SEC_WEBSOCKET_PROTOCOL in resp.headers: + resp_protocols = [ + proto.strip() + for proto in resp.headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(",") + ] + + for proto in resp_protocols: + if proto in protocols: + protocol = proto + break + + # websocket compress + notakeover = False + if compress: + compress_hdrs = resp.headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS) + if compress_hdrs: + try: + compress, notakeover = ws_ext_parse(compress_hdrs) + except WSHandshakeError as exc: + raise WSServerHandshakeError( + resp.request_info, + resp.history, + message=exc.args[0], + status=resp.status, + headers=resp.headers, + ) from exc + else: + compress = 0 + notakeover = False + + conn = resp.connection + assert conn is not None + conn_proto = conn.protocol + assert conn_proto is not None + + # For WS connection the read_timeout must be either receive_timeout or greater + # None == no timeout, i.e. infinite timeout, so None is the max timeout possible + if ws_timeout.ws_receive is None: + # Reset regardless + conn_proto.read_timeout = None + elif conn_proto.read_timeout is not None: + conn_proto.read_timeout = max( + ws_timeout.ws_receive, conn_proto.read_timeout + ) + + transport = conn.transport + assert transport is not None + reader = WebSocketDataQueue(conn_proto, 2**16, loop=self._loop) + conn_proto.set_parser(WebSocketReader(reader, max_msg_size), reader) + writer = WebSocketWriter( + conn_proto, + transport, + use_mask=True, + compress=compress, + notakeover=notakeover, + ) + except BaseException: + resp.close() + raise + else: + return self._ws_response_class( + reader, + writer, + protocol, + resp, + ws_timeout, + autoclose, + autoping, + self._loop, + heartbeat=heartbeat, + compress=compress, + client_notakeover=notakeover, + ) + + def _prepare_headers(self, headers: Optional[LooseHeaders]) -> "CIMultiDict[str]": + """Add default headers and transform it to CIMultiDict""" + # Convert headers to MultiDict + result = CIMultiDict(self._default_headers) + if headers: + if not isinstance(headers, (MultiDictProxy, MultiDict)): + headers = CIMultiDict(headers) + added_names: Set[str] = set() + for key, value in headers.items(): + if key in added_names: + result.add(key, value) + else: + result[key] = value + added_names.add(key) + return result + + def _get_netrc_auth(self, host: str) -> Optional[BasicAuth]: + """ + Get auth from netrc for the given host. + + This method is designed to be called in an executor to avoid + blocking I/O in the event loop. + """ + netrc_obj = netrc_from_env() + try: + return basicauth_from_netrc(netrc_obj, host) + except LookupError: + return None + + if sys.version_info >= (3, 11) and TYPE_CHECKING: + + def get( + self, + url: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> "_RequestContextManager": ... + + def options( + self, + url: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> "_RequestContextManager": ... + + def head( + self, + url: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> "_RequestContextManager": ... + + def post( + self, + url: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> "_RequestContextManager": ... + + def put( + self, + url: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> "_RequestContextManager": ... + + def patch( + self, + url: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> "_RequestContextManager": ... + + def delete( + self, + url: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> "_RequestContextManager": ... + + else: + + def get( + self, url: StrOrURL, *, allow_redirects: bool = True, **kwargs: Any + ) -> "_RequestContextManager": + """Perform HTTP GET request.""" + return _RequestContextManager( + self._request( + hdrs.METH_GET, url, allow_redirects=allow_redirects, **kwargs + ) + ) + + def options( + self, url: StrOrURL, *, allow_redirects: bool = True, **kwargs: Any + ) -> "_RequestContextManager": + """Perform HTTP OPTIONS request.""" + return _RequestContextManager( + self._request( + hdrs.METH_OPTIONS, url, allow_redirects=allow_redirects, **kwargs + ) + ) + + def head( + self, url: StrOrURL, *, allow_redirects: bool = False, **kwargs: Any + ) -> "_RequestContextManager": + """Perform HTTP HEAD request.""" + return _RequestContextManager( + self._request( + hdrs.METH_HEAD, url, allow_redirects=allow_redirects, **kwargs + ) + ) + + def post( + self, url: StrOrURL, *, data: Any = None, **kwargs: Any + ) -> "_RequestContextManager": + """Perform HTTP POST request.""" + return _RequestContextManager( + self._request(hdrs.METH_POST, url, data=data, **kwargs) + ) + + def put( + self, url: StrOrURL, *, data: Any = None, **kwargs: Any + ) -> "_RequestContextManager": + """Perform HTTP PUT request.""" + return _RequestContextManager( + self._request(hdrs.METH_PUT, url, data=data, **kwargs) + ) + + def patch( + self, url: StrOrURL, *, data: Any = None, **kwargs: Any + ) -> "_RequestContextManager": + """Perform HTTP PATCH request.""" + return _RequestContextManager( + self._request(hdrs.METH_PATCH, url, data=data, **kwargs) + ) + + def delete(self, url: StrOrURL, **kwargs: Any) -> "_RequestContextManager": + """Perform HTTP DELETE request.""" + return _RequestContextManager( + self._request(hdrs.METH_DELETE, url, **kwargs) + ) + + async def close(self) -> None: + """Close underlying connector. + + Release all acquired resources. + """ + if not self.closed: + if self._connector is not None and self._connector_owner: + await self._connector.close() + self._connector = None + + @property + def closed(self) -> bool: + """Is client session closed. + + A readonly property. + """ + return self._connector is None or self._connector.closed + + @property + def connector(self) -> Optional[BaseConnector]: + """Connector instance used for the session.""" + return self._connector + + @property + def cookie_jar(self) -> AbstractCookieJar: + """The session cookies.""" + return self._cookie_jar + + @property + def version(self) -> Tuple[int, int]: + """The session HTTP protocol version.""" + return self._version + + @property + def requote_redirect_url(self) -> bool: + """Do URL requoting on redirection handling.""" + return self._requote_redirect_url + + @requote_redirect_url.setter + def requote_redirect_url(self, val: bool) -> None: + """Do URL requoting on redirection handling.""" + warnings.warn( + "session.requote_redirect_url modification is deprecated #2778", + DeprecationWarning, + stacklevel=2, + ) + self._requote_redirect_url = val + + @property + def loop(self) -> asyncio.AbstractEventLoop: + """Session's loop.""" + warnings.warn( + "client.loop property is deprecated", DeprecationWarning, stacklevel=2 + ) + return self._loop + + @property + def timeout(self) -> ClientTimeout: + """Timeout for the session.""" + return self._timeout + + @property + def headers(self) -> "CIMultiDict[str]": + """The default headers of the client session.""" + return self._default_headers + + @property + def skip_auto_headers(self) -> FrozenSet[istr]: + """Headers for which autogeneration should be skipped""" + return self._skip_auto_headers + + @property + def auth(self) -> Optional[BasicAuth]: + """An object that represents HTTP Basic Authorization""" + return self._default_auth + + @property + def json_serialize(self) -> JSONEncoder: + """Json serializer callable""" + return self._json_serialize + + @property + def connector_owner(self) -> bool: + """Should connector be closed on session closing""" + return self._connector_owner + + @property + def raise_for_status( + self, + ) -> Union[bool, Callable[[ClientResponse], Awaitable[None]]]: + """Should `ClientResponse.raise_for_status()` be called for each response.""" + return self._raise_for_status + + @property + def auto_decompress(self) -> bool: + """Should the body response be automatically decompressed.""" + return self._auto_decompress + + @property + def trust_env(self) -> bool: + """ + Should proxies information from environment or netrc be trusted. + + Information is from HTTP_PROXY / HTTPS_PROXY environment variables + or ~/.netrc file if present. + """ + return self._trust_env + + @property + def trace_configs(self) -> List[TraceConfig]: + """A list of TraceConfig instances used for client tracing""" + return self._trace_configs + + def detach(self) -> None: + """Detach connector from session without closing the former. + + Session is switched to closed state anyway. + """ + self._connector = None + + def __enter__(self) -> None: + raise TypeError("Use async with instead") + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + # __exit__ should exist in pair with __enter__ but never executed + pass # pragma: no cover + + async def __aenter__(self) -> "ClientSession": + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + await self.close() + + +class _BaseRequestContextManager(Coroutine[Any, Any, _RetType], Generic[_RetType]): + + __slots__ = ("_coro", "_resp") + + def __init__(self, coro: Coroutine["asyncio.Future[Any]", None, _RetType]) -> None: + self._coro: Coroutine["asyncio.Future[Any]", None, _RetType] = coro + + def send(self, arg: None) -> "asyncio.Future[Any]": + return self._coro.send(arg) + + def throw(self, *args: Any, **kwargs: Any) -> "asyncio.Future[Any]": + return self._coro.throw(*args, **kwargs) + + def close(self) -> None: + return self._coro.close() + + def __await__(self) -> Generator[Any, None, _RetType]: + ret = self._coro.__await__() + return ret + + def __iter__(self) -> Generator[Any, None, _RetType]: + return self.__await__() + + async def __aenter__(self) -> _RetType: + self._resp: _RetType = await self._coro + return await self._resp.__aenter__() + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc: Optional[BaseException], + tb: Optional[TracebackType], + ) -> None: + await self._resp.__aexit__(exc_type, exc, tb) + + +_RequestContextManager = _BaseRequestContextManager[ClientResponse] +_WSRequestContextManager = _BaseRequestContextManager[ClientWebSocketResponse] + + +class _SessionRequestContextManager: + + __slots__ = ("_coro", "_resp", "_session") + + def __init__( + self, + coro: Coroutine["asyncio.Future[Any]", None, ClientResponse], + session: ClientSession, + ) -> None: + self._coro = coro + self._resp: Optional[ClientResponse] = None + self._session = session + + async def __aenter__(self) -> ClientResponse: + try: + self._resp = await self._coro + except BaseException: + await self._session.close() + raise + else: + return self._resp + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc: Optional[BaseException], + tb: Optional[TracebackType], + ) -> None: + assert self._resp is not None + self._resp.close() + await self._session.close() + + +if sys.version_info >= (3, 11) and TYPE_CHECKING: + + def request( + method: str, + url: StrOrURL, + *, + version: HttpVersion = http.HttpVersion11, + connector: Optional[BaseConnector] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + **kwargs: Unpack[_RequestOptions], + ) -> _SessionRequestContextManager: ... + +else: + + def request( + method: str, + url: StrOrURL, + *, + version: HttpVersion = http.HttpVersion11, + connector: Optional[BaseConnector] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + **kwargs: Any, + ) -> _SessionRequestContextManager: + """Constructs and sends a request. + + Returns response object. + method - HTTP method + url - request url + params - (optional) Dictionary or bytes to be sent in the query + string of the new request + data - (optional) Dictionary, bytes, or file-like object to + send in the body of the request + json - (optional) Any json compatible python object + headers - (optional) Dictionary of HTTP Headers to send with + the request + cookies - (optional) Dict object to send with the request + auth - (optional) BasicAuth named tuple represent HTTP Basic Auth + auth - aiohttp.helpers.BasicAuth + allow_redirects - (optional) If set to False, do not follow + redirects + version - Request HTTP version. + compress - Set to True if request has to be compressed + with deflate encoding. + chunked - Set to chunk size for chunked transfer encoding. + expect100 - Expect 100-continue response from server. + connector - BaseConnector sub-class instance to support + connection pooling. + read_until_eof - Read response until eof if response + does not have Content-Length header. + loop - Optional event loop. + timeout - Optional ClientTimeout settings structure, 5min + total timeout by default. + Usage:: + >>> import aiohttp + >>> async with aiohttp.request('GET', 'http://python.org/') as resp: + ... print(resp) + ... data = await resp.read() + + """ + connector_owner = False + if connector is None: + connector_owner = True + connector = TCPConnector(loop=loop, force_close=True) + + session = ClientSession( + loop=loop, + cookies=kwargs.pop("cookies", None), + version=version, + timeout=kwargs.pop("timeout", sentinel), + connector=connector, + connector_owner=connector_owner, + ) + + return _SessionRequestContextManager( + session._request(method, url, **kwargs), + session, + ) diff --git a/.venv/lib/python3.9/site-packages/aiohttp/client_exceptions.py b/.venv/lib/python3.9/site-packages/aiohttp/client_exceptions.py new file mode 100644 index 0000000..1d298e9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/client_exceptions.py @@ -0,0 +1,421 @@ +"""HTTP related errors.""" + +import asyncio +import warnings +from typing import TYPE_CHECKING, Optional, Tuple, Union + +from multidict import MultiMapping + +from .typedefs import StrOrURL + +if TYPE_CHECKING: + import ssl + + SSLContext = ssl.SSLContext +else: + try: + import ssl + + SSLContext = ssl.SSLContext + except ImportError: # pragma: no cover + ssl = SSLContext = None # type: ignore[assignment] + +if TYPE_CHECKING: + from .client_reqrep import ClientResponse, ConnectionKey, Fingerprint, RequestInfo + from .http_parser import RawResponseMessage +else: + RequestInfo = ClientResponse = ConnectionKey = RawResponseMessage = None + +__all__ = ( + "ClientError", + "ClientConnectionError", + "ClientConnectionResetError", + "ClientOSError", + "ClientConnectorError", + "ClientProxyConnectionError", + "ClientSSLError", + "ClientConnectorDNSError", + "ClientConnectorSSLError", + "ClientConnectorCertificateError", + "ConnectionTimeoutError", + "SocketTimeoutError", + "ServerConnectionError", + "ServerTimeoutError", + "ServerDisconnectedError", + "ServerFingerprintMismatch", + "ClientResponseError", + "ClientHttpProxyError", + "WSServerHandshakeError", + "ContentTypeError", + "ClientPayloadError", + "InvalidURL", + "InvalidUrlClientError", + "RedirectClientError", + "NonHttpUrlClientError", + "InvalidUrlRedirectClientError", + "NonHttpUrlRedirectClientError", + "WSMessageTypeError", +) + + +class ClientError(Exception): + """Base class for client connection errors.""" + + +class ClientResponseError(ClientError): + """Base class for exceptions that occur after getting a response. + + request_info: An instance of RequestInfo. + history: A sequence of responses, if redirects occurred. + status: HTTP status code. + message: Error message. + headers: Response headers. + """ + + def __init__( + self, + request_info: RequestInfo, + history: Tuple[ClientResponse, ...], + *, + code: Optional[int] = None, + status: Optional[int] = None, + message: str = "", + headers: Optional[MultiMapping[str]] = None, + ) -> None: + self.request_info = request_info + if code is not None: + if status is not None: + raise ValueError( + "Both code and status arguments are provided; " + "code is deprecated, use status instead" + ) + warnings.warn( + "code argument is deprecated, use status instead", + DeprecationWarning, + stacklevel=2, + ) + if status is not None: + self.status = status + elif code is not None: + self.status = code + else: + self.status = 0 + self.message = message + self.headers = headers + self.history = history + self.args = (request_info, history) + + def __str__(self) -> str: + return "{}, message={!r}, url={!r}".format( + self.status, + self.message, + str(self.request_info.real_url), + ) + + def __repr__(self) -> str: + args = f"{self.request_info!r}, {self.history!r}" + if self.status != 0: + args += f", status={self.status!r}" + if self.message != "": + args += f", message={self.message!r}" + if self.headers is not None: + args += f", headers={self.headers!r}" + return f"{type(self).__name__}({args})" + + @property + def code(self) -> int: + warnings.warn( + "code property is deprecated, use status instead", + DeprecationWarning, + stacklevel=2, + ) + return self.status + + @code.setter + def code(self, value: int) -> None: + warnings.warn( + "code property is deprecated, use status instead", + DeprecationWarning, + stacklevel=2, + ) + self.status = value + + +class ContentTypeError(ClientResponseError): + """ContentType found is not valid.""" + + +class WSServerHandshakeError(ClientResponseError): + """websocket server handshake error.""" + + +class ClientHttpProxyError(ClientResponseError): + """HTTP proxy error. + + Raised in :class:`aiohttp.connector.TCPConnector` if + proxy responds with status other than ``200 OK`` + on ``CONNECT`` request. + """ + + +class TooManyRedirects(ClientResponseError): + """Client was redirected too many times.""" + + +class ClientConnectionError(ClientError): + """Base class for client socket errors.""" + + +class ClientConnectionResetError(ClientConnectionError, ConnectionResetError): + """ConnectionResetError""" + + +class ClientOSError(ClientConnectionError, OSError): + """OSError error.""" + + +class ClientConnectorError(ClientOSError): + """Client connector error. + + Raised in :class:`aiohttp.connector.TCPConnector` if + a connection can not be established. + """ + + def __init__(self, connection_key: ConnectionKey, os_error: OSError) -> None: + self._conn_key = connection_key + self._os_error = os_error + super().__init__(os_error.errno, os_error.strerror) + self.args = (connection_key, os_error) + + @property + def os_error(self) -> OSError: + return self._os_error + + @property + def host(self) -> str: + return self._conn_key.host + + @property + def port(self) -> Optional[int]: + return self._conn_key.port + + @property + def ssl(self) -> Union[SSLContext, bool, "Fingerprint"]: + return self._conn_key.ssl + + def __str__(self) -> str: + return "Cannot connect to host {0.host}:{0.port} ssl:{1} [{2}]".format( + self, "default" if self.ssl is True else self.ssl, self.strerror + ) + + # OSError.__reduce__ does too much black magick + __reduce__ = BaseException.__reduce__ + + +class ClientConnectorDNSError(ClientConnectorError): + """DNS resolution failed during client connection. + + Raised in :class:`aiohttp.connector.TCPConnector` if + DNS resolution fails. + """ + + +class ClientProxyConnectionError(ClientConnectorError): + """Proxy connection error. + + Raised in :class:`aiohttp.connector.TCPConnector` if + connection to proxy can not be established. + """ + + +class UnixClientConnectorError(ClientConnectorError): + """Unix connector error. + + Raised in :py:class:`aiohttp.connector.UnixConnector` + if connection to unix socket can not be established. + """ + + def __init__( + self, path: str, connection_key: ConnectionKey, os_error: OSError + ) -> None: + self._path = path + super().__init__(connection_key, os_error) + + @property + def path(self) -> str: + return self._path + + def __str__(self) -> str: + return "Cannot connect to unix socket {0.path} ssl:{1} [{2}]".format( + self, "default" if self.ssl is True else self.ssl, self.strerror + ) + + +class ServerConnectionError(ClientConnectionError): + """Server connection errors.""" + + +class ServerDisconnectedError(ServerConnectionError): + """Server disconnected.""" + + def __init__(self, message: Union[RawResponseMessage, str, None] = None) -> None: + if message is None: + message = "Server disconnected" + + self.args = (message,) + self.message = message + + +class ServerTimeoutError(ServerConnectionError, asyncio.TimeoutError): + """Server timeout error.""" + + +class ConnectionTimeoutError(ServerTimeoutError): + """Connection timeout error.""" + + +class SocketTimeoutError(ServerTimeoutError): + """Socket timeout error.""" + + +class ServerFingerprintMismatch(ServerConnectionError): + """SSL certificate does not match expected fingerprint.""" + + def __init__(self, expected: bytes, got: bytes, host: str, port: int) -> None: + self.expected = expected + self.got = got + self.host = host + self.port = port + self.args = (expected, got, host, port) + + def __repr__(self) -> str: + return "<{} expected={!r} got={!r} host={!r} port={!r}>".format( + self.__class__.__name__, self.expected, self.got, self.host, self.port + ) + + +class ClientPayloadError(ClientError): + """Response payload error.""" + + +class InvalidURL(ClientError, ValueError): + """Invalid URL. + + URL used for fetching is malformed, e.g. it doesn't contains host + part. + """ + + # Derive from ValueError for backward compatibility + + def __init__(self, url: StrOrURL, description: Union[str, None] = None) -> None: + # The type of url is not yarl.URL because the exception can be raised + # on URL(url) call + self._url = url + self._description = description + + if description: + super().__init__(url, description) + else: + super().__init__(url) + + @property + def url(self) -> StrOrURL: + return self._url + + @property + def description(self) -> "str | None": + return self._description + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self}>" + + def __str__(self) -> str: + if self._description: + return f"{self._url} - {self._description}" + return str(self._url) + + +class InvalidUrlClientError(InvalidURL): + """Invalid URL client error.""" + + +class RedirectClientError(ClientError): + """Client redirect error.""" + + +class NonHttpUrlClientError(ClientError): + """Non http URL client error.""" + + +class InvalidUrlRedirectClientError(InvalidUrlClientError, RedirectClientError): + """Invalid URL redirect client error.""" + + +class NonHttpUrlRedirectClientError(NonHttpUrlClientError, RedirectClientError): + """Non http URL redirect client error.""" + + +class ClientSSLError(ClientConnectorError): + """Base error for ssl.*Errors.""" + + +if ssl is not None: + cert_errors = (ssl.CertificateError,) + cert_errors_bases = ( + ClientSSLError, + ssl.CertificateError, + ) + + ssl_errors = (ssl.SSLError,) + ssl_error_bases = (ClientSSLError, ssl.SSLError) +else: # pragma: no cover + cert_errors = tuple() + cert_errors_bases = ( + ClientSSLError, + ValueError, + ) + + ssl_errors = tuple() + ssl_error_bases = (ClientSSLError,) + + +class ClientConnectorSSLError(*ssl_error_bases): # type: ignore[misc] + """Response ssl error.""" + + +class ClientConnectorCertificateError(*cert_errors_bases): # type: ignore[misc] + """Response certificate error.""" + + def __init__( + self, connection_key: ConnectionKey, certificate_error: Exception + ) -> None: + self._conn_key = connection_key + self._certificate_error = certificate_error + self.args = (connection_key, certificate_error) + + @property + def certificate_error(self) -> Exception: + return self._certificate_error + + @property + def host(self) -> str: + return self._conn_key.host + + @property + def port(self) -> Optional[int]: + return self._conn_key.port + + @property + def ssl(self) -> bool: + return self._conn_key.is_ssl + + def __str__(self) -> str: + return ( + "Cannot connect to host {0.host}:{0.port} ssl:{0.ssl} " + "[{0.certificate_error.__class__.__name__}: " + "{0.certificate_error.args}]".format(self) + ) + + +class WSMessageTypeError(TypeError): + """WebSocket message type is not valid.""" diff --git a/.venv/lib/python3.9/site-packages/aiohttp/client_middleware_digest_auth.py b/.venv/lib/python3.9/site-packages/aiohttp/client_middleware_digest_auth.py new file mode 100644 index 0000000..5aab5ac --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/client_middleware_digest_auth.py @@ -0,0 +1,480 @@ +""" +Digest authentication middleware for aiohttp client. + +This middleware implements HTTP Digest Authentication according to RFC 7616, +providing a more secure alternative to Basic Authentication. It supports all +standard hash algorithms including MD5, SHA, SHA-256, SHA-512 and their session +variants, as well as both 'auth' and 'auth-int' quality of protection (qop) options. +""" + +import hashlib +import os +import re +import sys +import time +from typing import ( + Callable, + Dict, + Final, + FrozenSet, + List, + Literal, + Tuple, + TypedDict, + Union, +) + +from yarl import URL + +from . import hdrs +from .client_exceptions import ClientError +from .client_middlewares import ClientHandlerType +from .client_reqrep import ClientRequest, ClientResponse +from .payload import Payload + + +class DigestAuthChallenge(TypedDict, total=False): + realm: str + nonce: str + qop: str + algorithm: str + opaque: str + domain: str + stale: str + + +DigestFunctions: Dict[str, Callable[[bytes], "hashlib._Hash"]] = { + "MD5": hashlib.md5, + "MD5-SESS": hashlib.md5, + "SHA": hashlib.sha1, + "SHA-SESS": hashlib.sha1, + "SHA256": hashlib.sha256, + "SHA256-SESS": hashlib.sha256, + "SHA-256": hashlib.sha256, + "SHA-256-SESS": hashlib.sha256, + "SHA512": hashlib.sha512, + "SHA512-SESS": hashlib.sha512, + "SHA-512": hashlib.sha512, + "SHA-512-SESS": hashlib.sha512, +} + + +# Compile the regex pattern once at module level for performance +_HEADER_PAIRS_PATTERN = re.compile( + r'(?:^|\s|,\s*)(\w+)\s*=\s*(?:"((?:[^"\\]|\\.)*)"|([^\s,]+))' + if sys.version_info < (3, 11) + else r'(?:^|\s|,\s*)((?>\w+))\s*=\s*(?:"((?:[^"\\]|\\.)*)"|([^\s,]+))' + # +------------|--------|--|-|-|--|----|------|----|--||-----|-> Match valid start/sep + # +--------|--|-|-|--|----|------|----|--||-----|-> alphanumeric key (atomic + # | | | | | | | | || | group reduces backtracking) + # +--|-|-|--|----|------|----|--||-----|-> maybe whitespace + # | | | | | | | || | + # +-|-|--|----|------|----|--||-----|-> = (delimiter) + # +-|--|----|------|----|--||-----|-> maybe whitespace + # | | | | | || | + # +--|----|------|----|--||-----|-> group quoted or unquoted + # | | | | || | + # +----|------|----|--||-----|-> if quoted... + # +------|----|--||-----|-> anything but " or \ + # +----|--||-----|-> escaped characters allowed + # +--||-----|-> or can be empty string + # || | + # +|-----|-> if unquoted... + # +-----|-> anything but , or + # +-> at least one char req'd +) + + +# RFC 7616: Challenge parameters to extract +CHALLENGE_FIELDS: Final[ + Tuple[ + Literal["realm", "nonce", "qop", "algorithm", "opaque", "domain", "stale"], ... + ] +] = ( + "realm", + "nonce", + "qop", + "algorithm", + "opaque", + "domain", + "stale", +) + +# Supported digest authentication algorithms +# Use a tuple of sorted keys for predictable documentation and error messages +SUPPORTED_ALGORITHMS: Final[Tuple[str, ...]] = tuple(sorted(DigestFunctions.keys())) + +# RFC 7616: Fields that require quoting in the Digest auth header +# These fields must be enclosed in double quotes in the Authorization header. +# Algorithm, qop, and nc are never quoted per RFC specifications. +# This frozen set is used by the template-based header construction to +# automatically determine which fields need quotes. +QUOTED_AUTH_FIELDS: Final[FrozenSet[str]] = frozenset( + {"username", "realm", "nonce", "uri", "response", "opaque", "cnonce"} +) + + +def escape_quotes(value: str) -> str: + """Escape double quotes for HTTP header values.""" + return value.replace('"', '\\"') + + +def unescape_quotes(value: str) -> str: + """Unescape double quotes in HTTP header values.""" + return value.replace('\\"', '"') + + +def parse_header_pairs(header: str) -> Dict[str, str]: + """ + Parse key-value pairs from WWW-Authenticate or similar HTTP headers. + + This function handles the complex format of WWW-Authenticate header values, + supporting both quoted and unquoted values, proper handling of commas in + quoted values, and whitespace variations per RFC 7616. + + Examples of supported formats: + - key1="value1", key2=value2 + - key1 = "value1" , key2="value, with, commas" + - key1=value1,key2="value2" + - realm="example.com", nonce="12345", qop="auth" + + Args: + header: The header value string to parse + + Returns: + Dictionary mapping parameter names to their values + """ + return { + stripped_key: unescape_quotes(quoted_val) if quoted_val else unquoted_val + for key, quoted_val, unquoted_val in _HEADER_PAIRS_PATTERN.findall(header) + if (stripped_key := key.strip()) + } + + +class DigestAuthMiddleware: + """ + HTTP digest authentication middleware for aiohttp client. + + This middleware intercepts 401 Unauthorized responses containing a Digest + authentication challenge, calculates the appropriate digest credentials, + and automatically retries the request with the proper Authorization header. + + Features: + - Handles all aspects of Digest authentication handshake automatically + - Supports all standard hash algorithms: + - MD5, MD5-SESS + - SHA, SHA-SESS + - SHA256, SHA256-SESS, SHA-256, SHA-256-SESS + - SHA512, SHA512-SESS, SHA-512, SHA-512-SESS + - Supports 'auth' and 'auth-int' quality of protection modes + - Properly handles quoted strings and parameter parsing + - Includes replay attack protection with client nonce count tracking + - Supports preemptive authentication per RFC 7616 Section 3.6 + + Standards compliance: + - RFC 7616: HTTP Digest Access Authentication (primary reference) + - RFC 2617: HTTP Authentication (deprecated by RFC 7616) + - RFC 1945: Section 11.1 (username restrictions) + + Implementation notes: + The core digest calculation is inspired by the implementation in + https://github.com/requests/requests/blob/v2.18.4/requests/auth.py + with added support for modern digest auth features and error handling. + """ + + def __init__( + self, + login: str, + password: str, + preemptive: bool = True, + ) -> None: + if login is None: + raise ValueError("None is not allowed as login value") + + if password is None: + raise ValueError("None is not allowed as password value") + + if ":" in login: + raise ValueError('A ":" is not allowed in username (RFC 1945#section-11.1)') + + self._login_str: Final[str] = login + self._login_bytes: Final[bytes] = login.encode("utf-8") + self._password_bytes: Final[bytes] = password.encode("utf-8") + + self._last_nonce_bytes = b"" + self._nonce_count = 0 + self._challenge: DigestAuthChallenge = {} + self._preemptive: bool = preemptive + # Set of URLs defining the protection space + self._protection_space: List[str] = [] + + async def _encode( + self, method: str, url: URL, body: Union[Payload, Literal[b""]] + ) -> str: + """ + Build digest authorization header for the current challenge. + + Args: + method: The HTTP method (GET, POST, etc.) + url: The request URL + body: The request body (used for qop=auth-int) + + Returns: + A fully formatted Digest authorization header string + + Raises: + ClientError: If the challenge is missing required parameters or + contains unsupported values + + """ + challenge = self._challenge + if "realm" not in challenge: + raise ClientError( + "Malformed Digest auth challenge: Missing 'realm' parameter" + ) + + if "nonce" not in challenge: + raise ClientError( + "Malformed Digest auth challenge: Missing 'nonce' parameter" + ) + + # Empty realm values are allowed per RFC 7616 (SHOULD, not MUST, contain host name) + realm = challenge["realm"] + nonce = challenge["nonce"] + + # Empty nonce values are not allowed as they are security-critical for replay protection + if not nonce: + raise ClientError( + "Security issue: Digest auth challenge contains empty 'nonce' value" + ) + + qop_raw = challenge.get("qop", "") + # Preserve original algorithm case for response while using uppercase for processing + algorithm_original = challenge.get("algorithm", "MD5") + algorithm = algorithm_original.upper() + opaque = challenge.get("opaque", "") + + # Convert string values to bytes once + nonce_bytes = nonce.encode("utf-8") + realm_bytes = realm.encode("utf-8") + path = URL(url).path_qs + + # Process QoP + qop = "" + qop_bytes = b"" + if qop_raw: + valid_qops = {"auth", "auth-int"}.intersection( + {q.strip() for q in qop_raw.split(",") if q.strip()} + ) + if not valid_qops: + raise ClientError( + f"Digest auth error: Unsupported Quality of Protection (qop) value(s): {qop_raw}" + ) + + qop = "auth-int" if "auth-int" in valid_qops else "auth" + qop_bytes = qop.encode("utf-8") + + if algorithm not in DigestFunctions: + raise ClientError( + f"Digest auth error: Unsupported hash algorithm: {algorithm}. " + f"Supported algorithms: {', '.join(SUPPORTED_ALGORITHMS)}" + ) + hash_fn: Final = DigestFunctions[algorithm] + + def H(x: bytes) -> bytes: + """RFC 7616 Section 3: Hash function H(data) = hex(hash(data)).""" + return hash_fn(x).hexdigest().encode() + + def KD(s: bytes, d: bytes) -> bytes: + """RFC 7616 Section 3: KD(secret, data) = H(concat(secret, ":", data)).""" + return H(b":".join((s, d))) + + # Calculate A1 and A2 + A1 = b":".join((self._login_bytes, realm_bytes, self._password_bytes)) + A2 = f"{method.upper()}:{path}".encode() + if qop == "auth-int": + if isinstance(body, Payload): # will always be empty bytes unless Payload + entity_bytes = await body.as_bytes() # Get bytes from Payload + else: + entity_bytes = body + entity_hash = H(entity_bytes) + A2 = b":".join((A2, entity_hash)) + + HA1 = H(A1) + HA2 = H(A2) + + # Nonce count handling + if nonce_bytes == self._last_nonce_bytes: + self._nonce_count += 1 + else: + self._nonce_count = 1 + + self._last_nonce_bytes = nonce_bytes + ncvalue = f"{self._nonce_count:08x}" + ncvalue_bytes = ncvalue.encode("utf-8") + + # Generate client nonce + cnonce = hashlib.sha1( + b"".join( + [ + str(self._nonce_count).encode("utf-8"), + nonce_bytes, + time.ctime().encode("utf-8"), + os.urandom(8), + ] + ) + ).hexdigest()[:16] + cnonce_bytes = cnonce.encode("utf-8") + + # Special handling for session-based algorithms + if algorithm.upper().endswith("-SESS"): + HA1 = H(b":".join((HA1, nonce_bytes, cnonce_bytes))) + + # Calculate the response digest + if qop: + noncebit = b":".join( + (nonce_bytes, ncvalue_bytes, cnonce_bytes, qop_bytes, HA2) + ) + response_digest = KD(HA1, noncebit) + else: + response_digest = KD(HA1, b":".join((nonce_bytes, HA2))) + + # Define a dict mapping of header fields to their values + # Group fields into always-present, optional, and qop-dependent + header_fields = { + # Always present fields + "username": escape_quotes(self._login_str), + "realm": escape_quotes(realm), + "nonce": escape_quotes(nonce), + "uri": path, + "response": response_digest.decode(), + "algorithm": algorithm_original, + } + + # Optional fields + if opaque: + header_fields["opaque"] = escape_quotes(opaque) + + # QoP-dependent fields + if qop: + header_fields["qop"] = qop + header_fields["nc"] = ncvalue + header_fields["cnonce"] = cnonce + + # Build header using templates for each field type + pairs: List[str] = [] + for field, value in header_fields.items(): + if field in QUOTED_AUTH_FIELDS: + pairs.append(f'{field}="{value}"') + else: + pairs.append(f"{field}={value}") + + return f"Digest {', '.join(pairs)}" + + def _in_protection_space(self, url: URL) -> bool: + """ + Check if the given URL is within the current protection space. + + According to RFC 7616, a URI is in the protection space if any URI + in the protection space is a prefix of it (after both have been made absolute). + """ + request_str = str(url) + for space_str in self._protection_space: + # Check if request starts with space URL + if not request_str.startswith(space_str): + continue + # Exact match or space ends with / (proper directory prefix) + if len(request_str) == len(space_str) or space_str[-1] == "/": + return True + # Check next char is / to ensure proper path boundary + if request_str[len(space_str)] == "/": + return True + return False + + def _authenticate(self, response: ClientResponse) -> bool: + """ + Takes the given response and tries digest-auth, if needed. + + Returns true if the original request must be resent. + """ + if response.status != 401: + return False + + auth_header = response.headers.get("www-authenticate", "") + if not auth_header: + return False # No authentication header present + + method, sep, headers = auth_header.partition(" ") + if not sep: + # No space found in www-authenticate header + return False # Malformed auth header, missing scheme separator + + if method.lower() != "digest": + # Not a digest auth challenge (could be Basic, Bearer, etc.) + return False + + if not headers: + # We have a digest scheme but no parameters + return False # Malformed digest header, missing parameters + + # We have a digest auth header with content + if not (header_pairs := parse_header_pairs(headers)): + # Failed to parse any key-value pairs + return False # Malformed digest header, no valid parameters + + # Extract challenge parameters + self._challenge = {} + for field in CHALLENGE_FIELDS: + if value := header_pairs.get(field): + self._challenge[field] = value + + # Update protection space based on domain parameter or default to origin + origin = response.url.origin() + + if domain := self._challenge.get("domain"): + # Parse space-separated list of URIs + self._protection_space = [] + for uri in domain.split(): + # Remove quotes if present + uri = uri.strip('"') + if uri.startswith("/"): + # Path-absolute, relative to origin + self._protection_space.append(str(origin.join(URL(uri)))) + else: + # Absolute URI + self._protection_space.append(str(URL(uri))) + else: + # No domain specified, protection space is entire origin + self._protection_space = [str(origin)] + + # Return True only if we found at least one challenge parameter + return bool(self._challenge) + + async def __call__( + self, request: ClientRequest, handler: ClientHandlerType + ) -> ClientResponse: + """Run the digest auth middleware.""" + response = None + for retry_count in range(2): + # Apply authorization header if: + # 1. This is a retry after 401 (retry_count > 0), OR + # 2. Preemptive auth is enabled AND we have a challenge AND the URL is in protection space + if retry_count > 0 or ( + self._preemptive + and self._challenge + and self._in_protection_space(request.url) + ): + request.headers[hdrs.AUTHORIZATION] = await self._encode( + request.method, request.url, request.body + ) + + # Send the request + response = await handler(request) + + # Check if we need to authenticate + if not self._authenticate(response): + break + + # At this point, response is guaranteed to be defined + assert response is not None + return response diff --git a/.venv/lib/python3.9/site-packages/aiohttp/client_middlewares.py b/.venv/lib/python3.9/site-packages/aiohttp/client_middlewares.py new file mode 100644 index 0000000..3ca2cb2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/client_middlewares.py @@ -0,0 +1,55 @@ +"""Client middleware support.""" + +from collections.abc import Awaitable, Callable, Sequence + +from .client_reqrep import ClientRequest, ClientResponse + +__all__ = ("ClientMiddlewareType", "ClientHandlerType", "build_client_middlewares") + +# Type alias for client request handlers - functions that process requests and return responses +ClientHandlerType = Callable[[ClientRequest], Awaitable[ClientResponse]] + +# Type for client middleware - similar to server but uses ClientRequest/ClientResponse +ClientMiddlewareType = Callable[ + [ClientRequest, ClientHandlerType], Awaitable[ClientResponse] +] + + +def build_client_middlewares( + handler: ClientHandlerType, + middlewares: Sequence[ClientMiddlewareType], +) -> ClientHandlerType: + """ + Apply middlewares to request handler. + + The middlewares are applied in reverse order, so the first middleware + in the list wraps all subsequent middlewares and the handler. + + This implementation avoids using partial/update_wrapper to minimize overhead + and doesn't cache to avoid holding references to stateful middleware. + """ + # Optimize for single middleware case + if len(middlewares) == 1: + middleware = middlewares[0] + + async def single_middleware_handler(req: ClientRequest) -> ClientResponse: + return await middleware(req, handler) + + return single_middleware_handler + + # Build the chain for multiple middlewares + current_handler = handler + + for middleware in reversed(middlewares): + # Create a new closure that captures the current state + def make_wrapper( + mw: ClientMiddlewareType, next_h: ClientHandlerType + ) -> ClientHandlerType: + async def wrapped(req: ClientRequest) -> ClientResponse: + return await mw(req, next_h) + + return wrapped + + current_handler = make_wrapper(middleware, current_handler) + + return current_handler diff --git a/.venv/lib/python3.9/site-packages/aiohttp/client_proto.py b/.venv/lib/python3.9/site-packages/aiohttp/client_proto.py new file mode 100644 index 0000000..e2fb1ce --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/client_proto.py @@ -0,0 +1,359 @@ +import asyncio +from contextlib import suppress +from typing import Any, Optional, Tuple, Union + +from .base_protocol import BaseProtocol +from .client_exceptions import ( + ClientConnectionError, + ClientOSError, + ClientPayloadError, + ServerDisconnectedError, + SocketTimeoutError, +) +from .helpers import ( + _EXC_SENTINEL, + EMPTY_BODY_STATUS_CODES, + BaseTimerContext, + set_exception, + set_result, +) +from .http import HttpResponseParser, RawResponseMessage +from .http_exceptions import HttpProcessingError +from .streams import EMPTY_PAYLOAD, DataQueue, StreamReader + + +class ResponseHandler(BaseProtocol, DataQueue[Tuple[RawResponseMessage, StreamReader]]): + """Helper class to adapt between Protocol and StreamReader.""" + + def __init__(self, loop: asyncio.AbstractEventLoop) -> None: + BaseProtocol.__init__(self, loop=loop) + DataQueue.__init__(self, loop) + + self._should_close = False + + self._payload: Optional[StreamReader] = None + self._skip_payload = False + self._payload_parser = None + + self._timer = None + + self._tail = b"" + self._upgraded = False + self._parser: Optional[HttpResponseParser] = None + + self._read_timeout: Optional[float] = None + self._read_timeout_handle: Optional[asyncio.TimerHandle] = None + + self._timeout_ceil_threshold: Optional[float] = 5 + + self._closed: Union[None, asyncio.Future[None]] = None + self._connection_lost_called = False + + @property + def closed(self) -> Union[None, asyncio.Future[None]]: + """Future that is set when the connection is closed. + + This property returns a Future that will be completed when the connection + is closed. The Future is created lazily on first access to avoid creating + futures that will never be awaited. + + Returns: + - A Future[None] if the connection is still open or was closed after + this property was accessed + - None if connection_lost() was already called before this property + was ever accessed (indicating no one is waiting for the closure) + """ + if self._closed is None and not self._connection_lost_called: + self._closed = self._loop.create_future() + return self._closed + + @property + def upgraded(self) -> bool: + return self._upgraded + + @property + def should_close(self) -> bool: + return bool( + self._should_close + or (self._payload is not None and not self._payload.is_eof()) + or self._upgraded + or self._exception is not None + or self._payload_parser is not None + or self._buffer + or self._tail + ) + + def force_close(self) -> None: + self._should_close = True + + def close(self) -> None: + self._exception = None # Break cyclic references + transport = self.transport + if transport is not None: + transport.close() + self.transport = None + self._payload = None + self._drop_timeout() + + def abort(self) -> None: + self._exception = None # Break cyclic references + transport = self.transport + if transport is not None: + transport.abort() + self.transport = None + self._payload = None + self._drop_timeout() + + def is_connected(self) -> bool: + return self.transport is not None and not self.transport.is_closing() + + def connection_lost(self, exc: Optional[BaseException]) -> None: + self._connection_lost_called = True + self._drop_timeout() + + original_connection_error = exc + reraised_exc = original_connection_error + + connection_closed_cleanly = original_connection_error is None + + if self._closed is not None: + # If someone is waiting for the closed future, + # we should set it to None or an exception. If + # self._closed is None, it means that + # connection_lost() was called already + # or nobody is waiting for it. + if connection_closed_cleanly: + set_result(self._closed, None) + else: + assert original_connection_error is not None + set_exception( + self._closed, + ClientConnectionError( + f"Connection lost: {original_connection_error !s}", + ), + original_connection_error, + ) + + if self._payload_parser is not None: + with suppress(Exception): # FIXME: log this somehow? + self._payload_parser.feed_eof() + + uncompleted = None + if self._parser is not None: + try: + uncompleted = self._parser.feed_eof() + except Exception as underlying_exc: + if self._payload is not None: + client_payload_exc_msg = ( + f"Response payload is not completed: {underlying_exc !r}" + ) + if not connection_closed_cleanly: + client_payload_exc_msg = ( + f"{client_payload_exc_msg !s}. " + f"{original_connection_error !r}" + ) + set_exception( + self._payload, + ClientPayloadError(client_payload_exc_msg), + underlying_exc, + ) + + if not self.is_eof(): + if isinstance(original_connection_error, OSError): + reraised_exc = ClientOSError(*original_connection_error.args) + if connection_closed_cleanly: + reraised_exc = ServerDisconnectedError(uncompleted) + # assigns self._should_close to True as side effect, + # we do it anyway below + underlying_non_eof_exc = ( + _EXC_SENTINEL + if connection_closed_cleanly + else original_connection_error + ) + assert underlying_non_eof_exc is not None + assert reraised_exc is not None + self.set_exception(reraised_exc, underlying_non_eof_exc) + + self._should_close = True + self._parser = None + self._payload = None + self._payload_parser = None + self._reading_paused = False + + super().connection_lost(reraised_exc) + + def eof_received(self) -> None: + # should call parser.feed_eof() most likely + self._drop_timeout() + + def pause_reading(self) -> None: + super().pause_reading() + self._drop_timeout() + + def resume_reading(self) -> None: + super().resume_reading() + self._reschedule_timeout() + + def set_exception( + self, + exc: BaseException, + exc_cause: BaseException = _EXC_SENTINEL, + ) -> None: + self._should_close = True + self._drop_timeout() + super().set_exception(exc, exc_cause) + + def set_parser(self, parser: Any, payload: Any) -> None: + # TODO: actual types are: + # parser: WebSocketReader + # payload: WebSocketDataQueue + # but they are not generi enough + # Need an ABC for both types + self._payload = payload + self._payload_parser = parser + + self._drop_timeout() + + if self._tail: + data, self._tail = self._tail, b"" + self.data_received(data) + + def set_response_params( + self, + *, + timer: Optional[BaseTimerContext] = None, + skip_payload: bool = False, + read_until_eof: bool = False, + auto_decompress: bool = True, + read_timeout: Optional[float] = None, + read_bufsize: int = 2**16, + timeout_ceil_threshold: float = 5, + max_line_size: int = 8190, + max_field_size: int = 8190, + ) -> None: + self._skip_payload = skip_payload + + self._read_timeout = read_timeout + + self._timeout_ceil_threshold = timeout_ceil_threshold + + self._parser = HttpResponseParser( + self, + self._loop, + read_bufsize, + timer=timer, + payload_exception=ClientPayloadError, + response_with_body=not skip_payload, + read_until_eof=read_until_eof, + auto_decompress=auto_decompress, + max_line_size=max_line_size, + max_field_size=max_field_size, + ) + + if self._tail: + data, self._tail = self._tail, b"" + self.data_received(data) + + def _drop_timeout(self) -> None: + if self._read_timeout_handle is not None: + self._read_timeout_handle.cancel() + self._read_timeout_handle = None + + def _reschedule_timeout(self) -> None: + timeout = self._read_timeout + if self._read_timeout_handle is not None: + self._read_timeout_handle.cancel() + + if timeout: + self._read_timeout_handle = self._loop.call_later( + timeout, self._on_read_timeout + ) + else: + self._read_timeout_handle = None + + def start_timeout(self) -> None: + self._reschedule_timeout() + + @property + def read_timeout(self) -> Optional[float]: + return self._read_timeout + + @read_timeout.setter + def read_timeout(self, read_timeout: Optional[float]) -> None: + self._read_timeout = read_timeout + + def _on_read_timeout(self) -> None: + exc = SocketTimeoutError("Timeout on reading data from socket") + self.set_exception(exc) + if self._payload is not None: + set_exception(self._payload, exc) + + def data_received(self, data: bytes) -> None: + self._reschedule_timeout() + + if not data: + return + + # custom payload parser - currently always WebSocketReader + if self._payload_parser is not None: + eof, tail = self._payload_parser.feed_data(data) + if eof: + self._payload = None + self._payload_parser = None + + if tail: + self.data_received(tail) + return + + if self._upgraded or self._parser is None: + # i.e. websocket connection, websocket parser is not set yet + self._tail += data + return + + # parse http messages + try: + messages, upgraded, tail = self._parser.feed_data(data) + except BaseException as underlying_exc: + if self.transport is not None: + # connection.release() could be called BEFORE + # data_received(), the transport is already + # closed in this case + self.transport.close() + # should_close is True after the call + if isinstance(underlying_exc, HttpProcessingError): + exc = HttpProcessingError( + code=underlying_exc.code, + message=underlying_exc.message, + headers=underlying_exc.headers, + ) + else: + exc = HttpProcessingError() + self.set_exception(exc, underlying_exc) + return + + self._upgraded = upgraded + + payload: Optional[StreamReader] = None + for message, payload in messages: + if message.should_close: + self._should_close = True + + self._payload = payload + + if self._skip_payload or message.code in EMPTY_BODY_STATUS_CODES: + self.feed_data((message, EMPTY_PAYLOAD), 0) + else: + self.feed_data((message, payload), 0) + + if payload is not None: + # new message(s) was processed + # register timeout handler unsubscribing + # either on end-of-stream or immediately for + # EMPTY_PAYLOAD + if payload is not EMPTY_PAYLOAD: + payload.on_eof(self._drop_timeout) + else: + self._drop_timeout() + + if upgraded and tail: + self.data_received(tail) diff --git a/.venv/lib/python3.9/site-packages/aiohttp/client_reqrep.py b/.venv/lib/python3.9/site-packages/aiohttp/client_reqrep.py new file mode 100644 index 0000000..a9e0795 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/client_reqrep.py @@ -0,0 +1,1536 @@ +import asyncio +import codecs +import contextlib +import functools +import io +import re +import sys +import traceback +import warnings +from collections.abc import Mapping +from hashlib import md5, sha1, sha256 +from http.cookies import Morsel, SimpleCookie +from types import MappingProxyType, TracebackType +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + Iterable, + List, + Literal, + NamedTuple, + Optional, + Tuple, + Type, + Union, +) + +import attr +from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy +from yarl import URL + +from . import hdrs, helpers, http, multipart, payload +from ._cookie_helpers import ( + parse_cookie_header, + parse_set_cookie_headers, + preserve_morsel_with_coded_value, +) +from .abc import AbstractStreamWriter +from .client_exceptions import ( + ClientConnectionError, + ClientOSError, + ClientResponseError, + ContentTypeError, + InvalidURL, + ServerFingerprintMismatch, +) +from .compression_utils import HAS_BROTLI, HAS_ZSTD +from .formdata import FormData +from .helpers import ( + _SENTINEL, + BaseTimerContext, + BasicAuth, + HeadersMixin, + TimerNoop, + noop, + reify, + sentinel, + set_exception, + set_result, +) +from .http import ( + SERVER_SOFTWARE, + HttpVersion, + HttpVersion10, + HttpVersion11, + StreamWriter, +) +from .streams import StreamReader +from .typedefs import ( + DEFAULT_JSON_DECODER, + JSONDecoder, + LooseCookies, + LooseHeaders, + Query, + RawHeaders, +) + +if TYPE_CHECKING: + import ssl + from ssl import SSLContext +else: + try: + import ssl + from ssl import SSLContext + except ImportError: # pragma: no cover + ssl = None # type: ignore[assignment] + SSLContext = object # type: ignore[misc,assignment] + + +__all__ = ("ClientRequest", "ClientResponse", "RequestInfo", "Fingerprint") + + +if TYPE_CHECKING: + from .client import ClientSession + from .connector import Connection + from .tracing import Trace + + +_CONNECTION_CLOSED_EXCEPTION = ClientConnectionError("Connection closed") +_CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]") +json_re = re.compile(r"^application/(?:[\w.+-]+?\+)?json") + + +def _gen_default_accept_encoding() -> str: + encodings = [ + "gzip", + "deflate", + ] + if HAS_BROTLI: + encodings.append("br") + if HAS_ZSTD: + encodings.append("zstd") + return ", ".join(encodings) + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class ContentDisposition: + type: Optional[str] + parameters: "MappingProxyType[str, str]" + filename: Optional[str] + + +class _RequestInfo(NamedTuple): + url: URL + method: str + headers: "CIMultiDictProxy[str]" + real_url: URL + + +class RequestInfo(_RequestInfo): + + def __new__( + cls, + url: URL, + method: str, + headers: "CIMultiDictProxy[str]", + real_url: Union[URL, _SENTINEL] = sentinel, + ) -> "RequestInfo": + """Create a new RequestInfo instance. + + For backwards compatibility, the real_url parameter is optional. + """ + return tuple.__new__( + cls, (url, method, headers, url if real_url is sentinel else real_url) + ) + + +class Fingerprint: + HASHFUNC_BY_DIGESTLEN = { + 16: md5, + 20: sha1, + 32: sha256, + } + + def __init__(self, fingerprint: bytes) -> None: + digestlen = len(fingerprint) + hashfunc = self.HASHFUNC_BY_DIGESTLEN.get(digestlen) + if not hashfunc: + raise ValueError("fingerprint has invalid length") + elif hashfunc is md5 or hashfunc is sha1: + raise ValueError("md5 and sha1 are insecure and not supported. Use sha256.") + self._hashfunc = hashfunc + self._fingerprint = fingerprint + + @property + def fingerprint(self) -> bytes: + return self._fingerprint + + def check(self, transport: asyncio.Transport) -> None: + if not transport.get_extra_info("sslcontext"): + return + sslobj = transport.get_extra_info("ssl_object") + cert = sslobj.getpeercert(binary_form=True) + got = self._hashfunc(cert).digest() + if got != self._fingerprint: + host, port, *_ = transport.get_extra_info("peername") + raise ServerFingerprintMismatch(self._fingerprint, got, host, port) + + +if ssl is not None: + SSL_ALLOWED_TYPES = (ssl.SSLContext, bool, Fingerprint, type(None)) +else: # pragma: no cover + SSL_ALLOWED_TYPES = (bool, type(None)) + + +def _merge_ssl_params( + ssl: Union["SSLContext", bool, Fingerprint], + verify_ssl: Optional[bool], + ssl_context: Optional["SSLContext"], + fingerprint: Optional[bytes], +) -> Union["SSLContext", bool, Fingerprint]: + if ssl is None: + ssl = True # Double check for backwards compatibility + if verify_ssl is not None and not verify_ssl: + warnings.warn( + "verify_ssl is deprecated, use ssl=False instead", + DeprecationWarning, + stacklevel=3, + ) + if ssl is not True: + raise ValueError( + "verify_ssl, ssl_context, fingerprint and ssl " + "parameters are mutually exclusive" + ) + else: + ssl = False + if ssl_context is not None: + warnings.warn( + "ssl_context is deprecated, use ssl=context instead", + DeprecationWarning, + stacklevel=3, + ) + if ssl is not True: + raise ValueError( + "verify_ssl, ssl_context, fingerprint and ssl " + "parameters are mutually exclusive" + ) + else: + ssl = ssl_context + if fingerprint is not None: + warnings.warn( + "fingerprint is deprecated, use ssl=Fingerprint(fingerprint) instead", + DeprecationWarning, + stacklevel=3, + ) + if ssl is not True: + raise ValueError( + "verify_ssl, ssl_context, fingerprint and ssl " + "parameters are mutually exclusive" + ) + else: + ssl = Fingerprint(fingerprint) + if not isinstance(ssl, SSL_ALLOWED_TYPES): + raise TypeError( + "ssl should be SSLContext, bool, Fingerprint or None, " + "got {!r} instead.".format(ssl) + ) + return ssl + + +_SSL_SCHEMES = frozenset(("https", "wss")) + + +# ConnectionKey is a NamedTuple because it is used as a key in a dict +# and a set in the connector. Since a NamedTuple is a tuple it uses +# the fast native tuple __hash__ and __eq__ implementation in CPython. +class ConnectionKey(NamedTuple): + # the key should contain an information about used proxy / TLS + # to prevent reusing wrong connections from a pool + host: str + port: Optional[int] + is_ssl: bool + ssl: Union[SSLContext, bool, Fingerprint] + proxy: Optional[URL] + proxy_auth: Optional[BasicAuth] + proxy_headers_hash: Optional[int] # hash(CIMultiDict) + + +def _is_expected_content_type( + response_content_type: str, expected_content_type: str +) -> bool: + if expected_content_type == "application/json": + return json_re.match(response_content_type) is not None + return expected_content_type in response_content_type + + +def _warn_if_unclosed_payload(payload: payload.Payload, stacklevel: int = 2) -> None: + """Warn if the payload is not closed. + + Callers must check that the body is a Payload before calling this method. + + Args: + payload: The payload to check + stacklevel: Stack level for the warning (default 2 for direct callers) + """ + if not payload.autoclose and not payload.consumed: + warnings.warn( + "The previous request body contains unclosed resources. " + "Use await request.update_body() instead of setting request.body " + "directly to properly close resources and avoid leaks.", + ResourceWarning, + stacklevel=stacklevel, + ) + + +class ClientResponse(HeadersMixin): + + # Some of these attributes are None when created, + # but will be set by the start() method. + # As the end user will likely never see the None values, we cheat the types below. + # from the Status-Line of the response + version: Optional[HttpVersion] = None # HTTP-Version + status: int = None # type: ignore[assignment] # Status-Code + reason: Optional[str] = None # Reason-Phrase + + content: StreamReader = None # type: ignore[assignment] # Payload stream + _body: Optional[bytes] = None + _headers: CIMultiDictProxy[str] = None # type: ignore[assignment] + _history: Tuple["ClientResponse", ...] = () + _raw_headers: RawHeaders = None # type: ignore[assignment] + + _connection: Optional["Connection"] = None # current connection + _cookies: Optional[SimpleCookie] = None + _raw_cookie_headers: Optional[Tuple[str, ...]] = None + _continue: Optional["asyncio.Future[bool]"] = None + _source_traceback: Optional[traceback.StackSummary] = None + _session: Optional["ClientSession"] = None + # set up by ClientRequest after ClientResponse object creation + # post-init stage allows to not change ctor signature + _closed = True # to allow __del__ for non-initialized properly response + _released = False + _in_context = False + + _resolve_charset: Callable[["ClientResponse", bytes], str] = lambda *_: "utf-8" + + __writer: Optional["asyncio.Task[None]"] = None + + def __init__( + self, + method: str, + url: URL, + *, + writer: "Optional[asyncio.Task[None]]", + continue100: Optional["asyncio.Future[bool]"], + timer: BaseTimerContext, + request_info: RequestInfo, + traces: List["Trace"], + loop: asyncio.AbstractEventLoop, + session: "ClientSession", + ) -> None: + # URL forbids subclasses, so a simple type check is enough. + assert type(url) is URL + + self.method = method + + self._real_url = url + self._url = url.with_fragment(None) if url.raw_fragment else url + if writer is not None: + self._writer = writer + if continue100 is not None: + self._continue = continue100 + self._request_info = request_info + self._timer = timer if timer is not None else TimerNoop() + self._cache: Dict[str, Any] = {} + self._traces = traces + self._loop = loop + # Save reference to _resolve_charset, so that get_encoding() will still + # work after the response has finished reading the body. + # TODO: Fix session=None in tests (see ClientRequest.__init__). + if session is not None: + # store a reference to session #1985 + self._session = session + self._resolve_charset = session._resolve_charset + if loop.get_debug(): + self._source_traceback = traceback.extract_stack(sys._getframe(1)) + + def __reset_writer(self, _: object = None) -> None: + self.__writer = None + + @property + def _writer(self) -> Optional["asyncio.Task[None]"]: + """The writer task for streaming data. + + _writer is only provided for backwards compatibility + for subclasses that may need to access it. + """ + return self.__writer + + @_writer.setter + def _writer(self, writer: Optional["asyncio.Task[None]"]) -> None: + """Set the writer task for streaming data.""" + if self.__writer is not None: + self.__writer.remove_done_callback(self.__reset_writer) + self.__writer = writer + if writer is None: + return + if writer.done(): + # The writer is already done, so we can clear it immediately. + self.__writer = None + else: + writer.add_done_callback(self.__reset_writer) + + @property + def cookies(self) -> SimpleCookie: + if self._cookies is None: + if self._raw_cookie_headers is not None: + # Parse cookies for response.cookies (SimpleCookie for backward compatibility) + cookies = SimpleCookie() + # Use parse_set_cookie_headers for more lenient parsing that handles + # malformed cookies better than SimpleCookie.load + cookies.update(parse_set_cookie_headers(self._raw_cookie_headers)) + self._cookies = cookies + else: + self._cookies = SimpleCookie() + return self._cookies + + @cookies.setter + def cookies(self, cookies: SimpleCookie) -> None: + self._cookies = cookies + # Generate raw cookie headers from the SimpleCookie + if cookies: + self._raw_cookie_headers = tuple( + morsel.OutputString() for morsel in cookies.values() + ) + else: + self._raw_cookie_headers = None + + @reify + def url(self) -> URL: + return self._url + + @reify + def url_obj(self) -> URL: + warnings.warn("Deprecated, use .url #1654", DeprecationWarning, stacklevel=2) + return self._url + + @reify + def real_url(self) -> URL: + return self._real_url + + @reify + def host(self) -> str: + assert self._url.host is not None + return self._url.host + + @reify + def headers(self) -> "CIMultiDictProxy[str]": + return self._headers + + @reify + def raw_headers(self) -> RawHeaders: + return self._raw_headers + + @reify + def request_info(self) -> RequestInfo: + return self._request_info + + @reify + def content_disposition(self) -> Optional[ContentDisposition]: + raw = self._headers.get(hdrs.CONTENT_DISPOSITION) + if raw is None: + return None + disposition_type, params_dct = multipart.parse_content_disposition(raw) + params = MappingProxyType(params_dct) + filename = multipart.content_disposition_filename(params) + return ContentDisposition(disposition_type, params, filename) + + def __del__(self, _warnings: Any = warnings) -> None: + if self._closed: + return + + if self._connection is not None: + self._connection.release() + self._cleanup_writer() + + if self._loop.get_debug(): + kwargs = {"source": self} + _warnings.warn(f"Unclosed response {self!r}", ResourceWarning, **kwargs) + context = {"client_response": self, "message": "Unclosed response"} + if self._source_traceback: + context["source_traceback"] = self._source_traceback + self._loop.call_exception_handler(context) + + def __repr__(self) -> str: + out = io.StringIO() + ascii_encodable_url = str(self.url) + if self.reason: + ascii_encodable_reason = self.reason.encode( + "ascii", "backslashreplace" + ).decode("ascii") + else: + ascii_encodable_reason = "None" + print( + "".format( + ascii_encodable_url, self.status, ascii_encodable_reason + ), + file=out, + ) + print(self.headers, file=out) + return out.getvalue() + + @property + def connection(self) -> Optional["Connection"]: + return self._connection + + @reify + def history(self) -> Tuple["ClientResponse", ...]: + """A sequence of of responses, if redirects occurred.""" + return self._history + + @reify + def links(self) -> "MultiDictProxy[MultiDictProxy[Union[str, URL]]]": + links_str = ", ".join(self.headers.getall("link", [])) + + if not links_str: + return MultiDictProxy(MultiDict()) + + links: MultiDict[MultiDictProxy[Union[str, URL]]] = MultiDict() + + for val in re.split(r",(?=\s*<)", links_str): + match = re.match(r"\s*<(.*)>(.*)", val) + if match is None: # pragma: no cover + # the check exists to suppress mypy error + continue + url, params_str = match.groups() + params = params_str.split(";")[1:] + + link: MultiDict[Union[str, URL]] = MultiDict() + + for param in params: + match = re.match(r"^\s*(\S*)\s*=\s*(['\"]?)(.*?)(\2)\s*$", param, re.M) + if match is None: # pragma: no cover + # the check exists to suppress mypy error + continue + key, _, value, _ = match.groups() + + link.add(key, value) + + key = link.get("rel", url) + + link.add("url", self.url.join(URL(url))) + + links.add(str(key), MultiDictProxy(link)) + + return MultiDictProxy(links) + + async def start(self, connection: "Connection") -> "ClientResponse": + """Start response processing.""" + self._closed = False + self._protocol = connection.protocol + self._connection = connection + + with self._timer: + while True: + # read response + try: + protocol = self._protocol + message, payload = await protocol.read() # type: ignore[union-attr] + except http.HttpProcessingError as exc: + raise ClientResponseError( + self.request_info, + self.history, + status=exc.code, + message=exc.message, + headers=exc.headers, + ) from exc + + if message.code < 100 or message.code > 199 or message.code == 101: + break + + if self._continue is not None: + set_result(self._continue, True) + self._continue = None + + # payload eof handler + payload.on_eof(self._response_eof) + + # response status + self.version = message.version + self.status = message.code + self.reason = message.reason + + # headers + self._headers = message.headers # type is CIMultiDictProxy + self._raw_headers = message.raw_headers # type is Tuple[bytes, bytes] + + # payload + self.content = payload + + # cookies + if cookie_hdrs := self.headers.getall(hdrs.SET_COOKIE, ()): + # Store raw cookie headers for CookieJar + self._raw_cookie_headers = tuple(cookie_hdrs) + return self + + def _response_eof(self) -> None: + if self._closed: + return + + # protocol could be None because connection could be detached + protocol = self._connection and self._connection.protocol + if protocol is not None and protocol.upgraded: + return + + self._closed = True + self._cleanup_writer() + self._release_connection() + + @property + def closed(self) -> bool: + return self._closed + + def close(self) -> None: + if not self._released: + self._notify_content() + + self._closed = True + if self._loop is None or self._loop.is_closed(): + return + + self._cleanup_writer() + if self._connection is not None: + self._connection.close() + self._connection = None + + def release(self) -> Any: + if not self._released: + self._notify_content() + + self._closed = True + + self._cleanup_writer() + self._release_connection() + return noop() + + @property + def ok(self) -> bool: + """Returns ``True`` if ``status`` is less than ``400``, ``False`` if not. + + This is **not** a check for ``200 OK`` but a check that the response + status is under 400. + """ + return 400 > self.status + + def raise_for_status(self) -> None: + if not self.ok: + # reason should always be not None for a started response + assert self.reason is not None + + # If we're in a context we can rely on __aexit__() to release as the + # exception propagates. + if not self._in_context: + self.release() + + raise ClientResponseError( + self.request_info, + self.history, + status=self.status, + message=self.reason, + headers=self.headers, + ) + + def _release_connection(self) -> None: + if self._connection is not None: + if self.__writer is None: + self._connection.release() + self._connection = None + else: + self.__writer.add_done_callback(lambda f: self._release_connection()) + + async def _wait_released(self) -> None: + if self.__writer is not None: + try: + await self.__writer + except asyncio.CancelledError: + if ( + sys.version_info >= (3, 11) + and (task := asyncio.current_task()) + and task.cancelling() + ): + raise + self._release_connection() + + def _cleanup_writer(self) -> None: + if self.__writer is not None: + self.__writer.cancel() + self._session = None + + def _notify_content(self) -> None: + content = self.content + if content and content.exception() is None: + set_exception(content, _CONNECTION_CLOSED_EXCEPTION) + self._released = True + + async def wait_for_close(self) -> None: + if self.__writer is not None: + try: + await self.__writer + except asyncio.CancelledError: + if ( + sys.version_info >= (3, 11) + and (task := asyncio.current_task()) + and task.cancelling() + ): + raise + self.release() + + async def read(self) -> bytes: + """Read response payload.""" + if self._body is None: + try: + self._body = await self.content.read() + for trace in self._traces: + await trace.send_response_chunk_received( + self.method, self.url, self._body + ) + except BaseException: + self.close() + raise + elif self._released: # Response explicitly released + raise ClientConnectionError("Connection closed") + + protocol = self._connection and self._connection.protocol + if protocol is None or not protocol.upgraded: + await self._wait_released() # Underlying connection released + return self._body + + def get_encoding(self) -> str: + ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower() + mimetype = helpers.parse_mimetype(ctype) + + encoding = mimetype.parameters.get("charset") + if encoding: + with contextlib.suppress(LookupError, ValueError): + return codecs.lookup(encoding).name + + if mimetype.type == "application" and ( + mimetype.subtype == "json" or mimetype.subtype == "rdap" + ): + # RFC 7159 states that the default encoding is UTF-8. + # RFC 7483 defines application/rdap+json + return "utf-8" + + if self._body is None: + raise RuntimeError( + "Cannot compute fallback encoding of a not yet read body" + ) + + return self._resolve_charset(self, self._body) + + async def text(self, encoding: Optional[str] = None, errors: str = "strict") -> str: + """Read response payload and decode.""" + if self._body is None: + await self.read() + + if encoding is None: + encoding = self.get_encoding() + + return self._body.decode(encoding, errors=errors) # type: ignore[union-attr] + + async def json( + self, + *, + encoding: Optional[str] = None, + loads: JSONDecoder = DEFAULT_JSON_DECODER, + content_type: Optional[str] = "application/json", + ) -> Any: + """Read and decodes JSON response.""" + if self._body is None: + await self.read() + + if content_type: + ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower() + if not _is_expected_content_type(ctype, content_type): + raise ContentTypeError( + self.request_info, + self.history, + status=self.status, + message=( + "Attempt to decode JSON with unexpected mimetype: %s" % ctype + ), + headers=self.headers, + ) + + stripped = self._body.strip() # type: ignore[union-attr] + if not stripped: + return None + + if encoding is None: + encoding = self.get_encoding() + + return loads(stripped.decode(encoding)) + + async def __aenter__(self) -> "ClientResponse": + self._in_context = True + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + self._in_context = False + # similar to _RequestContextManager, we do not need to check + # for exceptions, response object can close connection + # if state is broken + self.release() + await self.wait_for_close() + + +class ClientRequest: + GET_METHODS = { + hdrs.METH_GET, + hdrs.METH_HEAD, + hdrs.METH_OPTIONS, + hdrs.METH_TRACE, + } + POST_METHODS = {hdrs.METH_PATCH, hdrs.METH_POST, hdrs.METH_PUT} + ALL_METHODS = GET_METHODS.union(POST_METHODS).union({hdrs.METH_DELETE}) + + DEFAULT_HEADERS = { + hdrs.ACCEPT: "*/*", + hdrs.ACCEPT_ENCODING: _gen_default_accept_encoding(), + } + + # Type of body depends on PAYLOAD_REGISTRY, which is dynamic. + _body: Union[None, payload.Payload] = None + auth = None + response = None + + __writer: Optional["asyncio.Task[None]"] = None # async task for streaming data + + # These class defaults help create_autospec() work correctly. + # If autospec is improved in future, maybe these can be removed. + url = URL() + method = "GET" + + _continue = None # waiter future for '100 Continue' response + + _skip_auto_headers: Optional["CIMultiDict[None]"] = None + + # N.B. + # Adding __del__ method with self._writer closing doesn't make sense + # because _writer is instance method, thus it keeps a reference to self. + # Until writer has finished finalizer will not be called. + + def __init__( + self, + method: str, + url: URL, + *, + params: Query = None, + headers: Optional[LooseHeaders] = None, + skip_auto_headers: Optional[Iterable[str]] = None, + data: Any = None, + cookies: Optional[LooseCookies] = None, + auth: Optional[BasicAuth] = None, + version: http.HttpVersion = http.HttpVersion11, + compress: Union[str, bool, None] = None, + chunked: Optional[bool] = None, + expect100: bool = False, + loop: Optional[asyncio.AbstractEventLoop] = None, + response_class: Optional[Type["ClientResponse"]] = None, + proxy: Optional[URL] = None, + proxy_auth: Optional[BasicAuth] = None, + timer: Optional[BaseTimerContext] = None, + session: Optional["ClientSession"] = None, + ssl: Union[SSLContext, bool, Fingerprint] = True, + proxy_headers: Optional[LooseHeaders] = None, + traces: Optional[List["Trace"]] = None, + trust_env: bool = False, + server_hostname: Optional[str] = None, + ): + if loop is None: + loop = asyncio.get_event_loop() + if match := _CONTAINS_CONTROL_CHAR_RE.search(method): + raise ValueError( + f"Method cannot contain non-token characters {method!r} " + f"(found at least {match.group()!r})" + ) + # URL forbids subclasses, so a simple type check is enough. + assert type(url) is URL, url + if proxy is not None: + assert type(proxy) is URL, proxy + # FIXME: session is None in tests only, need to fix tests + # assert session is not None + if TYPE_CHECKING: + assert session is not None + self._session = session + if params: + url = url.extend_query(params) + self.original_url = url + self.url = url.with_fragment(None) if url.raw_fragment else url + self.method = method.upper() + self.chunked = chunked + self.compress = compress + self.loop = loop + self.length = None + if response_class is None: + real_response_class = ClientResponse + else: + real_response_class = response_class + self.response_class: Type[ClientResponse] = real_response_class + self._timer = timer if timer is not None else TimerNoop() + self._ssl = ssl if ssl is not None else True + self.server_hostname = server_hostname + + if loop.get_debug(): + self._source_traceback = traceback.extract_stack(sys._getframe(1)) + + self.update_version(version) + self.update_host(url) + self.update_headers(headers) + self.update_auto_headers(skip_auto_headers) + self.update_cookies(cookies) + self.update_content_encoding(data) + self.update_auth(auth, trust_env) + self.update_proxy(proxy, proxy_auth, proxy_headers) + + self.update_body_from_data(data) + if data is not None or self.method not in self.GET_METHODS: + self.update_transfer_encoding() + self.update_expect_continue(expect100) + self._traces = [] if traces is None else traces + + def __reset_writer(self, _: object = None) -> None: + self.__writer = None + + def _get_content_length(self) -> Optional[int]: + """Extract and validate Content-Length header value. + + Returns parsed Content-Length value or None if not set. + Raises ValueError if header exists but cannot be parsed as an integer. + """ + if hdrs.CONTENT_LENGTH not in self.headers: + return None + + content_length_hdr = self.headers[hdrs.CONTENT_LENGTH] + try: + return int(content_length_hdr) + except ValueError: + raise ValueError( + f"Invalid Content-Length header: {content_length_hdr}" + ) from None + + @property + def skip_auto_headers(self) -> CIMultiDict[None]: + return self._skip_auto_headers or CIMultiDict() + + @property + def _writer(self) -> Optional["asyncio.Task[None]"]: + return self.__writer + + @_writer.setter + def _writer(self, writer: "asyncio.Task[None]") -> None: + if self.__writer is not None: + self.__writer.remove_done_callback(self.__reset_writer) + self.__writer = writer + writer.add_done_callback(self.__reset_writer) + + def is_ssl(self) -> bool: + return self.url.scheme in _SSL_SCHEMES + + @property + def ssl(self) -> Union["SSLContext", bool, Fingerprint]: + return self._ssl + + @property + def connection_key(self) -> ConnectionKey: + if proxy_headers := self.proxy_headers: + h: Optional[int] = hash(tuple(proxy_headers.items())) + else: + h = None + url = self.url + return tuple.__new__( + ConnectionKey, + ( + url.raw_host or "", + url.port, + url.scheme in _SSL_SCHEMES, + self._ssl, + self.proxy, + self.proxy_auth, + h, + ), + ) + + @property + def host(self) -> str: + ret = self.url.raw_host + assert ret is not None + return ret + + @property + def port(self) -> Optional[int]: + return self.url.port + + @property + def body(self) -> Union[payload.Payload, Literal[b""]]: + """Request body.""" + # empty body is represented as bytes for backwards compatibility + return self._body or b"" + + @body.setter + def body(self, value: Any) -> None: + """Set request body with warning for non-autoclose payloads. + + WARNING: This setter must be called from within an event loop and is not + thread-safe. Setting body outside of an event loop may raise RuntimeError + when closing file-based payloads. + + DEPRECATED: Direct assignment to body is deprecated and will be removed + in a future version. Use await update_body() instead for proper resource + management. + """ + # Close existing payload if present + if self._body is not None: + # Warn if the payload needs manual closing + # stacklevel=3: user code -> body setter -> _warn_if_unclosed_payload + _warn_if_unclosed_payload(self._body, stacklevel=3) + # NOTE: In the future, when we remove sync close support, + # this setter will need to be removed and only the async + # update_body() method will be available. For now, we call + # _close() for backwards compatibility. + self._body._close() + self._update_body(value) + + @property + def request_info(self) -> RequestInfo: + headers: CIMultiDictProxy[str] = CIMultiDictProxy(self.headers) + # These are created on every request, so we use a NamedTuple + # for performance reasons. We don't use the RequestInfo.__new__ + # method because it has a different signature which is provided + # for backwards compatibility only. + return tuple.__new__( + RequestInfo, (self.url, self.method, headers, self.original_url) + ) + + @property + def session(self) -> "ClientSession": + """Return the ClientSession instance. + + This property provides access to the ClientSession that initiated + this request, allowing middleware to make additional requests + using the same session. + """ + return self._session + + def update_host(self, url: URL) -> None: + """Update destination host, port and connection type (ssl).""" + # get host/port + if not url.raw_host: + raise InvalidURL(url) + + # basic auth info + if url.raw_user or url.raw_password: + self.auth = helpers.BasicAuth(url.user or "", url.password or "") + + def update_version(self, version: Union[http.HttpVersion, str]) -> None: + """Convert request version to two elements tuple. + + parser HTTP version '1.1' => (1, 1) + """ + if isinstance(version, str): + v = [part.strip() for part in version.split(".", 1)] + try: + version = http.HttpVersion(int(v[0]), int(v[1])) + except ValueError: + raise ValueError( + f"Can not parse http version number: {version}" + ) from None + self.version = version + + def update_headers(self, headers: Optional[LooseHeaders]) -> None: + """Update request headers.""" + self.headers: CIMultiDict[str] = CIMultiDict() + + # Build the host header + host = self.url.host_port_subcomponent + + # host_port_subcomponent is None when the URL is a relative URL. + # but we know we do not have a relative URL here. + assert host is not None + self.headers[hdrs.HOST] = host + + if not headers: + return + + if isinstance(headers, (dict, MultiDictProxy, MultiDict)): + headers = headers.items() + + for key, value in headers: # type: ignore[misc] + # A special case for Host header + if key in hdrs.HOST_ALL: + self.headers[key] = value + else: + self.headers.add(key, value) + + def update_auto_headers(self, skip_auto_headers: Optional[Iterable[str]]) -> None: + if skip_auto_headers is not None: + self._skip_auto_headers = CIMultiDict( + (hdr, None) for hdr in sorted(skip_auto_headers) + ) + used_headers = self.headers.copy() + used_headers.extend(self._skip_auto_headers) # type: ignore[arg-type] + else: + # Fast path when there are no headers to skip + # which is the most common case. + used_headers = self.headers + + for hdr, val in self.DEFAULT_HEADERS.items(): + if hdr not in used_headers: + self.headers[hdr] = val + + if hdrs.USER_AGENT not in used_headers: + self.headers[hdrs.USER_AGENT] = SERVER_SOFTWARE + + def update_cookies(self, cookies: Optional[LooseCookies]) -> None: + """Update request cookies header.""" + if not cookies: + return + + c = SimpleCookie() + if hdrs.COOKIE in self.headers: + # parse_cookie_header for RFC 6265 compliant Cookie header parsing + c.update(parse_cookie_header(self.headers.get(hdrs.COOKIE, ""))) + del self.headers[hdrs.COOKIE] + + if isinstance(cookies, Mapping): + iter_cookies = cookies.items() + else: + iter_cookies = cookies # type: ignore[assignment] + for name, value in iter_cookies: + if isinstance(value, Morsel): + # Use helper to preserve coded_value exactly as sent by server + c[name] = preserve_morsel_with_coded_value(value) + else: + c[name] = value # type: ignore[assignment] + + self.headers[hdrs.COOKIE] = c.output(header="", sep=";").strip() + + def update_content_encoding(self, data: Any) -> None: + """Set request content encoding.""" + if not data: + # Don't compress an empty body. + self.compress = None + return + + if self.headers.get(hdrs.CONTENT_ENCODING): + if self.compress: + raise ValueError( + "compress can not be set if Content-Encoding header is set" + ) + elif self.compress: + if not isinstance(self.compress, str): + self.compress = "deflate" + self.headers[hdrs.CONTENT_ENCODING] = self.compress + self.chunked = True # enable chunked, no need to deal with length + + def update_transfer_encoding(self) -> None: + """Analyze transfer-encoding header.""" + te = self.headers.get(hdrs.TRANSFER_ENCODING, "").lower() + + if "chunked" in te: + if self.chunked: + raise ValueError( + "chunked can not be set " + 'if "Transfer-Encoding: chunked" header is set' + ) + + elif self.chunked: + if hdrs.CONTENT_LENGTH in self.headers: + raise ValueError( + "chunked can not be set if Content-Length header is set" + ) + + self.headers[hdrs.TRANSFER_ENCODING] = "chunked" + + def update_auth(self, auth: Optional[BasicAuth], trust_env: bool = False) -> None: + """Set basic auth.""" + if auth is None: + auth = self.auth + if auth is None: + return + + if not isinstance(auth, helpers.BasicAuth): + raise TypeError("BasicAuth() tuple is required instead") + + self.headers[hdrs.AUTHORIZATION] = auth.encode() + + def update_body_from_data(self, body: Any, _stacklevel: int = 3) -> None: + """Update request body from data.""" + if self._body is not None: + _warn_if_unclosed_payload(self._body, stacklevel=_stacklevel) + + if body is None: + self._body = None + # Set Content-Length to 0 when body is None for methods that expect a body + if ( + self.method not in self.GET_METHODS + and not self.chunked + and hdrs.CONTENT_LENGTH not in self.headers + ): + self.headers[hdrs.CONTENT_LENGTH] = "0" + return + + # FormData + maybe_payload = body() if isinstance(body, FormData) else body + + try: + body_payload = payload.PAYLOAD_REGISTRY.get(maybe_payload, disposition=None) + except payload.LookupError: + body_payload = FormData(maybe_payload)() # type: ignore[arg-type] + + self._body = body_payload + # enable chunked encoding if needed + if not self.chunked and hdrs.CONTENT_LENGTH not in self.headers: + if (size := body_payload.size) is not None: + self.headers[hdrs.CONTENT_LENGTH] = str(size) + else: + self.chunked = True + + # copy payload headers + assert body_payload.headers + headers = self.headers + skip_headers = self._skip_auto_headers + for key, value in body_payload.headers.items(): + if key in headers or (skip_headers is not None and key in skip_headers): + continue + headers[key] = value + + def _update_body(self, body: Any) -> None: + """Update request body after its already been set.""" + # Remove existing Content-Length header since body is changing + if hdrs.CONTENT_LENGTH in self.headers: + del self.headers[hdrs.CONTENT_LENGTH] + + # Remove existing Transfer-Encoding header to avoid conflicts + if self.chunked and hdrs.TRANSFER_ENCODING in self.headers: + del self.headers[hdrs.TRANSFER_ENCODING] + + # Now update the body using the existing method + # Called from _update_body, add 1 to stacklevel from caller + self.update_body_from_data(body, _stacklevel=4) + + # Update transfer encoding headers if needed (same logic as __init__) + if body is not None or self.method not in self.GET_METHODS: + self.update_transfer_encoding() + + async def update_body(self, body: Any) -> None: + """ + Update request body and close previous payload if needed. + + This method safely updates the request body by first closing any existing + payload to prevent resource leaks, then setting the new body. + + IMPORTANT: Always use this method instead of setting request.body directly. + Direct assignment to request.body will leak resources if the previous body + contains file handles, streams, or other resources that need cleanup. + + Args: + body: The new body content. Can be: + - bytes/bytearray: Raw binary data + - str: Text data (will be encoded using charset from Content-Type) + - FormData: Form data that will be encoded as multipart/form-data + - Payload: A pre-configured payload object + - AsyncIterable: An async iterable of bytes chunks + - File-like object: Will be read and sent as binary data + - None: Clears the body + + Usage: + # CORRECT: Use update_body + await request.update_body(b"new request data") + + # WRONG: Don't set body directly + # request.body = b"new request data" # This will leak resources! + + # Update with form data + form_data = FormData() + form_data.add_field('field', 'value') + await request.update_body(form_data) + + # Clear body + await request.update_body(None) + + Note: + This method is async because it may need to close file handles or + other resources associated with the previous payload. Always await + this method to ensure proper cleanup. + + Warning: + Setting request.body directly is highly discouraged and can lead to: + - Resource leaks (unclosed file handles, streams) + - Memory leaks (unreleased buffers) + - Unexpected behavior with streaming payloads + + It is not recommended to change the payload type in middleware. If the + body was already set (e.g., as bytes), it's best to keep the same type + rather than converting it (e.g., to str) as this may result in unexpected + behavior. + + See Also: + - update_body_from_data: Synchronous body update without cleanup + - body property: Direct body access (STRONGLY DISCOURAGED) + + """ + # Close existing payload if it exists and needs closing + if self._body is not None: + await self._body.close() + self._update_body(body) + + def update_expect_continue(self, expect: bool = False) -> None: + if expect: + self.headers[hdrs.EXPECT] = "100-continue" + elif ( + hdrs.EXPECT in self.headers + and self.headers[hdrs.EXPECT].lower() == "100-continue" + ): + expect = True + + if expect: + self._continue = self.loop.create_future() + + def update_proxy( + self, + proxy: Optional[URL], + proxy_auth: Optional[BasicAuth], + proxy_headers: Optional[LooseHeaders], + ) -> None: + self.proxy = proxy + if proxy is None: + self.proxy_auth = None + self.proxy_headers = None + return + + if proxy_auth and not isinstance(proxy_auth, helpers.BasicAuth): + raise ValueError("proxy_auth must be None or BasicAuth() tuple") + self.proxy_auth = proxy_auth + + if proxy_headers is not None and not isinstance( + proxy_headers, (MultiDict, MultiDictProxy) + ): + proxy_headers = CIMultiDict(proxy_headers) + self.proxy_headers = proxy_headers + + async def write_bytes( + self, + writer: AbstractStreamWriter, + conn: "Connection", + content_length: Optional[int] = None, + ) -> None: + """ + Write the request body to the connection stream. + + This method handles writing different types of request bodies: + 1. Payload objects (using their specialized write_with_length method) + 2. Bytes/bytearray objects + 3. Iterable body content + + Args: + writer: The stream writer to write the body to + conn: The connection being used for this request + content_length: Optional maximum number of bytes to write from the body + (None means write the entire body) + + The method properly handles: + - Waiting for 100-Continue responses if required + - Content length constraints for chunked encoding + - Error handling for network issues, cancellation, and other exceptions + - Signaling EOF and timeout management + + Raises: + ClientOSError: When there's an OS-level error writing the body + ClientConnectionError: When there's a general connection error + asyncio.CancelledError: When the operation is cancelled + + """ + # 100 response + if self._continue is not None: + # Force headers to be sent before waiting for 100-continue + writer.send_headers() + await writer.drain() + await self._continue + + protocol = conn.protocol + assert protocol is not None + try: + # This should be a rare case but the + # self._body can be set to None while + # the task is being started or we wait above + # for the 100-continue response. + # The more likely case is we have an empty + # payload, but 100-continue is still expected. + if self._body is not None: + await self._body.write_with_length(writer, content_length) + except OSError as underlying_exc: + reraised_exc = underlying_exc + + # Distinguish between timeout and other OS errors for better error reporting + exc_is_not_timeout = underlying_exc.errno is not None or not isinstance( + underlying_exc, asyncio.TimeoutError + ) + if exc_is_not_timeout: + reraised_exc = ClientOSError( + underlying_exc.errno, + f"Can not write request body for {self.url !s}", + ) + + set_exception(protocol, reraised_exc, underlying_exc) + except asyncio.CancelledError: + # Body hasn't been fully sent, so connection can't be reused + conn.close() + raise + except Exception as underlying_exc: + set_exception( + protocol, + ClientConnectionError( + "Failed to send bytes into the underlying connection " + f"{conn !s}: {underlying_exc!r}", + ), + underlying_exc, + ) + else: + # Successfully wrote the body, signal EOF and start response timeout + await writer.write_eof() + protocol.start_timeout() + + async def send(self, conn: "Connection") -> "ClientResponse": + # Specify request target: + # - CONNECT request must send authority form URI + # - not CONNECT proxy must send absolute form URI + # - most common is origin form URI + if self.method == hdrs.METH_CONNECT: + connect_host = self.url.host_subcomponent + assert connect_host is not None + path = f"{connect_host}:{self.url.port}" + elif self.proxy and not self.is_ssl(): + path = str(self.url) + else: + path = self.url.raw_path_qs + + protocol = conn.protocol + assert protocol is not None + writer = StreamWriter( + protocol, + self.loop, + on_chunk_sent=( + functools.partial(self._on_chunk_request_sent, self.method, self.url) + if self._traces + else None + ), + on_headers_sent=( + functools.partial(self._on_headers_request_sent, self.method, self.url) + if self._traces + else None + ), + ) + + if self.compress: + writer.enable_compression(self.compress) # type: ignore[arg-type] + + if self.chunked is not None: + writer.enable_chunking() + + # set default content-type + if ( + self.method in self.POST_METHODS + and ( + self._skip_auto_headers is None + or hdrs.CONTENT_TYPE not in self._skip_auto_headers + ) + and hdrs.CONTENT_TYPE not in self.headers + ): + self.headers[hdrs.CONTENT_TYPE] = "application/octet-stream" + + v = self.version + if hdrs.CONNECTION not in self.headers: + if conn._connector.force_close: + if v == HttpVersion11: + self.headers[hdrs.CONNECTION] = "close" + elif v == HttpVersion10: + self.headers[hdrs.CONNECTION] = "keep-alive" + + # status + headers + status_line = f"{self.method} {path} HTTP/{v.major}.{v.minor}" + + # Buffer headers for potential coalescing with body + await writer.write_headers(status_line, self.headers) + + task: Optional["asyncio.Task[None]"] + if self._body or self._continue is not None or protocol.writing_paused: + coro = self.write_bytes(writer, conn, self._get_content_length()) + if sys.version_info >= (3, 12): + # Optimization for Python 3.12, try to write + # bytes immediately to avoid having to schedule + # the task on the event loop. + task = asyncio.Task(coro, loop=self.loop, eager_start=True) + else: + task = self.loop.create_task(coro) + if task.done(): + task = None + else: + self._writer = task + else: + # We have nothing to write because + # - there is no body + # - the protocol does not have writing paused + # - we are not waiting for a 100-continue response + protocol.start_timeout() + writer.set_eof() + task = None + response_class = self.response_class + assert response_class is not None + self.response = response_class( + self.method, + self.original_url, + writer=task, + continue100=self._continue, + timer=self._timer, + request_info=self.request_info, + traces=self._traces, + loop=self.loop, + session=self._session, + ) + return self.response + + async def close(self) -> None: + if self.__writer is not None: + try: + await self.__writer + except asyncio.CancelledError: + if ( + sys.version_info >= (3, 11) + and (task := asyncio.current_task()) + and task.cancelling() + ): + raise + + def terminate(self) -> None: + if self.__writer is not None: + if not self.loop.is_closed(): + self.__writer.cancel() + self.__writer.remove_done_callback(self.__reset_writer) + self.__writer = None + + async def _on_chunk_request_sent(self, method: str, url: URL, chunk: bytes) -> None: + for trace in self._traces: + await trace.send_request_chunk_sent(method, url, chunk) + + async def _on_headers_request_sent( + self, method: str, url: URL, headers: "CIMultiDict[str]" + ) -> None: + for trace in self._traces: + await trace.send_request_headers(method, url, headers) diff --git a/.venv/lib/python3.9/site-packages/aiohttp/client_ws.py b/.venv/lib/python3.9/site-packages/aiohttp/client_ws.py new file mode 100644 index 0000000..daa57d1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/client_ws.py @@ -0,0 +1,428 @@ +"""WebSocket client for asyncio.""" + +import asyncio +import sys +from types import TracebackType +from typing import Any, Optional, Type, cast + +import attr + +from ._websocket.reader import WebSocketDataQueue +from .client_exceptions import ClientError, ServerTimeoutError, WSMessageTypeError +from .client_reqrep import ClientResponse +from .helpers import calculate_timeout_when, set_result +from .http import ( + WS_CLOSED_MESSAGE, + WS_CLOSING_MESSAGE, + WebSocketError, + WSCloseCode, + WSMessage, + WSMsgType, +) +from .http_websocket import _INTERNAL_RECEIVE_TYPES, WebSocketWriter +from .streams import EofStream +from .typedefs import ( + DEFAULT_JSON_DECODER, + DEFAULT_JSON_ENCODER, + JSONDecoder, + JSONEncoder, +) + +if sys.version_info >= (3, 11): + import asyncio as async_timeout +else: + import async_timeout + + +@attr.s(frozen=True, slots=True) +class ClientWSTimeout: + ws_receive = attr.ib(type=Optional[float], default=None) + ws_close = attr.ib(type=Optional[float], default=None) + + +DEFAULT_WS_CLIENT_TIMEOUT = ClientWSTimeout(ws_receive=None, ws_close=10.0) + + +class ClientWebSocketResponse: + def __init__( + self, + reader: WebSocketDataQueue, + writer: WebSocketWriter, + protocol: Optional[str], + response: ClientResponse, + timeout: ClientWSTimeout, + autoclose: bool, + autoping: bool, + loop: asyncio.AbstractEventLoop, + *, + heartbeat: Optional[float] = None, + compress: int = 0, + client_notakeover: bool = False, + ) -> None: + self._response = response + self._conn = response.connection + + self._writer = writer + self._reader = reader + self._protocol = protocol + self._closed = False + self._closing = False + self._close_code: Optional[int] = None + self._timeout = timeout + self._autoclose = autoclose + self._autoping = autoping + self._heartbeat = heartbeat + self._heartbeat_cb: Optional[asyncio.TimerHandle] = None + self._heartbeat_when: float = 0.0 + if heartbeat is not None: + self._pong_heartbeat = heartbeat / 2.0 + self._pong_response_cb: Optional[asyncio.TimerHandle] = None + self._loop = loop + self._waiting: bool = False + self._close_wait: Optional[asyncio.Future[None]] = None + self._exception: Optional[BaseException] = None + self._compress = compress + self._client_notakeover = client_notakeover + self._ping_task: Optional[asyncio.Task[None]] = None + + self._reset_heartbeat() + + def _cancel_heartbeat(self) -> None: + self._cancel_pong_response_cb() + if self._heartbeat_cb is not None: + self._heartbeat_cb.cancel() + self._heartbeat_cb = None + if self._ping_task is not None: + self._ping_task.cancel() + self._ping_task = None + + def _cancel_pong_response_cb(self) -> None: + if self._pong_response_cb is not None: + self._pong_response_cb.cancel() + self._pong_response_cb = None + + def _reset_heartbeat(self) -> None: + if self._heartbeat is None: + return + self._cancel_pong_response_cb() + loop = self._loop + assert loop is not None + conn = self._conn + timeout_ceil_threshold = ( + conn._connector._timeout_ceil_threshold if conn is not None else 5 + ) + now = loop.time() + when = calculate_timeout_when(now, self._heartbeat, timeout_ceil_threshold) + self._heartbeat_when = when + if self._heartbeat_cb is None: + # We do not cancel the previous heartbeat_cb here because + # it generates a significant amount of TimerHandle churn + # which causes asyncio to rebuild the heap frequently. + # Instead _send_heartbeat() will reschedule the next + # heartbeat if it fires too early. + self._heartbeat_cb = loop.call_at(when, self._send_heartbeat) + + def _send_heartbeat(self) -> None: + self._heartbeat_cb = None + loop = self._loop + now = loop.time() + if now < self._heartbeat_when: + # Heartbeat fired too early, reschedule + self._heartbeat_cb = loop.call_at( + self._heartbeat_when, self._send_heartbeat + ) + return + + conn = self._conn + timeout_ceil_threshold = ( + conn._connector._timeout_ceil_threshold if conn is not None else 5 + ) + when = calculate_timeout_when(now, self._pong_heartbeat, timeout_ceil_threshold) + self._cancel_pong_response_cb() + self._pong_response_cb = loop.call_at(when, self._pong_not_received) + + coro = self._writer.send_frame(b"", WSMsgType.PING) + if sys.version_info >= (3, 12): + # Optimization for Python 3.12, try to send the ping + # immediately to avoid having to schedule + # the task on the event loop. + ping_task = asyncio.Task(coro, loop=loop, eager_start=True) + else: + ping_task = loop.create_task(coro) + + if not ping_task.done(): + self._ping_task = ping_task + ping_task.add_done_callback(self._ping_task_done) + else: + self._ping_task_done(ping_task) + + def _ping_task_done(self, task: "asyncio.Task[None]") -> None: + """Callback for when the ping task completes.""" + if not task.cancelled() and (exc := task.exception()): + self._handle_ping_pong_exception(exc) + self._ping_task = None + + def _pong_not_received(self) -> None: + self._handle_ping_pong_exception( + ServerTimeoutError(f"No PONG received after {self._pong_heartbeat} seconds") + ) + + def _handle_ping_pong_exception(self, exc: BaseException) -> None: + """Handle exceptions raised during ping/pong processing.""" + if self._closed: + return + self._set_closed() + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + self._exception = exc + self._response.close() + if self._waiting and not self._closing: + self._reader.feed_data(WSMessage(WSMsgType.ERROR, exc, None), 0) + + def _set_closed(self) -> None: + """Set the connection to closed. + + Cancel any heartbeat timers and set the closed flag. + """ + self._closed = True + self._cancel_heartbeat() + + def _set_closing(self) -> None: + """Set the connection to closing. + + Cancel any heartbeat timers and set the closing flag. + """ + self._closing = True + self._cancel_heartbeat() + + @property + def closed(self) -> bool: + return self._closed + + @property + def close_code(self) -> Optional[int]: + return self._close_code + + @property + def protocol(self) -> Optional[str]: + return self._protocol + + @property + def compress(self) -> int: + return self._compress + + @property + def client_notakeover(self) -> bool: + return self._client_notakeover + + def get_extra_info(self, name: str, default: Any = None) -> Any: + """extra info from connection transport""" + conn = self._response.connection + if conn is None: + return default + transport = conn.transport + if transport is None: + return default + return transport.get_extra_info(name, default) + + def exception(self) -> Optional[BaseException]: + return self._exception + + async def ping(self, message: bytes = b"") -> None: + await self._writer.send_frame(message, WSMsgType.PING) + + async def pong(self, message: bytes = b"") -> None: + await self._writer.send_frame(message, WSMsgType.PONG) + + async def send_frame( + self, message: bytes, opcode: WSMsgType, compress: Optional[int] = None + ) -> None: + """Send a frame over the websocket.""" + await self._writer.send_frame(message, opcode, compress) + + async def send_str(self, data: str, compress: Optional[int] = None) -> None: + if not isinstance(data, str): + raise TypeError("data argument must be str (%r)" % type(data)) + await self._writer.send_frame( + data.encode("utf-8"), WSMsgType.TEXT, compress=compress + ) + + async def send_bytes(self, data: bytes, compress: Optional[int] = None) -> None: + if not isinstance(data, (bytes, bytearray, memoryview)): + raise TypeError("data argument must be byte-ish (%r)" % type(data)) + await self._writer.send_frame(data, WSMsgType.BINARY, compress=compress) + + async def send_json( + self, + data: Any, + compress: Optional[int] = None, + *, + dumps: JSONEncoder = DEFAULT_JSON_ENCODER, + ) -> None: + await self.send_str(dumps(data), compress=compress) + + async def close(self, *, code: int = WSCloseCode.OK, message: bytes = b"") -> bool: + # we need to break `receive()` cycle first, + # `close()` may be called from different task + if self._waiting and not self._closing: + assert self._loop is not None + self._close_wait = self._loop.create_future() + self._set_closing() + self._reader.feed_data(WS_CLOSING_MESSAGE, 0) + await self._close_wait + + if self._closed: + return False + + self._set_closed() + try: + await self._writer.close(code, message) + except asyncio.CancelledError: + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + self._response.close() + raise + except Exception as exc: + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + self._exception = exc + self._response.close() + return True + + if self._close_code: + self._response.close() + return True + + while True: + try: + async with async_timeout.timeout(self._timeout.ws_close): + msg = await self._reader.read() + except asyncio.CancelledError: + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + self._response.close() + raise + except Exception as exc: + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + self._exception = exc + self._response.close() + return True + + if msg.type is WSMsgType.CLOSE: + self._close_code = msg.data + self._response.close() + return True + + async def receive(self, timeout: Optional[float] = None) -> WSMessage: + receive_timeout = timeout or self._timeout.ws_receive + + while True: + if self._waiting: + raise RuntimeError("Concurrent call to receive() is not allowed") + + if self._closed: + return WS_CLOSED_MESSAGE + elif self._closing: + await self.close() + return WS_CLOSED_MESSAGE + + try: + self._waiting = True + try: + if receive_timeout: + # Entering the context manager and creating + # Timeout() object can take almost 50% of the + # run time in this loop so we avoid it if + # there is no read timeout. + async with async_timeout.timeout(receive_timeout): + msg = await self._reader.read() + else: + msg = await self._reader.read() + self._reset_heartbeat() + finally: + self._waiting = False + if self._close_wait: + set_result(self._close_wait, None) + except (asyncio.CancelledError, asyncio.TimeoutError): + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + raise + except EofStream: + self._close_code = WSCloseCode.OK + await self.close() + return WSMessage(WSMsgType.CLOSED, None, None) + except ClientError: + # Likely ServerDisconnectedError when connection is lost + self._set_closed() + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + return WS_CLOSED_MESSAGE + except WebSocketError as exc: + self._close_code = exc.code + await self.close(code=exc.code) + return WSMessage(WSMsgType.ERROR, exc, None) + except Exception as exc: + self._exception = exc + self._set_closing() + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + await self.close() + return WSMessage(WSMsgType.ERROR, exc, None) + + if msg.type not in _INTERNAL_RECEIVE_TYPES: + # If its not a close/closing/ping/pong message + # we can return it immediately + return msg + + if msg.type is WSMsgType.CLOSE: + self._set_closing() + self._close_code = msg.data + if not self._closed and self._autoclose: + await self.close() + elif msg.type is WSMsgType.CLOSING: + self._set_closing() + elif msg.type is WSMsgType.PING and self._autoping: + await self.pong(msg.data) + continue + elif msg.type is WSMsgType.PONG and self._autoping: + continue + + return msg + + async def receive_str(self, *, timeout: Optional[float] = None) -> str: + msg = await self.receive(timeout) + if msg.type is not WSMsgType.TEXT: + raise WSMessageTypeError( + f"Received message {msg.type}:{msg.data!r} is not WSMsgType.TEXT" + ) + return cast(str, msg.data) + + async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes: + msg = await self.receive(timeout) + if msg.type is not WSMsgType.BINARY: + raise WSMessageTypeError( + f"Received message {msg.type}:{msg.data!r} is not WSMsgType.BINARY" + ) + return cast(bytes, msg.data) + + async def receive_json( + self, + *, + loads: JSONDecoder = DEFAULT_JSON_DECODER, + timeout: Optional[float] = None, + ) -> Any: + data = await self.receive_str(timeout=timeout) + return loads(data) + + def __aiter__(self) -> "ClientWebSocketResponse": + return self + + async def __anext__(self) -> WSMessage: + msg = await self.receive() + if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED): + raise StopAsyncIteration + return msg + + async def __aenter__(self) -> "ClientWebSocketResponse": + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + await self.close() diff --git a/.venv/lib/python3.9/site-packages/aiohttp/compression_utils.py b/.venv/lib/python3.9/site-packages/aiohttp/compression_utils.py new file mode 100644 index 0000000..e478d24 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/compression_utils.py @@ -0,0 +1,348 @@ +import asyncio +import sys +import zlib +from abc import ABC, abstractmethod +from concurrent.futures import Executor +from typing import Any, Final, Optional, Protocol, TypedDict, cast + +if sys.version_info >= (3, 12): + from collections.abc import Buffer +else: + from typing import Union + + Buffer = Union[bytes, bytearray, "memoryview[int]", "memoryview[bytes]"] + +try: + try: + import brotlicffi as brotli + except ImportError: + import brotli + + HAS_BROTLI = True +except ImportError: # pragma: no cover + HAS_BROTLI = False + +try: + if sys.version_info >= (3, 14): + from compression.zstd import ZstdDecompressor # noqa: I900 + else: # TODO(PY314): Remove mentions of backports.zstd across codebase + from backports.zstd import ZstdDecompressor + + HAS_ZSTD = True +except ImportError: + HAS_ZSTD = False + + +MAX_SYNC_CHUNK_SIZE = 4096 +DEFAULT_MAX_DECOMPRESS_SIZE = 2**25 # 32MiB + +# Unlimited decompression constants - different libraries use different conventions +ZLIB_MAX_LENGTH_UNLIMITED = 0 # zlib uses 0 to mean unlimited +ZSTD_MAX_LENGTH_UNLIMITED = -1 # zstd uses -1 to mean unlimited + + +class ZLibCompressObjProtocol(Protocol): + def compress(self, data: Buffer) -> bytes: ... + def flush(self, mode: int = ..., /) -> bytes: ... + + +class ZLibDecompressObjProtocol(Protocol): + def decompress(self, data: Buffer, max_length: int = ...) -> bytes: ... + def flush(self, length: int = ..., /) -> bytes: ... + + @property + def eof(self) -> bool: ... + + +class ZLibBackendProtocol(Protocol): + MAX_WBITS: int + Z_FULL_FLUSH: int + Z_SYNC_FLUSH: int + Z_BEST_SPEED: int + Z_FINISH: int + + def compressobj( + self, + level: int = ..., + method: int = ..., + wbits: int = ..., + memLevel: int = ..., + strategy: int = ..., + zdict: Optional[Buffer] = ..., + ) -> ZLibCompressObjProtocol: ... + def decompressobj( + self, wbits: int = ..., zdict: Buffer = ... + ) -> ZLibDecompressObjProtocol: ... + + def compress( + self, data: Buffer, /, level: int = ..., wbits: int = ... + ) -> bytes: ... + def decompress( + self, data: Buffer, /, wbits: int = ..., bufsize: int = ... + ) -> bytes: ... + + +class CompressObjArgs(TypedDict, total=False): + wbits: int + strategy: int + level: int + + +class ZLibBackendWrapper: + def __init__(self, _zlib_backend: ZLibBackendProtocol): + self._zlib_backend: ZLibBackendProtocol = _zlib_backend + + @property + def name(self) -> str: + return getattr(self._zlib_backend, "__name__", "undefined") + + @property + def MAX_WBITS(self) -> int: + return self._zlib_backend.MAX_WBITS + + @property + def Z_FULL_FLUSH(self) -> int: + return self._zlib_backend.Z_FULL_FLUSH + + @property + def Z_SYNC_FLUSH(self) -> int: + return self._zlib_backend.Z_SYNC_FLUSH + + @property + def Z_BEST_SPEED(self) -> int: + return self._zlib_backend.Z_BEST_SPEED + + @property + def Z_FINISH(self) -> int: + return self._zlib_backend.Z_FINISH + + def compressobj(self, *args: Any, **kwargs: Any) -> ZLibCompressObjProtocol: + return self._zlib_backend.compressobj(*args, **kwargs) + + def decompressobj(self, *args: Any, **kwargs: Any) -> ZLibDecompressObjProtocol: + return self._zlib_backend.decompressobj(*args, **kwargs) + + def compress(self, data: Buffer, *args: Any, **kwargs: Any) -> bytes: + return self._zlib_backend.compress(data, *args, **kwargs) + + def decompress(self, data: Buffer, *args: Any, **kwargs: Any) -> bytes: + return self._zlib_backend.decompress(data, *args, **kwargs) + + # Everything not explicitly listed in the Protocol we just pass through + def __getattr__(self, attrname: str) -> Any: + return getattr(self._zlib_backend, attrname) + + +ZLibBackend: ZLibBackendWrapper = ZLibBackendWrapper(zlib) + + +def set_zlib_backend(new_zlib_backend: ZLibBackendProtocol) -> None: + ZLibBackend._zlib_backend = new_zlib_backend + + +def encoding_to_mode( + encoding: Optional[str] = None, + suppress_deflate_header: bool = False, +) -> int: + if encoding == "gzip": + return 16 + ZLibBackend.MAX_WBITS + + return -ZLibBackend.MAX_WBITS if suppress_deflate_header else ZLibBackend.MAX_WBITS + + +class DecompressionBaseHandler(ABC): + def __init__( + self, + executor: Optional[Executor] = None, + max_sync_chunk_size: Optional[int] = MAX_SYNC_CHUNK_SIZE, + ): + """Base class for decompression handlers.""" + self._executor = executor + self._max_sync_chunk_size = max_sync_chunk_size + + @abstractmethod + def decompress_sync( + self, data: bytes, max_length: int = ZLIB_MAX_LENGTH_UNLIMITED + ) -> bytes: + """Decompress the given data.""" + + async def decompress( + self, data: bytes, max_length: int = ZLIB_MAX_LENGTH_UNLIMITED + ) -> bytes: + """Decompress the given data.""" + if ( + self._max_sync_chunk_size is not None + and len(data) > self._max_sync_chunk_size + ): + return await asyncio.get_event_loop().run_in_executor( + self._executor, self.decompress_sync, data, max_length + ) + return self.decompress_sync(data, max_length) + + +class ZLibCompressor: + def __init__( + self, + encoding: Optional[str] = None, + suppress_deflate_header: bool = False, + level: Optional[int] = None, + wbits: Optional[int] = None, + strategy: Optional[int] = None, + executor: Optional[Executor] = None, + max_sync_chunk_size: Optional[int] = MAX_SYNC_CHUNK_SIZE, + ): + self._executor = executor + self._max_sync_chunk_size = max_sync_chunk_size + self._mode = ( + encoding_to_mode(encoding, suppress_deflate_header) + if wbits is None + else wbits + ) + self._zlib_backend: Final = ZLibBackendWrapper(ZLibBackend._zlib_backend) + + kwargs: CompressObjArgs = {} + kwargs["wbits"] = self._mode + if strategy is not None: + kwargs["strategy"] = strategy + if level is not None: + kwargs["level"] = level + self._compressor = self._zlib_backend.compressobj(**kwargs) + + def compress_sync(self, data: bytes) -> bytes: + return self._compressor.compress(data) + + async def compress(self, data: bytes) -> bytes: + """Compress the data and returned the compressed bytes. + + Note that flush() must be called after the last call to compress() + + If the data size is large than the max_sync_chunk_size, the compression + will be done in the executor. Otherwise, the compression will be done + in the event loop. + + **WARNING: This method is NOT cancellation-safe when used with flush().** + If this operation is cancelled, the compressor state may be corrupted. + The connection MUST be closed after cancellation to avoid data corruption + in subsequent compress operations. + + For cancellation-safe compression (e.g., WebSocket), the caller MUST wrap + compress() + flush() + send operations in a shield and lock to ensure atomicity. + """ + # For large payloads, offload compression to executor to avoid blocking event loop + should_use_executor = ( + self._max_sync_chunk_size is not None + and len(data) > self._max_sync_chunk_size + ) + if should_use_executor: + return await asyncio.get_running_loop().run_in_executor( + self._executor, self._compressor.compress, data + ) + return self.compress_sync(data) + + def flush(self, mode: Optional[int] = None) -> bytes: + """Flush the compressor synchronously. + + **WARNING: This method is NOT cancellation-safe when called after compress().** + The flush() operation accesses shared compressor state. If compress() was + cancelled, calling flush() may result in corrupted data. The connection MUST + be closed after compress() cancellation. + + For cancellation-safe compression (e.g., WebSocket), the caller MUST wrap + compress() + flush() + send operations in a shield and lock to ensure atomicity. + """ + return self._compressor.flush( + mode if mode is not None else self._zlib_backend.Z_FINISH + ) + + +class ZLibDecompressor(DecompressionBaseHandler): + def __init__( + self, + encoding: Optional[str] = None, + suppress_deflate_header: bool = False, + executor: Optional[Executor] = None, + max_sync_chunk_size: Optional[int] = MAX_SYNC_CHUNK_SIZE, + ): + super().__init__(executor=executor, max_sync_chunk_size=max_sync_chunk_size) + self._mode = encoding_to_mode(encoding, suppress_deflate_header) + self._zlib_backend: Final = ZLibBackendWrapper(ZLibBackend._zlib_backend) + self._decompressor = self._zlib_backend.decompressobj(wbits=self._mode) + + def decompress_sync( + self, data: Buffer, max_length: int = ZLIB_MAX_LENGTH_UNLIMITED + ) -> bytes: + return self._decompressor.decompress(data, max_length) + + def flush(self, length: int = 0) -> bytes: + return ( + self._decompressor.flush(length) + if length > 0 + else self._decompressor.flush() + ) + + @property + def eof(self) -> bool: + return self._decompressor.eof + + +class BrotliDecompressor(DecompressionBaseHandler): + # Supports both 'brotlipy' and 'Brotli' packages + # since they share an import name. The top branches + # are for 'brotlipy' and bottom branches for 'Brotli' + def __init__( + self, + executor: Optional[Executor] = None, + max_sync_chunk_size: Optional[int] = MAX_SYNC_CHUNK_SIZE, + ) -> None: + """Decompress data using the Brotli library.""" + if not HAS_BROTLI: + raise RuntimeError( + "The brotli decompression is not available. " + "Please install `Brotli` module" + ) + self._obj = brotli.Decompressor() + super().__init__(executor=executor, max_sync_chunk_size=max_sync_chunk_size) + + def decompress_sync( + self, data: Buffer, max_length: int = ZLIB_MAX_LENGTH_UNLIMITED + ) -> bytes: + """Decompress the given data.""" + if hasattr(self._obj, "decompress"): + return cast(bytes, self._obj.decompress(data, max_length)) + return cast(bytes, self._obj.process(data, max_length)) + + def flush(self) -> bytes: + """Flush the decompressor.""" + if hasattr(self._obj, "flush"): + return cast(bytes, self._obj.flush()) + return b"" + + +class ZSTDDecompressor(DecompressionBaseHandler): + def __init__( + self, + executor: Optional[Executor] = None, + max_sync_chunk_size: Optional[int] = MAX_SYNC_CHUNK_SIZE, + ) -> None: + if not HAS_ZSTD: + raise RuntimeError( + "The zstd decompression is not available. " + "Please install `backports.zstd` module" + ) + self._obj = ZstdDecompressor() + super().__init__(executor=executor, max_sync_chunk_size=max_sync_chunk_size) + + def decompress_sync( + self, data: bytes, max_length: int = ZLIB_MAX_LENGTH_UNLIMITED + ) -> bytes: + # zstd uses -1 for unlimited, while zlib uses 0 for unlimited + # Convert the zlib convention (0=unlimited) to zstd convention (-1=unlimited) + zstd_max_length = ( + ZSTD_MAX_LENGTH_UNLIMITED + if max_length == ZLIB_MAX_LENGTH_UNLIMITED + else max_length + ) + return self._obj.decompress(data, zstd_max_length) + + def flush(self) -> bytes: + return b"" diff --git a/.venv/lib/python3.9/site-packages/aiohttp/connector.py b/.venv/lib/python3.9/site-packages/aiohttp/connector.py new file mode 100644 index 0000000..290a424 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/connector.py @@ -0,0 +1,1842 @@ +import asyncio +import functools +import random +import socket +import sys +import traceback +import warnings +from collections import OrderedDict, defaultdict, deque +from contextlib import suppress +from http import HTTPStatus +from itertools import chain, cycle, islice +from time import monotonic +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + DefaultDict, + Deque, + Dict, + Iterator, + List, + Literal, + Optional, + Sequence, + Set, + Tuple, + Type, + Union, + cast, +) + +import aiohappyeyeballs +from aiohappyeyeballs import AddrInfoType, SocketFactoryType + +from . import hdrs, helpers +from .abc import AbstractResolver, ResolveResult +from .client_exceptions import ( + ClientConnectionError, + ClientConnectorCertificateError, + ClientConnectorDNSError, + ClientConnectorError, + ClientConnectorSSLError, + ClientHttpProxyError, + ClientProxyConnectionError, + ServerFingerprintMismatch, + UnixClientConnectorError, + cert_errors, + ssl_errors, +) +from .client_proto import ResponseHandler +from .client_reqrep import ClientRequest, Fingerprint, _merge_ssl_params +from .helpers import ( + _SENTINEL, + ceil_timeout, + is_ip_address, + noop, + sentinel, + set_exception, + set_result, +) +from .log import client_logger +from .resolver import DefaultResolver + +if sys.version_info >= (3, 12): + from collections.abc import Buffer +else: + Buffer = Union[bytes, bytearray, "memoryview[int]", "memoryview[bytes]"] + +if TYPE_CHECKING: + import ssl + + SSLContext = ssl.SSLContext +else: + try: + import ssl + + SSLContext = ssl.SSLContext + except ImportError: # pragma: no cover + ssl = None # type: ignore[assignment] + SSLContext = object # type: ignore[misc,assignment] + +EMPTY_SCHEMA_SET = frozenset({""}) +HTTP_SCHEMA_SET = frozenset({"http", "https"}) +WS_SCHEMA_SET = frozenset({"ws", "wss"}) + +HTTP_AND_EMPTY_SCHEMA_SET = HTTP_SCHEMA_SET | EMPTY_SCHEMA_SET +HIGH_LEVEL_SCHEMA_SET = HTTP_AND_EMPTY_SCHEMA_SET | WS_SCHEMA_SET + +NEEDS_CLEANUP_CLOSED = (3, 13, 0) <= sys.version_info < ( + 3, + 13, + 1, +) or sys.version_info < (3, 12, 7) +# Cleanup closed is no longer needed after https://github.com/python/cpython/pull/118960 +# which first appeared in Python 3.12.7 and 3.13.1 + + +__all__ = ( + "BaseConnector", + "TCPConnector", + "UnixConnector", + "NamedPipeConnector", + "AddrInfoType", + "SocketFactoryType", +) + + +if TYPE_CHECKING: + from .client import ClientTimeout + from .client_reqrep import ConnectionKey + from .tracing import Trace + + +class _DeprecationWaiter: + __slots__ = ("_awaitable", "_awaited") + + def __init__(self, awaitable: Awaitable[Any]) -> None: + self._awaitable = awaitable + self._awaited = False + + def __await__(self) -> Any: + self._awaited = True + return self._awaitable.__await__() + + def __del__(self) -> None: + if not self._awaited: + warnings.warn( + "Connector.close() is a coroutine, " + "please use await connector.close()", + DeprecationWarning, + ) + + +async def _wait_for_close(waiters: List[Awaitable[object]]) -> None: + """Wait for all waiters to finish closing.""" + results = await asyncio.gather(*waiters, return_exceptions=True) + for res in results: + if isinstance(res, Exception): + client_logger.debug("Error while closing connector: %r", res) + + +class Connection: + + _source_traceback = None + + def __init__( + self, + connector: "BaseConnector", + key: "ConnectionKey", + protocol: ResponseHandler, + loop: asyncio.AbstractEventLoop, + ) -> None: + self._key = key + self._connector = connector + self._loop = loop + self._protocol: Optional[ResponseHandler] = protocol + self._callbacks: List[Callable[[], None]] = [] + + if loop.get_debug(): + self._source_traceback = traceback.extract_stack(sys._getframe(1)) + + def __repr__(self) -> str: + return f"Connection<{self._key}>" + + def __del__(self, _warnings: Any = warnings) -> None: + if self._protocol is not None: + kwargs = {"source": self} + _warnings.warn(f"Unclosed connection {self!r}", ResourceWarning, **kwargs) + if self._loop.is_closed(): + return + + self._connector._release(self._key, self._protocol, should_close=True) + + context = {"client_connection": self, "message": "Unclosed connection"} + if self._source_traceback is not None: + context["source_traceback"] = self._source_traceback + self._loop.call_exception_handler(context) + + def __bool__(self) -> Literal[True]: + """Force subclasses to not be falsy, to make checks simpler.""" + return True + + @property + def loop(self) -> asyncio.AbstractEventLoop: + warnings.warn( + "connector.loop property is deprecated", DeprecationWarning, stacklevel=2 + ) + return self._loop + + @property + def transport(self) -> Optional[asyncio.Transport]: + if self._protocol is None: + return None + return self._protocol.transport + + @property + def protocol(self) -> Optional[ResponseHandler]: + return self._protocol + + def add_callback(self, callback: Callable[[], None]) -> None: + if callback is not None: + self._callbacks.append(callback) + + def _notify_release(self) -> None: + callbacks, self._callbacks = self._callbacks[:], [] + + for cb in callbacks: + with suppress(Exception): + cb() + + def close(self) -> None: + self._notify_release() + + if self._protocol is not None: + self._connector._release(self._key, self._protocol, should_close=True) + self._protocol = None + + def release(self) -> None: + self._notify_release() + + if self._protocol is not None: + self._connector._release(self._key, self._protocol) + self._protocol = None + + @property + def closed(self) -> bool: + return self._protocol is None or not self._protocol.is_connected() + + +class _ConnectTunnelConnection(Connection): + """Special connection wrapper for CONNECT tunnels that must never be pooled. + + This connection wraps the proxy connection that will be upgraded with TLS. + It must never be released to the pool because: + 1. Its 'closed' future will never complete, causing session.close() to hang + 2. It represents an intermediate state, not a reusable connection + 3. The real connection (with TLS) will be created separately + """ + + def release(self) -> None: + """Do nothing - don't pool or close the connection. + + These connections are an intermediate state during the CONNECT tunnel + setup and will be cleaned up naturally after the TLS upgrade. If they + were to be pooled, they would never be properly closed, causing + session.close() to wait forever for their 'closed' future. + """ + + +class _TransportPlaceholder: + """placeholder for BaseConnector.connect function""" + + __slots__ = ("closed", "transport") + + def __init__(self, closed_future: asyncio.Future[Optional[Exception]]) -> None: + """Initialize a placeholder for a transport.""" + self.closed = closed_future + self.transport = None + + def close(self) -> None: + """Close the placeholder.""" + + def abort(self) -> None: + """Abort the placeholder (does nothing).""" + + +class BaseConnector: + """Base connector class. + + keepalive_timeout - (optional) Keep-alive timeout. + force_close - Set to True to force close and do reconnect + after each request (and between redirects). + limit - The total number of simultaneous connections. + limit_per_host - Number of simultaneous connections to one host. + enable_cleanup_closed - Enables clean-up closed ssl transports. + Disabled by default. + timeout_ceil_threshold - Trigger ceiling of timeout values when + it's above timeout_ceil_threshold. + loop - Optional event loop. + """ + + _closed = True # prevent AttributeError in __del__ if ctor was failed + _source_traceback = None + + # abort transport after 2 seconds (cleanup broken connections) + _cleanup_closed_period = 2.0 + + allowed_protocol_schema_set = HIGH_LEVEL_SCHEMA_SET + + def __init__( + self, + *, + keepalive_timeout: Union[object, None, float] = sentinel, + force_close: bool = False, + limit: int = 100, + limit_per_host: int = 0, + enable_cleanup_closed: bool = False, + loop: Optional[asyncio.AbstractEventLoop] = None, + timeout_ceil_threshold: float = 5, + ) -> None: + + if force_close: + if keepalive_timeout is not None and keepalive_timeout is not sentinel: + raise ValueError( + "keepalive_timeout cannot be set if force_close is True" + ) + else: + if keepalive_timeout is sentinel: + keepalive_timeout = 15.0 + + loop = loop or asyncio.get_running_loop() + self._timeout_ceil_threshold = timeout_ceil_threshold + + self._closed = False + if loop.get_debug(): + self._source_traceback = traceback.extract_stack(sys._getframe(1)) + + # Connection pool of reusable connections. + # We use a deque to store connections because it has O(1) popleft() + # and O(1) append() operations to implement a FIFO queue. + self._conns: DefaultDict[ + ConnectionKey, Deque[Tuple[ResponseHandler, float]] + ] = defaultdict(deque) + self._limit = limit + self._limit_per_host = limit_per_host + self._acquired: Set[ResponseHandler] = set() + self._acquired_per_host: DefaultDict[ConnectionKey, Set[ResponseHandler]] = ( + defaultdict(set) + ) + self._keepalive_timeout = cast(float, keepalive_timeout) + self._force_close = force_close + + # {host_key: FIFO list of waiters} + # The FIFO is implemented with an OrderedDict with None keys because + # python does not have an ordered set. + self._waiters: DefaultDict[ + ConnectionKey, OrderedDict[asyncio.Future[None], None] + ] = defaultdict(OrderedDict) + + self._loop = loop + self._factory = functools.partial(ResponseHandler, loop=loop) + + # start keep-alive connection cleanup task + self._cleanup_handle: Optional[asyncio.TimerHandle] = None + + # start cleanup closed transports task + self._cleanup_closed_handle: Optional[asyncio.TimerHandle] = None + + if enable_cleanup_closed and not NEEDS_CLEANUP_CLOSED: + warnings.warn( + "enable_cleanup_closed ignored because " + "https://github.com/python/cpython/pull/118960 is fixed " + f"in Python version {sys.version_info}", + DeprecationWarning, + stacklevel=2, + ) + enable_cleanup_closed = False + + self._cleanup_closed_disabled = not enable_cleanup_closed + self._cleanup_closed_transports: List[Optional[asyncio.Transport]] = [] + self._placeholder_future: asyncio.Future[Optional[Exception]] = ( + loop.create_future() + ) + self._placeholder_future.set_result(None) + self._cleanup_closed() + + def __del__(self, _warnings: Any = warnings) -> None: + if self._closed: + return + if not self._conns: + return + + conns = [repr(c) for c in self._conns.values()] + + self._close() + + kwargs = {"source": self} + _warnings.warn(f"Unclosed connector {self!r}", ResourceWarning, **kwargs) + context = { + "connector": self, + "connections": conns, + "message": "Unclosed connector", + } + if self._source_traceback is not None: + context["source_traceback"] = self._source_traceback + self._loop.call_exception_handler(context) + + def __enter__(self) -> "BaseConnector": + warnings.warn( + '"with Connector():" is deprecated, ' + 'use "async with Connector():" instead', + DeprecationWarning, + ) + return self + + def __exit__(self, *exc: Any) -> None: + self._close() + + async def __aenter__(self) -> "BaseConnector": + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]] = None, + exc_value: Optional[BaseException] = None, + exc_traceback: Optional[TracebackType] = None, + ) -> None: + await self.close() + + @property + def force_close(self) -> bool: + """Ultimately close connection on releasing if True.""" + return self._force_close + + @property + def limit(self) -> int: + """The total number for simultaneous connections. + + If limit is 0 the connector has no limit. + The default limit size is 100. + """ + return self._limit + + @property + def limit_per_host(self) -> int: + """The limit for simultaneous connections to the same endpoint. + + Endpoints are the same if they are have equal + (host, port, is_ssl) triple. + """ + return self._limit_per_host + + def _cleanup(self) -> None: + """Cleanup unused transports.""" + if self._cleanup_handle: + self._cleanup_handle.cancel() + # _cleanup_handle should be unset, otherwise _release() will not + # recreate it ever! + self._cleanup_handle = None + + now = monotonic() + timeout = self._keepalive_timeout + + if self._conns: + connections = defaultdict(deque) + deadline = now - timeout + for key, conns in self._conns.items(): + alive: Deque[Tuple[ResponseHandler, float]] = deque() + for proto, use_time in conns: + if proto.is_connected() and use_time - deadline >= 0: + alive.append((proto, use_time)) + continue + transport = proto.transport + proto.close() + if not self._cleanup_closed_disabled and key.is_ssl: + self._cleanup_closed_transports.append(transport) + + if alive: + connections[key] = alive + + self._conns = connections + + if self._conns: + self._cleanup_handle = helpers.weakref_handle( + self, + "_cleanup", + timeout, + self._loop, + timeout_ceil_threshold=self._timeout_ceil_threshold, + ) + + def _cleanup_closed(self) -> None: + """Double confirmation for transport close. + + Some broken ssl servers may leave socket open without proper close. + """ + if self._cleanup_closed_handle: + self._cleanup_closed_handle.cancel() + + for transport in self._cleanup_closed_transports: + if transport is not None: + transport.abort() + + self._cleanup_closed_transports = [] + + if not self._cleanup_closed_disabled: + self._cleanup_closed_handle = helpers.weakref_handle( + self, + "_cleanup_closed", + self._cleanup_closed_period, + self._loop, + timeout_ceil_threshold=self._timeout_ceil_threshold, + ) + + def close(self, *, abort_ssl: bool = False) -> Awaitable[None]: + """Close all opened transports. + + :param abort_ssl: If True, SSL connections will be aborted immediately + without performing the shutdown handshake. This provides + faster cleanup at the cost of less graceful disconnection. + """ + if not (waiters := self._close(abort_ssl=abort_ssl)): + # If there are no connections to close, we can return a noop + # awaitable to avoid scheduling a task on the event loop. + return _DeprecationWaiter(noop()) + coro = _wait_for_close(waiters) + if sys.version_info >= (3, 12): + # Optimization for Python 3.12, try to close connections + # immediately to avoid having to schedule the task on the event loop. + task = asyncio.Task(coro, loop=self._loop, eager_start=True) + else: + task = self._loop.create_task(coro) + return _DeprecationWaiter(task) + + def _close(self, *, abort_ssl: bool = False) -> List[Awaitable[object]]: + waiters: List[Awaitable[object]] = [] + + if self._closed: + return waiters + + self._closed = True + + try: + if self._loop.is_closed(): + return waiters + + # cancel cleanup task + if self._cleanup_handle: + self._cleanup_handle.cancel() + + # cancel cleanup close task + if self._cleanup_closed_handle: + self._cleanup_closed_handle.cancel() + + for data in self._conns.values(): + for proto, _ in data: + if ( + abort_ssl + and proto.transport + and proto.transport.get_extra_info("sslcontext") is not None + ): + proto.abort() + else: + proto.close() + if closed := proto.closed: + waiters.append(closed) + + for proto in self._acquired: + if ( + abort_ssl + and proto.transport + and proto.transport.get_extra_info("sslcontext") is not None + ): + proto.abort() + else: + proto.close() + if closed := proto.closed: + waiters.append(closed) + + for transport in self._cleanup_closed_transports: + if transport is not None: + transport.abort() + + return waiters + + finally: + self._conns.clear() + self._acquired.clear() + for keyed_waiters in self._waiters.values(): + for keyed_waiter in keyed_waiters: + keyed_waiter.cancel() + self._waiters.clear() + self._cleanup_handle = None + self._cleanup_closed_transports.clear() + self._cleanup_closed_handle = None + + @property + def closed(self) -> bool: + """Is connector closed. + + A readonly property. + """ + return self._closed + + def _available_connections(self, key: "ConnectionKey") -> int: + """ + Return number of available connections. + + The limit, limit_per_host and the connection key are taken into account. + + If it returns less than 1 means that there are no connections + available. + """ + # check total available connections + # If there are no limits, this will always return 1 + total_remain = 1 + + if self._limit and (total_remain := self._limit - len(self._acquired)) <= 0: + return total_remain + + # check limit per host + if host_remain := self._limit_per_host: + if acquired := self._acquired_per_host.get(key): + host_remain -= len(acquired) + if total_remain > host_remain: + return host_remain + + return total_remain + + def _update_proxy_auth_header_and_build_proxy_req( + self, req: ClientRequest + ) -> ClientRequest: + """Set Proxy-Authorization header for non-SSL proxy requests and builds the proxy request for SSL proxy requests.""" + url = req.proxy + assert url is not None + headers: Dict[str, str] = {} + if req.proxy_headers is not None: + headers = req.proxy_headers # type: ignore[assignment] + headers[hdrs.HOST] = req.headers[hdrs.HOST] + proxy_req = ClientRequest( + hdrs.METH_GET, + url, + headers=headers, + auth=req.proxy_auth, + loop=self._loop, + ssl=req.ssl, + ) + auth = proxy_req.headers.pop(hdrs.AUTHORIZATION, None) + if auth is not None: + if not req.is_ssl(): + req.headers[hdrs.PROXY_AUTHORIZATION] = auth + else: + proxy_req.headers[hdrs.PROXY_AUTHORIZATION] = auth + return proxy_req + + async def connect( + self, req: ClientRequest, traces: List["Trace"], timeout: "ClientTimeout" + ) -> Connection: + """Get from pool or create new connection.""" + key = req.connection_key + if (conn := await self._get(key, traces)) is not None: + # If we do not have to wait and we can get a connection from the pool + # we can avoid the timeout ceil logic and directly return the connection + if req.proxy: + self._update_proxy_auth_header_and_build_proxy_req(req) + return conn + + async with ceil_timeout(timeout.connect, timeout.ceil_threshold): + if self._available_connections(key) <= 0: + await self._wait_for_available_connection(key, traces) + if (conn := await self._get(key, traces)) is not None: + if req.proxy: + self._update_proxy_auth_header_and_build_proxy_req(req) + return conn + + placeholder = cast( + ResponseHandler, _TransportPlaceholder(self._placeholder_future) + ) + self._acquired.add(placeholder) + if self._limit_per_host: + self._acquired_per_host[key].add(placeholder) + + try: + # Traces are done inside the try block to ensure that the + # that the placeholder is still cleaned up if an exception + # is raised. + if traces: + for trace in traces: + await trace.send_connection_create_start() + proto = await self._create_connection(req, traces, timeout) + if traces: + for trace in traces: + await trace.send_connection_create_end() + except BaseException: + self._release_acquired(key, placeholder) + raise + else: + if self._closed: + proto.close() + raise ClientConnectionError("Connector is closed.") + + # The connection was successfully created, drop the placeholder + # and add the real connection to the acquired set. There should + # be no awaits after the proto is added to the acquired set + # to ensure that the connection is not left in the acquired set + # on cancellation. + self._acquired.remove(placeholder) + self._acquired.add(proto) + if self._limit_per_host: + acquired_per_host = self._acquired_per_host[key] + acquired_per_host.remove(placeholder) + acquired_per_host.add(proto) + return Connection(self, key, proto, self._loop) + + async def _wait_for_available_connection( + self, key: "ConnectionKey", traces: List["Trace"] + ) -> None: + """Wait for an available connection slot.""" + # We loop here because there is a race between + # the connection limit check and the connection + # being acquired. If the connection is acquired + # between the check and the await statement, we + # need to loop again to check if the connection + # slot is still available. + attempts = 0 + while True: + fut: asyncio.Future[None] = self._loop.create_future() + keyed_waiters = self._waiters[key] + keyed_waiters[fut] = None + if attempts: + # If we have waited before, we need to move the waiter + # to the front of the queue as otherwise we might get + # starved and hit the timeout. + keyed_waiters.move_to_end(fut, last=False) + + try: + # Traces happen in the try block to ensure that the + # the waiter is still cleaned up if an exception is raised. + if traces: + for trace in traces: + await trace.send_connection_queued_start() + await fut + if traces: + for trace in traces: + await trace.send_connection_queued_end() + finally: + # pop the waiter from the queue if its still + # there and not already removed by _release_waiter + keyed_waiters.pop(fut, None) + if not self._waiters.get(key, True): + del self._waiters[key] + + if self._available_connections(key) > 0: + break + attempts += 1 + + async def _get( + self, key: "ConnectionKey", traces: List["Trace"] + ) -> Optional[Connection]: + """Get next reusable connection for the key or None. + + The connection will be marked as acquired. + """ + if (conns := self._conns.get(key)) is None: + return None + + t1 = monotonic() + while conns: + proto, t0 = conns.popleft() + # We will we reuse the connection if its connected and + # the keepalive timeout has not been exceeded + if proto.is_connected() and t1 - t0 <= self._keepalive_timeout: + if not conns: + # The very last connection was reclaimed: drop the key + del self._conns[key] + self._acquired.add(proto) + if self._limit_per_host: + self._acquired_per_host[key].add(proto) + if traces: + for trace in traces: + try: + await trace.send_connection_reuseconn() + except BaseException: + self._release_acquired(key, proto) + raise + return Connection(self, key, proto, self._loop) + + # Connection cannot be reused, close it + transport = proto.transport + proto.close() + # only for SSL transports + if not self._cleanup_closed_disabled and key.is_ssl: + self._cleanup_closed_transports.append(transport) + + # No more connections: drop the key + del self._conns[key] + return None + + def _release_waiter(self) -> None: + """ + Iterates over all waiters until one to be released is found. + + The one to be released is not finished and + belongs to a host that has available connections. + """ + if not self._waiters: + return + + # Having the dict keys ordered this avoids to iterate + # at the same order at each call. + queues = list(self._waiters) + random.shuffle(queues) + + for key in queues: + if self._available_connections(key) < 1: + continue + + waiters = self._waiters[key] + while waiters: + waiter, _ = waiters.popitem(last=False) + if not waiter.done(): + waiter.set_result(None) + return + + def _release_acquired(self, key: "ConnectionKey", proto: ResponseHandler) -> None: + """Release acquired connection.""" + if self._closed: + # acquired connection is already released on connector closing + return + + self._acquired.discard(proto) + if self._limit_per_host and (conns := self._acquired_per_host.get(key)): + conns.discard(proto) + if not conns: + del self._acquired_per_host[key] + self._release_waiter() + + def _release( + self, + key: "ConnectionKey", + protocol: ResponseHandler, + *, + should_close: bool = False, + ) -> None: + if self._closed: + # acquired connection is already released on connector closing + return + + self._release_acquired(key, protocol) + + if self._force_close or should_close or protocol.should_close: + transport = protocol.transport + protocol.close() + + if key.is_ssl and not self._cleanup_closed_disabled: + self._cleanup_closed_transports.append(transport) + return + + self._conns[key].append((protocol, monotonic())) + + if self._cleanup_handle is None: + self._cleanup_handle = helpers.weakref_handle( + self, + "_cleanup", + self._keepalive_timeout, + self._loop, + timeout_ceil_threshold=self._timeout_ceil_threshold, + ) + + async def _create_connection( + self, req: ClientRequest, traces: List["Trace"], timeout: "ClientTimeout" + ) -> ResponseHandler: + raise NotImplementedError() + + +class _DNSCacheTable: + def __init__(self, ttl: Optional[float] = None) -> None: + self._addrs_rr: Dict[Tuple[str, int], Tuple[Iterator[ResolveResult], int]] = {} + self._timestamps: Dict[Tuple[str, int], float] = {} + self._ttl = ttl + + def __contains__(self, host: object) -> bool: + return host in self._addrs_rr + + def add(self, key: Tuple[str, int], addrs: List[ResolveResult]) -> None: + self._addrs_rr[key] = (cycle(addrs), len(addrs)) + + if self._ttl is not None: + self._timestamps[key] = monotonic() + + def remove(self, key: Tuple[str, int]) -> None: + self._addrs_rr.pop(key, None) + + if self._ttl is not None: + self._timestamps.pop(key, None) + + def clear(self) -> None: + self._addrs_rr.clear() + self._timestamps.clear() + + def next_addrs(self, key: Tuple[str, int]) -> List[ResolveResult]: + loop, length = self._addrs_rr[key] + addrs = list(islice(loop, length)) + # Consume one more element to shift internal state of `cycle` + next(loop) + return addrs + + def expired(self, key: Tuple[str, int]) -> bool: + if self._ttl is None: + return False + + return self._timestamps[key] + self._ttl < monotonic() + + +def _make_ssl_context(verified: bool) -> SSLContext: + """Create SSL context. + + This method is not async-friendly and should be called from a thread + because it will load certificates from disk and do other blocking I/O. + """ + if ssl is None: + # No ssl support + return None + if verified: + sslcontext = ssl.create_default_context() + else: + sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + sslcontext.options |= ssl.OP_NO_SSLv2 + sslcontext.options |= ssl.OP_NO_SSLv3 + sslcontext.check_hostname = False + sslcontext.verify_mode = ssl.CERT_NONE + sslcontext.options |= ssl.OP_NO_COMPRESSION + sslcontext.set_default_verify_paths() + sslcontext.set_alpn_protocols(("http/1.1",)) + return sslcontext + + +# The default SSLContext objects are created at import time +# since they do blocking I/O to load certificates from disk, +# and imports should always be done before the event loop starts +# or in a thread. +_SSL_CONTEXT_VERIFIED = _make_ssl_context(True) +_SSL_CONTEXT_UNVERIFIED = _make_ssl_context(False) + + +class TCPConnector(BaseConnector): + """TCP connector. + + verify_ssl - Set to True to check ssl certifications. + fingerprint - Pass the binary sha256 + digest of the expected certificate in DER format to verify + that the certificate the server presents matches. See also + https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning + resolver - Enable DNS lookups and use this + resolver + use_dns_cache - Use memory cache for DNS lookups. + ttl_dns_cache - Max seconds having cached a DNS entry, None forever. + family - socket address family + local_addr - local tuple of (host, port) to bind socket to + + keepalive_timeout - (optional) Keep-alive timeout. + force_close - Set to True to force close and do reconnect + after each request (and between redirects). + limit - The total number of simultaneous connections. + limit_per_host - Number of simultaneous connections to one host. + enable_cleanup_closed - Enables clean-up closed ssl transports. + Disabled by default. + happy_eyeballs_delay - This is the “Connection Attempt Delay” + as defined in RFC 8305. To disable + the happy eyeballs algorithm, set to None. + interleave - “First Address Family Count” as defined in RFC 8305 + loop - Optional event loop. + socket_factory - A SocketFactoryType function that, if supplied, + will be used to create sockets given an + AddrInfoType. + ssl_shutdown_timeout - DEPRECATED. Will be removed in aiohttp 4.0. + Grace period for SSL shutdown handshake on TLS + connections. Default is 0 seconds (immediate abort). + This parameter allowed for a clean SSL shutdown by + notifying the remote peer of connection closure, + while avoiding excessive delays during connector cleanup. + Note: Only takes effect on Python 3.11+. + """ + + allowed_protocol_schema_set = HIGH_LEVEL_SCHEMA_SET | frozenset({"tcp"}) + + def __init__( + self, + *, + verify_ssl: bool = True, + fingerprint: Optional[bytes] = None, + use_dns_cache: bool = True, + ttl_dns_cache: Optional[int] = 10, + family: socket.AddressFamily = socket.AddressFamily.AF_UNSPEC, + ssl_context: Optional[SSLContext] = None, + ssl: Union[bool, Fingerprint, SSLContext] = True, + local_addr: Optional[Tuple[str, int]] = None, + resolver: Optional[AbstractResolver] = None, + keepalive_timeout: Union[None, float, object] = sentinel, + force_close: bool = False, + limit: int = 100, + limit_per_host: int = 0, + enable_cleanup_closed: bool = False, + loop: Optional[asyncio.AbstractEventLoop] = None, + timeout_ceil_threshold: float = 5, + happy_eyeballs_delay: Optional[float] = 0.25, + interleave: Optional[int] = None, + socket_factory: Optional[SocketFactoryType] = None, + ssl_shutdown_timeout: Union[_SENTINEL, None, float] = sentinel, + ): + super().__init__( + keepalive_timeout=keepalive_timeout, + force_close=force_close, + limit=limit, + limit_per_host=limit_per_host, + enable_cleanup_closed=enable_cleanup_closed, + loop=loop, + timeout_ceil_threshold=timeout_ceil_threshold, + ) + + self._ssl = _merge_ssl_params(ssl, verify_ssl, ssl_context, fingerprint) + + self._resolver: AbstractResolver + if resolver is None: + self._resolver = DefaultResolver(loop=self._loop) + self._resolver_owner = True + else: + self._resolver = resolver + self._resolver_owner = False + + self._use_dns_cache = use_dns_cache + self._cached_hosts = _DNSCacheTable(ttl=ttl_dns_cache) + self._throttle_dns_futures: Dict[ + Tuple[str, int], Set["asyncio.Future[None]"] + ] = {} + self._family = family + self._local_addr_infos = aiohappyeyeballs.addr_to_addr_infos(local_addr) + self._happy_eyeballs_delay = happy_eyeballs_delay + self._interleave = interleave + self._resolve_host_tasks: Set["asyncio.Task[List[ResolveResult]]"] = set() + self._socket_factory = socket_factory + self._ssl_shutdown_timeout: Optional[float] + # Handle ssl_shutdown_timeout with warning for Python < 3.11 + if ssl_shutdown_timeout is sentinel: + self._ssl_shutdown_timeout = 0 + else: + # Deprecation warning for ssl_shutdown_timeout parameter + warnings.warn( + "The ssl_shutdown_timeout parameter is deprecated and will be removed in aiohttp 4.0", + DeprecationWarning, + stacklevel=2, + ) + if ( + sys.version_info < (3, 11) + and ssl_shutdown_timeout is not None + and ssl_shutdown_timeout != 0 + ): + warnings.warn( + f"ssl_shutdown_timeout={ssl_shutdown_timeout} is ignored on Python < 3.11; " + "only ssl_shutdown_timeout=0 is supported. The timeout will be ignored.", + RuntimeWarning, + stacklevel=2, + ) + self._ssl_shutdown_timeout = ssl_shutdown_timeout + + def _close(self, *, abort_ssl: bool = False) -> List[Awaitable[object]]: + """Close all ongoing DNS calls.""" + for fut in chain.from_iterable(self._throttle_dns_futures.values()): + fut.cancel() + + waiters = super()._close(abort_ssl=abort_ssl) + + for t in self._resolve_host_tasks: + t.cancel() + waiters.append(t) + + return waiters + + async def close(self, *, abort_ssl: bool = False) -> None: + """ + Close all opened transports. + + :param abort_ssl: If True, SSL connections will be aborted immediately + without performing the shutdown handshake. If False (default), + the behavior is determined by ssl_shutdown_timeout: + - If ssl_shutdown_timeout=0: connections are aborted + - If ssl_shutdown_timeout>0: graceful shutdown is performed + """ + if self._resolver_owner: + await self._resolver.close() + # Use abort_ssl param if explicitly set, otherwise use ssl_shutdown_timeout default + await super().close(abort_ssl=abort_ssl or self._ssl_shutdown_timeout == 0) + + @property + def family(self) -> int: + """Socket family like AF_INET.""" + return self._family + + @property + def use_dns_cache(self) -> bool: + """True if local DNS caching is enabled.""" + return self._use_dns_cache + + def clear_dns_cache( + self, host: Optional[str] = None, port: Optional[int] = None + ) -> None: + """Remove specified host/port or clear all dns local cache.""" + if host is not None and port is not None: + self._cached_hosts.remove((host, port)) + elif host is not None or port is not None: + raise ValueError("either both host and port or none of them are allowed") + else: + self._cached_hosts.clear() + + async def _resolve_host( + self, host: str, port: int, traces: Optional[Sequence["Trace"]] = None + ) -> List[ResolveResult]: + """Resolve host and return list of addresses.""" + if is_ip_address(host): + return [ + { + "hostname": host, + "host": host, + "port": port, + "family": self._family, + "proto": 0, + "flags": 0, + } + ] + + if not self._use_dns_cache: + + if traces: + for trace in traces: + await trace.send_dns_resolvehost_start(host) + + res = await self._resolver.resolve(host, port, family=self._family) + + if traces: + for trace in traces: + await trace.send_dns_resolvehost_end(host) + + return res + + key = (host, port) + if key in self._cached_hosts and not self._cached_hosts.expired(key): + # get result early, before any await (#4014) + result = self._cached_hosts.next_addrs(key) + + if traces: + for trace in traces: + await trace.send_dns_cache_hit(host) + return result + + futures: Set["asyncio.Future[None]"] + # + # If multiple connectors are resolving the same host, we wait + # for the first one to resolve and then use the result for all of them. + # We use a throttle to ensure that we only resolve the host once + # and then use the result for all the waiters. + # + if key in self._throttle_dns_futures: + # get futures early, before any await (#4014) + futures = self._throttle_dns_futures[key] + future: asyncio.Future[None] = self._loop.create_future() + futures.add(future) + if traces: + for trace in traces: + await trace.send_dns_cache_hit(host) + try: + await future + finally: + futures.discard(future) + return self._cached_hosts.next_addrs(key) + + # update dict early, before any await (#4014) + self._throttle_dns_futures[key] = futures = set() + # In this case we need to create a task to ensure that we can shield + # the task from cancellation as cancelling this lookup should not cancel + # the underlying lookup or else the cancel event will get broadcast to + # all the waiters across all connections. + # + coro = self._resolve_host_with_throttle(key, host, port, futures, traces) + loop = asyncio.get_running_loop() + if sys.version_info >= (3, 12): + # Optimization for Python 3.12, try to send immediately + resolved_host_task = asyncio.Task(coro, loop=loop, eager_start=True) + else: + resolved_host_task = loop.create_task(coro) + + if not resolved_host_task.done(): + self._resolve_host_tasks.add(resolved_host_task) + resolved_host_task.add_done_callback(self._resolve_host_tasks.discard) + + try: + return await asyncio.shield(resolved_host_task) + except asyncio.CancelledError: + + def drop_exception(fut: "asyncio.Future[List[ResolveResult]]") -> None: + with suppress(Exception, asyncio.CancelledError): + fut.result() + + resolved_host_task.add_done_callback(drop_exception) + raise + + async def _resolve_host_with_throttle( + self, + key: Tuple[str, int], + host: str, + port: int, + futures: Set["asyncio.Future[None]"], + traces: Optional[Sequence["Trace"]], + ) -> List[ResolveResult]: + """Resolve host and set result for all waiters. + + This method must be run in a task and shielded from cancellation + to avoid cancelling the underlying lookup. + """ + try: + if traces: + for trace in traces: + await trace.send_dns_cache_miss(host) + + for trace in traces: + await trace.send_dns_resolvehost_start(host) + + addrs = await self._resolver.resolve(host, port, family=self._family) + if traces: + for trace in traces: + await trace.send_dns_resolvehost_end(host) + + self._cached_hosts.add(key, addrs) + for fut in futures: + set_result(fut, None) + except BaseException as e: + # any DNS exception is set for the waiters to raise the same exception. + # This coro is always run in task that is shielded from cancellation so + # we should never be propagating cancellation here. + for fut in futures: + set_exception(fut, e) + raise + finally: + self._throttle_dns_futures.pop(key) + + return self._cached_hosts.next_addrs(key) + + async def _create_connection( + self, req: ClientRequest, traces: List["Trace"], timeout: "ClientTimeout" + ) -> ResponseHandler: + """Create connection. + + Has same keyword arguments as BaseEventLoop.create_connection. + """ + if req.proxy: + _, proto = await self._create_proxy_connection(req, traces, timeout) + else: + _, proto = await self._create_direct_connection(req, traces, timeout) + + return proto + + def _get_ssl_context(self, req: ClientRequest) -> Optional[SSLContext]: + """Logic to get the correct SSL context + + 0. if req.ssl is false, return None + + 1. if ssl_context is specified in req, use it + 2. if _ssl_context is specified in self, use it + 3. otherwise: + 1. if verify_ssl is not specified in req, use self.ssl_context + (will generate a default context according to self.verify_ssl) + 2. if verify_ssl is True in req, generate a default SSL context + 3. if verify_ssl is False in req, generate a SSL context that + won't verify + """ + if not req.is_ssl(): + return None + + if ssl is None: # pragma: no cover + raise RuntimeError("SSL is not supported.") + sslcontext = req.ssl + if isinstance(sslcontext, ssl.SSLContext): + return sslcontext + if sslcontext is not True: + # not verified or fingerprinted + return _SSL_CONTEXT_UNVERIFIED + sslcontext = self._ssl + if isinstance(sslcontext, ssl.SSLContext): + return sslcontext + if sslcontext is not True: + # not verified or fingerprinted + return _SSL_CONTEXT_UNVERIFIED + return _SSL_CONTEXT_VERIFIED + + def _get_fingerprint(self, req: ClientRequest) -> Optional["Fingerprint"]: + ret = req.ssl + if isinstance(ret, Fingerprint): + return ret + ret = self._ssl + if isinstance(ret, Fingerprint): + return ret + return None + + async def _wrap_create_connection( + self, + *args: Any, + addr_infos: List[AddrInfoType], + req: ClientRequest, + timeout: "ClientTimeout", + client_error: Type[Exception] = ClientConnectorError, + **kwargs: Any, + ) -> Tuple[asyncio.Transport, ResponseHandler]: + try: + async with ceil_timeout( + timeout.sock_connect, ceil_threshold=timeout.ceil_threshold + ): + sock = await aiohappyeyeballs.start_connection( + addr_infos=addr_infos, + local_addr_infos=self._local_addr_infos, + happy_eyeballs_delay=self._happy_eyeballs_delay, + interleave=self._interleave, + loop=self._loop, + socket_factory=self._socket_factory, + ) + # Add ssl_shutdown_timeout for Python 3.11+ when SSL is used + if ( + kwargs.get("ssl") + and self._ssl_shutdown_timeout + and sys.version_info >= (3, 11) + ): + kwargs["ssl_shutdown_timeout"] = self._ssl_shutdown_timeout + return await self._loop.create_connection(*args, **kwargs, sock=sock) + except cert_errors as exc: + raise ClientConnectorCertificateError(req.connection_key, exc) from exc + except ssl_errors as exc: + raise ClientConnectorSSLError(req.connection_key, exc) from exc + except OSError as exc: + if exc.errno is None and isinstance(exc, asyncio.TimeoutError): + raise + raise client_error(req.connection_key, exc) from exc + + async def _wrap_existing_connection( + self, + *args: Any, + req: ClientRequest, + timeout: "ClientTimeout", + client_error: Type[Exception] = ClientConnectorError, + **kwargs: Any, + ) -> Tuple[asyncio.Transport, ResponseHandler]: + try: + async with ceil_timeout( + timeout.sock_connect, ceil_threshold=timeout.ceil_threshold + ): + return await self._loop.create_connection(*args, **kwargs) + except cert_errors as exc: + raise ClientConnectorCertificateError(req.connection_key, exc) from exc + except ssl_errors as exc: + raise ClientConnectorSSLError(req.connection_key, exc) from exc + except OSError as exc: + if exc.errno is None and isinstance(exc, asyncio.TimeoutError): + raise + raise client_error(req.connection_key, exc) from exc + + def _fail_on_no_start_tls(self, req: "ClientRequest") -> None: + """Raise a :py:exc:`RuntimeError` on missing ``start_tls()``. + + It is necessary for TLS-in-TLS so that it is possible to + send HTTPS queries through HTTPS proxies. + + This doesn't affect regular HTTP requests, though. + """ + if not req.is_ssl(): + return + + proxy_url = req.proxy + assert proxy_url is not None + if proxy_url.scheme != "https": + return + + self._check_loop_for_start_tls() + + def _check_loop_for_start_tls(self) -> None: + try: + self._loop.start_tls + except AttributeError as attr_exc: + raise RuntimeError( + "An HTTPS request is being sent through an HTTPS proxy. " + "This needs support for TLS in TLS but it is not implemented " + "in your runtime for the stdlib asyncio.\n\n" + "Please upgrade to Python 3.11 or higher. For more details, " + "please see:\n" + "* https://bugs.python.org/issue37179\n" + "* https://github.com/python/cpython/pull/28073\n" + "* https://docs.aiohttp.org/en/stable/" + "client_advanced.html#proxy-support\n" + "* https://github.com/aio-libs/aiohttp/discussions/6044\n", + ) from attr_exc + + def _loop_supports_start_tls(self) -> bool: + try: + self._check_loop_for_start_tls() + except RuntimeError: + return False + else: + return True + + def _warn_about_tls_in_tls( + self, + underlying_transport: asyncio.Transport, + req: ClientRequest, + ) -> None: + """Issue a warning if the requested URL has HTTPS scheme.""" + if req.request_info.url.scheme != "https": + return + + # Check if uvloop is being used, which supports TLS in TLS, + # otherwise assume that asyncio's native transport is being used. + if type(underlying_transport).__module__.startswith("uvloop"): + return + + # Support in asyncio was added in Python 3.11 (bpo-44011) + asyncio_supports_tls_in_tls = sys.version_info >= (3, 11) or getattr( + underlying_transport, + "_start_tls_compatible", + False, + ) + + if asyncio_supports_tls_in_tls: + return + + warnings.warn( + "An HTTPS request is being sent through an HTTPS proxy. " + "This support for TLS in TLS is known to be disabled " + "in the stdlib asyncio (Python <3.11). This is why you'll probably see " + "an error in the log below.\n\n" + "It is possible to enable it via monkeypatching. " + "For more details, see:\n" + "* https://bugs.python.org/issue37179\n" + "* https://github.com/python/cpython/pull/28073\n\n" + "You can temporarily patch this as follows:\n" + "* https://docs.aiohttp.org/en/stable/client_advanced.html#proxy-support\n" + "* https://github.com/aio-libs/aiohttp/discussions/6044\n", + RuntimeWarning, + source=self, + # Why `4`? At least 3 of the calls in the stack originate + # from the methods in this class. + stacklevel=3, + ) + + async def _start_tls_connection( + self, + underlying_transport: asyncio.Transport, + req: ClientRequest, + timeout: "ClientTimeout", + client_error: Type[Exception] = ClientConnectorError, + ) -> Tuple[asyncio.BaseTransport, ResponseHandler]: + """Wrap the raw TCP transport with TLS.""" + tls_proto = self._factory() # Create a brand new proto for TLS + sslcontext = self._get_ssl_context(req) + if TYPE_CHECKING: + # _start_tls_connection is unreachable in the current code path + # if sslcontext is None. + assert sslcontext is not None + + try: + async with ceil_timeout( + timeout.sock_connect, ceil_threshold=timeout.ceil_threshold + ): + try: + # ssl_shutdown_timeout is only available in Python 3.11+ + if sys.version_info >= (3, 11) and self._ssl_shutdown_timeout: + tls_transport = await self._loop.start_tls( + underlying_transport, + tls_proto, + sslcontext, + server_hostname=req.server_hostname or req.host, + ssl_handshake_timeout=timeout.total, + ssl_shutdown_timeout=self._ssl_shutdown_timeout, + ) + else: + tls_transport = await self._loop.start_tls( + underlying_transport, + tls_proto, + sslcontext, + server_hostname=req.server_hostname or req.host, + ssl_handshake_timeout=timeout.total, + ) + except BaseException: + # We need to close the underlying transport since + # `start_tls()` probably failed before it had a + # chance to do this: + if self._ssl_shutdown_timeout == 0: + underlying_transport.abort() + else: + underlying_transport.close() + raise + if isinstance(tls_transport, asyncio.Transport): + fingerprint = self._get_fingerprint(req) + if fingerprint: + try: + fingerprint.check(tls_transport) + except ServerFingerprintMismatch: + tls_transport.close() + if not self._cleanup_closed_disabled: + self._cleanup_closed_transports.append(tls_transport) + raise + except cert_errors as exc: + raise ClientConnectorCertificateError(req.connection_key, exc) from exc + except ssl_errors as exc: + raise ClientConnectorSSLError(req.connection_key, exc) from exc + except OSError as exc: + if exc.errno is None and isinstance(exc, asyncio.TimeoutError): + raise + raise client_error(req.connection_key, exc) from exc + except TypeError as type_err: + # Example cause looks like this: + # TypeError: transport is not supported by start_tls() + + raise ClientConnectionError( + "Cannot initialize a TLS-in-TLS connection to host " + f"{req.host!s}:{req.port:d} through an underlying connection " + f"to an HTTPS proxy {req.proxy!s} ssl:{req.ssl or 'default'} " + f"[{type_err!s}]" + ) from type_err + else: + if tls_transport is None: + msg = "Failed to start TLS (possibly caused by closing transport)" + raise client_error(req.connection_key, OSError(msg)) + tls_proto.connection_made( + tls_transport + ) # Kick the state machine of the new TLS protocol + + return tls_transport, tls_proto + + def _convert_hosts_to_addr_infos( + self, hosts: List[ResolveResult] + ) -> List[AddrInfoType]: + """Converts the list of hosts to a list of addr_infos. + + The list of hosts is the result of a DNS lookup. The list of + addr_infos is the result of a call to `socket.getaddrinfo()`. + """ + addr_infos: List[AddrInfoType] = [] + for hinfo in hosts: + host = hinfo["host"] + is_ipv6 = ":" in host + family = socket.AF_INET6 if is_ipv6 else socket.AF_INET + if self._family and self._family != family: + continue + addr = (host, hinfo["port"], 0, 0) if is_ipv6 else (host, hinfo["port"]) + addr_infos.append( + (family, socket.SOCK_STREAM, socket.IPPROTO_TCP, "", addr) + ) + return addr_infos + + async def _create_direct_connection( + self, + req: ClientRequest, + traces: List["Trace"], + timeout: "ClientTimeout", + *, + client_error: Type[Exception] = ClientConnectorError, + ) -> Tuple[asyncio.Transport, ResponseHandler]: + sslcontext = self._get_ssl_context(req) + fingerprint = self._get_fingerprint(req) + + host = req.url.raw_host + assert host is not None + # Replace multiple trailing dots with a single one. + # A trailing dot is only present for fully-qualified domain names. + # See https://github.com/aio-libs/aiohttp/pull/7364. + if host.endswith(".."): + host = host.rstrip(".") + "." + port = req.port + assert port is not None + try: + # Cancelling this lookup should not cancel the underlying lookup + # or else the cancel event will get broadcast to all the waiters + # across all connections. + hosts = await self._resolve_host(host, port, traces=traces) + except OSError as exc: + if exc.errno is None and isinstance(exc, asyncio.TimeoutError): + raise + # in case of proxy it is not ClientProxyConnectionError + # it is problem of resolving proxy ip itself + raise ClientConnectorDNSError(req.connection_key, exc) from exc + + last_exc: Optional[Exception] = None + addr_infos = self._convert_hosts_to_addr_infos(hosts) + while addr_infos: + # Strip trailing dots, certificates contain FQDN without dots. + # See https://github.com/aio-libs/aiohttp/issues/3636 + server_hostname = ( + (req.server_hostname or host).rstrip(".") if sslcontext else None + ) + + try: + transp, proto = await self._wrap_create_connection( + self._factory, + timeout=timeout, + ssl=sslcontext, + addr_infos=addr_infos, + server_hostname=server_hostname, + req=req, + client_error=client_error, + ) + except (ClientConnectorError, asyncio.TimeoutError) as exc: + last_exc = exc + aiohappyeyeballs.pop_addr_infos_interleave(addr_infos, self._interleave) + continue + + if req.is_ssl() and fingerprint: + try: + fingerprint.check(transp) + except ServerFingerprintMismatch as exc: + transp.close() + if not self._cleanup_closed_disabled: + self._cleanup_closed_transports.append(transp) + last_exc = exc + # Remove the bad peer from the list of addr_infos + sock: socket.socket = transp.get_extra_info("socket") + bad_peer = sock.getpeername() + aiohappyeyeballs.remove_addr_infos(addr_infos, bad_peer) + continue + + return transp, proto + else: + assert last_exc is not None + raise last_exc + + async def _create_proxy_connection( + self, req: ClientRequest, traces: List["Trace"], timeout: "ClientTimeout" + ) -> Tuple[asyncio.BaseTransport, ResponseHandler]: + self._fail_on_no_start_tls(req) + runtime_has_start_tls = self._loop_supports_start_tls() + proxy_req = self._update_proxy_auth_header_and_build_proxy_req(req) + + # create connection to proxy server + transport, proto = await self._create_direct_connection( + proxy_req, [], timeout, client_error=ClientProxyConnectionError + ) + + if req.is_ssl(): + if runtime_has_start_tls: + self._warn_about_tls_in_tls(transport, req) + + # For HTTPS requests over HTTP proxy + # we must notify proxy to tunnel connection + # so we send CONNECT command: + # CONNECT www.python.org:443 HTTP/1.1 + # Host: www.python.org + # + # next we must do TLS handshake and so on + # to do this we must wrap raw socket into secure one + # asyncio handles this perfectly + proxy_req.method = hdrs.METH_CONNECT + proxy_req.url = req.url + key = req.connection_key._replace( + proxy=None, proxy_auth=None, proxy_headers_hash=None + ) + conn = _ConnectTunnelConnection(self, key, proto, self._loop) + proxy_resp = await proxy_req.send(conn) + try: + protocol = conn._protocol + assert protocol is not None + + # read_until_eof=True will ensure the connection isn't closed + # once the response is received and processed allowing + # START_TLS to work on the connection below. + protocol.set_response_params( + read_until_eof=runtime_has_start_tls, + timeout_ceil_threshold=self._timeout_ceil_threshold, + ) + resp = await proxy_resp.start(conn) + except BaseException: + proxy_resp.close() + conn.close() + raise + else: + conn._protocol = None + try: + if resp.status != 200: + message = resp.reason + if message is None: + message = HTTPStatus(resp.status).phrase + raise ClientHttpProxyError( + proxy_resp.request_info, + resp.history, + status=resp.status, + message=message, + headers=resp.headers, + ) + if not runtime_has_start_tls: + rawsock = transport.get_extra_info("socket", default=None) + if rawsock is None: + raise RuntimeError( + "Transport does not expose socket instance" + ) + # Duplicate the socket, so now we can close proxy transport + rawsock = rawsock.dup() + except BaseException: + # It shouldn't be closed in `finally` because it's fed to + # `loop.start_tls()` and the docs say not to touch it after + # passing there. + transport.close() + raise + finally: + if not runtime_has_start_tls: + transport.close() + + if not runtime_has_start_tls: + # HTTP proxy with support for upgrade to HTTPS + sslcontext = self._get_ssl_context(req) + return await self._wrap_existing_connection( + self._factory, + timeout=timeout, + ssl=sslcontext, + sock=rawsock, + server_hostname=req.host, + req=req, + ) + + return await self._start_tls_connection( + # Access the old transport for the last time before it's + # closed and forgotten forever: + transport, + req=req, + timeout=timeout, + ) + finally: + proxy_resp.close() + + return transport, proto + + +class UnixConnector(BaseConnector): + """Unix socket connector. + + path - Unix socket path. + keepalive_timeout - (optional) Keep-alive timeout. + force_close - Set to True to force close and do reconnect + after each request (and between redirects). + limit - The total number of simultaneous connections. + limit_per_host - Number of simultaneous connections to one host. + loop - Optional event loop. + """ + + allowed_protocol_schema_set = HIGH_LEVEL_SCHEMA_SET | frozenset({"unix"}) + + def __init__( + self, + path: str, + force_close: bool = False, + keepalive_timeout: Union[object, float, None] = sentinel, + limit: int = 100, + limit_per_host: int = 0, + loop: Optional[asyncio.AbstractEventLoop] = None, + ) -> None: + super().__init__( + force_close=force_close, + keepalive_timeout=keepalive_timeout, + limit=limit, + limit_per_host=limit_per_host, + loop=loop, + ) + self._path = path + + @property + def path(self) -> str: + """Path to unix socket.""" + return self._path + + async def _create_connection( + self, req: ClientRequest, traces: List["Trace"], timeout: "ClientTimeout" + ) -> ResponseHandler: + try: + async with ceil_timeout( + timeout.sock_connect, ceil_threshold=timeout.ceil_threshold + ): + _, proto = await self._loop.create_unix_connection( + self._factory, self._path + ) + except OSError as exc: + if exc.errno is None and isinstance(exc, asyncio.TimeoutError): + raise + raise UnixClientConnectorError(self.path, req.connection_key, exc) from exc + + return proto + + +class NamedPipeConnector(BaseConnector): + """Named pipe connector. + + Only supported by the proactor event loop. + See also: https://docs.python.org/3/library/asyncio-eventloop.html + + path - Windows named pipe path. + keepalive_timeout - (optional) Keep-alive timeout. + force_close - Set to True to force close and do reconnect + after each request (and between redirects). + limit - The total number of simultaneous connections. + limit_per_host - Number of simultaneous connections to one host. + loop - Optional event loop. + """ + + allowed_protocol_schema_set = HIGH_LEVEL_SCHEMA_SET | frozenset({"npipe"}) + + def __init__( + self, + path: str, + force_close: bool = False, + keepalive_timeout: Union[object, float, None] = sentinel, + limit: int = 100, + limit_per_host: int = 0, + loop: Optional[asyncio.AbstractEventLoop] = None, + ) -> None: + super().__init__( + force_close=force_close, + keepalive_timeout=keepalive_timeout, + limit=limit, + limit_per_host=limit_per_host, + loop=loop, + ) + if not isinstance( + self._loop, + asyncio.ProactorEventLoop, # type: ignore[attr-defined] + ): + raise RuntimeError( + "Named Pipes only available in proactor loop under windows" + ) + self._path = path + + @property + def path(self) -> str: + """Path to the named pipe.""" + return self._path + + async def _create_connection( + self, req: ClientRequest, traces: List["Trace"], timeout: "ClientTimeout" + ) -> ResponseHandler: + try: + async with ceil_timeout( + timeout.sock_connect, ceil_threshold=timeout.ceil_threshold + ): + _, proto = await self._loop.create_pipe_connection( # type: ignore[attr-defined] + self._factory, self._path + ) + # the drain is required so that the connection_made is called + # and transport is set otherwise it is not set before the + # `assert conn.transport is not None` + # in client.py's _request method + await asyncio.sleep(0) + # other option is to manually set transport like + # `proto.transport = trans` + except OSError as exc: + if exc.errno is None and isinstance(exc, asyncio.TimeoutError): + raise + raise ClientConnectorError(req.connection_key, exc) from exc + + return cast(ResponseHandler, proto) diff --git a/.venv/lib/python3.9/site-packages/aiohttp/cookiejar.py b/.venv/lib/python3.9/site-packages/aiohttp/cookiejar.py new file mode 100644 index 0000000..193648d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/cookiejar.py @@ -0,0 +1,522 @@ +import asyncio +import calendar +import contextlib +import datetime +import heapq +import itertools +import os # noqa +import pathlib +import pickle +import re +import time +import warnings +from collections import defaultdict +from collections.abc import Mapping +from http.cookies import BaseCookie, Morsel, SimpleCookie +from typing import ( + DefaultDict, + Dict, + Iterable, + Iterator, + List, + Optional, + Set, + Tuple, + Union, +) + +from yarl import URL + +from ._cookie_helpers import preserve_morsel_with_coded_value +from .abc import AbstractCookieJar, ClearCookiePredicate +from .helpers import is_ip_address +from .typedefs import LooseCookies, PathLike, StrOrURL + +__all__ = ("CookieJar", "DummyCookieJar") + + +CookieItem = Union[str, "Morsel[str]"] + +# We cache these string methods here as their use is in performance critical code. +_FORMAT_PATH = "{}/{}".format +_FORMAT_DOMAIN_REVERSED = "{1}.{0}".format + +# The minimum number of scheduled cookie expirations before we start cleaning up +# the expiration heap. This is a performance optimization to avoid cleaning up the +# heap too often when there are only a few scheduled expirations. +_MIN_SCHEDULED_COOKIE_EXPIRATION = 100 +_SIMPLE_COOKIE = SimpleCookie() + + +class CookieJar(AbstractCookieJar): + """Implements cookie storage adhering to RFC 6265.""" + + DATE_TOKENS_RE = re.compile( + r"[\x09\x20-\x2F\x3B-\x40\x5B-\x60\x7B-\x7E]*" + r"(?P[\x00-\x08\x0A-\x1F\d:a-zA-Z\x7F-\xFF]+)" + ) + + DATE_HMS_TIME_RE = re.compile(r"(\d{1,2}):(\d{1,2}):(\d{1,2})") + + DATE_DAY_OF_MONTH_RE = re.compile(r"(\d{1,2})") + + DATE_MONTH_RE = re.compile( + "(jan)|(feb)|(mar)|(apr)|(may)|(jun)|(jul)|(aug)|(sep)|(oct)|(nov)|(dec)", + re.I, + ) + + DATE_YEAR_RE = re.compile(r"(\d{2,4})") + + # calendar.timegm() fails for timestamps after datetime.datetime.max + # Minus one as a loss of precision occurs when timestamp() is called. + MAX_TIME = ( + int(datetime.datetime.max.replace(tzinfo=datetime.timezone.utc).timestamp()) - 1 + ) + try: + calendar.timegm(time.gmtime(MAX_TIME)) + except (OSError, ValueError): + # Hit the maximum representable time on Windows + # https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/localtime-localtime32-localtime64 + # Throws ValueError on PyPy 3.9, OSError elsewhere + MAX_TIME = calendar.timegm((3000, 12, 31, 23, 59, 59, -1, -1, -1)) + except OverflowError: + # #4515: datetime.max may not be representable on 32-bit platforms + MAX_TIME = 2**31 - 1 + # Avoid minuses in the future, 3x faster + SUB_MAX_TIME = MAX_TIME - 1 + + def __init__( + self, + *, + unsafe: bool = False, + quote_cookie: bool = True, + treat_as_secure_origin: Union[StrOrURL, List[StrOrURL], None] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + ) -> None: + super().__init__(loop=loop) + self._cookies: DefaultDict[Tuple[str, str], SimpleCookie] = defaultdict( + SimpleCookie + ) + self._morsel_cache: DefaultDict[Tuple[str, str], Dict[str, Morsel[str]]] = ( + defaultdict(dict) + ) + self._host_only_cookies: Set[Tuple[str, str]] = set() + self._unsafe = unsafe + self._quote_cookie = quote_cookie + if treat_as_secure_origin is None: + treat_as_secure_origin = [] + elif isinstance(treat_as_secure_origin, URL): + treat_as_secure_origin = [treat_as_secure_origin.origin()] + elif isinstance(treat_as_secure_origin, str): + treat_as_secure_origin = [URL(treat_as_secure_origin).origin()] + else: + treat_as_secure_origin = [ + URL(url).origin() if isinstance(url, str) else url.origin() + for url in treat_as_secure_origin + ] + self._treat_as_secure_origin = treat_as_secure_origin + self._expire_heap: List[Tuple[float, Tuple[str, str, str]]] = [] + self._expirations: Dict[Tuple[str, str, str], float] = {} + + @property + def quote_cookie(self) -> bool: + return self._quote_cookie + + def save(self, file_path: PathLike) -> None: + file_path = pathlib.Path(file_path) + with file_path.open(mode="wb") as f: + pickle.dump(self._cookies, f, pickle.HIGHEST_PROTOCOL) + + def load(self, file_path: PathLike) -> None: + file_path = pathlib.Path(file_path) + with file_path.open(mode="rb") as f: + self._cookies = pickle.load(f) + + def clear(self, predicate: Optional[ClearCookiePredicate] = None) -> None: + if predicate is None: + self._expire_heap.clear() + self._cookies.clear() + self._morsel_cache.clear() + self._host_only_cookies.clear() + self._expirations.clear() + return + + now = time.time() + to_del = [ + key + for (domain, path), cookie in self._cookies.items() + for name, morsel in cookie.items() + if ( + (key := (domain, path, name)) in self._expirations + and self._expirations[key] <= now + ) + or predicate(morsel) + ] + if to_del: + self._delete_cookies(to_del) + + def clear_domain(self, domain: str) -> None: + self.clear(lambda x: self._is_domain_match(domain, x["domain"])) + + def __iter__(self) -> "Iterator[Morsel[str]]": + self._do_expiration() + for val in self._cookies.values(): + yield from val.values() + + def __len__(self) -> int: + """Return number of cookies. + + This function does not iterate self to avoid unnecessary expiration + checks. + """ + return sum(len(cookie.values()) for cookie in self._cookies.values()) + + def _do_expiration(self) -> None: + """Remove expired cookies.""" + if not (expire_heap_len := len(self._expire_heap)): + return + + # If the expiration heap grows larger than the number expirations + # times two, we clean it up to avoid keeping expired entries in + # the heap and consuming memory. We guard this with a minimum + # threshold to avoid cleaning up the heap too often when there are + # only a few scheduled expirations. + if ( + expire_heap_len > _MIN_SCHEDULED_COOKIE_EXPIRATION + and expire_heap_len > len(self._expirations) * 2 + ): + # Remove any expired entries from the expiration heap + # that do not match the expiration time in the expirations + # as it means the cookie has been re-added to the heap + # with a different expiration time. + self._expire_heap = [ + entry + for entry in self._expire_heap + if self._expirations.get(entry[1]) == entry[0] + ] + heapq.heapify(self._expire_heap) + + now = time.time() + to_del: List[Tuple[str, str, str]] = [] + # Find any expired cookies and add them to the to-delete list + while self._expire_heap: + when, cookie_key = self._expire_heap[0] + if when > now: + break + heapq.heappop(self._expire_heap) + # Check if the cookie hasn't been re-added to the heap + # with a different expiration time as it will be removed + # later when it reaches the top of the heap and its + # expiration time is met. + if self._expirations.get(cookie_key) == when: + to_del.append(cookie_key) + + if to_del: + self._delete_cookies(to_del) + + def _delete_cookies(self, to_del: List[Tuple[str, str, str]]) -> None: + for domain, path, name in to_del: + self._host_only_cookies.discard((domain, name)) + self._cookies[(domain, path)].pop(name, None) + self._morsel_cache[(domain, path)].pop(name, None) + self._expirations.pop((domain, path, name), None) + + def _expire_cookie(self, when: float, domain: str, path: str, name: str) -> None: + cookie_key = (domain, path, name) + if self._expirations.get(cookie_key) == when: + # Avoid adding duplicates to the heap + return + heapq.heappush(self._expire_heap, (when, cookie_key)) + self._expirations[cookie_key] = when + + def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> None: + """Update cookies.""" + hostname = response_url.raw_host + + if not self._unsafe and is_ip_address(hostname): + # Don't accept cookies from IPs + return + + if isinstance(cookies, Mapping): + cookies = cookies.items() + + for name, cookie in cookies: + if not isinstance(cookie, Morsel): + tmp = SimpleCookie() + tmp[name] = cookie # type: ignore[assignment] + cookie = tmp[name] + + domain = cookie["domain"] + + # ignore domains with trailing dots + if domain and domain[-1] == ".": + domain = "" + del cookie["domain"] + + if not domain and hostname is not None: + # Set the cookie's domain to the response hostname + # and set its host-only-flag + self._host_only_cookies.add((hostname, name)) + domain = cookie["domain"] = hostname + + if domain and domain[0] == ".": + # Remove leading dot + domain = domain[1:] + cookie["domain"] = domain + + if hostname and not self._is_domain_match(domain, hostname): + # Setting cookies for different domains is not allowed + continue + + path = cookie["path"] + if not path or path[0] != "/": + # Set the cookie's path to the response path + path = response_url.path + if not path.startswith("/"): + path = "/" + else: + # Cut everything from the last slash to the end + path = "/" + path[1 : path.rfind("/")] + cookie["path"] = path + path = path.rstrip("/") + + if max_age := cookie["max-age"]: + try: + delta_seconds = int(max_age) + max_age_expiration = min(time.time() + delta_seconds, self.MAX_TIME) + self._expire_cookie(max_age_expiration, domain, path, name) + except ValueError: + cookie["max-age"] = "" + + elif expires := cookie["expires"]: + if expire_time := self._parse_date(expires): + self._expire_cookie(expire_time, domain, path, name) + else: + cookie["expires"] = "" + + key = (domain, path) + if self._cookies[key].get(name) != cookie: + # Don't blow away the cache if the same + # cookie gets set again + self._cookies[key][name] = cookie + self._morsel_cache[key].pop(name, None) + + self._do_expiration() + + def filter_cookies(self, request_url: URL = URL()) -> "BaseCookie[str]": + """Returns this jar's cookies filtered by their attributes.""" + # We always use BaseCookie now since all + # cookies set on on filtered are fully constructed + # Morsels, not just names and values. + filtered: BaseCookie[str] = BaseCookie() + if not self._cookies: + # Skip do_expiration() if there are no cookies. + return filtered + self._do_expiration() + if not self._cookies: + # Skip rest of function if no non-expired cookies. + return filtered + if type(request_url) is not URL: + warnings.warn( + "filter_cookies expects yarl.URL instances only," + f"and will stop working in 4.x, got {type(request_url)}", + DeprecationWarning, + stacklevel=2, + ) + request_url = URL(request_url) + hostname = request_url.raw_host or "" + + is_not_secure = request_url.scheme not in ("https", "wss") + if is_not_secure and self._treat_as_secure_origin: + request_origin = URL() + with contextlib.suppress(ValueError): + request_origin = request_url.origin() + is_not_secure = request_origin not in self._treat_as_secure_origin + + # Send shared cookie + key = ("", "") + for c in self._cookies[key].values(): + # Check cache first + if c.key in self._morsel_cache[key]: + filtered[c.key] = self._morsel_cache[key][c.key] + continue + + # Build and cache the morsel + mrsl_val = self._build_morsel(c) + self._morsel_cache[key][c.key] = mrsl_val + filtered[c.key] = mrsl_val + + if is_ip_address(hostname): + if not self._unsafe: + return filtered + domains: Iterable[str] = (hostname,) + else: + # Get all the subdomains that might match a cookie (e.g. "foo.bar.com", "bar.com", "com") + domains = itertools.accumulate( + reversed(hostname.split(".")), _FORMAT_DOMAIN_REVERSED + ) + + # Get all the path prefixes that might match a cookie (e.g. "", "/foo", "/foo/bar") + paths = itertools.accumulate(request_url.path.split("/"), _FORMAT_PATH) + # Create every combination of (domain, path) pairs. + pairs = itertools.product(domains, paths) + + path_len = len(request_url.path) + # Point 2: https://www.rfc-editor.org/rfc/rfc6265.html#section-5.4 + for p in pairs: + if p not in self._cookies: + continue + for name, cookie in self._cookies[p].items(): + domain = cookie["domain"] + + if (domain, name) in self._host_only_cookies and domain != hostname: + continue + + # Skip edge case when the cookie has a trailing slash but request doesn't. + if len(cookie["path"]) > path_len: + continue + + if is_not_secure and cookie["secure"]: + continue + + # We already built the Morsel so reuse it here + if name in self._morsel_cache[p]: + filtered[name] = self._morsel_cache[p][name] + continue + + # Build and cache the morsel + mrsl_val = self._build_morsel(cookie) + self._morsel_cache[p][name] = mrsl_val + filtered[name] = mrsl_val + + return filtered + + def _build_morsel(self, cookie: Morsel[str]) -> Morsel[str]: + """Build a morsel for sending, respecting quote_cookie setting.""" + if self._quote_cookie and cookie.coded_value and cookie.coded_value[0] == '"': + return preserve_morsel_with_coded_value(cookie) + morsel: Morsel[str] = Morsel() + if self._quote_cookie: + value, coded_value = _SIMPLE_COOKIE.value_encode(cookie.value) + else: + coded_value = value = cookie.value + # We use __setstate__ instead of the public set() API because it allows us to + # bypass validation and set already validated state. This is more stable than + # setting protected attributes directly and unlikely to change since it would + # break pickling. + morsel.__setstate__({"key": cookie.key, "value": value, "coded_value": coded_value}) # type: ignore[attr-defined] + return morsel + + @staticmethod + def _is_domain_match(domain: str, hostname: str) -> bool: + """Implements domain matching adhering to RFC 6265.""" + if hostname == domain: + return True + + if not hostname.endswith(domain): + return False + + non_matching = hostname[: -len(domain)] + + if not non_matching.endswith("."): + return False + + return not is_ip_address(hostname) + + @classmethod + def _parse_date(cls, date_str: str) -> Optional[int]: + """Implements date string parsing adhering to RFC 6265.""" + if not date_str: + return None + + found_time = False + found_day = False + found_month = False + found_year = False + + hour = minute = second = 0 + day = 0 + month = 0 + year = 0 + + for token_match in cls.DATE_TOKENS_RE.finditer(date_str): + + token = token_match.group("token") + + if not found_time: + time_match = cls.DATE_HMS_TIME_RE.match(token) + if time_match: + found_time = True + hour, minute, second = (int(s) for s in time_match.groups()) + continue + + if not found_day: + day_match = cls.DATE_DAY_OF_MONTH_RE.match(token) + if day_match: + found_day = True + day = int(day_match.group()) + continue + + if not found_month: + month_match = cls.DATE_MONTH_RE.match(token) + if month_match: + found_month = True + assert month_match.lastindex is not None + month = month_match.lastindex + continue + + if not found_year: + year_match = cls.DATE_YEAR_RE.match(token) + if year_match: + found_year = True + year = int(year_match.group()) + + if 70 <= year <= 99: + year += 1900 + elif 0 <= year <= 69: + year += 2000 + + if False in (found_day, found_month, found_year, found_time): + return None + + if not 1 <= day <= 31: + return None + + if year < 1601 or hour > 23 or minute > 59 or second > 59: + return None + + return calendar.timegm((year, month, day, hour, minute, second, -1, -1, -1)) + + +class DummyCookieJar(AbstractCookieJar): + """Implements a dummy cookie storage. + + It can be used with the ClientSession when no cookie processing is needed. + + """ + + def __init__(self, *, loop: Optional[asyncio.AbstractEventLoop] = None) -> None: + super().__init__(loop=loop) + + def __iter__(self) -> "Iterator[Morsel[str]]": + while False: + yield None + + def __len__(self) -> int: + return 0 + + @property + def quote_cookie(self) -> bool: + return True + + def clear(self, predicate: Optional[ClearCookiePredicate] = None) -> None: + pass + + def clear_domain(self, domain: str) -> None: + pass + + def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> None: + pass + + def filter_cookies(self, request_url: URL) -> "BaseCookie[str]": + return SimpleCookie() diff --git a/.venv/lib/python3.9/site-packages/aiohttp/formdata.py b/.venv/lib/python3.9/site-packages/aiohttp/formdata.py new file mode 100644 index 0000000..a5a4f60 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/formdata.py @@ -0,0 +1,179 @@ +import io +import warnings +from typing import Any, Iterable, List, Optional +from urllib.parse import urlencode + +from multidict import MultiDict, MultiDictProxy + +from . import hdrs, multipart, payload +from .helpers import guess_filename +from .payload import Payload + +__all__ = ("FormData",) + + +class FormData: + """Helper class for form body generation. + + Supports multipart/form-data and application/x-www-form-urlencoded. + """ + + def __init__( + self, + fields: Iterable[Any] = (), + quote_fields: bool = True, + charset: Optional[str] = None, + *, + default_to_multipart: bool = False, + ) -> None: + self._writer = multipart.MultipartWriter("form-data") + self._fields: List[Any] = [] + self._is_multipart = default_to_multipart + self._quote_fields = quote_fields + self._charset = charset + + if isinstance(fields, dict): + fields = list(fields.items()) + elif not isinstance(fields, (list, tuple)): + fields = (fields,) + self.add_fields(*fields) + + @property + def is_multipart(self) -> bool: + return self._is_multipart + + def add_field( + self, + name: str, + value: Any, + *, + content_type: Optional[str] = None, + filename: Optional[str] = None, + content_transfer_encoding: Optional[str] = None, + ) -> None: + + if isinstance(value, io.IOBase): + self._is_multipart = True + elif isinstance(value, (bytes, bytearray, memoryview)): + msg = ( + "In v4, passing bytes will no longer create a file field. " + "Please explicitly use the filename parameter or pass a BytesIO object." + ) + if filename is None and content_transfer_encoding is None: + warnings.warn(msg, DeprecationWarning) + filename = name + + type_options: MultiDict[str] = MultiDict({"name": name}) + if filename is not None and not isinstance(filename, str): + raise TypeError("filename must be an instance of str. Got: %s" % filename) + if filename is None and isinstance(value, io.IOBase): + filename = guess_filename(value, name) + if filename is not None: + type_options["filename"] = filename + self._is_multipart = True + + headers = {} + if content_type is not None: + if not isinstance(content_type, str): + raise TypeError( + "content_type must be an instance of str. Got: %s" % content_type + ) + headers[hdrs.CONTENT_TYPE] = content_type + self._is_multipart = True + if content_transfer_encoding is not None: + if not isinstance(content_transfer_encoding, str): + raise TypeError( + "content_transfer_encoding must be an instance" + " of str. Got: %s" % content_transfer_encoding + ) + msg = ( + "content_transfer_encoding is deprecated. " + "To maintain compatibility with v4 please pass a BytesPayload." + ) + warnings.warn(msg, DeprecationWarning) + self._is_multipart = True + + self._fields.append((type_options, headers, value)) + + def add_fields(self, *fields: Any) -> None: + to_add = list(fields) + + while to_add: + rec = to_add.pop(0) + + if isinstance(rec, io.IOBase): + k = guess_filename(rec, "unknown") + self.add_field(k, rec) # type: ignore[arg-type] + + elif isinstance(rec, (MultiDictProxy, MultiDict)): + to_add.extend(rec.items()) + + elif isinstance(rec, (list, tuple)) and len(rec) == 2: + k, fp = rec + self.add_field(k, fp) + + else: + raise TypeError( + "Only io.IOBase, multidict and (name, file) " + "pairs allowed, use .add_field() for passing " + "more complex parameters, got {!r}".format(rec) + ) + + def _gen_form_urlencoded(self) -> payload.BytesPayload: + # form data (x-www-form-urlencoded) + data = [] + for type_options, _, value in self._fields: + data.append((type_options["name"], value)) + + charset = self._charset if self._charset is not None else "utf-8" + + if charset == "utf-8": + content_type = "application/x-www-form-urlencoded" + else: + content_type = "application/x-www-form-urlencoded; charset=%s" % charset + + return payload.BytesPayload( + urlencode(data, doseq=True, encoding=charset).encode(), + content_type=content_type, + ) + + def _gen_form_data(self) -> multipart.MultipartWriter: + """Encode a list of fields using the multipart/form-data MIME format""" + for dispparams, headers, value in self._fields: + try: + if hdrs.CONTENT_TYPE in headers: + part = payload.get_payload( + value, + content_type=headers[hdrs.CONTENT_TYPE], + headers=headers, + encoding=self._charset, + ) + else: + part = payload.get_payload( + value, headers=headers, encoding=self._charset + ) + except Exception as exc: + raise TypeError( + "Can not serialize value type: %r\n " + "headers: %r\n value: %r" % (type(value), headers, value) + ) from exc + + if dispparams: + part.set_content_disposition( + "form-data", quote_fields=self._quote_fields, **dispparams + ) + # FIXME cgi.FieldStorage doesn't likes body parts with + # Content-Length which were sent via chunked transfer encoding + assert part.headers is not None + part.headers.popall(hdrs.CONTENT_LENGTH, None) + + self._writer.append_payload(part) + + self._fields.clear() + return self._writer + + def __call__(self) -> Payload: + if self._is_multipart: + return self._gen_form_data() + else: + return self._gen_form_urlencoded() diff --git a/.venv/lib/python3.9/site-packages/aiohttp/hdrs.py b/.venv/lib/python3.9/site-packages/aiohttp/hdrs.py new file mode 100644 index 0000000..c8d6b35 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/hdrs.py @@ -0,0 +1,121 @@ +"""HTTP Headers constants.""" + +# After changing the file content call ./tools/gen.py +# to regenerate the headers parser +import itertools +from typing import Final, Set + +from multidict import istr + +METH_ANY: Final[str] = "*" +METH_CONNECT: Final[str] = "CONNECT" +METH_HEAD: Final[str] = "HEAD" +METH_GET: Final[str] = "GET" +METH_DELETE: Final[str] = "DELETE" +METH_OPTIONS: Final[str] = "OPTIONS" +METH_PATCH: Final[str] = "PATCH" +METH_POST: Final[str] = "POST" +METH_PUT: Final[str] = "PUT" +METH_TRACE: Final[str] = "TRACE" + +METH_ALL: Final[Set[str]] = { + METH_CONNECT, + METH_HEAD, + METH_GET, + METH_DELETE, + METH_OPTIONS, + METH_PATCH, + METH_POST, + METH_PUT, + METH_TRACE, +} + +ACCEPT: Final[istr] = istr("Accept") +ACCEPT_CHARSET: Final[istr] = istr("Accept-Charset") +ACCEPT_ENCODING: Final[istr] = istr("Accept-Encoding") +ACCEPT_LANGUAGE: Final[istr] = istr("Accept-Language") +ACCEPT_RANGES: Final[istr] = istr("Accept-Ranges") +ACCESS_CONTROL_MAX_AGE: Final[istr] = istr("Access-Control-Max-Age") +ACCESS_CONTROL_ALLOW_CREDENTIALS: Final[istr] = istr("Access-Control-Allow-Credentials") +ACCESS_CONTROL_ALLOW_HEADERS: Final[istr] = istr("Access-Control-Allow-Headers") +ACCESS_CONTROL_ALLOW_METHODS: Final[istr] = istr("Access-Control-Allow-Methods") +ACCESS_CONTROL_ALLOW_ORIGIN: Final[istr] = istr("Access-Control-Allow-Origin") +ACCESS_CONTROL_EXPOSE_HEADERS: Final[istr] = istr("Access-Control-Expose-Headers") +ACCESS_CONTROL_REQUEST_HEADERS: Final[istr] = istr("Access-Control-Request-Headers") +ACCESS_CONTROL_REQUEST_METHOD: Final[istr] = istr("Access-Control-Request-Method") +AGE: Final[istr] = istr("Age") +ALLOW: Final[istr] = istr("Allow") +AUTHORIZATION: Final[istr] = istr("Authorization") +CACHE_CONTROL: Final[istr] = istr("Cache-Control") +CONNECTION: Final[istr] = istr("Connection") +CONTENT_DISPOSITION: Final[istr] = istr("Content-Disposition") +CONTENT_ENCODING: Final[istr] = istr("Content-Encoding") +CONTENT_LANGUAGE: Final[istr] = istr("Content-Language") +CONTENT_LENGTH: Final[istr] = istr("Content-Length") +CONTENT_LOCATION: Final[istr] = istr("Content-Location") +CONTENT_MD5: Final[istr] = istr("Content-MD5") +CONTENT_RANGE: Final[istr] = istr("Content-Range") +CONTENT_TRANSFER_ENCODING: Final[istr] = istr("Content-Transfer-Encoding") +CONTENT_TYPE: Final[istr] = istr("Content-Type") +COOKIE: Final[istr] = istr("Cookie") +DATE: Final[istr] = istr("Date") +DESTINATION: Final[istr] = istr("Destination") +DIGEST: Final[istr] = istr("Digest") +ETAG: Final[istr] = istr("Etag") +EXPECT: Final[istr] = istr("Expect") +EXPIRES: Final[istr] = istr("Expires") +FORWARDED: Final[istr] = istr("Forwarded") +FROM: Final[istr] = istr("From") +HOST: Final[istr] = istr("Host") +IF_MATCH: Final[istr] = istr("If-Match") +IF_MODIFIED_SINCE: Final[istr] = istr("If-Modified-Since") +IF_NONE_MATCH: Final[istr] = istr("If-None-Match") +IF_RANGE: Final[istr] = istr("If-Range") +IF_UNMODIFIED_SINCE: Final[istr] = istr("If-Unmodified-Since") +KEEP_ALIVE: Final[istr] = istr("Keep-Alive") +LAST_EVENT_ID: Final[istr] = istr("Last-Event-ID") +LAST_MODIFIED: Final[istr] = istr("Last-Modified") +LINK: Final[istr] = istr("Link") +LOCATION: Final[istr] = istr("Location") +MAX_FORWARDS: Final[istr] = istr("Max-Forwards") +ORIGIN: Final[istr] = istr("Origin") +PRAGMA: Final[istr] = istr("Pragma") +PROXY_AUTHENTICATE: Final[istr] = istr("Proxy-Authenticate") +PROXY_AUTHORIZATION: Final[istr] = istr("Proxy-Authorization") +RANGE: Final[istr] = istr("Range") +REFERER: Final[istr] = istr("Referer") +RETRY_AFTER: Final[istr] = istr("Retry-After") +SEC_WEBSOCKET_ACCEPT: Final[istr] = istr("Sec-WebSocket-Accept") +SEC_WEBSOCKET_VERSION: Final[istr] = istr("Sec-WebSocket-Version") +SEC_WEBSOCKET_PROTOCOL: Final[istr] = istr("Sec-WebSocket-Protocol") +SEC_WEBSOCKET_EXTENSIONS: Final[istr] = istr("Sec-WebSocket-Extensions") +SEC_WEBSOCKET_KEY: Final[istr] = istr("Sec-WebSocket-Key") +SEC_WEBSOCKET_KEY1: Final[istr] = istr("Sec-WebSocket-Key1") +SERVER: Final[istr] = istr("Server") +SET_COOKIE: Final[istr] = istr("Set-Cookie") +TE: Final[istr] = istr("TE") +TRAILER: Final[istr] = istr("Trailer") +TRANSFER_ENCODING: Final[istr] = istr("Transfer-Encoding") +UPGRADE: Final[istr] = istr("Upgrade") +URI: Final[istr] = istr("URI") +USER_AGENT: Final[istr] = istr("User-Agent") +VARY: Final[istr] = istr("Vary") +VIA: Final[istr] = istr("Via") +WANT_DIGEST: Final[istr] = istr("Want-Digest") +WARNING: Final[istr] = istr("Warning") +WWW_AUTHENTICATE: Final[istr] = istr("WWW-Authenticate") +X_FORWARDED_FOR: Final[istr] = istr("X-Forwarded-For") +X_FORWARDED_HOST: Final[istr] = istr("X-Forwarded-Host") +X_FORWARDED_PROTO: Final[istr] = istr("X-Forwarded-Proto") + +# These are the upper/lower case variants of the headers/methods +# Example: {'hOst', 'host', 'HoST', 'HOSt', 'hOsT', 'HosT', 'hoSt', ...} +METH_HEAD_ALL: Final = frozenset( + map("".join, itertools.product(*zip(METH_HEAD.upper(), METH_HEAD.lower()))) +) +METH_CONNECT_ALL: Final = frozenset( + map("".join, itertools.product(*zip(METH_CONNECT.upper(), METH_CONNECT.lower()))) +) +HOST_ALL: Final = frozenset( + map("".join, itertools.product(*zip(HOST.upper(), HOST.lower()))) +) diff --git a/.venv/lib/python3.9/site-packages/aiohttp/helpers.py b/.venv/lib/python3.9/site-packages/aiohttp/helpers.py new file mode 100644 index 0000000..dfab987 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/helpers.py @@ -0,0 +1,986 @@ +"""Various helper functions""" + +import asyncio +import base64 +import binascii +import contextlib +import datetime +import enum +import functools +import inspect +import netrc +import os +import platform +import re +import sys +import time +import weakref +from collections import namedtuple +from contextlib import suppress +from email.message import EmailMessage +from email.parser import HeaderParser +from email.policy import HTTP +from email.utils import parsedate +from math import ceil +from pathlib import Path +from types import MappingProxyType, TracebackType +from typing import ( + Any, + Callable, + ContextManager, + Dict, + Generator, + Generic, + Iterable, + Iterator, + List, + Mapping, + Optional, + Protocol, + Tuple, + Type, + TypeVar, + Union, + get_args, + overload, +) +from urllib.parse import quote +from urllib.request import getproxies, proxy_bypass + +import attr +from multidict import MultiDict, MultiDictProxy, MultiMapping +from propcache.api import under_cached_property as reify +from yarl import URL + +from . import hdrs +from .log import client_logger + +if sys.version_info >= (3, 11): + import asyncio as async_timeout +else: + import async_timeout + +__all__ = ("BasicAuth", "ChainMapProxy", "ETag", "reify") + +IS_MACOS = platform.system() == "Darwin" +IS_WINDOWS = platform.system() == "Windows" + +PY_310 = sys.version_info >= (3, 10) +PY_311 = sys.version_info >= (3, 11) + + +_T = TypeVar("_T") +_S = TypeVar("_S") + +_SENTINEL = enum.Enum("_SENTINEL", "sentinel") +sentinel = _SENTINEL.sentinel + +NO_EXTENSIONS = bool(os.environ.get("AIOHTTP_NO_EXTENSIONS")) + +# https://datatracker.ietf.org/doc/html/rfc9112#section-6.3-2.1 +EMPTY_BODY_STATUS_CODES = frozenset((204, 304, *range(100, 200))) +# https://datatracker.ietf.org/doc/html/rfc9112#section-6.3-2.1 +# https://datatracker.ietf.org/doc/html/rfc9112#section-6.3-2.2 +EMPTY_BODY_METHODS = hdrs.METH_HEAD_ALL + +DEBUG = sys.flags.dev_mode or ( + not sys.flags.ignore_environment and bool(os.environ.get("PYTHONASYNCIODEBUG")) +) + + +CHAR = {chr(i) for i in range(0, 128)} +CTL = {chr(i) for i in range(0, 32)} | { + chr(127), +} +SEPARATORS = { + "(", + ")", + "<", + ">", + "@", + ",", + ";", + ":", + "\\", + '"', + "/", + "[", + "]", + "?", + "=", + "{", + "}", + " ", + chr(9), +} +TOKEN = CHAR ^ CTL ^ SEPARATORS + + +class noop: + def __await__(self) -> Generator[None, None, None]: + yield + + +class BasicAuth(namedtuple("BasicAuth", ["login", "password", "encoding"])): + """Http basic authentication helper.""" + + def __new__( + cls, login: str, password: str = "", encoding: str = "latin1" + ) -> "BasicAuth": + if login is None: + raise ValueError("None is not allowed as login value") + + if password is None: + raise ValueError("None is not allowed as password value") + + if ":" in login: + raise ValueError('A ":" is not allowed in login (RFC 1945#section-11.1)') + + return super().__new__(cls, login, password, encoding) + + @classmethod + def decode(cls, auth_header: str, encoding: str = "latin1") -> "BasicAuth": + """Create a BasicAuth object from an Authorization HTTP header.""" + try: + auth_type, encoded_credentials = auth_header.split(" ", 1) + except ValueError: + raise ValueError("Could not parse authorization header.") + + if auth_type.lower() != "basic": + raise ValueError("Unknown authorization method %s" % auth_type) + + try: + decoded = base64.b64decode( + encoded_credentials.encode("ascii"), validate=True + ).decode(encoding) + except binascii.Error: + raise ValueError("Invalid base64 encoding.") + + try: + # RFC 2617 HTTP Authentication + # https://www.ietf.org/rfc/rfc2617.txt + # the colon must be present, but the username and password may be + # otherwise blank. + username, password = decoded.split(":", 1) + except ValueError: + raise ValueError("Invalid credentials.") + + return cls(username, password, encoding=encoding) + + @classmethod + def from_url(cls, url: URL, *, encoding: str = "latin1") -> Optional["BasicAuth"]: + """Create BasicAuth from url.""" + if not isinstance(url, URL): + raise TypeError("url should be yarl.URL instance") + # Check raw_user and raw_password first as yarl is likely + # to already have these values parsed from the netloc in the cache. + if url.raw_user is None and url.raw_password is None: + return None + return cls(url.user or "", url.password or "", encoding=encoding) + + def encode(self) -> str: + """Encode credentials.""" + creds = (f"{self.login}:{self.password}").encode(self.encoding) + return "Basic %s" % base64.b64encode(creds).decode(self.encoding) + + +def strip_auth_from_url(url: URL) -> Tuple[URL, Optional[BasicAuth]]: + """Remove user and password from URL if present and return BasicAuth object.""" + # Check raw_user and raw_password first as yarl is likely + # to already have these values parsed from the netloc in the cache. + if url.raw_user is None and url.raw_password is None: + return url, None + return url.with_user(None), BasicAuth(url.user or "", url.password or "") + + +def netrc_from_env() -> Optional[netrc.netrc]: + """Load netrc from file. + + Attempt to load it from the path specified by the env-var + NETRC or in the default location in the user's home directory. + + Returns None if it couldn't be found or fails to parse. + """ + netrc_env = os.environ.get("NETRC") + + if netrc_env is not None: + netrc_path = Path(netrc_env) + else: + try: + home_dir = Path.home() + except RuntimeError as e: # pragma: no cover + # if pathlib can't resolve home, it may raise a RuntimeError + client_logger.debug( + "Could not resolve home directory when " + "trying to look for .netrc file: %s", + e, + ) + return None + + netrc_path = home_dir / ("_netrc" if IS_WINDOWS else ".netrc") + + try: + return netrc.netrc(str(netrc_path)) + except netrc.NetrcParseError as e: + client_logger.warning("Could not parse .netrc file: %s", e) + except OSError as e: + netrc_exists = False + with contextlib.suppress(OSError): + netrc_exists = netrc_path.is_file() + # we couldn't read the file (doesn't exist, permissions, etc.) + if netrc_env or netrc_exists: + # only warn if the environment wanted us to load it, + # or it appears like the default file does actually exist + client_logger.warning("Could not read .netrc file: %s", e) + + return None + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class ProxyInfo: + proxy: URL + proxy_auth: Optional[BasicAuth] + + +def basicauth_from_netrc(netrc_obj: Optional[netrc.netrc], host: str) -> BasicAuth: + """ + Return :py:class:`~aiohttp.BasicAuth` credentials for ``host`` from ``netrc_obj``. + + :raises LookupError: if ``netrc_obj`` is :py:data:`None` or if no + entry is found for the ``host``. + """ + if netrc_obj is None: + raise LookupError("No .netrc file found") + auth_from_netrc = netrc_obj.authenticators(host) + + if auth_from_netrc is None: + raise LookupError(f"No entry for {host!s} found in the `.netrc` file.") + login, account, password = auth_from_netrc + + # TODO(PY311): username = login or account + # Up to python 3.10, account could be None if not specified, + # and login will be empty string if not specified. From 3.11, + # login and account will be empty string if not specified. + username = login if (login or account is None) else account + + # TODO(PY311): Remove this, as password will be empty string + # if not specified + if password is None: + password = "" + + return BasicAuth(username, password) + + +def proxies_from_env() -> Dict[str, ProxyInfo]: + proxy_urls = { + k: URL(v) + for k, v in getproxies().items() + if k in ("http", "https", "ws", "wss") + } + netrc_obj = netrc_from_env() + stripped = {k: strip_auth_from_url(v) for k, v in proxy_urls.items()} + ret = {} + for proto, val in stripped.items(): + proxy, auth = val + if proxy.scheme in ("https", "wss"): + client_logger.warning( + "%s proxies %s are not supported, ignoring", proxy.scheme.upper(), proxy + ) + continue + if netrc_obj and auth is None: + if proxy.host is not None: + try: + auth = basicauth_from_netrc(netrc_obj, proxy.host) + except LookupError: + auth = None + ret[proto] = ProxyInfo(proxy, auth) + return ret + + +def get_env_proxy_for_url(url: URL) -> Tuple[URL, Optional[BasicAuth]]: + """Get a permitted proxy for the given URL from the env.""" + if url.host is not None and proxy_bypass(url.host): + raise LookupError(f"Proxying is disallowed for `{url.host!r}`") + + proxies_in_env = proxies_from_env() + try: + proxy_info = proxies_in_env[url.scheme] + except KeyError: + raise LookupError(f"No proxies found for `{url!s}` in the env") + else: + return proxy_info.proxy, proxy_info.proxy_auth + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class MimeType: + type: str + subtype: str + suffix: str + parameters: "MultiDictProxy[str]" + + +@functools.lru_cache(maxsize=56) +def parse_mimetype(mimetype: str) -> MimeType: + """Parses a MIME type into its components. + + mimetype is a MIME type string. + + Returns a MimeType object. + + Example: + + >>> parse_mimetype('text/html; charset=utf-8') + MimeType(type='text', subtype='html', suffix='', + parameters={'charset': 'utf-8'}) + + """ + if not mimetype: + return MimeType( + type="", subtype="", suffix="", parameters=MultiDictProxy(MultiDict()) + ) + + parts = mimetype.split(";") + params: MultiDict[str] = MultiDict() + for item in parts[1:]: + if not item: + continue + key, _, value = item.partition("=") + params.add(key.lower().strip(), value.strip(' "')) + + fulltype = parts[0].strip().lower() + if fulltype == "*": + fulltype = "*/*" + + mtype, _, stype = fulltype.partition("/") + stype, _, suffix = stype.partition("+") + + return MimeType( + type=mtype, subtype=stype, suffix=suffix, parameters=MultiDictProxy(params) + ) + + +class EnsureOctetStream(EmailMessage): + def __init__(self) -> None: + super().__init__() + # https://www.rfc-editor.org/rfc/rfc9110#section-8.3-5 + self.set_default_type("application/octet-stream") + + def get_content_type(self) -> str: + """Re-implementation from Message + + Returns application/octet-stream in place of plain/text when + value is wrong. + + The way this class is used guarantees that content-type will + be present so simplify the checks wrt to the base implementation. + """ + value = self.get("content-type", "").lower() + + # Based on the implementation of _splitparam in the standard library + ctype, _, _ = value.partition(";") + ctype = ctype.strip() + if ctype.count("/") != 1: + return self.get_default_type() + return ctype + + +@functools.lru_cache(maxsize=56) +def parse_content_type(raw: str) -> Tuple[str, MappingProxyType[str, str]]: + """Parse Content-Type header. + + Returns a tuple of the parsed content type and a + MappingProxyType of parameters. The default returned value + is `application/octet-stream` + """ + msg = HeaderParser(EnsureOctetStream, policy=HTTP).parsestr(f"Content-Type: {raw}") + content_type = msg.get_content_type() + params = msg.get_params(()) + content_dict = dict(params[1:]) # First element is content type again + return content_type, MappingProxyType(content_dict) + + +def guess_filename(obj: Any, default: Optional[str] = None) -> Optional[str]: + name = getattr(obj, "name", None) + if name and isinstance(name, str) and name[0] != "<" and name[-1] != ">": + return Path(name).name + return default + + +not_qtext_re = re.compile(r"[^\041\043-\133\135-\176]") +QCONTENT = {chr(i) for i in range(0x20, 0x7F)} | {"\t"} + + +def quoted_string(content: str) -> str: + """Return 7-bit content as quoted-string. + + Format content into a quoted-string as defined in RFC5322 for + Internet Message Format. Notice that this is not the 8-bit HTTP + format, but the 7-bit email format. Content must be in usascii or + a ValueError is raised. + """ + if not (QCONTENT > set(content)): + raise ValueError(f"bad content for quoted-string {content!r}") + return not_qtext_re.sub(lambda x: "\\" + x.group(0), content) + + +def content_disposition_header( + disptype: str, quote_fields: bool = True, _charset: str = "utf-8", **params: str +) -> str: + """Sets ``Content-Disposition`` header for MIME. + + This is the MIME payload Content-Disposition header from RFC 2183 + and RFC 7579 section 4.2, not the HTTP Content-Disposition from + RFC 6266. + + disptype is a disposition type: inline, attachment, form-data. + Should be valid extension token (see RFC 2183) + + quote_fields performs value quoting to 7-bit MIME headers + according to RFC 7578. Set to quote_fields to False if recipient + can take 8-bit file names and field values. + + _charset specifies the charset to use when quote_fields is True. + + params is a dict with disposition params. + """ + if not disptype or not (TOKEN > set(disptype)): + raise ValueError(f"bad content disposition type {disptype!r}") + + value = disptype + if params: + lparams = [] + for key, val in params.items(): + if not key or not (TOKEN > set(key)): + raise ValueError(f"bad content disposition parameter {key!r}={val!r}") + if quote_fields: + if key.lower() == "filename": + qval = quote(val, "", encoding=_charset) + lparams.append((key, '"%s"' % qval)) + else: + try: + qval = quoted_string(val) + except ValueError: + qval = "".join( + (_charset, "''", quote(val, "", encoding=_charset)) + ) + lparams.append((key + "*", qval)) + else: + lparams.append((key, '"%s"' % qval)) + else: + qval = val.replace("\\", "\\\\").replace('"', '\\"') + lparams.append((key, '"%s"' % qval)) + sparams = "; ".join("=".join(pair) for pair in lparams) + value = "; ".join((value, sparams)) + return value + + +def is_ip_address(host: Optional[str]) -> bool: + """Check if host looks like an IP Address. + + This check is only meant as a heuristic to ensure that + a host is not a domain name. + """ + if not host: + return False + # For a host to be an ipv4 address, it must be all numeric. + # The host must contain a colon to be an IPv6 address. + return ":" in host or host.replace(".", "").isdigit() + + +_cached_current_datetime: Optional[int] = None +_cached_formatted_datetime = "" + + +def rfc822_formatted_time() -> str: + global _cached_current_datetime + global _cached_formatted_datetime + + now = int(time.time()) + if now != _cached_current_datetime: + # Weekday and month names for HTTP date/time formatting; + # always English! + # Tuples are constants stored in codeobject! + _weekdayname = ("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun") + _monthname = ( + "", # Dummy so we can use 1-based month numbers + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", + ) + + year, month, day, hh, mm, ss, wd, *tail = time.gmtime(now) + _cached_formatted_datetime = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( + _weekdayname[wd], + day, + _monthname[month], + year, + hh, + mm, + ss, + ) + _cached_current_datetime = now + return _cached_formatted_datetime + + +def _weakref_handle(info: "Tuple[weakref.ref[object], str]") -> None: + ref, name = info + ob = ref() + if ob is not None: + with suppress(Exception): + getattr(ob, name)() + + +def weakref_handle( + ob: object, + name: str, + timeout: float, + loop: asyncio.AbstractEventLoop, + timeout_ceil_threshold: float = 5, +) -> Optional[asyncio.TimerHandle]: + if timeout is not None and timeout > 0: + when = loop.time() + timeout + if timeout >= timeout_ceil_threshold: + when = ceil(when) + + return loop.call_at(when, _weakref_handle, (weakref.ref(ob), name)) + return None + + +def call_later( + cb: Callable[[], Any], + timeout: float, + loop: asyncio.AbstractEventLoop, + timeout_ceil_threshold: float = 5, +) -> Optional[asyncio.TimerHandle]: + if timeout is None or timeout <= 0: + return None + now = loop.time() + when = calculate_timeout_when(now, timeout, timeout_ceil_threshold) + return loop.call_at(when, cb) + + +def calculate_timeout_when( + loop_time: float, + timeout: float, + timeout_ceiling_threshold: float, +) -> float: + """Calculate when to execute a timeout.""" + when = loop_time + timeout + if timeout > timeout_ceiling_threshold: + return ceil(when) + return when + + +class TimeoutHandle: + """Timeout handle""" + + __slots__ = ("_timeout", "_loop", "_ceil_threshold", "_callbacks") + + def __init__( + self, + loop: asyncio.AbstractEventLoop, + timeout: Optional[float], + ceil_threshold: float = 5, + ) -> None: + self._timeout = timeout + self._loop = loop + self._ceil_threshold = ceil_threshold + self._callbacks: List[ + Tuple[Callable[..., None], Tuple[Any, ...], Dict[str, Any]] + ] = [] + + def register( + self, callback: Callable[..., None], *args: Any, **kwargs: Any + ) -> None: + self._callbacks.append((callback, args, kwargs)) + + def close(self) -> None: + self._callbacks.clear() + + def start(self) -> Optional[asyncio.TimerHandle]: + timeout = self._timeout + if timeout is not None and timeout > 0: + when = self._loop.time() + timeout + if timeout >= self._ceil_threshold: + when = ceil(when) + return self._loop.call_at(when, self.__call__) + else: + return None + + def timer(self) -> "BaseTimerContext": + if self._timeout is not None and self._timeout > 0: + timer = TimerContext(self._loop) + self.register(timer.timeout) + return timer + else: + return TimerNoop() + + def __call__(self) -> None: + for cb, args, kwargs in self._callbacks: + with suppress(Exception): + cb(*args, **kwargs) + + self._callbacks.clear() + + +class BaseTimerContext(ContextManager["BaseTimerContext"]): + + __slots__ = () + + def assert_timeout(self) -> None: + """Raise TimeoutError if timeout has been exceeded.""" + + +class TimerNoop(BaseTimerContext): + + __slots__ = () + + def __enter__(self) -> BaseTimerContext: + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + return + + +class TimerContext(BaseTimerContext): + """Low resolution timeout context manager""" + + __slots__ = ("_loop", "_tasks", "_cancelled", "_cancelling") + + def __init__(self, loop: asyncio.AbstractEventLoop) -> None: + self._loop = loop + self._tasks: List[asyncio.Task[Any]] = [] + self._cancelled = False + self._cancelling = 0 + + def assert_timeout(self) -> None: + """Raise TimeoutError if timer has already been cancelled.""" + if self._cancelled: + raise asyncio.TimeoutError from None + + def __enter__(self) -> BaseTimerContext: + task = asyncio.current_task(loop=self._loop) + if task is None: + raise RuntimeError("Timeout context manager should be used inside a task") + + if sys.version_info >= (3, 11): + # Remember if the task was already cancelling + # so when we __exit__ we can decide if we should + # raise asyncio.TimeoutError or let the cancellation propagate + self._cancelling = task.cancelling() + + if self._cancelled: + raise asyncio.TimeoutError from None + + self._tasks.append(task) + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> Optional[bool]: + enter_task: Optional[asyncio.Task[Any]] = None + if self._tasks: + enter_task = self._tasks.pop() + + if exc_type is asyncio.CancelledError and self._cancelled: + assert enter_task is not None + # The timeout was hit, and the task was cancelled + # so we need to uncancel the last task that entered the context manager + # since the cancellation should not leak out of the context manager + if sys.version_info >= (3, 11): + # If the task was already cancelling don't raise + # asyncio.TimeoutError and instead return None + # to allow the cancellation to propagate + if enter_task.uncancel() > self._cancelling: + return None + raise asyncio.TimeoutError from exc_val + return None + + def timeout(self) -> None: + if not self._cancelled: + for task in set(self._tasks): + task.cancel() + + self._cancelled = True + + +def ceil_timeout( + delay: Optional[float], ceil_threshold: float = 5 +) -> async_timeout.Timeout: + if delay is None or delay <= 0: + return async_timeout.timeout(None) + + loop = asyncio.get_running_loop() + now = loop.time() + when = now + delay + if delay > ceil_threshold: + when = ceil(when) + return async_timeout.timeout_at(when) + + +class HeadersMixin: + """Mixin for handling headers.""" + + ATTRS = frozenset(["_content_type", "_content_dict", "_stored_content_type"]) + + _headers: MultiMapping[str] + _content_type: Optional[str] = None + _content_dict: Optional[Dict[str, str]] = None + _stored_content_type: Union[str, None, _SENTINEL] = sentinel + + def _parse_content_type(self, raw: Optional[str]) -> None: + self._stored_content_type = raw + if raw is None: + # default value according to RFC 2616 + self._content_type = "application/octet-stream" + self._content_dict = {} + else: + content_type, content_mapping_proxy = parse_content_type(raw) + self._content_type = content_type + # _content_dict needs to be mutable so we can update it + self._content_dict = content_mapping_proxy.copy() + + @property + def content_type(self) -> str: + """The value of content part for Content-Type HTTP header.""" + raw = self._headers.get(hdrs.CONTENT_TYPE) + if self._stored_content_type != raw: + self._parse_content_type(raw) + assert self._content_type is not None + return self._content_type + + @property + def charset(self) -> Optional[str]: + """The value of charset part for Content-Type HTTP header.""" + raw = self._headers.get(hdrs.CONTENT_TYPE) + if self._stored_content_type != raw: + self._parse_content_type(raw) + assert self._content_dict is not None + return self._content_dict.get("charset") + + @property + def content_length(self) -> Optional[int]: + """The value of Content-Length HTTP header.""" + content_length = self._headers.get(hdrs.CONTENT_LENGTH) + return None if content_length is None else int(content_length) + + +def set_result(fut: "asyncio.Future[_T]", result: _T) -> None: + if not fut.done(): + fut.set_result(result) + + +_EXC_SENTINEL = BaseException() + + +class ErrorableProtocol(Protocol): + def set_exception( + self, + exc: BaseException, + exc_cause: BaseException = ..., + ) -> None: ... # pragma: no cover + + +def set_exception( + fut: "asyncio.Future[_T] | ErrorableProtocol", + exc: BaseException, + exc_cause: BaseException = _EXC_SENTINEL, +) -> None: + """Set future exception. + + If the future is marked as complete, this function is a no-op. + + :param exc_cause: An exception that is a direct cause of ``exc``. + Only set if provided. + """ + if asyncio.isfuture(fut) and fut.done(): + return + + exc_is_sentinel = exc_cause is _EXC_SENTINEL + exc_causes_itself = exc is exc_cause + if not exc_is_sentinel and not exc_causes_itself: + exc.__cause__ = exc_cause + + fut.set_exception(exc) + + +@functools.total_ordering +class AppKey(Generic[_T]): + """Keys for static typing support in Application.""" + + __slots__ = ("_name", "_t", "__orig_class__") + + # This may be set by Python when instantiating with a generic type. We need to + # support this, in order to support types that are not concrete classes, + # like Iterable, which can't be passed as the second parameter to __init__. + __orig_class__: Type[object] + + def __init__(self, name: str, t: Optional[Type[_T]] = None): + # Prefix with module name to help deduplicate key names. + frame = inspect.currentframe() + while frame: + if frame.f_code.co_name == "": + module: str = frame.f_globals["__name__"] + break + frame = frame.f_back + + self._name = module + "." + name + self._t = t + + def __lt__(self, other: object) -> bool: + if isinstance(other, AppKey): + return self._name < other._name + return True # Order AppKey above other types. + + def __repr__(self) -> str: + t = self._t + if t is None: + with suppress(AttributeError): + # Set to type arg. + t = get_args(self.__orig_class__)[0] + + if t is None: + t_repr = "<>" + elif isinstance(t, type): + if t.__module__ == "builtins": + t_repr = t.__qualname__ + else: + t_repr = f"{t.__module__}.{t.__qualname__}" + else: + t_repr = repr(t) + return f"" + + +class ChainMapProxy(Mapping[Union[str, AppKey[Any]], Any]): + __slots__ = ("_maps",) + + def __init__(self, maps: Iterable[Mapping[Union[str, AppKey[Any]], Any]]) -> None: + self._maps = tuple(maps) + + def __init_subclass__(cls) -> None: + raise TypeError( + "Inheritance class {} from ChainMapProxy " + "is forbidden".format(cls.__name__) + ) + + @overload # type: ignore[override] + def __getitem__(self, key: AppKey[_T]) -> _T: ... + + @overload + def __getitem__(self, key: str) -> Any: ... + + def __getitem__(self, key: Union[str, AppKey[_T]]) -> Any: + for mapping in self._maps: + try: + return mapping[key] + except KeyError: + pass + raise KeyError(key) + + @overload # type: ignore[override] + def get(self, key: AppKey[_T], default: _S) -> Union[_T, _S]: ... + + @overload + def get(self, key: AppKey[_T], default: None = ...) -> Optional[_T]: ... + + @overload + def get(self, key: str, default: Any = ...) -> Any: ... + + def get(self, key: Union[str, AppKey[_T]], default: Any = None) -> Any: + try: + return self[key] + except KeyError: + return default + + def __len__(self) -> int: + # reuses stored hash values if possible + return len(set().union(*self._maps)) + + def __iter__(self) -> Iterator[Union[str, AppKey[Any]]]: + d: Dict[Union[str, AppKey[Any]], Any] = {} + for mapping in reversed(self._maps): + # reuses stored hash values if possible + d.update(mapping) + return iter(d) + + def __contains__(self, key: object) -> bool: + return any(key in m for m in self._maps) + + def __bool__(self) -> bool: + return any(self._maps) + + def __repr__(self) -> str: + content = ", ".join(map(repr, self._maps)) + return f"ChainMapProxy({content})" + + +# https://tools.ietf.org/html/rfc7232#section-2.3 +_ETAGC = r"[!\x23-\x7E\x80-\xff]+" +_ETAGC_RE = re.compile(_ETAGC) +_QUOTED_ETAG = rf'(W/)?"({_ETAGC})"' +QUOTED_ETAG_RE = re.compile(_QUOTED_ETAG) +LIST_QUOTED_ETAG_RE = re.compile(rf"({_QUOTED_ETAG})(?:\s*,\s*|$)|(.)") + +ETAG_ANY = "*" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class ETag: + value: str + is_weak: bool = False + + +def validate_etag_value(value: str) -> None: + if value != ETAG_ANY and not _ETAGC_RE.fullmatch(value): + raise ValueError( + f"Value {value!r} is not a valid etag. Maybe it contains '\"'?" + ) + + +def parse_http_date(date_str: Optional[str]) -> Optional[datetime.datetime]: + """Process a date string, return a datetime object""" + if date_str is not None: + timetuple = parsedate(date_str) + if timetuple is not None: + with suppress(ValueError): + return datetime.datetime(*timetuple[:6], tzinfo=datetime.timezone.utc) + return None + + +@functools.lru_cache +def must_be_empty_body(method: str, code: int) -> bool: + """Check if a request must return an empty body.""" + return ( + code in EMPTY_BODY_STATUS_CODES + or method in EMPTY_BODY_METHODS + or (200 <= code < 300 and method in hdrs.METH_CONNECT_ALL) + ) + + +def should_remove_content_length(method: str, code: int) -> bool: + """Check if a Content-Length header should be removed. + + This should always be a subset of must_be_empty_body + """ + # https://www.rfc-editor.org/rfc/rfc9110.html#section-8.6-8 + # https://www.rfc-editor.org/rfc/rfc9110.html#section-15.4.5-4 + return code in EMPTY_BODY_STATUS_CODES or ( + 200 <= code < 300 and method in hdrs.METH_CONNECT_ALL + ) diff --git a/.venv/lib/python3.9/site-packages/aiohttp/http.py b/.venv/lib/python3.9/site-packages/aiohttp/http.py new file mode 100644 index 0000000..a1feae2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/http.py @@ -0,0 +1,72 @@ +import sys +from http import HTTPStatus +from typing import Mapping, Tuple + +from . import __version__ +from .http_exceptions import HttpProcessingError as HttpProcessingError +from .http_parser import ( + HeadersParser as HeadersParser, + HttpParser as HttpParser, + HttpRequestParser as HttpRequestParser, + HttpResponseParser as HttpResponseParser, + RawRequestMessage as RawRequestMessage, + RawResponseMessage as RawResponseMessage, +) +from .http_websocket import ( + WS_CLOSED_MESSAGE as WS_CLOSED_MESSAGE, + WS_CLOSING_MESSAGE as WS_CLOSING_MESSAGE, + WS_KEY as WS_KEY, + WebSocketError as WebSocketError, + WebSocketReader as WebSocketReader, + WebSocketWriter as WebSocketWriter, + WSCloseCode as WSCloseCode, + WSMessage as WSMessage, + WSMsgType as WSMsgType, + ws_ext_gen as ws_ext_gen, + ws_ext_parse as ws_ext_parse, +) +from .http_writer import ( + HttpVersion as HttpVersion, + HttpVersion10 as HttpVersion10, + HttpVersion11 as HttpVersion11, + StreamWriter as StreamWriter, +) + +__all__ = ( + "HttpProcessingError", + "RESPONSES", + "SERVER_SOFTWARE", + # .http_writer + "StreamWriter", + "HttpVersion", + "HttpVersion10", + "HttpVersion11", + # .http_parser + "HeadersParser", + "HttpParser", + "HttpRequestParser", + "HttpResponseParser", + "RawRequestMessage", + "RawResponseMessage", + # .http_websocket + "WS_CLOSED_MESSAGE", + "WS_CLOSING_MESSAGE", + "WS_KEY", + "WebSocketReader", + "WebSocketWriter", + "ws_ext_gen", + "ws_ext_parse", + "WSMessage", + "WebSocketError", + "WSMsgType", + "WSCloseCode", +) + + +SERVER_SOFTWARE: str = "Python/{0[0]}.{0[1]} aiohttp/{1}".format( + sys.version_info, __version__ +) + +RESPONSES: Mapping[int, Tuple[str, str]] = { + v: (v.phrase, v.description) for v in HTTPStatus.__members__.values() +} diff --git a/.venv/lib/python3.9/site-packages/aiohttp/http_exceptions.py b/.venv/lib/python3.9/site-packages/aiohttp/http_exceptions.py new file mode 100644 index 0000000..0b5867c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/http_exceptions.py @@ -0,0 +1,116 @@ +"""Low-level http related exceptions.""" + +from textwrap import indent +from typing import Optional, Union + +from .typedefs import _CIMultiDict + +__all__ = ("HttpProcessingError",) + + +class HttpProcessingError(Exception): + """HTTP error. + + Shortcut for raising HTTP errors with custom code, message and headers. + + code: HTTP Error code. + message: (optional) Error message. + headers: (optional) Headers to be sent in response, a list of pairs + """ + + code = 0 + message = "" + headers = None + + def __init__( + self, + *, + code: Optional[int] = None, + message: str = "", + headers: Optional[_CIMultiDict] = None, + ) -> None: + if code is not None: + self.code = code + self.headers = headers + self.message = message + + def __str__(self) -> str: + msg = indent(self.message, " ") + return f"{self.code}, message:\n{msg}" + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}: {self.code}, message={self.message!r}>" + + +class BadHttpMessage(HttpProcessingError): + + code = 400 + message = "Bad Request" + + def __init__(self, message: str, *, headers: Optional[_CIMultiDict] = None) -> None: + super().__init__(message=message, headers=headers) + self.args = (message,) + + +class HttpBadRequest(BadHttpMessage): + + code = 400 + message = "Bad Request" + + +class PayloadEncodingError(BadHttpMessage): + """Base class for payload errors""" + + +class ContentEncodingError(PayloadEncodingError): + """Content encoding error.""" + + +class TransferEncodingError(PayloadEncodingError): + """transfer encoding error.""" + + +class ContentLengthError(PayloadEncodingError): + """Not enough data to satisfy content length header.""" + + +class DecompressSizeError(PayloadEncodingError): + """Decompressed size exceeds the configured limit.""" + + +class LineTooLong(BadHttpMessage): + def __init__( + self, line: str, limit: str = "Unknown", actual_size: str = "Unknown" + ) -> None: + super().__init__( + f"Got more than {limit} bytes ({actual_size}) when reading {line}." + ) + self.args = (line, limit, actual_size) + + +class InvalidHeader(BadHttpMessage): + def __init__(self, hdr: Union[bytes, str]) -> None: + hdr_s = hdr.decode(errors="backslashreplace") if isinstance(hdr, bytes) else hdr + super().__init__(f"Invalid HTTP header: {hdr!r}") + self.hdr = hdr_s + self.args = (hdr,) + + +class BadStatusLine(BadHttpMessage): + def __init__(self, line: str = "", error: Optional[str] = None) -> None: + if not isinstance(line, str): + line = repr(line) + super().__init__(error or f"Bad status line {line!r}") + self.args = (line,) + self.line = line + + +class BadHttpMethod(BadStatusLine): + """Invalid HTTP method in status line.""" + + def __init__(self, line: str = "", error: Optional[str] = None) -> None: + super().__init__(line, error or f"Bad HTTP method in status line {line!r}") + + +class InvalidURLError(BadHttpMessage): + pass diff --git a/.venv/lib/python3.9/site-packages/aiohttp/http_parser.py b/.venv/lib/python3.9/site-packages/aiohttp/http_parser.py new file mode 100644 index 0000000..393e76a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/http_parser.py @@ -0,0 +1,1086 @@ +import abc +import asyncio +import re +import string +from contextlib import suppress +from enum import IntEnum +from typing import ( + Any, + ClassVar, + Final, + Generic, + List, + Literal, + NamedTuple, + Optional, + Pattern, + Set, + Tuple, + Type, + TypeVar, + Union, +) + +from multidict import CIMultiDict, CIMultiDictProxy, istr +from yarl import URL + +from . import hdrs +from .base_protocol import BaseProtocol +from .compression_utils import ( + DEFAULT_MAX_DECOMPRESS_SIZE, + HAS_BROTLI, + HAS_ZSTD, + BrotliDecompressor, + ZLibDecompressor, + ZSTDDecompressor, +) +from .helpers import ( + _EXC_SENTINEL, + DEBUG, + EMPTY_BODY_METHODS, + EMPTY_BODY_STATUS_CODES, + NO_EXTENSIONS, + BaseTimerContext, + set_exception, +) +from .http_exceptions import ( + BadHttpMessage, + BadHttpMethod, + BadStatusLine, + ContentEncodingError, + ContentLengthError, + DecompressSizeError, + InvalidHeader, + InvalidURLError, + LineTooLong, + TransferEncodingError, +) +from .http_writer import HttpVersion, HttpVersion10 +from .streams import EMPTY_PAYLOAD, StreamReader +from .typedefs import RawHeaders + +__all__ = ( + "HeadersParser", + "HttpParser", + "HttpRequestParser", + "HttpResponseParser", + "RawRequestMessage", + "RawResponseMessage", +) + +_SEP = Literal[b"\r\n", b"\n"] + +ASCIISET: Final[Set[str]] = set(string.printable) + +# See https://www.rfc-editor.org/rfc/rfc9110.html#name-overview +# and https://www.rfc-editor.org/rfc/rfc9110.html#name-tokens +# +# method = token +# tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / +# "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA +# token = 1*tchar +_TCHAR_SPECIALS: Final[str] = re.escape("!#$%&'*+-.^_`|~") +TOKENRE: Final[Pattern[str]] = re.compile(f"[0-9A-Za-z{_TCHAR_SPECIALS}]+") +VERSRE: Final[Pattern[str]] = re.compile(r"HTTP/(\d)\.(\d)", re.ASCII) +DIGITS: Final[Pattern[str]] = re.compile(r"\d+", re.ASCII) +HEXDIGITS: Final[Pattern[bytes]] = re.compile(rb"[0-9a-fA-F]+") + + +class RawRequestMessage(NamedTuple): + method: str + path: str + version: HttpVersion + headers: "CIMultiDictProxy[str]" + raw_headers: RawHeaders + should_close: bool + compression: Optional[str] + upgrade: bool + chunked: bool + url: URL + + +class RawResponseMessage(NamedTuple): + version: HttpVersion + code: int + reason: str + headers: CIMultiDictProxy[str] + raw_headers: RawHeaders + should_close: bool + compression: Optional[str] + upgrade: bool + chunked: bool + + +_MsgT = TypeVar("_MsgT", RawRequestMessage, RawResponseMessage) + + +class ParseState(IntEnum): + + PARSE_NONE = 0 + PARSE_LENGTH = 1 + PARSE_CHUNKED = 2 + PARSE_UNTIL_EOF = 3 + + +class ChunkState(IntEnum): + PARSE_CHUNKED_SIZE = 0 + PARSE_CHUNKED_CHUNK = 1 + PARSE_CHUNKED_CHUNK_EOF = 2 + PARSE_MAYBE_TRAILERS = 3 + PARSE_TRAILERS = 4 + + +class HeadersParser: + def __init__( + self, + max_line_size: int = 8190, + max_headers: int = 32768, + max_field_size: int = 8190, + lax: bool = False, + ) -> None: + self.max_line_size = max_line_size + self.max_headers = max_headers + self.max_field_size = max_field_size + self._lax = lax + + def parse_headers( + self, lines: List[bytes] + ) -> Tuple["CIMultiDictProxy[str]", RawHeaders]: + headers: CIMultiDict[str] = CIMultiDict() + # note: "raw" does not mean inclusion of OWS before/after the field value + raw_headers = [] + + lines_idx = 0 + line = lines[lines_idx] + line_count = len(lines) + + while line: + # Parse initial header name : value pair. + try: + bname, bvalue = line.split(b":", 1) + except ValueError: + raise InvalidHeader(line) from None + + if len(bname) == 0: + raise InvalidHeader(bname) + + # https://www.rfc-editor.org/rfc/rfc9112.html#section-5.1-2 + if {bname[0], bname[-1]} & {32, 9}: # {" ", "\t"} + raise InvalidHeader(line) + + bvalue = bvalue.lstrip(b" \t") + if len(bname) > self.max_field_size: + raise LineTooLong( + "request header name {}".format( + bname.decode("utf8", "backslashreplace") + ), + str(self.max_field_size), + str(len(bname)), + ) + name = bname.decode("utf-8", "surrogateescape") + if not TOKENRE.fullmatch(name): + raise InvalidHeader(bname) + + header_length = len(bvalue) + + # next line + lines_idx += 1 + line = lines[lines_idx] + + # consume continuation lines + continuation = self._lax and line and line[0] in (32, 9) # (' ', '\t') + + # Deprecated: https://www.rfc-editor.org/rfc/rfc9112.html#name-obsolete-line-folding + if continuation: + bvalue_lst = [bvalue] + while continuation: + header_length += len(line) + if header_length > self.max_field_size: + raise LineTooLong( + "request header field {}".format( + bname.decode("utf8", "backslashreplace") + ), + str(self.max_field_size), + str(header_length), + ) + bvalue_lst.append(line) + + # next line + lines_idx += 1 + if lines_idx < line_count: + line = lines[lines_idx] + if line: + continuation = line[0] in (32, 9) # (' ', '\t') + else: + line = b"" + break + bvalue = b"".join(bvalue_lst) + else: + if header_length > self.max_field_size: + raise LineTooLong( + "request header field {}".format( + bname.decode("utf8", "backslashreplace") + ), + str(self.max_field_size), + str(header_length), + ) + + bvalue = bvalue.strip(b" \t") + value = bvalue.decode("utf-8", "surrogateescape") + + # https://www.rfc-editor.org/rfc/rfc9110.html#section-5.5-5 + if "\n" in value or "\r" in value or "\x00" in value: + raise InvalidHeader(bvalue) + + headers.add(name, value) + raw_headers.append((bname, bvalue)) + + return (CIMultiDictProxy(headers), tuple(raw_headers)) + + +def _is_supported_upgrade(headers: CIMultiDictProxy[str]) -> bool: + """Check if the upgrade header is supported.""" + u = headers.get(hdrs.UPGRADE, "") + # .lower() can transform non-ascii characters. + return u.isascii() and u.lower() in {"tcp", "websocket"} + + +class HttpParser(abc.ABC, Generic[_MsgT]): + lax: ClassVar[bool] = False + + def __init__( + self, + protocol: Optional[BaseProtocol] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + limit: int = 2**16, + max_line_size: int = 8190, + max_headers: int = 32768, + max_field_size: int = 8190, + timer: Optional[BaseTimerContext] = None, + code: Optional[int] = None, + method: Optional[str] = None, + payload_exception: Optional[Type[BaseException]] = None, + response_with_body: bool = True, + read_until_eof: bool = False, + auto_decompress: bool = True, + ) -> None: + self.protocol = protocol + self.loop = loop + self.max_line_size = max_line_size + self.max_headers = max_headers + self.max_field_size = max_field_size + self.timer = timer + self.code = code + self.method = method + self.payload_exception = payload_exception + self.response_with_body = response_with_body + self.read_until_eof = read_until_eof + + self._lines: List[bytes] = [] + self._tail = b"" + self._upgraded = False + self._payload = None + self._payload_parser: Optional[HttpPayloadParser] = None + self._auto_decompress = auto_decompress + self._limit = limit + self._headers_parser = HeadersParser( + max_line_size, max_headers, max_field_size, self.lax + ) + + @abc.abstractmethod + def parse_message(self, lines: List[bytes]) -> _MsgT: ... + + @abc.abstractmethod + def _is_chunked_te(self, te: str) -> bool: ... + + def feed_eof(self) -> Optional[_MsgT]: + if self._payload_parser is not None: + self._payload_parser.feed_eof() + self._payload_parser = None + else: + # try to extract partial message + if self._tail: + self._lines.append(self._tail) + + if self._lines: + if self._lines[-1] != "\r\n": + self._lines.append(b"") + with suppress(Exception): + return self.parse_message(self._lines) + return None + + def feed_data( + self, + data: bytes, + SEP: _SEP = b"\r\n", + EMPTY: bytes = b"", + CONTENT_LENGTH: istr = hdrs.CONTENT_LENGTH, + METH_CONNECT: str = hdrs.METH_CONNECT, + SEC_WEBSOCKET_KEY1: istr = hdrs.SEC_WEBSOCKET_KEY1, + ) -> Tuple[List[Tuple[_MsgT, StreamReader]], bool, bytes]: + + messages = [] + + if self._tail: + data, self._tail = self._tail + data, b"" + + data_len = len(data) + start_pos = 0 + loop = self.loop + + should_close = False + while start_pos < data_len: + + # read HTTP message (request/response line + headers), \r\n\r\n + # and split by lines + if self._payload_parser is None and not self._upgraded: + pos = data.find(SEP, start_pos) + # consume \r\n + if pos == start_pos and not self._lines: + start_pos = pos + len(SEP) + continue + + if pos >= start_pos: + if should_close: + raise BadHttpMessage("Data after `Connection: close`") + + # line found + line = data[start_pos:pos] + if SEP == b"\n": # For lax response parsing + line = line.rstrip(b"\r") + self._lines.append(line) + start_pos = pos + len(SEP) + + # \r\n\r\n found + if self._lines[-1] == EMPTY: + try: + msg: _MsgT = self.parse_message(self._lines) + finally: + self._lines.clear() + + def get_content_length() -> Optional[int]: + # payload length + length_hdr = msg.headers.get(CONTENT_LENGTH) + if length_hdr is None: + return None + + # Shouldn't allow +/- or other number formats. + # https://www.rfc-editor.org/rfc/rfc9110#section-8.6-2 + # msg.headers is already stripped of leading/trailing wsp + if not DIGITS.fullmatch(length_hdr): + raise InvalidHeader(CONTENT_LENGTH) + + return int(length_hdr) + + length = get_content_length() + # do not support old websocket spec + if SEC_WEBSOCKET_KEY1 in msg.headers: + raise InvalidHeader(SEC_WEBSOCKET_KEY1) + + self._upgraded = msg.upgrade and _is_supported_upgrade( + msg.headers + ) + + method = getattr(msg, "method", self.method) + # code is only present on responses + code = getattr(msg, "code", 0) + + assert self.protocol is not None + # calculate payload + empty_body = code in EMPTY_BODY_STATUS_CODES or bool( + method and method in EMPTY_BODY_METHODS + ) + if not empty_body and ( + ((length is not None and length > 0) or msg.chunked) + and not self._upgraded + ): + payload = StreamReader( + self.protocol, + timer=self.timer, + loop=loop, + limit=self._limit, + ) + payload_parser = HttpPayloadParser( + payload, + length=length, + chunked=msg.chunked, + method=method, + compression=msg.compression, + code=self.code, + response_with_body=self.response_with_body, + auto_decompress=self._auto_decompress, + lax=self.lax, + headers_parser=self._headers_parser, + ) + if not payload_parser.done: + self._payload_parser = payload_parser + elif method == METH_CONNECT: + assert isinstance(msg, RawRequestMessage) + payload = StreamReader( + self.protocol, + timer=self.timer, + loop=loop, + limit=self._limit, + ) + self._upgraded = True + self._payload_parser = HttpPayloadParser( + payload, + method=msg.method, + compression=msg.compression, + auto_decompress=self._auto_decompress, + lax=self.lax, + headers_parser=self._headers_parser, + ) + elif not empty_body and length is None and self.read_until_eof: + payload = StreamReader( + self.protocol, + timer=self.timer, + loop=loop, + limit=self._limit, + ) + payload_parser = HttpPayloadParser( + payload, + length=length, + chunked=msg.chunked, + method=method, + compression=msg.compression, + code=self.code, + response_with_body=self.response_with_body, + auto_decompress=self._auto_decompress, + lax=self.lax, + headers_parser=self._headers_parser, + ) + if not payload_parser.done: + self._payload_parser = payload_parser + else: + payload = EMPTY_PAYLOAD + + messages.append((msg, payload)) + should_close = msg.should_close + else: + self._tail = data[start_pos:] + data = EMPTY + break + + # no parser, just store + elif self._payload_parser is None and self._upgraded: + assert not self._lines + break + + # feed payload + elif data and start_pos < data_len: + assert not self._lines + assert self._payload_parser is not None + try: + eof, data = self._payload_parser.feed_data(data[start_pos:], SEP) + except BaseException as underlying_exc: + reraised_exc = underlying_exc + if self.payload_exception is not None: + reraised_exc = self.payload_exception(str(underlying_exc)) + + set_exception( + self._payload_parser.payload, + reraised_exc, + underlying_exc, + ) + + eof = True + data = b"" + if isinstance( + underlying_exc, (InvalidHeader, TransferEncodingError) + ): + raise + + if eof: + start_pos = 0 + data_len = len(data) + self._payload_parser = None + continue + else: + break + + if data and start_pos < data_len: + data = data[start_pos:] + else: + data = EMPTY + + return messages, self._upgraded, data + + def parse_headers( + self, lines: List[bytes] + ) -> Tuple[ + "CIMultiDictProxy[str]", RawHeaders, Optional[bool], Optional[str], bool, bool + ]: + """Parses RFC 5322 headers from a stream. + + Line continuations are supported. Returns list of header name + and value pairs. Header name is in upper case. + """ + headers, raw_headers = self._headers_parser.parse_headers(lines) + close_conn = None + encoding = None + upgrade = False + chunked = False + + # https://www.rfc-editor.org/rfc/rfc9110.html#section-5.5-6 + # https://www.rfc-editor.org/rfc/rfc9110.html#name-collected-abnf + singletons = ( + hdrs.CONTENT_LENGTH, + hdrs.CONTENT_LOCATION, + hdrs.CONTENT_RANGE, + hdrs.CONTENT_TYPE, + hdrs.ETAG, + hdrs.HOST, + hdrs.MAX_FORWARDS, + hdrs.SERVER, + hdrs.TRANSFER_ENCODING, + hdrs.USER_AGENT, + ) + bad_hdr = next((h for h in singletons if len(headers.getall(h, ())) > 1), None) + if bad_hdr is not None: + raise BadHttpMessage(f"Duplicate '{bad_hdr}' header found.") + + # keep-alive + conn = headers.get(hdrs.CONNECTION) + if conn: + v = conn.lower() + if v == "close": + close_conn = True + elif v == "keep-alive": + close_conn = False + # https://www.rfc-editor.org/rfc/rfc9110.html#name-101-switching-protocols + elif v == "upgrade" and headers.get(hdrs.UPGRADE): + upgrade = True + + # encoding + enc = headers.get(hdrs.CONTENT_ENCODING, "") + if enc.isascii() and enc.lower() in {"gzip", "deflate", "br", "zstd"}: + encoding = enc + + # chunking + te = headers.get(hdrs.TRANSFER_ENCODING) + if te is not None: + if self._is_chunked_te(te): + chunked = True + + if hdrs.CONTENT_LENGTH in headers: + raise BadHttpMessage( + "Transfer-Encoding can't be present with Content-Length", + ) + + return (headers, raw_headers, close_conn, encoding, upgrade, chunked) + + def set_upgraded(self, val: bool) -> None: + """Set connection upgraded (to websocket) mode. + + :param bool val: new state. + """ + self._upgraded = val + + +class HttpRequestParser(HttpParser[RawRequestMessage]): + """Read request status line. + + Exception .http_exceptions.BadStatusLine + could be raised in case of any errors in status line. + Returns RawRequestMessage. + """ + + def parse_message(self, lines: List[bytes]) -> RawRequestMessage: + # request line + line = lines[0].decode("utf-8", "surrogateescape") + try: + method, path, version = line.split(" ", maxsplit=2) + except ValueError: + raise BadHttpMethod(line) from None + + if len(path) > self.max_line_size: + raise LineTooLong( + "Status line is too long", str(self.max_line_size), str(len(path)) + ) + + # method + if not TOKENRE.fullmatch(method): + raise BadHttpMethod(method) + + # version + match = VERSRE.fullmatch(version) + if match is None: + raise BadStatusLine(line) + version_o = HttpVersion(int(match.group(1)), int(match.group(2))) + + if method == "CONNECT": + # authority-form, + # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.3 + url = URL.build(authority=path, encoded=True) + elif path.startswith("/"): + # origin-form, + # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.1 + path_part, _hash_separator, url_fragment = path.partition("#") + path_part, _question_mark_separator, qs_part = path_part.partition("?") + + # NOTE: `yarl.URL.build()` is used to mimic what the Cython-based + # NOTE: parser does, otherwise it results into the same + # NOTE: HTTP Request-Line input producing different + # NOTE: `yarl.URL()` objects + url = URL.build( + path=path_part, + query_string=qs_part, + fragment=url_fragment, + encoded=True, + ) + elif path == "*" and method == "OPTIONS": + # asterisk-form, + url = URL(path, encoded=True) + else: + # absolute-form for proxy maybe, + # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.2 + url = URL(path, encoded=True) + if url.scheme == "": + # not absolute-form + raise InvalidURLError( + path.encode(errors="surrogateescape").decode("latin1") + ) + + # read headers + ( + headers, + raw_headers, + close, + compression, + upgrade, + chunked, + ) = self.parse_headers(lines[1:]) + + if close is None: # then the headers weren't set in the request + if version_o <= HttpVersion10: # HTTP 1.0 must asks to not close + close = True + else: # HTTP 1.1 must ask to close. + close = False + + return RawRequestMessage( + method, + path, + version_o, + headers, + raw_headers, + close, + compression, + upgrade, + chunked, + url, + ) + + def _is_chunked_te(self, te: str) -> bool: + te = te.rsplit(",", maxsplit=1)[-1].strip(" \t") + # .lower() transforms some non-ascii chars, so must check first. + if te.isascii() and te.lower() == "chunked": + return True + # https://www.rfc-editor.org/rfc/rfc9112#section-6.3-2.4.3 + raise BadHttpMessage("Request has invalid `Transfer-Encoding`") + + +class HttpResponseParser(HttpParser[RawResponseMessage]): + """Read response status line and headers. + + BadStatusLine could be raised in case of any errors in status line. + Returns RawResponseMessage. + """ + + # Lax mode should only be enabled on response parser. + lax = not DEBUG + + def feed_data( + self, + data: bytes, + SEP: Optional[_SEP] = None, + *args: Any, + **kwargs: Any, + ) -> Tuple[List[Tuple[RawResponseMessage, StreamReader]], bool, bytes]: + if SEP is None: + SEP = b"\r\n" if DEBUG else b"\n" + return super().feed_data(data, SEP, *args, **kwargs) + + def parse_message(self, lines: List[bytes]) -> RawResponseMessage: + line = lines[0].decode("utf-8", "surrogateescape") + try: + version, status = line.split(maxsplit=1) + except ValueError: + raise BadStatusLine(line) from None + + try: + status, reason = status.split(maxsplit=1) + except ValueError: + status = status.strip() + reason = "" + + if len(reason) > self.max_line_size: + raise LineTooLong( + "Status line is too long", str(self.max_line_size), str(len(reason)) + ) + + # version + match = VERSRE.fullmatch(version) + if match is None: + raise BadStatusLine(line) + version_o = HttpVersion(int(match.group(1)), int(match.group(2))) + + # The status code is a three-digit ASCII number, no padding + if len(status) != 3 or not DIGITS.fullmatch(status): + raise BadStatusLine(line) + status_i = int(status) + + # read headers + ( + headers, + raw_headers, + close, + compression, + upgrade, + chunked, + ) = self.parse_headers(lines[1:]) + + if close is None: + if version_o <= HttpVersion10: + close = True + # https://www.rfc-editor.org/rfc/rfc9112.html#name-message-body-length + elif 100 <= status_i < 200 or status_i in {204, 304}: + close = False + elif hdrs.CONTENT_LENGTH in headers or hdrs.TRANSFER_ENCODING in headers: + close = False + else: + # https://www.rfc-editor.org/rfc/rfc9112.html#section-6.3-2.8 + close = True + + return RawResponseMessage( + version_o, + status_i, + reason.strip(), + headers, + raw_headers, + close, + compression, + upgrade, + chunked, + ) + + def _is_chunked_te(self, te: str) -> bool: + # https://www.rfc-editor.org/rfc/rfc9112#section-6.3-2.4.2 + return te.rsplit(",", maxsplit=1)[-1].strip(" \t").lower() == "chunked" + + +class HttpPayloadParser: + def __init__( + self, + payload: StreamReader, + length: Optional[int] = None, + chunked: bool = False, + compression: Optional[str] = None, + code: Optional[int] = None, + method: Optional[str] = None, + response_with_body: bool = True, + auto_decompress: bool = True, + lax: bool = False, + *, + headers_parser: HeadersParser, + ) -> None: + self._length = 0 + self._type = ParseState.PARSE_UNTIL_EOF + self._chunk = ChunkState.PARSE_CHUNKED_SIZE + self._chunk_size = 0 + self._chunk_tail = b"" + self._auto_decompress = auto_decompress + self._lax = lax + self._headers_parser = headers_parser + self._trailer_lines: list[bytes] = [] + self.done = False + + # payload decompression wrapper + if response_with_body and compression and self._auto_decompress: + real_payload: Union[StreamReader, DeflateBuffer] = DeflateBuffer( + payload, compression + ) + else: + real_payload = payload + + # payload parser + if not response_with_body: + # don't parse payload if it's not expected to be received + self._type = ParseState.PARSE_NONE + real_payload.feed_eof() + self.done = True + elif chunked: + self._type = ParseState.PARSE_CHUNKED + elif length is not None: + self._type = ParseState.PARSE_LENGTH + self._length = length + if self._length == 0: + real_payload.feed_eof() + self.done = True + + self.payload = real_payload + + def feed_eof(self) -> None: + if self._type == ParseState.PARSE_UNTIL_EOF: + self.payload.feed_eof() + elif self._type == ParseState.PARSE_LENGTH: + raise ContentLengthError( + "Not enough data to satisfy content length header." + ) + elif self._type == ParseState.PARSE_CHUNKED: + raise TransferEncodingError( + "Not enough data to satisfy transfer length header." + ) + + def feed_data( + self, chunk: bytes, SEP: _SEP = b"\r\n", CHUNK_EXT: bytes = b";" + ) -> Tuple[bool, bytes]: + # Read specified amount of bytes + if self._type == ParseState.PARSE_LENGTH: + required = self._length + chunk_len = len(chunk) + + if required >= chunk_len: + self._length = required - chunk_len + self.payload.feed_data(chunk, chunk_len) + if self._length == 0: + self.payload.feed_eof() + return True, b"" + else: + self._length = 0 + self.payload.feed_data(chunk[:required], required) + self.payload.feed_eof() + return True, chunk[required:] + + # Chunked transfer encoding parser + elif self._type == ParseState.PARSE_CHUNKED: + if self._chunk_tail: + chunk = self._chunk_tail + chunk + self._chunk_tail = b"" + + while chunk: + + # read next chunk size + if self._chunk == ChunkState.PARSE_CHUNKED_SIZE: + pos = chunk.find(SEP) + if pos >= 0: + i = chunk.find(CHUNK_EXT, 0, pos) + if i >= 0: + size_b = chunk[:i] # strip chunk-extensions + # Verify no LF in the chunk-extension + if b"\n" in (ext := chunk[i:pos]): + exc = TransferEncodingError( + f"Unexpected LF in chunk-extension: {ext!r}" + ) + set_exception(self.payload, exc) + raise exc + else: + size_b = chunk[:pos] + + if self._lax: # Allow whitespace in lax mode. + size_b = size_b.strip() + + if not re.fullmatch(HEXDIGITS, size_b): + exc = TransferEncodingError( + chunk[:pos].decode("ascii", "surrogateescape") + ) + set_exception(self.payload, exc) + raise exc + size = int(bytes(size_b), 16) + + chunk = chunk[pos + len(SEP) :] + if size == 0: # eof marker + self._chunk = ChunkState.PARSE_TRAILERS + if self._lax and chunk.startswith(b"\r"): + chunk = chunk[1:] + else: + self._chunk = ChunkState.PARSE_CHUNKED_CHUNK + self._chunk_size = size + self.payload.begin_http_chunk_receiving() + else: + self._chunk_tail = chunk + return False, b"" + + # read chunk and feed buffer + if self._chunk == ChunkState.PARSE_CHUNKED_CHUNK: + required = self._chunk_size + chunk_len = len(chunk) + + if required > chunk_len: + self._chunk_size = required - chunk_len + self.payload.feed_data(chunk, chunk_len) + return False, b"" + else: + self._chunk_size = 0 + self.payload.feed_data(chunk[:required], required) + chunk = chunk[required:] + self._chunk = ChunkState.PARSE_CHUNKED_CHUNK_EOF + self.payload.end_http_chunk_receiving() + + # toss the CRLF at the end of the chunk + if self._chunk == ChunkState.PARSE_CHUNKED_CHUNK_EOF: + if self._lax and chunk.startswith(b"\r"): + chunk = chunk[1:] + if chunk[: len(SEP)] == SEP: + chunk = chunk[len(SEP) :] + self._chunk = ChunkState.PARSE_CHUNKED_SIZE + else: + self._chunk_tail = chunk + return False, b"" + + if self._chunk == ChunkState.PARSE_TRAILERS: + pos = chunk.find(SEP) + if pos < 0: # No line found + self._chunk_tail = chunk + return False, b"" + + line = chunk[:pos] + chunk = chunk[pos + len(SEP) :] + if SEP == b"\n": # For lax response parsing + line = line.rstrip(b"\r") + self._trailer_lines.append(line) + + # \r\n\r\n found, end of stream + if self._trailer_lines[-1] == b"": + # Headers and trailers are defined the same way, + # so we reuse the HeadersParser here. + try: + trailers, raw_trailers = self._headers_parser.parse_headers( + self._trailer_lines + ) + finally: + self._trailer_lines.clear() + self.payload.feed_eof() + return True, chunk + + # Read all bytes until eof + elif self._type == ParseState.PARSE_UNTIL_EOF: + self.payload.feed_data(chunk, len(chunk)) + + return False, b"" + + +class DeflateBuffer: + """DeflateStream decompress stream and feed data into specified stream.""" + + decompressor: Any + + def __init__( + self, + out: StreamReader, + encoding: Optional[str], + max_decompress_size: int = DEFAULT_MAX_DECOMPRESS_SIZE, + ) -> None: + self.out = out + self.size = 0 + out.total_compressed_bytes = self.size + self.encoding = encoding + self._started_decoding = False + + self.decompressor: Union[BrotliDecompressor, ZLibDecompressor, ZSTDDecompressor] + if encoding == "br": + if not HAS_BROTLI: # pragma: no cover + raise ContentEncodingError( + "Can not decode content-encoding: brotli (br). " + "Please install `Brotli`" + ) + self.decompressor = BrotliDecompressor() + elif encoding == "zstd": + if not HAS_ZSTD: + raise ContentEncodingError( + "Can not decode content-encoding: zstandard (zstd). " + "Please install `backports.zstd`" + ) + self.decompressor = ZSTDDecompressor() + else: + self.decompressor = ZLibDecompressor(encoding=encoding) + + self._max_decompress_size = max_decompress_size + + def set_exception( + self, + exc: BaseException, + exc_cause: BaseException = _EXC_SENTINEL, + ) -> None: + set_exception(self.out, exc, exc_cause) + + def feed_data(self, chunk: bytes, size: int) -> None: + if not size: + return + + self.size += size + self.out.total_compressed_bytes = self.size + + # RFC1950 + # bits 0..3 = CM = 0b1000 = 8 = "deflate" + # bits 4..7 = CINFO = 1..7 = windows size. + if ( + not self._started_decoding + and self.encoding == "deflate" + and chunk[0] & 0xF != 8 + ): + # Change the decoder to decompress incorrectly compressed data + # Actually we should issue a warning about non-RFC-compliant data. + self.decompressor = ZLibDecompressor( + encoding=self.encoding, suppress_deflate_header=True + ) + + try: + # Decompress with limit + 1 so we can detect if output exceeds limit + chunk = self.decompressor.decompress_sync( + chunk, max_length=self._max_decompress_size + 1 + ) + except Exception: + raise ContentEncodingError( + "Can not decode content-encoding: %s" % self.encoding + ) + + self._started_decoding = True + + # Check if decompression limit was exceeded + if len(chunk) > self._max_decompress_size: + raise DecompressSizeError( + "Decompressed data exceeds the configured limit of %d bytes" + % self._max_decompress_size + ) + + if chunk: + self.out.feed_data(chunk, len(chunk)) + + def feed_eof(self) -> None: + chunk = self.decompressor.flush() + + if chunk or self.size > 0: + self.out.feed_data(chunk, len(chunk)) + if self.encoding == "deflate" and not self.decompressor.eof: + raise ContentEncodingError("deflate") + + self.out.feed_eof() + + def begin_http_chunk_receiving(self) -> None: + self.out.begin_http_chunk_receiving() + + def end_http_chunk_receiving(self) -> None: + self.out.end_http_chunk_receiving() + + +HttpRequestParserPy = HttpRequestParser +HttpResponseParserPy = HttpResponseParser +RawRequestMessagePy = RawRequestMessage +RawResponseMessagePy = RawResponseMessage + +try: + if not NO_EXTENSIONS: + from ._http_parser import ( # type: ignore[import-not-found,no-redef] + HttpRequestParser, + HttpResponseParser, + RawRequestMessage, + RawResponseMessage, + ) + + HttpRequestParserC = HttpRequestParser + HttpResponseParserC = HttpResponseParser + RawRequestMessageC = RawRequestMessage + RawResponseMessageC = RawResponseMessage +except ImportError: # pragma: no cover + pass diff --git a/.venv/lib/python3.9/site-packages/aiohttp/http_websocket.py b/.venv/lib/python3.9/site-packages/aiohttp/http_websocket.py new file mode 100644 index 0000000..6b4b30e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/http_websocket.py @@ -0,0 +1,36 @@ +"""WebSocket protocol versions 13 and 8.""" + +from ._websocket.helpers import WS_KEY, ws_ext_gen, ws_ext_parse +from ._websocket.models import ( + WS_CLOSED_MESSAGE, + WS_CLOSING_MESSAGE, + WebSocketError, + WSCloseCode, + WSHandshakeError, + WSMessage, + WSMsgType, +) +from ._websocket.reader import WebSocketReader +from ._websocket.writer import WebSocketWriter + +# Messages that the WebSocketResponse.receive needs to handle internally +_INTERNAL_RECEIVE_TYPES = frozenset( + (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.PING, WSMsgType.PONG) +) + + +__all__ = ( + "WS_CLOSED_MESSAGE", + "WS_CLOSING_MESSAGE", + "WS_KEY", + "WebSocketReader", + "WebSocketWriter", + "WSMessage", + "WebSocketError", + "WSMsgType", + "WSCloseCode", + "ws_ext_gen", + "ws_ext_parse", + "WSHandshakeError", + "WSMessage", +) diff --git a/.venv/lib/python3.9/site-packages/aiohttp/http_writer.py b/.venv/lib/python3.9/site-packages/aiohttp/http_writer.py new file mode 100644 index 0000000..a140b21 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/http_writer.py @@ -0,0 +1,378 @@ +"""Http related parsers and protocol.""" + +import asyncio +import sys +from typing import ( # noqa + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Iterable, + List, + NamedTuple, + Optional, + Union, +) + +from multidict import CIMultiDict + +from .abc import AbstractStreamWriter +from .base_protocol import BaseProtocol +from .client_exceptions import ClientConnectionResetError +from .compression_utils import ZLibCompressor +from .helpers import NO_EXTENSIONS + +__all__ = ("StreamWriter", "HttpVersion", "HttpVersion10", "HttpVersion11") + + +MIN_PAYLOAD_FOR_WRITELINES = 2048 +IS_PY313_BEFORE_313_2 = (3, 13, 0) <= sys.version_info < (3, 13, 2) +IS_PY_BEFORE_312_9 = sys.version_info < (3, 12, 9) +SKIP_WRITELINES = IS_PY313_BEFORE_313_2 or IS_PY_BEFORE_312_9 +# writelines is not safe for use +# on Python 3.12+ until 3.12.9 +# on Python 3.13+ until 3.13.2 +# and on older versions it not any faster than write +# CVE-2024-12254: https://github.com/python/cpython/pull/127656 + + +class HttpVersion(NamedTuple): + major: int + minor: int + + +HttpVersion10 = HttpVersion(1, 0) +HttpVersion11 = HttpVersion(1, 1) + + +_T_OnChunkSent = Optional[Callable[[bytes], Awaitable[None]]] +_T_OnHeadersSent = Optional[Callable[["CIMultiDict[str]"], Awaitable[None]]] + + +class StreamWriter(AbstractStreamWriter): + + length: Optional[int] = None + chunked: bool = False + _eof: bool = False + _compress: Optional[ZLibCompressor] = None + + def __init__( + self, + protocol: BaseProtocol, + loop: asyncio.AbstractEventLoop, + on_chunk_sent: _T_OnChunkSent = None, + on_headers_sent: _T_OnHeadersSent = None, + ) -> None: + self._protocol = protocol + self.loop = loop + self._on_chunk_sent: _T_OnChunkSent = on_chunk_sent + self._on_headers_sent: _T_OnHeadersSent = on_headers_sent + self._headers_buf: Optional[bytes] = None + self._headers_written: bool = False + + @property + def transport(self) -> Optional[asyncio.Transport]: + return self._protocol.transport + + @property + def protocol(self) -> BaseProtocol: + return self._protocol + + def enable_chunking(self) -> None: + self.chunked = True + + def enable_compression( + self, encoding: str = "deflate", strategy: Optional[int] = None + ) -> None: + self._compress = ZLibCompressor(encoding=encoding, strategy=strategy) + + def _write(self, chunk: Union[bytes, bytearray, memoryview]) -> None: + size = len(chunk) + self.buffer_size += size + self.output_size += size + transport = self._protocol.transport + if transport is None or transport.is_closing(): + raise ClientConnectionResetError("Cannot write to closing transport") + transport.write(chunk) + + def _writelines(self, chunks: Iterable[bytes]) -> None: + size = 0 + for chunk in chunks: + size += len(chunk) + self.buffer_size += size + self.output_size += size + transport = self._protocol.transport + if transport is None or transport.is_closing(): + raise ClientConnectionResetError("Cannot write to closing transport") + if SKIP_WRITELINES or size < MIN_PAYLOAD_FOR_WRITELINES: + transport.write(b"".join(chunks)) + else: + transport.writelines(chunks) + + def _write_chunked_payload( + self, chunk: Union[bytes, bytearray, "memoryview[int]", "memoryview[bytes]"] + ) -> None: + """Write a chunk with proper chunked encoding.""" + chunk_len_pre = f"{len(chunk):x}\r\n".encode("ascii") + self._writelines((chunk_len_pre, chunk, b"\r\n")) + + def _send_headers_with_payload( + self, + chunk: Union[bytes, bytearray, "memoryview[int]", "memoryview[bytes]"], + is_eof: bool, + ) -> None: + """Send buffered headers with payload, coalescing into single write.""" + # Mark headers as written + self._headers_written = True + headers_buf = self._headers_buf + self._headers_buf = None + + if TYPE_CHECKING: + # Safe because callers (write() and write_eof()) only invoke this method + # after checking that self._headers_buf is truthy + assert headers_buf is not None + + if not self.chunked: + # Non-chunked: coalesce headers with body + if chunk: + self._writelines((headers_buf, chunk)) + else: + self._write(headers_buf) + return + + # Coalesce headers with chunked data + if chunk: + chunk_len_pre = f"{len(chunk):x}\r\n".encode("ascii") + if is_eof: + self._writelines((headers_buf, chunk_len_pre, chunk, b"\r\n0\r\n\r\n")) + else: + self._writelines((headers_buf, chunk_len_pre, chunk, b"\r\n")) + elif is_eof: + self._writelines((headers_buf, b"0\r\n\r\n")) + else: + self._write(headers_buf) + + async def write( + self, + chunk: Union[bytes, bytearray, memoryview], + *, + drain: bool = True, + LIMIT: int = 0x10000, + ) -> None: + """ + Writes chunk of data to a stream. + + write_eof() indicates end of stream. + writer can't be used after write_eof() method being called. + write() return drain future. + """ + if self._on_chunk_sent is not None: + await self._on_chunk_sent(chunk) + + if isinstance(chunk, memoryview): + if chunk.nbytes != len(chunk): + # just reshape it + chunk = chunk.cast("c") + + if self._compress is not None: + chunk = await self._compress.compress(chunk) + if not chunk: + return + + if self.length is not None: + chunk_len = len(chunk) + if self.length >= chunk_len: + self.length = self.length - chunk_len + else: + chunk = chunk[: self.length] + self.length = 0 + if not chunk: + return + + # Handle buffered headers for small payload optimization + if self._headers_buf and not self._headers_written: + self._send_headers_with_payload(chunk, False) + if drain and self.buffer_size > LIMIT: + self.buffer_size = 0 + await self.drain() + return + + if chunk: + if self.chunked: + self._write_chunked_payload(chunk) + else: + self._write(chunk) + + if drain and self.buffer_size > LIMIT: + self.buffer_size = 0 + await self.drain() + + async def write_headers( + self, status_line: str, headers: "CIMultiDict[str]" + ) -> None: + """Write headers to the stream.""" + if self._on_headers_sent is not None: + await self._on_headers_sent(headers) + # status + headers + buf = _serialize_headers(status_line, headers) + self._headers_written = False + self._headers_buf = buf + + def send_headers(self) -> None: + """Force sending buffered headers if not already sent.""" + if not self._headers_buf or self._headers_written: + return + + self._headers_written = True + headers_buf = self._headers_buf + self._headers_buf = None + + if TYPE_CHECKING: + # Safe because we only enter this block when self._headers_buf is truthy + assert headers_buf is not None + + self._write(headers_buf) + + def set_eof(self) -> None: + """Indicate that the message is complete.""" + if self._eof: + return + + # If headers haven't been sent yet, send them now + # This handles the case where there's no body at all + if self._headers_buf and not self._headers_written: + self._headers_written = True + headers_buf = self._headers_buf + self._headers_buf = None + + if TYPE_CHECKING: + # Safe because we only enter this block when self._headers_buf is truthy + assert headers_buf is not None + + # Combine headers and chunked EOF marker in a single write + if self.chunked: + self._writelines((headers_buf, b"0\r\n\r\n")) + else: + self._write(headers_buf) + elif self.chunked and self._headers_written: + # Headers already sent, just send the final chunk marker + self._write(b"0\r\n\r\n") + + self._eof = True + + async def write_eof(self, chunk: bytes = b"") -> None: + if self._eof: + return + + if chunk and self._on_chunk_sent is not None: + await self._on_chunk_sent(chunk) + + # Handle body/compression + if self._compress: + chunks: List[bytes] = [] + chunks_len = 0 + if chunk and (compressed_chunk := await self._compress.compress(chunk)): + chunks_len = len(compressed_chunk) + chunks.append(compressed_chunk) + + flush_chunk = self._compress.flush() + chunks_len += len(flush_chunk) + chunks.append(flush_chunk) + assert chunks_len + + # Send buffered headers with compressed data if not yet sent + if self._headers_buf and not self._headers_written: + self._headers_written = True + headers_buf = self._headers_buf + self._headers_buf = None + + if self.chunked: + # Coalesce headers with compressed chunked data + chunk_len_pre = f"{chunks_len:x}\r\n".encode("ascii") + self._writelines( + (headers_buf, chunk_len_pre, *chunks, b"\r\n0\r\n\r\n") + ) + else: + # Coalesce headers with compressed data + self._writelines((headers_buf, *chunks)) + await self.drain() + self._eof = True + return + + # Headers already sent, just write compressed data + if self.chunked: + chunk_len_pre = f"{chunks_len:x}\r\n".encode("ascii") + self._writelines((chunk_len_pre, *chunks, b"\r\n0\r\n\r\n")) + elif len(chunks) > 1: + self._writelines(chunks) + else: + self._write(chunks[0]) + await self.drain() + self._eof = True + return + + # No compression - send buffered headers if not yet sent + if self._headers_buf and not self._headers_written: + # Use helper to send headers with payload + self._send_headers_with_payload(chunk, True) + await self.drain() + self._eof = True + return + + # Handle remaining body + if self.chunked: + if chunk: + # Write final chunk with EOF marker + self._writelines( + (f"{len(chunk):x}\r\n".encode("ascii"), chunk, b"\r\n0\r\n\r\n") + ) + else: + self._write(b"0\r\n\r\n") + await self.drain() + self._eof = True + return + + if chunk: + self._write(chunk) + await self.drain() + + self._eof = True + + async def drain(self) -> None: + """Flush the write buffer. + + The intended use is to write + + await w.write(data) + await w.drain() + """ + protocol = self._protocol + if protocol.transport is not None and protocol._paused: + await protocol._drain_helper() + + +def _safe_header(string: str) -> str: + if "\r" in string or "\n" in string: + raise ValueError( + "Newline or carriage return detected in headers. " + "Potential header injection attack." + ) + return string + + +def _py_serialize_headers(status_line: str, headers: "CIMultiDict[str]") -> bytes: + headers_gen = (_safe_header(k) + ": " + _safe_header(v) for k, v in headers.items()) + line = status_line + "\r\n" + "\r\n".join(headers_gen) + "\r\n\r\n" + return line.encode("utf-8") + + +_serialize_headers = _py_serialize_headers + +try: + import aiohttp._http_writer as _http_writer # type: ignore[import-not-found] + + _c_serialize_headers = _http_writer._serialize_headers + if not NO_EXTENSIONS: + _serialize_headers = _c_serialize_headers +except ImportError: + pass diff --git a/.venv/lib/python3.9/site-packages/aiohttp/log.py b/.venv/lib/python3.9/site-packages/aiohttp/log.py new file mode 100644 index 0000000..3cecea2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/log.py @@ -0,0 +1,8 @@ +import logging + +access_logger = logging.getLogger("aiohttp.access") +client_logger = logging.getLogger("aiohttp.client") +internal_logger = logging.getLogger("aiohttp.internal") +server_logger = logging.getLogger("aiohttp.server") +web_logger = logging.getLogger("aiohttp.web") +ws_logger = logging.getLogger("aiohttp.websocket") diff --git a/.venv/lib/python3.9/site-packages/aiohttp/multipart.py b/.venv/lib/python3.9/site-packages/aiohttp/multipart.py new file mode 100644 index 0000000..9c37f0b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/multipart.py @@ -0,0 +1,1152 @@ +import base64 +import binascii +import json +import re +import sys +import uuid +import warnings +from collections import deque +from collections.abc import Mapping, Sequence +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + Deque, + Dict, + Iterator, + List, + Optional, + Tuple, + Type, + Union, + cast, +) +from urllib.parse import parse_qsl, unquote, urlencode + +from multidict import CIMultiDict, CIMultiDictProxy + +from .abc import AbstractStreamWriter +from .compression_utils import ( + DEFAULT_MAX_DECOMPRESS_SIZE, + ZLibCompressor, + ZLibDecompressor, +) +from .hdrs import ( + CONTENT_DISPOSITION, + CONTENT_ENCODING, + CONTENT_LENGTH, + CONTENT_TRANSFER_ENCODING, + CONTENT_TYPE, +) +from .helpers import CHAR, TOKEN, parse_mimetype, reify +from .http import HeadersParser +from .log import internal_logger +from .payload import ( + JsonPayload, + LookupError, + Order, + Payload, + StringPayload, + get_payload, + payload_type, +) +from .streams import StreamReader + +if sys.version_info >= (3, 11): + from typing import Self +else: + from typing import TypeVar + + Self = TypeVar("Self", bound="BodyPartReader") + +__all__ = ( + "MultipartReader", + "MultipartWriter", + "BodyPartReader", + "BadContentDispositionHeader", + "BadContentDispositionParam", + "parse_content_disposition", + "content_disposition_filename", +) + + +if TYPE_CHECKING: + from .client_reqrep import ClientResponse + + +class BadContentDispositionHeader(RuntimeWarning): + pass + + +class BadContentDispositionParam(RuntimeWarning): + pass + + +def parse_content_disposition( + header: Optional[str], +) -> Tuple[Optional[str], Dict[str, str]]: + def is_token(string: str) -> bool: + return bool(string) and TOKEN >= set(string) + + def is_quoted(string: str) -> bool: + return string[0] == string[-1] == '"' + + def is_rfc5987(string: str) -> bool: + return is_token(string) and string.count("'") == 2 + + def is_extended_param(string: str) -> bool: + return string.endswith("*") + + def is_continuous_param(string: str) -> bool: + pos = string.find("*") + 1 + if not pos: + return False + substring = string[pos:-1] if string.endswith("*") else string[pos:] + return substring.isdigit() + + def unescape(text: str, *, chars: str = "".join(map(re.escape, CHAR))) -> str: + return re.sub(f"\\\\([{chars}])", "\\1", text) + + if not header: + return None, {} + + disptype, *parts = header.split(";") + if not is_token(disptype): + warnings.warn(BadContentDispositionHeader(header)) + return None, {} + + params: Dict[str, str] = {} + while parts: + item = parts.pop(0) + + if not item: # To handle trailing semicolons + warnings.warn(BadContentDispositionHeader(header)) + continue + + if "=" not in item: + warnings.warn(BadContentDispositionHeader(header)) + return None, {} + + key, value = item.split("=", 1) + key = key.lower().strip() + value = value.lstrip() + + if key in params: + warnings.warn(BadContentDispositionHeader(header)) + return None, {} + + if not is_token(key): + warnings.warn(BadContentDispositionParam(item)) + continue + + elif is_continuous_param(key): + if is_quoted(value): + value = unescape(value[1:-1]) + elif not is_token(value): + warnings.warn(BadContentDispositionParam(item)) + continue + + elif is_extended_param(key): + if is_rfc5987(value): + encoding, _, value = value.split("'", 2) + encoding = encoding or "utf-8" + else: + warnings.warn(BadContentDispositionParam(item)) + continue + + try: + value = unquote(value, encoding, "strict") + except UnicodeDecodeError: # pragma: nocover + warnings.warn(BadContentDispositionParam(item)) + continue + + else: + failed = True + if is_quoted(value): + failed = False + value = unescape(value[1:-1].lstrip("\\/")) + elif is_token(value): + failed = False + elif parts: + # maybe just ; in filename, in any case this is just + # one case fix, for proper fix we need to redesign parser + _value = f"{value};{parts[0]}" + if is_quoted(_value): + parts.pop(0) + value = unescape(_value[1:-1].lstrip("\\/")) + failed = False + + if failed: + warnings.warn(BadContentDispositionHeader(header)) + return None, {} + + params[key] = value + + return disptype.lower(), params + + +def content_disposition_filename( + params: Mapping[str, str], name: str = "filename" +) -> Optional[str]: + name_suf = "%s*" % name + if not params: + return None + elif name_suf in params: + return params[name_suf] + elif name in params: + return params[name] + else: + parts = [] + fnparams = sorted( + (key, value) for key, value in params.items() if key.startswith(name_suf) + ) + for num, (key, value) in enumerate(fnparams): + _, tail = key.split("*", 1) + if tail.endswith("*"): + tail = tail[:-1] + if tail == str(num): + parts.append(value) + else: + break + if not parts: + return None + value = "".join(parts) + if "'" in value: + encoding, _, value = value.split("'", 2) + encoding = encoding or "utf-8" + return unquote(value, encoding, "strict") + return value + + +class MultipartResponseWrapper: + """Wrapper around the MultipartReader. + + It takes care about + underlying connection and close it when it needs in. + """ + + def __init__( + self, + resp: "ClientResponse", + stream: "MultipartReader", + ) -> None: + self.resp = resp + self.stream = stream + + def __aiter__(self) -> "MultipartResponseWrapper": + return self + + async def __anext__( + self, + ) -> Union["MultipartReader", "BodyPartReader"]: + part = await self.next() + if part is None: + raise StopAsyncIteration + return part + + def at_eof(self) -> bool: + """Returns True when all response data had been read.""" + return self.resp.content.at_eof() + + async def next( + self, + ) -> Optional[Union["MultipartReader", "BodyPartReader"]]: + """Emits next multipart reader object.""" + item = await self.stream.next() + if self.stream.at_eof(): + await self.release() + return item + + async def release(self) -> None: + """Release the connection gracefully. + + All remaining content is read to the void. + """ + await self.resp.release() + + +class BodyPartReader: + """Multipart reader for single body part.""" + + chunk_size = 8192 + + def __init__( + self, + boundary: bytes, + headers: "CIMultiDictProxy[str]", + content: StreamReader, + *, + subtype: str = "mixed", + default_charset: Optional[str] = None, + max_decompress_size: int = DEFAULT_MAX_DECOMPRESS_SIZE, + ) -> None: + self.headers = headers + self._boundary = boundary + self._boundary_len = len(boundary) + 2 # Boundary + \r\n + self._content = content + self._default_charset = default_charset + self._at_eof = False + self._is_form_data = subtype == "form-data" + # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8 + length = None if self._is_form_data else self.headers.get(CONTENT_LENGTH, None) + self._length = int(length) if length is not None else None + self._read_bytes = 0 + self._unread: Deque[bytes] = deque() + self._prev_chunk: Optional[bytes] = None + self._content_eof = 0 + self._cache: Dict[str, Any] = {} + self._max_decompress_size = max_decompress_size + + def __aiter__(self: Self) -> Self: + return self + + async def __anext__(self) -> bytes: + part = await self.next() + if part is None: + raise StopAsyncIteration + return part + + async def next(self) -> Optional[bytes]: + item = await self.read() + if not item: + return None + return item + + async def read(self, *, decode: bool = False) -> bytes: + """Reads body part data. + + decode: Decodes data following by encoding + method from Content-Encoding header. If it missed + data remains untouched + """ + if self._at_eof: + return b"" + data = bytearray() + while not self._at_eof: + data.extend(await self.read_chunk(self.chunk_size)) + if decode: + return await self.decode(data) + return data + + async def read_chunk(self, size: int = chunk_size) -> bytes: + """Reads body part content chunk of the specified size. + + size: chunk size + """ + if self._at_eof: + return b"" + if self._length: + chunk = await self._read_chunk_from_length(size) + else: + chunk = await self._read_chunk_from_stream(size) + + # For the case of base64 data, we must read a fragment of size with a + # remainder of 0 by dividing by 4 for string without symbols \n or \r + encoding = self.headers.get(CONTENT_TRANSFER_ENCODING) + if encoding and encoding.lower() == "base64": + stripped_chunk = b"".join(chunk.split()) + remainder = len(stripped_chunk) % 4 + + while remainder != 0 and not self.at_eof(): + over_chunk_size = 4 - remainder + over_chunk = b"" + + if self._prev_chunk: + over_chunk = self._prev_chunk[:over_chunk_size] + self._prev_chunk = self._prev_chunk[len(over_chunk) :] + + if len(over_chunk) != over_chunk_size: + over_chunk += await self._content.read(4 - len(over_chunk)) + + if not over_chunk: + self._at_eof = True + + stripped_chunk += b"".join(over_chunk.split()) + chunk += over_chunk + remainder = len(stripped_chunk) % 4 + + self._read_bytes += len(chunk) + if self._read_bytes == self._length: + self._at_eof = True + if self._at_eof and await self._content.readline() != b"\r\n": + raise ValueError("Reader did not read all the data or it is malformed") + return chunk + + async def _read_chunk_from_length(self, size: int) -> bytes: + # Reads body part content chunk of the specified size. + # The body part must has Content-Length header with proper value. + assert self._length is not None, "Content-Length required for chunked read" + chunk_size = min(size, self._length - self._read_bytes) + chunk = await self._content.read(chunk_size) + if self._content.at_eof(): + self._at_eof = True + return chunk + + async def _read_chunk_from_stream(self, size: int) -> bytes: + # Reads content chunk of body part with unknown length. + # The Content-Length header for body part is not necessary. + assert ( + size >= self._boundary_len + ), "Chunk size must be greater or equal than boundary length + 2" + first_chunk = self._prev_chunk is None + if first_chunk: + # We need to re-add the CRLF that got removed from headers parsing. + self._prev_chunk = b"\r\n" + await self._content.read(size) + + chunk = b"" + # content.read() may return less than size, so we need to loop to ensure + # we have enough data to detect the boundary. + while len(chunk) < self._boundary_len: + chunk += await self._content.read(size) + self._content_eof += int(self._content.at_eof()) + if self._content_eof > 2: + raise ValueError("Reading after EOF") + if self._content_eof: + break + if len(chunk) > size: + self._content.unread_data(chunk[size:]) + chunk = chunk[:size] + + assert self._prev_chunk is not None + window = self._prev_chunk + chunk + sub = b"\r\n" + self._boundary + if first_chunk: + idx = window.find(sub) + else: + idx = window.find(sub, max(0, len(self._prev_chunk) - len(sub))) + if idx >= 0: + # pushing boundary back to content + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=DeprecationWarning) + self._content.unread_data(window[idx:]) + self._prev_chunk = self._prev_chunk[:idx] + chunk = window[len(self._prev_chunk) : idx] + if not chunk: + self._at_eof = True + result = self._prev_chunk[2 if first_chunk else 0 :] # Strip initial CRLF + self._prev_chunk = chunk + return result + + async def readline(self) -> bytes: + """Reads body part by line by line.""" + if self._at_eof: + return b"" + + if self._unread: + line = self._unread.popleft() + else: + line = await self._content.readline() + + if line.startswith(self._boundary): + # the very last boundary may not come with \r\n, + # so set single rules for everyone + sline = line.rstrip(b"\r\n") + boundary = self._boundary + last_boundary = self._boundary + b"--" + # ensure that we read exactly the boundary, not something alike + if sline == boundary or sline == last_boundary: + self._at_eof = True + self._unread.append(line) + return b"" + else: + next_line = await self._content.readline() + if next_line.startswith(self._boundary): + line = line[:-2] # strip CRLF but only once + self._unread.append(next_line) + + return line + + async def release(self) -> None: + """Like read(), but reads all the data to the void.""" + if self._at_eof: + return + while not self._at_eof: + await self.read_chunk(self.chunk_size) + + async def text(self, *, encoding: Optional[str] = None) -> str: + """Like read(), but assumes that body part contains text data.""" + data = await self.read(decode=True) + # see https://www.w3.org/TR/html5/forms.html#multipart/form-data-encoding-algorithm + # and https://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-send + encoding = encoding or self.get_charset(default="utf-8") + return data.decode(encoding) + + async def json(self, *, encoding: Optional[str] = None) -> Optional[Dict[str, Any]]: + """Like read(), but assumes that body parts contains JSON data.""" + data = await self.read(decode=True) + if not data: + return None + encoding = encoding or self.get_charset(default="utf-8") + return cast(Dict[str, Any], json.loads(data.decode(encoding))) + + async def form(self, *, encoding: Optional[str] = None) -> List[Tuple[str, str]]: + """Like read(), but assumes that body parts contain form urlencoded data.""" + data = await self.read(decode=True) + if not data: + return [] + if encoding is not None: + real_encoding = encoding + else: + real_encoding = self.get_charset(default="utf-8") + try: + decoded_data = data.rstrip().decode(real_encoding) + except UnicodeDecodeError: + raise ValueError("data cannot be decoded with %s encoding" % real_encoding) + + return parse_qsl( + decoded_data, + keep_blank_values=True, + encoding=real_encoding, + ) + + def at_eof(self) -> bool: + """Returns True if the boundary was reached or False otherwise.""" + return self._at_eof + + async def decode(self, data: bytes) -> bytes: + """Decodes data. + + Decoding is done according the specified Content-Encoding + or Content-Transfer-Encoding headers value. + """ + if CONTENT_TRANSFER_ENCODING in self.headers: + data = self._decode_content_transfer(data) + # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8 + if not self._is_form_data and CONTENT_ENCODING in self.headers: + return await self._decode_content(data) + return data + + async def _decode_content(self, data: bytes) -> bytes: + encoding = self.headers.get(CONTENT_ENCODING, "").lower() + if encoding == "identity": + return data + if encoding in {"deflate", "gzip"}: + return await ZLibDecompressor( + encoding=encoding, + suppress_deflate_header=True, + ).decompress(data, max_length=self._max_decompress_size) + + raise RuntimeError(f"unknown content encoding: {encoding}") + + def _decode_content_transfer(self, data: bytes) -> bytes: + encoding = self.headers.get(CONTENT_TRANSFER_ENCODING, "").lower() + + if encoding == "base64": + return base64.b64decode(data) + elif encoding == "quoted-printable": + return binascii.a2b_qp(data) + elif encoding in ("binary", "8bit", "7bit"): + return data + else: + raise RuntimeError(f"unknown content transfer encoding: {encoding}") + + def get_charset(self, default: str) -> str: + """Returns charset parameter from Content-Type header or default.""" + ctype = self.headers.get(CONTENT_TYPE, "") + mimetype = parse_mimetype(ctype) + return mimetype.parameters.get("charset", self._default_charset or default) + + @reify + def name(self) -> Optional[str]: + """Returns name specified in Content-Disposition header. + + If the header is missing or malformed, returns None. + """ + _, params = parse_content_disposition(self.headers.get(CONTENT_DISPOSITION)) + return content_disposition_filename(params, "name") + + @reify + def filename(self) -> Optional[str]: + """Returns filename specified in Content-Disposition header. + + Returns None if the header is missing or malformed. + """ + _, params = parse_content_disposition(self.headers.get(CONTENT_DISPOSITION)) + return content_disposition_filename(params, "filename") + + +@payload_type(BodyPartReader, order=Order.try_first) +class BodyPartReaderPayload(Payload): + _value: BodyPartReader + # _autoclose = False (inherited) - Streaming reader that may have resources + + def __init__(self, value: BodyPartReader, *args: Any, **kwargs: Any) -> None: + super().__init__(value, *args, **kwargs) + + params: Dict[str, str] = {} + if value.name is not None: + params["name"] = value.name + if value.filename is not None: + params["filename"] = value.filename + + if params: + self.set_content_disposition("attachment", True, **params) + + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + raise TypeError("Unable to decode.") + + async def as_bytes(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: + """Raises TypeError as body parts should be consumed via write(). + + This is intentional: BodyPartReader payloads are designed for streaming + large data (potentially gigabytes) and must be consumed only once via + the write() method to avoid memory exhaustion. They cannot be buffered + in memory for reuse. + """ + raise TypeError("Unable to read body part as bytes. Use write() to consume.") + + async def write(self, writer: AbstractStreamWriter) -> None: + field = self._value + chunk = await field.read_chunk(size=2**16) + while chunk: + await writer.write(await field.decode(chunk)) + chunk = await field.read_chunk(size=2**16) + + +class MultipartReader: + """Multipart body reader.""" + + #: Response wrapper, used when multipart readers constructs from response. + response_wrapper_cls = MultipartResponseWrapper + #: Multipart reader class, used to handle multipart/* body parts. + #: None points to type(self) + multipart_reader_cls: Optional[Type["MultipartReader"]] = None + #: Body part reader class for non multipart/* content types. + part_reader_cls = BodyPartReader + + def __init__(self, headers: Mapping[str, str], content: StreamReader) -> None: + self._mimetype = parse_mimetype(headers[CONTENT_TYPE]) + assert self._mimetype.type == "multipart", "multipart/* content type expected" + if "boundary" not in self._mimetype.parameters: + raise ValueError( + "boundary missed for Content-Type: %s" % headers[CONTENT_TYPE] + ) + + self.headers = headers + self._boundary = ("--" + self._get_boundary()).encode() + self._content = content + self._default_charset: Optional[str] = None + self._last_part: Optional[Union["MultipartReader", BodyPartReader]] = None + self._at_eof = False + self._at_bof = True + self._unread: List[bytes] = [] + + def __aiter__(self: Self) -> Self: + return self + + async def __anext__( + self, + ) -> Optional[Union["MultipartReader", BodyPartReader]]: + part = await self.next() + if part is None: + raise StopAsyncIteration + return part + + @classmethod + def from_response( + cls, + response: "ClientResponse", + ) -> MultipartResponseWrapper: + """Constructs reader instance from HTTP response. + + :param response: :class:`~aiohttp.client.ClientResponse` instance + """ + obj = cls.response_wrapper_cls( + response, cls(response.headers, response.content) + ) + return obj + + def at_eof(self) -> bool: + """Returns True if the final boundary was reached, false otherwise.""" + return self._at_eof + + async def next( + self, + ) -> Optional[Union["MultipartReader", BodyPartReader]]: + """Emits the next multipart body part.""" + # So, if we're at BOF, we need to skip till the boundary. + if self._at_eof: + return None + await self._maybe_release_last_part() + if self._at_bof: + await self._read_until_first_boundary() + self._at_bof = False + else: + await self._read_boundary() + if self._at_eof: # we just read the last boundary, nothing to do there + return None + + part = await self.fetch_next_part() + # https://datatracker.ietf.org/doc/html/rfc7578#section-4.6 + if ( + self._last_part is None + and self._mimetype.subtype == "form-data" + and isinstance(part, BodyPartReader) + ): + _, params = parse_content_disposition(part.headers.get(CONTENT_DISPOSITION)) + if params.get("name") == "_charset_": + # Longest encoding in https://encoding.spec.whatwg.org/encodings.json + # is 19 characters, so 32 should be more than enough for any valid encoding. + charset = await part.read_chunk(32) + if len(charset) > 31: + raise RuntimeError("Invalid default charset") + self._default_charset = charset.strip().decode() + part = await self.fetch_next_part() + self._last_part = part + return self._last_part + + async def release(self) -> None: + """Reads all the body parts to the void till the final boundary.""" + while not self._at_eof: + item = await self.next() + if item is None: + break + await item.release() + + async def fetch_next_part( + self, + ) -> Union["MultipartReader", BodyPartReader]: + """Returns the next body part reader.""" + headers = await self._read_headers() + return self._get_part_reader(headers) + + def _get_part_reader( + self, + headers: "CIMultiDictProxy[str]", + ) -> Union["MultipartReader", BodyPartReader]: + """Dispatches the response by the `Content-Type` header. + + Returns a suitable reader instance. + + :param dict headers: Response headers + """ + ctype = headers.get(CONTENT_TYPE, "") + mimetype = parse_mimetype(ctype) + + if mimetype.type == "multipart": + if self.multipart_reader_cls is None: + return type(self)(headers, self._content) + return self.multipart_reader_cls(headers, self._content) + else: + return self.part_reader_cls( + self._boundary, + headers, + self._content, + subtype=self._mimetype.subtype, + default_charset=self._default_charset, + ) + + def _get_boundary(self) -> str: + boundary = self._mimetype.parameters["boundary"] + if len(boundary) > 70: + raise ValueError("boundary %r is too long (70 chars max)" % boundary) + + return boundary + + async def _readline(self) -> bytes: + if self._unread: + return self._unread.pop() + return await self._content.readline() + + async def _read_until_first_boundary(self) -> None: + while True: + chunk = await self._readline() + if chunk == b"": + raise ValueError( + "Could not find starting boundary %r" % (self._boundary) + ) + chunk = chunk.rstrip() + if chunk == self._boundary: + return + elif chunk == self._boundary + b"--": + self._at_eof = True + return + + async def _read_boundary(self) -> None: + chunk = (await self._readline()).rstrip() + if chunk == self._boundary: + pass + elif chunk == self._boundary + b"--": + self._at_eof = True + epilogue = await self._readline() + next_line = await self._readline() + + # the epilogue is expected and then either the end of input or the + # parent multipart boundary, if the parent boundary is found then + # it should be marked as unread and handed to the parent for + # processing + if next_line[:2] == b"--": + self._unread.append(next_line) + # otherwise the request is likely missing an epilogue and both + # lines should be passed to the parent for processing + # (this handles the old behavior gracefully) + else: + self._unread.extend([next_line, epilogue]) + else: + raise ValueError(f"Invalid boundary {chunk!r}, expected {self._boundary!r}") + + async def _read_headers(self) -> "CIMultiDictProxy[str]": + lines = [] + while True: + chunk = await self._content.readline() + chunk = chunk.rstrip(b"\r\n") + lines.append(chunk) + if not chunk: + break + parser = HeadersParser() + headers, raw_headers = parser.parse_headers(lines) + return headers + + async def _maybe_release_last_part(self) -> None: + """Ensures that the last read body part is read completely.""" + if self._last_part is not None: + if not self._last_part.at_eof(): + await self._last_part.release() + self._unread.extend(self._last_part._unread) + self._last_part = None + + +_Part = Tuple[Payload, str, str] + + +class MultipartWriter(Payload): + """Multipart body writer.""" + + _value: None + # _consumed = False (inherited) - Can be encoded multiple times + _autoclose = True # No file handles, just collects parts in memory + + def __init__(self, subtype: str = "mixed", boundary: Optional[str] = None) -> None: + boundary = boundary if boundary is not None else uuid.uuid4().hex + # The underlying Payload API demands a str (utf-8), not bytes, + # so we need to ensure we don't lose anything during conversion. + # As a result, require the boundary to be ASCII only. + # In both situations. + + try: + self._boundary = boundary.encode("ascii") + except UnicodeEncodeError: + raise ValueError("boundary should contain ASCII only chars") from None + ctype = f"multipart/{subtype}; boundary={self._boundary_value}" + + super().__init__(None, content_type=ctype) + + self._parts: List[_Part] = [] + self._is_form_data = subtype == "form-data" + + def __enter__(self) -> "MultipartWriter": + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + pass + + def __iter__(self) -> Iterator[_Part]: + return iter(self._parts) + + def __len__(self) -> int: + return len(self._parts) + + def __bool__(self) -> bool: + return True + + _valid_tchar_regex = re.compile(rb"\A[!#$%&'*+\-.^_`|~\w]+\Z") + _invalid_qdtext_char_regex = re.compile(rb"[\x00-\x08\x0A-\x1F\x7F]") + + @property + def _boundary_value(self) -> str: + """Wrap boundary parameter value in quotes, if necessary. + + Reads self.boundary and returns a unicode string. + """ + # Refer to RFCs 7231, 7230, 5234. + # + # parameter = token "=" ( token / quoted-string ) + # token = 1*tchar + # quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE + # qdtext = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text + # obs-text = %x80-FF + # quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) + # tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" + # / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" + # / DIGIT / ALPHA + # ; any VCHAR, except delimiters + # VCHAR = %x21-7E + value = self._boundary + if re.match(self._valid_tchar_regex, value): + return value.decode("ascii") # cannot fail + + if re.search(self._invalid_qdtext_char_regex, value): + raise ValueError("boundary value contains invalid characters") + + # escape %x5C and %x22 + quoted_value_content = value.replace(b"\\", b"\\\\") + quoted_value_content = quoted_value_content.replace(b'"', b'\\"') + + return '"' + quoted_value_content.decode("ascii") + '"' + + @property + def boundary(self) -> str: + return self._boundary.decode("ascii") + + def append(self, obj: Any, headers: Optional[Mapping[str, str]] = None) -> Payload: + if headers is None: + headers = CIMultiDict() + + if isinstance(obj, Payload): + obj.headers.update(headers) + return self.append_payload(obj) + else: + try: + payload = get_payload(obj, headers=headers) + except LookupError: + raise TypeError("Cannot create payload from %r" % obj) + else: + return self.append_payload(payload) + + def append_payload(self, payload: Payload) -> Payload: + """Adds a new body part to multipart writer.""" + encoding: Optional[str] = None + te_encoding: Optional[str] = None + if self._is_form_data: + # https://datatracker.ietf.org/doc/html/rfc7578#section-4.7 + # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8 + assert ( + not {CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TRANSFER_ENCODING} + & payload.headers.keys() + ) + # Set default Content-Disposition in case user doesn't create one + if CONTENT_DISPOSITION not in payload.headers: + name = f"section-{len(self._parts)}" + payload.set_content_disposition("form-data", name=name) + else: + # compression + encoding = payload.headers.get(CONTENT_ENCODING, "").lower() + if encoding and encoding not in ("deflate", "gzip", "identity"): + raise RuntimeError(f"unknown content encoding: {encoding}") + if encoding == "identity": + encoding = None + + # te encoding + te_encoding = payload.headers.get(CONTENT_TRANSFER_ENCODING, "").lower() + if te_encoding not in ("", "base64", "quoted-printable", "binary"): + raise RuntimeError(f"unknown content transfer encoding: {te_encoding}") + if te_encoding == "binary": + te_encoding = None + + # size + size = payload.size + if size is not None and not (encoding or te_encoding): + payload.headers[CONTENT_LENGTH] = str(size) + + self._parts.append((payload, encoding, te_encoding)) # type: ignore[arg-type] + return payload + + def append_json( + self, obj: Any, headers: Optional[Mapping[str, str]] = None + ) -> Payload: + """Helper to append JSON part.""" + if headers is None: + headers = CIMultiDict() + + return self.append_payload(JsonPayload(obj, headers=headers)) + + def append_form( + self, + obj: Union[Sequence[Tuple[str, str]], Mapping[str, str]], + headers: Optional[Mapping[str, str]] = None, + ) -> Payload: + """Helper to append form urlencoded part.""" + assert isinstance(obj, (Sequence, Mapping)) + + if headers is None: + headers = CIMultiDict() + + if isinstance(obj, Mapping): + obj = list(obj.items()) + data = urlencode(obj, doseq=True) + + return self.append_payload( + StringPayload( + data, headers=headers, content_type="application/x-www-form-urlencoded" + ) + ) + + @property + def size(self) -> Optional[int]: + """Size of the payload.""" + total = 0 + for part, encoding, te_encoding in self._parts: + part_size = part.size + if encoding or te_encoding or part_size is None: + return None + + total += int( + 2 + + len(self._boundary) + + 2 + + part_size # b'--'+self._boundary+b'\r\n' + + len(part._binary_headers) + + 2 # b'\r\n' + ) + + total += 2 + len(self._boundary) + 4 # b'--'+self._boundary+b'--\r\n' + return total + + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + """Return string representation of the multipart data. + + WARNING: This method may do blocking I/O if parts contain file payloads. + It should not be called in the event loop. Use as_bytes().decode() instead. + """ + return "".join( + "--" + + self.boundary + + "\r\n" + + part._binary_headers.decode(encoding, errors) + + part.decode() + for part, _e, _te in self._parts + ) + + async def as_bytes(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: + """Return bytes representation of the multipart data. + + This method is async-safe and calls as_bytes on underlying payloads. + """ + parts: List[bytes] = [] + + # Process each part + for part, _e, _te in self._parts: + # Add boundary + parts.append(b"--" + self._boundary + b"\r\n") + + # Add headers + parts.append(part._binary_headers) + + # Add payload content using as_bytes for async safety + part_bytes = await part.as_bytes(encoding, errors) + parts.append(part_bytes) + + # Add trailing CRLF + parts.append(b"\r\n") + + # Add closing boundary + parts.append(b"--" + self._boundary + b"--\r\n") + + return b"".join(parts) + + async def write( + self, writer: AbstractStreamWriter, close_boundary: bool = True + ) -> None: + """Write body.""" + for part, encoding, te_encoding in self._parts: + if self._is_form_data: + # https://datatracker.ietf.org/doc/html/rfc7578#section-4.2 + assert CONTENT_DISPOSITION in part.headers + assert "name=" in part.headers[CONTENT_DISPOSITION] + + await writer.write(b"--" + self._boundary + b"\r\n") + await writer.write(part._binary_headers) + + if encoding or te_encoding: + w = MultipartPayloadWriter(writer) + if encoding: + w.enable_compression(encoding) + if te_encoding: + w.enable_encoding(te_encoding) + await part.write(w) # type: ignore[arg-type] + await w.write_eof() + else: + await part.write(writer) + + await writer.write(b"\r\n") + + if close_boundary: + await writer.write(b"--" + self._boundary + b"--\r\n") + + async def close(self) -> None: + """ + Close all part payloads that need explicit closing. + + IMPORTANT: This method must not await anything that might not finish + immediately, as it may be called during cleanup/cancellation. Schedule + any long-running operations without awaiting them. + """ + if self._consumed: + return + self._consumed = True + + # Close all parts that need explicit closing + # We catch and log exceptions to ensure all parts get a chance to close + # we do not use asyncio.gather() here because we are not allowed + # to suspend given we may be called during cleanup + for idx, (part, _, _) in enumerate(self._parts): + if not part.autoclose and not part.consumed: + try: + await part.close() + except Exception as exc: + internal_logger.error( + "Failed to close multipart part %d: %s", idx, exc, exc_info=True + ) + + +class MultipartPayloadWriter: + def __init__(self, writer: AbstractStreamWriter) -> None: + self._writer = writer + self._encoding: Optional[str] = None + self._compress: Optional[ZLibCompressor] = None + self._encoding_buffer: Optional[bytearray] = None + + def enable_encoding(self, encoding: str) -> None: + if encoding == "base64": + self._encoding = encoding + self._encoding_buffer = bytearray() + elif encoding == "quoted-printable": + self._encoding = "quoted-printable" + + def enable_compression( + self, encoding: str = "deflate", strategy: Optional[int] = None + ) -> None: + self._compress = ZLibCompressor( + encoding=encoding, + suppress_deflate_header=True, + strategy=strategy, + ) + + async def write_eof(self) -> None: + if self._compress is not None: + chunk = self._compress.flush() + if chunk: + self._compress = None + await self.write(chunk) + + if self._encoding == "base64": + if self._encoding_buffer: + await self._writer.write(base64.b64encode(self._encoding_buffer)) + + async def write(self, chunk: bytes) -> None: + if self._compress is not None: + if chunk: + chunk = await self._compress.compress(chunk) + if not chunk: + return + + if self._encoding == "base64": + buf = self._encoding_buffer + assert buf is not None + buf.extend(chunk) + + if buf: + div, mod = divmod(len(buf), 3) + enc_chunk, self._encoding_buffer = (buf[: div * 3], buf[div * 3 :]) + if enc_chunk: + b64chunk = base64.b64encode(enc_chunk) + await self._writer.write(b64chunk) + elif self._encoding == "quoted-printable": + await self._writer.write(binascii.b2a_qp(chunk)) + else: + await self._writer.write(chunk) diff --git a/.venv/lib/python3.9/site-packages/aiohttp/payload.py b/.venv/lib/python3.9/site-packages/aiohttp/payload.py new file mode 100644 index 0000000..5b88fa0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/payload.py @@ -0,0 +1,1120 @@ +import asyncio +import enum +import io +import json +import mimetypes +import os +import sys +import warnings +from abc import ABC, abstractmethod +from collections.abc import Iterable +from itertools import chain +from typing import ( + IO, + TYPE_CHECKING, + Any, + Dict, + Final, + List, + Optional, + Set, + TextIO, + Tuple, + Type, + Union, +) + +from multidict import CIMultiDict + +from . import hdrs +from .abc import AbstractStreamWriter +from .helpers import ( + _SENTINEL, + content_disposition_header, + guess_filename, + parse_mimetype, + sentinel, +) +from .streams import StreamReader +from .typedefs import JSONEncoder, _CIMultiDict + +__all__ = ( + "PAYLOAD_REGISTRY", + "get_payload", + "payload_type", + "Payload", + "BytesPayload", + "StringPayload", + "IOBasePayload", + "BytesIOPayload", + "BufferedReaderPayload", + "TextIOPayload", + "StringIOPayload", + "JsonPayload", + "AsyncIterablePayload", +) + +TOO_LARGE_BYTES_BODY: Final[int] = 2**20 # 1 MB +READ_SIZE: Final[int] = 2**16 # 64 KB +_CLOSE_FUTURES: Set[asyncio.Future[None]] = set() + + +class LookupError(Exception): + """Raised when no payload factory is found for the given data type.""" + + +class Order(str, enum.Enum): + normal = "normal" + try_first = "try_first" + try_last = "try_last" + + +def get_payload(data: Any, *args: Any, **kwargs: Any) -> "Payload": + return PAYLOAD_REGISTRY.get(data, *args, **kwargs) + + +def register_payload( + factory: Type["Payload"], type: Any, *, order: Order = Order.normal +) -> None: + PAYLOAD_REGISTRY.register(factory, type, order=order) + + +class payload_type: + def __init__(self, type: Any, *, order: Order = Order.normal) -> None: + self.type = type + self.order = order + + def __call__(self, factory: Type["Payload"]) -> Type["Payload"]: + register_payload(factory, self.type, order=self.order) + return factory + + +PayloadType = Type["Payload"] +_PayloadRegistryItem = Tuple[PayloadType, Any] + + +class PayloadRegistry: + """Payload registry. + + note: we need zope.interface for more efficient adapter search + """ + + __slots__ = ("_first", "_normal", "_last", "_normal_lookup") + + def __init__(self) -> None: + self._first: List[_PayloadRegistryItem] = [] + self._normal: List[_PayloadRegistryItem] = [] + self._last: List[_PayloadRegistryItem] = [] + self._normal_lookup: Dict[Any, PayloadType] = {} + + def get( + self, + data: Any, + *args: Any, + _CHAIN: "Type[chain[_PayloadRegistryItem]]" = chain, + **kwargs: Any, + ) -> "Payload": + if self._first: + for factory, type_ in self._first: + if isinstance(data, type_): + return factory(data, *args, **kwargs) + # Try the fast lookup first + if lookup_factory := self._normal_lookup.get(type(data)): + return lookup_factory(data, *args, **kwargs) + # Bail early if its already a Payload + if isinstance(data, Payload): + return data + # Fallback to the slower linear search + for factory, type_ in _CHAIN(self._normal, self._last): + if isinstance(data, type_): + return factory(data, *args, **kwargs) + raise LookupError() + + def register( + self, factory: PayloadType, type: Any, *, order: Order = Order.normal + ) -> None: + if order is Order.try_first: + self._first.append((factory, type)) + elif order is Order.normal: + self._normal.append((factory, type)) + if isinstance(type, Iterable): + for t in type: + self._normal_lookup[t] = factory + else: + self._normal_lookup[type] = factory + elif order is Order.try_last: + self._last.append((factory, type)) + else: + raise ValueError(f"Unsupported order {order!r}") + + +class Payload(ABC): + + _default_content_type: str = "application/octet-stream" + _size: Optional[int] = None + _consumed: bool = False # Default: payload has not been consumed yet + _autoclose: bool = False # Default: assume resource needs explicit closing + + def __init__( + self, + value: Any, + headers: Optional[ + Union[_CIMultiDict, Dict[str, str], Iterable[Tuple[str, str]]] + ] = None, + content_type: Union[str, None, _SENTINEL] = sentinel, + filename: Optional[str] = None, + encoding: Optional[str] = None, + **kwargs: Any, + ) -> None: + self._encoding = encoding + self._filename = filename + self._headers: _CIMultiDict = CIMultiDict() + self._value = value + if content_type is not sentinel and content_type is not None: + self._headers[hdrs.CONTENT_TYPE] = content_type + elif self._filename is not None: + if sys.version_info >= (3, 13): + guesser = mimetypes.guess_file_type + else: + guesser = mimetypes.guess_type + content_type = guesser(self._filename)[0] + if content_type is None: + content_type = self._default_content_type + self._headers[hdrs.CONTENT_TYPE] = content_type + else: + self._headers[hdrs.CONTENT_TYPE] = self._default_content_type + if headers: + self._headers.update(headers) + + @property + def size(self) -> Optional[int]: + """Size of the payload in bytes. + + Returns the number of bytes that will be transmitted when the payload + is written. For string payloads, this is the size after encoding to bytes, + not the length of the string. + """ + return self._size + + @property + def filename(self) -> Optional[str]: + """Filename of the payload.""" + return self._filename + + @property + def headers(self) -> _CIMultiDict: + """Custom item headers""" + return self._headers + + @property + def _binary_headers(self) -> bytes: + return ( + "".join([k + ": " + v + "\r\n" for k, v in self.headers.items()]).encode( + "utf-8" + ) + + b"\r\n" + ) + + @property + def encoding(self) -> Optional[str]: + """Payload encoding""" + return self._encoding + + @property + def content_type(self) -> str: + """Content type""" + return self._headers[hdrs.CONTENT_TYPE] + + @property + def consumed(self) -> bool: + """Whether the payload has been consumed and cannot be reused.""" + return self._consumed + + @property + def autoclose(self) -> bool: + """ + Whether the payload can close itself automatically. + + Returns True if the payload has no file handles or resources that need + explicit closing. If False, callers must await close() to release resources. + """ + return self._autoclose + + def set_content_disposition( + self, + disptype: str, + quote_fields: bool = True, + _charset: str = "utf-8", + **params: Any, + ) -> None: + """Sets ``Content-Disposition`` header.""" + self._headers[hdrs.CONTENT_DISPOSITION] = content_disposition_header( + disptype, quote_fields=quote_fields, _charset=_charset, **params + ) + + @abstractmethod + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + """ + Return string representation of the value. + + This is named decode() to allow compatibility with bytes objects. + """ + + @abstractmethod + async def write(self, writer: AbstractStreamWriter) -> None: + """ + Write payload to the writer stream. + + Args: + writer: An AbstractStreamWriter instance that handles the actual writing + + This is a legacy method that writes the entire payload without length constraints. + + Important: + For new implementations, use write_with_length() instead of this method. + This method is maintained for backwards compatibility and will eventually + delegate to write_with_length(writer, None) in all implementations. + + All payload subclasses must override this method for backwards compatibility, + but new code should use write_with_length for more flexibility and control. + + """ + + # write_with_length is new in aiohttp 3.12 + # it should be overridden by subclasses + async def write_with_length( + self, writer: AbstractStreamWriter, content_length: Optional[int] + ) -> None: + """ + Write payload with a specific content length constraint. + + Args: + writer: An AbstractStreamWriter instance that handles the actual writing + content_length: Maximum number of bytes to write (None for unlimited) + + This method allows writing payload content with a specific length constraint, + which is particularly useful for HTTP responses with Content-Length header. + + Note: + This is the base implementation that provides backwards compatibility + for subclasses that don't override this method. Specific payload types + should override this method to implement proper length-constrained writing. + + """ + # Backwards compatibility for subclasses that don't override this method + # and for the default implementation + await self.write(writer) + + async def as_bytes(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: + """ + Return bytes representation of the value. + + This is a convenience method that calls decode() and encodes the result + to bytes using the specified encoding. + """ + # Use instance encoding if available, otherwise use parameter + actual_encoding = self._encoding or encoding + return self.decode(actual_encoding, errors).encode(actual_encoding) + + def _close(self) -> None: + """ + Async safe synchronous close operations for backwards compatibility. + + This method exists only for backwards compatibility with code that + needs to clean up payloads synchronously. In the future, we will + drop this method and only support the async close() method. + + WARNING: This method must be safe to call from within the event loop + without blocking. Subclasses should not perform any blocking I/O here. + + WARNING: This method must be called from within an event loop for + certain payload types (e.g., IOBasePayload). Calling it outside an + event loop may raise RuntimeError. + """ + # This is a no-op by default, but subclasses can override it + # for non-blocking cleanup operations. + + async def close(self) -> None: + """ + Close the payload if it holds any resources. + + IMPORTANT: This method must not await anything that might not finish + immediately, as it may be called during cleanup/cancellation. Schedule + any long-running operations without awaiting them. + + In the future, this will be the only close method supported. + """ + self._close() + + +class BytesPayload(Payload): + _value: bytes + # _consumed = False (inherited) - Bytes are immutable and can be reused + _autoclose = True # No file handle, just bytes in memory + + def __init__( + self, value: Union[bytes, bytearray, memoryview], *args: Any, **kwargs: Any + ) -> None: + if "content_type" not in kwargs: + kwargs["content_type"] = "application/octet-stream" + + super().__init__(value, *args, **kwargs) + + if isinstance(value, memoryview): + self._size = value.nbytes + elif isinstance(value, (bytes, bytearray)): + self._size = len(value) + else: + raise TypeError(f"value argument must be byte-ish, not {type(value)!r}") + + if self._size > TOO_LARGE_BYTES_BODY: + kwargs = {"source": self} + warnings.warn( + "Sending a large body directly with raw bytes might" + " lock the event loop. You should probably pass an " + "io.BytesIO object instead", + ResourceWarning, + **kwargs, + ) + + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + return self._value.decode(encoding, errors) + + async def as_bytes(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: + """ + Return bytes representation of the value. + + This method returns the raw bytes content of the payload. + It is equivalent to accessing the _value attribute directly. + """ + return self._value + + async def write(self, writer: AbstractStreamWriter) -> None: + """ + Write the entire bytes payload to the writer stream. + + Args: + writer: An AbstractStreamWriter instance that handles the actual writing + + This method writes the entire bytes content without any length constraint. + + Note: + For new implementations that need length control, use write_with_length(). + This method is maintained for backwards compatibility and is equivalent + to write_with_length(writer, None). + + """ + await writer.write(self._value) + + async def write_with_length( + self, writer: AbstractStreamWriter, content_length: Optional[int] + ) -> None: + """ + Write bytes payload with a specific content length constraint. + + Args: + writer: An AbstractStreamWriter instance that handles the actual writing + content_length: Maximum number of bytes to write (None for unlimited) + + This method writes either the entire byte sequence or a slice of it + up to the specified content_length. For BytesPayload, this operation + is performed efficiently using array slicing. + + """ + if content_length is not None: + await writer.write(self._value[:content_length]) + else: + await writer.write(self._value) + + +class StringPayload(BytesPayload): + def __init__( + self, + value: str, + *args: Any, + encoding: Optional[str] = None, + content_type: Optional[str] = None, + **kwargs: Any, + ) -> None: + + if encoding is None: + if content_type is None: + real_encoding = "utf-8" + content_type = "text/plain; charset=utf-8" + else: + mimetype = parse_mimetype(content_type) + real_encoding = mimetype.parameters.get("charset", "utf-8") + else: + if content_type is None: + content_type = "text/plain; charset=%s" % encoding + real_encoding = encoding + + super().__init__( + value.encode(real_encoding), + encoding=real_encoding, + content_type=content_type, + *args, + **kwargs, + ) + + +class StringIOPayload(StringPayload): + def __init__(self, value: IO[str], *args: Any, **kwargs: Any) -> None: + super().__init__(value.read(), *args, **kwargs) + + +class IOBasePayload(Payload): + _value: io.IOBase + # _consumed = False (inherited) - File can be re-read from the same position + _start_position: Optional[int] = None + # _autoclose = False (inherited) - Has file handle that needs explicit closing + + def __init__( + self, value: IO[Any], disposition: str = "attachment", *args: Any, **kwargs: Any + ) -> None: + if "filename" not in kwargs: + kwargs["filename"] = guess_filename(value) + + super().__init__(value, *args, **kwargs) + + if self._filename is not None and disposition is not None: + if hdrs.CONTENT_DISPOSITION not in self.headers: + self.set_content_disposition(disposition, filename=self._filename) + + def _set_or_restore_start_position(self) -> None: + """Set or restore the start position of the file-like object.""" + if self._start_position is None: + try: + self._start_position = self._value.tell() + except (OSError, AttributeError): + self._consumed = True # Cannot seek, mark as consumed + return + try: + self._value.seek(self._start_position) + except (OSError, AttributeError): + # Failed to seek back - mark as consumed since we've already read + self._consumed = True + + def _read_and_available_len( + self, remaining_content_len: Optional[int] + ) -> Tuple[Optional[int], bytes]: + """ + Read the file-like object and return both its total size and the first chunk. + + Args: + remaining_content_len: Optional limit on how many bytes to read in this operation. + If None, READ_SIZE will be used as the default chunk size. + + Returns: + A tuple containing: + - The total size of the remaining unread content (None if size cannot be determined) + - The first chunk of bytes read from the file object + + This method is optimized to perform both size calculation and initial read + in a single operation, which is executed in a single executor job to minimize + context switches and file operations when streaming content. + + """ + self._set_or_restore_start_position() + size = self.size # Call size only once since it does I/O + return size, self._value.read( + min(READ_SIZE, size or READ_SIZE, remaining_content_len or READ_SIZE) + ) + + def _read(self, remaining_content_len: Optional[int]) -> bytes: + """ + Read a chunk of data from the file-like object. + + Args: + remaining_content_len: Optional maximum number of bytes to read. + If None, READ_SIZE will be used as the default chunk size. + + Returns: + A chunk of bytes read from the file object, respecting the + remaining_content_len limit if specified. + + This method is used for subsequent reads during streaming after + the initial _read_and_available_len call has been made. + + """ + return self._value.read(remaining_content_len or READ_SIZE) # type: ignore[no-any-return] + + @property + def size(self) -> Optional[int]: + """ + Size of the payload in bytes. + + Returns the total size of the payload content from the initial position. + This ensures consistent Content-Length for requests, including 307/308 redirects + where the same payload instance is reused. + + Returns None if the size cannot be determined (e.g., for unseekable streams). + """ + try: + # Store the start position on first access. + # This is critical when the same payload instance is reused (e.g., 307/308 + # redirects). Without storing the initial position, after the payload is + # read once, the file position would be at EOF, which would cause the + # size calculation to return 0 (file_size - EOF position). + # By storing the start position, we ensure the size calculation always + # returns the correct total size for any subsequent use. + if self._start_position is None: + self._start_position = self._value.tell() + + # Return the total size from the start position + # This ensures Content-Length is correct even after reading + return os.fstat(self._value.fileno()).st_size - self._start_position + except (AttributeError, OSError): + return None + + async def write(self, writer: AbstractStreamWriter) -> None: + """ + Write the entire file-like payload to the writer stream. + + Args: + writer: An AbstractStreamWriter instance that handles the actual writing + + This method writes the entire file content without any length constraint. + It delegates to write_with_length() with no length limit for implementation + consistency. + + Note: + For new implementations that need length control, use write_with_length() directly. + This method is maintained for backwards compatibility with existing code. + + """ + await self.write_with_length(writer, None) + + async def write_with_length( + self, writer: AbstractStreamWriter, content_length: Optional[int] + ) -> None: + """ + Write file-like payload with a specific content length constraint. + + Args: + writer: An AbstractStreamWriter instance that handles the actual writing + content_length: Maximum number of bytes to write (None for unlimited) + + This method implements optimized streaming of file content with length constraints: + + 1. File reading is performed in a thread pool to avoid blocking the event loop + 2. Content is read and written in chunks to maintain memory efficiency + 3. Writing stops when either: + - All available file content has been written (when size is known) + - The specified content_length has been reached + 4. File resources are properly closed even if the operation is cancelled + + The implementation carefully handles both known-size and unknown-size payloads, + as well as constrained and unconstrained content lengths. + + """ + loop = asyncio.get_running_loop() + total_written_len = 0 + remaining_content_len = content_length + + # Get initial data and available length + available_len, chunk = await loop.run_in_executor( + None, self._read_and_available_len, remaining_content_len + ) + # Process data chunks until done + while chunk: + chunk_len = len(chunk) + + # Write data with or without length constraint + if remaining_content_len is None: + await writer.write(chunk) + else: + await writer.write(chunk[:remaining_content_len]) + remaining_content_len -= chunk_len + + total_written_len += chunk_len + + # Check if we're done writing + if self._should_stop_writing( + available_len, total_written_len, remaining_content_len + ): + return + + # Read next chunk + chunk = await loop.run_in_executor( + None, + self._read, + ( + min(READ_SIZE, remaining_content_len) + if remaining_content_len is not None + else READ_SIZE + ), + ) + + def _should_stop_writing( + self, + available_len: Optional[int], + total_written_len: int, + remaining_content_len: Optional[int], + ) -> bool: + """ + Determine if we should stop writing data. + + Args: + available_len: Known size of the payload if available (None if unknown) + total_written_len: Number of bytes already written + remaining_content_len: Remaining bytes to be written for content-length limited responses + + Returns: + True if we should stop writing data, based on either: + - Having written all available data (when size is known) + - Having written all requested content (when content-length is specified) + + """ + return (available_len is not None and total_written_len >= available_len) or ( + remaining_content_len is not None and remaining_content_len <= 0 + ) + + def _close(self) -> None: + """ + Async safe synchronous close operations for backwards compatibility. + + This method exists only for backwards + compatibility. Use the async close() method instead. + + WARNING: This method MUST be called from within an event loop. + Calling it outside an event loop will raise RuntimeError. + """ + # Skip if already consumed + if self._consumed: + return + self._consumed = True # Mark as consumed to prevent further writes + # Schedule file closing without awaiting to prevent cancellation issues + loop = asyncio.get_running_loop() + close_future = loop.run_in_executor(None, self._value.close) + # Hold a strong reference to the future to prevent it from being + # garbage collected before it completes. + _CLOSE_FUTURES.add(close_future) + close_future.add_done_callback(_CLOSE_FUTURES.remove) + + async def close(self) -> None: + """ + Close the payload if it holds any resources. + + IMPORTANT: This method must not await anything that might not finish + immediately, as it may be called during cleanup/cancellation. Schedule + any long-running operations without awaiting them. + """ + self._close() + + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + """ + Return string representation of the value. + + WARNING: This method does blocking I/O and should not be called in the event loop. + """ + return self._read_all().decode(encoding, errors) + + def _read_all(self) -> bytes: + """Read the entire file-like object and return its content as bytes.""" + self._set_or_restore_start_position() + # Use readlines() to ensure we get all content + return b"".join(self._value.readlines()) + + async def as_bytes(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: + """ + Return bytes representation of the value. + + This method reads the entire file content and returns it as bytes. + It is equivalent to reading the file-like object directly. + The file reading is performed in an executor to avoid blocking the event loop. + """ + loop = asyncio.get_running_loop() + return await loop.run_in_executor(None, self._read_all) + + +class TextIOPayload(IOBasePayload): + _value: io.TextIOBase + # _autoclose = False (inherited) - Has text file handle that needs explicit closing + + def __init__( + self, + value: TextIO, + *args: Any, + encoding: Optional[str] = None, + content_type: Optional[str] = None, + **kwargs: Any, + ) -> None: + + if encoding is None: + if content_type is None: + encoding = "utf-8" + content_type = "text/plain; charset=utf-8" + else: + mimetype = parse_mimetype(content_type) + encoding = mimetype.parameters.get("charset", "utf-8") + else: + if content_type is None: + content_type = "text/plain; charset=%s" % encoding + + super().__init__( + value, + content_type=content_type, + encoding=encoding, + *args, + **kwargs, + ) + + def _read_and_available_len( + self, remaining_content_len: Optional[int] + ) -> Tuple[Optional[int], bytes]: + """ + Read the text file-like object and return both its total size and the first chunk. + + Args: + remaining_content_len: Optional limit on how many bytes to read in this operation. + If None, READ_SIZE will be used as the default chunk size. + + Returns: + A tuple containing: + - The total size of the remaining unread content (None if size cannot be determined) + - The first chunk of bytes read from the file object, encoded using the payload's encoding + + This method is optimized to perform both size calculation and initial read + in a single operation, which is executed in a single executor job to minimize + context switches and file operations when streaming content. + + Note: + TextIOPayload handles encoding of the text content before writing it + to the stream. If no encoding is specified, UTF-8 is used as the default. + + """ + self._set_or_restore_start_position() + size = self.size + chunk = self._value.read( + min(READ_SIZE, size or READ_SIZE, remaining_content_len or READ_SIZE) + ) + return size, chunk.encode(self._encoding) if self._encoding else chunk.encode() + + def _read(self, remaining_content_len: Optional[int]) -> bytes: + """ + Read a chunk of data from the text file-like object. + + Args: + remaining_content_len: Optional maximum number of bytes to read. + If None, READ_SIZE will be used as the default chunk size. + + Returns: + A chunk of bytes read from the file object and encoded using the payload's + encoding. The data is automatically converted from text to bytes. + + This method is used for subsequent reads during streaming after + the initial _read_and_available_len call has been made. It properly + handles text encoding, converting the text content to bytes using + the specified encoding (or UTF-8 if none was provided). + + """ + chunk = self._value.read(remaining_content_len or READ_SIZE) + return chunk.encode(self._encoding) if self._encoding else chunk.encode() + + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + """ + Return string representation of the value. + + WARNING: This method does blocking I/O and should not be called in the event loop. + """ + self._set_or_restore_start_position() + return self._value.read() + + async def as_bytes(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: + """ + Return bytes representation of the value. + + This method reads the entire text file content and returns it as bytes. + It encodes the text content using the specified encoding. + The file reading is performed in an executor to avoid blocking the event loop. + """ + loop = asyncio.get_running_loop() + + # Use instance encoding if available, otherwise use parameter + actual_encoding = self._encoding or encoding + + def _read_and_encode() -> bytes: + self._set_or_restore_start_position() + # TextIO read() always returns the full content + return self._value.read().encode(actual_encoding, errors) + + return await loop.run_in_executor(None, _read_and_encode) + + +class BytesIOPayload(IOBasePayload): + _value: io.BytesIO + _size: int # Always initialized in __init__ + _autoclose = True # BytesIO is in-memory, safe to auto-close + + def __init__(self, value: io.BytesIO, *args: Any, **kwargs: Any) -> None: + super().__init__(value, *args, **kwargs) + # Calculate size once during initialization + self._size = len(self._value.getbuffer()) - self._value.tell() + + @property + def size(self) -> int: + """Size of the payload in bytes. + + Returns the number of bytes in the BytesIO buffer that will be transmitted. + This is calculated once during initialization for efficiency. + """ + return self._size + + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + self._set_or_restore_start_position() + return self._value.read().decode(encoding, errors) + + async def write(self, writer: AbstractStreamWriter) -> None: + return await self.write_with_length(writer, None) + + async def write_with_length( + self, writer: AbstractStreamWriter, content_length: Optional[int] + ) -> None: + """ + Write BytesIO payload with a specific content length constraint. + + Args: + writer: An AbstractStreamWriter instance that handles the actual writing + content_length: Maximum number of bytes to write (None for unlimited) + + This implementation is specifically optimized for BytesIO objects: + + 1. Reads content in chunks to maintain memory efficiency + 2. Yields control back to the event loop periodically to prevent blocking + when dealing with large BytesIO objects + 3. Respects content_length constraints when specified + 4. Properly cleans up by closing the BytesIO object when done or on error + + The periodic yielding to the event loop is important for maintaining + responsiveness when processing large in-memory buffers. + + """ + self._set_or_restore_start_position() + loop_count = 0 + remaining_bytes = content_length + while chunk := self._value.read(READ_SIZE): + if loop_count > 0: + # Avoid blocking the event loop + # if they pass a large BytesIO object + # and we are not in the first iteration + # of the loop + await asyncio.sleep(0) + if remaining_bytes is None: + await writer.write(chunk) + else: + await writer.write(chunk[:remaining_bytes]) + remaining_bytes -= len(chunk) + if remaining_bytes <= 0: + return + loop_count += 1 + + async def as_bytes(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: + """ + Return bytes representation of the value. + + This method reads the entire BytesIO content and returns it as bytes. + It is equivalent to accessing the _value attribute directly. + """ + self._set_or_restore_start_position() + return self._value.read() + + async def close(self) -> None: + """ + Close the BytesIO payload. + + This does nothing since BytesIO is in-memory and does not require explicit closing. + """ + + +class BufferedReaderPayload(IOBasePayload): + _value: io.BufferedIOBase + # _autoclose = False (inherited) - Has buffered file handle that needs explicit closing + + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + self._set_or_restore_start_position() + return self._value.read().decode(encoding, errors) + + +class JsonPayload(BytesPayload): + def __init__( + self, + value: Any, + encoding: str = "utf-8", + content_type: str = "application/json", + dumps: JSONEncoder = json.dumps, + *args: Any, + **kwargs: Any, + ) -> None: + + super().__init__( + dumps(value).encode(encoding), + content_type=content_type, + encoding=encoding, + *args, + **kwargs, + ) + + +if TYPE_CHECKING: + from typing import AsyncIterable, AsyncIterator + + _AsyncIterator = AsyncIterator[bytes] + _AsyncIterable = AsyncIterable[bytes] +else: + from collections.abc import AsyncIterable, AsyncIterator + + _AsyncIterator = AsyncIterator + _AsyncIterable = AsyncIterable + + +class AsyncIterablePayload(Payload): + + _iter: Optional[_AsyncIterator] = None + _value: _AsyncIterable + _cached_chunks: Optional[List[bytes]] = None + # _consumed stays False to allow reuse with cached content + _autoclose = True # Iterator doesn't need explicit closing + + def __init__(self, value: _AsyncIterable, *args: Any, **kwargs: Any) -> None: + if not isinstance(value, AsyncIterable): + raise TypeError( + "value argument must support " + "collections.abc.AsyncIterable interface, " + "got {!r}".format(type(value)) + ) + + if "content_type" not in kwargs: + kwargs["content_type"] = "application/octet-stream" + + super().__init__(value, *args, **kwargs) + + self._iter = value.__aiter__() + + async def write(self, writer: AbstractStreamWriter) -> None: + """ + Write the entire async iterable payload to the writer stream. + + Args: + writer: An AbstractStreamWriter instance that handles the actual writing + + This method iterates through the async iterable and writes each chunk + to the writer without any length constraint. + + Note: + For new implementations that need length control, use write_with_length() directly. + This method is maintained for backwards compatibility with existing code. + + """ + await self.write_with_length(writer, None) + + async def write_with_length( + self, writer: AbstractStreamWriter, content_length: Optional[int] + ) -> None: + """ + Write async iterable payload with a specific content length constraint. + + Args: + writer: An AbstractStreamWriter instance that handles the actual writing + content_length: Maximum number of bytes to write (None for unlimited) + + This implementation handles streaming of async iterable content with length constraints: + + 1. If cached chunks are available, writes from them + 2. Otherwise iterates through the async iterable one chunk at a time + 3. Respects content_length constraints when specified + 4. Does NOT generate cache - that's done by as_bytes() + + """ + # If we have cached chunks, use them + if self._cached_chunks is not None: + remaining_bytes = content_length + for chunk in self._cached_chunks: + if remaining_bytes is None: + await writer.write(chunk) + elif remaining_bytes > 0: + await writer.write(chunk[:remaining_bytes]) + remaining_bytes -= len(chunk) + else: + break + return + + # If iterator is exhausted and we don't have cached chunks, nothing to write + if self._iter is None: + return + + # Stream from the iterator + remaining_bytes = content_length + + try: + while True: + if sys.version_info >= (3, 10): + chunk = await anext(self._iter) + else: + chunk = await self._iter.__anext__() + if remaining_bytes is None: + await writer.write(chunk) + # If we have a content length limit + elif remaining_bytes > 0: + await writer.write(chunk[:remaining_bytes]) + remaining_bytes -= len(chunk) + # We still want to exhaust the iterator even + # if we have reached the content length limit + # since the file handle may not get closed by + # the iterator if we don't do this + except StopAsyncIteration: + # Iterator is exhausted + self._iter = None + self._consumed = True # Mark as consumed when streamed without caching + + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + """Decode the payload content as a string if cached chunks are available.""" + if self._cached_chunks is not None: + return b"".join(self._cached_chunks).decode(encoding, errors) + raise TypeError("Unable to decode - content not cached. Call as_bytes() first.") + + async def as_bytes(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: + """ + Return bytes representation of the value. + + This method reads the entire async iterable content and returns it as bytes. + It generates and caches the chunks for future reuse. + """ + # If we have cached chunks, return them joined + if self._cached_chunks is not None: + return b"".join(self._cached_chunks) + + # If iterator is exhausted and no cache, return empty + if self._iter is None: + return b"" + + # Read all chunks and cache them + chunks: List[bytes] = [] + async for chunk in self._iter: + chunks.append(chunk) + + # Iterator is exhausted, cache the chunks + self._iter = None + self._cached_chunks = chunks + # Keep _consumed as False to allow reuse with cached chunks + + return b"".join(chunks) + + +class StreamReaderPayload(AsyncIterablePayload): + def __init__(self, value: StreamReader, *args: Any, **kwargs: Any) -> None: + super().__init__(value.iter_any(), *args, **kwargs) + + +PAYLOAD_REGISTRY = PayloadRegistry() +PAYLOAD_REGISTRY.register(BytesPayload, (bytes, bytearray, memoryview)) +PAYLOAD_REGISTRY.register(StringPayload, str) +PAYLOAD_REGISTRY.register(StringIOPayload, io.StringIO) +PAYLOAD_REGISTRY.register(TextIOPayload, io.TextIOBase) +PAYLOAD_REGISTRY.register(BytesIOPayload, io.BytesIO) +PAYLOAD_REGISTRY.register(BufferedReaderPayload, (io.BufferedReader, io.BufferedRandom)) +PAYLOAD_REGISTRY.register(IOBasePayload, io.IOBase) +PAYLOAD_REGISTRY.register(StreamReaderPayload, StreamReader) +# try_last for giving a chance to more specialized async interables like +# multipart.BodyPartReaderPayload override the default +PAYLOAD_REGISTRY.register(AsyncIterablePayload, AsyncIterable, order=Order.try_last) diff --git a/.venv/lib/python3.9/site-packages/aiohttp/payload_streamer.py b/.venv/lib/python3.9/site-packages/aiohttp/payload_streamer.py new file mode 100644 index 0000000..831fdc0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/payload_streamer.py @@ -0,0 +1,78 @@ +""" +Payload implementation for coroutines as data provider. + +As a simple case, you can upload data from file:: + + @aiohttp.streamer + async def file_sender(writer, file_name=None): + with open(file_name, 'rb') as f: + chunk = f.read(2**16) + while chunk: + await writer.write(chunk) + + chunk = f.read(2**16) + +Then you can use `file_sender` like this: + + async with session.post('http://httpbin.org/post', + data=file_sender(file_name='huge_file')) as resp: + print(await resp.text()) + +..note:: Coroutine must accept `writer` as first argument + +""" + +import types +import warnings +from typing import Any, Awaitable, Callable, Dict, Tuple + +from .abc import AbstractStreamWriter +from .payload import Payload, payload_type + +__all__ = ("streamer",) + + +class _stream_wrapper: + def __init__( + self, + coro: Callable[..., Awaitable[None]], + args: Tuple[Any, ...], + kwargs: Dict[str, Any], + ) -> None: + self.coro = types.coroutine(coro) + self.args = args + self.kwargs = kwargs + + async def __call__(self, writer: AbstractStreamWriter) -> None: + await self.coro(writer, *self.args, **self.kwargs) + + +class streamer: + def __init__(self, coro: Callable[..., Awaitable[None]]) -> None: + warnings.warn( + "@streamer is deprecated, use async generators instead", + DeprecationWarning, + stacklevel=2, + ) + self.coro = coro + + def __call__(self, *args: Any, **kwargs: Any) -> _stream_wrapper: + return _stream_wrapper(self.coro, args, kwargs) + + +@payload_type(_stream_wrapper) +class StreamWrapperPayload(Payload): + async def write(self, writer: AbstractStreamWriter) -> None: + await self._value(writer) + + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + raise TypeError("Unable to decode.") + + +@payload_type(streamer) +class StreamPayload(StreamWrapperPayload): + def __init__(self, value: Any, *args: Any, **kwargs: Any) -> None: + super().__init__(value(), *args, **kwargs) + + async def write(self, writer: AbstractStreamWriter) -> None: + await self._value(writer) diff --git a/.venv/lib/python3.9/site-packages/aiohttp/py.typed b/.venv/lib/python3.9/site-packages/aiohttp/py.typed new file mode 100644 index 0000000..f5642f7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/py.typed @@ -0,0 +1 @@ +Marker diff --git a/.venv/lib/python3.9/site-packages/aiohttp/pytest_plugin.py b/.venv/lib/python3.9/site-packages/aiohttp/pytest_plugin.py new file mode 100644 index 0000000..7d59fe8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/pytest_plugin.py @@ -0,0 +1,444 @@ +import asyncio +import contextlib +import inspect +import warnings +from typing import ( + Any, + Awaitable, + Callable, + Dict, + Iterator, + Optional, + Protocol, + Union, + overload, +) + +import pytest + +from .test_utils import ( + BaseTestServer, + RawTestServer, + TestClient, + TestServer, + loop_context, + setup_test_loop, + teardown_test_loop, + unused_port as _unused_port, +) +from .web import Application, BaseRequest, Request +from .web_protocol import _RequestHandler + +try: + import uvloop +except ImportError: # pragma: no cover + uvloop = None # type: ignore[assignment] + + +class AiohttpClient(Protocol): + @overload + async def __call__( + self, + __param: Application, + *, + server_kwargs: Optional[Dict[str, Any]] = None, + **kwargs: Any, + ) -> TestClient[Request, Application]: ... + @overload + async def __call__( + self, + __param: BaseTestServer, + *, + server_kwargs: Optional[Dict[str, Any]] = None, + **kwargs: Any, + ) -> TestClient[BaseRequest, None]: ... + + +class AiohttpServer(Protocol): + def __call__( + self, app: Application, *, port: Optional[int] = None, **kwargs: Any + ) -> Awaitable[TestServer]: ... + + +class AiohttpRawServer(Protocol): + def __call__( + self, handler: _RequestHandler, *, port: Optional[int] = None, **kwargs: Any + ) -> Awaitable[RawTestServer]: ... + + +def pytest_addoption(parser): # type: ignore[no-untyped-def] + parser.addoption( + "--aiohttp-fast", + action="store_true", + default=False, + help="run tests faster by disabling extra checks", + ) + parser.addoption( + "--aiohttp-loop", + action="store", + default="pyloop", + help="run tests with specific loop: pyloop, uvloop or all", + ) + parser.addoption( + "--aiohttp-enable-loop-debug", + action="store_true", + default=False, + help="enable event loop debug mode", + ) + + +def pytest_fixture_setup(fixturedef): # type: ignore[no-untyped-def] + """Set up pytest fixture. + + Allow fixtures to be coroutines. Run coroutine fixtures in an event loop. + """ + func = fixturedef.func + + if inspect.isasyncgenfunction(func): + # async generator fixture + is_async_gen = True + elif inspect.iscoroutinefunction(func): + # regular async fixture + is_async_gen = False + else: + # not an async fixture, nothing to do + return + + strip_request = False + if "request" not in fixturedef.argnames: + fixturedef.argnames += ("request",) + strip_request = True + + def wrapper(*args, **kwargs): # type: ignore[no-untyped-def] + request = kwargs["request"] + if strip_request: + del kwargs["request"] + + # if neither the fixture nor the test use the 'loop' fixture, + # 'getfixturevalue' will fail because the test is not parameterized + # (this can be removed someday if 'loop' is no longer parameterized) + if "loop" not in request.fixturenames: + raise Exception( + "Asynchronous fixtures must depend on the 'loop' fixture or " + "be used in tests depending from it." + ) + + _loop = request.getfixturevalue("loop") + + if is_async_gen: + # for async generators, we need to advance the generator once, + # then advance it again in a finalizer + gen = func(*args, **kwargs) + + def finalizer(): # type: ignore[no-untyped-def] + try: + return _loop.run_until_complete(gen.__anext__()) + except StopAsyncIteration: + pass + + request.addfinalizer(finalizer) + return _loop.run_until_complete(gen.__anext__()) + else: + return _loop.run_until_complete(func(*args, **kwargs)) + + fixturedef.func = wrapper + + +@pytest.fixture +def fast(request): # type: ignore[no-untyped-def] + """--fast config option""" + return request.config.getoption("--aiohttp-fast") + + +@pytest.fixture +def loop_debug(request): # type: ignore[no-untyped-def] + """--enable-loop-debug config option""" + return request.config.getoption("--aiohttp-enable-loop-debug") + + +@contextlib.contextmanager +def _runtime_warning_context(): # type: ignore[no-untyped-def] + """Context manager which checks for RuntimeWarnings. + + This exists specifically to + avoid "coroutine 'X' was never awaited" warnings being missed. + + If RuntimeWarnings occur in the context a RuntimeError is raised. + """ + with warnings.catch_warnings(record=True) as _warnings: + yield + rw = [ + "{w.filename}:{w.lineno}:{w.message}".format(w=w) + for w in _warnings + if w.category == RuntimeWarning + ] + if rw: + raise RuntimeError( + "{} Runtime Warning{},\n{}".format( + len(rw), "" if len(rw) == 1 else "s", "\n".join(rw) + ) + ) + + +@contextlib.contextmanager +def _passthrough_loop_context(loop, fast=False): # type: ignore[no-untyped-def] + """Passthrough loop context. + + Sets up and tears down a loop unless one is passed in via the loop + argument when it's passed straight through. + """ + if loop: + # loop already exists, pass it straight through + yield loop + else: + # this shadows loop_context's standard behavior + loop = setup_test_loop() + yield loop + teardown_test_loop(loop, fast=fast) + + +def pytest_pycollect_makeitem(collector, name, obj): # type: ignore[no-untyped-def] + """Fix pytest collecting for coroutines.""" + if collector.funcnamefilter(name) and inspect.iscoroutinefunction(obj): + return list(collector._genfunctions(name, obj)) + + +def pytest_pyfunc_call(pyfuncitem): # type: ignore[no-untyped-def] + """Run coroutines in an event loop instead of a normal function call.""" + fast = pyfuncitem.config.getoption("--aiohttp-fast") + if inspect.iscoroutinefunction(pyfuncitem.function): + existing_loop = ( + pyfuncitem.funcargs.get("proactor_loop") + or pyfuncitem.funcargs.get("selector_loop") + or pyfuncitem.funcargs.get("uvloop_loop") + or pyfuncitem.funcargs.get("loop", None) + ) + + with _runtime_warning_context(): + with _passthrough_loop_context(existing_loop, fast=fast) as _loop: + testargs = { + arg: pyfuncitem.funcargs[arg] + for arg in pyfuncitem._fixtureinfo.argnames + } + _loop.run_until_complete(pyfuncitem.obj(**testargs)) + + return True + + +def pytest_generate_tests(metafunc): # type: ignore[no-untyped-def] + if "loop_factory" not in metafunc.fixturenames: + return + + loops = metafunc.config.option.aiohttp_loop + avail_factories: dict[str, Callable[[], asyncio.AbstractEventLoop]] + avail_factories = {"pyloop": asyncio.new_event_loop} + + if uvloop is not None: # pragma: no cover + avail_factories["uvloop"] = uvloop.new_event_loop + + if loops == "all": + loops = "pyloop,uvloop?" + + factories = {} # type: ignore[var-annotated] + for name in loops.split(","): + required = not name.endswith("?") + name = name.strip(" ?") + if name not in avail_factories: # pragma: no cover + if required: + raise ValueError( + "Unknown loop '%s', available loops: %s" + % (name, list(factories.keys())) + ) + else: + continue + factories[name] = avail_factories[name] + metafunc.parametrize( + "loop_factory", list(factories.values()), ids=list(factories.keys()) + ) + + +@pytest.fixture +def loop( + loop_factory: Callable[[], asyncio.AbstractEventLoop], + fast: bool, + loop_debug: bool, +) -> Iterator[asyncio.AbstractEventLoop]: + """Return an instance of the event loop.""" + with loop_context(loop_factory, fast=fast) as _loop: + if loop_debug: + _loop.set_debug(True) # pragma: no cover + asyncio.set_event_loop(_loop) + yield _loop + + +@pytest.fixture +def proactor_loop() -> Iterator[asyncio.AbstractEventLoop]: + factory = asyncio.ProactorEventLoop # type: ignore[attr-defined] + + with loop_context(factory) as _loop: + asyncio.set_event_loop(_loop) + yield _loop + + +@pytest.fixture +def unused_port(aiohttp_unused_port: Callable[[], int]) -> Callable[[], int]: + warnings.warn( + "Deprecated, use aiohttp_unused_port fixture instead", + DeprecationWarning, + stacklevel=2, + ) + return aiohttp_unused_port + + +@pytest.fixture +def aiohttp_unused_port() -> Callable[[], int]: + """Return a port that is unused on the current host.""" + return _unused_port + + +@pytest.fixture +def aiohttp_server(loop: asyncio.AbstractEventLoop) -> Iterator[AiohttpServer]: + """Factory to create a TestServer instance, given an app. + + aiohttp_server(app, **kwargs) + """ + servers = [] + + async def go( + app: Application, + *, + host: str = "127.0.0.1", + port: Optional[int] = None, + **kwargs: Any, + ) -> TestServer: + server = TestServer(app, host=host, port=port) + await server.start_server(loop=loop, **kwargs) + servers.append(server) + return server + + yield go + + async def finalize() -> None: + while servers: + await servers.pop().close() + + loop.run_until_complete(finalize()) + + +@pytest.fixture +def test_server(aiohttp_server): # type: ignore[no-untyped-def] # pragma: no cover + warnings.warn( + "Deprecated, use aiohttp_server fixture instead", + DeprecationWarning, + stacklevel=2, + ) + return aiohttp_server + + +@pytest.fixture +def aiohttp_raw_server(loop: asyncio.AbstractEventLoop) -> Iterator[AiohttpRawServer]: + """Factory to create a RawTestServer instance, given a web handler. + + aiohttp_raw_server(handler, **kwargs) + """ + servers = [] + + async def go( + handler: _RequestHandler, *, port: Optional[int] = None, **kwargs: Any + ) -> RawTestServer: + server = RawTestServer(handler, port=port) + await server.start_server(loop=loop, **kwargs) + servers.append(server) + return server + + yield go + + async def finalize() -> None: + while servers: + await servers.pop().close() + + loop.run_until_complete(finalize()) + + +@pytest.fixture +def raw_test_server( # type: ignore[no-untyped-def] # pragma: no cover + aiohttp_raw_server, +): + warnings.warn( + "Deprecated, use aiohttp_raw_server fixture instead", + DeprecationWarning, + stacklevel=2, + ) + return aiohttp_raw_server + + +@pytest.fixture +def aiohttp_client(loop: asyncio.AbstractEventLoop) -> Iterator[AiohttpClient]: + """Factory to create a TestClient instance. + + aiohttp_client(app, **kwargs) + aiohttp_client(server, **kwargs) + aiohttp_client(raw_server, **kwargs) + """ + clients = [] + + @overload + async def go( + __param: Application, + *, + server_kwargs: Optional[Dict[str, Any]] = None, + **kwargs: Any, + ) -> TestClient[Request, Application]: ... + + @overload + async def go( + __param: BaseTestServer, + *, + server_kwargs: Optional[Dict[str, Any]] = None, + **kwargs: Any, + ) -> TestClient[BaseRequest, None]: ... + + async def go( + __param: Union[Application, BaseTestServer], + *args: Any, + server_kwargs: Optional[Dict[str, Any]] = None, + **kwargs: Any, + ) -> TestClient[Any, Any]: + if isinstance(__param, Callable) and not isinstance( # type: ignore[arg-type] + __param, (Application, BaseTestServer) + ): + __param = __param(loop, *args, **kwargs) + kwargs = {} + else: + assert not args, "args should be empty" + + if isinstance(__param, Application): + server_kwargs = server_kwargs or {} + server = TestServer(__param, loop=loop, **server_kwargs) + client = TestClient(server, loop=loop, **kwargs) + elif isinstance(__param, BaseTestServer): + client = TestClient(__param, loop=loop, **kwargs) + else: + raise ValueError("Unknown argument type: %r" % type(__param)) + + await client.start_server() + clients.append(client) + return client + + yield go + + async def finalize() -> None: + while clients: + await clients.pop().close() + + loop.run_until_complete(finalize()) + + +@pytest.fixture +def test_client(aiohttp_client): # type: ignore[no-untyped-def] # pragma: no cover + warnings.warn( + "Deprecated, use aiohttp_client fixture instead", + DeprecationWarning, + stacklevel=2, + ) + return aiohttp_client diff --git a/.venv/lib/python3.9/site-packages/aiohttp/resolver.py b/.venv/lib/python3.9/site-packages/aiohttp/resolver.py new file mode 100644 index 0000000..b20e567 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/resolver.py @@ -0,0 +1,274 @@ +import asyncio +import socket +import weakref +from typing import Any, Dict, Final, List, Optional, Tuple, Type, Union + +from .abc import AbstractResolver, ResolveResult + +__all__ = ("ThreadedResolver", "AsyncResolver", "DefaultResolver") + + +try: + import aiodns + + aiodns_default = hasattr(aiodns.DNSResolver, "getaddrinfo") +except ImportError: # pragma: no cover + aiodns = None # type: ignore[assignment] + aiodns_default = False + + +_NUMERIC_SOCKET_FLAGS = socket.AI_NUMERICHOST | socket.AI_NUMERICSERV +_NAME_SOCKET_FLAGS = socket.NI_NUMERICHOST | socket.NI_NUMERICSERV +_AI_ADDRCONFIG = socket.AI_ADDRCONFIG +if hasattr(socket, "AI_MASK"): + _AI_ADDRCONFIG &= socket.AI_MASK + + +class ThreadedResolver(AbstractResolver): + """Threaded resolver. + + Uses an Executor for synchronous getaddrinfo() calls. + concurrent.futures.ThreadPoolExecutor is used by default. + """ + + def __init__(self, loop: Optional[asyncio.AbstractEventLoop] = None) -> None: + self._loop = loop or asyncio.get_running_loop() + + async def resolve( + self, host: str, port: int = 0, family: socket.AddressFamily = socket.AF_INET + ) -> List[ResolveResult]: + infos = await self._loop.getaddrinfo( + host, + port, + type=socket.SOCK_STREAM, + family=family, + flags=_AI_ADDRCONFIG, + ) + + hosts: List[ResolveResult] = [] + for family, _, proto, _, address in infos: + if family == socket.AF_INET6: + if len(address) < 3: + # IPv6 is not supported by Python build, + # or IPv6 is not enabled in the host + continue + if address[3]: + # This is essential for link-local IPv6 addresses. + # LL IPv6 is a VERY rare case. Strictly speaking, we should use + # getnameinfo() unconditionally, but performance makes sense. + resolved_host, _port = await self._loop.getnameinfo( + address, _NAME_SOCKET_FLAGS + ) + port = int(_port) + else: + resolved_host, port = address[:2] + else: # IPv4 + assert family == socket.AF_INET + resolved_host, port = address # type: ignore[misc] + hosts.append( + ResolveResult( + hostname=host, + host=resolved_host, + port=port, + family=family, + proto=proto, + flags=_NUMERIC_SOCKET_FLAGS, + ) + ) + + return hosts + + async def close(self) -> None: + pass + + +class AsyncResolver(AbstractResolver): + """Use the `aiodns` package to make asynchronous DNS lookups""" + + def __init__( + self, + loop: Optional[asyncio.AbstractEventLoop] = None, + *args: Any, + **kwargs: Any, + ) -> None: + if aiodns is None: + raise RuntimeError("Resolver requires aiodns library") + + self._loop = loop or asyncio.get_running_loop() + self._manager: Optional[_DNSResolverManager] = None + # If custom args are provided, create a dedicated resolver instance + # This means each AsyncResolver with custom args gets its own + # aiodns.DNSResolver instance + if args or kwargs: + self._resolver = aiodns.DNSResolver(*args, **kwargs) + return + # Use the shared resolver from the manager for default arguments + self._manager = _DNSResolverManager() + self._resolver = self._manager.get_resolver(self, self._loop) + + if not hasattr(self._resolver, "gethostbyname"): + # aiodns 1.1 is not available, fallback to DNSResolver.query + self.resolve = self._resolve_with_query # type: ignore + + async def resolve( + self, host: str, port: int = 0, family: socket.AddressFamily = socket.AF_INET + ) -> List[ResolveResult]: + try: + resp = await self._resolver.getaddrinfo( + host, + port=port, + type=socket.SOCK_STREAM, + family=family, + flags=_AI_ADDRCONFIG, + ) + except aiodns.error.DNSError as exc: + msg = exc.args[1] if len(exc.args) >= 1 else "DNS lookup failed" + raise OSError(None, msg) from exc + hosts: List[ResolveResult] = [] + for node in resp.nodes: + address: Union[Tuple[bytes, int], Tuple[bytes, int, int, int]] = node.addr + family = node.family + if family == socket.AF_INET6: + if len(address) > 3 and address[3]: + # This is essential for link-local IPv6 addresses. + # LL IPv6 is a VERY rare case. Strictly speaking, we should use + # getnameinfo() unconditionally, but performance makes sense. + result = await self._resolver.getnameinfo( + (address[0].decode("ascii"), *address[1:]), + _NAME_SOCKET_FLAGS, + ) + resolved_host = result.node + else: + resolved_host = address[0].decode("ascii") + port = address[1] + else: # IPv4 + assert family == socket.AF_INET + resolved_host = address[0].decode("ascii") + port = address[1] + hosts.append( + ResolveResult( + hostname=host, + host=resolved_host, + port=port, + family=family, + proto=0, + flags=_NUMERIC_SOCKET_FLAGS, + ) + ) + + if not hosts: + raise OSError(None, "DNS lookup failed") + + return hosts + + async def _resolve_with_query( + self, host: str, port: int = 0, family: int = socket.AF_INET + ) -> List[Dict[str, Any]]: + qtype: Final = "AAAA" if family == socket.AF_INET6 else "A" + + try: + resp = await self._resolver.query(host, qtype) + except aiodns.error.DNSError as exc: + msg = exc.args[1] if len(exc.args) >= 1 else "DNS lookup failed" + raise OSError(None, msg) from exc + + hosts = [] + for rr in resp: + hosts.append( + { + "hostname": host, + "host": rr.host, + "port": port, + "family": family, + "proto": 0, + "flags": socket.AI_NUMERICHOST, + } + ) + + if not hosts: + raise OSError(None, "DNS lookup failed") + + return hosts + + async def close(self) -> None: + if self._manager: + # Release the resolver from the manager if using the shared resolver + self._manager.release_resolver(self, self._loop) + self._manager = None # Clear reference to manager + self._resolver = None # type: ignore[assignment] # Clear reference to resolver + return + # Otherwise cancel our dedicated resolver + if self._resolver is not None: + self._resolver.cancel() + self._resolver = None # type: ignore[assignment] # Clear reference + + +class _DNSResolverManager: + """Manager for aiodns.DNSResolver objects. + + This class manages shared aiodns.DNSResolver instances + with no custom arguments across different event loops. + """ + + _instance: Optional["_DNSResolverManager"] = None + + def __new__(cls) -> "_DNSResolverManager": + if cls._instance is None: + cls._instance = super().__new__(cls) + cls._instance._init() + return cls._instance + + def _init(self) -> None: + # Use WeakKeyDictionary to allow event loops to be garbage collected + self._loop_data: weakref.WeakKeyDictionary[ + asyncio.AbstractEventLoop, + tuple["aiodns.DNSResolver", weakref.WeakSet["AsyncResolver"]], + ] = weakref.WeakKeyDictionary() + + def get_resolver( + self, client: "AsyncResolver", loop: asyncio.AbstractEventLoop + ) -> "aiodns.DNSResolver": + """Get or create the shared aiodns.DNSResolver instance for a specific event loop. + + Args: + client: The AsyncResolver instance requesting the resolver. + This is required to track resolver usage. + loop: The event loop to use for the resolver. + """ + # Create a new resolver and client set for this loop if it doesn't exist + if loop not in self._loop_data: + resolver = aiodns.DNSResolver(loop=loop) + client_set: weakref.WeakSet["AsyncResolver"] = weakref.WeakSet() + self._loop_data[loop] = (resolver, client_set) + else: + # Get the existing resolver and client set + resolver, client_set = self._loop_data[loop] + + # Register this client with the loop + client_set.add(client) + return resolver + + def release_resolver( + self, client: "AsyncResolver", loop: asyncio.AbstractEventLoop + ) -> None: + """Release the resolver for an AsyncResolver client when it's closed. + + Args: + client: The AsyncResolver instance to release. + loop: The event loop the resolver was using. + """ + # Remove client from its loop's tracking + current_loop_data = self._loop_data.get(loop) + if current_loop_data is None: + return + resolver, client_set = current_loop_data + client_set.discard(client) + # If no more clients for this loop, cancel and remove its resolver + if not client_set: + if resolver is not None: + resolver.cancel() + del self._loop_data[loop] + + +_DefaultType = Type[Union[AsyncResolver, ThreadedResolver]] +DefaultResolver: _DefaultType = AsyncResolver if aiodns_default else ThreadedResolver diff --git a/.venv/lib/python3.9/site-packages/aiohttp/streams.py b/.venv/lib/python3.9/site-packages/aiohttp/streams.py new file mode 100644 index 0000000..6cc74fc --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/streams.py @@ -0,0 +1,758 @@ +import asyncio +import collections +import warnings +from typing import ( + Awaitable, + Callable, + Deque, + Final, + Generic, + List, + Optional, + Tuple, + TypeVar, +) + +from .base_protocol import BaseProtocol +from .helpers import ( + _EXC_SENTINEL, + BaseTimerContext, + TimerNoop, + set_exception, + set_result, +) +from .log import internal_logger + +__all__ = ( + "EMPTY_PAYLOAD", + "EofStream", + "StreamReader", + "DataQueue", +) + +_T = TypeVar("_T") + + +class EofStream(Exception): + """eof stream indication.""" + + +class AsyncStreamIterator(Generic[_T]): + + __slots__ = ("read_func",) + + def __init__(self, read_func: Callable[[], Awaitable[_T]]) -> None: + self.read_func = read_func + + def __aiter__(self) -> "AsyncStreamIterator[_T]": + return self + + async def __anext__(self) -> _T: + try: + rv = await self.read_func() + except EofStream: + raise StopAsyncIteration + if rv == b"": + raise StopAsyncIteration + return rv + + +class ChunkTupleAsyncStreamIterator: + + __slots__ = ("_stream",) + + def __init__(self, stream: "StreamReader") -> None: + self._stream = stream + + def __aiter__(self) -> "ChunkTupleAsyncStreamIterator": + return self + + async def __anext__(self) -> Tuple[bytes, bool]: + rv = await self._stream.readchunk() + if rv == (b"", False): + raise StopAsyncIteration + return rv + + +class AsyncStreamReaderMixin: + + __slots__ = () + + def __aiter__(self) -> AsyncStreamIterator[bytes]: + return AsyncStreamIterator(self.readline) # type: ignore[attr-defined] + + def iter_chunked(self, n: int) -> AsyncStreamIterator[bytes]: + """Returns an asynchronous iterator that yields chunks of size n.""" + return AsyncStreamIterator(lambda: self.read(n)) # type: ignore[attr-defined] + + def iter_any(self) -> AsyncStreamIterator[bytes]: + """Yield all available data as soon as it is received.""" + return AsyncStreamIterator(self.readany) # type: ignore[attr-defined] + + def iter_chunks(self) -> ChunkTupleAsyncStreamIterator: + """Yield chunks of data as they are received by the server. + + The yielded objects are tuples + of (bytes, bool) as returned by the StreamReader.readchunk method. + """ + return ChunkTupleAsyncStreamIterator(self) # type: ignore[arg-type] + + +class StreamReader(AsyncStreamReaderMixin): + """An enhancement of asyncio.StreamReader. + + Supports asynchronous iteration by line, chunk or as available:: + + async for line in reader: + ... + async for chunk in reader.iter_chunked(1024): + ... + async for slice in reader.iter_any(): + ... + + """ + + __slots__ = ( + "_protocol", + "_low_water", + "_high_water", + "_low_water_chunks", + "_high_water_chunks", + "_loop", + "_size", + "_cursor", + "_http_chunk_splits", + "_buffer", + "_buffer_offset", + "_eof", + "_waiter", + "_eof_waiter", + "_exception", + "_timer", + "_eof_callbacks", + "_eof_counter", + "total_bytes", + "total_compressed_bytes", + ) + + def __init__( + self, + protocol: BaseProtocol, + limit: int, + *, + timer: Optional[BaseTimerContext] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + ) -> None: + self._protocol = protocol + self._low_water = limit + self._high_water = limit * 2 + if loop is None: + loop = asyncio.get_event_loop() + # Ensure high_water_chunks >= 3 so it's always > low_water_chunks. + self._high_water_chunks = max(3, limit // 4) + # Use max(2, ...) because there's always at least 1 chunk split remaining + # (the current position), so we need low_water >= 2 to allow resume. + self._low_water_chunks = max(2, self._high_water_chunks // 2) + self._loop = loop + self._size = 0 + self._cursor = 0 + self._http_chunk_splits: Optional[Deque[int]] = None + self._buffer: Deque[bytes] = collections.deque() + self._buffer_offset = 0 + self._eof = False + self._waiter: Optional[asyncio.Future[None]] = None + self._eof_waiter: Optional[asyncio.Future[None]] = None + self._exception: Optional[BaseException] = None + self._timer = TimerNoop() if timer is None else timer + self._eof_callbacks: List[Callable[[], None]] = [] + self._eof_counter = 0 + self.total_bytes = 0 + self.total_compressed_bytes: Optional[int] = None + + def __repr__(self) -> str: + info = [self.__class__.__name__] + if self._size: + info.append("%d bytes" % self._size) + if self._eof: + info.append("eof") + if self._low_water != 2**16: # default limit + info.append("low=%d high=%d" % (self._low_water, self._high_water)) + if self._waiter: + info.append("w=%r" % self._waiter) + if self._exception: + info.append("e=%r" % self._exception) + return "<%s>" % " ".join(info) + + def get_read_buffer_limits(self) -> Tuple[int, int]: + return (self._low_water, self._high_water) + + def exception(self) -> Optional[BaseException]: + return self._exception + + def set_exception( + self, + exc: BaseException, + exc_cause: BaseException = _EXC_SENTINEL, + ) -> None: + self._exception = exc + self._eof_callbacks.clear() + + waiter = self._waiter + if waiter is not None: + self._waiter = None + set_exception(waiter, exc, exc_cause) + + waiter = self._eof_waiter + if waiter is not None: + self._eof_waiter = None + set_exception(waiter, exc, exc_cause) + + def on_eof(self, callback: Callable[[], None]) -> None: + if self._eof: + try: + callback() + except Exception: + internal_logger.exception("Exception in eof callback") + else: + self._eof_callbacks.append(callback) + + def feed_eof(self) -> None: + self._eof = True + + waiter = self._waiter + if waiter is not None: + self._waiter = None + set_result(waiter, None) + + waiter = self._eof_waiter + if waiter is not None: + self._eof_waiter = None + set_result(waiter, None) + + if self._protocol._reading_paused: + self._protocol.resume_reading() + + for cb in self._eof_callbacks: + try: + cb() + except Exception: + internal_logger.exception("Exception in eof callback") + + self._eof_callbacks.clear() + + def is_eof(self) -> bool: + """Return True if 'feed_eof' was called.""" + return self._eof + + def at_eof(self) -> bool: + """Return True if the buffer is empty and 'feed_eof' was called.""" + return self._eof and not self._buffer + + async def wait_eof(self) -> None: + if self._eof: + return + + assert self._eof_waiter is None + self._eof_waiter = self._loop.create_future() + try: + await self._eof_waiter + finally: + self._eof_waiter = None + + @property + def total_raw_bytes(self) -> int: + if self.total_compressed_bytes is None: + return self.total_bytes + return self.total_compressed_bytes + + def unread_data(self, data: bytes) -> None: + """rollback reading some data from stream, inserting it to buffer head.""" + warnings.warn( + "unread_data() is deprecated " + "and will be removed in future releases (#3260)", + DeprecationWarning, + stacklevel=2, + ) + if not data: + return + + if self._buffer_offset: + self._buffer[0] = self._buffer[0][self._buffer_offset :] + self._buffer_offset = 0 + self._size += len(data) + self._cursor -= len(data) + self._buffer.appendleft(data) + self._eof_counter = 0 + + # TODO: size is ignored, remove the param later + def feed_data(self, data: bytes, size: int = 0) -> None: + assert not self._eof, "feed_data after feed_eof" + + if not data: + return + + data_len = len(data) + self._size += data_len + self._buffer.append(data) + self.total_bytes += data_len + + waiter = self._waiter + if waiter is not None: + self._waiter = None + set_result(waiter, None) + + if self._size > self._high_water and not self._protocol._reading_paused: + self._protocol.pause_reading() + + def begin_http_chunk_receiving(self) -> None: + if self._http_chunk_splits is None: + if self.total_bytes: + raise RuntimeError( + "Called begin_http_chunk_receiving when some data was already fed" + ) + self._http_chunk_splits = collections.deque() + + def end_http_chunk_receiving(self) -> None: + if self._http_chunk_splits is None: + raise RuntimeError( + "Called end_chunk_receiving without calling " + "begin_chunk_receiving first" + ) + + # self._http_chunk_splits contains logical byte offsets from start of + # the body transfer. Each offset is the offset of the end of a chunk. + # "Logical" means bytes, accessible for a user. + # If no chunks containing logical data were received, current position + # is difinitely zero. + pos = self._http_chunk_splits[-1] if self._http_chunk_splits else 0 + + if self.total_bytes == pos: + # We should not add empty chunks here. So we check for that. + # Note, when chunked + gzip is used, we can receive a chunk + # of compressed data, but that data may not be enough for gzip FSM + # to yield any uncompressed data. That's why current position may + # not change after receiving a chunk. + return + + self._http_chunk_splits.append(self.total_bytes) + + # If we get too many small chunks before self._high_water is reached, then any + # .read() call becomes computationally expensive, and could block the event loop + # for too long, hence an additional self._high_water_chunks here. + if ( + len(self._http_chunk_splits) > self._high_water_chunks + and not self._protocol._reading_paused + ): + self._protocol.pause_reading() + + # wake up readchunk when end of http chunk received + waiter = self._waiter + if waiter is not None: + self._waiter = None + set_result(waiter, None) + + async def _wait(self, func_name: str) -> None: + if not self._protocol.connected: + raise RuntimeError("Connection closed.") + + # StreamReader uses a future to link the protocol feed_data() method + # to a read coroutine. Running two read coroutines at the same time + # would have an unexpected behaviour. It would not possible to know + # which coroutine would get the next data. + if self._waiter is not None: + raise RuntimeError( + "%s() called while another coroutine is " + "already waiting for incoming data" % func_name + ) + + waiter = self._waiter = self._loop.create_future() + try: + with self._timer: + await waiter + finally: + self._waiter = None + + async def readline(self) -> bytes: + return await self.readuntil() + + async def readuntil(self, separator: bytes = b"\n") -> bytes: + seplen = len(separator) + if seplen == 0: + raise ValueError("Separator should be at least one-byte string") + + if self._exception is not None: + raise self._exception + + chunk = b"" + chunk_size = 0 + not_enough = True + + while not_enough: + while self._buffer and not_enough: + offset = self._buffer_offset + ichar = self._buffer[0].find(separator, offset) + 1 + # Read from current offset to found separator or to the end. + data = self._read_nowait_chunk( + ichar - offset + seplen - 1 if ichar else -1 + ) + chunk += data + chunk_size += len(data) + if ichar: + not_enough = False + + if chunk_size > self._high_water: + raise ValueError("Chunk too big") + + if self._eof: + break + + if not_enough: + await self._wait("readuntil") + + return chunk + + async def read(self, n: int = -1) -> bytes: + if self._exception is not None: + raise self._exception + + # migration problem; with DataQueue you have to catch + # EofStream exception, so common way is to run payload.read() inside + # infinite loop. what can cause real infinite loop with StreamReader + # lets keep this code one major release. + if __debug__: + if self._eof and not self._buffer: + self._eof_counter = getattr(self, "_eof_counter", 0) + 1 + if self._eof_counter > 5: + internal_logger.warning( + "Multiple access to StreamReader in eof state, " + "might be infinite loop.", + stack_info=True, + ) + + if not n: + return b"" + + if n < 0: + # This used to just loop creating a new waiter hoping to + # collect everything in self._buffer, but that would + # deadlock if the subprocess sends more than self.limit + # bytes. So just call self.readany() until EOF. + blocks = [] + while True: + block = await self.readany() + if not block: + break + blocks.append(block) + return b"".join(blocks) + + # TODO: should be `if` instead of `while` + # because waiter maybe triggered on chunk end, + # without feeding any data + while not self._buffer and not self._eof: + await self._wait("read") + + return self._read_nowait(n) + + async def readany(self) -> bytes: + if self._exception is not None: + raise self._exception + + # TODO: should be `if` instead of `while` + # because waiter maybe triggered on chunk end, + # without feeding any data + while not self._buffer and not self._eof: + await self._wait("readany") + + return self._read_nowait(-1) + + async def readchunk(self) -> Tuple[bytes, bool]: + """Returns a tuple of (data, end_of_http_chunk). + + When chunked transfer + encoding is used, end_of_http_chunk is a boolean indicating if the end + of the data corresponds to the end of a HTTP chunk , otherwise it is + always False. + """ + while True: + if self._exception is not None: + raise self._exception + + while self._http_chunk_splits: + pos = self._http_chunk_splits.popleft() + if pos == self._cursor: + return (b"", True) + if pos > self._cursor: + return (self._read_nowait(pos - self._cursor), True) + internal_logger.warning( + "Skipping HTTP chunk end due to data " + "consumption beyond chunk boundary" + ) + + if self._buffer: + return (self._read_nowait_chunk(-1), False) + # return (self._read_nowait(-1), False) + + if self._eof: + # Special case for signifying EOF. + # (b'', True) is not a final return value actually. + return (b"", False) + + await self._wait("readchunk") + + async def readexactly(self, n: int) -> bytes: + if self._exception is not None: + raise self._exception + + blocks: List[bytes] = [] + while n > 0: + block = await self.read(n) + if not block: + partial = b"".join(blocks) + raise asyncio.IncompleteReadError(partial, len(partial) + n) + blocks.append(block) + n -= len(block) + + return b"".join(blocks) + + def read_nowait(self, n: int = -1) -> bytes: + # default was changed to be consistent with .read(-1) + # + # I believe the most users don't know about the method and + # they are not affected. + if self._exception is not None: + raise self._exception + + if self._waiter and not self._waiter.done(): + raise RuntimeError( + "Called while some coroutine is waiting for incoming data." + ) + + return self._read_nowait(n) + + def _read_nowait_chunk(self, n: int) -> bytes: + first_buffer = self._buffer[0] + offset = self._buffer_offset + if n != -1 and len(first_buffer) - offset > n: + data = first_buffer[offset : offset + n] + self._buffer_offset += n + + elif offset: + self._buffer.popleft() + data = first_buffer[offset:] + self._buffer_offset = 0 + + else: + data = self._buffer.popleft() + + data_len = len(data) + self._size -= data_len + self._cursor += data_len + + chunk_splits = self._http_chunk_splits + # Prevent memory leak: drop useless chunk splits + while chunk_splits and chunk_splits[0] < self._cursor: + chunk_splits.popleft() + + if ( + self._protocol._reading_paused + and self._size < self._low_water + and ( + self._http_chunk_splits is None + or len(self._http_chunk_splits) < self._low_water_chunks + ) + ): + self._protocol.resume_reading() + return data + + def _read_nowait(self, n: int) -> bytes: + """Read not more than n bytes, or whole buffer if n == -1""" + self._timer.assert_timeout() + + chunks = [] + while self._buffer: + chunk = self._read_nowait_chunk(n) + chunks.append(chunk) + if n != -1: + n -= len(chunk) + if n == 0: + break + + return b"".join(chunks) if chunks else b"" + + +class EmptyStreamReader(StreamReader): # lgtm [py/missing-call-to-init] + + __slots__ = ("_read_eof_chunk",) + + def __init__(self) -> None: + self._read_eof_chunk = False + self.total_bytes = 0 + + def __repr__(self) -> str: + return "<%s>" % self.__class__.__name__ + + def exception(self) -> Optional[BaseException]: + return None + + def set_exception( + self, + exc: BaseException, + exc_cause: BaseException = _EXC_SENTINEL, + ) -> None: + pass + + def on_eof(self, callback: Callable[[], None]) -> None: + try: + callback() + except Exception: + internal_logger.exception("Exception in eof callback") + + def feed_eof(self) -> None: + pass + + def is_eof(self) -> bool: + return True + + def at_eof(self) -> bool: + return True + + async def wait_eof(self) -> None: + return + + def feed_data(self, data: bytes, n: int = 0) -> None: + pass + + async def readline(self) -> bytes: + return b"" + + async def read(self, n: int = -1) -> bytes: + return b"" + + # TODO add async def readuntil + + async def readany(self) -> bytes: + return b"" + + async def readchunk(self) -> Tuple[bytes, bool]: + if not self._read_eof_chunk: + self._read_eof_chunk = True + return (b"", False) + + return (b"", True) + + async def readexactly(self, n: int) -> bytes: + raise asyncio.IncompleteReadError(b"", n) + + def read_nowait(self, n: int = -1) -> bytes: + return b"" + + +EMPTY_PAYLOAD: Final[StreamReader] = EmptyStreamReader() + + +class DataQueue(Generic[_T]): + """DataQueue is a general-purpose blocking queue with one reader.""" + + def __init__(self, loop: asyncio.AbstractEventLoop) -> None: + self._loop = loop + self._eof = False + self._waiter: Optional[asyncio.Future[None]] = None + self._exception: Optional[BaseException] = None + self._buffer: Deque[Tuple[_T, int]] = collections.deque() + + def __len__(self) -> int: + return len(self._buffer) + + def is_eof(self) -> bool: + return self._eof + + def at_eof(self) -> bool: + return self._eof and not self._buffer + + def exception(self) -> Optional[BaseException]: + return self._exception + + def set_exception( + self, + exc: BaseException, + exc_cause: BaseException = _EXC_SENTINEL, + ) -> None: + self._eof = True + self._exception = exc + if (waiter := self._waiter) is not None: + self._waiter = None + set_exception(waiter, exc, exc_cause) + + def feed_data(self, data: _T, size: int = 0) -> None: + self._buffer.append((data, size)) + if (waiter := self._waiter) is not None: + self._waiter = None + set_result(waiter, None) + + def feed_eof(self) -> None: + self._eof = True + if (waiter := self._waiter) is not None: + self._waiter = None + set_result(waiter, None) + + async def read(self) -> _T: + if not self._buffer and not self._eof: + assert not self._waiter + self._waiter = self._loop.create_future() + try: + await self._waiter + except (asyncio.CancelledError, asyncio.TimeoutError): + self._waiter = None + raise + if self._buffer: + data, _ = self._buffer.popleft() + return data + if self._exception is not None: + raise self._exception + raise EofStream + + def __aiter__(self) -> AsyncStreamIterator[_T]: + return AsyncStreamIterator(self.read) + + +class FlowControlDataQueue(DataQueue[_T]): + """FlowControlDataQueue resumes and pauses an underlying stream. + + It is a destination for parsed data. + + This class is deprecated and will be removed in version 4.0. + """ + + def __init__( + self, protocol: BaseProtocol, limit: int, *, loop: asyncio.AbstractEventLoop + ) -> None: + super().__init__(loop=loop) + self._size = 0 + self._protocol = protocol + self._limit = limit * 2 + + def feed_data(self, data: _T, size: int = 0) -> None: + super().feed_data(data, size) + self._size += size + + if self._size > self._limit and not self._protocol._reading_paused: + self._protocol.pause_reading() + + async def read(self) -> _T: + if not self._buffer and not self._eof: + assert not self._waiter + self._waiter = self._loop.create_future() + try: + await self._waiter + except (asyncio.CancelledError, asyncio.TimeoutError): + self._waiter = None + raise + if self._buffer: + data, size = self._buffer.popleft() + self._size -= size + if self._size < self._limit and self._protocol._reading_paused: + self._protocol.resume_reading() + return data + if self._exception is not None: + raise self._exception + raise EofStream diff --git a/.venv/lib/python3.9/site-packages/aiohttp/tcp_helpers.py b/.venv/lib/python3.9/site-packages/aiohttp/tcp_helpers.py new file mode 100644 index 0000000..88b2442 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/tcp_helpers.py @@ -0,0 +1,37 @@ +"""Helper methods to tune a TCP connection""" + +import asyncio +import socket +from contextlib import suppress +from typing import Optional # noqa + +__all__ = ("tcp_keepalive", "tcp_nodelay") + + +if hasattr(socket, "SO_KEEPALIVE"): + + def tcp_keepalive(transport: asyncio.Transport) -> None: + sock = transport.get_extra_info("socket") + if sock is not None: + sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) + +else: + + def tcp_keepalive(transport: asyncio.Transport) -> None: # pragma: no cover + pass + + +def tcp_nodelay(transport: asyncio.Transport, value: bool) -> None: + sock = transport.get_extra_info("socket") + + if sock is None: + return + + if sock.family not in (socket.AF_INET, socket.AF_INET6): + return + + value = bool(value) + + # socket may be closed already, on windows OSError get raised + with suppress(OSError): + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, value) diff --git a/.venv/lib/python3.9/site-packages/aiohttp/test_utils.py b/.venv/lib/python3.9/site-packages/aiohttp/test_utils.py new file mode 100644 index 0000000..87c3142 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/test_utils.py @@ -0,0 +1,774 @@ +"""Utilities shared by tests.""" + +import asyncio +import contextlib +import gc +import inspect +import ipaddress +import os +import socket +import sys +import warnings +from abc import ABC, abstractmethod +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Generic, + Iterator, + List, + Optional, + Type, + TypeVar, + cast, + overload, +) +from unittest import IsolatedAsyncioTestCase, mock + +from aiosignal import Signal +from multidict import CIMultiDict, CIMultiDictProxy +from yarl import URL + +import aiohttp +from aiohttp.client import ( + _RequestContextManager, + _RequestOptions, + _WSRequestContextManager, +) + +from . import ClientSession, hdrs +from .abc import AbstractCookieJar +from .client_reqrep import ClientResponse +from .client_ws import ClientWebSocketResponse +from .helpers import sentinel +from .http import HttpVersion, RawRequestMessage +from .streams import EMPTY_PAYLOAD, StreamReader +from .typedefs import StrOrURL +from .web import ( + Application, + AppRunner, + BaseRequest, + BaseRunner, + Request, + Server, + ServerRunner, + SockSite, + UrlMappingMatchInfo, +) +from .web_protocol import _RequestHandler + +if TYPE_CHECKING: + from ssl import SSLContext +else: + SSLContext = None + +if sys.version_info >= (3, 11) and TYPE_CHECKING: + from typing import Unpack + +if sys.version_info >= (3, 11): + from typing import Self +else: + Self = Any + +_ApplicationNone = TypeVar("_ApplicationNone", Application, None) +_Request = TypeVar("_Request", bound=BaseRequest) + +REUSE_ADDRESS = os.name == "posix" and sys.platform != "cygwin" + + +def get_unused_port_socket( + host: str, family: socket.AddressFamily = socket.AF_INET +) -> socket.socket: + return get_port_socket(host, 0, family) + + +def get_port_socket( + host: str, port: int, family: socket.AddressFamily +) -> socket.socket: + s = socket.socket(family, socket.SOCK_STREAM) + if REUSE_ADDRESS: + # Windows has different semantics for SO_REUSEADDR, + # so don't set it. Ref: + # https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind((host, port)) + return s + + +def unused_port() -> int: + """Return a port that is unused on the current host.""" + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.bind(("127.0.0.1", 0)) + return cast(int, s.getsockname()[1]) + + +class BaseTestServer(ABC): + __test__ = False + + def __init__( + self, + *, + scheme: str = "", + loop: Optional[asyncio.AbstractEventLoop] = None, + host: str = "127.0.0.1", + port: Optional[int] = None, + skip_url_asserts: bool = False, + socket_factory: Callable[ + [str, int, socket.AddressFamily], socket.socket + ] = get_port_socket, + **kwargs: Any, + ) -> None: + self._loop = loop + self.runner: Optional[BaseRunner] = None + self._root: Optional[URL] = None + self.host = host + self.port = port + self._closed = False + self.scheme = scheme + self.skip_url_asserts = skip_url_asserts + self.socket_factory = socket_factory + + async def start_server( + self, loop: Optional[asyncio.AbstractEventLoop] = None, **kwargs: Any + ) -> None: + if self.runner: + return + self._loop = loop + self._ssl = kwargs.pop("ssl", None) + self.runner = await self._make_runner(handler_cancellation=True, **kwargs) + await self.runner.setup() + if not self.port: + self.port = 0 + absolute_host = self.host + try: + version = ipaddress.ip_address(self.host).version + except ValueError: + version = 4 + if version == 6: + absolute_host = f"[{self.host}]" + family = socket.AF_INET6 if version == 6 else socket.AF_INET + _sock = self.socket_factory(self.host, self.port, family) + self.host, self.port = _sock.getsockname()[:2] + site = SockSite(self.runner, sock=_sock, ssl_context=self._ssl) + await site.start() + server = site._server + assert server is not None + sockets = server.sockets # type: ignore[attr-defined] + assert sockets is not None + self.port = sockets[0].getsockname()[1] + if not self.scheme: + self.scheme = "https" if self._ssl else "http" + self._root = URL(f"{self.scheme}://{absolute_host}:{self.port}") + + @abstractmethod # pragma: no cover + async def _make_runner(self, **kwargs: Any) -> BaseRunner: + pass + + def make_url(self, path: StrOrURL) -> URL: + assert self._root is not None + url = URL(path) + if not self.skip_url_asserts: + assert not url.absolute + return self._root.join(url) + else: + return URL(str(self._root) + str(path)) + + @property + def started(self) -> bool: + return self.runner is not None + + @property + def closed(self) -> bool: + return self._closed + + @property + def handler(self) -> Server: + # for backward compatibility + # web.Server instance + runner = self.runner + assert runner is not None + assert runner.server is not None + return runner.server + + async def close(self) -> None: + """Close all fixtures created by the test client. + + After that point, the TestClient is no longer usable. + + This is an idempotent function: running close multiple times + will not have any additional effects. + + close is also run when the object is garbage collected, and on + exit when used as a context manager. + + """ + if self.started and not self.closed: + assert self.runner is not None + await self.runner.cleanup() + self._root = None + self.port = None + self._closed = True + + def __enter__(self) -> None: + raise TypeError("Use async with instead") + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_value: Optional[BaseException], + traceback: Optional[TracebackType], + ) -> None: + # __exit__ should exist in pair with __enter__ but never executed + pass # pragma: no cover + + async def __aenter__(self) -> "BaseTestServer": + await self.start_server(loop=self._loop) + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc_value: Optional[BaseException], + traceback: Optional[TracebackType], + ) -> None: + await self.close() + + +class TestServer(BaseTestServer): + def __init__( + self, + app: Application, + *, + scheme: str = "", + host: str = "127.0.0.1", + port: Optional[int] = None, + **kwargs: Any, + ): + self.app = app + super().__init__(scheme=scheme, host=host, port=port, **kwargs) + + async def _make_runner(self, **kwargs: Any) -> BaseRunner: + return AppRunner(self.app, **kwargs) + + +class RawTestServer(BaseTestServer): + def __init__( + self, + handler: _RequestHandler, + *, + scheme: str = "", + host: str = "127.0.0.1", + port: Optional[int] = None, + **kwargs: Any, + ) -> None: + self._handler = handler + super().__init__(scheme=scheme, host=host, port=port, **kwargs) + + async def _make_runner(self, debug: bool = True, **kwargs: Any) -> ServerRunner: + srv = Server(self._handler, loop=self._loop, debug=debug, **kwargs) + return ServerRunner(srv, debug=debug, **kwargs) + + +class TestClient(Generic[_Request, _ApplicationNone]): + """ + A test client implementation. + + To write functional tests for aiohttp based servers. + + """ + + __test__ = False + + @overload + def __init__( + self: "TestClient[Request, Application]", + server: TestServer, + *, + cookie_jar: Optional[AbstractCookieJar] = None, + **kwargs: Any, + ) -> None: ... + @overload + def __init__( + self: "TestClient[_Request, None]", + server: BaseTestServer, + *, + cookie_jar: Optional[AbstractCookieJar] = None, + **kwargs: Any, + ) -> None: ... + def __init__( + self, + server: BaseTestServer, + *, + cookie_jar: Optional[AbstractCookieJar] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + **kwargs: Any, + ) -> None: + if not isinstance(server, BaseTestServer): + raise TypeError( + "server must be TestServer instance, found type: %r" % type(server) + ) + self._server = server + self._loop = loop + if cookie_jar is None: + cookie_jar = aiohttp.CookieJar(unsafe=True, loop=loop) + self._session = ClientSession(loop=loop, cookie_jar=cookie_jar, **kwargs) + self._session._retry_connection = False + self._closed = False + self._responses: List[ClientResponse] = [] + self._websockets: List[ClientWebSocketResponse] = [] + + async def start_server(self) -> None: + await self._server.start_server(loop=self._loop) + + @property + def host(self) -> str: + return self._server.host + + @property + def port(self) -> Optional[int]: + return self._server.port + + @property + def server(self) -> BaseTestServer: + return self._server + + @property + def app(self) -> _ApplicationNone: + return getattr(self._server, "app", None) # type: ignore[return-value] + + @property + def session(self) -> ClientSession: + """An internal aiohttp.ClientSession. + + Unlike the methods on the TestClient, client session requests + do not automatically include the host in the url queried, and + will require an absolute path to the resource. + + """ + return self._session + + def make_url(self, path: StrOrURL) -> URL: + return self._server.make_url(path) + + async def _request( + self, method: str, path: StrOrURL, **kwargs: Any + ) -> ClientResponse: + resp = await self._session.request(method, self.make_url(path), **kwargs) + # save it to close later + self._responses.append(resp) + return resp + + if sys.version_info >= (3, 11) and TYPE_CHECKING: + + def request( + self, method: str, path: StrOrURL, **kwargs: Unpack[_RequestOptions] + ) -> _RequestContextManager: ... + + def get( + self, + path: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> _RequestContextManager: ... + + def options( + self, + path: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> _RequestContextManager: ... + + def head( + self, + path: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> _RequestContextManager: ... + + def post( + self, + path: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> _RequestContextManager: ... + + def put( + self, + path: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> _RequestContextManager: ... + + def patch( + self, + path: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> _RequestContextManager: ... + + def delete( + self, + path: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> _RequestContextManager: ... + + else: + + def request( + self, method: str, path: StrOrURL, **kwargs: Any + ) -> _RequestContextManager: + """Routes a request to tested http server. + + The interface is identical to aiohttp.ClientSession.request, + except the loop kwarg is overridden by the instance used by the + test server. + + """ + return _RequestContextManager(self._request(method, path, **kwargs)) + + def get(self, path: StrOrURL, **kwargs: Any) -> _RequestContextManager: + """Perform an HTTP GET request.""" + return _RequestContextManager(self._request(hdrs.METH_GET, path, **kwargs)) + + def post(self, path: StrOrURL, **kwargs: Any) -> _RequestContextManager: + """Perform an HTTP POST request.""" + return _RequestContextManager(self._request(hdrs.METH_POST, path, **kwargs)) + + def options(self, path: StrOrURL, **kwargs: Any) -> _RequestContextManager: + """Perform an HTTP OPTIONS request.""" + return _RequestContextManager( + self._request(hdrs.METH_OPTIONS, path, **kwargs) + ) + + def head(self, path: StrOrURL, **kwargs: Any) -> _RequestContextManager: + """Perform an HTTP HEAD request.""" + return _RequestContextManager(self._request(hdrs.METH_HEAD, path, **kwargs)) + + def put(self, path: StrOrURL, **kwargs: Any) -> _RequestContextManager: + """Perform an HTTP PUT request.""" + return _RequestContextManager(self._request(hdrs.METH_PUT, path, **kwargs)) + + def patch(self, path: StrOrURL, **kwargs: Any) -> _RequestContextManager: + """Perform an HTTP PATCH request.""" + return _RequestContextManager( + self._request(hdrs.METH_PATCH, path, **kwargs) + ) + + def delete(self, path: StrOrURL, **kwargs: Any) -> _RequestContextManager: + """Perform an HTTP PATCH request.""" + return _RequestContextManager( + self._request(hdrs.METH_DELETE, path, **kwargs) + ) + + def ws_connect(self, path: StrOrURL, **kwargs: Any) -> _WSRequestContextManager: + """Initiate websocket connection. + + The api corresponds to aiohttp.ClientSession.ws_connect. + + """ + return _WSRequestContextManager(self._ws_connect(path, **kwargs)) + + async def _ws_connect( + self, path: StrOrURL, **kwargs: Any + ) -> ClientWebSocketResponse: + ws = await self._session.ws_connect(self.make_url(path), **kwargs) + self._websockets.append(ws) + return ws + + async def close(self) -> None: + """Close all fixtures created by the test client. + + After that point, the TestClient is no longer usable. + + This is an idempotent function: running close multiple times + will not have any additional effects. + + close is also run on exit when used as a(n) (asynchronous) + context manager. + + """ + if not self._closed: + for resp in self._responses: + resp.close() + for ws in self._websockets: + await ws.close() + await self._session.close() + await self._server.close() + self._closed = True + + def __enter__(self) -> None: + raise TypeError("Use async with instead") + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc: Optional[BaseException], + tb: Optional[TracebackType], + ) -> None: + # __exit__ should exist in pair with __enter__ but never executed + pass # pragma: no cover + + async def __aenter__(self) -> Self: + await self.start_server() + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc: Optional[BaseException], + tb: Optional[TracebackType], + ) -> None: + await self.close() + + +class AioHTTPTestCase(IsolatedAsyncioTestCase): + """A base class to allow for unittest web applications using aiohttp. + + Provides the following: + + * self.client (aiohttp.test_utils.TestClient): an aiohttp test client. + * self.loop (asyncio.BaseEventLoop): the event loop in which the + application and server are running. + * self.app (aiohttp.web.Application): the application returned by + self.get_application() + + Note that the TestClient's methods are asynchronous: you have to + execute function on the test client using asynchronous methods. + """ + + async def get_application(self) -> Application: + """Get application. + + This method should be overridden + to return the aiohttp.web.Application + object to test. + """ + return self.get_app() + + def get_app(self) -> Application: + """Obsolete method used to constructing web application. + + Use .get_application() coroutine instead. + """ + raise RuntimeError("Did you forget to define get_application()?") + + async def asyncSetUp(self) -> None: + self.loop = asyncio.get_running_loop() + return await self.setUpAsync() + + async def setUpAsync(self) -> None: + self.app = await self.get_application() + self.server = await self.get_server(self.app) + self.client = await self.get_client(self.server) + + await self.client.start_server() + + async def asyncTearDown(self) -> None: + return await self.tearDownAsync() + + async def tearDownAsync(self) -> None: + await self.client.close() + + async def get_server(self, app: Application) -> TestServer: + """Return a TestServer instance.""" + return TestServer(app, loop=self.loop) + + async def get_client(self, server: TestServer) -> TestClient[Request, Application]: + """Return a TestClient instance.""" + return TestClient(server, loop=self.loop) + + +def unittest_run_loop(func: Any, *args: Any, **kwargs: Any) -> Any: + """ + A decorator dedicated to use with asynchronous AioHTTPTestCase test methods. + + In 3.8+, this does nothing. + """ + warnings.warn( + "Decorator `@unittest_run_loop` is no longer needed in aiohttp 3.8+", + DeprecationWarning, + stacklevel=2, + ) + return func + + +_LOOP_FACTORY = Callable[[], asyncio.AbstractEventLoop] + + +@contextlib.contextmanager +def loop_context( + loop_factory: _LOOP_FACTORY = asyncio.new_event_loop, fast: bool = False +) -> Iterator[asyncio.AbstractEventLoop]: + """A contextmanager that creates an event_loop, for test purposes. + + Handles the creation and cleanup of a test loop. + """ + loop = setup_test_loop(loop_factory) + yield loop + teardown_test_loop(loop, fast=fast) + + +def setup_test_loop( + loop_factory: _LOOP_FACTORY = asyncio.new_event_loop, +) -> asyncio.AbstractEventLoop: + """Create and return an asyncio.BaseEventLoop instance. + + The caller should also call teardown_test_loop, + once they are done with the loop. + """ + loop = loop_factory() + asyncio.set_event_loop(loop) + return loop + + +def teardown_test_loop(loop: asyncio.AbstractEventLoop, fast: bool = False) -> None: + """Teardown and cleanup an event_loop created by setup_test_loop.""" + closed = loop.is_closed() + if not closed: + loop.call_soon(loop.stop) + loop.run_forever() + loop.close() + + if not fast: + gc.collect() + + asyncio.set_event_loop(None) + + +def _create_app_mock() -> mock.MagicMock: + def get_dict(app: Any, key: str) -> Any: + return app.__app_dict[key] + + def set_dict(app: Any, key: str, value: Any) -> None: + app.__app_dict[key] = value + + app = mock.MagicMock(spec=Application) + app.__app_dict = {} + app.__getitem__ = get_dict + app.__setitem__ = set_dict + + app._debug = False + app.on_response_prepare = Signal(app) + app.on_response_prepare.freeze() + return app + + +def _create_transport(sslcontext: Optional[SSLContext] = None) -> mock.Mock: + transport = mock.Mock() + + def get_extra_info(key: str) -> Optional[SSLContext]: + if key == "sslcontext": + return sslcontext + else: + return None + + transport.get_extra_info.side_effect = get_extra_info + return transport + + +def make_mocked_request( + method: str, + path: str, + headers: Any = None, + *, + match_info: Any = sentinel, + version: HttpVersion = HttpVersion(1, 1), + closing: bool = False, + app: Any = None, + writer: Any = sentinel, + protocol: Any = sentinel, + transport: Any = sentinel, + payload: StreamReader = EMPTY_PAYLOAD, + sslcontext: Optional[SSLContext] = None, + client_max_size: int = 1024**2, + loop: Any = ..., +) -> Request: + """Creates mocked web.Request testing purposes. + + Useful in unit tests, when spinning full web server is overkill or + specific conditions and errors are hard to trigger. + """ + task = mock.Mock() + if loop is ...: + # no loop passed, try to get the current one if + # its is running as we need a real loop to create + # executor jobs to be able to do testing + # with a real executor + try: + loop = asyncio.get_running_loop() + except RuntimeError: + loop = mock.Mock() + loop.create_future.return_value = () + + if version < HttpVersion(1, 1): + closing = True + + if headers: + headers = CIMultiDictProxy(CIMultiDict(headers)) + raw_hdrs = tuple( + (k.encode("utf-8"), v.encode("utf-8")) for k, v in headers.items() + ) + else: + headers = CIMultiDictProxy(CIMultiDict()) + raw_hdrs = () + + chunked = "chunked" in headers.get(hdrs.TRANSFER_ENCODING, "").lower() + + message = RawRequestMessage( + method, + path, + version, + headers, + raw_hdrs, + closing, + None, + False, + chunked, + URL(path), + ) + if app is None: + app = _create_app_mock() + + if transport is sentinel: + transport = _create_transport(sslcontext) + + if protocol is sentinel: + protocol = mock.Mock() + protocol.transport = transport + type(protocol).peername = mock.PropertyMock( + return_value=transport.get_extra_info("peername") + ) + type(protocol).ssl_context = mock.PropertyMock(return_value=sslcontext) + + if writer is sentinel: + writer = mock.Mock() + writer.write_headers = make_mocked_coro(None) + writer.write = make_mocked_coro(None) + writer.write_eof = make_mocked_coro(None) + writer.drain = make_mocked_coro(None) + writer.transport = transport + + protocol.transport = transport + protocol.writer = writer + + req = Request( + message, payload, protocol, writer, task, loop, client_max_size=client_max_size + ) + + match_info = UrlMappingMatchInfo( + {} if match_info is sentinel else match_info, mock.Mock() + ) + match_info.add_app(app) + req._match_info = match_info + + return req + + +def make_mocked_coro( + return_value: Any = sentinel, raise_exception: Any = sentinel +) -> Any: + """Creates a coroutine mock.""" + + async def mock_coro(*args: Any, **kwargs: Any) -> Any: + if raise_exception is not sentinel: + raise raise_exception + if not inspect.isawaitable(return_value): + return return_value + await return_value + + return mock.Mock(wraps=mock_coro) diff --git a/.venv/lib/python3.9/site-packages/aiohttp/tracing.py b/.venv/lib/python3.9/site-packages/aiohttp/tracing.py new file mode 100644 index 0000000..568fa7f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/tracing.py @@ -0,0 +1,455 @@ +from types import SimpleNamespace +from typing import TYPE_CHECKING, Mapping, Optional, Type, TypeVar + +import attr +from aiosignal import Signal +from multidict import CIMultiDict +from yarl import URL + +from .client_reqrep import ClientResponse + +if TYPE_CHECKING: + from .client import ClientSession + + _ParamT_contra = TypeVar("_ParamT_contra", contravariant=True) + _TracingSignal = Signal[ClientSession, SimpleNamespace, _ParamT_contra] + + +__all__ = ( + "TraceConfig", + "TraceRequestStartParams", + "TraceRequestEndParams", + "TraceRequestExceptionParams", + "TraceConnectionQueuedStartParams", + "TraceConnectionQueuedEndParams", + "TraceConnectionCreateStartParams", + "TraceConnectionCreateEndParams", + "TraceConnectionReuseconnParams", + "TraceDnsResolveHostStartParams", + "TraceDnsResolveHostEndParams", + "TraceDnsCacheHitParams", + "TraceDnsCacheMissParams", + "TraceRequestRedirectParams", + "TraceRequestChunkSentParams", + "TraceResponseChunkReceivedParams", + "TraceRequestHeadersSentParams", +) + + +class TraceConfig: + """First-class used to trace requests launched via ClientSession objects.""" + + def __init__( + self, trace_config_ctx_factory: Type[SimpleNamespace] = SimpleNamespace + ) -> None: + self._on_request_start: _TracingSignal[TraceRequestStartParams] = Signal(self) + self._on_request_chunk_sent: _TracingSignal[TraceRequestChunkSentParams] = ( + Signal(self) + ) + self._on_response_chunk_received: _TracingSignal[ + TraceResponseChunkReceivedParams + ] = Signal(self) + self._on_request_end: _TracingSignal[TraceRequestEndParams] = Signal(self) + self._on_request_exception: _TracingSignal[TraceRequestExceptionParams] = ( + Signal(self) + ) + self._on_request_redirect: _TracingSignal[TraceRequestRedirectParams] = Signal( + self + ) + self._on_connection_queued_start: _TracingSignal[ + TraceConnectionQueuedStartParams + ] = Signal(self) + self._on_connection_queued_end: _TracingSignal[ + TraceConnectionQueuedEndParams + ] = Signal(self) + self._on_connection_create_start: _TracingSignal[ + TraceConnectionCreateStartParams + ] = Signal(self) + self._on_connection_create_end: _TracingSignal[ + TraceConnectionCreateEndParams + ] = Signal(self) + self._on_connection_reuseconn: _TracingSignal[ + TraceConnectionReuseconnParams + ] = Signal(self) + self._on_dns_resolvehost_start: _TracingSignal[ + TraceDnsResolveHostStartParams + ] = Signal(self) + self._on_dns_resolvehost_end: _TracingSignal[TraceDnsResolveHostEndParams] = ( + Signal(self) + ) + self._on_dns_cache_hit: _TracingSignal[TraceDnsCacheHitParams] = Signal(self) + self._on_dns_cache_miss: _TracingSignal[TraceDnsCacheMissParams] = Signal(self) + self._on_request_headers_sent: _TracingSignal[TraceRequestHeadersSentParams] = ( + Signal(self) + ) + + self._trace_config_ctx_factory = trace_config_ctx_factory + + def trace_config_ctx( + self, trace_request_ctx: Optional[Mapping[str, str]] = None + ) -> SimpleNamespace: + """Return a new trace_config_ctx instance""" + return self._trace_config_ctx_factory(trace_request_ctx=trace_request_ctx) + + def freeze(self) -> None: + self._on_request_start.freeze() + self._on_request_chunk_sent.freeze() + self._on_response_chunk_received.freeze() + self._on_request_end.freeze() + self._on_request_exception.freeze() + self._on_request_redirect.freeze() + self._on_connection_queued_start.freeze() + self._on_connection_queued_end.freeze() + self._on_connection_create_start.freeze() + self._on_connection_create_end.freeze() + self._on_connection_reuseconn.freeze() + self._on_dns_resolvehost_start.freeze() + self._on_dns_resolvehost_end.freeze() + self._on_dns_cache_hit.freeze() + self._on_dns_cache_miss.freeze() + self._on_request_headers_sent.freeze() + + @property + def on_request_start(self) -> "_TracingSignal[TraceRequestStartParams]": + return self._on_request_start + + @property + def on_request_chunk_sent( + self, + ) -> "_TracingSignal[TraceRequestChunkSentParams]": + return self._on_request_chunk_sent + + @property + def on_response_chunk_received( + self, + ) -> "_TracingSignal[TraceResponseChunkReceivedParams]": + return self._on_response_chunk_received + + @property + def on_request_end(self) -> "_TracingSignal[TraceRequestEndParams]": + return self._on_request_end + + @property + def on_request_exception( + self, + ) -> "_TracingSignal[TraceRequestExceptionParams]": + return self._on_request_exception + + @property + def on_request_redirect( + self, + ) -> "_TracingSignal[TraceRequestRedirectParams]": + return self._on_request_redirect + + @property + def on_connection_queued_start( + self, + ) -> "_TracingSignal[TraceConnectionQueuedStartParams]": + return self._on_connection_queued_start + + @property + def on_connection_queued_end( + self, + ) -> "_TracingSignal[TraceConnectionQueuedEndParams]": + return self._on_connection_queued_end + + @property + def on_connection_create_start( + self, + ) -> "_TracingSignal[TraceConnectionCreateStartParams]": + return self._on_connection_create_start + + @property + def on_connection_create_end( + self, + ) -> "_TracingSignal[TraceConnectionCreateEndParams]": + return self._on_connection_create_end + + @property + def on_connection_reuseconn( + self, + ) -> "_TracingSignal[TraceConnectionReuseconnParams]": + return self._on_connection_reuseconn + + @property + def on_dns_resolvehost_start( + self, + ) -> "_TracingSignal[TraceDnsResolveHostStartParams]": + return self._on_dns_resolvehost_start + + @property + def on_dns_resolvehost_end( + self, + ) -> "_TracingSignal[TraceDnsResolveHostEndParams]": + return self._on_dns_resolvehost_end + + @property + def on_dns_cache_hit(self) -> "_TracingSignal[TraceDnsCacheHitParams]": + return self._on_dns_cache_hit + + @property + def on_dns_cache_miss(self) -> "_TracingSignal[TraceDnsCacheMissParams]": + return self._on_dns_cache_miss + + @property + def on_request_headers_sent( + self, + ) -> "_TracingSignal[TraceRequestHeadersSentParams]": + return self._on_request_headers_sent + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceRequestStartParams: + """Parameters sent by the `on_request_start` signal""" + + method: str + url: URL + headers: "CIMultiDict[str]" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceRequestChunkSentParams: + """Parameters sent by the `on_request_chunk_sent` signal""" + + method: str + url: URL + chunk: bytes + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceResponseChunkReceivedParams: + """Parameters sent by the `on_response_chunk_received` signal""" + + method: str + url: URL + chunk: bytes + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceRequestEndParams: + """Parameters sent by the `on_request_end` signal""" + + method: str + url: URL + headers: "CIMultiDict[str]" + response: ClientResponse + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceRequestExceptionParams: + """Parameters sent by the `on_request_exception` signal""" + + method: str + url: URL + headers: "CIMultiDict[str]" + exception: BaseException + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceRequestRedirectParams: + """Parameters sent by the `on_request_redirect` signal""" + + method: str + url: URL + headers: "CIMultiDict[str]" + response: ClientResponse + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceConnectionQueuedStartParams: + """Parameters sent by the `on_connection_queued_start` signal""" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceConnectionQueuedEndParams: + """Parameters sent by the `on_connection_queued_end` signal""" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceConnectionCreateStartParams: + """Parameters sent by the `on_connection_create_start` signal""" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceConnectionCreateEndParams: + """Parameters sent by the `on_connection_create_end` signal""" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceConnectionReuseconnParams: + """Parameters sent by the `on_connection_reuseconn` signal""" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceDnsResolveHostStartParams: + """Parameters sent by the `on_dns_resolvehost_start` signal""" + + host: str + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceDnsResolveHostEndParams: + """Parameters sent by the `on_dns_resolvehost_end` signal""" + + host: str + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceDnsCacheHitParams: + """Parameters sent by the `on_dns_cache_hit` signal""" + + host: str + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceDnsCacheMissParams: + """Parameters sent by the `on_dns_cache_miss` signal""" + + host: str + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceRequestHeadersSentParams: + """Parameters sent by the `on_request_headers_sent` signal""" + + method: str + url: URL + headers: "CIMultiDict[str]" + + +class Trace: + """Internal dependency holder class. + + Used to keep together the main dependencies used + at the moment of send a signal. + """ + + def __init__( + self, + session: "ClientSession", + trace_config: TraceConfig, + trace_config_ctx: SimpleNamespace, + ) -> None: + self._trace_config = trace_config + self._trace_config_ctx = trace_config_ctx + self._session = session + + async def send_request_start( + self, method: str, url: URL, headers: "CIMultiDict[str]" + ) -> None: + return await self._trace_config.on_request_start.send( + self._session, + self._trace_config_ctx, + TraceRequestStartParams(method, url, headers), + ) + + async def send_request_chunk_sent( + self, method: str, url: URL, chunk: bytes + ) -> None: + return await self._trace_config.on_request_chunk_sent.send( + self._session, + self._trace_config_ctx, + TraceRequestChunkSentParams(method, url, chunk), + ) + + async def send_response_chunk_received( + self, method: str, url: URL, chunk: bytes + ) -> None: + return await self._trace_config.on_response_chunk_received.send( + self._session, + self._trace_config_ctx, + TraceResponseChunkReceivedParams(method, url, chunk), + ) + + async def send_request_end( + self, + method: str, + url: URL, + headers: "CIMultiDict[str]", + response: ClientResponse, + ) -> None: + return await self._trace_config.on_request_end.send( + self._session, + self._trace_config_ctx, + TraceRequestEndParams(method, url, headers, response), + ) + + async def send_request_exception( + self, + method: str, + url: URL, + headers: "CIMultiDict[str]", + exception: BaseException, + ) -> None: + return await self._trace_config.on_request_exception.send( + self._session, + self._trace_config_ctx, + TraceRequestExceptionParams(method, url, headers, exception), + ) + + async def send_request_redirect( + self, + method: str, + url: URL, + headers: "CIMultiDict[str]", + response: ClientResponse, + ) -> None: + return await self._trace_config._on_request_redirect.send( + self._session, + self._trace_config_ctx, + TraceRequestRedirectParams(method, url, headers, response), + ) + + async def send_connection_queued_start(self) -> None: + return await self._trace_config.on_connection_queued_start.send( + self._session, self._trace_config_ctx, TraceConnectionQueuedStartParams() + ) + + async def send_connection_queued_end(self) -> None: + return await self._trace_config.on_connection_queued_end.send( + self._session, self._trace_config_ctx, TraceConnectionQueuedEndParams() + ) + + async def send_connection_create_start(self) -> None: + return await self._trace_config.on_connection_create_start.send( + self._session, self._trace_config_ctx, TraceConnectionCreateStartParams() + ) + + async def send_connection_create_end(self) -> None: + return await self._trace_config.on_connection_create_end.send( + self._session, self._trace_config_ctx, TraceConnectionCreateEndParams() + ) + + async def send_connection_reuseconn(self) -> None: + return await self._trace_config.on_connection_reuseconn.send( + self._session, self._trace_config_ctx, TraceConnectionReuseconnParams() + ) + + async def send_dns_resolvehost_start(self, host: str) -> None: + return await self._trace_config.on_dns_resolvehost_start.send( + self._session, self._trace_config_ctx, TraceDnsResolveHostStartParams(host) + ) + + async def send_dns_resolvehost_end(self, host: str) -> None: + return await self._trace_config.on_dns_resolvehost_end.send( + self._session, self._trace_config_ctx, TraceDnsResolveHostEndParams(host) + ) + + async def send_dns_cache_hit(self, host: str) -> None: + return await self._trace_config.on_dns_cache_hit.send( + self._session, self._trace_config_ctx, TraceDnsCacheHitParams(host) + ) + + async def send_dns_cache_miss(self, host: str) -> None: + return await self._trace_config.on_dns_cache_miss.send( + self._session, self._trace_config_ctx, TraceDnsCacheMissParams(host) + ) + + async def send_request_headers( + self, method: str, url: URL, headers: "CIMultiDict[str]" + ) -> None: + return await self._trace_config._on_request_headers_sent.send( + self._session, + self._trace_config_ctx, + TraceRequestHeadersSentParams(method, url, headers), + ) diff --git a/.venv/lib/python3.9/site-packages/aiohttp/typedefs.py b/.venv/lib/python3.9/site-packages/aiohttp/typedefs.py new file mode 100644 index 0000000..cc8c082 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/typedefs.py @@ -0,0 +1,69 @@ +import json +import os +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Iterable, + Mapping, + Protocol, + Tuple, + Union, +) + +from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy, istr +from yarl import URL, Query as _Query + +Query = _Query + +DEFAULT_JSON_ENCODER = json.dumps +DEFAULT_JSON_DECODER = json.loads + +if TYPE_CHECKING: + _CIMultiDict = CIMultiDict[str] + _CIMultiDictProxy = CIMultiDictProxy[str] + _MultiDict = MultiDict[str] + _MultiDictProxy = MultiDictProxy[str] + from http.cookies import BaseCookie, Morsel + + from .web import Request, StreamResponse +else: + _CIMultiDict = CIMultiDict + _CIMultiDictProxy = CIMultiDictProxy + _MultiDict = MultiDict + _MultiDictProxy = MultiDictProxy + +Byteish = Union[bytes, bytearray, memoryview] +JSONEncoder = Callable[[Any], str] +JSONDecoder = Callable[[str], Any] +LooseHeaders = Union[ + Mapping[str, str], + Mapping[istr, str], + _CIMultiDict, + _CIMultiDictProxy, + Iterable[Tuple[Union[str, istr], str]], +] +RawHeaders = Tuple[Tuple[bytes, bytes], ...] +StrOrURL = Union[str, URL] + +LooseCookiesMappings = Mapping[str, Union[str, "BaseCookie[str]", "Morsel[Any]"]] +LooseCookiesIterables = Iterable[ + Tuple[str, Union[str, "BaseCookie[str]", "Morsel[Any]"]] +] +LooseCookies = Union[ + LooseCookiesMappings, + LooseCookiesIterables, + "BaseCookie[str]", +] + +Handler = Callable[["Request"], Awaitable["StreamResponse"]] + + +class Middleware(Protocol): + def __call__( + self, request: "Request", handler: Handler + ) -> Awaitable["StreamResponse"]: ... + + +PathLike = Union[str, "os.PathLike[str]"] diff --git a/.venv/lib/python3.9/site-packages/aiohttp/web.py b/.venv/lib/python3.9/site-packages/aiohttp/web.py new file mode 100644 index 0000000..5a1fc96 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/web.py @@ -0,0 +1,592 @@ +import asyncio +import logging +import os +import socket +import sys +import warnings +from argparse import ArgumentParser +from collections.abc import Iterable +from contextlib import suppress +from importlib import import_module +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Iterable as TypingIterable, + List, + Optional, + Set, + Type, + Union, + cast, +) + +from .abc import AbstractAccessLogger +from .helpers import AppKey as AppKey +from .log import access_logger +from .typedefs import PathLike +from .web_app import Application as Application, CleanupError as CleanupError +from .web_exceptions import ( + HTTPAccepted as HTTPAccepted, + HTTPBadGateway as HTTPBadGateway, + HTTPBadRequest as HTTPBadRequest, + HTTPClientError as HTTPClientError, + HTTPConflict as HTTPConflict, + HTTPCreated as HTTPCreated, + HTTPError as HTTPError, + HTTPException as HTTPException, + HTTPExpectationFailed as HTTPExpectationFailed, + HTTPFailedDependency as HTTPFailedDependency, + HTTPForbidden as HTTPForbidden, + HTTPFound as HTTPFound, + HTTPGatewayTimeout as HTTPGatewayTimeout, + HTTPGone as HTTPGone, + HTTPInsufficientStorage as HTTPInsufficientStorage, + HTTPInternalServerError as HTTPInternalServerError, + HTTPLengthRequired as HTTPLengthRequired, + HTTPMethodNotAllowed as HTTPMethodNotAllowed, + HTTPMisdirectedRequest as HTTPMisdirectedRequest, + HTTPMove as HTTPMove, + HTTPMovedPermanently as HTTPMovedPermanently, + HTTPMultipleChoices as HTTPMultipleChoices, + HTTPNetworkAuthenticationRequired as HTTPNetworkAuthenticationRequired, + HTTPNoContent as HTTPNoContent, + HTTPNonAuthoritativeInformation as HTTPNonAuthoritativeInformation, + HTTPNotAcceptable as HTTPNotAcceptable, + HTTPNotExtended as HTTPNotExtended, + HTTPNotFound as HTTPNotFound, + HTTPNotImplemented as HTTPNotImplemented, + HTTPNotModified as HTTPNotModified, + HTTPOk as HTTPOk, + HTTPPartialContent as HTTPPartialContent, + HTTPPaymentRequired as HTTPPaymentRequired, + HTTPPermanentRedirect as HTTPPermanentRedirect, + HTTPPreconditionFailed as HTTPPreconditionFailed, + HTTPPreconditionRequired as HTTPPreconditionRequired, + HTTPProxyAuthenticationRequired as HTTPProxyAuthenticationRequired, + HTTPRedirection as HTTPRedirection, + HTTPRequestEntityTooLarge as HTTPRequestEntityTooLarge, + HTTPRequestHeaderFieldsTooLarge as HTTPRequestHeaderFieldsTooLarge, + HTTPRequestRangeNotSatisfiable as HTTPRequestRangeNotSatisfiable, + HTTPRequestTimeout as HTTPRequestTimeout, + HTTPRequestURITooLong as HTTPRequestURITooLong, + HTTPResetContent as HTTPResetContent, + HTTPSeeOther as HTTPSeeOther, + HTTPServerError as HTTPServerError, + HTTPServiceUnavailable as HTTPServiceUnavailable, + HTTPSuccessful as HTTPSuccessful, + HTTPTemporaryRedirect as HTTPTemporaryRedirect, + HTTPTooManyRequests as HTTPTooManyRequests, + HTTPUnauthorized as HTTPUnauthorized, + HTTPUnavailableForLegalReasons as HTTPUnavailableForLegalReasons, + HTTPUnprocessableEntity as HTTPUnprocessableEntity, + HTTPUnsupportedMediaType as HTTPUnsupportedMediaType, + HTTPUpgradeRequired as HTTPUpgradeRequired, + HTTPUseProxy as HTTPUseProxy, + HTTPVariantAlsoNegotiates as HTTPVariantAlsoNegotiates, + HTTPVersionNotSupported as HTTPVersionNotSupported, + NotAppKeyWarning as NotAppKeyWarning, +) +from .web_fileresponse import FileResponse as FileResponse +from .web_log import AccessLogger +from .web_middlewares import ( + middleware as middleware, + normalize_path_middleware as normalize_path_middleware, +) +from .web_protocol import ( + PayloadAccessError as PayloadAccessError, + RequestHandler as RequestHandler, + RequestPayloadError as RequestPayloadError, +) +from .web_request import ( + BaseRequest as BaseRequest, + FileField as FileField, + Request as Request, +) +from .web_response import ( + ContentCoding as ContentCoding, + Response as Response, + StreamResponse as StreamResponse, + json_response as json_response, +) +from .web_routedef import ( + AbstractRouteDef as AbstractRouteDef, + RouteDef as RouteDef, + RouteTableDef as RouteTableDef, + StaticDef as StaticDef, + delete as delete, + get as get, + head as head, + options as options, + patch as patch, + post as post, + put as put, + route as route, + static as static, + view as view, +) +from .web_runner import ( + AppRunner as AppRunner, + BaseRunner as BaseRunner, + BaseSite as BaseSite, + GracefulExit as GracefulExit, + NamedPipeSite as NamedPipeSite, + ServerRunner as ServerRunner, + SockSite as SockSite, + TCPSite as TCPSite, + UnixSite as UnixSite, +) +from .web_server import Server as Server +from .web_urldispatcher import ( + AbstractResource as AbstractResource, + AbstractRoute as AbstractRoute, + DynamicResource as DynamicResource, + PlainResource as PlainResource, + PrefixedSubAppResource as PrefixedSubAppResource, + Resource as Resource, + ResourceRoute as ResourceRoute, + StaticResource as StaticResource, + UrlDispatcher as UrlDispatcher, + UrlMappingMatchInfo as UrlMappingMatchInfo, + View as View, +) +from .web_ws import ( + WebSocketReady as WebSocketReady, + WebSocketResponse as WebSocketResponse, + WSMsgType as WSMsgType, +) + +__all__ = ( + # web_app + "AppKey", + "Application", + "CleanupError", + # web_exceptions + "NotAppKeyWarning", + "HTTPAccepted", + "HTTPBadGateway", + "HTTPBadRequest", + "HTTPClientError", + "HTTPConflict", + "HTTPCreated", + "HTTPError", + "HTTPException", + "HTTPExpectationFailed", + "HTTPFailedDependency", + "HTTPForbidden", + "HTTPFound", + "HTTPGatewayTimeout", + "HTTPGone", + "HTTPInsufficientStorage", + "HTTPInternalServerError", + "HTTPLengthRequired", + "HTTPMethodNotAllowed", + "HTTPMisdirectedRequest", + "HTTPMove", + "HTTPMovedPermanently", + "HTTPMultipleChoices", + "HTTPNetworkAuthenticationRequired", + "HTTPNoContent", + "HTTPNonAuthoritativeInformation", + "HTTPNotAcceptable", + "HTTPNotExtended", + "HTTPNotFound", + "HTTPNotImplemented", + "HTTPNotModified", + "HTTPOk", + "HTTPPartialContent", + "HTTPPaymentRequired", + "HTTPPermanentRedirect", + "HTTPPreconditionFailed", + "HTTPPreconditionRequired", + "HTTPProxyAuthenticationRequired", + "HTTPRedirection", + "HTTPRequestEntityTooLarge", + "HTTPRequestHeaderFieldsTooLarge", + "HTTPRequestRangeNotSatisfiable", + "HTTPRequestTimeout", + "HTTPRequestURITooLong", + "HTTPResetContent", + "HTTPSeeOther", + "HTTPServerError", + "HTTPServiceUnavailable", + "HTTPSuccessful", + "HTTPTemporaryRedirect", + "HTTPTooManyRequests", + "HTTPUnauthorized", + "HTTPUnavailableForLegalReasons", + "HTTPUnprocessableEntity", + "HTTPUnsupportedMediaType", + "HTTPUpgradeRequired", + "HTTPUseProxy", + "HTTPVariantAlsoNegotiates", + "HTTPVersionNotSupported", + # web_fileresponse + "FileResponse", + # web_middlewares + "middleware", + "normalize_path_middleware", + # web_protocol + "PayloadAccessError", + "RequestHandler", + "RequestPayloadError", + # web_request + "BaseRequest", + "FileField", + "Request", + # web_response + "ContentCoding", + "Response", + "StreamResponse", + "json_response", + # web_routedef + "AbstractRouteDef", + "RouteDef", + "RouteTableDef", + "StaticDef", + "delete", + "get", + "head", + "options", + "patch", + "post", + "put", + "route", + "static", + "view", + # web_runner + "AppRunner", + "BaseRunner", + "BaseSite", + "GracefulExit", + "ServerRunner", + "SockSite", + "TCPSite", + "UnixSite", + "NamedPipeSite", + # web_server + "Server", + # web_urldispatcher + "AbstractResource", + "AbstractRoute", + "DynamicResource", + "PlainResource", + "PrefixedSubAppResource", + "Resource", + "ResourceRoute", + "StaticResource", + "UrlDispatcher", + "UrlMappingMatchInfo", + "View", + # web_ws + "WebSocketReady", + "WebSocketResponse", + "WSMsgType", + # web + "run_app", +) + + +if TYPE_CHECKING: + from ssl import SSLContext +else: + try: + from ssl import SSLContext + except ImportError: # pragma: no cover + SSLContext = object # type: ignore[misc,assignment] + +# Only display warning when using -Wdefault, -We, -X dev or similar. +warnings.filterwarnings("ignore", category=NotAppKeyWarning, append=True) + +HostSequence = TypingIterable[str] + + +async def _run_app( + app: Union[Application, Awaitable[Application]], + *, + host: Optional[Union[str, HostSequence]] = None, + port: Optional[int] = None, + path: Union[PathLike, TypingIterable[PathLike], None] = None, + sock: Optional[Union[socket.socket, TypingIterable[socket.socket]]] = None, + ssl_context: Optional[SSLContext] = None, + print: Optional[Callable[..., None]] = print, + backlog: int = 128, + reuse_address: Optional[bool] = None, + reuse_port: Optional[bool] = None, + **kwargs: Any, # TODO(PY311): Use Unpack +) -> None: + # An internal function to actually do all dirty job for application running + if asyncio.iscoroutine(app): + app = await app + + app = cast(Application, app) + + runner = AppRunner(app, **kwargs) + + await runner.setup() + + sites: List[BaseSite] = [] + + try: + if host is not None: + if isinstance(host, str): + sites.append( + TCPSite( + runner, + host, + port, + ssl_context=ssl_context, + backlog=backlog, + reuse_address=reuse_address, + reuse_port=reuse_port, + ) + ) + else: + for h in host: + sites.append( + TCPSite( + runner, + h, + port, + ssl_context=ssl_context, + backlog=backlog, + reuse_address=reuse_address, + reuse_port=reuse_port, + ) + ) + elif path is None and sock is None or port is not None: + sites.append( + TCPSite( + runner, + port=port, + ssl_context=ssl_context, + backlog=backlog, + reuse_address=reuse_address, + reuse_port=reuse_port, + ) + ) + + if path is not None: + if isinstance(path, (str, os.PathLike)): + sites.append( + UnixSite( + runner, + path, + ssl_context=ssl_context, + backlog=backlog, + ) + ) + else: + for p in path: + sites.append( + UnixSite( + runner, + p, + ssl_context=ssl_context, + backlog=backlog, + ) + ) + + if sock is not None: + if not isinstance(sock, Iterable): + sites.append( + SockSite( + runner, + sock, + ssl_context=ssl_context, + backlog=backlog, + ) + ) + else: + for s in sock: + sites.append( + SockSite( + runner, + s, + ssl_context=ssl_context, + backlog=backlog, + ) + ) + for site in sites: + await site.start() + + if print: # pragma: no branch + names = sorted(str(s.name) for s in runner.sites) + print( + "======== Running on {} ========\n" + "(Press CTRL+C to quit)".format(", ".join(names)) + ) + + # sleep forever by 1 hour intervals, + while True: + await asyncio.sleep(3600) + finally: + await runner.cleanup() + + +def _cancel_tasks( + to_cancel: Set["asyncio.Task[Any]"], loop: asyncio.AbstractEventLoop +) -> None: + if not to_cancel: + return + + for task in to_cancel: + task.cancel() + + loop.run_until_complete(asyncio.gather(*to_cancel, return_exceptions=True)) + + for task in to_cancel: + if task.cancelled(): + continue + if task.exception() is not None: + loop.call_exception_handler( + { + "message": "unhandled exception during asyncio.run() shutdown", + "exception": task.exception(), + "task": task, + } + ) + + +def run_app( + app: Union[Application, Awaitable[Application]], + *, + host: Optional[Union[str, HostSequence]] = None, + port: Optional[int] = None, + path: Union[PathLike, TypingIterable[PathLike], None] = None, + sock: Optional[Union[socket.socket, TypingIterable[socket.socket]]] = None, + shutdown_timeout: float = 60.0, + keepalive_timeout: float = 75.0, + ssl_context: Optional[SSLContext] = None, + print: Optional[Callable[..., None]] = print, + backlog: int = 128, + access_log_class: Type[AbstractAccessLogger] = AccessLogger, + access_log_format: str = AccessLogger.LOG_FORMAT, + access_log: Optional[logging.Logger] = access_logger, + handle_signals: bool = True, + reuse_address: Optional[bool] = None, + reuse_port: Optional[bool] = None, + handler_cancellation: bool = False, + loop: Optional[asyncio.AbstractEventLoop] = None, + **kwargs: Any, +) -> None: + """Run an app locally""" + if loop is None: + loop = asyncio.new_event_loop() + + # Configure if and only if in debugging mode and using the default logger + if loop.get_debug() and access_log and access_log.name == "aiohttp.access": + if access_log.level == logging.NOTSET: + access_log.setLevel(logging.DEBUG) + if not access_log.hasHandlers(): + access_log.addHandler(logging.StreamHandler()) + + main_task = loop.create_task( + _run_app( + app, + host=host, + port=port, + path=path, + sock=sock, + shutdown_timeout=shutdown_timeout, + keepalive_timeout=keepalive_timeout, + ssl_context=ssl_context, + print=print, + backlog=backlog, + access_log_class=access_log_class, + access_log_format=access_log_format, + access_log=access_log, + handle_signals=handle_signals, + reuse_address=reuse_address, + reuse_port=reuse_port, + handler_cancellation=handler_cancellation, + **kwargs, + ) + ) + + try: + asyncio.set_event_loop(loop) + loop.run_until_complete(main_task) + except (GracefulExit, KeyboardInterrupt): # pragma: no cover + pass + finally: + try: + main_task.cancel() + with suppress(asyncio.CancelledError): + loop.run_until_complete(main_task) + finally: + _cancel_tasks(asyncio.all_tasks(loop), loop) + loop.run_until_complete(loop.shutdown_asyncgens()) + loop.close() + + +def main(argv: List[str]) -> None: + arg_parser = ArgumentParser( + description="aiohttp.web Application server", prog="aiohttp.web" + ) + arg_parser.add_argument( + "entry_func", + help=( + "Callable returning the `aiohttp.web.Application` instance to " + "run. Should be specified in the 'module:function' syntax." + ), + metavar="entry-func", + ) + arg_parser.add_argument( + "-H", + "--hostname", + help="TCP/IP hostname to serve on (default: localhost)", + default=None, + ) + arg_parser.add_argument( + "-P", + "--port", + help="TCP/IP port to serve on (default: %(default)r)", + type=int, + default=8080, + ) + arg_parser.add_argument( + "-U", + "--path", + help="Unix file system path to serve on. Can be combined with hostname " + "to serve on both Unix and TCP.", + ) + args, extra_argv = arg_parser.parse_known_args(argv) + + # Import logic + mod_str, _, func_str = args.entry_func.partition(":") + if not func_str or not mod_str: + arg_parser.error("'entry-func' not in 'module:function' syntax") + if mod_str.startswith("."): + arg_parser.error("relative module names not supported") + try: + module = import_module(mod_str) + except ImportError as ex: + arg_parser.error(f"unable to import {mod_str}: {ex}") + try: + func = getattr(module, func_str) + except AttributeError: + arg_parser.error(f"module {mod_str!r} has no attribute {func_str!r}") + + # Compatibility logic + if args.path is not None and not hasattr(socket, "AF_UNIX"): + arg_parser.error( + "file system paths not supported by your operating environment" + ) + + logging.basicConfig(level=logging.DEBUG) + + if args.path and args.hostname is None: + host = port = None + else: + host = args.hostname or "localhost" + port = args.port + + app = func(extra_argv) + run_app(app, host=host, port=port, path=args.path) + arg_parser.exit(message="Stopped\n") + + +if __name__ == "__main__": # pragma: no branch + main(sys.argv[1:]) # pragma: no cover diff --git a/.venv/lib/python3.9/site-packages/aiohttp/web_app.py b/.venv/lib/python3.9/site-packages/aiohttp/web_app.py new file mode 100644 index 0000000..619c008 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/web_app.py @@ -0,0 +1,620 @@ +import asyncio +import logging +import warnings +from functools import lru_cache, partial, update_wrapper +from typing import ( + TYPE_CHECKING, + Any, + AsyncIterator, + Awaitable, + Callable, + Dict, + Iterable, + Iterator, + List, + Mapping, + MutableMapping, + Optional, + Sequence, + Tuple, + Type, + TypeVar, + Union, + cast, + overload, +) + +from aiosignal import Signal +from frozenlist import FrozenList + +from . import hdrs +from .abc import ( + AbstractAccessLogger, + AbstractMatchInfo, + AbstractRouter, + AbstractStreamWriter, +) +from .helpers import DEBUG, AppKey +from .http_parser import RawRequestMessage +from .log import web_logger +from .streams import StreamReader +from .typedefs import Handler, Middleware +from .web_exceptions import NotAppKeyWarning +from .web_log import AccessLogger +from .web_middlewares import _fix_request_current_app +from .web_protocol import RequestHandler +from .web_request import Request +from .web_response import StreamResponse +from .web_routedef import AbstractRouteDef +from .web_server import Server +from .web_urldispatcher import ( + AbstractResource, + AbstractRoute, + Domain, + MaskDomain, + MatchedSubAppResource, + PrefixedSubAppResource, + SystemRoute, + UrlDispatcher, +) + +__all__ = ("Application", "CleanupError") + + +if TYPE_CHECKING: + _AppSignal = Signal["Application"] + _RespPrepareSignal = Signal[Request, StreamResponse] + _Middlewares = FrozenList[Middleware] + _MiddlewaresHandlers = Optional[Sequence[Tuple[Middleware, bool]]] + _Subapps = List["Application"] +else: + # No type checker mode, skip types + _AppSignal = Signal + _RespPrepareSignal = Signal + _Middlewares = FrozenList + _MiddlewaresHandlers = Optional[Sequence] + _Subapps = List + +_T = TypeVar("_T") +_U = TypeVar("_U") +_Resource = TypeVar("_Resource", bound=AbstractResource) + + +def _build_middlewares( + handler: Handler, apps: Tuple["Application", ...] +) -> Callable[[Request], Awaitable[StreamResponse]]: + """Apply middlewares to handler.""" + for app in apps[::-1]: + for m, _ in app._middlewares_handlers: # type: ignore[union-attr] + handler = update_wrapper(partial(m, handler=handler), handler) + return handler + + +_cached_build_middleware = lru_cache(maxsize=1024)(_build_middlewares) + + +class Application(MutableMapping[Union[str, AppKey[Any]], Any]): + ATTRS = frozenset( + [ + "logger", + "_debug", + "_router", + "_loop", + "_handler_args", + "_middlewares", + "_middlewares_handlers", + "_has_legacy_middlewares", + "_run_middlewares", + "_state", + "_frozen", + "_pre_frozen", + "_subapps", + "_on_response_prepare", + "_on_startup", + "_on_shutdown", + "_on_cleanup", + "_client_max_size", + "_cleanup_ctx", + ] + ) + + def __init__( + self, + *, + logger: logging.Logger = web_logger, + router: Optional[UrlDispatcher] = None, + middlewares: Iterable[Middleware] = (), + handler_args: Optional[Mapping[str, Any]] = None, + client_max_size: int = 1024**2, + loop: Optional[asyncio.AbstractEventLoop] = None, + debug: Any = ..., # mypy doesn't support ellipsis + ) -> None: + if router is None: + router = UrlDispatcher() + else: + warnings.warn( + "router argument is deprecated", DeprecationWarning, stacklevel=2 + ) + assert isinstance(router, AbstractRouter), router + + if loop is not None: + warnings.warn( + "loop argument is deprecated", DeprecationWarning, stacklevel=2 + ) + + if debug is not ...: + warnings.warn( + "debug argument is deprecated", DeprecationWarning, stacklevel=2 + ) + self._debug = debug + self._router: UrlDispatcher = router + self._loop = loop + self._handler_args = handler_args + self.logger = logger + + self._middlewares: _Middlewares = FrozenList(middlewares) + + # initialized on freezing + self._middlewares_handlers: _MiddlewaresHandlers = None + # initialized on freezing + self._run_middlewares: Optional[bool] = None + self._has_legacy_middlewares: bool = True + + self._state: Dict[Union[AppKey[Any], str], object] = {} + self._frozen = False + self._pre_frozen = False + self._subapps: _Subapps = [] + + self._on_response_prepare: _RespPrepareSignal = Signal(self) + self._on_startup: _AppSignal = Signal(self) + self._on_shutdown: _AppSignal = Signal(self) + self._on_cleanup: _AppSignal = Signal(self) + self._cleanup_ctx = CleanupContext() + self._on_startup.append(self._cleanup_ctx._on_startup) + self._on_cleanup.append(self._cleanup_ctx._on_cleanup) + self._client_max_size = client_max_size + + def __init_subclass__(cls: Type["Application"]) -> None: + warnings.warn( + "Inheritance class {} from web.Application " + "is discouraged".format(cls.__name__), + DeprecationWarning, + stacklevel=3, + ) + + if DEBUG: # pragma: no cover + + def __setattr__(self, name: str, val: Any) -> None: + if name not in self.ATTRS: + warnings.warn( + "Setting custom web.Application.{} attribute " + "is discouraged".format(name), + DeprecationWarning, + stacklevel=2, + ) + super().__setattr__(name, val) + + # MutableMapping API + + def __eq__(self, other: object) -> bool: + return self is other + + @overload # type: ignore[override] + def __getitem__(self, key: AppKey[_T]) -> _T: ... + + @overload + def __getitem__(self, key: str) -> Any: ... + + def __getitem__(self, key: Union[str, AppKey[_T]]) -> Any: + return self._state[key] + + def _check_frozen(self) -> None: + if self._frozen: + warnings.warn( + "Changing state of started or joined application is deprecated", + DeprecationWarning, + stacklevel=3, + ) + + @overload # type: ignore[override] + def __setitem__(self, key: AppKey[_T], value: _T) -> None: ... + + @overload + def __setitem__(self, key: str, value: Any) -> None: ... + + def __setitem__(self, key: Union[str, AppKey[_T]], value: Any) -> None: + self._check_frozen() + if not isinstance(key, AppKey): + warnings.warn( + "It is recommended to use web.AppKey instances for keys.\n" + + "https://docs.aiohttp.org/en/stable/web_advanced.html" + + "#application-s-config", + category=NotAppKeyWarning, + stacklevel=2, + ) + self._state[key] = value + + def __delitem__(self, key: Union[str, AppKey[_T]]) -> None: + self._check_frozen() + del self._state[key] + + def __len__(self) -> int: + return len(self._state) + + def __iter__(self) -> Iterator[Union[str, AppKey[Any]]]: + return iter(self._state) + + def __hash__(self) -> int: + return id(self) + + @overload # type: ignore[override] + def get(self, key: AppKey[_T], default: None = ...) -> Optional[_T]: ... + + @overload + def get(self, key: AppKey[_T], default: _U) -> Union[_T, _U]: ... + + @overload + def get(self, key: str, default: Any = ...) -> Any: ... + + def get(self, key: Union[str, AppKey[_T]], default: Any = None) -> Any: + return self._state.get(key, default) + + ######## + @property + def loop(self) -> asyncio.AbstractEventLoop: + # Technically the loop can be None + # but we mask it by explicit type cast + # to provide more convenient type annotation + warnings.warn("loop property is deprecated", DeprecationWarning, stacklevel=2) + return cast(asyncio.AbstractEventLoop, self._loop) + + def _set_loop(self, loop: Optional[asyncio.AbstractEventLoop]) -> None: + if loop is None: + loop = asyncio.get_event_loop() + if self._loop is not None and self._loop is not loop: + raise RuntimeError( + "web.Application instance initialized with different loop" + ) + + self._loop = loop + + # set loop debug + if self._debug is ...: + self._debug = loop.get_debug() + + # set loop to sub applications + for subapp in self._subapps: + subapp._set_loop(loop) + + @property + def pre_frozen(self) -> bool: + return self._pre_frozen + + def pre_freeze(self) -> None: + if self._pre_frozen: + return + + self._pre_frozen = True + self._middlewares.freeze() + self._router.freeze() + self._on_response_prepare.freeze() + self._cleanup_ctx.freeze() + self._on_startup.freeze() + self._on_shutdown.freeze() + self._on_cleanup.freeze() + self._middlewares_handlers = tuple(self._prepare_middleware()) + self._has_legacy_middlewares = any( + not new_style for _, new_style in self._middlewares_handlers + ) + + # If current app and any subapp do not have middlewares avoid run all + # of the code footprint that it implies, which have a middleware + # hardcoded per app that sets up the current_app attribute. If no + # middlewares are configured the handler will receive the proper + # current_app without needing all of this code. + self._run_middlewares = True if self.middlewares else False + + for subapp in self._subapps: + subapp.pre_freeze() + self._run_middlewares = self._run_middlewares or subapp._run_middlewares + + @property + def frozen(self) -> bool: + return self._frozen + + def freeze(self) -> None: + if self._frozen: + return + + self.pre_freeze() + self._frozen = True + for subapp in self._subapps: + subapp.freeze() + + @property + def debug(self) -> bool: + warnings.warn("debug property is deprecated", DeprecationWarning, stacklevel=2) + return self._debug # type: ignore[no-any-return] + + def _reg_subapp_signals(self, subapp: "Application") -> None: + def reg_handler(signame: str) -> None: + subsig = getattr(subapp, signame) + + async def handler(app: "Application") -> None: + await subsig.send(subapp) + + appsig = getattr(self, signame) + appsig.append(handler) + + reg_handler("on_startup") + reg_handler("on_shutdown") + reg_handler("on_cleanup") + + def add_subapp(self, prefix: str, subapp: "Application") -> PrefixedSubAppResource: + if not isinstance(prefix, str): + raise TypeError("Prefix must be str") + prefix = prefix.rstrip("/") + if not prefix: + raise ValueError("Prefix cannot be empty") + factory = partial(PrefixedSubAppResource, prefix, subapp) + return self._add_subapp(factory, subapp) + + def _add_subapp( + self, resource_factory: Callable[[], _Resource], subapp: "Application" + ) -> _Resource: + if self.frozen: + raise RuntimeError("Cannot add sub application to frozen application") + if subapp.frozen: + raise RuntimeError("Cannot add frozen application") + resource = resource_factory() + self.router.register_resource(resource) + self._reg_subapp_signals(subapp) + self._subapps.append(subapp) + subapp.pre_freeze() + if self._loop is not None: + subapp._set_loop(self._loop) + return resource + + def add_domain(self, domain: str, subapp: "Application") -> MatchedSubAppResource: + if not isinstance(domain, str): + raise TypeError("Domain must be str") + elif "*" in domain: + rule: Domain = MaskDomain(domain) + else: + rule = Domain(domain) + factory = partial(MatchedSubAppResource, rule, subapp) + return self._add_subapp(factory, subapp) + + def add_routes(self, routes: Iterable[AbstractRouteDef]) -> List[AbstractRoute]: + return self.router.add_routes(routes) + + @property + def on_response_prepare(self) -> _RespPrepareSignal: + return self._on_response_prepare + + @property + def on_startup(self) -> _AppSignal: + return self._on_startup + + @property + def on_shutdown(self) -> _AppSignal: + return self._on_shutdown + + @property + def on_cleanup(self) -> _AppSignal: + return self._on_cleanup + + @property + def cleanup_ctx(self) -> "CleanupContext": + return self._cleanup_ctx + + @property + def router(self) -> UrlDispatcher: + return self._router + + @property + def middlewares(self) -> _Middlewares: + return self._middlewares + + def _make_handler( + self, + *, + loop: Optional[asyncio.AbstractEventLoop] = None, + access_log_class: Type[AbstractAccessLogger] = AccessLogger, + **kwargs: Any, + ) -> Server: + + if not issubclass(access_log_class, AbstractAccessLogger): + raise TypeError( + "access_log_class must be subclass of " + "aiohttp.abc.AbstractAccessLogger, got {}".format(access_log_class) + ) + + self._set_loop(loop) + self.freeze() + + kwargs["debug"] = self._debug + kwargs["access_log_class"] = access_log_class + if self._handler_args: + for k, v in self._handler_args.items(): + kwargs[k] = v + + return Server( + self._handle, # type: ignore[arg-type] + request_factory=self._make_request, + loop=self._loop, + **kwargs, + ) + + def make_handler( + self, + *, + loop: Optional[asyncio.AbstractEventLoop] = None, + access_log_class: Type[AbstractAccessLogger] = AccessLogger, + **kwargs: Any, + ) -> Server: + + warnings.warn( + "Application.make_handler(...) is deprecated, use AppRunner API instead", + DeprecationWarning, + stacklevel=2, + ) + + return self._make_handler( + loop=loop, access_log_class=access_log_class, **kwargs + ) + + async def startup(self) -> None: + """Causes on_startup signal + + Should be called in the event loop along with the request handler. + """ + await self.on_startup.send(self) + + async def shutdown(self) -> None: + """Causes on_shutdown signal + + Should be called before cleanup() + """ + await self.on_shutdown.send(self) + + async def cleanup(self) -> None: + """Causes on_cleanup signal + + Should be called after shutdown() + """ + if self.on_cleanup.frozen: + await self.on_cleanup.send(self) + else: + # If an exception occurs in startup, ensure cleanup contexts are completed. + await self._cleanup_ctx._on_cleanup(self) + + def _make_request( + self, + message: RawRequestMessage, + payload: StreamReader, + protocol: RequestHandler, + writer: AbstractStreamWriter, + task: "asyncio.Task[None]", + _cls: Type[Request] = Request, + ) -> Request: + if TYPE_CHECKING: + assert self._loop is not None + return _cls( + message, + payload, + protocol, + writer, + task, + self._loop, + client_max_size=self._client_max_size, + ) + + def _prepare_middleware(self) -> Iterator[Tuple[Middleware, bool]]: + for m in reversed(self._middlewares): + if getattr(m, "__middleware_version__", None) == 1: + yield m, True + else: + warnings.warn( + f'old-style middleware "{m!r}" deprecated, see #2252', + DeprecationWarning, + stacklevel=2, + ) + yield m, False + + yield _fix_request_current_app(self), True + + async def _handle(self, request: Request) -> StreamResponse: + loop = asyncio.get_event_loop() + debug = loop.get_debug() + match_info = await self._router.resolve(request) + if debug: # pragma: no cover + if not isinstance(match_info, AbstractMatchInfo): + raise TypeError( + "match_info should be AbstractMatchInfo " + "instance, not {!r}".format(match_info) + ) + match_info.add_app(self) + + match_info.freeze() + + request._match_info = match_info + + if request.headers.get(hdrs.EXPECT): + resp = await match_info.expect_handler(request) + await request.writer.drain() + if resp is not None: + return resp + + handler = match_info.handler + + if self._run_middlewares: + # If its a SystemRoute, don't cache building the middlewares since + # they are constructed for every MatchInfoError as a new handler + # is made each time. + if not self._has_legacy_middlewares and not isinstance( + match_info.route, SystemRoute + ): + handler = _cached_build_middleware(handler, match_info.apps) + else: + for app in match_info.apps[::-1]: + for m, new_style in app._middlewares_handlers: # type: ignore[union-attr] + if new_style: + handler = update_wrapper( + partial(m, handler=handler), handler + ) + else: + handler = await m(app, handler) # type: ignore[arg-type,assignment] + + return await handler(request) + + def __call__(self) -> "Application": + """gunicorn compatibility""" + return self + + def __repr__(self) -> str: + return f"" + + def __bool__(self) -> bool: + return True + + +class CleanupError(RuntimeError): + @property + def exceptions(self) -> List[BaseException]: + return cast(List[BaseException], self.args[1]) + + +if TYPE_CHECKING: + _CleanupContextBase = FrozenList[Callable[[Application], AsyncIterator[None]]] +else: + _CleanupContextBase = FrozenList + + +class CleanupContext(_CleanupContextBase): + def __init__(self) -> None: + super().__init__() + self._exits: List[AsyncIterator[None]] = [] + + async def _on_startup(self, app: Application) -> None: + for cb in self: + it = cb(app).__aiter__() + await it.__anext__() + self._exits.append(it) + + async def _on_cleanup(self, app: Application) -> None: + errors = [] + for it in reversed(self._exits): + try: + await it.__anext__() + except StopAsyncIteration: + pass + except (Exception, asyncio.CancelledError) as exc: + errors.append(exc) + else: + errors.append(RuntimeError(f"{it!r} has more than one 'yield'")) + if errors: + if len(errors) == 1: + raise errors[0] + else: + raise CleanupError("Multiple errors on cleanup stage", errors) diff --git a/.venv/lib/python3.9/site-packages/aiohttp/web_exceptions.py b/.venv/lib/python3.9/site-packages/aiohttp/web_exceptions.py new file mode 100644 index 0000000..ee2c1e7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/web_exceptions.py @@ -0,0 +1,452 @@ +import warnings +from typing import Any, Dict, Iterable, List, Optional, Set # noqa + +from yarl import URL + +from .typedefs import LooseHeaders, StrOrURL +from .web_response import Response + +__all__ = ( + "HTTPException", + "HTTPError", + "HTTPRedirection", + "HTTPSuccessful", + "HTTPOk", + "HTTPCreated", + "HTTPAccepted", + "HTTPNonAuthoritativeInformation", + "HTTPNoContent", + "HTTPResetContent", + "HTTPPartialContent", + "HTTPMove", + "HTTPMultipleChoices", + "HTTPMovedPermanently", + "HTTPFound", + "HTTPSeeOther", + "HTTPNotModified", + "HTTPUseProxy", + "HTTPTemporaryRedirect", + "HTTPPermanentRedirect", + "HTTPClientError", + "HTTPBadRequest", + "HTTPUnauthorized", + "HTTPPaymentRequired", + "HTTPForbidden", + "HTTPNotFound", + "HTTPMethodNotAllowed", + "HTTPNotAcceptable", + "HTTPProxyAuthenticationRequired", + "HTTPRequestTimeout", + "HTTPConflict", + "HTTPGone", + "HTTPLengthRequired", + "HTTPPreconditionFailed", + "HTTPRequestEntityTooLarge", + "HTTPRequestURITooLong", + "HTTPUnsupportedMediaType", + "HTTPRequestRangeNotSatisfiable", + "HTTPExpectationFailed", + "HTTPMisdirectedRequest", + "HTTPUnprocessableEntity", + "HTTPFailedDependency", + "HTTPUpgradeRequired", + "HTTPPreconditionRequired", + "HTTPTooManyRequests", + "HTTPRequestHeaderFieldsTooLarge", + "HTTPUnavailableForLegalReasons", + "HTTPServerError", + "HTTPInternalServerError", + "HTTPNotImplemented", + "HTTPBadGateway", + "HTTPServiceUnavailable", + "HTTPGatewayTimeout", + "HTTPVersionNotSupported", + "HTTPVariantAlsoNegotiates", + "HTTPInsufficientStorage", + "HTTPNotExtended", + "HTTPNetworkAuthenticationRequired", +) + + +class NotAppKeyWarning(UserWarning): + """Warning when not using AppKey in Application.""" + + +############################################################ +# HTTP Exceptions +############################################################ + + +class HTTPException(Response, Exception): + + # You should set in subclasses: + # status = 200 + + status_code = -1 + empty_body = False + + __http_exception__ = True + + def __init__( + self, + *, + headers: Optional[LooseHeaders] = None, + reason: Optional[str] = None, + body: Any = None, + text: Optional[str] = None, + content_type: Optional[str] = None, + ) -> None: + if body is not None: + warnings.warn( + "body argument is deprecated for http web exceptions", + DeprecationWarning, + ) + Response.__init__( + self, + status=self.status_code, + headers=headers, + reason=reason, + body=body, + text=text, + content_type=content_type, + ) + Exception.__init__(self, self.reason) + if self.body is None and not self.empty_body: + self.text = f"{self.status}: {self.reason}" + + def __bool__(self) -> bool: + return True + + +class HTTPError(HTTPException): + """Base class for exceptions with status codes in the 400s and 500s.""" + + +class HTTPRedirection(HTTPException): + """Base class for exceptions with status codes in the 300s.""" + + +class HTTPSuccessful(HTTPException): + """Base class for exceptions with status codes in the 200s.""" + + +class HTTPOk(HTTPSuccessful): + status_code = 200 + + +class HTTPCreated(HTTPSuccessful): + status_code = 201 + + +class HTTPAccepted(HTTPSuccessful): + status_code = 202 + + +class HTTPNonAuthoritativeInformation(HTTPSuccessful): + status_code = 203 + + +class HTTPNoContent(HTTPSuccessful): + status_code = 204 + empty_body = True + + +class HTTPResetContent(HTTPSuccessful): + status_code = 205 + empty_body = True + + +class HTTPPartialContent(HTTPSuccessful): + status_code = 206 + + +############################################################ +# 3xx redirection +############################################################ + + +class HTTPMove(HTTPRedirection): + def __init__( + self, + location: StrOrURL, + *, + headers: Optional[LooseHeaders] = None, + reason: Optional[str] = None, + body: Any = None, + text: Optional[str] = None, + content_type: Optional[str] = None, + ) -> None: + if not location: + raise ValueError("HTTP redirects need a location to redirect to.") + super().__init__( + headers=headers, + reason=reason, + body=body, + text=text, + content_type=content_type, + ) + self.headers["Location"] = str(URL(location)) + self.location = location + + +class HTTPMultipleChoices(HTTPMove): + status_code = 300 + + +class HTTPMovedPermanently(HTTPMove): + status_code = 301 + + +class HTTPFound(HTTPMove): + status_code = 302 + + +# This one is safe after a POST (the redirected location will be +# retrieved with GET): +class HTTPSeeOther(HTTPMove): + status_code = 303 + + +class HTTPNotModified(HTTPRedirection): + # FIXME: this should include a date or etag header + status_code = 304 + empty_body = True + + +class HTTPUseProxy(HTTPMove): + # Not a move, but looks a little like one + status_code = 305 + + +class HTTPTemporaryRedirect(HTTPMove): + status_code = 307 + + +class HTTPPermanentRedirect(HTTPMove): + status_code = 308 + + +############################################################ +# 4xx client error +############################################################ + + +class HTTPClientError(HTTPError): + pass + + +class HTTPBadRequest(HTTPClientError): + status_code = 400 + + +class HTTPUnauthorized(HTTPClientError): + status_code = 401 + + +class HTTPPaymentRequired(HTTPClientError): + status_code = 402 + + +class HTTPForbidden(HTTPClientError): + status_code = 403 + + +class HTTPNotFound(HTTPClientError): + status_code = 404 + + +class HTTPMethodNotAllowed(HTTPClientError): + status_code = 405 + + def __init__( + self, + method: str, + allowed_methods: Iterable[str], + *, + headers: Optional[LooseHeaders] = None, + reason: Optional[str] = None, + body: Any = None, + text: Optional[str] = None, + content_type: Optional[str] = None, + ) -> None: + allow = ",".join(sorted(allowed_methods)) + super().__init__( + headers=headers, + reason=reason, + body=body, + text=text, + content_type=content_type, + ) + self.headers["Allow"] = allow + self.allowed_methods: Set[str] = set(allowed_methods) + self.method = method.upper() + + +class HTTPNotAcceptable(HTTPClientError): + status_code = 406 + + +class HTTPProxyAuthenticationRequired(HTTPClientError): + status_code = 407 + + +class HTTPRequestTimeout(HTTPClientError): + status_code = 408 + + +class HTTPConflict(HTTPClientError): + status_code = 409 + + +class HTTPGone(HTTPClientError): + status_code = 410 + + +class HTTPLengthRequired(HTTPClientError): + status_code = 411 + + +class HTTPPreconditionFailed(HTTPClientError): + status_code = 412 + + +class HTTPRequestEntityTooLarge(HTTPClientError): + status_code = 413 + + def __init__(self, max_size: float, actual_size: float, **kwargs: Any) -> None: + kwargs.setdefault( + "text", + "Maximum request body size {} exceeded, " + "actual body size {}".format(max_size, actual_size), + ) + super().__init__(**kwargs) + + +class HTTPRequestURITooLong(HTTPClientError): + status_code = 414 + + +class HTTPUnsupportedMediaType(HTTPClientError): + status_code = 415 + + +class HTTPRequestRangeNotSatisfiable(HTTPClientError): + status_code = 416 + + +class HTTPExpectationFailed(HTTPClientError): + status_code = 417 + + +class HTTPMisdirectedRequest(HTTPClientError): + status_code = 421 + + +class HTTPUnprocessableEntity(HTTPClientError): + status_code = 422 + + +class HTTPFailedDependency(HTTPClientError): + status_code = 424 + + +class HTTPUpgradeRequired(HTTPClientError): + status_code = 426 + + +class HTTPPreconditionRequired(HTTPClientError): + status_code = 428 + + +class HTTPTooManyRequests(HTTPClientError): + status_code = 429 + + +class HTTPRequestHeaderFieldsTooLarge(HTTPClientError): + status_code = 431 + + +class HTTPUnavailableForLegalReasons(HTTPClientError): + status_code = 451 + + def __init__( + self, + link: Optional[StrOrURL], + *, + headers: Optional[LooseHeaders] = None, + reason: Optional[str] = None, + body: Any = None, + text: Optional[str] = None, + content_type: Optional[str] = None, + ) -> None: + super().__init__( + headers=headers, + reason=reason, + body=body, + text=text, + content_type=content_type, + ) + self._link = None + if link: + self._link = URL(link) + self.headers["Link"] = f'<{str(self._link)}>; rel="blocked-by"' + + @property + def link(self) -> Optional[URL]: + return self._link + + +############################################################ +# 5xx Server Error +############################################################ +# Response status codes beginning with the digit "5" indicate cases in +# which the server is aware that it has erred or is incapable of +# performing the request. Except when responding to a HEAD request, the +# server SHOULD include an entity containing an explanation of the error +# situation, and whether it is a temporary or permanent condition. User +# agents SHOULD display any included entity to the user. These response +# codes are applicable to any request method. + + +class HTTPServerError(HTTPError): + pass + + +class HTTPInternalServerError(HTTPServerError): + status_code = 500 + + +class HTTPNotImplemented(HTTPServerError): + status_code = 501 + + +class HTTPBadGateway(HTTPServerError): + status_code = 502 + + +class HTTPServiceUnavailable(HTTPServerError): + status_code = 503 + + +class HTTPGatewayTimeout(HTTPServerError): + status_code = 504 + + +class HTTPVersionNotSupported(HTTPServerError): + status_code = 505 + + +class HTTPVariantAlsoNegotiates(HTTPServerError): + status_code = 506 + + +class HTTPInsufficientStorage(HTTPServerError): + status_code = 507 + + +class HTTPNotExtended(HTTPServerError): + status_code = 510 + + +class HTTPNetworkAuthenticationRequired(HTTPServerError): + status_code = 511 diff --git a/.venv/lib/python3.9/site-packages/aiohttp/web_fileresponse.py b/.venv/lib/python3.9/site-packages/aiohttp/web_fileresponse.py new file mode 100644 index 0000000..26484b9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/web_fileresponse.py @@ -0,0 +1,418 @@ +import asyncio +import io +import os +import pathlib +import sys +from contextlib import suppress +from enum import Enum, auto +from mimetypes import MimeTypes +from stat import S_ISREG +from types import MappingProxyType +from typing import ( # noqa + IO, + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Final, + Iterator, + List, + Optional, + Set, + Tuple, + Union, + cast, +) + +from . import hdrs +from .abc import AbstractStreamWriter +from .helpers import ETAG_ANY, ETag, must_be_empty_body +from .typedefs import LooseHeaders, PathLike +from .web_exceptions import ( + HTTPForbidden, + HTTPNotFound, + HTTPNotModified, + HTTPPartialContent, + HTTPPreconditionFailed, + HTTPRequestRangeNotSatisfiable, +) +from .web_response import StreamResponse + +__all__ = ("FileResponse",) + +if TYPE_CHECKING: + from .web_request import BaseRequest + + +_T_OnChunkSent = Optional[Callable[[bytes], Awaitable[None]]] + + +NOSENDFILE: Final[bool] = bool(os.environ.get("AIOHTTP_NOSENDFILE")) + +CONTENT_TYPES: Final[MimeTypes] = MimeTypes() + +# File extension to IANA encodings map that will be checked in the order defined. +ENCODING_EXTENSIONS = MappingProxyType( + {ext: CONTENT_TYPES.encodings_map[ext] for ext in (".br", ".gz")} +) + +FALLBACK_CONTENT_TYPE = "application/octet-stream" + +# Provide additional MIME type/extension pairs to be recognized. +# https://en.wikipedia.org/wiki/List_of_archive_formats#Compression_only +ADDITIONAL_CONTENT_TYPES = MappingProxyType( + { + "application/gzip": ".gz", + "application/x-brotli": ".br", + "application/x-bzip2": ".bz2", + "application/x-compress": ".Z", + "application/x-xz": ".xz", + } +) + + +class _FileResponseResult(Enum): + """The result of the file response.""" + + SEND_FILE = auto() # Ie a regular file to send + NOT_ACCEPTABLE = auto() # Ie a socket, or non-regular file + PRE_CONDITION_FAILED = auto() # Ie If-Match or If-None-Match failed + NOT_MODIFIED = auto() # 304 Not Modified + + +# Add custom pairs and clear the encodings map so guess_type ignores them. +CONTENT_TYPES.encodings_map.clear() +for content_type, extension in ADDITIONAL_CONTENT_TYPES.items(): + CONTENT_TYPES.add_type(content_type, extension) + + +_CLOSE_FUTURES: Set[asyncio.Future[None]] = set() + + +class FileResponse(StreamResponse): + """A response object can be used to send files.""" + + def __init__( + self, + path: PathLike, + chunk_size: int = 256 * 1024, + status: int = 200, + reason: Optional[str] = None, + headers: Optional[LooseHeaders] = None, + ) -> None: + super().__init__(status=status, reason=reason, headers=headers) + + self._path = pathlib.Path(path) + self._chunk_size = chunk_size + + def _seek_and_read(self, fobj: IO[Any], offset: int, chunk_size: int) -> bytes: + fobj.seek(offset) + return fobj.read(chunk_size) # type: ignore[no-any-return] + + async def _sendfile_fallback( + self, writer: AbstractStreamWriter, fobj: IO[Any], offset: int, count: int + ) -> AbstractStreamWriter: + # To keep memory usage low,fobj is transferred in chunks + # controlled by the constructor's chunk_size argument. + + chunk_size = self._chunk_size + loop = asyncio.get_event_loop() + chunk = await loop.run_in_executor( + None, self._seek_and_read, fobj, offset, chunk_size + ) + while chunk: + await writer.write(chunk) + count = count - chunk_size + if count <= 0: + break + chunk = await loop.run_in_executor(None, fobj.read, min(chunk_size, count)) + + await writer.drain() + return writer + + async def _sendfile( + self, request: "BaseRequest", fobj: IO[Any], offset: int, count: int + ) -> AbstractStreamWriter: + writer = await super().prepare(request) + assert writer is not None + + if NOSENDFILE or self.compression: + return await self._sendfile_fallback(writer, fobj, offset, count) + + loop = request._loop + transport = request.transport + assert transport is not None + + try: + await loop.sendfile(transport, fobj, offset, count) + except NotImplementedError: + return await self._sendfile_fallback(writer, fobj, offset, count) + + await super().write_eof() + return writer + + @staticmethod + def _etag_match(etag_value: str, etags: Tuple[ETag, ...], *, weak: bool) -> bool: + if len(etags) == 1 and etags[0].value == ETAG_ANY: + return True + return any( + etag.value == etag_value for etag in etags if weak or not etag.is_weak + ) + + async def _not_modified( + self, request: "BaseRequest", etag_value: str, last_modified: float + ) -> Optional[AbstractStreamWriter]: + self.set_status(HTTPNotModified.status_code) + self._length_check = False + self.etag = etag_value + self.last_modified = last_modified + # Delete any Content-Length headers provided by user. HTTP 304 + # should always have empty response body + return await super().prepare(request) + + async def _precondition_failed( + self, request: "BaseRequest" + ) -> Optional[AbstractStreamWriter]: + self.set_status(HTTPPreconditionFailed.status_code) + self.content_length = 0 + return await super().prepare(request) + + def _make_response( + self, request: "BaseRequest", accept_encoding: str + ) -> Tuple[ + _FileResponseResult, Optional[io.BufferedReader], os.stat_result, Optional[str] + ]: + """Return the response result, io object, stat result, and encoding. + + If an uncompressed file is returned, the encoding is set to + :py:data:`None`. + + This method should be called from a thread executor + since it calls os.stat which may block. + """ + file_path, st, file_encoding = self._get_file_path_stat_encoding( + accept_encoding + ) + if not file_path: + return _FileResponseResult.NOT_ACCEPTABLE, None, st, None + + etag_value = f"{st.st_mtime_ns:x}-{st.st_size:x}" + + # https://www.rfc-editor.org/rfc/rfc9110#section-13.1.1-2 + if (ifmatch := request.if_match) is not None and not self._etag_match( + etag_value, ifmatch, weak=False + ): + return _FileResponseResult.PRE_CONDITION_FAILED, None, st, file_encoding + + if ( + (unmodsince := request.if_unmodified_since) is not None + and ifmatch is None + and st.st_mtime > unmodsince.timestamp() + ): + return _FileResponseResult.PRE_CONDITION_FAILED, None, st, file_encoding + + # https://www.rfc-editor.org/rfc/rfc9110#section-13.1.2-2 + if (ifnonematch := request.if_none_match) is not None and self._etag_match( + etag_value, ifnonematch, weak=True + ): + return _FileResponseResult.NOT_MODIFIED, None, st, file_encoding + + if ( + (modsince := request.if_modified_since) is not None + and ifnonematch is None + and st.st_mtime <= modsince.timestamp() + ): + return _FileResponseResult.NOT_MODIFIED, None, st, file_encoding + + fobj = file_path.open("rb") + with suppress(OSError): + # fstat() may not be available on all platforms + # Once we open the file, we want the fstat() to ensure + # the file has not changed between the first stat() + # and the open(). + st = os.stat(fobj.fileno()) + return _FileResponseResult.SEND_FILE, fobj, st, file_encoding + + def _get_file_path_stat_encoding( + self, accept_encoding: str + ) -> Tuple[Optional[pathlib.Path], os.stat_result, Optional[str]]: + file_path = self._path + for file_extension, file_encoding in ENCODING_EXTENSIONS.items(): + if file_encoding not in accept_encoding: + continue + + compressed_path = file_path.with_suffix(file_path.suffix + file_extension) + with suppress(OSError): + # Do not follow symlinks and ignore any non-regular files. + st = compressed_path.lstat() + if S_ISREG(st.st_mode): + return compressed_path, st, file_encoding + + # Fallback to the uncompressed file + st = file_path.stat() + return file_path if S_ISREG(st.st_mode) else None, st, None + + async def prepare(self, request: "BaseRequest") -> Optional[AbstractStreamWriter]: + loop = asyncio.get_running_loop() + # Encoding comparisons should be case-insensitive + # https://www.rfc-editor.org/rfc/rfc9110#section-8.4.1 + accept_encoding = request.headers.get(hdrs.ACCEPT_ENCODING, "").lower() + try: + response_result, fobj, st, file_encoding = await loop.run_in_executor( + None, self._make_response, request, accept_encoding + ) + except PermissionError: + self.set_status(HTTPForbidden.status_code) + return await super().prepare(request) + except OSError: + # Most likely to be FileNotFoundError or OSError for circular + # symlinks in python >= 3.13, so respond with 404. + self.set_status(HTTPNotFound.status_code) + return await super().prepare(request) + + # Forbid special files like sockets, pipes, devices, etc. + if response_result is _FileResponseResult.NOT_ACCEPTABLE: + self.set_status(HTTPForbidden.status_code) + return await super().prepare(request) + + if response_result is _FileResponseResult.PRE_CONDITION_FAILED: + return await self._precondition_failed(request) + + if response_result is _FileResponseResult.NOT_MODIFIED: + etag_value = f"{st.st_mtime_ns:x}-{st.st_size:x}" + last_modified = st.st_mtime + return await self._not_modified(request, etag_value, last_modified) + + assert fobj is not None + try: + return await self._prepare_open_file(request, fobj, st, file_encoding) + finally: + # We do not await here because we do not want to wait + # for the executor to finish before returning the response + # so the connection can begin servicing another request + # as soon as possible. + close_future = loop.run_in_executor(None, fobj.close) + # Hold a strong reference to the future to prevent it from being + # garbage collected before it completes. + _CLOSE_FUTURES.add(close_future) + close_future.add_done_callback(_CLOSE_FUTURES.remove) + + async def _prepare_open_file( + self, + request: "BaseRequest", + fobj: io.BufferedReader, + st: os.stat_result, + file_encoding: Optional[str], + ) -> Optional[AbstractStreamWriter]: + status = self._status + file_size: int = st.st_size + file_mtime: float = st.st_mtime + count: int = file_size + start: Optional[int] = None + + if (ifrange := request.if_range) is None or file_mtime <= ifrange.timestamp(): + # If-Range header check: + # condition = cached date >= last modification date + # return 206 if True else 200. + # if False: + # Range header would not be processed, return 200 + # if True but Range header missing + # return 200 + try: + rng = request.http_range + start = rng.start + end: Optional[int] = rng.stop + except ValueError: + # https://tools.ietf.org/html/rfc7233: + # A server generating a 416 (Range Not Satisfiable) response to + # a byte-range request SHOULD send a Content-Range header field + # with an unsatisfied-range value. + # The complete-length in a 416 response indicates the current + # length of the selected representation. + # + # Will do the same below. Many servers ignore this and do not + # send a Content-Range header with HTTP 416 + self._headers[hdrs.CONTENT_RANGE] = f"bytes */{file_size}" + self.set_status(HTTPRequestRangeNotSatisfiable.status_code) + return await super().prepare(request) + + # If a range request has been made, convert start, end slice + # notation into file pointer offset and count + if start is not None: + if start < 0 and end is None: # return tail of file + start += file_size + if start < 0: + # if Range:bytes=-1000 in request header but file size + # is only 200, there would be trouble without this + start = 0 + count = file_size - start + else: + # rfc7233:If the last-byte-pos value is + # absent, or if the value is greater than or equal to + # the current length of the representation data, + # the byte range is interpreted as the remainder + # of the representation (i.e., the server replaces the + # value of last-byte-pos with a value that is one less than + # the current length of the selected representation). + count = ( + min(end if end is not None else file_size, file_size) - start + ) + + if start >= file_size: + # HTTP 416 should be returned in this case. + # + # According to https://tools.ietf.org/html/rfc7233: + # If a valid byte-range-set includes at least one + # byte-range-spec with a first-byte-pos that is less than + # the current length of the representation, or at least one + # suffix-byte-range-spec with a non-zero suffix-length, + # then the byte-range-set is satisfiable. Otherwise, the + # byte-range-set is unsatisfiable. + self._headers[hdrs.CONTENT_RANGE] = f"bytes */{file_size}" + self.set_status(HTTPRequestRangeNotSatisfiable.status_code) + return await super().prepare(request) + + status = HTTPPartialContent.status_code + # Even though you are sending the whole file, you should still + # return a HTTP 206 for a Range request. + self.set_status(status) + + # If the Content-Type header is not already set, guess it based on the + # extension of the request path. The encoding returned by guess_type + # can be ignored since the map was cleared above. + if hdrs.CONTENT_TYPE not in self._headers: + if sys.version_info >= (3, 13): + guesser = CONTENT_TYPES.guess_file_type + else: + guesser = CONTENT_TYPES.guess_type + self.content_type = guesser(self._path)[0] or FALLBACK_CONTENT_TYPE + + if file_encoding: + self._headers[hdrs.CONTENT_ENCODING] = file_encoding + self._headers[hdrs.VARY] = hdrs.ACCEPT_ENCODING + # Disable compression if we are already sending + # a compressed file since we don't want to double + # compress. + self._compression = False + + self.etag = f"{st.st_mtime_ns:x}-{st.st_size:x}" + self.last_modified = file_mtime + self.content_length = count + + self._headers[hdrs.ACCEPT_RANGES] = "bytes" + + if status == HTTPPartialContent.status_code: + real_start = start + assert real_start is not None + self._headers[hdrs.CONTENT_RANGE] = "bytes {}-{}/{}".format( + real_start, real_start + count - 1, file_size + ) + + # If we are sending 0 bytes calling sendfile() will throw a ValueError + if count == 0 or must_be_empty_body(request.method, status): + return await super().prepare(request) + + # be aware that start could be None or int=0 here. + offset = start or 0 + + return await self._sendfile(request, fobj, offset, count) diff --git a/.venv/lib/python3.9/site-packages/aiohttp/web_log.py b/.venv/lib/python3.9/site-packages/aiohttp/web_log.py new file mode 100644 index 0000000..d5ea2be --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/web_log.py @@ -0,0 +1,216 @@ +import datetime +import functools +import logging +import os +import re +import time as time_mod +from collections import namedtuple +from typing import Any, Callable, Dict, Iterable, List, Tuple # noqa + +from .abc import AbstractAccessLogger +from .web_request import BaseRequest +from .web_response import StreamResponse + +KeyMethod = namedtuple("KeyMethod", "key method") + + +class AccessLogger(AbstractAccessLogger): + """Helper object to log access. + + Usage: + log = logging.getLogger("spam") + log_format = "%a %{User-Agent}i" + access_logger = AccessLogger(log, log_format) + access_logger.log(request, response, time) + + Format: + %% The percent sign + %a Remote IP-address (IP-address of proxy if using reverse proxy) + %t Time when the request was started to process + %P The process ID of the child that serviced the request + %r First line of request + %s Response status code + %b Size of response in bytes, including HTTP headers + %T Time taken to serve the request, in seconds + %Tf Time taken to serve the request, in seconds with floating fraction + in .06f format + %D Time taken to serve the request, in microseconds + %{FOO}i request.headers['FOO'] + %{FOO}o response.headers['FOO'] + %{FOO}e os.environ['FOO'] + + """ + + LOG_FORMAT_MAP = { + "a": "remote_address", + "t": "request_start_time", + "P": "process_id", + "r": "first_request_line", + "s": "response_status", + "b": "response_size", + "T": "request_time", + "Tf": "request_time_frac", + "D": "request_time_micro", + "i": "request_header", + "o": "response_header", + } + + LOG_FORMAT = '%a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"' + FORMAT_RE = re.compile(r"%(\{([A-Za-z0-9\-_]+)\}([ioe])|[atPrsbOD]|Tf?)") + CLEANUP_RE = re.compile(r"(%[^s])") + _FORMAT_CACHE: Dict[str, Tuple[str, List[KeyMethod]]] = {} + + def __init__(self, logger: logging.Logger, log_format: str = LOG_FORMAT) -> None: + """Initialise the logger. + + logger is a logger object to be used for logging. + log_format is a string with apache compatible log format description. + + """ + super().__init__(logger, log_format=log_format) + + _compiled_format = AccessLogger._FORMAT_CACHE.get(log_format) + if not _compiled_format: + _compiled_format = self.compile_format(log_format) + AccessLogger._FORMAT_CACHE[log_format] = _compiled_format + + self._log_format, self._methods = _compiled_format + + def compile_format(self, log_format: str) -> Tuple[str, List[KeyMethod]]: + """Translate log_format into form usable by modulo formatting + + All known atoms will be replaced with %s + Also methods for formatting of those atoms will be added to + _methods in appropriate order + + For example we have log_format = "%a %t" + This format will be translated to "%s %s" + Also contents of _methods will be + [self._format_a, self._format_t] + These method will be called and results will be passed + to translated string format. + + Each _format_* method receive 'args' which is list of arguments + given to self.log + + Exceptions are _format_e, _format_i and _format_o methods which + also receive key name (by functools.partial) + + """ + # list of (key, method) tuples, we don't use an OrderedDict as users + # can repeat the same key more than once + methods = list() + + for atom in self.FORMAT_RE.findall(log_format): + if atom[1] == "": + format_key1 = self.LOG_FORMAT_MAP[atom[0]] + m = getattr(AccessLogger, "_format_%s" % atom[0]) + key_method = KeyMethod(format_key1, m) + else: + format_key2 = (self.LOG_FORMAT_MAP[atom[2]], atom[1]) + m = getattr(AccessLogger, "_format_%s" % atom[2]) + key_method = KeyMethod(format_key2, functools.partial(m, atom[1])) + + methods.append(key_method) + + log_format = self.FORMAT_RE.sub(r"%s", log_format) + log_format = self.CLEANUP_RE.sub(r"%\1", log_format) + return log_format, methods + + @staticmethod + def _format_i( + key: str, request: BaseRequest, response: StreamResponse, time: float + ) -> str: + if request is None: + return "(no headers)" + + # suboptimal, make istr(key) once + return request.headers.get(key, "-") + + @staticmethod + def _format_o( + key: str, request: BaseRequest, response: StreamResponse, time: float + ) -> str: + # suboptimal, make istr(key) once + return response.headers.get(key, "-") + + @staticmethod + def _format_a(request: BaseRequest, response: StreamResponse, time: float) -> str: + if request is None: + return "-" + ip = request.remote + return ip if ip is not None else "-" + + @staticmethod + def _format_t(request: BaseRequest, response: StreamResponse, time: float) -> str: + tz = datetime.timezone(datetime.timedelta(seconds=-time_mod.timezone)) + now = datetime.datetime.now(tz) + start_time = now - datetime.timedelta(seconds=time) + return start_time.strftime("[%d/%b/%Y:%H:%M:%S %z]") + + @staticmethod + def _format_P(request: BaseRequest, response: StreamResponse, time: float) -> str: + return "<%s>" % os.getpid() + + @staticmethod + def _format_r(request: BaseRequest, response: StreamResponse, time: float) -> str: + if request is None: + return "-" + return "{} {} HTTP/{}.{}".format( + request.method, + request.path_qs, + request.version.major, + request.version.minor, + ) + + @staticmethod + def _format_s(request: BaseRequest, response: StreamResponse, time: float) -> int: + return response.status + + @staticmethod + def _format_b(request: BaseRequest, response: StreamResponse, time: float) -> int: + return response.body_length + + @staticmethod + def _format_T(request: BaseRequest, response: StreamResponse, time: float) -> str: + return str(round(time)) + + @staticmethod + def _format_Tf(request: BaseRequest, response: StreamResponse, time: float) -> str: + return "%06f" % time + + @staticmethod + def _format_D(request: BaseRequest, response: StreamResponse, time: float) -> str: + return str(round(time * 1000000)) + + def _format_line( + self, request: BaseRequest, response: StreamResponse, time: float + ) -> Iterable[Tuple[str, Callable[[BaseRequest, StreamResponse, float], str]]]: + return [(key, method(request, response, time)) for key, method in self._methods] + + @property + def enabled(self) -> bool: + """Check if logger is enabled.""" + # Avoid formatting the log line if it will not be emitted. + return self.logger.isEnabledFor(logging.INFO) + + def log(self, request: BaseRequest, response: StreamResponse, time: float) -> None: + try: + fmt_info = self._format_line(request, response, time) + + values = list() + extra = dict() + for key, value in fmt_info: + values.append(value) + + if key.__class__ is str: + extra[key] = value + else: + k1, k2 = key # type: ignore[misc] + dct = extra.get(k1, {}) # type: ignore[var-annotated,has-type] + dct[k2] = value # type: ignore[index,has-type] + extra[k1] = dct # type: ignore[has-type,assignment] + + self.logger.info(self._log_format % tuple(values), extra=extra) + except Exception: + self.logger.exception("Error in logging") diff --git a/.venv/lib/python3.9/site-packages/aiohttp/web_middlewares.py b/.venv/lib/python3.9/site-packages/aiohttp/web_middlewares.py new file mode 100644 index 0000000..2f1f5f5 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/web_middlewares.py @@ -0,0 +1,121 @@ +import re +from typing import TYPE_CHECKING, Tuple, Type, TypeVar + +from .typedefs import Handler, Middleware +from .web_exceptions import HTTPMove, HTTPPermanentRedirect +from .web_request import Request +from .web_response import StreamResponse +from .web_urldispatcher import SystemRoute + +__all__ = ( + "middleware", + "normalize_path_middleware", +) + +if TYPE_CHECKING: + from .web_app import Application + +_Func = TypeVar("_Func") + + +async def _check_request_resolves(request: Request, path: str) -> Tuple[bool, Request]: + alt_request = request.clone(rel_url=path) + + match_info = await request.app.router.resolve(alt_request) + alt_request._match_info = match_info + + if match_info.http_exception is None: + return True, alt_request + + return False, request + + +def middleware(f: _Func) -> _Func: + f.__middleware_version__ = 1 # type: ignore[attr-defined] + return f + + +def normalize_path_middleware( + *, + append_slash: bool = True, + remove_slash: bool = False, + merge_slashes: bool = True, + redirect_class: Type[HTTPMove] = HTTPPermanentRedirect, +) -> Middleware: + """Factory for producing a middleware that normalizes the path of a request. + + Normalizing means: + - Add or remove a trailing slash to the path. + - Double slashes are replaced by one. + + The middleware returns as soon as it finds a path that resolves + correctly. The order if both merge and append/remove are enabled is + 1) merge slashes + 2) append/remove slash + 3) both merge slashes and append/remove slash. + If the path resolves with at least one of those conditions, it will + redirect to the new path. + + Only one of `append_slash` and `remove_slash` can be enabled. If both + are `True` the factory will raise an assertion error + + If `append_slash` is `True` the middleware will append a slash when + needed. If a resource is defined with trailing slash and the request + comes without it, it will append it automatically. + + If `remove_slash` is `True`, `append_slash` must be `False`. When enabled + the middleware will remove trailing slashes and redirect if the resource + is defined + + If merge_slashes is True, merge multiple consecutive slashes in the + path into one. + """ + correct_configuration = not (append_slash and remove_slash) + assert correct_configuration, "Cannot both remove and append slash" + + @middleware + async def impl(request: Request, handler: Handler) -> StreamResponse: + if isinstance(request.match_info.route, SystemRoute): + paths_to_check = [] + if "?" in request.raw_path: + path, query = request.raw_path.split("?", 1) + query = "?" + query + else: + query = "" + path = request.raw_path + + if merge_slashes: + paths_to_check.append(re.sub("//+", "/", path)) + if append_slash and not request.path.endswith("/"): + paths_to_check.append(path + "/") + if remove_slash and request.path.endswith("/"): + paths_to_check.append(path[:-1]) + if merge_slashes and append_slash: + paths_to_check.append(re.sub("//+", "/", path + "/")) + if merge_slashes and remove_slash: + merged_slashes = re.sub("//+", "/", path) + paths_to_check.append(merged_slashes[:-1]) + + for path in paths_to_check: + path = re.sub("^//+", "/", path) # SECURITY: GHSA-v6wp-4m6f-gcjg + resolves, request = await _check_request_resolves(request, path) + if resolves: + raise redirect_class(request.raw_path + query) + + return await handler(request) + + return impl + + +def _fix_request_current_app(app: "Application") -> Middleware: + @middleware + async def impl(request: Request, handler: Handler) -> StreamResponse: + match_info = request.match_info + prev = match_info.current_app + match_info.current_app = app + try: + return await handler(request) + finally: + match_info.current_app = prev + + return impl diff --git a/.venv/lib/python3.9/site-packages/aiohttp/web_protocol.py b/.venv/lib/python3.9/site-packages/aiohttp/web_protocol.py new file mode 100644 index 0000000..1bd344a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/aiohttp/web_protocol.py @@ -0,0 +1,792 @@ +import asyncio +import asyncio.streams +import sys +import traceback +import warnings +from collections import deque +from contextlib import suppress +from html import escape as html_escape +from http import HTTPStatus +from logging import Logger +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Deque, + Optional, + Sequence, + Tuple, + Type, + Union, + cast, +) + +import attr +import yarl +from propcache import under_cached_property + +from .abc import AbstractAccessLogger, AbstractStreamWriter +from .base_protocol import BaseProtocol +from .helpers import ceil_timeout +from .http import ( + HttpProcessingError, + HttpRequestParser, + HttpVersion10, + RawRequestMessage, + StreamWriter, +) +from .http_exceptions import BadHttpMethod +from .log import access_logger, server_logger +from .streams import EMPTY_PAYLOAD, StreamReader +from .tcp_helpers import tcp_keepalive +from .web_exceptions import HTTPException, HTTPInternalServerError +from .web_log import AccessLogger +from .web_request import BaseRequest +from .web_response import Response, StreamResponse + +__all__ = ("RequestHandler", "RequestPayloadError", "PayloadAccessError") + +if TYPE_CHECKING: + import ssl + + from .web_server import Server + + +_RequestFactory = Callable[ + [ + RawRequestMessage, + StreamReader, + "RequestHandler", + AbstractStreamWriter, + "asyncio.Task[None]", + ], + BaseRequest, +] + +_RequestHandler = Callable[[BaseRequest], Awaitable[StreamResponse]] + +ERROR = RawRequestMessage( + "UNKNOWN", + "/", + HttpVersion10, + {}, # type: ignore[arg-type] + {}, # type: ignore[arg-type] + True, + None, + False, + False, + yarl.URL("/"), +) + + +class RequestPayloadError(Exception): + """Payload parsing error.""" + + +class PayloadAccessError(Exception): + """Payload was accessed after response was sent.""" + + +_PAYLOAD_ACCESS_ERROR = PayloadAccessError() + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class _ErrInfo: + status: int + exc: BaseException + message: str + + +_MsgType = Tuple[Union[RawRequestMessage, _ErrInfo], StreamReader] + + +class RequestHandler(BaseProtocol): + """HTTP protocol implementation. + + RequestHandler handles incoming HTTP request. It reads request line, + request headers and request payload and calls handle_request() method. + By default it always returns with 404 response. + + RequestHandler handles errors in incoming request, like bad + status line, bad headers or incomplete payload. If any error occurs, + connection gets closed. + + keepalive_timeout -- number of seconds before closing + keep-alive connection + + tcp_keepalive -- TCP keep-alive is on, default is on + + debug -- enable debug mode + + logger -- custom logger object + + access_log_class -- custom class for access_logger + + access_log -- custom logging object + + access_log_format -- access log format string + + loop -- Optional event loop + + max_line_size -- Optional maximum header line size + + max_field_size -- Optional maximum header field size + + max_headers -- Optional maximum header size + + timeout_ceil_threshold -- Optional value to specify + threshold to ceil() timeout + values + + """ + + __slots__ = ( + "_request_count", + "_keepalive", + "_manager", + "_request_handler", + "_request_factory", + "_tcp_keepalive", + "_next_keepalive_close_time", + "_keepalive_handle", + "_keepalive_timeout", + "_lingering_time", + "_messages", + "_message_tail", + "_handler_waiter", + "_waiter", + "_task_handler", + "_upgrade", + "_payload_parser", + "_request_parser", + "_reading_paused", + "logger", + "debug", + "access_log", + "access_logger", + "_close", + "_force_close", + "_current_request", + "_timeout_ceil_threshold", + "_request_in_progress", + "_logging_enabled", + "_cache", + ) + + def __init__( + self, + manager: "Server", + *, + loop: asyncio.AbstractEventLoop, + # Default should be high enough that it's likely longer than a reverse proxy. + keepalive_timeout: float = 3630, + tcp_keepalive: bool = True, + logger: Logger = server_logger, + access_log_class: Type[AbstractAccessLogger] = AccessLogger, + access_log: Logger = access_logger, + access_log_format: str = AccessLogger.LOG_FORMAT, + debug: bool = False, + max_line_size: int = 8190, + max_headers: int = 32768, + max_field_size: int = 8190, + lingering_time: float = 10.0, + read_bufsize: int = 2**16, + auto_decompress: bool = True, + timeout_ceil_threshold: float = 5, + ): + super().__init__(loop) + + # _request_count is the number of requests processed with the same connection. + self._request_count = 0 + self._keepalive = False + self._current_request: Optional[BaseRequest] = None + self._manager: Optional[Server] = manager + self._request_handler: Optional[_RequestHandler] = manager.request_handler + self._request_factory: Optional[_RequestFactory] = manager.request_factory + + self._tcp_keepalive = tcp_keepalive + # placeholder to be replaced on keepalive timeout setup + self._next_keepalive_close_time = 0.0 + self._keepalive_handle: Optional[asyncio.Handle] = None + self._keepalive_timeout = keepalive_timeout + self._lingering_time = float(lingering_time) + + self._messages: Deque[_MsgType] = deque() + self._message_tail = b"" + + self._waiter: Optional[asyncio.Future[None]] = None + self._handler_waiter: Optional[asyncio.Future[None]] = None + self._task_handler: Optional[asyncio.Task[None]] = None + + self._upgrade = False + self._payload_parser: Any = None + self._request_parser: Optional[HttpRequestParser] = HttpRequestParser( + self, + loop, + read_bufsize, + max_line_size=max_line_size, + max_field_size=max_field_size, + max_headers=max_headers, + payload_exception=RequestPayloadError, + auto_decompress=auto_decompress, + ) + + self._timeout_ceil_threshold: float = 5 + try: + self._timeout_ceil_threshold = float(timeout_ceil_threshold) + except (TypeError, ValueError): + pass + + self.logger = logger + self.debug = debug + self.access_log = access_log + if access_log: + self.access_logger: Optional[AbstractAccessLogger] = access_log_class( + access_log, access_log_format + ) + self._logging_enabled = self.access_logger.enabled + else: + self.access_logger = None + self._logging_enabled = False + + self._close = False + self._force_close = False + self._request_in_progress = False + self._cache: dict[str, Any] = {} + + def __repr__(self) -> str: + return "<{} {}>".format( + self.__class__.__name__, + "connected" if self.transport is not None else "disconnected", + ) + + @under_cached_property + def ssl_context(self) -> Optional["ssl.SSLContext"]: + """Return SSLContext if available.""" + return ( + None + if self.transport is None + else self.transport.get_extra_info("sslcontext") + ) + + @under_cached_property + def peername( + self, + ) -> Optional[Union[str, Tuple[str, int, int, int], Tuple[str, int]]]: + """Return peername if available.""" + return ( + None + if self.transport is None + else self.transport.get_extra_info("peername") + ) + + @property + def keepalive_timeout(self) -> float: + return self._keepalive_timeout + + async def shutdown(self, timeout: Optional[float] = 15.0) -> None: + """Do worker process exit preparations. + + We need to clean up everything and stop accepting requests. + It is especially important for keep-alive connections. + """ + self._force_close = True + + if self._keepalive_handle is not None: + self._keepalive_handle.cancel() + + # Wait for graceful handler completion + if self._request_in_progress: + # The future is only created when we are shutting + # down while the handler is still processing a request + # to avoid creating a future for every request. + self._handler_waiter = self._loop.create_future() + try: + async with ceil_timeout(timeout): + await self._handler_waiter + except (asyncio.CancelledError, asyncio.TimeoutError): + self._handler_waiter = None + if ( + sys.version_info >= (3, 11) + and (task := asyncio.current_task()) + and task.cancelling() + ): + raise + # Then cancel handler and wait + try: + async with ceil_timeout(timeout): + if self._current_request is not None: + self._current_request._cancel(asyncio.CancelledError()) + + if self._task_handler is not None and not self._task_handler.done(): + await asyncio.shield(self._task_handler) + except (asyncio.CancelledError, asyncio.TimeoutError): + if ( + sys.version_info >= (3, 11) + and (task := asyncio.current_task()) + and task.cancelling() + ): + raise + + # force-close non-idle handler + if self._task_handler is not None: + self._task_handler.cancel() + + self.force_close() + + def connection_made(self, transport: asyncio.BaseTransport) -> None: + super().connection_made(transport) + + real_transport = cast(asyncio.Transport, transport) + if self._tcp_keepalive: + tcp_keepalive(real_transport) + + assert self._manager is not None + self._manager.connection_made(self, real_transport) + + loop = self._loop + if sys.version_info >= (3, 12): + task = asyncio.Task(self.start(), loop=loop, eager_start=True) + else: + task = loop.create_task(self.start()) + self._task_handler = task + + def connection_lost(self, exc: Optional[BaseException]) -> None: + if self._manager is None: + return + self._manager.connection_lost(self, exc) + + # Grab value before setting _manager to None. + handler_cancellation = self._manager.handler_cancellation + + self.force_close() + super().connection_lost(exc) + self._manager = None + self._request_factory = None + self._request_handler = None + self._request_parser = None + + if self._keepalive_handle is not None: + self._keepalive_handle.cancel() + + if self._current_request is not None: + if exc is None: + exc = ConnectionResetError("Connection lost") + self._current_request._cancel(exc) + + if handler_cancellation and self._task_handler is not None: + self._task_handler.cancel() + + self._task_handler = None + + if self._payload_parser is not None: + self._payload_parser.feed_eof() + self._payload_parser = None + + def set_parser(self, parser: Any) -> None: + # Actual type is WebReader + assert self._payload_parser is None + + self._payload_parser = parser + + if self._message_tail: + self._payload_parser.feed_data(self._message_tail) + self._message_tail = b"" + + def eof_received(self) -> None: + pass + + def data_received(self, data: bytes) -> None: + if self._force_close or self._close: + return + # parse http messages + messages: Sequence[_MsgType] + if self._payload_parser is None and not self._upgrade: + assert self._request_parser is not None + try: + messages, upgraded, tail = self._request_parser.feed_data(data) + except HttpProcessingError as exc: + messages = [ + (_ErrInfo(status=400, exc=exc, message=exc.message), EMPTY_PAYLOAD) + ] + upgraded = False + tail = b"" + + for msg, payload in messages or (): + self._request_count += 1 + self._messages.append((msg, payload)) + + waiter = self._waiter + if messages and waiter is not None and not waiter.done(): + # don't set result twice + waiter.set_result(None) + + self._upgrade = upgraded + if upgraded and tail: + self._message_tail = tail + + # no parser, just store + elif self._payload_parser is None and self._upgrade and data: + self._message_tail += data + + # feed payload + elif data: + eof, tail = self._payload_parser.feed_data(data) + if eof: + self.close() + + def keep_alive(self, val: bool) -> None: + """Set keep-alive connection mode. + + :param bool val: new state. + """ + self._keepalive = val + if self._keepalive_handle: + self._keepalive_handle.cancel() + self._keepalive_handle = None + + def close(self) -> None: + """Close connection. + + Stop accepting new pipelining messages and close + connection when handlers done processing messages. + """ + self._close = True + if self._waiter: + self._waiter.cancel() + + def force_close(self) -> None: + """Forcefully close connection.""" + self._force_close = True + if self._waiter: + self._waiter.cancel() + if self.transport is not None: + self.transport.close() + self.transport = None + + def log_access( + self, request: BaseRequest, response: StreamResponse, time: Optional[float] + ) -> None: + if self._logging_enabled and self.access_logger is not None: + if TYPE_CHECKING: + assert time is not None + self.access_logger.log(request, response, self._loop.time() - time) + + def log_debug(self, *args: Any, **kw: Any) -> None: + if self.debug: + self.logger.debug(*args, **kw) + + def log_exception(self, *args: Any, **kw: Any) -> None: + self.logger.exception(*args, **kw) + + def _process_keepalive(self) -> None: + self._keepalive_handle = None + if self._force_close or not self._keepalive: + return + + loop = self._loop + now = loop.time() + close_time = self._next_keepalive_close_time + if now < close_time: + # Keep alive close check fired too early, reschedule + self._keepalive_handle = loop.call_at(close_time, self._process_keepalive) + return + + # handler in idle state + if self._waiter and not self._waiter.done(): + self.force_close() + + async def _handle_request( + self, + request: BaseRequest, + start_time: Optional[float], + request_handler: Callable[[BaseRequest], Awaitable[StreamResponse]], + ) -> Tuple[StreamResponse, bool]: + self._request_in_progress = True + try: + try: + self._current_request = request + resp = await request_handler(request) + finally: + self._current_request = None + except HTTPException as exc: + resp = exc + resp, reset = await self.finish_response(request, resp, start_time) + except asyncio.CancelledError: + raise + except asyncio.TimeoutError as exc: + self.log_debug("Request handler timed out.", exc_info=exc) + resp = self.handle_error(request, 504) + resp, reset = await self.finish_response(request, resp, start_time) + except Exception as exc: + resp = self.handle_error(request, 500, exc) + resp, reset = await self.finish_response(request, resp, start_time) + else: + # Deprecation warning (See #2415) + if getattr(resp, "__http_exception__", False): + warnings.warn( + "returning HTTPException object is deprecated " + "(#2415) and will be removed, " + "please raise the exception instead", + DeprecationWarning, + ) + + resp, reset = await self.finish_response(request, resp, start_time) + finally: + self._request_in_progress = False + if self._handler_waiter is not None: + self._handler_waiter.set_result(None) + + return resp, reset + + async def start(self) -> None: + """Process incoming request. + + It reads request line, request headers and request payload, then + calls handle_request() method. Subclass has to override + handle_request(). start() handles various exceptions in request + or response handling. Connection is being closed always unless + keep_alive(True) specified. + """ + loop = self._loop + manager = self._manager + assert manager is not None + keepalive_timeout = self._keepalive_timeout + resp = None + assert self._request_factory is not None + assert self._request_handler is not None + + while not self._force_close: + if not self._messages: + try: + # wait for next request + self._waiter = loop.create_future() + await self._waiter + finally: + self._waiter = None + + message, payload = self._messages.popleft() + + # time is only fetched if logging is enabled as otherwise + # its thrown away and never used. + start = loop.time() if self._logging_enabled else None + + manager.requests_count += 1 + writer = StreamWriter(self, loop) + if isinstance(message, _ErrInfo): + # make request_factory work + request_handler = self._make_error_handler(message) + message = ERROR + else: + request_handler = self._request_handler + + # Important don't hold a reference to the current task + # as on traceback it will prevent the task from being + # collected and will cause a memory leak. + request = self._request_factory( + message, + payload, + self, + writer, + self._task_handler or asyncio.current_task(loop), # type: ignore[arg-type] + ) + try: + # a new task is used for copy context vars (#3406) + coro = self._handle_request(request, start, request_handler) + if sys.version_info >= (3, 12): + task = asyncio.Task(coro, loop=loop, eager_start=True) + else: + task = loop.create_task(coro) + try: + resp, reset = await task + except ConnectionError: + self.log_debug("Ignored premature client disconnection") + break + + # Drop the processed task from asyncio.Task.all_tasks() early + del task + if reset: + self.log_debug("Ignored premature client disconnection 2") + break + + # notify server about keep-alive + self._keepalive = bool(resp.keep_alive) + + # check payload + if not payload.is_eof(): + lingering_time = self._lingering_time + if not self._force_close and lingering_time: + self.log_debug( + "Start lingering close timer for %s sec.", lingering_time + ) + + now = loop.time() + end_t = now + lingering_time + + try: + while not payload.is_eof() and now < end_t: + async with ceil_timeout(end_t - now): + # read and ignore + await payload.readany() + now = loop.time() + except (asyncio.CancelledError, asyncio.TimeoutError): + if ( + sys.version_info >= (3, 11) + and (t := asyncio.current_task()) + and t.cancelling() + ): + raise + + # if payload still uncompleted + if not payload.is_eof() and not self._force_close: + self.log_debug("Uncompleted request.") + self.close() + + payload.set_exception(_PAYLOAD_ACCESS_ERROR) + + except asyncio.CancelledError: + self.log_debug("Ignored premature client disconnection") + self.force_close() + raise + except Exception as exc: + self.log_exception("Unhandled exception", exc_info=exc) + self.force_close() + except BaseException: + self.force_close() + raise + finally: + request._task = None # type: ignore[assignment] # Break reference cycle in case of exception + if self.transport is None and resp is not None: + self.log_debug("Ignored premature client disconnection.") + + if self._keepalive and not self._close and not self._force_close: + # start keep-alive timer + close_time = loop.time() + keepalive_timeout + self._next_keepalive_close_time = close_time + if self._keepalive_handle is None: + self._keepalive_handle = loop.call_at( + close_time, self._process_keepalive + ) + else: + break + + # remove handler, close transport if no handlers left + if not self._force_close: + self._task_handler = None + if self.transport is not None: + self.transport.close() + + async def finish_response( + self, request: BaseRequest, resp: StreamResponse, start_time: Optional[float] + ) -> Tuple[StreamResponse, bool]: + """Prepare the response and write_eof, then log access. + + This has to + be called within the context of any exception so the access logger + can get exception information. Returns True if the client disconnects + prematurely. + """ + request._finish() + if self._request_parser is not None: + self._request_parser.set_upgraded(False) + self._upgrade = False + if self._message_tail: + self._request_parser.feed_data(self._message_tail) + self._message_tail = b"" + try: + prepare_meth = resp.prepare + except AttributeError: + if resp is None: + self.log_exception("Missing return statement on request handler") + else: + self.log_exception( + "Web-handler should return a response instance, " + "got {!r}".format(resp) + ) + exc = HTTPInternalServerError() + resp = Response( + status=exc.status, reason=exc.reason, text=exc.text, headers=exc.headers + ) + prepare_meth = resp.prepare + try: + await prepare_meth(request) + await resp.write_eof() + except ConnectionError: + self.log_access(request, resp, start_time) + return resp, True + + self.log_access(request, resp, start_time) + return resp, False + + def handle_error( + self, + request: BaseRequest, + status: int = 500, + exc: Optional[BaseException] = None, + message: Optional[str] = None, + ) -> StreamResponse: + """Handle errors. + + Returns HTTP response with specific status code. Logs additional + information. It always closes current connection. + """ + if self._request_count == 1 and isinstance(exc, BadHttpMethod): + # BadHttpMethod is common when a client sends non-HTTP + # or encrypted traffic to an HTTP port. This is expected + # to happen when connected to the public internet so we log + # it at the debug level as to not fill logs with noise. + self.logger.debug( + "Error handling request from %s", request.remote, exc_info=exc + ) + else: + self.log_exception( + "Error handling request from %s", request.remote, exc_info=exc + ) + + # some data already got sent, connection is broken + if request.writer.output_size > 0: + raise ConnectionError( + "Response is sent already, cannot send another response " + "with the error message" + ) + + ct = "text/plain" + if status == HTTPStatus.INTERNAL_SERVER_ERROR: + title = "{0.value} {0.phrase}".format(HTTPStatus.INTERNAL_SERVER_ERROR) + msg = HTTPStatus.INTERNAL_SERVER_ERROR.description + tb = None + if self.debug: + with suppress(Exception): + tb = traceback.format_exc() + + if "text/html" in request.headers.get("Accept", ""): + if tb: + tb = html_escape(tb) + msg = f"

Traceback:

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

{title}

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

{index_of}

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

+ + attrs + +

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

+ + + + + + + + + +

+ + + +

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

+ + + +## Example + +*attrs* gives you a class decorator and a way to declaratively define the attributes on that class: + + + +```pycon +>>> from attrs import asdict, define, make_class, Factory + +>>> @define +... class SomeClass: +... a_number: int = 42 +... list_of_numbers: list[int] = Factory(list) +... +... def hard_math(self, another_number): +... return self.a_number + sum(self.list_of_numbers) * another_number + + +>>> sc = SomeClass(1, [1, 2, 3]) +>>> sc +SomeClass(a_number=1, list_of_numbers=[1, 2, 3]) + +>>> sc.hard_math(3) +19 +>>> sc == SomeClass(1, [1, 2, 3]) +True +>>> sc != SomeClass(2, [3, 2, 1]) +True + +>>> asdict(sc) +{'a_number': 1, 'list_of_numbers': [1, 2, 3]} + +>>> SomeClass() +SomeClass(a_number=42, list_of_numbers=[]) + +>>> C = make_class("C", ["a", "b"]) +>>> C("foo", "bar") +C(a='foo', b='bar') +``` + +After *declaring* your attributes, *attrs* gives you: + +- a concise and explicit overview of the class's attributes, +- a nice human-readable `__repr__`, +- equality-checking methods, +- an initializer, +- and much more, + +*without* writing dull boilerplate code again and again and *without* runtime performance penalties. + +--- + +This example uses *attrs*'s modern APIs that have been introduced in version 20.1.0, and the *attrs* package import name that has been added in version 21.3.0. +The classic APIs (`@attr.s`, `attr.ib`, plus their serious-business aliases) and the `attr` package import name will remain **indefinitely**. + +Check out [*On The Core API Names*](https://www.attrs.org/en/latest/names.html) for an in-depth explanation! + + +### Hate Type Annotations!? + +No problem! +Types are entirely **optional** with *attrs*. +Simply assign `attrs.field()` to the attributes instead of annotating them with types: + +```python +from attrs import define, field + +@define +class SomeClass: + a_number = field(default=42) + list_of_numbers = field(factory=list) +``` + + +## Data Classes + +On the tin, *attrs* might remind you of `dataclasses` (and indeed, `dataclasses` [are a descendant](https://hynek.me/articles/import-attrs/) of *attrs*). +In practice it does a lot more and is more flexible. +For instance, it allows you to define [special handling of NumPy arrays for equality checks](https://www.attrs.org/en/stable/comparison.html#customization), allows more ways to [plug into the initialization process](https://www.attrs.org/en/stable/init.html#hooking-yourself-into-initialization), has a replacement for `__init_subclass__`, and allows for stepping through the generated methods using a debugger. + +For more details, please refer to our [comparison page](https://www.attrs.org/en/stable/why.html#data-classes), but generally speaking, we are more likely to commit crimes against nature to make things work that one would expect to work, but that are quite complicated in practice. + + +## Project Information + +- [**Changelog**](https://www.attrs.org/en/stable/changelog.html) +- [**Documentation**](https://www.attrs.org/) +- [**PyPI**](https://pypi.org/project/attrs/) +- [**Source Code**](https://github.com/python-attrs/attrs) +- [**Contributing**](https://github.com/python-attrs/attrs/blob/main/.github/CONTRIBUTING.md) +- [**Third-party Extensions**](https://github.com/python-attrs/attrs/wiki/Extensions-to-attrs) +- **Get Help**: use the `python-attrs` tag on [Stack Overflow](https://stackoverflow.com/questions/tagged/python-attrs) + + +### *attrs* for Enterprise + +Available as part of the [Tidelift Subscription](https://tidelift.com/?utm_source=lifter&utm_medium=referral&utm_campaign=hynek). + +The maintainers of *attrs* and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. +Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. + +## Release Information + +### Backwards-incompatible Changes + +- Class-level `kw_only=True` behavior is now consistent with `dataclasses`. + + Previously, a class that sets `kw_only=True` makes all attributes keyword-only, including those from base classes. + If an attribute sets `kw_only=False`, that setting is ignored, and it is still made keyword-only. + + Now, only the attributes defined in that class that doesn't explicitly set `kw_only=False` are made keyword-only. + + This shouldn't be a problem for most users, unless you have a pattern like this: + + ```python + @attrs.define(kw_only=True) + class Base: + a: int + b: int = attrs.field(default=1, kw_only=False) + + @attrs.define + class Subclass(Base): + c: int + ``` + + Here, we have a `kw_only=True` *attrs* class (`Base`) with an attribute that sets `kw_only=False` and has a default (`Base.b`), and then create a subclass (`Subclass`) with required arguments (`Subclass.c`). + Previously this would work, since it would make `Base.b` keyword-only, but now this fails since `Base.b` is positional, and we have a required positional argument (`Subclass.c`) following another argument with defaults. + [#1457](https://github.com/python-attrs/attrs/issues/1457) + + +### Changes + +- Values passed to the `__init__()` method of `attrs` classes are now correctly passed to `__attrs_pre_init__()` instead of their default values (in cases where *kw_only* was not specified). + [#1427](https://github.com/python-attrs/attrs/issues/1427) +- Added support for Python 3.14 and [PEP 749](https://peps.python.org/pep-0749/). + [#1446](https://github.com/python-attrs/attrs/issues/1446), + [#1451](https://github.com/python-attrs/attrs/issues/1451) +- `attrs.validators.deep_mapping()` now allows to leave out either *key_validator* xor *value_validator*. + [#1448](https://github.com/python-attrs/attrs/issues/1448) +- `attrs.validators.deep_iterator()` and `attrs.validators.deep_mapping()` now accept lists and tuples for all validators and wrap them into a `attrs.validators.and_()`. + [#1449](https://github.com/python-attrs/attrs/issues/1449) +- Added a new **experimental** way to inspect classes: + + `attrs.inspect(cls)` returns the _effective_ class-wide parameters that were used by *attrs* to construct the class. + + The returned class is the same data structure that *attrs* uses internally to decide how to construct the final class. + [#1454](https://github.com/python-attrs/attrs/issues/1454) +- Fixed annotations for `attrs.field(converter=...)`. + Previously, a `tuple` of converters was only accepted if it had exactly one element. + [#1461](https://github.com/python-attrs/attrs/issues/1461) +- The performance of `attrs.asdict()` has been improved by 45–260%. + [#1463](https://github.com/python-attrs/attrs/issues/1463) +- The performance of `attrs.astuple()` has been improved by 49–270%. + [#1469](https://github.com/python-attrs/attrs/issues/1469) +- The type annotation for `attrs.validators.or_()` now allows for different types of validators. + + This was only an issue on Pyright. + [#1474](https://github.com/python-attrs/attrs/issues/1474) + + + +--- + +[Full changelog →](https://www.attrs.org/en/stable/changelog.html) diff --git a/.venv/lib/python3.9/site-packages/attrs-25.4.0.dist-info/RECORD b/.venv/lib/python3.9/site-packages/attrs-25.4.0.dist-info/RECORD new file mode 100644 index 0000000..ee0a43c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/attrs-25.4.0.dist-info/RECORD @@ -0,0 +1,55 @@ +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/attr/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/attr/_cmp.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/attr/_compat.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/attr/_config.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/attr/_funcs.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/attr/_make.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/attr/_next_gen.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/attr/_version_info.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/attr/converters.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/attr/exceptions.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/attr/filters.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/attr/setters.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/attr/validators.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/attrs/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/attrs/converters.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/attrs/exceptions.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/attrs/filters.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/attrs/setters.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/attrs/validators.cpython-39.pyc,, +attr/__init__.py,sha256=fOYIvt1eGSqQre4uCS3sJWKZ0mwAuC8UD6qba5OS9_U,2057 +attr/__init__.pyi,sha256=IZkzIjvtbRqDWGkDBIF9dd12FgDa379JYq3GHnVOvFQ,11309 +attr/_cmp.py,sha256=3Nn1TjxllUYiX_nJoVnEkXoDk0hM1DYKj5DE7GZe4i0,4117 +attr/_cmp.pyi,sha256=U-_RU_UZOyPUEQzXE6RMYQQcjkZRY25wTH99sN0s7MM,368 +attr/_compat.py,sha256=x0g7iEUOnBVJC72zyFCgb1eKqyxS-7f2LGnNyZ_r95s,2829 +attr/_config.py,sha256=dGq3xR6fgZEF6UBt_L0T-eUHIB4i43kRmH0P28sJVw8,843 +attr/_funcs.py,sha256=Ix5IETTfz5F01F-12MF_CSFomIn2h8b67EVVz2gCtBE,16479 +attr/_make.py,sha256=NRJDGS8syg2h3YNflVNoK2FwR3CpdSZxx8M6lacwljA,104141 +attr/_next_gen.py,sha256=BQtCUlzwg2gWHTYXBQvrEYBnzBUrDvO57u0Py6UCPhc,26274 +attr/_typing_compat.pyi,sha256=XDP54TUn-ZKhD62TOQebmzrwFyomhUCoGRpclb6alRA,469 +attr/_version_info.py,sha256=w4R-FYC3NK_kMkGUWJlYP4cVAlH9HRaC-um3fcjYkHM,2222 +attr/_version_info.pyi,sha256=x_M3L3WuB7r_ULXAWjx959udKQ4HLB8l-hsc1FDGNvk,209 +attr/converters.py,sha256=GlDeOzPeTFgeBBLbj9G57Ez5lAk68uhSALRYJ_exe84,3861 +attr/converters.pyi,sha256=orU2bff-VjQa2kMDyvnMQV73oJT2WRyQuw4ZR1ym1bE,643 +attr/exceptions.py,sha256=HRFq4iybmv7-DcZwyjl6M1euM2YeJVK_hFxuaBGAngI,1977 +attr/exceptions.pyi,sha256=zZq8bCUnKAy9mDtBEw42ZhPhAUIHoTKedDQInJD883M,539 +attr/filters.py,sha256=ZBiKWLp3R0LfCZsq7X11pn9WX8NslS2wXM4jsnLOGc8,1795 +attr/filters.pyi,sha256=3J5BG-dTxltBk1_-RuNRUHrv2qu1v8v4aDNAQ7_mifA,208 +attr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +attr/setters.py,sha256=5-dcT63GQK35ONEzSgfXCkbB7pPkaR-qv15mm4PVSzQ,1617 +attr/setters.pyi,sha256=NnVkaFU1BB4JB8E4JuXyrzTUgvtMpj8p3wBdJY7uix4,584 +attr/validators.py,sha256=1BnYGTuYvSucGEI4ju-RPNJteVzG0ZlfWpJiWoSFHQ8,21458 +attr/validators.pyi,sha256=ftmW3m4KJ3pQcIXAj-BejT7BY4ZfqrC1G-5W7XvoPds,4082 +attrs-25.4.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +attrs-25.4.0.dist-info/METADATA,sha256=2Rerxj7agcMRxiwdkt6lC2guqHAmkGKCH13nWWK7ZoQ,10473 +attrs-25.4.0.dist-info/RECORD,, +attrs-25.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87 +attrs-25.4.0.dist-info/licenses/LICENSE,sha256=iCEVyV38KvHutnFPjsbVy8q_Znyv-HKfQkINpj9xTp8,1109 +attrs/__init__.py,sha256=RxaAZNwYiEh-fcvHLZNpQ_DWKni73M_jxEPEftiq1Zc,1183 +attrs/__init__.pyi,sha256=2gV79g9UxJppGSM48hAZJ6h_MHb70dZoJL31ZNJeZYI,9416 +attrs/converters.py,sha256=8kQljrVwfSTRu8INwEk8SI0eGrzmWftsT7rM0EqyohM,76 +attrs/exceptions.py,sha256=ACCCmg19-vDFaDPY9vFl199SPXCQMN_bENs4DALjzms,76 +attrs/filters.py,sha256=VOUMZug9uEU6dUuA0dF1jInUK0PL3fLgP0VBS5d-CDE,73 +attrs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +attrs/setters.py,sha256=eL1YidYQV3T2h9_SYIZSZR1FAcHGb1TuCTy0E0Lv2SU,73 +attrs/validators.py,sha256=xcy6wD5TtTkdCG1f4XWbocPSO0faBjk5IfVJfP6SUj0,76 diff --git a/.venv/lib/python3.9/site-packages/attrs-25.4.0.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/attrs-25.4.0.dist-info/WHEEL new file mode 100644 index 0000000..12228d4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/attrs-25.4.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.27.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.9/site-packages/attrs-25.4.0.dist-info/licenses/LICENSE b/.venv/lib/python3.9/site-packages/attrs-25.4.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..2bd6453 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/attrs-25.4.0.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Hynek Schlawack and the attrs contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.venv/lib/python3.9/site-packages/attrs/__init__.py b/.venv/lib/python3.9/site-packages/attrs/__init__.py new file mode 100644 index 0000000..dc1ce4b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/attrs/__init__.py @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: MIT + +from attr import ( + NOTHING, + Attribute, + AttrsInstance, + Converter, + Factory, + NothingType, + _make_getattr, + assoc, + cmp_using, + define, + evolve, + field, + fields, + fields_dict, + frozen, + has, + make_class, + mutable, + resolve_types, + validate, +) +from attr._make import ClassProps +from attr._next_gen import asdict, astuple, inspect + +from . import converters, exceptions, filters, setters, validators + + +__all__ = [ + "NOTHING", + "Attribute", + "AttrsInstance", + "ClassProps", + "Converter", + "Factory", + "NothingType", + "__author__", + "__copyright__", + "__description__", + "__doc__", + "__email__", + "__license__", + "__title__", + "__url__", + "__version__", + "__version_info__", + "asdict", + "assoc", + "astuple", + "cmp_using", + "converters", + "define", + "evolve", + "exceptions", + "field", + "fields", + "fields_dict", + "filters", + "frozen", + "has", + "inspect", + "make_class", + "mutable", + "resolve_types", + "setters", + "validate", + "validators", +] + +__getattr__ = _make_getattr(__name__) diff --git a/.venv/lib/python3.9/site-packages/attrs/__init__.pyi b/.venv/lib/python3.9/site-packages/attrs/__init__.pyi new file mode 100644 index 0000000..6364bac --- /dev/null +++ b/.venv/lib/python3.9/site-packages/attrs/__init__.pyi @@ -0,0 +1,314 @@ +import sys + +from typing import ( + Any, + Callable, + Mapping, + Sequence, + overload, + TypeVar, +) + +# Because we need to type our own stuff, we have to make everything from +# attr explicitly public too. +from attr import __author__ as __author__ +from attr import __copyright__ as __copyright__ +from attr import __description__ as __description__ +from attr import __email__ as __email__ +from attr import __license__ as __license__ +from attr import __title__ as __title__ +from attr import __url__ as __url__ +from attr import __version__ as __version__ +from attr import __version_info__ as __version_info__ +from attr import assoc as assoc +from attr import Attribute as Attribute +from attr import AttrsInstance as AttrsInstance +from attr import cmp_using as cmp_using +from attr import converters as converters +from attr import Converter as Converter +from attr import evolve as evolve +from attr import exceptions as exceptions +from attr import Factory as Factory +from attr import fields as fields +from attr import fields_dict as fields_dict +from attr import filters as filters +from attr import has as has +from attr import make_class as make_class +from attr import NOTHING as NOTHING +from attr import resolve_types as resolve_types +from attr import setters as setters +from attr import validate as validate +from attr import validators as validators +from attr import attrib, asdict as asdict, astuple as astuple +from attr import NothingType as NothingType + +if sys.version_info >= (3, 11): + from typing import dataclass_transform +else: + from typing_extensions import dataclass_transform + +_T = TypeVar("_T") +_C = TypeVar("_C", bound=type) + +_EqOrderType = bool | Callable[[Any], Any] +_ValidatorType = Callable[[Any, "Attribute[_T]", _T], Any] +_CallableConverterType = Callable[[Any], Any] +_ConverterType = _CallableConverterType | Converter[Any, Any] +_ReprType = Callable[[Any], str] +_ReprArgType = bool | _ReprType +_OnSetAttrType = Callable[[Any, "Attribute[Any]", Any], Any] +_OnSetAttrArgType = _OnSetAttrType | list[_OnSetAttrType] | setters._NoOpType +_FieldTransformer = Callable[ + [type, list["Attribute[Any]"]], list["Attribute[Any]"] +] +# FIXME: in reality, if multiple validators are passed they must be in a list +# or tuple, but those are invariant and so would prevent subtypes of +# _ValidatorType from working when passed in a list or tuple. +_ValidatorArgType = _ValidatorType[_T] | Sequence[_ValidatorType[_T]] + +@overload +def field( + *, + default: None = ..., + validator: None = ..., + repr: _ReprArgType = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + converter: None = ..., + factory: None = ..., + kw_only: bool | None = ..., + eq: bool | None = ..., + order: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., + type: type | None = ..., +) -> Any: ... + +# This form catches an explicit None or no default and infers the type from the +# other arguments. +@overload +def field( + *, + default: None = ..., + validator: _ValidatorArgType[_T] | None = ..., + repr: _ReprArgType = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + converter: _ConverterType + | list[_ConverterType] + | tuple[_ConverterType, ...] + | None = ..., + factory: Callable[[], _T] | None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., + type: type | None = ..., +) -> _T: ... + +# This form catches an explicit default argument. +@overload +def field( + *, + default: _T, + validator: _ValidatorArgType[_T] | None = ..., + repr: _ReprArgType = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + converter: _ConverterType + | list[_ConverterType] + | tuple[_ConverterType, ...] + | None = ..., + factory: Callable[[], _T] | None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., + type: type | None = ..., +) -> _T: ... + +# This form covers type=non-Type: e.g. forward references (str), Any +@overload +def field( + *, + default: _T | None = ..., + validator: _ValidatorArgType[_T] | None = ..., + repr: _ReprArgType = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + converter: _ConverterType + | list[_ConverterType] + | tuple[_ConverterType, ...] + | None = ..., + factory: Callable[[], _T] | None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., + type: type | None = ..., +) -> Any: ... +@overload +@dataclass_transform(field_specifiers=(attrib, field)) +def define( + maybe_cls: _C, + *, + these: dict[str, Any] | None = ..., + repr: bool = ..., + unsafe_hash: bool | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: bool | None = ..., + order: bool | None = ..., + auto_detect: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., +) -> _C: ... +@overload +@dataclass_transform(field_specifiers=(attrib, field)) +def define( + maybe_cls: None = ..., + *, + these: dict[str, Any] | None = ..., + repr: bool = ..., + unsafe_hash: bool | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: bool | None = ..., + order: bool | None = ..., + auto_detect: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., +) -> Callable[[_C], _C]: ... + +mutable = define + +@overload +@dataclass_transform(frozen_default=True, field_specifiers=(attrib, field)) +def frozen( + maybe_cls: _C, + *, + these: dict[str, Any] | None = ..., + repr: bool = ..., + unsafe_hash: bool | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: bool | None = ..., + order: bool | None = ..., + auto_detect: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., +) -> _C: ... +@overload +@dataclass_transform(frozen_default=True, field_specifiers=(attrib, field)) +def frozen( + maybe_cls: None = ..., + *, + these: dict[str, Any] | None = ..., + repr: bool = ..., + unsafe_hash: bool | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: bool | None = ..., + order: bool | None = ..., + auto_detect: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., +) -> Callable[[_C], _C]: ... + +class ClassProps: + # XXX: somehow when defining/using enums Mypy starts looking at our own + # (untyped) code and causes tons of errors. + Hashability: Any + KeywordOnly: Any + + is_exception: bool + is_slotted: bool + has_weakref_slot: bool + is_frozen: bool + # kw_only: ClassProps.KeywordOnly + kw_only: Any + collected_fields_by_mro: bool + added_init: bool + added_repr: bool + added_eq: bool + added_ordering: bool + # hashability: ClassProps.Hashability + hashability: Any + added_match_args: bool + added_str: bool + added_pickling: bool + on_setattr_hook: _OnSetAttrType | None + field_transformer: Callable[[Attribute[Any]], Attribute[Any]] | None + + def __init__( + self, + is_exception: bool, + is_slotted: bool, + has_weakref_slot: bool, + is_frozen: bool, + # kw_only: ClassProps.KeywordOnly + kw_only: Any, + collected_fields_by_mro: bool, + added_init: bool, + added_repr: bool, + added_eq: bool, + added_ordering: bool, + # hashability: ClassProps.Hashability + hashability: Any, + added_match_args: bool, + added_str: bool, + added_pickling: bool, + on_setattr_hook: _OnSetAttrType, + field_transformer: Callable[[Attribute[Any]], Attribute[Any]], + ) -> None: ... + @property + def is_hashable(self) -> bool: ... + +def inspect(cls: type) -> ClassProps: ... diff --git a/.venv/lib/python3.9/site-packages/attrs/converters.py b/.venv/lib/python3.9/site-packages/attrs/converters.py new file mode 100644 index 0000000..7821f6c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/attrs/converters.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.converters import * # noqa: F403 diff --git a/.venv/lib/python3.9/site-packages/attrs/exceptions.py b/.venv/lib/python3.9/site-packages/attrs/exceptions.py new file mode 100644 index 0000000..3323f9d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/attrs/exceptions.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.exceptions import * # noqa: F403 diff --git a/.venv/lib/python3.9/site-packages/attrs/filters.py b/.venv/lib/python3.9/site-packages/attrs/filters.py new file mode 100644 index 0000000..3080f48 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/attrs/filters.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.filters import * # noqa: F403 diff --git a/.venv/lib/python3.9/site-packages/attrs/py.typed b/.venv/lib/python3.9/site-packages/attrs/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.9/site-packages/attrs/setters.py b/.venv/lib/python3.9/site-packages/attrs/setters.py new file mode 100644 index 0000000..f3d73bb --- /dev/null +++ b/.venv/lib/python3.9/site-packages/attrs/setters.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.setters import * # noqa: F403 diff --git a/.venv/lib/python3.9/site-packages/attrs/validators.py b/.venv/lib/python3.9/site-packages/attrs/validators.py new file mode 100644 index 0000000..037e124 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/attrs/validators.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.validators import * # noqa: F403 diff --git a/.venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/METADATA b/.venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/METADATA new file mode 100644 index 0000000..8568902 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/METADATA @@ -0,0 +1,64 @@ +Metadata-Version: 2.4 +Name: caldav +Version: 2.2.6 +Summary: CalDAV (RFC4791) client library +Project-URL: Repository, https://github.com/python-caldav/caldav +Project-URL: Issues, https://github.com/python-caldav/caldav/issues +Project-URL: Documentation, https://caldav.readthedocs.io/ +Project-URL: Changelog, https://github.com/python-caldav/caldav/blob/master/CHANGELOG.md +Author-email: Cyril Robert , Tobias Brox +License-Expression: GPL-3.0-or-later OR Apache-2.0 +License-File: COPYING.APACHE +License-File: COPYING.GPL +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Topic :: Office/Business :: Scheduling +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Dist: dnspython +Requires-Dist: icalendar-searcher<2,>=1.0.0 +Requires-Dist: icalendar>6.0.0 +Requires-Dist: lxml +Requires-Dist: niquests +Requires-Dist: python-dateutil +Requires-Dist: pyyaml +Requires-Dist: recurring-ical-events>=2.0.0 +Requires-Dist: typing-extensions; python_version < '3.11' +Provides-Extra: test +Requires-Dist: coverage; extra == 'test' +Requires-Dist: deptry>=0.24.0; (python_version >= '3.10') and extra == 'test' +Requires-Dist: manuel; extra == 'test' +Requires-Dist: proxy-py; extra == 'test' +Requires-Dist: pyfakefs; extra == 'test' +Requires-Dist: pytest; extra == 'test' +Requires-Dist: radicale; extra == 'test' +Requires-Dist: tzlocal; extra == 'test' +Requires-Dist: vobject; extra == 'test' +Requires-Dist: xandikos>=0.2.12; extra == 'test' +Description-Content-Type: text/markdown + +# caldav + +This project is a CalDAV ([RFC4791](http://www.ietf.org/rfc/rfc4791.txt)) client library for Python. + +Features: + + * create, modify calendar + * create, update and delete event + * search events by dates + * etc. + +The documentation was freshed up a bit as of version 2.0, and is available at https://caldav.readthedocs.io/ + +The package is published at [Pypi](https://pypi.org/project/caldav) + +Licences: + +Caldav is dual-licensed under the [GNU GENERAL PUBLIC LICENSE Version 3](COPYING.GPL) or the [Apache License 2.0](COPYING.APACHE). diff --git a/.venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/RECORD b/.venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/RECORD new file mode 100644 index 0000000..1a27052 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/RECORD @@ -0,0 +1,56 @@ +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/_version.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/calendarobjectresource.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/collection.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/compatibility_hints.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/config.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/davclient.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/davobject.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/discovery.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/elements/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/elements/base.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/elements/cdav.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/elements/dav.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/elements/ical.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/lib/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/lib/debug.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/lib/error.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/lib/namespace.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/lib/python_utilities.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/lib/url.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/lib/vcal.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/objects.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/requests.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/caldav/search.cpython-39.pyc,, +caldav-2.2.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +caldav-2.2.6.dist-info/METADATA,sha256=Diuvv3-o5dDIZe0Bh4HhlMF1sd0XSc0ZBp4cpO5K6Yo,2511 +caldav-2.2.6.dist-info/RECORD,, +caldav-2.2.6.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +caldav-2.2.6.dist-info/WHEEL,sha256=aha0VrrYvgDJ3Xxl3db_g_MDIW-ZexDdrc_m-Hk8YY4,105 +caldav-2.2.6.dist-info/licenses/COPYING.APACHE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358 +caldav-2.2.6.dist-info/licenses/COPYING.GPL,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147 +caldav/__init__.py,sha256=SIguQqvAIZ7T7LSsOwyhUS9JJ6YYwRD17pNUqykMk24,875 +caldav/_version.py,sha256=Yy8CV9q48KUMu2pMCEgiP9hyLI8En70mAREeufYt1vs,704 +caldav/calendarobjectresource.py,sha256=R4_VZNRj0t9PEZ2ULrYPMEB0LqxdbZeNI-8VGWfEufg,68562 +caldav/collection.py,sha256=3eJ728iKbKl__IVK2_yNlJyl-MtvknUPgsD7MFGT16M,59234 +caldav/compatibility_hints.py,sha256=kQjyEY4sRAClTYMWlmRKmeCqhQkGvJOU_G_piegN1-I,60664 +caldav/config.py,sha256=8paHkJKvjWaOJw7o79Zc6ZLbJls0GaFZofTxyuhBdYQ,4787 +caldav/davclient.py,sha256=T5uCx5_apGPJ15DX8Ilnlj_j_15wtnd_yQQcWLNk3ss,51462 +caldav/davobject.py,sha256=r3Ajayc_yQAd_PUlhULINhRo7iiPc1gaA0-7kGJrppM,15582 +caldav/discovery.py,sha256=2kjsePF3lYn8YV1wW2_GtaIze9jKGQNH0-HQZnDo8oU,19259 +caldav/elements/__init__.py,sha256=-JIt3g1eBf4bVQNNQd4JBeQNA7o0LK0W9tYKKX_r5s8,22 +caldav/elements/base.py,sha256=Bmf7xHKSVHAdjMNonDZmgnsmyRZKAGxwHqtTdK8v5sc,3220 +caldav/elements/cdav.py,sha256=tqQC6XK2W4C8P42fMrffaVvuBheZbg2n-jqwYP-haew,6212 +caldav/elements/dav.py,sha256=JOTEpeWCbgWwsh4nE3qCkdgY7MW58meMBJPDaE87PtE,2249 +caldav/elements/ical.py,sha256=ieCXmymCaydvOqN_t8SweOzED13acNnPHJSIqv5Chuc,322 +caldav/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +caldav/lib/debug.py,sha256=661wwx1hZJC1CwaxGj9kcAZt-J1aUge1qpA-cEArksc,334 +caldav/lib/error.py,sha256=vFn6GI7-hxaZwQa6nsqG_u8v-ew1pFJBmpkA0XVAmGw,3497 +caldav/lib/namespace.py,sha256=KyyLX_rwhBu0w9GhUqc2eZBB0sUICKtWG4tMqT0Rk4g,778 +caldav/lib/python_utilities.py,sha256=Q2FubT27eUNlXqhPpca9VLRxrTroklwOmH9d63-N9Dw,886 +caldav/lib/url.py,sha256=EY0f-NZAQEfsA4VLAay0qoPZibV3Zu17IxT9f0KslaI,7165 +caldav/lib/vcal.py,sha256=ZMGR0mO3EV-bp4BenkNhMbv8KKW76g9r42rcBCMYT3M,9999 +caldav/objects.py,sha256=Ux-MjbAKvd-7lBw9kWTtyWao2HgBJdseYYfbKqdZa5I,536 +caldav/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +caldav/requests.py,sha256=vXy9XAJ5t9f_TFjEGTTNm9smblWB0yq5AGwyrOZ10qk,515 +caldav/search.py,sha256=GBamWMDmHo82BPxVqP_LKcKXB4eKyBXJPx9lFvVBsp8,34210 diff --git a/.venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/REQUESTED b/.venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/WHEEL new file mode 100644 index 0000000..2ec21a5 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.28.0 +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any diff --git a/.venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/licenses/COPYING.APACHE b/.venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/licenses/COPYING.APACHE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/licenses/COPYING.APACHE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/licenses/COPYING.GPL b/.venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/licenses/COPYING.GPL new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav-2.2.6.dist-info/licenses/COPYING.GPL @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/.venv/lib/python3.9/site-packages/caldav/__init__.py b/.venv/lib/python3.9/site-packages/caldav/__init__.py new file mode 100644 index 0000000..433da77 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/__init__.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +import logging + +try: + from ._version import __version__ +except ModuleNotFoundError: + __version__ = "(unknown)" + import warnings + + warnings.warn( + "You need to install the `build` package and do a `python -m build` to get caldav.__version__ set correctly" + ) +from .davclient import DAVClient +from .davclient import get_davclient +from .search import CalDAVSearcher + +## TODO: this should go away in some future version of the library. +from .objects import * + +## We should consider if the NullHandler-logic below is needed or not, and +## if there are better alternatives? +# Silence notification of no default logging handler +log = logging.getLogger("caldav") + + +class NullHandler(logging.Handler): + def emit(self, record) -> None: + pass + + +log.addHandler(NullHandler()) + +__all__ = ["__version__", "DAVClient", "get_davclient"] diff --git a/.venv/lib/python3.9/site-packages/caldav/_version.py b/.venv/lib/python3.9/site-packages/caldav/_version.py new file mode 100644 index 0000000..57386bc --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/_version.py @@ -0,0 +1,34 @@ +# file generated by setuptools-scm +# don't change, don't track in version control + +__all__ = [ + "__version__", + "__version_tuple__", + "version", + "version_tuple", + "__commit_id__", + "commit_id", +] + +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import Tuple + from typing import Union + + VERSION_TUPLE = Tuple[Union[int, str], ...] + COMMIT_ID = Union[str, None] +else: + VERSION_TUPLE = object + COMMIT_ID = object + +version: str +__version__: str +__version_tuple__: VERSION_TUPLE +version_tuple: VERSION_TUPLE +commit_id: COMMIT_ID +__commit_id__: COMMIT_ID + +__version__ = version = '2.2.6' +__version_tuple__ = version_tuple = (2, 2, 6) + +__commit_id__ = commit_id = None diff --git a/.venv/lib/python3.9/site-packages/caldav/calendarobjectresource.py b/.venv/lib/python3.9/site-packages/caldav/calendarobjectresource.py new file mode 100644 index 0000000..f0b3ac0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/calendarobjectresource.py @@ -0,0 +1,1664 @@ +""" +Calendar Objects Resources, as defined in the RFC 4791. + +There are three subclasses Todo, Journal and Event. Those mirrors objects stored on the server. The word ``CalendarObjectResource`` is long, complicated and may be hard to understand. When you read the word "event" in any documentation, issue discussions, etc, then most likely it should be read as "a CalendarObjectResource, like an event, a task or a journal". Do not make the mistake of going directly to the Event-class if you want to contribute code for handling "events" - consider that the same code probably will be appicable to Joural and Todo events, if so, CalendarObjectResource is the right class! Clear as mud? + +FreeBusy is also defined as a Calendar Object Resource in the RFC, and it is a bit different . Perhaps there should be another class layer between CalendarObjectResource and Todo/Event/Journal to indicate that the three latter are closely related, while FreeBusy is something different. + +Alarms and Time zone objects does not have any class as for now. Those are typically subcomponents of an event/task/journal component. + +Users of the library should not need to construct any of those objects. To add new content to the calendar, use ``calendar.save_event``, ``calendar.save_todo`` or ``calendar.save_journal``. Those methods will return a CalendarObjectResource. +""" +import logging +import re +import sys +import uuid +import warnings +from collections import defaultdict +from datetime import datetime +from datetime import timedelta +from datetime import timezone +from typing import Any +from typing import List +from typing import Optional +from typing import Set +from typing import Tuple +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union +from urllib.parse import ParseResult +from urllib.parse import quote +from urllib.parse import SplitResult +from urllib.parse import unquote + +import icalendar +from dateutil.rrule import rrulestr +from icalendar import vCalAddress +from icalendar import vText + +try: + from typing import ClassVar, Optional, Union, Type + + TimeStamp = Optional[Union[date, datetime]] +except: + pass + +if TYPE_CHECKING: + from icalendar import vCalAddress + + from .davclient import DAVClient + +if sys.version_info < (3, 9): + from typing import Callable, Container, Iterable, Iterator, Sequence + + from typing_extensions import DefaultDict, Literal +else: + from collections import defaultdict as DefaultDict + from collections.abc import Callable, Container, Iterable, Iterator, Sequence + from typing import Literal + +if sys.version_info < (3, 11): + from typing_extensions import Self +else: + from typing import Self + +from .davobject import DAVObject +from .elements.cdav import CalendarData +from .elements import cdav +from .elements import dav +from .lib import error +from .lib import vcal +from .lib.error import errmsg +from .lib.python_utilities import to_normal_str +from .lib.python_utilities import to_unicode +from .lib.python_utilities import to_wire +from .lib.url import URL + +log = logging.getLogger("caldav") + + +class CalendarObjectResource(DAVObject): + """Ref RFC 4791, section 4.1, a "Calendar Object Resource" can be an + event, a todo-item, a journal entry, or a free/busy entry + + As per the RFC, a CalendarObjectResource can at most contain one + calendar component, with the exception of recurrence components. + Meaning that event.data typically contains one VCALENDAR with one + VEVENT and possibly one VTIMEZONE. + + In the case of expanded calendar date searches, each recurrence + will (by default) wrapped in a distinct CalendarObjectResource + object. This is a deviation from the definition given in the RFC. + """ + + ## There is also STARTTOFINISH, STARTTOSTART and FINISHTOFINISH in RFC9253, + ## those do not seem to have any reverse + ## (FINISHTOSTART and STARTTOFINISH may seem like reverse relations, but + ## as I read the RFC, FINISHTOSTART seems like the reverse of DEPENDS-ON) + ## (STARTTOSTART and FINISHTOFINISH may also seem like symmetric relations, + ## meaning they are their own reverse, but as I read the RFC they are + ## asymmetric) + RELTYPE_REVERSE_MAP: ClassVar = { + "PARENT": "CHILD", + "CHILD": "PARENT", + "SIBLING": "SIBLING", + ## this is how Tobias Brox inteprets RFC9253: + "DEPENDS-ON": "FINISHTOSTART", + "FINISHTOSTART": "DEPENDENT", + ## next/first is a special case, linked list + ## it needs special handling when length of list<>2 + # "NEXT": "FIRST", + # "FIRST": "NEXT", + } + + _ENDPARAM = None + + _vobject_instance = None + _icalendar_instance = None + _data = None + + def __init__( + self, + client: Optional["DAVClient"] = None, + url: Union[str, ParseResult, SplitResult, URL, None] = None, + data: Optional[Any] = None, + parent: Optional[Any] = None, + id: Optional[Any] = None, + props: Optional[Any] = None, + ) -> None: + """ + CalendarObjectResource has an additional parameter for its constructor: + * data = "...", vCal data for the event + """ + super(CalendarObjectResource, self).__init__( + client=client, url=url, parent=parent, id=id, props=props + ) + if data is not None: + self.data = data + if id: + old_id = self.icalendar_component.pop("UID", None) + self.icalendar_component.add("UID", id) + + def set_end(self, end, move_dtstart=False): + """The RFC specifies that a VEVENT/VTODO cannot have both + dtend/due and duration, so when setting dtend/due, the duration + field must be evicted + + WARNING: this method is likely to be deprecated and parts of + it moved to the icalendar library. If you decide to use it, + please put caldav<3.0 in the requirements. + """ + i = self.icalendar_component + ## TODO: are those lines useful for anything? + if hasattr(end, "tzinfo") and not end.tzinfo: + end = end.astimezone(timezone.utc) + duration = self.get_duration() + i.pop("DURATION", None) + i.pop(self._ENDPARAM, None) + + if move_dtstart and duration and "DTSTART" in i: + i.pop("DTSTART") + i.add("DTSTART", end - duration) + + i.add(self._ENDPARAM, end) + + def add_organizer(self) -> None: + """ + goes via self.client, finds the principal, figures out the right attendee-format and adds an + organizer line to the event + """ + if self.client is None: + raise ValueError("Unexpected value None for self.client") + + principal = self.client.principal() + ## TODO: remove Organizer-field, if exists + ## TODO: what if walk returns more than one vevent? + self.icalendar_component.add("organizer", principal.get_vcal_address()) + + def split_expanded(self) -> List[Self]: + """This was used internally for processing search results. + Library users probably don't need to care about this one. + + The logic is now handled directly in the search method. + + This method is probably used by nobody and nothing, but + it can't be removed easily as it's exposed as part of the + public API + """ + + warnings.warn( + "obj.split_expanded is likely to be removed in a future version of caldav. Feel free to protest if you need it", + DeprecationWarning, + stacklevel=2, + ) + + i = self.icalendar_instance.subcomponents + tz_ = [x for x in i if isinstance(x, icalendar.Timezone)] + ntz = [x for x in i if not isinstance(x, icalendar.Timezone)] + if len(ntz) == 1: + return [self] + if tz_: + error.assert_(len(tz_) == 1) + ret = [] + for ical_obj in ntz: + obj = self.copy(keep_uid=True) + obj.icalendar_instance.subcomponents = [] + if tz_: + obj.icalendar_instance.subcomponents.append(tz_[0]) + obj.icalendar_instance.subcomponents.append(ical_obj) + ret.append(obj) + return ret + + def expand_rrule( + self, start: datetime, end: datetime, include_completed: bool = True + ) -> None: + """This method will transform the calendar content of the + event and expand the calendar data from a "master copy" with + RRULE set and into a "recurrence set" with RECURRENCE-ID set + and no RRULE set. The main usage is for client-side expansion + in case the calendar server does not support server-side + expansion. If doing a `self.load`, the calendar + content will be replaced with the "master copy". + + :param event: Event + :param start: datetime + :param end: datetime + + """ + ## TODO: this has been *copied* over to the icalendar-searcher package. + ## This code was previously used internally by the search. + ## By now it's probably dead code, used by nothing and nobody. + ## Since it's exposed as part of the API, I cannot delete it, but I can + ## deprecate it. + warnings.warn( + "obj.expand_rrule is likely to be removed in a future version of caldav. Feel free to protest if you need it", + DeprecationWarning, + stacklevel=2, + ) + + import recurring_ical_events + + recurrings = recurring_ical_events.of( + self.icalendar_instance, components=["VJOURNAL", "VTODO", "VEVENT"] + ).between(start, end) + + recurrence_properties = {"exdate", "exrule", "rdate", "rrule"} + + error.assert_( + not any( + x + for x in recurrings + if not recurrence_properties.isdisjoint(set(x.keys())) + ) + ) + + calendar = self.icalendar_instance + calendar.subcomponents = [] + for occurrence in recurrings: + ## Ignore completed task recurrences + if ( + not include_completed + and occurrence.name == "VTODO" + and occurrence.get("STATUS") in ("COMPLETED", "CANCELLED") + ): + continue + ## TODO: If there are no reports of missing RECURRENCE-ID until 2027, + ## the if-statement below may be deleted + error.assert_("RECURRENCE-ID" in occurrence) + if "RECURRENCE-ID" not in occurrence: + occurrence.add("RECURRENCE-ID", occurrence.get("DTSTART").dt) + calendar.add_component(occurrence) + + def set_relation( + self, other, reltype=None, set_reverse=True + ) -> None: ## TODO: logic to find and set siblings? + """ + Sets a relation between this object and another object (given by uid or object). + """ + ##TODO: test coverage + reltype = reltype.upper() + if isinstance(other, CalendarObjectResource): + if other.id: + uid = other.id + else: + uid = other.icalendar_component["uid"] + else: + uid = other + if set_reverse: + other = self.parent.object_by_uid(uid) + if set_reverse: + ## TODO: special handling of NEXT/FIRST. + ## STARTTOFINISH does not have any equivalent "reverse". + reltype_reverse = self.RELTYPE_REVERSE_MAP[reltype] + other.set_relation(other=self, reltype=reltype_reverse, set_reverse=False) + + existing_relation = self.icalendar_component.get("related-to", None) + existing_relations = ( + existing_relation + if isinstance(existing_relation, list) + else [existing_relation] + ) + for rel in existing_relations: + if rel == uid: + return + + # without str(…), icalendar ignores properties + # because if type(uid) == vText + # then Component._encode does miss adding properties + # see https://github.com/collective/icalendar/issues/557 + # workaround should be safe to remove if issue gets fixed + uid = str(uid) + self.icalendar_component.add( + "related-to", uid, parameters={"RELTYPE": reltype}, encode=True + ) + + self.save() + + ## TODO: this method is undertested in the caldav library. + ## However, as this consolidated and eliminated quite some duplicated code in the + ## plann project, it is extensively tested in plann. + def get_relatives( + self, + reltypes: Optional[Container[str]] = None, + relfilter: Optional[Callable[[Any], bool]] = None, + fetch_objects: bool = True, + ignore_missing: bool = True, + ) -> DefaultDict[str, Set[str]]: + """ + By default, loads all objects pointed to by the RELATED-TO + property and loads the related objects. + + It's possible to filter, either by passing a set or a list of + acceptable relation types in reltypes, or by passing a lambda + function in relfilter. + + TODO: Make it possible to also check up reverse relationships + + TODO: this is partially overlapped by plann.lib._relships_by_type + in the plann tool. Should consolidate the code. + + TODO: should probably return some kind of object instead of a weird dict structure. + (but due to backward compatibility requirement, such an object should behave like + the current dict) + """ + from .collection import Calendar ## late import to avoid cycling imports + + ret = defaultdict(set) + relations = self.icalendar_component.get("RELATED-TO", []) + if not isinstance(relations, list): + relations = [relations] + for rel in relations: + if relfilter and not relfilter(rel): + continue + reltype = rel.params.get("RELTYPE", "PARENT") + if reltypes and reltype not in reltypes: + continue + ret[reltype].add(str(rel)) + + if fetch_objects: + for reltype in ret: + uids = ret[reltype] + reltype_set = set() + + if self.parent is None: + raise ValueError("Unexpected value None for self.parent") + + if not isinstance(self.parent, Calendar): + raise ValueError( + "self.parent expected to be of type Calendar but it is not" + ) + + for obj in uids: + try: + reltype_set.add(self.parent.object_by_uid(obj)) + except error.NotFoundError: + if not ignore_missing: + raise + + ret[reltype] = reltype_set + + return ret + + def _set_reverse_relation(self, other, reltype): + ## TODO: handle RFC9253 better! Particularly next/first-lists + reverse_reltype = self.RELTYPE_REVERSE_MAP.get(reltype) + if not reverse_reltype: + logging.error( + "Reltype %s not supported in object uid %s" % (reltype, self.id) + ) + return + other.set_relation(self, reverse_reltype, other) + + def _verify_reverse_relation(self, other, reltype) -> tuple: + revreltype = self.RELTYPE_REVERSE_MAP[reltype] + ## TODO: special case FIRST/NEXT needs special handling + other_relations = other.get_relatives( + fetch_objects=False, reltypes={revreltype} + ) + if not str(self.icalendar_component["uid"]) in other_relations[revreltype]: + ## I don't remember why we need to return a tuple + ## but it's propagated through the "public" methods, so we'll + ## have to leave it like this. + return (other, revreltype) + return False + + def _handle_reverse_relations( + self, verify: bool = False, fix: bool = False, pdb: bool = False + ) -> list: + """ + Goes through all relations and verifies that the return relation is set + if verify is set: + Returns a list of objects missing a reverse. + Use public method check_reverse_relations instead + if verify and fix is set: + Fixup all objects missing a reverse. + Use public method fix_reverse_relations instead. + If fix but not verify is set: + Assume all reverse relations are missing. + Used internally when creating new objects. + """ + ret = [] + assert verify or fix + relations = self.get_relatives() + for reltype in relations: + for other in relations[reltype]: + if verify: + foobar = self._verify_reverse_relation(other, reltype) + if foobar: + ret.append(foobar) + if pdb: + breakpoint() + if fix: + self._set_reverse_relation(other, reltype) + elif fix: + self._set_reverse_relation(other, reltype) + return ret + + def check_reverse_relations(self, pdb: bool = False) -> List[tuple]: + """ + Will verify that for all the objects we point at though + the RELATED-TO property, the other object points back to us as + well. + + Returns a list of tuples. Each tuple contains an object that + do not point back as expected, and the expected reltype + """ + return self._handle_reverse_relations(verify=True, fix=False, pdb=pdb) + + def fix_reverse_relations(self, pdb: bool = False) -> list: + """ + Will ensure that for all the objects we point at though + the RELATED-TO property, the other object points back to us as + well. + + Returns a list of tuples. Each tuple contains an object that + did not point back as expected, and the expected reltype + """ + return self._handle_reverse_relations(verify=True, fix=True, pdb=pdb) + + def _get_icalendar_component(self, assert_one=False): + """Returns the icalendar subcomponent - which should be an + Event, Journal, Todo or FreeBusy from the icalendar class + + See also https://github.com/python-caldav/caldav/issues/232 + """ + self.load(only_if_unloaded=True) + if not self.icalendar_instance: + return None + ## PERFORMANCE TODO: no point creating a big list here + ret = [ + x + for x in self.icalendar_instance.subcomponents + if not isinstance(x, icalendar.Timezone) + ] + error.assert_(len(ret) == 1 or not assert_one) + for x in ret: + for cl in ( + icalendar.Event, + icalendar.Journal, + icalendar.Todo, + icalendar.FreeBusy, + ): + if isinstance(x, cl): + return x + error.assert_(False) + + def _set_icalendar_component(self, value) -> None: + s = self.icalendar_instance.subcomponents + i = [i for i in range(0, len(s)) if not isinstance(s[i], icalendar.Timezone)] + if len(i) == 1: + self.icalendar_instance.subcomponents[i[0]] = value + else: + my_instance = icalendar.Calendar() + my_instance.add("prodid", self.icalendar_instance["prodid"]) + my_instance.add("version", self.icalendar_instance["version"]) + my_instance.add_component(value) + self.icalendar_instance = my_instance + + icalendar_component = property( + _get_icalendar_component, + _set_icalendar_component, + doc="icalendar component - this is the simplest way to access the event/task - it will give you the first component that isn't a timezone component. For recurrence sets, the master component will be returned. For any non-recurring event/task/journal, there should be only one calendar component in the object. For results from an expanded search, there should be only one calendar component in the object", + ) + + component = icalendar_component + + def get_due(self): + """ + A VTODO may have due or duration set. Return or calculate due. + + WARNING: this method is likely to be deprecated and moved to + the icalendar library. If you decide to use it, please put + caldav<3.0 in the requirements. + """ + i = self.icalendar_component + if "DUE" in i: + return i["DUE"].dt + elif "DTEND" in i: + return i["DTEND"].dt + elif "DURATION" in i and "DTSTART" in i: + return i["DTSTART"].dt + i["DURATION"].dt + else: + return None + + get_dtend = get_due + + def add_attendee( + self, attendee, no_default_parameters: bool = False, **parameters + ) -> None: + """ + For the current (event/todo/journal), add an attendee. + + The attendee can be any of the following: + * A principal + * An email address prepended with "mailto:" + * An email address without the "mailto:"-prefix + * A two-item tuple containing a common name and an email address + * (not supported, but planned: an ical text line starting with the word "ATTENDEE") + + Any number of attendee parameters can be given, those will be used + as defaults unless no_default_parameters is set to True: + + partstat=NEEDS-ACTION + cutype=UNKNOWN (unless a principal object is given) + rsvp=TRUE + role=REQ-PARTICIPANT + schedule-agent is not set + """ + from .collection import Principal ## late import to avoid cycling imports + + if isinstance(attendee, Principal): + attendee_obj = attendee.get_vcal_address() + elif isinstance(attendee, vCalAddress): + attendee_obj = attendee + elif isinstance(attendee, tuple): + if attendee[1].startswith("mailto:"): + attendee_obj = vCalAddress(attendee[1]) + else: + attendee_obj = vCalAddress("mailto:" + attendee[1]) + attendee_obj.params["cn"] = vText(attendee[0]) + elif isinstance(attendee, str): + if attendee.startswith("ATTENDEE"): + raise NotImplementedError( + "do we need to support this anyway? Should be trivial, but can't figure out how to do it with the icalendar.Event/vCalAddress objects right now" + ) + elif attendee.startswith("mailto:"): + attendee_obj = vCalAddress(attendee) + elif "@" in attendee and ":" not in attendee and ";" not in attendee: + attendee_obj = vCalAddress("mailto:" + attendee) + else: + error.assert_(False) + attendee_obj = vCalAddress() + + ## TODO: if possible, check that the attendee exists + ## TODO: check that the attendee will not be duplicated in the event. + if not no_default_parameters: + ## Sensible defaults: + attendee_obj.params["partstat"] = "NEEDS-ACTION" + if "cutype" not in attendee_obj.params: + attendee_obj.params["cutype"] = "UNKNOWN" + attendee_obj.params["rsvp"] = "TRUE" + attendee_obj.params["role"] = "REQ-PARTICIPANT" + params = {} + for key in parameters: + new_key = key.replace("_", "-") + if parameters[key] is True: + params[new_key] = "TRUE" + else: + params[new_key] = parameters[key] + attendee_obj.params.update(params) + ievent = self.icalendar_component + ievent.add("attendee", attendee_obj) + + def is_invite_request(self) -> bool: + """ + Returns True if this object is a request, see + https://www.rfc-editor.org/rfc/rfc2446.html#section-3.2.2 + """ + self.load(only_if_unloaded=True) + return self.icalendar_instance.get("method", None) == "REQUEST" + + def is_invite_reply(self) -> bool: + """ + Returns True if the object is a reply, see + https://www.rfc-editor.org/rfc/rfc2446.html#section-3.2.3 + """ + self.load(only_if_unloaded=True) + return self.icalendar_instance.get("method", None) == "REPLY" + + def accept_invite(self, calendar: Optional["Calendar"] = None) -> None: + """ + Accepts an invite - to be used on an invite object. + """ + self._reply_to_invite_request("ACCEPTED", calendar) + + def decline_invite(self, calendar: Optional["Calendar"] = None) -> None: + """ + Declines an invite - to be used on an invite object. + """ + self._reply_to_invite_request("DECLINED", calendar) + + def tentatively_accept_invite(self, calendar: Optional[Any] = None) -> None: + """ + Tentatively accept an invite - to be used on an invite object. + """ + self._reply_to_invite_request("TENTATIVE", calendar) + + ## TODO: DELEGATED is also a valid option, and for vtodos the + ## partstat can also be set to COMPLETED and IN-PROGRESS. + + def _reply_to_invite_request(self, partstat, calendar) -> None: + error.assert_(self.is_invite_request()) + if not calendar: + calendar = self.client.principal().calendars()[0] + ## we need to modify the icalendar code, update our own participant status + self.icalendar_instance.pop("METHOD") + self.change_attendee_status(partstat=partstat) + self.get_property(cdav.ScheduleTag(), use_cached=True) + try: + calendar.save_event(self.data) + except Exception: + ## TODO - TODO - TODO + ## RFC6638 does not seem to be very clear (or + ## perhaps I should read it more thoroughly) neither on + ## how to handle conflicts, nor if the reply should be + ## posted to the "outbox", saved back to the same url or + ## sent to a calendar. + self.load() + self.get_property(cdav.ScheduleTag(), use_cached=False) + outbox = self.client.principal().schedule_outbox() + if calendar.url != outbox.url: + self._reply_to_invite_request(partstat, calendar=outbox) + else: + self.save() + + def copy(self, keep_uid: bool = False, new_parent: Optional[Any] = None) -> Self: + """ + Events, todos etc can be copied within the same calendar, to another + calendar or even to another caldav server + """ + obj = self.__class__( + parent=new_parent or self.parent, + data=self.data, + id=self.id if keep_uid else str(uuid.uuid1()), + ) + if new_parent or not keep_uid: + obj.url = obj._generate_url() + else: + obj.url = self.url + return obj + + ## TODO: move get-logics to a load_by_get method. + ## The load method should deal with "server quirks". + def load(self, only_if_unloaded: bool = False) -> Self: + """ + (Re)load the object from the caldav server. + """ + if only_if_unloaded and self.is_loaded(): + return self + + if self.url is None: + raise ValueError("Unexpected value None for self.url") + + if self.client is None: + raise ValueError("Unexpected value None for self.client") + + try: + r = self.client.request(str(self.url)) + if r.status and r.status == 404: + raise error.NotFoundError(errmsg(r)) + self.data = r.raw + except error.NotFoundError: + raise + except: + return self.load_by_multiget() + if "Etag" in r.headers: + self.props[dav.GetEtag.tag] = r.headers["Etag"] + if "Schedule-Tag" in r.headers: + self.props[cdav.ScheduleTag.tag] = r.headers["Schedule-Tag"] + return self + + def load_by_multiget(self) -> Self: + """ + Some servers do not accept a GET, but we can still do a REPORT + with a multiget query + """ + error.assert_(self.url) + mydata = self.parent._multiget(event_urls=[self.url], raise_notfound=True) + try: + url, self.data = next(mydata) + except StopIteration: + ## We shouldn't come here. Something is wrong. + ## TODO: research it + ## As of 2025-05-20, this code section is used by + ## TestForServerECloud::testCreateOverwriteDeleteEvent + raise error.NotFoundError(self.url) + error.assert_(self.data) + error.assert_(next(mydata, None) is None) + return self + + ## TODO: self.id should either always be available or never + ## TODO: run this logic on load, to ensure `self.id` is set after loading + def _find_id_path(self, id=None, path=None) -> None: + """ + With CalDAV, every object has a URL. With icalendar, every object + should have a UID. This UID may or may not be copied into self.id. + + This method will: + + 0) if ID is given, assume that as the UID, and set it in the object + 1) if UID is given in the object, assume that as the ID + 2) if ID is not given, but the path is given, generate the ID from the + path + 3) If neither ID nor path is given, use the uuid method to generate an + ID (TODO: recommendation in the RFC is to concat some timestamp, serial or + random number and a domain) + 4) if no path is given, generate the URL from the ID + """ + i = self._get_icalendar_component(assert_one=False) + if not id and getattr(self, "id", None): + id = self.id + if not id: + id = i.pop("UID", None) + if id: + id = str(id) + if not path and getattr(self, "path", None): + path = self.path + if id is None and path is not None and str(path).endswith(".ics"): + ## TODO: do we ever get here? Perhaps this if is completely moot? + id = re.search("(/|^)([^/]*).ics", str(path)).group(2) + if id is None: + id = str(uuid.uuid1()) + + i.pop("UID", None) + i.add("UID", id) + + self.id = id + + for x in self.icalendar_instance.subcomponents: + if not isinstance(x, icalendar.Timezone): + error.assert_(x.get("UID", None) == self.id) + + if path is None: + path = self._generate_url() + else: + path = self.parent.url.join(path) + + self.url = URL.objectify(path) + + def _put(self, retry_on_failure=True): + ## SECURITY TODO: we should probably have a check here to verify that no such object exists already + r = self.client.put( + self.url, self.data, {"Content-Type": 'text/calendar; charset="utf-8"'} + ) + if r.status == 302: + path = [x[1] for x in r.headers if x[0] == "location"][0] + elif r.status not in (204, 201): + if retry_on_failure: + try: + import vobject + except ImportError: + retry_on_failure = False + if retry_on_failure: + ## This looks like a noop, but the object may be "cleaned". + ## See https://github.com/python-caldav/caldav/issues/43 + self.vobject_instance + return self._put(False) + else: + raise error.PutError(errmsg(r)) + + def _create(self, id=None, path=None, retry_on_failure=True) -> None: + ## TODO: Find a better method name + self._find_id_path(id=id, path=path) + self._put() + + def _generate_url(self): + ## See https://github.com/python-caldav/caldav/issues/143 for the rationale behind double-quoting slashes + ## TODO: should try to wrap my head around issues that arises when id contains weird characters. maybe it's + ## better to generate a new uuid here, particularly if id is in some unexpected format. + if not self.id: + self.id = self._get_icalendar_component(assert_one=False)["UID"] + return self.parent.url.join(quote(self.id.replace("/", "%2F")) + ".ics") + + def change_attendee_status(self, attendee: Optional[Any] = None, **kwargs) -> None: + """ + Updates the attendee-line according to the arguments received + """ + from .collection import Principal ## late import to avoid cycling imports + + if not attendee: + if self.client is None: + raise ValueError("Unexpected value None for self.client") + + attendee = self.client.principal() + + cnt = 0 + + if isinstance(attendee, Principal): + attendee_emails = attendee.calendar_user_address_set() + for addr in attendee_emails: + try: + self.change_attendee_status(addr, **kwargs) + ## TODO: can probably just return now + cnt += 1 + except error.NotFoundError: + pass + if not cnt: + raise error.NotFoundError( + "Principal %s is not invited to event" % str(attendee) + ) + error.assert_(cnt == 1) + return + + ical_obj = self.icalendar_component + attendee_lines = ical_obj["attendee"] + if isinstance(attendee_lines, str): + attendee_lines = [attendee_lines] + strip_mailto = lambda x: str(x).lower().replace("mailto:", "") + for attendee_line in attendee_lines: + if strip_mailto(attendee_line) == strip_mailto(attendee): + attendee_line.params.update(kwargs) + cnt += 1 + if not cnt: + raise error.NotFoundError("Participant %s not found in attendee list") + error.assert_(cnt == 1) + + def save( + self, + no_overwrite: bool = False, + no_create: bool = False, + obj_type: Optional[str] = None, + increase_seqno: bool = True, + if_schedule_tag_match: bool = False, + only_this_recurrence: bool = True, + all_recurrences: bool = False, + ) -> Self: + """Save the object, can be used for creation and update. + + no_overwrite and no_create will check if the object exists. + Those two are mutually exclusive. Some servers don't support + searching for an object uid without explicitly specifying what + kind of object it should be, hence obj_type can be passed. + obj_type is only used in conjunction with no_overwrite and + no_create. + + is_schedule_tag_match is currently ignored. (TODO - fix or remove) + + The SEQUENCE should be increased when saving a new version of + the object. If this behaviour is unwanted, then + increase_seqno should be set to False. Also, if SEQUENCE is + not set, then this will be ignored. + + The behaviour when saving a single recurrence object to the + server is as far as I can understand not defined in the RFCs, + but all servers I've tested against will overwrite the full + event with the recurrence instance (effectively deleting the + recurrence rule). That's almost for sure not what the caller + intended. only_this_recurrence and all_recurrences only + applies when trying to save a recurrence object. They are by + nature mutually exclusive, but since only_this_recurrence is + True by default, it will be ignored if all_recurrences is set. + + If you want to sent the recurrence as it is to the server, + you should set both all_recurrences and only_this_recurrence + to False. + + Returns: + * self + + """ + ## Rather than passing the icalendar data verbatimely, we're + ## efficiently running the icalendar code through the icalendar + ## library. This may cause data modifications and may "unfix" + ## https://github.com/python-caldav/caldav/issues/43 + ## TODO: think more about this + if not obj_type: + obj_type = self.__class__.__name__.lower() + if ( + self._vobject_instance is None + and self._data is None + and self._icalendar_instance is None + ): + ## TODO: This makes no sense. We should probably raise an error. + ## But the behaviour should be officially deprecated first. + return self + + path = self.url.path if self.url else None + + def get_self(): + self.id = self.id or self.icalendar_component.get("uid") + if self.id: + try: + if obj_type: + return getattr(self.parent, "%s_by_uid" % obj_type)(self.id) + else: + return self.parent.object_by_uid(self.id) + except error.NotFoundError: + return None + return None + + if no_overwrite or no_create: + ## SECURITY TODO: path names on the server does not + ## necessarily map cleanly to UUIDs. We need to do quite + ## some refactoring here to ensure all corner cases are + ## covered. Doing a GET first to check if the resource is + ## found and then a PUT also gives a potential race + ## condition. (Possibly the API gives no safe way to ensure + ## a unique new calendar item is created to the server without + ## overwriting old stuff or vice versa - it seems silly to me + ## to do a PUT instead of POST when creating new data). + ## TODO: the "find id"-logic is duplicated in _create, + ## should be refactored + existing = get_self() + if not self.id and no_create: + raise error.ConsistencyError("no_create flag was set, but no ID given") + if no_overwrite and existing: + raise error.ConsistencyError( + "no_overwrite flag was set, but object already exists" + ) + + if no_create and not existing: + raise error.ConsistencyError( + "no_create flag was set, but object does not exists" + ) + + ## Save a single recurrence-id and all calendars servers seems + ## to overwrite the full object, effectively deleting the + ## RRULE. I can't find this behaviour specified in the RFC. + ## That's probably not what the caller intended intended. + if ( + only_this_recurrence or all_recurrences + ) and "RECURRENCE-ID" in self.icalendar_component: + obj = get_self() ## get the full object, not only the recurrence + ici = obj.icalendar_instance # ical instance + if all_recurrences: + occ = obj.icalendar_component ## original calendar component + ncc = self.icalendar_component.copy() ## new calendar component + for prop in ["exdate", "exrule", "rdate", "rrule"]: + if prop in occ: + ncc[prop] = occ[prop] + + ## dtstart_diff = how much we've moved the time + ## TODO: we may easily have timezone problems here and events shifting some hours ... + dtstart_diff = ( + ncc.start.astimezone() - ncc["recurrence-id"].dt.astimezone() + ) + new_duration = ncc.duration + ncc.pop("dtstart") + ncc.add("dtstart", occ.start + dtstart_diff) + for ep in ("duration", "dtend"): + if ep in ncc: + ncc.pop(ep) + ncc.add("dtend", ncc.start + new_duration) + ncc.pop("recurrence-id") + s = ici.subcomponents + + ## Replace the "root" subcomponent + comp_idxes = ( + i + for i in range(0, len(s)) + if not isinstance(s[i], icalendar.Timezone) + ) + comp_idx = next(comp_idxes) + s[comp_idx] = ncc + + ## The recurrence-ids of all objects has to be + ## recalculated (this is probably not quite right. If + ## we move the time of a daily meeting from 8 to 10, + ## then we need to do this. If we move the date of + ## the first instance, then probably we shouldn't + ## ... oh well ... so many complications) + if dtstart_diff: + for i in comp_idxes: + rid = s[i].pop("recurrence-id") + s[i].add("recurrence-id", rid.dt + dtstart_diff) + + return obj.save(increase_seqno=increase_seqno) + if only_this_recurrence: + existing_idx = [ + i + for i in range(0, len(ici.subcomponents)) + if ici.subcomponents[i].get("recurrence-id") + == self.icalendar_component["recurrence-id"] + ] + error.assert_(len(existing_idx) <= 1) + if existing_idx: + ici.subcomponents[existing_idx[0]] = self.icalendar_component + else: + ici.add_component(self.icalendar_component) + return obj.save(increase_seqno=increase_seqno) + + if "SEQUENCE" in self.icalendar_component: + seqno = self.icalendar_component.pop("SEQUENCE", None) + if seqno is not None: + self.icalendar_component.add("SEQUENCE", seqno + 1) + + self._create(id=self.id, path=path) + return self + + def is_loaded(self): + """Returns True if there exists data in the object. An + object is considered not to be loaded if it contains no data + but just the URL. + + TOOD: bad side effect, converts the data to a string, + potentially breaking couplings + """ + return ( + (self._data and self._data.count("BEGIN:") > 1) + or self._vobject_instance + or self._icalendar_instance + ) + + def has_component(self): + """ + Returns True if there exists a VEVENT, VTODO or VJOURNAL in the data. + Returns False if it's only a VFREEBUSY, VTIMEZONE or unknown components. + + TODO: Bad side-effect: converts to data - any icalendar instances coupled to the object + will be decoupled. + + Used internally after search to remove empty search results (sometimes Google return such) + """ + return ( + self._data + or self._vobject_instance + or (self._icalendar_instance and self.icalendar_component) + ) and self.data.count("BEGIN:VEVENT") + self.data.count( + "BEGIN:VTODO" + ) + self.data.count( + "BEGIN:VJOURNAL" + ) > 0 + + def __str__(self) -> str: + return "%s: %s" % (self.__class__.__name__, self.url) + + ## implementation of the properties self.data, + ## self.vobject_instance and self.icalendar_instance follows. The + ## rule is that only one of them can be set at any time, this + ## since vobject_instance and icalendar_instance are mutable, + ## and any modification to those instances should apply + def _set_data(self, data): + ## The __init__ takes a data attribute, and it should be allowable to + ## set it to a vobject object or an icalendar object, hence we should + ## do type checking on the data (TODO: but should probably use + ## isinstance rather than this kind of logic + if type(data).__module__.startswith("vobject"): + self._set_vobject_instance(data) + return self + + if type(data).__module__.startswith("icalendar"): + self._set_icalendar_instance(data) + return self + + self._data = vcal.fix(data) + self._vobject_instance = None + self._icalendar_instance = None + return self + + def _get_data(self): + if self._data: + return to_normal_str(self._data) + elif self._vobject_instance: + return to_normal_str(self._vobject_instance.serialize()) + elif self._icalendar_instance: + return to_normal_str(self._icalendar_instance.to_ical()) + return None + + def _get_wire_data(self): + if self._data: + return to_wire(self._data) + elif self._vobject_instance: + return to_wire(self._vobject_instance.serialize()) + elif self._icalendar_instance: + return to_wire(self._icalendar_instance.to_ical()) + return None + + data: Any = property( + _get_data, _set_data, doc="vCal representation of the object as normal string" + ) + wire_data = property( + _get_wire_data, + _set_data, + doc="vCal representation of the object in wire format (UTF-8, CRLN)", + ) + + def _set_vobject_instance(self, inst: "vobject.base.Component"): + self._vobject_instance = inst + self._data = None + self._icalendar_instance = None + return self + + def _get_vobject_instance(self) -> Optional["vobject.base.Component"]: + try: + import vobject + except ImportError: + logging.critical( + "A vobject instance has been requested, but the vobject library is not installed (vobject is no longer an official dependency in 2.0)" + ) + return None + if not self._vobject_instance: + if self._get_data() is None: + return None + try: + self._set_vobject_instance( + vobject.readOne(to_unicode(self._get_data())) # type: ignore + ) + except: + log.critical( + "Something went wrong while loading icalendar data into the vobject class. ical url: " + + str(self.url) + ) + raise + return self._vobject_instance + + ## event.instance has always yielded a vobject, but will probably yield an icalendar_instance + ## in version 3.0! + def _get_deprecated_vobject_instance(self) -> Optional["vobject.base.Component"]: + warnings.warn( + "use event.vobject_instance or event.icalendar_instance", + DeprecationWarning, + stacklevel=2, + ) + return self._get_vobject_instance() + + def _set_deprecated_vobject_instance(self, inst: "vobject.base.Component"): + warnings.warn( + "use event.vobject_instance or event.icalendar_instance", + DeprecationWarning, + stacklevel=2, + ) + return self._get_vobject_instance(inst) + + vobject_instance: "vobject.base.VBase" = property( + _get_vobject_instance, + _set_vobject_instance, + doc="vobject instance of the object", + ) + + instance: "vobject.base.VBase" = property( + _get_deprecated_vobject_instance, + _set_deprecated_vobject_instance, + doc="vobject instance of the object (DEPRECATED! This will yield an icalendar instance in caldav 3.0)", + ) + + def _set_icalendar_instance(self, inst): + if not isinstance(inst, icalendar.Calendar): + ## assume inst is an Event, Journal or Todo. + ## TODO: perhaps a bit better sanity checking here? + try: ## DEPRECATION TODO: remove this try/except the future + ## icalendar 7.x behaviour (not released yet as of 2025-09 + cal = icalendar.Calendar.new() + except: + cal = icalendar.Calendar() + cal.add("prodid", "-//python-caldav//caldav//en_DK") + cal.add("version", "2.0") + cal.add_component(inst) + inst = cal + self._icalendar_instance = inst + self._data = None + self._vobject_instance = None + return self + + def _get_icalendar_instance(self): + if not self._icalendar_instance: + if not self.data: + return None + self.icalendar_instance = icalendar.Calendar.from_ical( + to_unicode(self.data) + ) + return self._icalendar_instance + + icalendar_instance: Any = property( + _get_icalendar_instance, + _set_icalendar_instance, + doc="icalendar instance of the object", + ) + + def get_duration(self) -> timedelta: + """According to the RFC, either DURATION or DUE should be set + for a task, but never both - implicitly meaning that DURATION + is the difference between DTSTART and DUE (personally I + believe that's stupid. If a task takes five minutes to + complete - say, fill in some simple form that should be + delivered before midnight at new years eve, then it feels + natural for me to define "duration" as five minutes, DTSTART + to "some days before new years eve" and DUE to 20xx-01-01 + 00:00:00 - but I digress. + + This method will return DURATION if set, otherwise the + difference between DUE and DTSTART (if both of them are set). + + TODO: should be fixed for Event class as well (only difference + is that DTEND is used rather than DUE) and possibly also for + Journal (defaults to one day, probably?) + + WARNING: this method is likely to be deprecated and moved to + the icalendar library. If you decide to use it, please put + caldav<3.0 in the requirements. + """ + i = self.icalendar_component + return self._get_duration(i) + + def _get_duration(self, i): + if "DURATION" in i: + return i["DURATION"].dt + elif "DTSTART" in i and self._ENDPARAM in i: + end = i[self._ENDPARAM].dt + start = i["DTSTART"].dt + ## We do have a problem here if one is a date and the other is a + ## datetime. This is NOT explicitly defined as a technical + ## breach in the RFC, so we need to work around it. + if isinstance(end, datetime) != isinstance(start, datetime): + start = datetime(start.year, start.month, start.day) + end = datetime(end.year, end.month, end.day) + return end - start + elif "DTSTART" in i and not isinstance(i["DTSTART"], datetime): + return timedelta(days=1) + else: + return timedelta(0) + + +class Event(CalendarObjectResource): + """ + The `Event` object is used to represent an event (VEVENT). + + As of 2020-12 it adds very little to the inheritated class. (I have + frequently asked myself if we need those subclasses ... perhaps + not) + """ + + set_dtend = CalendarObjectResource.set_end + _ENDPARAM = "DTEND" + + +class Journal(CalendarObjectResource): + """ + The `Journal` object is used to represent a journal entry (VJOURNAL). + + As of 2020-12 it adds nothing to the inheritated class. (I have + frequently asked myself if we need those subclasses ... perhaps + not) + """ + + pass + + +class FreeBusy(CalendarObjectResource): + """ + The `FreeBusy` object is used to represent a freebusy response from + the server. __init__ is overridden, as a FreeBusy response has no + URL or ID. The inheritated methods .save and .load is moot and + will probably throw errors (perhaps the class hierarchy should be + rethought, to prevent the FreeBusy from inheritating moot methods) + + Update: With RFC6638 a freebusy object can have a URL and an ID. + """ + + def __init__( + self, + parent, + data, + url: Union[str, ParseResult, SplitResult, URL, None] = None, + id: Optional[Any] = None, + ) -> None: + CalendarObjectResource.__init__( + self, client=parent.client, url=url, data=data, parent=parent, id=id + ) + + +class Todo(CalendarObjectResource): + """The `Todo` object is used to represent a todo item (VTODO). A + Todo-object can be completed. + + There is some extra logic here - arguably none of it belongs to + the caldav library, and should be moved either to the icalendar + library or to the plann library (plann is a cli-tool, should + probably be split up into one library for advanced calendaring + operations and the cli-tool as separate packages) + """ + + _ENDPARAM = "DUE" + + def _next(self, ts=None, i=None, dtstart=None, rrule=None, by=None, no_count=True): + """Special logic to fint the next DTSTART of a recurring + just-completed task. + + If any BY*-parameters are present, assume the task should have + fixed deadlines and preserve information from the previous + dtstart. If no BY*-parameters are present, assume the + frequency is meant to be the interval between the tasks. + + Examples: + + 1) Garbage collection happens every week on a Tuesday, but + never earlier than 09 in the morning. Hence, it may be + important to take out the thrash Monday evenings or Tuesday + morning. DTSTART of the original task is set to Tuesday + 2022-11-01T08:50, DUE to 09:00. + + 1A) Task is completed 07:50 on the 1st of November. Next + DTSTART should be Tuesday the 7th of November at 08:50. + + 1B) Task is completed 09:15 on the 1st of November (which is + probably OK, since they usually don't come before 09:30). + Next DTSTART should be Tuesday the 7th of November at 08:50. + + 1C) Task is completed at the 5th of November. We've lost the + DUE, but the calendar has no idea weather the DUE was a very + hard due or not - and anyway, probably we'd like to do it + again on Tuesday, so next DTSTART should be Tuesday the 7th of + November at 08:50. + + 1D) Task is completed at the 7th of November at 07:50. Next + DTSTART should be one hour later. Now, this is very silly, + but an algorithm cannot do guesswork on weather it's silly or + not. If DTSTART would be set to the earliest possible time + one could start thinking on this task (like, Monday evening), + then we would get Tue the 14th of November, which does make + sense. Unfortunately the icalendar standard does not specify + what should be used for DTSTART and DURATION/DUE. + + 1E) Task is completed on the 7th of November at 08:55. This + efficiently means we've lost the 1st of November recurrence + but have done the 7th of November recurrence instead, so next + timestamp will be the 14th of November. + + 2) Floors at home should be cleaned like once a week, but + there is no fixed deadline for it. For some people it may + make sense to have a routine doing it i.e. every Tuesday, but + this is not a strict requirement. If it wasn't done one + Tuesday, it's probably even more important to do it Wednesday. + If the floor was cleaned on a Saturday, it probably doesn't + make sense cleaning it again on Tuesday, but it probably + shouldn't wait until next Tuesday. Rrule is set to + FREQ=WEEKLY, but without any BYDAY. The original VTODO is set + up with DTSTART 16:00 on Tuesday the 1st of November and DUE + 17:00. After 17:00 there will be dinner, so best to get it + done before that. + + 2A) Floor cleaning was finished 14:30. The next recurrence + has DTSTART set to 13:30 (and DUE set to 14:30). The idea + here is that since the floor starts accumulating dirt right + after 14:30, obviously it is overdue at 16:00 Tuesday the 7th. + + 2B) Floor cleaning was procrastinated with one day and + finished Wednesday at 14:30. Next instance will be Wednesday + in a week, at 14:30. + + 2C) Floor cleaning was procrastinated with two weeks and + finished Tuesday the 14th at 14:30. Next instance will be + Tuesday the 21st at 14:30. + + While scenario 2 is the most trivial to implement, it may not + be the correct understanding of the RFC, and it may be tricky + to get the RECURRENCE-ID set correctly. + + """ + if not i: + i = self.icalendar_component + if not rrule: + rrule = i["RRULE"] + if not dtstart: + if by is True or ( + by is None and any((x for x in rrule if x.startswith("BY"))) + ): + if "DTSTART" in i: + dtstart = i["DTSTART"].dt + else: + dtstart = ts or datetime.now() + else: + dtstart = ts or datetime.now() - self._get_duration(i) + ## dtstart should be compared to the completion timestamp, which + ## is set in UTC in the complete() method. However, dtstart + ## may be a naïve or a floating timestamp + ## (TODO: what if it's a date?) + ## (TODO: we need test code for those corner cases!) + if hasattr(dtstart, "astimezone"): + dtstart = dtstart.astimezone(timezone.utc) + if not ts: + ts = dtstart + ## Counting is taken care of other places + if no_count and "COUNT" in rrule: + rrule = rrule.copy() + rrule.pop("COUNT") + rrule = rrulestr(rrule.to_ical().decode("utf-8"), dtstart=dtstart) + return rrule.after(ts) + + def _reduce_count(self, i=None) -> bool: + if not i: + i = self.icalendar_component + if "COUNT" in i["RRULE"]: + if i["RRULE"]["COUNT"][0] == 1: + return False + i["RRULE"]["COUNT"][0] -= 1 + return True + + def _complete_recurring_safe(self, completion_timestamp): + """This mode will create a new independent task which is + marked as completed, and modify the existing recurring task. + It is probably the most safe way to handle the completion of a + recurrence of a recurring task, though the link between the + completed task and the original task is lost. + """ + ## If count is one, then it is not really recurring + if not self._reduce_count(): + return self.complete(handle_rrule=False) + next_dtstart = self._next(completion_timestamp) + if not next_dtstart: + return self.complete(handle_rrule=False) + + completed = self.copy() + completed.url = self.parent.url.join(completed.id + ".ics") + completed.icalendar_component.pop("RRULE") + completed.save() + completed.complete() + + duration = self.get_duration() + i = self.icalendar_component + i.pop("DTSTART", None) + i.add("DTSTART", next_dtstart) + self.set_duration(duration, movable_attr="DUE") + + self.save() + + def _complete_recurring_thisandfuture(self, completion_timestamp) -> None: + """The RFC is not much helpful, a lot of guesswork is needed + to consider what the "right thing" to do wrg of a completion of + recurring tasks is ... but this is my shot at it. + + 1) The original, with rrule, will be kept as it is. The rrule + string is fetched from the first subcomponent of the + icalendar. + + 2) If there are multiple recurrence instances in subcomponents + and the last one is marked with RANGE=THISANDFUTURE, then + select this one. If it has the rrule property set, use this + rrule rather than the original one. Drop the RANGE parameter. + Calculate the next RECURRENCE-ID from the DTSTART of this + object. Mark task as completed. Increase SEQUENCE. + + 3) Create a new recurrence instance with RANGE=THISANDFUTURE, + without RRULE set (Ref + https://github.com/Kozea/Radicale/issues/1264). Set the + RECURRENCE-ID to the one calculated in #2. Calculate the + DTSTART based on rrule and completion timestamp/date. + """ + recurrences = self.icalendar_instance.subcomponents + orig = recurrences[0] + if "STATUS" not in orig: + orig["STATUS"] = "NEEDS-ACTION" + + if len(recurrences) == 1: + ## We copy the original one + just_completed = orig.copy() + just_completed.pop("RRULE") + just_completed.add( + "RECURRENCE-ID", orig.get("DTSTART", completion_timestamp) + ) + seqno = just_completed.pop("SEQUENCE", 0) + just_completed.add("SEQUENCE", seqno + 1) + recurrences.append(just_completed) + + prev = recurrences[-1] + rrule = prev.get("RRULE", orig["RRULE"]) + thisandfuture = prev.copy() + seqno = thisandfuture.pop("SEQUENCE", 0) + thisandfuture.add("SEQUENCE", seqno + 1) + + ## If we have multiple recurrences, assume the last one is a THISANDFUTURE. + ## (Otherwise, the data is coming from another client ...) + ## The RANGE parameter needs to be removed + if len(recurrences) > 2: + if prev["RECURRENCE-ID"].params.get("RANGE", None) == "THISANDFUTURE": + prev["RECURRENCE-ID"].params.pop("RANGE") + else: + raise NotImplementedError( + "multiple instances found, but last one is not of type THISANDFUTURE, possibly this has been created by some incompatible client, but we should deal with it" + ) + self._complete_ical(prev, completion_timestamp) + + thisandfuture.pop("RECURRENCE-ID", None) + thisandfuture.add("RECURRENCE-ID", self._next(i=prev, rrule=rrule)) + thisandfuture["RECURRENCE-ID"].params["RANGE"] = "THISANDFUTURE" + rrule2 = thisandfuture.pop("RRULE", None) + + ## Counting logic + if rrule2 is not None: + count = rrule2.get("COUNT", None) + if count is not None and count[0] in (0, 1): + for i in recurrences: + self._complete_ical(i, completion_timestamp=completion_timestamp) + thisandfuture.add("RRULE", rrule2) + else: + count = rrule.get("COUNT", None) + if count is not None and count[0] <= len( + [x for x in recurrences if not self.is_pending(x)] + ): + self._complete_ical( + recurrences[0], completion_timestamp=completion_timestamp + ) + self.save(increase_seqno=False) + return + + rrule = rrule2 or rrule + + duration = self._get_duration(i=prev) + thisandfuture.pop("DTSTART", None) + thisandfuture.pop("DUE", None) + next_dtstart = self._next(i=prev, rrule=rrule, ts=completion_timestamp) + thisandfuture.add("DTSTART", next_dtstart) + self._set_duration(i=thisandfuture, duration=duration, movable_attr="DUE") + self.icalendar_instance.subcomponents.append(thisandfuture) + self.save(increase_seqno=False) + + def complete( + self, + completion_timestamp: Optional[datetime] = None, + handle_rrule: bool = False, + rrule_mode: Literal["safe", "this_and_future"] = "safe", + ) -> None: + """Marks the task as completed. + + Parameters + ---------- + completion_timestamp : datetime + Defaults to ``datetime.now()``. + handle_rrule : Bool + If set to True, the library will try to be smart if + the task is recurring. The default is False, for backward + compatibility. I may consider making this one mandatory. + rrule_mode : str + The RFC leaves a lot of room for interpretation on how + to handle recurring tasks, and what works on one server may break at + another. The following modes are accepted: + * this_and_future - see doc for _complete_recurring_thisandfuture for details + * safe - see doc for _complete_recurring_safe for details + """ + if not completion_timestamp: + completion_timestamp = datetime.now(timezone.utc) + + if "RRULE" in self.icalendar_component and handle_rrule: + return getattr(self, "_complete_recurring_%s" % rrule_mode)( + completion_timestamp + ) + self._complete_ical(completion_timestamp=completion_timestamp) + self.save() + + def _complete_ical(self, i=None, completion_timestamp=None) -> None: + if i is None: + i = self.icalendar_component + assert self.is_pending(i) + status = i.pop("STATUS", None) + i.add("STATUS", "COMPLETED") + i.add("COMPLETED", completion_timestamp) + + def is_pending(self, i=None) -> Optional[bool]: + if i is None: + i = self.icalendar_component + if i.get("COMPLETED", None) is not None: + return False + if i.get("STATUS", "NEEDS-ACTION") in ("NEEDS-ACTION", "IN-PROCESS"): + return True + if i.get("STATUS", "NEEDS-ACTION") in ("CANCELLED", "COMPLETED"): + return False + ## input data does not conform to the RFC + assert False + + def uncomplete(self) -> None: + """Undo completion - marks a completed task as not completed""" + ### TODO: needs test code for code coverage! + ## (it has been tested through the calendar-cli test code) + if "status" in self.icalendar_component: + self.icalendar_component.pop("status") + self.icalendar_component.add("status", "NEEDS-ACTION") + if "completed" in self.icalendar_component: + self.icalendar_component.pop("completed") + self.save() + + ## TODO: should be moved up to the base class + def set_duration(self, duration, movable_attr="DTSTART"): + """ + If DTSTART and DUE/DTEND is already set, one of them should be moved. Which one? I believe that for EVENTS, the DTSTART should remain constant and DTEND should be moved, but for a task, I think the due date may be a hard deadline, hence by default we'll move DTSTART. + + TODO: can this be written in a better/shorter way? + + WARNING: this method may be deprecated and moved to + the icalendar library at some point in the future. + """ + i = self.icalendar_component + return self._set_duration(i, duration, movable_attr) + + def _set_duration(self, i, duration, movable_attr="DTSTART") -> None: + if ("DUE" in i or "DURATION" in i) and "DTSTART" in i: + i.pop(movable_attr, None) + if movable_attr == "DUE": + i.pop("DURATION", None) + if movable_attr == "DTSTART": + i.add("DTSTART", i["DUE"].dt - duration) + elif movable_attr == "DUE": + i.add("DUE", i["DTSTART"].dt + duration) + elif "DUE" in i: + i.add("DTSTART", i["DUE"].dt - duration) + elif "DTSTART" in i: + i.add("DUE", i["DTSTART"].dt + duration) + else: + if "DURATION" in i: + i.pop("DURATION") + i.add("DURATION", duration) + + def set_due(self, due, move_dtstart=False, check_dependent=False): + """The RFC specifies that a VTODO cannot have both due and + duration, so when setting due, the duration field must be + evicted + + check_dependent=True will raise some error if there exists a + parent calendar component (through RELATED-TO), and the parents + due or dtend is before the new dtend). + + WARNING: this method may become deprecated and parts of + it moved to the icalendar library at some point in the future. + + WARNING: the check_dependent-logic may be rewritten to support + RFC9253 in 3.x + """ + i = self.icalendar_component + if hasattr(due, "tzinfo") and not due.tzinfo: + due = due.astimezone(timezone.utc) + if check_dependent: + parents = self.get_relatives({"PARENT"}) + for parent in parents["PARENT"]: + pend = parent.get_dtend() + ## Make sure both timestamps aren't "naive": + if hasattr(pend, "tzinfo") and not pend.tzinfo: + pend = pend.astimezone(timezone.utc) + ## pend and due may be date and datetime, then they cannot be compared directly + if pend and pend.strftime("%s") < due.strftime("%s"): + if check_dependent == "return": + return parent + raise error.ConsistencyError( + "parent object has due/end %s, cannot procrastinate child object without first procrastinating parent object" + ) + CalendarObjectResource.set_end(self, due, move_dtstart) + + set_end = set_due diff --git a/.venv/lib/python3.9/site-packages/caldav/collection.py b/.venv/lib/python3.9/site-packages/caldav/collection.py new file mode 100644 index 0000000..530054c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/collection.py @@ -0,0 +1,1549 @@ +""" +I'm trying to be consistent with the terminology in the RFCs: + +CalendarSet is a collection of Calendars +Calendar is a collection of CalendarObjectResources +Principal is not a collection, but holds a CalendarSet. + +There are also some Mailbox classes to deal with RFC6638. + +A SynchronizableCalendarObjectCollection contains a local copy of objects from a calendar on the server. +""" +import logging +import sys +import uuid +import warnings +from dataclasses import dataclass +from datetime import datetime +from time import sleep +from typing import Any +from typing import List +from typing import Optional +from typing import Tuple +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union +from urllib.parse import ParseResult +from urllib.parse import quote +from urllib.parse import SplitResult +from urllib.parse import unquote + +import icalendar +from icalendar.caselessdict import CaselessDict + +try: + from typing import ClassVar, Optional, Union, Type + + TimeStamp = Optional[Union[date, datetime]] +except: + pass + +if TYPE_CHECKING: + from icalendar import vCalAddress + + from .davclient import DAVClient + +if sys.version_info < (3, 9): + from typing import Callable, Container, Iterable, Iterator, Sequence + + from typing_extensions import DefaultDict, Literal +else: + from collections import defaultdict as DefaultDict + from collections.abc import Callable, Container, Iterable, Iterator, Sequence + from typing import Literal + +if sys.version_info < (3, 11): + from typing_extensions import Self +else: + from typing import Self + +from .calendarobjectresource import CalendarObjectResource +from .calendarobjectresource import Event +from .calendarobjectresource import FreeBusy +from .calendarobjectresource import Journal +from .calendarobjectresource import Todo +from .davobject import DAVObject +from .elements.cdav import CalendarData +from .elements import cdav +from .elements import dav +from .lib import error +from .lib import vcal +from .lib.python_utilities import to_wire +from .lib.url import URL + +_CC = TypeVar("_CC", bound="CalendarObjectResource") +log = logging.getLogger("caldav") + + +class CalendarSet(DAVObject): + """ + A CalendarSet is a set of calendars. + """ + + def calendars(self) -> List["Calendar"]: + """ + List all calendar collections in this set. + + Returns: + * [Calendar(), ...] + """ + cals = [] + + data = self.children(cdav.Calendar.tag) + for c_url, c_type, c_name in data: + try: + cal_id = c_url.split("/")[-2] + if not cal_id: + continue + except: + log.error(f"Calendar {c_name} has unexpected url {c_url}") + cal_id = None + cals.append( + Calendar(self.client, id=cal_id, url=c_url, parent=self, name=c_name) + ) + + return cals + + def make_calendar( + self, + name: Optional[str] = None, + cal_id: Optional[str] = None, + supported_calendar_component_set: Optional[Any] = None, + method=None, + ) -> "Calendar": + """ + Utility method for creating a new calendar. + + Args: + name: the display name of the new calendar + cal_id: the uuid of the new calendar + supported_calendar_component_set: what kind of objects + (EVENT, VTODO, VFREEBUSY, VJOURNAL) the calendar should handle. + Should be set to ['VTODO'] when creating a task list in Zimbra - + in most other cases the default will be OK. + + Returns: + Calendar(...)-object + """ + return Calendar( + self.client, + name=name, + parent=self, + id=cal_id, + supported_calendar_component_set=supported_calendar_component_set, + ).save(method=method) + + def calendar( + self, name: Optional[str] = None, cal_id: Optional[str] = None + ) -> "Calendar": + """ + The calendar method will return a calendar object. If it gets a cal_id + but no name, it will not initiate any communication with the server + + Args: + name: return the calendar with this display name + cal_id: return the calendar with this calendar id or URL + + Returns: + Calendar(...)-object + """ + if name and not cal_id: + for calendar in self.calendars(): + display_name = calendar.get_display_name() + if display_name == name: + return calendar + if name and not cal_id: + raise error.NotFoundError( + "No calendar with name %s found under %s" % (name, self.url) + ) + if not cal_id and not name: + cals = self.calendars() + if not cals: + raise error.NotFoundError("no calendars found") + return cals[0] + + if self.client is None: + raise ValueError("Unexpected value None for self.client") + + if cal_id is None: + raise ValueError("Unexpected value None for cal_id") + + if str(URL.objectify(cal_id).canonical()).startswith( + str(self.client.url.canonical()) + ): + url = self.client.url.join(cal_id) + elif isinstance(cal_id, URL) or ( + isinstance(cal_id, str) + and (cal_id.startswith("https://") or cal_id.startswith("http://")) + ): + if self.url is None: + raise ValueError("Unexpected value None for self.url") + + url = self.url.join(cal_id) + else: + if self.url is None: + raise ValueError("Unexpected value None for self.url") + + if cal_id is None: + raise ValueError("Unexpected value None for cal_id") + + url = self.url.join(quote(cal_id) + "/") + + return Calendar(self.client, name=name, parent=self, url=url, id=cal_id) + + +class Principal(DAVObject): + """ + This class represents a DAV Principal. It doesn't do much, except + keep track of the URLs for the calendar-home-set, etc. + + A principal MUST have a non-empty DAV:displayname property + (defined in Section 13.2 of [RFC2518]), + and a DAV:resourcetype property (defined in Section 13.9 of [RFC2518]). + Additionally, a principal MUST report the DAV:principal XML element + in the value of the DAV:resourcetype property. + + (TODO: the resourcetype is actually never checked, and the DisplayName + is not stored anywhere) + """ + + def __init__( + self, + client: Optional["DAVClient"] = None, + url: Union[str, ParseResult, SplitResult, URL, None] = None, + calendar_home_set: URL = None, + **kwargs, ## to be passed to super.__init__ + ) -> None: + """ + Returns a Principal. + + End-users usually shouldn't need to construct Principal-objects directly. Use davclient.principal() to get the principal object of the logged-in user and davclient.principals() to get other principals. + + Args: + client: a DAVClient() object + url: The URL, if known. + calendar_home_set: the calendar home set, if known + + If url is not given, deduct principal path as well as calendar home set + path from doing propfinds. + """ + self._calendar_home_set = calendar_home_set + + super(Principal, self).__init__(client=client, url=url, **kwargs) + if url is None: + if self.client is None: + raise ValueError("Unexpected value None for self.client") + + self.url = self.client.url + cup = self.get_property(dav.CurrentUserPrincipal()) + + if cup is None: + log.warning("calendar server lacking a feature:") + log.warning("current-user-principal property not found") + log.warning("assuming %s is the principal URL" % self.client.url) + + self.url = self.client.url.join(URL.objectify(cup)) + + def make_calendar( + self, + name: Optional[str] = None, + cal_id: Optional[str] = None, + supported_calendar_component_set: Optional[Any] = None, + method=None, + ) -> "Calendar": + """ + Convenience method, bypasses the self.calendar_home_set object. + See CalendarSet.make_calendar for details. + """ + return self.calendar_home_set.make_calendar( + name, + cal_id, + supported_calendar_component_set=supported_calendar_component_set, + method=method, + ) + + def calendar( + self, + name: Optional[str] = None, + cal_id: Optional[str] = None, + cal_url: Optional[str] = None, + ) -> "Calendar": + """ + The calendar method will return a calendar object. + It will not initiate any communication with the server. + """ + if not cal_url: + return self.calendar_home_set.calendar(name, cal_id) + else: + if self.client is None: + raise ValueError("Unexpected value None for self.client") + + return Calendar(self.client, url=self.client.url.join(cal_url)) + + def get_vcal_address(self) -> "vCalAddress": + """ + Returns the principal, as an icalendar.vCalAddress object. + """ + from icalendar import vCalAddress, vText + + cn = self.get_display_name() + ids = self.calendar_user_address_set() + cutype = self.get_property(cdav.CalendarUserType()) + ret = vCalAddress(ids[0]) + ret.params["cn"] = vText(cn) + ret.params["cutype"] = vText(cutype) + return ret + + @property + def calendar_home_set(self): + if not self._calendar_home_set: + calendar_home_set_url = self.get_property(cdav.CalendarHomeSet()) + ## owncloud returns /remote.php/dav/calendars/tobixen@e.email/ + ## in that case the @ should be quoted. Perhaps other + ## implementations return already quoted URLs. Hacky workaround: + if ( + calendar_home_set_url is not None + and "@" in calendar_home_set_url + and "://" not in calendar_home_set_url + ): + calendar_home_set_url = quote(calendar_home_set_url) + self.calendar_home_set = calendar_home_set_url + return self._calendar_home_set + + @calendar_home_set.setter + def calendar_home_set(self, url) -> None: + if isinstance(url, CalendarSet): + self._calendar_home_set = url + return + sanitized_url = URL.objectify(url) + ## TODO: sanitized_url should never be None, this needs more + ## research. added here as it solves real-world issues, ref + ## https://github.com/python-caldav/caldav/pull/56 + if sanitized_url is not None: + if ( + sanitized_url.hostname + and sanitized_url.hostname != self.client.url.hostname + ): + # icloud (and others?) having a load balanced system, + # where each principal resides on one named host + ## TODO: + ## Here be dragons. sanitized_url will be the root + ## of all future objects derived from client. Changing + ## the client.url root by doing a principal.calendars() + ## is an unacceptable side effect and may be a cause of + ## incompatibilities with icloud. Do more research! + self.client.url = sanitized_url + self._calendar_home_set = CalendarSet( + self.client, self.client.url.join(sanitized_url) + ) + + def calendars(self) -> List["Calendar"]: + """ + Return the principal's calendars. + """ + return self.calendar_home_set.calendars() + + def freebusy_request(self, dtstart, dtend, attendees): + """Sends a freebusy-request for some attendee to the server + as per RFC6638. + """ + + freebusy_ical = icalendar.Calendar() + freebusy_ical.add("prodid", "-//tobixen/python-caldav//EN") + freebusy_ical.add("version", "2.0") + freebusy_ical.add("method", "REQUEST") + uid = uuid.uuid1() + freebusy_comp = icalendar.FreeBusy() + freebusy_comp.add("uid", uid) + freebusy_comp.add("dtstamp", datetime.now()) + freebusy_comp.add("dtstart", dtstart) + freebusy_comp.add("dtend", dtend) + freebusy_ical.add_component(freebusy_comp) + outbox = self.schedule_outbox() + caldavobj = FreeBusy(data=freebusy_ical, parent=outbox) + caldavobj.add_organizer() + for attendee in attendees: + caldavobj.add_attendee(attendee, no_default_parameters=True) + + response = self.client.post( + outbox.url, + caldavobj.data, + headers={"Content-Type": "text/calendar; charset=utf-8"}, + ) + return response.find_objects_and_props() + + def calendar_user_address_set(self) -> List[Optional[str]]: + """ + defined in RFC6638 + """ + _addresses: Optional[_Element] = self.get_property( + cdav.CalendarUserAddressSet(), parse_props=False + ) + + if _addresses is None: + raise error.NotFoundError("No calendar user addresses given from server") + + assert not [x for x in _addresses if x.tag != dav.Href().tag] + addresses = list(_addresses) + ## possibly the preferred attribute is iCloud-specific. + ## TODO: do more research on that + addresses.sort(key=lambda x: -int(x.get("preferred", 0))) + return [x.text for x in addresses] + + def schedule_inbox(self) -> "ScheduleInbox": + """ + Returns the schedule inbox, as defined in RFC6638 + """ + return ScheduleInbox(principal=self) + + def schedule_outbox(self) -> "ScheduleOutbox": + """ + Returns the schedule outbox, as defined in RFC6638 + """ + return ScheduleOutbox(principal=self) + + +class Calendar(DAVObject): + """ + The `Calendar` object is used to represent a calendar collection. + Refer to the RFC for details: + https://tools.ietf.org/html/rfc4791#section-5.3.1 + """ + + def _create( + self, name=None, id=None, supported_calendar_component_set=None, method=None + ) -> None: + """ + Create a new calendar with display name `name` in `parent`. + """ + if id is None: + id = str(uuid.uuid1()) + self.id = id + + if method is None: + if self.client: + supported = self.client.features.is_supported( + "create-calendar", return_type=dict + ) + if supported["support"] not in ("full", "fragile", "quirk"): + raise error.MkcalendarError( + "Creation of calendars (allegedly) not supported on this server" + ) + if ( + supported["support"] == "quirk" + and supported["behaviour"] == "mkcol-required" + ): + method = "mkcol" + else: + method = "mkcalendar" + else: + method = "mkcalendar" + + path = self.parent.url.join(id + "/") + self.url = path + + # TODO: mkcalendar seems to ignore the body on most servers? + # at least the name doesn't get set this way. + # zimbra gives 500 (!) if body is omitted ... + + prop = dav.Prop() + if name: + display_name = dav.DisplayName(name) + prop += [ + display_name, + ] + if supported_calendar_component_set: + sccs = cdav.SupportedCalendarComponentSet() + for scc in supported_calendar_component_set: + sccs += cdav.Comp(scc) + prop += sccs + if method == "mkcol": + from caldav.lib.debug import printxml + + prop += dav.ResourceType() + [dav.Collection(), cdav.Calendar()] + + set = dav.Set() + prop + + mkcol = (dav.Mkcol() if method == "mkcol" else cdav.Mkcalendar()) + set + + r = self._query( + root=mkcol, query_method=method, url=path, expected_return_value=201 + ) + + # COMPATIBILITY ISSUE + # name should already be set, but we've seen caldav servers failing + # on setting the DisplayName on calendar creation + # (DAViCal, Zimbra, ...). Doing an attempt on explicitly setting the + # display name using PROPPATCH. + if name: + try: + self.set_properties([display_name]) + except Exception as e: + ## TODO: investigate. Those asserts break. + try: + current_display_name = self.get_display_name() + error.assert_(current_display_name == name) + except: + log.warning( + "calendar server does not support display name on calendar? Ignoring", + exc_info=True, + ) + + def delete(self): + ## TODO: remove quirk handling from the functional tests + ## TODO: this needs test code + quirk_info = self.client.features.is_supported("delete-calendar", dict) + wipe = quirk_info["support"] in ("unsupported", "fragile") + if quirk_info["support"] == "fragile": + ## Do some retries on deleting the calendar + for x in range(0, 20): + try: + super().delete() + except error.DeleteError: + pass + try: + x = self.events() + sleep(0.3) + except error.NotFoundError: + wipe = False + break + + if wipe: + for x in self.search(): + x.delete() + else: + super().delete() + + def get_supported_components(self) -> List[Any]: + """ + returns a list of component types supported by the calendar, in + string format (typically ['VJOURNAL', 'VTODO', 'VEVENT']) + """ + if self.url is None: + raise ValueError("Unexpected value None for self.url") + + props = [cdav.SupportedCalendarComponentSet()] + response = self.get_properties(props, parse_response_xml=False) + response_list = response.find_objects_and_props() + prop = response_list[unquote(self.url.path)][ + cdav.SupportedCalendarComponentSet().tag + ] + return [supported.get("name") for supported in prop] + + def save_with_invites(self, ical: str, attendees, **attendeeoptions) -> None: + """ + sends a schedule request to the server. Equivalent with save_event, save_todo, etc, + but the attendees will be added to the ical object before sending it to the server. + """ + ## TODO: consolidate together with save_* + obj = self._calendar_comp_class_by_data(ical)(data=ical, client=self.client) + obj.parent = self + obj.add_organizer() + for attendee in attendees: + obj.add_attendee(attendee, **attendeeoptions) + obj.id = obj.icalendar_instance.walk("vevent")[0]["uid"] + obj.save() + return obj + + def _use_or_create_ics(self, ical, objtype, **ical_data): + if ical_data or ( + (isinstance(ical, str) or isinstance(ical, bytes)) + and b"BEGIN:VCALENDAR" not in to_wire(ical) + ): + ## TODO: the ical_fragment code is not much tested + if ical and "ical_fragment" not in ical_data: + ical_data["ical_fragment"] = ical + return vcal.create_ical(objtype=objtype, **ical_data) + return ical + + def save_object( + self, + ## TODO: this should be made optional. The class may be given in the ical object. + ## TODO: also, accept a string. + objclass: Type[DAVObject], + ## TODO: ical may also be a vobject or icalendar instance + ical: Optional[str] = None, + no_overwrite: bool = False, + no_create: bool = False, + **ical_data, + ) -> "CalendarResourceObject": + """Add a new event to the calendar, with the given ical. + + Args: + objclass: Event, Journal or Todo + ical: ical object (text, icalendar or vobject instance) + no_overwrite: existing calendar objects should not be overwritten + no_create: don't create a new object, existing calendar objects should be updated + dt_start: properties to be inserted into the icalendar object + , dt_end: properties to be inserted into the icalendar object + summary: properties to be inserted into the icalendar object + alarm_trigger: when given, one alarm will be added + alarm_action: when given, one alarm will be added + alarm_attach: when given, one alarm will be added + + Note that the list of parameters going into the icalendar + object and alamrs is not complete. Refer to the RFC or the + icalendar library for a full list of properties. + """ + o = objclass( + self.client, + data=self._use_or_create_ics( + ical, objtype=f"V{objclass.__name__.upper()}", **ical_data + ), + parent=self, + ) + o = o.save(no_overwrite=no_overwrite, no_create=no_create) + ## TODO: Saving nothing is currently giving an object with None as URL. + ## This should probably be changed in some future version to raise an error + ## See also CalendarObjectResource.save() + if o.url is not None: + o._handle_reverse_relations(fix=True) + return o + + ## TODO: maybe we should deprecate those three + def save_event(self, *largs, **kwargs) -> "Event": + """ + Returns ``self.save_object(Event, ...)`` - see :class:`save_object` + """ + return self.save_object(Event, *largs, **kwargs) + + def save_todo(self, *largs, **kwargs) -> "Todo": + """ + Returns ``self.save_object(Todo, ...)`` - so see :class:`save_object` + """ + return self.save_object(Todo, *largs, **kwargs) + + def save_journal(self, *largs, **kwargs) -> "Journal": + """ + Returns ``self.save_object(Journal, ...)`` - so see :class:`save_object` + """ + return self.save_object(Journal, *largs, **kwargs) + + ## legacy aliases + ## TODO: should be deprecated + + ## TODO: think more through this - is `save_foo` better than `add_foo`? + ## `save_foo` should not be used for updating existing content on the + ## calendar! + add_object = save_object + add_event = save_event + add_todo = save_todo + add_journal = save_journal + + def save(self, method=None): + """ + The save method for a calendar is only used to create it, for now. + We know we have to create it when we don't have a url. + + Returns: + * self + """ + if self.url is None: + self._create( + id=self.id, name=self.name, method=method, **self.extra_init_options + ) + return self + + # def data2object_class + + def _multiget( + self, event_urls: Iterable[URL], raise_notfound: bool = False + ) -> Iterable[str]: + """ + get multiple events' data. + TODO: Does it overlap the _request_report_build_resultlist method + """ + if self.url is None: + raise ValueError("Unexpected value None for self.url") + + rv = [] + prop = dav.Prop() + cdav.CalendarData() + root = ( + cdav.CalendarMultiGet() + + prop + + [dav.Href(value=u.path) for u in event_urls] + ) + response = self._query(root, 1, "report") + results = response.expand_simple_props([cdav.CalendarData()]) + if raise_notfound: + for href in response.statuses: + status = response.statuses[href] + if status and "404" in status: + raise error.NotFoundError(f"Status {status} in {href}") + for r in results: + yield (r, results[r][cdav.CalendarData.tag]) + + ## Replace the last lines with + def multiget( + self, event_urls: Iterable[URL], raise_notfound: bool = False + ) -> Iterable[_CC]: + """ + get multiple events' data + TODO: Does it overlap the _request_report_build_resultlist method? + @author mtorange@gmail.com (refactored by Tobias) + """ + results = self._multiget(event_urls, raise_notfound=raise_notfound) + for url, data in results: + yield self._calendar_comp_class_by_data(data)( + self.client, + url=self.url.join(url), + data=data, + parent=self, + ) + + def calendar_multiget(self, *largs, **kwargs): + """ + get multiple events' data + @author mtorange@gmail.com + (refactored by Tobias) + This is for backward compatibility. It may be removed in 3.0 or later release. + """ + return list(self.multiget(*largs, **kwargs)) + + def date_search( + self, + start: datetime, + end: Optional[datetime] = None, + compfilter: None = "VEVENT", + expand: Union[bool, Literal["maybe"]] = "maybe", + verify_expand: bool = False, + ) -> Sequence["CalendarObjectResource"]: + # type (TimeStamp, TimeStamp, str, str) -> CalendarObjectResource + """Deprecated. Use self.search() instead. + + Search events by date in the calendar. + + Args + start : defaults to datetime.today(). + end : same as above. + compfilter : defaults to events only. Set to None to fetch all calendar components. + expand : should recurrent events be expanded? (to preserve backward-compatibility the default "maybe" will be changed into True unless the date_search is open-ended) + verify_expand : not in use anymore, but kept for backward compatibility + + Returns: + * [CalendarObjectResource(), ...] + + Recurring events are expanded if they are occurring during the + specified time frame and if an end timestamp is given. + + Note that this is a deprecated method. The `search` method is + nearly equivalent. Differences: default for ``compfilter`` is + to search for all objects, default for ``expand`` is + ``False``, and it has a different default + ``split_expanded=True``. + """ + ## date_search will probably disappear in 3.0 + warnings.warn( + "use `calendar.search rather than `calendar.date_search`", + DeprecationWarning, + stacklevel=2, + ) + + if verify_expand: + logging.warning( + "verify_expand in date_search does not work anymore, as we're doing client side expansion instead" + ) + + ## for backward compatibility - expand should be false + ## in an open-ended date search, otherwise true + if expand == "maybe": + expand = end + + if compfilter == "VEVENT": + comp_class = Event + elif compfilter == "VTODO": + comp_class = Todo + else: + comp_class = None + + objects = self.search( + start=start, + end=end, + comp_class=comp_class, + expand=expand, + split_expanded=False, + ) + + return objects + + ## TODO: this logic has been partly duplicated in calendar_multiget, but + ## the code there is much more readable and condensed than this. + ## Can code below be refactored? + def _request_report_build_resultlist( + self, xml, comp_class=None, props=None, no_calendardata=False + ): + """ + Takes some input XML, does a report query on a calendar object + and returns the resource objects found. + """ + matches = [] + if props is None: + props_ = [cdav.CalendarData()] + else: + props_ = [cdav.CalendarData()] + props + response = self._query(xml, 1, "report") + results = response.expand_simple_props(props_) + for r in results: + pdata = results[r] + if cdav.CalendarData.tag in pdata: + cdata = pdata.pop(cdav.CalendarData.tag) + comp_class_ = ( + self._calendar_comp_class_by_data(cdata) + if comp_class is None + else comp_class + ) + else: + cdata = None + if comp_class_ is None: + ## no CalendarData fetched - which is normal i.e. when doing a sync-token report and only asking for the URLs + comp_class_ = CalendarObjectResource + url = URL(r) + if url.hostname is None: + # Quote when result is not a full URL + url = quote(r) + ## icloud hack - icloud returns the calendar URL as well as the calendar item URLs + if self.url.join(url) == self.url: + continue + matches.append( + comp_class_( + self.client, + url=self.url.join(url), + data=cdata, + parent=self, + props=pdata, + ) + ) + return (response, matches) + + def search( + self, + xml: str = None, + server_expand: bool = False, + split_expanded: bool = True, + sort_reverse: bool = False, + props: Optional[List[cdav.CalendarData]] = None, + filters=None, + post_filter=None, + _hacks=None, + **searchargs, + ) -> List[_CC]: + """Sends a search request towards the server, processes the + results if needed and returns the objects found. + + Refactoring 2025-11: a new class + class:`caldav.search.CalDAVSearcher` has been made, and + this method is sort of a wrapper for + CalDAVSearcher.search, ensuring backward + compatibility. The documentation may be slightly overlapping. + + I believe that for simple tasks, this method will be easier to + use than the new interface, hence there are no plans for the + foreseeable future to deprecate it. This search method will + continue working as it has been doing before for all + foreseeable future. I believe that for simple tasks, this + method will be easier to use than to construct a + CalDAVSearcher object and do searches from there. The + refactoring was made necessary because the parameter list to + `search` was becoming unmanagable. Advanced searches should + be done via the new interface. + + Caveat: The searching is done on the server side, the RFC is + not very crystal clear on many of the corner cases, and + servers often behave differently when presented with a search + request. There is planned work to work around server + incompatibilities on the client side, but as for now + complicated searches will give different results on different + servers. + + ``todo`` - searches explicitly for todo. Unless + ``include_completed`` is specified, there is some special + logic ensuring only pending tasks is returned. + + There is corresponding ``event`` and ``journal`` bools to + specify that the search should be only for events or journals. + When neither are set, one should expect to get all objects + returned - but quite some calendar servers will return + nothing. This will be solved client-side in the future, as + for 2.0 it's recommended to search separately for tasks, + events and journals to ensure consistent behaviour across + different calendar servers and providers. + + ``sort_keys`` refers to (case-insensitive) properties in the + icalendar object, ``sort_reverse`` can also be given. The + sorting will be done client-side. + + Use ``start`` and ``end`` for time-range searches. Open-ended + searches are supported (i.e. "everything in the future"), but + it's recommended to use closed ranges (i.e. have an "event + horizon" of a year and ask for "everything from now and one + year ahead") and get the data expanded. + + With the boolean ``expand`` set, you don't have to think too + much about recurrences - they will be expanded, and with the + (default) ``split_expanded`` set, each recurrence will be + returned as a separate list object (otherwise all recurrences + will be put into one ``VCALENDAR`` and returned as one + ``Event``). This makes it safe to use the ``event.component`` + property. The non-expanded resultset may include events where + the timespan doesn't match the date interval you searched for, + as well as items with multiple components ("special" + recurrences), meaning you may need logic on the client side to + handle the recurrences. *Only time range searches over closed + time intervals may be expanded*. + + As for 2.0, the expand-logic is by default done on the + client-side, for consistent results across various server + incompabilities. However, you may force server-side expansion + by setting ``server_expand=True`` + + Text attribute search parameters can be given to query the + "properties" in the calendar data: category, uid, summary, + comment, description, location, status. According to the RFC, + a substring search should be done. + + You may use no_category, no_summary, etc to search for objects + that are missing those attributes. + + Negated text matches are not supported yet. + + For power-users, those parameters are also supported: + + * ``xml`` - use this search query, and ignore other filter parameters + * ``comp_class`` - alternative to the ``event``, ``todo`` or ``journal`` booleans described above. + * ``filters`` - other kind of filters (in lxml tree format) + + """ + ## Late import to avoid cyclic imports + from .search import CalDAVSearcher + + ## This is basically a wrapper for CalDAVSearcher.search + ## The logic below will massage the parameters in ``searchargs`` + ## and put them into the CalDAVSearcher object. + + if searchargs.get("expand", True) not in (True, False): + warnings.warn( + "in cal.search(), expand should be a bool", + DeprecationWarning, + stacklevel=2, + ) + if searchargs["expand"] == "client": + searchargs["expand"] = True + if searchargs["expand"] == "server": + server_expand = True + searchargs["expand"] = False + + ## Transfer all the arguments to CalDAVSearcher + my_searcher = CalDAVSearcher() + for key in searchargs: + assert key[0] != "_" ## not allowed + alias = key + if key == "class_": ## because class is a reserved word + alias = "class" + if key == "no_category": + alias = "no_categories" + if key == "no_class_": + alias = "no_class" + if key == "sort_keys": + if isinstance(searchargs["sort_keys"], str): + searchargs["sort_keys"] = [searchargs["sort_keys"]] + for sortkey in searchargs["sort_keys"]: + my_searcher.add_sort_key(sortkey, sort_reverse) + continue + elif key == "comp_class" or key in my_searcher.__dataclass_fields__: + setattr(my_searcher, key, searchargs[key]) + continue + elif alias.startswith("no_"): + my_searcher.add_property_filter( + alias[3:], searchargs[key], operator="undef" + ) + else: + my_searcher.add_property_filter(alias, searchargs[key]) + + if not xml and filters: + xml = filters + + return my_searcher.search( + self, server_expand, split_expanded, props, xml, post_filter, _hacks + ) + + def freebusy_request(self, start: datetime, end: datetime) -> "FreeBusy": + """ + Search the calendar, but return only the free/busy information. + + Args: + start : defaults to datetime.today(). + end : same as above. + + Returns: + [FreeBusy(), ...] + """ + + root = cdav.FreeBusyQuery() + [cdav.TimeRange(start, end)] + response = self._query(root, 1, "report") + return FreeBusy(self, response.raw) + + def todos( + self, + sort_keys: Sequence[str] = ("due", "priority"), + include_completed: bool = False, + sort_key: Optional[str] = None, + ) -> List["Todo"]: + """ + Fetches a list of todo events (this is a wrapper around search). + + Args: + sort_keys: use this field in the VTODO for sorting (iterable of lower case string, i.e. ('priority','due')). + include_completed: boolean - by default, only pending tasks are listed + sort_key: DEPRECATED, for backwards compatibility with version 0.4. + """ + if sort_key: + sort_keys = (sort_key,) + + return self.search( + todo=True, include_completed=include_completed, sort_keys=sort_keys + ) + + def _calendar_comp_class_by_data(self, data): + """ + takes some data, either as icalendar text or icalender object (TODO: + consider vobject) and returns the appropriate + CalendarResourceObject child class. + """ + if data is None: + ## no data received - we'd need to load it before we can know what + ## class it really is. Assign the base class as for now. + return CalendarObjectResource + if hasattr(data, "split"): + for line in data.split("\n"): + line = line.strip() + if line == "BEGIN:VEVENT": + return Event + if line == "BEGIN:VTODO": + return Todo + if line == "BEGIN:VJOURNAL": + return Journal + if line == "BEGIN:VFREEBUSY": + return FreeBusy + elif hasattr(data, "subcomponents"): + if not len(data.subcomponents): + return CalendarObjectResource + + ical2caldav = { + icalendar.Event: Event, + icalendar.Todo: Todo, + icalendar.Journal: Journal, + icalendar.FreeBusy: FreeBusy, + } + for sc in data.subcomponents: + if sc.__class__ in ical2caldav: + return ical2caldav[sc.__class__] + return CalendarObjectResource + + def event_by_url(self, href, data: Optional[Any] = None) -> "Event": + """ + Returns the event with the given URL. + """ + return Event(url=href, data=data, parent=self).load() + + def object_by_uid( + self, + uid: str, + comp_filter: Optional[cdav.CompFilter] = None, + comp_class: Optional["CalendarObjectResource"] = None, + ) -> "Event": + """ + Get one event from the calendar. + + Args: + uid: the event uid + comp_class: filter by component type (Event, Todo, Journal) + comp_filter: for backward compatibility. Don't use! + + Returns: + Event() or None + """ + ## late import to avoid cyclic dependencies + from .search import CalDAVSearcher + + ## 2025-11: some logic validating the comp_filter and + ## comp_class has been removed, and replaced with the + ## recommendation not to use comp_filter. We're still using + ## comp_filter internally, but it's OK, it doesn't need to be + ## validated. + + ## Lots of old logic has been removed, the new search logic + ## can do the things for us: + searcher = CalDAVSearcher(comp_class=comp_class) + ## Default is substring + searcher.add_property_filter("uid", uid, "==") + items_found = searcher.search( + self, xml=comp_filter, _hacks="insist", post_filter=True + ) + + if not items_found: + raise error.NotFoundError("%s not found on server" % uid) + error.assert_(len(items_found) == 1) + return items_found[0] + + def todo_by_uid(self, uid: str) -> "CalendarObjectResource": + """ + Returns the task with the given uid (wraps around :class:`object_by_uid`) + """ + return self.object_by_uid(uid, comp_filter=cdav.CompFilter("VTODO")) + + def event_by_uid(self, uid: str) -> "CalendarObjectResource": + """ + Returns the event with the given uid (wraps around :class:`object_by_uid`) + """ + return self.object_by_uid(uid, comp_filter=cdav.CompFilter("VEVENT")) + + def journal_by_uid(self, uid: str) -> "CalendarObjectResource": + """ + Returns the journal with the given uid (wraps around :class:`object_by_uid`) + """ + return self.object_by_uid(uid, comp_filter=cdav.CompFilter("VJOURNAL")) + + # alias for backward compatibility + event = event_by_uid + + def events(self) -> List["Event"]: + """ + List all events from the calendar. + + Returns: + * [Event(), ...] + """ + return self.search(comp_class=Event) + + def _generate_fake_sync_token(self, objects: List["CalendarObjectResource"]) -> str: + """ + Generate a fake sync token for servers without sync support. + Uses a hash of all ETags to detect changes. + + Args: + objects: List of calendar objects to generate token from + + Returns: + A fake sync token string + """ + import hashlib + + etags = [] + for obj in objects: + if hasattr(obj, "props") and dav.GetEtag.tag in obj.props: + etags.append(str(obj.props[dav.GetEtag.tag])) + elif hasattr(obj, "url"): + ## If no etag, use URL as fallback identifier + etags.append(str(obj.url.canonical())) + etags.sort() ## Consistent ordering + combined = "|".join(etags) + hash_value = hashlib.md5(combined.encode()).hexdigest() + return f"fake-{hash_value}" + + def objects_by_sync_token( + self, + sync_token: Optional[Any] = None, + load_objects: bool = False, + disable_fallback: bool = False, + ) -> "SynchronizableCalendarObjectCollection": + """objects_by_sync_token aka objects + + Do a sync-collection report, ref RFC 6578 and + https://github.com/python-caldav/caldav/issues/87 + + This method will return all objects in the calendar if no + sync_token is passed (the method should then be referred to as + "objects"), or if the sync_token is unknown to the server. If + a sync-token known by the server is passed, it will return + objects that are added, deleted or modified since last time + the sync-token was set. + + If load_objects is set to True, the objects will be loaded - + otherwise empty CalendarObjectResource objects will be returned. + + This method will return a SynchronizableCalendarObjectCollection object, which is + an iterable. + + This method transparently falls back to retrieving all objects if the server + doesn't support sync tokens. The fallback behavior is identical from the user's + perspective, but less efficient as it transfers the entire calendar on each sync. + + If disable_fallback is set to True, the method will raise an exception instead + of falling back to retrieving all objects. This is useful for testing whether + the server truly supports sync tokens. + """ + ## Check if we should attempt to use sync tokens + ## (either server supports them, or we haven't checked yet, or this is a fake token) + use_sync_token = True + sync_support = self.client.features.is_supported("sync-token", return_type=dict) + if sync_support.get("support") == "unsupported": + if disable_fallback: + raise error.ReportError("Sync tokens are not supported by the server") + use_sync_token = False + ## If sync_token looks like a fake token, don't try real sync-collection + if ( + sync_token + and isinstance(sync_token, str) + and sync_token.startswith("fake-") + ): + use_sync_token = False + + if use_sync_token: + try: + cmd = dav.SyncCollection() + token = dav.SyncToken(value=sync_token) + level = dav.SyncLevel(value="1") + props = dav.Prop() + dav.GetEtag() + root = cmd + [level, token, props] + (response, objects) = self._request_report_build_resultlist( + root, props=[dav.GetEtag()], no_calendardata=True + ) + ## TODO: look more into this, I think sync_token should be directly available through response object + try: + sync_token = response.sync_token + except: + sync_token = response.tree.findall(".//" + dav.SyncToken.tag)[ + 0 + ].text + + ## this is not quite right - the etag we've fetched can already be outdated + if load_objects: + for obj in objects: + try: + obj.load() + except error.NotFoundError: + ## The object was deleted + pass + return SynchronizableCalendarObjectCollection( + calendar=self, objects=objects, sync_token=sync_token + ) + except (error.ReportError, error.DAVError) as e: + ## Server doesn't support sync tokens or the sync-collection REPORT failed + if disable_fallback: + raise + log.info( + f"Sync-collection REPORT failed ({e}), falling back to full retrieval" + ) + ## Fall through to fallback implementation + + ## FALLBACK: Server doesn't support sync tokens + ## Retrieve all objects and emulate sync token behavior + log.debug("Using fallback sync mechanism (retrieving all objects)") + + ## Use search() to get all objects. search() will include CalendarData by default. + ## We can't avoid this in the fallback mechanism without significant refactoring. + all_objects = list(self.search()) + + ## Load objects if requested (objects may already have data from search) + if load_objects: + for obj in all_objects: + ## Only load if not already loaded + if not hasattr(obj, "_data") or obj._data is None: + try: + obj.load() + except error.NotFoundError: + pass + + ## Fetch ETags for all objects if not already present + ## ETags are crucial for detecting changes in the fallback mechanism + if all_objects and ( + not hasattr(all_objects[0], "props") + or dav.GetEtag.tag not in all_objects[0].props + ): + ## Use PROPFIND to fetch ETags for all objects + try: + ## Do a depth-1 PROPFIND on the calendar to get all ETags + response = self._query_properties([dav.GetEtag()], depth=1) + etag_props = response.expand_simple_props([dav.GetEtag()]) + + ## Map ETags to objects by URL (using string keys for reliable comparison) + url_to_obj = {str(obj.url.canonical()): obj for obj in all_objects} + log.debug(f"Fallback: Fetching ETags for {len(url_to_obj)} objects") + for url_str, props in etag_props.items(): + canonical_url_str = str(self.url.join(url_str).canonical()) + if canonical_url_str in url_to_obj: + if not hasattr(url_to_obj[canonical_url_str], "props"): + url_to_obj[canonical_url_str].props = {} + url_to_obj[canonical_url_str].props.update(props) + log.debug(f"Fallback: Added ETag to {canonical_url_str}") + except Exception as e: + ## If fetching ETags fails, we'll fall back to URL-based tokens + ## which can't detect content changes, only additions/deletions + log.debug(f"Failed to fetch ETags for fallback sync: {e}") + pass + + ## Generate a fake sync token based on current state + fake_sync_token = self._generate_fake_sync_token(all_objects) + + ## If a sync_token was provided, check if anything has changed + if ( + sync_token + and isinstance(sync_token, str) + and sync_token.startswith("fake-") + ): + ## Compare the provided token with the new token + if sync_token == fake_sync_token: + ## Nothing has changed, return empty collection + return SynchronizableCalendarObjectCollection( + calendar=self, objects=[], sync_token=fake_sync_token + ) + ## If tokens differ, return all objects (emulating a full sync) + ## In a real implementation, we'd return only changed objects, + ## but that requires storing previous state which we don't have + + return SynchronizableCalendarObjectCollection( + calendar=self, objects=all_objects, sync_token=fake_sync_token + ) + + objects = objects_by_sync_token + + def journals(self) -> List["Journal"]: + """ + List all journals from the calendar. + + Returns: + * [Journal(), ...] + """ + return self.search(comp_class=Journal) + + +class ScheduleMailbox(Calendar): + """ + RFC6638 defines an inbox and an outbox for handling event scheduling. + + TODO: As ScheduleMailboxes works a bit like calendars, I've chosen + to inheritate the Calendar class, but this is a bit incorrect, a + ScheduleMailbox is a collection, but not really a calendar. We + should create a common base class for ScheduleMailbox and Calendar + eventually. + """ + + def __init__( + self, + client: Optional["DAVClient"] = None, + principal: Optional[Principal] = None, + url: Union[str, ParseResult, SplitResult, URL, None] = None, + ) -> None: + """ + Will locate the mbox if no url is given + """ + super(ScheduleMailbox, self).__init__(client=client, url=url) + self._items = None + if not client and principal: + self.client = principal.client + if not principal and client: + if self.client is None: + raise ValueError("Unexpected value None for self.client") + + principal = self.client.principal + if url is not None: + if client is None: + raise ValueError("Unexpected value None for client") + + self.url = client.url.join(URL.objectify(url)) + else: + if principal is None: + raise ValueError("Unexpected value None for principal") + + if self.client is None: + raise ValueError("Unexpected value None for self.client") + + self.url = principal.url + try: + # we ignore the type here as this is defined in sub-classes only; require more changes to + # properly fix in a future revision + self.url = self.client.url.join(URL(self.get_property(self.findprop()))) # type: ignore + except: + logging.error("something bad happened", exc_info=True) + error.assert_(self.client.check_scheduling_support()) + self.url = None + # we ignore the type here as this is defined in sub-classes only; require more changes to + # properly fix in a future revision + raise error.NotFoundError( + "principal has no %s. %s" + % (str(self.findprop()), error.ERR_FRAGMENT) # type: ignore + ) + + def get_items(self): + """ + TODO: work in progress + TODO: perhaps this belongs to the super class? + """ + if not self._items: + try: + self._items = self.objects(load_objects=True) + except: + logging.debug( + "caldav server does not seem to support a sync-token REPORT query on a scheduling mailbox" + ) + error.assert_("google" in str(self.url)) + self._items = [ + CalendarObjectResource(url=x[0], client=self.client) + for x in self.children() + ] + for x in self._items: + x.load() + else: + try: + self._items.sync() + except: + self._items = [ + CalendarObjectResource(url=x[0], client=self.client) + for x in self.children() + ] + for x in self._items: + x.load() + return self._items + + ## TODO: work in progress + + +# def get_invites(): +# for item in self.get_items(): +# if item.vobject_instance.vevent. + + +class ScheduleInbox(ScheduleMailbox): + findprop = cdav.ScheduleInboxURL + + +class ScheduleOutbox(ScheduleMailbox): + findprop = cdav.ScheduleOutboxURL + + +class SynchronizableCalendarObjectCollection: + """ + This class may hold a cached snapshot of a calendar, and changes + in the calendar can easily be copied over through the sync method. + + To create a SynchronizableCalendarObjectCollection object, use + calendar.objects(load_objects=True) + """ + + def __init__(self, calendar, objects, sync_token) -> None: + self.calendar = calendar + self.sync_token = sync_token + self.objects = objects + self._objects_by_url = None + + def __iter__(self) -> Iterator[Any]: + return self.objects.__iter__() + + def __len__(self) -> int: + return len(self.objects) + + def objects_by_url(self): + """ + returns a dict of the contents of the SynchronizableCalendarObjectCollection, URLs -> objects. + """ + if self._objects_by_url is None: + self._objects_by_url = {} + for obj in self: + self._objects_by_url[obj.url.canonical()] = obj + return self._objects_by_url + + def sync(self) -> Tuple[Any, Any]: + """ + This method will contact the caldav server, + request all changes from it, and sync up the collection. + + This method transparently falls back to comparing full calendar state + if the server doesn't support sync tokens. + """ + updated_objs = [] + deleted_objs = [] + + ## Check if we're using fake sync tokens (fallback mode) + is_fake_token = isinstance(self.sync_token, str) and self.sync_token.startswith( + "fake-" + ) + + if not is_fake_token: + ## Try to use real sync tokens + try: + updates = self.calendar.objects_by_sync_token( + self.sync_token, load_objects=False + ) + + ## If we got a fake token back, we've fallen back + if isinstance( + updates.sync_token, str + ) and updates.sync_token.startswith("fake-"): + is_fake_token = True + else: + ## Real sync token path + obu = self.objects_by_url() + for obj in updates: + obj.url = obj.url.canonical() + if ( + obj.url in obu + and dav.GetEtag.tag in obu[obj.url].props + and dav.GetEtag.tag in obj.props + ): + if ( + obu[obj.url].props[dav.GetEtag.tag] + == obj.props[dav.GetEtag.tag] + ): + continue + obu[obj.url] = obj + try: + obj.load() + updated_objs.append(obj) + except error.NotFoundError: + deleted_objs.append(obj) + obu.pop(obj.url) + + self.objects = list(obu.values()) + self._objects_by_url = None ## Invalidate cache + self.sync_token = updates.sync_token + return (updated_objs, deleted_objs) + except (error.ReportError, error.DAVError): + ## Sync failed, fall back + is_fake_token = True + + if is_fake_token: + ## FALLBACK: Compare full calendar state + log.debug("Using fallback sync mechanism (comparing all objects)") + + ## Retrieve all current objects from server + current_objects = list(self.calendar.search()) + + ## Load them + for obj in current_objects: + try: + obj.load() + except error.NotFoundError: + pass + + ## Build URL-indexed dicts for comparison + current_by_url = {obj.url.canonical(): obj for obj in current_objects} + old_by_url = self.objects_by_url() + + ## Find updated and new objects + for url, obj in current_by_url.items(): + if url in old_by_url: + ## Object exists in both - check if modified + ## Compare data if available, otherwise consider it unchanged + old_data = ( + old_by_url[url].data + if hasattr(old_by_url[url], "data") + else None + ) + new_data = obj.data if hasattr(obj, "data") else None + if old_data != new_data and new_data is not None: + updated_objs.append(obj) + else: + ## New object + updated_objs.append(obj) + + ## Find deleted objects + for url in old_by_url: + if url not in current_by_url: + deleted_objs.append(old_by_url[url]) + + ## Update internal state + self.objects = list(current_by_url.values()) + self._objects_by_url = None ## Invalidate cache + self.sync_token = self.calendar._generate_fake_sync_token(self.objects) + + return (updated_objs, deleted_objs) diff --git a/.venv/lib/python3.9/site-packages/caldav/compatibility_hints.py b/.venv/lib/python3.9/site-packages/caldav/compatibility_hints.py new file mode 100644 index 0000000..f02f3a1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/compatibility_hints.py @@ -0,0 +1,1175 @@ +# fmt: off +""" +This file serves as a database of different compatibility issues we've +encountered while working on the caldav library, and descriptions on +how the well-known servers behave. + +TODO: it should probably be split with the "feature definitions", +"server implementation details" and "feature database logic" in three separate files. +""" +import copy + +## NEW STYLE +## (we're gradually moving stuff from the good old +## "incompatibility_description" below over to +## "compatibility_features") + +class FeatureSet: + """Work in progress ... TODO: write a better class description. + + This class holds the description of different behaviour observed in + a class constant. + + An object of this class describes the feature set of a server. + + TODO: use enums? TODO: describe the different types TODO: think more through the different types, consolidate? + type -> "client-feature", "client-hints", "server-peculiarity", "tests-behaviour", "server-observation", "server-feature" (last is default) + support -> "full" (default), "unsupported", "fragile", "quirk", "broken", "ungraceful" + + unsupported means that attempts to use the feature will be silently ignored (this may actually be the worst option, as it may cause data loss). quirk means that the feature is suppored, but special handling needs to be done towards the server. fragile means that it sometimes works and sometimes not - either it's arbitrary, or we didn't spend enough time doing research into the patterns. My idea behind broken was that the server should do completely unexpected things. Probably a lot of things classified as "unsupported" today should rather be classified as "broken". Some AI-generated code is using"broken". TODO: look through and clean up. "ungraceful" means the server will throw some error (this may indeed be the most graceful, as the client may catch the error and handle it in the best possible way). + + types: + * client-feature means the client is supposed to do special things (like, rate-limiting). While the need for rate-limiting may be set by the server, it may not be possible to reliably establish it by probling the server, and the value may differ for different clients. + * server-peculiarity - weird behaviour detected at the server side, behaviour that is too odd to be described as "missing support for a feature". Example: there is some cache working, causing a delay from some object is sent to the server and until it can be retrieved. The difference between an "unsupported server-feature" and a "server-peculiarity" may be a bit floating - like, arguably "instant updates" may be considered a feature. + * tests-behaviour - configuration for the tests. Like, it's OK to wipe everyhting from the test calendar, location of test calendar, rate-limiting that only should apply to test runs, etc. + * server-observation - not features, but other facts found about the server + * server-feature - some feature (preferably rooted with a pointer to some specific section of the RFC) + * "support" -> "quirk" if we have a server-peculiarity where it's needed with special care to get the request through. + """ + FEATURES = { + "auto-connect": { + ## Nothing here - everything is under auto-connect.url as for now. + ## Other connection details - like what auth method to use - could also + ## be under the auto-connect umbrella + "type": "client-hints", + }, + "auto-connect.url": { + "description": "Instruction for how to access DAV. I.e. `/remote.php/dav` - see also https://github.com/python-caldav/caldav/issues/463. To be used in the get_davclient method if the URL only contains a domain", + "type": "client-hints", + "extra_keys": { + "basepath": "The path to append to the domain", + "domain": "Domain name may be given through the features - useful for well-known cloud solutions", + "scheme": "The scheme to prepend to the domain. Defaults to https", + ## TODO: in the future, templates for the principal URL, calendar URLs etc may also be added. + } + }, + "get-current-user-principal": { + "description": "Support for RFC5397, current principal extension. Most CalDAV servers have this, but it is an extension to the DAV standard"}, + "get-current-user-principal.has-calendar": { + "type": "server-observation", + "description": "Principal has one or more calendars. Some servers and providers comes with a pre-defined calendar for each user, for other servers a calendar has to be explicitly created (supported means there exists a calendar - it may be because the calendar was already provisioned together with the principal, or it may be because a calendar was created manually, the checks can't see the difference)"}, + "rate-limit": { + "type": "client-feature", + "description": "client (or test code) must not send requests too fast", + "extra_keys": { + "interval": "Rate limiting window, in seconds", + "count": "Max number of requests to send within the interval", + }}, + "search-cache": { + "type": "server-peculiarity", + "description": "The server delivers search results from a cache which is not immediately updated when an object is changed. Hence recent changes may not be reflected in search results", + "extra_keys": { + "delay": "after this number of seconds, we may be reasonably sure that the search results are updated", + } + }, + "tests-cleanup-calendar": { + "type": "tests-behaviour", + "description": "Deleting a calendar does not delete the objects, or perhaps create/delete of calendars does not work at all. For each test run, every calendar resource object should be deleted for every test run", + }, + "create-calendar": { + "description": "RFC4791 says that \"support for MKCALENDAR on the server is only RECOMMENDED and not REQUIRED because some calendar stores only support one calendar per user (or principal), and those are typically pre-created for each account\". Hence a conformant server may opt to not support creating calendars, this is often seen for cloud services (some services allows extra calendars to be made, but not through the CalDAV protocol). (RFC4791 also says that the server MAY support MKCOL in section 8.5.2. I do read it as MKCOL may be used for creating calendars - which is weird, since section 8.5.2 is titled \"external attachments\". We should consider testing this as well)", + }, + "create-calendar.auto": { + "default": { "support": "unsupported" }, + "description": "Accessing a calendar which does not exist automatically creates it", + }, + "create-calendar.set-displayname": { + "description": "It's possible to set the displayname on a calendar upon creation" + }, + "delete-calendar": { + "description": "RFC4791 says nothing about deletion of calendars, so the server implementation is free to choose weather this should be supported or not. Section 3.2.3.2 in RFC 6638 says that if a calendar is deleted, all the calendarobjectresources on the calendar should also be deleted - but it's a bit unclear if this only applies to scheduling objects or not. Some calendar servers moves the object to a trashcan rather than deleting it" + }, + "delete-calendar.free-namespace": { + "description": "The delete operations clears the namespace, so that another calendar with the same ID/name can be created" + }, + "http": { }, + "http.multiplexing": { + "description": "chulka/baikal:nginx is having Problems with using HTTP/2 with multiplexing, ref https://github.com/python-caldav/caldav/issues/564. I haven't (yet) been able to reproduce this locally, so no check for this yet. We'll define it as fragile in the radicale config as for now" + }, + "save-load": { + "description": "it's possible to save and load objects to the calendar" + }, + "save-load.event": {"description": "it's possible to save and load events to the calendar"}, + "save-load.event.recurrences": {"description": "it's possible to save and load recurring events to the calendar - events with an RRULE property set, including recurrence sets"}, + "save-load.event.recurrences.count": {"description": "The server will receive and store a recurring event with a count set in the RRULE"}, + "save-load.todo": {"description": "it's possible to save and load tasks to the calendar"}, + "save-load.todo.recurrences": {"description": "it's possible to save and load recurring tasks to the calendar"}, + "save-load.todo.recurrences.count": {"description": "The server will receive and store a recurring task with a count set in the RRULE"}, + "save-load.todo.mixed-calendar": {"description": "The same calendar may contain both events and tasks (Zimbra only allows tasks to be placed on special task lists)"}, + "save-load.journal": {"description": "The server will even accept journals"}, + "save-load.event.timezone": { + "description": "The server accepts events with non-UTC timezone information. When unsupported or broken, the server may reject events with timezone data (e.g., return 403 Forbidden). Related to GitHub issue https://github.com/python-caldav/caldav/issues/372." + }, + "search": { + "description": "calendar MUST support searching for objects using the REPORT method, as specified in RFC4791, section 7" + }, + "search.comp-type-optional": { + "description": "In all the search examples in the RFC, comptype is given during a search, the client specifies if it's event or tasks or journals that is wanted. However, as I read the RFC this is not required. If omitted, the server should deliver all objects. Many servers will not return anything if the COMPTYPE filter is not set. Other servers will return 404" + }, + "search.comp-type": { + "description": "Server correctly filters calendar-query results by component type. When 'broken', server may misclassify component types (e.g., returning TODOs when VEVENTs are requested). The library will perform client-side filtering to work around this issue", + "default": {"support": "full"} + }, + ## TODO - there is still quite a lot of search-related + ## stuff that hasn't been moved from the old "quirk list" + "search.time-range": { + "description": "Search for time or date ranges should work. This is specified in RFC4791, section 7.4 and section 9.9"}, + "search.time-range.accurate": { + "description": "Time-range searches should only return events/todos that actually fall within the requested time range. Some servers incorrectly return recurring events whose recurrences fall outside (after) the search interval, or events with no recurrences in the requested time range at all. RFC4791 section 9.9 specifies that a VEVENT component overlaps a time range if the condition (start < search_end AND end > search_start) is true.", + "links": ["https://datatracker.ietf.org/doc/html/rfc4791#section-9.9"], + }, + "search.time-range.todo": {"description": "basic time range searches for tasks works"}, + "search.time-range.event": {"description": "basic time range searches for event works"}, + "search.time-range.journal": {"description": "basic time range searches for journal works"}, + "search.time-range.alarm": {"description": "Time range searches for alarms work. The server supports searching for events based on when their alarms trigger, as specified in RFC4791 section 9.9"}, + "search.is-not-defined": { + "description": "Supports searching for objects where properties is-not-defined according to rfc4791 section 9.7.4" + }, + "search.text": { + "description": "Search for text attributes should work" + }, + "search.text.case-sensitive": { + "description": "In RFC4791, section-9.7.5, a text-match may pass a collation, and i;ascii-casemap MUST be the default, this is not checked (yet - TODO) by the caldav-server-checker project. Section 7.5 describes that the servers also are REQUIRED to support i;octet. The definitions of those collations are given in RFC4790, i;octet is a case-sensitive byte-by-byte comparition (fastest). search.text.case-sensitive is supported if passing the i;octet collation to search causes the search to be case-sensitive." + }, + "search.text.case-insensitive": { + "description": "The i;ascii-casemap requires ascii-characters to be case-insensitive, while non-ascii characters are compared byte-by-byte (case-sensitive). Proper unicode case-insensitive searches may be supported by the server, but it's not a requirement in the RFC. As for now, we consider case-insensitive searches to be supported if the i;ascii-casemap collation does what it's supposed to do.. In the future we may consider adding a search.text.case-insensitive.unicode. (i;unicode-casemap is defined in RFC5051)" + }, + "search.text.substring": { + "description": "According to RFC4791 the search done should be a substring search. The search.text.substring feature is set if the calendar server does this (as opposed to only return full matches). Substring matches does not always make sense, but it's mandated by the RFC. When a server does a substring match on some properties but an exact match on others, the support should be marked as fragile. Except for categories, which are handled in search.text.category.substring" + }, + "search.text.category": { + "description": "Search for category should work. This is not explicitly specified in RFC4791, but covered in section 9.7.5. No examples targets categories explicitly, but there are some text match examples in section 7.8.6 and following sections"}, + "search.text.category.substring": { + "description": "Substring search for category should work according to the RFC. I.e., search for mil should match family,finance", + }, + "search.text.by-uid": { + "description": "The server supports searching for objects by UID property. When unsupported, calendar.object_by_uid(uid) will not work. This may be removed in the feature - the checker-script is not checking the right thing (check TODO-comments), probably search by uid is no special case for any server implementations" + }, + "search.recurrences": { + "description": "Support for recurrences in search" + }, + "search.recurrences.includes-implicit": { + "description": "RFC 4791, section 7.4 says that the server MUST expand recurring components to determine whether any recurrence instances overlap the specified time range. Considered supported i.e. if a search for 2005 yields a yearly event happening first time in 2004.", + "links": ["https://datatracker.ietf.org/doc/html/rfc4791#section-7.4"], + }, + "search.recurrences.includes-implicit.todo": { + "description": "tasks can also be recurring" + }, + "search.recurrences.includes-implicit.todo.pending": { + "description": "a future recurrence of a pending task should always be pending and appear in searches for pending tasks" + }, + "search.recurrences.includes-implicit.event": { + "description": "support for events" + }, + "search.recurrences.includes-implicit.infinite-scope": { + "description": "Needless to say, search on any future date range, no matter how far out in the future, should yield the recurring object" + }, + "search.combined-is-logical-and": { + "description": "Multiple search filters should yield only those that passes all filters" + ## For "unsupported", we could also add a "behaviour" (returns everything, returns nothing, returns logical OR, etc). + }, + "search.recurrences.expanded": { + "description": "According to RFC 4791, the server MUST expand recurrence objects if asked for it - but many server doesn't do that. Some servers don't do expand at all, others deliver broken data, typically missing RECURRENCE-ID. The python caldav client library (from 2.0) does the expand-operation client-side no matter if it's supported or not", + "links": ["https://datatracker.ietf.org/doc/html/rfc4791#section-9.6.5"], + }, + "search.recurrences.expanded.todo": { + "description": "expanding tasks" + }, + "search.recurrences.expanded.event": { + "description": "exanding events" + }, + "search.recurrences.expanded.exception": { + "description": "Server expand should work correctly also if a recurrence set with exceptions is given" + }, + "sync-token": { + "description": "RFC6578 sync-collection reports are supported. Server provides sync tokens that can be used to efficiently retrieve only changed objects since last sync. Support can be 'full', 'fragile' (occasionally returns more content than expected), or 'unsupported'. Behaviour 'time-based' indicates second-precision tokens requiring sleep(1) between operations" + }, + "sync-token.delete": { + "description": "Server correctly handles sync-collection reports after objects have been deleted from the calendar (solved in Nextcloud in https://github.com/nextcloud/server/pull/44130)" + }, + 'freebusy-query': {'description': "freebusy queries come in two flavors, one query can be done towards a CalDAV server as defined in RFC4791, another query can be done through the scheduling framework, RFC 6638. Only RFC4791 is tested for as today"}, + "freebusy-query.rfc4791": { + "description": "Server supports free/busy-query REPORT as specified in RFC4791 section 7.10. The REPORT allows clients to query for free/busy time information for a time range. Servers without this support will typically return an error (often 500 Internal Server Error or 501 Not Implemented). Note: RFC6638 defines a different freebusy mechanism for scheduling", + "links": ["https://datatracker.ietf.org/doc/html/rfc4791#section-7.10"], + }, + "principal-search": { + "description": "Server supports searching for principals (CalDAV users). Principal search may be restricted for privacy/security reasons on many servers. (not to be confused with get-current-user-principal)" + }, + "principal-search.by-name": { + "description": "Server supports searching for principals by display name. Testing this properly requires setting up another user with a known name, so this check is not yet implemented" + }, + "principal-search.by-name.self": { + "description": "Server allows searching for own principal by display name. Some servers block this for privacy reasons even when general principal search works" + }, + "principal-search.list-all": { + "description": "Server allows listing all principals without a name filter. Often blocked for privacy/security reasons" + }, + "save": {}, + "save.duplicate-uid": {}, + "save.duplicate-uid.cross-calendar": { + "description": "Server allows events with the same UID to exist in different calendars and treats them as separate entities. Support can be 'full' (allowed), 'ungraceful' (rejected with error), or 'unsupported' (silently ignored or moved). Behaviour 'silently-ignored' means the duplicate is not saved but no error is thrown. Behaviour 'moved-instead-of-copied' means the event is moved from the original calendar to the new calendar (Zimbra behavior)" + }, + ## TODO: as for now, the tests will run towards the first calendar it will find, and most of the tests will assume the calendar is empty. This is bad. + "test-calendar": { + "type": "tests-behaviour", + "description": "if the server does not allow creating new calendars, then use the calendar with the given name for running tests (NOT SUPPORTED YET!), wipe the calendar between each test run (alternative for calendars not supporting the creation of new calendars is a very expensive delete objects one-by-one by uid)", + "extra_keys": { "name": "calendar name", "cleanup-regime": "thorough|pre|post|light|wipe-calendar" } + }, + "test-calendar.compatibility-tests": { + "type": "tests-behaviour", + "description": "if the server does not allow creating new calendars, then use the calendar with the given name for running the compatibility tests", + "extra_keys": { "name": "calendar name", "cleanup": "Set to True to clean up the calendar after compatibility run" } ## if needed, pad up with cal_id, url, etc + } ## if needed we may pad up with test-calendar.compatibility-tests.events, etc, etc + } + + def __init__(self, feature_set_dict=None): + """ + TODO: describe the feature_set better. + + Should be a dict on the same style as self.FEATURES, but different. + + Shortcuts accepted in the dict, like: + + { + "recurrences.search-includes-implicit-recurrences.infinite-scope": + "unsupported" } + + is equivalent with + + { + "recurrences": { + "features": { + "search-includes-inplicit-recurrences": { + "infinite-scope": + "support": "unsupported" }}}} + + (TODO: is this sane? Am I reinventing a configuration language?) + """ + if isinstance(feature_set_dict, FeatureSet): + self._server_features = copy.deepcopy(feature_set_dict._server_features) + + ## TODO: copy the FEATURES dict, or just the feature_set dict? + ## (anyways, that is an internal design decision that may be + ## changed ... but we need test code in place) + self.backward_compatibility_mode = feature_set_dict is None + self._server_features = {} + ## TODO: remove this when it can be removed + self._old_flags = [] + if feature_set_dict: + self.copyFeatureSet(feature_set_dict, collapse=False) + + + def set_feature(self, feature, value=True): + if isinstance(value, dict): + fc = {feature: value} + elif isinstance(value, str): + fc = {feature: {"support": value}} + elif value is True: + fc = {feature: {"support": "full"}} + elif value is False: + fc = {feature: {"support": "unsupported"}} + elif value is None: + fc = {feature: {"support": "unknown"}} + else: + assert False + self.copyFeatureSet(fc, collapse=False) + feat_def = self.find_feature(feature) + feat_type = feat_def.get('type', 'server-feature') + sup = fc[feature].get('support', feat_def.get('default', 'full')) + + + ## TODO: Why is this camelCase while every other method is with under_score? rename ... + def copyFeatureSet(self, feature_set, collapse=True): + for feature in feature_set: + ## TODO: temp - should be removed + if feature == 'old_flags': + self._old_flags = feature_set[feature] + continue + feature_info = self.find_feature(feature) + value = feature_set[feature] + if not feature in self._server_features: + self._server_features[feature] = {} + server_node = self._server_features[feature] + if isinstance(value, bool): + server_node['support'] = "full" if value else "unsupported" + elif isinstance(value, str) and not 'support' in server_node: + server_node['support'] = value + elif isinstance(value, dict): + server_node.update(value) + else: + assert False + if collapse: + self.collapse() + + def _collapse_key(self, feature_dict): + """ + Extract the key part of a feature dictionary for comparison during collapse. + + For collapse purposes, we compare the 'support' level (or 'enable', 'behaviour', 'observed') + but ignore differences in detailed behaviour messages, as those are often implementation-specific + error messages that shouldn't prevent collapsing. + """ + if not isinstance(feature_dict, dict): + return feature_dict + + # Return a tuple of the main status fields, ignoring detailed messages + return ( + feature_dict.get('support'), + feature_dict.get('enable'), + feature_dict.get('observed'), + ) + + def collapse(self): + """ + If all subfeatures are the same, it should be collapsed into the parent + + Messy and complex logic :-( + """ + features = list(self._server_features.keys()) + parents = set() + for feature in features: + if '.' in feature: + parents.add(feature[:feature.rfind('.')]) + parents = list(parents) + ## Parents needs to be ordered by the number of dots. We proceed those with most dots first. + parents.sort(key = lambda x: (-x.count('.'), x)) + for parent in parents: + parent_info = self.find_feature(parent) + + if len(parent_info['subfeatures']): + foo = self.is_supported(parent, return_type=dict, return_defaults=False) + if len(parent_info['subfeatures']) > 1 or foo is not None: + dont_collapse = False + foo_key = self._collapse_key(foo) if foo is not None else None + for sub in parent_info['subfeatures']: + bar = self._server_features.get(f"{parent}.{sub}") + if bar is None: + dont_collapse = True + break + bar_key = self._collapse_key(bar) + if foo is None: + foo = bar + foo_key = bar_key + elif bar_key != foo_key: + dont_collapse = True + break + if not dont_collapse: + if not parent in self._server_features: + self._server_features[parent] = {} + for sub in parent_info['subfeatures']: + self._server_features.pop(f"{parent}.{sub}") + self.copyFeatureSet({parent: foo}) + + def _default(self, feature_info): + if isinstance(feature_info, str): + feature_info = self.find_feature(feature_info) + if 'default' in feature_info: + return feature_info['default'] + feature_type = feature_info.get('type', 'server-feature') + ## TODO: move the default values up to some constant dict probably, like self.DEFAULTS = { "server-feature": {...}} + if feature_type == 'server-feature': + return { "support": "full" } + elif feature_type == 'client-feature': + return { "enable": False } + elif feature_type == 'server-peculiarity': + return { "behaviour": "normal" } + elif feature_type == 'server-observation': + return { "observed": True } + elif feature_type in ('tests-behaviour', 'client-hints'): + return { } + else: + breakpoint() + + def is_supported(self, feature, return_type=bool, return_defaults=True, accept_fragile=False): + """Work in progress + + TODO: write a better docstring + + The dotted features is essentially a tree. If feature foo + is unsupported it basically means that feature foo.bar is also + unsupported. Hence the extra logic visiting "nodes". + """ + feature_info = self.find_feature(feature) + feature_ = feature + while True: + if feature_ in self._server_features: + return self._convert_node(self._server_features[feature_], feature_info, return_type, accept_fragile) + if not '.' in feature_: + if not return_defaults: + return None + return self._convert_node(self._default(feature_info), feature_info, return_type, accept_fragile) + feature_ = feature_[:feature_.rfind('.')] + + def _convert_node(self, node, feature_info, return_type, accept_fragile=False): + """ + Return the information in a "node" given the wished return_type + + (The dotted feature format was an afterthought, the first + iteration of this code the feature tree was actually a + hierarchical dict, hence the naming of the method. I + considered it too complicated though) + """ + if return_type == str: + ## TODO: consider feature_info['type'], be smarter about it + return node.get('support', node.get('enable', node.get('behaviour'))) + elif return_type == dict: + return node + elif return_type == bool: + ## TODO: consider feature_info['type'], be smarter about this + support = node.get('support', 'full') + if support == 'quirk': + return True + if accept_fragile and support == 'fragile': + support = 'full' + if feature_info.get('type', 'server-feature') == 'server-feature': + return support == 'full' + else: + ## TODO: this may be improved + return not node.get('enable') and not node.get('behaviour') and not node.get('observed') + else: + assert False + + @classmethod + def find_feature(cls, feature: str) -> dict: + """ + Feature should be a string like feature.subfeature.subsubfeature. + + Looks through the FEATURES list and returns the relevant section. + + Will raise an Error if feature is not found + + (this is very simple now - used to be a hierarchy dict to be traversed) + """ + assert feature in cls.FEATURES ## A feature in the configured feature-list does not exist. TODO ... raise a better exception? + if not 'name' in cls.FEATURES[feature]: + cls.FEATURES[feature]['name'] = feature + if '.' in feature and not 'parent' in cls.FEATURES[feature]: + cls.FEATURES[feature]['parent'] = cls.find_feature(feature[:feature.rfind('.')]) + if not 'subfeatures' in cls.FEATURES[feature]: + tree = cls.feature_tree() + for x in feature.split('.'): + tree = tree[x] + cls.FEATURES[feature]['subfeatures'] = tree + return cls.FEATURES[feature] + + @classmethod + def _dots_to_tree(cls, target, source): + for feat in source: + node = target + path = feat.split('.') + for part in path: + if not part in node: + node[part] = {} + node = node[part] + return target + + @classmethod + def feature_tree(cls) -> dict: + """TODO: is this in use at all? Can it be deprecated already? + + TODO: the description may be outdated as I decided to refactor + things from "overly complex" to "just sufficiently complex". + Or maybe it's still a bit too complex. + + A "path" may have several "subpaths" in self.FEATURES + (i.e. feat.subfeat.A, feat.subfeat.B, feat.subfeat.C) + + This method will return `{'feat': { 'subfeat': {'A': {}, ...}}}` + making it possible to traverse the feature tree + + """ + ## I'm an old fart, grown up in an age where CPU-cycles was considered + ## expensive ... so I always cache things when possible ... + if hasattr(cls, '_feature_tree'): + return cls._feature_tree + cls._feature_tree = {} + cls._dots_to_tree(cls._feature_tree, cls.FEATURES) + return cls._feature_tree + + def dotted_feature_set_list(self, compact=False): + ret = {} + if compact: + self.collapse() + for x in self._server_features: + feature = self._server_features[x] + if compact and feature == self._default(x): + continue + ret[x] = feature.copy() + return ret + +#### OLD STYLE + +## THE LIST BELOW IS TO BE REMOVED COMPLETELY. DO NOT USE IT. + +## It's not considered to be part of the public API (though, it should +## have been prefixed with _ to make it clear). The list is being +## removed little-by-little, without regards of SemVer. + +## The lists below are specifying what tests should be skipped or +## modified to accept non-conforming resultsets from the different +## calendar servers. In addition there are some hacks in the library +## code itself to work around some known compatibility issues, like +## the caldav.lib.vcal.fix function. +## Here is a list of all observed (in)compatibility issues the test framework needs to know about +## TODO: +## * references to the relevant parts of the RFC would be nice. +## * Research should be done to triple-check that the issue is on the server side, and not on the client side +## * Some of the things below should be possible to probe the server for. +## * Perhaps some more readable format should be considered (yaml?). +## * Consider how to get this into the documentation +incompatibility_description = { + 'no_current-user-principal': + """Current user principal not supported by the server (flag is ignored by the tests as for now - pass the principal URL as the testing URL and it will work, albeit with one warning""", + + 'no_scheduling': + """RFC6833 is not supported""", + + 'no_scheduling_mailbox': + """Parts of RFC6833 is supported, but not the existence of inbox/mailbox""", + + 'no_scheduling_calendar_user_address_set': + """Parts of RFC6833 is supported, but not getting the calendar users addresses""", + + 'no_default_calendar': + """The given user starts without an assigned default calendar """ + """(or without pre-defined calendars at all)""", + + 'no_freebusy_rfc6638': + """Server does not support a freebusy-request as per RFC6638""", + + 'calendar_order': + """Server supports (nonstandard) calendar ordering property""", + + 'calendar_color': + """Server supports (nonstandard) calendar color property""", + + 'duplicates_not_allowed': + """Duplication of an event in the same calendar not allowed """ + """(even with different uid)""", + + + 'event_by_url_is_broken': + """A GET towards a valid calendar object resource URL will yield 404 (wtf?)""", + + 'no_delete_event': + """Zimbra does not support deleting an event, probably because event_by_url is broken""", + + + 'propfind_allprop_failure': + """The propfind test fails ... """ + """it asserts DAV:allprop response contains the text 'resourcetype', """ + """possibly this assert is wrong""", + + 'vtodo_datesearch_nodtstart_task_is_skipped': + """date searches for todo-items will not find tasks without a dtstart""", + + 'vtodo_datesearch_nodtstart_task_is_skipped_in_closed_date_range': + """only open-ended date searches for todo-items will find tasks without a dtstart""", + + 'vtodo_datesearch_notime_task_is_skipped': + """date searches for todo-items will (only) find tasks that has either """ + """a dtstart or due set""", + + 'vtodo_datesearch_nostart_future_tasks_delivered': + """Future tasks are yielded when doing a date search with some end timestamp and without start timestamp and the task contains both dtstart and due, but not duration (xandikos 0.2.12)""", + + 'vtodo_no_due_infinite_duration': + """date search will find todo-items without due if dtstart is """ + """before the date search interval. This is in breach of rfc4791""" + """section 9.9""", + + 'vtodo_no_dtstart_infinite_duration': + """date search will find todo-items without dtstart if due is """ + """after the date search interval. This is in breach of rfc4791""" + """section 9.9""", + + 'vtodo_no_dtstart_search_weirdness': + """Zimbra is weird""", + + 'vtodo_no_duration_search_weirdness': + """Zimbra is weird""", + + 'vtodo_with_due_weirdness': + """Zimbra is weird""", + + 'vtodo-cannot-be-uncompleted': + """If a VTODO object has been set with STATUS:COMPLETE, it's not possible to delete the COMPLTEDED attribute and change back to STATUS:IN-ACTION""", + + 'unique_calendar_ids': + """For every test, generate a new and unique calendar id""", + + 'sticky_events': + """Events should be deleted before the calendar is deleted, """ + """and/or deleting a calendar may not have immediate effect""", + + 'no_overwrite': + """events cannot be edited""", + + 'dav_not_supported': + """when asked, the server may claim it doesn't support the DAV protocol. Observed by one baikal server, should be investigated more (TODO) and robur""", + + 'text_search_is_case_insensitive': + """Probably not supporting the collation used by the caldav library""", + + 'date_search_ignores_duration': + """Date search with search interval overlapping event interval works on events with dtstart and dtend, but not on events with dtstart and due""", + + 'date_todo_search_ignores_duration': + """Same as above, but specifically for tasks""", + + 'fastmail_buggy_noexpand_date_search': + """The 'blissful anniversary' recurrent example event is returned when asked for a no-expand date search for some timestamps covering a completely different date""", + + 'non_existing_raises_other': + """Robur raises AuthorizationError when trying to access a non-existing resource (while 404 is expected). Probably so one shouldn't probe a public name space?""", + + 'no_supported_components_support': + """The supported components prop query does not work""", + + 'no_relships': + """The calendar server does not support child/parent relationships between calendar components""", + + 'robur_rrule_freq_yearly_expands_monthly': + """Robur expands a yearly event into a monthly event. I believe I've reported this one upstream at some point, but can't find back to it""", + + 'no_search_openended': + """An open-ended search will not work""", + +} + +## This is for Xandikos 0.2.12. +## Lots of development going on as of summer 2025, so expect the list to become shorter soon! +xandikos_v0_2_12 = { + ## this only applies for very simple installations + "auto-connect.url": {"domain": "localhost", "scheme": "http", "basepath": "/"}, + 'search.recurrences.includes-implicit': {'support': 'unsupported'}, + 'search.recurrences.expanded': {'support': 'unsupported'}, + 'search.time-range.todo': {'support': 'unsupported'}, + 'search.time-range.alarm': {'support': 'ungraceful', 'behaviour': '500 internal server error'}, + 'search.comp-type-optional': {'support': 'ungraceful'}, + "search.text.substring": {"support": "unsupported"}, + "search.text.category.substring": {"support": "unsupported"}, + 'principal-search': {'support': 'unsupported'}, + 'freebusy-query.rfc4791': {'support': 'ungraceful', 'behaviour': '500 internal server error'}, + "old_flags": [ + ## https://github.com/jelmer/xandikos/issues/8 + 'date_todo_search_ignores_duration', + 'vtodo_datesearch_nostart_future_tasks_delivered', + + ## scheduling is not supported + "no_scheduling", + + ## The test with an rrule and an overridden event passes as + ## long as it's with timestamps. With dates, xandikos gets + ## into troubles. I've chosen to edit the test to use timestamp + ## rather than date, just to have the test exercised ... but we + ## should report this upstream + #'broken_expand_on_exceptions', + + ] +} + +xandikos_v0_3 = { + ## this only applies for very simple installations + "auto-connect.url": {"domain": "localhost", "scheme": "http", "basepath": "/"}, + 'search.comp-type-optional': {'support': 'unsupported'}, + ## This suddenly disappeared. Should probably look more into the checks ... + #"search.recurrences.includes-implicit.todo.pending": {"support": "unsupported"}, + 'search.recurrences.expanded.todo': {'support': 'unsupported'}, + 'search.recurrences.expanded.exception': {'support': 'unsupported'}, + 'principal-search': {'support': 'ungraceful'}, + 'freebusy-query.rfc4791': {'support': 'ungraceful', 'behaviour': '500 internal server error'}, + "old_flags": [ + ## https://github.com/jelmer/xandikos/issues/8 + 'date_todo_search_ignores_duration', + 'vtodo_datesearch_nostart_future_tasks_delivered', + + ## scheduling is not supported + "no_scheduling", + + ## The test with an rrule and an overridden event passes as + ## long as it's with timestamps. With dates, xandikos gets + ## into troubles. I've chosen to edit the test to use timestamp + ## rather than date, just to have the test exercised ... but we + ## should report this upstream + #'broken_expand_on_exceptions', + + ] +} + +xandikos=xandikos_v0_3 + +## This seems to work as of version 3.5.4 of Radicale. +## There is much development going on at Radicale as of summar 2025, +## so I'm expecting this list to shrink a lot soon. +radicale = { + "search.text.case-sensitive": {"support": "unsupported"}, + "search.is-not-defined": {"support": "fragile", "behaviour": "seems to work for categories but not for dtend"}, + "search.recurrences.includes-implicit.todo.pending": {"support": "unsupported"}, + "search.recurrences.expanded.todo": {"support": "unsupported"}, + "search.recurrences.expanded.exception": {"support": "unsupported"}, + 'principal-search': {'support': 'unknown', 'behaviour': 'No display name available - cannot test'}, + 'principal-search.list-all': {'support': 'unsupported'}, + ## this only applies for very simple installations + "auto-connect.url": {"domain": "localhost", "scheme": "http", "basepath": "/"}, + ## freebusy is not supported yet, but on the long-term road map + 'old_flags': [ + ## calendar listings and calendar creation works a bit + ## "weird" on radicale + + 'no_scheduling', + 'no_search_openended', + + #'text_search_is_exact_match_sometimes', + + ## extra features not specified in RFC5545 + "calendar_order", + "calendar_color" + ] +} + +## Be aware that nextcloud by default have different rate limits, including how often a user is allowed to create a new calendar. This may break test runs badly. +nextcloud = { + 'auto-connect.url': { + 'basepath': '/remote.php/dav', + }, + 'search.combined-is-logical-and': {'support': 'unsupported'}, + 'search.comp-type-optional': {'support': 'ungraceful'}, + 'search.recurrences.expanded.todo': {'support': 'unsupported'}, + 'search.recurrences.expanded.exception': {'support': 'unsupported'}, ## TODO: verify + 'delete-calendar': { + 'support': 'fragile', + 'behaviour': 'Deleting a recently created calendar fails'}, + 'delete-calendar.free-namespace': { ## TODO: not caught by server-tester + 'behaviour': "deleting a calendar moves it to a trashbin, thrashbin has to be manually 'emptied' from the web-ui before the namespace is freed up", + 'support': 'fragile', + }, + 'search.comp-type-optional': { + 'support': 'ungraceful', + }, + "search.combined-is-logical-and": {"support": "unsupported"}, + 'search.recurrences.includes-implicit.todo': {'support': 'unsupported'}, + #'save-load.todo.mixed-calendar': {'support': 'unsupported'}, ## Why? It started complaining about this just recently. + 'principal-search.by-name': {'support': 'unsupported'}, + 'principal-search.list-all': {'support': 'ungraceful'}, + 'old_flags': ['unique_calendar_ids'], +} + +## TODO: Latest - mismatch between config and test script in delete-calendar.free-namespace ... and create-calendar.set-displayname? +ecloud = nextcloud | { + ## TODO: this applies only to test runs, not to ordinary usage + 'rate-limit': { + 'enable': True, + 'interval': 10, + 'count': 1, + 'description': "It's needed to manually empty trashbin frequently when running tests. Since this oepration takes some time and/or there are some caches, it's needed to run tests slowly, even when hammering the 'empty thrashbin' frequently", + }, + 'auto-connect.url': { + 'basepath': '/remote.php/dav', + 'domain': 'ecloud.global', + 'scheme': 'https', + }, +} + +## Zimbra is not very good at it's caldav support +zimbra = { + 'auto-connect.url': {'basepath': '/dav/'}, + 'search.recurrences.expanded.exception': {'support': 'unsupported'}, ## TODO: verify + 'create-calendar.set-displayname': {'support': 'unsupported'}, + 'save-load.todo.mixed-calendar': {'support': 'unsupported'}, + 'save-load.todo.recurrences.count': {'support': 'unsupported'}, ## This is a new problem? + 'save-load.journal': "ungraceful", + 'search.is-not-defined': {'support': 'unsupported'}, + #'search.text': 'unsupported', ## weeeird ... it wasn't like this before + 'search.text.substring': {'support': 'unsupported'}, + 'search.text.category': {'support': 'ungraceful'}, + 'search.is-not-defined': {'support': 'unsupported'}, + 'search.recurrences.expanded.todo': { "support": "unsupported" }, + 'search.comp-type-optional': {'support': 'fragile'}, ## TODO: more research on this, looks like a bug in the checker, + 'search.time-range.alarm': {'support': 'unsupported'}, + 'sync-token': {'support': 'ungraceful'}, + 'principal-search': "ungraceful", + 'save.duplicate-uid.cross-calendar': {'support': 'unsupported', "behaviour": "moved-instead-of-copied" }, + + "old_flags": [ + ## apparently, zimbra has no journal support + + ## setting display name in zimbra does not work (display name, + ## calendar-ID and URL is the same, the display name cannot be + ## changed, it can only be given if no calendar-ID is given. In + ## earlier versions of Zimbra display-name could be changed, but + ## then the calendar would not be available on the old URL + ## anymore) + 'event_by_url_is_broken', + 'no_delete_event', + 'vtodo_datesearch_notime_task_is_skipped', + 'no_relships', + + ## TODO: I just discovered that when searching for a date some + ## years after a recurring daily event was made, the event does + ## not appear. + + ## extra features not specified in RFC5545 + "calendar_order", + "calendar_color" + ] + ## TODO: there may be more, it should be organized and moved here. + ## Search for 'zimbra' in the code repository! +} + +bedework = { + 'search.comp-type': {'support': 'broken', 'behaviour': 'Server returns everything when searching for events and nothing when searching for todos'}, + #"search.combined-is-logical-and": { "support": "unsupported" }, + ## TODO: play with this and see if it's needed + 'search-cache': {'behaviour': 'delay', 'delay': 1.5}, + ## TODO: play with this and see if it's needed + 'old_flags': [ + 'propfind_allprop_failure', + 'duplicates_not_allowed', + ], + 'auto-connect.url': {'basepath': '/ucaldav/'}, + "save-load.journal": { + "support": "ungraceful" + }, + "search.time-range.alarm": { + "support": "unsupported" + }, + ## Huh? Non-deterministic behaviour of the checking script? + #"save.duplicate-uid.cross-calendar": { + # "support": "unsupported", + # "behaviour": "silently-ignored" + #}, + "freebusy-query.rfc4791": { + "support": "full" + }, + "search.time-range.todo": { + "support": "unsupported" + }, + "search.text": { + "support": "unsupported" + }, + "search.is-not-defined": { + "support": "fragile" + }, + "search.text.by-uid": { + "support": "fragile", + "behaviour": "sometimes the text search delivers everything, other times it doesn't deliver anything. When the text search delivers everything, then the post-filtering will save the day" + }, + "search.time-range.accurate": { + "support": "unsupported" + }, + "search.recurrences.includes-implicit.todo": { + "support": "unsupported" + }, + "search.recurrences.includes-implicit.infinite-scope": { + "support": "unsupported" + }, + "sync-token": { + "support": "fragile" + }, + ## Check results are non-deterministic!? + "search.recurrences.expanded.exception": { + "support": "unsupported" + }, + "search.recurrences.expanded.event": { + "support": "unsupported" + }, + ## It doesn't support expanding events, but it supports exapnding tasks!? + ## Or maybe there is a problem in the checker script? + ## TODO: look into this + #"search.recurrences.expanded.todo": True, + "principal-search": { + "support": "ungraceful", + } +} + +synology = { + 'principal-search': False, + 'search.time-range.alarm': False, + 'sync-token': 'fragile', + 'delete-calendar': False, + 'search.comp-type-optional': 'fragile', + "search.recurrences.expanded.exception": False, + 'old_flags': ['vtodo_datesearch_nodtstart_task_is_skipped'], +} + +baikal = { ## version 0.10.1 + "http.multiplexing": "fragile", ## ref https://github.com/python-caldav/caldav/issues/564 + "save-load.journal": {'support': 'ungraceful'}, + #'search.comp-type-optional': {'support': 'ungraceful'}, ## Possibly this has been fixed? + 'search.recurrences.expanded.todo': {'support': 'unsupported'}, + 'search.recurrences.expanded.exception': {'support': 'unsupported'}, + 'search.recurrences.includes-implicit.todo': {'support': 'unsupported'}, + "search.combined-is-logical-and": {"support": "unsupported"}, + 'principal-search.by-name': {'support': 'unsupported'}, ## This is weird - I'm quite sure the tests were passing without this one some few days ago. + 'principal-search.list-all': {'support': 'ungraceful'}, ## This is weird - I'm quite sure the tests were passing without this one some few days ago. + #'sync-token.delete': {'support': 'unsupported'}, ## Perhaps on some older servers? + 'old_flags': [ + ## extra features not specified in RFC5545 + "calendar_order", + "calendar_color" + ] +} ## TODO: testPrincipals, testWrongAuthType, testTodoDatesearch fails + +## Some unknown version of baikal has this +baikal_old = baikal | { + 'create-calendar': {'support': 'quirk', 'behaviour': 'mkcol-required'}, + 'create-calendar.auto': {'support': 'unsupported'}, ## this is the default, but the "quirk" from create-calendar overwrites it. Hm. + +} + +cyrus = { + "search.comp-type-optional": {"support": "ungraceful"}, + "search.recurrences.expanded.exception": {"support": "unsupported"}, + 'search.time-range.alarm': {'support': 'unsupported'}, + 'principal-search': {'support': 'ungraceful'}, + "test-calendar": {"cleanup-regime": "pre"}, + 'delete-calendar': { + 'support': 'fragile', + 'behaviour': 'Deleting a recently created calendar fails'}, + 'save.duplicate-uid.cross-calendar': {'support': 'ungraceful'}, + 'old_flags': [] +} + +## See comments on https://github.com/python-caldav/caldav/issues/3 +#icloud = [ +# 'unique_calendar_ids', +# 'duplicate_in_other_calendar_with_same_uid_breaks', +# 'sticky_events', +# 'no_journal', ## it threw a 500 internal server error! +# 'no_todo', +# "no_freebusy_rfc4791", +# 'no_recurring', +# 'propfind_allprop_failure', +# 'object_by_uid_is_broken' +#] + +davical = { + + "search.comp-type-optional": { "support": "fragile" }, + "search.recurrences.expanded.todo": { "support": "unsupported" }, + "search.recurrences.expanded.exception": { "support": "unsupported" }, + 'search.time-range.alarm': {'support': 'unsupported'}, + 'sync-token': {'support': 'fragile'}, + 'principal-search': {'support': 'unsupported'}, + 'principal-search.list-all': {'support': 'unsupported'}, + "old_flags": [ + #'no_journal', ## it threw a 500 internal server error! ## for old versions + #'nofreebusy', ## for old versions + 'fragile_sync_tokens', ## no issue raised yet + 'vtodo_datesearch_nodtstart_task_is_skipped', ## no issue raised yet + 'date_todo_search_ignores_duration', + 'calendar_color', + 'calendar_order', + 'vtodo_datesearch_notime_task_is_skipped', + ] +} + +sogo = { + "save-load.journal": { "support": "ungraceful" }, + 'freebusy-query.rfc4791': {'support': 'ungraceful'}, + "search.time-range.accurate": { + "support": "unsupported", + "description": "SOGo returns events/todos that fall outside the requested time range. For recurring events, it may return recurrences that start after the search interval ends, or events with no recurrences in the requested range at all." + }, + "search.time-range.alarm": { + "support": "unsupported" + }, + "search.time-range.event": { + "support": "unsupported" + }, + "search.time-range.todo": { + "support": "unsupported" + }, + "search.text": { + "support": "unsupported" + }, + "search.text.by-uid": True, + "search.is-not-defined": { + "support": "unsupported" + }, + "search.comp-type-optional": { + "support": "unsupported" + }, + "search.recurrences.includes-implicit.todo": { + "support": "unsupported" + }, + ## TODO: do some research into this, I think this is a bug in the checker script + "search.recurrences.includes-implicit.todo.pending": { + "support": "fragile" + }, + "search.recurrences.includes-implicit.infinite-scope": { + "support": "unsupported" + }, + "sync-token": { + "support": "fragile" + }, + "search.recurrences.expanded": { + "support": "unsupported" + }, + "principal-search": { + "support": "ungraceful", + "behaviour": "Search by name failed: ReportError at '501 Not Implemented - \n\n

An error occurred during object publishing

did not find the specified REPORT

\n\n', reason no reason", + }, + +} +## Old notes for sogo (todo - incorporate them in the structure above) +## https://www.sogo.nu/bugs/view.php?id=3065 +## left a note about time-based sync tokens on https://www.sogo.nu/bugs/view.php?id=5163 +## https://www.sogo.nu/bugs/view.php?id=5282 +## https://bugs.sogo.nu/view.php?id=5693 +## https://bugs.sogo.nu/view.php?id=5694 +#sogo = [ ## and in addition ... the requests are efficiently rate limited, as it spawns lots of postgresql connections all until it hits a limit, after that it's 501 errors ... +# "time_based_sync_tokens", +# "search_needs_comptype", +# "fastmail_buggy_noexpand_date_search", +# "text_search_not_working", +# "isnotdefined_not_working", +# 'no_journal', +# 'no_freebusoy_rfc4791' +#] + + + +#google = [ +# 'no_mkcalendar', +# 'no_overwrite', +# 'no_todo', +#] + +#fastmail = [ +# 'duplicates_not_allowed', +# 'duplicate_in_other_calendar_with_same_uid_breaks', +# 'no_todo', +# 'sticky_events', +# 'fastmail_buggy_noexpand_date_search', +# 'combined_search_not_working', +# 'text_search_is_exact_match_sometimes', +# 'rrule_takes_no_count', +# 'isnotdefined_not_working', +#] + +robur = { + "auto-connect.url": { + 'domain': 'calendar.robur.coop', + 'basepath': '/principals/', # TODO: this seems fishy + }, + "save-load.journal": { "support": "ungraceful" }, + "delete-calendar": { "support": "fragile" }, + "search.is-not-defined": { "support": "unsupported" }, + "search.time-range.todo": { "support": "unsupported" }, + "search.time-range.alarm": {'support': 'unsupported'}, + "search.text": { "support": "unsupported", "behaviour": "a text search ignores the filter and returns all elements" }, + "search.text.by-uid": { "support": "fragile", "behaviour": "Probably not supported, but my caldav-server-checker tool has issues with it at the moment" }, + "search.comp-type-optional": { "support": "ungraceful" }, + "search.recurrences.expanded.todo": { "support": "unsupported" }, + "search.recurrences.expanded.event": { "support": "fragile" }, + "search.recurrences.expanded.exception": { "support": "unsupported" }, + 'search.recurrences.includes-implicit.todo': {'support': 'unsupported'}, + 'principal-search': {'support': 'ungraceful'}, + 'freebusy-query.rfc4791': {'support': 'ungraceful'}, + 'old_flags': [ + 'non_existing_raises_other', ## AuthorizationError instead of NotFoundError + 'no_scheduling', + 'no_supported_components_support', + 'no_relships', + 'unique_calendar_ids', + ], + "sync-token": False, +} + +posteo = { + 'auto-connect.url': { + 'scheme': 'https', + 'domain': 'posteo.de:8443', + 'basepath': '/', + }, + 'create-calendar': {'support': 'unsupported'}, + 'save-load.journal': { "support": "ungraceful" }, + ## TODO1: we should ignore cases where observations are unknown while configuration is known + ## TODO2: there are more calendars available at the posteo account, so it should be possible to check this. + "save.duplicate-uid.cross-calendar": { "support": "unknown" }, + 'search.comp-type-optional': {'support': 'ungraceful'}, + 'search.recurrences.expanded.todo': {'support': 'unsupported'}, + 'search.recurrences.expanded.exception': {'support': 'unsupported'}, + 'search.recurrences.includes-implicit.todo': {'support': 'unsupported'}, + "search.combined-is-logical-and": {"support": "unsupported"}, + 'sync-token': {'support': 'ungraceful'}, + 'principal-search': {'support': 'unsupported'}, + 'old_flags': [ + 'no_scheduling', + #'no_recurring_todo', ## todo + ] +} + +#calendar_mail_ru = [ +# 'no_mkcalendar', ## weird. It was working in early June 2024, then it stopped working in mid-June 2024. +# 'no_current-user-principal', +# 'no_todo', +# 'no_journal', +# 'search_always_needs_comptype', +# 'no_sync_token', ## don't know if sync tokens are supported or not - the sync-token-code needs some workarounds ref https://github.com/python-caldav/caldav/issues/401 +# 'text_search_not_working', +# 'isnotdefined_not_working', +# 'no_scheduling_mailbox', +# 'no_freebusy_rfc4791', +# 'no_relships', ## mail.ru recreates the icalendar content, and strips everything it doesn't know anyhting about, including relationship info +#] + +purelymail = { + ## Purelymail claims that the search indexes are "lazily" populated, + ## so search works some minutes after the event was created/edited. + 'search-cache': {'behaviour': 'delay', 'delay': 160}, + "create-calendar.auto": {"support": "full"}, + 'search.time-range.alarm': {'support': 'unsupported'}, + 'principal-search': {'support': 'unsupported'}, + 'auto-connect.url': { + 'basepath': '/webdav/', + 'domain': 'purelymail.com', + }, + 'old_flags': [ + ## Known, work in progress + 'no_scheduling', + + ## Known, not a breach of standard + 'no_supported_components_support', + + ## I haven't raised this one with them yet + ] +} + +gmx = { + 'auto-connect.url': { + 'scheme': 'https', + 'domain': 'caldav.gmx.net', + ## This won't work yet. I'm not able to connect with gmx at all now, + ## so unable to create a verified fix for it now + 'basepath': '/begenda/dav/{username}/calendar', ## TODO: foobar + }, + 'create-calendar': {'support': 'unsupported'}, + 'search.comp-type-optional': {'support': 'fragile', 'description': 'unexpected results from date-search without comp-type - but only sometimes - TODO: research more'}, + 'search.recurrences.expanded': {'support': 'unsupported'}, + 'search.time-range.alarm': {'support': 'unsupported'}, + 'sync-token': {'support': 'unsupported'}, + 'principal-search': {'support': 'unsupported'}, + 'freebusy-query.rfc4791': {'support': 'unsupported'}, + "old_flags": [ + "no_scheduling_mailbox", + #"text_search_is_case_insensitive", + "no_search_openended", + "no_scheduling_calendar_user_address_set", + "vtodo-cannot-be-uncompleted", + ] +} + +# fmt: on diff --git a/.venv/lib/python3.9/site-packages/caldav/config.py b/.venv/lib/python3.9/site-packages/caldav/config.py new file mode 100644 index 0000000..bd1da62 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/config.py @@ -0,0 +1,125 @@ +import json +import logging +import os + +""" +This configuration parsing code was just copied from my plann library (and will be removed from there at some point in the future). Test coverage is poor as for now. +""" + +## This is being moved from my plann library. The code itself will be introduced into caldav 2.0, but proper test code and documentation will come in a later release (2.1?) + + +## TODO TODO TODO - write test code for all the corner cases +## TODO TODO TODO - write documentation of config format +def expand_config_section(config, section="default", blacklist=None): + """ + In the "normal" case, will return [ section ] + + We allow: + + * * includes all sections in config file + * "Meta"-sections in the config file with the keyword "contains" followed by a list of section names + * Recursive "meta"-sections + * Glob patterns (work_* for all sections starting with work_) + * Glob patterns in "meta"-sections + """ + ## Optimizating for a special case. The results should be the same without this optimization. + if section == "*": + return [x for x in config if not config[x].get("disable", False)] + + ## If it's not a glob-pattern ... + if set(section).isdisjoint(set("[*?")): + ## If it's referring to a "meta section" with the "contains" keyword + if "contains" in config[section]: + results = [] + if not blacklist: + blacklist = set() + blacklist.add(section) + for subsection in config[section]["contains"]: + if not subsection in results and not subsection in blacklist: + for recursivesubsection in expand_config_section( + config, subsection, blacklist + ): + if not recursivesubsection in results: + results.append(recursivesubsection) + return results + else: + ## Disabled sections should be ignored + if config.get("section", {}).get("disable", False): + return [] + + ## NORMAL CASE - return [ section ] + return [section] + ## section name is a glob pattern + matching_sections = [x for x in config if fnmatch(x, section)] + results = set() + for s in matching_sections: + if set(s).isdisjoint(set("[*?")): + results.update(expand_config_section(config, s)) + else: + ## Section names shouldn't contain []?* ... but in case they do ... don't recurse + results.add(s) + return results + + +def config_section(config, section="default"): + if section in config and "inherits" in config[section]: + ret = config_section(config, config[section]["inherits"]) + else: + ret = {} + if section in config: + ret.update(config[section]) + return ret + + +def read_config(fn, interactive_error=False): + if not fn: + cfgdir = f"{os.environ.get('HOME', '/')}/.config/" + for config_file in ( + f"{cfgdir}/caldav/calendar.conf", + f"{cfgdir}/caldav/calendar.yaml", + f"{cfgdir}/caldav/calendar.json", + f"{cfgdir}/calendar.conf", + "/etc/calendar.conf", + "/etc/caldav/calendar.conf", + ): + cfg = read_config(config_file) + if cfg: + return cfg + return None + + ## This can probably be refactored into fewer lines ... + try: + try: + with open(fn, "rb") as config_file: + return json.load(config_file) + except json.decoder.JSONDecodeError: + ## Late import, wrapped in try/except. yaml is external module, + ## and not included in the requirements as for now. + try: + import yaml + + try: + with open(fn, "rb") as config_file: + return yaml.load(config_file, yaml.Loader) + except yaml.scanner.ScannerError: + logging.error( + f"config file {fn} exists but is neither valid json nor yaml. Check the syntax." + ) + except ImportError: + logging.error( + f"config file {fn} exists but is not valid json, and pyyaml is not installed." + ) + + except FileNotFoundError: + ## File not found + logging.info("no config file found") + except ValueError: + if interactive_error: + logging.error( + "error in config file. Be aware that the interactive configuration will ignore and overwrite the current broken config file", + exc_info=True, + ) + else: + logging.error("error in config file. It will be ignored", exc_info=True) + return {} diff --git a/.venv/lib/python3.9/site-packages/caldav/davclient.py b/.venv/lib/python3.9/site-packages/caldav/davclient.py new file mode 100644 index 0000000..f719159 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/davclient.py @@ -0,0 +1,1311 @@ +#!/usr/bin/env python +import logging +import os +import sys +import warnings +from types import TracebackType +from typing import Any +from typing import cast +from typing import Dict +from typing import List +from typing import Optional +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union +from urllib.parse import unquote + + +try: + import niquests as requests + from niquests.auth import AuthBase + from niquests.models import Response + from niquests.structures import CaseInsensitiveDict +except ImportError: + import requests + from requests.auth import AuthBase + from requests.models import Response + from requests.structures import CaseInsensitiveDict + +from lxml import etree +from lxml.etree import _Element + +from .elements.base import BaseElement +from caldav import __version__ +from caldav.collection import Calendar +from caldav.collection import CalendarSet +from caldav.collection import Principal +import caldav.compatibility_hints +from caldav.compatibility_hints import FeatureSet +from caldav.elements import cdav +from caldav.elements import dav +from caldav.lib import error +from caldav.lib.python_utilities import to_normal_str +from caldav.lib.python_utilities import to_wire +from caldav.lib.url import URL +from caldav.objects import log +from caldav.requests import HTTPBearerAuth + +if TYPE_CHECKING: + pass + +if sys.version_info < (3, 9): + from typing import Iterable, Mapping +else: + from collections.abc import Iterable, Mapping + +if sys.version_info < (3, 11): + from typing_extensions import Self +else: + from typing import Self + +""" +The ``DAVClient`` class handles the basic communication with a +CalDAV server. In 1.x the recommended usage of the library is to +start constructing a DAVClient object. In 2.0 the function +``get_davclient`` was added as the new recommended way to get a +DAVClient object. In later versions there may be a ``get_calendar``, +eliminating the need to deal with DAVClient for most use cases. + +The ``DAVResponse`` class handles the data returned from the server. +In most use-cases library users will not interface with this class +directly. + +``get_davclient`` will return a DAVClient object, based either on +environmental variables, a configuration file or test configuration. +""" + +## TODO: this is also declared in davclient.DAVClient.__init__(...) +## TODO: it should be consolidated, duplication is a bad thing +## TODO: and it's almost certain that we'll forget to update this list +CONNKEYS = set( + ( + "url", + "proxy", + "username", + "password", + "timeout", + "headers", + "huge_tree", + "ssl_verify_cert", + "ssl_cert", + "auth", + "auth_type", + "features", + "enable_rfc6764", + "require_tls", + ) +) + + +def _auto_url( + url, + features, + timeout=10, + ssl_verify_cert=True, + enable_rfc6764=True, + username=None, + require_tls=True, +): + """ + Auto-construct URL from domain and features, with optional RFC6764 discovery. + + Args: + url: User-provided URL, domain, or email address + features: FeatureSet object or dict + timeout: Timeout for RFC6764 well-known URI lookups + ssl_verify_cert: SSL verification setting + enable_rfc6764: Whether to attempt RFC6764 discovery + username: Username to use for discovery if URL is not provided + require_tls: Only accept TLS connections during discovery (default: True) + + Returns: + A tuple of (url_string, discovered_username_or_None) + The discovered_username will be extracted from email addresses like user@example.com + """ + if isinstance(features, dict): + features = FeatureSet(features) + + # If URL already has a path component, don't do discovery + if url and "/" in str(url): + return (url, None) + + # If no URL provided but username contains @, use username for discovery + if not url and username and "@" in str(username) and enable_rfc6764: + log.debug(f"No URL provided, using username for RFC6764 discovery: {username}") + url = username + + # Try RFC6764 discovery first if enabled and we have a bare domain/email + if enable_rfc6764 and url: + from caldav.discovery import discover_caldav, DiscoveryError + + try: + service_info = discover_caldav( + identifier=url, + timeout=timeout, + ssl_verify_cert=ssl_verify_cert + if isinstance(ssl_verify_cert, bool) + else True, + require_tls=require_tls, + ) + if service_info: + log.info( + f"RFC6764 discovered service: {service_info.url} (source: {service_info.source})" + ) + if service_info.username: + log.debug( + f"Username discovered from email: {service_info.username}" + ) + return (service_info.url, service_info.username) + except DiscoveryError as e: + log.debug(f"RFC6764 discovery failed: {e}") + except Exception as e: + log.debug(f"RFC6764 discovery error: {e}") + + # Fall back to feature-based URL construction + url_hints = features.is_supported("auto-connect.url", dict) + # If URL is still empty or looks like an email (from failed discovery attempt), + # replace it with the domain from hints + if (not url or (url and "@" in str(url))) and "domain" in url_hints: + url = url_hints["domain"] + url = f"{url_hints.get('scheme', 'https')}://{url}{url_hints.get('basepath', '')}" + return (url, None) + + +class DAVResponse: + """ + This class is a response from a DAV request. It is instantiated from + the DAVClient class. End users of the library should not need to + know anything about this class. Since we often get XML responses, + it tries to parse it into `self.tree` + """ + + raw = "" + reason: str = "" + tree: Optional[_Element] = None + headers: CaseInsensitiveDict = None + status: int = 0 + davclient = None + huge_tree: bool = False + + def __init__( + self, response: Response, davclient: Optional["DAVClient"] = None + ) -> None: + self.headers = response.headers + self.status = response.status_code + log.debug("response headers: " + str(self.headers)) + log.debug("response status: " + str(self.status)) + + self._raw = response.content + self.davclient = davclient + if davclient: + self.huge_tree = davclient.huge_tree + + content_type = self.headers.get("Content-Type", "") + xml = ["text/xml", "application/xml"] + no_xml = ["text/plain", "text/calendar", "application/octet-stream"] + expect_xml = any((content_type.startswith(x) for x in xml)) + expect_no_xml = any((content_type.startswith(x) for x in no_xml)) + if ( + content_type + and not expect_xml + and not expect_no_xml + and response.status_code < 400 + and response.text + ): + error.weirdness(f"Unexpected content type: {content_type}") + try: + content_length = int(self.headers["Content-Length"]) + except: + content_length = -1 + if content_length == 0 or not self._raw: + self._raw = "" + self.tree = None + log.debug("No content delivered") + else: + ## For really huge objects we should pass the object as a stream to the + ## XML parser, like this: + # self.tree = etree.parse(response.raw, parser=etree.XMLParser(remove_blank_text=True)) + ## However, we would also need to decompress on the fly. I won't bother now. + try: + ## https://github.com/python-caldav/caldav/issues/142 + ## We cannot trust the content=type (iCloud, OX and others). + ## We'll try to parse the content as XML no matter + ## the content type given. + self.tree = etree.XML( + self._raw, + parser=etree.XMLParser( + remove_blank_text=True, huge_tree=self.huge_tree + ), + ) + except: + ## Content wasn't XML. What does the content-type say? + ## expect_no_xml means text/plain or text/calendar + ## expect_no_xml -> ok, pass on, with debug logging + ## expect_xml means text/xml or application/xml + ## expect_xml -> raise an error + ## anything else (text/plain, text/html, ''), + ## log an info message and continue (some servers return HTML error pages) + if not expect_no_xml or log.level <= logging.DEBUG: + if not expect_no_xml: + _log = logging.info + else: + _log = logging.debug + ## The statement below may not be true. + ## We may be expecting something else + _log( + "Expected some valid XML from the server, but got this: \n" + + str(self._raw), + exc_info=True, + ) + if expect_xml: + raise + else: + if log.level <= logging.DEBUG: + log.debug(etree.tostring(self.tree, pretty_print=True)) + + ## this if will always be true as for now, see other comments on streaming. + if hasattr(self, "_raw"): + log.debug(self._raw) + # ref https://github.com/python-caldav/caldav/issues/112 stray CRs may cause problems + if isinstance(self._raw, bytes): + self._raw = self._raw.replace(b"\r\n", b"\n") + elif isinstance(self._raw, str): + self._raw = self._raw.replace("\r\n", "\n") + self.status = response.status_code + ## ref https://github.com/python-caldav/caldav/issues/81, + ## incidents with a response without a reason has been + ## observed + try: + self.reason = response.reason + except AttributeError: + self.reason = "" + + @property + def raw(self) -> str: + ## TODO: this should not really be needed? + if not hasattr(self, "_raw"): + self._raw = etree.tostring(cast(_Element, self.tree), pretty_print=True) + return to_normal_str(self._raw) + + def _strip_to_multistatus(self): + """ + The general format of inbound data is something like this: + + + (...) + (...) + (...) + + + but sometimes the multistatus and/or xml element is missing in + self.tree. We don't want to bother with the multistatus and + xml tags, we just want the response list. + + An "Element" in the lxml library is a list-like object, so we + should typically return the element right above the responses. + If there is nothing but a response, return it as a list with + one element. + + (The equivalent of this method could probably be found with a + simple XPath query, but I'm not much into XPath) + """ + tree = self.tree + if tree.tag == "xml" and tree[0].tag == dav.MultiStatus.tag: + return tree[0] + if tree.tag == dav.MultiStatus.tag: + return self.tree + return [self.tree] + + def validate_status(self, status: str) -> None: + """ + status is a string like "HTTP/1.1 404 Not Found". 200, 207 and + 404 are considered good statuses. The SOGo caldav server even + returns "201 created" when doing a sync-report, to indicate + that a resource was created after the last sync-token. This + makes sense to me, but I've only seen it from SOGo, and it's + not in accordance with the examples in rfc6578. + """ + if ( + " 200 " not in status + and " 201 " not in status + and " 207 " not in status + and " 404 " not in status + ): + raise error.ResponseError(status) + + def _parse_response(self, response) -> Tuple[str, List[_Element], Optional[Any]]: + """ + One response should contain one or zero status children, one + href tag and zero or more propstats. Find them, assert there + isn't more in the response and return those three fields + """ + status = None + href: Optional[str] = None + propstats: List[_Element] = [] + check_404 = False ## special for purelymail + error.assert_(response.tag == dav.Response.tag) + for elem in response: + if elem.tag == dav.Status.tag: + error.assert_(not status) + status = elem.text + error.assert_(status) + self.validate_status(status) + elif elem.tag == dav.Href.tag: + assert not href + # Fix for https://github.com/python-caldav/caldav/issues/471 + # Confluence server quotes the user email twice. We unquote it manually. + if "%2540" in elem.text: + elem.text = elem.text.replace("%2540", "%40") + href = unquote(elem.text) + elif elem.tag == dav.PropStat.tag: + propstats.append(elem) + elif elem.tag == "{DAV:}error": + ## This happens with purelymail on a 404. + ## This code is mostly moot, but in debug + ## mode I want to be sure we do not toss away any data + children = elem.getchildren() + error.assert_(len(children) == 1) + error.assert_( + children[0].tag == "{https://purelymail.com}does-not-exist" + ) + check_404 = True + else: + ## i.e. purelymail may contain one more tag, ... + ## This is probably not a breach of the standard. It may + ## probably be ignored. But it's something we may want to + ## know. + error.weirdness("unexpected element found in response", elem) + error.assert_(href) + if check_404: + error.assert_("404" in status) + ## TODO: is this safe/sane? + ## Ref https://github.com/python-caldav/caldav/issues/435 the paths returned may be absolute URLs, + ## but the caller expects them to be paths. Could we have issues when a server has same path + ## but different URLs for different elements? Perhaps href should always be made into an URL-object? + if ":" in href: + href = unquote(URL(href).path) + return (cast(str, href), propstats, status) + + def find_objects_and_props(self) -> Dict[str, Dict[str, _Element]]: + """Check the response from the server, check that it is on an expected format, + find hrefs and props from it and check statuses delivered. + + The parsed data will be put into self.objects, a dict {href: + {proptag: prop_element}}. Further parsing of the prop_element + has to be done by the caller. + + self.sync_token will be populated if found, self.objects will be populated. + """ + self.objects: Dict[str, Dict[str, _Element]] = {} + self.statuses: Dict[str, str] = {} + + if "Schedule-Tag" in self.headers: + self.schedule_tag = self.headers["Schedule-Tag"] + + responses = self._strip_to_multistatus() + for r in responses: + if r.tag == dav.SyncToken.tag: + self.sync_token = r.text + continue + error.assert_(r.tag == dav.Response.tag) + + (href, propstats, status) = self._parse_response(r) + ## I would like to do this assert here ... + # error.assert_(not href in self.objects) + ## but then there was https://github.com/python-caldav/caldav/issues/136 + if href not in self.objects: + self.objects[href] = {} + self.statuses[href] = status + + ## The properties may be delivered either in one + ## propstat with multiple props or in multiple + ## propstat + for propstat in propstats: + cnt = 0 + status = propstat.find(dav.Status.tag) + error.assert_(status is not None) + if status is not None and status.text is not None: + error.assert_(len(status) == 0) + cnt += 1 + self.validate_status(status.text) + ## if a prop was not found, ignore it + if " 404 " in status.text: + continue + for prop in propstat.iterfind(dav.Prop.tag): + cnt += 1 + for theprop in prop: + self.objects[href][theprop.tag] = theprop + + ## there shouldn't be any more elements except for status and prop + error.assert_(cnt == len(propstat)) + + return self.objects + + def _expand_simple_prop( + self, proptag, props_found, multi_value_allowed=False, xpath=None + ): + values = [] + if proptag in props_found: + prop_xml = props_found[proptag] + for item in prop_xml.items(): + if proptag == "{urn:ietf:params:xml:ns:caldav}calendar-data": + if ( + item[0].lower().endswith("content-type") + and item[1].lower() == "text/calendar" + ): + continue + if item[0].lower().endswith("version") and item[1] in ("2", "2.0"): + continue + log.error( + f"If you see this, please add a report at https://github.com/python-caldav/caldav/issues/209 - in _expand_simple_prop, dealing with {proptag}, extra item found: {'='.join(item)}." + ) + if not xpath and len(prop_xml) == 0: + if prop_xml.text: + values.append(prop_xml.text) + else: + _xpath = xpath if xpath else ".//*" + leafs = prop_xml.findall(_xpath) + values = [] + for leaf in leafs: + error.assert_(not leaf.items()) + if leaf.text: + values.append(leaf.text) + else: + values.append(leaf.tag) + if multi_value_allowed: + return values + else: + if not values: + return None + error.assert_(len(values) == 1) + return values[0] + + ## TODO: word "expand" does not feel quite right. + def expand_simple_props( + self, + props: Iterable[BaseElement] = None, + multi_value_props: Iterable[Any] = None, + xpath: Optional[str] = None, + ) -> Dict[str, Dict[str, str]]: + """ + The find_objects_and_props() will stop at the xml element + below the prop tag. This method will expand those props into + text. + + Executes find_objects_and_props if not run already, then + modifies and returns self.objects. + """ + props = props or [] + multi_value_props = multi_value_props or [] + + if not hasattr(self, "objects"): + self.find_objects_and_props() + for href in self.objects: + props_found = self.objects[href] + for prop in props: + if prop.tag is None: + continue + + props_found[prop.tag] = self._expand_simple_prop( + prop.tag, props_found, xpath=xpath + ) + for prop in multi_value_props: + if prop.tag is None: + continue + + props_found[prop.tag] = self._expand_simple_prop( + prop.tag, props_found, xpath=xpath, multi_value_allowed=True + ) + # _Element objects in self.objects are parsed to str, thus the need to cast the return + return cast(Dict[str, Dict[str, str]], self.objects) + + +class DAVClient: + """ + Basic client for webdav, uses the niquests lib; gives access to + low-level operations towards the caldav server. + + Unless you have special needs, you should probably care most about + the constructor (__init__), the principal method and the calendar method. + """ + + proxy: Optional[str] = None + url: URL = None + huge_tree: bool = False + + def __init__( + self, + url: Optional[str] = "", + proxy: Optional[str] = None, + username: Optional[str] = None, + password: Optional[str] = None, + auth: Optional[AuthBase] = None, + auth_type: Optional[str] = None, + timeout: Optional[int] = None, + ssl_verify_cert: Union[bool, str] = True, + ssl_cert: Union[str, Tuple[str, str], None] = None, + headers: Mapping[str, str] = None, + huge_tree: bool = False, + features: Union[FeatureSet, dict, str] = None, + enable_rfc6764: bool = True, + require_tls: bool = True, + ) -> None: + """ + Sets up a HTTPConnection object towards the server in the url. + + Args: + url: A fully qualified url, domain name, or email address. Can be omitted if username + is an email address (RFC6764 discovery will use the username). + Examples: + - Full URL: `https://caldav.example.com/dav/` + - Domain: `example.com` (will attempt RFC6764 discovery if enable_rfc6764=True) + - Email: `user@example.com` (will attempt RFC6764 discovery if enable_rfc6764=True) + - URL with auth: `scheme://user:pass@hostname:port` + - Omit URL: Use `username='user@example.com'` for discovery + username: Username for authentication. If url is omitted and username contains @, + RFC6764 discovery will be attempted using the username as email address. + proxy: A string defining a proxy server: `scheme://hostname:port`. Scheme defaults to http, port defaults to 8080. + auth: A niquests.auth.AuthBase or requests.auth.AuthBase object, may be passed instead of username/password. username and password should be passed as arguments or in the URL + timeout and ssl_verify_cert are passed to niquests.request. + if auth_type is given, the auth-object will be auto-created. Auth_type can be ``bearer``, ``digest`` or ``basic``. Things are likely to work without ``auth_type`` set, but if nothing else the number of requests to the server will be reduced, and some servers may require this to squelch warnings of unexpected HTML delivered from the + server etc. + ssl_verify_cert can be the path of a CA-bundle or False. + huge_tree: boolean, enable XMLParser huge_tree to handle big events, beware of security issues, see : https://lxml.de/api/lxml.etree.XMLParser-class.html + features: The default, None, will in version 2.x enable all existing workarounds in the code for backward compability. Otherwise it will expect a FeatureSet or a dict as defined in `caldav.compatibility_hints` and use that to figure out what workarounds are needed. + enable_rfc6764: boolean, enable RFC6764 DNS-based service discovery for CalDAV/CardDAV. + Default: True. When enabled and a domain or email address is provided as url, + the library will attempt to discover the CalDAV service using: + 1. DNS SRV records (_caldavs._tcp / _caldav._tcp) + 2. DNS TXT records for path information + 3. Well-Known URIs (/.well-known/caldav) + Set to False to disable automatic discovery and rely only on feature hints. + SECURITY: See require_tls parameter for security considerations. + require_tls: boolean, require TLS (HTTPS) for discovered services. Default: True. + When True, RFC6764 discovery will ONLY accept HTTPS connections, + preventing DNS-based downgrade attacks where malicious DNS could + redirect to unencrypted HTTP. Set to False ONLY if you need to + support non-TLS servers and trust your DNS infrastructure. + This parameter has no effect if enable_rfc6764=False. + + The niquests library will honor a .netrc-file, if such a file exists + username and password may be omitted. + + THe niquest library will honor standard proxy environmental variables like + HTTP_PROXY, HTTPS_PROXY and ALL_PROXY. See https://niquests.readthedocs.io/en/latest/user/advanced.html#proxies + + If the caldav server is behind a proxy or replies with html instead of xml + when returning 401, warnings will be printed which might be unwanted. + Check auth parameter for details. + """ + headers = headers or {} + + ## Deprecation TODO: give a warning, user should use get_davclient or auto_calendar instead. Probably. + + if isinstance(features, str): + features = getattr(caldav.compatibility_hints, features) + self.features = FeatureSet(features) + self.huge_tree = huge_tree + + try: + multiplexed = self.features.is_supported("http.multiplexing") + self.session = requests.Session(multiplexed=multiplexed) + except TypeError: + self.session = requests.Session() + + url, discovered_username = _auto_url( + url, + self.features, + timeout=timeout or 10, + ssl_verify_cert=ssl_verify_cert, + enable_rfc6764=enable_rfc6764, + username=username, + require_tls=require_tls, + ) + + log.debug("url: " + str(url)) + self.url = URL.objectify(url) + # Prepare proxy info + if proxy is not None: + _proxy = proxy + # niquests library expects the proxy url to have a scheme + if "://" not in proxy: + _proxy = self.url.scheme + "://" + proxy + + # add a port is one is not specified + # TODO: this will break if using basic auth and embedding + # username:password in the proxy URL + p = _proxy.split(":") + if len(p) == 2: + _proxy += ":8080" + log.debug("init - proxy: %s" % (_proxy)) + + self.proxy = _proxy + + # Build global headers + self.headers = CaseInsensitiveDict( + { + "User-Agent": "python-caldav/" + __version__, + "Content-Type": "text/xml", + "Accept": "text/xml, text/calendar", + } + ) + self.headers.update(headers or {}) + if self.url.username is not None: + username = unquote(self.url.username) + password = unquote(self.url.password) + + # Use discovered username if no explicit username was provided + if username is None and discovered_username is not None: + username = discovered_username + log.debug(f"Using discovered username from RFC6764: {username}") + + self.username = username + self.password = password + self.auth = auth + self.auth_type = auth_type + + ## I had problems with passwords with non-ascii letters in it ... + if isinstance(self.password, str): + self.password = self.password.encode("utf-8") + if auth and self.auth_type: + logging.error( + "both auth object and auth_type sent to DAVClient. The latter will be ignored." + ) + elif self.auth_type: + self.build_auth_object() + + # TODO: it's possible to force through a specific auth method here, + # but no test code for this. + self.timeout = timeout + self.ssl_verify_cert = ssl_verify_cert + self.ssl_cert = ssl_cert + self.url = self.url.unauth() + log.debug("self.url: " + str(url)) + + self._principal = None + + def __enter__(self) -> Self: + ## Used for tests, to set up a temporarily test server + if hasattr(self, "setup"): + try: + self.setup() + except: + self.setup(self) + return self + + def __exit__( + self, + exc_type: Optional[BaseException] = None, + exc_value: Optional[BaseException] = None, + traceback: Optional[TracebackType] = None, + ) -> None: + self.close() + ## Used for tests, to tear down a temporarily test server + if hasattr(self, "teardown"): + try: + self.teardown() + except: + self.teardown(self) + + def close(self) -> None: + """ + Closes the DAVClient's session object + """ + self.session.close() + + def principals(self, name=None): + """ + Instead of returning the current logged-in principal, it attempts to query for all principals. This may or may not work dependent on the permissions and implementation of the calendar server. + """ + if name: + name_filter = [ + dav.PropertySearch() + + [dav.Prop() + [dav.DisplayName()]] + + dav.Match(value=name) + ] + else: + name_filter = [] + + query = ( + dav.PrincipalPropertySearch() + + name_filter + + [dav.Prop(), cdav.CalendarHomeSet(), dav.DisplayName()] + ) + response = self.report(self.url, etree.tostring(query.xmlelement())) + + ## Possibly we should follow redirects (response status 3xx), but as + ## for now we're just treating it in the same way as 4xx and 5xx - + ## probably the server did not support the operation + if response.status >= 300: + raise error.ReportError( + f"{response.status} {response.reason} - {response.raw}" + ) + + principal_dict = response.find_objects_and_props() + ret = [] + for x in principal_dict: + p = principal_dict[x] + if not dav.DisplayName.tag in p: + continue + name = p[dav.DisplayName.tag].text + error.assert_(not p[dav.DisplayName.tag].getchildren()) + error.assert_(not p[dav.DisplayName.tag].items()) + chs = p[cdav.CalendarHomeSet.tag] + error.assert_(not chs.items()) + error.assert_(not chs.text) + chs_href = chs.getchildren() + error.assert_(len(chs_href) == 1) + error.assert_(not chs_href[0].items()) + error.assert_(not chs_href[0].getchildren()) + chs_url = chs_href[0].text + calendar_home_set = CalendarSet(client=self, url=chs_url) + ret.append( + Principal( + client=self, url=x, name=name, calendar_home_set=calendar_home_set + ) + ) + return ret + + def principal(self, *largs, **kwargs): + """ + Convenience method, it gives a bit more object-oriented feel to + write client.principal() than Principal(client). + + This method returns a :class:`caldav.Principal` object, with + higher-level methods for dealing with the principals + calendars. + """ + if not self._principal: + self._principal = Principal(client=self, *largs, **kwargs) + return self._principal + + def calendar(self, **kwargs): + """Returns a calendar object. + + Typically, a URL should be given as a named parameter (url) + + No network traffic will be initiated by this method. + + If you don't know the URL of the calendar, use + client.principal().calendar(...) instead, or + client.principal().calendars() + """ + return Calendar(client=self, **kwargs) + + def check_dav_support(self) -> Optional[str]: + """ + Does a probe towards the server and returns True if it says it supports RFC4918 / DAV + """ + try: + ## SOGo does not return the full capability list on the caldav + ## root URL, and that's OK according to the RFC ... so apparently + ## we need to do an extra step here to fetch the URL of some + ## element that should come with caldav extras. + ## Anyway, packing this into a try-except in case it fails. + response = self.options(self.principal().url) + except: + response = self.options(str(self.url)) + return response.headers.get("DAV", None) + + def check_cdav_support(self) -> bool: + """ + Does a probe towards the server and returns True if it says it supports RFC4791 / CalDAV + """ + support_list = self.check_dav_support() + return support_list is not None and "calendar-access" in support_list + + def check_scheduling_support(self) -> bool: + """ + Does a probe towards the server and returns True if it says it supports RFC6833 / CalDAV Scheduling + """ + support_list = self.check_dav_support() + return support_list is not None and "calendar-auto-schedule" in support_list + + def propfind( + self, url: Optional[str] = None, props: str = "", depth: int = 0 + ) -> DAVResponse: + """ + Send a propfind request. + + Parameters + ---------- + url : URL + url for the root of the propfind. + props : xml + properties we want + depth : int + maximum recursion depth + + Returns + ------- + DAVResponse + """ + return self.request( + url or str(self.url), "PROPFIND", props, {"Depth": str(depth)} + ) + + def proppatch(self, url: str, body: str, dummy: None = None) -> DAVResponse: + """ + Send a proppatch request. + + Args: + url: url for the root of the propfind. + body: XML propertyupdate request + dummy: compatibility parameter + + Returns: + DAVResponse + """ + return self.request(url, "PROPPATCH", body) + + def report(self, url: str, query: str = "", depth: int = 0) -> DAVResponse: + """ + Send a report request. + + Args: + url: url for the root of the propfind. + query: XML request + depth: maximum recursion depth + + Returns + DAVResponse + """ + return self.request( + url, + "REPORT", + query, + {"Depth": str(depth), "Content-Type": 'application/xml; charset="utf-8"'}, + ) + + def mkcol(self, url: str, body: str, dummy: None = None) -> DAVResponse: + """ + Send a MKCOL request. + + MKCOL is basically not used with caldav, one should use + MKCALENDAR instead. However, some calendar servers MAY allow + "subcollections" to be made in a calendar, by using the MKCOL + query. As for 2020-05, this method is not exercised by test + code or referenced anywhere else in the caldav library, it's + included just for the sake of completeness. And, perhaps this + DAVClient class can be used for vCards and other WebDAV + purposes. + + Args: + url: url for the root of the mkcol + body: XML request + dummy: compatibility parameter + + Returns: + DAVResponse + """ + return self.request(url, "MKCOL", body) + + def mkcalendar(self, url: str, body: str = "", dummy: None = None) -> DAVResponse: + """ + Send a mkcalendar request. + + Args: + url: url for the root of the mkcalendar + body: XML request + dummy: compatibility parameter + + Returns: + DAVResponse + """ + return self.request(url, "MKCALENDAR", body) + + def put( + self, url: str, body: str, headers: Mapping[str, str] = None + ) -> DAVResponse: + """ + Send a put request. + """ + return self.request(url, "PUT", body, headers or {}) + + def post( + self, url: str, body: str, headers: Mapping[str, str] = None + ) -> DAVResponse: + """ + Send a POST request. + """ + return self.request(url, "POST", body, headers or {}) + + def delete(self, url: str) -> DAVResponse: + """ + Send a delete request. + """ + return self.request(url, "DELETE") + + def options(self, url: str) -> DAVResponse: + """ + Send an options request. + """ + return self.request(url, "OPTIONS") + + def extract_auth_types(self, header: str): + """This is probably meant for internal usage. It takes the + headers it got from the server and figures out what + authentication types the server supports + """ + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate#syntax + return {h.split()[0] for h in header.lower().split(",")} + + def build_auth_object(self, auth_types: Optional[List[str]] = None): + """Fixes self.auth. If ``self.auth_type`` is given, then + insist on using this one. If not, then assume auth_types to + be a list of acceptable auth types and choose the most + appropriate one (prefer digest or basic if username is given, + and bearer if password is given). + + Args: + auth_types - A list/tuple of acceptable auth_types + """ + auth_type = self.auth_type + if not auth_type and not auth_types: + raise error.AuthorizationError( + "No auth-type given. This shouldn't happen. Raise an issue at https://github.com/python-caldav/caldav/issues/ or by email noauthtype@plann.no" + ) + if auth_types and auth_type and auth_type not in auth_types: + raise error.AuthorizationError( + reason=f"Configuration specifies to use {auth_type}, but server only accepts {auth_types}" + ) + if not auth_type and auth_types: + if self.username and "digest" in auth_types: + auth_type = "digest" + elif self.username and "basic" in auth_types: + auth_type = "basic" + elif self.password and "bearer" in auth_types: + auth_type = "bearer" + elif "bearer" in auth_types: + raise error.AuthorizationError( + reason="Server provides bearer auth, but no password given. The bearer token should be configured as password" + ) + + if auth_type == "digest": + self.auth = requests.auth.HTTPDigestAuth(self.username, self.password) + elif auth_type == "basic": + self.auth = requests.auth.HTTPBasicAuth(self.username, self.password) + elif auth_type == "bearer": + self.auth = HTTPBearerAuth(self.password) + + def request( + self, + url: str, + method: str = "GET", + body: str = "", + headers: Mapping[str, str] = None, + ) -> DAVResponse: + """ + Actually sends the request, and does the authentication + """ + headers = headers or {} + + combined_headers = self.headers.copy() + combined_headers.update(headers or {}) + if (body is None or body == "") and "Content-Type" in combined_headers: + del combined_headers["Content-Type"] + + # objectify the url + url_obj = URL.objectify(url) + + proxies = None + if self.proxy is not None: + proxies = {url_obj.scheme: self.proxy} + log.debug("using proxy - %s" % (proxies)) + + log.debug( + "sending request - method={0}, url={1}, headers={2}\nbody:\n{3}".format( + method, str(url_obj), combined_headers, to_normal_str(body) + ) + ) + + try: + r = self.session.request( + method, + str(url_obj), + data=to_wire(body), + headers=combined_headers, + proxies=proxies, + auth=self.auth, + timeout=self.timeout, + verify=self.ssl_verify_cert, + cert=self.ssl_cert, + ) + log.debug("server responded with %i %s" % (r.status_code, r.reason)) + if ( + r.status_code == 401 + and "text/html" in self.headers.get("Content-Type", "") + and not self.auth + ): + # The server can return HTML on 401 sometimes (ie. it's behind a proxy) + # The user can avoid logging errors by setting the authentication type by themselves. + msg = ( + "No authentication object was provided. " + "HTML was returned when probing the server for supported authentication types. " + "To avoid logging errors, consider passing the auth_type connection parameter" + ) + if r.headers.get("WWW-Authenticate"): + auth_types = [ + t + for t in self.extract_auth_types(r.headers["WWW-Authenticate"]) + if t in ["basic", "digest", "bearer"] + ] + if auth_types: + msg += "\nSupported authentication types: %s" % ( + ", ".join(auth_types) + ) + log.warning(msg) + response = DAVResponse(r, self) + except: + ## this is a workaround needed due to some weird server + ## that would just abort the connection rather than send a + ## 401 when an unauthenticated request with a body was + ## sent to the server - ref https://github.com/python-caldav/caldav/issues/158 + if self.auth or not self.password: + raise + r = self.session.request( + method="GET", + url=str(url_obj), + headers=combined_headers, + proxies=proxies, + timeout=self.timeout, + verify=self.ssl_verify_cert, + cert=self.ssl_cert, + ) + if not r.status_code == 401: + raise + + ## Returned headers + r_headers = CaseInsensitiveDict(r.headers) + if ( + r.status_code == 401 + and "WWW-Authenticate" in r_headers + and not self.auth + and (self.username or self.password) + ): + auth_types = self.extract_auth_types(r_headers["WWW-Authenticate"]) + self.build_auth_object(auth_types) + + if not self.auth: + raise NotImplementedError( + "The server does not provide any of the currently " + "supported authentication methods: basic, digest, bearer" + ) + + return self.request(url, method, body, headers) + + elif ( + r.status_code == 401 + and "WWW-Authenticate" in r_headers + and self.auth + and self.password + and isinstance(self.password, bytes) + ): + ## TODO: this has become a mess and should be refactored. + ## (Arguably, this logic doesn't belong here at all. + ## with niquests it's possible to just pass the username + ## and password, maybe we should try that?) + + ## Most likely we're here due to wrong username/password + ## combo, but it could also be a multiplexing problem. + if ( + self.features.is_supported("http.multiplexing", return_defaults=False) + is None + ): + self.session = requests.Session() + self.features.set_feature("http.multiplexing", "unknown") + ## If this one also fails, we give up + ret = self.request(str(url_obj), method, body, headers) + self.features.set_feature("http.multiplexing", False) + return ret + + ## Most likely we're here due to wrong username/password + ## combo, but it could also be charset problems. Some + ## (ancient) servers don't like UTF-8 binary auth with + ## Digest authentication. An example are old SabreDAV + ## based servers. Not sure about UTF-8 and Basic Auth, + ## but likely the same. so retry if password is a bytes + ## sequence and not a string (see commit 13a4714, which + ## introduced this regression) + + auth_types = self.extract_auth_types(r_headers["WWW-Authenticate"]) + self.password = self.password.decode() + self.build_auth_object(auth_types) + + self.username = None + self.password = None + + return self.request(str(url_obj), method, body, headers) + + if error.debug_dump_communication: + import datetime + from tempfile import NamedTemporaryFile + + with NamedTemporaryFile(prefix="caldavcomm", delete=False) as commlog: + commlog.write(b"=" * 80 + b"\n") + commlog.write(f"{datetime.datetime.now():%FT%H:%M:%S}".encode("utf-8")) + commlog.write(b"\n====>\n") + commlog.write(f"{method} {url}\n".encode("utf-8")) + commlog.write( + b"\n".join(to_wire(f"{x}: {headers[x]}") for x in headers) + ) + commlog.write(b"\n\n") + commlog.write(to_wire(body)) + commlog.write(b"<====\n") + commlog.write(f"{response.status} {response.reason}".encode("utf-8")) + commlog.write( + b"\n".join( + to_wire(f"{x}: {response.headers[x]}") for x in response.headers + ) + ) + commlog.write(b"\n\n") + ct = response.headers.get("Content-Type", "") + if response.tree is not None: + commlog.write( + to_wire(etree.tostring(response.tree, pretty_print=True)) + ) + else: + commlog.write(to_wire(response._raw)) + commlog.write(b"\n") + + # this is an error condition that should be raised to the application + if ( + response.status == requests.codes.forbidden + or response.status == requests.codes.unauthorized + ): + try: + reason = response.reason + except AttributeError: + reason = "None given" + raise error.AuthorizationError(url=str(url_obj), reason=reason) + + return response + + +def auto_calendars( + config_file: str = None, + config_section: str = "default", + testconfig: bool = False, + environment: bool = True, + config_data: dict = None, + config_name: str = None, +) -> Iterable["Calendar"]: + """ + This will replace plann.lib.findcalendars() + """ + raise NotImplementedError("auto_calendars not implemented yet") + + +def auto_calendar(*largs, **kwargs) -> Iterable["Calendar"]: + """ + Alternative to auto_calendars - in most use cases, one calendar suffices + """ + return next(auto_calendars(*largs, **kwargs), None) + + +def auto_conn(*largs, config_data: dict = None, **kwargs): + """A quite stubbed verison of get_davclient was included in the + v1.5-release as auto_conn, but renamed a few days later. Probably + nobody except my caldav tester project uses auto_conn, but as a + thumb of rule anything released should stay "deprecated" for at + least one major release before being removed. + + TODO: remove in version 3.0 + """ + warnings.warn( + "auto_conn was renamed get_davclient", + DeprecationWarning, + stacklevel=2, + ) + if config_data: + kwargs.update(config_data) + return get_davclient(*largs, **kwargs) + + +def get_davclient( + check_config_file: bool = True, + config_file: str = None, + config_section: str = None, + testconfig: bool = False, + environment: bool = True, + name: str = None, + **config_data, +) -> "DAVClient": + """ + This function will yield a DAVClient object. It will not try to + connect (see auto_calendars for that). It will read configuration + from various sources, dependent on the parameters given, in this + order: + + * Data from the parameters given + * Environment variables prepended with `CALDAV_`, like `CALDAV_URL`, `CALDAV_USERNAME`, `CALDAV_PASSWORD`. + * Environment variables `PYTHON_CALDAV_USE_TEST_SERVER` and `CALDAV_CONFIG_FILE` will be honored if environment is set + * Data from `./tests/conf.py` or `./conf.py` (this includes the possibility to spin up a test server) + * Configuration file. Documented in the plann project as for now. (TODO - move it) + """ + if config_data: + return DAVClient(**config_data) + + if testconfig or (environment and os.environ.get("PYTHON_CALDAV_USE_TEST_SERVER")): + sys.path.insert(0, "tests") + sys.path.insert(1, ".") + ## TODO: move the code from client into here + try: + from conf import client + + idx = os.environ.get("PYTHON_CALDAV_TEST_SERVER_IDX") + try: + idx = int(idx) + except (ValueError, TypeError): + idx = None + name = name or os.environ.get("PYTHON_CALDAV_TEST_SERVER_NAME") + if name and not idx: + try: + idx = int(name) + name = None + except ValueError: + pass + conn = client(idx, name) + if conn: + return conn + except ImportError: + pass + finally: + sys.path = sys.path[2:] + + if environment: + conf = {} + for conf_key in ( + x + for x in os.environ + if x.startswith("CALDAV_") and not x.startswith("CALDAV_CONFIG") + ): + conf[conf_key[7:].lower()] = os.environ[conf_key] + if conf: + return DAVClient(**conf) + if not config_file: + config_file = os.environ.get("CALDAV_CONFIG_FILE") + if not config_section: + config_section = os.environ.get("CALDAV_CONFIG_SECTION") + + if check_config_file: + ## late import in 2.0, as the config stuff isn't properly tested + from . import config + + if not config_section: + config_section = "default" + + cfg = config.read_config(config_file) + if cfg: + section = config.config_section(cfg, config_section) + conn_params = {} + for k in section: + if k.startswith("caldav_") and section[k]: + key = k[7:] + if key == "pass": + key = "password" + if key == "user": + key = "username" + conn_params[key] = section[k] + if conn_params: + return DAVClient(**conn_params) diff --git a/.venv/lib/python3.9/site-packages/caldav/davobject.py b/.venv/lib/python3.9/site-packages/caldav/davobject.py new file mode 100644 index 0000000..efa07d7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/davobject.py @@ -0,0 +1,430 @@ +import logging +import sys +from typing import Any +from typing import List +from typing import Optional +from typing import Tuple +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union +from urllib.parse import ParseResult +from urllib.parse import quote +from urllib.parse import SplitResult +from urllib.parse import unquote + +from lxml import etree + +try: + from typing import ClassVar, Optional, Union, Type + + TimeStamp = Optional[Union[date, datetime]] +except: + pass + +if TYPE_CHECKING: + from icalendar import vCalAddress + + from .davclient import DAVClient + +if sys.version_info < (3, 9): + from typing import Callable, Container, Iterable, Iterator, Sequence + + from typing_extensions import DefaultDict, Literal +else: + from collections import defaultdict as DefaultDict + from collections.abc import Callable, Container, Iterable, Iterator, Sequence + from typing import Literal + +if sys.version_info < (3, 11): + from typing_extensions import Self +else: + from typing import Self + +from .elements import cdav, dav +from .elements.base import BaseElement +from .lib import error +from .lib.error import errmsg +from .lib.python_utilities import to_wire +from .lib.url import URL + +_CC = TypeVar("_CC", bound="CalendarObjectResource") +log = logging.getLogger("caldav") + + +""" +This file contains one class, the DAVObject which is the base +class for Calendar, Principal, CalendarObjectResource (Event) and many +others. There is some code here for handling some of the DAV-related +communication, and the class lists some common methods that are shared +on all kind of objects. Library users should not need to know a lot +about the DAVObject class, should never need to initialize one, but +may encounter inheritated methods coming from this class. +""" + + +class DAVObject: + """ + Base class for all DAV objects. Can be instantiated by a client + and an absolute or relative URL, or from the parent object. + """ + + id: Optional[str] = None + url: Optional[URL] = None + client: Optional["DAVClient"] = None + parent: Optional["DAVObject"] = None + name: Optional[str] = None + + def __init__( + self, + client: Optional["DAVClient"] = None, + url: Union[str, ParseResult, SplitResult, URL, None] = None, + parent: Optional["DAVObject"] = None, + name: Optional[str] = None, + id: Optional[str] = None, + props=None, + **extra, + ) -> None: + """ + Default constructor. + + Args: + client: A DAVClient instance + url: The url for this object. May be a full URL or a relative URL. + parent: The parent object - used when creating objects + name: A displayname - to be removed at some point, see https://github.com/python-caldav/caldav/issues/128 for details + props: a dict with known properties for this object + id: The resource id (UID for an Event) + """ + + if client is None and parent is not None: + client = parent.client + self.client = client + self.parent = parent + self.name = name + self.id = id + self.props = props or {} + self.extra_init_options = extra + # url may be a path relative to the caldav root + if client and url: + self.url = client.url.join(url) + elif url is None: + self.url = None + else: + self.url = URL.objectify(url) + + @property + def canonical_url(self) -> str: + if self.url is None: + raise ValueError("Unexpected value None for self.url") + return str(self.url.canonical()) + + def children(self, type: Optional[str] = None) -> List[Tuple[URL, Any, Any]]: + """List children, using a propfind (resourcetype) on the parent object, + at depth = 1. + + TODO: This is old code, it's querying for DisplayName and + ResourceTypes prop and returning a tuple of those. Those two + are relatively arbitrary. I think it's mostly only calendars + having DisplayName, but it may make sense to ask for the + children of a calendar also as an alternative way to get all + events? It should be redone into a more generic method, and + it should probably return a dict rather than a tuple. We + should also look over to see if there is any code duplication. + """ + ## Late import to avoid circular imports + from .collection import CalendarSet + + c = [] + + depth = 1 + + if self.url is None: + raise ValueError("Unexpected value None for self.url") + + props = [dav.DisplayName()] + multiprops = [dav.ResourceType()] + props_multiprops = props + multiprops + response = self._query_properties(props_multiprops, depth) + properties = response.expand_simple_props( + props=props, multi_value_props=multiprops + ) + + for path in properties: + resource_types = properties[path][dav.ResourceType.tag] + resource_name = properties[path][dav.DisplayName.tag] + + if type is None or type in resource_types: + url = URL(path) + if url.hostname is None: + # Quote when path is not a full URL + path = quote(path) + # TODO: investigate the RFCs thoroughly - why does a "get + # members of this collection"-request also return the + # collection URL itself? + # And why is the strip_trailing_slash-method needed? + # The collection URL should always end with a slash according + # to RFC 2518, section 5.2. + if (isinstance(self, CalendarSet) and type == cdav.Calendar.tag) or ( + self.url.canonical().strip_trailing_slash() + != self.url.join(path).canonical().strip_trailing_slash() + ): + c.append((self.url.join(path), resource_types, resource_name)) + + ## TODO: return objects rather than just URLs, and include + ## the properties we've already fetched + return c + + def _query_properties( + self, props: Optional[Sequence[BaseElement]] = None, depth: int = 0 + ): + """ + This is an internal method for doing a propfind query. It's a + result of code-refactoring work, attempting to consolidate + similar-looking code into a common method. + """ + root = None + # build the propfind request + if props is not None and len(props) > 0: + prop = dav.Prop() + props + root = dav.Propfind() + prop + + return self._query(root, depth) + + def _query( + self, + root=None, + depth=0, + query_method="propfind", + url=None, + expected_return_value=None, + ): + """ + This is an internal method for doing a query. It's a + result of code-refactoring work, attempting to consolidate + similar-looking code into a common method. + """ + body = "" + if root: + if hasattr(root, "xmlelement"): + body = etree.tostring( + root.xmlelement(), + encoding="utf-8", + xml_declaration=True, + pretty_print=error.debug_dump_communication, + ) + else: + body = root + if url is None: + url = self.url + ret = getattr(self.client, query_method)(url, body, depth) + if ret.status == 404: + raise error.NotFoundError(errmsg(ret)) + if ( + expected_return_value is not None and ret.status != expected_return_value + ) or ret.status >= 400: + ## COMPATIBILITY HACK - see https://github.com/python-caldav/caldav/issues/309 + ## TODO: server quirks! + body = to_wire(body) + if ( + ret.status == 500 + and b"D:getetag" not in body + and b" Optional[str]: + """ + Wrapper for the :class:`get_properties`, when only one property is wanted + + Args: + + prop: the property to search for + use_cached: don't send anything to the server if we've asked before + + Other parameters are sent directly to the :class:`get_properties` method + """ + ## TODO: use_cached should probably be true + if use_cached: + if prop.tag in self.props: + return self.props[prop.tag] + foo = self.get_properties([prop], **passthrough) + return foo.get(prop.tag, None) + + def get_properties( + self, + props: Optional[Sequence[BaseElement]] = None, + depth: int = 0, + parse_response_xml: bool = True, + parse_props: bool = True, + ): + """Get properties (PROPFIND) for this object. + + With parse_response_xml and parse_props set to True a + best-attempt will be done on decoding the XML we get from the + server - but this works only for properties that don't have + complex types. With parse_response_xml set to False, a + DAVResponse object will be returned, and it's up to the caller + to decode. With parse_props set to false but + parse_response_xml set to true, xml elements will be returned + rather than values. + + Args: + props: ``[dav.ResourceType(), dav.DisplayName(), ...]`` + + Returns: + ``{proptag: value, ...}`` + + """ + from .collection import Principal ## late import to avoid cyclic dependencies + + rc = None + response = self._query_properties(props, depth) + if not parse_response_xml: + return response + + if not parse_props: + properties = response.find_objects_and_props() + else: + properties = response.expand_simple_props(props) + + error.assert_(properties) + + if self.url is None: + raise ValueError("Unexpected value None for self.url") + + path = unquote(self.url.path) + if path.endswith("/"): + exchange_path = path[:-1] + else: + exchange_path = path + "/" + + if path in properties: + rc = properties[path] + elif exchange_path in properties: + if not isinstance(self, Principal): + ## Some caldav servers reports the URL for the current + ## principal to end with / when doing a propfind for + ## current-user-principal - I believe that's a bug, + ## the principal is not a collection and should not + ## end with /. (example in rfc5397 does not end with /). + ## ... but it gets worse ... when doing a propfind on the + ## principal, the href returned may be without the slash. + ## Such inconsistency is clearly a bug. + log.warning( + "potential path handling problem with ending slashes. Path given: %s, path found: %s. %s" + % (path, exchange_path, error.ERR_FRAGMENT) + ) + error.assert_(False) + rc = properties[exchange_path] + elif self.url in properties: + rc = properties[self.url] + elif "/principal/" in properties and path.endswith("/principal/"): + ## Workaround for a known iCloud bug. + ## The properties key is expected to be the same as the path. + ## path is on the format /123456/principal/ but properties key is /principal/ + ## tests apparently passed post bc589093a34f0ed0ef489ad5e9cba048750c9837 and 3ee4e42e2fa8f78b71e5ffd1ef322e4007df7a60, even without this workaround + ## TODO: should probably be investigated more. + ## (observed also by others, ref https://github.com/python-caldav/caldav/issues/168) + rc = properties["/principal/"] + elif "//" in path and path.replace("//", "/") in properties: + ## ref https://github.com/python-caldav/caldav/issues/302 + ## though, it would be nice to find the root cause, + ## self.url should not contain double slashes in the first place + rc = properties[path.replace("//", "/")] + elif len(properties) == 1: + ## Ref https://github.com/python-caldav/caldav/issues/191 ... + ## let's be pragmatic and just accept whatever the server is + ## throwing at us. But we'll log an error anyway. + log.warning( + "Possibly the server has a path handling problem, possibly the URL configured is wrong.\n" + "Path expected: %s, path found: %s %s.\n" + "Continuing, probably everything will be fine" + % (path, str(list(properties)), error.ERR_FRAGMENT) + ) + rc = list(properties.values())[0] + else: + log.warning( + "Possibly the server has a path handling problem. Path expected: %s, paths found: %s %s" + % (path, str(list(properties)), error.ERR_FRAGMENT) + ) + error.assert_(False) + + if parse_props: + if rc is None: + raise ValueError("Unexpected value None for rc") + + self.props.update(rc) + return rc + + def set_properties(self, props: Optional[Any] = None) -> Self: + """ + Set properties (PROPPATCH) for this object. + + * props = [dav.DisplayName('name'), ...] + + Returns: + * self + """ + props = [] if props is None else props + prop = dav.Prop() + props + set = dav.Set() + prop + root = dav.PropertyUpdate() + set + + r = self._query(root, query_method="proppatch") + + statuses = r.tree.findall(".//" + dav.Status.tag) + for s in statuses: + if " 200 " not in s.text: + raise error.PropsetError(s.text) + + return self + + def save(self) -> Self: + """ + Save the object. This is an abstract method, that all classes + derived from DAVObject implement. + + Returns: + * self + """ + raise NotImplementedError() + + def delete(self) -> None: + """ + Delete the object. + """ + if self.url is not None: + if self.client is None: + raise ValueError("Unexpected value None for self.client") + + r = self.client.delete(str(self.url)) + + # TODO: find out why we get 404 + if r.status not in (200, 204, 404): + raise error.DeleteError(errmsg(r)) + + def get_display_name(self): + """ + Get display name (calendar, principal, ...more?) + """ + return self.get_property(dav.DisplayName(), use_cached=True) + + def __str__(self) -> str: + try: + return ( + str(self.get_property(dav.DisplayName(), use_cached=True)) or self.url + ) + except: + return str(self.url) + + def __repr__(self) -> str: + return "%s(%s)" % (self.__class__.__name__, self.url) diff --git a/.venv/lib/python3.9/site-packages/caldav/discovery.py b/.venv/lib/python3.9/site-packages/caldav/discovery.py new file mode 100644 index 0000000..3a19d9a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/discovery.py @@ -0,0 +1,561 @@ +#!/usr/bin/env python +""" +RFC 6764 - Locating Services for Calendaring and Contacts (CalDAV/CardDAV) + +This module implements DNS-based service discovery for CalDAV and CardDAV +servers as specified in RFC 6764. It allows clients to discover service +endpoints from just a domain name or email address. + +Discovery methods (in order of preference): +1. DNS SRV records (_caldavs._tcp / _carddavs._tcp for TLS) +2. DNS TXT records (for path information) +3. Well-Known URIs (/.well-known/caldav or /.well-known/carddav) + +SECURITY CONSIDERATIONS: + DNS-based discovery is vulnerable to attacks if DNS is not secured with DNSSEC: + + - DNS Spoofing: Attackers can provide malicious SRV/TXT records pointing to + attacker-controlled servers + - Downgrade Attacks: Malicious DNS can specify non-TLS services, causing + credentials to be sent in plaintext + - Man-in-the-Middle: Even with HTTPS, attackers can redirect to their servers + + MITIGATIONS: + - require_tls=True (DEFAULT): Only accept HTTPS connections, preventing + downgrade attacks + - ssl_verify_cert=True (DEFAULT): Verify TLS certificates per RFC 6125 + - Domain validation (RFC 6764 Section 8): Discovered hostnames must be + in the same domain as the queried domain to prevent redirection attacks + - Use DNSSEC when possible for DNS integrity + - Manually verify discovered endpoints for sensitive applications + - Consider certificate pinning for known domains + + For high-security environments, manual configuration may be preferable to + automatic discovery. + +See: https://datatracker.ietf.org/doc/html/rfc6764 +""" +import logging +from dataclasses import dataclass +from typing import List +from typing import Optional +from typing import Tuple +from urllib.parse import urljoin +from urllib.parse import urlparse + +import dns.exception +import dns.resolver + +try: + import niquests as requests +except ImportError: + import requests + +from caldav.lib.error import DAVError + +log = logging.getLogger(__name__) + + +class DiscoveryError(DAVError): + """Raised when service discovery fails""" + + pass + + +@dataclass +class ServiceInfo: + """Information about a discovered CalDAV/CardDAV service""" + + url: str + hostname: str + port: int + path: str + tls: bool + priority: int = 0 + weight: int = 0 + source: str = "unknown" # 'srv', 'txt', 'well-known', 'manual' + username: Optional[str] = None # Extracted from email address if provided + + def __str__(self) -> str: + return f"ServiceInfo(url={self.url}, source={self.source}, priority={self.priority}, username={self.username})" + + +def _is_subdomain_or_same(discovered_domain: str, original_domain: str) -> bool: + """ + Check if discovered domain is the same as or a subdomain of the original domain. + + This prevents DNS hijacking attacks where malicious DNS records redirect + to completely different domains (e.g., acme.com -> evil.hackers.are.us). + + Args: + discovered_domain: The hostname discovered via DNS SRV/TXT or well-known URI + original_domain: The domain from the user's identifier + + Returns: + True if discovered_domain is safe (same domain or subdomain), False otherwise + + Examples: + >>> _is_subdomain_or_same('calendar.example.com', 'example.com') + True + >>> _is_subdomain_or_same('example.com', 'example.com') + True + >>> _is_subdomain_or_same('evil.com', 'example.com') + False + >>> _is_subdomain_or_same('subdomain.calendar.example.com', 'example.com') + True + >>> _is_subdomain_or_same('exampleXcom.evil.com', 'example.com') + False + """ + # Normalize to lowercase for comparison + discovered = discovered_domain.lower().strip(".") + original = original_domain.lower().strip(".") + + # Same domain is always allowed + if discovered == original: + return True + + # Check if discovered is a subdomain of original + # Must end with .original_domain to be a valid subdomain + if discovered.endswith("." + original): + return True + + return False + + +def _extract_domain(identifier: str) -> Tuple[str, Optional[str]]: + """ + Extract domain and optional username from an email address or URL. + + Args: + identifier: Email address (user@example.com) or domain (example.com) + + Returns: + A tuple of (domain, username) where username is None if not present + + Examples: + >>> _extract_domain('user@example.com') + ('example.com', 'user') + >>> _extract_domain('example.com') + ('example.com', None) + >>> _extract_domain('https://caldav.example.com/path') + ('caldav.example.com', None) + """ + # If it looks like a URL, parse it + if "://" in identifier: + parsed = urlparse(identifier) + return (parsed.hostname or identifier, None) + + # If it contains @, it's an email address + if "@" in identifier: + parts = identifier.split("@") + username = parts[0].strip() if parts[0] else None + domain = parts[-1].strip() + return (domain, username) + + # Otherwise assume it's already a domain + return (identifier.strip(), None) + + +def _parse_txt_record(txt_data: str) -> Optional[str]: + """ + Parse TXT record data to extract the path attribute. + + According to RFC 6764, TXT records contain attribute=value pairs. + We're looking for the 'path' attribute. + + Args: + txt_data: TXT record data (e.g., "path=/caldav/") + + Returns: + The path value or None if not found + + Examples: + >>> _parse_txt_record('path=/caldav/') + '/caldav/' + >>> _parse_txt_record('path=/caldav/ other=value') + '/caldav/' + """ + # TXT records are key=value pairs separated by spaces + for pair in txt_data.split(): + if "=" in pair: + key, value = pair.split("=", 1) + if key.strip().lower() == "path": + return value.strip() + return None + + +def _srv_lookup( + domain: str, service_type: str, use_tls: bool = True +) -> List[Tuple[str, int, int, int]]: + """ + Perform DNS SRV record lookup. + + Args: + domain: The domain to query + service_type: Either 'caldav' or 'carddav' + use_tls: If True, query for TLS service (_caldavs), else non-TLS (_caldav) + + Returns: + List of tuples: (hostname, port, priority, weight) + Sorted by priority (lower is better), then randomized by weight + """ + # Construct the SRV record name + # RFC 6764 defines: _caldavs._tcp, _caldav._tcp, _carddavs._tcp, _carddav._tcp + service_suffix = "s" if use_tls else "" + srv_name = f"_{service_type}{service_suffix}._tcp.{domain}" + + log.debug(f"Performing SRV lookup for {srv_name}") + + try: + answers = dns.resolver.resolve(srv_name, "SRV") + results = [] + + for rdata in answers: + hostname = str(rdata.target).rstrip(".") + port = int(rdata.port) + priority = int(rdata.priority) + weight = int(rdata.weight) + + log.debug( + f"Found SRV record: {hostname}:{port} (priority={priority}, weight={weight})" + ) + results.append((hostname, port, priority, weight)) + + # Sort by priority (lower is better), then by weight (for weighted random selection) + results.sort(key=lambda x: (x[2], -x[3])) + return results + + except ( + dns.resolver.NXDOMAIN, + dns.resolver.NoAnswer, + dns.exception.DNSException, + ) as e: + log.debug(f"SRV lookup failed for {srv_name}: {e}") + return [] + + +def _txt_lookup(domain: str, service_type: str, use_tls: bool = True) -> Optional[str]: + """ + Perform DNS TXT record lookup to find the service path. + + Args: + domain: The domain to query + service_type: Either 'caldav' or 'carddav' + use_tls: If True, query for TLS service (_caldavs), else non-TLS (_caldav) + + Returns: + The path from the TXT record, or None if not found + """ + service_suffix = "s" if use_tls else "" + txt_name = f"_{service_type}{service_suffix}._tcp.{domain}" + + log.debug(f"Performing TXT lookup for {txt_name}") + + try: + answers = dns.resolver.resolve(txt_name, "TXT") + + for rdata in answers: + # TXT records can have multiple strings; join them + txt_data = "".join( + [ + s.decode("utf-8") if isinstance(s, bytes) else s + for s in rdata.strings + ] + ) + log.debug(f"Found TXT record: {txt_data}") + + path = _parse_txt_record(txt_data) + if path: + return path + + except ( + dns.resolver.NXDOMAIN, + dns.resolver.NoAnswer, + dns.exception.DNSException, + ) as e: + log.debug(f"TXT lookup failed for {txt_name}: {e}") + + return None + + +def _well_known_lookup( + domain: str, service_type: str, timeout: int = 10, ssl_verify_cert: bool = True +) -> Optional[ServiceInfo]: + """ + Try to discover service via Well-Known URI (RFC 5785). + + According to RFC 6764, if SRV/TXT lookup fails, clients should try: + - https://domain/.well-known/caldav + - https://domain/.well-known/carddav + + Security: Redirects to different domains are validated per RFC 6764 Section 8. + + Args: + domain: The domain to query + service_type: Either 'caldav' or 'carddav' + timeout: Request timeout in seconds + ssl_verify_cert: Whether to verify SSL certificates + + Returns: + ServiceInfo if successful, None otherwise + """ + well_known_path = f"/.well-known/{service_type}" + url = f"https://{domain}{well_known_path}" + + log.debug(f"Trying well-known URI: {url}") + + try: + # We expect a redirect to the actual service URL + # Use HEAD or GET with allow_redirects + response = requests.get( + url, + timeout=timeout, + verify=ssl_verify_cert, + allow_redirects=False, # We want to see the redirect + ) + + # RFC 6764 says we should follow redirects + if response.status_code in (301, 302, 303, 307, 308): + location = response.headers.get("Location") + if location: + log.debug(f"Well-known URI redirected to: {location}") + + # Make it an absolute URL if it's relative + final_url = urljoin(url, location) + parsed = urlparse(final_url) + redirect_hostname = parsed.hostname or domain + + # RFC 6764 Section 8 Security: Validate redirect target is in same domain + if not _is_subdomain_or_same(redirect_hostname, domain): + log.warning( + f"RFC 6764 Security: Rejecting well-known redirect to different domain. " + f"Queried domain: {domain}, Redirect target: {redirect_hostname}. " + f"Ignoring this redirect for security." + ) + return None + + return ServiceInfo( + url=final_url, + hostname=redirect_hostname, + port=parsed.port or (443 if parsed.scheme == "https" else 80), + path=parsed.path or "/", + tls=parsed.scheme == "https", + source="well-known", + ) + + # If we get 200 OK, the well-known URI itself is the service endpoint + if response.status_code == 200: + log.debug(f"Well-known URI is the service endpoint: {url}") + return ServiceInfo( + url=url, + hostname=domain, + port=443, + path=well_known_path, + tls=True, + source="well-known", + ) + + except requests.exceptions.RequestException as e: + log.debug(f"Well-known URI lookup failed: {e}") + + return None + + +def discover_service( + identifier: str, + service_type: str = "caldav", + timeout: int = 10, + ssl_verify_cert: bool = True, + prefer_tls: bool = True, + require_tls: bool = True, +) -> Optional[ServiceInfo]: + """ + Discover CalDAV or CardDAV service for a domain or email address. + + This is the main entry point for RFC 6764 service discovery. + It tries multiple methods in order: + 1. DNS SRV records (with TLS preferred) + 2. DNS TXT records for path information + 3. Well-Known URIs as fallback + + SECURITY WARNING: + RFC 6764 discovery relies on DNS, which can be spoofed if not using DNSSEC. + An attacker controlling DNS could: + - Redirect connections to a malicious server + - Downgrade from HTTPS to HTTP to capture credentials + - Perform man-in-the-middle attacks + + By default, require_tls=True prevents HTTP downgrade attacks. + For production use, consider: + - Using DNSSEC-validated domains + - Manual verification of discovered endpoints + - Pinning certificates for known domains + + Args: + identifier: Domain name (example.com) or email address (user@example.com) + service_type: Either 'caldav' or 'carddav' + timeout: Timeout for HTTP requests in seconds + ssl_verify_cert: Whether to verify SSL certificates + prefer_tls: If True, try TLS services first (only used if require_tls=False) + require_tls: If True (default), ONLY accept TLS connections. This prevents + DNS-based downgrade attacks to plaintext HTTP. Set to False + only if you explicitly need to support non-TLS servers and + trust your DNS infrastructure. + + Returns: + ServiceInfo object with discovered service details, or None if discovery fails + + Raises: + DiscoveryError: If service_type is invalid + + Examples: + >>> info = discover_service('user@example.com', 'caldav') + >>> if info: + ... print(f"Service URL: {info.url}") + + >>> # Allow non-TLS (INSECURE - only for testing) + >>> info = discover_service('user@example.com', 'caldav', require_tls=False) + """ + if service_type not in ("caldav", "carddav"): + raise DiscoveryError( + reason=f"Invalid service_type: {service_type}. Must be 'caldav' or 'carddav'" + ) + + domain, username = _extract_domain(identifier) + log.info(f"Discovering {service_type} service for domain: {domain}") + if username: + log.debug(f"Username extracted from identifier: {username}") + + # Try SRV/TXT records first (RFC 6764 section 5) + # Security: require_tls=True prevents downgrade attacks + if require_tls: + tls_options = [True] # Only accept TLS connections + log.debug("require_tls=True: Only attempting TLS discovery") + else: + # Prefer TLS services over non-TLS when both are allowed + tls_options = [True, False] if prefer_tls else [False, True] + log.warning("require_tls=False: Allowing non-TLS connections (INSECURE)") + + for use_tls in tls_options: + srv_records = _srv_lookup(domain, service_type, use_tls) + + if srv_records: + # Use the highest priority record (first in sorted list) + hostname, port, priority, weight = srv_records[0] + + # RFC 6764 Section 8 Security: Validate discovered hostname is in same domain + # "clients SHOULD check that the target FQDN returned in the SRV record + # matches the original service domain that was queried" + if not _is_subdomain_or_same(hostname, domain): + log.warning( + f"RFC 6764 Security: Rejecting SRV record pointing to different domain. " + f"Queried domain: {domain}, Target FQDN: {hostname}. " + f"This may indicate DNS hijacking or misconfiguration." + ) + continue # Try next TLS option or fall back to well-known + + # Try to get path from TXT record + path = _txt_lookup(domain, service_type, use_tls) + if not path: + # RFC 6764 section 5: If no TXT record, try well-known URI for path + log.debug("No TXT record found, using root path") + path = "/" + + # Construct the service URL + scheme = "https" if use_tls else "http" + # Only include port in URL if it's non-standard + default_port = 443 if use_tls else 80 + if port != default_port: + url = f"{scheme}://{hostname}:{port}{path}" + else: + url = f"{scheme}://{hostname}{path}" + + log.info(f"Discovered {service_type} service via SRV: {url}") + + return ServiceInfo( + url=url, + hostname=hostname, + port=port, + path=path, + tls=use_tls, + priority=priority, + weight=weight, + source="srv", + username=username, + ) + + # Fallback to well-known URI (RFC 6764 section 5) + log.debug("SRV lookup failed, trying well-known URI") + well_known_info = _well_known_lookup(domain, service_type, timeout, ssl_verify_cert) + + if well_known_info: + # Preserve username from email address + well_known_info.username = username + log.info( + f"Discovered {service_type} service via well-known URI: {well_known_info.url}" + ) + return well_known_info + + # All discovery methods failed + log.warning(f"Failed to discover {service_type} service for {domain}") + return None + + +def discover_caldav( + identifier: str, + timeout: int = 10, + ssl_verify_cert: bool = True, + prefer_tls: bool = True, + require_tls: bool = True, +) -> Optional[ServiceInfo]: + """ + Convenience function to discover CalDAV service. + + Args: + identifier: Domain name or email address + timeout: Timeout for HTTP requests in seconds + ssl_verify_cert: Whether to verify SSL certificates + prefer_tls: If True, try TLS services first + require_tls: If True (default), only accept TLS connections + + Returns: + ServiceInfo object or None + """ + return discover_service( + identifier=identifier, + service_type="caldav", + timeout=timeout, + ssl_verify_cert=ssl_verify_cert, + prefer_tls=prefer_tls, + require_tls=require_tls, + ) + + +def discover_carddav( + identifier: str, + timeout: int = 10, + ssl_verify_cert: bool = True, + prefer_tls: bool = True, + require_tls: bool = True, +) -> Optional[ServiceInfo]: + """ + Convenience function to discover CardDAV service. + + Args: + identifier: Domain name or email address + timeout: Timeout for HTTP requests in seconds + ssl_verify_cert: Whether to verify SSL certificates + prefer_tls: If True, try TLS services first + require_tls: If True (default), only accept TLS connections + + Returns: + ServiceInfo object or None + """ + return discover_service( + identifier=identifier, + service_type="carddav", + timeout=timeout, + ssl_verify_cert=ssl_verify_cert, + prefer_tls=prefer_tls, + require_tls=require_tls, + ) diff --git a/.venv/lib/python3.9/site-packages/caldav/elements/__init__.py b/.venv/lib/python3.9/site-packages/caldav/elements/__init__.py new file mode 100644 index 0000000..4265cc3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/elements/__init__.py @@ -0,0 +1 @@ +#!/usr/bin/env python diff --git a/.venv/lib/python3.9/site-packages/caldav/elements/base.py b/.venv/lib/python3.9/site-packages/caldav/elements/base.py new file mode 100644 index 0000000..8739199 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/elements/base.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python +import sys +from typing import ClassVar +from typing import List +from typing import Optional +from typing import Union + +from lxml import etree +from lxml.etree import _Element + +from caldav.lib.namespace import nsmap +from caldav.lib.python_utilities import to_unicode + +if sys.version_info < (3, 9): + from typing import Iterable +else: + from collections.abc import Iterable + +if sys.version_info < (3, 11): + from typing_extensions import Self +else: + from typing import Self + + +class BaseElement: + children: Optional[List[Self]] = None + tag: ClassVar[Optional[str]] = None + value: Optional[str] = None + attributes: Optional[dict] = None + caldav_class = None + + def __init__( + self, name: Optional[str] = None, value: Union[str, bytes, None] = None + ) -> None: + self.children = [] + self.attributes = {} + value = to_unicode(value) + self.value = None + if name is not None: + self.attributes["name"] = name + if value is not None: + self.value = value + + def __add__( + self, other: Union["BaseElement", Iterable["BaseElement"]] + ) -> "BaseElement": + return self.append(other) + + def __str__(self) -> str: + utf8 = etree.tostring( + self.xmlelement(), encoding="utf-8", xml_declaration=True, pretty_print=True + ) + return str(utf8, "utf-8") + + def xmlelement(self) -> _Element: + if self.tag is None: + ## We can do better than this. tag may be a property that expands + ## from the class name. Another layer of base class to indicate + ## if it's the D-namespace, C-namespace or another namespace. + raise ValueError("Unexpected value None for self.tag") + + if self.attributes is None: + raise ValueError("Unexpected value None for self.attributes") + + root = etree.Element(self.tag, nsmap=nsmap) + if self.value is not None: + root.text = self.value + + for k in self.attributes: + root.set(k, self.attributes[k]) + + self.xmlchildren(root) + return root + + def xmlchildren(self, root: _Element) -> None: + if self.children is None: + raise ValueError("Unexpected value None for self.children") + + for c in self.children: + root.append(c.xmlelement()) + + def append(self, element: Union[Self, Iterable[Self]]) -> Self: + if self.children is None: + raise ValueError("Unexpected value None for self.children") + + if isinstance(element, Iterable): + self.children.extend(element) + else: + self.children.append(element) + + return self + + +class NamedBaseElement(BaseElement): + def __init__(self, name: Optional[str] = None) -> None: + super(NamedBaseElement, self).__init__(name=name) + + def xmlelement(self): + if self.attributes.get("name") is None: + raise Exception("name attribute must be defined") + return super(NamedBaseElement, self).xmlelement() + + +class ValuedBaseElement(BaseElement): + def __init__(self, value: Union[str, bytes, None] = None) -> None: + super(ValuedBaseElement, self).__init__(value=value) diff --git a/.venv/lib/python3.9/site-packages/caldav/elements/cdav.py b/.venv/lib/python3.9/site-packages/caldav/elements/cdav.py new file mode 100644 index 0000000..6df4926 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/elements/cdav.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python +import logging +from datetime import datetime +from datetime import timezone +from typing import ClassVar +from typing import Optional + +from .base import BaseElement +from .base import NamedBaseElement +from .base import ValuedBaseElement +from caldav.lib.namespace import ns + +utc_tz = timezone.utc + + +def _to_utc_date_string(ts): + # type (Union[date,datetime]]) -> str + """coerce datetimes to UTC (assume localtime if nothing is given)""" + if isinstance(ts, datetime): + try: + ## for any python version, this should work for a non-native + ## timestamp. + ## in python 3.6 and higher, ts.astimezone() will assume a + ## naive timestamp is localtime (and so do we) + ts = ts.astimezone(utc_tz) + except: + ## native time stamp and the current python version is + ## not able to treat it as localtime. + import tzlocal + + ts = ts.replace(tzinfo=tzlocal.get_localzone()) + + mindate = datetime.min.replace(tzinfo=utc_tz) + maxdate = datetime.max.replace(tzinfo=utc_tz) + if mindate + ts.tzinfo.utcoffset(ts) > ts: + logging.error( + "Cannot coerce datetime %s to UTC. Changed to min-date.", ts + ) + ts = mindate + elif ts > maxdate - ts.tzinfo.utcoffset(ts): + logging.error( + "Cannot coerce datetime %s to UTC. Changed to max-date.", ts + ) + ts = maxdate + else: + ts = ts.astimezone(utc_tz) + + return ts.strftime("%Y%m%dT%H%M%SZ") + + +# Operations +class CalendarQuery(BaseElement): + tag: ClassVar[str] = ns("C", "calendar-query") + + +class FreeBusyQuery(BaseElement): + tag: ClassVar[str] = ns("C", "free-busy-query") + + +class Mkcalendar(BaseElement): + tag: ClassVar[str] = ns("C", "mkcalendar") + + +class CalendarMultiGet(BaseElement): + tag: ClassVar[str] = ns("C", "calendar-multiget") + + +class ScheduleInboxURL(BaseElement): + tag: ClassVar[str] = ns("C", "schedule-inbox-URL") + + +class ScheduleOutboxURL(BaseElement): + tag: ClassVar[str] = ns("C", "schedule-outbox-URL") + + +# Filters +class Filter(BaseElement): + tag: ClassVar[str] = ns("C", "filter") + + +class CompFilter(NamedBaseElement): + tag: ClassVar[str] = ns("C", "comp-filter") + + +class PropFilter(NamedBaseElement): + tag: ClassVar[str] = ns("C", "prop-filter") + + +class ParamFilter(NamedBaseElement): + tag: ClassVar[str] = ns("C", "param-filter") + + +# Conditions +class TextMatch(ValuedBaseElement): + tag: ClassVar[str] = ns("C", "text-match") + + def __init__(self, value, collation: str = "i;octet", negate: bool = False) -> None: + super(TextMatch, self).__init__(value=value) + + if self.attributes is None: + raise ValueError("Unexpected value None for self.attributes") + + self.attributes["collation"] = collation + if negate: + self.attributes["negate-condition"] = "yes" + + +class TimeRange(BaseElement): + tag: ClassVar[str] = ns("C", "time-range") + + def __init__( + self, start: Optional[datetime] = None, end: Optional[datetime] = None + ) -> None: + ## start and end should be an icalendar "date with UTC time", + ## ref https://tools.ietf.org/html/rfc4791#section-9.9 + super(TimeRange, self).__init__() + + if self.attributes is None: + raise ValueError("Unexpected value None for self.attributes") + + if start is not None: + self.attributes["start"] = _to_utc_date_string(start) + if end is not None: + self.attributes["end"] = _to_utc_date_string(end) + + +class NotDefined(BaseElement): + tag: ClassVar[str] = ns("C", "is-not-defined") + + +# Components / Data +class CalendarData(BaseElement): + tag: ClassVar[str] = ns("C", "calendar-data") + + +class Expand(BaseElement): + tag: ClassVar[str] = ns("C", "expand") + + def __init__( + self, start: Optional[datetime], end: Optional[datetime] = None + ) -> None: + super(Expand, self).__init__() + + if self.attributes is None: + raise ValueError("Unexpected value None for self.attributes") + + if start is not None: + self.attributes["start"] = _to_utc_date_string(start) + if end is not None: + self.attributes["end"] = _to_utc_date_string(end) + + +class Comp(NamedBaseElement): + tag: ClassVar[str] = ns("C", "comp") + + +# Uhhm ... can't find any references to calendar-collection in rfc4791.txt +# and newer versions of baikal gives 403 forbidden when this one is +# encountered +# class CalendarCollection(BaseElement): +# tag = ns("C", "calendar-collection") + + +# Properties +class CalendarUserAddressSet(BaseElement): + tag: ClassVar[str] = ns("C", "calendar-user-address-set") + + +class CalendarUserType(BaseElement): + tag: ClassVar[str] = ns("C", "calendar-user-type") + + +class CalendarHomeSet(BaseElement): + tag: ClassVar[str] = ns("C", "calendar-home-set") + + +# calendar resource type, see rfc4791, sec. 4.2 +class Calendar(BaseElement): + tag: ClassVar[str] = ns("C", "calendar") + + +class CalendarDescription(ValuedBaseElement): + tag: ClassVar[str] = ns("C", "calendar-description") + + +class CalendarTimeZone(ValuedBaseElement): + tag: ClassVar[str] = ns("C", "calendar-timezone") + + +class SupportedCalendarComponentSet(ValuedBaseElement): + tag: ClassVar[str] = ns("C", "supported-calendar-component-set") + + +class SupportedCalendarData(ValuedBaseElement): + tag: ClassVar[str] = ns("C", "supported-calendar-data") + + +class MaxResourceSize(ValuedBaseElement): + tag: ClassVar[str] = ns("C", "max-resource-size") + + +class MinDateTime(ValuedBaseElement): + tag: ClassVar[str] = ns("C", "min-date-time") + + +class MaxDateTime(ValuedBaseElement): + tag: ClassVar[str] = ns("C", "max-date-time") + + +class MaxInstances(ValuedBaseElement): + tag: ClassVar[str] = ns("C", "max-instances") + + +class MaxAttendeesPerInstance(ValuedBaseElement): + tag: ClassVar[str] = ns("C", "max-attendees-per-instance") + + +class Allprop(BaseElement): + tag: ClassVar[str] = ns("C", "allprop") + + +class ScheduleTag(BaseElement): + tag: ClassVar[str] = ns("C", "schedule-tag") diff --git a/.venv/lib/python3.9/site-packages/caldav/elements/dav.py b/.venv/lib/python3.9/site-packages/caldav/elements/dav.py new file mode 100644 index 0000000..e56c8ac --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/elements/dav.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +from typing import ClassVar + +from .base import BaseElement +from .base import ValuedBaseElement +from caldav.lib.namespace import ns + + +# Operations +class Propfind(BaseElement): + tag: ClassVar[str] = ns("D", "propfind") + + +class PropertyUpdate(BaseElement): + tag: ClassVar[str] = ns("D", "propertyupdate") + + +class Mkcol(BaseElement): + tag: ClassVar[str] = ns("D", "mkcol") + + +class SyncCollection(BaseElement): + tag: ClassVar[str] = ns("D", "sync-collection") + + +class PrincipalPropertySearch(BaseElement): + tag: ClassVar[str] = ns("D", "principal-property-search") + + +class PropertySearch(BaseElement): + tag: ClassVar[str] = ns("D", "property-search") + + +# Filters + + +class Match(BaseElement): + tag: ClassVar[str] = ns("D", "match") + + +# Conditions +class SyncToken(BaseElement): + tag: ClassVar[str] = ns("D", "sync-token") + + +class SyncLevel(BaseElement): + tag: ClassVar[str] = ns("D", "sync-level") + + +# Components / Data + + +class Prop(BaseElement): + tag: ClassVar[str] = ns("D", "prop") + + +class Collection(BaseElement): + tag: ClassVar[str] = ns("D", "collection") + + +class Set(BaseElement): + tag: ClassVar[str] = ns("D", "set") + + +# Properties +class ResourceType(BaseElement): + tag: ClassVar[str] = ns("D", "resourcetype") + + +class DisplayName(ValuedBaseElement): + tag: ClassVar[str] = ns("D", "displayname") + + +class GetEtag(ValuedBaseElement): + tag: ClassVar[str] = ns("D", "getetag") + + +class Href(BaseElement): + tag: ClassVar[str] = ns("D", "href") + + +class SupportedReportSet(BaseElement): + tag = ns("D", "supported-report-set") + + +class Response(BaseElement): + tag: ClassVar[str] = ns("D", "response") + + +class Status(BaseElement): + tag: ClassVar[str] = ns("D", "status") + + +class PropStat(BaseElement): + tag: ClassVar[str] = ns("D", "propstat") + + +class MultiStatus(BaseElement): + tag: ClassVar[str] = ns("D", "multistatus") + + +class CurrentUserPrincipal(BaseElement): + tag: ClassVar[str] = ns("D", "current-user-principal") + + +class PrincipalCollectionSet(BaseElement): + tag: ClassVar[str] = ns("D", "principal-collection-set") + + +class Allprop(BaseElement): + tag: ClassVar[str] = ns("D", "allprop") + + +class Owner(BaseElement): + tag: ClassVar[str] = ns("D", "owner") diff --git a/.venv/lib/python3.9/site-packages/caldav/elements/ical.py b/.venv/lib/python3.9/site-packages/caldav/elements/ical.py new file mode 100644 index 0000000..ffae7e1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/elements/ical.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +from typing import ClassVar + +from .base import ValuedBaseElement +from caldav.lib.namespace import ns + + +# Properties +class CalendarColor(ValuedBaseElement): + tag: ClassVar[str] = ns("I", "calendar-color") + + +class CalendarOrder(ValuedBaseElement): + tag: ClassVar[str] = ns("I", "calendar-order") diff --git a/.venv/lib/python3.9/site-packages/caldav/lib/__init__.py b/.venv/lib/python3.9/site-packages/caldav/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.9/site-packages/caldav/lib/debug.py b/.venv/lib/python3.9/site-packages/caldav/lib/debug.py new file mode 100644 index 0000000..7c233bb --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/lib/debug.py @@ -0,0 +1,16 @@ +from lxml import etree + + +def xmlstring(root): + if isinstance(root, str): + return root + if hasattr(root, "xmlelement"): + root = root.xmlelement() + try: + return etree.tostring(root, pretty_print=True).decode("utf-8") + except: + return root + + +def printxml(root) -> None: + print(xmlstring(root)) diff --git a/.venv/lib/python3.9/site-packages/caldav/lib/error.py b/.venv/lib/python3.9/site-packages/caldav/lib/error.py new file mode 100644 index 0000000..b79f31c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/lib/error.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python +import logging +from collections import defaultdict +from typing import Dict +from typing import Optional + +from caldav import __version__ + +debug_dump_communication = False +try: + import os + + ## Environmental variables prepended with "PYTHON_CALDAV" are used for debug purposes, + ## environmental variables prepended with "CALDAV_" are for connection parameters + debug_dump_communication = os.environ.get("PYTHON_CALDAV_COMMDUMP", False) + ## one of DEBUG_PDB, DEBUG, DEVELOPMENT, PRODUCTION + debugmode = os.environ["PYTHON_CALDAV_DEBUGMODE"] +except: + if "dev" in __version__ or __version__ == "(unknown)": + debugmode = "DEVELOPMENT" + else: + debugmode = "PRODUCTION" + +log = logging.getLogger("caldav") +if debugmode.startswith("DEBUG"): + log.setLevel(logging.DEBUG) +else: + log.setLevel(logging.WARNING) + + +def errmsg(r) -> str: + """Utility for formatting a an error response to an error string""" + return "%s %s\n\n%s" % (r.status, r.reason, r.raw) + + +def weirdness(*reasons): + from caldav.lib.debug import xmlstring + + reason = " : ".join([xmlstring(x) for x in reasons]) + log.warning(f"Deviation from expectations found: {reason}") + if debugmode == "DEBUG_PDB": + log.error(f"Dropping into debugger due to {reason}") + import pdb + + pdb.set_trace() + + +def assert_(condition: object) -> None: + try: + assert condition + except AssertionError: + if debugmode == "PRODUCTION": + log.error( + "Deviation from expectations found. %s" % ERR_FRAGMENT, exc_info=True + ) + elif debugmode == "DEBUG_PDB": + log.error("Deviation from expectations found. Dropping into debugger") + import pdb + + pdb.set_trace() + else: + raise + + +ERR_FRAGMENT: str = "Please consider raising an issue at https://github.com/python-caldav/caldav/issues or reach out to t-caldav@tobixen.no, include this error and the traceback (if any) and tell what server you are using" + + +class DAVError(Exception): + url: Optional[str] = None + reason: str = "no reason" + + def __init__(self, url: Optional[str] = None, reason: Optional[str] = None) -> None: + if url: + self.url = url + if reason: + self.reason = reason + + def __str__(self) -> str: + return "%s at '%s', reason %s" % ( + self.__class__.__name__, + self.url, + self.reason, + ) + + +class AuthorizationError(DAVError): + """ + The client encountered an HTTP 403 error and is passing it on + to the user. The url property will contain the url in question, + the reason property will contain the excuse the server sent. + """ + + pass + + +class PropsetError(DAVError): + pass + + +class ProppatchError(DAVError): + pass + + +class PropfindError(DAVError): + pass + + +class ReportError(DAVError): + pass + + +class MkcolError(DAVError): + pass + + +class MkcalendarError(DAVError): + pass + + +class PutError(DAVError): + pass + + +class DeleteError(DAVError): + pass + + +class NotFoundError(DAVError): + pass + + +class ConsistencyError(DAVError): + pass + + +class ResponseError(DAVError): + pass + + +exception_by_method: Dict[str, DAVError] = defaultdict(lambda: DAVError) +for method in ( + "delete", + "put", + "mkcalendar", + "mkcol", + "report", + "propset", + "propfind", + "proppatch", +): + exception_by_method[method] = locals()[method[0].upper() + method[1:] + "Error"] diff --git a/.venv/lib/python3.9/site-packages/caldav/lib/namespace.py b/.venv/lib/python3.9/site-packages/caldav/lib/namespace.py new file mode 100644 index 0000000..af85669 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/lib/namespace.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +from typing import Any +from typing import Dict +from typing import Optional + +nsmap: Dict[str, str] = { + "D": "DAV:", + "C": "urn:ietf:params:xml:ns:caldav", +} + +## silly thing with this one ... but quite many caldav libraries, +## caldav clients and caldav servers supports this namespace and the +## calendar-color and calendar-order properties. However, those +## attributes aren't described anywhere, and the I-URL even gives a +## 404! I don't want to ship it in the namespace list of every request. +nsmap2: Dict[str, Any] = nsmap.copy() +nsmap2["I"] = ("http://apple.com/ns/ical/",) + + +def ns(prefix: str, tag: Optional[str] = None) -> str: + name = "{%s}" % nsmap2[prefix] + if tag is not None: + name = "%s%s" % (name, tag) + return name diff --git a/.venv/lib/python3.9/site-packages/caldav/lib/python_utilities.py b/.venv/lib/python3.9/site-packages/caldav/lib/python_utilities.py new file mode 100644 index 0000000..c668821 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/lib/python_utilities.py @@ -0,0 +1,41 @@ +## This file was originally made to fascilitate support for both python2 and python3. + + +def to_wire(text): + if text is None: + return None + if isinstance(text, str): + text = bytes(text, "utf-8") + text = text.replace(b"\n", b"\r\n") + text = text.replace(b"\r\r\n", b"\r\n") + return text + + +def to_local(text): + if text is None: + return None + if not isinstance(text, str): + text = text.decode("utf-8") + text = text.replace("\r\n", "\n") + return text + + +to_str = to_local + + +def to_normal_str(text): + """ + Make sure we return a normal string + """ + if text is None: + return text + if not isinstance(text, str): + text = text.decode("utf-8") + text = text.replace("\r\n", "\n") + return text + + +def to_unicode(text): + if text and isinstance(text, bytes): + return text.decode("utf-8") + return text diff --git a/.venv/lib/python3.9/site-packages/caldav/lib/url.py b/.venv/lib/python3.9/site-packages/caldav/lib/url.py new file mode 100644 index 0000000..44728d1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/lib/url.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python +import sys +import urllib.parse +from typing import Any +from typing import cast +from typing import Optional +from typing import Union +from urllib.parse import ParseResult +from urllib.parse import quote +from urllib.parse import SplitResult +from urllib.parse import unquote +from urllib.parse import urlparse +from urllib.parse import urlunparse + +from caldav.lib.python_utilities import to_normal_str +from caldav.lib.python_utilities import to_unicode + +if sys.version_info < (3, 11): + from typing_extensions import Self +else: + from typing import Self + + +class URL: + """ + This class is for wrapping URLs into objects. It's used + internally in the library, end users should not need to know + anything about this class. All methods that accept URLs can be + fed either with a URL object, a string or a urlparse.ParsedURL + object. + + Addresses may be one out of three: + + 1) a path relative to the DAV-root, i.e. "someuser/calendar" may + refer to + "http://my.davical-server.example.com/caldav.php/someuser/calendar". + + 2) an absolute path, i.e. "/caldav.php/someuser/calendar" + + 3) a fully qualified URL, i.e. + "http://someuser:somepass@my.davical-server.example.com/caldav.php/someuser/calendar". + Remark that hostname, port, user, pass is typically given when + instantiating the DAVClient object and cannot be overridden later. + + As of 2013-11, some methods in the caldav library expected strings + and some expected urlParseResult objects, some expected + fully qualified URLs and most expected absolute paths. The purpose + of this class is to ensure consistency and at the same time + maintaining backward compatibility. Basically, all methods should + accept any kind of URL. + + """ + + def __init__(self, url: Union[str, ParseResult, SplitResult]) -> None: + if isinstance(url, ParseResult) or isinstance(url, SplitResult): + self.url_parsed: Optional[Union[ParseResult, SplitResult]] = url + self.url_raw = None + else: + self.url_raw = url + self.url_parsed = None + + def __bool__(self) -> bool: + if self.url_raw or self.url_parsed: + return True + else: + return False + + def __ne__(self, other: object) -> bool: + return not self == other + + def __eq__(self, other: object) -> bool: + if str(self) == str(other): + return True + # The URLs could have insignificant differences + me = self.canonical() + if hasattr(other, "canonical"): + other = other.canonical() + return str(me) == str(other) + + def __hash__(self) -> int: + return hash(str(self)) + + # TODO: better naming? Will return url if url is already a URL + # object, else will instantiate a new URL object + @classmethod + def objectify(self, url: Union[Self, str, ParseResult, SplitResult]) -> "URL": + if url is None or isinstance(url, URL): + return url + else: + return URL(url) + + # To deal with all kind of methods/properties in the ParseResult + # class + def __getattr__(self, attr: str): + if "url_parsed" not in vars(self): + raise AttributeError + if self.url_parsed is None: + self.url_parsed = cast(urllib.parse.ParseResult, urlparse(self.url_raw)) + if hasattr(self.url_parsed, attr): + return getattr(self.url_parsed, attr) + else: + return getattr(self.__unicode__(), attr) + + # returns the url in text format + def __str__(self) -> str: + return to_normal_str(self.__unicode__()) + + # returns the url in text format + def __unicode__(self) -> str: + if self.url_raw is None: + if self.url_parsed is None: + raise ValueError("Unexpected value None for self.url_parsed") + + self.url_raw = self.url_parsed.geturl() + return to_unicode(self.url_raw) + + def __repr__(self) -> str: + return "URL(%s)" % str(self) + + def strip_trailing_slash(self) -> "URL": + if str(self)[-1] == "/": + return URL.objectify(str(self)[:-1]) + else: + return self + + def is_auth(self) -> bool: + return self.username is not None + + def unauth(self) -> "URL": + if not self.is_auth(): + return self + return URL.objectify( + ParseResult( + self.scheme, + "%s:%s" + % (self.hostname, self.port or {"https": 443, "http": 80}[self.scheme]), + self.path.replace("//", "/"), + self.params, + self.query, + self.fragment, + ) + ) + + def canonical(self) -> "URL": + """ + a canonical URL ... remove authentication details, make sure there + are no double slashes, and to make sure the URL is always the same, + run it through the urlparser, and make sure path is properly quoted + """ + url = self.unauth() + + arr = list(cast(urllib.parse.ParseResult, self.url_parsed)) + ## quoting path and removing double slashes + arr[2] = quote(unquote(url.path.replace("//", "/"))) + ## sensible defaults + if not arr[0]: + arr[0] = "https" + if arr[1] and ":" not in arr[1]: + if arr[0] == "https": + portpart = ":443" + elif arr[0] == "http": + portpart = ":80" + else: + portpart = "" + arr[1] += portpart + + # make sure to delete the string version + url.url_raw = urlunparse(arr) + url.url_parsed = None + + return url + + def join(self, path: Any) -> "URL": + """ + assumes this object is the base URL or base path. If the path + is relative, it should be appended to the base. If the path + is absolute, it should be added to the connection details of + self. If the path already contains connection details and the + connection details differ from self, raise an error. + """ + pathAsString = str(path) + if not path or not pathAsString: + return self + path = URL.objectify(path) + if ( + (path.scheme and self.scheme and path.scheme != self.scheme) + or (path.hostname and self.hostname and path.hostname != self.hostname) + or (path.port and self.port and path.port != self.port) + ): + raise ValueError("%s can't be joined with %s" % (self, path)) + + if path.path and path.path[0] == "/": + ret_path = path.path + else: + sep = "/" + if self.path.endswith("/"): + sep = "" + ret_path = "%s%s%s" % (self.path, sep, path.path) + return URL( + ParseResult( + self.scheme or path.scheme, + self.netloc or path.netloc, + ret_path, + path.params, + path.query, + path.fragment, + ) + ) + + +def make(url: Union[URL, str, ParseResult, SplitResult]) -> URL: + """Backward compatibility""" + return URL.objectify(url) diff --git a/.venv/lib/python3.9/site-packages/caldav/lib/vcal.py b/.venv/lib/python3.9/site-packages/caldav/lib/vcal.py new file mode 100644 index 0000000..2c1df22 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/lib/vcal.py @@ -0,0 +1,264 @@ +#!/usr/bin/env python +import datetime +import logging +import re +import uuid + +import icalendar + +from caldav.lib.python_utilities import to_normal_str + +## Global counter. We don't want to be too verbose on the users, ref https://github.com/home-assistant/core/issues/86938 +fixup_error_loggings = 0 + +## Fixups to the icalendar data to work around compatibility issues. + +## TODO: + +## 1) this should only be done if needed. Use try-except around the +## fragments where icalendar/vobject is parsing ical data, and do the +## fixups there. + +## 2) arguably, this is outside the scope of the caldav library. +## check if this can be done in vobject or icalendar libraries instead +## of here + + +## TODO: would be nice with proper documentation on what systems are +## generating broken data. Compatibility issues should also be collected +## in the documentation. somewhere. +def fix(event): + """This function receives some ical as it's given from the server, checks for + breakages with the standard, and attempts to fix up known issues: + + 1) COMPLETED MUST be a datetime in UTC according to the RFC, but sometimes + a date is given. (Google Calendar?) SOGo! Ref https://github.com/home-assistant/core/issues/106671 + + + 2) The RFC does not specify any range restrictions on the dates, + but clearly it doesn't make sense with a CREATED-timestamp that is + centuries or decades before RFC2445 was published in 1998. + Apparently some calendar servers generate nonsensical CREATED + timestamps while other calendar servers can't handle CREATED + timestamps prior to 1970. Probably it would make more sense to + drop the CREATED line completely rather than moving it from the + end of year 0AD to the beginning of year 1970. (Google Calendar) + + 3) iCloud apparently duplicates the DTSTAMP property sometimes - + keep the first DTSTAMP encountered (arguably the DTSTAMP with earliest value + should be kept). + + 4) ref https://github.com/python-caldav/caldav/issues/37, + X-APPLE-STRUCTURED-EVENT attribute sometimes comes with trailing + white space. I've decided to remove all trailing spaces, since + they seem to cause a traceback with vobject and those lines are + simply ignored by icalendar. + + 5) Zimbra can apparently create events with both dtstart, dtend + and duration set - which is forbidden according to the RFC. We + should probably verify that the data is consistent. As for now, + we'll just drop DURATION or DTEND (whatever comes last). + + 6) On FOSSDEM I was presented with icalendar data missing the + DTSTAMP field. This is mandatory according to the RFC. + + All logic here is done with on the ical string, and not on + icalendar objects. There are two reasons for it, originally + optimization (not having to parse the icalendar data and create an + object, if it's to be tossed away again shortly afterwards), but + also because broken icalendar data may cause the instantiation of + an icalendar object to break. + + TODO: this should probably be moved out from the library. + """ + event = to_normal_str(event) + if not event.endswith("\n"): + event = event + "\n" + + ## TODO: add ^ before COMPLETED and CREATED? + ## 1) Add an arbitrary time if completed is given as date + fixed = re.sub( + r"COMPLETED(?:;VALUE=DATE)?:(\d+)\s", r"COMPLETED:\g<1>T120000Z", event + ) + + ## 2) CREATED timestamps prior to epoch does not make sense, + ## change from year 0001 to epoch. + fixed = re.sub("CREATED:00001231T000000Z", "CREATED:19700101T000000Z", fixed) + fixed = re.sub(r"\\+('\")", r"\1", fixed) + + ## 4) trailing whitespace probably never makes sense + fixed = re.sub(" *$", "", fixed) + + ## 6) add DTSTAMP if not given + ## (corner case that DTSTAMP is given in one but not all the recurrences is ignored) + if not "\nDTSTAMP:" in fixed: + assert "\nEND" in fixed + dtstamp = datetime.datetime.now(tz=datetime.timezone.utc).strftime( + "%Y%m%dT%H%M%SZ" + ) + fixed = re.sub( + "(\nEND:(VTODO|VEVENT|VJOURNAL))", f"\nDTSTAMP:{dtstamp}\\1", fixed + ) + + ## 3 fix duplicated DTSTAMP ... and ... + ## 5 prepare to remove DURATION or DTEND/DUE if both DURATION and + ## DTEND/DUE is set. + ## remove duplication of DTSTAMP + fixed2 = ( + "\n".join(filter(LineFilterDiscardingDuplicates(), fixed.strip().split("\n"))) + + "\n" + ) + + if fixed2 != event: + ## This obscure code will ensure efficient rate-limiting of the error + ## logging. The "remove_bit" lambda will return 0 only for powers of + ## two (2, 4, 8, 16, 32, 64, etc). + global fixup_error_loggings + fixup_error_loggings += 1 + is_power_of_two = lambda n: not (n & (n - 1)) + logger = logging.getLogger("caldav") + if is_power_of_two(fixup_error_loggings): + log = logger.warning + else: + log = logger.debug + + log_message = [ + "Ical data was modified to avoid compatibility issues", + "(Your calendar server breaks the icalendar standard)", + "This is probably harmless, particularly if not editing events or tasks", + f"(error count: {fixup_error_loggings} - this error is ratelimited)", + ] + + try: + import difflib + + diff = list( + difflib.unified_diff(event.split("\n"), fixed2.split("\n"), lineterm="") + ) + except: + diff = ["Original: ", event, "Modified: ", fixed2] + + log("\n".join(log_message + diff)) + + return fixed2 + + +class LineFilterDiscardingDuplicates: + """Needs to be a class because it keeps track of whether a certain + group of date line was already encountered within a vobject. + This must be called line by line in order on the complete text, at + least comprising the complete vobject. + """ + + def __init__(self) -> None: + self.stamped = 0 + self.ended = 0 + + def __call__(self, line): + if line.startswith("BEGIN:V"): + self.stamped = 0 + self.ended = 0 + + elif re.match("(DURATION|DTEND|DUE)[:;]", line): + if self.ended: + return False + self.ended += 1 + + elif re.match("DTSTAMP[:;]", line): + if self.stamped: + return False + self.stamped += 1 + + return True + + +## sorry for being english-language-euro-centric ... fits rather perfectly as default language for me :-) +def create_ical(ical_fragment=None, objtype=None, language="en_DK", **props): + """Creates some icalendar based on properties given as parameters. + It basically creates an icalendar object with all the boilerplate, + some sensible defaults, the properties given and returns it as a + string. + + TODO: timezones not supported so far + """ + ical_fragment = to_normal_str(ical_fragment) + if "class_" in props: + props["class"] = props.pop("class_") + if not ical_fragment or not re.search("^BEGIN:V", ical_fragment, re.MULTILINE): + my_instance = icalendar.Calendar() + if objtype is None: + objtype = "VEVENT" + try: + component = icalendar.cal.component_factory[objtype]() + except: + component = icalendar.cal.component_factory.ComponentFactory()[objtype]() + my_instance.add_component(component) + ## STATUS should default to NEEDS-ACTION for tasks, if it's not set + ## (otherwise we cannot easily add a task to a davical calendar and + ## then find it again - ref https://gitlab.com/davical-project/davical/-/issues/281 + if ( + not props.get("status") + and "\nSTATUS:" not in (ical_fragment or "") + and objtype == "VTODO" + ): + props["status"] = "NEEDS-ACTION" + + else: + if not ical_fragment.strip().startswith("BEGIN:VCALENDAR"): + ical_fragment = ( + "BEGIN:VCALENDAR\n" + + to_normal_str(ical_fragment.strip()) + + "\nEND:VCALENDAR\n" + ) + my_instance = icalendar.Calendar.from_ical(ical_fragment) + component = my_instance.subcomponents[0] + ical_fragment = None + + ## Populate with mandatory fields, if missing + if not my_instance.get("prodid"): + my_instance.add("prodid", "-//python-caldav//caldav//" + language) + if not my_instance.get("version"): + my_instance.add("version", "2.0") + if not component.get("dtstamp") and not props.get("dtstamp"): + component.add("dtstamp", datetime.datetime.now(tz=datetime.timezone.utc)) + if not component.get("uid") and not props.get("uid"): + component.add("uid", uuid.uuid1()) + + alarm = {} + for prop in props: + if props[prop] is not None: + if isinstance(props[prop], datetime.datetime) and not props[prop].tzinfo: + ## We need to have a timezone! Assume UTC. + props[prop] = props[prop].astimezone(datetime.timezone.utc) + if prop in ("child", "parent"): + for value in props[prop]: + component.add( + "related-to", + str(value), + parameters={"reltype": prop.upper()}, + encode=True, + ) + elif prop.startswith("alarm_"): + alarm[prop[6:]] = props[prop] + else: + component.add(prop, props[prop]) + if alarm: + add_alarm(my_instance, alarm) + ret = to_normal_str(my_instance.to_ical()) + if ical_fragment and ical_fragment.strip(): + ret = re.sub( + "^END:V", + ical_fragment.strip() + "\nEND:V", + ret, + flags=re.MULTILINE, + count=1, + ) + return ret + + +def add_alarm(ical, alarm): + ia = icalendar.Alarm() + for prop in alarm: + ia.add(prop, alarm[prop]) + ical.subcomponents[0].add_component(ia) + return ical diff --git a/.venv/lib/python3.9/site-packages/caldav/objects.py b/.venv/lib/python3.9/site-packages/caldav/objects.py new file mode 100755 index 0000000..aeecab2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/objects.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python +""" +I got fed up with several thousand lines of code in one and the same file. + +This file is by now just a backward compatibility layer. + +Logic has been split out: + +* DAVObject base class -> davobject.py +* CalendarObjectResource base class -> calendarobjectresource.py +* Event/Todo/Journal/FreeBusy -> calendarobjectresource.py +* Everything else (mostly collection objects) -> collection.py +""" +## For backward compatibility +from .calendarobjectresource import * +from .collection import * +from .davobject import * diff --git a/.venv/lib/python3.9/site-packages/caldav/py.typed b/.venv/lib/python3.9/site-packages/caldav/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.9/site-packages/caldav/requests.py b/.venv/lib/python3.9/site-packages/caldav/requests.py new file mode 100644 index 0000000..23b4adf --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/requests.py @@ -0,0 +1,19 @@ +try: + from niquests.auth import AuthBase +except ImportError: + from requests.auth import AuthBase + + +class HTTPBearerAuth(AuthBase): + def __init__(self, password: str) -> None: + self.password = password + + def __eq__(self, other: object) -> bool: + return self.password == getattr(other, "password", None) + + def __ne__(self, other: object) -> bool: + return not self == other + + def __call__(self, r): + r.headers["Authorization"] = f"Bearer {self.password}" + return r diff --git a/.venv/lib/python3.9/site-packages/caldav/search.py b/.venv/lib/python3.9/site-packages/caldav/search.py new file mode 100644 index 0000000..e037790 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/caldav/search.py @@ -0,0 +1,783 @@ +import logging +from copy import deepcopy +from dataclasses import dataclass +from dataclasses import field +from dataclasses import replace +from datetime import datetime +from typing import Any +from typing import List +from typing import Optional + +from icalendar import Timezone +from icalendar.prop import TypesFactory +from icalendar_searcher import Searcher +from icalendar_searcher.collation import Collation +from lxml import etree + +from .calendarobjectresource import CalendarObjectResource +from .calendarobjectresource import Event +from .calendarobjectresource import Journal +from .calendarobjectresource import Todo +from .collection import Calendar +from .elements import cdav +from .elements import dav +from .elements.base import BaseElement +from .lib import error + +TypesFactory = TypesFactory() + + +def _collation_to_caldav(collation: Collation, case_sensitive: bool = True) -> str: + """Map icalendar-searcher Collation enum to CalDAV collation identifier. + + CalDAV supports collation identifiers from RFC 4790. The default is "i;ascii-casemap" + and servers must support at least "i;ascii-casemap" and "i;octet". + + :param collation: icalendar-searcher Collation enum value + :param case_sensitive: Whether the collation should be case-sensitive + :return: CalDAV collation identifier string + """ + if collation == Collation.SIMPLE: + # SIMPLE collation maps to CalDAV's basic collations + if case_sensitive: + return "i;octet" + else: + return "i;ascii-casemap" + elif collation == Collation.UNICODE: + # Unicode Collation Algorithm - not all servers support this + # Note: "i;unicode-casemap" is case-insensitive by definition + # For case-sensitive Unicode, we fall back to i;octet (binary) + if case_sensitive: + return "i;octet" + else: + return "i;unicode-casemap" + elif collation == Collation.LOCALE: + # Locale-specific collation - not widely supported in CalDAV + # Fallback to i;ascii-casemap as most servers don't support locale-specific collations + return "i;ascii-casemap" + else: + # Default to binary/octet for unknown collations + return "i;octet" + + +@dataclass +class CalDAVSearcher(Searcher): + """The baseclass (which is generic, and not CalDAV-specific) + allows building up a search query search logic. + + The base class also allows for simple client-side filtering (and + at some point in the future, more complex client-side filtering). + + The CalDAV protocol is difficult, ambigiuous and does not offer + all kind of searches. Client-side filtering may be needed to + smoothen over differences in how the different servers handle + search queries, as well as allowing for more complex searches. + + A search may be performed by first setting up a CalDAVSearcher, + populate it with filter options, and then initiate the search from + he CalDAVSearcher. Something like this (see the doc in the base + class): + + ``ComponentSearchFilter(from=..., to=...).search(calendar)`` + + However, for simple searches, the old way to + do it will always work: + + ``calendar.search(from=..., to=..., ...)`` + + The ``todo``, ``event`` and ``journal`` parameters are booleans + for filtering the component type. It's currently recommended to + set one and only one of them to True, as of 2025-11 there is no + guarantees for correct behaviour if setting two of them. Also, if + none is given (the default), all objects should be returned - + however, all examples in the CalDAV RFC filters things by + component, and the different servers do different things when + confronted with a search missing component type. With the correct + ``compatibility_hints`` (``davclient.features``) configured for + the caldav server, the algorithms will ensure correct behaviour. + + Both the iCalendar standard and the (Cal)DAV standard defines + "properties". Make sure not to confuse those. iCalendar + properties used for filtering can be passed using + ``searcher.add_property_filter``. + """ + + comp_class: Optional["CalendarObjectResource"] = None + _explicit_operators: set = field(default_factory=set) + + def add_property_filter( + self, + key: str, + value: Any, + operator: str = None, + case_sensitive: bool = True, + collation: Optional[Collation] = None, + locale: Optional[str] = None, + ) -> None: + """Adds a filter for some specific iCalendar property. + + Examples of valid iCalendar properties: SUMMARY, + LOCATION, DESCRIPTION, DTSTART, STATUS, CLASS, etc + + :param key: iCalendar property name (e.g., SUMMARY). + Special virtual property "category" (singular) is also supported + for substring matching within category names + :param value: Filter value, should adhere to the type defined in the RFC + :param operator: Comparison operator ("contains", "==", "undef"). If not + specified, the server decides the matching behavior (usually + substring search per RFC). If explicitly set to "contains" + and the server doesn't support substring search, client-side + filtering is used (may transfer more data from server). + :param case_sensitive: If False, text comparisons are case-insensitive. + Note: CalDAV standard case-insensitivity only applies + to ASCII characters. + :param collation: Advanced collation strategy for text comparison. + May not work on all servers. + :param locale: Locale string (e.g., "de_DE") for locale-aware collation. + Only used with collation=Collation.LOCALE. May not work on + all servers. + + **Supported operators:** + + * **contains** - substring match (e.g., "rain" matches "Training session" + and "Singing in the rain") + * **==** - exact match required, enforced client-side + * **undef** - matches if property is not defined (value parameter ignored) + + **Special handling for categories:** + + - **"categories"** (plural): Exact category name matching + - "contains": subset check (all filter categories must be in component) + - "==": exact set equality (same categories, order doesn't matter) + - Commas in filter values split into multiple categories + + - **"category"** (singular): Substring matching within category names + - "contains": substring match (e.g., "out" matches "outdoor") + - "==": exact match to at least one category name + - Commas in filter values treated as literal characters + + Examples: + # Case-insensitive search + searcher.add_property_filter("SUMMARY", "meeting", case_sensitive=False) + + # Explicit substring search (guaranteed via client-side if needed) + searcher.add_property_filter("LOCATION", "room", operator="contains") + + # Exact match + searcher.add_property_filter("STATUS", "CONFIRMED", operator="==") + """ + if operator is not None: + # Base class lowercases the key, so we need to as well + self._explicit_operators.add(key.lower()) + super().add_property_filter( + key, value, operator, case_sensitive, collation, locale + ) + else: + # operator not specified - don't pass it, let base class use default + # Don't track as explicit + super().add_property_filter( + key, + value, + case_sensitive=case_sensitive, + collation=collation, + locale=locale, + ) + + def _search_with_comptypes( + self, + calendar: Calendar, + server_expand: bool = False, + split_expanded: bool = True, + props: Optional[List[cdav.CalendarData]] = None, + xml: str = None, + _hacks: str = None, + post_filter: bool = None, + ) -> List[CalendarObjectResource]: + """ + Internal method - does three searches, one for each comp class (event, journal, todo). + """ + if xml and (isinstance(xml, str) or "calendar-query" in xml.tag): + raise NotImplementedError( + "full xml given, and it has to be patched to include comp_type" + ) + objects = [] + + assert self.event is None and self.todo is None and self.journal is None + + for comp_class in (Event, Todo, Journal): + clone = replace(self) + clone.comp_class = comp_class + objects += clone.search( + calendar, server_expand, split_expanded, props, xml, post_filter, _hacks + ) + return self.sort(objects) + + ## TODO: refactor, split more logic out in smaller methods + def search( + self, + calendar: Calendar, + server_expand: bool = False, + split_expanded: bool = True, + props: Optional[List[cdav.CalendarData]] = None, + xml: str = None, + post_filter=None, + _hacks: str = None, + ) -> List[CalendarObjectResource]: + """Do the search on a CalDAV calendar. + + Only CalDAV-specific parameters goes to this method. Those + parameters are pretty obscure - mostly for power users and + internal usage. Unless you have some very special needs, the + recommendation is to not pass anything but the calendar. + + :param calendar: Calendar to be searched + :param server_expand: Ask the CalDAV server to expand recurrences + :param split_expanded: Don't collect a recurrence set in one ical calendar + :param props: CalDAV properties to send in the query + :param xml: XML query to be sent to the server (string or elements) + :param post_filter: Do client-side filtering after querying the server + :param _hacks: Please don't ask! + + Make sure not to confuse he CalDAV properties with iCalendar properties. + + If ``xml`` is given, any other filtering will not be sent to the server. + They may still be applied through client-side filtering. (TODO: work in progress) + + ``post_filter`` takes three values, ``True`` will always + filter the results, ``False`` will never filter the results, + and the default ``None`` will cause automagics to happen (not + implemented yet). Or perhaps I'll just set it to True as + default. TODO - make a decision here + + In the CalDAV protocol, a VCALENDAR object returned from the + server may contain only one event/task/journal - but if the + object is recurrent, it may contain several recurrences. + ``split_expanded`` will split the recurrences into several + objects. If you don't know what you're doing, then leave this + flag on. + + Use ``searcher.search(calendar)`` to apply the search on a caldav server. + + """ + ## Handle servers with broken component-type filtering (e.g., Bedework) + ## Such servers may misclassify component types in responses + comp_type_support = calendar.client.features.is_supported( + "search.comp-type", str + ) + if ( + (self.comp_class or self.todo or self.event or self.journal) + and comp_type_support == "broken" + and not _hacks + and post_filter is not False + ): + _hacks = "no_comp_filter" + post_filter = True + + ## Setting default value for post_filter + if post_filter is None and ( + (self.todo and not self.include_completed) + or self.expand + or "categories" in self._property_filters + or "category" in self._property_filters + or not calendar.client.features.is_supported("search.text.case-sensitive") + or not calendar.client.features.is_supported("search.time-range.accurate") + ): + post_filter = True + + ## split_expanded should only take effect on expanded data + if not self.expand and not server_expand: + split_expanded = False + + if self.expand or server_expand: + if not self.start or not self.end: + raise error.ReportError("can't expand without a date range") + + ## special compatbility-case for servers that does not + ## support category search properly + things = ("filters", "operator", "locale", "collation") + things = [f"_property_{thing}" for thing in things] + if ( + not calendar.client.features.is_supported("search.text.category") + and ( + "categories" in self._property_filters + or "category" in self._property_filters + ) + and post_filter is not False + ): + replacements = {} + for thing in things: + replacements[thing] = getattr(self, thing).copy() + replacements[thing].pop("categories", None) + replacements[thing].pop("category", None) + clone = replace(self, **replacements) + objects = clone.search(calendar, server_expand, split_expanded, props, xml) + return self.filter(objects, post_filter, split_expanded, server_expand) + + ## special compatibility-case for servers that do not support substring search + ## Only applies when user explicitly requested substring search with operator="contains" + if ( + not calendar.client.features.is_supported("search.text.substring") + and post_filter is not False + ): + # Check if any property has explicitly specified operator="contains" + explicit_contains = [ + prop + for prop in self._property_operator + if prop in self._explicit_operators + and self._property_operator[prop] == "contains" + ] + if explicit_contains: + # Remove explicit substring filters from server query, + # will be applied client-side instead + replacements = {} + for thing in things: + replacements[thing] = getattr(self, thing).copy() + for prop in explicit_contains: + replacements[thing].pop(prop, None) + # Also need to preserve the _explicit_operators set but remove these properties + clone = replace(self, **replacements) + clone._explicit_operators = self._explicit_operators - set( + explicit_contains + ) + objects = clone.search( + calendar, server_expand, split_expanded, props, xml + ) + return self.filter( + objects, + post_filter=True, + split_expanded=split_expanded, + server_expand=server_expand, + ) + + ## special compatibility-case for servers that does not + ## support combined searches very well + if not calendar.client.features.is_supported("search.combined-is-logical-and"): + if self.start or self.end: + if self._property_filters: + replacements = {} + for thing in things: + replacements[thing] = {} + clone = replace(self, **replacements) + objects = clone.search( + calendar, server_expand, split_expanded, props, xml + ) + return self.filter( + objects, post_filter, split_expanded, server_expand + ) + + ## special compatibility-case when searching for pending todos + if self.todo and not self.include_completed: + ## There are two ways to get the pending tasks - we can + ## ask the server to filter them out, or we can do it + ## client side. + + ## If the server does not support combined searches, then it's + ## safest to do it client-side. + + ## There is a special case (observed with radicale as of + ## 2025-11) where future recurrences of a task does not + ## match when doing a server-side filtering, so for this + ## case we also do client-side filtering (but the + ## "feature" + ## search.recurrences.includes-implicit.todo.pending will + ## not be supported if the feature + ## "search.recurrences.includes-implicit.todo" is not + ## supported ... hence the weird or below) + + ## To be completely sure to get all pending tasks, for all + ## server implementations and for all valid icalendar + ## objects, we send three different searches to the + ## server. This is probably bloated, and may in many + ## cases be more expensive than to ask for all tasks. At + ## the other hand, for a well-used and well-handled old + ## todo-list, there may be a small set of pending tasks + ## and heaps of done tasks. + + ## TODO: consider if not ignore_completed3 is sufficient, + ## then the recursive part of the query here is moot, and + ## we wouldn't waste so much time on repeated queries + clone = replace(self, include_completed=True) + clone.include_completed = True + ## No point with expanding in the subqueries - the expand logic will be handled + ## further down. We leave server_expand as it is, though. + clone.expand = False + if ( + calendar.client.features.is_supported("search.text") + and calendar.client.features.is_supported( + "search.combined-is-logical-and" + ) + and ( + not calendar.client.features.is_supported( + "search.recurrences.includes-implicit.todo" + ) + or calendar.client.features.is_supported( + "search.recurrences.includes-implicit.todo.pending" + ) + ) + ): + matches = [] + for hacks in ( + "ignore_completed1", + "ignore_completed2", + "ignore_completed3", + ): + ## The algorithm below does not handle recurrence split gently + matches.extend( + clone.search( + calendar, + server_expand, + split_expanded=False, + props=props, + xml=xml, + _hacks=hacks, + ) + ) + else: + ## The algorithm below does not handle recurrence split gently + matches = clone.search( + calendar, + server_expand, + split_expanded=False, + props=props, + xml=xml, + _hacks=_hacks, + ) + objects = [] + match_set = set() + for item in matches: + if item.url not in match_set: + match_set.add(item.url) + objects.append(item) + else: + orig_xml = xml + + ## Now the xml variable may be either a full query or a filter + ## and it may be either a string or an object. + if not xml or ( + not isinstance(xml, str) and not xml.tag.endswith("calendar-query") + ): + (xml, self.comp_class) = self.build_search_xml_query( + server_expand, props=props, filters=xml, _hacks=_hacks + ) + + if not self.comp_class and not calendar.client.features.is_supported( + "search.comp-type-optional" + ): + if self.include_completed is None: + self.include_completed = True + + return self._search_with_comptypes( + calendar, + server_expand, + split_expanded, + props, + orig_xml, + post_filter, + _hacks, + ) + + try: + (response, objects) = calendar._request_report_build_resultlist( + xml, self.comp_class, props=props + ) + + except error.ReportError as err: + ## This is only for backward compatibility. + ## Partial fix https://github.com/python-caldav/caldav/issues/401 + if ( + calendar.client.features.backward_compatibility_mode + and not self.comp_class + and not "400" in err.reason + ): + return self._search_with_comptypes( + calendar, + server_expand, + split_expanded, + props, + orig_xml, + post_filter, + _hacks, + ) + raise + + ## Some things, like `calendar.object_by_uid`, should always work, no matter if `davclient.compatibility_hints` is correctly configured or not + if not objects and not self.comp_class and _hacks == "insist": + return self._search_with_comptypes( + calendar, + server_expand, + split_expanded, + props, + orig_xml, + post_filter, + _hacks, + ) + + obj2 = [] + + for o in objects: + ## This would not be needed if the servers would follow the standard ... + ## TODO: use calendar.calendar_multiget - see https://github.com/python-caldav/caldav/issues/487 + try: + o.load(only_if_unloaded=True) + obj2.append(o) + except: + logging.error( + "Server does not want to reveal details about the calendar object", + exc_info=True, + ) + pass + objects = obj2 + + ## Google sometimes returns empty objects + objects = [o for o in objects if o.has_component()] + objects = self.filter(objects, post_filter, split_expanded, server_expand) + + ## partial workaround for https://github.com/python-caldav/caldav/issues/201 + for obj in objects: + try: + obj.load(only_if_unloaded=True) + except: + pass + + return self.sort(objects) + + def filter( + self, + objects: List[CalendarObjectResource], + post_filter: Optional[bool] = None, + split_expanded: bool = True, + server_expand: bool = False, + ) -> List[CalendarObjectResource]: + """Apply client-side filtering and handle recurrence expansion/splitting. + + This method performs client-side filtering of calendar objects, handles + recurrence expansion, and splits expanded recurrences into separate objects + when requested. + + :param objects: List of Event/Todo/Journal objects to filter + :param post_filter: Whether to apply the searcher's filter logic. + - True: Always apply filters (check_component) + - False: Never apply filters, only handle splitting + - None: Use default behavior (depends on self.expand and other flags) + :param split_expanded: Whether to split recurrence sets into multiple + separate CalendarObjectResource objects. If False, a recurrence set + will be contained in a single object with multiple subcomponents. + :param server_expand: Indicates that the server was supposed to expand + recurrences. If True and split_expanded is True, splitting will be + performed even without self.expand being set. + :return: Filtered and/or split list of CalendarObjectResource objects + + The method handles: + - Client-side filtering when server returns too many results + - Exact match filtering (== operator) + - Recurrence expansion via self.check_component + - Splitting expanded recurrences into separate objects + - Preserving VTIMEZONE components when splitting + """ + if post_filter or self.expand or (split_expanded and server_expand): + objects_ = objects + objects = [] + for o in objects_: + if self.expand or post_filter: + filtered = self.check_component(o, expand_only=not post_filter) + if not filtered: + continue + else: + filtered = [ + x + for x in o.icalendar_instance.subcomponents + if not isinstance(x, Timezone) + ] + i = o.icalendar_instance + tz_ = [x for x in i.subcomponents if isinstance(x, Timezone)] + i.subcomponents = tz_ + for comp in filtered: + if isinstance(comp, Timezone): + continue + if split_expanded: + new_obj = o.copy(keep_uid=True) + new_i = new_obj.icalendar_instance + new_i.subcomponents = [] + for tz in tz_: + new_i.add_component(tz) + objects.append(new_obj) + else: + new_i = i + new_i.add_component(comp) + if not (split_expanded): + objects.append(o) + return objects + + def build_search_xml_query( + self, server_expand=False, props=None, filters=None, _hacks=None + ): + """This method will produce a caldav search query as an etree object. + + It is primarily to be used from the search method. See the + documentation for the search method for more information. + """ + # those xml elements are weird. (a+b)+c != a+(b+c). First makes b and c as list members of a, second makes c an element in b which is an element of a. + # First objective is to let this take over all xml search query building and see that the current tests pass. + # ref https://www.ietf.org/rfc/rfc4791.txt, section 7.8.9 for how to build a todo-query + # We'll play with it and don't mind it's getting ugly and don't mind that the test coverage is lacking. + # we'll refactor and create some unit tests later, as well as ftests for complicated queries. + + # build the request + data = cdav.CalendarData() + if server_expand: + if not self.start or not self.end: + raise error.ReportError("can't expand without a date range") + data += cdav.Expand(self.start, self.end) + if props is None: + props_ = [data] + else: + props_ = [data] + props + prop = dav.Prop() + props_ + vcalendar = cdav.CompFilter("VCALENDAR") + + comp_filter = None + + if filters: + ## It's disgraceful - `somexml = xml + [ more_elements ]` will alter xml, + ## and there exists no `xml.copy` + ## Hence, we need to import the deepcopy tool ... + filters = deepcopy(filters) + if filters.tag == cdav.CompFilter.tag: + comp_filter = filters + filters = [] + + else: + filters = [] + + vNotCompleted = cdav.TextMatch("COMPLETED", negate=True) + vNotCancelled = cdav.TextMatch("CANCELLED", negate=True) + vNeedsAction = cdav.TextMatch("NEEDS-ACTION") + vStatusNotCompleted = cdav.PropFilter("STATUS") + vNotCompleted + vStatusNotCancelled = cdav.PropFilter("STATUS") + vNotCancelled + vStatusNeedsAction = cdav.PropFilter("STATUS") + vNeedsAction + vStatusNotDefined = cdav.PropFilter("STATUS") + cdav.NotDefined() + vNoCompleteDate = cdav.PropFilter("COMPLETED") + cdav.NotDefined() + if _hacks == "ignore_completed1": + ## This query is quite much in line with https://tools.ietf.org/html/rfc4791#section-7.8.9 + filters.extend([vNoCompleteDate, vStatusNotCompleted, vStatusNotCancelled]) + elif _hacks == "ignore_completed2": + ## some server implementations (i.e. NextCloud + ## and Baikal) will yield "false" on a negated TextMatch + ## if the field is not defined. Hence, for those + ## implementations we need to turn back and ask again + ## ... do you have any VTODOs for us where the STATUS + ## field is not defined? (ref + ## https://github.com/python-caldav/caldav/issues/14) + filters.extend([vNoCompleteDate, vStatusNotDefined]) + elif _hacks == "ignore_completed3": + ## ... and considering recurring tasks we really need to + ## look a third time as well, this time for any task with + ## the NEEDS-ACTION status set (do we need the first go? + ## NEEDS-ACTION or no status set should cover them all?) + filters.extend([vStatusNeedsAction]) + + if self.start or self.end: + filters.append(cdav.TimeRange(self.start, self.end)) + + if self.alarm_start or self.alarm_end: + filters.append( + cdav.CompFilter("VALARM") + + cdav.TimeRange(self.alarm_start, self.alarm_end) + ) + + ## I've designed this badly, at different places the caller + ## may pass the component type either as boolean flags: + ## `search(event=True, ...)` + ## as a component class: + ## `search(comp_class=caldav.calendarobjectresource.Event)` + ## or as a component filter: + ## `search(filters=cdav.CompFilter('VEVENT'), ...)` + ## The only thing I don't support is the component name ('VEVENT'). + ## Anyway, this code section ensures both comp_filter and comp_class + ## is given. Or at least, it tries to ensure it. + for flag, comp_name, comp_class_ in ( + ("event", "VEVENT", Event), + ("todo", "VTODO", Todo), + ("journal", "VJOURNAL", Journal), + ): + flagged = getattr(self, flag) + if flagged: + ## event/journal/todo is set, we adjust comp_class accordingly + if self.comp_class is not None and self.comp_class is not comp_class_: + raise error.ConsistencyError( + f"inconsistent search parameters - comp_class = {self.comp_class}, want {comp_class_}" + ) + self.comp_class = comp_class_ + + if comp_filter and comp_filter.attributes["name"] == comp_name: + self.comp_class = comp_class_ + if flag == "todo" and not self.todo and self.include_completed is None: + self.include_completed = True + setattr(self, flag, True) + + if self.comp_class == comp_class_: + if comp_filter: + assert comp_filter.attributes["name"] == comp_name + else: + comp_filter = cdav.CompFilter(comp_name) + setattr(self, flag, True) + + if self.comp_class and not comp_filter: + raise error.ConsistencyError( + f"unsupported comp class {self.comp_class} for search" + ) + + ## Special hack for bedework. + ## If asked for todos, we should NOT give any comp_filter to the server, + ## we should rather ask for everything, and then do client-side filtering + if _hacks == "no_comp_filter": + comp_filter = None + self.comp_class = None + + for property in self._property_operator: + if self._property_operator[property] == "undef": + match = cdav.NotDefined() + filters.append(cdav.PropFilter(property.upper()) + match) + else: + value = self._property_filters[property] + property_ = property.upper() + if property.lower() == "category": + property_ = "CATEGORIES" + if property.lower() == "categories": + values = value.cats + else: + values = [value] + + for value in values: + if hasattr(value, "to_ical"): + value = value.to_ical() + + # Get collation setting for this property if available + collation_str = "i;octet" # Default to binary + if ( + hasattr(self, "_property_collation") + and property in self._property_collation + ): + case_sensitive = self._property_case_sensitive.get( + property, True + ) + collation_str = _collation_to_caldav( + self._property_collation[property], case_sensitive + ) + + match = cdav.TextMatch(value, collation=collation_str) + filters.append(cdav.PropFilter(property_) + match) + + if comp_filter and filters: + comp_filter += filters + vcalendar += comp_filter + elif comp_filter: + vcalendar += comp_filter + elif filters: + vcalendar += filters + + filter = cdav.Filter() + vcalendar + + root = cdav.CalendarQuery() + [prop, filter] + + return (root, self.comp_class) diff --git a/.venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/METADATA b/.venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/METADATA new file mode 100644 index 0000000..d2a40df --- /dev/null +++ b/.venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/METADATA @@ -0,0 +1,798 @@ +Metadata-Version: 2.4 +Name: charset-normalizer +Version: 3.4.6 +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. +> You can also register your own set of codecs, and yes, it would work as-is. + +

+ >>>>> 👉 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`[^1] | ❌ | ✅ | ❌ | +| `Reliable` **without** distinguishable standards | ✅ | ✅ | ✅ | +| `Reliable` **with** distinguishable standards | ✅ | ✅ | ✅ | +| `License` | _Disputed_[^2]
_restrictive_ | MIT | MPL-1.1
_restrictive_ | +| `Native Python` | ✅ | ✅ | ❌ | +| `Detect spoken language` | ✅ | ✅ | N/A | +| `UnicodeDecodeError Safety` | ✅ | ✅ | ❌ | +| `Whl Size (min)` | 500 kB | 150 kB | ~200 kB | +| `Supported Encoding` | 99 | [99](https://charset-normalizer.readthedocs.io/en/latest/user/support.html#supported-encodings) | 40 | +| `Can register custom encoding` | ❌ | ✅ | ❌ | + +

+Reading Normalized TextCat Reading Text +

+ +[^1]: They are clearly using specific code for a specific encoding even if covering most of used one. +[^2]: Chardet 7.0+ was relicensed from LGPL-2.1 to MIT following an AI-assisted rewrite. This relicensing is disputed on two independent grounds: **(a)** the original author [contests](https://github.com/chardet/chardet/issues/327) that the maintainer had the right to relicense, arguing the rewrite is a derivative work of the LGPL-licensed codebase since it was not a clean room implementation; **(b)** the copyright claim itself is [questionable](https://github.com/chardet/chardet/issues/334) given the code was primarily generated by an LLM, and AI-generated output may not be copyrightable under most jurisdictions. Either issue alone could undermine the MIT license. Beyond licensing, the rewrite raises questions about responsible use of AI in open source: key architectural ideas pioneered by charset-normalizer - notably decode-first validity filtering (our foundational approach since v1) and encoding pairwise similarity with the same algorithm and threshold — surfaced in chardet 7 without acknowledgment. The project also imported test files from charset-normalizer to train and benchmark against it, then claimed superior accuracy on those very files. Charset-normalizer has always been MIT-licensed, encoding-agnostic by design, and built on a verifiable human-authored history. + +## ⚡ Performance + +This package offer better performances (99th, and 95th) against Chardet. Here are some numbers. + +| Package | Accuracy | Mean per file (ms) | File per sec (est) | +|---------------------------------------------------|:--------:|:------------------:|:------------------:| +| [chardet 7.1](https://github.com/chardet/chardet) | 89 % | 3 ms | 333 file/sec | +| charset-normalizer | **97 %** | 3 ms | 333 file/sec | + +| Package | 99th percentile | 95th percentile | 50th percentile | +|---------------------------------------------------|:---------------:|:---------------:|:---------------:| +| [chardet 7.1](https://github.com/chardet/chardet) | 32 ms | 17 ms | < 1 ms | +| charset-normalizer | 16 ms | 10 ms | 1 ms | + +_updated as of March 2026 using CPython 3.12, Charset-Normalizer 3.4.6, and Chardet 7.1.0_ + +~Chardet's performance on larger file (1MB+) are very poor. Expect huge difference on large payload.~ No longer the case since Chardet 7.0+ + +> 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. +> Chardet claims on his documentation to have a greater accuracy than us based on the dataset they trained Chardet on(...) +> Well, it's normal, the opposite would have been worrying. Whereas charset-normalizer don't train on anything, our solution +> is based on a completely different algorithm, still heuristic through, it does not need weights across every encoding tables. + +## ✨ 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 + +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.6](https://github.com/Ousret/charset_normalizer/compare/3.4.5...3.4.6) (2026-03-15) + +### Changed +- Flattened the logic in `charset_normalizer.md` for higher performance. Removed `eligible(..)` and `feed(...)` + in favor of `feed_info(...)`. +- Raised upper bound for mypy[c] to 1.20, for our optimized version. +- Updated `UNICODE_RANGES_COMBINED` using Unicode blocks v17. + +### Fixed +- Edge case where noise difference between two candidates can be almost insignificant. (#672) +- CLI `--normalize` writing to wrong path when passing multiple files in. (#702) + +### Misc +- Freethreaded pre-built wheels now shipped in PyPI starting with 3.14t. (#616) + +## [3.4.5](https://github.com/Ousret/charset_normalizer/compare/3.4.4...3.4.5) (2026-03-06) + +### Changed +- Update `setuptools` constraint to `setuptools>=68,<=82`. +- Raised upper bound of mypyc for the optional pre-built extension to v1.19.1 + +### Fixed +- Add explicit link to lib math in our optimized build. (#692) +- Logger level not restored correctly for empty byte sequences. (#701) +- TypeError when passing bytearray to from_bytes. (#703) + +### Misc +- Applied safe micro-optimizations in both our noise detector and language detector. +- Rewrote the `query_yes_no` function (inside CLI) to avoid using ambiguous licensed code. +- Added `cd.py` submodule into mypyc optional compilation to reduce further the performance impact. + +## [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/.venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/RECORD b/.venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/RECORD new file mode 100644 index 0000000..e8bc0a9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/RECORD @@ -0,0 +1,36 @@ +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/charset_normalizer/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/charset_normalizer/__main__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/charset_normalizer/api.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/charset_normalizer/cd.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/charset_normalizer/cli/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/charset_normalizer/cli/__main__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/charset_normalizer/constant.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/charset_normalizer/legacy.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/charset_normalizer/md.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/charset_normalizer/models.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/charset_normalizer/utils.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/charset_normalizer/version.cpython-39.pyc,, +../../../bin/normalizer,sha256=Ae3vx1W3XVfbS3gY4geUXcu6mHdiOvOnqIPsXQ01wQ4,279 +81d243bd2c585b0f4821__mypyc.cpython-39-darwin.so,sha256=o3gx0pvpyqcKc4vfiCIZcAceJBqCaHI_XE-sDnfsFdE,716632 +charset_normalizer-3.4.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +charset_normalizer-3.4.6.dist-info/METADATA,sha256=dk9sTA95N-j8mBeWuSnjw6zoAYbY9J7viW3Jukhd8S4,40556 +charset_normalizer-3.4.6.dist-info/RECORD,, +charset_normalizer-3.4.6.dist-info/WHEEL,sha256=vZCBzNID7SLZXwhPoyS-rfHwwQgJ9hkVF-7dFVJBO3g,139 +charset_normalizer-3.4.6.dist-info/entry_points.txt,sha256=ADSTKrkXZ3hhdOVFi6DcUEHQRS0xfxDIE_pEz4wLIXA,65 +charset_normalizer-3.4.6.dist-info/licenses/LICENSE,sha256=bQ1Bv-FwrGx9wkjJpj4lTQ-0WmDVCoJX0K-SxuJJuIc,1071 +charset_normalizer-3.4.6.dist-info/top_level.txt,sha256=c_vZbitqecT2GfK3zdxSTLCn8C-6pGnHQY5o_5Y32M0,47 +charset_normalizer/__init__.py,sha256=OKRxRv2Zhnqk00tqkN0c1BtJjm165fWXLydE52IKuHc,1590 +charset_normalizer/__main__.py,sha256=yzYxMR-IhKRHYwcSlavEv8oGdwxsR89mr2X09qXGdps,109 +charset_normalizer/api.py,sha256=6kHyIpfgxrjR_o_s_IGZ_zweFIfiGRBf-tnKRPqwg_E,37960 +charset_normalizer/cd.cpython-39-darwin.so,sha256=wRdSMsOLNGPEzRJr41_hjArH6qdIRm04LtH0ByMWn8E,66592 +charset_normalizer/cd.py,sha256=v0iPJweGsRegXywrM1LzUgqW9bJ1KFvIblQHP1jm5FQ,15174 +charset_normalizer/cli/__init__.py,sha256=D8I86lFk2-py45JvqxniTirSj_sFyE6sjaY_0-G1shc,136 +charset_normalizer/cli/__main__.py,sha256=E9FFSV1E2iOE_B2B1tJHQT9ExJqc60Ks_c-08sNawh8,11940 +charset_normalizer/constant.py,sha256=UMxXijQRKnoj27qP5vjNPJed2kJUB9rmCbRuLBjwIQ0,44431 +charset_normalizer/legacy.py,sha256=yBIFMNABNPE5JkdKOWyVo36fZtV9nm8bf37LrDWulz8,2661 +charset_normalizer/md.cpython-39-darwin.so,sha256=QupwHHEnbFKyOxwiM8j2AlV3nr8xD5EPSw_aeQGwqY8,66592 +charset_normalizer/md.py,sha256=AYCdfDX79FrgoId3zXqmbCuDcbGr1NRuGqgJN94Rx9Q,30441 +charset_normalizer/models.py,sha256=01sizRL-AUJ5FOzANwAtV6l2rQ0Dd5dZpJUfXv6HTxc,12360 +charset_normalizer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +charset_normalizer/utils.py,sha256=9cpi-_0-vC9pGDfuoarhC6VlF_Jxwx5Jsa_8I4w2D8k,12282 +charset_normalizer/version.py,sha256=w4FVpMHIJ5sT9HC5JROzcW8kYPHryvjSgIDqUM5w1uw,115 diff --git a/.venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/WHEEL new file mode 100644 index 0000000..3331fae --- /dev/null +++ b/.venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: setuptools (82.0.0) +Root-Is-Purelib: false +Tag: cp39-cp39-macosx_10_9_universal2 +Generator: delocate 0.13.0 + diff --git a/.venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/entry_points.txt b/.venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/entry_points.txt new file mode 100644 index 0000000..65619e7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +normalizer = charset_normalizer.cli:cli_detect diff --git a/.venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/licenses/LICENSE b/.venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/licenses/LICENSE new file mode 100644 index 0000000..9725772 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +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/.venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/top_level.txt new file mode 100644 index 0000000..89847be --- /dev/null +++ b/.venv/lib/python3.9/site-packages/charset_normalizer-3.4.6.dist-info/top_level.txt @@ -0,0 +1,2 @@ +81d243bd2c585b0f4821__mypyc +charset_normalizer diff --git a/.venv/lib/python3.9/site-packages/charset_normalizer/__init__.py b/.venv/lib/python3.9/site-packages/charset_normalizer/__init__.py new file mode 100644 index 0000000..0d3a379 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/charset_normalizer/__init__.py @@ -0,0 +1,48 @@ +""" +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/.venv/lib/python3.9/site-packages/charset_normalizer/__main__.py b/.venv/lib/python3.9/site-packages/charset_normalizer/__main__.py new file mode 100644 index 0000000..e0e76f7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/charset_normalizer/__main__.py @@ -0,0 +1,6 @@ +from __future__ import annotations + +from .cli import cli_detect + +if __name__ == "__main__": + cli_detect() diff --git a/.venv/lib/python3.9/site-packages/charset_normalizer/api.py b/.venv/lib/python3.9/site-packages/charset_normalizer/api.py new file mode 100644 index 0000000..1f32091 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/charset_normalizer/api.py @@ -0,0 +1,974 @@ +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, + IANA_SUPPORTED_SIMILAR, + 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_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") +) + +# Pre-compute a reordered encoding list: multibyte first, then single-byte. +# This allows the mb_definitive_match optimization to fire earlier, skipping +# all single-byte encodings for genuine CJK content. Multibyte codecs +# hard-fail (UnicodeDecodeError) on single-byte data almost instantly, so +# testing them first costs negligible time for non-CJK files. +_mb_supported: list[str] = [] +_sb_supported: list[str] = [] + +for _supported_enc in IANA_SUPPORTED: + try: + if is_multi_byte_encoding(_supported_enc): + _mb_supported.append(_supported_enc) + else: + _sb_supported.append(_supported_enc) + except ImportError: + _sb_supported.append(_supported_enc) + +IANA_SUPPORTED_MB_FIRST: list[str] = _mb_supported + _sb_supported + + +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) + 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] = [] + soft_failure_skip: set[str] = set() + success_fast_tracked: set[str] = set() + + # Cache for decoded payload deduplication: hash(decoded_payload) -> (mean_mess_ratio, cd_ratios_merged, passed) + # When multiple encodings decode to the exact same string, we can skip the expensive + # mess_ratio and coherence_ratio analysis and reuse the results from the first encoding. + payload_result_cache: dict[int, tuple[float, list[tuple[str, float]], bool]] = {} + + # When a definitive result (chaos=0.0 and good coherence) is found after testing + # the prioritized encodings (ascii, utf_8), we can significantly reduce the remaining + # work. Encodings that target completely different language families (e.g., Cyrillic + # when the definitive match is Latin) are skipped entirely. + # Additionally, for same-family encodings that pass chaos probing, we reuse the + # definitive match's coherence ratios instead of recomputing them — a major savings + # since coherence_ratio accounts for ~30% of total time on slow Latin files. + definitive_match_found: bool = False + definitive_target_languages: set[str] = set() + # After the definitive match fires, we cap the number of additional same-family + # single-byte encodings that pass chaos probing. Once we've accumulated enough + # good candidates (N), further same-family SB encodings are unlikely to produce + # a better best() result and just waste mess_ratio + coherence_ratio time. + # The first encoding to trigger the definitive match is NOT counted (it's already in). + post_definitive_sb_success_count: int = 0 + POST_DEFINITIVE_SB_CAP: int = 7 + + # When a non-UTF multibyte encoding passes chaos probing with significant multibyte + # content (decoded length < 98% of raw length), skip all remaining single-byte encodings. + # Rationale: multi-byte decoders (CJK) have strict byte-sequence validation — if they + # decode without error AND pass chaos probing with substantial multibyte content, the + # data is genuinely multibyte encoded. Single-byte encodings will always decode (every + # byte maps to something) but waste time on mess_ratio before failing. + # The 98% threshold prevents false triggers on files that happen to have a few valid + # multibyte pairs (e.g., cp424/_ude_1.txt where big5 decodes with 99% ratio). + mb_definitive_match_found: bool = False + + 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_MB_FIRST: + 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 + + # Skip encodings similar to ones that already soft-failed (high mess ratio). + # Checked BEFORE the expensive decode attempt. + if encoding_iana in soft_failure_skip: + logger.log( + TRACE, + "%s is deemed too similar to a code page that was already considered unsuited. Continuing!", + encoding_iana, + ) + continue + + # Skip encodings that were already fast-tracked from a similar successful encoding. + if encoding_iana in success_fast_tracked: + logger.log( + TRACE, + "Skipping %s: already fast-tracked from a similar successful encoding.", + encoding_iana, + ) + continue + + try: + is_multi_byte_decoder: bool = is_multi_byte_encoding(encoding_iana) + except (ModuleNotFoundError, ImportError): # Defensive: + logger.log( + TRACE, + "Encoding %s does not provide an IncrementalDecoder", + encoding_iana, + ) + continue + + # When we've already found a definitive match (chaos=0.0 with good coherence) + # after testing the prioritized encodings, skip encodings that target + # completely different language families. This avoids running expensive + # mess_ratio + coherence_ratio on clearly unrelated candidates (e.g., Cyrillic + # when the definitive match is Latin-based). + if definitive_match_found: + if not is_multi_byte_decoder: + enc_languages = set(encoding_languages(encoding_iana)) + else: + enc_languages = set(mb_encoding_languages(encoding_iana)) + if not enc_languages.intersection(definitive_target_languages): + logger.log( + TRACE, + "Skipping %s: definitive match already found, this encoding targets different languages (%s vs %s).", + encoding_iana, + enc_languages, + definitive_target_languages, + ) + continue + + # After the definitive match, cap the number of additional same-family + # single-byte encodings that pass chaos probing. This avoids testing the + # tail of rare, low-value same-family encodings (mac_iceland, cp860, etc.) + # that almost never change best() but each cost ~1-2ms of mess_ratio + coherence. + if ( + definitive_match_found + and not is_multi_byte_decoder + and post_definitive_sb_success_count >= POST_DEFINITIVE_SB_CAP + ): + logger.log( + TRACE, + "Skipping %s: already accumulated %d same-family results after definitive match (cap=%d).", + encoding_iana, + post_definitive_sb_success_count, + POST_DEFINITIVE_SB_CAP, + ) + continue + + # When a multibyte encoding with significant multibyte content has already + # passed chaos probing, skip all single-byte encodings. They will either fail + # chaos probing (wasting mess_ratio time) or produce inferior results. + if mb_definitive_match_found and not is_multi_byte_decoder: + logger.log( + TRACE, + "Skipping single-byte %s: multi-byte definitive match already found.", + 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 + + 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, + ) + + # Payload-hash deduplication: if another encoding already decoded to the + # exact same string, reuse its mess_ratio and coherence results entirely. + # This is strictly more general than the old IANA_SUPPORTED_SIMILAR approach + # because it catches ALL identical decoding, not just pre-mapped ones. + if decoded_payload is not None and not is_multi_byte_decoder: + payload_hash: int = hash(decoded_payload) + cached = payload_result_cache.get(payload_hash) + if cached is not None: + cached_mess, cached_cd, cached_passed = cached + if cached_passed: + # The previous encoding with identical output passed chaos probing. + fast_match = CharsetMatch( + sequences, + encoding_iana, + cached_mess, + bom_or_sig_available, + cached_cd, + ( + 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(fast_match) + success_fast_tracked.add(encoding_iana) + logger.log( + TRACE, + "%s fast-tracked (identical decoded payload to a prior encoding, chaos=%f %%).", + encoding_iana, + round(cached_mess * 100, ndigits=3), + ) + + if ( + encoding_iana in [specified_encoding, "ascii", "utf_8"] + and cached_mess < 0.1 + ): + if cached_mess == 0.0: + logger.debug( + "Encoding detection: %s is most likely the one.", + fast_match.encoding, + ) + if explain: + logger.removeHandler(explain_handler) + logger.setLevel(previous_logger_level) + return CharsetMatches([fast_match]) + early_stop_results.append(fast_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: + logger.removeHandler(explain_handler) + logger.setLevel(previous_logger_level) + return CharsetMatches([probable_result]) + + continue + else: + # The previous encoding with identical output failed chaos probing. + tested_but_soft_failure.append(encoding_iana) + logger.log( + TRACE, + "%s fast-skipped (identical decoded payload to a prior encoding that failed chaos probing).", + encoding_iana, + ) + # Prepare fallbacks for special encodings even when skipped. + if enable_fallback and encoding_iana in [ + "ascii", + "utf_8", + specified_encoding, + "utf_16", + "utf_32", + ]: + 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 + + 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) + if encoding_iana in IANA_SUPPORTED_SIMILAR: + soft_failure_skip.update(IANA_SUPPORTED_SIMILAR[encoding_iana]) + # Cache this soft-failure so identical decoding from other encodings + # can be skipped immediately. + if decoded_payload is not None and not is_multi_byte_decoder: + payload_result_cache.setdefault( + hash(decoded_payload), (mean_mess_ratio, [], False) + ) + 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 = [] + + # Run coherence detection on all chunks. We previously tried limiting to + # 1-2 chunks for post-definitive encodings to save time, but this caused + # coverage regressions by producing unrepresentative coherence scores. + # The SB cap and language-family skip optimizations provide sufficient + # speedup without sacrificing coherence accuracy. + if encoding_iana != "ascii": + # We shall skip the CD when its about ASCII + # Most of the time its not relevant to run "language-detection" on it. + 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) + else: + 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) + + # Cache the successful result for payload-hash deduplication. + if decoded_payload is not None and not is_multi_byte_decoder: + payload_result_cache.setdefault( + hash(decoded_payload), + (mean_mess_ratio, cd_ratios_merged, True), + ) + + # Count post-definitive same-family SB successes for the early termination cap. + # Only count low-mess encodings (< 2%) toward the cap. High-mess encodings are + # marginal results that shouldn't prevent better-quality candidates from being + # tested. For example, iso8859_4 (mess=0%) should not be skipped just because + # 7 high-mess Latin encodings (cp1252 at 8%, etc.) were tried first. + if ( + definitive_match_found + and not is_multi_byte_decoder + and mean_mess_ratio < 0.02 + ): + post_definitive_sb_success_count += 1 + + 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 = early_stop_results.best() # type: ignore[assignment] + logger.debug( + "Encoding detection: %s is most likely the one.", + probable_result.encoding, # type: ignore[union-attr] + ) + if explain: # Defensive: ensure exit path clean handler + logger.removeHandler(explain_handler) + logger.setLevel(previous_logger_level) + + return CharsetMatches([probable_result]) + + # Once we find a result with good coherence (>= 0.5) after testing the + # prioritized encodings (ascii, utf_8), activate "definitive mode": skip + # encodings that target completely different language families. This avoids + # running expensive mess_ratio + coherence_ratio on clearly unrelated + # candidates (e.g., Cyrillic encodings when the match is Latin-based). + # We require coherence >= 0.5 to avoid false positives (e.g., cp1251 decoding + # Hebrew text with 0.0 chaos but wrong language detection at coherence 0.33). + if not definitive_match_found and not is_multi_byte_decoder: + best_coherence = ( + max((v for _, v in cd_ratios_merged), default=0.0) + if cd_ratios_merged + else 0.0 + ) + if best_coherence >= 0.5 and "ascii" in tested and "utf_8" in tested: + definitive_match_found = True + definitive_target_languages.update(target_languages) + logger.log( + TRACE, + "Definitive match found: %s (chaos=%.3f, coherence=%.2f). Encodings targeting different language families will be skipped.", + encoding_iana, + mean_mess_ratio, + best_coherence, + ) + + # When a non-UTF multibyte encoding passes chaos probing with significant + # multibyte content (decoded < 98% of raw), activate mb_definitive_match. + # This skips all remaining single-byte encodings which would either soft-fail + # (running expensive mess_ratio for nothing) or produce inferior results. + if ( + not mb_definitive_match_found + and is_multi_byte_decoder + and multi_byte_bonus + and decoded_payload is not None + and len(decoded_payload) < length * 0.98 + and encoding_iana + not in { + "utf_8", + "utf_8_sig", + "utf_16", + "utf_16_be", + "utf_16_le", + "utf_32", + "utf_32_be", + "utf_32_le", + "utf_7", + } + and "ascii" in tested + and "utf_8" in tested + ): + mb_definitive_match_found = True + logger.log( + TRACE, + "Multi-byte definitive match: %s (chaos=%.3f, decoded=%d/%d=%.1f%%). Single-byte encodings will be skipped.", + encoding_iana, + mean_mess_ratio, + len(decoded_payload), + length, + len(decoded_payload) / length * 100, + ) + + 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/.venv/lib/python3.9/site-packages/charset_normalizer/cd.cpython-39-darwin.so b/.venv/lib/python3.9/site-packages/charset_normalizer/cd.cpython-39-darwin.so new file mode 100755 index 0000000000000000000000000000000000000000..47e5ba86e11850b684add24f9e88e2d9fcce337f GIT binary patch literal 66592 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/charset_normalizer/cd.py b/.venv/lib/python3.9/site-packages/charset_normalizer/cd.py new file mode 100644 index 0000000..9545d35 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/charset_normalizer/cd.py @@ -0,0 +1,454 @@ +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, + _FREQUENCIES_SET, + _FREQUENCIES_RANK, +) +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( # Defensive: + "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]] = [] + + characters_set: frozenset[str] = frozenset(characters) + 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(_FREQUENCIES_SET[language] & characters_set) + + 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") # Defensive: + + character_approved_count: int = 0 + frequencies_language_set: frozenset[str] = _FREQUENCIES_SET[language] + lang_rank: dict[str, int] = _FREQUENCIES_RANK[language] + + ordered_characters_count: int = len(ordered_characters) + target_language_characters_count: int = len(FREQUENCIES[language]) + + large_alphabet: bool = target_language_characters_count > 26 + + expected_projection_ratio: float = ( + target_language_characters_count / ordered_characters_count + ) + + # Pre-built rank dict for ordered_characters (avoids repeated list slicing). + ordered_rank: dict[str, int] = { + char: rank for rank, char in enumerate(ordered_characters) + } + + # Pre-compute characters common to both orderings. + # Avoids repeated `c in ordered_rank` dict lookups in the inner counts. + common_chars: list[tuple[int, int]] = [ + (lr, ordered_rank[c]) for c, lr in lang_rank.items() if c in ordered_rank + ] + + # Pre-extract lr and orr arrays for faster iteration in the inner loop. + # Plain integer loops with local arrays are much faster under mypyc than + # generator expression sums over a list of tuples. + common_count: int = len(common_chars) + common_lr: list[int] = [p[0] for p in common_chars] + common_orr: list[int] = [p[1] for p in common_chars] + + 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 = lang_rank[character] + 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 + + # Count how many characters appear "before" in both orderings, + # and how many appear "at or after" in both orderings. + # Single pass over pre-extracted arrays — much faster under mypyc + # than two generator expression sums. + before_match_count: int = 0 + after_match_count: int = 0 + for i in range(common_count): + lr_i: int = common_lr[i] + orr_i: int = common_orr[i] + if lr_i < character_rank_in_language: + if orr_i < character_rank: + before_match_count += 1 + else: + if orr_i >= character_rank: + after_match_count += 1 + + after_len: int = target_language_characters_count - character_rank_in_language + + if character_rank_in_language == 0 and before_match_count <= 4: + character_approved_count += 1 + continue + + if after_len == 0 and after_match_count <= 4: + character_approved_count += 1 + continue + + if ( + character_rank_in_language > 0 + and before_match_count / character_rank_in_language >= 0.4 + ) or (after_len > 0 and after_match_count / after_len >= 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, list[str]] = {} + + # Fast path: track single-layer key to skip dict iteration for single-script text. + single_layer_key: str | None = None + multi_layer: bool = False + + # Cache the last character_range and its resolved layer to avoid repeated + # is_suspiciously_successive_range calls for consecutive same-range chars. + prev_character_range: str | None = None + prev_layer_target: str | None = None + + for character in decoded_sequence: + if character.isalpha() is False: + continue + + # ASCII fast-path: a-z and A-Z are always "Basic Latin". + # Avoids unicode_range() function call overhead for the most common case. + character_ord: int = ord(character) + if character_ord < 128: + character_range: str | None = "Basic Latin" + else: + character_range = unicode_range(character) + + if character_range is None: + continue + + # Fast path: same range as previous character → reuse cached layer target. + if character_range == prev_character_range: + if prev_layer_target is not None: + layers[prev_layer_target].append(character) + continue + + layer_target_range: str | None = None + + if multi_layer: + for discovered_range in layers: + if ( + is_suspiciously_successive_range(discovered_range, character_range) + is False + ): + layer_target_range = discovered_range + break + elif single_layer_key is not None: + if ( + is_suspiciously_successive_range(single_layer_key, character_range) + is False + ): + layer_target_range = single_layer_key + + if layer_target_range is None: + layer_target_range = character_range + + if layer_target_range not in layers: + layers[layer_target_range] = [] + if single_layer_key is None: + single_layer_key = layer_target_range + else: + multi_layer = True + + layers[layer_target_range].append(character) + + # Cache for next iteration + prev_character_range = character_range + prev_layer_target = layer_target_range + + return ["".join(chars).lower() for chars in 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 = len(layer) + + 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/.venv/lib/python3.9/site-packages/charset_normalizer/cli/__init__.py b/.venv/lib/python3.9/site-packages/charset_normalizer/cli/__init__.py new file mode 100644 index 0000000..543a5a4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/charset_normalizer/cli/__init__.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .__main__ import cli_detect, query_yes_no + +__all__ = ( + "cli_detect", + "query_yes_no", +) diff --git a/.venv/lib/python3.9/site-packages/charset_normalizer/cli/__main__.py b/.venv/lib/python3.9/site-packages/charset_normalizer/cli/__main__.py new file mode 100644 index 0000000..ad843c1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/charset_normalizer/cli/__main__.py @@ -0,0 +1,362 @@ +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: # Defensive: + """Ask a yes/no question via input() and return the answer as a bool.""" + prompt = " [Y/n] " if default == "yes" else " [y/N] " + + while True: + choice = input(question + prompt).strip().lower() + if not choice: + return default == "yes" + if choice in ("y", "yes"): + return True + if choice in ("n", "no"): + return False + print("Please respond with 'y' or '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: + cli_result = 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, + ) + x_.append(cli_result) + + 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: + cli_result.unicode_path = join(dir_path, ".".join(o_)) + + with open(cli_result.unicode_path, "wb") as fp: + fp.write(best_guess.output()) + except OSError as e: # Defensive: + 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__": # Defensive: + cli_detect() diff --git a/.venv/lib/python3.9/site-packages/charset_normalizer/constant.py b/.venv/lib/python3.9/site-packages/charset_normalizer/constant.py new file mode 100644 index 0000000..6ed7795 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/charset_normalizer/constant.py @@ -0,0 +1,2050 @@ +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/17.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), + "Todhri": range(67008, 67072), + "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), + "Sidetic": range(67904, 67936), + "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), + "Garay": range(68928, 69008), + "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), + "Tulu-Tigalari": range(70528, 70656), + "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), + "Myanmar Extended-C": range(71376, 71424), + "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), + "Sharada Supplement": range(72544, 72576), + "Sunuwar": range(72640, 72704), + "Bhaiksuki": range(72704, 72816), + "Marchen": range(72816, 72896), + "Masaram Gondi": range(72960, 73056), + "Gunjala Gondi": range(73056, 73136), + "Tolong Siki": range(73136, 73200), + "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), + "Egyptian Hieroglyphs Extended-A": range(78944, 82944), + "Anatolian Hieroglyphs": range(82944, 83584), + "Gurung Khema": range(90368, 90432), + "Bamum Supplement": range(92160, 92736), + "Mro": range(92736, 92784), + "Tangsa": range(92784, 92880), + "Bassa Vah": range(92880, 92928), + "Pahawh Hmong": range(92928, 93072), + "Kirat Rai": range(93504, 93568), + "Medefaidrin": range(93760, 93856), + "Beria Erfe": range(93856, 93920), + "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), + "Tangut Components Supplement": range(101760, 101888), + "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), + "Symbols for Legacy Computing Supplement": range(117760, 118464), + "Miscellaneous Symbols Supplement": range(118464, 118528), + "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), + "Ol Onal": range(124368, 124416), + "Tai Yo": range(124608, 124672), + "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(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 Unified Ideographs Extension I": range(191472, 192096), + "CJK Compatibility Ideographs Supplement": range(194560, 195104), + "CJK Unified Ideographs Extension G": range(196608, 201552), + "CJK Unified Ideographs Extension H": range(201552, 205744), + "CJK Unified Ideographs Extension J": range(205744, 210048), + "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: frozenset[str] = frozenset( + { + "<", + ">", + "=", + ":", + "/", + "&", + ";", + "{", + "}", + "[", + "]", + ",", + "|", + '"', + "-", + "(", + ")", + } +) + +# Sample character sets — replace with full lists if needed +COMMON_CHINESE_CHARACTERS = "的一是在不了有和人这中大为上个国我以要他时来用们生到作地于出就分对成会可主发年动同工也能下过子说产种面而方后多定行学法所民得经十三之进着等部度家电力里如水化高自二理起小物现实加量都两体制机当使点从业本去把性好应开它合还因由其些然前外天政四日那社义事平形相全表间样与关各重新线内数正心反你明看原又么利比或但质气第向道命此变条只没结解问意建月公无系军很情者最立代想已通并提直题党程展五果料象员革位入常文总次品式活设及管特件长求老头基资边流路级少图山统接知较将组见计别她手角期根论运农指几九区强放决西被干做必战先回则任取据处队南给色光门即保治北造百规热领七海口东导器压志世金增争济阶油思术极交受联什认六共权收证改清己美再采转更单风切打白教速花带安场身车例真务具万每目至达走积示议声报斗完类八离华名确才科张信马节话米整空元况今集温传土许步群广石记需段研界拉林律叫且究观越织装影算低持音众书布复容儿须际商非验连断深难近矿千周委素技备半办青省列习响约支般史感劳便团往酸历市克何除消构府太准精值号率族维划选标写存候毛亲快效斯院查江型眼王按格养易置派层片始却专状育厂京识适属圆包火住调满县局照参红细引听该铁价严龙飞" + +COMMON_JAPANESE_CHARACTERS = "日一国年大十二本中長出三時行見月分後前生五間上東四今金九入学高円子外八六下来気小七山話女北午百書先名川千水半男西電校語土木聞食車何南万毎白天母火右読友左休父雨" + +COMMON_KOREAN_CHARACTERS = "一二三四五六七八九十百千萬上下左右中人女子大小山川日月火水木金土父母天地國名年時文校學生" + +# Combine all into a frozenset +COMMON_CJK_CHARACTERS = frozenset( + "".join( + [ + COMMON_CHINESE_CHARACTERS, + COMMON_JAPANESE_CHARACTERS, + COMMON_KOREAN_CHARACTERS, + ] + ) +) + +KO_NAMES: frozenset[str] = frozenset({"johab", "cp949", "euc_kr"}) +ZH_NAMES: frozenset[str] = frozenset({"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) + +# Bit flags for unified character classification. +# A single unicodedata.name() call sets all relevant flags at once. +_LATIN: int = 1 +_ACCENTUATED: int = 1 << 1 +_CJK: int = 1 << 2 +_HANGUL: int = 1 << 3 +_KATAKANA: int = 1 << 4 +_HIRAGANA: int = 1 << 5 +_THAI: int = 1 << 6 +_ARABIC: int = 1 << 7 +_ARABIC_ISOLATED_FORM: int = 1 << 8 + +_ACCENT_KEYWORDS: tuple[str, ...] = ( + "WITH GRAVE", + "WITH ACUTE", + "WITH CEDILLA", + "WITH DIAERESIS", + "WITH CIRCUMFLEX", + "WITH TILDE", + "WITH MACRON", + "WITH RING ABOVE", +) + +# Pre-built lookup structures for FREQUENCIES (computed once at import time). +# character -> rank mapping per language (replaces list .index() calls). +_FREQUENCIES_RANK: dict[str, dict[str, int]] = { + lang: {char: rank for rank, char in enumerate(chars)} + for lang, chars in FREQUENCIES.items() +} + +# frozenset per language (avoids rebuilding set() per call). +_FREQUENCIES_SET: dict[str, frozenset[str]] = { + lang: frozenset(chars) for lang, chars in FREQUENCIES.items() +} diff --git a/.venv/lib/python3.9/site-packages/charset_normalizer/legacy.py b/.venv/lib/python3.9/site-packages/charset_normalizer/legacy.py new file mode 100644 index 0000000..293c1ef --- /dev/null +++ b/.venv/lib/python3.9/site-packages/charset_normalizer/legacy.py @@ -0,0 +1,79 @@ +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 + +if TYPE_CHECKING: + from typing 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/.venv/lib/python3.9/site-packages/charset_normalizer/md.cpython-39-darwin.so b/.venv/lib/python3.9/site-packages/charset_normalizer/md.cpython-39-darwin.so new file mode 100755 index 0000000000000000000000000000000000000000..7137f4216f8df292fc81029038f58aa2d75a4dd8 GIT binary patch literal 66592 zcmeI5acou99mh{!TUw=*Hh=>GeMP&0PN1x?3Km}h!|Om>Y;nyd9$xz%^vP@AG2n)P@fA^kSZeL3q zV~BBnPjc?>{Lb%pe&?LeeJ|;~=bYC!F75rTQp%U5R32ouQbiCmkW~Td3Z$S^0Qq-* zTndqyNPq-LfCNZ@1W14cNPq-LfCNZ@1U_X1{(RxROL*|l!h^rcM;?ou$Wdymna32! zH}N7s>-xJI>P@9r$m;@cGI}oR+$?6Tt;j>xWyj2Ted8Qk+PH|0VO&l*s^hXE@lsY> zV``u(Gu{dFred=3aCNsqzKJ)+R51s? z-`8bT^;I5=dq1xr<#Xa@X{Gi0xaYP)A3mOZw(Nws(%wzN-+Q06j)iopy`?c8(+Mj% zBwoH5m)`^s-5XC{V#(*uO;+4yo1L#)5LSfC%QM@<(5uKxCN25URiz5io2Fo zyWa`FW4B+QNzv8KO#o^o*-bN)>X<8v-}&C$l^F1Wq23V$B=Cfyh8 zJb~8zPuo%6grV)9WxZlL&~>^0{jm1SNdNMOErw(F|B~%;qW>0= z!}nieyA0|-U3ycNQt8rC`}zg@It$m8+o#_GQB{{Ny?|C-&A6zO@1)b|=4n#;tCVh+ z(h(_rRZ0g@swfN3EB{hscwW=|iusLm*OgbyE7Q8AJ<;BzqODO&H#LWnsfeXpai#1{@+?q*C`EQ8rAqAOey;}> z|IPDbv5`}aEH!elH#HI<0TLhq5+DH*AOR8}0TLhq5+H$36M^EAe0v46IDkdEu)V0R z8?nxeMetP{vBJ19e{n$9wl57OQf;w_ULLV(W39^^|DAP7K=C8Yne8;EYw!Zn-q1u=-H}onq06h>C;&J zhC!<$yil3sQ0|-BA+JZ&BglnV7EZsp|hul>x zh|7&mB%_-`Rz!xxL;@s00wh2JBtQZrKmsH{0wnMmARznyame3(j+K4I^0?fO2idJ= z?xXJi4;V*R7+s!&-7Kcj_W##cikDK&=80N#d4?ZIL-yB?y#IgSVyER?6F_utJa_-U z`GEtBPvTv9|NpxtUeLr7-5U=N`W?n(i_!M~m*44lyZirL-HkCQHY!Kk|Noqc*I)vO zo*Bc9EmH35kC2`H@Oo*@J$24b_5dm+1?=k(DepNX%WWz`9)AD-(0KCMvQuF2{`$=Q z|1z$A3L0!A9$pX2Dl-F!vtiW$nBBY>auoewE{^4;X`HRlK*<+~t!#C{OE_$T&% z%yR^2KhimZd%xLz-Sf{aI{LFS??3UCvfIYYL!W+RXAU^MfU^WrwpkTS&ynq2^0{)8 zd_OCYWy~`6k9^LcH{+ZEbV<{Z?S5XxcqA|r36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg!2dOY^Z|AFY)#gVix2yDTzph@W3pZ9&>h|Q75OJ|jn&Z~5K4~~o;@%r^<>tk0MGtOfU z8S_lkjW*^P@W~wWMAUthF&D#sq%q?>>#vKzK>2PwW|zl!%GY|zg-*E){$<}*-5)G} zWe3iF4z#Vs@zV3@^ylP5Ddqp5EWfiaH*gt=g!% zR8lPt=-T$Bp+u@J7SYQiR&6}mYQYR!zHO^+NyL+ucEsKBF#N<=WE*Q5AB;3v(yq#~ zl66)x+PYq>lVL?%y(|)n#o|qpNK>QcooZoSFewEqF~8at5y literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/charset_normalizer/md.py b/.venv/lib/python3.9/site-packages/charset_normalizer/md.py new file mode 100644 index 0000000..b41d9cf --- /dev/null +++ b/.venv/lib/python3.9/site-packages/charset_normalizer/md.py @@ -0,0 +1,936 @@ +from __future__ import annotations + +import sys +from functools import lru_cache +from logging import getLogger + +if sys.version_info >= (3, 8): + from typing import final +else: + try: + from typing_extensions import final + except ImportError: + + def final(cls): # type: ignore[misc,no-untyped-def] + return cls + + +from .constant import ( + COMMON_CJK_CHARACTERS, + COMMON_SAFE_ASCII_CHARACTERS, + TRACE, + UNICODE_SECONDARY_RANGE_KEYWORD, + _ACCENTUATED, + _ARABIC, + _ARABIC_ISOLATED_FORM, + _CJK, + _HANGUL, + _HIRAGANA, + _KATAKANA, + _LATIN, + _THAI, +) +from .utils import ( + _character_flags, + is_emoticon, + is_punctuation, + is_separator, + is_symbol, + remove_accent, + unicode_range, +) + +# Combined bitmask for CJK/Hangul/Katakana/Hiragana/Thai glyph detection. +_GLYPH_MASK: int = _CJK | _HANGUL | _KATAKANA | _HIRAGANA | _THAI + + +@final +class CharInfo: + """Pre-computed character properties shared across all detectors. + + Instantiated once and reused via :meth:`update` on every character + in the hot loop so that redundant calls to str methods + (``isalpha``, ``isupper``, …) and cached utility functions + (``_character_flags``, ``is_punctuation``, …) are avoided when + several plugins need the same information. + """ + + __slots__ = ( + "character", + "printable", + "alpha", + "upper", + "lower", + "space", + "digit", + "is_ascii", + "case_variable", + "flags", + "accentuated", + "latin", + "is_cjk", + "is_arabic", + "is_glyph", + "punct", + "sym", + ) + + def __init__(self) -> None: + self.character: str = "" + self.printable: bool = False + self.alpha: bool = False + self.upper: bool = False + self.lower: bool = False + self.space: bool = False + self.digit: bool = False + self.is_ascii: bool = False + self.case_variable: bool = False + self.flags: int = 0 + self.accentuated: bool = False + self.latin: bool = False + self.is_cjk: bool = False + self.is_arabic: bool = False + self.is_glyph: bool = False + self.punct: bool = False + self.sym: bool = False + + def update(self, character: str) -> None: + """Update all properties for *character* (called once per character).""" + self.character = character + + # ASCII fast-path: for characters with ord < 128, we can skip + # _character_flags() entirely and derive most properties from ord. + o: int = ord(character) + if o < 128: + self.is_ascii = True + self.accentuated = False + self.is_cjk = False + self.is_arabic = False + self.is_glyph = False + # ASCII alpha: a-z (97-122) or A-Z (65-90) + if 65 <= o <= 90: + # Uppercase ASCII letter + self.alpha = True + self.upper = True + self.lower = False + self.space = False + self.digit = False + self.printable = True + self.case_variable = True + self.flags = _LATIN + self.latin = True + self.punct = False + self.sym = False + elif 97 <= o <= 122: + # Lowercase ASCII letter + self.alpha = True + self.upper = False + self.lower = True + self.space = False + self.digit = False + self.printable = True + self.case_variable = True + self.flags = _LATIN + self.latin = True + self.punct = False + self.sym = False + elif 48 <= o <= 57: + # ASCII digit 0-9 + self.alpha = False + self.upper = False + self.lower = False + self.space = False + self.digit = True + self.printable = True + self.case_variable = False + self.flags = 0 + self.latin = False + self.punct = False + self.sym = False + elif o == 32 or (9 <= o <= 13): + # Space, tab, newline, etc. + self.alpha = False + self.upper = False + self.lower = False + self.space = True + self.digit = False + self.printable = o == 32 + self.case_variable = False + self.flags = 0 + self.latin = False + self.punct = False + self.sym = False + else: + # Other ASCII (punctuation, symbols, control chars) + self.printable = character.isprintable() + self.alpha = False + self.upper = False + self.lower = False + self.space = False + self.digit = False + self.case_variable = False + self.flags = 0 + self.latin = False + self.punct = is_punctuation(character) if self.printable else False + self.sym = is_symbol(character) if self.printable else False + else: + # Non-ASCII path + self.is_ascii = False + self.printable = character.isprintable() + self.alpha = character.isalpha() + self.upper = character.isupper() + self.lower = character.islower() + self.space = character.isspace() + self.digit = character.isdigit() + self.case_variable = self.lower != self.upper + + # Flag-based classification (single unicodedata.name() call, lru-cached) + flags: int + if self.alpha: + flags = _character_flags(character) + else: + flags = 0 + self.flags = flags + self.accentuated = bool(flags & _ACCENTUATED) + self.latin = bool(flags & _LATIN) + self.is_cjk = bool(flags & _CJK) + self.is_arabic = bool(flags & _ARABIC) + self.is_glyph = bool(flags & _GLYPH_MASK) + + # Eagerly compute punct and sym (avoids property dispatch overhead + # on 300K+ accesses in the hot loop). + self.punct = is_punctuation(character) if self.printable else False + self.sym = is_symbol(character) if self.printable else False + + +class MessDetectorPlugin: + """ + Base abstract class used for mess detection plugins. + All detectors MUST extend and implement given methods. + """ + + __slots__ = () + + def feed_info(self, character: str, info: CharInfo) -> None: + """ + The main routine to be executed upon character. + Insert the logic in witch the text would be considered chaotic. + """ + raise NotImplementedError # Defensive: + + def reset(self) -> None: # Defensive: + """ + 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 # Defensive: + + +@final +class TooManySymbolOrPunctuationPlugin(MessDetectorPlugin): + __slots__ = ( + "_punctuation_count", + "_symbol_count", + "_character_count", + "_last_printable_char", + "_frenzy_symbol_in_word", + ) + + 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 feed_info(self, character: str, info: CharInfo) -> None: + """Optimized feed using pre-computed character info.""" + self._character_count += 1 + + if ( + character != self._last_printable_char + and character not in COMMON_SAFE_ASCII_CHARACTERS + ): + if info.punct: + self._punctuation_count += 1 + elif not info.digit and info.sym and not is_emoticon(character): + 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 + + +@final +class TooManyAccentuatedPlugin(MessDetectorPlugin): + __slots__ = ("_character_count", "_accentuated_count") + + def __init__(self) -> None: + self._character_count: int = 0 + self._accentuated_count: int = 0 + + def feed_info(self, character: str, info: CharInfo) -> None: + """Optimized feed using pre-computed character info.""" + self._character_count += 1 + + if info.accentuated: + 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 + + +@final +class UnprintablePlugin(MessDetectorPlugin): + __slots__ = ("_unprintable_count", "_character_count") + + def __init__(self) -> None: + self._unprintable_count: int = 0 + self._character_count: int = 0 + + def feed_info(self, character: str, info: CharInfo) -> None: + """Optimized feed using pre-computed character info.""" + if ( + not info.space + and not info.printable + and character != "\x1a" + and character != "\ufeff" + ): + 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: # Defensive: + return 0.0 + + return (self._unprintable_count * 8) / self._character_count + + +@final +class SuspiciousDuplicateAccentPlugin(MessDetectorPlugin): + __slots__ = ( + "_successive_count", + "_character_count", + "_last_latin_character", + "_last_was_accentuated", + ) + + def __init__(self) -> None: + self._successive_count: int = 0 + self._character_count: int = 0 + + self._last_latin_character: str | None = None + self._last_was_accentuated: bool = False + + def feed_info(self, character: str, info: CharInfo) -> None: + """Optimized feed using pre-computed character info.""" + self._character_count += 1 + if ( + self._last_latin_character is not None + and info.accentuated + and self._last_was_accentuated + ): + if info.upper and self._last_latin_character.isupper(): + self._successive_count += 1 + if remove_accent(character) == remove_accent(self._last_latin_character): + self._successive_count += 1 + self._last_latin_character = character + self._last_was_accentuated = info.accentuated + + def reset(self) -> None: # Abstract + self._successive_count = 0 + self._character_count = 0 + self._last_latin_character = None + self._last_was_accentuated = False + + @property + def ratio(self) -> float: + if self._character_count == 0: + return 0.0 + + return (self._successive_count * 2) / self._character_count + + +@final +class SuspiciousRange(MessDetectorPlugin): + __slots__ = ( + "_suspicious_successive_range_count", + "_character_count", + "_last_printable_seen", + "_last_printable_range", + ) + + def __init__(self) -> None: + self._suspicious_successive_range_count: int = 0 + self._character_count: int = 0 + self._last_printable_seen: str | None = None + self._last_printable_range: str | None = None + + def feed_info(self, character: str, info: CharInfo) -> None: + """Optimized feed using pre-computed character info.""" + self._character_count += 1 + + if info.space or info.punct or character in COMMON_SAFE_ASCII_CHARACTERS: + self._last_printable_seen = None + self._last_printable_range = None + return + + if self._last_printable_seen is None: + self._last_printable_seen = character + self._last_printable_range = unicode_range(character) + return + + unicode_range_a: str | None = self._last_printable_range + 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 + self._last_printable_range = unicode_range_b + + def reset(self) -> None: # Abstract + self._character_count = 0 + self._suspicious_successive_range_count = 0 + self._last_printable_seen = None + self._last_printable_range = 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 + + +@final +class SuperWeirdWordPlugin(MessDetectorPlugin): + __slots__ = ( + "_word_count", + "_bad_word_count", + "_foreign_long_count", + "_is_current_word_bad", + "_foreign_long_watch", + "_character_count", + "_bad_character_count", + "_buffer_length", + "_buffer_last_char", + "_buffer_last_char_accentuated", + "_buffer_accent_count", + "_buffer_glyph_count", + "_buffer_upper_count", + ) + + 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_length: int = 0 + self._buffer_last_char: str | None = None + self._buffer_last_char_accentuated: bool = False + self._buffer_accent_count: int = 0 + self._buffer_glyph_count: int = 0 + self._buffer_upper_count: int = 0 + + def feed_info(self, character: str, info: CharInfo) -> None: + """Optimized feed using pre-computed character info.""" + if info.alpha: + self._buffer_length += 1 + self._buffer_last_char = character + + if info.upper: + self._buffer_upper_count += 1 + + self._buffer_last_char_accentuated = info.accentuated + + if info.accentuated: + self._buffer_accent_count += 1 + if ( + not self._foreign_long_watch + and (not info.latin or info.accentuated) + and not info.is_glyph + ): + self._foreign_long_watch = True + if info.is_glyph: + self._buffer_glyph_count += 1 + return + if not self._buffer_length: + return + if info.space or info.punct or is_separator(character): + self._word_count += 1 + buffer_length: int = self._buffer_length + + self._character_count += buffer_length + + if buffer_length >= 4: + if self._buffer_accent_count / buffer_length >= 0.5: + self._is_current_word_bad = True + elif ( + self._buffer_last_char_accentuated + and self._buffer_last_char.isupper() # type: ignore[union-attr] + and self._buffer_upper_count != buffer_length + ): + 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: + probable_camel_cased: bool = ( + self._buffer_upper_count > 0 + and self._buffer_upper_count / buffer_length <= 0.3 + ) + + 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 += buffer_length + self._is_current_word_bad = False + + self._foreign_long_watch = False + self._buffer_length = 0 + self._buffer_last_char = None + self._buffer_last_char_accentuated = False + self._buffer_accent_count = 0 + self._buffer_glyph_count = 0 + self._buffer_upper_count = 0 + elif ( + character not in {"<", ">", "-", "=", "~", "|", "_"} + and not info.digit + and info.sym + ): + self._is_current_word_bad = True + self._buffer_length += 1 + self._buffer_last_char = character + self._buffer_last_char_accentuated = False + + def reset(self) -> None: # Abstract + self._buffer_length = 0 + self._buffer_last_char = None + self._buffer_last_char_accentuated = False + 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 + self._buffer_accent_count = 0 + self._buffer_glyph_count = 0 + self._buffer_upper_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 + + +@final +class CjkUncommonPlugin(MessDetectorPlugin): + """ + Detect messy CJK text that probably means nothing. + """ + + __slots__ = ("_character_count", "_uncommon_count") + + def __init__(self) -> None: + self._character_count: int = 0 + self._uncommon_count: int = 0 + + def feed_info(self, character: str, info: CharInfo) -> None: + """Optimized feed using pre-computed character info.""" + self._character_count += 1 + + if character not in COMMON_CJK_CHARACTERS: + self._uncommon_count += 1 + + 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 + + +@final +class ArchaicUpperLowerPlugin(MessDetectorPlugin): + __slots__ = ( + "_buf", + "_character_count_since_last_sep", + "_successive_upper_lower_count", + "_successive_upper_lower_count_final", + "_character_count", + "_last_alpha_seen", + "_last_alpha_seen_upper", + "_last_alpha_seen_lower", + "_current_ascii_only", + ) + + 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._last_alpha_seen_upper: bool = False + self._last_alpha_seen_lower: bool = False + self._current_ascii_only: bool = True + + def feed_info(self, character: str, info: CharInfo) -> None: + """Optimized feed using pre-computed character info.""" + is_concerned: bool = info.alpha and info.case_variable + chunk_sep: bool = not is_concerned + + if chunk_sep and self._character_count_since_last_sep > 0: + if ( + self._character_count_since_last_sep <= 64 + and not info.digit + and not self._current_ascii_only + ): + 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 and not info.is_ascii: + self._current_ascii_only = False + + if self._last_alpha_seen is not None: + if (info.upper and self._last_alpha_seen_lower) or ( + info.lower and self._last_alpha_seen_upper + ): + if self._buf: + 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 + self._last_alpha_seen_upper = info.upper + self._last_alpha_seen_lower = info.lower + + 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._last_alpha_seen_upper = False + self._last_alpha_seen_lower = False + self._buf = False + self._current_ascii_only = True + + @property + def ratio(self) -> float: + if self._character_count == 0: # Defensive: + return 0.0 + + return self._successive_upper_lower_count_final / self._character_count + + +@final +class ArabicIsolatedFormPlugin(MessDetectorPlugin): + __slots__ = ("_character_count", "_isolated_form_count") + + 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 feed_info(self, character: str, info: CharInfo) -> None: + """Optimized feed using pre-computed character info.""" + self._character_count += 1 + + if info.flags & _ARABIC_ISOLATED_FORM: + 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. + """ + + seq_len: int = len(decoded_sequence) + + if seq_len < 511: + step: int = 32 + elif seq_len < 1024: + step = 64 + else: + step = 128 + + # Create each detector as a named local variable (unrolled from the generic loop). + # This eliminates per-character iteration over the detector list and + # per-character eligible() virtual dispatch, while keeping every plugin class + # intact and fully readable. + d_sp: TooManySymbolOrPunctuationPlugin = TooManySymbolOrPunctuationPlugin() + d_ta: TooManyAccentuatedPlugin = TooManyAccentuatedPlugin() + d_up: UnprintablePlugin = UnprintablePlugin() + d_sda: SuspiciousDuplicateAccentPlugin = SuspiciousDuplicateAccentPlugin() + d_sr: SuspiciousRange = SuspiciousRange() + d_sw: SuperWeirdWordPlugin = SuperWeirdWordPlugin() + d_cu: CjkUncommonPlugin = CjkUncommonPlugin() + d_au: ArchaicUpperLowerPlugin = ArchaicUpperLowerPlugin() + d_ai: ArabicIsolatedFormPlugin = ArabicIsolatedFormPlugin() + + # Local references for feed_info methods called in the hot loop. + d_sp_feed = d_sp.feed_info + d_ta_feed = d_ta.feed_info + d_up_feed = d_up.feed_info + d_sda_feed = d_sda.feed_info + d_sr_feed = d_sr.feed_info + d_sw_feed = d_sw.feed_info + d_cu_feed = d_cu.feed_info + d_au_feed = d_au.feed_info + d_ai_feed = d_ai.feed_info + + # Single reusable CharInfo object (avoids per-character allocation). + info: CharInfo = CharInfo() + info_update = info.update + + mean_mess_ratio: float + + for block_start in range(0, seq_len, step): + for character in decoded_sequence[block_start : block_start + step]: + # Pre-compute all character properties once (shared across all plugins). + info_update(character) + + # Detectors with eligible() == always True + d_up_feed(character, info) + d_sw_feed(character, info) + d_au_feed(character, info) + + # Detectors with eligible() == isprintable + if info.printable: + d_sp_feed(character, info) + d_sr_feed(character, info) + + # Detectors with eligible() == isalpha + if info.alpha: + d_ta_feed(character, info) + # SuspiciousDuplicateAccent: isalpha() and is_latin() + if info.latin: + d_sda_feed(character, info) + # CjkUncommon: is_cjk() + if info.is_cjk: + d_cu_feed(character, info) + # ArabicIsolatedForm: is_arabic() + if info.is_arabic: + d_ai_feed(character, info) + + mean_mess_ratio = ( + d_sp.ratio + + d_ta.ratio + + d_up.ratio + + d_sda.ratio + + d_sr.ratio + + d_sw.ratio + + d_cu.ratio + + d_au.ratio + + d_ai.ratio + ) + + if mean_mess_ratio >= maximum_threshold: + break + else: + # Flush last word buffer in SuperWeirdWordPlugin via trailing newline. + info_update("\n") + d_sw_feed("\n", info) + d_au_feed("\n", info) + d_up_feed("\n", info) + + mean_mess_ratio = ( + d_sp.ratio + + d_ta.ratio + + d_up.ratio + + d_sda.ratio + + d_sr.ratio + + d_sw.ratio + + d_cu.ratio + + d_au.ratio + + d_ai.ratio + ) + + if debug: # Defensive: + logger = getLogger("charset_normalizer") + + logger.log( + TRACE, + "Mess-detector extended-analysis start. " + f"intermediary_mean_mess_ratio_calc={step} mean_mess_ratio={mean_mess_ratio} " + f"maximum_threshold={maximum_threshold}", + ) + + if seq_len > 16: + logger.log(TRACE, f"Starting with: {decoded_sequence[:16]}") + logger.log(TRACE, f"Ending with: {decoded_sequence[-16::]}") + + for dt in [d_sp, d_ta, d_up, d_sda, d_sr, d_sw, d_cu, d_au, d_ai]: + logger.log(TRACE, f"{dt.__class__}: {dt.ratio}") + + return round(mean_mess_ratio, 3) diff --git a/.venv/lib/python3.9/site-packages/charset_normalizer/models.py b/.venv/lib/python3.9/site-packages/charset_normalizer/models.py new file mode 100644 index 0000000..30e8a16 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/charset_normalizer/models.py @@ -0,0 +1,359 @@ +from __future__ import annotations + +from encodings.aliases import aliases +from json import dumps +from re import sub +from typing import Any, Iterator, List, Tuple + +from .constant import RE_POSSIBLE_ENCODING_INDICATION, TOO_BIG_SEQUENCE +from .utils import iana_name, is_multi_byte_encoding, unicode_range + + +class CharsetMatch: + def __init__( + self, + payload: bytes | bytearray, + guessed_encoding: str, + mean_mess_ratio: float, + has_sig_or_bom: bool, + languages: CoherenceMatches, + decoded_payload: str | None = None, + preemptive_declaration: str | None = None, + ): + self._payload: bytes | bytearray = payload + + self._encoding: str = guessed_encoding + self._mean_mess_ratio: float = mean_mess_ratio + self._languages: CoherenceMatches = languages + self._has_sig_or_bom: bool = has_sig_or_bom + self._unicode_ranges: list[str] | None = None + + self._leaves: list[CharsetMatch] = [] + self._mean_coherence_ratio: float = 0.0 + + self._output_payload: bytes | None = None + self._output_encoding: str | None = None + + self._string: str | None = decoded_payload + + self._preemptive_declaration: str | None = preemptive_declaration + + def __eq__(self, other: object) -> 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 0.5% difference --> Use Coherence + if chaos_difference < 0.005 and coherence_difference > 0.02: + return self.coherence > other.coherence + elif chaos_difference < 0.005 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 | bytearray: + """ + 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) -> int: + """ + Retrieve a hash fingerprint of the decoded payload, used for deduplication. + """ + return hash(str(self)) + + +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/.venv/lib/python3.9/site-packages/charset_normalizer/py.typed b/.venv/lib/python3.9/site-packages/charset_normalizer/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.9/site-packages/charset_normalizer/utils.py b/.venv/lib/python3.9/site-packages/charset_normalizer/utils.py new file mode 100644 index 0000000..0f529b5 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/charset_normalizer/utils.py @@ -0,0 +1,422 @@ +from __future__ import annotations + +import importlib +import logging +import unicodedata +from bisect import bisect_right +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, + _LATIN, + _CJK, + _HANGUL, + _KATAKANA, + _HIRAGANA, + _THAI, + _ARABIC, + _ARABIC_ISOLATED_FORM, + _ACCENT_KEYWORDS, + _ACCENTUATED, +) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def _character_flags(character: str) -> int: + """Compute all name-based classification flags with a single unicodedata.name() call.""" + try: + desc: str = unicodedata.name(character) + except ValueError: + return 0 + + flags: int = 0 + + if "LATIN" in desc: + flags |= _LATIN + if "CJK" in desc: + flags |= _CJK + if "HANGUL" in desc: + flags |= _HANGUL + if "KATAKANA" in desc: + flags |= _KATAKANA + if "HIRAGANA" in desc: + flags |= _HIRAGANA + if "THAI" in desc: + flags |= _THAI + if "ARABIC" in desc: + flags |= _ARABIC + if "ISOLATED FORM" in desc: + flags |= _ARABIC_ISOLATED_FORM + + for kw in _ACCENT_KEYWORDS: + if kw in desc: + flags |= _ACCENTUATED + break + + return flags + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_accentuated(character: str) -> bool: + return bool(_character_flags(character) & _ACCENTUATED) + + +@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)) + + +# Pre-built sorted lookup table for O(log n) binary search in unicode_range(). +# Each entry is (range_start, range_end_exclusive, range_name). +_UNICODE_RANGES_SORTED: list[tuple[int, int, str]] = sorted( + (ord_range.start, ord_range.stop, name) + for name, ord_range in UNICODE_RANGES_COMBINED.items() +) +_UNICODE_RANGE_STARTS: list[int] = [e[0] for e in _UNICODE_RANGES_SORTED] + + +@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) + + # Binary search: find the rightmost range whose start <= character_ord + idx = bisect_right(_UNICODE_RANGE_STARTS, character_ord) - 1 + if idx >= 0: + start, stop, name = _UNICODE_RANGES_SORTED[idx] + if character_ord < stop: + return name + + return None + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_latin(character: str) -> bool: + return bool(_character_flags(character) & _LATIN) + + +@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: + return bool(_character_flags(character) & _CJK) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_hiragana(character: str) -> bool: + return bool(_character_flags(character) & _HIRAGANA) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_katakana(character: str) -> bool: + return bool(_character_flags(character) & _KATAKANA) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_hangul(character: str) -> bool: + return bool(_character_flags(character) & _HANGUL) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_thai(character: str) -> bool: + return bool(_character_flags(character) & _THAI) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_arabic(character: str) -> bool: + return bool(_character_flags(character) & _ARABIC) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_arabic_isolated_form(character: str) -> bool: + return bool(_character_flags(character) & _ARABIC_ISOLATED_FORM) + + +@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 | bytearray, search_zone: int = 8192 +) -> str | None: + """ + Extract using ASCII-only decoder any specified encoding in the first n-bytes. + """ + if not isinstance(sequence, (bytes, bytearray)): + 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 | bytearray) -> 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(256): + 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 / 256 + + +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 | bytearray, + 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/.venv/lib/python3.9/site-packages/charset_normalizer/version.py b/.venv/lib/python3.9/site-packages/charset_normalizer/version.py new file mode 100644 index 0000000..a80346f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/charset_normalizer/version.py @@ -0,0 +1,8 @@ +""" +Expose version +""" + +from __future__ import annotations + +__version__ = "3.4.6" +VERSION = __version__.split(".") diff --git a/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/LICENSE.txt b/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/LICENSE.txt new file mode 100644 index 0000000..d12a849 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright 2014 Pallets + +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/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/METADATA b/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/METADATA new file mode 100644 index 0000000..366d1a7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/METADATA @@ -0,0 +1,74 @@ +Metadata-Version: 2.3 +Name: click +Version: 8.1.8 +Summary: Composable command line interface toolkit +Maintainer-email: Pallets +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Typing :: Typed +Requires-Dist: colorama; platform_system == 'Windows' +Requires-Dist: importlib-metadata; python_version < '3.8' +Project-URL: Changes, https://click.palletsprojects.com/changes/ +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://click.palletsprojects.com/ +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Source, https://github.com/pallets/click/ + +# $ click_ + +Click is a Python package for creating beautiful command line interfaces +in a composable way with as little code as necessary. It's the "Command +Line Interface Creation Kit". It's highly configurable but comes with +sensible defaults out of the box. + +It aims to make the process of writing command line tools quick and fun +while also preventing any frustration caused by the inability to +implement an intended CLI API. + +Click in three points: + +- Arbitrary nesting of commands +- Automatic help page generation +- Supports lazy loading of subcommands at runtime + + +## A Simple Example + +```python +import click + +@click.command() +@click.option("--count", default=1, help="Number of greetings.") +@click.option("--name", prompt="Your name", help="The person to greet.") +def hello(count, name): + """Simple program that greets NAME for a total of COUNT times.""" + for _ in range(count): + click.echo(f"Hello, {name}!") + +if __name__ == '__main__': + hello() +``` + +``` +$ python hello.py --count=3 +Your name: Click +Hello, Click! +Hello, Click! +Hello, Click! +``` + + +## Donate + +The Pallets organization develops and supports Click and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, [please +donate today][]. + +[please donate today]: https://palletsprojects.com/donate + diff --git a/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/RECORD b/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/RECORD new file mode 100644 index 0000000..f1f567b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/RECORD @@ -0,0 +1,38 @@ +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/click/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/click/_compat.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/click/_termui_impl.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/click/_textwrap.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/click/_winconsole.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/click/core.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/click/decorators.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/click/exceptions.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/click/formatting.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/click/globals.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/click/parser.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/click/shell_completion.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/click/termui.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/click/testing.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/click/types.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/click/utils.cpython-39.pyc,, +click-8.1.8.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +click-8.1.8.dist-info/LICENSE.txt,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 +click-8.1.8.dist-info/METADATA,sha256=WJtQ6uGS2ybLfvUE4vC0XIhIBr4yFGwjrMBR2fiCQ-Q,2263 +click-8.1.8.dist-info/RECORD,, +click-8.1.8.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82 +click/__init__.py,sha256=j1DJeCbga4ribkv5uyvIAzI0oFN13fW9mevDKShFelo,3188 +click/_compat.py,sha256=IGKh_J5QdfKELitnRfTGHneejWxoCw_NX9tfMbdcg3w,18730 +click/_termui_impl.py,sha256=a5z7I9gOFeMmu7Gb6_RPyQ8GPuVP1EeblixcWSPSQPk,24783 +click/_textwrap.py,sha256=10fQ64OcBUMuK7mFvh8363_uoOxPlRItZBmKzRJDgoY,1353 +click/_winconsole.py,sha256=5ju3jQkcZD0W27WEMGqmEP4y_crUVzPCqsX_FYb7BO0,7860 +click/core.py,sha256=Q1nEVdctZwvIPOlt4vfHko0TYnHCeE40UEEul8Wpyvs,114748 +click/decorators.py,sha256=7t6F-QWowtLh6F_6l-4YV4Y4yNTcqFQEu9i37zIz68s,18925 +click/exceptions.py,sha256=V7zDT6emqJ8iNl0kF1P5kpFmLMWQ1T1L7aNNKM4YR0w,9600 +click/formatting.py,sha256=Frf0-5W33-loyY_i9qrwXR8-STnW3m5gvyxLVUdyxyk,9706 +click/globals.py,sha256=cuJ6Bbo073lgEEmhjr394PeM-QFmXM-Ci-wmfsd7H5g,1954 +click/parser.py,sha256=h4sndcpF5OHrZQN8vD8IWb5OByvW7ABbhRToxovrqS8,19067 +click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +click/shell_completion.py,sha256=TR0dXEGcvWb9Eo3aaQEXGhnvNS3FF4H4QcuLnvAvYo4,18636 +click/termui.py,sha256=dLxiS70UOvIYBda_nEEZaPAFOVDVmRs1sEPMuLDowQo,28310 +click/testing.py,sha256=3RA8anCf7TZ8-5RAF5it2Te-aWXBAL5VLasQnMiC2ZQ,16282 +click/types.py,sha256=BD5Qqq4h-8kawBmOIzJlmq4xzThAf4wCvaOLZSBDNx0,36422 +click/utils.py,sha256=ce-IrO9ilII76LGkU354pOdHbepM8UftfNH7SfMU_28,20330 diff --git a/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/WHEEL new file mode 100644 index 0000000..e3c6fee --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.10.1 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.9/site-packages/click/__init__.py b/.venv/lib/python3.9/site-packages/click/__init__.py new file mode 100644 index 0000000..2610d0e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/__init__.py @@ -0,0 +1,75 @@ +""" +Click is a simple Python module inspired by the stdlib optparse to make +writing command line scripts fun. Unlike other modules, it's based +around a simple API that does not come with too much magic and is +composable. +""" + +from .core import Argument as Argument +from .core import BaseCommand as BaseCommand +from .core import Command as Command +from .core import CommandCollection as CommandCollection +from .core import Context as Context +from .core import Group as Group +from .core import MultiCommand as MultiCommand +from .core import Option as Option +from .core import Parameter as Parameter +from .decorators import argument as argument +from .decorators import command as command +from .decorators import confirmation_option as confirmation_option +from .decorators import group as group +from .decorators import help_option as help_option +from .decorators import HelpOption as HelpOption +from .decorators import make_pass_decorator as make_pass_decorator +from .decorators import option as option +from .decorators import pass_context as pass_context +from .decorators import pass_obj as pass_obj +from .decorators import password_option as password_option +from .decorators import version_option as version_option +from .exceptions import Abort as Abort +from .exceptions import BadArgumentUsage as BadArgumentUsage +from .exceptions import BadOptionUsage as BadOptionUsage +from .exceptions import BadParameter as BadParameter +from .exceptions import ClickException as ClickException +from .exceptions import FileError as FileError +from .exceptions import MissingParameter as MissingParameter +from .exceptions import NoSuchOption as NoSuchOption +from .exceptions import UsageError as UsageError +from .formatting import HelpFormatter as HelpFormatter +from .formatting import wrap_text as wrap_text +from .globals import get_current_context as get_current_context +from .parser import OptionParser as OptionParser +from .termui import clear as clear +from .termui import confirm as confirm +from .termui import echo_via_pager as echo_via_pager +from .termui import edit as edit +from .termui import getchar as getchar +from .termui import launch as launch +from .termui import pause as pause +from .termui import progressbar as progressbar +from .termui import prompt as prompt +from .termui import secho as secho +from .termui import style as style +from .termui import unstyle as unstyle +from .types import BOOL as BOOL +from .types import Choice as Choice +from .types import DateTime as DateTime +from .types import File as File +from .types import FLOAT as FLOAT +from .types import FloatRange as FloatRange +from .types import INT as INT +from .types import IntRange as IntRange +from .types import ParamType as ParamType +from .types import Path as Path +from .types import STRING as STRING +from .types import Tuple as Tuple +from .types import UNPROCESSED as UNPROCESSED +from .types import UUID as UUID +from .utils import echo as echo +from .utils import format_filename as format_filename +from .utils import get_app_dir as get_app_dir +from .utils import get_binary_stream as get_binary_stream +from .utils import get_text_stream as get_text_stream +from .utils import open_file as open_file + +__version__ = "8.1.8" diff --git a/.venv/lib/python3.9/site-packages/click/_compat.py b/.venv/lib/python3.9/site-packages/click/_compat.py new file mode 100644 index 0000000..9153d15 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/_compat.py @@ -0,0 +1,623 @@ +import codecs +import io +import os +import re +import sys +import typing as t +from weakref import WeakKeyDictionary + +CYGWIN = sys.platform.startswith("cygwin") +WIN = sys.platform.startswith("win") +auto_wrap_for_ansi: t.Optional[t.Callable[[t.TextIO], t.TextIO]] = None +_ansi_re = re.compile(r"\033\[[;?0-9]*[a-zA-Z]") + + +def _make_text_stream( + stream: t.BinaryIO, + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if encoding is None: + encoding = get_best_encoding(stream) + if errors is None: + errors = "replace" + return _NonClosingTextIOWrapper( + stream, + encoding, + errors, + line_buffering=True, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def is_ascii_encoding(encoding: str) -> bool: + """Checks if a given encoding is ascii.""" + try: + return codecs.lookup(encoding).name == "ascii" + except LookupError: + return False + + +def get_best_encoding(stream: t.IO[t.Any]) -> str: + """Returns the default stream encoding if not found.""" + rv = getattr(stream, "encoding", None) or sys.getdefaultencoding() + if is_ascii_encoding(rv): + return "utf-8" + return rv + + +class _NonClosingTextIOWrapper(io.TextIOWrapper): + def __init__( + self, + stream: t.BinaryIO, + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, + force_writable: bool = False, + **extra: t.Any, + ) -> None: + self._stream = stream = t.cast( + t.BinaryIO, _FixupStream(stream, force_readable, force_writable) + ) + super().__init__(stream, encoding, errors, **extra) + + def __del__(self) -> None: + try: + self.detach() + except Exception: + pass + + def isatty(self) -> bool: + # https://bitbucket.org/pypy/pypy/issue/1803 + return self._stream.isatty() + + +class _FixupStream: + """The new io interface needs more from streams than streams + traditionally implement. As such, this fix-up code is necessary in + some circumstances. + + The forcing of readable and writable flags are there because some tools + put badly patched objects on sys (one such offender are certain version + of jupyter notebook). + """ + + def __init__( + self, + stream: t.BinaryIO, + force_readable: bool = False, + force_writable: bool = False, + ): + self._stream = stream + self._force_readable = force_readable + self._force_writable = force_writable + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._stream, name) + + def read1(self, size: int) -> bytes: + f = getattr(self._stream, "read1", None) + + if f is not None: + return t.cast(bytes, f(size)) + + return self._stream.read(size) + + def readable(self) -> bool: + if self._force_readable: + return True + x = getattr(self._stream, "readable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.read(0) + except Exception: + return False + return True + + def writable(self) -> bool: + if self._force_writable: + return True + x = getattr(self._stream, "writable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.write("") # type: ignore + except Exception: + try: + self._stream.write(b"") + except Exception: + return False + return True + + def seekable(self) -> bool: + x = getattr(self._stream, "seekable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.seek(self._stream.tell()) + except Exception: + return False + return True + + +def _is_binary_reader(stream: t.IO[t.Any], default: bool = False) -> bool: + try: + return isinstance(stream.read(0), bytes) + except Exception: + return default + # This happens in some cases where the stream was already + # closed. In this case, we assume the default. + + +def _is_binary_writer(stream: t.IO[t.Any], default: bool = False) -> bool: + try: + stream.write(b"") + except Exception: + try: + stream.write("") + return False + except Exception: + pass + return default + return True + + +def _find_binary_reader(stream: t.IO[t.Any]) -> t.Optional[t.BinaryIO]: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_reader(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_reader(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _find_binary_writer(stream: t.IO[t.Any]) -> t.Optional[t.BinaryIO]: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_writer(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_writer(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _stream_is_misconfigured(stream: t.TextIO) -> bool: + """A stream is misconfigured if its encoding is ASCII.""" + # If the stream does not have an encoding set, we assume it's set + # to ASCII. This appears to happen in certain unittest + # environments. It's not quite clear what the correct behavior is + # but this at least will force Click to recover somehow. + return is_ascii_encoding(getattr(stream, "encoding", None) or "ascii") + + +def _is_compat_stream_attr(stream: t.TextIO, attr: str, value: t.Optional[str]) -> bool: + """A stream attribute is compatible if it is equal to the + desired value or the desired value is unset and the attribute + has a value. + """ + stream_value = getattr(stream, attr, None) + return stream_value == value or (value is None and stream_value is not None) + + +def _is_compatible_text_stream( + stream: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] +) -> bool: + """Check if a stream's encoding and errors attributes are + compatible with the desired values. + """ + return _is_compat_stream_attr( + stream, "encoding", encoding + ) and _is_compat_stream_attr(stream, "errors", errors) + + +def _force_correct_text_stream( + text_stream: t.IO[t.Any], + encoding: t.Optional[str], + errors: t.Optional[str], + is_binary: t.Callable[[t.IO[t.Any], bool], bool], + find_binary: t.Callable[[t.IO[t.Any]], t.Optional[t.BinaryIO]], + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if is_binary(text_stream, False): + binary_reader = t.cast(t.BinaryIO, text_stream) + else: + text_stream = t.cast(t.TextIO, text_stream) + # If the stream looks compatible, and won't default to a + # misconfigured ascii encoding, return it as-is. + if _is_compatible_text_stream(text_stream, encoding, errors) and not ( + encoding is None and _stream_is_misconfigured(text_stream) + ): + return text_stream + + # Otherwise, get the underlying binary reader. + possible_binary_reader = find_binary(text_stream) + + # If that's not possible, silently use the original reader + # and get mojibake instead of exceptions. + if possible_binary_reader is None: + return text_stream + + binary_reader = possible_binary_reader + + # Default errors to replace instead of strict in order to get + # something that works. + if errors is None: + errors = "replace" + + # Wrap the binary stream in a text stream with the correct + # encoding parameters. + return _make_text_stream( + binary_reader, + encoding, + errors, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def _force_correct_text_reader( + text_reader: t.IO[t.Any], + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_reader, + encoding, + errors, + _is_binary_reader, + _find_binary_reader, + force_readable=force_readable, + ) + + +def _force_correct_text_writer( + text_writer: t.IO[t.Any], + encoding: t.Optional[str], + errors: t.Optional[str], + force_writable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_writer, + encoding, + errors, + _is_binary_writer, + _find_binary_writer, + force_writable=force_writable, + ) + + +def get_binary_stdin() -> t.BinaryIO: + reader = _find_binary_reader(sys.stdin) + if reader is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdin.") + return reader + + +def get_binary_stdout() -> t.BinaryIO: + writer = _find_binary_writer(sys.stdout) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdout.") + return writer + + +def get_binary_stderr() -> t.BinaryIO: + writer = _find_binary_writer(sys.stderr) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stderr.") + return writer + + +def get_text_stdin( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdin, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_reader(sys.stdin, encoding, errors, force_readable=True) + + +def get_text_stdout( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdout, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stdout, encoding, errors, force_writable=True) + + +def get_text_stderr( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stderr, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stderr, encoding, errors, force_writable=True) + + +def _wrap_io_open( + file: t.Union[str, "os.PathLike[str]", int], + mode: str, + encoding: t.Optional[str], + errors: t.Optional[str], +) -> t.IO[t.Any]: + """Handles not passing ``encoding`` and ``errors`` in binary mode.""" + if "b" in mode: + return open(file, mode) + + return open(file, mode, encoding=encoding, errors=errors) + + +def open_stream( + filename: "t.Union[str, os.PathLike[str]]", + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + atomic: bool = False, +) -> t.Tuple[t.IO[t.Any], bool]: + binary = "b" in mode + filename = os.fspath(filename) + + # Standard streams first. These are simple because they ignore the + # atomic flag. Use fsdecode to handle Path("-"). + if os.fsdecode(filename) == "-": + if any(m in mode for m in ["w", "a", "x"]): + if binary: + return get_binary_stdout(), False + return get_text_stdout(encoding=encoding, errors=errors), False + if binary: + return get_binary_stdin(), False + return get_text_stdin(encoding=encoding, errors=errors), False + + # Non-atomic writes directly go out through the regular open functions. + if not atomic: + return _wrap_io_open(filename, mode, encoding, errors), True + + # Some usability stuff for atomic writes + if "a" in mode: + raise ValueError( + "Appending to an existing file is not supported, because that" + " would involve an expensive `copy`-operation to a temporary" + " file. Open the file in normal `w`-mode and copy explicitly" + " if that's what you're after." + ) + if "x" in mode: + raise ValueError("Use the `overwrite`-parameter instead.") + if "w" not in mode: + raise ValueError("Atomic writes only make sense with `w`-mode.") + + # Atomic writes are more complicated. They work by opening a file + # as a proxy in the same folder and then using the fdopen + # functionality to wrap it in a Python file. Then we wrap it in an + # atomic file that moves the file over on close. + import errno + import random + + try: + perm: t.Optional[int] = os.stat(filename).st_mode + except OSError: + perm = None + + flags = os.O_RDWR | os.O_CREAT | os.O_EXCL + + if binary: + flags |= getattr(os, "O_BINARY", 0) + + while True: + tmp_filename = os.path.join( + os.path.dirname(filename), + f".__atomic-write{random.randrange(1 << 32):08x}", + ) + try: + fd = os.open(tmp_filename, flags, 0o666 if perm is None else perm) + break + except OSError as e: + if e.errno == errno.EEXIST or ( + os.name == "nt" + and e.errno == errno.EACCES + and os.path.isdir(e.filename) + and os.access(e.filename, os.W_OK) + ): + continue + raise + + if perm is not None: + os.chmod(tmp_filename, perm) # in case perm includes bits in umask + + f = _wrap_io_open(fd, mode, encoding, errors) + af = _AtomicFile(f, tmp_filename, os.path.realpath(filename)) + return t.cast(t.IO[t.Any], af), True + + +class _AtomicFile: + def __init__(self, f: t.IO[t.Any], tmp_filename: str, real_filename: str) -> None: + self._f = f + self._tmp_filename = tmp_filename + self._real_filename = real_filename + self.closed = False + + @property + def name(self) -> str: + return self._real_filename + + def close(self, delete: bool = False) -> None: + if self.closed: + return + self._f.close() + os.replace(self._tmp_filename, self._real_filename) + self.closed = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._f, name) + + def __enter__(self) -> "_AtomicFile": + return self + + def __exit__(self, exc_type: t.Optional[t.Type[BaseException]], *_: t.Any) -> None: + self.close(delete=exc_type is not None) + + def __repr__(self) -> str: + return repr(self._f) + + +def strip_ansi(value: str) -> str: + return _ansi_re.sub("", value) + + +def _is_jupyter_kernel_output(stream: t.IO[t.Any]) -> bool: + while isinstance(stream, (_FixupStream, _NonClosingTextIOWrapper)): + stream = stream._stream + + return stream.__class__.__module__.startswith("ipykernel.") + + +def should_strip_ansi( + stream: t.Optional[t.IO[t.Any]] = None, color: t.Optional[bool] = None +) -> bool: + if color is None: + if stream is None: + stream = sys.stdin + return not isatty(stream) and not _is_jupyter_kernel_output(stream) + return not color + + +# On Windows, wrap the output streams with colorama to support ANSI +# color codes. +# NOTE: double check is needed so mypy does not analyze this on Linux +if sys.platform.startswith("win") and WIN: + from ._winconsole import _get_windows_console_stream + + def _get_argv_encoding() -> str: + import locale + + return locale.getpreferredencoding() + + _ansi_stream_wrappers: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def auto_wrap_for_ansi( + stream: t.TextIO, color: t.Optional[bool] = None + ) -> t.TextIO: + """Support ANSI color and style codes on Windows by wrapping a + stream with colorama. + """ + try: + cached = _ansi_stream_wrappers.get(stream) + except Exception: + cached = None + + if cached is not None: + return cached + + import colorama + + strip = should_strip_ansi(stream, color) + ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip) + rv = t.cast(t.TextIO, ansi_wrapper.stream) + _write = rv.write + + def _safe_write(s): + try: + return _write(s) + except BaseException: + ansi_wrapper.reset_all() + raise + + rv.write = _safe_write + + try: + _ansi_stream_wrappers[stream] = rv + except Exception: + pass + + return rv + +else: + + def _get_argv_encoding() -> str: + return getattr(sys.stdin, "encoding", None) or sys.getfilesystemencoding() + + def _get_windows_console_stream( + f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] + ) -> t.Optional[t.TextIO]: + return None + + +def term_len(x: str) -> int: + return len(strip_ansi(x)) + + +def isatty(stream: t.IO[t.Any]) -> bool: + try: + return stream.isatty() + except Exception: + return False + + +def _make_cached_stream_func( + src_func: t.Callable[[], t.Optional[t.TextIO]], + wrapper_func: t.Callable[[], t.TextIO], +) -> t.Callable[[], t.Optional[t.TextIO]]: + cache: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def func() -> t.Optional[t.TextIO]: + stream = src_func() + + if stream is None: + return None + + try: + rv = cache.get(stream) + except Exception: + rv = None + if rv is not None: + return rv + rv = wrapper_func() + try: + cache[stream] = rv + except Exception: + pass + return rv + + return func + + +_default_text_stdin = _make_cached_stream_func(lambda: sys.stdin, get_text_stdin) +_default_text_stdout = _make_cached_stream_func(lambda: sys.stdout, get_text_stdout) +_default_text_stderr = _make_cached_stream_func(lambda: sys.stderr, get_text_stderr) + + +binary_streams: t.Mapping[str, t.Callable[[], t.BinaryIO]] = { + "stdin": get_binary_stdin, + "stdout": get_binary_stdout, + "stderr": get_binary_stderr, +} + +text_streams: t.Mapping[ + str, t.Callable[[t.Optional[str], t.Optional[str]], t.TextIO] +] = { + "stdin": get_text_stdin, + "stdout": get_text_stdout, + "stderr": get_text_stderr, +} diff --git a/.venv/lib/python3.9/site-packages/click/_termui_impl.py b/.venv/lib/python3.9/site-packages/click/_termui_impl.py new file mode 100644 index 0000000..ad9f8f6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/_termui_impl.py @@ -0,0 +1,788 @@ +""" +This module contains implementations for the termui module. To keep the +import time of Click down, some infrequently used functionality is +placed in this module and only imported as needed. +""" + +import contextlib +import math +import os +import sys +import time +import typing as t +from gettext import gettext as _ +from io import StringIO +from shutil import which +from types import TracebackType + +from ._compat import _default_text_stdout +from ._compat import CYGWIN +from ._compat import get_best_encoding +from ._compat import isatty +from ._compat import open_stream +from ._compat import strip_ansi +from ._compat import term_len +from ._compat import WIN +from .exceptions import ClickException +from .utils import echo + +V = t.TypeVar("V") + +if os.name == "nt": + BEFORE_BAR = "\r" + AFTER_BAR = "\n" +else: + BEFORE_BAR = "\r\033[?25l" + AFTER_BAR = "\033[?25h\n" + + +class ProgressBar(t.Generic[V]): + def __init__( + self, + iterable: t.Optional[t.Iterable[V]], + length: t.Optional[int] = None, + fill_char: str = "#", + empty_char: str = " ", + bar_template: str = "%(bar)s", + info_sep: str = " ", + show_eta: bool = True, + show_percent: t.Optional[bool] = None, + show_pos: bool = False, + item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, + label: t.Optional[str] = None, + file: t.Optional[t.TextIO] = None, + color: t.Optional[bool] = None, + update_min_steps: int = 1, + width: int = 30, + ) -> None: + self.fill_char = fill_char + self.empty_char = empty_char + self.bar_template = bar_template + self.info_sep = info_sep + self.show_eta = show_eta + self.show_percent = show_percent + self.show_pos = show_pos + self.item_show_func = item_show_func + self.label: str = label or "" + + if file is None: + file = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if file is None: + file = StringIO() + + self.file = file + self.color = color + self.update_min_steps = update_min_steps + self._completed_intervals = 0 + self.width: int = width + self.autowidth: bool = width == 0 + + if length is None: + from operator import length_hint + + length = length_hint(iterable, -1) + + if length == -1: + length = None + if iterable is None: + if length is None: + raise TypeError("iterable or length is required") + iterable = t.cast(t.Iterable[V], range(length)) + self.iter: t.Iterable[V] = iter(iterable) + self.length = length + self.pos = 0 + self.avg: t.List[float] = [] + self.last_eta: float + self.start: float + self.start = self.last_eta = time.time() + self.eta_known: bool = False + self.finished: bool = False + self.max_width: t.Optional[int] = None + self.entered: bool = False + self.current_item: t.Optional[V] = None + self.is_hidden: bool = not isatty(self.file) + self._last_line: t.Optional[str] = None + + def __enter__(self) -> "ProgressBar[V]": + self.entered = True + self.render_progress() + return self + + def __exit__( + self, + exc_type: t.Optional[t.Type[BaseException]], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + self.render_finish() + + def __iter__(self) -> t.Iterator[V]: + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + self.render_progress() + return self.generator() + + def __next__(self) -> V: + # Iteration is defined in terms of a generator function, + # returned by iter(self); use that to define next(). This works + # because `self.iter` is an iterable consumed by that generator, + # so it is re-entry safe. Calling `next(self.generator())` + # twice works and does "what you want". + return next(iter(self)) + + def render_finish(self) -> None: + if self.is_hidden: + return + self.file.write(AFTER_BAR) + self.file.flush() + + @property + def pct(self) -> float: + if self.finished: + return 1.0 + return min(self.pos / (float(self.length or 1) or 1), 1.0) + + @property + def time_per_iteration(self) -> float: + if not self.avg: + return 0.0 + return sum(self.avg) / float(len(self.avg)) + + @property + def eta(self) -> float: + if self.length is not None and not self.finished: + return self.time_per_iteration * (self.length - self.pos) + return 0.0 + + def format_eta(self) -> str: + if self.eta_known: + t = int(self.eta) + seconds = t % 60 + t //= 60 + minutes = t % 60 + t //= 60 + hours = t % 24 + t //= 24 + if t > 0: + return f"{t}d {hours:02}:{minutes:02}:{seconds:02}" + else: + return f"{hours:02}:{minutes:02}:{seconds:02}" + return "" + + def format_pos(self) -> str: + pos = str(self.pos) + if self.length is not None: + pos += f"/{self.length}" + return pos + + def format_pct(self) -> str: + return f"{int(self.pct * 100): 4}%"[1:] + + def format_bar(self) -> str: + if self.length is not None: + bar_length = int(self.pct * self.width) + bar = self.fill_char * bar_length + bar += self.empty_char * (self.width - bar_length) + elif self.finished: + bar = self.fill_char * self.width + else: + chars = list(self.empty_char * (self.width or 1)) + if self.time_per_iteration != 0: + chars[ + int( + (math.cos(self.pos * self.time_per_iteration) / 2.0 + 0.5) + * self.width + ) + ] = self.fill_char + bar = "".join(chars) + return bar + + def format_progress_line(self) -> str: + show_percent = self.show_percent + + info_bits = [] + if self.length is not None and show_percent is None: + show_percent = not self.show_pos + + if self.show_pos: + info_bits.append(self.format_pos()) + if show_percent: + info_bits.append(self.format_pct()) + if self.show_eta and self.eta_known and not self.finished: + info_bits.append(self.format_eta()) + if self.item_show_func is not None: + item_info = self.item_show_func(self.current_item) + if item_info is not None: + info_bits.append(item_info) + + return ( + self.bar_template + % { + "label": self.label, + "bar": self.format_bar(), + "info": self.info_sep.join(info_bits), + } + ).rstrip() + + def render_progress(self) -> None: + import shutil + + if self.is_hidden: + # Only output the label as it changes if the output is not a + # TTY. Use file=stderr if you expect to be piping stdout. + if self._last_line != self.label: + self._last_line = self.label + echo(self.label, file=self.file, color=self.color) + + return + + buf = [] + # Update width in case the terminal has been resized + if self.autowidth: + old_width = self.width + self.width = 0 + clutter_length = term_len(self.format_progress_line()) + new_width = max(0, shutil.get_terminal_size().columns - clutter_length) + if new_width < old_width: + buf.append(BEFORE_BAR) + buf.append(" " * self.max_width) # type: ignore + self.max_width = new_width + self.width = new_width + + clear_width = self.width + if self.max_width is not None: + clear_width = self.max_width + + buf.append(BEFORE_BAR) + line = self.format_progress_line() + line_len = term_len(line) + if self.max_width is None or self.max_width < line_len: + self.max_width = line_len + + buf.append(line) + buf.append(" " * (clear_width - line_len)) + line = "".join(buf) + # Render the line only if it changed. + + if line != self._last_line: + self._last_line = line + echo(line, file=self.file, color=self.color, nl=False) + self.file.flush() + + def make_step(self, n_steps: int) -> None: + self.pos += n_steps + if self.length is not None and self.pos >= self.length: + self.finished = True + + if (time.time() - self.last_eta) < 1.0: + return + + self.last_eta = time.time() + + # self.avg is a rolling list of length <= 7 of steps where steps are + # defined as time elapsed divided by the total progress through + # self.length. + if self.pos: + step = (time.time() - self.start) / self.pos + else: + step = time.time() - self.start + + self.avg = self.avg[-6:] + [step] + + self.eta_known = self.length is not None + + def update(self, n_steps: int, current_item: t.Optional[V] = None) -> None: + """Update the progress bar by advancing a specified number of + steps, and optionally set the ``current_item`` for this new + position. + + :param n_steps: Number of steps to advance. + :param current_item: Optional item to set as ``current_item`` + for the updated position. + + .. versionchanged:: 8.0 + Added the ``current_item`` optional parameter. + + .. versionchanged:: 8.0 + Only render when the number of steps meets the + ``update_min_steps`` threshold. + """ + if current_item is not None: + self.current_item = current_item + + self._completed_intervals += n_steps + + if self._completed_intervals >= self.update_min_steps: + self.make_step(self._completed_intervals) + self.render_progress() + self._completed_intervals = 0 + + def finish(self) -> None: + self.eta_known = False + self.current_item = None + self.finished = True + + def generator(self) -> t.Iterator[V]: + """Return a generator which yields the items added to the bar + during construction, and updates the progress bar *after* the + yielded block returns. + """ + # WARNING: the iterator interface for `ProgressBar` relies on + # this and only works because this is a simple generator which + # doesn't create or manage additional state. If this function + # changes, the impact should be evaluated both against + # `iter(bar)` and `next(bar)`. `next()` in particular may call + # `self.generator()` repeatedly, and this must remain safe in + # order for that interface to work. + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + + if self.is_hidden: + yield from self.iter + else: + for rv in self.iter: + self.current_item = rv + + # This allows show_item_func to be updated before the + # item is processed. Only trigger at the beginning of + # the update interval. + if self._completed_intervals == 0: + self.render_progress() + + yield rv + self.update(1) + + self.finish() + self.render_progress() + + +def pager(generator: t.Iterable[str], color: t.Optional[bool] = None) -> None: + """Decide what method to use for paging through text.""" + stdout = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if stdout is None: + stdout = StringIO() + + if not isatty(sys.stdin) or not isatty(stdout): + return _nullpager(stdout, generator, color) + pager_cmd = (os.environ.get("PAGER", None) or "").strip() + if pager_cmd: + if WIN: + if _tempfilepager(generator, pager_cmd, color): + return + elif _pipepager(generator, pager_cmd, color): + return + if os.environ.get("TERM") in ("dumb", "emacs"): + return _nullpager(stdout, generator, color) + if (WIN or sys.platform.startswith("os2")) and _tempfilepager( + generator, "more", color + ): + return + if _pipepager(generator, "less", color): + return + + import tempfile + + fd, filename = tempfile.mkstemp() + os.close(fd) + try: + if _pipepager(generator, "more", color): + return + return _nullpager(stdout, generator, color) + finally: + os.unlink(filename) + + +def _pipepager(generator: t.Iterable[str], cmd: str, color: t.Optional[bool]) -> bool: + """Page through text by feeding it to another program. Invoking a + pager through this might support colors. + + Returns True if the command was found, False otherwise and thus another + pager should be attempted. + """ + cmd_absolute = which(cmd) + if cmd_absolute is None: + return False + + import subprocess + + env = dict(os.environ) + + # If we're piping to less we might support colors under the + # condition that + cmd_detail = cmd.rsplit("/", 1)[-1].split() + if color is None and cmd_detail[0] == "less": + less_flags = f"{os.environ.get('LESS', '')}{' '.join(cmd_detail[1:])}" + if not less_flags: + env["LESS"] = "-R" + color = True + elif "r" in less_flags or "R" in less_flags: + color = True + + c = subprocess.Popen( + [cmd_absolute], + shell=True, + stdin=subprocess.PIPE, + env=env, + errors="replace", + text=True, + ) + assert c.stdin is not None + try: + for text in generator: + if not color: + text = strip_ansi(text) + + c.stdin.write(text) + except (OSError, KeyboardInterrupt): + pass + else: + c.stdin.close() + + # Less doesn't respect ^C, but catches it for its own UI purposes (aborting + # search or other commands inside less). + # + # That means when the user hits ^C, the parent process (click) terminates, + # but less is still alive, paging the output and messing up the terminal. + # + # If the user wants to make the pager exit on ^C, they should set + # `LESS='-K'`. It's not our decision to make. + while True: + try: + c.wait() + except KeyboardInterrupt: + pass + else: + break + + return True + + +def _tempfilepager( + generator: t.Iterable[str], + cmd: str, + color: t.Optional[bool], +) -> bool: + """Page through text by invoking a program on a temporary file. + + Returns True if the command was found, False otherwise and thus another + pager should be attempted. + """ + # Which is necessary for Windows, it is also recommended in the Popen docs. + cmd_absolute = which(cmd) + if cmd_absolute is None: + return False + + import subprocess + import tempfile + + fd, filename = tempfile.mkstemp() + # TODO: This never terminates if the passed generator never terminates. + text = "".join(generator) + if not color: + text = strip_ansi(text) + encoding = get_best_encoding(sys.stdout) + with open_stream(filename, "wb")[0] as f: + f.write(text.encode(encoding)) + try: + subprocess.call([cmd_absolute, filename]) + except OSError: + # Command not found + pass + finally: + os.close(fd) + os.unlink(filename) + + return True + + +def _nullpager( + stream: t.TextIO, generator: t.Iterable[str], color: t.Optional[bool] +) -> None: + """Simply print unformatted text. This is the ultimate fallback.""" + for text in generator: + if not color: + text = strip_ansi(text) + stream.write(text) + + +class Editor: + def __init__( + self, + editor: t.Optional[str] = None, + env: t.Optional[t.Mapping[str, str]] = None, + require_save: bool = True, + extension: str = ".txt", + ) -> None: + self.editor = editor + self.env = env + self.require_save = require_save + self.extension = extension + + def get_editor(self) -> str: + if self.editor is not None: + return self.editor + for key in "VISUAL", "EDITOR": + rv = os.environ.get(key) + if rv: + return rv + if WIN: + return "notepad" + for editor in "sensible-editor", "vim", "nano": + if which(editor) is not None: + return editor + return "vi" + + def edit_file(self, filename: str) -> None: + import subprocess + + editor = self.get_editor() + environ: t.Optional[t.Dict[str, str]] = None + + if self.env: + environ = os.environ.copy() + environ.update(self.env) + + try: + c = subprocess.Popen(f'{editor} "{filename}"', env=environ, shell=True) + exit_code = c.wait() + if exit_code != 0: + raise ClickException( + _("{editor}: Editing failed").format(editor=editor) + ) + except OSError as e: + raise ClickException( + _("{editor}: Editing failed: {e}").format(editor=editor, e=e) + ) from e + + def edit(self, text: t.Optional[t.AnyStr]) -> t.Optional[t.AnyStr]: + import tempfile + + if not text: + data = b"" + elif isinstance(text, (bytes, bytearray)): + data = text + else: + if text and not text.endswith("\n"): + text += "\n" + + if WIN: + data = text.replace("\n", "\r\n").encode("utf-8-sig") + else: + data = text.encode("utf-8") + + fd, name = tempfile.mkstemp(prefix="editor-", suffix=self.extension) + f: t.BinaryIO + + try: + with os.fdopen(fd, "wb") as f: + f.write(data) + + # If the filesystem resolution is 1 second, like Mac OS + # 10.12 Extended, or 2 seconds, like FAT32, and the editor + # closes very fast, require_save can fail. Set the modified + # time to be 2 seconds in the past to work around this. + os.utime(name, (os.path.getatime(name), os.path.getmtime(name) - 2)) + # Depending on the resolution, the exact value might not be + # recorded, so get the new recorded value. + timestamp = os.path.getmtime(name) + + self.edit_file(name) + + if self.require_save and os.path.getmtime(name) == timestamp: + return None + + with open(name, "rb") as f: + rv = f.read() + + if isinstance(text, (bytes, bytearray)): + return rv + + return rv.decode("utf-8-sig").replace("\r\n", "\n") # type: ignore + finally: + os.unlink(name) + + +def open_url(url: str, wait: bool = False, locate: bool = False) -> int: + import subprocess + + def _unquote_file(url: str) -> str: + from urllib.parse import unquote + + if url.startswith("file://"): + url = unquote(url[7:]) + + return url + + if sys.platform == "darwin": + args = ["open"] + if wait: + args.append("-W") + if locate: + args.append("-R") + args.append(_unquote_file(url)) + null = open("/dev/null", "w") + try: + return subprocess.Popen(args, stderr=null).wait() + finally: + null.close() + elif WIN: + if locate: + url = _unquote_file(url) + args = ["explorer", f"/select,{url}"] + else: + args = ["start"] + if wait: + args.append("/WAIT") + args.append("") + args.append(url) + try: + return subprocess.call(args) + except OSError: + # Command not found + return 127 + elif CYGWIN: + if locate: + url = _unquote_file(url) + args = ["cygstart", os.path.dirname(url)] + else: + args = ["cygstart"] + if wait: + args.append("-w") + args.append(url) + try: + return subprocess.call(args) + except OSError: + # Command not found + return 127 + + try: + if locate: + url = os.path.dirname(_unquote_file(url)) or "." + else: + url = _unquote_file(url) + c = subprocess.Popen(["xdg-open", url]) + if wait: + return c.wait() + return 0 + except OSError: + if url.startswith(("http://", "https://")) and not locate and not wait: + import webbrowser + + webbrowser.open(url) + return 0 + return 1 + + +def _translate_ch_to_exc(ch: str) -> t.Optional[BaseException]: + if ch == "\x03": + raise KeyboardInterrupt() + + if ch == "\x04" and not WIN: # Unix-like, Ctrl+D + raise EOFError() + + if ch == "\x1a" and WIN: # Windows, Ctrl+Z + raise EOFError() + + return None + + +if WIN: + import msvcrt + + @contextlib.contextmanager + def raw_terminal() -> t.Iterator[int]: + yield -1 + + def getchar(echo: bool) -> str: + # The function `getch` will return a bytes object corresponding to + # the pressed character. Since Windows 10 build 1803, it will also + # return \x00 when called a second time after pressing a regular key. + # + # `getwch` does not share this probably-bugged behavior. Moreover, it + # returns a Unicode object by default, which is what we want. + # + # Either of these functions will return \x00 or \xe0 to indicate + # a special key, and you need to call the same function again to get + # the "rest" of the code. The fun part is that \u00e0 is + # "latin small letter a with grave", so if you type that on a French + # keyboard, you _also_ get a \xe0. + # E.g., consider the Up arrow. This returns \xe0 and then \x48. The + # resulting Unicode string reads as "a with grave" + "capital H". + # This is indistinguishable from when the user actually types + # "a with grave" and then "capital H". + # + # When \xe0 is returned, we assume it's part of a special-key sequence + # and call `getwch` again, but that means that when the user types + # the \u00e0 character, `getchar` doesn't return until a second + # character is typed. + # The alternative is returning immediately, but that would mess up + # cross-platform handling of arrow keys and others that start with + # \xe0. Another option is using `getch`, but then we can't reliably + # read non-ASCII characters, because return values of `getch` are + # limited to the current 8-bit codepage. + # + # Anyway, Click doesn't claim to do this Right(tm), and using `getwch` + # is doing the right thing in more situations than with `getch`. + func: t.Callable[[], str] + + if echo: + func = msvcrt.getwche # type: ignore + else: + func = msvcrt.getwch # type: ignore + + rv = func() + + if rv in ("\x00", "\xe0"): + # \x00 and \xe0 are control characters that indicate special key, + # see above. + rv += func() + + _translate_ch_to_exc(rv) + return rv + +else: + import termios + import tty + + @contextlib.contextmanager + def raw_terminal() -> t.Iterator[int]: + f: t.Optional[t.TextIO] + fd: int + + if not isatty(sys.stdin): + f = open("/dev/tty") + fd = f.fileno() + else: + fd = sys.stdin.fileno() + f = None + + try: + old_settings = termios.tcgetattr(fd) + + try: + tty.setraw(fd) + yield fd + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + sys.stdout.flush() + + if f is not None: + f.close() + except termios.error: + pass + + def getchar(echo: bool) -> str: + with raw_terminal() as fd: + ch = os.read(fd, 32).decode(get_best_encoding(sys.stdin), "replace") + + if echo and isatty(sys.stdout): + sys.stdout.write(ch) + + _translate_ch_to_exc(ch) + return ch diff --git a/.venv/lib/python3.9/site-packages/click/_textwrap.py b/.venv/lib/python3.9/site-packages/click/_textwrap.py new file mode 100644 index 0000000..b47dcbd --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/_textwrap.py @@ -0,0 +1,49 @@ +import textwrap +import typing as t +from contextlib import contextmanager + + +class TextWrapper(textwrap.TextWrapper): + def _handle_long_word( + self, + reversed_chunks: t.List[str], + cur_line: t.List[str], + cur_len: int, + width: int, + ) -> None: + space_left = max(width - cur_len, 1) + + if self.break_long_words: + last = reversed_chunks[-1] + cut = last[:space_left] + res = last[space_left:] + cur_line.append(cut) + reversed_chunks[-1] = res + elif not cur_line: + cur_line.append(reversed_chunks.pop()) + + @contextmanager + def extra_indent(self, indent: str) -> t.Iterator[None]: + old_initial_indent = self.initial_indent + old_subsequent_indent = self.subsequent_indent + self.initial_indent += indent + self.subsequent_indent += indent + + try: + yield + finally: + self.initial_indent = old_initial_indent + self.subsequent_indent = old_subsequent_indent + + def indent_only(self, text: str) -> str: + rv = [] + + for idx, line in enumerate(text.splitlines()): + indent = self.initial_indent + + if idx > 0: + indent = self.subsequent_indent + + rv.append(f"{indent}{line}") + + return "\n".join(rv) diff --git a/.venv/lib/python3.9/site-packages/click/_winconsole.py b/.venv/lib/python3.9/site-packages/click/_winconsole.py new file mode 100644 index 0000000..6b20df3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/_winconsole.py @@ -0,0 +1,279 @@ +# This module is based on the excellent work by Adam Bartoš who +# provided a lot of what went into the implementation here in +# the discussion to issue1602 in the Python bug tracker. +# +# There are some general differences in regards to how this works +# compared to the original patches as we do not need to patch +# the entire interpreter but just work in our little world of +# echo and prompt. +import io +import sys +import time +import typing as t +from ctypes import byref +from ctypes import c_char +from ctypes import c_char_p +from ctypes import c_int +from ctypes import c_ssize_t +from ctypes import c_ulong +from ctypes import c_void_p +from ctypes import POINTER +from ctypes import py_object +from ctypes import Structure +from ctypes.wintypes import DWORD +from ctypes.wintypes import HANDLE +from ctypes.wintypes import LPCWSTR +from ctypes.wintypes import LPWSTR + +from ._compat import _NonClosingTextIOWrapper + +assert sys.platform == "win32" +import msvcrt # noqa: E402 +from ctypes import windll # noqa: E402 +from ctypes import WINFUNCTYPE # noqa: E402 + +c_ssize_p = POINTER(c_ssize_t) + +kernel32 = windll.kernel32 +GetStdHandle = kernel32.GetStdHandle +ReadConsoleW = kernel32.ReadConsoleW +WriteConsoleW = kernel32.WriteConsoleW +GetConsoleMode = kernel32.GetConsoleMode +GetLastError = kernel32.GetLastError +GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32)) +CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( + ("CommandLineToArgvW", windll.shell32) +) +LocalFree = WINFUNCTYPE(c_void_p, c_void_p)(("LocalFree", windll.kernel32)) + +STDIN_HANDLE = GetStdHandle(-10) +STDOUT_HANDLE = GetStdHandle(-11) +STDERR_HANDLE = GetStdHandle(-12) + +PyBUF_SIMPLE = 0 +PyBUF_WRITABLE = 1 + +ERROR_SUCCESS = 0 +ERROR_NOT_ENOUGH_MEMORY = 8 +ERROR_OPERATION_ABORTED = 995 + +STDIN_FILENO = 0 +STDOUT_FILENO = 1 +STDERR_FILENO = 2 + +EOF = b"\x1a" +MAX_BYTES_WRITTEN = 32767 + +try: + from ctypes import pythonapi +except ImportError: + # On PyPy we cannot get buffers so our ability to operate here is + # severely limited. + get_buffer = None +else: + + class Py_buffer(Structure): + _fields_ = [ + ("buf", c_void_p), + ("obj", py_object), + ("len", c_ssize_t), + ("itemsize", c_ssize_t), + ("readonly", c_int), + ("ndim", c_int), + ("format", c_char_p), + ("shape", c_ssize_p), + ("strides", c_ssize_p), + ("suboffsets", c_ssize_p), + ("internal", c_void_p), + ] + + PyObject_GetBuffer = pythonapi.PyObject_GetBuffer + PyBuffer_Release = pythonapi.PyBuffer_Release + + def get_buffer(obj, writable=False): + buf = Py_buffer() + flags = PyBUF_WRITABLE if writable else PyBUF_SIMPLE + PyObject_GetBuffer(py_object(obj), byref(buf), flags) + + try: + buffer_type = c_char * buf.len + return buffer_type.from_address(buf.buf) + finally: + PyBuffer_Release(byref(buf)) + + +class _WindowsConsoleRawIOBase(io.RawIOBase): + def __init__(self, handle): + self.handle = handle + + def isatty(self): + super().isatty() + return True + + +class _WindowsConsoleReader(_WindowsConsoleRawIOBase): + def readable(self): + return True + + def readinto(self, b): + bytes_to_be_read = len(b) + if not bytes_to_be_read: + return 0 + elif bytes_to_be_read % 2: + raise ValueError( + "cannot read odd number of bytes from UTF-16-LE encoded console" + ) + + buffer = get_buffer(b, writable=True) + code_units_to_be_read = bytes_to_be_read // 2 + code_units_read = c_ulong() + + rv = ReadConsoleW( + HANDLE(self.handle), + buffer, + code_units_to_be_read, + byref(code_units_read), + None, + ) + if GetLastError() == ERROR_OPERATION_ABORTED: + # wait for KeyboardInterrupt + time.sleep(0.1) + if not rv: + raise OSError(f"Windows error: {GetLastError()}") + + if buffer[0] == EOF: + return 0 + return 2 * code_units_read.value + + +class _WindowsConsoleWriter(_WindowsConsoleRawIOBase): + def writable(self): + return True + + @staticmethod + def _get_error_message(errno): + if errno == ERROR_SUCCESS: + return "ERROR_SUCCESS" + elif errno == ERROR_NOT_ENOUGH_MEMORY: + return "ERROR_NOT_ENOUGH_MEMORY" + return f"Windows error {errno}" + + def write(self, b): + bytes_to_be_written = len(b) + buf = get_buffer(b) + code_units_to_be_written = min(bytes_to_be_written, MAX_BYTES_WRITTEN) // 2 + code_units_written = c_ulong() + + WriteConsoleW( + HANDLE(self.handle), + buf, + code_units_to_be_written, + byref(code_units_written), + None, + ) + bytes_written = 2 * code_units_written.value + + if bytes_written == 0 and bytes_to_be_written > 0: + raise OSError(self._get_error_message(GetLastError())) + return bytes_written + + +class ConsoleStream: + def __init__(self, text_stream: t.TextIO, byte_stream: t.BinaryIO) -> None: + self._text_stream = text_stream + self.buffer = byte_stream + + @property + def name(self) -> str: + return self.buffer.name + + def write(self, x: t.AnyStr) -> int: + if isinstance(x, str): + return self._text_stream.write(x) + try: + self.flush() + except Exception: + pass + return self.buffer.write(x) + + def writelines(self, lines: t.Iterable[t.AnyStr]) -> None: + for line in lines: + self.write(line) + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._text_stream, name) + + def isatty(self) -> bool: + return self.buffer.isatty() + + def __repr__(self): + return f"" + + +def _get_text_stdin(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stdout(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stderr(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +_stream_factories: t.Mapping[int, t.Callable[[t.BinaryIO], t.TextIO]] = { + 0: _get_text_stdin, + 1: _get_text_stdout, + 2: _get_text_stderr, +} + + +def _is_console(f: t.TextIO) -> bool: + if not hasattr(f, "fileno"): + return False + + try: + fileno = f.fileno() + except (OSError, io.UnsupportedOperation): + return False + + handle = msvcrt.get_osfhandle(fileno) + return bool(GetConsoleMode(handle, byref(DWORD()))) + + +def _get_windows_console_stream( + f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] +) -> t.Optional[t.TextIO]: + if ( + get_buffer is not None + and encoding in {"utf-16-le", None} + and errors in {"strict", None} + and _is_console(f) + ): + func = _stream_factories.get(f.fileno()) + if func is not None: + b = getattr(f, "buffer", None) + + if b is None: + return None + + return func(b) diff --git a/.venv/lib/python3.9/site-packages/click/core.py b/.venv/lib/python3.9/site-packages/click/core.py new file mode 100644 index 0000000..e630501 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/core.py @@ -0,0 +1,3047 @@ +import enum +import errno +import inspect +import os +import sys +import typing as t +from collections import abc +from contextlib import contextmanager +from contextlib import ExitStack +from functools import update_wrapper +from gettext import gettext as _ +from gettext import ngettext +from itertools import repeat +from types import TracebackType + +from . import types +from .exceptions import Abort +from .exceptions import BadParameter +from .exceptions import ClickException +from .exceptions import Exit +from .exceptions import MissingParameter +from .exceptions import UsageError +from .formatting import HelpFormatter +from .formatting import join_options +from .globals import pop_context +from .globals import push_context +from .parser import _flag_needs_value +from .parser import OptionParser +from .parser import split_opt +from .termui import confirm +from .termui import prompt +from .termui import style +from .utils import _detect_program_name +from .utils import _expand_args +from .utils import echo +from .utils import make_default_short_help +from .utils import make_str +from .utils import PacifyFlushWrapper + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .decorators import HelpOption + from .shell_completion import CompletionItem + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) +V = t.TypeVar("V") + + +def _complete_visible_commands( + ctx: "Context", incomplete: str +) -> t.Iterator[t.Tuple[str, "Command"]]: + """List all the subcommands of a group that start with the + incomplete value and aren't hidden. + + :param ctx: Invocation context for the group. + :param incomplete: Value being completed. May be empty. + """ + multi = t.cast(MultiCommand, ctx.command) + + for name in multi.list_commands(ctx): + if name.startswith(incomplete): + command = multi.get_command(ctx, name) + + if command is not None and not command.hidden: + yield name, command + + +def _check_multicommand( + base_command: "MultiCommand", cmd_name: str, cmd: "Command", register: bool = False +) -> None: + if not base_command.chain or not isinstance(cmd, MultiCommand): + return + if register: + hint = ( + "It is not possible to add multi commands as children to" + " another multi command that is in chain mode." + ) + else: + hint = ( + "Found a multi command as subcommand to a multi command" + " that is in chain mode. This is not supported." + ) + raise RuntimeError( + f"{hint}. Command {base_command.name!r} is set to chain and" + f" {cmd_name!r} was added as a subcommand but it in itself is a" + f" multi command. ({cmd_name!r} is a {type(cmd).__name__}" + f" within a chained {type(base_command).__name__} named" + f" {base_command.name!r})." + ) + + +def batch(iterable: t.Iterable[V], batch_size: int) -> t.List[t.Tuple[V, ...]]: + return list(zip(*repeat(iter(iterable), batch_size))) + + +@contextmanager +def augment_usage_errors( + ctx: "Context", param: t.Optional["Parameter"] = None +) -> t.Iterator[None]: + """Context manager that attaches extra information to exceptions.""" + try: + yield + except BadParameter as e: + if e.ctx is None: + e.ctx = ctx + if param is not None and e.param is None: + e.param = param + raise + except UsageError as e: + if e.ctx is None: + e.ctx = ctx + raise + + +def iter_params_for_processing( + invocation_order: t.Sequence["Parameter"], + declaration_order: t.Sequence["Parameter"], +) -> t.List["Parameter"]: + """Returns all declared parameters in the order they should be processed. + + The declared parameters are re-shuffled depending on the order in which + they were invoked, as well as the eagerness of each parameters. + + The invocation order takes precedence over the declaration order. I.e. the + order in which the user provided them to the CLI is respected. + + This behavior and its effect on callback evaluation is detailed at: + https://click.palletsprojects.com/en/stable/advanced/#callback-evaluation-order + """ + + def sort_key(item: "Parameter") -> t.Tuple[bool, float]: + try: + idx: float = invocation_order.index(item) + except ValueError: + idx = float("inf") + + return not item.is_eager, idx + + return sorted(declaration_order, key=sort_key) + + +class ParameterSource(enum.Enum): + """This is an :class:`~enum.Enum` that indicates the source of a + parameter's value. + + Use :meth:`click.Context.get_parameter_source` to get the + source for a parameter by name. + + .. versionchanged:: 8.0 + Use :class:`~enum.Enum` and drop the ``validate`` method. + + .. versionchanged:: 8.0 + Added the ``PROMPT`` value. + """ + + COMMANDLINE = enum.auto() + """The value was provided by the command line args.""" + ENVIRONMENT = enum.auto() + """The value was provided with an environment variable.""" + DEFAULT = enum.auto() + """Used the default specified by the parameter.""" + DEFAULT_MAP = enum.auto() + """Used a default provided by :attr:`Context.default_map`.""" + PROMPT = enum.auto() + """Used a prompt to confirm a default or provide a value.""" + + +class Context: + """The context is a special internal object that holds state relevant + for the script execution at every single level. It's normally invisible + to commands unless they opt-in to getting access to it. + + The context is useful as it can pass internal objects around and can + control special execution features such as reading data from + environment variables. + + A context can be used as context manager in which case it will call + :meth:`close` on teardown. + + :param command: the command class for this context. + :param parent: the parent context. + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it is usually + the name of the script, for commands below it it's + the name of the script. + :param obj: an arbitrary object of user data. + :param auto_envvar_prefix: the prefix to use for automatic environment + variables. If this is `None` then reading + from environment variables is disabled. This + does not affect manually set environment + variables which are always read. + :param default_map: a dictionary (like object) with default values + for parameters. + :param terminal_width: the width of the terminal. The default is + inherit from parent context. If no context + defines the terminal width then auto + detection will be applied. + :param max_content_width: the maximum width for content rendered by + Click (this currently only affects help + pages). This defaults to 80 characters if + not overridden. In other words: even if the + terminal is larger than that, Click will not + format things wider than 80 characters by + default. In addition to that, formatters might + add some safety mapping on the right. + :param resilient_parsing: if this flag is enabled then Click will + parse without any interactivity or callback + invocation. Default values will also be + ignored. This is useful for implementing + things such as completion support. + :param allow_extra_args: if this is set to `True` then extra arguments + at the end will not raise an error and will be + kept on the context. The default is to inherit + from the command. + :param allow_interspersed_args: if this is set to `False` then options + and arguments cannot be mixed. The + default is to inherit from the command. + :param ignore_unknown_options: instructs click to ignore options it does + not know and keeps them for later + processing. + :param help_option_names: optionally a list of strings that define how + the default help parameter is named. The + default is ``['--help']``. + :param token_normalize_func: an optional function that is used to + normalize tokens (options, choices, + etc.). This for instance can be used to + implement case insensitive behavior. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are used in texts that Click prints which is by + default not the case. This for instance would affect + help output. + :param show_default: Show the default value for commands. If this + value is not set, it defaults to the value from the parent + context. ``Command.show_default`` overrides this default for the + specific command. + + .. versionchanged:: 8.1 + The ``show_default`` parameter is overridden by + ``Command.show_default``, instead of the other way around. + + .. versionchanged:: 8.0 + The ``show_default`` parameter defaults to the value from the + parent context. + + .. versionchanged:: 7.1 + Added the ``show_default`` parameter. + + .. versionchanged:: 4.0 + Added the ``color``, ``ignore_unknown_options``, and + ``max_content_width`` parameters. + + .. versionchanged:: 3.0 + Added the ``allow_extra_args`` and ``allow_interspersed_args`` + parameters. + + .. versionchanged:: 2.0 + Added the ``resilient_parsing``, ``help_option_names``, and + ``token_normalize_func`` parameters. + """ + + #: The formatter class to create with :meth:`make_formatter`. + #: + #: .. versionadded:: 8.0 + formatter_class: t.Type["HelpFormatter"] = HelpFormatter + + def __init__( + self, + command: "Command", + parent: t.Optional["Context"] = None, + info_name: t.Optional[str] = None, + obj: t.Optional[t.Any] = None, + auto_envvar_prefix: t.Optional[str] = None, + default_map: t.Optional[t.MutableMapping[str, t.Any]] = None, + terminal_width: t.Optional[int] = None, + max_content_width: t.Optional[int] = None, + resilient_parsing: bool = False, + allow_extra_args: t.Optional[bool] = None, + allow_interspersed_args: t.Optional[bool] = None, + ignore_unknown_options: t.Optional[bool] = None, + help_option_names: t.Optional[t.List[str]] = None, + token_normalize_func: t.Optional[t.Callable[[str], str]] = None, + color: t.Optional[bool] = None, + show_default: t.Optional[bool] = None, + ) -> None: + #: the parent context or `None` if none exists. + self.parent = parent + #: the :class:`Command` for this context. + self.command = command + #: the descriptive information name + self.info_name = info_name + #: Map of parameter names to their parsed values. Parameters + #: with ``expose_value=False`` are not stored. + self.params: t.Dict[str, t.Any] = {} + #: the leftover arguments. + self.args: t.List[str] = [] + #: protected arguments. These are arguments that are prepended + #: to `args` when certain parsing scenarios are encountered but + #: must be never propagated to another arguments. This is used + #: to implement nested parsing. + self.protected_args: t.List[str] = [] + #: the collected prefixes of the command's options. + self._opt_prefixes: t.Set[str] = set(parent._opt_prefixes) if parent else set() + + if obj is None and parent is not None: + obj = parent.obj + + #: the user object stored. + self.obj: t.Any = obj + self._meta: t.Dict[str, t.Any] = getattr(parent, "meta", {}) + + #: A dictionary (-like object) with defaults for parameters. + if ( + default_map is None + and info_name is not None + and parent is not None + and parent.default_map is not None + ): + default_map = parent.default_map.get(info_name) + + self.default_map: t.Optional[t.MutableMapping[str, t.Any]] = default_map + + #: This flag indicates if a subcommand is going to be executed. A + #: group callback can use this information to figure out if it's + #: being executed directly or because the execution flow passes + #: onwards to a subcommand. By default it's None, but it can be + #: the name of the subcommand to execute. + #: + #: If chaining is enabled this will be set to ``'*'`` in case + #: any commands are executed. It is however not possible to + #: figure out which ones. If you require this knowledge you + #: should use a :func:`result_callback`. + self.invoked_subcommand: t.Optional[str] = None + + if terminal_width is None and parent is not None: + terminal_width = parent.terminal_width + + #: The width of the terminal (None is autodetection). + self.terminal_width: t.Optional[int] = terminal_width + + if max_content_width is None and parent is not None: + max_content_width = parent.max_content_width + + #: The maximum width of formatted content (None implies a sensible + #: default which is 80 for most things). + self.max_content_width: t.Optional[int] = max_content_width + + if allow_extra_args is None: + allow_extra_args = command.allow_extra_args + + #: Indicates if the context allows extra args or if it should + #: fail on parsing. + #: + #: .. versionadded:: 3.0 + self.allow_extra_args = allow_extra_args + + if allow_interspersed_args is None: + allow_interspersed_args = command.allow_interspersed_args + + #: Indicates if the context allows mixing of arguments and + #: options or not. + #: + #: .. versionadded:: 3.0 + self.allow_interspersed_args: bool = allow_interspersed_args + + if ignore_unknown_options is None: + ignore_unknown_options = command.ignore_unknown_options + + #: Instructs click to ignore options that a command does not + #: understand and will store it on the context for later + #: processing. This is primarily useful for situations where you + #: want to call into external programs. Generally this pattern is + #: strongly discouraged because it's not possibly to losslessly + #: forward all arguments. + #: + #: .. versionadded:: 4.0 + self.ignore_unknown_options: bool = ignore_unknown_options + + if help_option_names is None: + if parent is not None: + help_option_names = parent.help_option_names + else: + help_option_names = ["--help"] + + #: The names for the help options. + self.help_option_names: t.List[str] = help_option_names + + if token_normalize_func is None and parent is not None: + token_normalize_func = parent.token_normalize_func + + #: An optional normalization function for tokens. This is + #: options, choices, commands etc. + self.token_normalize_func: t.Optional[t.Callable[[str], str]] = ( + token_normalize_func + ) + + #: Indicates if resilient parsing is enabled. In that case Click + #: will do its best to not cause any failures and default values + #: will be ignored. Useful for completion. + self.resilient_parsing: bool = resilient_parsing + + # If there is no envvar prefix yet, but the parent has one and + # the command on this level has a name, we can expand the envvar + # prefix automatically. + if auto_envvar_prefix is None: + if ( + parent is not None + and parent.auto_envvar_prefix is not None + and self.info_name is not None + ): + auto_envvar_prefix = ( + f"{parent.auto_envvar_prefix}_{self.info_name.upper()}" + ) + else: + auto_envvar_prefix = auto_envvar_prefix.upper() + + if auto_envvar_prefix is not None: + auto_envvar_prefix = auto_envvar_prefix.replace("-", "_") + + self.auto_envvar_prefix: t.Optional[str] = auto_envvar_prefix + + if color is None and parent is not None: + color = parent.color + + #: Controls if styling output is wanted or not. + self.color: t.Optional[bool] = color + + if show_default is None and parent is not None: + show_default = parent.show_default + + #: Show option default values when formatting help text. + self.show_default: t.Optional[bool] = show_default + + self._close_callbacks: t.List[t.Callable[[], t.Any]] = [] + self._depth = 0 + self._parameter_source: t.Dict[str, ParameterSource] = {} + self._exit_stack = ExitStack() + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire CLI + structure. + + .. code-block:: python + + with Context(cli) as ctx: + info = ctx.to_info_dict() + + .. versionadded:: 8.0 + """ + return { + "command": self.command.to_info_dict(self), + "info_name": self.info_name, + "allow_extra_args": self.allow_extra_args, + "allow_interspersed_args": self.allow_interspersed_args, + "ignore_unknown_options": self.ignore_unknown_options, + "auto_envvar_prefix": self.auto_envvar_prefix, + } + + def __enter__(self) -> "Context": + self._depth += 1 + push_context(self) + return self + + def __exit__( + self, + exc_type: t.Optional[t.Type[BaseException]], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + self._depth -= 1 + if self._depth == 0: + self.close() + pop_context() + + @contextmanager + def scope(self, cleanup: bool = True) -> t.Iterator["Context"]: + """This helper method can be used with the context object to promote + it to the current thread local (see :func:`get_current_context`). + The default behavior of this is to invoke the cleanup functions which + can be disabled by setting `cleanup` to `False`. The cleanup + functions are typically used for things such as closing file handles. + + If the cleanup is intended the context object can also be directly + used as a context manager. + + Example usage:: + + with ctx.scope(): + assert get_current_context() is ctx + + This is equivalent:: + + with ctx: + assert get_current_context() is ctx + + .. versionadded:: 5.0 + + :param cleanup: controls if the cleanup functions should be run or + not. The default is to run these functions. In + some situations the context only wants to be + temporarily pushed in which case this can be disabled. + Nested pushes automatically defer the cleanup. + """ + if not cleanup: + self._depth += 1 + try: + with self as rv: + yield rv + finally: + if not cleanup: + self._depth -= 1 + + @property + def meta(self) -> t.Dict[str, t.Any]: + """This is a dictionary which is shared with all the contexts + that are nested. It exists so that click utilities can store some + state here if they need to. It is however the responsibility of + that code to manage this dictionary well. + + The keys are supposed to be unique dotted strings. For instance + module paths are a good choice for it. What is stored in there is + irrelevant for the operation of click. However what is important is + that code that places data here adheres to the general semantics of + the system. + + Example usage:: + + LANG_KEY = f'{__name__}.lang' + + def set_language(value): + ctx = get_current_context() + ctx.meta[LANG_KEY] = value + + def get_language(): + return get_current_context().meta.get(LANG_KEY, 'en_US') + + .. versionadded:: 5.0 + """ + return self._meta + + def make_formatter(self) -> HelpFormatter: + """Creates the :class:`~click.HelpFormatter` for the help and + usage output. + + To quickly customize the formatter class used without overriding + this method, set the :attr:`formatter_class` attribute. + + .. versionchanged:: 8.0 + Added the :attr:`formatter_class` attribute. + """ + return self.formatter_class( + width=self.terminal_width, max_width=self.max_content_width + ) + + def with_resource(self, context_manager: t.ContextManager[V]) -> V: + """Register a resource as if it were used in a ``with`` + statement. The resource will be cleaned up when the context is + popped. + + Uses :meth:`contextlib.ExitStack.enter_context`. It calls the + resource's ``__enter__()`` method and returns the result. When + the context is popped, it closes the stack, which calls the + resource's ``__exit__()`` method. + + To register a cleanup function for something that isn't a + context manager, use :meth:`call_on_close`. Or use something + from :mod:`contextlib` to turn it into a context manager first. + + .. code-block:: python + + @click.group() + @click.option("--name") + @click.pass_context + def cli(ctx): + ctx.obj = ctx.with_resource(connect_db(name)) + + :param context_manager: The context manager to enter. + :return: Whatever ``context_manager.__enter__()`` returns. + + .. versionadded:: 8.0 + """ + return self._exit_stack.enter_context(context_manager) + + def call_on_close(self, f: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: + """Register a function to be called when the context tears down. + + This can be used to close resources opened during the script + execution. Resources that support Python's context manager + protocol which would be used in a ``with`` statement should be + registered with :meth:`with_resource` instead. + + :param f: The function to execute on teardown. + """ + return self._exit_stack.callback(f) + + def close(self) -> None: + """Invoke all close callbacks registered with + :meth:`call_on_close`, and exit all context managers entered + with :meth:`with_resource`. + """ + self._exit_stack.close() + # In case the context is reused, create a new exit stack. + self._exit_stack = ExitStack() + + @property + def command_path(self) -> str: + """The computed command path. This is used for the ``usage`` + information on the help page. It's automatically created by + combining the info names of the chain of contexts to the root. + """ + rv = "" + if self.info_name is not None: + rv = self.info_name + if self.parent is not None: + parent_command_path = [self.parent.command_path] + + if isinstance(self.parent.command, Command): + for param in self.parent.command.get_params(self): + parent_command_path.extend(param.get_usage_pieces(self)) + + rv = f"{' '.join(parent_command_path)} {rv}" + return rv.lstrip() + + def find_root(self) -> "Context": + """Finds the outermost context.""" + node = self + while node.parent is not None: + node = node.parent + return node + + def find_object(self, object_type: t.Type[V]) -> t.Optional[V]: + """Finds the closest object of a given type.""" + node: t.Optional[Context] = self + + while node is not None: + if isinstance(node.obj, object_type): + return node.obj + + node = node.parent + + return None + + def ensure_object(self, object_type: t.Type[V]) -> V: + """Like :meth:`find_object` but sets the innermost object to a + new instance of `object_type` if it does not exist. + """ + rv = self.find_object(object_type) + if rv is None: + self.obj = rv = object_type() + return rv + + @t.overload + def lookup_default( + self, name: str, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: ... + + @t.overload + def lookup_default( + self, name: str, call: "te.Literal[False]" = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: ... + + def lookup_default(self, name: str, call: bool = True) -> t.Optional[t.Any]: + """Get the default for a parameter from :attr:`default_map`. + + :param name: Name of the parameter. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + if self.default_map is not None: + value = self.default_map.get(name) + + if call and callable(value): + return value() + + return value + + return None + + def fail(self, message: str) -> "te.NoReturn": + """Aborts the execution of the program with a specific error + message. + + :param message: the error message to fail with. + """ + raise UsageError(message, self) + + def abort(self) -> "te.NoReturn": + """Aborts the script.""" + raise Abort() + + def exit(self, code: int = 0) -> "te.NoReturn": + """Exits the application with a given exit code.""" + raise Exit(code) + + def get_usage(self) -> str: + """Helper method to get formatted usage string for the current + context and command. + """ + return self.command.get_usage(self) + + def get_help(self) -> str: + """Helper method to get formatted help page for the current + context and command. + """ + return self.command.get_help(self) + + def _make_sub_context(self, command: "Command") -> "Context": + """Create a new context of the same type as this context, but + for a new command. + + :meta private: + """ + return type(self)(command, info_name=command.name, parent=self) + + @t.overload + def invoke( + __self, + __callback: "t.Callable[..., V]", + *args: t.Any, + **kwargs: t.Any, + ) -> V: ... + + @t.overload + def invoke( + __self, + __callback: "Command", + *args: t.Any, + **kwargs: t.Any, + ) -> t.Any: ... + + def invoke( + __self, + __callback: t.Union["Command", "t.Callable[..., V]"], + *args: t.Any, + **kwargs: t.Any, + ) -> t.Union[t.Any, V]: + """Invokes a command callback in exactly the way it expects. There + are two ways to invoke this method: + + 1. the first argument can be a callback and all other arguments and + keyword arguments are forwarded directly to the function. + 2. the first argument is a click command object. In that case all + arguments are forwarded as well but proper click parameters + (options and click arguments) must be keyword arguments and Click + will fill in defaults. + + Note that before Click 3.2 keyword arguments were not properly filled + in against the intention of this code and no context was created. For + more information about this change and why it was done in a bugfix + release see :ref:`upgrade-to-3.2`. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if :meth:`forward` is called at multiple levels. + """ + if isinstance(__callback, Command): + other_cmd = __callback + + if other_cmd.callback is None: + raise TypeError( + "The given command does not have a callback that can be invoked." + ) + else: + __callback = t.cast("t.Callable[..., V]", other_cmd.callback) + + ctx = __self._make_sub_context(other_cmd) + + for param in other_cmd.params: + if param.name not in kwargs and param.expose_value: + kwargs[param.name] = param.type_cast_value( # type: ignore + ctx, param.get_default(ctx) + ) + + # Track all kwargs as params, so that forward() will pass + # them on in subsequent calls. + ctx.params.update(kwargs) + else: + ctx = __self + + with augment_usage_errors(__self): + with ctx: + return __callback(*args, **kwargs) + + def forward(__self, __cmd: "Command", *args: t.Any, **kwargs: t.Any) -> t.Any: + """Similar to :meth:`invoke` but fills in default keyword + arguments from the current context if the other command expects + it. This cannot invoke callbacks directly, only other commands. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if ``forward`` is called at multiple levels. + """ + # Can only forward to other commands, not direct callbacks. + if not isinstance(__cmd, Command): + raise TypeError("Callback is not a command.") + + for param in __self.params: + if param not in kwargs: + kwargs[param] = __self.params[param] + + return __self.invoke(__cmd, *args, **kwargs) + + def set_parameter_source(self, name: str, source: ParameterSource) -> None: + """Set the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + :param name: The name of the parameter. + :param source: A member of :class:`~click.core.ParameterSource`. + """ + self._parameter_source[name] = source + + def get_parameter_source(self, name: str) -> t.Optional[ParameterSource]: + """Get the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + This can be useful for determining when a user specified a value + on the command line that is the same as the default value. It + will be :attr:`~click.core.ParameterSource.DEFAULT` only if the + value was actually taken from the default. + + :param name: The name of the parameter. + :rtype: ParameterSource + + .. versionchanged:: 8.0 + Returns ``None`` if the parameter was not provided from any + source. + """ + return self._parameter_source.get(name) + + +class BaseCommand: + """The base command implements the minimal API contract of commands. + Most code will never use this as it does not implement a lot of useful + functionality but it can act as the direct subclass of alternative + parsing methods that do not depend on the Click parser. + + For instance, this can be used to bridge Click and other systems like + argparse or docopt. + + Because base commands do not implement a lot of the API that other + parts of Click take for granted, they are not supported for all + operations. For instance, they cannot be used with the decorators + usually and they have no built-in callback system. + + .. versionchanged:: 2.0 + Added the `context_settings` parameter. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + """ + + #: The context class to create with :meth:`make_context`. + #: + #: .. versionadded:: 8.0 + context_class: t.Type[Context] = Context + #: the default for the :attr:`Context.allow_extra_args` flag. + allow_extra_args = False + #: the default for the :attr:`Context.allow_interspersed_args` flag. + allow_interspersed_args = True + #: the default for the :attr:`Context.ignore_unknown_options` flag. + ignore_unknown_options = False + + def __init__( + self, + name: t.Optional[str], + context_settings: t.Optional[t.MutableMapping[str, t.Any]] = None, + ) -> None: + #: the name the command thinks it has. Upon registering a command + #: on a :class:`Group` the group will default the command name + #: with this information. You should instead use the + #: :class:`Context`\'s :attr:`~Context.info_name` attribute. + self.name = name + + if context_settings is None: + context_settings = {} + + #: an optional dictionary with defaults passed to the context. + self.context_settings: t.MutableMapping[str, t.Any] = context_settings + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire structure + below this command. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + :param ctx: A :class:`Context` representing this command. + + .. versionadded:: 8.0 + """ + return {"name": self.name} + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def get_usage(self, ctx: Context) -> str: + raise NotImplementedError("Base commands cannot get usage") + + def get_help(self, ctx: Context) -> str: + raise NotImplementedError("Base commands cannot get help") + + def make_context( + self, + info_name: t.Optional[str], + args: t.List[str], + parent: t.Optional[Context] = None, + **extra: t.Any, + ) -> Context: + """This function when given an info name and arguments will kick + off the parsing and create a new :class:`Context`. It does not + invoke the actual command callback though. + + To quickly customize the context class used without overriding + this method, set the :attr:`context_class` attribute. + + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it's usually + the name of the script, for commands below it's + the name of the command. + :param args: the arguments to parse as list of strings. + :param parent: the parent context if available. + :param extra: extra keyword arguments forwarded to the context + constructor. + + .. versionchanged:: 8.0 + Added the :attr:`context_class` attribute. + """ + for key, value in self.context_settings.items(): + if key not in extra: + extra[key] = value + + ctx = self.context_class( + self, # type: ignore[arg-type] + info_name=info_name, + parent=parent, + **extra, + ) + + with ctx.scope(cleanup=False): + self.parse_args(ctx, args) + return ctx + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + """Given a context and a list of arguments this creates the parser + and parses the arguments, then modifies the context as necessary. + This is automatically invoked by :meth:`make_context`. + """ + raise NotImplementedError("Base commands do not know how to parse arguments.") + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the command. The default + implementation is raising a not implemented error. + """ + raise NotImplementedError("Base commands are not invocable by default") + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of chained multi-commands. + + Any command could be part of a chained multi-command, so sibling + commands are valid at any point during command completion. Other + command classes will return more completions. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results: t.List[CompletionItem] = [] + + while ctx.parent is not None: + ctx = ctx.parent + + if isinstance(ctx.command, MultiCommand) and ctx.command.chain: + results.extend( + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + if name not in ctx.protected_args + ) + + return results + + @t.overload + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: "te.Literal[True]" = True, + **extra: t.Any, + ) -> "te.NoReturn": ... + + @t.overload + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: bool = ..., + **extra: t.Any, + ) -> t.Any: ... + + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: bool = True, + windows_expand_args: bool = True, + **extra: t.Any, + ) -> t.Any: + """This is the way to invoke a script with all the bells and + whistles as a command line application. This will always terminate + the application after a call. If this is not wanted, ``SystemExit`` + needs to be caught. + + This method is also available by directly calling the instance of + a :class:`Command`. + + :param args: the arguments that should be used for parsing. If not + provided, ``sys.argv[1:]`` is used. + :param prog_name: the program name that should be used. By default + the program name is constructed by taking the file + name from ``sys.argv[0]``. + :param complete_var: the environment variable that controls the + bash completion support. The default is + ``"__COMPLETE"`` with prog_name in + uppercase. + :param standalone_mode: the default behavior is to invoke the script + in standalone mode. Click will then + handle exceptions and convert them into + error messages and the function will never + return but shut down the interpreter. If + this is set to `False` they will be + propagated to the caller and the return + value of this function is the return value + of :meth:`invoke`. + :param windows_expand_args: Expand glob patterns, user dir, and + env vars in command line args on Windows. + :param extra: extra keyword arguments are forwarded to the context + constructor. See :class:`Context` for more information. + + .. versionchanged:: 8.0.1 + Added the ``windows_expand_args`` parameter to allow + disabling command line arg expansion on Windows. + + .. versionchanged:: 8.0 + When taking arguments from ``sys.argv`` on Windows, glob + patterns, user dir, and env vars are expanded. + + .. versionchanged:: 3.0 + Added the ``standalone_mode`` parameter. + """ + if args is None: + args = sys.argv[1:] + + if os.name == "nt" and windows_expand_args: + args = _expand_args(args) + else: + args = list(args) + + if prog_name is None: + prog_name = _detect_program_name() + + # Process shell completion requests and exit early. + self._main_shell_completion(extra, prog_name, complete_var) + + try: + try: + with self.make_context(prog_name, args, **extra) as ctx: + rv = self.invoke(ctx) + if not standalone_mode: + return rv + # it's not safe to `ctx.exit(rv)` here! + # note that `rv` may actually contain data like "1" which + # has obvious effects + # more subtle case: `rv=[None, None]` can come out of + # chained commands which all returned `None` -- so it's not + # even always obvious that `rv` indicates success/failure + # by its truthiness/falsiness + ctx.exit() + except (EOFError, KeyboardInterrupt) as e: + echo(file=sys.stderr) + raise Abort() from e + except ClickException as e: + if not standalone_mode: + raise + e.show() + sys.exit(e.exit_code) + except OSError as e: + if e.errno == errno.EPIPE: + sys.stdout = t.cast(t.TextIO, PacifyFlushWrapper(sys.stdout)) + sys.stderr = t.cast(t.TextIO, PacifyFlushWrapper(sys.stderr)) + sys.exit(1) + else: + raise + except Exit as e: + if standalone_mode: + sys.exit(e.exit_code) + else: + # in non-standalone mode, return the exit code + # note that this is only reached if `self.invoke` above raises + # an Exit explicitly -- thus bypassing the check there which + # would return its result + # the results of non-standalone execution may therefore be + # somewhat ambiguous: if there are codepaths which lead to + # `ctx.exit(1)` and to `return 1`, the caller won't be able to + # tell the difference between the two + return e.exit_code + except Abort: + if not standalone_mode: + raise + echo(_("Aborted!"), file=sys.stderr) + sys.exit(1) + + def _main_shell_completion( + self, + ctx_args: t.MutableMapping[str, t.Any], + prog_name: str, + complete_var: t.Optional[str] = None, + ) -> None: + """Check if the shell is asking for tab completion, process + that, then exit early. Called from :meth:`main` before the + program is invoked. + + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. Defaults to + ``_{PROG_NAME}_COMPLETE``. + + .. versionchanged:: 8.2.0 + Dots (``.``) in ``prog_name`` are replaced with underscores (``_``). + """ + if complete_var is None: + complete_name = prog_name.replace("-", "_").replace(".", "_") + complete_var = f"_{complete_name}_COMPLETE".upper() + + instruction = os.environ.get(complete_var) + + if not instruction: + return + + from .shell_completion import shell_complete + + rv = shell_complete(self, ctx_args, prog_name, complete_var, instruction) + sys.exit(rv) + + def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any: + """Alias for :meth:`main`.""" + return self.main(*args, **kwargs) + + +class Command(BaseCommand): + """Commands are the basic building block of command line interfaces in + Click. A basic command handles command line parsing and might dispatch + more parsing to commands nested below it. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + :param callback: the callback to invoke. This is optional. + :param params: the parameters to register with this command. This can + be either :class:`Option` or :class:`Argument` objects. + :param help: the help string to use for this command. + :param epilog: like the help string but it's printed at the end of the + help page after everything else. + :param short_help: the short help to use for this command. This is + shown on the command listing of the parent command. + :param add_help_option: by default each command registers a ``--help`` + option. This can be disabled by this parameter. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is disabled by default. + If enabled this will add ``--help`` as argument + if no arguments are passed + :param hidden: hide this command from help outputs. + + :param deprecated: issues a message indicating that + the command is deprecated. + + .. versionchanged:: 8.1 + ``help``, ``epilog``, and ``short_help`` are stored unprocessed, + all formatting is done when outputting help text, not at init, + and is done even if not using the ``@command`` decorator. + + .. versionchanged:: 8.0 + Added a ``repr`` showing the command name. + + .. versionchanged:: 7.1 + Added the ``no_args_is_help`` parameter. + + .. versionchanged:: 2.0 + Added the ``context_settings`` parameter. + """ + + def __init__( + self, + name: t.Optional[str], + context_settings: t.Optional[t.MutableMapping[str, t.Any]] = None, + callback: t.Optional[t.Callable[..., t.Any]] = None, + params: t.Optional[t.List["Parameter"]] = None, + help: t.Optional[str] = None, + epilog: t.Optional[str] = None, + short_help: t.Optional[str] = None, + options_metavar: t.Optional[str] = "[OPTIONS]", + add_help_option: bool = True, + no_args_is_help: bool = False, + hidden: bool = False, + deprecated: bool = False, + ) -> None: + super().__init__(name, context_settings) + #: the callback to execute when the command fires. This might be + #: `None` in which case nothing happens. + self.callback = callback + #: the list of parameters for this command in the order they + #: should show up in the help page and execute. Eager parameters + #: will automatically be handled before non eager ones. + self.params: t.List[Parameter] = params or [] + self.help = help + self.epilog = epilog + self.options_metavar = options_metavar + self.short_help = short_help + self.add_help_option = add_help_option + self._help_option: t.Optional[HelpOption] = None + self.no_args_is_help = no_args_is_help + self.hidden = hidden + self.deprecated = deprecated + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + info_dict.update( + params=[param.to_info_dict() for param in self.get_params(ctx)], + help=self.help, + epilog=self.epilog, + short_help=self.short_help, + hidden=self.hidden, + deprecated=self.deprecated, + ) + return info_dict + + def get_usage(self, ctx: Context) -> str: + """Formats the usage line into a string and returns it. + + Calls :meth:`format_usage` internally. + """ + formatter = ctx.make_formatter() + self.format_usage(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_params(self, ctx: Context) -> t.List["Parameter"]: + rv = self.params + help_option = self.get_help_option(ctx) + + if help_option is not None: + rv = [*rv, help_option] + + return rv + + def format_usage(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the usage line into the formatter. + + This is a low-level method called by :meth:`get_usage`. + """ + pieces = self.collect_usage_pieces(ctx) + formatter.write_usage(ctx.command_path, " ".join(pieces)) + + def collect_usage_pieces(self, ctx: Context) -> t.List[str]: + """Returns all the pieces that go into the usage line and returns + it as a list of strings. + """ + rv = [self.options_metavar] if self.options_metavar else [] + + for param in self.get_params(ctx): + rv.extend(param.get_usage_pieces(ctx)) + + return rv + + def get_help_option_names(self, ctx: Context) -> t.List[str]: + """Returns the names for the help option.""" + all_names = set(ctx.help_option_names) + for param in self.params: + all_names.difference_update(param.opts) + all_names.difference_update(param.secondary_opts) + return list(all_names) + + def get_help_option(self, ctx: Context) -> t.Optional["Option"]: + """Returns the help option object. + + Unless ``add_help_option`` is ``False``. + + .. versionchanged:: 8.1.8 + The help option is now cached to avoid creating it multiple times. + """ + help_options = self.get_help_option_names(ctx) + + if not help_options or not self.add_help_option: + return None + + # Cache the help option object in private _help_option attribute to + # avoid creating it multiple times. Not doing this will break the + # callback odering by iter_params_for_processing(), which relies on + # object comparison. + if self._help_option is None: + # Avoid circular import. + from .decorators import HelpOption + + self._help_option = HelpOption(help_options) + + return self._help_option + + def make_parser(self, ctx: Context) -> OptionParser: + """Creates the underlying option parser for this command.""" + parser = OptionParser(ctx) + for param in self.get_params(ctx): + param.add_to_parser(parser, ctx) + return parser + + def get_help(self, ctx: Context) -> str: + """Formats the help into a string and returns it. + + Calls :meth:`format_help` internally. + """ + formatter = ctx.make_formatter() + self.format_help(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_short_help_str(self, limit: int = 45) -> str: + """Gets short help for the command or makes it by shortening the + long help string. + """ + if self.short_help: + text = inspect.cleandoc(self.short_help) + elif self.help: + text = make_default_short_help(self.help, limit) + else: + text = "" + + if self.deprecated: + text = _("(Deprecated) {text}").format(text=text) + + return text.strip() + + def format_help(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help into the formatter if it exists. + + This is a low-level method called by :meth:`get_help`. + + This calls the following methods: + + - :meth:`format_usage` + - :meth:`format_help_text` + - :meth:`format_options` + - :meth:`format_epilog` + """ + self.format_usage(ctx, formatter) + self.format_help_text(ctx, formatter) + self.format_options(ctx, formatter) + self.format_epilog(ctx, formatter) + + def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help text to the formatter if it exists.""" + if self.help is not None: + # truncate the help text to the first form feed + text = inspect.cleandoc(self.help).partition("\f")[0] + else: + text = "" + + if self.deprecated: + text = _("(Deprecated) {text}").format(text=text) + + if text: + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(text) + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes all the options into the formatter if they exist.""" + opts = [] + for param in self.get_params(ctx): + rv = param.get_help_record(ctx) + if rv is not None: + opts.append(rv) + + if opts: + with formatter.section(_("Options")): + formatter.write_dl(opts) + + def format_epilog(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the epilog into the formatter if it exists.""" + if self.epilog: + epilog = inspect.cleandoc(self.epilog) + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(epilog) + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + parser = self.make_parser(ctx) + opts, args, param_order = parser.parse_args(args=args) + + for param in iter_params_for_processing(param_order, self.get_params(ctx)): + value, args = param.handle_parse_result(ctx, opts, args) + + if args and not ctx.allow_extra_args and not ctx.resilient_parsing: + ctx.fail( + ngettext( + "Got unexpected extra argument ({args})", + "Got unexpected extra arguments ({args})", + len(args), + ).format(args=" ".join(map(str, args))) + ) + + ctx.args = args + ctx._opt_prefixes.update(parser._opt_prefixes) + return args + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the attached callback (if it exists) + in the right way. + """ + if self.deprecated: + message = _( + "DeprecationWarning: The command {name!r} is deprecated." + ).format(name=self.name) + echo(style(message, fg="red"), err=True) + + if self.callback is not None: + return ctx.invoke(self.callback, **ctx.params) + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of options and chained multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results: t.List[CompletionItem] = [] + + if incomplete and not incomplete[0].isalnum(): + for param in self.get_params(ctx): + if ( + not isinstance(param, Option) + or param.hidden + or ( + not param.multiple + and ctx.get_parameter_source(param.name) # type: ignore + is ParameterSource.COMMANDLINE + ) + ): + continue + + results.extend( + CompletionItem(name, help=param.help) + for name in [*param.opts, *param.secondary_opts] + if name.startswith(incomplete) + ) + + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class MultiCommand(Command): + """A multi command is the basic implementation of a command that + dispatches to subcommands. The most common version is the + :class:`Group`. + + :param invoke_without_command: this controls how the multi command itself + is invoked. By default it's only invoked + if a subcommand is provided. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is enabled by default if + `invoke_without_command` is disabled or disabled + if it's enabled. If enabled this will add + ``--help`` as argument if no arguments are + passed. + :param subcommand_metavar: the string that is used in the documentation + to indicate the subcommand place. + :param chain: if this is set to `True` chaining of multiple subcommands + is enabled. This restricts the form of commands in that + they cannot have optional arguments but it allows + multiple commands to be chained together. + :param result_callback: The result callback to attach to this multi + command. This can be set or changed later with the + :meth:`result_callback` decorator. + :param attrs: Other command arguments described in :class:`Command`. + """ + + allow_extra_args = True + allow_interspersed_args = False + + def __init__( + self, + name: t.Optional[str] = None, + invoke_without_command: bool = False, + no_args_is_help: t.Optional[bool] = None, + subcommand_metavar: t.Optional[str] = None, + chain: bool = False, + result_callback: t.Optional[t.Callable[..., t.Any]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + + if no_args_is_help is None: + no_args_is_help = not invoke_without_command + + self.no_args_is_help = no_args_is_help + self.invoke_without_command = invoke_without_command + + if subcommand_metavar is None: + if chain: + subcommand_metavar = "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." + else: + subcommand_metavar = "COMMAND [ARGS]..." + + self.subcommand_metavar = subcommand_metavar + self.chain = chain + # The result callback that is stored. This can be set or + # overridden with the :func:`result_callback` decorator. + self._result_callback = result_callback + + if self.chain: + for param in self.params: + if isinstance(param, Argument) and not param.required: + raise RuntimeError( + "Multi commands in chain mode cannot have" + " optional arguments." + ) + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + commands = {} + + for name in self.list_commands(ctx): + command = self.get_command(ctx, name) + + if command is None: + continue + + sub_ctx = ctx._make_sub_context(command) + + with sub_ctx.scope(cleanup=False): + commands[name] = command.to_info_dict(sub_ctx) + + info_dict.update(commands=commands, chain=self.chain) + return info_dict + + def collect_usage_pieces(self, ctx: Context) -> t.List[str]: + rv = super().collect_usage_pieces(ctx) + rv.append(self.subcommand_metavar) + return rv + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + super().format_options(ctx, formatter) + self.format_commands(ctx, formatter) + + def result_callback(self, replace: bool = False) -> t.Callable[[F], F]: + """Adds a result callback to the command. By default if a + result callback is already registered this will chain them but + this can be disabled with the `replace` parameter. The result + callback is invoked with the return value of the subcommand + (or the list of return values from all subcommands if chaining + is enabled) as well as the parameters as they would be passed + to the main callback. + + Example:: + + @click.group() + @click.option('-i', '--input', default=23) + def cli(input): + return 42 + + @cli.result_callback() + def process_result(result, input): + return result + input + + :param replace: if set to `True` an already existing result + callback will be removed. + + .. versionchanged:: 8.0 + Renamed from ``resultcallback``. + + .. versionadded:: 3.0 + """ + + def decorator(f: F) -> F: + old_callback = self._result_callback + + if old_callback is None or replace: + self._result_callback = f + return f + + def function(__value, *args, **kwargs): # type: ignore + inner = old_callback(__value, *args, **kwargs) + return f(inner, *args, **kwargs) + + self._result_callback = rv = update_wrapper(t.cast(F, function), f) + return rv # type: ignore[return-value] + + return decorator + + def format_commands(self, ctx: Context, formatter: HelpFormatter) -> None: + """Extra format methods for multi methods that adds all the commands + after the options. + """ + commands = [] + for subcommand in self.list_commands(ctx): + cmd = self.get_command(ctx, subcommand) + # What is this, the tool lied about a command. Ignore it + if cmd is None: + continue + if cmd.hidden: + continue + + commands.append((subcommand, cmd)) + + # allow for 3 times the default spacing + if len(commands): + limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands) + + rows = [] + for subcommand, cmd in commands: + help = cmd.get_short_help_str(limit) + rows.append((subcommand, help)) + + if rows: + with formatter.section(_("Commands")): + formatter.write_dl(rows) + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + rest = super().parse_args(ctx, args) + + if self.chain: + ctx.protected_args = rest + ctx.args = [] + elif rest: + ctx.protected_args, ctx.args = rest[:1], rest[1:] + + return ctx.args + + def invoke(self, ctx: Context) -> t.Any: + def _process_result(value: t.Any) -> t.Any: + if self._result_callback is not None: + value = ctx.invoke(self._result_callback, value, **ctx.params) + return value + + if not ctx.protected_args: + if self.invoke_without_command: + # No subcommand was invoked, so the result callback is + # invoked with the group return value for regular + # groups, or an empty list for chained groups. + with ctx: + rv = super().invoke(ctx) + return _process_result([] if self.chain else rv) + ctx.fail(_("Missing command.")) + + # Fetch args back out + args = [*ctx.protected_args, *ctx.args] + ctx.args = [] + ctx.protected_args = [] + + # If we're not in chain mode, we only allow the invocation of a + # single command but we also inform the current context about the + # name of the command to invoke. + if not self.chain: + # Make sure the context is entered so we do not clean up + # resources until the result processor has worked. + with ctx: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + ctx.invoked_subcommand = cmd_name + super().invoke(ctx) + sub_ctx = cmd.make_context(cmd_name, args, parent=ctx) + with sub_ctx: + return _process_result(sub_ctx.command.invoke(sub_ctx)) + + # In chain mode we create the contexts step by step, but after the + # base command has been invoked. Because at that point we do not + # know the subcommands yet, the invoked subcommand attribute is + # set to ``*`` to inform the command that subcommands are executed + # but nothing else. + with ctx: + ctx.invoked_subcommand = "*" if args else None + super().invoke(ctx) + + # Otherwise we make every single context and invoke them in a + # chain. In that case the return value to the result processor + # is the list of all invoked subcommand's results. + contexts = [] + while args: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + sub_ctx = cmd.make_context( + cmd_name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + ) + contexts.append(sub_ctx) + args, sub_ctx.args = sub_ctx.args, [] + + rv = [] + for sub_ctx in contexts: + with sub_ctx: + rv.append(sub_ctx.command.invoke(sub_ctx)) + return _process_result(rv) + + def resolve_command( + self, ctx: Context, args: t.List[str] + ) -> t.Tuple[t.Optional[str], t.Optional[Command], t.List[str]]: + cmd_name = make_str(args[0]) + original_cmd_name = cmd_name + + # Get the command + cmd = self.get_command(ctx, cmd_name) + + # If we can't find the command but there is a normalization + # function available, we try with that one. + if cmd is None and ctx.token_normalize_func is not None: + cmd_name = ctx.token_normalize_func(cmd_name) + cmd = self.get_command(ctx, cmd_name) + + # If we don't find the command we want to show an error message + # to the user that it was not provided. However, there is + # something else we should do: if the first argument looks like + # an option we want to kick off parsing again for arguments to + # resolve things like --help which now should go to the main + # place. + if cmd is None and not ctx.resilient_parsing: + if split_opt(cmd_name)[0]: + self.parse_args(ctx, ctx.args) + ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name)) + return cmd_name if cmd else None, cmd, args[1:] + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + """Given a context and a command name, this returns a + :class:`Command` object if it exists or returns `None`. + """ + raise NotImplementedError + + def list_commands(self, ctx: Context) -> t.List[str]: + """Returns a list of subcommand names in the order they should + appear. + """ + return [] + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of options, subcommands, and chained + multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results = [ + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + ] + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class Group(MultiCommand): + """A group allows a command to have subcommands attached. This is + the most common way to implement nesting in Click. + + :param name: The name of the group command. + :param commands: A dict mapping names to :class:`Command` objects. + Can also be a list of :class:`Command`, which will use + :attr:`Command.name` to create the dict. + :param attrs: Other command arguments described in + :class:`MultiCommand`, :class:`Command`, and + :class:`BaseCommand`. + + .. versionchanged:: 8.0 + The ``commands`` argument can be a list of command objects. + """ + + #: If set, this is used by the group's :meth:`command` decorator + #: as the default :class:`Command` class. This is useful to make all + #: subcommands use a custom command class. + #: + #: .. versionadded:: 8.0 + command_class: t.Optional[t.Type[Command]] = None + + #: If set, this is used by the group's :meth:`group` decorator + #: as the default :class:`Group` class. This is useful to make all + #: subgroups use a custom group class. + #: + #: If set to the special value :class:`type` (literally + #: ``group_class = type``), this group's class will be used as the + #: default class. This makes a custom group class continue to make + #: custom groups. + #: + #: .. versionadded:: 8.0 + group_class: t.Optional[t.Union[t.Type["Group"], t.Type[type]]] = None + # Literal[type] isn't valid, so use Type[type] + + def __init__( + self, + name: t.Optional[str] = None, + commands: t.Optional[ + t.Union[t.MutableMapping[str, Command], t.Sequence[Command]] + ] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + + if commands is None: + commands = {} + elif isinstance(commands, abc.Sequence): + commands = {c.name: c for c in commands if c.name is not None} + + #: The registered subcommands by their exported names. + self.commands: t.MutableMapping[str, Command] = commands + + def add_command(self, cmd: Command, name: t.Optional[str] = None) -> None: + """Registers another :class:`Command` with this group. If the name + is not provided, the name of the command is used. + """ + name = name or cmd.name + if name is None: + raise TypeError("Command has no name.") + _check_multicommand(self, name, cmd, register=True) + self.commands[name] = cmd + + @t.overload + def command(self, __func: t.Callable[..., t.Any]) -> Command: ... + + @t.overload + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Command]: ... + + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Union[t.Callable[[t.Callable[..., t.Any]], Command], Command]: + """A shortcut decorator for declaring and attaching a command to + the group. This takes the same arguments as :func:`command` and + immediately registers the created command with this group by + calling :meth:`add_command`. + + To customize the command class used, set the + :attr:`command_class` attribute. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`command_class` attribute. + """ + from .decorators import command + + func: t.Optional[t.Callable[..., t.Any]] = None + + if args and callable(args[0]): + assert ( + len(args) == 1 and not kwargs + ), "Use 'command(**kwargs)(callable)' to provide arguments." + (func,) = args + args = () + + if self.command_class and kwargs.get("cls") is None: + kwargs["cls"] = self.command_class + + def decorator(f: t.Callable[..., t.Any]) -> Command: + cmd: Command = command(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + return decorator + + @t.overload + def group(self, __func: t.Callable[..., t.Any]) -> "Group": ... + + @t.overload + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], "Group"]: ... + + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Union[t.Callable[[t.Callable[..., t.Any]], "Group"], "Group"]: + """A shortcut decorator for declaring and attaching a group to + the group. This takes the same arguments as :func:`group` and + immediately registers the created group with this group by + calling :meth:`add_command`. + + To customize the group class used, set the :attr:`group_class` + attribute. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`group_class` attribute. + """ + from .decorators import group + + func: t.Optional[t.Callable[..., t.Any]] = None + + if args and callable(args[0]): + assert ( + len(args) == 1 and not kwargs + ), "Use 'group(**kwargs)(callable)' to provide arguments." + (func,) = args + args = () + + if self.group_class is not None and kwargs.get("cls") is None: + if self.group_class is type: + kwargs["cls"] = type(self) + else: + kwargs["cls"] = self.group_class + + def decorator(f: t.Callable[..., t.Any]) -> "Group": + cmd: Group = group(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + return decorator + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + return self.commands.get(cmd_name) + + def list_commands(self, ctx: Context) -> t.List[str]: + return sorted(self.commands) + + +class CommandCollection(MultiCommand): + """A command collection is a multi command that merges multiple multi + commands together into one. This is a straightforward implementation + that accepts a list of different multi commands as sources and + provides all the commands for each of them. + + See :class:`MultiCommand` and :class:`Command` for the description of + ``name`` and ``attrs``. + """ + + def __init__( + self, + name: t.Optional[str] = None, + sources: t.Optional[t.List[MultiCommand]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + #: The list of registered multi commands. + self.sources: t.List[MultiCommand] = sources or [] + + def add_source(self, multi_cmd: MultiCommand) -> None: + """Adds a new multi command to the chain dispatcher.""" + self.sources.append(multi_cmd) + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + for source in self.sources: + rv = source.get_command(ctx, cmd_name) + + if rv is not None: + if self.chain: + _check_multicommand(self, cmd_name, rv) + + return rv + + return None + + def list_commands(self, ctx: Context) -> t.List[str]: + rv: t.Set[str] = set() + + for source in self.sources: + rv.update(source.list_commands(ctx)) + + return sorted(rv) + + +def _check_iter(value: t.Any) -> t.Iterator[t.Any]: + """Check if the value is iterable but not a string. Raises a type + error, or return an iterator over the value. + """ + if isinstance(value, str): + raise TypeError + + return iter(value) + + +class Parameter: + r"""A parameter to a command comes in two versions: they are either + :class:`Option`\s or :class:`Argument`\s. Other subclasses are currently + not supported by design as some of the internals for parsing are + intentionally not finalized. + + Some settings are supported by both options and arguments. + + :param param_decls: the parameter declarations for this option or + argument. This is a list of flags or argument + names. + :param type: the type that should be used. Either a :class:`ParamType` + or a Python type. The latter is converted into the former + automatically if supported. + :param required: controls if this is optional or not. + :param default: the default value if omitted. This can also be a callable, + in which case it's invoked when the default is needed + without any arguments. + :param callback: A function to further process or validate the value + after type conversion. It is called as ``f(ctx, param, value)`` + and must return the value. It is called for all sources, + including prompts. + :param nargs: the number of arguments to match. If not ``1`` the return + value is a tuple instead of single value. The default for + nargs is ``1`` (except if the type is a tuple, then it's + the arity of the tuple). If ``nargs=-1``, all remaining + parameters are collected. + :param metavar: how the value is represented in the help page. + :param expose_value: if this is `True` then the value is passed onwards + to the command callback and stored on the context, + otherwise it's skipped. + :param is_eager: eager values are processed before non eager ones. This + should not be set for arguments or it will inverse the + order of processing. + :param envvar: a string or list of strings that are environment variables + that should be checked. + :param shell_complete: A function that returns custom shell + completions. Used instead of the param's type completion if + given. Takes ``ctx, param, incomplete`` and must return a list + of :class:`~click.shell_completion.CompletionItem` or a list of + strings. + + .. versionchanged:: 8.0 + ``process_value`` validates required parameters and bounded + ``nargs``, and invokes the parameter callback before returning + the value. This allows the callback to validate prompts. + ``full_process_value`` is removed. + + .. versionchanged:: 8.0 + ``autocompletion`` is renamed to ``shell_complete`` and has new + semantics described above. The old name is deprecated and will + be removed in 8.1, until then it will be wrapped to match the + new requirements. + + .. versionchanged:: 8.0 + For ``multiple=True, nargs>1``, the default must be a list of + tuples. + + .. versionchanged:: 8.0 + Setting a default is no longer required for ``nargs>1``, it will + default to ``None``. ``multiple=True`` or ``nargs=-1`` will + default to ``()``. + + .. versionchanged:: 7.1 + Empty environment variables are ignored rather than taking the + empty string value. This makes it possible for scripts to clear + variables if they can't unset them. + + .. versionchanged:: 2.0 + Changed signature for parameter callback to also be passed the + parameter. The old callback format will still work, but it will + raise a warning to give you a chance to migrate the code easier. + """ + + param_type_name = "parameter" + + def __init__( + self, + param_decls: t.Optional[t.Sequence[str]] = None, + type: t.Optional[t.Union[types.ParamType, t.Any]] = None, + required: bool = False, + default: t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]] = None, + callback: t.Optional[t.Callable[[Context, "Parameter", t.Any], t.Any]] = None, + nargs: t.Optional[int] = None, + multiple: bool = False, + metavar: t.Optional[str] = None, + expose_value: bool = True, + is_eager: bool = False, + envvar: t.Optional[t.Union[str, t.Sequence[str]]] = None, + shell_complete: t.Optional[ + t.Callable[ + [Context, "Parameter", str], + t.Union[t.List["CompletionItem"], t.List[str]], + ] + ] = None, + ) -> None: + self.name: t.Optional[str] + self.opts: t.List[str] + self.secondary_opts: t.List[str] + self.name, self.opts, self.secondary_opts = self._parse_decls( + param_decls or (), expose_value + ) + self.type: types.ParamType = types.convert_type(type, default) + + # Default nargs to what the type tells us if we have that + # information available. + if nargs is None: + if self.type.is_composite: + nargs = self.type.arity + else: + nargs = 1 + + self.required = required + self.callback = callback + self.nargs = nargs + self.multiple = multiple + self.expose_value = expose_value + self.default = default + self.is_eager = is_eager + self.metavar = metavar + self.envvar = envvar + self._custom_shell_complete = shell_complete + + if __debug__: + if self.type.is_composite and nargs != self.type.arity: + raise ValueError( + f"'nargs' must be {self.type.arity} (or None) for" + f" type {self.type!r}, but it was {nargs}." + ) + + # Skip no default or callable default. + check_default = default if not callable(default) else None + + if check_default is not None: + if multiple: + try: + # Only check the first value against nargs. + check_default = next(_check_iter(check_default), None) + except TypeError: + raise ValueError( + "'default' must be a list when 'multiple' is true." + ) from None + + # Can be None for multiple with empty default. + if nargs != 1 and check_default is not None: + try: + _check_iter(check_default) + except TypeError: + if multiple: + message = ( + "'default' must be a list of lists when 'multiple' is" + " true and 'nargs' != 1." + ) + else: + message = "'default' must be a list when 'nargs' != 1." + + raise ValueError(message) from None + + if nargs > 1 and len(check_default) != nargs: + subject = "item length" if multiple else "length" + raise ValueError( + f"'default' {subject} must match nargs={nargs}." + ) + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + return { + "name": self.name, + "param_type_name": self.param_type_name, + "opts": self.opts, + "secondary_opts": self.secondary_opts, + "type": self.type.to_info_dict(), + "required": self.required, + "nargs": self.nargs, + "multiple": self.multiple, + "default": self.default, + "envvar": self.envvar, + } + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + raise NotImplementedError() + + @property + def human_readable_name(self) -> str: + """Returns the human readable name of this parameter. This is the + same as the name for options, but the metavar for arguments. + """ + return self.name # type: ignore + + def make_metavar(self) -> str: + if self.metavar is not None: + return self.metavar + + metavar = self.type.get_metavar(self) + + if metavar is None: + metavar = self.type.name.upper() + + if self.nargs != 1: + metavar += "..." + + return metavar + + @t.overload + def get_default( + self, ctx: Context, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: ... + + @t.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + """Get the default for the parameter. Tries + :meth:`Context.lookup_default` first, then the local default. + + :param ctx: Current context. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0.2 + Type casting is no longer performed when getting a default. + + .. versionchanged:: 8.0.1 + Type casting can fail in resilient parsing mode. Invalid + defaults will not prevent showing help text. + + .. versionchanged:: 8.0 + Looks at ``ctx.default_map`` first. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + value = ctx.lookup_default(self.name, call=False) # type: ignore + + if value is None: + value = self.default + + if call and callable(value): + value = value() + + return value + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + raise NotImplementedError() + + def consume_value( + self, ctx: Context, opts: t.Mapping[str, t.Any] + ) -> t.Tuple[t.Any, ParameterSource]: + value = opts.get(self.name) # type: ignore + source = ParameterSource.COMMANDLINE + + if value is None: + value = self.value_from_envvar(ctx) + source = ParameterSource.ENVIRONMENT + + if value is None: + value = ctx.lookup_default(self.name) # type: ignore + source = ParameterSource.DEFAULT_MAP + + if value is None: + value = self.get_default(ctx) + source = ParameterSource.DEFAULT + + return value, source + + def type_cast_value(self, ctx: Context, value: t.Any) -> t.Any: + """Convert and validate a value against the option's + :attr:`type`, :attr:`multiple`, and :attr:`nargs`. + """ + if value is None: + return () if self.multiple or self.nargs == -1 else None + + def check_iter(value: t.Any) -> t.Iterator[t.Any]: + try: + return _check_iter(value) + except TypeError: + # This should only happen when passing in args manually, + # the parser should construct an iterable when parsing + # the command line. + raise BadParameter( + _("Value must be an iterable."), ctx=ctx, param=self + ) from None + + if self.nargs == 1 or self.type.is_composite: + + def convert(value: t.Any) -> t.Any: + return self.type(value, param=self, ctx=ctx) + + elif self.nargs == -1: + + def convert(value: t.Any) -> t.Any: # t.Tuple[t.Any, ...] + return tuple(self.type(x, self, ctx) for x in check_iter(value)) + + else: # nargs > 1 + + def convert(value: t.Any) -> t.Any: # t.Tuple[t.Any, ...] + value = tuple(check_iter(value)) + + if len(value) != self.nargs: + raise BadParameter( + ngettext( + "Takes {nargs} values but 1 was given.", + "Takes {nargs} values but {len} were given.", + len(value), + ).format(nargs=self.nargs, len=len(value)), + ctx=ctx, + param=self, + ) + + return tuple(self.type(x, self, ctx) for x in value) + + if self.multiple: + return tuple(convert(x) for x in check_iter(value)) + + return convert(value) + + def value_is_missing(self, value: t.Any) -> bool: + if value is None: + return True + + if (self.nargs != 1 or self.multiple) and value == (): + return True + + return False + + def process_value(self, ctx: Context, value: t.Any) -> t.Any: + value = self.type_cast_value(ctx, value) + + if self.required and self.value_is_missing(value): + raise MissingParameter(ctx=ctx, param=self) + + if self.callback is not None: + value = self.callback(ctx, self, value) + + return value + + def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: + if self.envvar is None: + return None + + if isinstance(self.envvar, str): + rv = os.environ.get(self.envvar) + + if rv: + return rv + else: + for envvar in self.envvar: + rv = os.environ.get(envvar) + + if rv: + return rv + + return None + + def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: + rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) + + if rv is not None and self.nargs != 1: + rv = self.type.split_envvar_value(rv) + + return rv + + def handle_parse_result( + self, ctx: Context, opts: t.Mapping[str, t.Any], args: t.List[str] + ) -> t.Tuple[t.Any, t.List[str]]: + with augment_usage_errors(ctx, param=self): + value, source = self.consume_value(ctx, opts) + ctx.set_parameter_source(self.name, source) # type: ignore + + try: + value = self.process_value(ctx, value) + except Exception: + if not ctx.resilient_parsing: + raise + + value = None + + if self.expose_value: + ctx.params[self.name] = value # type: ignore + + return value, args + + def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: + pass + + def get_usage_pieces(self, ctx: Context) -> t.List[str]: + return [] + + def get_error_hint(self, ctx: Context) -> str: + """Get a stringified version of the param for use in error messages to + indicate which param caused the error. + """ + hint_list = self.opts or [self.human_readable_name] + return " / ".join(f"'{x}'" for x in hint_list) + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. If a + ``shell_complete`` function was given during init, it is used. + Otherwise, the :attr:`type` + :meth:`~click.types.ParamType.shell_complete` function is used. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + if self._custom_shell_complete is not None: + results = self._custom_shell_complete(ctx, self, incomplete) + + if results and isinstance(results[0], str): + from click.shell_completion import CompletionItem + + results = [CompletionItem(c) for c in results] + + return t.cast(t.List["CompletionItem"], results) + + return self.type.shell_complete(ctx, self, incomplete) + + +class Option(Parameter): + """Options are usually optional values on the command line and + have some extra features that arguments don't have. + + All other parameters are passed onwards to the parameter constructor. + + :param show_default: Show the default value for this option in its + help text. Values are not shown by default, unless + :attr:`Context.show_default` is ``True``. If this value is a + string, it shows that string in parentheses instead of the + actual value. This is particularly useful for dynamic options. + For single option boolean flags, the default remains hidden if + its value is ``False``. + :param show_envvar: Controls if an environment variable should be + shown on the help page. Normally, environment variables are not + shown. + :param prompt: If set to ``True`` or a non empty string then the + user will be prompted for input. If set to ``True`` the prompt + will be the option name capitalized. + :param confirmation_prompt: Prompt a second time to confirm the + value if it was prompted for. Can be set to a string instead of + ``True`` to customize the message. + :param prompt_required: If set to ``False``, the user will be + prompted for input only when the option was specified as a flag + without a value. + :param hide_input: If this is ``True`` then the input on the prompt + will be hidden from the user. This is useful for password input. + :param is_flag: forces this option to act as a flag. The default is + auto detection. + :param flag_value: which value should be used for this flag if it's + enabled. This is set to a boolean automatically if + the option string contains a slash to mark two options. + :param multiple: if this is set to `True` then the argument is accepted + multiple times and recorded. This is similar to ``nargs`` + in how it works but supports arbitrary number of + arguments. + :param count: this flag makes an option increment an integer. + :param allow_from_autoenv: if this is enabled then the value of this + parameter will be pulled from an environment + variable in case a prefix is defined on the + context. + :param help: the help string. + :param hidden: hide this option from help outputs. + :param attrs: Other command arguments described in :class:`Parameter`. + + .. versionchanged:: 8.1.0 + Help text indentation is cleaned here instead of only in the + ``@option`` decorator. + + .. versionchanged:: 8.1.0 + The ``show_default`` parameter overrides + ``Context.show_default``. + + .. versionchanged:: 8.1.0 + The default of a single option boolean flag is not shown if the + default value is ``False``. + + .. versionchanged:: 8.0.1 + ``type`` is detected from ``flag_value`` if given. + """ + + param_type_name = "option" + + def __init__( + self, + param_decls: t.Optional[t.Sequence[str]] = None, + show_default: t.Union[bool, str, None] = None, + prompt: t.Union[bool, str] = False, + confirmation_prompt: t.Union[bool, str] = False, + prompt_required: bool = True, + hide_input: bool = False, + is_flag: t.Optional[bool] = None, + flag_value: t.Optional[t.Any] = None, + multiple: bool = False, + count: bool = False, + allow_from_autoenv: bool = True, + type: t.Optional[t.Union[types.ParamType, t.Any]] = None, + help: t.Optional[str] = None, + hidden: bool = False, + show_choices: bool = True, + show_envvar: bool = False, + **attrs: t.Any, + ) -> None: + if help: + help = inspect.cleandoc(help) + + default_is_missing = "default" not in attrs + super().__init__(param_decls, type=type, multiple=multiple, **attrs) + + if prompt is True: + if self.name is None: + raise TypeError("'name' is required with 'prompt=True'.") + + prompt_text: t.Optional[str] = self.name.replace("_", " ").capitalize() + elif prompt is False: + prompt_text = None + else: + prompt_text = prompt + + self.prompt = prompt_text + self.confirmation_prompt = confirmation_prompt + self.prompt_required = prompt_required + self.hide_input = hide_input + self.hidden = hidden + + # If prompt is enabled but not required, then the option can be + # used as a flag to indicate using prompt or flag_value. + self._flag_needs_value = self.prompt is not None and not self.prompt_required + + if is_flag is None: + if flag_value is not None: + # Implicitly a flag because flag_value was set. + is_flag = True + elif self._flag_needs_value: + # Not a flag, but when used as a flag it shows a prompt. + is_flag = False + else: + # Implicitly a flag because flag options were given. + is_flag = bool(self.secondary_opts) + elif is_flag is False and not self._flag_needs_value: + # Not a flag, and prompt is not enabled, can be used as a + # flag if flag_value is set. + self._flag_needs_value = flag_value is not None + + self.default: t.Union[t.Any, t.Callable[[], t.Any]] + + if is_flag and default_is_missing and not self.required: + if multiple: + self.default = () + else: + self.default = False + + if flag_value is None: + flag_value = not self.default + + self.type: types.ParamType + if is_flag and type is None: + # Re-guess the type from the flag value instead of the + # default. + self.type = types.convert_type(None, flag_value) + + self.is_flag: bool = is_flag + self.is_bool_flag: bool = is_flag and isinstance(self.type, types.BoolParamType) + self.flag_value: t.Any = flag_value + + # Counting + self.count = count + if count: + if type is None: + self.type = types.IntRange(min=0) + if default_is_missing: + self.default = 0 + + self.allow_from_autoenv = allow_from_autoenv + self.help = help + self.show_default = show_default + self.show_choices = show_choices + self.show_envvar = show_envvar + + if __debug__: + if self.nargs == -1: + raise TypeError("nargs=-1 is not supported for options.") + + if self.prompt and self.is_flag and not self.is_bool_flag: + raise TypeError("'prompt' is not valid for non-boolean flag.") + + if not self.is_bool_flag and self.secondary_opts: + raise TypeError("Secondary flag is not valid for non-boolean flag.") + + if self.is_bool_flag and self.hide_input and self.prompt is not None: + raise TypeError( + "'prompt' with 'hide_input' is not valid for boolean flag." + ) + + if self.count: + if self.multiple: + raise TypeError("'count' is not valid with 'multiple'.") + + if self.is_flag: + raise TypeError("'count' is not valid with 'is_flag'.") + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + help=self.help, + prompt=self.prompt, + is_flag=self.is_flag, + flag_value=self.flag_value, + count=self.count, + hidden=self.hidden, + ) + return info_dict + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + opts = [] + secondary_opts = [] + name = None + possible_names = [] + + for decl in decls: + if decl.isidentifier(): + if name is not None: + raise TypeError(f"Name '{name}' defined twice") + name = decl + else: + split_char = ";" if decl[:1] == "/" else "/" + if split_char in decl: + first, second = decl.split(split_char, 1) + first = first.rstrip() + if first: + possible_names.append(split_opt(first)) + opts.append(first) + second = second.lstrip() + if second: + secondary_opts.append(second.lstrip()) + if first == second: + raise ValueError( + f"Boolean option {decl!r} cannot use the" + " same flag for true/false." + ) + else: + possible_names.append(split_opt(decl)) + opts.append(decl) + + if name is None and possible_names: + possible_names.sort(key=lambda x: -len(x[0])) # group long options first + name = possible_names[0][1].replace("-", "_").lower() + if not name.isidentifier(): + name = None + + if name is None: + if not expose_value: + return None, opts, secondary_opts + raise TypeError( + f"Could not determine name for option with declarations {decls!r}" + ) + + if not opts and not secondary_opts: + raise TypeError( + f"No options defined but a name was passed ({name})." + " Did you mean to declare an argument instead? Did" + f" you mean to pass '--{name}'?" + ) + + return name, opts, secondary_opts + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + if self.multiple: + action = "append" + elif self.count: + action = "count" + else: + action = "store" + + if self.is_flag: + action = f"{action}_const" + + if self.is_bool_flag and self.secondary_opts: + parser.add_option( + obj=self, opts=self.opts, dest=self.name, action=action, const=True + ) + parser.add_option( + obj=self, + opts=self.secondary_opts, + dest=self.name, + action=action, + const=False, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + const=self.flag_value, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + nargs=self.nargs, + ) + + def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: + if self.hidden: + return None + + any_prefix_is_slash = False + + def _write_opts(opts: t.Sequence[str]) -> str: + nonlocal any_prefix_is_slash + + rv, any_slashes = join_options(opts) + + if any_slashes: + any_prefix_is_slash = True + + if not self.is_flag and not self.count: + rv += f" {self.make_metavar()}" + + return rv + + rv = [_write_opts(self.opts)] + + if self.secondary_opts: + rv.append(_write_opts(self.secondary_opts)) + + help = self.help or "" + extra = [] + + if self.show_envvar: + envvar = self.envvar + + if envvar is None: + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + + if envvar is not None: + var_str = ( + envvar + if isinstance(envvar, str) + else ", ".join(str(d) for d in envvar) + ) + extra.append(_("env var: {var}").format(var=var_str)) + + # Temporarily enable resilient parsing to avoid type casting + # failing for the default. Might be possible to extend this to + # help formatting in general. + resilient = ctx.resilient_parsing + ctx.resilient_parsing = True + + try: + default_value = self.get_default(ctx, call=False) + finally: + ctx.resilient_parsing = resilient + + show_default = False + show_default_is_str = False + + if self.show_default is not None: + if isinstance(self.show_default, str): + show_default_is_str = show_default = True + else: + show_default = self.show_default + elif ctx.show_default is not None: + show_default = ctx.show_default + + if show_default_is_str or (show_default and (default_value is not None)): + if show_default_is_str: + default_string = f"({self.show_default})" + elif isinstance(default_value, (list, tuple)): + default_string = ", ".join(str(d) for d in default_value) + elif inspect.isfunction(default_value): + default_string = _("(dynamic)") + elif self.is_bool_flag and self.secondary_opts: + # For boolean flags that have distinct True/False opts, + # use the opt without prefix instead of the value. + default_string = split_opt( + (self.opts if default_value else self.secondary_opts)[0] + )[1] + elif self.is_bool_flag and not self.secondary_opts and not default_value: + default_string = "" + elif default_value == "": + default_string = '""' + else: + default_string = str(default_value) + + if default_string: + extra.append(_("default: {default}").format(default=default_string)) + + if ( + isinstance(self.type, types._NumberRangeBase) + # skip count with default range type + and not (self.count and self.type.min == 0 and self.type.max is None) + ): + range_str = self.type._describe_range() + + if range_str: + extra.append(range_str) + + if self.required: + extra.append(_("required")) + + if extra: + extra_str = "; ".join(extra) + help = f"{help} [{extra_str}]" if help else f"[{extra_str}]" + + return ("; " if any_prefix_is_slash else " / ").join(rv), help + + @t.overload + def get_default( + self, ctx: Context, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: ... + + @t.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + # If we're a non boolean flag our default is more complex because + # we need to look at all flags in the same group to figure out + # if we're the default one in which case we return the flag + # value as default. + if self.is_flag and not self.is_bool_flag: + for param in ctx.command.params: + if param.name == self.name and param.default: + return t.cast(Option, param).flag_value + + return None + + return super().get_default(ctx, call=call) + + def prompt_for_value(self, ctx: Context) -> t.Any: + """This is an alternative flow that can be activated in the full + value processing if a value does not exist. It will prompt the + user until a valid value exists and then returns the processed + value as result. + """ + assert self.prompt is not None + + # Calculate the default before prompting anything to be stable. + default = self.get_default(ctx) + + # If this is a prompt for a flag we need to handle this + # differently. + if self.is_bool_flag: + return confirm(self.prompt, default) + + return prompt( + self.prompt, + default=default, + type=self.type, + hide_input=self.hide_input, + show_choices=self.show_choices, + confirmation_prompt=self.confirmation_prompt, + value_proc=lambda x: self.process_value(ctx, x), + ) + + def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: + rv = super().resolve_envvar_value(ctx) + + if rv is not None: + return rv + + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + rv = os.environ.get(envvar) + + if rv: + return rv + + return None + + def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: + rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) + + if rv is None: + return None + + value_depth = (self.nargs != 1) + bool(self.multiple) + + if value_depth > 0: + rv = self.type.split_envvar_value(rv) + + if self.multiple and self.nargs != 1: + rv = batch(rv, self.nargs) + + return rv + + def consume_value( + self, ctx: Context, opts: t.Mapping[str, "Parameter"] + ) -> t.Tuple[t.Any, ParameterSource]: + value, source = super().consume_value(ctx, opts) + + # The parser will emit a sentinel value if the option can be + # given as a flag without a value. This is different from None + # to distinguish from the flag not being given at all. + if value is _flag_needs_value: + if self.prompt is not None and not ctx.resilient_parsing: + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + else: + value = self.flag_value + source = ParameterSource.COMMANDLINE + + elif ( + self.multiple + and value is not None + and any(v is _flag_needs_value for v in value) + ): + value = [self.flag_value if v is _flag_needs_value else v for v in value] + source = ParameterSource.COMMANDLINE + + # The value wasn't set, or used the param's default, prompt if + # prompting is enabled. + elif ( + source in {None, ParameterSource.DEFAULT} + and self.prompt is not None + and (self.required or self.prompt_required) + and not ctx.resilient_parsing + ): + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + + return value, source + + +class Argument(Parameter): + """Arguments are positional parameters to a command. They generally + provide fewer features than options but can have infinite ``nargs`` + and are required by default. + + All parameters are passed onwards to the constructor of :class:`Parameter`. + """ + + param_type_name = "argument" + + def __init__( + self, + param_decls: t.Sequence[str], + required: t.Optional[bool] = None, + **attrs: t.Any, + ) -> None: + if required is None: + if attrs.get("default") is not None: + required = False + else: + required = attrs.get("nargs", 1) > 0 + + if "multiple" in attrs: + raise TypeError("__init__() got an unexpected keyword argument 'multiple'.") + + super().__init__(param_decls, required=required, **attrs) + + if __debug__: + if self.default is not None and self.nargs == -1: + raise TypeError("'default' is not supported for nargs=-1.") + + @property + def human_readable_name(self) -> str: + if self.metavar is not None: + return self.metavar + return self.name.upper() # type: ignore + + def make_metavar(self) -> str: + if self.metavar is not None: + return self.metavar + var = self.type.get_metavar(self) + if not var: + var = self.name.upper() # type: ignore + if not self.required: + var = f"[{var}]" + if self.nargs != 1: + var += "..." + return var + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + if not decls: + if not expose_value: + return None, [], [] + raise TypeError("Argument is marked as exposed, but does not have a name.") + if len(decls) == 1: + name = arg = decls[0] + name = name.replace("-", "_").lower() + else: + raise TypeError( + "Arguments take exactly one parameter declaration, got" + f" {len(decls)}." + ) + return name, [arg], [] + + def get_usage_pieces(self, ctx: Context) -> t.List[str]: + return [self.make_metavar()] + + def get_error_hint(self, ctx: Context) -> str: + return f"'{self.make_metavar()}'" + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) diff --git a/.venv/lib/python3.9/site-packages/click/decorators.py b/.venv/lib/python3.9/site-packages/click/decorators.py new file mode 100644 index 0000000..bcf8906 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/decorators.py @@ -0,0 +1,562 @@ +import inspect +import types +import typing as t +from functools import update_wrapper +from gettext import gettext as _ + +from .core import Argument +from .core import Command +from .core import Context +from .core import Group +from .core import Option +from .core import Parameter +from .globals import get_current_context +from .utils import echo + +if t.TYPE_CHECKING: + import typing_extensions as te + + P = te.ParamSpec("P") + +R = t.TypeVar("R") +T = t.TypeVar("T") +_AnyCallable = t.Callable[..., t.Any] +FC = t.TypeVar("FC", bound=t.Union[_AnyCallable, Command]) + + +def pass_context(f: "t.Callable[te.Concatenate[Context, P], R]") -> "t.Callable[P, R]": + """Marks a callback as wanting to receive the current context + object as first argument. + """ + + def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": + return f(get_current_context(), *args, **kwargs) + + return update_wrapper(new_func, f) + + +def pass_obj(f: "t.Callable[te.Concatenate[t.Any, P], R]") -> "t.Callable[P, R]": + """Similar to :func:`pass_context`, but only pass the object on the + context onwards (:attr:`Context.obj`). This is useful if that object + represents the state of a nested system. + """ + + def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": + return f(get_current_context().obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + +def make_pass_decorator( + object_type: t.Type[T], ensure: bool = False +) -> t.Callable[["t.Callable[te.Concatenate[T, P], R]"], "t.Callable[P, R]"]: + """Given an object type this creates a decorator that will work + similar to :func:`pass_obj` but instead of passing the object of the + current context, it will find the innermost context of type + :func:`object_type`. + + This generates a decorator that works roughly like this:: + + from functools import update_wrapper + + def decorator(f): + @pass_context + def new_func(ctx, *args, **kwargs): + obj = ctx.find_object(object_type) + return ctx.invoke(f, obj, *args, **kwargs) + return update_wrapper(new_func, f) + return decorator + + :param object_type: the type of the object to pass. + :param ensure: if set to `True`, a new object will be created and + remembered on the context if it's not there yet. + """ + + def decorator(f: "t.Callable[te.Concatenate[T, P], R]") -> "t.Callable[P, R]": + def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": + ctx = get_current_context() + + obj: t.Optional[T] + if ensure: + obj = ctx.ensure_object(object_type) + else: + obj = ctx.find_object(object_type) + + if obj is None: + raise RuntimeError( + "Managed to invoke callback without a context" + f" object of type {object_type.__name__!r}" + " existing." + ) + + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + return decorator + + +def pass_meta_key( + key: str, *, doc_description: t.Optional[str] = None +) -> "t.Callable[[t.Callable[te.Concatenate[t.Any, P], R]], t.Callable[P, R]]": + """Create a decorator that passes a key from + :attr:`click.Context.meta` as the first argument to the decorated + function. + + :param key: Key in ``Context.meta`` to pass. + :param doc_description: Description of the object being passed, + inserted into the decorator's docstring. Defaults to "the 'key' + key from Context.meta". + + .. versionadded:: 8.0 + """ + + def decorator(f: "t.Callable[te.Concatenate[t.Any, P], R]") -> "t.Callable[P, R]": + def new_func(*args: "P.args", **kwargs: "P.kwargs") -> R: + ctx = get_current_context() + obj = ctx.meta[key] + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + if doc_description is None: + doc_description = f"the {key!r} key from :attr:`click.Context.meta`" + + decorator.__doc__ = ( + f"Decorator that passes {doc_description} as the first argument" + " to the decorated function." + ) + return decorator + + +CmdType = t.TypeVar("CmdType", bound=Command) + + +# variant: no call, directly as decorator for a function. +@t.overload +def command(name: _AnyCallable) -> Command: ... + + +# variant: with positional name and with positional or keyword cls argument: +# @command(namearg, CommandCls, ...) or @command(namearg, cls=CommandCls, ...) +@t.overload +def command( + name: t.Optional[str], + cls: t.Type[CmdType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], CmdType]: ... + + +# variant: name omitted, cls _must_ be a keyword argument, @command(cls=CommandCls, ...) +@t.overload +def command( + name: None = None, + *, + cls: t.Type[CmdType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], CmdType]: ... + + +# variant: with optional string name, no cls argument provided. +@t.overload +def command( + name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any +) -> t.Callable[[_AnyCallable], Command]: ... + + +def command( + name: t.Union[t.Optional[str], _AnyCallable] = None, + cls: t.Optional[t.Type[CmdType]] = None, + **attrs: t.Any, +) -> t.Union[Command, t.Callable[[_AnyCallable], t.Union[Command, CmdType]]]: + r"""Creates a new :class:`Command` and uses the decorated function as + callback. This will also automatically attach all decorated + :func:`option`\s and :func:`argument`\s as parameters to the command. + + The name of the command defaults to the name of the function with + underscores replaced by dashes. If you want to change that, you can + pass the intended name as the first argument. + + All keyword arguments are forwarded to the underlying command class. + For the ``params`` argument, any decorated params are appended to + the end of the list. + + Once decorated the function turns into a :class:`Command` instance + that can be invoked as a command line utility or be attached to a + command :class:`Group`. + + :param name: the name of the command. This defaults to the function + name with underscores replaced by dashes. + :param cls: the command class to instantiate. This defaults to + :class:`Command`. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.1 + The ``params`` argument can be used. Decorated params are + appended to the end of the list. + """ + + func: t.Optional[t.Callable[[_AnyCallable], t.Any]] = None + + if callable(name): + func = name + name = None + assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class." + assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments." + + if cls is None: + cls = t.cast(t.Type[CmdType], Command) + + def decorator(f: _AnyCallable) -> CmdType: + if isinstance(f, Command): + raise TypeError("Attempted to convert a callback into a command twice.") + + attr_params = attrs.pop("params", None) + params = attr_params if attr_params is not None else [] + + try: + decorator_params = f.__click_params__ # type: ignore + except AttributeError: + pass + else: + del f.__click_params__ # type: ignore + params.extend(reversed(decorator_params)) + + if attrs.get("help") is None: + attrs["help"] = f.__doc__ + + if t.TYPE_CHECKING: + assert cls is not None + assert not callable(name) + + cmd = cls( + name=name or f.__name__.lower().replace("_", "-"), + callback=f, + params=params, + **attrs, + ) + cmd.__doc__ = f.__doc__ + return cmd + + if func is not None: + return decorator(func) + + return decorator + + +GrpType = t.TypeVar("GrpType", bound=Group) + + +# variant: no call, directly as decorator for a function. +@t.overload +def group(name: _AnyCallable) -> Group: ... + + +# variant: with positional name and with positional or keyword cls argument: +# @group(namearg, GroupCls, ...) or @group(namearg, cls=GroupCls, ...) +@t.overload +def group( + name: t.Optional[str], + cls: t.Type[GrpType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], GrpType]: ... + + +# variant: name omitted, cls _must_ be a keyword argument, @group(cmd=GroupCls, ...) +@t.overload +def group( + name: None = None, + *, + cls: t.Type[GrpType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], GrpType]: ... + + +# variant: with optional string name, no cls argument provided. +@t.overload +def group( + name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any +) -> t.Callable[[_AnyCallable], Group]: ... + + +def group( + name: t.Union[str, _AnyCallable, None] = None, + cls: t.Optional[t.Type[GrpType]] = None, + **attrs: t.Any, +) -> t.Union[Group, t.Callable[[_AnyCallable], t.Union[Group, GrpType]]]: + """Creates a new :class:`Group` with a function as callback. This + works otherwise the same as :func:`command` just that the `cls` + parameter is set to :class:`Group`. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + """ + if cls is None: + cls = t.cast(t.Type[GrpType], Group) + + if callable(name): + return command(cls=cls, **attrs)(name) + + return command(name, cls, **attrs) + + +def _param_memo(f: t.Callable[..., t.Any], param: Parameter) -> None: + if isinstance(f, Command): + f.params.append(param) + else: + if not hasattr(f, "__click_params__"): + f.__click_params__ = [] # type: ignore + + f.__click_params__.append(param) # type: ignore + + +def argument( + *param_decls: str, cls: t.Optional[t.Type[Argument]] = None, **attrs: t.Any +) -> t.Callable[[FC], FC]: + """Attaches an argument to the command. All positional arguments are + passed as parameter declarations to :class:`Argument`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Argument` instance manually + and attaching it to the :attr:`Command.params` list. + + For the default argument class, refer to :class:`Argument` and + :class:`Parameter` for descriptions of parameters. + + :param cls: the argument class to instantiate. This defaults to + :class:`Argument`. + :param param_decls: Passed as positional arguments to the constructor of + ``cls``. + :param attrs: Passed as keyword arguments to the constructor of ``cls``. + """ + if cls is None: + cls = Argument + + def decorator(f: FC) -> FC: + _param_memo(f, cls(param_decls, **attrs)) + return f + + return decorator + + +def option( + *param_decls: str, cls: t.Optional[t.Type[Option]] = None, **attrs: t.Any +) -> t.Callable[[FC], FC]: + """Attaches an option to the command. All positional arguments are + passed as parameter declarations to :class:`Option`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Option` instance manually + and attaching it to the :attr:`Command.params` list. + + For the default option class, refer to :class:`Option` and + :class:`Parameter` for descriptions of parameters. + + :param cls: the option class to instantiate. This defaults to + :class:`Option`. + :param param_decls: Passed as positional arguments to the constructor of + ``cls``. + :param attrs: Passed as keyword arguments to the constructor of ``cls``. + """ + if cls is None: + cls = Option + + def decorator(f: FC) -> FC: + _param_memo(f, cls(param_decls, **attrs)) + return f + + return decorator + + +def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--yes`` option which shows a prompt before continuing if + not passed. If the prompt is declined, the program will exit. + + :param param_decls: One or more option names. Defaults to the single + value ``"--yes"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value: + ctx.abort() + + if not param_decls: + param_decls = ("--yes",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("callback", callback) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("prompt", "Do you want to continue?") + kwargs.setdefault("help", "Confirm the action without prompting.") + return option(*param_decls, **kwargs) + + +def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--password`` option which prompts for a password, hiding + input and asking to enter the value again for confirmation. + + :param param_decls: One or more option names. Defaults to the single + value ``"--password"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + if not param_decls: + param_decls = ("--password",) + + kwargs.setdefault("prompt", True) + kwargs.setdefault("confirmation_prompt", True) + kwargs.setdefault("hide_input", True) + return option(*param_decls, **kwargs) + + +def version_option( + version: t.Optional[str] = None, + *param_decls: str, + package_name: t.Optional[str] = None, + prog_name: t.Optional[str] = None, + message: t.Optional[str] = None, + **kwargs: t.Any, +) -> t.Callable[[FC], FC]: + """Add a ``--version`` option which immediately prints the version + number and exits the program. + + If ``version`` is not provided, Click will try to detect it using + :func:`importlib.metadata.version` to get the version for the + ``package_name``. On Python < 3.8, the ``importlib_metadata`` + backport must be installed. + + If ``package_name`` is not provided, Click will try to detect it by + inspecting the stack frames. This will be used to detect the + version, so it must match the name of the installed package. + + :param version: The version number to show. If not provided, Click + will try to detect it. + :param param_decls: One or more option names. Defaults to the single + value ``"--version"``. + :param package_name: The package name to detect the version from. If + not provided, Click will try to detect it. + :param prog_name: The name of the CLI to show in the message. If not + provided, it will be detected from the command. + :param message: The message to show. The values ``%(prog)s``, + ``%(package)s``, and ``%(version)s`` are available. Defaults to + ``"%(prog)s, version %(version)s"``. + :param kwargs: Extra arguments are passed to :func:`option`. + :raise RuntimeError: ``version`` could not be detected. + + .. versionchanged:: 8.0 + Add the ``package_name`` parameter, and the ``%(package)s`` + value for messages. + + .. versionchanged:: 8.0 + Use :mod:`importlib.metadata` instead of ``pkg_resources``. The + version is detected based on the package name, not the entry + point name. The Python package name must match the installed + package name, or be passed with ``package_name=``. + """ + if message is None: + message = _("%(prog)s, version %(version)s") + + if version is None and package_name is None: + frame = inspect.currentframe() + f_back = frame.f_back if frame is not None else None + f_globals = f_back.f_globals if f_back is not None else None + # break reference cycle + # https://docs.python.org/3/library/inspect.html#the-interpreter-stack + del frame + + if f_globals is not None: + package_name = f_globals.get("__name__") + + if package_name == "__main__": + package_name = f_globals.get("__package__") + + if package_name: + package_name = package_name.partition(".")[0] + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value or ctx.resilient_parsing: + return + + nonlocal prog_name + nonlocal version + + if prog_name is None: + prog_name = ctx.find_root().info_name + + if version is None and package_name is not None: + metadata: t.Optional[types.ModuleType] + + try: + from importlib import metadata + except ImportError: + # Python < 3.8 + import importlib_metadata as metadata # type: ignore + + try: + version = metadata.version(package_name) # type: ignore + except metadata.PackageNotFoundError: # type: ignore + raise RuntimeError( + f"{package_name!r} is not installed. Try passing" + " 'package_name' instead." + ) from None + + if version is None: + raise RuntimeError( + f"Could not determine the version for {package_name!r} automatically." + ) + + echo( + message % {"prog": prog_name, "package": package_name, "version": version}, + color=ctx.color, + ) + ctx.exit() + + if not param_decls: + param_decls = ("--version",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show the version and exit.")) + kwargs["callback"] = callback + return option(*param_decls, **kwargs) + + +class HelpOption(Option): + """Pre-configured ``--help`` option which immediately prints the help page + and exits the program. + """ + + def __init__( + self, + param_decls: t.Optional[t.Sequence[str]] = None, + **kwargs: t.Any, + ) -> None: + if not param_decls: + param_decls = ("--help",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show this message and exit.")) + kwargs.setdefault("callback", self.show_help) + + super().__init__(param_decls, **kwargs) + + @staticmethod + def show_help(ctx: Context, param: Parameter, value: bool) -> None: + """Callback that print the help page on ```` and exits.""" + if value and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + +def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Decorator for the pre-configured ``--help`` option defined above. + + :param param_decls: One or more option names. Defaults to the single + value ``"--help"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + kwargs.setdefault("cls", HelpOption) + return option(*param_decls, **kwargs) diff --git a/.venv/lib/python3.9/site-packages/click/exceptions.py b/.venv/lib/python3.9/site-packages/click/exceptions.py new file mode 100644 index 0000000..0b83151 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/exceptions.py @@ -0,0 +1,296 @@ +import typing as t +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import get_text_stderr +from .globals import resolve_color_default +from .utils import echo +from .utils import format_filename + +if t.TYPE_CHECKING: + from .core import Command + from .core import Context + from .core import Parameter + + +def _join_param_hints( + param_hint: t.Optional[t.Union[t.Sequence[str], str]], +) -> t.Optional[str]: + if param_hint is not None and not isinstance(param_hint, str): + return " / ".join(repr(x) for x in param_hint) + + return param_hint + + +class ClickException(Exception): + """An exception that Click can handle and show to the user.""" + + #: The exit code for this exception. + exit_code = 1 + + def __init__(self, message: str) -> None: + super().__init__(message) + # The context will be removed by the time we print the message, so cache + # the color settings here to be used later on (in `show`) + self.show_color: t.Optional[bool] = resolve_color_default() + self.message = message + + def format_message(self) -> str: + return self.message + + def __str__(self) -> str: + return self.message + + def show(self, file: t.Optional[t.IO[t.Any]] = None) -> None: + if file is None: + file = get_text_stderr() + + echo( + _("Error: {message}").format(message=self.format_message()), + file=file, + color=self.show_color, + ) + + +class UsageError(ClickException): + """An internal exception that signals a usage error. This typically + aborts any further handling. + + :param message: the error message to display. + :param ctx: optionally the context that caused this error. Click will + fill in the context automatically in some situations. + """ + + exit_code = 2 + + def __init__(self, message: str, ctx: t.Optional["Context"] = None) -> None: + super().__init__(message) + self.ctx = ctx + self.cmd: t.Optional[Command] = self.ctx.command if self.ctx else None + + def show(self, file: t.Optional[t.IO[t.Any]] = None) -> None: + if file is None: + file = get_text_stderr() + color = None + hint = "" + if ( + self.ctx is not None + and self.ctx.command.get_help_option(self.ctx) is not None + ): + hint = _("Try '{command} {option}' for help.").format( + command=self.ctx.command_path, option=self.ctx.help_option_names[0] + ) + hint = f"{hint}\n" + if self.ctx is not None: + color = self.ctx.color + echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color) + echo( + _("Error: {message}").format(message=self.format_message()), + file=file, + color=color, + ) + + +class BadParameter(UsageError): + """An exception that formats out a standardized error message for a + bad parameter. This is useful when thrown from a callback or type as + Click will attach contextual information to it (for instance, which + parameter it is). + + .. versionadded:: 2.0 + + :param param: the parameter object that caused this error. This can + be left out, and Click will attach this info itself + if possible. + :param param_hint: a string that shows up as parameter name. This + can be used as alternative to `param` in cases + where custom validation should happen. If it is + a string it's used as such, if it's a list then + each item is quoted and separated. + """ + + def __init__( + self, + message: str, + ctx: t.Optional["Context"] = None, + param: t.Optional["Parameter"] = None, + param_hint: t.Optional[str] = None, + ) -> None: + super().__init__(message, ctx) + self.param = param + self.param_hint = param_hint + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + return _("Invalid value: {message}").format(message=self.message) + + return _("Invalid value for {param_hint}: {message}").format( + param_hint=_join_param_hints(param_hint), message=self.message + ) + + +class MissingParameter(BadParameter): + """Raised if click required an option or argument but it was not + provided when invoking the script. + + .. versionadded:: 4.0 + + :param param_type: a string that indicates the type of the parameter. + The default is to inherit the parameter type from + the given `param`. Valid values are ``'parameter'``, + ``'option'`` or ``'argument'``. + """ + + def __init__( + self, + message: t.Optional[str] = None, + ctx: t.Optional["Context"] = None, + param: t.Optional["Parameter"] = None, + param_hint: t.Optional[str] = None, + param_type: t.Optional[str] = None, + ) -> None: + super().__init__(message or "", ctx, param, param_hint) + self.param_type = param_type + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint: t.Optional[str] = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + param_hint = None + + param_hint = _join_param_hints(param_hint) + param_hint = f" {param_hint}" if param_hint else "" + + param_type = self.param_type + if param_type is None and self.param is not None: + param_type = self.param.param_type_name + + msg = self.message + if self.param is not None: + msg_extra = self.param.type.get_missing_message(self.param) + if msg_extra: + if msg: + msg += f". {msg_extra}" + else: + msg = msg_extra + + msg = f" {msg}" if msg else "" + + # Translate param_type for known types. + if param_type == "argument": + missing = _("Missing argument") + elif param_type == "option": + missing = _("Missing option") + elif param_type == "parameter": + missing = _("Missing parameter") + else: + missing = _("Missing {param_type}").format(param_type=param_type) + + return f"{missing}{param_hint}.{msg}" + + def __str__(self) -> str: + if not self.message: + param_name = self.param.name if self.param else None + return _("Missing parameter: {param_name}").format(param_name=param_name) + else: + return self.message + + +class NoSuchOption(UsageError): + """Raised if click attempted to handle an option that does not + exist. + + .. versionadded:: 4.0 + """ + + def __init__( + self, + option_name: str, + message: t.Optional[str] = None, + possibilities: t.Optional[t.Sequence[str]] = None, + ctx: t.Optional["Context"] = None, + ) -> None: + if message is None: + message = _("No such option: {name}").format(name=option_name) + + super().__init__(message, ctx) + self.option_name = option_name + self.possibilities = possibilities + + def format_message(self) -> str: + if not self.possibilities: + return self.message + + possibility_str = ", ".join(sorted(self.possibilities)) + suggest = ngettext( + "Did you mean {possibility}?", + "(Possible options: {possibilities})", + len(self.possibilities), + ).format(possibility=possibility_str, possibilities=possibility_str) + return f"{self.message} {suggest}" + + +class BadOptionUsage(UsageError): + """Raised if an option is generally supplied but the use of the option + was incorrect. This is for instance raised if the number of arguments + for an option is not correct. + + .. versionadded:: 4.0 + + :param option_name: the name of the option being used incorrectly. + """ + + def __init__( + self, option_name: str, message: str, ctx: t.Optional["Context"] = None + ) -> None: + super().__init__(message, ctx) + self.option_name = option_name + + +class BadArgumentUsage(UsageError): + """Raised if an argument is generally supplied but the use of the argument + was incorrect. This is for instance raised if the number of values + for an argument is not correct. + + .. versionadded:: 6.0 + """ + + +class FileError(ClickException): + """Raised if a file cannot be opened.""" + + def __init__(self, filename: str, hint: t.Optional[str] = None) -> None: + if hint is None: + hint = _("unknown error") + + super().__init__(hint) + self.ui_filename: str = format_filename(filename) + self.filename = filename + + def format_message(self) -> str: + return _("Could not open file {filename!r}: {message}").format( + filename=self.ui_filename, message=self.message + ) + + +class Abort(RuntimeError): + """An internal signalling exception that signals Click to abort.""" + + +class Exit(RuntimeError): + """An exception that indicates that the application should exit with some + status code. + + :param code: the status code to exit with. + """ + + __slots__ = ("exit_code",) + + def __init__(self, code: int = 0) -> None: + self.exit_code: int = code diff --git a/.venv/lib/python3.9/site-packages/click/formatting.py b/.venv/lib/python3.9/site-packages/click/formatting.py new file mode 100644 index 0000000..ddd2a2f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/formatting.py @@ -0,0 +1,301 @@ +import typing as t +from contextlib import contextmanager +from gettext import gettext as _ + +from ._compat import term_len +from .parser import split_opt + +# Can force a width. This is used by the test system +FORCED_WIDTH: t.Optional[int] = None + + +def measure_table(rows: t.Iterable[t.Tuple[str, str]]) -> t.Tuple[int, ...]: + widths: t.Dict[int, int] = {} + + for row in rows: + for idx, col in enumerate(row): + widths[idx] = max(widths.get(idx, 0), term_len(col)) + + return tuple(y for x, y in sorted(widths.items())) + + +def iter_rows( + rows: t.Iterable[t.Tuple[str, str]], col_count: int +) -> t.Iterator[t.Tuple[str, ...]]: + for row in rows: + yield row + ("",) * (col_count - len(row)) + + +def wrap_text( + text: str, + width: int = 78, + initial_indent: str = "", + subsequent_indent: str = "", + preserve_paragraphs: bool = False, +) -> str: + """A helper function that intelligently wraps text. By default, it + assumes that it operates on a single paragraph of text but if the + `preserve_paragraphs` parameter is provided it will intelligently + handle paragraphs (defined by two empty lines). + + If paragraphs are handled, a paragraph can be prefixed with an empty + line containing the ``\\b`` character (``\\x08``) to indicate that + no rewrapping should happen in that block. + + :param text: the text that should be rewrapped. + :param width: the maximum width for the text. + :param initial_indent: the initial indent that should be placed on the + first line as a string. + :param subsequent_indent: the indent string that should be placed on + each consecutive line. + :param preserve_paragraphs: if this flag is set then the wrapping will + intelligently handle paragraphs. + """ + from ._textwrap import TextWrapper + + text = text.expandtabs() + wrapper = TextWrapper( + width, + initial_indent=initial_indent, + subsequent_indent=subsequent_indent, + replace_whitespace=False, + ) + if not preserve_paragraphs: + return wrapper.fill(text) + + p: t.List[t.Tuple[int, bool, str]] = [] + buf: t.List[str] = [] + indent = None + + def _flush_par() -> None: + if not buf: + return + if buf[0].strip() == "\b": + p.append((indent or 0, True, "\n".join(buf[1:]))) + else: + p.append((indent or 0, False, " ".join(buf))) + del buf[:] + + for line in text.splitlines(): + if not line: + _flush_par() + indent = None + else: + if indent is None: + orig_len = term_len(line) + line = line.lstrip() + indent = orig_len - term_len(line) + buf.append(line) + _flush_par() + + rv = [] + for indent, raw, text in p: + with wrapper.extra_indent(" " * indent): + if raw: + rv.append(wrapper.indent_only(text)) + else: + rv.append(wrapper.fill(text)) + + return "\n\n".join(rv) + + +class HelpFormatter: + """This class helps with formatting text-based help pages. It's + usually just needed for very special internal cases, but it's also + exposed so that developers can write their own fancy outputs. + + At present, it always writes into memory. + + :param indent_increment: the additional increment for each level. + :param width: the width for the text. This defaults to the terminal + width clamped to a maximum of 78. + """ + + def __init__( + self, + indent_increment: int = 2, + width: t.Optional[int] = None, + max_width: t.Optional[int] = None, + ) -> None: + import shutil + + self.indent_increment = indent_increment + if max_width is None: + max_width = 80 + if width is None: + width = FORCED_WIDTH + if width is None: + width = max(min(shutil.get_terminal_size().columns, max_width) - 2, 50) + self.width = width + self.current_indent = 0 + self.buffer: t.List[str] = [] + + def write(self, string: str) -> None: + """Writes a unicode string into the internal buffer.""" + self.buffer.append(string) + + def indent(self) -> None: + """Increases the indentation.""" + self.current_indent += self.indent_increment + + def dedent(self) -> None: + """Decreases the indentation.""" + self.current_indent -= self.indent_increment + + def write_usage( + self, prog: str, args: str = "", prefix: t.Optional[str] = None + ) -> None: + """Writes a usage line into the buffer. + + :param prog: the program name. + :param args: whitespace separated list of arguments. + :param prefix: The prefix for the first line. Defaults to + ``"Usage: "``. + """ + if prefix is None: + prefix = f"{_('Usage:')} " + + usage_prefix = f"{prefix:>{self.current_indent}}{prog} " + text_width = self.width - self.current_indent + + if text_width >= (term_len(usage_prefix) + 20): + # The arguments will fit to the right of the prefix. + indent = " " * term_len(usage_prefix) + self.write( + wrap_text( + args, + text_width, + initial_indent=usage_prefix, + subsequent_indent=indent, + ) + ) + else: + # The prefix is too long, put the arguments on the next line. + self.write(usage_prefix) + self.write("\n") + indent = " " * (max(self.current_indent, term_len(prefix)) + 4) + self.write( + wrap_text( + args, text_width, initial_indent=indent, subsequent_indent=indent + ) + ) + + self.write("\n") + + def write_heading(self, heading: str) -> None: + """Writes a heading into the buffer.""" + self.write(f"{'':>{self.current_indent}}{heading}:\n") + + def write_paragraph(self) -> None: + """Writes a paragraph into the buffer.""" + if self.buffer: + self.write("\n") + + def write_text(self, text: str) -> None: + """Writes re-indented text into the buffer. This rewraps and + preserves paragraphs. + """ + indent = " " * self.current_indent + self.write( + wrap_text( + text, + self.width, + initial_indent=indent, + subsequent_indent=indent, + preserve_paragraphs=True, + ) + ) + self.write("\n") + + def write_dl( + self, + rows: t.Sequence[t.Tuple[str, str]], + col_max: int = 30, + col_spacing: int = 2, + ) -> None: + """Writes a definition list into the buffer. This is how options + and commands are usually formatted. + + :param rows: a list of two item tuples for the terms and values. + :param col_max: the maximum width of the first column. + :param col_spacing: the number of spaces between the first and + second column. + """ + rows = list(rows) + widths = measure_table(rows) + if len(widths) != 2: + raise TypeError("Expected two columns for definition list") + + first_col = min(widths[0], col_max) + col_spacing + + for first, second in iter_rows(rows, len(widths)): + self.write(f"{'':>{self.current_indent}}{first}") + if not second: + self.write("\n") + continue + if term_len(first) <= first_col - col_spacing: + self.write(" " * (first_col - term_len(first))) + else: + self.write("\n") + self.write(" " * (first_col + self.current_indent)) + + text_width = max(self.width - first_col - 2, 10) + wrapped_text = wrap_text(second, text_width, preserve_paragraphs=True) + lines = wrapped_text.splitlines() + + if lines: + self.write(f"{lines[0]}\n") + + for line in lines[1:]: + self.write(f"{'':>{first_col + self.current_indent}}{line}\n") + else: + self.write("\n") + + @contextmanager + def section(self, name: str) -> t.Iterator[None]: + """Helpful context manager that writes a paragraph, a heading, + and the indents. + + :param name: the section name that is written as heading. + """ + self.write_paragraph() + self.write_heading(name) + self.indent() + try: + yield + finally: + self.dedent() + + @contextmanager + def indentation(self) -> t.Iterator[None]: + """A context manager that increases the indentation.""" + self.indent() + try: + yield + finally: + self.dedent() + + def getvalue(self) -> str: + """Returns the buffer contents.""" + return "".join(self.buffer) + + +def join_options(options: t.Sequence[str]) -> t.Tuple[str, bool]: + """Given a list of option strings this joins them in the most appropriate + way and returns them in the form ``(formatted_string, + any_prefix_is_slash)`` where the second item in the tuple is a flag that + indicates if any of the option prefixes was a slash. + """ + rv = [] + any_prefix_is_slash = False + + for opt in options: + prefix = split_opt(opt)[0] + + if prefix == "/": + any_prefix_is_slash = True + + rv.append((len(prefix), opt)) + + rv.sort(key=lambda x: x[0]) + return ", ".join(x[1] for x in rv), any_prefix_is_slash diff --git a/.venv/lib/python3.9/site-packages/click/globals.py b/.venv/lib/python3.9/site-packages/click/globals.py new file mode 100644 index 0000000..191e712 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/globals.py @@ -0,0 +1,67 @@ +import typing as t +from threading import local + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .core import Context + +_local = local() + + +@t.overload +def get_current_context(silent: "te.Literal[False]" = False) -> "Context": ... + + +@t.overload +def get_current_context(silent: bool = ...) -> t.Optional["Context"]: ... + + +def get_current_context(silent: bool = False) -> t.Optional["Context"]: + """Returns the current click context. This can be used as a way to + access the current context object from anywhere. This is a more implicit + alternative to the :func:`pass_context` decorator. This function is + primarily useful for helpers such as :func:`echo` which might be + interested in changing its behavior based on the current context. + + To push the current context, :meth:`Context.scope` can be used. + + .. versionadded:: 5.0 + + :param silent: if set to `True` the return value is `None` if no context + is available. The default behavior is to raise a + :exc:`RuntimeError`. + """ + try: + return t.cast("Context", _local.stack[-1]) + except (AttributeError, IndexError) as e: + if not silent: + raise RuntimeError("There is no active click context.") from e + + return None + + +def push_context(ctx: "Context") -> None: + """Pushes a new context to the current stack.""" + _local.__dict__.setdefault("stack", []).append(ctx) + + +def pop_context() -> None: + """Removes the top level from the stack.""" + _local.stack.pop() + + +def resolve_color_default(color: t.Optional[bool] = None) -> t.Optional[bool]: + """Internal helper to get the default value of the color flag. If a + value is passed it's returned unchanged, otherwise it's looked up from + the current context. + """ + if color is not None: + return color + + ctx = get_current_context(silent=True) + + if ctx is not None: + return ctx.color + + return None diff --git a/.venv/lib/python3.9/site-packages/click/parser.py b/.venv/lib/python3.9/site-packages/click/parser.py new file mode 100644 index 0000000..600b843 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/parser.py @@ -0,0 +1,531 @@ +""" +This module started out as largely a copy paste from the stdlib's +optparse module with the features removed that we do not need from +optparse because we implement them in Click on a higher level (for +instance type handling, help formatting and a lot more). + +The plan is to remove more and more from here over time. + +The reason this is a different module and not optparse from the stdlib +is that there are differences in 2.x and 3.x about the error messages +generated and optparse in the stdlib uses gettext for no good reason +and might cause us issues. + +Click uses parts of optparse written by Gregory P. Ward and maintained +by the Python Software Foundation. This is limited to code in parser.py. + +Copyright 2001-2006 Gregory P. Ward. All rights reserved. +Copyright 2002-2006 Python Software Foundation. All rights reserved. +""" + +# This code uses parts of optparse written by Gregory P. Ward and +# maintained by the Python Software Foundation. +# Copyright 2001-2006 Gregory P. Ward +# Copyright 2002-2006 Python Software Foundation +import typing as t +from collections import deque +from gettext import gettext as _ +from gettext import ngettext + +from .exceptions import BadArgumentUsage +from .exceptions import BadOptionUsage +from .exceptions import NoSuchOption +from .exceptions import UsageError + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .core import Argument as CoreArgument + from .core import Context + from .core import Option as CoreOption + from .core import Parameter as CoreParameter + +V = t.TypeVar("V") + +# Sentinel value that indicates an option was passed as a flag without a +# value but is not a flag option. Option.consume_value uses this to +# prompt or use the flag_value. +_flag_needs_value = object() + + +def _unpack_args( + args: t.Sequence[str], nargs_spec: t.Sequence[int] +) -> t.Tuple[t.Sequence[t.Union[str, t.Sequence[t.Optional[str]], None]], t.List[str]]: + """Given an iterable of arguments and an iterable of nargs specifications, + it returns a tuple with all the unpacked arguments at the first index + and all remaining arguments as the second. + + The nargs specification is the number of arguments that should be consumed + or `-1` to indicate that this position should eat up all the remainders. + + Missing items are filled with `None`. + """ + args = deque(args) + nargs_spec = deque(nargs_spec) + rv: t.List[t.Union[str, t.Tuple[t.Optional[str], ...], None]] = [] + spos: t.Optional[int] = None + + def _fetch(c: "te.Deque[V]") -> t.Optional[V]: + try: + if spos is None: + return c.popleft() + else: + return c.pop() + except IndexError: + return None + + while nargs_spec: + nargs = _fetch(nargs_spec) + + if nargs is None: + continue + + if nargs == 1: + rv.append(_fetch(args)) + elif nargs > 1: + x = [_fetch(args) for _ in range(nargs)] + + # If we're reversed, we're pulling in the arguments in reverse, + # so we need to turn them around. + if spos is not None: + x.reverse() + + rv.append(tuple(x)) + elif nargs < 0: + if spos is not None: + raise TypeError("Cannot have two nargs < 0") + + spos = len(rv) + rv.append(None) + + # spos is the position of the wildcard (star). If it's not `None`, + # we fill it with the remainder. + if spos is not None: + rv[spos] = tuple(args) + args = [] + rv[spos + 1 :] = reversed(rv[spos + 1 :]) + + return tuple(rv), list(args) + + +def split_opt(opt: str) -> t.Tuple[str, str]: + first = opt[:1] + if first.isalnum(): + return "", opt + if opt[1:2] == first: + return opt[:2], opt[2:] + return first, opt[1:] + + +def normalize_opt(opt: str, ctx: t.Optional["Context"]) -> str: + if ctx is None or ctx.token_normalize_func is None: + return opt + prefix, opt = split_opt(opt) + return f"{prefix}{ctx.token_normalize_func(opt)}" + + +def split_arg_string(string: str) -> t.List[str]: + """Split an argument string as with :func:`shlex.split`, but don't + fail if the string is incomplete. Ignores a missing closing quote or + incomplete escape sequence and uses the partial token as-is. + + .. code-block:: python + + split_arg_string("example 'my file") + ["example", "my file"] + + split_arg_string("example my\\") + ["example", "my"] + + :param string: String to split. + """ + import shlex + + lex = shlex.shlex(string, posix=True) + lex.whitespace_split = True + lex.commenters = "" + out = [] + + try: + for token in lex: + out.append(token) + except ValueError: + # Raised when end-of-string is reached in an invalid state. Use + # the partial token as-is. The quote or escape character is in + # lex.state, not lex.token. + out.append(lex.token) + + return out + + +class Option: + def __init__( + self, + obj: "CoreOption", + opts: t.Sequence[str], + dest: t.Optional[str], + action: t.Optional[str] = None, + nargs: int = 1, + const: t.Optional[t.Any] = None, + ): + self._short_opts = [] + self._long_opts = [] + self.prefixes: t.Set[str] = set() + + for opt in opts: + prefix, value = split_opt(opt) + if not prefix: + raise ValueError(f"Invalid start character for option ({opt})") + self.prefixes.add(prefix[0]) + if len(prefix) == 1 and len(value) == 1: + self._short_opts.append(opt) + else: + self._long_opts.append(opt) + self.prefixes.add(prefix) + + if action is None: + action = "store" + + self.dest = dest + self.action = action + self.nargs = nargs + self.const = const + self.obj = obj + + @property + def takes_value(self) -> bool: + return self.action in ("store", "append") + + def process(self, value: t.Any, state: "ParsingState") -> None: + if self.action == "store": + state.opts[self.dest] = value # type: ignore + elif self.action == "store_const": + state.opts[self.dest] = self.const # type: ignore + elif self.action == "append": + state.opts.setdefault(self.dest, []).append(value) # type: ignore + elif self.action == "append_const": + state.opts.setdefault(self.dest, []).append(self.const) # type: ignore + elif self.action == "count": + state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 # type: ignore + else: + raise ValueError(f"unknown action '{self.action}'") + state.order.append(self.obj) + + +class Argument: + def __init__(self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1): + self.dest = dest + self.nargs = nargs + self.obj = obj + + def process( + self, + value: t.Union[t.Optional[str], t.Sequence[t.Optional[str]]], + state: "ParsingState", + ) -> None: + if self.nargs > 1: + assert value is not None + holes = sum(1 for x in value if x is None) + if holes == len(value): + value = None + elif holes != 0: + raise BadArgumentUsage( + _("Argument {name!r} takes {nargs} values.").format( + name=self.dest, nargs=self.nargs + ) + ) + + if self.nargs == -1 and self.obj.envvar is not None and value == (): + # Replace empty tuple with None so that a value from the + # environment may be tried. + value = None + + state.opts[self.dest] = value # type: ignore + state.order.append(self.obj) + + +class ParsingState: + def __init__(self, rargs: t.List[str]) -> None: + self.opts: t.Dict[str, t.Any] = {} + self.largs: t.List[str] = [] + self.rargs = rargs + self.order: t.List[CoreParameter] = [] + + +class OptionParser: + """The option parser is an internal class that is ultimately used to + parse options and arguments. It's modelled after optparse and brings + a similar but vastly simplified API. It should generally not be used + directly as the high level Click classes wrap it for you. + + It's not nearly as extensible as optparse or argparse as it does not + implement features that are implemented on a higher level (such as + types or defaults). + + :param ctx: optionally the :class:`~click.Context` where this parser + should go with. + """ + + def __init__(self, ctx: t.Optional["Context"] = None) -> None: + #: The :class:`~click.Context` for this parser. This might be + #: `None` for some advanced use cases. + self.ctx = ctx + #: This controls how the parser deals with interspersed arguments. + #: If this is set to `False`, the parser will stop on the first + #: non-option. Click uses this to implement nested subcommands + #: safely. + self.allow_interspersed_args: bool = True + #: This tells the parser how to deal with unknown options. By + #: default it will error out (which is sensible), but there is a + #: second mode where it will ignore it and continue processing + #: after shifting all the unknown options into the resulting args. + self.ignore_unknown_options: bool = False + + if ctx is not None: + self.allow_interspersed_args = ctx.allow_interspersed_args + self.ignore_unknown_options = ctx.ignore_unknown_options + + self._short_opt: t.Dict[str, Option] = {} + self._long_opt: t.Dict[str, Option] = {} + self._opt_prefixes = {"-", "--"} + self._args: t.List[Argument] = [] + + def add_option( + self, + obj: "CoreOption", + opts: t.Sequence[str], + dest: t.Optional[str], + action: t.Optional[str] = None, + nargs: int = 1, + const: t.Optional[t.Any] = None, + ) -> None: + """Adds a new option named `dest` to the parser. The destination + is not inferred (unlike with optparse) and needs to be explicitly + provided. Action can be any of ``store``, ``store_const``, + ``append``, ``append_const`` or ``count``. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + opts = [normalize_opt(opt, self.ctx) for opt in opts] + option = Option(obj, opts, dest, action=action, nargs=nargs, const=const) + self._opt_prefixes.update(option.prefixes) + for opt in option._short_opts: + self._short_opt[opt] = option + for opt in option._long_opts: + self._long_opt[opt] = option + + def add_argument( + self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1 + ) -> None: + """Adds a positional argument named `dest` to the parser. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + self._args.append(Argument(obj, dest=dest, nargs=nargs)) + + def parse_args( + self, args: t.List[str] + ) -> t.Tuple[t.Dict[str, t.Any], t.List[str], t.List["CoreParameter"]]: + """Parses positional arguments and returns ``(values, args, order)`` + for the parsed options and arguments as well as the leftover + arguments if there are any. The order is a list of objects as they + appear on the command line. If arguments appear multiple times they + will be memorized multiple times as well. + """ + state = ParsingState(args) + try: + self._process_args_for_options(state) + self._process_args_for_args(state) + except UsageError: + if self.ctx is None or not self.ctx.resilient_parsing: + raise + return state.opts, state.largs, state.order + + def _process_args_for_args(self, state: ParsingState) -> None: + pargs, args = _unpack_args( + state.largs + state.rargs, [x.nargs for x in self._args] + ) + + for idx, arg in enumerate(self._args): + arg.process(pargs[idx], state) + + state.largs = args + state.rargs = [] + + def _process_args_for_options(self, state: ParsingState) -> None: + while state.rargs: + arg = state.rargs.pop(0) + arglen = len(arg) + # Double dashes always handled explicitly regardless of what + # prefixes are valid. + if arg == "--": + return + elif arg[:1] in self._opt_prefixes and arglen > 1: + self._process_opts(arg, state) + elif self.allow_interspersed_args: + state.largs.append(arg) + else: + state.rargs.insert(0, arg) + return + + # Say this is the original argument list: + # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] + # ^ + # (we are about to process arg(i)). + # + # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of + # [arg0, ..., arg(i-1)] (any options and their arguments will have + # been removed from largs). + # + # The while loop will usually consume 1 or more arguments per pass. + # If it consumes 1 (eg. arg is an option that takes no arguments), + # then after _process_arg() is done the situation is: + # + # largs = subset of [arg0, ..., arg(i)] + # rargs = [arg(i+1), ..., arg(N-1)] + # + # If allow_interspersed_args is false, largs will always be + # *empty* -- still a subset of [arg0, ..., arg(i-1)], but + # not a very interesting subset! + + def _match_long_opt( + self, opt: str, explicit_value: t.Optional[str], state: ParsingState + ) -> None: + if opt not in self._long_opt: + from difflib import get_close_matches + + possibilities = get_close_matches(opt, self._long_opt) + raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx) + + option = self._long_opt[opt] + if option.takes_value: + # At this point it's safe to modify rargs by injecting the + # explicit value, because no exception is raised in this + # branch. This means that the inserted value will be fully + # consumed. + if explicit_value is not None: + state.rargs.insert(0, explicit_value) + + value = self._get_value_from_state(opt, option, state) + + elif explicit_value is not None: + raise BadOptionUsage( + opt, _("Option {name!r} does not take a value.").format(name=opt) + ) + + else: + value = None + + option.process(value, state) + + def _match_short_opt(self, arg: str, state: ParsingState) -> None: + stop = False + i = 1 + prefix = arg[0] + unknown_options = [] + + for ch in arg[1:]: + opt = normalize_opt(f"{prefix}{ch}", self.ctx) + option = self._short_opt.get(opt) + i += 1 + + if not option: + if self.ignore_unknown_options: + unknown_options.append(ch) + continue + raise NoSuchOption(opt, ctx=self.ctx) + if option.takes_value: + # Any characters left in arg? Pretend they're the + # next arg, and stop consuming characters of arg. + if i < len(arg): + state.rargs.insert(0, arg[i:]) + stop = True + + value = self._get_value_from_state(opt, option, state) + + else: + value = None + + option.process(value, state) + + if stop: + break + + # If we got any unknown options we recombine the string of the + # remaining options and re-attach the prefix, then report that + # to the state as new larg. This way there is basic combinatorics + # that can be achieved while still ignoring unknown arguments. + if self.ignore_unknown_options and unknown_options: + state.largs.append(f"{prefix}{''.join(unknown_options)}") + + def _get_value_from_state( + self, option_name: str, option: Option, state: ParsingState + ) -> t.Any: + nargs = option.nargs + + if len(state.rargs) < nargs: + if option.obj._flag_needs_value: + # Option allows omitting the value. + value = _flag_needs_value + else: + raise BadOptionUsage( + option_name, + ngettext( + "Option {name!r} requires an argument.", + "Option {name!r} requires {nargs} arguments.", + nargs, + ).format(name=option_name, nargs=nargs), + ) + elif nargs == 1: + next_rarg = state.rargs[0] + + if ( + option.obj._flag_needs_value + and isinstance(next_rarg, str) + and next_rarg[:1] in self._opt_prefixes + and len(next_rarg) > 1 + ): + # The next arg looks like the start of an option, don't + # use it as the value if omitting the value is allowed. + value = _flag_needs_value + else: + value = state.rargs.pop(0) + else: + value = tuple(state.rargs[:nargs]) + del state.rargs[:nargs] + + return value + + def _process_opts(self, arg: str, state: ParsingState) -> None: + explicit_value = None + # Long option handling happens in two parts. The first part is + # supporting explicitly attached values. In any case, we will try + # to long match the option first. + if "=" in arg: + long_opt, explicit_value = arg.split("=", 1) + else: + long_opt = arg + norm_long_opt = normalize_opt(long_opt, self.ctx) + + # At this point we will match the (assumed) long option through + # the long option matching code. Note that this allows options + # like "-foo" to be matched as long options. + try: + self._match_long_opt(norm_long_opt, explicit_value, state) + except NoSuchOption: + # At this point the long option matching failed, and we need + # to try with short options. However there is a special rule + # which says, that if we have a two character options prefix + # (applies to "--foo" for instance), we do not dispatch to the + # short option code and will instead raise the no option + # error. + if arg[:2] not in self._opt_prefixes: + self._match_short_opt(arg, state) + return + + if not self.ignore_unknown_options: + raise + + state.largs.append(arg) diff --git a/.venv/lib/python3.9/site-packages/click/py.typed b/.venv/lib/python3.9/site-packages/click/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.9/site-packages/click/shell_completion.py b/.venv/lib/python3.9/site-packages/click/shell_completion.py new file mode 100644 index 0000000..07d0f09 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/shell_completion.py @@ -0,0 +1,603 @@ +import os +import re +import typing as t +from gettext import gettext as _ + +from .core import Argument +from .core import BaseCommand +from .core import Context +from .core import MultiCommand +from .core import Option +from .core import Parameter +from .core import ParameterSource +from .parser import split_arg_string +from .utils import echo + + +def shell_complete( + cli: BaseCommand, + ctx_args: t.MutableMapping[str, t.Any], + prog_name: str, + complete_var: str, + instruction: str, +) -> int: + """Perform shell completion for the given CLI program. + + :param cli: Command being called. + :param ctx_args: Extra arguments to pass to + ``cli.make_context``. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + :param instruction: Value of ``complete_var`` with the completion + instruction and shell, in the form ``instruction_shell``. + :return: Status code to exit with. + """ + shell, _, instruction = instruction.partition("_") + comp_cls = get_completion_class(shell) + + if comp_cls is None: + return 1 + + comp = comp_cls(cli, ctx_args, prog_name, complete_var) + + if instruction == "source": + echo(comp.source()) + return 0 + + if instruction == "complete": + echo(comp.complete()) + return 0 + + return 1 + + +class CompletionItem: + """Represents a completion value and metadata about the value. The + default metadata is ``type`` to indicate special shell handling, + and ``help`` if a shell supports showing a help string next to the + value. + + Arbitrary parameters can be passed when creating the object, and + accessed using ``item.attr``. If an attribute wasn't passed, + accessing it returns ``None``. + + :param value: The completion suggestion. + :param type: Tells the shell script to provide special completion + support for the type. Click uses ``"dir"`` and ``"file"``. + :param help: String shown next to the value if supported. + :param kwargs: Arbitrary metadata. The built-in implementations + don't use this, but custom type completions paired with custom + shell support could use it. + """ + + __slots__ = ("value", "type", "help", "_info") + + def __init__( + self, + value: t.Any, + type: str = "plain", + help: t.Optional[str] = None, + **kwargs: t.Any, + ) -> None: + self.value: t.Any = value + self.type: str = type + self.help: t.Optional[str] = help + self._info = kwargs + + def __getattr__(self, name: str) -> t.Any: + return self._info.get(name) + + +# Only Bash >= 4.4 has the nosort option. +_SOURCE_BASH = """\ +%(complete_func)s() { + local IFS=$'\\n' + local response + + response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \ +%(complete_var)s=bash_complete $1) + + for completion in $response; do + IFS=',' read type value <<< "$completion" + + if [[ $type == 'dir' ]]; then + COMPREPLY=() + compopt -o dirnames + elif [[ $type == 'file' ]]; then + COMPREPLY=() + compopt -o default + elif [[ $type == 'plain' ]]; then + COMPREPLY+=($value) + fi + done + + return 0 +} + +%(complete_func)s_setup() { + complete -o nosort -F %(complete_func)s %(prog_name)s +} + +%(complete_func)s_setup; +""" + +_SOURCE_ZSH = """\ +#compdef %(prog_name)s + +%(complete_func)s() { + local -a completions + local -a completions_with_descriptions + local -a response + (( ! $+commands[%(prog_name)s] )) && return 1 + + response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) \ +%(complete_var)s=zsh_complete %(prog_name)s)}") + + for type key descr in ${response}; do + if [[ "$type" == "plain" ]]; then + if [[ "$descr" == "_" ]]; then + completions+=("$key") + else + completions_with_descriptions+=("$key":"$descr") + fi + elif [[ "$type" == "dir" ]]; then + _path_files -/ + elif [[ "$type" == "file" ]]; then + _path_files -f + fi + done + + if [ -n "$completions_with_descriptions" ]; then + _describe -V unsorted completions_with_descriptions -U + fi + + if [ -n "$completions" ]; then + compadd -U -V unsorted -a completions + fi +} + +if [[ $zsh_eval_context[-1] == loadautofunc ]]; then + # autoload from fpath, call function directly + %(complete_func)s "$@" +else + # eval/source/. command, register function for later + compdef %(complete_func)s %(prog_name)s +fi +""" + +_SOURCE_FISH = """\ +function %(complete_func)s; + set -l response (env %(complete_var)s=fish_complete COMP_WORDS=(commandline -cp) \ +COMP_CWORD=(commandline -t) %(prog_name)s); + + for completion in $response; + set -l metadata (string split "," $completion); + + if test $metadata[1] = "dir"; + __fish_complete_directories $metadata[2]; + else if test $metadata[1] = "file"; + __fish_complete_path $metadata[2]; + else if test $metadata[1] = "plain"; + echo $metadata[2]; + end; + end; +end; + +complete --no-files --command %(prog_name)s --arguments \ +"(%(complete_func)s)"; +""" + + +class ShellComplete: + """Base class for providing shell completion support. A subclass for + a given shell will override attributes and methods to implement the + completion instructions (``source`` and ``complete``). + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + + .. versionadded:: 8.0 + """ + + name: t.ClassVar[str] + """Name to register the shell as with :func:`add_completion_class`. + This is used in completion instructions (``{name}_source`` and + ``{name}_complete``). + """ + + source_template: t.ClassVar[str] + """Completion script template formatted by :meth:`source`. This must + be provided by subclasses. + """ + + def __init__( + self, + cli: BaseCommand, + ctx_args: t.MutableMapping[str, t.Any], + prog_name: str, + complete_var: str, + ) -> None: + self.cli = cli + self.ctx_args = ctx_args + self.prog_name = prog_name + self.complete_var = complete_var + + @property + def func_name(self) -> str: + """The name of the shell function defined by the completion + script. + """ + safe_name = re.sub(r"\W*", "", self.prog_name.replace("-", "_"), flags=re.ASCII) + return f"_{safe_name}_completion" + + def source_vars(self) -> t.Dict[str, t.Any]: + """Vars for formatting :attr:`source_template`. + + By default this provides ``complete_func``, ``complete_var``, + and ``prog_name``. + """ + return { + "complete_func": self.func_name, + "complete_var": self.complete_var, + "prog_name": self.prog_name, + } + + def source(self) -> str: + """Produce the shell script that defines the completion + function. By default this ``%``-style formats + :attr:`source_template` with the dict returned by + :meth:`source_vars`. + """ + return self.source_template % self.source_vars() + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + """Use the env vars defined by the shell script to return a + tuple of ``args, incomplete``. This must be implemented by + subclasses. + """ + raise NotImplementedError + + def get_completions( + self, args: t.List[str], incomplete: str + ) -> t.List[CompletionItem]: + """Determine the context and last complete command or parameter + from the complete args. Call that object's ``shell_complete`` + method to get the completions for the incomplete value. + + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + ctx = _resolve_context(self.cli, self.ctx_args, self.prog_name, args) + obj, incomplete = _resolve_incomplete(ctx, args, incomplete) + return obj.shell_complete(ctx, incomplete) + + def format_completion(self, item: CompletionItem) -> str: + """Format a completion item into the form recognized by the + shell script. This must be implemented by subclasses. + + :param item: Completion item to format. + """ + raise NotImplementedError + + def complete(self) -> str: + """Produce the completion data to send back to the shell. + + By default this calls :meth:`get_completion_args`, gets the + completions, then calls :meth:`format_completion` for each + completion. + """ + args, incomplete = self.get_completion_args() + completions = self.get_completions(args, incomplete) + out = [self.format_completion(item) for item in completions] + return "\n".join(out) + + +class BashComplete(ShellComplete): + """Shell completion for Bash.""" + + name = "bash" + source_template = _SOURCE_BASH + + @staticmethod + def _check_version() -> None: + import shutil + import subprocess + + bash_exe = shutil.which("bash") + + if bash_exe is None: + match = None + else: + output = subprocess.run( + [bash_exe, "--norc", "-c", 'echo "${BASH_VERSION}"'], + stdout=subprocess.PIPE, + ) + match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode()) + + if match is not None: + major, minor = match.groups() + + if major < "4" or major == "4" and minor < "4": + echo( + _( + "Shell completion is not supported for Bash" + " versions older than 4.4." + ), + err=True, + ) + else: + echo( + _("Couldn't detect Bash version, shell completion is not supported."), + err=True, + ) + + def source(self) -> str: + self._check_version() + return super().source() + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + return f"{item.type},{item.value}" + + +class ZshComplete(ShellComplete): + """Shell completion for Zsh.""" + + name = "zsh" + source_template = _SOURCE_ZSH + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + return f"{item.type}\n{item.value}\n{item.help if item.help else '_'}" + + +class FishComplete(ShellComplete): + """Shell completion for Fish.""" + + name = "fish" + source_template = _SOURCE_FISH + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + incomplete = os.environ["COMP_CWORD"] + args = cwords[1:] + + # Fish stores the partial word in both COMP_WORDS and + # COMP_CWORD, remove it from complete args. + if incomplete and args and args[-1] == incomplete: + args.pop() + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + if item.help: + return f"{item.type},{item.value}\t{item.help}" + + return f"{item.type},{item.value}" + + +ShellCompleteType = t.TypeVar("ShellCompleteType", bound=t.Type[ShellComplete]) + + +_available_shells: t.Dict[str, t.Type[ShellComplete]] = { + "bash": BashComplete, + "fish": FishComplete, + "zsh": ZshComplete, +} + + +def add_completion_class( + cls: ShellCompleteType, name: t.Optional[str] = None +) -> ShellCompleteType: + """Register a :class:`ShellComplete` subclass under the given name. + The name will be provided by the completion instruction environment + variable during completion. + + :param cls: The completion class that will handle completion for the + shell. + :param name: Name to register the class under. Defaults to the + class's ``name`` attribute. + """ + if name is None: + name = cls.name + + _available_shells[name] = cls + + return cls + + +def get_completion_class(shell: str) -> t.Optional[t.Type[ShellComplete]]: + """Look up a registered :class:`ShellComplete` subclass by the name + provided by the completion instruction environment variable. If the + name isn't registered, returns ``None``. + + :param shell: Name the class is registered under. + """ + return _available_shells.get(shell) + + +def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool: + """Determine if the given parameter is an argument that can still + accept values. + + :param ctx: Invocation context for the command represented by the + parsed complete args. + :param param: Argument object being checked. + """ + if not isinstance(param, Argument): + return False + + assert param.name is not None + # Will be None if expose_value is False. + value = ctx.params.get(param.name) + return ( + param.nargs == -1 + or ctx.get_parameter_source(param.name) is not ParameterSource.COMMANDLINE + or ( + param.nargs > 1 + and isinstance(value, (tuple, list)) + and len(value) < param.nargs + ) + ) + + +def _start_of_option(ctx: Context, value: str) -> bool: + """Check if the value looks like the start of an option.""" + if not value: + return False + + c = value[0] + return c in ctx._opt_prefixes + + +def _is_incomplete_option(ctx: Context, args: t.List[str], param: Parameter) -> bool: + """Determine if the given parameter is an option that needs a value. + + :param args: List of complete args before the incomplete value. + :param param: Option object being checked. + """ + if not isinstance(param, Option): + return False + + if param.is_flag or param.count: + return False + + last_option = None + + for index, arg in enumerate(reversed(args)): + if index + 1 > param.nargs: + break + + if _start_of_option(ctx, arg): + last_option = arg + + return last_option is not None and last_option in param.opts + + +def _resolve_context( + cli: BaseCommand, + ctx_args: t.MutableMapping[str, t.Any], + prog_name: str, + args: t.List[str], +) -> Context: + """Produce the context hierarchy starting with the command and + traversing the complete arguments. This only follows the commands, + it doesn't trigger input prompts or callbacks. + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param args: List of complete args before the incomplete value. + """ + ctx_args["resilient_parsing"] = True + ctx = cli.make_context(prog_name, args.copy(), **ctx_args) + args = ctx.protected_args + ctx.args + + while args: + command = ctx.command + + if isinstance(command, MultiCommand): + if not command.chain: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + ctx = cmd.make_context(name, args, parent=ctx, resilient_parsing=True) + args = ctx.protected_args + ctx.args + else: + sub_ctx = ctx + + while args: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + sub_ctx = cmd.make_context( + name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + resilient_parsing=True, + ) + args = sub_ctx.args + + ctx = sub_ctx + args = [*sub_ctx.protected_args, *sub_ctx.args] + else: + break + + return ctx + + +def _resolve_incomplete( + ctx: Context, args: t.List[str], incomplete: str +) -> t.Tuple[t.Union[BaseCommand, Parameter], str]: + """Find the Click object that will handle the completion of the + incomplete value. Return the object and the incomplete value. + + :param ctx: Invocation context for the command represented by + the parsed complete args. + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + # Different shells treat an "=" between a long option name and + # value differently. Might keep the value joined, return the "=" + # as a separate item, or return the split name and value. Always + # split and discard the "=" to make completion easier. + if incomplete == "=": + incomplete = "" + elif "=" in incomplete and _start_of_option(ctx, incomplete): + name, _, incomplete = incomplete.partition("=") + args.append(name) + + # The "--" marker tells Click to stop treating values as options + # even if they start with the option character. If it hasn't been + # given and the incomplete arg looks like an option, the current + # command will provide option name completions. + if "--" not in args and _start_of_option(ctx, incomplete): + return ctx.command, incomplete + + params = ctx.command.get_params(ctx) + + # If the last complete arg is an option name with an incomplete + # value, the option will provide value completions. + for param in params: + if _is_incomplete_option(ctx, args, param): + return param, incomplete + + # It's not an option name or value. The first argument without a + # parsed value will provide value completions. + for param in params: + if _is_incomplete_argument(ctx, param): + return param, incomplete + + # There were no unparsed arguments, the command may be a group that + # will provide command name completions. + return ctx.command, incomplete diff --git a/.venv/lib/python3.9/site-packages/click/termui.py b/.venv/lib/python3.9/site-packages/click/termui.py new file mode 100644 index 0000000..c084f19 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/termui.py @@ -0,0 +1,784 @@ +import inspect +import io +import itertools +import sys +import typing as t +from gettext import gettext as _ + +from ._compat import isatty +from ._compat import strip_ansi +from .exceptions import Abort +from .exceptions import UsageError +from .globals import resolve_color_default +from .types import Choice +from .types import convert_type +from .types import ParamType +from .utils import echo +from .utils import LazyFile + +if t.TYPE_CHECKING: + from ._termui_impl import ProgressBar + +V = t.TypeVar("V") + +# The prompt functions to use. The doc tools currently override these +# functions to customize how they work. +visible_prompt_func: t.Callable[[str], str] = input + +_ansi_colors = { + "black": 30, + "red": 31, + "green": 32, + "yellow": 33, + "blue": 34, + "magenta": 35, + "cyan": 36, + "white": 37, + "reset": 39, + "bright_black": 90, + "bright_red": 91, + "bright_green": 92, + "bright_yellow": 93, + "bright_blue": 94, + "bright_magenta": 95, + "bright_cyan": 96, + "bright_white": 97, +} +_ansi_reset_all = "\033[0m" + + +def hidden_prompt_func(prompt: str) -> str: + import getpass + + return getpass.getpass(prompt) + + +def _build_prompt( + text: str, + suffix: str, + show_default: bool = False, + default: t.Optional[t.Any] = None, + show_choices: bool = True, + type: t.Optional[ParamType] = None, +) -> str: + prompt = text + if type is not None and show_choices and isinstance(type, Choice): + prompt += f" ({', '.join(map(str, type.choices))})" + if default is not None and show_default: + prompt = f"{prompt} [{_format_default(default)}]" + return f"{prompt}{suffix}" + + +def _format_default(default: t.Any) -> t.Any: + if isinstance(default, (io.IOBase, LazyFile)) and hasattr(default, "name"): + return default.name + + return default + + +def prompt( + text: str, + default: t.Optional[t.Any] = None, + hide_input: bool = False, + confirmation_prompt: t.Union[bool, str] = False, + type: t.Optional[t.Union[ParamType, t.Any]] = None, + value_proc: t.Optional[t.Callable[[str], t.Any]] = None, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, + show_choices: bool = True, +) -> t.Any: + """Prompts a user for input. This is a convenience function that can + be used to prompt a user for input later. + + If the user aborts the input by sending an interrupt signal, this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the text to show for the prompt. + :param default: the default value to use if no input happens. If this + is not given it will prompt until it's aborted. + :param hide_input: if this is set to true then the input value will + be hidden. + :param confirmation_prompt: Prompt a second time to confirm the + value. Can be set to a string instead of ``True`` to customize + the message. + :param type: the type to use to check the value against. + :param value_proc: if this parameter is provided it's a function that + is invoked instead of the type conversion to + convert a value. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + :param show_choices: Show or hide choices if the passed type is a Choice. + For example if type is a Choice of either day or week, + show_choices is true and text is "Group by" then the + prompt will be "Group by (day, week): ". + + .. versionadded:: 8.0 + ``confirmation_prompt`` can be a custom string. + + .. versionadded:: 7.0 + Added the ``show_choices`` parameter. + + .. versionadded:: 6.0 + Added unicode support for cmd.exe on Windows. + + .. versionadded:: 4.0 + Added the `err` parameter. + + """ + + def prompt_func(text: str) -> str: + f = hidden_prompt_func if hide_input else visible_prompt_func + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(text.rstrip(" "), nl=False, err=err) + # Echo a space to stdout to work around an issue where + # readline causes backspace to clear the whole line. + return f(" ") + except (KeyboardInterrupt, EOFError): + # getpass doesn't print a newline if the user aborts input with ^C. + # Allegedly this behavior is inherited from getpass(3). + # A doc bug has been filed at https://bugs.python.org/issue24711 + if hide_input: + echo(None, err=err) + raise Abort() from None + + if value_proc is None: + value_proc = convert_type(type, default) + + prompt = _build_prompt( + text, prompt_suffix, show_default, default, show_choices, type + ) + + if confirmation_prompt: + if confirmation_prompt is True: + confirmation_prompt = _("Repeat for confirmation") + + confirmation_prompt = _build_prompt(confirmation_prompt, prompt_suffix) + + while True: + while True: + value = prompt_func(prompt) + if value: + break + elif default is not None: + value = default + break + try: + result = value_proc(value) + except UsageError as e: + if hide_input: + echo(_("Error: The value you entered was invalid."), err=err) + else: + echo(_("Error: {e.message}").format(e=e), err=err) + continue + if not confirmation_prompt: + return result + while True: + value2 = prompt_func(confirmation_prompt) + is_empty = not value and not value2 + if value2 or is_empty: + break + if value == value2: + return result + echo(_("Error: The two entered values do not match."), err=err) + + +def confirm( + text: str, + default: t.Optional[bool] = False, + abort: bool = False, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, +) -> bool: + """Prompts for confirmation (yes/no question). + + If the user aborts the input by sending a interrupt signal this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the question to ask. + :param default: The default value to use when no input is given. If + ``None``, repeat until input is given. + :param abort: if this is set to `True` a negative answer aborts the + exception by raising :exc:`Abort`. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + + .. versionchanged:: 8.0 + Repeat until input is given if ``default`` is ``None``. + + .. versionadded:: 4.0 + Added the ``err`` parameter. + """ + prompt = _build_prompt( + text, + prompt_suffix, + show_default, + "y/n" if default is None else ("Y/n" if default else "y/N"), + ) + + while True: + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(prompt.rstrip(" "), nl=False, err=err) + # Echo a space to stdout to work around an issue where + # readline causes backspace to clear the whole line. + value = visible_prompt_func(" ").lower().strip() + except (KeyboardInterrupt, EOFError): + raise Abort() from None + if value in ("y", "yes"): + rv = True + elif value in ("n", "no"): + rv = False + elif default is not None and value == "": + rv = default + else: + echo(_("Error: invalid input"), err=err) + continue + break + if abort and not rv: + raise Abort() + return rv + + +def echo_via_pager( + text_or_generator: t.Union[t.Iterable[str], t.Callable[[], t.Iterable[str]], str], + color: t.Optional[bool] = None, +) -> None: + """This function takes a text and shows it via an environment specific + pager on stdout. + + .. versionchanged:: 3.0 + Added the `color` flag. + + :param text_or_generator: the text to page, or alternatively, a + generator emitting the text to page. + :param color: controls if the pager supports ANSI colors or not. The + default is autodetection. + """ + color = resolve_color_default(color) + + if inspect.isgeneratorfunction(text_or_generator): + i = t.cast(t.Callable[[], t.Iterable[str]], text_or_generator)() + elif isinstance(text_or_generator, str): + i = [text_or_generator] + else: + i = iter(t.cast(t.Iterable[str], text_or_generator)) + + # convert every element of i to a text type if necessary + text_generator = (el if isinstance(el, str) else str(el) for el in i) + + from ._termui_impl import pager + + return pager(itertools.chain(text_generator, "\n"), color) + + +def progressbar( + iterable: t.Optional[t.Iterable[V]] = None, + length: t.Optional[int] = None, + label: t.Optional[str] = None, + show_eta: bool = True, + show_percent: t.Optional[bool] = None, + show_pos: bool = False, + item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.Optional[t.TextIO] = None, + color: t.Optional[bool] = None, + update_min_steps: int = 1, +) -> "ProgressBar[V]": + """This function creates an iterable context manager that can be used + to iterate over something while showing a progress bar. It will + either iterate over the `iterable` or `length` items (that are counted + up). While iteration happens, this function will print a rendered + progress bar to the given `file` (defaults to stdout) and will attempt + to calculate remaining time and more. By default, this progress bar + will not be rendered if the file is not a terminal. + + The context manager creates the progress bar. When the context + manager is entered the progress bar is already created. With every + iteration over the progress bar, the iterable passed to the bar is + advanced and the bar is updated. When the context manager exits, + a newline is printed and the progress bar is finalized on screen. + + Note: The progress bar is currently designed for use cases where the + total progress can be expected to take at least several seconds. + Because of this, the ProgressBar class object won't display + progress that is considered too fast, and progress where the time + between steps is less than a second. + + No printing must happen or the progress bar will be unintentionally + destroyed. + + Example usage:: + + with progressbar(items) as bar: + for item in bar: + do_something_with(item) + + Alternatively, if no iterable is specified, one can manually update the + progress bar through the `update()` method instead of directly + iterating over the progress bar. The update method accepts the number + of steps to increment the bar with:: + + with progressbar(length=chunks.total_bytes) as bar: + for chunk in chunks: + process_chunk(chunk) + bar.update(chunks.bytes) + + The ``update()`` method also takes an optional value specifying the + ``current_item`` at the new position. This is useful when used + together with ``item_show_func`` to customize the output for each + manual step:: + + with click.progressbar( + length=total_size, + label='Unzipping archive', + item_show_func=lambda a: a.filename + ) as bar: + for archive in zip_file: + archive.extract() + bar.update(archive.size, archive) + + :param iterable: an iterable to iterate over. If not provided the length + is required. + :param length: the number of items to iterate over. By default the + progressbar will attempt to ask the iterator about its + length, which might or might not work. If an iterable is + also provided this parameter can be used to override the + length. If an iterable is not provided the progress bar + will iterate over a range of that length. + :param label: the label to show next to the progress bar. + :param show_eta: enables or disables the estimated time display. This is + automatically disabled if the length cannot be + determined. + :param show_percent: enables or disables the percentage display. The + default is `True` if the iterable has a length or + `False` if not. + :param show_pos: enables or disables the absolute position display. The + default is `False`. + :param item_show_func: A function called with the current item which + can return a string to show next to the progress bar. If the + function returns ``None`` nothing is shown. The current item can + be ``None``, such as when entering and exiting the bar. + :param fill_char: the character to use to show the filled part of the + progress bar. + :param empty_char: the character to use to show the non-filled part of + the progress bar. + :param bar_template: the format string to use as template for the bar. + The parameters in it are ``label`` for the label, + ``bar`` for the progress bar and ``info`` for the + info section. + :param info_sep: the separator between multiple info items (eta etc.) + :param width: the width of the progress bar in characters, 0 means full + terminal width + :param file: The file to write to. If this is not a terminal then + only the label is printed. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are included anywhere in the progress bar output + which is not the case by default. + :param update_min_steps: Render only when this many updates have + completed. This allows tuning for very fast iterators. + + .. versionchanged:: 8.0 + Output is shown even if execution time is less than 0.5 seconds. + + .. versionchanged:: 8.0 + ``item_show_func`` shows the current item, not the previous one. + + .. versionchanged:: 8.0 + Labels are echoed if the output is not a TTY. Reverts a change + in 7.0 that removed all output. + + .. versionadded:: 8.0 + Added the ``update_min_steps`` parameter. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. Added the ``update`` method to + the object. + + .. versionadded:: 2.0 + """ + from ._termui_impl import ProgressBar + + color = resolve_color_default(color) + return ProgressBar( + iterable=iterable, + length=length, + show_eta=show_eta, + show_percent=show_percent, + show_pos=show_pos, + item_show_func=item_show_func, + fill_char=fill_char, + empty_char=empty_char, + bar_template=bar_template, + info_sep=info_sep, + file=file, + label=label, + width=width, + color=color, + update_min_steps=update_min_steps, + ) + + +def clear() -> None: + """Clears the terminal screen. This will have the effect of clearing + the whole visible space of the terminal and moving the cursor to the + top left. This does not do anything if not connected to a terminal. + + .. versionadded:: 2.0 + """ + if not isatty(sys.stdout): + return + + # ANSI escape \033[2J clears the screen, \033[1;1H moves the cursor + echo("\033[2J\033[1;1H", nl=False) + + +def _interpret_color( + color: t.Union[int, t.Tuple[int, int, int], str], offset: int = 0 +) -> str: + if isinstance(color, int): + return f"{38 + offset};5;{color:d}" + + if isinstance(color, (tuple, list)): + r, g, b = color + return f"{38 + offset};2;{r:d};{g:d};{b:d}" + + return str(_ansi_colors[color] + offset) + + +def style( + text: t.Any, + fg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, + bg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, + bold: t.Optional[bool] = None, + dim: t.Optional[bool] = None, + underline: t.Optional[bool] = None, + overline: t.Optional[bool] = None, + italic: t.Optional[bool] = None, + blink: t.Optional[bool] = None, + reverse: t.Optional[bool] = None, + strikethrough: t.Optional[bool] = None, + reset: bool = True, +) -> str: + """Styles a text with ANSI styles and returns the new string. By + default the styling is self contained which means that at the end + of the string a reset code is issued. This can be prevented by + passing ``reset=False``. + + Examples:: + + click.echo(click.style('Hello World!', fg='green')) + click.echo(click.style('ATTENTION!', blink=True)) + click.echo(click.style('Some things', reverse=True, fg='cyan')) + click.echo(click.style('More colors', fg=(255, 12, 128), bg=117)) + + Supported color names: + + * ``black`` (might be a gray) + * ``red`` + * ``green`` + * ``yellow`` (might be an orange) + * ``blue`` + * ``magenta`` + * ``cyan`` + * ``white`` (might be light gray) + * ``bright_black`` + * ``bright_red`` + * ``bright_green`` + * ``bright_yellow`` + * ``bright_blue`` + * ``bright_magenta`` + * ``bright_cyan`` + * ``bright_white`` + * ``reset`` (reset the color code only) + + If the terminal supports it, color may also be specified as: + + - An integer in the interval [0, 255]. The terminal must support + 8-bit/256-color mode. + - An RGB tuple of three integers in [0, 255]. The terminal must + support 24-bit/true-color mode. + + See https://en.wikipedia.org/wiki/ANSI_color and + https://gist.github.com/XVilka/8346728 for more information. + + :param text: the string to style with ansi codes. + :param fg: if provided this will become the foreground color. + :param bg: if provided this will become the background color. + :param bold: if provided this will enable or disable bold mode. + :param dim: if provided this will enable or disable dim mode. This is + badly supported. + :param underline: if provided this will enable or disable underline. + :param overline: if provided this will enable or disable overline. + :param italic: if provided this will enable or disable italic. + :param blink: if provided this will enable or disable blinking. + :param reverse: if provided this will enable or disable inverse + rendering (foreground becomes background and the + other way round). + :param strikethrough: if provided this will enable or disable + striking through text. + :param reset: by default a reset-all code is added at the end of the + string which means that styles do not carry over. This + can be disabled to compose styles. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. + + .. versionchanged:: 8.0 + Added support for 256 and RGB color codes. + + .. versionchanged:: 8.0 + Added the ``strikethrough``, ``italic``, and ``overline`` + parameters. + + .. versionchanged:: 7.0 + Added support for bright colors. + + .. versionadded:: 2.0 + """ + if not isinstance(text, str): + text = str(text) + + bits = [] + + if fg: + try: + bits.append(f"\033[{_interpret_color(fg)}m") + except KeyError: + raise TypeError(f"Unknown color {fg!r}") from None + + if bg: + try: + bits.append(f"\033[{_interpret_color(bg, 10)}m") + except KeyError: + raise TypeError(f"Unknown color {bg!r}") from None + + if bold is not None: + bits.append(f"\033[{1 if bold else 22}m") + if dim is not None: + bits.append(f"\033[{2 if dim else 22}m") + if underline is not None: + bits.append(f"\033[{4 if underline else 24}m") + if overline is not None: + bits.append(f"\033[{53 if overline else 55}m") + if italic is not None: + bits.append(f"\033[{3 if italic else 23}m") + if blink is not None: + bits.append(f"\033[{5 if blink else 25}m") + if reverse is not None: + bits.append(f"\033[{7 if reverse else 27}m") + if strikethrough is not None: + bits.append(f"\033[{9 if strikethrough else 29}m") + bits.append(text) + if reset: + bits.append(_ansi_reset_all) + return "".join(bits) + + +def unstyle(text: str) -> str: + """Removes ANSI styling information from a string. Usually it's not + necessary to use this function as Click's echo function will + automatically remove styling if necessary. + + .. versionadded:: 2.0 + + :param text: the text to remove style information from. + """ + return strip_ansi(text) + + +def secho( + message: t.Optional[t.Any] = None, + file: t.Optional[t.IO[t.AnyStr]] = None, + nl: bool = True, + err: bool = False, + color: t.Optional[bool] = None, + **styles: t.Any, +) -> None: + """This function combines :func:`echo` and :func:`style` into one + call. As such the following two calls are the same:: + + click.secho('Hello World!', fg='green') + click.echo(click.style('Hello World!', fg='green')) + + All keyword arguments are forwarded to the underlying functions + depending on which one they go with. + + Non-string types will be converted to :class:`str`. However, + :class:`bytes` are passed directly to :meth:`echo` without applying + style. If you want to style bytes that represent text, call + :meth:`bytes.decode` first. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. Bytes are + passed through without style applied. + + .. versionadded:: 2.0 + """ + if message is not None and not isinstance(message, (bytes, bytearray)): + message = style(message, **styles) + + return echo(message, file=file, nl=nl, err=err, color=color) + + +def edit( + text: t.Optional[t.AnyStr] = None, + editor: t.Optional[str] = None, + env: t.Optional[t.Mapping[str, str]] = None, + require_save: bool = True, + extension: str = ".txt", + filename: t.Optional[str] = None, +) -> t.Optional[t.AnyStr]: + r"""Edits the given text in the defined editor. If an editor is given + (should be the full path to the executable but the regular operating + system search path is used for finding the executable) it overrides + the detected editor. Optionally, some environment variables can be + used. If the editor is closed without changes, `None` is returned. In + case a file is edited directly the return value is always `None` and + `require_save` and `extension` are ignored. + + If the editor cannot be opened a :exc:`UsageError` is raised. + + Note for Windows: to simplify cross-platform usage, the newlines are + automatically converted from POSIX to Windows and vice versa. As such, + the message here will have ``\n`` as newline markers. + + :param text: the text to edit. + :param editor: optionally the editor to use. Defaults to automatic + detection. + :param env: environment variables to forward to the editor. + :param require_save: if this is true, then not saving in the editor + will make the return value become `None`. + :param extension: the extension to tell the editor about. This defaults + to `.txt` but changing this might change syntax + highlighting. + :param filename: if provided it will edit this file instead of the + provided text contents. It will not use a temporary + file as an indirection in that case. + """ + from ._termui_impl import Editor + + ed = Editor(editor=editor, env=env, require_save=require_save, extension=extension) + + if filename is None: + return ed.edit(text) + + ed.edit_file(filename) + return None + + +def launch(url: str, wait: bool = False, locate: bool = False) -> int: + """This function launches the given URL (or filename) in the default + viewer application for this file type. If this is an executable, it + might launch the executable in a new session. The return value is + the exit code of the launched application. Usually, ``0`` indicates + success. + + Examples:: + + click.launch('https://click.palletsprojects.com/') + click.launch('/my/downloaded/file', locate=True) + + .. versionadded:: 2.0 + + :param url: URL or filename of the thing to launch. + :param wait: Wait for the program to exit before returning. This + only works if the launched program blocks. In particular, + ``xdg-open`` on Linux does not block. + :param locate: if this is set to `True` then instead of launching the + application associated with the URL it will attempt to + launch a file manager with the file located. This + might have weird effects if the URL does not point to + the filesystem. + """ + from ._termui_impl import open_url + + return open_url(url, wait=wait, locate=locate) + + +# If this is provided, getchar() calls into this instead. This is used +# for unittesting purposes. +_getchar: t.Optional[t.Callable[[bool], str]] = None + + +def getchar(echo: bool = False) -> str: + """Fetches a single character from the terminal and returns it. This + will always return a unicode character and under certain rare + circumstances this might return more than one character. The + situations which more than one character is returned is when for + whatever reason multiple characters end up in the terminal buffer or + standard input was not actually a terminal. + + Note that this will always read from the terminal, even if something + is piped into the standard input. + + Note for Windows: in rare cases when typing non-ASCII characters, this + function might wait for a second character and then return both at once. + This is because certain Unicode characters look like special-key markers. + + .. versionadded:: 2.0 + + :param echo: if set to `True`, the character read will also show up on + the terminal. The default is to not show it. + """ + global _getchar + + if _getchar is None: + from ._termui_impl import getchar as f + + _getchar = f + + return _getchar(echo) + + +def raw_terminal() -> t.ContextManager[int]: + from ._termui_impl import raw_terminal as f + + return f() + + +def pause(info: t.Optional[str] = None, err: bool = False) -> None: + """This command stops execution and waits for the user to press any + key to continue. This is similar to the Windows batch "pause" + command. If the program is not run through a terminal, this command + will instead do nothing. + + .. versionadded:: 2.0 + + .. versionadded:: 4.0 + Added the `err` parameter. + + :param info: The message to print before pausing. Defaults to + ``"Press any key to continue..."``. + :param err: if set to message goes to ``stderr`` instead of + ``stdout``, the same as with echo. + """ + if not isatty(sys.stdin) or not isatty(sys.stdout): + return + + if info is None: + info = _("Press any key to continue...") + + try: + if info: + echo(info, nl=False, err=err) + try: + getchar() + except (KeyboardInterrupt, EOFError): + pass + finally: + if info: + echo(err=err) diff --git a/.venv/lib/python3.9/site-packages/click/testing.py b/.venv/lib/python3.9/site-packages/click/testing.py new file mode 100644 index 0000000..772b215 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/testing.py @@ -0,0 +1,483 @@ +import contextlib +import io +import os +import shlex +import shutil +import sys +import tempfile +import typing as t +from types import TracebackType + +from . import _compat +from . import formatting +from . import termui +from . import utils +from ._compat import _find_binary_reader + +if t.TYPE_CHECKING: + from .core import BaseCommand + + +class EchoingStdin: + def __init__(self, input: t.BinaryIO, output: t.BinaryIO) -> None: + self._input = input + self._output = output + self._paused = False + + def __getattr__(self, x: str) -> t.Any: + return getattr(self._input, x) + + def _echo(self, rv: bytes) -> bytes: + if not self._paused: + self._output.write(rv) + + return rv + + def read(self, n: int = -1) -> bytes: + return self._echo(self._input.read(n)) + + def read1(self, n: int = -1) -> bytes: + return self._echo(self._input.read1(n)) # type: ignore + + def readline(self, n: int = -1) -> bytes: + return self._echo(self._input.readline(n)) + + def readlines(self) -> t.List[bytes]: + return [self._echo(x) for x in self._input.readlines()] + + def __iter__(self) -> t.Iterator[bytes]: + return iter(self._echo(x) for x in self._input) + + def __repr__(self) -> str: + return repr(self._input) + + +@contextlib.contextmanager +def _pause_echo(stream: t.Optional[EchoingStdin]) -> t.Iterator[None]: + if stream is None: + yield + else: + stream._paused = True + yield + stream._paused = False + + +class _NamedTextIOWrapper(io.TextIOWrapper): + def __init__( + self, buffer: t.BinaryIO, name: str, mode: str, **kwargs: t.Any + ) -> None: + super().__init__(buffer, **kwargs) + self._name = name + self._mode = mode + + @property + def name(self) -> str: + return self._name + + @property + def mode(self) -> str: + return self._mode + + +def make_input_stream( + input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]], charset: str +) -> t.BinaryIO: + # Is already an input stream. + if hasattr(input, "read"): + rv = _find_binary_reader(t.cast(t.IO[t.Any], input)) + + if rv is not None: + return rv + + raise TypeError("Could not find binary reader for input stream.") + + if input is None: + input = b"" + elif isinstance(input, str): + input = input.encode(charset) + + return io.BytesIO(input) + + +class Result: + """Holds the captured result of an invoked CLI script.""" + + def __init__( + self, + runner: "CliRunner", + stdout_bytes: bytes, + stderr_bytes: t.Optional[bytes], + return_value: t.Any, + exit_code: int, + exception: t.Optional[BaseException], + exc_info: t.Optional[ + t.Tuple[t.Type[BaseException], BaseException, TracebackType] + ] = None, + ): + #: The runner that created the result + self.runner = runner + #: The standard output as bytes. + self.stdout_bytes = stdout_bytes + #: The standard error as bytes, or None if not available + self.stderr_bytes = stderr_bytes + #: The value returned from the invoked command. + #: + #: .. versionadded:: 8.0 + self.return_value = return_value + #: The exit code as integer. + self.exit_code = exit_code + #: The exception that happened if one did. + self.exception = exception + #: The traceback + self.exc_info = exc_info + + @property + def output(self) -> str: + """The (standard) output as unicode string.""" + return self.stdout + + @property + def stdout(self) -> str: + """The standard output as unicode string.""" + return self.stdout_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + @property + def stderr(self) -> str: + """The standard error as unicode string.""" + if self.stderr_bytes is None: + raise ValueError("stderr not separately captured") + return self.stderr_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + def __repr__(self) -> str: + exc_str = repr(self.exception) if self.exception else "okay" + return f"<{type(self).__name__} {exc_str}>" + + +class CliRunner: + """The CLI runner provides functionality to invoke a Click command line + script for unittesting purposes in a isolated environment. This only + works in single-threaded systems without any concurrency as it changes the + global interpreter state. + + :param charset: the character set for the input and output data. + :param env: a dictionary with environment variables for overriding. + :param echo_stdin: if this is set to `True`, then reading from stdin writes + to stdout. This is useful for showing examples in + some circumstances. Note that regular prompts + will automatically echo the input. + :param mix_stderr: if this is set to `False`, then stdout and stderr are + preserved as independent streams. This is useful for + Unix-philosophy apps that have predictable stdout and + noisy stderr, such that each may be measured + independently + """ + + def __init__( + self, + charset: str = "utf-8", + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + echo_stdin: bool = False, + mix_stderr: bool = True, + ) -> None: + self.charset = charset + self.env: t.Mapping[str, t.Optional[str]] = env or {} + self.echo_stdin = echo_stdin + self.mix_stderr = mix_stderr + + def get_default_prog_name(self, cli: "BaseCommand") -> str: + """Given a command object it will return the default program name + for it. The default is the `name` attribute or ``"root"`` if not + set. + """ + return cli.name or "root" + + def make_env( + self, overrides: t.Optional[t.Mapping[str, t.Optional[str]]] = None + ) -> t.Mapping[str, t.Optional[str]]: + """Returns the environment overrides for invoking a script.""" + rv = dict(self.env) + if overrides: + rv.update(overrides) + return rv + + @contextlib.contextmanager + def isolation( + self, + input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]] = None, + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + color: bool = False, + ) -> t.Iterator[t.Tuple[io.BytesIO, t.Optional[io.BytesIO]]]: + """A context manager that sets up the isolation for invoking of a + command line tool. This sets up stdin with the given input data + and `os.environ` with the overrides from the given dictionary. + This also rebinds some internals in Click to be mocked (like the + prompt functionality). + + This is automatically done in the :meth:`invoke` method. + + :param input: the input stream to put into sys.stdin. + :param env: the environment overrides as dictionary. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionchanged:: 8.0 + ``stderr`` is opened with ``errors="backslashreplace"`` + instead of the default ``"strict"``. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + """ + bytes_input = make_input_stream(input, self.charset) + echo_input = None + + old_stdin = sys.stdin + old_stdout = sys.stdout + old_stderr = sys.stderr + old_forced_width = formatting.FORCED_WIDTH + formatting.FORCED_WIDTH = 80 + + env = self.make_env(env) + + bytes_output = io.BytesIO() + + if self.echo_stdin: + bytes_input = echo_input = t.cast( + t.BinaryIO, EchoingStdin(bytes_input, bytes_output) + ) + + sys.stdin = text_input = _NamedTextIOWrapper( + bytes_input, encoding=self.charset, name="", mode="r" + ) + + if self.echo_stdin: + # Force unbuffered reads, otherwise TextIOWrapper reads a + # large chunk which is echoed early. + text_input._CHUNK_SIZE = 1 # type: ignore + + sys.stdout = _NamedTextIOWrapper( + bytes_output, encoding=self.charset, name="", mode="w" + ) + + bytes_error = None + if self.mix_stderr: + sys.stderr = sys.stdout + else: + bytes_error = io.BytesIO() + sys.stderr = _NamedTextIOWrapper( + bytes_error, + encoding=self.charset, + name="", + mode="w", + errors="backslashreplace", + ) + + @_pause_echo(echo_input) # type: ignore + def visible_input(prompt: t.Optional[str] = None) -> str: + sys.stdout.write(prompt or "") + val = text_input.readline().rstrip("\r\n") + sys.stdout.write(f"{val}\n") + sys.stdout.flush() + return val + + @_pause_echo(echo_input) # type: ignore + def hidden_input(prompt: t.Optional[str] = None) -> str: + sys.stdout.write(f"{prompt or ''}\n") + sys.stdout.flush() + return text_input.readline().rstrip("\r\n") + + @_pause_echo(echo_input) # type: ignore + def _getchar(echo: bool) -> str: + char = sys.stdin.read(1) + + if echo: + sys.stdout.write(char) + + sys.stdout.flush() + return char + + default_color = color + + def should_strip_ansi( + stream: t.Optional[t.IO[t.Any]] = None, color: t.Optional[bool] = None + ) -> bool: + if color is None: + return not default_color + return not color + + old_visible_prompt_func = termui.visible_prompt_func + old_hidden_prompt_func = termui.hidden_prompt_func + old__getchar_func = termui._getchar + old_should_strip_ansi = utils.should_strip_ansi # type: ignore + old__compat_should_strip_ansi = _compat.should_strip_ansi + termui.visible_prompt_func = visible_input + termui.hidden_prompt_func = hidden_input + termui._getchar = _getchar + utils.should_strip_ansi = should_strip_ansi # type: ignore + _compat.should_strip_ansi = should_strip_ansi + + old_env = {} + try: + for key, value in env.items(): + old_env[key] = os.environ.get(key) + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + yield (bytes_output, bytes_error) + finally: + for key, value in old_env.items(): + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + sys.stdout = old_stdout + sys.stderr = old_stderr + sys.stdin = old_stdin + termui.visible_prompt_func = old_visible_prompt_func + termui.hidden_prompt_func = old_hidden_prompt_func + termui._getchar = old__getchar_func + utils.should_strip_ansi = old_should_strip_ansi # type: ignore + _compat.should_strip_ansi = old__compat_should_strip_ansi + formatting.FORCED_WIDTH = old_forced_width + + def invoke( + self, + cli: "BaseCommand", + args: t.Optional[t.Union[str, t.Sequence[str]]] = None, + input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]] = None, + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + catch_exceptions: bool = True, + color: bool = False, + **extra: t.Any, + ) -> Result: + """Invokes a command in an isolated environment. The arguments are + forwarded directly to the command line script, the `extra` keyword + arguments are passed to the :meth:`~clickpkg.Command.main` function of + the command. + + This returns a :class:`Result` object. + + :param cli: the command to invoke + :param args: the arguments to invoke. It may be given as an iterable + or a string. When given as string it will be interpreted + as a Unix shell command. More details at + :func:`shlex.split`. + :param input: the input data for `sys.stdin`. + :param env: the environment overrides. + :param catch_exceptions: Whether to catch any other exceptions than + ``SystemExit``. + :param extra: the keyword arguments to pass to :meth:`main`. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionchanged:: 8.0 + The result object has the ``return_value`` attribute with + the value returned from the invoked command. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionchanged:: 3.0 + Added the ``catch_exceptions`` parameter. + + .. versionchanged:: 3.0 + The result object has the ``exc_info`` attribute with the + traceback if available. + """ + exc_info = None + with self.isolation(input=input, env=env, color=color) as outstreams: + return_value = None + exception: t.Optional[BaseException] = None + exit_code = 0 + + if isinstance(args, str): + args = shlex.split(args) + + try: + prog_name = extra.pop("prog_name") + except KeyError: + prog_name = self.get_default_prog_name(cli) + + try: + return_value = cli.main(args=args or (), prog_name=prog_name, **extra) + except SystemExit as e: + exc_info = sys.exc_info() + e_code = t.cast(t.Optional[t.Union[int, t.Any]], e.code) + + if e_code is None: + e_code = 0 + + if e_code != 0: + exception = e + + if not isinstance(e_code, int): + sys.stdout.write(str(e_code)) + sys.stdout.write("\n") + e_code = 1 + + exit_code = e_code + + except Exception as e: + if not catch_exceptions: + raise + exception = e + exit_code = 1 + exc_info = sys.exc_info() + finally: + sys.stdout.flush() + stdout = outstreams[0].getvalue() + if self.mix_stderr: + stderr = None + else: + stderr = outstreams[1].getvalue() # type: ignore + + return Result( + runner=self, + stdout_bytes=stdout, + stderr_bytes=stderr, + return_value=return_value, + exit_code=exit_code, + exception=exception, + exc_info=exc_info, # type: ignore + ) + + @contextlib.contextmanager + def isolated_filesystem( + self, temp_dir: t.Optional[t.Union[str, "os.PathLike[str]"]] = None + ) -> t.Iterator[str]: + """A context manager that creates a temporary directory and + changes the current working directory to it. This isolates tests + that affect the contents of the CWD to prevent them from + interfering with each other. + + :param temp_dir: Create the temporary directory under this + directory. If given, the created directory is not removed + when exiting. + + .. versionchanged:: 8.0 + Added the ``temp_dir`` parameter. + """ + cwd = os.getcwd() + dt = tempfile.mkdtemp(dir=temp_dir) + os.chdir(dt) + + try: + yield dt + finally: + os.chdir(cwd) + + if temp_dir is None: + try: + shutil.rmtree(dt) + except OSError: + pass diff --git a/.venv/lib/python3.9/site-packages/click/types.py b/.venv/lib/python3.9/site-packages/click/types.py new file mode 100644 index 0000000..a70fd58 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/types.py @@ -0,0 +1,1093 @@ +import os +import stat +import sys +import typing as t +from datetime import datetime +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import _get_argv_encoding +from ._compat import open_stream +from .exceptions import BadParameter +from .utils import format_filename +from .utils import LazyFile +from .utils import safecall + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .core import Context + from .core import Parameter + from .shell_completion import CompletionItem + + +class ParamType: + """Represents the type of a parameter. Validates and converts values + from the command line or Python into the correct type. + + To implement a custom type, subclass and implement at least the + following: + + - The :attr:`name` class attribute must be set. + - Calling an instance of the type with ``None`` must return + ``None``. This is already implemented by default. + - :meth:`convert` must convert string values to the correct type. + - :meth:`convert` must accept values that are already the correct + type. + - It must be able to convert a value if the ``ctx`` and ``param`` + arguments are ``None``. This can occur when converting prompt + input. + """ + + is_composite: t.ClassVar[bool] = False + arity: t.ClassVar[int] = 1 + + #: the descriptive name of this type + name: str + + #: if a list of this type is expected and the value is pulled from a + #: string environment variable, this is what splits it up. `None` + #: means any whitespace. For all parameters the general rule is that + #: whitespace splits them up. The exception are paths and files which + #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on + #: Windows). + envvar_list_splitter: t.ClassVar[t.Optional[str]] = None + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + # The class name without the "ParamType" suffix. + param_type = type(self).__name__.partition("ParamType")[0] + param_type = param_type.partition("ParameterType")[0] + + # Custom subclasses might not remember to set a name. + if hasattr(self, "name"): + name = self.name + else: + name = param_type + + return {"param_type": param_type, "name": name} + + def __call__( + self, + value: t.Any, + param: t.Optional["Parameter"] = None, + ctx: t.Optional["Context"] = None, + ) -> t.Any: + if value is not None: + return self.convert(value, param, ctx) + + def get_metavar(self, param: "Parameter") -> t.Optional[str]: + """Returns the metavar default for this param if it provides one.""" + + def get_missing_message(self, param: "Parameter") -> t.Optional[str]: + """Optionally might return extra information about a missing + parameter. + + .. versionadded:: 2.0 + """ + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + """Convert the value to the correct type. This is not called if + the value is ``None`` (the missing value). + + This must accept string values from the command line, as well as + values that are already the correct type. It may also convert + other compatible types. + + The ``param`` and ``ctx`` arguments may be ``None`` in certain + situations, such as when converting prompt input. + + If the value cannot be converted, call :meth:`fail` with a + descriptive message. + + :param value: The value to convert. + :param param: The parameter that is using this type to convert + its value. May be ``None``. + :param ctx: The current context that arrived at this value. May + be ``None``. + """ + return value + + def split_envvar_value(self, rv: str) -> t.Sequence[str]: + """Given a value from an environment variable this splits it up + into small chunks depending on the defined envvar list splitter. + + If the splitter is set to `None`, which means that whitespace splits, + then leading and trailing whitespace is ignored. Otherwise, leading + and trailing splitters usually lead to empty items being included. + """ + return (rv or "").split(self.envvar_list_splitter) + + def fail( + self, + message: str, + param: t.Optional["Parameter"] = None, + ctx: t.Optional["Context"] = None, + ) -> "t.NoReturn": + """Helper method to fail with an invalid value message.""" + raise BadParameter(message, ctx=ctx, param=param) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a list of + :class:`~click.shell_completion.CompletionItem` objects for the + incomplete value. Most types do not provide completions, but + some do, and this allows custom types to provide custom + completions as well. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + return [] + + +class CompositeParamType(ParamType): + is_composite = True + + @property + def arity(self) -> int: # type: ignore + raise NotImplementedError() + + +class FuncParamType(ParamType): + def __init__(self, func: t.Callable[[t.Any], t.Any]) -> None: + self.name: str = func.__name__ + self.func = func + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["func"] = self.func + return info_dict + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + return self.func(value) + except ValueError: + try: + value = str(value) + except UnicodeError: + value = value.decode("utf-8", "replace") + + self.fail(value, param, ctx) + + +class UnprocessedParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + return value + + def __repr__(self) -> str: + return "UNPROCESSED" + + +class StringParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if isinstance(value, bytes): + enc = _get_argv_encoding() + try: + value = value.decode(enc) + except UnicodeError: + fs_enc = sys.getfilesystemencoding() + if fs_enc != enc: + try: + value = value.decode(fs_enc) + except UnicodeError: + value = value.decode("utf-8", "replace") + else: + value = value.decode("utf-8", "replace") + return value + return str(value) + + def __repr__(self) -> str: + return "STRING" + + +class Choice(ParamType): + """The choice type allows a value to be checked against a fixed set + of supported values. All of these values have to be strings. + + You should only pass a list or tuple of choices. Other iterables + (like generators) may lead to surprising results. + + The resulting value will always be one of the originally passed choices + regardless of ``case_sensitive`` or any ``ctx.token_normalize_func`` + being specified. + + See :ref:`choice-opts` for an example. + + :param case_sensitive: Set to false to make choices case + insensitive. Defaults to true. + """ + + name = "choice" + + def __init__(self, choices: t.Sequence[str], case_sensitive: bool = True) -> None: + self.choices = choices + self.case_sensitive = case_sensitive + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["choices"] = self.choices + info_dict["case_sensitive"] = self.case_sensitive + return info_dict + + def get_metavar(self, param: "Parameter") -> str: + choices_str = "|".join(self.choices) + + # Use curly braces to indicate a required argument. + if param.required and param.param_type_name == "argument": + return f"{{{choices_str}}}" + + # Use square braces to indicate an option or optional argument. + return f"[{choices_str}]" + + def get_missing_message(self, param: "Parameter") -> str: + return _("Choose from:\n\t{choices}").format(choices=",\n\t".join(self.choices)) + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + # Match through normalization and case sensitivity + # first do token_normalize_func, then lowercase + # preserve original `value` to produce an accurate message in + # `self.fail` + normed_value = value + normed_choices = {choice: choice for choice in self.choices} + + if ctx is not None and ctx.token_normalize_func is not None: + normed_value = ctx.token_normalize_func(value) + normed_choices = { + ctx.token_normalize_func(normed_choice): original + for normed_choice, original in normed_choices.items() + } + + if not self.case_sensitive: + normed_value = normed_value.casefold() + normed_choices = { + normed_choice.casefold(): original + for normed_choice, original in normed_choices.items() + } + + if normed_value in normed_choices: + return normed_choices[normed_value] + + choices_str = ", ".join(map(repr, self.choices)) + self.fail( + ngettext( + "{value!r} is not {choice}.", + "{value!r} is not one of {choices}.", + len(self.choices), + ).format(value=value, choice=choices_str, choices=choices_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return f"Choice({list(self.choices)})" + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Complete choices that start with the incomplete value. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + str_choices = map(str, self.choices) + + if self.case_sensitive: + matched = (c for c in str_choices if c.startswith(incomplete)) + else: + incomplete = incomplete.lower() + matched = (c for c in str_choices if c.lower().startswith(incomplete)) + + return [CompletionItem(c) for c in matched] + + +class DateTime(ParamType): + """The DateTime type converts date strings into `datetime` objects. + + The format strings which are checked are configurable, but default to some + common (non-timezone aware) ISO 8601 formats. + + When specifying *DateTime* formats, you should only pass a list or a tuple. + Other iterables, like generators, may lead to surprising results. + + The format strings are processed using ``datetime.strptime``, and this + consequently defines the format strings which are allowed. + + Parsing is tried using each format, in order, and the first format which + parses successfully is used. + + :param formats: A list or tuple of date format strings, in the order in + which they should be tried. Defaults to + ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``, + ``'%Y-%m-%d %H:%M:%S'``. + """ + + name = "datetime" + + def __init__(self, formats: t.Optional[t.Sequence[str]] = None): + self.formats: t.Sequence[str] = formats or [ + "%Y-%m-%d", + "%Y-%m-%dT%H:%M:%S", + "%Y-%m-%d %H:%M:%S", + ] + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["formats"] = self.formats + return info_dict + + def get_metavar(self, param: "Parameter") -> str: + return f"[{'|'.join(self.formats)}]" + + def _try_to_convert_date(self, value: t.Any, format: str) -> t.Optional[datetime]: + try: + return datetime.strptime(value, format) + except ValueError: + return None + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if isinstance(value, datetime): + return value + + for format in self.formats: + converted = self._try_to_convert_date(value, format) + + if converted is not None: + return converted + + formats_str = ", ".join(map(repr, self.formats)) + self.fail( + ngettext( + "{value!r} does not match the format {format}.", + "{value!r} does not match the formats {formats}.", + len(self.formats), + ).format(value=value, format=formats_str, formats=formats_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return "DateTime" + + +class _NumberParamTypeBase(ParamType): + _number_class: t.ClassVar[t.Type[t.Any]] + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + return self._number_class(value) + except ValueError: + self.fail( + _("{value!r} is not a valid {number_type}.").format( + value=value, number_type=self.name + ), + param, + ctx, + ) + + +class _NumberRangeBase(_NumberParamTypeBase): + def __init__( + self, + min: t.Optional[float] = None, + max: t.Optional[float] = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + self.min = min + self.max = max + self.min_open = min_open + self.max_open = max_open + self.clamp = clamp + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + min=self.min, + max=self.max, + min_open=self.min_open, + max_open=self.max_open, + clamp=self.clamp, + ) + return info_dict + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + import operator + + rv = super().convert(value, param, ctx) + lt_min: bool = self.min is not None and ( + operator.le if self.min_open else operator.lt + )(rv, self.min) + gt_max: bool = self.max is not None and ( + operator.ge if self.max_open else operator.gt + )(rv, self.max) + + if self.clamp: + if lt_min: + return self._clamp(self.min, 1, self.min_open) # type: ignore + + if gt_max: + return self._clamp(self.max, -1, self.max_open) # type: ignore + + if lt_min or gt_max: + self.fail( + _("{value} is not in the range {range}.").format( + value=rv, range=self._describe_range() + ), + param, + ctx, + ) + + return rv + + def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: + """Find the valid value to clamp to bound in the given + direction. + + :param bound: The boundary value. + :param dir: 1 or -1 indicating the direction to move. + :param open: If true, the range does not include the bound. + """ + raise NotImplementedError + + def _describe_range(self) -> str: + """Describe the range for use in help text.""" + if self.min is None: + op = "<" if self.max_open else "<=" + return f"x{op}{self.max}" + + if self.max is None: + op = ">" if self.min_open else ">=" + return f"x{op}{self.min}" + + lop = "<" if self.min_open else "<=" + rop = "<" if self.max_open else "<=" + return f"{self.min}{lop}x{rop}{self.max}" + + def __repr__(self) -> str: + clamp = " clamped" if self.clamp else "" + return f"<{type(self).__name__} {self._describe_range()}{clamp}>" + + +class IntParamType(_NumberParamTypeBase): + name = "integer" + _number_class = int + + def __repr__(self) -> str: + return "INT" + + +class IntRange(_NumberRangeBase, IntParamType): + """Restrict an :data:`click.INT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "integer range" + + def _clamp( # type: ignore + self, bound: int, dir: "te.Literal[1, -1]", open: bool + ) -> int: + if not open: + return bound + + return bound + dir + + +class FloatParamType(_NumberParamTypeBase): + name = "float" + _number_class = float + + def __repr__(self) -> str: + return "FLOAT" + + +class FloatRange(_NumberRangeBase, FloatParamType): + """Restrict a :data:`click.FLOAT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. This is not supported if either + boundary is marked ``open``. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "float range" + + def __init__( + self, + min: t.Optional[float] = None, + max: t.Optional[float] = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + super().__init__( + min=min, max=max, min_open=min_open, max_open=max_open, clamp=clamp + ) + + if (min_open or max_open) and clamp: + raise TypeError("Clamping is not supported for open bounds.") + + def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: + if not open: + return bound + + # Could use Python 3.9's math.nextafter here, but clamping an + # open float range doesn't seem to be particularly useful. It's + # left up to the user to write a callback to do it if needed. + raise RuntimeError("Clamping is not supported for open bounds.") + + +class BoolParamType(ParamType): + name = "boolean" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if value in {False, True}: + return bool(value) + + norm = value.strip().lower() + + if norm in {"1", "true", "t", "yes", "y", "on"}: + return True + + if norm in {"0", "false", "f", "no", "n", "off"}: + return False + + self.fail( + _("{value!r} is not a valid boolean.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "BOOL" + + +class UUIDParameterType(ParamType): + name = "uuid" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + import uuid + + if isinstance(value, uuid.UUID): + return value + + value = value.strip() + + try: + return uuid.UUID(value) + except ValueError: + self.fail( + _("{value!r} is not a valid UUID.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "UUID" + + +class File(ParamType): + """Declares a parameter to be a file for reading or writing. The file + is automatically closed once the context tears down (after the command + finished working). + + Files can be opened for reading or writing. The special value ``-`` + indicates stdin or stdout depending on the mode. + + By default, the file is opened for reading text data, but it can also be + opened in binary mode or for writing. The encoding parameter can be used + to force a specific encoding. + + The `lazy` flag controls if the file should be opened immediately or upon + first IO. The default is to be non-lazy for standard input and output + streams as well as files opened for reading, `lazy` otherwise. When opening a + file lazily for reading, it is still opened temporarily for validation, but + will not be held open until first IO. lazy is mainly useful when opening + for writing to avoid creating the file until it is needed. + + Files can also be opened atomically in which case all writes go into a + separate file in the same folder and upon completion the file will + be moved over to the original location. This is useful if a file + regularly read by other users is modified. + + See :ref:`file-args` for more information. + + .. versionchanged:: 2.0 + Added the ``atomic`` parameter. + """ + + name = "filename" + envvar_list_splitter: t.ClassVar[str] = os.path.pathsep + + def __init__( + self, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + lazy: t.Optional[bool] = None, + atomic: bool = False, + ) -> None: + self.mode = mode + self.encoding = encoding + self.errors = errors + self.lazy = lazy + self.atomic = atomic + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update(mode=self.mode, encoding=self.encoding) + return info_dict + + def resolve_lazy_flag(self, value: "t.Union[str, os.PathLike[str]]") -> bool: + if self.lazy is not None: + return self.lazy + if os.fspath(value) == "-": + return False + elif "w" in self.mode: + return True + return False + + def convert( + self, + value: t.Union[str, "os.PathLike[str]", t.IO[t.Any]], + param: t.Optional["Parameter"], + ctx: t.Optional["Context"], + ) -> t.IO[t.Any]: + if _is_file_like(value): + return value + + value = t.cast("t.Union[str, os.PathLike[str]]", value) + + try: + lazy = self.resolve_lazy_flag(value) + + if lazy: + lf = LazyFile( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + + if ctx is not None: + ctx.call_on_close(lf.close_intelligently) + + return t.cast(t.IO[t.Any], lf) + + f, should_close = open_stream( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + + # If a context is provided, we automatically close the file + # at the end of the context execution (or flush out). If a + # context does not exist, it's the caller's responsibility to + # properly close the file. This for instance happens when the + # type is used with prompts. + if ctx is not None: + if should_close: + ctx.call_on_close(safecall(f.close)) + else: + ctx.call_on_close(safecall(f.flush)) + + return f + except OSError as e: + self.fail(f"'{format_filename(value)}': {e.strerror}", param, ctx) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a special completion marker that tells the completion + system to use the shell to provide file path completions. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + return [CompletionItem(incomplete, type="file")] + + +def _is_file_like(value: t.Any) -> "te.TypeGuard[t.IO[t.Any]]": + return hasattr(value, "read") or hasattr(value, "write") + + +class Path(ParamType): + """The ``Path`` type is similar to the :class:`File` type, but + returns the filename instead of an open file. Various checks can be + enabled to validate the type of file and permissions. + + :param exists: The file or directory needs to exist for the value to + be valid. If this is not set to ``True``, and the file does not + exist, then all further checks are silently skipped. + :param file_okay: Allow a file as a value. + :param dir_okay: Allow a directory as a value. + :param readable: if true, a readable check is performed. + :param writable: if true, a writable check is performed. + :param executable: if true, an executable check is performed. + :param resolve_path: Make the value absolute and resolve any + symlinks. A ``~`` is not expanded, as this is supposed to be + done by the shell only. + :param allow_dash: Allow a single dash as a value, which indicates + a standard stream (but does not open it). Use + :func:`~click.open_file` to handle opening this value. + :param path_type: Convert the incoming path value to this type. If + ``None``, keep Python's default, which is ``str``. Useful to + convert to :class:`pathlib.Path`. + + .. versionchanged:: 8.1 + Added the ``executable`` parameter. + + .. versionchanged:: 8.0 + Allow passing ``path_type=pathlib.Path``. + + .. versionchanged:: 6.0 + Added the ``allow_dash`` parameter. + """ + + envvar_list_splitter: t.ClassVar[str] = os.path.pathsep + + def __init__( + self, + exists: bool = False, + file_okay: bool = True, + dir_okay: bool = True, + writable: bool = False, + readable: bool = True, + resolve_path: bool = False, + allow_dash: bool = False, + path_type: t.Optional[t.Type[t.Any]] = None, + executable: bool = False, + ): + self.exists = exists + self.file_okay = file_okay + self.dir_okay = dir_okay + self.readable = readable + self.writable = writable + self.executable = executable + self.resolve_path = resolve_path + self.allow_dash = allow_dash + self.type = path_type + + if self.file_okay and not self.dir_okay: + self.name: str = _("file") + elif self.dir_okay and not self.file_okay: + self.name = _("directory") + else: + self.name = _("path") + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + exists=self.exists, + file_okay=self.file_okay, + dir_okay=self.dir_okay, + writable=self.writable, + readable=self.readable, + allow_dash=self.allow_dash, + ) + return info_dict + + def coerce_path_result( + self, value: "t.Union[str, os.PathLike[str]]" + ) -> "t.Union[str, bytes, os.PathLike[str]]": + if self.type is not None and not isinstance(value, self.type): + if self.type is str: + return os.fsdecode(value) + elif self.type is bytes: + return os.fsencode(value) + else: + return t.cast("os.PathLike[str]", self.type(value)) + + return value + + def convert( + self, + value: "t.Union[str, os.PathLike[str]]", + param: t.Optional["Parameter"], + ctx: t.Optional["Context"], + ) -> "t.Union[str, bytes, os.PathLike[str]]": + rv = value + + is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-") + + if not is_dash: + if self.resolve_path: + # os.path.realpath doesn't resolve symlinks on Windows + # until Python 3.8. Use pathlib for now. + import pathlib + + rv = os.fsdecode(pathlib.Path(rv).resolve()) + + try: + st = os.stat(rv) + except OSError: + if not self.exists: + return self.coerce_path_result(rv) + self.fail( + _("{name} {filename!r} does not exist.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if not self.file_okay and stat.S_ISREG(st.st_mode): + self.fail( + _("{name} {filename!r} is a file.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + if not self.dir_okay and stat.S_ISDIR(st.st_mode): + self.fail( + _("{name} {filename!r} is a directory.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.readable and not os.access(rv, os.R_OK): + self.fail( + _("{name} {filename!r} is not readable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.writable and not os.access(rv, os.W_OK): + self.fail( + _("{name} {filename!r} is not writable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.executable and not os.access(value, os.X_OK): + self.fail( + _("{name} {filename!r} is not executable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + return self.coerce_path_result(rv) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a special completion marker that tells the completion + system to use the shell to provide path completions for only + directories or any paths. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + type = "dir" if self.dir_okay and not self.file_okay else "file" + return [CompletionItem(incomplete, type=type)] + + +class Tuple(CompositeParamType): + """The default behavior of Click is to apply a type on a value directly. + This works well in most cases, except for when `nargs` is set to a fixed + count and different types should be used for different items. In this + case the :class:`Tuple` type can be used. This type can only be used + if `nargs` is set to a fixed number. + + For more information see :ref:`tuple-type`. + + This can be selected by using a Python tuple literal as a type. + + :param types: a list of types that should be used for the tuple items. + """ + + def __init__(self, types: t.Sequence[t.Union[t.Type[t.Any], ParamType]]) -> None: + self.types: t.Sequence[ParamType] = [convert_type(ty) for ty in types] + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["types"] = [t.to_info_dict() for t in self.types] + return info_dict + + @property + def name(self) -> str: # type: ignore + return f"<{' '.join(ty.name for ty in self.types)}>" + + @property + def arity(self) -> int: # type: ignore + return len(self.types) + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + len_type = len(self.types) + len_value = len(value) + + if len_value != len_type: + self.fail( + ngettext( + "{len_type} values are required, but {len_value} was given.", + "{len_type} values are required, but {len_value} were given.", + len_value, + ).format(len_type=len_type, len_value=len_value), + param=param, + ctx=ctx, + ) + + return tuple(ty(x, param, ctx) for ty, x in zip(self.types, value)) + + +def convert_type(ty: t.Optional[t.Any], default: t.Optional[t.Any] = None) -> ParamType: + """Find the most appropriate :class:`ParamType` for the given Python + type. If the type isn't provided, it can be inferred from a default + value. + """ + guessed_type = False + + if ty is None and default is not None: + if isinstance(default, (tuple, list)): + # If the default is empty, ty will remain None and will + # return STRING. + if default: + item = default[0] + + # A tuple of tuples needs to detect the inner types. + # Can't call convert recursively because that would + # incorrectly unwind the tuple to a single type. + if isinstance(item, (tuple, list)): + ty = tuple(map(type, item)) + else: + ty = type(item) + else: + ty = type(default) + + guessed_type = True + + if isinstance(ty, tuple): + return Tuple(ty) + + if isinstance(ty, ParamType): + return ty + + if ty is str or ty is None: + return STRING + + if ty is int: + return INT + + if ty is float: + return FLOAT + + if ty is bool: + return BOOL + + if guessed_type: + return STRING + + if __debug__: + try: + if issubclass(ty, ParamType): + raise AssertionError( + f"Attempted to use an uninstantiated parameter type ({ty})." + ) + except TypeError: + # ty is an instance (correct), so issubclass fails. + pass + + return FuncParamType(ty) + + +#: A dummy parameter type that just does nothing. From a user's +#: perspective this appears to just be the same as `STRING` but +#: internally no string conversion takes place if the input was bytes. +#: This is usually useful when working with file paths as they can +#: appear in bytes and unicode. +#: +#: For path related uses the :class:`Path` type is a better choice but +#: there are situations where an unprocessed type is useful which is why +#: it is is provided. +#: +#: .. versionadded:: 4.0 +UNPROCESSED = UnprocessedParamType() + +#: A unicode string parameter type which is the implicit default. This +#: can also be selected by using ``str`` as type. +STRING = StringParamType() + +#: An integer parameter. This can also be selected by using ``int`` as +#: type. +INT = IntParamType() + +#: A floating point value parameter. This can also be selected by using +#: ``float`` as type. +FLOAT = FloatParamType() + +#: A boolean parameter. This is the default for boolean flags. This can +#: also be selected by using ``bool`` as a type. +BOOL = BoolParamType() + +#: A UUID parameter. +UUID = UUIDParameterType() diff --git a/.venv/lib/python3.9/site-packages/click/utils.py b/.venv/lib/python3.9/site-packages/click/utils.py new file mode 100644 index 0000000..836c6f2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/utils.py @@ -0,0 +1,624 @@ +import os +import re +import sys +import typing as t +from functools import update_wrapper +from types import ModuleType +from types import TracebackType + +from ._compat import _default_text_stderr +from ._compat import _default_text_stdout +from ._compat import _find_binary_writer +from ._compat import auto_wrap_for_ansi +from ._compat import binary_streams +from ._compat import open_stream +from ._compat import should_strip_ansi +from ._compat import strip_ansi +from ._compat import text_streams +from ._compat import WIN +from .globals import resolve_color_default + +if t.TYPE_CHECKING: + import typing_extensions as te + + P = te.ParamSpec("P") + +R = t.TypeVar("R") + + +def _posixify(name: str) -> str: + return "-".join(name.split()).lower() + + +def safecall(func: "t.Callable[P, R]") -> "t.Callable[P, t.Optional[R]]": + """Wraps a function so that it swallows exceptions.""" + + def wrapper(*args: "P.args", **kwargs: "P.kwargs") -> t.Optional[R]: + try: + return func(*args, **kwargs) + except Exception: + pass + return None + + return update_wrapper(wrapper, func) + + +def make_str(value: t.Any) -> str: + """Converts a value into a valid string.""" + if isinstance(value, bytes): + try: + return value.decode(sys.getfilesystemencoding()) + except UnicodeError: + return value.decode("utf-8", "replace") + return str(value) + + +def make_default_short_help(help: str, max_length: int = 45) -> str: + """Returns a condensed version of help string.""" + # Consider only the first paragraph. + paragraph_end = help.find("\n\n") + + if paragraph_end != -1: + help = help[:paragraph_end] + + # Collapse newlines, tabs, and spaces. + words = help.split() + + if not words: + return "" + + # The first paragraph started with a "no rewrap" marker, ignore it. + if words[0] == "\b": + words = words[1:] + + total_length = 0 + last_index = len(words) - 1 + + for i, word in enumerate(words): + total_length += len(word) + (i > 0) + + if total_length > max_length: # too long, truncate + break + + if word[-1] == ".": # sentence end, truncate without "..." + return " ".join(words[: i + 1]) + + if total_length == max_length and i != last_index: + break # not at sentence end, truncate with "..." + else: + return " ".join(words) # no truncation needed + + # Account for the length of the suffix. + total_length += len("...") + + # remove words until the length is short enough + while i > 0: + total_length -= len(words[i]) + (i > 0) + + if total_length <= max_length: + break + + i -= 1 + + return " ".join(words[:i]) + "..." + + +class LazyFile: + """A lazy file works like a regular file but it does not fully open + the file but it does perform some basic checks early to see if the + filename parameter does make sense. This is useful for safely opening + files for writing. + """ + + def __init__( + self, + filename: t.Union[str, "os.PathLike[str]"], + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + atomic: bool = False, + ): + self.name: str = os.fspath(filename) + self.mode = mode + self.encoding = encoding + self.errors = errors + self.atomic = atomic + self._f: t.Optional[t.IO[t.Any]] + self.should_close: bool + + if self.name == "-": + self._f, self.should_close = open_stream(filename, mode, encoding, errors) + else: + if "r" in mode: + # Open and close the file in case we're opening it for + # reading so that we can catch at least some errors in + # some cases early. + open(filename, mode).close() + self._f = None + self.should_close = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self.open(), name) + + def __repr__(self) -> str: + if self._f is not None: + return repr(self._f) + return f"" + + def open(self) -> t.IO[t.Any]: + """Opens the file if it's not yet open. This call might fail with + a :exc:`FileError`. Not handling this error will produce an error + that Click shows. + """ + if self._f is not None: + return self._f + try: + rv, self.should_close = open_stream( + self.name, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + except OSError as e: + from .exceptions import FileError + + raise FileError(self.name, hint=e.strerror) from e + self._f = rv + return rv + + def close(self) -> None: + """Closes the underlying file, no matter what.""" + if self._f is not None: + self._f.close() + + def close_intelligently(self) -> None: + """This function only closes the file if it was opened by the lazy + file wrapper. For instance this will never close stdin. + """ + if self.should_close: + self.close() + + def __enter__(self) -> "LazyFile": + return self + + def __exit__( + self, + exc_type: t.Optional[t.Type[BaseException]], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + self.close_intelligently() + + def __iter__(self) -> t.Iterator[t.AnyStr]: + self.open() + return iter(self._f) # type: ignore + + +class KeepOpenFile: + def __init__(self, file: t.IO[t.Any]) -> None: + self._file: t.IO[t.Any] = file + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._file, name) + + def __enter__(self) -> "KeepOpenFile": + return self + + def __exit__( + self, + exc_type: t.Optional[t.Type[BaseException]], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + pass + + def __repr__(self) -> str: + return repr(self._file) + + def __iter__(self) -> t.Iterator[t.AnyStr]: + return iter(self._file) + + +def echo( + message: t.Optional[t.Any] = None, + file: t.Optional[t.IO[t.Any]] = None, + nl: bool = True, + err: bool = False, + color: t.Optional[bool] = None, +) -> None: + """Print a message and newline to stdout or a file. This should be + used instead of :func:`print` because it provides better support + for different data, files, and environments. + + Compared to :func:`print`, this does the following: + + - Ensures that the output encoding is not misconfigured on Linux. + - Supports Unicode in the Windows console. + - Supports writing to binary outputs, and supports writing bytes + to text outputs. + - Supports colors and styles on Windows. + - Removes ANSI color and style codes if the output does not look + like an interactive terminal. + - Always flushes the output. + + :param message: The string or bytes to output. Other objects are + converted to strings. + :param file: The file to write to. Defaults to ``stdout``. + :param err: Write to ``stderr`` instead of ``stdout``. + :param nl: Print a newline after the message. Enabled by default. + :param color: Force showing or hiding colors and other styles. By + default Click will remove color if the output does not look like + an interactive terminal. + + .. versionchanged:: 6.0 + Support Unicode output on the Windows console. Click does not + modify ``sys.stdout``, so ``sys.stdout.write()`` and ``print()`` + will still not support Unicode. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionadded:: 3.0 + Added the ``err`` parameter. + + .. versionchanged:: 2.0 + Support colors on Windows if colorama is installed. + """ + if file is None: + if err: + file = _default_text_stderr() + else: + file = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if file is None: + return + + # Convert non bytes/text into the native string type. + if message is not None and not isinstance(message, (str, bytes, bytearray)): + out: t.Optional[t.Union[str, bytes]] = str(message) + else: + out = message + + if nl: + out = out or "" + if isinstance(out, str): + out += "\n" + else: + out += b"\n" + + if not out: + file.flush() + return + + # If there is a message and the value looks like bytes, we manually + # need to find the binary stream and write the message in there. + # This is done separately so that most stream types will work as you + # would expect. Eg: you can write to StringIO for other cases. + if isinstance(out, (bytes, bytearray)): + binary_file = _find_binary_writer(file) + + if binary_file is not None: + file.flush() + binary_file.write(out) + binary_file.flush() + return + + # ANSI style code support. For no message or bytes, nothing happens. + # When outputting to a file instead of a terminal, strip codes. + else: + color = resolve_color_default(color) + + if should_strip_ansi(file, color): + out = strip_ansi(out) + elif WIN: + if auto_wrap_for_ansi is not None: + file = auto_wrap_for_ansi(file, color) # type: ignore + elif not color: + out = strip_ansi(out) + + file.write(out) # type: ignore + file.flush() + + +def get_binary_stream(name: "te.Literal['stdin', 'stdout', 'stderr']") -> t.BinaryIO: + """Returns a system stream for byte processing. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + """ + opener = binary_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener() + + +def get_text_stream( + name: "te.Literal['stdin', 'stdout', 'stderr']", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", +) -> t.TextIO: + """Returns a system stream for text processing. This usually returns + a wrapped stream around a binary stream returned from + :func:`get_binary_stream` but it also can take shortcuts for already + correctly configured streams. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + :param encoding: overrides the detected default encoding. + :param errors: overrides the default error mode. + """ + opener = text_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener(encoding, errors) + + +def open_file( + filename: t.Union[str, "os.PathLike[str]"], + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + lazy: bool = False, + atomic: bool = False, +) -> t.IO[t.Any]: + """Open a file, with extra behavior to handle ``'-'`` to indicate + a standard stream, lazy open on write, and atomic write. Similar to + the behavior of the :class:`~click.File` param type. + + If ``'-'`` is given to open ``stdout`` or ``stdin``, the stream is + wrapped so that using it in a context manager will not close it. + This makes it possible to use the function without accidentally + closing a standard stream: + + .. code-block:: python + + with open_file(filename) as f: + ... + + :param filename: The name or Path of the file to open, or ``'-'`` for + ``stdin``/``stdout``. + :param mode: The mode in which to open the file. + :param encoding: The encoding to decode or encode a file opened in + text mode. + :param errors: The error handling mode. + :param lazy: Wait to open the file until it is accessed. For read + mode, the file is temporarily opened to raise access errors + early, then closed until it is read again. + :param atomic: Write to a temporary file and replace the given file + on close. + + .. versionadded:: 3.0 + """ + if lazy: + return t.cast( + t.IO[t.Any], LazyFile(filename, mode, encoding, errors, atomic=atomic) + ) + + f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic) + + if not should_close: + f = t.cast(t.IO[t.Any], KeepOpenFile(f)) + + return f + + +def format_filename( + filename: "t.Union[str, bytes, os.PathLike[str], os.PathLike[bytes]]", + shorten: bool = False, +) -> str: + """Format a filename as a string for display. Ensures the filename can be + displayed by replacing any invalid bytes or surrogate escapes in the name + with the replacement character ``�``. + + Invalid bytes or surrogate escapes will raise an error when written to a + stream with ``errors="strict"``. This will typically happen with ``stdout`` + when the locale is something like ``en_GB.UTF-8``. + + Many scenarios *are* safe to write surrogates though, due to PEP 538 and + PEP 540, including: + + - Writing to ``stderr``, which uses ``errors="backslashreplace"``. + - The system has ``LANG=C.UTF-8``, ``C``, or ``POSIX``. Python opens + stdout and stderr with ``errors="surrogateescape"``. + - None of ``LANG/LC_*`` are set. Python assumes ``LANG=C.UTF-8``. + - Python is started in UTF-8 mode with ``PYTHONUTF8=1`` or ``-X utf8``. + Python opens stdout and stderr with ``errors="surrogateescape"``. + + :param filename: formats a filename for UI display. This will also convert + the filename into unicode without failing. + :param shorten: this optionally shortens the filename to strip of the + path that leads up to it. + """ + if shorten: + filename = os.path.basename(filename) + else: + filename = os.fspath(filename) + + if isinstance(filename, bytes): + filename = filename.decode(sys.getfilesystemencoding(), "replace") + else: + filename = filename.encode("utf-8", "surrogateescape").decode( + "utf-8", "replace" + ) + + return filename + + +def get_app_dir(app_name: str, roaming: bool = True, force_posix: bool = False) -> str: + r"""Returns the config folder for the application. The default behavior + is to return whatever is most appropriate for the operating system. + + To give you an idea, for an app called ``"Foo Bar"``, something like + the following folders could be returned: + + Mac OS X: + ``~/Library/Application Support/Foo Bar`` + Mac OS X (POSIX): + ``~/.foo-bar`` + Unix: + ``~/.config/foo-bar`` + Unix (POSIX): + ``~/.foo-bar`` + Windows (roaming): + ``C:\Users\\AppData\Roaming\Foo Bar`` + Windows (not roaming): + ``C:\Users\\AppData\Local\Foo Bar`` + + .. versionadded:: 2.0 + + :param app_name: the application name. This should be properly capitalized + and can contain whitespace. + :param roaming: controls if the folder should be roaming or not on Windows. + Has no effect otherwise. + :param force_posix: if this is set to `True` then on any POSIX system the + folder will be stored in the home folder with a leading + dot instead of the XDG config home or darwin's + application support folder. + """ + if WIN: + key = "APPDATA" if roaming else "LOCALAPPDATA" + folder = os.environ.get(key) + if folder is None: + folder = os.path.expanduser("~") + return os.path.join(folder, app_name) + if force_posix: + return os.path.join(os.path.expanduser(f"~/.{_posixify(app_name)}")) + if sys.platform == "darwin": + return os.path.join( + os.path.expanduser("~/Library/Application Support"), app_name + ) + return os.path.join( + os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")), + _posixify(app_name), + ) + + +class PacifyFlushWrapper: + """This wrapper is used to catch and suppress BrokenPipeErrors resulting + from ``.flush()`` being called on broken pipe during the shutdown/final-GC + of the Python interpreter. Notably ``.flush()`` is always called on + ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any + other cleanup code, and the case where the underlying file is not a broken + pipe, all calls and attributes are proxied. + """ + + def __init__(self, wrapped: t.IO[t.Any]) -> None: + self.wrapped = wrapped + + def flush(self) -> None: + try: + self.wrapped.flush() + except OSError as e: + import errno + + if e.errno != errno.EPIPE: + raise + + def __getattr__(self, attr: str) -> t.Any: + return getattr(self.wrapped, attr) + + +def _detect_program_name( + path: t.Optional[str] = None, _main: t.Optional[ModuleType] = None +) -> str: + """Determine the command used to run the program, for use in help + text. If a file or entry point was executed, the file name is + returned. If ``python -m`` was used to execute a module or package, + ``python -m name`` is returned. + + This doesn't try to be too precise, the goal is to give a concise + name for help text. Files are only shown as their name without the + path. ``python`` is only shown for modules, and the full path to + ``sys.executable`` is not shown. + + :param path: The Python file being executed. Python puts this in + ``sys.argv[0]``, which is used by default. + :param _main: The ``__main__`` module. This should only be passed + during internal testing. + + .. versionadded:: 8.0 + Based on command args detection in the Werkzeug reloader. + + :meta private: + """ + if _main is None: + _main = sys.modules["__main__"] + + if not path: + path = sys.argv[0] + + # The value of __package__ indicates how Python was called. It may + # not exist if a setuptools script is installed as an egg. It may be + # set incorrectly for entry points created with pip on Windows. + # It is set to "" inside a Shiv or PEX zipapp. + if getattr(_main, "__package__", None) in {None, ""} or ( + os.name == "nt" + and _main.__package__ == "" + and not os.path.exists(path) + and os.path.exists(f"{path}.exe") + ): + # Executed a file, like "python app.py". + return os.path.basename(path) + + # Executed a module, like "python -m example". + # Rewritten by Python from "-m script" to "/path/to/script.py". + # Need to look at main module to determine how it was executed. + py_module = t.cast(str, _main.__package__) + name = os.path.splitext(os.path.basename(path))[0] + + # A submodule like "example.cli". + if name != "__main__": + py_module = f"{py_module}.{name}" + + return f"python -m {py_module.lstrip('.')}" + + +def _expand_args( + args: t.Iterable[str], + *, + user: bool = True, + env: bool = True, + glob_recursive: bool = True, +) -> t.List[str]: + """Simulate Unix shell expansion with Python functions. + + See :func:`glob.glob`, :func:`os.path.expanduser`, and + :func:`os.path.expandvars`. + + This is intended for use on Windows, where the shell does not do any + expansion. It may not exactly match what a Unix shell would do. + + :param args: List of command line arguments to expand. + :param user: Expand user home directory. + :param env: Expand environment variables. + :param glob_recursive: ``**`` matches directories recursively. + + .. versionchanged:: 8.1 + Invalid glob patterns are treated as empty expansions rather + than raising an error. + + .. versionadded:: 8.0 + + :meta private: + """ + from glob import glob + + out = [] + + for arg in args: + if user: + arg = os.path.expanduser(arg) + + if env: + arg = os.path.expandvars(arg) + + try: + matches = glob(arg, recursive=glob_recursive) + except re.error: + matches = [] + + if not matches: + out.append(arg) + else: + out.extend(matches) + + return out diff --git a/.venv/lib/python3.9/site-packages/dateutil/__init__.py b/.venv/lib/python3.9/site-packages/dateutil/__init__.py new file mode 100644 index 0000000..a2c19c0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/__init__.py @@ -0,0 +1,24 @@ +# -*- 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/.venv/lib/python3.9/site-packages/dateutil/_common.py b/.venv/lib/python3.9/site-packages/dateutil/_common.py new file mode 100644 index 0000000..4eb2659 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/_common.py @@ -0,0 +1,43 @@ +""" +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/.venv/lib/python3.9/site-packages/dateutil/_version.py b/.venv/lib/python3.9/site-packages/dateutil/_version.py new file mode 100644 index 0000000..ddda980 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/_version.py @@ -0,0 +1,4 @@ +# 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/.venv/lib/python3.9/site-packages/dateutil/easter.py b/.venv/lib/python3.9/site-packages/dateutil/easter.py new file mode 100644 index 0000000..f74d1f7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/easter.py @@ -0,0 +1,89 @@ +# -*- 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/.venv/lib/python3.9/site-packages/dateutil/parser/__init__.py b/.venv/lib/python3.9/site-packages/dateutil/parser/__init__.py new file mode 100644 index 0000000..d174b0e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/parser/__init__.py @@ -0,0 +1,61 @@ +# -*- 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/.venv/lib/python3.9/site-packages/dateutil/parser/_parser.py b/.venv/lib/python3.9/site-packages/dateutil/parser/_parser.py new file mode 100644 index 0000000..37d1663 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/parser/_parser.py @@ -0,0 +1,1613 @@ +# -*- coding: utf-8 -*- +""" +This module offers a generic date/time string parser which is able to parse +most known formats to represent a date and/or time. + +This module attempts to be forgiving with regards to unlikely input formats, +returning a datetime object even for dates which are ambiguous. If an element +of a date/time stamp is omitted, the following rules are applied: + +- If AM or PM is left unspecified, a 24-hour clock is assumed, however, an hour + on a 12-hour clock (``0 <= hour <= 12``) *must* be specified if AM or PM is + specified. +- If a time zone is omitted, a timezone-naive datetime is returned. + +If any other elements are missing, they are taken from the +:class:`datetime.datetime` object passed to the parameter ``default``. If this +results in a day number exceeding the valid number of days per month, the +value falls back to the end of the month. + +Additional resources about date/time string formats can be found below: + +- `A summary of the international standard date and time notation + `_ +- `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/.venv/lib/python3.9/site-packages/dateutil/parser/isoparser.py b/.venv/lib/python3.9/site-packages/dateutil/parser/isoparser.py new file mode 100644 index 0000000..7060087 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/parser/isoparser.py @@ -0,0 +1,416 @@ +# -*- 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/.venv/lib/python3.9/site-packages/dateutil/relativedelta.py b/.venv/lib/python3.9/site-packages/dateutil/relativedelta.py new file mode 100644 index 0000000..cd323a5 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/relativedelta.py @@ -0,0 +1,599 @@ +# -*- 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/.venv/lib/python3.9/site-packages/dateutil/rrule.py b/.venv/lib/python3.9/site-packages/dateutil/rrule.py new file mode 100644 index 0000000..571a0d2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/rrule.py @@ -0,0 +1,1737 @@ +# -*- 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/.venv/lib/python3.9/site-packages/dateutil/tz/__init__.py b/.venv/lib/python3.9/site-packages/dateutil/tz/__init__.py new file mode 100644 index 0000000..af1352c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/tz/__init__.py @@ -0,0 +1,12 @@ +# -*- 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/.venv/lib/python3.9/site-packages/dateutil/tz/_common.py b/.venv/lib/python3.9/site-packages/dateutil/tz/_common.py new file mode 100644 index 0000000..e6ac118 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/tz/_common.py @@ -0,0 +1,419 @@ +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/.venv/lib/python3.9/site-packages/dateutil/tz/_factories.py b/.venv/lib/python3.9/site-packages/dateutil/tz/_factories.py new file mode 100644 index 0000000..f8a6589 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/tz/_factories.py @@ -0,0 +1,80 @@ +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/.venv/lib/python3.9/site-packages/dateutil/tz/tz.py b/.venv/lib/python3.9/site-packages/dateutil/tz/tz.py new file mode 100644 index 0000000..6175914 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/tz/tz.py @@ -0,0 +1,1849 @@ +# -*- 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/.venv/lib/python3.9/site-packages/dateutil/tz/win.py b/.venv/lib/python3.9/site-packages/dateutil/tz/win.py new file mode 100644 index 0000000..cde07ba --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/tz/win.py @@ -0,0 +1,370 @@ +# -*- 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/.venv/lib/python3.9/site-packages/dateutil/tzwin.py b/.venv/lib/python3.9/site-packages/dateutil/tzwin.py new file mode 100644 index 0000000..cebc673 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/tzwin.py @@ -0,0 +1,2 @@ +# tzwin has moved to dateutil.tz.win +from .tz.win import * diff --git a/.venv/lib/python3.9/site-packages/dateutil/utils.py b/.venv/lib/python3.9/site-packages/dateutil/utils.py new file mode 100644 index 0000000..dd2d245 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/utils.py @@ -0,0 +1,71 @@ +# -*- 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/.venv/lib/python3.9/site-packages/dateutil/zoneinfo/__init__.py b/.venv/lib/python3.9/site-packages/dateutil/zoneinfo/__init__.py new file mode 100644 index 0000000..34f11ad --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/zoneinfo/__init__.py @@ -0,0 +1,167 @@ +# -*- 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/.venv/lib/python3.9/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz b/.venv/lib/python3.9/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..1461f8c862df8c3eae75e0ea376788b5b2602269 GIT binary patch literal 156400 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

Toller Termin für

mal zu"gucken"

und so

+URL:https://podio.com/xxxxxxyyyyyy/zpodio-testgelande/apps/calen + dar/items/5 +LOCATION:online +DTSTART:20220222T183000Z +DTEND:20220222T193000Z +DTSTAMP:20220220T142821Z +END:VEVENT +END:VCALENDAR +X-COMMENT:Cached from 2022-02-20 14:28:21 - new at most every 1800sec. diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_466_convert_tzid_with_slash.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_466_convert_tzid_with_slash.ics new file mode 100644 index 0000000..2ec7c85 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_466_convert_tzid_with_slash.ics @@ -0,0 +1,12 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:0cab49a0-1167-40f0-bfed-ecb4d117047d +DTSTAMP:20221019T102950Z +DTSTART;TZID=/Europe/Stockholm:20221021T200000 +DTEND;TZID=/Europe/Stockholm:20221021T210000 +SUMMARY:Just chatting +DESCRIPTION:Just Chatting. +CATEGORIES:Just Chatting +RRULE:FREQ=WEEKLY;BYDAY=FR +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_466_respect_unique_timezone.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_466_respect_unique_timezone.ics new file mode 100644 index 0000000..5cbebe7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_466_respect_unique_timezone.ics @@ -0,0 +1,29 @@ +BEGIN:VCALENDAR +BEGIN:VTIMEZONE +TZID:/Europe/CUSTOM +BEGIN:DAYLIGHT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +TZNAME:CEST +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +TZNAME:CET +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +UID:0cab49a0-1167-40f0-bfed-ecb4d117047d +DTSTAMP:20221019T102950Z +DTSTART;TZID=/Europe/CUSTOM:20221021T200000 +DTEND;TZID=/Europe/CUSTOM:20221021T210000 +SUMMARY:Just chatting +DESCRIPTION:Just Chatting. +CATEGORIES:Just Chatting +RRULE:FREQ=WEEKLY;BYDAY=FR +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_526_calendar_with_different_events.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_526_calendar_with_different_events.ics new file mode 100644 index 0000000..67b32f5 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_526_calendar_with_different_events.ics @@ -0,0 +1,18 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:icalendar-2023 +BEGIN:VEVENT +UID:ical-jacadzaca-3 +SUMMARY: Some very different event ':' +DTSTART;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T160000 +DTEND;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T163000 +DTSTAMP:20211004T150245Z +END:VEVENT +BEGIN:VEVENT +UID:ical-jacadzaca-4 +SUMMARY: Some very different other event +DTSTART;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T164000 +DTEND;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T165000 +DTSTAMP:20211004T150245Z +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_526_calendar_with_event_subset.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_526_calendar_with_event_subset.ics new file mode 100644 index 0000000..4624f2f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_526_calendar_with_event_subset.ics @@ -0,0 +1,11 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:icalendar-2023 +BEGIN:VEVENT +UID:1 +SUMMARY: Some event ':' +DTSTART;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T160000 +DTEND;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T163000 +DTSTAMP:20211004T150245Z +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_526_calendar_with_events.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_526_calendar_with_events.ics new file mode 100644 index 0000000..2406497 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_526_calendar_with_events.ics @@ -0,0 +1,18 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:icalendar-2023 +BEGIN:VEVENT +UID:1 +SUMMARY: Some event ':' +DTSTART;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T160000 +DTEND;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T163000 +DTSTAMP:20211004T150245Z +END:VEVENT +BEGIN:VEVENT +UID:2 +SUMMARY: Some other event +DTSTART;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T164000 +DTEND;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T165000 +DTSTAMP:20211004T150245Z +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_526_calendar_with_shuffeled_events.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_526_calendar_with_shuffeled_events.ics new file mode 100644 index 0000000..6833bda --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_526_calendar_with_shuffeled_events.ics @@ -0,0 +1,18 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:icalendar-2023 +BEGIN:VEVENT +UID:2 +SUMMARY: Some other event +DTSTART;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T164000 +DTEND;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T165000 +DTSTAMP:20211004T150245Z +END:VEVENT +BEGIN:VEVENT +UID:1 +SUMMARY: Some event ':' +DTSTART;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T160000 +DTEND;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T163000 +DTSTAMP:20211004T150245Z +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_722_missing_VTIMEZONE_custom.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_722_missing_VTIMEZONE_custom.ics new file mode 100644 index 0000000..f7738b1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_722_missing_VTIMEZONE_custom.ics @@ -0,0 +1,5 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +DTSTART;TZID=CUSTOM_tzid;VALUE=DATE-TIME:20140829T080000 +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_722_missing_timezones.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_722_missing_timezones.ics new file mode 100644 index 0000000..225b64e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_722_missing_timezones.ics @@ -0,0 +1,20 @@ +BEGIN:VCALENDAR +DESCRIPTION:We leave the timezones out but we use common names so that they are added. +BEGIN:VEVENT +DTSTART;TZID=America/New_York;VALUE=DATE-TIME:20140829T080000 +DTEND;TZID=America/Los_Angeles;VALUE=DATE-TIME:20140829T080000 +RDATE;VALUE=PERIOD;TZID=Europe/Berlin:20240913T120000/PT2H +SUMMARY:an event with a custom tz name +END:VEVENT +BEGIN:VEVENT +RECURRENCE-ID;TZID=Europe/Moscow:20190309T020000 +END:VEVENT +BEGIN:VTODO +DUE;TZID=Asia/Singapore;VALUE=DATE-TIME:20140829T080000 +END:VTODO +BEGIN:VEVENT +RDATE;TZID=Mexico/General:20190309T020000 +RDATE;TZID=America/Noronha:20190309T020000 +DTSTAMP:19920901T130000Z +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_722_timezone_transition_ambiguity.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_722_timezone_transition_ambiguity.ics new file mode 100644 index 0000000..83a3cc1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_722_timezone_transition_ambiguity.ics @@ -0,0 +1,44 @@ +BEGIN:VCALENDAR +BEGIN:VTIMEZONE +TZID:MyTimezone +BEGIN:STANDARD +COMMENT:The timezone starts at 2024-01-01 with +12h offset +TZOFFSETFROM:+1000 +TZOFFSETTO:+1200 +DTSTART:20240101T000000 +TZNAME:winter +END:STANDARD +BEGIN:DAYLIGHT +COMMENT:The timezone goes from +12h to +10h at 8 am +TZOFFSETFROM:+1200 +TZOFFSETTO:+1000 +DTSTART:20240505T080000 +TZNAME:summer +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +UID:0 +SUMMARY:This event is clearly in winter +DTSTART;TZID=MyTimezone;VALUE=DATE-TIME:20240303T080000 +X-TZNAME:winter +END:VEVENT +BEGIN:VEVENT +UID:1 +SUMMARY:This event is clearly in summer +X-TZNAME:summer +DTSTART;TZID=MyTimezone;VALUE=DATE-TIME:20240803T080000 +END:VEVENT +BEGIN:VEVENT +UID:2 +SUMMARY:Transition is from 8am -> 6am, so 8:00:01 is summer +X-TZNAME:summer +DTSTART;TZID=MyTimezone;VALUE=DATE-TIME:20240505T080001 +END:VEVENT +BEGIN:VEVENT +UID:3 +SUMMARY:Transition is from 8am -> 6am, so 7:00:01 is winter + RFC5545 does not allow us to be at the later TZ. +X-TZNAME:winter +DTSTART;TZID=MyTimezone;VALUE=DATE-TIME:20240505T070001 +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_798_freebusy.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_798_freebusy.ics new file mode 100644 index 0000000..3f04d52 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_798_freebusy.ics @@ -0,0 +1,12 @@ +BEGIN:VCALENDAR +BEGIN:VFREEBUSY +FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:19970308T160000Z/PT8H30M +END:VFREEBUSY +BEGIN:VFREEBUSY +FREEBUSY:19970308T160000Z/PT3H,19970308T200000Z/PT1H +END:VFREEBUSY +BEGIN:VFREEBUSY +FREEBUSY;FBTYPE=FREE:19970308T160000Z/PT3H,19970308T200000Z/PT1H + ,19970308T230000Z/19970309T000000Z +END:VFREEBUSY +END:VCALENDAR \ No newline at end of file diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_798_related_to.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_798_related_to.ics new file mode 100644 index 0000000..76d75a8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_798_related_to.ics @@ -0,0 +1,8 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +RELATED-TO:jsmith.part7.19960817T083000.xyzMail@example.com +END:VEVENT +BEGIN:VEVENT +RELATED-TO;RELTYPE=SIBLING:19960401-080045-4000F192713@example.com +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_836_do_not_quote_tzid.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_836_do_not_quote_tzid.ics new file mode 100644 index 0000000..cbd3c4f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_836_do_not_quote_tzid.ics @@ -0,0 +1,27 @@ +BEGIN:VCALENDAR +METHOD:PUBLISH +PRODID:Microsoft Exchange Server 2010 +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:Eastern Standard Time +BEGIN:STANDARD +DTSTART:16010101T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=11 +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:16010101T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SU;BYMONTH=3 +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +UID:minimal-demo-event-est-20241028@example.com +SUMMARY:Anonymous Test Event for TZID +DTSTART;TZID=Eastern Standard Time:20241028T170000 +DTEND;TZID=Eastern Standard Time:20241028T180000 +DTSTAMP:20250514T023916Z +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/multiple_calendar_components.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/multiple_calendar_components.ics new file mode 100644 index 0000000..dbbde27 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/multiple_calendar_components.ics @@ -0,0 +1,80 @@ +BEGIN:VCALENDAR +VERSION + + :2.0 +PRODID + + :-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN +METHOD + + :PUBLISH +BEGIN:VEVENT +UID + + :956630271 +SUMMARY + + :Christmas Day +CLASS + + :PUBLIC +X-MOZILLA-ALARM-DEFAULT-UNITS + + :minutes +X-MOZILLA-ALARM-DEFAULT-LENGTH + + :15 +X-MOZILLA-RECUR-DEFAULT-UNITS + + :weeks +X-MOZILLA-RECUR-DEFAULT-INTERVAL + + :1 +DTSTART + + ;VALUE=DATE + :20031225 +DTEND + + ;VALUE=DATE + :20031226 +DTSTAMP + + :20020430T114937Z +END:VEVENT +END:VCALENDAR +BEGIN:VCALENDAR +VERSION + :2.0 +PRODID + :-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN +METHOD + :PUBLISH +BEGIN:VEVENT +UID + :911737808 +SUMMARY + :Boxing Day +CLASS + :PUBLIC +X-MOZILLA-ALARM-DEFAULT-UNITS + :minutes +X-MOZILLA-ALARM-DEFAULT-LENGTH + :15 +X-MOZILLA-RECUR-DEFAULT-UNITS + :weeks +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :1 +DTSTART + ;VALUE=DATE + :20030501 +DTSTAMP + :20020430T114937Z +END:VEVENT +BEGIN:VEVENT +UID + :wh4t3v3r +DTSTART;VALUE=DATE:20031225 +SUMMARY:Christmas again! +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/pacific_fiji.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/pacific_fiji.ics new file mode 100644 index 0000000..0e231e0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/pacific_fiji.ics @@ -0,0 +1,52 @@ +BEGIN:VCALENDAR +PRODID:-//tzurl.org//NONSGML Olson 2014g//EN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:custom_Pacific/Fiji +TZURL:http://tzurl.org/zoneinfo/Pacific/Fiji +X-LIC-LOCATION:Pacific/Fiji +BEGIN:DAYLIGHT +TZOFFSETFROM:+1200 +TZOFFSETTO:+1300 +DTSTART:20101024T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYMONTHDAY=21,22,23,24,25,26,27;BYDAY=SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+1300 +TZOFFSETTO:+1200 +DTSTART:20140119T020000 +RRULE:FREQ=YEARLY;BYMONTH=1;BYMONTHDAY=18,19,20,21,22,23,24;BYDAY=SU +END:STANDARD +BEGIN:STANDARD +TZOFFSETFROM:+115544 +TZOFFSETTO:+1200 +DTSTART:19151026T000000 +RDATE:19151026T000000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETFROM:+1200 +TZOFFSETTO:+1300 +DTSTART:19981101T020000 +RDATE:19981101T020000 +RDATE:19991107T020000 +RDATE:20091129T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+1300 +TZOFFSETTO:+1200 +DTSTART:19990228T030000 +RDATE:19990228T030000 +RDATE:20000227T030000 +RDATE:20100328T030000 +RDATE:20110306T030000 +RDATE:20120122T030000 +RDATE:20130120T030000 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +UID:noend123 +DTSTART;TZID=custom_Pacific/Fiji;VALUE=DATE-TIME:20140829T080000 +DTSTART;TZID=custom_Pacific/Fiji;VALUE=DATE-TIME:20140829T100000 +SUMMARY:an event with a custom tz name +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/parsing_error.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/parsing_error.ics new file mode 100644 index 0000000..08d03b6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/parsing_error.ics @@ -0,0 +1,21 @@ +BEGIN:VCALENDAR +PRODID:-//Google Inc//Google Calendar 70.9054//EN +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:PUBLISH +BEGIN:VEVENT +DESCRIPTION:Perfectly OK event +DTSTART;VALUE=DATE:20080303 +DTEND;VALUE=DATE:20080304 +RRULE:FREQ=DAILY;UNTIL=20080323T235959Z +EXDATE;VALUE=DATE:20080311 +END:VEVENT +BEGIN:VEVENT +DESCRIPTION:Wrong event +DTSTART;VALUE=DATE:20080303 +DTEND;VALUE=DATE:20080304 +RRULE:FREQ=DAILY;UNTIL=20080323T235959Z +EXDATE;VALUE=DATE:20080311 +EXDATE;VALUE=DATE: +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/parsing_error_in_UTC_offset.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/parsing_error_in_UTC_offset.ics new file mode 100644 index 0000000..46eef74 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/parsing_error_in_UTC_offset.ics @@ -0,0 +1,11 @@ +BEGIN:VCALENDAR +BEGIN:VTIMEZONE +TZID:Europe/Prague +BEGIN:STANDARD +DTSTART:18500101T000000 +TZNAME:PMT +TZOFFSETFROM:+5744 +TZOFFSETTO:+5744 +END:STANDARD +END:VTIMEZONE +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/period_with_timezone.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/period_with_timezone.ics new file mode 100644 index 0000000..e82c10f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/period_with_timezone.ics @@ -0,0 +1,32 @@ +BEGIN:VCALENDAR +VERSION:2.0 +X-WR-CALNAME;VALUE=TEXT:Test RDATE +BEGIN:VTIMEZONE +TZID:America/Vancouver +BEGIN:STANDARD +DTSTART:20221106T020000 +TZOFFSETFROM:-0700 +TZOFFSETTO:-0800 +RDATE:20231105T020000 +TZNAME:PST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:20230312T020000 +TZOFFSETFROM:-0800 +TZOFFSETTO:-0700 +RDATE:20240310T020000 +TZNAME:PDT +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +UID:1 +DESCRIPTION:Test RDATE +DTSTART;TZID=America/Vancouver:20230920T120000 +DTEND;TZID=America/Vancouver:20230920T140000 +EXDATE;TZID=America/Vancouver:20231220T120000 +RDATE;VALUE=PERIOD;TZID=America/Vancouver:20231213T120000/20231213T150000 +RRULE:FREQ=MONTHLY;COUNT=9;INTERVAL=1;BYDAY=+3WE;BYMONTH=1,2,3,4,5,9,10,11, + 12;WKST=MO +SUMMARY:Test RDATE +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/pr_480_summary_with_colon.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/pr_480_summary_with_colon.ics new file mode 100644 index 0000000..96b6917 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/pr_480_summary_with_colon.ics @@ -0,0 +1,7 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +SUMMARY:Example calendar with a ': ' in the summary +END:VEVENT +BEGIN:VEVENT +SUMMARY:Another event with a ': ' in the summary +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/property_params.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/property_params.ics new file mode 100644 index 0000000..7563d2f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/property_params.ics @@ -0,0 +1,21 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID://RESEARCH IN MOTION//BIS 3.0 +METHOD:REQUEST +BEGIN:VEVENT +SEQUENCE:2 +X-RIM-REVISION:0 +SUMMARY:Test meeting from BB +X-MICROSOFT-CDO-ALLDAYEVENT:TRUE +CLASS:PUBLIC +ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN="RembrandXS":MAILTO:rembrand@xs4all.nl +ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN="RembrandDX":MAILTO:rembrand@daxlab.com +ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN="RembrandSB":MAILTO:rembspam@xs4all.nl +UID:XRIMCAL-628059586-522954492-9750559 +DTSTART;VALUE=DATE:20120814 +DTEND;VALUE=DATE:20120815 +DESCRIPTION:Test meeting from BB +DTSTAMP:20120813T151458Z +ORGANIZER:mailto:rembrand@daxlab.com +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/rfc_5545_RDATE_example.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/rfc_5545_RDATE_example.ics new file mode 100644 index 0000000..d0fe21b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/rfc_5545_RDATE_example.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:0 +RDATE:19970714T123000 +END:VEVENT +BEGIN:VEVENT +UID:1 +RDATE:19970714T123000Z +END:VEVENT +BEGIN:VEVENT +UID:2 +RDATE;TZID=America/New_York:19970714T083000 +END:VEVENT +BEGIN:VEVENT +UID:3 +RDATE;VALUE=PERIOD:19960403T020000Z/19960403T040000Z, + 19960404T010000Z/PT3H +END:VEVENT +BEGIN:VEVENT +UID:4 +RDATE;VALUE=DATE:19970101,19970120,19970217,19970421, + 19970526,19970704,19970901,19971014,19971128,19971129,19971225 +END:VEVENT +BEGIN:VEVENT +UID:5 +RDATE;VALUE=DATE:19970101,19970120,19970217,19970421, + 19970526,19970704,19970901,19971014,19971128,19971129,19971225 +RDATE;VALUE=PERIOD:19960403T020000Z/19960403T040000Z, + 19960404T010000Z/PT3H +RDATE:19970714T123000Z +RDATE;TZID=America/New_York:19970714T083000 +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/rfc_6868.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/rfc_6868.ics new file mode 100644 index 0000000..a3dd173 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/rfc_6868.ics @@ -0,0 +1,6 @@ +BEGIN:VCALENDAR +X-PARAM;NEWLINE=^n;ALL=^^^'^n;UNKNOWN=^a^ ^asd:asd +BEGIN:VEVENT +ATTENDEE;CN=George Herman ^'Babe^' Ruth:mailto:babe@example.com +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/rfc_7529.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/rfc_7529.ics new file mode 100644 index 0000000..1ba3e1f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/rfc_7529.ics @@ -0,0 +1,29 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID://RESEARCH IN MOTION//BIS 3.0 +METHOD:REQUEST +BEGIN:VEVENT +UID:4.3.1 +DTSTART;VALUE=DATE:20130210 +RRULE:RSCALE=CHINESE;FREQ=YEARLY +SUMMARY:Chinese New Year +END:VEVENT +BEGIN:VEVENT +UID:4.3.2 +DTSTART;VALUE=DATE:20130906 +RRULE:RSCALE=ETHIOPIC;FREQ=MONTHLY;BYMONTH=13 +SUMMARY:First day of 13th month +END:VEVENT +BEGIN:VEVENT +UID:4.3.3 +DTSTART;VALUE=DATE:20140208 +RRULE:RSCALE=HEBREW;FREQ=YEARLY;BYMONTH=5L;BYMONTHDAY=8;SKIP=FORWARD +SUMMARY:Anniversary +END:VEVENT +BEGIN:VEVENT +UID:4.3.4 +DTSTART;VALUE=DATE:20120229 +RRULE:RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=FORWARD +SUMMARY:Anniversary +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/small_bad_calendar.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/small_bad_calendar.ics new file mode 100644 index 0000000..94228f1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/small_bad_calendar.ics @@ -0,0 +1,3 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/time.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/time.ics new file mode 100644 index 0000000..d730a4c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/time.ics @@ -0,0 +1,3 @@ +BEGIN:VCALENDAR +X-SOMETIME;VALUE=TIME:172010 +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/timezone_rdate.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/timezone_rdate.ics new file mode 100644 index 0000000..5a4f7a9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/timezone_rdate.ics @@ -0,0 +1,55 @@ +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:posix/Europe/Vaduz +BEGIN:STANDARD +TZNAME:CET +TZOFFSETFROM:+002946 +TZOFFSETTO:+0100 +DTSTART:19011213T211538 +RDATE;VALUE=DATE-TIME:19011213T211538 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:CEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19810329T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZNAME:CEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19410505T010000 +RDATE;VALUE=DATE-TIME:19410505T010000 +RDATE;VALUE=DATE-TIME:19420504T010000 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19810927T030000 +RRULE:FREQ=YEARLY;COUNT=15;BYDAY=-1SU;BYMONTH=9 +END:STANDARD +BEGIN:STANDARD +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19961027T030000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +END:STANDARD +BEGIN:STANDARD +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19411006T020000 +RDATE;VALUE=DATE-TIME:19411006T020000 +RDATE;VALUE=DATE-TIME:19421005T020000 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +UID:123 +DTSTART;TZID=posix/Europe/Vaduz:20120213T100000 +SUMMARY=testevent +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/timezone_same_start.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/timezone_same_start.ics new file mode 100644 index 0000000..a221ae3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/timezone_same_start.ics @@ -0,0 +1,27 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:Microsoft Exchange Server 2010 +METHOD:REQUEST +BEGIN:VTIMEZONE +TZID:Pacific Standard Time +BEGIN:STANDARD +DTSTART:16010101T020000 +RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=11 +TZOFFSETFROM:-0700 +TZOFFSETTO:-0800 +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:16010101T020000 +RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SU;BYMONTH=3 +TZOFFSETFROM:-0800 +TZOFFSETTO:-0700 +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +SUMMARY;LANGUAGE=en-US:Test 4 +DTSTART;TZID="Pacific Standard Time":20170224T120000 +DTEND;TZID="Pacific Standard Time":20170224T123000 +DTSTAMP:20170224T180431Z +UID:040000008200E00074C5B7101A82E0080000000090E19664858ED20100000000000000 +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/timezone_same_start_and_offset.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/timezone_same_start_and_offset.ics new file mode 100644 index 0000000..0ec7ba0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/timezone_same_start_and_offset.ics @@ -0,0 +1,23 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:Microsoft Exchange Server 2010 +BEGIN:VTIMEZONE +TZID:Tokyo Standard Time +BEGIN:STANDARD +DTSTART:16010101T000000 +TZOFFSETFROM:+0900 +TZOFFSETTO:+0900 +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:16010101T000000 +TZOFFSETFROM:+0900 +TZOFFSETTO:+0900 +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +DTSTART;TZID="Tokyo Standard Time":20170224T120000 +DTEND;TZID="Tokyo Standard Time":20170224T123000 +UID:blafoobar +SUMMARY:this is an event +END:VEVENT +END:VCALENDARD diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/timezoned.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/timezoned.ics new file mode 100644 index 0000000..5878b72 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/timezoned.ics @@ -0,0 +1,36 @@ +BEGIN:VCALENDAR +PRODID:-//Plone.org//NONSGML plone.app.event//EN +VERSION:2.0 +X-WR-CALNAME:test create calendar +X-WR-CALDESC:icalendar test +X-WR-RELCALID:12345 +X-WR-TIMEZONE:Europe/Vienna +BEGIN:VTIMEZONE +TZID:Europe/Vienna +X-LIC-LOCATION:Europe/Vienna +BEGIN:DAYLIGHT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +TZNAME:CEST +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +TZNAME:CET +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DTSTART;TZID=Europe/Vienna:20120213T100000 +DTEND;TZID=Europe/Vienna:20120217T180000 +DTSTAMP:20101010T091010Z +CREATED:20101010T091010Z +UID:123456 +SUMMARY:artsprint 2012 +DESCRIPTION:sprinting at the artsprint +LOCATION:aka bild, wien +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/x_location.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/x_location.ics new file mode 100644 index 0000000..97ffbe7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/x_location.ics @@ -0,0 +1,48 @@ +BEGIN:VCALENDAR +PRODID:-//Google Inc//Google Calendar 70.9054//EN +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:PUBLISH +X-WR-CALNAME:ITC +X-WR-TIMEZONE:Europe/Zurich +X-WR-CALDESC:ITC Bookings +BEGIN:VTIMEZONE +TZID:Europe/Zurich +X-LIC-LOCATION:Europe/Zurich +BEGIN:DAYLIGHT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +TZNAME:CEST +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +TZNAME:CET +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DTSTART;TZID=Europe/Zurich:20161028T140000 +DTEND;TZID=Europe/Zurich:20161028T143000 +RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR +DTSTAMP:20161031T192828Z +UID:BFE33ADD-5553-48B5-B5A5-F9DA5CA4C393 +CREATED:20161029T121229Z +DESCRIPTION:Some Description +LAST-MODIFIED:20161029T121229Z +LOCATION:Roadstar 16\n12764 Happyville\nDenmark +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:Daily Sync +TRANSP:OPAQUE +X-APPLE-STRUCTURED-LOCATION;VALUE=URI;X-ADDRESS="Röadstar 16\n12764 Happyvi + lle\nDenmark";X-APPLE-MAPKIT-HANDLE=CAESARoSCWYTYFhHQBEGfw4hQCIBDQoHRGVubW + FyaxJES0hhcHB5dmlsbGUqSGFwcHl2aWxsZTIHSGFwcHl2aWxsZToEMTI3NjRCDQpSb2Fkc3Rh + cloCMTZiUm9hZHN0YXIgMTYBEU1vcmRvcgENCk1vcmRvcioSUm9hZHN0YXIgMTYyUm9hZHN0YX + IgMTYxMjc2NCBIYXBweXZpbGxlMgdEZW5tYXJrOThA=;X-APPLE-RADIUS=49.913058665846 + 98;X-APPLE-REFERENCEFRAME=1;X-TITLE=:geo:52.382762,7.528319 +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/conftest.py b/.venv/lib/python3.9/site-packages/icalendar/tests/conftest.py new file mode 100644 index 0000000..47efb8c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/conftest.py @@ -0,0 +1,358 @@ +try: + from backports import zoneinfo # type: ignore # noqa: PGH003 +except ImportError: + import zoneinfo +from typing import Generator + +import pytest + +import icalendar + +from . import timezone_ids + +try: + import pytz +except ImportError: + pytz = None +import itertools +import sys +import uuid +from pathlib import Path + +from dateutil import tz + +from icalendar.cal import Calendar, Component +from icalendar.timezone import TZP +from icalendar.timezone import tzp as _tzp + +HAS_PYTZ = pytz is not None +if HAS_PYTZ: + PYTZ_UTC = [ + pytz.utc, + pytz.timezone("UTC"), + ] + PYTZ_IN_TIMEZONE = [ + lambda dt, tzname: pytz.timezone(tzname).localize(dt), + ] + PYTZ_TZP = ["pytz"] +else: + PYTZ_UTC = [] + PYTZ_IN_TIMEZONE = [] + PYTZ_TZP = [] + + +class DataSource: + """A collection of parsed ICS elements (e.g calendars, timezones, events)""" + + def __init__(self, data_source_folder: Path, parser): + self._parser = parser + self._data_source_folder = data_source_folder + + def keys(self): + """Return all the files that could be used.""" + return [ + p.stem + for p in self._data_source_folder.iterdir() + if p.suffix.lower() == ".ics" + ] + + def __getitem__(self, attribute): + """Parse a file and return the result stored in the attribute.""" + if attribute.endswith(".ics"): + source_file = attribute + attribute = attribute[:-4] + else: + source_file = attribute + ".ics" + source_path = self._data_source_folder / source_file + if not source_path.is_file(): + raise AttributeError(f"{source_path} does not exist.") + with source_path.open("rb") as f: + raw_ics = f.read() + source = self._parser(raw_ics) + if not isinstance(source, list): + source.raw_ics = raw_ics + source.source_file = source_file + self.__dict__[attribute] = source + return source + + def __contains__(self, key): + """key in self.keys()""" + if key.endswith(".ics"): + key = key[:-4] + return key in self.keys() + + def __getattr__(self, key): + return self[key] + + def __repr__(self): + return repr(self.__dict__) + + @property + def multiple(self): + """Return a list of all components parsed.""" + return self.__class__( + self._data_source_folder, lambda data: self._parser(data, multiple=True) + ) + + +HERE = Path(__file__).parent +CALENDARS_FOLDER = HERE / "calendars" +TIMEZONES_FOLDER = HERE / "timezones" +EVENTS_FOLDER = HERE / "events" +ALARMS_FOLDER = HERE / "alarms" + + +@pytest.fixture(scope="module") +def calendars(tzp): + return DataSource(CALENDARS_FOLDER, icalendar.Calendar.from_ical) + + +@pytest.fixture(scope="module") +def timezones(tzp): + return DataSource(TIMEZONES_FOLDER, icalendar.Timezone.from_ical) + + +@pytest.fixture(scope="module") +def events(tzp): + return DataSource(EVENTS_FOLDER, icalendar.Event.from_ical) + + +@pytest.fixture(scope="module") +def alarms(tzp): + return DataSource(ALARMS_FOLDER, icalendar.Alarm.from_ical) + + +@pytest.fixture(params=PYTZ_UTC + [zoneinfo.ZoneInfo("UTC"), tz.UTC, tz.gettz("UTC")]) +def utc(request, tzp): + return request.param + + +@pytest.fixture( + params=PYTZ_IN_TIMEZONE + + [ + lambda dt, tzname: dt.replace(tzinfo=tz.gettz(tzname)), + lambda dt, tzname: dt.replace(tzinfo=zoneinfo.ZoneInfo(tzname)), + ] +) +def in_timezone(request, tzp): + return request.param + + +# exclude broken calendars here +ICS_FILES_EXCLUDE = ( + "big_bad_calendar.ics", + "issue_104_broken_calendar.ics", + "small_bad_calendar.ics", + "multiple_calendar_components.ics", + "pr_480_summary_with_colon.ics", + "parsing_error_in_UTC_offset.ics", + "parsing_error.ics", +) +ICS_FILES = [ + file.name + for file in itertools.chain( + CALENDARS_FOLDER.iterdir(), TIMEZONES_FOLDER.iterdir(), EVENTS_FOLDER.iterdir() + ) + if file.name not in ICS_FILES_EXCLUDE +] + + +@pytest.fixture(params=ICS_FILES) +def ics_file(tzp, calendars, timezones, events, request): + """An example ICS file.""" + ics_file = request.param + print("example file:", ics_file) + for data in calendars, timezones, events: + if ics_file in data: + return data[ics_file] + raise ValueError(f"Could not find file {ics_file}.") + + +FUZZ_V1 = [key for key in CALENDARS_FOLDER.iterdir() if "fuzz-testcase" in str(key)] + + +@pytest.fixture(params=FUZZ_V1) +def fuzz_v1_calendar(request): + """Clusterfuzz calendars.""" + return request.param + + +@pytest.fixture +def x_sometime(): + """Map x_sometime to time""" + icalendar.cal.types_factory.types_map["X-SOMETIME"] = "time" + yield + icalendar.cal.types_factory.types_map.pop("X-SOMETIME") + + +@pytest.fixture +def factory(): + """Return a new component factory.""" + return icalendar.ComponentFactory() + + +@pytest.fixture +def vUTCOffset_ignore_exceptions(): + icalendar.vUTCOffset.ignore_exceptions = True + yield + icalendar.vUTCOffset.ignore_exceptions = False + + +@pytest.fixture +def event_component(tzp): + """Return an event component.""" + c = Component() + c.name = "VEVENT" + return c + + +@pytest.fixture +def c(tzp): + """Return an empty component.""" + c = Component() + return c + + +comp = c + + +@pytest.fixture +def calendar_component(tzp): + """Return an empty component.""" + c = Component() + c.name = "VCALENDAR" + return c + + +@pytest.fixture +def filled_event_component(c, calendar_component): + """Return an event with some values and add it to calendar_component.""" + e = Component(summary="A brief history of time") + e.name = "VEVENT" + e.add("dtend", "20000102T000000", encode=0) + e.add("dtstart", "20000101T000000", encode=0) + calendar_component.add_component(e) + return e + + +@pytest.fixture() +def calendar_with_resources(tzp): + c = Calendar() + c["resources"] = 'Chair, Table, "Room: 42"' + return c + + +@pytest.fixture(scope="module") +def tzp(tzp_name) -> Generator[TZP, None, None]: + """The timezone provider.""" + _tzp.use(tzp_name) + yield _tzp + _tzp.use_default() + + +@pytest.fixture(params=PYTZ_TZP + ["zoneinfo"]) +def other_tzp(request, tzp): + """This is annother timezone provider. + + The purpose here is to cross test: pytz <-> zoneinfo. + tzp as parameter makes sure we test the cross product. + """ + return TZP(request.param) + + +@pytest.fixture +def pytz_only(tzp, tzp_name) -> str: + """Skip tests that are not running under pytz.""" + assert tzp.uses_pytz() + return tzp_name + + +@pytest.fixture +def zoneinfo_only(tzp, request, tzp_name) -> str: + """Skip tests that are not running under zoneinfo.""" + assert tzp.uses_zoneinfo() + return tzp_name + + +@pytest.fixture +def no_pytz(tzp_name) -> str: + """Do not run tests with pytz.""" + assert tzp_name != "pytz" + return tzp_name + + +@pytest.fixture +def no_zoneinfo(tzp_name) -> str: + """Do not run tests with zoneinfo.""" + assert tzp_name != "zoneinfo" + return tzp_name + + +def pytest_generate_tests(metafunc): + """Parametrize without skipping: + + tzp_name will be parametrized according to the use of + - pytz_only + - zoneinfo_only + - no_pytz + - no_zoneinfo + + See https://docs.pytest.org/en/6.2.x/example/parametrize.html#deferring-the-setup-of-parametrized-resources + """ + if "tzp_name" in metafunc.fixturenames: + tzp_names = PYTZ_TZP + ["zoneinfo"] + if "zoneinfo_only" in metafunc.fixturenames: + tzp_names = ["zoneinfo"] + if "pytz_only" in metafunc.fixturenames: + tzp_names = PYTZ_TZP + assert not ( + "zoneinfo_only" in metafunc.fixturenames + and "pytz_only" in metafunc.fixturenames + ), "Use pytz_only or zoneinfo_only but not both!" + for name in ["pytz", "zoneinfo"]: + if f"no_{name}" in metafunc.fixturenames and name in tzp_names: + tzp_names.remove(name) + metafunc.parametrize("tzp_name", tzp_names, scope="module") + + +class DoctestZoneInfo(zoneinfo.ZoneInfo): + """Constent ZoneInfo representation for tests.""" + + def __repr__(self): + return f"ZoneInfo(key={self.key!r})" + + +def doctest_print(obj): + """doctest print""" + if isinstance(obj, bytes): + obj = obj.decode("UTF-8") + print(str(obj).strip().replace("\r\n", "\n").replace("\r", "\n")) + + +def doctest_import(name, *args, **kw): + """Replace the import mechanism to skip the whole doctest if we import pytz.""" + if name == "pytz": + return pytz + return __import__(name, *args, **kw) + + +@pytest.fixture +def env_for_doctest(monkeypatch): + """Modify the environment to make doctests run.""" + monkeypatch.setitem(sys.modules, "zoneinfo", zoneinfo) + monkeypatch.setattr(zoneinfo, "ZoneInfo", DoctestZoneInfo) + from icalendar.timezone.zoneinfo import ZONEINFO + uid = uuid.UUID("d755cef5-2311-46ed-a0e1-6733c9e15c63", version=4) + monkeypatch.setattr(uuid, "uuid4", lambda: uid) + + monkeypatch.setattr(ZONEINFO, "utc", zoneinfo.ZoneInfo("UTC")) + return {"print": doctest_print} + + +@pytest.fixture(params=timezone_ids.TZIDS) +def tzid(request: pytest.FixtureRequest) -> str: + """Return a timezone id to be used with pytz or zoneinfo. + + This goes through all the different timezones possible. + """ + return request.param diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_escaped_character1.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_escaped_character1.ics new file mode 100644 index 0000000..d082b9f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_escaped_character1.ics @@ -0,0 +1,3 @@ +BEGIN:VEVENT +ORGANIZER;CN=Society\, 2014:that +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_escaped_character2.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_escaped_character2.ics new file mode 100644 index 0000000..88b849d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_escaped_character2.ics @@ -0,0 +1,3 @@ +BEGIN:VEVENT +ORGANIZER;CN=Society\\ 2014:that +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_escaped_character3.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_escaped_character3.ics new file mode 100644 index 0000000..3c657c4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_escaped_character3.ics @@ -0,0 +1,3 @@ +BEGIN:VEVENT +ORGANIZER;CN=Society\; 2014:that +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_escaped_character4.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_escaped_character4.ics new file mode 100644 index 0000000..037c1c4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_escaped_character4.ics @@ -0,0 +1,3 @@ +BEGIN:VEVENT +ORGANIZER;CN=Society\: 2014:that +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_escaped_characters.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_escaped_characters.ics new file mode 100644 index 0000000..c9671d1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_escaped_characters.ics @@ -0,0 +1,3 @@ +BEGIN:VEVENT +ORGANIZER;CN=that\, that\; %th%%at%\ that\::это\, то\; that\ %th%%at%\: +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_recurrence.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_recurrence.ics new file mode 100644 index 0000000..c2dd182 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_recurrence.ics @@ -0,0 +1,7 @@ +BEGIN:VEVENT +DTSTART:19960401T010000 +DTEND:19960401T020000 +RRULE:FREQ=DAILY;COUNT=100 +EXDATE:19960402T010000Z,19960403T010000Z,19960404T010000Z +SUMMARY:A recurring event with exdates +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_recurrence_exdates_on_different_lines.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_recurrence_exdates_on_different_lines.ics new file mode 100644 index 0000000..8f329d7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_recurrence_exdates_on_different_lines.ics @@ -0,0 +1,12 @@ +BEGIN:VEVENT +DTSTART;TZID=Europe/Vienna:20120327T100000 +DTEND;TZID=Europe/Vienna:20120327T180000 +RRULE:FREQ=WEEKLY;UNTIL=20120703T080000Z;BYDAY=TU +EXDATE;TZID=Europe/Vienna:20120529T100000 +EXDATE;TZID=Europe/Vienna:20120403T100000 +EXDATE;TZID=Europe/Vienna:20120410T100000 +EXDATE;TZID=Europe/Vienna:20120501T100000 +EXDATE;TZID=Europe/Vienna:20120417T100000 +DTSTAMP:20130716T120638Z +SUMMARY:A Recurring event with multiple exdates, one per line. +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_rsvp.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_rsvp.ics new file mode 100644 index 0000000..01963d5 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_rsvp.ics @@ -0,0 +1,3 @@ +BEGIN:VEVENT +ATTENDEE;RSVP=TRUE:mailto:someone@example.com +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_unicode_fields.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_unicode_fields.ics new file mode 100644 index 0000000..037e505 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_unicode_fields.ics @@ -0,0 +1,9 @@ +BEGIN:VEVENT +DTSTART:20101010T100000Z +DTEND:20101010T120000Z +CREATED:20101010T100000Z +UID:123456 +SUMMARY:Non-ASCII Test: ÄÖÜ äöü € +DESCRIPTION:icalendar should be able to handle non-ascii: €äüöÄÜÖ. +LOCATION:Tribstrül +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_unicode_organizer.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_unicode_organizer.ics new file mode 100644 index 0000000..2fb3be1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/event_with_unicode_organizer.ics @@ -0,0 +1,3 @@ +BEGIN:VEVENT +ORGANIZER;CN="Джон Доу":mailto:john.doe@example.org +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_100_transformed_doctests_into_unittests.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_100_transformed_doctests_into_unittests.ics new file mode 100644 index 0000000..803f567 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_100_transformed_doctests_into_unittests.ics @@ -0,0 +1,3 @@ +BEGIN:VEVENT +SUMMARY;LANGUAGE=ru:te +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_101_icalendar_chokes_on_umlauts_in_organizer.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_101_icalendar_chokes_on_umlauts_in_organizer.ics new file mode 100644 index 0000000..27a6d09 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_101_icalendar_chokes_on_umlauts_in_organizer.ics @@ -0,0 +1,14 @@ +BEGIN:VEVENT +SUMMARY:wichtiger termin 1 +DTSTART:20130416T100000Z +DTEND:20130416T110000Z +DTSTAMP:20130416T092616Z +UID:20130416112341.10064jz0k4j7uem8@acmenet.de +CLASS:PUBLIC +CREATED:20130416T092341Z +LAST-MODIFIED:20130416T092341Z +LOCATION:im büro +ORGANIZER;CN="acme, ädmin":mailto:adm-acme@mydomain.de +STATUS:CONFIRMED +TRANSP:OPAQUE +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_104_mark_events_broken.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_104_mark_events_broken.ics new file mode 100644 index 0000000..a3ecea2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_104_mark_events_broken.ics @@ -0,0 +1,10 @@ +BEGIN:VEVENT +DTSTART:20140401T000000Z +DTEND:20140401T010000Z +DTSTAMP:20140401T000000Z +SUMMARY:Broken Eevnt +CLASS:PUBLIC +STATUS:CONFIRMED +TRANSP:OPAQUE +X +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_112_missing_tzinfo_on_exdate.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_112_missing_tzinfo_on_exdate.ics new file mode 100644 index 0000000..7363ab0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_112_missing_tzinfo_on_exdate.ics @@ -0,0 +1,20 @@ +BEGIN:VEVENT +DTSTART;TZID=America/New_York:20130907T120000 +DTEND;TZID=America/New_York:20130907T170000 +RRULE:FREQ=WEEKLY;BYDAY=FR,SA;UNTIL=20131025T035959Z +EXDATE;TZID=America/New_York:20131012T120000 +EXDATE;TZID=America/New_York:20131011T120000 +DTSTAMP:20131021T025552Z +UID:ak30b02u7858q1oo6ji9dm4mgg@google.com +CREATED:20130903T181453Z +DESCRIPTION:The Fieldhouse and Hard Rock Cafe are working with PhillyRising + to provide live entertainment on Friday and Saturday afternoons throughout + the Summer. +LAST-MODIFIED:20131015T210927Z +LOCATION:12th and Market Streets (weather permitting) +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:Market East Live! +TRANSP:OPAQUE +END:VEVENT + diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_156_RDATE_with_PERIOD.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_156_RDATE_with_PERIOD.ics new file mode 100644 index 0000000..398859c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_156_RDATE_with_PERIOD.ics @@ -0,0 +1,7 @@ +BEGIN:VEVENT +SUMMARY:RDATE period +DTSTART:19961230T020000Z +DTEND:19961230T060000Z +UID:rdate_period +RDATE;VALUE=PERIOD:19970101T180000Z/19970102T070000Z +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_156_RDATE_with_PERIOD_list.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_156_RDATE_with_PERIOD_list.ics new file mode 100644 index 0000000..3202e85 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_156_RDATE_with_PERIOD_list.ics @@ -0,0 +1,8 @@ +BEGIN:VEVENT +SUMMARY:RDATE period +DTSTART:19961230T020000Z +DTEND:19961230T060000Z +UID:rdate_period +RDATE;VALUE=PERIOD:19970101T180000Z/19970102T070000Z,19970109T180000Z/PT5H + 30M +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_157_removes_trailing_semicolon.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_157_removes_trailing_semicolon.ics new file mode 100644 index 0000000..766b56f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_157_removes_trailing_semicolon.ics @@ -0,0 +1,4 @@ +BEGIN:VEVENT +DTSTART:20150325T101010 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU; +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_184_broken_representation_of_period.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_184_broken_representation_of_period.ics new file mode 100644 index 0000000..8b6ed13 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_184_broken_representation_of_period.ics @@ -0,0 +1,6 @@ +BEGIN:VEVENT +DTSTART:20150219T133000 +DTSTAMP:20150219T133000 +UID:1234567 +RDATE;VALUE=PERIOD:20150219T133000/PT10H +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_464_invalid_rdate.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_464_invalid_rdate.ics new file mode 100644 index 0000000..f4e587d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_464_invalid_rdate.ics @@ -0,0 +1,7 @@ +BEGIN:VEVENT +SUMMARY:RDATE period +DTSTART:19961230T020000Z +DTEND:19961230T060000Z +UID:rdate_period +RDATE;VALUE=PERIOD:19970101T180000Z/19970102T070000Z,199709T180000Z/PT5H30M +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_53_description_parsed_properly.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_53_description_parsed_properly.ics new file mode 100644 index 0000000..9edc183 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_53_description_parsed_properly.ics @@ -0,0 +1,19 @@ +BEGIN:VEVENT +DTSTAMP:20120605T003759Z +DTSTART;TZID=America/New_York:20120712T183000 +DTEND;TZID=America/New_York:20120712T213000 +STATUS:CONFIRMED +SUMMARY:DevOps DC Meetup +DESCRIPTION:DevOpsDC\nThursday\, July 12 at 6:30 PM\n\nThis will be a joi + nt meetup / hack night with the DC jQuery Users Group. The idea behind + the hack night: Small teams consisting of at least 1 member...\n\nDeta + ils: http://www.meetup.com/DevOpsDC/events/47635522/ +CLASS:PUBLIC +CREATED:20120111T120339Z +GEO:38.90;-77.01 +LOCATION:Fathom Creative\, Inc. (1333 14th Street Northwest\, Washington + D.C.\, DC 20005) +URL:http://www.meetup.com/DevOpsDC/events/47635522/ +LAST-MODIFIED:20120522T174406Z +UID:event_qtkfrcyqkbnb@meetup.com +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_64_event_with_ascii_summary.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_64_event_with_ascii_summary.ics new file mode 100644 index 0000000..e7cdbf4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_64_event_with_ascii_summary.ics @@ -0,0 +1,3 @@ +BEGIN:VEVENT +SUMMARY:abcdef +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_64_event_with_non_ascii_summary.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_64_event_with_non_ascii_summary.ics new file mode 100644 index 0000000..bed239a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_64_event_with_non_ascii_summary.ics @@ -0,0 +1,3 @@ +BEGIN:VEVENT +SUMMARY:åäö +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_70_rrule_causes_attribute_error.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_70_rrule_causes_attribute_error.ics new file mode 100644 index 0000000..1e062e4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_70_rrule_causes_attribute_error.ics @@ -0,0 +1,12 @@ +BEGIN:VEVENT +CREATED:20081114T072804Z +UID:D449CA84-00A3-4E55-83E1-34B58268853B +DTEND:20070220T180000 +RRULE:FREQ=WEEKLY;INTERVAL=1;UNTIL=20070619T225959 +TRANSP:OPAQUE +SUMMARY:Esb mellon phone conf +DTSTART:20070220T170000 +DTSTAMP:20070221T095412Z +SEQUENCE:0 +END:VEVENT + diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_82_expected_output.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_82_expected_output.ics new file mode 100644 index 0000000..61872f6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/issue_82_expected_output.ics @@ -0,0 +1,3 @@ +BEGIN:VEVENT +ATTACH;ENCODING=BASE64;FMTTYPE=text/plain;VALUE=BINARY:dGV4dA== +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/rfc_9074_example_1.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/rfc_9074_example_1.ics new file mode 100644 index 0000000..97f885f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/rfc_9074_example_1.ics @@ -0,0 +1,16 @@ +BEGIN:VEVENT +CREATED:20210302T151004Z +UID:AC67C078-CED3-4BF5-9726-832C3749F627 +DTSTAMP:20210302T151004Z +DTSTART;TZID=America/New_York:20210302T103000 +DTEND;TZID=America/New_York:20210302T113000 +SUMMARY:Meeting + +BEGIN:VALARM +UID:8297C37D-BA2D-4476-91AE-C1EAA364F8E1 +TRIGGER:-PT15M +DESCRIPTION:Event reminder +ACTION:DISPLAY +END:VALARM + +END:VEVENT \ No newline at end of file diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/rfc_9074_example_2.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/rfc_9074_example_2.ics new file mode 100644 index 0000000..4dfe34c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/rfc_9074_example_2.ics @@ -0,0 +1,25 @@ +BEGIN:VEVENT +CREATED:20210302T151004Z +UID:AC67C078-CED3-4BF5-9726-832C3749F627 +DTSTAMP:20210302T151516Z +DTSTART;TZID=America/New_York:20210302T103000 +DTEND;TZID=America/New_York:20210302T113000 +SUMMARY:Meeting + +BEGIN:VALARM +UID:8297C37D-BA2D-4476-91AE-C1EAA364F8E1 +TRIGGER:-PT15M +DESCRIPTION:Event reminder +ACTION:DISPLAY +ACKNOWLEDGED:20210302T151514Z +END:VALARM + +BEGIN:VALARM +UID:DE7B5C34-83FF-47FE-BE9E-FF41AE6DD097 +TRIGGER;VALUE=DATE-TIME:20210302T152000Z +RELATED-TO;RELTYPE=SNOOZE:8297C37D-BA2D-4476-91AE-C1EAA364F8E1 +DESCRIPTION:Event reminder +ACTION:DISPLAY +END:VALARM + +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/rfc_9074_example_3.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/rfc_9074_example_3.ics new file mode 100644 index 0000000..e0b134f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/rfc_9074_example_3.ics @@ -0,0 +1,25 @@ +BEGIN:VEVENT +CREATED:20210302T151004Z +UID:AC67C078-CED3-4BF5-9726-832C3749F627 +DTSTAMP:20210302T152026Z +DTSTART;TZID=America/New_York:20210302T103000 +DTEND;TZID=America/New_York:20210302T113000 +SUMMARY:Meeting + +BEGIN:VALARM +UID:8297C37D-BA2D-4476-91AE-C1EAA364F8E1 +TRIGGER:-PT15M +DESCRIPTION:Event reminder +ACTION:DISPLAY +ACKNOWLEDGED:20210302T152024Z +END:VALARM + +BEGIN:VALARM +UID:87D690A7-B5E8-4EB4-8500-491F50AFE394 +TRIGGER;VALUE=DATE-TIME:20210302T152500Z +RELATED-TO;RELTYPE=SNOOZE:8297C37D-BA2D-4476-91AE-C1EAA364F8E1 +DESCRIPTION:Event reminder +ACTION:DISPLAY +END:VALARM + +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/rfc_9074_example_4.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/rfc_9074_example_4.ics new file mode 100644 index 0000000..fe2f011 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/rfc_9074_example_4.ics @@ -0,0 +1,26 @@ +BEGIN:VEVENT +CREATED:20210302T151004Z +UID:AC67C078-CED3-4BF5-9726-832C3749F627 +DTSTAMP:20210302T152508Z +DTSTART;TZID=America/New_York:20210302T103000 +DTEND;TZID=America/New_York:20210302T113000 +SUMMARY:Meeting + +BEGIN:VALARM +UID:8297C37D-BA2D-4476-91AE-C1EAA364F8E1 +TRIGGER:-PT15M +DESCRIPTION:Event reminder +ACTION:DISPLAY +ACKNOWLEDGED:20210302T152507Z +END:VALARM + +BEGIN:VALARM +UID:87D690A7-B5E8-4EB4-8500-491F50AFE394 +TRIGGER;VALUE=DATE-TIME:20210302T152500Z +RELATED-TO;RELTYPE=SNOOZE:8297C37D-BA2D-4476-91AE-C1EAA364F8E1 +DESCRIPTION:Event reminder +ACTION:DISPLAY +ACKNOWLEDGED:20210302T152507Z +END:VALARM + +END:VEVENT \ No newline at end of file diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/events/rfc_9074_example_proximity.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/events/rfc_9074_example_proximity.ics new file mode 100644 index 0000000..9f8ceac --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/events/rfc_9074_example_proximity.ics @@ -0,0 +1,14 @@ +BEGIN:VEVENT +BEGIN:VALARM +UID:77D80D14-906B-4257-963F-85B1E734DBB6 +ACTION:DISPLAY +TRIGGER;VALUE=DATE-TIME:19760401T005545Z +DESCRIPTION:Remember to buy milk +PROXIMITY:DEPART +BEGIN:VLOCATION +UID:123456-abcdef-98765432 +NAME:Office +URL:geo:40.443,-79.945;u=10 +END:VLOCATION +END:VALARM +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/fuzzed/__init__.py b/.venv/lib/python3.9/site-packages/icalendar/tests/fuzzed/__init__.py new file mode 100644 index 0000000..2b7e1e4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/fuzzed/__init__.py @@ -0,0 +1,25 @@ +"""This is a collection of test files that are generated from the fuzzer. + +The fuzzer finds the cases in which the icalendar module breaks. +These test cases reproduce the failure. +Some more tests can be added to make sure that the behavior works properly. +""" + + +def fuzz_calendar_v1( + from_ical, calendar_string: str, multiple: bool, should_walk: bool +): + """Take a from_ical function and reproduce the error. + + The calendar_string is a fuzzed input. + """ + cal = from_ical(calendar_string, multiple=multiple) + + if not multiple: + cal = [cal] + for c in cal: + if should_walk: + for event in c.walk("VEVENT"): + event.to_ical() + else: + c.to_ical() diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/fuzzed/generate_python_test_cases_from_downloaded_clusterfuzz_test_cases.sh b/.venv/lib/python3.9/site-packages/icalendar/tests/fuzzed/generate_python_test_cases_from_downloaded_clusterfuzz_test_cases.sh new file mode 100755 index 0000000..1b5eb22 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/fuzzed/generate_python_test_cases_from_downloaded_clusterfuzz_test_cases.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# +# This script generates a test case from a test case file that was downloaded. +# +# You will need to follow the setup instructions here: +# https://google.github.io/oss-fuzz/advanced-topics/reproducing/#reproduce-using-local-source-checkout +# +set -e + +HERE="`dirname \"$0\"`" +OSS_FUZZ_DIRECTORY="$HOME/oss-fuzz" +DOWNLOADS_DIRECTORY="$HOME/Downloads" +LOCAL_ICALENDAR_DIRECTORY="$HERE/../../../../" +PYTHON_TEST_CASE_DIRECTORY="$HERE/../calendars/" +PROJECT_NAME="icalendar" + +echo "### Building Project $PROJECT_NAME" +python "$OSS_FUZZ_DIRECTORY/infra/helper.py" build_fuzzers --sanitizer undefined "$PROJECT_NAME" "$LOCAL_ICALENDAR_DIRECTORY" + +# we capture the output +OUTPUT="`mktemp`" + +# test case files look like this: +# clusterfuzz-testcase-minimized-ical_fuzzer-4878676239712256 +for testcase in "$DOWNLOADS_DIRECTORY/clusterfuzz-testcase-"* +do + echo "### Reproducing $testcase" + python "$OSS_FUZZ_DIRECTORY/infra/helper.py" reproduce "$PROJECT_NAME" ical_fuzzer "$testcase" | tee "$OUTPUT" + if [ $PIPESTATUS -eq 0 ] + then + echo "### Testcase fixed! $testcase" + continue + fi + echo "### Testcase reproduced! $testcase" + TEST_FILE_CONTENT="`cat \"$OUTPUT\" | sed -n '/--- start calendar ---/,/--- end calendar ---/{/--- start calendar ---/b;/--- end calendar ---/b;p}'`" + if [ -z "$TEST_FILE_CONTENT" ] + then + echo "### No test file content for $testcase" + exit 1 + fi + ICS_FILE="$PYTHON_TEST_CASE_DIRECTORY/`basename \"$testcase\"`.ics" + # decode and ignore garbage, see https://stackoverflow.com/a/15490765/1320237 + echo $TEST_FILE_CONTENT | base64 -di > /dev/null + echo "Created $ICS_FILE" +done diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/fuzzed/test_fuzzed_calendars.py b/.venv/lib/python3.9/site-packages/icalendar/tests/fuzzed/test_fuzzed_calendars.py new file mode 100644 index 0000000..31d3ad3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/fuzzed/test_fuzzed_calendars.py @@ -0,0 +1,12 @@ +"""This test tests all fuzzed calendars.""" + +import icalendar +from icalendar.tests.fuzzed import fuzz_calendar_v1 + + +def test_fuzz_v1(fuzz_v1_calendar): + """Test a calendar.""" + with open(fuzz_v1_calendar, "rb") as f: + fuzz_calendar_v1( + icalendar.Calendar.from_ical, f.read(), multiple=True, should_walk=True + ) diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/hypothesis/test_fuzzing.py b/.venv/lib/python3.9/site-packages/icalendar/tests/hypothesis/test_fuzzing.py new file mode 100644 index 0000000..24f5012 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/hypothesis/test_fuzzing.py @@ -0,0 +1,34 @@ +import string +import unittest + +import hypothesis.strategies as st +from hypothesis import given, settings + +from icalendar.parser import Contentline, Contentlines, Parameters + + +def printable_characters(**kw): + return st.text(st.characters(blacklist_categories=("Cc", "Cs"), **kw)) + + +key = st.text(string.ascii_letters + string.digits, min_size=1) +value = printable_characters(blacklist_characters='\\;:"') + + +class TestFuzzing(unittest.TestCase): + @given( + lines=st.lists(st.tuples(key, st.dictionaries(key, value), value), min_size=1) + ) + @settings(max_examples=10**3) + def test_main(self, lines): + cl = Contentlines() + for key, params, value in lines: + try: + params = Parameters(**params) + except TypeError: + # Happens when there is a random parameter 'self'... + continue + cl.append(Contentline.from_parts(key, params, value)) + cl.append("") + + assert Contentlines.from_ical(cl.to_ical()) == cl diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/prop/__init__.py b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/__init__.py new file mode 100644 index 0000000..aa0f4f9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/__init__.py @@ -0,0 +1 @@ +"""Test the value types of properties.""" diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_constructors.py b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_constructors.py new file mode 100644 index 0000000..00501ae --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_constructors.py @@ -0,0 +1,120 @@ +from icalendar.prop import ( + vBoolean, + vInline, + vUTCOffset, + vCategory, + vCalAddress, + vWeekday, + vDuration, + vFloat, + vGeo, + vInt, + vText, + vMonth, + vUTCOffset, + vFrequency, + vRecur, + vDatetime, + vUri, +) +import datetime + + +def test_param_vCategory(): + obj = vCategory(["Work", "Personal"], params={"SOME_PARAM": "VALUE"}) + assert isinstance(obj, vCategory) + assert obj.params["SOME_PARAM"] == "VALUE" + + +def test_param_vCalAddress(): + obj = vCalAddress("mailto:jane_doe@example.com", params={"SOME_PARAM": "VALUE"}) + assert isinstance(obj, vCalAddress) + assert obj.params["SOME_PARAM"] == "VALUE" + + +def test_param_vWeekday(): + obj = vWeekday("2FR", params={"SOME_PARAM": "VALUE"}) + assert isinstance(obj, vWeekday) + assert obj.params["SOME_PARAM"] == "VALUE" + + +def test_param_vBoolean(): + obj = vBoolean(True, params={"SOME_PARAM": "VALUE"}) + assert isinstance(obj, vBoolean) + assert obj.params["SOME_PARAM"] == "VALUE" + + +def test_param_vDuration(): + td = datetime.timedelta(days=15, seconds=18020) + obj = vDuration(td, params={"SOME_PARAM": "VALUE"}) + assert isinstance(obj, vDuration) + assert obj.params["SOME_PARAM"] == "VALUE" + + +def test_param_vFloat(): + obj = vFloat("1.333", params={"SOME_PARAM": "VALUE"}) + assert isinstance(obj, vFloat) + assert obj.params["SOME_PARAM"] == "VALUE" + + +def test_param_vGeo(): + obj = vGeo((37.386013, -122.082932), params={"SOME_PARAM": "VALUE"}) + assert isinstance(obj, vGeo) + assert obj.params["SOME_PARAM"] == "VALUE" + + +def test_param_vInt(): + obj = vInt("87", params={"SOME_PARAM": "VALUE"}) + assert isinstance(obj, vInt) + assert obj.params["SOME_PARAM"] == "VALUE" + + +def test_param_vInline(): + obj = vInline("sometxt", params={"SOME_PARAM": "VALUE"}) + assert isinstance(obj, vInline) + assert obj.params["SOME_PARAM"] == "VALUE" + + +def test_param_vText(): + obj = vText("sometxt", params={"SOME_PARAM": "VALUE"}) + assert isinstance(obj, vText) + assert obj.params["SOME_PARAM"] == "VALUE" + + +def test_param_vMonth(): + obj = vMonth(1, params={"SOME_PARAM": "VALUE"}) + assert isinstance(obj, vMonth) + assert obj.params["SOME_PARAM"] == "VALUE" + + +def test_param_vUTCOffset(): + obj = vUTCOffset( + datetime.timedelta(days=-1, seconds=68400), params={"SOME_PARAM": "VALUE"} + ) + assert isinstance(obj, vUTCOffset) + assert obj.params["SOME_PARAM"] == "VALUE" + + +def test_param_vFrequency(): + obj = vFrequency("DAILY", params={"SOME_PARAM": "VALUE"}) + assert isinstance(obj, vFrequency) + assert obj.params["SOME_PARAM"] == "VALUE" + + +def test_param_vRecur(): + obj = vRecur({"FREQ": ["DAILY"], "COUNT": [10]}, params={"SOME_PARAM": "VALUE"}) + assert isinstance(obj, vRecur) + assert obj.params["SOME_PARAM"] == "VALUE" + + +def test_param_vDatetime(): + dt = datetime.datetime(2025, 3, 16, 14, 30, 0, tzinfo=datetime.timezone.utc) + obj = vDatetime(dt, params={"SOME_PARAM": "VALUE"}) + assert isinstance(obj, vDatetime) + assert obj.params["SOME_PARAM"] == "VALUE" + + +def test_param_vUri(): + obj = vUri("WWW.WESBITE.COM", params={"SOME_PARAM": "VALUE"}) + assert isinstance(obj, vUri) + assert obj.params["SOME_PARAM"] == "VALUE" diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_identity_and_equality.py b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_identity_and_equality.py new file mode 100644 index 0000000..550bd37 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_identity_and_equality.py @@ -0,0 +1,70 @@ +"""Test the identity and equality between properties.""" + +from datetime import date, datetime, time + +from icalendar import vDDDTypes +from icalendar.timezone.zoneinfo import zoneinfo + +try: + import pytz +except ImportError: + pytz = None +from copy import deepcopy + +import pytest +from dateutil import tz + +vDDDTypes_list = [ + vDDDTypes( + datetime( + year=2022, + month=7, + day=22, + hour=12, + minute=7, + tzinfo=zoneinfo.ZoneInfo("Europe/London"), + ) + ), + vDDDTypes(datetime(year=2022, month=7, day=22, hour=12, minute=7)), + vDDDTypes(datetime(year=2022, month=7, day=22, hour=12, minute=7, tzinfo=tz.UTC)), + vDDDTypes(date(year=2022, month=7, day=22)), + vDDDTypes(date(year=2022, month=7, day=23)), + vDDDTypes(time(hour=22, minute=7, second=2)), +] +if pytz: + vDDDTypes_list.append( + vDDDTypes( + pytz.timezone("EST").localize( + datetime(year=2022, month=7, day=22, hour=12, minute=7) + ) + ), + ) + + +def identity(x): + return x + + +@pytest.mark.parametrize( + "map", + [ + deepcopy, + identity, + hash, + ], +) +@pytest.mark.parametrize("v_type", vDDDTypes_list) +@pytest.mark.parametrize("other", vDDDTypes_list) +def test_vDDDTypes_equivalance(map, v_type, other): + if v_type is other: + assert map(v_type) == map(other), f"identity implies equality: {map.__name__}()" + assert map(v_type) == map(other), f"identity implies equality: {map.__name__}()" + else: + assert map(v_type) != map(other), f"expected inequality: {map.__name__}()" + assert map(v_type) != map(other), f"expected inequality: {map.__name__}()" + + +@pytest.mark.parametrize("v_type", vDDDTypes_list) +def test_inequality_with_different_types(v_type): + assert v_type != 42 + assert v_type != "test" diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_property_values.py b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_property_values.py new file mode 100644 index 0000000..d47f523 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_property_values.py @@ -0,0 +1,19 @@ +"""Test that composed values are properly converted.""" + +from datetime import datetime + +from icalendar import Event + + +def test_vDDDLists_timezone(tzp): + """Test vDDDLists with timezone information.""" + vevent = Event() + dt1 = tzp.localize(datetime(2013, 1, 1), "Europe/Vienna") + dt2 = tzp.localize(datetime(2013, 1, 2), "Europe/Vienna") + dt3 = tzp.localize(datetime(2013, 1, 3), "Europe/Vienna") + vevent.add("rdate", [dt1, dt2]) + vevent.add("exdate", dt3) + ical = vevent.to_ical() + + assert b"RDATE;TZID=Europe/Vienna:20130101T000000,20130102T000000" in ical + assert b"EXDATE;TZID=Europe/Vienna:20130103T000000" in ical diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_unit.py b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_unit.py new file mode 100644 index 0000000..ce47811 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_unit.py @@ -0,0 +1,363 @@ +import unittest +from datetime import date, datetime, time, timedelta + +from icalendar.parser import Parameters + + +class TestProp(unittest.TestCase): + def test_prop_vFloat(self): + from icalendar.prop import vFloat + + self.assertEqual(vFloat(1.0).to_ical(), b"1.0") + self.assertEqual(vFloat.from_ical("42"), 42.0) + self.assertEqual(vFloat(42).to_ical(), b"42.0") + self.assertRaises(ValueError, vFloat.from_ical, "1s3") + + def test_prop_vInt(self): + from icalendar.prop import vInt + + self.assertEqual(vInt(42).to_ical(), b"42") + self.assertEqual(vInt.from_ical("13"), 13) + self.assertRaises(ValueError, vInt.from_ical, "1s3") + + def test_prop_vDDDLists(self): + from icalendar.prop import vDDDLists + + dt_list = vDDDLists.from_ical("19960402T010000Z") + self.assertIsInstance(dt_list, list) + self.assertEqual(len(dt_list), 1) + self.assertIsInstance(dt_list[0], datetime) + self.assertEqual(str(dt_list[0]), "1996-04-02 01:00:00+00:00") + + p = "19960402T010000Z,19960403T010000Z,19960404T010000Z" + dt_list = vDDDLists.from_ical(p) + self.assertEqual(len(dt_list), 3) + self.assertEqual(str(dt_list[0]), "1996-04-02 01:00:00+00:00") + self.assertEqual(str(dt_list[2]), "1996-04-04 01:00:00+00:00") + + dt_list = vDDDLists([]) + self.assertEqual(dt_list.to_ical(), b"") + + dt_list = vDDDLists([datetime(2000, 1, 1)]) + self.assertEqual(dt_list.to_ical(), b"20000101T000000") + + dt_list = vDDDLists([datetime(2000, 1, 1), datetime(2000, 11, 11)]) + self.assertEqual(dt_list.to_ical(), b"20000101T000000,20001111T000000") + + instance = vDDDLists([]) + self.assertNotEqual(instance, "value") + + def test_prop_vDate(self): + from icalendar.prop import vDate + + self.assertEqual(vDate(date(2001, 1, 1)).to_ical(), b"20010101") + self.assertEqual(vDate(date(1899, 1, 1)).to_ical(), b"18990101") + + self.assertEqual(vDate.from_ical("20010102"), date(2001, 1, 2)) + + self.assertRaises(ValueError, vDate, "d") + self.assertRaises(ValueError, vDate.from_ical, "200102") + + def test_prop_vDuration(self): + from icalendar.prop import vDuration + + self.assertEqual(vDuration(timedelta(11)).to_ical(), b"P11D") + self.assertEqual(vDuration(timedelta(-14)).to_ical(), b"-P14D") + self.assertEqual(vDuration(timedelta(1, 7384)).to_ical(), b"P1DT2H3M4S") + self.assertEqual(vDuration(timedelta(1, 7380)).to_ical(), b"P1DT2H3M") + self.assertEqual(vDuration(timedelta(1, 7200)).to_ical(), b"P1DT2H") + self.assertEqual(vDuration(timedelta(0, 7200)).to_ical(), b"PT2H") + self.assertEqual(vDuration(timedelta(0, 7384)).to_ical(), b"PT2H3M4S") + self.assertEqual(vDuration(timedelta(0, 184)).to_ical(), b"PT3M4S") + self.assertEqual(vDuration(timedelta(0, 22)).to_ical(), b"PT22S") + self.assertEqual(vDuration(timedelta(0, 3622)).to_ical(), b"PT1H0M22S") + self.assertEqual(vDuration(timedelta(days=1, hours=5)).to_ical(), b"P1DT5H") + self.assertEqual(vDuration(timedelta(hours=-5)).to_ical(), b"-PT5H") + self.assertEqual(vDuration(timedelta(days=-1, hours=-5)).to_ical(), b"-P1DT5H") + + # How does the parsing work? + self.assertEqual(vDuration.from_ical("PT1H0M22S"), timedelta(0, 3622)) + + self.assertRaises(ValueError, vDuration.from_ical, "kox") + + self.assertEqual(vDuration.from_ical("-P14D"), timedelta(-14)) + + self.assertRaises(ValueError, vDuration, 11) + + # calling to_ical twice should result in same output + duration = vDuration(timedelta(days=-1, hours=-5)) + self.assertEqual(duration.to_ical(), b"-P1DT5H") + self.assertEqual(duration.to_ical(), b"-P1DT5H") + + def test_prop_vWeekday(self): + from icalendar.prop import vWeekday + + self.assertEqual(vWeekday("mo").to_ical(), b"MO") + self.assertRaises(ValueError, vWeekday, "erwer") + self.assertEqual(vWeekday.from_ical("mo"), "MO") + self.assertEqual(vWeekday.from_ical("+3mo"), "+3MO") + self.assertRaises(ValueError, vWeekday.from_ical, "Saturday") + self.assertEqual(vWeekday("+mo").to_ical(), b"+MO") + self.assertEqual(vWeekday("+3mo").to_ical(), b"+3MO") + self.assertEqual(vWeekday("-tu").to_ical(), b"-TU") + + def test_prop_vFrequency(self): + from icalendar.prop import vFrequency + + self.assertRaises(ValueError, vFrequency, "bad test") + self.assertEqual(vFrequency("daily").to_ical(), b"DAILY") + self.assertEqual(vFrequency("daily").from_ical("MONTHLY"), "MONTHLY") + self.assertRaises(ValueError, vFrequency.from_ical, 234) + + def test_prop_vRecur(self): + from icalendar.prop import vRecur + + # Let's see how close we can get to one from the rfc: + # FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9;BYMINUTE=30 + + r = dict({"freq": "yearly", "interval": 2}) + r.update({"bymonth": 1, "byday": "su", "byhour": [8, 9], "byminute": 30}) + self.assertEqual( + vRecur(r).to_ical(), + b"FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=SU;BYMONTH=1", + ) + + r = vRecur(FREQ="yearly", INTERVAL=2) + r.update( + { + "BYMONTH": 1, + "BYDAY": "su", + "BYHOUR": [8, 9], + "BYMINUTE": 30, + } + ) + self.assertEqual( + r.to_ical(), + b"FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=SU;BYMONTH=1", + ) + + r = vRecur(freq="DAILY", count=10) + r["bysecond"] = [0, 15, 30, 45] + self.assertEqual(r.to_ical(), b"FREQ=DAILY;COUNT=10;BYSECOND=0,15,30,45") + + r = vRecur(freq="DAILY", until=datetime(2005, 1, 1, 12, 0, 0)) + self.assertEqual(r.to_ical(), b"FREQ=DAILY;UNTIL=20050101T120000") + + # How do we fare with regards to parsing? + r = vRecur.from_ical("FREQ=DAILY;INTERVAL=2;COUNT=10") + self.assertEqual(r, {"COUNT": [10], "FREQ": ["DAILY"], "INTERVAL": [2]}) + self.assertEqual(vRecur(r).to_ical(), b"FREQ=DAILY;COUNT=10;INTERVAL=2") + + r = vRecur.from_ical( + "FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=-SU;BYHOUR=8,9;BYMINUTE=30" + ) + self.assertEqual( + r, + { + "BYHOUR": [8, 9], + "BYDAY": ["-SU"], + "BYMINUTE": [30], + "BYMONTH": [1], + "FREQ": ["YEARLY"], + "INTERVAL": [2], + }, + ) + + self.assertEqual( + vRecur(r).to_ical(), + b"FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=-SU;BYMONTH=1", + ) + + r = vRecur.from_ical("FREQ=WEEKLY;INTERVAL=1;BYWEEKDAY=TH") + + self.assertEqual(r, {"FREQ": ["WEEKLY"], "INTERVAL": [1], "BYWEEKDAY": ["TH"]}) + + self.assertEqual(vRecur(r).to_ical(), b"FREQ=WEEKLY;INTERVAL=1;BYWEEKDAY=TH") + + # Some examples from the spec + r = vRecur.from_ical("FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1") + self.assertEqual( + vRecur(r).to_ical(), b"FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1" + ) + + p = "FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9;BYMINUTE=30" + r = vRecur.from_ical(p) + self.assertEqual( + vRecur(r).to_ical(), + b"FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=SU;BYMONTH=1", + ) + + # and some errors + self.assertRaises(ValueError, vRecur.from_ical, "BYDAY=12") + + # when key is not RFC-compliant, parse it as vText + r = vRecur.from_ical("FREQ=MONTHLY;BYOTHER=TEXT;BYEASTER=-3") + self.assertEqual(vRecur(r).to_ical(), b"FREQ=MONTHLY;BYEASTER=-3;BYOTHER=TEXT") + + def test_prop_vText(self): + from icalendar.prop import vText + + self.assertEqual(vText("Simple text").to_ical(), b"Simple text") + + # Escaped text + t = vText("Text ; with escaped, chars") + self.assertEqual(t.to_ical(), b"Text \\; with escaped\\, chars") + + # Escaped newlines + self.assertEqual( + vText("Text with escaped\\N chars").to_ical(), b"Text with escaped\\n chars" + ) + + # If you pass a unicode object, it will be utf-8 encoded. As this is + # the (only) standard that RFC 5545 support. + t = vText("international chars \xe4\xf6\xfc") + self.assertEqual(t.to_ical(), b"international chars \xc3\xa4\xc3\xb6\xc3\xbc") + + # and parsing? + self.assertEqual( + vText.from_ical("Text \\; with escaped\\, chars"), + "Text ; with escaped, chars", + ) + + t = vText.from_ical("A string with\\; some\\\\ characters in\\it") + self.assertEqual(t, "A string with; some\\ characters in\\it") + + # We are forgiving to utf-8 encoding errors: + # We intentionally use a string with unexpected encoding + # + self.assertEqual(vText.from_ical(b"Ol\xe9"), "Ol\ufffd") + + # Notice how accented E character, encoded with latin-1, got replaced + # with the official U+FFFD REPLACEMENT CHARACTER. + + def test_prop_vTime(self): + from icalendar.prop import vTime + + self.assertEqual(vTime(12, 30, 0).to_ical(), "123000") + self.assertEqual(vTime.from_ical("123000"), time(12, 30)) + + # We should also fail, right? + self.assertRaises(ValueError, vTime.from_ical, "263000") + + self.assertRaises(ValueError, vTime, "263000") + + def test_prop_vUri(self): + from icalendar.prop import vUri + + self.assertEqual( + vUri("http://www.example.com/").to_ical(), b"http://www.example.com/" + ) + self.assertEqual( + vUri.from_ical("http://www.example.com/"), "http://www.example.com/" + ) + + def test_prop_vGeo(self): + from icalendar.prop import vGeo + + # Pass a list + self.assertEqual(vGeo([1.2, 3.0]).to_ical(), "1.2;3.0") + + # Pass a tuple + self.assertEqual(vGeo((1.2, 3.0)).to_ical(), "1.2;3.0") + + g = vGeo.from_ical("37.386013;-122.082932") + self.assertEqual(g, (float("37.386013"), float("-122.082932"))) + + self.assertEqual(vGeo(g).to_ical(), "37.386013;-122.082932") + + self.assertRaises(ValueError, vGeo, "g") + self.assertRaises(ValueError, vGeo.from_ical, "1s3;1s3") + + def test_prop_vUTCOffset(self): + from icalendar.prop import vUTCOffset + + self.assertEqual(vUTCOffset(timedelta(hours=2)).to_ical(), "+0200") + + self.assertEqual(vUTCOffset(timedelta(hours=-5)).to_ical(), "-0500") + + self.assertEqual(vUTCOffset(timedelta()).to_ical(), "+0000") + + self.assertEqual(vUTCOffset(timedelta(minutes=-30)).to_ical(), "-0030") + + self.assertEqual(vUTCOffset(timedelta(hours=2, minutes=-30)).to_ical(), "+0130") + + self.assertEqual(vUTCOffset(timedelta(hours=1, minutes=30)).to_ical(), "+0130") + + # Support seconds + self.assertEqual( + vUTCOffset(timedelta(hours=1, minutes=30, seconds=7)).to_ical(), "+013007" + ) + + # Parsing + + self.assertEqual(vUTCOffset.from_ical("0000"), timedelta(0)) + self.assertEqual(vUTCOffset.from_ical("-0030"), timedelta(-1, 84600)) + self.assertEqual(vUTCOffset.from_ical("+0200"), timedelta(0, 7200)) + self.assertEqual(vUTCOffset.from_ical("+023040"), timedelta(0, 9040)) + + self.assertEqual(vUTCOffset(vUTCOffset.from_ical("+0230")).to_ical(), "+0230") + + # And a few failures + self.assertRaises(ValueError, vUTCOffset.from_ical, "+323k") + + self.assertRaises(ValueError, vUTCOffset.from_ical, "+2400") + + self.assertRaises(ValueError, vUTCOffset, "0:00:00") + + def test_prop_vInline(self): + from icalendar.prop import vInline + + self.assertEqual(vInline("Some text"), "Some text") + self.assertEqual(vInline("Some text").to_ical(), b"Some text") + self.assertEqual(vInline.from_ical("Some text"), "Some text") + + t2 = vInline("other text") + t2.params["cn"] = "Test Osterone" + self.assertIsInstance(t2.params, Parameters) + self.assertEqual(t2.params, {"CN": "Test Osterone"}) + + def test_prop_vCategory(self): + from icalendar.prop import vCategory + + catz = ["cat 1", "cat 2", "cat 3"] + v_cat = vCategory(catz) + + self.assertEqual(v_cat.to_ical(), b"cat 1,cat 2,cat 3") + self.assertEqual(vCategory.from_ical(v_cat.to_ical()), catz) + c = vCategory(vCategory.from_ical("APPOINTMENT,EDUCATION")) + cats = list(c) + assert cats == ["APPOINTMENT", "EDUCATION"] + + def test_prop_TypesFactory(self): + from icalendar.prop import TypesFactory + + # To get a type you can use it like this. + factory = TypesFactory() + datetime_parser = factory["date-time"] + self.assertEqual( + datetime_parser(datetime(2001, 1, 1)).to_ical(), b"20010101T000000" + ) + + # A typical use is when the parser tries to find a content type and use + # text as the default + value = "20050101T123000" + value_type = "date-time" + self.assertEqual( + factory.get(value_type, "text").from_ical(value), + datetime(2005, 1, 1, 12, 30), + ) + + # It can also be used to directly encode property and parameter values + self.assertEqual( + factory.to_ical("comment", "by Rasmussen, Max M\xfcller"), + b"by Rasmussen\\, Max M\xc3\xbcller", + ) + self.assertEqual(factory.to_ical("priority", 1), b"1") + self.assertEqual( + factory.to_ical("cn", "Rasmussen, Max M\xfcller"), + b"Rasmussen\\, Max M\xc3\xbcller", + ) + self.assertEqual( + factory.from_ical("cn", b"Rasmussen\\, Max M\xc3\xb8ller"), + "Rasmussen, Max M\xf8ller", + ) diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vBinary.py b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vBinary.py new file mode 100644 index 0000000..f8be3e3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vBinary.py @@ -0,0 +1,45 @@ +"""Test vBinary""" + +import pytest + +from icalendar import vBinary +from icalendar.parser import Parameters + + +def test_text(): + txt = b"This is gibberish" + txt_ical = b"VGhpcyBpcyBnaWJiZXJpc2g=" + assert vBinary(txt).to_ical() == txt_ical + assert vBinary.from_ical(txt_ical) == txt + + +def test_binary(): + txt = b"Binary data \x13 \x56" + txt_ical = b"QmluYXJ5IGRhdGEgEyBW" + assert vBinary(txt).to_ical() == txt_ical + assert vBinary.from_ical(txt_ical) == txt + + +def test_param(): + assert isinstance(vBinary("txt").params, Parameters) + assert vBinary("txt").params == {"VALUE": "BINARY", "ENCODING": "BASE64"} + + +def test_long_data(): + """Long data should not have line breaks, as that would interfere""" + txt = b"a" * 99 + txt_ical = b"YWFh" * 33 + assert vBinary(txt).to_ical() == txt_ical + assert vBinary.from_ical(txt_ical) == txt + + +def test_repr(): + instance = vBinary("value") + assert repr(instance) == "vBinary(b'dmFsdWU=')" + + +def test_from_ical(): + with pytest.raises(ValueError, match="Not valid base 64 encoding."): + vBinary.from_ical("value") + with pytest.raises(ValueError, match="Not valid base 64 encoding."): + vBinary.from_ical("áèਮ") diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vBoolean.py b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vBoolean.py new file mode 100644 index 0000000..67a852f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vBoolean.py @@ -0,0 +1,22 @@ +import pytest + +from icalendar.prop import vBoolean + + +def test_true(): + assert vBoolean(True).to_ical() == b"TRUE" + + +def test_false(): + assert vBoolean(0).to_ical() == b"FALSE" + + +def test_roundtrip(): + assert vBoolean.from_ical(vBoolean(True).to_ical()) == True + assert vBoolean.from_ical("true") == True + + +def test_error(): + """Error: key not exists""" + with pytest.raises(ValueError): + vBoolean.from_ical("ture") diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vCalAddress.py b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vCalAddress.py new file mode 100644 index 0000000..e7b5635 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vCalAddress.py @@ -0,0 +1,57 @@ +from icalendar.parser import Parameters +from icalendar.prop import vCalAddress + +txt = b"MAILTO:maxm@mxm.dk" +a = vCalAddress(txt) +a.params["cn"] = "Max M" + + +def test_to_ical(): + assert a.to_ical() == txt + + +def test_params(): + assert isinstance(a.params, Parameters) + assert a.params == {"CN": "Max M"} + + +def test_from_ical(): + assert vCalAddress.from_ical(txt) == "MAILTO:maxm@mxm.dk" + + +def test_repr(): + instance = vCalAddress("value") + assert repr(instance) == "vCalAddress('value')" + + +def test_email_malformed(): + """Sometimes, people forget to add mailto that.""" + address = vCalAddress("me@you.we") + assert address.email == "me@you.we" + + +def test_email_mailto(): + """Email with a normal mailto link.""" + address = vCalAddress("mailto:icalendar@email.list") + assert address.email == "icalendar@email.list" + + +def test_capital_email(): + """mailto can be capital letters.""" + address = vCalAddress("MAILTO:yemaya@posteo.net") + assert address.email == "yemaya@posteo.net" + + +def test_name(): + """We want the name, too!""" + address = vCalAddress("MAILTO:yemaya@posteo.net") + assert address.name == "" + address.params["CN"] = "name!" + assert address.name == "name!" + + +def test_set_the_name(): + address = vCalAddress("MAILTO:yemaya@posteo.net") + address.name = "Yemaya :)" + assert address.name == "Yemaya :)" + assert address.params["CN"] == "Yemaya :)" diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vDDDTypes.py b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vDDDTypes.py new file mode 100644 index 0000000..9579758 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vDDDTypes.py @@ -0,0 +1,37 @@ +from datetime import date, datetime, time, timedelta + +import pytest + +from icalendar.prop import vDDDTypes + + +def test_instance(): + assert isinstance(vDDDTypes.from_ical("20010101T123000"), datetime) + assert isinstance(vDDDTypes.from_ical("20010101"), date) + + +def test_datetime_with_timezone(tzp): + assert vDDDTypes.from_ical("20010101T123000Z") == tzp.localize_utc( + datetime(2001, 1, 1, 12, 30) + ) + + +def test_timedelta(): + assert vDDDTypes.from_ical("P31D") == timedelta(31) + assert vDDDTypes.from_ical("-P31D") == timedelta(-31) + + +def test_bad_input(): + with pytest.raises(ValueError): + vDDDTypes(42) + + +def test_time_from_string(): + assert vDDDTypes.from_ical("123000") == time(12, 30) + assert isinstance(vDDDTypes.from_ical("123000"), time) + + +def test_invalid_period_to_ical(): + invalid_period = (datetime(2000, 1, 1), datetime(2000, 1, 2), datetime(2000, 1, 2)) + with pytest.raises(ValueError): + vDDDTypes(invalid_period).to_ical() diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vDatetime.py b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vDatetime.py new file mode 100644 index 0000000..00a8972 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vDatetime.py @@ -0,0 +1,47 @@ +from datetime import datetime + +import pytest + +from icalendar.prop import vDatetime + + +def test_to_ical(): + assert vDatetime(datetime(2001, 1, 1, 12, 30, 0)).to_ical() == b"20010101T123000" + + +def test_from_ical(): + assert vDatetime.from_ical("20000101T120000") == datetime(2000, 1, 1, 12, 0) + assert vDatetime.from_ical("20010101T000000") == datetime(2001, 1, 1, 0, 0) + + +def test_to_ical_utc(tzp): + dutc = tzp.localize_utc(datetime(2001, 1, 1, 12, 30, 0)) + assert vDatetime(dutc).to_ical() == b"20010101T123000Z" + + +def test_to_ical_utc_1899(tzp): + dutc = tzp.localize_utc(datetime(1899, 1, 1, 12, 30, 0)) + assert vDatetime(dutc).to_ical() == b"18990101T123000Z" + + +def test_bad_ical(): + with pytest.raises(ValueError): + vDatetime.from_ical("20010101T000000A") + + +def test_roundtrip(): + utc = vDatetime.from_ical("20010101T000000Z") + assert vDatetime(utc).to_ical() == b"20010101T000000Z" + + +def test_transition(tzp): + # 1 minute before transition to DST + dat = vDatetime.from_ical("20120311T015959", "America/Denver") + assert dat.strftime("%Y%m%d%H%M%S %z") == "20120311015959 -0700" + + # After transition to DST + dat = vDatetime.from_ical("20120311T030000", "America/Denver") + assert dat.strftime("%Y%m%d%H%M%S %z") == "20120311030000 -0600" + + dat = vDatetime.from_ical("20101010T000000", "Europe/Vienna") + assert vDatetime(dat).to_ical() == b"20101010T000000" diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vPeriod.py b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vPeriod.py new file mode 100644 index 0000000..c1485f2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vPeriod.py @@ -0,0 +1,63 @@ +import unittest +from datetime import datetime, timedelta + +import pytest + +from icalendar.prop import vPeriod + + +class TestProp(unittest.TestCase): + def test_one_day(self): + # One day in exact datetimes + per = (datetime(2000, 1, 1), datetime(2000, 1, 2)) + self.assertEqual(vPeriod(per).to_ical(), b"20000101T000000/20000102T000000") + + per = (datetime(2000, 1, 1), timedelta(days=31)) + self.assertEqual(vPeriod(per).to_ical(), b"20000101T000000/P31D") + + def test_roundtrip(self): + p = vPeriod.from_ical("20000101T000000/20000102T000000") + self.assertEqual(p, (datetime(2000, 1, 1, 0, 0), datetime(2000, 1, 2, 0, 0))) + self.assertEqual(vPeriod(p).to_ical(), b"20000101T000000/20000102T000000") + + self.assertEqual( + vPeriod.from_ical("20000101T000000/P31D"), + (datetime(2000, 1, 1, 0, 0), timedelta(31)), + ) + + def test_round_trip_with_absolute_time(self): + p = vPeriod.from_ical("20000101T000000Z/20000102T000000Z") + self.assertEqual(vPeriod(p).to_ical(), b"20000101T000000Z/20000102T000000Z") + + def test_bad_input(self): + self.assertRaises(ValueError, vPeriod.from_ical, "20000101T000000/Psd31D") + + +def test_timezoned(tzp): + start = tzp.localize(datetime(2000, 1, 1), "Europe/Copenhagen") + end = tzp.localize(datetime(2000, 1, 2), "Europe/Copenhagen") + per = (start, end) + assert vPeriod(per).to_ical() == b"20000101T000000/20000102T000000" + assert vPeriod(per).params["TZID"] == "Europe/Copenhagen" + + +def test_timezoned_with_timedelta(tzp): + p = vPeriod( + (tzp.localize(datetime(2000, 1, 1), "Europe/Copenhagen"), timedelta(days=31)) + ) + assert p.to_ical() == b"20000101T000000/P31D" + + +@pytest.mark.parametrize( + "params", + [ + ("20000101T000000", datetime(2000, 1, 2)), + (datetime(2000, 1, 1), "20000102T000000"), + (datetime(2000, 1, 2), datetime(2000, 1, 1)), + (datetime(2000, 1, 2), timedelta(-1)), + ], +) +def test_invalid_parameters(params): + """The parameters are of wrong type or of wrong order.""" + with pytest.raises(ValueError): + vPeriod(params) diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vWeekday.py b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vWeekday.py new file mode 100644 index 0000000..5d886c0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vWeekday.py @@ -0,0 +1,27 @@ +import pytest + +from icalendar.prop import vWeekday + + +def test_simple(): + weekday = vWeekday("SU") + assert weekday.to_ical() == b"SU" + assert weekday.weekday == "SU" + assert weekday.relative is None + + +def test_relative(): + weekday = vWeekday("-1MO") + assert weekday.to_ical() == b"-1MO" + assert weekday.weekday == "MO" + assert weekday.relative == -1 + + +def test_roundtrip(): + assert vWeekday.from_ical(vWeekday("+2TH").to_ical()) == "+2TH" + + +def test_error(): + """Error: Expected weekday abbrevation, got: \"-100MO\" """ + with pytest.raises(ValueError): + vWeekday.from_ical("-100MO") diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_windows_to_olson_mapping.py b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_windows_to_olson_mapping.py new file mode 100644 index 0000000..489d0d2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_windows_to_olson_mapping.py @@ -0,0 +1,22 @@ +"""Test the mappings from windows to olson tzids""" + +from datetime import datetime + +import pytest + +from icalendar import vDatetime +from icalendar.timezone.windows_to_olson import WINDOWS_TO_OLSON + + +def test_windows_timezone(tzp): + """Test that the timezone is mapped correctly to olson.""" + dt = vDatetime.from_ical("20170507T181920", "Eastern Standard Time") + expected = tzp.localize(datetime(2017, 5, 7, 18, 19, 20), "America/New_York") + assert dt.tzinfo == expected.tzinfo + assert dt == expected + + +@pytest.mark.parametrize("olson_id", WINDOWS_TO_OLSON.values()) +def test_olson_names(tzp, olson_id): + """test if all mappings actually map to valid tzids""" + assert tzp.timezone(olson_id) is not None diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_bom_calendar.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_bom_calendar.py new file mode 100644 index 0000000..c588819 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_bom_calendar.py @@ -0,0 +1,4 @@ +def test_bom_calendar(calendars): + assert calendars.bom_calendar.walk( + "VCALENDAR" + ), "Unable to parse a calendar starting with an Unicode BOM" diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_cli_tool.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_cli_tool.py new file mode 100644 index 0000000..0784442 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_cli_tool.py @@ -0,0 +1,112 @@ +import unittest +from datetime import datetime + +from icalendar import Calendar, cli + +try: + import zoneinfo +except ModuleNotFoundError: + from backports import zoneinfo + +INPUT = """ +BEGIN:VCALENDAR +VERSION:2.0 +CALSCALE:GREGORIAN +BEGIN:VEVENT +SUMMARY:Test Summary +ORGANIZER:organizer@test.test +ATTENDEE:attendee1@example.com +ATTENDEE:attendee2@test.test +COMMENT:Comment +DTSTART;TZID=Europe/Warsaw:20220820T103400 +DTEND;TZID=Europe/Warsaw:20220820T113400 +LOCATION:New Amsterdam, 1000 Sunrise Test Street +DESCRIPTION: Test Description +END:VEVENT +BEGIN:VEVENT +ORGANIZER:organizer@test.test +ATTENDEE:attendee1@example.com +SUMMARY:Test summary +DTSTART;TZID=Europe/Warsaw:20220820T200000 +DTEND;TZID=Europe/Warsaw:20220820T203000 +LOCATION:New Amsterdam, 1010 Test Street +DESCRIPTION:Test Description\\nThis one is multiline +END:VEVENT +BEGIN:VEVENT +UID:1 +SUMMARY:TEST +DTSTART:20220511 +DURATION:P5D +END:VEVENT +END:VCALENDAR +""" + + +def local_datetime(dt): + return ( + datetime.strptime(dt, "%Y%m%dT%H%M%S") + .replace(tzinfo=zoneinfo.ZoneInfo("Europe/Warsaw")) + .astimezone() + .strftime("%c") + ) + + +# datetimes are displayed in the local timezone, so we cannot just hardcode them +firststart = local_datetime("20220820T103400") +firstend = local_datetime("20220820T113400") +secondstart = local_datetime("20220820T200000") +secondend = local_datetime("20220820T203000") + +PROPER_OUTPUT = f""" Organizer: organizer + Attendees: + attendee1 + attendee2 + Summary : Test Summary + Starts : {firststart} + End : {firstend} + Duration : 1:00:00 + Location : New Amsterdam, 1000 Sunrise Test Street + Comment : Comment + Description: + Test Description + + Organizer: organizer + Attendees: + attendee1 + Summary : Test summary + Starts : {secondstart} + End : {secondend} + Duration : 0:30:00 + Location : New Amsterdam, 1010 Test Street + Comment : + Description: + Test Description + This one is multiline + + Organizer: + Attendees: + + Summary : TEST + Starts : Wed May 11 00:00:00 2022 + End : Mon May 16 00:00:00 2022 + Duration : 5 days, 0:00:00 + Location : + Comment : + Description: + + +""" + + +class CLIToolTest(unittest.TestCase): + def test_output_is_proper(self): + self.maxDiff = None + calendar = Calendar.from_ical(INPUT) + output = "" + for event in calendar.walk("vevent"): + output += cli.view(event) + "\n\n" + self.assertEqual(PROPER_OUTPUT, output) + + +if __name__ == "__main__": + unittest.main() diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_components_break_on_bad_ics.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_components_break_on_bad_ics.py new file mode 100644 index 0000000..8d86b23 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_components_break_on_bad_ics.py @@ -0,0 +1,48 @@ +import pytest + + +def test_ignore_exceptions_on_broken_events_issue_104(events): + """Issue #104 - line parsing error in a VEVENT + (which has ignore_exceptions). Should mark the event broken + but not raise an exception. + + https://github.com/collective/icalendar/issues/104 + """ + assert events.issue_104_mark_events_broken.errors == [ + (None, "Content line could not be parsed into parts: 'X': Invalid content line") + ] + + +def test_dont_ignore_exceptions_on_broken_calendars_issue_104(calendars): + """Issue #104 - line parsing error in a VCALENDAR + (which doesn't have ignore_exceptions). Should raise an exception. + """ + with pytest.raises(ValueError): + calendars.issue_104_broken_calendar + + +def test_rdate_dosent_become_none_on_invalid_input_issue_464(events): + """Issue #464 - [BUG] RDATE can become None if value is invalid + https://github.com/collective/icalendar/issues/464 + """ + assert ( + "RDATE", + "Expected period format, got: 199709T180000Z/PT5H30M", + ) in events.issue_464_invalid_rdate.errors + assert b"RDATE:None" not in events.issue_464_invalid_rdate.to_ical() + + +@pytest.mark.parametrize( + "calendar_name", + [ + "big_bad_calendar", + "small_bad_calendar", + "multiple_calendar_components", + "pr_480_summary_with_colon", + ], +) +def test_error_message_doesnt_get_too_big(calendars, calendar_name): + with pytest.raises(ValueError) as exception: + calendars[calendar_name] + # Ignore part before first : for the test. + assert len(str(exception).split(": ", 1)[1]) <= 100 diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_create_release.sh b/.venv/lib/python3.9/site-packages/icalendar/tests/test_create_release.sh new file mode 100755 index 0000000..ef07e20 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_create_release.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# +# Create a release file and test it. +# + +set -e +cd "`dirname \"$0\"`" +cd "../../.." + +rm -rf dist +pip3 install build +python3 -m build +archive=`echo dist/icalendar-*.tar.gz` + +if ! [ -f "$archive" ]; then + echo "ERROR: Cannot find distribution archive '$archive'." + exit 1 +fi + +if tar -tf "$archive" | grep -q 'fuzzing/'; then + echo "ERROR: Fuzzing files are included in the release." + echo " See https://github.com/collective/icalendar/pull/569" + exit 1 +fi + +if ! tar -tf "$archive" | grep -q '/docs/'; then + echo "ERROR: The documentation is not included in the release, but should be." + echo " See https://github.com/collective/icalendar/issues/712" + exit 1 +fi + +echo "Checks passed." diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_encoding.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_encoding.py new file mode 100644 index 0000000..4cc1557 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_encoding.py @@ -0,0 +1,98 @@ +import datetime + +import pytest + + +@pytest.mark.parametrize( + ("field", "expected_value"), + [ + ("PRODID", "-//Plönë.org//NONSGML plone.app.event//EN"), + ("X-WR-CALDESC", "test non ascii: äöü ÄÖÜ €"), + ], +) +def test_calendar_from_ical_respects_unicode(field, expected_value, calendars): + cal = calendars.calendar_with_unicode + assert cal[field].to_ical().decode("utf-8") == expected_value + + +@pytest.mark.parametrize( + ("test_input", "field", "expected_value"), + [ + ("event_with_unicode_fields", "SUMMARY", "Non-ASCII Test: ÄÖÜ äöü €"), + ( + "event_with_unicode_fields", + "DESCRIPTION", + "icalendar should be able to handle non-ascii: €äüöÄÜÖ.", + ), + ("event_with_unicode_fields", "LOCATION", "Tribstrül"), + # Non-unicode characters in summary + # https://github.com/collective/icalendar/issues/64 + ("issue_64_event_with_non_ascii_summary", "SUMMARY", "åäö"), + # Unicode characters in summary + ("issue_64_event_with_ascii_summary", "SUMMARY", "abcdef"), + ], +) +def test_event_from_ical_respects_unicode(test_input, field, expected_value, events): + event = events[test_input] + assert event[field].to_ical().decode("utf-8") == expected_value + + +@pytest.mark.parametrize( + ("test_input", "expected_output"), + [ + # chokes on umlauts in ORGANIZER + # https://github.com/collective/icalendar/issues/101 + ("issue_101_icalendar_chokes_on_umlauts_in_organizer", "acme, ädmin"), + ("event_with_unicode_organizer", "Джон Доу"), + ], +) +def test_events_parameter_unicoded(events, test_input, expected_output): + assert events[test_input]["ORGANIZER"].params["CN"] == expected_output + + +def test_parses_event_with_non_ascii_tzid_issue_237(calendars, in_timezone): + """Issue #237 - Fail to parse timezone with non-ascii TZID + see https://github.com/collective/icalendar/issues/237 + """ + start = calendars.issue_237_fail_to_parse_timezone_with_non_ascii_tzid.walk( + "VEVENT" + )[0].decoded("DTSTART") + expected = in_timezone(datetime.datetime(2017, 5, 11, 13, 30), "America/Sao_Paulo") + assert not calendars.issue_237_fail_to_parse_timezone_with_non_ascii_tzid.errors + assert start == expected + + +def test_parses_timezone_with_non_ascii_tzid_issue_237(timezones): + """Issue #237 - Fail to parse timezone with non-ascii TZID + see https://github.com/collective/icalendar/issues/237 + """ + assert timezones.issue_237_brazilia_standard["tzid"] == "(UTC-03:00) Brasília" + + +@pytest.mark.parametrize("timezone_name", ["standard", "daylight"]) +def test_parses_timezone_with_non_ascii_tzname_issue_273(timezones, timezone_name): + """Issue #237 - Fail to parse timezone with non-ascii TZID + see https://github.com/collective/icalendar/issues/237 + """ + assert ( + timezones.issue_237_brazilia_standard.walk(timezone_name)[0]["TZNAME"] + == f"Brasília {timezone_name}" + ) + + +def test_broken_property(calendars): + """ + Test if error messages are encoded properly. + """ + for event in calendars.broken_ical.walk("vevent"): + assert len(event.errors) == 1, "Not the right amount of errors." + error = event.errors[0][1] + assert error.startswith("Content line could not be parsed into parts") + + +def test_apple_xlocation(calendars): + """ + Test if we support base64 encoded binary data in parameter values. + """ + for event in calendars.x_location.walk("vevent"): + assert len(event.errors) == 0, "Got too many errors" diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_equality.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_equality.py new file mode 100644 index 0000000..9cfa391 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_equality.py @@ -0,0 +1,188 @@ +"""Test the equality and inequality of components.""" + +import contextlib +import copy + +try: + from pytz import UnknownTimeZoneError +except ImportError: + + class UnknownTimeZoneError(Exception): + pass + + +from datetime import date, datetime, time, timedelta + +import pytest + +from icalendar.prop import ( + vBinary, + vBoolean, + vCategory, + vDate, + vDatetime, + vDDDLists, + vDDDTypes, + vDuration, + vGeo, + vPeriod, + vText, + vTime, +) + + +def assert_equal(actual_value, expected_value): + """Make sure both values are equal""" + assert actual_value == expected_value + assert expected_value == actual_value + + +def assert_not_equal(actual_value, expected_value): + """Make sure both values are not equal""" + assert actual_value != expected_value + assert expected_value != actual_value + + +def test_parsed_calendars_are_equal_if_parsed_again(ics_file, tzp): + """Ensure that a calendar equals the same calendar. + + ics -> calendar -> ics -> same calendar + """ + copy_of_calendar = ics_file.__class__.from_ical(ics_file.to_ical()) + assert_equal(copy_of_calendar, ics_file) + + +def test_parsed_calendars_are_equal_if_from_same_source(ics_file, tzp): + """Ensure that a calendar equals the same calendar. + + ics -> calendar + ics -> same calendar + """ + cal1 = ics_file.__class__.from_ical(ics_file.raw_ics) + cal2 = ics_file.__class__.from_ical(ics_file.raw_ics) + assert_equal(cal1, cal2) + + +def test_copies_are_equal(ics_file, tzp): + """Ensure that copies are equal.""" + copy1 = ics_file.copy() + copy1.subcomponents = ics_file.subcomponents + copy2 = ics_file.copy() + copy2.subcomponents = ics_file.subcomponents[:] + assert_equal(copy1, copy2) + assert_equal(copy1, ics_file) + assert_equal(copy2, ics_file) + + +def test_copy_does_not_copy_subcomponents(calendars, tzp): + """If we copy the subcomponents, assumptions around copies will be broken.""" + assert calendars.timezoned.subcomponents + assert not calendars.timezoned.copy().subcomponents + + +def test_deep_copies_are_equal(ics_file, tzp): + """Ensure that deep copies are equal. + + Ignore errors when a custom time zone is used. + This is still covered by the parsing test. + """ + if ( + ics_file.source_file == "issue_722_timezone_transition_ambiguity.ics" + and tzp.uses_zoneinfo() + ): + pytest.skip("This test fails for now.") + with contextlib.suppress(UnknownTimeZoneError): + assert_equal(copy.deepcopy(ics_file), copy.deepcopy(ics_file)) + with contextlib.suppress(UnknownTimeZoneError): + assert_equal(copy.deepcopy(ics_file), ics_file) + + +def test_vGeo(): + """Check the equality of vGeo.""" + assert_equal(vGeo(("100", "12.33")), vGeo(("100.00", "12.330"))) + assert_not_equal(vGeo(("100", "12.331")), vGeo(("100.00", "12.330"))) + assert_not_equal(vGeo(("10", "12.33")), vGeo(("100.00", "12.330"))) + + +def test_vBinary(): + assert_equal(vBinary("asd"), vBinary("asd")) + assert_not_equal(vBinary("asdf"), vBinary("asd")) + + +def test_vBoolean(): + assert_equal(vBoolean.from_ical("TRUE"), vBoolean.from_ical("TRUE")) + assert_equal(vBoolean.from_ical("FALSE"), vBoolean.from_ical("FALSE")) + assert_not_equal(vBoolean.from_ical("TRUE"), vBoolean.from_ical("FALSE")) + + +def test_vCategory(): + assert_equal(vCategory("HELLO"), vCategory("HELLO")) + assert_equal(vCategory(["a", "b"]), vCategory(["a", "b"])) + assert_not_equal(vCategory(["a", "b"]), vCategory(["a", "b", "c"])) + + +def test_vText(): + assert_equal(vText("HELLO"), vText("HELLO")) + assert_not_equal(vText("HELLO1"), vText("HELLO")) + + +@pytest.mark.parametrize( + ("vType", "v1", "v2"), + [ + (vDatetime, datetime(2023, 11, 1, 10, 11), datetime(2023, 11, 1, 10, 10)), + (vDate, date(2023, 11, 1), date(2023, 10, 31)), + (vDuration, timedelta(3, 11, 1), timedelta(23, 10, 31)), + ( + vPeriod, + (datetime(2023, 11, 1, 10, 11), timedelta(3, 11, 1)), + (datetime(2023, 11, 1, 10, 11), timedelta(23, 10, 31)), + ), + ( + vPeriod, + (datetime(2023, 11, 1, 10, 1), timedelta(3, 11, 1)), + (datetime(2023, 11, 1, 10, 11), timedelta(3, 11, 1)), + ), + ( + vPeriod, + (datetime(2023, 11, 1, 10, 1), datetime(2023, 11, 1, 10, 3)), + (datetime(2023, 11, 1, 10, 1), datetime(2023, 11, 1, 10, 2)), + ), + (vTime, time(10, 10, 10), time(10, 10, 11)), + ], +) +@pytest.mark.parametrize("eq", ["==", "!="]) +@pytest.mark.parametrize("cls1", [0, 1]) +@pytest.mark.parametrize("cls2", [0, 1]) +@pytest.mark.parametrize("hash", [lambda x: x, hash]) +def test_vDDDTypes_and_others(vType, v1, v2, cls1, cls2, eq, hash): + """Check equality and inequality.""" + t1 = (vType, vDDDTypes)[cls1] + t2 = (vType, vDDDTypes)[cls2] + if eq == "==": + assert hash(v1) == hash(v1) + assert hash(t1(v1)) == hash(t2(v1)) + assert hash(t1(v1)) == hash(t2(v1)) + else: + assert hash(v1) != hash(v2) + assert hash(t1(v1)) != hash(t2(v2)) + + +def test_repr_vDDDTypes(): + assert "vDDDTypes" in repr(vDDDTypes(timedelta(3, 11, 1))) + + +vDDDLists_examples = [ # noqa: N816 + vDDDLists([]), + vDDDLists([datetime(2023, 11, 1, 10, 1)]), + vDDDLists([datetime(2023, 11, 1, 10, 1), date(2023, 11, 1)]), +] + + +@pytest.mark.parametrize("l1", vDDDLists_examples) +@pytest.mark.parametrize("l2", vDDDLists_examples) +def test_vDDDLists(l1, l2): + """Check the equality functions of vDDDLists.""" + equal = l1 is l2 + l2 = copy.deepcopy(l2) + assert equal == (l1 == l2) + assert equal != (l1 != l2) diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_examples.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_examples.py new file mode 100644 index 0000000..d6b1e7f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_examples.py @@ -0,0 +1,72 @@ +"""tests ensuring that *the* way of doing things works""" + +import datetime + +import pytest + +from icalendar import Calendar, Event, Timezone + + +def test_creating_calendar_with_unicode_fields(calendars, utc): + """create a calendar with events that contain unicode characters in their fields""" + cal = Calendar() + cal.add("PRODID", "-//Plönë.org//NONSGML plone.app.event//EN") + cal.add("VERSION", "2.0") + cal.add("X-WR-CALNAME", "äöü ÄÖÜ €") + cal.add("X-WR-CALDESC", "test non ascii: äöü ÄÖÜ €") + cal.add("X-WR-RELCALID", "12345") + + event = Event() + event.add("DTSTART", datetime.datetime(2010, 10, 10, 10, 0, 0, tzinfo=utc)) + event.add("DTEND", datetime.datetime(2010, 10, 10, 12, 0, 0, tzinfo=utc)) + event.add("CREATED", datetime.datetime(2010, 10, 10, 0, 0, 0, tzinfo=utc)) + event.add("UID", "123456") + event.add("SUMMARY", "Non-ASCII Test: ÄÖÜ äöü €") + event.add("DESCRIPTION", "icalendar should be able to de/serialize non-ascii.") + event.add("LOCATION", "Tribstrül") + cal.add_component(event) + + # test_create_event_simple + event1 = Event() + event1.add("DTSTART", datetime.datetime(2010, 10, 10, 0, 0, 0, tzinfo=utc)) + event1.add("SUMMARY", "åäö") + cal.add_component(event1) + + # test_unicode_parameter_name + # test for issue #80 https://github.com/collective/icalendar/issues/80 + event2 = Event() + event2.add("DESCRIPTION", "äöüßÄÖÜ") + cal.add_component(event2) + + assert cal.to_ical() == calendars.created_calendar_with_unicode_fields.raw_ics + + +@pytest.mark.parametrize( + ("component", "example"), + [ + (Calendar, "example"), + (Calendar, "example.ics"), + (Event, "event_with_rsvp"), + (Timezone, "pacific_fiji"), + ], +) +def test_component_has_examples(tzp, calendars, timezones, events, component, example): + """Check that the examples function works.""" + mapping = {Calendar: calendars, Event: events, Timezone: timezones} + example_component = component.example(example) + expected_component = mapping[component][example] + assert example_component == expected_component + + +def test_invalid_examples_lists_the_others(): + """We need a bit of guidance here.""" + with pytest.raises(ValueError) as e: + Calendar.example("does not exist") + assert "example.ics" in str(e.value) + + +@pytest.mark.parametrize("component", [Calendar, Event, Timezone]) +def test_default_example(component): + """Check that we have a default example.""" + example = component.example() + assert isinstance(example, component) diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_icalendar.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_icalendar.py new file mode 100644 index 0000000..531eb90 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_icalendar.py @@ -0,0 +1,304 @@ +import unittest + +from icalendar.parser import ( + Contentline, + Contentlines, + Parameters, + dquote, + foldline, + q_join, + q_split, +) +from icalendar.prop import vText + + +class IcalendarTestCase(unittest.TestCase): + def setUp(self): + if not hasattr(self, "assertRaisesRegex"): + self.assertRaisesRegex = self.assertRaisesRegexp + + def test_long_lines(self): + c = Contentlines([Contentline("BEGIN:VEVENT")]) + c.append(Contentline("".join("123456789 " * 10))) + self.assertEqual( + c.to_ical(), + b"BEGIN:VEVENT\r\n123456789 123456789 123456789 123456789 " + b"123456789 123456789 123456789 1234\r\n 56789 123456789 " + b"123456789 \r\n", + ) + + # from doctests + # Notice that there is an extra empty string in the end of the content + # lines. That is so they can be easily joined with: + # '\r\n'.join(contentlines)) + self.assertEqual( + Contentlines.from_ical("A short line\r\n"), ["A short line", ""] + ) + self.assertEqual( + Contentlines.from_ical("A faked\r\n long line\r\n"), + ["A faked long line", ""], + ) + self.assertEqual( + Contentlines.from_ical( + "A faked\r\n long line\r\nAnd another lin\r\n\te that is folded\r\n" + ), + ["A faked long line", "And another line that is folded", ""], + ) + + def test_contentline_class(self): + self.assertEqual( + Contentline("Si meliora dies, ut vina, poemata reddit").to_ical(), + b"Si meliora dies, ut vina, poemata reddit", + ) + + # A long line gets folded + c = Contentline("".join(["123456789 "] * 10)).to_ical() + self.assertEqual( + c, + ( + b"123456789 123456789 123456789 123456789 123456789 123456789 " + b"123456789 1234\r\n 56789 123456789 123456789 " + ), + ) + + # A folded line gets unfolded + self.assertEqual( + Contentline.from_ical(c), + ( + "123456789 123456789 123456789 123456789 123456789 123456789 " + "123456789 123456789 123456789 123456789 " + ), + ) + + # https://tools.ietf.org/html/rfc5545#section-3.3.11 + # An intentional formatted text line break MUST only be included in + # a "TEXT" property value by representing the line break with the + # character sequence of BACKSLASH, followed by a LATIN SMALL LETTER + # N or a LATIN CAPITAL LETTER N, that is "\n" or "\N". + + # Newlines are not allowed in content lines + self.assertRaises(AssertionError, Contentline, b"1234\r\n\r\n1234") + + self.assertEqual(Contentline("1234\\n\\n1234").to_ical(), b"1234\\n\\n1234") + + # We do not fold within a UTF-8 character + c = Contentline( + b"This line has a UTF-8 character where it should be " + b"folded. Make sure it g\xc3\xabts folded before that " + b"character." + ) + + self.assertIn(b"\xc3\xab", c.to_ical()) + + # Another test of the above + c = Contentline(b"x" * 73 + b"\xc3\xab" + b"\\n " + b"y" * 10) + + self.assertEqual(c.to_ical().count(b"\xc3"), 1) + + # Don't fail if we fold a line that is exactly X times 74 characters + # long + c = Contentline("".join(["x"] * 148)).to_ical() + + # It can parse itself into parts, + # which is a tuple of (name, params, vals) + self.assertEqual( + Contentline("dtstart:20050101T120000").parts(), + ("dtstart", Parameters({}), "20050101T120000"), + ) + + self.assertEqual( + Contentline("dtstart;value=datetime:20050101T120000").parts(), + ("dtstart", Parameters({"VALUE": "datetime"}), "20050101T120000"), + ) + + c = Contentline( + "ATTENDEE;CN=Max Rasmussen;ROLE=REQ-PARTICIPANT:MAILTO:maxm@example.com" + ) + self.assertEqual( + c.parts(), + ( + "ATTENDEE", + Parameters({"ROLE": "REQ-PARTICIPANT", "CN": "Max Rasmussen"}), + "MAILTO:maxm@example.com", + ), + ) + self.assertEqual( + c.to_ical().decode("utf-8"), + "ATTENDEE;CN=Max Rasmussen;ROLE=REQ-PARTICIPANT:MAILTO:maxm@example.com", + ) + + # and back again + # NOTE: we are quoting property values with spaces in it. + parts = ( + "ATTENDEE", + Parameters({"ROLE": "REQ-PARTICIPANT", "CN": "Max Rasmussen"}), + "MAILTO:maxm@example.com", + ) + self.assertEqual( + Contentline.from_parts(*parts), + 'ATTENDEE;CN="Max Rasmussen";ROLE=REQ-PARTICIPANT:' + "MAILTO:maxm@example.com", + ) + + # and again + parts = ("ATTENDEE", Parameters(), "MAILTO:maxm@example.com") + self.assertEqual( + Contentline.from_parts(*parts), "ATTENDEE:MAILTO:maxm@example.com" + ) + + # A value can also be any of the types defined in PropertyValues + parts = ("ATTENDEE", Parameters(), vText("MAILTO:test@example.com")) + self.assertEqual( + Contentline.from_parts(*parts), "ATTENDEE:MAILTO:test@example.com" + ) + + # A value in UTF-8 + parts = ("SUMMARY", Parameters(), vText("INternational char æ ø å")) + self.assertEqual( + Contentline.from_parts(*parts), "SUMMARY:INternational char æ ø å" + ) + + # A value can also be unicode + parts = ("SUMMARY", Parameters(), vText("INternational char æ ø å")) + self.assertEqual( + Contentline.from_parts(*parts), "SUMMARY:INternational char æ ø å" + ) + + # Traversing could look like this. + name, params, vals = c.parts() + self.assertEqual(name, "ATTENDEE") + self.assertEqual(vals, "MAILTO:maxm@example.com") + self.assertEqual( + sorted(params.items()), + sorted([("ROLE", "REQ-PARTICIPANT"), ("CN", "Max Rasmussen")]), + ) + + # And the traditional failure + with self.assertRaisesRegex( + ValueError, "Content line could not be parsed into parts" + ): + Contentline("ATTENDEE;maxm@example.com").parts() + + # Another failure: + with self.assertRaisesRegex( + ValueError, "Content line could not be parsed into parts" + ): + Contentline(":maxm@example.com").parts() + + self.assertEqual( + Contentline("key;param=:value").parts(), + ("key", Parameters({"PARAM": ""}), "value"), + ) + + self.assertEqual( + Contentline('key;param="pvalue":value').parts(), + ("key", Parameters({"PARAM": "pvalue"}), "value"), + ) + + # Should bomb on missing param: + with self.assertRaisesRegex( + ValueError, "Content line could not be parsed into parts" + ): + Contentline.from_ical("k;:no param").parts() + + self.assertEqual( + Contentline("key;param=pvalue:value", strict=False).parts(), + ("key", Parameters({"PARAM": "pvalue"}), "value"), + ) + + # If strict is set to True, uppercase param values that are not + # double-quoted, this is because the spec says non-quoted params are + # case-insensitive. + self.assertEqual( + Contentline("key;param=pvalue:value", strict=True).parts(), + ("key", Parameters({"PARAM": "PVALUE"}), "value"), + ) + + self.assertEqual( + Contentline('key;param="pValue":value', strict=True).parts(), + ("key", Parameters({"PARAM": "pValue"}), "value"), + ) + + contains_base64 = ( + b"X-APPLE-STRUCTURED-LOCATION;" + b'VALUE=URI;X-ADDRESS="Kaiserliche Hofburg, 1010 Wien";' + b"X-APPLE-MAPKIT-HANDLE=CAESxQEZgr3QZXJyZWljaA==;" + b"X-APPLE-RADIUS=328.7978217977285;X-APPLE-REFERENCEFRAME=1;" + b"X-TITLE=Heldenplatz:geo:48.206686,16.363235" + ) + + self.assertEqual( + Contentline(contains_base64, strict=True).parts(), + ( + "X-APPLE-STRUCTURED-LOCATION", + Parameters( + { + "X-APPLE-RADIUS": "328.7978217977285", + "X-ADDRESS": "Kaiserliche Hofburg, 1010 Wien", + "X-APPLE-REFERENCEFRAME": "1", + "X-TITLE": "HELDENPLATZ", + "X-APPLE-MAPKIT-HANDLE": "CAESXQEZGR3QZXJYZWLJAA==", + "VALUE": "URI", + } + ), + "geo:48.206686,16.363235", + ), + ) + + def test_fold_line(self): + self.assertEqual(foldline("foo"), "foo") + self.assertEqual( + foldline( + "Lorem ipsum dolor sit amet, consectetur adipiscing " + "elit. Vestibulum convallis imperdiet dui posuere." + ), + ( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + "Vestibulum conval\r\n lis imperdiet dui posuere." + ), + ) + + # I don't really get this test + # at least just but bytes in there + # porting it to "run" under python 2 & 3 makes it not much better + with self.assertRaises(AssertionError): + foldline("привет".encode(), limit=3) + + self.assertEqual(foldline("foobar", limit=4), "foo\r\n bar") + self.assertEqual( + foldline( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit" + ". Vestibulum convallis imperdiet dui posuere." + ), + ( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit." + " Vestibulum conval\r\n lis imperdiet dui posuere." + ), + ) + self.assertEqual( + foldline("DESCRIPTION:АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЪЭЮЯ"), + "DESCRIPTION:АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЪЭ\r\n ЮЯ", + ) + + def test_value_double_quoting(self): + self.assertEqual(dquote("Max"), "Max") + self.assertEqual(dquote("Rasmussen, Max"), '"Rasmussen, Max"') + self.assertEqual(dquote("name:value"), '"name:value"') + + def test_q_split(self): + self.assertEqual( + q_split('Max,Moller,"Rasmussen, Max"'), + ["Max", "Moller", '"Rasmussen, Max"'], + ) + + def test_q_split_bin(self): + for s in ("X-SOMETHING=ABCDE==", ",,,"): + for maxsplit in range(-1, 3): + self.assertEqual( + q_split(s, "=", maxsplit=maxsplit), s.split("=", maxsplit) + ) + + def test_q_join(self): + self.assertEqual( + q_join(["Max", "Moller", "Rasmussen, Max"]), 'Max,Moller,"Rasmussen, Max"' + ) diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_116.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_116.py new file mode 100644 index 0000000..a2ef073 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_116.py @@ -0,0 +1,28 @@ +import icalendar + + +def test_issue_116(): + """Issue #116/#117 - How to add 'X-APPLE-STRUCTURED-LOCATION' + https://github.com/collective/icalendar/issues/116 + https://github.com/collective/icalendar/issues/117 + """ + event = icalendar.Event() + event.add( + "X-APPLE-STRUCTURED-LOCATION", + "geo:-33.868900,151.207000", + parameters={ + "VALUE": "URI", + "X-ADDRESS": "367 George Street Sydney CBD NSW 2000", + "X-APPLE-RADIUS": "72", + "X-TITLE": "367 George Street", + }, + ) + assert event.to_ical() == ( + b"BEGIN:VEVENT\r\nX-APPLE-STRUCTURED-LOCATION;VALUE=URI;" + b'X-ADDRESS="367 George Street Sydney \r\n CBD NSW 2000";' + b'X-APPLE-RADIUS=72;X-TITLE="367 George Street":' + b"geo:-33.868900\r\n \\,151.207000\r\nEND:VEVENT\r\n" + ) + + # roundtrip + assert event.to_ical() == icalendar.Event.from_ical(event.to_ical()).to_ical() diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_165_missing_event.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_165_missing_event.py new file mode 100644 index 0000000..8e9e736 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_165_missing_event.py @@ -0,0 +1,9 @@ +"""Issue #165 - Problem parsing a file with event recurring on weekdays + +https://github.com/collective/icalendar/issues/165 +""" + + +def test_issue_165_missing_event(calendars): + events = list(calendars.issue_165_missing_event.walk("VEVENT")) + assert len(events) == 1, "There was an event missing from the parsed events' list." diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_168_parsing_invalid_calendars_no_warning.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_168_parsing_invalid_calendars_no_warning.py new file mode 100644 index 0000000..f747238 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_168_parsing_invalid_calendars_no_warning.py @@ -0,0 +1,16 @@ +"""Issue #168 - Parsing invalid icalendars fails without any warning + +https://github.com/collective/icalendar/issues/168 +""" + + +def test_issue_168_parsing_inavlid_calendars_no_warning(calendars): + expected_error = ( + None, + "Content line could not be parsed into parts: 'X-APPLE-RADIUS=49.91307046514149': X-APPLE-RADIUS=49.91307046514149", + ) + assert expected_error in calendars.issue_168_input.walk("VEVENT")[0].errors + assert ( + calendars.issue_168_input.to_ical() + == calendars.issue_168_expected_output.raw_ics + ) diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_218_parse_calendar.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_218_parse_calendar.py new file mode 100644 index 0000000..032f9a4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_218_parse_calendar.py @@ -0,0 +1,32 @@ +"""Parse the calendar and make sure the timezone is used. + +See https://github.com/collective/icalendar/issues/218 +""" +from __future__ import annotations + +from datetime import datetime, timedelta +from typing import TYPE_CHECKING + +import pytest + +from icalendar.timezone.tzid import tzid_from_dt + +if TYPE_CHECKING: + from icalendar import Event + +@pytest.fixture() +def event(calendars) -> Event: + """The event to check.""" + return calendars.issue_218_bad_tzid.events[0] + + +def test_event_has_start_and_end(event : Event): + """The calendar should be parsed and the start and end have a timezone.""" + assert event.start.replace(tzinfo=None) == datetime(2017, 2, 28, 23, 00) + assert event.end.replace(tzinfo=None) == datetime(2017, 2, 28, 23, 30) + +def test_timezone(event:Event): + """The event uses a timezone.""" + assert event.start.tzinfo == event.end.tzinfo + assert tzid_from_dt(event.start) == "UTC+11" + assert event.start.utcoffset() == timedelta(hours=11) diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_27_period.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_27_period.py new file mode 100644 index 0000000..a25af91 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_27_period.py @@ -0,0 +1,19 @@ +"""Issue #27 - multiple periods + +https://github.com/collective/icalendar/issues/27 +""" + + +def test_issue_27_multiple_periods(calendars): + free_busy = list( + calendars.issue_27_multiple_periods_in_freebusy_multiple_freebusies.walk( + "VFREEBUSY" + ) + )[0] + free_busy_period = free_busy["freebusy"] + print(free_busy["freebusy"]) + equivalent_way_of_defining_free_busy = list( + calendars.issue_27_multiple_periods_in_freebusy_one_freebusy.walk("VFREEBUSY") + )[0] + free_busy_period_equivalent = equivalent_way_of_defining_free_busy["freebusy"] + assert free_busy_period == free_busy_period_equivalent diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_301_add_rrule_as_string.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_301_add_rrule_as_string.py new file mode 100644 index 0000000..0d5d71a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_301_add_rrule_as_string.py @@ -0,0 +1,14 @@ +"""We want to add rrule as a string for convenience. + +See https://github.com/collective/icalendar/issues/301 +""" + +from icalendar.cal import Event + + +def test_rrule_add_example(): + event = Event() + event.add("RRULE", "FREQ=DAILY;INTERVAL=2") + assert "FREQ=DAILY;INTERVAL=2" in event.to_ical().decode("utf-8") + assert event.rrules[0]["freq"] == ["DAILY"] + assert event.rrules[0]["interval"] == [2] diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_318_skip_default_parameters.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_318_skip_default_parameters.py new file mode 100644 index 0000000..78e5a09 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_318_skip_default_parameters.py @@ -0,0 +1,32 @@ +"""Some parameters are specified as default by the RFC 5545. + +These tests make sure that these parameter values are not added to the +properties. + +Example: + + DTSTART;VALUE=DATE-TIME:20190616T050000Z +equals DTSTART:20190616T050000Z +""" + +from datetime import datetime + +import pytest + +from icalendar import Event + + +@pytest.mark.parametrize( + "attr", + [ + "DTSTART", + "DTEND", + "DTSTAMP", + ], +) +def test_datetime_in_event(attr): + """Check that the "VALUE=DATE-TIME" is absent because not needed.""" + event = Event() + event.add(attr, datetime(2022, 10, 13, 9, 16, 42)) + ics = event.to_ical() + assert b"VALUE=DATE-TIME" not in ics diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_322_single_strings_characters_split_into_multiple_categories.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_322_single_strings_characters_split_into_multiple_categories.py new file mode 100644 index 0000000..d99ccb3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_322_single_strings_characters_split_into_multiple_categories.py @@ -0,0 +1,10 @@ +from icalendar import Calendar, Event + + +def test_issue_322_single_string_split_into_multiple_categories(calendars): + calendar = Calendar() + event = Event() + event.add("summary", "Event with bare string as argument for categories") + event.add("categories", "Lecture") + calendar.add_component(event) + assert calendar.to_ical() == calendars.issue_322_expected_calendar.raw_ics diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_336_dateutil_timezone.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_336_dateutil_timezone.py new file mode 100644 index 0000000..2d923f4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_336_dateutil_timezone.py @@ -0,0 +1,30 @@ +"""We would like to be able to get the right timezone names. + +See https://github.com/collective/icalendar/issues/336 + +It appears that the timezone Brazil/DeNoronha is actually America/Noronha. +""" + +from datetime import datetime + +from dateutil import tz + +from icalendar import Event +from icalendar.timezone import tzid_from_tzinfo + +valid_names = ("America/Noronha", "Brazil/DeNoronha") + + +def test_timezone_name_directly(): + """Try to get the name directly.""" + tzinfo = tz.gettz("Brazil/DeNoronha") + assert tzid_from_tzinfo(tzinfo) in valid_names + + +def test_in_event(): + """The example has an event in it and we want to have the id in it.""" + event = Event() + event.start = datetime(2025, 5, 13, 14, 23, tzinfo=tz.gettz("Brazil/DeNoronha")) + ics = event.to_ical().decode() + print(ics) + assert any(name in ics for name in valid_names) diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_348_exception_parsing_value.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_348_exception_parsing_value.py new file mode 100644 index 0000000..80a3440 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_348_exception_parsing_value.py @@ -0,0 +1,23 @@ +"""These are tests for Issue #348 + +see https://github.com/collective/icalendar/issues/348 +""" + + +def test_calendar_can_be_parsed_correctly(calendars): + """Exception when there's no ':' when parsing value #348 + + see https://github.com/collective/icalendar/issues/348 + """ + freebusy = calendars.issue_348_exception_parsing_value.walk("VFREEBUSY")[0] + assert freebusy["ORGANIZER"].params["CN"] == "Sixt SE" + + +def test_parameters_are_not_truncated(calendars): + """We skip to the end and we do not want to loose parameters. + + see https://github.com/collective/icalendar/pull/514#issuecomment-1505878801 + """ + freebusy = calendars.issue_348_exception_parsing_value.walk("VFREEBUSY")[0] + assert freebusy["X-ORGANIZER2"].params["CN"] == "Sixt SE" + assert freebusy["X-ORGANIZER2"].params["CN2"] == "Test!" diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_350.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_350.py new file mode 100644 index 0000000..d7ad545 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_350.py @@ -0,0 +1,9 @@ +"""Issue #350 - Ignore X-... properties also at end of file? + +https://github.com/collective/icalendar/issues/350 +""" + + +def test_issue_350(calendars): + calendar = list(calendars.issue_350.walk("X-COMMENT")) + assert len(calendar) == 0, "X-COMMENT at the end of the file was parsed" diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_500_vboolean_for_parameter.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_500_vboolean_for_parameter.py new file mode 100644 index 0000000..23b6b2a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_500_vboolean_for_parameter.py @@ -0,0 +1,10 @@ +from icalendar import Event, vBoolean, vCalAddress + + +def test_vBoolean_can_be_used_as_parameter_issue_500(events): + """https://github.com/collective/icalendar/issues/500""" + attendee = vCalAddress("mailto:someone@example.com") + attendee.params["rsvp"] = vBoolean(True) + event = Event() + event.add("attendee", attendee) + assert event.to_ical() == events.event_with_rsvp.raw_ics diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_557_encode_native_parameters.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_557_encode_native_parameters.py new file mode 100644 index 0000000..0c19cf4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_557_encode_native_parameters.py @@ -0,0 +1,151 @@ +"""These are tests for Issue #557 + +TL;DR: Component._encode lost given parameters +if the object to encode was already of native type, +making its behavior unexpected. + +see https://github.com/collective/icalendar/issues/557""" + +import unittest + +from icalendar.cal import Component + + +class TestComponentEncode(unittest.TestCase): + def test_encode_non_native_parameters(self): + """Test _encode to add parameters to non-natives""" + self.__assert_native_content(self.summary) + self.__assert_native_kept_parameters(self.summary) + + def test_encode_native_keep_params_None(self): + """_encode should keep parameters on natives + if parameters=None + """ + new_sum = self.__add_params( + self.summary, + parameters=None, + ) + self.__assert_native_content(new_sum) + self.__assert_native_kept_parameters(new_sum) + + def test_encode_native_keep_params_empty(self): + """_encode should keep paramters on natives + if parameters={} + """ + new_sum = self.__add_params( + self.summary, + parameters={}, + ) + self.__assert_native_content(new_sum) + self.__assert_native_kept_parameters(new_sum) + + def test_encode_native_append_params(self): + """_encode should append paramters on natives + keeping old parameters + """ + new_sum = self.__add_params( + self.summary, + parameters={"X-PARAM": "Test123"}, + ) + self.__assert_native_content(new_sum) + self.__assert_native_kept_parameters(new_sum) + self.assertParameter(new_sum, "X-PARAM", "Test123") + + def test_encode_native_overwrite_params(self): + """_encode should overwrite single parameters + if they have the same name as old ones""" + new_sum = self.__add_params( + self.summary, + parameters={"LANGUAGE": "de"}, + ) + self.__assert_native_content(new_sum) + self.assertParameter(new_sum, "LANGUAGE", "de") + + def test_encode_native_remove_params(self): + """_encode should remove single parameters + if they are explicitly set to None""" + new_sum = self.__add_params( + self.summary, + parameters={"LANGUAGE": None}, + ) + self.__assert_native_content(new_sum) + self.assertParameterMissing(new_sum, "LANGUAGE") + + def test_encode_native_remove_already_missing(self): + """_encode should ignore removing a parameter + that was already missing""" + self.assertParameterMissing(self.summary, "X-MISSING") + new_sum = self.__add_params( + self.summary, + parameters={"X-MISSING": None}, + ) + self.__assert_native_content(new_sum) + self.__assert_native_kept_parameters(new_sum) + self.assertParameterMissing(self.summary, "X-MISSING") + + def test_encode_native_full_test(self): + """full test case with keeping, overwriting & removing properties""" + # preperation + orig_sum = self.__add_params( + self.summary, + parameters={ + "X-OVERWRITE": "overwrite me!", + "X-REMOVE": "remove me!", + "X-MISSING": None, + }, + ) + # preperation check + self.__assert_native_content(orig_sum) + self.__assert_native_kept_parameters(orig_sum) + self.assertParameter(orig_sum, "X-OVERWRITE", "overwrite me!") + self.assertParameter(orig_sum, "X-REMOVE", "remove me!") + self.assertParameterMissing(orig_sum, "X-MISSING") + # modification + new_sum = self.__add_params( + orig_sum, + parameters={ + "X-OVERWRITE": "overwritten", + "X-REMOVE": None, + "X-MISSING": None, + }, + ) + # final asserts + self.__assert_native_content(new_sum) + self.__assert_native_kept_parameters(new_sum) + self.assertParameter(new_sum, "X-OVERWRITE", "overwritten") + self.assertParameterMissing(new_sum, "X-REMOVE") + self.assertParameterMissing(new_sum, "X-MISSING") + + def setUp(self): + self.summary = self.__gen_native() + + def __assert_native_kept_parameters(self, obj): + self.assertParameter(obj, "LANGUAGE", "en") + + def __assert_native_content(self, obj): + self.assertEqual(obj, "English Summary") + + def __add_params(self, obj, parameters): + return Component._encode( + "SUMMARY", + obj, + parameters=parameters, + encode=True, + ) + + def __gen_native(self): + return Component._encode( + "SUMMARY", + "English Summary", + parameters={ + "LANGUAGE": "en", + }, + encode=True, + ) + + def assertParameterMissing(self, obj, name): + self.assertNotIn(name, obj.params) + + def assertParameter(self, obj, name, val): + self.assertIn(name, obj.params) + self.assertEqual(obj.params[name], val) diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_662_component_properties.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_662_component_properties.py new file mode 100644 index 0000000..fed41f7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_662_component_properties.py @@ -0,0 +1,711 @@ +"""This tests the properties of components and their types.""" + +from __future__ import annotations +from datetime import date, datetime, timedelta + +import pytest + +from icalendar.error import IncompleteComponent, InvalidCalendar +from icalendar.cal import Alarm + +try: + from zoneinfo import ZoneInfo +except ImportError: + from backports.zoneinfo import ZoneInfo # type: ignore PGH003 + +from icalendar import ( + Event, + Journal, + Todo, + vDDDTypes, + vDatetime, +) +from icalendar.prop import vDuration + + +def prop(component: Event | Todo, prop: str) -> str: + """Translate the end property. + + This allows us to run the same tests on Event and Todo. + """ + if isinstance(component, Todo) and prop.upper() == "DTEND": + return "DUE" + return prop + + +@pytest.fixture(params=[Event, Todo]) +def start_end_component(request): + """The event to test.""" + return request.param() + + +@pytest.fixture( + params=[ + datetime(2022, 7, 22, 12, 7), + date(2022, 7, 22), + datetime(2022, 7, 22, 13, 7, tzinfo=ZoneInfo("Europe/Paris")), + ] +) +def dtstart(request, set_component_start, start_end_component): + """Start of the event.""" + set_component_start(start_end_component, request.param) + return request.param + + +def _set_component_start_init(component, start): + """Create the event with the __init__ method.""" + d = dict(component) + d["dtstart"] = vDDDTypes(start) + component.clear() + component.update(type(component)(d)) + + +def _set_component_dtstart(component, start): + """Create the event with the dtstart property.""" + component.DTSTART = start + + +def _set_component_start_attr(component, start): + """Create the event with the dtstart property.""" + component.start = start + + +def _set_component_start_ics(component, start): + """Create the event with the start property.""" + component.add("dtstart", start) + ics = component.to_ical().decode() + print(ics) + component.clear() + component.update(type(component).from_ical(ics)) + + +@pytest.fixture( + params=[ + _set_component_start_init, + _set_component_start_ics, + _set_component_dtstart, + _set_component_start_attr, + ] +) +def set_component_start(request): + """Create a new event.""" + return request.param + + +def test_component_dtstart(dtstart, start_end_component): + """Test the start of events.""" + assert start_end_component.DTSTART == dtstart + + +def test_event_start(dtstart, start_end_component): + """Test the start of events.""" + assert start_end_component.start == dtstart + + +invalid_start_event_1 = Event() +invalid_start_event_1.add("dtstart", datetime(2022, 7, 22, 12, 7)) +invalid_start_event_1.add("dtstart", datetime(2022, 7, 22, 12, 8)) +invalid_start_event_2 = Event.from_ical(invalid_start_event_1.to_ical()) +invalid_start_event_3 = Event() +invalid_start_event_3.add("DTSTART", (date(2018, 1, 1), date(2018, 2, 1))) +invalid_start_todo_1 = Todo(invalid_start_event_1) +invalid_start_todo_2 = Todo(invalid_start_event_2) +invalid_start_todo_3 = Todo(invalid_start_event_3) + + +@pytest.mark.parametrize( + "invalid_event", + [ + invalid_start_event_1, + invalid_start_event_2, + invalid_start_event_3, + invalid_start_todo_1, + invalid_start_todo_2, + invalid_start_todo_3, + ], +) +def test_multiple_dtstart(invalid_event): + """Check that we get the right error.""" + with pytest.raises(InvalidCalendar): + invalid_event.start # noqa: B018 + with pytest.raises(InvalidCalendar): + invalid_event.DTSTART # noqa: B018 + + +def test_no_dtstart(start_end_component): + """DTSTART is optional. + + The following is REQUIRED if the component + appears in an iCalendar object that doesn't + specify the "METHOD" property; otherwise, it + is OPTIONAL; in any case, it MUST NOT occur + more than once. + """ + assert start_end_component.DTSTART is None + with pytest.raises(IncompleteComponent): + start_end_component.start # noqa: B018 + + +@pytest.fixture( + params=[ + datetime(2022, 7, 22, 12, 8), + date(2022, 7, 23), + datetime(2022, 7, 22, 14, 7, tzinfo=ZoneInfo("Europe/Paris")), + ] +) +def dtend(request, set_component_end, start_end_component): + """end of the event.""" + set_component_end(start_end_component, request.param) + return request.param + + +def _set_component_end_init(component, end): + """Create the event with the __init__ method.""" + d = dict(component) + d[prop(component, "dtend")] = vDDDTypes(end) + component.clear() + component.update(type(component)(d)) + + +def _set_component_end_property(component, end): + """Create the event with the dtend property.""" + setattr(component, prop(component, "DTEND"), end) + + +def _set_component_end_attr(component, end): + """Create the event with the dtend property.""" + component.end = end + + +def _set_component_end_ics(component, end): + """Create the event with the end property.""" + component.add(prop(component, "DTEND"), end) + ics = component.to_ical().decode() + print(ics) + component.clear() + component.update(type(component).from_ical(ics)) + + +@pytest.fixture( + params=[ + _set_component_end_init, + _set_component_end_ics, + _set_component_end_property, + _set_component_end_attr, + ] +) +def set_component_end(request): + """Create a new event.""" + return request.param + + +def test_component_end_property(dtend, start_end_component): + """Test the end of events.""" + attr = prop(start_end_component, "DTEND") + assert getattr(start_end_component, attr) == dtend # noqa: SIM300 + + +def test_component_end(dtend, start_end_component): + """Test the end of events.""" + assert start_end_component.end == dtend + + +@pytest.mark.parametrize("attr", ["DTSTART", "DTEND"]) +def test_delete_attr(start_end_component, dtstart, dtend, attr): + attr = prop(start_end_component, attr) + delattr(start_end_component, attr) + assert getattr(start_end_component, attr) is None + delattr(start_end_component, attr) + + +def _set_duration_vdddtypes(event: Event, duration: timedelta): + """Set the vDDDTypes value""" + event["DURATION"] = vDDDTypes(duration) + + +def _set_duration_add(event: Event, duration: timedelta): + """Set the vDDDTypes value""" + event.add("DURATION", duration) + + +def _set_duration_vduration(event: Event, duration: timedelta): + """Set the vDDDTypes value""" + event["DURATION"] = vDuration(duration) + + +@pytest.fixture( + params=[_set_duration_vdddtypes, _set_duration_add, _set_duration_vduration] +) +def duration(start_end_component, dtstart, request): + """... events have a DATE value type for the "DTSTART" property ... + If such a "VEVENT" has a "DURATION" + property, it MUST be specified as a "dur-day" or "dur-week" value. + """ + duration = ( + timedelta(hours=1) if isinstance(dtstart, datetime) else timedelta(days=2) + ) + request.param(start_end_component, duration) + return duration + + +def test_start_and_duration(start_end_component, dtstart, duration): + """Check calculation of end with duration.""" + dur = start_end_component.end - start_end_component.start + assert dur == duration + assert start_end_component.duration == duration + + +# The "VEVENT" is also the calendar component used to specify an +# anniversary or daily reminder within a calendar. These events +# have a DATE value type for the "DTSTART" property instead of the +# default value type of DATE-TIME. If such a "VEVENT" has a "DTEND" +# property, it MUST be specified as a DATE value also. +invalid_event_end_1 = Event() +invalid_event_end_1.add("DTSTART", datetime(2024, 1, 1, 10, 20)) +invalid_event_end_1.add("DTEND", date(2024, 1, 1)) +invalid_event_end_2 = Event() +invalid_event_end_2.add("DTEND", datetime(2024, 1, 1, 10, 20)) +invalid_event_end_2.add("DTSTART", date(2024, 1, 1)) +invalid_event_end_3 = Event() +invalid_event_end_3.add("DTEND", datetime(2024, 1, 1, 10, 20)) +invalid_event_end_3.add("DTSTART", datetime(2024, 1, 1, 10, 20)) +invalid_event_end_3.add("DURATION", timedelta(days=1)) +invalid_event_end_4 = Event() +invalid_event_end_4.add("DTSTART", date(2024, 1, 1)) +invalid_event_end_4.add("DURATION", timedelta(hours=1)) + +invalid_todo_end_1 = Todo() +invalid_todo_end_1.add("DTSTART", datetime(2024, 1, 1, 10, 20)) +invalid_todo_end_1.add("DUE", date(2024, 1, 1)) +invalid_todo_end_2 = Todo() +invalid_todo_end_2.add("DUE", datetime(2024, 1, 1, 10, 20)) +invalid_todo_end_2.add("DTSTART", date(2024, 1, 1)) +invalid_todo_end_3 = Todo() +invalid_todo_end_3.add("DUE", datetime(2024, 1, 1, 10, 20)) +invalid_todo_end_3.add("DTSTART", datetime(2024, 1, 1, 10, 20)) +invalid_todo_end_3.add("DURATION", timedelta(days=1)) +invalid_todo_end_4 = Todo() +invalid_todo_end_4.add("DTSTART", date(2024, 1, 1)) +invalid_todo_end_4.add("DURATION", timedelta(hours=1)) + + +@pytest.mark.parametrize( + ("invalid_component", "message"), + [ + ( + invalid_event_end_1, + "DTSTART and DTEND must be of the same type, either date or datetime.", + ), + ( + invalid_event_end_2, + "DTSTART and DTEND must be of the same type, either date or datetime.", + ), + ( + invalid_event_end_3, + "Only one of DTEND and DURATION may be in a VEVENT, not both.", + ), + ( + invalid_event_end_4, + "When DTSTART is a date, DURATION must be of days or weeks.", + ), + ( + invalid_todo_end_1, + "DTSTART and DUE must be of the same type, either date or datetime.", + ), + ( + invalid_todo_end_2, + "DTSTART and DUE must be of the same type, either date or datetime.", + ), + ( + invalid_todo_end_3, + "Only one of DUE and DURATION may be in a VTODO, not both.", + ), + ( + invalid_todo_end_4, + "When DTSTART is a date, DURATION must be of days or weeks.", + ), + ], +) +@pytest.mark.parametrize("attr", ["start", "end"]) +def test_invalid_event(invalid_component, message, attr): + """Test that the end and start throuw the right error.""" + with pytest.raises(InvalidCalendar) as e: + getattr(invalid_component, attr) + assert e.value.args[0] == message + + +def test_event_duration_zero(): + """ + For cases where a "VEVENT" calendar component + specifies a "DTSTART" property with a DATE-TIME value type but no + "DTEND" property, the event ends on the same calendar date and + time of day specified by the "DTSTART" property. + """ + event = Event() + event.start = datetime(2024, 10, 11, 10, 20) + assert event.end == event.start + assert event.duration == timedelta(days=0) + + +def test_event_duration_one_day(): + """ + For cases where a "VEVENT" calendar component + specifies a "DTSTART" property with a DATE value type but no + "DTEND" nor "DURATION" property, the event's duration is taken to + be one day + """ + event = Event() + event.start = date(2024, 10, 11) + assert event.end == event.start + timedelta(days=1) + assert event.duration == timedelta(days=1) + + +def test_todo_duration_zero(): + """We do not know about the duration of a todo really.""" + todo = Todo() + todo.start = datetime(2024, 10, 11, 10, 20) + assert todo.end == todo.start + assert todo.duration == timedelta(days=0) + + +def test_todo_duration_one_day(): + """The end is at the end of the day, excluding midnight. + + RFC 5545: + The following is an example of a "VTODO" calendar + component that needs to be completed before May 1st, 2007. On + midnight May 1st, 2007 this to-do would be considered overdue. + """ + event = Event() + event.start = date(2024, 10, 11) + assert event.end == event.start + timedelta(days=1) + assert event.duration == timedelta(days=1) + + +incomplete_event_1 = Event() +incomplete_event_2 = Event() +incomplete_event_2.add("DURATION", timedelta(hours=1)) +incomplete_todo_1 = Todo() +incomplete_todo_2 = Todo() +incomplete_todo_2.add("DURATION", timedelta(hours=1)) + + +@pytest.mark.parametrize( + "incomplete_event_end", + [ + incomplete_event_1, + incomplete_event_2, + incomplete_todo_1, + incomplete_todo_2, + ], +) +@pytest.mark.parametrize("attr", ["start", "end", "duration"]) +def test_incomplete_event(incomplete_event_end, attr): + """Test that the end throws the right error.""" + with pytest.raises(IncompleteComponent): + getattr(incomplete_event_end, attr) + + +@pytest.mark.parametrize( + "invalid_value", + [ + object(), + timedelta(days=1), + (datetime(2024, 10, 11, 10, 20), timedelta(days=1)), + ], +) +@pytest.mark.parametrize( + ("Component", "attr"), + [ + (Event, "start"), + (Event, "end"), + (Event, "DTSTART"), + (Event, "DTEND"), + (Journal, "start"), + (Journal, "end"), + (Journal, "DTSTART"), + (Todo, "start"), + (Todo, "end"), + (Todo, "DTSTART"), + (Todo, "DUE"), + ], +) +def test_set_invalid_start(invalid_value, attr, Component): + """Check that we get the right error. + + - other types that vDDDTypes accepts + - object + """ + component = Component() + with pytest.raises(TypeError) as e: + setattr(component, attr, invalid_value) + assert ( + e.value.args[0] == f"Use datetime or date, not {type(invalid_value).__name__}." + ) + + +def setitem(d: dict, key, value): + d[key] = value + + +@pytest.mark.parametrize( + "invalid_value", + [ + object(), + None, + (datetime(2024, 10, 11, 10, 20), timedelta(days=1)), + date(2012, 2, 2), + datetime(2022, 2, 2), + ], +) +def test_check_invalid_duration(start_end_component, invalid_value): + """Check that we get the right error.""" + start_end_component["DURATION"] = invalid_value + with pytest.raises(InvalidCalendar) as e: + start_end_component.DURATION # noqa: B018 + assert ( + e.value.args[0] + == f"DURATION must be a timedelta, not {type(invalid_value).__name__}." + ) + + +def test_setting_the_end_deletes_the_duration(start_end_component): + """Setting the end should not break the event.""" + DTEND = prop(start_end_component, "DTEND") + start_end_component.DTSTART = datetime(2024, 10, 11, 10, 20) + start_end_component.DURATION = timedelta(days=1) + setattr(start_end_component, DTEND, datetime(2024, 10, 11, 10, 21)) + assert "DURATION" not in start_end_component + assert start_end_component.DURATION is None + end = getattr(start_end_component, DTEND) + assert end == datetime(2024, 10, 11, 10, 21) + + +def test_setting_duration_deletes_the_end(start_end_component): + """Setting the duration should not break the event.""" + DTEND = prop(start_end_component, "DTEND") + start_end_component.DTSTART = datetime(2024, 10, 11, 10, 20) + setattr(start_end_component, DTEND, datetime(2024, 10, 11, 10, 21)) + start_end_component.DURATION = timedelta(days=1) + assert DTEND not in start_end_component + assert getattr(start_end_component, DTEND) is None + assert start_end_component.DURATION == timedelta(days=1) + + +valid_values = pytest.mark.parametrize( + ("attr", "value"), + [ + ("DTSTART", datetime(2024, 10, 11, 10, 20)), + ("DTEND", datetime(2024, 10, 11, 10, 20)), + ("DURATION", timedelta(days=1)), + ], +) + + +@valid_values +def test_setting_to_none_deletes_value(start_end_component, attr, value): + """Setting attributes to None deletes them.""" + attr = prop(start_end_component, attr) + setattr(start_end_component, attr, value) + assert attr in start_end_component + assert getattr(start_end_component, attr) == value + setattr(start_end_component, attr, None) + assert attr not in start_end_component + + +@valid_values +def test_setting_a_value_twice(start_end_component, attr, value): + """Setting attributes twice replaces them.""" + attr = prop(start_end_component, attr) + setattr(start_end_component, attr, value + timedelta(days=1)) + setattr(start_end_component, attr, value) + assert getattr(start_end_component, attr) == value + + +@pytest.mark.parametrize("attr", ["DTSTART", "DTEND", "DURATION"]) +def test_invalid_none(start_end_component, attr): + """Special case for None.""" + attr = prop(start_end_component, attr) + start_end_component[attr] = None + with pytest.raises(InvalidCalendar): + getattr(start_end_component, attr) + + +def test_delete_duration(start_end_component): + """Test the del command.""" + start_end_component.DURATION = timedelta(days=1) + del start_end_component.DURATION + assert start_end_component.DURATION is None + + +@pytest.mark.parametrize("attr", ["DTSTART", "end", "start"]) +@pytest.mark.parametrize( + "start", + [ + datetime(2024, 10, 11, 10, 20), + date(2024, 10, 11), + datetime(2024, 10, 11, 10, 20, tzinfo=ZoneInfo("Europe/Paris")), + ], +) +def test_journal_start(start, attr): + """Test that we can set the start of a journal.""" + j = Journal() + setattr(j, attr, start) + assert start == j.DTSTART + assert j.start == start + assert j.end == start + assert j.duration == timedelta(0) + + +@pytest.mark.parametrize("attr", ["start", "end"]) +def test_delete_journal_start(attr): + """Delete the start of the journal.""" + j = Journal() + j.start = datetime(2010, 11, 12, 13, 14) + j.DTSTART = None + assert j.DTSTART is None + assert "DTSTART" not in j + with pytest.raises(IncompleteComponent): + getattr(j, attr) + + +def setting_twice_does_not_duplicate_the_entry(): + j = Journal() + j.DTSTART = date(2024, 1, 1) + j.DTSTART = date(2024, 1, 3) + assert date(2024, 1, 3) == j.DTSTART + assert j.start == date(2024, 1, 3) + assert j.end == date(2024, 1, 3) + + +@pytest.mark.parametrize( + ("file", "trigger", "related"), + [ + ( + "rfc_5545_absolute_alarm_example", + vDatetime.from_ical("19970317T133000Z"), + "START", + ), + ("rfc_5545_end", timedelta(days=-2), "END"), + ("start_date", timedelta(days=-2), "START"), + ], +) +def test_get_alarm_trigger_property(alarms, file, trigger, related): + """Get the trigger property.""" + alarm = alarms[file] + assert alarm.TRIGGER == trigger + assert alarm.TRIGGER_RELATED == related + + +def test_set_alarm_trigger(): + """Set the alarm trigger.""" + a = Alarm() + a.TRIGGER = timedelta(hours=1) + assert a.TRIGGER == timedelta(hours=1) + assert a.TRIGGER_RELATED == "START" + + +def test_set_alarm_trigger_related(): + """Set the alarm trigger.""" + a = Alarm() + a.TRIGGER = timedelta(hours=1) + a.TRIGGER_RELATED = "END" + assert a.TRIGGER == timedelta(hours=1) + assert a.TRIGGER_RELATED == "END" + + +def test_get_related_without_trigger(): + """The default is start""" + assert Alarm().TRIGGER_RELATED == "START" + + +def test_cannot_set_related_without_trigger(): + """TRIGGER must be set to set the parameter.""" + with pytest.raises(ValueError) as e: + a = Alarm() + a.TRIGGER_RELATED = "END" + assert ( + e.value.args[0] + == "You must set a TRIGGER before setting the RELATED parameter." + ) + + +@pytest.mark.parametrize( + ("file", "triggers"), + [ + ( + "rfc_5545_absolute_alarm_example", + ( + (), + (), + ( + vDatetime.from_ical("19970317T133000Z"), + vDatetime.from_ical("19970317T134500Z"), + vDatetime.from_ical("19970317T140000Z"), + vDatetime.from_ical("19970317T141500Z"), + vDatetime.from_ical("19970317T143000Z"), + ), + ), + ), + ("rfc_5545_end", ((), (timedelta(days=-2),), ())), + ("start_date", ((timedelta(days=-2),), (), ())), + ], +) +def test_get_alarm_triggers(alarms, file, triggers): + """Get the trigger property.""" + alarm = alarms[file] + print(tuple(alarm.triggers)) + print(triggers) + assert alarm.triggers == triggers + + +def test_triggers_emtpy_alarm(): + """An alarm with no trigger has no triggers.""" + assert Alarm().triggers == ((), (), ()) + + +h1 = timedelta(hours=1) + + +def test_triggers_emtpy_with_no_repeat(): + """Check incomplete values.""" + a = Alarm() + a.TRIGGER = h1 + a.DURATION = h1 + assert a.triggers == ((h1,), (), ()) + + +def test_triggers_emtpy_with_no_duration(): + """Check incomplete values.""" + a = Alarm() + a.TRIGGER = h1 + a.REPEAT = 10 + assert a.triggers == ((h1,), (), ()) + + +@pytest.mark.parametrize( + ("file", "triggers"), + [ + ( + "rfc_5545_absolute_alarm_example", + ((), (), (vDatetime.from_ical("19970317T133000Z"),)), + ), + ("rfc_5545_end", ((), (timedelta(days=-2),), ())), + ("start_date", ((timedelta(days=-2),), (), ())), + ], +) +@pytest.mark.parametrize("duration", [timedelta(days=-1), h1]) +@pytest.mark.parametrize("repeat", [1, 3]) +def test_get_alarm_triggers_repeated(alarms, file, triggers, duration, repeat): + """Get the trigger property.""" + alarm = alarms[file].copy() + alarm.REPEAT = repeat + alarm.DURATION = duration + for expected, triggers in zip(triggers, alarm.triggers): + if not expected: + assert triggers == () + continue + assert len(triggers) == 1 + repeat + assert triggers[0] == expected[0] + for x, y in zip(triggers[:-1], triggers[1:]): + assert y - x == duration diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_716_alarm_time_computation.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_716_alarm_time_computation.py new file mode 100644 index 0000000..7b2cdaf --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_716_alarm_time_computation.py @@ -0,0 +1,438 @@ +"""Test the alarm time computation. + +Events can have alarms. +Alarms can be in this state: + +- active - the user wants the alarm to pop up +- acknowledged - the user no longer wants the alarm +- snoozed - the user moved that alarm to another time + +The alarms can only work on the properties of the event like +DTSTART, DTEND, and DURATION. + +""" + +from datetime import date, datetime, timedelta, timezone + +import pytest + +from icalendar import Event +from icalendar.alarms import Alarms +from icalendar.cal import Alarm +from icalendar.error import IncompleteAlarmInformation +from icalendar.prop import vDatetime +from icalendar.tools import normalize_pytz + +UTC = timezone.utc +EXAMPLE_TRIGGER = datetime(1997, 3, 17, 13, 30, tzinfo=UTC) + + +def test_absolute_alarm_time_rfc_example(alarms): + """Check that the absolute alarm is recognized. + + The following example is for a "VALARM" calendar component + that specifies an audio alarm that will sound at a precise time + and repeat 4 more times at 15-minute intervals: + """ + a = Alarms(alarms.rfc_5545_absolute_alarm_example) + times = a.times + assert len(times) == 5 + for i, t in enumerate(times): + assert t.trigger == EXAMPLE_TRIGGER + timedelta(minutes=15 * i) + + +alarm_1 = Alarm() +alarm_1.add("TRIGGER", EXAMPLE_TRIGGER) +alarm_2 = Alarm() +alarm_2["TRIGGER"] = vDatetime(EXAMPLE_TRIGGER) + + +@pytest.mark.parametrize("alarm", [alarm_1, alarm_2]) +def test_absolute_alarm_time_with_vDatetime(alarm): + """Check that the absolute alarm is recognized. + + The following example is for a "VALARM" calendar component + that specifies an audio alarm that will sound at a precise time + and repeat 4 more times at 15-minute intervals: + """ + a = Alarms(alarm) + times = a.times + assert len(times) == 1 + assert times[0].trigger == EXAMPLE_TRIGGER + + +alarm_incomplete_1 = Alarm() +alarm_incomplete_1.TRIGGER = timedelta(hours=2) +alarm_incomplete_1.DURATION = timedelta(hours=1) +alarm_incomplete_2 = Alarm() +alarm_incomplete_2.TRIGGER = timedelta(hours=2) +alarm_incomplete_2.REPEAT = 100 + + +@pytest.mark.parametrize("alarm", [alarm_incomplete_1, alarm_incomplete_2]) +def test_alarm_has_only_one_of_repeat_or_duration(alarm): + """This is an edge case and we should ignore the repetition.""" + a = Alarms(alarm) + a.set_start(datetime(2027, 12, 2)) + assert len(a.times) == 1 + + +@pytest.fixture(params=[(0, timedelta(minutes=-30)), (1, timedelta(minutes=-25))]) +def alarm_before_start(calendars, request): + """An example alarm relative to the start of a component.""" + index, td = request.param + alarm = calendars.alarm_etar_future.subcomponents[-1].subcomponents[index] + assert isinstance(alarm, Alarm) + assert alarm.get("TRIGGER").dt == td + alarm.test_td = td + return alarm + + +def test_cannot_compute_relative_alarm_without_start(alarm_before_start): + """We have an alarm without a start of a component.""" + with pytest.raises(IncompleteAlarmInformation) as e: + Alarms(alarm_before_start).times # noqa: B018 + assert ( + e.value.args[0] + == f"Use {Alarms.__name__}.{Alarms.set_start.__name__} because at least one alarm is relative to the start of a component." + ) + + +@pytest.mark.parametrize( + ("dtstart", "timezone", "trigger"), + [ + ( + datetime(2024, 10, 29, 13, 10), + "UTC", + datetime(2024, 10, 29, 13, 10, tzinfo=UTC), + ), + (date(2024, 11, 16), None, datetime(2024, 11, 16, 0, 0)), + ( + datetime(2024, 10, 29, 13, 10), + "Asia/Singapore", + datetime(2024, 10, 29, 5, 10, tzinfo=UTC), + ), + (datetime(2024, 10, 29, 13, 20), None, datetime(2024, 10, 29, 13, 20)), + ], +) +def test_can_complete_relative_calculation_if_a_start_is_given( + alarm_before_start, dtstart, timezone, trigger, tzp +): + """The start is given and required.""" + start = dtstart if timezone is None else tzp.localize(dtstart, timezone) + alarms = Alarms(alarm_before_start) + alarms.set_start(start) + assert len(alarms.times) == 1 + time = alarms.times[0] + expected_trigger = normalize_pytz(trigger + alarm_before_start.test_td) + assert time.trigger == expected_trigger + + +@pytest.mark.parametrize("dtstart", [date(1998, 10, 1), date(2023, 12, 31)]) +def test_start_as_date_with_delta_as_date_stays_date(alarms, dtstart): + """If we have an alarm with a day delta and the event is a day event, we should stay as a date.""" + a = Alarms(alarms.start_date) + a.set_start(dtstart) + assert len(a.times) == 1 + assert a.times[0].trigger == dtstart - timedelta(days=2) + + +def test_cannot_compute_relative_alarm_without_end(alarms): + """We have an alarm without an end of a component.""" + with pytest.raises(IncompleteAlarmInformation) as e: + Alarms(alarms.rfc_5545_end).times # noqa: B018 + assert ( + e.value.args[0] + == f"Use {Alarms.__name__}.{Alarms.set_end.__name__} because at least one alarm is relative to the end of a component." + ) + + +@pytest.mark.parametrize( + ("dtend", "timezone", "trigger"), + [ + ( + datetime(2024, 10, 29, 13, 10), + "UTC", + datetime(2024, 10, 29, 13, 10, tzinfo=UTC), + ), + (date(2024, 11, 16), None, date(2024, 11, 16)), + ( + datetime(2024, 10, 29, 13, 10), + "Asia/Singapore", + datetime(2024, 10, 29, 5, 10, tzinfo=UTC), + ), + (datetime(2024, 10, 29, 13, 20), None, datetime(2024, 10, 29, 13, 20)), + ], +) +def test_can_complete_relative_calculation(alarms, dtend, timezone, trigger, tzp): + """The start is given and required.""" + start = dtend if timezone is None else tzp.localize(dtend, timezone) + alarms = Alarms(alarms.rfc_5545_end) + alarms.set_end(start) + assert len(alarms.times) == 1 + time = alarms.times[0] + expected_trigger = normalize_pytz(trigger - timedelta(days=2)) + assert time.trigger == expected_trigger + + +@pytest.mark.parametrize("dtend", [date(1998, 10, 1), date(2023, 12, 31)]) +def test_end_as_date_with_delta_as_date_stays_date(alarms, dtend): + """If we have an alarm with a day delta and the event is a day event, we should stay as a date.""" + a = Alarms(alarms.rfc_5545_end) + a.set_end(dtend) + assert len(a.times) == 1 + assert a.times[0].trigger == dtend - timedelta(days=2) + + +def test_add_multiple_alarms(alarms): + """We can add multiple alarms.""" + a = Alarms() + a.add_alarm(alarms.start_date) + a.add_alarm(alarms.rfc_5545_end) + a.add_alarm(alarms.rfc_5545_absolute_alarm_example) + with pytest.raises(IncompleteAlarmInformation): + a.times # noqa: B018 + a.set_start(datetime(2012, 3, 5)) + with pytest.raises(IncompleteAlarmInformation): + a.times # noqa: B018 + a.set_end(datetime(2012, 3, 5)) + assert len(a.times) == 7 + + +def test_alarms_from_event_have_right_times(calendars): + """We can collect from an event.""" + event = calendars.alarm_etar_future.subcomponents[-1] + a = Alarms(event) + assert len(a.times) == 3 + assert a.times[0].parent == event + + +def test_cannot_set_the_event_twice(calendars): + """We cannot set an event twice. This make the state ambiguous.""" + event = calendars.alarm_etar_future.subcomponents[-1] + a = Alarms() + a.add_component(event) + a.add_component(event) # same component is ok + with pytest.raises(ValueError): + a.add_component(calendars.alarm_google_future.subcomponents[-1]) + + +@pytest.mark.parametrize( + ("calendar", "index", "count", "message"), + [ + ("alarm_etar_future", -1, 3, "Etar (1): we just created the alarm"), + ("alarm_etar_notification", -1, 2, "Etar (2): the notification popped up"), + ( + "alarm_etar_notification_clicked", + -1, + 0, + "Etar (3): the notification was dismissed", + ), # TODO: check that that is really true + ( + "alarm_google_future", + -1, + 4, + "Google (1): we just created the event with alarms", + ), + ( + "alarm_google_acknowledged", + -1, + 2, + "Google (2): 2 alarms happened at the same time", + ), + ("alarm_thunderbird_future", -1, 2, "Thunderbird (1.1): 2 alarms are set"), + ( + "alarm_thunderbird_snoozed_until_1457", + -1, + 2, + "Thunderbird (1.2): 2 alarms are snoozed to another time", + ), + ( + "alarm_thunderbird_closed", + -1, + 0, + "Thunderbird (1.3): all alarms are dismissed (closed)", + ), + ("alarm_thunderbird_2_future", -1, 2, "Thunderbird (2.1): 2 alarms active"), + ( + "alarm_thunderbird_2_notification_popped_up", + -1, + 2, + "Thunderbird (2.2): one alarm popped up as a notification", + ), + ( + "alarm_thunderbird_2_notification_5_min_postponed", + -1, + 2, + "Thunderbird (2.3): 1 alarm active and one postponed by 5 minutes", + ), + ( + "alarm_thunderbird_2_notification_5_min_postponed_and_popped_up", + -1, + 2, + "Thunderbird (2.4): 1 alarm active and one postponed by 5 minutes and now popped up", + ), + ( + "alarm_thunderbird_2_notification_5_min_postponed_and_closed", + -1, + 1, + "Thunderbird (2.5): 1 alarm active and one postponed by 5 minutes and is now acknowledged", + ), + ], +) +def test_number_of_active_alarms_from_calendar_software( + calendars, calendar, index, count, message +): + """Check that we extract calculate the correct amount of active alarms.""" + event = calendars[calendar].subcomponents[index] + a = Alarms(event) + active_alarms = ( + a.active + ) # We do not need to pass a timezone because the events have a timezone + assert ( + len(active_alarms) == count + ), f"{message} - I expect {count} alarms active but got {len(active_alarms)}." + + +three_alarms = Alarm() +three_alarms.REPEAT = 2 +three_alarms.add("DURATION", timedelta(hours=1)) # 2 hours & 1 hour before +three_alarms.add("TRIGGER", -timedelta(hours=3)) # 3 hours before + + +@pytest.mark.parametrize( + ("start", "acknowledged", "timezone", "count"), + [ + (datetime(2024, 10, 10), datetime(2024, 10, 9), "UTC", 3), + (datetime(2024, 10, 10, 12), datetime(2024, 10, 10, 9, 1), "UTC", 2), + (datetime(2024, 10, 10, 12), datetime(2024, 10, 10, 10, 1), "UTC", 1), + (datetime(2024, 10, 10, 12), datetime(2024, 10, 10, 11, 1), "UTC", 0), + ( + datetime(2024, 10, 10, 12, tzinfo=timezone.utc), + datetime(2024, 10, 10, 11, 1), + None, + 0, + ), + ], +) +def test_number_of_active_alarms_with_moving_time( + start, acknowledged, count, tzp, timezone +): + """Check how many alarms are active after a time they are acknowledged.""" + a = Alarms() + a.add_alarm(three_alarms) + a.set_start(start) + a.set_local_timezone(timezone) + a.acknowledge_until(tzp.localize_utc(acknowledged)) + active = a.active + assert len(active) == count + + +def test_incomplete_alarm_information_for_active_state(tzp): + """Make sure we throw the right error.""" + a = Alarms() + a.add_alarm(three_alarms) + a.set_start(date(2017, 12, 1)) + a.acknowledge_until(tzp.localize_utc(datetime(2012, 10, 10, 12))) + with pytest.raises(IncompleteAlarmInformation) as e: + a.active # noqa: B018 + assert ( + e.value.args[0] + == f"A local timezone is required to check if the alarm is still active. Use Alarms.{Alarms.set_local_timezone.__name__}()." + ) + + +@pytest.mark.parametrize( + "calendar_name", + [ + "alarm_etar_future", + "alarm_google_acknowledged", + "alarm_thunderbird_closed", + "alarm_thunderbird_future", + "alarm_thunderbird_snoozed_until_1457", + ], +) +def test_thunderbird_recognition(calendars, calendar_name): + """Check if we correctly discover Thunderbird's alarm algorithm.""" + calendar = calendars[calendar_name] + event = calendar.subcomponents[-1] + assert isinstance(event, Event) + assert event.is_thunderbird() == ("thunderbird" in calendar_name) + + +@pytest.mark.parametrize( + "snooze", + [ + datetime(2012, 10, 10, 11, 1), # before everything + datetime(2017, 12, 1, 10, 1), + datetime(2017, 12, 1, 11, 1), + datetime(2017, 12, 1, 12, 1), + datetime(2017, 12, 1, 13, 1), # snooze until after the start of the event + ], +) +def test_snoozed_alarm_has_trigger_at_snooze_time(tzp, snooze): + """When an alarm is snoozed, it pops up after the snooze time.""" + a = Alarms() + a.add_alarm(three_alarms) + a.set_start(datetime(2017, 12, 1, 13)) + a.set_local_timezone("UTC") + snooze_utc = tzp.localize_utc(snooze) + a.snooze_until(snooze_utc) + active = a.active + assert len(active) == 3 + for alarm in active: + assert alarm.trigger >= snooze_utc + + +@pytest.mark.parametrize( + ("event_index", "alarm_times"), + [ + # Assume that we have the following event with an alarm set to trigger 15 minutes before the meeting: + (1, ("20210302T101500",)), + # When the alarm is triggered, the user decides to "snooze" it for 5 minutes. + # The client acknowledges the original alarm and creates a new "snooze" + # alarm as a sibling of, and relates it to, the original alarm (note that + # both occurrences of "VALARM" reside within the same "parent" VEVENT): + (2, ("20210302T102000",)), + # When the "snooze" alarm is triggered, the user decides to "snooze" it + # again for an additional 5 minutes. The client once again acknowledges + # the original alarm, removes the triggered "snooze" alarm, and creates another + # new "snooze" alarm as a sibling of, and relates it to, the original alarm + # (note the different UID for the new "snooze" alarm): + (3, ("20210302T102500",)), + # When the second "snooze" alarm is triggered, the user decides to dismiss it. + # The client acknowledges both the original alarm and the second "snooze" alarm: + (4, ()), + ], +) +def test_rfc_9074_alarm_times(events, event_index, alarm_times): + """Test the examples from the RFC and their timing. + + Add times use America/New_York as timezone. + """ + a = events[f"rfc_9074_example_{event_index}"].alarms + assert len(a.active) == len(alarm_times) + expected_alarm_times = { + vDatetime.from_ical(t, "America/New_York") for t in alarm_times + } + computed_alarm_times = {alarm.trigger for alarm in a.active} + assert expected_alarm_times == computed_alarm_times + + +def test_set_to_None(): + """acknowledge_until, snooze_until, set_local_timezone.""" + a = Alarms() + a.set_start(None) + a.set_end(None) + a.set_local_timezone(None) + a.acknowledge_until(None) + a.snooze_until(None) + assert vars(a) == vars(Alarms()) + + +def test_delete_TRIGGER(): + """Delete the TRIGGER property.""" + a = Alarm() + a.TRIGGER = datetime(2017, 12, 1, 10, 1) + del a.TRIGGER + assert a.TRIGGER is None diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_720_uid_property.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_720_uid_property.py new file mode 100644 index 0000000..898f157 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_720_uid_property.py @@ -0,0 +1,12 @@ +"""This tests the UID property. + +See https://github.com/collective/icalendar/issues/740 +""" + +from icalendar import Alarm + + +def test_alarm_uses_x_property_too(): + alarm = Alarm() + alarm["X-ALARMUID"] = "1234" + assert alarm.uid == "1234" diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_722_generate_vtimezone.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_722_generate_vtimezone.py new file mode 100644 index 0000000..4ed37da --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_722_generate_vtimezone.py @@ -0,0 +1,437 @@ +"""Generate VTIMEZONE components from actual timezone information. + +When we generate VTIMEZONE from actual tzinfo instances of +- dateutil +- zoneinfo +- pytz + +Then, we cannot assume that the future information stays the same but +we should be able to create tests that work for the past. +""" + +from datetime import date, datetime, timedelta +from re import findall + +import pytest +from dateutil.tz import gettz + +try: + from zoneinfo import available_timezones +except ImportError: + from backports.zoneinfo import available_timezones + +from icalendar import Calendar, Component, Event, Timezone +from icalendar.timezone import tzid_from_tzinfo, tzids_from_tzinfo + +tzids = pytest.mark.parametrize( + "tzid", + [ + "Europe/Berlin", + "Asia/Singapore", + "America/New_York", + ], +) + + +def assert_components_equal(c1: Component, c2: Component): + """Print the diff of two components.""" + ML = 32 + ll1 = c1.to_ical().decode().splitlines() + ll2 = c2.to_ical().decode().splitlines() + pad = max(len(l) for l in ll1 if len(l) <= ML) + diff = 0 + for l1, l2 in zip(ll1, ll2): + a = len(l1) > 32 or len(l2) > 32 + print( + a * " " + l1, + " " * (pad - len(l1)), + a * "\n->" + l2, + " " * (pad - len(l2)), + "\tdiff!" if l1 != l2 else "", + ) + diff += l1 != l2 + assert not diff, f"{diff} lines differ" + + +@tzids +def test_conversion_converges(tzp, tzid): + """tzinfo -> VTIMEZONE -> tzinfo -> VTIMEZONE + + We can assume that both generated VTIMEZONEs are equivalent. + """ + if tzp.uses_pytz(): + pytest.skip( + "pytz will not converge on the first run. This is problematic. PYTZ-TODO" + ) + tzinfo1 = tzp.timezone(tzid) + assert tzinfo1 is not None + generated1 = Timezone.from_tzinfo(tzinfo1) + generated1["TZID"] = ( + "test-generated" # change the TZID so we do not use an existing one + ) + tzinfo2 = generated1.to_tz() + generated2 = Timezone.from_tzinfo(tzinfo2, "test-generated") + tzinfo3 = generated2.to_tz() + generated3 = Timezone.from_tzinfo(tzinfo3, "test-generated") + # pprint(generated1.get_transitions()) + # pprint(generated2.get_transitions()) + assert_components_equal(generated1, generated2) + assert_components_equal(generated2, generated3) + assert 2 <= len(generated1.standard + generated1.daylight) <= 3 + assert 2 <= len(generated2.standard + generated2.daylight) <= 3 + assert dict(generated1) == dict(generated2) + assert generated1.to_ical().decode() == generated2.to_ical().decode() + assert generated1.daylight == generated2.daylight + assert generated1.standard == generated2.standard + assert generated1 == generated2 + + +@tzids +def both_tzps_generate_the_same_info(tzid, tzp): + """We want to make sure that we get the same info for all timezone implementations. + + We assume that + - the timezone implementations have the same info within the days we test + - the timezone transitions times do not change because they are before last_date + """ + # default generation + tz1 = Timezone.from_tzid(tzid, tzp, last_date=date(2024, 1, 1)) + tzp.use_zoneinfo() # we compare to zoneinfo + tz2 = Timezone.from_tzid(tzid, tzp, last_date=date(2024, 1, 1)) + assert_components_equal(tz1, tz2) + assert tz1 == tz2 + + +@tzids +def test_tzid_matches(tzid, tzp): + """Check the TZID.""" + tz = Timezone.from_tzinfo(tzp.timezone(tzid)) + assert tz["TZID"] == tzid + + +def test_do_not_convert_utc(tzp): + """We do not need to convert UTC but it should work.""" + utc = Timezone.from_tzid("UTC") + assert utc.daylight == [] + assert len(utc.standard) == 1 + standard = utc.standard[0] + assert standard["TZOFFSETFROM"].td == timedelta(0) + assert standard["TZOFFSETTO"].td == timedelta(0) + assert standard["TZNAME"] == "UTC" + + +def test_berlin_time(tzp): + """Test the Europe/Berlin timezone conversion.""" + tz = Timezone.from_tzid("Europe/Berlin") + # we should have two timezones in it + for x in tz.standard: + print(x.name, x["TZNAME"], x["TZOFFSETFROM"].td, x["TZOFFSETTO"].td) + print(x.to_ical().decode()) + assert len(tz.daylight) == 1 + assert len(tz.standard) in (1, 2), "We start in winter" + dst = tz.daylight[-1] + sta = tz.standard[-1] + assert dst["TZNAME"] == "CEST" # summer + assert sta["TZNAME"] == "CET" + assert dst["TZOFFSETFROM"].td == timedelta(hours=1) # summer + assert sta["TZOFFSETFROM"].td == timedelta(hours=2) + assert dst["TZOFFSETTO"].td == timedelta(hours=2) # summer + assert sta["TZOFFSETTO"].td == timedelta(hours=1) + + +def test_range_is_not_crossed(): + first_date = datetime(2023, 1, 1) + last_date = datetime(2024, 1, 1) + + def check(dt): + assert first_date <= dt <= last_date + + tz = Timezone.from_tzid("Europe/Berlin", last_date=last_date, first_date=first_date) + for sub in tz.standard + tz.daylight: + check(sub.DTSTART) + for rdate in sub.get("RDATE", []): + check(rdate) + + +@tzids +def test_use_the_original_timezone(tzid, tzp): + """When we get the timezone again, usually, we should use the + one of the library/tzp.""" + tzinfo1 = tzp.timezone(tzid) + assert tzinfo1 is not None + generated1 = Timezone.from_tzinfo(tzinfo1) + tzinfo2 = generated1.to_tz() + assert type(tzinfo1) == type(tzinfo2) + assert tzinfo1 == tzinfo2 + + +@pytest.mark.parametrize( + ("tzid", "dt", "tzname"), + [ + ("Asia/Singapore", datetime(1970, 1, 1), "+0730"), + ("Asia/Singapore", datetime(1981, 12, 31), "+0730"), + ("Asia/Singapore", datetime(1981, 12, 31, 23, 10), "+0730"), + ("Asia/Singapore", datetime(1981, 12, 31, 23, 34), "+0730"), + ("Asia/Singapore", datetime(1981, 12, 31, 23, 59, 59), "+0730"), + ("Asia/Singapore", datetime(1982, 1, 1), "+08"), + ("Asia/Singapore", datetime(1982, 1, 1, 0, 1), "+08"), + ("Asia/Singapore", datetime(1982, 1, 1, 0, 34), "+08"), + ("Asia/Singapore", datetime(1982, 1, 1, 1, 0), "+08"), + ("Asia/Singapore", datetime(1982, 1, 1, 1, 1), "+08"), + ("Europe/Berlin", datetime(1970, 1, 1), "CET"), + ("Europe/Berlin", datetime(2024, 3, 31, 0, 0), "CET"), + ("Europe/Berlin", datetime(2024, 3, 31, 1, 0), "CET"), + ("Europe/Berlin", datetime(2024, 3, 31, 2, 0), "CET"), + ("Europe/Berlin", datetime(2024, 3, 31, 2, 59, 59), "CET"), + ("Europe/Berlin", datetime(2024, 3, 31, 3, 0), "CEST"), + ("Europe/Berlin", datetime(2024, 3, 31, 3, 0, 1), "CEST"), + ("Europe/Berlin", datetime(2024, 3, 31, 4, 0), "CEST"), + ("Europe/Berlin", datetime(2024, 10, 27, 0, 0), "CEST"), + ("Europe/Berlin", datetime(2024, 10, 27, 1, 0), "CEST"), + ("Europe/Berlin", datetime(2024, 10, 27, 2, 0), "CEST"), + ("Europe/Berlin", datetime(2024, 10, 27, 2, 30), "CEST"), + ("Europe/Berlin", datetime(2024, 10, 27, 2, 59, 59), "CEST"), + ("Europe/Berlin", datetime(2024, 10, 27, 3, 0), "CET"), + ("Europe/Berlin", datetime(2024, 10, 27, 3, 0, 1), "CET"), + ("Europe/Berlin", datetime(2024, 10, 27, 4, 0), "CET"), + # transition times from https://www.zeitverschiebung.net/de/timezone/america--new_york + ("America/New_York", datetime(1970, 1, 1), "EST"), + # Daylight Saving Time + ("America/New_York", datetime(2024, 11, 3, 0, 0), "EDT"), + ("America/New_York", datetime(2024, 11, 3, 1, 0), "EDT"), + ("America/New_York", datetime(2024, 11, 3, 1, 59, 59), "EDT"), + # 03.11.2024 2:00am -> 1:00am Standard + # ("America/New_York", datetime(2024, 11, 3, 2, 0), "EDT"), + ("America/New_York", datetime(2024, 11, 3, 2, 0, 1), "EST"), + ("America/New_York", datetime(2024, 11, 3, 3, 0), "EST"), + ("America/New_York", datetime(2025, 3, 9, 1, 0), "EST"), + ("America/New_York", datetime(2025, 3, 9, 1, 59, 59), "EST"), + ("America/New_York", datetime(2025, 3, 9, 2, 0), "EST"), + # 09.03.2025 2:00am -> 3:00am Daylight Saving Time + ("America/New_York", datetime(2025, 3, 9, 3, 0), "EDT"), + ("America/New_York", datetime(2025, 3, 9, 3, 1, 1), "EDT"), + ("America/New_York", datetime(2025, 3, 9, 4, 0), "EDT"), + ], +) +def test_check_datetimes_around_transition_times(tzp, tzid, dt, tzname): + """We should make sure than the datetimes with the generated timezones + work as expected: They have the right UTC offset, dst and tzname. + """ + message = f"{tzid}: {dt} (expected in {tzname})" + expected_dt = tzp.localize(dt, tzid) + component = Timezone.from_tzinfo(tzp.timezone(tzid)) + generated_tzinfo = component.to_tz(tzp, lookup_tzid=False) + generated_dt = dt.replace(tzinfo=generated_tzinfo) + print(generated_tzinfo) + if tzp.uses_pytz(): + # generated_dt = generated_tzinfo.localize(dt) + generated_dt = generated_tzinfo.normalize(generated_dt) + if dt in ( + datetime(2024, 10, 27, 1, 0), + datetime(2024, 11, 3, 1, 59, 59), + datetime(2024, 11, 3, 1, 0), + datetime(2024, 11, 3, 0, 0), + datetime(2024, 10, 27, 2, 59, 59), + datetime(2024, 10, 27, 2, 30), + datetime(2024, 10, 27, 2, 0), + datetime(2024, 10, 27, 1, 0), + ): + pytest.skip("We do not know how to do this. PYTZ-TODO") + assert generated_dt.tzname() == expected_dt.tzname() == tzname, message + assert generated_dt.dst() == expected_dt.dst(), message + assert generated_dt.utcoffset() == expected_dt.utcoffset(), message + + +@pytest.mark.parametrize("uid", [0, 1, 2, 3]) +def test_dateutil_timezone_when_time_is_going_backwards(calendars, tzp, uid): + """When going from Daylight Saving Time to Standard Time, times can be ambiguous. + For example, at 3:00 AM, the time falls back to 2:00 AM, repeating a full hour of times from 2:00 AM to 3:00 AM on the same date. + + By the RFC 5545, we cannot accommodate this case. All datetimes should + be BEFORE the transition if ambiguous. However, dateutil can + create a timezone that allows the event to be after this ambiguous time span, of course. + + Each event has its timezone saved in it. + """ + cal: Calendar = calendars.issue_722_timezone_transition_ambiguity + event: Event = cal.events[uid] + expected_tzname = str(event["X-TZNAME"]) + actual_tzname = event.start.tzname() + assert actual_tzname == expected_tzname, event["SUMMARY"] + + +def query_tzid(query: str, cal: Calendar) -> str: + """The tzid from the query.""" + try: + tzinfo = eval(query, {"cal": cal}) # noqa: S307 + except Exception as e: + raise ValueError(query) from e + return tzid_from_tzinfo(tzinfo) + + +# these are queries for all the places that have a TZID +# according to RFC 5545 +queries = [ + "cal.events[0].start.tzinfo", # DTSTART + "cal.events[0].end.tzinfo", # DTEND + # EXDATE + "cal.todos[0].end.tzinfo", # DUE + "cal.events[0].get('RDATE').dts[0].dt[0].tzinfo", # RDATE + "cal.events[1].get('RECURRENCE-ID').dt.tzinfo", # RECURRENCE-ID + "cal.events[2].get('RDATE')[0].dts[0].dt.tzinfo", # RDATE multiple + "cal.events[2].get('RDATE')[1].dts[0].dt.tzinfo", # RDATE multiple +] + + +@pytest.mark.parametrize("query", queries) +def test_add_missing_timezones_to_example(calendars, query): + """Add the missing timezones to the calendar.""" + cal = calendars.issue_722_missing_timezones + tzid = query_tzid(query, cal) + tzs = cal.get_missing_tzids() + assert tzid in tzs + + +def test_queries_yield_unique_tzids(calendars): + """We make sure each query tests a unique place to find for the algorithm.""" + cal = calendars.issue_722_missing_timezones + tzids = set() + for query in queries: + tzid = query_tzid(query, cal) + print(query, "->", tzid) + tzids.add(tzid) + assert len(tzids) == len(queries) + + +def test_we_do_not_miss_to_add_a_query(calendars): + """Make sure all tzids are actually queried.""" + cal = calendars.issue_722_missing_timezones + raw = cal.raw_ics.decode() + ids = set(findall("TZID=([a-zA-Z_/+-]+)", raw)) + assert cal.get_used_tzids() == ids, "We find all tzids and they are unique." + assert len(ids) == len(queries), "We query all the tzids." + + +def test_unknown_tzid(calendars): + """If we have an unknown tzid with no timezone component.""" + cal = calendars.issue_722_missing_VTIMEZONE_custom + assert "CUSTOM_tzid" in cal.get_used_tzids() + assert "CUSTOM_tzid" in cal.get_missing_tzids() + + +def test_custom_timezone_is_found_and_used(calendars): + """Check the custom timezone component is not missing.""" + cal = calendars.america_new_york + assert "custom_America/New_York" in cal.get_used_tzids() + assert "custom_America/New_York" not in cal.get_missing_tzids() + + +def test_not_missing_anything(): + """Check that no timezone is missing.""" + cal = Calendar() + assert cal.get_missing_tzids() == set() + + +def test_utc_is_not_missing(calendars): + """UTC should not be found missing.""" + cal = calendars.issue_722_missing_timezones + assert "UTC" not in cal.get_missing_tzids() + assert "UTC" not in cal.get_used_tzids() + + +def test_dateutil_timezone_is_not_found_with_tzname(calendars, no_pytz): + """dateutil is an example of a timezone that has no tzid. + + In this test we make sure that the timezone is said to be missing. + """ + cal: Calendar = calendars.america_new_york + cal.subcomponents.remove(cal.timezones[0]) + assert cal.get_missing_tzids() == {"custom_America/New_York"} + assert "dateutil" in repr(cal.events[0].start.tzinfo.__class__) + + +@pytest.mark.parametrize("tzname", ["America/New_York", "Arctic/Longyearbyen"]) +# @pytest.mark.parametrize("component", ["STANDARD", "DAYLIGHT"]) +def test_dateutil_timezone_is_matched_with_tzname(tzname): + """dateutil is an example of a timezone that has no tzid. + + In this test we make sure that the timezone is matched by its + tzname() in the timezone in the STANDARD and DAYLIGHT components. + """ + cal = Calendar() + event = Event() + event.start = datetime(2024, 11, 12, tzinfo=gettz(tzname)) + print(dir(event.start.tzinfo)) + cal.add_component(event) + assert cal.get_missing_tzids() == {tzname} + cal.add_missing_timezones() + assert cal.get_missing_tzids() == set() + + +def test_dateutil_timezone_is_also_added(calendars): + """We find and add a dateutil timezone. + + This is important as we use those in the zoneinfo implementation. + """ + + +@pytest.mark.parametrize( + "calendar", + [ + "example", + "america_new_york", # custom + "timezone_same_start", # known tzid + "period_with_timezone", # known tzid + ], +) +def test_timezone_is_not_missing(calendars, calendar): + """Check that these calendars have no timezone missing.""" + cal: Calendar = calendars[calendar] + timezones = cal.timezones[:] + assert set() == cal.get_missing_tzids() + cal.add_missing_timezones() + assert set() == cal.get_missing_tzids() + assert cal.timezones == timezones + + +def test_add_missing_known_timezones(calendars): + """Add all timezones specified.""" + cal: Calendar = calendars.issue_722_missing_timezones + assert len(cal.timezones) == 0 + cal.add_missing_timezones() + assert len(cal.timezones) == len(queries), "all timezones are known" + assert len(cal.get_missing_tzids()) == 0 + + +def test_cannot_add_unknown_timezone(calendars): + """I cannot add a timezone that is unknown.""" + cal: Calendar = calendars.issue_722_missing_VTIMEZONE_custom + assert len(cal.timezones) == 0 + assert cal.get_missing_tzids() == {"CUSTOM_tzid"} + cal.add_missing_timezones() + assert cal.timezones == [], "we cannot add this timezone" + assert cal.get_missing_tzids() == {"CUSTOM_tzid"} + + +def test_cannot_create_a_timezone_from_an_invalid_tzid(): + with pytest.raises(ValueError): + Timezone.from_tzid("invalid/tzid") + + +def test_dates_before_and_after_are_considered(): + """When we add the timezones, we should check the calendar to see + if all dates really occur in the span we use. + + We should also consider a huge default range. + """ + pytest.skip("todo") + + +@pytest.mark.parametrize("tzid", available_timezones() - {"Factory", "localtime"}) +def test_we_can_identify_dateutil_timezones(tzid): + """dateutil and others were badly supported. + + But if we know their shortcodes, we should be able to identify them. + """ + tz = gettz(tzid) + assert tz is None or tzid in tzids_from_tzinfo(tz) diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_798_property_parameters.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_798_property_parameters.py new file mode 100644 index 0000000..19b7700 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_798_property_parameters.py @@ -0,0 +1,199 @@ +"""Test the property parameters.""" + +from datetime import datetime + +import pytest + +from icalendar import ( + CUTYPE, + FBTYPE, + PARTSTAT, + RANGE, + RELTYPE, + vCalAddress, + vDatetime, + vPeriod, + vText, +) +from icalendar.parser import Parameters +from icalendar.timezone.tzp import TZP + + +class Prop: + + params: Parameters + + def __init__(self, **parameters): + """Create a new property.""" + self.params = Parameters(parameters) + + def to_ical(self) -> str: + """Parameters to bytes to string.""" + return self.params.to_ical().decode("utf-8") + + from icalendar.param import ( + ALTREP, + CN, + CUTYPE, + DELEGATED_FROM, + DELEGATED_TO, + RELTYPE, + ) + + +@pytest.fixture() +def p(): + """Empty test property.""" + return Prop() + + +def test_set_altrep(p): + p.ALTREP = "http://example.com" + assert p.params == {"ALTREP": "http://example.com"} + assert p.ALTREP == "http://example.com" + +def test_altrep_must_be_quoted(): + """altrepparam = "ALTREP" "=" DQUOTE uri DQUOTE""" + assert Prop(ALTREP="1234aA").to_ical() == 'ALTREP="1234aA"' + +def test_del_altrep(p): + """Del when empty""" + del p.ALTREP + assert p.params == {} + assert p.ALTREP is None + +def test_del_altrep_full(p): + """Del when empty""" + p.ALTREP = "http://example.com" + del p.ALTREP + assert p.params == {} + assert p.ALTREP is None + + +def test_get_cutype(p): + """The default is individual.""" + assert p.CUTYPE == "INDIVIDUAL" + + +def test_set_lowercase(): + p = Prop(CUTYPE="individual") + assert p.CUTYPE == "INDIVIDUAL" + p.CUTYPE = "unknown" + assert p.CUTYPE == CUTYPE.UNKNOWN + + +def test_set_delegation_to_string(p): + p.DELEGATED_FROM = "mailto:foo" + assert p.DELEGATED_FROM == ("mailto:foo",) + +def test_set_delegation_to_tuple(p): + p.DELEGATED_TO = ("mailto:foo","mailto:bar") + assert p.DELEGATED_TO == ("mailto:foo","mailto:bar") + +def test_delete_delegation_to(p): + p.DELEGATED_TO = ("mailto:foo","mailto:bar") + del p.DELEGATED_TO + assert p.DELEGATED_TO == () + +@pytest.mark.parametrize( + ("value", "expected"), + [ + ((), ""), + (("mailto:foo",), 'DELEGATED-TO="mailto:foo"'), + (("mailto:foo","mailto:bar"), 'DELEGATED-TO="mailto:foo","mailto:bar"'), + (('mailto:"asd"',), "DELEGATED-TO=\"mailto:^'asd^'\""), + ] +) +def test_serialize_delegation_to(p, value, expected): + p.DELEGATED_TO = value + assert p.to_ical() == expected + + +@pytest.mark.parametrize( + ("index", "expected"), + [ + (0, FBTYPE.BUSY_UNAVAILABLE), + (1, FBTYPE.BUSY), + (2, FBTYPE.FREE), + ] +) +def test_get_fbtype(calendars, index, expected): + fb = calendars.issue_798_freebusy.freebusy[index] + p : vPeriod = fb["FREEBUSY"] + if isinstance(p, list): + p = p[0] + assert expected == p.FBTYPE + + +@pytest.fixture() +def addr(): + return vCalAddress("mailto:foo") + +def test_partstat_get(addr: vCalAddress): + """test the default partstat""" + assert addr.PARTSTAT == "NEEDS-ACTION" + + +def test_set_the_partstat(addr: vCalAddress): + addr.PARTSTAT = PARTSTAT.ACCEPTED + assert addr.PARTSTAT == "ACCEPTED" + + +def test_this_and_future(): + assert vDatetime(datetime(2019, 12, 10)).RANGE is None + +def test_this_and_future_set(): + d = vDatetime(datetime(2019, 12, 10)) + d.RANGE = RANGE.THISANDFUTURE + assert d.params["RANGE"] == "THISANDFUTURE" + + +def test_rsvp_default(addr): + assert not addr.RSVP + +@pytest.mark.parametrize("rsvp", [True, False]) +def test_set_rsvp(addr: vCalAddress, rsvp): + addr.RSVP = rsvp + assert addr.RSVP == rsvp + assert addr.params["RSVP"] == ("TRUE" if rsvp else "FALSE") + + +def test_sent_by(addr: vCalAddress): + assert addr.SENT_BY is None + + + +def test_set_sent_by(addr: vCalAddress): + addr.SENT_BY = "mailto:asd" + assert addr.SENT_BY == "mailto:asd" + assert addr.params["SENT-BY"] == "mailto:asd" + assert addr.params.to_ical() == b'SENT-BY="mailto:asd"' + + +@pytest.mark.parametrize("tzid", [None, "Europe/Berlin"]) +def test_tzid(tzid, tzp:TZP): + dt = vDatetime(tzp.localize(datetime(2019, 12, 10), tzid)) + assert dt.TZID is None + + +@pytest.mark.parametrize( + ("index", "reltype"), + [ + (0, RELTYPE.PARENT), + (1, RELTYPE.SIBLING), + ] +) +def test_reltype_example(calendars, index, reltype): + """The reltype parameter in the examples.""" + event = calendars.issue_798_related_to.events[index] + print(event.to_ical().decode()) + r : vText = event["RELATED-TO"] + print(r) + print(r.params) + assert reltype == r.RELTYPE + + +def test_set_reltype(p): + p.RELTYPE = RELTYPE.CHILD + assert p.RELTYPE == RELTYPE.CHILD + assert p.params["RELTYPE"] == "CHILD" diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_802.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_802.py new file mode 100644 index 0000000..0a0ac87 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_802.py @@ -0,0 +1,54 @@ +"""Test the sequence and other int properies. + +https://www.rfc-editor.org/rfc/rfc5545#section-3.8.7.4 +https://github.com/collective/icalendar/issues/802 + +""" + +import pytest + +from icalendar import Component, Event, Journal, Todo + + +@pytest.fixture(params=[0, None]) +def default_sequence(request): + return request.param + +@pytest.fixture(params=[Event, Journal, Todo]) +def component(request, default_sequence) -> Component: + """Return a component.""" + component : Component = request.param() + if default_sequence is not None: + component["SEQUENCE"] = default_sequence + return component + + +def test_sequence_is_0(component: Component): + """Check the default value.""" + assert component.sequence == 0 + + +def test_increase_sequence(component: Component): + """Check the default value.""" + component.sequence += 1 + assert component.sequence == 1 + assert component["SEQUENCE"] == 1 + + +def test_set_sequence(component: Component): + """Check the default value.""" + component.sequence = 400 + assert component.sequence == 400 + assert component["SEQUENCE"] == 400 + + +def test_delete_sequence_default(component: Component): + """Delete the value.""" + del component.sequence + assert component.sequence == 0 + +def test_delete_sequence_with_value(component: Component): + """Delete the value.""" + component.sequence = 400 + del component.sequence + assert component.sequence == 0 diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_828.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_828.py new file mode 100644 index 0000000..5183a10 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_828.py @@ -0,0 +1,162 @@ +"""Events differ although their times are equal.""" + +import contextlib +from datetime import date, datetime, timedelta, timezone + +import pytest + +from icalendar import Event, Journal, vDate, vDatetime, vDDDLists, vDDDTypes + +try: + from zoneinfo import ZoneInfo +except ImportError: + from backports.zoneinfo import ZoneInfo # type: ignore PGH003 + + +def to_dt(a: date) -> date: + return a + + +def to_event(a): + e = Event() + e.start = a + e.end = a + timedelta(days=1) + e.add("RECURRENCE-ID", a - timedelta(days=1)) + return e + + +def to_journal(a): + """return a journal for testing""" + j = Journal() + j.start = a + j.end = a + timedelta(days=1) + j.add("RECURRENCE-ID", a - timedelta(days=1)) + return j + + +def to_vDD(a): + """Return a value type.""" + if isinstance(a, datetime): + return vDatetime(a) + return vDate(a) + + +def to_vDDDTypes(a): + return vDDDTypes(a) + + +def to_vDDDLists(a): + return vDDDLists([a]) + + +equal_dt_pairs = [ + (date(1998, 10, 1), date(1998, 10, 1)), + (datetime(2023, 12, 31), datetime(2023, 12, 31)), + ( + datetime(2023, 12, 31, tzinfo=timezone.utc), + datetime(2023, 12, 31, tzinfo=ZoneInfo("UTC")), + ), + ( + datetime(2023, 12, 31, tzinfo=ZoneInfo("UTC")), + datetime(2023, 12, 31, tzinfo=ZoneInfo("UTC")), + ), + ( + datetime(2025, 12, 31, tzinfo=ZoneInfo("UTC")), + datetime(2025, 12, 31, tzinfo=ZoneInfo("GMT+0")), + ), + ( + datetime(2025, 12, 31, 12, tzinfo=ZoneInfo("Europe/Zurich")), + datetime(2025, 12, 31, 11, tzinfo=ZoneInfo("UTC")), + ), +] + + +param_equal_dts = pytest.mark.parametrize(("d1", "d2"), equal_dt_pairs) + +param_transform = pytest.mark.parametrize( + "transform", [to_dt, to_event, to_journal, to_vDD, to_vDDDTypes, to_vDDDLists] +) + + +@param_equal_dts +@param_transform +def test_equality_of_equal_datetimes(d1, d2, transform): + """Check that the values are equal.""" + a = transform(d1) + b = transform(d2) + assert_equal(a, b) + + +def assert_equal(a, b): + """Check all equality tests.""" + assert a == b, f"1 equal: {a} == {b}" + assert b == a, f"2 equal reversed: {b} == {a}" + assert not a != b, f"3 not equal: {a} != {b}" # noqa: SIM202 + assert not b != a, f"4 not equal reversed: {b} != {a}" # noqa: SIM202 + assert bool(a) == bool(b), f"5 equal bool: {bool(a)} == {bool(b)}" + with contextlib.suppress(TypeError): + assert hash(a) == hash(b) + + +def assert_not_eq(a, b): + """Check all equality tests.""" + assert a != b, f"1 unequal: {a} == {b}" + assert b != a, f"2 unequal reversed: {b} == {a}" + assert not a == b, f"3 not unequal: {a} != {b}" # noqa: SIM201 + assert not b == a, f"4 not unequal reversed: {b} != {a}" # noqa: SIM201 + + +dts = [to_dt, to_vDD, to_vDDDTypes, to_vDDDLists] + + +@pytest.mark.parametrize("transform1", dts) +@pytest.mark.parametrize("transform2", dts) +@param_equal_dts +def test_property_times_for_date_and_datetime(d1, d2, transform1, transform2): + """Check the equality of the date and date implementations.""" + a = transform1(d1) + b = transform2(d2) + assert_equal(a, b) + + +def test_datetime_is_in_list_representation(): + dt = datetime(2023, 12, 31) + assert str(dt) in str(vDDDLists([dt])) + + +unequal_pairs = [(a, b) for a, b in equal_dt_pairs if a != b] + + +@pytest.mark.parametrize(("d1", "d2"), unequal_pairs) +@param_transform +def test_inequality(d1, d2, transform): + """The values are not equal.""" + a = transform(d1) + b = transform(d2) + assert_not_eq(a, b) + + +@pytest.mark.parametrize("ddd_type", [to_vDD, to_vDDDTypes]) +@param_equal_dts +def test_not_equal_if_parameters_differ(d1, d2, ddd_type): + """If the items are equal but the parameters differ, they should not be equal.""" + d1 = ddd_type(d1) + d2 = ddd_type(d2) + d1.params["foo"] = "bar" + assert_not_eq(d1, d2) + + +@pytest.mark.parametrize("ddd_type", [to_vDD, to_vDDDTypes]) +@param_equal_dts +def test_equal_ignoring_x_params(d1, d2, ddd_type): + """RFC 5545: Applications MUST ignore x-param and iana-param values they don't recognize.""" + d1 = ddd_type(d1) + d2 = ddd_type(d2) + d1.params["x-foo"] = "bar" + assert_equal(d1, d2) + + +def test_list_is_not_hashable(): + """We cannot hash a list because it changes.""" + with pytest.raises(TypeError): + hash(vDDDLists([datetime(2023, 12, 31)])) diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_836_do_not_quote_tzid.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_836_do_not_quote_tzid.py new file mode 100644 index 0000000..53f3c3a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_836_do_not_quote_tzid.py @@ -0,0 +1,80 @@ +"""Quoting the TZID parameter creates compatibility problems. + +See https://github.com/collective/icalendar/issues/836 + +:rfc:`5545`: + +.. code-block:: text + + param-value = paramtext / quoted-string + paramtext = *SAFE-CHAR + quoted-string = DQUOTE *QSAFE-CHAR DQUOTE + SAFE-CHAR = WSP / %x21 / %x23-2B / %x2D-39 / %x3C-7E + / NON-US-ASCII + ; Any character except CONTROL, DQUOTE, ";", ":", "," + NON-US-ASCII = UTF8-2 / UTF8-3 / UTF8-4 + ; UTF8-2, UTF8-3, and UTF8-4 are defined in [RFC3629] + +""" +from datetime import datetime, time +from icalendar import Event, Parameters, vDDDTypes, vDatetime, vPeriod, vTime +import pytest + +from icalendar.prop import vDDDLists + +# All the controls except HTAB +CONTROL = { + i for i in range(256) if 0x00 <= i <= 0x08 or 0x0A <= i <= 0x1F or i == 0x7F +} + +# Any character except CONTROL, DQUOTE, ";", ":", "," +SAFE_CHAR = set(range(256)) - CONTROL - set(b'";:,') + +param_tzid = pytest.mark.parametrize( + "tzid", + [ + "Europe/London", + "Eastern Standard Time", + ] +) + +@param_tzid +@pytest.mark.parametrize( + "vdt", + [ + vDatetime(datetime(2024, 10, 11, 12, 0)), + vTime(time(23, 59, 59)), + vDDDTypes(datetime(2024, 10, 11, 12, 0)), + vPeriod((datetime(2024, 10, 11, 12, 0), datetime(2024, 10, 11, 13, 0))), + vDDDLists([datetime(2024, 10, 11, 12, 0)]), + ] +) +def test_parameter_is_not_quoted_when_not_needed(tzid, vdt): + """Check that serializing the value works without quoting.""" + e = Event() + vdt.params["TZID"] = tzid + e["DTSTART"] = vdt + ics = e.to_ical().decode() + print(ics) + assert tzid in ics + assert f'"{tzid}' not in ics + assert f'{tzid}"' not in ics + + +@pytest.mark.parametrize( + "safe_char", + list(map(chr, sorted(SAFE_CHAR))) +) +def test_safe_char_is_not_escaped(safe_char): + """Check that paramerter serialization is without quotes for safe chars.""" + params = Parameters(tzid=safe_char) + result = params.to_ical().decode() + assert '"' not in result + + +def test_get_calendar_and_serialize_it_wihtout_quotes(calendars): + """The example calendar should not contain the timezone with quotes.""" + ics = calendars.issue_836_do_not_quote_tzid.to_ical().decode() + assert '"Eastern Standard' not in ics + assert 'Standard Time"' not in ics + assert "Eastern Standard Time" in ics diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_multiple.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_multiple.py new file mode 100644 index 0000000..44399bb --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_multiple.py @@ -0,0 +1,33 @@ +"""Testing multiple VCALENDAR components and multiple VEVENT components""" + +from icalendar.prop import vText +from icalendar.cal import Event +import pytest + +def test_multiple(calendars): + """Check opening multiple calendars.""" + + cals = calendars.multiple.multiple_calendar_components + + assert len(cals) == 2 + assert [comp.name for comp in cals[0].walk()] == ["VCALENDAR", "VEVENT"] + assert [comp.name for comp in cals[1].walk()] == ["VCALENDAR", "VEVENT", "VEVENT"] + assert cals[0]["prodid"] == vText( + "-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN" + ) + +def test_multiple_events(): + """Raises ValueError unless multiple=True""" + event_components=""" +BEGIN:VEVENT +END:VEVENT +BEGIN:VEVENT +END:VEVENT +""" + with pytest.raises(ValueError) as exception: + Event.from_ical(event_components, multiple=False) + +def test_missing_event(): + """Raises ValueError if no component found""" + with pytest.raises(ValueError) as exception: + Event.from_ical('') diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_oss_fuzz_errors.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_oss_fuzz_errors.py new file mode 100644 index 0000000..235c947 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_oss_fuzz_errors.py @@ -0,0 +1,20 @@ +"""This file collects errors that the OSS FUZZ build has found.""" + +from datetime import time + +import pytest + +from icalendar import Calendar +from icalendar.prop import vDDDLists + + +def test_stack_is_empty(): + """If we get passed an invalid string, we expect to get a ValueError.""" + with pytest.raises(ValueError): + Calendar.from_ical("END:CALENDAR") + + +def test_vdd_list_type_mismatch(): + """If we pass in a string type, we expect it to be converted to bytes""" + vddd_list = vDDDLists([time(hour=6, minute=6, second=6)]) + assert vddd_list.to_ical() == b"060606" diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_parsing.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_parsing.py new file mode 100644 index 0000000..acb67ad --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_parsing.py @@ -0,0 +1,257 @@ +"""Tests checking that parsing works""" + +import base64 +from datetime import datetime + +import pytest + +from icalendar import Calendar, Event, vBinary, vRecur +from icalendar.parser import Contentline, Parameters, unescape_char + + +@pytest.mark.parametrize( + "calendar_name", + [ + # Issue #178 - A component with an unknown/invalid name is represented + # as one of the known components, the information about the original + # component name is lost. + # https://github.com/collective/icalendar/issues/178 https://github.com/collective/icalendar/pull/180 + # Parsing of a nonstandard component + "issue_178_component_with_invalid_name_represented", + # Nonstandard component inside other components, also has properties + "issue_178_custom_component_inside_other", + # Nonstandard component is able to contain other components + "issue_178_custom_component_contains_other", + ], +) +def test_calendar_to_ical_is_inverse_of_from_ical(calendars, calendar_name): + calendar = getattr(calendars, calendar_name) + assert calendar.to_ical().splitlines() == calendar.raw_ics.splitlines() + assert calendar.to_ical() == calendar.raw_ics + + +@pytest.mark.parametrize( + ("raw_content_line", "expected_output"), + [ + # Issue #142 - Multivalued parameters. This is needed for VCard 3.0. + # see https://github.com/collective/icalendar/pull/142 + ( + "TEL;TYPE=HOME,VOICE:000000000", + ("TEL", Parameters({"TYPE": ["HOME", "VOICE"]}), "000000000"), + ), + # Issue #143 - Allow dots in property names. Another vCard related issue. + # see https://github.com/collective/icalendar/pull/143 + ( + "ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.ADR:;;This is the Adress 08; Some City;;12345;Germany", + ( + "ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.ADR", + Parameters(), + ";;This is the Adress 08; Some City;;12345;Germany", + ), + ), + ( + "ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.X-ABLABEL:", + ( + "ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.X-ABLABEL", + Parameters(), + "", + ), + ), + ], +) +def test_content_lines_parsed_properly(raw_content_line, expected_output): + assert Contentline.from_ical(raw_content_line).parts() == expected_output + + +@pytest.mark.parametrize( + "timezone_info", + [ + # General timezone aware dates in ical string + (b"DTSTART;TZID=America/New_York:20130907T120000"), + (b"DTEND;TZID=America/New_York:20130907T170000"), + # Specific timezone aware exdates in ical string + (b"EXDATE;TZID=America/New_York:20131012T120000"), + (b"EXDATE;TZID=America/New_York:20131011T120000"), + ], +) +def test_timezone_info_present_in_ical_issue_112(events, timezone_info): + """Issue #112 - No timezone info on EXDATE + + https://github.com/collective/icalendar/issues/112 + """ + assert timezone_info in events.issue_112_missing_tzinfo_on_exdate.to_ical() + + +def test_timezone_name_parsed_issue_112(events): + """Issue #112 - No timezone info on EXDATE + + https://github.com/collective/icalendar/issues/112 + """ + assert ( + events.issue_112_missing_tzinfo_on_exdate["exdate"][0].dts[0].dt.tzname() + == "EDT" + ) + + +def test_issue_157_removes_trailing_semicolon(events): + """Issue #157 - Recurring rules and trailing semicolons + + https://github.com/collective/icalendar/pull/157 + """ + recur = events.issue_157_removes_trailing_semicolon.decoded("RRULE") + assert isinstance(recur, vRecur) + assert recur.to_ical() == b"FREQ=YEARLY;BYDAY=1SU;BYMONTH=11" + + +@pytest.mark.parametrize( + "event_name", + [ + # https://github.com/collective/icalendar/pull/100 + ("issue_100_transformed_doctests_into_unittests"), + ("issue_184_broken_representation_of_period"), + # PERIOD should be put back into shape + "issue_156_RDATE_with_PERIOD", + "issue_156_RDATE_with_PERIOD_list", + "event_with_unicode_organizer", + ], +) +def test_event_to_ical_is_inverse_of_from_ical(events, event_name): + """Make sure that an event's ICS is equal to the ICS it was made from.""" + event = events[event_name] + assert event.to_ical().splitlines() == event.raw_ics.splitlines() + assert event.to_ical() == event.raw_ics + + +def test_decode_rrule_attribute_error_issue_70(events): + # Issue #70 - e.decode("RRULE") causes Attribute Error + # see https://github.com/collective/icalendar/issues/70 + recur = events.issue_70_rrule_causes_attribute_error.decoded("RRULE") + assert isinstance(recur, vRecur) + assert recur.to_ical() == b"FREQ=WEEKLY;UNTIL=20070619T225959;INTERVAL=1" + + +def test_description_parsed_properly_issue_53(events): + """Issue #53 - Parsing failure on some descriptions? + + https://github.com/collective/icalendar/issues/53 + """ + assert ( + b"July 12 at 6:30 PM" + in events.issue_53_description_parsed_properly["DESCRIPTION"].to_ical() + ) + + +def test_raises_value_error_for_properties_without_parent_pull_179(): + """Found an issue where from_ical() would raise IndexError for + properties without parent components. + + https://github.com/collective/icalendar/pull/179 + """ + with pytest.raises(ValueError): + Calendar.from_ical("VERSION:2.0") + + +def test_tzid_parsed_properly_issue_53(timezones): + """Issue #53 - Parsing failure on some descriptions? + + https://github.com/collective/icalendar/issues/53 + """ + assert ( + timezones.issue_53_tzid_parsed_properly["tzid"].to_ical() == b"America/New_York" + ) + + +def test_timezones_to_ical_is_inverse_of_from_ical(timezones): + """Issue #55 - Parse error on utc-offset with seconds value + see https://github.com/collective/icalendar/issues/55""" + timezone = timezones["issue_55_parse_error_on_utc_offset_with_seconds"] + assert timezone.to_ical() == timezone.raw_ics + + +@pytest.mark.parametrize( + ("date", "expected_output"), + [ + (datetime(2012, 7, 16, 0, 0, 0), b"DTSTART:20120716T000000Z"), + (datetime(2021, 11, 17, 15, 9, 15), b"DTSTART:20211117T150915Z"), + ], +) +def test_no_tzid_when_utc(utc, date, expected_output): + """Issue #58 - TZID on UTC DATE-TIMEs + Issue #335 - UTC timezone identification is broken + + https://github.com/collective/icalendar/issues/58 + https://github.com/collective/icalendar/issues/335 + """ + # According to RFC 5545: "The TZID property parameter MUST NOT be + # applied to DATE-TIME or TIME properties whose time values are + # specified in UTC. + date = date.replace(tzinfo=utc) + event = Event() + event.add("dtstart", date) + assert expected_output in event.to_ical() + + +def test_vBinary_base64_encoded_issue_82(): + """Issue #82 - vBinary __repr__ called rather than to_ical from + container types + https://github.com/collective/icalendar/issues/82 + """ + b = vBinary("text") + b.params["FMTTYPE"] = "text/plain" + assert b.to_ical() == base64.b64encode(b"text") + + +def test_creates_event_with_base64_encoded_attachment_issue_82(events): + """Issue #82 - vBinary __repr__ called rather than to_ical from + container types + https://github.com/collective/icalendar/issues/82 + """ + b = vBinary("text") + b.params["FMTTYPE"] = "text/plain" + event = Event() + event.add("ATTACH", b) + assert event.to_ical() == events.issue_82_expected_output.raw_ics + + +@pytest.mark.parametrize( + "calendar_name", + [ + # Issue #466 - [BUG] TZID timezone is ignored when forward-slash is used + # https://github.com/collective/icalendar/issues/466 + "issue_466_respect_unique_timezone", + "issue_466_convert_tzid_with_slash", + ], +) +def test_handles_unique_tzid(calendars, in_timezone, calendar_name): + calendar = calendars[calendar_name] + event = calendar.walk("VEVENT")[0] + print(vars(event)) + start_dt = event["dtstart"].dt + end_dt = event["dtend"].dt + assert start_dt == in_timezone(datetime(2022, 10, 21, 20, 0, 0), "Europe/Stockholm") + assert end_dt == in_timezone(datetime(2022, 10, 21, 21, 0, 0), "Europe/Stockholm") + + +@pytest.mark.parametrize( + ("event_name", "expected_cn", "expected_ics"), + [ + ( + "event_with_escaped_characters", + r"that, that; %th%%at%\ that:", + "это, то; that\\ %th%%at%:", + ), + ("event_with_escaped_character1", r"Society, 2014", "that"), + ("event_with_escaped_character2", r"Society\ 2014", "that"), + ("event_with_escaped_character3", r"Society; 2014", "that"), + ("event_with_escaped_character4", r"Society: 2014", "that"), + ], +) +def test_escaped_characters_read(event_name, expected_cn, expected_ics, events): + event = events[event_name] + assert event["ORGANIZER"].params["CN"] == expected_cn + assert event["ORGANIZER"].to_ical() == expected_ics.encode("utf-8") + + +def test_unescape_char(): + assert unescape_char(b"123") == b"123" + assert unescape_char(b"\\n") == b"\n" diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_period.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_period.py new file mode 100644 index 0000000..bb1f362 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_period.py @@ -0,0 +1,112 @@ +"""These tests make sure that we have some coverage on the usage of the PERIOD value type. + +See +- https://github.com/collective/icalendar/issues/156 +- https://github.com/pimutils/khal/issues/152#issuecomment-933635248 +""" + +import datetime + +import pytest + +from icalendar.prop import vDDDTypes, vPeriod + + +@pytest.mark.parametrize( + ("calname", "tzname", "index", "period_string"), + [ + ( + "issue_156_RDATE_with_PERIOD_TZID_khal_2", + "Europe/Berlin", + 0, + "20211101T160000/20211101T163000", + ), + ( + "issue_156_RDATE_with_PERIOD_TZID_khal_2", + "Europe/Berlin", + 1, + "20211206T160000/20211206T163000", + ), + ( + "issue_156_RDATE_with_PERIOD_TZID_khal_2", + "Europe/Berlin", + 2, + "20220103T160000/20220103T163000", + ), + ( + "issue_156_RDATE_with_PERIOD_TZID_khal_2", + "Europe/Berlin", + 3, + "20220207T160000/20220207T163000", + ), + ] + + [ + ("issue_156_RDATE_with_PERIOD_TZID_khal", "America/Chicago", i, period) + for i, period in enumerate( + ( + "20180327T080000/20180327T0" + "90000,20180403T080000/20180403T090000,20180410T080000/20180410T090000,2018" + "0417T080000/20180417T090000,20180424T080000/20180424T090000,20180501T08000" + "0/20180501T090000,20180508T080000/20180508T090000,20180515T080000/20180515" + "T090000,20180522T080000/20180522T090000,20180529T080000/20180529T090000,20" + "180605T080000/20180605T090000,20180612T080000/20180612T090000,20180619T080" + "000/20180619T090000,20180626T080000/20180626T090000,20180703T080000/201807" + "03T090000,20180710T080000/20180710T090000,20180717T080000/20180717T090000," + "20180724T080000/20180724T090000,20180731T080000/20180731T090000" + ).split(",") + ) + ], +) +def test_issue_156_period_list_in_rdate( + calendars, calname, tzname, index, period_string +): + """Check items in a list of period values.""" + calendar = calendars[calname] + rdate = calendar.walk("vevent")[0]["rdate"] + period = rdate.dts[index] + assert period.dt == vDDDTypes.from_ical(period_string, timezone=tzname) + + +def test_duration_properly_parsed(events): + """This checks the duration PT5H30M.""" + start = vDDDTypes.from_ical("19970109T180000Z") + duration = vDDDTypes.from_ical("PT5H30M") + rdate = events.issue_156_RDATE_with_PERIOD_list["RDATE"] + print(rdate) + period = rdate.dts[1].dt + print(dir(duration)) + assert period[0] == start + assert period[1].days == 0 + assert period[1].seconds == (5 * 60 + 30) * 60 + assert period[1] == duration + + +def test_tzid_is_part_of_the_parameters(calendars): + """The TZID should be mentioned in the parameters.""" + event = list(calendars.period_with_timezone.walk("VEVENT"))[0] + assert event["RDATE"].params["TZID"] == "America/Vancouver" + + +def test_tzid_is_part_of_the_period_values(calendars, tzp): + """The TZID should be set in the datetime.""" + event = list(calendars.period_with_timezone.walk("VEVENT"))[0] + start, end = event["RDATE"].dts[0].dt + assert start == tzp.localize( + datetime.datetime(2023, 12, 13, 12), "America/Vancouver" + ) + assert end == tzp.localize(datetime.datetime(2023, 12, 13, 15), "America/Vancouver") + + +def test_period_overlaps(): + # 30 minute increments + datetime_1 = datetime.datetime(2024, 11, 20, 12, 0) # 12:00 + datetime_2 = datetime.datetime(2024, 11, 20, 12, 30) # 12:30 + datetime_3 = datetime.datetime(2024, 11, 20, 13, 0) # 13:00 + + period_1 = vPeriod((datetime_1, datetime_2)) + period_2 = vPeriod((datetime_1, datetime_3)) + period_3 = vPeriod((datetime_2, datetime_3)) + + assert period_1.overlaps(period_2) + assert period_3.overlaps(period_2) + assert not period_1.overlaps(period_3) diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_property_params.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_property_params.py new file mode 100644 index 0000000..aaaeb23 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_property_params.py @@ -0,0 +1,145 @@ +import re + +import pytest + +from icalendar import Calendar, Event, Parameters, vCalAddress + + +@pytest.mark.parametrize( + ("parameter", "expected"), + [ + # Simple parameter:value pair + (Parameters(parameter1="Value1"), b"PARAMETER1=Value1"), + # Parameter with list of values must be separated by comma + (Parameters({"parameter1": ["Value1", "Value2"]}), b"PARAMETER1=Value1,Value2"), + # Multiple parameters must be separated by a semicolon + ( + Parameters({"RSVP": "TRUE", "ROLE": "REQ-PARTICIPANT"}), + b"ROLE=REQ-PARTICIPANT;RSVP=TRUE", + ), + # Parameter values containing ',;:' must be double quoted + (Parameters({"ALTREP": "http://www.wiz.org"}), b'ALTREP="http://www.wiz.org"'), + # list items must be quoted separately + ( + Parameters( + {"MEMBER": ["MAILTO:projectA@host.com", "MAILTO:projectB@host.com"]} + ), + b'MEMBER="MAILTO:projectA@host.com","MAILTO:projectB@host.com"', + ), + ( + Parameters( + { + "parameter1": "Value1", + "parameter2": ["Value2", "Value3"], + "ALTREP": ["http://www.wiz.org", "value4"], + } + ), + b'ALTREP="http://www.wiz.org","value4";PARAMETER1=Value1;PARAMETER2=Value2,Value3', + ), + # Including empty strings + (Parameters({"PARAM": ""}), b"PARAM="), + # We can also parse parameter strings + ( + Parameters( + {"MEMBER": ["MAILTO:projectA@host.com", "MAILTO:projectB@host.com"]} + ), + b'MEMBER="MAILTO:projectA@host.com","MAILTO:projectB@host.com"', + ), + # We can also parse parameter strings + ( + Parameters( + { + "PARAMETER1": "Value1", + "ALTREP": ["http://www.wiz.org", "value4"], + "PARAMETER2": ["Value2", "Value3"], + } + ), + b'ALTREP="http://www.wiz.org","value4";PARAMETER1=Value1;PARAMETER2=Value2,Value3', + ), + ], +) +def test_parameter_to_ical_is_inverse_of_from_ical(parameter, expected): + assert parameter.to_ical() == expected + assert Parameters.from_ical(expected.decode("utf-8")) == parameter + + +def test_parse_parameter_string_without_quotes(): + assert Parameters.from_ical("PARAM1=Value 1;PARA2=Value 2") == Parameters( + {"PARAM1": "Value 1", "PARA2": "Value 2"} + ) + + +def test_parametr_is_case_insensitive(): + parameter = Parameters(parameter1="Value1") + assert parameter["parameter1"] == parameter["PARAMETER1"] == parameter["PaRaMeTer1"] + + +def test_parameter_keys_are_uppercase(): + parameter = Parameters(parameter1="Value1") + assert list(parameter.keys()) == ["PARAMETER1"] + + +@pytest.mark.parametrize( + ("cn_param", "cn_quoted"), + [ + # not double-quoted + ("Aramis", "Aramis"), + # if a space is present - enclose in double quotes + ("Aramis Alameda", '"Aramis Alameda"'), + # a single quote in parameter value - double quote the value + ("Aramis d'Alameda", '"Aramis d\'Alameda"'), + ("Арамис д'Аламеда", '"Арамис д\'Аламеда"'), + # Before, double quote is replaced with single quote + # Since RFC 6868, we replace this with ^' + ('Aramis d"Alameda', '"Aramis d^\'Alameda"'), + ], +) +def test_quoting(cn_param, cn_quoted): + event = Event() + attendee = vCalAddress("test@example.com") + attendee.params["CN"] = cn_param + event.add("ATTENDEE", attendee) + assert f"ATTENDEE;CN={cn_quoted}:test@example.com" in event.to_ical().decode( + "utf-8" + ) + + +def test_property_params(): + """Property parameters with values containing a COLON character, a + SEMICOLON character or a COMMA character MUST be placed in quoted + text.""" + cal_address = vCalAddress("mailto:john.doe@example.org") + cal_address.params["CN"] = "Doe, John" + ical = Calendar() + ical.add("organizer", cal_address) + + ical_str = Calendar.to_ical(ical) + exp_str = ( + b"""BEGIN:VCALENDAR\r\nORGANIZER;CN="Doe, John":""" + b"""mailto:john.doe@example.org\r\nEND:VCALENDAR\r\n""" + ) + + assert ical_str == exp_str + + # other way around: ensure the property parameters can be restored from + # an icalendar string. + ical2 = Calendar.from_ical(ical_str) + assert ical2.get("ORGANIZER").params.get("CN") == "Doe, John" + + +def test_parse_and_access_property_params(calendars): + """Parse an ics string and access some property parameters then. + This is a follow-up of a question received per email. + + """ + event = calendars.property_params.walk("VEVENT")[0] + attendee = event["attendee"][0] + assert attendee.to_ical() == b"MAILTO:rembrand@xs4all.nl" + assert attendee.params.to_ical() == b"CN=RembrandXS;PARTSTAT=NEEDS-ACTION;RSVP=TRUE" + assert attendee.params["cn"] == "RembrandXS" + + +def test_repr(): + """Test correct class representation.""" + it = Parameters(parameter1="Value1") + assert re.match(r"Parameters\({u?'PARAMETER1': u?'Value1'}\)", str(it)) diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_pytz_zoneinfo_integration.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_pytz_zoneinfo_integration.py new file mode 100644 index 0000000..c93735e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_pytz_zoneinfo_integration.py @@ -0,0 +1,104 @@ +"""This tests the switch to different timezone implementations. + +These are mostly located in icalendar.timezone. +""" + +try: + import pytz + + from icalendar.timezone.pytz import PYTZ +except ImportError: + pytz = None +import copy +import pickle +from datetime import datetime + +import pytest +from dateutil.rrule import MONTHLY, rrule +from dateutil.tz.tz import _tzicalvtz + +from icalendar.timezone.zoneinfo import ZONEINFO, zoneinfo + +if pytz: + PYTZ_TIMEZONES = pytz.all_timezones + TZP_ = [PYTZ(), ZONEINFO()] + NEW_TZP_NAME = ["pytz", "zoneinfo"] +else: + PYTZ_TIMEZONES = [] + TZP_ = [ZONEINFO()] + NEW_TZP_NAME = ["zoneinfo"] + + +@pytest.mark.parametrize( + "tz_name", PYTZ_TIMEZONES + list(zoneinfo.available_timezones()) +) +@pytest.mark.parametrize("tzp_", TZP_) +def test_timezone_names_are_known(tz_name, tzp_): + """Make sure that all timezones are understood.""" + if tz_name in ("Factory", "localtime"): + pytest.skip() + assert tzp_.knows_timezone_id( + tz_name + ), f"{tzp_.__class__.__name__} should know {tz_name}" + + +@pytest.mark.parametrize("func", [pickle.dumps, copy.copy, copy.deepcopy]) +@pytest.mark.parametrize( + "obj", + [ + _tzicalvtz("id"), + rrule(freq=MONTHLY, count=4, dtstart=datetime(2028, 10, 1), cache=True), + ], +) +def test_can_pickle_timezone(func, tzp, obj): + """Check that we can serialize and copy timezones.""" + func(obj) + + +def test_copied_rrule_is_the_same(): + """When we copy an rrule, we want it to be the same after this.""" + r = rrule(freq=MONTHLY, count=4, dtstart=datetime(2028, 10, 1), cache=True) + assert str(copy.deepcopy(r)) == str(r) + + +def test_tzp_properly_switches(tzp, tzp_name): + """We want the default implementation to switch.""" + assert (tzp_name == "pytz") == tzp.uses_pytz() + + +def test_tzp_is_pytz_only(tzp, tzp_name, pytz_only): + """We want the default implementation to switch.""" + assert tzp_name == "pytz" + assert tzp.uses_pytz() + + +def test_cache_reuse_timezone_cache(tzp, timezones): + """Make sure we do not cache the timezones twice and change them.""" + tzp.cache_timezone_component(timezones.pacific_fiji) + tzp1 = tzp.timezone("custom_Pacific/Fiji") + assert tzp1 is tzp.timezone("custom_Pacific/Fiji") + tzp.cache_timezone_component(timezones.pacific_fiji) + assert tzp1 is tzp.timezone("custom_Pacific/Fiji"), "Cache is not replaced." + + +@pytest.mark.parametrize("new_tzp_name", NEW_TZP_NAME) +def test_cache_is_emptied_when_tzp_is_switched(tzp, timezones, new_tzp_name): + """Make sure we do not reuse the timezones created when we switch the provider.""" + tzp.cache_timezone_component(timezones.pacific_fiji) + tz1 = tzp.timezone("custom_Pacific/Fiji") + tzp.use(new_tzp_name) + tzp.cache_timezone_component(timezones.pacific_fiji) + tz2 = tzp.timezone("custom_Pacific/Fiji") + assert tz1 is not tz2 + + +def test_invalid_name(tzp): + """Check that the provider name is OK.""" + provider = "invalid_provider" + with pytest.raises(ValueError) as e: + tzp.use(provider) + # f"Unknown provider {provider}. Use 'pytz' or 'zoneinfo'." + message = e.value.args[0] + assert f"Unknown provider {provider}." in message + assert "zoneinfo" in message + assert "pytz" in message diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_recurrence.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_recurrence.py new file mode 100644 index 0000000..e237a8c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_recurrence.py @@ -0,0 +1,130 @@ +from datetime import date, datetime + +import pytest + +from icalendar import Event + + +def test_recurrence_properly_parsed(events): + assert events.event_with_recurrence["rrule"] == {"COUNT": [100], "FREQ": ["DAILY"]} + + +@pytest.mark.parametrize( + ("i", "exception_date"), + [ + (0, datetime(1996, 4, 2, 1, 0)), + (1, datetime(1996, 4, 3, 1, 0)), + (2, datetime(1996, 4, 4, 1, 0)), + ], +) +def test_exdate_properly_parsed(events, i, exception_date, in_timezone): + assert events.event_with_recurrence["exdate"].dts[i].dt == in_timezone( + exception_date, "UTC" + ) + + +def test_exdate_properly_marshalled(events): + actual = events.event_with_recurrence["exdate"].to_ical() + assert actual == b"19960402T010000Z,19960403T010000Z,19960404T010000Z" + + +# TODO: DOCUMENT BETTER! +# In this case we have multiple EXDATE definitions, one per line. +# Icalendar makes a list out of this instead of zipping it into one +# vDDDLists object. Actually, this feels correct for me, as it also +# allows to define different timezones per exdate line - but client +# code has to handle this as list and not blindly expecting to be able +# to call event['EXDATE'].to_ical() on it: +def test_exdate_formed_from_exdates_on_multiple_lines_is_a_list(events): + exdate = events.event_with_recurrence_exdates_on_different_lines["exdate"] + assert isinstance(exdate, list) + + +@pytest.mark.parametrize( + ("i", "exception_date", "exception_date_ics"), + [ + (0, datetime(2012, 5, 29, 10, 0), b"20120529T100000"), + (1, datetime(2012, 4, 3, 10, 0), b"20120403T100000"), + (2, datetime(2012, 4, 10, 10, 0), b"20120410T100000"), + (3, datetime(2012, 5, 1, 10, 0), b"20120501T100000"), + (4, datetime(2012, 4, 17, 10, 0), b"20120417T100000"), + ], +) +def test_list_exdate_to_ical_is_inverse_of_from_ical( + events, i, exception_date, exception_date_ics, in_timezone +): + exdate = events.event_with_recurrence_exdates_on_different_lines["exdate"] + assert exdate[i].dts[0].dt == in_timezone(exception_date, "Europe/Vienna") + assert exdate[i].to_ical() == exception_date_ics + + +@pytest.mark.parametrize( + ("freq", "byday", "dtstart", "expected"), + [ + # Test some YEARLY BYDAY repeats + ( + "YEARLY", + "1SU", + date(2016, 1, 3), # 1st Sunday in year + b"BEGIN:VEVENT\r\nSUMMARY:Event YEARLY 1SU\r\nDTSTART;VALUE=DATE:20160103\r\nRRULE:FREQ=YEARLY;BYDAY=1SU\r\nEND:VEVENT\r\n", + ), + ( + "YEARLY", + "53MO", + date(1984, 12, 31), # 53rd Monday in (leap) year + b"BEGIN:VEVENT\r\nSUMMARY:Event YEARLY 53MO\r\nDTSTART;VALUE=DATE:19841231\r\nRRULE:FREQ=YEARLY;BYDAY=53MO\r\nEND:VEVENT\r\n", + ), + ( + "YEARLY", + "-1TU", + date(1999, 12, 28), # Last Tuesday in year + b"BEGIN:VEVENT\r\nSUMMARY:Event YEARLY -1TU\r\nDTSTART;VALUE=DATE:19991228\r\nRRULE:FREQ=YEARLY;BYDAY=-1TU\r\nEND:VEVENT\r\n", + ), + ( + "YEARLY", + "-17WE", + date(2000, 9, 6), # 17th-to-last Wednesday in year + b"BEGIN:VEVENT\r\nSUMMARY:Event YEARLY -17WE\r\nDTSTART;VALUE=DATE:20000906\r\nRRULE:FREQ=YEARLY;BYDAY=-17WE\r\nEND:VEVENT\r\n", + ), + # Test some MONTHLY BYDAY repeats + ( + "MONTHLY", + "2TH", + date(2003, 4, 10), # 2nd Thursday in month + b"BEGIN:VEVENT\r\nSUMMARY:Event MONTHLY 2TH\r\nDTSTART;VALUE=DATE:20030410\r\nRRULE:FREQ=MONTHLY;BYDAY=2TH\r\nEND:VEVENT\r\n", + ), + ( + "MONTHLY", + "-3FR", + date(2017, 5, 12), # 3rd-to-last Friday in month + b"BEGIN:VEVENT\r\nSUMMARY:Event MONTHLY -3FR\r\nDTSTART;VALUE=DATE:20170512\r\nRRULE:FREQ=MONTHLY;BYDAY=-3FR\r\nEND:VEVENT\r\n", + ), + ( + "MONTHLY", + "-5SA", + date(2053, 11, 1), # 5th-to-last Saturday in month + b"BEGIN:VEVENT\r\nSUMMARY:Event MONTHLY -5SA\r\nDTSTART;VALUE=DATE:20531101\r\nRRULE:FREQ=MONTHLY;BYDAY=-5SA\r\nEND:VEVENT\r\n", + ), + # Specifically test examples from the report of Issue #518 + # https://github.com/collective/icalendar/issues/518 + ( + "YEARLY", + "9MO", + date(2023, 2, 27), # 9th Monday in year + b"BEGIN:VEVENT\r\nSUMMARY:Event YEARLY 9MO\r\nDTSTART;VALUE=DATE:20230227\r\nRRULE:FREQ=YEARLY;BYDAY=9MO\r\nEND:VEVENT\r\n", + ), + ( + "YEARLY", + "10MO", + date(2023, 3, 6), # 10th Monday in year + b"BEGIN:VEVENT\r\nSUMMARY:Event YEARLY 10MO\r\nDTSTART;VALUE=DATE:20230306\r\nRRULE:FREQ=YEARLY;BYDAY=10MO\r\nEND:VEVENT\r\n", + ), + ], +) +def test_byday_to_ical(freq, byday, dtstart, expected): + """Test the BYDAY rule is correctly processed by to_ical().""" + event = Event() + event.add("SUMMARY", " ".join(["Event", freq, byday])) + event.add("DTSTART", dtstart) + event.add("RRULE", {"FREQ": [freq], "BYDAY": byday}) + assert event.to_ical() == expected diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_6868.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_6868.py new file mode 100644 index 0000000..7a562c5 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_6868.py @@ -0,0 +1,108 @@ +"""This implemensts RFC 6868. + +There are only some changes to parameters needed. +""" +import os +from typing import TYPE_CHECKING + +import pytest + +from icalendar import Calendar +from icalendar.parser import dquote, rfc_6868_escape, rfc_6868_unescape + +if TYPE_CHECKING: + from icalendar import vCalAddress, vText + + +param_duplicate = pytest.mark.parametrize( + "duplicate", + [ lambda x:x, lambda cal: Calendar.from_ical(cal.to_ical()) ] +) + +@param_duplicate +def test_rfc_6868_example(calendars, duplicate): + """Check the example from the RFC.""" + cal : Calendar = duplicate(calendars.rfc_6868) + attendee : vCalAddress = cal.events[0]["attendee"] + assert attendee.name == 'George Herman "Babe" Ruth' + + +@param_duplicate +def test_all_parameters(calendars, duplicate): + """Check that all examples get decoded correctly.""" + cal : Calendar = duplicate(calendars.rfc_6868) + param = cal["X-PARAM"].params["ALL"] + assert param == '^"\n' + + +@param_duplicate +def test_unknown_character(calendars, duplicate): + """if a ^ (U+005E) character is followed by any character other than + the ones above, parsers MUST leave both the ^ and the following + character in place""" + cal : Calendar = duplicate(calendars.rfc_6868) + param = cal["X-PARAM"].params["UNKNOWN"] + assert param == "^a^ ^asd" + + +@pytest.mark.parametrize( + ("text", "expected"), + [ + ("^a", "^a"), + ("^^", "^"), + # ("^n", ), # see other test + ("^'", '"'), + ("^^a^b", "^a^b") + ], +) +@pytest.mark.parametrize( + "modify", [lambda x: x, lambda x: x*10, lambda x: f"asd{x}aaA^AA"] +) +def test_unescape(text, expected, modify): + """Check unescaping.""" + result = rfc_6868_unescape(modify(text)) + assert result == modify(expected) + + +@pytest.mark.parametrize( + "newline", ["\n", "\r\n", "\r", os.linesep]) +def test_unescape_newline(newline, monkeypatch): + """Unescape the newline.""" + monkeypatch.setattr(os, "linesep", newline) + assert rfc_6868_unescape("^n") == newline + + +param_values = pytest.mark.parametrize("text, expected", [ + ("", ""), + ("^", "^^"), + ("^n", "^^n"), + ("This text\n has\r several\r\n lines", "This text^n has^n several^n lines"), + ('Call me "Harry".', "Call me ^'Harry^'."), +] +) + +@param_values +def test_escape_rfc_6868(text, expected): + """Check that we can escape the content with special characters.""" + escaped = rfc_6868_escape(text) + assert escaped == expected, f"{escaped!r} == {expected!r}" + assert rfc_6868_escape(rfc_6868_unescape(escaped)) == expected + + +@param_values +def test_escaping_parameters(text, expected): + cal = Calendar() + cal.add("X-Param", "asd") + param : vText = cal["X-PARAM"] + param.params["RFC6868"] = text + ical = cal.to_ical().decode() + print(ical) + assert "X-PARAM;RFC6868=" + dquote(expected) in ical + + +def test_encode_example_again(calendars): + """The example file should yield its content again.""" + cal : Calendar = calendars.rfc_6868 + again = Calendar.from_ical(cal.to_ical()) + assert cal == again + assert cal.to_ical() == again.to_ical() diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_7529.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_7529.py new file mode 100644 index 0000000..e74be7e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_7529.py @@ -0,0 +1,86 @@ +"""This tests the compatibility with RFC 7529. + +See +- https://github.com/collective/icalendar/issues/655 +- https://www.rfc-editor.org/rfc/rfc7529.html +""" + +import pytest + +from icalendar.prop import vMonth, vRecur, vSkip + + +@pytest.mark.parametrize( + ("uid", "scale"), + [ + ("4.3.1", "CHINESE"), + ("4.3.2", "ETHIOPIC"), + ("4.3.3", "HEBREW"), + ("4.3.4", "GREGORIAN"), + ], +) +def test_rscale(calendars, uid, scale): + """Check that the RSCALE is parsed correctly.""" + event = calendars.rfc_7529.walk(select=lambda c: c.get("UID") == uid)[0] + print(event.errors) + rrule = event["RRULE"] + print(rrule) + assert rrule["RSCALE"] == [scale] + + +@pytest.mark.parametrize( + ("uid", "skip"), + [ + ("4.3.2", None), + ("4.3.3", ["FORWARD"]), + ], +) +def test_rscale_with_skip(calendars, uid, skip): + """Check that the RSCALE is parsed correctly.""" + event = calendars.rfc_7529.walk(select=lambda c: c.get("UID") == uid)[0] + recur = event["RRULE"] + assert recur.get("SKIP") == skip + + +def test_leap_month(calendars): + """Check that we can parse the leap month.""" + event = calendars.rfc_7529.walk(select=lambda c: c.get("UID") == "4.3.3")[0] + recur = event["RRULE"] + assert recur["BYMONTH"][0].leap is True + + +@pytest.mark.parametrize( + ("ty", "recur", "ics"), + [ + ( + vRecur, + vRecur(rscale="CHINESE", freq="YEARLY"), + b"RSCALE=CHINESE;FREQ=YEARLY", + ), + (vRecur, vRecur(bymonth=vMonth(10)), b"BYMONTH=10"), + (vRecur, vRecur(bymonth=vMonth("5L")), b"BYMONTH=5L"), + (vMonth, vMonth(10), b"10"), + (vMonth, vMonth("5L"), b"5L"), + (vSkip, vSkip.OMIT, b"OMIT"), + (vSkip, vSkip.BACKWARD, b"BACKWARD"), + (vSkip, vSkip.FORWARD, b"FORWARD"), + (vSkip, vSkip("OMIT"), b"OMIT"), + (vSkip, vSkip("BACKWARD"), b"BACKWARD"), + (vSkip, vSkip("FORWARD"), b"FORWARD"), + ( + vRecur, + vRecur(rscale="GREGORIAN", freq="YEARLY", skip="FORWARD"), + b"RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=FORWARD", + ), + ( + vRecur, + vRecur(rscale="GREGORIAN", freq="YEARLY", skip=vSkip.FORWARD), + b"RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=FORWARD", + ), + ], +) +def test_conversion(ty, recur, ics): + """Test string conversion.""" + assert recur.to_ical() == ics + assert ty.from_ical(ics.decode()) == recur + assert ty.from_ical(ics.decode()).to_ical() == ics diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_7986.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_7986.py new file mode 100644 index 0000000..bed74fd --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_7986.py @@ -0,0 +1,191 @@ +"""This tests additional attributes from :rfc:`7986`. + +Some attributes are also available as ``X-*`` attributes. +They are also considered. +""" +from __future__ import annotations + +from typing import Union + +import pytest + +from icalendar import Calendar, Event, Journal, Todo +from icalendar.prop import vText + + +@pytest.fixture() +def calendar() -> Calendar: + """Empty calendar""" + return Calendar() + + +param_name = pytest.mark.parametrize("name", ["Company Vacation Days", "Calendar Name"]) +param_prop = pytest.mark.parametrize("prop", ["NAME", "X-WR-CALNAME"]) + + +@param_prop +@param_name +def test_get_calendar_name(prop, name, calendar): + """Get the name of the calendar.""" + calendar.add(prop, name) + assert calendar.calendar_name == name + + +@param_name +def test_set_calendar_name(name, calendar): + """Setting the name overrides the old attributes.""" + calendar.calendar_name = name + assert calendar.calendar_name == name + assert calendar["NAME"] == name + + +@param_name +@param_prop +def test_replace_name(name, prop, calendar): + """Setting the name overrides the old attributes.""" + calendar[prop] = "Other Name" + calendar.calendar_name = name + assert calendar.calendar_name == name + + +@param_name +@param_prop +def test_del_name(name, calendar, prop): + """Delete the name.""" + calendar.add(prop, name) + del calendar.calendar_name + assert calendar.calendar_name is None + + +def test_default_name(calendar): + """We have no name by default.""" + assert calendar.calendar_name is None + + +@param_name +def test_setting_the_name_deletes_the_non_standard_attribute(calendar, name): + """The default_attr is deleted when setting the name.""" + calendar["X-WR-CALNAME"] = name + assert "X-WR-CALNAME" in calendar + calendar.calendar_name = "other name" + assert "X-WR-CALNAME" not in calendar + + +@param_name +@pytest.mark.parametrize("order", [1, 2]) +def test_multiple_names_use_the_one_without_a_language(calendar, name, order): + """Add several names and use the one without a language param.""" + if order == 1: + calendar.add("NAME", name) + calendar.add("NAME", vText("Kalendername", params={"LANGUAGE":"de"})) + if order == 2: + calendar.add("NAME", name) + assert calendar.calendar_name == name + + +@param_name +def test_name_is_preferred(calendar, name): + """NAME is more important that X-WR-CALNAME""" + calendar.add("NAME", name) + calendar.add("X-WR-CALNAME", "asd") + assert calendar.calendar_name == name + + + +# For description, we would use the same tests as name, but we also use the +# same code, so it is all right. + +param_color = pytest.mark.parametrize("desc", ["DESCRIPTION", "X-WR-CALDESC"]) + +@param_color +@param_name +def test_description(calendar, desc, name): + """Get the value""" + calendar.add(desc, name) + assert calendar.description == name + +# For color, we would use the same tests as name, but we also use the +# same code, so it is all right. + +param_color = pytest.mark.parametrize("color_param", ["COLOR", "X-APPLE-CALENDAR-COLOR"]) + +@param_color +def test_get_calendar_color(calendar, color_param, color): + """Get the value""" + calendar.add(color_param, color) + assert calendar.color == color + +@param_color +def test_delete_calendar_color(calendar, color_param, color): + """Delete the value""" + calendar.add(color_param, color) + del calendar.color + assert calendar.color == "" + assert color_param not in calendar + +@param_color +def test_set_calendar_color(calendar, color_param, color): + """Set the color and it replaces what is there.""" + calendar.add(color_param, "green") + calendar.color = color + assert calendar.color == color + assert calendar["COLOR"] == color + +def test_get_COLOR_first(calendar, color): + """We prefer COLOR over X-APPLE-CALENDAR-COLOR""" + calendar.add("COLOR", color) + calendar.add("X-APPLE-CALENDAR-COLOR", "green") + assert calendar.color == color + +# The color of the event is a bit different +# It only appears once and does not have a backup. + +@pytest.fixture(params=[Calendar, Event, Todo, Journal]) +def color_component(request) -> Union[Calendar, Event, Todo, Journal]: + """An empty component that should have a color attribute.""" + return request.param() + +@pytest.fixture(params=["blue", "#123456"]) +def color(request) -> str: + """Return a color.""" + return request.param + +def test_default_color(color_component: Union[Calendar, Event, Todo, Journal]): + """There is no color by default.""" + assert color_component.color == "" + +def test_set_the_color(color:str, color_component: Union[Calendar, Event, Todo, Journal]): + """We set the value and get it.""" + color_component.color = color + assert color_component.color == color + assert color_component["COLOR"] == color + +def test_replace_color(color:str, color_component: Union[Calendar, Event, Todo, Journal]): + """Replace the color.""" + color_component.color = "blue" + color_component.color = color + assert color_component.color == color + assert color_component["COLOR"] == color + + +def test_multiple_colors(color_component: Union[Calendar, Event, Todo, Journal]): + """Add several colors and use the first one.""" + color_component.add("COLOR", "blue") + color_component.add("COLOR", "green") + assert color_component.color == "blue" + + +def test_delete_the_color(color_component: Union[Calendar, Event, Todo, Journal]): + """Delete the color.""" + color_component.color = "blue" + del color_component.color + assert "COLOR" not in color_component + assert color_component.color == "" + + +def test_set_if_multiple_colors(color: str, color_component: Union[Calendar, Event, Todo, Journal]): + """Add several colors and use the first one.""" + color_component.add("COLOR", "blue") + color_component.add("COLOR", "green") + color_component.color = color + assert color_component.color == color diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_7986_categories.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_7986_categories.py new file mode 100644 index 0000000..75708be --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_7986_categories.py @@ -0,0 +1,71 @@ +"""This tests the compatibility with RFC 7529. + +CATEGORIES property + +See +- https://github.com/collective/icalendar/issues/655 +- https://www.rfc-editor.org/rfc/rfc7529.html +""" + +from typing import Union + +import pytest + +from icalendar import Calendar, Event, Journal, Todo + +CTJE = Union[Calendar, Event, Journal, Todo] + +@pytest.fixture(params=[Event, Calendar, Todo, Journal]) +def component(request): + """An empty component with possible categories.""" + return request.param() + + +def test_no_categories_at_creation(component: CTJE): + """An empty component has no categories.""" + assert "CATEGORIES" not in component + assert component.categories == [] + + +def test_add_one_category(component: CTJE): + """Add one category.""" + component.add("categories", "Lecture") + assert component.categories == ["Lecture"] + +def test_add_multiple_categories(component: CTJE): + """Add categories.""" + component.add("categories", ["Lecture", "Workshop"]) + assert component.categories == ["Lecture", "Workshop"] + +def test_set_categories(component: CTJE): + """Set categories.""" + component.categories = ["Lecture", "Workshop"] + assert component.categories == ["Lecture", "Workshop"] + + +def test_modify_list(component: CTJE): + """Modify the list and it still works.""" + component.categories = categories = ["cat1"] + categories.append("cat2") + assert component.categories == ["cat1", "cat2"] + + +def test_delete_categories(component: CTJE): + """Delete categories.""" + component.categories = ["Lecture", "Workshop"] + del component.categories + assert "CATEGORIES" not in component + assert component.categories == [] + + +def test_manage_add_append_remove_categories(component: CTJE): + """Manage multiple categories by merging them, then append + and remove a category from the resulting set.""" + component.add("categories", ["c1", "c2"]) + component.add("categories", ["c3", "c4"]) + assert component.categories == ["c1", "c2", "c3", "c4"] + component.categories.append("c5") + assert component.categories == ["c1", "c2", "c3", "c4", "c5"] + component.categories.remove("c2") + assert component.categories == ["c1", "c3", "c4", "c5"] + assert "c1,c3,c4,c5" in component.to_ical().decode() diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_9074.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_9074.py new file mode 100644 index 0000000..63b455b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_9074.py @@ -0,0 +1,44 @@ +"""Test the VALARM compatibility of RFC 9074. + +See https://www.rfc-editor.org/rfc/rfc9074.html +and also https://github.com/collective/icalendar/issues/657 +""" + +import pytest + +from icalendar.prop import vDDDTypes, vText + + +@pytest.mark.parametrize( + ("prop", "cls", "file", "alarm_index"), + [ + ("UID", vText, "rfc_9074_example_1", 0), + ("RELATED-TO", vText, "rfc_9074_example_2", 1), + ("ACKNOWLEDGED", vDDDTypes, "rfc_9074_example_3", 0), + ("PROXIMITY", vText, "rfc_9074_example_proximity", 0), + ], +) +def test_calendar_types(events, prop, cls, file, alarm_index): + """Check the types of the properties.""" + event = events[file] + alarm = event.subcomponents[alarm_index] + value = alarm[prop] + assert isinstance(value, cls) + + +def test_snooze(events): + """Check values of the alarms.""" + alarm_1 = events.rfc_9074_example_3.subcomponents[0] + assert alarm_1["ACKNOWLEDGED"].dt == vDDDTypes.from_ical("20210302T152024Z") + alarm_2 = events.rfc_9074_example_3.subcomponents[1] + assert alarm_2["RELATED-TO"] == "8297C37D-BA2D-4476-91AE-C1EAA364F8E1" + assert alarm_2["RELATED-TO"].params["RELTYPE"] == "SNOOZE" + + +def test_proximity(events): + """Check the proximity values.""" + alarm = events.rfc_9074_example_proximity.subcomponents[0] + assert alarm["PROXIMITY"] == "DEPART" + assert len(alarm.subcomponents) == 1 + location = alarm.subcomponents[0] + assert location["UID"] == "123456-abcdef-98765432" diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_time.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_time.py new file mode 100644 index 0000000..ce12752 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_time.py @@ -0,0 +1,30 @@ +import datetime +import os + +import icalendar + + +def test_value_type_is_not_mapped(): + """Usually, the value should be absent.""" + assert "X-SOMETIME" not in icalendar.cal.types_factory.types_map + + +def test_value_type_is_mapped(x_sometime): + """The value is mapped for the test.""" + assert "X-SOMETIME" in icalendar.cal.types_factory.types_map + + +def test_create_from_ical(x_sometime): + directory = os.path.dirname(__file__) + ics = open(os.path.join(directory, "calendars", "time.ics"), "rb") + cal = icalendar.Calendar.from_ical(ics.read()) + ics.close() + + assert cal["X-SOMETIME"].dt == datetime.time(17, 20, 10) + assert cal["X-SOMETIME"].to_ical() == "172010" + + +def test_create_to_ical(x_sometime): + cal = icalendar.Calendar() + cal.add("X-SOMETIME", datetime.time(17, 20, 10)) + assert b"X-SOMETIME;VALUE=TIME:172010" in cal.to_ical().splitlines() diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_timezone_identification.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_timezone_identification.py new file mode 100644 index 0000000..a6263c2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_timezone_identification.py @@ -0,0 +1,32 @@ +"""Test that we can identify all timezones. + +Timezones can be removed from ./timezone_ids.py if they make the tests fail: +Timezone information changes over time and can be dependent on the operating system's +timezone database (zoneinfo, dateutil) or the package (pytz). +We want to make sure we can roughly identify most of them. +""" + +from icalendar.timezone import tzid_from_tzinfo, tzids_from_tzinfo +from icalendar.timezone.tzp import TZP + + +def test_can_identify_zoneinfo(tzid, zoneinfo_only, tzp:TZP): + """Check that all those zoneinfo timezones can be identified.""" + assert tzid in tzids_from_tzinfo(tzp.timezone(tzid)) + + +def test_can_identify_pytz(tzid, pytz_only, tzp:TZP): + """Check that all those pytz timezones can be identified.""" + assert tzid in tzids_from_tzinfo(tzp.timezone(tzid)) + + +def test_can_identify_dateutil(tzid): + """Check that all those dateutil timezones can be identified.""" + from dateutil.tz import gettz + assert tzid in tzids_from_tzinfo(gettz(tzid)) + + +def test_utc_is_identified(utc): + """Test UTC because it is handled in a special way.""" + assert "UTC" in tzids_from_tzinfo(utc) + assert tzid_from_tzinfo(utc) == "UTC" diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_timezoned.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_timezoned.py new file mode 100644 index 0000000..a5cc9cd --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_timezoned.py @@ -0,0 +1,452 @@ +import datetime + +import dateutil.parser + +import icalendar +from icalendar.prop import tzid_from_dt + + +def test_create_from_ical(calendars, other_tzp): + """Create a calendar from a .ics file.""" + cal = calendars.timezoned + + assert cal["prodid"].to_ical() == b"-//Plone.org//NONSGML plone.app.event//EN" + + timezones = cal.walk("VTIMEZONE") + assert len(timezones) == 1 + + tz = timezones[0] + assert tz["tzid"].to_ical() == b"Europe/Vienna" + + std = tz.walk("STANDARD")[0] + assert std.decoded("TZOFFSETFROM") == datetime.timedelta(0, 7200) + + ev1 = cal.walk("VEVENT")[0] + assert ev1.decoded("DTSTART") == other_tzp.localize( + datetime.datetime(2012, 2, 13, 10, 0, 0), "Europe/Vienna" + ) + assert ev1.decoded("DTSTAMP") == other_tzp.localize( + datetime.datetime(2010, 10, 10, 9, 10, 10), "UTC" + ) + + +def test_create_to_ical(tzp): + cal = icalendar.Calendar() + + cal.add("prodid", "-//Plone.org//NONSGML plone.app.event//EN") + cal.add("version", "2.0") + cal.add("x-wr-calname", "test create calendar") + cal.add("x-wr-caldesc", "icalendar tests") + cal.add("x-wr-relcalid", "12345") + cal.add("x-wr-timezone", "Europe/Vienna") + + tzc = icalendar.Timezone() + tzc.add("tzid", "Europe/Vienna") + tzc.add("x-lic-location", "Europe/Vienna") + + tzs = icalendar.TimezoneStandard() + tzs.add("tzname", "CET") + tzs.add("dtstart", datetime.datetime(1970, 10, 25, 3, 0, 0)) + tzs.add("rrule", {"freq": "yearly", "bymonth": 10, "byday": "-1su"}) + tzs.add("TZOFFSETFROM", datetime.timedelta(hours=2)) + tzs.add("TZOFFSETTO", datetime.timedelta(hours=1)) + + tzd = icalendar.TimezoneDaylight() + tzd.add("tzname", "CEST") + tzd.add("dtstart", datetime.datetime(1970, 3, 29, 2, 0, 0)) + tzs.add("rrule", {"freq": "yearly", "bymonth": 3, "byday": "-1su"}) + tzd.add("TZOFFSETFROM", datetime.timedelta(hours=1)) + tzd.add("TZOFFSETTO", datetime.timedelta(hours=2)) + + tzc.add_component(tzs) + tzc.add_component(tzd) + cal.add_component(tzc) + + event = icalendar.Event() + event.add( + "dtstart", + tzp.localize(datetime.datetime(2012, 2, 13, 10, 00, 00), "Europe/Vienna"), + ) + event.add( + "dtend", + tzp.localize(datetime.datetime(2012, 2, 17, 18, 00, 00), "Europe/Vienna"), + ) + event.add( + "dtstamp", + tzp.localize(datetime.datetime(2010, 10, 10, 10, 10, 10), "Europe/Vienna"), + ) + event.add( + "created", + tzp.localize(datetime.datetime(2010, 10, 10, 10, 10, 10), "Europe/Vienna"), + ) + event.add("uid", "123456") + event.add( + "last-modified", + tzp.localize(datetime.datetime(2010, 10, 10, 10, 10, 10), "Europe/Vienna"), + ) + event.add("summary", "artsprint 2012") + # event.add('rrule', 'FREQ=YEARLY;INTERVAL=1;COUNT=10') + event.add("description", "sprinting at the artsprint") + event.add("location", "aka bild, wien") + event.add("categories", "first subject") + event.add("categories", "second subject") + event.add("attendee", "häns") + event.add("attendee", "franz") + event.add("attendee", "sepp") + event.add("contact", "Max Mustermann, 1010 Wien") + event.add("url", "https://plone.org") + cal.add_component(event) + + test_out = b"|".join(cal.to_ical().splitlines()) + test_out = test_out.decode("utf-8") + + vtimezone_lines = "BEGIN:VTIMEZONE|TZID:Europe/Vienna|X-LIC-LOCATION:" + "Europe/Vienna|BEGIN:STANDARD|DTSTART:19701025T03" + "0000|RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10|RRULE:FREQ=YEARLY;B" + "YDAY=-1SU;BYMONTH=3|TZNAME:CET|TZOFFSETFROM:+0200|TZOFFSETTO:+01" + "00|END:STANDARD|BEGIN:DAYLIGHT|DTSTART:19700329T" + "020000|TZNAME:CEST|TZOFFSETFROM:+0100|TZOFFSETTO:+0200|END:DAYLI" + "GHT|END:VTIMEZONE" + assert vtimezone_lines in test_out + + test_str = "DTSTART;TZID=Europe/Vienna:20120213T100000" + assert test_str in test_out + assert "ATTENDEE:sepp" in test_out + + # ical standard expects DTSTAMP and CREATED in UTC + assert "DTSTAMP:20101010T081010Z" in test_out + assert "CREATED:20101010T081010Z" in test_out + + +def test_tzinfo_dateutil(): + """Test for issues #77, #63 + references: #73,7430b66862346fe3a6a100ab25e35a8711446717 + """ + date = dateutil.parser.parse("2012-08-30T22:41:00Z") + date2 = dateutil.parser.parse("2012-08-30T22:41:00 +02:00") + assert date.tzinfo.__module__.startswith("dateutil.tz") + assert date2.tzinfo.__module__.startswith("dateutil.tz") + + # make sure, it's parsed properly and doesn't throw an error + assert icalendar.vDDDTypes(date).to_ical() == b"20120830T224100Z" + assert icalendar.vDDDTypes(date2).to_ical() == b"20120830T224100" + + +def test_create_america_new_york(calendars, tzp): + """testing America/New_York, the most complex example from the RFC""" + cal = calendars.america_new_york + dt = cal.events[0].start + assert tzid_from_dt(dt) in ("custom_America/New_York", "EDT") + +def test_create_america_new_york_forward_reference(calendars, tzp): + """testing America/New_York variant with VTIMEZONE as a forward reference""" + cal = calendars.america_new_york_forward_reference + dt = cal.walk('VEVENT')[0]['DTSTART'][0].dt + assert tzid_from_dt(dt) in ('custom_America/New_York_Forward_reference', "EDT") + +def test_america_new_york_with_pytz(calendars, tzp, pytz_only): + """Create a custom timezone with pytz and test the transition times.""" + print(tzp) + cal = calendars.america_new_york + dt = cal.events[0].start + tz = dt.tzinfo + tz_new_york = tzp.timezone("America/New_York") + # for reasons (tm) the locally installed version of the timezone + # database isn't always complete, therefore we only compare some + # transition times + ny_transition_times = [] + ny_transition_info = [] + for num, date in enumerate(tz_new_york._utc_transition_times): + if ( + datetime.datetime(1967, 4, 30, 7, 0) + <= date + <= datetime.datetime(2037, 11, 1, 6, 0) + ): + ny_transition_times.append(date) + ny_transition_info.append(tz_new_york._transition_info[num]) + assert tz._utc_transition_times[:142] == ny_transition_times + assert tz._transition_info[0:142] == ny_transition_info + assert ( + datetime.timedelta(-1, 72000), + datetime.timedelta(0, 3600), + "EDT", + ) in tz._tzinfos.keys() + assert ( + datetime.timedelta(-1, 68400), + datetime.timedelta(0), + "EST", + ) in tz._tzinfos.keys() + + +fiji_transition_times = [ + datetime.datetime(1915, 10, 25, 12, 4), + datetime.datetime(1998, 10, 31, 14, 0), + datetime.datetime(1999, 2, 27, 14, 0), + datetime.datetime(1999, 11, 6, 14, 0), + datetime.datetime(2000, 2, 26, 14, 0), + datetime.datetime(2009, 11, 28, 14, 0), + datetime.datetime(2010, 3, 27, 14, 0), + datetime.datetime(2010, 10, 23, 14, 0), + datetime.datetime(2011, 3, 5, 14, 0), + datetime.datetime(2011, 10, 22, 14, 0), + datetime.datetime(2012, 1, 21, 14, 0), + datetime.datetime(2012, 10, 20, 14, 0), + datetime.datetime(2013, 1, 19, 14, 0), + datetime.datetime(2013, 10, 26, 14, 0), + datetime.datetime(2014, 1, 18, 13, 0), + datetime.datetime(2014, 10, 25, 14, 0), + datetime.datetime(2015, 1, 17, 13, 0), + datetime.datetime(2015, 10, 24, 14, 0), + datetime.datetime(2016, 1, 23, 13, 0), + datetime.datetime(2016, 10, 22, 14, 0), + datetime.datetime(2017, 1, 21, 13, 0), + datetime.datetime(2017, 10, 21, 14, 0), + datetime.datetime(2018, 1, 20, 13, 0), + datetime.datetime(2018, 10, 20, 14, 0), + datetime.datetime(2019, 1, 19, 13, 0), + datetime.datetime(2019, 10, 26, 14, 0), + datetime.datetime(2020, 1, 18, 13, 0), + datetime.datetime(2020, 10, 24, 14, 0), + datetime.datetime(2021, 1, 23, 13, 0), + datetime.datetime(2021, 10, 23, 14, 0), + datetime.datetime(2022, 1, 22, 13, 0), + datetime.datetime(2022, 10, 22, 14, 0), + datetime.datetime(2023, 1, 21, 13, 0), + datetime.datetime(2023, 10, 21, 14, 0), + datetime.datetime(2024, 1, 20, 13, 0), + datetime.datetime(2024, 10, 26, 14, 0), + datetime.datetime(2025, 1, 18, 13, 0), + datetime.datetime(2025, 10, 25, 14, 0), + datetime.datetime(2026, 1, 17, 13, 0), + datetime.datetime(2026, 10, 24, 14, 0), + datetime.datetime(2027, 1, 23, 13, 0), + datetime.datetime(2027, 10, 23, 14, 0), + datetime.datetime(2028, 1, 22, 13, 0), + datetime.datetime(2028, 10, 21, 14, 0), + datetime.datetime(2029, 1, 20, 13, 0), + datetime.datetime(2029, 10, 20, 14, 0), + datetime.datetime(2030, 1, 19, 13, 0), + datetime.datetime(2030, 10, 26, 14, 0), + datetime.datetime(2031, 1, 18, 13, 0), + datetime.datetime(2031, 10, 25, 14, 0), + datetime.datetime(2032, 1, 17, 13, 0), + datetime.datetime(2032, 10, 23, 14, 0), + datetime.datetime(2033, 1, 22, 13, 0), + datetime.datetime(2033, 10, 22, 14, 0), + datetime.datetime(2034, 1, 21, 13, 0), + datetime.datetime(2034, 10, 21, 14, 0), + datetime.datetime(2035, 1, 20, 13, 0), + datetime.datetime(2035, 10, 20, 14, 0), + datetime.datetime(2036, 1, 19, 13, 0), + datetime.datetime(2036, 10, 25, 14, 0), + datetime.datetime(2037, 1, 17, 13, 0), + datetime.datetime(2037, 10, 24, 14, 0), + datetime.datetime(2038, 1, 23, 13, 0), + datetime.datetime(2038, 10, 23, 14, 0), +] + +fiji_transition_info = ( + [ + ( + datetime.timedelta(0, 43200), + datetime.timedelta(0), + "custom_Pacific/Fiji_19151026T000000_+115544_+1200", + ) + ] + + 3 + * [ + ( + datetime.timedelta(0, 46800), + datetime.timedelta(0, 3600), + "custom_Pacific/Fiji_19981101T020000_+1200_+1300", + ), + ( + datetime.timedelta(0, 43200), + datetime.timedelta(0), + "custom_Pacific/Fiji_19990228T030000_+1300_+1200", + ), + ] + + 3 + * [ + ( + datetime.timedelta(0, 46800), + datetime.timedelta(0, 3600), + "custom_Pacific/Fiji_20101024T020000_+1200_+1300", + ), + ( + datetime.timedelta(0, 43200), + datetime.timedelta(0), + "custom_Pacific/Fiji_19990228T030000_+1300_+1200", + ), + ] + + 25 + * [ + ( + datetime.timedelta(0, 46800), + datetime.timedelta(0, 3600), + "custom_Pacific/Fiji_20101024T020000_+1200_+1300", + ), + ( + datetime.timedelta(0, 43200), + datetime.timedelta(0), + "custom_Pacific/Fiji_20140119T020000_+1300_+1200", + ), + ] + + [ + ( + datetime.timedelta(0, 46800), + datetime.timedelta(0, 3600), + "custom_Pacific/Fiji_20101024T020000_+1200_+1300", + ) + ] +) + + +def test_create_pacific_fiji(calendars, pytz_only): + """testing Pacific/Fiji, another pretty complex example with more than + one RDATE property per subcomponent""" + cal = calendars.pacific_fiji + + tz = cal.walk("VEVENT")[0]["DTSTART"][0].dt.tzinfo + assert str(tz) == "custom_Pacific/Fiji" + assert tz._utc_transition_times == fiji_transition_times + assert tz._transition_info == fiji_transition_info + assert ( + datetime.timedelta(0, 46800), + datetime.timedelta(0, 3600), + "custom_Pacific/Fiji_19981101T020000_+1200_+1300", + ) in tz._tzinfos.keys() + assert ( + datetime.timedelta(0, 43200), + datetime.timedelta(0), + "custom_Pacific/Fiji_19990228T030000_+1300_+1200", + ) in tz._tzinfos.keys() + + +# these are the expected offsets before and after the fiji_transition_times +fiji_expected_offsets = [ + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), + (datetime.timedelta(hours=13), datetime.timedelta(hours=13)), + (datetime.timedelta(hours=12), datetime.timedelta(hours=12)), +] + + +def test_transition_times_fiji(tzp, timezones): + """The transition times are computed.""" + tz = timezones.pacific_fiji.to_tz(tzp, lookup_tzid=False) + offsets = [] # [(before, after), ...] + for i, transition_time in enumerate(fiji_transition_times): + before_after_offset = [] + for offset in (datetime.timedelta(hours=-1), datetime.timedelta(hours=+1)): + time_in_timezone = tzp.localize(transition_time + offset, tz) + utc_offset = time_in_timezone.utcoffset() + before_after_offset.append(utc_offset) + offsets.append(tuple(before_after_offset)) + assert offsets == fiji_expected_offsets + + +def test_same_start_date(calendars): + """testing if we can handle VTIMEZONEs whose different components + have the same start DTIMEs.""" + cal = calendars.timezone_same_start + d = cal.subcomponents[1]["DTSTART"].dt + assert d.strftime("%c") == "Fri Feb 24 12:00:00 2017" + + +def test_same_start_date_and_offset(calendars): + """testing if we can handle VTIMEZONEs whose different components + have the same DTSTARTs, TZOFFSETFROM, and TZOFFSETTO.""" + cal = calendars.timezone_same_start_and_offset + d = cal.subcomponents[1]["DTSTART"].dt + assert d.strftime("%c") == "Fri Feb 24 12:00:00 2017" + + +def test_rdate(calendars): + """testing if we can handle VTIMEZONEs who only have an RDATE, not RRULE""" + cal = calendars.timezone_rdate + vevent = cal.walk("VEVENT")[0] + assert tzid_from_dt(vevent["DTSTART"].dt) in ("posix/Europe/Vaduz", "CET") + + +def test_rdate_pytz(calendars, pytz_only): + """testing if we can handle VTIMEZONEs who only have an RDATE, not RRULE""" + cal = calendars.timezone_rdate + vevent = cal.walk("VEVENT")[0] + tz = vevent["DTSTART"].dt.tzinfo + assert tz._utc_transition_times[:6] == [ + datetime.datetime(1901, 12, 13, 20, 45, 38), + datetime.datetime(1941, 5, 5, 0, 0, 0), + datetime.datetime(1941, 10, 6, 0, 0, 0), + datetime.datetime(1942, 5, 4, 0, 0, 0), + datetime.datetime(1942, 10, 5, 0, 0, 0), + datetime.datetime(1981, 3, 29, 1, 0), + ] + assert tz._transition_info[:6] == [ + (datetime.timedelta(0, 3600), datetime.timedelta(0), "CET"), + (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), "CEST"), + (datetime.timedelta(0, 3600), datetime.timedelta(0), "CET"), + (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), "CEST"), + (datetime.timedelta(0, 3600), datetime.timedelta(0), "CET"), + (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), "CEST"), + ] diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_unit_cal.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_unit_cal.py new file mode 100644 index 0000000..84e8d39 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_unit_cal.py @@ -0,0 +1,489 @@ +import itertools +import re +from datetime import datetime, timedelta + +import pytest + +import icalendar +from icalendar import prop +from icalendar.cal import Calendar, Component, Event +from icalendar.prop import tzid_from_dt + + +def test_cal_Component(calendar_component): + """A component is like a dictionary with extra methods and attributes.""" + assert calendar_component + assert calendar_component.is_empty() + + +def test_nonempty_calendar_component(calendar_component): + """Every key defines a property.A property can consist of either a + single item. This can be set with a single value... + """ + calendar_component["prodid"] = "-//max m//icalendar.mxm.dk/" + assert not calendar_component.is_empty() + assert calendar_component == Calendar({"PRODID": "-//max m//icalendar.mxm.dk/"}) + + # or with a list + calendar_component["ATTENDEE"] = ["Max M", "Rasmussen"] + assert calendar_component == Calendar( + {"ATTENDEE": ["Max M", "Rasmussen"], "PRODID": "-//max m//icalendar.mxm.dk/"} + ) + + +def test_add_multiple_values(event_component): + """add multiple values to a property. + + If you use the add method you don't have to considder if a value is + a list or not. + """ + # add multiple values at once + event_component.add("attendee", ["test@test.com", "test2@test.com"]) + + # or add one per line + event_component.add("attendee", "maxm@mxm.dk") + event_component.add("attendee", "test@example.dk") + + # add again multiple values at once to very concatenaton of lists + event_component.add("attendee", ["test3@test.com", "test4@test.com"]) + + assert event_component == Event( + { + "ATTENDEE": [ + prop.vCalAddress("test@test.com"), + prop.vCalAddress("test2@test.com"), + prop.vCalAddress("maxm@mxm.dk"), + prop.vCalAddress("test@example.dk"), + prop.vCalAddress("test3@test.com"), + prop.vCalAddress("test4@test.com"), + ] + } + ) + + +def test_get_content_directly(c): + """You can get the values back directly ...""" + c.add("prodid", "-//my product//") + assert c["prodid"] == prop.vText("-//my product//") + # ... or decoded to a python type + assert c.decoded("prodid") == b"-//my product//" + + +def test_get_default_value(c): + """With default values for non existing properties""" + assert c.decoded("version", "No Version") == "No Version" + + +def test_default_list_example(c): + c.add("rdate", [datetime(2013, 3, 28), datetime(2013, 3, 27)]) + assert isinstance(c.decoded("rdate"), prop.vDDDLists) + + +def test_render_component(calendar_component): + """The component can render itself in the RFC 5545 format.""" + calendar_component.add("attendee", "Max M") + assert ( + calendar_component.to_ical() + == b"BEGIN:VCALENDAR\r\nATTENDEE:Max M\r\nEND:VCALENDAR\r\n" + ) + + +def test_nested_component_event_ics(filled_event_component): + """Check the ical string of the event component.""" + assert filled_event_component.to_ical() == ( + b"BEGIN:VEVENT\r\nDTEND:20000102T000000\r\n" + + b"DTSTART:20000101T000000\r\nSUMMARY:A brief history of time\r" + + b"\nEND:VEVENT\r\n" + ) + + +def test_nested_components(calendar_component, filled_event_component): + """Components can be nested, so You can add a subcomponent. Eg a calendar + holds events.""" + self.assertEqual( + calendar_component.subcomponents, + [ + Event( + { + "DTEND": "20000102T000000", + "DTSTART": "20000101T000000", + "SUMMARY": "A brief history of time", + } + ) + ], + ) + + +def test_walk_filled_calendar_component(calendar_component, filled_event_component): + """We can walk over nested componentes with the walk method.""" + assert [i.name for i in calendar_component.walk()] == ["VCALENDAR", "VEVENT"] + + +def test_filter_walk(calendar_component, filled_event_component): + """We can also just walk over specific component types, by filtering + them on their name.""" + assert [i.name for i in calendar_component.walk("VEVENT")] == ["VEVENT"] + assert [i["dtstart"] for i in calendar_component.walk("VEVENT")] == [ + "20000101T000000" + ] + + +def test_recursive_property_items(calendar_component, filled_event_component): + """We can enumerate property items recursively with the property_items + method.""" + calendar_component.add("attendee", "Max M") + assert calendar_component.property_items() == [ + ("BEGIN", b"VCALENDAR"), + ("ATTENDEE", prop.vCalAddress("Max M")), + ("BEGIN", b"VEVENT"), + ("DTEND", "20000102T000000"), + ("DTSTART", "20000101T000000"), + ("SUMMARY", "A brief history of time"), + ("END", b"VEVENT"), + ("END", b"VCALENDAR"), + ] + + +def test_flat_property_items(calendar_component, filled_event_component): + """We can also enumerate property items just under the component.""" + assert calendar_component.property_items(recursive=False) == [ + ("BEGIN", b"VCALENDAR"), + ("ATTENDEE", prop.vCalAddress("Max M")), + ("END", b"VCALENDAR"), + ] + + +def test_flat_property_items(filled_event_component): + """Flat enumeration on the event.""" + assert filled_event_component.property_items(recursive=False) == [ + ("BEGIN", b"VEVENT"), + ("DTEND", "20000102T000000"), + ("DTSTART", "20000101T000000"), + ("SUMMARY", "A brief history of time"), + ("END", b"VEVENT"), + ] + + +def test_indent(): + """Text fields which span multiple mulitple lines require proper indenting""" + c = Calendar() + c["description"] = "Paragraph one\n\nParagraph two" + assert c.to_ical() == ( + b"BEGIN:VCALENDAR\r\nDESCRIPTION:Paragraph one\\n\\nParagraph two" + + b"\r\nEND:VCALENDAR\r\n" + ) + + +def test_INLINE_properties(calendar_with_resources): + """INLINE properties have their values on one property line. Note the + double quoting of the value with a colon in it. + """ + assert calendar_with_resources == Calendar( + {"RESOURCES": 'Chair, Table, "Room: 42"'} + ) + assert calendar_with_resources.to_ical() == ( + b'BEGIN:VCALENDAR\r\nRESOURCES:Chair\\, Table\\, "Room: 42"\r\n' + + b"END:VCALENDAR\r\n" + ) + + +def test_get_inline(calendar_with_resources): + """The inline values must be handled by the get_inline() and + set_inline() methods. + """ + assert calendar_with_resources.get_inline("resources", decode=0) == [ + "Chair", + "Table", + "Room: 42", + ] + + +def test_get_inline_decoded(calendar_with_resources): + """These can also be decoded""" + assert calendar_with_resources.get_inline("resources", decode=1) == [ + b"Chair", + b"Table", + b"Room: 42", + ] + + +def test_set_inline(calendar_with_resources): + """You can set them directly ...""" + calendar_with_resources.set_inline( + "resources", ["A", "List", "of", "some, recources"], encode=1 + ) + assert calendar_with_resources["resources"] == 'A,List,of,"some, recources"' + assert calendar_with_resources.get_inline("resources", decode=0) == [ + "A", + "List", + "of", + "some, recources", + ] + + +def test_inline_free_busy_inline(c): + c["freebusy"] = ( + "19970308T160000Z/PT3H,19970308T200000Z/PT1H," + + "19970308T230000Z/19970309T000000Z" + ) + assert c.get_inline("freebusy", decode=0) == [ + "19970308T160000Z/PT3H", + "19970308T200000Z/PT1H", + "19970308T230000Z/19970309T000000Z", + ] + + freebusy = c.get_inline("freebusy", decode=1) + assert isinstance(freebusy[0][0], datetime) + assert isinstance(freebusy[0][1], timedelta) + + +def test_cal_Component_add(comp, tzp): + """Test the for timezone correctness: dtstart should preserve it's + timezone, created, dtstamp and last-modified must be in UTC. + """ + comp.add("dtstart", tzp.localize(datetime(2010, 10, 10, 10, 0, 0), "Europe/Vienna")) + comp.add("created", datetime(2010, 10, 10, 12, 0, 0)) + comp.add("dtstamp", tzp.localize(datetime(2010, 10, 10, 14, 0, 0), "Europe/Vienna")) + comp.add("last-modified", tzp.localize_utc(datetime(2010, 10, 10, 16, 0, 0))) + + lines = comp.to_ical().splitlines() + assert b"DTSTART;TZID=Europe/Vienna:20101010T100000" in lines + assert b"CREATED:20101010T120000Z" in lines + assert b"DTSTAMP:20101010T120000Z" in lines + assert b"LAST-MODIFIED:20101010T160000Z" in lines + + +def test_cal_Component_add_no_reencode(comp): + """Already encoded values should not be re-encoded.""" + comp.add("ATTACH", "me") + comp.add("ATTACH", "you", encode=False) + binary = prop.vBinary("us") + comp.add("ATTACH", binary) + + assert comp["ATTACH"] == ["me", "you", binary] + + +def test_cal_Component_add_property_parameter(comp): + """Test the for timezone correctness: dtstart should preserve it's + timezone, crated, dtstamp and last-modified must be in UTC. + """ + comp.add("X-TEST-PROP", "tryout.", parameters={"prop1": "val1", "prop2": "val2"}) + lines = comp.to_ical().splitlines() + assert b"X-TEST-PROP;PROP1=val1;PROP2=val2:tryout." in lines + + +comp_prop = pytest.mark.parametrize( + "component_name, property_name", + [ + ("VEVENT", "DTSTART"), + ("VEVENT", "DTEND"), + ("VEVENT", "RECURRENCE-ID"), + ("VTODO", "DUE"), + ], +) + + +@comp_prop +def test_cal_Component_from_ical(component_name, property_name, tzp): + """Check for proper handling of TZID parameter of datetime properties""" + component_str = "BEGIN:" + component_name + "\n" + component_str += property_name + ";TZID=America/Denver:" + component_str += "20120404T073000\nEND:" + component_name + component = Component.from_ical(component_str) + assert tzid_from_dt(component[property_name].dt) == "America/Denver" + + +@comp_prop +def test_cal_Component_from_ical_2(component_name, property_name, tzp): + """Check for proper handling of TZID parameter of datetime properties""" + component_str = "BEGIN:" + component_name + "\n" + component_str += property_name + ":" + component_str += "20120404T073000\nEND:" + component_name + component = Component.from_ical(component_str) + assert component[property_name].dt.tzinfo == None + + +def test_cal_Component_to_ical_property_order(): + component_str = [ + b"BEGIN:VEVENT", + b"DTSTART:19970714T170000Z", + b"DTEND:19970715T035959Z", + b"SUMMARY:Bastille Day Party", + b"END:VEVENT", + ] + component = Component.from_ical(b"\r\n".join(component_str)) + + sorted_str = component.to_ical().splitlines() + assert sorted_str != component_str + assert set(sorted_str) == set(component_str) + + preserved_str = component.to_ical(sorted=False).splitlines() + assert preserved_str == component_str + + +def test_cal_Component_to_ical_parameter_order(): + component_str = [ + b"BEGIN:VEVENT", + b"X-FOOBAR;C=one;A=two;B=three:helloworld.", + b"END:VEVENT", + ] + component = Component.from_ical(b"\r\n".join(component_str)) + + sorted_str = component.to_ical().splitlines() + assert sorted_str[0] == component_str[0] + assert sorted_str[1] == b"X-FOOBAR;A=two;B=three;C=one:helloworld." + assert sorted_str[2] == component_str[2] + + preserved_str = component.to_ical(sorted=False).splitlines() + assert preserved_str == component_str + + +@pytest.fixture +def repr_example(c): + class ReprExample: + component = c + component["key1"] = "value1" + calendar = Calendar() + calendar["key1"] = "value1" + event = Event() + event["key1"] = "value1" + nested = Component(key1="VALUE1") + nested.add_component(component) + nested.add_component(calendar) + + return ReprExample + + +def test_repr_component(repr_example): + """Test correct class representation.""" + assert re.match(r"Component\({u?'KEY1': u?'value1'}\)", str(repr_example.component)) + + +def test_repr_calendar(repr_example): + assert re.match(r"VCALENDAR\({u?'KEY1': u?'value1'}\)", str(repr_example.calendar)) + + +def test_repr_event(repr_example): + assert re.match(r"VEVENT\({u?'KEY1': u?'value1'}\)", str(repr_example.event)) + + +def test_nested_components(repr_example): + """Representation of nested Components""" + repr_example.calendar.add_component(repr_example.event) + print(repr_example.nested) + assert re.match( + r"Component\({u?'KEY1': u?'VALUE1'}, " + r"Component\({u?'KEY1': u?'value1'}\), " + r"VCALENDAR\({u?'KEY1': u?'value1'}, " + r"VEVENT\({u?'KEY1': u?'value1'}\)\)\)", + str(repr_example.nested), + ) + + +def test_component_factory_VEVENT(factory): + """Check the events in the component factory""" + component = factory["VEVENT"] + event = component(dtstart="19700101") + assert event.to_ical() == b"BEGIN:VEVENT\r\nDTSTART:19700101\r\nEND:VEVENT\r\n" + + +def test_component_factory_VCALENDAR(factory): + """Check the VCALENDAR in the factory.""" + assert factory.get("VCALENDAR") == icalendar.cal.Calendar + + +def test_minimal_calendar_component_with_one_event(): + """Setting up a minimal calendar component looks like this""" + cal = Calendar() + + # Some properties are required to be compliant + cal["prodid"] = "-//My calendar product//mxm.dk//" + cal["version"] = "2.0" + + # We also need at least one subcomponent for a calendar to be compliant + event = Event() + event["summary"] = "Python meeting about calendaring" + event["uid"] = "42" + event.add("dtstart", datetime(2005, 4, 4, 8, 0, 0)) + cal.add_component(event) + assert ( + cal.subcomponents[0].to_ical() + == b"BEGIN:VEVENT\r\nSUMMARY:Python meeting about calendaring\r\n" + + b"DTSTART:20050404T080000\r\nUID:42\r\n" + + b"END:VEVENT\r\n" + ) + + +def test_calendar_with_parsing_errors_includes_all_events(calendars): + """Parsing a complete calendar from a string will silently ignore wrong + events but adding the error information to the component's 'errors' + attribute. The error in the following is the third EXDATE: it has an + empty DATE. + """ + event_descriptions = [ + e["DESCRIPTION"].to_ical() for e in calendars.parsing_error.walk("VEVENT") + ] + assert event_descriptions == [b"Perfectly OK event", b"Wrong event"] + + +def test_calendar_with_parsing_errors_has_an_error_in_one_event(calendars): + """Parsing a complete calendar from a string will silently ignore wrong + events but adding the error information to the component's 'errors' + attribute. The error in the following is the third EXDATE: it has an + empty DATE. + """ + errors = [e.errors for e in calendars.parsing_error.walk("VEVENT")] + assert errors == [[], [("EXDATE", "Expected datetime, date, or time, got: ''")]] + + +def test_cal_strict_parsing(calendars): + """If components are damaged, we raise an exception.""" + with pytest.raises(ValueError): + calendars.parsing_error_in_UTC_offset + + +def test_cal_ignore_errors_parsing(calendars, vUTCOffset_ignore_exceptions): + """If we diable the errors, we should be able to put the calendar back together.""" + assert ( + calendars.parsing_error_in_UTC_offset.to_ical() + == calendars.parsing_error_in_UTC_offset.raw_ics + ) + + +@pytest.mark.parametrize( + ("calendar", "other_calendar"), + itertools.product( + [ + "issue_156_RDATE_with_PERIOD_TZID_khal", + "issue_156_RDATE_with_PERIOD_TZID_khal_2", + "issue_178_custom_component_contains_other", + "issue_178_custom_component_inside_other", + "issue_526_calendar_with_events", + "issue_526_calendar_with_different_events", + "issue_526_calendar_with_event_subset", + ], + repeat=2, + ), +) +def test_comparing_calendars(calendars, calendar, other_calendar, tzp): + are_calendars_equal = calendars[calendar] == calendars[other_calendar] + are_calendars_actually_equal = calendar == other_calendar + assert are_calendars_equal == are_calendars_actually_equal + + +@pytest.mark.parametrize( + ("calendar", "shuffeled_calendar"), + [ + ( + "issue_526_calendar_with_events", + "issue_526_calendar_with_shuffeled_events", + ), + ], +) +def test_calendars_with_same_subcomponents_in_different_order_are_equal( + calendars, calendar, shuffeled_calendar +): + assert ( + calendars[calendar].subcomponents != calendars[shuffeled_calendar].subcomponents + ) + assert calendars[calendar] == calendars[shuffeled_calendar] diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_unit_caselessdict.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_unit_caselessdict.py new file mode 100644 index 0000000..fefbc4d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_unit_caselessdict.py @@ -0,0 +1,127 @@ +import unittest + +import icalendar + + +class TestCaselessdict(unittest.TestCase): + def test_caselessdict_canonsort_keys(self): + canonsort_keys = icalendar.caselessdict.canonsort_keys + + keys = ["DTEND", "DTSTAMP", "DTSTART", "UID", "SUMMARY", "LOCATION"] + + out = canonsort_keys(keys) + self.assertEqual( + out, ["DTEND", "DTSTAMP", "DTSTART", "LOCATION", "SUMMARY", "UID"] + ) + + out = canonsort_keys( + keys, + ( + "SUMMARY", + "DTSTART", + "DTEND", + ), + ) + self.assertEqual( + out, ["SUMMARY", "DTSTART", "DTEND", "DTSTAMP", "LOCATION", "UID"] + ) + + out = canonsort_keys( + keys, + ( + "UID", + "DTSTART", + "DTEND", + ), + ) + self.assertEqual( + out, ["UID", "DTSTART", "DTEND", "DTSTAMP", "LOCATION", "SUMMARY"] + ) + + out = canonsort_keys(keys, ("UID", "DTSTART", "DTEND", "RRULE", "EXDATE")) + self.assertEqual( + out, ["UID", "DTSTART", "DTEND", "DTSTAMP", "LOCATION", "SUMMARY"] + ) + + def test_caselessdict_canonsort_items(self): + canonsort_items = icalendar.caselessdict.canonsort_items + + d = { + "i": 7, + "c": "at", + "a": 3.5, + "l": (2, 3), + "e": [4, 5], + "n": 13, + "d": {"x": "y"}, + "r": 1.0, + } + + out = canonsort_items(d) + self.assertEqual( + out, + [ + ("a", 3.5), + ("c", "at"), + ("d", {"x": "y"}), + ("e", [4, 5]), + ("i", 7), + ("l", (2, 3)), + ("n", 13), + ("r", 1.0), + ], + ) + + out = canonsort_items(d, ("i", "c", "a")) + self.assertTrue( + out, + [ + ("i", 7), + ("c", "at"), + ("a", 3.5), + ("d", {"x": "y"}), + ("e", [4, 5]), + ("l", (2, 3)), + ("n", 13), + ("r", 1.0), + ], + ) + + def test_caselessdict_copy(self): + CaselessDict = icalendar.caselessdict.CaselessDict + + original_dict = CaselessDict(key1="val1", key2="val2") + copied_dict = original_dict.copy() + + self.assertEqual(original_dict, copied_dict) + + def test_CaselessDict(self): + CaselessDict = icalendar.caselessdict.CaselessDict + + ncd = CaselessDict(key1="val1", key2="val2") + self.assertEqual(ncd, CaselessDict({"KEY2": "val2", "KEY1": "val1"})) + + self.assertEqual(ncd["key1"], "val1") + self.assertEqual(ncd["KEY1"], "val1") + + assert ncd.has_key("key1") + + ncd["KEY3"] = "val3" + self.assertEqual(ncd["key3"], "val3") + + self.assertEqual(ncd.setdefault("key3", "FOUND"), "val3") + self.assertEqual(ncd.setdefault("key4", "NOT FOUND"), "NOT FOUND") + self.assertEqual(ncd["key4"], "NOT FOUND") + self.assertEqual(ncd.get("key1"), "val1") + self.assertEqual(ncd.get("key3", "NOT FOUND"), "val3") + self.assertEqual(ncd.get("key4", "NOT FOUND"), "NOT FOUND") + self.assertIn("key4", ncd) + + del ncd["key4"] + self.assertNotIn("key4", ncd) + + ncd.update({"key5": "val5", "KEY6": "val6", "KEY5": "val7"}) + self.assertEqual(ncd["key6"], "val6") + + keys = sorted(ncd.keys()) + self.assertEqual(keys, ["KEY1", "KEY2", "KEY3", "KEY5", "KEY6"]) diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_unit_parser_tools.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_unit_parser_tools.py new file mode 100644 index 0000000..8f3f378 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_unit_parser_tools.py @@ -0,0 +1,33 @@ +import unittest + +from icalendar.parser_tools import data_encode, to_unicode, from_unicode + + +class TestParserTools(unittest.TestCase): + def test_parser_tools_to_unicode(self): + self.assertEqual(to_unicode(b"spam"), "spam") + self.assertEqual(to_unicode("spam"), "spam") + self.assertEqual(to_unicode(b"spam"), "spam") + self.assertEqual(to_unicode(b"\xc6\xb5"), "\u01b5") + self.assertEqual(to_unicode(b"\xc6\xb5"), "\u01b5") + self.assertEqual(to_unicode(b"\xc6\xb5", encoding="ascii"), "\u01b5") + self.assertEqual(to_unicode(1), 1) + self.assertIsNone(to_unicode(None)) + + def test_parser_tools_from_unicode(self): + self.assertEqual(from_unicode("\u01b5", encoding="ascii"), b"\xc6\xb5") + + def test_parser_tools_data_encode(self): + data1 = { + "k1": "v1", + "k2": "v2", + "k3": "v3", + "li1": ["it1", "it2", {"k4": "v4", "k5": "v5"}, 123], + } + res = { + b"k3": b"v3", + b"k2": b"v2", + b"k1": b"v1", + b"li1": [b"it1", b"it2", {b"k5": b"v5", b"k4": b"v4"}, 123], + } + self.assertEqual(data_encode(data1), res) diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_unit_tools.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_unit_tools.py new file mode 100644 index 0000000..2b70a13 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_unit_tools.py @@ -0,0 +1,88 @@ +import unittest + +import pytest + +from icalendar.tools import UIDGenerator + + +class TestTools(unittest.TestCase): + def test_tools_UIDGenerator(self): + # Automatic semi-random uid + g = UIDGenerator() + uid = g.uid() + + txt = uid.to_ical() + length = 15 + 1 + 16 + 1 + 11 + self.assertEqual(len(txt), length) + self.assertIn(b"@example.com", txt) + + # You should at least insert your own hostname to be more compliant + uid = g.uid("Example.ORG") + txt = uid.to_ical() + self.assertEqual(len(txt), length) + self.assertIn(b"@Example.ORG", txt) + + # You can also insert a path or similar + uid = g.uid("Example.ORG", "/path/to/content") + txt = uid.to_ical() + self.assertEqual(len(txt), length) + self.assertIn(b"-/path/to/content@Example.ORG", txt) + + +@pytest.mark.parametrize( + ("split", "expected", "args", "kw"), + [ + # default argument host_name + ( + "@", + "example.com", + (), + {}, + ), + ("@", "example.com", ("example.com",), {}), + ("@", "example.com", (), {"host_name": "example.com"}), + # replaced host_name + ("@", "test.test", ("test.test",), {}), + ("@", "test.test", (), {"host_name": "test.test"}), + # replace unique + ( + "-", + "123@example.com", + (), + {"unique": "123"}, + ), + ( + "-", + "abc@example.com", + (), + {"unique": "abc"}, + ), + # replace host_name and unique + ( + "-", + "1234@test.icalendar", + (), + {"unique": "1234", "host_name": "test.icalendar"}, + ), + ( + "-", + "abc@test.example.com", + ("test.example.com", "abc"), + {}, + ), + ], +) +def test_uid_generator_issue_345(args, kw, split, expected): + """Issue #345 - Why is tools.UIDGenerator a class (that must be instantiated) instead of a module? + + see https://github.com/collective/icalendar/issues/345 + """ + uid = UIDGenerator.uid(*args, **kw) + assert uid.split(split)[1] == expected + + +def test_warning(): + with pytest.warns(DeprecationWarning): + UIDGenerator.uid() + with pytest.warns(DeprecationWarning): + UIDGenerator.rnd_string() diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/test_with_doctest.py b/.venv/lib/python3.9/site-packages/icalendar/tests/test_with_doctest.py new file mode 100644 index 0000000..d2050d4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/test_with_doctest.py @@ -0,0 +1,106 @@ +"""This file tests the source code provided by the documentation. + +See +- doctest documentation: https://docs.python.org/3/library/doctest.html +- Issue 443: https://github.com/collective/icalendar/issues/443 + +This file should be tests, too: + + >>> print("Hello World!") + Hello World! + +""" + +import doctest +import importlib +import os +import sys + +import pytest + +HERE = os.path.dirname(__file__) or "." +ICALENDAR_PATH = os.path.dirname(HERE) + +PYTHON_FILES = [ + "/".join((dirpath, filename)) + for dirpath, dirnames, filenames in os.walk(ICALENDAR_PATH) + for filename in filenames + if filename.lower().endswith(".py") and "fuzzing" not in dirpath +] + +MODULE_NAMES = [ + "icalendar" + + python_file[len(ICALENDAR_PATH) : -3].replace("\\", "/").replace("/", ".") + for python_file in PYTHON_FILES +] + + +def test_this_module_is_among_them(): + assert __name__ in MODULE_NAMES + + +@pytest.mark.parametrize("module_name", MODULE_NAMES) +def test_docstring_of_python_file(module_name, env_for_doctest): + """This test runs doctest on the Python module.""" + try: + module = importlib.import_module(module_name) + except ModuleNotFoundError as e: + if e.name == "pytz": + pytest.skip("pytz is not installed, skipping this module.") + raise + test_result = doctest.testmod(module, name=module_name, globs=env_for_doctest) + assert test_result.failed == 0, f"{test_result.failed} errors in {module_name}" + + +# This collection needs to exclude .tox and other subdirectories +DOCUMENTATION_PATH = os.path.join(HERE, "../../../") + +try: + DOCUMENT_PATHS = [ + os.path.join(DOCUMENTATION_PATH, subdir, filename) + for subdir in ["docs", "."] + for filename in os.listdir(os.path.join(DOCUMENTATION_PATH, subdir)) + if filename.lower().endswith(".rst") + ] +except FileNotFoundError: + raise OSError( + "Could not find the documentation - remove the build folder and try again." + ) + + +@pytest.mark.parametrize( + "filename", + [ + "README.rst", + "index.rst", + ], +) +def test_files_is_included(filename): + assert any(path.endswith(filename) for path in DOCUMENT_PATHS) + + +@pytest.mark.parametrize("document", DOCUMENT_PATHS) +def test_documentation_file(document, zoneinfo_only, env_for_doctest, tzp): + """This test runs doctest on a documentation file. + + functions are also replaced to work. + """ + try: + import pytz + except ImportError: + pytest.skip("pytz not installed, skipping this file.") + try: + # set raise_on_error to False if you wand to see the error for debug + test_result = doctest.testfile( + document, module_relative=False, globs=env_for_doctest, raise_on_error=False + ) + finally: + tzp.use_zoneinfo() + assert ( + test_result.failed == 0 + ), f"{test_result.failed} errors in {os.path.basename(document)}" + + +def test_can_import_zoneinfo(env_for_doctest): + """Allow importing zoneinfo for tests.""" + assert "zoneinfo" in sys.modules diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/timezone_ids.py b/.venv/lib/python3.9/site-packages/icalendar/tests/timezone_ids.py new file mode 100644 index 0000000..2ccdf19 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/timezone_ids.py @@ -0,0 +1,599 @@ +"""This file contains all the timezone ids that should be tested.""" + +TZIDS = [ + "Africa/Abidjan", + "Africa/Accra", + "Africa/Addis_Ababa", + "Africa/Algiers", + "Africa/Asmara", + "Africa/Asmera", + "Africa/Bamako", + "Africa/Bangui", + "Africa/Banjul", + "Africa/Bissau", + "Africa/Blantyre", + "Africa/Brazzaville", + "Africa/Bujumbura", + "Africa/Cairo", + "Africa/Casablanca", + "Africa/Ceuta", + "Africa/Conakry", + "Africa/Dakar", + "Africa/Dar_es_Salaam", + "Africa/Djibouti", + "Africa/Douala", + "Africa/El_Aaiun", + "Africa/Freetown", + "Africa/Gaborone", + "Africa/Harare", + "Africa/Johannesburg", + "Africa/Juba", + "Africa/Kampala", + "Africa/Khartoum", + "Africa/Kigali", + "Africa/Kinshasa", + "Africa/Lagos", + "Africa/Libreville", + "Africa/Lome", + "Africa/Luanda", + "Africa/Lubumbashi", + "Africa/Lusaka", + "Africa/Malabo", + "Africa/Maputo", + "Africa/Maseru", + "Africa/Mbabane", + "Africa/Mogadishu", + "Africa/Monrovia", + "Africa/Nairobi", + "Africa/Ndjamena", + "Africa/Niamey", + "Africa/Nouakchott", + "Africa/Ouagadougou", + "Africa/Porto-Novo", + "Africa/Sao_Tome", + "Africa/Timbuktu", + "Africa/Tripoli", + "Africa/Tunis", + "Africa/Windhoek", + "America/Adak", + "America/Anchorage", + "America/Anguilla", + "America/Antigua", + "America/Araguaina", + "America/Argentina/Buenos_Aires", + "America/Argentina/Catamarca", + "America/Argentina/ComodRivadavia", + "America/Argentina/Cordoba", + "America/Argentina/Jujuy", + "America/Argentina/La_Rioja", + "America/Argentina/Mendoza", + "America/Argentina/Rio_Gallegos", + "America/Argentina/Salta", + "America/Argentina/San_Juan", + "America/Argentina/San_Luis", + "America/Argentina/Tucuman", + "America/Argentina/Ushuaia", + "America/Aruba", + "America/Asuncion", + "America/Atikokan", + "America/Atka", + "America/Bahia", + "America/Bahia_Banderas", + "America/Barbados", + "America/Belem", + "America/Belize", + "America/Blanc-Sablon", + "America/Boa_Vista", + "America/Bogota", + "America/Boise", + "America/Buenos_Aires", + "America/Cambridge_Bay", + "America/Campo_Grande", + "America/Cancun", + "America/Caracas", + "America/Catamarca", + "America/Cayenne", + "America/Cayman", + "America/Chicago", + "America/Chihuahua", + "America/Ciudad_Juarez", + "America/Coral_Harbour", + "America/Cordoba", + "America/Costa_Rica", + "America/Creston", + "America/Cuiaba", + "America/Curacao", + "America/Danmarkshavn", + "America/Dawson", + "America/Dawson_Creek", + "America/Denver", + "America/Detroit", + "America/Dominica", + "America/Edmonton", + "America/Eirunepe", + "America/El_Salvador", + "America/Ensenada", + "America/Fortaleza", + "America/Fort_Nelson", + "America/Fort_Wayne", + "America/Glace_Bay", + "America/Godthab", + "America/Goose_Bay", + "America/Grand_Turk", + "America/Grenada", + "America/Guadeloupe", + "America/Guatemala", + "America/Guayaquil", + "America/Guyana", + "America/Halifax", + "America/Havana", + "America/Hermosillo", + "America/Indiana/Indianapolis", + "America/Indiana/Knox", + "America/Indiana/Marengo", + "America/Indiana/Petersburg", + "America/Indianapolis", + "America/Indiana/Tell_City", + "America/Indiana/Vevay", + "America/Indiana/Vincennes", + "America/Indiana/Winamac", + "America/Inuvik", + "America/Iqaluit", + "America/Jamaica", + "America/Jujuy", + "America/Juneau", + "America/Kentucky/Louisville", + "America/Kentucky/Monticello", + "America/Knox_IN", + "America/Kralendijk", + "America/La_Paz", + "America/Lima", + "America/Los_Angeles", + "America/Louisville", + "America/Lower_Princes", + "America/Maceio", + "America/Managua", + "America/Manaus", + "America/Marigot", + "America/Martinique", + "America/Matamoros", + "America/Mazatlan", + "America/Mendoza", + "America/Menominee", + "America/Metlakatla", + "America/Mexico_City", + "America/Miquelon", + "America/Moncton", + "America/Monterrey", + "America/Montevideo", + "America/Montreal", + "America/Montserrat", + "America/Nassau", + "America/New_York", + "America/Nipigon", + "America/Nome", + "America/Noronha", + "America/North_Dakota/Beulah", + "America/North_Dakota/Center", + "America/North_Dakota/New_Salem", + "America/Nuuk", + "America/Ojinaga", + "America/Panama", + "America/Pangnirtung", + "America/Paramaribo", + "America/Phoenix", + "America/Port-au-Prince", + "America/Porto_Acre", + "America/Port_of_Spain", + "America/Porto_Velho", + "America/Puerto_Rico", + "America/Punta_Arenas", + "America/Rainy_River", + "America/Rankin_Inlet", + "America/Recife", + "America/Regina", + "America/Resolute", + "America/Rio_Branco", + "America/Rosario", + "America/Santa_Isabel", + "America/Santarem", + "America/Santiago", + "America/Santo_Domingo", + "America/Sao_Paulo", + "America/Scoresbysund", + "America/Shiprock", + "America/Sitka", + "America/St_Barthelemy", + "America/St_Johns", + "America/St_Kitts", + "America/St_Lucia", + "America/St_Thomas", + "America/St_Vincent", + "America/Swift_Current", + "America/Tegucigalpa", + "America/Thule", + "America/Thunder_Bay", + "America/Tijuana", + "America/Toronto", + "America/Tortola", + "America/Vancouver", + "America/Virgin", + "America/Whitehorse", + "America/Winnipeg", + "America/Yakutat", + "America/Yellowknife", + "Antarctica/Casey", + "Antarctica/Davis", + "Antarctica/DumontDUrville", + "Antarctica/Macquarie", + "Antarctica/Mawson", + "Antarctica/McMurdo", + "Antarctica/Palmer", + "Antarctica/Rothera", + "Antarctica/South_Pole", + "Antarctica/Syowa", + "Antarctica/Troll", + "Antarctica/Vostok", + "Arctic/Longyearbyen", + "Asia/Aden", + "Asia/Almaty", + "Asia/Amman", + "Asia/Anadyr", + "Asia/Aqtau", + "Asia/Aqtobe", + "Asia/Ashgabat", + "Asia/Ashkhabad", + "Asia/Atyrau", + "Asia/Baghdad", + "Asia/Bahrain", + "Asia/Baku", + "Asia/Bangkok", + "Asia/Barnaul", + "Asia/Beirut", + "Asia/Bishkek", + "Asia/Brunei", + "Asia/Calcutta", + "Asia/Chita", + "Asia/Choibalsan", + "Asia/Chongqing", + "Asia/Chungking", + "Asia/Colombo", + "Asia/Dacca", + "Asia/Damascus", + "Asia/Dhaka", + "Asia/Dili", + "Asia/Dubai", + "Asia/Dushanbe", + "Asia/Famagusta", + "Asia/Gaza", + "Asia/Harbin", + "Asia/Hebron", + "Asia/Ho_Chi_Minh", + "Asia/Hong_Kong", + "Asia/Hovd", + "Asia/Irkutsk", + "Asia/Istanbul", + "Asia/Jakarta", + "Asia/Jayapura", + "Asia/Jerusalem", + "Asia/Kabul", + "Asia/Kamchatka", + "Asia/Karachi", + "Asia/Kashgar", + "Asia/Kathmandu", + "Asia/Katmandu", + "Asia/Khandyga", + "Asia/Kolkata", + "Asia/Krasnoyarsk", + "Asia/Kuala_Lumpur", + "Asia/Kuching", + "Asia/Kuwait", + "Asia/Macao", + "Asia/Macau", + "Asia/Magadan", + "Asia/Makassar", + "Asia/Manila", + "Asia/Muscat", + "Asia/Nicosia", + "Asia/Novokuznetsk", + "Asia/Novosibirsk", + "Asia/Omsk", + "Asia/Oral", + "Asia/Phnom_Penh", + "Asia/Pontianak", + "Asia/Pyongyang", + "Asia/Qatar", + "Asia/Qostanay", + "Asia/Qyzylorda", + "Asia/Rangoon", + "Asia/Riyadh", + "Asia/Saigon", + "Asia/Sakhalin", + "Asia/Samarkand", + "Asia/Seoul", + "Asia/Shanghai", + "Asia/Singapore", + "Asia/Srednekolymsk", + "Asia/Taipei", + "Asia/Tashkent", + "Asia/Tbilisi", + "Asia/Tel_Aviv", + "Asia/Thimbu", + "Asia/Thimphu", + "Asia/Tokyo", + "Asia/Tomsk", + "Asia/Ujung_Pandang", + "Asia/Ulaanbaatar", + "Asia/Ulan_Bator", + "Asia/Urumqi", + "Asia/Ust-Nera", + "Asia/Vientiane", + "Asia/Vladivostok", + "Asia/Yakutsk", + "Asia/Yangon", + "Asia/Yekaterinburg", + "Asia/Yerevan", + "Atlantic/Azores", + "Atlantic/Bermuda", + "Atlantic/Canary", + "Atlantic/Cape_Verde", + "Atlantic/Faeroe", + "Atlantic/Faroe", + "Atlantic/Jan_Mayen", + "Atlantic/Madeira", + "Atlantic/Reykjavik", + "Atlantic/South_Georgia", + "Atlantic/Stanley", + "Atlantic/St_Helena", + "Australia/ACT", + "Australia/Adelaide", + "Australia/Brisbane", + "Australia/Broken_Hill", + "Australia/Canberra", + "Australia/Currie", + "Australia/Darwin", + "Australia/Eucla", + "Australia/Hobart", + "Australia/LHI", + "Australia/Lindeman", + "Australia/Lord_Howe", + "Australia/Melbourne", + "Australia/North", + "Australia/NSW", + "Australia/Perth", + "Australia/Queensland", + "Australia/South", + "Australia/Sydney", + "Australia/Tasmania", + "Australia/Victoria", + "Australia/West", + "Australia/Yancowinna", + "Brazil/Acre", + "Brazil/DeNoronha", + "Brazil/East", + "Brazil/West", + "Canada/Atlantic", + "Canada/Central", + "Canada/Eastern", + "Canada/Mountain", + "Canada/Newfoundland", + "Canada/Pacific", + "Canada/Saskatchewan", + "Canada/Yukon", + "CET", + "Chile/Continental", + "Chile/EasterIsland", + "CST6CDT", + "Cuba", + "EET", + "Egypt", + "Eire", + "EST", + "EST5EDT", + "Etc/GMT", + "Etc/GMT+0", + "Etc/GMT-0", + "Etc/GMT0", + "Etc/GMT+1", + "Etc/GMT-1", + "Etc/GMT+10", + "Etc/GMT-10", + "Etc/GMT+11", + "Etc/GMT-11", + "Etc/GMT+12", + "Etc/GMT-12", + "Etc/GMT-13", + "Etc/GMT-14", + "Etc/GMT+2", + "Etc/GMT-2", + "Etc/GMT+3", + "Etc/GMT-3", + "Etc/GMT+4", + "Etc/GMT-4", + "Etc/GMT+5", + "Etc/GMT-5", + "Etc/GMT+6", + "Etc/GMT-6", + "Etc/GMT+7", + "Etc/GMT-7", + "Etc/GMT+8", + "Etc/GMT-8", + "Etc/GMT+9", + "Etc/GMT-9", + "Etc/Greenwich", + "Etc/UCT", + "Etc/Universal", + "Etc/UTC", + "Etc/Zulu", + "Europe/Amsterdam", + "Europe/Andorra", + "Europe/Astrakhan", + "Europe/Athens", + "Europe/Belfast", + "Europe/Belgrade", + "Europe/Berlin", + "Europe/Bratislava", + "Europe/Brussels", + "Europe/Bucharest", + "Europe/Budapest", + "Europe/Busingen", + "Europe/Chisinau", + "Europe/Copenhagen", + "Europe/Dublin", + "Europe/Gibraltar", + "Europe/Guernsey", + "Europe/Helsinki", + "Europe/Isle_of_Man", + "Europe/Istanbul", + "Europe/Jersey", + "Europe/Kaliningrad", + "Europe/Kiev", + "Europe/Kirov", + "Europe/Kyiv", + "Europe/Lisbon", + "Europe/Ljubljana", + "Europe/London", + "Europe/Luxembourg", + "Europe/Madrid", + "Europe/Malta", + "Europe/Mariehamn", + "Europe/Minsk", + "Europe/Monaco", + "Europe/Moscow", + "Europe/Nicosia", + "Europe/Oslo", + "Europe/Paris", + "Europe/Podgorica", + "Europe/Prague", + "Europe/Riga", + "Europe/Rome", + "Europe/Samara", + "Europe/San_Marino", + "Europe/Sarajevo", + "Europe/Saratov", + "Europe/Simferopol", + "Europe/Skopje", + "Europe/Sofia", + "Europe/Stockholm", + "Europe/Tallinn", + "Europe/Tirane", + "Europe/Tiraspol", + "Europe/Ulyanovsk", + "Europe/Uzhgorod", + "Europe/Vaduz", + "Europe/Vatican", + "Europe/Vienna", + "Europe/Vilnius", + "Europe/Volgograd", + "Europe/Warsaw", + "Europe/Zagreb", + "Europe/Zaporozhye", + "Europe/Zurich", + "GB", + "GB-Eire", + "GMT", + "GMT+0", + "GMT-0", + "GMT0", + "Greenwich", + "Hongkong", + "HST", + "Iceland", + "Indian/Antananarivo", + "Indian/Chagos", + "Indian/Christmas", + "Indian/Cocos", + "Indian/Comoro", + "Indian/Kerguelen", + "Indian/Mahe", + "Indian/Maldives", + "Indian/Mauritius", + "Indian/Mayotte", + "Indian/Reunion", + "Iran", + "Asia/Tehran", + "Israel", + "Jamaica", + "Japan", + "Kwajalein", + "Libya", + "MET", + "Mexico/BajaNorte", + "Mexico/BajaSur", + "Mexico/General", + "MST", + "MST7MDT", + "Navajo", + "NZ", + "NZ-CHAT", + "Pacific/Chatham", + "Pacific/Apia", + "Pacific/Auckland", + "Pacific/Bougainville", + "Pacific/Chuuk", + "Pacific/Easter", + "Pacific/Efate", + "Pacific/Enderbury", + "Pacific/Fakaofo", + "Pacific/Fiji", + "Pacific/Funafuti", + "Pacific/Galapagos", + "Pacific/Gambier", + "Pacific/Guadalcanal", + "Pacific/Guam", + "Pacific/Honolulu", + "Pacific/Johnston", + "Pacific/Kanton", + "Pacific/Kiritimati", + "Pacific/Kosrae", + "Pacific/Kwajalein", + "Pacific/Majuro", + "Pacific/Marquesas", + "Pacific/Midway", + "Pacific/Nauru", + "Pacific/Niue", + "Pacific/Norfolk", + "Pacific/Noumea", + "Pacific/Pago_Pago", + "Pacific/Palau", + "Pacific/Pitcairn", + "Pacific/Pohnpei", + "Pacific/Ponape", + "Pacific/Port_Moresby", + "Pacific/Rarotonga", + "Pacific/Saipan", + "Pacific/Samoa", + "Pacific/Tahiti", + "Pacific/Tarawa", + "Pacific/Tongatapu", + "Pacific/Truk", + "Pacific/Wake", + "Pacific/Wallis", + "Pacific/Yap", + "Poland", + "Portugal", + "PRC", + "PST8PDT", + "ROC", + "ROK", + "Singapore", + "Turkey", + "UCT", + "Universal", + "US/Alaska", + "US/Aleutian", + "US/Arizona", + "US/Central", + "US/Eastern", + "US/East-Indiana", + "US/Hawaii", + "US/Indiana-Starke", + "US/Michigan", + "US/Mountain", + "US/Pacific", + "US/Samoa", + "UTC", + "WET", + "W-SU", + "Zulu", +] diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/timezones/issue_237_brazilia_standard.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/timezones/issue_237_brazilia_standard.ics new file mode 100644 index 0000000..462f3fc --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/timezones/issue_237_brazilia_standard.ics @@ -0,0 +1,17 @@ +BEGIN:VTIMEZONE +TZID:(UTC-03:00) Brasília +BEGIN:STANDARD +TZNAME:Brasília standard +DTSTART:16010101T235959 +TZOFFSETFROM:-0200 +TZOFFSETTO:-0300 +RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=3SA;BYMONTH=2 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:Brasília daylight +DTSTART:16010101T235959 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0200 +RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SA;BYMONTH=10 +END:DAYLIGHT +END:VTIMEZONE diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/timezones/issue_53_tzid_parsed_properly.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/timezones/issue_53_tzid_parsed_properly.ics new file mode 100644 index 0000000..17b9a72 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/timezones/issue_53_tzid_parsed_properly.ics @@ -0,0 +1,19 @@ +BEGIN:VTIMEZONE +TZID:America/New_York +TZURL:http://tzurl.org/zoneinfo-outlook/America/New_York +X-LIC-LOCATION:America/New_York +BEGIN:DAYLIGHT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU +END:STANDARD +END:VTIMEZONE diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/timezones/issue_55_parse_error_on_utc_offset_with_seconds.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/timezones/issue_55_parse_error_on_utc_offset_with_seconds.ics new file mode 100644 index 0000000..09d639c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/timezones/issue_55_parse_error_on_utc_offset_with_seconds.ics @@ -0,0 +1,10 @@ +BEGIN:VTIMEZONE +TZID:America/Los Angeles +BEGIN:STANDARD +DTSTART:18831118T120702 +RDATE:18831118T120702 +TZNAME:PST +TZOFFSETFROM:-075258 +TZOFFSETTO:-0800 +END:STANDARD +END:VTIMEZONE diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/timezones/pacific_fiji.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/timezones/pacific_fiji.ics new file mode 100644 index 0000000..d5b8773 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/timezones/pacific_fiji.ics @@ -0,0 +1,42 @@ +BEGIN:VTIMEZONE +TZID:custom_Pacific/Fiji +TZURL:http://tzurl.org/zoneinfo/Pacific/Fiji +X-LIC-LOCATION:Pacific/Fiji +BEGIN:DAYLIGHT +TZOFFSETFROM:+1200 +TZOFFSETTO:+1300 +DTSTART:20101024T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYMONTHDAY=21,22,23,24,25,26,27;BYDAY=SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+1300 +TZOFFSETTO:+1200 +DTSTART:20140119T020000 +RRULE:FREQ=YEARLY;BYMONTH=1;BYMONTHDAY=18,19,20,21,22,23,24;BYDAY=SU +END:STANDARD +BEGIN:STANDARD +TZOFFSETFROM:+115544 +TZOFFSETTO:+1200 +DTSTART:19151026T000000 +RDATE:19151026T000000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETFROM:+1200 +TZOFFSETTO:+1300 +DTSTART:19981101T020000 +RDATE:19981101T020000 +RDATE:19991107T020000 +RDATE:20091129T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+1300 +TZOFFSETTO:+1200 +DTSTART:19990228T030000 +RDATE:19990228T030000 +RDATE:20000227T030000 +RDATE:20100328T030000 +RDATE:20110306T030000 +RDATE:20120122T030000 +RDATE:20130120T030000 +END:STANDARD +END:VTIMEZONE diff --git a/.venv/lib/python3.9/site-packages/icalendar/timezone/__init__.py b/.venv/lib/python3.9/site-packages/icalendar/timezone/__init__.py new file mode 100644 index 0000000..7367bc3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/timezone/__init__.py @@ -0,0 +1,27 @@ +"""This package contains all functionality for timezones.""" + +from .tzid import tzid_from_dt, tzid_from_tzinfo, tzids_from_tzinfo +from .tzp import TZP + +tzp = TZP() + + +def use_pytz(): + """Use pytz as the implementation that looks up and creates timezones.""" + tzp.use_pytz() + + +def use_zoneinfo(): + """Use zoneinfo as the implementation that looks up and creates timezones.""" + tzp.use_zoneinfo() + + +__all__ = [ + "TZP", + "tzp", + "use_pytz", + "use_zoneinfo", + "tzid_from_tzinfo", + "tzid_from_dt", + "tzids_from_tzinfo", +] diff --git a/.venv/lib/python3.9/site-packages/icalendar/timezone/equivalent_timezone_ids.py b/.venv/lib/python3.9/site-packages/icalendar/timezone/equivalent_timezone_ids.py new file mode 100644 index 0000000..d7b6dff --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/timezone/equivalent_timezone_ids.py @@ -0,0 +1,148 @@ +"""This module helps identifying the timezone ids and where they differ. + +The algorithm: We use the tzname and the utcoffset for each hour from +1970 - 2030. +We make a big map. +If they are equivalent, they are equivalent within the time that is mostly used. + +You can regenerate the information from this module. + +See also: +- https://stackoverflow.com/questions/79185519/which-timezones-are-equivalent + +Run this module: + + python -m icalendar.timezone.equivalent_timezone_ids + +""" + +from __future__ import annotations + +from collections import defaultdict +from datetime import datetime, timedelta, tzinfo +from pathlib import Path +from pprint import pprint +from typing import Callable, NamedTuple, Optional + +from pytz import AmbiguousTimeError, NonExistentTimeError +from zoneinfo import ZoneInfo, available_timezones + +START = datetime(1970, 1, 1) # noqa: DTZ001 +END = datetime(2020, 1, 1) # noqa: DTZ001 +DISTANCE_FROM_TIMEZONE_CHANGE = timedelta(hours=12) + +DTS = [] +dt = START +while dt <= END: + DTS.append(dt) + dt += timedelta( + hours=25 + ) # This must be big enough to be fast and small enough to identify the timeszones before it is the present year +del dt + + +def main( + create_timezones: list[Callable[[str], tzinfo]], + name: str, +): + """Generate a lookup table for timezone information if unknown timezones. + + We cannot create one lookup for all because they seem to be all equivalent + if we mix timezone implementations. + """ + print(create_timezones, name) + unsorted_tzids = available_timezones() + unsorted_tzids.remove("localtime") + unsorted_tzids.remove("Factory") + + class TZ(NamedTuple): + tz: tzinfo + id: str + + tzs = [ + TZ(create_timezone(tzid), tzid) + for create_timezone in create_timezones + for tzid in unsorted_tzids + ] + + def generate_tree( + tzs: list[TZ], + step: timedelta = timedelta(hours=1), + start: datetime = START, + end: datetime = END, + todo: Optional[set[str]] = None, + ) -> tuple[datetime, dict[timedelta, set[str]]] | set[str]: # should be recursive + """Generate a lookup tree.""" + if todo is None: + todo = [tz.id for tz in tzs] + print(f"{len(todo)} left to compute") + print(len(tzs)) + if len(tzs) == 0: + raise ValueError("tzs cannot be empty") + if len(tzs) == 1: + todo.remove(tzs[0].id) + return {tzs[0].id} + while start < end: + offsets: dict[timedelta, list[TZ]] = defaultdict(list) + try: + # if we are around a timezone change, we must move on + # see https://github.com/collective/icalendar/issues/776 + around_tz_change = not all( + tz.tz.utcoffset(start) + == tz.tz.utcoffset(start - DISTANCE_FROM_TIMEZONE_CHANGE) + == tz.tz.utcoffset(start + DISTANCE_FROM_TIMEZONE_CHANGE) + for tz in tzs + ) + except (NonExistentTimeError, AmbiguousTimeError): + around_tz_change = True + if around_tz_change: + start += DISTANCE_FROM_TIMEZONE_CHANGE + continue + for tz in tzs: + offsets[tz.tz.utcoffset(start)].append(tz) + if len(offsets) == 1: + start += step + continue + lookup = {} + for offset, tzs in offsets.items(): + lookup[offset] = generate_tree( + tzs=tzs, step=step, start=start + step, end=end, todo=todo + ) + return start, lookup + print(f"reached end with {len(tzs)} timezones - assuming they are equivalent.") + result = set() + for tz in tzs: + result.add(tz.id) + todo.remove(tz.id) + return result + + lookup = generate_tree(tzs, step=timedelta(hours=33)) + + file = Path(__file__).parent / f"equivalent_timezone_ids_{name}.py" + print(f"The result is written to {file}.") + print("lookup = ", end="") + pprint(lookup) + with file.open("w") as f: + f.write( + f"'''This file is automatically generated by {Path(__file__).name}'''\n" + ) + f.write("import datetime\n\n") + f.write("\nlookup = ") + pprint(lookup, stream=f) + f.write("\n\n__all__ = ['lookup']\n") + + return lookup + + +__all__ = ["main"] + +if __name__ == "__main__": + from dateutil.tz import gettz + from pytz import timezone + from zoneinfo import ZoneInfo + + # add more timezone implementations if you like + main( + [ZoneInfo, timezone, gettz], + "result", + ) diff --git a/.venv/lib/python3.9/site-packages/icalendar/timezone/equivalent_timezone_ids_result.py b/.venv/lib/python3.9/site-packages/icalendar/timezone/equivalent_timezone_ids_result.py new file mode 100644 index 0000000..cc91a78 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/timezone/equivalent_timezone_ids_result.py @@ -0,0 +1,3120 @@ +"""This file is automatically generated by equivalent_timezone_ids.py""" + +import datetime + + +lookup = ( + datetime.datetime(1970, 1, 2, 0, 0), + { + datetime.timedelta(days=-1, seconds=43200): ( + datetime.datetime(1979, 10, 2, 0, 0), + { + datetime.timedelta(days=-1, seconds=43200): ( + datetime.datetime(1993, 8, 22, 21, 0), + { + datetime.timedelta(days=-1, seconds=43200): {"Etc/GMT+12"}, + datetime.timedelta(seconds=43200): { + "Kwajalein", + "Pacific/Kwajalein", + }, + }, + ), + datetime.timedelta(days=-1, seconds=46800): { + "Pacific/Enderbury", + "Pacific/Kanton", + }, + }, + ), + datetime.timedelta(days=-1, seconds=46800): ( + datetime.datetime(1970, 4, 26, 15, 0), + { + datetime.timedelta(days=-1, seconds=46800): ( + datetime.datetime(2010, 9, 26, 15, 0), + { + datetime.timedelta(days=-1, seconds=46800): ( + datetime.datetime(2011, 12, 31, 21, 0), + { + datetime.timedelta(days=-1, seconds=46800): { + "Etc/GMT+11", + "Pacific/Midway", + "Pacific/Niue", + "Pacific/Pago_Pago", + "Pacific/Samoa", + "US/Samoa", + }, + datetime.timedelta(seconds=46800): {"Pacific/Fakaofo"}, + }, + ), + datetime.timedelta(days=-1, seconds=50400): {"Pacific/Apia"}, + }, + ), + datetime.timedelta(days=-1, seconds=50400): ( + datetime.datetime(1983, 10, 30, 21, 0), + { + datetime.timedelta(days=-1, seconds=50400): { + "America/Adak", + "America/Atka", + "US/Aleutian", + }, + datetime.timedelta(days=-1, seconds=54000): {"America/Nome"}, + }, + ), + }, + ), + datetime.timedelta(days=-1, seconds=48000): {"Pacific/Kiritimati"}, + datetime.timedelta(days=-1, seconds=48600): {"Pacific/Rarotonga"}, + datetime.timedelta(days=-1, seconds=50400): ( + datetime.datetime(1970, 4, 26, 15, 0), + { + datetime.timedelta(days=-1, seconds=50400): { + "Etc/GMT+10", + "HST", + "Pacific/Honolulu", + "Pacific/Johnston", + "Pacific/Tahiti", + "US/Hawaii", + }, + datetime.timedelta(days=-1, seconds=54000): { + "America/Anchorage", + "US/Alaska", + }, + }, + ), + datetime.timedelta(days=-1, seconds=52200): {"Pacific/Marquesas"}, + datetime.timedelta(days=-1, seconds=54000): ( + datetime.datetime(1970, 4, 26, 15, 0), + { + datetime.timedelta(days=-1, seconds=54000): ( + datetime.datetime(1973, 10, 28, 18, 0), + { + datetime.timedelta(days=-1, seconds=54000): { + "Etc/GMT+9", + "Pacific/Gambier", + }, + datetime.timedelta(days=-1, seconds=57600): {"America/Dawson"}, + }, + ), + datetime.timedelta(days=-1, seconds=57600): {"America/Yakutat"}, + }, + ), + datetime.timedelta(days=-1, seconds=55800): {"Pacific/Pitcairn"}, + datetime.timedelta(days=-1, seconds=57600): ( + datetime.datetime(1970, 4, 26, 15, 0), + { + datetime.timedelta(days=-1, seconds=57600): ( + datetime.datetime(1972, 4, 30, 21, 0), + { + datetime.timedelta(days=-1, seconds=57600): ( + datetime.datetime(1976, 4, 25, 15, 0), + { + datetime.timedelta(days=-1, seconds=57600): ( + datetime.datetime(1980, 4, 27, 15, 0), + { + datetime.timedelta(days=-1, seconds=57600): { + "Etc/GMT+8" + }, + datetime.timedelta(days=-1, seconds=61200): { + "America/Whitehorse", + "Canada/Yukon", + }, + }, + ), + datetime.timedelta(days=-1, seconds=61200): { + "America/Ensenada", + "America/Santa_Isabel", + "America/Tijuana", + "Mexico/BajaNorte", + }, + }, + ), + datetime.timedelta(days=-1, seconds=61200): {"America/Inuvik"}, + }, + ), + datetime.timedelta(days=-1, seconds=61200): ( + datetime.datetime(1972, 10, 30, 0, 0), + { + datetime.timedelta(days=-1, seconds=57600): ( + datetime.datetime(1974, 1, 6, 18, 0), + { + datetime.timedelta(days=-1, seconds=57600): ( + datetime.datetime(2015, 11, 1, 21, 0), + { + datetime.timedelta(days=-1, seconds=57600): { + "America/Vancouver", + "Canada/Pacific", + }, + datetime.timedelta(days=-1, seconds=61200): { + "America/Fort_Nelson" + }, + }, + ), + datetime.timedelta(days=-1, seconds=61200): ( + datetime.datetime(1980, 4, 28, 0, 0), + { + datetime.timedelta(days=-1, seconds=57600): { + "America/Juneau" + }, + datetime.timedelta(days=-1, seconds=61200): ( + datetime.datetime(1983, 10, 30, 21, 0), + { + datetime.timedelta( + days=-1, seconds=54000 + ): {"America/Sitka"}, + datetime.timedelta( + days=-1, seconds=57600 + ): ( + datetime.datetime( + 1984, 4, 29, 21, 0 + ), + { + datetime.timedelta( + days=-1, seconds=57600 + ): {"America/Metlakatla"}, + datetime.timedelta( + days=-1, seconds=61200 + ): { + "America/Los_Angeles", + "PST8PDT", + "US/Pacific", + }, + }, + ), + }, + ), + }, + ), + }, + ), + datetime.timedelta(days=-1, seconds=61200): { + "America/Dawson_Creek" + }, + }, + ), + }, + ), + datetime.timedelta(days=-1, seconds=61200): ( + datetime.datetime(1970, 4, 26, 15, 0), + { + datetime.timedelta(days=-1, seconds=61200): ( + datetime.datetime(1972, 4, 30, 21, 0), + { + datetime.timedelta(days=-1, seconds=61200): ( + datetime.datetime(1996, 4, 7, 15, 0), + { + datetime.timedelta(days=-1, seconds=61200): { + "America/Creston", + "America/Phoenix", + "Etc/GMT+7", + "MST", + "US/Arizona", + }, + datetime.timedelta(days=-1, seconds=64800): ( + datetime.datetime(1999, 4, 4, 15, 0), + { + datetime.timedelta(days=-1, seconds=61200): { + "America/Hermosillo" + }, + datetime.timedelta(days=-1, seconds=64800): ( + datetime.datetime(2010, 4, 5, 0, 0), + { + datetime.timedelta( + days=-1, seconds=64800 + ): { + "America/Mazatlan", + "Mexico/BajaSur", + }, + datetime.timedelta( + days=-1, seconds=68400 + ): {"America/Bahia_Banderas"}, + }, + ), + }, + ), + }, + ), + datetime.timedelta(days=-1, seconds=64800): ( + datetime.datetime(1972, 10, 29, 21, 0), + { + datetime.timedelta(days=-1, seconds=61200): ( + datetime.datetime(1999, 11, 1, 0, 0), + { + datetime.timedelta(days=-1, seconds=61200): { + "America/Edmonton", + "America/Yellowknife", + "Canada/Mountain", + }, + datetime.timedelta(days=-1, seconds=64800): { + "America/Cambridge_Bay" + }, + }, + ), + datetime.timedelta(days=-1, seconds=64800): { + "America/Swift_Current" + }, + }, + ), + }, + ), + datetime.timedelta(days=-1, seconds=64800): ( + datetime.datetime(1974, 1, 6, 18, 0), + { + datetime.timedelta(days=-1, seconds=61200): {"America/Boise"}, + datetime.timedelta(days=-1, seconds=64800): ( + datetime.datetime(1992, 10, 25, 18, 0), + { + datetime.timedelta(days=-1, seconds=61200): ( + datetime.datetime(2003, 10, 26, 15, 0), + { + datetime.timedelta(days=-1, seconds=61200): ( + datetime.datetime(2010, 11, 7, 21, 0), + { + datetime.timedelta( + days=-1, seconds=61200 + ): { + "America/Denver", + "America/Shiprock", + "MST7MDT", + "Navajo", + "US/Mountain", + }, + datetime.timedelta( + days=-1, seconds=64800 + ): {"America/North_Dakota/Beulah"}, + }, + ), + datetime.timedelta(days=-1, seconds=64800): { + "America/North_Dakota/New_Salem" + }, + }, + ), + datetime.timedelta(days=-1, seconds=64800): { + "America/North_Dakota/Center" + }, + }, + ), + }, + ), + }, + ), + datetime.timedelta(days=-1, seconds=64800): ( + datetime.datetime(1970, 3, 29, 15, 0), + { + datetime.timedelta(days=-1, seconds=61200): { + "Chile/EasterIsland", + "Pacific/Easter", + }, + datetime.timedelta(days=-1, seconds=64800): ( + datetime.datetime(1970, 4, 26, 15, 0), + { + datetime.timedelta(days=-1, seconds=64800): ( + datetime.datetime(1972, 4, 30, 21, 0), + { + datetime.timedelta(days=-1, seconds=64800): ( + datetime.datetime(1973, 5, 1, 15, 0), + { + datetime.timedelta(days=-1, seconds=64800): ( + datetime.datetime(1973, 11, 25, 18, 0), + { + datetime.timedelta( + days=-1, seconds=64800 + ): ( + datetime.datetime( + 1973, 12, 5, 21, 0 + ), + { + datetime.timedelta( + days=-1, seconds=64800 + ): ( + datetime.datetime( + 1979, 2, 26, 0, 0 + ), + { + datetime.timedelta( + days=-1, + seconds=64800, + ): ( + datetime.datetime( + 1981, + 12, + 27, + 0, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=64800, + ): ( + datetime.datetime( + 1987, + 5, + 4, + 0, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=64800, + ): ( + datetime.datetime( + 1988, + 4, + 4, + 0, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=64800, + ): ( + datetime.datetime( + 1996, + 4, + 7, + 15, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=64800, + ): { + "America/Regina", + "Canada/Saskatchewan", + "Etc/GMT+6", + }, + datetime.timedelta( + days=-1, + seconds=68400, + ): ( + datetime.datetime( + 1998, + 4, + 5, + 15, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=64800, + ): ( + datetime.datetime( + 2010, + 3, + 14, + 21, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=61200, + ): { + "America/Chihuahua" + }, + datetime.timedelta( + days=-1, + seconds=64800, + ): { + "America/Ciudad_Juarez", + "America/Ojinaga", + }, + }, + ), + datetime.timedelta( + days=-1, + seconds=68400, + ): { + "America/Mexico_City", + "Mexico/General", + }, + }, + ), + }, + ), + datetime.timedelta( + days=-1, + seconds=68400, + ): ( + datetime.datetime( + 2010, + 3, + 14, + 21, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=64800, + ): { + "America/Monterrey" + }, + datetime.timedelta( + days=-1, + seconds=68400, + ): { + "America/Matamoros" + }, + }, + ), + }, + ), + datetime.timedelta( + days=-1, + seconds=68400, + ): ( + datetime.datetime( + 2006, + 5, + 8, + 0, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=64800, + ): { + "America/El_Salvador" + }, + datetime.timedelta( + days=-1, + seconds=68400, + ): { + "America/Tegucigalpa" + }, + }, + ), + }, + ), + datetime.timedelta( + days=-1, + seconds=68400, + ): ( + datetime.datetime( + 1982, + 11, + 2, + 18, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=64800, + ): { + "America/Merida" + }, + datetime.timedelta( + days=-1, + seconds=68400, + ): { + "America/Cancun" + }, + }, + ), + }, + ), + datetime.timedelta( + days=-1, + seconds=68400, + ): { + "America/Costa_Rica" + }, + }, + ), + datetime.timedelta( + days=-1, seconds=68400 + ): {"America/Belize"}, + }, + ), + datetime.timedelta( + days=-1, seconds=68400 + ): {"America/Guatemala"}, + }, + ), + datetime.timedelta(days=-1, seconds=68400): { + "America/Managua" + }, + }, + ), + datetime.timedelta(days=-1, seconds=68400): ( + datetime.datetime(2006, 10, 29, 21, 0), + { + datetime.timedelta(days=-1, seconds=64800): { + "America/Rankin_Inlet" + }, + datetime.timedelta(days=-1, seconds=68400): { + "America/Resolute" + }, + }, + ), + }, + ), + datetime.timedelta(days=-1, seconds=68400): ( + datetime.datetime(1974, 1, 6, 18, 0), + { + datetime.timedelta(days=-1, seconds=64800): { + "America/Rainy_River", + "America/Winnipeg", + "Canada/Central", + }, + datetime.timedelta(days=-1, seconds=68400): ( + datetime.datetime(1977, 10, 31, 0, 0), + { + datetime.timedelta(days=-1, seconds=64800): ( + datetime.datetime(1991, 10, 27, 18, 0), + { + datetime.timedelta( + days=-1, seconds=64800 + ): ( + datetime.datetime( + 2000, 10, 29, 15, 0 + ), + { + datetime.timedelta( + days=-1, seconds=64800 + ): { + "America/Chicago", + "CST6CDT", + "US/Central", + }, + datetime.timedelta( + days=-1, seconds=68400 + ): { + "America/Kentucky/Monticello" + }, + }, + ), + datetime.timedelta( + days=-1, seconds=68400 + ): { + "America/Indiana/Knox", + "America/Knox_IN", + "US/Indiana-Starke", + }, + }, + ), + datetime.timedelta(days=-1, seconds=68400): { + "America/Indiana/Petersburg" + }, + }, + ), + }, + ), + }, + ), + }, + ), + datetime.timedelta(days=-1, seconds=68400): ( + datetime.datetime(1970, 4, 26, 15, 0), + { + datetime.timedelta(days=-1, seconds=68400): ( + datetime.datetime(1972, 4, 30, 21, 0), + { + datetime.timedelta(days=-1, seconds=68400): ( + datetime.datetime(1973, 4, 29, 21, 0), + { + datetime.timedelta(days=-1, seconds=68400): ( + datetime.datetime(1973, 10, 28, 21, 0), + { + datetime.timedelta(days=-1, seconds=64800): { + "America/Menominee" + }, + datetime.timedelta(days=-1, seconds=68400): ( + datetime.datetime(1974, 1, 6, 15, 0), + { + datetime.timedelta( + days=-1, seconds=68400 + ): ( + datetime.datetime( + 1979, 4, 29, 21, 0 + ), + { + datetime.timedelta( + days=-1, seconds=68400 + ): ( + datetime.datetime( + 1983, 5, 8, 18, 0 + ), + { + datetime.timedelta( + days=-1, + seconds=68400, + ): ( + datetime.datetime( + 1985, + 11, + 2, + 15, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=68400, + ): ( + datetime.datetime( + 1986, + 1, + 1, + 18, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=64800, + ): { + "Pacific/Galapagos" + }, + datetime.timedelta( + days=-1, + seconds=68400, + ): ( + datetime.datetime( + 1992, + 5, + 4, + 0, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=68400, + ): ( + datetime.datetime( + 1992, + 11, + 28, + 15, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=68400, + ): { + "America/Atikokan", + "America/Cayman", + "America/Coral_Harbour", + "America/Panama", + "EST", + "Etc/GMT+5", + }, + datetime.timedelta( + days=-1, + seconds=72000, + ): { + "America/Guayaquil" + }, + }, + ), + datetime.timedelta( + days=-1, + seconds=72000, + ): { + "America/Bogota" + }, + }, + ), + datetime.timedelta( + days=-1, + seconds=72000, + ): { + "America/Lima" + }, + }, + ), + datetime.timedelta( + days=-1, + seconds=72000, + ): ( + datetime.datetime( + 1993, + 10, + 17, + 15, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=68400, + ): { + "America/Porto_Acre", + "America/Rio_Branco", + "Brazil/Acre", + }, + datetime.timedelta( + days=-1, + seconds=72000, + ): { + "America/Eirunepe" + }, + }, + ), + }, + ), + datetime.timedelta( + days=-1, + seconds=72000, + ): { + "America/Port-au-Prince" + }, + }, + ), + datetime.timedelta( + days=-1, seconds=72000 + ): {"America/Grand_Turk"}, + }, + ), + datetime.timedelta( + days=-1, seconds=72000 + ): {"America/Jamaica", "Jamaica"}, + }, + ), + }, + ), + datetime.timedelta(days=-1, seconds=72000): { + "America/Detroit", + "US/Michigan", + }, + }, + ), + datetime.timedelta(days=-1, seconds=72000): { + "America/Iqaluit", + "America/Pangnirtung", + }, + }, + ), + datetime.timedelta(days=-1, seconds=72000): ( + datetime.datetime(1971, 4, 25, 15, 0), + { + datetime.timedelta(days=-1, seconds=68400): ( + datetime.datetime(2006, 4, 2, 15, 0), + { + datetime.timedelta(days=-1, seconds=68400): ( + datetime.datetime(2007, 3, 12, 3, 0), + { + datetime.timedelta(days=-1, seconds=68400): ( + datetime.datetime(2007, 11, 4, 15, 0), + { + datetime.timedelta( + days=-1, seconds=64800 + ): {"America/Indiana/Tell_City"}, + datetime.timedelta( + days=-1, seconds=68400 + ): {"America/Indiana/Vincennes"}, + }, + ), + datetime.timedelta(days=-1, seconds=72000): { + "America/Indiana/Winamac" + }, + }, + ), + datetime.timedelta(days=-1, seconds=72000): { + "America/Fort_Wayne", + "America/Indiana/Indianapolis", + "America/Indianapolis", + "US/East-Indiana", + }, + }, + ), + datetime.timedelta(days=-1, seconds=72000): ( + datetime.datetime(1972, 10, 8, 12, 0), + { + datetime.timedelta(days=-1, seconds=68400): { + "America/Havana", + "Cuba", + }, + datetime.timedelta(days=-1, seconds=72000): ( + datetime.datetime(1973, 4, 29, 15, 0), + { + datetime.timedelta(days=-1, seconds=68400): { + "America/Indiana/Vevay" + }, + datetime.timedelta(days=-1, seconds=72000): ( + datetime.datetime(1974, 1, 6, 18, 0), + { + datetime.timedelta( + days=-1, seconds=68400 + ): ( + datetime.datetime( + 1974, 4, 28, 15, 0 + ), + { + datetime.timedelta( + days=-1, seconds=68400 + ): ( + datetime.datetime( + 1976, 4, 26, 0, 0 + ), + { + datetime.timedelta( + days=-1, + seconds=68400, + ): { + "America/Indiana/Marengo" + }, + datetime.timedelta( + days=-1, + seconds=72000, + ): { + "America/Kentucky/Louisville", + "America/Louisville", + }, + }, + ), + datetime.timedelta( + days=-1, seconds=72000 + ): { + "America/Montreal", + "America/Nassau", + "America/Nipigon", + "America/Thunder_Bay", + "America/Toronto", + "Canada/Eastern", + }, + }, + ), + datetime.timedelta( + days=-1, seconds=72000 + ): { + "America/New_York", + "EST5EDT", + "US/Eastern", + }, + }, + ), + }, + ), + }, + ), + }, + ), + }, + ), + datetime.timedelta(days=-1, seconds=70200): {"America/Santo_Domingo"}, + datetime.timedelta(days=-1, seconds=72000): ( + datetime.datetime(1970, 4, 26, 15, 0), + { + datetime.timedelta(days=-1, seconds=72000): ( + datetime.datetime(1972, 4, 30, 21, 0), + { + datetime.timedelta(days=-1, seconds=72000): ( + datetime.datetime(1972, 10, 2, 0, 0), + { + datetime.timedelta(days=-1, seconds=72000): ( + datetime.datetime(1974, 4, 28, 21, 0), + { + datetime.timedelta(days=-1, seconds=72000): ( + datetime.datetime(1977, 6, 12, 18, 0), + { + datetime.timedelta( + days=-1, seconds=72000 + ): ( + datetime.datetime( + 1980, 4, 6, 18, 0 + ), + { + datetime.timedelta( + days=-1, seconds=72000 + ): ( + datetime.datetime( + 1980, 5, 2, 0, 0 + ), + { + datetime.timedelta( + days=-1, + seconds=72000, + ): ( + datetime.datetime( + 1983, 5, 2, 0, 0 + ), + { + datetime.timedelta( + days=-1, + seconds=72000, + ): ( + datetime.datetime( + 1985, + 11, + 2, + 18, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=72000, + ): ( + datetime.datetime( + 1991, + 3, + 31, + 18, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=72000, + ): ( + datetime.datetime( + 2007, + 12, + 10, + 0, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=70200, + ): { + "America/Caracas" + }, + datetime.timedelta( + days=-1, + seconds=72000, + ): { + "America/Anguilla", + "America/Antigua", + "America/Aruba", + "America/Blanc-Sablon", + "America/Curacao", + "America/Dominica", + "America/Grenada", + "America/Guadeloupe", + "America/Kralendijk", + "America/La_Paz", + "America/Lower_Princes", + "America/Marigot", + "America/Montserrat", + "America/Port_of_Spain", + "America/Puerto_Rico", + "America/St_Barthelemy", + "America/St_Kitts", + "America/St_Lucia", + "America/St_Thomas", + "America/St_Vincent", + "America/Tortola", + "America/Virgin", + "Etc/GMT+4", + }, + }, + ), + datetime.timedelta( + days=-1, + seconds=75600, + ): { + "America/Thule" + }, + }, + ), + datetime.timedelta( + days=-1, + seconds=75600, + ): ( + datetime.datetime( + 1988, + 10, + 16, + 15, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=72000, + ): ( + datetime.datetime( + 1993, + 10, + 17, + 15, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=72000, + ): ( + datetime.datetime( + 1999, + 10, + 3, + 18, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=72000, + ): ( + datetime.datetime( + 2008, + 6, + 24, + 15, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=72000, + ): { + "America/Porto_Velho" + }, + datetime.timedelta( + days=-1, + seconds=75600, + ): { + "America/Santarem" + }, + }, + ), + datetime.timedelta( + days=-1, + seconds=75600, + ): { + "America/Boa_Vista" + }, + }, + ), + datetime.timedelta( + days=-1, + seconds=75600, + ): { + "America/Manaus", + "Brazil/West", + }, + }, + ), + datetime.timedelta( + days=-1, + seconds=75600, + ): ( + datetime.datetime( + 2003, + 10, + 19, + 21, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=72000, + ): { + "America/Cuiaba" + }, + datetime.timedelta( + days=-1, + seconds=75600, + ): { + "America/Campo_Grande" + }, + }, + ), + }, + ), + }, + ), + datetime.timedelta( + days=-1, + seconds=75600, + ): { + "Atlantic/Stanley" + }, + }, + ), + datetime.timedelta( + days=-1, + seconds=75600, + ): {"America/Miquelon"}, + }, + ), + datetime.timedelta( + days=-1, seconds=75600 + ): {"America/Martinique"}, + }, + ), + datetime.timedelta( + days=-1, seconds=75600 + ): {"America/Barbados"}, + }, + ), + datetime.timedelta(days=-1, seconds=75600): { + "Atlantic/Bermuda" + }, + }, + ), + datetime.timedelta(days=-1, seconds=75600): { + "America/Asuncion" + }, + }, + ), + datetime.timedelta(days=-1, seconds=75600): { + "America/Glace_Bay" + }, + }, + ), + datetime.timedelta(days=-1, seconds=75600): ( + datetime.datetime(1973, 4, 30, 0, 0), + { + datetime.timedelta(days=-1, seconds=72000): {"America/Moncton"}, + datetime.timedelta(days=-1, seconds=75600): ( + datetime.datetime(1988, 4, 3, 15, 0), + { + datetime.timedelta(days=-1, seconds=75600): { + "America/Halifax", + "Canada/Atlantic", + }, + datetime.timedelta(days=-1, seconds=79200): { + "America/Goose_Bay" + }, + }, + ), + }, + ), + }, + ), + datetime.timedelta(days=-1, seconds=72900): {"America/Guyana"}, + datetime.timedelta(days=-1, seconds=73800): ( + datetime.datetime(1970, 4, 26, 15, 0), + { + datetime.timedelta(days=-1, seconds=73800): {"America/Paramaribo"}, + datetime.timedelta(days=-1, seconds=77400): { + "America/St_Johns", + "Canada/Newfoundland", + }, + }, + ), + datetime.timedelta(days=-1, seconds=75600): ( + datetime.datetime(1970, 3, 29, 15, 0), + { + datetime.timedelta(days=-1, seconds=72000): ( + datetime.datetime(2017, 5, 14, 18, 0), + { + datetime.timedelta(days=-1, seconds=72000): { + "America/Santiago", + "Chile/Continental", + }, + datetime.timedelta(days=-1, seconds=75600): { + "America/Punta_Arenas" + }, + }, + ), + datetime.timedelta(days=-1, seconds=75600): ( + datetime.datetime(1970, 4, 25, 18, 0), + { + datetime.timedelta(days=-1, seconds=75600): ( + datetime.datetime(1974, 1, 23, 21, 0), + { + datetime.timedelta(days=-1, seconds=75600): ( + datetime.datetime(1980, 4, 7, 0, 0), + { + datetime.timedelta(days=-1, seconds=75600): ( + datetime.datetime(1985, 11, 3, 0, 0), + { + datetime.timedelta( + days=-1, seconds=75600 + ): {"America/Cayenne", "Etc/GMT+3"}, + datetime.timedelta( + days=-1, seconds=79200 + ): ( + datetime.datetime( + 1988, 10, 16, 15, 0 + ), + { + datetime.timedelta( + days=-1, seconds=75600 + ): {"America/Belem"}, + datetime.timedelta( + days=-1, seconds=79200 + ): ( + datetime.datetime( + 1990, 10, 22, 0, 0 + ), + { + datetime.timedelta( + days=-1, + seconds=75600, + ): ( + datetime.datetime( + 1995, + 10, + 15, + 15, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=75600, + ): ( + datetime.datetime( + 2000, + 10, + 15, + 12, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=75600, + ): { + "America/Recife" + }, + datetime.timedelta( + days=-1, + seconds=79200, + ): { + "America/Fortaleza" + }, + }, + ), + datetime.timedelta( + days=-1, + seconds=79200, + ): ( + datetime.datetime( + 1996, + 10, + 6, + 18, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=75600, + ): { + "America/Maceio" + }, + datetime.timedelta( + days=-1, + seconds=79200, + ): { + "America/Araguaina" + }, + }, + ), + }, + ), + datetime.timedelta( + days=-1, + seconds=79200, + ): ( + datetime.datetime( + 2003, + 10, + 19, + 21, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=75600, + ): { + "America/Bahia" + }, + datetime.timedelta( + days=-1, + seconds=79200, + ): { + "America/Sao_Paulo", + "Brazil/East", + }, + }, + ), + }, + ), + }, + ), + }, + ), + datetime.timedelta(days=-1, seconds=79200): ( + datetime.datetime(1996, 1, 2, 0, 0), + { + datetime.timedelta( + days=-1, seconds=75600 + ): {"America/Godthab", "America/Nuuk"}, + datetime.timedelta(0): { + "America/Danmarkshavn" + }, + }, + ), + }, + ), + datetime.timedelta(days=-1, seconds=79200): ( + datetime.datetime(1982, 5, 1, 21, 0), + { + datetime.timedelta(days=-1, seconds=72000): { + "Antarctica/Palmer" + }, + datetime.timedelta(days=-1, seconds=75600): ( + datetime.datetime(1990, 3, 4, 21, 0), + { + datetime.timedelta( + days=-1, seconds=72000 + ): ( + datetime.datetime( + 1990, 10, 16, 0, 0 + ), + { + datetime.timedelta( + days=-1, seconds=72000 + ): { + "America/Argentina/Jujuy", + "America/Jujuy", + }, + datetime.timedelta( + days=-1, seconds=75600 + ): { + "America/Argentina/Mendoza", + "America/Mendoza", + }, + }, + ), + datetime.timedelta( + days=-1, seconds=75600 + ): ( + datetime.datetime( + 1991, 3, 1, 15, 0 + ), + { + datetime.timedelta( + days=-1, seconds=72000 + ): ( + datetime.datetime( + 2004, 6, 20, 18, 0 + ), + { + datetime.timedelta( + days=-1, + seconds=72000, + ): { + "America/Argentina/San_Juan" + }, + datetime.timedelta( + days=-1, + seconds=75600, + ): { + "America/Argentina/La_Rioja" + }, + }, + ), + datetime.timedelta( + days=-1, seconds=79200 + ): ( + datetime.datetime( + 1991, 3, 4, 0, 0 + ), + { + datetime.timedelta( + days=-1, + seconds=72000, + ): ( + datetime.datetime( + 2004, + 6, + 1, + 12, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=72000, + ): ( + datetime.datetime( + 2004, + 6, + 14, + 0, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=72000, + ): { + "America/Argentina/Catamarca", + "America/Argentina/ComodRivadavia", + "America/Catamarca", + }, + datetime.timedelta( + days=-1, + seconds=75600, + ): { + "America/Argentina/Tucuman" + }, + }, + ), + datetime.timedelta( + days=-1, + seconds=75600, + ): ( + datetime.datetime( + 2008, + 10, + 19, + 15, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=75600, + ): { + "America/Argentina/Salta" + }, + datetime.timedelta( + days=-1, + seconds=79200, + ): { + "America/Argentina/Cordoba", + "America/Cordoba", + "America/Rosario", + }, + }, + ), + }, + ), + datetime.timedelta( + days=-1, + seconds=75600, + ): ( + datetime.datetime( + 2004, + 5, + 30, + 15, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=72000, + ): { + "America/Argentina/Ushuaia" + }, + datetime.timedelta( + days=-1, + seconds=75600, + ): ( + datetime.datetime( + 2004, + 6, + 1, + 12, + 0, + ), + { + datetime.timedelta( + days=-1, + seconds=72000, + ): { + "America/Argentina/Rio_Gallegos" + }, + datetime.timedelta( + days=-1, + seconds=75600, + ): { + "America/Argentina/Buenos_Aires", + "America/Buenos_Aires", + }, + }, + ), + }, + ), + }, + ), + }, + ), + datetime.timedelta( + days=-1, seconds=79200 + ): {"America/Argentina/San_Luis"}, + }, + ), + }, + ), + }, + ), + datetime.timedelta(days=-1, seconds=79200): { + "America/Montevideo" + }, + }, + ), + }, + ), + datetime.timedelta(days=-1, seconds=79200): ( + datetime.datetime(1975, 11, 25, 18, 0), + { + datetime.timedelta(days=-1, seconds=79200): ( + datetime.datetime(1980, 4, 6, 21, 0), + { + datetime.timedelta(days=-1, seconds=79200): ( + datetime.datetime(1985, 11, 2, 21, 0), + { + datetime.timedelta(days=-1, seconds=79200): { + "Atlantic/South_Georgia", + "Etc/GMT+2", + }, + datetime.timedelta(days=-1, seconds=82800): { + "America/Noronha", + "Brazil/DeNoronha", + }, + }, + ), + datetime.timedelta(days=-1, seconds=82800): { + "America/Scoresbysund" + }, + }, + ), + datetime.timedelta(days=-1, seconds=82800): {"Atlantic/Cape_Verde"}, + }, + ), + datetime.timedelta(days=-1, seconds=82800): ( + datetime.datetime(1975, 1, 1, 15, 0), + { + datetime.timedelta(days=-1, seconds=82800): ( + datetime.datetime(1976, 4, 15, 0, 0), + { + datetime.timedelta(days=-1, seconds=82800): ( + datetime.datetime(1982, 3, 29, 0, 0), + { + datetime.timedelta(days=-1, seconds=82800): { + "Etc/GMT+1" + }, + datetime.timedelta(0): {"Atlantic/Azores"}, + }, + ), + datetime.timedelta(0): {"Africa/El_Aaiun"}, + }, + ), + datetime.timedelta(0): {"Africa/Bissau"}, + }, + ), + datetime.timedelta(days=-1, seconds=83730): {"Africa/Monrovia"}, + datetime.timedelta(days=-1, seconds=83760): {"Africa/Monrovia"}, + datetime.timedelta(0): ( + datetime.datetime(1971, 4, 26, 12, 0), + { + datetime.timedelta(0): ( + datetime.datetime(1974, 6, 25, 0, 0), + { + datetime.timedelta(0): ( + datetime.datetime(1976, 12, 1, 15, 0), + { + datetime.timedelta(days=-1, seconds=75600): { + "Antarctica/Rothera" + }, + datetime.timedelta(0): ( + datetime.datetime(1977, 4, 4, 0, 0), + { + datetime.timedelta(0): ( + datetime.datetime(1980, 4, 6, 15, 0), + { + datetime.timedelta(0): ( + datetime.datetime( + 1981, 3, 29, 18, 0 + ), + { + datetime.timedelta(0): ( + datetime.datetime( + 1982, 4, 4, 15, 0 + ), + { + datetime.timedelta(0): ( + datetime.datetime( + 2005, + 3, + 27, + 15, + 0, + ), + { + datetime.timedelta( + 0 + ): ( + datetime.datetime( + 2018, + 1, + 1, + 18, + 0, + ), + { + datetime.timedelta( + 0 + ): { + "Africa/Abidjan", + "Africa/Accra", + "Africa/Bamako", + "Africa/Banjul", + "Africa/Conakry", + "Africa/Dakar", + "Africa/Freetown", + "Africa/Lome", + "Africa/Nouakchott", + "Africa/Ouagadougou", + "Africa/Timbuktu", + "Atlantic/Reykjavik", + "Atlantic/St_Helena", + "Etc/GMT", + "Etc/GMT+0", + "Etc/GMT-0", + "Etc/GMT0", + "Etc/Greenwich", + "Etc/UCT", + "Etc/UTC", + "Etc/Universal", + "Etc/Zulu", + "GMT", + "GMT+0", + "GMT-0", + "GMT0", + "Greenwich", + "Iceland", + "UCT", + "UTC", + "Universal", + "Zulu", + }, + datetime.timedelta( + seconds=3600 + ): { + "Africa/Sao_Tome" + }, + }, + ), + datetime.timedelta( + seconds=7200 + ): { + "Antarctica/Troll" + }, + }, + ), + datetime.timedelta( + seconds=3600 + ): {"Atlantic/Madeira"}, + }, + ), + datetime.timedelta( + seconds=3600 + ): { + "Atlantic/Faeroe", + "Atlantic/Faroe", + }, + }, + ), + datetime.timedelta(seconds=3600): { + "Atlantic/Canary" + }, + }, + ), + datetime.timedelta(seconds=3600): {"WET"}, + }, + ), + }, + ), + datetime.timedelta(seconds=3600): ( + datetime.datetime(1986, 1, 1, 12, 0), + { + datetime.timedelta(0): {"Africa/Casablanca"}, + datetime.timedelta(seconds=3600): {"Africa/Ceuta"}, + }, + ), + }, + ), + datetime.timedelta(seconds=3600): {"Africa/Algiers"}, + }, + ), + datetime.timedelta(seconds=3600): ( + datetime.datetime(1970, 6, 1, 0, 0), + { + datetime.timedelta(seconds=3600): ( + datetime.datetime(1971, 11, 1, 0, 0), + { + datetime.timedelta(0): { + "Eire", + "Europe/Belfast", + "Europe/Dublin", + "Europe/Guernsey", + "Europe/Isle_of_Man", + "Europe/Jersey", + "Europe/London", + "GB", + "GB-Eire", + }, + datetime.timedelta(seconds=3600): ( + datetime.datetime(1974, 4, 14, 15, 0), + { + datetime.timedelta(seconds=3600): ( + datetime.datetime(1974, 5, 4, 21, 0), + { + datetime.timedelta(seconds=3600): ( + datetime.datetime(1976, 3, 28, 21, 0), + { + datetime.timedelta(seconds=3600): ( + datetime.datetime( + 1976, 9, 26, 21, 0 + ), + { + datetime.timedelta(0): ( + datetime.datetime( + 1980, 3, 30, 18, 0 + ), + { + datetime.timedelta(0): { + "Europe/Lisbon", + "Portugal", + "WET", + }, + datetime.timedelta( + seconds=3600 + ): {"Portugal"}, + }, + ), + datetime.timedelta( + seconds=3600 + ): ( + datetime.datetime( + 1977, 4, 3, 18, 0 + ), + { + datetime.timedelta( + seconds=3600 + ): ( + datetime.datetime( + 1977, + 4, + 30, + 21, + 0, + ), + { + datetime.timedelta( + seconds=3600 + ): ( + datetime.datetime( + 1979, + 4, + 1, + 18, + 0, + ), + { + datetime.timedelta( + seconds=3600 + ): ( + datetime.datetime( + 1979, + 10, + 15, + 0, + 0, + ), + { + datetime.timedelta( + seconds=3600 + ): ( + datetime.datetime( + 1980, + 4, + 6, + 15, + 0, + ), + { + datetime.timedelta( + seconds=3600 + ): ( + datetime.datetime( + 1981, + 3, + 29, + 18, + 0, + ), + { + datetime.timedelta( + seconds=3600 + ): ( + datetime.datetime( + 1982, + 3, + 28, + 18, + 0, + ), + { + datetime.timedelta( + seconds=3600 + ): ( + datetime.datetime( + 1983, + 3, + 27, + 18, + 0, + ), + { + datetime.timedelta( + seconds=3600 + ): ( + datetime.datetime( + 1985, + 4, + 1, + 0, + 0, + ), + { + datetime.timedelta( + seconds=3600 + ): { + "Africa/Bangui", + "Africa/Brazzaville", + "Africa/Douala", + "Africa/Kinshasa", + "Africa/Lagos", + "Africa/Libreville", + "Africa/Luanda", + "Africa/Malabo", + "Africa/Niamey", + "Africa/Porto-Novo", + "Etc/GMT-1", + }, + datetime.timedelta( + seconds=7200 + ): { + "Europe/Andorra" + }, + }, + ), + datetime.timedelta( + seconds=7200 + ): { + "Europe/Belgrade", + "Europe/Ljubljana", + "Europe/Podgorica", + "Europe/Sarajevo", + "Europe/Skopje", + "Europe/Zagreb", + }, + }, + ), + datetime.timedelta( + seconds=7200 + ): { + "Europe/Gibraltar" + }, + }, + ), + datetime.timedelta( + seconds=7200 + ): { + "Europe/Busingen", + "Europe/Vaduz", + "Europe/Zurich", + }, + }, + ), + datetime.timedelta( + seconds=7200 + ): { + "Arctic/Longyearbyen", + "Atlantic/Jan_Mayen", + "Europe/Berlin", + "Europe/Budapest", + "Europe/Copenhagen", + "Europe/Oslo", + "Europe/Stockholm", + "Europe/Vienna", + }, + }, + ), + datetime.timedelta( + seconds=7200 + ): { + "Africa/Ndjamena" + }, + }, + ), + datetime.timedelta( + seconds=7200 + ): { + "Europe/Bratislava", + "Europe/Prague", + }, + }, + ), + datetime.timedelta( + seconds=7200 + ): { + "Africa/Tunis" + }, + }, + ), + datetime.timedelta( + seconds=7200 + ): { + "CET", + "Europe/Amsterdam", + "Europe/Brussels", + "Europe/Luxembourg", + "Europe/Warsaw", + "MET", + "Poland", + }, + }, + ), + }, + ), + datetime.timedelta(seconds=7200): { + "Europe/Monaco", + "Europe/Paris", + }, + }, + ), + datetime.timedelta(seconds=7200): { + "Europe/Tirane" + }, + }, + ), + datetime.timedelta(seconds=7200): {"Europe/Madrid"}, + }, + ), + }, + ), + datetime.timedelta(seconds=7200): ( + datetime.datetime(1973, 3, 31, 18, 0), + { + datetime.timedelta(seconds=3600): { + "Europe/Rome", + "Europe/San_Marino", + "Europe/Vatican", + }, + datetime.timedelta(seconds=7200): {"Europe/Malta"}, + }, + ), + }, + ), + datetime.timedelta(seconds=7200): ( + datetime.datetime(1970, 5, 1, 15, 0), + { + datetime.timedelta(seconds=7200): ( + datetime.datetime(1972, 6, 23, 0, 0), + { + datetime.timedelta(seconds=7200): ( + datetime.datetime(1973, 6, 3, 15, 0), + { + datetime.timedelta(seconds=7200): ( + datetime.datetime(1973, 6, 6, 21, 0), + { + datetime.timedelta(seconds=7200): ( + datetime.datetime(1974, 7, 8, 0, 0), + { + datetime.timedelta(seconds=7200): ( + datetime.datetime( + 1975, 4, 13, 18, 0 + ), + { + datetime.timedelta( + seconds=7200 + ): ( + datetime.datetime( + 1977, 4, 3, 18, 0 + ), + { + datetime.timedelta( + seconds=7200 + ): ( + datetime.datetime( + 1979, + 4, + 1, + 15, + 0, + ), + { + datetime.timedelta( + seconds=7200 + ): ( + datetime.datetime( + 1979, + 5, + 27, + 15, + 0, + ), + { + datetime.timedelta( + seconds=7200 + ): ( + datetime.datetime( + 1981, + 3, + 29, + 15, + 0, + ), + { + datetime.timedelta( + seconds=7200 + ): ( + datetime.datetime( + 1982, + 1, + 1, + 21, + 0, + ), + { + datetime.timedelta( + seconds=3600 + ): { + "Africa/Tripoli", + "Libya", + }, + datetime.timedelta( + seconds=7200 + ): ( + datetime.datetime( + 1994, + 3, + 21, + 18, + 0, + ), + { + datetime.timedelta( + seconds=3600 + ): { + "Africa/Windhoek" + }, + datetime.timedelta( + seconds=7200 + ): { + "Africa/Blantyre", + "Africa/Bujumbura", + "Africa/Gaborone", + "Africa/Harare", + "Africa/Johannesburg", + "Africa/Kigali", + "Africa/Lubumbashi", + "Africa/Lusaka", + "Africa/Maputo", + "Africa/Maseru", + "Africa/Mbabane", + "Etc/GMT-2", + }, + }, + ), + }, + ), + datetime.timedelta( + seconds=10800 + ): { + "Europe/Helsinki", + "Europe/Mariehamn", + }, + }, + ), + datetime.timedelta( + seconds=10800 + ): { + "Europe/Bucharest" + }, + }, + ), + datetime.timedelta( + seconds=10800 + ): { + "Europe/Sofia" + }, + }, + ), + datetime.timedelta( + seconds=10800 + ): {"EET"}, + }, + ), + datetime.timedelta( + seconds=10800 + ): ( + datetime.datetime( + 1975, 10, 12, 18, 0 + ), + { + datetime.timedelta( + seconds=7200 + ): ( + datetime.datetime( + 2016, + 10, + 31, + 3, + 0, + ), + { + datetime.timedelta( + seconds=7200 + ): { + "Asia/Nicosia", + "Europe/Nicosia", + }, + datetime.timedelta( + seconds=10800 + ): { + "Asia/Famagusta" + }, + }, + ), + datetime.timedelta( + seconds=10800 + ): { + "EET", + "Europe/Athens", + }, + }, + ), + }, + ), + datetime.timedelta(seconds=10800): ( + datetime.datetime( + 1996, 3, 15, 21, 0 + ), + { + datetime.timedelta( + seconds=7200 + ): ( + datetime.datetime( + 2008, 8, 29, 18, 0 + ), + { + datetime.timedelta( + seconds=7200 + ): {"Asia/Gaza"}, + datetime.timedelta( + seconds=10800 + ): {"Asia/Hebron"}, + }, + ), + datetime.timedelta( + seconds=10800 + ): { + "Asia/Jerusalem", + "Asia/Tel_Aviv", + "Israel", + }, + }, + ), + }, + ), + datetime.timedelta(seconds=10800): { + "Asia/Amman" + }, + }, + ), + datetime.timedelta(seconds=10800): { + "Asia/Istanbul", + "Europe/Istanbul", + "Turkey", + }, + }, + ), + datetime.timedelta(seconds=10800): {"Asia/Beirut"}, + }, + ), + datetime.timedelta(seconds=10800): ( + datetime.datetime(1970, 10, 1, 18, 0), + { + datetime.timedelta(seconds=7200): ( + datetime.datetime(1977, 9, 2, 0, 0), + { + datetime.timedelta(seconds=7200): {"Asia/Damascus"}, + datetime.timedelta(seconds=10800): { + "Africa/Cairo", + "Egypt", + }, + }, + ), + datetime.timedelta(seconds=10800): ( + datetime.datetime(2017, 11, 1, 18, 0), + { + datetime.timedelta(seconds=7200): {"Africa/Khartoum"}, + datetime.timedelta(seconds=10800): {"Africa/Juba"}, + }, + ), + }, + ), + }, + ), + datetime.timedelta(seconds=10800): ( + datetime.datetime(1981, 4, 1, 15, 0), + { + datetime.timedelta(seconds=10800): ( + datetime.datetime(1982, 5, 1, 18, 0), + { + datetime.timedelta(seconds=10800): { + "Africa/Addis_Ababa", + "Africa/Asmara", + "Africa/Asmera", + "Africa/Dar_es_Salaam", + "Africa/Djibouti", + "Africa/Kampala", + "Africa/Mogadishu", + "Africa/Nairobi", + "Antarctica/Syowa", + "Asia/Aden", + "Asia/Kuwait", + "Asia/Riyadh", + "Etc/GMT-3", + "Indian/Antananarivo", + "Indian/Comoro", + "Indian/Mayotte", + }, + datetime.timedelta(seconds=14400): {"Asia/Baghdad"}, + }, + ), + datetime.timedelta(seconds=14400): ( + datetime.datetime(1989, 3, 26, 21, 0), + { + datetime.timedelta(seconds=10800): ( + datetime.datetime(1996, 9, 29, 15, 0), + { + datetime.timedelta(seconds=7200): {"Europe/Riga"}, + datetime.timedelta(seconds=10800): ( + datetime.datetime(1998, 3, 29, 15, 0), + { + datetime.timedelta(seconds=7200): { + "Europe/Vilnius" + }, + datetime.timedelta(seconds=10800): ( + datetime.datetime(2000, 3, 26, 21, 0), + { + datetime.timedelta(seconds=7200): { + "Europe/Tallinn" + }, + datetime.timedelta(seconds=10800): { + "Europe/Kaliningrad" + }, + }, + ), + }, + ), + }, + ), + datetime.timedelta(seconds=14400): ( + datetime.datetime(1990, 3, 25, 21, 0), + { + datetime.timedelta(seconds=10800): ( + datetime.datetime(1990, 7, 2, 0, 0), + { + datetime.timedelta(seconds=7200): { + "Europe/Simferopol" + }, + datetime.timedelta(seconds=10800): { + "Europe/Minsk" + }, + }, + ), + datetime.timedelta(seconds=14400): ( + datetime.datetime(1990, 5, 6, 15, 0), + { + datetime.timedelta(seconds=10800): { + "Europe/Chisinau", + "Europe/Tiraspol", + }, + datetime.timedelta(seconds=14400): ( + datetime.datetime(1990, 7, 1, 15, 0), + { + datetime.timedelta(seconds=10800): { + "Europe/Kiev", + "Europe/Kyiv", + "Europe/Uzhgorod", + "Europe/Zaporozhye", + }, + datetime.timedelta(seconds=14400): { + "Europe/Moscow", + "W-SU", + }, + }, + ), + }, + ), + }, + ), + }, + ), + }, + ), + datetime.timedelta(seconds=12600): {"Iran", "Asia/Tehran"}, + datetime.timedelta(seconds=14400): ( + datetime.datetime(1972, 6, 1, 21, 0), + { + datetime.timedelta(seconds=10800): {"Asia/Bahrain", "Asia/Qatar"}, + datetime.timedelta(seconds=14400): ( + datetime.datetime(1981, 4, 1, 15, 0), + { + datetime.timedelta(seconds=14400): ( + datetime.datetime(1982, 10, 11, 0, 0), + { + datetime.timedelta(seconds=14400): { + "Asia/Dubai", + "Asia/Muscat", + "Etc/GMT-4", + "Indian/Mahe", + "Indian/Reunion", + }, + datetime.timedelta(seconds=18000): {"Indian/Mauritius"}, + }, + ), + datetime.timedelta(seconds=18000): ( + datetime.datetime(1988, 3, 27, 21, 0), + { + datetime.timedelta(seconds=14400): ( + datetime.datetime(2016, 12, 4, 18, 0), + { + datetime.timedelta(seconds=10800): { + "Europe/Volgograd" + }, + datetime.timedelta(seconds=14400): { + "Europe/Saratov" + }, + }, + ), + datetime.timedelta(seconds=18000): ( + datetime.datetime(1989, 3, 26, 21, 0), + { + datetime.timedelta(seconds=14400): ( + datetime.datetime(1991, 3, 31, 18, 0), + { + datetime.timedelta(seconds=10800): ( + datetime.datetime( + 1991, 9, 29, 18, 0 + ), + { + datetime.timedelta( + seconds=7200 + ): {"Europe/Ulyanovsk"}, + datetime.timedelta( + seconds=10800 + ): {"Europe/Samara"}, + }, + ), + datetime.timedelta(seconds=14400): ( + datetime.datetime( + 2016, 3, 27, 18, 0 + ), + { + datetime.timedelta( + seconds=10800 + ): {"Europe/Kirov"}, + datetime.timedelta( + seconds=14400 + ): {"Europe/Astrakhan"}, + }, + ), + }, + ), + datetime.timedelta(seconds=18000): ( + datetime.datetime(1992, 9, 27, 18, 0), + { + datetime.timedelta(seconds=10800): ( + datetime.datetime( + 1994, 9, 25, 18, 0 + ), + { + datetime.timedelta( + seconds=10800 + ): {"Asia/Yerevan"}, + datetime.timedelta( + seconds=14400 + ): {"Asia/Tbilisi"}, + }, + ), + datetime.timedelta(seconds=14400): { + "Asia/Baku" + }, + }, + ), + }, + ), + }, + ), + }, + ), + }, + ), + datetime.timedelta(seconds=16200): {"Asia/Kabul"}, + datetime.timedelta(seconds=18000): ( + datetime.datetime(1981, 4, 1, 15, 0), + { + datetime.timedelta(seconds=18000): ( + datetime.datetime(1981, 10, 2, 0, 0), + { + datetime.timedelta(seconds=18000): ( + datetime.datetime(1996, 1, 1, 21, 0), + { + datetime.timedelta(seconds=18000): ( + datetime.datetime(2002, 4, 8, 0, 0), + { + datetime.timedelta(seconds=18000): { + "Etc/GMT-5", + "Indian/Kerguelen", + "Indian/Maldives", + }, + datetime.timedelta(seconds=21600): { + "Asia/Karachi" + }, + }, + ), + datetime.timedelta(seconds=21600): {"Indian/Chagos"}, + }, + ), + datetime.timedelta(seconds=21600): ( + datetime.datetime(1994, 9, 26, 3, 0), + { + datetime.timedelta(seconds=14400): {"Asia/Aqtau"}, + datetime.timedelta(seconds=18000): {"Asia/Atyrau"}, + }, + ), + }, + ), + datetime.timedelta(seconds=21600): ( + datetime.datetime(1981, 10, 1, 12, 0), + { + datetime.timedelta(seconds=18000): ( + datetime.datetime(1992, 3, 29, 18, 0), + { + datetime.timedelta(seconds=18000): { + "Asia/Ashgabat", + "Asia/Ashkhabad", + }, + datetime.timedelta(seconds=21600): { + "Asia/Yekaterinburg" + }, + }, + ), + datetime.timedelta(seconds=21600): ( + datetime.datetime(1989, 3, 26, 15, 0), + { + datetime.timedelta(seconds=18000): {"Asia/Oral"}, + datetime.timedelta(seconds=21600): ( + datetime.datetime(1991, 4, 1, 0, 0), + { + datetime.timedelta(seconds=18000): ( + datetime.datetime(1991, 9, 30, 0, 0), + { + datetime.timedelta(seconds=14400): ( + datetime.datetime( + 2004, 10, 31, 18, 0 + ), + { + datetime.timedelta( + seconds=18000 + ): {"Asia/Aqtobe"}, + datetime.timedelta( + seconds=21600 + ): {"Asia/Qostanay"}, + }, + ), + datetime.timedelta(seconds=18000): { + "Asia/Qyzylorda" + }, + }, + ), + datetime.timedelta(seconds=21600): { + "Asia/Samarkand" + }, + }, + ), + }, + ), + }, + ), + }, + ), + datetime.timedelta(seconds=19800): ( + datetime.datetime(1986, 1, 1, 18, 0), + { + datetime.timedelta(seconds=19800): ( + datetime.datetime(1987, 10, 1, 18, 0), + { + datetime.timedelta(seconds=19800): ( + datetime.datetime(1996, 5, 25, 15, 0), + { + datetime.timedelta(seconds=19800): { + "Asia/Calcutta", + "Asia/Kolkata", + }, + datetime.timedelta(seconds=23400): {"Asia/Colombo"}, + }, + ), + datetime.timedelta(seconds=21600): { + "Asia/Thimbu", + "Asia/Thimphu", + }, + }, + ), + datetime.timedelta(seconds=20700): {"Asia/Kathmandu", "Asia/Katmandu"}, + }, + ), + datetime.timedelta(seconds=21600): ( + datetime.datetime(1978, 1, 2, 0, 0), + { + datetime.timedelta(seconds=21600): ( + datetime.datetime(1981, 4, 1, 18, 0), + { + datetime.timedelta(seconds=21600): ( + datetime.datetime(2009, 6, 20, 18, 0), + { + datetime.timedelta(seconds=21600): ( + datetime.datetime(2009, 10, 18, 21, 0), + { + datetime.timedelta(seconds=18000): { + "Antarctica/Mawson" + }, + datetime.timedelta(seconds=21600): { + "Asia/Kashgar", + "Asia/Urumqi", + "Etc/GMT-6", + }, + }, + ), + datetime.timedelta(seconds=25200): { + "Asia/Dacca", + "Asia/Dhaka", + }, + }, + ), + datetime.timedelta(seconds=25200): ( + datetime.datetime(1991, 8, 31, 21, 0), + { + datetime.timedelta(seconds=18000): {"Asia/Bishkek"}, + datetime.timedelta(seconds=21600): ( + datetime.datetime(1991, 9, 9, 15, 0), + { + datetime.timedelta(seconds=18000): { + "Asia/Dushanbe" + }, + datetime.timedelta(seconds=21600): ( + datetime.datetime(1992, 1, 19, 18, 0), + { + datetime.timedelta(seconds=18000): { + "Asia/Tashkent" + }, + datetime.timedelta(seconds=21600): ( + datetime.datetime( + 2005, 3, 27, 15, 0 + ), + { + datetime.timedelta( + seconds=21600 + ): {"Asia/Almaty"}, + datetime.timedelta( + seconds=25200 + ): {"Asia/Omsk"}, + }, + ), + }, + ), + }, + ), + }, + ), + }, + ), + datetime.timedelta(seconds=25200): {"Asia/Hovd"}, + }, + ), + datetime.timedelta(seconds=23400): { + "Asia/Rangoon", + "Asia/Yangon", + "Indian/Cocos", + }, + datetime.timedelta(seconds=25200): ( + datetime.datetime(1978, 1, 2, 0, 0), + { + datetime.timedelta(seconds=25200): ( + datetime.datetime(1981, 4, 1, 18, 0), + { + datetime.timedelta(seconds=25200): ( + datetime.datetime(1994, 2, 1, 12, 0), + { + datetime.timedelta(0): {"Antarctica/Vostok"}, + datetime.timedelta(seconds=25200): ( + datetime.datetime(2009, 10, 18, 21, 0), + { + datetime.timedelta(seconds=18000): { + "Antarctica/Davis" + }, + datetime.timedelta(seconds=25200): { + "Asia/Bangkok", + "Asia/Jakarta", + "Asia/Phnom_Penh", + "Asia/Vientiane", + "Etc/GMT-7", + "Indian/Christmas", + }, + }, + ), + }, + ), + datetime.timedelta(seconds=28800): ( + datetime.datetime(1993, 5, 23, 18, 0), + { + datetime.timedelta(seconds=25200): {"Asia/Novosibirsk"}, + datetime.timedelta(seconds=28800): ( + datetime.datetime(1995, 5, 28, 18, 0), + { + datetime.timedelta(seconds=25200): { + "Asia/Barnaul" + }, + datetime.timedelta(seconds=28800): ( + datetime.datetime(2002, 5, 2, 0, 0), + { + datetime.timedelta(seconds=25200): { + "Asia/Tomsk" + }, + datetime.timedelta(seconds=28800): ( + datetime.datetime( + 2010, 3, 28, 15, 0 + ), + { + datetime.timedelta( + seconds=25200 + ): {"Asia/Novokuznetsk"}, + datetime.timedelta( + seconds=28800 + ): {"Asia/Krasnoyarsk"}, + }, + ), + }, + ), + }, + ), + }, + ), + }, + ), + datetime.timedelta(seconds=28800): { + "Asia/Choibalsan", + "Asia/Ulaanbaatar", + "Asia/Ulan_Bator", + }, + }, + ), + datetime.timedelta(seconds=27000): { + "Asia/Kuala_Lumpur", + "Asia/Singapore", + "Singapore", + }, + datetime.timedelta(seconds=28800): ( + datetime.datetime(1970, 4, 19, 18, 0), + { + datetime.timedelta(seconds=28800): ( + datetime.datetime(1974, 4, 1, 15, 0), + { + datetime.timedelta(seconds=28800): ( + datetime.datetime(1974, 10, 27, 15, 0), + { + datetime.timedelta(seconds=28800): ( + datetime.datetime(1975, 6, 13, 21, 0), + { + datetime.timedelta(seconds=25200): { + "Asia/Ho_Chi_Minh", + "Asia/Saigon", + }, + datetime.timedelta(seconds=28800): ( + datetime.datetime(1978, 3, 22, 21, 0), + { + datetime.timedelta(seconds=28800): ( + datetime.datetime( + 1981, 4, 1, 21, 0 + ), + { + datetime.timedelta( + seconds=28800 + ): ( + datetime.datetime( + 1986, 5, 4, 21, 0 + ), + { + datetime.timedelta( + seconds=28800 + ): ( + datetime.datetime( + 1988, + 1, + 1, + 18, + 0, + ), + { + datetime.timedelta( + seconds=25200 + ): { + "Asia/Pontianak" + }, + datetime.timedelta( + seconds=28800 + ): ( + datetime.datetime( + 2009, + 10, + 19, + 3, + 0, + ), + { + datetime.timedelta( + seconds=28800 + ): { + "Asia/Brunei", + "Asia/Kuching", + "Asia/Makassar", + "Asia/Ujung_Pandang", + "Etc/GMT-8", + }, + datetime.timedelta( + seconds=39600 + ): { + "Antarctica/Casey" + }, + }, + ), + }, + ), + datetime.timedelta( + seconds=32400 + ): { + "Asia/Chongqing", + "Asia/Chungking", + "Asia/Harbin", + "Asia/Shanghai", + "PRC", + }, + }, + ), + datetime.timedelta( + seconds=32400 + ): {"Asia/Irkutsk"}, + }, + ), + datetime.timedelta(seconds=32400): { + "Asia/Manila" + }, + }, + ), + }, + ), + datetime.timedelta(seconds=32400): { + "Australia/Perth", + "Australia/West", + }, + }, + ), + datetime.timedelta(seconds=32400): {"Asia/Taipei", "ROC"}, + }, + ), + datetime.timedelta(seconds=32400): { + "Asia/Hong_Kong", + "Asia/Macao", + "Asia/Macau", + "Hongkong", + }, + }, + ), + datetime.timedelta(seconds=31500): {"Australia/Eucla"}, + datetime.timedelta(seconds=32400): ( + datetime.datetime(1976, 5, 3, 18, 0), + { + datetime.timedelta(seconds=28800): {"Asia/Dili"}, + datetime.timedelta(seconds=32400): ( + datetime.datetime(1981, 4, 1, 18, 0), + { + datetime.timedelta(seconds=32400): ( + datetime.datetime(1987, 5, 10, 15, 0), + { + datetime.timedelta(seconds=32400): ( + datetime.datetime(2015, 8, 15, 15, 0), + { + datetime.timedelta(seconds=30600): { + "Asia/Pyongyang" + }, + datetime.timedelta(seconds=32400): { + "Asia/Jayapura", + "Asia/Tokyo", + "Etc/GMT-9", + "Japan", + "Pacific/Palau", + }, + }, + ), + datetime.timedelta(seconds=36000): { + "Asia/Seoul", + "ROK", + }, + }, + ), + datetime.timedelta(seconds=36000): ( + datetime.datetime(2004, 1, 1, 21, 0), + { + datetime.timedelta(seconds=32400): ( + datetime.datetime(2014, 10, 27, 0, 0), + { + datetime.timedelta(seconds=28800): { + "Asia/Chita" + }, + datetime.timedelta(seconds=32400): { + "Asia/Yakutsk" + }, + }, + ), + datetime.timedelta(seconds=36000): {"Asia/Khandyga"}, + }, + ), + datetime.timedelta(seconds=43200): {"Asia/Ust-Nera"}, + }, + ), + }, + ), + datetime.timedelta(seconds=34200): ( + datetime.datetime(1971, 10, 31, 21, 0), + { + datetime.timedelta(seconds=34200): { + "Australia/Darwin", + "Australia/North", + }, + datetime.timedelta(seconds=37800): ( + datetime.datetime(1982, 3, 7, 18, 0), + { + datetime.timedelta(seconds=34200): { + "Australia/Adelaide", + "Australia/South", + }, + datetime.timedelta(seconds=37800): { + "Australia/Broken_Hill", + "Australia/Yancowinna", + }, + }, + ), + }, + ), + datetime.timedelta(seconds=36000): ( + datetime.datetime(1970, 4, 26, 15, 0), + { + datetime.timedelta(seconds=36000): ( + datetime.datetime(1971, 10, 31, 21, 0), + { + datetime.timedelta(seconds=36000): ( + datetime.datetime(1981, 3, 2, 0, 0), + { + datetime.timedelta(seconds=36000): ( + datetime.datetime(1981, 4, 1, 18, 0), + { + datetime.timedelta(seconds=36000): ( + datetime.datetime(2014, 12, 28, 21, 0), + { + datetime.timedelta(seconds=36000): { + "Antarctica/DumontDUrville", + "Etc/GMT-10", + "Pacific/Chuuk", + "Pacific/Port_Moresby", + "Pacific/Truk", + "Pacific/Yap", + }, + datetime.timedelta(seconds=39600): { + "Pacific/Bougainville" + }, + }, + ), + datetime.timedelta(seconds=39600): { + "Asia/Vladivostok" + }, + }, + ), + datetime.timedelta(seconds=37800): { + "Australia/LHI", + "Australia/Lord_Howe", + }, + }, + ), + datetime.timedelta(seconds=39600): ( + datetime.datetime(1972, 10, 29, 21, 0), + { + datetime.timedelta(seconds=36000): ( + datetime.datetime(1992, 10, 26, 0, 0), + { + datetime.timedelta(seconds=36000): { + "Australia/Brisbane", + "Australia/Queensland", + }, + datetime.timedelta(seconds=39600): { + "Australia/Lindeman" + }, + }, + ), + datetime.timedelta(seconds=39600): ( + datetime.datetime(1982, 3, 7, 18, 0), + { + datetime.timedelta(seconds=36000): { + "Australia/Melbourne", + "Australia/Victoria", + }, + datetime.timedelta(seconds=39600): { + "Australia/ACT", + "Australia/Canberra", + "Australia/NSW", + "Australia/Sydney", + }, + }, + ), + }, + ), + }, + ), + datetime.timedelta(seconds=39600): {"Pacific/Guam", "Pacific/Saipan"}, + }, + ), + datetime.timedelta(seconds=39600): ( + datetime.datetime(1970, 3, 8, 15, 0), + { + datetime.timedelta(seconds=36000): ( + datetime.datetime(2010, 4, 4, 21, 0), + { + datetime.timedelta(seconds=36000): { + "Australia/Currie", + "Australia/Hobart", + "Australia/Tasmania", + }, + datetime.timedelta(seconds=39600): {"Antarctica/Macquarie"}, + }, + ), + datetime.timedelta(seconds=39600): ( + datetime.datetime(1973, 12, 23, 15, 0), + { + datetime.timedelta(seconds=39600): ( + datetime.datetime(1977, 12, 4, 15, 0), + { + datetime.timedelta(seconds=39600): ( + datetime.datetime(1981, 4, 1, 18, 0), + { + datetime.timedelta(seconds=39600): { + "Etc/GMT-11", + "Pacific/Guadalcanal", + "Pacific/Pohnpei", + "Pacific/Ponape", + }, + datetime.timedelta(seconds=43200): ( + datetime.datetime(1997, 3, 30, 15, 0), + { + datetime.timedelta(seconds=39600): { + "Asia/Sakhalin" + }, + datetime.timedelta(seconds=43200): ( + datetime.datetime( + 2014, 10, 26, 18, 0 + ), + { + datetime.timedelta( + seconds=36000 + ): {"Asia/Magadan"}, + datetime.timedelta( + seconds=39600 + ): {"Asia/Srednekolymsk"}, + }, + ), + }, + ), + }, + ), + datetime.timedelta(seconds=43200): {"Pacific/Noumea"}, + }, + ), + datetime.timedelta(seconds=43200): {"Pacific/Efate"}, + }, + ), + }, + ), + datetime.timedelta(seconds=41400): ( + datetime.datetime(1974, 10, 27, 15, 0), + { + datetime.timedelta(seconds=41400): {"Pacific/Nauru"}, + datetime.timedelta(seconds=45000): {"Pacific/Norfolk"}, + }, + ), + datetime.timedelta(seconds=43200): ( + datetime.datetime(1974, 11, 3, 21, 0), + { + datetime.timedelta(seconds=43200): ( + datetime.datetime(1981, 4, 1, 15, 0), + { + datetime.timedelta(seconds=43200): ( + datetime.datetime(1998, 11, 1, 18, 0), + { + datetime.timedelta(seconds=43200): ( + datetime.datetime(1999, 1, 1, 18, 0), + { + datetime.timedelta(seconds=39600): { + "Pacific/Kosrae" + }, + datetime.timedelta(seconds=43200): { + "Etc/GMT-12", + "Pacific/Funafuti", + "Pacific/Majuro", + "Pacific/Tarawa", + "Pacific/Wake", + "Pacific/Wallis", + }, + }, + ), + datetime.timedelta(seconds=46800): {"Pacific/Fiji"}, + }, + ), + datetime.timedelta(seconds=46800): {"Asia/Kamchatka"}, + }, + ), + datetime.timedelta(seconds=46800): { + "Antarctica/McMurdo", + "Antarctica/South_Pole", + "NZ", + "Pacific/Auckland", + }, + }, + ), + datetime.timedelta(seconds=45900): {"NZ-CHAT", "Pacific/Chatham"}, + datetime.timedelta(seconds=46800): ( + datetime.datetime(1981, 4, 1, 15, 0), + { + datetime.timedelta(seconds=46800): ( + datetime.datetime(1999, 10, 7, 21, 0), + { + datetime.timedelta(seconds=46800): {"Etc/GMT-13"}, + datetime.timedelta(seconds=50400): {"Pacific/Tongatapu"}, + }, + ), + datetime.timedelta(seconds=50400): {"Asia/Anadyr"}, + }, + ), + datetime.timedelta(seconds=50400): {"Etc/GMT-14"}, + }, +) + + +__all__ = ["lookup"] diff --git a/.venv/lib/python3.9/site-packages/icalendar/timezone/provider.py b/.venv/lib/python3.9/site-packages/icalendar/timezone/provider.py new file mode 100644 index 0000000..cf3c081 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/timezone/provider.py @@ -0,0 +1,57 @@ +"""The interface for timezone implementations.""" + +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING, Optional + +if TYPE_CHECKING: + from datetime import datetime, tzinfo + + from dateutil.rrule import rrule + + from icalendar import prop + + +class TZProvider(ABC): + """Interface for timezone implementations.""" + + @property + @abstractmethod + def name(self) -> str: + """The name of the implementation.""" + + @abstractmethod + def localize_utc(self, dt: datetime) -> datetime: + """Return the datetime in UTC.""" + + @abstractmethod + def localize(self, dt: datetime, tz: tzinfo) -> datetime: + """Localize a datetime to a timezone.""" + + @abstractmethod + def knows_timezone_id(self, id: str) -> bool: + """Whether the timezone is already cached by the implementation.""" + + @abstractmethod + def fix_rrule_until(self, rrule: rrule, ical_rrule: prop.vRecur) -> None: + """Make sure the until value works for the rrule generated from the ical_rrule.""" + + @abstractmethod + def create_timezone(self, name: str, transition_times, transition_info) -> tzinfo: + """Create a pytz timezone file given information.""" + + @abstractmethod + def timezone(self, name: str) -> Optional[tzinfo]: + """Return a timezone with a name or None if we cannot find it.""" + + @abstractmethod + def uses_pytz(self) -> bool: + """Whether we use pytz.""" + + @abstractmethod + def uses_zoneinfo(self) -> bool: + """Whether we use zoneinfo.""" + + +__all__ = ["TZProvider"] diff --git a/.venv/lib/python3.9/site-packages/icalendar/timezone/pytz.py b/.venv/lib/python3.9/site-packages/icalendar/timezone/pytz.py new file mode 100644 index 0000000..c69ed69 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/timezone/pytz.py @@ -0,0 +1,72 @@ +"""Use pytz timezones.""" + +from __future__ import annotations +import pytz +from .. import cal +from datetime import datetime, tzinfo +from pytz.tzinfo import DstTzInfo +from typing import Optional +from .provider import TZProvider +from icalendar import prop +from dateutil.rrule import rrule + + +class PYTZ(TZProvider): + """Provide icalendar with timezones from pytz.""" + + name = "pytz" + + def localize_utc(self, dt: datetime) -> datetime: + """Return the datetime in UTC.""" + if getattr(dt, "tzinfo", False) and dt.tzinfo is not None: + return dt.astimezone(pytz.utc) + # assume UTC for naive datetime instances + return pytz.utc.localize(dt) + + def localize(self, dt: datetime, tz: tzinfo) -> datetime: + """Localize a datetime to a timezone.""" + return tz.localize(dt) + + def knows_timezone_id(self, id: str) -> bool: + """Whether the timezone is already cached by the implementation.""" + return id in pytz.all_timezones + + def fix_rrule_until(self, rrule: rrule, ical_rrule: prop.vRecur) -> None: + """Make sure the until value works for the rrule generated from the ical_rrule.""" + if not {"UNTIL", "COUNT"}.intersection(ical_rrule.keys()): + # pytz.timezones don't know any transition dates after 2038 + # either + rrule._until = datetime(2038, 12, 31, tzinfo=pytz.UTC) + + def create_timezone(self, tz: cal.Timezone) -> tzinfo: + """Create a pytz timezone from the given information.""" + transition_times, transition_info = tz.get_transitions() + name = tz.tz_name + cls = type( + name, + (DstTzInfo,), + { + "zone": name, + "_utc_transition_times": transition_times, + "_transition_info": transition_info, + }, + ) + return cls() + + def timezone(self, name: str) -> Optional[tzinfo]: + """Return a timezone with a name or None if we cannot find it.""" + try: + return pytz.timezone(name) + except pytz.UnknownTimeZoneError: + pass + + def uses_pytz(self) -> bool: + """Whether we use pytz.""" + return True + + def uses_zoneinfo(self) -> bool: + """Whether we use zoneinfo.""" + return False + + +__all__ = ["PYTZ"] diff --git a/.venv/lib/python3.9/site-packages/icalendar/timezone/tzid.py b/.venv/lib/python3.9/site-packages/icalendar/timezone/tzid.py new file mode 100644 index 0000000..dffb1c7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/timezone/tzid.py @@ -0,0 +1,112 @@ +"""This module identifies timezones. + +Normally, timezones have ids. +This is a way to access the ids if you have a +datetime.tzinfo object. +""" + +from __future__ import annotations + +from collections import defaultdict +from pathlib import Path +from typing import TYPE_CHECKING, Optional + +from dateutil.tz import tz + +from icalendar.timezone import equivalent_timezone_ids_result + +if TYPE_CHECKING: + from datetime import datetime, tzinfo + +DATEUTIL_UTC = tz.gettz("UTC") +DATEUTIL_UTC_PATH : Optional[str] = getattr(DATEUTIL_UTC, "_filename", None) +DATEUTIL_ZONEINFO_PATH = ( + None if DATEUTIL_UTC_PATH is None else Path(DATEUTIL_UTC_PATH).parent +) + +def tzids_from_tzinfo(tzinfo: Optional[tzinfo]) -> tuple[str]: + """Get several timezone ids if we can identify the timezone. + + >>> import zoneinfo + >>> from icalendar.timezone.tzid import tzids_from_tzinfo + >>> tzids_from_tzinfo(zoneinfo.ZoneInfo("Arctic/Longyearbyen")) + ('Arctic/Longyearbyen', 'Atlantic/Jan_Mayen', 'Europe/Berlin', 'Europe/Budapest', 'Europe/Copenhagen', 'Europe/Oslo', 'Europe/Stockholm', 'Europe/Vienna') + >>> from dateutil.tz import gettz + >>> tzids_from_tzinfo(gettz("Europe/Berlin")) + ('Europe/Berlin', 'Arctic/Longyearbyen', 'Atlantic/Jan_Mayen', 'Europe/Budapest', 'Europe/Copenhagen', 'Europe/Oslo', 'Europe/Stockholm', 'Europe/Vienna') + + """ # The example might need to change if you recreate the lookup tree + if tzinfo is None: + return () + if hasattr(tzinfo, "zone"): + return get_equivalent_tzids(tzinfo.zone) # pytz implementation + if hasattr(tzinfo, "key"): + return get_equivalent_tzids(tzinfo.key) # ZoneInfo implementation + if isinstance(tzinfo, tz._tzicalvtz): # noqa: SLF001 + return get_equivalent_tzids(tzinfo._tzid) # noqa: SLF001 + if isinstance(tzinfo, tz.tzstr): + return get_equivalent_tzids(tzinfo._s) # noqa: SLF001 + if hasattr(tzinfo, "_filename"): # dateutil.tz.tzfile # noqa: SIM102 + if DATEUTIL_ZONEINFO_PATH is not None: + # tzfile('/usr/share/zoneinfo/Europe/Berlin') + path = tzinfo._filename # noqa: SLF001 + if path.startswith(str(DATEUTIL_ZONEINFO_PATH)): + tzid = str(Path(path).relative_to(DATEUTIL_ZONEINFO_PATH)) + return get_equivalent_tzids(tzid) + return get_equivalent_tzids(path) + if isinstance(tzinfo, tz.tzutc): + return get_equivalent_tzids("UTC") + return () + + +def tzid_from_tzinfo(tzinfo: Optional[tzinfo]) -> Optional[str]: + """Retrieve the timezone id from the tzinfo object. + + Some timezones are equivalent. + Thus, we might return one ID that is equivelant to others. + """ + tzids = tzids_from_tzinfo(tzinfo) + if "UTC" in tzids: + return "UTC" + if not tzids: + return None + return tzids[0] + + +def tzid_from_dt(dt: datetime) -> Optional[str]: + """Retrieve the timezone id from the datetime object.""" + tzid = tzid_from_tzinfo(dt.tzinfo) + if tzid is None: + return dt.tzname() + return tzid + + +_EQUIVALENT_IDS : dict[str, set[str]] = defaultdict(set) + +def _add_equivalent_ids(value:tuple|dict|set): + """This adds equivalent ids/ + + As soon as one timezone implementation used claims their equivalence, + they are considered equivalent. + Have a look at icalendar.timezone.equivalent_timezone_ids. + """ + if isinstance(value, set): + for tzid in value: + _EQUIVALENT_IDS[tzid].update(value) + elif isinstance(value, tuple): + _add_equivalent_ids(value[1]) + elif isinstance(value, dict): + for value in value.values(): + _add_equivalent_ids(value) + else: + raise TypeError(f"Expected tuple, dict or set, not {value.__class__.__name__}: {value!r}") + +_add_equivalent_ids(equivalent_timezone_ids_result.lookup) + +def get_equivalent_tzids(tzid: str) -> tuple[str]: + """This returns the tzids which are equivalent to this one.""" + ids = _EQUIVALENT_IDS.get(tzid, set()) + return (tzid,) + tuple(sorted(ids - {tzid})) + + +__all__ = ["tzid_from_tzinfo", "tzid_from_dt", "tzids_from_tzinfo"] diff --git a/.venv/lib/python3.9/site-packages/icalendar/timezone/tzp.py b/.venv/lib/python3.9/site-packages/icalendar/timezone/tzp.py new file mode 100644 index 0000000..4e81604 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/timezone/tzp.py @@ -0,0 +1,146 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Optional, Union + +from icalendar.tools import to_datetime + +from .windows_to_olson import WINDOWS_TO_OLSON + +if TYPE_CHECKING: + import datetime + + from dateutil.rrule import rrule + + from icalendar import cal, prop + + from .provider import TZProvider + +DEFAULT_TIMEZONE_PROVIDER = "zoneinfo" + + +class TZP: + """This is the timezone provider proxy. + + If you would like to have another timezone implementation, + you can create a new one and pass it to this proxy. + All of icalendar will then use this timezone implementation. + """ + + def __init__(self, provider: Union[str, TZProvider] = DEFAULT_TIMEZONE_PROVIDER): + """Create a new timezone implementation proxy.""" + self.use(provider) + + def use_pytz(self) -> None: + """Use pytz as the timezone provider.""" + from .pytz import PYTZ + + self._use(PYTZ()) + + def use_zoneinfo(self) -> None: + """Use zoneinfo as the timezone provider.""" + from .zoneinfo import ZONEINFO + + self._use(ZONEINFO()) + + def _use(self, provider: TZProvider) -> None: + """Use a timezone implementation.""" + self.__tz_cache = {} + self.__provider = provider + + def use(self, provider: Union[str, TZProvider]): + """Switch to a different timezone provider.""" + if isinstance(provider, str): + use_provider = getattr(self, f"use_{provider}", None) + if use_provider is None: + raise ValueError( + f"Unknown provider {provider}. Use 'pytz' or 'zoneinfo'." + ) + use_provider() + else: + self._use(provider) + + def use_default(self): + """Use the default timezone provider.""" + self.use(DEFAULT_TIMEZONE_PROVIDER) + + def localize_utc(self, dt: datetime.date) -> datetime.datetime: + """Return the datetime in UTC. + + If the datetime has no timezone, set UTC as its timezone. + """ + return self.__provider.localize_utc(to_datetime(dt)) + + def localize( + self, dt: datetime.date, tz: Union[datetime.tzinfo, str, None] + ) -> datetime.datetime: + """Localize a datetime to a timezone.""" + if isinstance(tz, str): + tz = self.timezone(tz) + if tz is None: + return dt.replace(tzinfo=None) + return self.__provider.localize(to_datetime(dt), tz) + + def cache_timezone_component(self, timezone_component: cal.Timezone) -> None: + """Cache the timezone that is created from a timezone component + if it is not already known. + + This can influence the result from timezone(): Once cached, the + custom timezone is returned from timezone(). + """ + _unclean_id = timezone_component["TZID"] + _id = self.clean_timezone_id(_unclean_id) + if ( + not self.__provider.knows_timezone_id(_id) + and not self.__provider.knows_timezone_id(_unclean_id) + and _id not in self.__tz_cache + ): + self.__tz_cache[_id] = timezone_component.to_tz(self, lookup_tzid=False) + + def fix_rrule_until(self, rrule: rrule, ical_rrule: prop.vRecur) -> None: + """Make sure the until value works.""" + self.__provider.fix_rrule_until(rrule, ical_rrule) + + def create_timezone(self, timezone_component: cal.Timezone) -> datetime.tzinfo: + """Create a timezone from a timezone component. + + This component will not be cached. + """ + return self.__provider.create_timezone(timezone_component) + + def clean_timezone_id(self, tzid: str) -> str: + """Return a clean version of the timezone id. + + Timezone ids can be a bit unclean, starting with a / for example. + Internally, we should use this to identify timezones. + """ + return tzid.strip("/") + + def timezone(self, tz_id: str) -> Optional[datetime.tzinfo]: + """Return a timezone with an id or None if we cannot find it.""" + _unclean_id = tz_id + tz_id = self.clean_timezone_id(tz_id) + tz = self.__provider.timezone(tz_id) + if tz is not None: + return tz + if tz_id in WINDOWS_TO_OLSON: + tz = self.__provider.timezone(WINDOWS_TO_OLSON[tz_id]) + return tz or self.__provider.timezone(_unclean_id) or self.__tz_cache.get(tz_id) + + def uses_pytz(self) -> bool: + """Whether we use pytz at all.""" + return self.__provider.uses_pytz() + + def uses_zoneinfo(self) -> bool: + """Whether we use zoneinfo.""" + return self.__provider.uses_zoneinfo() + + @property + def name(self) -> str: + """The name of the timezone component used.""" + return self.__provider.name + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({repr(self.name)})" + + +__all__ = ["TZP"] diff --git a/.venv/lib/python3.9/site-packages/icalendar/timezone/windows_to_olson.py b/.venv/lib/python3.9/site-packages/icalendar/timezone/windows_to_olson.py new file mode 100644 index 0000000..c05132f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/timezone/windows_to_olson.py @@ -0,0 +1,119 @@ +"""This module contains mappings from Windows timezone identifiers to +Olson timezone identifiers. + +The data is taken from the unicode consortium [0], the proposal and rationale +for this mapping is also available at the unicode consortium [1]. + + +[0] https://www.unicode.org/cldr/cldr-aux/charts/29/supplemental/zone_tzid.html +[1] https://cldr.unicode.org/development/development-process/design-proposals/extended-windows-olson-zid-mapping # noqa +""" + +WINDOWS_TO_OLSON = { + "AUS Central Standard Time": "Australia/Darwin", + "AUS Eastern Standard Time": "Australia/Sydney", + "Afghanistan Standard Time": "Asia/Kabul", + "Alaskan Standard Time": "America/Anchorage", + "Arab Standard Time": "Asia/Riyadh", + "Arabian Standard Time": "Asia/Dubai", + "Arabic Standard Time": "Asia/Baghdad", + "Argentina Standard Time": "America/Argentina/Buenos_Aires", + "Atlantic Standard Time": "America/Halifax", + "Azerbaijan Standard Time": "Asia/Baku", + "Azores Standard Time": "Atlantic/Azores", + "Bahia Standard Time": "America/Bahia", + "Bangladesh Standard Time": "Asia/Dhaka", + "Belarus Standard Time": "Europe/Minsk", + "Canada Central Standard Time": "America/Regina", + "Cape Verde Standard Time": "Atlantic/Cape_Verde", + "Caucasus Standard Time": "Asia/Yerevan", + "Cen. Australia Standard Time": "Australia/Adelaide", + "Central America Standard Time": "America/Guatemala", + "Central Asia Standard Time": "Asia/Almaty", + "Central Brazilian Standard Time": "America/Cuiaba", + "Central Europe Standard Time": "Europe/Budapest", + "Central European Standard Time": "Europe/Warsaw", + "Central Pacific Standard Time": "Pacific/Guadalcanal", + "Central Standard Time": "America/Chicago", + "Central Standard Time (Mexico)": "America/Mexico_City", + "China Standard Time": "Asia/Shanghai", + "Dateline Standard Time": "Etc/GMT+12", + "E. Africa Standard Time": "Africa/Nairobi", + "E. Australia Standard Time": "Australia/Brisbane", + "E. Europe Standard Time": "Europe/Chisinau", + "E. South America Standard Time": "America/Sao_Paulo", + "Eastern Standard Time": "America/New_York", + "Eastern Standard Time (Mexico)": "America/Cancun", + "Egypt Standard Time": "Africa/Cairo", + "Ekaterinburg Standard Time": "Asia/Yekaterinburg", + "FLE Standard Time": "Europe/Kyiv", + "Fiji Standard Time": "Pacific/Fiji", + "GMT Standard Time": "Europe/London", + "GTB Standard Time": "Europe/Bucharest", + "Georgian Standard Time": "Asia/Tbilisi", + "Greenland Standard Time": "America/Nuuk", + "Greenwich Standard Time": "Atlantic/Reykjavik", + "Hawaiian Standard Time": "Pacific/Honolulu", + "India Standard Time": "Asia/Kolkata", + "Iran Standard Time": "Asia/Tehran", + "Israel Standard Time": "Asia/Jerusalem", + "Jordan Standard Time": "Asia/Amman", + "Kaliningrad Standard Time": "Europe/Kaliningrad", + "Korea Standard Time": "Asia/Seoul", + "Libya Standard Time": "Africa/Tripoli", + "Line Islands Standard Time": "Pacific/Kiritimati", + "Magadan Standard Time": "Asia/Magadan", + "Mauritius Standard Time": "Indian/Mauritius", + "Middle East Standard Time": "Asia/Beirut", + "Montevideo Standard Time": "America/Montevideo", + "Morocco Standard Time": "Africa/Casablanca", + "Mountain Standard Time": "America/Denver", + "Mountain Standard Time (Mexico)": "America/Chihuahua", + "Myanmar Standard Time": "Asia/Yangon", + "N. Central Asia Standard Time": "Asia/Novosibirsk", + "Namibia Standard Time": "Africa/Windhoek", + "Nepal Standard Time": "Asia/Kathmandu", + "New Zealand Standard Time": "Pacific/Auckland", + "Newfoundland Standard Time": "America/St_Johns", + "North Asia East Standard Time": "Asia/Irkutsk", + "North Asia Standard Time": "Asia/Krasnoyarsk", + "North Korea Standard Time": "Asia/Pyongyang", + "Pacific SA Standard Time": "America/Santiago", + "Pacific Standard Time": "America/Los_Angeles", + "Pakistan Standard Time": "Asia/Karachi", + "Paraguay Standard Time": "America/Asuncion", + "Romance Standard Time": "Europe/Paris", + "Russia Time Zone 10": "Asia/Srednekolymsk", + "Russia Time Zone 11": "Asia/Kamchatka", + "Russia Time Zone 3": "Europe/Samara", + "Russian Standard Time": "Europe/Moscow", + "SA Eastern Standard Time": "America/Cayenne", + "SA Pacific Standard Time": "America/Bogota", + "SA Western Standard Time": "America/La_Paz", + "SE Asia Standard Time": "Asia/Bangkok", + "Samoa Standard Time": "Pacific/Apia", + "Singapore Standard Time": "Asia/Singapore", + "South Africa Standard Time": "Africa/Johannesburg", + "Sri Lanka Standard Time": "Asia/Colombo", + "Syria Standard Time": "Asia/Damascus", + "Taipei Standard Time": "Asia/Taipei", + "Tasmania Standard Time": "Australia/Hobart", + "Tokyo Standard Time": "Asia/Tokyo", + "Tonga Standard Time": "Pacific/Tongatapu", + "Turkey Standard Time": "Europe/Istanbul", + "US Eastern Standard Time": "America/Indiana/Indianapolis", + "US Mountain Standard Time": "America/Phoenix", + "UTC": "Etc/GMT", + "UTC+12": "Etc/GMT-12", + "UTC-02": "Etc/GMT+2", + "UTC-11": "Etc/GMT+11", + "Ulaanbaatar Standard Time": "Asia/Ulaanbaatar", + "Venezuela Standard Time": "America/Caracas", + "Vladivostok Standard Time": "Asia/Vladivostok", + "W. Australia Standard Time": "Australia/Perth", + "W. Central Africa Standard Time": "Africa/Lagos", + "W. Europe Standard Time": "Europe/Berlin", + "West Asia Standard Time": "Asia/Tashkent", + "West Pacific Standard Time": "Pacific/Port_Moresby", + "Yakutsk Standard Time": "Asia/Yakutsk", +} diff --git a/.venv/lib/python3.9/site-packages/icalendar/timezone/zoneinfo.py b/.venv/lib/python3.9/site-packages/icalendar/timezone/zoneinfo.py new file mode 100644 index 0000000..3336861 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/timezone/zoneinfo.py @@ -0,0 +1,160 @@ +"""Use zoneinfo timezones""" + +from __future__ import annotations + +from icalendar.tools import is_date, to_datetime + +try: + import zoneinfo +except ImportError: + from backports import zoneinfo # type: ignore # noqa: PGH003 +import copy +import copyreg +import functools +from datetime import datetime, tzinfo +from io import StringIO +from typing import TYPE_CHECKING, Optional + +from dateutil.rrule import rrule, rruleset +from dateutil.tz import tzical +from dateutil.tz.tz import _tzicalvtz + +from .provider import TZProvider + +if TYPE_CHECKING: + from icalendar import cal, prop + from icalendar.prop import vDDDTypes + + +class ZONEINFO(TZProvider): + """Provide icalendar with timezones from zoneinfo.""" + + name = "zoneinfo" + utc = zoneinfo.ZoneInfo("UTC") + _available_timezones = zoneinfo.available_timezones() + + def localize(self, dt: datetime, tz: zoneinfo.ZoneInfo) -> datetime: + """Localize a datetime to a timezone.""" + return dt.replace(tzinfo=tz) + + def localize_utc(self, dt: datetime) -> datetime: + """Return the datetime in UTC.""" + if getattr(dt, "tzinfo", False) and dt.tzinfo is not None: + return dt.astimezone(self.utc) + return self.localize(dt, self.utc) + + def timezone(self, name: str) -> Optional[tzinfo]: + """Return a timezone with a name or None if we cannot find it.""" + try: + return zoneinfo.ZoneInfo(name) + except zoneinfo.ZoneInfoNotFoundError: + pass + except ValueError: + # ValueError: ZoneInfo keys may not be absolute paths, got: /Europe/CUSTOM + pass + + def knows_timezone_id(self, id: str) -> bool: + """Whether the timezone is already cached by the implementation.""" + return id in self._available_timezones + + def fix_rrule_until(self, rrule: rrule, ical_rrule: prop.vRecur) -> None: + """Make sure the until value works for the rrule generated from the ical_rrule.""" + if not {"UNTIL", "COUNT"}.intersection(ical_rrule.keys()): + # zoninfo does not know any transition dates after 2038 + rrule._until = datetime(2038, 12, 31, tzinfo=self.utc) + + def create_timezone(self, tz: cal.Timezone) -> tzinfo: + """Create a timezone from the given information.""" + try: + return self._create_timezone(tz) + except ValueError: + # We might have a custom component in there. + # see https://github.com/python/cpython/issues/120217 + tz = copy.deepcopy(tz) + for sub in tz.walk(): + for attr in list(sub.keys()): + if attr.lower().startswith("x-"): + sub.pop(attr) + for sub in tz.subcomponents: + start : vDDDTypes = sub.get("DTSTART") + if start and is_date(start.dt): + # ValueError: Unsupported DTSTART param in VTIMEZONE: VALUE=DATE + sub.DTSTART = to_datetime(start.dt) + return self._create_timezone(tz) + + def _create_timezone(self, tz: cal.Timezone) -> tzinfo: + """Create a timezone and maybe fail""" + file = StringIO(tz.to_ical().decode("UTF-8", "replace")) + return tzical(file).get() + + def uses_pytz(self) -> bool: + """Whether we use pytz.""" + return False + + def uses_zoneinfo(self) -> bool: + """Whether we use zoneinfo.""" + return True + + +def pickle_tzicalvtz(tzicalvtz: tz._tzicalvtz): + """Because we use dateutil.tzical, we need to make it pickle-able.""" + return _tzicalvtz, (tzicalvtz._tzid, tzicalvtz._comps) + + +copyreg.pickle(_tzicalvtz, pickle_tzicalvtz) + + +def pickle_rrule_with_cache(self: rrule): + """Make sure we can also pickle rrules that cache. + + This is mainly copied from rrule.replace. + """ + 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) + # from https://stackoverflow.com/a/64915638/1320237 + return functools.partial(rrule, new_kwargs.pop("freq"), **new_kwargs), () + + +copyreg.pickle(rrule, pickle_rrule_with_cache) + + +def pickle_rruleset_with_cache(rs: rruleset): + """Pickle an rruleset.""" + # self._rrule = [] + # self._rdate = [] + # self._exrule = [] + # self._exdate = [] + return unpickle_rruleset_with_cache, ( + rs._rrule, + rs._rdate, + rs._exrule, + rs._exdate, + False if rs._cache is None else True, + ) + + +def unpickle_rruleset_with_cache(rrule, rdate, exrule, exdate, cache): + """unpickling the rruleset.""" + rs = rruleset(cache) + for o in rrule: + rs.rrule(o) + for o in rdate: + rs.rdate(o) + for o in exrule: + rs.exrule(o) + for o in exdate: + rs.exdate(o) + return rs + + +copyreg.pickle(rruleset, pickle_rruleset_with_cache) + +__all__ = ["ZONEINFO"] diff --git a/.venv/lib/python3.9/site-packages/icalendar/tools.py b/.venv/lib/python3.9/site-packages/icalendar/tools.py new file mode 100644 index 0000000..5badd82 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tools.py @@ -0,0 +1,113 @@ +"""Utility functions for icalendar.""" + +from __future__ import annotations + +import random +from datetime import date, datetime, tzinfo +from string import ascii_letters, digits +from warnings import warn + +from icalendar.parser_tools import to_unicode + +from .error import WillBeRemovedInVersion7 + + +class UIDGenerator: + """Use this only if you're too lazy to create real UUIDs. + + .. deprecated:: 6.2.1 + + Use the Python standard library's :func:`uuid.uuid4` instead. + """ + + chars = list(ascii_letters + digits) + + @staticmethod + def rnd_string(length=16) -> str: + """Generates a string with random characters of length. + + .. deprecated:: 6.2.1 + + Use the Python standard library's :func:`uuid.uuid4` instead. + """ + warn( + "Use https://docs.python.org/3/library/uuid.html#uuid.uuid4 instead.", + WillBeRemovedInVersion7, + stacklevel=1 + ) + return "".join([random.choice(UIDGenerator.chars) for _ in range(length)]) + + @staticmethod + def uid(host_name="example.com", unique=""): + """Generates a unique ID consisting of ``datetime-uniquevalue@host``. + + For example: + + .. code-block:: text + + 20050105T225746Z-HKtJMqUgdO0jDUwm@example.com + + .. deprecated:: 6.2.1 + + Use the Python standard library's :func:`uuid.uuid5` instead. + """ + from icalendar.prop import vDatetime, vText + warn( + "Use https://docs.python.org/3/library/uuid.html#uuid.uuid5 instead.", + WillBeRemovedInVersion7, + stacklevel=1 + ) + + host_name = to_unicode(host_name) + unique = unique or UIDGenerator.rnd_string() + today = to_unicode(vDatetime(datetime.today()).to_ical()) + return vText(f"{today}-{unique}@{host_name}") + + +def is_date(dt: date) -> bool: + """Whether this is a date and not a datetime.""" + return isinstance(dt, date) and not isinstance(dt, datetime) + + +def is_datetime(dt: date) -> bool: + """Whether this is a date and not a datetime.""" + return isinstance(dt, datetime) + + +def to_datetime(dt: date) -> datetime: + """Make sure we have a datetime, not a date.""" + if is_date(dt): + return datetime(dt.year, dt.month, dt.day) # noqa: DTZ001 + return dt + + +def is_pytz(tz: tzinfo): + """Whether the timezone requires localize() and normalize().""" + return hasattr(tz, "localize") + + +def is_pytz_dt(dt: date): + """Whether the time requires localize() and normalize().""" + return is_datetime(dt) and is_pytz(dt.tzinfo) + + +def normalize_pytz(dt: date): + """We have to normalize the time after a calculation if we use pytz. + + pytz requires this function to be used in order to correctly calculate the + timezone's offset after calculations. + """ + if is_pytz_dt(dt): + return dt.tzinfo.normalize(dt) + return dt + + +__all__ = [ + "UIDGenerator", + "is_date", + "is_datetime", + "to_datetime", + "is_pytz", + "is_pytz_dt", + "normalize_pytz", +] diff --git a/.venv/lib/python3.9/site-packages/icalendar/version.py b/.venv/lib/python3.9/site-packages/icalendar/version.py new file mode 100644 index 0000000..c34a78d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/version.py @@ -0,0 +1,14 @@ +"""Version file as a stable interface for the generated _version.py file.""" + +try: + from ._version import __version__, __version_tuple__, version, version_tuple +except ModuleNotFoundError: + __version__ = version = "0.0.0dev0" + __version_tuple__ = version_tuple = (0, 0, 0, "dev0") + +__all__ = [ + "__version__", + "version", + "__version_tuple__", + "version_tuple", +] diff --git a/.venv/lib/python3.9/site-packages/icalendar_searcher-1.0.3.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/icalendar_searcher-1.0.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar_searcher-1.0.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/icalendar_searcher-1.0.3.dist-info/METADATA b/.venv/lib/python3.9/site-packages/icalendar_searcher-1.0.3.dist-info/METADATA new file mode 100644 index 0000000..745ff6e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar_searcher-1.0.3.dist-info/METADATA @@ -0,0 +1,172 @@ +Metadata-Version: 2.4 +Name: icalendar-searcher +Version: 1.0.3 +Summary: Search, filter and sort iCalendar components +License-File: LICENSE.md +Author: Tobias Brox +Author-email: tobias@redpill-linpro.com +Requires-Python: >=3.9,<4.0 +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 +Provides-Extra: collation +Requires-Dist: icalendar (>=6.0) +Requires-Dist: recurring-ical-events (>=3.8.0) +Project-URL: Issues, https://github.com/python-caldav/icalendar-searcher/issues +Project-URL: Repository, https://github.com/python-caldav/icalendar-searcher +Description-Content-Type: text/markdown + +# icalendar-searcher + +This library should contain logic for searching, filtering and sorting icalendar data, as well as logic for storing and representing an icalendar search query. + +## Audience + +* This will be used for the python CalDAV client library, both for bundling search parameters together in one object, for doing client-side filtering when the server does not support the desired search query. +* This may be useful in any kind of software handling collections of calendar content and needing to do filtering or searches on it. +* This may also be useful by calendar server developers. + +## Maturity + +I've decided to release 1.0 now as I think it's getting more or less feature-complete (with the exception of advanced operators). However, the library has not been battle-tested. + +`sort` and `check_component` is used by the caldav project. It has not been used much in the wild yet, but they have been through some QA and is also tested by test-code from the caldav project. + +`filter`, `filter_calendar` and `sort_calendar` has not been tested yet, is AI-generated and is only covered by AI-generated test code. There may be dragons. + +## Maturity + +I've decided to release 1.0 now as I think it's getting more or less feature-complete (with the exception of advanced operators). The API is considered stable and will follow semantic versioning going forward. + +`sort` and `check_component` is used by the caldav project, it has not been used much in the wild yet, but has been through quite some testing. + +`filter`, `filter_calendar` and `sort_calendar` has not been tested yet, is AI-generated and is only covered by AI-generated test code. There may be dragons. + +**Not yet implemented**: operators like `!=`, `<>`, `<`, `<=`, `>`, `>=`, `~` (regex), etc. + +## Usage + +No proper usage documentation has been written yet, sorry. There are tons of inline comments and docstrings though. The AI has contributed with quite some verbose comments in the test code, but little of it has been exposed to any proper QA. + +### Basic Example + +```python +from icalendar_searcher import Searcher +from datetime import datetime + +# Create a searcher with filters +searcher = Searcher( + event=True, # Only events + start=datetime(2025, 1, 1), + end=datetime(2025, 12, 31) +) + +# Add property filters +searcher.add_property_filter("SUMMARY", "meeting", operator="contains") +searcher.add_property_filter("LOCATION", "Office", operator="contains", case_sensitive=False) + +# Check if a single component matches +result = searcher.check_component(calendar) + +# Filter a list of components +calendars = [cal1, cal2, cal3] +matching = searcher.filter(calendars) + +# Sort components +searcher.add_sort_key("DTSTART") +sorted_calendars = searcher.sort(calendars) +``` + +### Case-Sensitive vs Case-Insensitive Filtering + +By default, text filtering is **case-sensitive**. You can make it case-insensitive: + +```python +# Case-insensitive search (simple API) +searcher.add_property_filter("SUMMARY", "meeting", case_sensitive=False) + +# Case-sensitive search (default) +searcher.add_property_filter("SUMMARY", "Meeting") +``` + +Case-insensitivity is not straight-forward. It may cause non-deterministic sort results, and there may be problems with unicode characters - i.e., according to the CalDAV standard, case-insensitivity should only apply to ASCII-characters - causing mismatches between naïve and NAÏVE, cliché and CLICHÉ, smörgåsbord and SMÖRGÅSBORD, not to forget millions of words in non-English languages, complete non-latin scripts, etc. By default this library in itself handles Unicode relatively well - but for CalDAV searches, one may experience that case-insensitivity only applies to ASCII letters. + +### Category Filtering + +Two virtual properties are available for filtering by categories: + +- **"categories"** (plural): Categories is considered to be a set of categories to be matched with the set of categories found in the event. If categories is given as a string, it will be split by the commas. +- **"category"** (singular): Category is considered to be a string, to be matched literally towards at least one of the categories in the event. + +Examples: + +```python +# Match events with both "WORK" and "URGENT" categories (exact match) +searcher.add_property_filter("categories", "WORK,URGENT", operator="contains") + +# Match events where any category contains "out" (e.g., "outdoor", "outing") +searcher.add_property_filter("category", "out", operator="contains") +``` + +### Advanced Collation (requires PyICU) + +For advanced Unicode and locale-aware text comparison, install with: + +```bash +pip install 'icalendar-searcher[collation]' +``` + +Locale-specific collation is useful for proper handling of language-specific characters: + +```python +from icalendar_searcher.collation import Collation + +# Turkish locale: İstanbul matches "istanbul" (Turkish İ → i) +searcher.add_property_filter("LOCATION", "istanbul", + collation=Collation.LOCALE, + locale="tr_TR", + case_sensitive=False) + +# Without Turkish locale, İstanbul won't match "istanbul" +searcher.add_property_filter("LOCATION", "istanbul", + collation=Collation.UNICODE, + case_sensitive=False) # Won't match İstanbul + +# Locale-aware sorting (e.g., Norwegian sorts æ, ø, å after z) +searcher.add_sort_key("SUMMARY", collation=Collation.LOCALE, locale="nb_NO") +``` + +## Related projects + +* The project depends on the [icalendar](https://github.com/collective/icalendar) library, all calendar contents handled by this library is coming in and out as ``icalendar.Component`` or ``icalendar.Calendar``. However, the library should also support wrapped objects (like instances of the ``caldav.Event`` class). +* The project depends on the [recurring-ical-events](https://github.com/niccokunzmann/python-recurring-ical-events) library for expanding recurrence sets. +* The project is used by the [Python CalDAV client library](https://github.com/python-caldav/caldav) + +## History + +This is a spin-off from the python caldav project, started by Tobias Brox in 2025-11, the maintainer of the Python CalDAV client library at that time, in collaboration with the main contributors of the icalendar library. + +## AI + +The author has been experimenting a bit with AI usage while creating this project, both GitHub Copilot and Claude has been tested. Most of the test code has been created by the AI. AI has also been utilized a bit for refactoring, bug hunting, and a little bit code generation (particularly wrg of the collations support), but most of the "business logic" was written by me. + +While all the AI-generated changes in the business logic has been looked through thoroughly, very little QA has been done on the test code. + +## Performance + +In the early versions, the filter method will take the calendar contents one by one and check if it matches or not. This will work out well enough for small calendar sets. If lots of searches are to be done on fairly static data (like what typically may happen at the server side in a calendaring system), then it would be an idea to add indexes. + +The expansion part may cause millions of recurrences to be created. It even supports open-ended intervals. It's possible by returning generators. However, things will most likely blow up and eat all the CPU and memory it gets the hands on whenever one wants to do sorting of such a thing. + +## License + +As for now I'm releasing this under the GNU Affero General Public License v.3.0. If you find this too restrictive or if this causes license compatibility issues for you, I will consider to fix some dual licensing, like it's done with the python CalDAV library. + +This also means that any contributor has to accept that the code is released under AGPL v3.0 and at some point in the future may be dual-licensed under some more permissive license, like the EUPL v1.1. + +I don't have very strong opinions on licenses. If you have any issues in one way or another, please reach out. + diff --git a/.venv/lib/python3.9/site-packages/icalendar_searcher-1.0.3.dist-info/RECORD b/.venv/lib/python3.9/site-packages/icalendar_searcher-1.0.3.dist-info/RECORD new file mode 100644 index 0000000..f71fd5c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar_searcher-1.0.3.dist-info/RECORD @@ -0,0 +1,15 @@ +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar_searcher/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar_searcher/collation.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar_searcher/filters.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar_searcher/searcher.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar_searcher/utils.cpython-39.pyc,, +icalendar_searcher-1.0.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +icalendar_searcher-1.0.3.dist-info/METADATA,sha256=131RFLRAXnHU4y2ojlCiF3a8XRJ2yS-0wjTG5BBez8w,8940 +icalendar_searcher-1.0.3.dist-info/RECORD,, +icalendar_searcher-1.0.3.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88 +icalendar_searcher-1.0.3.dist-info/licenses/LICENSE.md,sha256=ADUqsZhl4juwq34PRTMiBqumpm11s_PMli_dZQjWPqQ,34260 +icalendar_searcher/__init__.py,sha256=j6McJPuV8H9bcY4yvYcjLmepk3EVJiP5WHk9cYZrYn8,562 +icalendar_searcher/collation.py,sha256=J3VrxSujIw2T1nAyJfKN1fJ8y2mlfJCFp_ye96F7IN4,7993 +icalendar_searcher/filters.py,sha256=Vs3L19qOt8WTk7FX_T69U0nEJ1KgvCtJO2VsLx3EChM,21437 +icalendar_searcher/searcher.py,sha256=Xs1sjTxJsX_bHr8mzRAcOlpOn1JiGPpi4vXhO3X7zkI,39578 +icalendar_searcher/utils.py,sha256=QbcE5sw3nWhDuQiHnPj85fZ2Uevuh7D7llKz4wM1ax8,2193 diff --git a/.venv/lib/python3.9/site-packages/icalendar_searcher-1.0.3.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/icalendar_searcher-1.0.3.dist-info/WHEEL new file mode 100644 index 0000000..90df349 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar_searcher-1.0.3.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: poetry-core 2.2.1 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.9/site-packages/icalendar_searcher-1.0.3.dist-info/licenses/LICENSE.md b/.venv/lib/python3.9/site-packages/icalendar_searcher-1.0.3.dist-info/licenses/LICENSE.md new file mode 100644 index 0000000..c6f01c6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar_searcher-1.0.3.dist-info/licenses/LICENSE.md @@ -0,0 +1,660 @@ +# GNU AFFERO GENERAL PUBLIC LICENSE + +Version 3, 19 November 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +## Preamble + +The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains +free software for all its users. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + +A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + +The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + +An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing +under this license. + +The precise terms and conditions for copying, distribution and +modification follow. + +## TERMS AND CONDITIONS + +### 0. Definitions. + +"This License" refers to version 3 of the GNU Affero General Public +License. + +"Copyright" also means copyright-like laws that apply to other kinds +of works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of +an exact copy. The resulting work is called a "modified version" of +the earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user +through a computer network, with no transfer of a copy, is not +conveying. + +An interactive user interface displays "Appropriate Legal Notices" to +the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +### 1. Source Code. + +The "source code" for a work means the preferred form of the work for +making modifications to it. "Object code" means any non-source form of +a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can +regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same +work. + +### 2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, +without conditions so long as your license otherwise remains in force. +You may convey covered works to others for the sole purpose of having +them make modifications exclusively for you, or provide you with +facilities for running those works, provided that you comply with the +terms of this License in conveying all material for which you do not +control copyright. Those thus making or running the covered works for +you must do so exclusively on your behalf, under your direction and +control, on terms that prohibit them from making any copies of your +copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 makes +it unnecessary. + +### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such +circumvention is effected by exercising rights under this License with +respect to the covered work, and you disclaim any intention to limit +operation or modification of the work as a means of enforcing, against +the work's users, your or third parties' legal rights to forbid +circumvention of technological measures. + +### 4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +### 5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these +conditions: + +- a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. +- b) The work must carry prominent notices stating that it is + released under this License and any conditions added under + section 7. This requirement modifies the requirement in section 4 + to "keep intact all notices". +- c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. +- d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +### 6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of +sections 4 and 5, provided that you also convey the machine-readable +Corresponding Source under the terms of this License, in one of these +ways: + +- a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. +- b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the Corresponding + Source from a network server at no charge. +- c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. +- d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. +- e) Convey the object code using peer-to-peer transmission, + provided you inform other peers where the object code and + Corresponding Source of the work are being offered to the general + public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, +family, or household purposes, or (2) anything designed or sold for +incorporation into a dwelling. In determining whether a product is a +consumer product, doubtful cases shall be resolved in favor of +coverage. For a particular product received by a particular user, +"normally used" refers to a typical or common use of that class of +product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected +to use, the product. A product is a consumer product regardless of +whether the product has substantial commercial, industrial or +non-consumer uses, unless such uses represent the only significant +mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to +install and execute modified versions of a covered work in that User +Product from a modified version of its Corresponding Source. The +information must suffice to ensure that the continued functioning of +the modified object code is in no case prevented or interfered with +solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or +updates for a work that has been modified or installed by the +recipient, or for the User Product in which it has been modified or +installed. Access to a network may be denied when the modification +itself materially and adversely affects the operation of the network +or violates the rules and protocols for communication across the +network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +### 7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders +of that material) supplement the terms of this License with terms: + +- a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or +- b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or +- c) Prohibiting misrepresentation of the origin of that material, + or requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or +- d) Limiting the use for publicity purposes of names of licensors + or authors of the material; or +- e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or +- f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions + of it) with contractual assumptions of liability to the recipient, + for any liability that these contractual assumptions directly + impose on those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; the +above requirements apply either way. + +### 8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +### 9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run +a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +### 10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +### 11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims owned +or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within the +scope of its coverage, prohibits the exercise of, or is conditioned on +the non-exercise of one or more of the rights that are specifically +granted under this License. You may not convey a covered work if you +are a party to an arrangement with a third party that is in the +business of distributing software, under which you make payment to the +third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties +who would receive the covered work from you, a discriminatory patent +license (a) in connection with copies of the covered work conveyed by +you (or copies made from those copies), or (b) primarily for and in +connection with specific products or compilations that contain the +covered work, unless you entered into that arrangement, or that patent +license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +### 12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under +this License and any other pertinent obligations, then as a +consequence you may not convey it at all. For example, if you agree to +terms that obligate you to collect a royalty for further conveying +from those to whom you convey the Program, the only way you could +satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +### 13. Remote Network Interaction; Use with the GNU General Public License. + +Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your +version supports such interaction) an opportunity to receive the +Corresponding Source of your version by providing access to the +Corresponding Source from a network server at no charge, through some +standard or customary means of facilitating copying of software. This +Corresponding Source shall include the Corresponding Source for any +work covered by version 3 of the GNU General Public License that is +incorporated pursuant to the following paragraph. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + +### 14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions +of the GNU Affero General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever +published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions +of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +### 15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +### 16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR +CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR +LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM +TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER +PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +### 17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +## How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively state +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper +mail. + +If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for +the specific requirements. + +You should also get your employer (if you work as a programmer) or +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. For more information on this, and how to apply and follow +the GNU AGPL, see . diff --git a/.venv/lib/python3.9/site-packages/icalendar_searcher/__init__.py b/.venv/lib/python3.9/site-packages/icalendar_searcher/__init__.py new file mode 100644 index 0000000..d3a4a4c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar_searcher/__init__.py @@ -0,0 +1,21 @@ +"""icalendar-searcher: Search and filter iCalendar components. + +This package provides the Searcher class for filtering, sorting, and +expanding calendar components (VEVENT, VTODO, VJOURNAL). +""" + +## for python 3.9 support +from __future__ import annotations + +from .collation import Collation +from .searcher import Searcher + +__all__ = ["Searcher", "Collation"] + +# Version is set by poetry-dynamic-versioning at build time +try: + from importlib.metadata import version + + __version__ = version("icalendar-searcher") +except Exception: + __version__ = "unknown" diff --git a/.venv/lib/python3.9/site-packages/icalendar_searcher/collation.py b/.venv/lib/python3.9/site-packages/icalendar_searcher/collation.py new file mode 100644 index 0000000..ae968ba --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar_searcher/collation.py @@ -0,0 +1,232 @@ +"""Text collation support for string comparisons. + +This module provides collation (text comparison) functionality with optional +PyICU support for advanced Unicode collation. Falls back to simple binary +and case-insensitive comparisons when PyICU is not available. +""" + +from __future__ import annotations + +from collections.abc import Callable +from enum import Enum + +# Try to import PyICU for advanced collation support +try: + from icu import Collator as ICUCollator + from icu import Locale as ICULocale + + HAS_PYICU = True +except ImportError: + HAS_PYICU = False + + +class Collation(str, Enum): + """Text comparison collation strategies. + + For most users, use case_sensitive parameter in add_property_filter() + instead of working with Collation directly. + + Examples: + # Simple API (recommended for most users): + searcher.add_property_filter("SUMMARY", "meeting", case_sensitive=False) + + # Advanced API (for power users): + searcher.add_property_filter("SUMMARY", "Müller", + collation=Collation.LOCALE, + locale="de_DE") + """ + + SIMPLE = "simple" + """Simple Python-based collation (no PyICU required). + + - case_sensitive=True: Byte-for-byte comparison + - case_sensitive=False: Python's str.lower() comparison + """ + + UNICODE = "unicode" + """Unicode Collation Algorithm (UCA) root collation. + + - case_sensitive=True: ICU TERTIARY strength (distinguishes case) + - case_sensitive=False: ICU SECONDARY strength (ignores case) + + Requires PyICU to be installed.""" + + LOCALE = "locale" + """Locale-aware collation using CLDR rules. + + - case_sensitive=True: ICU TERTIARY strength (distinguishes case) + - case_sensitive=False: ICU SECONDARY strength (ignores case) + + Requires PyICU to be installed and locale parameter.""" + + +class CollationError(Exception): + """Raised when collation operation cannot be performed.""" + + pass + + +def get_collation_function( + collation: Collation = Collation.SIMPLE, + case_sensitive: bool = True, + locale: str | None = None, +) -> Callable[[str, str], bool]: + """Get a collation function for substring matching. + + Args: + collation: The collation strategy to use + case_sensitive: Whether comparison should be case-sensitive + locale: Locale string (e.g., "de_DE", "en_US") for LOCALE collation + + Returns: + A function that takes (needle, haystack) and returns True if needle + is found in haystack according to the collation rules. + + Raises: + CollationError: If PyICU is required but not available, or if + invalid parameters are provided. + + Examples: + >>> match_fn = get_collation_function(Collation.SIMPLE, case_sensitive=False) + >>> match_fn("test", "This is a TEST") + True + """ + if collation == Collation.SIMPLE: + if case_sensitive: + return _binary_contains + else: + return _case_insensitive_contains + + elif collation in (Collation.UNICODE, Collation.LOCALE): + if not HAS_PYICU: + raise CollationError( + f"Collation '{collation}' requires PyICU to be installed. " + "Install with: pip install 'icalendar-searcher[collation]'" + ) + + if collation == Collation.LOCALE: + if not locale: + raise CollationError("LOCALE collation requires a locale parameter") + return _get_icu_contains(locale, case_sensitive) + else: + # UNICODE collation uses root locale + return _get_icu_contains(None, case_sensitive) + + else: + raise CollationError(f"Unknown collation: {collation}") + + +def get_sort_key_function( + collation: Collation = Collation.SIMPLE, + case_sensitive: bool = True, + locale: str | None = None, +) -> Callable[[str], bytes]: + """Get a collation function for generating sort keys. + + Args: + collation: The collation strategy to use + case_sensitive: Whether comparison should be case-sensitive + locale: Locale string (e.g., "de_DE", "en_US") for LOCALE collation + + Returns: + A function that takes a string and returns a sort key (bytes) that + can be used for sorting according to the collation rules. + + Raises: + CollationError: If PyICU is required but not available, or if + invalid parameters are provided. + + Examples: + >>> sort_key_fn = get_sort_key_function(Collation.SIMPLE, case_sensitive=False) + >>> sorted(["Zebra", "apple", "Banana"], key=sort_key_fn) + ['apple', 'Banana', 'Zebra'] + """ + if collation == Collation.SIMPLE: + if case_sensitive: + return lambda s: s.encode("utf-8") + else: + return lambda s: s.lower().encode("utf-8") + + elif collation in (Collation.UNICODE, Collation.LOCALE): + if not HAS_PYICU: + raise CollationError( + f"Collation '{collation}' requires PyICU to be installed. " + "Install with: pip install 'icalendar-searcher[collation]'" + ) + + if collation == Collation.LOCALE: + if not locale: + raise CollationError("LOCALE collation requires a locale parameter") + return _get_icu_sort_key(locale, case_sensitive) + else: + # UNICODE collation uses root locale + return _get_icu_sort_key(None, case_sensitive) + + else: + raise CollationError(f"Unknown collation: {collation}") + + +# ============================================================================ +# Internal implementation functions +# ============================================================================ + + +def _binary_contains(needle: str, haystack: str) -> bool: + """Binary (case-sensitive) substring match.""" + return needle in haystack + + +def _case_insensitive_contains(needle: str, haystack: str) -> bool: + """Case-insensitive substring match.""" + return needle.lower() in haystack.lower() + + +def _get_icu_contains(locale: str | None, case_sensitive: bool) -> Callable[[str, str], bool]: + """Get ICU-based substring matcher. + + Note: This is a simplified implementation. PyICU doesn't expose ICU's + StringSearch API which would be needed for proper substring matching with + collation. For now, we use Python's built-in matching. + + Future enhancement: Implement proper collation-aware substring matching. + """ + + def icu_contains(needle: str, haystack: str) -> bool: + """Check if needle is in haystack. + + This is a fallback implementation until proper ICU StringSearch support + is added. It provides reasonable behavior for most use cases. + """ + # TODO: Use ICU StringSearch for proper collation-aware substring matching + # For now, fall back to Python's built-in contains + if case_sensitive: + return needle in haystack + else: + return needle.lower() in haystack.lower() + + return icu_contains + + +def _get_icu_sort_key(locale: str | None, case_sensitive: bool) -> Callable[[str], bytes]: + """Get ICU-based sort key function. + + Creates a collator instance and returns a function that generates sort keys. + The collator strength is configured based on case_sensitive parameter. + """ + icu_locale = ICULocale(locale) if locale else ICULocale.getRoot() + collator = ICUCollator.createInstance(icu_locale) + + # Set strength based on case sensitivity: + # PRIMARY = base character differences only + # SECONDARY = base + accent differences (case-insensitive) + # TERTIARY = base + accent + case differences (case-sensitive, default) + if case_sensitive: + collator.setStrength(ICUCollator.TERTIARY) + else: + collator.setStrength(ICUCollator.SECONDARY) + + def icu_sort_key(s: str) -> bytes: + """Generate ICU collation sort key.""" + return collator.getSortKey(s) + + return icu_sort_key diff --git a/.venv/lib/python3.9/site-packages/icalendar_searcher/filters.py b/.venv/lib/python3.9/site-packages/icalendar_searcher/filters.py new file mode 100644 index 0000000..4e9bbe4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar_searcher/filters.py @@ -0,0 +1,456 @@ +"""Filtering logic for icalendar components.""" + +from collections.abc import Iterable +from datetime import datetime, timedelta + +from icalendar import Component, error +from icalendar.prop import vCategory, vText +from recurring_ical_events import DATE_MAX_DT, DATE_MIN_DT + +from .collation import Collation, get_collation_function +from .utils import _normalize_dt + + +class FilterMixin: + """Mixin class providing filtering methods for calendar components. + + This class is meant to be mixed into the Searcher dataclass. + It expects the following attributes to be available on self: + - start, end: datetime range filters + - alarm_start, alarm_end: alarm range filters + - include_completed: bool for filtering completed todos + - _property_filters: dict of property filters + - _property_operator: dict of property operators + - _property_collation: dict of property collations + - _property_locale: dict of property locales + """ + + def _check_range(self, component: Component) -> bool: + """Check if a component falls within the time range specified by self.start and self.end. + + Implements RFC4791 section 9.9 time-range filtering logic for VEVENT, VTODO, and VJOURNAL. + + :param component: A single calendar component (VEVENT, VTODO, or VJOURNAL) + :return: True if the component matches the time range, False otherwise + """ + comp_name = component.name + + ## The logic below should correspond neatly with RFC4791 section 9.9 + + ## fetch comp_end and comp_start + ## note that comp_end is DTSTART + DURATION if DURATION is given. + ## note that for tasks, comp_end is set to DUE + ## This logic is all handled by the start/end properties in the + ## icalendar library, and makes the logic here less complex + + try: + comp_end = _normalize_dt(component.end) + except error.IncompleteComponent: + comp_end = None + + try: + comp_start = _normalize_dt(component.start) + except error.IncompleteComponent: + if component.name == "VEVENT": + ## for events, DTSTART is mandatory + raise + comp_start = None + + if comp_name == "VEVENT": + ## comp_start is always set. + if not comp_end and isinstance(comp_start, datetime): + ## if comp_end is not set and comp_start is a datetime, + ## consider zero duration + comp_end = comp_start + elif not comp_end: + ## if comp_end is not set and comp_start is a datetime, + ## consider one day duration + comp_end = comp_start + timedelta(day=1) + ## TODO: What time of the day does the day change? + ## Time zones are difficult! TODO: as for now, + ## self.start is a datetime, but in the future dates + ## should be allowed as well. Then the time zone + ## problem for full-day events is at least simplified. + + elif comp_name == "VTODO": + ## There is a long matrix for VTODO in the RFC, and it + ## may seem complicated, but it isn't that bad: + + ## * A task with DTSTART and DURATION is equivalent with a + ## task with DTSTART and DUE. This complexity is + ## already handled by the icalendar library, so all rows + ## in the matrix where VTODO has the DURATION property?" + ## is Y may be removed. + ## + ## * If either DUE or DTSTART is set, use it. + if comp_end and not comp_start: + comp_start = comp_end + if comp_start and not comp_end: + comp_end = comp_start + + ## * If both created/completed is set and + ## comp_start/comp_end is not set, then use those instead + if not comp_start: + if "CREATED" in component: + comp_start = _normalize_dt(component["CREATED"].dt) + if "COMPLETED" in component: + comp_end = _normalize_dt(component["COMPLETED"].dt) + + ## * A task may have a DUE before the DTSTART. The + ## complicated OR-logic in the table may be eliminated + ## by swapping start/end if necessary: + if comp_end and comp_start and comp_end < comp_start: + tmp = comp_start + comp_start = comp_end + comp_end = tmp + + ## * A task with no timestamps is considered to be done "at any or all days". + if not comp_end and not comp_start: + comp_start = _normalize_dt(DATE_MIN_DT) + comp_end = _normalize_dt(DATE_MAX_DT) + + elif comp_name == "VJOURNAL": + if not comp_start: + ## Journal without DTSTART doesn't match time ranges + return False + if isinstance(comp_start, datetime): + comp_end = comp_start + else: + comp_end = comp_start + timedelta(days=1) + + if comp_start == comp_end: + ## Now the match requirement is start <= comp_end + ## while otherwise the match requirement is start < comp_end + ## minor detail, we'll work around it: + comp_end += timedelta(seconds=1) + + ## After the logic above, all rows in the matrix boils down to + ## this: (we could reduce it even more by defaulting + ## self.start and self.end to DATE_MIN_DT etc) + if self.start and self.end and comp_end: + return self.start < comp_end and self.end > comp_start + elif self.end: + return self.end > comp_start + elif self.start and comp_end: + return self.start < comp_end + return True + + def _check_completed_filter(self, component: Component) -> bool: + """Check if a component should be included based on the include_completed filter. + + :param component: A single calendar component + :return: True if the component should be included, False if it should be filtered out + """ + if self.include_completed: + return True + + ## If include_completed is False, exclude completed/cancelled VTODOs + ## Include everything that is not a VTODO, or VTODOs that are not completed/cancelled + if component.name != "VTODO": + return True + + ## For VTODOs, exclude if STATUS is COMPLETED or CANCELLED, or if COMPLETED property is set + status = component.get("STATUS", "NEEDS-ACTION") + if status in ("COMPLETED", "CANCELLED"): + return False + if "COMPLETED" in component: + return False + + return True + + ## DISCLAIMER: partly AI-generated code. Refactored a bit by human hands + ## Should be refactored more, there is quite some code duplication here. + ## Code duplication is bad, IMO. + def _check_property_filters(self, component: Component) -> bool: + """Check if a component matches all property filters. + + :param component: A single calendar component + :return: True if the component matches all property filters, False otherwise + """ + for key, operator in self._property_operator.items(): + filter_value = self._property_filters.get(key) + + # Map "category" (singular) to "CATEGORIES" (plural) in the component + if key in ("categories", "category"): + comp_key = "categories" + comp_value = set([str(x) for x in component.categories]) + else: + comp_key = key + comp_value = component.get(comp_key) + + # Get collation settings for this property + collation = self._property_collation.get(key, Collation.SIMPLE) + locale = self._property_locale.get(key) + case_sensitive = self._property_case_sensitive.get(key, True) + + ## "categories" (plural) needs special preprocessing - split on commas + if key == "categories" and comp_value is not None and filter_value is not None: + if isinstance(filter_value, vCategory): + ## TODO: This special case, handling one element different from several, is a bit bad indeed + if len(filter_value.cats) == 1: + filter_value = str(filter_value.cats[0]) + if "," in filter_value: + filter_value = set(filter_value.split(",")) + else: + filter_value = set([str(x) for x in filter_value.cats]) + elif isinstance(filter_value, str) or isinstance(filter_value, vText): + ## TODO: probably this is irrelevant dead code + filter_value = str(filter_value) + if "," in filter_value: + filter_value = set(filter_value.split(",")) + elif isinstance(filter_value, Iterable): + ## TODO: probably this is irrelevant dead code + # Convert iterable to set, splitting on commas if strings contain them + result_set = set() + for item in filter_value: + item_str = str(item) + if "," in item_str: + result_set.update(item_str.split(",")) + else: + result_set.add(item_str) + filter_value = result_set + if operator == "undef": + ## Property should NOT be defined + if comp_key in component: + return False + elif operator == "contains": + ## Property should contain the filter value (substring match) + if comp_key not in component: + return False + if key == "category": + # "category" (singular) does substring matching within category names + # comp_value is a vCategory object + if comp_value is not None: + filter_str = str(filter_value) + # Check if filter_str is a substring of any category + collation_fn = get_collation_function(collation, case_sensitive, locale) + for cat in comp_value: + if collation_fn(filter_str, cat): + return True + return False + if key == "categories": + # For categories, "contains" means filter categories is a subset of component categories + # filter_value can be a string (single category) or set (multiple categories) + if isinstance(filter_value, str): + # Single category: check if it's in component categories + if not case_sensitive: + return any(filter_value.lower() == cv.lower() for cv in comp_value) + else: + return filter_value in comp_value + else: + # Multiple categories (set): check if all are in component categories (subset check) + assert isinstance(filter_value, set), ( + f"Expected set but got {type(filter_value)}" + ) + for fv in filter_value: + if not case_sensitive: + if not any(fv.lower() == cv.lower() for cv in comp_value): + return False + else: + if fv not in comp_value: + return False + return True + + ## Convert to string for substring matching + comp_str = str(comp_value) + filter_str = str(filter_value) + + # Use collation function for text matching + collation_fn = get_collation_function(collation, case_sensitive, locale) + if not collation_fn(filter_str, comp_str): + return False + elif operator == "==": + ## Property should exactly match the filter value + if comp_key not in component: + return False + + ## For "category" (singular), check exact match to at least one category name + if key == "category": + if comp_value is not None: + filter_str = str(filter_value) + # Check if filter_str exactly matches any category + for cat in comp_value: + if not case_sensitive: + if filter_str.lower() == cat.lower(): + return True + else: + if filter_str == cat: + return True + return False + + ## For categories, check exact set equality with collation support + if key == "categories": + # filter_value can be a string (single category) or set (multiple categories) + assert isinstance(comp_value, set), f"Expected set but got {type(comp_value)}" + + if isinstance(filter_value, str): + # Single category with "==" operator: component must have exactly that one category + if len(comp_value) != 1: + return False + if not case_sensitive: + return filter_value.lower() == list(comp_value)[0].lower() + else: + return filter_value in comp_value + else: + # Multiple categories (set): check exact equality with collation + assert isinstance(filter_value, set), ( + f"Expected set but got {type(filter_value)}" + ) + if len(filter_value) != len(comp_value): + return False + # Check if all filter categories have a matching component category + for fv in filter_value: + found = False + for cv in comp_value: + if not case_sensitive: + if fv.lower() == cv.lower(): + found = True + break + else: + if fv == cv: + found = True + break + if not found: + return False + return True + + ## Compare the values This is tricky, as the values + ## may have different types. TODO: we should add more + ## logic for the different property types. Maybe get + ## it into the icalendar library. + if comp_value == filter_value: + return True + if isinstance(filter_value, str) and isinstance(comp_value, set): + return filter_value in comp_value + + # For text properties, use collation for exact match comparison + if isinstance(filter_value, (str, vText)) and isinstance(comp_value, (str, vText)): + comp_str = str(comp_value) + filter_str = str(filter_value) + + # Use collation-specific comparison + if collation == Collation.SIMPLE: + if case_sensitive: + return comp_str == filter_str + else: + return comp_str.lower() == filter_str.lower() + elif collation in (Collation.UNICODE, Collation.LOCALE): + # For UNICODE/LOCALE collations, use sort keys for comparison + # Two strings are equal if they have the same sort key + from .collation import get_sort_key_function + + sort_key_fn = get_sort_key_function(collation, case_sensitive, locale) + return sort_key_fn(comp_str) == sort_key_fn(filter_str) + + return False + else: + ## This shouldn't happen as add_property_filter validates operators + raise NotImplementedError(f"Operator {operator} not implemented") + + return True + + ## DISCLAIMER: Mostly AI-generated code, with a touch of human polishing + ## and bugfixing. Alarms are a bit complex. + def _check_alarm_range(self, component: Component) -> bool: + """Check if a component has alarms that fire within the alarm time range. + + Implements RFC 4791 section 9.9 alarm time-range filtering. + + :param component: A single calendar component (VEVENT, VTODO, or VJOURNAL) + :return: True if any alarm fires within the alarm range, False otherwise + """ + from datetime import timedelta + + ## Get all VALARM subcomponents + alarms = [x for x in component.subcomponents if x.name == "VALARM"] + + if not alarms: + ## No alarms - doesn't match alarm search + return False + + ## Get component start/end for relative trigger calculations + ## Use try/except because .start/.end may raise IncompleteComponent + ## For VTODO, RFC 5545 says TRIGGER is relative to DUE if present, else DTSTART + comp_start = None + comp_end = None + try: + comp_start = _normalize_dt(component.start) + except error.IncompleteComponent: + pass + try: + comp_end = _normalize_dt(component.end) + except error.IncompleteComponent: + pass + + ## For each alarm, calculate when it fires + for alarm in alarms: + if "TRIGGER" not in alarm: + continue + + trigger = alarm["TRIGGER"] + + ## The icalendar library stores trigger values in .dt attribute + ## which can be either datetime (absolute) or timedelta (relative) + if not hasattr(trigger, "dt"): + continue + + trigger_value = trigger.dt + + ## Check if trigger is absolute (datetime) or relative (timedelta) + if isinstance(trigger_value, timedelta): + ## Relative trigger - timedelta from start or end + trigger_delta = trigger_value + + ## Check TRIGGER's RELATED parameter (default is START) + ## For VTODO, default anchor is DUE if present, else DTSTART + related = "START" # Default per RFC 5545 + if hasattr(trigger, "params") and "RELATED" in trigger.params: + related = trigger.params["RELATED"] + + ## Calculate alarm time based on RELATED and component type + if related == "END" and comp_end: + alarm_time = comp_end + trigger_delta + elif comp_start: + alarm_time = comp_start + trigger_delta + else: + ## No start, end, or due to relate to + continue + else: + ## Absolute trigger - direct datetime + alarm_time = _normalize_dt(trigger_value) + + ## Check for REPEAT and DURATION (repeating alarms/snooze functionality) + ## Check all repetitions, not just the first alarm + if "REPEAT" in alarm and "DURATION" in alarm: + repeat_count = alarm["REPEAT"] + duration = alarm["DURATION"].dt if hasattr(alarm["DURATION"], "dt") else None + + if duration: + ## Check each repetition + for i in range(int(repeat_count) + 1): + repeat_time = alarm_time + (duration * i) + ## Check if this repetition fires within the alarm range + if self.alarm_start and self.alarm_end: + if self.alarm_start <= repeat_time < self.alarm_end: + return True + elif self.alarm_start: + if repeat_time >= self.alarm_start: + return True + elif self.alarm_end: + if repeat_time < self.alarm_end: + return True + ## None of the repetitions matched + continue + + ## Check if this alarm (first occurrence) fires within the alarm range + if self.alarm_start and self.alarm_end: + if self.alarm_start <= alarm_time < self.alarm_end: + return True + elif self.alarm_start: + if alarm_time >= self.alarm_start: + return True + elif self.alarm_end: + if alarm_time < self.alarm_end: + return True + + return False diff --git a/.venv/lib/python3.9/site-packages/icalendar_searcher/searcher.py b/.venv/lib/python3.9/site-packages/icalendar_searcher/searcher.py new file mode 100644 index 0000000..077af23 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar_searcher/searcher.py @@ -0,0 +1,886 @@ +"""Main Searcher class for icalendar component filtering and sorting.""" + +from __future__ import annotations + +import logging +from collections.abc import Iterable +from dataclasses import dataclass, field +from datetime import datetime +from typing import TYPE_CHECKING, Any + +import recurring_ical_events +from icalendar import Calendar, Component, Timezone +from recurring_ical_events import DATE_MAX_DT, DATE_MIN_DT + +from .collation import Collation, get_sort_key_function +from .filters import FilterMixin +from .utils import _iterable_or_false, _normalize_dt, types_factory + +if TYPE_CHECKING: + from caldav.calendarobjectresource import CalendarObjectResource + + +@dataclass +class Searcher(FilterMixin): + """This class will: + + * Allow to build up a calendar search query. + * Help filterering or sorting a list of calendar components + + Primarily VJOURNAL, VTODO and VEVENT Calendar components are + intended to be supported, but we should consider if there is any + point supporting FREEBUSY as well. + + This class is a bit stubbed as of 2025-11, many things not working + yet. This class was split out from the CalDAV library and is + intended for generic search logic not related to the CalDAV + protocol. As of 2025-11, the first priority is to make the bare + minimum of support needed for supporting refactorings of the + CalDAV library. + + Long-term plan is to also allow for adding subqueries that can be + bound together with logical AND or OR. (Will AND every be needed? + After all, add different criterias to one Searcher object, and all + of them has to match) + + Properties (like SUMMARY, CATEGORIES, etc) are not meant to be + sent through the constructor, use the :func:`icalendar_searcher.Searcher.add_property_filter` + method. Same goes with sort keys, they can be added through the + `func:icalendar_searcher.Searcher.add_sort_key` method. + + The ``todo``, ``event`` and ``journal`` parameters are booleans + for filtering the component type. If i.e. both todo and + journal is set to True, everything but events should be returned. + If none is given (the default), all objects should be returned. + + If ``todo`` is given ``include_completed`` defaults to False, + which means completed tasks will be tfiltered out. + + ``start`` and ``end`` is giving a time range. RFC4791, section + 9.9 gives a very clear and sane definitions of what should be + returned when searching a CalDAV calendar for contents over a time + span. While this package is not related to CalDAV pper se, the + logic seems sane, so we will stick to that one. Timestamps should + ideally be with a time zone, if not given the local time zone will + be assumed. All-day events may be tricky to get correct when + timestamps are given and calendar data covers multiple time zones. + + ``alarm_start`` and ``alarm_end`` is similar for alarm searching + + If ``expand`` is set to True, recurring objects will be expanded + into reccurence objects for the time period specified by ``start`` + and ``end``. Generators are used, so ``expand`` may even work + without ``start`` and ``end`` set if care is taken + (i.e. converting the generator to a list may cause problems) + + Unless you now what you are doing, ``expand`` should probably be + set to true, and start and end should be given and shouldn't be + too far away from each other. Currently the sorting algorithm + will blow up if the expand yields infinite number of events. It + may probably blow up even if expand yields millions of events. Be + careful. + + For filtering an icalendar instance ``mycal`` containing a + VCALENDAR with multiple independent events, one may do + ``searcher.filter([mycal])``. The CalDAV library contains a + CalDAVSearcher class inheritating this class and including a + ``.search(caldav_calendar)`` method. The idea is that it may be + done in the same way also for searching other calendars or + calendar-like systems using other protocols. + + The filtering and sorting methods should accept both wrapper + objects (for the CalDAV library, those are called + CalendarObjectResource and contains extra information like the URL + and the client object) and ``icalendar.Calendar`` objects from the + icalendar library. Wrapper objects should have the + ``icalendar.Calendar`` object available through a property + ``icalendar_instance``. Even for methods expecting a simple + component, a ``Calendar`` should be given. This is of + imporantance for recurrences with exceptions (like, "daily meeting + is held at 10:00, except today the meeting is postponed until + 12:00" may be represented through a calendar with two + subcomponents) + + Methods expecting multiple components will always expect a list of + calendars (CalDAV-style) - but the algorithms should also be + robust enough to handle multiple independent components embedded + in a single Calendar. + + Other ideas that one may consider implementing: + * limit, offset. + * fuzzy matching + + """ + + todo: bool = None + event: bool = None + journal: bool = None + start: datetime = None + end: datetime = None + alarm_start: datetime = None + alarm_end: datetime = None + include_completed: bool = None + + expand: bool = False + + _sort_keys: list = field(default_factory=list) + _sort_collation: dict = field(default_factory=dict) + _sort_locale: dict = field(default_factory=dict) + _sort_case_sensitive: dict = field(default_factory=dict) + _property_filters: dict = field(default_factory=dict) + _property_operator: dict = field(default_factory=dict) + _property_collation: dict = field(default_factory=dict) + _property_locale: dict = field(default_factory=dict) + _property_case_sensitive: dict = field(default_factory=dict) + + def add_property_filter( + self, + key: str, + value: Any, + operator: str = "contains", + case_sensitive: bool = True, + collation: Collation | None = None, + locale: str | None = None, + ) -> None: + """Adds a filter for some specific iCalendar property. + + Examples of valid iCalendar properties: SUMMARY, + LOCATION, DESCRIPTION, DTSTART, STATUS, CLASS, etc + + :param key: must be an icalendar property, i.e. SUMMARY + Special virtual property "category" (singular) is also supported + for substring matching within category names + :param value: should adhere to the type defined in the RFC + :param operator: Comparision operator ("contains", "==", etc) + :param case_sensitive: If False, text comparisons are case-insensitive. + :param collation: Advanced collation strategy for text comparison. + If specified, overrides case_sensitive parameter. + Only needed by power users for locale-aware collation. + :param locale: Locale string (e.g., "de_DE") for locale-aware collation. + Only used with collation=Collation.LOCALE. + + The case_sensitive parameter only applies to text properties. + The default has been made for case sensitive searches. This + is contrary to the CalDAV standard, where case insensitive + searches are considered the default, but it's in accordance + with the CalDAV library, where case sensitive searches has + been the default. + + **Special handling for categories:** + + - **"categories"** (plural): Exact category name matching + - "contains": subset check (all filter categories must be in component) + - "==": exact set equality (same categories, order doesn't matter) + - Commas in filter values split into multiple categories + + - **"category"** (singular): Substring matching within category names + - "contains": substring match (e.g., "out" matches "outdoor") + - "==": exact match to at least one category name + - Commas in filter values treated as literal characters + + For the operator, the following is (planned to be) supported: + + * contains - will do a substring match (A search for "summary" + "contains" "rain" will return both events with summary + "Training session" and "Singing in the rain") + + * == - exact match is required + + * ~ - regexp match + + * <, >, <=, >= - comparision + + * <> or != - inqueality, both supported + + * def, undef - will match if the property is (not) defined. value can be set to None, the value will be ignored. + + Examples: + # Case-insensitive search (simple API) + searcher.add_property_filter("SUMMARY", "meeting", case_sensitive=False) + + # Case-sensitive search (default) + searcher.add_property_filter("SUMMARY", "Meeting") + + # Advanced: locale-aware collation (requires PyICU) + searcher.add_property_filter("SUMMARY", "Müller", + collation=Collation.LOCALE, + locale="de_DE") + + """ + ## Special handling of property "category" (singular) vs "categories" (plural). + ## "categories" (plural): list of categories with exact matching (no substring) + ## - "contains": subset check (all filter categories must be in component) + ## - "==": exact set equality (same categories, order doesn't matter) + ## - Commas split into multiple categories + ## "category" (singular): substring matching within category names + ## - "contains": substring match (e.g., "out" matches "outdoor") + ## - "==": exact match to at least one category name + ## - Commas NOT split, treated as literal part of category name + key = key.lower() + if operator not in ("contains", "undef", "=="): + raise NotImplementedError(f"The operator {operator} is not supported yet.") + if operator != "undef": + ## Map "category" to "categories" for types_factory lookup + property_key = "categories" if key == "category" else key + + ## Special treatment for "categories" (plural): split on commas + if key == "categories" and isinstance(value, str): + ## If someone asks for FAMILY,FINANCE, they want a match on anything + ## having both those categories set, not a category literally named "FAMILY,FINANCE" + fact = types_factory.for_property(property_key) + self._property_filters[key] = fact(fact.from_ical(value)) + elif key == "category": + ## For "category" (singular), store as string (no comma splitting) + ## This allows substring matching within category names + self._property_filters[key] = value + else: + self._property_filters[key] = types_factory.for_property(property_key)(value) + self._property_operator[key] = operator + + # Determine collation strategy + if collation is not None: + # Power user specified explicit collation + self._property_collation[key] = collation + self._property_locale[key] = locale + self._property_case_sensitive[key] = case_sensitive + else: + # Simple API: use SIMPLE collation with case_sensitive parameter + self._property_collation[key] = Collation.SIMPLE + self._property_locale[key] = None + self._property_case_sensitive[key] = case_sensitive + + def add_sort_key( + self, + key: str, + reversed: bool = None, + case_sensitive: bool = True, + collation: Collation | None = None, + locale: str | None = None, + ) -> None: + """Add a sort key for sorting components. + + Special keys "isnt_overdue" and "hasnt_started" is + supported, those will compare the DUE (for a task) or the + DTSTART with the current wall clock and return a bool. + + Except for that, the sort key should be an icalendar property. + + :param key: The property name to sort by + :param reversed: If True, sort in reverse order + :param case_sensitive: If False, text sorting is case-insensitive. + Only applies to text properties. Default is True. + :param collation: Advanced collation strategy for text sorting. + If specified, overrides case_sensitive parameter. + :param locale: Locale string (e.g., "de_DE") for locale-aware sorting. + Only used with collation=Collation.LOCALE. + + Examples: + # Case-insensitive sorting (simple API) + searcher.add_sort_key("SUMMARY", case_sensitive=False) + + # Case-sensitive sorting (default) + searcher.add_sort_key("SUMMARY") + + # Advanced: locale-aware sorting (requires PyICU) + searcher.add_sort_key("SUMMARY", collation=Collation.LOCALE, locale="de_DE") + """ + key = key.lower() + assert key in types_factory.types_map or key in ( + "isnt_overdue", + "hasnt_started", + ) + self._sort_keys.append((key, reversed)) + + # Determine collation strategy for sorting + if collation is not None: + # Power user specified explicit collation + self._sort_collation[key] = collation + self._sort_locale[key] = locale + self._sort_case_sensitive[key] = case_sensitive + else: + # Simple API + self._sort_collation[key] = Collation.SIMPLE + self._sort_locale[key] = None + self._sort_case_sensitive[key] = case_sensitive + + def check_component( + self, + component: Calendar | Component | CalendarObjectResource, + expand_only: bool = False, + _ignore_rrule_and_time: bool = False, + ) -> Iterable[Component]: + """Checks if one component (or recurrence set) matches the + filters. If the component parameter is a calendar containing + several independent components, an Exception may be raised, + though recurrence sets should be suppored. + + * If there is no match, ``None`` or ``False`` will be returned + (the exact return value is currently not clearly defined, + but ``bool(return_value)`` should yield False). + + * If a time specification is given and the component given is + a recurring component, it will be expanded internally to + check if it matches the given time specification. + + * If there is a match, the component should be returned + (wrapped in a tuple) - unless ``expand`` is set, in which + case all matching recurrences will be returned (as a + generator object). + + :param component: Todo, Event, Calendar or such + :param expand_only: Don't do any filtering, just expand + + """ + ## For consistant and predictable return value, we need to + ## transform the component in the very start. We need to make + ## it into a list so we can iterate on it without throwing + ## away data. TODO: This will break badly if the + ## component already is a generator with infinite amount of + ## recurrences. It should be possible to fix this in a smart + ## way. I.e., make a new wrappable class ListLikeGenerator + ## that stores the elements we've taken out from the + ## generator. Such a class would also eliminate the need of + ## _generator_or_false. + orig_recurrence_set = self._validate_and_normalize_component(component) + + ## Early return if no work needed + if expand_only and not self.expand: + return orig_recurrence_set + + ## Ensure timezone is set. Ensure start and end are datetime objects. + for attr in ("start", "end", "alarm_start", "alarm_end"): + value = getattr(self, attr) + if value: + if not isinstance(value, datetime): + logging.warning( + "Date-range searches not well supported yet; use datetime rather than dates" + ) + setattr(self, attr, _normalize_dt(value)) + + ## recurrence_set is our internal generator/iterator containing + ## everything that hasn't been filtered out yet (in most + ## cases, the generator will yield either one or zero + ## components - but recurrences are tricky). orig_recurrence_set + ## is a list, so iterations on recurrence_set will not affect + ## orig_recurrence_set + recurrence_set = orig_recurrence_set + + ## Recurrences may be a night-mare. + ## I'm not sure if this is very well thought through, but my thoughts now are: + + ## 1) we need to check if the not-expanded base element + ## matches any non-date-related search-filters before doing + ## anything else. We do this with a recursive call with an + ## internal parameter _ignore_rrule_and_time set. + + ## 2) If the base element does not match, we may probably skip + ## expansion (only problem I can see with it is some filtering + ## like "show me all events happening on a Monday" - which is + ## anyway not supported as for now). It's important to skip + ## expansion, otherwise we may end up doing an infinite (or + ## very-huge) expansion just to verify that we can return + ## None/False + + ## 3) if the base element does not match, there may still be + ## special cases matching. We solve this by running the + ## filter logics on all but the + + ## 4) if the base element matches, we may need to expand it + first = orig_recurrence_set[0] + if not expand_only and "RRULE" in first and not _ignore_rrule_and_time: + ## TODO: implement logic above + base_element_match = self.check_component(first, _ignore_rrule_and_time=True) + if not base_element_match: + ## Base element is to be ignored. recurrence_set is still a list + recurrence_set = recurrence_set[1:] # Remove first element (base), keep exceptions + + ## self.include_completed should default to False if todo is explicity set, + ## otherwise True + if self.include_completed is None: + self.include_completed = not self.todo + + ## Component type flags are a bit difficult. In the CalDAV library, + ## if all of them are None, everything should be returned. If only + ## one of them is True, then only this kind of component type is + ## returned. In any other case, no guarantees of correctness are given. + + ## Let's skip the last remark and try to make a generic and + ## correct solution from the start: 1) if any flags are True, + ## then consider flags set as None as False. 2) if any flags + ## are still None, then consider those to be True. 3) List + ## the flags that are True as acceptable component types: + + comptypesl = ("todo", "event", "journal") + if any(getattr(self, x) for x in comptypesl): + for x in comptypesl: + if getattr(self, x) is None: + setattr(self, x, False) + else: + for x in comptypesl: + if getattr(self, x) is None: + setattr(self, x, True) + + comptypesu = set([f"V{x.upper()}" for x in comptypesl if getattr(self, x)]) + + ## if expand_only, expand all comptypes, otherwise only the comptypes specified in the filters + comptypes_for_expansion = ["VTODO", "VEVENT", "VJOURNAL"] if expand_only else comptypesu + + if not _ignore_rrule_and_time and "RRULE" in first: + recurrence_set = self._expand_recurrences(recurrence_set, comptypes_for_expansion) + + if not expand_only: + ## OPTIMIZATION TODO: If the object was recurring, we should + ## probably trust recur.between to do the right thing? + if not _ignore_rrule_and_time and (self.start or self.end): + recurrence_set = (x for x in recurrence_set if self._check_range(x)) + + ## This if is just to save some few CPU cycles - skip filtering if it's not needed + if not all(getattr(self, x) for x in comptypesl): + recurrence_set = (x for x in recurrence_set if x.name in comptypesu) + + ## Filter based on include_completed setting + recurrence_set = (x for x in recurrence_set if self._check_completed_filter(x)) + + ## Apply property filters + if self._property_filters or self._property_operator: + recurrence_set = (x for x in recurrence_set if self._check_property_filters(x)) + + ## Apply alarm filters + if not _ignore_rrule_and_time and (self.alarm_start or self.alarm_end): + recurrence_set = (x for x in recurrence_set if self._check_alarm_range(x)) + + if self.expand: + ## TODO: fix wrapping, if needed + return _iterable_or_false(recurrence_set) + else: + if next(recurrence_set, None): + return orig_recurrence_set + else: + return None + + def filter( + self, components: list[Calendar | Component], split_expanded: bool = False + ) -> list[Calendar | Component]: + """Filter components according to the search criteria, optionally expanding recurrences. + + This method does not modify the input list. It returns a new list with + filtered components. + + :param components: List of Calendar or Component objects to filter + :param split_expanded: If True and recurrences are expanded, return each + recurrence as a separate Calendar object. If False, all matching + recurrences from a single input will be returned in one Calendar. + :return: New list containing only components that match the filter criteria + + Examples: + searcher = Searcher(event=True, start=datetime(2025, 1, 1)) + searcher.add_property_filter("SUMMARY", "meeting", operator="contains") + filtered = searcher.filter(calendars) # Returns matching calendars + + # With split_expanded=True, each recurrence becomes a separate Calendar + searcher.expand = True + split_results = searcher.filter(calendars, split_expanded=True) + """ + results: list[Calendar | Component] = [] + + for component in components: + # check_component returns an iterable of matching components (possibly expanded) + matched = self.check_component(component) + + # Convert to list to check if we got any results + if matched: + matched_list = list(matched) + if not matched_list: + continue + + if split_expanded and len(matched_list) > 1: + # Split expanded recurrences into separate Calendar objects + # Each recurrence becomes its own Calendar + for comp in matched_list: + if isinstance(comp, Timezone): + continue + + # Create new Calendar for this recurrence + new_cal = Calendar() + + # If original was a Calendar, copy its properties + if isinstance(component, Calendar): + for key, value in component.items(): + new_cal[key] = value + + # Preserve timezone components + timezones = [ + tz for tz in component.subcomponents if isinstance(tz, Timezone) + ] + for tz in timezones: + from copy import deepcopy + + new_cal.add_component(deepcopy(tz)) + + # Add the matched component + from copy import deepcopy + + new_cal.add_component(deepcopy(comp)) + results.append(new_cal) + else: + # Return as-is, or create a Calendar with all matched components + if isinstance(component, Calendar): + # Create new Calendar with matched components + new_cal = Calendar() + for key, value in component.items(): + new_cal[key] = value + + # Preserve timezone components + timezones = [ + tz for tz in component.subcomponents if isinstance(tz, Timezone) + ] + for tz in timezones: + from copy import deepcopy + + new_cal.add_component(deepcopy(tz)) + + # Add all matched components + for comp in matched_list: + if not isinstance(comp, Timezone): + from copy import deepcopy + + new_cal.add_component(deepcopy(comp)) + + results.append(new_cal) + else: + # Component was passed directly, return matched components + for comp in matched_list: + from copy import deepcopy + + results.append(deepcopy(comp)) + + return results + + def filter_calendar(self, calendar: Calendar) -> Calendar: + """Filter subcomponents within a Calendar object according to search criteria. + + This method does not modify the input Calendar. It returns a new Calendar + containing only the subcomponents that match the filter criteria. + + :param calendar: Calendar object containing multiple subcomponents to filter + :return: New Calendar object with only matching subcomponents, or None if no matches + + Examples: + searcher = Searcher(event=True, start=datetime(2025, 1, 1)) + searcher.add_property_filter("SUMMARY", "meeting", operator="contains") + filtered_cal = searcher.filter_calendar(calendar) # Returns Calendar with matching events + """ + from copy import deepcopy + + # Separate timezone components from other components + timezones = [comp for comp in calendar.subcomponents if isinstance(comp, Timezone)] + other_components = [ + comp for comp in calendar.subcomponents if not isinstance(comp, Timezone) + ] + + # Filter each component + matching_components = [] + for comp in other_components: + matched = self.check_component(comp) + if matched: + matched_list = list(matched) + if matched_list: + # Add all matching occurrences (expanded or not) + matching_components.extend(matched_list) + + # If no matches, return None or empty calendar + if not matching_components: + return None + + # Create new calendar with matching components + new_calendar = Calendar() + + # Copy calendar-level properties + for key, value in calendar.items(): + new_calendar[key] = value + + # Add timezone components first + for tz in timezones: + new_calendar.add_component(deepcopy(tz)) + + # Add matching components + for comp in matching_components: + if not isinstance(comp, Timezone): + new_calendar.add_component(deepcopy(comp)) + + return new_calendar + + def sort( + self, components: list[Component | CalendarObjectResource] + ) -> list[Component | CalendarObjectResource]: + """Sort calendar objects according to configured sort keys. + + This method does not modify the input list. It returns a new sorted list. + + (the idea with this is that the input may be a generator) + + :param components: List of Calendar or CalendarObjectResource objects to sort + :return: New sorted list + + Examples: + searcher = Searcher() + searcher.add_sort_key("DTSTART") + sorted_events = searcher.sort(events) # Returns new sorted list + """ + if self._sort_keys: + return sorted(components, key=self.sorting_value) + else: + return components.copy() + + def sort_calendar(self, calendar: Calendar) -> Calendar: + """Sort subcomponents within a Calendar object according to configured sort keys. + + This method does not modify the input Calendar. It returns a new Calendar + with sorted subcomponents (excluding VTIMEZONE components). + + :param calendar: Calendar object containing multiple subcomponents to sort + :return: New Calendar object with sorted subcomponents + + Examples: + searcher = Searcher() + searcher.add_sort_key("DTSTART") + sorted_cal = searcher.sort_calendar(calendar) # Returns new Calendar with sorted events + """ + if not self._sort_keys: + # No sorting needed, return a copy + from copy import deepcopy + + return deepcopy(calendar) + + # Separate timezone components from other components + from icalendar import Timezone + + timezones = [comp for comp in calendar.subcomponents if isinstance(comp, Timezone)] + other_components = [ + comp for comp in calendar.subcomponents if not isinstance(comp, Timezone) + ] + + # Sort the non-timezone components + sorted_components = sorted(other_components, key=self.sorting_value) + + # Create new calendar with sorted components + from copy import deepcopy + + new_calendar = Calendar() + + # Copy calendar-level properties + for key, value in calendar.items(): + new_calendar[key] = value + + # Add timezone components first + for tz in timezones: + new_calendar.add_component(deepcopy(tz)) + + # Add sorted components + for comp in sorted_components: + new_calendar.add_component(deepcopy(comp)) + + return new_calendar + + def sorting_value(self, component: Component | CalendarObjectResource) -> tuple: + """Returns a sortable value from the component, based on the sort keys + + The component may be an icalendar.Calendar, an + icalendar.Component (i.e. icalendar.Event) or an + caldav.CalendarObjectResource (i.e. caldav.Event). + """ + ret = [] + ## TODO: this logic has been moved more or less as-is from the + ## caldav library. It may need some rethinking and QA work. + + ## TODO: we disregard any complexity wrg of recurring events + component = self._unwrap(component) + if isinstance(component, Calendar): + not_tz_components = (x for x in component.subcomponents if not isinstance(x, Timezone)) + comp = next(not_tz_components) + else: + comp = component + + defaults = { + ## TODO: all possible non-string sort attributes needs to be listed here, otherwise we will get type errors when comparing objects with the property defined vs undefined (or maybe we should make an "undefined" object that always will compare below any other type? Perhaps there exists such an object already?) + "due": "2050-01-01", + "dtstart": "1970-01-01", + "priority": 0, + "status": { + "VTODO": "NEEDS-ACTION", + "VJOURNAL": "FINAL", + "VEVENT": "TENTATIVE", + }[comp.name], + "category": "", + ## Usage of strftime is a simple way to ensure there won't be + ## problems if comparing dates with timestamps + "isnt_overdue": not ( + "due" in comp + and comp["due"].dt.strftime("%F%H%M%S") < datetime.now().strftime("%F%H%M%S") + ), + "hasnt_started": ( + "dtstart" in comp + and comp["dtstart"].dt.strftime("%F%H%M%S") > datetime.now().strftime("%F%H%M%S") + ), + } + for sort_key, reverse in self._sort_keys: + if sort_key == "categories": + val = comp.categories + else: + val = comp.get(sort_key, None) + if val is None: + ret.append(defaults.get(sort_key, "")) + continue + + # Track if this is a text property (for collation) + # Apply collation BEFORE datetime/category conversion + is_text_property = isinstance(val, str) and sort_key in self._sort_collation + + if hasattr(val, "dt"): + val = val.dt + if hasattr(val, "strftime"): + val = val.strftime("%F%H%M%S") + + ## TODO: I don't have time to fix test code for this at + ## the moment (but the bug in v1.0.0 was caught by cyrus + ## test code in caldav library, cyrus splits the + ## categories field, and this is allowed according to RFC + ## 7986, section 5.6) TODO: we should fix tests not only + ## for sorting lists and categories, but also filtering on + ## lists and multi-line categories. + if isinstance(val, list): + ## sorting lists may be difficult. As I understand + ## the standard, the order of the list is not + ## significant - the order of the elements may be + ## reshuffled, and the icalendar component will + ## semantically be equivalent. To get deterministic + ## sorting of semantically equivalent components, I've + ## decided to sort the list (TODO: collation support?) + val = sorted(val) + + ## TODO: what if the list contains numbers? + val = ",".join([str(x) for x in val]) + + # Apply collation only to text properties (not datetime strings) + if is_text_property and isinstance(val, str): + collation = self._sort_collation[sort_key] + locale = self._sort_locale.get(sort_key) + case_sensitive = self._sort_case_sensitive.get(sort_key, True) + sort_key_fn = get_sort_key_function(collation, case_sensitive, locale) + val = sort_key_fn(val) + + if reverse: + if isinstance(val, (str, bytes)): + if isinstance(val, str): + val = val.encode() + val = bytes(b ^ 0xFF for b in val) + else: + val = -val + ret.append(val) + + return ret + + def _unwrap(self, component: Calendar | CalendarObjectResource) -> Calendar: + """ + To support the caldav library (and possibly other libraries where the + icalendar component is wrapped) + """ + try: + component = component.icalendar_instance + except AttributeError: + pass + if isinstance(component, Component) and not isinstance(component, Calendar): + cal = Calendar() + cal.add_component(component) + component = cal + return component + + def _validate_and_normalize_component( + self, component: Calendar | Component | CalendarObjectResource + ) -> list[Component]: + """This method serves two purposes: + + 1) Be liberal in what "component" it accepts and return + something well-defined. For instance, coponent may be a + wrapped object (caldav.Event), an icalendar.Calendar or an + icalendar.Event. The return value will always be a list of + icalendar components (i.e. Event), and Timezone components will + be removed. + + 2) Do some verification that the "component" is as expected + and raise a ValueError if not. The "component" should either + be one single component or a recurrence set. A recurrence set + should conform to those rules: + + 2.1) All components in the recurrence set should have the same UID + + 2.2) First element ("master") of the recurrence set may have the RRULE + property set + + 2.3) Any following elements of a recurrence set ("exception + recurrences") should have the RECURRENCE-ID property set. + + 2.4) (there are more properties that may only be set in the + master or only in the recurrences, but currently we don't do + more checking than this) + + As for now, we do not support component to be a generator or a + list, and things will blow up if component.subcomponents is a + generator yielding infinite or too many subcomponents. + + """ + + component = self._unwrap(component) + components = [x for x in component.subcomponents if not isinstance(x, Timezone)] + + ## We shouldn't get here. There should always be a valid component. + if not len(components): + raise ValueError("Empty component?") + first = components[0] + + ## A recurrence set should always be one "master" with + ## rrule-id set, followed by zero or more objects without + ## rrule-id but with recurrence-id set + if len(components) > 1: + if ( + ("RRULE" not in components[0] and "RECURRENCE-ID" not in components[0]) + or not all("recurrence-id" in x for x in components[1:]) + or any("RRULE" in x for x in components[1:]) + ): + raise ValueError( + "Expected a valid recurrence set, either with one master component followed with special recurrences or with only occurrences" + ) + + ## components should typically be a list with only one component. + ## if there are more components, it should be a recurrence set + ## one of the things identifying a recurrence set is that the + ## uid is the same for all components in the set + if any(x for x in components if x["uid"] != first["uid"]): + raise ValueError( + "Input parameter component is supposed to contain a single component or a recurrence set - but multiple UIDs found" + ) + return components + + def _expand_recurrences( + self, recurrence_set: list[Component], comptypesu: set[str] + ) -> Iterable[Component]: + """Expand recurring events within the searcher's time range. + + Ensures expanded occurrences comply with RFC 5545: + - Each occurrence has RECURRENCE-ID set + - Each occurrence does NOT have RRULE (RRULE and RECURRENCE-ID are mutually exclusive) + + :param recurrence_set: List of calendar components to expand + :param comptypesu: Set of component type strings (e.g., {"VEVENT", "VTODO"}) + :return: Iterable of expanded component instances + """ + cal = Calendar() + for x in recurrence_set: + cal.add_component(x) + recur = recurring_ical_events.of(cal, components=comptypesu) + + # Use local variables for start/end to avoid modifying searcher state + start = self.start if self.start else _normalize_dt(DATE_MIN_DT) + end = self.end if self.end else _normalize_dt(DATE_MAX_DT) + + return recur.between(start, end) diff --git a/.venv/lib/python3.9/site-packages/icalendar_searcher/utils.py b/.venv/lib/python3.9/site-packages/icalendar_searcher/utils.py new file mode 100644 index 0000000..418ab88 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar_searcher/utils.py @@ -0,0 +1,58 @@ +"""Utility functions for icalendar-searcher.""" + +from __future__ import annotations + +from collections.abc import Iterable, Iterator +from datetime import date, datetime +from itertools import tee + +from icalendar.prop import TypesFactory + +## We need an instance of the icalendar.prop.TypesFactory class. +## We'll make a global instance rather than instantiate it for +## every loop iteration +types_factory = TypesFactory() + + +## Helper to normalize date/datetime for comparison +## (I feel this one is duplicated over many projects ...) +def _normalize_dt(dt_value: date | datetime) -> datetime: + """Convert date to datetime for comparison, or return datetime as-is with timezone.""" + if dt_value is None: + return None + ## If it's a date (not datetime), convert to datetime at midnight + if hasattr(dt_value, "year") and not hasattr(dt_value, "hour"): + from datetime import time + + return datetime.combine(dt_value, time.min).astimezone() + ## TODO: we should probably do some research on the default calendar timezone, + ## which may not be the same as the local timezone ... uh ... timezones are + ## difficult. + return dt_value.astimezone() + + +## Helper - generators are generally more neat than lists, +## but bool(x) will always return True. I'd like to verify +## that a generator is not empty, without side effects. +def _iterable_or_false(g: Iterable, _debug_print_peek: bool = False) -> bool | Iterable: + """This method will return False if it's not possible to get an + item from the iterable (which can only be done by utilizing + `next`). It will then return a new iterator that behaves like + the original iterator (like if `next` wasn't used). + + Uses itertools.tee to create two independent iterators: one for + checking if the iterator is empty, and one to return. + """ + if not isinstance(g, Iterator): + return bool(g) and g + + # Create two independent iterators from the input + check_it, result_it = tee(g) + + try: + my_value = next(check_it) + if _debug_print_peek: + print(my_value) + return result_it # Return the untouched iterator + except StopIteration: + return False diff --git a/.venv/lib/python3.9/site-packages/idna-3.11.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/idna-3.11.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/idna-3.11.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/idna-3.11.dist-info/METADATA b/.venv/lib/python3.9/site-packages/idna-3.11.dist-info/METADATA new file mode 100644 index 0000000..7a4a4b7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/idna-3.11.dist-info/METADATA @@ -0,0 +1,209 @@ +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/.venv/lib/python3.9/site-packages/idna-3.11.dist-info/RECORD b/.venv/lib/python3.9/site-packages/idna-3.11.dist-info/RECORD new file mode 100644 index 0000000..9d13985 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/idna-3.11.dist-info/RECORD @@ -0,0 +1,22 @@ +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/idna/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/idna/codec.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/idna/compat.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/idna/core.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/idna/idnadata.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/idna/intranges.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/idna/package_data.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/idna/uts46data.cpython-39.pyc,, +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/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/.venv/lib/python3.9/site-packages/idna-3.11.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/idna-3.11.dist-info/WHEEL new file mode 100644 index 0000000..d8b9936 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/idna-3.11.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.12.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.9/site-packages/idna-3.11.dist-info/licenses/LICENSE.md b/.venv/lib/python3.9/site-packages/idna-3.11.dist-info/licenses/LICENSE.md new file mode 100644 index 0000000..256ba90 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/idna-3.11.dist-info/licenses/LICENSE.md @@ -0,0 +1,31 @@ +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/.venv/lib/python3.9/site-packages/idna/__init__.py b/.venv/lib/python3.9/site-packages/idna/__init__.py new file mode 100644 index 0000000..cfdc030 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/idna/__init__.py @@ -0,0 +1,45 @@ +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/.venv/lib/python3.9/site-packages/idna/codec.py b/.venv/lib/python3.9/site-packages/idna/codec.py new file mode 100644 index 0000000..cbc2e4f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/idna/codec.py @@ -0,0 +1,122 @@ +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/.venv/lib/python3.9/site-packages/idna/compat.py b/.venv/lib/python3.9/site-packages/idna/compat.py new file mode 100644 index 0000000..1df9f2a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/idna/compat.py @@ -0,0 +1,15 @@ +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/.venv/lib/python3.9/site-packages/idna/core.py b/.venv/lib/python3.9/site-packages/idna/core.py new file mode 100644 index 0000000..8177bf7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/idna/core.py @@ -0,0 +1,437 @@ +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/.venv/lib/python3.9/site-packages/idna/idnadata.py b/.venv/lib/python3.9/site-packages/idna/idnadata.py new file mode 100644 index 0000000..ded47ca --- /dev/null +++ b/.venv/lib/python3.9/site-packages/idna/idnadata.py @@ -0,0 +1,4309 @@ +# 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/.venv/lib/python3.9/site-packages/idna/intranges.py b/.venv/lib/python3.9/site-packages/idna/intranges.py new file mode 100644 index 0000000..7bfaa8d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/idna/intranges.py @@ -0,0 +1,57 @@ +""" +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/.venv/lib/python3.9/site-packages/idna/package_data.py b/.venv/lib/python3.9/site-packages/idna/package_data.py new file mode 100644 index 0000000..7272c8d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/idna/package_data.py @@ -0,0 +1 @@ +__version__ = "3.11" diff --git a/.venv/lib/python3.9/site-packages/idna/py.typed b/.venv/lib/python3.9/site-packages/idna/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.9/site-packages/idna/uts46data.py b/.venv/lib/python3.9/site-packages/idna/uts46data.py new file mode 100644 index 0000000..4610b71 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/idna/uts46data.py @@ -0,0 +1,8841 @@ +# 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/.venv/lib/python3.9/site-packages/jh2-5.0.10.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/jh2-5.0.10.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2-5.0.10.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/jh2-5.0.10.dist-info/METADATA b/.venv/lib/python3.9/site-packages/jh2-5.0.10.dist-info/METADATA new file mode 100644 index 0000000..26721b0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2-5.0.10.dist-info/METADATA @@ -0,0 +1,106 @@ +Metadata-Version: 2.4 +Name: jh2 +Version: 5.0.10 +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.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 :: Internet :: WWW/HTTP +Classifier: Topic :: Software Development :: Libraries +License-File: LICENSE +Summary: HTTP/2 State-Machine based protocol implementation +Keywords: http2,hpack,h2 +Author-email: Cory Benfield +Maintainer-email: "Ahmed R. TAHRI" +License-Expression: MIT +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst; charset=UTF-8 +Project-URL: Changelog, https://github.com/jawah/h2/blob/main/CHANGELOG.rst +Project-URL: Code, https://github.com/jawah/h2 +Project-URL: Issue tracker, https://github.com/jawah/h2/issues + +========================== +jh2: HTTP/2 Protocol Stack +========================== + +This repository is a fork of the well known hyper/h2 package. We want to provide a cleaner and faster HTTP/2 +state machine while keeping a pure Python implementation. We decided to embed the leaf dependencies as we want +a neater dependency tree and along with that a easier maintenance burden. We believe it was a mistake to ship +three packages (h2, hpack, and hyperframe). + +Analysis shown that h2 spend a lot of time doing hpack encode and decode operations, this is why we decided to offer +a complementary optimized build. The pure Python version will still be available. + +This repository contains a pure-Python implementation of a HTTP/2 protocol +stack. It's written from the ground up to be embeddable in whatever program you +choose to use, ensuring that you can speak HTTP/2 regardless of your +programming paradigm. + +You use it like this: + +.. code-block:: python + + import jh2.connection + import jh2.config + + config = jh2.config.H2Configuration() + conn = jh2.connection.H2Connection(config=config) + conn.send_headers(stream_id=stream_id, headers=headers) + conn.send_data(stream_id, data) + socket.sendall(conn.data_to_send()) + events = conn.receive_data(socket_data) + +This repository does not provide a parsing layer, a network layer, or any rules +about concurrency. Instead, it's a purely in-memory solution, defined in terms +of data actions and HTTP/2 frames. This is one building block of a full Python +HTTP implementation. + +To install it, just run: + +.. code-block:: console + + $ python -m pip install jh2 + +Documentation +============= + +Documentation is available at https://h2.readthedocs.io . + +Contributing +============ + +``jh2`` welcomes contributions from anyone! Unlike many other projects we +are happy to accept cosmetic contributions and small contributions, in addition +to large feature requests and changes. + +Before you contribute (either by opening an issue or filing a pull request), +please `read the contribution guidelines`_. + +.. _read the contribution guidelines: http://python-hyper.org/en/latest/contributing.html + +License +======= + +``jh2`` is made available under the MIT License. For more details, see the +``LICENSE`` file in the repository. + +Authors +======= + +``h2`` was authored by Cory Benfield and is maintained by the Jawah OSS organization under the ``jh2`` name. + diff --git a/.venv/lib/python3.9/site-packages/jh2-5.0.10.dist-info/RECORD b/.venv/lib/python3.9/site-packages/jh2-5.0.10.dist-info/RECORD new file mode 100644 index 0000000..399005d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2-5.0.10.dist-info/RECORD @@ -0,0 +1,54 @@ +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/config.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/connection.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/errors.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/events.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/exceptions.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/frame_buffer.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/hpack/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/hpack/exceptions.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/hpack/hpack.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/hpack/huffman.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/hpack/huffman_constants.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/hpack/huffman_table.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/hpack/struct.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/hpack/table.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/hyperframe/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/hyperframe/exceptions.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/hyperframe/flags.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/hyperframe/frame.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/settings.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/stream.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/utilities.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/jh2/windows.cpython-39.pyc,, +jh2-5.0.10.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +jh2-5.0.10.dist-info/METADATA,sha256=VutyJCzG8ZIcjtY_Jbah93iqrnC388CE_nU6yUoUeRQ,4030 +jh2-5.0.10.dist-info/RECORD,, +jh2-5.0.10.dist-info/WHEEL,sha256=kifCJ_eQb-HOF-7YR7yAmtH23zUrPDoMU-JPMNPTBjw,146 +jh2-5.0.10.dist-info/licenses/LICENSE,sha256=ImAuRFaKkIyLJZ2SVd_195FgOASnWeI_UTedLRcwKmw,1102 +jh2/__init__.py,sha256=7DQBJyPypuHkUH-KR4GaDiTQijYetrtv98CkLp01nxQ,100 +jh2/_hazmat.abi3.so,sha256=HxuuMfIW5M8fQZa3htrLtQIAcjzUtEh55-maSHaJfxM,1361504 +jh2/_hazmat.pyi,sha256=vlnQqKLXqbM8OsVfx5NiGqr4KEpmwKIoF8oo0yDBhRk,671 +jh2/config.py,sha256=3kOXMiHeFBY5F39WchCiPSQs5fiX8JwUHsnCIIIQnKQ,7750 +jh2/connection.py,sha256=960xdwuugFPi3V79b9BU7-HokvuVhK5ZaM91it-D7FE,84208 +jh2/errors.py,sha256=roV6gr5sKwJ0cckTfTAbVs9zqqYH-fg9Y5BgIQ4JXEA,1557 +jh2/events.py,sha256=13BNjILOVS_iSKgLw6eROlFyzLc-KgTwh1P2-Qy1tYo,21529 +jh2/exceptions.py,sha256=TGUi2hyg6AF2VrCCxQeIz7L4-tEqj9MY2bbZ4oi2Yqk,5310 +jh2/frame_buffer.py,sha256=SLc6yZQw-WYNQNShOKrQiVPWkFR8hqEYP3ag54CsTsc,6230 +jh2/hpack/__init__.py,sha256=Rl5ypGLCvGBduZQIDvvlMM6y9bhaBww8xRQMDzbFzw4,559 +jh2/hpack/exceptions.py,sha256=RhMJUhQPzw58LJ7GzfIua-tI2AzVyvrOUcM6_jAndE4,991 +jh2/hpack/hpack.py,sha256=WOjNnwL3GWxnqRVjQQ3jcDlHIy3QL2GwnKqFClOIrcs,22298 +jh2/hpack/huffman.py,sha256=sB9pgzLw8gjEpGFf613pkmnATGVv3uxCNqpnr43lpSQ,2426 +jh2/hpack/huffman_constants.py,sha256=MR9FhnEGlEXaMNsLDt36LTG6fyRY_KtvCr5a-X9iAUo,5541 +jh2/hpack/huffman_table.py,sha256=HQz60i6k1usN5Kw9m1XqlnArkAzfyWhU-tedoSYNzhg,168334 +jh2/hpack/struct.py,sha256=GYDgzjhgjU9vjbso5Z4Yt3nNHb-MrEtDGmfC6Yx2OVM,1064 +jh2/hpack/table.py,sha256=jBnzZ58oENZxGRQOfJIc3rdr5hBiUVgwK1g4qai-VF0,7828 +jh2/hyperframe/__init__.py,sha256=xVZqf0meDLaIa4H9ZhwmB12L7WCep44zYkyWiZ46MQs,90 +jh2/hyperframe/exceptions.py,sha256=MklxwxK4sBllsL2-Cx_twZcxduvLEAboL2zjDLQeDIQ,1623 +jh2/hyperframe/flags.py,sha256=veoNPgBUkJ_KCtv_Tns0ZwxzC0R7uIWiSCNz_LeyEpY,1392 +jh2/hyperframe/frame.py,sha256=OLGJ2e96fc2assedxFm2nV360SLlmOHeIyquAPTcfPQ,31930 +jh2/hyperframe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jh2/settings.py,sha256=aN6p8DttaCY1S9E3fgYrolXEKIDP4CXP_D4_WuJ5esI,11820 +jh2/stream.py,sha256=55Fi34Bzi2irfprxOQQnWflKLh8aliSW63waGwjiam4,55165 +jh2/utilities.py,sha256=X_OjwyXAaEZ14cnEgXkX-TonF0OK8pbry2eZTNixZ4s,24039 +jh2/windows.py,sha256=993XynsE2HHnIo51nwzSmTpC2-wAzVF1Fpx5y9GtLIg,5562 diff --git a/.venv/lib/python3.9/site-packages/jh2-5.0.10.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/jh2-5.0.10.dist-info/WHEEL new file mode 100644 index 0000000..36f5d3c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2-5.0.10.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: maturin (1.9.4) +Root-Is-Purelib: false +Tag: cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2 diff --git a/.venv/lib/python3.9/site-packages/jh2-5.0.10.dist-info/licenses/LICENSE b/.venv/lib/python3.9/site-packages/jh2-5.0.10.dist-info/licenses/LICENSE new file mode 100644 index 0000000..f66a23b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2-5.0.10.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015-2025 Cory Benfield and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/.venv/lib/python3.9/site-packages/jh2/__init__.py b/.venv/lib/python3.9/site-packages/jh2/__init__.py new file mode 100644 index 0000000..4a0722f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/__init__.py @@ -0,0 +1,10 @@ +""" +h2 +~~ + +A HTTP/2 implementation. +""" + +from __future__ import annotations + +__version__ = "5.0.10" diff --git a/.venv/lib/python3.9/site-packages/jh2/_hazmat.abi3.so b/.venv/lib/python3.9/site-packages/jh2/_hazmat.abi3.so new file mode 100755 index 0000000000000000000000000000000000000000..218548f5982f0105d79198cc8f0035ee92cba3a4 GIT binary patch literal 1361504 zcmeFadwf*I`9Hpa1cDGZC}_NZQBi}}phiOhO@PI-vOyH1SjBJ=L=la!D|iV+lWlW) zdfHUQ(oc&`tJc&@8@1R#3xo zU;g839Q;qi!GB*r{O1AxCL~0CwbIgI_-*au^Ocod9GHHwB&GgT^4b8;pYxo7B>vBI zUX_)F=PnH=^L4GSc9?#b{~SyCe0)yVN%}Y4SLuB$D_b0n%wC*G>RMlu()BzG*!kx^ z>nFhF)W<&aPuEvAf9`eVa~H{TYJYze*Y!Q=0OdYQVg7W3@9H^o%H}SeGq*fk7M?Y` zD||(}bba&B&^7Sc0X0#jH&RwMXK{GZHP>J5BzCQ@{#f0U5l8C`?z3A**Sb;P4c9MD z4oKJf+9vAyieA+<@Hu5X+=`vBtSoZ361+ z*5#RX^8Wzmby?ZfHzcvP@A_(-{65s@g^O5S;=V&zR(9R2vc)$qoPEP}W#!>T-QcTn z;AwO!;j_0sVjZ7=EiI)QNh z@%fwU3(sFPch*&9H_cka+1qtITHn$2HL)`M@!4D7!8$qd3%;W3bHMCcU(MfieYs8v zK6~pMoTy1h67|hqOz^tFSMSs}-BZq6-$9;qU4G6D3m4vSy_?(>KHuLpeD!Vygx>ma z2TRIdS(yWvliIbudZ)g9!Pm7u&I<$o%gRE*DU$;eg9#p=sawc*W@)>IbI2+Bne}+< z%EEIY{^xW_FIrSSD?IVTT9dMW4~Y6ivKXXC2F-|lct(bAM$Dhj zUlYIc{6|#_(9B zFkN1Jg$z#Y31o2}HYFQ%;lub>o$CwVYM5Uc!~y^ugPzT}s=rU=pj(D@Yc`%s_?)Sq zbC~ITBo-nuiIZ6DePDsvt;=%^t1m&y!0_*FGpxm^xFdXsVNJ_Mil@v_K$m>nM6^1Y zi=E7B_w5?FUp|xJ!~6Kbl1ZTY^Bh362+aaYfoc$-`YjV@5EhKc(~AlNzF6~ znc@SH%*tcfDkXLN3^@tQXu|T^M*O^lpX5-;_`Bl)_&WNhg{K6~e;IaJCdeY6b(QQ3 z&=K7L-B_6l=s{>cK>*z#z8kp{5TETpy!GE2;vcpG*kcIsur3e_K0!OAnzK2!2Vw{^ zHBlB50BbP@q$4~8$+?m&qdX%vf-lKBW_XCv;Rn-<+jnMy-dU@ivErD1h((v~-MiOl z2~=|bU%m0J&u0ccBXwDmF=eNL*f8?eD8o9)u>6L-ZWnsH7pYN33~N;<0)KYY$6Blv zIeEOdh0YE^*A+^G z@2Xp_Mv4_Jmq*lMMd#}%Dvr+7QM47EuA^u^I#oyO5H)mkC8AQHWfw_GNi9ICiLokG z(B!lUpq~Fb=%BBLU@2e5=LyqQRm~Xw;mGMF*rLRI;+S!tG-$rTd1akrSQ%3vAz$e1 z>yQ2sZ!nbSF~Q0YlEIAL6CR*8f(e5?j0@V!XR6za(tOoT;em#IYo@yUDZnu=hr$g&8%wdIzZOa7X#qKK(Gg{Flz}&y#U)dnw16W5j_Q>4ABPgm+=oHY7ZTF3%ize z0jbnp$2voHNquo6+f(PvO!IXJOljfE4O@?#WfX!;+6~LdM+h=A@(_aZj9i2uB?F?T z1OshkBg80Y8dg!Jv`Xr-3x0DH+p4HvD{ZU|3Vldd6S(z9>p<|a`|$&mk+3#F9C_t! z593w%P?zyKjR}c5Fnb#-^+Me@SeeV{f-3EZM=SR<##b&ox{B2b%3#7<8?5E)whh-uKF(HwSx)X@tO^G zotR&<#(Bo88g$=fe)0YOj@NK@$<*}xjq~eoYrcKFlvF13>xJ2g@!IyFjMu|A$#{K2 z0_iqh?_Tj;j92lI-tqeWr2QJNIUADWb;&fNY9}V|uAupaVPAl`TLQVUGc#llt-c(( zs69g&_OO2)(GSsri;#jz{WhLJmw!XBmd;|ujRVP(%$DMdil<#WU|R7dsvI7XkeNl` zP%}39kE+Cb;lHT5$S^B94KuRKFiTN)Mrj|Gh5WHa1~nnXYse`(fGmMFgp^5Jz($%> zO(%FF>YcGkowzoY!SAd7&jI3o*PoZH|JgPBSARhIzMt^7?n%IZZBO-Mbpvi!izV6@v7Nx)AUX81;<$aE{McRZ*7~32CW4p$Op?!<ERz8_1LM3Z{@PiN$M>+ozV!2H29G7P-i24^Ri{fIs4UB|_pGT^4-TAOzHbN`!z~ zt3sA7qPz>%`@^LRT4Dp5aoYyI2%2G+HmI*bi6Lt>btxBs$*8y<74HWCS_idC00*!K zw1rG8Xq@rBHG?f?eLqL|lt^QHwZ=s0n~pkg)J*0P_?_nc(JDeJBWHI)-8QVL7`zp` zBwCK~TdgFzG}kcKwNoFrigH3`bc;j{fMB;tG+QeP(N;d0E1D1h%p${_2BKJuI&Z~F zXF_f$0s6EA=uPPY0;oXhGAwUh07lnU+6W440wuO$2y%$gXp{f$4aS&^nHTl<`S0GC z`K-VC77$UxK{e4zHs2C8pnu3vv_z*ON(w{jbT`%J6w*S()gKJ9#Xtok` zlq-X{nwV9;o4_TeBw?xMufCI1s7VdO&75nMc>)3a_COQR9 zp&@H-Yf1D2fAuwhI%F+u3RXRx8$WoSv;Kb?>;LEd)ginLS`je;92m4J91FlsIF}XV@~ry?_nDdG*7`kcsgmln||M@)Zvlj~D&b8}T-1Epf+VxicPD+$hQJc$}+;V-iy1o1Kw2 ztku)etQtH!jfM+68ib-7Z45>shc;jwa_QpG)b$o@uS2QIoMCw;~No&V7Kv%0E$5|ssoAgsB11s97pLXe|2K}V;Q@ws_*H0@Y zu>>i9(t=2Vs8lb4Aj;Y>n7VGwiqWeE zcd0m4-E(@HPs=XKCt5vbI3`L53vH=bxCh?E@xNBCI~ru_YsmIT3%|nZsQ^D(Iz$`E z&qP8+8+3FfCR@FZ)*@P`qYzcml{#9<*(lLefo0b)(bW9TUMCF6E*Nh7V915E(a~Ub_dF_SQ%Th zR!eu$Id#z)x<5 z0?EH3SE4szC|Bo5bQyqHmyKu%O#Dy+G9`LoiFHXPxT+6N;IX1ba86MLG^e4?#i;C7 zNd5^>9B6o*oRIv#gir(scrR=dAfPc@H+Bgc6HXnpZpx;9Cm@`IrqNiGjR6?l*t#5a z2#F*`Ig6peO}-u$qb}7yS9F zUjyPWf;E6z=8|Y+GIe0uTPiTDSSmA5G(A96E>9u@0rY%4K|;;cK+lvXn_~h&ML7pt zPG_QNPi49N!zD<8Sa8`oQK4nQ_ZA49Py*tmzxo79U`xzTYe5F3otiT)=a2r?Ti zc*KvZU-^Ow{mMqy8xAUN`k+s-Z*y_w=Jbg*4+uAeu7#3#O9hL4adj;>)fSy6WD@7D zwX+VY6%0z6pTepYG9!%~74^G9EI`fXvFZNkC8!ACvPyK3=uyVy6l9xY)qG_}cM(Z? zEE!dY{n;JMa(30;*F^AF7mKDL19_m-Pu1G)CsB7Gza}QM%8r5TkSD#Sa7NHd9Xfj+ zvS0+MjesOYMKRZj-eRs6WyW0b6V_td_ec=Emw}}NEsOLE(J^#Z(J^#k(J^#&qGRX= zM8{yu5KE#5rLv|ZarmP&s>k9))XZyqzNfM=Wb3&QRB3qRm{+d`T7qVaVP||is2^he zgDvM}GM{F!Z7ACuZO5;#_^&d|;Pb?pt5G1XDeI~E9J_tcOjpzjayeJ{SIY~}=J z8W~WUV^xYeoA6jOq0ox{9udoa1i$8l(dOi_sHlh)K}gpoh5CjQU7=2~li13rT*o;h zQhOW3Wo#>DrttS@Cb1&xcSotAT+y0_h!$saKvp4&u!(@tng)fv$QZ8*|SC~*(~ z_cA(>H-0kp68?ZBd!|?aJdC+5(nQ_&Av=&V{&`v}`QlM%C3inabDQ3OptWtfs38#r z`M(S8Q{SjFY&_egyHx*}AfsL^JY^Wxc2Hmu!Yv{sW$%V|5-e>^+DS@)AlgY_)v>tj zotA@V$iT#!0f6mF5@wzN#%9DT)K{Rq3-5@?q$n=8sm@R}7fs;&ea)Oh^W^T=vlZPTlh zttBWA?)p!dAh|yJ0Sq}?OTq+Y4E6jL;jWD{!cQzm>T9Gq7LqucP}g0R z!t=85C_Hbxz?U@!1{}i#Cn?H2gb=k%V5b=hX8{o8EPD-yv$Rae!&|=2XV^TUkntkM zb))f{Ct9mdPGV)LL`&P%XWNCm>N)}|)53E#zhJk;*!N8uXo@}vy($yK*{&N)Qp6$0 zb5Ijod(PUUC9sl^VZ7>ervmlzY?rQ_2%n0_?o87{H#|-S=A@u$`)m}WXaEazL_0S?)@ArS zkJsQ)R!s53WeRpf(Ot8EV%-_pZ=9+w9Kk`{s?6VGW{$Wo$!~hFx6h@fV+Pud{js;_ zyoXL{d;8korcU^M+1mr?=6=`T*q*5W`#sgK?cw=CN@5Rh2`COJ#M(m6EU-@6#f#UF zO4YL=@{s?gY5acFi}6)P^cnx%O~#n4qt4L^?nbNv(tQ5tucfyo&{TzquT=NEtYPq1 zFJW0%6h-QiYpbasvb6AQy$^1c+-**th&FxqNlV1XNai+vFDZ@OJ zpxee0SJWt5yiU|f+W?Lweh4lMaBvQm&m<`SA z_W&o;A8kgE;vJ!YBg4-4(nl$)7j7+4DrfX0Fw9jxj*7iXI%w;i2Qa{#U*V*%o7#46VPWJ@tVLyCD-9O(2UB~gSdcodtHwH66xam^6DC_cZq`yn zso##`zUwN!j!z^XehMrYFbZ&(yWX6pn0V`?uNCFQ__|GGgZ#;G^_`9Vdsq0nPPp^ z8o0Z#KVh`9uz#$NKRT7;2%8S8N>C1;RO^E@;FFG_sJ)79VZ7gWpZ(S3Er5C#p*ExQ zSfM4-62o?-Jgty9YOc6Mo>t2=HCIoOr*&;oZrvn2!AinT!;a5kfO&+NfL-KckPOz? z9TG&pCRLg1`boLf2THKw5DDzz5=4(@U`YU+690UGDDls%h!X!Sj41KX>JTOV*#M%P zdu$oKVar{nA4#P4H{ci7p`ias`!NJHaGzbF&VK4r{JQR8q}VHHfnKuets( zUp1gv*BlHy!^mMo-@$HF9h&o3KYQQ_c{d4r?GJ# zGPfJ%+o~NffowUFA@flR1U%&+XcR!O89(Zsz^J|PgJu86sFLN6&tDxz{c0SleG0}H z$3*b%bf=XWXa(I7nqbe8{&DP*>R2ObLL}x2jvYvbvIe?glVPvWL}s(NI^Y_pIhvr< z$0%1R86kx~1My-%q7r*m;Ta+8QBJ%PE0P_w0+`fTN?UsWY~+^U=*>&!8VkNEH2PMw z1sA*qZ{d%Xg2oj}dmo)8rc2WHg!)#rLiepCgy48h6GtyK{6CO=Q)f??8tzvFR`bW? zE=9+sV6X4t%^KpYxRDqE%qyAKd^conQ`G~>Eo6KFTN@@mTa8@}n4jW6IfF3`Kml*; zKN1<`fG~qID5Ks{BVk_zLOTTz=_2?w3CCFQTwy5f3wV(g;R%7kL}&dBE+m$X;3)Ka#smNT_6?s-{9?jHuE6 z>K~$T&^oWx!1%~tr7^k{%8=GNTZ8rmt#GPqg8}hI%%U5!4XYU3m}n@@Mc9Oi&pJca zH5fVA{eN(tSnv#N1#zgB$m7$7HEh+H{e8t9&6$z@c&Nk!X?xPWbb@|>^aBdcM*w6M zGDz?#p)Gsp_A%I;Fpu@Y=}kz}ADs^cJ!Cx~Ogq7~h*}QX)>k+%JJegNFe+8T(|q9| zYHddbHS3AKEXp{>i2=J#YJ_X<4t4r{sjj&@R9h*~X^TKnBVI)*E7B$lAz1{mv+7Z1 z!G@n$VrkoRUp1;b@hWyJ8*W9cSj-q6>)afcB!RvRbC0&pP5pwN$o)h1DPN!6-xpI9 z|H;vW93@uvJ*bak^m`c8-;yXWz4V|iaR>FnZywaGaQA5->>(d+17t zJGJ2Gg!1LDz6lNPlR7rEY3lgPFMFbn36Fgvbqx7USL%3t8qncTN57YQq>i65i%T7W z6zT}$RqS*mIc^8;?6<_*0Ipu4k)6f{)z?^a*NEVq{AcWWFEsk=@FWNn*-<{w+26+u z-}_wnv>x`7v(W~UWiJ`~x6WSjmlt7glFU}jblAZin&68VUGfg~CbBp_&akaza%MWV zU~)HY0(e65tfQz?*ZfZgEC8|VKxyh7qhB7c^{o^#!Em| zht7iehR2uK;SssLPwjksHoD)<*^16T$p z@J}{$xxk_EI`|hb5{L^_8x#h|M*kOJSzAlsenLeeX!g_E<0P?ZL5a^PF|W@>Z&?b; z%@)ZtDGv$`&E&N77uQUJo%|WZEcjTUpyL_q(>S zy0&$r1hxaa)^-YOD@mzMMALVMt4IDRxGK@`{DBhHi>p<40v@>Hf|R+p9bHP)h(lVm zTu1AWI$uXuB05t?YZ0BUqcw<5m1tsNiV<^ElOn#*Ob^zbLP3tr#I=~-@J}Cs=3o9a z#c+164IhG2z^;P;R$I`hZmGdc9~v0dfmiS51HI-Ruyyt2k7-z=UyHdl)&k7RsTUzc z@0l3L>nWH=XYT9m5Wc-2`4&L815EWu4shde?WxZZIWxcuBWUq{P$Xed3;K;+fAWPiu$#;nd;4T_W z9OgLkw806@g`@B~Q*F^p)n%hdPU|`e_i|K^<0kv!llP)n^*do4ml9oCO?ieG*34`o ziXxu6v>oT?(sqxE;HAn&H`GA>^uv$#U!#A>x9-QZyr8N4JK2w!spcQ3t?$YGn6og& z_G>?8raDOqZ&i1JQ>arx^GV;A#D0wU6Ys*%>P;tD+p!f>$LAKul4_RL8rpbStKRyn zYrM2EJ%DQAxVG1L$rNa8udoVnY?x4luoC8YE(e4|>FpQNmKf<1TQ#}JFjJ>;eLR&> z*Ih4V`y~yHtACuIBJF>IN0Ih72xM7*{RhU}{I>0v%G0R)!N}9{ZQC!gAEQUufe8^R z$C!&9Ii-$!NmD=vwnD-suxYi!DvLh<=R0!F)(adYVStGo5+63vEH84$TBkbahi)IF z$>R3S96GN_vq+FemmtTM#a$rnBdWJ8IC(K3O-Rkv(MCjbbhI8|gLgZp$y@`D41`@! zxf2+*?M!6VdlkPAwKXUZa+-a_Rnvf zUsr?qP>Os<^D9#zOYV0X>J8c(_GiCyrh4uG-K{ff(XAKF|HAyb8I7~2{_6kt^UFRj zWqzHCNALW);-meUU%4m$ujkj(B0vK5-_86o+_6p}*rNyhKbT*+u`cs#!4ci(*LBrB z%&$e*S?2tD2s~Z|Fas0wi~H?7-B7i2s&in(${13KF~QMKe-yrvn9&&nkr1+Sxdb>K zH9(R5JFX0054)UCo&5_+eK_z`)Z(zboRfb~-8qG#bBEdqg^34=KR{SjQ46xI61Jal zS)z&enP6v3O*XL!nbAZwn}A}2^TR89XyP=d3DarfcBhH(2nQ7Pca&wLa0AxBsW`gB zl*n{&46wZklsv6k@`(E-3`*jDnTHT;4s2iaQ+hn%q=r>e2hN?1bbWxtIW$tWd1}Hz z2Ybyi!2ZfI>b{Y!YQUa!9FGA9MB?1#PU77iNc4kCLJd0typWjMXlYEXMaYrz5eep| z5_1RfcfXVR5yIr3*FUvt=gh?TZN5>)uW#f$AoeLdVC>NTZ*~Bo-26~gKi3GA`wg5S z+Zy7K?Ny_tDKfm7iC#g}Z(*~jE)SK^VuNUwOkLp7E;xoQ+C$^#GEdfN7&)N45{E-u zozoyk(@6l*z9UcxB9J{u?Ri2u2>HSjJQOVAnNp7sBOutu2;ex7Rf_dMH_w!qY)%q? zd(|Bf8pKnQ;)Fg{^GQ$$@?H?9tB9P9A+JZtk8dX zAH+o~0Jyo4ZXU@iFhqwhI6->3Rc-w(7r}efc_^i}-2qvr?}HHiu@2+mo=3|#L8E9T zeM97g`)9xA3HO~Sz4uk%Wv4m=`LUzKQd)JvncfeV2+}|s&1%|6#Px}D&<1rG_*IH$ z6PzloQp{-WQOOMeIK+zTQUNM+d?^F_n5?D*Oo7E&uA(tyG8eaT zc{~dn!gfWydhmXDlwcEfW8+v{)rt^eDE~8t5i6r0GXa;iG*g}IK#j8xcLFf1QVK}{gzK$gwCD3N4Hhiw z%CLcQ#ezK{YPtTXtXlx0EJ*>UYQ924Sychn7Cnd!{cj-MjtbQ2=cK4suaa-jP&q>k zl}`h^{kL&h9qmzRQ>(p0cQ_%J+JPO(hxLaXo zz1mKSlog*wS=JrL5oP+o*M6c*#vO*C#FzxCKgVExa95YJVJ*PNMA@z`$1xI42$EXle>WkZwV~&ns7c}v z)o@2th~M!`a@XU5^@&cFJc)w`x|5}UfU$)<6b$osZhy^+d$E(w!2;{N2fOKd60ML) zWS0H}O!WuoeUYH+n~!YJ_9dHFZ@JjPs^@7lEH33FRYOz;M61 z6pzq!I|9&}Nna3$VyPf5si8F?1Z4{Y25v=TBd+T(7wf$mTC9|fnY=It4EHaU&aC%G z?;_nL%tEzlJ~aFendW4;LN408^8pIjs3oTPyxyL*f#(g4n+`Beq<2L0db+P~oGij{UtSn~0PcN#b;D2v){nVZ@)7bH)T55Qi8^z_ z{}S!@bYI+D6jh2VBYj_7DWA9K=N#HL;j3Cugv8{UDYgc$|I_>8K0E_Wq%OO<-WRtK zS%|-9;bFx670-QfPaO%U#(SXReQ^&l@f5;>LMu>6lHaCQ`!cH*^qT=$eNdFSXX90 z1%c;N2csOjawQE{X3MA!dWWPVQC!?)I&V{-LEx{~nim>j>BZ4lLwx0 z1NI@0z*x2c9E|1A=kN}yWgGp&4#7)~R|}chP=^!C?(d9zF7CChvtIr2XB_gq>e8hk zzuh-tu)5Hnz9Y^sKZPlwNlis|3=_mvDRd$nC7GOwQGnc~JrT!0tjQdmh7i;xW5M+7 zLhiqD7^{EHVYDwO4EUd1l3(1ky-#svb2?B}H}Qb{66*#%1g}!onk6estgAD_dFs>C zdKv{AsTkKtR_qaE2Y*Wply>0cI2?uEpprb^*-?WFr|6@T8=eHrw+79>qciH~p1K>I z5#FIj>AL;ZXR-`u6+9K=xY(gJ?oar$YdUkL*J?-;c-T<}qc?5N!WAi0N#KD;kqxH` zO$y>uH?byCiBW=9D|#YS&IZLxc8sb`tvK$!5+``8p8;o3pPibbk*vTYXZvz!Bjzid zWwHd!=Ga>^n!26=f(pQMS1%n(MdX_|{se*Xeyl}bAw;%t&reaWEP|=^dFaq9|E~iD zjwMz>g^dms2H2uSv(t7}%M4x;&C@T1L>J>F)P>uIztLVf`<}*vh4?xKJ`3cMx`F6hDP|IbdK7)7V0o6 z=`{9iE1zww?ZnvNZ@DqLZOI&*Y{ALdXW~PHIGS%Pcm_4`9AQbT_^%?;1TbI(Eoqda zzmUvoT030#D%#SZEWmmPZ|6*2+=ts>p&iKOy5KQCSwN0ZfrSt5{=oAxJnw@aa`)m} z;Ll0IKQ9&jt6lgH6Zkj0*VnKI1;4!yKi&H6eR%fT)IO}mbFvRz_=WKEHw|kznK-uj zprAAl()S4&xER-r^1u)3 zF{3;L1jV+}u;swyM6{PE@nC6SUg%)^g>zBa1hhN4@@e)N^;bA)^f1-8Mpx}n> zxxoRr%;l17W5av7p#dBGf1Y$4USEGg@rDnxiw8U%-0)#uaKHuQ6J8e4y#2tfWWCjf0MHv9{))( ztjsr`h&_C>Gky$~6abp`Uk$Wwr?f%i?@pG7eE5Y&KA4--7?d`*#H$?XF*tq-W1OOq zEM$)ir@%OVkz*}@^q1+Hfl{n&o2N3hYa}P!UeyW7;e(x4F6i)Q9&NnqdkAmLF9W|mER%&d(;w?QU`JJCF%9Qa=-I| z=wVO(kR5s8Uf+Db^Q*{`Si&SHRDz%NtgjD4&s5=$$o1tWF0#L-Z>cEPimcRmPNh}^@=yXoso0D!vF6etBM>g%(aSb;*Wg2Ll_^1IKR)s5tgYO!Yj){|%0RjpKCc-XlhZCShg~bA|uO71ViZN-M1uvk);$+pHBl zn`xH5>eyQ2rRpdk#bah^Iu7kqNw1PEldQv^CG?`$K>9nLxonFTsCkC}`lfmQ#huuE z$(Z8r?~~K6qh1Nw>lBw^rkt4i)W6(4N9997Scf#M&gFyJ*+;wn;5c<kCD2N^oB^g$p#ko`Fi_?LaA&9?nHv02I(-3C+Q0>(RAfYeL%tNG>kz7P-7|B7Tl96msV7Wl)EXTr` zm@`8%gL|K*`pTi{58)syxeoOZmPkMWo2LE1UtJ66u!BY_!y%yPV|+BJ4a#(Z;BHg* zyq`d+zj~{nlUBz-oni4nCH*d-g^O(yiaD21auPF8nOH`-1WGu$LseReLEY^m`5}e_+l&B6I&n6r(-$R9ELf* z2tMQhZG53Vb2J|5JcBeIc%(!5_*}z+x)B7T&S!FV$h-wL_TgNJF9rC8=te!3eEHBY z&`laxF%Z^bbQ5Y3Z+ABRkwKiIV6ju$zo9b?hDX0?{Q-d7T!4zd(<*! zWXWkx$-kt?q@@1h>}T`34ag7JvY>UdrT4EE`B5Syis!fROc~;@uEx-bR@daOnVJs% zTKxtJs~_e1d~)W&H5{otq>wL1nG!nzS!u!WZjNhSVP~x0%x-BOt2YeDu5dd%n6$q6 z^T%a)Zt0JTFnC|Z{v?|!d{j;2a6M97($yX-%iY2KiZV3po)mOh6J_3`Oy{ZT0brVNiFVDuhQ)|yQOw~K&b{W$u8-&7g% zQKnbMh$qv5I5@=#^ zFfAgS(%`p{zK{mxcyvc$I?#?eJ);fm1N`Qjf=|RGqWM~1RTbVOb+_&h) z_j%20bT#Q`1Af5D*~54aI76Uue9e)0n)fyd{m)JC-cuo=P_M{d@ZRtR;JZY5eOe?8 zf^K!d2hVMVw-k4J*&BFcJ%GOXpImhfo_G@{xMxG;Zp@=zw@X{6UGSRf(kasg+EZ%RHI`}HhQU@@! zfGi9>4dA#VqoUDS$$hOx!qGs>X)+s+oc8d`wcnR>3--|0GGysRhpSqofyJfr(7zqw z;i1cMsShcjKTfp7^Huo_aL)z!L8i9F~TI z;}$VsZ^O0f%ceN)rsvWa$s!-mBE}bI24Rx13b`W$<66NzeKDmTL$gQ@nS~8$2~Riu z^&+^&(;P}34*diQ0Kz7ay~Elu*Brf^9;#ZH=y@>{6Bv1+NYHcAxy4w!&Di5kSdYQB zvqqtDoMZb4fnm#%WPKicod(L0D}yvpq=IFDUZm_Eb(};%BcxfhEqr67bkq!hZP+WW<@MG`^iP7z}BIv7sI2;WLZ$ z#x?Ej(hv8_<=FQCcWM&4*AlXiOG8_=sFLCVd!Czexb%Ev3F`jHsr!&_b*r62*&*y_ zkhlNz(jM(A&i=oF%~^g!>wJ7e%l&><*5Z8_fLp`4z4+%h_PLbx>bcs!wqPG*5f@qtIh8ah&62Xc$-LYH`c@ zoZSTV!H^WqHi}2lY}ZTStiLvr(TQdYY0W*}ddO;q4G8cGDxjTl>Ay)W9@~Jkeb@}w zcw}vttlfN)g_DzmgOj@)yeY}S$;_#RAwxabHW<3&EvTI>#eGW{xk#Da>XukK8q*)$ z7=m&hEh2o;KJn{*zhWi2<*px{cFlE}Y@TQOZXdwDJ_l7w>*A5hkaZJw!l*~Rp>oB; zAOaPqBOC;i!SWRH&zidu78%z*J=s3$b!<@_aKr z#`!mTVO!)ya4t5c7LCIDF@ZM!4|n%R4ENAjb1VJT|K##XpY-Iu?UA{7OseyUPDkxr zH6cprr^mp#oW!|>OU{WxFLn+Ix1u@dg-s0X)qIkT?4UJqL@ow{D-P~u=P{uO2|N+Y zh7d^YjxQIOHIodxq(-d+tDz;XFV|;|DTw}Pb~*>W5!ueS)Bi1{?R3^z&ypM7OUG!} zNhPaTSICak756(sOBnpjQ z#~y^s(OKf_{LxnwW=6KTPuAE%*~@ubJrA)1gB$d@K-(01Y;I_btl<4sxLSdj64X?U zZ6|5$Vnyblc0KSo+vAHJ4`{srfV0By(qFCcGBD}`z!*O|<$Z8(3wU34XtZ30;IF1z z!{pId9G4mSaI$sVaiD!{F}T&c~)Hx6x_%bYB3hC;FPdM2EHK>KJ z8`WPIa#ab7lCx(^bzi=mco+P#y{h_NH43{jtWwp$#DRqvsT1u{ZHgT1$ZK%d1HS6# zQw1=90UM;{$@ZY-XkJu3a|oxZz+^5LWdW1U%{>FFC(z zY^MjKo&KmyBj-*DyWp%Z(dMWdF*|il9YR)VhMl0oFxuRz?uDJDC(5_HlLz<=ng3L4 zOJxc~FBG}4N;FZLm^f!(?q%14=DXNOOjnm;|H`q+LcSjeuu0I_fS>l2s;|J*X{^VU z9fM$Emm~NERbOCE=nkkQ<*agUrD4y27QfXqUc)}dcnE)JJVlbjh88H}OhTvE4p!kE~P09NCg^M30DELFBJIe9}TV#|;0aPUpLw=$qMe3_Bv!uWlHgV<2R7r0eVHl%3F+a(t;l4M-yCgtlY2Dds!w?uHrW`l83Nt^$U-=Q>_ zT%Iv{12&d75UBHPZA#>6$ zJa>kyK6va5nf(BAAERnxKAOx0jFFc+Hl<-nXb&n1S{FZ$MOEl|Y)7}J2hAbebzxdDL;l^wos05YNCjrl3AanTN0*&$$SJX0f=}0h!n)6Zx*fvduj4P^%4z7KTnggx1bKwH+}w_fLOJjs(!G;sK0RqK zBMmJBGG{yPzeo(4M{WHq$)jFfKu>EebfeGk^D6I3 zI(+k7z!amYPX*X&s%>ens)U9XAB1G!tx+BGnjRDN43_xuGIbD&s4QJBNvrV(63y?ETaVk9dAK06YXT;ezm+5{9@F~k4p9=U(fEm1lzw_dM_M?3QF46w#Q$T(m zpXVG_xm_KNys}>~Z7SCz_zbIhS48XtI9E3DtIpV@0rN>L2GzOHL(!D1{$|9AftW7k z@BQZTyp}Xs9<$H!XT3y!OMBe%57FUz5PQ(NGLwD!72@ie=W(e?Qv`Bst13mp?|oqF zkpYHPRfH9jQzF-{&wIOOK#W)ag!K6 zU|cymEpjGZH{ivUZS36u^IRurw?E*3rD`VNfR^1U_oTv4Q7!o?W(Kr8euBN=-G|{z zgGBM4DWpJ&^bf4jLI>W^C{n()aAE{YX8mU z&*`zeUf)*jG(3(9aHHFD;{ZDA#-1VUR>5!IXD=)2)HSpD?A&HCAN;W$ru8C(TjW%N zKIQ_RO6R2gy1kzRGy>35SG|^JHiH{>q+_ABT&AQlL6q@};1gVEn1+E6Dw6>LJ0%Zc zq4pIv@ck|N4jWh$P02=>3%6o%mNTa^8Ftm8sptm=A+ZBMAyT#3Nci(%vu=;#M=wZ3 zBT7gxMM--UMt&do`Gv;M9oroI1ZRB*_({i{A%4Es9Y5EAaNrDPFU?+a06+m7riG!U zXeJ)MwZFHK>7&1AhLZhtza34klK6Z>#^VoS(wTvFF!!JDL|4rKl&Xx+>zO)n#B{{B zIdQ{~Ti3ZbwKK+J_?Q8?9YyPqCdxZ`ZKVn{@h;U z?azD%*#D?M>|(M%=eJ9L?%JsPgTU#}&uaJ4pM-xA&crXzkp;0$IIQ!s4eLaD`1JjV=#nWYzr zNrAqrBU?lERXMrwBa>}8`!So*-V6Hv1?u+FsGo}rr`20ff5IoPEohA$q1(mR{Cx2y zGHH&tB0}tp6MrW}TL(I>$M&gDojeonkN8r(U2w~3v}eohzBpERW@Z}tvWn_whd2$T zg|C1Xkw?YPIiHz0cc!3LiYjcDwj(NGGg{NfX_0w27{wK^-tR z@x}R1%G?nqxS&||z43&%Mg8(@g!=Q!yiA}UZECVli%I96^w-{|Xf{xZ%F(7a-kH#G z-Q&?vCLL!Vz4ihyjmX;&TZDe87mC>kJ~Dqb;DAm-=Kf=q?o)rl3$+nHP{-cM@9U9R zwXrdyzWMpNuJ6ZsZGN?zP^b4V;SWjv3jc22@&Gtsw52+EU?%w)uXmtPq@5Bj$*~ji zT;!MMeu%=qV@6qE`_Ki>6Zuaoc_(xPY8QjMIh8`AIPPO83+;b$j=_LB9{GqYVXJy% zra+*Epd$6hB5z;1wBPgjD^#QAqYliPY*bsy=jr-6v0qWdwi25SvC(YuKisdFOR<%x zqR0J;@yJ36cRujI6EoiPS1~}fk;F)q(b=zfh>4FPk%fk$kZL&BvtNPxczUqskGp&y z_WU7}yPBUK*ODh;&wrdk8t6J*xtQ;lOKme)w;YaIl zE`6o?pWgHReSo>}{H|aQ>;mRMG}di@VLO(b39y0=Snr`Earf3lKomX)KR4i~3%^Ui zC;LD8vh$_Wu@4l6uhi%21yrohA&K`6De_t_Z$Zarao5KD80VB|L&PXSj=!z?2-b$n znJ@Fo>@I8=CP|1+wS&vj9W+-z|GiX*QuQ5u-oi5R~>CtuaGZVmuE8mwlL&X!tQlWnB@7S zwjC~3YeWIsr|gQYk1ET@qoE9I@W7V^JTPT~2Vr0lHt=Bq4ErfARJ1){SKKc6*RQc+ zzy=UerM{|6=U0e;M3`D}I`2}k@3umqwIYY;gks-);=Hbudm+C*MxH*MtNW)2A(*1m z35@;GFl>gj*%$I^K02a@M%Cyhv1p%8JlW|HwnUn%5K&7wKBI@*^{#XgwGgVIW4nS{ zD6>Y}G3<;#V*LiOBT^W8EY!O|lT?Whkg^3xBqWJ(2QGVX--Dg3v`*zlr0a%N(XJ+O zOfVkjAe@LbnR|VQoWqXZ65!gEG-iDAU z&qEu0$1;Bf?&9aD)o%7~C^;s9lJ`2cS<5SdZfbVhM?#tewp<4|*>)2I9 zknBF5g7);ct=gIc?H-RhZJM*Q?!z*1)GLGzzS^)v$cNjgop5;w?=oD@0MG1perGVS z%PRTQ`NE(%t;lQKtJ=I2>X2vaaP(Q;^+^&RFM_4^h>tU182De|<03d{dhqdSM-m?g z!}DW*@o^WHh2H@_MiwOTkysBv@7;p&37T(`?OLY>tr^0^h5|`J>ul0tc%0zNUtJ>^ zH}fzj=kso+TN%TSqw55()q4mN6$nj+I^0UpCom_GfDe*(eJ#eD)wf2 zj0ziaG+4*gTaKtYn^2`ILDiKtrFB;6?cy<7tvyC-;uonO!&%ZZil^XFAb6j^+gYVO zjbv?epsokX{*6P~a>#%O5weAM=?_d$DTk#P98i`F`igE4dR>id>A1e3qfx}P$4WuH zT4<4NR7!B%9S(JuA*?inPVV|uKLX(eahLPIbQ-avE;%LzQF2Q}5>d`}z4nR6k!x`G zrP5+)qefJdAgawO1)^|#qm+o6^H~a_((x#WdQO_n%Kb$TB;U{sKAv)LD*)chYCOY7_R5!OP1;$7~En3$RG~~*)t04*zZZ$js)k_`wq1o44y5$ix7^? zjJ_(-J?bztN^yXsCMT&Ml05n)?w7y?XXlMh@oB8D=D}>ER zDK$JX`1p*JTg2WqHu*XN4QXL`TnBdH zZ4p9+un=K8!hD396N?Lu{0;_kws6ANv@?SZ!pbr6A&i%jiGo&cY*!?2?@^VR58AKwxD;K)m4Cv&eoY7Lk zX)Ubdl>yZTA8^)z7a+V=NUD&tI8m(3sgEJ`aY@~w9zq!4r=$ze1Khj!HW%+?bNQ$O z=YMpcIdQRnRBK=}Y99LSUUO4z2rGhFQk*5A+saKb1_sJGGy?t6y=w<5q`Q)b7U|yG z1r4{$-8fUh_aU#v3x_@KPf%@sVq}l{MF~|%Tp#FYVRBInZ(`uf>&Er<#kONm;dPM- zh~)~sU!z06M_QkVk#b6ao)wT0qgIgG^+zFF<^B>3T zUJUMUn0;e+BQFM{&7oD&B2d6>FrigwEzlai<*HMsr}4@9v&UGapna#i4~4pQmmU#& z)a_$aHRe6)C^#o#Dy>w836@f_#I~w8&yi{`rVe z{*@z?=_5zPDEUe;-$d-KtSN=q`vng!mi|hMy}H;BFc8J-P`fj`u`REf06FdpgjTcw zoEY{HeawoGZvf;mnp#x>K=Y^-Z|rm+y#p_yR_y*6k=;dM?jkK+g?Z7Io)koYjwNDJ zVQvzISi7aDMZ4aT0g;L+52<0M$RmtG$}PBr3a zmP;+}1ulElMMx#ZT}(>p*L74s0tCDW$lM8@LoT7OgxmxBMP3qfv8@8hxZyyPdbti@ zBrjlj{0%`}8&S7nqz82mvkXx8PlVmhZ><#4{hTPXGG3B9T(a6E82T~;k&ipMuWA>x zGQq}LZpUsQ559}CelfuO$P|klrby;U%7A>Ji-<7CdW4V+vgE6S0MEfQiPseYv2^if z%eY-RtU(s(r;I(~?5_fXw#PNLnAZrAU&yGOm2@3cDvOq@g|?LOkB`d~ofRd@8d z9e_qCm2gGsL?ow3CPPc6RX^d-XlG09AW01yH@9dvDEQ()VWebw8Z6ntFY!$L63@gh z@l54bvg_h-0RbL{-lRcH?7x~|9d_a0?QE`EHnDqlhW zLN3h37Q?mqiUq>YN?T)(vL04_MWX8SFa$kSEfy7BHB}B>^;E=U&0i^N{wl8dV>3{L zTA)%m*&sa1xW57GKRDT$KP3iJIhlA=HjAM5X#Xxhy9M)P)GMCFzb$HlwVDo#z*QdZ z7KQpR-jp?y3$;39t!7uv$)nB1V{w~b5%BENE@&%O(<@ROLRZq>AtLk1hcmyMUYTNB+T29f`tBf6ePe|j7T{4$2}l{cy1Q?-vbg>z0w0y zcl`Wp4vP?!*g~WV;qH}INmbGu;?drMP!))V(U=oL1((1&sL}AsKB%CH$ku3RMO0Hk zn@eDnoPvfqE*iEG4P`j5<&kPWJPI1#k#@6=w0b~8uAqSk_Mibu;VyR4x}huI3g4meG|6aF`OmnA=Ya>b0k|uxICM zm*gD!OPstR#uZtP~PiP5S6@R>mFzM^dMzg89<>4w%mi zF0Ipx=u>!X7o(Nqrb$ zQUMix3jSI%Vt%B1{iO`+EJeo!wl@hZoc689w>jm?B)&wahCxpX7fHrd9mr> z54SHR?N5I`fok|S+Mk%eYG=9DVHeMlVIpV2BO1m7KCP8CF$7e#@KwyVCX^yDfp3eji0F_zZ?AQI&S~)v%IPc ze*Op-ng1L7WToKencww*pWAoBn+c=AWzc-)x3aF0VCc0B1Vt}s5}GCHK#{&i;zX$C zFh-HdyZ0DO0*%yzR!B>A2uVxe&_YXKjzUWqKv6ct@+}s%Bsqytj9(zcv}uHktkEe1 z)Bpmy@(w`|57RU`RMzScQ-iu=Sa)NV7BB3nmzF^0BzgEr+T(7+H7}qgoZ=T+T1Cqb zXlW37m*P(Xe+3+q1hVF=<+3w}40Q@XEwRoVAs3m_1qw?%+&Y9*GMp2zF6wqObIIQ* zVz%Jp&Xjlh;zE1e+0S8lyZdL_+ogM<92T+*Zf@nS?N;^saa=O(P=nEsdSsZFp*Z#d z;SafTcs1j>H3@j{J(erNNq{||5N0E6$4OFnXl4Q%zz7%su3c0$_mVQ4o8EE-bF%$f zum?LKgx~~$V2}DKDpV(?Kp_4T-u5RX+aJE4?KkqUZ%^$naoT?n=2}WFKihxkNG}Fl z{|)bbc8!e*6#ViSk%_b!EJc*#)T95=1J7VYX4WSCkN(xHyMD6M^xY_}PD^PzgVNd%TOgT46+QYNZA2C#@!6v( za8mq_{Df+}2P&SI{_<%go`*f=Bi$tZk9MJivJS`35Ac)RA2P7@iX*9h1J_fCx4?|p zYSfdsA5r_Sfj0)}q`|*RWQD6nLC0p`VC45Wd}r|E)x%g%4#rZ~lf-!JDpZ(!)B_+z z4tAY3brCfN8Hv#e)Z>my`1_>#N8MDg4>Xn>*At`Ix*b6PxE;tH_WOK18c;WIp*CCIkzTSvZqdcWJ zA!1BC5{X1w^>ExPvcqpDHI~`({s<;uR3EN0~%bm_j(X?Eo$!Q7 z2$60h{K4y<5kBt{J;G%uLSWwn%T|jH#m_?gct=?1kK+rMo;_R22QT(cl3LX1-8^;o zA7wpIfI1!gV_y{Lx$#zbP0?j_My%y>Fw&zcIg?!eO!~IUvh^+Dw|~udi-s4=-RZY~ z?SpV%l#S4rhA&{i=7VE!?_$pRjgfCbdIm?oxMV2qmC4lQ7LO^aIWsNRifm%wC>C`B zH*^*AyHoIW*sTu10g#D2?1lX^I%T^>7d>t6*3B)>+3-=n%4eHuhNKl$Za=rE^2N%c zns8cLJiOtfK^l_9BPyS5$9r=(vu-=L^4ZFoi_=oF_lXDaqS)M3yg~IVu53Sdp8xvh zdHzLh2l=0TWuE_gxf@X$ciHY@ytxnZy6JuA`QsT+-Ap#MEIlol2C$c8k7`N`uzN(s zi$C*K5%K-ub)L=pbw+Poax`_QP#Os3ZS_M)r(oJvxE^{d@}LN$fKPLYk^gP=DNe$` z&QfA6%Z{&2k^~50)HB8y|BXX1r1vfL9G%&HS-Pec9>FmyI`RI8c#rcpnN5&i-t#wm zF#ed#IDeD0$7B3CN6*XEo4$X*=v9q@6~{uvsJb;1hfpgK4PmVi1fN##frXb?OEOE) zSsuJNg1Xl8NJK9v&R+(vV$!2bvghPcLpm3}(j{<64?5ocEGN!+hvashU4ME-W|tb< z$teOifuwd8M6a+_bL~^^PGRdoAa!iiY*nY4dQb8DP#3#R!C9y>!DKgIq^IED_8@Vn z`ut!_zFqi1DOQvLS2)hhae!WJL3-EmrVsrVq6SFgT5jUmL{mi_(A3Ukv5j-TAy9|K z8kBL`!2`H);YRGN>{0gtJBF39p4zUs6K!B2J7YBx6Y^=9Ezw z2cV~^SzMHG{8YOtIiDi7vyKO4fKb{p9{nJY--QkJRa?NfATilxYH(Z}P=Z3513Q4Y+x9En2xl?Zr3t_IgDz|*r_1DY+V7MD>P3iSZ z7R@RzM@~B1u|oJ#Av!6WahC=C^`w2Upu3Lf%7R7_5|0A>7;b3;-+3DgPVp#T&wABE zgUEckF`Lg`@GzT8r(hs~3g zdmF3=CM;6=Lf0mQm|SE`6S5ID7&xJ+zl%{%@Wi1W%@>Rk&!0+iSZg@GWs<250*0Q- z0f=&wgnHt68TasU&H+Vy?8H5wh)GJN?=h}iarWmjpZnn@mR9Eu)B_+N`-nK+#^StR zjw&gFmJT7RTY}bEqU%#6>j`lIG6bqW2%qax$Gsy-9cOw3REPG3i5!+n9edQlgcoD_ zOB_#TBL|}q?^ahn^l&>y8?cn%10W&u3zY$p$i?*Gu=2Q8yqE&IaMceA zM>fS_T5#+~UnC7jV_}e|A&I^sOA&(k*MRxgf+u2Rc}Ju8iqwg(2yP#RuLy1*rHf=e zTqKLoVbAv1d;F?9XR_n;G2=$E9Y9l*32+@R>3ukTe29*9_X-9K{}rc?K?8c^N3M-> zQm)p4DloC+UJfSzsqmJo#4VmR>QG?DgQ&)597LUi%0njn3zlYTAMz&3^P~f*2w&LETQG}9y%PHo zTH#a@>XQ2Rs3`!62TGc8@fCRkvWOz!gu)Y$UWt~_)Z7|+@L{5^gpyFFd;Kg+;LIFo zKy;0jaL>ankC=p|O^}PNP?s;wuHn%C$t-=xDt%0j_R)Jj)ukP*ftm%`g}Ai0$sX<& ztK^;JE|k_La4Fch@by5YJ#@utSE>6nlhm?DU5<-OVO179&9F>6 z>Y-<6=7j#_3(bM8V1R+Gkn=*&cGlyZGj_&(lG* zv4hpyAQm_2-J^jUO#0)2Wwik>JofD(M?N|#bYxjEXeV-IkaQEi6c;pScJxgPF9oAS z&VWK#h_D@DK0-2BU`iNlETomKjns_lAm4+OE)pqy@_`BQ6WgW5&xI6AJAw8jJL>A+Ru3^7?Y&>u7#4}^0CfB_-4f}^#g zE{skFdIJU}JDT_cx!ez+^2bVdlGT0*D%REVYe>PW<(#3vVbs0=UEM&bahR^~wI}-N z)i2xIF{VpJyrWlFG$&cnbcwcP2G?=&;I9&2RRRnC0QYp^6=&VwvIzKG@={SRVXfub z=g@aBJhnKtSIG->oXcUYwL;Ik zeg>{lQ6VTExKBz`b;ecXA)-8Hj6_!vt-&;}1%i`oz}8cMv)x@9=<~gzZC-RDCYs|X zY-y)`Rw4(;|O(+@DU^H0TsAM4JJSMigQo2T`01Idl2H|5w8H<-f^wa z?R}5Abeom|1r}Dlar5;XGx26eI;S!PUCFIj6gp&K82p*Y^KBAJ0SPeeZjhbMHO(oO91T z_Z&QiAX4M}QN!z638G*P)k#3x7Nn56FZffgrJ+5u8oGNm!bCpV5`%2XHgi%&lb72j-^cNsHYpvM&-b+a zsPcq+?;B6RIB^1L#Q<4W>nYeNq{pmI(>-AV=;RHO5mK_0_r+3ugfiv5&tv>|iqXr| z_`p#-G&`b^0*4eQkv=XV>(jPWJCBX&3huFw2nQF*Qj&1t&UtLJHkdt81%PF;vsLCL zu^`Y9Y%rgCvqi8CW&yqcWYUWaV^ajyfpJ1W*+QwQxUe4xs3Yg5*0#vWdwrEqaMwq< zy|(Cv+jjCw6etTlEK&}_fnpIqOsB0Nb6L69&z)t`U9-!9f|>VRpqQ@vH39k$!aPI< z`ZJY?xt<$rnfWb9imIlqSw;=xb8sxFj-K0Q5$w z=~AxV+gwE{3H3F9jmUf%SVuFp6!6xICvC9ITmObU#O5~G=EhPm{*)v+V@nwhv+*kT z%q3H{-C%~0FQrSF- z9Kj{8c?}d;>``1t`Y5g)JZeL7>oiko&Tdxo8Cdhp6EccTHRpqHow`M}+Lc1yStnd8 zv|5v-2}0|5DFyD}YJ#f@7AxKq^TGJP zk%_T_3GuYW6pdax@P1Q*B-2x0CHmdEjl}^`F@8f7O?oUgz8lQ$lu#usOD7QPA3zri z&DWI!XX`K*)5VM)4|02aB|YqLdrU7?Pt-(@DX4lPw%{!6^qnoP?a<@Rf9}UrA6vw( zTsk42wa31eV@Jx1+vW9Uc0XZmo2*hHOSY48X5!bWlR)A!cD!dhKTZp6F%A5hq3jZq z=MQ3kul=tObzOR=+$JFM-p)@>V{uLnbI4X!2?jbQ;_UoMNghJiSpOwL)T@`QEF!N7 zBi`V%=^D8wBF5Lk7fr=VhDtu$9qn5yEOK1qiuA$gG~Ddyl@p0qNWLT@co~-OhX@F7 zX+@}bg=aS~03Z>Wp8q42IMwS{qKPD)6WveRB`L&3U%B`qXW$3A3rvWAVu&D-+;`!_ zj*tSE@*FPUgHk_%EvF^IOno={h#xqf-z-UuXW_-}coqt{2LyW=&rOeKjpr@A%3}NO z@q9w3?fWdOtnn z4|mXP3^5Sz?d06no?$YnG7P=iGtAXSdfGFb&v=;qudr)oTYCn-6mmwb6$GN$SN5{P zt&fJx>!3|~_S5@;%=3J+ChJ#nj8mPX($gX*LO!9A8e5#Hj)cJn=~inQ33z6CL-w(%*|e zkq>v>jyiKqzier#+e`-@rZ-7I;yusvb1j@JD_D<7Uw`Hh3Z?k`f^wl)>GS+z0rKyx z1GAhzY5v;FdGvdY+nz@g)B*H3Ub@ADx89-QWL)YY^h>UzY#Wz4k=L_hQR)`3#GgoF zMi5nn9KiX@Pb~`XC1S|{z@+}$R^!4s<*A_};VpeFGvTldl4yQRmE1jx9pRq47S4h& z&a*My&F*|GeNFjtzI*V&)Mk&{=Z~but=mi~v*T8wo0cucKub*B7yP-NKk3z#Jik@@ zL%q$q3MMauv>*KwB}jC&d4(jwBszQ|^(61Y%F!e@X;bpP_{x7tq9TDz*SWPoR4_0l zsYQ6e&TDtnT(wKy(Pu#pWP^$~X5oFRcv8bdD?gtj{sSy`jPEOZ0sx;&mGXNkzcLKNMS^B) z_BC`tFUxhCZ+Nt~(atFp7RrtJmjsX84M~WNO`YydtO09UtA(txSXEkjwpC5k>guJc z)e?P{)oqcb5w+%p%V9&pr7T(nJ5{awf@psOH75fk;gI+WW~F z{xZb=cRoWXql++^?J}c~Q=Wgz^!Ko3StHl}u#TL%%=oIeQQMB`um2CdDTCSQi3FO` zFaBVc8U47uqU$AlJp9q8%#Bc9?q?B~qNh3ZS8t`qWSh%1-^Y!}o(w_;Ha-3~J0@Zd zZBFk6lX{E^LTUN$w|8?yHoxCFCP@)g30My=BA?-Aycc?uy1$w_^Tv5bC>G8Sjvn>5 z1^ser#`~k{SU@mN1S9S?&xE3|3UlOR-dxEx7+VjbS3`G0`=e`zcJ+Uuk(9Emzfd*Bt6r9CYpx zDCD+EHD~|$^07^K*Gx|~eODJ%T4%ndFU2z{7fLKAZ@x4pR|-%B*jw9|F7{h{=?3#4 zM>#EhJ=UtV186dK+C5V{caaP^Cbd&{td2^D=%Lc^}E|QeAo0%($3$bd};sU?M!m#=nfegBC%r&dW_0 z;6%lq*QJ!kTYnn_9wlPFe{{jDUohwO&q+e7-g=Qy_PZ7WjXcZ0S`sWKQ(m&yRlz6pGf2ss~s>W7X$4 z$-{h$)OkkNc#GTFnCGQVn!eZ|aPFB2<Axl}-)0dwEWXV@D8r3h+O&w+z<80_$)B*o`ok@3buL~U+_R;ab zQO0U$ilmG8ZcJ|(l`$_Q!K!Rx_UQE%S`u07W&PF5KiBgpr@Q4z3iYMf42&=*e=J!w z%Nd&GUDVN*yqXTll5a|FmLw55(yjcObmjZGN$1-nR;P_3cPj6h;_p!p2u7_p$B>@M z4tm}niXCkxBW(04JiPE~no(|pMc;=}e){Ju47rt8X-NESJ#o^u*s;BPLj&*Z1uwoA zqfCliR@CajDc63GmZ!`LBng47k2tg*`^B%>$US_v{Z8mQ@ACW>NZ1#iuxGWXPsnl# zK$)EquKc03CfzJQUY+?&*~Ke1vZN%Sjc+o?;K7M39uXjt%JMO@YLrpZ~)MjGYCXOU-X|^HwB`E@XNbh9Nl4xR&x|eH#wV1_;{sDrwE|85rP1JS)8!s-Q{NVM&$acSw1Zn zue9xnUEE?cauPYrM;zLqmtBhbjiaec7o&<>BBDhlCo6T`eQNT|DM&QR2f8^B9Gz2x z&U1^o3CY0~gRH9q*OP^~@kyW1$Oz$;OMG6;VExiYqaOY}1l$s2Q@!A<)R(z!w!!et ziC+TSY;-v0aD38=f_0DcWaYNTC%_NQGo7-*YW3A_@wjtTe?ZbFN~FQgrM9rWMF` zU=IUiiN!8t%ukM~ejA!uO>?~sD}?{^O}cBvb^zVb2D6z$iE1;CMZ{iKQqJ9hlzelk zO|hd%IL@U^;jKc~x;g&aV*X;QVP+PYRY))#&yThx6rClYyUs7!%>I0^`YI4rHl}^T z1@e;{9yB!`IaS=)KX{V-9ey$yGxg=V1W(OR+0Uamhu!vP7`kYt%lL&isQK)S1*GHt zjrCoVTHIIvirGJsU=_*zmYz3c_}^`eFmy2_C9H?&8xV`WY>spjBGRkA?4H}HbJ|py z6?gN0grYvTNhxN246bG9U!1XTAlG&8)XTu+ zL`r(sA0Kg_cD7edhm;-2cNz0tfJ5h!Xr%|*X~+@K?gW+6JY+$|<`z$wz#e(Kye)%} zV+j{*D0WOd2QY*{a#sg;VZVPv*3J6*Ccks;FkC){UvD`spOD)wf#Q zo@dv$lzi#>PTX#N4H@;Remi+z!92@2K?J4qQ{&CN$l&vCmhmjTmUu;`QRli|hBVn; zY~Nap5wxQRWdVjBiP(V>p&-5!&6>wwSH*X{1tBf*PFlYz`V~2*J-t^_xb>wg(# z&b~VflaZ#9nU8QYo1-NQNPR4l8s9aTio)?E9D@giBhNe{{DW-!lFdB!UM7Mk@kfpD zHw9#5^gzu(MDl~qkioYJ^_*(_&faYk#Rg&2+qW`C&Zv8NgSsx^M@Dmf z-$@q@ILUX4DINh-b=H6fXLGfDSA0v}KOQ}5pZWcA!ehiIa8xdmUXD`(hQCkvTo&9# zyojgDOJy2Agg9DHm1hNFH;#lH3+XvGaUmIR5rEwNj1YIWzDuMj*#e8^j^D-n2+&t4 z?Ccw#g#LpA-#jOMVUIKFBKDzWIW@P^*viAmZ06Gvzr}5@amO_B-!hx}>o8S0Gh9{xxZ4 zR1$7{`VW^%Gx=}q7%RK|8oEl|JK4IsVaOTu(Uq6sqI$DTJ7&+hzCeTXdX~n9`e7fIA3f8*|)dMawlTVDCV42*pK&97;i9N%1|T(%szD3zMm`--kX* z*GhVH&20UxrJ;Rf5Y&{i&AgAcN{A zy0V<>2Lq<8lq92!e`src9q#9Vqo5(MiW7GMuZ2-b}~n#M4qNbbfS;1iL> zyvy?*Ah(R`a#Dh^1D5j|9DTs=_>oYOV{ezroKf@8g&FZ|PB&sCO^EK*Ng%pl^neI& zM9<3JsCjjx_A3j|c&Q>2@V|Zlwm4&ll5V;hWN60jD|vu95|sIK#(L%3HB)4qTjOOi z(}(<14}g5No57(x8DF5TpPMho;O9%@Q=AhrB3V>_|jyfu&}U+s}UzG0$9%h?+@WU3^#hh&~sAt#WBK zEFvg@Wq<|uNLcd^(IFEfRzEu@PLw);u+tO?Q?6TGBICG*U3?JH)1GIDhH02ci+7nG z@}s3MVr&#Iy^|?Wa+AE=b&$^ZkXmMZre z9K>BoaiKa3&6S8FFOkt)!MD`;rE9Mw>vP)~Zqpg&A`-F&gsr;G40ubrI=3UIW;A1w zPVY-PN`Ve^+HFuPpZ7L=1#!F9u-?4RubKT? z7Jg^ygNg#t7w!EI#Om%7c_qH~%K^?_M9Li$h*cxyZZMHM#59mA34^IG(sZ>~pv>o4 zcDxl~usLtSY>+Wl449swTva(p;5+L+k?OfD(_*%)M;RcSY}t#x%Zq^g>{dHJE&OJz zFgyOP9aWGyz<`r@Mdvly;&{_AbF>DjOx~{J4J{0)DN9f85fGwsBCGPGLW^N3vsibm zs%bDm9#weyv9Q%#C8O?4W{u7T*3IV4 z9XRKk+eBDpOPIqLa?Ne$(o$k4m)=Zr&fVV=D&KJBX_zAfhj$Wo?MLtd@9ZCuSW{X0 ze2?{7J7(8nOn19i5nw9(X=13F&cVjat;2J3mUwD*#^zxEV0qW9dwsQI`W1(Nm*^{o znid|(7ejX}Ak~QW9ZEEwRW7#ygc`|+F(_gYI5LfQsn0^<;%^H4)}gMdW~lV$RsFBu;=+Fv=ErDOp6-PD^B!@0;G=Dt;#Yue?@6?N*ub zEM$6Hd!@xf)seeUTKjOaz@=EKtfuEBV5QDxP^wy z!5x_-G%YV?+AEc%LEe)xF3xv}eqek0mjeW%&WRQxB43Oe>^A;q?hnga8!U}K_;P0B zS@H%lPH*-~&oof)1=ImaFQnQP@;=4Br|gxMkjRv~Dwue=G%49*XTX&-prPIS!V>J> z7rxMA?|a@tnn+dAtG&`VvPe%RFb*u2OnapR1*%oTsWRdp?JS88kSK+QP{=GrOqBg^ zZ}va8W!V2f$>{1FB6a8y?$r2Edu~hr(0}~@^G9&wr2iBC2o5V3Gv@!4KY}fAo$v3D z;2bPod+|r0`VQB=cK*htP#8#V(j@362nX09Jct`GV%1~5f+MoZxD?2nOO@Lt%^K_o zC*rE5lrpm{rK;VmCPY*qrnCOB_@Kz~-cTmbt@?%9McmUXZTvRKyKBtrTK!uaB=HqW5nw=lv30f}HAE8=!u?YA@cA zy!OVPnD_pG^Vfk2#Q5UW`Kx-HeG>AOF%&zZ(1uv2hM@jyQL$U+Zxt$ua-PHQ5_rQ% zei!o#ujl5FhLs65z-$-5WnrG~5%(7Q&|obrWV=jT+V=yVK-priDuqw*$MouwTmr3N z5PJH~7b)7k$S!76>Wx<{rxwcgP>iP*UZM}Lk!>bVm{9%bmMm3F>jHQ9mMI7YSB{4H zpg3&JycxP36ROYZdQ-;$5|TwUdBR)O+hxO)rG{W>nteKBANh&$zSF3gjm-Z1AXB<_ z)PH6NlV$hY{;V*83_Z>hB)Eb~GxT3LQh%96yFJddv^MK+rr&(up!qPmRwP4^v&XRQ zk_^V_QLOo^oB6RUrjohd>NdhuzF|8j78VGJqCu6Dktr)^hKLP0H35+!Ft`U-#HHG? z;7U~+zu-z$6Tjd}RU^N}{4V7eep0nq@QDy4g}lp6dN4dEaSA3r&eX*!=OGtfg(iZv z(Z+I9Sy1F@F6nST>-8S{36uDR?ek~Kr4b0<_eZ%*>YhNc*sxpI77-WpaR52pS7=!w+{#}B#74(aa z&votaaQvR4Syw#vU!_@B{yd9j?ZT^$V55Riu=2;>pJp91?|)0P-0LEU{PWlJBwING zfm87N>y>O>%ukwZeGXB|B3rvZ)q`y1li-rA9iVZtv?lAXYW1?nR!Ph*;gR-xPqMYU zB&W#Mmp`+a`;wY*|0YxZZoB|c?Uv!r=Tq|c93ExK_J00(H7j^ph4eudp)+cjbQhQp z-e?!!ZIm{L{bT#`x9E)EX#Hn+XSP+&@P1ryIB_gyc;|%{NXkc&GSW?1OmfvyQkvwU zDYfy9?Gxm53B%=1=->i$51mPsu6dF(E$E-?-s%zF7?CD_^M*42K**%n8Vi>$MRAEO zp{E@k%_4vEssg{~S%2v&e{*x8zw{G-b5pV3^OE1w#;2Y9rJs^M#NXUf>@RKhmvWnL zbGP6gr70za-I7v_(v*~9ly*x}N(%qm`O|d^JlKS5gYEuOTH3^}Vv1`8pF9sp>Z{81 zNUy)&8N~MGi#*>G3`Y_Z zQ(}nv!))$k-SLQ+|F1WsK zQB>_<_&eqOL*6qt@YX&$1%L8JKdv@%2e9i;ccH7a`>_(~4SV-3Z_k|Je=H4q`|R^M z!2$NQiQP5Cdh=U-83R;)qBRV)J!;`cort0{1;6b5Z}xq67k<*C3kPL&A*KHi9qIQO z|DS|sVBq3!emn0whClB+hW`Z?=XVYN^(+(CMQZpj<2`fuE8X)aT0-6D;qTRaog=zE?jMc(gh2!aJb> zua0vDi5X~4;Dxt?15YgrM)_a9YSs<`&vU`(@fD(Oj~!POEJauQQ?wx7poaiB$L9w; z&4K3C1%cA1Ay9?T&f;KcTgdYYiBQGnkfsj_Mk!O;7T~i#N-fdj^Mj?&`J>+8@KwI( zbH2K_`upqN?xM4^&Qn7mzQ=ybb=gx3g}zOQ?bR<(`dZM_9)JSVa-eh-4HVi|i-UmZ z>EP2&fzqc)hkQ2|2TE7_OBu8#4g}k;Tmb|$dupMdrv1_WK5hbCIb>XHa6cW*XftD3 zS-$ql!bFJ-Ve);}9uu5F{VKhW49<5)QHE#0^D>H+U2*Gx$aQ~Eid?2b}(+9f@D<5ayhcE|_xA)5sA1_TG(f_Y5L+n_WC1X8! zgUVRy8~$&H^detvdl@2l)qm|W#QRN2eX`mEnJd<1?rji0)GBBoFlX&tL@L=l-%2Il ztj%0pnfx&s53Hnio-fB6QU4`!F?$~7ER%{tvCE1vZz&0tf9(AwtBAOebf9p;&-H~&3n_EvTPjocPN6r%U4IM)+BaKuWsZqsquz= zbQ7>OIQ)YN&XI?4A;3HPDEUzD;X~=#bb%QQ%J$l^=Eq`-Q2tc-v-n$7pZ+TSiOpSO z8h1vBQCxAcf;cNV!)=>1&cmowfEZ%`*rC~3ewJWgK1WMNmtotQlO_Wul z6W%)uG_iKe0IU2-@2wW5+nej#!sDoB*4>F*9CQ|QA>HSpZE>$e``(4kz8xl2hz)NZ zuq673_|nD62J%ITY+zld9vuE0fpU4J3OblQPU^uRtb74$WASgYPC5(To+Xm(pkWBL zdyG(t{%(GP;;*xS{`y=R+iI@p$C|ZfhleJYv*q{TXKdf(mKvkJeFO(r{i8Q@J#(IT zspY2NHaI(uFS3oD(3**}DSd-$Z!t&Id(Nc|Nktn>X#rlLP&gstCJlweTUg_v05AVR z#m1JZCeI9X1s+w@q11x$eQY*~9VJ94C?Y+ZMxMR(suHT}@`R6}x>|B_pJgp8WFb#2 zRj?U+RRnP^6B_=7cVW{GRc&KD;p>8Z=Pp7A5gw7a8UrQ?tdW@Q??xe1RZByG=s!(8 zZDEldh{8hN7InpBx5b*DBG`GtGcsEXPqZx&<$#ug!=FpFbU16VJ=hmdXp!f`r)iW7 z%&n#Ar8Hbm!ziK9Zj3M73wYzr{DBcl=7Zz^!%G%Wq3APaqZ|rG0eV|J`-#Xx$%?7}MSP6qm-PqJ)$IbY909?k16 zN%vwXC41Vzr_tTDJlfiK){UiWOP?2d0A)IOM_=8O?Y_FN2h4s5ce zmC+CU5>2F3kzhZ8_gtAFP9BpLHB8(C znPpGOhT-givIK`jX0nP8pdNGaA{EI`kgDd zRI@RYndWG=Te*&Ng?rdZ^hKyBn0NL%SDdu3@TmK=Sz{hZ@zq=*q90x9T z=LVy#S00k6@hzdl@x(0Y}63Z6wX>FBf?Q!g%Ds$Q}Eb}2HmUo3;Zpb`APM^Xfe)|5p1D0Oyd1bJ? zwdS9`&2NMFEys$S>8o1}Zhkpn_88woeZ-{Zs6cm9WlqPe7ZZmBq+63%&E9%pk+ROh zQ(0rU6YJ(%ZXmku$|>=f!2Xb@vO#umV@DsLH2bSdRSUE5GS$K~Su2>!S71%UDE!q9 zLWF|q?pem5Fn*Q1$`DX>-&th{dz)u0Abakvb^vCdQh|+(XR~wU@`@ZTZLESymOmGM zgPylq_aSQXrE9*l2?3$zybc9Aw7Wc?f$CJl?`S-4Vk?j%Ur%`cr*1; zC25ccQpR@J7}OO4XRrFGAp%kRA!&WoSC`nvfAfmvqPf!e?;g!;Jarz7=-@Kzsd^zu z^$oqB58p$z#!sE@`S3z{FSGBd^Wj=)J9W4l-(5iXf#<_jvuPq#MX%?>`;$fFgM%0s z7%2hP%eJ?_{e?bYZZD3?FI}A} zR7q_ErQr-K`w=L-vt%pD^ggsVWn3TFtR;-cJvVrTMP+Kx8Jf%algT|Y^&yjm$;4)3 zfsX42J%?|8KUejSrh(cy`!7j;8tHF-KhJ);R6mWc5JcXUQhvWE(wrk}?dM7M^Bi4| z{);!2;@Z?l6ph%mGs&0>Q>*X~Z^bD``(uR=Dzgvy@c=(X`gFq#D3^`gu@?oV(Wr$d zJq_`X5cAWof>v$`=|Q@LX(6AW$+KC^Hfz5m_9pC)eyYwrM1UL3ow)o&QIGK=kV9dh zdtJ@mzPjs%b@jROV+?YKtrXyGkI$r~#H+3=;N8FdIUg5aE{}6kHza1=;e4F?Qc0NBvft0VSDcU+U zzQiL(74_hV)jwwK&qn z9VDWnDvw(u5}ndbZA(A4r!#b2Mot;oK8IYYk{ z+qci{TM2I*NRHGWb%zOb;!M&7N#p|;(0m>>HM&gp^M2PozjfGO*$|mVVr7GKw*IV` zE?HVNP^eqg6jJ1|HFA#oG@~_gzCH%(zF|L(fpuaG{ARj>_F521|45u#VpW`GJ2GUg zo-!)%M}2I}bl{Nf&~!InU31!;+x$fI!4{VhTr@k1dg$(KpT0ur9Iu z55z+39U}SE>gO&vKjNjXAcqY<#-RakCC09I`SlPqG?E;X4lUK8ojsdRlSjC7+bsdifV3hNJX+Y^~X+w19NNm zBo*1K(2>qT7y3?a(DP+tq}*4GOe1SB%4ySExs}m_InO#bKOZ4ioE`8bP}f=*C|~cb z-W za`>$o_6UyXvG+VSvaqVa!%LT z&+q9#&p@J`3Qm5GjgO@WA_n}M1N&|MY@GM< z!AH&TUcPFEx2Ac9_vI$hF0Ih@ykeyEhUg&t0KQYGbvYIGbz!lsx`fxyypJ~pJ)8Xd zN%*A@9`>DK6#w`%idYjrO}^49T-=s z&+&E7*sm=V{R(d%3Pj8W;&KZ_%mw0d1;QIa-s(W)0iDh)X` zI~L;Zx-gzgcBWtv9bgI?_>t=+E98OUUwk&hzvnx$#-9`%T0rtdXV7wt$cz2aliP}U zD3XUFF)Nn`2v#*TPt22Q+W0fL%_6Hb5TC@K?>&b#o!@f~?Z zwg`Ub63@!w_2p!>xY`A-dY2FC&o#H`wP+VGcL#kDc+>|Ik-7R4M{Bo2(An<{uHfKT zr2O4#-}0bn@eg;TX|y~n$lz|MBhTHf!{1uDTcG3l1@UG zzdO<{HPp@NM5`nwnemII7pXp7WBb(kJ^CcB6}Iiu<66OguumPfPYO|HpG@z4+C%!3 z$Dc=KQxWj2%bE*Wg|aMXvepxCe`}WgnCrybdc%?>s#MKc*umf_?>9q-3I18C7_(GS z=p;hGF4;sd{t9J+(M}!nFe^4B>$rrwN}b$MBCu$PHSO<;|L%+K?(Md=Z0PrG?HGFY zovm$(m(m)<7jv7Jn`9CNCF8;dwn;pU3SHLbcb56d@>n|~YoBttZEvM zT{>HLbx5J;U5n%=7HQKTr(F}>K=L-Me5-z=IuW1cyDODSc}*2j>32Lqj~hq}mVX)E z15$a&nIi3LlKJDCq`RME_becnT)}AW%nP(u=E@;Ho{KAU1sn81*C`RsAo{f0z1js6 znd+)D1I-)q5XS80932(vZ-4X^^D^R(oPh|l*keaoX?HbAGoy76P*aw`j&qrSCp-MJ znSKQ#waIUwcw#~bC3sdjCH+gK6n@9ffb=hZljmgc^;bmxYt7BC0LoyrCO_YTD(%8S zHpx$vm{ze!r_>O0jQmJ!0u3LasoKHTh>rx}GVq#Ml3;%(_JY6is*+pE!Q1Bx)?oFc+1Q`H$AGa4%%i^f1Ef@fnY^V_s0mim8BUulER4zf#{ptAJb1x zV@!i@^fIP?0NrLxSIDL*h!O{3oIQ(Jel(jg-pCLEP0v^;RqL;7+Tt%??Y-$6uK$aJ zj)P2`jgL=lob->VvTh_vv86+D!Ip%U3{*o6qW^FrB~EdesYXtRXL-5ZTuG zos$baEJWc#o3`LAAR^0-reXS#$?}MEFMei3mrc_*=K}e0rc&o!vWPh$89GH*h+ojc zE?YCQTGr9BDI_TZ$n{qmmMnq;vaViCmom4Yr*%KGye5-Ng^||Eb;SZQi)vkRb*)RT zu64=PB?k%?8MHmYhpQ|;40G|}V2cl^s!#(2qj7Tuo6F=X>)b(?ykh(46|zWbCM3>q zp%EgxJF84U%EpR)sKUEk_pJEsJ3an4fI!>?{EV71*WELxf+HobX-6BWj@fb7U4WvB zBY|$57i#%b&Ed0ZSICYir>2_mv{f$?ATz7JQmTH&j-jnuw{$G&gQ=PsdgzZd8$CI> zxsz(#7M6Izr`E2RK?|Pnr0us*C@oyvTMGx;7H;0Q1(sA3NK_$6BJTI}u7XphSQHt_ zPjakc_lPK5CMCq*JvkR5K6` zw5W=}6C8byj+Imvax1$hU73jQ<63izecFeDGgv_rqQPQ9z_#{rCqm{P;7sd%qUvt- z&EJqyAxDa;cR*ZvaxrDABHfQJ;n(l2YzxNjE?5jGEfL6u?1vw)llDLgkTun|S9_%a z0ViEtIFf2~8y<4bOZJM^E}$;Zte|#<>O1fRM!w33Mj5P`no7N-e-8In!oe0Tkk$Q3 zZmaipq%bhqePRoOBn4x*ZSHpeNxH9aW3N%WLKHH{pyC>YLql)&E2ghCc)Y525nJyMD)VZoQP4H+?7-s3)ns;{Yp3bXt|*AgBt5 z7Ba&VAMXl?h%i{eUUX?`NfU6itq);V$f7{WD zVheS7yYU&^Bj#40>nhP_xlhzr%>`JH((<9=>)l(`ObT+4(?B1r z^=PbmCV2N8>C;GGpnZd(iV=n*ze;qe;KTiB0h%ru94$|FI}}z2;N%^oy|Wyk_sMW< zZNy1Q&xuYLp{SCye=zR2xGBYgpW28*t%);Dxg#q_sbca6!?XI3akXQP$RWsIcT1>z zOZbzxtROizC%*1hbNNSoxg+bXzm-IBVw2{I-B=&4@zx7RF|VA-L>Kd9?S8M}RVVsS zNL;~au|GNmoz@(5D>Fp_I0rm-CrT$RHHmg^AD)n!+X||b`OHRvR?qzz3ONq%0*6-C|?)oTwsXYvg2- z1rml^GdR<9@)Z5eVSk{gXq!Y-tOhcFEbr={(FF}6J5PXg@@A4yTZRD%estxe#QH6_ zf{{7n3|ft;vKGoOidsO?edl*+a0AhQ$jT3S2vMwZIzMjbWk^6?6&A1=y_9HYXu0na zl9<>hOMek?j@_hR5jKM5oi(E|bK8Wu8^%#i=bURIiFl1`oMz3)|`myolk z+fS$BhiOsUcSpT+52ewnLPn1wxawZ8icS(t5blEjR3<-BITDGO(#Xu;g+FZF6XE8vj6&23Z3MD||!a4S_ky~-KJW80vh4Xoc zRxgSztk;J-$=1MA;zF*OyR6Dv|2$?u@z?odzC10SVv6a1~S#+a%+vT=J4a zk1&cL!7j(A4I@*-kdNw7yf)ekU-Z6_XSRxyMtYZ5H8mVthEyI4-5EW$JUVF%gH*w& z2>SPiNIMH;vAbkAK_nS&aR+mR1c*kXoz!=zI=TDVnmv7W^Ei&%6o?&@aT@n5izD$m z)1-4l@trbH2l?=PPs^~LS(KV1ZQ3r}46((u5X4*}7 zKtUzP%t?Axw6hA9D~nPxU_Wwge#`hmPdqBYbDXgaxoQt_LkNoP^NNxW zlhmu2yyX-M+-?gbH)RQ&_luR^A}xB9U~|T%EAiuWo3bl54@(t0Sy(4fN#Z@+DviXq==shrDaJE6GM}f=3uJU z2sv&B9g>es^?E@WOC=s1A|vxA(&iRnCOD8Vw?9HDc8>&Fc(-O;dW_#sjFk4-)*YgW zmgpt+BvJqLw$A=ktxd@aF(1*DWVhZ1gv739^sks}+bavsQ>2Uhe8XK>6zt8{1*e1G z&+z}NANc=+?d1O>>A5ETznTM-{vULAk0?VYLWvxCBZ%6syK}6}+x5_Ds&$$-I~Z4)mqFC;Udv zd+mip*u?IR9s%Y>KmJCffvej$b2i;Y1Z5mfmt;&&;( zkj$!{j&98fC$p>vP?2iY%%>+YH^P?h5tttx6Ta-?%PyaN8N|A!BL8SaSz)r0oo*NM zNJ^2L5>)pe-Dcm1^jd9Ga@Q`*Td&n_Ufk2{e<; z82vBugsWK1izP}dS|S*Ux^-DtENyETu!SW&;vT%cVc|%gV%5(@7cQalSoJ@n3-6aU zF(gb(F+0$c3{T+qpU4*(Ms7FKU_t8^k$5<{&A1z`A=_K@__>$pxW1$9ZA$@eo_Ky2 z;Is&gK!Bo0O{8*nU8}%3S}dZFIRaL+>bYp>pD>XQ*YFjRl??TVowKu0#>><|7sR1g z1wtp|P|FZBFnTh_upymajYp_+EaQ349rPm`F*~pP4`bvLCuCvdG+uQmv7GA_BLz`h zjJ!YqZ`I>8Nl9X)^Ox;lqcM$kWZ7$ype=JFhy7a^wz1j`}a1J-dZ_WRg3=Z;~Vo zlBL51&cb9T@J2xB(|*VS3XaN&)`h-uQFD`&cVmUPA7usFj~D4j8=tfo5vWD)l>oB_ z0adVD)z5|dSWHPvF*WL~q{3H5%KC8f9;j55EVHWx@zkm-oE1{a9RF7`br0m;+x#`c z>QYu@ri$Z$_!A6Bh8%d~&q=#qacb;~U&u@pygyE5&tLK5cd8fJP=3R2Azz)%_khi} z(&oE7E1yU5osnuoCCUAzjpMS@c9gUuw7wxPQo|F{+m*8F%^NX371>XZWq(>FpS~t5 z^ONnSVL%8$WdCiFKMj&xNwQ#C^)oVjtkpp;LL3s()(yWvKAFoOnQ-}Z#Y=o@mrv*0 zR4KSuvfvB<Q(vutP!|y3tOL2SjOP zz-`D-rwJUgTgc8c!p-yEt>ih8Jn0>^J-{&o$gy42+&ED`Dqq+JHP`7!i<%MY7dfW+ z2El|C!jHrm&cR3W>K?+lfofC6AJ8QlP8xss?OU4`bRD;-wG)e3@ilq!W6icK%Y!2> z)dDVKR4NufSY*w0pb5yF5NJe9Gq)Te`v>zB>1hd1?dhCR2fP3%&XA8}6`ZzrP7ZEe zZ#DMJ`Hm|9?q8oN01B#P3V?y@jf1cICHf>UjErz>U^ID8iZEY>F+D z#m(blZn5{NxomT@r{-7@2$2!k^xG||9+`9A66I2Wgq1;_6e`AVJHLzg?cjGYznvia z5;N>C8o|DMAf!UZ#l56%_Chxl|Cx5W)hw%3Tqx1zTTJociVI2urJkg2NvgJyPOKf0 zVuAu-(Llr<>(YnjMHbQuQHW^0xU-}$@fp0B*M63s^9v;5u2P-zmq@qzJDKmKz3cpq zg*v0enIo#~S4?WXvPL~0VN#_AcTz()WKODdICE0xiOZejq+Y{|8M1^C#|lU%mD8K8 z%qv<3d1q?u^r|36Z*+PsQ{&C$RCAvTUmQ^&)5qD>cbm9)hILoD zAeOoRFrD#tNzb&u-e&#Y@tHu{%le&;pBQoEYd3H`*Y9n(;z>>Q&El8mpnG+yQ|lKq z_5TCw_uN1AzJAaADq}W3zm73-^x#)$`LsiIHvfz3ch8&Bv#IO%4ZWgbb|wA$tlu>f zEhXB^`gL%(lAhE7J=X6N__s+->SMf^>brGPGuQ7aSnmH{T)@{rioSILUx*=iY617T zRz@(jfG<5*r~JRTfJ-9jDc1%3!3bTzLrHi2@Aw-F&bdF3bHE{Y_#gY%m=n+5>y;-q zW3`@f9|`j?{PNGq_s3@9<-KTK-)wt$Oq^q0?{Z=W4Y@=xBsTad)GtwaJm<)p{T_^1 zFr&X>ZC~qu)gS#7W!QwDh~w?+R`$)17Bb|4$v^CV&)wrdsLTsW+urY)PZp8pqLx_+ zEqCqt?JX5Ax9jtDPcSuJP2O~T7royxoS-qTKg#>sWB_WD<&*sG|KI(N&0qHYzi_|f zckc^X`A_+yJU_}_z_Xs~_k|M8)(vc`)^PUfDB>|*?$l=LRr3O?Zaeoo#D4|nwnJ|E z2n2PennwpRljyk3$LLq(clIOvif8Ne{X4y{F-nu9~JxrSJQ9BA|sedHCnb0)a>;V}@8r`11{FMX{B2= zXGlAuqn-{=Fkd5KrfYNQh2p#1_qXG5K4bw$DZ@LlSLz#BasQv}zo7R)V{cnM8FWV7 z{bK_CVGj_f``S~3%O>v5F~EWDXT;6&O^X=?;294*O*KOm9=(QEVNVBiT_?L8fAr{W zMuKe@-t{YJgBojgPF61m1tX;Hc(U#xJ?l0XQ=9CoX5C14ckIz!>yN=*-@({X1dgV& zvvul>5LUn)kEALmTlN%{NtGTAp;p~Do0yyJAZTDIH^E;eySYvhqa7(EhFZRJFl_O+ zpY_clsEjjqNEvSnc#{Je4zAXl2u0-9>eKv^v`veD7%TnQVD9I8dd75r1@VTMTl`dy zJ4CMC(eIoEe4ue3=rd+~LkA2aBub=H`o zmq~h9x3NMlB_>VG)V~7@&Gp3ZYMR+_I0(wJU2^h%d?bQ`nuCOwnEC%B?hC`Gin#!$ z18r=++ogw>*V}V!{ZfBJWc>6}r1mI=_>HDBO?1&FwvSRUNchtMYeu) zVO#!++e_h-kK)TPHDBrbh0_7{F;p)w%~N5T8T^NsSG|NV`*cHJvTl4={?2$$tE_bS z9sF1Lx!c~_r+T)h@yzm0xJae8K4FgP^Mbs1mkU+2(PfU5k50JTT-*maBKg7cZ^&x} z4W#25?rdH`YZ0ybisiDiOo9e_8Nhg(gRhY($QxxRzDnf1mN*Kd$5b2Pu&?-qSBk z`ag30u@y1tL9mJ;l_4%n59^Ki_YFqNgpU24b;Yp$S-jdLxAgb%V#Y0ESY-%LgJPF9 z)y{ouAntcc*VfMM?CZU?87n$iX+ypaT9K;`$&eS7r*E(Qe(Cl{+x8F6Z2v%Mf7P$t z_Fws(+x`wer2WY$_K?>#guYq-m9Nnn+(j5sqcjYj^y)9*y&nO=;tyRS1FqzblnGjI zE&Y4L=aXk{5zu_koO|{&OJb^T1i|)qQyjw z>+uB>@k9Ls9B>p(WQyO=MC)vxAkpe<-t-}h^kfHm0?o|y53rj+b%V@*hX43Jl6a}W zLZKJYdzhKvZl-=oJPo<=?{3@9ef&4ji)U-2ul+I2WZdTdJ1Qf<$2` z%q`>;SONmeyR-STo8OuM-qxn8M<)i*mUDv&Dt z&ayIoeBFbG)oB8!?>mV807DY_EXPPG?IjwyyQYwe0qr#T7>kG&gFK?cnrR;~u2Pk2 zLse5Oay4nt=UC)AeRu(dTrJrbgY~~h;1W8&!|lk!5+<Km zumUN7-9ZojyMU!3xsA5sugZ|c&VrnB`CBw*HDa~AK}3=h7s>z-9i(ijCoV>art^rb zbIJL<>bUDcC`J-a-qCL}6<9XmA4W`Yy{9&LhgYd>SkO7-76t@9Ys~5t9N76?k8hdq9kWE5*)qTe=%VFLJGo4x)vm@f|(1nM^9 zF=lhUhl>K7<2*WPkM#o4vx>wo9Pi94zU{C<pbWEi9D}7wf!JPp8i>==Z zG@vv9$`1rwVfh!N-P}BjKR@G-0_u$F9&SB)jqzUH#& zUvht_0L@fNV#Td#8RFJuQ2`8bYlHa%lnKCY!z)ZmF1VQ&a}a<5xvI zq~JG!fK8ig4kzGAkA<>Qq^aS^bUN!JdlR_o_9e@Y-esB+;cNlto(3IZ;n=TUr5$^9;U9y*wPLZ;z_NqvVlA#Q(KzOaN+^fwr4q-X`XLG8Q2lVU z`ZcmN629U5!?FlZKCeo6goLrbsu#!iJxp|}wiCW#&|cOMLP@yT=hSkZ0@-Jas1Y3G zs4TP?Yt;6JswMbwj)h)}R{b#=`m-LkIb)A_leV0KH;|{;B=lqcvB;6$tt^q@{TeEe z^n0!c;QL)F+ECfUS%>}R&Mhqee3LsXKmWVr=R^HXG?3 zikN-nc4VxekRQ3m#;K?`vYO#Xe$eRss&_S zDOXX<+2m*R$>goSj|A#p%B4lO{_s4hcz>z*@KnXS1Y&o|wG;MY;o|tt?44(5eIup5 zoyaC$qi)3P^4h9NR#`fnHs5t$>gM)M_@|Nw=8{C0R4Vf4wq{izE^1I`e(XHX}d&tyd zytIF%%0_Je=1b-DFGQO3uUx*x3TCDo3kPV(8#zmQLdK7-bQ|+VFhM}v`dbrKxUkk^ z*Vvsnf*v2HH#Y4^oP&lWjwubo^c#E{obk!er^N4q(F9c5TYnQZq0)h`BJ{oFV?oj2 zeKc4F4JY`nUSBc~GrV>B$bFAmikLnlvWUzd4gyq>Du>JDIR)puBJ-JT2WUl`8Gi3& zO*6c+y7H~To>Oq&P$4pj3jx(a^0E050Jv?#X}ZC@2R*YVtx&e97^ar? zD*NI}1UV-f36L6fup(h@7Eb2p@B9bjRuu7Lwn8{?I14SVTI}!6*H93$>NCV3^Zt4! z=O2t>hCp=S1y~Y@Sr!%?V&D9+*3}q`>yWd_H9HB)Bu~!;2;SV2 zb-#U6YYMLR**86dPg$1e!My7bC}okN>n3JxP-2cXOmT{(xE-$eV4ltSsCC@FsjW$l zBpv`b*qWThw>0wWeKS3asx9|~k5tu}sIu@*91V3LH~;DxAy0V~`^y5xRTEki`rc~# zKcxD8)ohKrj7-Td@fCl`Jf6zbe<$PH?b%OgU6HPu%X{-@*hH!@>t6jCet4ktwgbPe z8H{=C%ez8bfBA;h25hV9ZInXJDBpgx%uHb~gJ`h9JVZk#h*Bm!A{qLT-t_}t7t>Nc z6}b9=59Ph9LElsQfesSWoBQ}HeEy;Ofjj~E+w}u)|46#A#e8s?62?sZz^MY&^bDxP zf8aPt3<)e0T1Fu=8I0CR-}XE&Lgm@|K8y%soW6*_BMG-T2k_Y6)`%IR(}M0_)x9^XGT`Nl#WvepdhLCWvGrc!Rt7HFLzL zy+a)JyG{~Q)&?u8Ef7%+F%5f-7?wtItwjURR(C*#ni{G*W7W;k>P;H-{4+kaA*wxo zKo%99%Bxb*U&vA*@cC0T+LMa*@T#7qqF)rcHmeOHr!8QOEoPkpL#P9p0g*9;i}9+? zhN{L`)uveJvuM?$(a__7*#v(WCNiN@uy7xh)1KuA)q8_p{V`LR$E*-q*PSC!*%276 z1ZllleF+12){N`{wC6Pv{5jUgADaL-5QVu8Ncu1Nx4bzMkIj?W zHJrfzD++n$zuXHvffYTu*dGfNb1Ae$_0=BPDJUq;*aKL5W+q@+eCwq>PMUV|w?c=d z`R7r4(~MU07=1OPq({!!HwA}>^PIJx>qVLW`SYU2-Xj^G$eEF!D6r?R-{Jofw=;=X<@91NaYW%>^O758DT-H4 zmgEbf0-{5=8KqkWBUv18&RFYBZ8qvy!x`BNGH z==yr7!@0wV=N^8&<+yy03-P_;G@-dpC|7n_C3YVCX<+EM)3iB z{-OMA6k5`OlWB+eyK3cV-np*915 zt^I+jAC5zrayeuQQtM2?*ns2pu}?DX%0KjceXyKz*R8h4>))6XDowl}u!ac3{ML_& zd$i6;zwJlz5GsE9zR9j7ljvS+&^h32+Q6RR8BQ>4^>UT)AGOr|fm`>k+th6aQJWyh zVCm3<$dtXVkE7(gHq-v*#eFjPHFF)?w{~8Bjv~@l+^V34auEq@ZMw=S+SK@47x&3A zKd+XE^5J;P_|owWRu?Z5Y>Yq!JiAE1tGRz49(GWq?r=+w92_dg<2ir+_nt0l|{ zuJ(O*n3Q?=r_z7^^wR$n{YJO+@A|L(&EB$pztw;3)nrMnU!{*4O$m(J6u|w6BT;9F zd0)2?U!y~QQzQ&}s_{E}H>vbI>LOA=*WdC6179rC@&K5euW_KTI=o?==sW460VnxR zF$?Ziv6>@y;z?wjS8IRW^9H#yBaAEREYabLEafKuST7JlcHe1F|4nDxT;Z)DijBW5rfDUmn1xJPze4RPhpJhTJXeO=?ln4j)C zvW3UkAzy9_>a934waf!@p=@&e6^ud~MsF2z(Xf6;8Z$q=klRi~CdTiHVEl8PB05EK z`6MoND28|T3cp;jDIhHvJrDDoiYcMk1=EPl^Pz|W#CnM7t~N=}@Yr61D)Y*GfB6_d z`ZW9zhS4JdUUFB}cZYYCmL3IA`8UrWY4b+>(s7NaQf4+@&?hJU_dK*Gqc4~o-ZS9L znat!Ix?4Z|&a5%NK=m1jRSugHigsc)EB&1i9b}*xq38wEwp0B&1`C}k$E`;jhw8cA zQmCWpx+;hi$y+j(2lsgL0hM>X;uCc4nMNy#9h3Tw*j;nTZvBRC) ztv5pPQW|b_VZaotg6rj|2xbG3cnkt@hY{5XN!zCs)hWVx zYH(Q@v8B{RUjxd-&cZZ-evA$;;V?|9bmn3nGwNmzmd#@v2E;#?DR69aF<%*N>GnD1 z8_A>Y*<-#E9vdZJ%va3gQv1kK(aVgaf^X_S&d+j-1sHf|k>s(rZpE-nY|2;}9i*BU zV(P%7y-r_IiXhm5h1u@EtmlKMGfjHzScj)v71Ou8`EO|DUHaxskso~B+7|QW$H{wh zFtjCVotL(1)pCN!is<6ItOzRA+M@REMyskIM8*fq3awa8h|N87Aw3pyRCNoq_Jxwc z?5V1L+k|UKj+R29P~Vz#$>^JPYLZfNOqRry0%VGP%dt2x&A#Q^w+rlBp?#al8GEqrN(^SH@eO{xPA=`w;nXtmJ{Y+~0RlG+-Jz}CA$%(j5HS=-P^Er%XIb;YEC#|oReK%lgbi}c8t*%C(JceEk%%-dMP zwB``+DJ;9eS}OzI`lC~zdH4e&?V7jMju@^ z5g7m`v0f!Nx22^j4w|JJyh(|m#l)StOTP>jK4ZZ)_KY%tk3ew5p3wru91`X&!=6#( zp??jbas<6Uvx+coYnM(cg4_NDqLsrSPpUP9tjG*XQehw(9LDZchgcGdd6of|=MtXF z+~<)z%MeR?8P77t@?0UV8RxGNt}7yd8j1_@_^08qOH4_9esSTCWSnJJlwX~0q0prL(4 zfBdA%5M5qX0-TLL!JT*OQDgEH^pWF$zEdzT>0;37k~vFKm9dF6TE}g+j)<*8XoRg} z5Ots^3{MoX&>I={g@V%5QsMW+cjLvJ!52`dNxt3(pe;VW{8k{YYZ>Vd2~Y`onR)NX z$Y>{ULlkj@?nIho`n>g`mk?S{2eB!Jm#jB4uMmM_z1l;_TvvPsJslZ<6N2MCw-i*j-?YGRUue!eIEQ)A&wCH8n z{7|M~C|^lKSlgmVJVlD^NMigo0R?whI*EJF1gEm>iDhlSHjA~5J@L1!?V_D6DDBjS z%P*-d{vglh!IOaKPyA7cMzsxMxLj9q^-}G0<0L=s{TXA){#jyG?BucsY5q*f_W}5! zyx}Ln--9>Yxf2=oH3bOm0g&K$zd7zva>`?-ur~0h{=7EV)8|NBJR@iiTSu5 zjz~s-Y+^Bf%)Irx=*vmCmA;-m$2EmPC(vEGnZRf>7KEHDJrl~uJP(hoYt0XIuMI^% z4U|6{-VlEoz}2$`uipB#yoI7K1h%{o@H|ait8W73pN99;@4n5eJ%F=nA_E%vIr-wh z;ZLCMt#54c))so)o!HaLmqc^I!~OA2W$8DtWd36;eH>2iiTL?dcqf8bg<=QtSo2b1 zAJL}-o!rl@j*g$`zIef9*@L~a(B;Rp!rlm9O z81f1+zLr%Sg6`B}CK`%{H+wf=s^?m3GrJUF7pw#!)fkJ(&+br39 zQKo#LIhq=P;^(@XzR{{++sp-WOV$Gr@s4OV(Uz@55w%z{V?i>bhm7b?*3}f@&>L2cM|f^z z968Jwov(i--jP8~Fm~p~%apCCKO!w$1G| zVf6{Q5a7&&f~*sV#2}zx+guN7v9-kZXIj1=8-uPS*H`{2Qn zEVa$HA^k{H#H8^XYlwSB#N+5h$+k#vCOG;Sv4S6U)c{cW>IlUodOI;4Vb^(i>+Az$ z2Yi_EGFIvVL(uxK1Kk&Go4MZ2uZn99c9xjR3As7y-dDPzPV0~!MK1QsFYF(EhnVz4 z{Y4CTaWMLTN)mG2+ZELDz9Iv~Hl=~aQFt);bYroYfhwI$oUP}p$!)M)6qhjwFZDUc zc31AVi4f+?fd%i5RajZ%KE*HiQ27+vN-?6Ve5x=Qz2qtE?TIE0=>>{n1Lxk_&}w{Gl(IPJ8VWJj)+|pmODkh`79Zb_$}X-mxwa)cnex^=q;fa z-pid3s~ph|mW}{HuN%=N=qS!Ixz1fPU0aIMP4h6@>I|*H4kFYB7&buMs4YZ3x(T!UAn!FDkL8WZ=(IPqgYE^r{{7TC8*lW!$kY63b%xQwUynni4>4@RnscEl-$VG|qn*NWR@E+-OFreM+l@B+_vkuqX0fG^Vx9B-^!QBSd>amL1)Zfa9 zT~Pb&$ya`&0HZsY!EkeeiH&R&tg{ZtK)JJuP&{VEa}CsO^muQOopK=BHNnX}CK#pc z=&Hru`Vl0OGGd|=EGzU!cO-R*w|tJ-GrAm+^ z2)d9#N;fH8LATY&!f(UTy~w)?|6ODVMn51PG;lAF+@nLldZQ#e7eR?nL+e{k9j}C+ z4aPXaeMJmFVY!%j$a)_gZVV6Ai&!o~3y;7Hg;~)gJGiPQ4*YDp>JvMt%SD_%;tdFWj2XD=}ntvP{LXbEetg&v5giY3HY}`{tBrDLm+c!B?!t< zHtIb&^VnI)M(WC^c{7i85dz*1_0Iq6HkRoX)EOl~B7pm2I|gId7fy64FQrZZa4MTk zn)xgJ43)pI~{Uu$o^U0Lyd@gb4b16M+LK|;<$%$P;rHpmB5u5`?XV6m$&&^uJ2dYuI zqNw(2f#s9%yTI}?uzV7Jm7dWdMi>{Y%%>G$#$jvRzZQz4>f6PmfE^ zCmS+5pKQpIkr#K&wez_vnh~qNPRqaP`$8Q@Lkn@x8?J_Qy&x~vp6IuzCfL%-;qa}uZ)0u!Lx)zUiX6MJ$)=GeSkJ3gEQ(K2)VR6LV@05Zsm(P6reK~NP0iQ`JlC* z7~cu&u;afEMYfGkyD4C&qE~yay@5j~Aj_Vs|0fLZDlV1lZC)W&S(k;4heFP%Qe|4} z&DG~fUpAPxXvkbQJ`>{fdFFP`zd!tjmddEWJ^y|~-b?Iz>ioNn#58`y2S|HzTpl}v zZA4!|GwW74d8Ca7TCZ?rlo=%;XKu%OIsYb$VA39RgDJ^8|2|Hj`sH+mGIjoaxFpUc zQ3|~W>ov8kEm<$U#Df*b)Zb#ywR!4x&Uc>)^*rCbn3^EDvcmQDTd($9$C4>^zWc{o zEs?V4Di>J)>Eq8j{$!$8WqSYiL-)J??EWTufVbW6K13G5@JEq(ATISn*z9;kx!(&F zmuRqZq1gNa%=?z_Z9TrH_nUPaOQ7IwV!%`b`&5#3K5~2~Hp|EXs6FTDHsPoFExCOU z42Xw%6y>p_LNS~qL^t6RAW;6K_of@kG{G4&6qauO2WUf{jY6G+(dlko-ui`PN!2kO zCOWsxL%4^!H6p57#ld2QMP?)=P){z*AiPwUR;AH)M?TkFO0pe}>4l-#Q2{;HCk(xj_R3e!z9tyECV!$c=0JED zS31nk*$m5w1U#>&2Iak)>wPV`{gc(OF6GomiU>Nv3OF3 ze@!*RTp1E4zamGfxWR9c6+bG9>12*Z`G(US^%W#=kI|V#q&In?_LUTix)%1}iU?D9 z6x!YE{o=?6)Lem?vwPQN)Vp8G4r#p^G}RK^8cG}Q*vzI?e(bINqW{OUSH*J+h(RRWu!3!j!fL9`3SmU5ZR0v?n`Muv-b4f_>viCmc{LgcE9y06lt?&AN z@BMwhRQpFj(Qf;@gcIto{cLGJ+L&tp2Dkltfiyw*AM&;RgK@{S|KFO=y@zS_NpE_k zsqyH2#Y;aUUvzy<}*eB%PcJ7n(a}5tu#4omm*PoSsesri8dOwzaK1&gqnHM1{ zz3Aup(yJ@edL{m2r^v(0q%VB*TOmi~KvH77z5Ji9?uR};G^sazoFY9O43IQ^R<9hB3^o=^yT48`SWM~q>yO}zLfvbbcs<4Pg)?Ha9}iIK;lMQ@hkQS zn#;vtd_s+aWZJ&mURKVu;2L91%pvmA%8cJ(cXS<1*Xb9Lc7xGqK7t8PFiE^0B#B#YticnuF;b6LQZ^GoT z04^5R`l|Qrzc*)Q9H?B4lf+b!IU*ub=tyo;%K0tkx0GKf>y%=C^Z700H;>Cd-R~Y2IX}Xm87rXfvl)t!|z02);FL!n!X}M)$RL}?gs<*E{_rK*i zKGj}+u*dA4!>w%hJDJ%%e@M>kSC}3rthgVq@eKz#zw=q?9bV&VbPO5^KzWT%RFk5- z#wT*tgz_4n=(VPB*2Ii*SEJ_2Wa1K^HW}B96>%hupI826p?!-DYa8)zhV_x)`-swB z*^5Lh3;$BoTOswt6VyfUGptFhP~~cVSs@ja%nGTXWL78<+v_?u_-lzt#{4x2%^Kvw zIS3|W;>FCR{6Si=pu;jZ0dZ044f9I2J+6fX_oX+03-D^50)i1)Em0N|x z9VrcxXqLlQHNciN^1F&(2|8M>f{uQzf{q?=f{xbU?Y|X2p)@V$?cAyz@lyff$Vs=a z?ql7KJt`z8mhBK?SvTc#B!Nsad&q9JObk6iyKaeX+25ezmpCTG30|uFndSclofMhV z{yoh_PFnVnIxzi3e)NcPGx$dV{jtAMipZ&@t?7!RSy>h2QFQT^c4xoR!^zZdCS@868#zyUH zUh`1rYo-`2ov>b}y47uVRPfAnj8Ced$~G*D->_%!Rs4NAMJ_Vq;4ASjIrV2`FQ(GC$lRw}e@y8!E`jC+{0clf`K z-Op}(mKnBQQ3t%KejO3Ewp9#`Axk`qzsByHQcJ~Jb<#P&!!sme|^IT5rQr;92i z`(3m)Op92uqKX2MCW#>kz_mIIWo4VXkBr?eMFpy=O45n0v}b>fokj-cdGrq>??`%{ z{3q|xEeI)APTBM!p=0sA5B3Pxzy=)%;9-8JqmWaK(A-#oyi|VJ8&bpVr5`GV-&XDd zOk&mv(D`g1CTIXso7h#-{aC47Vm~;zXTdl1#}ei+-P^Y4dgebUN6L{n4u9WUwonr~ zSD?%}?M&%DF|e??lp0#M*4s5N%Ph8e0$*encY&sJ{MP045M7?5SVHPK`69Mqc*!ob zu^l1>9mO9tz4sMq@;i(43H=u~!R`P2p8fw;qW>dR{}EqNg&LnchCjdb$z!OZ&*@{h zw8t2d`L!K?jb;19&ZI~phf)@a^aeOd`YMvYVains|G;S}?7*of%p82gKM>ng9Qg-{ zm#B5Yo=aBYdM$<;!AbEm8*~pMI^wB#9p}oc`;<2`N zs>f=Tm(isvNtzsQ-Rzbjaq|kN%mC}j(4M8udj@&h9uq~dl=`__3ODf@r&Oj}>SRg@ zeAaFG?suiFEc6LZi7Y;>-@v=jYfGD78}4a+jOQ8g8OVRV24aVKlNiQ|Z}AD>*s5o` zzKC+Fj5AT%itU1Li9L2iPW;j8K(TE1V0|}JT2pE@3x>UFO-%_}S?^Kpo(EKIGm3Ri z4K~9Cmn!pjhCGazmts_}xD*j)=B0V@a@{*6PrHXCa2a~$lw2JppSSrjdd%U06Wf%U zuc)2lz709<+YH|@f{)D+d|WXipBhGBZSaa=v;G7sC;!W8Jq+d*Ow_2=iO-aMBRJ2o zv@6xWsHFz*NT@Whwc-iN#0Xot&o!f-1XbS^R)FFrPhyuMrC!N2C5uu z2vaTPdaq%kHKrGV&-)JS#xEozjc_@)JO#@t z!RRw{it*l5ydu|yTsu|G%*p3kYL_y3TssZU%mE-AA9~K_Rj=i=XR)Ak6Zvx;Z^e*I z04ZWj(FWwN2AtYt%VXSItt}TqWV8Up43V9B1Rf21RIf+I3{p(mi}u417|{murD4q} z$c^o!OsHd&6BGdQE}K}ct(-|c0_*}`*2F?VR`AiNN6AZM^UMbBUk?J%ux=L;1atdv7`;oHJi zzm!tyi=*DD@(@JGqO4&)8coL`H}-Xz;pB`)nNe-IIHkFBnw&|QEikdnB;65i_eJ}b zuL|SK3<@+z<+1`P1acp+5Yze7=!u_57OX31Dn6hwo196WMvt$4pokpHHVO$ylR6qR zLJyf~@De^jGmP2>5aRWw`875T4~HV4n=I8!Lm>tGGM zN+Cs0*6vxFv0hudzHZCnCCZ5;CPvu0ErFA?wap6fW8xhMF>T;Sepm5cepmC8VMJz; zLT#ZZJcZyO=Vb6mWrKYoF;=ze))TB^g^TE%ufEdb+gzDJ3`ej5sKj-LZU8u|Vqavk z=^41waC?@9Jn7kmSo|Pcx3lGH_3#tL)FvmXDL-T(;}RW%w+=uSGh~;Cx~e+gQk1RcI+v<7Qru6*?eIjGu#G7d zSA;roCQ}`aRFr}T3Sn0k->lWiSs3mZt*Q;Ne^2lY|B?oI%a1 z5|<2yXXKES*V_yo>=dudNES4(0~#0@AOv&(J53t;?oU}5m$M%JhrMhKrDA{Ae!2jT z(bvN4dtyIjG9B>8GJ;}X^vZvO8>78e=MZp(aAik3XJ62B7F-F^&e|&pU!IBqx9G68 z{5q-x&D9%`F_$Y)SkhodRtOHa&p825SkmALPC=ylAwo)Rz0b4R0~9zu=%6R!G?fdF zgQb?gDF_Kaixmf?_gdXvpu>6fEndZTaqBF(a&Q+|hc@NWRpcoEOaA<)@-hfg5MsIO@?Y68umr7oop;^#YRRBEqmlGK`saF zXT(3dk!Qk;)^pq)6h%@K*jaSZA;rn%{q-9RNc4LV0drizzw>!J2RQZFx$~XQWw@Oy zmd=T6U%OU8(#yN2>||*B_SJ!pi$9;L|O2Y7C`?lG^7o z78ih3+=e^IgZAUEyMx@y*TPgdr_IC%Ci@_Ee^a$D{P>Nm2q%+79+AH25m6NFwoe=J z`4^78(8t`Uli2g#bQUcSXK@ZeOz!n68rGR7J*IBm}bR%czjDjt9;bH&Utcyk!tUz6hn|F;>~h5 zCAAad1LACYNZpbK`?gMp&OHzLi;tD34w4Vd7X+_%Z~SUS0N)9WRJT+o5G4&ayoOdD zy_fK8VDTkj^nP%|d`d$4k{pckIk|#|0zKEzb`O0;(mp($Sj|Y)!Nvi#hq|=o1Ax#b zn(nsRw|5AmudQwOSs(j~o~tP>&&U3S{kR7E<>=sWcKi%x)CV6+1N9T+Gy{;+#GDEB zy$N@5DFs<6367 zdMc5+Qa(O9CDPAtKzB5xR$Nae)Mt6!FDf~*sQH~ieAo?LueA0p94-x#G;O!6O3a7b zNehMKp`_ou$?1jN#tUhDQvC&q#v8Mg5+v!HuPl4X`9g{+4sOQMan%C|X|?|V2i zaAE9OI+jMQZO*uavgA+vNyZ&oF>6@Q?hwziY?d`uj*aXYhHv5fkxjpMSk6=4}4 zfHvA5P2(<>>$Z2inv|GEu`MR!gQFUg{dMyf$Wyh6?~)jgJpk4{Y+nt(DbD$L+UM#; zod%B6w;Gwk2m-%KSQ|qLzh_A8HEB|BHEpV?bhi!qBc^hW^dx+-aQ7!C zUbNbicnajOw(0H&X~tw2W9iX%h`^BE#DZ)CTr(NVox26OFwd{rzdBR4w_$B^`>WA2Gw@?U(j}q3{o4BjgDj6gBJKCl`{mf3&)57a7ajT(13uO?}WT>ejt2 z<=TBXR30p^CFP{oDB3D}2yyXB@te~@G)v7p@T( zilt7`gGz{t3@cBSh+WJ_GXC`aM;}GsMdZW>0`S(63T^F$6_$ghO-vYo&p1WR+mpIY3>+Q;feYlhjUZKOp3uZ z|JW&(nJRX&D#jA2V&_r}#`!x=u`C+U>g9Or(B7rZdxv|ve2>GygD^?Q*oG5{>Fuf+ zJRdV;R_q0yGY{i*?@n#yo7})KMlJu-0__*bxzpAM_U4z;HrvhID86X~^#hlth&r3$ zD4gx+Vdt=a4%-KXnAN?rxD@8H4W|Y(d~>c@WYe^^TzPOx*eXFRIi?5ThuuAFZ{!o) zef`$01-Ya=TTTx>LVWaDQIQa;T(_>Wo^Od-nHZ@Qq`-pP53;D23*kl|Yu7r~lu zw!2Oj3APtJFWXno_jvaE9`=Zg^c+yLl}4&1@n7L~lqU zC$Q9np5oQdcxVd0o+e(+H!<7=me?y_INH4qU~%H@1h<^a6Va_qf_}umVR$E?UYzCD zU}R3fiDs}7zEa6nt;kmIgPs$bmSbeAe=mVjiS~Z@?Jp_vF1$<4X<+@i)R-VTt*%7% zjkB^c!j+G&$qG~um50ENz6WWhegtJD=aE(?G{$~zSCXN2{mjLs@;f|Lpx-INje0lIdf}qt6p(2r0>aO9!#p^)) zyY{Ix6|3N(nmC7*K(}rG8Xk`rnAKzk$;T`DTmY~F7c#3nACW->L}?zu>n6E$!gOFA z7YpQCb88{#^+uT0!(G7J96S?lWY3F2U~t;qU|)ysN0htaDcGfV@%3yzGs4qhCl=)D z-CK1JC%8-`=V&>1%?80hiA6zRbA(feL9~VpAan!~I1vPF4gkTsM;w|1@MzK*X%FC{Rg z^mTq~p6Kgx*b;U^U-!%>>+3M%(z)5LvThp=a~*yQ-CdHL+js7OIx_!a^&RGrTZhg2 zru#zXAAA*M?Hf#Drj#$70)%op_!zn&1qk;34_zRDTLQhPxei{9E;d(p4x2Q>gN(35 z#kg5Plvo{}`?$5MU_hGp4V8<9|Bu*%^ zAAa89NS19B43LH&-K}&#ER?-*1X9Bp3BHmIcJeMZ7|fk`C=4Cua3g5`oVfpX0r{)q z)1?L+zojcPN?wYe;NJhuBmEkh;81|kckK)5MIFg?<6q(fc|D@XYmL{A6S-&$!o@5@ zFh^_y1L5feD~p;{ePO@xoa4;84<0E69Vvh3M7;50NmPKF!-3{3l*Nr?`jL$Sb_-sh z=KH$b1I;-|bXPnEl0zGetg&FBN|fN1F~%u1OS|<`z3hIx|tK zZ_dgqca`8A_5aIoHkjZd}*E~UVTK!L{74Xgp6xknOE=qjT>N3)M?Fdg5 zf(ufTHG!(TjI?l|f`87UPT+rygMWscmxg}aqi=;GSAuCo6@Mceb_ptl8^KpSu+FVJ zl2|7O=%%pFr$sjkqsei~DO}^A5)h5QL2!y-85EEXmRX&KW#)lpp409Tn_%Bq2G~?i zGx6L8Nz7ysyG?oJ^y1)`#l%N1{yg}_F$OGf@ypZr2ZLYcrQ??~o>lmTXpom&{K6&{ z?tehyg@apKO$WEeaKHp1-PVXC1LnZVNJ%t)@P^=6rCEdC6?nju4$g39PT7e>3j240 z`Nw4i$s;6Onk4lR4mZpD73 z`zV#Xq;}NsjoV#PeZX$jTr49h7SU>~k$0#Y1k3!1mm>2BmXT)dTTr4&6-kT`7#TNt z&olN{5D_PdD#ljUy%C%ozdnIeKwAgwb!|X+vP|IRIF2o%vStW^UjiJamyTENr8w0n zFMvk32%{92r3C7^nIeLe?tsbbWiMAY|En6UMTJ)IIcqANllA*|GtOMmTT0C6Z|dJns^}-dmo!Nf=E>&l{|-faQh7Wg-;Vn&D}=oR$DU zu2POz;p>V`Yl|b(4KYk>vl93sV-})$HzT)t3jIaR@CTrrDDk|4gdCA9p?mi|MlXd8 zS}meAlTF*v_&8-oW=UHOtrFT$*_uF$fP@N1jyfM=xhp0sqPXk(;Zo{EaPoUwYH+cW z^Wrq+Cu&403M5PugSvk;8HqeOI8k|WdF&xQQ;S}9TJZ1jucasy?bezY=)7vg+C6o$ z$Pxi9Hbl^ux3!5UVrM680W)J{NV!CPBNT@hbP$PWlWDHYsaRWatqd=m;qnhAdlF-T zeKS;CjOZ7tx9CvIltoutk*OYFT4Y;PxP{)sf8ej4IG%6N$Y*Q!cru<9&7TugbyI3J z`_9J~+Bci9Q5lGI||d!K_^Kp8~unMNvB2|b{fenlAaxS(B!R5YfMiksN4n1Tf}`NR3?3@Na~ zW>rinv*T}zpQ`MGq7SVpB( zJE`r}pf=`yhLaqKO60*f1pj)D{-sd#?N+cYK1{7&g@n064}xum_h8^VSiI@%X|RA7 z)n;W>gabRWeAY7+U%Ft26x{xnal&R_?Z5IDF}>I4#)~Arr;}q$fp(=KK1;<6U7u?- zzm?;Q_;V;blJ9GNVV0ev^uts<;dwaiO(tGhlfI>XXH&GWdA>HhdB67cR-Cl?>cr;3`*rPtsA%vE zD{3@%k!P{6b-y;}qzQbq-_$PH;yn*+<0bVzP#vZZ-eQfCH_(gZ>P2t*%T3*$f+JAP|+#;wPhkUm*P$ubxoEdcWgT$AH zT8`>swO;uMukpi`Xm{iKaZRSDrF$>lz;}x-QdDm~?Y^k5u1l*^IJo(>Z2Wnr--8a~ zKl!lHLl>3?BJtIHay`uAF3D=;oxZft32;k12J&zU)v2op!4mkgV>nPC+QjWjAF3QJ!t{ z37TrIYe!w>^eoSj~+d4Du^>%BQZ%Huh~PJ}Towen>6VEo1TX zKjBYfA|>hJSzs~EEPB#!y=b)?+sQvQ4Z7%KZpp5?@>kB>KBVS03vw9k?vNdQXo`$9*V)Xfx~7&hAmOw|59r)?mqvGes4oPV1G=A40uA6}Vg{39AYPnvOFbQ3w7`XlxfOBZiRZ zFbsrpA4TN4ilK7}2}YKSob(g=%BAqo9sI&%X8-9~0T~=O@H7%+$**I5j8sN>zMA_r z>N!3G_FGIE)3m@j4_bWF)B5|fD|_{~kMj!7y+ek5yPB*Re|y-E`zSwLP1bldS&urC zC7g6Gvd_fiwNySQ&zBvo6b~J;XRC8vtJ#a|0zVf;9+PB7+ z*KVz);PXD^eHITk+e0YE-l#cu2ajYtwNS-N^P2}0j88nx6jJ=_ZwV>ByORD1OJ;`2DRv{O z*txle_hsZn7HhYmP59U-`q;=^f++GaUP69t!iRb!_?)PJ77oU~>3P*}p?dOuTyvJl zIA&x4RBdriMj!_mw|_Z|m5gNn8$5+!4WtxgtQUL(4!5h{y2Sp5T|$ChQg8KTtbm95cZ$r%muHY2{z_sS`p!?*y06cP5zr zsR@oOdD)DdyZkw9{--DQwEedn>;(XGS5~k>nCvoR*->!a(#PZv)4xN(gjiyiGBuRz zMuh;|&skzvEt;#x8y>itt)iRQR1%G!gA|vqhgt{Vi(^J=Sc^LoYccdxgbeFEp+*~X zlW3OB*`zo=1n&d3KMMnyb)Kxm06LbMf5qRmRKAfm^(OGp+br%KJTD@%aF|^#*6&-*8PPWdQ6O_WouMFev`sfe~ip z&eY6w+Xsir%v{18YCjh6%yDBdcqTK^HBd}jJ@@;J>3vLwQnr4I$yleL@KaBQh$tDK zcQV#p?@q=#XOk1`KM)TNETt#~HY_?{<+Su*b_~8D+PdE`R+7v4t-Eu3aH~Ch2@C+Gd!Ps{r=`+a{{z5lqhzwf`T>VLW4 z_fGwf_w`=U_qnlQ*^d1kdij<|NI-?lU%2mguo)D+SMk63oIfBk4ze7RwM!2V*25$i zP*iM5hvY91N~W!y0WUhbWQ%B35AN1A-%|oYHxfibj{Tr)9~G>X3|nH?q03zHxU0*| zSJAr4F@3i^YNRj>2Z;w20IZddr9X3H7r&9-U$r;wJVuj>dIonZK%N`>5>=;GK=DtZ zrQn3Y@NzkGV@FQTAl9nK`f*yf66OY}&@_g)+a69fQ=Ls?KPa4Q_|8HcGmwYBJ~zfm z6>3|_%OCJk>?s6`J0)!LoTw2zl1T{KbW$HW?T^op2(8IEssXnn%W1-)=ZW@LE}%YD zg!u7wa$~;(ait&g?25r$gx0n4Fe4zZ+vN4TdFp9r@@bbm?V_x6twSGJ{~k^JODUJdg{{w}qMlKW$PvG9S8 z@9kByeZAC$CDZ=4hndShlF5#6_iJi=GO%x>3<7-VvOo-mwDbfi4Myajos7WFf(bL0 zwFvx37dVR3f%e_fiIsSh#h*8BJ|M6A8Qb=zKF5}*{{fuKuEm-$xR_o?8O2U%Qke$TNf^_Vpj`XB%j(7 zc_N_kWvPDiA;t2h&ma61lXn|4XDEvtmDTdO&bl~SZlmZ!e`Jv-{)N=?A(y*F5NV9i z$DY7fOz+<$75QB_z^qd_$?wFgMi1YK2B)Ugo-0a_{?mb7rnf9W{Ksy)WjM9JNxlUK-yCN@{j!ZJ()JTh zvg!NjRc@vA14k3{rLuC&ROSK}VhW0eT*2q(3tj^cD}S&th6^(gix&HjYgyO?HpbmW zX;(4va2!@*`^D~|3>xpW^^kl_z{7QjSCS=Zw><;E0Z3)}zo_5bDeKY`GDWEryG$BO zF)+-l`^~Uk-`J;8QigVh3@u5Zd)iMNd?82v+@34R@T}d=c>=@C$nO*QUIFNuJ$nL>v$bgT7C;s$sp94;` zbC~}s&AO11^SjeXd(7l5*s|=NU zSa!N2K?!m;iZ&Z98)fTa#EPGWv$Mf@4BRps0(58^J%OQ9y%&@PUMFW(nSJdkOvWz# zX4s;hFqtRY+d07@_S?UySx9>%l%Q3l7NEK3r}kngj8Vv53YIqxzKWI5U$i%#EBcQ_ zFS@q|P}E-(z_YL0-U(bHWQ~OkxlpX;S z+drY^Ui4J*@uAX6orm>pGje@?U-`Oq$*FA`p}Pt?G6L_3N1{zG`oNnK7}u$~=UCpx zl&cMGe;3uMB?MhA6S9ud}#4?gOB{Jr~F;jFZuU-4ajOn5A7ZHQ4=qHH<7R{Ea2SwjGkLm^} z(bayvQGjyd0$DH;4&J%goqTuEHmcWutCu?Xn9~#2HOO`H`~Or0LaSAQXaLu}b!+7M zmd@Eu-;^_R>@F#$E>rX%Jcv{Tfr3!6B=Kw%4!1;^$oDdKn41V9p{6F(q;C0KN`-j(ucn7>T@0CU9 zRvi@}`3aB3AzLQ1fXU45F_}GC#@NkLxF;8x8l-^Lfk7UX%3s2asqiBO(eF9p&C^@b zj~{pG!PPVH?Y>fEeI`S42nSrD!1_QB-xCEdBpEEZu}^!F=?aI0pG>}x8~f|6yby8Z z5fJ1*(($t5&j0*h%#7MG^Xwl{LZzS*Vm_bk)1$auM^TxXd!b+U`JKZ3)v`!x+qky1 zKEcxDyW@c6SJdm{faMTX;1j{}&4-d;nVS2AK3e#z(7t>C3LQ2&-B;~N8)U+`2Dm1p z0pBIoRW%;QC3An1hAnlRhvvqHGoEAebA|`0L_Fl+37n9`o?>0cYm@);aF@&iE35RQ z>ZAqoMaV|v1cjNzJhOWLco08!B*G+XmZ&E70B^+jC^;s2e)Ij_-`Ik?Bp~m1<|lEWgGge9Qoz9}iofe4UuiZ))O@J(P?@Rh(Go@4 z-obDQSmBV<_Vmu!oA!tTe|KHM2XKtY$R81#A^hm$5!CvX2F zn5|v>3IT|rpz#Gs?AF99@o^Tx;35o}`E1Za>=!~Dm^JYpBrz8D4EMf~`zEDei<3kK zwVU#+6VDM$9})EMkzlR;!8+MeI4{s#70IE{vjkXdFtO z4$>{*$)iB-Ib6y>w21+nA_Isrjq?&f%8VVepB{(*?7g3y{XS3fS2-h`1%%*MoH!*w z4%(Ld2MG@jNq>Xwr{@Tn1U$GT*hO$gB-mgN{bvekT*(MIF(KFxKRdNJf^XdMzR5}1 z2?XITkej3U%9=Pr&i_1fImliZY<}s zgab-s_@8Ivqe@)HCbmc<_^}@Gw~{n#|E|n5|GZ=P!&3OeoWA%&{I<3QP8ZCe;OrfN zGso%pc22*FztW4_a~KXfo>;b+PO-#IpY%VWKWIxQU~*$$CU`iqZkJrQzaw0qQedI5 z%7B@-|3jR191u@OJTA}E_IrVIs5mKa$xAW1M#_MF6jSOVrw3J;nqt{O!chss;_94! zD6c{rW~-r~NxTBjOGPc6?o6ONaY=I;A0bbBb}ZZVCc2G|3*x=%J=U<3gW>Ere^MaR zP=27r6LjNM#J?z#y?>)JG|F)oGHfw4cB+loQrVH?+}r9tGIpmFmDJzA<50#F{}1yp zBgol7<=Wcrxv@V&%To=evAMl)O{qHMIxkh!)vs+l&iemO*56%#-e>e3w5Ri0{8aGX z!6&~v@Fi)Xcm0SP&M7_}DC@y)2=d#4oDb&Y$r_o&2h3suhGGY?wkK|S0F<*o1fTXt zlU&evgc?2xG=8Yc^a&aZdBw%Q4Il=)`9kaq)wspegSZXQ-4P2>>HwEJZ^A98nzIAuXg4I_Qbo zLrYTxKIW%P+6txVJC4CW<^ACw98R~P$#r>~cU_4y?L0_(?|%Y^=sQ%{sep{LgVn}$ z(~@tbNFN~6Abq|jZBry=KX&$qO5SJz{+|xyF?$OA(f)V```sa}?sf&Zh3?Fxu-=ut z-jL)kB3Z6&B`P~Jjnn%w#Zr61Ex#0kH7I_j=r1GFQ5nK#Zt)Ox$q_S85`y#>Eihuu zfWYf(assS+vmScwfO;ByTd2q8xv`(TnvM??4>NFXtdT&2bXu99C$9cc(llO1UMcoo zZUP>Q z&-6`ix__n3$wj}4@-AQ#H#!r;E8FX`Az&!Yu-D^?R!UB-#P1|uf9LjZ{L5^qwnl5yc-jJ=j!wHJ-L4p z`H;}x_Ba2v?ilhQ>Hn{te?8lsy zXhnf?{*=hDiy&Q`CuIL8kD+jYkl*u>f+9k)-j+9(vl6Y6jYYh8Q1tc7YKCIAz%RQ?j?eSp^%j)_^LBL@g;@c z!*`Po6KAMrYshmpT2j0H#pCy`ufA;2wTAco>WkwuByE7aZMuPe_@h z`{$zBaW-_95t*b5?%=nWUyKoV>lB|3IV$GsQo&_K8;Koivu{X!)*-<>&GvZX;7+6Y zz44fEC5qdK3?WCLzja#Vf~8X;L*C^Y>~K|7lC}~$Vru$pUY1DA-!`L2`qyqBc`emH^dW7or|y*Sn0KdyXTD3(O-9l4YA9`~qBiC}N;k3h{hIzE z(vl!98hzi;^rxj?Tz9BKIH+m{!RMQ#OHNlRq$|BAsybCWr;Ngzk;PiyNbPSiEU>Ql z13|yVP3TH|XlC&LL^pkYVJdxozGTyPQY2av%Y_M7Bj;%;K?%&V1YJ&?oeH{~GM#(9 zU!*DYUMXY3vx@)X(LppQ^9CtgGdUkDl#j}4)^syGd4b_=t2t4ROrGbs<`r>SPDC7H z!mN3`;}g?6ZJu)c&u@d-SWlozn;zPqNk)<_weNfQD`5zpLq2|{MzXZ#J=`5O!_#x8 zhD|LOJm>d5S&g4bDOixKE2V<#D)P6POpO`70-w?Lnvoa-#jU1#6g5z1PO=hc zqEM>!6z3c-GI&nTGdWg}U(~mg=oOm|*}&qMXKNttU8pwhhU5JnYjAh59=_CB%Bu^x z&DX;$?qUjGUCynpGj6V@<2KG$lV+C)x!ousA4)!F~TSKxihUQ22ho2P?{4UOltzOQN?WJI0r&ZDCM>o5M(B% zEl3Pxa(T~@2wb@%F;6RZhEg%AJT;bi>0_BUH5|~&r!uPY^wG>YtvoRtcPLenyR>rf zQ49Cz+DnbH@vuvbx62vN3GR4wMp3Tv@5^dev8BGp7GsmkdRDPu-($ht0aY!~tqF^X zBP=i3qLyKR9+^P6i{xZvPuxe?1Rx@3VaVy1xWs>Nff>HGm`F75ZeJ5DHCmT8(nRd` zsf@<{kkXo^wjE;e9~`1nQyoZmvm*KVZSs zh{+3Um#n&zW_jeZvIbKl!$$c~5c|WW&e~48$+YgCr@UI|1<0^WEok?YvT^54i;NlL zE-J3W2bpB{eq#R=vYpUN6^xu3zF@H5yARbaVp(WsH`mUj_(w){&kLr96BqZv<}!vC zeI!+y78!D>bj1vxgQO`FF`E-Yaje-5jQgUqKQ=g?>F@i) z=lKaqFs+59d{s;bG=KO4P|7|Z3S(J`9nu3`+TLb(+Em+WKj;ZqpQ_7_mL7PHAltDr z6&Wf`!ugf;^l(p>-_wrpnnlFzDR!g^Y_MYZLZE3tN3A*gybacu+;mQ5p^>qk*A=w>rA4K?OuT`!vjt{%1! zW@+a%2&`#=1t-8G4QXL5njI`V8%U^VVFJ*GZ3Wo&Pc1Wd=5ld>vP9NRXy+AxOcI<4 zZ4xVMlz|3ubk!!Trs-pmi@BuB?F9R5A?65Wj4bxAQ1lzjv%NEy5k#J67h+?jI|2MJgRt)R{tXl2b(bt5ZaJ=WrGGj+Oo>m-qNA<(Ed}%jmoLAe(?qASCv)w1?kt+virXv z_0{~B;W+}3r*pp*;?{q#DqJ^KO7MkRBDpV=`|vNu7OUsKP-`XcrA9oQ^0c~%h!FTY z+SGqpK2NJ3nHqUmy^npFSBVKU$;ew3xi?!r)V=$)kYIen(^cWl37}@AXLyuQjqs&@ zSVF&bHUBTKvMNU6iN9ZX>CkgvsAeKj_tyl7UCv~CUx{jHO1L67mvTda@H6=z$jzB* z1%~qH%%N3f6+^pf{^|Fu_gh=hXZo!VAeJRv6d1|g4~(=qWqf4kRO_yhlpizF2pwT? z3)SFiY`z}ZQT>$R*{aUoQ}#u2)R_@;Cox&~KQGf{M((i9$U|yotgiSs(kHk)!&iTV zJ^3{~GNie({xFwU;y%17L(x6ZtPzUBU0qG!;T3E{>_ZEI0XCjB6v6xkQs)sVkcAZ% zK0nlK>)rlnV6GYdZmx?5n5?oTf(cYkzUoYJ`p8++q%@(n^PJiiP#f&WwQwY)1@Lb0 zJua}wT(YxBolN?2NghG+w3y288`?~VZfqrEuqQB1aI4?@k&9UkYYW@?8NcwR)^vQK zhYG{vw{-sJqMcvJ&^|(AoK-HvC2q!%%o z-@pZC7(;$UfeLoVOP-go$Zq>*&mD(e>`Ky$XSk9$_x5+;`aY5|N>5+~h(&!oJ4fKp zAtFtvZR)EFL{S-eM4sCR$)kdgrUVh~ILCThk9%z+WgfCaHQIGg=8LwPTR-QFTQ}90TYoTk=#UW-zBUw0> zIo+j9D^RvN?$slz1=E66m%p-ylB)2-V>{9GR)y~$+od$V zuj@tILF7cTy=VV!yJ)g#+EspGH_|LQkGU#Rr#^?WF;S$=Ao*7M2FMU(k|FiG9gusc zoqJU~RiJa}hrCxas*p=J&b-p2U2cy$j_J`b=}|%z7b1ps#Y>BSl;}-tGvCY1a_o2` z{a&qwa+&L%^V}Bys&GZtg>w9t>}&%GM|~8oY?PGZ@Cyq2R<<#}&00tn$x&#_{FY;~ zhidxk)%;GepvW@0;NX&+Db}1za;8}qTrwpbxWq>6zNFKzUN(xJgKgL@^Lh2NGN1I% zQ6wN(zwzCAzvrmhqJRL?*yhMT;I>P;RPAy_UFId7Q!Mim+Q_=Z!NeD{4$@GYG_meX#N-d;3#&%wlZA$&_urTOSdciEqta;81&wvk~QfhM4wrO1hcw+3cuE{mSXJ7zi?04{g zq=9|fdnE8vNl_4CgAA&(Cs}mm1dk28ZuFjrE(U zv1+O+*kifsKmnn#h;uYnZJ1_+4&?`G{gEp=s>)_+ZPl9$&*9ht>3aH-^{~G=`d7Rp zvlc3Nl@vdb$$5{8$DmSt66bIc2+nVfDezllu4S8|Am!{DkST6psE$HlYr}e)6gEwq zBc#5=jUL}2X8f)K^toeDAkGwv&A(8CLf>{Go7VR(;vg2GGEx z@(1*@Rp^*D2^-R(65l_I`G&LF4v|V@8J9H|B0Iwq`H&)`dgRBgWOjPAmKXLc;)7t; z@@Oj!`#>$lDoq|cwAW?YiXTgLlCMCFXid1c%fF?Dck`mV;jQdbLL@AIJcmN+OgEY4 zKa;6K{PVsp+GFbpX%I%LO@3Nd?Ba*QDY-pRVs=M|2Tq+m)QK6$zPF9ftgJE%zmeqQq1N@tumY7x1Rg? zSa>~lKJ$M)DC1H2b3AzUz`oV=HJ=i&BI9`@tN;1TN}JDd{m$n%8OpKq`SH?EH=l&m zAAdeKNJWw158j)a&uL^&=ry0elv2#+Z(Baad`?%B*{QeN0;1%nS$1^zi0q6e`NL#x z#;C{U$33U3#P<((^LOf(30CYqm$LaD{Ua}AF6;3T2Ox5KZhxRjA!5;f9efS&T2P+8 z>o+bcfw>G^j`5y@m+WB-#0AQ|_TTRY6{xvY+x95(9x#ez$T?DBH?!PZcKH*T89sG< ztkUErQ4^dLxg4Zp?-6sbcG=ahC#R|y-4W~aq3w7`y_Onc~4~$}L9b#ob9-)MMpWC1S>$N?l}N6xLPuR%1k-Q*4y9Z_=W<# zs0F>#eBC{RQTy{z^672XOS=BCR88X(ouLEc)%cc6k{!veC}(8I?2&5JIR;VBROfx^ z&dHLjN`1VH*k04xY82ta1fhQ0K5Ms*OY_w3T$JCyP`a+%KD>*|W)OmMe zL8#7Q$7DfTt7QD|VRl$0y>~|8ERGVShr+cCsQISiiRD4?J@q+9gzUxYv{$+F_1hBs zqO@^C|7}R^+VZC*UR+jw`FgHQ?+5sBVc>|HXxf((Q3nZGvI^}u{(qRX_8MNYZ@*<| zlW|9UNEc9Telv?*B}?*eaG1iK`aRE3Y~l4Bq9&?TN8XG~>omyo#i=r#C4>w|jj~0` zsY%RLY%b(X*8xK87C9V0f{E|0L7mcSzp`A)hFbTytzL1RQY)q#2#s=*kkAgBb_(QUnolRnRHl}TO zZRG`kUu+P667g=~Q3MIogO_RuDf)OoP|^W64bWDeK^cKA=fD=H*%(ngXs#)Bh4a`g z`yL9!*mwolXl~Tmsn{I`sAN{~4e9;IrXqHHTr0-wgl>nT*eE{?#^M)Zi0R`~6EJG)#($op;@N`R|AaD z`h1;K)MBpvFW-TvCs2qRw|;PkLcR1@%grK-U)ER6u=!O~0_Gt8WDVkTlv*iN6nh-q zq1vz)vacJ2p7f6qz*&v5QP)jk{L3~99ZQonljG|xPvn56p?8hYyL;K8!X&bV?>i2? zYdwbELCk`yk5BK~lJxFR0~Ngk-{Oy@RO;Z|$bw?(sC9{*Ks%RUakWm&D73*jgi$3B zaM)S4!D@fe`-{Gk0!Qdu$pN3T6)4IOdPykXZibi~KNh2fOeRf^+YIGvtC?E6OL4A%N3NbG}Q&_f%vVZ$!QVugdW6C;V-Ig z11un}t$q(ln}qOzZgfTTE_R3>9dr&JJ|59KfhHi3u3=_eIThn%Ae2vVC6rIG43fj#Qjx{eg>bn;thqUM zI@ebAL2&3!4?`rko8ES9 zxg@G(3$BsI{gDD3eRsomK_bC(KQ5IuRbfiJYICNGs-|N)-G*%AsvV_zlV}fMo8bi+iQk9ryU{} z)R0xjgiJ2xjY4Cw??@Ra95k~%hR=+PTR4v({Dk5_ptIo3a)xu%rJ)Vb!3mw{t2=$# z(5g;t=p?A;r7|LG0M2O;LR83`ZKHcj@OcLBv@?))l4NalzhuqXVAz@z|2ajGH6d5& zWKHSo)8)6m>6!mH^sMdk&@+gjE5E(eJpjC^xNV_lLOUQX3#K1S&pK1|tfLn_Lyp_R zHin*c97E3%@|;M64t0@yq%k&rN}kJhNgC4*uUXkD@*GsHFL_SQDtJXCN9!q>+ASl6 zs=bgHL{E9n5$1kEV+eEOJ@Ncr@XK&pd&+YOSx!l>pGlU3axUM)=5k~?#?(JWV|Jlv zzZHrWzr43dH}&BpDMO_5gY|=uvLp>tUon$Nb>E>iXxK>(4V%v$kcw3Ij6=hYmFmcC zosjB=CurCqk?IQgR`#bO&t)UeMICugj_-hQ^)Jt<1?eHrm13W9szjdKq!4Nkc}^u;hg3Q8Ty}4HPKXuq z95Jm*p8LmNd&qM&E|E&ea~)8mj^oI4;{U7XIS%f<$rSQkw#auK&3m&&zUxr(U0l(n zC^ydp|0DK_x^t+MR`)D-FkCLVYEbgsG;)uM(_>X`ZwYTQ#>I*P?2eBmMszdh{sF^#7;m(LWygKbsyE`un97|6}xMBTD6dXx{%gJ-YD0 zPeqUHgnm)PrIwU_@&5*T#JLoT9_9R>NRM)$M}_}CJ*tDI!V~`Q(<96H{PYM<7SZ?p z?lZ|pQE160==%_bT={6-{vv4E-W$ePvhl6QSI4Vo%H)Q@oM4N-XumCfIl8gFq$II@iRvPy>1$L{lCpjECnbF;B_)kYNJ+}}CGu7puhv6e z!bM)$zdpOXl-FC9QHnmbUOnj3+5ZiB>ERK*=u@fRctK&DTTgBSi_GDzO;n_w+t>JlIiBMaW&?F+%reHW0Rd)#y zN|H~aJ2oA&T(9*M5C8B~0wY>$PF1Q4Ihx>byvW@zdUzngxrTQTK63f3Rx{1lffx2m zUy1PLz!(XG6`LAe{$8$JP@f8fH7xuL4QpJ13IpDn6uOG&PD12r(N=tqRgMkknUYuf z(+`Tgb(gCjJVxG9h(=o(718MD{7DV50l|gnrecEOa;piiUX;J+O1nAK3NQ8Y=Yj~pZ;Yoa8bgvho$?x3}xE)_ZBaEr;$?Djm8}aWYyhHD-ekFd5 z)Dax)RLSYgW~wIV8Cl&D_{FUciB_sSJyqq2RM@!)6V&@PFHuJ}fhWu|D!4K^C-g;a z`C*Jp<#h)FceoZg{__a3R|N-6@BBVrXY#{&a-BP!-l5v-<<9{z`y_hTmZW!2#TC6nwC!Qvn&--2 z`3WzF9`>zWz4U=fJNIerTV$&5Vc%NoDg!^eed{8es2tA@)&2hMTST1xZ`-$uXd=zN zReL;b;HR-~c~>Weuuo^-`b`h2_wNa+_c`oav-vi$Uz7GN2K3*wZ!J3`Ns{1=KC6A} zpP$9PHKPBfK9_y#pPyvks&!R|q5nv?Z)x}3og#&xzzV<>`_``B_N`sL>|1O)Js$-k zl(0WVB4#c+b;HxUxL7Opu3gIBwN??nC^wse_s7<#yJPKLKT*`CmcC_3HVx4xV(^+O z+Qc3fuY4?C`H}>JUstD?OahUzfZgX>zzUU0@lxtm7BF1@9ShhmV&|pW>yN&P{p*j% zv41@$wC&G5Xq%c(NkoJJth7%9*k34*fhgSoCI(4m0GpOjDvG77UTXg(ullfJ^~Bf5 z*uM%tqy5V%-^>0bk#9ZiUk9K!ecQigQ`WIdi88P+`xgi`-To!!ydL(i4(wkYV*l#E z{>8;7Si}hX*}O|EVq_6;En?4oHfpFWV$0!rkEMpa>E9}~2>vFC;gBa2{NR>A8*Q5cW#tYAiY~zvc_Q_~C2vrde|a^QU4kW5;*!8e$gb z4CYVPU?a3MgJ>^;q7GF*4z_MJy;}nlnPwGM75*;6y6dZ5I+4h7(W!^MG=x)7#aC^4 z&>>CA_waeKn_+O1{2w|qiHK3g7Ch?Swy(g^)0w$Wj$AY3SU>qUNULjOp^V5hlJsm1 z=9FxSE#tm{2nId02L~zt>Zy@4i?Ca*De1xQg3n;br*tXA#s&7>Aq9Ryd9c?{#0hIk zhaQ5q zCjuw;Q;&o`6P-g!Ffy?%iK-k^9Z1{6=Wql(aPkgd*VN$W{G#U9vG>hwGrcd=RH!9X z@L`ibB%3Tmu;h6AJVo#qpfS^vH^sWkgGj>yUjb;$^bmPE4yoZ4JSkXT?+ewB%Sqjn z9UzMdkWpt|m37yNNJQTy^7yV3ozH!Qg;ktLpwze%<**#SW_!nOVX;#v7Ge5rHKQtx z9+5v+MeU1z>Ocsk{YCF-bra<1s7NJnfk-#EDL4o(I)M#hgvdk+Xh_^Mp`MYMx%p;h za1F8YM#6E96!nu>r3rus!M@f6po1iKce6I%V?8He+M6)IU-Be5WwNJ)$DKMYTyZMz zE+vak&Xn+7r}D}7a^-?LNPZMr4{U6X5*J6DRqz2O84E6#SR z5?FC4Dtk27{?%?k;~bF{DD4RM!?`1r7ca+++VWeuaeRMyIY(3@#W6CL>zi3f|99KAxmun>n0giCb9)zpyZP{ z-ViQ*KyVK6ke5*XCB*=;M4$;=ml4?Rtae550(!d%G)>UcfyB${+|%bbvVEW%Qowu} zZI~Lt%)D|RJtmcpY}?G~L{)#0Wv%!EYJg_HwTYg-J*Dg}a!EuA&f#Vs|M&2p$XC)D zNJu%6hH@mjAXl&jzbxHEkUK&CnTGSWlwWyU%&)vHH015Xe8wmr&z7-3^V5r#wbxOyNTkSSa$Q z69h~tGm~P(1j`i#hftD`VD?QWe}-hLlq=vxax9pUZgu~7p-32JM8s4aK!EMh__{kg9LSkz+Egn0mY^7&- zz-Gb^(=)k(c(QB5bHpijP9}ebWtwGXW@pWR0kng3&n{BFF_>0|{}(soicAS19i2xA z>1d)ySr@q?e)fjZ#LwO^T7pSOi>_CzGeM7}7wjM}(df47W@?L#z}M&&*(~ z&;Wn<)(pmatA}{yVZ<=c$&j&nWUL;u>{d@_&0qbIGc!0$LB@&$Taw}I@WV|IZ z-Vzxv`6#%O@s`MVOJuwyy~lfNi5hRKGhRuVD2as4K^=k;aqrB8ko&lKk!W_ny)}M= z=u;}bKey4zaN-~N28puZgkGrl{#8*jQ-Pjv74Ck{V_H!wE#DPi*KlY# zmVahhsHGso2+QuCT;m%ZiK^Zf5&g_lNAIuqHxdW6J;$_Pclnr_skX| z$vHdkkl`s)k5i@Xubmg<@v zd1EW|YLMx9D=BO2;k!1XFiGDRe6Z##Y#|uZIbdYT7i#)F=ZCAy=oNA!K_gC=hw(66~2?O(!K}88d7o6h=J-|?7gKRnh@62E{el`B|oZ2c= z0v{`KTt$8hc)Ww0nB+JxwMum)g-LXih)4)$(=4-;jjHF$1)DMl^d(GgDSw8P%I1?( zCcT|Cg>p8GpB|gumYSbSo$3%!l#lVo)NDz-n)k&(nV2{fzQK+`A}Y|61jPl0cIx3` zCnx4zLv48=G7|rBNuzbE=gDO5P&WM-pX_30(fIx zb)+KmBK~AuGl(f~BkhVsH+7i+h!b0#wiQt^u z6q6$qT#1WBG-L81kE@ZkR`rm!q-1)7m)PI@6EE4Yks49NB}g8w^-}KvtR+m zNI$|PSF35AW?FI#=+#sw%G#@WJBvnk<8!0cO$8=(Yis;MM?Wt748;{c!*7D0;R`An z1#%%jPM&-c88@0lm!qW{qg8&KY@Yhd29qD>h#0iXf^cG$$jhgwSvw#^g0Fkem)hi| zrlm8w>2$E1?TK535(PH1ADk3moAdF8&_XTDm$FLka1;a;xoAf!hUJlXD>rwFHFFq3 zVsFj89$>zGnDbC{Ydz06zl8^33dACb;4!jq$SGpqrAvh#@OWmw2o z$YT655gn%YHLdPihGtm%<6oNJfR^t6V(wkQqpHsR|CtaWf#?o`imldQy^v}Z3R+^Y zW+Z_bm;qE!tWs&!RBf#a2|^1Jn2fTWPD@)o$F|3obK0KL_Oy*{xoAsG0FxjpK~(Tc zz}sxcVvrVsBKdv3Ywx)vgo{0W&i~KDWZ%~HUGI9=yWZR4^>kWE)FJr2aQsvRhcrG2 ze2`YH1wO+_&fYP%YT^f-B&d~_0iOZfW#mJ0Et}sLN(YOQsO-}&1qDwprM_g=k1A{B zivg70Vx=SgS9sjuF_l2Ggl^E9$&3~utL@2*R)2Ay?r2#ghVMr9L5SG)a4CT(TCin( z+C53sa6nk~wga`}>?v&7}{x~k;GS`OkcoGvkcJkWG?*0no( zy?wl4uVroy_Z;NnvDXGkd~^7br$ZN%!5xkq0ULuNdrYm2l7NQeZ3{S*%_gDrO2 zx_1t(g+tcr+(sDo(V=)^35_1{to*mC*dMWF`F;ya$dpi`c5bLVG82>S$0sJ{KY?Rd z(f1|LC6F%DliqHFkvi6W3 z8O|e-|AY(1QRt+uzlb%c@}kiZ0uid6W;3@pR9<sC#P1)G_%}4wUl&v&Hi7Lg-x-nxNcsGY#cYN*^5S)VFY#+53E+VT zl9)eGg50A-bKI4K97cwuR7hG(stQk*1aOTkX}Bv(f-AjT$?lW{i0dta0N0sZAqkQ( zNv1T#@YF{7G?!}m60M`>+#-`3%Ssj%AZRBLRCQ7Dk&^KM741?2C5Rba=~7gCMp8Z( zQfM?J7bV<~#R!=Y=17*o8>%oOCicV`2@{P~)vjR4q#W&5hh#~0=o9@lqNXX-ZqiOy zGxTL9H>MsWOX_pw$U8lm(_B~PR6nxLv|3`P`jP68D|xCP>Gr0AO!Xtxd?Sc*K9kww zZG-kL2ZNX4m$TH}&n<%Kq0eSNm&mt`=mC*x7=P7UHxFUbhW%K!9nglZarT&y4!IZm zRkmBG-c5NFE^FppbH#ANL3qle7R+lh0X-DK2K~`R51B1qq?8a_yZQtleflZ)U)cKn zZnQ_VUEZz7piN?DC2dk`R-1I2*+;!;E!K(MEDJxUyU0(@U&sAQl8Wp%xq75`5VmHU z=I$R7V^*>vqHG$2z-a&MYkPe6^h=oOKz&dgT)uH~V({avP9^A)Hp3MfEz@RcnKm=8 z(^{s@(lTxKv`iS^?w_?XT=rH?fT*{Rqcxzv^WX239zi!9(j(|5nbkAB->w(i5W(|n z8ir@Ra|PsbDJB>nF~$(f!FABWHf{2E-6J}_5P(cCcUl05_izA}^;wU4P6?)gHiQuxif`53BaLr5Nqy9so3EkE@*;hD5}fL6@`Gp#41&zQM7-rE$#6 z-`A385%{z}V!v%vRF%*VVu?~KbB*m}6E0&yGxkHRwMC#AJB>!?7ZjrXzR_nI{Av(> zvCjj=lp>2}8TkGa_%;|_-i%gIZes@J+!s?frqOO=8tpdrhIVlyfh;!&C%hX4-U*QQ z{LSFpd?3jkI5!_qatF=;dOuJk^ONWF8g zEpX~NG*r_!gM~R8tqt2qz;QYZDRbCe0U1@Z0y3y(1!PQdl~+LdiTwIxM=ZY%jY1x6 z5BDKYIFGiwT82E@?rIsxI5m%4hiDmg1ry(NgSziy-QaE%*%97|5-1yB+!+5fyKch@U`b2kUdUFg55+B%$0VQM_ zP@+3@s{DaS3_yIqpNaAO0gUPZWC#2~gg#(W`P2c)**^>SAr?*ITDsP{)Rn)x`i2su z?ykPUXdaSkj?p|2Z}leB4>_6#;;nuT)JOBMjY3BfZ&jKHvCL^u!o<~Ov=4$uxKCAE z#^JxHE;ZJZKcRm()7=zI5j@Zbj}SaUoYPGMqz{;HbP=*VXGe;(o#~4%LhwM;q9E@u zi63ZJC0Gm;EQ$q-f$Y2HEAadLfr14bPou}U5I#^2}024Y9=^w;)~Qboju}_G!(GcHt--e z@hD;x7ontR(JpE&II#IJN{Sy|Vs=s5KCz4XI7*5;l+#mE?75v?)CIG=UDWM-VHX9t zN#4f0ki8xeu>#V=D7Z|-ujYutznvVwHM=4l;?FMz6Me<%9DM~MLG%^X?pEs0xw?uI z2jpy}EQS!xL?vsd)>ndT=}b&lU6T9O&)zatsq(8C~xP+Z&+wt;EDIgyOwE=D-?^ z2e5Z~i?CoEWzbDUp-f~6lMgvQL(QIt>Vxa45LWG?B*0XHB%&~ z-!wJ4MOF+a?P%q3ts-8I*i*|XHNQMEa1+z1LOobK(Vb)2PR-7d@KrCRI6%?mmgtlC z*ey`XKth0oFHwP)xQulpJCcrB#1N@!gnO(Rg-YC2FA#Qacv=t9$s&znf?{bxF^7IR zitCc)H;gKSu`pUYq^F_ zE{~&MU}q_VERfza$jaA=OmHB*>Ji|4Cqzm=uolaX$nY5ceOaa#6|Sp8xETYi_HZIn zmpm0Fm-*vzynhkLJAyx+yQ-b@CzpHVtn7r9pAG2b4bbAm)NNhc8ee-Fu94l+B3;2iYJo>To}?@ zy0&aOXn$~+(jJ-)EcNIMhykKjjGkCF21MNe3%-$10wWMjk3mN-{#uyYW-Zo!3pPI6 zgV`6Q@zW40&i$gCf+Lgzezwhe8Z*}#bZx@xcT`vwIdc^lEQnj2zX~-oduQQ~tK{|Y zle1uw{=51xsq%NbsxEbzS%=gU=U~pUN>sbZHImL|AYRuv=7lZe{xgnI^ z;#o?XnGYW4g83K7iuo8DyU2(ptlshw{q|?$)*sIC5{^#;QP+Z~>)HuVBkQnclq0To zq9mi808a_G7a!ao1KT@Ef7>eDgRQL7X(r*mAv$2kn43?`Ss7h8Ws%_D+s``3ZZpPlgrN^4CERR<(tu zLOW#1#rDC-(+XV6Emv@2-msC7wHEsbBS{+uoA(JtoU+C;?h{KWq7@ljs2p6QpQJTH zcuoc&7?E}P2C@#PD$*Rr-im@x<4^x3jrJSk_hxQtk13G3u8S=lV%?<)}m%rc)fI%Iw z&x1H2;HE*OJZ%sRMRCo)!-aQ{XHYwe$XPFj{4b~O^9U2XY&qPX6_ss6*uO7ew`!nM zL(Tf2_)a5bn@bY(MABbD{u$xTY9D~!t@ceYhdEBw$ph$&C)lG3I3}XNw2yNebx2XR zeZ^{DvD&BY!fD0X_K~MJ-M)2d-+nD5kwoPQqT~q8ZW@4E`4%2fmF`PTW_=S+|0wPD z1y4mWB3j;7c|RtgqYHyqjl|Ewj3R;&@KXVW#so53Aj$9WjM=!g z_y$9vcRJT_#qg)t@aq?h*f8Q*k(eJe!3miIKQC{Nq))3H&l1B)rHTxyE@04Awo6t+ z3KH6W7>2w6>wK(+OdLPgKFCvRNdInNnlByy$KeLtO-af48MpGM~6hxB&t?cV6GMYmP&iGA?8y+g3G@L!~9Cl+IIaqY1 ztH&TrOVKk>x=zSmIDX3HcoBY%X)D-?<@wDJj@S6E@ytAJnVQhYxr^{IH=DB`!&}2$fjvj6@ zm50lQbJFllki{zzIK0LDg+S4+WSGdHuJm~6KD$A+g%{<-J8i19aA4<~QRvpM?N02YL{wF_U z0miXpwY%{_gmHdICLq`pqCZ&9c$$6IfeP)fAm0%m?#Eh+gLn>?b)tT(7~X+UoNYzL z@ZH!gy;I-Fp}xCP)>7yWi)S)cFMR=er&OeLPpB!?C%F1IzjptjGGGiP#5MML*^`kI z(kG_pX>>t@uZH885FN*T=;?e`RN;FGnM!ri1KyC4|=9&x`|Z>#<(;LY;A zP|-27;{N5egC*`?$j5AN;9qs>my!2B1aRp3q4ItZ@$mBf(Byb`c`L2G0lr%T*s$cS zlZUY&3}+Y9<_YzGE1~{*fV;vK>VtzdTZ*ZCxhKFMwS@(K3+SCIzn{_O$?sQzk?G!% ziD6I6?lr4$8CdN)k{BBM4BTTzcz=Z)Nm9JY?Z1lvzjF({2^?P*PK;RxBFL^D8v)kD zY;wk%5xdT8>=J`tkl|7=_bc zuW5GyKD5mu9NFtNpHpsXnB(Duz+#F*%q)NanJz3^8j)wc?#u$av5(*|8gRl31<6s3 zXr;wxpay#Trao*J5YX?l>hEBJ0<}4GsUaj)w2W+AJ8sqKpi;8qOiR)PEBYcZ%1X{F zJjwZ(3_pteJZxhuln|E?jz1_CLe@E99XMImo)wx8;mL~LPjRl$94Oh1h=xx_AY3^J zVQ|sOdXkJ;Y-rISEa(R%Xa6~ZgbT8dL>i!*;vtEujBO40j7WHpjMWE8Fg_^hva|N} zgCtWHC(HRx*D7)1a!x)C4{BrKoV8K1@o~IOFV^q{6&cyMjI1L5j;v_BB6VqYl1Th! zk4`&my8R8>b4`B{gJ*@}%nF3!>aT?2@XT-=A0Qj?0eu{_(7E_zuTMr?w9vWugXQ!6 z^f&BpWTq9k2Q-?)80fb8OQbuuzXR3Zf$Hx-^>-i|3QvaKy}y#Er~BJ#`YXDF+`BpH zogNrSBKMA9l=kJ!cw&z)`S*36{JRnP_mw^6--x0QBk$%oP$Taq$gYuhC(WYY^%T4^ z>`pk&2`d>>{Lg!q_!((85`&p=-sfbk?@PjQLYSj&=|#4E6(Esp+sNM_DQ5OvdU)A3 zrT4V9N0!Z5w?>xD`DLyw`zwe67`n~1*Y;{~|MaH~>#XsBGZwPDtaH^823pLrbgnMn zTvcaYB`*4v@+6FhZ}PL(z%<`-oyRo)k}vL@PX3&C=sG+pHjAn&#eV$;t&RJVRa}ie zOGAh2s>}fdn zfSoX0sq0dPAR>HLP;v;_P{Bj9x8K3FsL1X#7GqQME28`Q$g(P4nI8nR#}u%D78T@j z)0O$SMP=A&=y?7ZSnC|0vh=Cl1y}{K@yhWjlYor?oTp*CQ8{LqlqcQ%%F388E{}Z+ zP43}WRu)7aadf}3nm$4KmEkv7c{-cdtY2BpX$7#|{~!33v3Q8xp2@Vu?dz~UWk9Qn z{vYB~W^_%+Glm$hT9OS&kKw&t8Hbo}cn=QC%i+FDV7g5PXq?MQvw*v7IM`v>aMlo) z4TtO2_P}u6#@MHPx@+T8KGWStRF>c3ZY(Ox@8rgm>-v>B4`mp0B*QZ2sa=M(!CA=K zsvg2cEJKf6{(L{nKazv2{sVuF_8fl=*Ebv$$n9;jFBKBABC!T#oWr?g^9*y|n{)X3 z)Tvqe?ivdxADqnumquUy;ZlakKfK|XEP8VM!wc{aFR-FP&BU_3*5Zqy4B8dBF5*%b zQAdO4A}+Vz0@!vllc%Nm6T+Db^%1kknn(WOnB-tTis7ha+%j!v9Eo;nUH)=7ek*o6 zUmpa0z7#VAFc#tf2SP8rPhofNl*Z7Bf&fzXAk0>KK0z&3Ou0!gEjDNVC(yjU( z(8RQl_+UIBLo17Y|FvWRD$#v>daf`NU_6yrDkF32A7znNwCUAOVw zaHVZIZsR5J_8GVFKH~PrbmR7od`)=w`Xt3UX2EvR#SfBsbV*VeCp+J9VzI*KpQNc7>Fu`0zC z4dbG_Zo_TKp_i)5PrHaY~)pF!?;$&TsW6#@@4_tOdi zME=Hod@*R_=>o)LhZ0lsOAc`zckKuM|LngY6tNbcNe9y$ztJ2BEv)F@7<;GOaQ$x^ zwZZNjj{hIOg@*#LF_98JnH3+JD3U<0liA(M+Dq`3g zbeWp@?5k*(?%?rh7an)$67loU6MtL>-K3mW2P5O$hTs*;dHT@ZUgqQa=eB13b0fA1 zr;ynMC2GVUe=eJW{C1XHY~H4( z{d3t6v`ZCSPqUGV_3odG#9vNTTM>rCq#w;USJJbrZ?54>uz|VNvw?wLWi|zO7kD-> z#y=Mum?Qe;#&2Vtp9zn7h-0p`c&u1-_6Oy<(n_;GNPFe>V*7$j;>cdPn_RD4vrqSo zUKroQ8NK}c6thtXs`ig(^zu#R^o(8(PGGxGHrd-Q%;k$@>~ne1UB>>Gt6UlTr?_Tk zr`8MY7Z9ftdv7D7$@DdT{PD@dOgur@+}Y>obIm#7#N-M&W7u2dGg0=^?7=~W5^Q2K z^g^wZnxs^<=2y`XjN=@|Og|-P!}PQaVJJD={h8XWGuHL8ABw(< zv`(F^U|YtayV50K8WW5!gt}Chc!$x4?bm~)TaZo5jJP#9?@AUN&UnV)dt1=vRQK4+ zU}jt3ao9HB+({< z{8`trp56{2{HS)VLN9)lS2IZ^K~gFq3`Z(&`xS1>yz=Iqcu0Ai`K_YgtNpSu&zUz7 zE7se6I8(z2G@~4BF3t{xsgLMU3Sb}XAfA*W*jIEIi=pC>eKHUxgMlz6YPHYzINn<8 zNRLrBd)Hm)GO$?bp4$04z;pG1<}5XThgILmm~`L1M&F%IlsHr?5XS|Lslo7n6otVv zBZgL|zD}dq)rz-9sPyl%r@@a^AbbZKA`clgT6`K5bJ|j#m$0Zk!O}Ms$zXi`6L7@S z82<%aNlx%OboEc@W(hV)(9o$$5q<(^k~gr6;`Aq^oR>FGOk95!`qy>Xv@&g}?JU0Z z@P`Edu&HXCJsyFjgq@+L_bvJ2o8R;gmG&wUqWQ1Fx4s!E@v0(;9*{U7tp_fng)5`ZBx4DZIK1NZ!d0SE=~TRG)uV z1_Fx*0|Dm_ieg-+=Xymk_mS*tJdg9nGc`D;Kfzd|RsRl?CD_#}UcU7G*;4>j1wl=z zXQ*8h zW2C`x>ET4>iHLz)XD`9(^hASi(Y1M@vL|PsjL5nIQSByT!VJC=(cf0|#%;)xn@EWY z?X(I`d*4djo67BIO4nLZtrwFLgSWEl*sjC(_zhr=;N3QmZ3DhF_EjpXaB`xukeIkn zQtPbmJI9l2#=4yTcSrC=?}~1O-3SeA`yft3XP+t=ZqH~A&K4v`0aX1tz;yd_VCbx8 z0@3%d0_?)n5TMmn+6%tjLsJ#PkYG4tQHV!Y4Wy62C*2ekx)ct<8>r{D^&U#%7E8h*_0uJ;kcLcHj?E{Cc# zgzzs6*?mKa>2P(Db5zxVKNTfbsnN3Yt^}#Iyav-EXU&PQze%Jll9)L>6d!GBO;lF2 z8P%P{f04%OrG$UdA`>44PjA8#rzwj@A!M&l{&^z9C21im80$nQ(-`$jcpYsU%;8`) z>uA`tWXLA0vmNQolFg!ZOfNnfdP;IR*0Qe)?b|9c74h$jByKiqLAdRQMhG<@tr+e-5#{MRdqLntb0KmcczSgv4#@=gVX8eP85%g#(2^fENtIFbyzZraj z!?ZB?{|KXhk7>P56}a>g?M6H%V8bGZN{Q-lIW;~kb*fz0?I)9VFatBqmR{R*a>$-T z(Y-FOt#2s!@6DrCD95TfllOh z+i^)&Fp!riHz6XK-Je&gF%fZ3~X|n6|x7P6|!p-*dQ!%vFW^E z=XQP{j`X|05H@Cp@Z+*Y|3L{#gX+-#e_7u{`(B9n|BkPF%a9Szs?W}DBMa$e>3rxP zv~PgRPj_E`qwkJ}(^wv~_oTxRvG^iQ!KQcp=qy*`obxra_!7>k=lUCremHd2MtA*v z3qehggY0C5kLpOfc~=arb(aVuq^+1e3DO4Sj@`XZi_H&4#kVyWdxjT)%wC0 z6Iuf)P)$B#-fi$PRsstU6^Z6z$`3VGnj*|jkvXYQ{!Ep~K3&*U1;+@VHH@O=8TTq+ zVMjQ4jshrudr)@-z%c|D5CP<&m17)0rI9VD>M8opMmB>l_$IJML=xyLqAke?MhJgk-?l6m>$2)s02IUul+S1K5XE;3Ry|Zu zlca(%$qDgrk_yT~wu}w$!b9u&B#jS|ID`)vAH52aNSdHCtQlKt|KNO?a&t$BVQ*KL zHQ^5hFZu$YCuHaawuFGlfYv=Op+K=5fT@9Q2C!9HBL!IWbF5VWHa`v6iX6aJw2ffF z5Q7^Mb6@s?l-j_vN$_j}o=rXAIkky0iH>04*+h4@bxp7bH+6%jfn}KoOS6AW!!ykJ z>WNWQFn>1{oWC1xdN&o6hU_c2%o99!Q~niccn8+A~Z2_BdOjAs>6rz+wr_yGL7b1!C*B^d- z__Cfx4kpR`_1+M1*t$#mT=ZX`86(hNX^PepU0^*dUI$b;=*QHCh(^q9Ue_v!)b2{grkuSwBo8kPx&SS&x%A33pk<2Ed)y(MH zksaYf>^}<_s1WDfvp2FoByzA7z1zHb##-42vHn?Ims=~tpOz$VbdweP7T?T^gL=9p z{q&KZeyn_nnzIgAD~m?z#Ux$?6KjfDy;wRG8^(SS*=Dlz2YJ4zdF{7iepD&p_`^m{ z7k_ZNxP-WCb&&{iyF#ldW-U_qb7~-z{IytVSI&y2t1tYN6zYF-JK?jt4|HiZ5bEbi3`EaRk^`H6Ej8B}uFMk{)t#&;ahHtAtJxCl!&Wgqqan4T78{lA239+hH(u7q7sIOm zfkvlI>Lwt25X-BxI05G1>_Dcyu0ORLIX-7HSoJ?u>aLbh7nZbtpR-*?d*Ag6;J|CQ zfLX1CnyI$FfuF{ETu1xbtd&hXZ`1v@U>CE>8#e>Zp#5+O&Y?C%k=0tHTdI>wh35n~!Fx=P)P=_ba4(x4Y)X(JWp0U;)`4{dPZ{0y? zFFn*;Sfa~K7Y?tsYS!+#+`1!tUfZ5=y0q(Q{&^j`oPQplH&$COgr0; z>(9pOD!mTyzB=}-6xA@h@{`<)ZSg5gX|5&2{{_eJOudbjZubTDZmsjA+Kfsz&`ywDCGQW@ny{r#sA( zZun^3;U=~2@Uq^aa_+df4uZ;v+%cnpKhu^4>_%s(kT_9Wtk6JUa-%n&Ds4vVa7{?I z?x4+hS$*;ME_uAsz!X9=D8-8eE$zc@Hy{owy7m(5T zw@8M1DHgUJv*$w-<{C(x)-Rm+g(ylcKGk0D;S$(UB<#R=Q~snmtfq)XWT<|{zI#?>dyeZ*&BDL?->fX zmqFvWgyd(ak~oNIpzi^->eeX8lFy1w#!-%~gD4m@Zo0W7L;@?Vm4=|D`UUDb{6QSx zV$ar`j6>h-0d-?eXK}D@UEe2|#o6u`s*V?ufRZK{4S|{DJ6> z%PQ&)Zl8mn$XZ@io|1<(QQttc{j#dUR=#x18fvYqIi*juy~4jjs_YHzKt7!yYh1a(Dy3`7PvxXMMuNlMI(` z=UoHAkv^f1oT&9u>eP_`3A8jGS(bcT<;qva(0^@`teftHVR!v&6d=1Y_2Yp4hodTJhana^EnQ*u%f?K zuT;@WZpi!vGD3obiEFzmE*aCGrLSf)q3au+FVhc&@M6MM648KjW`SIGE3{4@Qagfv zJgx|VGyWhEVe1a$`;y5KSmXL%#l0kKjYns5(rvtzHfj?ecpPgUTau0L<7qs~r5?}>OUqpc& zDtqScli0ZyxcO$6S1cLjcUmdJ_-46kcLIwB#H5&@i*ri);4knNi(kl`N0!by>k0dP zx4PLwn%?PK_hjRe!TyT6w?Ox2>MEAh`2DGxrgw_mmdrk@?#Xt(+wUvs&dchatXneG z-z|Aws)`TB#V^^Hk-3aDvboxt)pnw_^4V(Zl9ILLX0$tW-<(I<^!q;5R_eI#J|HmO zlkX4tY4+Wm8>4~#Qs4$D&klv3><1As5F2}8F}6FzAHI92g6+d>NalLY=NULZ>fw8G z3(5fz>LBxmi~c3*w~i)_ly%Xa98d8P z>7eV`rg-`{#BPg}kO`xhJ-bAkOItXDAbJ4Xtyjq93=vhso?0QIX~K}bef_}}m?)pG zW_~cX!KxQOp|khwXF~CN+n_LXr)KP*-TJZF-k;$=5ZZ+k^Zexct4!d6R^^_@z)oNC zs=s{%GMUUqt+t;cYBv-6XSGU6C+;wbNs#=s(zjHp5kBV!f{j84C<1AG(h!QTneDnp z(DUEBJ@@+icur?`lO|k>$M^n%Szi2)_;>X^7`Ko4@~BA}UE5i^7N}4cQ9)@Wl1d9n!MiJ zZ%;n(IV=}0JRka#YE=BJXm^Iv8EbMxQrYu(W=OH7i)B6Il{&3{wYzi|E^y%)J1 zW&XRJ={EoMDh+EF=aPST%bovwH2=>|&wo8_<0&)$%{}w~0?q$nz0d!v;nmIvCcagi z{4C~Kh|RldPRFNuVnOZ6f30Bj%_4^=;<~_>W;mBnuWs-w$9IgRr@Bc`?3Oe+h-6w9 zKS{+|n&Zlw()A5XCMx{k_Uz(dY;O&La=tah_&gtlDw$}k;{9D74WmIC& z;0zbVhGa{Y?l8K(;JO*kua(-|!bH0UO~6YUrh_W~6UhxEpvh9WeM4zk3XV6J!&ZZb z+C4_dwl{h5nSFd2d8pPxcmq=k`;tU{Axr#mfyd#6d>CDHj_7)9I83Hd2JXD`=HO$U z33(;Uro3FRMY;Z9q8AJLPzhw!V{0)fgiC|%k9AZJZC%59sch2hq}Rn{aN7eXI$h? zEgf5VZFM>()1%oTc=GO<8rl329K~!LFNuc%$bo-V>uML=Ro&sATsQFOZGVkDRBqd! z;W^uO!|$c{QwILW(Edx(?LRJf zS@F@w;ESK?J_Zwb&W=HM{5f*_r)S&$mG1a|V;cWK9_H`YHcUTy+oRUe+y0Z&ySM#2 zJZIZ}DE=I|{ll{DKbHAxAH5HcpK$a(ymVUkK0L{Dwh!Ime+>A4Y3*0>yIMn6e8F@m zG-zlq6Bu7NZvsF}p_f(J39J{%ykltm={*{Me7DA@*R#Mi;sB0L|EcvD>1Y12(a+0{ zKRW!s_{r|@pTP6s;D2QNX@xO3#`Q}{kB&ZdMMrP`k3Z49`Ej1J&F`^(9RvQ1G5GTl zLjnFAaRDF-&=Cpf+5tyz{i~;TZ~fCeXIr0(KLN9!Ui9t~H4Kq-OO**Q)KaxtO^wJZ z_qU)e7MUS?Q^0->u?y$O4R>5YypQkjqq}CY&g(WZaEy5;t~LC>*3- zWy!sOKo4%VQD-FX>56vc-+dX9V;>C<-8hQFgPObEBE$sof%YGF_k&1~Kf{;cjYand z{hPaXNZCQe3rF^98F|~HgZ>gWm$92vgKe}p!E_jb$U{!VRJ_|CjJ{tSEPT?6jp0SS zAU>s!J?+mLzj&}8U0i0ZtgtA2xpSJy;5Na!?*u(1PKpa%yEmiNc^{!oy0O-MZ|Es; zQs~f>jZQ1y5XHkQIBU|kjm{s^w{6ZZxwTfgDSpg7yoR;p`?^scJM*>Ck@Ct-+2-7p zPI-%)veCIA{pM=-O`8+Z4F;@1JAfs@6~)08zc3;-3rhjU@`DR%z8SCjvr^cBi5R`w zfgon&u!{^nIpSF8+PDd0;>&f2rzYdJ#M7d4JB+XBWdp;xwsIZKm97LTxGv*5nd|Z; zfoo7j2zhhmWr<5?yvhD@?kO)?>ZIa<+qV>d^X*f_MPXE~( zs6G-4R<~6BN!c+cBH21R^9wmdIVIYs3b#exu^6JjoZ`e_3t+KwYQb zx=#TKc-dM-RJLgQ!qArXNMTD={f2qP(WZWK!6|#L_DSbGQeka$l0RghfNe!^-wSw% z8k@&O0EE52{$Lf(E~fS&qLBEl2dcKp1^{!E7hHL_EYM4_+PdN(wp}N29!}o~N4rop zrB>R{MBiFSt#eP)@vQc9a`E5@=wNwdwN)0ja6cZQEBL#8iWIK#J(WZGOmCOzJ@`vq^B)Q z#uqe?FYH+eb}p)WZ#Z71Z-p$l`Z))!dqyD+4g)1!mH z2@yUzhCGfuvZC_Fb$(gs^Ie*E2Kt@rFCEIoiZ#>fx(^Dh*y(2d;@Cq|7$G}{08a|* z3?_)$A}^{*kc1$ZIwjv)d1v3Iw~7m2K*`Yb)-Vi43xb6mLF<273!Hr@D>!rlbrR|= z2*YPZTX6Rb7=>D>vHd1Sa9dUV zvzGk|on*>~x>Qn#e%G#Zv?zlkMb-A?{?0RMxWYx#f@o)+b?+mTg@4mW!Ne6XFZ5&_ zK=oTIugH`8Z*gH$lT(m7-<*PJI^V~-_diUQrUP`I4X^2Z+-EDHy|(F{;=rS)1Gf=- z9Rnow)?_S3AMZk~gN!J3O-7SVK0{;-nrP9GqORc3W)~!Z)sk~L&uHv)QrWnIF!V6g z)k=KC##E^%KkfzxkKzdetp+CdGI%J>(JeJmR@gp)AjTpxR~V44BYw`pnl)9JHmq*w z=fuE-bZ4dHBapbUr%FtL3Y!+!jrLg&HU=IATYOHK&)EU3nc3@LE~T<=<2RT}dLO^P z(AFN~r)s_Vo9aX8=Y~N}JGC0T9G27C=zj3B&K;VMiuEgrr*{o zcZ>s%QoGNYs`bemTmkAR; z9ugXLENJVTZc0w0J^+Pq9Uh>XFHRq=OgR8Oz)RDZ4CYxYArzwg&hz@F`M}|xYWSyU zIBq8WjZ^iZ^kba0^0X7NWX!XEunsbUeP45+{xvIh7TGH8mmwj+Ew6_}LSD9P%>aXf zYa=Kp*9#CH`aLqp=_?3jrZnLFlF-0__b{}8459@^IT_JYpV>>#4Amg)DXCurTJE~& z$Q;1n`p_JNGr0bMTlL38AZH(NHU*y8v%safUj(8-ca_q@PhP}PtTn6KmYpN^2I@Yjz)k0c=zcDd7lMUtM<8v`PM@=ZKUoYpLTW=! zeoSh2Y@F57@7ryU3`$L4{eVFX`k$PT2>T#%>;tYYI6_gdLB7;g*e0GRiWu7WY{U

OiKl~3~Q5eSMq((XTub1zUl1usaM}D$ip~w1~Rc7v!U}Qsh_&Rs=_;<8A zpL|rKN}Q?a@k_M2?~-DDkB{N2gT@SZ5I+w}4yv9B+Bjbb7PoT}p?CO??)PY`H*@jK z;DOO*_79j~AL}FQ`j&MW-n#d+`p1uEFkP z@t4Rk(-{2k`WrKhQL~YnnZ)4_^eV+sa1WIY;CLAaBFI4qxQ5o8i}f=LB~eQ0sI*}J zUNImZ=Os{VY4vTY){+Z@?t{Kb!L@iL3L!E(V`X_5`9xeJ+O@h(q=Cshwfg7zMMJj# zjBJ)exfK!c+^GHFMQWOEFwkgwwYvXePs(Pi?v16r0M%L`mdG-1%M=}mG+(T(U#f^8 zx}SO3Dzf}IA`xds_w{MoH>hd9i19hca}KXOuC_~c%czl{8d*$%Xk$jxK8T=2KWz2m zEwgD~Zq8BtxKX_+y6;1_9mWH)Wz#B7MH8C#$lVM(M7zNiI#-E(B3!UsUBs1z=6CNC zYa*?FI<Z5cm@tQiit>Ucg6{3g=&L=ZUUfU7A6YNC0 zE)8Fd@|Q*JEhe?S2ecd95}?6X5NBzy?nkhPxSTqIv+?Ry;<#K}ShtuY^by+nVwp*( z-tKej$E}<7Kfl@ysd;ocub%YnkeWtgD3PVDzdtpR*bgZL=Nu#aJ$cqG6YJ$RI9&7uqMZl>*-ZMZgygd08e<4M37S+=$G|&4T>~EXyE!a67xK*_B?g7Ym9R&Q!%#Cc+ZJ zy#z6O8cHt-YxPg?wBa*;A=EQ~a>P)chz8#n{~LdXE3`;n@me*;|Es*-BCoT#kzxF! z@;Xyq1DunLua(y_d7Ub+cgSmz`YL6wlh=`|th`R;)mAH5Kt8d8q$@}YpOHd~S+odn z;?R$Pd61{y&8GQkLSlQlGp&Uy!*L6!li_5_^=9oCrq*ITfIku|m`uxCn3f`qbnRYJ z5zyZx<=UR|NLObys*?3nGAYo9)lEqxvLt$Q+R!9VyDA9~0?rGcvAK!Tv9& z=Fdf2ixO0kMHREC!dA1a5?fSFZ zdW#CI?dGF@*CH8W54nc-zdzdan2{si}A5vczQYxrCxH~ zqjCx0a7Qa>7hMTR)-b-d@zuJ7AM@nLDEn(aekZ|ECy9H^Y-%Uh*6uG-qgYjZPG|V? zukwX?@3KCI7D%|TXI8Vt&NrH5OTK@%`}@wg>`+b@T2~$r?|Y>g`Pyf^ESJJ>bdh47 z_Z_hi2%@}ZLnwUbWtwGkx0X{@wyfTDm8-ZuEl3`(Sd5bmS<*=V9M7tHn!qwvTYvSp2rQumOdut!H1!Zxnv%kb-`*%_b+duKTz|}v zuHb=(6c8$m-$&QEg%@b&BAH_qu|>MwO?>g?cgl8hD`dKcRdHR=7~E%?k!8SB>9lvl zhQBH-c^m)YBV|LxPR6@=D8Gtmr&S$I!5eU={Oc%5+=|?a`JqQ08V0lqIl@L4{I)DD zLxWnyS90miAZww8P3~jd_sFTE=b@qTU(x#@I_e1 zuFxLcA?XA+FW#}_yz*F`LA1z3xn5@vh~W{{gRi)&_G7ERZ)GAvX4~gTTGZr{b{ScX(WVQF-dK3_koLl^fcsTYhIVVg2DRH{;2Uw7 zh@U6M3M``KsBhSxft#wea{l07$gn-{h8uc0kX^6tk>4Z0veCsH;#&oN+(2z>q{03oi@-u!+vVyn=R0fRjYXSj!Od)YktPi?D#nL`B z??Bk{=&#W5pYh8l{@s?7q2F5Xv!(6T3%(cu2=n>^dq*p-B;uTcOgyC3li^a)Rn!4L zng?^kry}GZ&s)-`=)L%#74SRgpdI}Yaw~cR8g$9pjpZ?}87EnurVvOHxZuT$?5VSpgot`j=Y|}897yHOkr{BV0~=tYdq8* zJteDSm#mJQdA=c>A2%uyYFR@)?XstpItk= zjIFf_%h3PNU>Ws3=;$8-S-x0SHp>)e&$9g&FJVpsaMs6a3x9lnYZDWRGdLVn57k^> zxr%b!fQ^AS>v%gR_k9r`HhYX4e;GN^n_rdL@4<`e-V5{X_&i+S#WQ9bjzQ2&q4@ZY zMEBFU{5)9p82~CHaDtPjmZ{lEHIJ1W-mCN+=$^3lR=N$bz7XgFF``Uh9gi1Cpm=Ld zJ|cYfyzoFE!f0O3JlJKkW>gn7dR}5OAct?Q@vBUsX&ykYbYQL?&aYnr{m`0Tk>=mR zj6^7r*K;DxA9*k|w`;`nd2tS6_L~4y^QV@nvL3S=a+-^FB|?@QLvi9-)Y#AmSRGpo z+mAz8UvPFRjzHhRL}jS9n=~|_PNj@T z0csyG#k?k)Aue@Rl~iO~AGB~lZoMk%7vH3**)=1S21m|ua@{syd4^|G$P92AiXV$wjw7 z&XH{PD({X4fEmRu7v6>)dPmJ*A{*u>npPhB5`M5`L`y}s+sfiEgDj^~jJr@Q-AdX5 z#E1|fE@Ons5F3$-Jbik;^-hu^4+$v-V>jga*;tX&T^=dP4~KL8iOoU%hCEgyE0YC4 zzAg&J23D-f8k*H!(zoVTCqrFh=rV16qdGB{Ory|z4vlPXkDSQiZdAJxE_7SWx-3Io zJ;r*+u5AF?o}u%?tXQg{fc_3Fp*qK}`|=BnUJSW>jzJO7lh&*^^Vjd?FVK{sf);BIU&T65n#JlV=I^BEI zipy2}uw1=&}NS zen1{MpJLTRaakcUUZXFT>5GXk4JWN~;wnNxOpdvkfF#Gf9YT{lO`eq8Z3fPiT7qPi zn2|U$%pseY^-U*Ru#IQFwbU1zQy?R-i0+Ij!Szawm1Ce1-+PBtVduDb6(_+)|4&t) zOU=>@*=lIsV^FQn2sP%J&y(44_!^OdqZ=*uS!|tDz?eTHJtXshu6EG%7qXH2MNoSh zx*j2O8oCrT0Gou)0lFJqP9rI{_sFCr3o1grO?rhqoE{o&TTICxS~vB$AyjG$&vO|M4pwL z#{X@g}J34eVe{T-wt1mZOe)Sw{ zdDOGoDyG&1TbU)fm3V4C`G{gMOXYXTa`_$lex<&kZQaUeBi2w=BxLngPp)C}ZUbD` zI(W18!rbk1ZsT~ko0@AE_wk=owGKnY#KF*5dQw~97Q#M0^3 zd!D~ax8jO(8p}73-R!*=J;)2*#)V?CgCap#(5hhbD(`0NF~QSF!P9rB#3{R-HP%re zq@OS+Q3|(AOO79&clxGRT!LfsU_tMN=J?UVG1-mMHCHA{1&)2od>UIXqWySLN|$?M z_l^k`eo}K28NVkq1#@ZpOnXu}|sh>OWjM9(3n+Dz{veW8M#VjmfXMFHsA*&XvKFQmXl$Y=D zH=?f^T&=8DfS0BUM(;!ugOG)KYbCFhtCq9b8dJM%!ikz zWBpRF?xHDLJO%Q;f%UC21B58G`c5G~TW0(w=KnS>Nh&?NrBfcv+uJz@Rf|)|{-IQ9 zl>WO6o2Wg^8_Di8C-h+>d^vRs!oA;u@Czse!snzTe1)u{Ho~omvhIlv1+Nb4lH`C5 zq2Un%bp~I|Jxo}cQ6WQmsjb+njPHJUJlDkiKwGtm8i@G3vOrp3Tc)^o& z98re-!1U~zzU+^@THSkYIkFI=SqB+sZ_K1QU|DwsYW*7(7+U>*l1)Il4AG_pceY>f z?%{Ag)=%lhlzESc0h21SwdhAw5Gipl*6QDps$>^bk>@$m>JFWiY~>2oOIj(S6+A&{ z-1CB4KSV4aivcvH`h~D|!jtiD_8Td$5$B(-wzq5>mlGX-0NUZy^?9B18|ktA`R_K}oLN9A=yi(K)jC zFiI;)7JlElVtnWA5!zOfNb(e2C7H{)$)jUexI;x*bBC7J!nf57@IE9Kp+-J>I(hVl*(1% z)WUKp!%O@g))2(h!CcSC7!QIP7;L0^bN@|JQz+Y`P-zWF4RcMlAOaOk503Wjli!}R zOn&Uxj7!!j)M0MkbN)v_v0ow~T%1JR`N^sI#AQ(E4GJ2qewGY{nt-2xNM{ADk+Hr| zp=*OXTv@ql!HdaSMtkcxt33$aUQRX6%E|3HvU2uv>O{8dX5jO)<8pLlx3MzShB>%1 zHOZfn@Bht7jt%efC(Xg8J0#&KN=rrZ7=OqJgP)5+@<2HXW@KyUCTuzG675v1_rp?J%a_4=-ljJ)1!`?*b%e z!LOu{GxIFJL1wXsZEkc5}D0KrRn6PP5 z0Bu%XloUp;p*>kFFDF|PZ6XtvOAe}bnU^~`@+1d!7qSNys|t=OvFQM`XxH_jtD;>u zaCT4rWF><8v*@APN|>Kro9b2@MY?hCia*agj0{6WQQo|y7|-uf&np3PDvRf{PL*1n zxOB*9BsuJ#0Fq|bF~oI>!^ZoO11oCOTD#37I3R(9_+tVU?~Aw|Djm9cg~=|??1qkL z6)N+5dh1{i+iNTl62Z{8WNAUre4#5cxy*r-78D*-5}B+WUPN$7`9M+}T9h&e#w1(( zV6I)}FOoy?-aqgwF+D&2A3RS-$J5+VXYXXj(!Vs6ghZzPPx?XSzk`mxAybEG$LYo_ zX)2ABP&TFLR*;m8V*JChM^K=@O2%O9`=ihiN=~zc z(Q}-_#8TJabg)2ajBLTJ5rJ(D@95A(?Pi~=dYqH^df~@>TS`)#CE7g-xxlLN)cWq?z@!=3sEYs%#ld8av+MX3z+e9DaGnx?^n`3;Gbo)UfJ@Dlfq0Mm-+c@)d(gViHgv<{GAiN zBAL@i&OtI2WinY@*I-vrMDh`VlEuSi{+vatTX2-=(T#W|5aoo zr6HYg6&nt%?pM$?^{khA6jl}}K(zV_XB@eJZyg&4t*%z(q{cI=Abx!BQ2!lW7b<`E03D| zO4+m(<2jKhUWGMtv2-oD&~H@A%>xyAl`N?F9}5VLq5E00K*rWkm9nOO8{?a91p4Xt z?*LeJoZ(9yyTot3TBu!hEtF5L#WGH!@iC|HMNx4$DVxgaO=_T1RcuC)o@hkXTdEdZ zsJ6SdbEQqS;C9VQ*4Eg~@ZHurk1=&qwCfD*(cjC&HTs*vbLVHR#H=FGk*rGAk3*x9 zjI5m{S9s`YK zwD8jVDH`_+Cv-Z7on-0Q^OMjF?}p|hpa{gSP^!K~L3_Ffs* zo;;=)!By9Ab+)+Y@ttY9BiWj8^|xt_M&AOP-{9)MoarD|tDi5_l*ZL($-4uzpzy(w zNud9wyO3P^1HNWy^`>H-4wZ{x3`JH+KYK$C@!j$jR5$E{m43>Lcr=s168AlnYw?}k zr9;x#bu#p7P5gRB3c@B~Jyh~C%dazDe^A`W)-PbP1D2CWJH#9M1#qUgXcA}Y>`|7M z&xFlJ7TJJ{E zsZYohjvjZjo3t^j)tLgdQJSBsA(tA2n%F=e zirc%Krd!EMeFRT3Z(^yn{=mIJT*;Am78Gyf-wx*~hFkLfkN(}xclq~6%F92gC&GB9 z7^vDI*b%ryBdp>YYIUoHq+@bO2Za^4*9?NV=5j}?qqurOoggRV%UHfl52G1g44bVX z^V>DoM@ot+!n4I zkR>E5^9eyWEQ~pM*!*F4OYA~1`@_~vY&vlh4e0kGm)()a6IJ=t0-j@h8_@l^%06EcHl*y3tM;Eh> zw_2SVKBsp47YNNHrr7{pVJ}2R>6NfMsqh_Jkg5MzIuW~@o_!WV&$M3!+xpaeiO<+^ zOOwsyuzI7RWhG+SUv1t)D|Y1y+Iso^a}+Ts0&Rj64*D4FfT$lc9^Semj-JBS&X`qH z?hMV>6i#u_n(X*Cc~6@vvt>NhY#h^FsI9w@tdMb0Q>lJ_7Am048)$kXD5RgCnuq*? zIJ7=5xZ|tSDwn1Aa>=MB&8y^65QXoRIaq^kgx9t1@U2N;M5J0fjQk9M%lx^jHC%4% zX<@$(4^kL)L@`lNhuF*q>t`tzsAR~o9x>8IMH1efWP)eF=n$RvVl09 z31a%ITOJ196TT$LOq1vkj(OqGBw|~pq*{>phu~MvPlq9lIPEq|s#=ep=a;NUzvs!B zut%%=n0dr6DDAh3MS%duXG8}p54OU|vq3ZW9R^G(uetqDs*43ND&PWcJMxjaX{W8< zxLqm{IsX>bCU+Uv6O)qciXxo-uTgrp%t&&$;x~%ynEZO3&>mq!=~}!*K7fLAA|(SC zgomV10Ca2UTD*X~aHw#P75e-XQl9muy1%)*2_h$Y?)V+Uj#s9+lH_-KE3KI`^mwZf0 zIx|=Z?e4RZ=&I_@bgyIrphY583CGpyZQ0VA4*bL+_P;4|6D@RzV;o1hf1$WSIGV|5*Bq^`KLA9# zuF>kmfh^iJ*Y?v$fz&K0HcEeRb`rDSqv80!cOzb_`xj})C|#cH8=J+kNsVy)3bKEr z8;>NjnuEK$A=cLE*mfgC*lX3n@>E~lk_l}8CNq^Jk^(Jg_2(ngrQkkOUX0RysY-rF zCGoEiDZc?tS1QjV-3`flmV`!D+GkRkZcAng*9s)0X;xs2~&~FDPar$#AXAOEzy3n64h)OoZ47UC)J^zP8$PcPr=G}JwI?4aZ19ldBJS#CM$ z@?D;imi(Q3UgnfZ|CTJ9o|akwol}A_5=I2Gw$P2-gsMzPI3hh_p7ExL+M~tbWf5Yw zV)_AnvPuL@47T&ygvEBH;pU{RwkF-4ix4rdyo9Ns1Ds z9t&EB9@D@UL(JoEBjpV__)X&?pMzfe1}-8Nt%jxq!GEHaKx~pycV;usvAuZGb7Tt) zRqS798W)4>NvIImkKB#fc16DQkS7AR%@k&?PvE7+5>b5!Rlh_%`UziBWAP;~Y@2L* zsS^{Ec4fPzG`oU;KADXUmES8i;ZEgKRpsVa&r`EqWm9@B&r^%OIsOaMQX^`p`bzH^ ziN{(L3lx&w(hMMBZ6al2GC=L%)6wnDF zI27;--B(rgcdGC>uLlY~8c6)3?yOTBNSL&a48`@o<4DI^RB_gM> znff3ehmi;h)B)pjB5}Xs4Df!w#D^+wCiZuqn~%KoZT5HZ2ZRF}r5yugAo{m%S4PJ! z9umaRcC?$_dHNJsd0+z_v?IY$Dw-#yo|#2eo((c<)iKE2c5|1=%SWn2{4XXM?}6OM>U%0?^l|{rc6KieT|az`+$J1W3%i+^=^L0H9|4OrxZ? zzxb#Y{ZkGT%UJ53y6WPLDHAVtb7ev3gJ9vlp!<{;I}t81wkik)#BYl!yI&Ig#V54r z0)Zp3T`mcZ{)9Sa7+F{Pi`i4u^5R*QJF$FXu0$5e#4-W5#w7d?&j;f8InpsFqO$RO zL72_8%pr`BFJ|9FN=%{#^gX*}r?>M7RoA5S$1K0*}A(@-yYphP>pGB<*|fAc_|snGn{xC z9$bmVr~JDlPRoq$@6)s&Pvcae4NuN7oOqSU?g5T044$IR8N^!1*^Zws{%z6b%%=Us z)aVk|X2aiti?Q-BZh8@K<6~vok6X*?wudi|-$Lg^1vJE8e5B@Fd?c>KCnDBIX37!k z439D(!f#%b@$~=|J1kIv z9k}eotNcvy@gklLxxBWgsf4U)B(bx)hyqzs+zIT!pc2<1r!{A%wSiA|Schh7h`n=_ z6HHPiz(Y>EPJ{=n2d@FMPB4x7+5DElSb{q6kf3zl>`*ak_}Sqlcph5)!=vockd(7M ztERe3=aU+ZS7Pgo=Eakj1}LxyB{+Rl(c1{qLl%GzY-DLK3La-aJP6n^8@?52EJ3nAN~3$*q1!i2Q-#ylQPd-Ajw9 z`?-UZ50yIiIpVv!gx`eI(&`rRgL%+T#e2d}tCFM7`TQa73CVMq9(yONa8KiTtzdfW zb;3a6p$NzO4yn^K{n5=?$p}JeTyKZ7*er9o{-@wQUeBNIDPqr{ z_!|Q6s;QYWAEzN^B2ji6;O zXnE7C6B}cjXXW1AiQ!1Y&6@~7F%&1(X0LGrBV;?YdX4*bd!nZ_(p$XQA9}Oj4&(58 zHL){}Us!u4f!^M(!56VTZPS)%rbllKdnYrJ5iK2%+y}DIs+47A1scw#nP$V`##5e%r>P6d>SE@hL)jbGyor zUy;$g>mz1~F=V+E;`rr8`}JC0@$56F^BQ!27$`nMR4{nkkVPurQS)*>Fm>-~b*xHM zl!pmhrILh%h&W{4v`^)@U2@1G%Lk^gS!pNFeZ@((a;avXyk#ac9Wsa8nYvn>WUEYnVPcfh2j8)xMC_OkVS?R&o+4e(9 z4{i|+HB+FpJxm>qyLh(MU-@VIkP!08LnYGT1*CL@WtKvgPryq#iYDeRg;KeukjVamM zOY`&2!o0|2|I;oF^4wKct^sjX2ghk9v0)pl11dUfuMNMWX`QPIjb2}RC1e}k_j-nM z;`Qm}7zCCVNEO!Ac+VEFvt(Pp+x*#uNms5=ZTw+rK}Wye>-*hxt^Ip-8RNHtIpVVy zP&E}zW34PlVK)AFGsz&zyzLzHip$&G;e0k;^G?=WwMws$1&IBAS9- zp^oL@+vd|BsE>70$N`4ChQ~`gRq>mL^6}E-_2&!i`$}V1%9%Wo`B!8s+pMbiGfzuo ze$Kgt;JFfl^1sTWGl)7=a�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

av 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%0DG-ro1mtgch0VU-#SU^Gvi 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 zΞ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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/jh2/_hazmat.pyi b/.venv/lib/python3.9/site-packages/jh2/_hazmat.pyi new file mode 100644 index 0000000..e71d0f4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/_hazmat.pyi @@ -0,0 +1,21 @@ +from __future__ import annotations + +class Encoder: + def __init__(self) -> None: ... + @property + def header_table_size(self) -> int: ... + @header_table_size.setter + def header_table_size(self, value) -> None: ... + def encode( + self, headers: list[tuple[bytes, bytes]], huffman: bool = True + ) -> bytes: ... + +class Decoder: + def __init__(self) -> None: ... + @property + def header_table_size(self) -> int: ... + @header_table_size.setter + def header_table_size(self, value: int) -> None: ... + @property + def max_header_list_size(self) -> int: ... + def decode(self, data: bytes, raw: bool) -> list[tuple[bytes, bytes]]: ... diff --git a/.venv/lib/python3.9/site-packages/jh2/config.py b/.venv/lib/python3.9/site-packages/jh2/config.py new file mode 100644 index 0000000..ba0b8e9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/config.py @@ -0,0 +1,203 @@ +""" +h2/config +~~~~~~~~~ + +Objects for controlling the configuration of the HTTP/2 stack. +""" + +from __future__ import annotations + +import sys + + +class _BooleanConfigOption: + """ + Descriptor for handling a boolean config option. This will block + attempts to set boolean config options to non-bools. + """ + + def __init__(self, name): + self.name = name + self.attr_name = "_%s" % self.name + + def __get__(self, instance, owner): + return getattr(instance, self.attr_name) + + def __set__(self, instance, value): + if not isinstance(value, bool): + raise ValueError("%s must be a bool" % self.name) + setattr(instance, self.attr_name, value) + + +class DummyLogger: + """ + A Logger object that does not actual logging, hence a DummyLogger. + + For the class the log operation is merely a no-op. The intent is to avoid + conditionals being sprinkled throughout the h2 code for calls to + logging functions when no logger is passed into the corresponding object. + """ + + def __init__(self, *vargs): + pass + + def debug(self, *vargs, **kwargs): + """ + No-op logging. Only level needed for now. + """ + pass + + def trace(self, *vargs, **kwargs): + """ + No-op logging. Only level needed for now. + """ + pass + + +class OutputLogger: + """ + A Logger object that prints to stderr or any other file-like object. + + This class is provided for convenience and not part of the stable API. + + :param file: A file-like object passed to the print function. + Defaults to ``sys.stderr``. + :param trace: Enables trace-level output. Defaults to ``False``. + """ + + def __init__(self, file=None, trace_level=False): + super().__init__() + self.file = file or sys.stderr + self.trace_level = trace_level + + def debug(self, fmtstr, *args): + print(f"h2 (debug): {fmtstr % args}", file=self.file) + + def trace(self, fmtstr, *args): + if self.trace_level: + print(f"h2 (trace): {fmtstr % args}", file=self.file) + + +class H2Configuration: + """ + An object that controls the way a single HTTP/2 connection behaves. + + This object allows the users to customize behaviour. In particular, it + allows users to enable or disable optional features, or to otherwise handle + various unusual behaviours. + + This object has very little behaviour of its own: it mostly just ensures + that configuration is self-consistent. + + :param client_side: Whether this object is to be used on the client side of + a connection, or on the server side. Affects the logic used by the + state machine, the default settings values, the allowable stream IDs, + and several other properties. Defaults to ``True``. + :type client_side: ``bool`` + + :param header_encoding: Controls whether the headers emitted by this object + in events are transparently decoded to ``unicode`` strings, and what + encoding is used to do that decoding. This defaults to ``None``, + meaning that headers will be returned as bytes. To automatically + decode headers (that is, to return them as unicode strings), this can + be set to the string name of any encoding, e.g. ``'utf-8'``. + + .. versionchanged:: 3.0.0 + Changed default value from ``'utf-8'`` to ``None`` + + :type header_encoding: ``str``, ``False``, or ``None`` + + :param validate_outbound_headers: Controls whether the headers emitted + by this object are validated against the rules in RFC 7540. + Disabling this setting will cause outbound header validation to + be skipped, and allow the object to emit headers that may be illegal + according to RFC 7540. Defaults to ``True``. + :type validate_outbound_headers: ``bool`` + + :param normalize_outbound_headers: Controls whether the headers emitted + by this object are normalized before sending. Disabling this setting + will cause outbound header normalization to be skipped, and allow + the object to emit headers that may be illegal according to + RFC 7540. Defaults to ``True``. + :type normalize_outbound_headers: ``bool`` + + :param split_outbound_cookies: Controls whether the outbound cookie + headers are split before sending or not. According to RFC 7540 + - 8.1.2.5 the outbound header cookie headers may be split to improve + headers compression. Default is ``False``. + :type split_outbound_cookies: ``bool`` + + :param validate_inbound_headers: Controls whether the headers received + by this object are validated against the rules in RFC 7540. + Disabling this setting will cause inbound header validation to + be skipped, and allow the object to receive headers that may be illegal + according to RFC 7540. Defaults to ``True``. + :type validate_inbound_headers: ``bool`` + + :param normalize_inbound_headers: Controls whether the headers received by + this object are normalized according to the rules of RFC 7540. + Disabling this setting may lead to h2 emitting header blocks that + some RFCs forbid, e.g. with multiple cookie fields. + + .. versionadded:: 3.0.0 + + :type normalize_inbound_headers: ``bool`` + + :param logger: A logger that conforms to the requirements for this module, + those being no I/O and no context switches, which is needed in order + to run in asynchronous operation. + + .. versionadded:: 2.6.0 + + :type logger: ``logging.Logger`` + """ + + client_side = _BooleanConfigOption("client_side") + validate_outbound_headers = _BooleanConfigOption("validate_outbound_headers") + normalize_outbound_headers = _BooleanConfigOption("normalize_outbound_headers") + split_outbound_cookies = _BooleanConfigOption("split_outbound_cookies") + validate_inbound_headers = _BooleanConfigOption("validate_inbound_headers") + normalize_inbound_headers = _BooleanConfigOption("normalize_inbound_headers") + + def __init__( + self, + client_side=True, + header_encoding=None, + validate_outbound_headers=True, + normalize_outbound_headers=True, + split_outbound_cookies=False, + validate_inbound_headers=True, + normalize_inbound_headers=True, + logger=None, + ): + self.client_side = client_side + self.header_encoding = header_encoding + self.validate_outbound_headers = validate_outbound_headers + self.normalize_outbound_headers = normalize_outbound_headers + self.split_outbound_cookies = split_outbound_cookies + self.validate_inbound_headers = validate_inbound_headers + self.normalize_inbound_headers = normalize_inbound_headers + self.logger = logger or DummyLogger(__name__) + + @property + def header_encoding(self): + """ + Controls whether the headers emitted by this object in events are + transparently decoded to ``unicode`` strings, and what encoding is used + to do that decoding. This defaults to ``None``, meaning that headers + will be returned as bytes. To automatically decode headers (that is, to + return them as unicode strings), this can be set to the string name of + any encoding, e.g. ``'utf-8'``. + """ + return self._header_encoding + + @header_encoding.setter + def header_encoding(self, value): + """ + Enforces constraints on the value of header encoding. + """ + if not isinstance(value, (bool, str, type(None))): + raise ValueError("header_encoding must be bool, string, or None") + if value is True: + raise ValueError("header_encoding cannot be True") + self._header_encoding = value diff --git a/.venv/lib/python3.9/site-packages/jh2/connection.py b/.venv/lib/python3.9/site-packages/jh2/connection.py new file mode 100644 index 0000000..cc6823f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/connection.py @@ -0,0 +1,2131 @@ +""" +h2/connection +~~~~~~~~~~~~~ + +An implementation of a HTTP/2 connection. +""" + +from __future__ import annotations + +import base64 +from enum import Enum, IntEnum + +from .config import H2Configuration +from .errors import ErrorCodes, _error_code_from_int +from .events import ( + AlternativeServiceAvailable, + ConnectionTerminated, + PingAckReceived, + PingReceived, + PriorityUpdated, + RemoteSettingsChanged, + SettingsAcknowledged, + UnknownFrameReceived, + WindowUpdated, +) +from .exceptions import ( + DenialOfServiceError, + FlowControlError, + FrameTooLargeError, + NoAvailableStreamIDError, + NoSuchStreamError, + ProtocolError, + RFC1122Error, + StreamClosedError, + StreamIDTooLowError, + TooManyStreamsError, +) +from .frame_buffer import FrameBuffer +from .hpack import HeaderTuple, NeverIndexedHeaderTuple + +try: + # We try to load optimized hpack encoder and decoder (rust module) + # for the sake of speed... + from ._hazmat import Decoder, Encoder, HPACKError, OversizedHeaderListError + + ALTERNATIVE_HPACK = True +except ImportError: + from .hpack.exceptions import HPACKError, OversizedHeaderListError + from .hpack.hpack import Decoder, Encoder + + ALTERNATIVE_HPACK = False + +from .hyperframe.exceptions import InvalidPaddingError +from .hyperframe.frame import ( + AltSvcFrame, + ContinuationFrame, + DataFrame, + ExtensionFrame, + GoAwayFrame, + HeadersFrame, + PingFrame, + PriorityFrame, + PushPromiseFrame, + RstStreamFrame, + SettingsFrame, + WindowUpdateFrame, +) +from .settings import SettingCodes, Settings +from .stream import H2Stream, StreamClosedBy +from .utilities import SizeLimitDict, guard_increment_window +from .windows import WindowManager + + +class ConnectionState(Enum): + IDLE = 0 + CLIENT_OPEN = 1 + SERVER_OPEN = 2 + CLOSED = 3 + + +class ConnectionInputs(Enum): + SEND_HEADERS = 0 + SEND_PUSH_PROMISE = 1 + SEND_DATA = 2 + SEND_GOAWAY = 3 + SEND_WINDOW_UPDATE = 4 + SEND_PING = 5 + SEND_SETTINGS = 6 + SEND_RST_STREAM = 7 + SEND_PRIORITY = 8 + RECV_HEADERS = 9 + RECV_PUSH_PROMISE = 10 + RECV_DATA = 11 + RECV_GOAWAY = 12 + RECV_WINDOW_UPDATE = 13 + RECV_PING = 14 + RECV_SETTINGS = 15 + RECV_RST_STREAM = 16 + RECV_PRIORITY = 17 + SEND_ALTERNATIVE_SERVICE = 18 # Added in 2.3.0 + RECV_ALTERNATIVE_SERVICE = 19 # Added in 2.3.0 + + +class AllowedStreamIDs(IntEnum): + EVEN = 0 + ODD = 1 + + +class H2ConnectionStateMachine: + """ + A single HTTP/2 connection state machine. + + This state machine, while defined in its own class, is logically part of + the H2Connection class also defined in this file. The state machine itself + maintains very little state directly, instead focusing entirely on managing + state transitions. + """ + + # For the purposes of this state machine we treat HEADERS and their + # associated CONTINUATION frames as a single jumbo frame. The protocol + # allows/requires this by preventing other frames from being interleved in + # between HEADERS/CONTINUATION frames. + # + # The _transitions dictionary contains a mapping of tuples of + # (state, input) to tuples of (side_effect_function, end_state). This map + # contains all allowed transitions: anything not in this map is invalid + # and immediately causes a transition to ``closed``. + + _transitions = { + # State: idle + (ConnectionState.IDLE, ConnectionInputs.SEND_HEADERS): ( + None, + ConnectionState.CLIENT_OPEN, + ), + (ConnectionState.IDLE, ConnectionInputs.RECV_HEADERS): ( + None, + ConnectionState.SERVER_OPEN, + ), + (ConnectionState.IDLE, ConnectionInputs.SEND_SETTINGS): ( + None, + ConnectionState.IDLE, + ), + (ConnectionState.IDLE, ConnectionInputs.RECV_SETTINGS): ( + None, + ConnectionState.IDLE, + ), + (ConnectionState.IDLE, ConnectionInputs.SEND_WINDOW_UPDATE): ( + None, + ConnectionState.IDLE, + ), + (ConnectionState.IDLE, ConnectionInputs.RECV_WINDOW_UPDATE): ( + None, + ConnectionState.IDLE, + ), + (ConnectionState.IDLE, ConnectionInputs.SEND_PING): ( + None, + ConnectionState.IDLE, + ), + (ConnectionState.IDLE, ConnectionInputs.RECV_PING): ( + None, + ConnectionState.IDLE, + ), + (ConnectionState.IDLE, ConnectionInputs.SEND_GOAWAY): ( + None, + ConnectionState.CLOSED, + ), + (ConnectionState.IDLE, ConnectionInputs.RECV_GOAWAY): ( + None, + ConnectionState.CLOSED, + ), + (ConnectionState.IDLE, ConnectionInputs.SEND_PRIORITY): ( + None, + ConnectionState.IDLE, + ), + (ConnectionState.IDLE, ConnectionInputs.RECV_PRIORITY): ( + None, + ConnectionState.IDLE, + ), + (ConnectionState.IDLE, ConnectionInputs.SEND_ALTERNATIVE_SERVICE): ( + None, + ConnectionState.SERVER_OPEN, + ), + (ConnectionState.IDLE, ConnectionInputs.RECV_ALTERNATIVE_SERVICE): ( + None, + ConnectionState.CLIENT_OPEN, + ), + # State: open, client side. + (ConnectionState.CLIENT_OPEN, ConnectionInputs.SEND_HEADERS): ( + None, + ConnectionState.CLIENT_OPEN, + ), + (ConnectionState.CLIENT_OPEN, ConnectionInputs.SEND_DATA): ( + None, + ConnectionState.CLIENT_OPEN, + ), + (ConnectionState.CLIENT_OPEN, ConnectionInputs.SEND_GOAWAY): ( + None, + ConnectionState.CLOSED, + ), + (ConnectionState.CLIENT_OPEN, ConnectionInputs.SEND_WINDOW_UPDATE): ( + None, + ConnectionState.CLIENT_OPEN, + ), + (ConnectionState.CLIENT_OPEN, ConnectionInputs.SEND_PING): ( + None, + ConnectionState.CLIENT_OPEN, + ), + (ConnectionState.CLIENT_OPEN, ConnectionInputs.SEND_SETTINGS): ( + None, + ConnectionState.CLIENT_OPEN, + ), + (ConnectionState.CLIENT_OPEN, ConnectionInputs.SEND_PRIORITY): ( + None, + ConnectionState.CLIENT_OPEN, + ), + (ConnectionState.CLIENT_OPEN, ConnectionInputs.RECV_HEADERS): ( + None, + ConnectionState.CLIENT_OPEN, + ), + (ConnectionState.CLIENT_OPEN, ConnectionInputs.RECV_PUSH_PROMISE): ( + None, + ConnectionState.CLIENT_OPEN, + ), + (ConnectionState.CLIENT_OPEN, ConnectionInputs.RECV_DATA): ( + None, + ConnectionState.CLIENT_OPEN, + ), + (ConnectionState.CLIENT_OPEN, ConnectionInputs.RECV_GOAWAY): ( + None, + ConnectionState.CLOSED, + ), + (ConnectionState.CLIENT_OPEN, ConnectionInputs.RECV_WINDOW_UPDATE): ( + None, + ConnectionState.CLIENT_OPEN, + ), + (ConnectionState.CLIENT_OPEN, ConnectionInputs.RECV_PING): ( + None, + ConnectionState.CLIENT_OPEN, + ), + (ConnectionState.CLIENT_OPEN, ConnectionInputs.RECV_SETTINGS): ( + None, + ConnectionState.CLIENT_OPEN, + ), + (ConnectionState.CLIENT_OPEN, ConnectionInputs.SEND_RST_STREAM): ( + None, + ConnectionState.CLIENT_OPEN, + ), + (ConnectionState.CLIENT_OPEN, ConnectionInputs.RECV_RST_STREAM): ( + None, + ConnectionState.CLIENT_OPEN, + ), + (ConnectionState.CLIENT_OPEN, ConnectionInputs.RECV_PRIORITY): ( + None, + ConnectionState.CLIENT_OPEN, + ), + (ConnectionState.CLIENT_OPEN, ConnectionInputs.RECV_ALTERNATIVE_SERVICE): ( + None, + ConnectionState.CLIENT_OPEN, + ), + # State: open, server side. + (ConnectionState.SERVER_OPEN, ConnectionInputs.SEND_HEADERS): ( + None, + ConnectionState.SERVER_OPEN, + ), + (ConnectionState.SERVER_OPEN, ConnectionInputs.SEND_PUSH_PROMISE): ( + None, + ConnectionState.SERVER_OPEN, + ), + (ConnectionState.SERVER_OPEN, ConnectionInputs.SEND_DATA): ( + None, + ConnectionState.SERVER_OPEN, + ), + (ConnectionState.SERVER_OPEN, ConnectionInputs.SEND_GOAWAY): ( + None, + ConnectionState.CLOSED, + ), + (ConnectionState.SERVER_OPEN, ConnectionInputs.SEND_WINDOW_UPDATE): ( + None, + ConnectionState.SERVER_OPEN, + ), + (ConnectionState.SERVER_OPEN, ConnectionInputs.SEND_PING): ( + None, + ConnectionState.SERVER_OPEN, + ), + (ConnectionState.SERVER_OPEN, ConnectionInputs.SEND_SETTINGS): ( + None, + ConnectionState.SERVER_OPEN, + ), + (ConnectionState.SERVER_OPEN, ConnectionInputs.SEND_PRIORITY): ( + None, + ConnectionState.SERVER_OPEN, + ), + (ConnectionState.SERVER_OPEN, ConnectionInputs.RECV_HEADERS): ( + None, + ConnectionState.SERVER_OPEN, + ), + (ConnectionState.SERVER_OPEN, ConnectionInputs.RECV_DATA): ( + None, + ConnectionState.SERVER_OPEN, + ), + (ConnectionState.SERVER_OPEN, ConnectionInputs.RECV_GOAWAY): ( + None, + ConnectionState.CLOSED, + ), + (ConnectionState.SERVER_OPEN, ConnectionInputs.RECV_WINDOW_UPDATE): ( + None, + ConnectionState.SERVER_OPEN, + ), + (ConnectionState.SERVER_OPEN, ConnectionInputs.RECV_PING): ( + None, + ConnectionState.SERVER_OPEN, + ), + (ConnectionState.SERVER_OPEN, ConnectionInputs.RECV_SETTINGS): ( + None, + ConnectionState.SERVER_OPEN, + ), + (ConnectionState.SERVER_OPEN, ConnectionInputs.RECV_PRIORITY): ( + None, + ConnectionState.SERVER_OPEN, + ), + (ConnectionState.SERVER_OPEN, ConnectionInputs.SEND_RST_STREAM): ( + None, + ConnectionState.SERVER_OPEN, + ), + (ConnectionState.SERVER_OPEN, ConnectionInputs.RECV_RST_STREAM): ( + None, + ConnectionState.SERVER_OPEN, + ), + (ConnectionState.SERVER_OPEN, ConnectionInputs.SEND_ALTERNATIVE_SERVICE): ( + None, + ConnectionState.SERVER_OPEN, + ), + (ConnectionState.SERVER_OPEN, ConnectionInputs.RECV_ALTERNATIVE_SERVICE): ( + None, + ConnectionState.SERVER_OPEN, + ), + # State: closed + (ConnectionState.CLOSED, ConnectionInputs.SEND_GOAWAY): ( + None, + ConnectionState.CLOSED, + ), + (ConnectionState.CLOSED, ConnectionInputs.RECV_GOAWAY): ( + None, + ConnectionState.CLOSED, + ), + } + + def __init__(self): + self.state = ConnectionState.IDLE + + def process_input(self, input_): + """ + Process a specific input in the state machine. + """ + if not isinstance(input_, ConnectionInputs): + raise ValueError("Input must be an instance of ConnectionInputs") + + try: + func, target_state = self._transitions[(self.state, input_)] + except KeyError: + old_state = self.state + self.state = ConnectionState.CLOSED + raise ProtocolError(f"Invalid input {input_} in state {old_state}") + else: + self.state = target_state + if func is not None: # pragma: no cover + return func() + + return [] + + +class H2Connection: + """ + A low-level HTTP/2 connection object. This handles building and receiving + frames and maintains both connection and per-stream state for all streams + on this connection. + + This wraps a HTTP/2 Connection state machine implementation, ensuring that + frames can only be sent/received when the connection is in a valid state. + It also builds stream state machines on demand to ensure that the + constraints of those state machines are met as well. Attempts to create + frames that cannot be sent will raise a ``ProtocolError``. + + .. versionchanged:: 2.3.0 + Added the ``header_encoding`` keyword argument. + + .. versionchanged:: 2.5.0 + Added the ``config`` keyword argument. Deprecated the ``client_side`` + and ``header_encoding`` parameters. + + .. versionchanged:: 3.0.0 + Removed deprecated parameters and properties. + + :param config: The configuration for the HTTP/2 connection. + + .. versionadded:: 2.5.0 + + :type config: :class:`H2Configuration ` + """ + + # The initial maximum outbound frame size. This can be changed by receiving + # a settings frame. + DEFAULT_MAX_OUTBOUND_FRAME_SIZE = 65535 + + # The initial maximum inbound frame size. This is somewhat arbitrarily + # chosen. + DEFAULT_MAX_INBOUND_FRAME_SIZE = 2**24 + + # The highest acceptable stream ID. + HIGHEST_ALLOWED_STREAM_ID = 2**31 - 1 + + # The largest acceptable window increment. + MAX_WINDOW_INCREMENT = 2**31 - 1 + + # The initial default value of SETTINGS_MAX_HEADER_LIST_SIZE. + DEFAULT_MAX_HEADER_LIST_SIZE = 2**16 + + # Keep in memory limited amount of results for streams closes + MAX_CLOSED_STREAMS = 2**16 + + def __init__(self, config=None): + self.state_machine = H2ConnectionStateMachine() + self.streams = {} + self.highest_inbound_stream_id = 0 + self.highest_outbound_stream_id = 0 + self.encoder = Encoder() + self.decoder = Decoder() + + # This won't always actually do anything: for versions of HPACK older + # than 2.3.0 it does nothing. However, we have to try! + self.decoder.max_header_list_size = self.DEFAULT_MAX_HEADER_LIST_SIZE + + #: The configuration for this HTTP/2 connection object. + #: + #: .. versionadded:: 2.5.0 + self.config = config + if self.config is None: + self.config = H2Configuration( + client_side=True, + ) + + # Objects that store settings, including defaults. + # + # We set the MAX_CONCURRENT_STREAMS value to 100 because its default is + # unbounded, and that's a dangerous default because it allows + # essentially unbounded resources to be allocated regardless of how + # they will be used. 100 should be suitable for the average + # application. This default obviously does not apply to the remote + # peer's settings: the remote peer controls them! + # + # We also set MAX_HEADER_LIST_SIZE to a reasonable value. This is to + # advertise our defence against CVE-2016-6581. However, not all + # versions of HPACK will let us do it. That's ok: we should at least + # suggest that we're not vulnerable. + self.local_settings = Settings( + client=self.config.client_side, + initial_values={ + SettingCodes.MAX_CONCURRENT_STREAMS: 100, + SettingCodes.MAX_HEADER_LIST_SIZE: self.DEFAULT_MAX_HEADER_LIST_SIZE, + }, + ) + self.remote_settings = Settings(client=not self.config.client_side) + + # The current value of the connection flow control windows on the + # connection. + self.outbound_flow_control_window = self.remote_settings.initial_window_size + + #: The maximum size of a frame that can be emitted by this peer, in + #: bytes. + self.max_outbound_frame_size = self.remote_settings.max_frame_size + + #: The maximum size of a frame that can be received by this peer, in + #: bytes. + self.max_inbound_frame_size = self.local_settings.max_frame_size + + # Buffer for incoming data. + self.incoming_buffer = FrameBuffer(server=not self.config.client_side) + + # A private variable to store a sequence of received header frames + # until completion. + self._header_frames = [] + + # Data that needs to be sent. + self._data_to_send = bytearray() + + # Keeps track of how streams are closed. + # Used to ensure that we don't blow up in the face of frames that were + # in flight when a RST_STREAM was sent. + # Also used to determine whether we should consider a frame received + # while a stream is closed as either a stream error or a connection + # error. + self._closed_streams = SizeLimitDict(size_limit=self.MAX_CLOSED_STREAMS) + + # The flow control window manager for the connection. + self._inbound_flow_control_window_manager = WindowManager( + max_window_size=self.local_settings.initial_window_size + ) + + # When in doubt use dict-dispatch. + self._frame_dispatch_table = { + HeadersFrame: self._receive_headers_frame, + PushPromiseFrame: self._receive_push_promise_frame, + SettingsFrame: self._receive_settings_frame, + DataFrame: self._receive_data_frame, + WindowUpdateFrame: self._receive_window_update_frame, + PingFrame: self._receive_ping_frame, + RstStreamFrame: self._receive_rst_stream_frame, + PriorityFrame: self._receive_priority_frame, + GoAwayFrame: self._receive_goaway_frame, + ContinuationFrame: self._receive_naked_continuation, + AltSvcFrame: self._receive_alt_svc_frame, + ExtensionFrame: self._receive_unknown_frame, + } + + def _prepare_for_sending(self, frames): + if not frames: + return + self._data_to_send += b"".join(f.serialize() for f in frames) + assert all(f.body_len <= self.max_outbound_frame_size for f in frames) + + def _open_streams(self, remainder): + """ + A common method of counting number of open streams. Returns the number + of streams that are open *and* that have (stream ID % 2) == remainder. + While it iterates, also deletes any closed streams. + """ + count = 0 + to_delete = [] + + for stream_id, stream in self.streams.items(): + if stream.open and (stream_id % 2 == remainder): + count += 1 + elif stream.closed: + to_delete.append(stream_id) + + for stream_id in to_delete: + stream = self.streams.pop(stream_id) + self._closed_streams[stream_id] = stream.closed_by + + return count + + @property + def open_outbound_streams(self): + """ + The current number of open outbound streams. + """ + outbound_numbers = int(self.config.client_side) + return self._open_streams(outbound_numbers) + + @property + def open_inbound_streams(self): + """ + The current number of open inbound streams. + """ + inbound_numbers = int(not self.config.client_side) + return self._open_streams(inbound_numbers) + + @property + def inbound_flow_control_window(self): + """ + The size of the inbound flow control window for the connection. This is + rarely publicly useful: instead, use :meth:`remote_flow_control_window + `. This + shortcut is largely present to provide a shortcut to this data. + """ + return self._inbound_flow_control_window_manager.current_window_size + + def _begin_new_stream(self, stream_id, allowed_ids): + """ + Initiate a new stream. + + .. versionchanged:: 2.0.0 + Removed this function from the public API. + + :param stream_id: The ID of the stream to open. + :param allowed_ids: What kind of stream ID is allowed. + """ + self.config.logger.debug("Attempting to initiate stream ID %d", stream_id) + outbound = self._stream_id_is_outbound(stream_id) + highest_stream_id = ( + self.highest_outbound_stream_id + if outbound + else self.highest_inbound_stream_id + ) + + if stream_id <= highest_stream_id: + raise StreamIDTooLowError(stream_id, highest_stream_id) + + if (stream_id % 2) != int(allowed_ids): + raise ProtocolError("Invalid stream ID for peer.") + + s = H2Stream( + stream_id, + config=self.config, + inbound_window_size=self.local_settings.initial_window_size, + outbound_window_size=self.remote_settings.initial_window_size, + ) + self.config.logger.debug("Stream ID %d created", stream_id) + s.max_inbound_frame_size = self.max_inbound_frame_size + s.max_outbound_frame_size = self.max_outbound_frame_size + + self.streams[stream_id] = s + self.config.logger.debug("Current streams: %s", self.streams.keys()) + + if outbound: + self.highest_outbound_stream_id = stream_id + else: + self.highest_inbound_stream_id = stream_id + + return s + + def initiate_connection(self): + """ + Provides any data that needs to be sent at the start of the connection. + Must be called for both clients and servers. + """ + self.config.logger.debug("Initializing connection") + self.state_machine.process_input(ConnectionInputs.SEND_SETTINGS) + if self.config.client_side: + preamble = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" + else: + preamble = b"" + + f = SettingsFrame(0) + for setting, value in self.local_settings.items(): + f.settings[setting] = value + self.config.logger.debug("Send Settings frame: %s", self.local_settings) + + self._data_to_send += preamble + f.serialize() + + def initiate_upgrade_connection(self, settings_header=None): + """ + Call to initialise the connection object for use with an upgraded + HTTP/2 connection (i.e. a connection negotiated using the + ``Upgrade: h2c`` HTTP header). + + This method differs from :meth:`initiate_connection + ` in several ways. + Firstly, it handles the additional SETTINGS frame that is sent in the + ``HTTP2-Settings`` header field. When called on a client connection, + this method will return a bytestring that the caller can put in the + ``HTTP2-Settings`` field they send on their initial request. When + called on a server connection, the user **must** provide the value they + received from the client in the ``HTTP2-Settings`` header field to the + ``settings_header`` argument, which will be used appropriately. + + Additionally, this method sets up stream 1 in a half-closed state + appropriate for this side of the connection, to reflect the fact that + the request is already complete. + + Finally, this method also prepares the appropriate preamble to be sent + after the upgrade. + + .. versionadded:: 2.3.0 + + :param settings_header: (optional, server-only): The value of the + ``HTTP2-Settings`` header field received from the client. + :type settings_header: ``bytes`` + + :returns: For clients, a bytestring to put in the ``HTTP2-Settings``. + For servers, returns nothing. + :rtype: ``bytes`` or ``None`` + """ + self.config.logger.debug( + "Upgrade connection. Current settings: %s", self.local_settings + ) + + frame_data = None + # Begin by getting the preamble in place. + self.initiate_connection() + + if self.config.client_side: + f = SettingsFrame(0) + for setting, value in self.local_settings.items(): + f.settings[setting] = value + + frame_data = f.serialize_body() + frame_data = base64.urlsafe_b64encode(frame_data) + elif settings_header: + # We have a settings header from the client. This needs to be + # applied, but we want to throw away the ACK. We do this by + # inserting the data into a Settings frame and then passing it to + # the state machine, but ignoring the return value. + settings_header = base64.urlsafe_b64decode(settings_header) + f = SettingsFrame(0) + f.parse_body(settings_header) + self._receive_settings_frame(f) + + # Set up appropriate state. Stream 1 in a half-closed state: + # half-closed(local) for clients, half-closed(remote) for servers. + # Additionally, we need to set up the Connection state machine. + connection_input = ( + ConnectionInputs.SEND_HEADERS + if self.config.client_side + else ConnectionInputs.RECV_HEADERS + ) + self.config.logger.debug("Process input %s", connection_input) + self.state_machine.process_input(connection_input) + + # Set up stream 1. + self._begin_new_stream(stream_id=1, allowed_ids=AllowedStreamIDs.ODD) + self.streams[1].upgrade(self.config.client_side) + return frame_data + + def _get_or_create_stream(self, stream_id, allowed_ids): + """ + Gets a stream by its stream ID. Will create one if one does not already + exist. Use allowed_ids to circumvent the usual stream ID rules for + clients and servers. + + .. versionchanged:: 2.0.0 + Removed this function from the public API. + """ + try: + return self.streams[stream_id] + except KeyError: + return self._begin_new_stream(stream_id, allowed_ids) + + def _get_stream_by_id(self, stream_id): + """ + Gets a stream by its stream ID. Raises NoSuchStreamError if the stream + ID does not correspond to a known stream and is higher than the current + maximum: raises if it is lower than the current maximum. + + .. versionchanged:: 2.0.0 + Removed this function from the public API. + """ + try: + return self.streams[stream_id] + except KeyError: + outbound = self._stream_id_is_outbound(stream_id) + highest_stream_id = ( + self.highest_outbound_stream_id + if outbound + else self.highest_inbound_stream_id + ) + + if stream_id > highest_stream_id: + raise NoSuchStreamError(stream_id) + else: + raise StreamClosedError(stream_id) + + def get_next_available_stream_id(self): + """ + Returns an integer suitable for use as the stream ID for the next + stream created by this endpoint. For server endpoints, this stream ID + will be even. For client endpoints, this stream ID will be odd. If no + stream IDs are available, raises :class:`NoAvailableStreamIDError + `. + + .. warning:: The return value from this function does not change until + the stream ID has actually been used by sending or pushing + headers on that stream. For that reason, it should be + called as close as possible to the actual use of the + stream ID. + + .. versionadded:: 2.0.0 + + :raises: :class:`NoAvailableStreamIDError + ` + :returns: The next free stream ID this peer can use to initiate a + stream. + :rtype: ``int`` + """ + # No streams have been opened yet, so return the lowest allowed stream + # ID. + if not self.highest_outbound_stream_id: + next_stream_id = 1 if self.config.client_side else 2 + else: + next_stream_id = self.highest_outbound_stream_id + 2 + self.config.logger.debug("Next available stream ID %d", next_stream_id) + if next_stream_id > self.HIGHEST_ALLOWED_STREAM_ID: + raise NoAvailableStreamIDError("Exhausted allowed stream IDs") + + return next_stream_id + + def send_headers( + self, + stream_id, + headers, + end_stream=False, + priority_weight=None, + priority_depends_on=None, + priority_exclusive=None, + ): + """ + Send headers on a given stream. + + This function can be used to send request or response headers: the kind + that are sent depends on whether this connection has been opened as a + client or server connection, and whether the stream was opened by the + remote peer or not. + + If this is a client connection, calling ``send_headers`` will send the + headers as a request. It will also implicitly open the stream being + used. If this is a client connection and ``send_headers`` has *already* + been called, this will send trailers instead. + + If this is a server connection, calling ``send_headers`` will send the + headers as a response. It is a protocol error for a server to open a + stream by sending headers. If this is a server connection and + ``send_headers`` has *already* been called, this will send trailers + instead. + + When acting as a server, you may call ``send_headers`` any number of + times allowed by the following rules, in this order: + + - zero or more times with ``(':status', '1XX')`` (where ``1XX`` is a + placeholder for any 100-level status code). + - once with any other status header. + - zero or one time for trailers. + + That is, you are allowed to send as many informational responses as you + like, followed by one complete response and zero or one HTTP trailer + blocks. + + Clients may send one or two header blocks: one request block, and + optionally one trailer block. + + If it is important to send HPACK "never indexed" header fields (as + defined in `RFC 7451 Section 7.1.3 + `_), the user may + instead provide headers using the HPACK library's :class:`HeaderTuple + ` and :class:`NeverIndexedHeaderTuple + ` objects. + + This method also allows users to prioritize the stream immediately, + by sending priority information on the HEADERS frame directly. To do + this, any one of ``priority_weight``, ``priority_depends_on``, or + ``priority_exclusive`` must be set to a value that is not ``None``. For + more information on the priority fields, see :meth:`prioritize + `. + + .. warning:: In HTTP/2, it is mandatory that all the HTTP/2 special + headers (that is, ones whose header keys begin with ``:``) appear + at the start of the header block, before any normal headers. + + .. versionchanged:: 2.3.0 + Added support for using :class:`HeaderTuple + ` objects to store headers. + + .. versionchanged:: 2.4.0 + Added the ability to provide priority keyword arguments: + ``priority_weight``, ``priority_depends_on``, and + ``priority_exclusive``. + + :param stream_id: The stream ID to send the headers on. If this stream + does not currently exist, it will be created. + :type stream_id: ``int`` + + :param headers: The request/response headers to send. + :type headers: An iterable of two tuples of bytestrings or + :class:`HeaderTuple ` objects. + + :param end_stream: Whether this headers frame should end the stream + immediately (that is, whether no more data will be sent after this + frame). Defaults to ``False``. + :type end_stream: ``bool`` + + :param priority_weight: Sets the priority weight of the stream. See + :meth:`prioritize ` for more + about how this field works. Defaults to ``None``, which means that + no priority information will be sent. + :type priority_weight: ``int`` or ``None`` + + :param priority_depends_on: Sets which stream this one depends on for + priority purposes. See :meth:`prioritize + ` for more about how this + field works. Defaults to ``None``, which means that no priority + information will be sent. + :type priority_depends_on: ``int`` or ``None`` + + :param priority_exclusive: Sets whether this stream exclusively depends + on the stream given in ``priority_depends_on`` for priority + purposes. See :meth:`prioritize + ` for more about how this + field workds. Defaults to ``None``, which means that no priority + information will be sent. + :type priority_depends_on: ``bool`` or ``None`` + + :returns: Nothing + """ + self.config.logger.debug("Send headers on stream ID %d", stream_id) + + # Check we can open the stream. + if stream_id not in self.streams: + max_open_streams = self.remote_settings.max_concurrent_streams + if (self.open_outbound_streams + 1) > max_open_streams: + raise TooManyStreamsError( + "Max outbound streams is %d, %d open" + % (max_open_streams, self.open_outbound_streams) + ) + + self.state_machine.process_input(ConnectionInputs.SEND_HEADERS) + stream = self._get_or_create_stream( + stream_id, AllowedStreamIDs(self.config.client_side) + ) + frames = stream.send_headers(headers, self.encoder, end_stream) + + # We may need to send priority information. + priority_present = ( + (priority_weight is not None) + or (priority_depends_on is not None) + or (priority_exclusive is not None) + ) + + if priority_present: + if not self.config.client_side: + raise RFC1122Error("Servers SHOULD NOT prioritize streams.") + + headers_frame = frames[0] + headers_frame.flags.add("PRIORITY") + frames[0] = _add_frame_priority( + headers_frame, priority_weight, priority_depends_on, priority_exclusive + ) + + self._prepare_for_sending(frames) + + def send_data(self, stream_id, data, end_stream=False, pad_length=None): + """ + Send data on a given stream. + + This method does no breaking up of data: if the data is larger than the + value returned by :meth:`local_flow_control_window + ` for this stream + then a :class:`FlowControlError ` will + be raised. If the data is larger than :data:`max_outbound_frame_size + ` then a + :class:`FrameTooLargeError ` will be + raised. + + h2 does this to avoid buffering the data internally. If the user + has more data to send than h2 will allow, consider breaking it up + and buffering it externally. + + :param stream_id: The ID of the stream on which to send the data. + :type stream_id: ``int`` + :param data: The data to send on the stream. + :type data: ``bytes`` + :param end_stream: (optional) Whether this is the last data to be sent + on the stream. Defaults to ``False``. + :type end_stream: ``bool`` + :param pad_length: (optional) Length of the padding to apply to the + data frame. Defaults to ``None`` for no use of padding. Note that + a value of ``0`` results in padding of length ``0`` + (with the "padding" flag set on the frame). + + .. versionadded:: 2.6.0 + + :type pad_length: ``int`` + :returns: Nothing + """ + self.config.logger.debug( + "Send data on stream ID %d with len %d", stream_id, len(data) + ) + frame_size = len(data) + if pad_length is not None: + if not isinstance(pad_length, int): + raise TypeError("pad_length must be an int") + if pad_length < 0 or pad_length > 255: + raise ValueError("pad_length must be within range: [0, 255]") + # Account for padding bytes plus the 1-byte padding length field. + frame_size += pad_length + 1 + self.config.logger.debug( + "Frame size on stream ID %d is %d", stream_id, frame_size + ) + + if frame_size > self.local_flow_control_window(stream_id): + raise FlowControlError( + "Cannot send %d bytes, flow control window is %d." + % (frame_size, self.local_flow_control_window(stream_id)) + ) + elif frame_size > self.max_outbound_frame_size: + raise FrameTooLargeError( + "Cannot send frame size %d, max frame size is %d" + % (frame_size, self.max_outbound_frame_size) + ) + + self.state_machine.process_input(ConnectionInputs.SEND_DATA) + frames = self.streams[stream_id].send_data( + data, end_stream, pad_length=pad_length + ) + + self._prepare_for_sending(frames) + + self.outbound_flow_control_window -= frame_size + self.config.logger.debug( + "Outbound flow control window size is %d", self.outbound_flow_control_window + ) + assert self.outbound_flow_control_window >= 0 + + def end_stream(self, stream_id): + """ + Cleanly end a given stream. + + This method ends a stream by sending an empty DATA frame on that stream + with the ``END_STREAM`` flag set. + + :param stream_id: The ID of the stream to end. + :type stream_id: ``int`` + :returns: Nothing + """ + self.config.logger.debug("End stream ID %d", stream_id) + self.state_machine.process_input(ConnectionInputs.SEND_DATA) + frames = self.streams[stream_id].end_stream() + self._prepare_for_sending(frames) + + def increment_flow_control_window(self, increment, stream_id=None): + """ + Increment a flow control window, optionally for a single stream. Allows + the remote peer to send more data. + + .. versionchanged:: 2.0.0 + Rejects attempts to increment the flow control window by out of + range values with a ``ValueError``. + + :param increment: The amount to increment the flow control window by. + :type increment: ``int`` + :param stream_id: (optional) The ID of the stream that should have its + flow control window opened. If not present or ``None``, the + connection flow control window will be opened instead. + :type stream_id: ``int`` or ``None`` + :returns: Nothing + :raises: ``ValueError`` + """ + if not (1 <= increment <= self.MAX_WINDOW_INCREMENT): + raise ValueError( + "Flow control increment must be between 1 and %d" + % self.MAX_WINDOW_INCREMENT + ) + + self.state_machine.process_input(ConnectionInputs.SEND_WINDOW_UPDATE) + + if stream_id is not None: + stream = self.streams[stream_id] + frames = stream.increase_flow_control_window(increment) + + self.config.logger.debug( + "Increase stream ID %d flow control window by %d", stream_id, increment + ) + else: + self._inbound_flow_control_window_manager.window_opened(increment) + f = WindowUpdateFrame(0) + f.window_increment = increment + frames = [f] + + self.config.logger.debug( + "Increase connection flow control window by %d", increment + ) + + self._prepare_for_sending(frames) + + def push_stream(self, stream_id, promised_stream_id, request_headers): + """ + Push a response to the client by sending a PUSH_PROMISE frame. + + If it is important to send HPACK "never indexed" header fields (as + defined in `RFC 7451 Section 7.1.3 + `_), the user may + instead provide headers using the HPACK library's :class:`HeaderTuple + ` and :class:`NeverIndexedHeaderTuple + ` objects. + + :param stream_id: The ID of the stream that this push is a response to. + :type stream_id: ``int`` + :param promised_stream_id: The ID of the stream that the pushed + response will be sent on. + :type promised_stream_id: ``int`` + :param request_headers: The headers of the request that the pushed + response will be responding to. + :type request_headers: An iterable of two tuples of bytestrings or + :class:`HeaderTuple ` objects. + :returns: Nothing + """ + self.config.logger.debug("Send Push Promise frame on stream ID %d", stream_id) + + if not self.remote_settings.enable_push: + raise ProtocolError("Remote peer has disabled stream push") + + self.state_machine.process_input(ConnectionInputs.SEND_PUSH_PROMISE) + stream = self._get_stream_by_id(stream_id) + + # We need to prevent users pushing streams in response to streams that + # they themselves have already pushed: see #163 and RFC 7540 § 6.6. The + # easiest way to do that is to assert that the stream_id is not even: + # this shortcut works because only servers can push and the state + # machine will enforce this. + if (stream_id % 2) == 0: + raise ProtocolError("Cannot recursively push streams.") + + new_stream = self._begin_new_stream(promised_stream_id, AllowedStreamIDs.EVEN) + self.streams[promised_stream_id] = new_stream + + frames = stream.push_stream_in_band( + promised_stream_id, request_headers, self.encoder + ) + new_frames = new_stream.locally_pushed() + self._prepare_for_sending(frames + new_frames) + + def ping(self, opaque_data): + """ + Send a PING frame. + + :param opaque_data: A bytestring of length 8 that will be sent in the + PING frame. + :returns: Nothing + """ + self.config.logger.debug("Send Ping frame") + + if not isinstance(opaque_data, bytes) or len(opaque_data) != 8: + raise ValueError("Invalid value for ping data: %r" % opaque_data) + + self.state_machine.process_input(ConnectionInputs.SEND_PING) + f = PingFrame(0) + f.opaque_data = opaque_data + self._prepare_for_sending([f]) + + def reset_stream(self, stream_id, error_code=0): + """ + Reset a stream. + + This method forcibly closes a stream by sending a RST_STREAM frame for + a given stream. This is not a graceful closure. To gracefully end a + stream, try the :meth:`end_stream + ` method. + + :param stream_id: The ID of the stream to reset. + :type stream_id: ``int`` + :param error_code: (optional) The error code to use to reset the + stream. Defaults to :data:`ErrorCodes.NO_ERROR + `. + :type error_code: ``int`` + :returns: Nothing + """ + self.config.logger.debug("Reset stream ID %d", stream_id) + self.state_machine.process_input(ConnectionInputs.SEND_RST_STREAM) + stream = self._get_stream_by_id(stream_id) + frames = stream.reset_stream(error_code) + self._prepare_for_sending(frames) + + def close_connection(self, error_code=0, additional_data=None, last_stream_id=None): + """ + Close a connection, emitting a GOAWAY frame. + + .. versionchanged:: 2.4.0 + Added ``additional_data`` and ``last_stream_id`` arguments. + + :param error_code: (optional) The error code to send in the GOAWAY + frame. + :param additional_data: (optional) Additional debug data indicating + a reason for closing the connection. Must be a bytestring. + :param last_stream_id: (optional) The last stream which was processed + by the sender. Defaults to ``highest_inbound_stream_id``. + :returns: Nothing + """ + self.config.logger.debug("Close connection") + self.state_machine.process_input(ConnectionInputs.SEND_GOAWAY) + + # Additional_data must be bytes + if additional_data is not None: + assert isinstance(additional_data, bytes) + + if last_stream_id is None: + last_stream_id = self.highest_inbound_stream_id + + f = GoAwayFrame( + stream_id=0, + last_stream_id=last_stream_id, + error_code=error_code, + additional_data=(additional_data or b""), + ) + self._prepare_for_sending([f]) + + def update_settings(self, new_settings): + """ + Update the local settings. This will prepare and emit the appropriate + SETTINGS frame. + + :param new_settings: A dictionary of {setting: new value} + """ + self.config.logger.debug("Update connection settings to %s", new_settings) + self.state_machine.process_input(ConnectionInputs.SEND_SETTINGS) + self.local_settings.update(new_settings) + s = SettingsFrame(0) + s.settings = new_settings + self._prepare_for_sending([s]) + + def advertise_alternative_service(self, field_value, origin=None, stream_id=None): + """ + Notify a client about an available Alternative Service. + + An Alternative Service is defined in `RFC 7838 + `_. An Alternative Service + notification informs a client that a given origin is also available + elsewhere. + + Alternative Services can be advertised in two ways. Firstly, they can + be advertised explicitly: that is, a server can say "origin X is also + available at Y". To advertise like this, set the ``origin`` argument + and not the ``stream_id`` argument. Alternatively, they can be + advertised implicitly: that is, a server can say "the origin you're + contacting on stream X is also available at Y". To advertise like this, + set the ``stream_id`` argument and not the ``origin`` argument. + + The explicit method of advertising can be done as long as the + connection is active. The implicit method can only be done after the + client has sent the request headers and before the server has sent the + response headers: outside of those points, h2 will forbid sending + the Alternative Service advertisement by raising a ProtocolError. + + The ``field_value`` parameter is specified in RFC 7838. h2 does + not validate or introspect this argument: the user is required to + ensure that it's well-formed. ``field_value`` corresponds to RFC 7838's + "Alternative Service Field Value". + + .. note:: It is strongly preferred to use the explicit method of + advertising Alternative Services. The implicit method of + advertising Alternative Services has a number of subtleties + and can lead to inconsistencies between the server and + client. h2 allows both mechanisms, but caution is + strongly advised. + + .. versionadded:: 2.3.0 + + :param field_value: The RFC 7838 Alternative Service Field Value. This + argument is not introspected by h2: the user is responsible + for ensuring that it is well-formed. + :type field_value: ``bytes`` + + :param origin: The origin/authority to which the Alternative Service + being advertised applies. Must not be provided at the same time as + ``stream_id``. + :type origin: ``bytes`` or ``None`` + + :param stream_id: The ID of the stream which was sent to the authority + for which this Alternative Service advertisement applies. Must not + be provided at the same time as ``origin``. + :type stream_id: ``int`` or ``None`` + + :returns: Nothing. + """ + if not isinstance(field_value, bytes): + raise ValueError("Field must be bytestring.") + + if origin is not None and stream_id is not None: + raise ValueError("Must not provide both origin and stream_id") + + self.state_machine.process_input(ConnectionInputs.SEND_ALTERNATIVE_SERVICE) + + if origin is not None: + # This ALTSVC is sent on stream zero. + f = AltSvcFrame(stream_id=0) + f.origin = origin + f.field = field_value + frames = [f] + else: + stream = self._get_stream_by_id(stream_id) + frames = stream.advertise_alternative_service(field_value) + + self._prepare_for_sending(frames) + + def prioritize(self, stream_id, weight=None, depends_on=None, exclusive=None): + """ + Notify a server about the priority of a stream. + + Stream priorities are a form of guidance to a remote server: they + inform the server about how important a given response is, so that the + server may allocate its resources (e.g. bandwidth, CPU time, etc.) + accordingly. This exists to allow clients to ensure that the most + important data arrives earlier, while less important data does not + starve out the more important data. + + Stream priorities are explained in depth in `RFC 7540 Section 5.3 + `_. + + This method updates the priority information of a single stream. It may + be called well before a stream is actively in use, or well after a + stream is closed. + + .. warning:: RFC 7540 allows for servers to change the priority of + streams. However, h2 **does not** allow server + stacks to do this. This is because most clients do not + adequately know how to respond when provided conflicting + priority information, and relatively little utility is + provided by making that functionality available. + + .. note:: h2 **does not** maintain any information about the + RFC 7540 priority tree. That means that h2 does not + prevent incautious users from creating invalid priority + trees, particularly by creating priority loops. While some + basic error checking is provided by h2, users are + strongly recommended to understand their prioritisation + strategies before using the priority tools here. + + .. note:: Priority information is strictly advisory. Servers are + allowed to disregard it entirely. Avoid relying on the idea + that your priority signaling will definitely be obeyed. + + .. versionadded:: 2.4.0 + + :param stream_id: The ID of the stream to prioritize. + :type stream_id: ``int`` + + :param weight: The weight to give the stream. Defaults to ``16``, the + default weight of any stream. May be any value between ``1`` and + ``256`` inclusive. The relative weight of a stream indicates what + proportion of available resources will be allocated to that + stream. + :type weight: ``int`` + + :param depends_on: The ID of the stream on which this stream depends. + This stream will only be progressed if it is impossible to + progress the parent stream (the one on which this one depends). + Passing the value ``0`` means that this stream does not depend on + any other. Defaults to ``0``. + :type depends_on: ``int`` + + :param exclusive: Whether this stream is an exclusive dependency of its + "parent" stream (i.e. the stream given by ``depends_on``). If a + stream is an exclusive dependency of another, that means that all + previously-set children of the parent are moved to become children + of the new exclusively-dependent stream. Defaults to ``False``. + :type exclusive: ``bool`` + """ + if not self.config.client_side: + raise RFC1122Error("Servers SHOULD NOT prioritize streams.") + + self.state_machine.process_input(ConnectionInputs.SEND_PRIORITY) + + frame = PriorityFrame(stream_id) + frame = _add_frame_priority(frame, weight, depends_on, exclusive) + + self._prepare_for_sending([frame]) + + def local_flow_control_window(self, stream_id): + """ + Returns the maximum amount of data that can be sent on stream + ``stream_id``. + + This value will never be larger than the total data that can be sent on + the connection: even if the given stream allows more data, the + connection window provides a logical maximum to the amount of data that + can be sent. + + The maximum data that can be sent in a single data frame on a stream + is either this value, or the maximum frame size, whichever is + *smaller*. + + :param stream_id: The ID of the stream whose flow control window is + being queried. + :type stream_id: ``int`` + :returns: The amount of data in bytes that can be sent on the stream + before the flow control window is exhausted. + :rtype: ``int`` + """ + stream = self._get_stream_by_id(stream_id) + return min( + self.outbound_flow_control_window, stream.outbound_flow_control_window + ) + + def remote_flow_control_window(self, stream_id): + """ + Returns the maximum amount of data the remote peer can send on stream + ``stream_id``. + + This value will never be larger than the total data that can be sent on + the connection: even if the given stream allows more data, the + connection window provides a logical maximum to the amount of data that + can be sent. + + The maximum data that can be sent in a single data frame on a stream + is either this value, or the maximum frame size, whichever is + *smaller*. + + :param stream_id: The ID of the stream whose flow control window is + being queried. + :type stream_id: ``int`` + :returns: The amount of data in bytes that can be received on the + stream before the flow control window is exhausted. + :rtype: ``int`` + """ + stream = self._get_stream_by_id(stream_id) + return min(self.inbound_flow_control_window, stream.inbound_flow_control_window) + + def acknowledge_received_data(self, acknowledged_size, stream_id): + """ + Inform the :class:`H2Connection ` that a + certain number of flow-controlled bytes have been processed, and that + the space should be handed back to the remote peer at an opportune + time. + + .. versionadded:: 2.5.0 + + :param acknowledged_size: The total *flow-controlled size* of the data + that has been processed. Note that this must include the amount of + padding that was sent with that data. + :type acknowledged_size: ``int`` + :param stream_id: The ID of the stream on which this data was received. + :type stream_id: ``int`` + :returns: Nothing + :rtype: ``None`` + """ + self.config.logger.debug( + "Ack received data on stream ID %d with size %d", + stream_id, + acknowledged_size, + ) + if stream_id <= 0: + raise ValueError( + "Stream ID %d is not valid for acknowledge_received_data" % stream_id + ) + if acknowledged_size < 0: + raise ValueError("Cannot acknowledge negative data") + + frames = [] + + conn_manager = self._inbound_flow_control_window_manager + conn_increment = conn_manager.process_bytes(acknowledged_size) + if conn_increment: + f = WindowUpdateFrame(0) + f.window_increment = conn_increment + frames.append(f) + + try: + stream = self._get_stream_by_id(stream_id) + except StreamClosedError: + # The stream is already gone. We're not worried about incrementing + # the window in this case. + pass + else: + # No point incrementing the windows of closed streams. + if stream.open: + frames.extend(stream.acknowledge_received_data(acknowledged_size)) + + self._prepare_for_sending(frames) + + def data_to_send(self, amount=None): + """ + Returns some data for sending out of the internal data buffer. + + This method is analogous to ``read`` on a file-like object, but it + doesn't block. Instead, it returns as much data as the user asks for, + or less if that much data is not available. It does not perform any + I/O, and so uses a different name. + + :param amount: (optional) The maximum amount of data to return. If not + set, or set to ``None``, will return as much data as possible. + :type amount: ``int`` + :returns: A bytestring containing the data to send on the wire. + :rtype: ``bytes`` + """ + if amount is None: + data = bytes(self._data_to_send) + self._data_to_send = bytearray() + return data + else: + data = bytes(self._data_to_send[:amount]) + self._data_to_send = self._data_to_send[amount:] + return data + + def clear_outbound_data_buffer(self): + """ + Clears the outbound data buffer, such that if this call was immediately + followed by a call to + :meth:`data_to_send `, that + call would return no data. + + This method should not normally be used, but is made available to avoid + exposing implementation details. + """ + self._data_to_send = bytearray() + + def _acknowledge_settings(self): + """ + Acknowledge settings that have been received. + + .. versionchanged:: 2.0.0 + Removed from public API, removed useless ``event`` parameter, made + automatic. + + :returns: Nothing + """ + self.state_machine.process_input(ConnectionInputs.SEND_SETTINGS) + + changes = self.remote_settings.acknowledge() + + if SettingCodes.INITIAL_WINDOW_SIZE in changes: + setting = changes[SettingCodes.INITIAL_WINDOW_SIZE] + self._flow_control_change_from_settings( + setting.original_value, + setting.new_value, + ) + + # HEADER_TABLE_SIZE changes by the remote part affect our encoder: cf. + # RFC 7540 Section 6.5.2. + if SettingCodes.HEADER_TABLE_SIZE in changes: + setting = changes[SettingCodes.HEADER_TABLE_SIZE] + self.encoder.header_table_size = setting.new_value + + if SettingCodes.MAX_FRAME_SIZE in changes: + setting = changes[SettingCodes.MAX_FRAME_SIZE] + self.max_outbound_frame_size = setting.new_value + for stream in self.streams.values(): + stream.max_outbound_frame_size = setting.new_value + + f = SettingsFrame(0) + f.flags.add("ACK") + return [f] + + def _flow_control_change_from_settings(self, old_value, new_value): + """ + Update flow control windows in response to a change in the value of + SETTINGS_INITIAL_WINDOW_SIZE. + + When this setting is changed, it automatically updates all flow control + windows by the delta in the settings values. Note that it does not + increment the *connection* flow control window, per section 6.9.2 of + RFC 7540. + """ + delta = new_value - old_value + + for stream in self.streams.values(): + stream.outbound_flow_control_window = guard_increment_window( + stream.outbound_flow_control_window, delta + ) + + def _inbound_flow_control_change_from_settings(self, old_value, new_value): + """ + Update remote flow control windows in response to a change in the value + of SETTINGS_INITIAL_WINDOW_SIZE. + + When this setting is changed, it automatically updates all remote flow + control windows by the delta in the settings values. + """ + delta = new_value - old_value + + for stream in self.streams.values(): + stream._inbound_flow_control_change_from_settings(delta) + + def receive_data(self, data): + """ + Pass some received HTTP/2 data to the connection for handling. + + :param data: The data received from the remote peer on the network. + :type data: ``bytes`` + :returns: A list of events that the remote peer triggered by sending + this data. + """ + self.config.logger.trace( + "Process received data on connection. Received data: %r", data + ) + + events = [] + self.incoming_buffer.add_data(data) + self.incoming_buffer.max_frame_size = self.max_inbound_frame_size + + try: + for frame in self.incoming_buffer: + events.extend(self._receive_frame(frame)) + except InvalidPaddingError: + self._terminate_connection(ErrorCodes.PROTOCOL_ERROR) + raise ProtocolError("Received frame with invalid padding.") + except ProtocolError as e: + # For whatever reason, receiving the frame caused a protocol error. + # We should prepare to emit a GoAway frame before throwing the + # exception up further. No need for an event: the exception will + # do fine. + self._terminate_connection(e.error_code) + raise + + return events + + def _receive_frame(self, frame): + """ + Handle a frame received on the connection. + + .. versionchanged:: 2.0.0 + Removed from the public API. + """ + # self.config.logger.trace("Received frame: %s", repr(frame)) + try: + # I don't love using __class__ here, maybe reconsider it. + frames, events = self._frame_dispatch_table[frame.__class__](frame) + except StreamClosedError as e: + # If the stream was closed by RST_STREAM, we just send a RST_STREAM + # to the remote peer. Otherwise, this is a connection error, and so + # we will re-raise to trigger one. + if self._stream_is_closed_by_reset(e.stream_id): + f = RstStreamFrame(e.stream_id) + f.error_code = e.error_code + self._prepare_for_sending([f]) + events = e._events + else: + raise + except StreamIDTooLowError as e: + # The stream ID seems invalid. This may happen when the closed + # stream has been cleaned up, or when the remote peer has opened a + # new stream with a higher stream ID than this one, forcing it + # closed implicitly. + # + # Check how the stream was closed: depending on the mechanism, it + # is either a stream error or a connection error. + if self._stream_is_closed_by_reset(e.stream_id): + # Closed by RST_STREAM is a stream error. + f = RstStreamFrame(e.stream_id) + f.error_code = ErrorCodes.STREAM_CLOSED + self._prepare_for_sending([f]) + events = [] + elif self._stream_is_closed_by_end(e.stream_id): + # Closed by END_STREAM is a connection error. + raise StreamClosedError(e.stream_id) + else: + # Closed implicitly, also a connection error, but of type + # PROTOCOL_ERROR. + raise + else: + self._prepare_for_sending(frames) + + return events + + def _terminate_connection(self, error_code): + """ + Terminate the connection early. Used in error handling blocks to send + GOAWAY frames. + """ + f = GoAwayFrame(0) + f.last_stream_id = self.highest_inbound_stream_id + f.error_code = error_code + self.state_machine.process_input(ConnectionInputs.SEND_GOAWAY) + self._prepare_for_sending([f]) + + def _receive_headers_frame(self, frame): + """ + Receive a headers frame on the connection. + """ + # If necessary, check we can open the stream. Also validate that the + # stream ID is valid. + if frame.stream_id not in self.streams: + max_open_streams = self.local_settings.max_concurrent_streams + if (self.open_inbound_streams + 1) > max_open_streams: + raise TooManyStreamsError( + "Max outbound streams is %d, %d open" + % (max_open_streams, self.open_outbound_streams) + ) + + # Let's decode the headers. We handle headers as bytes internally up + # until we hang them off the event, at which point we may optionally + # convert them to unicode. + headers = _decode_headers(self.decoder, frame.data) + + events = self.state_machine.process_input(ConnectionInputs.RECV_HEADERS) + stream = self._get_or_create_stream( + frame.stream_id, AllowedStreamIDs(not self.config.client_side) + ) + frames, stream_events = stream.receive_headers( + headers, "END_STREAM" in frame.flags, self.config.header_encoding + ) + + if "PRIORITY" in frame.flags: + p_frames, p_events = self._receive_priority_frame(frame) + stream_events[0].priority_updated = p_events[0] + stream_events.extend(p_events) + assert not p_frames + + return frames, events + stream_events + + def _receive_push_promise_frame(self, frame): + """ + Receive a push-promise frame on the connection. + """ + if not self.local_settings.enable_push: + raise ProtocolError("Received pushed stream") + + pushed_headers = _decode_headers(self.decoder, frame.data) + + events = self.state_machine.process_input(ConnectionInputs.RECV_PUSH_PROMISE) + + try: + stream = self._get_stream_by_id(frame.stream_id) + except NoSuchStreamError: + # We need to check if the parent stream was reset by us. If it was + # then we presume that the PUSH_PROMISE was in flight when we reset + # the parent stream. Rather than accept the new stream, just reset + # it. + # + # If this was closed naturally, however, we should call this a + # PROTOCOL_ERROR: pushing a stream on a naturally closed stream is + # a real problem because it creates a brand new stream that the + # remote peer now believes exists. + if ( + self._stream_closed_by(frame.stream_id) + == StreamClosedBy.SEND_RST_STREAM + ): + f = RstStreamFrame(frame.promised_stream_id) + f.error_code = ErrorCodes.REFUSED_STREAM + return [f], events + + raise ProtocolError("Attempted to push on closed stream.") + + # We need to prevent peers pushing streams in response to streams that + # they themselves have already pushed: see #163 and RFC 7540 § 6.6. The + # easiest way to do that is to assert that the stream_id is not even: + # this shortcut works because only servers can push and the state + # machine will enforce this. + if (frame.stream_id % 2) == 0: + raise ProtocolError("Cannot recursively push streams.") + + try: + frames, stream_events = stream.receive_push_promise_in_band( + frame.promised_stream_id, + pushed_headers, + self.config.header_encoding, + ) + except StreamClosedError: + # The parent stream was reset by us, so we presume that + # PUSH_PROMISE was in flight when we reset the parent stream. + # So we just reset the new stream. + f = RstStreamFrame(frame.promised_stream_id) + f.error_code = ErrorCodes.REFUSED_STREAM + return [f], events + + new_stream = self._begin_new_stream( + frame.promised_stream_id, AllowedStreamIDs.EVEN + ) + self.streams[frame.promised_stream_id] = new_stream + new_stream.remotely_pushed(pushed_headers) + + return frames, events + stream_events + + def _handle_data_on_closed_stream(self, events, exc, frame): + # This stream is already closed - and yet we received a DATA frame. + # The received DATA frame counts towards the connection flow window. + # We need to manually to acknowledge the DATA frame to update the flow + # window of the connection. Otherwise the whole connection stalls due + # the inbound flow window being 0. + frames = [] + conn_manager = self._inbound_flow_control_window_manager + conn_increment = conn_manager.process_bytes(frame.flow_controlled_length) + if conn_increment: + f = WindowUpdateFrame(0) + f.window_increment = conn_increment + frames.append(f) + self.config.logger.debug( + "Received DATA frame on closed stream %d - " + "auto-emitted a WINDOW_UPDATE by %d", + frame.stream_id, + conn_increment, + ) + f = RstStreamFrame(exc.stream_id) + f.error_code = exc.error_code + frames.append(f) + self.config.logger.debug( + "Stream %d already CLOSED or cleaned up - " + "auto-emitted a RST_FRAME" % frame.stream_id + ) + return frames, events + exc._events + + def _receive_data_frame(self, frame): + """ + Receive a data frame on the connection. + """ + flow_controlled_length = frame.flow_controlled_length + + events = self.state_machine.process_input(ConnectionInputs.RECV_DATA) + self._inbound_flow_control_window_manager.window_consumed( + flow_controlled_length + ) + + try: + stream = self._get_stream_by_id(frame.stream_id) + frames, stream_events = stream.receive_data( + frame.data, "END_STREAM" in frame.flags, flow_controlled_length + ) + except StreamClosedError as e: + # This stream is either marked as CLOSED or already gone from our + # internal state. + return self._handle_data_on_closed_stream(events, e, frame) + + return frames, events + stream_events + + def _receive_settings_frame(self, frame): + """ + Receive a SETTINGS frame on the connection. + """ + events = self.state_machine.process_input(ConnectionInputs.RECV_SETTINGS) + + # This is an ack of the local settings. + if "ACK" in frame.flags: + changed_settings = self._local_settings_acked() + ack_event = SettingsAcknowledged() + ack_event.changed_settings = changed_settings + events.append(ack_event) + return [], events + + # Add the new settings. + self.remote_settings.update(frame.settings) + events.append( + RemoteSettingsChanged.from_settings(self.remote_settings, frame.settings) + ) + frames = self._acknowledge_settings() + + return frames, events + + def _receive_window_update_frame(self, frame): + """ + Receive a WINDOW_UPDATE frame on the connection. + """ + # hyperframe will take care of validating the window_increment. + # If we reach in here, we can assume a valid value. + + events = self.state_machine.process_input(ConnectionInputs.RECV_WINDOW_UPDATE) + + if frame.stream_id: + try: + stream = self._get_stream_by_id(frame.stream_id) + frames, stream_events = stream.receive_window_update( + frame.window_increment + ) + except StreamClosedError: + return [], events + else: + # Increment our local flow control window. + self.outbound_flow_control_window = guard_increment_window( + self.outbound_flow_control_window, frame.window_increment + ) + + # FIXME: Should we split this into one event per active stream? + window_updated_event = WindowUpdated() + window_updated_event.stream_id = 0 + window_updated_event.delta = frame.window_increment + stream_events = [window_updated_event] + frames = [] + + return frames, events + stream_events + + def _receive_ping_frame(self, frame): + """ + Receive a PING frame on the connection. + """ + events = self.state_machine.process_input(ConnectionInputs.RECV_PING) + flags = [] + + if "ACK" in frame.flags: + evt = PingAckReceived() + else: + evt = PingReceived() + + # automatically ACK the PING with the same 'opaque data' + f = PingFrame(0) + f.flags = {"ACK"} + f.opaque_data = frame.opaque_data + flags.append(f) + + evt.ping_data = frame.opaque_data + events.append(evt) + + return flags, events + + def _receive_rst_stream_frame(self, frame): + """ + Receive a RST_STREAM frame on the connection. + """ + events = self.state_machine.process_input(ConnectionInputs.RECV_RST_STREAM) + try: + stream = self._get_stream_by_id(frame.stream_id) + except NoSuchStreamError: + # The stream is missing. That's ok, we just do nothing here. + stream_frames = [] + stream_events = [] + else: + stream_frames, stream_events = stream.stream_reset(frame) + + return stream_frames, events + stream_events + + def _receive_priority_frame(self, frame): + """ + Receive a PRIORITY frame on the connection. + """ + events = self.state_machine.process_input(ConnectionInputs.RECV_PRIORITY) + + event = PriorityUpdated() + event.stream_id = frame.stream_id + event.depends_on = frame.depends_on + event.exclusive = frame.exclusive + + # Weight is an integer between 1 and 256, but the byte only allows + # 0 to 255: add one. + event.weight = frame.stream_weight + 1 + + # A stream may not depend on itself. + if event.depends_on == frame.stream_id: + raise ProtocolError("Stream %d may not depend on itself" % frame.stream_id) + events.append(event) + + return [], events + + def _receive_goaway_frame(self, frame): + """ + Receive a GOAWAY frame on the connection. + """ + events = self.state_machine.process_input(ConnectionInputs.RECV_GOAWAY) + + # Clear the outbound data buffer: we cannot send further data now. + self.clear_outbound_data_buffer() + + # Fire an appropriate ConnectionTerminated event. + new_event = ConnectionTerminated() + new_event.error_code = _error_code_from_int(frame.error_code) + new_event.last_stream_id = frame.last_stream_id + new_event.additional_data = ( + frame.additional_data if frame.additional_data else None + ) + events.append(new_event) + + return [], events + + def _receive_naked_continuation(self, frame): + """ + A naked CONTINUATION frame has been received. This is always an error, + but the type of error it is depends on the state of the stream and must + transition the state of the stream, so we need to pass it to the + appropriate stream. + """ + stream = self._get_stream_by_id(frame.stream_id) + stream.receive_continuation() + assert False, "Should not be reachable" + + def _receive_alt_svc_frame(self, frame): + """ + An ALTSVC frame has been received. This frame, specified in RFC 7838, + is used to advertise alternative places where the same service can be + reached. + + This frame can optionally be received either on a stream or on stream + 0, and its semantics are different in each case. + """ + events = self.state_machine.process_input( + ConnectionInputs.RECV_ALTERNATIVE_SERVICE + ) + frames = [] + + if frame.stream_id: + # Given that it makes no sense to receive ALTSVC on a stream + # before that stream has been opened with a HEADERS frame, the + # ALTSVC frame cannot create a stream. If the stream is not + # present, we simply ignore the frame. + try: + stream = self._get_stream_by_id(frame.stream_id) + except (NoSuchStreamError, StreamClosedError): + pass + else: + stream_frames, stream_events = stream.receive_alt_svc(frame) + frames.extend(stream_frames) + events.extend(stream_events) + else: + # This frame is sent on stream 0. The origin field on the frame + # must be present, though if it isn't it's not a ProtocolError + # (annoyingly), we just need to ignore it. + if not frame.origin: + return frames, events + + # If we're a server, we want to ignore this (RFC 7838 says so). + if not self.config.client_side: + return frames, events + + event = AlternativeServiceAvailable() + event.origin = frame.origin + event.field_value = frame.field + events.append(event) + + return frames, events + + def _receive_unknown_frame(self, frame): + """ + We have received a frame that we do not understand. This is almost + certainly an extension frame, though it's impossible to be entirely + sure. + + RFC 7540 § 5.5 says that we MUST ignore unknown frame types: so we + do. We do notify the user that we received one, however. + """ + # All we do here is log. + self.config.logger.debug( + "Received unknown extension frame (ID %d)", frame.stream_id + ) + event = UnknownFrameReceived() + event.frame = frame + return [], [event] + + def _local_settings_acked(self): + """ + Handle the local settings being ACKed, update internal state. + """ + changes = self.local_settings.acknowledge() + + if SettingCodes.INITIAL_WINDOW_SIZE in changes: + setting = changes[SettingCodes.INITIAL_WINDOW_SIZE] + self._inbound_flow_control_change_from_settings( + setting.original_value, + setting.new_value, + ) + + if SettingCodes.MAX_HEADER_LIST_SIZE in changes: + setting = changes[SettingCodes.MAX_HEADER_LIST_SIZE] + self.decoder.max_header_list_size = setting.new_value + + if SettingCodes.MAX_FRAME_SIZE in changes: + setting = changes[SettingCodes.MAX_FRAME_SIZE] + self.max_inbound_frame_size = setting.new_value + + if SettingCodes.HEADER_TABLE_SIZE in changes: + setting = changes[SettingCodes.HEADER_TABLE_SIZE] + # This is safe across all hpack versions: some versions just won't + # respect it. + self.decoder.max_allowed_table_size = setting.new_value + + return changes + + def _stream_id_is_outbound(self, stream_id): + """ + Returns ``True`` if the stream ID corresponds to an outbound stream + (one initiated by this peer), returns ``False`` otherwise. + """ + return stream_id % 2 == int(self.config.client_side) + + def _stream_closed_by(self, stream_id): + """ + Returns how the stream was closed. + + The return value will be either a member of + ``h2.stream.StreamClosedBy`` or ``None``. If ``None``, the stream was + closed implicitly by the peer opening a stream with a higher stream ID + before opening this one. + """ + if stream_id in self.streams: + return self.streams[stream_id].closed_by + if stream_id in self._closed_streams: + return self._closed_streams[stream_id] + return None + + def _stream_is_closed_by_reset(self, stream_id): + """ + Returns ``True`` if the stream was closed by sending or receiving a + RST_STREAM frame. Returns ``False`` otherwise. + """ + return self._stream_closed_by(stream_id) in ( + StreamClosedBy.RECV_RST_STREAM, + StreamClosedBy.SEND_RST_STREAM, + ) + + def _stream_is_closed_by_end(self, stream_id): + """ + Returns ``True`` if the stream was closed by sending or receiving an + END_STREAM flag in a HEADERS or DATA frame. Returns ``False`` + otherwise. + """ + return self._stream_closed_by(stream_id) in ( + StreamClosedBy.RECV_END_STREAM, + StreamClosedBy.SEND_END_STREAM, + ) + + +def _add_frame_priority(frame, weight=None, depends_on=None, exclusive=None): + """ + Adds priority data to a given frame. Does not change any flags set on that + frame: if the caller is adding priority information to a HEADERS frame they + must set that themselves. + + This method also deliberately sets defaults for anything missing. + + This method validates the input values. + """ + # A stream may not depend on itself. + if depends_on == frame.stream_id: + raise ProtocolError("Stream %d may not depend on itself" % frame.stream_id) + + # Weight must be between 1 and 256. + if weight is not None: + if weight > 256 or weight < 1: + raise ProtocolError("Weight must be between 1 and 256, not %d" % weight) + else: + # Weight is an integer between 1 and 256, but the byte only allows + # 0 to 255: subtract one. + weight -= 1 + + # Set defaults for anything not provided. + weight = weight if weight is not None else 15 + depends_on = depends_on if depends_on is not None else 0 + exclusive = exclusive if exclusive is not None else False + + frame.stream_weight = weight + frame.depends_on = depends_on + frame.exclusive = exclusive + + return frame + + +def _decode_headers(decoder, encoded_header_block): + """ + Decode a HPACK-encoded header block, translating HPACK exceptions into + sensible h2 errors. + + This only ever returns bytestring headers: h2 may emit them as + unicode later, but internally it processes them as bytestrings only. + """ + try: + if ALTERNATIVE_HPACK: + return [ + NeverIndexedHeaderTuple(header[0], header[1]) + if header[-1] + else HeaderTuple(header[0], header[1]) + for header in decoder.decode(encoded_header_block, raw=True) + ] + + return decoder.decode(encoded_header_block, raw=True) + except OversizedHeaderListError as e: + # This is a symptom of a HPACK bomb attack: the user has + # disregarded our requirements on how large a header block we'll + # accept. + raise DenialOfServiceError("Oversized header block: %s" % e) + except (HPACKError, IndexError, TypeError, UnicodeDecodeError) as e: + # We should only need HPACKError here, but versions of HPACK older + # than 2.1.0 throw all three others as well. For maximum + # compatibility, catch all of them. + raise ProtocolError("Error decoding header block: %s" % e) + except BaseException as e: + # this branch only applies to the optimized (e.g. alt hpack encoder/decoder) + # the underlying library does not handle well enough invalid payloads. + if "index out of bounds" in str(e): + raise ProtocolError("Error decoding header block: %s" % e) + raise diff --git a/.venv/lib/python3.9/site-packages/jh2/errors.py b/.venv/lib/python3.9/site-packages/jh2/errors.py new file mode 100644 index 0000000..a3ec4a9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/errors.py @@ -0,0 +1,78 @@ +""" +h2/errors +~~~~~~~~~ + +Global error code registry containing the established HTTP/2 error codes. + +The current registry is available at: +https://tools.ietf.org/html/rfc7540#section-11.4 +""" + +from __future__ import annotations + +import enum + + +class ErrorCodes(enum.IntEnum): + """ + All known HTTP/2 error codes. + + .. versionadded:: 2.5.0 + """ + + #: Graceful shutdown. + NO_ERROR = 0x0 + + #: Protocol error detected. + PROTOCOL_ERROR = 0x1 + + #: Implementation fault. + INTERNAL_ERROR = 0x2 + + #: Flow-control limits exceeded. + FLOW_CONTROL_ERROR = 0x3 + + #: Settings not acknowledged. + SETTINGS_TIMEOUT = 0x4 + + #: Frame received for closed stream. + STREAM_CLOSED = 0x5 + + #: Frame size incorrect. + FRAME_SIZE_ERROR = 0x6 + + #: Stream not processed. + REFUSED_STREAM = 0x7 + + #: Stream cancelled. + CANCEL = 0x8 + + #: Compression state not updated. + COMPRESSION_ERROR = 0x9 + + #: TCP connection error for CONNECT method. + CONNECT_ERROR = 0xA + + #: Processing capacity exceeded. + ENHANCE_YOUR_CALM = 0xB + + #: Negotiated TLS parameters not acceptable. + INADEQUATE_SECURITY = 0xC + + #: Use HTTP/1.1 for the request. + HTTP_1_1_REQUIRED = 0xD + + +def _error_code_from_int(code): + """ + Given an integer error code, returns either one of :class:`ErrorCodes + ` or, if not present in the known set of codes, + returns the integer directly. + """ + try: + return ErrorCodes(code) + except ValueError: + return code + + +__all__ = ["ErrorCodes"] diff --git a/.venv/lib/python3.9/site-packages/jh2/events.py b/.venv/lib/python3.9/site-packages/jh2/events.py new file mode 100644 index 0000000..77e54d0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/events.py @@ -0,0 +1,651 @@ +""" +h2/events +~~~~~~~~~ + +Defines Event types for HTTP/2. + +Events are returned by the H2 state machine to allow implementations to keep +track of events triggered by receiving data. Each time data is provided to the +H2 state machine it processes the data and returns a list of Event objects. +""" + +from __future__ import annotations + +import binascii + +from .settings import ChangedSetting, _setting_code_from_int + + +class Event: + """ + Base class for h2 events. + """ + + pass + + +class RequestReceived(Event): + """ + The RequestReceived event is fired whenever request headers are received. + This event carries the HTTP headers for the given request and the stream ID + of the new stream. + + .. versionchanged:: 2.3.0 + Changed the type of ``headers`` to :class:`HeaderTuple + `. This has no effect on current users. + + .. versionchanged:: 2.4.0 + Added ``stream_ended`` and ``priority_updated`` properties. + """ + + def __init__(self): + #: The Stream ID for the stream this request was made on. + self.stream_id = None + + #: The request headers. + self.headers = None + + #: If this request also ended the stream, the associated + #: :class:`StreamEnded ` event will be available + #: here. + #: + #: .. versionadded:: 2.4.0 + self.stream_ended = None + + #: If this request also had associated priority information, the + #: associated :class:`PriorityUpdated ` + #: event will be available here. + #: + #: .. versionadded:: 2.4.0 + self.priority_updated = None + + def __repr__(self): + return "".format( + self.stream_id, self.headers + ) + + +class ResponseReceived(Event): + """ + The ResponseReceived event is fired whenever response headers are received. + This event carries the HTTP headers for the given response and the stream + ID of the new stream. + + .. versionchanged:: 2.3.0 + Changed the type of ``headers`` to :class:`HeaderTuple + `. This has no effect on current users. + + .. versionchanged:: 2.4.0 + Added ``stream_ended`` and ``priority_updated`` properties. + """ + + def __init__(self): + #: The Stream ID for the stream this response was made on. + self.stream_id = None + + #: The response headers. + self.headers = None + + #: If this response also ended the stream, the associated + #: :class:`StreamEnded ` event will be available + #: here. + #: + #: .. versionadded:: 2.4.0 + self.stream_ended = None + + #: If this response also had associated priority information, the + #: associated :class:`PriorityUpdated ` + #: event will be available here. + #: + #: .. versionadded:: 2.4.0 + self.priority_updated = None + + def __repr__(self): + return "".format( + self.stream_id, self.headers + ) + + +class TrailersReceived(Event): + """ + The TrailersReceived event is fired whenever trailers are received on a + stream. Trailers are a set of headers sent after the body of the + request/response, and are used to provide information that wasn't known + ahead of time (e.g. content-length). This event carries the HTTP header + fields that form the trailers and the stream ID of the stream on which they + were received. + + .. versionchanged:: 2.3.0 + Changed the type of ``headers`` to :class:`HeaderTuple + `. This has no effect on current users. + + .. versionchanged:: 2.4.0 + Added ``stream_ended`` and ``priority_updated`` properties. + """ + + def __init__(self): + #: The Stream ID for the stream on which these trailers were received. + self.stream_id = None + + #: The trailers themselves. + self.headers = None + + #: Trailers always end streams. This property has the associated + #: :class:`StreamEnded ` in it. + #: + #: .. versionadded:: 2.4.0 + self.stream_ended = None + + #: If the trailers also set associated priority information, the + #: associated :class:`PriorityUpdated ` + #: event will be available here. + #: + #: .. versionadded:: 2.4.0 + self.priority_updated = None + + def __repr__(self): + return "".format( + self.stream_id, self.headers + ) + + +class _HeadersSent(Event): + """ + The _HeadersSent event is fired whenever headers are sent. + + This is an internal event, used to determine validation steps on + outgoing header blocks. + """ + + pass + + +class _ResponseSent(_HeadersSent): + """ + The _ResponseSent event is fired whenever response headers are sent + on a stream. + + This is an internal event, used to determine validation steps on + outgoing header blocks. + """ + + pass + + +class _RequestSent(_HeadersSent): + """ + The _RequestSent event is fired whenever request headers are sent + on a stream. + + This is an internal event, used to determine validation steps on + outgoing header blocks. + """ + + pass + + +class _TrailersSent(_HeadersSent): + """ + The _TrailersSent event is fired whenever trailers are sent on a + stream. Trailers are a set of headers sent after the body of the + request/response, and are used to provide information that wasn't known + ahead of time (e.g. content-length). + + This is an internal event, used to determine validation steps on + outgoing header blocks. + """ + + pass + + +class _PushedRequestSent(_HeadersSent): + """ + The _PushedRequestSent event is fired whenever pushed request headers are + sent. + + This is an internal event, used to determine validation steps on outgoing + header blocks. + """ + + pass + + +class InformationalResponseReceived(Event): + """ + The InformationalResponseReceived event is fired when an informational + response (that is, one whose status code is a 1XX code) is received from + the remote peer. + + The remote peer may send any number of these, from zero upwards. These + responses are most commonly sent in response to requests that have the + ``expect: 100-continue`` header field present. Most users can safely + ignore this event unless you are intending to use the + ``expect: 100-continue`` flow, or are for any reason expecting a different + 1XX status code. + + .. versionadded:: 2.2.0 + + .. versionchanged:: 2.3.0 + Changed the type of ``headers`` to :class:`HeaderTuple + `. This has no effect on current users. + + .. versionchanged:: 2.4.0 + Added ``priority_updated`` property. + """ + + def __init__(self): + #: The Stream ID for the stream this informational response was made + #: on. + self.stream_id = None + + #: The headers for this informational response. + self.headers = None + + #: If this response also had associated priority information, the + #: associated :class:`PriorityUpdated ` + #: event will be available here. + #: + #: .. versionadded:: 2.4.0 + self.priority_updated = None + + def __repr__(self): + return "".format( + self.stream_id, self.headers + ) + + +class DataReceived(Event): + """ + The DataReceived event is fired whenever data is received on a stream from + the remote peer. The event carries the data itself, and the stream ID on + which the data was received. + + .. versionchanged:: 2.4.0 + Added ``stream_ended`` property. + """ + + def __init__(self): + #: The Stream ID for the stream this data was received on. + self.stream_id = None + + #: The data itself. + self.data = None + + #: The amount of data received that counts against the flow control + #: window. Note that padding counts against the flow control window, so + #: when adjusting flow control you should always use this field rather + #: than ``len(data)``. + self.flow_controlled_length = None + + #: If this data chunk also completed the stream, the associated + #: :class:`StreamEnded ` event will be available + #: here. + #: + #: .. versionadded:: 2.4.0 + self.stream_ended = None + + def __repr__(self): + return "" % ( + self.stream_id, + self.flow_controlled_length, + _bytes_representation(self.data[:20]), + ) + + +class WindowUpdated(Event): + """ + The WindowUpdated event is fired whenever a flow control window changes + size. HTTP/2 defines flow control windows for connections and streams: this + event fires for both connections and streams. The event carries the ID of + the stream to which it applies (set to zero if the window update applies to + the connection), and the delta in the window size. + """ + + def __init__(self): + #: The Stream ID of the stream whose flow control window was changed. + #: May be ``0`` if the connection window was changed. + self.stream_id = None + + #: The window delta. + self.delta = None + + def __repr__(self): + return "".format( + self.stream_id, self.delta + ) + + +class RemoteSettingsChanged(Event): + """ + The RemoteSettingsChanged event is fired whenever the remote peer changes + its settings. It contains a complete inventory of changed settings, + including their previous values. + + In HTTP/2, settings changes need to be acknowledged. h2 automatically + acknowledges settings changes for efficiency. However, it is possible that + the caller may not be happy with the changed setting. + + When this event is received, the caller should confirm that the new + settings are acceptable. If they are not acceptable, the user should close + the connection with the error code :data:`PROTOCOL_ERROR + `. + + .. versionchanged:: 2.0.0 + Prior to this version the user needed to acknowledge settings changes. + This is no longer the case: h2 now automatically acknowledges + them. + """ + + def __init__(self): + #: A dictionary of setting byte to + #: :class:`ChangedSetting `, representing + #: the changed settings. + self.changed_settings = {} + + @classmethod + def from_settings(cls, old_settings, new_settings): + """ + Build a RemoteSettingsChanged event from a set of changed settings. + + :param old_settings: A complete collection of old settings, in the form + of a dictionary of ``{setting: value}``. + :param new_settings: All the changed settings and their new values, in + the form of a dictionary of ``{setting: value}``. + """ + e = cls() + for setting, new_value in new_settings.items(): + setting = _setting_code_from_int(setting) + original_value = old_settings.get(setting) + change = ChangedSetting(setting, original_value, new_value) + e.changed_settings[setting] = change + + return e + + def __repr__(self): + return "".format( + ", ".join(repr(cs) for cs in self.changed_settings.values()), + ) + + +class PingReceived(Event): + """ + The PingReceived event is fired whenever a PING is received. It contains + the 'opaque data' of the PING frame. A ping acknowledgment with the same + 'opaque data' is automatically emitted after receiving a ping. + + .. versionadded:: 3.1.0 + """ + + def __init__(self): + #: The data included on the ping. + self.ping_data = None + + def __repr__(self): + return "".format( + _bytes_representation(self.ping_data), + ) + + +class PingAckReceived(Event): + """ + The PingAckReceived event is fired whenever a PING acknowledgment is + received. It contains the 'opaque data' of the PING+ACK frame, allowing the + user to correlate PINGs and calculate RTT. + + .. versionadded:: 3.1.0 + + .. versionchanged:: 4.0.0 + Removed deprecated but equivalent ``PingAcknowledged``. + """ + + def __init__(self): + #: The data included on the ping. + self.ping_data = None + + def __repr__(self): + return "".format( + _bytes_representation(self.ping_data), + ) + + +class StreamEnded(Event): + """ + The StreamEnded event is fired whenever a stream is ended by a remote + party. The stream may not be fully closed if it has not been closed + locally, but no further data or headers should be expected on that stream. + """ + + def __init__(self): + #: The Stream ID of the stream that was closed. + self.stream_id = None + + def __repr__(self): + return "" % self.stream_id + + +class StreamReset(Event): + """ + The StreamReset event is fired in two situations. The first is when the + remote party forcefully resets the stream. The second is when the remote + party has made a protocol error which only affects a single stream. In this + case, h2 will terminate the stream early and return this event. + + .. versionchanged:: 2.0.0 + This event is now fired when h2 automatically resets a stream. + """ + + def __init__(self): + #: The Stream ID of the stream that was reset. + self.stream_id = None + + #: The error code given. Either one of :class:`ErrorCodes + #: ` or ``int`` + self.error_code = None + + #: Whether the remote peer sent a RST_STREAM or we did. + self.remote_reset = True + + def __repr__(self): + return "".format( + self.stream_id, self.error_code, self.remote_reset + ) + + +class PushedStreamReceived(Event): + """ + The PushedStreamReceived event is fired whenever a pushed stream has been + received from a remote peer. The event carries on it the new stream ID, the + ID of the parent stream, and the request headers pushed by the remote peer. + """ + + def __init__(self): + #: The Stream ID of the stream created by the push. + self.pushed_stream_id = None + + #: The Stream ID of the stream that the push is related to. + self.parent_stream_id = None + + #: The request headers, sent by the remote party in the push. + self.headers = None + + def __repr__(self): + return ( + "" + % ( + self.pushed_stream_id, + self.parent_stream_id, + self.headers, + ) + ) + + +class SettingsAcknowledged(Event): + """ + The SettingsAcknowledged event is fired whenever a settings ACK is received + from the remote peer. The event carries on it the settings that were + acknowedged, in the same format as + :class:`h2.events.RemoteSettingsChanged`. + """ + + def __init__(self): + #: A dictionary of setting byte to + #: :class:`ChangedSetting `, representing + #: the changed settings. + self.changed_settings = {} + + def __repr__(self): + return "".format( + ", ".join(repr(cs) for cs in self.changed_settings.values()), + ) + + +class PriorityUpdated(Event): + """ + The PriorityUpdated event is fired whenever a stream sends updated priority + information. This can occur when the stream is opened, or at any time + during the stream lifetime. + + This event is purely advisory, and does not need to be acted on. + + .. versionadded:: 2.0.0 + """ + + def __init__(self): + #: The ID of the stream whose priority information is being updated. + self.stream_id = None + + #: The new stream weight. May be the same as the original stream + #: weight. An integer between 1 and 256. + self.weight = None + + #: The stream ID this stream now depends on. May be ``0``. + self.depends_on = None + + #: Whether the stream *exclusively* depends on the parent stream. If it + #: does, this stream should inherit the current children of its new + #: parent. + self.exclusive = None + + def __repr__(self): + return ( + "" + % (self.stream_id, self.weight, self.depends_on, self.exclusive) + ) + + +class ConnectionTerminated(Event): + """ + The ConnectionTerminated event is fired when a connection is torn down by + the remote peer using a GOAWAY frame. Once received, no further action may + be taken on the connection: a new connection must be established. + """ + + def __init__(self): + #: The error code cited when tearing down the connection. Should be + #: one of :class:`ErrorCodes `, but may not be if + #: unknown HTTP/2 extensions are being used. + self.error_code = None + + #: The stream ID of the last stream the remote peer saw. This can + #: provide an indication of what data, if any, never reached the remote + #: peer and so can safely be resent. + self.last_stream_id = None + + #: Additional debug data that can be appended to GOAWAY frame. + self.additional_data = None + + def __repr__(self): + return ( + "" + % ( + self.error_code, + self.last_stream_id, + _bytes_representation( + self.additional_data[:20] if self.additional_data else None + ), + ) + ) + + +class AlternativeServiceAvailable(Event): + """ + The AlternativeServiceAvailable event is fired when the remote peer + advertises an `RFC 7838 `_ Alternative + Service using an ALTSVC frame. + + This event always carries the origin to which the ALTSVC information + applies. That origin is either supplied by the server directly, or inferred + by h2 from the ``:authority`` pseudo-header field that was sent by + the user when initiating a given stream. + + This event also carries what RFC 7838 calls the "Alternative Service Field + Value", which is formatted like a HTTP header field and contains the + relevant alternative service information. h2 does not parse or in any + way modify that information: the user is required to do that. + + This event can only be fired on the client end of a connection. + + .. versionadded:: 2.3.0 + """ + + def __init__(self): + #: The origin to which the alternative service field value applies. + #: This field is either supplied by the server directly, or inferred by + #: h2 from the ``:authority`` pseudo-header field that was sent + #: by the user when initiating the stream on which the frame was + #: received. + self.origin = None + + #: The ALTSVC field value. This contains information about the HTTP + #: alternative service being advertised by the server. h2 does + #: not parse this field: it is left exactly as sent by the server. The + #: structure of the data in this field is given by `RFC 7838 Section 3 + #: `_. + self.field_value = None + + def __repr__(self): + return "".format( + self.origin.decode("utf-8", "ignore"), + self.field_value.decode("utf-8", "ignore"), + ) + + +class UnknownFrameReceived(Event): + """ + The UnknownFrameReceived event is fired when the remote peer sends a frame + that h2 does not understand. This occurs primarily when the remote + peer is employing HTTP/2 extensions that h2 doesn't know anything + about. + + RFC 7540 requires that HTTP/2 implementations ignore these frames. h2 + does so. However, this event is fired to allow implementations to perform + special processing on those frames if needed (e.g. if the implementation + is capable of handling the frame itself). + + .. versionadded:: 2.7.0 + """ + + def __init__(self): + #: The hyperframe Frame object that encapsulates the received frame. + self.frame = None + + def __repr__(self): + return "" + + +def _bytes_representation(data): + """ + Converts a bytestring into something that is safe to print on all Python + platforms. + + This function is relatively expensive, so it should not be called on the + mainline of the code. It's safe to use in things like object repr methods + though. + """ + if data is None: + return None + + return binascii.hexlify(data).decode("ascii") diff --git a/.venv/lib/python3.9/site-packages/jh2/exceptions.py b/.venv/lib/python3.9/site-packages/jh2/exceptions.py new file mode 100644 index 0000000..45caab4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/exceptions.py @@ -0,0 +1,205 @@ +""" +h2/exceptions +~~~~~~~~~~~~~ + +Exceptions for the HTTP/2 module. +""" + +from __future__ import annotations + +from .errors import ErrorCodes + + +class H2Error(Exception): + """ + The base class for all exceptions for the HTTP/2 module. + """ + + +class ProtocolError(H2Error): + """ + An action was attempted in violation of the HTTP/2 protocol. + """ + + #: The error code corresponds to this kind of Protocol Error. + error_code = ErrorCodes.PROTOCOL_ERROR + + +class FrameTooLargeError(ProtocolError): + """ + The frame that we tried to send or that we received was too large. + """ + + #: The error code corresponds to this kind of Protocol Error. + error_code = ErrorCodes.FRAME_SIZE_ERROR + + +class FrameDataMissingError(ProtocolError): + """ + The frame that we received is missing some data. + + .. versionadded:: 2.0.0 + """ + + #: The error code corresponds to this kind of Protocol Error. + error_code = ErrorCodes.FRAME_SIZE_ERROR + + +class TooManyStreamsError(ProtocolError): + """ + An attempt was made to open a stream that would lead to too many concurrent + streams. + """ + + pass + + +class FlowControlError(ProtocolError): + """ + An attempted action violates flow control constraints. + """ + + #: The error code corresponds to this kind of Protocol Error. + error_code = ErrorCodes.FLOW_CONTROL_ERROR + + +class StreamIDTooLowError(ProtocolError): + """ + An attempt was made to open a stream that had an ID that is lower than the + highest ID we have seen on this connection. + """ + + def __init__(self, stream_id, max_stream_id): + #: The ID of the stream that we attempted to open. + self.stream_id = stream_id + + #: The current highest-seen stream ID. + self.max_stream_id = max_stream_id + + def __str__(self): + return "StreamIDTooLowError: %d is lower than %d" % ( + self.stream_id, + self.max_stream_id, + ) + + +class NoAvailableStreamIDError(ProtocolError): + """ + There are no available stream IDs left to the connection. All stream IDs + have been exhausted. + + .. versionadded:: 2.0.0 + """ + + pass + + +class NoSuchStreamError(ProtocolError): + """ + A stream-specific action referenced a stream that does not exist. + + .. versionchanged:: 2.0.0 + Became a subclass of :class:`ProtocolError + ` + """ + + def __init__(self, stream_id): + #: The stream ID corresponds to the non-existent stream. + self.stream_id = stream_id + + +class StreamClosedError(NoSuchStreamError): + """ + A more specific form of + :class:`NoSuchStreamError `. Indicates + that the stream has since been closed, and that all state relating to that + stream has been removed. + """ + + def __init__(self, stream_id): + #: The stream ID corresponds to the nonexistent stream. + self.stream_id = stream_id + + #: The relevant HTTP/2 error code. + self.error_code = ErrorCodes.STREAM_CLOSED + + # Any events that internal code may need to fire. Not relevant to + # external users that may receive a StreamClosedError. + self._events = [] + + +class InvalidSettingsValueError(ProtocolError, ValueError): + """ + An attempt was made to set an invalid Settings value. + + .. versionadded:: 2.0.0 + """ + + def __init__(self, msg, error_code): + super().__init__(msg) + self.error_code = error_code + + +class InvalidBodyLengthError(ProtocolError): + """ + The remote peer sent more or less data that the Content-Length header + indicated. + + .. versionadded:: 2.0.0 + """ + + def __init__(self, expected, actual): + self.expected_length = expected + self.actual_length = actual + + def __str__(self): + return "InvalidBodyLengthError: Expected %d bytes, received %d" % ( + self.expected_length, + self.actual_length, + ) + + +class UnsupportedFrameError(ProtocolError): + """ + The remote peer sent a frame that is unsupported in this context. + + .. versionadded:: 2.1.0 + + .. versionchanged:: 4.0.0 + Removed deprecated KeyError parent class. + """ + + pass + + +class RFC1122Error(H2Error): + """ + Emitted when users attempt to do something that is literally allowed by the + relevant RFC, but is sufficiently ill-defined that it's unwise to allow + users to actually do it. + + While there is some disagreement about whether or not we should be liberal + in what accept, it is a truth universally acknowledged that we should be + conservative in what emit. + + .. versionadded:: 2.4.0 + """ + + # shazow says I'm going to regret naming the exception this way. If that + # turns out to be true, TELL HIM NOTHING. + pass + + +class DenialOfServiceError(ProtocolError): + """ + Emitted when the remote peer exhibits a behaviour that is likely to be an + attempt to perform a Denial of Service attack on the implementation. This + is a form of ProtocolError that carries a different error code, and allows + more easy detection of this kind of behaviour. + + .. versionadded:: 2.5.0 + """ + + #: The error code corresponds to this kind of + #: :class:`ProtocolError ` + error_code = ErrorCodes.ENHANCE_YOUR_CALM diff --git a/.venv/lib/python3.9/site-packages/jh2/frame_buffer.py b/.venv/lib/python3.9/site-packages/jh2/frame_buffer.py new file mode 100644 index 0000000..8ded52f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/frame_buffer.py @@ -0,0 +1,158 @@ +""" +h2/frame_buffer +~~~~~~~~~~~~~~~ + +A data structure that provides a way to iterate over a byte buffer in terms of +frames. +""" + +from __future__ import annotations + +from .exceptions import FrameDataMissingError, FrameTooLargeError, ProtocolError +from .hyperframe.exceptions import InvalidDataError, InvalidFrameError +from .hyperframe.frame import ContinuationFrame, Frame, HeadersFrame, PushPromiseFrame + +# To avoid a DOS attack based on sending loads of continuation frames, we limit +# the maximum number we're perpared to receive. In this case, we'll set the +# limit to 64, which means the largest encoded header block we can receive by +# default is 262144 bytes long, and the largest possible *at all* is 1073741760 +# bytes long. +# +# This value seems reasonable for now, but in future we may want to evaluate +# making it configurable. +CONTINUATION_BACKLOG = 64 + + +class FrameBuffer: + """ + This is a data structure that expects to act as a buffer for HTTP/2 data + that allows iteraton in terms of H2 frames. + """ + + def __init__(self, server=False): + self._data = bytearray() + self.max_frame_size = 0 + self._preamble = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" if server else b"" + self._preamble_len = len(self._preamble) + self._headers_buffer = [] + + def add_data(self, data): + """ + Add more data to the frame buffer. + + :param data: A bytestring containing the byte buffer. + """ + if self._preamble_len: + data_len = len(data) + of_which_preamble = min(self._preamble_len, data_len) + + if self._preamble[:of_which_preamble] != data[:of_which_preamble]: + raise ProtocolError("Invalid HTTP/2 preamble.") + + data = data[of_which_preamble:] + self._preamble_len -= of_which_preamble + self._preamble = self._preamble[of_which_preamble:] + + self._data += data + + def _validate_frame_length(self, length): + """ + Confirm that the frame is an appropriate length. + """ + if length > self.max_frame_size: + raise FrameTooLargeError( + "Received overlong frame: length %d, max %d" + % (length, self.max_frame_size) + ) + + def _update_header_buffer(self, f): + """ + Updates the internal header buffer. Returns a frame that should replace + the current one. May throw exceptions if this frame is invalid. + """ + # Check if we're in the middle of a headers block. If we are, this + # frame *must* be a CONTINUATION frame with the same stream ID as the + # leading HEADERS or PUSH_PROMISE frame. Anything else is a + # ProtocolError. If the frame *is* valid, append it to the header + # buffer. + if self._headers_buffer: + stream_id = self._headers_buffer[0].stream_id + valid_frame = ( + f is not None + and isinstance(f, ContinuationFrame) + and f.stream_id == stream_id + ) + if not valid_frame: + raise ProtocolError("Invalid frame during header block.") + + # Append the frame to the buffer. + self._headers_buffer.append(f) + if len(self._headers_buffer) > CONTINUATION_BACKLOG: + raise ProtocolError("Too many continuation frames received.") + + # If this is the end of the header block, then we want to build a + # mutant HEADERS frame that's massive. Use the original one we got, + # then set END_HEADERS and set its data appopriately. If it's not + # the end of the block, lose the current frame: we can't yield it. + if "END_HEADERS" in f.flags: + f = self._headers_buffer[0] + f.flags.add("END_HEADERS") + f.data = b"".join(x.data for x in self._headers_buffer) + self._headers_buffer = [] + else: + f = None + elif ( + isinstance(f, (HeadersFrame, PushPromiseFrame)) + and "END_HEADERS" not in f.flags + ): + # This is the start of a headers block! Save the frame off and then + # act like we didn't receive one. + self._headers_buffer.append(f) + f = None + + return f + + # The methods below support the iterator protocol. + def __iter__(self): + return self + + def __next__(self): + # First, check that we have enough data to successfully parse the + # next frame header. If not, bail. Otherwise, parse it. + if len(self._data) < 9: + raise StopIteration() + + try: + f, length = Frame.parse_frame_header(memoryview(self._data[:9])) + except (InvalidDataError, InvalidFrameError) as e: # pragma: no cover + raise ProtocolError("Received frame with invalid header: %s" % str(e)) + + # Next, check that we have enough length to parse the frame body. If + # not, bail, leaving the frame header data in the buffer for next time. + if len(self._data) < length + 9: + raise StopIteration() + + # Confirm the frame has an appropriate length. + self._validate_frame_length(length) + + # Try to parse the frame body + try: + f.parse_body(memoryview(self._data[9 : 9 + length])) + except InvalidDataError: + raise ProtocolError("Received frame with non-compliant data") + except InvalidFrameError: + raise FrameDataMissingError("Frame data missing or invalid") + + # At this point, as we know we'll use or discard the entire frame, we + # can update the data. + self._data = self._data[9 + length :] + + # Pass the frame through the header buffer. + f = self._update_header_buffer(f) + + # If we got a frame we didn't understand or shouldn't yield, rather + # than return None it'd be better if we just tried to get the next + # frame in the sequence instead. Recurse back into ourselves to do + # that. This is safe because the amount of work we have to do here is + # strictly bounded by the length of the buffer. + return f if f is not None else self.__next__() diff --git a/.venv/lib/python3.9/site-packages/jh2/hpack/__init__.py b/.venv/lib/python3.9/site-packages/jh2/hpack/__init__.py new file mode 100644 index 0000000..2fd2a15 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/hpack/__init__.py @@ -0,0 +1,30 @@ +""" +hpack +~~~~~ + +HTTP/2 header encoding for Python. +""" + +from __future__ import annotations + +from .exceptions import ( + HPACKDecodingError, + HPACKError, + InvalidTableIndex, + InvalidTableSizeError, + OversizedHeaderListError, +) +from .hpack import Decoder, Encoder +from .struct import HeaderTuple, NeverIndexedHeaderTuple + +__all__ = [ + "Encoder", + "Decoder", + "HeaderTuple", + "NeverIndexedHeaderTuple", + "HPACKError", + "HPACKDecodingError", + "InvalidTableIndex", + "OversizedHeaderListError", + "InvalidTableSizeError", +] diff --git a/.venv/lib/python3.9/site-packages/jh2/hpack/exceptions.py b/.venv/lib/python3.9/site-packages/jh2/hpack/exceptions.py new file mode 100644 index 0000000..bdb5f05 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/hpack/exceptions.py @@ -0,0 +1,55 @@ +""" +hyper/http20/exceptions +~~~~~~~~~~~~~~~~~~~~~~~ + +This defines exceptions used in the HTTP/2 portion of hyper. +""" + +from __future__ import annotations + + +class HPACKError(Exception): + """ + The base class for all ``hpack`` exceptions. + """ + + pass + + +class HPACKDecodingError(HPACKError): + """ + An error has been encountered while performing HPACK decoding. + """ + + pass + + +class InvalidTableIndex(HPACKDecodingError): + """ + An invalid table index was received. + """ + + pass + + +class OversizedHeaderListError(HPACKDecodingError): + """ + A header list that was larger than we allow has been received. This may be + a DoS attack. + + .. versionadded:: 2.3.0 + """ + + pass + + +class InvalidTableSizeError(HPACKDecodingError): + """ + An attempt was made to change the decoder table size to a value larger than + allowed, or the list was shrunk and the remote peer didn't shrink their + table size. + + .. versionadded:: 3.0.0 + """ + + pass diff --git a/.venv/lib/python3.9/site-packages/jh2/hpack/hpack.py b/.venv/lib/python3.9/site-packages/jh2/hpack/hpack.py new file mode 100644 index 0000000..50fcce2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/hpack/hpack.py @@ -0,0 +1,610 @@ +""" +hpack/hpack +~~~~~~~~~~~ + +Implements the HPACK header compression algorithm as detailed by the IETF. +""" + +from __future__ import annotations + +import logging + +from .exceptions import ( + HPACKDecodingError, + InvalidTableSizeError, + OversizedHeaderListError, +) +from .huffman import HuffmanEncoder +from .huffman_constants import REQUEST_CODES, REQUEST_CODES_LENGTH +from .huffman_table import decode_huffman +from .struct import HeaderTuple, NeverIndexedHeaderTuple +from .table import HeaderTable, table_entry_size + +log = logging.getLogger(__name__) + +INDEX_NONE = b"\x00" +INDEX_NEVER = b"\x10" +INDEX_INCREMENTAL = b"\x40" + +# Precompute 2^i for 1-8 for use in prefix calcs. +# Zero index is not used but there to save a subtraction +# as prefix numbers are not zero indexed. +_PREFIX_BIT_MAX_NUMBERS = [(2**i) - 1 for i in range(9)] + +basestring = (str, bytes) + + +# We default the maximum header list we're willing to accept to 64kB. That's a +# lot of headers, but if applications want to raise it they can do. +DEFAULT_MAX_HEADER_LIST_SIZE = 2**16 + + +def _unicode_if_needed(header, raw): + """ + Provides a header as a unicode string if raw is False, otherwise returns + it as a bytestring. + """ + name = bytes(header[0]) + value = bytes(header[1]) + if not raw: + name = name.decode("utf-8") + value = value.decode("utf-8") + return header.__class__(name, value) + + +def encode_integer(integer, prefix_bits): + """ + This encodes an integer according to the wacky integer encoding rules + defined in the HPACK spec. + """ + log.debug("Encoding %d with %d bits", integer, prefix_bits) + + if integer < 0: + raise ValueError("Can only encode positive integers, got %s" % integer) + + if prefix_bits < 1 or prefix_bits > 8: + raise ValueError("Prefix bits must be between 1 and 8, got %s" % prefix_bits) + + max_number = _PREFIX_BIT_MAX_NUMBERS[prefix_bits] + + if integer < max_number: + return bytearray([integer]) # Seriously? + else: + elements = [max_number] + integer -= max_number + + while integer >= 128: + elements.append((integer & 127) + 128) + integer >>= 7 + + elements.append(integer) + + return bytearray(elements) + + +def decode_integer(data, prefix_bits): + """ + This decodes an integer according to the wacky integer encoding rules + defined in the HPACK spec. Returns a tuple of the decoded integer and the + number of bytes that were consumed from ``data`` in order to get that + integer. + """ + if prefix_bits < 1 or prefix_bits > 8: + raise ValueError("Prefix bits must be between 1 and 8, got %s" % prefix_bits) + + max_number = _PREFIX_BIT_MAX_NUMBERS[prefix_bits] + index = 1 + shift = 0 + mask = 0xFF >> (8 - prefix_bits) + + try: + number = data[0] & mask + if number == max_number: + while True: + next_byte = data[index] + index += 1 + + if next_byte >= 128: + number += (next_byte - 128) << shift + else: + number += next_byte << shift + break + shift += 7 + + except IndexError: + raise HPACKDecodingError( + "Unable to decode HPACK integer representation from %r" % data + ) + + log.debug("Decoded %d, consumed %d bytes", number, index) + + return number, index + + +def _dict_to_iterable(header_dict): + """ + This converts a dictionary to an iterable of two-tuples. This is a + HPACK-specific function because it pulls "special-headers" out first and + then emits them. + """ + assert isinstance(header_dict, dict) + keys = sorted(header_dict.keys(), key=lambda k: not _to_bytes(k).startswith(b":")) + for key in keys: + yield key, header_dict[key] + + +def _to_bytes(string): + """ + Convert string to bytes. + """ + if not isinstance(string, basestring): # pragma: no cover + string = str(string) + + return string if isinstance(string, bytes) else string.encode("utf-8") + + +class Encoder: + """ + An HPACK encoder object. This object takes HTTP headers and emits encoded + HTTP/2 header blocks. + """ + + def __init__(self): + self.header_table = HeaderTable() + self.huffman_coder = HuffmanEncoder(REQUEST_CODES, REQUEST_CODES_LENGTH) + self.table_size_changes = [] + + @property + def header_table_size(self): + """ + Controls the size of the HPACK header table. + """ + return self.header_table.maxsize + + @header_table_size.setter + def header_table_size(self, value): + self.header_table.maxsize = value + if self.header_table.resized: + self.table_size_changes.append(value) + + def encode(self, headers, huffman=True): + """ + Takes a set of headers and encodes them into a HPACK-encoded header + block. + + :param headers: The headers to encode. Must be either an iterable of + tuples, an iterable of :class:`HeaderTuple + `, or a ``dict``. + + If an iterable of tuples, the tuples may be either + two-tuples or three-tuples. If they are two-tuples, the + tuples must be of the format ``(name, value)``. If they + are three-tuples, they must be of the format + ``(name, value, sensitive)``, where ``sensitive`` is a + boolean value indicating whether the header should be + added to header tables anywhere. If not present, + ``sensitive`` defaults to ``False``. + + If an iterable of :class:`HeaderTuple + `, the tuples must always be + two-tuples. Instead of using ``sensitive`` as a third + tuple entry, use :class:`NeverIndexedHeaderTuple + ` to request that + the field never be indexed. + + .. warning:: HTTP/2 requires that all special headers + (headers whose names begin with ``:`` characters) + appear at the *start* of the header block. While + this method will ensure that happens for ``dict`` + subclasses, callers using any other iterable of + tuples **must** ensure they place their special + headers at the start of the iterable. + + For efficiency reasons users should prefer to use + iterables of two-tuples: fixing the ordering of + dictionary headers is an expensive operation that + should be avoided if possible. + + :param huffman: (optional) Whether to Huffman-encode any header sent as + a literal value. Except for use when debugging, it is + recommended that this be left enabled. + + :returns: A bytestring containing the HPACK-encoded header block. + """ + # Transforming the headers into a header block is a procedure that can + # be modeled as a chain or pipe. First, the headers are encoded. This + # encoding can be done a number of ways. If the header name-value pair + # are already in the header table we can represent them using the + # indexed representation: the same is true if they are in the static + # table. Otherwise, a literal representation will be used. + header_block = [] + + # Turn the headers into a list of tuples if possible. This is the + # natural way to interact with them in HPACK. Because dictionaries are + # un-ordered, we need to make sure we grab the "special" headers first. + if isinstance(headers, dict): + headers = _dict_to_iterable(headers) + + # Before we begin, if the header table size has been changed we need + # to signal all changes since last emission appropriately. + if self.header_table.resized: + header_block.append(self._encode_table_size_change()) + self.header_table.resized = False + + # Add each header to the header block + for header in headers: + sensitive = False + if isinstance(header, HeaderTuple): + sensitive = not header.indexable + elif len(header) > 2: + sensitive = header[2] + + header = (_to_bytes(header[0]), _to_bytes(header[1])) + header_block.append(self.add(header, sensitive, huffman)) + + header_block = b"".join(header_block) + + log.debug("Encoded header block to %s", header_block) + + return header_block + + def add(self, to_add, sensitive, huffman=False): + """ + This function takes a header key-value tuple and serializes it. + """ + log.debug( + "Adding %s to the header table, sensitive:%s, huffman:%s", + to_add, + sensitive, + huffman, + ) + + name, value = to_add + + # Set our indexing mode + indexbit = INDEX_INCREMENTAL if not sensitive else INDEX_NEVER + + # Search for a matching header in the header table. + match = self.header_table.search(name, value) + + if match is None: + # Not in the header table. Encode using the literal syntax, + # and add it to the header table. + encoded = self._encode_literal(name, value, indexbit, huffman) + if not sensitive: + self.header_table.add(name, value) + return encoded + + # The header is in the table, break out the values. If we matched + # perfectly, we can use the indexed representation: otherwise we + # can use the indexed literal. + index, name, perfect = match + + if perfect: + # Indexed representation. + encoded = self._encode_indexed(index) + else: + # Indexed literal. We are going to add header to the + # header table unconditionally. It is a future todo to + # filter out headers which are known to be ineffective for + # indexing since they just take space in the table and + # pushed out other valuable headers. + encoded = self._encode_indexed_literal(index, value, indexbit, huffman) + if not sensitive: + self.header_table.add(name, value) + + return encoded + + def _encode_indexed(self, index): + """ + Encodes a header using the indexed representation. + """ + field = encode_integer(index, 7) + field[0] |= 0x80 # we set the top bit + return bytes(field) + + def _encode_literal(self, name, value, indexbit, huffman=False): + """ + Encodes a header with a literal name and literal value. If ``indexing`` + is True, the header will be added to the header table: otherwise it + will not. + """ + if huffman: + name = self.huffman_coder.encode(name) + value = self.huffman_coder.encode(value) + + name_len = encode_integer(len(name), 7) + value_len = encode_integer(len(value), 7) + + if huffman: + name_len[0] |= 0x80 + value_len[0] |= 0x80 + + return b"".join([indexbit, bytes(name_len), name, bytes(value_len), value]) + + def _encode_indexed_literal(self, index, value, indexbit, huffman=False): + """ + Encodes a header with an indexed name and a literal value and performs + incremental indexing. + """ + if indexbit != INDEX_INCREMENTAL: + prefix = encode_integer(index, 4) + else: + prefix = encode_integer(index, 6) + + prefix[0] |= ord(indexbit) + + if huffman: + value = self.huffman_coder.encode(value) + + value_len = encode_integer(len(value), 7) + + if huffman: + value_len[0] |= 0x80 + + return b"".join([bytes(prefix), bytes(value_len), value]) + + def _encode_table_size_change(self): + """ + Produces the encoded form of all header table size change context + updates. + """ + block = b"" + for size_bytes in self.table_size_changes: + size_bytes = encode_integer(size_bytes, 5) + size_bytes[0] |= 0x20 + block += bytes(size_bytes) + self.table_size_changes = [] + return block + + +class Decoder: + """ + An HPACK decoder object. + + .. versionchanged:: 2.3.0 + Added ``max_header_list_size`` argument. + + :param max_header_list_size: The maximum decompressed size we will allow + for any single header block. This is a protection against DoS attacks + that attempt to force the application to expand a relatively small + amount of data into a really large header list, allowing enormous + amounts of memory to be allocated. + + If this amount of data is exceeded, a `OversizedHeaderListError + ` exception will be raised. At this + point the connection should be shut down, as the HPACK state will no + longer be usable. + + Defaults to 64kB. + :type max_header_list_size: ``int`` + """ + + def __init__(self, max_header_list_size=DEFAULT_MAX_HEADER_LIST_SIZE): + self.header_table = HeaderTable() + + #: The maximum decompressed size we will allow for any single header + #: block. This is a protection against DoS attacks that attempt to + #: force the application to expand a relatively small amount of data + #: into a really large header list, allowing enormous amounts of memory + #: to be allocated. + #: + #: If this amount of data is exceeded, a `OversizedHeaderListError + #: ` exception will be raised. At this + #: point the connection should be shut down, as the HPACK state will no + #: longer be usable. + #: + #: Defaults to 64kB. + #: + #: .. versionadded:: 2.3.0 + self.max_header_list_size = max_header_list_size + + #: Maximum allowed header table size. + #: + #: A HTTP/2 implementation should set this to the most recent value of + #: SETTINGS_HEADER_TABLE_SIZE that it sent *and has received an ACK + #: for*. Once this setting is set, the actual header table size will be + #: checked at the end of each decoding run and whenever it is changed, + #: to confirm that it fits in this size. + self.max_allowed_table_size = self.header_table.maxsize + + @property + def header_table_size(self): + """ + Controls the size of the HPACK header table. + """ + return self.header_table.maxsize + + @header_table_size.setter + def header_table_size(self, value): + self.header_table.maxsize = value + + def decode(self, data, raw=False): + """ + Takes an HPACK-encoded header block and decodes it into a header set. + + :param data: A bytestring representing a complete HPACK-encoded header + block. + :param raw: (optional) Whether to return the headers as tuples of raw + byte strings or to decode them as UTF-8 before returning + them. The default value is False, which returns tuples of + Unicode strings + :returns: A list of two-tuples of ``(name, value)`` representing the + HPACK-encoded headers, in the order they were decoded. + :raises HPACKDecodingError: If an error is encountered while decoding + the header block. + """ + log.debug("Decoding %s", data) + + data_mem = memoryview(data) + headers = [] + data_len = len(data) + inflated_size = 0 + current_index = 0 + + while current_index < data_len: + # Work out what kind of header we're decoding. + # If the high bit is 1, it's an indexed field. + current = data[current_index] + indexed = True if current & 0x80 else False + + # Otherwise, if the second-highest bit is 1 it's a field that does + # alter the header table. + literal_index = True if current & 0x40 else False + + # Otherwise, if the third-highest bit is 1 it's an encoding context + # update. + encoding_update = True if current & 0x20 else False + + if indexed: + header, consumed = self._decode_indexed(data_mem[current_index:]) + elif literal_index: + # It's a literal header that does affect the header table. + header, consumed = self._decode_literal_index(data_mem[current_index:]) + elif encoding_update: + # It's an update to the encoding context. These are forbidden + # in a header block after any actual header. + if headers: + raise HPACKDecodingError( + "Table size update not at the start of the block" + ) + consumed = self._update_encoding_context(data_mem[current_index:]) + header = None + else: + # It's a literal header that does not affect the header table. + header, consumed = self._decode_literal_no_index( + data_mem[current_index:] + ) + + if header: + headers.append(header) + inflated_size += table_entry_size(*header) + + if inflated_size > self.max_header_list_size: + raise OversizedHeaderListError( + "A header list larger than %d has been received" + % self.max_header_list_size + ) + + current_index += consumed + + # Confirm that the table size is lower than the maximum. We do this + # here to ensure that we catch when the max has been *shrunk* and the + # remote peer hasn't actually done that. + self._assert_valid_table_size() + + try: + return [_unicode_if_needed(h, raw) for h in headers] + except UnicodeDecodeError: + raise HPACKDecodingError("Unable to decode headers as UTF-8.") + + def _assert_valid_table_size(self): + """ + Check that the table size set by the encoder is lower than the maximum + we expect to have. + """ + if self.header_table_size > self.max_allowed_table_size: + raise InvalidTableSizeError( + "Encoder did not shrink table size to within the max" + ) + + def _update_encoding_context(self, data): + """ + Handles a byte that updates the encoding context. + """ + # We've been asked to resize the header table. + new_size, consumed = decode_integer(data, 5) + if new_size > self.max_allowed_table_size: + raise InvalidTableSizeError("Encoder exceeded max allowable table size") + self.header_table_size = new_size + return consumed + + def _decode_indexed(self, data): + """ + Decodes a header represented using the indexed representation. + """ + index, consumed = decode_integer(data, 7) + header = HeaderTuple(*self.header_table.get_by_index(index)) + log.debug("Decoded %s, consumed %d", header, consumed) + return header, consumed + + def _decode_literal_no_index(self, data): + return self._decode_literal(data, False) + + def _decode_literal_index(self, data): + return self._decode_literal(data, True) + + def _decode_literal(self, data, should_index): + """ + Decodes a header represented with a literal. + """ + total_consumed = 0 + + # When should_index is true, if the low six bits of the first byte are + # nonzero, the header name is indexed. + # When should_index is false, if the low four bits of the first byte + # are nonzero the header name is indexed. + if should_index: + indexed_name = data[0] & 0x3F + name_len = 6 + not_indexable = False + else: + high_byte = data[0] + indexed_name = high_byte & 0x0F + name_len = 4 + not_indexable = high_byte & 0x10 + + if indexed_name: + # Indexed header name. + index, consumed = decode_integer(data, name_len) + name = self.header_table.get_by_index(index)[0] + + total_consumed = consumed + length = 0 + else: + # Literal header name. The first byte was consumed, so we need to + # move forward. + data = data[1:] + + length, consumed = decode_integer(data, 7) + name = data[consumed : consumed + length] + if len(name) != length: + raise HPACKDecodingError("Truncated header block") + + if data[0] & 0x80: + name = decode_huffman(name) + total_consumed = consumed + length + 1 # Since we moved forward 1. + + data = data[consumed + length :] + + # The header value is definitely length-based. + length, consumed = decode_integer(data, 7) + value = data[consumed : consumed + length] + if len(value) != length: + raise HPACKDecodingError("Truncated header block") + + if data[0] & 0x80: + value = decode_huffman(value) + + # Updated the total consumed length. + total_consumed += length + consumed + + # If we have been told never to index the header field, encode that in + # the tuple we use. + if not_indexable: + header = NeverIndexedHeaderTuple(name, value) + else: + header = HeaderTuple(name, value) + + # If we've been asked to index this, add it to the header table. + if should_index: + self.header_table.add(name, value) + + log.debug( + "Decoded %s, total consumed %d bytes, indexed %s", + header, + total_consumed, + should_index, + ) + + return header, total_consumed diff --git a/.venv/lib/python3.9/site-packages/jh2/hpack/huffman.py b/.venv/lib/python3.9/site-packages/jh2/hpack/huffman.py new file mode 100644 index 0000000..330935b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/hpack/huffman.py @@ -0,0 +1,66 @@ +""" +hpack/huffman_decoder +~~~~~~~~~~~~~~~~~~~~~ + +An implementation of a bitwise prefix tree specially built for decoding +Huffman-coded content where we already know the Huffman table. +""" + +from __future__ import annotations + + +class HuffmanEncoder: + """ + Encodes a string according to the Huffman encoding table defined in the + HPACK specification. + """ + + def __init__(self, huffman_code_list, huffman_code_list_lengths): + self.huffman_code_list = huffman_code_list + self.huffman_code_list_lengths = huffman_code_list_lengths + + def encode(self, bytes_to_encode): + """ + Given a string of bytes, encodes them according to the HPACK Huffman + specification. + """ + # If handed the empty string, just immediately return. + if not bytes_to_encode: + return b"" + + final_num = 0 + final_int_len = 0 + + # Turn each byte into its huffman code. These codes aren't necessarily + # octet aligned, so keep track of how far through an octet we are. To + # handle this cleanly, just use a single giant integer. + for byte in bytes_to_encode: + bin_int_len = self.huffman_code_list_lengths[byte] + bin_int = self.huffman_code_list[byte] & (2 ** (bin_int_len + 1) - 1) + final_num <<= bin_int_len + final_num |= bin_int + final_int_len += bin_int_len + + # Pad out to an octet with ones. + bits_to_be_padded = (8 - (final_int_len % 8)) % 8 + final_num <<= bits_to_be_padded + final_num |= (1 << bits_to_be_padded) - 1 + + # Convert the number to hex and strip off the leading '0x' and the + # trailing 'L', if present. + final_num = hex(final_num)[2:].rstrip("L") + + # If this is odd, prepend a zero. + final_num = "0" + final_num if len(final_num) % 2 != 0 else final_num + + # This number should have twice as many digits as bytes. If not, we're + # missing some leading zeroes. Work out how many bytes we want and how + # many digits we have, then add the missing zero digits to the front. + total_bytes = (final_int_len + bits_to_be_padded) // 8 + expected_digits = total_bytes * 2 + + if len(final_num) != expected_digits: + missing_digits = expected_digits - len(final_num) + final_num = ("0" * missing_digits) + final_num + + return bytes.fromhex(final_num) diff --git a/.venv/lib/python3.9/site-packages/jh2/hpack/huffman_constants.py b/.venv/lib/python3.9/site-packages/jh2/hpack/huffman_constants.py new file mode 100644 index 0000000..7703755 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/hpack/huffman_constants.py @@ -0,0 +1,530 @@ +""" +hpack/huffman_constants +~~~~~~~~~~~~~~~~~~~~~~~ + +Defines the constant Huffman table. This takes up an upsetting amount of space, +but c'est la vie. +""" + +from __future__ import annotations + +# flake8: noqa +REQUEST_CODES = [ + 0x1FF8, + 0x7FFFD8, + 0xFFFFFE2, + 0xFFFFFE3, + 0xFFFFFE4, + 0xFFFFFE5, + 0xFFFFFE6, + 0xFFFFFE7, + 0xFFFFFE8, + 0xFFFFEA, + 0x3FFFFFFC, + 0xFFFFFE9, + 0xFFFFFEA, + 0x3FFFFFFD, + 0xFFFFFEB, + 0xFFFFFEC, + 0xFFFFFED, + 0xFFFFFEE, + 0xFFFFFEF, + 0xFFFFFF0, + 0xFFFFFF1, + 0xFFFFFF2, + 0x3FFFFFFE, + 0xFFFFFF3, + 0xFFFFFF4, + 0xFFFFFF5, + 0xFFFFFF6, + 0xFFFFFF7, + 0xFFFFFF8, + 0xFFFFFF9, + 0xFFFFFFA, + 0xFFFFFFB, + 0x14, + 0x3F8, + 0x3F9, + 0xFFA, + 0x1FF9, + 0x15, + 0xF8, + 0x7FA, + 0x3FA, + 0x3FB, + 0xF9, + 0x7FB, + 0xFA, + 0x16, + 0x17, + 0x18, + 0x0, + 0x1, + 0x2, + 0x19, + 0x1A, + 0x1B, + 0x1C, + 0x1D, + 0x1E, + 0x1F, + 0x5C, + 0xFB, + 0x7FFC, + 0x20, + 0xFFB, + 0x3FC, + 0x1FFA, + 0x21, + 0x5D, + 0x5E, + 0x5F, + 0x60, + 0x61, + 0x62, + 0x63, + 0x64, + 0x65, + 0x66, + 0x67, + 0x68, + 0x69, + 0x6A, + 0x6B, + 0x6C, + 0x6D, + 0x6E, + 0x6F, + 0x70, + 0x71, + 0x72, + 0xFC, + 0x73, + 0xFD, + 0x1FFB, + 0x7FFF0, + 0x1FFC, + 0x3FFC, + 0x22, + 0x7FFD, + 0x3, + 0x23, + 0x4, + 0x24, + 0x5, + 0x25, + 0x26, + 0x27, + 0x6, + 0x74, + 0x75, + 0x28, + 0x29, + 0x2A, + 0x7, + 0x2B, + 0x76, + 0x2C, + 0x8, + 0x9, + 0x2D, + 0x77, + 0x78, + 0x79, + 0x7A, + 0x7B, + 0x7FFE, + 0x7FC, + 0x3FFD, + 0x1FFD, + 0xFFFFFFC, + 0xFFFE6, + 0x3FFFD2, + 0xFFFE7, + 0xFFFE8, + 0x3FFFD3, + 0x3FFFD4, + 0x3FFFD5, + 0x7FFFD9, + 0x3FFFD6, + 0x7FFFDA, + 0x7FFFDB, + 0x7FFFDC, + 0x7FFFDD, + 0x7FFFDE, + 0xFFFFEB, + 0x7FFFDF, + 0xFFFFEC, + 0xFFFFED, + 0x3FFFD7, + 0x7FFFE0, + 0xFFFFEE, + 0x7FFFE1, + 0x7FFFE2, + 0x7FFFE3, + 0x7FFFE4, + 0x1FFFDC, + 0x3FFFD8, + 0x7FFFE5, + 0x3FFFD9, + 0x7FFFE6, + 0x7FFFE7, + 0xFFFFEF, + 0x3FFFDA, + 0x1FFFDD, + 0xFFFE9, + 0x3FFFDB, + 0x3FFFDC, + 0x7FFFE8, + 0x7FFFE9, + 0x1FFFDE, + 0x7FFFEA, + 0x3FFFDD, + 0x3FFFDE, + 0xFFFFF0, + 0x1FFFDF, + 0x3FFFDF, + 0x7FFFEB, + 0x7FFFEC, + 0x1FFFE0, + 0x1FFFE1, + 0x3FFFE0, + 0x1FFFE2, + 0x7FFFED, + 0x3FFFE1, + 0x7FFFEE, + 0x7FFFEF, + 0xFFFEA, + 0x3FFFE2, + 0x3FFFE3, + 0x3FFFE4, + 0x7FFFF0, + 0x3FFFE5, + 0x3FFFE6, + 0x7FFFF1, + 0x3FFFFE0, + 0x3FFFFE1, + 0xFFFEB, + 0x7FFF1, + 0x3FFFE7, + 0x7FFFF2, + 0x3FFFE8, + 0x1FFFFEC, + 0x3FFFFE2, + 0x3FFFFE3, + 0x3FFFFE4, + 0x7FFFFDE, + 0x7FFFFDF, + 0x3FFFFE5, + 0xFFFFF1, + 0x1FFFFED, + 0x7FFF2, + 0x1FFFE3, + 0x3FFFFE6, + 0x7FFFFE0, + 0x7FFFFE1, + 0x3FFFFE7, + 0x7FFFFE2, + 0xFFFFF2, + 0x1FFFE4, + 0x1FFFE5, + 0x3FFFFE8, + 0x3FFFFE9, + 0xFFFFFFD, + 0x7FFFFE3, + 0x7FFFFE4, + 0x7FFFFE5, + 0xFFFEC, + 0xFFFFF3, + 0xFFFED, + 0x1FFFE6, + 0x3FFFE9, + 0x1FFFE7, + 0x1FFFE8, + 0x7FFFF3, + 0x3FFFEA, + 0x3FFFEB, + 0x1FFFFEE, + 0x1FFFFEF, + 0xFFFFF4, + 0xFFFFF5, + 0x3FFFFEA, + 0x7FFFF4, + 0x3FFFFEB, + 0x7FFFFE6, + 0x3FFFFEC, + 0x3FFFFED, + 0x7FFFFE7, + 0x7FFFFE8, + 0x7FFFFE9, + 0x7FFFFEA, + 0x7FFFFEB, + 0xFFFFFFE, + 0x7FFFFEC, + 0x7FFFFED, + 0x7FFFFEE, + 0x7FFFFEF, + 0x7FFFFF0, + 0x3FFFFEE, + 0x3FFFFFFF, +] + +REQUEST_CODES_LENGTH = [ + 13, + 23, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 24, + 30, + 28, + 28, + 30, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 30, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 6, + 10, + 10, + 12, + 13, + 6, + 8, + 11, + 10, + 10, + 8, + 11, + 8, + 6, + 6, + 6, + 5, + 5, + 5, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 7, + 8, + 15, + 6, + 12, + 10, + 13, + 6, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 8, + 7, + 8, + 13, + 19, + 13, + 14, + 6, + 15, + 5, + 6, + 5, + 6, + 5, + 6, + 6, + 6, + 5, + 7, + 7, + 6, + 6, + 6, + 5, + 6, + 7, + 6, + 5, + 5, + 6, + 7, + 7, + 7, + 7, + 7, + 15, + 11, + 14, + 13, + 28, + 20, + 22, + 20, + 20, + 22, + 22, + 22, + 23, + 22, + 23, + 23, + 23, + 23, + 23, + 24, + 23, + 24, + 24, + 22, + 23, + 24, + 23, + 23, + 23, + 23, + 21, + 22, + 23, + 22, + 23, + 23, + 24, + 22, + 21, + 20, + 22, + 22, + 23, + 23, + 21, + 23, + 22, + 22, + 24, + 21, + 22, + 23, + 23, + 21, + 21, + 22, + 21, + 23, + 22, + 23, + 23, + 20, + 22, + 22, + 22, + 23, + 22, + 22, + 23, + 26, + 26, + 20, + 19, + 22, + 23, + 22, + 25, + 26, + 26, + 26, + 27, + 27, + 26, + 24, + 25, + 19, + 21, + 26, + 27, + 27, + 26, + 27, + 24, + 21, + 21, + 26, + 26, + 28, + 27, + 27, + 27, + 20, + 24, + 20, + 21, + 22, + 21, + 21, + 23, + 22, + 22, + 25, + 25, + 24, + 24, + 26, + 23, + 26, + 27, + 26, + 26, + 27, + 27, + 27, + 27, + 27, + 28, + 27, + 27, + 27, + 27, + 27, + 26, + 30, +] diff --git a/.venv/lib/python3.9/site-packages/jh2/hpack/huffman_table.py b/.venv/lib/python3.9/site-packages/jh2/hpack/huffman_table.py new file mode 100644 index 0000000..2dc1146 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/hpack/huffman_table.py @@ -0,0 +1,4486 @@ +""" +hpack/huffman_table +~~~~~~~~~~~~~~~~~~~ + +This implementation of a Huffman decoding table for HTTP/2 is essentially a +Python port of the work originally done for nghttp2's Huffman decoding. For +this reason, while this file is made available under the MIT license as is the +rest of this module, this file is undoubtedly a derivative work of the nghttp2 +file ``nghttp2_hd_huffman_data.c``, obtained from +https://github.com/tatsuhiro-t/nghttp2/ at commit +d2b55ad1a245e1d1964579fa3fac36ebf3939e72. That work is made available under +the Apache 2.0 license under the following terms: + + Copyright (c) 2013 Tatsuhiro Tsujikawa + + 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. + +The essence of this approach is that it builds a finite state machine out of +4-bit nibbles of Huffman coded data. The input function passes 4 bits worth of +data to the state machine each time, which uses those 4 bits of data along with +the current accumulated state data to process the data given. + +For the sake of efficiency, the in-memory representation of the states, +transitions, and result values of the state machine are represented as a long +list containing three-tuples. This list is enormously long, and viewing it as +an in-memory representation is not very clear, but it is laid out here in a way +that is intended to be *somewhat* more clear. + +Essentially, the list is structured as 256 collections of 16 entries (one for +each nibble) of three-tuples. Each collection is called a "node", and the +zeroth collection is called the "root node". The state machine tracks one +value: the "state" byte. + +For each nibble passed to the state machine, it first multiplies the "state" +byte by 16 and adds the numerical value of the nibble. This number is the index +into the large flat list. + +The three-tuple that is found by looking up that index consists of three +values: + +- a new state value, used for subsequent decoding +- a collection of flags, used to determine whether data is emitted or whether + the state machine is complete. +- the byte value to emit, assuming that emitting a byte is required. + +The flags are consulted, if necessary a byte is emitted, and then the next +nibble is used. This continues until the state machine believes it has +completely Huffman-decoded the data. + +This approach has relatively little indirection, and therefore performs +relatively well, particularly on implementations like PyPy where the cost of +loops at the Python-level is not too expensive. The total number of loop +iterations is 4x the number of bytes passed to the decoder. +""" + +from __future__ import annotations + +from .exceptions import HPACKDecodingError + + +# This defines the state machine "class" at the top of the file. The reason we +# do this is to keep the terrifing monster state table at the *bottom* of the +# file so you don't have to actually *look* at the damn thing. +def decode_huffman(huffman_string): + """ + Given a bytestring of Huffman-encoded data for HPACK, returns a bytestring + of the decompressed data. + """ + if not huffman_string: + return b"" + + state = 0 + flags = 0 + decoded_bytes = bytearray() + + # Perversely, bytearrays are a lot more convenient across Python 2 and + # Python 3 because they behave *the same way* on both platforms. Given that + # we really do want numerical bytes when we iterate here, let's use a + # bytearray. + huffman_string = bytearray(huffman_string) + + # This loop is unrolled somewhat. Because we use a nibble, not a byte, we + # need to handle each nibble twice. We unroll that: it makes the loop body + # a bit longer, but that's ok. + for input_byte in huffman_string: + index = (state * 16) + (input_byte >> 4) + state, flags, output_byte = HUFFMAN_TABLE[index] + + if flags & HUFFMAN_FAIL: + raise HPACKDecodingError("Invalid Huffman String") + + if flags & HUFFMAN_EMIT_SYMBOL: + decoded_bytes.append(output_byte) + + index = (state * 16) + (input_byte & 0x0F) + state, flags, output_byte = HUFFMAN_TABLE[index] + + if flags & HUFFMAN_FAIL: + raise HPACKDecodingError("Invalid Huffman String") + + if flags & HUFFMAN_EMIT_SYMBOL: + decoded_bytes.append(output_byte) + + if not (flags & HUFFMAN_COMPLETE): + raise HPACKDecodingError("Incomplete Huffman string") + + return bytes(decoded_bytes) + + +# Some decoder flags to control state transitions. +HUFFMAN_COMPLETE = 1 +HUFFMAN_EMIT_SYMBOL = 1 << 1 +HUFFMAN_FAIL = 1 << 2 + +# This is the monster table. Avert your eyes, children. +HUFFMAN_TABLE = [ + # Node 0 (Root Node, never emits symbols.) + (4, 0, 0), + (5, 0, 0), + (7, 0, 0), + (8, 0, 0), + (11, 0, 0), + (12, 0, 0), + (16, 0, 0), + (19, 0, 0), + (25, 0, 0), + (28, 0, 0), + (32, 0, 0), + (35, 0, 0), + (42, 0, 0), + (49, 0, 0), + (57, 0, 0), + (64, HUFFMAN_COMPLETE, 0), + # Node 1 + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 48), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 49), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 50), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 97), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 99), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 101), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 105), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 111), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 115), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 116), + (13, 0, 0), + (14, 0, 0), + (17, 0, 0), + (18, 0, 0), + (20, 0, 0), + (21, 0, 0), + # Node 2 + (1, HUFFMAN_EMIT_SYMBOL, 48), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 48), + (1, HUFFMAN_EMIT_SYMBOL, 49), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 49), + (1, HUFFMAN_EMIT_SYMBOL, 50), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 50), + (1, HUFFMAN_EMIT_SYMBOL, 97), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 97), + (1, HUFFMAN_EMIT_SYMBOL, 99), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 99), + (1, HUFFMAN_EMIT_SYMBOL, 101), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 101), + (1, HUFFMAN_EMIT_SYMBOL, 105), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 105), + (1, HUFFMAN_EMIT_SYMBOL, 111), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 111), + # Node 3 + (2, HUFFMAN_EMIT_SYMBOL, 48), + (9, HUFFMAN_EMIT_SYMBOL, 48), + (23, HUFFMAN_EMIT_SYMBOL, 48), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 48), + (2, HUFFMAN_EMIT_SYMBOL, 49), + (9, HUFFMAN_EMIT_SYMBOL, 49), + (23, HUFFMAN_EMIT_SYMBOL, 49), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 49), + (2, HUFFMAN_EMIT_SYMBOL, 50), + (9, HUFFMAN_EMIT_SYMBOL, 50), + (23, HUFFMAN_EMIT_SYMBOL, 50), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 50), + (2, HUFFMAN_EMIT_SYMBOL, 97), + (9, HUFFMAN_EMIT_SYMBOL, 97), + (23, HUFFMAN_EMIT_SYMBOL, 97), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 97), + # Node 4 + (3, HUFFMAN_EMIT_SYMBOL, 48), + (6, HUFFMAN_EMIT_SYMBOL, 48), + (10, HUFFMAN_EMIT_SYMBOL, 48), + (15, HUFFMAN_EMIT_SYMBOL, 48), + (24, HUFFMAN_EMIT_SYMBOL, 48), + (31, HUFFMAN_EMIT_SYMBOL, 48), + (41, HUFFMAN_EMIT_SYMBOL, 48), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 48), + (3, HUFFMAN_EMIT_SYMBOL, 49), + (6, HUFFMAN_EMIT_SYMBOL, 49), + (10, HUFFMAN_EMIT_SYMBOL, 49), + (15, HUFFMAN_EMIT_SYMBOL, 49), + (24, HUFFMAN_EMIT_SYMBOL, 49), + (31, HUFFMAN_EMIT_SYMBOL, 49), + (41, HUFFMAN_EMIT_SYMBOL, 49), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 49), + # Node 5 + (3, HUFFMAN_EMIT_SYMBOL, 50), + (6, HUFFMAN_EMIT_SYMBOL, 50), + (10, HUFFMAN_EMIT_SYMBOL, 50), + (15, HUFFMAN_EMIT_SYMBOL, 50), + (24, HUFFMAN_EMIT_SYMBOL, 50), + (31, HUFFMAN_EMIT_SYMBOL, 50), + (41, HUFFMAN_EMIT_SYMBOL, 50), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 50), + (3, HUFFMAN_EMIT_SYMBOL, 97), + (6, HUFFMAN_EMIT_SYMBOL, 97), + (10, HUFFMAN_EMIT_SYMBOL, 97), + (15, HUFFMAN_EMIT_SYMBOL, 97), + (24, HUFFMAN_EMIT_SYMBOL, 97), + (31, HUFFMAN_EMIT_SYMBOL, 97), + (41, HUFFMAN_EMIT_SYMBOL, 97), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 97), + # Node 6 + (2, HUFFMAN_EMIT_SYMBOL, 99), + (9, HUFFMAN_EMIT_SYMBOL, 99), + (23, HUFFMAN_EMIT_SYMBOL, 99), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 99), + (2, HUFFMAN_EMIT_SYMBOL, 101), + (9, HUFFMAN_EMIT_SYMBOL, 101), + (23, HUFFMAN_EMIT_SYMBOL, 101), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 101), + (2, HUFFMAN_EMIT_SYMBOL, 105), + (9, HUFFMAN_EMIT_SYMBOL, 105), + (23, HUFFMAN_EMIT_SYMBOL, 105), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 105), + (2, HUFFMAN_EMIT_SYMBOL, 111), + (9, HUFFMAN_EMIT_SYMBOL, 111), + (23, HUFFMAN_EMIT_SYMBOL, 111), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 111), + # Node 7 + (3, HUFFMAN_EMIT_SYMBOL, 99), + (6, HUFFMAN_EMIT_SYMBOL, 99), + (10, HUFFMAN_EMIT_SYMBOL, 99), + (15, HUFFMAN_EMIT_SYMBOL, 99), + (24, HUFFMAN_EMIT_SYMBOL, 99), + (31, HUFFMAN_EMIT_SYMBOL, 99), + (41, HUFFMAN_EMIT_SYMBOL, 99), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 99), + (3, HUFFMAN_EMIT_SYMBOL, 101), + (6, HUFFMAN_EMIT_SYMBOL, 101), + (10, HUFFMAN_EMIT_SYMBOL, 101), + (15, HUFFMAN_EMIT_SYMBOL, 101), + (24, HUFFMAN_EMIT_SYMBOL, 101), + (31, HUFFMAN_EMIT_SYMBOL, 101), + (41, HUFFMAN_EMIT_SYMBOL, 101), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 101), + # Node 8 + (3, HUFFMAN_EMIT_SYMBOL, 105), + (6, HUFFMAN_EMIT_SYMBOL, 105), + (10, HUFFMAN_EMIT_SYMBOL, 105), + (15, HUFFMAN_EMIT_SYMBOL, 105), + (24, HUFFMAN_EMIT_SYMBOL, 105), + (31, HUFFMAN_EMIT_SYMBOL, 105), + (41, HUFFMAN_EMIT_SYMBOL, 105), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 105), + (3, HUFFMAN_EMIT_SYMBOL, 111), + (6, HUFFMAN_EMIT_SYMBOL, 111), + (10, HUFFMAN_EMIT_SYMBOL, 111), + (15, HUFFMAN_EMIT_SYMBOL, 111), + (24, HUFFMAN_EMIT_SYMBOL, 111), + (31, HUFFMAN_EMIT_SYMBOL, 111), + (41, HUFFMAN_EMIT_SYMBOL, 111), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 111), + # Node 9 + (1, HUFFMAN_EMIT_SYMBOL, 115), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 115), + (1, HUFFMAN_EMIT_SYMBOL, 116), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 116), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 32), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 37), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 45), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 46), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 47), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 51), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 52), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 53), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 54), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 55), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 56), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 57), + # Node 10 + (2, HUFFMAN_EMIT_SYMBOL, 115), + (9, HUFFMAN_EMIT_SYMBOL, 115), + (23, HUFFMAN_EMIT_SYMBOL, 115), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 115), + (2, HUFFMAN_EMIT_SYMBOL, 116), + (9, HUFFMAN_EMIT_SYMBOL, 116), + (23, HUFFMAN_EMIT_SYMBOL, 116), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 116), + (1, HUFFMAN_EMIT_SYMBOL, 32), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 32), + (1, HUFFMAN_EMIT_SYMBOL, 37), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 37), + (1, HUFFMAN_EMIT_SYMBOL, 45), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 45), + (1, HUFFMAN_EMIT_SYMBOL, 46), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 46), + # Node 11 + (3, HUFFMAN_EMIT_SYMBOL, 115), + (6, HUFFMAN_EMIT_SYMBOL, 115), + (10, HUFFMAN_EMIT_SYMBOL, 115), + (15, HUFFMAN_EMIT_SYMBOL, 115), + (24, HUFFMAN_EMIT_SYMBOL, 115), + (31, HUFFMAN_EMIT_SYMBOL, 115), + (41, HUFFMAN_EMIT_SYMBOL, 115), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 115), + (3, HUFFMAN_EMIT_SYMBOL, 116), + (6, HUFFMAN_EMIT_SYMBOL, 116), + (10, HUFFMAN_EMIT_SYMBOL, 116), + (15, HUFFMAN_EMIT_SYMBOL, 116), + (24, HUFFMAN_EMIT_SYMBOL, 116), + (31, HUFFMAN_EMIT_SYMBOL, 116), + (41, HUFFMAN_EMIT_SYMBOL, 116), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 116), + # Node 12 + (2, HUFFMAN_EMIT_SYMBOL, 32), + (9, HUFFMAN_EMIT_SYMBOL, 32), + (23, HUFFMAN_EMIT_SYMBOL, 32), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 32), + (2, HUFFMAN_EMIT_SYMBOL, 37), + (9, HUFFMAN_EMIT_SYMBOL, 37), + (23, HUFFMAN_EMIT_SYMBOL, 37), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 37), + (2, HUFFMAN_EMIT_SYMBOL, 45), + (9, HUFFMAN_EMIT_SYMBOL, 45), + (23, HUFFMAN_EMIT_SYMBOL, 45), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 45), + (2, HUFFMAN_EMIT_SYMBOL, 46), + (9, HUFFMAN_EMIT_SYMBOL, 46), + (23, HUFFMAN_EMIT_SYMBOL, 46), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 46), + # Node 13 + (3, HUFFMAN_EMIT_SYMBOL, 32), + (6, HUFFMAN_EMIT_SYMBOL, 32), + (10, HUFFMAN_EMIT_SYMBOL, 32), + (15, HUFFMAN_EMIT_SYMBOL, 32), + (24, HUFFMAN_EMIT_SYMBOL, 32), + (31, HUFFMAN_EMIT_SYMBOL, 32), + (41, HUFFMAN_EMIT_SYMBOL, 32), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 32), + (3, HUFFMAN_EMIT_SYMBOL, 37), + (6, HUFFMAN_EMIT_SYMBOL, 37), + (10, HUFFMAN_EMIT_SYMBOL, 37), + (15, HUFFMAN_EMIT_SYMBOL, 37), + (24, HUFFMAN_EMIT_SYMBOL, 37), + (31, HUFFMAN_EMIT_SYMBOL, 37), + (41, HUFFMAN_EMIT_SYMBOL, 37), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 37), + # Node 14 + (3, HUFFMAN_EMIT_SYMBOL, 45), + (6, HUFFMAN_EMIT_SYMBOL, 45), + (10, HUFFMAN_EMIT_SYMBOL, 45), + (15, HUFFMAN_EMIT_SYMBOL, 45), + (24, HUFFMAN_EMIT_SYMBOL, 45), + (31, HUFFMAN_EMIT_SYMBOL, 45), + (41, HUFFMAN_EMIT_SYMBOL, 45), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 45), + (3, HUFFMAN_EMIT_SYMBOL, 46), + (6, HUFFMAN_EMIT_SYMBOL, 46), + (10, HUFFMAN_EMIT_SYMBOL, 46), + (15, HUFFMAN_EMIT_SYMBOL, 46), + (24, HUFFMAN_EMIT_SYMBOL, 46), + (31, HUFFMAN_EMIT_SYMBOL, 46), + (41, HUFFMAN_EMIT_SYMBOL, 46), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 46), + # Node 15 + (1, HUFFMAN_EMIT_SYMBOL, 47), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 47), + (1, HUFFMAN_EMIT_SYMBOL, 51), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 51), + (1, HUFFMAN_EMIT_SYMBOL, 52), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 52), + (1, HUFFMAN_EMIT_SYMBOL, 53), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 53), + (1, HUFFMAN_EMIT_SYMBOL, 54), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 54), + (1, HUFFMAN_EMIT_SYMBOL, 55), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 55), + (1, HUFFMAN_EMIT_SYMBOL, 56), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 56), + (1, HUFFMAN_EMIT_SYMBOL, 57), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 57), + # Node 16 + (2, HUFFMAN_EMIT_SYMBOL, 47), + (9, HUFFMAN_EMIT_SYMBOL, 47), + (23, HUFFMAN_EMIT_SYMBOL, 47), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 47), + (2, HUFFMAN_EMIT_SYMBOL, 51), + (9, HUFFMAN_EMIT_SYMBOL, 51), + (23, HUFFMAN_EMIT_SYMBOL, 51), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 51), + (2, HUFFMAN_EMIT_SYMBOL, 52), + (9, HUFFMAN_EMIT_SYMBOL, 52), + (23, HUFFMAN_EMIT_SYMBOL, 52), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 52), + (2, HUFFMAN_EMIT_SYMBOL, 53), + (9, HUFFMAN_EMIT_SYMBOL, 53), + (23, HUFFMAN_EMIT_SYMBOL, 53), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 53), + # Node 17 + (3, HUFFMAN_EMIT_SYMBOL, 47), + (6, HUFFMAN_EMIT_SYMBOL, 47), + (10, HUFFMAN_EMIT_SYMBOL, 47), + (15, HUFFMAN_EMIT_SYMBOL, 47), + (24, HUFFMAN_EMIT_SYMBOL, 47), + (31, HUFFMAN_EMIT_SYMBOL, 47), + (41, HUFFMAN_EMIT_SYMBOL, 47), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 47), + (3, HUFFMAN_EMIT_SYMBOL, 51), + (6, HUFFMAN_EMIT_SYMBOL, 51), + (10, HUFFMAN_EMIT_SYMBOL, 51), + (15, HUFFMAN_EMIT_SYMBOL, 51), + (24, HUFFMAN_EMIT_SYMBOL, 51), + (31, HUFFMAN_EMIT_SYMBOL, 51), + (41, HUFFMAN_EMIT_SYMBOL, 51), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 51), + # Node 18 + (3, HUFFMAN_EMIT_SYMBOL, 52), + (6, HUFFMAN_EMIT_SYMBOL, 52), + (10, HUFFMAN_EMIT_SYMBOL, 52), + (15, HUFFMAN_EMIT_SYMBOL, 52), + (24, HUFFMAN_EMIT_SYMBOL, 52), + (31, HUFFMAN_EMIT_SYMBOL, 52), + (41, HUFFMAN_EMIT_SYMBOL, 52), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 52), + (3, HUFFMAN_EMIT_SYMBOL, 53), + (6, HUFFMAN_EMIT_SYMBOL, 53), + (10, HUFFMAN_EMIT_SYMBOL, 53), + (15, HUFFMAN_EMIT_SYMBOL, 53), + (24, HUFFMAN_EMIT_SYMBOL, 53), + (31, HUFFMAN_EMIT_SYMBOL, 53), + (41, HUFFMAN_EMIT_SYMBOL, 53), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 53), + # Node 19 + (2, HUFFMAN_EMIT_SYMBOL, 54), + (9, HUFFMAN_EMIT_SYMBOL, 54), + (23, HUFFMAN_EMIT_SYMBOL, 54), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 54), + (2, HUFFMAN_EMIT_SYMBOL, 55), + (9, HUFFMAN_EMIT_SYMBOL, 55), + (23, HUFFMAN_EMIT_SYMBOL, 55), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 55), + (2, HUFFMAN_EMIT_SYMBOL, 56), + (9, HUFFMAN_EMIT_SYMBOL, 56), + (23, HUFFMAN_EMIT_SYMBOL, 56), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 56), + (2, HUFFMAN_EMIT_SYMBOL, 57), + (9, HUFFMAN_EMIT_SYMBOL, 57), + (23, HUFFMAN_EMIT_SYMBOL, 57), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 57), + # Node 20 + (3, HUFFMAN_EMIT_SYMBOL, 54), + (6, HUFFMAN_EMIT_SYMBOL, 54), + (10, HUFFMAN_EMIT_SYMBOL, 54), + (15, HUFFMAN_EMIT_SYMBOL, 54), + (24, HUFFMAN_EMIT_SYMBOL, 54), + (31, HUFFMAN_EMIT_SYMBOL, 54), + (41, HUFFMAN_EMIT_SYMBOL, 54), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 54), + (3, HUFFMAN_EMIT_SYMBOL, 55), + (6, HUFFMAN_EMIT_SYMBOL, 55), + (10, HUFFMAN_EMIT_SYMBOL, 55), + (15, HUFFMAN_EMIT_SYMBOL, 55), + (24, HUFFMAN_EMIT_SYMBOL, 55), + (31, HUFFMAN_EMIT_SYMBOL, 55), + (41, HUFFMAN_EMIT_SYMBOL, 55), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 55), + # Node 21 + (3, HUFFMAN_EMIT_SYMBOL, 56), + (6, HUFFMAN_EMIT_SYMBOL, 56), + (10, HUFFMAN_EMIT_SYMBOL, 56), + (15, HUFFMAN_EMIT_SYMBOL, 56), + (24, HUFFMAN_EMIT_SYMBOL, 56), + (31, HUFFMAN_EMIT_SYMBOL, 56), + (41, HUFFMAN_EMIT_SYMBOL, 56), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 56), + (3, HUFFMAN_EMIT_SYMBOL, 57), + (6, HUFFMAN_EMIT_SYMBOL, 57), + (10, HUFFMAN_EMIT_SYMBOL, 57), + (15, HUFFMAN_EMIT_SYMBOL, 57), + (24, HUFFMAN_EMIT_SYMBOL, 57), + (31, HUFFMAN_EMIT_SYMBOL, 57), + (41, HUFFMAN_EMIT_SYMBOL, 57), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 57), + # Node 22 + (26, 0, 0), + (27, 0, 0), + (29, 0, 0), + (30, 0, 0), + (33, 0, 0), + (34, 0, 0), + (36, 0, 0), + (37, 0, 0), + (43, 0, 0), + (46, 0, 0), + (50, 0, 0), + (53, 0, 0), + (58, 0, 0), + (61, 0, 0), + (65, 0, 0), + (68, HUFFMAN_COMPLETE, 0), + # Node 23 + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 61), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 65), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 95), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 98), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 100), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 102), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 103), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 104), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 108), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 109), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 110), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 112), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 114), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 117), + (38, 0, 0), + (39, 0, 0), + # Node 24 + (1, HUFFMAN_EMIT_SYMBOL, 61), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 61), + (1, HUFFMAN_EMIT_SYMBOL, 65), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 65), + (1, HUFFMAN_EMIT_SYMBOL, 95), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 95), + (1, HUFFMAN_EMIT_SYMBOL, 98), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 98), + (1, HUFFMAN_EMIT_SYMBOL, 100), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 100), + (1, HUFFMAN_EMIT_SYMBOL, 102), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 102), + (1, HUFFMAN_EMIT_SYMBOL, 103), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 103), + (1, HUFFMAN_EMIT_SYMBOL, 104), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 104), + # Node 25 + (2, HUFFMAN_EMIT_SYMBOL, 61), + (9, HUFFMAN_EMIT_SYMBOL, 61), + (23, HUFFMAN_EMIT_SYMBOL, 61), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 61), + (2, HUFFMAN_EMIT_SYMBOL, 65), + (9, HUFFMAN_EMIT_SYMBOL, 65), + (23, HUFFMAN_EMIT_SYMBOL, 65), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 65), + (2, HUFFMAN_EMIT_SYMBOL, 95), + (9, HUFFMAN_EMIT_SYMBOL, 95), + (23, HUFFMAN_EMIT_SYMBOL, 95), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 95), + (2, HUFFMAN_EMIT_SYMBOL, 98), + (9, HUFFMAN_EMIT_SYMBOL, 98), + (23, HUFFMAN_EMIT_SYMBOL, 98), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 98), + # Node 26 + (3, HUFFMAN_EMIT_SYMBOL, 61), + (6, HUFFMAN_EMIT_SYMBOL, 61), + (10, HUFFMAN_EMIT_SYMBOL, 61), + (15, HUFFMAN_EMIT_SYMBOL, 61), + (24, HUFFMAN_EMIT_SYMBOL, 61), + (31, HUFFMAN_EMIT_SYMBOL, 61), + (41, HUFFMAN_EMIT_SYMBOL, 61), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 61), + (3, HUFFMAN_EMIT_SYMBOL, 65), + (6, HUFFMAN_EMIT_SYMBOL, 65), + (10, HUFFMAN_EMIT_SYMBOL, 65), + (15, HUFFMAN_EMIT_SYMBOL, 65), + (24, HUFFMAN_EMIT_SYMBOL, 65), + (31, HUFFMAN_EMIT_SYMBOL, 65), + (41, HUFFMAN_EMIT_SYMBOL, 65), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 65), + # Node 27 + (3, HUFFMAN_EMIT_SYMBOL, 95), + (6, HUFFMAN_EMIT_SYMBOL, 95), + (10, HUFFMAN_EMIT_SYMBOL, 95), + (15, HUFFMAN_EMIT_SYMBOL, 95), + (24, HUFFMAN_EMIT_SYMBOL, 95), + (31, HUFFMAN_EMIT_SYMBOL, 95), + (41, HUFFMAN_EMIT_SYMBOL, 95), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 95), + (3, HUFFMAN_EMIT_SYMBOL, 98), + (6, HUFFMAN_EMIT_SYMBOL, 98), + (10, HUFFMAN_EMIT_SYMBOL, 98), + (15, HUFFMAN_EMIT_SYMBOL, 98), + (24, HUFFMAN_EMIT_SYMBOL, 98), + (31, HUFFMAN_EMIT_SYMBOL, 98), + (41, HUFFMAN_EMIT_SYMBOL, 98), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 98), + # Node 28 + (2, HUFFMAN_EMIT_SYMBOL, 100), + (9, HUFFMAN_EMIT_SYMBOL, 100), + (23, HUFFMAN_EMIT_SYMBOL, 100), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 100), + (2, HUFFMAN_EMIT_SYMBOL, 102), + (9, HUFFMAN_EMIT_SYMBOL, 102), + (23, HUFFMAN_EMIT_SYMBOL, 102), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 102), + (2, HUFFMAN_EMIT_SYMBOL, 103), + (9, HUFFMAN_EMIT_SYMBOL, 103), + (23, HUFFMAN_EMIT_SYMBOL, 103), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 103), + (2, HUFFMAN_EMIT_SYMBOL, 104), + (9, HUFFMAN_EMIT_SYMBOL, 104), + (23, HUFFMAN_EMIT_SYMBOL, 104), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 104), + # Node 29 + (3, HUFFMAN_EMIT_SYMBOL, 100), + (6, HUFFMAN_EMIT_SYMBOL, 100), + (10, HUFFMAN_EMIT_SYMBOL, 100), + (15, HUFFMAN_EMIT_SYMBOL, 100), + (24, HUFFMAN_EMIT_SYMBOL, 100), + (31, HUFFMAN_EMIT_SYMBOL, 100), + (41, HUFFMAN_EMIT_SYMBOL, 100), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 100), + (3, HUFFMAN_EMIT_SYMBOL, 102), + (6, HUFFMAN_EMIT_SYMBOL, 102), + (10, HUFFMAN_EMIT_SYMBOL, 102), + (15, HUFFMAN_EMIT_SYMBOL, 102), + (24, HUFFMAN_EMIT_SYMBOL, 102), + (31, HUFFMAN_EMIT_SYMBOL, 102), + (41, HUFFMAN_EMIT_SYMBOL, 102), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 102), + # Node 30 + (3, HUFFMAN_EMIT_SYMBOL, 103), + (6, HUFFMAN_EMIT_SYMBOL, 103), + (10, HUFFMAN_EMIT_SYMBOL, 103), + (15, HUFFMAN_EMIT_SYMBOL, 103), + (24, HUFFMAN_EMIT_SYMBOL, 103), + (31, HUFFMAN_EMIT_SYMBOL, 103), + (41, HUFFMAN_EMIT_SYMBOL, 103), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 103), + (3, HUFFMAN_EMIT_SYMBOL, 104), + (6, HUFFMAN_EMIT_SYMBOL, 104), + (10, HUFFMAN_EMIT_SYMBOL, 104), + (15, HUFFMAN_EMIT_SYMBOL, 104), + (24, HUFFMAN_EMIT_SYMBOL, 104), + (31, HUFFMAN_EMIT_SYMBOL, 104), + (41, HUFFMAN_EMIT_SYMBOL, 104), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 104), + # Node 31 + (1, HUFFMAN_EMIT_SYMBOL, 108), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 108), + (1, HUFFMAN_EMIT_SYMBOL, 109), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 109), + (1, HUFFMAN_EMIT_SYMBOL, 110), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 110), + (1, HUFFMAN_EMIT_SYMBOL, 112), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 112), + (1, HUFFMAN_EMIT_SYMBOL, 114), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 114), + (1, HUFFMAN_EMIT_SYMBOL, 117), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 117), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 58), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 66), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 67), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 68), + # Node 32 + (2, HUFFMAN_EMIT_SYMBOL, 108), + (9, HUFFMAN_EMIT_SYMBOL, 108), + (23, HUFFMAN_EMIT_SYMBOL, 108), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 108), + (2, HUFFMAN_EMIT_SYMBOL, 109), + (9, HUFFMAN_EMIT_SYMBOL, 109), + (23, HUFFMAN_EMIT_SYMBOL, 109), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 109), + (2, HUFFMAN_EMIT_SYMBOL, 110), + (9, HUFFMAN_EMIT_SYMBOL, 110), + (23, HUFFMAN_EMIT_SYMBOL, 110), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 110), + (2, HUFFMAN_EMIT_SYMBOL, 112), + (9, HUFFMAN_EMIT_SYMBOL, 112), + (23, HUFFMAN_EMIT_SYMBOL, 112), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 112), + # Node 33 + (3, HUFFMAN_EMIT_SYMBOL, 108), + (6, HUFFMAN_EMIT_SYMBOL, 108), + (10, HUFFMAN_EMIT_SYMBOL, 108), + (15, HUFFMAN_EMIT_SYMBOL, 108), + (24, HUFFMAN_EMIT_SYMBOL, 108), + (31, HUFFMAN_EMIT_SYMBOL, 108), + (41, HUFFMAN_EMIT_SYMBOL, 108), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 108), + (3, HUFFMAN_EMIT_SYMBOL, 109), + (6, HUFFMAN_EMIT_SYMBOL, 109), + (10, HUFFMAN_EMIT_SYMBOL, 109), + (15, HUFFMAN_EMIT_SYMBOL, 109), + (24, HUFFMAN_EMIT_SYMBOL, 109), + (31, HUFFMAN_EMIT_SYMBOL, 109), + (41, HUFFMAN_EMIT_SYMBOL, 109), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 109), + # Node 34 + (3, HUFFMAN_EMIT_SYMBOL, 110), + (6, HUFFMAN_EMIT_SYMBOL, 110), + (10, HUFFMAN_EMIT_SYMBOL, 110), + (15, HUFFMAN_EMIT_SYMBOL, 110), + (24, HUFFMAN_EMIT_SYMBOL, 110), + (31, HUFFMAN_EMIT_SYMBOL, 110), + (41, HUFFMAN_EMIT_SYMBOL, 110), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 110), + (3, HUFFMAN_EMIT_SYMBOL, 112), + (6, HUFFMAN_EMIT_SYMBOL, 112), + (10, HUFFMAN_EMIT_SYMBOL, 112), + (15, HUFFMAN_EMIT_SYMBOL, 112), + (24, HUFFMAN_EMIT_SYMBOL, 112), + (31, HUFFMAN_EMIT_SYMBOL, 112), + (41, HUFFMAN_EMIT_SYMBOL, 112), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 112), + # Node 35 + (2, HUFFMAN_EMIT_SYMBOL, 114), + (9, HUFFMAN_EMIT_SYMBOL, 114), + (23, HUFFMAN_EMIT_SYMBOL, 114), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 114), + (2, HUFFMAN_EMIT_SYMBOL, 117), + (9, HUFFMAN_EMIT_SYMBOL, 117), + (23, HUFFMAN_EMIT_SYMBOL, 117), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 117), + (1, HUFFMAN_EMIT_SYMBOL, 58), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 58), + (1, HUFFMAN_EMIT_SYMBOL, 66), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 66), + (1, HUFFMAN_EMIT_SYMBOL, 67), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 67), + (1, HUFFMAN_EMIT_SYMBOL, 68), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 68), + # Node 36 + (3, HUFFMAN_EMIT_SYMBOL, 114), + (6, HUFFMAN_EMIT_SYMBOL, 114), + (10, HUFFMAN_EMIT_SYMBOL, 114), + (15, HUFFMAN_EMIT_SYMBOL, 114), + (24, HUFFMAN_EMIT_SYMBOL, 114), + (31, HUFFMAN_EMIT_SYMBOL, 114), + (41, HUFFMAN_EMIT_SYMBOL, 114), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 114), + (3, HUFFMAN_EMIT_SYMBOL, 117), + (6, HUFFMAN_EMIT_SYMBOL, 117), + (10, HUFFMAN_EMIT_SYMBOL, 117), + (15, HUFFMAN_EMIT_SYMBOL, 117), + (24, HUFFMAN_EMIT_SYMBOL, 117), + (31, HUFFMAN_EMIT_SYMBOL, 117), + (41, HUFFMAN_EMIT_SYMBOL, 117), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 117), + # Node 37 + (2, HUFFMAN_EMIT_SYMBOL, 58), + (9, HUFFMAN_EMIT_SYMBOL, 58), + (23, HUFFMAN_EMIT_SYMBOL, 58), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 58), + (2, HUFFMAN_EMIT_SYMBOL, 66), + (9, HUFFMAN_EMIT_SYMBOL, 66), + (23, HUFFMAN_EMIT_SYMBOL, 66), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 66), + (2, HUFFMAN_EMIT_SYMBOL, 67), + (9, HUFFMAN_EMIT_SYMBOL, 67), + (23, HUFFMAN_EMIT_SYMBOL, 67), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 67), + (2, HUFFMAN_EMIT_SYMBOL, 68), + (9, HUFFMAN_EMIT_SYMBOL, 68), + (23, HUFFMAN_EMIT_SYMBOL, 68), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 68), + # Node 38 + (3, HUFFMAN_EMIT_SYMBOL, 58), + (6, HUFFMAN_EMIT_SYMBOL, 58), + (10, HUFFMAN_EMIT_SYMBOL, 58), + (15, HUFFMAN_EMIT_SYMBOL, 58), + (24, HUFFMAN_EMIT_SYMBOL, 58), + (31, HUFFMAN_EMIT_SYMBOL, 58), + (41, HUFFMAN_EMIT_SYMBOL, 58), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 58), + (3, HUFFMAN_EMIT_SYMBOL, 66), + (6, HUFFMAN_EMIT_SYMBOL, 66), + (10, HUFFMAN_EMIT_SYMBOL, 66), + (15, HUFFMAN_EMIT_SYMBOL, 66), + (24, HUFFMAN_EMIT_SYMBOL, 66), + (31, HUFFMAN_EMIT_SYMBOL, 66), + (41, HUFFMAN_EMIT_SYMBOL, 66), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 66), + # Node 39 + (3, HUFFMAN_EMIT_SYMBOL, 67), + (6, HUFFMAN_EMIT_SYMBOL, 67), + (10, HUFFMAN_EMIT_SYMBOL, 67), + (15, HUFFMAN_EMIT_SYMBOL, 67), + (24, HUFFMAN_EMIT_SYMBOL, 67), + (31, HUFFMAN_EMIT_SYMBOL, 67), + (41, HUFFMAN_EMIT_SYMBOL, 67), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 67), + (3, HUFFMAN_EMIT_SYMBOL, 68), + (6, HUFFMAN_EMIT_SYMBOL, 68), + (10, HUFFMAN_EMIT_SYMBOL, 68), + (15, HUFFMAN_EMIT_SYMBOL, 68), + (24, HUFFMAN_EMIT_SYMBOL, 68), + (31, HUFFMAN_EMIT_SYMBOL, 68), + (41, HUFFMAN_EMIT_SYMBOL, 68), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 68), + # Node 40 + (44, 0, 0), + (45, 0, 0), + (47, 0, 0), + (48, 0, 0), + (51, 0, 0), + (52, 0, 0), + (54, 0, 0), + (55, 0, 0), + (59, 0, 0), + (60, 0, 0), + (62, 0, 0), + (63, 0, 0), + (66, 0, 0), + (67, 0, 0), + (69, 0, 0), + (72, HUFFMAN_COMPLETE, 0), + # Node 41 + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 69), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 70), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 71), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 72), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 73), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 74), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 75), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 76), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 77), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 78), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 79), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 80), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 81), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 82), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 83), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 84), + # Node 42 + (1, HUFFMAN_EMIT_SYMBOL, 69), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 69), + (1, HUFFMAN_EMIT_SYMBOL, 70), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 70), + (1, HUFFMAN_EMIT_SYMBOL, 71), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 71), + (1, HUFFMAN_EMIT_SYMBOL, 72), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 72), + (1, HUFFMAN_EMIT_SYMBOL, 73), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 73), + (1, HUFFMAN_EMIT_SYMBOL, 74), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 74), + (1, HUFFMAN_EMIT_SYMBOL, 75), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 75), + (1, HUFFMAN_EMIT_SYMBOL, 76), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 76), + # Node 43 + (2, HUFFMAN_EMIT_SYMBOL, 69), + (9, HUFFMAN_EMIT_SYMBOL, 69), + (23, HUFFMAN_EMIT_SYMBOL, 69), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 69), + (2, HUFFMAN_EMIT_SYMBOL, 70), + (9, HUFFMAN_EMIT_SYMBOL, 70), + (23, HUFFMAN_EMIT_SYMBOL, 70), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 70), + (2, HUFFMAN_EMIT_SYMBOL, 71), + (9, HUFFMAN_EMIT_SYMBOL, 71), + (23, HUFFMAN_EMIT_SYMBOL, 71), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 71), + (2, HUFFMAN_EMIT_SYMBOL, 72), + (9, HUFFMAN_EMIT_SYMBOL, 72), + (23, HUFFMAN_EMIT_SYMBOL, 72), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 72), + # Node 44 + (3, HUFFMAN_EMIT_SYMBOL, 69), + (6, HUFFMAN_EMIT_SYMBOL, 69), + (10, HUFFMAN_EMIT_SYMBOL, 69), + (15, HUFFMAN_EMIT_SYMBOL, 69), + (24, HUFFMAN_EMIT_SYMBOL, 69), + (31, HUFFMAN_EMIT_SYMBOL, 69), + (41, HUFFMAN_EMIT_SYMBOL, 69), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 69), + (3, HUFFMAN_EMIT_SYMBOL, 70), + (6, HUFFMAN_EMIT_SYMBOL, 70), + (10, HUFFMAN_EMIT_SYMBOL, 70), + (15, HUFFMAN_EMIT_SYMBOL, 70), + (24, HUFFMAN_EMIT_SYMBOL, 70), + (31, HUFFMAN_EMIT_SYMBOL, 70), + (41, HUFFMAN_EMIT_SYMBOL, 70), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 70), + # Node 45 + (3, HUFFMAN_EMIT_SYMBOL, 71), + (6, HUFFMAN_EMIT_SYMBOL, 71), + (10, HUFFMAN_EMIT_SYMBOL, 71), + (15, HUFFMAN_EMIT_SYMBOL, 71), + (24, HUFFMAN_EMIT_SYMBOL, 71), + (31, HUFFMAN_EMIT_SYMBOL, 71), + (41, HUFFMAN_EMIT_SYMBOL, 71), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 71), + (3, HUFFMAN_EMIT_SYMBOL, 72), + (6, HUFFMAN_EMIT_SYMBOL, 72), + (10, HUFFMAN_EMIT_SYMBOL, 72), + (15, HUFFMAN_EMIT_SYMBOL, 72), + (24, HUFFMAN_EMIT_SYMBOL, 72), + (31, HUFFMAN_EMIT_SYMBOL, 72), + (41, HUFFMAN_EMIT_SYMBOL, 72), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 72), + # Node 46 + (2, HUFFMAN_EMIT_SYMBOL, 73), + (9, HUFFMAN_EMIT_SYMBOL, 73), + (23, HUFFMAN_EMIT_SYMBOL, 73), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 73), + (2, HUFFMAN_EMIT_SYMBOL, 74), + (9, HUFFMAN_EMIT_SYMBOL, 74), + (23, HUFFMAN_EMIT_SYMBOL, 74), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 74), + (2, HUFFMAN_EMIT_SYMBOL, 75), + (9, HUFFMAN_EMIT_SYMBOL, 75), + (23, HUFFMAN_EMIT_SYMBOL, 75), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 75), + (2, HUFFMAN_EMIT_SYMBOL, 76), + (9, HUFFMAN_EMIT_SYMBOL, 76), + (23, HUFFMAN_EMIT_SYMBOL, 76), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 76), + # Node 47 + (3, HUFFMAN_EMIT_SYMBOL, 73), + (6, HUFFMAN_EMIT_SYMBOL, 73), + (10, HUFFMAN_EMIT_SYMBOL, 73), + (15, HUFFMAN_EMIT_SYMBOL, 73), + (24, HUFFMAN_EMIT_SYMBOL, 73), + (31, HUFFMAN_EMIT_SYMBOL, 73), + (41, HUFFMAN_EMIT_SYMBOL, 73), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 73), + (3, HUFFMAN_EMIT_SYMBOL, 74), + (6, HUFFMAN_EMIT_SYMBOL, 74), + (10, HUFFMAN_EMIT_SYMBOL, 74), + (15, HUFFMAN_EMIT_SYMBOL, 74), + (24, HUFFMAN_EMIT_SYMBOL, 74), + (31, HUFFMAN_EMIT_SYMBOL, 74), + (41, HUFFMAN_EMIT_SYMBOL, 74), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 74), + # Node 48 + (3, HUFFMAN_EMIT_SYMBOL, 75), + (6, HUFFMAN_EMIT_SYMBOL, 75), + (10, HUFFMAN_EMIT_SYMBOL, 75), + (15, HUFFMAN_EMIT_SYMBOL, 75), + (24, HUFFMAN_EMIT_SYMBOL, 75), + (31, HUFFMAN_EMIT_SYMBOL, 75), + (41, HUFFMAN_EMIT_SYMBOL, 75), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 75), + (3, HUFFMAN_EMIT_SYMBOL, 76), + (6, HUFFMAN_EMIT_SYMBOL, 76), + (10, HUFFMAN_EMIT_SYMBOL, 76), + (15, HUFFMAN_EMIT_SYMBOL, 76), + (24, HUFFMAN_EMIT_SYMBOL, 76), + (31, HUFFMAN_EMIT_SYMBOL, 76), + (41, HUFFMAN_EMIT_SYMBOL, 76), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 76), + # Node 49 + (1, HUFFMAN_EMIT_SYMBOL, 77), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 77), + (1, HUFFMAN_EMIT_SYMBOL, 78), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 78), + (1, HUFFMAN_EMIT_SYMBOL, 79), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 79), + (1, HUFFMAN_EMIT_SYMBOL, 80), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 80), + (1, HUFFMAN_EMIT_SYMBOL, 81), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 81), + (1, HUFFMAN_EMIT_SYMBOL, 82), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 82), + (1, HUFFMAN_EMIT_SYMBOL, 83), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 83), + (1, HUFFMAN_EMIT_SYMBOL, 84), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 84), + # Node 50 + (2, HUFFMAN_EMIT_SYMBOL, 77), + (9, HUFFMAN_EMIT_SYMBOL, 77), + (23, HUFFMAN_EMIT_SYMBOL, 77), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 77), + (2, HUFFMAN_EMIT_SYMBOL, 78), + (9, HUFFMAN_EMIT_SYMBOL, 78), + (23, HUFFMAN_EMIT_SYMBOL, 78), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 78), + (2, HUFFMAN_EMIT_SYMBOL, 79), + (9, HUFFMAN_EMIT_SYMBOL, 79), + (23, HUFFMAN_EMIT_SYMBOL, 79), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 79), + (2, HUFFMAN_EMIT_SYMBOL, 80), + (9, HUFFMAN_EMIT_SYMBOL, 80), + (23, HUFFMAN_EMIT_SYMBOL, 80), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 80), + # Node 51 + (3, HUFFMAN_EMIT_SYMBOL, 77), + (6, HUFFMAN_EMIT_SYMBOL, 77), + (10, HUFFMAN_EMIT_SYMBOL, 77), + (15, HUFFMAN_EMIT_SYMBOL, 77), + (24, HUFFMAN_EMIT_SYMBOL, 77), + (31, HUFFMAN_EMIT_SYMBOL, 77), + (41, HUFFMAN_EMIT_SYMBOL, 77), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 77), + (3, HUFFMAN_EMIT_SYMBOL, 78), + (6, HUFFMAN_EMIT_SYMBOL, 78), + (10, HUFFMAN_EMIT_SYMBOL, 78), + (15, HUFFMAN_EMIT_SYMBOL, 78), + (24, HUFFMAN_EMIT_SYMBOL, 78), + (31, HUFFMAN_EMIT_SYMBOL, 78), + (41, HUFFMAN_EMIT_SYMBOL, 78), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 78), + # Node 52 + (3, HUFFMAN_EMIT_SYMBOL, 79), + (6, HUFFMAN_EMIT_SYMBOL, 79), + (10, HUFFMAN_EMIT_SYMBOL, 79), + (15, HUFFMAN_EMIT_SYMBOL, 79), + (24, HUFFMAN_EMIT_SYMBOL, 79), + (31, HUFFMAN_EMIT_SYMBOL, 79), + (41, HUFFMAN_EMIT_SYMBOL, 79), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 79), + (3, HUFFMAN_EMIT_SYMBOL, 80), + (6, HUFFMAN_EMIT_SYMBOL, 80), + (10, HUFFMAN_EMIT_SYMBOL, 80), + (15, HUFFMAN_EMIT_SYMBOL, 80), + (24, HUFFMAN_EMIT_SYMBOL, 80), + (31, HUFFMAN_EMIT_SYMBOL, 80), + (41, HUFFMAN_EMIT_SYMBOL, 80), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 80), + # Node 53 + (2, HUFFMAN_EMIT_SYMBOL, 81), + (9, HUFFMAN_EMIT_SYMBOL, 81), + (23, HUFFMAN_EMIT_SYMBOL, 81), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 81), + (2, HUFFMAN_EMIT_SYMBOL, 82), + (9, HUFFMAN_EMIT_SYMBOL, 82), + (23, HUFFMAN_EMIT_SYMBOL, 82), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 82), + (2, HUFFMAN_EMIT_SYMBOL, 83), + (9, HUFFMAN_EMIT_SYMBOL, 83), + (23, HUFFMAN_EMIT_SYMBOL, 83), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 83), + (2, HUFFMAN_EMIT_SYMBOL, 84), + (9, HUFFMAN_EMIT_SYMBOL, 84), + (23, HUFFMAN_EMIT_SYMBOL, 84), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 84), + # Node 54 + (3, HUFFMAN_EMIT_SYMBOL, 81), + (6, HUFFMAN_EMIT_SYMBOL, 81), + (10, HUFFMAN_EMIT_SYMBOL, 81), + (15, HUFFMAN_EMIT_SYMBOL, 81), + (24, HUFFMAN_EMIT_SYMBOL, 81), + (31, HUFFMAN_EMIT_SYMBOL, 81), + (41, HUFFMAN_EMIT_SYMBOL, 81), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 81), + (3, HUFFMAN_EMIT_SYMBOL, 82), + (6, HUFFMAN_EMIT_SYMBOL, 82), + (10, HUFFMAN_EMIT_SYMBOL, 82), + (15, HUFFMAN_EMIT_SYMBOL, 82), + (24, HUFFMAN_EMIT_SYMBOL, 82), + (31, HUFFMAN_EMIT_SYMBOL, 82), + (41, HUFFMAN_EMIT_SYMBOL, 82), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 82), + # Node 55 + (3, HUFFMAN_EMIT_SYMBOL, 83), + (6, HUFFMAN_EMIT_SYMBOL, 83), + (10, HUFFMAN_EMIT_SYMBOL, 83), + (15, HUFFMAN_EMIT_SYMBOL, 83), + (24, HUFFMAN_EMIT_SYMBOL, 83), + (31, HUFFMAN_EMIT_SYMBOL, 83), + (41, HUFFMAN_EMIT_SYMBOL, 83), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 83), + (3, HUFFMAN_EMIT_SYMBOL, 84), + (6, HUFFMAN_EMIT_SYMBOL, 84), + (10, HUFFMAN_EMIT_SYMBOL, 84), + (15, HUFFMAN_EMIT_SYMBOL, 84), + (24, HUFFMAN_EMIT_SYMBOL, 84), + (31, HUFFMAN_EMIT_SYMBOL, 84), + (41, HUFFMAN_EMIT_SYMBOL, 84), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 84), + # Node 56 + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 85), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 86), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 87), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 89), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 106), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 107), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 113), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 118), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 119), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 120), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 121), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 122), + (70, 0, 0), + (71, 0, 0), + (73, 0, 0), + (74, HUFFMAN_COMPLETE, 0), + # Node 57 + (1, HUFFMAN_EMIT_SYMBOL, 85), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 85), + (1, HUFFMAN_EMIT_SYMBOL, 86), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 86), + (1, HUFFMAN_EMIT_SYMBOL, 87), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 87), + (1, HUFFMAN_EMIT_SYMBOL, 89), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 89), + (1, HUFFMAN_EMIT_SYMBOL, 106), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 106), + (1, HUFFMAN_EMIT_SYMBOL, 107), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 107), + (1, HUFFMAN_EMIT_SYMBOL, 113), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 113), + (1, HUFFMAN_EMIT_SYMBOL, 118), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 118), + # Node 58 + (2, HUFFMAN_EMIT_SYMBOL, 85), + (9, HUFFMAN_EMIT_SYMBOL, 85), + (23, HUFFMAN_EMIT_SYMBOL, 85), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 85), + (2, HUFFMAN_EMIT_SYMBOL, 86), + (9, HUFFMAN_EMIT_SYMBOL, 86), + (23, HUFFMAN_EMIT_SYMBOL, 86), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 86), + (2, HUFFMAN_EMIT_SYMBOL, 87), + (9, HUFFMAN_EMIT_SYMBOL, 87), + (23, HUFFMAN_EMIT_SYMBOL, 87), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 87), + (2, HUFFMAN_EMIT_SYMBOL, 89), + (9, HUFFMAN_EMIT_SYMBOL, 89), + (23, HUFFMAN_EMIT_SYMBOL, 89), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 89), + # Node 59 + (3, HUFFMAN_EMIT_SYMBOL, 85), + (6, HUFFMAN_EMIT_SYMBOL, 85), + (10, HUFFMAN_EMIT_SYMBOL, 85), + (15, HUFFMAN_EMIT_SYMBOL, 85), + (24, HUFFMAN_EMIT_SYMBOL, 85), + (31, HUFFMAN_EMIT_SYMBOL, 85), + (41, HUFFMAN_EMIT_SYMBOL, 85), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 85), + (3, HUFFMAN_EMIT_SYMBOL, 86), + (6, HUFFMAN_EMIT_SYMBOL, 86), + (10, HUFFMAN_EMIT_SYMBOL, 86), + (15, HUFFMAN_EMIT_SYMBOL, 86), + (24, HUFFMAN_EMIT_SYMBOL, 86), + (31, HUFFMAN_EMIT_SYMBOL, 86), + (41, HUFFMAN_EMIT_SYMBOL, 86), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 86), + # Node 60 + (3, HUFFMAN_EMIT_SYMBOL, 87), + (6, HUFFMAN_EMIT_SYMBOL, 87), + (10, HUFFMAN_EMIT_SYMBOL, 87), + (15, HUFFMAN_EMIT_SYMBOL, 87), + (24, HUFFMAN_EMIT_SYMBOL, 87), + (31, HUFFMAN_EMIT_SYMBOL, 87), + (41, HUFFMAN_EMIT_SYMBOL, 87), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 87), + (3, HUFFMAN_EMIT_SYMBOL, 89), + (6, HUFFMAN_EMIT_SYMBOL, 89), + (10, HUFFMAN_EMIT_SYMBOL, 89), + (15, HUFFMAN_EMIT_SYMBOL, 89), + (24, HUFFMAN_EMIT_SYMBOL, 89), + (31, HUFFMAN_EMIT_SYMBOL, 89), + (41, HUFFMAN_EMIT_SYMBOL, 89), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 89), + # Node 61 + (2, HUFFMAN_EMIT_SYMBOL, 106), + (9, HUFFMAN_EMIT_SYMBOL, 106), + (23, HUFFMAN_EMIT_SYMBOL, 106), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 106), + (2, HUFFMAN_EMIT_SYMBOL, 107), + (9, HUFFMAN_EMIT_SYMBOL, 107), + (23, HUFFMAN_EMIT_SYMBOL, 107), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 107), + (2, HUFFMAN_EMIT_SYMBOL, 113), + (9, HUFFMAN_EMIT_SYMBOL, 113), + (23, HUFFMAN_EMIT_SYMBOL, 113), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 113), + (2, HUFFMAN_EMIT_SYMBOL, 118), + (9, HUFFMAN_EMIT_SYMBOL, 118), + (23, HUFFMAN_EMIT_SYMBOL, 118), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 118), + # Node 62 + (3, HUFFMAN_EMIT_SYMBOL, 106), + (6, HUFFMAN_EMIT_SYMBOL, 106), + (10, HUFFMAN_EMIT_SYMBOL, 106), + (15, HUFFMAN_EMIT_SYMBOL, 106), + (24, HUFFMAN_EMIT_SYMBOL, 106), + (31, HUFFMAN_EMIT_SYMBOL, 106), + (41, HUFFMAN_EMIT_SYMBOL, 106), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 106), + (3, HUFFMAN_EMIT_SYMBOL, 107), + (6, HUFFMAN_EMIT_SYMBOL, 107), + (10, HUFFMAN_EMIT_SYMBOL, 107), + (15, HUFFMAN_EMIT_SYMBOL, 107), + (24, HUFFMAN_EMIT_SYMBOL, 107), + (31, HUFFMAN_EMIT_SYMBOL, 107), + (41, HUFFMAN_EMIT_SYMBOL, 107), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 107), + # Node 63 + (3, HUFFMAN_EMIT_SYMBOL, 113), + (6, HUFFMAN_EMIT_SYMBOL, 113), + (10, HUFFMAN_EMIT_SYMBOL, 113), + (15, HUFFMAN_EMIT_SYMBOL, 113), + (24, HUFFMAN_EMIT_SYMBOL, 113), + (31, HUFFMAN_EMIT_SYMBOL, 113), + (41, HUFFMAN_EMIT_SYMBOL, 113), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 113), + (3, HUFFMAN_EMIT_SYMBOL, 118), + (6, HUFFMAN_EMIT_SYMBOL, 118), + (10, HUFFMAN_EMIT_SYMBOL, 118), + (15, HUFFMAN_EMIT_SYMBOL, 118), + (24, HUFFMAN_EMIT_SYMBOL, 118), + (31, HUFFMAN_EMIT_SYMBOL, 118), + (41, HUFFMAN_EMIT_SYMBOL, 118), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 118), + # Node 64 + (1, HUFFMAN_EMIT_SYMBOL, 119), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 119), + (1, HUFFMAN_EMIT_SYMBOL, 120), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 120), + (1, HUFFMAN_EMIT_SYMBOL, 121), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 121), + (1, HUFFMAN_EMIT_SYMBOL, 122), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 122), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 38), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 42), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 44), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 59), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 88), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 90), + (75, 0, 0), + (78, 0, 0), + # Node 65 + (2, HUFFMAN_EMIT_SYMBOL, 119), + (9, HUFFMAN_EMIT_SYMBOL, 119), + (23, HUFFMAN_EMIT_SYMBOL, 119), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 119), + (2, HUFFMAN_EMIT_SYMBOL, 120), + (9, HUFFMAN_EMIT_SYMBOL, 120), + (23, HUFFMAN_EMIT_SYMBOL, 120), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 120), + (2, HUFFMAN_EMIT_SYMBOL, 121), + (9, HUFFMAN_EMIT_SYMBOL, 121), + (23, HUFFMAN_EMIT_SYMBOL, 121), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 121), + (2, HUFFMAN_EMIT_SYMBOL, 122), + (9, HUFFMAN_EMIT_SYMBOL, 122), + (23, HUFFMAN_EMIT_SYMBOL, 122), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 122), + # Node 66 + (3, HUFFMAN_EMIT_SYMBOL, 119), + (6, HUFFMAN_EMIT_SYMBOL, 119), + (10, HUFFMAN_EMIT_SYMBOL, 119), + (15, HUFFMAN_EMIT_SYMBOL, 119), + (24, HUFFMAN_EMIT_SYMBOL, 119), + (31, HUFFMAN_EMIT_SYMBOL, 119), + (41, HUFFMAN_EMIT_SYMBOL, 119), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 119), + (3, HUFFMAN_EMIT_SYMBOL, 120), + (6, HUFFMAN_EMIT_SYMBOL, 120), + (10, HUFFMAN_EMIT_SYMBOL, 120), + (15, HUFFMAN_EMIT_SYMBOL, 120), + (24, HUFFMAN_EMIT_SYMBOL, 120), + (31, HUFFMAN_EMIT_SYMBOL, 120), + (41, HUFFMAN_EMIT_SYMBOL, 120), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 120), + # Node 67 + (3, HUFFMAN_EMIT_SYMBOL, 121), + (6, HUFFMAN_EMIT_SYMBOL, 121), + (10, HUFFMAN_EMIT_SYMBOL, 121), + (15, HUFFMAN_EMIT_SYMBOL, 121), + (24, HUFFMAN_EMIT_SYMBOL, 121), + (31, HUFFMAN_EMIT_SYMBOL, 121), + (41, HUFFMAN_EMIT_SYMBOL, 121), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 121), + (3, HUFFMAN_EMIT_SYMBOL, 122), + (6, HUFFMAN_EMIT_SYMBOL, 122), + (10, HUFFMAN_EMIT_SYMBOL, 122), + (15, HUFFMAN_EMIT_SYMBOL, 122), + (24, HUFFMAN_EMIT_SYMBOL, 122), + (31, HUFFMAN_EMIT_SYMBOL, 122), + (41, HUFFMAN_EMIT_SYMBOL, 122), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 122), + # Node 68 + (1, HUFFMAN_EMIT_SYMBOL, 38), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 38), + (1, HUFFMAN_EMIT_SYMBOL, 42), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 42), + (1, HUFFMAN_EMIT_SYMBOL, 44), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 44), + (1, HUFFMAN_EMIT_SYMBOL, 59), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 59), + (1, HUFFMAN_EMIT_SYMBOL, 88), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 88), + (1, HUFFMAN_EMIT_SYMBOL, 90), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 90), + (76, 0, 0), + (77, 0, 0), + (79, 0, 0), + (81, 0, 0), + # Node 69 + (2, HUFFMAN_EMIT_SYMBOL, 38), + (9, HUFFMAN_EMIT_SYMBOL, 38), + (23, HUFFMAN_EMIT_SYMBOL, 38), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 38), + (2, HUFFMAN_EMIT_SYMBOL, 42), + (9, HUFFMAN_EMIT_SYMBOL, 42), + (23, HUFFMAN_EMIT_SYMBOL, 42), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 42), + (2, HUFFMAN_EMIT_SYMBOL, 44), + (9, HUFFMAN_EMIT_SYMBOL, 44), + (23, HUFFMAN_EMIT_SYMBOL, 44), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 44), + (2, HUFFMAN_EMIT_SYMBOL, 59), + (9, HUFFMAN_EMIT_SYMBOL, 59), + (23, HUFFMAN_EMIT_SYMBOL, 59), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 59), + # Node 70 + (3, HUFFMAN_EMIT_SYMBOL, 38), + (6, HUFFMAN_EMIT_SYMBOL, 38), + (10, HUFFMAN_EMIT_SYMBOL, 38), + (15, HUFFMAN_EMIT_SYMBOL, 38), + (24, HUFFMAN_EMIT_SYMBOL, 38), + (31, HUFFMAN_EMIT_SYMBOL, 38), + (41, HUFFMAN_EMIT_SYMBOL, 38), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 38), + (3, HUFFMAN_EMIT_SYMBOL, 42), + (6, HUFFMAN_EMIT_SYMBOL, 42), + (10, HUFFMAN_EMIT_SYMBOL, 42), + (15, HUFFMAN_EMIT_SYMBOL, 42), + (24, HUFFMAN_EMIT_SYMBOL, 42), + (31, HUFFMAN_EMIT_SYMBOL, 42), + (41, HUFFMAN_EMIT_SYMBOL, 42), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 42), + # Node 71 + (3, HUFFMAN_EMIT_SYMBOL, 44), + (6, HUFFMAN_EMIT_SYMBOL, 44), + (10, HUFFMAN_EMIT_SYMBOL, 44), + (15, HUFFMAN_EMIT_SYMBOL, 44), + (24, HUFFMAN_EMIT_SYMBOL, 44), + (31, HUFFMAN_EMIT_SYMBOL, 44), + (41, HUFFMAN_EMIT_SYMBOL, 44), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 44), + (3, HUFFMAN_EMIT_SYMBOL, 59), + (6, HUFFMAN_EMIT_SYMBOL, 59), + (10, HUFFMAN_EMIT_SYMBOL, 59), + (15, HUFFMAN_EMIT_SYMBOL, 59), + (24, HUFFMAN_EMIT_SYMBOL, 59), + (31, HUFFMAN_EMIT_SYMBOL, 59), + (41, HUFFMAN_EMIT_SYMBOL, 59), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 59), + # Node 72 + (2, HUFFMAN_EMIT_SYMBOL, 88), + (9, HUFFMAN_EMIT_SYMBOL, 88), + (23, HUFFMAN_EMIT_SYMBOL, 88), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 88), + (2, HUFFMAN_EMIT_SYMBOL, 90), + (9, HUFFMAN_EMIT_SYMBOL, 90), + (23, HUFFMAN_EMIT_SYMBOL, 90), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 90), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 33), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 34), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 40), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 41), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 63), + (80, 0, 0), + (82, 0, 0), + (84, 0, 0), + # Node 73 + (3, HUFFMAN_EMIT_SYMBOL, 88), + (6, HUFFMAN_EMIT_SYMBOL, 88), + (10, HUFFMAN_EMIT_SYMBOL, 88), + (15, HUFFMAN_EMIT_SYMBOL, 88), + (24, HUFFMAN_EMIT_SYMBOL, 88), + (31, HUFFMAN_EMIT_SYMBOL, 88), + (41, HUFFMAN_EMIT_SYMBOL, 88), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 88), + (3, HUFFMAN_EMIT_SYMBOL, 90), + (6, HUFFMAN_EMIT_SYMBOL, 90), + (10, HUFFMAN_EMIT_SYMBOL, 90), + (15, HUFFMAN_EMIT_SYMBOL, 90), + (24, HUFFMAN_EMIT_SYMBOL, 90), + (31, HUFFMAN_EMIT_SYMBOL, 90), + (41, HUFFMAN_EMIT_SYMBOL, 90), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 90), + # Node 74 + (1, HUFFMAN_EMIT_SYMBOL, 33), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 33), + (1, HUFFMAN_EMIT_SYMBOL, 34), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 34), + (1, HUFFMAN_EMIT_SYMBOL, 40), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 40), + (1, HUFFMAN_EMIT_SYMBOL, 41), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 41), + (1, HUFFMAN_EMIT_SYMBOL, 63), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 63), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 39), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 43), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 124), + (83, 0, 0), + (85, 0, 0), + (88, 0, 0), + # Node 75 + (2, HUFFMAN_EMIT_SYMBOL, 33), + (9, HUFFMAN_EMIT_SYMBOL, 33), + (23, HUFFMAN_EMIT_SYMBOL, 33), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 33), + (2, HUFFMAN_EMIT_SYMBOL, 34), + (9, HUFFMAN_EMIT_SYMBOL, 34), + (23, HUFFMAN_EMIT_SYMBOL, 34), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 34), + (2, HUFFMAN_EMIT_SYMBOL, 40), + (9, HUFFMAN_EMIT_SYMBOL, 40), + (23, HUFFMAN_EMIT_SYMBOL, 40), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 40), + (2, HUFFMAN_EMIT_SYMBOL, 41), + (9, HUFFMAN_EMIT_SYMBOL, 41), + (23, HUFFMAN_EMIT_SYMBOL, 41), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 41), + # Node 76 + (3, HUFFMAN_EMIT_SYMBOL, 33), + (6, HUFFMAN_EMIT_SYMBOL, 33), + (10, HUFFMAN_EMIT_SYMBOL, 33), + (15, HUFFMAN_EMIT_SYMBOL, 33), + (24, HUFFMAN_EMIT_SYMBOL, 33), + (31, HUFFMAN_EMIT_SYMBOL, 33), + (41, HUFFMAN_EMIT_SYMBOL, 33), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 33), + (3, HUFFMAN_EMIT_SYMBOL, 34), + (6, HUFFMAN_EMIT_SYMBOL, 34), + (10, HUFFMAN_EMIT_SYMBOL, 34), + (15, HUFFMAN_EMIT_SYMBOL, 34), + (24, HUFFMAN_EMIT_SYMBOL, 34), + (31, HUFFMAN_EMIT_SYMBOL, 34), + (41, HUFFMAN_EMIT_SYMBOL, 34), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 34), + # Node 77 + (3, HUFFMAN_EMIT_SYMBOL, 40), + (6, HUFFMAN_EMIT_SYMBOL, 40), + (10, HUFFMAN_EMIT_SYMBOL, 40), + (15, HUFFMAN_EMIT_SYMBOL, 40), + (24, HUFFMAN_EMIT_SYMBOL, 40), + (31, HUFFMAN_EMIT_SYMBOL, 40), + (41, HUFFMAN_EMIT_SYMBOL, 40), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 40), + (3, HUFFMAN_EMIT_SYMBOL, 41), + (6, HUFFMAN_EMIT_SYMBOL, 41), + (10, HUFFMAN_EMIT_SYMBOL, 41), + (15, HUFFMAN_EMIT_SYMBOL, 41), + (24, HUFFMAN_EMIT_SYMBOL, 41), + (31, HUFFMAN_EMIT_SYMBOL, 41), + (41, HUFFMAN_EMIT_SYMBOL, 41), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 41), + # Node 78 + (2, HUFFMAN_EMIT_SYMBOL, 63), + (9, HUFFMAN_EMIT_SYMBOL, 63), + (23, HUFFMAN_EMIT_SYMBOL, 63), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 63), + (1, HUFFMAN_EMIT_SYMBOL, 39), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 39), + (1, HUFFMAN_EMIT_SYMBOL, 43), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 43), + (1, HUFFMAN_EMIT_SYMBOL, 124), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 124), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 35), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 62), + (86, 0, 0), + (87, 0, 0), + (89, 0, 0), + (90, 0, 0), + # Node 79 + (3, HUFFMAN_EMIT_SYMBOL, 63), + (6, HUFFMAN_EMIT_SYMBOL, 63), + (10, HUFFMAN_EMIT_SYMBOL, 63), + (15, HUFFMAN_EMIT_SYMBOL, 63), + (24, HUFFMAN_EMIT_SYMBOL, 63), + (31, HUFFMAN_EMIT_SYMBOL, 63), + (41, HUFFMAN_EMIT_SYMBOL, 63), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 63), + (2, HUFFMAN_EMIT_SYMBOL, 39), + (9, HUFFMAN_EMIT_SYMBOL, 39), + (23, HUFFMAN_EMIT_SYMBOL, 39), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 39), + (2, HUFFMAN_EMIT_SYMBOL, 43), + (9, HUFFMAN_EMIT_SYMBOL, 43), + (23, HUFFMAN_EMIT_SYMBOL, 43), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 43), + # Node 80 + (3, HUFFMAN_EMIT_SYMBOL, 39), + (6, HUFFMAN_EMIT_SYMBOL, 39), + (10, HUFFMAN_EMIT_SYMBOL, 39), + (15, HUFFMAN_EMIT_SYMBOL, 39), + (24, HUFFMAN_EMIT_SYMBOL, 39), + (31, HUFFMAN_EMIT_SYMBOL, 39), + (41, HUFFMAN_EMIT_SYMBOL, 39), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 39), + (3, HUFFMAN_EMIT_SYMBOL, 43), + (6, HUFFMAN_EMIT_SYMBOL, 43), + (10, HUFFMAN_EMIT_SYMBOL, 43), + (15, HUFFMAN_EMIT_SYMBOL, 43), + (24, HUFFMAN_EMIT_SYMBOL, 43), + (31, HUFFMAN_EMIT_SYMBOL, 43), + (41, HUFFMAN_EMIT_SYMBOL, 43), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 43), + # Node 81 + (2, HUFFMAN_EMIT_SYMBOL, 124), + (9, HUFFMAN_EMIT_SYMBOL, 124), + (23, HUFFMAN_EMIT_SYMBOL, 124), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 124), + (1, HUFFMAN_EMIT_SYMBOL, 35), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 35), + (1, HUFFMAN_EMIT_SYMBOL, 62), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 62), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 0), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 36), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 64), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 91), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 93), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 126), + (91, 0, 0), + (92, 0, 0), + # Node 82 + (3, HUFFMAN_EMIT_SYMBOL, 124), + (6, HUFFMAN_EMIT_SYMBOL, 124), + (10, HUFFMAN_EMIT_SYMBOL, 124), + (15, HUFFMAN_EMIT_SYMBOL, 124), + (24, HUFFMAN_EMIT_SYMBOL, 124), + (31, HUFFMAN_EMIT_SYMBOL, 124), + (41, HUFFMAN_EMIT_SYMBOL, 124), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 124), + (2, HUFFMAN_EMIT_SYMBOL, 35), + (9, HUFFMAN_EMIT_SYMBOL, 35), + (23, HUFFMAN_EMIT_SYMBOL, 35), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 35), + (2, HUFFMAN_EMIT_SYMBOL, 62), + (9, HUFFMAN_EMIT_SYMBOL, 62), + (23, HUFFMAN_EMIT_SYMBOL, 62), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 62), + # Node 83 + (3, HUFFMAN_EMIT_SYMBOL, 35), + (6, HUFFMAN_EMIT_SYMBOL, 35), + (10, HUFFMAN_EMIT_SYMBOL, 35), + (15, HUFFMAN_EMIT_SYMBOL, 35), + (24, HUFFMAN_EMIT_SYMBOL, 35), + (31, HUFFMAN_EMIT_SYMBOL, 35), + (41, HUFFMAN_EMIT_SYMBOL, 35), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 35), + (3, HUFFMAN_EMIT_SYMBOL, 62), + (6, HUFFMAN_EMIT_SYMBOL, 62), + (10, HUFFMAN_EMIT_SYMBOL, 62), + (15, HUFFMAN_EMIT_SYMBOL, 62), + (24, HUFFMAN_EMIT_SYMBOL, 62), + (31, HUFFMAN_EMIT_SYMBOL, 62), + (41, HUFFMAN_EMIT_SYMBOL, 62), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 62), + # Node 84 + (1, HUFFMAN_EMIT_SYMBOL, 0), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 0), + (1, HUFFMAN_EMIT_SYMBOL, 36), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 36), + (1, HUFFMAN_EMIT_SYMBOL, 64), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 64), + (1, HUFFMAN_EMIT_SYMBOL, 91), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 91), + (1, HUFFMAN_EMIT_SYMBOL, 93), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 93), + (1, HUFFMAN_EMIT_SYMBOL, 126), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 126), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 94), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 125), + (93, 0, 0), + (94, 0, 0), + # Node 85 + (2, HUFFMAN_EMIT_SYMBOL, 0), + (9, HUFFMAN_EMIT_SYMBOL, 0), + (23, HUFFMAN_EMIT_SYMBOL, 0), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 0), + (2, HUFFMAN_EMIT_SYMBOL, 36), + (9, HUFFMAN_EMIT_SYMBOL, 36), + (23, HUFFMAN_EMIT_SYMBOL, 36), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 36), + (2, HUFFMAN_EMIT_SYMBOL, 64), + (9, HUFFMAN_EMIT_SYMBOL, 64), + (23, HUFFMAN_EMIT_SYMBOL, 64), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 64), + (2, HUFFMAN_EMIT_SYMBOL, 91), + (9, HUFFMAN_EMIT_SYMBOL, 91), + (23, HUFFMAN_EMIT_SYMBOL, 91), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 91), + # Node 86 + (3, HUFFMAN_EMIT_SYMBOL, 0), + (6, HUFFMAN_EMIT_SYMBOL, 0), + (10, HUFFMAN_EMIT_SYMBOL, 0), + (15, HUFFMAN_EMIT_SYMBOL, 0), + (24, HUFFMAN_EMIT_SYMBOL, 0), + (31, HUFFMAN_EMIT_SYMBOL, 0), + (41, HUFFMAN_EMIT_SYMBOL, 0), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 0), + (3, HUFFMAN_EMIT_SYMBOL, 36), + (6, HUFFMAN_EMIT_SYMBOL, 36), + (10, HUFFMAN_EMIT_SYMBOL, 36), + (15, HUFFMAN_EMIT_SYMBOL, 36), + (24, HUFFMAN_EMIT_SYMBOL, 36), + (31, HUFFMAN_EMIT_SYMBOL, 36), + (41, HUFFMAN_EMIT_SYMBOL, 36), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 36), + # Node 87 + (3, HUFFMAN_EMIT_SYMBOL, 64), + (6, HUFFMAN_EMIT_SYMBOL, 64), + (10, HUFFMAN_EMIT_SYMBOL, 64), + (15, HUFFMAN_EMIT_SYMBOL, 64), + (24, HUFFMAN_EMIT_SYMBOL, 64), + (31, HUFFMAN_EMIT_SYMBOL, 64), + (41, HUFFMAN_EMIT_SYMBOL, 64), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 64), + (3, HUFFMAN_EMIT_SYMBOL, 91), + (6, HUFFMAN_EMIT_SYMBOL, 91), + (10, HUFFMAN_EMIT_SYMBOL, 91), + (15, HUFFMAN_EMIT_SYMBOL, 91), + (24, HUFFMAN_EMIT_SYMBOL, 91), + (31, HUFFMAN_EMIT_SYMBOL, 91), + (41, HUFFMAN_EMIT_SYMBOL, 91), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 91), + # Node 88 + (2, HUFFMAN_EMIT_SYMBOL, 93), + (9, HUFFMAN_EMIT_SYMBOL, 93), + (23, HUFFMAN_EMIT_SYMBOL, 93), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 93), + (2, HUFFMAN_EMIT_SYMBOL, 126), + (9, HUFFMAN_EMIT_SYMBOL, 126), + (23, HUFFMAN_EMIT_SYMBOL, 126), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 126), + (1, HUFFMAN_EMIT_SYMBOL, 94), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 94), + (1, HUFFMAN_EMIT_SYMBOL, 125), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 125), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 60), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 96), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 123), + (95, 0, 0), + # Node 89 + (3, HUFFMAN_EMIT_SYMBOL, 93), + (6, HUFFMAN_EMIT_SYMBOL, 93), + (10, HUFFMAN_EMIT_SYMBOL, 93), + (15, HUFFMAN_EMIT_SYMBOL, 93), + (24, HUFFMAN_EMIT_SYMBOL, 93), + (31, HUFFMAN_EMIT_SYMBOL, 93), + (41, HUFFMAN_EMIT_SYMBOL, 93), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 93), + (3, HUFFMAN_EMIT_SYMBOL, 126), + (6, HUFFMAN_EMIT_SYMBOL, 126), + (10, HUFFMAN_EMIT_SYMBOL, 126), + (15, HUFFMAN_EMIT_SYMBOL, 126), + (24, HUFFMAN_EMIT_SYMBOL, 126), + (31, HUFFMAN_EMIT_SYMBOL, 126), + (41, HUFFMAN_EMIT_SYMBOL, 126), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 126), + # Node 90 + (2, HUFFMAN_EMIT_SYMBOL, 94), + (9, HUFFMAN_EMIT_SYMBOL, 94), + (23, HUFFMAN_EMIT_SYMBOL, 94), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 94), + (2, HUFFMAN_EMIT_SYMBOL, 125), + (9, HUFFMAN_EMIT_SYMBOL, 125), + (23, HUFFMAN_EMIT_SYMBOL, 125), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 125), + (1, HUFFMAN_EMIT_SYMBOL, 60), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 60), + (1, HUFFMAN_EMIT_SYMBOL, 96), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 96), + (1, HUFFMAN_EMIT_SYMBOL, 123), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 123), + (96, 0, 0), + (110, 0, 0), + # Node 91 + (3, HUFFMAN_EMIT_SYMBOL, 94), + (6, HUFFMAN_EMIT_SYMBOL, 94), + (10, HUFFMAN_EMIT_SYMBOL, 94), + (15, HUFFMAN_EMIT_SYMBOL, 94), + (24, HUFFMAN_EMIT_SYMBOL, 94), + (31, HUFFMAN_EMIT_SYMBOL, 94), + (41, HUFFMAN_EMIT_SYMBOL, 94), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 94), + (3, HUFFMAN_EMIT_SYMBOL, 125), + (6, HUFFMAN_EMIT_SYMBOL, 125), + (10, HUFFMAN_EMIT_SYMBOL, 125), + (15, HUFFMAN_EMIT_SYMBOL, 125), + (24, HUFFMAN_EMIT_SYMBOL, 125), + (31, HUFFMAN_EMIT_SYMBOL, 125), + (41, HUFFMAN_EMIT_SYMBOL, 125), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 125), + # Node 92 + (2, HUFFMAN_EMIT_SYMBOL, 60), + (9, HUFFMAN_EMIT_SYMBOL, 60), + (23, HUFFMAN_EMIT_SYMBOL, 60), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 60), + (2, HUFFMAN_EMIT_SYMBOL, 96), + (9, HUFFMAN_EMIT_SYMBOL, 96), + (23, HUFFMAN_EMIT_SYMBOL, 96), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 96), + (2, HUFFMAN_EMIT_SYMBOL, 123), + (9, HUFFMAN_EMIT_SYMBOL, 123), + (23, HUFFMAN_EMIT_SYMBOL, 123), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 123), + (97, 0, 0), + (101, 0, 0), + (111, 0, 0), + (133, 0, 0), + # Node 93 + (3, HUFFMAN_EMIT_SYMBOL, 60), + (6, HUFFMAN_EMIT_SYMBOL, 60), + (10, HUFFMAN_EMIT_SYMBOL, 60), + (15, HUFFMAN_EMIT_SYMBOL, 60), + (24, HUFFMAN_EMIT_SYMBOL, 60), + (31, HUFFMAN_EMIT_SYMBOL, 60), + (41, HUFFMAN_EMIT_SYMBOL, 60), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 60), + (3, HUFFMAN_EMIT_SYMBOL, 96), + (6, HUFFMAN_EMIT_SYMBOL, 96), + (10, HUFFMAN_EMIT_SYMBOL, 96), + (15, HUFFMAN_EMIT_SYMBOL, 96), + (24, HUFFMAN_EMIT_SYMBOL, 96), + (31, HUFFMAN_EMIT_SYMBOL, 96), + (41, HUFFMAN_EMIT_SYMBOL, 96), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 96), + # Node 94 + (3, HUFFMAN_EMIT_SYMBOL, 123), + (6, HUFFMAN_EMIT_SYMBOL, 123), + (10, HUFFMAN_EMIT_SYMBOL, 123), + (15, HUFFMAN_EMIT_SYMBOL, 123), + (24, HUFFMAN_EMIT_SYMBOL, 123), + (31, HUFFMAN_EMIT_SYMBOL, 123), + (41, HUFFMAN_EMIT_SYMBOL, 123), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 123), + (98, 0, 0), + (99, 0, 0), + (102, 0, 0), + (105, 0, 0), + (112, 0, 0), + (119, 0, 0), + (134, 0, 0), + (153, 0, 0), + # Node 95 + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 92), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 195), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 208), + (100, 0, 0), + (103, 0, 0), + (104, 0, 0), + (106, 0, 0), + (107, 0, 0), + (113, 0, 0), + (116, 0, 0), + (120, 0, 0), + (126, 0, 0), + (135, 0, 0), + (142, 0, 0), + (154, 0, 0), + (169, 0, 0), + # Node 96 + (1, HUFFMAN_EMIT_SYMBOL, 92), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 92), + (1, HUFFMAN_EMIT_SYMBOL, 195), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 195), + (1, HUFFMAN_EMIT_SYMBOL, 208), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 208), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 128), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 130), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 131), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 162), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 184), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 194), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 224), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 226), + (108, 0, 0), + (109, 0, 0), + # Node 97 + (2, HUFFMAN_EMIT_SYMBOL, 92), + (9, HUFFMAN_EMIT_SYMBOL, 92), + (23, HUFFMAN_EMIT_SYMBOL, 92), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 92), + (2, HUFFMAN_EMIT_SYMBOL, 195), + (9, HUFFMAN_EMIT_SYMBOL, 195), + (23, HUFFMAN_EMIT_SYMBOL, 195), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 195), + (2, HUFFMAN_EMIT_SYMBOL, 208), + (9, HUFFMAN_EMIT_SYMBOL, 208), + (23, HUFFMAN_EMIT_SYMBOL, 208), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 208), + (1, HUFFMAN_EMIT_SYMBOL, 128), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 128), + (1, HUFFMAN_EMIT_SYMBOL, 130), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 130), + # Node 98 + (3, HUFFMAN_EMIT_SYMBOL, 92), + (6, HUFFMAN_EMIT_SYMBOL, 92), + (10, HUFFMAN_EMIT_SYMBOL, 92), + (15, HUFFMAN_EMIT_SYMBOL, 92), + (24, HUFFMAN_EMIT_SYMBOL, 92), + (31, HUFFMAN_EMIT_SYMBOL, 92), + (41, HUFFMAN_EMIT_SYMBOL, 92), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 92), + (3, HUFFMAN_EMIT_SYMBOL, 195), + (6, HUFFMAN_EMIT_SYMBOL, 195), + (10, HUFFMAN_EMIT_SYMBOL, 195), + (15, HUFFMAN_EMIT_SYMBOL, 195), + (24, HUFFMAN_EMIT_SYMBOL, 195), + (31, HUFFMAN_EMIT_SYMBOL, 195), + (41, HUFFMAN_EMIT_SYMBOL, 195), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 195), + # Node 99 + (3, HUFFMAN_EMIT_SYMBOL, 208), + (6, HUFFMAN_EMIT_SYMBOL, 208), + (10, HUFFMAN_EMIT_SYMBOL, 208), + (15, HUFFMAN_EMIT_SYMBOL, 208), + (24, HUFFMAN_EMIT_SYMBOL, 208), + (31, HUFFMAN_EMIT_SYMBOL, 208), + (41, HUFFMAN_EMIT_SYMBOL, 208), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 208), + (2, HUFFMAN_EMIT_SYMBOL, 128), + (9, HUFFMAN_EMIT_SYMBOL, 128), + (23, HUFFMAN_EMIT_SYMBOL, 128), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 128), + (2, HUFFMAN_EMIT_SYMBOL, 130), + (9, HUFFMAN_EMIT_SYMBOL, 130), + (23, HUFFMAN_EMIT_SYMBOL, 130), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 130), + # Node 100 + (3, HUFFMAN_EMIT_SYMBOL, 128), + (6, HUFFMAN_EMIT_SYMBOL, 128), + (10, HUFFMAN_EMIT_SYMBOL, 128), + (15, HUFFMAN_EMIT_SYMBOL, 128), + (24, HUFFMAN_EMIT_SYMBOL, 128), + (31, HUFFMAN_EMIT_SYMBOL, 128), + (41, HUFFMAN_EMIT_SYMBOL, 128), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 128), + (3, HUFFMAN_EMIT_SYMBOL, 130), + (6, HUFFMAN_EMIT_SYMBOL, 130), + (10, HUFFMAN_EMIT_SYMBOL, 130), + (15, HUFFMAN_EMIT_SYMBOL, 130), + (24, HUFFMAN_EMIT_SYMBOL, 130), + (31, HUFFMAN_EMIT_SYMBOL, 130), + (41, HUFFMAN_EMIT_SYMBOL, 130), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 130), + # Node 101 + (1, HUFFMAN_EMIT_SYMBOL, 131), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 131), + (1, HUFFMAN_EMIT_SYMBOL, 162), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 162), + (1, HUFFMAN_EMIT_SYMBOL, 184), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 184), + (1, HUFFMAN_EMIT_SYMBOL, 194), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 194), + (1, HUFFMAN_EMIT_SYMBOL, 224), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 224), + (1, HUFFMAN_EMIT_SYMBOL, 226), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 226), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 153), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 161), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 167), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 172), + # Node 102 + (2, HUFFMAN_EMIT_SYMBOL, 131), + (9, HUFFMAN_EMIT_SYMBOL, 131), + (23, HUFFMAN_EMIT_SYMBOL, 131), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 131), + (2, HUFFMAN_EMIT_SYMBOL, 162), + (9, HUFFMAN_EMIT_SYMBOL, 162), + (23, HUFFMAN_EMIT_SYMBOL, 162), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 162), + (2, HUFFMAN_EMIT_SYMBOL, 184), + (9, HUFFMAN_EMIT_SYMBOL, 184), + (23, HUFFMAN_EMIT_SYMBOL, 184), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 184), + (2, HUFFMAN_EMIT_SYMBOL, 194), + (9, HUFFMAN_EMIT_SYMBOL, 194), + (23, HUFFMAN_EMIT_SYMBOL, 194), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 194), + # Node 103 + (3, HUFFMAN_EMIT_SYMBOL, 131), + (6, HUFFMAN_EMIT_SYMBOL, 131), + (10, HUFFMAN_EMIT_SYMBOL, 131), + (15, HUFFMAN_EMIT_SYMBOL, 131), + (24, HUFFMAN_EMIT_SYMBOL, 131), + (31, HUFFMAN_EMIT_SYMBOL, 131), + (41, HUFFMAN_EMIT_SYMBOL, 131), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 131), + (3, HUFFMAN_EMIT_SYMBOL, 162), + (6, HUFFMAN_EMIT_SYMBOL, 162), + (10, HUFFMAN_EMIT_SYMBOL, 162), + (15, HUFFMAN_EMIT_SYMBOL, 162), + (24, HUFFMAN_EMIT_SYMBOL, 162), + (31, HUFFMAN_EMIT_SYMBOL, 162), + (41, HUFFMAN_EMIT_SYMBOL, 162), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 162), + # Node 104 + (3, HUFFMAN_EMIT_SYMBOL, 184), + (6, HUFFMAN_EMIT_SYMBOL, 184), + (10, HUFFMAN_EMIT_SYMBOL, 184), + (15, HUFFMAN_EMIT_SYMBOL, 184), + (24, HUFFMAN_EMIT_SYMBOL, 184), + (31, HUFFMAN_EMIT_SYMBOL, 184), + (41, HUFFMAN_EMIT_SYMBOL, 184), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 184), + (3, HUFFMAN_EMIT_SYMBOL, 194), + (6, HUFFMAN_EMIT_SYMBOL, 194), + (10, HUFFMAN_EMIT_SYMBOL, 194), + (15, HUFFMAN_EMIT_SYMBOL, 194), + (24, HUFFMAN_EMIT_SYMBOL, 194), + (31, HUFFMAN_EMIT_SYMBOL, 194), + (41, HUFFMAN_EMIT_SYMBOL, 194), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 194), + # Node 105 + (2, HUFFMAN_EMIT_SYMBOL, 224), + (9, HUFFMAN_EMIT_SYMBOL, 224), + (23, HUFFMAN_EMIT_SYMBOL, 224), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 224), + (2, HUFFMAN_EMIT_SYMBOL, 226), + (9, HUFFMAN_EMIT_SYMBOL, 226), + (23, HUFFMAN_EMIT_SYMBOL, 226), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 226), + (1, HUFFMAN_EMIT_SYMBOL, 153), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 153), + (1, HUFFMAN_EMIT_SYMBOL, 161), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 161), + (1, HUFFMAN_EMIT_SYMBOL, 167), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 167), + (1, HUFFMAN_EMIT_SYMBOL, 172), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 172), + # Node 106 + (3, HUFFMAN_EMIT_SYMBOL, 224), + (6, HUFFMAN_EMIT_SYMBOL, 224), + (10, HUFFMAN_EMIT_SYMBOL, 224), + (15, HUFFMAN_EMIT_SYMBOL, 224), + (24, HUFFMAN_EMIT_SYMBOL, 224), + (31, HUFFMAN_EMIT_SYMBOL, 224), + (41, HUFFMAN_EMIT_SYMBOL, 224), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 224), + (3, HUFFMAN_EMIT_SYMBOL, 226), + (6, HUFFMAN_EMIT_SYMBOL, 226), + (10, HUFFMAN_EMIT_SYMBOL, 226), + (15, HUFFMAN_EMIT_SYMBOL, 226), + (24, HUFFMAN_EMIT_SYMBOL, 226), + (31, HUFFMAN_EMIT_SYMBOL, 226), + (41, HUFFMAN_EMIT_SYMBOL, 226), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 226), + # Node 107 + (2, HUFFMAN_EMIT_SYMBOL, 153), + (9, HUFFMAN_EMIT_SYMBOL, 153), + (23, HUFFMAN_EMIT_SYMBOL, 153), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 153), + (2, HUFFMAN_EMIT_SYMBOL, 161), + (9, HUFFMAN_EMIT_SYMBOL, 161), + (23, HUFFMAN_EMIT_SYMBOL, 161), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 161), + (2, HUFFMAN_EMIT_SYMBOL, 167), + (9, HUFFMAN_EMIT_SYMBOL, 167), + (23, HUFFMAN_EMIT_SYMBOL, 167), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 167), + (2, HUFFMAN_EMIT_SYMBOL, 172), + (9, HUFFMAN_EMIT_SYMBOL, 172), + (23, HUFFMAN_EMIT_SYMBOL, 172), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 172), + # Node 108 + (3, HUFFMAN_EMIT_SYMBOL, 153), + (6, HUFFMAN_EMIT_SYMBOL, 153), + (10, HUFFMAN_EMIT_SYMBOL, 153), + (15, HUFFMAN_EMIT_SYMBOL, 153), + (24, HUFFMAN_EMIT_SYMBOL, 153), + (31, HUFFMAN_EMIT_SYMBOL, 153), + (41, HUFFMAN_EMIT_SYMBOL, 153), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 153), + (3, HUFFMAN_EMIT_SYMBOL, 161), + (6, HUFFMAN_EMIT_SYMBOL, 161), + (10, HUFFMAN_EMIT_SYMBOL, 161), + (15, HUFFMAN_EMIT_SYMBOL, 161), + (24, HUFFMAN_EMIT_SYMBOL, 161), + (31, HUFFMAN_EMIT_SYMBOL, 161), + (41, HUFFMAN_EMIT_SYMBOL, 161), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 161), + # Node 109 + (3, HUFFMAN_EMIT_SYMBOL, 167), + (6, HUFFMAN_EMIT_SYMBOL, 167), + (10, HUFFMAN_EMIT_SYMBOL, 167), + (15, HUFFMAN_EMIT_SYMBOL, 167), + (24, HUFFMAN_EMIT_SYMBOL, 167), + (31, HUFFMAN_EMIT_SYMBOL, 167), + (41, HUFFMAN_EMIT_SYMBOL, 167), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 167), + (3, HUFFMAN_EMIT_SYMBOL, 172), + (6, HUFFMAN_EMIT_SYMBOL, 172), + (10, HUFFMAN_EMIT_SYMBOL, 172), + (15, HUFFMAN_EMIT_SYMBOL, 172), + (24, HUFFMAN_EMIT_SYMBOL, 172), + (31, HUFFMAN_EMIT_SYMBOL, 172), + (41, HUFFMAN_EMIT_SYMBOL, 172), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 172), + # Node 110 + (114, 0, 0), + (115, 0, 0), + (117, 0, 0), + (118, 0, 0), + (121, 0, 0), + (123, 0, 0), + (127, 0, 0), + (130, 0, 0), + (136, 0, 0), + (139, 0, 0), + (143, 0, 0), + (146, 0, 0), + (155, 0, 0), + (162, 0, 0), + (170, 0, 0), + (180, 0, 0), + # Node 111 + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 176), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 177), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 179), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 209), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 216), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 217), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 227), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 229), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 230), + (122, 0, 0), + (124, 0, 0), + (125, 0, 0), + (128, 0, 0), + (129, 0, 0), + (131, 0, 0), + (132, 0, 0), + # Node 112 + (1, HUFFMAN_EMIT_SYMBOL, 176), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 176), + (1, HUFFMAN_EMIT_SYMBOL, 177), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 177), + (1, HUFFMAN_EMIT_SYMBOL, 179), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 179), + (1, HUFFMAN_EMIT_SYMBOL, 209), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 209), + (1, HUFFMAN_EMIT_SYMBOL, 216), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 216), + (1, HUFFMAN_EMIT_SYMBOL, 217), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 217), + (1, HUFFMAN_EMIT_SYMBOL, 227), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 227), + (1, HUFFMAN_EMIT_SYMBOL, 229), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 229), + # Node 113 + (2, HUFFMAN_EMIT_SYMBOL, 176), + (9, HUFFMAN_EMIT_SYMBOL, 176), + (23, HUFFMAN_EMIT_SYMBOL, 176), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 176), + (2, HUFFMAN_EMIT_SYMBOL, 177), + (9, HUFFMAN_EMIT_SYMBOL, 177), + (23, HUFFMAN_EMIT_SYMBOL, 177), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 177), + (2, HUFFMAN_EMIT_SYMBOL, 179), + (9, HUFFMAN_EMIT_SYMBOL, 179), + (23, HUFFMAN_EMIT_SYMBOL, 179), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 179), + (2, HUFFMAN_EMIT_SYMBOL, 209), + (9, HUFFMAN_EMIT_SYMBOL, 209), + (23, HUFFMAN_EMIT_SYMBOL, 209), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 209), + # Node 114 + (3, HUFFMAN_EMIT_SYMBOL, 176), + (6, HUFFMAN_EMIT_SYMBOL, 176), + (10, HUFFMAN_EMIT_SYMBOL, 176), + (15, HUFFMAN_EMIT_SYMBOL, 176), + (24, HUFFMAN_EMIT_SYMBOL, 176), + (31, HUFFMAN_EMIT_SYMBOL, 176), + (41, HUFFMAN_EMIT_SYMBOL, 176), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 176), + (3, HUFFMAN_EMIT_SYMBOL, 177), + (6, HUFFMAN_EMIT_SYMBOL, 177), + (10, HUFFMAN_EMIT_SYMBOL, 177), + (15, HUFFMAN_EMIT_SYMBOL, 177), + (24, HUFFMAN_EMIT_SYMBOL, 177), + (31, HUFFMAN_EMIT_SYMBOL, 177), + (41, HUFFMAN_EMIT_SYMBOL, 177), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 177), + # Node 115 + (3, HUFFMAN_EMIT_SYMBOL, 179), + (6, HUFFMAN_EMIT_SYMBOL, 179), + (10, HUFFMAN_EMIT_SYMBOL, 179), + (15, HUFFMAN_EMIT_SYMBOL, 179), + (24, HUFFMAN_EMIT_SYMBOL, 179), + (31, HUFFMAN_EMIT_SYMBOL, 179), + (41, HUFFMAN_EMIT_SYMBOL, 179), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 179), + (3, HUFFMAN_EMIT_SYMBOL, 209), + (6, HUFFMAN_EMIT_SYMBOL, 209), + (10, HUFFMAN_EMIT_SYMBOL, 209), + (15, HUFFMAN_EMIT_SYMBOL, 209), + (24, HUFFMAN_EMIT_SYMBOL, 209), + (31, HUFFMAN_EMIT_SYMBOL, 209), + (41, HUFFMAN_EMIT_SYMBOL, 209), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 209), + # Node 116 + (2, HUFFMAN_EMIT_SYMBOL, 216), + (9, HUFFMAN_EMIT_SYMBOL, 216), + (23, HUFFMAN_EMIT_SYMBOL, 216), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 216), + (2, HUFFMAN_EMIT_SYMBOL, 217), + (9, HUFFMAN_EMIT_SYMBOL, 217), + (23, HUFFMAN_EMIT_SYMBOL, 217), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 217), + (2, HUFFMAN_EMIT_SYMBOL, 227), + (9, HUFFMAN_EMIT_SYMBOL, 227), + (23, HUFFMAN_EMIT_SYMBOL, 227), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 227), + (2, HUFFMAN_EMIT_SYMBOL, 229), + (9, HUFFMAN_EMIT_SYMBOL, 229), + (23, HUFFMAN_EMIT_SYMBOL, 229), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 229), + # Node 117 + (3, HUFFMAN_EMIT_SYMBOL, 216), + (6, HUFFMAN_EMIT_SYMBOL, 216), + (10, HUFFMAN_EMIT_SYMBOL, 216), + (15, HUFFMAN_EMIT_SYMBOL, 216), + (24, HUFFMAN_EMIT_SYMBOL, 216), + (31, HUFFMAN_EMIT_SYMBOL, 216), + (41, HUFFMAN_EMIT_SYMBOL, 216), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 216), + (3, HUFFMAN_EMIT_SYMBOL, 217), + (6, HUFFMAN_EMIT_SYMBOL, 217), + (10, HUFFMAN_EMIT_SYMBOL, 217), + (15, HUFFMAN_EMIT_SYMBOL, 217), + (24, HUFFMAN_EMIT_SYMBOL, 217), + (31, HUFFMAN_EMIT_SYMBOL, 217), + (41, HUFFMAN_EMIT_SYMBOL, 217), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 217), + # Node 118 + (3, HUFFMAN_EMIT_SYMBOL, 227), + (6, HUFFMAN_EMIT_SYMBOL, 227), + (10, HUFFMAN_EMIT_SYMBOL, 227), + (15, HUFFMAN_EMIT_SYMBOL, 227), + (24, HUFFMAN_EMIT_SYMBOL, 227), + (31, HUFFMAN_EMIT_SYMBOL, 227), + (41, HUFFMAN_EMIT_SYMBOL, 227), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 227), + (3, HUFFMAN_EMIT_SYMBOL, 229), + (6, HUFFMAN_EMIT_SYMBOL, 229), + (10, HUFFMAN_EMIT_SYMBOL, 229), + (15, HUFFMAN_EMIT_SYMBOL, 229), + (24, HUFFMAN_EMIT_SYMBOL, 229), + (31, HUFFMAN_EMIT_SYMBOL, 229), + (41, HUFFMAN_EMIT_SYMBOL, 229), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 229), + # Node 119 + (1, HUFFMAN_EMIT_SYMBOL, 230), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 230), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 129), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 132), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 133), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 134), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 136), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 146), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 154), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 156), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 160), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 163), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 164), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 169), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 170), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 173), + # Node 120 + (2, HUFFMAN_EMIT_SYMBOL, 230), + (9, HUFFMAN_EMIT_SYMBOL, 230), + (23, HUFFMAN_EMIT_SYMBOL, 230), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 230), + (1, HUFFMAN_EMIT_SYMBOL, 129), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 129), + (1, HUFFMAN_EMIT_SYMBOL, 132), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 132), + (1, HUFFMAN_EMIT_SYMBOL, 133), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 133), + (1, HUFFMAN_EMIT_SYMBOL, 134), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 134), + (1, HUFFMAN_EMIT_SYMBOL, 136), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 136), + (1, HUFFMAN_EMIT_SYMBOL, 146), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 146), + # Node 121 + (3, HUFFMAN_EMIT_SYMBOL, 230), + (6, HUFFMAN_EMIT_SYMBOL, 230), + (10, HUFFMAN_EMIT_SYMBOL, 230), + (15, HUFFMAN_EMIT_SYMBOL, 230), + (24, HUFFMAN_EMIT_SYMBOL, 230), + (31, HUFFMAN_EMIT_SYMBOL, 230), + (41, HUFFMAN_EMIT_SYMBOL, 230), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 230), + (2, HUFFMAN_EMIT_SYMBOL, 129), + (9, HUFFMAN_EMIT_SYMBOL, 129), + (23, HUFFMAN_EMIT_SYMBOL, 129), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 129), + (2, HUFFMAN_EMIT_SYMBOL, 132), + (9, HUFFMAN_EMIT_SYMBOL, 132), + (23, HUFFMAN_EMIT_SYMBOL, 132), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 132), + # Node 122 + (3, HUFFMAN_EMIT_SYMBOL, 129), + (6, HUFFMAN_EMIT_SYMBOL, 129), + (10, HUFFMAN_EMIT_SYMBOL, 129), + (15, HUFFMAN_EMIT_SYMBOL, 129), + (24, HUFFMAN_EMIT_SYMBOL, 129), + (31, HUFFMAN_EMIT_SYMBOL, 129), + (41, HUFFMAN_EMIT_SYMBOL, 129), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 129), + (3, HUFFMAN_EMIT_SYMBOL, 132), + (6, HUFFMAN_EMIT_SYMBOL, 132), + (10, HUFFMAN_EMIT_SYMBOL, 132), + (15, HUFFMAN_EMIT_SYMBOL, 132), + (24, HUFFMAN_EMIT_SYMBOL, 132), + (31, HUFFMAN_EMIT_SYMBOL, 132), + (41, HUFFMAN_EMIT_SYMBOL, 132), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 132), + # Node 123 + (2, HUFFMAN_EMIT_SYMBOL, 133), + (9, HUFFMAN_EMIT_SYMBOL, 133), + (23, HUFFMAN_EMIT_SYMBOL, 133), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 133), + (2, HUFFMAN_EMIT_SYMBOL, 134), + (9, HUFFMAN_EMIT_SYMBOL, 134), + (23, HUFFMAN_EMIT_SYMBOL, 134), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 134), + (2, HUFFMAN_EMIT_SYMBOL, 136), + (9, HUFFMAN_EMIT_SYMBOL, 136), + (23, HUFFMAN_EMIT_SYMBOL, 136), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 136), + (2, HUFFMAN_EMIT_SYMBOL, 146), + (9, HUFFMAN_EMIT_SYMBOL, 146), + (23, HUFFMAN_EMIT_SYMBOL, 146), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 146), + # Node 124 + (3, HUFFMAN_EMIT_SYMBOL, 133), + (6, HUFFMAN_EMIT_SYMBOL, 133), + (10, HUFFMAN_EMIT_SYMBOL, 133), + (15, HUFFMAN_EMIT_SYMBOL, 133), + (24, HUFFMAN_EMIT_SYMBOL, 133), + (31, HUFFMAN_EMIT_SYMBOL, 133), + (41, HUFFMAN_EMIT_SYMBOL, 133), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 133), + (3, HUFFMAN_EMIT_SYMBOL, 134), + (6, HUFFMAN_EMIT_SYMBOL, 134), + (10, HUFFMAN_EMIT_SYMBOL, 134), + (15, HUFFMAN_EMIT_SYMBOL, 134), + (24, HUFFMAN_EMIT_SYMBOL, 134), + (31, HUFFMAN_EMIT_SYMBOL, 134), + (41, HUFFMAN_EMIT_SYMBOL, 134), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 134), + # Node 125 + (3, HUFFMAN_EMIT_SYMBOL, 136), + (6, HUFFMAN_EMIT_SYMBOL, 136), + (10, HUFFMAN_EMIT_SYMBOL, 136), + (15, HUFFMAN_EMIT_SYMBOL, 136), + (24, HUFFMAN_EMIT_SYMBOL, 136), + (31, HUFFMAN_EMIT_SYMBOL, 136), + (41, HUFFMAN_EMIT_SYMBOL, 136), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 136), + (3, HUFFMAN_EMIT_SYMBOL, 146), + (6, HUFFMAN_EMIT_SYMBOL, 146), + (10, HUFFMAN_EMIT_SYMBOL, 146), + (15, HUFFMAN_EMIT_SYMBOL, 146), + (24, HUFFMAN_EMIT_SYMBOL, 146), + (31, HUFFMAN_EMIT_SYMBOL, 146), + (41, HUFFMAN_EMIT_SYMBOL, 146), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 146), + # Node 126 + (1, HUFFMAN_EMIT_SYMBOL, 154), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 154), + (1, HUFFMAN_EMIT_SYMBOL, 156), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 156), + (1, HUFFMAN_EMIT_SYMBOL, 160), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 160), + (1, HUFFMAN_EMIT_SYMBOL, 163), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 163), + (1, HUFFMAN_EMIT_SYMBOL, 164), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 164), + (1, HUFFMAN_EMIT_SYMBOL, 169), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 169), + (1, HUFFMAN_EMIT_SYMBOL, 170), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 170), + (1, HUFFMAN_EMIT_SYMBOL, 173), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 173), + # Node 127 + (2, HUFFMAN_EMIT_SYMBOL, 154), + (9, HUFFMAN_EMIT_SYMBOL, 154), + (23, HUFFMAN_EMIT_SYMBOL, 154), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 154), + (2, HUFFMAN_EMIT_SYMBOL, 156), + (9, HUFFMAN_EMIT_SYMBOL, 156), + (23, HUFFMAN_EMIT_SYMBOL, 156), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 156), + (2, HUFFMAN_EMIT_SYMBOL, 160), + (9, HUFFMAN_EMIT_SYMBOL, 160), + (23, HUFFMAN_EMIT_SYMBOL, 160), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 160), + (2, HUFFMAN_EMIT_SYMBOL, 163), + (9, HUFFMAN_EMIT_SYMBOL, 163), + (23, HUFFMAN_EMIT_SYMBOL, 163), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 163), + # Node 128 + (3, HUFFMAN_EMIT_SYMBOL, 154), + (6, HUFFMAN_EMIT_SYMBOL, 154), + (10, HUFFMAN_EMIT_SYMBOL, 154), + (15, HUFFMAN_EMIT_SYMBOL, 154), + (24, HUFFMAN_EMIT_SYMBOL, 154), + (31, HUFFMAN_EMIT_SYMBOL, 154), + (41, HUFFMAN_EMIT_SYMBOL, 154), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 154), + (3, HUFFMAN_EMIT_SYMBOL, 156), + (6, HUFFMAN_EMIT_SYMBOL, 156), + (10, HUFFMAN_EMIT_SYMBOL, 156), + (15, HUFFMAN_EMIT_SYMBOL, 156), + (24, HUFFMAN_EMIT_SYMBOL, 156), + (31, HUFFMAN_EMIT_SYMBOL, 156), + (41, HUFFMAN_EMIT_SYMBOL, 156), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 156), + # Node 129 + (3, HUFFMAN_EMIT_SYMBOL, 160), + (6, HUFFMAN_EMIT_SYMBOL, 160), + (10, HUFFMAN_EMIT_SYMBOL, 160), + (15, HUFFMAN_EMIT_SYMBOL, 160), + (24, HUFFMAN_EMIT_SYMBOL, 160), + (31, HUFFMAN_EMIT_SYMBOL, 160), + (41, HUFFMAN_EMIT_SYMBOL, 160), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 160), + (3, HUFFMAN_EMIT_SYMBOL, 163), + (6, HUFFMAN_EMIT_SYMBOL, 163), + (10, HUFFMAN_EMIT_SYMBOL, 163), + (15, HUFFMAN_EMIT_SYMBOL, 163), + (24, HUFFMAN_EMIT_SYMBOL, 163), + (31, HUFFMAN_EMIT_SYMBOL, 163), + (41, HUFFMAN_EMIT_SYMBOL, 163), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 163), + # Node 130 + (2, HUFFMAN_EMIT_SYMBOL, 164), + (9, HUFFMAN_EMIT_SYMBOL, 164), + (23, HUFFMAN_EMIT_SYMBOL, 164), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 164), + (2, HUFFMAN_EMIT_SYMBOL, 169), + (9, HUFFMAN_EMIT_SYMBOL, 169), + (23, HUFFMAN_EMIT_SYMBOL, 169), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 169), + (2, HUFFMAN_EMIT_SYMBOL, 170), + (9, HUFFMAN_EMIT_SYMBOL, 170), + (23, HUFFMAN_EMIT_SYMBOL, 170), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 170), + (2, HUFFMAN_EMIT_SYMBOL, 173), + (9, HUFFMAN_EMIT_SYMBOL, 173), + (23, HUFFMAN_EMIT_SYMBOL, 173), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 173), + # Node 131 + (3, HUFFMAN_EMIT_SYMBOL, 164), + (6, HUFFMAN_EMIT_SYMBOL, 164), + (10, HUFFMAN_EMIT_SYMBOL, 164), + (15, HUFFMAN_EMIT_SYMBOL, 164), + (24, HUFFMAN_EMIT_SYMBOL, 164), + (31, HUFFMAN_EMIT_SYMBOL, 164), + (41, HUFFMAN_EMIT_SYMBOL, 164), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 164), + (3, HUFFMAN_EMIT_SYMBOL, 169), + (6, HUFFMAN_EMIT_SYMBOL, 169), + (10, HUFFMAN_EMIT_SYMBOL, 169), + (15, HUFFMAN_EMIT_SYMBOL, 169), + (24, HUFFMAN_EMIT_SYMBOL, 169), + (31, HUFFMAN_EMIT_SYMBOL, 169), + (41, HUFFMAN_EMIT_SYMBOL, 169), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 169), + # Node 132 + (3, HUFFMAN_EMIT_SYMBOL, 170), + (6, HUFFMAN_EMIT_SYMBOL, 170), + (10, HUFFMAN_EMIT_SYMBOL, 170), + (15, HUFFMAN_EMIT_SYMBOL, 170), + (24, HUFFMAN_EMIT_SYMBOL, 170), + (31, HUFFMAN_EMIT_SYMBOL, 170), + (41, HUFFMAN_EMIT_SYMBOL, 170), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 170), + (3, HUFFMAN_EMIT_SYMBOL, 173), + (6, HUFFMAN_EMIT_SYMBOL, 173), + (10, HUFFMAN_EMIT_SYMBOL, 173), + (15, HUFFMAN_EMIT_SYMBOL, 173), + (24, HUFFMAN_EMIT_SYMBOL, 173), + (31, HUFFMAN_EMIT_SYMBOL, 173), + (41, HUFFMAN_EMIT_SYMBOL, 173), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 173), + # Node 133 + (137, 0, 0), + (138, 0, 0), + (140, 0, 0), + (141, 0, 0), + (144, 0, 0), + (145, 0, 0), + (147, 0, 0), + (150, 0, 0), + (156, 0, 0), + (159, 0, 0), + (163, 0, 0), + (166, 0, 0), + (171, 0, 0), + (174, 0, 0), + (181, 0, 0), + (190, 0, 0), + # Node 134 + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 178), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 181), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 185), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 186), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 187), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 189), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 190), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 196), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 198), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 228), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 232), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 233), + (148, 0, 0), + (149, 0, 0), + (151, 0, 0), + (152, 0, 0), + # Node 135 + (1, HUFFMAN_EMIT_SYMBOL, 178), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 178), + (1, HUFFMAN_EMIT_SYMBOL, 181), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 181), + (1, HUFFMAN_EMIT_SYMBOL, 185), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 185), + (1, HUFFMAN_EMIT_SYMBOL, 186), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 186), + (1, HUFFMAN_EMIT_SYMBOL, 187), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 187), + (1, HUFFMAN_EMIT_SYMBOL, 189), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 189), + (1, HUFFMAN_EMIT_SYMBOL, 190), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 190), + (1, HUFFMAN_EMIT_SYMBOL, 196), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 196), + # Node 136 + (2, HUFFMAN_EMIT_SYMBOL, 178), + (9, HUFFMAN_EMIT_SYMBOL, 178), + (23, HUFFMAN_EMIT_SYMBOL, 178), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 178), + (2, HUFFMAN_EMIT_SYMBOL, 181), + (9, HUFFMAN_EMIT_SYMBOL, 181), + (23, HUFFMAN_EMIT_SYMBOL, 181), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 181), + (2, HUFFMAN_EMIT_SYMBOL, 185), + (9, HUFFMAN_EMIT_SYMBOL, 185), + (23, HUFFMAN_EMIT_SYMBOL, 185), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 185), + (2, HUFFMAN_EMIT_SYMBOL, 186), + (9, HUFFMAN_EMIT_SYMBOL, 186), + (23, HUFFMAN_EMIT_SYMBOL, 186), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 186), + # Node 137 + (3, HUFFMAN_EMIT_SYMBOL, 178), + (6, HUFFMAN_EMIT_SYMBOL, 178), + (10, HUFFMAN_EMIT_SYMBOL, 178), + (15, HUFFMAN_EMIT_SYMBOL, 178), + (24, HUFFMAN_EMIT_SYMBOL, 178), + (31, HUFFMAN_EMIT_SYMBOL, 178), + (41, HUFFMAN_EMIT_SYMBOL, 178), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 178), + (3, HUFFMAN_EMIT_SYMBOL, 181), + (6, HUFFMAN_EMIT_SYMBOL, 181), + (10, HUFFMAN_EMIT_SYMBOL, 181), + (15, HUFFMAN_EMIT_SYMBOL, 181), + (24, HUFFMAN_EMIT_SYMBOL, 181), + (31, HUFFMAN_EMIT_SYMBOL, 181), + (41, HUFFMAN_EMIT_SYMBOL, 181), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 181), + # Node 138 + (3, HUFFMAN_EMIT_SYMBOL, 185), + (6, HUFFMAN_EMIT_SYMBOL, 185), + (10, HUFFMAN_EMIT_SYMBOL, 185), + (15, HUFFMAN_EMIT_SYMBOL, 185), + (24, HUFFMAN_EMIT_SYMBOL, 185), + (31, HUFFMAN_EMIT_SYMBOL, 185), + (41, HUFFMAN_EMIT_SYMBOL, 185), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 185), + (3, HUFFMAN_EMIT_SYMBOL, 186), + (6, HUFFMAN_EMIT_SYMBOL, 186), + (10, HUFFMAN_EMIT_SYMBOL, 186), + (15, HUFFMAN_EMIT_SYMBOL, 186), + (24, HUFFMAN_EMIT_SYMBOL, 186), + (31, HUFFMAN_EMIT_SYMBOL, 186), + (41, HUFFMAN_EMIT_SYMBOL, 186), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 186), + # Node 139 + (2, HUFFMAN_EMIT_SYMBOL, 187), + (9, HUFFMAN_EMIT_SYMBOL, 187), + (23, HUFFMAN_EMIT_SYMBOL, 187), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 187), + (2, HUFFMAN_EMIT_SYMBOL, 189), + (9, HUFFMAN_EMIT_SYMBOL, 189), + (23, HUFFMAN_EMIT_SYMBOL, 189), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 189), + (2, HUFFMAN_EMIT_SYMBOL, 190), + (9, HUFFMAN_EMIT_SYMBOL, 190), + (23, HUFFMAN_EMIT_SYMBOL, 190), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 190), + (2, HUFFMAN_EMIT_SYMBOL, 196), + (9, HUFFMAN_EMIT_SYMBOL, 196), + (23, HUFFMAN_EMIT_SYMBOL, 196), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 196), + # Node 140 + (3, HUFFMAN_EMIT_SYMBOL, 187), + (6, HUFFMAN_EMIT_SYMBOL, 187), + (10, HUFFMAN_EMIT_SYMBOL, 187), + (15, HUFFMAN_EMIT_SYMBOL, 187), + (24, HUFFMAN_EMIT_SYMBOL, 187), + (31, HUFFMAN_EMIT_SYMBOL, 187), + (41, HUFFMAN_EMIT_SYMBOL, 187), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 187), + (3, HUFFMAN_EMIT_SYMBOL, 189), + (6, HUFFMAN_EMIT_SYMBOL, 189), + (10, HUFFMAN_EMIT_SYMBOL, 189), + (15, HUFFMAN_EMIT_SYMBOL, 189), + (24, HUFFMAN_EMIT_SYMBOL, 189), + (31, HUFFMAN_EMIT_SYMBOL, 189), + (41, HUFFMAN_EMIT_SYMBOL, 189), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 189), + # Node 141 + (3, HUFFMAN_EMIT_SYMBOL, 190), + (6, HUFFMAN_EMIT_SYMBOL, 190), + (10, HUFFMAN_EMIT_SYMBOL, 190), + (15, HUFFMAN_EMIT_SYMBOL, 190), + (24, HUFFMAN_EMIT_SYMBOL, 190), + (31, HUFFMAN_EMIT_SYMBOL, 190), + (41, HUFFMAN_EMIT_SYMBOL, 190), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 190), + (3, HUFFMAN_EMIT_SYMBOL, 196), + (6, HUFFMAN_EMIT_SYMBOL, 196), + (10, HUFFMAN_EMIT_SYMBOL, 196), + (15, HUFFMAN_EMIT_SYMBOL, 196), + (24, HUFFMAN_EMIT_SYMBOL, 196), + (31, HUFFMAN_EMIT_SYMBOL, 196), + (41, HUFFMAN_EMIT_SYMBOL, 196), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 196), + # Node 142 + (1, HUFFMAN_EMIT_SYMBOL, 198), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 198), + (1, HUFFMAN_EMIT_SYMBOL, 228), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 228), + (1, HUFFMAN_EMIT_SYMBOL, 232), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 232), + (1, HUFFMAN_EMIT_SYMBOL, 233), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 233), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 1), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 135), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 137), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 138), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 139), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 140), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 141), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 143), + # Node 143 + (2, HUFFMAN_EMIT_SYMBOL, 198), + (9, HUFFMAN_EMIT_SYMBOL, 198), + (23, HUFFMAN_EMIT_SYMBOL, 198), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 198), + (2, HUFFMAN_EMIT_SYMBOL, 228), + (9, HUFFMAN_EMIT_SYMBOL, 228), + (23, HUFFMAN_EMIT_SYMBOL, 228), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 228), + (2, HUFFMAN_EMIT_SYMBOL, 232), + (9, HUFFMAN_EMIT_SYMBOL, 232), + (23, HUFFMAN_EMIT_SYMBOL, 232), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 232), + (2, HUFFMAN_EMIT_SYMBOL, 233), + (9, HUFFMAN_EMIT_SYMBOL, 233), + (23, HUFFMAN_EMIT_SYMBOL, 233), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 233), + # Node 144 + (3, HUFFMAN_EMIT_SYMBOL, 198), + (6, HUFFMAN_EMIT_SYMBOL, 198), + (10, HUFFMAN_EMIT_SYMBOL, 198), + (15, HUFFMAN_EMIT_SYMBOL, 198), + (24, HUFFMAN_EMIT_SYMBOL, 198), + (31, HUFFMAN_EMIT_SYMBOL, 198), + (41, HUFFMAN_EMIT_SYMBOL, 198), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 198), + (3, HUFFMAN_EMIT_SYMBOL, 228), + (6, HUFFMAN_EMIT_SYMBOL, 228), + (10, HUFFMAN_EMIT_SYMBOL, 228), + (15, HUFFMAN_EMIT_SYMBOL, 228), + (24, HUFFMAN_EMIT_SYMBOL, 228), + (31, HUFFMAN_EMIT_SYMBOL, 228), + (41, HUFFMAN_EMIT_SYMBOL, 228), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 228), + # Node 145 + (3, HUFFMAN_EMIT_SYMBOL, 232), + (6, HUFFMAN_EMIT_SYMBOL, 232), + (10, HUFFMAN_EMIT_SYMBOL, 232), + (15, HUFFMAN_EMIT_SYMBOL, 232), + (24, HUFFMAN_EMIT_SYMBOL, 232), + (31, HUFFMAN_EMIT_SYMBOL, 232), + (41, HUFFMAN_EMIT_SYMBOL, 232), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 232), + (3, HUFFMAN_EMIT_SYMBOL, 233), + (6, HUFFMAN_EMIT_SYMBOL, 233), + (10, HUFFMAN_EMIT_SYMBOL, 233), + (15, HUFFMAN_EMIT_SYMBOL, 233), + (24, HUFFMAN_EMIT_SYMBOL, 233), + (31, HUFFMAN_EMIT_SYMBOL, 233), + (41, HUFFMAN_EMIT_SYMBOL, 233), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 233), + # Node 146 + (1, HUFFMAN_EMIT_SYMBOL, 1), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 1), + (1, HUFFMAN_EMIT_SYMBOL, 135), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 135), + (1, HUFFMAN_EMIT_SYMBOL, 137), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 137), + (1, HUFFMAN_EMIT_SYMBOL, 138), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 138), + (1, HUFFMAN_EMIT_SYMBOL, 139), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 139), + (1, HUFFMAN_EMIT_SYMBOL, 140), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 140), + (1, HUFFMAN_EMIT_SYMBOL, 141), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 141), + (1, HUFFMAN_EMIT_SYMBOL, 143), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 143), + # Node 147 + (2, HUFFMAN_EMIT_SYMBOL, 1), + (9, HUFFMAN_EMIT_SYMBOL, 1), + (23, HUFFMAN_EMIT_SYMBOL, 1), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 1), + (2, HUFFMAN_EMIT_SYMBOL, 135), + (9, HUFFMAN_EMIT_SYMBOL, 135), + (23, HUFFMAN_EMIT_SYMBOL, 135), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 135), + (2, HUFFMAN_EMIT_SYMBOL, 137), + (9, HUFFMAN_EMIT_SYMBOL, 137), + (23, HUFFMAN_EMIT_SYMBOL, 137), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 137), + (2, HUFFMAN_EMIT_SYMBOL, 138), + (9, HUFFMAN_EMIT_SYMBOL, 138), + (23, HUFFMAN_EMIT_SYMBOL, 138), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 138), + # Node 148 + (3, HUFFMAN_EMIT_SYMBOL, 1), + (6, HUFFMAN_EMIT_SYMBOL, 1), + (10, HUFFMAN_EMIT_SYMBOL, 1), + (15, HUFFMAN_EMIT_SYMBOL, 1), + (24, HUFFMAN_EMIT_SYMBOL, 1), + (31, HUFFMAN_EMIT_SYMBOL, 1), + (41, HUFFMAN_EMIT_SYMBOL, 1), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 1), + (3, HUFFMAN_EMIT_SYMBOL, 135), + (6, HUFFMAN_EMIT_SYMBOL, 135), + (10, HUFFMAN_EMIT_SYMBOL, 135), + (15, HUFFMAN_EMIT_SYMBOL, 135), + (24, HUFFMAN_EMIT_SYMBOL, 135), + (31, HUFFMAN_EMIT_SYMBOL, 135), + (41, HUFFMAN_EMIT_SYMBOL, 135), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 135), + # Node 149 + (3, HUFFMAN_EMIT_SYMBOL, 137), + (6, HUFFMAN_EMIT_SYMBOL, 137), + (10, HUFFMAN_EMIT_SYMBOL, 137), + (15, HUFFMAN_EMIT_SYMBOL, 137), + (24, HUFFMAN_EMIT_SYMBOL, 137), + (31, HUFFMAN_EMIT_SYMBOL, 137), + (41, HUFFMAN_EMIT_SYMBOL, 137), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 137), + (3, HUFFMAN_EMIT_SYMBOL, 138), + (6, HUFFMAN_EMIT_SYMBOL, 138), + (10, HUFFMAN_EMIT_SYMBOL, 138), + (15, HUFFMAN_EMIT_SYMBOL, 138), + (24, HUFFMAN_EMIT_SYMBOL, 138), + (31, HUFFMAN_EMIT_SYMBOL, 138), + (41, HUFFMAN_EMIT_SYMBOL, 138), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 138), + # Node 150 + (2, HUFFMAN_EMIT_SYMBOL, 139), + (9, HUFFMAN_EMIT_SYMBOL, 139), + (23, HUFFMAN_EMIT_SYMBOL, 139), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 139), + (2, HUFFMAN_EMIT_SYMBOL, 140), + (9, HUFFMAN_EMIT_SYMBOL, 140), + (23, HUFFMAN_EMIT_SYMBOL, 140), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 140), + (2, HUFFMAN_EMIT_SYMBOL, 141), + (9, HUFFMAN_EMIT_SYMBOL, 141), + (23, HUFFMAN_EMIT_SYMBOL, 141), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 141), + (2, HUFFMAN_EMIT_SYMBOL, 143), + (9, HUFFMAN_EMIT_SYMBOL, 143), + (23, HUFFMAN_EMIT_SYMBOL, 143), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 143), + # Node 151 + (3, HUFFMAN_EMIT_SYMBOL, 139), + (6, HUFFMAN_EMIT_SYMBOL, 139), + (10, HUFFMAN_EMIT_SYMBOL, 139), + (15, HUFFMAN_EMIT_SYMBOL, 139), + (24, HUFFMAN_EMIT_SYMBOL, 139), + (31, HUFFMAN_EMIT_SYMBOL, 139), + (41, HUFFMAN_EMIT_SYMBOL, 139), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 139), + (3, HUFFMAN_EMIT_SYMBOL, 140), + (6, HUFFMAN_EMIT_SYMBOL, 140), + (10, HUFFMAN_EMIT_SYMBOL, 140), + (15, HUFFMAN_EMIT_SYMBOL, 140), + (24, HUFFMAN_EMIT_SYMBOL, 140), + (31, HUFFMAN_EMIT_SYMBOL, 140), + (41, HUFFMAN_EMIT_SYMBOL, 140), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 140), + # Node 152 + (3, HUFFMAN_EMIT_SYMBOL, 141), + (6, HUFFMAN_EMIT_SYMBOL, 141), + (10, HUFFMAN_EMIT_SYMBOL, 141), + (15, HUFFMAN_EMIT_SYMBOL, 141), + (24, HUFFMAN_EMIT_SYMBOL, 141), + (31, HUFFMAN_EMIT_SYMBOL, 141), + (41, HUFFMAN_EMIT_SYMBOL, 141), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 141), + (3, HUFFMAN_EMIT_SYMBOL, 143), + (6, HUFFMAN_EMIT_SYMBOL, 143), + (10, HUFFMAN_EMIT_SYMBOL, 143), + (15, HUFFMAN_EMIT_SYMBOL, 143), + (24, HUFFMAN_EMIT_SYMBOL, 143), + (31, HUFFMAN_EMIT_SYMBOL, 143), + (41, HUFFMAN_EMIT_SYMBOL, 143), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 143), + # Node 153 + (157, 0, 0), + (158, 0, 0), + (160, 0, 0), + (161, 0, 0), + (164, 0, 0), + (165, 0, 0), + (167, 0, 0), + (168, 0, 0), + (172, 0, 0), + (173, 0, 0), + (175, 0, 0), + (177, 0, 0), + (182, 0, 0), + (185, 0, 0), + (191, 0, 0), + (207, 0, 0), + # Node 154 + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 147), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 149), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 150), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 151), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 152), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 155), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 157), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 158), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 165), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 166), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 168), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 174), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 175), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 180), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 182), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 183), + # Node 155 + (1, HUFFMAN_EMIT_SYMBOL, 147), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 147), + (1, HUFFMAN_EMIT_SYMBOL, 149), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 149), + (1, HUFFMAN_EMIT_SYMBOL, 150), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 150), + (1, HUFFMAN_EMIT_SYMBOL, 151), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 151), + (1, HUFFMAN_EMIT_SYMBOL, 152), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 152), + (1, HUFFMAN_EMIT_SYMBOL, 155), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 155), + (1, HUFFMAN_EMIT_SYMBOL, 157), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 157), + (1, HUFFMAN_EMIT_SYMBOL, 158), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 158), + # Node 156 + (2, HUFFMAN_EMIT_SYMBOL, 147), + (9, HUFFMAN_EMIT_SYMBOL, 147), + (23, HUFFMAN_EMIT_SYMBOL, 147), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 147), + (2, HUFFMAN_EMIT_SYMBOL, 149), + (9, HUFFMAN_EMIT_SYMBOL, 149), + (23, HUFFMAN_EMIT_SYMBOL, 149), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 149), + (2, HUFFMAN_EMIT_SYMBOL, 150), + (9, HUFFMAN_EMIT_SYMBOL, 150), + (23, HUFFMAN_EMIT_SYMBOL, 150), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 150), + (2, HUFFMAN_EMIT_SYMBOL, 151), + (9, HUFFMAN_EMIT_SYMBOL, 151), + (23, HUFFMAN_EMIT_SYMBOL, 151), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 151), + # Node 157 + (3, HUFFMAN_EMIT_SYMBOL, 147), + (6, HUFFMAN_EMIT_SYMBOL, 147), + (10, HUFFMAN_EMIT_SYMBOL, 147), + (15, HUFFMAN_EMIT_SYMBOL, 147), + (24, HUFFMAN_EMIT_SYMBOL, 147), + (31, HUFFMAN_EMIT_SYMBOL, 147), + (41, HUFFMAN_EMIT_SYMBOL, 147), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 147), + (3, HUFFMAN_EMIT_SYMBOL, 149), + (6, HUFFMAN_EMIT_SYMBOL, 149), + (10, HUFFMAN_EMIT_SYMBOL, 149), + (15, HUFFMAN_EMIT_SYMBOL, 149), + (24, HUFFMAN_EMIT_SYMBOL, 149), + (31, HUFFMAN_EMIT_SYMBOL, 149), + (41, HUFFMAN_EMIT_SYMBOL, 149), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 149), + # Node 158 + (3, HUFFMAN_EMIT_SYMBOL, 150), + (6, HUFFMAN_EMIT_SYMBOL, 150), + (10, HUFFMAN_EMIT_SYMBOL, 150), + (15, HUFFMAN_EMIT_SYMBOL, 150), + (24, HUFFMAN_EMIT_SYMBOL, 150), + (31, HUFFMAN_EMIT_SYMBOL, 150), + (41, HUFFMAN_EMIT_SYMBOL, 150), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 150), + (3, HUFFMAN_EMIT_SYMBOL, 151), + (6, HUFFMAN_EMIT_SYMBOL, 151), + (10, HUFFMAN_EMIT_SYMBOL, 151), + (15, HUFFMAN_EMIT_SYMBOL, 151), + (24, HUFFMAN_EMIT_SYMBOL, 151), + (31, HUFFMAN_EMIT_SYMBOL, 151), + (41, HUFFMAN_EMIT_SYMBOL, 151), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 151), + # Node 159 + (2, HUFFMAN_EMIT_SYMBOL, 152), + (9, HUFFMAN_EMIT_SYMBOL, 152), + (23, HUFFMAN_EMIT_SYMBOL, 152), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 152), + (2, HUFFMAN_EMIT_SYMBOL, 155), + (9, HUFFMAN_EMIT_SYMBOL, 155), + (23, HUFFMAN_EMIT_SYMBOL, 155), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 155), + (2, HUFFMAN_EMIT_SYMBOL, 157), + (9, HUFFMAN_EMIT_SYMBOL, 157), + (23, HUFFMAN_EMIT_SYMBOL, 157), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 157), + (2, HUFFMAN_EMIT_SYMBOL, 158), + (9, HUFFMAN_EMIT_SYMBOL, 158), + (23, HUFFMAN_EMIT_SYMBOL, 158), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 158), + # Node 160 + (3, HUFFMAN_EMIT_SYMBOL, 152), + (6, HUFFMAN_EMIT_SYMBOL, 152), + (10, HUFFMAN_EMIT_SYMBOL, 152), + (15, HUFFMAN_EMIT_SYMBOL, 152), + (24, HUFFMAN_EMIT_SYMBOL, 152), + (31, HUFFMAN_EMIT_SYMBOL, 152), + (41, HUFFMAN_EMIT_SYMBOL, 152), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 152), + (3, HUFFMAN_EMIT_SYMBOL, 155), + (6, HUFFMAN_EMIT_SYMBOL, 155), + (10, HUFFMAN_EMIT_SYMBOL, 155), + (15, HUFFMAN_EMIT_SYMBOL, 155), + (24, HUFFMAN_EMIT_SYMBOL, 155), + (31, HUFFMAN_EMIT_SYMBOL, 155), + (41, HUFFMAN_EMIT_SYMBOL, 155), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 155), + # Node 161 + (3, HUFFMAN_EMIT_SYMBOL, 157), + (6, HUFFMAN_EMIT_SYMBOL, 157), + (10, HUFFMAN_EMIT_SYMBOL, 157), + (15, HUFFMAN_EMIT_SYMBOL, 157), + (24, HUFFMAN_EMIT_SYMBOL, 157), + (31, HUFFMAN_EMIT_SYMBOL, 157), + (41, HUFFMAN_EMIT_SYMBOL, 157), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 157), + (3, HUFFMAN_EMIT_SYMBOL, 158), + (6, HUFFMAN_EMIT_SYMBOL, 158), + (10, HUFFMAN_EMIT_SYMBOL, 158), + (15, HUFFMAN_EMIT_SYMBOL, 158), + (24, HUFFMAN_EMIT_SYMBOL, 158), + (31, HUFFMAN_EMIT_SYMBOL, 158), + (41, HUFFMAN_EMIT_SYMBOL, 158), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 158), + # Node 162 + (1, HUFFMAN_EMIT_SYMBOL, 165), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 165), + (1, HUFFMAN_EMIT_SYMBOL, 166), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 166), + (1, HUFFMAN_EMIT_SYMBOL, 168), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 168), + (1, HUFFMAN_EMIT_SYMBOL, 174), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 174), + (1, HUFFMAN_EMIT_SYMBOL, 175), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 175), + (1, HUFFMAN_EMIT_SYMBOL, 180), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 180), + (1, HUFFMAN_EMIT_SYMBOL, 182), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 182), + (1, HUFFMAN_EMIT_SYMBOL, 183), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 183), + # Node 163 + (2, HUFFMAN_EMIT_SYMBOL, 165), + (9, HUFFMAN_EMIT_SYMBOL, 165), + (23, HUFFMAN_EMIT_SYMBOL, 165), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 165), + (2, HUFFMAN_EMIT_SYMBOL, 166), + (9, HUFFMAN_EMIT_SYMBOL, 166), + (23, HUFFMAN_EMIT_SYMBOL, 166), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 166), + (2, HUFFMAN_EMIT_SYMBOL, 168), + (9, HUFFMAN_EMIT_SYMBOL, 168), + (23, HUFFMAN_EMIT_SYMBOL, 168), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 168), + (2, HUFFMAN_EMIT_SYMBOL, 174), + (9, HUFFMAN_EMIT_SYMBOL, 174), + (23, HUFFMAN_EMIT_SYMBOL, 174), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 174), + # Node 164 + (3, HUFFMAN_EMIT_SYMBOL, 165), + (6, HUFFMAN_EMIT_SYMBOL, 165), + (10, HUFFMAN_EMIT_SYMBOL, 165), + (15, HUFFMAN_EMIT_SYMBOL, 165), + (24, HUFFMAN_EMIT_SYMBOL, 165), + (31, HUFFMAN_EMIT_SYMBOL, 165), + (41, HUFFMAN_EMIT_SYMBOL, 165), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 165), + (3, HUFFMAN_EMIT_SYMBOL, 166), + (6, HUFFMAN_EMIT_SYMBOL, 166), + (10, HUFFMAN_EMIT_SYMBOL, 166), + (15, HUFFMAN_EMIT_SYMBOL, 166), + (24, HUFFMAN_EMIT_SYMBOL, 166), + (31, HUFFMAN_EMIT_SYMBOL, 166), + (41, HUFFMAN_EMIT_SYMBOL, 166), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 166), + # Node 165 + (3, HUFFMAN_EMIT_SYMBOL, 168), + (6, HUFFMAN_EMIT_SYMBOL, 168), + (10, HUFFMAN_EMIT_SYMBOL, 168), + (15, HUFFMAN_EMIT_SYMBOL, 168), + (24, HUFFMAN_EMIT_SYMBOL, 168), + (31, HUFFMAN_EMIT_SYMBOL, 168), + (41, HUFFMAN_EMIT_SYMBOL, 168), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 168), + (3, HUFFMAN_EMIT_SYMBOL, 174), + (6, HUFFMAN_EMIT_SYMBOL, 174), + (10, HUFFMAN_EMIT_SYMBOL, 174), + (15, HUFFMAN_EMIT_SYMBOL, 174), + (24, HUFFMAN_EMIT_SYMBOL, 174), + (31, HUFFMAN_EMIT_SYMBOL, 174), + (41, HUFFMAN_EMIT_SYMBOL, 174), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 174), + # Node 166 + (2, HUFFMAN_EMIT_SYMBOL, 175), + (9, HUFFMAN_EMIT_SYMBOL, 175), + (23, HUFFMAN_EMIT_SYMBOL, 175), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 175), + (2, HUFFMAN_EMIT_SYMBOL, 180), + (9, HUFFMAN_EMIT_SYMBOL, 180), + (23, HUFFMAN_EMIT_SYMBOL, 180), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 180), + (2, HUFFMAN_EMIT_SYMBOL, 182), + (9, HUFFMAN_EMIT_SYMBOL, 182), + (23, HUFFMAN_EMIT_SYMBOL, 182), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 182), + (2, HUFFMAN_EMIT_SYMBOL, 183), + (9, HUFFMAN_EMIT_SYMBOL, 183), + (23, HUFFMAN_EMIT_SYMBOL, 183), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 183), + # Node 167 + (3, HUFFMAN_EMIT_SYMBOL, 175), + (6, HUFFMAN_EMIT_SYMBOL, 175), + (10, HUFFMAN_EMIT_SYMBOL, 175), + (15, HUFFMAN_EMIT_SYMBOL, 175), + (24, HUFFMAN_EMIT_SYMBOL, 175), + (31, HUFFMAN_EMIT_SYMBOL, 175), + (41, HUFFMAN_EMIT_SYMBOL, 175), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 175), + (3, HUFFMAN_EMIT_SYMBOL, 180), + (6, HUFFMAN_EMIT_SYMBOL, 180), + (10, HUFFMAN_EMIT_SYMBOL, 180), + (15, HUFFMAN_EMIT_SYMBOL, 180), + (24, HUFFMAN_EMIT_SYMBOL, 180), + (31, HUFFMAN_EMIT_SYMBOL, 180), + (41, HUFFMAN_EMIT_SYMBOL, 180), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 180), + # Node 168 + (3, HUFFMAN_EMIT_SYMBOL, 182), + (6, HUFFMAN_EMIT_SYMBOL, 182), + (10, HUFFMAN_EMIT_SYMBOL, 182), + (15, HUFFMAN_EMIT_SYMBOL, 182), + (24, HUFFMAN_EMIT_SYMBOL, 182), + (31, HUFFMAN_EMIT_SYMBOL, 182), + (41, HUFFMAN_EMIT_SYMBOL, 182), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 182), + (3, HUFFMAN_EMIT_SYMBOL, 183), + (6, HUFFMAN_EMIT_SYMBOL, 183), + (10, HUFFMAN_EMIT_SYMBOL, 183), + (15, HUFFMAN_EMIT_SYMBOL, 183), + (24, HUFFMAN_EMIT_SYMBOL, 183), + (31, HUFFMAN_EMIT_SYMBOL, 183), + (41, HUFFMAN_EMIT_SYMBOL, 183), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 183), + # Node 169 + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 188), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 191), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 197), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 231), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 239), + (176, 0, 0), + (178, 0, 0), + (179, 0, 0), + (183, 0, 0), + (184, 0, 0), + (186, 0, 0), + (187, 0, 0), + (192, 0, 0), + (199, 0, 0), + (208, 0, 0), + (223, 0, 0), + # Node 170 + (1, HUFFMAN_EMIT_SYMBOL, 188), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 188), + (1, HUFFMAN_EMIT_SYMBOL, 191), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 191), + (1, HUFFMAN_EMIT_SYMBOL, 197), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 197), + (1, HUFFMAN_EMIT_SYMBOL, 231), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 231), + (1, HUFFMAN_EMIT_SYMBOL, 239), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 239), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 9), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 142), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 144), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 145), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 148), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 159), + # Node 171 + (2, HUFFMAN_EMIT_SYMBOL, 188), + (9, HUFFMAN_EMIT_SYMBOL, 188), + (23, HUFFMAN_EMIT_SYMBOL, 188), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 188), + (2, HUFFMAN_EMIT_SYMBOL, 191), + (9, HUFFMAN_EMIT_SYMBOL, 191), + (23, HUFFMAN_EMIT_SYMBOL, 191), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 191), + (2, HUFFMAN_EMIT_SYMBOL, 197), + (9, HUFFMAN_EMIT_SYMBOL, 197), + (23, HUFFMAN_EMIT_SYMBOL, 197), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 197), + (2, HUFFMAN_EMIT_SYMBOL, 231), + (9, HUFFMAN_EMIT_SYMBOL, 231), + (23, HUFFMAN_EMIT_SYMBOL, 231), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 231), + # Node 172 + (3, HUFFMAN_EMIT_SYMBOL, 188), + (6, HUFFMAN_EMIT_SYMBOL, 188), + (10, HUFFMAN_EMIT_SYMBOL, 188), + (15, HUFFMAN_EMIT_SYMBOL, 188), + (24, HUFFMAN_EMIT_SYMBOL, 188), + (31, HUFFMAN_EMIT_SYMBOL, 188), + (41, HUFFMAN_EMIT_SYMBOL, 188), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 188), + (3, HUFFMAN_EMIT_SYMBOL, 191), + (6, HUFFMAN_EMIT_SYMBOL, 191), + (10, HUFFMAN_EMIT_SYMBOL, 191), + (15, HUFFMAN_EMIT_SYMBOL, 191), + (24, HUFFMAN_EMIT_SYMBOL, 191), + (31, HUFFMAN_EMIT_SYMBOL, 191), + (41, HUFFMAN_EMIT_SYMBOL, 191), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 191), + # Node 173 + (3, HUFFMAN_EMIT_SYMBOL, 197), + (6, HUFFMAN_EMIT_SYMBOL, 197), + (10, HUFFMAN_EMIT_SYMBOL, 197), + (15, HUFFMAN_EMIT_SYMBOL, 197), + (24, HUFFMAN_EMIT_SYMBOL, 197), + (31, HUFFMAN_EMIT_SYMBOL, 197), + (41, HUFFMAN_EMIT_SYMBOL, 197), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 197), + (3, HUFFMAN_EMIT_SYMBOL, 231), + (6, HUFFMAN_EMIT_SYMBOL, 231), + (10, HUFFMAN_EMIT_SYMBOL, 231), + (15, HUFFMAN_EMIT_SYMBOL, 231), + (24, HUFFMAN_EMIT_SYMBOL, 231), + (31, HUFFMAN_EMIT_SYMBOL, 231), + (41, HUFFMAN_EMIT_SYMBOL, 231), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 231), + # Node 174 + (2, HUFFMAN_EMIT_SYMBOL, 239), + (9, HUFFMAN_EMIT_SYMBOL, 239), + (23, HUFFMAN_EMIT_SYMBOL, 239), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 239), + (1, HUFFMAN_EMIT_SYMBOL, 9), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 9), + (1, HUFFMAN_EMIT_SYMBOL, 142), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 142), + (1, HUFFMAN_EMIT_SYMBOL, 144), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 144), + (1, HUFFMAN_EMIT_SYMBOL, 145), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 145), + (1, HUFFMAN_EMIT_SYMBOL, 148), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 148), + (1, HUFFMAN_EMIT_SYMBOL, 159), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 159), + # Node 175 + (3, HUFFMAN_EMIT_SYMBOL, 239), + (6, HUFFMAN_EMIT_SYMBOL, 239), + (10, HUFFMAN_EMIT_SYMBOL, 239), + (15, HUFFMAN_EMIT_SYMBOL, 239), + (24, HUFFMAN_EMIT_SYMBOL, 239), + (31, HUFFMAN_EMIT_SYMBOL, 239), + (41, HUFFMAN_EMIT_SYMBOL, 239), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 239), + (2, HUFFMAN_EMIT_SYMBOL, 9), + (9, HUFFMAN_EMIT_SYMBOL, 9), + (23, HUFFMAN_EMIT_SYMBOL, 9), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 9), + (2, HUFFMAN_EMIT_SYMBOL, 142), + (9, HUFFMAN_EMIT_SYMBOL, 142), + (23, HUFFMAN_EMIT_SYMBOL, 142), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 142), + # Node 176 + (3, HUFFMAN_EMIT_SYMBOL, 9), + (6, HUFFMAN_EMIT_SYMBOL, 9), + (10, HUFFMAN_EMIT_SYMBOL, 9), + (15, HUFFMAN_EMIT_SYMBOL, 9), + (24, HUFFMAN_EMIT_SYMBOL, 9), + (31, HUFFMAN_EMIT_SYMBOL, 9), + (41, HUFFMAN_EMIT_SYMBOL, 9), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 9), + (3, HUFFMAN_EMIT_SYMBOL, 142), + (6, HUFFMAN_EMIT_SYMBOL, 142), + (10, HUFFMAN_EMIT_SYMBOL, 142), + (15, HUFFMAN_EMIT_SYMBOL, 142), + (24, HUFFMAN_EMIT_SYMBOL, 142), + (31, HUFFMAN_EMIT_SYMBOL, 142), + (41, HUFFMAN_EMIT_SYMBOL, 142), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 142), + # Node 177 + (2, HUFFMAN_EMIT_SYMBOL, 144), + (9, HUFFMAN_EMIT_SYMBOL, 144), + (23, HUFFMAN_EMIT_SYMBOL, 144), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 144), + (2, HUFFMAN_EMIT_SYMBOL, 145), + (9, HUFFMAN_EMIT_SYMBOL, 145), + (23, HUFFMAN_EMIT_SYMBOL, 145), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 145), + (2, HUFFMAN_EMIT_SYMBOL, 148), + (9, HUFFMAN_EMIT_SYMBOL, 148), + (23, HUFFMAN_EMIT_SYMBOL, 148), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 148), + (2, HUFFMAN_EMIT_SYMBOL, 159), + (9, HUFFMAN_EMIT_SYMBOL, 159), + (23, HUFFMAN_EMIT_SYMBOL, 159), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 159), + # Node 178 + (3, HUFFMAN_EMIT_SYMBOL, 144), + (6, HUFFMAN_EMIT_SYMBOL, 144), + (10, HUFFMAN_EMIT_SYMBOL, 144), + (15, HUFFMAN_EMIT_SYMBOL, 144), + (24, HUFFMAN_EMIT_SYMBOL, 144), + (31, HUFFMAN_EMIT_SYMBOL, 144), + (41, HUFFMAN_EMIT_SYMBOL, 144), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 144), + (3, HUFFMAN_EMIT_SYMBOL, 145), + (6, HUFFMAN_EMIT_SYMBOL, 145), + (10, HUFFMAN_EMIT_SYMBOL, 145), + (15, HUFFMAN_EMIT_SYMBOL, 145), + (24, HUFFMAN_EMIT_SYMBOL, 145), + (31, HUFFMAN_EMIT_SYMBOL, 145), + (41, HUFFMAN_EMIT_SYMBOL, 145), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 145), + # Node 179 + (3, HUFFMAN_EMIT_SYMBOL, 148), + (6, HUFFMAN_EMIT_SYMBOL, 148), + (10, HUFFMAN_EMIT_SYMBOL, 148), + (15, HUFFMAN_EMIT_SYMBOL, 148), + (24, HUFFMAN_EMIT_SYMBOL, 148), + (31, HUFFMAN_EMIT_SYMBOL, 148), + (41, HUFFMAN_EMIT_SYMBOL, 148), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 148), + (3, HUFFMAN_EMIT_SYMBOL, 159), + (6, HUFFMAN_EMIT_SYMBOL, 159), + (10, HUFFMAN_EMIT_SYMBOL, 159), + (15, HUFFMAN_EMIT_SYMBOL, 159), + (24, HUFFMAN_EMIT_SYMBOL, 159), + (31, HUFFMAN_EMIT_SYMBOL, 159), + (41, HUFFMAN_EMIT_SYMBOL, 159), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 159), + # Node 180 + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 171), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 206), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 215), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 225), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 236), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 237), + (188, 0, 0), + (189, 0, 0), + (193, 0, 0), + (196, 0, 0), + (200, 0, 0), + (203, 0, 0), + (209, 0, 0), + (216, 0, 0), + (224, 0, 0), + (238, 0, 0), + # Node 181 + (1, HUFFMAN_EMIT_SYMBOL, 171), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 171), + (1, HUFFMAN_EMIT_SYMBOL, 206), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 206), + (1, HUFFMAN_EMIT_SYMBOL, 215), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 215), + (1, HUFFMAN_EMIT_SYMBOL, 225), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 225), + (1, HUFFMAN_EMIT_SYMBOL, 236), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 236), + (1, HUFFMAN_EMIT_SYMBOL, 237), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 237), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 199), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 207), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 234), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 235), + # Node 182 + (2, HUFFMAN_EMIT_SYMBOL, 171), + (9, HUFFMAN_EMIT_SYMBOL, 171), + (23, HUFFMAN_EMIT_SYMBOL, 171), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 171), + (2, HUFFMAN_EMIT_SYMBOL, 206), + (9, HUFFMAN_EMIT_SYMBOL, 206), + (23, HUFFMAN_EMIT_SYMBOL, 206), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 206), + (2, HUFFMAN_EMIT_SYMBOL, 215), + (9, HUFFMAN_EMIT_SYMBOL, 215), + (23, HUFFMAN_EMIT_SYMBOL, 215), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 215), + (2, HUFFMAN_EMIT_SYMBOL, 225), + (9, HUFFMAN_EMIT_SYMBOL, 225), + (23, HUFFMAN_EMIT_SYMBOL, 225), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 225), + # Node 183 + (3, HUFFMAN_EMIT_SYMBOL, 171), + (6, HUFFMAN_EMIT_SYMBOL, 171), + (10, HUFFMAN_EMIT_SYMBOL, 171), + (15, HUFFMAN_EMIT_SYMBOL, 171), + (24, HUFFMAN_EMIT_SYMBOL, 171), + (31, HUFFMAN_EMIT_SYMBOL, 171), + (41, HUFFMAN_EMIT_SYMBOL, 171), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 171), + (3, HUFFMAN_EMIT_SYMBOL, 206), + (6, HUFFMAN_EMIT_SYMBOL, 206), + (10, HUFFMAN_EMIT_SYMBOL, 206), + (15, HUFFMAN_EMIT_SYMBOL, 206), + (24, HUFFMAN_EMIT_SYMBOL, 206), + (31, HUFFMAN_EMIT_SYMBOL, 206), + (41, HUFFMAN_EMIT_SYMBOL, 206), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 206), + # Node 184 + (3, HUFFMAN_EMIT_SYMBOL, 215), + (6, HUFFMAN_EMIT_SYMBOL, 215), + (10, HUFFMAN_EMIT_SYMBOL, 215), + (15, HUFFMAN_EMIT_SYMBOL, 215), + (24, HUFFMAN_EMIT_SYMBOL, 215), + (31, HUFFMAN_EMIT_SYMBOL, 215), + (41, HUFFMAN_EMIT_SYMBOL, 215), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 215), + (3, HUFFMAN_EMIT_SYMBOL, 225), + (6, HUFFMAN_EMIT_SYMBOL, 225), + (10, HUFFMAN_EMIT_SYMBOL, 225), + (15, HUFFMAN_EMIT_SYMBOL, 225), + (24, HUFFMAN_EMIT_SYMBOL, 225), + (31, HUFFMAN_EMIT_SYMBOL, 225), + (41, HUFFMAN_EMIT_SYMBOL, 225), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 225), + # Node 185 + (2, HUFFMAN_EMIT_SYMBOL, 236), + (9, HUFFMAN_EMIT_SYMBOL, 236), + (23, HUFFMAN_EMIT_SYMBOL, 236), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 236), + (2, HUFFMAN_EMIT_SYMBOL, 237), + (9, HUFFMAN_EMIT_SYMBOL, 237), + (23, HUFFMAN_EMIT_SYMBOL, 237), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 237), + (1, HUFFMAN_EMIT_SYMBOL, 199), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 199), + (1, HUFFMAN_EMIT_SYMBOL, 207), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 207), + (1, HUFFMAN_EMIT_SYMBOL, 234), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 234), + (1, HUFFMAN_EMIT_SYMBOL, 235), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 235), + # Node 186 + (3, HUFFMAN_EMIT_SYMBOL, 236), + (6, HUFFMAN_EMIT_SYMBOL, 236), + (10, HUFFMAN_EMIT_SYMBOL, 236), + (15, HUFFMAN_EMIT_SYMBOL, 236), + (24, HUFFMAN_EMIT_SYMBOL, 236), + (31, HUFFMAN_EMIT_SYMBOL, 236), + (41, HUFFMAN_EMIT_SYMBOL, 236), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 236), + (3, HUFFMAN_EMIT_SYMBOL, 237), + (6, HUFFMAN_EMIT_SYMBOL, 237), + (10, HUFFMAN_EMIT_SYMBOL, 237), + (15, HUFFMAN_EMIT_SYMBOL, 237), + (24, HUFFMAN_EMIT_SYMBOL, 237), + (31, HUFFMAN_EMIT_SYMBOL, 237), + (41, HUFFMAN_EMIT_SYMBOL, 237), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 237), + # Node 187 + (2, HUFFMAN_EMIT_SYMBOL, 199), + (9, HUFFMAN_EMIT_SYMBOL, 199), + (23, HUFFMAN_EMIT_SYMBOL, 199), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 199), + (2, HUFFMAN_EMIT_SYMBOL, 207), + (9, HUFFMAN_EMIT_SYMBOL, 207), + (23, HUFFMAN_EMIT_SYMBOL, 207), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 207), + (2, HUFFMAN_EMIT_SYMBOL, 234), + (9, HUFFMAN_EMIT_SYMBOL, 234), + (23, HUFFMAN_EMIT_SYMBOL, 234), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 234), + (2, HUFFMAN_EMIT_SYMBOL, 235), + (9, HUFFMAN_EMIT_SYMBOL, 235), + (23, HUFFMAN_EMIT_SYMBOL, 235), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 235), + # Node 188 + (3, HUFFMAN_EMIT_SYMBOL, 199), + (6, HUFFMAN_EMIT_SYMBOL, 199), + (10, HUFFMAN_EMIT_SYMBOL, 199), + (15, HUFFMAN_EMIT_SYMBOL, 199), + (24, HUFFMAN_EMIT_SYMBOL, 199), + (31, HUFFMAN_EMIT_SYMBOL, 199), + (41, HUFFMAN_EMIT_SYMBOL, 199), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 199), + (3, HUFFMAN_EMIT_SYMBOL, 207), + (6, HUFFMAN_EMIT_SYMBOL, 207), + (10, HUFFMAN_EMIT_SYMBOL, 207), + (15, HUFFMAN_EMIT_SYMBOL, 207), + (24, HUFFMAN_EMIT_SYMBOL, 207), + (31, HUFFMAN_EMIT_SYMBOL, 207), + (41, HUFFMAN_EMIT_SYMBOL, 207), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 207), + # Node 189 + (3, HUFFMAN_EMIT_SYMBOL, 234), + (6, HUFFMAN_EMIT_SYMBOL, 234), + (10, HUFFMAN_EMIT_SYMBOL, 234), + (15, HUFFMAN_EMIT_SYMBOL, 234), + (24, HUFFMAN_EMIT_SYMBOL, 234), + (31, HUFFMAN_EMIT_SYMBOL, 234), + (41, HUFFMAN_EMIT_SYMBOL, 234), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 234), + (3, HUFFMAN_EMIT_SYMBOL, 235), + (6, HUFFMAN_EMIT_SYMBOL, 235), + (10, HUFFMAN_EMIT_SYMBOL, 235), + (15, HUFFMAN_EMIT_SYMBOL, 235), + (24, HUFFMAN_EMIT_SYMBOL, 235), + (31, HUFFMAN_EMIT_SYMBOL, 235), + (41, HUFFMAN_EMIT_SYMBOL, 235), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 235), + # Node 190 + (194, 0, 0), + (195, 0, 0), + (197, 0, 0), + (198, 0, 0), + (201, 0, 0), + (202, 0, 0), + (204, 0, 0), + (205, 0, 0), + (210, 0, 0), + (213, 0, 0), + (217, 0, 0), + (220, 0, 0), + (225, 0, 0), + (231, 0, 0), + (239, 0, 0), + (246, 0, 0), + # Node 191 + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 192), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 193), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 200), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 201), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 202), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 205), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 210), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 213), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 218), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 219), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 238), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 240), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 242), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 243), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 255), + (206, 0, 0), + # Node 192 + (1, HUFFMAN_EMIT_SYMBOL, 192), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 192), + (1, HUFFMAN_EMIT_SYMBOL, 193), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 193), + (1, HUFFMAN_EMIT_SYMBOL, 200), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 200), + (1, HUFFMAN_EMIT_SYMBOL, 201), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 201), + (1, HUFFMAN_EMIT_SYMBOL, 202), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 202), + (1, HUFFMAN_EMIT_SYMBOL, 205), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 205), + (1, HUFFMAN_EMIT_SYMBOL, 210), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 210), + (1, HUFFMAN_EMIT_SYMBOL, 213), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 213), + # Node 193 + (2, HUFFMAN_EMIT_SYMBOL, 192), + (9, HUFFMAN_EMIT_SYMBOL, 192), + (23, HUFFMAN_EMIT_SYMBOL, 192), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 192), + (2, HUFFMAN_EMIT_SYMBOL, 193), + (9, HUFFMAN_EMIT_SYMBOL, 193), + (23, HUFFMAN_EMIT_SYMBOL, 193), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 193), + (2, HUFFMAN_EMIT_SYMBOL, 200), + (9, HUFFMAN_EMIT_SYMBOL, 200), + (23, HUFFMAN_EMIT_SYMBOL, 200), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 200), + (2, HUFFMAN_EMIT_SYMBOL, 201), + (9, HUFFMAN_EMIT_SYMBOL, 201), + (23, HUFFMAN_EMIT_SYMBOL, 201), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 201), + # Node 194 + (3, HUFFMAN_EMIT_SYMBOL, 192), + (6, HUFFMAN_EMIT_SYMBOL, 192), + (10, HUFFMAN_EMIT_SYMBOL, 192), + (15, HUFFMAN_EMIT_SYMBOL, 192), + (24, HUFFMAN_EMIT_SYMBOL, 192), + (31, HUFFMAN_EMIT_SYMBOL, 192), + (41, HUFFMAN_EMIT_SYMBOL, 192), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 192), + (3, HUFFMAN_EMIT_SYMBOL, 193), + (6, HUFFMAN_EMIT_SYMBOL, 193), + (10, HUFFMAN_EMIT_SYMBOL, 193), + (15, HUFFMAN_EMIT_SYMBOL, 193), + (24, HUFFMAN_EMIT_SYMBOL, 193), + (31, HUFFMAN_EMIT_SYMBOL, 193), + (41, HUFFMAN_EMIT_SYMBOL, 193), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 193), + # Node 195 + (3, HUFFMAN_EMIT_SYMBOL, 200), + (6, HUFFMAN_EMIT_SYMBOL, 200), + (10, HUFFMAN_EMIT_SYMBOL, 200), + (15, HUFFMAN_EMIT_SYMBOL, 200), + (24, HUFFMAN_EMIT_SYMBOL, 200), + (31, HUFFMAN_EMIT_SYMBOL, 200), + (41, HUFFMAN_EMIT_SYMBOL, 200), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 200), + (3, HUFFMAN_EMIT_SYMBOL, 201), + (6, HUFFMAN_EMIT_SYMBOL, 201), + (10, HUFFMAN_EMIT_SYMBOL, 201), + (15, HUFFMAN_EMIT_SYMBOL, 201), + (24, HUFFMAN_EMIT_SYMBOL, 201), + (31, HUFFMAN_EMIT_SYMBOL, 201), + (41, HUFFMAN_EMIT_SYMBOL, 201), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 201), + # Node 196 + (2, HUFFMAN_EMIT_SYMBOL, 202), + (9, HUFFMAN_EMIT_SYMBOL, 202), + (23, HUFFMAN_EMIT_SYMBOL, 202), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 202), + (2, HUFFMAN_EMIT_SYMBOL, 205), + (9, HUFFMAN_EMIT_SYMBOL, 205), + (23, HUFFMAN_EMIT_SYMBOL, 205), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 205), + (2, HUFFMAN_EMIT_SYMBOL, 210), + (9, HUFFMAN_EMIT_SYMBOL, 210), + (23, HUFFMAN_EMIT_SYMBOL, 210), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 210), + (2, HUFFMAN_EMIT_SYMBOL, 213), + (9, HUFFMAN_EMIT_SYMBOL, 213), + (23, HUFFMAN_EMIT_SYMBOL, 213), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 213), + # Node 197 + (3, HUFFMAN_EMIT_SYMBOL, 202), + (6, HUFFMAN_EMIT_SYMBOL, 202), + (10, HUFFMAN_EMIT_SYMBOL, 202), + (15, HUFFMAN_EMIT_SYMBOL, 202), + (24, HUFFMAN_EMIT_SYMBOL, 202), + (31, HUFFMAN_EMIT_SYMBOL, 202), + (41, HUFFMAN_EMIT_SYMBOL, 202), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 202), + (3, HUFFMAN_EMIT_SYMBOL, 205), + (6, HUFFMAN_EMIT_SYMBOL, 205), + (10, HUFFMAN_EMIT_SYMBOL, 205), + (15, HUFFMAN_EMIT_SYMBOL, 205), + (24, HUFFMAN_EMIT_SYMBOL, 205), + (31, HUFFMAN_EMIT_SYMBOL, 205), + (41, HUFFMAN_EMIT_SYMBOL, 205), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 205), + # Node 198 + (3, HUFFMAN_EMIT_SYMBOL, 210), + (6, HUFFMAN_EMIT_SYMBOL, 210), + (10, HUFFMAN_EMIT_SYMBOL, 210), + (15, HUFFMAN_EMIT_SYMBOL, 210), + (24, HUFFMAN_EMIT_SYMBOL, 210), + (31, HUFFMAN_EMIT_SYMBOL, 210), + (41, HUFFMAN_EMIT_SYMBOL, 210), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 210), + (3, HUFFMAN_EMIT_SYMBOL, 213), + (6, HUFFMAN_EMIT_SYMBOL, 213), + (10, HUFFMAN_EMIT_SYMBOL, 213), + (15, HUFFMAN_EMIT_SYMBOL, 213), + (24, HUFFMAN_EMIT_SYMBOL, 213), + (31, HUFFMAN_EMIT_SYMBOL, 213), + (41, HUFFMAN_EMIT_SYMBOL, 213), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 213), + # Node 199 + (1, HUFFMAN_EMIT_SYMBOL, 218), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 218), + (1, HUFFMAN_EMIT_SYMBOL, 219), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 219), + (1, HUFFMAN_EMIT_SYMBOL, 238), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 238), + (1, HUFFMAN_EMIT_SYMBOL, 240), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 240), + (1, HUFFMAN_EMIT_SYMBOL, 242), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 242), + (1, HUFFMAN_EMIT_SYMBOL, 243), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 243), + (1, HUFFMAN_EMIT_SYMBOL, 255), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 255), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 203), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 204), + # Node 200 + (2, HUFFMAN_EMIT_SYMBOL, 218), + (9, HUFFMAN_EMIT_SYMBOL, 218), + (23, HUFFMAN_EMIT_SYMBOL, 218), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 218), + (2, HUFFMAN_EMIT_SYMBOL, 219), + (9, HUFFMAN_EMIT_SYMBOL, 219), + (23, HUFFMAN_EMIT_SYMBOL, 219), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 219), + (2, HUFFMAN_EMIT_SYMBOL, 238), + (9, HUFFMAN_EMIT_SYMBOL, 238), + (23, HUFFMAN_EMIT_SYMBOL, 238), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 238), + (2, HUFFMAN_EMIT_SYMBOL, 240), + (9, HUFFMAN_EMIT_SYMBOL, 240), + (23, HUFFMAN_EMIT_SYMBOL, 240), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 240), + # Node 201 + (3, HUFFMAN_EMIT_SYMBOL, 218), + (6, HUFFMAN_EMIT_SYMBOL, 218), + (10, HUFFMAN_EMIT_SYMBOL, 218), + (15, HUFFMAN_EMIT_SYMBOL, 218), + (24, HUFFMAN_EMIT_SYMBOL, 218), + (31, HUFFMAN_EMIT_SYMBOL, 218), + (41, HUFFMAN_EMIT_SYMBOL, 218), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 218), + (3, HUFFMAN_EMIT_SYMBOL, 219), + (6, HUFFMAN_EMIT_SYMBOL, 219), + (10, HUFFMAN_EMIT_SYMBOL, 219), + (15, HUFFMAN_EMIT_SYMBOL, 219), + (24, HUFFMAN_EMIT_SYMBOL, 219), + (31, HUFFMAN_EMIT_SYMBOL, 219), + (41, HUFFMAN_EMIT_SYMBOL, 219), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 219), + # Node 202 + (3, HUFFMAN_EMIT_SYMBOL, 238), + (6, HUFFMAN_EMIT_SYMBOL, 238), + (10, HUFFMAN_EMIT_SYMBOL, 238), + (15, HUFFMAN_EMIT_SYMBOL, 238), + (24, HUFFMAN_EMIT_SYMBOL, 238), + (31, HUFFMAN_EMIT_SYMBOL, 238), + (41, HUFFMAN_EMIT_SYMBOL, 238), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 238), + (3, HUFFMAN_EMIT_SYMBOL, 240), + (6, HUFFMAN_EMIT_SYMBOL, 240), + (10, HUFFMAN_EMIT_SYMBOL, 240), + (15, HUFFMAN_EMIT_SYMBOL, 240), + (24, HUFFMAN_EMIT_SYMBOL, 240), + (31, HUFFMAN_EMIT_SYMBOL, 240), + (41, HUFFMAN_EMIT_SYMBOL, 240), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 240), + # Node 203 + (2, HUFFMAN_EMIT_SYMBOL, 242), + (9, HUFFMAN_EMIT_SYMBOL, 242), + (23, HUFFMAN_EMIT_SYMBOL, 242), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 242), + (2, HUFFMAN_EMIT_SYMBOL, 243), + (9, HUFFMAN_EMIT_SYMBOL, 243), + (23, HUFFMAN_EMIT_SYMBOL, 243), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 243), + (2, HUFFMAN_EMIT_SYMBOL, 255), + (9, HUFFMAN_EMIT_SYMBOL, 255), + (23, HUFFMAN_EMIT_SYMBOL, 255), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 255), + (1, HUFFMAN_EMIT_SYMBOL, 203), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 203), + (1, HUFFMAN_EMIT_SYMBOL, 204), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 204), + # Node 204 + (3, HUFFMAN_EMIT_SYMBOL, 242), + (6, HUFFMAN_EMIT_SYMBOL, 242), + (10, HUFFMAN_EMIT_SYMBOL, 242), + (15, HUFFMAN_EMIT_SYMBOL, 242), + (24, HUFFMAN_EMIT_SYMBOL, 242), + (31, HUFFMAN_EMIT_SYMBOL, 242), + (41, HUFFMAN_EMIT_SYMBOL, 242), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 242), + (3, HUFFMAN_EMIT_SYMBOL, 243), + (6, HUFFMAN_EMIT_SYMBOL, 243), + (10, HUFFMAN_EMIT_SYMBOL, 243), + (15, HUFFMAN_EMIT_SYMBOL, 243), + (24, HUFFMAN_EMIT_SYMBOL, 243), + (31, HUFFMAN_EMIT_SYMBOL, 243), + (41, HUFFMAN_EMIT_SYMBOL, 243), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 243), + # Node 205 + (3, HUFFMAN_EMIT_SYMBOL, 255), + (6, HUFFMAN_EMIT_SYMBOL, 255), + (10, HUFFMAN_EMIT_SYMBOL, 255), + (15, HUFFMAN_EMIT_SYMBOL, 255), + (24, HUFFMAN_EMIT_SYMBOL, 255), + (31, HUFFMAN_EMIT_SYMBOL, 255), + (41, HUFFMAN_EMIT_SYMBOL, 255), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 255), + (2, HUFFMAN_EMIT_SYMBOL, 203), + (9, HUFFMAN_EMIT_SYMBOL, 203), + (23, HUFFMAN_EMIT_SYMBOL, 203), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 203), + (2, HUFFMAN_EMIT_SYMBOL, 204), + (9, HUFFMAN_EMIT_SYMBOL, 204), + (23, HUFFMAN_EMIT_SYMBOL, 204), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 204), + # Node 206 + (3, HUFFMAN_EMIT_SYMBOL, 203), + (6, HUFFMAN_EMIT_SYMBOL, 203), + (10, HUFFMAN_EMIT_SYMBOL, 203), + (15, HUFFMAN_EMIT_SYMBOL, 203), + (24, HUFFMAN_EMIT_SYMBOL, 203), + (31, HUFFMAN_EMIT_SYMBOL, 203), + (41, HUFFMAN_EMIT_SYMBOL, 203), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 203), + (3, HUFFMAN_EMIT_SYMBOL, 204), + (6, HUFFMAN_EMIT_SYMBOL, 204), + (10, HUFFMAN_EMIT_SYMBOL, 204), + (15, HUFFMAN_EMIT_SYMBOL, 204), + (24, HUFFMAN_EMIT_SYMBOL, 204), + (31, HUFFMAN_EMIT_SYMBOL, 204), + (41, HUFFMAN_EMIT_SYMBOL, 204), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 204), + # Node 207 + (211, 0, 0), + (212, 0, 0), + (214, 0, 0), + (215, 0, 0), + (218, 0, 0), + (219, 0, 0), + (221, 0, 0), + (222, 0, 0), + (226, 0, 0), + (228, 0, 0), + (232, 0, 0), + (235, 0, 0), + (240, 0, 0), + (243, 0, 0), + (247, 0, 0), + (250, 0, 0), + # Node 208 + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 211), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 212), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 214), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 221), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 222), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 223), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 241), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 244), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 245), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 246), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 247), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 248), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 250), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 251), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 252), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 253), + # Node 209 + (1, HUFFMAN_EMIT_SYMBOL, 211), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 211), + (1, HUFFMAN_EMIT_SYMBOL, 212), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 212), + (1, HUFFMAN_EMIT_SYMBOL, 214), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 214), + (1, HUFFMAN_EMIT_SYMBOL, 221), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 221), + (1, HUFFMAN_EMIT_SYMBOL, 222), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 222), + (1, HUFFMAN_EMIT_SYMBOL, 223), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 223), + (1, HUFFMAN_EMIT_SYMBOL, 241), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 241), + (1, HUFFMAN_EMIT_SYMBOL, 244), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 244), + # Node 210 + (2, HUFFMAN_EMIT_SYMBOL, 211), + (9, HUFFMAN_EMIT_SYMBOL, 211), + (23, HUFFMAN_EMIT_SYMBOL, 211), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 211), + (2, HUFFMAN_EMIT_SYMBOL, 212), + (9, HUFFMAN_EMIT_SYMBOL, 212), + (23, HUFFMAN_EMIT_SYMBOL, 212), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 212), + (2, HUFFMAN_EMIT_SYMBOL, 214), + (9, HUFFMAN_EMIT_SYMBOL, 214), + (23, HUFFMAN_EMIT_SYMBOL, 214), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 214), + (2, HUFFMAN_EMIT_SYMBOL, 221), + (9, HUFFMAN_EMIT_SYMBOL, 221), + (23, HUFFMAN_EMIT_SYMBOL, 221), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 221), + # Node 211 + (3, HUFFMAN_EMIT_SYMBOL, 211), + (6, HUFFMAN_EMIT_SYMBOL, 211), + (10, HUFFMAN_EMIT_SYMBOL, 211), + (15, HUFFMAN_EMIT_SYMBOL, 211), + (24, HUFFMAN_EMIT_SYMBOL, 211), + (31, HUFFMAN_EMIT_SYMBOL, 211), + (41, HUFFMAN_EMIT_SYMBOL, 211), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 211), + (3, HUFFMAN_EMIT_SYMBOL, 212), + (6, HUFFMAN_EMIT_SYMBOL, 212), + (10, HUFFMAN_EMIT_SYMBOL, 212), + (15, HUFFMAN_EMIT_SYMBOL, 212), + (24, HUFFMAN_EMIT_SYMBOL, 212), + (31, HUFFMAN_EMIT_SYMBOL, 212), + (41, HUFFMAN_EMIT_SYMBOL, 212), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 212), + # Node 212 + (3, HUFFMAN_EMIT_SYMBOL, 214), + (6, HUFFMAN_EMIT_SYMBOL, 214), + (10, HUFFMAN_EMIT_SYMBOL, 214), + (15, HUFFMAN_EMIT_SYMBOL, 214), + (24, HUFFMAN_EMIT_SYMBOL, 214), + (31, HUFFMAN_EMIT_SYMBOL, 214), + (41, HUFFMAN_EMIT_SYMBOL, 214), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 214), + (3, HUFFMAN_EMIT_SYMBOL, 221), + (6, HUFFMAN_EMIT_SYMBOL, 221), + (10, HUFFMAN_EMIT_SYMBOL, 221), + (15, HUFFMAN_EMIT_SYMBOL, 221), + (24, HUFFMAN_EMIT_SYMBOL, 221), + (31, HUFFMAN_EMIT_SYMBOL, 221), + (41, HUFFMAN_EMIT_SYMBOL, 221), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 221), + # Node 213 + (2, HUFFMAN_EMIT_SYMBOL, 222), + (9, HUFFMAN_EMIT_SYMBOL, 222), + (23, HUFFMAN_EMIT_SYMBOL, 222), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 222), + (2, HUFFMAN_EMIT_SYMBOL, 223), + (9, HUFFMAN_EMIT_SYMBOL, 223), + (23, HUFFMAN_EMIT_SYMBOL, 223), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 223), + (2, HUFFMAN_EMIT_SYMBOL, 241), + (9, HUFFMAN_EMIT_SYMBOL, 241), + (23, HUFFMAN_EMIT_SYMBOL, 241), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 241), + (2, HUFFMAN_EMIT_SYMBOL, 244), + (9, HUFFMAN_EMIT_SYMBOL, 244), + (23, HUFFMAN_EMIT_SYMBOL, 244), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 244), + # Node 214 + (3, HUFFMAN_EMIT_SYMBOL, 222), + (6, HUFFMAN_EMIT_SYMBOL, 222), + (10, HUFFMAN_EMIT_SYMBOL, 222), + (15, HUFFMAN_EMIT_SYMBOL, 222), + (24, HUFFMAN_EMIT_SYMBOL, 222), + (31, HUFFMAN_EMIT_SYMBOL, 222), + (41, HUFFMAN_EMIT_SYMBOL, 222), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 222), + (3, HUFFMAN_EMIT_SYMBOL, 223), + (6, HUFFMAN_EMIT_SYMBOL, 223), + (10, HUFFMAN_EMIT_SYMBOL, 223), + (15, HUFFMAN_EMIT_SYMBOL, 223), + (24, HUFFMAN_EMIT_SYMBOL, 223), + (31, HUFFMAN_EMIT_SYMBOL, 223), + (41, HUFFMAN_EMIT_SYMBOL, 223), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 223), + # Node 215 + (3, HUFFMAN_EMIT_SYMBOL, 241), + (6, HUFFMAN_EMIT_SYMBOL, 241), + (10, HUFFMAN_EMIT_SYMBOL, 241), + (15, HUFFMAN_EMIT_SYMBOL, 241), + (24, HUFFMAN_EMIT_SYMBOL, 241), + (31, HUFFMAN_EMIT_SYMBOL, 241), + (41, HUFFMAN_EMIT_SYMBOL, 241), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 241), + (3, HUFFMAN_EMIT_SYMBOL, 244), + (6, HUFFMAN_EMIT_SYMBOL, 244), + (10, HUFFMAN_EMIT_SYMBOL, 244), + (15, HUFFMAN_EMIT_SYMBOL, 244), + (24, HUFFMAN_EMIT_SYMBOL, 244), + (31, HUFFMAN_EMIT_SYMBOL, 244), + (41, HUFFMAN_EMIT_SYMBOL, 244), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 244), + # Node 216 + (1, HUFFMAN_EMIT_SYMBOL, 245), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 245), + (1, HUFFMAN_EMIT_SYMBOL, 246), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 246), + (1, HUFFMAN_EMIT_SYMBOL, 247), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 247), + (1, HUFFMAN_EMIT_SYMBOL, 248), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 248), + (1, HUFFMAN_EMIT_SYMBOL, 250), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 250), + (1, HUFFMAN_EMIT_SYMBOL, 251), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 251), + (1, HUFFMAN_EMIT_SYMBOL, 252), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 252), + (1, HUFFMAN_EMIT_SYMBOL, 253), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 253), + # Node 217 + (2, HUFFMAN_EMIT_SYMBOL, 245), + (9, HUFFMAN_EMIT_SYMBOL, 245), + (23, HUFFMAN_EMIT_SYMBOL, 245), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 245), + (2, HUFFMAN_EMIT_SYMBOL, 246), + (9, HUFFMAN_EMIT_SYMBOL, 246), + (23, HUFFMAN_EMIT_SYMBOL, 246), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 246), + (2, HUFFMAN_EMIT_SYMBOL, 247), + (9, HUFFMAN_EMIT_SYMBOL, 247), + (23, HUFFMAN_EMIT_SYMBOL, 247), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 247), + (2, HUFFMAN_EMIT_SYMBOL, 248), + (9, HUFFMAN_EMIT_SYMBOL, 248), + (23, HUFFMAN_EMIT_SYMBOL, 248), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 248), + # Node 218 + (3, HUFFMAN_EMIT_SYMBOL, 245), + (6, HUFFMAN_EMIT_SYMBOL, 245), + (10, HUFFMAN_EMIT_SYMBOL, 245), + (15, HUFFMAN_EMIT_SYMBOL, 245), + (24, HUFFMAN_EMIT_SYMBOL, 245), + (31, HUFFMAN_EMIT_SYMBOL, 245), + (41, HUFFMAN_EMIT_SYMBOL, 245), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 245), + (3, HUFFMAN_EMIT_SYMBOL, 246), + (6, HUFFMAN_EMIT_SYMBOL, 246), + (10, HUFFMAN_EMIT_SYMBOL, 246), + (15, HUFFMAN_EMIT_SYMBOL, 246), + (24, HUFFMAN_EMIT_SYMBOL, 246), + (31, HUFFMAN_EMIT_SYMBOL, 246), + (41, HUFFMAN_EMIT_SYMBOL, 246), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 246), + # Node 219 + (3, HUFFMAN_EMIT_SYMBOL, 247), + (6, HUFFMAN_EMIT_SYMBOL, 247), + (10, HUFFMAN_EMIT_SYMBOL, 247), + (15, HUFFMAN_EMIT_SYMBOL, 247), + (24, HUFFMAN_EMIT_SYMBOL, 247), + (31, HUFFMAN_EMIT_SYMBOL, 247), + (41, HUFFMAN_EMIT_SYMBOL, 247), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 247), + (3, HUFFMAN_EMIT_SYMBOL, 248), + (6, HUFFMAN_EMIT_SYMBOL, 248), + (10, HUFFMAN_EMIT_SYMBOL, 248), + (15, HUFFMAN_EMIT_SYMBOL, 248), + (24, HUFFMAN_EMIT_SYMBOL, 248), + (31, HUFFMAN_EMIT_SYMBOL, 248), + (41, HUFFMAN_EMIT_SYMBOL, 248), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 248), + # Node 220 + (2, HUFFMAN_EMIT_SYMBOL, 250), + (9, HUFFMAN_EMIT_SYMBOL, 250), + (23, HUFFMAN_EMIT_SYMBOL, 250), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 250), + (2, HUFFMAN_EMIT_SYMBOL, 251), + (9, HUFFMAN_EMIT_SYMBOL, 251), + (23, HUFFMAN_EMIT_SYMBOL, 251), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 251), + (2, HUFFMAN_EMIT_SYMBOL, 252), + (9, HUFFMAN_EMIT_SYMBOL, 252), + (23, HUFFMAN_EMIT_SYMBOL, 252), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 252), + (2, HUFFMAN_EMIT_SYMBOL, 253), + (9, HUFFMAN_EMIT_SYMBOL, 253), + (23, HUFFMAN_EMIT_SYMBOL, 253), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 253), + # Node 221 + (3, HUFFMAN_EMIT_SYMBOL, 250), + (6, HUFFMAN_EMIT_SYMBOL, 250), + (10, HUFFMAN_EMIT_SYMBOL, 250), + (15, HUFFMAN_EMIT_SYMBOL, 250), + (24, HUFFMAN_EMIT_SYMBOL, 250), + (31, HUFFMAN_EMIT_SYMBOL, 250), + (41, HUFFMAN_EMIT_SYMBOL, 250), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 250), + (3, HUFFMAN_EMIT_SYMBOL, 251), + (6, HUFFMAN_EMIT_SYMBOL, 251), + (10, HUFFMAN_EMIT_SYMBOL, 251), + (15, HUFFMAN_EMIT_SYMBOL, 251), + (24, HUFFMAN_EMIT_SYMBOL, 251), + (31, HUFFMAN_EMIT_SYMBOL, 251), + (41, HUFFMAN_EMIT_SYMBOL, 251), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 251), + # Node 222 + (3, HUFFMAN_EMIT_SYMBOL, 252), + (6, HUFFMAN_EMIT_SYMBOL, 252), + (10, HUFFMAN_EMIT_SYMBOL, 252), + (15, HUFFMAN_EMIT_SYMBOL, 252), + (24, HUFFMAN_EMIT_SYMBOL, 252), + (31, HUFFMAN_EMIT_SYMBOL, 252), + (41, HUFFMAN_EMIT_SYMBOL, 252), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 252), + (3, HUFFMAN_EMIT_SYMBOL, 253), + (6, HUFFMAN_EMIT_SYMBOL, 253), + (10, HUFFMAN_EMIT_SYMBOL, 253), + (15, HUFFMAN_EMIT_SYMBOL, 253), + (24, HUFFMAN_EMIT_SYMBOL, 253), + (31, HUFFMAN_EMIT_SYMBOL, 253), + (41, HUFFMAN_EMIT_SYMBOL, 253), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 253), + # Node 223 + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 254), + (227, 0, 0), + (229, 0, 0), + (230, 0, 0), + (233, 0, 0), + (234, 0, 0), + (236, 0, 0), + (237, 0, 0), + (241, 0, 0), + (242, 0, 0), + (244, 0, 0), + (245, 0, 0), + (248, 0, 0), + (249, 0, 0), + (251, 0, 0), + (252, 0, 0), + # Node 224 + (1, HUFFMAN_EMIT_SYMBOL, 254), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 254), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 2), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 3), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 4), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 5), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 6), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 7), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 8), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 11), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 12), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 14), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 15), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 16), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 17), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 18), + # Node 225 + (2, HUFFMAN_EMIT_SYMBOL, 254), + (9, HUFFMAN_EMIT_SYMBOL, 254), + (23, HUFFMAN_EMIT_SYMBOL, 254), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 254), + (1, HUFFMAN_EMIT_SYMBOL, 2), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 2), + (1, HUFFMAN_EMIT_SYMBOL, 3), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 3), + (1, HUFFMAN_EMIT_SYMBOL, 4), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 4), + (1, HUFFMAN_EMIT_SYMBOL, 5), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 5), + (1, HUFFMAN_EMIT_SYMBOL, 6), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 6), + (1, HUFFMAN_EMIT_SYMBOL, 7), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 7), + # Node 226 + (3, HUFFMAN_EMIT_SYMBOL, 254), + (6, HUFFMAN_EMIT_SYMBOL, 254), + (10, HUFFMAN_EMIT_SYMBOL, 254), + (15, HUFFMAN_EMIT_SYMBOL, 254), + (24, HUFFMAN_EMIT_SYMBOL, 254), + (31, HUFFMAN_EMIT_SYMBOL, 254), + (41, HUFFMAN_EMIT_SYMBOL, 254), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 254), + (2, HUFFMAN_EMIT_SYMBOL, 2), + (9, HUFFMAN_EMIT_SYMBOL, 2), + (23, HUFFMAN_EMIT_SYMBOL, 2), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 2), + (2, HUFFMAN_EMIT_SYMBOL, 3), + (9, HUFFMAN_EMIT_SYMBOL, 3), + (23, HUFFMAN_EMIT_SYMBOL, 3), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 3), + # Node 227 + (3, HUFFMAN_EMIT_SYMBOL, 2), + (6, HUFFMAN_EMIT_SYMBOL, 2), + (10, HUFFMAN_EMIT_SYMBOL, 2), + (15, HUFFMAN_EMIT_SYMBOL, 2), + (24, HUFFMAN_EMIT_SYMBOL, 2), + (31, HUFFMAN_EMIT_SYMBOL, 2), + (41, HUFFMAN_EMIT_SYMBOL, 2), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 2), + (3, HUFFMAN_EMIT_SYMBOL, 3), + (6, HUFFMAN_EMIT_SYMBOL, 3), + (10, HUFFMAN_EMIT_SYMBOL, 3), + (15, HUFFMAN_EMIT_SYMBOL, 3), + (24, HUFFMAN_EMIT_SYMBOL, 3), + (31, HUFFMAN_EMIT_SYMBOL, 3), + (41, HUFFMAN_EMIT_SYMBOL, 3), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 3), + # Node 228 + (2, HUFFMAN_EMIT_SYMBOL, 4), + (9, HUFFMAN_EMIT_SYMBOL, 4), + (23, HUFFMAN_EMIT_SYMBOL, 4), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 4), + (2, HUFFMAN_EMIT_SYMBOL, 5), + (9, HUFFMAN_EMIT_SYMBOL, 5), + (23, HUFFMAN_EMIT_SYMBOL, 5), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 5), + (2, HUFFMAN_EMIT_SYMBOL, 6), + (9, HUFFMAN_EMIT_SYMBOL, 6), + (23, HUFFMAN_EMIT_SYMBOL, 6), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 6), + (2, HUFFMAN_EMIT_SYMBOL, 7), + (9, HUFFMAN_EMIT_SYMBOL, 7), + (23, HUFFMAN_EMIT_SYMBOL, 7), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 7), + # Node 229 + (3, HUFFMAN_EMIT_SYMBOL, 4), + (6, HUFFMAN_EMIT_SYMBOL, 4), + (10, HUFFMAN_EMIT_SYMBOL, 4), + (15, HUFFMAN_EMIT_SYMBOL, 4), + (24, HUFFMAN_EMIT_SYMBOL, 4), + (31, HUFFMAN_EMIT_SYMBOL, 4), + (41, HUFFMAN_EMIT_SYMBOL, 4), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 4), + (3, HUFFMAN_EMIT_SYMBOL, 5), + (6, HUFFMAN_EMIT_SYMBOL, 5), + (10, HUFFMAN_EMIT_SYMBOL, 5), + (15, HUFFMAN_EMIT_SYMBOL, 5), + (24, HUFFMAN_EMIT_SYMBOL, 5), + (31, HUFFMAN_EMIT_SYMBOL, 5), + (41, HUFFMAN_EMIT_SYMBOL, 5), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 5), + # Node 230 + (3, HUFFMAN_EMIT_SYMBOL, 6), + (6, HUFFMAN_EMIT_SYMBOL, 6), + (10, HUFFMAN_EMIT_SYMBOL, 6), + (15, HUFFMAN_EMIT_SYMBOL, 6), + (24, HUFFMAN_EMIT_SYMBOL, 6), + (31, HUFFMAN_EMIT_SYMBOL, 6), + (41, HUFFMAN_EMIT_SYMBOL, 6), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 6), + (3, HUFFMAN_EMIT_SYMBOL, 7), + (6, HUFFMAN_EMIT_SYMBOL, 7), + (10, HUFFMAN_EMIT_SYMBOL, 7), + (15, HUFFMAN_EMIT_SYMBOL, 7), + (24, HUFFMAN_EMIT_SYMBOL, 7), + (31, HUFFMAN_EMIT_SYMBOL, 7), + (41, HUFFMAN_EMIT_SYMBOL, 7), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 7), + # Node 231 + (1, HUFFMAN_EMIT_SYMBOL, 8), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 8), + (1, HUFFMAN_EMIT_SYMBOL, 11), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 11), + (1, HUFFMAN_EMIT_SYMBOL, 12), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 12), + (1, HUFFMAN_EMIT_SYMBOL, 14), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 14), + (1, HUFFMAN_EMIT_SYMBOL, 15), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 15), + (1, HUFFMAN_EMIT_SYMBOL, 16), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 16), + (1, HUFFMAN_EMIT_SYMBOL, 17), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 17), + (1, HUFFMAN_EMIT_SYMBOL, 18), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 18), + # Node 232 + (2, HUFFMAN_EMIT_SYMBOL, 8), + (9, HUFFMAN_EMIT_SYMBOL, 8), + (23, HUFFMAN_EMIT_SYMBOL, 8), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 8), + (2, HUFFMAN_EMIT_SYMBOL, 11), + (9, HUFFMAN_EMIT_SYMBOL, 11), + (23, HUFFMAN_EMIT_SYMBOL, 11), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 11), + (2, HUFFMAN_EMIT_SYMBOL, 12), + (9, HUFFMAN_EMIT_SYMBOL, 12), + (23, HUFFMAN_EMIT_SYMBOL, 12), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 12), + (2, HUFFMAN_EMIT_SYMBOL, 14), + (9, HUFFMAN_EMIT_SYMBOL, 14), + (23, HUFFMAN_EMIT_SYMBOL, 14), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 14), + # Node 233 + (3, HUFFMAN_EMIT_SYMBOL, 8), + (6, HUFFMAN_EMIT_SYMBOL, 8), + (10, HUFFMAN_EMIT_SYMBOL, 8), + (15, HUFFMAN_EMIT_SYMBOL, 8), + (24, HUFFMAN_EMIT_SYMBOL, 8), + (31, HUFFMAN_EMIT_SYMBOL, 8), + (41, HUFFMAN_EMIT_SYMBOL, 8), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 8), + (3, HUFFMAN_EMIT_SYMBOL, 11), + (6, HUFFMAN_EMIT_SYMBOL, 11), + (10, HUFFMAN_EMIT_SYMBOL, 11), + (15, HUFFMAN_EMIT_SYMBOL, 11), + (24, HUFFMAN_EMIT_SYMBOL, 11), + (31, HUFFMAN_EMIT_SYMBOL, 11), + (41, HUFFMAN_EMIT_SYMBOL, 11), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 11), + # Node 234 + (3, HUFFMAN_EMIT_SYMBOL, 12), + (6, HUFFMAN_EMIT_SYMBOL, 12), + (10, HUFFMAN_EMIT_SYMBOL, 12), + (15, HUFFMAN_EMIT_SYMBOL, 12), + (24, HUFFMAN_EMIT_SYMBOL, 12), + (31, HUFFMAN_EMIT_SYMBOL, 12), + (41, HUFFMAN_EMIT_SYMBOL, 12), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 12), + (3, HUFFMAN_EMIT_SYMBOL, 14), + (6, HUFFMAN_EMIT_SYMBOL, 14), + (10, HUFFMAN_EMIT_SYMBOL, 14), + (15, HUFFMAN_EMIT_SYMBOL, 14), + (24, HUFFMAN_EMIT_SYMBOL, 14), + (31, HUFFMAN_EMIT_SYMBOL, 14), + (41, HUFFMAN_EMIT_SYMBOL, 14), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 14), + # Node 235 + (2, HUFFMAN_EMIT_SYMBOL, 15), + (9, HUFFMAN_EMIT_SYMBOL, 15), + (23, HUFFMAN_EMIT_SYMBOL, 15), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 15), + (2, HUFFMAN_EMIT_SYMBOL, 16), + (9, HUFFMAN_EMIT_SYMBOL, 16), + (23, HUFFMAN_EMIT_SYMBOL, 16), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 16), + (2, HUFFMAN_EMIT_SYMBOL, 17), + (9, HUFFMAN_EMIT_SYMBOL, 17), + (23, HUFFMAN_EMIT_SYMBOL, 17), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 17), + (2, HUFFMAN_EMIT_SYMBOL, 18), + (9, HUFFMAN_EMIT_SYMBOL, 18), + (23, HUFFMAN_EMIT_SYMBOL, 18), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 18), + # Node 236 + (3, HUFFMAN_EMIT_SYMBOL, 15), + (6, HUFFMAN_EMIT_SYMBOL, 15), + (10, HUFFMAN_EMIT_SYMBOL, 15), + (15, HUFFMAN_EMIT_SYMBOL, 15), + (24, HUFFMAN_EMIT_SYMBOL, 15), + (31, HUFFMAN_EMIT_SYMBOL, 15), + (41, HUFFMAN_EMIT_SYMBOL, 15), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 15), + (3, HUFFMAN_EMIT_SYMBOL, 16), + (6, HUFFMAN_EMIT_SYMBOL, 16), + (10, HUFFMAN_EMIT_SYMBOL, 16), + (15, HUFFMAN_EMIT_SYMBOL, 16), + (24, HUFFMAN_EMIT_SYMBOL, 16), + (31, HUFFMAN_EMIT_SYMBOL, 16), + (41, HUFFMAN_EMIT_SYMBOL, 16), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 16), + # Node 237 + (3, HUFFMAN_EMIT_SYMBOL, 17), + (6, HUFFMAN_EMIT_SYMBOL, 17), + (10, HUFFMAN_EMIT_SYMBOL, 17), + (15, HUFFMAN_EMIT_SYMBOL, 17), + (24, HUFFMAN_EMIT_SYMBOL, 17), + (31, HUFFMAN_EMIT_SYMBOL, 17), + (41, HUFFMAN_EMIT_SYMBOL, 17), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 17), + (3, HUFFMAN_EMIT_SYMBOL, 18), + (6, HUFFMAN_EMIT_SYMBOL, 18), + (10, HUFFMAN_EMIT_SYMBOL, 18), + (15, HUFFMAN_EMIT_SYMBOL, 18), + (24, HUFFMAN_EMIT_SYMBOL, 18), + (31, HUFFMAN_EMIT_SYMBOL, 18), + (41, HUFFMAN_EMIT_SYMBOL, 18), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 18), + # Node 238 + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 19), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 20), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 21), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 23), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 24), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 25), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 26), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 27), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 28), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 29), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 30), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 31), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 127), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 220), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 249), + (253, 0, 0), + # Node 239 + (1, HUFFMAN_EMIT_SYMBOL, 19), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 19), + (1, HUFFMAN_EMIT_SYMBOL, 20), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 20), + (1, HUFFMAN_EMIT_SYMBOL, 21), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 21), + (1, HUFFMAN_EMIT_SYMBOL, 23), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 23), + (1, HUFFMAN_EMIT_SYMBOL, 24), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 24), + (1, HUFFMAN_EMIT_SYMBOL, 25), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 25), + (1, HUFFMAN_EMIT_SYMBOL, 26), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 26), + (1, HUFFMAN_EMIT_SYMBOL, 27), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 27), + # Node 240 + (2, HUFFMAN_EMIT_SYMBOL, 19), + (9, HUFFMAN_EMIT_SYMBOL, 19), + (23, HUFFMAN_EMIT_SYMBOL, 19), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 19), + (2, HUFFMAN_EMIT_SYMBOL, 20), + (9, HUFFMAN_EMIT_SYMBOL, 20), + (23, HUFFMAN_EMIT_SYMBOL, 20), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 20), + (2, HUFFMAN_EMIT_SYMBOL, 21), + (9, HUFFMAN_EMIT_SYMBOL, 21), + (23, HUFFMAN_EMIT_SYMBOL, 21), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 21), + (2, HUFFMAN_EMIT_SYMBOL, 23), + (9, HUFFMAN_EMIT_SYMBOL, 23), + (23, HUFFMAN_EMIT_SYMBOL, 23), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 23), + # Node 241 + (3, HUFFMAN_EMIT_SYMBOL, 19), + (6, HUFFMAN_EMIT_SYMBOL, 19), + (10, HUFFMAN_EMIT_SYMBOL, 19), + (15, HUFFMAN_EMIT_SYMBOL, 19), + (24, HUFFMAN_EMIT_SYMBOL, 19), + (31, HUFFMAN_EMIT_SYMBOL, 19), + (41, HUFFMAN_EMIT_SYMBOL, 19), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 19), + (3, HUFFMAN_EMIT_SYMBOL, 20), + (6, HUFFMAN_EMIT_SYMBOL, 20), + (10, HUFFMAN_EMIT_SYMBOL, 20), + (15, HUFFMAN_EMIT_SYMBOL, 20), + (24, HUFFMAN_EMIT_SYMBOL, 20), + (31, HUFFMAN_EMIT_SYMBOL, 20), + (41, HUFFMAN_EMIT_SYMBOL, 20), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 20), + # Node 242 + (3, HUFFMAN_EMIT_SYMBOL, 21), + (6, HUFFMAN_EMIT_SYMBOL, 21), + (10, HUFFMAN_EMIT_SYMBOL, 21), + (15, HUFFMAN_EMIT_SYMBOL, 21), + (24, HUFFMAN_EMIT_SYMBOL, 21), + (31, HUFFMAN_EMIT_SYMBOL, 21), + (41, HUFFMAN_EMIT_SYMBOL, 21), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 21), + (3, HUFFMAN_EMIT_SYMBOL, 23), + (6, HUFFMAN_EMIT_SYMBOL, 23), + (10, HUFFMAN_EMIT_SYMBOL, 23), + (15, HUFFMAN_EMIT_SYMBOL, 23), + (24, HUFFMAN_EMIT_SYMBOL, 23), + (31, HUFFMAN_EMIT_SYMBOL, 23), + (41, HUFFMAN_EMIT_SYMBOL, 23), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 23), + # Node 243 + (2, HUFFMAN_EMIT_SYMBOL, 24), + (9, HUFFMAN_EMIT_SYMBOL, 24), + (23, HUFFMAN_EMIT_SYMBOL, 24), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 24), + (2, HUFFMAN_EMIT_SYMBOL, 25), + (9, HUFFMAN_EMIT_SYMBOL, 25), + (23, HUFFMAN_EMIT_SYMBOL, 25), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 25), + (2, HUFFMAN_EMIT_SYMBOL, 26), + (9, HUFFMAN_EMIT_SYMBOL, 26), + (23, HUFFMAN_EMIT_SYMBOL, 26), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 26), + (2, HUFFMAN_EMIT_SYMBOL, 27), + (9, HUFFMAN_EMIT_SYMBOL, 27), + (23, HUFFMAN_EMIT_SYMBOL, 27), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 27), + # Node 244 + (3, HUFFMAN_EMIT_SYMBOL, 24), + (6, HUFFMAN_EMIT_SYMBOL, 24), + (10, HUFFMAN_EMIT_SYMBOL, 24), + (15, HUFFMAN_EMIT_SYMBOL, 24), + (24, HUFFMAN_EMIT_SYMBOL, 24), + (31, HUFFMAN_EMIT_SYMBOL, 24), + (41, HUFFMAN_EMIT_SYMBOL, 24), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 24), + (3, HUFFMAN_EMIT_SYMBOL, 25), + (6, HUFFMAN_EMIT_SYMBOL, 25), + (10, HUFFMAN_EMIT_SYMBOL, 25), + (15, HUFFMAN_EMIT_SYMBOL, 25), + (24, HUFFMAN_EMIT_SYMBOL, 25), + (31, HUFFMAN_EMIT_SYMBOL, 25), + (41, HUFFMAN_EMIT_SYMBOL, 25), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 25), + # Node 245 + (3, HUFFMAN_EMIT_SYMBOL, 26), + (6, HUFFMAN_EMIT_SYMBOL, 26), + (10, HUFFMAN_EMIT_SYMBOL, 26), + (15, HUFFMAN_EMIT_SYMBOL, 26), + (24, HUFFMAN_EMIT_SYMBOL, 26), + (31, HUFFMAN_EMIT_SYMBOL, 26), + (41, HUFFMAN_EMIT_SYMBOL, 26), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 26), + (3, HUFFMAN_EMIT_SYMBOL, 27), + (6, HUFFMAN_EMIT_SYMBOL, 27), + (10, HUFFMAN_EMIT_SYMBOL, 27), + (15, HUFFMAN_EMIT_SYMBOL, 27), + (24, HUFFMAN_EMIT_SYMBOL, 27), + (31, HUFFMAN_EMIT_SYMBOL, 27), + (41, HUFFMAN_EMIT_SYMBOL, 27), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 27), + # Node 246 + (1, HUFFMAN_EMIT_SYMBOL, 28), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 28), + (1, HUFFMAN_EMIT_SYMBOL, 29), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 29), + (1, HUFFMAN_EMIT_SYMBOL, 30), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 30), + (1, HUFFMAN_EMIT_SYMBOL, 31), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 31), + (1, HUFFMAN_EMIT_SYMBOL, 127), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 127), + (1, HUFFMAN_EMIT_SYMBOL, 220), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 220), + (1, HUFFMAN_EMIT_SYMBOL, 249), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 249), + (254, 0, 0), + (255, 0, 0), + # Node 247 + (2, HUFFMAN_EMIT_SYMBOL, 28), + (9, HUFFMAN_EMIT_SYMBOL, 28), + (23, HUFFMAN_EMIT_SYMBOL, 28), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 28), + (2, HUFFMAN_EMIT_SYMBOL, 29), + (9, HUFFMAN_EMIT_SYMBOL, 29), + (23, HUFFMAN_EMIT_SYMBOL, 29), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 29), + (2, HUFFMAN_EMIT_SYMBOL, 30), + (9, HUFFMAN_EMIT_SYMBOL, 30), + (23, HUFFMAN_EMIT_SYMBOL, 30), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 30), + (2, HUFFMAN_EMIT_SYMBOL, 31), + (9, HUFFMAN_EMIT_SYMBOL, 31), + (23, HUFFMAN_EMIT_SYMBOL, 31), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 31), + # Node 248 + (3, HUFFMAN_EMIT_SYMBOL, 28), + (6, HUFFMAN_EMIT_SYMBOL, 28), + (10, HUFFMAN_EMIT_SYMBOL, 28), + (15, HUFFMAN_EMIT_SYMBOL, 28), + (24, HUFFMAN_EMIT_SYMBOL, 28), + (31, HUFFMAN_EMIT_SYMBOL, 28), + (41, HUFFMAN_EMIT_SYMBOL, 28), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 28), + (3, HUFFMAN_EMIT_SYMBOL, 29), + (6, HUFFMAN_EMIT_SYMBOL, 29), + (10, HUFFMAN_EMIT_SYMBOL, 29), + (15, HUFFMAN_EMIT_SYMBOL, 29), + (24, HUFFMAN_EMIT_SYMBOL, 29), + (31, HUFFMAN_EMIT_SYMBOL, 29), + (41, HUFFMAN_EMIT_SYMBOL, 29), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 29), + # Node 249 + (3, HUFFMAN_EMIT_SYMBOL, 30), + (6, HUFFMAN_EMIT_SYMBOL, 30), + (10, HUFFMAN_EMIT_SYMBOL, 30), + (15, HUFFMAN_EMIT_SYMBOL, 30), + (24, HUFFMAN_EMIT_SYMBOL, 30), + (31, HUFFMAN_EMIT_SYMBOL, 30), + (41, HUFFMAN_EMIT_SYMBOL, 30), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 30), + (3, HUFFMAN_EMIT_SYMBOL, 31), + (6, HUFFMAN_EMIT_SYMBOL, 31), + (10, HUFFMAN_EMIT_SYMBOL, 31), + (15, HUFFMAN_EMIT_SYMBOL, 31), + (24, HUFFMAN_EMIT_SYMBOL, 31), + (31, HUFFMAN_EMIT_SYMBOL, 31), + (41, HUFFMAN_EMIT_SYMBOL, 31), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 31), + # Node 250 + (2, HUFFMAN_EMIT_SYMBOL, 127), + (9, HUFFMAN_EMIT_SYMBOL, 127), + (23, HUFFMAN_EMIT_SYMBOL, 127), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 127), + (2, HUFFMAN_EMIT_SYMBOL, 220), + (9, HUFFMAN_EMIT_SYMBOL, 220), + (23, HUFFMAN_EMIT_SYMBOL, 220), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 220), + (2, HUFFMAN_EMIT_SYMBOL, 249), + (9, HUFFMAN_EMIT_SYMBOL, 249), + (23, HUFFMAN_EMIT_SYMBOL, 249), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 249), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 10), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 13), + (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 22), + (0, HUFFMAN_FAIL, 0), + # Node 251 + (3, HUFFMAN_EMIT_SYMBOL, 127), + (6, HUFFMAN_EMIT_SYMBOL, 127), + (10, HUFFMAN_EMIT_SYMBOL, 127), + (15, HUFFMAN_EMIT_SYMBOL, 127), + (24, HUFFMAN_EMIT_SYMBOL, 127), + (31, HUFFMAN_EMIT_SYMBOL, 127), + (41, HUFFMAN_EMIT_SYMBOL, 127), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 127), + (3, HUFFMAN_EMIT_SYMBOL, 220), + (6, HUFFMAN_EMIT_SYMBOL, 220), + (10, HUFFMAN_EMIT_SYMBOL, 220), + (15, HUFFMAN_EMIT_SYMBOL, 220), + (24, HUFFMAN_EMIT_SYMBOL, 220), + (31, HUFFMAN_EMIT_SYMBOL, 220), + (41, HUFFMAN_EMIT_SYMBOL, 220), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 220), + # Node 252 + (3, HUFFMAN_EMIT_SYMBOL, 249), + (6, HUFFMAN_EMIT_SYMBOL, 249), + (10, HUFFMAN_EMIT_SYMBOL, 249), + (15, HUFFMAN_EMIT_SYMBOL, 249), + (24, HUFFMAN_EMIT_SYMBOL, 249), + (31, HUFFMAN_EMIT_SYMBOL, 249), + (41, HUFFMAN_EMIT_SYMBOL, 249), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 249), + (1, HUFFMAN_EMIT_SYMBOL, 10), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 10), + (1, HUFFMAN_EMIT_SYMBOL, 13), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 13), + (1, HUFFMAN_EMIT_SYMBOL, 22), + (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 22), + (0, HUFFMAN_FAIL, 0), + (0, HUFFMAN_FAIL, 0), + # Node 253 + (2, HUFFMAN_EMIT_SYMBOL, 10), + (9, HUFFMAN_EMIT_SYMBOL, 10), + (23, HUFFMAN_EMIT_SYMBOL, 10), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 10), + (2, HUFFMAN_EMIT_SYMBOL, 13), + (9, HUFFMAN_EMIT_SYMBOL, 13), + (23, HUFFMAN_EMIT_SYMBOL, 13), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 13), + (2, HUFFMAN_EMIT_SYMBOL, 22), + (9, HUFFMAN_EMIT_SYMBOL, 22), + (23, HUFFMAN_EMIT_SYMBOL, 22), + (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 22), + (0, HUFFMAN_FAIL, 0), + (0, HUFFMAN_FAIL, 0), + (0, HUFFMAN_FAIL, 0), + (0, HUFFMAN_FAIL, 0), + # Node 254 + (3, HUFFMAN_EMIT_SYMBOL, 10), + (6, HUFFMAN_EMIT_SYMBOL, 10), + (10, HUFFMAN_EMIT_SYMBOL, 10), + (15, HUFFMAN_EMIT_SYMBOL, 10), + (24, HUFFMAN_EMIT_SYMBOL, 10), + (31, HUFFMAN_EMIT_SYMBOL, 10), + (41, HUFFMAN_EMIT_SYMBOL, 10), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 10), + (3, HUFFMAN_EMIT_SYMBOL, 13), + (6, HUFFMAN_EMIT_SYMBOL, 13), + (10, HUFFMAN_EMIT_SYMBOL, 13), + (15, HUFFMAN_EMIT_SYMBOL, 13), + (24, HUFFMAN_EMIT_SYMBOL, 13), + (31, HUFFMAN_EMIT_SYMBOL, 13), + (41, HUFFMAN_EMIT_SYMBOL, 13), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 13), + # Node 255 + (3, HUFFMAN_EMIT_SYMBOL, 22), + (6, HUFFMAN_EMIT_SYMBOL, 22), + (10, HUFFMAN_EMIT_SYMBOL, 22), + (15, HUFFMAN_EMIT_SYMBOL, 22), + (24, HUFFMAN_EMIT_SYMBOL, 22), + (31, HUFFMAN_EMIT_SYMBOL, 22), + (41, HUFFMAN_EMIT_SYMBOL, 22), + (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 22), + (0, HUFFMAN_FAIL, 0), + (0, HUFFMAN_FAIL, 0), + (0, HUFFMAN_FAIL, 0), + (0, HUFFMAN_FAIL, 0), + (0, HUFFMAN_FAIL, 0), + (0, HUFFMAN_FAIL, 0), + (0, HUFFMAN_FAIL, 0), + (0, HUFFMAN_FAIL, 0), +] diff --git a/.venv/lib/python3.9/site-packages/jh2/hpack/struct.py b/.venv/lib/python3.9/site-packages/jh2/hpack/struct.py new file mode 100644 index 0000000..362d4bc --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/hpack/struct.py @@ -0,0 +1,42 @@ +""" +hpack/struct +~~~~~~~~~~~~ + +Contains structures for representing header fields with associated metadata. +""" + +from __future__ import annotations + + +class HeaderTuple(tuple): + """ + A data structure that stores a single header field. + + HTTP headers can be thought of as tuples of ``(field name, field value)``. + A single header block is a sequence of such tuples. + + In HTTP/2, however, certain bits of additional information are required for + compressing these headers: in particular, whether the header field can be + safely added to the HPACK compression context. + + This class stores a header that can be added to the compression context. In + all other ways it behaves exactly like a tuple. + """ + + __slots__ = () + + indexable = True + + def __new__(cls, *args): + return tuple.__new__(cls, args) + + +class NeverIndexedHeaderTuple(HeaderTuple): + """ + A data structure that stores a single header field that cannot be added to + a HTTP/2 header compression context. + """ + + __slots__ = () + + indexable = False diff --git a/.venv/lib/python3.9/site-packages/jh2/hpack/table.py b/.venv/lib/python3.9/site-packages/jh2/hpack/table.py new file mode 100644 index 0000000..f772ef2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/hpack/table.py @@ -0,0 +1,237 @@ +# flake8: noqa +from __future__ import annotations + +import logging +from collections import deque + +from .exceptions import InvalidTableIndex + +log = logging.getLogger(__name__) + + +def table_entry_size(name, value): + """ + Calculates the size of a single entry + + This size is mostly irrelevant to us and defined + specifically to accommodate memory management for + lower level implementations. The 32 extra bytes are + considered the "maximum" overhead that would be + required to represent each entry in the table. + + See RFC7541 Section 4.1 + """ + return 32 + len(name) + len(value) + + +class HeaderTable: + """ + Implements the combined static and dynamic header table + + The name and value arguments for all the functions + should ONLY be byte strings (b'') however this is not + strictly enforced in the interface. + + See RFC7541 Section 2.3 + """ + + #: Default maximum size of the dynamic table. See + #: RFC7540 Section 6.5.2. + DEFAULT_SIZE = 4096 + + #: Constant list of static headers. See RFC7541 Section + #: 2.3.1 and Appendix A + STATIC_TABLE = ( + (b":authority", b""), # noqa + (b":method", b"GET"), # noqa + (b":method", b"POST"), # noqa + (b":path", b"/"), # noqa + (b":path", b"/index.html"), # noqa + (b":scheme", b"http"), # noqa + (b":scheme", b"https"), # noqa + (b":status", b"200"), # noqa + (b":status", b"204"), # noqa + (b":status", b"206"), # noqa + (b":status", b"304"), # noqa + (b":status", b"400"), # noqa + (b":status", b"404"), # noqa + (b":status", b"500"), # noqa + (b"accept-charset", b""), # noqa + (b"accept-encoding", b"gzip, deflate"), # noqa + (b"accept-language", b""), # noqa + (b"accept-ranges", b""), # noqa + (b"accept", b""), # noqa + (b"access-control-allow-origin", b""), # noqa + (b"age", b""), # noqa + (b"allow", b""), # noqa + (b"authorization", b""), # noqa + (b"cache-control", b""), # noqa + (b"content-disposition", b""), # noqa + (b"content-encoding", b""), # noqa + (b"content-language", b""), # noqa + (b"content-length", b""), # noqa + (b"content-location", b""), # noqa + (b"content-range", b""), # noqa + (b"content-type", b""), # noqa + (b"cookie", b""), # noqa + (b"date", b""), # noqa + (b"etag", b""), # noqa + (b"expect", b""), # noqa + (b"expires", b""), # noqa + (b"from", b""), # noqa + (b"host", b""), # noqa + (b"if-match", b""), # noqa + (b"if-modified-since", b""), # noqa + (b"if-none-match", b""), # noqa + (b"if-range", b""), # noqa + (b"if-unmodified-since", b""), # noqa + (b"last-modified", b""), # noqa + (b"link", b""), # noqa + (b"location", b""), # noqa + (b"max-forwards", b""), # noqa + (b"proxy-authenticate", b""), # noqa + (b"proxy-authorization", b""), # noqa + (b"range", b""), # noqa + (b"referer", b""), # noqa + (b"refresh", b""), # noqa + (b"retry-after", b""), # noqa + (b"server", b""), # noqa + (b"set-cookie", b""), # noqa + (b"strict-transport-security", b""), # noqa + (b"transfer-encoding", b""), # noqa + (b"user-agent", b""), # noqa + (b"vary", b""), # noqa + (b"via", b""), # noqa + (b"www-authenticate", b""), # noqa + ) # noqa + + STATIC_TABLE_LENGTH = len(STATIC_TABLE) + + def __init__(self): + self._maxsize = HeaderTable.DEFAULT_SIZE + self._current_size = 0 + self.resized = False + self.dynamic_entries = deque() + + def get_by_index(self, index): + """ + Returns the entry specified by index + + Note that the table is 1-based ie an index of 0 is + invalid. This is due to the fact that a zero value + index signals that a completely unindexed header + follows. + + The entry will either be from the static table or + the dynamic table depending on the value of index. + """ + original_index = index + index -= 1 + if 0 <= index: + if index < HeaderTable.STATIC_TABLE_LENGTH: + return HeaderTable.STATIC_TABLE[index] + + index -= HeaderTable.STATIC_TABLE_LENGTH + if index < len(self.dynamic_entries): + return self.dynamic_entries[index] + + raise InvalidTableIndex("Invalid table index %d" % original_index) + + def __repr__(self): + return "HeaderTable(%d, %s, %r)" % ( + self._maxsize, + self.resized, + self.dynamic_entries, + ) + + def add(self, name, value): + """ + Adds a new entry to the table + + We reduce the table size if the entry will make the + table size greater than maxsize. + """ + # We just clear the table if the entry is too big + size = table_entry_size(name, value) + if size > self._maxsize: + self.dynamic_entries.clear() + self._current_size = 0 + else: + # Add new entry + self.dynamic_entries.appendleft((name, value)) + self._current_size += size + self._shrink() + + def search(self, name, value): + """ + Searches the table for the entry specified by name + and value + + Returns one of the following: + - ``None``, no match at all + - ``(index, name, None)`` for partial matches on name only. + - ``(index, name, value)`` for perfect matches. + """ + partial = None + + header_name_search_result = HeaderTable.STATIC_TABLE_MAPPING.get(name) + if header_name_search_result: + index = header_name_search_result[1].get(value) + if index is not None: + return index, name, value + else: + partial = (header_name_search_result[0], name, None) + + offset = HeaderTable.STATIC_TABLE_LENGTH + 1 + for i, (n, v) in enumerate(self.dynamic_entries): + if n == name: + if v == value: + return i + offset, n, v + elif partial is None: + partial = (i + offset, n, None) + return partial + + @property + def maxsize(self): + return self._maxsize + + @maxsize.setter + def maxsize(self, newmax): + newmax = int(newmax) + log.debug("Resizing header table to %d from %d", newmax, self._maxsize) + oldmax = self._maxsize + self._maxsize = newmax + self.resized = newmax != oldmax + if newmax <= 0: + self.dynamic_entries.clear() + self._current_size = 0 + elif oldmax > newmax: + self._shrink() + + def _shrink(self): + """ + Shrinks the dynamic table to be at or below maxsize + """ + cursize = self._current_size + while cursize > self._maxsize: + name, value = self.dynamic_entries.pop() + cursize -= table_entry_size(name, value) + log.debug("Evicting %s: %s from the header table", name, value) + self._current_size = cursize + + +def _build_static_table_mapping(): + """ + Build static table mapping from header name to tuple with next structure: + (, ). + + static_table_mapping used for hash searching. + """ + static_table_mapping = {} + for index, (name, value) in enumerate(HeaderTable.STATIC_TABLE, 1): + header_name_search_result = static_table_mapping.setdefault(name, (index, {})) + header_name_search_result[1][value] = index + return static_table_mapping + + +HeaderTable.STATIC_TABLE_MAPPING = _build_static_table_mapping() diff --git a/.venv/lib/python3.9/site-packages/jh2/hyperframe/__init__.py b/.venv/lib/python3.9/site-packages/jh2/hyperframe/__init__.py new file mode 100644 index 0000000..a8c078f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/hyperframe/__init__.py @@ -0,0 +1,6 @@ +""" +hyperframe +~~~~~~~~~~ + +A module for providing a pure-Python HTTP/2 framing layer. +""" diff --git a/.venv/lib/python3.9/site-packages/jh2/hyperframe/exceptions.py b/.venv/lib/python3.9/site-packages/jh2/hyperframe/exceptions.py new file mode 100644 index 0000000..8ef7432 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/hyperframe/exceptions.py @@ -0,0 +1,72 @@ +""" +hyperframe/exceptions +~~~~~~~~~~~~~~~~~~~~~ + +Defines the exceptions that can be thrown by hyperframe. +""" + +from __future__ import annotations + + +class HyperframeError(Exception): + """ + The base class for all exceptions for the hyperframe module. + + .. versionadded:: 6.0.0 + """ + + +class UnknownFrameError(HyperframeError): + """ + A frame of unknown type was received. + + .. versionchanged:: 6.0.0 + Changed base class from `ValueError` to :class:`HyperframeError` + """ + + def __init__(self, frame_type: int, length: int) -> None: + #: The type byte of the unknown frame that was received. + self.frame_type = frame_type + + #: The length of the data portion of the unknown frame. + self.length = length + + def __str__(self) -> str: + return ( + "UnknownFrameError: Unknown frame type 0x%X received, " + "length %d bytes" % (self.frame_type, self.length) + ) + + +class InvalidPaddingError(HyperframeError): + """ + A frame with invalid padding was received. + + .. versionchanged:: 6.0.0 + Changed base class from `ValueError` to :class:`HyperframeError` + """ + + pass + + +class InvalidFrameError(HyperframeError): + """ + Parsing a frame failed because the data was not laid out appropriately. + + .. versionadded:: 3.0.2 + + .. versionchanged:: 6.0.0 + Changed base class from `ValueError` to :class:`HyperframeError` + """ + + pass + + +class InvalidDataError(HyperframeError): + """ + Content or data of a frame was is invalid or violates the specification. + + .. versionadded:: 6.0.0 + """ + + pass diff --git a/.venv/lib/python3.9/site-packages/jh2/hyperframe/flags.py b/.venv/lib/python3.9/site-packages/jh2/hyperframe/flags.py new file mode 100644 index 0000000..0b2897c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/hyperframe/flags.py @@ -0,0 +1,54 @@ +""" +hyperframe/flags +~~~~~~~~~~~~~~~~ + +Defines basic Flag and Flags data structures. +""" + +from __future__ import annotations + +from collections.abc import MutableSet +from typing import Iterable, Iterator, NamedTuple + + +class Flag(NamedTuple): + name: str + bit: int + + +class Flags(MutableSet): # type: ignore + """ + A simple MutableSet implementation that will only accept known flags as + elements. + + Will behave like a regular set(), except that a ValueError will be thrown + when .add()ing unexpected flags. + """ + + def __init__(self, defined_flags: Iterable[Flag]): + self._valid_flags = {flag.name for flag in defined_flags} + self._flags: set[str] = set() + + def __repr__(self) -> str: + return repr(sorted(list(self._flags))) + + def __contains__(self, x: object) -> bool: + return self._flags.__contains__(x) + + def __iter__(self) -> Iterator[str]: + return self._flags.__iter__() + + def __len__(self) -> int: + return self._flags.__len__() + + def discard(self, value: str) -> None: + return self._flags.discard(value) + + def add(self, value: str) -> None: + if value not in self._valid_flags: + raise ValueError( + "Unexpected flag: {}. Valid flags are: {}".format( + value, self._valid_flags + ) + ) + return self._flags.add(value) diff --git a/.venv/lib/python3.9/site-packages/jh2/hyperframe/frame.py b/.venv/lib/python3.9/site-packages/jh2/hyperframe/frame.py new file mode 100644 index 0000000..627dfad --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/hyperframe/frame.py @@ -0,0 +1,1000 @@ +""" +hyperframe/frame +~~~~~~~~~~~~~~~~ + +Defines framing logic for HTTP/2. Provides both classes to represent framed +data and logic for aiding the connection when it comes to reading from the +socket. +""" + +from __future__ import annotations + +import binascii +import struct +from typing import Any, Iterable + +from .exceptions import ( + InvalidDataError, + InvalidFrameError, + InvalidPaddingError, + UnknownFrameError, +) +from .flags import Flag, Flags + +# The maximum initial length of a frame. Some frames have shorter maximum +# lengths. +FRAME_MAX_LEN = 2**14 + +# The maximum allowed length of a frame. +FRAME_MAX_ALLOWED_LEN = (2**24) - 1 + +# Stream association enumerations. +_STREAM_ASSOC_HAS_STREAM = "has-stream" +_STREAM_ASSOC_NO_STREAM = "no-stream" +_STREAM_ASSOC_EITHER = "either" + +# B -> unsigned char (1) +# H -> unsigned short (2) +# L -> unsigned long (4) + +# Structs for packing and unpacking +_STRUCT_HBBBL = struct.Struct(">HBBBL") +_STRUCT_LL = struct.Struct(">LL") +_STRUCT_HL = struct.Struct(">HL") +_STRUCT_LB = struct.Struct(">LB") +_STRUCT_L = struct.Struct(">L") +_STRUCT_H = struct.Struct(">H") +_STRUCT_B = struct.Struct(">B") + + +class Frame: + """ + The base class for all HTTP/2 frames. + """ + + #: The flags defined on this type of frame. + defined_flags: list[Flag] = [] + + #: The byte used to define the type of the frame. + type: int | None = None + + # If 'has-stream', the frame's stream_id must be non-zero. If 'no-stream', + # it must be zero. If 'either', it's not checked. + stream_association: str | None = None + + def __init__(self, stream_id: int, flags: Iterable[str] = ()) -> None: + #: The stream identifier for the stream this frame was received on. + #: Set to 0 for frames sent on the connection (stream-id 0). + self.stream_id = stream_id + + #: The flags set for this frame. + self.flags = Flags(self.defined_flags) + + #: The frame length, excluding the nine-byte header. + self.body_len = 0 + + for flag in flags: + self.flags.add(flag) + + if not self.stream_id and self.stream_association == _STREAM_ASSOC_HAS_STREAM: + raise InvalidDataError( + "Stream ID must be non-zero for {}".format( + type(self).__name__, + ) + ) + if self.stream_id and self.stream_association == _STREAM_ASSOC_NO_STREAM: + raise InvalidDataError( + "Stream ID must be zero for {} with stream_id={}".format( + type(self).__name__, + self.stream_id, + ) + ) + + def __repr__(self) -> str: + return ("{}(stream_id={}, flags={}): {}").format( + type(self).__name__, + self.stream_id, + repr(self.flags), + self._body_repr(), + ) + + def _body_repr(self) -> str: + # More specific implementation may be provided by subclasses of Frame. + # This fallback shows the serialized (and truncated) body content. + return _raw_data_repr(self.serialize_body()) + + @staticmethod + def explain(data: memoryview) -> tuple[Frame, int]: + """ + Takes a bytestring and tries to parse a single frame and print it. + + This function is only provided for debugging purposes. + + :param data: A memoryview object containing the raw data of at least + one complete frame (header and body). + + .. versionadded:: 6.0.0 + """ + frame, length = Frame.parse_frame_header(data[:9]) + frame.parse_body(data[9 : 9 + length]) + print(frame) + return frame, length + + @staticmethod + def parse_frame_header( + header: memoryview, strict: bool = False + ) -> tuple[Frame, int]: + """ + Takes a 9-byte frame header and returns a tuple of the appropriate + Frame object and the length that needs to be read from the socket. + + This populates the flags field, and determines how long the body is. + + :param header: A memoryview object containing the 9-byte frame header + data of a frame. Must not contain more or less. + + :param strict: Whether to raise an exception when encountering a frame + not defined by spec and implemented by hyperframe. + + :raises hyperframe.exceptions.UnknownFrameError: If a frame of unknown + type is received. + + .. versionchanged:: 5.0.0 + Added ``strict`` parameter to accommodate :class:`ExtensionFrame` + """ + try: + fields = _STRUCT_HBBBL.unpack(header) + except struct.error: + raise InvalidFrameError("Invalid frame header") + + # First 24 bits are frame length. + length = (fields[0] << 8) + fields[1] + type = fields[2] + flags = fields[3] + stream_id = fields[4] & 0x7FFFFFFF + + try: + frame = FRAMES[type](stream_id) + except KeyError: + if strict: + raise UnknownFrameError(type, length) + frame = ExtensionFrame(type=type, stream_id=stream_id) + + frame.parse_flags(flags) + return (frame, length) + + def parse_flags(self, flag_byte: int) -> Flags: + for flag, flag_bit in self.defined_flags: + if flag_byte & flag_bit: + self.flags.add(flag) + + return self.flags + + def serialize(self) -> bytes: + """ + Convert a frame into a bytestring, representing the serialized form of + the frame. + """ + body = self.serialize_body() + self.body_len = len(body) + + # Build the common frame header. + # First, get the flags. + flags = 0 + + for flag, flag_bit in self.defined_flags: + if flag in self.flags: + flags |= flag_bit + + header = _STRUCT_HBBBL.pack( + (self.body_len >> 8) & 0xFFFF, # Length spread over top 24 bits + self.body_len & 0xFF, + self.type, + flags, + self.stream_id & 0x7FFFFFFF, # Stream ID is 32 bits. + ) + + return header + body + + def serialize_body(self) -> bytes: + raise NotImplementedError() + + def parse_body(self, data: memoryview) -> None: + """ + Given the body of a frame, parses it into frame data. This populates + the non-header parts of the frame: that is, it does not populate the + stream ID or flags. + + :param data: A memoryview object containing the body data of the frame. + Must not contain *more* data than the length returned by + :meth:`parse_frame_header + `. + """ + raise NotImplementedError() + + +class Padding: + """ + Mixin for frames that contain padding. Defines extra fields that can be + used and set by frames that can be padded. + """ + + def __init__(self, stream_id: int, pad_length: int = 0, **kwargs: Any) -> None: + super().__init__(stream_id, **kwargs) # type: ignore + + #: The length of the padding to use. + self.pad_length = pad_length + + def serialize_padding_data(self) -> bytes: + if "PADDED" in self.flags: # type: ignore + return _STRUCT_B.pack(self.pad_length) + return b"" + + def parse_padding_data(self, data: memoryview) -> int: + if "PADDED" in self.flags: # type: ignore + try: + self.pad_length = struct.unpack("!B", data[:1])[0] + except struct.error: + raise InvalidFrameError("Invalid Padding data") + return 1 + return 0 + + #: .. deprecated:: 5.2.1 + #: Use self.pad_length instead. + @property + def total_padding(self) -> int: # pragma: no cover + import warnings + + warnings.warn( + "total_padding contains the same information as pad_length.", + DeprecationWarning, + ) + return self.pad_length + + +class Priority: + """ + Mixin for frames that contain priority data. Defines extra fields that can + be used and set by frames that contain priority data. + """ + + def __init__( + self, + stream_id: int, + depends_on: int = 0x0, + stream_weight: int = 0x0, + exclusive: bool = False, + **kwargs: Any, + ) -> None: + super().__init__(stream_id, **kwargs) # type: ignore + + #: The stream ID of the stream on which this stream depends. + self.depends_on = depends_on + + #: The weight of the stream. This is an integer between 0 and 256. + self.stream_weight = stream_weight + + #: Whether the exclusive bit was set. + self.exclusive = exclusive + + def serialize_priority_data(self) -> bytes: + return _STRUCT_LB.pack( + self.depends_on + (0x80000000 if self.exclusive else 0), self.stream_weight + ) + + def parse_priority_data(self, data: memoryview) -> int: + try: + self.depends_on, self.stream_weight = _STRUCT_LB.unpack(data[:5]) + except struct.error: + raise InvalidFrameError("Invalid Priority data") + + self.exclusive = True if self.depends_on >> 31 else False + self.depends_on &= 0x7FFFFFFF + return 5 + + +class DataFrame(Padding, Frame): + """ + DATA frames convey arbitrary, variable-length sequences of octets + associated with a stream. One or more DATA frames are used, for instance, + to carry HTTP request or response payloads. + """ + + #: The flags defined for DATA frames. + defined_flags = [ + Flag("END_STREAM", 0x01), + Flag("PADDED", 0x08), + ] + + #: The type byte for data frames. + type = 0x0 + + stream_association = _STREAM_ASSOC_HAS_STREAM + + def __init__(self, stream_id: int, data: bytes = b"", **kwargs: Any) -> None: + super().__init__(stream_id, **kwargs) + + #: The data contained on this frame. + self.data = data + + def serialize_body(self) -> bytes: + padding_data = self.serialize_padding_data() + padding = b"\0" * self.pad_length + if isinstance(self.data, memoryview): + self.data = self.data.tobytes() + return b"".join([padding_data, self.data, padding]) + + def parse_body(self, data: memoryview) -> None: + padding_data_length = self.parse_padding_data(data) + self.data = data[padding_data_length : len(data) - self.pad_length].tobytes() + self.body_len = len(data) + + if self.pad_length and self.pad_length >= self.body_len: + raise InvalidPaddingError("Padding is too long.") + + @property + def flow_controlled_length(self) -> int: + """ + The length of the frame that needs to be accounted for when considering + flow control. + """ + padding_len = 0 + if "PADDED" in self.flags: + # Account for extra 1-byte padding length field, which is still + # present if possibly zero-valued. + padding_len = self.pad_length + 1 + return len(self.data) + padding_len + + +class PriorityFrame(Priority, Frame): + """ + The PRIORITY frame specifies the sender-advised priority of a stream. It + can be sent at any time for an existing stream. This enables + reprioritisation of existing streams. + """ + + #: The flags defined for PRIORITY frames. + defined_flags: list[Flag] = [] + + #: The type byte defined for PRIORITY frames. + type = 0x02 + + stream_association = _STREAM_ASSOC_HAS_STREAM + + def _body_repr(self) -> str: + return "exclusive={}, depends_on={}, stream_weight={}".format( + self.exclusive, self.depends_on, self.stream_weight + ) + + def serialize_body(self) -> bytes: + return self.serialize_priority_data() + + def parse_body(self, data: memoryview) -> None: + if len(data) > 5: + raise InvalidFrameError( + "PRIORITY must have 5 byte body: actual length %s." % len(data) + ) + + self.parse_priority_data(data) + self.body_len = 5 + + +class RstStreamFrame(Frame): + """ + The RST_STREAM frame allows for abnormal termination of a stream. When sent + by the initiator of a stream, it indicates that they wish to cancel the + stream or that an error condition has occurred. When sent by the receiver + of a stream, it indicates that either the receiver is rejecting the stream, + requesting that the stream be cancelled or that an error condition has + occurred. + """ + + #: The flags defined for RST_STREAM frames. + defined_flags: list[Flag] = [] + + #: The type byte defined for RST_STREAM frames. + type = 0x03 + + stream_association = _STREAM_ASSOC_HAS_STREAM + + def __init__(self, stream_id: int, error_code: int = 0, **kwargs: Any) -> None: + super().__init__(stream_id, **kwargs) + + #: The error code used when resetting the stream. + self.error_code = error_code + + def _body_repr(self) -> str: + return "error_code={}".format( + self.error_code, + ) + + def serialize_body(self) -> bytes: + return _STRUCT_L.pack(self.error_code) + + def parse_body(self, data: memoryview) -> None: + if len(data) != 4: + raise InvalidFrameError( + "RST_STREAM must have 4 byte body: actual length %s." % len(data) + ) + + try: + self.error_code = _STRUCT_L.unpack(data)[0] + except struct.error: # pragma: no cover + raise InvalidFrameError("Invalid RST_STREAM body") + + self.body_len = 4 + + +class SettingsFrame(Frame): + """ + The SETTINGS frame conveys configuration parameters that affect how + endpoints communicate. The parameters are either constraints on peer + behavior or preferences. + + Settings are not negotiated. Settings describe characteristics of the + sending peer, which are used by the receiving peer. Different values for + the same setting can be advertised by each peer. For example, a client + might set a high initial flow control window, whereas a server might set a + lower value to conserve resources. + """ + + #: The flags defined for SETTINGS frames. + defined_flags = [Flag("ACK", 0x01)] + + #: The type byte defined for SETTINGS frames. + type = 0x04 + + stream_association = _STREAM_ASSOC_NO_STREAM + + # We need to define the known settings, they may as well be class + # attributes. + #: The byte that signals the SETTINGS_HEADER_TABLE_SIZE setting. + HEADER_TABLE_SIZE = 0x01 + #: The byte that signals the SETTINGS_ENABLE_PUSH setting. + ENABLE_PUSH = 0x02 + #: The byte that signals the SETTINGS_MAX_CONCURRENT_STREAMS setting. + MAX_CONCURRENT_STREAMS = 0x03 + #: The byte that signals the SETTINGS_INITIAL_WINDOW_SIZE setting. + INITIAL_WINDOW_SIZE = 0x04 + #: The byte that signals the SETTINGS_MAX_FRAME_SIZE setting. + MAX_FRAME_SIZE = 0x05 + #: The byte that signals the SETTINGS_MAX_HEADER_LIST_SIZE setting. + MAX_HEADER_LIST_SIZE = 0x06 + #: The byte that signals SETTINGS_ENABLE_CONNECT_PROTOCOL setting. + ENABLE_CONNECT_PROTOCOL = 0x08 + + def __init__( + self, + stream_id: int = 0, + settings: dict[int, int] | None = None, + **kwargs: Any, + ) -> None: + super().__init__(stream_id, **kwargs) + + if settings and "ACK" in kwargs.get("flags", ()): + raise InvalidDataError("Settings must be empty if ACK flag is set.") + + #: A dictionary of the setting type byte to the value of the setting. + self.settings = settings or {} + + def _body_repr(self) -> str: + return "settings={}".format( + self.settings, + ) + + def serialize_body(self) -> bytes: + return b"".join( + [ + _STRUCT_HL.pack(setting & 0xFF, value) + for setting, value in self.settings.items() + ] + ) + + def parse_body(self, data: memoryview) -> None: + if "ACK" in self.flags and len(data) > 0: + raise InvalidDataError( + "SETTINGS ack frame must not have payload: got %s bytes" % len(data) + ) + + body_len = 0 + for i in range(0, len(data), 6): + try: + name, value = _STRUCT_HL.unpack(data[i : i + 6]) + except struct.error: + raise InvalidFrameError("Invalid SETTINGS body") + + self.settings[name] = value + body_len += 6 + + self.body_len = body_len + + +class PushPromiseFrame(Padding, Frame): + """ + The PUSH_PROMISE frame is used to notify the peer endpoint in advance of + streams the sender intends to initiate. + """ + + #: The flags defined for PUSH_PROMISE frames. + defined_flags = [Flag("END_HEADERS", 0x04), Flag("PADDED", 0x08)] + + #: The type byte defined for PUSH_PROMISE frames. + type = 0x05 + + stream_association = _STREAM_ASSOC_HAS_STREAM + + def __init__( + self, + stream_id: int, + promised_stream_id: int = 0, + data: bytes = b"", + **kwargs: Any, + ) -> None: + super().__init__(stream_id, **kwargs) + + #: The stream ID that is promised by this frame. + self.promised_stream_id = promised_stream_id + + #: The HPACK-encoded header block for the simulated request on the new + #: stream. + self.data = data + + def _body_repr(self) -> str: + return "promised_stream_id={}, data={}".format( + self.promised_stream_id, + _raw_data_repr(self.data), + ) + + def serialize_body(self) -> bytes: + padding_data = self.serialize_padding_data() + padding = b"\0" * self.pad_length + data = _STRUCT_L.pack(self.promised_stream_id) + return b"".join([padding_data, data, self.data, padding]) + + def parse_body(self, data: memoryview) -> None: + padding_data_length = self.parse_padding_data(data) + + try: + self.promised_stream_id = _STRUCT_L.unpack( + data[padding_data_length : padding_data_length + 4] + )[0] + except struct.error: + raise InvalidFrameError("Invalid PUSH_PROMISE body") + + self.data = data[ + padding_data_length + 4 : len(data) - self.pad_length + ].tobytes() + self.body_len = len(data) + + if self.promised_stream_id == 0 or self.promised_stream_id % 2 != 0: + raise InvalidDataError( + "Invalid PUSH_PROMISE promised stream id: %s" % self.promised_stream_id + ) + + if self.pad_length and self.pad_length >= self.body_len: + raise InvalidPaddingError("Padding is too long.") + + +class PingFrame(Frame): + """ + The PING frame is a mechanism for measuring a minimal round-trip time from + the sender, as well as determining whether an idle connection is still + functional. PING frames can be sent from any endpoint. + """ + + #: The flags defined for PING frames. + defined_flags = [Flag("ACK", 0x01)] + + #: The type byte defined for PING frames. + type = 0x06 + + stream_association = _STREAM_ASSOC_NO_STREAM + + def __init__( + self, stream_id: int = 0, opaque_data: bytes = b"", **kwargs: Any + ) -> None: + super().__init__(stream_id, **kwargs) + + #: The opaque data sent in this PING frame, as a bytestring. + self.opaque_data = opaque_data + + def _body_repr(self) -> str: + return "opaque_data={!r}".format( + self.opaque_data, + ) + + def serialize_body(self) -> bytes: + if len(self.opaque_data) > 8: + raise InvalidFrameError( + "PING frame may not have more than 8 bytes of data, got %r" + % self.opaque_data + ) + + data = self.opaque_data + data += b"\x00" * (8 - len(self.opaque_data)) + return data + + def parse_body(self, data: memoryview) -> None: + if len(data) != 8: + raise InvalidFrameError( + "PING frame must have 8 byte length: got %s" % len(data) + ) + + self.opaque_data = data.tobytes() + self.body_len = 8 + + +class GoAwayFrame(Frame): + """ + The GOAWAY frame informs the remote peer to stop creating streams on this + connection. It can be sent from the client or the server. Once sent, the + sender will ignore frames sent on new streams for the remainder of the + connection. + """ + + #: The flags defined for GOAWAY frames. + defined_flags: list[Flag] = [] + + #: The type byte defined for GOAWAY frames. + type = 0x07 + + stream_association = _STREAM_ASSOC_NO_STREAM + + def __init__( + self, + stream_id: int = 0, + last_stream_id: int = 0, + error_code: int = 0, + additional_data: bytes = b"", + **kwargs: Any, + ) -> None: + super().__init__(stream_id, **kwargs) + + #: The last stream ID definitely seen by the remote peer. + self.last_stream_id = last_stream_id + + #: The error code for connection teardown. + self.error_code = error_code + + #: Any additional data sent in the GOAWAY. + self.additional_data = additional_data + + def _body_repr(self) -> str: + return "last_stream_id={}, error_code={}, additional_data={!r}".format( + self.last_stream_id, + self.error_code, + self.additional_data, + ) + + def serialize_body(self) -> bytes: + data = _STRUCT_LL.pack(self.last_stream_id & 0x7FFFFFFF, self.error_code) + data += self.additional_data + + return data + + def parse_body(self, data: memoryview) -> None: + try: + self.last_stream_id, self.error_code = _STRUCT_LL.unpack(data[:8]) + except struct.error: + raise InvalidFrameError("Invalid GOAWAY body.") + + self.body_len = len(data) + + if len(data) > 8: + self.additional_data = data[8:].tobytes() + + +class WindowUpdateFrame(Frame): + """ + The WINDOW_UPDATE frame is used to implement flow control. + + Flow control operates at two levels: on each individual stream and on the + entire connection. + + Both types of flow control are hop by hop; that is, only between the two + endpoints. Intermediaries do not forward WINDOW_UPDATE frames between + dependent connections. However, throttling of data transfer by any receiver + can indirectly cause the propagation of flow control information toward the + original sender. + """ + + #: The flags defined for WINDOW_UPDATE frames. + defined_flags: list[Flag] = [] + + #: The type byte defined for WINDOW_UPDATE frames. + type = 0x08 + + stream_association = _STREAM_ASSOC_EITHER + + def __init__( + self, stream_id: int, window_increment: int = 0, **kwargs: Any + ) -> None: + super().__init__(stream_id, **kwargs) + + #: The amount the flow control window is to be incremented. + self.window_increment = window_increment + + def _body_repr(self) -> str: + return "window_increment={}".format( + self.window_increment, + ) + + def serialize_body(self) -> bytes: + return _STRUCT_L.pack(self.window_increment & 0x7FFFFFFF) + + def parse_body(self, data: memoryview) -> None: + if len(data) > 4: + raise InvalidFrameError( + "WINDOW_UPDATE frame must have 4 byte length: got %s" % len(data) + ) + + try: + self.window_increment = _STRUCT_L.unpack(data)[0] + except struct.error: + raise InvalidFrameError("Invalid WINDOW_UPDATE body") + + if not 1 <= self.window_increment <= 2**31 - 1: + raise InvalidDataError( + "WINDOW_UPDATE increment must be between 1 to 2^31-1" + ) + + self.body_len = 4 + + +class HeadersFrame(Padding, Priority, Frame): + """ + The HEADERS frame carries name-value pairs. It is used to open a stream. + HEADERS frames can be sent on a stream in the "open" or "half closed + (remote)" states. + + The HeadersFrame class is actually basically a data frame in this + implementation, because of the requirement to control the sizes of frames. + A header block fragment that doesn't fit in an entire HEADERS frame needs + to be followed with CONTINUATION frames. From the perspective of the frame + building code the header block is an opaque data segment. + """ + + #: The flags defined for HEADERS frames. + defined_flags = [ + Flag("END_STREAM", 0x01), + Flag("END_HEADERS", 0x04), + Flag("PADDED", 0x08), + Flag("PRIORITY", 0x20), + ] + + #: The type byte defined for HEADERS frames. + type = 0x01 + + stream_association = _STREAM_ASSOC_HAS_STREAM + + def __init__(self, stream_id: int, data: bytes = b"", **kwargs: Any) -> None: + super().__init__(stream_id, **kwargs) + + #: The HPACK-encoded header block. + self.data = data + + def _body_repr(self) -> str: + return "exclusive={}, depends_on={}, stream_weight={}, data={}".format( + self.exclusive, + self.depends_on, + self.stream_weight, + _raw_data_repr(self.data), + ) + + def serialize_body(self) -> bytes: + padding_data = self.serialize_padding_data() + padding = b"\0" * self.pad_length + + if "PRIORITY" in self.flags: + priority_data = self.serialize_priority_data() + else: + priority_data = b"" + + return b"".join([padding_data, priority_data, self.data, padding]) + + def parse_body(self, data: memoryview) -> None: + padding_data_length = self.parse_padding_data(data) + data = data[padding_data_length:] + + if "PRIORITY" in self.flags: + priority_data_length = self.parse_priority_data(data) + else: + priority_data_length = 0 + + self.body_len = len(data) + self.data = data[priority_data_length : len(data) - self.pad_length].tobytes() + + if self.pad_length and self.pad_length >= self.body_len: + raise InvalidPaddingError("Padding is too long.") + + +class ContinuationFrame(Frame): + """ + The CONTINUATION frame is used to continue a sequence of header block + fragments. Any number of CONTINUATION frames can be sent on an existing + stream, as long as the preceding frame on the same stream is one of + HEADERS, PUSH_PROMISE or CONTINUATION without the END_HEADERS flag set. + + Much like the HEADERS frame, hyper treats this as an opaque data frame with + different flags and a different type. + """ + + #: The flags defined for CONTINUATION frames. + defined_flags = [Flag("END_HEADERS", 0x04)] + + #: The type byte defined for CONTINUATION frames. + type = 0x09 + + stream_association = _STREAM_ASSOC_HAS_STREAM + + def __init__(self, stream_id: int, data: bytes = b"", **kwargs: Any) -> None: + super().__init__(stream_id, **kwargs) + + #: The HPACK-encoded header block. + self.data = data + + def _body_repr(self) -> str: + return "data={}".format( + _raw_data_repr(self.data), + ) + + def serialize_body(self) -> bytes: + return self.data + + def parse_body(self, data: memoryview) -> None: + self.data = data.tobytes() + self.body_len = len(data) + + +class AltSvcFrame(Frame): + """ + The ALTSVC frame is used to advertise alternate services that the current + host, or a different one, can understand. This frame is standardised as + part of RFC 7838. + + This frame does no work to validate that the ALTSVC field parameter is + acceptable per the rules of RFC 7838. + + .. note:: If the ``stream_id`` of this frame is nonzero, the origin field + must have zero length. Conversely, if the ``stream_id`` of this + frame is zero, the origin field must have nonzero length. Put + another way, a valid ALTSVC frame has ``stream_id != 0`` XOR + ``len(origin) != 0``. + """ + + type = 0xA + + stream_association = _STREAM_ASSOC_EITHER + + def __init__( + self, stream_id: int, origin: bytes = b"", field: bytes = b"", **kwargs: Any + ) -> None: + super().__init__(stream_id, **kwargs) + + if not isinstance(origin, bytes): + raise InvalidDataError("AltSvc origin must be bytestring.") + if not isinstance(field, bytes): + raise InvalidDataError("AltSvc field must be a bytestring.") + self.origin = origin + self.field = field + + def _body_repr(self) -> str: + return "origin={!r}, field={!r}".format( + self.origin, + self.field, + ) + + def serialize_body(self) -> bytes: + origin_len = _STRUCT_H.pack(len(self.origin)) + return b"".join([origin_len, self.origin, self.field]) + + def parse_body(self, data: memoryview) -> None: + try: + origin_len = _STRUCT_H.unpack(data[0:2])[0] + self.origin = data[2 : 2 + origin_len].tobytes() + + if len(self.origin) != origin_len: + raise InvalidFrameError("Invalid ALTSVC frame body.") + + self.field = data[2 + origin_len :].tobytes() + except (struct.error, ValueError): + raise InvalidFrameError("Invalid ALTSVC frame body.") + + self.body_len = len(data) + + +class ExtensionFrame(Frame): + """ + ExtensionFrame is used to wrap frames which are not natively interpretable + by hyperframe. + + Although certain byte prefixes are ordained by specification to have + certain contextual meanings, frames with other prefixes are not prohibited, + and may be used to communicate arbitrary meaning between HTTP/2 peers. + + Thus, hyperframe, rather than raising an exception when such a frame is + encountered, wraps it in a generic frame to be properly acted upon by + upstream consumers which might have additional context on how to use it. + + .. versionadded:: 5.0.0 + """ + + stream_association = _STREAM_ASSOC_EITHER + + def __init__( + self, + type: int, + stream_id: int, + flag_byte: int = 0x0, + body: bytes = b"", + **kwargs: Any, + ) -> None: + super().__init__(stream_id, **kwargs) + self.type = type + self.flag_byte = flag_byte + self.body = body + + def _body_repr(self) -> str: + return "type={}, flag_byte={}, body={}".format( + self.type, + self.flag_byte, + _raw_data_repr(self.body), + ) + + def parse_flags(self, flag_byte: int) -> None: # type: ignore + """ + For extension frames, we parse the flags by just storing a flag byte. + """ + self.flag_byte = flag_byte + + def parse_body(self, data: memoryview) -> None: + self.body = data.tobytes() + self.body_len = len(data) + + def serialize(self) -> bytes: + """ + A broad override of the serialize method that ensures that the data + comes back out exactly as it came in. This should not be used in most + user code: it exists only as a helper method if frames need to be + reconstituted. + """ + # Build the frame header. + # First, get the flags. + flags = self.flag_byte + + header = _STRUCT_HBBBL.pack( + (self.body_len >> 8) & 0xFFFF, # Length spread over top 24 bits + self.body_len & 0xFF, + self.type, + flags, + self.stream_id & 0x7FFFFFFF, # Stream ID is 32 bits. + ) + + return header + self.body + + +def _raw_data_repr(data: bytes | None) -> str: + if not data: + return "None" + r = binascii.hexlify(data).decode("ascii") + if len(r) > 20: + r = r[:20] + "..." + return "" + + +_FRAME_CLASSES: list[type[Frame]] = [ + DataFrame, + HeadersFrame, + PriorityFrame, + RstStreamFrame, + SettingsFrame, + PushPromiseFrame, + PingFrame, + GoAwayFrame, + WindowUpdateFrame, + ContinuationFrame, + AltSvcFrame, +] +#: FRAMES maps the type byte for each frame to the class used to represent that +#: frame. +FRAMES = {cls.type: cls for cls in _FRAME_CLASSES} diff --git a/.venv/lib/python3.9/site-packages/jh2/hyperframe/py.typed b/.venv/lib/python3.9/site-packages/jh2/hyperframe/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.9/site-packages/jh2/settings.py b/.venv/lib/python3.9/site-packages/jh2/settings.py new file mode 100644 index 0000000..a04e939 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/settings.py @@ -0,0 +1,338 @@ +""" +h2/settings +~~~~~~~~~~~ + +This module contains a HTTP/2 settings object. This object provides a simple +API for manipulating HTTP/2 settings, keeping track of both the current active +state of the settings and the unacknowledged future values of the settings. +""" + +from __future__ import annotations + +import collections +import enum +from collections.abc import MutableMapping + +from .errors import ErrorCodes +from .exceptions import InvalidSettingsValueError +from .hyperframe.frame import SettingsFrame + + +class SettingCodes(enum.IntEnum): + """ + All known HTTP/2 setting codes. + + .. versionadded:: 2.6.0 + """ + + #: Allows the sender to inform the remote endpoint of the maximum size of + #: the header compression table used to decode header blocks, in octets. + HEADER_TABLE_SIZE = SettingsFrame.HEADER_TABLE_SIZE + + #: This setting can be used to disable server push. To disable server push + #: on a client, set this to 0. + ENABLE_PUSH = SettingsFrame.ENABLE_PUSH + + #: Indicates the maximum number of concurrent streams that the sender will + #: allow. + MAX_CONCURRENT_STREAMS = SettingsFrame.MAX_CONCURRENT_STREAMS + + #: Indicates the sender's initial window size (in octets) for stream-level + #: flow control. + INITIAL_WINDOW_SIZE = SettingsFrame.INITIAL_WINDOW_SIZE + + #: Indicates the size of the largest frame payload that the sender is + #: willing to receive, in octets. + MAX_FRAME_SIZE = SettingsFrame.MAX_FRAME_SIZE + + #: This advisory setting informs a peer of the maximum size of header list + #: that the sender is prepared to accept, in octets. The value is based on + #: the uncompressed size of header fields, including the length of the name + #: and value in octets plus an overhead of 32 octets for each header field. + MAX_HEADER_LIST_SIZE = SettingsFrame.MAX_HEADER_LIST_SIZE + + #: This setting can be used to enable the connect protocol. To enable on a + #: client set this to 1. + ENABLE_CONNECT_PROTOCOL = SettingsFrame.ENABLE_CONNECT_PROTOCOL + + +def _setting_code_from_int(code): + """ + Given an integer setting code, returns either one of :class:`SettingCodes + ` or, if not present in the known set of codes, + returns the integer directly. + """ + try: + return SettingCodes(code) + except ValueError: + return code + + +class ChangedSetting: + def __init__(self, setting, original_value, new_value): + #: The setting code given. Either one of :class:`SettingCodes + #: ` or ``int`` + #: + #: .. versionchanged:: 2.6.0 + self.setting = setting + + #: The original value before being changed. + self.original_value = original_value + + #: The new value after being changed. + self.new_value = new_value + + def __repr__(self): + return ("ChangedSetting(setting=%s, original_value=%s, new_value=%s)") % ( + self.setting, + self.original_value, + self.new_value, + ) + + +class Settings(MutableMapping): + """ + An object that encapsulates HTTP/2 settings state. + + HTTP/2 Settings are a complex beast. Each party, remote and local, has its + own settings and a view of the other party's settings. When a settings + frame is emitted by a peer it cannot assume that the new settings values + are in place until the remote peer acknowledges the setting. In principle, + multiple settings changes can be "in flight" at the same time, all with + different values. + + This object encapsulates this mess. It provides a dict-like interface to + settings, which return the *current* values of the settings in question. + Additionally, it keeps track of the stack of proposed values: each time an + acknowledgement is sent/received, it updates the current values with the + stack of proposed values. On top of all that, it validates the values to + make sure they're allowed, and raises :class:`InvalidSettingsValueError + ` if they are not. + + Finally, this object understands what the default values of the HTTP/2 + settings are, and sets those defaults appropriately. + + .. versionchanged:: 2.2.0 + Added the ``initial_values`` parameter. + + .. versionchanged:: 2.5.0 + Added the ``max_header_list_size`` property. + + :param client: (optional) Whether these settings should be defaulted for a + client implementation or a server implementation. Defaults to ``True``. + :type client: ``bool`` + :param initial_values: (optional) Any initial values the user would like + set, rather than RFC 7540's defaults. + :type initial_vales: ``MutableMapping`` + """ + + def __init__(self, client=True, initial_values=None): + # Backing object for the settings. This is a dictionary of + # (setting: [list of values]), where the first value in the list is the + # current value of the setting. Strictly this doesn't use lists but + # instead uses collections.deque to avoid repeated memory allocations. + # + # This contains the default values for HTTP/2. + self._settings = { + SettingCodes.HEADER_TABLE_SIZE: collections.deque([4096]), + SettingCodes.ENABLE_PUSH: collections.deque([int(client)]), + SettingCodes.INITIAL_WINDOW_SIZE: collections.deque([65535]), + SettingCodes.MAX_FRAME_SIZE: collections.deque([16384]), + SettingCodes.ENABLE_CONNECT_PROTOCOL: collections.deque([0]), + } + if initial_values is not None: + for key, value in initial_values.items(): + invalid = _validate_setting(key, value) + if invalid: + raise InvalidSettingsValueError( + "Setting %d has invalid value %d" % (key, value), + error_code=invalid, + ) + self._settings[key] = collections.deque([value]) + self._any_update = True + + @property + def has_update(self): + try: + return self._any_update + finally: + self._any_update = False + + def acknowledge(self): + """ + The settings have been acknowledged, either by the user (remote + settings) or by the remote peer (local settings). + + :returns: A dict of {setting: ChangedSetting} that were applied. + """ + changed_settings = {} + + # If there is more than one setting in the list, we have a setting + # value outstanding. Update them. + for k, v in self._settings.items(): + if len(v) > 1: + old_setting = v.popleft() + new_setting = v[0] + changed_settings[k] = ChangedSetting(k, old_setting, new_setting) + + return changed_settings + + # Provide easy-access to well known settings. + @property + def header_table_size(self): + """ + The current value of the :data:`HEADER_TABLE_SIZE + ` setting. + """ + return self[SettingCodes.HEADER_TABLE_SIZE] + + @header_table_size.setter + def header_table_size(self, value): + self[SettingCodes.HEADER_TABLE_SIZE] = value + + @property + def enable_push(self): + """ + The current value of the :data:`ENABLE_PUSH + ` setting. + """ + return self[SettingCodes.ENABLE_PUSH] + + @enable_push.setter + def enable_push(self, value): + self[SettingCodes.ENABLE_PUSH] = value + + @property + def initial_window_size(self): + """ + The current value of the :data:`INITIAL_WINDOW_SIZE + ` setting. + """ + return self[SettingCodes.INITIAL_WINDOW_SIZE] + + @initial_window_size.setter + def initial_window_size(self, value): + self[SettingCodes.INITIAL_WINDOW_SIZE] = value + + @property + def max_frame_size(self): + """ + The current value of the :data:`MAX_FRAME_SIZE + ` setting. + """ + return self[SettingCodes.MAX_FRAME_SIZE] + + @max_frame_size.setter + def max_frame_size(self, value): + self[SettingCodes.MAX_FRAME_SIZE] = value + + @property + def max_concurrent_streams(self): + """ + The current value of the :data:`MAX_CONCURRENT_STREAMS + ` setting. + """ + return self.get(SettingCodes.MAX_CONCURRENT_STREAMS, 2**32 + 1) + + @max_concurrent_streams.setter + def max_concurrent_streams(self, value): + self[SettingCodes.MAX_CONCURRENT_STREAMS] = value + + @property + def max_header_list_size(self): + """ + The current value of the :data:`MAX_HEADER_LIST_SIZE + ` setting. If not set, + returns ``None``, which means unlimited. + + .. versionadded:: 2.5.0 + """ + return self.get(SettingCodes.MAX_HEADER_LIST_SIZE, None) + + @max_header_list_size.setter + def max_header_list_size(self, value): + self[SettingCodes.MAX_HEADER_LIST_SIZE] = value + + @property + def enable_connect_protocol(self): + """ + The current value of the :data:`ENABLE_CONNECT_PROTOCOL + ` setting. + """ + return self[SettingCodes.ENABLE_CONNECT_PROTOCOL] + + @enable_connect_protocol.setter + def enable_connect_protocol(self, value): + self[SettingCodes.ENABLE_CONNECT_PROTOCOL] = value + + # Implement the MutableMapping API. + def __getitem__(self, key): + val = self._settings[key][0] + + # Things that were created when a setting was received should stay + # KeyError'd. + if val is None: + raise KeyError + + return val + + def __setitem__(self, key, value): + invalid = _validate_setting(key, value) + if invalid: + raise InvalidSettingsValueError( + "Setting %d has invalid value %d" % (key, value), error_code=invalid + ) + + try: + items = self._settings[key] + except KeyError: + items = collections.deque([None]) + self._settings[key] = items + + items.append(value) + self._any_update = True + + def __delitem__(self, key): + del self._settings[key] + + def __iter__(self): + return self._settings.__iter__() + + def __len__(self): + return len(self._settings) + + def __eq__(self, other): + if isinstance(other, Settings): + return self._settings == other._settings + else: + return NotImplemented + + def __ne__(self, other): + if isinstance(other, Settings): + return not self == other + else: + return NotImplemented + + +def _validate_setting(setting, value): # noqa: C901 + """ + Confirms that a specific setting has a well-formed value. If the setting is + invalid, returns an error code. Otherwise, returns 0 (NO_ERROR). + """ + if setting == SettingCodes.ENABLE_PUSH: + if value not in (0, 1): + return ErrorCodes.PROTOCOL_ERROR + elif setting == SettingCodes.INITIAL_WINDOW_SIZE: + if not 0 <= value <= 2147483647: # 2^31 - 1 + return ErrorCodes.FLOW_CONTROL_ERROR + elif setting == SettingCodes.MAX_FRAME_SIZE: + if not 16384 <= value <= 16777215: # 2^14 and 2^24 - 1 + return ErrorCodes.PROTOCOL_ERROR + elif setting == SettingCodes.MAX_HEADER_LIST_SIZE: + if value < 0: + return ErrorCodes.PROTOCOL_ERROR + elif setting == SettingCodes.ENABLE_CONNECT_PROTOCOL: + if value not in (0, 1): + return ErrorCodes.PROTOCOL_ERROR + + return 0 diff --git a/.venv/lib/python3.9/site-packages/jh2/stream.py b/.venv/lib/python3.9/site-packages/jh2/stream.py new file mode 100644 index 0000000..4e8f866 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/stream.py @@ -0,0 +1,1474 @@ +""" +h2/stream +~~~~~~~~~ + +An implementation of a HTTP/2 stream. +""" + +from __future__ import annotations + +from enum import Enum, IntEnum + +from .errors import ErrorCodes, _error_code_from_int +from .events import ( + AlternativeServiceAvailable, + DataReceived, + InformationalResponseReceived, + PushedStreamReceived, + RequestReceived, + ResponseReceived, + StreamEnded, + StreamReset, + TrailersReceived, + WindowUpdated, + _PushedRequestSent, + _RequestSent, + _ResponseSent, + _TrailersSent, +) +from .exceptions import ( + FlowControlError, + InvalidBodyLengthError, + ProtocolError, + StreamClosedError, +) +from .hpack import NeverIndexedHeaderTuple + +try: + from ._hazmat import Encoder # noqa: F401 + + ALTERNATIVE_HPACK = True +except ImportError: + ALTERNATIVE_HPACK = False +from .hyperframe.frame import ( + AltSvcFrame, + ContinuationFrame, + DataFrame, + HeadersFrame, + PushPromiseFrame, + RstStreamFrame, + WindowUpdateFrame, +) +from .utilities import ( + HeaderValidationFlags, + authority_from_headers, + extract_method_header, + guard_increment_window, + is_informational_response, + normalize_inbound_headers, + normalize_outbound_headers, + validate_headers, + validate_outbound_headers, +) +from .windows import WindowManager + + +class StreamState(IntEnum): + IDLE = 0 + RESERVED_REMOTE = 1 + RESERVED_LOCAL = 2 + OPEN = 3 + HALF_CLOSED_REMOTE = 4 + HALF_CLOSED_LOCAL = 5 + CLOSED = 6 + + +class StreamInputs(Enum): + SEND_HEADERS = 0 + SEND_PUSH_PROMISE = 1 + SEND_RST_STREAM = 2 + SEND_DATA = 3 + SEND_WINDOW_UPDATE = 4 + SEND_END_STREAM = 5 + RECV_HEADERS = 6 + RECV_PUSH_PROMISE = 7 + RECV_RST_STREAM = 8 + RECV_DATA = 9 + RECV_WINDOW_UPDATE = 10 + RECV_END_STREAM = 11 + RECV_CONTINUATION = 12 # Added in 2.0.0 + SEND_INFORMATIONAL_HEADERS = 13 # Added in 2.2.0 + RECV_INFORMATIONAL_HEADERS = 14 # Added in 2.2.0 + SEND_ALTERNATIVE_SERVICE = 15 # Added in 2.3.0 + RECV_ALTERNATIVE_SERVICE = 16 # Added in 2.3.0 + UPGRADE_CLIENT = 17 # Added 2.3.0 + UPGRADE_SERVER = 18 # Added 2.3.0 + + +class StreamClosedBy(Enum): + SEND_END_STREAM = 0 + RECV_END_STREAM = 1 + SEND_RST_STREAM = 2 + RECV_RST_STREAM = 3 + + +# This array is initialized once, and is indexed by the stream states above. +# It indicates whether a stream in the given state is open. The reason we do +# this is that we potentially check whether a stream in a given state is open +# quite frequently: given that we check so often, we should do so in the +# fastest and most performant way possible. +STREAM_OPEN = [False for _ in range(0, len(StreamState))] +STREAM_OPEN[StreamState.OPEN] = True +STREAM_OPEN[StreamState.HALF_CLOSED_LOCAL] = True +STREAM_OPEN[StreamState.HALF_CLOSED_REMOTE] = True + + +class H2StreamStateMachine: + """ + A single HTTP/2 stream state machine. + + This stream object implements basically the state machine described in + RFC 7540 section 5.1. + + :param stream_id: The stream ID of this stream. This is stored primarily + for logging purposes. + """ + + def __init__(self, stream_id): + self.state = StreamState.IDLE + self.stream_id = stream_id + + #: Whether this peer is the client side of this stream. + self.client = None + + # Whether trailers have been sent/received on this stream or not. + self.headers_sent = None + self.trailers_sent = None + self.headers_received = None + self.trailers_received = None + + # How the stream was closed. One of StreamClosedBy. + self.stream_closed_by = None + + def process_input(self, input_): + """ + Process a specific input in the state machine. + """ + if not isinstance(input_, StreamInputs): + raise ValueError("Input must be an instance of StreamInputs") + + try: + func, target_state = _transitions[(self.state, input_)] + except KeyError: + old_state = self.state + self.state = StreamState.CLOSED + raise ProtocolError(f"Invalid input {input_} in state {old_state}") + else: + previous_state = self.state + self.state = target_state + if func is not None: + try: + return func(self, previous_state) + except ProtocolError: + self.state = StreamState.CLOSED + raise + except AssertionError as e: # pragma: no cover + self.state = StreamState.CLOSED + raise ProtocolError(e) + + return [] + + def request_sent(self, previous_state): + """ + Fires when a request is sent. + """ + self.client = True + self.headers_sent = True + event = _RequestSent() + + return [event] + + def response_sent(self, previous_state): + """ + Fires when something that should be a response is sent. This 'response' + may actually be trailers. + """ + if not self.headers_sent: + if self.client is True or self.client is None: + raise ProtocolError("Client cannot send responses.") + self.headers_sent = True + event = _ResponseSent() + else: + assert not self.trailers_sent + self.trailers_sent = True + event = _TrailersSent() + + return [event] + + def request_received(self, previous_state): + """ + Fires when a request is received. + """ + assert not self.headers_received + assert not self.trailers_received + + self.client = False + self.headers_received = True + event = RequestReceived() + + event.stream_id = self.stream_id + return [event] + + def response_received(self, previous_state): + """ + Fires when a response is received. Also disambiguates between responses + and trailers. + """ + if not self.headers_received: + assert self.client is True + self.headers_received = True + event = ResponseReceived() + else: + assert not self.trailers_received + self.trailers_received = True + event = TrailersReceived() + + event.stream_id = self.stream_id + return [event] + + def data_received(self, previous_state): + """ + Fires when data is received. + """ + if not self.headers_received: + raise ProtocolError("cannot receive data before headers") + event = DataReceived() + event.stream_id = self.stream_id + return [event] + + def window_updated(self, previous_state): + """ + Fires when a window update frame is received. + """ + event = WindowUpdated() + event.stream_id = self.stream_id + return [event] + + def stream_half_closed(self, previous_state): + """ + Fires when an END_STREAM flag is received in the OPEN state, + transitioning this stream to a HALF_CLOSED_REMOTE state. + """ + event = StreamEnded() + event.stream_id = self.stream_id + return [event] + + def stream_ended(self, previous_state): + """ + Fires when a stream is cleanly ended. + """ + self.stream_closed_by = StreamClosedBy.RECV_END_STREAM + event = StreamEnded() + event.stream_id = self.stream_id + return [event] + + def stream_reset(self, previous_state): + """ + Fired when a stream is forcefully reset. + """ + self.stream_closed_by = StreamClosedBy.RECV_RST_STREAM + event = StreamReset() + event.stream_id = self.stream_id + return [event] + + def send_new_pushed_stream(self, previous_state): + """ + Fires on the newly pushed stream, when pushed by the local peer. + + No event here, but definitionally this peer must be a server. + """ + assert self.client is None + self.client = False + self.headers_received = True + return [] + + def recv_new_pushed_stream(self, previous_state): + """ + Fires on the newly pushed stream, when pushed by the remote peer. + + No event here, but definitionally this peer must be a client. + """ + assert self.client is None + self.client = True + self.headers_sent = True + return [] + + def send_push_promise(self, previous_state): + """ + Fires on the already-existing stream when a PUSH_PROMISE frame is sent. + We may only send PUSH_PROMISE frames if we're a server. + """ + if self.client is True: + raise ProtocolError("Cannot push streams from client peers.") + + event = _PushedRequestSent() + return [event] + + def recv_push_promise(self, previous_state): + """ + Fires on the already-existing stream when a PUSH_PROMISE frame is + received. We may only receive PUSH_PROMISE frames if we're a client. + + Fires a PushedStreamReceived event. + """ + if not self.client: + if self.client is None: # pragma: no cover + msg = "Idle streams cannot receive pushes" + else: # pragma: no cover + msg = "Cannot receive pushed streams as a server" + raise ProtocolError(msg) + + event = PushedStreamReceived() + event.parent_stream_id = self.stream_id + return [event] + + def send_end_stream(self, previous_state): + """ + Called when an attempt is made to send END_STREAM in the + HALF_CLOSED_REMOTE state. + """ + self.stream_closed_by = StreamClosedBy.SEND_END_STREAM + + def send_reset_stream(self, previous_state): + """ + Called when an attempt is made to send RST_STREAM in a non-closed + stream state. + """ + self.stream_closed_by = StreamClosedBy.SEND_RST_STREAM + + def reset_stream_on_error(self, previous_state): + """ + Called when we need to forcefully emit another RST_STREAM frame on + behalf of the state machine. + + If this is the first time we've done this, we should also hang an event + off the StreamClosedError so that the user can be informed. We know + it's the first time we've done this if the stream is currently in a + state other than CLOSED. + """ + self.stream_closed_by = StreamClosedBy.SEND_RST_STREAM + + error = StreamClosedError(self.stream_id) + + event = StreamReset() + event.stream_id = self.stream_id + event.error_code = ErrorCodes.STREAM_CLOSED + event.remote_reset = False + error._events = [event] + raise error + + def recv_on_closed_stream(self, previous_state): + """ + Called when an unexpected frame is received on an already-closed + stream. + + An endpoint that receives an unexpected frame should treat it as + a stream error or connection error with type STREAM_CLOSED, depending + on the specific frame. The error handling is done at a higher level: + this just raises the appropriate error. + """ + raise StreamClosedError(self.stream_id) + + def send_on_closed_stream(self, previous_state): + """ + Called when an attempt is made to send data on an already-closed + stream. + + This essentially overrides the standard logic by throwing a + more-specific error: StreamClosedError. This is a ProtocolError, so it + matches the standard API of the state machine, but provides more detail + to the user. + """ + raise StreamClosedError(self.stream_id) + + def recv_push_on_closed_stream(self, previous_state): + """ + Called when a PUSH_PROMISE frame is received on a full stop + stream. + + If the stream was closed by us sending a RST_STREAM frame, then we + presume that the PUSH_PROMISE was in flight when we reset the parent + stream. Rathen than accept the new stream, we just reset it. + Otherwise, we should call this a PROTOCOL_ERROR: pushing a stream on a + naturally closed stream is a real problem because it creates a brand + new stream that the remote peer now believes exists. + """ + assert self.stream_closed_by is not None + + if self.stream_closed_by == StreamClosedBy.SEND_RST_STREAM: + raise StreamClosedError(self.stream_id) + else: + raise ProtocolError("Attempted to push on closed stream.") + + def send_push_on_closed_stream(self, previous_state): + """ + Called when an attempt is made to push on an already-closed stream. + + This essentially overrides the standard logic by providing a more + useful error message. It's necessary because simply indicating that the + stream is closed is not enough: there is now a new stream that is not + allowed to be there. The only recourse is to tear the whole connection + down. + """ + raise ProtocolError("Attempted to push on closed stream.") + + def send_informational_response(self, previous_state): + """ + Called when an informational header block is sent (that is, a block + where the :status header has a 1XX value). + + Only enforces that these are sent *before* final headers are sent. + """ + if self.headers_sent: + raise ProtocolError("Information response after final response") + + event = _ResponseSent() + return [event] + + def recv_informational_response(self, previous_state): + """ + Called when an informational header block is received (that is, a block + where the :status header has a 1XX value). + """ + if self.headers_received: + raise ProtocolError("Informational response after final response") + + event = InformationalResponseReceived() + event.stream_id = self.stream_id + return [event] + + def recv_alt_svc(self, previous_state): + """ + Called when receiving an ALTSVC frame. + + RFC 7838 allows us to receive ALTSVC frames at any stream state, which + is really absurdly overzealous. For that reason, we want to limit the + states in which we can actually receive it. It's really only sensible + to receive it after we've sent our own headers and before the server + has sent its header block: the server can't guarantee that we have any + state around after it completes its header block, and the server + doesn't know what origin we're talking about before we've sent ours. + + For that reason, this function applies a few extra checks on both state + and some of the little state variables we keep around. If those suggest + an unreasonable situation for the ALTSVC frame to have been sent in, + we quietly ignore it (as RFC 7838 suggests). + + This function is also *not* always called by the state machine. In some + states (IDLE, RESERVED_LOCAL, CLOSED) we don't bother to call it, + because we know the frame cannot be valid in that state (IDLE because + the server cannot know what origin the stream applies to, CLOSED + because the server cannot assume we still have state around, + RESERVED_LOCAL because by definition if we're in the RESERVED_LOCAL + state then *we* are the server). + """ + # Servers can't receive ALTSVC frames, but RFC 7838 tells us to ignore + # them. + if self.client is False: + return [] + + # If we've received the response headers from the server they can't + # guarantee we still have any state around. Other implementations + # (like nghttp2) ignore ALTSVC in this state, so we will too. + if self.headers_received: + return [] + + # Otherwise, this is a sensible enough frame to have received. Return + # the event and let it get populated. + return [AlternativeServiceAvailable()] + + def send_alt_svc(self, previous_state): + """ + Called when sending an ALTSVC frame on this stream. + + For consistency with the restrictions we apply on receiving ALTSVC + frames in ``recv_alt_svc``, we want to restrict when users can send + ALTSVC frames to the situations when we ourselves would accept them. + + That means: when we are a server, when we have received the request + headers, and when we have not yet sent our own response headers. + """ + # We should not send ALTSVC after we've sent response headers, as the + # client may have disposed of its state. + if self.headers_sent: + raise ProtocolError("Cannot send ALTSVC after sending response headers.") + + return + + +# STATE MACHINE +# +# The stream state machine is defined here to avoid the need to allocate it +# repeatedly for each stream. It cannot be defined in the stream class because +# it needs to be able to reference the callbacks defined on the class, but +# because Python's scoping rules are weird the class object is not actually in +# scope during the body of the class object. +# +# For the sake of clarity, we reproduce the RFC 7540 state machine here: +# +# +--------+ +# send PP | | recv PP +# ,--------| idle |--------. +# / | | \ +# v +--------+ v +# +----------+ | +----------+ +# | | | send H / | | +# ,------| reserved | | recv H | reserved |------. +# | | (local) | | | (remote) | | +# | +----------+ v +----------+ | +# | | +--------+ | | +# | | recv ES | | send ES | | +# | send H | ,-------| open |-------. | recv H | +# | | / | | \ | | +# | v v +--------+ v v | +# | +----------+ | +----------+ | +# | | half | | | half | | +# | | closed | | send R / | closed | | +# | | (remote) | | recv R | (local) | | +# | +----------+ | +----------+ | +# | | | | | +# | | send ES / | recv ES / | | +# | | send R / v send R / | | +# | | recv R +--------+ recv R | | +# | send R / `----------->| |<-----------' send R / | +# | recv R | closed | recv R | +# `----------------------->| |<----------------------' +# +--------+ +# +# send: endpoint sends this frame +# recv: endpoint receives this frame +# +# H: HEADERS frame (with implied CONTINUATIONs) +# PP: PUSH_PROMISE frame (with implied CONTINUATIONs) +# ES: END_STREAM flag +# R: RST_STREAM frame +# +# For the purposes of this state machine we treat HEADERS and their +# associated CONTINUATION frames as a single jumbo frame. The protocol +# allows/requires this by preventing other frames from being interleved in +# between HEADERS/CONTINUATION frames. However, if a CONTINUATION frame is +# received without a prior HEADERS frame, it *will* be passed to this state +# machine. The state machine should always reject that frame, either as an +# invalid transition or because the stream is closed. +# +# There is a confusing relationship around PUSH_PROMISE frames. The state +# machine above considers them to be frames belonging to the new stream, +# which is *somewhat* true. However, they are sent with the stream ID of +# their related stream, and are only sendable in some cases. +# For this reason, our state machine implementation below allows for +# PUSH_PROMISE frames both in the IDLE state (as in the diagram), but also +# in the OPEN, HALF_CLOSED_LOCAL, and HALF_CLOSED_REMOTE states. +# Essentially, for h2, PUSH_PROMISE frames are effectively sent on +# two streams. +# +# The _transitions dictionary contains a mapping of tuples of +# (state, input) to tuples of (side_effect_function, end_state). This +# map contains all allowed transitions: anything not in this map is +# invalid and immediately causes a transition to ``closed``. +_transitions = { + # State: idle + (StreamState.IDLE, StreamInputs.SEND_HEADERS): ( + H2StreamStateMachine.request_sent, + StreamState.OPEN, + ), + (StreamState.IDLE, StreamInputs.RECV_HEADERS): ( + H2StreamStateMachine.request_received, + StreamState.OPEN, + ), + (StreamState.IDLE, StreamInputs.RECV_DATA): ( + H2StreamStateMachine.reset_stream_on_error, + StreamState.CLOSED, + ), + (StreamState.IDLE, StreamInputs.SEND_PUSH_PROMISE): ( + H2StreamStateMachine.send_new_pushed_stream, + StreamState.RESERVED_LOCAL, + ), + (StreamState.IDLE, StreamInputs.RECV_PUSH_PROMISE): ( + H2StreamStateMachine.recv_new_pushed_stream, + StreamState.RESERVED_REMOTE, + ), + (StreamState.IDLE, StreamInputs.RECV_ALTERNATIVE_SERVICE): (None, StreamState.IDLE), + (StreamState.IDLE, StreamInputs.UPGRADE_CLIENT): ( + H2StreamStateMachine.request_sent, + StreamState.HALF_CLOSED_LOCAL, + ), + (StreamState.IDLE, StreamInputs.UPGRADE_SERVER): ( + H2StreamStateMachine.request_received, + StreamState.HALF_CLOSED_REMOTE, + ), + # State: reserved local + (StreamState.RESERVED_LOCAL, StreamInputs.SEND_HEADERS): ( + H2StreamStateMachine.response_sent, + StreamState.HALF_CLOSED_REMOTE, + ), + (StreamState.RESERVED_LOCAL, StreamInputs.RECV_DATA): ( + H2StreamStateMachine.reset_stream_on_error, + StreamState.CLOSED, + ), + (StreamState.RESERVED_LOCAL, StreamInputs.SEND_WINDOW_UPDATE): ( + None, + StreamState.RESERVED_LOCAL, + ), + (StreamState.RESERVED_LOCAL, StreamInputs.RECV_WINDOW_UPDATE): ( + H2StreamStateMachine.window_updated, + StreamState.RESERVED_LOCAL, + ), + (StreamState.RESERVED_LOCAL, StreamInputs.SEND_RST_STREAM): ( + H2StreamStateMachine.send_reset_stream, + StreamState.CLOSED, + ), + (StreamState.RESERVED_LOCAL, StreamInputs.RECV_RST_STREAM): ( + H2StreamStateMachine.stream_reset, + StreamState.CLOSED, + ), + (StreamState.RESERVED_LOCAL, StreamInputs.SEND_ALTERNATIVE_SERVICE): ( + H2StreamStateMachine.send_alt_svc, + StreamState.RESERVED_LOCAL, + ), + (StreamState.RESERVED_LOCAL, StreamInputs.RECV_ALTERNATIVE_SERVICE): ( + None, + StreamState.RESERVED_LOCAL, + ), + # State: reserved remote + (StreamState.RESERVED_REMOTE, StreamInputs.RECV_HEADERS): ( + H2StreamStateMachine.response_received, + StreamState.HALF_CLOSED_LOCAL, + ), + (StreamState.RESERVED_REMOTE, StreamInputs.RECV_DATA): ( + H2StreamStateMachine.reset_stream_on_error, + StreamState.CLOSED, + ), + (StreamState.RESERVED_REMOTE, StreamInputs.SEND_WINDOW_UPDATE): ( + None, + StreamState.RESERVED_REMOTE, + ), + (StreamState.RESERVED_REMOTE, StreamInputs.RECV_WINDOW_UPDATE): ( + H2StreamStateMachine.window_updated, + StreamState.RESERVED_REMOTE, + ), + (StreamState.RESERVED_REMOTE, StreamInputs.SEND_RST_STREAM): ( + H2StreamStateMachine.send_reset_stream, + StreamState.CLOSED, + ), + (StreamState.RESERVED_REMOTE, StreamInputs.RECV_RST_STREAM): ( + H2StreamStateMachine.stream_reset, + StreamState.CLOSED, + ), + (StreamState.RESERVED_REMOTE, StreamInputs.RECV_ALTERNATIVE_SERVICE): ( + H2StreamStateMachine.recv_alt_svc, + StreamState.RESERVED_REMOTE, + ), + # State: open + (StreamState.OPEN, StreamInputs.SEND_HEADERS): ( + H2StreamStateMachine.response_sent, + StreamState.OPEN, + ), + (StreamState.OPEN, StreamInputs.RECV_HEADERS): ( + H2StreamStateMachine.response_received, + StreamState.OPEN, + ), + (StreamState.OPEN, StreamInputs.SEND_DATA): (None, StreamState.OPEN), + (StreamState.OPEN, StreamInputs.RECV_DATA): ( + H2StreamStateMachine.data_received, + StreamState.OPEN, + ), + (StreamState.OPEN, StreamInputs.SEND_END_STREAM): ( + None, + StreamState.HALF_CLOSED_LOCAL, + ), + (StreamState.OPEN, StreamInputs.RECV_END_STREAM): ( + H2StreamStateMachine.stream_half_closed, + StreamState.HALF_CLOSED_REMOTE, + ), + (StreamState.OPEN, StreamInputs.SEND_WINDOW_UPDATE): (None, StreamState.OPEN), + (StreamState.OPEN, StreamInputs.RECV_WINDOW_UPDATE): ( + H2StreamStateMachine.window_updated, + StreamState.OPEN, + ), + (StreamState.OPEN, StreamInputs.SEND_RST_STREAM): ( + H2StreamStateMachine.send_reset_stream, + StreamState.CLOSED, + ), + (StreamState.OPEN, StreamInputs.RECV_RST_STREAM): ( + H2StreamStateMachine.stream_reset, + StreamState.CLOSED, + ), + (StreamState.OPEN, StreamInputs.SEND_PUSH_PROMISE): ( + H2StreamStateMachine.send_push_promise, + StreamState.OPEN, + ), + (StreamState.OPEN, StreamInputs.RECV_PUSH_PROMISE): ( + H2StreamStateMachine.recv_push_promise, + StreamState.OPEN, + ), + (StreamState.OPEN, StreamInputs.SEND_INFORMATIONAL_HEADERS): ( + H2StreamStateMachine.send_informational_response, + StreamState.OPEN, + ), + (StreamState.OPEN, StreamInputs.RECV_INFORMATIONAL_HEADERS): ( + H2StreamStateMachine.recv_informational_response, + StreamState.OPEN, + ), + (StreamState.OPEN, StreamInputs.SEND_ALTERNATIVE_SERVICE): ( + H2StreamStateMachine.send_alt_svc, + StreamState.OPEN, + ), + (StreamState.OPEN, StreamInputs.RECV_ALTERNATIVE_SERVICE): ( + H2StreamStateMachine.recv_alt_svc, + StreamState.OPEN, + ), + # State: half-closed remote + (StreamState.HALF_CLOSED_REMOTE, StreamInputs.SEND_HEADERS): ( + H2StreamStateMachine.response_sent, + StreamState.HALF_CLOSED_REMOTE, + ), + (StreamState.HALF_CLOSED_REMOTE, StreamInputs.RECV_HEADERS): ( + H2StreamStateMachine.reset_stream_on_error, + StreamState.CLOSED, + ), + (StreamState.HALF_CLOSED_REMOTE, StreamInputs.SEND_DATA): ( + None, + StreamState.HALF_CLOSED_REMOTE, + ), + (StreamState.HALF_CLOSED_REMOTE, StreamInputs.RECV_DATA): ( + H2StreamStateMachine.reset_stream_on_error, + StreamState.CLOSED, + ), + (StreamState.HALF_CLOSED_REMOTE, StreamInputs.SEND_END_STREAM): ( + H2StreamStateMachine.send_end_stream, + StreamState.CLOSED, + ), + (StreamState.HALF_CLOSED_REMOTE, StreamInputs.SEND_WINDOW_UPDATE): ( + None, + StreamState.HALF_CLOSED_REMOTE, + ), + (StreamState.HALF_CLOSED_REMOTE, StreamInputs.RECV_WINDOW_UPDATE): ( + H2StreamStateMachine.window_updated, + StreamState.HALF_CLOSED_REMOTE, + ), + (StreamState.HALF_CLOSED_REMOTE, StreamInputs.SEND_RST_STREAM): ( + H2StreamStateMachine.send_reset_stream, + StreamState.CLOSED, + ), + (StreamState.HALF_CLOSED_REMOTE, StreamInputs.RECV_RST_STREAM): ( + H2StreamStateMachine.stream_reset, + StreamState.CLOSED, + ), + (StreamState.HALF_CLOSED_REMOTE, StreamInputs.SEND_PUSH_PROMISE): ( + H2StreamStateMachine.send_push_promise, + StreamState.HALF_CLOSED_REMOTE, + ), + (StreamState.HALF_CLOSED_REMOTE, StreamInputs.RECV_PUSH_PROMISE): ( + H2StreamStateMachine.reset_stream_on_error, + StreamState.CLOSED, + ), + (StreamState.HALF_CLOSED_REMOTE, StreamInputs.SEND_INFORMATIONAL_HEADERS): ( + H2StreamStateMachine.send_informational_response, + StreamState.HALF_CLOSED_REMOTE, + ), + (StreamState.HALF_CLOSED_REMOTE, StreamInputs.SEND_ALTERNATIVE_SERVICE): ( + H2StreamStateMachine.send_alt_svc, + StreamState.HALF_CLOSED_REMOTE, + ), + (StreamState.HALF_CLOSED_REMOTE, StreamInputs.RECV_ALTERNATIVE_SERVICE): ( + H2StreamStateMachine.recv_alt_svc, + StreamState.HALF_CLOSED_REMOTE, + ), + # State: half-closed local + (StreamState.HALF_CLOSED_LOCAL, StreamInputs.RECV_HEADERS): ( + H2StreamStateMachine.response_received, + StreamState.HALF_CLOSED_LOCAL, + ), + (StreamState.HALF_CLOSED_LOCAL, StreamInputs.RECV_DATA): ( + H2StreamStateMachine.data_received, + StreamState.HALF_CLOSED_LOCAL, + ), + (StreamState.HALF_CLOSED_LOCAL, StreamInputs.RECV_END_STREAM): ( + H2StreamStateMachine.stream_ended, + StreamState.CLOSED, + ), + (StreamState.HALF_CLOSED_LOCAL, StreamInputs.SEND_WINDOW_UPDATE): ( + None, + StreamState.HALF_CLOSED_LOCAL, + ), + (StreamState.HALF_CLOSED_LOCAL, StreamInputs.RECV_WINDOW_UPDATE): ( + H2StreamStateMachine.window_updated, + StreamState.HALF_CLOSED_LOCAL, + ), + (StreamState.HALF_CLOSED_LOCAL, StreamInputs.SEND_RST_STREAM): ( + H2StreamStateMachine.send_reset_stream, + StreamState.CLOSED, + ), + (StreamState.HALF_CLOSED_LOCAL, StreamInputs.RECV_RST_STREAM): ( + H2StreamStateMachine.stream_reset, + StreamState.CLOSED, + ), + (StreamState.HALF_CLOSED_LOCAL, StreamInputs.RECV_PUSH_PROMISE): ( + H2StreamStateMachine.recv_push_promise, + StreamState.HALF_CLOSED_LOCAL, + ), + (StreamState.HALF_CLOSED_LOCAL, StreamInputs.RECV_INFORMATIONAL_HEADERS): ( + H2StreamStateMachine.recv_informational_response, + StreamState.HALF_CLOSED_LOCAL, + ), + (StreamState.HALF_CLOSED_LOCAL, StreamInputs.SEND_ALTERNATIVE_SERVICE): ( + H2StreamStateMachine.send_alt_svc, + StreamState.HALF_CLOSED_LOCAL, + ), + (StreamState.HALF_CLOSED_LOCAL, StreamInputs.RECV_ALTERNATIVE_SERVICE): ( + H2StreamStateMachine.recv_alt_svc, + StreamState.HALF_CLOSED_LOCAL, + ), + # State: closed + (StreamState.CLOSED, StreamInputs.RECV_END_STREAM): (None, StreamState.CLOSED), + (StreamState.CLOSED, StreamInputs.RECV_ALTERNATIVE_SERVICE): ( + None, + StreamState.CLOSED, + ), + # RFC 7540 Section 5.1 defines how the end point should react when + # receiving a frame on a closed stream with the following statements: + # + # > An endpoint that receives any frame other than PRIORITY after receiving + # > a RST_STREAM MUST treat that as a stream error of type STREAM_CLOSED. + # > An endpoint that receives any frames after receiving a frame with the + # > END_STREAM flag set MUST treat that as a connection error of type + # > STREAM_CLOSED. + (StreamState.CLOSED, StreamInputs.RECV_HEADERS): ( + H2StreamStateMachine.recv_on_closed_stream, + StreamState.CLOSED, + ), + (StreamState.CLOSED, StreamInputs.RECV_DATA): ( + H2StreamStateMachine.recv_on_closed_stream, + StreamState.CLOSED, + ), + # > WINDOW_UPDATE or RST_STREAM frames can be received in this state + # > for a short period after a DATA or HEADERS frame containing a + # > END_STREAM flag is sent, as instructed in RFC 7540 Section 5.1. But we + # > don't have access to a clock so we just always allow it. + (StreamState.CLOSED, StreamInputs.RECV_WINDOW_UPDATE): (None, StreamState.CLOSED), + (StreamState.CLOSED, StreamInputs.RECV_RST_STREAM): (None, StreamState.CLOSED), + # > A receiver MUST treat the receipt of a PUSH_PROMISE on a stream that is + # > neither "open" nor "half-closed (local)" as a connection error of type + # > PROTOCOL_ERROR. + (StreamState.CLOSED, StreamInputs.RECV_PUSH_PROMISE): ( + H2StreamStateMachine.recv_push_on_closed_stream, + StreamState.CLOSED, + ), + # Also, users should be forbidden from sending on closed streams. + (StreamState.CLOSED, StreamInputs.SEND_HEADERS): ( + H2StreamStateMachine.send_on_closed_stream, + StreamState.CLOSED, + ), + (StreamState.CLOSED, StreamInputs.SEND_PUSH_PROMISE): ( + H2StreamStateMachine.send_push_on_closed_stream, + StreamState.CLOSED, + ), + (StreamState.CLOSED, StreamInputs.SEND_RST_STREAM): ( + H2StreamStateMachine.send_on_closed_stream, + StreamState.CLOSED, + ), + (StreamState.CLOSED, StreamInputs.SEND_DATA): ( + H2StreamStateMachine.send_on_closed_stream, + StreamState.CLOSED, + ), + (StreamState.CLOSED, StreamInputs.SEND_WINDOW_UPDATE): ( + H2StreamStateMachine.send_on_closed_stream, + StreamState.CLOSED, + ), + (StreamState.CLOSED, StreamInputs.SEND_END_STREAM): ( + H2StreamStateMachine.send_on_closed_stream, + StreamState.CLOSED, + ), +} + + +class H2Stream: + """ + A low-level HTTP/2 stream object. This handles building and receiving + frames and maintains per-stream state. + + This wraps a HTTP/2 Stream state machine implementation, ensuring that + frames can only be sent/received when the stream is in a valid state. + Attempts to create frames that cannot be sent will raise a + ``ProtocolError``. + """ + + def __init__(self, stream_id, config, inbound_window_size, outbound_window_size): + self.state_machine = H2StreamStateMachine(stream_id) + self.stream_id = stream_id + self.max_outbound_frame_size = None + self.request_method = None + + # The current value of the outbound stream flow control window + self.outbound_flow_control_window = outbound_window_size + + # The flow control manager. + self._inbound_window_manager = WindowManager(inbound_window_size) + + # The expected content length, if any. + self._expected_content_length = None + + # The actual received content length. Always tracked. + self._actual_content_length = 0 + + # The authority we believe this stream belongs to. + self._authority = None + + # The configuration for this stream. + self.config = config + + def __repr__(self): + return "<%s id:%d state:%r>" % ( + type(self).__name__, + self.stream_id, + self.state_machine.state, + ) + + @property + def inbound_flow_control_window(self): + """ + The size of the inbound flow control window for the stream. This is + rarely publicly useful: instead, use :meth:`remote_flow_control_window + `. This shortcut is + largely present to provide a shortcut to this data. + """ + return self._inbound_window_manager.current_window_size + + @property + def open(self): + """ + Whether the stream is 'open' in any sense: that is, whether it counts + against the number of concurrent streams. + """ + # RFC 7540 Section 5.1.2 defines 'open' for this purpose to mean either + # the OPEN state or either of the HALF_CLOSED states. Perplexingly, + # this excludes the reserved states. + # For more detail on why we're doing this in this slightly weird way, + # see the comment on ``STREAM_OPEN`` at the top of the file. + return STREAM_OPEN[self.state_machine.state] + + @property + def closed(self): + """ + Whether the stream is closed. + """ + return self.state_machine.state == StreamState.CLOSED + + @property + def closed_by(self): + """ + Returns how the stream was closed, as one of StreamClosedBy. + """ + return self.state_machine.stream_closed_by + + def upgrade(self, client_side): + """ + Called by the connection to indicate that this stream is the initial + request/response of an upgraded connection. Places the stream into an + appropriate state. + """ + self.config.logger.debug("Upgrading %r", self) + + assert self.stream_id == 1 + input_ = ( + StreamInputs.UPGRADE_CLIENT if client_side else StreamInputs.UPGRADE_SERVER + ) + + # This may return events, we deliberately don't want them. + self.state_machine.process_input(input_) + return + + def send_headers(self, headers, encoder, end_stream=False): + """ + Returns a list of HEADERS/CONTINUATION frames to emit as either headers + or trailers. + """ + self.config.logger.debug("Send headers %s on %r", headers, self) + + # Because encoding headers makes an irreversible change to the header + # compression context, we make the state transition before we encode + # them. + + # First, check if we're a client. If we are, no problem: if we aren't, + # we need to scan the header block to see if this is an informational + # response. + input_ = StreamInputs.SEND_HEADERS + if (not self.state_machine.client) and is_informational_response(headers): + if end_stream: + raise ProtocolError("Cannot set END_STREAM on informational responses.") + + input_ = StreamInputs.SEND_INFORMATIONAL_HEADERS + + events = self.state_machine.process_input(input_) + + hf = HeadersFrame(self.stream_id) + hdr_validation_flags = self._build_hdr_validation_flags(events) + frames = self._build_headers_frames(headers, encoder, hf, hdr_validation_flags) + + if end_stream: + # Not a bug: the END_STREAM flag is valid on the initial HEADERS + # frame, not the CONTINUATION frames that follow. + self.state_machine.process_input(StreamInputs.SEND_END_STREAM) + frames[0].flags.add("END_STREAM") + + if self.state_machine.trailers_sent and not end_stream: + raise ProtocolError("Trailers must have END_STREAM set.") + + if self.state_machine.client and self._authority is None: + self._authority = authority_from_headers(headers) + + # store request method for _initialize_content_length + self.request_method = extract_method_header(headers) + + return frames + + def push_stream_in_band(self, related_stream_id, headers, encoder): + """ + Returns a list of PUSH_PROMISE/CONTINUATION frames to emit as a pushed + stream header. Called on the stream that has the PUSH_PROMISE frame + sent on it. + """ + self.config.logger.debug("Push stream %r", self) + + # Because encoding headers makes an irreversible change to the header + # compression context, we make the state transition *first*. + + events = self.state_machine.process_input(StreamInputs.SEND_PUSH_PROMISE) + + ppf = PushPromiseFrame(self.stream_id) + ppf.promised_stream_id = related_stream_id + hdr_validation_flags = self._build_hdr_validation_flags(events) + frames = self._build_headers_frames(headers, encoder, ppf, hdr_validation_flags) + + return frames + + def locally_pushed(self): + """ + Mark this stream as one that was pushed by this peer. Must be called + immediately after initialization. Sends no frames, simply updates the + state machine. + """ + # This does not trigger any events. + events = self.state_machine.process_input(StreamInputs.SEND_PUSH_PROMISE) + assert not events + return [] + + def send_data(self, data, end_stream=False, pad_length=None): + """ + Prepare some data frames. Optionally end the stream. + + .. warning:: Does not perform flow control checks. + """ + self.config.logger.debug( + "Send data on %r with end stream set to %s", self, end_stream + ) + + self.state_machine.process_input(StreamInputs.SEND_DATA) + + df = DataFrame(self.stream_id) + df.data = data + if end_stream: + self.state_machine.process_input(StreamInputs.SEND_END_STREAM) + df.flags.add("END_STREAM") + if pad_length is not None: + df.flags.add("PADDED") + df.pad_length = pad_length + + # Subtract flow_controlled_length to account for possible padding + self.outbound_flow_control_window -= df.flow_controlled_length + assert self.outbound_flow_control_window >= 0 + + return [df] + + def end_stream(self): + """ + End a stream without sending data. + """ + self.config.logger.debug("End stream %r", self) + + self.state_machine.process_input(StreamInputs.SEND_END_STREAM) + df = DataFrame(self.stream_id) + df.flags.add("END_STREAM") + return [df] + + def advertise_alternative_service(self, field_value): + """ + Advertise an RFC 7838 alternative service. The semantics of this are + better documented in the ``H2Connection`` class. + """ + self.config.logger.debug( + "Advertise alternative service of %r for %r", field_value, self + ) + self.state_machine.process_input(StreamInputs.SEND_ALTERNATIVE_SERVICE) + asf = AltSvcFrame(self.stream_id) + asf.field = field_value + return [asf] + + def increase_flow_control_window(self, increment): + """ + Increase the size of the flow control window for the remote side. + """ + self.config.logger.debug( + "Increase flow control window for %r by %d", self, increment + ) + self.state_machine.process_input(StreamInputs.SEND_WINDOW_UPDATE) + self._inbound_window_manager.window_opened(increment) + + wuf = WindowUpdateFrame(self.stream_id) + wuf.window_increment = increment + return [wuf] + + def receive_push_promise_in_band( + self, promised_stream_id, headers, header_encoding + ): + """ + Receives a push promise frame sent on this stream, pushing a remote + stream. This is called on the stream that has the PUSH_PROMISE sent + on it. + """ + self.config.logger.debug( + "Receive Push Promise on %r for remote stream %d", self, promised_stream_id + ) + events = self.state_machine.process_input(StreamInputs.RECV_PUSH_PROMISE) + events[0].pushed_stream_id = promised_stream_id + + hdr_validation_flags = self._build_hdr_validation_flags(events) + events[0].headers = self._process_received_headers( + headers, hdr_validation_flags, header_encoding + ) + return [], events + + def remotely_pushed(self, pushed_headers): + """ + Mark this stream as one that was pushed by the remote peer. Must be + called immediately after initialization. Sends no frames, simply + updates the state machine. + """ + self.config.logger.debug("%r pushed by remote peer", self) + events = self.state_machine.process_input(StreamInputs.RECV_PUSH_PROMISE) + self._authority = authority_from_headers(pushed_headers) + return [], events + + def receive_headers(self, headers, end_stream, header_encoding): + """ + Receive a set of headers (or trailers). + """ + if is_informational_response(headers): + if end_stream: + raise ProtocolError("Cannot set END_STREAM on informational responses") + input_ = StreamInputs.RECV_INFORMATIONAL_HEADERS + else: + input_ = StreamInputs.RECV_HEADERS + + events = self.state_machine.process_input(input_) + + if end_stream: + es_events = self.state_machine.process_input(StreamInputs.RECV_END_STREAM) + events[0].stream_ended = es_events[0] + events += es_events + + self._initialize_content_length(headers) + + if isinstance(events[0], TrailersReceived): + if not end_stream: + raise ProtocolError("Trailers must have END_STREAM set") + + hdr_validation_flags = self._build_hdr_validation_flags(events) + events[0].headers = self._process_received_headers( + headers, hdr_validation_flags, header_encoding + ) + return [], events + + def receive_data(self, data, end_stream, flow_control_len): + """ + Receive some data. + """ + self.config.logger.debug( + "Receive data on %r with end stream %s and flow control length set to %d", + self, + end_stream, + flow_control_len, + ) + events = self.state_machine.process_input(StreamInputs.RECV_DATA) + self._inbound_window_manager.window_consumed(flow_control_len) + self._track_content_length(len(data), end_stream) + + if end_stream: + es_events = self.state_machine.process_input(StreamInputs.RECV_END_STREAM) + events[0].stream_ended = es_events[0] + events.extend(es_events) + + events[0].data = data + events[0].flow_controlled_length = flow_control_len + return [], events + + def receive_window_update(self, increment): + """ + Handle a WINDOW_UPDATE increment. + """ + self.config.logger.debug( + "Receive Window Update on %r for increment of %d", self, increment + ) + events = self.state_machine.process_input(StreamInputs.RECV_WINDOW_UPDATE) + frames = [] + + # If we encounter a problem with incrementing the flow control window, + # this should be treated as a *stream* error, not a *connection* error. + # That means we need to catch the error and forcibly close the stream. + if events: + events[0].delta = increment + try: + self.outbound_flow_control_window = guard_increment_window( + self.outbound_flow_control_window, increment + ) + except FlowControlError: + # Ok, this is bad. We're going to need to perform a local + # reset. + event = StreamReset() + event.stream_id = self.stream_id + event.error_code = ErrorCodes.FLOW_CONTROL_ERROR + event.remote_reset = False + + events = [event] + frames = self.reset_stream(event.error_code) + + return frames, events + + def receive_continuation(self): + """ + A naked CONTINUATION frame has been received. This is always an error, + but the type of error it is depends on the state of the stream and must + transition the state of the stream, so we need to handle it. + """ + self.config.logger.debug("Receive Continuation frame on %r", self) + self.state_machine.process_input(StreamInputs.RECV_CONTINUATION) + assert False, "Should not be reachable" + + def receive_alt_svc(self, frame): + """ + An Alternative Service frame was received on the stream. This frame + inherits the origin associated with this stream. + """ + self.config.logger.debug("Receive Alternative Service frame on stream %r", self) + + # If the origin is present, RFC 7838 says we have to ignore it. + if frame.origin: + return [], [] + + events = self.state_machine.process_input(StreamInputs.RECV_ALTERNATIVE_SERVICE) + + # There are lots of situations where we want to ignore the ALTSVC + # frame. If we need to pay attention, we'll have an event and should + # fill it out. + if events: + assert isinstance(events[0], AlternativeServiceAvailable) + events[0].origin = self._authority + events[0].field_value = frame.field + + return [], events + + def reset_stream(self, error_code=0): + """ + Close the stream locally. Reset the stream with an error code. + """ + self.config.logger.debug("Local reset %r with error code: %d", self, error_code) + self.state_machine.process_input(StreamInputs.SEND_RST_STREAM) + + rsf = RstStreamFrame(self.stream_id) + rsf.error_code = error_code + return [rsf] + + def stream_reset(self, frame): + """ + Handle a stream being reset remotely. + """ + self.config.logger.debug( + "Remote reset %r with error code: %d", self, frame.error_code + ) + events = self.state_machine.process_input(StreamInputs.RECV_RST_STREAM) + + if events: + # We don't fire an event if this stream is already closed. + events[0].error_code = _error_code_from_int(frame.error_code) + + return [], events + + def acknowledge_received_data(self, acknowledged_size): + """ + The user has informed us that they've processed some amount of data + that was received on this stream. Pass that to the window manager and + potentially return some WindowUpdate frames. + """ + self.config.logger.debug( + "Acknowledge received data with size %d on %r", acknowledged_size, self + ) + increment = self._inbound_window_manager.process_bytes(acknowledged_size) + if increment: + f = WindowUpdateFrame(self.stream_id) + f.window_increment = increment + return [f] + + return [] + + def _build_hdr_validation_flags(self, events): + """ + Constructs a set of header validation flags for use when normalizing + and validating header blocks. + """ + is_trailer = isinstance(events[0], (_TrailersSent, TrailersReceived)) + is_response_header = isinstance( + events[0], (_ResponseSent, ResponseReceived, InformationalResponseReceived) + ) + is_push_promise = isinstance( + events[0], (PushedStreamReceived, _PushedRequestSent) + ) + + return HeaderValidationFlags( + is_client=self.state_machine.client, + is_trailer=is_trailer, + is_response_header=is_response_header, + is_push_promise=is_push_promise, + ) + + def _build_headers_frames( + self, headers, encoder, first_frame, hdr_validation_flags + ): + """ + Helper method to build headers or push promise frames. + """ + # We need to lowercase the header names, and to ensure that secure + # header fields are kept out of compression contexts. + if self.config.normalize_outbound_headers: + # also we may want to split outbound cookies to improve + # headers compression + should_split_outbound_cookies = self.config.split_outbound_cookies + + headers = normalize_outbound_headers( + headers, hdr_validation_flags, should_split_outbound_cookies + ) + if self.config.validate_outbound_headers: + headers = validate_outbound_headers(headers, hdr_validation_flags) + + if ALTERNATIVE_HPACK: + encoded_headers = encoder.encode( + [ + ( + h[0] if isinstance(h[0], bytes) else h[0].encode(), + h[1] if isinstance(h[1], bytes) else h[1].encode(), + isinstance(h, NeverIndexedHeaderTuple), + ) + for h in headers + ] + ) + else: + encoded_headers = encoder.encode(headers) + + # Slice into blocks of max_outbound_frame_size. Be careful with this: + # it only works right because we never send padded frames or priority + # information on the frames. Revisit this if we do. + header_blocks = [ + encoded_headers[i : i + self.max_outbound_frame_size] + for i in range(0, len(encoded_headers), self.max_outbound_frame_size) + ] + + frames = [] + first_frame.data = header_blocks[0] + frames.append(first_frame) + + for block in header_blocks[1:]: + cf = ContinuationFrame(self.stream_id) + cf.data = block + frames.append(cf) + + frames[-1].flags.add("END_HEADERS") + return frames + + def _process_received_headers( + self, headers, header_validation_flags, header_encoding + ): + """ + When headers have been received from the remote peer, run a processing + pipeline on them to transform them into the appropriate form for + attaching to an event. + """ + if self.config.normalize_inbound_headers: + headers = normalize_inbound_headers(headers, header_validation_flags) + + if self.config.validate_inbound_headers: + headers = validate_headers(headers, header_validation_flags) + + if header_encoding: + headers = _decode_headers(headers, header_encoding) + + # The above steps are all generators, so we need to concretize the + # headers now. + return list(headers) + + def _initialize_content_length(self, headers): + """ + Checks the headers for a content-length header and initializes the + _expected_content_length field from it. It's not an error for no + Content-Length header to be present. + """ + if self.request_method == b"HEAD": + self._expected_content_length = 0 + return + + for n, v in headers: + if n == b"content-length": + try: + self._expected_content_length = int(v, 10) + except ValueError: + raise ProtocolError("Invalid content-length header: %s" % v) + + return + + def _track_content_length(self, length, end_stream): + """ + Update the expected content length in response to data being received. + Validates that the appropriate amount of data is sent. Always updates + the received data, but only validates the length against the + content-length header if one was sent. + + :param length: The length of the body chunk received. + :param end_stream: If this is the last body chunk received. + """ + self._actual_content_length += length + actual = self._actual_content_length + expected = self._expected_content_length + + if expected is not None: + if expected < actual: + raise InvalidBodyLengthError(expected, actual) + + if end_stream and expected != actual: + raise InvalidBodyLengthError(expected, actual) + + def _inbound_flow_control_change_from_settings(self, delta): + """ + We changed SETTINGS_INITIAL_WINDOW_SIZE, which means we need to + update the target window size for flow control. For our flow control + strategy, this means we need to do two things: we need to adjust the + current window size, but we also need to set the target maximum window + size to the new value. + """ + new_max_size = self._inbound_window_manager.max_window_size + delta + self._inbound_window_manager.window_opened(delta) + self._inbound_window_manager.max_window_size = new_max_size + + +def _decode_headers(headers, encoding): + """ + Given an iterable of header two-tuples and an encoding, decodes those + headers using that encoding while preserving the type of the header tuple. + This ensures that the use of ``HeaderTuple`` is preserved. + """ + for header in headers: + # This function expects to work on decoded headers, which are always + # HeaderTuple objects. + name, value = header + + name = name.decode(encoding) + value = value.decode(encoding) + + yield header.__class__(name, value) diff --git a/.venv/lib/python3.9/site-packages/jh2/utilities.py b/.venv/lib/python3.9/site-packages/jh2/utilities.py new file mode 100644 index 0000000..1b54ccd --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/utilities.py @@ -0,0 +1,694 @@ +""" +h2/utilities +~~~~~~~~~~~~ + +Utility functions that do not belong in a separate module. +""" + +from __future__ import annotations + +import collections +import re +from string import whitespace + +from .exceptions import FlowControlError, ProtocolError +from .hpack import HeaderTuple, NeverIndexedHeaderTuple + +UPPER_RE = re.compile(b"[A-Z]") + +# A set of headers that are hop-by-hop or connection-specific and thus +# forbidden in HTTP/2. This list comes from RFC 7540 § 8.1.2.2. +CONNECTION_HEADERS = frozenset( + [ + b"connection", + "connection", + b"proxy-connection", + "proxy-connection", + b"keep-alive", + "keep-alive", + b"transfer-encoding", + "transfer-encoding", + b"upgrade", + "upgrade", + ] +) + + +_ALLOWED_PSEUDO_HEADER_FIELDS = frozenset( + [ + b":method", + ":method", + b":scheme", + ":scheme", + b":authority", + ":authority", + b":path", + ":path", + b":status", + ":status", + b":protocol", + ":protocol", + ] +) + + +_SECURE_HEADERS = frozenset( + [ + # May have basic credentials which are vulnerable to dictionary attacks. + b"authorization", + "authorization", + b"proxy-authorization", + "proxy-authorization", + ] +) + + +_REQUEST_ONLY_HEADERS = frozenset( + [ + b":scheme", + ":scheme", + b":path", + ":path", + b":authority", + ":authority", + b":method", + ":method", + b":protocol", + ":protocol", + ] +) + + +_RESPONSE_ONLY_HEADERS = frozenset([b":status", ":status"]) + + +# A Set of pseudo headers that are only valid if the method is +# CONNECT, see RFC 8441 § 5 +_CONNECT_REQUEST_ONLY_HEADERS = frozenset([b":protocol", ":protocol"]) + + +_WHITESPACE = frozenset(map(ord, whitespace)) + + +def _secure_headers(headers, hdr_validation_flags): + """ + Certain headers are at risk of being attacked during the header compression + phase, and so need to be kept out of header compression contexts. This + function automatically transforms certain specific headers into HPACK + never-indexed fields to ensure they don't get added to header compression + contexts. + + This function currently implements two rules: + + - 'authorization' and 'proxy-authorization' fields are automatically made + never-indexed. + - Any 'cookie' header field shorter than 20 bytes long is made + never-indexed. + + These fields are the most at-risk. These rules are inspired by Firefox + and nghttp2. + """ + for header in headers: + if header[0] in _SECURE_HEADERS: + yield NeverIndexedHeaderTuple(*header) + elif header[0] in (b"cookie", "cookie") and len(header[1]) < 20: + yield NeverIndexedHeaderTuple(*header) + else: + yield header + + +def extract_method_header(headers): + """ + Extracts the request method from the headers list. + """ + for k, v in headers: + if k in (b":method", ":method"): + if not isinstance(v, bytes): + return v.encode("utf-8") + else: + return v + + +def is_informational_response(headers): + """ + Searches a header block for a :status header to confirm that a given + collection of headers are an informational response. Assumes the header + block is well formed: that is, that the HTTP/2 special headers are first + in the block, and so that it can stop looking when it finds the first + header field whose name does not begin with a colon. + + :param headers: The HTTP/2 header block. + :returns: A boolean indicating if this is an informational response. + """ + for n, v in headers: + if isinstance(n, bytes): + sigil = b":" + status = b":status" + informational_start = b"1" + else: + sigil = ":" + status = ":status" + informational_start = "1" + + # If we find a non-special header, we're done here: stop looping. + if not n.startswith(sigil): + return False + + # This isn't the status header, bail. + if n != status: + continue + + # If the first digit is a 1, we've got informational headers. + return v.startswith(informational_start) + + +def guard_increment_window(current, increment): + """ + Increments a flow control window, guarding against that window becoming too + large. + + :param current: The current value of the flow control window. + :param increment: The increment to apply to that window. + :returns: The new value of the window. + :raises: ``FlowControlError`` + """ + # The largest value the flow control window may take. + LARGEST_FLOW_CONTROL_WINDOW = 2**31 - 1 + + new_size = current + increment + + if new_size > LARGEST_FLOW_CONTROL_WINDOW: + raise FlowControlError( + "May not increment flow control window past %d" + % LARGEST_FLOW_CONTROL_WINDOW + ) + + return new_size + + +def authority_from_headers(headers): + """ + Given a header set, searches for the authority header and returns the + value. + + Note that this doesn't terminate early, so should only be called if the + headers are for a client request. Otherwise, will loop over the entire + header set, which is potentially unwise. + + :param headers: The HTTP header set. + :returns: The value of the authority header, or ``None``. + :rtype: ``bytes`` or ``None``. + """ + for n, v in headers: + # This gets run against headers that come both from HPACK and from the + # user, so we may have unicode floating around in here. We only want + # bytes. + if n in (b":authority", ":authority"): + return v.encode("utf-8") if not isinstance(v, bytes) else v + + return None + + +# Flags used by the validate_headers pipeline to determine which checks +# should be applied to a given set of headers. +HeaderValidationFlags = collections.namedtuple( + "HeaderValidationFlags", + ["is_client", "is_trailer", "is_response_header", "is_push_promise"], +) + + +def validate_headers(headers, hdr_validation_flags): + """ + Validates a header sequence against a set of constraints from RFC 7540. + + :param headers: The HTTP header set. + :param hdr_validation_flags: An instance of HeaderValidationFlags. + """ + # This validation logic is built on a sequence of generators that are + # iterated over to provide the final header list. This reduces some of the + # overhead of doing this checking. However, it's worth noting that this + # checking remains somewhat expensive, and attempts should be made wherever + # possible to reduce the time spent doing them. + # + # For example, we avoid tuple unpacking in loops because it represents a + # fixed cost that we don't want to spend, instead indexing into the header + # tuples. + headers = _reject_empty_header_names(headers, hdr_validation_flags) + headers = _reject_uppercase_header_fields(headers, hdr_validation_flags) + headers = _reject_surrounding_whitespace(headers, hdr_validation_flags) + headers = _reject_te(headers, hdr_validation_flags) + headers = _reject_connection_header(headers, hdr_validation_flags) + headers = _reject_pseudo_header_fields(headers, hdr_validation_flags) + headers = _check_host_authority_header(headers, hdr_validation_flags) + headers = _check_path_header(headers, hdr_validation_flags) + + return headers + + +def _reject_empty_header_names(headers, hdr_validation_flags): + """ + Raises a ProtocolError if any header names are empty (length 0). + While hpack decodes such headers without errors, they are semantically + forbidden in HTTP, see RFC 7230, stating that they must be at least one + character long. + """ + for header in headers: + if len(header[0]) == 0: + raise ProtocolError("Received header name with zero length.") + yield header + + +def _reject_uppercase_header_fields(headers, hdr_validation_flags): + """ + Raises a ProtocolError if any uppercase character is found in a header + block. + """ + for header in headers: + if UPPER_RE.search(header[0]): + raise ProtocolError("Received uppercase header name %s." % header[0]) + yield header + + +def _reject_surrounding_whitespace(headers, hdr_validation_flags): + """ + Raises a ProtocolError if any header name or value is surrounded by + whitespace characters. + """ + # For compatibility with RFC 7230 header fields, we need to allow the field + # value to be an empty string. This is ludicrous, but technically allowed. + # The field name may not be empty, though, so we can safely assume that it + # must have at least one character in it and throw exceptions if it + # doesn't. + for header in headers: + if header[0][0] in _WHITESPACE or header[0][-1] in _WHITESPACE: + raise ProtocolError( + "Received header name surrounded by whitespace %r" % header[0] + ) + if header[1] and ( + (header[1][0] in _WHITESPACE) or (header[1][-1] in _WHITESPACE) + ): + raise ProtocolError( + "Received header value surrounded by whitespace %r" % header[1] + ) + yield header + + +def _reject_te(headers, hdr_validation_flags): + """ + Raises a ProtocolError if the TE header is present in a header block and + its value is anything other than "trailers". + """ + for header in headers: + if header[0] in (b"te", "te"): + if header[1].lower() not in (b"trailers", "trailers"): + raise ProtocolError("Invalid value for TE header: %s" % header[1]) + + yield header + + +def _reject_connection_header(headers, hdr_validation_flags): + """ + Raises a ProtocolError if the Connection header is present in a header + block. + """ + for header in headers: + if header[0] in CONNECTION_HEADERS: + raise ProtocolError( + "Connection-specific header field present: %s." % header[0] + ) + + yield header + + +def _custom_startswith(test_string, bytes_prefix, unicode_prefix): + """ + Given a string that might be a bytestring or a Unicode string, + return True if it starts with the appropriate prefix. + """ + if isinstance(test_string, bytes): + return test_string.startswith(bytes_prefix) + else: + return test_string.startswith(unicode_prefix) + + +def _assert_header_in_set(string_header, bytes_header, header_set): + """ + Given a set of header names, checks whether the string or byte version of + the header name is present. Raises a Protocol error with the appropriate + error if it's missing. + """ + if not (string_header in header_set or bytes_header in header_set): + raise ProtocolError("Header block missing mandatory %s header" % string_header) + + +def _reject_pseudo_header_fields(headers, hdr_validation_flags): + """ + Raises a ProtocolError if duplicate pseudo-header fields are found in a + header block or if a pseudo-header field appears in a block after an + ordinary header field. + + Raises a ProtocolError if pseudo-header fields are found in trailers. + """ + seen_pseudo_header_fields = set() + seen_regular_header = False + method = None + + for header in headers: + if _custom_startswith(header[0], b":", ":"): + if header[0] in seen_pseudo_header_fields: + raise ProtocolError( + "Received duplicate pseudo-header field %s" % header[0] + ) + + seen_pseudo_header_fields.add(header[0]) + + if seen_regular_header: + raise ProtocolError( + "Received pseudo-header field out of sequence: %s" % header[0] + ) + + if header[0] not in _ALLOWED_PSEUDO_HEADER_FIELDS: + raise ProtocolError( + "Received custom pseudo-header field %s" % header[0] + ) + + if header[0] in (b":method", ":method"): + if not isinstance(header[1], bytes): + method = header[1].encode("utf-8") + else: + method = header[1] + + else: + seen_regular_header = True + + yield header + + # Check the pseudo-headers we got to confirm they're acceptable. + _check_pseudo_header_field_acceptability( + seen_pseudo_header_fields, method, hdr_validation_flags + ) + + +def _check_pseudo_header_field_acceptability( + pseudo_headers, method, hdr_validation_flags +): + """ + Given the set of pseudo-headers present in a header block and the + validation flags, confirms that RFC 7540 allows them. + """ + # Pseudo-header fields MUST NOT appear in trailers - RFC 7540 § 8.1.2.1 + if hdr_validation_flags.is_trailer and pseudo_headers: + raise ProtocolError("Received pseudo-header in trailer %s" % pseudo_headers) + + # If ':status' pseudo-header is not there in a response header, reject it. + # Similarly, if ':path', ':method', or ':scheme' are not there in a request + # header, reject it. Additionally, if a response contains any request-only + # headers or vice-versa, reject it. + # Relevant RFC section: RFC 7540 § 8.1.2.4 + # https://tools.ietf.org/html/rfc7540#section-8.1.2.4 + if hdr_validation_flags.is_response_header: + _assert_header_in_set(":status", b":status", pseudo_headers) + invalid_response_headers = pseudo_headers & _REQUEST_ONLY_HEADERS + if invalid_response_headers: + raise ProtocolError( + "Encountered request-only headers %s" % invalid_response_headers + ) + elif ( + not hdr_validation_flags.is_response_header + and not hdr_validation_flags.is_trailer + ): + # This is a request, so we need to have seen :path, :method, and + # :scheme. + _assert_header_in_set(":path", b":path", pseudo_headers) + _assert_header_in_set(":method", b":method", pseudo_headers) + _assert_header_in_set(":scheme", b":scheme", pseudo_headers) + invalid_request_headers = pseudo_headers & _RESPONSE_ONLY_HEADERS + if invalid_request_headers: + raise ProtocolError( + "Encountered response-only headers %s" % invalid_request_headers + ) + if method != b"CONNECT": + invalid_headers = pseudo_headers & _CONNECT_REQUEST_ONLY_HEADERS + if invalid_headers: + raise ProtocolError( + "Encountered connect-request-only headers %s" % invalid_headers + ) + + +def _validate_host_authority_header(headers): + """ + Given the :authority and Host headers from a request block that isn't + a trailer, check that: + 1. At least one of these headers is set. + 2. If both headers are set, they match. + + :param headers: The HTTP header set. + :raises: ``ProtocolError`` + """ + # We use None as a sentinel value. Iterate over the list of headers, + # and record the value of these headers (if present). We don't need + # to worry about receiving duplicate :authority headers, as this is + # enforced by the _reject_pseudo_header_fields() pipeline. + # + # TODO: We should also guard against receiving duplicate Host headers, + # and against sending duplicate headers. + authority_header_val = None + host_header_val = None + + for header in headers: + if header[0] in (b":authority", ":authority"): + authority_header_val = header[1] + elif header[0] in (b"host", "host"): + host_header_val = header[1] + + yield header + + # If we have not-None values for these variables, then we know we saw + # the corresponding header. + authority_present = authority_header_val is not None + host_present = host_header_val is not None + + # It is an error for a request header block to contain neither + # an :authority header nor a Host header. + if not authority_present and not host_present: + raise ProtocolError( + "Request header block does not have an :authority or Host header." + ) + + # If we receive both headers, they should definitely match. + if authority_present and host_present: + if authority_header_val != host_header_val: + raise ProtocolError( + "Request header block has mismatched :authority and " + "Host headers: %r / %r" % (authority_header_val, host_header_val) + ) + + +def _check_host_authority_header(headers, hdr_validation_flags): + """ + Raises a ProtocolError if a header block arrives that does not contain an + :authority or a Host header, or if a header block contains both fields, + but their values do not match. + """ + # We only expect to see :authority and Host headers on request header + # blocks that aren't trailers, so skip this validation if this is a + # response header or we're looking at trailer blocks. + skip_validation = ( + hdr_validation_flags.is_response_header or hdr_validation_flags.is_trailer + ) + if skip_validation: + return headers + + return _validate_host_authority_header(headers) + + +def _check_path_header(headers, hdr_validation_flags): + """ + Raise a ProtocolError if a header block arrives or is sent that contains an + empty :path header. + """ + + def inner(): + for header in headers: + if header[0] in (b":path", ":path"): + if not header[1]: + raise ProtocolError("An empty :path header is forbidden") + + yield header + + # We only expect to see :authority and Host headers on request header + # blocks that aren't trailers, so skip this validation if this is a + # response header or we're looking at trailer blocks. + skip_validation = ( + hdr_validation_flags.is_response_header or hdr_validation_flags.is_trailer + ) + if skip_validation: + return headers + else: + return inner() + + +def _lowercase_header_names(headers, hdr_validation_flags): + """ + Given an iterable of header two-tuples, rebuilds that iterable with the + header names lowercased. This generator produces tuples that preserve the + original type of the header tuple for tuple and any ``HeaderTuple``. + """ + for header in headers: + if isinstance(header, HeaderTuple): + yield header.__class__(header[0].lower(), header[1]) + else: + yield (header[0].lower(), header[1]) + + +def _strip_surrounding_whitespace(headers, hdr_validation_flags): + """ + Given an iterable of header two-tuples, strip both leading and trailing + whitespace from both header names and header values. This generator + produces tuples that preserve the original type of the header tuple for + tuple and any ``HeaderTuple``. + """ + for header in headers: + if isinstance(header, HeaderTuple): + yield header.__class__(header[0].strip(), header[1].strip()) + else: + yield (header[0].strip(), header[1].strip()) + + +def _strip_connection_headers(headers, hdr_validation_flags): + """ + Strip any connection headers as per RFC7540 § 8.1.2.2. + """ + for header in headers: + if header[0] not in CONNECTION_HEADERS: + yield header + + +def _check_sent_host_authority_header(headers, hdr_validation_flags): + """ + Raises an InvalidHeaderBlockError if we try to send a header block + that does not contain an :authority or a Host header, or if + the header block contains both fields, but their values do not match. + """ + # We only expect to see :authority and Host headers on request header + # blocks that aren't trailers, so skip this validation if this is a + # response header or we're looking at trailer blocks. + skip_validation = ( + hdr_validation_flags.is_response_header or hdr_validation_flags.is_trailer + ) + if skip_validation: + return headers + + return _validate_host_authority_header(headers) + + +def _combine_cookie_fields(headers, hdr_validation_flags): + """ + RFC 7540 § 8.1.2.5 allows HTTP/2 clients to split the Cookie header field, + which must normally appear only once, into multiple fields for better + compression. However, they MUST be joined back up again when received. + This normalization step applies that transform. The side-effect is that + all cookie fields now appear *last* in the header block. + """ + # There is a problem here about header indexing. Specifically, it's + # possible that all these cookies are sent with different header indexing + # values. At this point it shouldn't matter too much, so we apply our own + # logic and make them never-indexed. + cookies = [] + for header in headers: + if header[0] == b"cookie": + cookies.append(header[1]) + else: + yield header + if cookies: + cookie_val = b"; ".join(cookies) + yield NeverIndexedHeaderTuple(b"cookie", cookie_val) + + +def _split_outbound_cookie_fields(headers, hdr_validation_flags): + """ + RFC 7540 § 8.1.2.5 allows for better compression efficiency, + to split the Cookie header field into separate header fields + + We want to do it for outbound requests, as we are doing for + inbound. + """ + for header in headers: + if header[0] in (b"cookie", "cookie"): + needle = b"; " if isinstance(header[0], bytes) else "; " + + if needle in header[1]: + for cookie_val in header[1].split(needle): + if isinstance(header, HeaderTuple): + yield header.__class__(header[0], cookie_val) + else: + yield header[0], cookie_val + else: + yield header + else: + yield header + + +def normalize_outbound_headers( + headers, hdr_validation_flags, should_split_outbound_cookies +): + """ + Normalizes a header sequence that we are about to send. + + :param headers: The HTTP header set. + :param hdr_validation_flags: An instance of HeaderValidationFlags. + :param should_split_outbound_cookies: boolean flag + """ + headers = _lowercase_header_names(headers, hdr_validation_flags) + if should_split_outbound_cookies: + headers = _split_outbound_cookie_fields(headers, hdr_validation_flags) + headers = _strip_surrounding_whitespace(headers, hdr_validation_flags) + headers = _strip_connection_headers(headers, hdr_validation_flags) + headers = _secure_headers(headers, hdr_validation_flags) + + return headers + + +def normalize_inbound_headers(headers, hdr_validation_flags): + """ + Normalizes a header sequence that we have received. + + :param headers: The HTTP header set. + :param hdr_validation_flags: An instance of HeaderValidationFlags + """ + headers = _combine_cookie_fields(headers, hdr_validation_flags) + return headers + + +def validate_outbound_headers(headers, hdr_validation_flags): + """ + Validates and normalizes a header sequence that we are about to send. + + :param headers: The HTTP header set. + :param hdr_validation_flags: An instance of HeaderValidationFlags. + """ + headers = _reject_te(headers, hdr_validation_flags) + headers = _reject_connection_header(headers, hdr_validation_flags) + headers = _reject_pseudo_header_fields(headers, hdr_validation_flags) + headers = _check_sent_host_authority_header(headers, hdr_validation_flags) + headers = _check_path_header(headers, hdr_validation_flags) + + return headers + + +class SizeLimitDict(collections.OrderedDict): + def __init__(self, *args, **kwargs): + self._size_limit = kwargs.pop("size_limit", None) + super().__init__(*args, **kwargs) + + self._check_size_limit() + + def __setitem__(self, key, value): + super().__setitem__(key, value) + + self._check_size_limit() + + def _check_size_limit(self): + if self._size_limit is not None: + while len(self) > self._size_limit: + self.popitem(last=False) diff --git a/.venv/lib/python3.9/site-packages/jh2/windows.py b/.venv/lib/python3.9/site-packages/jh2/windows.py new file mode 100644 index 0000000..d595848 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/jh2/windows.py @@ -0,0 +1,139 @@ +""" +h2/windows +~~~~~~~~~~ + +Defines tools for managing HTTP/2 flow control windows. + +The objects defined in this module are used to automatically manage HTTP/2 +flow control windows. Specifically, they keep track of what the size of the +window is, how much data has been consumed from that window, and how much data +the user has already used. It then implements a basic algorithm that attempts +to manage the flow control window without user input, trying to ensure that it +does not emit too many WINDOW_UPDATE frames. +""" + +from __future__ import annotations + +from .exceptions import FlowControlError + +# The largest acceptable value for a HTTP/2 flow control window. +LARGEST_FLOW_CONTROL_WINDOW = 2**31 - 1 + + +class WindowManager: + """ + A basic HTTP/2 window manager. + + :param max_window_size: The maximum size of the flow control window. + :type max_window_size: ``int`` + """ + + def __init__(self, max_window_size): + assert max_window_size <= LARGEST_FLOW_CONTROL_WINDOW + self.max_window_size = max_window_size + self.current_window_size = max_window_size + self._bytes_processed = 0 + + def window_consumed(self, size): + """ + We have received a certain number of bytes from the remote peer. This + necessarily shrinks the flow control window! + + :param size: The number of flow controlled bytes we received from the + remote peer. + :type size: ``int`` + :returns: Nothing. + :rtype: ``None`` + """ + self.current_window_size -= size + if self.current_window_size < 0: + raise FlowControlError("Flow control window shrunk below 0") + + def window_opened(self, size): + """ + The flow control window has been incremented, either because of manual + flow control management or because of the user changing the flow + control settings. This can have the effect of increasing what we + consider to be the "maximum" flow control window size. + + This does not increase our view of how many bytes have been processed, + only of how much space is in the window. + + :param size: The increment to the flow control window we received. + :type size: ``int`` + :returns: Nothing + :rtype: ``None`` + """ + self.current_window_size += size + + if self.current_window_size > LARGEST_FLOW_CONTROL_WINDOW: + raise FlowControlError( + "Flow control window mustn't exceed %d" % LARGEST_FLOW_CONTROL_WINDOW + ) + + if self.current_window_size > self.max_window_size: + self.max_window_size = self.current_window_size + + def process_bytes(self, size): + """ + The application has informed us that it has processed a certain number + of bytes. This may cause us to want to emit a window update frame. If + we do want to emit a window update frame, this method will return the + number of bytes that we should increment the window by. + + :param size: The number of flow controlled bytes that the application + has processed. + :type size: ``int`` + :returns: The number of bytes to increment the flow control window by, + or ``None``. + :rtype: ``int`` or ``None`` + """ + self._bytes_processed += size + return self._maybe_update_window() + + def _maybe_update_window(self): + """ + Run the algorithm. + + Our current algorithm can be described like this. + + 1. If no bytes have been processed, we immediately return 0. There is + no meaningful way for us to hand space in the window back to the + remote peer, so let's not even try. + 2. If there is no space in the flow control window, and we have + processed at least 1024 bytes (or 1/4 of the window, if the window + is smaller), we will emit a window update frame. This is to avoid + the risk of blocking a stream altogether. + 3. If there is space in the flow control window, and we have processed + at least 1/2 of the window worth of bytes, we will emit a window + update frame. This is to minimise the number of window update frames + we have to emit. + + In a healthy system with large flow control windows, this will + irregularly emit WINDOW_UPDATE frames. This prevents us starving the + connection by emitting eleventy bajillion WINDOW_UPDATE frames, + especially in situations where the remote peer is sending a lot of very + small DATA frames. + """ + # TODO: Can the window be smaller than 1024 bytes? If not, we can + # streamline this algorithm. + if not self._bytes_processed: + return None + + max_increment = self.max_window_size - self.current_window_size + increment = 0 + + # Note that, even though we may increment less than _bytes_processed, + # we still want to set it to zero whenever we emit an increment. This + # is because we'll always increment up to the maximum we can. + if (self.current_window_size == 0) and ( + self._bytes_processed > min(1024, self.max_window_size // 4) + ): + increment = min(self._bytes_processed, max_increment) + self._bytes_processed = 0 + elif self._bytes_processed >= (self.max_window_size // 2): + increment = min(self._bytes_processed, max_increment) + self._bytes_processed = 0 + + self.current_window_size += increment + return increment diff --git a/.venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/METADATA b/.venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/METADATA new file mode 100644 index 0000000..f1d57c5 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/METADATA @@ -0,0 +1,103 @@ +Metadata-Version: 2.4 +Name: lxml +Version: 6.0.2 +Summary: Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API. +Home-page: https://lxml.de/ +Author: lxml dev team +Author-email: lxml@lxml.de +Maintainer: lxml dev team +Maintainer-email: lxml@lxml.de +License: BSD-3-Clause +Project-URL: Source, https://github.com/lxml/lxml +Project-URL: Bug Tracker, https://bugs.launchpad.net/lxml +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Information Technology +Classifier: Programming Language :: Cython +Classifier: Programming Language :: Python :: 3 +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 :: C +Classifier: Operating System :: OS Independent +Classifier: Topic :: Text Processing :: Markup :: HTML +Classifier: Topic :: Text Processing :: Markup :: XML +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.8 +License-File: LICENSE.txt +License-File: LICENSES.txt +Provides-Extra: source +Provides-Extra: cssselect +Requires-Dist: cssselect>=0.7; extra == "cssselect" +Provides-Extra: html5 +Requires-Dist: html5lib; extra == "html5" +Provides-Extra: htmlsoup +Requires-Dist: BeautifulSoup4; extra == "htmlsoup" +Provides-Extra: html-clean +Requires-Dist: lxml_html_clean; extra == "html-clean" +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: home-page +Dynamic: license +Dynamic: license-file +Dynamic: maintainer +Dynamic: maintainer-email +Dynamic: project-url +Dynamic: provides-extra +Dynamic: requires-python +Dynamic: summary + +lxml is a Pythonic, mature binding for the libxml2 and libxslt libraries. +It provides safe and convenient access to these libraries using the +ElementTree API. + +It extends the ElementTree API significantly to offer support for XPath, +RelaxNG, XML Schema, XSLT, C14N and much more. + +To contact the project, go to the `project home page `_ +or see our bug tracker at https://launchpad.net/lxml + +In case you want to use the current in-development version of lxml, +you can get it from the github repository at +https://github.com/lxml/lxml . Note that this requires Cython to +build the sources, see the build instructions on the project home page. + + +After an official release of a new stable series, bug fixes may become available at +https://github.com/lxml/lxml/tree/lxml-6.0 . +Running ``pip install https://github.com/lxml/lxml/archive/refs/heads/lxml-6.0.tar.gz`` +will install the unreleased branch state as soon as a maintenance branch has been established. +Note that this requires Cython to be installed at an appropriate version for the build. + +6.0.2 (2025-09-21) +================== + +Bugs fixed +---------- + +* LP#2125278: Compilation with libxml2 2.15.0 failed. + Original patch by Xi Ruoyao. + +* Setting ``decompress=True`` in the parser had no effect in libxml2 2.15. + +* Binary wheels on Linux and macOS use the library version libxml2 2.14.6. + See https://gitlab.gnome.org/GNOME/libxml2/-/releases/v2.14.6 + +* Test failures in libxml2 2.15.0 were fixed. + +Other changes +------------- + +* Binary wheels for Py3.9-3.11 on the ``riscv64`` architecture were added. + +* Error constants were updated to match libxml2 2.15.0. + +* Built using Cython 3.1.4. + + diff --git a/.venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/RECORD b/.venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/RECORD new file mode 100644 index 0000000..eb44b5e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/RECORD @@ -0,0 +1,204 @@ +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/ElementInclude.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/_elementpath.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/builder.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/cssselect.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/doctestcompare.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/html/ElementSoup.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/html/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/html/_diffcommand.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/html/_difflib.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/html/_html5builder.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/html/_setmixin.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/html/builder.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/html/clean.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/html/defs.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/html/diff.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/html/formfill.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/html/html5parser.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/html/soupparser.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/html/usedoctest.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/includes/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/includes/extlibs/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/includes/libexslt/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/includes/libxml/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/includes/libxslt/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/isoschematron/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/pyclasslookup.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/sax.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/lxml/usedoctest.cpython-39.pyc,, +lxml-6.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +lxml-6.0.2.dist-info/METADATA,sha256=0qIHkwlNTTMz4-c5e8ZnbbGgt_vpYZHCEoqXyckR95Q,3622 +lxml-6.0.2.dist-info/RECORD,, +lxml-6.0.2.dist-info/WHEEL,sha256=Sli93J_eWejDC8SYRdplFsirRhz71iXfsqaGlL-WXt4,139 +lxml-6.0.2.dist-info/licenses/LICENSE.txt,sha256=j8K1aBM1FuRoRdIUeRet7uFkjnCumrXtbFQXr-9M6FU,1507 +lxml-6.0.2.dist-info/licenses/LICENSES.txt,sha256=QdSd1AaqDhVIptXyGjDWv2OLPNlutyid00jYPtLkA5I,1514 +lxml-6.0.2.dist-info/top_level.txt,sha256=NjD988wqaKq512nshNdLt-uDxsjkp4Bh51m6N-dhUrk,5 +lxml/ElementInclude.py,sha256=PSLeZFvCa76WHJulPLxcZXJtCI2-4dK2CtqPRiYOAQg,8560 +lxml/__init__.py,sha256=rgOcPyZUNBFL30ylxIxd8fHHWi6TwyIUCi8Av84XWwo,574 +lxml/_elementpath.cpython-39-darwin.so,sha256=bLMQjJKxDD_TYBnSjkj8Ulaz_x-OGZyBBANoroBP9Rs,497776 +lxml/_elementpath.py,sha256=b80hM3ndAkTtRX6v54za3LkkAqCcd0700BbMPZHnTBU,10959 +lxml/apihelpers.pxi,sha256=9S6bzp-VKCUPZv0f6-el5PsbPFN4FJqSnMCIYilS0eU,63881 +lxml/builder.cpython-39-darwin.so,sha256=FMGelyMN0skDjhPYG_2wHGrFn60_atPRt0pF9XNjYhg,291248 +lxml/builder.py,sha256=KI1HxHTd4wJqqVfmTRtSbXBQdl2T-P36ih4hT-J3MNw,8485 +lxml/classlookup.pxi,sha256=Tax8Vhbm5C6UCjgmRFsYjW0pFHxIuTthH1MOgASDLgc,22435 +lxml/cleanup.pxi,sha256=ZNEpbv7qx_ICPzsxhCaMUHCOfiznOoZ_u3jlYXHAuh4,8454 +lxml/cssselect.py,sha256=_wZdX-B9p5MeIYABmENIYRWEkwXwX-7jO8Dkf-1rUZU,3306 +lxml/debug.pxi,sha256=KTcpR8-slUYvmIPbE35GoHDNTb-gjTEvD7bw6LltM4c,1125 +lxml/docloader.pxi,sha256=bYSZAxxbBEfVzfLXTUWFRfOyUTfV23L7i9hR2dgtSNY,5772 +lxml/doctestcompare.py,sha256=40EDnkwpcvW86qNa86990OXF42xdHaosSZoiBsEjkzU,17731 +lxml/dtd.pxi,sha256=IAKkmA4ZoC68sqAWcTqoS8jEGYcPQrVMCZgn4iLBYko,15281 +lxml/etree.cpython-39-darwin.so,sha256=1dxECBWNkHaqx3kW2B_rdGJJOGqhAwifHiwpIM9fJPc,9667432 +lxml/etree.h,sha256=_NkGkD3C_jpE4UegvQ6Y32_ycTbUCLyOBz9xfWRPkug,9792 +lxml/etree.pyx,sha256=2qCb8ZNjsdoB0fUELYwAM4ldLQZWS5_gt-OxKEUM-vs,138014 +lxml/etree_api.h,sha256=dNCm28ubaVS8SbhLuxs9JvYWg41NoR_yD3qTRr7hliA,17372 +lxml/extensions.pxi,sha256=xKLad35EQgpsDhs07tw31aKJBBMWIK9rMc0JTXETAUA,32022 +lxml/html/ElementSoup.py,sha256=s_dLobLMuKn2DhexR-iDXdZrMFg1RjLy1feHsIeZMpw,320 +lxml/html/__init__.py,sha256=CC5WdsvSptZhr9MZya1qsL6JKVbviYdrHOhXrGhmORg,64425 +lxml/html/_diffcommand.py,sha256=kz_7EP9PmYWuczlZcGiw74_rG0eTKvQ2lrO0rkiwlYE,2081 +lxml/html/_difflib.cpython-39-darwin.so,sha256=dMVHZht3-JzseMx38JS4F4PleEoGgocS-DeNmK0wlr4,1121904 +lxml/html/_difflib.py,sha256=GgH_jVrZQC8tI8WV_lFZQsXFJ3mOTAPup1zjBJNvkPo,84954 +lxml/html/_html5builder.py,sha256=NLaT-Ev-aBgJpeQl-6ZbJChLZK5GV-znDkHOJD5VQC4,3230 +lxml/html/_setmixin.py,sha256=8IFIOLmVz0G-XzsD2tCEkSFWO-dgPBHgvHufC8ni67s,1188 +lxml/html/builder.py,sha256=Uz3r5uiuCdoN0UPa7ngoLMwAadVIhslzGvlRPGigY_M,6187 +lxml/html/clean.py,sha256=FghSJy4jt2RaBy6dgusowkU18hxpZ4XLE5ceCK9qxyA,503 +lxml/html/defs.py,sha256=l_6nh4DHvrsVyWVqWCUUx14QiahRyZv4Melqy_thf6Q,4250 +lxml/html/diff.cpython-39-darwin.so,sha256=JRjIXbSLwiLMZgYR6HCADsQvm885MQ6IpNCYT0PlAyI,732312 +lxml/html/diff.py,sha256=Za0By-yeYlQEjUu7m7xKB288kKiy8VBS5gT0RPOaFY0,32989 +lxml/html/formfill.py,sha256=umgk0BbkAI1W6q9musFbL-cDnI_aap2NsLBJqk0UmVI,9681 +lxml/html/html5parser.py,sha256=dnyC4cqHxywjZSzk0mu2L7THTZjxhg4yF4pncjusa_w,8634 +lxml/html/soupparser.py,sha256=xo8VvNeOEb-SChuXLKCRECh8J7HBiJLE9sAbEskoUUQ,10197 +lxml/html/usedoctest.py,sha256=tPlmVz4KK1GRKV5DJLrdVECeqsT9PlDzSqqTodVi5s0,249 +lxml/includes/__init__.pxd,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +lxml/includes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +lxml/includes/c14n.pxd,sha256=DBQcOJ0c_YS245ohMb8fmuEC1kFyv1LrNY_8Mf-syZg,1110 +lxml/includes/config.pxd,sha256=H6Mrl8It21hzRI2hzMId9W48QqkYYkoLT4dniLNmdTw,96 +lxml/includes/dtdvalid.pxd,sha256=Nv0OykjYehv2lO-Zj--q6jS3TAC_dvQVPSgPMuse1NM,689 +lxml/includes/etree_defs.h,sha256=h_UjJTmNUqPyKNNrWB9hxmt6v4CF7_83XVY8dOfxqW0,14524 +lxml/includes/etreepublic.pxd,sha256=Bn4d3JkWPqXputXqI-eJ0xmPrwNFPTfDCa7axgjB7FM,10184 +lxml/includes/extlibs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +lxml/includes/extlibs/libcharset.h,sha256=GA0FumrbNI4VDGlzq3lf5CLaCwXgn4unw2l0btGQFwI,1510 +lxml/includes/extlibs/localcharset.h,sha256=Z_AagaQeq0aDE7NPsVOqEf4nO4KcUp46ggo4d0ONIOQ,6338 +lxml/includes/extlibs/zconf.h,sha256=ROVD_0UUx6mgHWSAGcLJqB0RBcv6PHfx-vbNhur6ir0,16464 +lxml/includes/extlibs/zlib.h,sha256=ilV5r3LqT0J_8ApBUPDMs_xcHkN59ybhARM7Grn8YAw,96829 +lxml/includes/htmlparser.pxd,sha256=9uASkP5dU7OE2lCOLT-z2e01qSbFlp4ehgwdostF_qk,2802 +lxml/includes/libexslt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +lxml/includes/libexslt/exslt.h,sha256=eSW5tMJAewSUANLqk7AGEiU8b2BbCNRyauHnez7nKSU,3114 +lxml/includes/libexslt/exsltconfig.h,sha256=QHxzEbRlv_h0USBvpr0Zrl0Muzlc71VCrvgR6lqnLEY,1172 +lxml/includes/libexslt/exsltexports.h,sha256=1Jm9KTXm2FUUJIZ6V6-Uw55yG0BMULX3_goyxDd2LL8,1077 +lxml/includes/libxml/HTMLparser.h,sha256=sU4xGqj-vBtEvzlxA3hBPWJboifvkc4F1hynKXmsl3k,9569 +lxml/includes/libxml/HTMLtree.h,sha256=Q7UBKFbQ8fx4d_dMnmR6ay8JmfOhopFkDp2B63YkLDU,3517 +lxml/includes/libxml/SAX.h,sha256=SFnG27EFrYGUB9HDL_xSIGBwEns5pl07rApXWThFZFM,386 +lxml/includes/libxml/SAX2.h,sha256=RfFP5o3le-Rg8bnA2GW7L7L9_pfXCs3TieODcv1DTWY,4240 +lxml/includes/libxml/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +lxml/includes/libxml/c14n.h,sha256=BSBXw6nIZutC8mWvbRrLLmoWjw3wRt-nM93vjXGMCm8,2742 +lxml/includes/libxml/catalog.h,sha256=H9ssTCaBjtDqc-AZqCk1R7h8F2iD9szqLjJyHpaczXg,4633 +lxml/includes/libxml/chvalid.h,sha256=TZcceNp6Cw0QlYwIqK9GxyYqL5UiAjpQyjt_yrZGTQE,5087 +lxml/includes/libxml/debugXML.h,sha256=XXRNI39gJW7bGRC4SzE4ad-SJ906BsUGz3AwOtkKuS4,1667 +lxml/includes/libxml/dict.h,sha256=SweaPGMtTTf4je6dNTIoEzcfEvpsAT9_PhR7FC0K-rQ,1770 +lxml/includes/libxml/encoding.h,sha256=haL7ratww2wkIERGmtwUqU2BbTVe52FZFU7MmrOpsPk,9623 +lxml/includes/libxml/entities.h,sha256=LEOCA826-0f8dhRJzC_2hvUVsSH7lKQjrea9hSTdBbo,4419 +lxml/includes/libxml/globals.h,sha256=NH8zyRI5cXJJGp5k2aLxOm-reJEGOFX6LYP82GBXRlY,583 +lxml/includes/libxml/hash.h,sha256=KIIpAYKBfGUU3ydWhGehUyfuauZz_Ps0gyambzQo_rc,7017 +lxml/includes/libxml/list.h,sha256=oh7iJNQajRA_cHsNk9CcFPYkaW2smf4J_MpedPPjC4k,3128 +lxml/includes/libxml/nanoftp.h,sha256=22PBtWhJueYLFvwukt4oFooRct_xJA83hbluHRBNXUM,302 +lxml/includes/libxml/nanohttp.h,sha256=bLbzYjAyAKmP3ComMOPH6XaUImu6bNAESF1HrVtRve0,2124 +lxml/includes/libxml/parser.h,sha256=Uq7-ce55UUAsvo4n6CiBlNQpmowewvWhOsQtgGM1UQ8,48498 +lxml/includes/libxml/parserInternals.h,sha256=8_Wr6UgRzm8BRn1RPLxyBkw6BagAdDvVqMA_e181_EI,14539 +lxml/includes/libxml/relaxng.h,sha256=VXZ74r5Yja06KqypdBHc8neDwPxQ2aMrsWHSdRt5oi4,5991 +lxml/includes/libxml/schemasInternals.h,sha256=V8M4In3zf24EX55Yt4dcfxwp7NpHGYViKnLKwtyrPJ4,26233 +lxml/includes/libxml/schematron.h,sha256=8EhPDhvtlMxl9e0C5rSbEruOvzJS5BC_OOFbq9RXZnY,4255 +lxml/includes/libxml/threads.h,sha256=mT3CgK4lXK7-NDnUOFXqYuCK6fyY70S3BsHF-TnT45k,1619 +lxml/includes/libxml/tree.h,sha256=zTRLt6h5x6ApyeXgs90CKQZSAl2hKm7b5NxtPKUQFAE,36106 +lxml/includes/libxml/uri.h,sha256=J9teJHme5z883c4twF5oImEYY-E3xSvhdSGpyRVtvIg,2855 +lxml/includes/libxml/valid.h,sha256=By61IbPvk_eLux7a8x0mOaly7oclFaSGaFE8b2xZcUE,13226 +lxml/includes/libxml/xinclude.h,sha256=K3I5jhw2zAMj26LuRNZc15Bwv2JE2hWxwVn4TCqv2b4,3258 +lxml/includes/libxml/xlink.h,sha256=TVLOkISrcKDelo9n_XIUyPiStDYa8NxuF2dz70aBFCI,5062 +lxml/includes/libxml/xmlIO.h,sha256=FvbuMYTy1-S5PScabE03wz0oWKf626pmXvOPZNuLm-w,11948 +lxml/includes/libxml/xmlautomata.h,sha256=7Sc3YgPz1ZIBKCHPSxs5oAwJEZWQ1RT2kyUw85pUtmU,4004 +lxml/includes/libxml/xmlerror.h,sha256=mMfltMxUza6kiSBfP2QfnY3UlMP_rEXKfX0wruBLl4A,37561 +lxml/includes/libxml/xmlexports.h,sha256=IyV3AMeQVbOl0wkjlnNX4B8WUZ-5GNKQmxZc6-maWUU,2025 +lxml/includes/libxml/xmlmemory.h,sha256=m7wGvVMxNzZiuOAo3vkjxaVWstc8aQLzb6obbjPsebE,4658 +lxml/includes/libxml/xmlmodule.h,sha256=ERUHUmDdZRmh6NjLYWUpse51rLWR8qNjPHOtdgmlLF0,1198 +lxml/includes/libxml/xmlreader.h,sha256=BAHinlSOTXX3DEax9BniaIIPAXJyLGfzym9R-27LCcU,12387 +lxml/includes/libxml/xmlregexp.h,sha256=_q6C1XRy8DS3kSmLbEKpvkKQciTgjTJgGc_zUQ6m22M,2632 +lxml/includes/libxml/xmlsave.h,sha256=zcEQr9sO5CsFrnoOLshhdsqMEr8k4AeFhbkYyNfO9Fs,2934 +lxml/includes/libxml/xmlschemas.h,sha256=5AfLnYUcfmxHRzg0dVpdHig--4ui1-XDwDgpKGDKCiU,7067 +lxml/includes/libxml/xmlschemastypes.h,sha256=MYwlGmoKAo3lHRaaKgnCXiLmPT9KRjdxyCJ7TEyZ6jM,4583 +lxml/includes/libxml/xmlstring.h,sha256=d5PpqxP1I1sfmCUHvVJtjoC9h7hLHcAAQ5ok_Rtf50I,5271 +lxml/includes/libxml/xmlunicode.h,sha256=8sq3wEW2AiyTCuc3ZceOEkce7lfrI7VnkRfwEQgc6pU,278 +lxml/includes/libxml/xmlversion.h,sha256=oVpaE_xbttaeZNFKSuSfcLOceWz7LQgKP71Z1msXZNo,5112 +lxml/includes/libxml/xmlwriter.h,sha256=BEUwYNKx3xymDE9vepksEK7yVq9SXYm1d2pQnzlPy90,20688 +lxml/includes/libxml/xpath.h,sha256=CQv6X_pRhuXoCVpqoDXYB7FfusLK7AuPxCNigwhNYAA,16156 +lxml/includes/libxml/xpathInternals.h,sha256=mc9B5tdpfssyz_NPUzww6dKuWCtBybBiBRJkTe4AE4U,18504 +lxml/includes/libxml/xpointer.h,sha256=DAxMsfPp2SSZgXFrPbxBA84RwTMRf35Qg_LBbUzPQhA,1026 +lxml/includes/libxslt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +lxml/includes/libxslt/attributes.h,sha256=qKwzfGf7r89esLC65s96iYJWRA-s-Ezss2_V6Mmo1hk,957 +lxml/includes/libxslt/documents.h,sha256=kBihgH5pqRvFalhm_fOFHtJTFhTpBcm681yT5dxgwfw,2704 +lxml/includes/libxslt/extensions.h,sha256=W5UMyJqUP_1zt6sXZ0mgc0gAIwDJrZ8gjByhyrWqvd8,6899 +lxml/includes/libxslt/extra.h,sha256=6X3Wu3NdPtrlqz-Koo7dB-rccnnszi6j3zg599gTByg,1640 +lxml/includes/libxslt/functions.h,sha256=fc4CZj-9KeBHzO9-WWU_bNqmaEZAz3n7NNwClIBXk14,1972 +lxml/includes/libxslt/imports.h,sha256=18kIjoGqdFXR63Ce3ZtzxsTiYV3XGKpchYakMUPDuUI,1840 +lxml/includes/libxslt/keys.h,sha256=16v25VEluS7jYhgg6gYFwVxgGMn-1ctnlhhWWT4RcBY,1155 +lxml/includes/libxslt/namespaces.h,sha256=VofSn2Kkn-a5JyRKCmY3jPp7amQy3n09vzy0KUQt4q0,1666 +lxml/includes/libxslt/numbersInternals.h,sha256=Eg5gYZ5p3h0_e5wyI61S-0E6_ArVJzv0yr63j6BU2fc,2019 +lxml/includes/libxslt/pattern.h,sha256=tJ-BPfs9UYgiZMMoQZbhij3g7xVppYq7TrrOu25eR7Q,2110 +lxml/includes/libxslt/preproc.h,sha256=D_LjEdHhsdyBnEAvflnwFgoR4hGUb72kgEhXkkmPRsw,896 +lxml/includes/libxslt/security.h,sha256=fUD1cy_WxFCTvTNAF0WOQIU4p5CNWn1LHFyZJd-Fx5U,2652 +lxml/includes/libxslt/templates.h,sha256=bnt6Jqui6KU5pNUdMNPbQZkZ5d-VTWqC0TMGkOlVoIo,2268 +lxml/includes/libxslt/transform.h,sha256=ICT7meUV0OTAx27WaKVrKj-aUmR9LSpTNaOAJd2UStg,6311 +lxml/includes/libxslt/variables.h,sha256=cQAgPe4QCcK2uKbWg7Iz-9peM9xWGm7m3M6jQm0sjIA,3143 +lxml/includes/libxslt/xslt.h,sha256=wmFx2Q31Pd8Iq2phAQpY9J3QQatb8lWg3gABtqKFgEw,1964 +lxml/includes/libxslt/xsltInternals.h,sha256=2EbEKYmnYZq0HjGnUMAlpqnqZJurRXzjlgk5Js1WYaY,57949 +lxml/includes/libxslt/xsltconfig.h,sha256=cV5scdRK6xmOHeOg3OCw6hBfcQ_nrtNs_tKefX67304,2910 +lxml/includes/libxslt/xsltexports.h,sha256=1-luH-0bCIgBAlKAXhV-dqHBfwOAQNDamiYbxIlTf0k,1124 +lxml/includes/libxslt/xsltlocale.h,sha256=ppxGEmJfZIJgwRQzCM0_77p9WNekEWq1NrdYZrQl4IE,942 +lxml/includes/libxslt/xsltutils.h,sha256=1eguYgR9-jeNOVlBUktHboaq-VLX6JXraO80TfbARKM,9085 +lxml/includes/lxml-version.h,sha256=KZfk_lJnXSnxkyRdUV5taHsWJe4xbC6UEYfYldlfouI,71 +lxml/includes/relaxng.pxd,sha256=HzHlQ6mCcf_tj_JZ9NAVJTVAv8ScCkE8Ifq15y3bS0c,2615 +lxml/includes/schematron.pxd,sha256=Hob7xh-K-MKqp7WiG8thMagf5EkQzmgfi4ds0EF91JA,1604 +lxml/includes/tree.pxd,sha256=XApzMRy_LSqCtQ-OTS-vNSW7CT_OWstybfIT2H84LsA,20179 +lxml/includes/uri.pxd,sha256=3vOXw6AbSPxAM9uo71T1qnfx-wd9ezXLDQtWsb2zX0I,145 +lxml/includes/xinclude.pxd,sha256=CuO_XZNB6E2JK1qXXWn11APrjFQV5kA6SMyb77WZn0A,804 +lxml/includes/xmlerror.pxd,sha256=OQqayytkV0NigAPbsQCCcvmy7luRe0XhVzpTdzJjP3g,58837 +lxml/includes/xmlparser.pxd,sha256=eDGyU5kZyNVksK0dUhMIi7rnE-LSevXsqyl72v99Ess,13730 +lxml/includes/xmlschema.pxd,sha256=OLZPd2WDJyopiXJJyo-dAyyYHaeSYFiMAI4tqIiv-Ik,1702 +lxml/includes/xpath.pxd,sha256=e8-ZYUbRG7N1mHETAlknJ_QqAteOosrYLRgpH-OsTkg,5603 +lxml/includes/xslt.pxd,sha256=4yl3pOu7pAvsx5Tc-W4IWCoB8wgtSSR62HI1jqu6jko,8241 +lxml/isoschematron/__init__.py,sha256=uauerYeKTlWFCJSqieIHhF5l6rYV2myeEJ0Imd1LzRc,13274 +lxml/isoschematron/resources/rng/iso-schematron.rng,sha256=VsWxPyi3iViJDDbjJJw0wWkEHkLrz9zoCA8zJLor9N4,18337 +lxml/isoschematron/resources/xsl/RNG2Schtrn.xsl,sha256=ObebsB8Wt-d3uIA_U5NU85TpnQ3PxPX38TdOAqosMac,3172 +lxml/isoschematron/resources/xsl/XSD2Schtrn.xsl,sha256=QweRrIIM-zFcgg98GXA2CaWfIbgVE0XKEeYSfvv67A0,4563 +lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_abstract_expand.xsl,sha256=xSZ_Ekq_I-62ZpiE5AqYYHwFW_qh855zt9V4_s7rbkY,11703 +lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_dsdl_include.xsl,sha256=x42QJ-dxQ1waPzydsCoQnp2Xj15y53nW43O7BuoDRHk,39957 +lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_message.xsl,sha256=Tr9BnO6pzjVWwhqJfm10UlvAy95EgfSCz2iMlrVGT6Q,2015 +lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_skeleton_for_xslt1.xsl,sha256=ue8q_88X4e_jsJizo31GRNBxNhdxkEE9fY20oq0Iqwk,71764 +lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_svrl_for_xslt1.xsl,sha256=BBAdsVSi5zAzeGepuN6gS1saQINDqITXKplmmj4dTWg,20382 +lxml/isoschematron/resources/xsl/iso-schematron-xslt1/readme.txt,sha256=OGLiFswuLJEW5EPYKOeoauuCJFEtVa6jyzBE1OcJI98,3310 +lxml/iterparse.pxi,sha256=JXvYhSOCaRjT_hYbRGMlJt2rlqx0TiRpN4FE1jQc63w,16521 +lxml/lxml.etree.h,sha256=_NkGkD3C_jpE4UegvQ6Y32_ycTbUCLyOBz9xfWRPkug,9792 +lxml/lxml.etree_api.h,sha256=dAbJPd53D_9CIGzePAUB3otgyhG4o2cSdA4-6apdzRA,17377 +lxml/nsclasses.pxi,sha256=5pzNBhBtlqObPdThL9QIGRs1Dxj1qnr0PyXuTCURqTg,9129 +lxml/objectify.cpython-39-darwin.so,sha256=ViaJLjaF61LbuAfG1W_JQjiw94eI0t4W6Fo39Vr7qiA,5235008 +lxml/objectify.pyx,sha256=I4bQQXmQssBtk5bTrid-eVURBLKRTM5iQZiviugIrts,75823 +lxml/objectpath.pxi,sha256=s5TNG2-EbaWWKLFAiX303B95zK_Ui8ausB__3QvFFGw,11450 +lxml/parser.pxi,sha256=VZfychEJ3-XPE3x6oGOEzn6HVAr74R7lXfDSVF-hq-U,85411 +lxml/parsertarget.pxi,sha256=v1PidxRaG5giwXcTDkpBI7PDFmsZuOcK0y9LdkQaY8M,6326 +lxml/proxy.pxi,sha256=8IVvYF2KTuzl7Hb3XGHEmcxfSLbUZkA2Q1Y50hLsyzE,23929 +lxml/public-api.pxi,sha256=XoP6_cJOEoQIItvE1RiYCKYD1ry4AobaOr4XLo0KSE4,6666 +lxml/pyclasslookup.py,sha256=gLD1HM2HtITYYiGzjEOewSwbB7XkVx_NZv_quCt79Oc,92 +lxml/readonlytree.pxi,sha256=ddRYczhHieJ4XUvWvTPW9N9oQ8vuKtv7lC1mtE1qvH8,18976 +lxml/relaxng.pxi,sha256=3OQ-fZMzP-KF5vM6HTozT_9ee3J0DJnpj9RcHC8LoMw,6339 +lxml/sax.cpython-39-darwin.so,sha256=S7bAW0Qog3b2gbfTY143QChGQgF_F64jRRQbBZafJIM,416008 +lxml/sax.py,sha256=yrNvKD6rlon48jrR-1qpFXER8j4psYC2R5yt0u6TWLs,9706 +lxml/saxparser.pxi,sha256=TmkdM5h9xII9iKRaBk_1NGk2KTfeesl5Ha8bpFQGqLc,33529 +lxml/schematron.pxi,sha256=F2OHKZUl57-byBk_wWtPTnHZ1fwlj0FtwG3VuGtG-UY,6064 +lxml/serializer.pxi,sha256=iIXfechFHfvFs2sTk7wMIy3sDJxmaMPbNO33mkLLBUE,68063 +lxml/usedoctest.py,sha256=qRgZKQVcAZcl-zN0AIXVJnOsETUXz2nPXkxuzs1lGgk,230 +lxml/xinclude.pxi,sha256=7eBrI_OK47mmrHQ0ixbixRI8pKqQ1nwkMV-OmKUVlD4,2456 +lxml/xmlerror.pxi,sha256=i1kR42WB2BAxtrmh7m2ADlH-jffVQ-blW3pW0Ps4s-g,50061 +lxml/xmlid.pxi,sha256=5zf9oR6bsCtavGiOmilNyHqYwgG_bnrIabSd2SURtm0,6073 +lxml/xmlschema.pxi,sha256=mumNoHni5S3BQPtcmOHRd61KRaVWu4eOie2wQeB0e6E,8490 +lxml/xpath.pxi,sha256=aqW24V817dUxps4Gnc8h7Tm3QVlITKvxU5_9WgJUIFg,19132 +lxml/xslt.pxi,sha256=wxdbuvNFVA8eP57tHmBYWER__ceFhf6HGdsbBHbx_0A,36315 +lxml/xsltext.pxi,sha256=TImDiAPlAezC07P7RY1N9YChA7AuKFH-G53hXdel9yc,11088 diff --git a/.venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/WHEEL new file mode 100644 index 0000000..e752d95 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: false +Tag: cp39-cp39-macosx_10_9_universal2 +Generator: delocate 0.13.0 + diff --git a/.venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/licenses/LICENSE.txt b/.venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/licenses/LICENSE.txt new file mode 100644 index 0000000..0bdf039 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/licenses/LICENSE.txt @@ -0,0 +1,31 @@ +BSD 3-Clause License + +Copyright (c) 2004 Infrae. 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 Infrae 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 INFRAE 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/.venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/licenses/LICENSES.txt b/.venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/licenses/LICENSES.txt new file mode 100644 index 0000000..9f97c18 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/licenses/LICENSES.txt @@ -0,0 +1,29 @@ +lxml is copyright Infrae and distributed under the BSD license (see +doc/licenses/BSD.txt), with the following exceptions: + +Some code, such a selftest.py, selftest2.py and +src/lxml/_elementpath.py are derived from ElementTree and +cElementTree. See doc/licenses/elementtree.txt for the license text. + +lxml.cssselect and lxml.html are copyright Ian Bicking and distributed +under the BSD license (see doc/licenses/BSD.txt). + +test.py, the test-runner script, is GPL and copyright Shuttleworth +Foundation. See doc/licenses/GPL.txt. It is believed the unchanged +inclusion of test.py to run the unit test suite falls under the +"aggregation" clause of the GPL and thus does not affect the license +of the rest of the package. + +The isoschematron implementation uses several XSL and RelaxNG resources: + * The (XML syntax) RelaxNG schema for schematron, copyright International + Organization for Standardization (see + src/lxml/isoschematron/resources/rng/iso-schematron.rng for the license + text) + * The skeleton iso-schematron-xlt1 pure-xslt schematron implementation + xsl stylesheets, copyright Rick Jelliffe and Academia Sinica Computing + Center, Taiwan (see the xsl files here for the license text: + src/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/) + * The xsd/rng schema schematron extraction xsl transformations are unlicensed + and copyright the respective authors as noted (see + src/lxml/isoschematron/resources/xsl/RNG2Schtrn.xsl and + src/lxml/isoschematron/resources/xsl/XSD2Schtrn.xsl) diff --git a/.venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/top_level.txt new file mode 100644 index 0000000..ab90481 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/lxml-6.0.2.dist-info/top_level.txt @@ -0,0 +1 @@ +lxml diff --git a/.venv/lib/python3.9/site-packages/lxml/ElementInclude.py b/.venv/lib/python3.9/site-packages/lxml/ElementInclude.py new file mode 100644 index 0000000..2188433 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/lxml/ElementInclude.py @@ -0,0 +1,244 @@ +# +# ElementTree +# $Id: ElementInclude.py 1862 2004-06-18 07:31:02Z Fredrik $ +# +# limited xinclude support for element trees +# +# history: +# 2003-08-15 fl created +# 2003-11-14 fl fixed default loader +# +# Copyright (c) 2003-2004 by Fredrik Lundh. All rights reserved. +# +# fredrik@pythonware.com +# http://www.pythonware.com +# +# -------------------------------------------------------------------- +# The ElementTree toolkit is +# +# Copyright (c) 1999-2004 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- + +""" +Limited XInclude support for the ElementTree package. + +While lxml.etree has full support for XInclude (see +`etree.ElementTree.xinclude()`), this module provides a simpler, pure +Python, ElementTree compatible implementation that supports a simple +form of custom URL resolvers. +""" + +from lxml import etree +try: + from urlparse import urljoin + from urllib2 import urlopen +except ImportError: + # Python 3 + from urllib.parse import urljoin + from urllib.request import urlopen + +XINCLUDE = "{http://www.w3.org/2001/XInclude}" + +XINCLUDE_INCLUDE = XINCLUDE + "include" +XINCLUDE_FALLBACK = XINCLUDE + "fallback" +XINCLUDE_ITER_TAG = XINCLUDE + "*" + +# For security reasons, the inclusion depth is limited to this read-only value by default. +DEFAULT_MAX_INCLUSION_DEPTH = 6 + + +## +# Fatal include error. + +class FatalIncludeError(etree.LxmlSyntaxError): + pass + + +class LimitedRecursiveIncludeError(FatalIncludeError): + pass + + +## +# ET compatible default loader. +# This loader reads an included resource from disk. +# +# @param href Resource reference. +# @param parse Parse mode. Either "xml" or "text". +# @param encoding Optional text encoding. +# @return The expanded resource. If the parse mode is "xml", this +# is an ElementTree instance. If the parse mode is "text", this +# is a Unicode string. If the loader fails, it can return None +# or raise an IOError exception. +# @throws IOError If the loader fails to load the resource. + +def default_loader(href, parse, encoding=None): + file = open(href, 'rb') + if parse == "xml": + data = etree.parse(file).getroot() + else: + data = file.read() + if not encoding: + encoding = 'utf-8' + data = data.decode(encoding) + file.close() + return data + + +## +# Default loader used by lxml.etree - handles custom resolvers properly +# + +def _lxml_default_loader(href, parse, encoding=None, parser=None): + if parse == "xml": + data = etree.parse(href, parser).getroot() + else: + if "://" in href: + f = urlopen(href) + else: + f = open(href, 'rb') + data = f.read() + f.close() + if not encoding: + encoding = 'utf-8' + data = data.decode(encoding) + return data + + +## +# Wrapper for ET compatibility - drops the parser + +def _wrap_et_loader(loader): + def load(href, parse, encoding=None, parser=None): + return loader(href, parse, encoding) + return load + + +## +# Expand XInclude directives. +# +# @param elem Root element. +# @param loader Optional resource loader. If omitted, it defaults +# to {@link default_loader}. If given, it should be a callable +# that implements the same interface as default_loader. +# @param base_url The base URL of the original file, to resolve +# relative include file references. +# @param max_depth The maximum number of recursive inclusions. +# Limited to reduce the risk of malicious content explosion. +# Pass None to disable the limitation. +# @throws LimitedRecursiveIncludeError If the {@link max_depth} was exceeded. +# @throws FatalIncludeError If the function fails to include a given +# resource, or if the tree contains malformed XInclude elements. +# @throws IOError If the function fails to load a given resource. +# @returns the node or its replacement if it was an XInclude node + +def include(elem, loader=None, base_url=None, + max_depth=DEFAULT_MAX_INCLUSION_DEPTH): + if max_depth is None: + max_depth = -1 + elif max_depth < 0: + raise ValueError("expected non-negative depth or None for 'max_depth', got %r" % max_depth) + + if base_url is None: + if hasattr(elem, 'getroot'): + tree = elem + elem = elem.getroot() + else: + tree = elem.getroottree() + if hasattr(tree, 'docinfo'): + base_url = tree.docinfo.URL + elif hasattr(elem, 'getroot'): + elem = elem.getroot() + _include(elem, loader, base_url, max_depth) + + +def _include(elem, loader=None, base_url=None, + max_depth=DEFAULT_MAX_INCLUSION_DEPTH, _parent_hrefs=None): + if loader is not None: + load_include = _wrap_et_loader(loader) + else: + load_include = _lxml_default_loader + + if _parent_hrefs is None: + _parent_hrefs = set() + + parser = elem.getroottree().parser + + include_elements = list( + elem.iter(XINCLUDE_ITER_TAG)) + + for e in include_elements: + if e.tag == XINCLUDE_INCLUDE: + # process xinclude directive + href = urljoin(base_url, e.get("href")) + parse = e.get("parse", "xml") + parent = e.getparent() + if parse == "xml": + if href in _parent_hrefs: + raise FatalIncludeError( + "recursive include of %r detected" % href + ) + if max_depth == 0: + raise LimitedRecursiveIncludeError( + "maximum xinclude depth reached when including file %s" % href) + node = load_include(href, parse, parser=parser) + if node is None: + raise FatalIncludeError( + "cannot load %r as %r" % (href, parse) + ) + node = _include(node, loader, href, max_depth - 1, {href} | _parent_hrefs) + if e.tail: + node.tail = (node.tail or "") + e.tail + if parent is None: + return node # replaced the root node! + parent.replace(e, node) + elif parse == "text": + text = load_include(href, parse, encoding=e.get("encoding")) + if text is None: + raise FatalIncludeError( + "cannot load %r as %r" % (href, parse) + ) + predecessor = e.getprevious() + if predecessor is not None: + predecessor.tail = (predecessor.tail or "") + text + elif parent is None: + return text # replaced the root node! + else: + parent.text = (parent.text or "") + text + (e.tail or "") + parent.remove(e) + else: + raise FatalIncludeError( + "unknown parse type in xi:include tag (%r)" % parse + ) + elif e.tag == XINCLUDE_FALLBACK: + parent = e.getparent() + if parent is not None and parent.tag != XINCLUDE_INCLUDE: + raise FatalIncludeError( + "xi:fallback tag must be child of xi:include (%r)" % e.tag + ) + else: + raise FatalIncludeError( + "Invalid element found in XInclude namespace (%r)" % e.tag + ) + return elem diff --git a/.venv/lib/python3.9/site-packages/lxml/__init__.py b/.venv/lib/python3.9/site-packages/lxml/__init__.py new file mode 100644 index 0000000..58c2133 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/lxml/__init__.py @@ -0,0 +1,22 @@ +# this is a package + +__version__ = "6.0.2" + + +def get_include(): + """ + Returns a list of header include paths (for lxml itself, libxml2 + and libxslt) needed to compile C code against lxml if it was built + with statically linked libraries. + """ + import os + lxml_path = __path__[0] + include_path = os.path.join(lxml_path, 'includes') + includes = [include_path, lxml_path] + + for name in os.listdir(include_path): + path = os.path.join(include_path, name) + if os.path.isdir(path): + includes.append(path) + + return includes diff --git a/.venv/lib/python3.9/site-packages/lxml/_elementpath.cpython-39-darwin.so b/.venv/lib/python3.9/site-packages/lxml/_elementpath.cpython-39-darwin.so new file mode 100755 index 0000000000000000000000000000000000000000..5343c031a906da72b076da9dee6876f0f227050b GIT binary patch literal 497776 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

T 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℞j=4yK{2Zhsg>{e(=-52yMMp#(mqSMm(jY$v={TKrkPW3{c$bJe zGNEG<(jY$v={S*fkPW57>>g3aInZ%F(jY$v>4;+;WJBqAJ0YTu)1YH0(jY$v>1f%l z>j&9TIx2q?QO9x6(FbXepM!M#n{|*4rNh%dqK+8oI0|WypM!L~&pOD4(lPw>h&sOA zjd3K>B0mS|c!hP44W(n{SrK)73LQI;7Wp|yM;+@R8%oEqk?Yvo(6JS1ke`EetY#f# z13GpM+7`KvH9*H}NQ3+wq~mthK{k|*7mtp3El)tlvq*#dC>_>yC)7uxe+-}ffc-c% zs{KO0_|HzdUwjnuM=J9^%RF!x8I0d{4f@TQsvj16ud(d^{Rgu3EL#Ry7p|$w)_$g< z{iOGq(!X!Rc^g085bnz>Vlf6k3$h2b$tK8T;g5K#q3m)~2L|=nE>;-KfFUuY)m2b>B z&|Hz5t_kl*%hBf~V18o~u;TaC{UrKr%vE6QkUz5WQt$g z`hfkMb5>g`2~47qWkP8+!N9-N%>8NtfJE_ z&s}J1-^U(exzAp69(9kGm?c~ZDV0#;yinyu{+R#<7*-;z%2k@i~JpuVqWt*br z=4$E8x0~b3rd)H^k+-3@SgtP-?Jr8NM$Etk7-lMDUdV z`MJv4=Bo5<|H~cZf4n9C=SIl?cbH4g!93x~ zw)`JSyD}+4K2~;+kAaqa^p22^cR3&X&Gvp2-yao0=2e#Y+9q>I-?u$mDBoiZwDSFv z$oG+4bB_quoY%oMe~$0uZxh6*_@9GTRsly`<3nM{jHFtdOzg|egAZX{CDag|4S|TpC2Lr=Wzb<9Dn5Z z?fUllzk>7M?+Ek1C_?_<`x5PS#Qx00TBVNr-|VlbPqU7bk6U{h;;2vi1npsO(9#~a zeA(8oZ3TTYc;!e#eeid{&G_c(HHhn|U;7q$yaIXeD4%3gwQ&)~37hf z*@v)^ZEpij{m6H!&pmGKlfaZ-^8E(rMC5JEA@a5fc{_{pW?MV74Yp*SDAtTWN97Oe zujrkXyf>pYL_cBNfwXGP(;FzyRE$-^V+l@A=k{P+a3@_uKJtt9GK+L9gl+u}UGG64 zeO)c>oU&vmJZm3%BsLzm*s%JA*2by&oTlG> zhJ7+4w$jHwNyc?Zz6=^`;ky?Yuc)y;od@r~ovv5s-|9P>6Wpf80eU=wGT`S?b1k)L z=UaR>3UnmhGnDSMnqEifOY9K|*PA2p3)UHg(@(~=#p8L%aGdhKqA`aQrmeh){GxEV;G7tyH^+Ci(A_O`4-0*~g+9?jqk zy$RDZE%Yo4jWb@tX^Jd#z(QYZp|7{lH(BU~7W#J<`c?~ln}xpJLf>tn@3YVkSm;$2 z`e6&b)vgqPc8Ik7W#7w z{gs8@ZK3yS`clk041ejE%3T+}P%{j4eCKfe?@jNy&<`1C|9jJWF09K}G_5PVfN46* zC5LHRk9Zx^w61VD)BgsIv4WDNHMZ|Ey$v+RHY)yO&@D{=9rUpnjF8=}pa(Kd>vYF3 zO=ojl#x$K>SHScp(6=!CI_UeDeiif+Ow+d#-(|W1G@iMXt>;1GnNiVy1l^D6=Rl8S zn)VLkxm~4s67(#l9|wIC(~p9_gXwC}Pcr=w=(m_&1^R2I9|YYMn=I&hSAag5>3czs zV)|~-SxnynI>>Yd=%q~4cWCOEz7_O)Oy2@}2h(&e$^oWt27Nph#*$r}dDv?Z(>H(~ z&2%Z~%a|?!UBq+%^b)4&43!m3`#?X*bOGqsn4SgtBc|zG{qLC00o@&opD7PlfySJ^ zqOSmbKGV}dU%_-1=($W^3i=kNadu&^RZL$DdIQs$pxJPdv(|tjo!}Q6Z zr!svK=s8UH295P*O7=L=4=~*W^b1TM19~gd-9UfObR6h-4A{wTXV6&Jq-ZDT^O!b3 zU&-{3I7{nBruTuqm+9|8KgskS(62E44d_pp{t9#p(_es&kJGlAK@Vd3U!X@bjdK`# zO=0>I&{&hN^7avEI((n<`~m1SOur9$J=5=k-oo@|(Ab}$(*Ff?XB=8YvTuMsmFd?& zr!u_}bRN^#2i@xyrvD830jB>1x{m33(DYGr()kSNW~QG4-5DSBp!g?1Co;VbbPCfo zpgl}K4ElPeSA$;7bQS0Zrtb&6o$32Pcg4mX(ti)=Q<=UK^cbcqK~HD;HqgaP)7fZC zm@Wr>57WN|jqmX&oeM$J#vn9(Mk;>Ao`AO~WEj5ELSK^G>+==@7JEYZMlg_j2F0FX zjZG?^XQUO*ESO#5FA2Kxyt6zdzL2ZPQ|JvAdvd+5;(-6!f;?~Dxh^9u6bcl~ED3qX z1_J(oF}^6zTdK*6z4J6NHRLZIAMyq~p#p!AF?C*1$iqo4^Y}_waHx?=|BoJMB;iNl z(8NLX8)}?kBpFFb#tp_9XBbx+7Z`5iT;oPu)?ni*V_-jHpy3J5E6OeKC*}GB{*q8Z zkvC|#XOV9-6=8-nISx7*0|c|8IA3(M|;B6lbNAI6sc?ChwOeDmAKK$=3RR07GZ((t0p3VVf*c%Lz z3%x}uvD-bXq$n5mXXhHo4E?yA3ySgza=k%UL6Me)BqBT^M4=+Mh7X*3!91e? zQg9Ld6Ufa+fuI&D(55!1aL8L4GT_w^yj$pj58XzgCzP9Sgk2OgP-MiW0BjW+{^B6j z?I5!3_DD@c3@WxNHaNmt>N?CjuGrw^K+9X#F8rqC3)-9c|i z)o~P8uwZtPCscwAAQ}ZhQN=#gk6pmqv3XNebgBx-lWrcPQOmz0J}YCYYLK{OS*0i+vq7StHwe>^^) z0;(LWeS*3b4WHkW=cTgM1f1j#6wEFtLd-0*bjp?JBIbBz6CEs>$u16}h0M)|6j~uP zVQ8Rr+w4YIgMfzu6LoHdy$;o-4e6Q=efWK(AD~Q`^h$|GB=L&fB zwNd|Zh>RrRo+>Pa*T`DXUFZ#Y)Qx^95xy+B(cS(4$>sa=jFjAY66+c;&E*NX1{9x< zMq{RGx^*Ibol_GBxr)5rJd}J9y205cGzOx(={kP^k%c9}5Y;R+NVtF?ZrnM_wc(@^ zqsK|R6xPMo!g4`F7Xh+{B2FA|eV%J} z!L{C^LDqOwD->KwuD-j_k%#=VW>MQp?bfv@D}R7YTOzOXdgk~Ff^F%@MeaO*E(Gu| z1XTnzbDAQh#6uv=**^bF43-eflrX6A(+xROVTM1CZh>3`62&7P(jp{SZugw)+Ceqy z(hY-R>-M*;NW38z?sE^9jf-ojwXHr}TB#!gF`UIk;%4)p#uj?!shbDIo`?RMMhEaK z?(EsF$O0-CMgF3p;xIil4!Ye3XCPEk?DI-qb<2kP#_wvYut`?0xUMTe^`Toiw<1eAtc^9sB^ zJioYr=bsfguUR717YI~&H z;rnRfv-k z4Wof9hdf1iRBUsS+-&)tYte9`q%gw4BZ)2pgIX}k+<+;rAadZ#Lm!QFy3lY%XtTpQ z(Ltu;0cNcDUq8qUm?GJ0v#FlcEE&asL`T_Gz`lC&D=x^L16QHrrJix1+%vNa+^WjV z3it~R?KboklwTS_;VSZ6Gv~Pmsx&T2G;m1R6_BMSQH`IdZ>O#^Tw(?dHw$JJKmj#+ z2lF94D?sh^IQlmtA0vYzx)BZ%G)=K71hZuDy<)VkXS!UjDGy0lZmSIr|H zk1OBnDORHb19y!V)ex0k4HCjhX)sXacS+yj3i|w^Audb^xRm095})hxqKaXJ=E=j2 znu~5gHOOHF1F4B57ugJ=VkrqcKEMJsxT9THCM6|Z6&48FJ2FQMr)LB8tft57N94UT z@l2zh^NuL>SwAavayUblr-37~bN0`&Gva5ZJ|~iUuScjV79GwNrh9zN{n zB_ICtlK;F?9{%%+d3Z8rj`Y))y5k29n1;Se58`n*U=Aq{QyunU>cREEejg7Aq63+Y z;R9OA64I|9q3k10G;Pxc8sWr+X!HwcoWkP^8n|{KqFO}Q=y1f0u{S2dFoy%8(2tNu z%57s=Jq{j_G&1O-p>DB1h$$xw#eCrbF&=o@kAv}yWS1l+Z!;#iMY22KSa(9<^~FiLuv-^8h=3%Jj#79l2ERF z^i)OV5bme-BZ;M(SKV-4`{7E2yYg@iTswXew`{WlUh;3CfO3dyMJI^KFE2)gRAG#D3Knq>I6~h+|6e-Z2|Y`Za$WW$+viV-vURHO5q17Pwr!_n!zp!;$tZ z!`VL>8607B$~ebxZXanl;zt?Aicy9$=RBj+_R)r;{(Qs8xWI5Ery1tPG^4X`oMALx zXml#R$Z&3$U>Mbx7>-Ss7-q^u!;v%5FgGLIKhbawnSzaMS%$ND8W!qZW|%S44I^=e z;jEv5>$%);j=aJ!w&TQ$tSb$pG}~~-US$|Vu7=L54Wr3zIJ0vMBYvjgteR;U8*>e3 zR-R$hdktstEW_9`+i)(+$0EN1!+$Evdl4! z>>M*DWr^wVEi*fnk)0w^6bkwg#xW??9_mFAqf7I+e`7tx5Hx@qZdBSw&KWWBf*FjIc>6}q-nho`) zBY(YVtXOY4wj8N_yG&dvM@-pN%nr7`r&3QB@Ve>f{krMg__}HCA^Hu|od1UDD1XE3l=`OWY-7vvG)gttsb@mr?31z`)q#=n}5oqsjW#hXn>^=8w|cpKOHwrR$^V>;sB zF`Zdh0MhiXX_js=jg4DOv+h09h=1QSd!l&x7KsK?4@5l>^+41EQ4d5t5cNRR15pn| zJrMOk)B{luL_HAoK-2?K4@5l>^+41EQ4d5t5cNRR15pn|JrMOk)B{luL_HAoK-2?K z4@5l>^+41EQ4d5t5cNRR15pn|JrMOk)B{luL_HAoK-2?K4@5l>^+41EQ4d5t5cNRR z15pqB|HcEKH0yokrr}a~J_;YjqQ8NR&t!azk)z_%7$3!W0^>Nw(-?PWoXfZyW4}%Q zdd3d6H=nVS@dC!37%yfV!?@grm)UTIP5v&%V(%UszL&AsUt!}vVB@c}@mJgMLt(7_ z_edDKls{{1_)!~v%!Z$^;U^hOe(P-fXBqco|JQwGD!a!qex3QJG5(ry7sh)TcV%qQ zA^Z62&;IYvxCiq`Fz(Dag|XNl$o7RliTSademdhWtp92oe->l0R~qKKRKCg>J6PXB z#!kk+W!#DJt&Al<6^xH(`wugg{IqiZMgAwolK;;5ARYb5{qPve3x1ZdlvlG&em(PX zh=9`bJY(_y<2L`j#{6W??+=XS{)_uU=cgz0`!GJ5@gT-M7E-zU22+#^S#>8K1!Avz@Wjx1Eed z|1QQ-Ui%n}J#(kN-hQlaw5_~PW&X)r9>vU;`!|jGr!s#L^QC{tWBw`3zn%GVeT%~O zb^Xp|zSM_@nExA=zs$ye$;LNr{7;xK_3=~2a{W6PU%}UZG0O{ojII3pFki}J0`n!m zmopZB=i20bjHSNq=JJ>F3@~5pPhopfp7U(_7BZIn^yT!T{}!9Q-`nsVjC=9*uVs83 z;};l<{c4;3mzf{O={GT!_F^cfm;3oc=1cuijfUl)`p4-M9DKKcL!(w>Nm9!3jZd_X zpKQa`htVVYqG8knQ4d5t5cNRR15pn|JrMOk)B{luL_HAoK-2?K4@5l>^+41EQ4d5t z5cNRR15pn|JrMOk)B{luL_HAoK-2?K4@5l>^+41EQ4d5t5cNRR15pn|JrMOk)B{lu zL_HAoK-2?K4@5l>^+41EQ4d5t5cR{AOsi`u!heP9^8h7KcH;4T>9L8Y^hnXB^ zahSuQkHb<97jsy_;R+6`IjrNbfx}H4Zsl-0hr2l3&tdF#o!Mla2SUv9A#1`aoIxRt~09PZ+9KZmjZ=JMyz#o-VRM{=0K;bac8In3uU z#92`3sZmYGvD zA#GRMpY5Zxj`MA*E?~UkTur1h&b+mKdlb7=V`+~zGiEWCGK|Sg%})tOwIk$wh;^K? zSmxG!r}Hc4Gc-P;vHsUEy6@GLoQIQIt#J&?U&s7v4)@#iOD3d!6u!s{|8QaN7HwDB zN4Z|f=K$ts+0vD=Vo4`ErQZc)iHl7 zhXGr<+ZhjA+dfBsu%&y7app9!@ZW`U9!bU;t!JT4o$wo;JTyP^`9t%YUOhB_>pO?$ zj~uN1V$YwP|B-WDWO+Gnrs2WJd^zu>>hZ{YIWH#hkCFLuUQKdCWWJnNQ~%e1|V+lWmLmg`mA$x?QCWr*QZSWg~1RYh6d43n%%nh>o=Xz~z!^(?0)K@`Jrx zs;;(W>=%X`ex-65v`?4VNZa)ebK4?*A!sj`F4` z{@-MzBkeDAP%dS;{~NnMSBt$|*5v(M4gcHprN2_Se97f9%yv1+UR%U31nuQAW0p3& zg~MMc8(}lsf2DFc?FW4)rG|5&iM-jih+hcW%cVU37c$&|nd~PzYH2zU_?giiY0v7Q zTzVJ$OauR;;F%+l>bWQaz2-f%a-xtpe^DTg7$LB3jIQc zJ2LZBM>V=5foOcx15pn|JrMOk)B{luL_HAoK-2?K4@5l>^+41EQ4d5t5cNRR15pn| zJrMOk)B{luL_P4|dEnT-<&Lu7nX$#ar>0FwD~_Go&&auHTygxA^x|0j=K?jpEH)Ow zxH6}!ICE;c^}i`&+WwRHDdQp6yUdAyAeAJNn8q-pP%ZUJTKi7^iG<2jW;*s z&YD;3H7=bSUqSI>#+4N1h6?;eI$FhL6i6xs$Cl>0(?X#@!OW77cWfZw4;bP23%y0& zfG6Y+j4dq)wMCCFEcORNwzT7m^1P+CxQo5>Y%vo(g?1y8uJs0H`TW<}Qcfu;3Kblb zm}94V0!0Nyv)i&XHRLZIAEM0SVlC10f+26AO*47O7B#JCroW^pZ-PJ9^G6MSTG?u3G%l{vv*G~2?x#2d=@=UF1S%#@!Caxe6Ts3eSvKvja$&FraO z-z>T~qpVtXrJIVM74YPG(>=L!w92`S99>fE^IELX1zX^>qJmt1URw<62^*T|Ee%cd zhfuygZ=ts+=2QbqANN8jygl(P@F%?rBBEkk6v40W%P*syZ=HCIxw( zkQepqI)5M!Q6-e!Af<}eH^jVeepv-KG^9Kq&DtF~=krx(R>n)0;tD-H6yMM;zRh)w zZSmB|j4h?an=O%nf{=GwQNU9W^vu-taw~JiiEfx5<0%O$`%Mv;>b9MUs!h6^bpqY2 zP=(F#m$2ep%nG21q^X({fP*X16~0!-sU|b6xY%2y z3b)=G8w}t44VGwjMWZEZDmo;xvzgD}&r7MYFse z18OWz*Q|*pg)`w*R0UF0-7a!`(PSUG2=bXIM_TDXV>{N= zl9?g&L)!hCGij3&uBYK5IMttiY} z-kv92IU2D@jJ5`++Rl~+zBU3wwcGXW7@3}+YMdL|#f}f6SaN-ypz=_ojYl0d6~X3q z@l(9TO8?gOu?4yLWBi519^J||b;L~f`;~W_+a*9#sE0I0U{@qYn;%;2Z1F=`Cu?y@ zFJ9h0mPSpui`qqAhT*V3fDxT9d^2^x*EcC?sM0B>dgqpSi*mj0G5(?uD!%e!wT(v& z7s=FeysrQPe52RvxUBqu*OM1+3n_I2bHYQnOt1EABQwtChia zo2+q@rHkQ68V1s>rJHaX5*2$@3%1LW0G7x3JbK8}Vu@EZDnt(#FkRMJm5CZyqRq=w z-Fdl=l;Ne-brtPd8D1KIlA`798ErRjm5qUHw=t^^X0}zN+K!l(F<^cBG~=n?Dk9gk zscC4RM9!rX*crjE7Vk{&r-B#wpwU>Fpp(hlye;hl(>$%o@ zkW4diG0e7(H{BN(`L8RI8-5q3pa+nt+-Lov`tS&4P1v2(wRG#YxO?rX z8#*;O7CYCOr8~Q9jPZ3#8B*@pO(gX0Uu9OAb$elx}zzos&jeVnlAO{yD4sG zd{b=owCr7RWydY=vE}H+Jrm;NyTy0wHl$lO5DBsJ*DXG_ceifcV)g&=2?+_Y3Gv-x z<5gsAEEoyh65?xP79U;ftnRefxydmhB{?fAxz>@Lv)EZWV}m1mjalv}&G*gE_xVbF zzEH?l8rtG$bXJ%X_Bi%*N=+?YhFoT-zpT{6-uf>YL2@XgckeQ0=>+&UIXNX|ShwEE z$$j#3hIPvza%4*4uo!q1qzZ>+WNbB4H7CsIR*#bFlUHwQ5tW$Kj3g>qCFXys%@xQ| z;>eK`h9wV6PRt%=#CF43Mp)`A=iZ&nSkAc{&3NP~RGSQAI^#WkH4ZRNK2_ri#`XHx z5#wRTc>^^6CB`2yexGsUAkF`dakn8FAC2?&D1UNZUNU1j?`|SvIq%NPSkAXAV=U*} zEoUs})vaT^pU$oqDaLYs*=ELa{@53c<=n8Y$LRdXxnHL_6JM(gr;k#RO3)b$U><&1x1oRF&1pAxV0w~_HVjJKv~{#A^#Gc+y|KI2Np!^Ud< zTE-g~Z;|xlG=D#1InS%dvFsl{f9p)fDVaL`MU1yk(Adj3b)v=r#?6y8Ucxwes>bUW z%ehsrGM4kJni$J@RX;J7bF2FF(D{?|r^YZ2@p)9!8Ou30s~F39GhZ>z;qz8T;s8Xd zpK|WWBF1w5$(xMj9FXqlbSS-?$1#PmoJVmNV>uV%1ID#E`uY=()ADjYK`CQ7r{ERF zvR^;;c%5GM%}-z~`^Nu|vFzV|m$B?$?%PYt%Rb^_#JFJsyNQ^Q#H;e5qd z_Mx10l9reKAoCb+2x4loj8x5k?oyYAKaQ^v~~?_<1?@iD*A_Ur$k)1SsTV}-_-FrIP0#p#;4C8K$ z-(=jM@ixZ87=O<=h4ImeI{%rBPh~ur@hHaGj3+V9XY6HM%=iYz^BGq%E@%7*;|j*l zGp=I%F5^1JI~X@IKEU`R#>ZhCP4#&@7nm%%!}rHp^WxQy{xjF&MU&-hNpZpKxNOBmNNUdp(E z@k+*97(d5Y&iQ_mal&2NKie5E|AWRqG0wbSo!?s+%lWns zFqZRdH!xn#=i7e4cuS3zj~}Y-%Q>s3Gv31Iu8w9b=MyewEa&3A!C1~?i%Zh_SaCKbshz#CXOAoj#56cE&RqCqA$Fix~SDuVGxv_!Y)`7=Oli#z~B88DGY@k?{h?O^hF4 zoY0{2^C!kb7=Oq(gK;b4Y{tFL*7hqHpUpVtMXhft<4ndO#yO1dWn9X*j&Td)&Lgz` zl$W%=>5R)5uVlQD@fVC^U)Jf<&e8G{7%yd9!T3GKTNn=>snhq~sO3Y9vl;)HaTVjZ zQ9Av0##xLLU(xapGWIe4oN+DV5$9_8J&Zk!6JOQxw=m9Typi#8#`_p=VLbdituN*^ zEuYUgp7Fhm6B)n3IGgbgj4K!qNzwY&F}{LvLZjAyJ>wALCm63`yq$3^;}b^n^)jB$ zxc}=~-(8G{G5#ClOvWdiujS`6zLIer$rB6ED#ECNQRX9_n9O z7~jh{@l7rNKI3G@y;518@eIagjF&O4W!%8Hj`1GGIh(Zp;b~f51!EuM2F8ytj`@pD z|0&~4#=l9|@~*cue+J_m#(!X3!T4jwD;N(Lqvh)u`xrMfewwlCuUh{;#@URAXK49l zjPn^+Fn*A6%4RM9F=HR&-;CArrHpeJS1?}9cq8L~Fy6zs*ElV2yshycwyBNR3IOXqJ{#(XA#{H*g`6|YjGTzMiCdM%zY57MP4`IBOaXI7I zshl6iqX@?s#!)-8d@kcN8Q;J-jq(36p1^n|<7tfR8Rs(o3u8ayZH%vH{1xN*jDKLf zfN^Y=&fj9j$1yHv+>h}x#%D0DV4TAEF2>^--^2Ja#`iMLWxRs1pYa2X=QCc(cnRax zjPGXr5aZR1A7T6i;~K_)V*DuM*BC#>_+7?NF#d$`lZ?M$T*vr(#?LbDIZgZTb;c=- zzh-q9x?{)``FJc9A#j8hoD!FUqmzcZfB_%p^=Gyb0OEXF-9)A=oB zd@AEI#z~A9GCr5_Zy9GYzLoKG#ubdOW&AMX-!XoS@tusHWn9I0J>$n1KhL;<@oS7f zXZ!+>(FJ;`yxRUYFGxYVXV0;qem5fhkyq58KjB6QZGOlBs%lK8sa~Z$M zxPtL^#w!``Wc)beU5sC3ypQqQjPbJGpuZm&pUT*ExxSt>#uqWpV_eL55#xIqKg9TX z#xF76#`qJ)Uo!rb@sEsmFz$MVwm;@et}l!yFiv88IpZ|Oxr`?<_A$Piae#3V<9Uqd zGhWE}R>rq5zL)Xu8CNsDgK;C{wTwSw`~u@|8NbZfd8NL-O^i=u{2}8JjOF_!g@j`b zBR0*U%4-4Pu7r%hvkj`*vqAmhX>T%vio3 z=VdJ4k6XZ4z8`n5r04IG)G?OtJ^h8TeD7%oWBJ~b<0_q><;(Q-oyWMIaggy=#*Z>? zVZ4KJ@84_rQ?J(gMl$v=p22u7qE%CV3Dsqw!OTSPx7*Ugw~UgykL>vpxvpim-27rll-2(T0gBnA$h?d z)%B+mfjO_CIWvHki3;o^0J+qvL!X$6y zlf0~dq4h8%FPPGkd?|kPN0{WTe3F;-GPHh%V{{%laBxZ$t8e zDLu(&*!*whlf10Qq4hZ=FIeP#&_REM$-b3O^0I!1*7K0OU`kK+p;rA7CV4BL7b1DVB0t0?Z{?G`tS6%NMIek%@>V{{%X%eRzeMtaDLu*e#*h98lf0Er^0K~();p2BU`ns# zZRKy}lf0~lqV-WEFIeOgZ1Pq<$;|6OHFYCEzeHY0K7Wo95yp>P#vi^(KgOR*o zk+<8o@=0FSi_!Wqk{2xUWj6a(KFQ1aGFop&@`6R)o_{N!RB@kw6RyV3eLk{3+rNwx|<`Xfy8RzAtg`Z!uINAiLxeI$7+ zpX6md9j&h;dBGxIPR8*^nCx5mBroglXgwau3#RmBzkvw+Q9$xmKFP~^JzBp<@`5Qn z$=BmYe}qZi$|rgGo+hpLQ~5tvC0FT5zKI0!C-Rb>Fv-h$Kw2M2@`8y^@~K4Nj{>r9 z<&(UuAEfn!BrllKlYA8s_@jX2t$dP~^@gv%8e641O<*j^@-P#@_koYk4o}_MLyLgZ{?G`tXHM=t0XU2Tq*e3F;- zt+d{i7nTmNt6lf0~-rS-HVFIeO!*yOEz zl9%)-Tg~W|9{y@_DxMxAI9|);rVsXOb5z@-v9Q9|aR_ ze3F;-(X?Kg#1pdHOUL6^dvtFKl&p~@>V{{%ld0tk4^G| zDLu)j;zxgkN#4pQd0DSb>$gc>Fr`=ZhY0*pK=M{T$;Yt$dP~_4u?tpVA5zd3*h_@=0FS z@6&pIk{2xUcKcR7$;*0wTK`XJ1&h4B{#f}WFZ%;%zW~V#7I}OAt$dP~{RFhXfaC>> zygmO`KFQ1e1KN*3X$6bC-M*Di^0Hrn_AijUV3D`mxAI9|_BYUe2TChgFyL~I4+5bWNK`5_?&fDI_nL(v$pl z{OFG`$y@m(FZ)+$KMTnVrt~DQCc7>2RzAtgeiz#RLh^z|-frK@CwbW)L;Gb&Ua-j9 z$B$M%$;*Bk+FwKRf<@kkywe}yi8emT%l;eMk3;f;DSf2txAI9|_Uq989g-IumhVi~ z+w5EUBrp4WXul813l{m_L|Ejle3F;_K(s%I=+^0GgP_A8ORV39A?=EL${p}xYh+)r$lKc=E1%?Le;e(0BYD9h@3K9=S@|R{`{Djyd*>Y>MScGNqbWAT zU=$4~v0$TJqgmmo0!KNT5ncA~7Fh3Y_uSp1*n7hUme^tiqfu;N?1K3kqfrUO8lxom z6=Mq;L9Eg5^PZXa?6c3!+}+;s-|sicNA5k(=k;kb^O@Nx&x7ydz*CdI-1J}D!Snk$ zd`}0Sn*6(_er*TO@9psY9e8T;w*A@;p5N!;dp+>fEx{5}=mtAeK{ztGgL?cn)6E52_9Pfgy|ukGOZ{VTqQ1y4=ByLtaY z+rjgDS$sbWo|?QJe{BcP?`!eBEqH43)n@#(9X!9s#rL`3smZ@$cx?yI?|1P%FL-M5 zwerpq7U*(g2hZ<)@%=A&YUGzy1+z{4+76!I2jhET@YLk*Ia~^n1se5hJ9vIijPHxV zQzJj>pCt(kGHWQzL&+{n`$m-^b&7dGOR>ytad%?e!nu*Mp}fZ^vKT!Pk2{ zzQ+eo9mZ=r_;Wn|T#u(FZ^vKT!SnlmeD9C?sma^pN87>Q?$rP|c1Ne&oPfgzLzuFF-{v_aE0z5VO?wGt}fi5?8@CSMI z!yg5BYUB@^zqB1Z{Z(xAcd z8sMqP+wHII;AeR}{BMA#4&${Q{5+3`zYg%!{%ychhw<7Dp8jv( z4+lIoc{~2v4xavU;6DdEbr`Sh;OSon{&v7qhw<7Dp8j~?p9efOc{~2v4xavd;Lis< zHF?{AZ3lntQsgWsfd3!x)Z}gb+7AA9kB7e?@YLk(@vH6NS9v`A3xTI5Z|m1~@M}CC z{)oU+lehJ2JNVZ<9{!8KQgasPBwu7htD)_SkPmTOR zytacsXc=2?Ve=YFT$dC5h`+wRF zp8mJsj|)6C`Poti7HHJ3?cnLJ3;w&nQzJj>zW{&8f`7nkJ9zr{y4d5XksrK${;loc z=?@J4!B9Un`I%A%7HHJ3?cnJ@4F1HxQzJj>KMH@yf`7nkJ9zpVga0w`)W{z+{@Mk943_LaYWvE;hXwrzUT=pSFXi|2g=h15Zuf zwqM)9(_bC@*MX-FruZ3j<(hwy(0o|^n|QU(@i)UWN}_k0Q# zxvuhfYOF>5$4SBh4PM*9PxW~CS492P$PeD`f7%Y7{ukko5j-{d%cTq~(5PSA!P8$O z{5OK9Mt;<9d2I(z|BmqY2%eh!2q^;#H0sxO@bm`>|B&FRkstM2UfaRbeM&m0!PEaG{9%HpCg14#+4GMH zJ9zrbg#S$N)Z~wod|05%jU7DwYr@|qcxvRw_B&J(7HII=4xauv;h)p#zvnNLe0PV= z^2`rA_(@)V`11r$9mZ=rc>4Q<|4;DLVZ643r+-lR3k6Rd#%nux`V;LeKQzEohw<7D z{vogb@J9-sI*iwL@bp(Y-Q%glcx?wy|EBPFiu$R^%W7wVE;n}Y^oI)nsNkuQAN!Bp z{@Mdl0JT-Y)?JUsc#txqTT;bmpJT>y8 z{~3qQ?*G~jp8j9q4;DN%c{_hYdd)QmxaGs@YG?vwu7fXTKK00 zPfh+D(|>ITPye;>XA7R1yzRfXgQve+_`d~D9mZ=rc>2eMzg+OtVZ643r$1fz*9A`< z#%nux`rm~=UhvdmytadF*i-pTSd;xA#A_9X$O*!(TLb>M&m0!PB2K{7ZwUCNDoLodvqw*um5PH2hJ6 zr$&C9KX!HK?D<36!P8$g{8xjgCU56|Z3j>P*6?=?o|?Sfe%cP6{;=U68$5LwukGOJ zKO6qE!Bdm(VfwG_;OTE0{iPCbHiUZcxv*qP5s&qp8mYy-y1wNdAt3! z9X$Pi!yhv}dE0+&2TyExj1Pc#0oeZ3VZ643XFP%TJ)WBU+opbP2haEeh)00>smUK_cx?yI zcm;@G0G^tBqw!x0i#s0R8Q%c$4!~2Bx5tmRgJ(Ph#76*6P2L_q+76!a6A(`UJT-ZH z{AfFP##=!A1@P45?fk9n;2EC*@fyHWlehD?wu5Ip2gG*(PfgyAzqW&C{0GE?08dTc z9>3ZSp7A0OKLR{8d3*e7J9x&IK)ea?)Z}gZwH-X;Q6N4Acxv)?|Iv2vj9-Cx7T~GL z+xuDc*aXX{1ouiT2JvoCKQ(#Ve{BcPcsPiU1D-mJ*LLuXpM!Wh;Hk;m{ZHG$Gu{s3?|`Q!Z^vKT z!81M&;`M;1CU3`I+rcxQ590fPrzUU5U)#Ym{?G97E>BI~j=#2pXS^W94?_LaVZ643 zXM7>V8v;*F-j1KPgJ(P<#3uqzP2P^5wu5K@ zp7EU!?+H9Lc{_gE4xaI#5FZLWHF-OJ+76!aqYzIDJT-Yce%cP6@um=e3OqG=JAT>@ zp7E&=uL?Xh`Fn8klLflm*ugWN72;cgr$&C9|89|l1sc4zgJ=9J#KQtljr`y*mxKix zytad9ysVo%o|^oj4xQz-9X#V}A>J11rzUT=pSFW%JTAoN0#6;rYdd(x??OB;@YLkT zoAzrvc*gre{4em-T4DrsuQdjh>v(I>I^G(zj<*J_a}dv?TIgMP`=h_?o<djh>v(I>I^G(zj<*J_JCaj<*JVm}j5h*>$`%*mb-$ zXdQ12TE|<1*74S$b-Xob9d8X<$6JHe@z$VqyftVYZw*?b@z$V+d3MBGgVyoZpmn@8XdQ12 zTE|<1*74S$b-Xob9d8X<$6JHe@z$VqyftVYZw*?&S$@xW@zxMut@qCIyi3JbgVyoYpmlsT zXdPb-TE|y|*74P#b$m5w9bXMv$5(UmtN3crI=&jTj;{u-_td&^Yc%eywgKZv*1TQ2r>yfx^scx&y2*vy1&uK89mwPh|%+nhM!nn{~boJ zGWrFh-!%GPM*m=R*ABV$^)~uQqsxts7`@Qw%Z$F==qHU{Yc%|B>hbv4=&y|4N#;JC zzo*fqMvpRjp3&zSeU;I-8vTILPa6FvqhB}rL!-YodY4YQ{p)Tt{1oc`7a2Xw=!r(3 zY;?WR=NWyy(aVf}!swTbe%t8JjQ+{!E<5D*ucy(+89meJxY4H>eT~s~8~v2g@GGas z`$MCDG`j1Kx%m$;y3puRM$a&Mj?rfreZA558vT^fuNl3`=&eS#mum~%|6dqA(CBeS z&oVl0^chB9Y4q(zKWy~>7`@Ku4Mu-v^bR}a_HS>a4>Nj@(IbqWY4j;ZpJDV;qwg{L zk4A4a`aeb&$hC{!UR{ko#OQ%WPcV9((G5mlWAt*P|77%gMt^T~ceysw?H^?HSfeYA zP8xlV(MyfK)96QBElqHj=Z${L=nY1HX!K^Izc%^{*&qKSX^W(sNxdZkWs-2*jFdD=(r8IzBpokloTTxRCP*rmG*QwdNs}c_kyIgR znxyHHW=O*Ec7mi|Njg!|EJ-IxI$2Ug(rihUlBy(CONvRFD`}pjxFj5(wUX*2B_t&! z)k`{6Qc6-4NoPqqN7A{H&XaV$qzfcnBbcsf!SjVB_t3+rmdl9O@LqG74Ik!gwgXjRSB zhWgqV3dE}E(b{-*G!q*aTeKjVs+Ls^C_WAQ@Mtm=cxhz>7iBPbWk(Rr6v_`>}phOeAV>L^4$ub?S#29iJadgmLA`39-6l z${8YLE(cmG!|DSpbYfLiLn;-krk)Z@XObyr_yJ14%aaK$-Ob1BVs|$iSImf}q*cC3 z#WS&KiBvS6j#j$6h_oOwEF+t`vLO?bD%{b>+R?E@EESd7M=p%J-ISGM>*|xKOisSB ziR#$GoHeqm<>>NgU2f46=f_fWYLg3ca!qMSWa1{Hw+_9KebMQLG@>Gtte0&ojgw8} zuUwRt&7V^uwr|dgX^F~YL!x?ovMO4eV~~TSAt$T5`7z#Q(T21;;M^&&c4{hG6|0nE z4b_jTO-3`$F*73B;Ek)hj(gaslneom6l9ly^^YKTJR`%Zt0y?eIPBg!Iab2gVgFTm zU8K#$e|-Z%-II`dqT9f zHdzG}YD!GW%)DeZM|ncbQGO}T@XDkS!7S_0?xZTi%)xYr3k76HtGuDEQbt8K5ExI5 zO-!nl?Qd7gaiB7>gB~*_hPk^s=1el)x{8L%jO<8Gb0$_q(usPRax-&Kh&L^lN$-pt z6;C*GM+&1jBHMiun8;?tqBT=ubDW9GTR+O5uDlha%OWFUx^P)BHqFwV?q+jhe|Prv z?5b&rMk_I6#H(aPF{7wvhpx#^RxH;&LFBSH*43IZ(X=xOd&t(qIvShU(d_F;FXv*EIdBV%Ijh-BCB2yza6p1~amml;@5fzq>USdC+zu?UyAp zvoj00ha4_4>SN9^jT|U*RBhC~Zg5sO+c@K0qBv{hxUY83r;fsZ(12-)conWm^k8t^ zh!{*dSN4!#9vo7^YqJ|w=XKYrYa|+-Nj8zajMCK}pNusV_1<7M&k6xnv}q3mnOc9WOF2kz9~)4;wMz{8Qmf>ms~e3#I#) zci20p%C)C&m6^e5q*{}6sN>4b-B+_YCPZsuQ|sMUXV>EFQl8An`O3Y@#g&d&^)5Lr zA(vcLHL+@Gy<_CSjEbf+Wy$(QW%Ht#G-V8O%-$X$rA5k60J|fXP|kHC4E}TpF)CV{ zj>#5mzyypH(npyzfL|qWw%XdQmLwWseP+#Gxv`A*bBL`eTf{9Lspi?xEm&Dn&h+1CbtWYTPYTy}jZ7GOIb*o% zajlWPxM-9YdpW0M0@=x0b~M>>z3DtsBiDe93Xm)Q24|mh##gmFE#0lg)lMS>mW0|U z3c?tj+7PV`F@tQzTqQvA!P}nb5H;ZKOeu9ooI9CiTES`{SJsHl>5{j6!i9Ft~iJ;Dr&JHN%*ob?e z$Q($!oa(uial8?aHFd@R;&w7;Fa`v4*B#g3nItod~} zI!Jv>;4$i30}oN(3Rdm~r5Y0WRg5Xoxj^Pd#T<&?MYuk4XVOePr^ZN$bMvUMNZ&p3 za;2-10?MgLPKc4nLfn9gWRf+pgj^0dGa+t8Nl`p%wntgsjNRg*3M2B%6<6(Y+Kt!Z z;=#E=)|ys@7v&t$+o4JMcCILrmP6jT8fkT}0?Np@8O7V783ASF+l&%_Q&-2*RWjen zWlLMOcQfVXx6zv^EZ@GCZigWcC?nry4BQUQ2q+`pW(@MTyd3OuDdL?&TRZ(UU0ib@@`1LY@YYoSI+f~ zPAhpgBw#+zzaasIrQj#6`t_uKxRp;FHf3}LY6{OYv1#+D zVHH!$hK(N|88`BT855_BsEAA$Id#m$5qVPVZT*7a>ypBN%Qdvwxp2)s`imrza(&0T zy&Ef@IXx0T3R%(I%HWEMF#OJAA(oQ8>*f^T|NP26MXQh>+1cFQ9jDQ}De4X`?kV_} z+svFTLVkoecY~Y_k^Pw_cXe?i-aZlHm9VM`52;ve_P&;Pf2=~DMZt(OOxOsQ1YUTC z*J9bxE@b1goj)NSleVX*3tN4GnDb$ zm1S&wp&L4g%??R%%X^6*L)i=C(6;)!ubaQc?X+CBjU~;tG0W=9I8004!7q2oBhK?e z+#y48u*oAtJQC!yF*%0Ij0w+XMxIJTQwMF~AZ}Lq*S(T#anA$LZ0Adx?R)@U(!xm~ z04?>W-GQyCc~I+X9_-gVs5Lbg4rqPVg@u0IgIiU1aU1F`@#`Mas=5cZq3%I`-Gu|1 zt$bjlQf~KGN1XdLahd<|&W2CT2|kS%mWEO8A&93rJW5$s*j!dLAgHWJ_FyS=PF_tO zr-RY(Ir0pIm4!`YMFU!ts|dN|mV2|sl^|orx!XH&+SKu5%SVogjG9(nHg)X8@`wz6 z{wx7&k6&(Rv6f~qQJgMgq46E7X|jMD6nrufqg3q>|0S_---hJ zRutH`qQJft2llNvuy4hIeJc*^TXA6DiUa#r9N4$wz`hj+_N_RuZzX|!D+%mdNnqbf z0{d1H*te3vzLf;_tt7B-C4qe_3G7>GVBbmu`&Js*x6;7Al?L`rq6q}->!pExD-G;h zX<*+<1N$~m`sV$ra2`y{?+pF4%TahC)cqBfMrNE`+;`PRB;}!n|I!<;{JbA*`hh7i z^$Xp77r`k^t;G}8VR%w3FM$O>@{}axNh&Nkk8iRMlpQGiS!ff_BtbU$&=MJf9$MyB zE{}Pt_}nL;Q_chXMy(*ghdu<=vod#>ro=K0Dd#N}|0M}`RQ(c~dZrf19c%ro8ZSo4 zdlK$jwf6N=Zfra{^ndbuPuR>;#_YpA4A&gIMaj-&UpB@XTwKUQphPU>{)n5SrBAr> zDJJ|eQa+^w+@s3}54f|I58lkvjC_g;cnXmZ9`N)aAH3C@*Q+UJ|cv+Fp`>p|c%|;x*59C~Eud{r;8Ec4(D< zQKZVkRfRfRsI#5>@VZ4=C$=b8*rV*$!HU8x8g{?eKG<#(=A1H6Tj;H9|Hiwir=>vN zH8|&~e46B6W@XRDflt9z2e6O$SEjgl#lvuAZ)*JgXL!#0mTiFikqO(IlKQ9nc7=hfZu|!zWZ+>$ivDxrqVo^*3^^F zc2O^Kw6Kwd0q=9P8Ev+k;k7p3+t3C($6r%Zba<`KFYYE$e+Ov%3QJ3etJxy_VGG@j zgNn!=4aKGQ8`qyB+!uGuScDy<*?CE0e&6%M{Jw@_{<%B%?V^@8 zwn^M?bm&N1JiF_Zm=?6Y2)`9A7z4g)WGc^_Y<->iY_Z(dHorq=M?05nezFN8o%?=G zPE%o|Jj3+vj)pw!3cguhhvO9OF*}vLU%JykX^ZQ*u zkG$97Om9MumgY)mRZrCWb*3HpjB2A&{I=xN8}DU3(~ee`;I|>4o~XC}Ogq|C%JylC zzn8VCEzQq#0ec!>XEcM7&&J6uqE#cJUyL*@$!AC!7qgvO<8Pq6#>IPE({!lyMYOIn z>b+0XlDvktQ8C-8HLV+3?`2fep}dARr-;^dM!(i-nv&PpHZEs-wWs;f4Sd_ybf!M9 zmu|NM?03I4H9LKt9e~y!^YpOY(c$<~I48yUisve=_mka5lpcdJr1G z?{yg9z8T%Z(sky%O7@4WCCzAdREqeXc1xS>zpib78u9T_8K2vbY)H#nZ_bMo&O3c+ z@rN0c&xw=sdA%<=jk`nlcR-E!&&fJ6 z6I-NL!A(p=JUT|AmFZ-y_`Gz#n-Hg_;(EdPkk0MC?`=pvOvqX7c?DuFxR()^tnx7| z{Xuffs>Hoq98Na5!vD6RhP$Y;*?m@-=&Y0Fs3Ki|G;UjR+YhD2Fa{5;ZddW~Y%J~i z5A%ymxc)$?=ZL4*YzyY*I&GE#1IINWsvOXVbq)gYQUE`{?yd9w$ldg_c#l_XCUKMltsx}7PLHgC_ zY;Am$;ym|~PwaB?q_WLZgN*F%rz9*+yb`Ly;Q%sQOdWj7W+mQ0c4jM8Al_h8IYdR& zK?!T5IBQSVD^#{i8qQ&&ob0msa);0TYzR|dq)`0oh+i&wFFPkQUZqnjlJ!b79&_2? zbK(ngvWSkPD`j_W{j}% z{Qb1K;_#%T**tih6H=k;9Lqg^d=i*F@40wFTXlWpV^KAoiLYn1gXQkKj_aT7cFbht1*oiSNTeI;;R`qhk93V^ z6m7?BLN``!vl>$IY*FyQ)}T7RK)eZJJIihZ*ULPX%opzB3W_#yke2UPZfJ%K29A(S zv{DLkJn@P9)%nWVi1@}6x7%_#!HWD9k#ubmF0|d(73HiWegJW}!AW=4y>36pTen&f ziTg)ZpmRIFR5ULzhs?OL7qC6q7eAc0({l0bhXi%bHxd3&W-0h36z{e{7Plhb;=gRr zxFX*qrQzF)#*9}@t>cW;-JDs-R8%L!9kMZ-{-nMM(0_!2kK6fxmt)u>PCbBZ0T=W* z%QygNqSovkYv;@EJlY#{zbg9~>V#Not~k~7-S_3_e#HYy{GR!n(Pk+v=sztj`%8bg z9*d>=i$nRE{%|Pf{wJNPa{nmM-o^S=HBC8}cm3tOTQA2;RWiM>Z{dJ`L;5x(#OY}& z9jz_uTUEbEcC19-;vxP0DspNN51a08BbDff4|$X>l%r~Av zlywrFtMkx;_7e1|z5GwYNJx?hi_UUPL5Fp{dbKZ*XmTxB+PzuNTsck4w!Lq4G_@d} z=$B3w+;Q@dkJ7(=c+Jwc2Az0r_jle{R+oOc`?8IXuHNy!Wc_}tum9=5YtR35`3tEX zpa1XNSv#)kboXO-?zzu9zdmbA_mf`Q@AZCF-#m71_a*PH+}y3}mmRnLyW6-JUm{JoIl%R-J#**VgKC5=gv_6||c=EX=o&M+B8~(V*(y9@COGh8P?%fZ*cxBw{ z@4tN1vaN^jc)%UocH67xZ<-hFLC{k8q(UO#i)rK^kk9n-sJ=SvSOI{FVYmd@H~<~9575*@brKjV-1 zW?k1Owp}#*@ZJAgb@o}O4;V9K^$VL%zrAY4&7VDa-P3=6b-+7A*KRtiK9RX`uOoJ< zT>n67&BEc!I=s1V^2*NZ4j6y`!yEe?IcDPHsi%v-nf=VW-LAR$$BoNw_~7Hk?LM7( z{aufJ@!#1GY`tJa=h+L7dTjBfU!;Fqv~BANOBY->ZqE5D_xxq~X+8U#|N6{(N56df zlEs5A9F`ujIsHJ#wR?TDQ}H?b^}KOZ$Ntk6C+=Q)=ou?dIIYh+M?82`yR-LAUUb$) zZx?)W!r;Abd*-%+Bj@ZnZScw|`({3Uy32j1pTFSqKdxRq{jpus)z_Z$=Cg-SJgj2S zjC(h2IdrE7x;**NTPM%C{(;v|ytvEqgZtE6zrItad)5~Hq2be=3f`*u==Kxu+wr8H zbMNo+WQX{{$N%ZLH@7v6eQj&MQ7gJ9hA-(+a7xdHcIC%xJ$mx1kN^9u)QmTdD!*XM zLuyzR5wHr?{%dH3~y;r1{0pYX}w$2|D`u;XLzAMmsGPZm77 zEm6ML~fBND6!+VT6c-k?i{pPI? zJ}j#^;rX}!y{h}{qc-e6V*V{}t{nQ{b5~5Cc*I@b&)oN)2ekY3+OAtK|LB=6W$S-4 z`^~L$3SpWu4y`cj}xmZ$EVXtDj{4xZ58d zyXu8qeit7&X4T}YmQUJH^>yK2YA(5J?Swl@?tA*SPfwrx;Qi5=e>C~bxv%Un^qvt@x)px#ZM55igNN+V{Z~)!w_(eW zft4%HzW%)>(|$`pE$GAPiL$;@uIXuR4{$-#J!!=MlSnyzR;t_Wq>c$qQcl>wXt4 zAA8Zh3!dJx`02ZPzWS%#3T{nKe6Z}?3npLs#;l)Djs0!ZeMRfnzaQIl#bGZjPjq-? zc&{a8ckO)1H)D4kc=N<=z4!U?Y!I{K4lMD8|>D}dz zPFk_(vQ;m(d#wL$2d&s*Aa<7eBZ*N(e0`IkF}ZmJmf>viKU zeB+dJf4X+JXTJ#onI zzkKiGO&5**d`Or2WBOjO#}zm9K6n23$RpR3|L2Ojjr|WBa%jWDbM}2_%avW8x^nb; zy%x2<;a(R(kGbW&6Fk?~8*EJnOj?oep2op}1FZ&2H!Z zb(i6jpI=kI{?)q=yye#K|9S1KR|lQ^))TMXxWhhkUpeK?sb~Iq-sKNIdRXn2tIEEg z^S=WIZ`kvR<5vvtvF48ULw8QzSki9aB~KjnpQ{h}Vbk&JwysG`dtvaU&))fELBan4 D?zaIP literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/lxml/_elementpath.py b/.venv/lib/python3.9/site-packages/lxml/_elementpath.py new file mode 100644 index 0000000..760a1e0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/lxml/_elementpath.py @@ -0,0 +1,343 @@ +# cython: language_level=3 + +# +# ElementTree +# $Id: ElementPath.py 3375 2008-02-13 08:05:08Z fredrik $ +# +# limited xpath support for element trees +# +# history: +# 2003-05-23 fl created +# 2003-05-28 fl added support for // etc +# 2003-08-27 fl fixed parsing of periods in element names +# 2007-09-10 fl new selection engine +# 2007-09-12 fl fixed parent selector +# 2007-09-13 fl added iterfind; changed findall to return a list +# 2007-11-30 fl added namespaces support +# 2009-10-30 fl added child element value filter +# +# Copyright (c) 2003-2009 by Fredrik Lundh. All rights reserved. +# +# fredrik@pythonware.com +# http://www.pythonware.com +# +# -------------------------------------------------------------------- +# The ElementTree toolkit is +# +# Copyright (c) 1999-2009 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- + +## +# Implementation module for XPath support. There's usually no reason +# to import this module directly; the ElementTree does this for +# you, if needed. +## + + +import re + +xpath_tokenizer_re = re.compile( + "(" + "'[^']*'|\"[^\"]*\"|" + "::|" + "//?|" + r"\.\.|" + r"\(\)|" + r"[/.*:\[\]\(\)@=])|" + r"((?:\{[^}]+\})?[^/\[\]\(\)@=\s]+)|" + r"\s+" + ) + +def xpath_tokenizer(pattern, namespaces=None, with_prefixes=True): + # ElementTree uses '', lxml used None originally. + default_namespace = (namespaces.get(None) or namespaces.get('')) if namespaces else None + parsing_attribute = False + for token in xpath_tokenizer_re.findall(pattern): + ttype, tag = token + if tag and tag[0] != "{": + if ":" in tag and with_prefixes: + prefix, uri = tag.split(":", 1) + try: + if not namespaces: + raise KeyError + yield ttype, "{%s}%s" % (namespaces[prefix], uri) + except KeyError: + raise SyntaxError("prefix %r not found in prefix map" % prefix) + elif tag.isdecimal(): + yield token # index + elif default_namespace and not parsing_attribute: + yield ttype, "{%s}%s" % (default_namespace, tag) + else: + yield token + parsing_attribute = False + else: + yield token + parsing_attribute = ttype == '@' + + +def prepare_child(next, token): + tag = token[1] + def select(result): + for elem in result: + yield from elem.iterchildren(tag) + return select + +def prepare_star(next, token): + def select(result): + for elem in result: + yield from elem.iterchildren('*') + return select + +def prepare_self(next, token): + def select(result): + return result + return select + +def prepare_descendant(next, token): + token = next() + if token[0] == "*": + tag = "*" + elif not token[0]: + tag = token[1] + else: + raise SyntaxError("invalid descendant") + def select(result): + for elem in result: + yield from elem.iterdescendants(tag) + return select + +def prepare_parent(next, token): + def select(result): + for elem in result: + parent = elem.getparent() + if parent is not None: + yield parent + return select + +def prepare_predicate(next, token): + # FIXME: replace with real parser!!! refs: + # http://effbot.org/zone/simple-iterator-parser.htm + # http://javascript.crockford.com/tdop/tdop.html + signature = '' + predicate = [] + while 1: + token = next() + if token[0] == "]": + break + if token == ('', ''): + # ignore whitespace + continue + if token[0] and token[0][:1] in "'\"": + token = "'", token[0][1:-1] + signature += token[0] or "-" + predicate.append(token[1]) + + # use signature to determine predicate type + if signature == "@-": + # [@attribute] predicate + key = predicate[1] + def select(result): + for elem in result: + if elem.get(key) is not None: + yield elem + return select + if signature == "@-='": + # [@attribute='value'] + key = predicate[1] + value = predicate[-1] + def select(result): + for elem in result: + if elem.get(key) == value: + yield elem + return select + if signature == "-" and not re.match(r"-?\d+$", predicate[0]): + # [tag] + tag = predicate[0] + def select(result): + for elem in result: + for _ in elem.iterchildren(tag): + yield elem + break + return select + if signature == ".='" or (signature == "-='" and not re.match(r"-?\d+$", predicate[0])): + # [.='value'] or [tag='value'] + tag = predicate[0] + value = predicate[-1] + if tag: + def select(result): + for elem in result: + for e in elem.iterchildren(tag): + if "".join(e.itertext()) == value: + yield elem + break + else: + def select(result): + for elem in result: + if "".join(elem.itertext()) == value: + yield elem + return select + if signature == "-" or signature == "-()" or signature == "-()-": + # [index] or [last()] or [last()-index] + if signature == "-": + # [index] + index = int(predicate[0]) - 1 + if index < 0: + if index == -1: + raise SyntaxError( + "indices in path predicates are 1-based, not 0-based") + else: + raise SyntaxError("path index >= 1 expected") + else: + if predicate[0] != "last": + raise SyntaxError("unsupported function") + if signature == "-()-": + try: + index = int(predicate[2]) - 1 + except ValueError: + raise SyntaxError("unsupported expression") + else: + index = -1 + def select(result): + for elem in result: + parent = elem.getparent() + if parent is None: + continue + try: + # FIXME: what if the selector is "*" ? + elems = list(parent.iterchildren(elem.tag)) + if elems[index] is elem: + yield elem + except IndexError: + pass + return select + raise SyntaxError("invalid predicate") + +ops = { + "": prepare_child, + "*": prepare_star, + ".": prepare_self, + "..": prepare_parent, + "//": prepare_descendant, + "[": prepare_predicate, +} + + +# -------------------------------------------------------------------- + +_cache = {} + + +def _build_path_iterator(path, namespaces, with_prefixes=True): + """compile selector pattern""" + if path[-1:] == "/": + path += "*" # implicit all (FIXME: keep this?) + + cache_key = (path,) + if namespaces: + # lxml originally used None for the default namespace but ElementTree uses the + # more convenient (all-strings-dict) empty string, so we support both here, + # preferring the more convenient '', as long as they aren't ambiguous. + if None in namespaces: + if '' in namespaces and namespaces[None] != namespaces['']: + raise ValueError("Ambiguous default namespace provided: %r versus %r" % ( + namespaces[None], namespaces[''])) + cache_key += (namespaces[None],) + tuple(sorted( + item for item in namespaces.items() if item[0] is not None)) + else: + cache_key += tuple(sorted(namespaces.items())) + + try: + return _cache[cache_key] + except KeyError: + pass + if len(_cache) > 100: + _cache.clear() + + if path[:1] == "/": + raise SyntaxError("cannot use absolute path on element") + stream = iter(xpath_tokenizer(path, namespaces, with_prefixes=with_prefixes)) + try: + _next = stream.next + except AttributeError: + # Python 3 + _next = stream.__next__ + try: + token = _next() + except StopIteration: + raise SyntaxError("empty path expression") + selector = [] + while 1: + try: + selector.append(ops[token[0]](_next, token)) + except StopIteration: + raise SyntaxError("invalid path") + try: + token = _next() + if token[0] == "/": + token = _next() + except StopIteration: + break + _cache[cache_key] = selector + return selector + + +## +# Iterate over the matching nodes + +def iterfind(elem, path, namespaces=None, with_prefixes=True): + selector = _build_path_iterator(path, namespaces, with_prefixes=with_prefixes) + result = iter((elem,)) + for select in selector: + result = select(result) + return result + + +## +# Find first matching object. + +def find(elem, path, namespaces=None, with_prefixes=True): + it = iterfind(elem, path, namespaces, with_prefixes=with_prefixes) + try: + return next(it) + except StopIteration: + return None + + +## +# Find all matching objects. + +def findall(elem, path, namespaces=None, with_prefixes=True): + return list(iterfind(elem, path, namespaces)) + + +## +# Find text for first matching object. + +def findtext(elem, path, default=None, namespaces=None, with_prefixes=True): + el = find(elem, path, namespaces, with_prefixes=with_prefixes) + if el is None: + return default + else: + return el.text or '' diff --git a/.venv/lib/python3.9/site-packages/lxml/apihelpers.pxi b/.venv/lib/python3.9/site-packages/lxml/apihelpers.pxi new file mode 100644 index 0000000..f683e70 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/lxml/apihelpers.pxi @@ -0,0 +1,1801 @@ +# Private/public helper functions for API functions + +from lxml.includes cimport uri + + +cdef void displayNode(xmlNode* c_node, indent) noexcept: + # to help with debugging + cdef xmlNode* c_child + try: + print(indent * ' ', c_node) + c_child = c_node.children + while c_child is not NULL: + displayNode(c_child, indent + 1) + c_child = c_child.next + finally: + return # swallow any exceptions + +cdef inline bint _isHtmlDocument(_Element element) except -1: + cdef xmlNode* c_node = element._c_node + return ( + c_node is not NULL and c_node.doc is not NULL and + c_node.doc.properties & tree.XML_DOC_HTML != 0 + ) + +cdef inline int _assertValidNode(_Element element) except -1: + assert element._c_node is not NULL, "invalid Element proxy at %s" % id(element) + +cdef inline int _assertValidDoc(_Document doc) except -1: + assert doc._c_doc is not NULL, "invalid Document proxy at %s" % id(doc) + +cdef _Document _documentOrRaise(object input): + """Call this to get the document of a _Document, _ElementTree or _Element + object, or to raise an exception if it can't be determined. + + Should be used in all API functions for consistency. + """ + cdef _Document doc + if isinstance(input, _ElementTree): + if (<_ElementTree>input)._context_node is not None: + doc = (<_ElementTree>input)._context_node._doc + else: + doc = None + elif isinstance(input, _Element): + doc = (<_Element>input)._doc + elif isinstance(input, _Document): + doc = <_Document>input + else: + raise TypeError, f"Invalid input object: {python._fqtypename(input).decode('utf8')}" + if doc is None: + raise ValueError, f"Input object has no document: {python._fqtypename(input).decode('utf8')}" + _assertValidDoc(doc) + return doc + +cdef _Element _rootNodeOrRaise(object input): + """Call this to get the root node of a _Document, _ElementTree or + _Element object, or to raise an exception if it can't be determined. + + Should be used in all API functions for consistency. + """ + cdef _Element node + if isinstance(input, _ElementTree): + node = (<_ElementTree>input)._context_node + elif isinstance(input, _Element): + node = <_Element>input + elif isinstance(input, _Document): + node = (<_Document>input).getroot() + else: + raise TypeError, f"Invalid input object: {python._fqtypename(input).decode('utf8')}" + if (node is None or not node._c_node or + node._c_node.type != tree.XML_ELEMENT_NODE): + raise ValueError, f"Input object is not an XML element: {python._fqtypename(input).decode('utf8')}" + _assertValidNode(node) + return node + +cdef bint _isAncestorOrSame(xmlNode* c_ancestor, xmlNode* c_node) noexcept: + while c_node: + if c_node is c_ancestor: + return True + c_node = c_node.parent + return False + +cdef _Element _makeElement(tag, xmlDoc* c_doc, _Document doc, + _BaseParser parser, text, tail, attrib, nsmap, + dict extra_attrs): + """Create a new element and initialize text content, namespaces and + attributes. + + This helper function will reuse as much of the existing document as + possible: + + If 'parser' is None, the parser will be inherited from 'doc' or the + default parser will be used. + + If 'doc' is None, 'c_doc' is used to create a new _Document and the new + element is made its root node. + + If 'c_doc' is also NULL, a new xmlDoc will be created. + """ + cdef xmlNode* c_node + if doc is not None: + c_doc = doc._c_doc + ns_utf, name_utf = _getNsTag(tag) + if parser is not None and parser._for_html: + _htmlTagValidOrRaise(name_utf) + if c_doc is NULL: + c_doc = _newHTMLDoc() + else: + _tagValidOrRaise(name_utf) + if c_doc is NULL: + c_doc = _newXMLDoc() + c_node = _createElement(c_doc, name_utf) + if c_node is NULL: + if doc is None and c_doc is not NULL: + tree.xmlFreeDoc(c_doc) + raise MemoryError() + try: + if doc is None: + tree.xmlDocSetRootElement(c_doc, c_node) + doc = _documentFactory(c_doc, parser) + if text is not None: + _setNodeText(c_node, text) + if tail is not None: + _setTailText(c_node, tail) + # add namespaces to node if necessary + _setNodeNamespaces(c_node, doc, ns_utf, nsmap) + _initNodeAttributes(c_node, doc, attrib, extra_attrs) + return _elementFactory(doc, c_node) + except: + # free allocated c_node/c_doc unless Python does it for us + if c_node.doc is not c_doc: + # node not yet in document => will not be freed by document + if tail is not None: + _removeText(c_node.next) # tail + tree.xmlFreeNode(c_node) + if doc is None: + # c_doc will not be freed by doc + tree.xmlFreeDoc(c_doc) + raise + +cdef int _initNewElement(_Element element, bint is_html, name_utf, ns_utf, + _BaseParser parser, attrib, nsmap, dict extra_attrs) except -1: + """Initialise a new Element object. + + This is used when users instantiate a Python Element subclass + directly, without it being mapped to an existing XML node. + """ + cdef xmlDoc* c_doc + cdef xmlNode* c_node + cdef _Document doc + if is_html: + _htmlTagValidOrRaise(name_utf) + c_doc = _newHTMLDoc() + else: + _tagValidOrRaise(name_utf) + c_doc = _newXMLDoc() + c_node = _createElement(c_doc, name_utf) + if c_node is NULL: + if c_doc is not NULL: + tree.xmlFreeDoc(c_doc) + raise MemoryError() + tree.xmlDocSetRootElement(c_doc, c_node) + doc = _documentFactory(c_doc, parser) + # add namespaces to node if necessary + _setNodeNamespaces(c_node, doc, ns_utf, nsmap) + _initNodeAttributes(c_node, doc, attrib, extra_attrs) + _registerProxy(element, doc, c_node) + element._init() + return 0 + +cdef _Element _makeSubElement(_Element parent, tag, text, tail, + attrib, nsmap, dict extra_attrs): + """Create a new child element and initialize text content, namespaces and + attributes. + """ + cdef xmlNode* c_node + cdef xmlDoc* c_doc + if parent is None or parent._doc is None: + return None + _assertValidNode(parent) + ns_utf, name_utf = _getNsTag(tag) + c_doc = parent._doc._c_doc + + if parent._doc._parser is not None and parent._doc._parser._for_html: + _htmlTagValidOrRaise(name_utf) + else: + _tagValidOrRaise(name_utf) + + c_node = _createElement(c_doc, name_utf) + if c_node is NULL: + raise MemoryError() + tree.xmlAddChild(parent._c_node, c_node) + + try: + if text is not None: + _setNodeText(c_node, text) + if tail is not None: + _setTailText(c_node, tail) + + # add namespaces to node if necessary + _setNodeNamespaces(c_node, parent._doc, ns_utf, nsmap) + _initNodeAttributes(c_node, parent._doc, attrib, extra_attrs) + return _elementFactory(parent._doc, c_node) + except: + # make sure we clean up in case of an error + _removeNode(parent._doc, c_node) + raise + + +cdef int _setNodeNamespaces(xmlNode* c_node, _Document doc, + object node_ns_utf, object nsmap) except -1: + """Lookup current namespace prefixes, then set namespace structure for + node (if 'node_ns_utf' was provided) and register new ns-prefix mappings. + + 'node_ns_utf' should only be passed for a newly created node. + """ + cdef xmlNs* c_ns + cdef list nsdefs + + if nsmap: + for prefix, href in _iter_nsmap(nsmap): + href_utf = _utf8(href) + _uriValidOrRaise(href_utf) + c_href = _xcstr(href_utf) + if prefix is not None: + prefix_utf = _utf8(prefix) + _prefixValidOrRaise(prefix_utf) + c_prefix = _xcstr(prefix_utf) + else: + c_prefix = NULL + # add namespace with prefix if it is not already known + c_ns = tree.xmlSearchNs(doc._c_doc, c_node, c_prefix) + if c_ns is NULL or \ + c_ns.href is NULL or \ + tree.xmlStrcmp(c_ns.href, c_href) != 0: + c_ns = tree.xmlNewNs(c_node, c_href, c_prefix) + if href_utf == node_ns_utf: + tree.xmlSetNs(c_node, c_ns) + node_ns_utf = None + + if node_ns_utf is not None: + _uriValidOrRaise(node_ns_utf) + doc._setNodeNs(c_node, _xcstr(node_ns_utf)) + return 0 + + +cdef dict _build_nsmap(xmlNode* c_node): + """ + Namespace prefix->URI mapping known in the context of this Element. + This includes all namespace declarations of the parents. + """ + cdef xmlNs* c_ns + nsmap = {} + while c_node is not NULL and c_node.type == tree.XML_ELEMENT_NODE: + c_ns = c_node.nsDef + while c_ns is not NULL: + if c_ns.prefix or c_ns.href: + prefix = funicodeOrNone(c_ns.prefix) + if prefix not in nsmap: + nsmap[prefix] = funicodeOrNone(c_ns.href) + c_ns = c_ns.next + c_node = c_node.parent + return nsmap + + +cdef _iter_nsmap(nsmap): + """ + Create a reproducibly ordered iterable from an nsmap mapping. + Tries to preserve an existing order and sorts if it assumes no order. + + The difference to _iter_attrib() is that None doesn't sort with strings + in Py3.x. + """ + if isinstance(nsmap, dict): + # dicts are insertion-ordered in Py3.6+ => keep the user provided order. + return nsmap.items() + if len(nsmap) <= 1: + return nsmap.items() + # nsmap will usually be a plain unordered dict => avoid type checking overhead + if type(nsmap) is not dict and isinstance(nsmap, OrderedDict): + return nsmap.items() # keep existing order + if None not in nsmap: + return sorted(nsmap.items()) + + # Move the default namespace to the end. This makes sure libxml2 + # prefers a prefix if the ns is defined redundantly on the same + # element. That way, users can work around a problem themselves + # where default namespace attributes on non-default namespaced + # elements serialise without prefix (i.e. into the non-default + # namespace). + default_ns = nsmap[None] + nsdefs = [(k, v) for k, v in nsmap.items() if k is not None] + nsdefs.sort() + nsdefs.append((None, default_ns)) + return nsdefs + + +cdef _iter_attrib(attrib): + """ + Create a reproducibly ordered iterable from an attrib mapping. + Tries to preserve an existing order and sorts if it assumes no order. + """ + # dicts are insertion-ordered in Py3.6+ => keep the user provided order. + if isinstance(attrib, (dict, _Attrib, OrderedDict)): + return attrib.items() + # assume it's an unordered mapping of some kind + return sorted(attrib.items()) + + +cdef _initNodeAttributes(xmlNode* c_node, _Document doc, attrib, dict extra): + """Initialise the attributes of an element node. + """ + cdef bint is_html + cdef xmlNs* c_ns + if attrib is not None and not hasattr(attrib, 'items'): + raise TypeError, f"Invalid attribute dictionary: {python._fqtypename(attrib).decode('utf8')}" + if not attrib and not extra: + return # nothing to do + is_html = doc._parser._for_html + seen = set() + if extra: + for name, value in extra.items(): + _addAttributeToNode(c_node, doc, is_html, name, value, seen) + if attrib: + for name, value in _iter_attrib(attrib): + _addAttributeToNode(c_node, doc, is_html, name, value, seen) + + +cdef int _addAttributeToNode(xmlNode* c_node, _Document doc, bint is_html, + name, value, set seen_tags) except -1: + ns_utf, name_utf = tag = _getNsTag(name) + if tag in seen_tags: + return 0 + seen_tags.add(tag) + if not is_html: + _attributeValidOrRaise(name_utf) + value_utf = _utf8(value) + if ns_utf is None: + tree.xmlNewProp(c_node, _xcstr(name_utf), _xcstr(value_utf)) + else: + _uriValidOrRaise(ns_utf) + c_ns = doc._findOrBuildNodeNs(c_node, _xcstr(ns_utf), NULL, 1) + tree.xmlNewNsProp(c_node, c_ns, + _xcstr(name_utf), _xcstr(value_utf)) + return 0 + + +ctypedef struct _ns_node_ref: + xmlNs* ns + xmlNode* node + + +cdef int _collectNsDefs(xmlNode* c_element, _ns_node_ref **_c_ns_list, + size_t *_c_ns_list_len, size_t *_c_ns_list_size) except -1: + c_ns_list = _c_ns_list[0] + cdef size_t c_ns_list_len = _c_ns_list_len[0] + cdef size_t c_ns_list_size = _c_ns_list_size[0] + + c_nsdef = c_element.nsDef + while c_nsdef is not NULL: + if c_ns_list_len >= c_ns_list_size: + if c_ns_list is NULL: + c_ns_list_size = 20 + else: + c_ns_list_size *= 2 + c_nsref_ptr = <_ns_node_ref*> python.lxml_realloc( + c_ns_list, c_ns_list_size, sizeof(_ns_node_ref)) + if c_nsref_ptr is NULL: + if c_ns_list is not NULL: + python.lxml_free(c_ns_list) + _c_ns_list[0] = NULL + raise MemoryError() + c_ns_list = c_nsref_ptr + + c_ns_list[c_ns_list_len] = _ns_node_ref(c_nsdef, c_element) + c_ns_list_len += 1 + c_nsdef = c_nsdef.next + + _c_ns_list_size[0] = c_ns_list_size + _c_ns_list_len[0] = c_ns_list_len + _c_ns_list[0] = c_ns_list + + +cdef int _removeUnusedNamespaceDeclarations(xmlNode* c_element, set prefixes_to_keep) except -1: + """Remove any namespace declarations from a subtree that are not used by + any of its elements (or attributes). + + If a 'prefixes_to_keep' is provided, it must be a set of prefixes. + Any corresponding namespace mappings will not be removed as part of the cleanup. + """ + cdef xmlNode* c_node + cdef _ns_node_ref* c_ns_list = NULL + cdef size_t c_ns_list_size = 0 + cdef size_t c_ns_list_len = 0 + cdef size_t i + + if c_element.parent and c_element.parent.type == tree.XML_DOCUMENT_NODE: + # include declarations on the document node + _collectNsDefs(c_element.parent, &c_ns_list, &c_ns_list_len, &c_ns_list_size) + + tree.BEGIN_FOR_EACH_ELEMENT_FROM(c_element, c_element, 1) + # collect all new namespace declarations into the ns list + if c_element.nsDef: + _collectNsDefs(c_element, &c_ns_list, &c_ns_list_len, &c_ns_list_size) + + # remove all namespace declarations from the list that are referenced + if c_ns_list_len and c_element.type == tree.XML_ELEMENT_NODE: + c_node = c_element + while c_node and c_ns_list_len: + if c_node.ns: + for i in range(c_ns_list_len): + if c_node.ns is c_ns_list[i].ns: + c_ns_list_len -= 1 + c_ns_list[i] = c_ns_list[c_ns_list_len] + #c_ns_list[c_ns_list_len] = _ns_node_ref(NULL, NULL) + break + if c_node is c_element: + # continue with attributes + c_node = c_element.properties + else: + c_node = c_node.next + tree.END_FOR_EACH_ELEMENT_FROM(c_element) + + if c_ns_list is NULL: + return 0 + + # free all namespace declarations that remained in the list, + # except for those we should keep explicitly + cdef xmlNs* c_nsdef + for i in range(c_ns_list_len): + if prefixes_to_keep is not None: + if c_ns_list[i].ns.prefix and c_ns_list[i].ns.prefix in prefixes_to_keep: + continue + c_node = c_ns_list[i].node + c_nsdef = c_node.nsDef + if c_nsdef is c_ns_list[i].ns: + c_node.nsDef = c_node.nsDef.next + else: + while c_nsdef.next is not c_ns_list[i].ns: + c_nsdef = c_nsdef.next + c_nsdef.next = c_nsdef.next.next + tree.xmlFreeNs(c_ns_list[i].ns) + + if c_ns_list is not NULL: + python.lxml_free(c_ns_list) + return 0 + +cdef xmlNs* _searchNsByHref(xmlNode* c_node, const_xmlChar* c_href, bint is_attribute) noexcept: + """Search a namespace declaration that covers a node (element or + attribute). + + For attributes, try to find a prefixed namespace declaration + instead of the default namespaces. This helps in supporting + round-trips for attributes on elements with a different namespace. + """ + cdef xmlNs* c_ns + cdef xmlNs* c_default_ns = NULL + cdef xmlNode* c_element + if c_href is NULL or c_node is NULL or c_node.type == tree.XML_ENTITY_REF_NODE: + return NULL + if tree.xmlStrcmp(c_href, tree.XML_XML_NAMESPACE) == 0: + # no special cases here, let libxml2 handle this + return tree.xmlSearchNsByHref(c_node.doc, c_node, c_href) + if c_node.type == tree.XML_ATTRIBUTE_NODE: + is_attribute = 1 + while c_node is not NULL and c_node.type != tree.XML_ELEMENT_NODE: + c_node = c_node.parent + c_element = c_node + while c_node is not NULL: + if c_node.type == tree.XML_ELEMENT_NODE: + c_ns = c_node.nsDef + while c_ns is not NULL: + if c_ns.href is not NULL and tree.xmlStrcmp(c_href, c_ns.href) == 0: + if c_ns.prefix is NULL and is_attribute: + # for attributes, continue searching a named + # prefix, but keep the first default namespace + # declaration that we found + if c_default_ns is NULL: + c_default_ns = c_ns + elif tree.xmlSearchNs( + c_element.doc, c_element, c_ns.prefix) is c_ns: + # start node is in namespace scope => found! + return c_ns + c_ns = c_ns.next + if c_node is not c_element and c_node.ns is not NULL: + # optimise: the node may have the namespace itself + c_ns = c_node.ns + if c_ns.href is not NULL and tree.xmlStrcmp(c_href, c_ns.href) == 0: + if c_ns.prefix is NULL and is_attribute: + # for attributes, continue searching a named + # prefix, but keep the first default namespace + # declaration that we found + if c_default_ns is NULL: + c_default_ns = c_ns + elif tree.xmlSearchNs( + c_element.doc, c_element, c_ns.prefix) is c_ns: + # start node is in namespace scope => found! + return c_ns + c_node = c_node.parent + # nothing found => use a matching default namespace or fail + if c_default_ns is not NULL: + if tree.xmlSearchNs(c_element.doc, c_element, NULL) is c_default_ns: + return c_default_ns + return NULL + +cdef int _replaceNodeByChildren(_Document doc, xmlNode* c_node) except -1: + # NOTE: this does not deallocate the node, just unlink it! + cdef xmlNode* c_parent + cdef xmlNode* c_child + if c_node.children is NULL: + tree.xmlUnlinkNode(c_node) + return 0 + + c_parent = c_node.parent + # fix parent links of children + c_child = c_node.children + while c_child is not NULL: + c_child.parent = c_parent + c_child = c_child.next + + # fix namespace references of children if their parent's namespace + # declarations get lost + if c_node.nsDef is not NULL: + c_child = c_node.children + while c_child is not NULL: + moveNodeToDocument(doc, doc._c_doc, c_child) + c_child = c_child.next + + # fix sibling links to/from child slice + if c_node.prev is NULL: + c_parent.children = c_node.children + else: + c_node.prev.next = c_node.children + c_node.children.prev = c_node.prev + if c_node.next is NULL: + c_parent.last = c_node.last + else: + c_node.next.prev = c_node.last + c_node.last.next = c_node.next + + # unlink c_node + c_node.children = c_node.last = NULL + c_node.parent = c_node.next = c_node.prev = NULL + return 0 + +cdef unicode _attributeValue(xmlNode* c_element, xmlAttr* c_attrib_node): + c_href = _getNs(c_attrib_node) + value = tree.xmlGetNsProp(c_element, c_attrib_node.name, c_href) + try: + result = funicode(value) + finally: + tree.xmlFree(value) + return result + +cdef unicode _attributeValueFromNsName(xmlNode* c_element, + const_xmlChar* c_href, const_xmlChar* c_name): + c_result = tree.xmlGetNsProp(c_element, c_name, c_href) + if c_result is NULL: + return None + try: + result = funicode(c_result) + finally: + tree.xmlFree(c_result) + return result + +cdef object _getNodeAttributeValue(xmlNode* c_node, key, default): + ns, tag = _getNsTag(key) + c_href = NULL if ns is None else _xcstr(ns) + c_result = tree.xmlGetNsProp(c_node, _xcstr(tag), c_href) + if c_result is NULL: + # XXX free namespace that is not in use..? + return default + try: + result = funicode(c_result) + finally: + tree.xmlFree(c_result) + return result + +cdef inline object _getAttributeValue(_Element element, key, default): + return _getNodeAttributeValue(element._c_node, key, default) + +cdef int _setAttributeValue(_Element element, key, value) except -1: + cdef const_xmlChar* c_value + cdef xmlNs* c_ns + ns, tag = _getNsTag(key) + is_html = element._doc._parser._for_html + if not is_html: + _attributeValidOrRaise(tag) + c_tag = _xcstr(tag) + if value is None and is_html: + c_value = NULL + else: + if isinstance(value, QName): + value = _resolveQNameText(element, value) + else: + value = _utf8(value) + c_value = _xcstr(value) + if ns is None: + c_ns = NULL + else: + c_ns = element._doc._findOrBuildNodeNs(element._c_node, _xcstr(ns), NULL, 1) + tree.xmlSetNsProp(element._c_node, c_ns, c_tag, c_value) + return 0 + +cdef int _delAttribute(_Element element, key) except -1: + ns, tag = _getNsTag(key) + c_href = NULL if ns is None else _xcstr(ns) + if _delAttributeFromNsName(element._c_node, c_href, _xcstr(tag)): + raise KeyError, key + return 0 + +cdef int _delAttributeFromNsName(xmlNode* c_node, const_xmlChar* c_href, const_xmlChar* c_name) noexcept: + c_attr = tree.xmlHasNsProp(c_node, c_name, c_href) + if c_attr is NULL: + # XXX free namespace that is not in use..? + return -1 + tree.xmlRemoveProp(c_attr) + return 0 + +cdef list _collectAttributes(xmlNode* c_node, int collecttype): + """Collect all attributes of a node in a list. Depending on collecttype, + it collects either the name (1), the value (2) or the name-value tuples. + """ + cdef Py_ssize_t count + c_attr = c_node.properties + count = 0 + while c_attr is not NULL: + if c_attr.type == tree.XML_ATTRIBUTE_NODE: + count += 1 + c_attr = c_attr.next + + if not count: + return [] + + attributes = [None] * count + c_attr = c_node.properties + count = 0 + while c_attr is not NULL: + if c_attr.type == tree.XML_ATTRIBUTE_NODE: + if collecttype == 1: + item = _namespacedName(c_attr) + elif collecttype == 2: + item = _attributeValue(c_node, c_attr) + else: + item = (_namespacedName(c_attr), + _attributeValue(c_node, c_attr)) + attributes[count] = item + count += 1 + c_attr = c_attr.next + return attributes + +cdef object __RE_XML_ENCODING = re.compile( + r'^(<\?xml[^>]+)\s+encoding\s*=\s*["\'][^"\']*["\'](\s*\?>|)', re.U) + +cdef object __REPLACE_XML_ENCODING = __RE_XML_ENCODING.sub +cdef object __HAS_XML_ENCODING = __RE_XML_ENCODING.match + +cdef object _stripEncodingDeclaration(object xml_string): + # this is a hack to remove the XML encoding declaration from unicode + return __REPLACE_XML_ENCODING(r'\g<1>\g<2>', xml_string) + +cdef bint _hasEncodingDeclaration(object xml_string) except -1: + # check if a (unicode) string has an XML encoding declaration + return __HAS_XML_ENCODING(xml_string) is not None + +cdef inline bint _hasText(xmlNode* c_node) noexcept: + return c_node is not NULL and _textNodeOrSkip(c_node.children) is not NULL + +cdef inline bint _hasTail(xmlNode* c_node) noexcept: + return c_node is not NULL and _textNodeOrSkip(c_node.next) is not NULL + +cdef inline bint _hasNonWhitespaceTail(xmlNode* c_node) except -1: + return _hasNonWhitespaceText(c_node, tail=True) + +cdef bint _hasNonWhitespaceText(xmlNode* c_node, bint tail=False) except -1: + c_text_node = c_node and _textNodeOrSkip(c_node.next if tail else c_node.children) + if c_text_node is NULL: + return False + while c_text_node is not NULL: + if c_text_node.content[0] != c'\0' and not _collectText(c_text_node).isspace(): + return True + c_text_node = _textNodeOrSkip(c_text_node.next) + return False + +cdef unicode _collectText(xmlNode* c_node): + """Collect all text nodes and return them as a unicode string. + + Start collecting at c_node. + + If there was no text to collect, return None + """ + cdef Py_ssize_t scount + cdef xmlChar* c_text + cdef xmlNode* c_node_cur + # check for multiple text nodes + scount = 0 + c_text = NULL + c_node_cur = c_node = _textNodeOrSkip(c_node) + while c_node_cur is not NULL: + if c_node_cur.content[0] != c'\0': + c_text = c_node_cur.content + scount += 1 + c_node_cur = _textNodeOrSkip(c_node_cur.next) + + # handle two most common cases first + if c_text is NULL: + return '' if scount > 0 else None + if scount == 1: + return funicode(c_text) + + # the rest is not performance critical anymore + result = b'' + while c_node is not NULL: + result += c_node.content + c_node = _textNodeOrSkip(c_node.next) + return funicode(result) + +cdef void _removeText(xmlNode* c_node) noexcept: + """Remove all text nodes. + + Start removing at c_node. + """ + cdef xmlNode* c_next + c_node = _textNodeOrSkip(c_node) + while c_node is not NULL: + c_next = _textNodeOrSkip(c_node.next) + tree.xmlUnlinkNode(c_node) + tree.xmlFreeNode(c_node) + c_node = c_next + +cdef xmlNode* _createTextNode(xmlDoc* doc, value) except NULL: + cdef xmlNode* c_text_node + if isinstance(value, CDATA): + c_text_node = tree.xmlNewCDataBlock( + doc, _xcstr((value)._utf8_data), + python.PyBytes_GET_SIZE((value)._utf8_data)) + else: + text = _utf8(value) + c_text_node = tree.xmlNewDocText(doc, _xcstr(text)) + if not c_text_node: + raise MemoryError() + return c_text_node + +cdef int _setNodeText(xmlNode* c_node, value) except -1: + # remove all text nodes at the start first + _removeText(c_node.children) + if value is None: + return 0 + # now add new text node with value at start + c_text_node = _createTextNode(c_node.doc, value) + if c_node.children is NULL: + tree.xmlAddChild(c_node, c_text_node) + else: + tree.xmlAddPrevSibling(c_node.children, c_text_node) + return 0 + +cdef int _setTailText(xmlNode* c_node, value) except -1: + # remove all text nodes at the start first + _removeText(c_node.next) + if value is None: + return 0 + # now append new text node with value + c_text_node = _createTextNode(c_node.doc, value) + tree.xmlAddNextSibling(c_node, c_text_node) + return 0 + +cdef bytes _resolveQNameText(_Element element, value): + cdef xmlNs* c_ns + ns, tag = _getNsTag(value) + if ns is None: + return tag + else: + c_ns = element._doc._findOrBuildNodeNs( + element._c_node, _xcstr(ns), NULL, 0) + return python.PyBytes_FromFormat('%s:%s', c_ns.prefix, _cstr(tag)) + +cdef inline bint _hasChild(xmlNode* c_node) noexcept: + return c_node is not NULL and _findChildForwards(c_node, 0) is not NULL + +cdef inline Py_ssize_t _countElements(xmlNode* c_node) noexcept: + "Counts the elements within the following siblings and the node itself." + cdef Py_ssize_t count + count = 0 + while c_node is not NULL: + if _isElement(c_node): + count += 1 + c_node = c_node.next + return count + +cdef int _findChildSlice( + slice sliceobject, xmlNode* c_parent, + xmlNode** c_start_node, Py_ssize_t* c_step, Py_ssize_t* c_length) except -1: + """Resolve a children slice. + + Returns the start node, step size and the slice length in the + pointer arguments. + """ + cdef Py_ssize_t start = 0, stop = 0, childcount + childcount = _countElements(c_parent.children) + if childcount == 0: + c_start_node[0] = NULL + c_length[0] = 0 + if sliceobject.step is None: + c_step[0] = 1 + else: + python._PyEval_SliceIndex(sliceobject.step, c_step) + return 0 + python.PySlice_GetIndicesEx( + sliceobject, childcount, &start, &stop, c_step, c_length) + if start > childcount // 2: + c_start_node[0] = _findChildBackwards(c_parent, childcount - start - 1) + else: + c_start_node[0] = _findChild(c_parent, start) + return 0 + +cdef bint _isFullSlice(slice sliceobject) except -1: + """Conservative guess if this slice is a full slice as in ``s[:]``. + """ + cdef Py_ssize_t step = 0 + if sliceobject is None: + return 0 + if sliceobject.start is None and \ + sliceobject.stop is None: + if sliceobject.step is None: + return 1 + python._PyEval_SliceIndex(sliceobject.step, &step) + if step == 1: + return 1 + return 0 + return 0 + +cdef _collectChildren(_Element element): + cdef xmlNode* c_node + cdef list result = [] + c_node = element._c_node.children + if c_node is not NULL: + if not _isElement(c_node): + c_node = _nextElement(c_node) + while c_node is not NULL: + result.append(_elementFactory(element._doc, c_node)) + c_node = _nextElement(c_node) + return result + +cdef inline xmlNode* _findChild(xmlNode* c_node, Py_ssize_t index) noexcept: + if index < 0: + return _findChildBackwards(c_node, -index - 1) + else: + return _findChildForwards(c_node, index) + +cdef inline xmlNode* _findChildForwards(xmlNode* c_node, Py_ssize_t index) noexcept: + """Return child element of c_node with index, or return NULL if not found. + """ + cdef xmlNode* c_child + cdef Py_ssize_t c + c_child = c_node.children + c = 0 + while c_child is not NULL: + if _isElement(c_child): + if c == index: + return c_child + c += 1 + c_child = c_child.next + return NULL + +cdef inline xmlNode* _findChildBackwards(xmlNode* c_node, Py_ssize_t index) noexcept: + """Return child element of c_node with index, or return NULL if not found. + Search from the end. + """ + cdef xmlNode* c_child + cdef Py_ssize_t c + c_child = c_node.last + c = 0 + while c_child is not NULL: + if _isElement(c_child): + if c == index: + return c_child + c += 1 + c_child = c_child.prev + return NULL + +cdef inline xmlNode* _textNodeOrSkip(xmlNode* c_node) noexcept nogil: + """Return the node if it's a text node. Skip over ignorable nodes in a + series of text nodes. Return NULL if a non-ignorable node is found. + + This is used to skip over XInclude nodes when collecting adjacent text + nodes. + """ + while c_node is not NULL: + if c_node.type == tree.XML_TEXT_NODE or \ + c_node.type == tree.XML_CDATA_SECTION_NODE: + return c_node + elif c_node.type == tree.XML_XINCLUDE_START or \ + c_node.type == tree.XML_XINCLUDE_END: + c_node = c_node.next + else: + return NULL + return NULL + +cdef inline xmlNode* _nextElement(xmlNode* c_node) noexcept: + """Given a node, find the next sibling that is an element. + """ + if c_node is NULL: + return NULL + c_node = c_node.next + while c_node is not NULL: + if _isElement(c_node): + return c_node + c_node = c_node.next + return NULL + +cdef inline xmlNode* _previousElement(xmlNode* c_node) noexcept: + """Given a node, find the next sibling that is an element. + """ + if c_node is NULL: + return NULL + c_node = c_node.prev + while c_node is not NULL: + if _isElement(c_node): + return c_node + c_node = c_node.prev + return NULL + +cdef inline xmlNode* _parentElement(xmlNode* c_node) noexcept: + "Given a node, find the parent element." + if c_node is NULL or not _isElement(c_node): + return NULL + c_node = c_node.parent + if c_node is NULL or not _isElement(c_node): + return NULL + return c_node + +cdef inline bint _tagMatches(xmlNode* c_node, const_xmlChar* c_href, const_xmlChar* c_name) noexcept: + """Tests if the node matches namespace URI and tag name. + + A node matches if it matches both c_href and c_name. + + A node matches c_href if any of the following is true: + * c_href is NULL + * its namespace is NULL and c_href is the empty string + * its namespace string equals the c_href string + + A node matches c_name if any of the following is true: + * c_name is NULL + * its name string equals the c_name string + """ + if c_node is NULL: + return 0 + if c_node.type != tree.XML_ELEMENT_NODE: + # not an element, only succeed if we match everything + return c_name is NULL and c_href is NULL + if c_name is NULL: + if c_href is NULL: + # always match + return 1 + else: + c_node_href = _getNs(c_node) + if c_node_href is NULL: + return c_href[0] == c'\0' + else: + return tree.xmlStrcmp(c_node_href, c_href) == 0 + elif c_href is NULL: + if _getNs(c_node) is not NULL: + return 0 + return c_node.name == c_name or tree.xmlStrcmp(c_node.name, c_name) == 0 + elif c_node.name == c_name or tree.xmlStrcmp(c_node.name, c_name) == 0: + c_node_href = _getNs(c_node) + if c_node_href is NULL: + return c_href[0] == c'\0' + else: + return tree.xmlStrcmp(c_node_href, c_href) == 0 + else: + return 0 + +cdef inline bint _tagMatchesExactly(xmlNode* c_node, qname* c_qname) noexcept: + """Tests if the node matches namespace URI and tag name. + + This differs from _tagMatches() in that it does not consider a + NULL value in qname.href a wildcard, and that it expects the c_name + to be taken from the doc dict, i.e. it only compares the names by + address. + + A node matches if it matches both href and c_name of the qname. + + A node matches c_href if any of the following is true: + * its namespace is NULL and c_href is the empty string + * its namespace string equals the c_href string + + A node matches c_name if any of the following is true: + * c_name is NULL + * its name string points to the same address (!) as c_name + """ + return _nsTagMatchesExactly(_getNs(c_node), c_node.name, c_qname) + +cdef inline bint _nsTagMatchesExactly(const_xmlChar* c_node_href, + const_xmlChar* c_node_name, + qname* c_qname) noexcept: + """Tests if name and namespace URI match those of c_qname. + + This differs from _tagMatches() in that it does not consider a + NULL value in qname.href a wildcard, and that it expects the c_name + to be taken from the doc dict, i.e. it only compares the names by + address. + + A node matches if it matches both href and c_name of the qname. + + A node matches c_href if any of the following is true: + * its namespace is NULL and c_href is the empty string + * its namespace string equals the c_href string + + A node matches c_name if any of the following is true: + * c_name is NULL + * its name string points to the same address (!) as c_name + """ + cdef char* c_href + if c_qname.c_name is not NULL and c_qname.c_name is not c_node_name: + return 0 + if c_qname.href is NULL: + return 1 + c_href = python.__cstr(c_qname.href) + if c_href[0] == b'\0': + return c_node_href is NULL or c_node_href[0] == b'\0' + elif c_node_href is NULL: + return 0 + else: + return tree.xmlStrcmp(c_href, c_node_href) == 0 + +cdef Py_ssize_t _mapTagsToQnameMatchArray(xmlDoc* c_doc, list ns_tags, + qname* c_ns_tags, bint force_into_dict) except -1: + """Map a sequence of (name, namespace) pairs to a qname array for efficient + matching with _tagMatchesExactly() above. + + Note that each qname struct in the array owns its href byte string object + if it is not NULL. + """ + cdef Py_ssize_t count = 0, i, c_tag_len + cdef bytes ns, tag + cdef const_xmlChar* c_tag + + for ns, tag in ns_tags: + if tag is None: + c_tag = NULL + else: + c_tag_len = len(tag) + if c_tag_len > limits.INT_MAX: + # too long, not in the dict => not in the document + continue + elif force_into_dict: + c_tag = tree.xmlDictLookup(c_doc.dict, _xcstr(tag), c_tag_len) + if c_tag is NULL: + # clean up before raising the error + for i in xrange(count): + cpython.ref.Py_XDECREF(c_ns_tags[i].href) + raise MemoryError() + else: + c_tag = tree.xmlDictExists(c_doc.dict, _xcstr(tag), c_tag_len) + if c_tag is NULL: + # not in the dict => not in the document + continue + + c_ns_tags[count].c_name = c_tag + if ns is None: + c_ns_tags[count].href = NULL + else: + cpython.ref.Py_INCREF(ns) # keep an owned reference! + c_ns_tags[count].href = ns + count += 1 + return count + +cdef int _removeNode(_Document doc, xmlNode* c_node) except -1: + """Unlink and free a node and subnodes if possible. Otherwise, make sure + it's self-contained. + """ + cdef xmlNode* c_next + c_next = c_node.next + tree.xmlUnlinkNode(c_node) + _moveTail(c_next, c_node) + if not attemptDeallocation(c_node): + # make namespaces absolute + moveNodeToDocument(doc, c_node.doc, c_node) + return 0 + +cdef int _removeSiblings(xmlNode* c_element, tree.xmlElementType node_type, bint with_tail) except -1: + cdef xmlNode* c_node + cdef xmlNode* c_next + c_node = c_element.next + while c_node is not NULL: + c_next = _nextElement(c_node) + if c_node.type == node_type: + if with_tail: + _removeText(c_node.next) + tree.xmlUnlinkNode(c_node) + attemptDeallocation(c_node) + c_node = c_next + c_node = c_element.prev + while c_node is not NULL: + c_next = _previousElement(c_node) + if c_node.type == node_type: + if with_tail: + _removeText(c_node.next) + tree.xmlUnlinkNode(c_node) + attemptDeallocation(c_node) + c_node = c_next + return 0 + +cdef void _moveTail(xmlNode* c_tail, xmlNode* c_target) noexcept: + cdef xmlNode* c_next + # tail support: look for any text nodes trailing this node and + # move them too + c_tail = _textNodeOrSkip(c_tail) + while c_tail is not NULL: + c_next = _textNodeOrSkip(c_tail.next) + c_target = tree.xmlAddNextSibling(c_target, c_tail) + c_tail = c_next + +cdef int _copyTail(xmlNode* c_tail, xmlNode* c_target) except -1: + cdef xmlNode* c_new_tail + # tail copying support: look for any text nodes trailing this node and + # copy it to the target node + c_tail = _textNodeOrSkip(c_tail) + while c_tail is not NULL: + if c_target.doc is not c_tail.doc: + c_new_tail = tree.xmlDocCopyNode(c_tail, c_target.doc, 0) + else: + c_new_tail = tree.xmlCopyNode(c_tail, 0) + if c_new_tail is NULL: + raise MemoryError() + c_target = tree.xmlAddNextSibling(c_target, c_new_tail) + c_tail = _textNodeOrSkip(c_tail.next) + return 0 + +cdef int _copyNonElementSiblings(xmlNode* c_node, xmlNode* c_target) except -1: + cdef xmlNode* c_copy + cdef xmlNode* c_sibling = c_node + while c_sibling.prev != NULL and \ + (c_sibling.prev.type == tree.XML_PI_NODE or + c_sibling.prev.type == tree.XML_COMMENT_NODE or + c_sibling.prev.type == tree.XML_DTD_NODE): + c_sibling = c_sibling.prev + while c_sibling != c_node: + if c_sibling.type == tree.XML_DTD_NODE: + c_copy = _copyDtd(c_sibling) + if c_sibling == c_node.doc.intSubset: + c_target.doc.intSubset = c_copy + else: # c_sibling == c_node.doc.extSubset + c_target.doc.extSubset = c_copy + else: + c_copy = tree.xmlDocCopyNode(c_sibling, c_target.doc, 1) + if c_copy is NULL: + raise MemoryError() + tree.xmlAddPrevSibling(c_target, c_copy) + c_sibling = c_sibling.next + while c_sibling.next != NULL and \ + (c_sibling.next.type == tree.XML_PI_NODE or + c_sibling.next.type == tree.XML_COMMENT_NODE): + c_sibling = c_sibling.next + c_copy = tree.xmlDocCopyNode(c_sibling, c_target.doc, 1) + if c_copy is NULL: + raise MemoryError() + tree.xmlAddNextSibling(c_target, c_copy) + +cdef int _deleteSlice(_Document doc, xmlNode* c_node, + Py_ssize_t count, Py_ssize_t step) except -1: + """Delete slice, ``count`` items starting with ``c_node`` with a step + width of ``step``. + """ + cdef xmlNode* c_next + cdef Py_ssize_t c, i + cdef _node_to_node_function next_element + if c_node is NULL: + return 0 + if step > 0: + next_element = _nextElement + else: + step = -step + next_element = _previousElement + # now start deleting nodes + c = 0 + c_next = c_node + while c_node is not NULL and c < count: + for i in range(step): + c_next = next_element(c_next) + if c_next is NULL: + break + _removeNode(doc, c_node) + c += 1 + c_node = c_next + return 0 + +cdef int _replaceSlice(_Element parent, xmlNode* c_node, + Py_ssize_t slicelength, Py_ssize_t step, + bint left_to_right, elements) except -1: + """Replace the slice of ``count`` elements starting at ``c_node`` with + positive step width ``step`` by the Elements in ``elements``. The + direction is given by the boolean argument ``left_to_right``. + + ``c_node`` may be NULL to indicate the end of the children list. + """ + cdef xmlNode* c_orig_neighbour + cdef xmlNode* c_next + cdef xmlDoc* c_source_doc + cdef _Element element + cdef Py_ssize_t seqlength, i, c + cdef _node_to_node_function next_element + assert step > 0 + if left_to_right: + next_element = _nextElement + else: + next_element = _previousElement + + if not isinstance(elements, (list, tuple)): + elements = list(elements) + + if step != 1 or not left_to_right: + # *replacing* children stepwise with list => check size! + seqlength = len(elements) + if seqlength != slicelength: + raise ValueError, f"attempt to assign sequence of size {seqlength} " \ + f"to extended slice of size {slicelength}" + + if c_node is NULL: + # no children yet => add all elements straight away + if left_to_right: + for element in elements: + assert element is not None, "Node must not be None" + _appendChild(parent, element) + else: + for element in elements: + assert element is not None, "Node must not be None" + _prependChild(parent, element) + return 0 + + # remove the elements first as some might be re-added + if left_to_right: + # L->R, remember left neighbour + c_orig_neighbour = _previousElement(c_node) + else: + # R->L, remember right neighbour + c_orig_neighbour = _nextElement(c_node) + + # We remove the original slice elements one by one. Since we hold + # a Python reference to all elements that we will insert, it is + # safe to let _removeNode() try (and fail) to free them even if + # the element itself or one of its descendents will be reinserted. + c = 0 + c_next = c_node + while c_node is not NULL and c < slicelength: + for i in range(step): + c_next = next_element(c_next) + if c_next is NULL: + break + _removeNode(parent._doc, c_node) + c += 1 + c_node = c_next + + # make sure each element is inserted only once + elements = iter(elements) + + # find the first node right of the new insertion point + if left_to_right: + if c_orig_neighbour is not NULL: + c_node = next_element(c_orig_neighbour) + else: + # before the first element + c_node = _findChildForwards(parent._c_node, 0) + elif c_orig_neighbour is NULL: + # at the end, but reversed stepping + # append one element and go to the next insertion point + for element in elements: + assert element is not None, "Node must not be None" + _appendChild(parent, element) + c_node = element._c_node + if slicelength > 0: + slicelength -= 1 + for i in range(1, step): + c_node = next_element(c_node) + if c_node is NULL: + break + break + else: + c_node = c_orig_neighbour + + if left_to_right: + # adjust step size after removing slice as we are not stepping + # over the newly inserted elements + step -= 1 + + # now insert elements where we removed them + if c_node is not NULL: + for element in elements: + assert element is not None, "Node must not be None" + _assertValidNode(element) + # move element and tail over + c_source_doc = element._c_node.doc + c_next = element._c_node.next + tree.xmlAddPrevSibling(c_node, element._c_node) + _moveTail(c_next, element._c_node) + + # integrate element into new document + moveNodeToDocument(parent._doc, c_source_doc, element._c_node) + + # stop at the end of the slice + if slicelength > 0: + slicelength -= 1 + for i in range(step): + c_node = next_element(c_node) + if c_node is NULL: + break + if c_node is NULL: + break + else: + # everything inserted + return 0 + + # append the remaining elements at the respective end + if left_to_right: + for element in elements: + assert element is not None, "Node must not be None" + _assertValidNode(element) + _appendChild(parent, element) + else: + for element in elements: + assert element is not None, "Node must not be None" + _assertValidNode(element) + _prependChild(parent, element) + + return 0 + + +cdef int _linkChild(xmlNode* c_parent, xmlNode* c_node) except -1: + """Adaptation of 'xmlAddChild()' that deep-fix the document links iteratively. + """ + assert _isElement(c_node) + c_node.parent = c_parent + if c_parent.children is NULL: + c_parent.children = c_parent.last = c_node + else: + c_node.prev = c_parent.last + c_parent.last.next = c_node + c_parent.last = c_node + + _setTreeDoc(c_node, c_parent.doc) + return 0 + + +cdef int _appendChild(_Element parent, _Element child) except -1: + """Append a new child to a parent element. + """ + c_node = child._c_node + c_source_doc = c_node.doc + # prevent cycles + if _isAncestorOrSame(c_node, parent._c_node): + raise ValueError("cannot append parent to itself") + # store possible text node + c_next = c_node.next + # move node itself + tree.xmlUnlinkNode(c_node) + # do not call xmlAddChild() here since it would deep-traverse the tree + _linkChild(parent._c_node, c_node) + _moveTail(c_next, c_node) + # uh oh, elements may be pointing to different doc when + # parent element has moved; change them too.. + moveNodeToDocument(parent._doc, c_source_doc, c_node) + return 0 + +cdef int _prependChild(_Element parent, _Element child) except -1: + """Prepend a new child to a parent element. + """ + c_node = child._c_node + c_source_doc = c_node.doc + # prevent cycles + if _isAncestorOrSame(c_node, parent._c_node): + raise ValueError("cannot append parent to itself") + # store possible text node + c_next = c_node.next + # move node itself + c_child = _findChildForwards(parent._c_node, 0) + if c_child is NULL: + tree.xmlUnlinkNode(c_node) + # do not call xmlAddChild() here since it would deep-traverse the tree + _linkChild(parent._c_node, c_node) + else: + tree.xmlAddPrevSibling(c_child, c_node) + _moveTail(c_next, c_node) + # uh oh, elements may be pointing to different doc when + # parent element has moved; change them too.. + moveNodeToDocument(parent._doc, c_source_doc, c_node) + return 0 + +cdef int _appendSibling(_Element element, _Element sibling) except -1: + """Add a new sibling behind an element. + """ + return _addSibling(element, sibling, as_next=True) + +cdef int _prependSibling(_Element element, _Element sibling) except -1: + """Add a new sibling before an element. + """ + return _addSibling(element, sibling, as_next=False) + +cdef int _addSibling(_Element element, _Element sibling, bint as_next) except -1: + c_node = sibling._c_node + c_source_doc = c_node.doc + # prevent cycles + if _isAncestorOrSame(c_node, element._c_node): + if element._c_node is c_node: + return 0 # nothing to do + raise ValueError("cannot add ancestor as sibling, please break cycle first") + # store possible text node + c_next = c_node.next + # move node itself + if as_next: + # must insert after any tail text + c_next_node = _nextElement(element._c_node) + if c_next_node is NULL: + c_next_node = element._c_node + while c_next_node.next: + c_next_node = c_next_node.next + tree.xmlAddNextSibling(c_next_node, c_node) + else: + tree.xmlAddPrevSibling(c_next_node, c_node) + else: + tree.xmlAddPrevSibling(element._c_node, c_node) + _moveTail(c_next, c_node) + # uh oh, elements may be pointing to different doc when + # parent element has moved; change them too.. + moveNodeToDocument(element._doc, c_source_doc, c_node) + return 0 + +cdef inline bint isutf8(const_xmlChar* s) noexcept: + cdef xmlChar c = s[0] + while c != c'\0': + if c & 0x80: + return True + s += 1 + c = s[0] + return False + +cdef bint isutf8l(const_xmlChar* s, size_t length) noexcept: + """ + Search for non-ASCII characters in the string, knowing its length in advance. + """ + cdef unsigned int i + cdef unsigned long non_ascii_mask + cdef const unsigned long *lptr = s + + cdef const unsigned long *end = lptr + length // sizeof(unsigned long) + if length >= sizeof(non_ascii_mask): + # Build constant 0x80808080... mask (and let the C compiler fold it). + non_ascii_mask = 0 + for i in range(sizeof(non_ascii_mask) // 2): + non_ascii_mask = (non_ascii_mask << 16) | 0x8080 + + # Advance to long-aligned character before we start reading longs. + while (s) % sizeof(unsigned long) and s < end: + if s[0] & 0x80: + return True + s += 1 + + # Read one long at a time + lptr = s + while lptr < end: + if lptr[0] & non_ascii_mask: + return True + lptr += 1 + s = lptr + + while s < (end + length % sizeof(unsigned long)): + if s[0] & 0x80: + return True + s += 1 + + return False + +cdef int _is_valid_xml_ascii(bytes pystring) except -1: + """Check if a string is XML ascii content.""" + cdef signed char ch + # When ch is a *signed* char, non-ascii characters are negative integers + # and xmlIsChar_ch does not accept them. + for ch in pystring: + if not tree.xmlIsChar_ch(ch): + return 0 + return 1 + +cdef bint _is_valid_xml_utf8(bytes pystring) except -1: + """Check if a string is like valid UTF-8 XML content.""" + cdef const_xmlChar* s = _xcstr(pystring) + cdef const_xmlChar* c_end = s + len(pystring) + cdef unsigned long next3 = 0 + if s < c_end - 2: + next3 = (s[0] << 8) | (s[1]) + + while s < c_end - 2: + next3 = 0x00ffffff & ((next3 << 8) | s[2]) + if s[0] & 0x80: + # 0xefbfbe and 0xefbfbf are utf-8 encodings of + # forbidden characters \ufffe and \uffff + if next3 == 0x00efbfbe or next3 == 0x00efbfbf: + return 0 + # 0xeda080 and 0xedbfbf are utf-8 encodings of + # \ud800 and \udfff. Anything between them (inclusive) + # is forbidden, because they are surrogate blocks in utf-16. + if 0x00eda080 <= next3 <= 0x00edbfbf: + return 0 + elif not tree.xmlIsChar_ch(s[0]): + return 0 # invalid ascii char + s += 1 + + while s < c_end: + if not s[0] & 0x80 and not tree.xmlIsChar_ch(s[0]): + return 0 # invalid ascii char + s += 1 + + return 1 + +cdef inline unicode funicodeOrNone(const_xmlChar* s): + return funicode(s) if s is not NULL else None + +cdef inline unicode funicodeOrEmpty(const_xmlChar* s): + return funicode(s) if s is not NULL else '' + +cdef unicode funicode(const_xmlChar* s): + return s.decode('UTF-8') + +cdef bytes _utf8(object s): + """Test if a string is valid user input and encode it to UTF-8. + Reject all bytes/unicode input that contains non-XML characters. + Reject all bytes input that contains non-ASCII characters. + """ + cdef int valid + cdef bytes utf8_string + if isinstance(s, unicode): + utf8_string = (s).encode('utf8') + valid = _is_valid_xml_utf8(utf8_string) + elif isinstance(s, (bytes, bytearray)): + utf8_string = s if type(s) is bytes else bytes(s) + valid = _is_valid_xml_ascii(utf8_string) + else: + raise TypeError("Argument must be bytes or unicode, got '%.200s'" % type(s).__name__) + if not valid: + raise ValueError( + "All strings must be XML compatible: Unicode or ASCII, no NULL bytes or control characters") + return utf8_string + + +cdef bytes _utf8orNone(object s): + return _utf8(s) if s is not None else None + + +cdef enum: + NO_FILE_PATH = 0 + ABS_UNIX_FILE_PATH = 1 + ABS_WIN_FILE_PATH = 2 + REL_FILE_PATH = 3 + + +cdef bint _isFilePath(const_xmlChar* c_path) noexcept: + "simple heuristic to see if a path is a filename" + cdef xmlChar c + # test if it looks like an absolute Unix path or a Windows network path + if c_path[0] == c'/': + return ABS_UNIX_FILE_PATH + + # test if it looks like an absolute Windows path or URL + if c'a' <= c_path[0] <= c'z' or c'A' <= c_path[0] <= c'Z': + c_path += 1 + if c_path[0] == c':' and c_path[1] in b'\0\\': + return ABS_WIN_FILE_PATH # C: or C:\... + + # test if it looks like a URL with scheme:// + while c'a' <= c_path[0] <= c'z' or c'A' <= c_path[0] <= c'Z': + c_path += 1 + if c_path[0] == c':' and c_path[1] == c'/' and c_path[2] == c'/': + return NO_FILE_PATH + + # assume it's a relative path + return REL_FILE_PATH + + +cdef object _getFSPathOrObject(object obj): + """ + Get the __fspath__ attribute of an object if it exists. + Otherwise, the original object is returned. + """ + if _isString(obj): + return obj + try: + return python.PyOS_FSPath(obj) + except TypeError: + return obj + + +cdef object _encodeFilename(object filename): + """Make sure a filename is 8-bit encoded (or None). + """ + if filename is None: + return None + elif isinstance(filename, bytes): + return filename + elif isinstance(filename, unicode): + filename8 = (filename).encode('utf8') + if _isFilePath(filename8): + try: + return python.PyUnicode_AsEncodedString( + filename, _C_FILENAME_ENCODING, NULL) + except UnicodeEncodeError: + pass + return filename8 + else: + raise TypeError("Argument must be string or unicode.") + +cdef object _decodeFilename(const_xmlChar* c_path): + """Make the filename a unicode string if we are in Py3. + """ + return _decodeFilenameWithLength(c_path, tree.xmlStrlen(c_path)) + +cdef object _decodeFilenameWithLength(const_xmlChar* c_path, size_t c_len): + """Make the filename a unicode string if we are in Py3. + """ + if _isFilePath(c_path): + try: + return python.PyUnicode_Decode( + c_path, c_len, _C_FILENAME_ENCODING, NULL) + except UnicodeDecodeError: + pass + try: + return (c_path)[:c_len].decode('UTF-8') + except UnicodeDecodeError: + # this is a stupid fallback, but it might still work... + return (c_path)[:c_len].decode('latin-1', 'replace') + +cdef object _encodeFilenameUTF8(object filename): + """Recode filename as UTF-8. Tries ASCII, local filesystem encoding and + UTF-8 as source encoding. + """ + cdef char* c_filename + if filename is None: + return None + elif isinstance(filename, bytes): + if not isutf8l(filename, len(filename)): + # plain ASCII! + return filename + c_filename = _cstr(filename) + try: + # try to decode with default encoding + filename = python.PyUnicode_Decode( + c_filename, len(filename), + _C_FILENAME_ENCODING, NULL) + except UnicodeDecodeError as decode_exc: + try: + # try if it's proper UTF-8 + (filename).decode('utf8') + return filename + except UnicodeDecodeError: + raise decode_exc # otherwise re-raise original exception + if isinstance(filename, unicode): + return (filename).encode('utf8') + else: + raise TypeError("Argument must be string or unicode.") + +cdef tuple _getNsTag(tag): + """Given a tag, find namespace URI and tag name. + Return None for NS uri if no namespace URI provided. + """ + return __getNsTag(tag, 0) + +cdef tuple _getNsTagWithEmptyNs(tag): + """Given a tag, find namespace URI and tag name. Return None for NS uri + if no namespace URI provided, or the empty string if namespace + part is '{}'. + """ + return __getNsTag(tag, 1) + +cdef tuple __getNsTag(tag, bint empty_ns): + cdef char* c_tag + cdef char* c_ns_end + cdef Py_ssize_t taglen + cdef Py_ssize_t nslen + cdef bytes ns = None + # _isString() is much faster than isinstance() + if not _isString(tag) and isinstance(tag, QName): + tag = (tag).text + tag = _utf8(tag) + c_tag = _cstr(tag) + if c_tag[0] == c'{': + c_tag += 1 + c_ns_end = cstring_h.strchr(c_tag, c'}') + if c_ns_end is NULL: + raise ValueError, "Invalid tag name" + nslen = c_ns_end - c_tag + taglen = python.PyBytes_GET_SIZE(tag) - nslen - 2 + if taglen == 0: + raise ValueError, "Empty tag name" + if nslen > 0: + ns = c_tag[:nslen] + elif empty_ns: + ns = b'' + tag = c_ns_end[1:taglen+1] + elif python.PyBytes_GET_SIZE(tag) == 0: + raise ValueError, "Empty tag name" + return ns, tag + +cdef inline int _pyXmlNameIsValid(name_utf8): + return _xmlNameIsValid(_xcstr(name_utf8)) and b':' not in name_utf8 + +cdef inline int _pyHtmlNameIsValid(name_utf8): + return _htmlNameIsValid(_xcstr(name_utf8)) + +cdef inline int _xmlNameIsValid(const_xmlChar* c_name) noexcept: + return tree.xmlValidateNameValue(c_name) + +cdef int _htmlNameIsValid(const_xmlChar* c_name) noexcept: + if c_name is NULL or c_name[0] == c'\0': + return 0 + while c_name[0] != c'\0': + if c_name[0] in b'&<>/"\'\t\n\x0B\x0C\r ': + return 0 + c_name += 1 + return 1 + +cdef bint _characterReferenceIsValid(const_xmlChar* c_name) noexcept: + cdef bint is_hex + if c_name[0] == c'x': + c_name += 1 + is_hex = 1 + else: + is_hex = 0 + if c_name[0] == c'\0': + return 0 + while c_name[0] != c'\0': + if c_name[0] < c'0' or c_name[0] > c'9': + if not is_hex: + return 0 + if not (c'a' <= c_name[0] <= c'f'): + if not (c'A' <= c_name[0] <= c'F'): + return 0 + c_name += 1 + return 1 + +cdef int _tagValidOrRaise(tag_utf) except -1: + if not _pyXmlNameIsValid(tag_utf): + raise ValueError(f"Invalid tag name {(tag_utf).decode('utf8')!r}") + return 0 + +cdef int _htmlTagValidOrRaise(tag_utf) except -1: + if not _pyHtmlNameIsValid(tag_utf): + raise ValueError(f"Invalid HTML tag name {(tag_utf).decode('utf8')!r}") + return 0 + +cdef int _attributeValidOrRaise(name_utf) except -1: + if not _pyXmlNameIsValid(name_utf): + raise ValueError(f"Invalid attribute name {(name_utf).decode('utf8')!r}") + return 0 + +cdef int _prefixValidOrRaise(tag_utf) except -1: + if not _pyXmlNameIsValid(tag_utf): + raise ValueError(f"Invalid namespace prefix {(tag_utf).decode('utf8')!r}") + return 0 + +cdef int _uriValidOrRaise(uri_utf) except -1: + cdef uri.xmlURI* c_uri = uri.xmlParseURI(_cstr(uri_utf)) + if c_uri is NULL: + raise ValueError(f"Invalid namespace URI {(uri_utf).decode('utf8')!r}") + uri.xmlFreeURI(c_uri) + return 0 + +cdef inline unicode _namespacedName(xmlNode* c_node): + return _namespacedNameFromNsName(_getNs(c_node), c_node.name) + + +cdef unicode _namespacedNameFromNsName(const_xmlChar* c_href, const_xmlChar* c_name): + name = funicode(c_name) + if c_href is NULL: + return name + href = funicode(c_href) + return f"{{{href}}}{name}" + + +cdef _getFilenameForFile(source): + """Given a Python File or Gzip object, give filename back. + + Returns None if not a file object. + """ + # urllib2 provides a geturl() method + try: + return source.geturl() + except: + pass + # file instances have a name attribute + try: + filename = source.name + if _isString(filename): + return os_path_abspath(filename) + except: + pass + # gzip file instances have a filename attribute (before Py3k) + try: + filename = source.filename + if _isString(filename): + return os_path_abspath(filename) + except: + pass + # can't determine filename + return None diff --git a/.venv/lib/python3.9/site-packages/lxml/builder.cpython-39-darwin.so b/.venv/lib/python3.9/site-packages/lxml/builder.cpython-39-darwin.so new file mode 100755 index 0000000000000000000000000000000000000000..6f07458c296f3fe613431c4fa0b72ec9eee22c99 GIT binary patch literal 291248 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Μ9@B3SR=6sbU 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ðs`FO5x{vF!>b^#qZL|UnTrp zKPJE8q4-@o`D4P*^=9%b9*W(^Y*Cck2uXZ&^f z#$PU-{JP%F^>6YkR{m~`YxyIl`!1dQx<1bJa`G#tlI|B#fgeBQcj@HU^>nVUlV357 zlHV}Ohx{(x@Lx(pfdJRz4ZmRJ@3Lgi{UV1>eqFEU`aSs-55@1&$*=4CT<<5pV&xC| z+;{2Z*Zlz8A3%P^%3tK;cj?CemstbcPhk8nSo!_&r~3^^H~g1de(pao{DPI=U;p(w zbmRXP%g_A^#{Ytq-yi>74xRi}S6F`TZy>+oq4-@o`E~yT_d}3hvGP}0>%H`M>Eze_ z65KyQe#Ock^YOcM^6U9g+;2gC#mXO$`6=RsKDy!GXdU4G48t#2`8)jl4&C_w8q3f9 z9LE2GmA~7Pz3}UP57No6`#re-gZzq>zs1Mz(#hY|VGVG<2>BHc#qZLM``222?k_R! z3s(LP-}p_>`68YCy8ndxQOK`Y`GdLEe&U5bI{9_K3iq#&Uop+I{;Bf0@6ySy`&+o* zh5U+@zZZ7!M@;u!I{9@!4EM*7Uop*_`kT!75tHAglVA7Ga6b+C71KQV{rT6WlfU?8 z7{ZVHZ;by1D}N;w`0+#c)jTozb$<@`>yTeD>2zPRhsW>I$sfEGhVbM59ut1S${+Bh zpPDD#@OP3s5a50w!{2G3agXi?X%Iht=>K3Rk;UZKbFa96$b|n}89MnZE%f-+JnbXD zo?pfNMkf3|{$d}$npb|^kHq~+Ey2z^W48>!tdj+qCx!lq5Eo{nEblGi~GIEub6b>{$WtyM@)W~PX53H zqy_@qA7=b7Soy0AHN&swNhiPVALD*9@+($;fB(y+lVA6nasL_l6)S%)75MQ(|GRYZ z>;5$ES0le-nrHYsslbmP^1F2M>wY%wZzI2AnkRo175MQ(ewS|C-)hJK?uRq(3s(LC z?mKkj{-c(k`{#`Nf|cK2{t6sA`E`FC_uG+Q@lgCO-S}VZbhmAM(3&^6UOU?iZx{ifNwi z56FK{KodXG$*=nfxxbM7ij}{^n9jKG(vAP0v_fwKzvGN!D+;{2Z zuM~dnza+onq4-@o`8$N4`!&h0cqo3CPW~R@=l)LeD;|p9rJL|SZ3D>tpeFo+hvF}A z=qCJvxqsAz-=Q=A`s1(Cp_}jv=6+KXeuq97e}_Xi;a7}y!h~Nj+#ZZy_pdU1Cj8Gp zfgks?n(zzO@cZiz-S4XWy5E)iU&*gn`ThCdrPF=gAItr+woUA zCBI_j4_dNU|G0GWcMCuFJQTl6Cx75s>p|}KCBNdK_+2{r zgTl}K!Q@vw6u(O+f3fg$KQZ|g55@1&$zLV>D}-P1Q2Z{P{4K)I{mOJ-@lgCOo&24` z&;8BhSFHS1HhMGZFXl-nzwUqLerWP5R{mmW@khMSM<>7Tm*)Oy6MxScEthZp0)=9-2Y8}#mZkf+|r5ZzDp;+?hogFaq=ssdAjc}|1O>Ux}Ti;%gL`; z`Gc^7KVrJ?(#hZRk|77UAD#S)X`b%KEZO6C>Eze_>fFChe#Oe)?c;apz=|2@O6cqo3CPW~3* z=YD?o`^m2u%0cswODDhX|ED|v`4uaFB^}3)nC`oD@~gZ6 zG7)s-QF~0l}li#J2zvWd!4p82}gkP}o56J%xo%||~K=}mnD;|p9rIWu`?4vvb z`4tbv@6ySy@(z@LAiv_F_+2{rRX&39669Ar6u(O+f9y3If0VBvzhdPNTCx}aE}i@x z!cTb&@+%&S-=&kkOZX|jL4L)`?@vFMPJWf|pu7k96)S(S&;Kr+{J~ypfbt>aSFHR& zAHPc{f3fgWo`n30mA}}>@6yTNBK(v;A-`hf_lMu5lfO&&DX&6)#mbLvvH1}%bm)|4 zIRtcyYei=t~?8|E6+mg%Ciu=@+`!zJPWZa&qD0Vvk<%TEX1xn3$ZKDLhQ=3 z5WDg$#I8IGu`AC)?8>tcyYei=t~?8|E6+k)De@nbXCZdwS%_VE7GhVPh1iv6A$H|i zh+TOWVppDpxI^5hJPWZa&qD0Vvk-TQdCId8_Xwsu3-NZrlxHD!tcyYei=JH$T9vk<%TEX1xn z3$ZKDLhQ=35WDg$#I8IGu`AC)?8>tcyYei=t~?8|E6+mg%Ciu=@+`!zJj*d75k8S; zq5R4*d!oH{n4#ni>e`*4L1FZ1Du53lv%jXr#v z58vg(n|=6UAO5`$|HX$t^5GFkFlc${akLMg;KK`j_m$q;T!M6 zU+`hhW4YpY`EC`S8a+JOXXC>%aYd_(&fv^5JBId#Tres-d`J55WFMaC!wY@5%7>eL zc%u*B=EJ}6;pct$Egyd0hxb6=(oL_!eRz@&&-LM@J{TAO4#U z?}xsS>;I#Dc$N>J>ci*waDxwD>cj08!%6%5rVroa!~f&M5BTt7K3t9ZJc4H>o?1M0 zcvj(Aji(+@1D-}aQ9Lm`7vNzV6vvamlf<(I&ssdqcrLv+C_=LS4C;<*VA+p$~l+=}O$cslWX3(xI%?!a>=p6}qB944Jon(a7Z2OMpW^u$p8N1@#`AML|AXfjcz%iJS9tEn^J_fa zc(&kq01w;A-{AQzo`>)}jAtvJNANs~=P^8w<9Pzl@9;c{rw7kdc%H`d44&WPVf*?V zo^5zu!1E%Wm+-uT=T$tf;pxTm2Rwho^E#e4@VtrVEj%eaX*_*+{(|SPc;3eI4xa6J z-o>*6&)@L;FP``Ce1PX;JpaJ+PduOC`8%F|JT-V$;JFab20T~dxeCulJgs=XjHeCH zSMapsxf;(kcslT0i|4C&zJ}*EJm1FiT|D2z!?=7N&&zoJgy+wA-pBJHo{#WU9uds5QhT7a;j=YN3vE-vwdj zE(&w*JhifJxNtQ5S6hHI=$#hS&$`S)HS`t>#d74Z9INcp&D>j|1RgV-QRAix1vsBu zuc&ae%)!<^>~qT81)@gGLGJ4jihEm+YGv-~ z5gqTQ9>Mll@a%hfgp|3ZM>MkU=n>7V8+x+!^nM=E&Ay#SHJ!V8L@RSMk7zje@>rb+ zuDyviW?!ZOigRBNk$0yCz<|p%GJ44OHWExF(GZI^C3ua4)iZ}=XUxjI8Ga3G*1a%; zCI-JL0~Gg)424phGFJ?d;M|arfqKt{>UeizxEg*#TAaKqF<_W#xeG%O4&OFcV;IQW zFPz(qJUy+MXxyHFjkwfU%;=cAFfwKk`b=SQZohyDLC&=ot|srh$l%OH z7tnKWxFB?{w#bHhZA8|c7THF!FSBrHS+`hZ^fLunoRxbkGA5wmURaSuu(wfUY3UUe zj%L<@?u-O=O72w{pp&V6GA6ZJ&eCaeY}1P;GPar1=rho{YQoj@?wD{j&E*mq4Rfo6 z1D)$62Guq9NMtCeP|XDr19S!i6efKEg%Kx^zDpu97G?-?I|N7ruZ3_mk=yNE4bXHC z&4)f6G?)KmZh)Xp*3}QLM#dgvL~00Y-rVxw8P+n$?;w{w&>Si7gT3WJHBcVy^$x0m z3IO*yC{kWHwv($GJQD9{Akfk^*VdhmPF5lIZe?(Ioa-2<>t?B} zdl(GSxqyLKLqEvP3ocm_9SnoswLqh|bVphS0{4bFLq%+7u3NA=nH%Q}cJ7IkpL{ zj3i{xrR;N?^=^+)jhD$x5;>G6-ajPnIogRPL|IeZi#3PDjNq}buj5_jHP9MfcB1vW zji}1zOwEt%j*ClR#0;ZWW7K2KIF2<%YLeB1`2r~ts=-}j16fFEuM_5SZl&Ci-okxGK2U;Omj}?1ar;GNy|~Bam`Aov_8@hX-u#V zH8ya_57P)~7?*BFchk7$vfVUpa^CKkjji#UnsS`$BnO*3_9O1P#bl!$QZXgBjnhWB{#R^z_e@V~)4mCSA(ndzZnpui~; zTFM-dMPQ~}f-8!Zh;&WHH0R--C&prpdS~cL$%Zvv9YsBc;+w=pv-p{wf|upNJyfl; zo9u(uLc8K+kZliTUNn)2$i<_WsH$IG8E(q-gKWj%^*m(i%`5M;#%Uj%rgY(=lEtSi z(Fh#e%%Yu>=a(#5I=7^vB6M2m*=H?UJa0*8LFv-6Mf3heE={LF@+*2tsQv1yOX};w zrZr-j9cb=&8=Hd`di9JqhZCyjHK$&#l1UA7mFZxbXEnjNz3a^FQrYfobyGwt6BE$} zahlkUGQ9S4U_vgA)X%SLihErHnTfJvNt@wyf=jbM=PCW^>{N!nls2O-%gV_)j=2|^ zKBJugQcGnxyu`y%-u8q!;4^ zh$c`?R7ablNenm!>t&f*!0(I9A^@Xd@XY9IJCHZ45q+745EV*eWI%tzZKpJfQPAUh zi8qsNX*4VGd{pJ~^-NUz6q^Y!ayPwb)y@n}?YP}$49BpPz>Im#GG-zla|4)oXo{>L zeYfIcYDhbMHcFUp7e`j%ZcyzmsZ;O-CzX!(mN0yoR_K_du$N-faNY7`B7%|b?XpdKyHrANy57l=lQB9bxtiyefQf&-YUuSt`;1Ia5I$B>-I7MwJ zA21T$O#@yZa3~v&-<1<5?8?B@T^Tr04Gcc)_Jw3;hBBcsO8^|@`nZP}2m4Gp1wJ#b z^qJY&@X}}0v5x&n)@wUAp+m!+n_};0cTIRP{;r&ny3tE&muG#L#uc&e~R+gUXlEFyzQ05e}{P|1#s74Hr<}V({k6$C$Y~&=GCF4wV15Z>8j9*aCIWu)Ert7 zZ9;b$W8jRzjH)Cak2G-P%{Cm z2`Gp{qP5B-fClhVAzu6INdj7fh>C!hQp+H=4bt{7O4VcAW6;`@nbsbxNn3g|(e^af zo>RnDPi=dgfTs;cD=3!?g7bZU*4{hWnFQMY`uqO%gV!s}-s|$LXFcn=t!G{Kocdd9 znGg|ZsdusngDthYHXO^`S8|^3vBvk@Kd!Xg!?Is6w|^?#>K8&B0L<2i z=7drl7VZ}WTn+;;vnHCR_}S7Mz2!5v=q zUv_I>i9sUD%WGHMaa&&r&4BvFs+TLW;mh|&&6n5SPTZ1?_vJewdf&TUqj&Y8Ix?ty zDPw8bzxY2`k<_2HU6tg7o|U~Eg?USFghOwAQ7tdu z(NBG?uX2}V-F*L=!}8kI^)>+M=A+4Kq^b`}f6ft_zVTjj>oRW{(1En(cB1e; zNm#Rru=W53ztaQnh%d-2A;m7{-+srkJADwCqp!K;M&_1YjoyUXRkz&M7lXF6uU!Q{ zqgR*p?X{aGhVm!WF6-sT6@VSE>eA2m>1}kdlKofbj=f+tXfypQ51fAe9W5Vo(MlsS zSmVd@3A}a(U9R^t)ZhiYAyLzVmXNY@_9fUNy{rAQNVWaoXJqw*+?&US5Uu3*a-=UN zvEs%ROV|z2+o8TX4i3FJV(Cma@W%^02!oY7-dw?}=c4$xG~r_Qw-g<0U^4<2eexKy90>759Y275-fEF+h1eOYMdjwhG;{rdK<9Ef4VOjt|OhciN2e z{{E(?&9H9S#}Q9>t@e&Jx2|5c;>KDJq!ZnCam~o|8uPFo-1GNx2YRnw&Ok@g%aIP~ z=at?*P(An8o8=iUer@fv;|$P025qnr{Hq8eMEzzL{}_VDhxUID!D%zj?)B#X!7lre zoHk=d?|kR4g7wzK|B(S642B;ekH!BHWTwqHXXZZy$Bz)edZ$`{6{$XjJ=5FK@Q)Dd zuYmKTX{KLA6kyeKGzMXU>37|B$MWfN^_~A#-LcepFcS0|ZoFgK(&JaK#ilc{xE=3p z>20g-K5NF=)6PE&UsHOj{+4sjT6(+P*>l#p=TGm2?g8(e95|jLQ$S}AsCRnx#P10T zoS?u73Y?(82@0H`zzGVRpuhH8e|_lMV= zU%k_8-@hRy+V`URt5&aBvZ^;J6Tg~HC#y?H@K5i4cp(=aeYgJ{c(n~T-f}yKzy{*? zu|K;7T|D*fhi3-hw^Uc@xs?XOi@f7xmAm-qoln>t^B8ug#SQRrKSR2~Xot&0lTprSmUeIB)KJ zPdBx>CELiFV)TxH`8fH?D&nu{ z<$x)H*}~^#nea@$(@C20_3u9Vpg)zppWmy^wrb)ie$#EnDK$Je?oGsiWT!8Y^?()o=Yf#eI+5l4WiO;@|tPjZa&c z%}*=+vTrN{pcq{k1vGW*;z`v#VI z#%n9!wiOuIRnOlJmy7G@ODe_y#8gH;28^}(#@~zpO1{y99>(598|^+Uh_h8Td?-k zt38<1iGG-F?SZ%aWiCf{(!rrGAGs|r-cy@Ty>ZkpK#mIIM_Q(YOUcDsM5CGSJ>24ZoYHayg>U$(rkBKOGdqk|H+-yNbj{SVV1 z;v5Shi?0FW&=VKMl36Bo9p#d@npE;5rp1*DTc!>;=FCyCQ^{BVn}Vh#2e~QBHZeX~ zl`_!DmihRo*f`2W$oJPz-U&=(ZPy(xPG1}q`zqy{CiPS>hMOBcqz}k$&V{!>IHkU6 z(dbyDbMs}%;RNdZ~46=EWG8w6C9PhjB?Mp@Rr|u`zz?}e9G@^2!u8X z9^d+_@yhSSjs;p@JLP0(Q97`eRA~To8gHCSB;J>=hIEgN0!IBrv~CvL<`#L zo@(NICTGPlLwl=EJ}H*m5QqiFM;E<1%WTmYb@I-*?wrK)y`x4^4+6}(8E%7yBKu{gX{HYSqF?F_|o%1k`BWJD|{ zo)ynqpB1yPe^nl9q0E*^Lt<-atF@{;)|eYPzr&PPG-gHa31pj>1GF2?GOajK`PG55 zD>jnO*u5MYW5$*S44({*?hNvG3OFSBWY!IoE8{uJGxC=|S?R;U7y0@RU=SafJ4wFP@rcO>0vDjr$X^uwk*mU{hMmX&JCF)iW!pIa22z4eYydg&{&Qpu2M zG2{PrQPVS9@3_WGUzwE(lYR>C$x72|`*m|9wLfT5=RxN%f4j(gKWGwlm8PYgewVaO zv*WM}TL-gm`K@I8&MagReS@t=CR6LNak7P{(T}D(t5T66Zr#ZAnEF1HdUjl{LkA%D zJZ}!IzhXPTGn2sS99osyb+9t^HQKJr>Z!b?-@fFj}blP(g)%tZ`^u<`H7Lx+tU-u!@utLj*HX#?$t|9b#%F_uX_EH zF46If!SgzmH!+fG!bbS(MT4g1S=kg8?R>AZ)C8Y_VMfTpX2vgc&E$tSAN91>a~&fRhOQP zzz&_#YY%pm(9YM%lid&=$-Kv}Z{F~Uo{f|%!)EAjb|BQfwOqFB8f?+k*rcnlO*Qc& zw=Irit75WiS(Bl``{{~i!Ee7P_7K0TJ{?IdW6bmZ)5P=eDN0A0n0V+9j9v7-bj2Zl zBmBY}n?vnGV&<&MRDkvx1#2p@`!Unf3Y_NGFG)4Mj_wgY@L>+H96k&s?xT-Q?Ne;d(UQ`6v+QK!wk#08X{%HIL%nrdWk zX&|;M7>MoOZDM>!Zp(j*?(aryAAd|AtnJ5tGT^2D*nVDBML+XwKYvDG4A!1> zasYV3?*-rsMjQNZ71bXmk?w(~Q^q88YMbXPEM#aWmr=z~^n6 z7D;tzY{r|J$jml_?!d-utBRDzN`^)k@lC)jopb2(WcvJqL9dKU{03Pvr#kwghBD71 zH-4FIKaj3@2YK-KuZgtpAXEOd_1JrjG*g7(<<4_EEB-55S>t3 zw$CanTKRFQgdcyYe0;Rz%3iwDpDq4JAOD$cLQ!}0;+Z}|Xz;5&d@wnER6 z$n03DLB6i#58u2vkkj3Mn)M&|ns+Wq)n)jAN&HR!{H;mtkajFQ9(D0JRzEf?U;Q`+ z9U7Gn9q!_{w;$l|@$)>dA5CsQF74|_pxEt43SBt4m&R7+q#ykKxK-`AV>j4%e9Fb+ zSpE1XINA7L`UQ?FC_p<-LIVHMi&WGtjYd4XDPUyaS z)0}wI0`xC3qB0kF`8?~rf_LoXHu**SgGGs1y!&OslhaecW7iYD{^930!DA+JR%|_R z)W3Ho_4OwS-&wd_$FeFXg?A_YT?T zD=Sm7aaM+crq#}qk&R~A4q_p7*pBlV!|2F?wj(esy6B1^G{a_C+kp*{u9uF9j_9dK zB3GSbD^vFYtLm0WYO?rhR8M&g^*)(lPa?0SYkmmM7N_f~Qoj*S^eZ|9oTO)tF{YQg zV=8&}<2(YK@#_cJyREc6NjQURpS{aV6wzMYFc;?uRlPVLoPiz0Z{AP6nHe}I!TC9G zZlIsijSaniOkU!2@KRs$Z$wWx`LRawyEX)3>2*WnffCtS`9*touJp->`r($}r}Cc8 z8moRnhsF5AmaboR^wocXUo^K*hE|^%T~r8f>8aH!x0ZJJS_|@yb_$y_kI2Yx&0JA-8TWb3p1|MPB5={&m~GDjkuR_#$e7Qdc!>w|8&#|9}^ zjc!pn^|y(#UvSHQc961*;K`=HVBhc$uHf11O}D8o^u6-_kG-zV@`8!J5E{cVFA}Kph3Qdm97zY0khp~BPb}SsK>dl)%UR}V=OH!}2 z(#e}SIX0QRDDf8uhjz;^PTS6N`h@^7>_GhROy?x0`PHGERq_dK8I}{G91)ePAX8QxpaQwPV&OgOFBld z0$-|3MFl$)N!>=C{EqgMZ5(EA{i#*Gc@5;*d9mV7q&E_KlP)M7g&hK}_(d?oysyg| z*G=rDrU0E%$lrMWilF@uus5wss+4>?u6N2sIp z>q)n?LsvaWS~GEFjkWq7K{rIFm@T3wvR?CF^hdBG#E=is-xPn1)M?tAu6V)1D@Zso zy28ZE(kbr_tN01&KX^A?@jSoPCpoayuewyabs4a~0Zids3jSU6OMUgm$fHY%&wjOn zZ;JY1#{N<0=!{Dd<1#++Ibe})$K`IyIpZ=uaTd?Sif0M7$}e-r1$$=8t{`7+E$6q{ z_3@f}eX?lGv(C-3{@b-8(634UgD_Zy!&t1yzi_{mtwEZdFx|;W&^R6+DyoGJyy`6Wp zON_I17w_p840fPzEqs44kPvTr{s-#@$aS7=+uAeUy){AV+WvU?31ElQz@2QYpDo{I zk$7)d1l#SbHF|Zd{<+8Moxnt%ZmhWwd@kD@Qlln z=zAa`863iRNE2J=Gxy4=n-=6mw$}#omj4ocRrH5> z@q(Ger#>?@UVNrWeE=NsawmFEKIIe4EhDV2?6>I?6Agc!7hh8mN!|TMcKpr?`UiY} zY<0aG^W6(wlfj9J@#On?)yW^9=yLPNk>Bj{w`9i2AD3Vq)z&}vB&Q$Ec}_oAe_Qzt z@E(5UlugU&$AMr@VjX>1h|l;II(h^=rFqP^hng|(1alK_1xF?x4~$&C0NuDSFk<2I zdlwG>O7^DV!(X0d>%T)kRsUD4UoYp6y(9Sxv&_tqz|s7tJ2)!wTwv7lw~(=$10xqM zxOc(uMD_#2N4z{`(-&#~FSPNvTc5Sjn#)-0`%7?m;yw6O>w+UFqx=O=Jusm*khA=O z{j7gdH;44uqz8DH{rwni^)U7!SFS^jeXB_l7y3H1_&#>TuLopnq|+ZJ?;`4=lNSvK zrux$Q&96*&E-Pnwg0aY@pWb*!8Gq9DF1TJe2rsfd35bO_7em`N2M$&MG##Lr!7Vx4moV==M4%qu#G$yA3-YbUy_o;gKwcqEl3 zy@~im7io)#^r zoVba%QODoMO2(@jTH<8)i?xRF16ScQLhMEIeH&a|AlOmX@Q<#Y3oYg_e)#0*JL$s^ zJR)3QJe+R*Z)m3R{|&I;Kgv2UFb1ph9Pl3JZykS0>JEnA3&0SK2Fv@g;6tc6MNY{Bv^2o-x4x&qzFwW8%RaY``G2e3W(u&=P)s zma={tIx^5V2NiA`)ffDL!V%O56LrhI?n?D{C zvphw7NONBNqSQCkKJ99r;P%Pt-A>v^CUx;)N>}&I56QGg5&B648=PR4@#2={pxEPve(!^|xtkMDdN@GA5m# z)ikMpTHS}FVQ2m2+K!TTPX9D?)~u#e`=>>ZkaltZwB!e*VN?C^B1*fue_GRF(k|+s zW=PXmW%eVJ-s*djey#gxhKw=RS{S7=AS+=|1ITD zar37P%-^Ma=xO0k8<_vP@}aHGKWAY6PUV-o`Q-!if2n-NVcn^N+tufl&zM;F7Z0re zL*+9*Hh;mu{O>A%k(N0zQm3ALC3;-SuXo6(5Vm5-v#K`2+v)^ z@wug^a|*1UK5m(xjEn7=Y~q$i*A~WhgXgi@N(ZxuEoU(|3Nl9;g0G#8&rMu;?J(l( z0rm+D!Jh~cuggjtW^81W9%9TIvVxn;zI$IOgqCgi5wdG{5+{;>{>CJ;F9;uO%o_jF z?4xF19G>cU@7@PD-bd^Mo6wD&h-9yOx@zR}-wtQbetOc#`@W5TWPPLJKtXqy@-JqE zUdjc2V^;2_DZ$)L3qKd>8U8ubqdjZ#iDWD8qixSGd1O?b{zUV{qQlt8F8ux6k3`~StV3N8nE1ea*?HzMd+_!Bv6{v& zn4bCkWx~D#*hzHzVfgs~{>xhHLpZ#xSnqCPV3FYLS4Fc@$G@>LJH`e4$0xLIvySr; zZ_!`b`F6nr*3q?>Fa{Sh7V{XBxs1&m=B-+n$g+OHA@;Yfq8|3ACMo~I@ztzfQvz)^ z16SuPmIF`m+HUI~+`(F1Pype4jr5#;LJMjN7lvdgDDdrXriX8hf0~ z;H^qv{uZ8g#=6f2-q73EzO=s|ztVyD?Fd`^{w)(fe1g>r2FLH|LGY^@1V4`-a*)^c z*ey#ND{G-{eAQmsHD>V09%vc*II{Qgzl~3%5B;+7kzQOJS;_R9WxF+>lzvBGcZ34$ zX~iA|wpXUkgbrcmZ?cbme$aeNG2|@zGMqAo_>(i2apLl_|7D~%OLq}JY|g?TW8Jh% zYt@W@gOl#WR`5T(In(vvEj5PDd!8MQx{Tq6!V)b%C>!&wm8$D zZN2NGA92q%T^3P{phfX-#Vndq>2l#C*x+h$ilQeo+ff_mxU}tuKRS8`I#uw#OWWZ? z>GHRE*P3qTI%-eKaEsrM&hqhG*A53BsH;=P~I*s(5b`+YCIpb2%`eyzbb9X$1Ve&;sF|uW|xn|U(yx7B<~H*EOt}g;qW+9FeAPz$_)y@k?>6Gcizja-U+vrU)SVA1y#T)>`6O}K4fGekgPWL*EOm2A`a4Wjy5*#VA8?GQVM@ z)!Cc9`8oDm@wLq*KP!{Y&R<48{^jOg|B^isHotC4>>6O>H;i?8Y_p%owi1hRe3ZU5 zLvNkXk553p3HYo4AH^xe54VaRj;32a!@Hj@+E1SI5$JbrWP3jtN76Nlr7rrPgFb-yVD#00PesT3G@^`UL3{7 zNT8uV4Igz@6Lj;Zwc#^M&u8k;{(aWohhvZDYZ~w4jOVYFf1L6Bh4POvp7gcG!LN^o zKcakQTKa_`bEh2q^IZJ&;rQz#@Y_c+cN)dMoM0^F+D$t~=uAhaPv|jkn%kI@K7B6z zYnHyR9~H|f84}Ayx9<5?FrL%S?-?f5wJtlp3!N<+oaAo_uK@E2T?mK z&@Y-x*tXbH;cqK|jh;nYarBYe(i+hL+A6257u~cMNjpee^U*zr@X;TVoC3E5xE1I# z?F|w9J#P6ul=tI)X!6i_5A_Oi49CpbD-?F>*G)7vUBPn`p`-V{vJ6=$n^zROS$eIc zC>9EQ_CeNs_RW80<70kZXY<#LkG=KlKzz@WCf*oqMN(hm_iJ}WQg399?>6w|8(AlJ zw;?ZYWYM;@T?L7`z;7GfQ$bu{R)f<{tbulVSi6m4L;Ub%JAO?0N9j`|utO`Y9B*fD z&gMhUUle-|xS`QzX4`1`<)b(JmM6wqHJh*rH=+d>@#L~YHy8FD@ABInK zsKD9x<(Atg`mF~C^yg+XI+FSqaOq)2t8T2bTn69}WnA)48c*%O6Gs^@xv!!@~_|T94p>yFW?4IW9p&_Ho)t{`9d7GX^ zFRupQElVcHI!Tj#7mvIt8+5AGVKX$RX{1bu_?G4q`>$~QkD`KD1#^;$H`D$0JZ#-9 zf1)sP9r#TE7xmR^yZT6B#f`vvs?KR|7waeTQFhbjQ0&bALz{=rjil<1rYl~nfY*@g zN6#}Y2a(~cIb(PLd7bs=bn7|5ZHI23VeCr3DqB82p%|X#0rFuZd(y2ZaUKQTwOQve zCA%vrn`C?}E|t*d-E`{+$^`PGi_kOqNyfYLLujP9#(effPIJ~m7Px@u(7R}*&0(%BK@TbUYX#h?g zyzA{@KZX(`NKS(OHpH(JAn~E3Z~B(1vu#t%7k%PlcPO^S{g|O-*E|nKd5TiH?%P zx_zr}e9E`2Fq&p6eAr(Y*OdC<|v4UVkZIbY{Vrbc|BHBiaP_i0o75k`k> z1;3K0i#syv;ok0?mD^S{E0XGghJ)cQ8k{3o#r^Sj#)$nt$`cR$#m7Sh4i8CZhItlE zMTavfYYO_q0k;|t=LcMzS(luZ3A4)wGwFk=xq2@3nt-c*-w)lYuJQ4~O&-rUd?1;d z4N4<;zH^t z(H(7r_yOh0JBE)iU3tgw5lWSJ3?HF{yygwpYi{3f-c>SAGCLms{c?QF%kVWX#phgz z@40~3@O<`a25oFudT%r^D%d0YE34BB6C-%{_icSDUGt*yiizVx>rDL+ej{CTm@_Wz z$j14uu5BL|UDN>oOYhbfa2AB;@Y&8;v9BLWKcf9|&9dv9%es|!@uu0GZdLiMl)uic z+rsn3JTGLdE&sC5Q)N0ym!8w}ex5Jnxw4QuMf6(~T{NFx*}3Gq>6#YawU@)v2>TJn zek>Ec&M)a(_t-Wq_7wA~snk2$ZL1FbE%*W8G6At|WJ6(0uFD2OJ%BXIHwlnlY+}CYj|{x@A5}{$0R0gw5)5}Ps|a|OfzYlNS_-cHS}17;TISYs*^AU>seoV|l4J?A0O;dpy6)Mh7e z;fuYVuk!w4B3Znb{Hh?PdocRtp|5P-qlK|-+NQ0sx(q6%y@-ouqo3qQKv|nv@lz*My9h2FU5mX#AfKKW=n`7z0ym2_ysG178d%`toj{ZBb zlY&G;U`GXVpAyYJMH%rBW3jW2XQys{Vk`P%GU?5(oHWB<+ION@_+I?NF6^Ux$-(@w zCxG)1_^G{R;HW)Sx);amdkA?*5~s7Yikys(8%pRby6{kJXD!bSPP^7;X=8jd=^(yw zVpRRNmx0Di3*82T$(#qQUOB+M>c-G^$E@+_V4(w{qdSsuy+_R~-9{JQgUbfGQRl`H#Tj4GFnXR_4)gkCP zrH)wiJq%)rj=YS>O@g(~97jQg@@#u2<*nKoz^B4N~Yxom=l}BcQR}Mz zMPEv3>kaag(BR9&>YodY=nfFe^!hGbGgo`Wpq5N) z(t-9g_FdCWO7|Vuw8~=iW7~zvyy%5XnHR5uHj1YVXMUZ9ywnmW3a}nfieBv(Cs2LK zHT~UHNbKj-(dC@Q*m?eltWBSWp4EHrePs{+@deyb^)~Vs3XIs4Paijp%-S@Un8p1+ z7#6!`?cF^O+_R)-3hnHHSG$;3E&#t;%}1GIPhqV-M4g-IyX<5qaGW_{Vd4n;<9G3Q z5L@t16CIttbrg64GZRnwGP?@7I{^Oc4xXG4K1IV!tT>q0-N_nE4&z!zJ@K4(u0r!0 zuZ;HP9>B+`Wxe`L@EXUy3`;wA&RLDW63QtjWFB4AwTW2JaFZf!Z|KIN*nIk0wtblO zC#<{?6Ix7rqH85Io&{Z-eKLLkx+43VOQOb^!<^kW52&Yq&E(~ci=>`qt)s!M*MM$l zK+f@9_LhCpu?Wq=HlqhbJGULH3!2CeQ>P1hgjf@c&==`L!Mop<&ri%`yhz_`LdA+tIlcyWThzXv ze#8hT>Q=Ls=ht2B$er|=`1uYCCofS3Z0QYyd^CujF1-)dy1I)w{zc3qD$mcxm7l1zxUVRF= zRM}m~t~0K|#BM7)P7Gw?1kR@OF1;%rss3}iyotKQX-78PJ69|I&Sl)@AS+uZo0s#7 znfq<1?U8MeJ(kXD@QrD;o{6POe(bX+JAqKFE6rVXLDwHbe%d+vEdfS(tawM> z=yIKT`zShdt*0~R1Y#dUXZAp&P@u@_%z~4$HeJQov@Qt6?pk|KPwra3&OEtSXUg9A zb*9yqjHBXRiVwVrEax$P$ka;C1=LgnD-*xurmFb)=*HRT#;w>I>Bjk#%hZitnf2(; z_oq6#aRj>YeRM*`xE^0O&JXp|jZpdUt$hv0*F9Fwt3!^Q!*7z0 z-4kUW9T}fA-reU7FO=>}m;VRyExjB@hNtnT_Rr<~<{PAsq_6(ADlyU!iF@;9A5d5gM(ojrMzwMfky2Rq}q3SLpHbv9?u>$ta6 zdxvD_O3@po>^ppzm{5mLpEhO0Q^iBSqTTNiQ|122R55!Wg1~v0IC#H$8F6vd`>E=U zb?c2Z`*y>t4+Gy{uLJwd+&(VZT*1G9~DVxJ15=SQIfd~z$f%A6Z}XA^pesh^F^OK)$ct~dWFMo&f9 z$D??0=2&#TpRV~jycee34Zb>Ksnf(dr>&DQ4mufYq5dk{I%-s32Ds-92&bM&Q&=W?FAv1YR)#CYT)iI{LlRc|STPmF28&Npqo1hjcic6(pC zMg4k+vbtM=vzdh+y~Jn9w;eh)@vC^6p-su zSUb{Qul=<9G_Vaa>8F8w1v6rhG~&Gj%H6CNOo()_U%aP)RuE&5uGk_&wP07koBBy}fa zqWGca`7^U4I*+;U1LpPjkG=YECw54hX7nn#p9!`&Z(7PsS$V#NJr=ptmD&%(NVQ?hA$zG)iiBjmIJ$3SPs)OEI#e@WNiY<-wVFGt5sUG<; z<*TmhY5nBmz_sf+^rbG}w7BK^!5Ix4(WI9am*J1G79(2dt_$h6y0JYKrA{0D_M``> zdkC6$IsLNh&-K_s`J#tu`vYUVK3m)G=RfVP% zn)v64JG?of>#OZ3VUE}eFX;};!F;vXfa&^by>+WKuFOT|GiJW|{0?_MzpYGs%()1w z&zO&VUGR^ww(}hDBfvAGPm>?FS@D0ZhkWhb^dnkd|5xz0yyMrGw7rLU&;r_@Pu()+ zLVMghiM%$opG{*NKn4tbTRsZCo^NJmwiRH#Wj$?at>zSx)oyfq+i zq4MyR2g{pFUWz({$-{-@9VV~Z#YOVy@b!d5IeD4mrdVm7%Omeoufdh0hF*EdOOy&1 z#(pqdN|bjDo}56Q)&}hu2KwR_fsqoK6bO3*u^EBop)jv%pbNr z^{6|4Kv&!OgD*xfmc1vk1K}O%@=5Ti##`fBz_>o(doSnx^Spa{QnYD8$91^+Z7+N= z96rg^Z*P$<-p|x;o^BgYev~_lq~8Xk_v_?~=aa+&8^!nNwMOQHo=%Gn9bBjVih9So zOZVr>I|e`fRC&kXneUTl;C1I-r+jH5}y&BzDrq8Hb?R-nToJBngfll zhL$O0PjsF^S@orqGHvWTKg_enKxL+o_t*R(E9>w@h`)ZmPlLAl&@<@bYR&#e!MUztgxGU(`!!*u-{XY77GFaD_P0Q}tO z&eKZSD=*((`a8_C9Z&oe?Rod~w)VlVL8ohf-;mK)9bS*#^Y+B+?i- zK8pPyjG1IHWSo8QA^59&l$q7rHgZ&rFQK-*a}SYx?uc`Fxf$Kn=<2b?UOkqdI7mD1 zXT)Q;YXn`Cmw1;v%@3jX3^xwkYp4A-{>LXHW5=-Z{{xJ_+s12*d^Y|Yf`1Hs`X9iz z`n%0%Wtp;buHu7dp3T)p8NX-+cJEEZ6)|4y6;S5 zFa*59=#EZs!LH?N{PgUN;d4Y}h&Qid{UmV1gUi8l`3l9thj-5%A33}pUUuf7p~NBPS{H$rv&K-E7{{7Z zCSTn_zSb?$+)o%_?Lsz5dm5h~f!%WXN4!*rzu)1$ckrI%y^ekS3+a=`gMVh+%-iYm z681$fM%xTy#6FF^^2080$EVT9lkMH^BxODONe z+s7s5fV+HLKVJ33NS*!`BrdS|;}TbK-brc9L3UQMZ&!17^(~AU2_bHAItP6PPo5^W)^EA@QzxgT%`> zxbgBA2E@xZxBkho3CD|HYkqvZnEHVE@pR_Lw6%Mw{GPtJ`i1a=Y{L4f1LNwu&X^Z} z3p_PHPM*OX*KT~>n;*M&VRPtz(K(dU{P>rAA7fCvFy(Ks3ug?CcP+j+o^u{+>t`5y z&rHeuN_(GN89sjeqmpIfxvqcT#CxM71NNNc{&mCRjpzjP4125Tt9Qm~GCcJG{N$Fk z^R~z&Q}fkH*ckjMtE;Y*PRNm+K{sJrxZAG%d{c8We6GDs4UYfI+S0_t;i)FI2l!>k zIJ(x_mWH6`7f(#wPJ7-yD|jKD73$_&MKukg$H>A> z6XAIeXW@2eD0p~%rGM({di;up;XD%)USDEr?mmgSZa+iRY1m-wUinfttyF2w^FF~O zMp#om)U?iYWjD%MZ_&98`TP=luZ2@$25pQg-1G-z)lcU(=3crh>_PgMB!(1bKe8XT zGsl>iI0d?-@X1TTed0>y!qB3Qb`)2sV=uG!9_HD`1t_b0`SRwho{BxH8;EeXUrs{$ zsWiYip`XB0PhA|}$US&v%$w))D;W^)v=NU|Jl4|olg_vr*2pwRui{T@=be)ycCT84bRm4$ z_2*%6@!HA59l6M)fq^gl{ddM1TBHqPcQS7+ltzE>- z6}N5Xw>H52kED70taY^p__>I^7&+kP?WMhxGC#V$GT!0p4UM^fjQlux{r(KNN}n8H z?3N(IYWq>z)B3S&)@t&9|HpKzcZO#@dn&EXUj30?n|*nA`Vqw_U!P!Fe@~x$e&M!^ z*v~e_e#(hq%WoSj_H!-mxqe}9-D-{Pz}U|=H}zoOUE(xm86xNc6zLUveKVhRi9`5YX7U!+IcSG zxg*f?eapYVaAWV*Ut~>n?*jIxXn)oGz{baPZ?%QnaeC}8tYNIDzF&TH=Bpfd+lh+` zet4A1Cb3SEC-7=-U zWyaew@497Pd$%`cYGa9&$hhpU+WM1QW(|Dg$5lA``{lGf(boB$TSxs-AJr%AbGG!G z*PnjASlpog_Dz<5Ly1ygYEIt7w>LZG(c>a`p1e-eVIB3GxWOyuORUS@{Sd+jglwXYol(+ z*tUA-j`t|<7;1I!6zZE1o-OOOW1n$$_Dt(O(Pz0kJc%97gsJsCXRndg%i~&zSC!M^!kil^7?!ia5PR4 z@Dl$>cUM7+;eyY-8-9IbY`^->Jy)aS6_2vI41aGpxLoYe1s>}w_Zn#`W58Eu1ut~V z_+hKR9&g-4+v=+yM*sdf>-wReGe-FQD*FRq*0HxDiC!A)j6aL%TSSx4jL&V?{^==W z82?ku%E<4oJ&Z1^DcLIjxv$NzDSkDFJBc5l&I0Z=ng0xDUPp#F^O^;(_w`M3pn5sT zK^tp+Df*|iZhYkyZ=XGJ59pd@V|UtPA0ZxY*T12&e#QT)10F5NthfGc^=cis!Ds#M zLMo;&g5#whzUfEA^aI59Gw%n^vhsVibPM}jyM{eJ;SJ7%4{46ryUC>sW}q+by3fR} z-tXkhAsxQXfj`xkW0g(6(Et8;ox#_>Y4u|!d--(N${xl# zHxOYzGkd|ATWT+}^p^IO(|_V|QM-TJyT1UKc3-5Or=XLP!|~?(JXCSj;x2yPddn=`nCn z|Ge+SdptJ;Tf`pw?Fs6X1#)v2^hDmPr=8i{+o%4qN2OKI-K=>pAof>@?g-Vhmyonl z)A*Qy7K(SNzv^ogxmga~xHG+_1RGoOc0ufF%{P5z#PcD12dk4@zpRY1WxAWrT@$r+ zxu5O=`j=U^5!D^0{8Fjqrv~!-M^17)oMKHr~0^ueyhJ;UrVlC#(vlSeVyv- zYv~|34cb@k>dWk_IZj{goIQhvZ?eZvx+QROBsHfe@?Z{h|426X)l@NUGy?gp9i5v!d`{CUER0VvUQ_`J~Il%Y$MKtOJ z{u257VH2wYeweY9uOVKr_|b+xt*@O>qK9=CXMCM@(i^m|Cxrawpwn{EX~S8!WKYjp z?#^JXvVpM=fxrIjI@Y=&&K(Ww!_Jz&h3$L*mKBbK?(VKim2K zlGT6Ml4|!Xanl6m576{3#`Dmv^2G|f+d0Q7S<(DOzJPrBka5(B)nXARrZItcr%#LZS-#;Te9=eWu1-LgvXIwXGPi7!z)3QmC z?QfkFT{M+5cW)O*7rk@zxv_sqKfY*Pj#>HZz?UYh(_I*Wf1WU#vvu>wY?|-@<+~}b zGkWh*{&C9vC*>Ze+}r%>jNZ*-zBnPtIA!IS*qlJll=aBvos3b0JvXvV4XzDsz=j&^ zOr!hW$a{o$tv^T6PouFd@-xEfi?2)oyCXVGAYEnUL%1~TrCpx#h?{Yaxqlw>juGUY z4QyY`WphT%WwT-~ze7$1KZ&2{hc7+(0lLDCx%AepmYfcZxolR<#p-(TnGAH1D7pJVGULw{U~9$AP!S%6-d&%TFh z?w@w{J@{?VeA>uZi`$&B7H4tQTHGY*N6sfthIST)JD1ZQ_tLW*A4=i&oa*1vn`&`+^m&3?hE`Wwrq$uGL>U(zHn39UZ1#o zx@9bV$Sf;<@RwCTlBsk{9%UPm!2om;oHoW=wBgiRi{AbHY)Z0s5M2D_TPQDB(L?>p zzbv|hI@2v7%K!bgV`w~m0s36L+KK(hjy#+eZ(GAUO+3)x^G1E!JYxEpp zPtEJ}r*;JA=(uC3)MVHYY)@&XU8x`q``ORd(DniDa@81aA!e%mx5(i$8s{APYxr*2 z{tHAe));w@PId0O5naqzWMc~x-y=OdmHQE0KIwuUTdA)#0NDlcoA!o9PUd?#(t#zx z0c*m#YtY=QJ9h%?XLI7>L4LmmY^{NIxUf5Vd9Wbyq4FjZvl8v@(VopvcN_XDihj0f zH)_toy@RKVXLVL7KQRiQweIx(`lAV2b^-q&WrTYN_XVo_pMd`ro>z*_>>-tI6usAT z*T!IU9F*RJj7x*hjyFyo#Ex$md_Rh{<2V0bvg6P_(~h5tT*3=0+a*sK=O68U6!08i z$Dz{~-;w{%+W#{2D`m9qxWKdHq968RDY63W8KP;mZ2mkaKCQhMCA>@De-d579%}9< z48*>#c@uFx*0fsBWjxj~HvTbdd@tQ{0DaUvh^|^q*<TR~n6d+8h9dX0>KiQ<9q z%BRRXowoe=l?qpE$}()Iw};cFIW}cSnfi=Pv3BOZA<@Q-x|$0g*2Y0EUv zkiFSf#W|f4+C1%lo&BAPV`3}zmdS7A&z>u0Pn5II?)dg544m2E#rX{GGq87h;?J_C z4IKYIpj`IG&&MAvdWYCB^f&DJNx9_%mC^~$8t86hWe6}!fGL?T4O;)H-@e^E`cs7N z>T>BUIg^f&oQ)u_5g85y^qt3|#J|7Eyb?HVoS#wo2D@*sFp)*MkPAB$bk+jZ-*L2C zN11%a%Aa3i{gR@@evJX;hCAh2grnLs2c*A>65B{`;w~a*FNyk8n5dw?)%^MKZFlia zQa(sqN&38;GK#-m^>(^d@p?NZ@NyVhmT7$86~*~)rmTJU?YZ@Msy<+q7m7xAtbM4GY#c!cv5Uxj|c$;JI;(YloTqh2DWk1gKuR0Fmc zx$0dn@a;d@&YGk4pR}?61o{;=(dId{_Y0o4s(trY@#uEm)9jPq#r#y`Hk5MFgTu7NsGcJA|4+`)~fJ?Mw4G;gk{`GI1>FA^&X8E1|WGP>WxnRkX} zuwRt6L&#P&zw&d?k9J;R`3k?q#_H+2S0@b3pNmi+ zWsZybs&OeD>5NTTz*N}wTs~&pI97*FuAUX^f#(z#v-#s4K36_EYHwBZIaWvIs$IVE z=cir0m7ToA80e<6KB8eJowU9oe^R`w^8xxUw&*ul8nPPG8dpKKzS*L0LAo(6A6-?~ z@9XGY`&i|la&BU$@K9Q#Gsbq0ir$;u_h!ai@4|Jr)xPsBXqPwr?bzo-=%x4B+bkHO`N3;muy~1&Z0cZK+^8YF+r@bH>$ZMp$_7W;j z``D-2yga+tO>L<r8WA>_SF8E@$G+aj^;k?ih(nSDC1Z9x(Q#n0ol))U!}WGo{6BFX6wEaeygRE zPI3G?^v8~p+lR-FvL1aq`oql6vS;4&etSu36}o>qx^^+?wp>q6?4Nm;{TIE?qb<=e zLRvFCRf#TPzxgBYv(H9jG==;mdwEpW)*q1@TSxx2v|m(6`;^nX#^#mg#%jrve^Jh_ zcvSF;cz!L^Q(?>08}U9pPZdbj7su5{~_`s(HKT*x;IZM`kIv3lO4?)g%iwkxWNwUg`9~t`-4u;2-I&j4?t&hzv2ZROZe?N@WA!;`IPNpW zi)Dk6yG7l=YIOVY8qeGzG|SPibK>t}^Q(cKL?1_~*MV)G%X=N~pW%Hc@1iRy~D-1ji;K1{naX?H5^ z4rzPuX1Cos?su?tCQ;|V6rXc({-%e2Al~>qX?fuPpbO`tz?s(cUO3eD-nA|q?mczj zQ~~FQ)E(-;x$n!=K?c>f8A+Y2w)Ymgb)r3Poh8%(*Ns_jod>A%9T(pVn%-*)w7qwc zTPJzYt#coB?x;*{q^$aA+x)Jc1998t&6Vusi|&jV>gTn+H_fZR->v@y^>3yAB43-I zu5|Dm0*z?1#H|x~->vgJbtu2_Vz9@hh3eg2~hPqO$6o>xKttN9ZR9~KSS z^CvheCp@+iLwJtoJlZ>e?N;oObnD{^p5fjH?RS)KS?#8aW>L}$NPo$NqxPQW`7+wj zIBR^r$K8JO?R!DuKKSMjJnNf6uk-sZzt~vT9Qh{q91{z}XHn9dd6rE@zqWL-u9?HS z)E?fYyH)`|hq-TQK)!ipybv8a7#|*FJ>9eE`M_)fZ|OSL6ZXErcM_Vw^>pZ!@6t>( zpTzTN;BYnN)$a)Huco~Qcl>VT*_p%7iAU*|eBmSX0lwMc+0rr8twTSL;8%KBWz#4qML>@B=1I@DozKYgFszK**sR$m^7Bd6Ye zyjUsz)IA?HDQtAhW%T)O?y*?+VKBb#bmpqO-}PC(;eC!tt>F18%1q>UO*v)w?wszY zue-1DV|Dj!{MfpyIr}p_5s~DVh}LWUUPAmv6vr zzW4mh#F+TgrUxd(^j#K9|4RA>f0Pi9(YM`x$r?&FczI)Y z@Q7m@vnkKI`Obz3&RlUiT|SZSdW){Nlx#C z*6pY3tYKc_Ootz4$>zO#gnLm*KY-6lU7JtbMsviLTyPB;$BzFyX$C%gAHLyt>^^=o zzpbM&yqR`5_p)O-bHi%Zc-D`Qn%{TYF3Ef0v%`Lq7s^&dW)|I8Fl&3Z9%>3??_yz!VR%bIuhJGe`c5Es% zj|_2qGuf^v-}Wq^Ey+#(W!&)%e~m-$dgY5L--a*u3VfycO%vs%9|d=&iKN!}U`zkh z!5^`til}&>C87g_e)G>6{H5M-Si#c$ITe$aA-(QXxmo4`;euTdqzPF?Apoc4O zJ4%k69s3Js(dDlWwx97u@KT?L6h%_^qyL_S5BC!*-nv9{NsUp^9i#sDN@^S%?u%@X zBMVl4pf|L4?g{G4zLb#uC1_UX&IOdFv6DP9mTt^Y@bx{t{KVzE9XyJ`r^D6h0iK0} zY}HMSK|M0z;G{d?<*W9`>B8NtJJEhAzT*=v{+;Ms-A|uc_6L+5;%n%LCikAZjHB+lkh zM|-aPHl6+@%v`4q-@dZAPl3)k$kaw+V;-JrYZ|nQ8rG_?`TB0Sa7X{`lpY)av)X~V z+=V&r7%<2CVd5wHVGiiq#~qkgyD*=mZ`t&%iEnDxEn>ekcx39~eC+jV=&w2z$iMhJ zWVoZ&6jjtP23zioq^=;37@VU^w<%w-*C9;_~e!OcfN!g}PP^Q`C^Lae8sk23Q*KWj@$A98HjM=x5 z{Cafk+2kv4BES35nL0aC%07!m@>gTWoq1bTd>J(PEo)D^Dbs548M0eM3idYnd=)vGD(#@WSuW7}f9{@KRzb zZ_sALD3f}eZ+kVNTT0=**OfMwv|O{&C~bH|_p?0GP(a!!=N{=&_Vw;i8h*s%W7!Lb zJ=3>vUQ!z02xbl5`ZW4x&VMKk`FMPSS&0nWZ|iWs=pzltnPT(WQ>$3d?a(#MI7()= z=4LH^VpMSPQ&qzjFT)0v)n~=d7$1qpmyB8bC(gp`IoE7ISeq4Z8fwP=xi}F2GwWK? zS2q3x9F70a)rLF%-{aXC!)oYt33R&{`psj%!(8roaJ~(yanznc{mSP^zc7ToRwF4h zin1qBXEb%kKJcWX$v0(-v0{P7aIhgPw$B;z*a zY4*gecE4|jObjP&0X~hsRku$2I2p4A_%q%BHRphP>*-H{@M! z=j=+?oZUHtI5Dw^cB6GY6;0fqQyiSo-L}NkSQw0hBa``w4V*o1JIS=n_rup=@zoc{X+3ze_8)Eiqci_Mf?OY}FsX6i>Gf+E&n@)z%~ao5?cn*x zq=~N^-Tp7(`A4K($!~+3b{fz7iSDA~pcx7L;TW2nHUi7tv zVShy5kZ7Xp+dPNdc?XATw|s)KrEWdZwuSr#m;O)j+#IxP+uQtW+wD=s^>l~KBJ{(R z=!uV`FRnmuT+Uvq%UJ&z64U%u@*w{VySAt<8=Bs!dxz}YbVoklbSmasuhGkp4Rr8E z_)PZtwv_+^ovRbhhcf zF6}=dj>B0Vd{E*z_m92)aG(mfq#pq0Xx0*H&NKEK4=-^3*4pvY9GMe+;HhmPSLW(g zJNGQV3_UtIL$&`CIq?sWmEZGj?G1EY!L#^OJbE3^OZfBXxR_^nZQB;?=T&?|XeIC7 zIDUfXBk+rCQx0-i##oeLe~91j8_s!PovHTLbaaNTbg7B){j5~_$zVM1p1XSHQeS+u zpI?uU#9IxL+e*%Xk2S60$^XON+s8>+mH*@SJ^+iNlA_{6I;hBE$m5DgD=v?MArP!a zW(~v6z>dt$tTVH~Ch3|~YoLOWl38Ms0a}qtqFJG#;X_IJn3=9-wJI7u24?bmzt6d@ znKNf+)jpr^@B7bpW?%Q-*ZW-OeeQFvbMAAW``qV&Wxj11))$1WS>AV`ABSlUF#d8B z{IlHMPM(y7uS341{K!`?#W)5$k2Vj!ciiexWvFlA{ub+%-(h?Z>Y0(iyq`}bSJHnt z#MA(7w0?W$>GmkOqcP)^EiT zw5z>`O(e01oB%wZkJxkx@)`Z*^$FOP*EwrE zu5;iaD>!ChDQr?VKiKkbd=FzE)#bP*$2nh)Jbw_+)Y{hSJxtS%{qX#RAI^hkTWBA` z={&w}?+UE_cr@;nIO0tn=VlrE;67o#v|)}m`@b-c`j5O%_MqIodr5DMXY0S|5w!Do z@BbWW`vuaY_WB;=hj3I$xw{AQJs1;qHO`G<)5{>U{-23B@5Qs`-wYS!;?qduL{pBj zmf6b~bA-HR{s!a6?|m0#l;!iA;MUaycl-qBD%{uOv7gNxl(!uSYYVvSqhuNU1!Rsz z3BHK7D#}T}3+whie!iXWj{Lrwec{!#-&|`yu1COhvELwcifz|0-@c6NXud@uzlVeQ z7Q&o>Cm?IS{TZ_6*EY;`x*S+-a1mstmub{`qj8X}n;K!KsZQM#Lf<$0j^Dy}S=xL9 z!$tk{KI*62A$J^Q+g8{pAs4T4kjLUs`+f@YJ4d1IhB4Tjd&%d-e7G5Vp-lIJy?*W7 z%jL;tYrE+{zrQIvOv4gf zyAx@D_xRo$-$6PqW&EV<41v5u%Ef_O*y z4b8*T!MWDh;>b|6dcUv}->0@A|5z3;gnPTB_luCX z^x8A}4)_@f`O>4Y#~999#(s!+4*|ZH?MJlb(64tm_jkG%{;m{%?;%fFR*pg#mw0>i zv{hiQo}l{FOHZuZ%QiLV5x9H(Tw1>0iZd|RAiy&d?S=~n;JjZ@ciN|L^o9R+$;;|d zeNQ#_+(5pU*D-H455T+l9?UhJ3|oWK3QjA(b3wif@?GJw{TJPcJUkZ9xEIjB%x~O$ z7h+qC<-A?;_;$!od(JRA!g7K^CP7G?k2p#XQbCIqm zlXZLl4sI3FNn4_f)ISB>i05h*-jVx(cey6+|b5DY-<8nw>Rp6SNKkg`+P?*{M0JU!9kh5kY%?Mb8-0Y-HEw5Y;(Sf zc|5x?kLOt2Gwo$7j>J626|bOPcnG|+z(d_(c&`YLbAVi47`$(S*TFje9M0pZI_XB_ z=W^7A>oBgCZP{;&4Qan`K;Gb_^J|n*ckY{6C2ODIn=a;jUxv0E+o5xy*JsHj$mil< zU4y#1{^w}_vAx3gsv*d~M(B1(J#-f2I1ZK>zW1IA`RAC29mV+zIL4z~d;xjP{dJpx z`3zuQwkEkM9}rjSuDuWIXCpl3`Q^Od z&=3D9gVApaTzCJz(fT}m75v9=)Iv}0u?XX0;CoRIJ%=;f6O*C43_QGtoBr;>ka-`> zL;t2pqdari_j!I*u;x1OH0~=Pe+7s3uV~zF#&x~~ch;@jamG8G>8soTnRVc=aptuL z;o9|(`8>T3^5;a4vTpX0-U56K+}-xl-;js7vA`S9e_s|lwQ6nM-cO)>1t{+(&aHTd zE<>FXD%)5UD&IKx69LBM=f?;eKfR&m2uZU6G(J?(f_ z^grmZ6_+1)*oxE8PssY4{y09q61F}zz>7NKYOh7*o#t35`3HylHxBq^s0&d;)@At%* z%^v96uf+IjEdlmm!D!kQSe6fQRCmc1??a9nVfeJ z)>1Use?bsvol8Pf9C_aH*gFj2 z%{%1t?_nHI{WOfxn~VO4eXtXrqxJuSoz6f!I(ih^iMILAMP2*c-%nvrBs?!nnlC|F z0=Es}J%#u^h%|H@!f%6zZ+Z~%V_zHF1k8`#Sd$Cidbj)){Rym3?H)geU=3iCcjh^D z7~_Hd4chtzl(}vD_MPJ9NhiX7%eI}&+|3J+Uak><{Jaojh`P-`Q+|I_hWsf*{*)u{ zDv*bj$V-&pi+bXl0L!o~zxr)}{cwC{qiwQka1)>1yh8K6`(UoXv$_e- z=`+d>SX2i0U1%HEp?>)k_Q7P^`I{Bry^d{Swv}%HUI|=>z3D!+ZCeTz|#0isR@1b5ZwUYo7~@cH@-Alc zF2J*@-xE_do}=F$apqnc#~>ct@IKG{=DXq*xTjd}G0c-7qrTc3!5EaoAR|8g!f02A z`j5Yx-yJ%CbNO{uWo0W^HhzG7j-zah!rqLB!Cwu+ZwKx~eK#I^eSH;sW`7k}zuCVn zHQ$?n2^5&715Al7F=dHOf@aZ~;UZ_|!tn_h=)kX94_FCmU&ke)xlPA~Mr z7$%f~4W^xC_k+MU>N{UxJ2xm>@fY|p_8wu&$!dgydbegJ&h+yH^nBmox;*cv?nXH% zUia%aOC7V;i8kpaLDizj@OI4KDc^)~kG4+~;}qLbW~SlTH=*vr>JjZn?klgn@Q8so z99i8f`0N>n1Q(7iYd^9P_e3M^gNx)H@mQ4mD%@ASabHzq9jlAVt5#G6yRSZa;4|0V zfOY)WBVYNh|1jR^*f!XTG*{u>dTQmE(TAfgjgL5Yv{xMdFx%drK|Zow&G5?x*>_Ne z-y7lYhw!=HTvZ90Ta`=NW#m=_=u0 z;PB6N_$wSf+Xcni1dIpY0Zdw158*%`HT$hs9rD7Nd%ga%Rr-yB_bK}UJ1g+KL;K)n zT=yM3i{IWq*!wfcyieIbi9u$$ez97}V{p)R9eCFC!~MYZtIYk6>)Pi~m09PFi{mP6T@>{`tv$*K+*ll5@vzJOKXs;h=xbuRf4pK%8wq z*WsHUgWoq`N7h&FnqJJ8eXs-TE6&@RhH+|Dr@%hAznRxJA)F5A(Fbyeu^rkYv|VrX zD-6bb%9ksoUc_8h&Mn)B?@=8{Yc+UPfz9jR;kr7ehxLnjR>OV_|4Z;6LcRaOM9hh4 z2+TMpe&<{!;oOfs+g?Q6{qZ6jrA(MOp)Y}L5%wRgyK?d#juD-QeCxm%;cC7=pTq9} z`7xrGmya3USZ>FNR+rl`qMcX|i2LadMmnqUd}O~2zh4i=yZK27+w7-{I(#t70KZE_ zFoq1zr$3&E`zM9Cvfuh4tPRTc8_EjzqP(N(Qr7ilrVk=_pCRlsWIx^DgMBsYP(Ng6 zOxgJBi-V27#`l4B;Qa|{R@+^Sblw5CTi~Ym&b5$lhuaM}YkseST*q>Pu}VuJbL`sA zH@n*H3m;*(I@m4b*saR3TO4+qDt4<9yX_FWvCl>A_9=wn+HIX@H$Jzpo|LiKSN={z869I;&2~~^~`iZpS*_W2+KLn9Gmlb#Hal_-go^rvE1|Ct_?wDy!9FCmzoz1Ohw{n3wpCmk^_P{l zEwdZfG5(|v>rWjhPq;552#f77j1w-Xn;&$pKM&*282>Ng+HM#-5JnrG;j<(Rj)_57 z2eX}4tlhL4{tm-YhqVDWn7-$JuMR*vrkZ`2gKhkgX0#VDPxJhttFJ`dnXi9&!_C*i zafbGLxgD42wsAIKPGt-De@9%{r@($6j;A?;`SG89ru`D|n^$#jGv6}~ca&o@-k|<^0-(B1mBJAHGUK^1AeBRv&`EDF6k6iotX2@(8@tMl;aQiOHkKbn-c`)l&)U_ex z8S2`dgYP}i^wXNOV;{Jg2%k2;7jpsq@^L@HW*ISYJK3|_(Zg5Qf!m1u$2f9~{l{3e zB4OO|5n&9|FnZfC<`xO#rjH2Y7!9L`4dcurVO;$YVH}`gnErza8%Gxj z?~i|C-=5K0W%6yjKQ{sIgm@R6XxkLjvrWV^`=R_c z!&~U*Sc+@nTS?CLHzVFZ*stv_``&oSrlX$1y@Pp&%!ALW_P|X%-9+CbAbEV_?vs^>WuMc=f!u?*B`ZdQ3b}1GJjm&+efXA z6?32Z$<|*Zq$E`jS?!|0+JnR|3HtSHXHz6OZ zq+GL(T8H)x-Pb$r(^z)I{rislM7USs`OsJ%+(rAp3;Vgx-@n6e2j+*hgNyYO_HNv_ zZ};EY#Vi^qN!7x|{RF`G&k_+hD^h;C~zXk=WkZ zfiaVv*h`IL8h0RFf1}(NGM>*bQtk&C{Rq!f?r-0x%3}FFaK|D|=i$4^)E=SHJNUg6 z?GTfm2=jpR2eIxI+Ka;x|8mZ2H!FO6-1Hc(aVX+p@6{tV zy&>Niu#dwXmw;bgVe6U`Au~Shueqla-g+$GrObLWH{u%YkJ*0w zDbO=cJ(&J~qi+yn-Wi_`jC=Sq%Fa}r*%xEnu+QXR%r$C79JXQosHqi58^-QTMI2}| zyvM&f9`6sd8RZuc588|}^O`oJ9H5`z9rU#$Pgf1thW;{?m%-II@cu>Hl%ZdbWwJl& zM0^*$;}=NFNk||2h-eeqv<+^|ANs4haKheYz|#<)!HCZ^#HXsUeB&pIADH<{yEl%>5sfL_woS~_TB;=pG$R^XL%!Z>~mCqy&;#u%`X@1Gc(_%pkw*h zvUL^Yt*}vsahLKIgM264zJ_zB*lj*!KTP(~y!J<1)_%aa@3L|K3F61^iW=WbfcaeI zyiiT+3y8;4;Jk~oy0I^eaa2ql`^nCMy*`IC`z`K;{rJtj;5pW{eTjN>KtcQRPOOv0 zdUWtV^UpAo9_*>E_s#(9seTvIqjhAlX9N4MweN*>%ij?$`X$U5_7;p?Lz|j?BwOBk zUE0PdbI%*u+)r<4*?b0W0r)$R#zuKQT!^@_j<9nXy4KHZu`Dgs@0Y@LVI)U*jUEDF|VFBFrSw%aVJefWqmnY{q z`B4?1j>fZLDdO`1@&VzQejYyiGR6(IskCdWXqc0cKOHE`=GyWucrM0Z&%=A%VCam! z^_l3jjf{VJ*Qi1}sH%7G)t$IM%3%8e(Lt+^m-6&&qd0A zx1n~_2mMeN?gwnHF~DmZHdn9i10CP9*=87x@ZC1UpO8+ZC)Z}cy|8=m&w3PXGa%ng zo8bb4=e8LN?j6|FJim}%A(p3J`F7tc_;$p5=6t5nu?_Q~JI-H(GxH-jsHb)hjt?tn zXZ~Keb^inIuZ24TT-RP~2b>0*ArH-c@EmM$JIX^P+!|qHJ`>(SpAFNAxs8WwxiRGs z>xP=Tf#dekhke^0@^wQ9HrR%6*j5=}%3krd%6+(=)+Yrvv~||bf_LJ4on`h$|E8qr z{nrZ9#5G}HDRmudy(FJGo9B(zlB1)UjUuvXj0- z8`|>x4}_z3T#4(!Mg?K;y(x|`=9Swv>j6db;z=iri;9ME$fmg_9^*IGI`Ngdq2PKm zocPW{T>9eP3d)at(#U8>x4oQyV3PCn9+k8$MV9Qi~?KFN`XI`T+I z9_`3u9eJE1PjKWZj(nCQ&v4|~j(n~opXbP5a^(4ryugt!aO9*TryY5*BcrX7vlZql z?BVX;v*EsTZj{JQ`{(&Rk?8uuO`6rHind0SrgL_c`(dj{J}#|K5@R=*Ukx@-vS7 zoFl*J$bWa_R~-3uM}E_h-?j2*QO}RK@1WJY&;0eq;jHs}=E{G6_J`n^6rld^&oL?Z zmStql6F*aAeit}jJo-Z-^Zo8ck#B(fFOjc@ z+!uqR7>Dmb9wG8IkSB}$Ey(jkUIqCgk*|P!oygyWe4EJMfc$5Xx&8$DgiQFCLOvKT zB)rzekOzu<5#&#cycBZ1$Ssf)BBvpLLu9U1ah=Es$UhZ14*7nOIbQS`k-6^iTO!Yc z+y^h*jKdcp4;1-4$oST1-1)A0j>z?pFBJK+klRI`0r@78KLhzLk*7j_ROHE!IY5us zngID|3{;|g2INnRTnl-Q$frSGB=Ts;9N0wvqagoK`=1e#TLw-o)gCOHuwsG$R`9C7} zhI|Yfn7nRJ$ft^20r^~!1IQ_n|AVob%SC<<@_Lc~1^EGy--i66$ZtUYK;+jU_s2^J zuk|YA5h8a&o+k3&AkP>1MaYXqejf4)k)MTptH^(Xyj|ocAwMhfW02nvc_-xk_OsXh z9pvLheh6}{$iIdBd6Bn6UMw=l^eePb*%Z{Fm2^Foso zv8Gru8(I))$fnY*p#`aQXkjcFOGmQtg5=)yBL+7`|6VX^Y z$fO&F(P5Z43~g==YBSk1n3+^dx*;|$_(Cu~lZmCX@lXw5dJl3mrAST>W5WE~G@(lGIc@rigO z6HIGrPNlOpQPYwQsitO_YqnCODx4P%CchA%xO zddmFM<{R;}1q~w?L`Dn_ogS(VHz5r%n;%0$;bf#KmT8VO#3*N)BF#8wTbpB$1`iIV zC8M#W_Tn?((Mq%84T};Yhd)dA!2YiX&w4pGl4ud}i@`;~SAyzbaBxNtX>N`sqd_Fo znrw)thBl zbE+90np>BK3-ZvoG(?)?;90d6E`jM-rX_*I7>~wSA~TC1N)eAOMF2?R0yv>qo3dqs z%$0G5pJ|RU7i=)u$ig5Si6`KmMJ~hLn2q6Pf|g{wAr*}UEzQVxoH8*6Yl(}&Xi7y} z5;1cHi2=i>EsWcREs;bHFr(-i&xCWuEUSSl3W-IMFASJv1sVz zP_{9i3E4104UuFhl}xmT=Ep)N7ST{VnN7hhMj;nUGeT-kry63J%+TO$lOv%)l9)lJ zTGc9cek_&@#Z7${iw+IVj-AZAjW0| z!eL&48l=e-LIxEocx3jGGrRLsV%DUxky4755Q6&?|;i8SGEj!hb6zvz6kCec0tz5YTrQXictW6R zPJ|XDA`3I7Au+La3Tt#kHy@Ts0vf(5L- zSeGnD2Bp%xp5wSA7FmQcoA*b%gwcK>Kc!C?QxBPEjja=mr-g~sd^}g+Q6yZ&C>j!} zOiP*tTcn(V6(C%3Qm1HaK?Kz?Ls8L0cF~do7)j!0viHAH+o~{c{#Z7I+c_fnprtV! z&R3fs<(Dz1*#~7pv>T)dN@r7~)!fF&;wT<>xR3En$eMOuae|3OGL<~p)SYgkx<(+| z(u`YL?i^cz;T}na@`V!(vb-5WOX4UhtW+9W(yTC1oFWSrAXm+ewV2J6Ob8DaR&F7U zo+)57uWQk+aba{nDk=pb{O=-Dke?qOn^~i`&`wrjh_}s>OsF;0!Ypb=D;ZWcfiZTj~hHV7t)|wI1C#%DCfX96rl_W9YR5?*d2S(6(Xva*?Hgoa=ItakbW`TtET5YEq0nrXc4tXo|3TfX9d} zxcYKxN?2jpU6MvKo$s7#k->k5KReas|P_nXh_I<@C!hJ@l8Pg zRdyN1|0b_Y0F#GWo1nayT>oDha%wCcL-miR2yTKUjkrA$cvSN?w0FU$ahK!n%QUhg zz&}<5^5P#0O|3tBy0KpF-G2z`Vs;2bLQV0dY@ea2%9d_4SEpzlZ6Ez{R7z+a=)F3` zgogJ?-l~#Y$S6L)k;klGVomd7rY4xni{qY*EHt%+n^(u%TF5q@kjn*^FjwH6Ivy|6 zlZQrfc$&fS9G+nC{QND8r&4I*^zn1%R4*PpHs_5&9Up3pCD@ME#0?1zGA|{A*kX*J zy@%RoF_OdvQ~?tW`IdAtbdh);8(RF8`~{owTEI8{0>ca-I>?pBAc zjYO>axO(QLiBiVYak);LT|4~+!xEA!+reNwpsfEEw>Jn-_B85^oaw@uTDv4soAbstpyP;{+TA6MHvZafPA z0z9=7iB`mop)((o-Q5sFYlk(DD;C4TjLxIMPPs$RT(;m*!;&Z-auo(&Lh(96zbM)I zVuo8DMh~G|sJR8*g?wZZN9Ly%;~CqKjyGrBTeX&jd7P6QwJgQ1Lnu^h3emW3%Pt-a zEZr`tc9rW12${R|bSb9eZ0QAggy*x_xq7ZR+0s-ye;kWw?Xdad+!9Lj zJITV;oG`j6n6;4)Ep+2pFl&cJ#+khJ1OFHW?!-j))27Vf-v2bp-H8jcpDr5K|D>!r z@qsN0j17aDTsB_#9f?Xf+4xqp=o4B93SY0302RVXS&1Y&9mhd4}xI`1`jQC zMa37y8_)>ALo16%MlNVPHt?uo*^@@D>Fu={?v_B)s$x}do1Mm2buN@#1<=$wG?^LZ zRFmYH!e8w+3s9x88!Y5B3~G%7y1$bdKeS=v@;BXo7mpz!W02D?8q_>!>>#P6Y#axD zC4WX#cKaH_?sV&AU%*4BL6AQek&PCKxmW9U6Qo8ze7}+AV~JnXTbWr*&6v><&ue zja^@gko2LW&}kK1kcuaf5Zg=3(9pY$K0CCdnEZ%#en}@fo2`*#16oZ>8)D{7EBDIY z7(p$I+k$N_^tf6h2FwsP$_CD6JFX=WMXw(G*|rC=1R0bI6ZQ85^w&?;pi$6}g#%{? z=?J>K$2!+ERcG!s78h-qZ=L)gKD4mluBAOp2S;B1(BkENt@$v&(yJ_;Vjo^?wP#v8 zJ*Ya7qTYAnW0kz}jh%&3sjcaCn>P<5_8 zz?Up)G{FA&M?Z+$|{nnM{0P z(tNE%mk?CwnPchCG^FsY13eHd&XLC%xy zvMPS7^9z@KvN(a--ex?~V|ap@ep}c&mNebhgn6%0FQDHGMJ*f-WfH0EkPx~}LMGrY zo^co2J`nVY;6;P)C8jG&0~n-FKJS98OUOVW#TC%=j>p~?@T&X893gkT;2iUHivejL z|CeJS^Vx&8WVK|iS6F!Lonb5;M&WK@a&kp-1`31yyuh0P@nFS2S)U67@q)*vg#;`4 zWWP3;=0IrR98*qAt5>UM@A3s7Ys{_2i)g07PCT{3s1mpenXqap8$zdsjDK*NT4}GNmia62-`*V;8o>Q8$@8nmL}UirI_G z#&@;JbZd*j=%T7OZRmI+o^55G$ZMllaib1KcN_XN-KvR-S={3yFW@Pa-;bMQzRM<@gp7_Lx0wIb++ zg;UDb9s%8vL1lj|=CJD6prR@iRK9(DP~JH(C<_e=%GVAGDvquWddxd12p%~psCfFM zpse4gFwWsq2xCZ4HfCs0F>7d0-Z(s{TsAVOSTz#Y9~D$Ij|zf$H9^JZnxJeqCeAD! z9hCR|bWj$p4a(oH4a(M^9#n2UE9kNFGw?S(sMs(a*P0Pjbj}Pa6ZJuG^f^IgbS}7a zgPwPsA5>I+F{r$C3Cz(Nl&!lc2>LDydd&R_u77b5Jlhsjo^WMQHsq=xxaX>%tbSEc z(ebUIXXQ0PS>xKE=dIUaQ2%v7&o$qL`*(vL)4vy#Z@)1pYx_YERNoYooqtnM-o8F4 z>sTN3n0j+iIp!BZaQ!cW9{1dXaJL~|_Xd?UzX{6j_)XCB@$Es`%KL-z${pZ67?dX- z3W5y}1!Z-Q1i`jPf}Y!c2OTC1Bz_-M^m{ZYU;b!N_V}Yg#jZzT=bb_MhMht1_|Blm zBaa8c@F!rmCxeQ$PlAi{E}Z8+6_iyy9R!_E2W2Px3F-M$P*#aW@`pTw>*I`pge(K4 zU&#U`3zRHSvOviKB@2`+P_jVD0woKSEKssQ$pR${lq^uPK*<6n3zRHSvOviKB@2`+ zP_jVD0woKSEKssQ$pR${lq^uPK*<6n3zRHSvOviKB@2`+P_jVD0woKSEKssQ$pR${ zlq~T7qy=t&%_h4%2pM6E;DdsB25%SKQ}9EAPZ$5aUN_z_NyYl_Ex1B(AHh|E`w2ck z@BqQb3a;~TSnvSRPk+;1??AzAUU*js?j!o|2u@3U_y4Em?=Skp1n(y}Bv|=F1osmC zD8Xuv(>(q}!5aPy!D`>Hiv7wYKC?XiY!BCa_*@U4CHBzp=X&~KqSy4DCb+kx=R^;m zCwxtBMDQWvKkMl)65LPHbCqCC|8;`ZUa!Al?SHU@w@&mL{_TP*MgN%i*ZAJ)h4)Lr zRdW6N1@{pCLxOvW{tq7iDZv_khhVkWD}pP8|BA#% z!_x(8d45*HSA3r6H9pZC4w-mL6yDW9626`ucV1gpJn z5Ulshk0kyY{w<={`~R0-e%>y6P2Vp)|Mv^l^gQh89}}$Q^%=pMKhFu)^0r&Brtd$3 zwZ7{k@l*W~f;D{uz4V-pEwa7*Ws z1xgkuS)gQrk_Ac@C|RInfszGE7ARSuWPy?eN){+tpk#rP1xgkuS)gQrk_Ac@C|RIn zfszGE7ARSuWPy?eN){+tpk#rP1xgkuS)gQrk_Ac@C|RInfszGE7ARSuWPy?eN){+t zpk#rP1xgkuS)gQrk_Ac@`0p)H{}0QVC+CEmm&$p$oL9;DdO2^9^Br>DCg(@w{Ir~3 zlJjmkzc1&iPJ7*cat_IPh@8jBd6JxG$@zRaH_ACH=QcU7l=E6Sub1;?Io~7a9ddqL z&K+{zCFebIu6)_Xv#*?wmUFe7Yvf!f=jn2uE9a=3o8`Pr&h2tuBjW@I=R4%Q zP0o+V`Dr=7BZ%h{&J?mesWhn*M8eCDYhzx^sZk9gkl z!uiG;)^hAo%b#lJVn0^?WN+JGTW^c~|NG=OO%z+Z318>gPa5He@IS*fLYpPwO%S|N z@JPXL-)x051#cBSw!SsLZQ`*`@P@Bh3rrQAiQX;t*Q(Qs zQ^tNwe-p%CyM!0*COloY;iLTN`Uj6oIGx>uH(jo~U9LNKZ1HsH`X3+V?_3G*>2AW& z&>KFczm)jf^%3DM`n+auCG-84eQ6Z{Ued}$@$2jQN$b+Z z_5O20qw(w>E9rAW-P^~j^nd2O?bgC@mg?D71Yb6ha*;wg|OG*{s-)ZBOWw6>}$oTju@ zgqo+$nc)7-o|ykr_t{ftPcVN|M5h?82nKv;CpY(Po{I4M&zXe()i|2r(pENm#%CGT zadqKYtrH@d*v$DC#2T{U`qt)H@YxIduA=+IDJ{u{Y&?~;&cbdYnO6l+e$8X^qfbCGm1o?MvMO>1gSrL!JkS~412>bZR;*6O*Oor<<3 zVlz_NDXErZ)KkugH2Kk(xj2?ykVq}@e9mr3X5(Evi))wo&dH{lr)6VllbU=o=Co$A zu_iBM+R$@4HlVX{MbQf30L9+17STdH5 zH=G&EGGlC9>sy)=F~=*T=)iN5@rG2?bqUXGhIQg!j!j8KOtzsogr`Ijnb@3cx}_n@ zVCgg?mGoTd(=9odPry?+lZ`Yi3O6(^3NLJlq@zJw+XpiFylhonv^5chkCyr2`Elev zqHAxUv@MzA(+k6}T|64e#!wQLq|#A1wKT<&*$lj!S%v!S6SosLE6yfg1SJC@0&(k78l z=Uri0do$8^M}d3Ze4TlBRyUKE((~>@$0@mF?h%EWDvWE?ry~uq`MA5tY%4dpF(r)} zP|I_>b+eUicziT!>rQ%KRiHvq%d6HFSFz?Nb6tVjhE4YkMci$m(_@QciM&BJi^nAD zQ#>K?e@+$!CCu_Zi6L#Z9!#Y;zPUM;G*;i{xJvonAx^9=$7iPFMEcGG*Et#FYS_g5 zX-C1@LJ1w7yF=-;DwG-2FENwPyYo(GM-qut1FyL!?>IY_b8V|Ic~V%(w2Py8jNt|h zPfNy4^j5hFT0rT;$E&U@P{1g8BX6*(87)ooV`*kR{ckRCoR*xGKmiinR!@gV zF)7;$oM*?HP<`2YI=#cHX3hy`lFg`hvkOSrDXz9XF%h<)>(gCar^J)yhD$RiCgp9` z(Z#1VACjikZD*ku)+nEgMHbDDEihHb?gF)Q3l+GZIWdfCmvL*WG`VDL7l!+^j7H8_ zt=-r2zBBjSDqnL>GN)TxsPi7m$U9FjPix#q#T5B!W3#W;s(Nb?g{f(`6)2`gGPYfS z>djY}T6SlF>$J?YB%a{O23yB@x;ZWL8~CuJ89O{38v=CiEO4KVMj>5y7rMq98YiZj znj^L<-P4_NLMmmmtcROrs0uYn?Zp&%9g9SBwIf5^AsSZw6WFkt8nZUr zDGJllV^t~*il;sI+%-B}g;c1WuG=gr<-5g^ZARX#wo+#!t2t&XKgR>E%qF8L5p6w9 zE}6zANbv=6`}}No+)-Ob&BJ(=b=0OPT1t3zV2alkDkjC)R5jUCsL0>w8$1Q#oL6q{ zs?3*AbVhS#18abq!BuK(k|LW@ONg(TcM} zw6N>BF=$dIq)u_29*ZoFb>Zx$7Oivht|mnx{F!8GNfI}|xySdoK4ztw8N0S#mX)su zC4%jqqM#~c3smoiRfQhUjx37RH)}%Hiqb99=OoeGYgiPEB9bOY)eno>^i*n5i;39= zan`aiC6dWbOf|PoY>ePGfYnTC+wA$um8`9z2v6k2^qm$hH);89Q@!&P+M#Z+0iG+k z$D{__*~@#WSrCcaCma<`v8IM56Xy<+EDZ76{L8i6{7s?Hvv`w=NAgnU+F0o(c2e8%`{3N`!NM4z%w}d;0Xbr{~HO z@9EKAmR-5u(xtn4yJ4X)oVh(SL2_n#z9XH}}K)f4}}GtSehr z)?U_GzPab_vgoR^&1GwjTwA`XZ0hccWsjFF@4dNfN6(dI+k5rHZ`HwT`Yb>E@$xn0 z9SC%I-42{zD%(|-XuPM-$^+ZMT3gn?U!OjG`}XVGx2mdd-#%4U0Db%P?bD|UG7x^i zfc>ia^&^J2s=oc9rk)CW?$>91@D<`clo4%{Oy=+5|9p%$&YwGK3ZY`hNS-yL4 zbY1zniX9bimtS8|SGTTwYuVQFZRN|6lIg3;YbTjsU0qxGq`K<<_SZbCf9U$MA$xk& zRxi7!tbhONAvI&>&8n%Xt*x!DshwIqwJr#r-9HEp#1b94p4yv&b^WwHeXRbGqpiJ9 z6x@ES#p4C{4Otu$yj<{Qf>#Z+`fm%aI?>`=1h)zPwcx}_R{yx*c|$CIRq!ssWe3>! ztv%W5KPkBHP>W9$yhiZ3f}fU!Vv>S&owsib)^*x`Dp=QPdqA+R%l3j`U6*aIU|nbJ z0F+bOcZaOIc9P(Z(KdcF1natM3kB=CW0wlnb;GU~tm}XMLa?p__KaX%@9Q1Gx^CAY z2if@Ox?3Xz>$+Lf1?zfOiv;U>RVxKI%DPlP5xiUQZv@XBXVdqTU|ol&M_+q=T{ovj zu&$5OAXwMQxk|9EXLGOM;ieZa2%Zx>b+*0UdxD=9{K-$)>xJf6{YirRo?~&n;FW@x z3D$M4?i8%+TD>V)*RdLYunqs&FWK;>32qEq+%9;CtcP@uU|pZ;O~JYj)k#9?vGhx-QPkf^~hI zfrr}g+hx6+X@YlNCgBO*yxihdg4cY_;-3m``?|${6TC}szr$?!x;|E|U|p{&E%^F1 zmftRT{kJXtp5U5mEWTIpBZ8k4{F2~T1@93Y9B!{)d9CFiD!8xUlLUtZpCNd-;CjK6 z1jhx}3%*!zRPeV1Cj@U4oE7|Q!OI0dA$X18R|T&VT!w{~nEx9DA1-*a;FAU4BY2|V zM+ARf@Uwzbf_Dl2n&3Txzbm+Et+m(B1ospCTfrg0e-T_Q_$|RTf_oof<6kTISizG7 zpCWj=;LixI7aS2hPw*1K3BgwgUMlz|!OI2TC3ubCM+L7J{5Qdy1^-*{J%T@Rq>cX$ z!Gi?v6nwhimjurdyi0IgaPS>#&r1aN6Z~z#M+?41@CkzN6+B$|iu?rC?ppa4$JFw!EwQp{%rL> z5!@#DEx{WEpN97!@=tiy`oBbQhv54K?-9KJAnU*HU#$Pn3LYbPjo_%@rv$GRd}Oud z?-V>%@bKs4`hwd8ZxVcm;GKdy1s^b2;{UwmPZT^)a9Z&7f^QVOUGQ^)_Xs`;?}4;O zO@|F{uHa>Yzb$yZ;6Do9CHSCES^kh0EPpz2Wf16k<1K=3f6eMI5xhn4wSu<`zFF`? zf_Dn;^}6N%S#WQ`{}9|q@E*ba1Rsd^SjK07;2Obog3l8i7W`GgZGx{8e1+hj3jU7Z zKMCIdpEiE42|i44aI(FANbsS8hX_7F@F>Bj2tG~lRKXJkpD%cZ;6}l-1ZM@$7ThMd zUhqo6=L+U~J?%4B@Or`L3BE&cMDR|*S-~BGFB1H3!B+`BXs8YUI>E;XUMG00;M)bC zDfmvopA-B`!Ht6N7ra>TLxR69_z!}=E%+(HHwo?#{Byyt2)#DX7LMx_XvJdu8VY!XE|e zJ_)|a4&QR~&DzXkfJk?g%SbQ6C6za2{;f1^CO<&>B-mr-HIB^SFHTnibB4tCtv%6 z*+0zdD^`A=Lkjt>o_y^;W`8pIij{v46NVo#(g&t2cu zldt{d>^~=8ad&)IPrmlAv%j5u#me91<-e;ZU;E?PKTp15<-bhLdDvdQT#qd zzGC`k{Ljb1A2Io^o_zg&#qU|7V?5IQSzb-_?`f zd74oN{9Z=BV)`$3eOFJueoy1~HS!fJzs&}pyS}R@U%$V-BYefm_v7#C$=C07{C>yl zD^|W={#-rz`hAbz`^Z-S84-y~l#{WJc< zz2~Q^CttsR@_Q)xij}_`Vem)H>$`gL^?ND5pOUYb{(1c<4*rPAclG4!_thHVD^~t0 z!_M z3i$CvzN;r+zYp_!G5Lz=pZrxg_#-CY)swH^llgs_e8u!n{!|?N5tHxg$=C1C{2ood zV)`fFfBv|7^7VT)zh9HDSozBk27ko7zN;r+zi;ziO%tdh+#qKELmiuUPrpz3aPr^7Z>a#{-bBSowba@9N3d@d6w_K)&Mc z_^zIO9bdrl2IMR5j_>MC`Wvi)I6lFoUvPJP9lt<5`8s}q;~B_Tto**cZTQ49JU#h3 z-htyE$X87NEI<9^{g{}1S5Llv$86KOtYS@~2Y3 zkEa=)o_rmj!tpBPE2jTq_H*^*>v)!g@D(fnC0-mqVqV|Xldt1nI39-AS4{uB{w^H+ z5tHxg$#46*Q3o79L%w4ACqH4;IlikWU&q&Qybbw^mEY*`T|N0a9*5&|$XBfVTJQdI z_2jR*!d`&mdB|6+{64*`o_L0*Ctt_=aQqMXis_%}ucCk-PvpCL@^yR=#|x3KnEuK4 zpZ~6&d>v23@kQh-R=y11$X(ynldt2CI39_7#me{F@2;MF9k0akOXMq7K5i@XBc9>u z$=C5s9PdQFV)|$L`xtiKey*N;9S;>8ZuyG4dBuZ{CeRlR=$6IS5Ll<|KfNsUSDx{ zd{<9?RIbnQW8^DVew9sLuKc-r@^yR}$D5I_xI4b9CqFCK=lC@86)WFQzpE#Ix$rrj zjeN!3@m)RnYlP47Z{#ahzF&S_J^4C5j^pLXSFC*Bey*N;9Z$#cb>!!;ls}*E>dD_M z@#lCv@)biYdjGk4@^!o($M2D^SoyyFT|N1a$n`nik9@`5@m)RnIv$YY1Ibt19pBZH zze}#q@r2|nR=%HquAY1yZ^-e7Np*YTL1?)a{r{N-|ej&~(rvGV=<$JLX+M)({b zOTOan_^zJ(^}^?PTJjYu-?zW3Ctt_ga{Mj%io4^xdh&IAF30PVuedwDt0#Yl#Gm7P z$ycm=|Ne3Hofjt3@RvGV=)hpQ+5CAmJw50kH0`Tq4?J^8zY&+*3OD^|XLeOFJu zjz{MBWbzev$9MJQ>-c4kXC_~<^8NU`dh(C{j;;SV{+WEm-SJ&L`8qzDio4^xdh&IAH^+OEuUPs1 z{qO3@KVPoT@!{kvR=yv9S5JOa_#978zGCJ3*LU^gXNAx4=j1C^et%m$a_wJNPyR~b zbG$nFik0u@zpE!-$Fp;MJNb&0@2B6@lfO=`&++i&D^|XLeOFKZt-|N{dGZx^$9MJQ z>-c((w!}%TLD^|Y${B-r?>--PS2O(dv^8NU`dh+MW^*Mip ze8tK?!TbL2>dDvnC7f?UzGCG^J-(|aU+1H6ehT@DmG6&VarNZu{1wh;AzyKh@73R~ zo_w9}!uc=cD^~t`kMHWq*ZDD=FGId!--ze z$01)a{j>k79S48Jk6q<)5o3U+4F5z7P3|mGAeTyL$3z zk*`?!zWrQ1`8t1y^NGk;to&M}fj{CIo}PT2Z^ZdW-;m$M{p9S;78$#?bS>-;&+rz2l6{TJiA zdh&I?-Jv1NSFHRH1^js8^<6#rIzR8MkmW0;e_nqp4*rzy>dDvneDgwj<@?Vc zS5LmqNBlteik0uz|E`{VoxjNWjLd(<%I`}7Kb~fIdh&I?Bj-PouNb09zyJJj_2ldP zNY0leU$OEh@#6Rq^ZKrye4S6p`IY1=hG?$u^IbjpI{%XMG09il9pBZHuk$rIf0KO0 z-SJ&L`8vOo^F7H|+#TQ5ldtnZIX{$q#oh5;J^4C+l=Df+SFHRx*pWZt8J?beoo{-& z@D+E*clG4!{8Y|Y<@FUS-(1+azNdDvn!JIElzT)oquAY3IPt5tn-=xd2Pa=K^@viJ`4g_5e4Q`O`Qzj(R=$7#yL$3L!(&SxiIG5wS8mmgP8zRq{Q zU-*i<7Vu+W7Rpnt0!OQ_jA5K`HGc4&*Qs#@^w7`t`9)IV&(hwhpQ)F z*AKWv_==V9r{C3+uj>tP{Q+KIad&)IPrj~C!1W5qSFC)${pjk+KjC?6Ag*sfzT)oq zuAY2d|A6ZukgvEqzN;r+*Gu5~3FIqQK9XgA#4|iS`MSOW*IOW8G5xdr`1W`8A*$s<9ZI{D^~tgtIm~QS5N-34(oyIKaj6j`F{FcJ^AZ}&-Eh6 zSFC*h`mUb*UBc)3667mZew~e8F8;2bd|iKn>rs%eSowbZRnPS*POQXu+1Km^xLyVE z?Si>p1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc z1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc z1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc z1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1#xD# zy??k~1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1+lwc1@Y~2eXdtQ?5MH>|z5UInqcUIp-Z|@25WDMB5WDMB5WDMB5WDMB5WDMB5WDMB5WDMB5WDMB z82`@t6vXcO6vVnd1*ZE1A)A%vKi8|^8Vc@u6~x`GS5XeuL3l{%`V$QAp#4#w%la0h zfua7u1de~_dN}Ukx8zK0J$Bd#lu@Y{96w{?%`b?-s|CB$S^qN@`K-nU0mbg86N(UhnqZnv4^kn@b^6YQxD(o z;paX4riUx>UhT%epNEHexX#1pcsTCiOFX>B!$0!yRu4bq;g>x8rib@=_z=`RZu$m! z_zVxv@^IS2D?R*O5C6!+cX;?W9)7~ZfA{cv9`4nnFg=HOcz}n8dw7zEXL~r};f#mB z=HY8Se2a(g^6>9H{G5m1^l%yGJ-PYU*TbVcJk!GqJjrJp7P{|LWm) zJbXZ}!s{LD;SnC5=;7HOUg+Uv9=^iE*L(Oj4?p7J=REwbhpX`HcI|hVhlhFiEDwi0 zob~WGJp3IGZ}RXx9)8fnPkH!n9^U6+&XaZ1H^9TgJY4JH86KYJ;k1X_J^XzS|H8vh zdH5e5e%Hf2(N=NO^GOd6@$f_s*LyhS;bk7a(!-lQ{D6o5>fyIMd@$NVZv0R1@aY~t z%fnyraFd6>;^8YS#wG3V1`prn;k!M2pNAjz@N(2|U&qmo<8mC|#IXX$N*q_s5$G34@gJUg@@8Gx&$9HjDkK=ndzK`Pu9P4o0h~o!1Zo;u1#}9Gb zf#c^m?!>Vb2kYj$ar^?uJve@e<5xJg;kXyaeK>xD<9-|u;P@?$2XQ=%;}IOc!@>Ie zQ5-vQ`~k-waXg0OaU4(JcoN4`IG)DwCmhe<_%n`Yar^}b>;LC*bl`Xa$BQ`risL05 zf5Y*29RI-4iQ{D)ui$tU$1WVN;dmX#KXL5F@dge)KiMJr=u=SfjZ8(E+rc!4yKG%5n%thHf_1Y}f%#oZ^A7n0Gaa%o zsi=u$A2qsk-T7Hui|qR>8h6)cb;|GgEH1enp9S0P<_hscHNc6u&! z(|w-BwQ!ebb#(T47N`6U&*I?h?`&P{ezt|1v_j+T`%IMEs}-QjCarlllzVFgQ%RVe zv$;K}bu*i<<^9TjtiFTw>u$tKAH{cIg~r`_RiWHY`K_v{aCTnJL)|}BU2=P^x(>c4 zy`6Gft->>9*<)3ZZmw#ew<~teO=~9EcQqGs;f|~HZg*CLE!B#77qiD|-VahQo2$aj z*;O@lIrLRc1O*-=Wpcvet+B# zHSfdQ2(>_AcR4L^(ydP&$AXOl-H>I!PS>m1!82d`XryEx%L1RMsMAhpx}j;_kJ;rg z51lPcUB}$srLLpdv^4Kvb}MzDvrTDHSF=B9UZWe6c5&&FQ25m6k0c#loJ4w?kLCl+ zD`eMEsJd=9>N=vd+dV+x=x)XccV5tJDxKe1lr9BZinM)&_DEVA(@oo8Yj^(O3hO(O;$vIS^P#l1fX z^nROY>rTg|;3m%P_UUk(Z9eJh7O8^$J&n@Y*ppaOUt~v5S1p+iLeV`u=@pwf+ikZE zm=Z~3V#uZzY*}s`VD0=ip4KImEbzfQKgYmUo!ncup>a{z?C*)jc;01UOC%i)bgxfy z#wMS+-94kNi6}tJ{P29cRd!)Zz5x|-vV~SR?fd4|rc^W>TN-P~ZRrWkENuJ9w}FWf zY>*nAfc-$T@nnXaLJi70H)qb~R0u52EkqYb((%arL<}J}r_$N5*)vz@>L<(0Fuq-_ zZ9wpr(94B$E!a8OZB|{eZMdl_%>MHdGD~UBvwFZF41<|lfQyTk6H>HxikIZJg+?!Y~g&QZn0B<~>NV+$hK@V8jFR4-Z*{csPFWHObF zWU&ErE{MtI-a0oGkIv%5S~G@4EUZ&NH&{JeN69K%08xdQ}RBJ-{vL0uMs2N7>7rMZ3Pn6)SGu)yumF=rK9YcNTxD* z(@1SE>da)85hdCe=Wbqntz~CN;`tjXo=nElxJ~$)T9h^NV}xv4D)dLQe`I1$4Wsm7 zvwP-F9J{NBO{zZ@^|Wx<-httUR@~;v|AIOKHbwzOHJ^gA<=T5=A3Xkr%l z8E4bjl5CDQEW%@@s1XECB@y7Rot^RPF(Qd~Ipgl861nX+^YLY+Aanl&n6}kU~9%+ZT-}P@Ef`MIP+# zYMqZsFIo$I6qiZ1EtE;AY&I6f`#}qe^XDRoMYAI5eC=*4hFn`jy54g4JA-ka56+`> z#?0}v&zz&yE$(OLhpVTIpHn|^{PgMJ&rJUO=Vs2HG$(xaDz*GiQW{2jQ-cBZ@hquI}n{R58bzVveU2a~xgF@zi3DV~RPRR?Klk z@gx;Z(}<#p8c{S=BZ?+#MA3AOD4MVlMN>ASXwpU$P20$#X&YHIZ6k}OZDi53jVzkB zkww!svS`{y7ERm8qG=mhG;O1brfpQww2dm7woygXHmYdaMioulsG?~bRWxm*il%K; z(X`bROkI+Il& z@)g7OL)ylYejL%J6JH+uHiFZT_F4(1$IepuVTb*-GfjVk{dk~(N>`fuPOedMniG7Z zAJ1+Fy!01Qqj}rSYdJODymuQ?_b=8T@`6j|hrR*RLyi}@!mVou4#bz@-IC_W9a^;dqwTTi(AG8S@bs{mSUHPRIVg)~*FCit7r?E1191zbq5S}}~v?gGoQvzwh=Di1}(NAWEZgO;R9Q9nKn60*Li z5(rw1e29LjK#07xNUdl=l!qVm-kF`5y8|o))DN7w_ndRjy>sqi?*E^~7}!6d^;nM4 zfC056Fm6AV#7jhK`lwA01)T5&xPqrYg>}%)=ph330~{3dgYXo*kw$CO60lPgD%zpZ zmz#2S>{A2u3Q{*XI8Uv`!C6igfQ$X&BqAB@2J8h?mz0R3TSj}=*K=hge999k62v@^ zYN%xo8iArtP=HKJq|akm0NK~@5Fim7763WfumDKUl)&haEoUZ%gxJ}T^f5$8Ioe}j zs2nL|!?TzqN5&8|$q_}&K@0-sHNz4#tY}yYo}4Et!K@6&7dlTFw!I70HEzZvN5{$&E7^MRG$;NO2_I_KPPw8tnEO zjWDY>Nu-YB%9O0U9$%QpIwfD2XFMe^127&})O`nil42?IMBNr59Eu?dL^CL}c$KsGsLL~D#Et_PP*N}SmtBZI*Z$Tsd+ z<10^TWM)fF#%*RNPDa_l1{{CRtJ5HqS+ z4P<>W$c3R@gaptnQVMa0@ee9cPyz|uCV-PQU}7NmB9hRh7dp`dWLWqHj80c9C15!S zI~XLzkUN~yt5!)6;TR20RtQi7!8^4=fesgobORtjk4u#4m?J#ZGNi@CcDG?6n!9v)l$teQenIs?oHPN^ zgCx$NCWKGmySFu*~BL}*PJZ}vyrAtVtQ8Y@h0THh> z1fWDm1b8BCp4z^^nP|{L7J?~N(nQ1pIHLo;5}Yk*4W?dNv=IcfOrOZRm#?B z^I$-s4VaXP$5G3f7^`qTqe{U2N|j}iB22LpOscF?`hB424FZ?U${{a~a)~?|R6No1 zLwcxzm>!)8ZjAwYw55&cG{+GQm8fDRVybS8I|{I**UeiBm@WQV@WYh80LLKFA+R@Q_3bVzc`a58emvAf#Xgm@r|fyGThEf4F;o zz-O$}#+e*I1q8_SRGFae^Nj;dmSx}*Z&Gkef9BB!w1M9eV77LeG5GMgG58C9JqY+( z55C|ZmubccK90uBe`!dyj`_XeKHWvT#JcM)7OMU%#d(Bc+UASlrwZOqiL^_1_`*jU zb#WMpxaq7Kv{L;c}nZvH) za_@a!8AtAgS(@}ObEw4*yw(1$d)B4<8*HyyyqUD(e6sf}hwC<#WkUl8o99``_xqc_ z6CiYcrE;+~Oa1zr$Ke~>W_^3&t9;pR`<+%ERX=3^*!#!d5Z+NLqxjEyt`w9A9&RnX zcq}%$*ll*suO(-5mK1j37tWUsC>p%#JsfQdZ89&o{_fqyD=J=jw4V7QINmz$@+U6G z&vngmbLz`Ko1Weie3n@2E)Bdn!>XveI&&Z}y~TB9X}N#amS=w}TbSZ!VVvhv9sMHP z=3LKpqdILxN7IdJJlprc(MPM@tx~cy1yv!Iby_=J+Rk=Nj7DVpYups`8r zTJY1=fv`95O)p-otJt1lym!voAF3*2yZhQZ9D^J9+O4s7yp;bD4E`^tFR!IKjbGNb z7@)IBDW!HNCyEpB@MeHO0e2 z1zSr$kDU44y*tilx*CO-rw=?;hV3q&-YJ@&_?J0RttNFHM`AOgBI7N8<=d^btJCnT z3a0Uz)=1{9yA}K>tu`~+R?yp37k$rjzjxZ)JD-~0%s=(EvsdVUKeQ4o*WmV|?fq_M z{VNq89X30j8vki!>V4and*`P(U30G55PJJrMC+mRo06Lx{;-GSyOvlK z($#k{w9ieRG5lHiW49k)snYv0u5?vfS?)^d3BqT3oOr*}W%lR7mZ%H|?W!)P?C$wP wo_T|zE>(?@mse>dh2Awjc6h#eVL)3B(HazfboH0!SywFOot|^e#JVK-e@GhueE>> from lxml import etree as ET + >>> from lxml.builder import E + + >>> ET.tostring(E("tag")) + '' + >>> ET.tostring(E("tag", "text")) + 'text' + >>> ET.tostring(E("tag", "text", key="value")) + 'text' + >>> ET.tostring(E("tag", E("subtag", "text"), "tail")) + 'texttail' + + For simple tags, the factory also allows you to write ``E.tag(...)`` instead + of ``E('tag', ...)``:: + + >>> ET.tostring(E.tag()) + '' + >>> ET.tostring(E.tag("text")) + 'text' + >>> ET.tostring(E.tag(E.subtag("text"), "tail")) + 'texttail' + + Here's a somewhat larger example; this shows how to generate HTML + documents, using a mix of prepared factory functions for inline elements, + nested ``E.tag`` calls, and embedded XHTML fragments:: + + # some common inline elements + A = E.a + I = E.i + B = E.b + + def CLASS(v): + # helper function, 'class' is a reserved word + return {'class': v} + + page = ( + E.html( + E.head( + E.title("This is a sample document") + ), + E.body( + E.h1("Hello!", CLASS("title")), + E.p("This is a paragraph with ", B("bold"), " text in it!"), + E.p("This is another paragraph, with a ", + A("link", href="http://www.python.org"), "."), + E.p("Here are some reserved characters: ."), + ET.XML("

And finally, here is an embedded XHTML fragment.

"), + ) + ) + ) + + print ET.tostring(page) + + Here's a prettyprinted version of the output from the above script:: + + + + This is a sample document + + +

Hello!

+

This is a paragraph with bold text in it!

+
+

Here are some reserved characters: <spam&egg>.

+

And finally, here is an embedded XHTML fragment.

+ + + + For namespace support, you can pass a namespace map (``nsmap``) + and/or a specific target ``namespace`` to the ElementMaker class:: + + >>> E = ElementMaker(namespace="http://my.ns/") + >>> print(ET.tostring( E.test )) + + + >>> E = ElementMaker(namespace="http://my.ns/", nsmap={'p':'http://my.ns/'}) + >>> print(ET.tostring( E.test )) + + """ + + def __init__(self, typemap=None, + namespace=None, nsmap=None, makeelement=None): + self._namespace = '{' + namespace + '}' if namespace is not None else None + self._nsmap = dict(nsmap) if nsmap else None + + assert makeelement is None or callable(makeelement) + self._makeelement = makeelement if makeelement is not None else ET.Element + + # initialize the default type map functions for this element factory + typemap = dict(typemap) if typemap else {} + + def add_text(elem, item): + try: + last_child = elem[-1] + except IndexError: + elem.text = (elem.text or "") + item + else: + last_child.tail = (last_child.tail or "") + item + + def add_cdata(elem, cdata): + if elem.text: + raise ValueError("Can't add a CDATA section. Element already has some text: %r" % elem.text) + elem.text = cdata + + if str not in typemap: + typemap[str] = add_text + if unicode not in typemap: + typemap[unicode] = add_text + if ET.CDATA not in typemap: + typemap[ET.CDATA] = add_cdata + + def add_dict(elem, item): + attrib = elem.attrib + for k, v in item.items(): + if isinstance(v, basestring): + attrib[k] = v + else: + attrib[k] = typemap[type(v)](None, v) + + if dict not in typemap: + typemap[dict] = add_dict + + self._typemap = typemap + + def __call__(self, tag, *children, **attrib): + typemap = self._typemap + + # We'll usually get a 'str', and the compiled type check is very fast. + if not isinstance(tag, str) and isinstance(tag, _QName): + # A QName is explicitly qualified, do not look at self._namespace. + tag = tag.text + elif self._namespace is not None and tag[0] != '{': + tag = self._namespace + tag + elem = self._makeelement(tag, nsmap=self._nsmap) + if attrib: + typemap[dict](elem, attrib) + + for item in children: + if callable(item): + item = item() + t = typemap.get(type(item)) + if t is None: + if ET.iselement(item): + elem.append(item) + continue + for basetype in type(item).__mro__: + # See if the typemap knows of any of this type's bases. + t = typemap.get(basetype) + if t is not None: + break + else: + raise TypeError("bad argument type: %s(%r)" % + (type(item).__name__, item)) + v = t(elem, item) + if v: + typemap.get(type(v))(elem, v) + + return elem + + def __getattr__(self, tag): + return partial(self, tag) + + # Allow subscripting ElementMaker in type annotions (PEP 560) + def __class_getitem__(cls, item): + return _GenericAlias(cls, item) + + +# create factory object +E = ElementMaker() diff --git a/.venv/lib/python3.9/site-packages/lxml/classlookup.pxi b/.venv/lib/python3.9/site-packages/lxml/classlookup.pxi new file mode 100644 index 0000000..92d1d47 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/lxml/classlookup.pxi @@ -0,0 +1,580 @@ +# Configurable Element class lookup + +################################################################################ +# Custom Element classes + +cdef public class ElementBase(_Element) [ type LxmlElementBaseType, + object LxmlElementBase ]: + """ElementBase(*children, attrib=None, nsmap=None, **_extra) + + The public Element class. All custom Element classes must inherit + from this one. To create an Element, use the `Element()` factory. + + BIG FAT WARNING: Subclasses *must not* override __init__ or + __new__ as it is absolutely undefined when these objects will be + created or destroyed. All persistent state of Elements must be + stored in the underlying XML. If you really need to initialize + the object after creation, you can implement an ``_init(self)`` + method that will be called directly after object creation. + + Subclasses of this class can be instantiated to create a new + Element. By default, the tag name will be the class name and the + namespace will be empty. You can modify this with the following + class attributes: + + * TAG - the tag name, possibly containing a namespace in Clark + notation + + * NAMESPACE - the default namespace URI, unless provided as part + of the TAG attribute. + + * HTML - flag if the class is an HTML tag, as opposed to an XML + tag. This only applies to un-namespaced tags and defaults to + false (i.e. XML). + + * PARSER - the parser that provides the configuration for the + newly created document. Providing an HTML parser here will + default to creating an HTML element. + + In user code, the latter three are commonly inherited in class + hierarchies that implement a common namespace. + """ + def __init__(self, *children, attrib=None, nsmap=None, **_extra): + """ElementBase(*children, attrib=None, nsmap=None, **_extra) + """ + cdef bint is_html = 0 + cdef _BaseParser parser + cdef _Element last_child + # don't use normal attribute access as it might be overridden + _getattr = object.__getattribute__ + try: + namespace = _utf8(_getattr(self, 'NAMESPACE')) + except AttributeError: + namespace = None + try: + ns, tag = _getNsTag(_getattr(self, 'TAG')) + if ns is not None: + namespace = ns + except AttributeError: + tag = _utf8(_getattr(_getattr(self, '__class__'), '__name__')) + if b'.' in tag: + tag = tag.split(b'.')[-1] + try: + parser = _getattr(self, 'PARSER') + except AttributeError: + parser = None + for child in children: + if isinstance(child, _Element): + parser = (<_Element>child)._doc._parser + break + if isinstance(parser, HTMLParser): + is_html = 1 + if namespace is None: + try: + is_html = _getattr(self, 'HTML') + except AttributeError: + pass + _initNewElement(self, is_html, tag, namespace, parser, + attrib, nsmap, _extra) + last_child = None + for child in children: + if _isString(child): + if last_child is None: + _setNodeText(self._c_node, + (_collectText(self._c_node.children) or '') + child) + else: + _setTailText(last_child._c_node, + (_collectText(last_child._c_node.next) or '') + child) + elif isinstance(child, _Element): + last_child = child + _appendChild(self, last_child) + elif isinstance(child, type) and issubclass(child, ElementBase): + last_child = child() + _appendChild(self, last_child) + else: + raise TypeError, f"Invalid child type: {type(child)!r}" + +cdef class CommentBase(_Comment): + """All custom Comment classes must inherit from this one. + + To create an XML Comment instance, use the ``Comment()`` factory. + + Subclasses *must not* override __init__ or __new__ as it is + absolutely undefined when these objects will be created or + destroyed. All persistent state of Comments must be stored in the + underlying XML. If you really need to initialize the object after + creation, you can implement an ``_init(self)`` method that will be + called after object creation. + """ + def __init__(self, text): + # copied from Comment() factory + cdef _Document doc + cdef xmlDoc* c_doc + if text is None: + text = b'' + else: + text = _utf8(text) + c_doc = _newXMLDoc() + doc = _documentFactory(c_doc, None) + self._c_node = _createComment(c_doc, _xcstr(text)) + if self._c_node is NULL: + raise MemoryError() + tree.xmlAddChild(c_doc, self._c_node) + _registerProxy(self, doc, self._c_node) + self._init() + +cdef class PIBase(_ProcessingInstruction): + """All custom Processing Instruction classes must inherit from this one. + + To create an XML ProcessingInstruction instance, use the ``PI()`` + factory. + + Subclasses *must not* override __init__ or __new__ as it is + absolutely undefined when these objects will be created or + destroyed. All persistent state of PIs must be stored in the + underlying XML. If you really need to initialize the object after + creation, you can implement an ``_init(self)`` method that will be + called after object creation. + """ + def __init__(self, target, text=None): + # copied from PI() factory + cdef _Document doc + cdef xmlDoc* c_doc + target = _utf8(target) + if text is None: + text = b'' + else: + text = _utf8(text) + c_doc = _newXMLDoc() + doc = _documentFactory(c_doc, None) + self._c_node = _createPI(c_doc, _xcstr(target), _xcstr(text)) + if self._c_node is NULL: + raise MemoryError() + tree.xmlAddChild(c_doc, self._c_node) + _registerProxy(self, doc, self._c_node) + self._init() + +cdef class EntityBase(_Entity): + """All custom Entity classes must inherit from this one. + + To create an XML Entity instance, use the ``Entity()`` factory. + + Subclasses *must not* override __init__ or __new__ as it is + absolutely undefined when these objects will be created or + destroyed. All persistent state of Entities must be stored in the + underlying XML. If you really need to initialize the object after + creation, you can implement an ``_init(self)`` method that will be + called after object creation. + """ + def __init__(self, name): + cdef _Document doc + cdef xmlDoc* c_doc + name_utf = _utf8(name) + c_name = _xcstr(name_utf) + if c_name[0] == c'#': + if not _characterReferenceIsValid(c_name + 1): + raise ValueError, f"Invalid character reference: '{name}'" + elif not _xmlNameIsValid(c_name): + raise ValueError, f"Invalid entity reference: '{name}'" + c_doc = _newXMLDoc() + doc = _documentFactory(c_doc, None) + self._c_node = _createEntity(c_doc, c_name) + if self._c_node is NULL: + raise MemoryError() + tree.xmlAddChild(c_doc, self._c_node) + _registerProxy(self, doc, self._c_node) + self._init() + + +cdef int _validateNodeClass(xmlNode* c_node, cls) except -1: + if c_node.type == tree.XML_ELEMENT_NODE: + expected = ElementBase + elif c_node.type == tree.XML_COMMENT_NODE: + expected = CommentBase + elif c_node.type == tree.XML_ENTITY_REF_NODE: + expected = EntityBase + elif c_node.type == tree.XML_PI_NODE: + expected = PIBase + else: + assert False, f"Unknown node type: {c_node.type}" + + if not (isinstance(cls, type) and issubclass(cls, expected)): + raise TypeError( + f"result of class lookup must be subclass of {type(expected)}, got {type(cls)}") + return 0 + + +################################################################################ +# Element class lookup + +ctypedef public object (*_element_class_lookup_function)(object, _Document, xmlNode*) + +# class to store element class lookup functions +cdef public class ElementClassLookup [ type LxmlElementClassLookupType, + object LxmlElementClassLookup ]: + """ElementClassLookup(self) + Superclass of Element class lookups. + """ + cdef _element_class_lookup_function _lookup_function + + +cdef public class FallbackElementClassLookup(ElementClassLookup) \ + [ type LxmlFallbackElementClassLookupType, + object LxmlFallbackElementClassLookup ]: + """FallbackElementClassLookup(self, fallback=None) + + Superclass of Element class lookups with additional fallback. + """ + cdef readonly ElementClassLookup fallback + cdef _element_class_lookup_function _fallback_function + def __cinit__(self): + # fall back to default lookup + self._fallback_function = _lookupDefaultElementClass + + def __init__(self, ElementClassLookup fallback=None): + if fallback is not None: + self._setFallback(fallback) + else: + self._fallback_function = _lookupDefaultElementClass + + cdef void _setFallback(self, ElementClassLookup lookup): + """Sets the fallback scheme for this lookup method. + """ + self.fallback = lookup + self._fallback_function = lookup._lookup_function + if self._fallback_function is NULL: + self._fallback_function = _lookupDefaultElementClass + + def set_fallback(self, ElementClassLookup lookup not None): + """set_fallback(self, lookup) + + Sets the fallback scheme for this lookup method. + """ + self._setFallback(lookup) + +cdef inline object _callLookupFallback(FallbackElementClassLookup lookup, + _Document doc, xmlNode* c_node): + return lookup._fallback_function(lookup.fallback, doc, c_node) + + +################################################################################ +# default lookup scheme + +cdef class ElementDefaultClassLookup(ElementClassLookup): + """ElementDefaultClassLookup(self, element=None, comment=None, pi=None, entity=None) + Element class lookup scheme that always returns the default Element + class. + + The keyword arguments ``element``, ``comment``, ``pi`` and ``entity`` + accept the respective Element classes. + """ + cdef readonly object element_class + cdef readonly object comment_class + cdef readonly object pi_class + cdef readonly object entity_class + def __cinit__(self): + self._lookup_function = _lookupDefaultElementClass + + def __init__(self, element=None, comment=None, pi=None, entity=None): + if element is None: + self.element_class = _Element + elif issubclass(element, ElementBase): + self.element_class = element + else: + raise TypeError, "element class must be subclass of ElementBase" + + if comment is None: + self.comment_class = _Comment + elif issubclass(comment, CommentBase): + self.comment_class = comment + else: + raise TypeError, "comment class must be subclass of CommentBase" + + if entity is None: + self.entity_class = _Entity + elif issubclass(entity, EntityBase): + self.entity_class = entity + else: + raise TypeError, "Entity class must be subclass of EntityBase" + + if pi is None: + self.pi_class = None # special case, see below + elif issubclass(pi, PIBase): + self.pi_class = pi + else: + raise TypeError, "PI class must be subclass of PIBase" + +cdef object _lookupDefaultElementClass(state, _Document _doc, xmlNode* c_node): + "Trivial class lookup function that always returns the default class." + if c_node.type == tree.XML_ELEMENT_NODE: + if state is not None: + return (state).element_class + else: + return _Element + elif c_node.type == tree.XML_COMMENT_NODE: + if state is not None: + return (state).comment_class + else: + return _Comment + elif c_node.type == tree.XML_ENTITY_REF_NODE: + if state is not None: + return (state).entity_class + else: + return _Entity + elif c_node.type == tree.XML_PI_NODE: + if state is None or (state).pi_class is None: + # special case XSLT-PI + if c_node.name is not NULL and c_node.content is not NULL: + if tree.xmlStrcmp(c_node.name, "xml-stylesheet") == 0: + if tree.xmlStrstr(c_node.content, "text/xsl") is not NULL or \ + tree.xmlStrstr(c_node.content, "text/xml") is not NULL: + return _XSLTProcessingInstruction + return _ProcessingInstruction + else: + return (state).pi_class + else: + assert False, f"Unknown node type: {c_node.type}" + + +################################################################################ +# attribute based lookup scheme + +cdef class AttributeBasedElementClassLookup(FallbackElementClassLookup): + """AttributeBasedElementClassLookup(self, attribute_name, class_mapping, fallback=None) + Checks an attribute of an Element and looks up the value in a + class dictionary. + + Arguments: + - attribute name - '{ns}name' style string + - class mapping - Python dict mapping attribute values to Element classes + - fallback - optional fallback lookup mechanism + + A None key in the class mapping will be checked if the attribute is + missing. + """ + cdef object _class_mapping + cdef tuple _pytag + cdef const_xmlChar* _c_ns + cdef const_xmlChar* _c_name + def __cinit__(self): + self._lookup_function = _attribute_class_lookup + + def __init__(self, attribute_name, class_mapping, + ElementClassLookup fallback=None): + self._pytag = _getNsTag(attribute_name) + ns, name = self._pytag + if ns is None: + self._c_ns = NULL + else: + self._c_ns = _xcstr(ns) + self._c_name = _xcstr(name) + self._class_mapping = dict(class_mapping) + + FallbackElementClassLookup.__init__(self, fallback) + +cdef object _attribute_class_lookup(state, _Document doc, xmlNode* c_node): + cdef AttributeBasedElementClassLookup lookup + cdef python.PyObject* dict_result + + lookup = state + if c_node.type == tree.XML_ELEMENT_NODE: + value = _attributeValueFromNsName( + c_node, lookup._c_ns, lookup._c_name) + dict_result = python.PyDict_GetItem(lookup._class_mapping, value) + if dict_result is not NULL: + cls = dict_result + _validateNodeClass(c_node, cls) + return cls + return _callLookupFallback(lookup, doc, c_node) + + +################################################################################ +# per-parser lookup scheme + +cdef class ParserBasedElementClassLookup(FallbackElementClassLookup): + """ParserBasedElementClassLookup(self, fallback=None) + Element class lookup based on the XML parser. + """ + def __cinit__(self): + self._lookup_function = _parser_class_lookup + +cdef object _parser_class_lookup(state, _Document doc, xmlNode* c_node): + if doc._parser._class_lookup is not None: + return doc._parser._class_lookup._lookup_function( + doc._parser._class_lookup, doc, c_node) + return _callLookupFallback(state, doc, c_node) + + +################################################################################ +# custom class lookup based on node type, namespace, name + +cdef class CustomElementClassLookup(FallbackElementClassLookup): + """CustomElementClassLookup(self, fallback=None) + Element class lookup based on a subclass method. + + You can inherit from this class and override the method:: + + lookup(self, type, doc, namespace, name) + + to lookup the element class for a node. Arguments of the method: + * type: one of 'element', 'comment', 'PI', 'entity' + * doc: document that the node is in + * namespace: namespace URI of the node (or None for comments/PIs/entities) + * name: name of the element/entity, None for comments, target for PIs + + If you return None from this method, the fallback will be called. + """ + def __cinit__(self): + self._lookup_function = _custom_class_lookup + + def lookup(self, type, doc, namespace, name): + "lookup(self, type, doc, namespace, name)" + return None + +cdef object _custom_class_lookup(state, _Document doc, xmlNode* c_node): + cdef CustomElementClassLookup lookup + + lookup = state + + if c_node.type == tree.XML_ELEMENT_NODE: + element_type = "element" + elif c_node.type == tree.XML_COMMENT_NODE: + element_type = "comment" + elif c_node.type == tree.XML_PI_NODE: + element_type = "PI" + elif c_node.type == tree.XML_ENTITY_REF_NODE: + element_type = "entity" + else: + element_type = "element" + if c_node.name is NULL: + name = None + else: + name = funicode(c_node.name) + c_str = tree._getNs(c_node) + ns = funicode(c_str) if c_str is not NULL else None + + cls = lookup.lookup(element_type, doc, ns, name) + if cls is not None: + _validateNodeClass(c_node, cls) + return cls + return _callLookupFallback(lookup, doc, c_node) + + +################################################################################ +# read-only tree based class lookup + +cdef class PythonElementClassLookup(FallbackElementClassLookup): + """PythonElementClassLookup(self, fallback=None) + Element class lookup based on a subclass method. + + This class lookup scheme allows access to the entire XML tree in + read-only mode. To use it, re-implement the ``lookup(self, doc, + root)`` method in a subclass:: + + from lxml import etree, pyclasslookup + + class MyElementClass(etree.ElementBase): + honkey = True + + class MyLookup(pyclasslookup.PythonElementClassLookup): + def lookup(self, doc, root): + if root.tag == "sometag": + return MyElementClass + else: + for child in root: + if child.tag == "someothertag": + return MyElementClass + # delegate to default + return None + + If you return None from this method, the fallback will be called. + + The first argument is the opaque document instance that contains + the Element. The second argument is a lightweight Element proxy + implementation that is only valid during the lookup. Do not try + to keep a reference to it. Once the lookup is done, the proxy + will be invalid. + + Also, you cannot wrap such a read-only Element in an ElementTree, + and you must take care not to keep a reference to them outside of + the `lookup()` method. + + Note that the API of the Element objects is not complete. It is + purely read-only and does not support all features of the normal + `lxml.etree` API (such as XPath, extended slicing or some + iteration methods). + + See https://lxml.de/element_classes.html + """ + def __cinit__(self): + self._lookup_function = _python_class_lookup + + def lookup(self, doc, element): + """lookup(self, doc, element) + + Override this method to implement your own lookup scheme. + """ + return None + +cdef object _python_class_lookup(state, _Document doc, tree.xmlNode* c_node): + cdef PythonElementClassLookup lookup + cdef _ReadOnlyProxy proxy + lookup = state + + proxy = _newReadOnlyProxy(None, c_node) + cls = lookup.lookup(doc, proxy) + _freeReadOnlyProxies(proxy) + + if cls is not None: + _validateNodeClass(c_node, cls) + return cls + return _callLookupFallback(lookup, doc, c_node) + +################################################################################ +# Global setup + +cdef _element_class_lookup_function LOOKUP_ELEMENT_CLASS +cdef object ELEMENT_CLASS_LOOKUP_STATE + +cdef void _setElementClassLookupFunction( + _element_class_lookup_function function, object state): + global LOOKUP_ELEMENT_CLASS, ELEMENT_CLASS_LOOKUP_STATE + if function is NULL: + state = DEFAULT_ELEMENT_CLASS_LOOKUP + function = DEFAULT_ELEMENT_CLASS_LOOKUP._lookup_function + + ELEMENT_CLASS_LOOKUP_STATE = state + LOOKUP_ELEMENT_CLASS = function + +def set_element_class_lookup(ElementClassLookup lookup = None): + """set_element_class_lookup(lookup = None) + + Set the global element class lookup method. + + This defines the main entry point for looking up element implementations. + The standard implementation uses the :class:`ParserBasedElementClassLookup` + to delegate to different lookup schemes for each parser. + + .. warning:: + + This should only be changed by applications, not by library packages. + In most cases, parser specific lookups should be preferred, + which can be configured via + :meth:`~lxml.etree.XMLParser.set_element_class_lookup` + (and the same for HTML parsers). + + Globally replacing the element class lookup by something other than a + :class:`ParserBasedElementClassLookup` will prevent parser specific lookup + schemes from working. Several tools rely on parser specific lookups, + including :mod:`lxml.html` and :mod:`lxml.objectify`. + """ + if lookup is None or lookup._lookup_function is NULL: + _setElementClassLookupFunction(NULL, None) + else: + _setElementClassLookupFunction(lookup._lookup_function, lookup) + +# default setup: parser delegation +cdef ParserBasedElementClassLookup DEFAULT_ELEMENT_CLASS_LOOKUP +DEFAULT_ELEMENT_CLASS_LOOKUP = ParserBasedElementClassLookup() + +set_element_class_lookup(DEFAULT_ELEMENT_CLASS_LOOKUP) diff --git a/.venv/lib/python3.9/site-packages/lxml/cleanup.pxi b/.venv/lib/python3.9/site-packages/lxml/cleanup.pxi new file mode 100644 index 0000000..8e266b3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/lxml/cleanup.pxi @@ -0,0 +1,215 @@ +# functions for tree cleanup and removing elements from subtrees + +def cleanup_namespaces(tree_or_element, top_nsmap=None, keep_ns_prefixes=None): + """cleanup_namespaces(tree_or_element, top_nsmap=None, keep_ns_prefixes=None) + + Remove all namespace declarations from a subtree that are not used + by any of the elements or attributes in that tree. + + If a 'top_nsmap' is provided, it must be a mapping from prefixes + to namespace URIs. These namespaces will be declared on the top + element of the subtree before running the cleanup, which allows + moving namespace declarations to the top of the tree. + + If a 'keep_ns_prefixes' is provided, it must be a list of prefixes. + These prefixes will not be removed as part of the cleanup. + """ + element = _rootNodeOrRaise(tree_or_element) + c_element = element._c_node + + if top_nsmap: + doc = element._doc + # declare namespaces from nsmap, then apply them to the subtree + _setNodeNamespaces(c_element, doc, None, top_nsmap) + moveNodeToDocument(doc, c_element.doc, c_element) + + keep_ns_prefixes = ( + set([_utf8(prefix) for prefix in keep_ns_prefixes]) + if keep_ns_prefixes else None) + + _removeUnusedNamespaceDeclarations(c_element, keep_ns_prefixes) + + +def strip_attributes(tree_or_element, *attribute_names): + """strip_attributes(tree_or_element, *attribute_names) + + Delete all attributes with the provided attribute names from an + Element (or ElementTree) and its descendants. + + Attribute names can contain wildcards as in `_Element.iter`. + + Example usage:: + + strip_attributes(root_element, + 'simpleattr', + '{http://some/ns}attrname', + '{http://other/ns}*') + """ + cdef _MultiTagMatcher matcher + element = _rootNodeOrRaise(tree_or_element) + if not attribute_names: + return + + matcher = _MultiTagMatcher.__new__(_MultiTagMatcher, attribute_names) + matcher.cacheTags(element._doc) + if matcher.rejectsAllAttributes(): + return + _strip_attributes(element._c_node, matcher) + + +cdef _strip_attributes(xmlNode* c_node, _MultiTagMatcher matcher): + cdef xmlAttr* c_attr + cdef xmlAttr* c_next_attr + tree.BEGIN_FOR_EACH_ELEMENT_FROM(c_node, c_node, 1) + if c_node.type == tree.XML_ELEMENT_NODE: + c_attr = c_node.properties + while c_attr is not NULL: + c_next_attr = c_attr.next + if matcher.matchesAttribute(c_attr): + tree.xmlRemoveProp(c_attr) + c_attr = c_next_attr + tree.END_FOR_EACH_ELEMENT_FROM(c_node) + + +def strip_elements(tree_or_element, *tag_names, bint with_tail=True): + """strip_elements(tree_or_element, *tag_names, with_tail=True) + + Delete all elements with the provided tag names from a tree or + subtree. This will remove the elements and their entire subtree, + including all their attributes, text content and descendants. It + will also remove the tail text of the element unless you + explicitly set the ``with_tail`` keyword argument option to False. + + Tag names can contain wildcards as in `_Element.iter`. + + Note that this will not delete the element (or ElementTree root + element) that you passed even if it matches. It will only treat + its descendants. If you want to include the root element, check + its tag name directly before even calling this function. + + Example usage:: + + strip_elements(some_element, + 'simpletagname', # non-namespaced tag + '{http://some/ns}tagname', # namespaced tag + '{http://some/other/ns}*' # any tag from a namespace + lxml.etree.Comment # comments + ) + """ + cdef _MultiTagMatcher matcher + doc = _documentOrRaise(tree_or_element) + element = _rootNodeOrRaise(tree_or_element) + if not tag_names: + return + + matcher = _MultiTagMatcher.__new__(_MultiTagMatcher, tag_names) + matcher.cacheTags(doc) + if matcher.rejectsAll(): + return + + if isinstance(tree_or_element, _ElementTree): + # include PIs and comments next to the root node + if matcher.matchesType(tree.XML_COMMENT_NODE): + _removeSiblings(element._c_node, tree.XML_COMMENT_NODE, with_tail) + if matcher.matchesType(tree.XML_PI_NODE): + _removeSiblings(element._c_node, tree.XML_PI_NODE, with_tail) + _strip_elements(doc, element._c_node, matcher, with_tail) + +cdef _strip_elements(_Document doc, xmlNode* c_node, _MultiTagMatcher matcher, + bint with_tail): + cdef xmlNode* c_child + cdef xmlNode* c_next + + tree.BEGIN_FOR_EACH_ELEMENT_FROM(c_node, c_node, 1) + if c_node.type == tree.XML_ELEMENT_NODE: + # we run through the children here to prevent any problems + # with the tree iteration which would occur if we unlinked the + # c_node itself + c_child = _findChildForwards(c_node, 0) + while c_child is not NULL: + c_next = _nextElement(c_child) + if matcher.matches(c_child): + if c_child.type == tree.XML_ELEMENT_NODE: + if not with_tail: + tree.xmlUnlinkNode(c_child) + _removeNode(doc, c_child) + else: + if with_tail: + _removeText(c_child.next) + tree.xmlUnlinkNode(c_child) + attemptDeallocation(c_child) + c_child = c_next + tree.END_FOR_EACH_ELEMENT_FROM(c_node) + + +def strip_tags(tree_or_element, *tag_names): + """strip_tags(tree_or_element, *tag_names) + + Delete all elements with the provided tag names from a tree or + subtree. This will remove the elements and their attributes, but + *not* their text/tail content or descendants. Instead, it will + merge the text content and children of the element into its + parent. + + Tag names can contain wildcards as in `_Element.iter`. + + Note that this will not delete the element (or ElementTree root + element) that you passed even if it matches. It will only treat + its descendants. + + Example usage:: + + strip_tags(some_element, + 'simpletagname', # non-namespaced tag + '{http://some/ns}tagname', # namespaced tag + '{http://some/other/ns}*' # any tag from a namespace + Comment # comments (including their text!) + ) + """ + cdef _MultiTagMatcher matcher + doc = _documentOrRaise(tree_or_element) + element = _rootNodeOrRaise(tree_or_element) + if not tag_names: + return + + matcher = _MultiTagMatcher.__new__(_MultiTagMatcher, tag_names) + matcher.cacheTags(doc) + if matcher.rejectsAll(): + return + + if isinstance(tree_or_element, _ElementTree): + # include PIs and comments next to the root node + if matcher.matchesType(tree.XML_COMMENT_NODE): + _removeSiblings(element._c_node, tree.XML_COMMENT_NODE, 0) + if matcher.matchesType(tree.XML_PI_NODE): + _removeSiblings(element._c_node, tree.XML_PI_NODE, 0) + _strip_tags(doc, element._c_node, matcher) + +cdef _strip_tags(_Document doc, xmlNode* c_node, _MultiTagMatcher matcher): + cdef xmlNode* c_child + cdef xmlNode* c_next + + tree.BEGIN_FOR_EACH_ELEMENT_FROM(c_node, c_node, 1) + if c_node.type == tree.XML_ELEMENT_NODE: + # we run through the children here to prevent any problems + # with the tree iteration which would occur if we unlinked the + # c_node itself + c_child = _findChildForwards(c_node, 0) + while c_child is not NULL: + if not matcher.matches(c_child): + c_child = _nextElement(c_child) + continue + if c_child.type == tree.XML_ELEMENT_NODE: + c_next = _findChildForwards(c_child, 0) or _nextElement(c_child) + _replaceNodeByChildren(doc, c_child) + if not attemptDeallocation(c_child): + if c_child.nsDef is not NULL: + # make namespaces absolute + moveNodeToDocument(doc, doc._c_doc, c_child) + c_child = c_next + else: + c_next = _nextElement(c_child) + tree.xmlUnlinkNode(c_child) + attemptDeallocation(c_child) + c_child = c_next + tree.END_FOR_EACH_ELEMENT_FROM(c_node) diff --git a/.venv/lib/python3.9/site-packages/lxml/cssselect.py b/.venv/lib/python3.9/site-packages/lxml/cssselect.py new file mode 100644 index 0000000..54cd75a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/lxml/cssselect.py @@ -0,0 +1,101 @@ +"""CSS Selectors based on XPath. + +This module supports selecting XML/HTML tags based on CSS selectors. +See the `CSSSelector` class for details. + +This is a thin wrapper around cssselect 0.7 or later. +""" + + +from . import etree +try: + import cssselect as external_cssselect +except ImportError: + raise ImportError( + 'cssselect does not seem to be installed. ' + 'See https://pypi.org/project/cssselect/') + + +SelectorSyntaxError = external_cssselect.SelectorSyntaxError +ExpressionError = external_cssselect.ExpressionError +SelectorError = external_cssselect.SelectorError + + +__all__ = ['SelectorSyntaxError', 'ExpressionError', 'SelectorError', + 'CSSSelector'] + + +class LxmlTranslator(external_cssselect.GenericTranslator): + """ + A custom CSS selector to XPath translator with lxml-specific extensions. + """ + def xpath_contains_function(self, xpath, function): + # Defined there, removed in later drafts: + # http://www.w3.org/TR/2001/CR-css3-selectors-20011113/#content-selectors + if function.argument_types() not in (['STRING'], ['IDENT']): + raise ExpressionError( + "Expected a single string or ident for :contains(), got %r" + % function.arguments) + value = function.arguments[0].value + return xpath.add_condition( + 'contains(__lxml_internal_css:lower-case(string(.)), %s)' + % self.xpath_literal(value.lower())) + + +class LxmlHTMLTranslator(LxmlTranslator, external_cssselect.HTMLTranslator): + """ + lxml extensions + HTML support. + """ + + +def _make_lower_case(context, s): + return s.lower() + +ns = etree.FunctionNamespace('http://codespeak.net/lxml/css/') +ns.prefix = '__lxml_internal_css' +ns['lower-case'] = _make_lower_case + + +class CSSSelector(etree.XPath): + """A CSS selector. + + Usage:: + + >>> from lxml import etree, cssselect + >>> select = cssselect.CSSSelector("a tag > child") + + >>> root = etree.XML("TEXT") + >>> [ el.tag for el in select(root) ] + ['child'] + + To use CSS namespaces, you need to pass a prefix-to-namespace + mapping as ``namespaces`` keyword argument:: + + >>> rdfns = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' + >>> select_ns = cssselect.CSSSelector('root > rdf|Description', + ... namespaces={'rdf': rdfns}) + + >>> rdf = etree.XML(( + ... '' + ... 'blah' + ... '') % rdfns) + >>> [(el.tag, el.text) for el in select_ns(rdf)] + [('{http://www.w3.org/1999/02/22-rdf-syntax-ns#}Description', 'blah')] + + """ + def __init__(self, css, namespaces=None, translator='xml'): + if translator == 'xml': + translator = LxmlTranslator() + elif translator == 'html': + translator = LxmlHTMLTranslator() + elif translator == 'xhtml': + translator = LxmlHTMLTranslator(xhtml=True) + path = translator.css_to_xpath(css) + super().__init__(path, namespaces=namespaces) + self.css = css + + def __repr__(self): + return '<%s %x for %r>' % ( + self.__class__.__name__, + abs(id(self)), + self.css) diff --git a/.venv/lib/python3.9/site-packages/lxml/debug.pxi b/.venv/lib/python3.9/site-packages/lxml/debug.pxi new file mode 100644 index 0000000..d728e84 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/lxml/debug.pxi @@ -0,0 +1,36 @@ +@cython.final +@cython.internal +cdef class _MemDebug: + """Debugging support for the memory allocation in libxml2. + """ + def bytes_used(self): + """bytes_used(self) + + Returns the total amount of memory (in bytes) currently used by libxml2. + Note that libxml2 constrains this value to a C int, which limits + the accuracy on 64 bit systems. + """ + return tree.xmlMemUsed() + + def blocks_used(self): + """blocks_used(self) + + Returns the total number of memory blocks currently allocated by libxml2. + Note that libxml2 constrains this value to a C int, which limits + the accuracy on 64 bit systems. + """ + return tree.xmlMemBlocks() + + def dict_size(self): + """dict_size(self) + + Returns the current size of the global name dictionary used by libxml2 + for the current thread. Each thread has its own dictionary. + """ + c_dict = __GLOBAL_PARSER_CONTEXT._getThreadDict(NULL) + if c_dict is NULL: + raise MemoryError() + return tree.xmlDictSize(c_dict) + + +memory_debugger = _MemDebug() diff --git a/.venv/lib/python3.9/site-packages/lxml/docloader.pxi b/.venv/lib/python3.9/site-packages/lxml/docloader.pxi new file mode 100644 index 0000000..7b38f43 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/lxml/docloader.pxi @@ -0,0 +1,178 @@ +# Custom resolver API + +ctypedef enum _InputDocumentDataType: + PARSER_DATA_INVALID + PARSER_DATA_EMPTY + PARSER_DATA_STRING + PARSER_DATA_FILENAME + PARSER_DATA_FILE + +@cython.final +@cython.internal +cdef class _InputDocument: + cdef _InputDocumentDataType _type + cdef bytes _data_bytes + cdef object _filename + cdef object _file + cdef bint _close_file + + def __cinit__(self): + self._type = PARSER_DATA_INVALID + + +cdef class Resolver: + "This is the base class of all resolvers." + def resolve(self, system_url, public_id, context): + """resolve(self, system_url, public_id, context) + + Override this method to resolve an external source by + ``system_url`` and ``public_id``. The third argument is an + opaque context object. + + Return the result of one of the ``resolve_*()`` methods. + """ + return None + + def resolve_empty(self, context): + """resolve_empty(self, context) + + Return an empty input document. + + Pass context as parameter. + """ + cdef _InputDocument doc_ref + doc_ref = _InputDocument() + doc_ref._type = PARSER_DATA_EMPTY + return doc_ref + + def resolve_string(self, string, context, *, base_url=None): + """resolve_string(self, string, context, base_url=None) + + Return a parsable string as input document. + + Pass data string and context as parameters. You can pass the + source URL or filename through the ``base_url`` keyword + argument. + """ + cdef _InputDocument doc_ref + if isinstance(string, unicode): + string = (string).encode('utf8') + elif not isinstance(string, bytes): + raise TypeError, "argument must be a byte string or unicode string" + doc_ref = _InputDocument() + doc_ref._type = PARSER_DATA_STRING + doc_ref._data_bytes = string + if base_url is not None: + doc_ref._filename = _encodeFilename(base_url) + return doc_ref + + def resolve_filename(self, filename, context): + """resolve_filename(self, filename, context) + + Return the name of a parsable file as input document. + + Pass filename and context as parameters. You can also pass a + URL with an HTTP, FTP or file target. + """ + cdef _InputDocument doc_ref + doc_ref = _InputDocument() + doc_ref._type = PARSER_DATA_FILENAME + doc_ref._filename = _encodeFilename(filename) + return doc_ref + + def resolve_file(self, f, context, *, base_url=None, bint close=True): + """resolve_file(self, f, context, base_url=None, close=True) + + Return an open file-like object as input document. + + Pass open file and context as parameters. You can pass the + base URL or filename of the file through the ``base_url`` + keyword argument. If the ``close`` flag is True (the + default), the file will be closed after reading. + + Note that using ``.resolve_filename()`` is more efficient, + especially in threaded environments. + """ + cdef _InputDocument doc_ref + try: + f.read + except AttributeError: + raise TypeError, "Argument is not a file-like object" + doc_ref = _InputDocument() + doc_ref._type = PARSER_DATA_FILE + if base_url is not None: + doc_ref._filename = _encodeFilename(base_url) + else: + doc_ref._filename = _getFilenameForFile(f) + doc_ref._close_file = close + doc_ref._file = f + return doc_ref + +@cython.final +@cython.internal +cdef class _ResolverRegistry: + cdef object _resolvers + cdef Resolver _default_resolver + def __cinit__(self, Resolver default_resolver=None): + self._resolvers = set() + self._default_resolver = default_resolver + + def add(self, Resolver resolver not None): + """add(self, resolver) + + Register a resolver. + + For each requested entity, the 'resolve' method of the resolver will + be called and the result will be passed to the parser. If this method + returns None, the request will be delegated to other resolvers or the + default resolver. The resolvers will be tested in an arbitrary order + until the first match is found. + """ + self._resolvers.add(resolver) + + def remove(self, resolver): + "remove(self, resolver)" + self._resolvers.discard(resolver) + + cdef _ResolverRegistry _copy(self): + cdef _ResolverRegistry registry + registry = _ResolverRegistry(self._default_resolver) + registry._resolvers = self._resolvers.copy() + return registry + + def copy(self): + "copy(self)" + return self._copy() + + def resolve(self, system_url, public_id, context): + "resolve(self, system_url, public_id, context)" + for resolver in self._resolvers: + result = resolver.resolve(system_url, public_id, context) + if result is not None: + return result + if self._default_resolver is None: + return None + return self._default_resolver.resolve(system_url, public_id, context) + + def __repr__(self): + return repr(self._resolvers) + + +@cython.internal +cdef class _ResolverContext(_ExceptionContext): + cdef _ResolverRegistry _resolvers + cdef _TempStore _storage + + cdef int clear(self) except -1: + _ExceptionContext.clear(self) + self._storage.clear() + return 0 + + +cdef _initResolverContext(_ResolverContext context, + _ResolverRegistry resolvers): + if resolvers is None: + context._resolvers = _ResolverRegistry() + else: + context._resolvers = resolvers + context._storage = _TempStore() diff --git a/.venv/lib/python3.9/site-packages/lxml/doctestcompare.py b/.venv/lib/python3.9/site-packages/lxml/doctestcompare.py new file mode 100644 index 0000000..8099771 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/lxml/doctestcompare.py @@ -0,0 +1,488 @@ +""" +lxml-based doctest output comparison. + +Note: normally, you should just import the `lxml.usedoctest` and +`lxml.html.usedoctest` modules from within a doctest, instead of this +one:: + + >>> import lxml.usedoctest # for XML output + + >>> import lxml.html.usedoctest # for HTML output + +To use this module directly, you must call ``lxmldoctest.install()``, +which will cause doctest to use this in all subsequent calls. + +This changes the way output is checked and comparisons are made for +XML or HTML-like content. + +XML or HTML content is noticed because the example starts with ``<`` +(it's HTML if it starts with ```` or include an ``any`` +attribute in the tag. An ``any`` tag matches any tag, while the +attribute matches any and all attributes. + +When a match fails, the reformatted example and gotten text is +displayed (indented), and a rough diff-like output is given. Anything +marked with ``+`` is in the output but wasn't supposed to be, and +similarly ``-`` means its in the example but wasn't in the output. + +You can disable parsing on one line with ``# doctest:+NOPARSE_MARKUP`` +""" + +from lxml import etree +import sys +import re +import doctest +try: + from html import escape as html_escape +except ImportError: + from cgi import escape as html_escape + +__all__ = ['PARSE_HTML', 'PARSE_XML', 'NOPARSE_MARKUP', 'LXMLOutputChecker', + 'LHTMLOutputChecker', 'install', 'temp_install'] + +PARSE_HTML = doctest.register_optionflag('PARSE_HTML') +PARSE_XML = doctest.register_optionflag('PARSE_XML') +NOPARSE_MARKUP = doctest.register_optionflag('NOPARSE_MARKUP') + +OutputChecker = doctest.OutputChecker + +def strip(v): + if v is None: + return None + else: + return v.strip() + +def norm_whitespace(v): + return _norm_whitespace_re.sub(' ', v) + +_html_parser = etree.HTMLParser(recover=False, remove_blank_text=True) + +def html_fromstring(html): + return etree.fromstring(html, _html_parser) + +# We use this to distinguish repr()s from elements: +_repr_re = re.compile(r'^<[^>]+ (at|object) ') +_norm_whitespace_re = re.compile(r'[ \t\n][ \t\n]+') + +class LXMLOutputChecker(OutputChecker): + + empty_tags = ( + 'param', 'img', 'area', 'br', 'basefont', 'input', + 'base', 'meta', 'link', 'col') + + def get_default_parser(self): + return etree.XML + + def check_output(self, want, got, optionflags): + alt_self = getattr(self, '_temp_override_self', None) + if alt_self is not None: + super_method = self._temp_call_super_check_output + self = alt_self + else: + super_method = OutputChecker.check_output + parser = self.get_parser(want, got, optionflags) + if not parser: + return super_method( + self, want, got, optionflags) + try: + want_doc = parser(want) + except etree.XMLSyntaxError: + return False + try: + got_doc = parser(got) + except etree.XMLSyntaxError: + return False + return self.compare_docs(want_doc, got_doc) + + def get_parser(self, want, got, optionflags): + parser = None + if NOPARSE_MARKUP & optionflags: + return None + if PARSE_HTML & optionflags: + parser = html_fromstring + elif PARSE_XML & optionflags: + parser = etree.XML + elif (want.strip().lower().startswith('' % el.tag + return '<%s %s>' % (el.tag, ' '.join(attrs)) + + def format_end_tag(self, el): + if isinstance(el, etree.CommentBase): + # FIXME: probably PIs should be handled specially too? + return '-->' + return '' % el.tag + + def collect_diff(self, want, got, html, indent): + parts = [] + if not len(want) and not len(got): + parts.append(' '*indent) + parts.append(self.collect_diff_tag(want, got)) + if not self.html_empty_tag(got, html): + parts.append(self.collect_diff_text(want.text, got.text)) + parts.append(self.collect_diff_end_tag(want, got)) + parts.append(self.collect_diff_text(want.tail, got.tail)) + parts.append('\n') + return ''.join(parts) + parts.append(' '*indent) + parts.append(self.collect_diff_tag(want, got)) + parts.append('\n') + if strip(want.text) or strip(got.text): + parts.append(' '*indent) + parts.append(self.collect_diff_text(want.text, got.text)) + parts.append('\n') + want_children = list(want) + got_children = list(got) + while want_children or got_children: + if not want_children: + parts.append(self.format_doc(got_children.pop(0), html, indent+2, '+')) + continue + if not got_children: + parts.append(self.format_doc(want_children.pop(0), html, indent+2, '-')) + continue + parts.append(self.collect_diff( + want_children.pop(0), got_children.pop(0), html, indent+2)) + parts.append(' '*indent) + parts.append(self.collect_diff_end_tag(want, got)) + parts.append('\n') + if strip(want.tail) or strip(got.tail): + parts.append(' '*indent) + parts.append(self.collect_diff_text(want.tail, got.tail)) + parts.append('\n') + return ''.join(parts) + + def collect_diff_tag(self, want, got): + if not self.tag_compare(want.tag, got.tag): + tag = '%s (got: %s)' % (want.tag, got.tag) + else: + tag = got.tag + attrs = [] + any = want.tag == 'any' or 'any' in want.attrib + for name, value in sorted(got.attrib.items()): + if name not in want.attrib and not any: + attrs.append('+%s="%s"' % (name, self.format_text(value, False))) + else: + if name in want.attrib: + text = self.collect_diff_text(want.attrib[name], value, False) + else: + text = self.format_text(value, False) + attrs.append('%s="%s"' % (name, text)) + if not any: + for name, value in sorted(want.attrib.items()): + if name in got.attrib: + continue + attrs.append('-%s="%s"' % (name, self.format_text(value, False))) + if attrs: + tag = '<%s %s>' % (tag, ' '.join(attrs)) + else: + tag = '<%s>' % tag + return tag + + def collect_diff_end_tag(self, want, got): + if want.tag != got.tag: + tag = '%s (got: %s)' % (want.tag, got.tag) + else: + tag = got.tag + return '' % tag + + def collect_diff_text(self, want, got, strip=True): + if self.text_compare(want, got, strip): + if not got: + return '' + return self.format_text(got, strip) + text = '%s (got: %s)' % (want, got) + return self.format_text(text, strip) + +class LHTMLOutputChecker(LXMLOutputChecker): + def get_default_parser(self): + return html_fromstring + +def install(html=False): + """ + Install doctestcompare for all future doctests. + + If html is true, then by default the HTML parser will be used; + otherwise the XML parser is used. + """ + if html: + doctest.OutputChecker = LHTMLOutputChecker + else: + doctest.OutputChecker = LXMLOutputChecker + +def temp_install(html=False, del_module=None): + """ + Use this *inside* a doctest to enable this checker for this + doctest only. + + If html is true, then by default the HTML parser will be used; + otherwise the XML parser is used. + """ + if html: + Checker = LHTMLOutputChecker + else: + Checker = LXMLOutputChecker + frame = _find_doctest_frame() + dt_self = frame.f_locals['self'] + checker = Checker() + old_checker = dt_self._checker + dt_self._checker = checker + # The unfortunate thing is that there is a local variable 'check' + # in the function that runs the doctests, that is a bound method + # into the output checker. We have to update that. We can't + # modify the frame, so we have to modify the object in place. The + # only way to do this is to actually change the func_code + # attribute of the method. We change it, and then wait for + # __record_outcome to be run, which signals the end of the __run + # method, at which point we restore the previous check_output + # implementation. + check_func = frame.f_locals['check'].__func__ + checker_check_func = checker.check_output.__func__ + # Because we can't patch up func_globals, this is the only global + # in check_output that we care about: + doctest.etree = etree + _RestoreChecker(dt_self, old_checker, checker, + check_func, checker_check_func, + del_module) + +class _RestoreChecker: + def __init__(self, dt_self, old_checker, new_checker, check_func, clone_func, + del_module): + self.dt_self = dt_self + self.checker = old_checker + self.checker._temp_call_super_check_output = self.call_super + self.checker._temp_override_self = new_checker + self.check_func = check_func + self.clone_func = clone_func + self.del_module = del_module + self.install_clone() + self.install_dt_self() + def install_clone(self): + self.func_code = self.check_func.__code__ + self.func_globals = self.check_func.__globals__ + self.check_func.__code__ = self.clone_func.__code__ + def uninstall_clone(self): + self.check_func.__code__ = self.func_code + def install_dt_self(self): + self.prev_func = self.dt_self._DocTestRunner__record_outcome + self.dt_self._DocTestRunner__record_outcome = self + def uninstall_dt_self(self): + self.dt_self._DocTestRunner__record_outcome = self.prev_func + def uninstall_module(self): + if self.del_module: + import sys + del sys.modules[self.del_module] + if '.' in self.del_module: + package, module = self.del_module.rsplit('.', 1) + package_mod = sys.modules[package] + delattr(package_mod, module) + def __call__(self, *args, **kw): + self.uninstall_clone() + self.uninstall_dt_self() + del self.checker._temp_override_self + del self.checker._temp_call_super_check_output + result = self.prev_func(*args, **kw) + self.uninstall_module() + return result + def call_super(self, *args, **kw): + self.uninstall_clone() + try: + return self.check_func(*args, **kw) + finally: + self.install_clone() + +def _find_doctest_frame(): + import sys + frame = sys._getframe(1) + while frame: + l = frame.f_locals + if 'BOOM' in l: + # Sign of doctest + return frame + frame = frame.f_back + raise LookupError( + "Could not find doctest (only use this function *inside* a doctest)") + +__test__ = { + 'basic': ''' + >>> temp_install() + >>> print """stuff""" + ... + >>> print """""" + + + + >>> print """blahblahblah""" # doctest: +NOPARSE_MARKUP, +ELLIPSIS + ...foo /> + '''} + +if __name__ == '__main__': + import doctest + doctest.testmod() + + diff --git a/.venv/lib/python3.9/site-packages/lxml/dtd.pxi b/.venv/lib/python3.9/site-packages/lxml/dtd.pxi new file mode 100644 index 0000000..ee1b3d4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/lxml/dtd.pxi @@ -0,0 +1,479 @@ +# support for DTD validation +from lxml.includes cimport dtdvalid + +cdef class DTDError(LxmlError): + """Base class for DTD errors. + """ + +cdef class DTDParseError(DTDError): + """Error while parsing a DTD. + """ + +cdef class DTDValidateError(DTDError): + """Error while validating an XML document with a DTD. + """ + + +cdef inline int _assertValidDTDNode(node, void *c_node) except -1: + assert c_node is not NULL, "invalid DTD proxy at %s" % id(node) + + +@cython.final +@cython.internal +@cython.freelist(8) +cdef class _DTDElementContentDecl: + cdef DTD _dtd + cdef tree.xmlElementContent* _c_node + + def __repr__(self): + return "<%s.%s object name=%r type=%r occur=%r at 0x%x>" % (self.__class__.__module__, self.__class__.__name__, self.name, self.type, self.occur, id(self)) + + @property + def name(self): + _assertValidDTDNode(self, self._c_node) + return funicodeOrNone(self._c_node.name) + + @property + def type(self): + _assertValidDTDNode(self, self._c_node) + cdef int type = self._c_node.type + if type == tree.XML_ELEMENT_CONTENT_PCDATA: + return "pcdata" + elif type == tree.XML_ELEMENT_CONTENT_ELEMENT: + return "element" + elif type == tree.XML_ELEMENT_CONTENT_SEQ: + return "seq" + elif type == tree.XML_ELEMENT_CONTENT_OR: + return "or" + else: + return None + + @property + def occur(self): + _assertValidDTDNode(self, self._c_node) + cdef int occur = self._c_node.ocur + if occur == tree.XML_ELEMENT_CONTENT_ONCE: + return "once" + elif occur == tree.XML_ELEMENT_CONTENT_OPT: + return "opt" + elif occur == tree.XML_ELEMENT_CONTENT_MULT: + return "mult" + elif occur == tree.XML_ELEMENT_CONTENT_PLUS: + return "plus" + else: + return None + + @property + def left(self): + _assertValidDTDNode(self, self._c_node) + c1 = self._c_node.c1 + if c1: + node = <_DTDElementContentDecl>_DTDElementContentDecl.__new__(_DTDElementContentDecl) + node._dtd = self._dtd + node._c_node = c1 + return node + else: + return None + + @property + def right(self): + _assertValidDTDNode(self, self._c_node) + c2 = self._c_node.c2 + if c2: + node = <_DTDElementContentDecl>_DTDElementContentDecl.__new__(_DTDElementContentDecl) + node._dtd = self._dtd + node._c_node = c2 + return node + else: + return None + + +@cython.final +@cython.internal +@cython.freelist(8) +cdef class _DTDAttributeDecl: + cdef DTD _dtd + cdef tree.xmlAttribute* _c_node + + def __repr__(self): + return "<%s.%s object name=%r elemname=%r prefix=%r type=%r default=%r default_value=%r at 0x%x>" % (self.__class__.__module__, self.__class__.__name__, self.name, self.elemname, self.prefix, self.type, self.default, self.default_value, id(self)) + + @property + def name(self): + _assertValidDTDNode(self, self._c_node) + return funicodeOrNone(self._c_node.name) + + @property + def elemname(self): + _assertValidDTDNode(self, self._c_node) + return funicodeOrNone(self._c_node.elem) + + @property + def prefix(self): + _assertValidDTDNode(self, self._c_node) + return funicodeOrNone(self._c_node.prefix) + + @property + def type(self): + _assertValidDTDNode(self, self._c_node) + cdef int type = self._c_node.atype + if type == tree.XML_ATTRIBUTE_CDATA: + return "cdata" + elif type == tree.XML_ATTRIBUTE_ID: + return "id" + elif type == tree.XML_ATTRIBUTE_IDREF: + return "idref" + elif type == tree.XML_ATTRIBUTE_IDREFS: + return "idrefs" + elif type == tree.XML_ATTRIBUTE_ENTITY: + return "entity" + elif type == tree.XML_ATTRIBUTE_ENTITIES: + return "entities" + elif type == tree.XML_ATTRIBUTE_NMTOKEN: + return "nmtoken" + elif type == tree.XML_ATTRIBUTE_NMTOKENS: + return "nmtokens" + elif type == tree.XML_ATTRIBUTE_ENUMERATION: + return "enumeration" + elif type == tree.XML_ATTRIBUTE_NOTATION: + return "notation" + else: + return None + + @property + def default(self): + _assertValidDTDNode(self, self._c_node) + cdef int default = self._c_node.def_ + if default == tree.XML_ATTRIBUTE_NONE: + return "none" + elif default == tree.XML_ATTRIBUTE_REQUIRED: + return "required" + elif default == tree.XML_ATTRIBUTE_IMPLIED: + return "implied" + elif default == tree.XML_ATTRIBUTE_FIXED: + return "fixed" + else: + return None + + @property + def default_value(self): + _assertValidDTDNode(self, self._c_node) + return funicodeOrNone(self._c_node.defaultValue) + + def itervalues(self): + _assertValidDTDNode(self, self._c_node) + cdef tree.xmlEnumeration *c_node = self._c_node.tree + while c_node is not NULL: + yield funicode(c_node.name) + c_node = c_node.next + + def values(self): + return list(self.itervalues()) + + +@cython.final +@cython.internal +@cython.freelist(8) +cdef class _DTDElementDecl: + cdef DTD _dtd + cdef tree.xmlElement* _c_node + + def __repr__(self): + return "<%s.%s object name=%r prefix=%r type=%r at 0x%x>" % (self.__class__.__module__, self.__class__.__name__, self.name, self.prefix, self.type, id(self)) + + @property + def name(self): + _assertValidDTDNode(self, self._c_node) + return funicodeOrNone(self._c_node.name) + + @property + def prefix(self): + _assertValidDTDNode(self, self._c_node) + return funicodeOrNone(self._c_node.prefix) + + @property + def type(self): + _assertValidDTDNode(self, self._c_node) + cdef int type = self._c_node.etype + if type == tree.XML_ELEMENT_TYPE_UNDEFINED: + return "undefined" + elif type == tree.XML_ELEMENT_TYPE_EMPTY: + return "empty" + elif type == tree.XML_ELEMENT_TYPE_ANY: + return "any" + elif type == tree.XML_ELEMENT_TYPE_MIXED: + return "mixed" + elif type == tree.XML_ELEMENT_TYPE_ELEMENT: + return "element" + else: + return None + + @property + def content(self): + _assertValidDTDNode(self, self._c_node) + cdef tree.xmlElementContent *content = self._c_node.content + if content: + node = <_DTDElementContentDecl>_DTDElementContentDecl.__new__(_DTDElementContentDecl) + node._dtd = self._dtd + node._c_node = content + return node + else: + return None + + def iterattributes(self): + _assertValidDTDNode(self, self._c_node) + cdef tree.xmlAttribute *c_node = self._c_node.attributes + while c_node: + node = <_DTDAttributeDecl>_DTDAttributeDecl.__new__(_DTDAttributeDecl) + node._dtd = self._dtd + node._c_node = c_node + yield node + c_node = c_node.nexth + + def attributes(self): + return list(self.iterattributes()) + + +@cython.final +@cython.internal +@cython.freelist(8) +cdef class _DTDEntityDecl: + cdef DTD _dtd + cdef tree.xmlEntity* _c_node + def __repr__(self): + return "<%s.%s object name=%r at 0x%x>" % (self.__class__.__module__, self.__class__.__name__, self.name, id(self)) + + @property + def name(self): + _assertValidDTDNode(self, self._c_node) + return funicodeOrNone(self._c_node.name) + + @property + def orig(self): + _assertValidDTDNode(self, self._c_node) + return funicodeOrNone(self._c_node.orig) + + @property + def content(self): + _assertValidDTDNode(self, self._c_node) + return funicodeOrNone(self._c_node.content) + + @property + def system_url(self): + _assertValidDTDNode(self, self._c_node) + return funicodeOrNone(self._c_node.SystemID) + + +################################################################################ +# DTD + +cdef class DTD(_Validator): + """DTD(self, file=None, external_id=None) + A DTD validator. + + Can load from filesystem directly given a filename or file-like object. + Alternatively, pass the keyword parameter ``external_id`` to load from a + catalog. + """ + cdef tree.xmlDtd* _c_dtd + def __init__(self, file=None, *, external_id=None): + _Validator.__init__(self) + if file is not None: + file = _getFSPathOrObject(file) + if _isString(file): + file = _encodeFilename(file) + with self._error_log: + orig_loader = _register_document_loader() + self._c_dtd = xmlparser.xmlParseDTD(NULL, _xcstr(file)) + _reset_document_loader(orig_loader) + elif hasattr(file, 'read'): + orig_loader = _register_document_loader() + self._c_dtd = _parseDtdFromFilelike(file) + _reset_document_loader(orig_loader) + else: + raise DTDParseError, "file must be a filename, file-like or path-like object" + elif external_id is not None: + external_id_utf = _utf8(external_id) + with self._error_log: + orig_loader = _register_document_loader() + self._c_dtd = xmlparser.xmlParseDTD(external_id_utf, NULL) + _reset_document_loader(orig_loader) + else: + raise DTDParseError, "either filename or external ID required" + + if self._c_dtd is NULL: + raise DTDParseError( + self._error_log._buildExceptionMessage("error parsing DTD"), + self._error_log) + + @property + def name(self): + if self._c_dtd is NULL: + return None + return funicodeOrNone(self._c_dtd.name) + + @property + def external_id(self): + if self._c_dtd is NULL: + return None + return funicodeOrNone(self._c_dtd.ExternalID) + + @property + def system_url(self): + if self._c_dtd is NULL: + return None + return funicodeOrNone(self._c_dtd.SystemID) + + def iterelements(self): + cdef tree.xmlNode *c_node = self._c_dtd.children if self._c_dtd is not NULL else NULL + while c_node is not NULL: + if c_node.type == tree.XML_ELEMENT_DECL: + node = _DTDElementDecl() + node._dtd = self + node._c_node = c_node + yield node + c_node = c_node.next + + def elements(self): + return list(self.iterelements()) + + def iterentities(self): + cdef tree.xmlNode *c_node = self._c_dtd.children if self._c_dtd is not NULL else NULL + while c_node is not NULL: + if c_node.type == tree.XML_ENTITY_DECL: + node = _DTDEntityDecl() + node._dtd = self + node._c_node = c_node + yield node + c_node = c_node.next + + def entities(self): + return list(self.iterentities()) + + def __dealloc__(self): + tree.xmlFreeDtd(self._c_dtd) + + def __call__(self, etree): + """__call__(self, etree) + + Validate doc using the DTD. + + Returns true if the document is valid, false if not. + """ + cdef _Document doc + cdef _Element root_node + cdef xmlDoc* c_doc + cdef dtdvalid.xmlValidCtxt* valid_ctxt + cdef int ret = -1 + + assert self._c_dtd is not NULL, "DTD not initialised" + doc = _documentOrRaise(etree) + root_node = _rootNodeOrRaise(etree) + + valid_ctxt = dtdvalid.xmlNewValidCtxt() + if valid_ctxt is NULL: + raise DTDError("Failed to create validation context") + + # work around error reporting bug in libxml2 <= 2.9.1 (and later?) + # https://bugzilla.gnome.org/show_bug.cgi?id=724903 + valid_ctxt.error = _nullGenericErrorFunc + valid_ctxt.userData = NULL + + try: + with self._error_log: + c_doc = _fakeRootDoc(doc._c_doc, root_node._c_node) + ret = dtdvalid.xmlValidateDtd(valid_ctxt, c_doc, self._c_dtd) + _destroyFakeDoc(doc._c_doc, c_doc) + finally: + dtdvalid.xmlFreeValidCtxt(valid_ctxt) + + if ret == -1: + raise DTDValidateError("Internal error in DTD validation", + self._error_log) + return ret == 1 + + +cdef tree.xmlDtd* _parseDtdFromFilelike(file) except NULL: + cdef _ExceptionContext exc_context + cdef _FileReaderContext dtd_parser + cdef _ErrorLog error_log + cdef tree.xmlDtd* c_dtd = NULL + exc_context = _ExceptionContext() + dtd_parser = _FileReaderContext(file, exc_context, None) + error_log = _ErrorLog() + + with error_log: + c_dtd = dtd_parser._readDtd() + + exc_context._raise_if_stored() + if c_dtd is NULL: + raise DTDParseError("error parsing DTD", error_log) + return c_dtd + +cdef DTD _dtdFactory(tree.xmlDtd* c_dtd): + # do not run through DTD.__init__()! + cdef DTD dtd + if c_dtd is NULL: + return None + dtd = DTD.__new__(DTD) + dtd._c_dtd = _copyDtd(c_dtd) + _Validator.__init__(dtd) + return dtd + + +cdef tree.xmlDtd* _copyDtd(tree.xmlDtd* c_orig_dtd) except NULL: + """ + Copy a DTD. libxml2 (currently) fails to set up the element->attributes + links when copying DTDs, so we have to rebuild them here. + """ + c_dtd = tree.xmlCopyDtd(c_orig_dtd) + if not c_dtd: + raise MemoryError + cdef tree.xmlNode* c_node = c_dtd.children + while c_node: + if c_node.type == tree.XML_ATTRIBUTE_DECL: + _linkDtdAttribute(c_dtd, c_node) + c_node = c_node.next + return c_dtd + + +cdef void _linkDtdAttribute(tree.xmlDtd* c_dtd, tree.xmlAttribute* c_attr) noexcept: + """ + Create the link to the DTD attribute declaration from the corresponding + element declaration. + """ + c_elem = dtdvalid.xmlGetDtdElementDesc(c_dtd, c_attr.elem) + if not c_elem: + # no such element? something is wrong with the DTD ... + return + c_pos = c_elem.attributes + if not c_pos: + c_elem.attributes = c_attr + c_attr.nexth = NULL + return + # libxml2 keeps namespace declarations first, and we need to make + # sure we don't re-insert attributes that are already there + if _isDtdNsDecl(c_attr): + if not _isDtdNsDecl(c_pos): + c_elem.attributes = c_attr + c_attr.nexth = c_pos + return + while c_pos != c_attr and c_pos.nexth and _isDtdNsDecl(c_pos.nexth): + c_pos = c_pos.nexth + else: + # append at end + while c_pos != c_attr and c_pos.nexth: + c_pos = c_pos.nexth + if c_pos == c_attr: + return + c_attr.nexth = c_pos.nexth + c_pos.nexth = c_attr + + +cdef bint _isDtdNsDecl(tree.xmlAttribute* c_attr) noexcept: + if cstring_h.strcmp(c_attr.name, "xmlns") == 0: + return True + if (c_attr.prefix is not NULL and + cstring_h.strcmp(c_attr.prefix, "xmlns") == 0): + return True + return False diff --git a/.venv/lib/python3.9/site-packages/lxml/etree.cpython-39-darwin.so b/.venv/lib/python3.9/site-packages/lxml/etree.cpython-39-darwin.so new file mode 100755 index 0000000000000000000000000000000000000000..9c3769e59ffa7fab972038845bfdcd069fd6c5c1 GIT binary patch literal 9667432 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

Zta+)oG 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 z

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

    r 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

    9927))}qP#4A6}* z-2lK&Ge8bypN{$sx< zSU()GOEBZrA_Kx1Pj@o8^}`g~<4s3qG_ksK4oH)KWBondeGB}5Z2irh2h*{Ct) zBQbKFzNWA0S$$Q{KX6$F>=S+J=p6>URQlRq$= zv1fQ{hXS0y)WM=7QJWQrAq@D zQAX#Y$#p=X?GOA89u5VBg=GKosZc;zNcQbZ|BLb1Z~3N+WN^8ts%~&a{wAi*wgsRY-fqV z$4etpG!rq9=#QEcNPmT^7H1*%6$7~PBI?h$`nP4W`P(0CvN`xRW{lOxn(+W7XVdJq z?5snf@3Bq5WEzGs=>#OQuDS`2RiK8V&@bkTKU#)nelZ6Yi5s;@c=lbRoy~p@ch+uV zmoc0@1HZ3_sKNZcHVulox#A#K^euC~DC)->WcLmRm>k(1thxv(xubO`&MLNU3H|i9 zalP`2Uf)!31dEy@g_h=iUv(cz#P~>-I$uv8@DxzV_P`QN?noI&C3`&Bj7l1xY(^#9 z{#U7F%<2tLiA>ckspRqt>D>w&MRv0gpe7LW68fd7K+^$F33?{H2E=gA`50@GGr^ABZ7oZ-8O57lp%Wb4us&j2SO`3mJjBn;=x&0o3spjs+qpADTKa#zo3PM937 zi891QP{l$%A1!Gg2%E_B5GFF>Dg88CAFW$rQYg#*K6Z-eHOy9K z6w9rT3I@Nz>wPq>m)ErG)rO6?dj2xtvd~@GfnSQiPf5#pu*|<%3=w8lAdeXyR+cq?H)n9jcBKyuQ5)GWSvy zdrGa#5?{pIYPxW|kGNB^nj6Q+GJ-#BqyYN}h2{Qry9U8$FOni68=!goQ6_MV!Zn=* z9=Gl__lK)`i_BJxt#_d&!=A-$VnXjiqpAN|71|t{L0vwJ$ zvYCelqshC-^xWn}=9iK=8luyVcCK&HWKY1)Y-fGwu@8L+STp4Lq-K^1uVfoOL8`2@ z>ajAI1W4+JU0AA>eX{5z3^4{2T@M`C+g-dBUEis*z^&t7WnR}FvKRTWouf0S5A5~2 z(b`VmNX`~@E;4`U>`Q{G82S@bT^%BWj%x?diI@eyW<#uW7GlcM^e9DZ})Ar|Z!CO~Q z%K+NEkf-k_to&?=g#xO-L#gSKURxX0*J8h5ud2LKByXkz-C~^Cia+_ezl(WL#H*ds zM|cT4uj=o;t>%7{{F?qNwMILQf{>bAv?{!DBeiv1;f+46B4y5TTuD0`;??ZHj{vjK z!9z#J8itOB@To5HCzk0v7!FeCf{=_d0x6G2-b8YA@Gm#l7*fa0zp zGb_zD2@!am=_Wk!!a@QM@Z2HjI%+ZXz>Mh3IK-HWjD(%CIU{CHU!eC{wB?$$c zOX?aZzfG7+qCmM;i{UoYryiEShL37)D-AlMds_8%#@`J&=zKs;0RN6`Cl6`-8SF%lHH$0?>YUdh1cv_=cD)lzG8cYB6z&jpIZhP1{% zw5PiDt>;sK+i8sx9qTGEmC>x@Ia;MM@@=$S9fbpDSc&>?t3sDRSEp<6BrVGl_ ztd$1(V1_Zm!xXSGbNPe#U*BFXeQ>VE(k$79`e6H)H6s*lqMy7z_~I`bciGDy#>B)N zald|=>4W}1Z7ONA+VsIDHh?l`G4OeNdBL!92i-xmF0W)R9;^=l?@yBHf_v0J>ZS{} zmWtaf4=v!l=t_|+-F944w9|jP!T-v^>e`>gCdlCx}^M;8$sSXCl z-|0O2=do!Y?wFK&$D)%&>aP)4^P-94tY7(S)Mal8uTkG>z*^ZCg8S3^=}T~bIw;@h zRcdHLone=%8=s_aw5Fj2aIDab&`kNJe(a z6taaeoXDqe3{3&Kl61EobNWX#(M|-K+rK%1@A(@@^OvnZO5@Swdk83YpYdb&pT7cl zLlw_7N>riFG?&{SN)a^Y4D`uQx#|~nvU1Q*Idw9n(f5utb&~k*tS-ts1D^E8Hi`Op zJKL#AO_el2KADyo%*>2O;PZRZ5Xn@<{*5M8cM>F6%Aj1ppAwVTxjR(NCZ zIv?FRSXmOryCQWO^k=$5EES_KIdy*iICHcri1YQhrF>D9jiLPs>&+2H%izh2_0t-Zu2yAkWphy`N-_R-=loI=p3(_$&7 z_;_Q`;$}+vtgpTe`5dPyUgjfhVK5nnL~^+G9gmVozv+`XGRSH-81X zry8UI7KYi|D{ih42|<(XTsmhrqpBVw*6o5~hv-KBMq^3*$Tx5f{ ziZ!P^IHnCXVJ+dK9l`!uv=sBluOqvo??0OekVksbBi;W?F59(1wj?Q;D|kVb zJnA;6H(fk-g?_qK@#A0DD^N4{;hUfNgG6FOfF36qDVo_EHuAsHwwl}1Onyf~%-#Kf zqS@aO+CY9drR?8xygcy}dr3atO7UR5+R}a*=$oUyb;4zX`sS>&bNc2T?(zRd-`p>t zum3xJb3*k0P~W_QG5S6F=8+o9_4LhYb2SeX-KL+szByuzn=8_h{jn`EGwh?EX8Pth zE7)zeZ~EqqnDT#9-+Y4_LtQ1%H|t3H`X6EYv*TtvT+njvficsGi#AZed z-QXy}**x^?5SLV_8|9NZ$Tr*GDRnEx$(bLHL}&^Nan-dx{2WZxG0<~}1r zeRH_v>HoaGc?|oW%_qDAEv1>hxd%zj^v&&*5bB$o>7}{8`3ub3f~K-AsgS0YT2kbw zukwWRoBHNvs~lZBeY1SF(e@3Wn!dRsZ%CD85N1x_tT-3O2=vW(qjdp&^NrN6>62rR4!}{g~HirLi^vzFa{~mp_j$FU4 zZ|?H(2KCMFBfqL|9tkb{9)0s*+HKB$FPzt0-#pBys>f$v{EEK$XYz-NVP4-HuhewO zUTtmgs#$zX-|Vx`ujreHlfAjVxqm^mGI^T5xpOXCsBd;7CDb=Rz7gt87k7DIKi#_W z<8Qfjpl>eVn}5Ytp}r~fw9q#vvIX$F^v%77o4$DzRs0Tp^V`pzaM_@~89OPbZ}xMK zPT#Cz9h0+fo})!gOZ%qz3;*6-@ojas{I~3zM+Nk?!Kj+DYW#|QQ-^u}Bl~7qH6_+F zZf*u0+s61AEckN9&C2Q)yW*pz@Vgiq?23=!Eoa=!huGAe+BwVSIBooTqcAbC%YChO z)wIj~H%q-xmd$AP_i36dG>YqKn!7)3nr46f8w%(B`0Tv~T~HEeWy2KccgnWj17BBE zNrfh?X)dWyrAR6%U2^Cz7D#I{tKqs4faC8zAXHO7UG4;ew!;$!`E``RdQ5)`bwXJSdtFxww}=JM+LAt%!7kj+`|>Qn3iYLr^wPX@uezkd&OPUn z3OhI5k|H}6^dNZvApMGcuE#%CCOBF*WYiBkp-)t zXU&Ihk1sr7LpR0Z0vFxR8yt5$7O$KZw%}CQYY&+u(%rtQqwBWcTv_e3u!U6G0)KPi z$DA7WBrhTGiT$X>x%&3!N&dl8#k+st#gX1wjCDY*mEI^!exs%kkK;5BuT__OaoPLC zj@bK3qnnkDEN>_a_iHbD3pVzOo<4&gr+~>c7upAt=R?hy$A^h_@j~XY|Js9 z?T`9rzf4VOLvvz?CFgf<56OM0(33Yiu-=JH@X`!e#2-WVGRuMES92Urj$y7p*$H{W zUb#gZE{M|W_UNblgd2H&kN$QJn`hp~T+Au(vu(ryyrp5U1xW8d(Q<3|1Z0e&1i*^b z1qS$oO!Tz$H|VF-bjjD>+hFu(VXHVKOLucpMaQ&yK**Wi{PA0p*HP)*fJ(nQO&p&? zWAYi&{dY-p*`5rtHIicIzcAiSy;fRO@mp``r`y~7_zOD+%G4En^LOKo!w3NiY)IEo zPv%@1@AhQdB2=b&lNw9U?k{>y&(y?&l~|KbMd~Ae`5{uKu0Ka2TOn-n{yoj@ckVu> z=^r`SnNz0fkq4g)$N$6yA8X`M`^C0Nd@B@^Q=+aHA+=PZ{-(s764jV4P7;IF)HF zvwluxGHMNP1(?jiRp)qKcqM>=7j6SekgC2D5nNOt=E!f3kj>satnh!EVRAW9rsZ*o!mY zn?Rk=KIl!(4_?szK#f3a>ebq_kY0l>$5U_&h_+e!;NYqVoN87>K#7;){o$9BABHDJ zUXNsyS?+sfmb+Nw5rG$&v&4WyzgsCZ|2mzFbBjRb7FTh5bEq&v~&)5ZpBvGg(^kJ6Tq)!BWyaeKt<{YtRbI09yqwYr|Z!- ztr{Yc!M~8ILm$rqtNv9xRilk{Q3tu*-OX|lBc_%DApxC*zltKr z*>kLmBL`g&ky3qT40Xjcxpds1?};Y$)l(g-4%OJJ-VYd{_?rh!7rG!9q4=N23OUU# z+4%GXODgo>T&$#Y$;02+-0~(ytGe@m!@qJT%`R&*orN6G`T+SH?8#x^!GML>>T{?y~>Z{Wgo zCzaJ*0qofkFxhkfjVA6<1Nc!Sl~3G!lxR;OPKLs+Q$A!|Vh!9IE@DIdVM&=NBZuhJ z!XVX+qduhNL`0nR~>oXBfFniU^A*AExxkuY8qxFKVEGkxyfkGj?C<3gQycD`2b?e zMyf;@cW*>)r?`Ri7WDF@ODd$7yDcfQ&AS>^v$w~0z&)N*8->>+Mu|Yf+t{69590!3%4m}A195gT9H`7yz zc3A8_;0OatTc+MFQ#A3DeaX8ny=(7Bf^bYN(!u4vG>p=NhOO=%O}wJdIsc`|wHiU3 zueJUB0-rKERTE0KyO%n-kuKiqev@nq2ypBc=&O%3N;R?!4k&Gc)UX3e=LN-_xcS?C zR@9>?yMovAhK?{0*2syQ`&3uDWWkqC{;|ldn+TEmSMH!=SJ@#O@-I64Z6rl};dUWy&cDq8iHIue|4x=9#le!_{u>3rc=vvc!e?TsL$GOh-V z?!?e^of!H>CxwHXOm{)3nPj7yi-1dRb*}IC-)nup(dsCg!W(_xA46xZ?+;+i%se5f z*cxaH^|=k{-+infbZSG+nqFDXm^sEhI%B4sGIDyj`EIn-!+$o~`*+To(!(#>@cEu8 zAxm~Ea_Ew$rc`J2m+JiC9!>B6Tlb%?3K)2AG^~(;oyT{j|1waF;tmDSs%r}Nr)nxlIb9lu1x zK8uR+BA` z)mCVr8;U@Ze$W+^Sq58Ep|R+w=$HPPVRDneNzKodYi~lao%*MhC2Eps@cz+Z>I&B;Y76R6_lnd&*>+fZ zjAZ60n;i4Tb+;>7EhOScKK5Hl^JrqVisjlrmG&!AS!~&piR}5uH?*qc=k*qfrM@q1WRB6lOhA6_FNpK@RITxn^b*G&9j96`E5oa7l&0 z9B)aHHx{Xf&Tk?4nRJc~=u%DouF4IC+?@R*jP*;^R{U)f?>o#J)7=}oZEWs`+m&}% zkSRrFYMxa7-3OUV(P-g8ol=?((!_?T1(}mbWy%=5xNyoCd#7Rf#edp#S-~6p)&YxB zZpygqP^OFoQ%2o1P^ZHQtDtafx}V?9lGcuteCPl2Oc(4r)whwuh)JK8;oHw7f2iT8TRR*u7dcN*#_S(h@L`7FHUb>eM7a ziC^v3i;UCWe^TdrSm&MnaWv}zEzwE7nI5^?q?b4O^?*h@nZYvgwd+N#Lc-$;&8B;r zL#IEkhrQg-FEEbR$(;+pzp5Bd6$VuDd}NAr;qyr;1TOKcstwnF+u8arzNy&0l$`Xy zGMnRJ-R-VV;+IT~I(dgn$X=RzIu2~RR05mQU^dTft1E1MP~g(Lj1*Po#jCUXR1EId z9ukZuex$qpx5a>YC)H=Supg0JN2FEi8~*`Hr9KGw7A|`5GvlIrSeE*ScQ*zy9+pWx z^9FwaNi`u+19+mzDzK5!=>z{yf&od$*K9lY5ZKm-E030r&GrLY>LchlBkGx)Dqa)p zSD9G)*)*h5U6PB;xq!47?$Co)#Zu!JMQ^+j%o#D(=mYoWR(My>B_uRQ;oyUKy}vx> z=)l|9f&px7bDU*%N35It`z3O`@r&#xUhjyWG9KJHFShvm*r3MfopakSjwUY@IeVRm zbrAE8LqP65FV<8aOL22=(@W*4PVtT!;!|?<9D&hfX>9JgA+a4AW388}o*D(+aq#Uw z*TDN~z*_$#fW}iiO4s5C*G&hnnXc-zH@>?+=61XX2A7n+%BJ8hqzsiC@Zgt&>1ER( zn`Xnrckrs%?~6#gR+V0y*^8^6u}0iP`YfBhx;TBJ(z~p`ebF+z)H}R=&G+pu|2%q6 z4S)GM;L;pe`3#RjLrRV2*0yRXIuFmfj;8)>B=~8!NFXsX;;aK)Hj%U`l3iy~;(t?!~jw98Y1wd{KFVab!Yv@Kr@Q)Ze~l zUHi+wq=Fa6`mtGLl;w+VVvC`=bbPwM9VoSGy7GnD&Dav4sv<;Q&hEv2#iDYrqE}}o zu~{04w#3XE)Hrsy5}}h`jDuqL2IqJ0Di;}C)!+Z_HgN1k8ee9@rvF0ZtV>P!m<%y_BH_%*6`S z>s}p4$zk8`2=7`V(vQCM{K)#UF=iA0@hG?OWyX z$C@X=U*zJdv^!E1-ehC@{~Oq^sb;^1yTrJ*nm#q(XZla?uUYITIB{;Kr&G|~ z^D9?5R%qo)kHRPwT2AASqfI1YqkjHEGoS;IlID-YDwRvouU^8YMB*snC)^*(29~(n zX{!)vPA;v}5SGr?Un$-(=D1OeSJk0BROq3Uhr{%MHh{?C)Ih0;9OzH;bVJp6%jwp4AGO)IRM+rpCtTg_4lHHOTAFxPACmaJ#rF* zp!O#MX6hjJcmUe@3P}^-UOF~~`OuROxIN&&6j?adI`%};KJ+p9R9RnI$JElQV%M1> zAOF3f5=t*>Cv0u?1AJ&-H3ZFoRb{)4g~UDC(pSdV+>eHocX-t_HS_$`Zhz{XrbXu7 zz{+MGtAaKd*|VrzIt2xpN{{4@AeqY^r(3rCNQzpic$gP-$c{x-bzqAwns{8CtR=-< zNmN@>EU+XtZeFz{zP-hRX)_=2AlVgRiput3WcNE?>+bLThYkpEc&fu;Ze=rC$n{Fj zxJh^^zDI{YHB7-dJz;$&gCM7f1*LOdaPwU>F^e=7qZ|ihoCs{`m7FV#+75wG$v=hi z-52^Z@s2%Pk*qii>3+FZ=TmIC+UgT@p9te&xU2XlN?JscN%l%>Dzp+jLo#%jeL;(1 zRN^3ZRx{D0h{eJRYX#TITC=g#049e>>VGecXcUw3 z+tJ*xLefBS`1T}0O#GH*p@3|tk{>UUpwUh4s2Y~`1=5Zn86dj`Ksw86#f=8&w{;SF zMf>o^3h`s?FS%v%5rcEUh6ahzYF@h?d4t3VC8bMVTWadcNS3~J&oc1-FPmsRC5C&l z-c3*zKIKSYao$#%z6#U^GLD_=u;>7M=mrDu7zf}~-T<()5Wq+GqjQ=y@$XncHsl&) zRD9ysLX}jt(^wD6n9Z7ah5s-_Eg4*tQ-W_ZGH1d*Ml2<)9;OGdTFsZt0rv7EBio zc{dMZrl`)JO_UH^__mA{xkw{tucO^s8uvG%wka+3qZkYdRTbB@(2wd(A?jOJ%kLEa zS4aE0eRD6emF@;~K@rfHe(WdZDJdz;+xzZKem}g;ac}+I+uiQ1r+ce$Z=K!SDEHQi zH&eZCFt7sEYm#12y|#3lREOc`9BL5(Oa*>CQ`bT3oFDJ1$*|X373pSCBGrJ~)%(K~ zA#kP=P{3A+K~m|Z{8XgUAM%s!Y5Q5Zcn{A1-1j>Eim#Fe05X$MN;6*b2=syn0_WS&8W z7IyZHErAaeF&SMdy!%A#ChMP)Q#fMWRE$Q$9H+UZfWM`en(?)04xhTm={$bz9%u2` z3I$d>4@YIT)RS!xzLr;ey&kh>Lo{-icB7E8y%?sF{wNP-(13k7s~)Rq+m7E_o7ONO z;BgT;VWg5mbto(k#!!6$bw(3*X~f`?BkaZYG6t6%YA*>;=&#yWYY)@Ny7`}B%F3%_ zk?dj8#kk5xqF@X)ZcVwWu{^Za!+AWk(!=TggVd5rt^@$7c4zvxQ&EGxgFI;uRYyG6 zpy@ex_?FE}=K9P=Ai)&AlDg+Zh0ofHPjTg;*H+oh(Y*$j+)fTWsa-pm9l%0zhV@xi zi{vcV{TTvwIN2{Gd(~k)9HWN=c{o}RgZ!WO0ob_$bddke-rZY06T-O5a|>I*xV>OJ zgFgda%67zeXmH7v2E^P{dJ)ZG5kzfs-XfKJ{NXua{9!=m<$Zg(%e^eM7j0zv6V!ZC zL?{q?`eOj0!QMkSI%bb@3F){)a0dDz(~ zJ_WxoW*8U#db5+hX{u}8$=$SY6~Cp=t1t0hWGpGn=${y5EKSX42AzBad6{XdPA|jR z;#|l@jnKhq&jMbup0BJ8{;U@l0c3EG%j`wBEAizLdod3NX4S4xA(f^E@v*KQWmyIs zL5|FV4iX+seh0{whT?yznXPW~-8zNEfXsFy=7%aZxOe6mwRkUG;H9HAV)j#+xNP@^ zFV&#L#8*R70*e&JuH>$ZHHSOdnED!ZVJ^eMI>dINI_Lrg>!MMGNtq#x2v~aT3ZZj_ zL+88`zOhqU3p=~%Ru>?!j+*bs7;x-ca?uJP?~DKh5n~MCTcwB%?s2reC_D`>huh0U zy?~>I-7YA0VRs)(N7s~t~b5LNT>L!GYW=ycVGgaO|y0pjwkDIPc(6? zfX@qP9_xiyp`sx`@B^53k%3M;RioB;wGO-;*M%>;d)fOmq9bMA&f~4=E z23j-`8Wddak_tEG-f&6v1qJI}k_1(#x<8^M?)7=aG`SfpG*iTPY+7Xwa<76!BeKS) z0wRlfMhVemJ3=9X$IJP^B#zLLeu;ROAV300Xgwh+8{8wIvdP_ek!W(|Ggt6h*Ts0< zI3n{dGYoWk9GTr#n@#B9Jf0kgPRBgkczJ%FRg4z-hws9tnAf9+L88OZOuw4rliS*d zNIM5&Oy+=^@yz(iPVPf?roqk!Jd?*W|0i3y53lDwfKP%24kbD7za6W2eu*>{_~<)n z34zJS&unF-d(79W03G2OiBkSmW8jp@;-@FULqeyI5j3VNrH|B7h|5u1$|F0-aqnbp z1-{sPZSo5y-rRz9wd}neK%yO;n8y4^AU!wX^}b^A~(@QKnkXJ%{C>h*o%W0 z;UcgLU_eJCB@~uDe_lf2=W`wGOnrgOr`o-!w!-VTzkX6#EJRB|voj6(=GNPg3A!SnqqCXtx0_?7CVv8;K&haoAcX}gQGB2y(;2tBN5RGJl z`9LpICF2L#)7(U3yt7U3JqD7X={*a}E%V>#m(wS@`i;95=o8W8)|=X7*z~x>rSWcj zCwll;8DrtNRh=Y$A{oTOZc+Rf_12qhYTMwFd3>wt$Mimz2ZN~O@Af2!_A$N0-fylC z=C^FIDQm%-8}s6q_tvzrD!yN4f7r&~4xsZB;^Y^gkpVA34^!us+Eg8sy2X{^7ANtV zH)W(W2zjR7ZbWruXJpxg)A6&(?VlYwsA?YmcyIHaD@rN`HE^GcS3DuC=LF3|s$h3L zc&>teR1ogJ1?BhTb67rG1pn?&4bj9lR%#@UAR3)+KYW=J7Y**7?%TMQW7R?FC~q9g7boMvxS6>VWt1 zgY-2RlubA9{`w&)@MnPcLN;AsUka@pPpULG*fyBpe;RX?4&I#ly(Iq^rFCkOv+io$ z7E%jPSaN(m&b1Rw%&=62i@KjDZKj;er_8N-@$OqiVQ;2X3mRIoz#2N7hWzi>x%uT1 zE3UAt8&Jva9Rs}-s${!E*&9&FvbzOFKC^9j#cyJ7mP*D9)5_oDNEFv?z@(HPMJtAX z6yw&-Vu?+#a6la8M;8?NX}GkwaR)^cE3qN)^$&$#KX6}rV>oqRe?F?9Y&~Dm>}f3Z z7@w`>OmEF~f*}}}%$LH5ca!#V(WCOQ-ClnL_DU|}CQMORyPsCM4dM$C?B;HHB3td8 zPq?a24Br^8_IP|k5XAR5av;vombQNkRy~M^jr6b|4-q{K(9k@L(T|R8!!mC`xmPE2 z$?NK0Rd#qB`4#U`_YjGhO#y5G2S)lWeiVSDTgG35{QSpumMQ4Piga3X7R%Itwcb=w z2uBcxdA20+>u!p$w&A9*uly^?UNLxVpQ?ef#m4dV!tJg*7J}{=JgT~tds_tOZ;zxy z<^4&Fe`68D0P8#dTLw^bpE<5%s0w+cW?2Y~J5vhGDZ|)^?z)qB02cczGE)*g9l4;F zrWwoIgiqfAc5?7Lf4*$2&A8=pQjmO~f_1Zu`WmN=8K+F83}44=$?W z36@2F|FuTE)D?d+JI7{u9raC5)r?b_TIFx2mYi%gYusRsv(8A^E_jn7eH}&;*7F=4 zD(JBoJK#7@IGP@nIFWRl+-*ZOwM^sH@r+>9AJUw`37qy)oj+r6%>uEWGU1h+a7vDo z@-sd1>3ti|d_1+gsl#>vjs=-uX5qsu!ZwDEL)lYWx2e!`g;^gX?>(eBg$=5%y|99I zxx&=&y78t%|2)og=m#(!=5**SdatLKN0VpilH0%Y2X_TwAes=$Lr$4bOIiVL3}j|g zoGsRYIo9@3cO`gqxK7mesiovln$%@!qNA5BeLd7B@3ZgNfB7yHC+{F(&L-;LNg6OG zc^FnnF~4DR#G0MS3}yICOTV}a3yQ1iHd3)I4cHYp6pf@#9E#jxFVlqo6rx}Gsnvl93%OkQ%T)(;=Dge9yMzjN#-xyf=0GjKfO&i5wM9)&%nvtte|W;$F4oE)>zo0 zmVTIZ&0@NVwq!TGX`b0l^(u+=^h$=}g|87?_W;X)_bdua-F<@VdkM3@yVNiAJYA`u z@bv%jb|&yuRA>KZyBe2pgORH8%1Q#HfdzIVq zDpsx7R*hOKF0JTWTdW!bSU_!E(284K>NeM?EGpod|M&NtnYnXAZ2P|d_y6bfA$R7? z%vqlE?B_Yp@vG@XTR$5=jkoQgt5S_vDb6|@N2R>S9iv;{w5p@_@xDCw2XAYXcg{VQ zdZQTW^S@vdY5+Quf%(TNNL5w*VZONIP?YQ}@hqOXt+HriUPd05EZed~AMYbf7jh3v zs?Dyh1U7=a@3s*WFl8zZm%~o1u>YeL~;-=mY zT~XZB8|{kXrryD>s5MsoZmuY9>h*O+aZ~Rr$O!8%ZtD52s6AHK-(6AM)LU#tiNo#^ z*`jR{>z+vf6NP2YenM{X*t$#GKAOmpK-7nde%ao!TRqQ~O$;^8Q zE+&Bea^*7NzGIf87sscl#yF^NC#xZu1~WsNoW?Cf@~OAk{J`%`_rtPbl(ik89xfNr zu|u!kyEHtB97a)}Gc!i4@lF4!fqa;K$Fy)5D2#0l{mrL^>AR?oQ*>hYtGuN}nFjBC zDk7Jot;)E#$vJwafGeXVbN1v{@VURo?s@>CwJpYkT14Uv}QzYK>e|g=fXck1zd%T-nVjAq{2tVQlR{qu`K36x} z)uNI`_3ZUern9P+Jx$pet#oby59=!bl1x89vAFMz2M6AqMbu;4_3?eJ(mGcC9)kV` zF6&yBm0Of2*82HW;2_L-Z4hXFy5u*f$+N=kjqmSYc|#!N&ZIu9P6^V?$$If<{zxS3 z8MZjlT6AakslnHn6D=iT?`zqZ^S9}6C(u-fGe=8>VEqxJU#xZ3mOYt3i-y0xU5>pQ z+&R$6%psx-uX-sT5=7TZ)?D1k^BYfg-igVI4A{C3xoU^$RwvN{S<2-YLb|7aTDhK4 zZrD-aHN)vz+eJJzmHI4NC_Eb|RPtsibbWW)OlFRdQl}VB))EsZOtz*L;NDOK!t#2% zZBKdQhzVW1tvBL-bc);sS&KG~jyN~r-yw)AE>yI0^=@J|)x$54lFRY!{^4s^+lJ0* zjH1Z`)PBq>ZMsE*TU0N`w`7gbV)da3$9orRtW3^N1_&gCMO?&8^NRA4>gAg3%Okyp zd0uV#Sg*D}|I5a9HqG@H+z%QsgPQP>hb4%OoHmF~Hud3x!d3ou)K}x3dT{17&5kCj zai(7WepR9e-Jg3O>c1lM(BWqP*>t$yVQ%}=KU%E=4YD6ZCe59iD-;S_x8LFRh($K- zE!Dg04!~7@6Ynz-t8R`~-<58Of>ioMDx1G+P{1~y(GjugvS5tI#h_24(O2EwPMB27U1KQ-nY2{#U%0%EC-mr+O|A=L+{8 zBC$Pr&A!4Huj1id=o&H6Y1_8Gf?QtG=`b+Ks8FJMRFSL1lZ-2V*3tIE<{ScJ2 zzz?kBV4%BMZ`b?J?1FY@(@Y`>%JOCQ!1}AOX@3SUz+W)5CF?2Kn8gO}Dh?!Z@oKht zDC@$*NM_?q7LKUE>(AG@hfHbEB6Y4|j$p9Qc`kp*%RCNqmk^+?KrrO^Jl`7zk$8`o zcIjF45CZZndZRoNGk*$WHN~I6ARRyO&G{63cW+6>8$vg)gAP-6#^JaAlu|q6a12Ch z#HyF_@1wv0e@E(fKuUo3hkr8gUV$BNFZ%U)(Td(TCcKhw@`{8fq-=rJGSNivk0;=; zpM)4b%K$QhLpw&CC5Ys=*W@kT5(QevMsBnoKVMMu5_jGb$vbn~5u7-!}GTuP1x9iHTCzI_DR$xctDB435~y77_6yJK=KsU)b*mm8X1Y<5`5Nr;&P+2 zp#IFAT{{~tsGr2A9lJ&Tv?w0nvP5h6$}rXL&L>W$>wui{VVLwdL15Gl*~wNqgZ6Eq zBAGTeTva`TdB!`(7?5<6?B*$VbCTUW#*N=|2YECt)bT`r9p;aVl>}EC>+-+Q#M#>X zJ`6cNX9MP+%EW7Zej>7{BZAs<6DPc|O($Ve1ADrsdBlbNmQRbDZ4s=2EA7kuslFHKW# z`jy2fHh5>^{_eMESNL4zi%omx`vWC&pibd^TrI&1t?mVmlw^c;-cMI)v%7@!6P7Gg3lg>7Cf6g7@s@pK^z(+>JAd-Gk(<;5KT~j)eDYXnK zNW(So`DA7bDsJ#*m80ayjvJZXci^&-1IrA)Ud4c4%gqlDR5&E=*}zVC23LXf!Eh^! zEigcb$zt3^sL_Ukt zuvEiQ#qF|-Tmx~t>~vQYx68)5qV||wR;{93^==OummQ7=hV{1mJ7Tdxs=NX3oE$@9 zM^>4;VYp!QFuEULw217QUx^Rf`QP#Lt1`x&6IH)-ljX)b^-LS<%G+$L6S-roo5ja^ z5pIZZtPZCSm&DX5H?phQA}}fC{m~ioFJbwe+IEVj8~L44di(t6fVkj%IL{h#;w(45 z)kSQHk;?PXpUfcvW8(Ad14xkk%YkdB1f<@yWZ+6~!m7*@_Zh-WVx;$;@OSR(A)#wVfn;?<0Do zcxD&|r)y*#^qy~8Wx;g}_YLaeY&d4D&by3UVpY`xZxR4r<2#ue;Ox}HB+X3d;V3tW z;3ZxfVIr=%&O46HJuQm|)UY!3-!cMBNpFJ&d-!M1uv|67CJ(gO+CBs2d7WjdfvluC z&^A`UtEbBf_!cW**!!{ql5M9kL~Z=)#OZZ9FIiMz?fr(^XJ9n@$+v~s5h_q=!`apOdR3>E=VVWw;E|C1_ z1At^IEt@`&;R2IF$w@C{s*;&MFmL&GP2P7MyEH~qYSKJ@?9#QTiIJ6Fr=P-B`lD`e z%r2Lxke?`5dVqe4w$fi^36o~2GQ7t02&6#TbW^n%i+PL|IJor5U0EaPXF&Wo<@rNL#gbV zdha66@8OVa4sO0-X!xs^B}t(;1eV$)dY(J>6FO8h>;sk zdtSfvFMD`x#6NE~c=S7Fd&cKgJas)V#eg~I-PJM^lId%Cj1^y>_3N_#2$%6u2fPU- zL0V61NyqS5T2I5T;$wKK8E^42%s!NK@yYfVP%y^jeOu*ku1|`?UDlUbQJh`B>x#s# z-5P?a9t(iH|D-)ijQF|Z5SE}+c541^pojJqfi0Q&ix|`r{)8IOI{Qc(&v}zffL=aY zHhvxq8ih&IrC+CR+^_mVze15zsiIu)fWEMxM_gZ+?DT~#f}uKnVZ^C6 z-0McEucbS42T{F(IUngpqqcy8zA|Rs@BDLk&B9jxwoi}+o=Uq+YfX}u*ll5lS{}?z z(B)X{{VtR`5Zo}g0D|;4wBirb=Pu;d8A7uF8tWpi$RF9ceUK-&nezW$meLK1+3`xZ z>dQ|zSxR+CDjeZe-ZG|kiReJCxev4-`DxN!n1|9ii%){F7$eqFgky~Egvk+;WVzRh z&SOr7h*yYpF4Lo2b^rN>8M|YV2dPPyy#fh_=Svn+lRh`mxfXP`EKI7|X$%!-;~wlT zHR=0cQX$kPZ@hzD8`>fu_rIKx$#jDiM>#dqC|4OugZ@Iq%`p}ImqyoJ6P2b_nya4iTbr6Iaeyga;((WpWNiU)jI`Ks{P8F7 zt^qTn3`EkP9IuFgnz>g9Jv4glx4~<>QewH+(Y8Js3Nq#@CeG4Nzf<_}cSF~;!z`cU zncwn=Z7m5wAO>xTN++W```OJTSKP|P_SByGu0*B#eGS>vUUfN8v_z#{{4*^dodrKC z$vI+-@*}lUc+fW#W^3C~s`_(@euYMjVJZ7OQmV4TrmjlW%_@xYlg^KnDr$`>RpZHy zcx@l*+E}An03JAv>X~23o@rWuMl~1F=2R)mPdcY-ep1u-i8aC55?_DO{)J0**7?G? zK4nzeRiCN>3tM#1xeo9u=uN!SG*m82W|XJ9V_>}f zm_F4SzU9~kd*TQ(V7YC;aKYdCH2~(u`AG?POlx5z3`Qga4SR;F258=r8C$}YufQ~| z%cR~m4Tv&>0`viIT@}@zmkkx=EgE>DDO=yuPeIvw+Y>tIjLYTF0dirneu|W>KK5b1 z1Ab|Fi*`cC&ucq(;fzZhG>3${|EdO@N(W4}I;9MBH`VG9y`HNWe#ao#>3aW&J{AIP zTNo)G92DIICb_WT~S<>!N;nB7UAm(?n% zJfoSoa3sgY8!6Da58et?wqNs5e8B5>St*e9Dn3^)8T$>d25;o_hD3k@f}~~I$nzRL zM7}QT2IZ83{ZFugojA{&2%B=pJAiiXm^yw2oJ*Ox+yi2ZaQNIF#M_*0Y;t^kdjB=fx_zDb4@+<6j&9fCkcztfM-*V#N5Y*hoKaBG6(EA|JuCKsdV54BB8#R| zA&Chwk`O)o^YiWL)@qF{Gf;Bs=>n$jWbLxRU7W7*=v##BGg>u&LilOmdS0h^`#*yb z7`}9e-wV^-;rH6l{y*aP7-l>~um3&#-u2f17{8yCGch2r|2zCX)}R^U_s@<^{NA0Wy5M&eH5h*X zD)`Ld_X}gK+y5n3W3Kcu?&$W}AaT^~m(Wzf?{)5|=Q}Z?+ddF}-}~19zfZgTU*q@g zpME!fS6RCo;rIE!`Y!yw2g?0U{C;_Mclf>g+8BOowLaqC{dH5|-`Cr-z|$D#-^uiJ zxzS7^-Z$Xe>x%hy^@va*9*a56=ynM zCjPBwZu@u0xzn3y^i>1una!qw9S4c8r`L+ZN^iX^WXQ1jssV#|$JEAYiBN6SIr2Q| z%af!nL9r}dCbh=$lCv|n7q-<|wkkZ}NBWWKb2&}cXDd#$dm?qONdYcOR`dQe#7|+IhQtSfUSCp)#tYmLLOY?<;5nnLv6n)*wCctKt)^$atm4 z%+w$PJ9U;dmTz3=58gnA#U3o*6X z=h@4ng{>dlxQ&d5>;c%|c_n=E|Mp32l*!ET04M(=LFjC%l|QaK`8aQeI!?z-|I-SG z@_(=W*!V}gS=pxYh5DZ&%VYfG8hNF|m5PPM`rm?Nx}^I+hIV@80vQH7wYI(9)LJMS zJ|f?LvpN#9R~I_D)z}!VS~z6-zc6@PhXPr7Riy9Psza#njRiA~FzAKy@RBh`b>ppy(lfY&GRq*h zNU+RU#{|DF5L#4$a*;$_W+5KCRt-Dmxs!z~9`hjWu(Pykz)iaN0T-G#j1rLM2Vg6R zFy}HVBWmG5m0h?6j;O3+QD7?jF-M9#3><s_=i|Vh0XcSQu ztr8rd_uPEg`oK)q2RftmftAtvz;gBUU#<^qT38rB1ZOOvph-A{g$~^b4n1 z!1?`?~Jxiwp}O8F8v#KpykClBqFqYA9V!* zvCDNPAS!SUv^}dXKe9wV$9Jjmf{PuSuu$$Nz%-1pE_(eV@A3&DDFUMWK>mymskJLk zdlkPOC71Ec34U&(I9TK1wEUZ&Tg#7ZO3Ux-+ZtL%Wo^X>AR0?&lYA!k%9zpSh0f}o zM?TjokpxT)+4W7E`@5|NQoXt65k%5S z`V%0ywZEClw+KcOMDEPd^I-FH9{L6SW6KCDDPaKbTx$dP65E?7i2LVcRYr{Ep)d00&()NBqA2|dJnGt+%v1=-KioYj>XG?G{ee8m zpP_fga^@Lw|{;lN~yJrfBMFM=b!%iTBj-efALS>HaC!_W8eixhWT|G z{vY|L5tg%tO_}TBkCJIyJGRa6y_z~Yc14yFKJksf8)_neF9y>YtvIQHZN(n@f%#qX z#VHF$=W`|~ceKf{eEpj-&B;s)&r(F=>J+CX&;mk6!eW{K_FB-+t9W5wu`*h6A_-iz zdM++h`LNYsRsOL_RVueJRa($;2?Ej8cdhEnPK0|U?%$a{qL+}YuASTY?smE2?}=qE z^DcFvHw$NhvBTFb8{SmDXjQM44>nz>wf~mRo{eQg+gmz&v3YvYTicc@A|TasEnz#o zYP^fehp$^YylJ;ZpY&>Zf76HUgKv8_ZZ-5t@}RRHVbR;$&VEu~Y_=$so3Wq`jO*e5 z=|s4a)Cir_CI2obOqIzaie+bPwW*xcedrv2gL^1XpD&Va=O$#k0q^{sE1C~Gb%!{K zM`W}*?*E)e`PcidM{vy}r!bprWo!a$Pymb!Cj}%6h!LQCCi3<;r>H9R_cU1B5>-0F8xzBz4EH}}| zLPNc3%cw{|q98i`KC4y1YmJuRM}GBn{@Q=SCJ@oW@JY9c5^mG(p z()z+D`|5rF4T0`+Dy8i0pzJA??ZSIZPMwF~7wsm~!`1!>{=|#T$hk;QF6K|nzg*1! zXMZ%`Aefl^(?|qNK?Y2L-P@l9x@wK2U@kFof0439Qe(v*KFIFOft*VWA7FPDw}OSg zm)&Vak2g=YXSK~-9Ev6cW+L^Tv(~0rQz!lV5B!6z0K*y5lEjFS3jh9FjP3D$J z%LsTPc0dK39y3fo*^B~^STEH=V|~`*6Jq`utT6M>v|?*%)zZ|Iz{Y#@@2W_&sT91) zU3*0N-wcMa{BLTBxd9tota=!g_7%I1%;u8=E0R(r3Le^w7rU_H-8bfYNmsNW#tOggun*&*PkC3=9((I0R|GgB9=b$DEDv01 zJam(NS zt99m#Tdz(ubrkl^2`Iz`Vfojxy`*U&9)NbXZ0`R9gu3Ek3wt2IX+t?Laa(nPh_f!p z!tFtGK`>Q`vp(Dxf^|C}vKFlf(u)KdYr^cjr9pOHC30!1WK*T>6 zFLh|mqx}FIthlKp?a(JtUfc&w&szKkR#)_4GObgEY+31M zqsr`KZXnge%Na;Os@V@|`A}LG%k}W|VOsRIz{pU{j2(r^-JH^ zfXHg}ad1Qxr&&S8={t4%y60XOS8}hr44wn7|FMXmcV0m* zMF2^ye>4Au3B<%(=ZKRGpM;&+ITp3k;9&_fZ37*=W~RLn`mp}^eN&o7jSwd8j_jWHlM5f z(LC&!=1M!x_lE}+oJIv5&jv8}_YO+6#9s60_I%nga&5A#rfh9;LgEt{g`*fGzzR^( z4EaZ;j!iS>Z+Wr(cj>YQb)nVLUjxZimtAg5Z!64hWevIbL9x6p?Dq~ZlcKoVIoB1% z+3yTj6lcHZxS}}wJ;4>l+3!dzO0=D+nMZl7eXgmWywtOwkOC81iS@_wCUSM#4V|5v z^S_hb>7VgG&;K|0{{sKx>+Qw*P|HUXz@7;~E3x>2DL9bdUP)|mWCx#O2z+9h9B)@2 zqnpAC)ZJzfb+ZD+auO#H5nH%?JMC`1(_*-~0#h17m^a+9{f|kQVno*l2O6HBq`PcM|_# zR^+(b?=7fCr!yh+aBaIt4Hrg$Z-}^RS&thhLVKAnEJwO=qXTUQDvoF0RyXe|!lMw$ zsomAc$e#}uqZ%zfK8r3VGs^o;f${OigN=djZf!As{B9ubAv$ovZlvK!fv>&7qC?(i=vgMpS`~xr%1Y#ymRs%&BHqx=*j>Q`qGN+%ZK1 z;2_Zy{Y-V`s<)qMeBles%eEcqn1AZ;gSF)C&QL#srK55KQqj^;7f6Y>zb$s?CHh!b zhEKu>VtY$xS^jy-{V(1W%$!{(6Rl&s|GpKu5KUQMxM*?fcI?mewbFXysjdcp5bx!$ z;sxnH77;a%o4n^fL0l4q%uag$pugLgM$E7=?Wr;CtTB1N!)r@e%uqNKj_ix{@s9A7 zwKd7I(Z*b}*p`0&A2g$`6(mT*tjKUg`G8S3%$G~_hnJb{IA3gbsVuZ9eC6NtcK7R9 zX91Jtr#Y?{#gDdAIQ0bzIb%Vb=>)Ewp(c|`Yg>^?6|QX+dFcLV)Dc=p5LLjcsJDxU zZb0`Qx>f+-%%ihr+8|*Y6LFLkMUZyg@6sOHLlTM6npbOR47v5~XuXT9l7;;_=JD)m z39SK;W(mny47nK*P%?uU$#=w)T2Di}M|)YtW@a58`bWd_>TMyst3Ph0FgXUS2!g2E zv`8S5;~&$Quw-U8gR=1t7(_L9^)Lt`-&5YHP%~@G72j^8OUaKc&Y+X2oMNPEA|7!V z*vxnDTb8a7;#DvFsL<93$E)JXrfu>yUbT;rp?H;&bCB@G8xe7;GVjQ)jOtmx^)=HM ztUD`ha+epSPCIi~|=bp#a|v${R_%rUE1_p?vmzSNl26Wqb9E`TB1dL9AfM8zXaq(01|`BL#EK(>DIiI&o@y%^RKzm(xOh)x=z)* zP95Mn^*Y|6s8cCAWo(2Q+>CGW_0j=5KMrxCh=kGo>%C!yEE7ud&!DHtM|;%sa#yX( zzW|K=Va$)1$eTQb^|7f)>@9sX&uZGrsZOV|lbTEE(elUfhqf}O!Ww7Fz!?RI^GfRA zUmR3=fnEtT)lTa4SMrJ)P~>S%+iujTW>8RF<|DKG3+(QcOB^RaTW)vkZk~VMG~;#V zC%ay}ZZE1rD;;5sa{@NCzu;y1af6!MSCcN>KZ1@(LUOQry*K|Y+QkU0>HND)IjqFt z?oOALn2GMrj*k(Do<7WC2nvz6>>#bU?f74=^Y|X_dDXP_Opa{ij)0+5pVVE zMS+CS4r?UDcf?!Gw@h!Y6aVdatEHd|p$D%6!zkYBL6sp^nn;!%X|7cR7V?h8Tm9)R z94Z#3{|C_=xw6>$OZ)@RlbJvAasDbpLR5|wLA=yI2Z_&>uF#K*m-^*N0sBnMhK}HR zm+GewFV${e^m`g6G_>7c`J!RsQB8)4qcG*$-pBRn-}f7fm->x*>?O7{Oejg^j={HA zhI&EN$)aA0L+~noi9gpI9|KmaWh~budmrA_HSFXL$e4B5b9u?1P@*kE5OP9WC8YIdJ>M$fabICDPKzb}sj5SvN9g&`2FK79!qZ2D&r1J%qQ)KQmf9fR zWz8Ng;rHWh+~GlcO{fIRI6eabaz!Y7PX1v4y^0t2G6`}A?8`-H-S>h~qR1|pUJm)& zqR!)7_>ZlGq?9v}*rz{pwjm?O&D8%hTfxs9WbYFDr2R<(%GG-P7JXRpS2;KtENTHd zM_#Ff3aoQ4$2tiQ2H^34sQoW=0Z~`OAM^5Z`4>=SS9%_YnN|JioQ(#hZ z(nQk2FY&*8+CkNo6x+MAKbqqve)0r?gYkq%xX)#ZhCivfwR7(F1_0H@fPdmkQP)r=<&_{y8r>NQdsY%syUXx`H9R(%CP8WKWQQJw1OREr+Cg+wmdk?l90u_uKGCS}zzl(K`Xvm8N!ApPvQnCyaA_j+kLP zh2x%}&@sbdTt>`L{4ZjL-NwW8DPo2wNZ)B~ujI{c9D@aGyJg>q6Ee&qYrCs|uvOFZ zpYp$M?dxmL4fvl{_zL`whW}&x`d{wXL>#(Zon84~JIq9$FSouGi-*zK=yC9c{BOCl zzs)^(FT2pYEKdaMA##N{6Lpl$S@j_184FAN0K#AIzbRrBeqy+a;!uHn>W6Ry zMeJiYS6AEuI8>d?Re$_rqsyD|2eh#-u8055FT-Gto5niywYWwNrQ2YRlZv`>-7YqY zJzQ62a7S16j(6o3PXPi8=4eNk5jFi;>>^ivy6ed{>Pb>PInnh5uqG{!w4EkcAl1i} zjoOiFduS()I=f&eu9dE%6Mg**Jgrz?ar>zk9_p0)-5+Rb{gPnwu zrFs1nHc$WNC`UoLgn5>7r5QPxiZ)L#vk&{7c7Yi_Z!80Eor`};N!05Y51!aga*7-N zzHbjNacjT*z-*M0dC9AHWdL<8v&zRNGuv|6xjdU_<}p1Q{4_B--X9i~jbYR$wAUoJ z+Jyh3hU?D|{VCU*iI=&*=Wd4AplJY ztEzih^?Ocz86oZscySZ5MtS>9?1K)~kiX|#GbTKyb7Rt8od?dPe>o_ZGlCu#MukHX~}vgiMlnZ^p6rvkEB zWzU>mx9rMhRm?xBQ70^SYSdO5#(Z_lo7k=@3;v9sTPZG4#89Kuu=D*{6m^^xvd~xS zgPE*2c1L4A51b#m|Mp&5V4;C8WJIjGplFDSa@AX$X`FByyhm*V0Ehp|4IwAI=19jV zj1#V~zIPMWc1+L_$CR@>SZ4;(8Rq&l?%;&G;yQ{}Kd$-<7&Xz1kj|m&YC3NmV$pw#JoW|OBjm2H~a`4(B)+L?xA=EH4xLhJ;I z+RFXJ#b}^%5p+^&MRg>sgA$TNvKw|OAzQ4QdZ&yC@5^jTQzgmk7jt(Cck`2q*I(~?`5wf9%B8*Q-XK-@s9$XZD} zzJ8jOV0UarbI|z07YKyc(Ma{)f@V5yw$>Weo2x#k-(EfZ-#`xlD{~aU!a^bS)X)_* zoiDK>=HI|0dw-w+X(?zbxKdH(W-jsv>ki-z5d??Tkt0$OH_XIinqJ)x(|w(H^1$IF z1ClPL!r>}x@EQlEg0sF=m{OsQx^MBQ?-aonk^gnBUokk$wi2dc8+yd37q8H$M;8sc zzZ>-7+cN0;Au|p7D$3wF@jnLY|J?;Va&VIBgXv9^Ba%(2dXw#J!Tum{Kyk-%ui9)Wz6^S$j02tGaK^*T-cZ& z;v(Oh7PK_5f*T>ZY;{BOF)1UA{A(2W?=Q9n=J;iHs83pn5)pq|hr&{CHUTxGRmfP5 zGBw{VN4G3tq~yXkg8h}6mvcUG!MBo7r7KF%1|VK{2$*)OB<}xIPs}yL*)(MVY4=E3 znhRQNPGuX2)~vq&pS+jN3 zxesN}XXc84*D^$24<1%*qop=GFMUqnwYYl}fJq;tnaNF@U!$M^EpVXKv~u38SOzN) z?Hk(F1zeT2b8MPz17SfvxtTsOHq#JYGV>!%@z1b)>RQ&5v6#p~pz|sY+=i_0 zzsjhjA35X!B^qN#mVBWh2Y6wNS?PlTMFFw;Cew$x0+WLZ0?dr0Ab&nkmh!XERnRO* zTJJK`FD9T-Vy@_`wdxAU#Kdhm@DaL=u7u0Ryl6kC z8>CC;?N&NFU%;YOH%&PE`fTqGPXPDOJUPThUR_O7xXJ5S4I@VFuGYg`X)rVENI@nE zpUgbP1c5y@ZOY7&%=8A6HC3g&o?j>EbJN4VjT9d@b$10*_s-)5tPp~w_uo=9TCpY2 zl?ULA^?|~&!9cVurke29_k{Pnb-3Qj27FY)5dx;pX$*k*J1Jtq z3WKNoj@gOL>YI^{!IOg`KJbsAgI2_G*c$^Ch(6da4p9!rQmb)@_OKxqjo$(F1Ju^CtTcd+eEa0U~=1;_LiXLOoEh32;G9;J-{ zz2g*d9pg0Iv^GQyjvwAtcpY{s1Z4uJ4%Y-;#wQUqbeX+*nmu<6V|{K$Kn^YRp}UzQ z_DBaNe~waQ`=@H>$PkOs!5DQuMh8r+dg5koKljjNTx{mjALFXk>Y1SIm*^#rjnm%3 zxu7Hw(qlE0oJ+5mvjAp4y+&b9E}3oi0@;$$nd>z}xRkP(%GM0yQYs>s!@0DIvNfr? z+>CPnwqJbSS+gvo83#jpS;ze!Zd4hKmqoReP=UQ;3x#uy;cYpU{v8a9rNOp`v& z)-Q-EMvH5D$$}_2*$9D|OiDM{G>HDY_Hgy^VS!LhMItQH4MvoMBJ&{%ChoAUV-r^- zXRdw3Ce8){YjRX{nJa7tKGNMpQ0(9loe6@c-Vi|$Zp*>(C9!F&(WkL-=N_^-iKbC= z;Xk%Y(`d>Ii=Tr+#KFt5kNl#{Mi|t8Z?#5Q5{!~~-t5Fy;LflR4JMo!-6>nS(G{uo z3;%2bGs+gKJ5;=#QfZ^<8BBhJNf-ZzxHI|sXygEcr3f^c0C(mUaA*Bt>T_BgCO2b# z;ZX9r&S!)=xAI_UySBM!Z*cf(=1jeJb{VFi;i`PLA* z^55f6z(a*m4t@rBL+wzH2nFUEa%e%kh6iv0`Iww;}0 zLWs1>1#1zcYGd&qem>_2fJB>fVu5(3auSPB`6pTmrR0wGp7Au8l^y=-uMf};7kv6_ zbw@o_Y*QbpMgcPvvSl`-Aioka39R>6y^KBHwztM~ON))E z*^S8^Ky%NgZa^z&g)Q98ctTmwZkS`E(TQl;f2Q1dHVo~QKWb<;&nDGw<|C9_c!e9; z05>vIbS>gbLrmDHBIK}vm1OzB?q6-^bX?$=eP#hoJCe*3HC2epkSzCleASp?VxU7BSArWH{7k`=} zgh{A~6#SbH>VenIFk?L0{}$ZIi&_F#6xxc}RE6<{lh_S*9(b%_=b`uz+P)|3>@nN0 z6KcCbN@GeVB(X^Cmk{+KlKYT>L2_WHdmbWbD*KRlQ($v}tUu)rWPRYj7(z&gzthJS zP3JImYG1MbznGSs9!T?=rL(g?Z!#t5#y1{f zQL=#&#U%0iD*tZpb7XpA@xd`?_U{c?I(c$FBaE%2I}0e^OCu)#sC_dGtIOY#9t98I z6Tk5iuM{4x`aLiSvsx6)s7J+w{=f+#HEk9T4G4cnQCG!ntSN41a)P5Ec+-Q_C5nC# z(@Og192G>o>SN+NWr0@h_dtas=l$mC0&?E(MqNP8tNgfDEw=_1TX66<`x{QhSgl*(U6y-)X{>)GS~=9ViBgfrS?i<%NiC1ZS{n?(|7A z-D8a^B=pUT7<8EA>r?-8zzac`Oo7E09=skszyyjvSQ0%jp-}kZp5Q^t%n3ajck*{W z_A3}u^?T#Be$j6xCbuG9M_pzSule20+Fm0aw14QG!N*+2^$-I%N*@Q6=_=X96BFD{%&66ASi=B2OS#xU9HWL8vZ%NK$aHUq^fW8 zdvGvP&@0?fvc_rH;Q+`io4lw?!Wl)TeBj?JLJt&f@27)g;yIs$stt_rd@Hs4f3h|| zi*>aw`wX9I;Q9MMXSQ+(+#KxNv3GyXZ&YF3b*J3GJN{{a3Mua|LtEHpOZfM~JIIZsh1Yg^SOVbS-t z!i5mc13ZL^xh>Sm}2oz(f*rw zK6kSxHU6$4lIPV0AqYyJ9)%z{$G?k`@}%aTZE&-CcMSZ?$=zNhpClpRrlQYFtT;fz{fI-NVEt zcJ9HtK~>^0o+Q)b)pakifdkxFIC=XXoQ4*ksenmVhkTVI*V0ewUJ;14!>6ZxD~P0W zB)LFgt9gu0NOyr%E?`gTLVD5;jxN_6xfP0M?}oCZ0%6S>)E6GfM}UfRBMO)wOjH!@C4D)xx6mBsdGbpKHs$5WM-;$6gwhU zBY`Z5Hzv2UQs;1>p)A|Z#T^6U@#d+PC7}LaG{vIpEGL+d>Afz z$4oZ>DfpcBw_&in+G{!_UlyVX2?9jSH9F|{YR<5EsCBe+|ykDL?BeQidx2TR)5o}lmKRWxm*^R ztLa)k&7Vh<_#&u`mzWQEG=EHmVB*Ci4HNs~gCW=6_EUox+h)CVbVo|&dyU={?)5^@ z3-^XlV5?JKO3}_$SGYQ!!ZqM>lRS5!w61q;ufElk@V}Io>D&Tt4|5ln=z!iel_%{5vQnZbUk!+-2ojLGMZO|lqaGIOK`HI_eZ1X{Q= zro>ykOT+3BT~r?`zYExHD978U{+lSAu2hwOl|PrkA-nG0OS0>C=9lmEuehDmf=Yd3 z$S+|-AMrzIuik>UC^wJI{glIi>X!Y5kzq)Q^&~Tv0rE1{3jPzoVpV82Dx790Z!NCR zox6ZDsoYcX7)9f~noRdoby@bz7fg~D{2|hhv#-WD$I4eC&hZ(-4lNkOfN>m1pNkmB z=P(FJr|~$NY7Ur&cdMAj^zDpcTuiTJs{fT2jTad8@+jg4dir~ws;#|F>JzMHqa<$VmRmxB30~ z3gdG}7}(*{Hrc_j^L>E};-HY9xI6v1|3F9nQG9M(fBx|ek^Vf^T{`_) zr_e_F^Av8N`{KNk7Q7q9>Bcm#V*Pmzt4pX4|5AV6HJA!^6gA@y6Msj4b~c20$54jF zv%Bia|013~t}oX%>(j2vat(8B@w)PTM#{1~PZCveJVX>|$<~E>XH`bZvQ~v%yuA9^ zO}?>Ezjqm$@KT%de45y=HxV|9VtIjX#7|`;=bLrcT~scb^qyNVi2-TNi$&!gv)WYY zar!B!(tY>PjO$3F&vCWDDOc&INR=LHANISm&Q$3X%sM~Zd>c_zZqbYm@KmK)-mQTu z{ronjHs5nAL~d&H*7P5**&@n~>FmZmmVOZ4^VIHQTSsb)PHmnOs?E%`7^u|d$?Z;E zZWWnGn%AlhIE|Os`>#@$!?>o2%l%P$2SE=C7U|1%UY|`wf?f1wMewlxL5NG|b5L{? zzB5%6n_U9?3H9aRDHve-@>~9n(JRvYSoe@U={&J#*cB@DPmDyE(?(rX4LHe$d!UBv zC2si*Fw$^t0K}$rRxW~z+QVxn*q4MZ+9B88g=HLnIdjM8{OXZka{l#)`Yt;E`WUpD z6#o<7+bx*qic1b3B$0#{dB!ghijVFILLpR~H%m(9dVj9qm8-tL%B1f>xIdIVsOlVZ z1qmH9Ptr>MdH#gD{?hWW{=+{p`FqjmP}kp%X5h7PUH<^%ZP;LN@<1oq*%g89QPF0u z8-rXo?!(jB)}PM$51#FOQa_`8G4r=?ma@+oewkk*I}twqvsThKnYrEy*86P~SjUx5 z#DCxhw;8`o6?l8TO5a0*2?;K94e|g(piScnkXhunHyWF>w*!5qZ$Y2wD{an8JhH0; zg^No5^dkeunWIb{`73u2Sr(8+>d3lzP?WL})MZzqj{M;XQ%7#rfnd3QxA5cd*5K3; zJ-&g*esjH!z*F2Zv1iGQA~%??&PtoF7CcF9npHo0mIKrvp`LeCVWU=0Y`H*5f$3hs zyI=%Pt^BX4$)SXVETOQ$>i0~jo@9CFP?10Gx)5#nD8Gn77bInKZC6P-$KT`oCMoAg zQtm1Nwn}iyJ|!`6V9NWsI~#g&)2fc4AfJ>j$JhPMXcSe6R_eWn#Od?j@Hu+TZ3AQ~ z?PB02bA~s3P^xA6bc&Ql-9P6>%{3(S6n7{OPV1j8EG(2RwMm_TuW@=g*ze*k=An*t z!PJZ^ieu`9t|*SFr&>|sm7O*7rad=JbDR68$2|PlL(QlVFt9iM{eLbX?oAJ;AXysA zl7DK45$(oys0F6AaM3J-JJZOFri{9_{=0?iHNyA$kj|JcUz-5ATikni*2`DmJf)<*LEM~4LRT?se3$altI z^4)q8iusr`%zFB7$f7 z94PXOrF|3TSLml8%xiWEm~djyy(Y|e*H4i!Kf^xk_d2KLAOSAD-VmPtL`?U3xDhl! zucD&aD#}&=p~Be9KpZD+`vOIO?=vuV+vN}2LG1<``^QOE6`PEqkhpEfpnnxRxNeN6 z8^Qixm4TDEG^+Rcra6;MF8axxhIs=Ou1=kb;4@>FXkIm;)aIrl73+H4=??zpY z2R0)2ta=?u>9UhK?sqOM3C+g8k#Dg0J0{k9mrL2O5a$zg;ibqM|K!X8wL@2g{^`p4 z=g>FyU}EYz7dKQs&w1?LvFI9g{7?L`e>l~fA7V!emcg|Al53PqA(-cA@oI^`t814B zhKk`D)e?V`=z$QDBZm zdrNUWMY8vjYr!q0rj8|)A+Ok%{xdo2sk}A~jL_tBLCoE3HL^r!+*}F5=J5u_-5flxCA18k8#mEdF6e&$|aT( z{~6Ea%=7?r$n4*<{N`l3nGl12$@1?AfXR}`1W~zL5)tXwyXYLJZ$;}K{~dlGoig3k zq&ttsw<-; z2zlWTq(zmtw%6xI_Fzv&;*@`yl$fL+qyX5lYrlXj6Pw`ih8lY4N6#F^)Y=#Q$~fl< z@o@6hZ2OC{I={1RNYW>{w>~^Pi?_3^hWW=zG|#-SZ&(Lm>P}6vAc$a5 zp+0-(SkTgK#~<41pAO4u+es`!I`DKVQIL+duAxuYY(sZ4*C4Y5EJDQ&Gny>}npe7RYk*UEbhMhVESdY1dhygNXb$k@R8Oim`IQH` zZ3rxr6uGdsnF~e|NOIV1fh<%&omuZkmDN=qRX+0!OZePg=N*rf-uWrc!>G$*+hjk& zSxJiMWJdHT9s5bpb$Ud-`xwBd&!^lW}djh8I%xnoUO8-df$4R%Z!IDn(p zFTcvrAY*C03R>Uf{S!@(q)- zuO4c0*8hje**jZfa(3&VA@N?tdTV17nvBi-=Xm;Tu|qe$Zno!1{taEp%Fn;Z6}b5x zEhW=uFl>!rv8S$q!Fv!8Xx<)eyqWVBAJn-7coH8{!7{_`lgZ71>X?G$KE(PMm)y<% z<%0^Y+@tW)=ZDf!wRZ6ph}=v&xYRfl18wMQCNZvL=8J(|fGM7Gp62i14~S6w7HdY| zhYGu(`#_2`4>QA&?Fob#8Fgy@LY!*cOS?cJ*6K%-Foq%d4*-w8dlw8Xyw>!J{{jW! zyK{LrnR(P+oh&b<7zIYzAknzMsFY`4VAZk)Z{R@I7|U3D))3EZs9b;HYYjcuA(9@} zszZHpS?;Z8v$=7QNpmm0d|?eo0o4_|P`w)i$%r({!tPxng+o`eiBB z8>=#`;3?U7{$^Mz9RYJXb_)kZD*k@?iIAM@B+r5;n4m2iN|IL&Rf|Iu$18>|?uEswF`gR?eK0#~_xY^g%(->z_4}#~7eQUgNB^>F{m}vR3Z_^PC zl}{(rmDc}Piv0g5>O;&{d4U0W$M8wXw2qqO9V@23*RdmcxP#}7ZNalu_AD=;6(Ey- zA53SnSlyy``_#4kwWMR8X#BuyO!nmR4M$~nE(bkQ-cEHbPq4ob?u+kJ$r*(quYe&; z^}+x{R;Ly^1QCWTO15@Tja9)Q4zfY)#~|i5cqa{MfXi*~;8im16MM!<^K{LPU*9*i(`79= zrrYU@VStv%us-L&oo?My+^L=dsVRHK@rV3-lS~os>0g`d4TU_>Q2BK-t$bKIu5Qs= zeORv_1f?dZjMxsejALa39+U>ntqBq6gG`P*eOjwx)A+Gh+ESO|PaFHnaWr3Z)yzv& zy1s@tUYsLu?P@){D&%&bp9ia$Gw@=s^FRA!YpEMI z(}eQ|Urvq1oD4ZO$GX@th?4;`AB#d!hx}J&)Aj2#z#^HX*ZpsJX1Q8X_h#&A>fVv9mBdys&h$w!kPrxOu;BL9!#{|Nra;Z?LAI5s=}MKV6Hjnz9J zDgpZ?AX;X;*rV~Znl^FKj{XMj!g`{;rIDbQi2fS9bM<9ovc{_w2R8azI@PbKWFKwQ z^Ab@*{3}P{SH3r@E5?4W#*6vevuVeI6B29TD5ftkZewAPH>@y2{zm;^tk+=U zql6!ua7uVCQHgH|WBwrh@Vu^*=C>V4P)n12*zf2nv*)jx3G2{0X}2acH9n^r<=hE& zjn=laZwd!eEQHrw-wZ>&^gCz33$t!BsRp_o;jeC$^sM%31BsbT`otq#?X;7XZwZfS_^|`5}y>X$#(p$3=n>)7U{KZ1A>hNBA z6Pv@00?EwYLSau!L#2~xB2{@L2C2eT>MEZ|=9bV9hTeOszpgjiWeKb#Nb=_Cv?(!N zBnQ&Styoiv>|EEZ(Up;oMis!_f)f`J;Ybx>FxZCmm`s1f3tB@q_|-htW|Re-ehUgX zm+afK41%)YOd-EY$PpY(q!XoByj9;Ol&6AS|89qnmOZb(EO1fSC&6ZFMW#KEgrLi+ zllH03?IW-d7&jm7h!6iB36ei@g?-a$`$n;{gTAUSnNN9^&&X9EsO7jgGS}z*{9h?{ z1N3(Sp@Jdjx1%|Kb79z)8qBf+=~Y!s@2!bHh>jL!7VT-XUKC%yB$=KJO0c$B_b0i} zVZ+meqa+~`n z0jBO8i94cgJ}BU?8Drip?fe6w@y`53Er@_-i&JPX*DC)SrkB~!EY>`x6b-nZhNzQz zZ>n*bltNO|Jt>1Gl9?v^*l^9A6Dn#6WA0<;g+3r4TG9uwqdA5tyxz&GMB+)S(i|cM z`A*EG2qbX($X+6}01E5ZTW?p6C5kMd_R&16t9&+@yP5%x$!+1P-82j_=K-1Jmb&2-?i=RaZaGrO+Vzs^-4{6zQi)iN}A63At$1n#Z*9$sn{w zm|vCHgwAmUOjY9J9$f6h#Yei>U7GfFKpkMtS<0(5iX1UuYwyr59ox!FNP?yD*&w#?f2=#tmr1{qqZr`queZT#S3g@PcZE5-0N@(%GK@ zPVw1?JLd0~KMHuAHQf+P#NcMbx!RkW^FDyk8z*04Z`gDfs77GTD3NE!*$ApTI|qk? z%2whE;x4n5BH_G53vUz)6O)1W@Ko|W^l$x^ZWa7nSLxdVXRmqePnysI_s?%*qac?H z<5ze>UsxfGz<-^|RN`=lgdrO%>V4w_AuB-F@Jz(ac{yvpu_nXD>T?!zP@glb0GM6$ zIg>aNJZ=Y!+s%(hc%(}#?f><4*-^>#M8{;3>A^-aCS{66%xrZLaG)(4*bR0u(hK%+(Lt4p1-=Aj9g&oOE6B^2!F8|a- zHb^W4OdXp96W_T7p&!Ce7of3TeJyN@3pnrFuGCc-0qkj&a@0|OW4_PeTpEdqdQmN2 z<-n^3aCb;~MAR?7U9PfKRp2v1Ix&0#QZ4zhZ7Qob+WPE~L8UCkou8PcRf@ zzFX_kVs)cPDiDd+P(#30h|kk#1KP`{)ll{Zw+s-;mHdg;e(B-X{!CG?d&hK%W&T#` zNo-sFnTp^k^d|JyrjJ%R#;~_V=*M-3v@brZ`=0iw=Mh$qwo<-q1uL!k* zxn)|jUj?}1<`%_hAuovZEJ^u{kia&R%ifOui`eHiR#TCUf0-+a+xTa@qV^b-9&bg7 z=QapUoQ;2kv+?KC<02cs(;w`<7nLWC4)T{WGyc{T)01T867KTns;GjZn#Z(vC>(VD zZ7m^S<4(Rn1or`M>c-viQRDY%n%af)yZOxD&ljN(N1F8oKoV$o*bKcHV?eR-T%m_v zV&NBJR_cAOu>rt$_u`Zsd=P*0);)|9=tB6hHqUW+%BL5T5t1Cs74hkj$J8Sr;tZ z33MlwP5;ffWSAh%b;KPpT%Sw4xqned(a`ED-*oF~9Xmz#_@fEc5k9O{y@6^dj`OiN zj*KyRdB3$Aw;+?=Qu?@e?p$*e+3EI~6)6|H4L1^BnRQ(L5JB8v|BkRd*l8KSe!vhR zgEQ2W0at2oy|>@)l-9M>_UOe9EOe@K=LYEv!i7=ijI8rcz)(w^dogogxj32L)aLeQ z6V#0G;63Z>G2p?9ncEFrH#b!|at-khL*X{vH<6JdyZzJF;Hj?)T7LwYmjKhElX z4}W{ulkwb@+?vZa@uS}<5ZRq_?P9`HE`+G{PJ2*p>d{1&u@j#xu#!P4@l5mP)pj0K zfVR{zga-_5xqk`tZVq^sZo^<^&LAE>j9_Lvig!rCV1h4zck@w7wDAuFAtjwQ{@p`Y zRRV4ed*_eRuHWI3T zIoGN)^|YWMy#`=R11}UUcSUhE>LFKTzN0{WxLrkTDJ(NZA#*uanp|N1k=2Tx{W#yi zNX2Odc^kTPgf<0l=a(DKByQ3G%xz+Q3a~%Z3&w`S`BHm|5f4#lKZh14KQmrjdX_!X zY20%{%E-Jg#_sLcz$3Ra?OZzBg;0{$>_y$CknSJ)6H_gZ{#_784S1ctg(|h5|JvFL z!)!T8IkTc^6)M9?Yf%|8T2KbBw}UKf6?<82E0Y?->mQsCctqJs#qIDq%l_AH@p}2~ z_8^AW+x^~3Hpc6T4&fubz8xI-zs2hnboF22^`(Uu46pCfz2Wt>jM*5|O?DAq3AkJU z@E()L08OE(uYZ9R7>kRg%|6LWi*)vsD~cmqg%u_4U+tut?Xz2J@b8J7!v}_S zXVQM#Pnm;`@vw#C*Eni(G;?*E(af%cjb^5E2h9w`a~09dwdWwHr-KO^u2mz;>4O>; zd<1z?JwO6IN{s|A$8F&^9BSAvhk0fVI-VWjY#$5ro1X7jR8VE_8l-l0hKE@P?idfj zOQ&WI+)O&H$x5lU&q*~{g|_EUqTTp!tAEF;wAK9OlQ#y!FGC|8gQC`h1!ZED45Xh~uf^)Ez_M{iu3Nf}k zHJf#m&RK2xlS&@3Hv7{pQ?qqK^{i>TWK$(4)toYM$nH0q3u?>Y|E3xA>a zo2(l>WYfmp0*=QSVsdpXYCIsAFONj_O7{4yKlJWCj0^xKR!)Zey_sd5^y_8jN|3XB z+@s5qTagQObUEGbu|)m=)iQ3Zpt=vO^l91-5Y6LrYsOU7PSV4@Nu`&o)DFe!EO)fG{FszXk!G4wUzMt}D5kRo6 zZt%CNts&QkH2mZG*A0HIuCjgVKK0%RlFBTq8~l7-<)Wzvdq)nj4y;?mWXNsR@?@{< zkwfzLbBS>D-+?36_nca8>jc@}`5RPTx#*(f>a*>s=l%NJVViN=kQ}|CE;qVos`A^$ z6Try6rw2qUvb@m5;XzD&Bh)YsSN&pXOD)t4n!a|LkoQt>zhh`Bw`H%omiAxQS1zj0 zUDoT2`sC;@f@+)YrrKqC7sj=fYNTe|hEwqu9#o&bv^;={qsiE)n*5e0dorSqw`~2( zP&=63-i;1fyiGOzX-3w==qsH5;JF9BR2ys9Ts}*Vl@Lxr1K!c#qmhlnSr+)04ljAh za29l2p6X4-UHoC);MdXR-U(o&aocVX8|;5R<(<%fBnzx{vp0yocxzF8C!%BqOkvnVnsW}{Hz;z{g2@h@`En$?T-B8 zpxl`JhbOPY)DZc@BUZ=AfB8o6R{x7&-n+5a#3kP`1Xv8`qZD?>pP!F9jx~3)ztBU4@kgAMtxmfe*oY>wCgK#euh2N>KTK z%#5FO5eJGk$SxO4E~X#h`UHc`@#A36^|^|*6C!pUk>zg(aS4<7*UfCP*Qg&=iMy|>J)Tw*u53y-Rre6;|tV0NwEUJ;7*x6JE zQhK9@%-^(ellgrb`*!SZoX5E5MOAR{k53l2AB}v%&4R}0bgGqSk-e+&nwwo+8X1r&sF)jY&Pm{5IgoQELv2dQ@h#^r$W^jz`2V5q zUErgtu7>ZKkU&7-1Vo4zBwz&MrA-u+$SntEU`A&Ut9YrU+NR=_R$)d^xg<_fa-QQT zuhrJ}X{~K-rLFD5`#ehVJ`*q(R0v=Z#d1|V!%&T=5D+upf9-S5WCB>*_kEu4_kH>O zCg+@eU3=}l*IsMwwbxcyeZ|An?k+2cc_k*AN>;F0{CK~?l$9$1bk!fI755`c?-p*j z+cZdj)2W`JTG6W5cV@gm;8Y#I(kt3A|2iYkH!?!RhHj%;fiWx>3`gV%G)p09k&#ar zaraSAH_(ospW)cjB1TTH=-9yc>BTvmdZA4=t!2M(IrZ}?4Nko{ms58+Wi@Vl*(4x~ z|3GbL_x3}=^{XHSjtunPby+kFDph#U>VBh;@TIu8O$>pR;G^U>c90kr^hvdi4DqKl z#|#vjGy9gUt9)m{DYTv)B~cVY0%t+{bQl-zknAze5tcGHDq%^2y>IKr<%L0WNK<|v zPkNXuMmji6a~!(gIY696s3QYWX!?K}U&qX?`7|YZuj+WUcImQOeO|xm9)uJQF(0&^ z{#8B#*+FbFD-wPn_hC6=Ue>oPsaLL_$0Eh~f*s;vvD5~?oM`-+K-znZ4_0rne1Puj zF~87f%!feqn9`JA&W2JB;ZRN%(;sH_#{$Z+^MgfI$n?2^`1E2K3J_`kwKP)mV~idlnICJMwrz`i z2d8R51QVKAgo&}hzQc-rTSnbs@@%~tWaG9T5#S64*e*Ur(<19bZYihh3K{v_=ElGn z<~J7f6{EvLZ9Z0Rk%&$tj|<0kFfvKG&7UO5K@Mq+_CYsv+jh|n6$&~zf+|Kw9_y*u z@B*xxid_@%Me~t7cDzV21@yV-KaTBBDNe-$*@@yMZm1&2l?(bV7%0ilT~KKGArgn$ zlGZS#Q^{GcNT0iQ$b8c3#dJh+3O}gNf}G5>ClyFnlj@qs)ucj;oe*xc56KnlaJ!rd zf(62iLoYk(tK6;q@O}z}0b$zpogVL7qL;P$IE)*6oZwLPfspr*oEu^iHtEJ+@kU^6 z{uUTgToj(j4p?jX1jULYImzd}z0l{a?wb^>4Ex5$mkmRLP%cOQsgi(Y5cdpQGK`I&+TJ*)m7la#>-z zlJf0>=RXxNFAP;5ximgu64W7+(2Y0c9C4X~bi88sj##Jj1#DRkHftezjC^(id`IWH zxK?;p=s0A3^7DMmVH~QZ=TfbK4#~O;Jt7oWNR~^lAYIn~x_*d%Zr-)I_G}MspWlHq5i}(rI01 z!*Ny@Uf!aUtb4xc;H9GrFF9Co^fTS@apL-~{m1pcjYI$*j;;Ujb^mPrbyuQBbQ^BZ zzii`Q9>LhRntlqyAPUV>)UL>(mq$tHN;mY7Y>31sUev*rXn*{fLe{h|LDqRTu%mL2 z#op1;L*$%t0)RvD+Y8x8o~o_NpCt8*X{C(t?0OUH7!f(0T}dZJ^rMsrV_os0$$lgu zCvkHB2``e`}#c8$t=}-t$R?3fZtLq+C9#| z$MfSs>#=KPt*&ORu6EYy5}62UwT~U{DU8aFm)RxuSQpzRjFJ+N$Be|BW}i1dGQK(A zJMymR56Bct^z0fXVQ*(J)0E1GULOPgEWp3#Bmvr|fNmD$at_cN9H2X`wH?O+tw!aZ zYXs=~0Qx=$=yB{CjJ@w5_hwS;-yf6S!)TKph1?;m!E?1~_sb12Lhm4(P1Y@{Y^|X` zfrvFJ=@h#EV#=_dRs}@9tozTm!8)em%0@p0N*e{!cTMnIY z3LL!p_#$cSgH$JldYr~iV?D`jtU8VDHugSMwALX06lkmrNQ$_OB2pjN?G6a^&-~;c z4*Rh-fXB4EzvR5^1VConJ=-q!%i6siyPXqE{mxk@Z1*oewcA~27y6B^HqSdN+qtG) zq;b@RtM;Ci<5Ep7CaWHRi74Uxc-VnZARQ5(8^mmdJw(mI0-{q3x&Oe{6Q@2QTHR)i-DTmGsBS6Wk}TM$#`mar!G~T4z%Ob7WNz zZ9x^)B45E_-PA12!rHBB91pn)n`?Vf-Pmfh0aYc4Pu9;C%Tgq|Ky$#Y2VS`6;>T}2 za^uAxH6=ehy?1AP%qsJq{ED}dji>1-29i5_>lJN!MTgJuCY$>i_v9P%%N3CV<}5y} z=@e-e-MUT{c0d)DI=%PD@xF$6XMV*N3NsERKj>ZYR>dJ>T(YU3ac91PYTli1jvAXC zuo)QF(ySeUY_sg09ox`QZx+QeN7kpg&6A7A3-#t{s)6!RV!f~kSPF05s9^b6!Lsl4 z-Us4?9u`>ECzB2=8!8S7B;$YuO?Ek|rAVncwOq$&2vzv#P<-Nd8pWk*#;SZV&%?<_ z-Y(}2kw1qEMct&1Mhh4P)_*`o_$rjI-r(_}=}M1k z-d7?aBPe+>Awp5I^@q&Ks4{3AoKo}vd9Eh~i&#vo#REVl>uZ3qBI3+4EA)5>k;!9}#f6;E`fZp_tLKLq*2^QcVxN zm{o_yWt6r<(jc)<9~UqdjB|u)SHCK8xPD=en9L=Txgyc6e9E$6ihkk1;`udfVMgDtNE(gF#{S-Q6>Y{w z#+f%+vCbetfw!$9nM@RWTZFXMC=Edt|5BgPF;>;ptodcB-xN8&3m?<8RegK_(gXHjkbyo#I(dkxpJIpHdoa`gX7 z>ZC@Bz<6dyA}I6?ONeMYN|WIdVGeA=qPX#DCnMJl@W zhhJN@N%yXs8t==am6W|uOBkfSs;8F&B4miZi_PB(Uwlxj-@8t)ZR`VAttT6M`RE_L zHYuOvewZu+k%Xj%^K-_Vlutei-Q@GOQaq)D^Q$hw*<-Vmj%Vn_iJ+|KsKi(g^cNV0 zlfz}7hbp)7y~ba%S+DsV!_k}Bbd1_oqz1u@QJr^HppFa(~4-pYd99O@Yt5 zS+96Ixu)3XeQidZ8_F{rpd(vguW$8f!Hp3; zSiO$MfPY{ebcvrabGqNVkyf-aOzbfU{GrOj(d|KFgVO{7Tw5mR7t({s-7WO@f;C2P zGMVfTB6LG=eOg6}?oIkCT5Zt1uVo>}c|k7Jx0Ac}HK48upp)}N7Wptd4#mT728}P2 z*EwTWi_h4lW7u>OZZ#n4a*~(}{K$t5CERf_dL`43!m2Tl+=tqyDG=M+5h&Z@M+aWH z-e>yT=1;QKa9H}ch&MoO*=GzSFY28n$E`#^2|J-bCt6oFdfedCQcn=z=5s=| zI+MU)Y%y$FT@9~3Bm9hj9pJKP-CWFyfXWevd4L{44H;e$iu)5Ndn|)4lC~-1&(3r) zaS66u#sAsYQ1LiXc>DlRoRg|{C#;hh{?cfSEuWC9uW5m_%4de3(dxyip6s*G#+gPv78<^Gzd4xH(QRqm>_}oE>w`_^k|_=4wADt` zX4{a!(?xE`7D9fndQkjAMef#PlRS|dg4}6TDA&Ul1(WaR2fd%63Uu`|vBsiM<-UM6 zbxZ6LPqaN$c>v>)_>!VP*#Yav0rsB7o06^-`P+lBSKGA#G;m*#_)r-)O&zr6lSDVp z3>!-d^$aeRSbo!aN~3@xEYb5)`jXVKeEj#!z@6N38RUXmq7vB!P%0zSo?!l-8((@SvDq)=&lZ}H9AqZ~@{rRCJ ztKSp(gU}GC#OQyf7gV&S=NqlqW4EaK88F%Yky6iPhm=w|7(|&u6|MXH=sr}T9H;?8 zEWB6NOz?W}*F?!w0eSk&sUT8(47l_Em@ZJ(_vBn{u>B_t`RH6~kW;_R=PZ6_Vz>*3 zt<$=A!MzjC6Q>*Eg9npdTY#FXj6^UR-D|xwh1tSkc)^+MUm!7=>?tO9G_wnR$@=$Y}rY_8|RvS$smT zsj&&YJUI7^HA62y2x?O^vB}U&ttd}SKJ8lO0mGGr=@)?hneognSp7^9#%|!?c1%!h zo;|BQ%F1V5?Pom+nM+HI&2amXTPj*B65Jfe_U<*1+uec6ceG_IX)52v9QFuk(;I{4 zDA5_o76L^l>5an+&wwi(FB;s?1tunk-C!z(< zS*)0>r7W#2fuxleil6rjm8o)TNSm6XH6LEWHhO$}5 zEW9-C8$DW#dofTd;erhog%XBT8{H?q1EiK}Hm8?95B|bH7##o;bjt zfM1;iZH&V*;Gm-&v&VLj)LJP>TH+vS947nXjMha`Ab#y=ESLpJlIlx7EE zQc`)tXpSEq!C^jA4+RGs;-c7P#2>OET=xiuIZ01Uyx_OvWU-NLX7j!qMFte{cR7J7 zZaj9Fe%r9<(=_M$j z7`0`)No}F5wzq8M^e6=Rp!O)->-RRyR8HfzlbTNA-kSdq%p9*b^k?@nj<5%`rR;>5_XnW|Carlw@|BphUjL0TY33-53;P4k8}Ojzl{=o zS5bq%Ew)SJZ9s#6BLlMshZ)+ela`1gKV$+Y;3)`LI4CeBj^$U&!kSvZXZ*6GqJ)jz z&O1wtaE}YsiH+AuCQX#4!nK@xm&gmgQhJOP=t&||;)gkQc#^hET)A_nS$>| z`^}+wb5iFM3_N*=q5}7=NkLxHIw?M=1~rhkF}bf-YB}*~yAN zI$6_N-VpvP&3Tgp^2ax)PNWP6<{L%sI0HCiciM0=`Vx^J$mDcTATFD#m_w_LcS1%o zY_x>&fBIXRAhrx(gYmH~atp$Qq!Bvyds-};5)l}J>};GS*R~ zEmPLs@qRKk0@eAo*qvl{vdy@6+bE#^+-km8ZPwh*99wiDQ$nb!Ql^mRMbcQm>q;>G zf@}g}eC>)BhX; zKkoO~3BrUMJ~r(t!d!l@8~-5d1H+P`>2C`fud8+9GuBifIS)|Pb>C_UvyEHnU|R)S zg;;|ocBVFHYS zOO@$-$YBDic{Ah2x^{t>qp6_C)#61GGs+LFDR!tDk5%{|Y4dQ1&0O5Z0cw>TqL_$` zg50!7WtRw1{raMOk1_RHUfHGm_1!8NbP*0t23iH8jU4$G?N2VHZ=QNBP@{F8I@5VB zbe;iurr*fr2L+#?k@hkVSVLNrswoto)uAkI^l!|xeosAA0V((6ykl*VD9p0_o`R~f14GoJKXjOrPN9UWx=V@el|N2EN{kv+TFZ(SmZR{mgKG; z^$C4-5o5xq`I2**dyPCx^S9~os{BKZ?d|P&jPWIm14eT5k=Sl6e(A-&_>}V4kv=R~ z>=CxYO*T!>TNsF6lizSTC24ivkYyxrR5#l7%^iAdr{<3bF80S~mg}*OJ_vq|{966m zl$KC@dV#Oua&|m%fvYlg7zb1>K2^;(oN6{n1}xszQAtlS6i5a!hKmP2{ikZ_MW&vT z=_Qq^{VQuJBttLB@QBK=|DUR*H<=1i5?vn;SYKH~9}@Q_@kROXf!WU3UM)WD;`og6 zSZ5zW>Z*2a%EtktjTWC&)v%yXdO#pPy+;~*-ZoByH06yi>xd;dCgJFNRnNsb ztC#kz*d)dLqLEuO%WFHeQEXuLtL+pv_Icr5)5XsV2UOcRd5Y#)(7(3x2^fLr#iS$l zj)yv{*N1LR-puqAQjknd4kkNB_`NNtF+KbYU)lPqnx^uqhDa~3T@HZ{ z9=P?OWDFV~OU6L5bA->k-g}V!E4{2;uSt~q8uAgFB-hQ7>(-6K>V@y!5=eeX@*`(+ z9;SLz;MN0nDTrZ~Q%aL6rR+dWV|l|>y)s54yn?z^WSCw17lB(p-W>jJQIM08cBYX& zZ&RT9VCdGSTY*DG&)EYU-T3Je*4LJxA~E2N&4q7*g@c0Xeu<`tD|ykP%%te+Q>f%Ox0 zK4j3o)=sZsZ!t2$^skKVEHL~}u9i~`q<9QQwESSPu*)l08`A-K(VKe0S^BQ6YN?qpz!Nz~qEI5eT9>#djSFPAy@Uj{moZace2C|k6$`V zrnwxKiya;ry+s%I(kO(VEYRxHya{CYY4tmKg~qa~zf8M5R-6lK6)wa9uCL6>Bn z{R8CzE8`$F16GPMUvaDY1qJf`ockT;8xZrOr`ZUZ&`&jC(G3o0bM5%N$fe+U8ubCE zG+5Z>c=Q=-u97wuR#Qe%^wx>8OQS6lZy2$TVGFK{B@V?p3g*kv#CX5^KP~o{`L*e( z6Z$a%ShmqQ#dZzS>i6(IVVJgT8_)bh6TI3obxrE)d;)5JKux^^_ML&T`U@(V)9hb6 z@+>w9cd3Q$pwV3Wfh=@Y+M`XHuOqp)M}6z_ve0S%15N&>g_{3x{ObHe&9cg|5*kg$ z#?2oJDV}jL0zEVowc;VQ#;tD%H`6kHi(AB2_X=u`UwYJNvePeNQJf|j7=G=di`1rc zRy8bcAq&67tun^WR9QN{B1h`7M4;vaD8F+P@;pYh>ZK z_;tx5nxgon`@bT~>tx}#_zf1VtK*kiUy=L`lJi@;74?Mj}<>j%y=1Lw|Ofxqlv8OP*rT7G$a z%&1WHP0xhA`_W!LCaY!HRvc~L{arcS*MQj$6Q(WerG06jqD8skexC^4yG4;0SRj4# zUOhg|E4o$IFLSYY+9?>VR1`w(9HEj^3<2*N?iB>86=_7a^(X)ygH;qw%?)3Nj(M@P zma7(5<1l8q2#i5OgI+c&_vTQulRqx@u)zjPmUAh+ax=+yRP`^^9>D z^b>8Xreu7%vT8bFTQy+;!%$B$)G>6j?V6H>8@0NR$(@pa6j3?~QH?f1zMK6kXt)sK z%%5!%ulzME5C)pvC0P1Ej-H4Gg}?6nJ@Uw@!qN_lEiLR99T;235!*w?mf5L4V+bmS zH8AjZdZPPc>u_eHA|53otB(;)5&i=*k+W7S;z%Kp-l85Q6Gx88(KI_%?G$B;;C9&M zgnbD-sO%1@8y04#%AB0p`YJNBQ}1E6v2oSo`eAZ6DHQeAzTEp-0n?CwIRs7_p=({sGAiTqhrl`C{Q0w?>4-!i6t zOFRw;*7{q$ZtW5%=M${ti~d&oEh<=v4w71_)%sg++rdhn^b?HCBZV~D!Ab)98o>cO z_>XolUuXddR)S8k^@trTViKn@kT%z(9cfdEM%ne%sNr`@GH{( z0A^<*Ix>piXc0Cd=TG7p>h!?8SZDsCfGBl3LXIQyjtTHXBnHP9YpbSvV-g@#3i?xj zfM?CpJcx$H+iEGdW@(QJk#ka8d5v}Uh>oDJ2_HpA)hzYCpw%}@DwZ0_^omw5x+3fx zJM**$Wco|eOyql5JWdq)sMY^SrS#$#MIczs5>0X&h{+LAmPe)XXiw_&MCS57Ia)36 z(b;N-&R#>UKZwpG;;HDRtA&*z_L0Z~>4_?y*iNnPKITjMJkl(Tj&xw_F_(K#B={6n zzNTs>>twA?t|(KMqe3NwW%UWwn)vb0A!Q7%x+e+l8gH?Qa_yXF$qg0T*pVGG;yiIE zm>zKqy};@sP+hP}ir`icF3MT_^?;c_Oh!9;esyfjocNGLo4#R%68uIscp<^*mzxVB zQV)O41>bkx7_w`I+h<}6X1HOwL-e@uDQ5~}`|pGZ-}`%)&)%vAhE^y26NpVLl!9#k zm&_eB??S((Xsae%X!ROB#51L#Zmf7ls!K#P%;|#&e9>L{%hhCNBetnd`7`5C(Ad_0 zXCsX(b-xiZCqB!2P%bBl|3dP@Lmq>l%gjZi*bCfl%_&ox+!N$_i*V)M=w5&``@OM3 zfR#HDw?+n~FH~XAb%jNTGcoU!)k959{IsE?==WGxg2)bQ@+chS9FGq<7KOj}25Gxl zaFfa+d~REJI`RTgGe-Z7fvpbqymYPI443|LPGZ6+>w}RBKWE=aq>Z1o!Prs3&xJv-OnV4z)*?4W)Nb2y#7kc%LA3f(;_p|ZO%RgV) z5#8Hd*@zb6F#bGW86@z=3;s4tM$E*kDaD5l2@E@W#$6I`6iieR6Q@XB{l@X1TL&lM z59@4Rbz{ABiR^&IUlO2l*Bkod>_Rz)v<0*=8n4gx2NsY&P_-%)=$RohSfgT(X_sP$ z*k2Cv*4+3VDJ;Ahs%VU03=@n|1El zjEU@eos~zOH3UaqUpyvb_&s}v1{5&_enI)6>hseo=I`2rafRv6gNOd6+p5Y}3JJl# z-vk-XKVd|GA8u=DF2djU*RV}v(gb2SmVDRV5uS-nMCZ`+$gyZb2WPC2Qz@F(j^m^H zlo%iJDH)^SUS5d2T_WQX#<3yXacO)E&!$3bVg#|)w`mSjLlJ=NW~SXfq5K!AMIt`- zMy^!GKcR6Z>X@^E^*ND9tjkM>K;9COlQ|QNZN8$Onx8|Bs4*3!-Smg?>7B8T{P~&^ zp>7zC&+2%7GJSe{`mR{V?)g2)!(w!M%Q(Iw)ov|>g^Xtt@-WBtk(lJ3{80S2k{F+L zLjUNUuQ4C?T1}uy*mC8qKK3e`^6BVq{BYX2RapXXBBc2{#CCQ_>O%oFCkT&nHt@Og z&4&M}5xMXW5cq39{{(sv-SL7va+(k?NSzL#@s|WhXVMERDEv))=(EIi`T5f79wtWZ zIl%r^74Z{~R^a(?ccOS7M~E!H%(uZm7VNY}zHgp#GOw{C71~2TCMxw3|Ddj4 z>)zn5=4$n&KtpTIey9D$CE0EERnp8&>E-ToGao?Ve#BE&dL48AmE|;3c^rd!J@lx6 zkzkk9JjLWacI1L8?HBmYX_8Ck+r#E0*}fWx5dYF?su-ADISag%kUtW_3hIWW)U^^u zJ2c1`J6Xy_%V*?oRM**AXAOrA$X~bh!RF=E`5H!Ko()W7jM*>q2(TQkSn@6BK(|0J zUezZ@z+J~$WfCHDkxgNy!3aBn-9VICJ80maDM&T2k@Y|o>NIdm^vU=YUgMNUB>@^X zV?v8v6x>J+eURlpT>IK$c5+;cMPx%|BghsM;~IlYwx$5!SEssTg4*#vlZe>P!={#K z1Ew-2dazTYhJ+w>5E&ZCp!!A*+>-ojn3FJ8su?TuMA zvf9c{%hVr93A8j*q^ODPC$Yz{SeDiZW_w#yC;=_q1G;Y`b378X25|{;XJK33-cNWx2 zZu1NbEw@F|aw!!hwUIcDLZ#pt3Q^TvPAVIiu=8fG3%2Z{?7c-EEJpx@!@KA8k^?~AXm8Bx+0}t)2dXy&Zr@0sj{Sz*Ng3}u7?1N86ngNe6T1h?-h!Y( z;(%GHiV%?228_<=bC=_T^7h<{jrOjol^y04ggBgm)b2$oAK*LZS;4a~2a zrx$@UDf`bQLfEH|4jXs{blm)fjm4~hsZ)WE{TI7MbC#E_>u(ue&0yXT+YmA{bub}_G}FV5D#_aUeLKiK)# z^PN+FukQK7MwEpyb)AjByi>rRuujfmCTI{Quo?9dz{Dp$`+{`QcI)FJ9F^fGSF8Ji z)Ip;sE+YQ~FC)FgO~1ELHQG7M%+LOhK~9mj_eZ-^UX)DDz3f8A-lz)QVf{Q?sK~ow zL?&JPL*CUgY8S9p^^aJl*u9D&6+P-Jm43}H4L18qvpst%y%wN)r)RlwTn0orS%q*0 zF+I#1rfi${&m^nKQ~r-_m8 z;ON2^N+dqUKWX>Lif{|iDm8>xt7|7NXif^?7?dMH51dmZJh3E6Z6U_$Pf4l}ic_Uy zIDF*PuE`Lq?E9S5`~n-o=&IkQ%;;(9i>)oBiH+;4NVyZY6#=-brV-Rc^G;VV!Gx>A zWr-OkMuavronvqqp=rH>sG1y~_vs?d$NA#XlLogr3Xv*5r_vz)G?wdgz| zBRHJF2_9Xz;V%NsLPgbr_DGVR)hee5#c)_}$k00O@;iuUnkbSL` zR`&-=f`<8w-+qf3(8cc{zb+cNwoD})EtwlKUJA$@ zHGWYg4|8Y}G0K3}773~FQghLTN}l{7@Q7ZNTB)FKO%I%5_Py{QW$eloSqgRkK5G^d&rdL;>5_Ot#s+o$^Qr;82`8=f>; z(gT~Nbsk-;(V=o=5R;!Smp$G1^VuokTe9-4GN!csQON$wBb>2T1!(nRs^Lz+D`%RQ z78>E5@E>=#V26@w8exA4&L(o1Tyhns78%@%OqUml@Dq>fQh&+90W4{ezGJHglT&%LQPbLg|=Y zb62NJa^$O!ckYUC*+-$Wz4fi^d};M^l~-)>6CRzdQn}e%Ow|tkQA%5*&Ua0gzMPCb z{Z=Gy+2-a(TrA1g82;CtQ+V3L{x+3J*QPXc>H#85n{FwVlET2HAM}^!h0R%o(eKg+ z_T90*5z#h`d-j^)H;wR<%3xdcPP1O;Tq(#kxC~X#Z3BJFL*APnqv;AQ?D{Pz2vmMD|JE!ra3T5${$KT2 z=ND==0=4$jWQpw1txCrGR@I@~zndwk64c+p+sn8nfIV5v`HrWAj7L|iqansjN%a!@ zd5%5ltymvNXOpnO-f7%!P5Par53daLxNcmaIUz97Q)>c-K8{|fA&T8m9M1ebJ*L_J zx(o>SI4HGM9JKoXUTTnRN;{wKmF~Ox3TZlJ8u}O?T^(C{ZI0$(`Q4-yKis=`Zgt*| zmEW`aJjOe46f``XU1{ygSi!Zr>sVcdWX6`VF#^yI3yko}@nR;|w9`D&)h+ zh>!Oh02fPMam9A2v#aSd<~|)TuV=7jtes#F2c~%;^U5Nv{sN|m8Vyx4SLDofeAd$p ztrgG(5sid0q$E+Z|4G42`c#>(cCRcUSW5hGg{n|S@{iPOQ(&UFU|cQ#I4jXUB|hd^ z$sDTuaNg;X<6FNKJ9w>Ogj2%Pb_u~+c;U#zGw=WJnd5YG-{IP)Q zp!aXOn%$4urODFVR3BPP*`nA2abS&hR%`-w>Hf|BrzwGc-puHtf& zfGXzKeqH+8^a3B^lAFbGP~2G^_QtSlkQ`CX%&$Dh(V8w$3>R)x|V29`s8S| zEOy@*Pqak(M@>_8>PqvqNTd=3SbvPEW1w%$x`7XMF+T(CnVfq>(|W2OY);lcxvS5T z`MUMN?0l7(n&U+FMZvCCzl_1n^2{>MQY|a_3>-pMY{I7GmyKl`jquZI;{I<&g^-?e za>u70JNkuIf0QWJP~}=GH=k}UC}d_N_vTfuUv!G_ORxV~t+v^<|7oKsXlfidDhl+t zP(R4HiJqZEyt^q`m6nS;Lu}qe>WN36v}&$XnvDl7(}8DXw$1bw3&hbD%?v>MO~HHP%*mwn*aT@e&XrY%PCT^Sx%)@dl#4w+HOei44F4_AnCox z&0Kp?!W9^!LuDVMms@fZ;(7!)Uhyo>YGZz_x^@jCZ?vzh-SBU1_P;C9`azT)d2$A& zC|V)l-YMr=WQ}_lQn?j<4VF{(92mtRysBW2=_SE)rx&NM=WuAbWRNlZE+dPyJ79Tb zM##=zI?qv_KzkOFwO61z{4VFS#|6sPxsBDPHX=`8M(Cz>BM`Z>o58kxFejjWzft~e zn4{g%A}>k#w}r2^Iod~wIojPk@5XE750Y9_wfo9CD4qB$&%$f8Dl)S{PUCM}Bo>%-0*Lj1nWM2Atz3i|Lsi=@A_J!gNv%g-} zhSMF6)j-X1`ajzu9AMJEp!WTQ1a10?_GvQ@D;bH==(H|a%@juYz(`0exgk_NFo;F8 zw?zVs4e26Rf5O!;9BrIGiIdgC{%_j%vD3bfo%Z!9+n^ic+~#$SZ)r@E{OjI-U)8=7 z^q)w{P7fZDHr_?wg{sdCdNFlC6lqHjRQy-NY0`xpXQUScGvb5jz<1`0;cs!>wtMhc zv0bm(+o{)dHb#9gOyM%oljJQbP3-AOm*0s6aDoJreTAQ-pHWTo8|%`2)e+Ejdf5@5 zK`#z=`jW}KMlPbwNE*wPXOUzR$g!N)!yKy_tq|W;g&nH9w0hZ%W3Fm-OL%eUh}(4s zXqNV1y`=EAgsM*qdOx9Q(tSfH+}0=P4X4TNJ{hhEXr6x!?dvb?lis9xyK|ezsoSPt zS*L2=7@9|#1zz&H&3o1n)@{jMHj8DXI?eq!jN6m41FE?L?XK6Emeu0^%o8u}wv~cqaQoI)45yDuKwMO>3IR1DbY^=4e?a4j8#>IYPOlex{ z4%RkfYnCG9%EMQNtBZoM-3aH+pTS9r`yy!|bA~ z?#kC5NboIi_OP=bCA;iBYwGxBh&zOErYmyh1HY8eRnznI0AN9Jc3#?Hs-yX@)IHb4c0p*>{xKxghuWD8zgb;gJJqhHMC4u3cU323GMpJ zf}bF|JloKx%L|KcT#BVqA&wU=!H?LpB>1ih_7-~?p*dBX&*M43@GOd0U|6v)E zdzjo%;ojSWnZ|JCThZ73l^<&NKSpdVTTn8$AK+cLC%10T?#LpEV`85XV&1BDHjt=c zl0Byt29agm!xqCRG~hzWMrs%16^B@Sx)~9s>G4c!jZm)L6bu9L6u;Mjtp0>o4057n(oX& zg8e=zLDee;YE^2jkoq`1`z!4FW0&MDI(1HS&rD}?o^-FfK_Tz6*L2&o5X)ibJg%@O z&L#);58i?t`v*0i)qV}yCFS&Et87}R{W&#me?`eM$klatGP>&?B1ExxY$;T8z=4e$ zr2uH-%*sv?pxBGFxVi~dTNsYiXt5UbE%KP*oqAb+Gi;S{c2h2Q zK;*TryiocaWj`f5mF$moVEQjwR)b5-feKj4Hu`5Zc}Z7SvD2x&UMrySJ^yL1fw2fwxZ+b9hUQNVb#oR$TQ z_&6TyhgPXaTs9rC6)fW(?3ui3W$Nx^txVm$sg-d(df83OwX(TSXl2orTG`?RlK$jH zu42lvVxuTNHqH~}bauhlh~WHy^?d^6s>pbK31!k%^pBWFArPx`&gBXwf1Bl(+;YwD zEEdu2)?cI#%z_>Fxtatk%Kb`fr3vb|gLUtb!B8JxmgToqUsmTxRiCwmCw3+57#wBJ z1vLL_sNGdgcf2W8nbqHh&)O^Ljbi&K#qs!a&oe|YW##&J?w^QCMdmmwE*Z^nvLAo0 z_#QGZ8^h+}t^Ym>gAO(|Rc9*xOz(DP!n{(FU8<7FogUe(z05AveaH8o@ww$yb(UMF zsye|UYJ!6Ta;g`2IiWpV)}H&(d)=N=_S|(NnXt--&*f~X8L}<&+Jc>-GFOtE%}RQFU;~T_=f|Xr?u~Uy>Uw+=PB3pp|Vk>hIvm-g_wi zCo#GYqB??yjdk=}aMJSUyrtmoH_3UEa2QsKPN&0A*Mg%q z-}2buE9MUfdbxLFy;IEH&!-z6mZ&LH~L*~r^Wc$dOv4w?R z@t+#|VgO!!?49=B(1MUzRg`827kNLSZaAxN7djx$uZ(&Rh06tzIKf5h7cgEbB*2KD z!T-oL;;IpgmV=RSKwQ1mZweWIDk4U!dll{`&|E*74{0cywYcS!X?)*Ll-qlm@XeXc z%zCm2xeJ*pXXt#O9L;}f4M*~k@ zFk|&Vk0uzf)JjB41eWEu;*%jq( zig>|Gc7g`sfTW$0;3b88v}_}+-4}6M&U{n?&_4Ax5;cqRGJT>LjExOni#x|5y!fSE z@l&SoNcZAjfKe(ndFK{#E5jFpJJB*Vs|&Ur*n*2yC&4?dbj zJNsx($q!R9x%vjo`a<$Vm*4~iMSUg`^_dH+@WNfa^mgluG1AF#NuYDq>ze@+H`np8 zb19ICtz7S_krA$5T15{oq0?U~C-KWApjoKsPd^!qD(k4v*g2=Uu0Wy`p*oKOAXM_5 z9a{w}=XcSm`Oqn$JvkzB&U{K`kot2bfHPGcBrpNxGM9kytPFgu{%<5UuNFi+_9F)o z@J$^Ijm+kdsa{ggk;Z!OQ~`gOyEnlxEg@Qf`) z>!;2jvab$8=`q_^+k5-Y#UNA}W;h|6+z^Fy@#mIsL)cHSbN$&3QAT=_=I1KJHrw=L zWHP{CCI8ChUy1xH3O4&^f;d=v);|9H#z;KK=iA)=v?2(T%r#UUTAfffE*TS$Yj_Qs zOSABfacai=$AM*p8rf{;DL}VuHJJ) zvbfshhGcP-P$A%oGYlMMsN#qJEc;l4>wT(OmRmuBa!Drl(Lz7PxsEW#2r9qN7CxUFl7;UAHzW&Ri3<7G;8Xm1&iomTbsZ}1AolP$fmKHk7q7Gi z)0r@M_R)_?3{^x77{JA=t*1{FbE;xY_~~~1M-xZ7&xdHsmec6f!c%C!*@|%vJ3Gzu z2ZZAP~l75)gv) z+D}%DdkekMA`YOWWT}VRrEVcKU|u?!FeTc4KsW}I-e`ZErKR5H3)hG(=?8>J5~4~H zK~PGHtjf(PyVsI4(v+L_CvMt*%S~JBru|u0+Q<(=MoN_M2EI$%6{H+ocrzUenD^S? zK~@o+*M&t+dLvSWqXBP}BuCR+9&imwjsjh`BtsRH&xg$KVkYz+*u$=~s{~Hvl;F`e z8i-8B@cz3t)}jM+?JvI4J0}V=6oCVb23cNUtWe!s|Me<&Nr3dQaX|Hf@u74>njO#( zt2c9UK)^IaXCqxf*8b@~U}Pr}qS$jjj;T|jVzYM}Z{ta~2tVS*ndps~=(XnTnOCZ7 z0I$R!S_Y4dhX<(nFw-{nAT!n(a@c)z7bRE|8o#(jtNR6o3!j0)&71{cusv2r+gS4p zp)}a$|LAgv1S2PCE}bzXgbadZy4t{Z?5v2 zKkxxD(@!}wTvR&Fhf~sWpYgrY(E)K!sI2RKywRvZrD*BQFoei@5XlcnXTm%;OlLU~ zQ$Hbj;6{w-Ix}s|3gz$7>QO97A$>!wjv>54`idk-NMC1Wv!pAa1fGoV)oOfxCOmVz zKpBQrCL@>eF8*k^Fj_XN3z9ONdB2eNM?WGd@9?+5u8W`k$fPp&G)VKydpW755rZ;eF_<|BayI29>Kf20X>I)fHl~#p7>^Ke@ z*a#2Fkpl&*}zXJbn zZ~2$T&+LEe!v8ro{MAkg$BZBE3E=OtFRk4_j@*p&uQSqR)H2d#LZC*JnV^*^?S@wN zTYLD=W%x%K{)?aRnX`(a_9f=*(b{N#InK(eeCA?S-m|yeT;>UzrKR>!rr&xfhSx+W ze$$OSR|j7#hup}Iua!RYCWyh@Yndo3g_h0?K}G{YMyq_xYDitA6qbndO3e6Tnf66K z2tXkVgpprrJ;W_~0khvNRB1a|u=O3%rku6F`r`dB(|6$CnB{1-&6;rRw38*N1=x`P zxyCkVej8qB8)r&m_i;>uJ4W7fm#lOZXNB-QeNV7@T9wGAi|}qb0}|#-wnNZuBW;9O ztlzXpI8i8jjcf2i=&X49GhS;q%b2JnkH~Unj`P>y=hW6S?*5^uBlXN%7zt%{l&`vh zsKaFM()zW^Ut&FP=ZC61|ASp(q0txIrhBUp-rU_q_E zPMT7?WL96f{>W9ddYndVNo#gf57paMZm~8O2|CKHG*8e0IA}**>tNX4Fc$1l3eD``#((n?C zJ1-+2c3ytwhHPtaLSAB}Q;AP=quz#gsgS!2=ViGY@=H}aC9hhMV}WZh=!=P%m_I1>Kk1TnIP2CAYhYpLCh@1UKZgZY<(+qT9 zcDdy|=)MeaU*@~zoaAQzw%dwPZpnAMQ4hFL%iUc6=|*jF3;Yi^&DY)RzjJ|~=(hbD zH`fghIW7Ir9Z^f&m%H7lV)y08?#sn)nrZIKe0OZ*xgqzunEx*~WQH5^yqmb$jmmSQ zl5WUi7u2)dkbQ2*i|)(YZmw1C%Ro2k9QWnB?#ufwHnOuI?MAh^@SNn9bCMf1)D5}M z4e4~tdBF{-bYFVAFHgC-8r_!{-72=bX(qWL%iNF|ZcDFnqc*rvXS?N$abHsI%U(Cl zJ8rI>?#r*-mrLE3#p;DUs`J^ZUC(yD{0^aq`;g!1g#Ci&@AxJ8x$$5243TCkzl+Fs zg;U--zHjDNom2j?`74P3HospxX&wUx`F)e`O9;(EoTrDd$^7o;dltW! zc$fI)Jn!WDr#%0|N&7kBO?+?Vw}hXhyTf_P`!eS}pY((I4RpT$$g?-Ufb%VB-z9z* z-%)-C_Ar@Z$g?h$@&+`saC|CoHAP{*s3Ihydd zcpl~ZJv7WicpfFJfM*j=AI}^3_2+jlzg>jg#q$}STlrnh`%iiPoxIC^{-F ze_hqkb*bs|n;hM7R$IHm4M90+?+g6c4asV2>)ep6wsxKh`KPqCDY3%ken8yP7F;H4z*~ z)tpsjM#srbMS@=GeH*>vjS-nHk*g5Dh8mQ5K8s?%8Fa|#lzi@EQ&Q1qEOsj zTW>odnn~3Ro^T=y8)|B!htIe#AJ0Pwt*;wX(QDcMhm8A5CK6sUrG(1kV5pA zL*zsw1t8>F-?YoHzAka*#LbfKEqRYmT*kXob_Cd5do^yG5&yD*Sb`e{Pi#-awp$); zq`a7vcb`+Qeb2-z%qY*xZTzzWTNFTA-9VXTKr>-EwgNq@vGSOTZ?v&J$j@lAE(8Fx zU>0A-)Emu$?`=4jm+#@HPQBb>7EDo5cbEke)yth`!8r92F$*qKFAL3r3smAo5Yh8^ z3mCB)fG`WB4kOmUJ2827%yRqdU2eE0-4piL%l6k}_Sb6rYlZ!_$^Ketf4y#hi6sGL zHrZbZ`zv98weTe^yJw0*)HhY=cB@`cBBuyAt$~=1ha~kXa~{?l9HAy1EWV75t*Ld4 zHPr##`kVfV5<&mmc*6c6ewH2oy)TO&YscU6W$~xj@ppV#{AVn?)PLuf#sAfgk9=AD zvv&N#FN+s8fbti8S$tH*TQy%6KTF~Zq{rM<6-aqsN)K%NK4lV!SI>%R%KMJ>oyl^W`Pkd|WK9q)0tZR4CFD4N z!Q$6sjiE@_Cr9c^QsOiBoctwUo&UE^{=ZI8?eD4bdz}0`CGacS@8o~f&cBXNW9V8Z@QHtz-|oLs zC+$_FrT^l5HWz&I#<}=gkOgt*qTKlIuw>;Gdpz3Zy*b53NWNX3UzPWpuJXn><*E38 zqP#k%yl2KM_`cv19dWc5^X@zQdkBR4oD}~lDY}*D(vMvD#yF|JXP5VEPI&|VuFCth zlj6I_l?O%?f1~uja<*!wj^p5;(aWLu`R6vD>v^Sw6nbWs@HkrCD}1nAD)tX!zp5g> zJ=SZBg|jHeg+OoZ0oi#_hSFJig=6x*%J%uaxv;ksKP>lrgk@Afq=|UGoOYt72{jNV zr;^Aw;K1Rf*y6Ry-WV)r{8iOrYakvI<9%!RZ9ul6fH4nqqbj?(#*rL-HaDw|w(oC! zOWjYM9t49DRozD5s%0%P_pV>$5?GEb0 z4__W1WLzHayM-HFw|FAMy7LE|AR8U5{5Y&lU9ZO~J<*Rs<~K^h#ue<_=4nNjiif$N z{PQ}1&3imxo}B(EKsCM!Q092K@byBUAtzn7+Qij0=gFRDP_)_lhOh{-+s}1@vIQXA z-3*ZL=Ic8D(AA$xM3d7bGMLTDVhd2Wn@<<7>v$JG_woFiaJ&>}K?fsu()XhPO^(z0 zh>bOm4p)Al#ePXrT@e0v%%(*4<33C*%f;e0b|0SAwz$?*`O=qshzJ>RKa}9{UlNo4 z(=n}t=>BCb_4UuKZ~ldF;0(Q6wDYb?#uv-r^jeTQmy#4x5q}f_1=ekg zz)`&5N{Mk;`!0*A615*5FBnTKhZ!7sA7@+~C~nR#rOnD;7sqfdksknEU0>HVUR?U` z`oFj(V7vx{`q#_c{meF*FJ*`|&HjWGCtcEIAn+U#Bo=!OY(pq3p}(qo;&H7RUV$en zPU0gDy>0!aFSs{n6xMSK{7p5H@3UXOkbKSlpG%1xANe`U19OJWFOuUFOBv^KS$wng zKtExMQwXH7#Xm>-{xcca3Q|622U06CD1v_`(l2O6@#;)^fj#XH$p@bPIW55W&NNpxN3L-0 zZyxU-zLS|=b9p>}1G9aDCo&)yOEACNncvO2oEBkzU&{OrS6)*kw4|Y7oCum)xry*3`vmw&{N&^#`6(q?0iE7O;F9 zSkOVNDjv2do$=n_G~RVvAQWNHj1<^3`&H4nN>zcKXz-Y#nA1 zAz3J81wCy2l$;Eb6{#Imtwwa}FVguy3V_dp|BMy658a`>*?J-X#kn&-j^iS!uekg)ik^f^v4yX<1hW=&b4%4dK%>2due?GSgZ%mjrF zOml->|2}&yQnunrdQdvvI5pG>-* zq^AjjJY13%qMD(cu@j(TSXJiLJdQ z$DV*8(eoAINV#NP;VO+DZRjU2+d*vEb8FDacEo>KwXYtpZ@~l#7+PHVD!#O=XO7T^ zE_KaW&lP`pZ=9I?WXd^&#Q`h@jAIw7Vmy>$XI2Q8ld~Ra^;1ZzoBm>M zi2B$w9#;nmu7sXcRmXXqK>TX2+)?J;5bvAuwj>k17`oc3OBnIQN8gM0ZFNI)UX8|N zWB-bzcU?uBu`!uE&6})Phef%bTw9=f-}F`N;;mTszOH9l{l*9W;Tyu{Y|mx!zOU%! zpw}uAVxB5;dpJJkHNEzA-fHwrbHz5)w{K#Ro(LJUTXNd#tK1#s&I{Hz=5--3S00LT zi35Ivytp$MLJ9&eU=4AcVU^BVj#W01cAUqOryAVxbx^^%5Br zS&N7{%T;|^-BIu;Mv8Tx@FMkUbt&H5*+!)~{m+@){{A|gd4#y`>=^jvhE?6$$_@9~ zz9*=VGj!yL?#c;fGa^z>QGKMSQK9%0udMg~e108Io5>_2iRye6Ek2`F4JI8&d`63& zOceONEk0tC2}ToF#D z8O(N*a!SEQOwibby(aU?Ip8k-ejBy=Bx!^AE4`uE*g95Cs5P4O_GKK#yRfS6AU;Lo zS9-JSKlAILw<(@~(7PtNrdRxof7e!Bpyt;>=GP#+u_g-S%60EMwT<#jHj?IHin>6G z`dTpl4ZDM~@czT{@)K??VxSzvx{L_GW@n!KrPx{O=wlIifUq)nH zj#}cTb#=&q)na3gn;jrp9379Ju|=I-ri*nKQl`Pg=C}>7kTDN`C zLoCEEB9LXCI~O2Q)+{>^Hy>BBl#2;&w8*MbfzfXC%4Zz$*B|9*Yi!MEpLse!MNdzc zchNt!(_&(2EK!jmdZx)|?DCbpJ$xfT`6@rwVz;^EtlR!mf)gQAA!jbdaOaOq9og4* zrjA-~jShR@H)~A)!KC1Pe`J)c?_~aO(!GtS2R3<|lg+*26JEi8!d3lo#FQjibcc+k z9$}*}J|^KkBnk3g5lW&9C1HCGT5>)O&W?ARo_sttKBm>%Zm&?J3|%75AcP{R^74bm z!ov8VgI;=7)-W0U6SY;96$fRV>*jBe@saVq6(5vw)8cQtg2I|)#oH~wQP`bD6^-~p zPWJb1G}a{>PpfEQ%xsh~Q{Zi>XoMnp+j7?Xtb7jP!NgPSFU6ApHi)ceKJy0%)o-W) zh2f;w*J)fp^kgVLh7(?hg?*E9;L>8;=(R1`(Svfih7bJoC8IOw)RoHLxmp9y3yfc! z;Br?vGg3LN2!Ds-$-Z)0u~zr*{RIxc=lN#mrKb7FI+}%h24^(t2p9%(&CmVBGM8Dq}*Vi zP1k0folWA_|I{u$h4w zn2`+Nf}(;;L0nM`^H`LSz|08aI7(ewx>;MTwXODVwNl(50Zal#jfz{X1#Oi#4kA#6 z1T^`7&%N)>5b!zKuGR*i-9s7F#cm$tjEvRc!%vzF2 z%ZNz}u1*OkZC*AZ*tJ*ptb@7T| z{El|T!Pw4Fne+J&1UU{Jr;$L}X7hFRN!fa{F(Wf!cQlVx4zYUBQW%JSXb}B&<(*(JFrI0+HC%z;FXNXWq*%P z*^t2}=T8(q6~Bvj@Wf(l@fWd-o8j!cDiVbsf_Bu8U6wdVZ9bUhPwmwPKOa6T zA^`$}Zl^p3?H+zEYP06TpKX#IG#5I~0=JJ^-WmQV&V@kP>(Dp3sxU~+olNm#=t#k* zjL6aG{-k&8mBmj!#H@M!%mBjAOh?*tZF) zIHyH+x@NVg#G)Q-LlPa)L z1s~>Aa6t~wy`f2Ztb46~`o)DZR`8)byv}&J+F)0DyoBW|%!hi)c+E$HWmVnGm5jKz z`QWS-nejblES;1!KFL3;CdY{UPLHOC=-*l6d5S$)h*0|}ZghIN21wOZ%7iWXn}Yr# zfP}#@Y0jkVfp29`3Ma)hkX#nxDy92Qg988+0z&|o?KULRn zrQZf)+e7Er4Tc8FtOVjgILNo==D^T57*%io(DfCa$WOE0T$7pa%y=3P#=q@P$d>=& zzRt0~#nx3$;BUz+!2xCKs?V7?aj2@)k)&tC|EYIHmQfY&Xj3qLCYRtA)Sx1WzR8W! zJ0;;~jXVoH$c?$z=xN=R<>nPvY#9tt7fk_C!QQqN#JmP#$_(n06hB<`YDfoh-C632 z<3~<|%X)0G;)J_!qodlq=$*xq+UIxC)@=LH9t+1&mTj91^^VW%p|%1!h?RWFYUsQ8 z9h9U=1!|uH4&~S<>HP^&q?IUUe_}p!RgEY6JsINZ`u7!#Z_>fz8)@nBRm9Jg<#@Z^ z83|pbyBb-~tvts+&F(}7pY`Cgb?*6|@Mu>kvp=yIK-G3fPY%Lp4=sBi>WA8);HKD~ zC8;{P!F|^7C$*uAQ#HLyu>pGL%`?0Kv$9jAQJQ7DEBipnA9^Z`kMBbEQ5K21b2>-9 z;Cj-Rj2Pgg-TF~qs5NpMs8=q489d~OrO7xPv`=6-uY$4HIO(0^e(3+Tl5qH6w!#%M z?uWkLs4VoVk8sr%7-x%2T=dHj$R`~;aIICy|HsX#R_q9)c-Gk6kn=?Jt_IM zQ=(8PB?(o+BynJ|B7PeALn{V;>aY7E|K_(}-@JJCvnD4%<^(a9P4FjTY1Pt%D z-grmJjc}(cOxuqnSslj^NfumJ6+c*71{6gn)?6m{`P3$(7 zV^}*r;BUnaOFTIfV%{Ci^05qGfAUj4@icJ6m2HIyU6c!`C}%}rZwjLEKFSp^cOpMB zh-ZB`0T|Ezfvwlxv|?YDxUAU$Sr;0yDGMPY;zriD_2Z-Mr zBJyqy82%`9f@~Y6`m)=3BIjOwbb$xItvkPu;^J7nfP zv|XXb2eDeex93sqo`m|yb)9k?9C6y+A49^AL921&wl5l*o#ys%<4yN}aZdkgyaxeL z{W$>Kmf_w-q~2 z%A!cmhv5IZG`EsP13Vo$I2S0Z4_yd6w+cL&e90OffTff0Q}7Yiq6v&)|Sq?C=yDKpPfr|#oNDd$eCvp;sRLPVjkdz?Xq z&;8D3{rWm$&h%%%IVAnt@Z8uAo~w%v2B_NlKLB*f_W*P-epLJj>zDaKsW@nTAcfUZ zPp^+dESgaFgF+w#XMuPZWU4y&m%*ASAx&~~`{28tP#aGYTR^g0)@085an|SW4%PpP z+(NVFaNYe{b(dPH@{feN3sK;ffy0+|LV2_0u!`4^@psYhJ7OKHTbQ%?m%Q(qeqT<& zZ@c>dc;DY{U(4#)rkwBN$FTQZw#nS<^qM0R)+ zB0X$Xed(?Gm*Lp*O1C#$+ADK~&hf{mvYgS(4&UJCzddw=10hS=Rzw)ra=EM z@r@E~np>WrnU#^|RUIpE+9@9BP>H@0_2BV~JaXE(7KmJ8b(9CdC+i8FizZj2QpRLs7PM1jz%c!07M8n&TvGX)5sI3FPPJO<+>IWDnW zJxe4<+=#P6CkNwaqjs}|$HDyvvf-?F9{jcq=Kl_CSF&h4#YP4a!xk##F!3j)tz(71 zYX1ELnVA2Fe_^%vd9A@B7mG&?z?JdGYj!f;P_mN?1a zBMVIQPfKo%5p9w5;Q6*t9S$mc(TcubLF4cLE>BrIS>~=;p40z38NuPK7l(gyWe@TL zYQd_*e_~}1rak^M;Z>&R$nD$uQ@akf=XQ`@|6(yo`;3B=Ysx{LdrzTqtP+p_*I{)O zb5C*TMwFDycti}w2W5WGc2$fR6gnDLgQ&-Cmx}h7RE&`6``xeX_>xdR*dN*>3#h&F zXsg)f6X^0cWY!T^Q;)ia&r>I5kv4 z0OmRP=CfE`%_m!H>ejL&RMT=o<(H9KW>kGRbT9c}yIc@!EZNE~Q(OJW6Dq&*C&pnx zl1;k@e>)iulB1OB#oQ`DPm=kB~K7RhG8T?2) z{E0%#jg7C?qQcTW#U}0Qenm((rUP!f~nD zR+ld7WZW|yze4g|)j|VT?6nmbOXcn-qT#*^aIr!3C4NY=_hmKvBxUI~I#)j9Z>T3y zhF%rKvG>V)yS2QgK@eMB({@7T=YFm@3~{k>Ye8j*zisA?Dp>D-`L3XHjC^gLCqQ&m zK&%Tl4>nKdCLvfOonGcXD)wG^&F6GkPnWgjHLakD3Hx>~M}XAq@$JKPHDE-p991Q@ zKR;18_~$U{0SrhhBgZRYTHa0K{2I!ODR(uL_o1I^ecU56KZ@UwzZw$l$J+V$XRu#{ zHC76F6y;+0ySruCI@b_$s*^Z7^yx4A+cqJG$ZzSxVO|mdd_ehXB$v29{&FK#U@_Lo zm*O>wKjcC_$195@o?|%`Vs(kdZKpI;#K&~=#YZ;FR&-k^6ijq!COKKEBu8GUJ1~mM z_7Tw1NFFd>yi6*O56-xI7zR(|wnKKVHz7`mDWwP}vVJ^Er9BY!qx_ zETZhlxFi9K#J|MX=&^rooq7o(Fn+%p`<-g+Kc|j8at|Xvvso^|i>RFk#_k)6XJ~&g z_V3hbS^Oq@{2s%*-xnXqSD{OSGZumX0jE0jk*Dl6^SxmFNsWN-0(GB(fIerM>ry>! z2)yxyj9ypotMyV)!p-W+6J?8m7b|JPvl?opp^d~b?Og75 zB|jC2;*BdT;AdPFpIxIrDyplH_eAm7Lx&eQmN+%7f!L(*DBdcCvSiv2ULC?}|4@o}=? z6Jy!{T@VuH9_Efi-%fFzHLL8hw|4nsSCoqVQ1BMv7H=^&>`PhAS@bN7=cF{>Ai!Wi zeEih-q-kZ1Rh?+_x>Sh9Y!dRUmQyh=xr&$+!`s9Sawl`y*$prjZ(<4grPm5D}-q>4dSA-#slpb}ANxe?}8C z`xY}hxoutSim9oKHNkX!a)O|jJnsDNW)lsq9uBo@KqQS@pWi2_m8TsMZc~+Y$Ko0Q zEA?EfP?FXs1RsLmsk!)_D!4clekUIRzczoD>Hmm3<5u`%bBbk(BS3_eQ|gcVi!tO< zXWIM?vD4KY+g46Or%HF?Znx%;hHonZx8^Px&7RQ9624W_S{(Wyv3MV%F>mfhes(rD zwv`WSC@*D>T&Y@rY;tkqLOLWcW4OcyZzanFWu{5U?-`J%Y!a1 z_AlA5hSr7Nh({@*KbwOdq%SIaUr1fiqm05|TvOK1h=xI3%|iES+;^>>@)j7;zw<nkOH2-9-2P*Km>pkK6Chik+xw^ZI`{oHN!{<=$8hL%?nImTz z%RZoIy6j;*@P<64zxhM@n=$guIU_yB(x<60a-6Y@nYX|BdCoT*)88D;H=Z@pXEI;b zAiJ{!p+oI89EDQr^R}2|@p2?Cle`X%0CJvs@-wR|i zg=aZaIH27W%I3H=+wLaS+2#ia&EH9c&aUDza(~tYE{bWSrk2>KlRYI+_y2_bWl>f1 zuV}uhzIG+jlu4y~b1Rvhh56`-vFB~kcnAM8#{Dq-k6r>M)_lhao1~nGr#(4rQ%zRk z*1pCAKa!$wYgc2LWRF?x>PI-5@%S!dX@H-}B6-V&M8$1wqgwl-wmewtjqP4ihmPI0 zq{a0CO8bMD)J9}0aR|v?)E4>`Rxv#(LxY@cf26bCSzWT&%ZqDDPdB1+su~7h{ZTj* zsNVzeh+L0c#|x41HWRBU9brU&r7CZ=s;Jx$UP1E|fZKfa#Y%Y_uc?(HX@N=P_uX*W zy}0V7L~aX?`=`Y=aRp&`e*sY^6ogv}&4GKvqMp?lQOR=>e)(Dcy38KoyZgAzuHl!} zfh{A_gVMb5 zTu`&1;5M_;SXEw#)F_^RlmA7TOBsn@?o*g5z^O}eWeFzB2uwgB_$Eth8o93@eR@mU z>w$deEN!*FnWYN;dokdp8(mM$HOJE-pGMj}r{&WNAVi^cJe4X$kNwreFEuK?2$XC$fR^&-?Cwl5VYZ1UGtNb zt`}XKem(;8tiiWKra-i$fxf0#a9e}#KT?g`dAW+0owVQ%LTh8YZgEYvrku{45WTK0 z0OvHo4wu|FvMwI{08Rc8+!rHBkKML0#_hclI)-z%BX`xf8|4;@!Xa^tU+D^yK#uD2 zwqHH{nE&Ad%+RUPy(ZpoUsDPN{n)R@2nX3}^@KuG1`rA4QgG?r%vGZBpZAIaU)3ZI zNC2{}-Hbew@n_+Xka@GnTU6&Q+88Pj=lamMrzby9yg&#)V-oB5#U(o$rMr0H4aevc zOA1_E0uNy3`0cx`#C)vgx3Q|j$l>N-YF}X2=6sV86^lsnB`?_^ZHa94^wm8BLwX`@BF~ zJ!e7$3_71Nf6&V_>3t|{SYR5=B&v(=#IwY=d0AEGan$si)PRYJlJf)M4bpOcx|PpT zt9hwjwQcaMktW9``kc>n9vc&_A9u3ig6n9%BKC|Q_V2Vqai$3%YnIziQ}bbNaG45B)@utCHBRIW z5G8NN1VxXiFX#yIyddxk!px&QH6u%0M&tkk_Pi>?hBQrLt6tRu`^JmU<*L7n;kXm9 zJ+Y4!8eW#_g7D}Gw97d9ltey)SS0?|i2R+mu@A(fIPwIp$YdZj2bdf5A}A7H<+Vmc z9D<}q6FB^dM@Gs&&c#cgTYp6QaJBX*k+jAGLZe90a_WSek298z;ZsHu-nKPdyEfcR z4?`$H3e#*qd0NuCPkng2`cXfC50$hI*HN6zr_{mGZ{}l+U(WiK^%!}V=sIxOXPHd+ ze}xnLD=CQy$(vKWVgQ#CSQl?%eTXnTM!t!w#K~NcqoR&u-IEtfsThcQ2FB7l?QOnC z5l~WC8A+K2FBu5<`>fAOMb4S?8QN9gdel{a7ImN&=sON!*pJkCmejxZrUHE*KGg10xSDf8O4P3gSoqKAAeVii!roMV(Eat; z#7J34n<*!T#9l}n0El%XiwF;{UPrQmE==@tML0&Xo%eR*DXu!R#w_n6OGuF#u=Au&azwKz+N^RcN89>w!9=2;;?I2;HS zcy`C;y~3eX*pRs&X0e; zJ=f(XG|L$&w;%5vQ(rJ7OU@jMVh4<8jQHhw-{NyQ~W_jd3cVkW? z`uH7%j|O}5Z)+X@b^R^Rs*ToBU5Z(=57&AU_q#fZa^F}`24m@XzI2S5 ziX4Q%ixT%MuG?~)vQx}C@G9Y^;pV=YQ^^5v<5!q4<`ltKXp-PB6iDttD8TB*EYhrQ z|GA3Q4HaAHIauA37Yj1dtd85^meg<6tWLI@iq;ivB5i}Tk+%F8628UkAu-qdER9du zLe{9_ibT~G@)Dv@lG;T5FD-xDa8b>8+s{qe5`cy(-D;K9xLx7q(er!ZxRZSyhb}f5 zOJ8K7@H2#4jxv@C5@U7YPqv2Z)`nXUG~$3v_}>Y)oC86r`(tGhKu~=60`ErsjX(Nk z)LF*u%-Nh;A(}=ZmKP={!YO=}f2GmZB`u0*9;r*LP&Bh&)6BZWvx;O|g=B<6AXISX zavd!=6LJ}$TKYk7f?yt#tmy2S4sn}DN)2GB3{-i)v`pqay}%yB~9Vx&c@Q8 z@t!ZWm{(T^^R-Qxb7!Qga>nQ7KD5Mns}>G)S$C3P5}7+)I?JWRZzg8|dQ!ly}0-=ggn%e=)xpXfzqr%`Km`q8&aJ-!Ud}hWB8czLks9 z*Z9e>#O$o%&>4@4o*u4EovL9tMPPV(h#JI7T{K#Xex{3}T&uje7bZ&K`JxnTU2{8t zS+^mXQZpRh?&p&DLH^!>8TDKMQzezZF4vW1TCEGE^7%ERJDWz$;*V)nEZFwz|5HH!;WUUH(epb<#?P>6nkF2CVQWMh@^VX^>a)ZBR&A=F)L> zvNq)Wdlt8y%`Y)SBjQfXQkagZ%@>_VNwxcUl@wLh){g$XNVG(yOxYUJ>AWQ78C2Wq z5t@Ki93e$idK_{;xTvEA{OgT9BYJ;o#G&v(3xXi491|53>K1Ueh~~^)lhOvt0&4cS z&R7Sc&z9|4+%LL!asRlwJ+|yUBO$cNY52FS*jrd5yErIYu`bnxc;MJ)sZ&?e5b&$p zbYdj-V96Dx?iO`Q4>(jAn9-_mUCe=7-||JHa+&*h0lmNScU8E_nAK{1#`JmJyO8HO zs6LsBS*SjSUqXNH-wNGw6V+!jrS)ETW@@mMua)wiQ2D-dvX;b^B+3E6a{y4n9J{G} zwS_(MGn(45qLdwckLvH1==`YUuKVx42A3<(Wex}*7;P+bGk%=)xk^Sj1ybx)!4(S! z6sH@Whzp-n2n)9ySNTco!?c8{+>)4p=;>U!dC37`Zq*wSamChJ`p&&M^*7kqSD}F} zhV|5$90by_IMI#fqlvh1vn4HBu+}X(pt;$)#Pf=mwRRFYqEv?WjHpm2Eq$q5x+c3N zL(N}eLR!KV14LTF4dcTl2ZTEr2^sH_1ByLGPoO+`9De}b9u~s00|i+_GnRe>cgt#8 zT243pA@A(wBCqp|^dvi^t#aM%&8S?}JWH#N^nAE3@ldYxd_GrtE`hEU*vL#YI z|5#~I>szB$<64-GQO~x%#E-05#ocA03cK;djR=qQ#JH^+#oj;DhUHQYZ?$7hX9JAPAwm*#B zNbL)&<@j`iZ;PPt2zvd$<~Vx2zZh@FKrYZcHPo5E{f+1^9*_jt zc=%HsmaCBt0&O@cC>QEg%6a^Hv;ywGE88-6F80{467mZe5eY@`mhLnnLbz%(uUDIA zSc9jO=3LqPIc=U&47iHe^~G|K1wmW=tG%)h_ETA}X9KQ=CAG|U^7rR^oczB-!X#rn zvvA-1B-hz0PS0Xq9m?r>I>jlmKfgOmIDp=@zrS4rhj5m*--tQyX2mmF=M9qG9rOTV z*JNA9pYflq4-WiRPN19!QxO(+Dk@@tuvdhn+8H|MMp#;WY&j}XtrAk7l1If72YD8Y zGrMHiTPk0ext<~XkF~TEDv}E?ZMWq@fXTQm)$4)EZMj9_nRL+9Yu!5bLog zD-3M4t|I(OJeOM#gP>%q^|M)OY^Ty3p37QchhIf~@tbXz7Y_i`-jzMqC_e#Z#mVo>HK;j;Yp8YZ3@<8le(p@#C zZeo5eJ2NvsJGbUeurojGJ3AhY_JI|TfLu7XK$kTmDmB%Vri!xtMtFwglnbvBLWi8E z@XJ`07pYw^G2DE5XkoaS?bBMmW{;ZfQ+(g9K-s%S*wYb@-@q@t@fk(&vE94_x0N*+ z;Xid?vE1F$WkgTqb;H=60!^|P&$CtlqeM6-j=hfoS@)j;SR`*05i2QZxR}|!E_su} zZG_);FWc$vpkBsNsC#MT$?nDWUh5c{WM>*u=1xYRAKBMfCieu&?deCWZ471bsSYH0GBVxJzDmWWTA!$jIpd(oJoe{2P%)xN>M*_l9)ipSr2pagUZE=RGeKo$Uwkq~RgRlTz1933Eq;!Y~O zVy<%KfcT^fP8}cHMa|s&NthMN)?pU!d5kwiSdo*-=l&1c_HJfl^)CJpJhV;1N(mF< zUM$^|bb5{eB|hWHGDW7SXUe{yUDq2_I)+fF$NyxrgJ9h7X<97L1%U?j}%W>tD3U70*E#eQvv2Tvnars6FWH^dvAYM?y-ND6zoa=gu>7wu? zNfM~Cyf0M*sOUl`e8%3KtffW0Ik`$bN$AGY>M4J=5&f-tD!9#v3WswxDvf3L@~hou zhh_~;4Kav3zlmrP|FAfe`RMJ~xq1D<1mkQ>usTHQ*Pz#RRDvU8iW~vfqVPwe~S2v8$ zJ&y^D)jP3JvXOWqi3;R5{#|4FznEre*E)|#4F4YlD{N>{$Y!$hk(KPiyBO(OTCGQ) zvt9Npse|u_6CXz84W3bPnC53mKJirYV|-0P&bm8J_0h^$_}>=CO$rS{AFPaS*Q2EL zVusTU=hAG3b3DPKM`k$B->!I<(9?PV%Vjt)f<~CmDF-v1!g-46NQErMll&CW($M6v zzY#6}3pA%^pa~oV&7s-f-DZ3C2SqCO{a7EjGFUHdh9}Dvf!zka6>iC&f1EIXf!v5L z2a~p_OI61wbdd^$@@nqIOZJ1m>M9fZ@Fq&8$Pzp(*S>vr8i|j90TrUB7&qOj1d(VR z`JCs1VY}Z9H&<78THaA<=&6p_#W$gBy2-?h{6IxXzRInW0-?DxDSMfWX3bnWN5*#; zij40JKTH;b9F1qB;QWi6X%~@r4|%9vKKHlZCuP>$mWH=j)=5UnNObbCdb&w%B}pz% z43Xp#*+P>dTLd{;l)???8w$`n92CydFv0sP3kmi}c>yfE?b}hXsT7R#4I%B8to>o z(V`|&i_GxV2Hb3zkfyb4A3Z*6gg*g@*lzE@mRPWs4PG`Ea84j%zg{EUX2FSSn{oax zaB9!56`iTGqcc_h1?#4uM4JpJkyV{Y6g06Xevdqt_kbWxOUcs404%Eo}w8Dy{% z8LVBFlSy1!a&SNnsak1=uDlA{wbl4$g)z>oW@74BlvM0AiAt4#5WWlS59PgPqr96 zv5Dn@_~nGdxXn$Rvw0n9B4^CLyIIc09LKK#>e#byrL<)XP{Pw=^cYtT+8?9L&%Z19 zcK`^drl5P6%{}HU#3E=hDsikhO2m79YMyuWEPF+mAqi;mAw$;SvVZ8=Z}w(eUu?r zm%Sx%3h%p{+_j@bA@!`$8tK?wN+aEipnmYCG_SYx(;tA@8AvyD2udD7gjj2{rGdYyq{8+;!30BJd&acKgU#Bz}cJh{-|r`>Fc?aCurEQgVTeZ-D91>k59d|GA zs?g*3_?`gRi^w13gxS%0oX?fCyNE7m=QP@Z#*`DEQ#yz9Pu(P`a(%COlle#^fOB)|0kaCOSU`Yb|CRcV&Cac3}uF@o+3+|_-wYMB-vy( z{gYLaER?Y_f1B@FnBG%%8+k^eH$srRy`M#F`tT+83x!07&7)Mb?uY9}jwB>c_ICe( z>Nc`?{tN0Fzds6=xk$o@JSI=dpG73cuSB-l9s;}yI}0FcrGy#LqcP)b9_o$zwj?Eq zR>uF~si|_GW)7r@nb}Ruy8kN)E+;L7{qJKF2 z8cqY)W_eqKhBCPqaqNkG=ZkGf-a}Kin%z8aGMZNMr;NraG47G0L{hq+e+4Y*bqK1L z)1whSD{F+kc;ocinP*3v6lGG}7u;In`F?%!_8+BX#Ls9z$q4PWAoz&OKnA``8=_UZ zvDZH-e~s@}swii1N9nrH;>m1eXfH7emo>je z7!+P-73ZTs3`YMGy2KM+R5GoD^^p+;9xEk2>B zYyv3j>L~krSJT?00vKveXT)I z)KoU3n|ZvJKWfEA_TWoVByp2CRg|Nf1)#0{+myAhC)(_l`AX^=(F^z>RSE3k2Ke6D zFU~-ZyEhwp%_0=56R&`td)<)&>K&$5%TnFvdVV|X@^AWCdQOJHD&^{VSLS&rysPS| zR2Tb|A}VJ#Y_28ruprZt?mJUQbK#0i1-)X`dQ|gyI}FiS!)`7UU)JzB3aK+eG9KY%FVbEW)p;O`pmlRJtdb zI~5x%Yo4cEHo;h<-m++(_qrTtj(zKHac^AGl4Bk2PFshetJ0MAZ)dKs4q2&6;K%m+ z2fhcqu{RO@R@3#G##48~=z7?r!)aW2+lE7Hl_FK0aeC9^)MuNIVW<)jTh-_obi45R zYnfYrr9g$;lYEHdC)YznIV!GXZn^X1j7Gg<{h|BMLNnMT4voZ&_Kc;-unI_G?-0+8 zp%bnq%eBE~t-2qjc6zcqUmTRbC~eQ#*BUHPX^9WF1@Ap-d>_=}zaD-cejo?za?Vl8 zu{biD$`S0z7Yjb1l|6nteuy0G;kHxR!)Rkeei!L0V^u4~CoPGQGn>ERmu2&WsPNhb z-z)NzK3I7ij`N`#m)E&BhBxBj`lUY+wl`N-$R_@2?6!Mwh^=JTk^F1Le9qUI&p)6? z0i_~7r&!<)l)Yhut0=4(NT$!0PSw3s%&-JbS`Pt(QNK(Y(g6l4{zU6Po|^AYshsZ}K6@nx;28-JjxN^!y}l^?a=fHe$8|bChwEIFqpigmRDa4ko^TgD*Nlr za~G#;cB9-zsD3Sa(6SQDuVewQ1T;c;)xd>>at7YJE47GJMpAFKXX_qeS18uC$si9| z6x0`@P-l^X98XF^ej4v=$h~DZa6;)s zHgc24i7uZgeBp!)S9ovjp}7LH>o6sToAFY?f2+nz(1neWxgO4;UsbF3jdxs@=EJwjNZ8%=#%|aw2*HmZ82FxJeeJa_nkHPh zsu5UZp`EZ!Z52jFF!h8A@9uY0@UKQ>KeK>7Z`J9xjF}L&)rFN zu?kOOp9n;9oK#zX3t=+qMFB6^Zth@2w?Zc2s=vxXn~L~#ljRsxKifO;V_tdVbFlno zYvzUJz>(f%6e}v1pY#10Lt@vwJACeM4rxIMro@EUc1e0neOStROtYbd*%%IcL%o#Y zpa@6Kz>K(xVe!i6-U%`rljB9#4+<<|+Z4?2rDu5v#SQ~4kt)8;^1egmXjy%!MQV8J zEHLM^C&wq0V_o8il`|1QuvIoem@?F|)f}XJ#p=0(y@h|HdM#<8vFfUyLjXa%Lf8a{ zBY;ksVfoS|&g=&pF$26s1tZuW>UY|?rD0!VVRDJmn|ImP7kpx@+712M=Wg}6_a45J zCoWFoZUpqCac6=%6Z8IamJ<353YKUyc~^qCeD*(yDe}cySx#9i`9ynpkQ^gBc6TYo znTV_8Jbv0p&8imqIC&K>vY6oef<$40VrRyeNBDVE@Rw+@p`P(e%Qf+FsM-2+2a)(J z8ohHNQT6e~I5o+MshyuVHLs@W3Y?l1;?&gJHvxN}92aLLcfmB;e$I;gu=qGD^D79l zO)L|!OH&;0@vX3TOal4cjHO2blOI1qxS^*JmiZ3Eipc698zV+oUM4MMRn};R$p~&Y9SFFvM5KCQ+=rUG%@)xjUjr1IOa=rsxH4iqC zW8lBw6gR7fU~PIU_bS_lcKaHWN1^yDJS=TSm-ok7vQV+~QxRaynGxx!R%L@Xj?9!1 zH2UkTVh*9`A4A^`?Aw)miXY5q1!|DA{1>+2a+urT{{dC+PfVk#FSGuAs5nml$S3rr!f+ON9+5 zIo_1G*LWl8L-uyLuaWKY%;|y1V%pBA`pGNBqOoH$v#VV}wbc8WG32bD@eoDwY9Y|Z zp>KQk?P7bAvxdg9k&ptyxNJ(3iC(|OWrk=#RPNNRw(!rB@dj&Cm&D#uVb!b>0t4jW z0rLO*r=_zqTqDv|t$Q4FY57l<_SoC?1>O5GIY?Mc=hnuWdL6398WOKQ-#NL~%h{o+>To=0w^2i=w5!J;*aY8-KkBBzw}9B2sYaG=?$Y1Q`V z98Puz)6iU=fksqQ?Zip1D9uu~GZ$&1UFgRCQmGQ1(Bup?M_&c*`k+yfffn`pV4MWZv7aLx2OK-1BhhValnuuY}~%IjQVW z?BuJvGV}C;Y!&O}>hPdPGp|_%$yRYN=^ARj6@{0<2$N5W8KF}J^#=@W)3q1kLb)4_ z@ef@yR?oKQj%f7^;Pdp}JXX)L;l$CKThJ{}mQ84NM(g=f+BPt(2dfNkTdhmzjIIXx zv%uq?t2PVI)o#>G%%w<*&^MQIeQdl-eTb;PqlRl z4CK^~@396!E?pe^avLK^aD_0rk8k?~K2|N2iE3(=n!LcEK$Ekc{YHca_YXZ%tL&Rc z>Wb^w!9}Mhm&c|Wvcna&HO7361TWlVH>uotlH=|l){nL!;fW222OlHMG*&O(rVf1W zVRM(G$452DsTEf$Ur?UmtcMG@Pwp0lA<=c#(J~V{O5*ar$V?m@C4s)@*q_2&(=!1O zOvZi?PYvr-@J;-sIKR}~7}4wKBDs)1S@FT`{ZYN0Ep<5<_p%%ZF}xo*d4M?5K`J7S zbadk{9;DwhLeHy0Oo7<9){n=k3BF_;!3Q%bvrm@&gi1gbo6gOuZ7O{R%-I{8jXr%t zlg^fz1&=yUHf4i-xQ@Hq#5BQp*kJAP%4xC|^>d1>-8p4#m%(}0TidWiGK_m$)k;kBeCkC|1+sUI$JxxOL< z%;nle!8#f-B619-_^skPHVXy!iSdPHA5?V;lIvrfoL?ZRMqe(9D|^RCbfY(wQ$rnL zBcTRI*s^kINPJJCaF|b+hkZ^j!d&4w`L=pKMO5A^FNA^v^jQ8vzb7$6TyyMMDYTrS6( ziUIuT4!vDtYDN~zk|PT+b$n&pjmSTEz%Fc3^v<3s_vLW`VB@S6;k6P1LHL5y@FaZh zvh|CP3dUMf722Pu>1T`3kN82pmwwT>u?~m)sknbw8y5q8!F^*fzmr0; zB5{jb<(%xyo6z=DQ+WSKQr$4yaepbPp`nuDF7xcvDY@}2buqtgIbEd-XJk_Y?elOv zx2q2rmz8jR>jvH6PW^q)5+DnC!uzKi(JJ5$CT=TndukSyxTl+4?C zaEd`U_tti6(Q2KV7%8U7E+yfXuA#HSE&a%ILyO@Skk-kDb3{$cbd!`Fet`W1(>bqd zVDd|PRpaVx_6Z~^N;r=r84-t(WGT;1<|$`{gK__Ld+Q{Tr#;jEh0!=OZNED_S@c4N zI+cYi05s^TX(={-wXUXaUrkG%q^Nkyh$c~~j2`8~=!6_K>@j6=t>kau7#b5_C`_DR z5xYq+p%B-u?l**_@oLRzM#pmLhZjPTqzb=i;*uU;rHjp$oE`4f{n$c9eX zQ`(_sK?j#f8oUG6E`d@Up9d`8AH|8R4tCtWSL=_$9Up#5Keyk(RQQ7GrgI7=2?s!Q^?j=D2> z;6tY-ucd;h9WQ{FK-^nGBYrXfb(bvh(J|7_LN`yPJaN{JMpP2nyqblaeeWtCE^eNXu-Ir#{vf#LYzrokYBl|ELx!G}bO-pBUpkjFrzt8ZyKw=8~T;lsW zb1EbBc?vh^7d&zKRTh7Qw~KTTF3ux-Vv38gEI!U@PrY=Ct9K_Vw{cpzYq{}Iyw6(*!eyKj}1#OG20zVDjwoAyZ8^Ol}>#whN&2|0B2$yqg1Zo z>5cw@A8?6`v1~MJUoL$=F_0+eD{*VwFNy1Bt|u6KBXy(JKWbVAn_bqjl3=n`?!lo# zrOH>hab}`qCJIBtZDW@V`ZOmd$499_szK6JgzINku+`6mHfL@v1x_+!%>Q9w`DWI@VE&JwQ{j%oAW&gL;01w+aZfv?rHbTtR7 zDKh&755CA zO;#Ywh(6=s3Py@J02&ds{XwDN`U2;a*ZaE<&0B!NVTF^WCB+KSw-SYKwENrjz%OepaTeM0soDcT$kuEYd4@&11AD&qsN}uat-$?FwBfNbW+5?_3~C;+v_`T(HS^wmr8bc|3wbI_S>7 zeE4%dL`AO6t<_d67vdC2>*#ZDv~e=)!_-R7F^0%_*fV`*~Rq)wt3!2lpve4jNO$^x;oBp&I#4?0`LB6BJ zAojefEn=Ug?qoOx2PqMTDh0?;Z1uj?-Nmyue~M!kTXQc;vtLX`^NP%hNloNSeFh2> zFwxEe^u{clyjka<8qHPS*lp^R@9qOOX-Gl0NfwHfShhufBG*^H3 zl-j{D0xnHhC-A*5_MIoTRkiV=V8rl}b<`y1HW~Lz{6@k<<{qwP%TC@? zxSAK@!NKvNV-`w>i7Zh!TYv3{2(hvmYqcz!`Nv2oM`!B%q(@T zK&1R6v1y7-zH7Ey;J1@t9b`Eyoj`>g?RGkOmgu+NxsGcjhhW#khgMq)gCsi7DAw9B$!! z`F}x%v1)>wtlpc7!~16wM50PA&5OMkexo?tdYch_lagTzYIe^qs;k*^o3Uzae$gh= zPd?rX(D08hG_qxVmi;n!>JD+}tDLsdrbSX(mbAj+c!7_fd;jT5Gaocx4rbyr)5Q;| z%-ZBzwu>L5SYbt{a(T#xptsRIbNdPe?aX&M)pha$E$(K^T%vjpr`+1LAYX~ENn}(Q z0zZ-e*=>^{K-1H;hwlKM?t1Zc{&-f)#|R|Mr7o%ix%#GDU33I2Ysz`cLj43CB=v8# z+%xA@4w6splK*HGAJ?j_;)_QS6<=~JqMDP-T=w$YtL9jMM&x9s+ximyLQVcaeyNP> z|I&tuv~!_(18s~puWkRmn3G$r--tYpQD1UQ7?El^()m*e->Z~f<25T8nR@vn^;;58 zv~or&XA!k+6a5nk*?4xwfhTA0rO^**fW5X?#LWmNNfBy@7Rr-aPXLe>FLKGq8M9W1 zwr(yGF~2blMSRy$XWyz5*sQqPy2&H5|>^{Nc=6(&Xg zmDnVKDEhu|A}Hi6HEFp+NuG=V6B~%Ma)pa7P$thjvRP!^OmZVEqsf{jQ$0-{+chw_ zFTw@=qP)TuYXhu9FB|#RAz}}p=yel}wLoJ}6^p`zhJ^U`$EP<5{4)oOH@;qHaptTq z8zax+s9vE3ep*JVz@N?5`F3r&KO^J4-zB)Duj(@xU*E+u`Fo)`-!Wt{{zscPCznA; zS^5>9q{aFD?IfOL{LEEN&PdkLK1(2-f08~Bcz>eQ(J6Kr;a~DIK$6A6iZZLxPbQJY zr^Khc;w}5axL;}axM_7FItfZu(bEMg8I3{(})h&ayG&>+<4@bV9uBzu7Tq)dF zO#K9$*bw`cGnGP}x+uHaRa85|H34TcbUoKh^jg+1Z7ruQqIU6CsvLVwU9#4hu(%qk9%JzU-SfE9>%x(BBG(P#bWnS3I5s{A4A ztdn3jd&3vN?+QYH4NF#;Jo9NfV%=Na0X_BWBk;FUvY5f10z( z@(CM{ZF>+@|-3Lho!_drFvxj5zyS;s%pUjDJgoaM<914MhWuH)&@Zmfx+lH?e9 zi`#SODNLv1d$S!`6Nwg=W)`|98PVg_Qy#8WLp|ktjA)^HDxgd@2Wl1;;>8qYzGh(u zzSypwI?{?9kMqWFn#oFIZ7D}sQO&|4+xa@z?iwhHXi$V3`&I&tHT4NlAq9A`nJ$hS zH)sXqjyYNZnMkdMaC;TRnU46U4%f)iagYs&+-&^=JrIJv?*xSbD?(S&<)=bnFZA3d$;2m+(%#fU@zs>HpnCEE^Iv{WvM-~*)q`vX= z#jDiH5}356m2U%blxWzB=@^k0d22Hso!to8snUE$el1|aeI%U%+Hpm!rL8F`hYRWa zCern#xW{?xnjsV@^&-pKPu{DU3?C*udy+ub5DC>9)&ByPY7TZnm zRk)>qQ_}u~9JGLJrZhe0)|{KWO(jkRmBV;=U5w1%@0IVZC++v|DMW8d^~vTJqCd&p z{ztH4&#kCV=i2*8qCe@|dyc3>{q-OnYTY4ps6<@D8*Fl*eR$o_jTtZ_SHf`sGksOl513U8RH5kxPWCNQASbnJmgUQS^pW8ei@C=Ty8>wTy^d zTiUap!%r)VU~6h;;3l39=BFn4Ar4qi!NUQ;PA_Ii|<%h92Kzo)J;PcFwcY63ROMEb)Wa@qNc; z!LFhLNOaR;y8*<=-1c*^h0d#{j+j=VRTA^f>p2_jrQt(W-pZ=JLY!v$=fi zmj8vz|HOtO$C5o9mw&RS9>}8v#MyX@W{u*R`+hLqF4HY$mKFOAZKo3!^a>g<Q^wyy$@Vf+8gOv^vxM;wrsMzR@txCenWOL4hnAV(r8$b|$VPYDTp zmpkEoh%Xv@)l;xhifk5J$k^UWcqd5 zOMa5`aVZ~z82MP$riD-+IpThbh{V&R{TFV`cdeNw(1QkvFQi4cv?$0?Jd``vMu+gW zZ?#&8kEPsRZ38~aOl#r2N1RpZyP-iN=BN71|pdg*A5-+BY{or@-H8T?DxHn)-#E2eBB=aAoWV!|GD7qW}FI z1jLV05H7g)3PH@czd1KFl3ADW6nHVJz+Mf@s0{V$8NWM z+yylq{rPkTrwLsv^6Np4G?Ws?9Ik3~h3;gOj7^ENK0*!GVY}DznLJ;{#l*s_Fx&^r zZ|DN0f`{(u7xFoT-NRp2U1XL}*IGN3wyAR*Jjm}N2^D85SQvf~+eo{py#G-uZ^u5O z@{%fFd9;|MPT*?KiE8IrcIxbsZB9v}Q}Tu`$?*r`RJUcpO=2RV^fNa?C-7>aV4F^o zdx+rB_tQ%}nTn^uS0Ea6Zd8rFm8hna)5i;$Xk*o6cX3T{CRwvg>We2qk9s>oyNatY=>zdKjcQn}R%=r$vdxHo z$I1gDX5`G?vWk;CC{W9ApKWAY=fcZU3mAL2d7wGT@}lLYZsvolw`8W!xAf2i!&eSl zYp<5=AeJ!I%dxB9fXSu6*FLLfkq!d*_PIQnE>2G5x{TJ__G^24=b-tPn6#lE$#ip} zNI3zKH%L&Vba5vKGuqEL?ol<*H^613PLMkCukUB}d`k>u?tC+ydcIG_r$YYXP>GuG zxzp5yPYT9MN+1dIbD8ls{*uSn8?t6#(VH>@W*3e9Klc*s3k5|)ZBaCF5wrj$CC3R( ztT+Tc41XsV>pz4ZW`pJ|Xm-A#>EYuvJ@io!B{x$r^dm5_<{aQkoy?QaL3e8;JSf#m zUW}C&g=@~`AB1uN($}vpEHBNnRRxv9J80H(D!3pv=^tY5XY747pNhK=AwQt)9AR&2 zqB&bUcIJ(Qnq6jI&#mPi^Xi&~1(%sq#6xFJ67QTDs9D%?qB*v`M-Cn!F)RD5e*u;J zJ8!4xbfHpt-h|EMkL3^D$WnY+PBIcho-YR*dFkZ`jd>;86}2wc-^bv{YU9gu!=xfIBmAC7WN{xyAk~(udIkPEM=-;d;*Kqr{WVB zq@KhlfJ>Bb#3z7m&6D^9n33FuMeOT_FXojsNE((h)iAP@4OQMl1U{ghWL`*v6U;Ii z^O@)J-9&RRL~yCuKbr_v<9OgB5uV@jWW5K+Rb|qs!1#uGpO1fcgL*2Mpv_!q)`&i< z$~rKrCwZdbjy8Tfu9h7L`lI2;d9tdcVJVXvUoOt88GebmNRVpI1I%&e911gYqa-?W zW8P}{&T=UWT>f?b+i#aUzvbVjsned{=KANy zFj3l{RLaxA{v-)A_28dBLEhr$nFu7JhT7`4@+?8c2OuQKlX=;#o@9o$t0##q{#ZRp zZ1FqlNyu}fdg?&EwLHZq&x~y?6uqvtBTjw;`@YYa3W2l`Z!sO6n3H;>5)My|6XzX07gkiV~w@p%2*=A zV?q)7KnhosNJ1GD@zr$gQtfD!eqf`26mk2mYPCoI9sFGSvMnL&%Q$@tFa-c3r)=ljCaW z0d&^=mY^P216lrs&uFW;lWjGJ()P)}7rU$ezdc%MV(UZ>B?{mDyCzB@(0?sBB7u6_5opwfsqloLAW`^KR#OlC zZ%wHylU8JXclD9K^RxpYPe73EdO)~6tEp~3ys5ttm5{oEb|z)D(>VWdSb6hsBKQv4 zQQJ5g<3qaI7HpH)tA$n#IUZ~pXQ`lsj5cS+gBu*@KtWdL6As(?VK>C~5Njc==j;+C zMIX*BQV#wc|GvNN&GddL+skf$Tf@lW&^M{0v1fQ*PB>mYTj-SaHdZxqCEOROlb!w5 ztop{PT1fr9XNyy>*LOe`9K1gzXI6ApYrRYFkFOmdD;Vtb#6p+6mQPRC3?}+4FL1rF zQmR(_!!H-}(b(-R+_?$xfs(UfGB%N?koz{)Wt(L;_+N-~Qm5StB<;W6!Npep) z?5lu-xkz@M9g`INp>FZX1!di;IEO*rlc zd7f8XpL|ou#Xz5MB)I!ggD^!u_t$;i!5@AD^V(O77p$$j8sm@oO!%%kFHNpbAB|#w z#%?IcfdfAW{%&h$g<`tPC-GH93|}p*$HCBZ;T#Nk(ir-1qadU4ASBhPa%lN&wxBXd zl87Gil&AWpk+f1bpz!pGdJCS27?Sw@eb>E$BxvpoTP6kBJ_;tN&%IaU3=7+Hfb zZ*He5PD*hTb;myxjJ;-EO;Ipr1>Cjlj*17FtpBdWoo;{KCmk+I6bvLvwMfZSZ@zB} z_Y!axniJZguDye?MO}+M{3!~?W_IlcFj71q$e$tp8RnLNd#^l~+PjP-_*41d0(009 zCMdjPc;Pg)KRUV88!I0vw>}n5?j&xjuW(x-8lFdbu+Ro%StmK?6)(HgL#Vy%&pI@3 zp6!o^)6}edPxT}J{IUWO=)#R6b%#A=_~crfwIqdigAthAvLITgvdO%BG%CpOI$WDi6oE56e3LCUeIeuEi6E!%!}$vst{=hkV5X zoQV9fYRxxf+&^cSCwm&k7l@v1h50sRaO8cBEJ-ZZOW?;9+{m6A{e!nOyZKiiQq4=i z1Xg=davNx*4VDXsX`PPPgO<@pKNK{m^Teocnh|_w9Yw!^cvUw576xP8DlH1M57HEz zLKe^9VC)VSKoy$e-Naviu3YApC*3zrE47`}GkF~!QL!5b@mb*vJXFB9#Q_%MgIkp* zVSTYgbLfcd%gg>2h?~bd!IG_#awy4&j)vPhBt`)lqJ%Px2l`1l-~3j~D_$jgP^6|+ z;t+@fJi~|zBM{IDdaKTWeE);u?-<`(4e&2isfaBorZGu#1~VL|rb5tW(}vs}@n1XF zr)WA~-|>TK;}YYnTCNn@D@U*D40VOeSBi8Ac^_ zFQ+sGp+`wYp(kUn)xw~tmV8rdX7^^n>pcv-x>ZLFX|J4FdozvylE#xJEnDY&r{TPb zAG!QN@yqE+rqiX)TW6oIF7?P26bGDhEG$*mi!Q8R!`oQ)Ru%XC=nLQO-eek8Q0AnrKsJp5%H(p4O^oi&VJ-W81OqYRU$fq~M=3 zQ(km{Wy!xg|A`Hd;J7qDB1FCfPEO&se4x~c4ABFngr}$x+OULrr_Pp|GO^j^ zCOgl$mYVY6cNZ9G<_{lfKW9^j0cXPz|*z?Z|G1i zg_n3FDlZCMNN^z6x}M2lId~(1g94n5QpNPFWj$CX`ZicOG|$N z(_3zTaa!gJ#plBM3nS7*1st29qqz;JcZGy^$8PGD;`kUR+G2iEZIITO>Ej>pfs|;DiY}zr)i`60K)>HKXGW>KDg%v(s4g}&aMP?O_P%j{E4)QA;C!jK zmp9A2knQ4jgpUc@$=$fw5!us7nCo1aJN=d)%$5uG=o#w5Xf*mATo@6#;ZX2#Ciq~V z4H`v~=v@&HXOIiRwcp8Ij(Op?@;s#5DM;?-16743#GNcsWm~JNf+5azkE)g@Ji5S$ z)>DIUtz#VfN0&ZMX&^okA-XYiT>RpK@TaZe{ds1W29H9r@{k7O^LmS%x{Et}3RFB^ zo>}KCPgNuJoJhPIJ-B<-YXOA1Wlkhi6}I{k%93rruhrn)*N(^W^iD?AP&9Yd$Y z{Z1#F8_E?Fsr8h1O1CaE{{~AwDKBNdlH#+Ybj<}a#<#5>Bcqr=^!6sRIvJoi4dv>OaLc@b)--OQ7!f>axBd+`=)cH|X zo$gYni&LkOI)cdwx?pc=lsehWv4tD;*I3B{w9;uV0@6NPq7qQRG8nYJA z9-kd*Pe|qaR`T#N*&!Yx!X5R(8(%Qf-vD7rQ##j-+6-Jm0S2RVGgnxMBNPtJWgZhH zfU2eM>Gj9lv8RZtEzO_?O*ZdlyLYT}BQ*wvavMbUv0_D0}1i zLJ{+R&lm85EJZlk}Z7Lv^#+7bheNLWB)q}7T z0Xh1vM3QqVPxM>%Fl=Kav!Cwd6DW0DhdN2coyDuA^_tNmIpYKV$m)HH5xt2gf8uf! zE44YDXRMl+$F<)kd%g#(yJQMuTh({kCcC&;M~>o2KTF!U%UxmbW85Vh@M<69u6$hk zY9He+NpB(h7=+^BasL)-uenP+yL))@#&0IxdJO(|k0iV3SaX--*Ui`63Ir?vKXYdS z9#wTV{>cUb!5bA66(t}FC|06qNeP%CGccnQ77037lau=5t29);P!Tax_7bC z+FDz?XjK-`kN}e4M!*fk7PJ-bI4)>y*fjJ1z31FJGYLWM_x!)_DUW9Ea_(8)^Pcy- z=UrdZqDwh+$l&eS4wcTO2H8w_7FK1Hh2hL+GL$WP&DTzVH$4H`u=#YD4rwr+oAPV-gMpEMc6x)KxA=vp3Y0=x|Mod)_35&&{}evKlf06?kBWeUj)p`lqfAghKo?N z3M(b+wjz-`yt5x4&J--XE&Hx1BaszH9pB3kWUN?k|$!$yJ!Y^v+sk;SErT+0BXd>OUukAr<l^;8A!9 zyjeyaUF0hN2imzE!RIab_^M}JZZ_&(=BlIAQ|6kSTO+@bnkbk>p7GZ#D$wn;Pnp(!Qg0BjU!h5)-6iX#6Ts_Y z<@~Ui7Np?0PQgm%$q`ow;@6BA5V=s^_vXDEfuvRuHP(r+!Ky&}aD`F{==t_b(GMK* zoZGRgLE)!)?@sx{LW7`{drn8Fx|i6Cq6l|qejzU<=_{2VM!zt5qE12tBG@7rkd{4Z z(TMcCEK&@|cYfpqIKH`&UhsD}MULc5bE94i75f#uL=MfdO!+JXlcz%vXa5j_=&ut2 zpX1+9N&Ow|#W~tFNhncLn15rmTeF)@i*MGj+tJLyM=WT1aF;7@RUu!TLQSWG^Vg-e_3RT zu_BNY-G6?#e{_HU$T$XknRq_!wEs@elfAO(vpjtOqjC8iMxUf6A+AQuv&^r4U`y2FLdV@oY1j;8{wy~bXIxwXVj;v$olXyWF9=udXo4% zB+->Opqn*fAeTA_t3lI}Ckvl?YQjkt4Z+ zO2LT5R`D0HATZ!RV5YUR@pJ2+I?bcSfWLecJ)jxg#rw?bhvjtvU)a_9wcYOZkL2}0 zc|Bdf-XpK0Yorp7n4YhZ@*bzN_cRnXm*r^8@GwxPnUaRR(cyCJK(<{ky4Zm5Cz{v$ zJ+8*^eRF4UUK%I!5S=@h%Q7w#XYxl@L_2=Do%}qluz7;BGlqkq_^Dq$iMM%t)OFuI z4TDAZd@L5FMc8BN5Ex+HBU0WP4|aOx5LiQWQDrs9B7&KIiZKaZX>&QBbtT8x%n?c* zo|N5d8JME<;m?b4s;;eO1h!7Ys9xK$kHeOo+F-IdJM)xkQSB9}E3uF2qOQb@{R;#P zVg>HR2bn7-<~lyeL{8A^^wt%Vb{t-$$;N-`roX3}9?L+47p2F0sm512{p#@f5S%?u z_;JLxcYIZT@Qx>`JSIj?}te@eCgs`>4am-lyEDxoy zkLD78;fC2{Ud4f(zF?0yLS5x!ps8dhbHyG}Y(VKa#+Ygjb;wUOhhM&{`*ZJl-JkRv zp2H{UIUM?g!(ji1fII(fUHdpj<{&E}cRAQ%daWJ)6A9&O2S5{Z#E?HTTA9wsXd?bMu z@B)#AKg#LT+waf|d0Hh;2k}&e3j~n4HLGul+^BQV2rEN0mxV|Jf4R^%gkrTW~-b(wYsT z0SUy1*9n;uk{>U4#6BJSaM^Naj5P;7N*oA+InnTCG@SS$Ut&;=Q{G;+X(&Vb`tS2G zJ5YG1o|8ZB64WX1Q<2_9vdptn#qud^hkQ`M2g%pv!DJpJpA{snX1_?{l3p;B_LMT< zYev{nhuF5A3Th6azplNvlGT!YaE8}f7^77}Q@ulm%O!ICXl^7d_Dz~c9V{nZ24FwS zx{=@fU1EnSUok~fH zv7a@tSiCi=+1x-C37m+Nd`x^y%B(1-evaR4NF`g7-9UQuRu^wUVljCH^JSGa2Ac94 z>8;1E+9#;BP_?Kt70#fVtmO5gnFv_7lDz&_dU`88MS22-FvgIGKtlG7XFUElN|9SS!G$)PpQs;eld*rI?lrpdJR@uV&L!FXSv3o!1BUXQ?z1 zom=Dy_pl~j9z8&wIJ`n|(42vTW~&jqMcylcpb@K5FILG^iKUg6{<;p&;(`*eE>i^h z7iTheR?nwzlwoxXjI7+nkyPHRbAe=^5ea=CSa*zt2$dC|-$F8)-NHXsHE;CwY~eHQ zIVwzAz%mtriNEMB>A2hEtLi23GcenQW^GBVlehD50Id_Q&h7Rev%h&ry{M^?9|6%) zbuTeEdUna~ZkdeLji#-;sZ*Y{Hs;_n7@gb4!*;nT$5eUHSS-Lf+^02q9ynPO86v%i zbc?R}1=V}PC-9L7>-Y%P6C%fG2QwEcH16YSa7^0@*hz>U0*r!rKhy{@(huasq$C3_ z4%13J>$;jpTBNP8-__(b;+Zzl6Mn{PjV(l{2xn_7s-=dYwP2#1H#CnuZ9!pPxS};G z>~eYJI|6FUY^DfdORWrKBC<{q(*~{Cm=O20Z^%q$>^*i3NqZ|_I&%{~nt~Gw>U9JjiX?&>c1zT zK#KT6jxiaiCd{o4h<2Usf~dC(B6NId5Or@iXd^ZONT`8Zy*K_qAarzKqu4IpgIvuBXu=W!6xi~pP#b>tTuwv42|1LciVece zj#NrGHcdOK3T>kggGEdnFZkB)Geq%@3%`~q`e-a$Q*a7PPM^V#;|&xg7b>ZwFH(Q{ zd?w?Q^M>+YMF)?Q6Ne0Ku2ZDKfZL_y1Ca$-hcg_wsiW=vK_P*5;q3*&a#a3%ss+__ zpXmOaaL;7_k_P97lPgE*qr9yPWx*9i2CfzyB6~Vk^+V*k zt%|28H0l_!XL03ga93jY4Gb223Bm};E8&gW4+x<3+TwNo#qR~JMG~HYqY&9gM1s@Z zYc2hPV(=d|R*YU2R$41%nFgB{$&dV_T5{_Voz?#GGQPcAib+DXy{7siXf0KBMqV>- zEv_{6*F-}d96SRheK0RH5N2Q_>rj}1S6bKQMtXDXRm3D`Gml7br0!bR6Gn3?Bm((L>5THQ@=t8L z4`g_o?$>{hE`!9QURR^BBPc49h(wy~k#i`LA2R2`EWU5gM?R^+kjF~O4oj0gKMsFq zeV0`@1~#aHJls24YXmGLynKP5fOP?k&;>9;7ch2b5My2lIKwi=yby}l2n0g$rJ50H z(2US9;PDx8&3nmj&>Y4Ay|ztKTh5#vh_=BC`K(2n8QLRB93@=O9+kk6u(-1)v+VEv zO=j8Gjq7~buaS9WnYu{cks%tiS)jDno&dD6yac5sm3^;y&V9-E_4(G3SFTFj zQH%i(kxVJXQ&60ic%A2I(QQZmNN5Lsu2>Bmj8+fJiG0TmJGjUhi|ZMTO0kHZ3o&%N zege08T_s>pVHkca1!O;7CKm+pb2jG6rsxCmgnw@JgF94zh@+`)C!bGN>vkYOo`h7q zH~Kg*#3~_`iMYP~;m)C*)!P&KRJ$)HE7e}!&0!6FaiYF(XNVmmPt zS$}4Xz>dw_F5VRkd|Nkd;N zKOdOZ=X_;+Qq*bpcTlHYjhs14od$mGwD+=OYjv6!TR-@-42D1K`&yWL70jV|9O^d# zrwd+E2xR{nnWk{e5EH5Ox~y6&zrI#qhgv^>QSj=?sZ55*4tlx!zpIZJ&JeOec^G{} zHTp@RcoOe5m8GBj5A+eKf;&!9>M@RT*^pGi=s&UT2oDWZHSaa_X95KR=@62(+pci; zrl>3+L8*|rW;(wi^Wp2&pgu|(a$a@fuU#z_eZ78VKI7pSug3Gq|7<*8e${yX)*{_d zTyn7-s~|mnoI?Y7QMBa&ZapgAp37S)g~w>gSaKOQlXO@+~<*~5bi4PoFLFAcgtwE zbU6q)Ox&NGS~_kvSziuGFM7?yrJC3dZej{G4Fiy!uRHZHkS3DpE=40U)x~T1(yQbhx{J#YF;$v_v|>2R=&vI`k*)s-=uJ*pp%``L zoV#JD1J?a5bW4IzTK8|$SF030PQr4ZEJFm^F=9`#@rxJOZQ7-dD3v@waMO%XvK@Vw zLKm~Ez>C5VYJ4A>h3}CH2iN2Ca!9fOH8lJ?v)1Ywcdc%7*XkyP=bN%@Pa33O(Su*H zR8w{fdyw=&QUA_pP|E%jMM`#L$*0^t8!J!?oQ0*-K9b3dVz-P3B=6YFA!R+WK`tvM z_hPIud}3k%ud?ASCKX1k2T#3eO;a0e5-?$(X|P{9pcq0UmXL?mH4VznS>#R_pObuW zC=5q-eWFUaz$)8AjBDG|M}-_27mRMq^_Bjs`XBKMybLQ>l{Ono9s@T>0&!#Qe(lc4 z>hk#T!}D(Wc$M;H9J0ro^Ga#6zA?t^kziFiZO8@APJie^Pq`%GbcK1sGgO)h7RRw0 zz~R~6`Xi(jL`99e=?KwL39h)sm0(XGVRO)2B#7lVR|;0)FGpI5RiI2upbBA*So#fJ z#|Rpt>oM*Mtt-^g_}3M|ku^|tdiKJ_$&rFpe{U2>lDDJaYSm79b`O;xNb${^GDxTl zU(UiN|G?--ufz}Ojyf;t`MO*>3GME)EPw-(e0op&Psyc`L?=;>+Nx*dLWRDBq8HDW z%y#pxvEmYZbrCSPk#K88&4J?Z8}Zx8@{`J))lAC1NOKncu65Rf>~~u&eE~JlE-A^$(Cb30 zY_B*je@xfMZ5AX<{)5HSoV-#7`8L>b(?2w>%-ko9CSSgx?F^Xh-)loXO}tSDh`gwew6JA4Yi z+^O|~15#Jth>b`rP_nI<3BHp;tuljg%6D4@B5uMI+^Nk^|+TwP9{1h3FJSoAq1;n^SpJWgXe>iSWTo7) z-Sej_pLP8qrn;V#D!<}$k`j#ME4C{NQ27-{<+IFG6y(zu(j<{q&+Dsnk}XNeEQBp7~22=x)f@myV{WH z>IS@8Ze@E;-qeRxpSDW^#y?w#+AL;~U9~>b8=&=kWz6 zN#2$C0Sd&&kah(rx3+}v8lmT%v@3{-%#CcP0O|84*=j>%_)sqMdeS0~8!;j8l7QtH z_`BvnRip4lmCfG8!@DM7#n~aN4wBW<{t#%(M>*T%Bw_g*O#iba5ZfR@HdGqjN#r;s zrTWiNDrMylTPivxKC@q1spxkiE(0R*7`ef^veV;JLFc-do$n>?Mk3gtHxk1BB*)s- zR2IbXpn`Azv{-*D^;DL8(|WW>Z7(`lK5N)0wwvV5h7L{5W)SEnxTCt7IgWL@h&H&G zDc-Xkq8l-5dy@|Yti^y3qPmHF8s4e(POaP|UIuKoHMdX#S&&f@9q|u@Bw9+$E>s!- zOQpA^OSB}!oKD!4%>E`U)v!Rcnj`uUN4AYvd-j{u#13<^w2X*oiu`Kpyq~ifug`qn zk=|Z!EfT6vz!7Va&~Wm_`K;@vx}1+BX^))j?!)dT+|w?QoyM9Ecjz1`M+OEP(JyE= zeaiZT8^z?G7CxQS-;r~*w@d%HL=1AYd-n;^TsoU~>kpbA5dn0`b;dXK4 zSS*sgks_keT^1oy_CcG7QmN!1zByB#=AEgZ9T5GGHx|eU+nqA9d0UHSY_= z^GUC@ZXa%l`HtI+hymY%=yG*QTeCzfE%yARVW_EY>F_55{kE&m^ zRtm63MqV?PM7ZFC9?Q&68J)4@8a0%qGL%3S`K5bZ6sQVq`2wsrVzQ?RNFkAo$f zFbt^M6f}G0Yum?|`G&=eLgVvqt*`t0w1TugpTL4sZ3BCWyk)_-=lv zbl`E37m|g`_&#L#x9e_Qn6J9ER4-h?{>oLR(oV9qJdTaZ5`&efVozzRGXMD zwI+{{^D2jPEBqPoUa1(WR{;5E4ANu1B5+-bg>HL$S-mPp`Zp{%n>pMobGS6kYuzur z7BiUPwiab^TS;-ox;~4}x$StajIq(okr-x4O= zkeWQ7HOyLc2g8uic9B7)jq}N|xkx5DLGVV%tGx3pnBzPw*_8Mf_Y^C2i%IBEj?_!U z;Ad27)6Yg4i-efQr{-))t`>&+8Tm8ht(^6x>x?nmlXpnrXWYVbE1TJ|S?X*6#FCZvpTRg`6B2*qg$n*keO~Gl zC55h!golm0bDau*MukChp;YKKf2p8iEfMsW!*JE6rhJGtWP7YzRcpOe1p=$CwW~@p ztt0-@RXwDuQt%hPscMtY9KIOxEp>y!ivy;bjJP+)RNJCVqtBW?Ji2xtB@n)zqY9F~ zEk5PZK)muP`;_@ozl15$$*zgLwVcoZkNT|2r<^2#DrnUQ@TY!rmk;L4HkJ##c808q ziLM&BOzjru1*|zm_~ffVVht+VT4X?IQOQqx$DWZ)1g{|~?@=@J(*)qkUw`!rl z-C0cXYRMmPHx^o_CR?2=?~|F!qI6omug{f=yhVY-}&ujWjAjhrsx6A{J34AXNF@~yW& zgSt4g;Ydp>1v0xJs6q;i*c)7h%sLT;D7mVCSs?zj+}U&Bm7H%9k1GPKY+S!PG5f%M zzoNMASzU2$0c)w+U^<-D?=J>a*`I0@X8%xU^$z<-7EH5u4`((+6nK(!sp|eOWCF~V zz!`oK8G{=uWd~=i$qo*B@Ng9*GI>%qz4lsTdYR+c&Zi(07cv}3!3(qm8h5ds5AVWS zyGV9Xw(~}@_G$53w->^IvYp>Jkp2A2fc1UZ&y!=U{3H?kMAj<*()Q@U7{iqH-qiDY ze}7N*cM?`5yjC7tTXC?8O&cM+Z11o7O84OcDV#*<@FOEH3jd?N_ivMKdQHUl($A$+ zDX!V|%4T!1j5~3>=n0_uAO1mrurrZVleyP^n|p{+Vu!j<*)OU4RN_T}866nx%3%7X zC9LVC!J^14GXVU5JHaxu;<_1pNo96RaAO9~h;k_cdSX-pCuO<n zk(ZL)#U7w;2mITs(oL^PQR$~MzpQgk+~y*gUy@!lfbm>mPt2-4eV@B(i@9=EZ6|yA zGG{M;z+K9JOsb_U6(!!o{zmaD*kiF@R_Qg%GOP?)v!M8DE5Sw4vJ@W;=Y;nGMzb4j z!L+|F2h%bVx(hp#(2ar`jds~J@ni2J;%Ch3J49g%U+X$P+9xK&m8LavQu4#jMSHRg zw?yZ`yu2uT6V8*16Z8aJUZd?gC&A2M=X%B@n3;)v0#!knnLx4Ifcz$Qv(LAer@Qh! zzB!o1uT5MJ1zsz%KH1_rJ~$kQF@IUE345TL3m9u+HzoejlbOU?f7MpS%Byu@Aw zRD&SB8Vce_`7NXF2zj@0Y|gjjZMUqqh{Xqc2tk?Tmf-p|>=?dOCmRqV0yvZgLM)K@kH%ohnrSk`^k>_Gg&oM6?4K&i-A z8tzz&LP)abzT_=^ZT?kWlIo^Y=kDT=Me1BKz$d$SpB0QDs;MwG*v769DUQQV)Xh`m z2;s^tkI=ERWlYz|)o?nea<3=PG_|0Gs5J53Nr{ciE>?dtRoY2dEo$8GwBxsm9ySS{FjKJbqgB`+Uu-C3V2eUQ88z0wF)_vw#ZaXK; z9H^qFHM|<#ceb(US*dw78xa5U@zQv>(TK`6qaf`qeZKmaq##^SOdMNp>Fe{SF<)u; zJE@Dk=JUY+ISv1h0WSUj^HYx2l(aGcU;6d0-vIFvos(e#4Is5#_85YL%483su#cwT^3BO0CQJGbH zxnI}OUtAcHPV#X#ASS2nugb^m9p2;}A%iE2F8mCq4$*XohY4qwN!i}KN`2KM-H%Dd ztKi>KHQ&L>{{||csjyKuU0vl~Y1Cb!uJUjTm*@mFw~N!b%5-ph0dJ*A!VKN$_-x+Z z73cDS>RBOQoS?3{;f`L!6#775V=#J%kNt!%VzE7JR236Ef0Gm z6qn!UvR*2?ssUl7AA^iKCQKFhP@PD`T-{-tZ!==Q;$8Fr-9Q0{S6~iZQ)rgOEQ=W; z{Id4$NZg|<(@y2g;7RkZkXq=XPpkZDAl=&!|RI21&5p{zDt(Sp~St?E9oVg!kp1zB03e^ zKSTFQdbqI9f84_}rH6FNX+IoG4)4h8kv@gV0nkc%rnIezOPCy<*yH6_(MoLv=ENV$ z=*uypsg?C7_Pu+vOu)V|{-OZ+dc1A=t?rJ&xy-B>#$b}iWZBHjl_v?VBNEDlm`u zMPbhCMl5&h`xf<4F7q-`#uwS=U*Q*I#dLuuE1tH#q(Mg@NuQ71_XYT&WL{_Zy|lVU z;=V{>ah1}+YKlZ&?AlZJV)r`id|dFg^zR$lhwJtns&|@e0@5yKO_4xtu|NfIN_kbP zbNiVqonmrsknk!O>8K<9!vhFC67fk*ZTPC|8Orsd(L%8IvX6p4umOUS<_?&}3E~)S3WdQ0%#+)!hX-L-bO0igdIymtv#^(33+0f>% z)=-nS9Y=Xsjzp>%;DYc}DLy0lN=%m5(jR zyXs1U^2<>Ly&-WSUr|?Gi5j^xSsMAW%#vQ)j^#c^s+UF1kuHBs zspJr@RLVzo$!QODe zRjVVt))G!3Xer4K;WZb^b#x7|#GR@~bW=A17&x!j49k^=-J{I}GFIgrHi%&OHL4H_ z*aeb7`lBmJx;yMc;g`Z#Pwch-S;oPssiH_~#g`MUSH#G8FX>h8I@o8ge?>yY-*lo< zkq8$n)Cc3CYv_v`U1>I?iGz4nJg}#J>B3*{%!k?U4rkByjp(c1nZMHi=1kR%bjYXZ z{%Mi!5*o=^k^~UWW@aq?ySkaktmTH8tcx%r-5MSqy`9hw--_-(HT=EkexmVE7Zw14 zoEm~WoeFWeEHYHEY5z*d#re*DE~U?)G{GiR$@9a7Di(VGNR^a*@Q;)|CTl;Hhu0sg z(Xx1fMr*By$5)1TeZfw_V@Ee%cb1Uj_R-vpB}elx0nt)OJzHjYtGyoWRxa{U-AuIrA46HQ*=392(JvdKWYX*bd zcFSJLW|;{EsvnoM+Hay3$GKh#$v@Z`9U;$`*&#)rsBDt|@7MmP(^&h%?abP@5l*;k z-`U~Q{8VOtmv9sr=!iv*5u3s@!Jyqw3f3vNuS2%(OMmBX-5!x(sA-bj$`dXW`W!wn zc^*}`8^%!Rqm=my(tR7VmYMCc$p3IRpGoRf>HCj*vgC@`%}+Xs-F)b7XE*J>!HSs@OD_QJcA(X9_7&;PIljR`8RUhq zR`)d2ZS@)tGH9 zN^Y{}%mPYmeB$@;ncc%D5#P28?C|S17MIf%pLxwHc1L8I;h_O*2BP>GeelB0VY9kJ z9V?Dw5_rpZPUbx$cD7)qg_zJO-&!lt3JO<=M0c&&17NMYFHqVxn^4oe2VW)o_|AH< zez5;=pPs^Z5l87tty+N?!0qW)U$+5fI28YtJg=F*iWnfeEv(#ex0CM0d*@N9l1BN11j25x_&j#%A;vwYj})RE6|rPJKduDjx_4N$Mb->S@R}Cm= z240B`WW07cgF_4Uc=9;iOc(Cc09dZ-^)le;#SD6IrTOp5A@3Qn#XPs3P~F=YZ9Cdn zv=SzY#Wl4TKUKq526Ea`yJa^4uT-iY1#b-pnRHd3xjReVn{wmTF39N+FBl;@VjcR} z7XGWTVl<8}#o^a|@p0*R!Kv<%L*fN1f$v~NUWnT=oE2a24ClGD%gmQKv8B2BxnVWsc%2EwDMHtaV z6z5z{ABR@^B-9X+O>T;Hj`yJ1=oPoKMc{|0zL-CLb6qff9;!4R-ZJxP3I)ZLX?9}I z5%TSsI-XCoZCa#)uZzP(E)>_bt-ST)yQb>#;=yLzNwoYFFMAp>hJc%;v^FhrX8kl; zqKAp^@iY_<cSK@Z_vP^xlzoeCqMppayJHUvYtZN)(#CplM=4L4= zOi%rp@@aJ6nVg5X<=DrSST4eyVG<%)$de&y-iW=$HCJVk6DieSVv}+rhKtKPllqLh zMk$K!Lsh+!8|PAT);gEJ;*yYAc_pn0Sp{6?tM(JO%bjEI;2Lm~F=Dvm_AgizjD87Z zE@T0FYsvs=b2?vAQzq&H*7Xj*7nnK7m0syhuDq>C!!_ za>KWgwMLV(mVxBM2ZE^YMcW31kBYVp3ip8C5Ni4Fi5xyK_atA>jAtIrJL5S5-1OGq zTFk0Qss2PdUls4`RnhxeR>gh4POpkRp&_h_SDaO`w{pmC!~6^wWvvRSK6kEBw_JwB zb>2-z-965AK8hFFpXt7DpgQ8?qr-jsW4;l+TAs1GreL-Mp-l*^_$Gf~bl+)4Y$|dL zP{gRamy66W??J4mD$bE0jN7Fl3VBNeB;hb$5W_xu=Y!g`8{)bs3F~B#7?8Ubn&mhatW0_PaY*zU#_YqkR%h@ zffNLDHUKre7wbn0zOdJxyIg@6yD2HnrkkNKiRtnvUhu}%nbU4l?$@4nfmXZI?#(}H z-$&M9DzQoi5FhpDV_>(Qdt2D8?EzPQ@(+Q(wlgn7AA$^MYq*a}mK#6!s0U#rw@PWv zn;{p_0+yqlT`58ygxf^GVs6*amhM2y6Ju#n01o!#8Fz}A0@F1+7hMMA;6gCddC6=v z%gdStdAxu_mKPJVh_{^P+Y#@hM}wxHJfzX|M2C1sH?&DhY3eO94b1 zwLv+<`IsjhOQ21fDvj64cWz(ASyHTyyKgU0RC`_xSNX78)jenDwWMu zob2|dbWmdlKgVDkMKygvHg@e zEY^1JoNGI2-0O{|3TM3g`+X=1wc}ti70k5s4|ZCL~j1tR|U9^2}N- z%uh6xL$WizaWJc!ap%p{1;!zega3j+Ef9%ZOkd_#MCS8^Og^*u>GHkEE&Ozy7`dLG zLVx5+YB^SB`j=66J&CZfFEuvR%6!R`kw_Q*&i?soad*{n7%iVOVl|2)A(N@*LRN;JZ^&of zoQHwL^U;USNAG%bHe^29m3WcDY|e^#VROdBX?w_g ztemGn$1tb=OQ5`uTTl1`5LH=3Rhff%fXP2uDuy?^hEYGArzR~>^`v@R*ScV6d~C|B zRd?1;hVcnLm5~!+s{0C(3?WvqfI^w9Cr@BY=1i_^@SiBAENq}-gU^ZPXg!ypg+hgs zK6f{&Bb_Hl@C57|Tn{PmV1FxJ!D6I$8Kq3r{0vgez_QW~{ zEQi|p8l9V259F72*NDTwh%DJ45OZ|3SZgO#8_8Nld{EE_>bTC3Z2 zB+u!{SyNc3Gr?1DJ-mrfv>F=H0Ru4AD_Hpx*X$+s1}^}bW6kX|pA_aOaW{XokyQFy z$-^w)&dYrJvku=rEZNtE0)&F5|{GiE3o0BPc`JOc+7=d;x5MEzzgD0LIDK{ zWcN2aP|1gsT;*IBcrie6t|gb%1o{x5}3n2h!bFHRR7?CU}RyDC1Kfa(H6rW>T z(dfY`MyB09pUB;b0g9VV)5IplYEr6q`9ca-3w%5a2<)iHCN_P; z9q=mSdVqWKGG}7K-pr(Anb^CqZOUJ!$WWEyaWTF`o>@DC+HoB;Yh?nGM@Yv{t5LJ- zE;wlEW{HM@F?%z{>@D^s<0*+UYO@_$BS#X9v$iXgu7jexrs~5K4P!68RZP{xAmS&vQ=BN|ksGMyrAxn-wF}Z`Z6czBzuc`s8mO8FlP1f3!Q}Y3lV0H4DSHgvz>I%Z|<{Gx2SRQaRU1#3fPE1d)N7MMjP- z_l%VJgv_a&ZS_*dmV1%-T@z|hO?y-RO2G#;=mpcI@n7_H2b`4268JGw+D$y2W^2m- zteNO)A;F$iLf`6zU~*0}l|&}-+Q%#fW<7M zkkx~5D)|>#zKum${(8Nqj2BF~xP7ii*Uv!YONY4ZB`a7Xm$9fHQlFBAOsHq8nC$ot z3Gkm)gN#KZoVH0C0Ci%Zj_ivA{YMa^BL47=D?R@B&h};A{*W>s@`oljem8TJ`QqP) zYd&of)v@`973>^1E25)MA%aaxcb7SQAmL*m< zboa@V?vp+|k^i4!vw8wpPi3QQW5V6*->?$C&pUZ;@W1?K|L>NQXG=eE?AImU>&25R z%_gt;O3-}G-n$YAgT+B?c59Z4fAc3zH6!?;ThLJYzZ;y4OgG(aR^!!gn zMRXf(MtkiiL~p_BJ0AONWg08abIJlHTi?fVYfxIvVo$4;{YRMbT!{rJ7tqX%Mk$kz zbP7G5=r>=kG&gb*kY0P8UQZB9Wb+gI${)6+n(_zoxpYI4<47F7N^=h)IQusbAJX?m zR9$Z0|1D}6`aTVN@XpM7)AoUB^~1%Jg95zIMJ=oX>44eW>-XskFf|szDWO9|7#JUQV;)S)$dg0@tSl;w z*b+)-Wde$yTf;{PK%G25=VP9M(M1u)wNq(_DK%nEe4-rHKC{d32m8@{V{cFtzJlPs zQv*%ort+R1w1z8#O=HXD25ap;f(@v2%Q=9StA|j5VLXg{l0;VgzWqFh8j*5O7oa^l zN8LD){;N-7W31q%Jwv+goJ`y_0RIrThl+A~i6LG4kR*qs0GLiA#4%5!k$pZ5N$2fj zsac?`a|={Imi+M{@khvCE0W*084$W1G(2?3cv>R?P7CX!bBBvNr=yv(^IyPd`wBT# zb$^5m@?g3rpwdXl5`b$KZCF#ZLGJk1RKd0@C3vb92_+uqUADUHH*VQzfGfG7F=hgk zIL<-Fv*E-@+7}k4NYb|(rc-El$<~rh_A-R-da2pZ!#@CuBnvHNU;kTS!gkt^^H8!> zIjc;?4-#CAJx62F>fz`}&fJuT%<|OKyn|iMMKT5gVC=#NQU+q~*(*k#QVN={ z232jP;=>`Rp5Q4y7HVcCPM6v>?bDl?*-J8$iPNY6!rP>Pz$aXe>oA>^5nxb8pdkOa zbSIR{tQoa0IFLiD8*oe_nwCVHyT^&9)m`SQ2Mato*@@3}Hmj&ufHFj)a^Z>ti9#yH zgFccPJvtZIVR&5depH-0-Y3M?x1^3{Hc0V}@4>5=+*eL?V@_!g&DU^b!JzC1So@&a?mZA038Bl0Z@I}HISitM zr!e7R1SdFY(9T}_HAH!J4RifSR9fNFcqB^<2RN87`@2^&!P*g-%qenu!~c3f&{B@d zDvpKn6S0@7N^V<&8aOc$2?<<#nIkai3QGCn*CR0bDC3ju8}flS=lRlgK4VNG*+;rD zf2NSif0pb>926W>&NIyFk$2+fY}MD$vd{cD%VAou&~cddn}3#`IsMFdO9!izQ!UKw zVoJ`S2wu*ZN6AS-LdPFLyAq^*0NNm&myFnU2PBi=)?LpW>?w`9240Bepi#F%U5Tu0+uQS;h{i+)*=7O9wT>tlMR%SN%u?bqnBz&wcodfgbzU8%(z5-Gw5$_wY2yT*su(KJ z-pANO$Wwu|t~>Y~IfdCs-cICGp@<%KMZOQPbBm@Qbf3oCTmasN( zS;^jZN#j$@!;E)vP&;q4cov+2$2P`y&XJV#KZ2iW_hJXxnzYL{T`bS)VmV#xu))(s z$?@X`9D`!)I!b83E*AdatT+pu(<5c>F|pEoMRBb!1x-AO{Y655x(0;B5i;{pI2;gM zf5Scv(X_m~QL%>07t`;Md3{mGPc*rLTxyEeA~sNbHQw2D_bhcNnuBlD;<0}yRMo1a z=}qC@0W&Ar$E!HK`b+CAjU=!PPd0>kInSEchcWW%G94!M+CiKUmRMio4cWf?OvBNkK7hz!zXAI<1| z=EE8`KP-yQCsEH)HJ0p+Ihxav@oEs+=RT;mR)Nkb`ccVNP2n#?BPSHzA2hqbk}1o| zuFjB!!2cYcRfe9&(33|xum^)R|bIGT^0v`a>_3Q@{Y(lf(L`BeS zcrlq8x3V7-L-vY*bt$O1BRs_#Q;1rAPUY%n85y31vj&n*VX%zpR4Y&z-QRQ0QP1QK zkXN|0ubZjt0h4X3^`pjF%=VOKNC$kQTB@Hu3uAGz_8@e4{qg)J1Px^FxA4mz(oAER znLO|>ud!gE;ykP+Le>M87NXI7Pu7wYyVh9HkUUZT_VmD3LButuvQT&{`FE{R?O%@7 zQgl-x3sY|nR}|?!o!Zk=oc@OPiNvtV{^*x^()i=XVE^*O=QM$9Y=d;JS6q>#Q2b9b zI7f%#^J~O;`r2jiAZT{voWW-}hEAO>pqr7VckEJXK-R22P+4Lf{QNs@DYH&037GfJ zr@^Pxs>&PxI7M4`$*mCis?Wpa$V z>#5Zl5CTArcK<}B#%`v@w&Y}Kwrb%QR2Zzn^LL#NP}B^`DpZrOL^-|VzblsLX5}*W zeSt{wSmF}}djY8ib38upeth0H$k0>G0kctuDC*lCeMLD%mf`dMiuk<0?TnPB!-3U^36afb$9c<=3JA#f^RWX7n16J8OH)a(B z3-_?6(Fs*?^@^s7a@D`4iV^Y)n@^+ikAYRr8=I2*4baI%6u`HFA{SyUpTzQlhf&>d^Av+ zIYt(6RDXmsn|>KDcp~#%RLbkdp1l5$PXn1x)ysIn?5t1ak^Xc|AN6S}+>2gKrict8 zDi8Jhc?ui__Trwf+|D^)+tcC%))ZZ)%K_pd=aZ%i^>I^F1Zwh(>cp8?{%K=;xoN7n zULItrzMG zHqQG`a2N$4+_KurS&AIZQT4;e)myO?P&HX=q$zn@U=KSdk6wK4yo#>5T&{30tX)5q zD>gkj-6nn%Z_|E7YCRuvo>6BkN$pNjwZKS~3K%&LD+_d_mQsbE#*aC`LA(90LA{z0 zezDTdbWR>G(;p0_92IZ&;X~_rIedJY>_!Q1y)1N6Yc7v~VAdKTjTQ0X=Ls%*Q^zU( zphgB~M&*^e9xOrvko#v>9y;4{x7<`IXIYx8=AcfLzAAy+$D*L|S@cxIFFjN_Mn<#| z)xbfz;MDB1rWRVG{dGIc`D$3mEhz1e&q{gwlcLGr5Mseii1*lAVdd|Yv)V_(!TgXi zA#2L25NXFNJ{Aav{l~8MDr!4FVTD_Cmzlo`t-g|k0NLIEH%eIv%GJj zhX5XKY0B05w~}9PJv~n)zArtt3fW|?9%qHN6!}I5=-Ma&nRM+kF5riT(Y>pPEbgZF zd6}+uric*fS?B$>e|nA7v|(7{BY~a3*Z(pQZy=^lk5q%t+_~kG(tQ9-CfZmiApn>| zV{s*&l(1Cn^9>d8{1bWTeNHeA*Haq;a>A_BJ>%nLAMu3VNz_i6pX2ySvD?Tn+w90~ zLQ(G)wDQO2r=qtPdW;xCF(geAh)Crx?S<5}7tzI3DO_5unT3JUPiI{lux1unWv{R@ zKd8Oqm3->04)5~y$LO)F%@b~N{8Lg*zS2*SvZ|+%Pm_PqnfQv0ScFYD1dB+pWQbjPz|G-`Qon0bYr}(Gdm`oGHua_9DWv>P}ReGmHG@4TZqO+doyI+Uo;{(a*=^ zrJDSupGYti^*r(k2;HV3sda_`3^;ntQU?wErZ=C+Ha)yJt)!~Q#d|Pz+EFVK+TSH) zT~V0K^Ox+HVD?3)b$j83<{d9crv+$1s}x(-3Gu!c5rk*w1!fOqz^&n2s|z(2S~yKn zujTE-FCD(Bmsh4!A)gM^t@-in!bHy} z5v1Rl{k=WCBl@Tln#d$BV-;G27bLwr7y~XW*(UH+jNjvGd0I7kOdzntD%uL`0KZlN zKU6LOiumFY`687GP^1D-5uj)a0gC#rlf9C7lJkfsIXxYKs?rJ+T^R41sIbb&w2(Z# z1HS@NqGpo#QvnrU@;$h>?eJ?KSR=u#{z#nD(ItsbloEbTqDu}xHXWac@BPG|s_3P{ zPtnVzmjV$zuF3`-ehTu~{=}Q{M`1a!=vWymxeDr|E!z$51{`U`D`K6$@CD^B(daMS zV$?08SbHFo_%&qRQqIaXVpsE4i@&`ljPd+tqC>TKqw@=UMkd$|%jIw&aLO1l z!FXafk5^50U{b}DwI?s_1`sSkEWQi>#QNWd=MfORBG2&kf zPOuU+9rxDO%PRj%{YQTfSR*4{pOM*1UZsL}EZV~x!0QNXPN$~ zQ9t8m7)Onp&i@nnznuSv@qaP@7a2!&rIlxPQB2}qm4w+{nkovp6%@BUFKB&O2$fO< zF=@)K&lS-FgVH)9pQ|(q;*=Nmtb8#M?Z1NIrT?SowW}X3N^737&U0@6Cr|XMRAu%^-CrYi zKtUZ%ZCmXtT;?b>O;4$G#9tsG5AZCDR+yQU;gE`bX;QJ}W4%r)`bKwmF`pDM2+^e4 z(+O_d(}|`-=tNToIsvWigiieYlx#Yoz}f1`z8;#d0h*-{zj13usk~enHtw_wi5ubq zErU!k2Ao+xTD}=HUslSER*(>c`aVEm!Z2{9q?+%K!d*k=V~;Xogdd%V>txI8i387> zk*=85=4dhv?mNm3J1&z73JCR<`5#5aP;~KD0#DM#cHgOrMYcPOVJupj^kPU;xh(t# zN!39{ocVSKaQ)6pm>xM0km98&hEEul;3< z!rw{U+LPdu<##_&1H+R8q<@F6KQ~?f$(__MD`3}cfLS&|9pEUl{x?la;KrA7$FG!W zO6fH%A##Ik`G&h5^!y4p_Fb|Ta~gwO+Wj0Anvb~^g@f57t$m>MQg&{Qo~j>+YEaLX zp+|dy%<_PcbwYfqPqRhBym_|Q?pB-gGoDL9!x=1Q3Tn+`}Uh( zQnhnn=QF7;FJzt}g)hHLW_p(}T$HTzaqYbR`HbSapRa^ z+=pP`)q3=44BUWhK8=Ces3bif8TgklGZJdHv;N9XoI%bA31FE9!CKpf)H8OMP9hw% z>l{*v+k$KAa@Q*g)sD#85kQj6UE&EeOX>Wl0v|=U%{N)dyAhJhppl)k7s3(92Ij3@ zDXV(cH1b% z7s5dGwC~{{@1l@=IFJ#g#rYlPN9&X8TIafZ!l1h~4wM59pSj<``_CM0>}nyK04s~C z9{X~Js$MW#&6fRFR4W~+>a}V3>-AQ*b?!W+FcPxL;k1RpFdbc;_OH^@AvAT3rl}uw zLQ`Q|`HVsGhXfGx(&3AXoQ{f*t`O5b%(BK8yEmPaQ<@Nuj%!IMV4B}|*HgN`H+Y>r zV6X1$Wh_X61_jp>`tYm!2hX1A-mT6`bGtVw)4kaU_`j-qo%HYG$xPrRulYI52;7cr zPYsT~Y!rhANHQ6UfMxaJBWRKI{zzFWfaqm0q1J&RKa10cTs4CkMj|tRs=ce)wKp+K zKUcF@_~o3Yng;el{sZ|s?ZAy4IuOcq;MPt$kZE7pn|arAbW4%vVDVYB!4V3o9W-AT zOtP;6;~5%0nY>bn{Ekb&9Yo$U*D3J<{ZmHQ3QdA9C_+-Km|}JS2thlhxR)jbw+N-4 zCJLp;{*Ooj21q{;`HPkLoKJpud@0VaZ*Jk%;jRq=TmhJAW^T^3k z>!ZKULj;pD>M&&WB%XBuDK=+WfIReol96R}dO-Q#5@Z}WHXDfaTiCDj)d8TI0{~+b z&ZcCmn}S!Z=`@b| z#X$E>uW-0upsi@JOIfA@ZHz57p2}fE%pJ>r1)e@mB_$06x>LE*vH*-ksw1W<+eJrO zlr~;$CoOWV7aeI4x9u@CZNHe77H#Z+sT(`R)O&kp1DQpnp(Pp*$fd&%cp%o<*t!d6 zq-<>UEIJIf9+n?SuXit4`zgpS)gQ9n_dS$e?;jA(Ew6fkUUH@_$PV(d(FO&}GcrwYg`AEO_8y2>HAHi~~cLW?qVS zKsFZM&PJnYX12sKd;bxB-94{zRw;nL@te;pb9bas@Noz2fA1908Dd~*MfRNVg8=f8 z|6<&G^{8wBs^!~$uer-1j4Ur9jO`IE(F@;+LriX6ex^OVy1A^PSe*kbVd!gL1~fT# z{ogx)ZPYISn}|ieddX(C7VJyerDeyi=FC7B24)UM&dxzK@t7AIGTMr;YGU_DYBVHeB?-pPx-fYg_I)JhDXaD8cH^s(&2u2Z(L>>3=!IPB}6g zVtT!1HVXgEUj?e`aFjX(RPFT@ioVHyp+JVUCQ+*)aUyL8Uc$G8u(a@5^NZ|mJKDjn z&0hRKXR!M(+AQAhkqx`A+AQLSz%gr6Nc6+;Ou;eh;d-@z+w<3WcVKy7tQebPHrBk; z(7N4_gsj+zA6c`1=bU3y0*;#f>j+K4(}e6NL}wbC>nAi4#l2GgMc&lx|76a$)}Y4r z)b9U;zu^hB`#+N3(Y5<`@k>xD{+2B_?tGA6rF0}Ed|u7F4Xtk(wPGe9Au{-Q4}T-q zlr5V%SE&`s9-4DYg2Se#Y%M0C-kN>&#$6B>ypwVC6dSFVpY7b%T=m7ZHt$9S+1BPW zeH`@4YW9CV^V_xiKj$Sv@3LjaUD%vPPL+%rB|C`1RUA3KWCy3+`bm_){CsEyq5y2u zqk-wg>TozrYk&^fe=nYFjVrV!6b)%C-7@RgXmu}!zl=JX#`WU(>q*FQ(VEahUeB3i zHrB!^rK(G}%zhmVM#7xalG8BiQ_8FnU>FtK80MpsB%9h70qcDH0hIkk_yP{9K{J1# z*SvfnrUAZzg=&qp5|*YvqVv42<`%UaaNKzVWxf6J)ZR#1a39%eRTSp5nBx>PrsREI zykvWEZuKNT?2MniSm6kVWe&=>hOF~M===jT6{kg}h^FF{$oR_m0#Y`E7v5*V5WJvA z8nK7f#oWkvUX0Hp8GT;K4)0odU9*3rQ74j-${f^N=Ly0cx5+?hbEAweR?MUtE4KLJV_HDlZnL@aKEzeQobJwtzfV42)3~-~f9A=i zq{_xD8G}Es@1lFUVyyIZx6{+CZpOby3$+L3Y+HSD^k6?@Df+8a^kC09-&Wa-jfb}x zcdq48a!@?#p*y46`hiY&HJICL8^tuTcE4vEeU$ZN)ZIrxXUz3SJ9bCh7dg2ZL`kZB zgF3FI)4a^uRK9M%p9OS2NXN_4Dp>o*AjDJ1euUK>-68Feo6N?HPi0Jj_`R3&!->}Z z{R9!+ZM4=X4f{r0j2c947v(o({x(1{qJ&jf8@|O)PnR2oeo2xpJtwzvQhh{Ku^~`Z zCzS?snnLlXJiju^spuEXf8Fl2TXnu{}+Sax2{>FvpM;8N8ABZiSD;Ci}T zhHX731(Oqr-caDdw=jYEQ8983s`Z10u!?_9|PF*hsm>~%-vE3Bh%jR8K*&YF=%uq<#cuc`jbb0 z7(7{oHU+GL_g>YQJ<{JIL5Z3VO+RYSxu~~C>o+D(H2<}?372V4WN`E#febeQ7&a4V z8i$M;Pq|K;^k?oIE@;|%XN`}KTLKgXpy>YNX1+tffp>OB8`nnnpD^>axHXuU(f$2c z&$S2l@xAfjy22MCYoZ6gkM1mb@HMy@cnT@JmVy6Bm zZb>lJ+Wn8P?WxMT4&&mUb+zkvZWj8|t=zb4V|n2#<%Mq$&xl477xTyMjMKdnj5|k5 ze`q##uDbZX5gW?IwZ_fsuQg^g;}3C5!KJ;Ac9+(Lvb7f)b$Rr$vg+ok#2;KUk;NxAO}_X*KO+$qVj^U)SuNz7D3}iA zJ)Il&f9wtkEBjjFHxvfj{6zbAJ?nNp**UiZhPw1wIE-vwXW>W)$w}0_tkN2fm3v6- zYj5`K+_p5UqSMk9A>Apb7G(#QYLLF`F6SEYKMCo=?vLyR(fy}|`w~7pd_P$9rixG0 zetqMgc!~*G;Vu)K=FD&LR@8Ywrc`b4*d0-nbHx|3cYhF_~ z!@2QVD%9ED7H{2#@W}+DS97SU{uqwR{x@1~Z_Oj}bZ?-kfiJM=X;eR0)&}$LML^}5 znHPZFiZ=F=3nW<2CsamN2*>w$3n%l*XhzBKq8kDkl|th9a-PNu9`2Eu;xC@~+9~!? zW%d+5TnfDsA#nY1y0JTHOao%-I01x=SI};wAe+L+NY_g9&(d*!H#M^M{mR%&rSX5F zjtQ>4^3&;MH&a7&Z9p&_4+^VBxZKdl8mTzEAfU4-dspNzm*JTfY++mYhiIU}*P@oY zw*f)HFNK#MX10`U3M!sODoXn+anR^-WH7qFc+PnQc5R+D42$7FX>;U#Vm5PJCSiEU zs@7rL@2MMK9Ni1ITWAiPH zu}~{DV5vYVc=?tcN4}-se2*SOeg3)oxqT(yg30DvqM)L9AFDhN541%kChd82D!MP< zh{1p|NC}@8ChF>iDII-l)vk>()j`MJ5{T;B-o;R{ zN%`37Ao)g}ssk3|sPox`#Gjg>_ggSjfs(LbPFe&YXT;X=HgQlFsWui8oFnDtw)27oJAcg^^!An$wI%{BHq<63p{fvQnG4IS*#+|Qo zjV(sx1_Drm_f<@-_MUe#(BIKjNO7or5UI@W+VS)=c(|;|^gJ1L*duu|a|OjpUga}y zRD13ORW*U72mfLSMpiDd8e{vg5Wgj@z}sU3dnV=+f-AXFd_nB*|nPdkIE&# z%jGv@UeluJN6-q%7`+7TBd7dX;j>g?`7XdJK9jz;<<1;bbD(FraqmVlFTyaF$KD9{ zPVDZfwp5lm6pBcHZ*cF8K08pH_i~J-P24v5pQZU!t7IDO!v>`Yo6er6yZq~KJN(4c zdzcA()&P1Z^Ryk4No;CxQudjVm#{qyS-;&Ub7id>49AJQr(Q-NMic!w8mhy+HDwtp zuS(OlpIt;$i;~v51{m>;A$Clat~?`OQS-}VvFdx;V zHF?BPY&XAn`AuWjCqD9djEE(_jJ=O^LZghjsLYbn(LpfEWX&dr9{3^w`gQ-O^HFyH zhdTX7@WRJx@yyIQ434fm1V^7g6i1~!aUpYP%@J*f;UejeOK*nIvSuSIP5f!>%gvdwd|_%+4!Sc`1F4xwc)X$*CHhhto@bOWcK z!e&^O2Dp3DH%y6PAYjM;fv88Rz8s_8ZKc)gU|S~JzNgSuE`2@ z;d2#Lc~?p6IxA+`w={el>pSU6zH}w_|CoCd_^7J0@qZE$2ngPwprD`;QHfdwg-T@0 zzy$8V1cHcSm5Rpobt%G(fNY5~f!tmOuVnGbMMR~fL(s?|MPkIXyz{Gp5-~udCs$)=c$&i7_ns(b-U9~cPIQE-4#mi z`i6jD-*ZdOl`%pTr}B$3fPaY(xD@Je`o(b6b7RDFU|5I-j|~zAUafz-Y=u=*$U+TS zzw0JH_N`3;JF->LK83SqwI(=1hCr%Hl^Zi!9oIca(d-G)BA$RiN= z4D>@}!H_Qfveox+t#X2s{H~oMV-)Jn%ZX1&lh^l9?B@upZXJ#72(N|~h2M)mpo#0& zszV(jTeD~@CW*8?LIoRzzG{3cZngfpOINykT}gJhZ7C-gW2+0uuU9&y)TsGT{B&Do zms+xkhw|M=mjPl4b&u*g5=qV%m&W*j35S`JjZ77cKw)M+m?SaUfAvrl4v z8`j*$*V$OpmI;+rrJBrdERpFvb|zmDvT09zxn{>Kb*^G6=xwbh*@<*3dqo{IYUE&U z(0;jhBfk9H~63Q$?(dYbcwz zkV=9j4UnGY;q%F_LSUo0b&ObORA5k9t+8Y;R|3RB$VuWws>}x669kX%JuIM0 z7)p_E8PM=|y0v+e>akG@)(wSsEa zegi>s@zaPMIB~18T;5J|1T-iF46QD^PoQ_9{AR7=Ms9V>I=F3_+p=I-LNcwX1#sK9 zx0awaWi6&+mAeXU`WA-l?13uyS&pt?AuCf*NmWkppcBDOMzBum`~$sHH0@*x>-DA9 zo2G1u>6)@Bi%J${lcV-&UIJx|5IH~eL>Thp(J$Dc|L{%lPb5;nm$a(m08DT=obOJ8 zBAU*XvvYij3%IrNYH|FSr&J-syiEQ*_fIIx5sJCgIlgU6zW6dlZD)ZjQs-f zBOOO*8)PyeyO=ay47Lc0WJ!-T>oJ$hyTA8>QK?G|Ko2i_H3;&EyG;wSyzpaheO+5; z95Y^~%(n`W2tuELc zh8p4BXw;V^%$n-A|I17RN1pnJ{r0!&_b-_tX+Vf=kr3Mk71K$YU|Sc-%!u8}Frk*n zEXpM3>_aK1sC9@JaxR>diHl3+I{xF{4waN!-vkNnrsUCdb&0stsXINXIn!zLM4h(p zL3-QkG1RS16G>)JvqpOz18GDA2suiCT1RtGjs?MYF(apZE2i~eWjnf68Z6mp#2=!Y z!q++stjV+&%U&$ByV>lw*=)bwsIlp7u=48OWPlk9LBDURI54!=Mw;BnLm}H=6uQXY zEv?jVU}93CHvZCS9I0z?mHgu56>PJzj>bf{f6$DttyPfSPq2Z*X2VAL$ZT`^v&ji;UPv>gu<{Jg}eEjD;0!e~Dl5#{~n& zLu@k*icT&lj2iJf`(FkJx?l~C)6(6a-ZSxpLLkQVY0Gg{tLJ1EMC?9hOfg=1W4ix) zF8=LwqXL2TJBHHx|D~ z6=Uttp#oiq2!kSDhj)Yq)e1Xn#QsMW=z@|yF0#jP$`)FRUn17OqPGNF0Tqa}l&7J1-T@+-rR6t?<5O2q@-`u`8;ro0Wp?)RklcK#4jr!H>ps`E7G(P zyAR|TZRg=8R<>fSefJjyqwT;Kg({SWZ3(+R+#bAY6%|&s-#$d-H8exzON&03i>`ve09C>+NXwcpD z1sugZ;iB83pu`BFgykzV12`TA@Gc0A$H`HxO2-#jW9A%IH~>gIQN?0#V3ULjLR1F6 zp3I`P!$u4j9A87t9rTHR^E3Qj0an{e`_%Y-sdL`e)Tay7XGeIPqCDMPdb3UF4LLaX zm)V8#wqJRr39-S20OWk%U$P_ecDaj7<<^!U&UfQVEm? zD6u<<6PNn0V!BqyF^f>VXHS-&e*$#*D*rXknP&iTFhiIoTLD*rBKnGunB2L;5}z{o z%s?sTz3QFVuJEp}S{b?s^ETgoZ!B8DYn$l#1s}J-d7Qdr*)m_CzF?7{e*mq#sCT+U zht%41I}@^>mcrrNWkDb%54wAO0e*^<`W65jyDB6w8q4D93=&Up&gm{Vs)j58aQU6e z5rczvSY+vIV7+!o|H7U2KPcOIu2CbFI2bchCXelAwi#O*)V>u#^bNr_i+T&gCD&&B zStsg9`@f(|=a0^mY~z*`Qcozj z7cu_%+0#T^7m6$`$vnD~9jwG~Bb!l26fU5XSs9mQtkf-7$~nLoTHlR}zH(9IkIw1c zm;6YI{6r>4XOsQ=TEV@?r62qQkSrbRjnynl->Ng{LWT>Hg(ryDv+X{; zoR;I<9iZkYv`jv+;>)lomSd@?|L8R`h!xcL_$>lssGPKlbG10Skt_3@+i-d^5Gd z95i`_5&MkE8Ec1k7lmcL(iPL>V2lYXogj5iYlTJT8`)s`lhC*zZjH6Kd97 zIGP4OI6{!&N2PD)>cGSCjt|;{AFN5ud?;wYps}m$apZAnJlRJ$hzx(plCRGTln|wD z#Ad%SqTVjy%uMZdfPjPFC4-MfJ_wbBj}}DUuI3SeU+K6J6MwH0ZOfk%4}t?=P_tQ| zrnls4oe82+;A6p~M2`AiLB2*YCLud6Hd|xy8U|=qmMx2qPp$gn)~#DjOB9EU)HAq~~l6T5+kYs;8T4r~bQkR!kj%R;n{pkJqOWB`)tcDV@-hkK0 z3zl>|GXfA*`^k&T?S8Q76{^en(d2+#(ZjE+h;&rjt$Lpf1O`0T(2Q>_BcSZ}$}1Q3{ze8@*%&O|6s-R!ube$pgeyq$Od1H? z`MPNSLwG6HCx!*<51r%B*~~Urki}ZUMkE8$VDbTh+9KF!vIPB0-{UT)7jNB!lyU|4 zU!`*QvXqLF__250m{6e80`q%aWgM=;zoBd?!vaKQ%c8F+e~561p-F^^Xni-C%QKWf zk){WzgLu;?RPtr`T)|r5+KJ7_7 zHWhYva4CnH%kW?&{(K4-f{Be~*6uRvAOXGQ6A>M2L`qdD55tb#~P|mH;gP^^(Fa5*qEeN^bIE4~b$yJ6*3x6B>>?;gdp?zI+t`qf1%QO;Vs%-Hjk%N~? ztMet%zu{)5CIgp9W&Q5i%KSUOBNaKHK+NUT77>YyLKn?csa&pNuw=P_HU@E~X%BB2 zE~z?rrBU;Sg2`Ka$5{yHdbrv$>n~D^(yLPJOz*R*k}Ja}t5ehJ!)N$V%5@_xj{u2$ zKb`(`CRkuKq&iFUfVfd0QhC5^GqsytQB?eebLRU;NYc?D&EMm1SkPB+h<~2p)jQ%g zZ;!{BX6dmxflqt=h*R~pCPJI}Qgch%V6+&Jak_=W?&7aN;d4XQT?NU`#A3xO;DGZ# zC@lJs0an5!4$NKz>GhSy|Enj2?8g*85&wRw?3@kb;(1RJ*!f8jGLkfF+CP<@TbyjC z%Q_tqvVSe4XJmY^csk$-pBhur@f#zyk(Auc}*z1MeBrI|NJiPwSa@mI2|Dc4MjWNCQv&IC2JJ98-9b=jHLSIew+yG1tLO z@h=Y_aG_x7`74i?=ak2>8hM2$hC&5hja7-sQ zS&%EB#=BC+>wj4Wj2G_i&_(y*>_^;STy!10T{W!*8vYk+a^miGo!J1Wu1X!CAF zJ*N3-_h;Ja`Sek7SG_WPGxdKTM(<7UsHVSUyR}|)U_N6Ub1A;3_Z3wQ?GTBl{QVpJ z0|jZ4G1FE-YuzYBm%gLdI*wzcvndMXOc}_!H)>7+o05UDl0Eaf1JLmMSpa<*&%VUp z!AbEp3qduqYdqw9fOw**hf%XtN&(w<(IqBc;pUk5Lu~czUyuuu?|cir-0Qr^t{=3^ z7?W9~{5&6JG|H1)*;CB;c$A+whDlc7SsY_RTe8=w=A&}^eEF{2enqguHm69!rJ4Kd zOC!f4%zK^ktcHvi`<2FsGb@&JTF8O@xNRv8W98zAkfkWibcRRU{n<}U3I<+jg}6={1KK254|N1ns* z#^B3DfL7yw2><<(W_`RB9ujW5Y#_3fa!pbKQ$j*ZlveG(GElHFGLWIrk~G>YvtIvU zd*Qo5VTA3y&gad{H`~Vf!G77_UX41q0tI#r)uLtW}#_BubM zRMOyTZBU_{a~At&Fq+B!A>sDlDMvg6l4ZmVR_t}WXC-g=(6fSu_}{8o(uKnrTZX~nGgZl=t5E~h8$wS54I*f_1t61w#4JC5pfjG6V^k{?i+ zZ&7ZMbLR)(|H4uMbqyISZY`o$`^s|&X+yk^x1GUfpr>KRJ7r~2NeXF2o3=hw{6(m^ znX^GTp^`&rKevU7KblD&Y^m z|G@c{!V7%9>E~4+{$>K^`Eui>4fRg$o%?3P^VfgYnLNC0KRC~~`fWZ>o@-(!y>s91 z?Dy}GK1uuaM=ypsh!1&&1-cp-cmSN7@DGBGmxEx$CQS1a3Vt9O($-7eWFc6OiSk^6 z{Z&Z^z4xb?@gDhpR9+df3e-;~Od6GC(>t$xQNu#faLYgU1LnTEL8_RXXh*F3 ztoY?PbcBi7*{na2J~u2Z1aM$*s$o<&siR?`&7z>OH94{N zTz3JqfR{QysN+Xaoe|qb6@-{mf?M^`KSA)qh1G}KwdJ=@_2I9$kK9E@#~)KH$N#bn z!XN#U98w;38K>)NQjspn$@)3ZF4E7!W5hSN4<3KLix67JDISVrHg4#GjEkFOpOoQP z?;?bkqZ26ilLwA_@)`Ei)xRKBNiFxkbRH9&spU8>zb&fME<^~7Lcuf9Dvp`&>c9Dz zomgG?e51H#3bSSuE4L4$cw&`NJb9^6eCsl!_?{I;aiq>Dp4&vOGu(xXNaE5bWcQr) z=mqduXNIiF&7t_yefgZreWCc|zWh4DHGOGc{^xEtPj`nTq(g{MDcu+fG4=C$JJ(1T z?fgrrN#ZdKDr=6kM-?Kyn!LgJHp5-zi<6H6(q9ig#SWs{OuaJeql8nLgqcQH3{1{J6b zvV=+bN@7W2hd|9=-baCFGBL6zVv2u1d^^#9mZi^BKP)v$1|$4Q?dQ}u*2p+Q*2LvN zX@xWLbahJL-jEe(q((Aq93_jMC<2U^qLMx6wrF$ZjB%;D+bne}WfIaa+1*7`yMaTW zL2Ik5s(zb777Ct(}po&nF)kdOc(a{t``brP!mT4C<(l*oDs$7j{1*`)^_jgJNNQqP*n=FW_ z%gT7w;N?SG#U1Tex4*t9Qmi2L$ol9P!mn~-X`sLKIY8ii$-TUoOuO3~`tJl3(Qio|@ z#)R5kPV%~1GZ8qDy@r%MikGwAWv-duRHQK#sq;@rMOLOFD^ii=smQVlF#$8n`_DK;t6m})@sd4YiMoDK4F&5eQ&(oRJ0JkskCPhVbZGkR{ zFE9v2*VM5f&^i7=YE!F6?h`-f7M3NzaG>7}BpZ;Wwje9+!{fW}Z>j2-~PX&88nh59Bm~ zgltztAI($vVnu7igN0(l)M$eHhqqc1+?Hfph9-F@gkK_uni8~CI{j7e@(6ms$r}?!lxWRlHfbU(-@+|X;8d%I1Lqmhjc*c$Q;@5lR4~? zwZ*6R_Q@NRK2(%Z+5ABZ`Wv!mpm#iL|HAKrO9d-M8KV@)$N`P;ib9oT5&8Fzlmj$> z=Q3ztQxvrCM^o@l`9<@(nTj|NHh;Dx^V&)Q6*#hkYE?zo4VG-be=69uPHf}OlNTZe z$2)~LiNF)e%l6*w*n;+JShRwL_%02GuUnTX4#+K6;QAXABBTq}p9; z4@vWDuE-V55}~cx^CUo@bKP%`u=E@b{LboZPwy`_n;q5$=VpASThm7k&ld2Du9G#( z5p znLVRF%1q&|NK+F&g=@uO1-VcxmKj~CIjnU;Wc<-xzT_a3)J4HTBl}DzV`SHhcgRR~ zt(9fx^v7b|k^pV+s}GZH{Yv-9=HdLbPX-QG{!dFd9K$>5c^8rkK+3FUrR7>G4B{ho zoX3{%k!q@qnr$*{n*@i*sOz~7*`rA0?P^|xwkZNr!Q|+e?2oAOql?f*>jd&<^Z`O4 zwBv^aF+GF?rOJ!Z2ySw4#ezBb%HE%cz;j*D9>#&H=>s@?wIL|cBW4ykzxtWD z++gw;C6NxoJwrMBOlx?NY3Hif&*;}2sj+q@fnBE~9pS93(cSpJNR5JvK8iHD8sqHs zt-Ej9H=++7BmtmtE}C8xx@da0VD#wtX#ttuVEq0S&XS)TQHF)?L3(eGDK&HIJ-=lv z;RJmzm7=&MqVvx*mYDQwJ`=L+X{)pJ!wP&*XIlM%j_wh@9r=(^Zj#K)Hv&X5FUu)c z8F$Dm-eN`%kDoR|3}Er;D==+12cLF(WflXkv8{R)v_{gawgM& zrp$M22`DBSNt!TX4|1b6EX6&Fj;MvcX2!WJ2&b#^-q1(x)%%jyjp%TxhJCVlvsNX} z>>fSZDLf9XQS@ks@HG;|0JToyOp1wmCwho|c^Na05F5-Xv&ap-yAJz%6_1RXh5SVs zwQEK{{;r=k8!KaO;!iy6w*y6J2Vx;fTm&!}kET|CE*gE}BksI! zu;1F}uisrz*1nPI_Rct;c9!i%Ts>DVpt{Dx>v{1RF^o2HWINK4=Gr8uQd8da2x_aM z$W20E$5Xpga$aVYz4_aJdX=q`kFyESeP4>kV}uH2sC039Flv<}ZN4~OM`paQ%3obK z^(4YJR5$ye0>*>lZ$kg_l1-csmGvr_?Epx>x`NHPbg9Wi!yv0q%v~;ug|M@k_IS3H zH_D5*;0UZ}_jwEpm9!Ipn-PjoCrE-ey=7@-v53{p9D-%!>>0&ayTfz*#5{?d<(J8b zv6xJvzfED(T|4(+OKbDD`e7YN_@^wkeH6Wb>$GsLQd(dZO>!pWN10Z zQG{SgW08{@5Nbsm&mYDxJ~v>!5&bj!e-#fRb7*Vtj1t=D=r>-fqn-MX+qPd{eF$5T zZ`${Vq!5&66yV|T4!`5*?=XM+9Sv=T{Rvo2$-8JGZ`#w0$KS?JD9$LMII9n;ntRcA zgL+Rqq~7gQ~Rcdw&JCg+@02sDNKN^*a23MLaGg+;bs0Ca-|%}$k1E&{@r$rJefCO$@3XsCp_uh0Pc5yzLMf0$&+=^D zDtRaC?hVIZ&}4mK)xYs+)yKoCHdh5|+tps1{&;Fue z0!U;}bH}6bZra)ITo>^xY<0*Z~8&H!PP&2%A{Tq8rtM<^t?F^}H zY3;lmw>~wdcQS8zrG|B-8kP%%+G`lo$h_ixGNzHa@+;mnGA41SQe!H8$sJTJMcPP_ z!5ND9>M>gH{;DPEJ62X8HIb%w36VDOaoHKP=M8j<=^vF1`4ZM> z{%K|Ge0W`@lp5*`tqV#H;XymmM_s~&;+8(~AES^;o{p_4{|Bv4%fN9tLlijL^9MSk zAXWchg#Ek`_WHIFo)oYSXOC*_S(#B?_sf>>|0hRv_;2p0zC}OAlyCt43lna8azco? zVxa`5GVy9OKDpzEF&w7wh?HRDRsjl+DP**l2r`C1C|G9A$X?_qgz6n_E{cqSc&~2| zIpx6GBAUTP2z974aPrZViuWogBTi5Uc$bFfWWktuKuuxHO)kdF6a*#yS5JxIl_k;c z+Sn3G$HUT(G9qo{m}&XxaS*Tdv|K=|-m+N#^Og<%lhbkwA-7v0o6;Ys&0FH*D|i#N z>?>9+Je8O*t`OhPK-HKjIoKA((Ay(u55~W1z=of4$alvuIl(V^Irlglzd|bM_ZOC5 zw@}F*a$ZtLv9p4w<<@Q8l4O_K_h|=}YrwTLj%ItalMG%6wmB7;N12{{v$ zm9I>%N5MV4$!%bJfN))*Fcd)P;3Y#8(wsa*5tp-vDAH1tp5crnb_zzX>sty~8o>si znVIT$mi-G;{ShLTrYo}&$F9I*^z{>nwWhqXIQe1cx1FyQ#a7vF=SeatpuOyhjDlie zEUJQ$DkKcy>E`yL?0fTc*+FHyjhe-5`zV&BCRh6K+asqCe`GoORpF+q4+-mKERiE2 zBHvv?$>-_ZHW9DHtz?KpW$rq>&R18v)Vioc{JKs;I~FjA(6OV>b5L( z3^}zwVL=o5jOo!UzJwz}2o^O&%;(@mN1iv(82#uY-7jv$;Ky9y%r2jp>7&U~kb2}D zaC4sfMAcH;Q(CE1)ohIA7gES4V$0%uo`Yo^_50xlj;KFF_p1->m9f?Ap$huH#;Ex~ zUFEJrw=7M<;|Hy=MJn2!E*!qS`Vg!QJK>A`Wjj_CJfkZ(YSjFgtF?lD$~!*sE3O1S z!qOTwJGhJCPO_O_amz4j?ol7M8En*4%7<)tK4tt!&er`YSMQ}iBji1O);2ID(I>}I zh)NLZRjv@>Uv;P=X=Jf#qz&&2X^x&_yiAR zgKKTh(>f%9ds?Z+{{8r0(5Ov2ENl7&wWhJ{y}F-g-{+aLx4BD}|6$AhtrSs|txa?h zcIgzMQBmsN9~IqcQRIP^!_jQT0ZnAy+@;7vY%-mJRe)Fh5h-Cw{XiJz0!a&KAt zk4NKjuDEd~enjKZ-6vxo371Bj+lG5dAkdn2JPpN%*oof!ocG#+3w%MFAmKQxUzWU< z7Y{!ykUno~){E}Rp}aVsbJDz+o_P_zfTJA++Frm`=yV$>_-@1`=$2F}Z>8ct!!Au! zec!eAKH!3@c)8W=w7;4yDW3nuzG9vb5F1v@d9;5n+~`K3j5PuhrDqnD{+F$PZ}E|5 z{X5Jxid~!+B0H7-)?>68HXp$GjJcN7MFpXB@qt zZ*E6Ob|25bdv7t`+N6W+gZ4EI9`C5}3irtf6(Se|-)z*JscFi5MWnYTfosb-*Qz;AK2Bp+yf$p%s(yWM*b;!TCG{P6Mz&wwRYg*zb25INAT$$jV zy8af1Q*5+p!)ZsUL?#yZj?S`T?LVyG&h_v(jzQUZFGob2eX3rU{}p&_0f1=km99h7 z9PRqmsUmR1YP`;6GIS;t=KuY$_|qfa%Q-!|dI5>{yso;Rwbqa8_@oklU0CTeA$~Fi zvf!VTdZTOl2(MwpelK^Dxn49)Yc99-35Nq z45BeB^tZ3`NB4mz|W?ifCx^AL0ty z3a(i@l@i+}H{t7CQWIHtESVYcuVU3KkJ$c{-e<_gtE6)h5`hCsIiLvj7XO$;EB_cz zxNS`?*pu@vSjSrFqcy&W-3fd_e{CKYRXCLI)r6L`HT^mPJjI-@5NSK`48Q^ZtraL2QR}yW z^6zNVgdF@|0A>B*R-p9s->&|bRI`_nh6>d#knERo#^-CS>8S}`z#@_87tZ0_T%mGc zPoXmmn~UrR?lBvdrFWeOizVvp(u_V(&f8!y>_&)tjkcn1Utd53DAC@j(|1f?q)}4C z1?-oE{?ms)=IK75NP5j*|5;am^b>4!pMb}BLP>Y-R^8c4cPgC~bok%K*Be^m>#!{U z%FDeCSQy(z=LwNqXz&qE3O0#@|Dep$IYLG^<~!M^r6=a`_UX$Gxzo{2fHqlUcMJV7 z)TLB1Hd=2et!WB)yh73vY5S~@)i-2KDKM=`Y&UF1N7Il$B+fHz;rYVszmYXPE%k-! z{8Ks~q-PK^Y7)!_nUZP)QQL-x5-sBQw&Vgm`=8!2S^YYtDphUy zID6L&-}z5&mo0c_a7Fn>$ZoXJ$zinOh0^^sttiGtdl!g{!@RRmC4+BHh|)@SzE5)g zA6D}J5`O$>e+EC48(NF?BYRFZKW)@Zf=MtXZIB>DH3M-O5UW9HxtwPW7lHKZ5RUQo zkFsH`#o>Pk&ZdRPFdLLo+xHVO;0q1vM{j1T-iUb{FTr^?M`|Lt)*WNgC(E?Sd9k zXfl7Lw4)gMG>ezEcbsT`(ES*8b<)MO zK_`89A^8K0TAr}E7h!V`!e)a%dZ3WO>~sfn$XU#q0F!!&aH%XsFSs(EUh%G+7yfDT z42H{RYPffb9}kUB)b=7Re2NXa5J_FhY(y$n@>)qNZy`@!1F76o5tz)eKL@RM5!uF* z=}j$+j8aF-gUO#~)`yx=@gLg}E)LkwYO#7>C-Ks-9+W${3+D>pAF!U2Tdj^@%DZ84 z6PvB9962ce+@EA?;h=n40Z_$P3OnvkqmAoCj84I6Bi|wyD~W!ec+K_l?_Y@1p06Ox zG2;Q97%Ssi-Hu9}XU1XLio!oI)LlSI;a_{HMQ9vC>9ZmHv99PMQ*(dSywkcrNydSZXPqmUSOl!!I_luR!gjyk( zM*iO7p25j^BT+lxUm?1Q^!oBZqx2&cij_n8any+s-B7r+@1hz}A|{mCS|9!RD86Z= z+IY=h@W)j^Cih(B%r9?_M8C-KKcCZnbNI4ku~i@a=!pM?95Ne4dI-&l!CYAN{pxV) zQs*Kl$?N1GthzFRQsgFzaLtla0h-HVe-F2#3@ebsM5+@?h4O{d_;@ZV)xR@!#-n5q z{wgO>@R8Vw)!dx)uxO5nhfo(mjd{QsbC!1>enz(L!>6cL;69s^<a!1f6VVk?T-3Uhm3Kz$7RrehqkHvQai~#ewq~uCl5|ozps9UF z9UV0~SM8<@Kcar&r%wNfqK~^}`e&*BsZu|sQ(Gx5M5$TiDl&uiP|b1-8!Ei~f#kja zzqYk{H(hf2@9qM3pW?XZQ4IP8gv!_{faG7WVL_938J%bPBImM1@7@XE`tUY|&%#L4 z-!L)&Vb(B~btI@&H5~c~F8ly|!y?j;Pm<@i@^1oX{HZRzrTBP?Ys}>Ioun#bS?0-0 zZj_GMy%~j$vMziQPd)WA9>r4lIp<#DvnT(X&Xm#@IzDos`~p3{(RrkH6sEU*8POtn z!9T_+JdykhM(0h@gIn?rgR9_?8r&4>yFt8U6k?144|y<2ebF#dO7W!%0^^Gm1DUI` zKVHz6%4y?be$rK-7LsS8(o|nJs8A2;(q2%-QyNlLwUeqmAgecgHYdBPSGeKXOn&%} zJSVay!1t-#G5kWQl-_oem!_gqrIujzJ_>ON_ATKWC2gv*IaA9c6_1&}jZUuKh;0}HSlS}D> z{G)=3%w?^9C{JtMr?z)piC(dtz`=W1>J_6+UE3wzlOpd)5AR7I?@0%*swr75e*K2q zjXSa)?sgxFZAnZcgN7`){sK8jh1@QEuk$_<^!KX&FYjR^Q{p}RPw!z^AbVWayn z-8Q1oa#=h+CJ`c!eco1>PMySqjT_%*|MY9m&N~(y|XU~K_Igb_sHjTtHLeZo5DX) zg(1mle*M_;Q{3_b(R(ScuwiP}!|S|*)45i23xJ?4FT(_Sb5{kHG*`xAdJc;EVanV^1(DHfxo z4h^PQEs3(>!?<-z2U_%BE3+t-W$*N_m7Ohu1q7ug zd8P7`+nH*izIv*=d%PJ?hYC8kG6x~+a;*0O0uf{m=SbUUO8?b?NotdWUEIRqbgOlPDyk3Y%7L)5Jo-KnXO0hrEN^N zn=;dh!vy-svIk0imnwK8JmL5=pM|MOVlS#9>6&B?N!OqzN;;Gt52drPAgv!l<2q`l zEb;3?x`S(E9)ebVP*Ol`HjCdgU;osMcRYjeD4}={YnfcyAlCO^^Z`(^pC_(U8=^|JqQUx zFGe+vmHrV(?cHF>mKpgxjBE+zY?ds_;|fQY^nab6uBT}bRrN$I-weE@J%RAa3b5_W zhEWQGj+JMz=ClBx4rkp=J#Q9q=!qvIWq@Wa;A-%@mlNb*#VC$j{CuHsEO&gxSofyzvAI4Id^EKm%o zRp&3pHF`o0M;iV6>vF<GdSU2gkJj^JcU+%4EC>0KQ9uf~zh&8fa1u*9r zV7f(<27SpNnYNJ?uAs!UIY1|5tuFi>FI)l9UbCaFd^gJ~c%f$%ywvr{(cjcz>oeTQ zFvNg?(j`eT`)y%ayg?_5K0K0gNhd7(dd#%}yRWmFUyF3a+M_4){$}_6_fGKs=kEI_ zkN4h>3Mz*eb~on`cF2I$7duqsBqz?Z1|^fYo=MW49Psk!ztDzLS>gi)OdS-puuBUR zIZ^zS-aDXr>VRRmKuQoIB27Pz|9)Vr_oSU32ch2-K3TUPofRTX1l?GwzsKC6-y8b9 z8J$(3_p%edpQPVQd8Pl+@gEt@y;=Lp&1%bTA6+BJmJ^wv?5^mAwmH2gY0NkWLo{>h zJddeLLXKTbs216AA(Rz@l*v4~LlJ~W1^$=L#qym!1|7LyEJGx?SG+{p;K)wi@=0R# zOPpIs?_VwYi+WYW9JQel=IN0-e*ylRm#{)~_<$;-<{WOK>qOM{MLI>-tzaBJBX*KJ zRVUWb#={QG=MY(JKkxLBVk7n`*93|#QpdJGP%lPdT*e}gB^XC1?W(nz_*j0t@kGHT z%pW8Pmn%-?h}2>VWBwHvud`BLDEpyy0E-3RAD zVn-HeeFYtnyZ%Az1Jw}*$!$0OFG9fq+fNH(hm;9(=Dl?kSNhW2IV48f<@ov{FfrEy z-w~b22fWCczsHM0N8~&*BA9fjPN)*$&g+A1YU%**^vi+`>O!6;FP54bk5~98kHP&XHB3g}j4dKO}ZAvyq_I#oN1C@b98d_nTN;DRUA1_d{r-Kpu z!{^~LD9o}f&qAkRu5i)sq6O+C(M$1)N!L;bBFaJcK0b(wr zUF-;)HrLC)eaN0dfSA8T+J)?2ccXPUJJhIn$~Y78(;U)-Zht=E`1Uv><@R-M=ZwNf z`HUYa)xQoeJsUMFD(GIg$y)EN0(?GaIq*gv0xgEVDnOXKlf6-?{xfIN%=ko(1&Man zX@3#EdlZsB_H*9v=(arX2Q>Bjhq9U~q$w&*Z15_j-QRLl<|;2L)nZB?kKJZkybu`I z(?KtD>_Is0txO*Z^(le^>NMJcM1QVjB0qmly(;6j)p|zsk^F4*cZf(3>na3E!^0IF z;o-&6`JtT1`O!7*x;9K&zHQM{$I*m0u>nN2j#7JR_=Em z!#7RX1rWeU{zjhMDlZ{F?Y-gz$D3W~RxE2N;1>9)n@P6Q6Drk4DDBdJ0-s~)$g11< zr|;BTM+gV= zAM?);0wvBUc_E+pXi>u_3 zUBTkNdULbHZ?vZ&EbI^eIXN}DMs%sZ@B{%wgmZg(1cgh^^CcmU`fdPs$3ye1zTHJ7 z*=YV(AOz}UERC=xX4i`rOLM#Qa$m&m21swM^VVQ; zxsdStvG{p8j0;rxZ#zt~Dti@vp146RKJ2XmHOoIUaTO1;{M*y}J*&?2oLoUdGE>VC z*yZ$w_}|=>TU*QItjo5b^%m}Ebtun%r26xYY{ukT-2~)F=Ix5FL@^0G6ss2aeh6Y-WEGCWFOW0N9-0{$5B+Ifm#p%c!Iw zx|_c;$#zt5UXGodN?b^zMmZs&hcNA_70CiNJGq)9sS!!+(i@)S7IG0-gL@NQMRH$| z{#_Cgjk!L@7hNND`XU_~R8Qnuh{Z)-45?lcVu+Ny_H@Ff&Z{8rIiy$oc7}3x<{aI7 zdoFxIDts4{Tf0^y^W+pS&;7i3OMLqlQi7j)mPSLTyuWjWLYd2`6%=cdy*XjVdBjo6zq7Q|=9W5gP`ajC}V_G1cqhjPLlF}X(WSv`rD$^6yA#hLM+ zE|VX-KgKDP6}u2wotww!z;%2s^U|TAS|(rqMOP_aEBu4#EI@Q8+bvCrSFnQ9b;^Bj zmw;-7mE-fDqYNg=;hux`(~JHAWIXyJ^PSf6OcIQ6$^P-q`)7~;6Fy9j|6OhvzbRz% z^HBTs5#~{QijScqi5Rb2f#r^W2p0RcZv)GJD6pW;04%K`Qa|@N5IJi9lMuNx8zLkB zrxiq6@XzI<){#-Gb9a*ykK?xI?KiTmpoeKuUi)^%ej}h01s__xE7Wpg>Q9pc0?zqc zQ7~I9Fu^K0($G-#-~6rOe;s|#s$I@?@)~y8B4;N!l`dVKE>oW{q95g1wen^5S7c(c z%usaAKw*YN*is?NO>3p#oO5`ZFafG*Hqq!x5s`giVkfDHaEirc>F&eDkUb$4-s60) zpO9veK&qvz;4cARwbU^=5&ExV_h-Q$J{|4K5#fGH$9WOQ{S|Ka(ldFc>8b&hl;E5S zEu~sb4G8sAD6XF5ft@$i7tU8YTwv+^ZVMAd|2`AGy6~uC)Tc_9KHyu$#;p0Rbg0FA zWaxMG-vkEuUvwSr9e8e%Wu7_LV$@V%Cl#9RF%PBObA%%lzpGHBJK`Z%g#-8tpvd3k z+ym>PCUG_OVmxk$E@P>&eWX0d?YQC--u<>Pcvh2nP*!Vp=MiDCB^qzqrkPf04Y;ZkkD~pp1YeFKAB$ITWcnNLM6X z9N{{RnzOjjT{(#>AkxO~9oB6!YVK{%Gv>JZRe@KA)lW?RO)9lYNL;W@IbY=@MRQ%j zRv?qIS49~@>L!@>tiC~u{0G*|B4TRw@ki$uw25?0Ude-QEgpt1h^`SMt&ejO8$@??l!-L@=ZB=I^XotGMnTmSM1Ku3b?Q@QP~+E<(gcV1!g?Dj4A=w z9n3_scNRWSjq~_&vN_uK7Elx{`#X^^RUJcFY!#B4_*0-8xJf}bygg{WD$Yk&h(n8A zR%O}@>ZlWed<4ZqNIE=Bu8Qz#?z2C-f_@c*uS?Ew`3I5l2L{@RdHN!`zTvRo>}%T3 zAo9D!-E>g;lt-Vuug^@D(3*P}`HpB<)A%f^Jk{uF(V2i0syMT+FMMv=zM;l*HkHyC zTj`LOWGhXm1IsY%?7&Rc$Idf%NDB3t=Z2f~nD9oFYVC>&PWKATLe@vleYY!nEStB~dGp;^e{M=q27{1{|e7L>xHDg;Is(@_(4^h`PL5S@v(rKV6{g8DW2 z`f>`FhcHozbd$vv-Mqk~=`GnJcz5CWbih}Ov%P#}^dF2j^0o|ekv`4_yX zSbaQ+IZzx0s0gNM|~Ji%hqdel^a%EIt}6`3z3+>LOrb6J8R z>y--XQ(@ulnZS&Mp(@v61o|*j=tEda*|*jzyWYpnRV)Ftw*VI-j8!v-k0WQhpHFhj zTq0#;j~OV7T(swVg7J}%a+Qw?pO|-Mi+ro6+@3W_K99~sS61^2@Yrp49+xhy(y3!v z4@L|OqDB?gI60%-zE&2fHyfFxeu@JlXsV>qysRoKr{9|ZU1ldUIT~RXJue7L*!C)V zS3>XHJlfuVTW-%cRr?MzP87vSzVP3?K?m&_sJgE$4BF%Fa4x5V?zbduh%|VBZBgjt zOd-G&`8hs4cgWUtvGY8hJc76PvC+W8PmK}rWH-8Gc{Z6kfMJ8Pod?PGd{pSfD6hHh zFi{>6!xBg&+O@%mNmz)G{i{g|Up{jN9+cH*)O1ku4MQD$wUAaLe@;A4L7@iwe<2^- z1GP~A4ly;!E{Z>OA#?TvBqH!Y0hP>vLJt5U`-#)kEbT-t226MOB3;$@(ph=;yna`x zsZ3t@S(jcN7fgGEM=NQ@&rXo((YjcDBx%Hur~% zUGI)_LiX=u_)Dejfx2z-dCjHYuc@)47rS;6L+3q%782z2>p^|-vZcY2S4khslk!2g z7aFlcO4QrKGM@6RB5nfd{R;oF(!3y=6jqMZ5H9mDLv(Y!?pEuiL6xM3s;@O|p*+c+ zLfU%GQe@2eG{F8`B6~3tanH{s@$dfdJwcn)(AS8V%=pn_0gIygD!eBrl*i|F%h2bl zp)(^FRn5z}CvslZyxhAZXIIT@b8n=3)jZ<-!HSi$yYF(YJ(GRy>?-L5AZ!+fpPZ7X z6KMii>>$6c9jO(#G7g~$(!1W4xH!!!kk5DiOxDiarMQ~ZL9c}j2@~V>2y=n{+}T^3 z`REsX#M#bTl7!1VN^Ugf>XG$Do2w4xM5YSK^_ck^t37%v#!<4>VKM0=o%Q^Q=>$t1 zlHyyni9cG#B{@gOIf_4PLc);oLtgNB=r)KEXtaff#XAx(m$gKbJgxD%u zRf3dRD^x^0R$l(S;3)jnFRn;$nUPbD*(*6T`C=Dv0;EiZyDE{PuwkLcOD0YuD$oXx zo!+C^>5n{i8cUgo)US&qg5=>c)%LX&!o)(P9(Ypl-es&co1azl%V(&F{%QUqttM0F z?W+F<2JvXjfCN6W0eP|mt6UO*TnmQQrzLK4T`;0}yX2_r~ ze2?=vp;pwkt%C0hKM8)yx#R zRkB9g$#R7vNXWZ94p?)y5=?(7X<%2_xk@)G>{_J=xaQ&%3luJH}q~M z7jJKZF{v~3PQgIh0G}=wu3Am#BXvw~$Ozn1TW-xI|G{K-Et%toq=Rs8W^$oRe}U2p zXW@_8tG$LIPm$C<2)IzICH-w{Qu8fP}EoUn}HSmY@+6Wi=H(t`0Ji zVw2-fqBt(75k*Cfh*F7SipZG#4K?2C_S-d{BJOk)aV(>)B*txu5N)jt{=TjvDQulw zP4eC)JMl!;5Rp;iyds>21k#0+!k1nLGLo*$npA|+pbufa+mKMBgnQm)o`o-9Xj<~> zNo4~CfL)G}ysR-(_3SuN!rd_YDi@$#{!5xFB(d)P8WDK%j6l7=*oA%UaY2OWY~l9f zT!!M)O{v3<)Hz29lka-C*W4$?;seItCp_NVI;Yn@p|s&Mp;b~=PCQ=vpx(du-1+m9 zGDkzHbfA1FI}lK+!|3A_d{@mse)wud67l?Pg>^UdE`kD&gjLGcRv)vQQTwsG8AoIi zjjEPiP65Giq*v9i3N_4o$75

    W!bLuidkWYFH5=s=^b_g|@29jE@II#C^|6@{{$Kk?Fx{;EM` zotGA6mdG1(K$z@@#WmMbuLLDyx3M;Y_D{6;?fc;_Y|N-=7RbFnwb)@O$mlgpo{Ly(me36s*k-b8kv>^ClQcWHT6o0B9@R?J6IuNl&K=npI8s!ukMleG1 z@c$$S-yT3>7`8z(4>*mOTIde14kR%>0ks18v}*&sN|2xKoB$|ZePbSKu28@eWY(fPVa z4=zumYh}fIK`}}H@_-|fs$}`q!2aQ*sscS|b?!n?v>+H?E8S2%vag}Pk`~4JAe`2@ zuRK5ttE8;|oV=4+{6@_@hL8+G)aTqKYK1+{SY}p$#{pM5IS=mi(V2WD_&H&Y(00Ru z25^7CX+t*>*Sk0SA5lKhcK$sVr@3aM=4q-IJAP|L(*}RlFeXDXu9TPTH5Q;%!6Qj9 z0bGIiaZdN^w&Yx(wDlO(CvaQh#%axuAm&CzVOurud^28}6M_F@J;6UY{m6*~k*-qc zuv}mhRr%q7@D#y=_&rMnof#Y|L@g5Ei=d~9UqJ!56Bnp*bc64CmIdE>ENxZJ-C&mx z-{@LZB772MJ<=kBd!#NjXl|`OV<0hhZuo^d8Nih1$HDr`Ssd;hk>B}8G-%&ib-Z?VJ7%UG20!}e0+Bt8{be=7Zj{gw zJroYao?v(N4B36Bdpqa}1YHK{2^kWSN^2NNO@nN4DmauiHwi!WI@6aEd$0w6EArf( z8F|ZeMfFIGueyF6UlVXEE0;@LZ(Z6(GeL@#s5(Dr|>k_ zh~dl<7c<3K#m5xd4=Y|1v*j6LwmeVU=T4RVP+5QyvmZ*Ov=Orn0{iQx-8qRbzf`#+ z`~{K@q;yl5xbptKkW#ozl4MvFeUvh~yRKpb4MgTJLaIdDFRM626@EqO7k#>Bkw3MOD*-gVDs3Y3JRFPADTQNqo0Y--WTrg|mdyy=W|>oy z*D-zC!%T*|?7xI0gq2P$wpc_r>}F9>LVX|YW`)xPowUe1(QLVsV%gGMa>;ECrBlUR zBVj{J!+AjE4p28c4iB>;^L6h>nTZLaJ(~p9_>!o&niHecKKQYkoi}(|R{Zsxll1Hd zN)q!f#PMa_y+WQ`$CrnpH+q$3wUc;-U2}MRa6elCqJ(H?6C3A@C0J_p=hQ!cc)ZJZ zOSWN0OU);Xbs&E6DUyi5R~j(N6P$p&;%S`8E5D|*oQ($(eE457LS&gnX0Aea$E-YP z<}{R7qJU6wXny!hju%xbkc4tR*RX-fX%zF(#=H)`fPL$mi94X{$9DIuxUv>!$s4l1 zk83~A(617c9{tnwYt5qnbM&hfJq~IoS0vqkh8`#F|8wJ6B__AYiTjA#r06jT=2E$E zCh@$|RHK}p$(mmk*nb@*e&HoP34oVDqz;AnOxqqL!Ls4^4qJi?QyTt3NG*;pKqxuqG zsQgn^Y0^E#^Ub$Zfg&!S#(9>S`*8UqLTwe?Z84|%J`hrO!C1peYfsK5swxw*LR=4pzc=4owaZ!Kgv^5rYED{68@xpbe#a! z7tT?+T&Q-x3c2M?%Ro4NYG^er4GZNXnHxTMRF6nzdBEA8KW;{Mk@5B%t)D@9GQ{T`_*Wx7#w$V^w`}QIjVr)YDR*G)d%j3oTQ3) zGx8lh2kwmA_VW~$6?4t^d&I-df zYIydcK%KnhA8(N1fmwq-c)(cuYJdr72Ixxx&4n6D0-E3JOPS@XiMR#GXHsL8s!u^{ ztcIP2R(_KPOe634;uOdQSmdH{7k%df?QMOxOqXnaE*p(6)OT5EtT7J)0<>DgNA5HJ zKEw+oYb)M61fmoEFi!ecR93$>=#j8RCdUFqX-rr?rOTDWRAN!;r7TK4ltt+@Wl<_r z7Q7Bs^YVR0Y?k5*XM;`?o17osqXdiEX6Zb+KY8z?Z^BIA-##gMN{jVYZr{?ouaebH zp-?b7r?=?~e;TyI;$WF_dcH4WQCR%-n}T+zw@KEzFyU2Z!d=~0=p21dygCk&c0RgFfA3Vuuae+ zrfIzYDoLG7M%7mr=w65q4%5+uH2|gFT8w~dqCTWKCMr6+hcDb)46=iSXBEfuWSP9t zY5UINq6qbVpCmDDkn;Ag7S6}%)=y?g+fIKR^MZBz!rxPFsaGqvRH_`K4QT;sBz*$K zRg1LKro94FJJE_j#IcofYoD{FGHw0)g+Sv#;!67JI`0@Y-%+T7W0O`XiQ83>^oD9s zow?B}B@r4ZAu6SB08R)GT(l_H-7H#^y-vZqT2}t0ydss2OrtZslR~@LZ+NJUw>oyw z4Y#z!fMe9%%+>K^+Ru?`hp8#dcFIVMmg+@|BFEM*MS+rr0KU9REUXJ(E9xX`ZXF&o zL95RB&D98}An_@pXN8{r$d$*-t*;cQ+6UdvC-XnyscOj#N!z)8YTQybJ4}Y%8N-`yR0_>%5OCSXs9p^zExZKlP&2N zgEF!}T;F&MQC=Ia1GviAxz`#0dIrq~Jjgc~;1uU`BjwHLFkj@#P`qpH4J{VLEvhjJ zFecOrl3mzc6W2H^qTZUA+vGgQZkrfGL2vwBGvm+q#vgt_ja?tu@*KiNN9Upc)74r1 z64On(U;D2Ar~B0{yI*HYzeFCRJI+w5PmYtNsXfgOIfI7_?#?{NP5mVv;ME(JF=ikVCpxl^z55R7p(qaIW-vZcc zWp>nVGIsNjLGvkzpRG`i4Npe zbo|bvaw~=hwFJ|ajGu=p50UjHZ>T?-GUi`~B|%gIIgv@xLv7}t8*T38CRA>|Kzl_+ zn+Hca4zHRsH0KkCi#-{DQzq{g~C+_I~g_Yxfo1Rog9AM#$sa7Lg6>3 z)M{F#T3P31v}wt2lGQZt3 zVFt!eU#DRlvMMX0L%t6=XG{A%pC?a~ah%0Cn2BF6V%-WfTZWlseCn81zx|UF4?QC8 zFs15#LdCjLUO5LJM)5$C%wU5!LvL{IqLe<_C+W<+@{<$gJa^Bdvxa*J60+&wKi*$` zu!}Xp2Z_6>@HqUoke{}b6J03lvf~+fNvd++AL8rdzE@4*{PaiZZ^H=bJ9Ksf9T20n zZn~OxUVj;16-AuxQeGL|aE+3(j-6eDsAwB-P@+dyL!b$!}+V%i5fjht%5L%H6_g9M;r+xsK;tdB`ec^HB2Q!^qzGq^^VXK}u zOiqD4qBK>P9J;|C5r~^P))@FHiuc3~paTCURro_^v0F`KFU3-g)`)=f5|3D(`qZsh z4qb=-hk2fXzw{@Q@7|fp%hZ_C$m!A90iQTrF_ep?qJ|Ns0(*K$A)XR1L(N}&NF@J4 zG#F>IKot6C>UZByU*k$V{#!Y)gE|Y$>_wA!#zdQl9dU z$6Zw6cX-}%eYy32ZLtF41BkCjH(sKY=ilnSe@nkVOTB-r)q9XG?Vlp$oojYy(*Cryi!IjH+J3vx4N+SRi-f4vfQk#P zptX9(tB6Zk6q*0~bI!f9ME!R8e*eE8uUDA6p8K5VJm)#je#%SS$2xkIxc@kd8_NX7 z!!)h_^On0|8E8q5Z`Sec9J~F2aYNZc&PzfSWpic>MGY;>EVZ>UTLq}?e_0#X6xA}W zELDMx9vvBb6QkwdACFOhuMFwm^j22f;%{FYte5$x_jCFmGwwa*fwpCp)7jQp8@h{a z4tBHM7*A3?GnBZS1NILwTXdjOVM`aB5(JQDZZ?PO$Dd}Ek5|`5h&bY9@S5;aqxMbpnUL($!l0I`p zBr6THOHBO)bG`0VnT>aPJ%}?CyU3?nFH@{&IMTsdVdv(lp_LTKY~6c?Gum{WS(=_* zn&@;cvivW!vNO)j0il%#X4@m#b&1Z{o8H+JZ)qJzRuZYb%-ky(6&sa99ytA8B}&3Q ze5fD1TLH^{|0@#3qV~hN60Vj%gI|Tx5e#KYpB)fddBB-x#LT|FV>%P<&aMJ`Q)p#- ze9sw_hgn${DrHScv;)txtv7{AOPnVuBNB>a+mro8dkJ+>{HLI8Q-8vyEj z{bBS^5>d3FTk8jhE~xUx7^^8;bRS4N-S5vIls@+no?}Px*1fZdYfEg3T_+;+dMkcZ zEl0BHbGuqU=sW+NAhIGMhLzOJ~r z=pO@&G^7_l1AHXFRxPZd8VL}rUr-&nwEme$={nPr=>Klp*my}oH$ClLOltfv35e|F z%$3Cr;q+H(nTzo~2ZwHLqkS5RZ{HSgeYQhw5DDytPcjRr29lg*C5kbFSf!~xYbL4Da=*b;ZLfiBecc`oyrr{jB0TG*EX zdNg$F6kWO{Yp_2Q{q&aX148N@?I_!ACvej{!8Wu&4edY0?2^|uB-j2|b@_Gw@_zWJ zEJV&E+A;kdUq~RaXXY+Lkgi&IEIl{$abz)lyz4GT^%Sx!eT-?>ZVANW7c)r)Ah$kh zzMkKGzW&3`*ZMr^|9kUQv*|Q%aJN~it5qMHo1r%CaF^#FX6F0T^WDPf`FEygdj8XR zN9Mm6|6sQBEPRVF=#`ODIDp#zgn7T%EIzsIAiZA8EN4ky$0m^bHUFPvi*q)C+`uM~ z`wyM}GSfM}fp6G7Am5$eRZrN|iM*6Yg}A^GIe0&L{)ZB(w2|iluQ8m42fS66ytYdl zR{1>O9{OH5#EYNlHa`qMI4R%0|8+*N3C_zjXuHueg7GK)r;I=` z**+r}OJfb`(Kqn(Y5d^XF`vp0zT0X^{C~s`>i;8tpjfGK2C`7wB?Y`-aKwaQXn|re zSQEOn{=ef2;ztS>A(Z0^*X!~o|MDKV0@MDCP#gYsJj-T9$kW#CHyhfr z{PCYH0C0NeI*7zD$SdL60>&_X2yIv>{t934npjmZscPKQEq2`N4J`l30jR6Ge*J%4UOVF^GoL;#uPL;za3KB_dF`!5c5?di+C|JP z+2{i{JtZ%%&7WB$t3^IVRwGGcwB$AC*kIP4;T)b(U@o(Dn`39|w~sdjmWb!)-g<6% z1LiLC=UdD?;e3+3b%p`?|CPLT8F0GK9cL%V=V4C&sk}w#REhgFzoERmb+|4s^e_L< z<*j}<{tNSqp)>;Ko<5;luN5=DWxo+U6LZYT28pG1G;i52>2$#(nMvHq*_Zye1 z3x;}ih4a#vxT=HsDS_%}O~^G*$M>ETy6vkFKl^G|@ncuH$3pQIU6XddI=Jrj{1@jd zY+{S_tGE8=`qk|%W(a7@$X4iAMsyB+UABwD`5LqO74kFsm6e~n=~wT%<85PsJT;5? zIvYujHjRYem{&hSx5_?UsDgxUUCx!f(qWa1K*vg--xdF$udiXOD0OCfF=p)O>ojAt z?%zj>;$xTm>q<)OG+sZ=fBioD`q3yL>1lSr9uQ0DHemPW`N8k!nYH*I{CI~JtS3Hy zm|+_HME-E-LQC@hkL0h1kF~s^yZluJ?*D!H>#tvi9bD-*nU}wI>GHY$<^Rp~_^)V| z|8zaZBsAFP2UeR*OoknJFNj_MO!V@H5f%$nXq1*Z$-n#q|MJ0HHZ6ER?xs)VmUj7@ zo-_3~_e%#_*ZR7h-^0 z7|GRN)xD-*5R4-N%g6)s*)C#`<@d>LW;=ing`guE*q2+*VvgA!VkpO;G3&g@vG=Ui z>g|?oIek88+U|!$JyF7J3GR_^krxjnMU7G!ZDu&hgW$Wywc?NRz1>E69wUt#F}IU9 z0a3Y;?{7x_EsgQp?tOpAkNgEWdRS(!Ua66sn4^aq?bUg@I$HG5ivhuDx1csYzHuQA zG3PVxq@P&tqBGPTr$Y^*XLbW1OQCSj-dv5%z#ba(AJo#{Bh8pjuGP0d=8C&`J3jvP z>-kFT5K4y{yNJOch3MnspVSR9s$T!G|M|Y|Uv#L?`?wFa{XSn z!@%X&mpS344?OxWp}$#%}hd zAYKAVOj}Q+&M!|-cqhK6=ls7%*wCKv1T(lwl?X*@TAE|vw4K4+>s4`DN$hw{OI7S( zkK!=QVjWYhrM=3OFHV*dTQ?GH(uHZn8mQ=Lr=rEf9Kdtr&meTXI&&ol!T1XPYBR}f zl#p|TVQ@M6Ctj#O-l$Y zjU(@Pi6&=N>P0>H8MRHS13_JDXk|JPrt@iD#rtwC3XD6%wg>J0OX^ITBRTa&)4&S;)?Bqk^ECWB> zVoaGLUo?7OzlFuOGt+gM%6j8Jy3R7Rq04;~_jM1{pRf4iY0HH;pmhHmN^u&$+FC(}cWsl%B2u#f1AO_$Se`u(8J=U$L=Kis;-i*53ztm+kGA*`HSP z^XdEAA?Qe#QId> z_a+y)%Dt7tX!0em|Cp^F8%>sNzG@V^9^z7DF>R3F`FZ0&{51JAs2`8)$?USRQFi$r zX}&eY4F})q>z>9TgM|Y&wWLM#QRe>cP@J|MlUe|eh|<-q{MZ}kRO!Z7bV2_RMw@u6 zIa*^Ksr;`>7>fg@GqHf_Pz*X?I!B1;?HjC|@<1hR`(j-VEy#JDbb+BXRT$$OgG!WkFn7i34n7fB-1as?e z@%$b3Pv$R~tq>YAkCt8F+~!_WWhbh+L;eO|FcVcdu*FYK$Umv+x1iw3dBsmw=AV?r z70o}na;Z=R04E6m)~#)>H%tJq-)Oq5iU$oYArzND{Er~Dfw40p=_Br=VUL1jwS52W>>$EZRE z>B|!i0e$^qY+#>tPeV$8f5-00WZ(~l^g#_Tks13v9-s$WGYXa~pr82oC@i+bImEr> zJ3cGv>z>ac%~ICDd~R^)wvXr!E;g+@YeMn2Ig6%dH%lr@yut}ZNV~jag+GXgftq=_ zEoAXJ2snphtsds#U+T@|Ur0tUh@%YK{X(2-=v2qYuZG{2#E$T)^Yl%P9{=V&x{0#z z&|Nt=f6bvut&XN{8cu`k6`%};IVDcHQu=pR!g;vk&!%@+B1~cf1Q<%phRqrQ?i>!J zQ(ba|2r<+)iF5NAeyPdSKYpN#q?KQa`~HxXq8{Ni=(fyfYO?7m?7dN|YVrs0Od zrO$uoQWIL^BaYn_mvX_`z-u0B;_*FGL#deT1T(1$saCFavsw?MN3kK@5V| z;E-4h;qWU@v(RQ#B9Dn$kU2_#O)IdQX-bxN(rka5y1BNV9I?y++$gx8lMk83&5gu#1g9obqT#uiV;$G^jp+sX-Hna)LYDP{TPjbGc?b#zltQu zez8pGFIDMbY&E?alV=VGx-mX<A>6=`Dkz^nDwo|64VbljIr?Pkz`LXUVOk?CX zB16VM>^Xl_m{g*pL#8|ohdo*-feJ^U3TIDlhuXfw3AM_$dWXk9q(1Mj^eMd&`vPTn zdc@c5AJa)Bm^KZJ?;S@FOGEmU9;2QPCKO>YB?C%`xMRE;cM@W=LEPysY=@D%Hq@1E z&|ENJ4e9X_Xxc5{KMS%m&(gd2hvZ?77t2jADMeV$ZloB{#L&uy5v58le+RLIZ#w;= z+4Xfb+hc#vyv`NPvc#V_;6ruH`QzCyfMKJ=2QBio<&5L~3I{zIzoM*Ly7yg1k`lld z_YSpvokK&a!u-uXvTm~P-k-UMhSL-G$Jc!nO%FaY5?|X>SQxV_NqwB1;Z^EvH+hP% zBq1j8DD9?iAnGC8Cv@ANA@}qtE7PZ37vIojaTEqW*zVJdvfw%S;msXV^13oV(Zmoa zcVzd88zpX|9k}bVOs5*0@=<)f^&o!+k=9LI+<)PBg0$-1itfK-8XA;Y9%uc>+v z9Ih3}ROMUdHtyXA{Ef?$tZi;8 z@_A#X6s$kw0n%{-IlZ7EnOlA;IF!6lp`P;8NQ%2vscQ#uP|1?r#6_WD8939PPrpy-nq*hO)%T~`qv1#F^uh>i#3n)NQA$o+a?kHmP)y&zS z9m;FedrObMY3_&;H*}8zH%%e9P~u&liI4X#v1ogBSq^P4a2TM?Ah|Ia8=1LRpe8+m zDTrzPkZwSJk0<^EAiq1x0vR7a+lQq&4@=`G!D3Jt-?a~Zp}uT&u%=zj(9Ig_^+X;s zx7Tx9GGbdPsiGO>k~Sn4ukg^csIUDtlh|J{%){U2VSiOo*Cdj@$&%#G{s-^Aq8#hg z;`{0Gu!}{s-s2RKyWhO{d0v#5n=b2RZWT2Wdn9w8TKU=~Cb>W}!?%aW->fieXhXVw z53$!9_vj1tdsu8L`~-oVJYY=?H=~N4xjf%`*v&9Mj8L)B?wAI zic)6!-HP$v?RvcWu`utvJZ|U(JW_0tqQlf!7+gCmbF5Z}cKKX{*nWXSB&ssN|1u1gD-$hUCFyXn^FXCp_#V|*3`sbz@jMr zRy&7h#KlE}4as?|J_!5Tk^5sgj< zKS$Nv5=lPoo;?62mV6O9Y+K5sKzMDl^kuTbG7Gt2Te+4azN(s;&M`rA3}UW1CfiHy zdlgpQINLTf+%$x#CUXK842+1Ee0w}Iw@W5!3Yh`caU!aGQ^ORfudHJ7Ed~m>i4{B* zJ2JDKP9cB7MY`OU?-dxiJGddKbe_!LJNnQIO{<@AmGwyos>BFY#VK`R6u*t}7~93c zs!@kwq?(hM$5T~0@m|*2J_3R!rdSo^Ucw_ncI8|qF`A31p6$L-X0Clsg0j$o)`;yq z=U2c<&eAAhad ziLLUs<>rf}-_1@RLziDdJu-CJjlXSn4hkE8?ySrd|B>$c$9e220%gg6Yx4QumjahA zyKH7L$^OQ`nRnzClB>wKJIP9XKplG|AiYWySv$Tzf?$I29ps&-;rk)dx_`-;*pOUQ zD;yt+Bn*Ke7Z_G9*{S1QZSUPu6(4``MMCUn+!;w9u|wT+bZ8K39DauZaB&)3k-f?A zZ!#MIv(VXhaL(EH0er0_Hpm-ctc}La_qr}2I+X+;?d$$yssB;964NcKeIysnL2uX8 z7~6+#`L5S8_lzA{y?mRoI%wGN@B3-`;BU|!{D0^9b}NowFvn*16^zBSeUyNnPPSJa zG?SWL?Viqjq01~Hv9}Ey6zj6<=PJ!5y>u?7MwYWqCt^q@buK#Z2jsyarJic)kbSP2 z3%sAgv7zc?Kr_Ijq`EmZMBbY%lc`DOZp^Vq-@bf4Y1Kac{5hbX-P%PrT{axx3neo# zsX~?~RyW6IdqyhrX($k0r7BbI6iRqiv+a+=3kYeyC8P`b7ZTEBX=pRQohPKzgk$6E zs3w_!qwLD0~(tloq3V?_?a+UlW?=a zZTO1cnWNn>hZxGe!FDTY_8?qmD+Vn6oQTVq(e0+S1@<%O0bP^N82B}?){vfj+{Ai_ zl&Fiw*Yk7QE=MdWb~qk82nfW3C7at14&#G`L@9Uq{ zi@}WBSN;P?ugpOz?qwH~08Ft=33&M?S4Dq585g2S9NIAakkvl7U15| z*mJiuVb3KS@`yikfK-zmWDwxWp*INCy;$~v!jAZ{-q@b^KPsMKm$Q(ZEX&uw%KczS zAW2Abw&|y8&@v|{Rup7_GcQ>mJ0{vWIgvF{L^!dQAApeDtNC^2mE1_pe$$N9G4b*5!^TRS3*5JE^*cA!eT74Ev(>*d=-+ty zcOqcfiIqO)XpXh`QDr>0_<^Zi?qdhArRG++>w)ASZe=iyF*!g;K|o3?K<1~R;;o*- zDT@t>!YwnxO@5MZV(QuCeyg7&eaPT7O~bW*;hGbLq0vr{;&n64kUKqJq(zk^+XHL}Ud-bs&J- zAfBZ#M=h_n+~VW9uY0`?#j$c;zhHeU`u+or`R@bv4TTF$AvuUVfT0$m&RI!(6Tq2= z+64RDqH*9i%u*688Xl}ni^i+1K5#?b7j&3kH2QAOEgEp#VtUqggD_AfbE0hECv=9` zq3AY&v7~1=ZrYfR>{I2;_;cU}_YS^r?8l5;77CdDfu(@8hFi2qRPy&GrH)iXAs#}B zPV@fy93%S|aZ!DUEDd$4oJ9R#5;05xOi|Z@`1n^~mKvq4H|IvFi$mkSXrwAkZA0=> zguP3vqRER@Kk4d`Q5E;6o&g~u&=W$&#IfFy%-%LCNJ7lm9x5fPq!vapilj6VA<0?1 z|Ch{*JRt$`S1<7ZQu2NLW>i06nYh~I0Lg}MThAI18{3e+z7(h_`{{o14hHbjo#ECq zPahX*yF>FM`=olbiEV2&eVB@s8MX^YqocIyZubz(PWHO9h)I3PltN@W*W|GH&zOv{ zyD@RJaK*>}7TyQ1*CKWQ5+7wJxOa0X4l)dYFzTL_2dNF#KY-6~J!cg1E4K~t7jjJ_ z{{>wSdnvRW^$r0Wn6a&81JL(NgYfOvfTH(;10 z&MEHgi+${ja&Ok5nOX3**LnTOe3P+XiV*JpOK!6H651#6C&ZZP*k=5t?;FuVtT;Cn zV&q?(8AFE#Tq5fXrmuDI^;=EN;E4gFSPL%{qj>lxzdJ+S6*|l_ikG+guon!fkuL-O zo801#H`4pZIdD$iEarcaqMrR*{I6WJLHWKRLC<`l`M~0Cd9YZ+{}$!C^@0xb-Rc{3 z%P33%eNJymKAjUjLd9@iUo@ruJA&+G2+}Y+hZ1DFP2C!|ob(kL?^#1dQRp}$rs-Owk6D1D!gg&hQ@%JQY4X!8Z?y z*37PT-tl4D1)@xA?|U!dZ!}71T`1zJWqs{ibh548Tn>k#k1_!YU8hXpV*0lFC4&S& zeUJc6oWp?%0P-h2lr?6n57|(0fZY25e487=wt-JFm};uG5kwIAi}#XY7`f+*1`6)< z9-Ooxb%oJCD;rb4F#6}1%~C=JHKZPvHtO{QwdPXHI#9LoQDedUn|#c^NHk(7bepV- zL?fCI{vaBWfbThIPv77clmhN(-MwNDy;yQ#6p8Qv7@qvFe0Yca)f-WPMjh%`#U0Nkp|w!ZcTl=V?-t z*Pkh`hklB%$V1q~D7Vy2bmDqJpT44fm%J@=myPB38UOFbg5%&?_uuIS&urj0kT-MU zTbMx(m$#dW8NRqym#*CxCIoQ7>7pXbH4xd{`*&KlaUz>MAo+}>*9bzNHO0K+YY>Je zz^0nUdR0gwzhOAp)f^$nd&oCiQ@>PY!p)PVrnfjf0(kT#!*iUmB7~D8$-QQ(ewK?h zPZCnIgn3Kodbm1TvlARim8Hy}oK&dOA1ypucSLirT>H#?&c;{1%62p<{J?GZae z!m&yCz`@;s;4E_Goaow6+nJmjBUWl6syaf)X|O*+#ws!=a!`+6e#aVcL{$L-EKgBO zLu4fH8S9TNm7?)zo{2h)-D;CN+Sislll?nC;ts(!7_ta1iqte$h7xJ@qNcgZmwwrH z#TFu!AANV5X=L>rf2fyEvAx5sO|UW-qczHz@4Tsvr~tOx!^->TyppC z-iNt~a`OV@R=+jxTHiJ0kcPC-|E6QV=~S5&a)W!vjq1`i_hM7Hd7JwTzYWO`WfI64 zM{>3RzrPq^JDuK4Y%(0&i5VChsW~dC_`q4RJD?!|i5O)N3#t!%XsSMMa~GHoyyxBX z@%65de?+N?qNN`G(NMD?;2+Tx6jJk!Ga#C3LV;_s{KM8C{y6{8GCjzbe*C&c)J1V@ z!6Mkwe&;;HmZsv#)V9<%I-e@MwN(sDB!p*|1dsvNzD zWi_jHJf@+0G?A=Er1^)pRUuR;?wBAyW_dHL{X}ZFRaxFOz%sYKVqwv7lPa9)L{l+A z7LMf#-D+2OAe=|x6*^_BY5mLbV-!)y8J=CsOS|>_TXNd(XYCWys>-y$K}2ROjh4Dq zkW91lVs0P^(s-#XD%zBg$7Pa;{^OLDHFo7AHVBz{^;S*Jj zG$DoqfdyF2M2!teO*LZn9cv>syX~Uf6qLMq;&Zu->P@jpEX^cH%1lf&j7Q%H5+VAF}__T2U4qfje8TA%h}FN z?D!eqTfXqLhqSYx@->bJ+)DAJ&sZr$^M^P(GJ+VINNHD)737%=Do9TLD%v}%sJ)YN z?LpoeS3OSe9r)f z*xLTFlMHnL#3+&onn)t(s5P=TXU(x8oS6alSnoe{Z{l^90p2h&)Biu&2p>;<3dGc0 zd<4?4NLmw6o)|#WzHSuaZca_UI33Ai=Uh-LDPbV_I&&mIy0_ejU7_ZA2So$Z=Dobd z5^itJwYNfT68?}l<{(2=cxT1i_r>@0SgQ=+iG5o-zc5nZjQ(+0k)s(h}4tJ4|IT!fQpbW5Ro96CpE>OW5WL+O~tRD zs928TES2;;U@FW~>7BD3UOV?D9^P|w6K zmzbfOIlVza?aKZn4(e2FaMe8E$N0$LoU?CL$-tURdpEHSD6|{g`&w9hv}){(CZ|@2 zD9)6ce5htF2CUocZ6PT+#c*5^sNp?k>jI{Y+-oaNF)^Y-CCRc}457_`kBhXy21Hy> z_xq|jrDmpIcu|i{3J7pIYW9ADvvFsUJkko}$U~TR$1q5qbEDYQh#I zIvng5B&d{bmReG4+^O_7#-5l)kBX7y;_p3KYP8ChTCoeJxrF0k~wyh+^fc0QqZLWv>f!VOT#ybAZ* zjHfu^4F4%3woi*F)uuKgtOwB|4Ln}UR<3IQoBcFn^nqw{nus8jI7RPw)K=*gkKNeC zZwONZCB$uh$Ru(okn#KGGBsXba{suUFfyDh>`!u%2+>3x>_LDI zC0lvQbASNM&NG;Ff6zy=E6ucAY_O+J?u6b@Mw0LP{iE5gNa}Myx|w|AaYVOCVPN+T zx?JgA{3kdwJCSNnk)~uM`Z}!aQ5*8}rlxXXd`eFLF@|O_tlBcM`~Aoi zY}&IizGqPAwiCHzD)YV%S2&H6Tmmu{Hiql!>Ox;aDDhD*GsK3K)dn{DTKHMcaYW8P z#5j*pEs+H-Ik6n$KrOlrD+bAT>3-Yy4PhC0(wWNf3;yv0j-Ru~p@f`Qh5+B-z*tr; zH?ubv^%F{*#|0~}U-HJjY^G5&H#wz(3>LaYf`=$lnm)rq5k*dv&D8(FsuG*(b*qf7 z?&%A}ZjGzPdIU2E(>n;JF1dpRyucQnxrQ75AX0Kp`XT%fr~fuHo8G|KB|3WzyNKZ! z+chJVQTwX%Y&6-66sHfpCya@G3l!X~?OLO^=7DdUh*%L0JuqqB+~mZ0So%YWNqoSV zGxrR_hQFSzr08VZJwgw%5L6LLXn`>hzU5WS#lC;Gf$z_vn5(7xonv8B1goJe__g55 zc+V)3e$r$i~!SuxXP8RX`-j7JNYkM4rKzG>4CuWL~hN zFvl>ZCpfVn11UkcG%$&&*Y2DZ>}TUo_b$m9zku>2XM9UdC^A%h(eNMbXbc&d4$q{S z#?+T^ihz?ZbAkzs)<46MKRAOLQ`a)F7@4QcLxZ^GSG|41eJ&UtInsSJP}ro-?xd}1 z6L^GnyHzMQz7I2$DD%6KqbzO8nz@H*YOub8_CekF(Q+PnhvoEXFmCWCT|1O`jHen? z#|A0r7KPNT0k>O(!c7J@miu*yknV=ugbtMNJUSF3ZcN8dp;5v2Ix&T?j3D7uy4T=D z$u#IQv^lsD(M)4L;yDR)yP?t8n~ahb1y453OldZwq!28N-ZJf~Y_r|^Mr)ST+FT!T zL-#3Sv(2uY;2v*~{2KAa>m^B+ud%Wmj$t~4!k>PhN#oxa$egXqdGOy);~)Gb zGZ(HjgK6cAe=(K6^F*O}h`V-{kLJGa13DC%MRBYPFldBa#i6Im4x=*)Z7_k}p@hQD zxjj#}2fAM)_MBUsK~fi+TdSASAKe||&NFx8_5u%#Gc^5`Wjv5HwFS)Vf{&Q+_+z7BivcNg*v+uBtBHjn5Yn2PJIPBO3j7CGK=M{#KotDJk*#PKAdPl!)9J} zu9syn^PXM0k20L)E$1(!*{~|?^`54ChmZ25p3F~ZMUzzWac{0#`M8i@I7fU6ukhon34!%U_c>&}brmsl?74%gb9Y9@Xa-=s2B!F=-L-$`(_vG2%-e(XD9 z_BQuV1%zH$7eB|}R)Iy(kNRn9Ms{e{YdAuQMck26MByEdis(3Sc&8TDYI(PE(w%E? zDkRo8qg%(da1=YHby4ZHIyk&_QIBhMFsOA=Pbhz1DDgfYvFnrZXqP&SgIEFf%pDme zW<`>*jVh%>o<(EwHTHxH^oPdO^-7s=zGO2ouF$gS^~>8=SuZsxh$ygS27cc)rjnRz z>WDcno4BAtdu(IP4(GpUgpk3k^P}Lc4fd^X+P6~PMOF}GyS9x~H(xuZ9o4^ye#WM# zld*dBFm|@O7prmijt~Y}9}%*(_3)f%@&H9p&G81Cyb+d#5j?_OJyoeyvLQLv$gYLF zh=l-g^$jDg=KI%u9GXeoZTVF$t(aCM@GC3R+Qv&`OHiO>o{ufCV%vB~hpt&f6bLP6)~0X z&2N-nbMas0M%p17(aMU=X1hqS?Jhp-{U7`zX2G6e_b76ovYgAeiJMItx5nlb*UOUU z$1L5crfLi%)dZ!bTF}@yV+LR|IUh3E?DQs?c&s+8ZbCrW>|jAzTNO$6jihCX0VC{T zZkTVW@!60w3*n(DeuPjJrQA-U6ujcPs?NN_6_A2ZMM1k-;N2<2NbXayB*2%T2cPNh zysG*_L05YDOt?X6)qynWz@sNsd%cShOv#8pdgn?!Z)ix))z;h$?EQa~P=~it-26jE zIlj2PBzCBGtZt^b>9HUCHun~ebO*DF*^Qa7ITbpqtqxpvg|ji>KSlJ>WC-I|X4LSq z$Yyl@l^D_I$>x%ETUYqP69I_KnRRgYIYRP4ch^28y-nmS7JRv zaU1B)dDz_ItGrchZ#CPu?(8(TIOCRV6TH=8Z{2Rc#TmCg0Ozn!#4@PZl=u-Z8OgJKPkc|Gg|A01 z?UiUK-Jad9xyGw=^xCsqX}JS><;lQ2j2B>$@If-pZ+%Pe&$(p4zDF;+(=H=d@f*m| z%IC*%{D%d41M&*dfSBe4;_xf;H zsY4yWzOVTkST)Bo%szRhNZ=9vv)bFJ{k26SRZBD|qDD&|k- z0G{Ur{3M$EE4S(;mX^{~L-P3HP$`84SiC;BlVv!SZsvuCR9h22^~sl67GEF1CStas zMy-p){OR%MmhyP=1s>SI1CdnQSl&=3jDTwbhA@?TWGy-AIVvjr%Spet8js<(-tl%euzT}%(z zzvyVmK=bw8O}do4J3_0$`@butg@)uGYPp7L;WuO&c#4x;i|HYMunmo(p$+xx_{>iP zPe!xYZq(7#%RZgyVm~1X|RR8R=Y0V>)rylr!1GrB4LI>uGpz((q10J|g!3re3F{wDrXHp&-nwbWbtQ#a4K~5&nnMEO`Hi zm&1EQ>X)LxNXigr=22h(plSn0;r#NKZHG;s0$^%#z&ypyyL`F`n8Q8+n4j9`eg_Ei z^C0*$dOl$AS?pL5fqRN5>9Pv9B2t zVVp~~I;Q@h$;{FU5A|<^+OFUO)Rn5{VeU{^3a+H`QlDe+53?63gHgv>KhC(xSq&>f zwvw~dpVXq#_}=!0aq*4(MYH~L={YXx0h{;7aqsR_Vr(qU?=IKY*Ksn=B!CH^W?}nn57{-IPpu zj{+gb{gvsIp5fAE_e5}kxP*N;=E4SNz9RNP#J~&g4A?a)sf-YNOAnD7t-^Sum*wq4 z-{j@o5X+h=E0EGRb2cOAvM`Z0UKV8fH{T{=|W zrqb9Gnai0-Bmg)+rVR{5uR7n$z5RXr_U*a1Tm84!(KX}ixQ3TAN2>){yaiYiD1E?| z7=QtcS{AJw1Eq!UxZD@cD1^^%e6DRgwkh)%>a{@oQ2^R^axa$J7nd5){1-fY zrFmHhWs5oX_~e9P`tU_NOzSQv#r;hc6(pe~w_KR8VVl%|T+eDXM{cLa=<~`@UZX0lA>(d*%guz$gq$EV{bP|qQl-;=Y0nMn^AL}iRRtd&fwi2+efym_9E~(lVJCascVMUx@SBJ z9!1cnf)b;EU0JKAB!ZA|L2k&ll5gfFWa_DU^KXGe#z+^MHj{g zxs#+Y1pD^bVDBV(sF!@MwB$FXA{ySQq7-@swyO%cA_lSXFtsKmmbzDVsC*t#v$;S< z)g1X|e@Ul)Jz+&RW~&O~<)=42tWTPeewd!J`~$5C%5bOJ?LW#z(!L!Cma!G zWi>PmhgS9hn}qCg=;>}(F>=x5SC-P0;J?bdibp{F6VDim-43X37nQ16OtzQE7@Kv^ zOpu`Qir7`E~B!@Zn<#=6jv}d>MMo)@Xcx=^ z<=|jEg2c>|wUV|Q(P&0HGhvfqbjLi!4^B8^f0&j!&04^@4_R6GR;TQzaKc3Z5Y?La(_GbCvT4%)9ARucB@Dr{+n$4yx}4%a?F9l5Xq* z%LFtjKY|5j=|)JHdW5V$7&!;>$;?pxqm#&7D*8;pzmEDRXr;s>0Q~_wc0BqV?`e(Q z#;+b-QJSoOA}9#R4fLc}kQ$luJ2unPqYC*4LCc}U3A{)mCejROQxQB{hq*NNvZ^k7 zp#py6FFX!ueT&2oF!0jt9qwi4T7F;x>I^@4UA+A@cZ$8yLD}2;Qxgq$9|TJXwaFGP z`SBr!SEj#3&F7G z+}>+@hGo@j!I9Wf;(W$_{Q15D1iEW+7f;0@&LE#>W~+BW>*6O$V&@MayzpkK9Y10%k&oK4DgMI9FN7*)=Uub%z>f0NVcr?r;8mkr1S11jx(q#>% z0sHl>KL4wO0C`s;z8rpLot>bcinviML$bJ2n*aN|Q1*@HY(m zIhii#T!iALmdcE?YfX7&e%|d!s^D|nr zEw;m3SUi8rV(1puZcc3eLeXKZ`-d$!q`*nL!3oMaIQg;Q2_9|pev(JYcXKEaK?^JE zT@&lp`s3jSEg7~o%Ld{W>8N@6BR9TTXP#X_Wsx4R@KOfU$nSwQ8pfb8bZ!VN&ZP@+ zLbgXFp&ip8MB3jVX_y8&^Wizn zK$wy_*R#Wzoq=Pf5N$*(e>cIj`gSOFYs50 z53#EW0aAR4;=s^t;x>dyKX?~RPdVksNHLwFI#*MGL_LPQ&oa}`kgE|Z*FD&~+&+Fi zJ*s80p;5>f*X~|^uj*)-=i54OINy6rGKZf{$#hOnAye{6Go@o#hpY^PHDi_h%mT5W z`?u8+LQFxsCK%}8-dePckg$N9BpZ_EBe;A2>ObDURkUjgaIUsJ+^}GTdI-$VwRN_f zm3-3e$-3hA^UTaL9=BawZM(Q(!3};F)w|n%qy_PYx!%=u%j@SJn|s%};NR=zM}I2n zPSt~D^U{1r!*0gO7hknpb@>e-UAr${&OeGAp%W-&>&=3yYyK%ua z%nFmRH?YYp4WY!{JV3~Ht|MzA=|v3JegfA?J$T9n14L5iAjNb#H|A2qrfuM*lM^f{fyz>n*y?NnPJ)ju#-OJ#SDoDhjQm5oj1Xj&HoKf;1*4laQp0&|j z*65Xxw!~`u=C^$D+B;gKl`i|Rr;jo|Ds?hB0rIfPD}l!ACj_>tl^kiPFoRaF6v2Y3&_e@m_{6%f_LBGd2|lXMDb*)3$yOwqEtc3vyv*< zm;FM2#K*K2rpvbX$Waq=JwD;$Y`&B&Pdt&n7=!1$?&;w3jKOniH5N<^p7V^sGryZv z|45R2gHJJdil{zr@D#p}yOSX;60srYJiZN~7_r*OUxBR;zyCNWR;EuJpBx2rAIRl+ zVrOg&WOK~?3bq@}T8S>u&RUC2fF`zr8rk;M8Yo593CvBUT9CgbF+0O5dTR0&-p>&h`V98_O9XD!CX;;lAa~PVi#w%Mz$^pbu z-<5<-p7N+ZFF__<_SsTNaZHj>+l6phu9R?vd)W%Z8axwLVj{aFC&OinBGKh?ud{f| z%Uh6ocX=x{d5$Jxsik8iBaLL)-6e%tNzM`zxk6ZIZac<-f;v5ZbjNUUQFln4xGBOA zO^xRIfn0Y>xTrWJlcmIB+Zh{c%wVEtzA=VxNM4rB%b1*$lXQ4+k@dQQV^&_}9x0f` zEJUIMW*U4TJF*~eN=jtD$ZSG8#jAAj^Y)?<=E#z?J6|3~ z>vz&R$oGq|YFCiBy^=1wkpttB3Bq{FFX~ob!{A$@yb{y5kdEW-+ER%4Li)?mwd(af z$5e+SCzKzK)bxx-cb|~?C?ivm1zc`RhJ6iJ+O&JC9 zhg{+`_UFu<90l^6f>yo$`S}>whu6xYWk%&K>6RK_hL-36?~9f!eyn$-=7!pUzJpAEh6rExQvm~sCJ|WL$84s|2`1!K9^7SPq-IYj zG0W)sYw1fUajuS2^CO0}+(2LJ+uAWUFFC$02@^jTpN;rW&HO!16FXZF&{jbpJMuB% zw0ZoFX|JnkkBw_cKHZpns$TgA``x9Y4sduTrrY}D_PUx^8k2jML?{l=LXr86u$nq4 znw%fuRfl}@H^K^LX-LjkVe409I(Wp0VzPhRMrc7Vt4tkyXsG2w*URFQdyrfz_G=11 zA_k;A6zezErEe?^Z+d%`_*C7fJ>k;lYP#%6Xu_U4)=##@Xdo1?0nDY#lKV7Aq7>4? z5rty~&}lxD7>to1dTCc=%`3g4$+`jM2o{lW7F3-wpe{*@`ou^y`C_gMv3`xI8?mob zJ7uZqX(OsB4ma@8m(=jGP}@!7TB`8T#X7*fYK=zQTQk9_oToD*xtr^MfT(wv%|tXs{L)XML<{T|%|!$45PD(;`Sjc%i~RHB zX~z4!e>UD{HzuECu;H!l_i)3h0||$(6b$$~OK=TrYe;s6lUp)(7)&tc3(c6rLf;6W zYtu&5nP=$;<9dicu8KFz^(%9tmJ1E->cXL&&(QAhhjwSUbW46{cT$ed4lR}RHL}0{ zz;M6Z$fC=yVsxFO{9%n~ScM^mHNvn)lbvUalde{2EO^2Vg@= zB=kl`8&WkQQ;P2o=aYO~JQtT{eErA*Q$iuW3;3m&Wug^KnQC4dtQMJ{>=yf2>;e_F;Q?)$=3u=j~(P)nDcLZsmQ+}L&z7-GCaG`zrpN0mF9$yiT3Uznc-Z> z({rx9BW-&z>n~Az1N`KBB79F;*M+0%Jc#!n4y}OjZf-OhIo)DYrI)U&USYq z(izhK756cO{ntjUj<`B1>;3DBZi=SR0hMpbMeDka9hh0n4SLU}3(&QeGlwvnQ&+4u z24A;jyy@u0SQ?NF`hDG8ehvM=UClbk7h{B=+}vz1{Tisl49H_w@)*x#9^;DM)YasR z_+*myrJ#`T`pkBou-361Zo@yATTBPjm`O@EJBNo7at$l@3#%wD$d5e8q@Hp{kCvMr ztL}htOMv;#GYz=g^idw-I^wp1sR6j35Aabwz|Mo+pYVQey5uYeIo_GUV*_*0KhAyK zIssymS$1)|i1uuz*qU9M)!r$(aezq8nk|2BE(Z@v)Va%Cksq24;MXdN;dgpfq2a~z zQu46q+r2_|JjQyc@5LG@FIFE$aaQIUidU=N1)XoEK0-~Y39ri2o8Av1O}Bq~GshG) z^HZ8B@UNNq<^Jf?-+ukALLjaZ5P|#?@Du8Nu7+*W4W=V5QE*809CV0>I2Kz8wXHB? z_{sL?P~w}qD;qQ!bcW~bcekHm2AcKf|85~J76bRZAIdNzFJXZdRPqUPr?0ozCazd5;*14qJepr8*3D3((_@l*OP@27xULYQj}9vk@|z zj0PnUDpV5Tp|J*KgNbiqN6^V5IfL4#RoiG?6C3l1JA%63`hgv=d_D4jU$Q$#eV1v6 zfs&!v1`37Rm^l78-NR96W&hT_XV|F2Nc!`oW7-qM8Kk%)?p&~bhgNpR_nzVO53M|q zwJf_1x7eHB+}3d=&KHcqUov{VmFbkO^qZ!Jy;CYFpSQ0le&%F?WG;EHI z{*mk&|Niwm7*>{+y+$#gwa)yk%g=NgRI(>!p5L}0^?nDfRA2+0qC6bUx1=x4&-AHPj-m2t*b>@NY z_tRzn#dR1);JErer-3MwtqP8c4N@=<4ML@I&98;riGTb#-#J?=&uzThOvV|TC);4YNiJ~ACREd>u?CceWG#uB=MW17Tr}=)I*acerIJ+8S0%c{Rv{gPC357eQyCCFeJsCuYBLO(-^8m=^Au|7)nrz0BE6UvFrhkr-MMNmt}N>GZo zSA?^jvA1|Jlz0hRg5PI8;!Z}!BPcJT1 z8S6_)7Pxj}U5%U58z-huIUDVwXM-sdao&!R6wiBBdZV;z?H$8|-Jj7fjX}K(#2DWiz#%1t}_jE)mbSUw6E{KBL_HmG@r6rJPz8<`$7HKN;V;%rS!R0)vhMLDSVu$F) zD|`$crQ?qy=hG20?kz1IPj^R*J`d!mOep)3B6DMU&?k>+19k%SEjAk3L|s5t)+V@| z5N^N;n-65Y0Xg|6l6*|_s+Rkr-B8;c?n8wmX@kUl#;B9H`Sx&CSGbi}MUWd-J5ERD zbRM)_|0_Bkv`VoUiD2n|&2OqJzIW#e`sSLD}^0)A$J5m6Z)L!%C%@XP${Y;l0FSywxkR4?390KhcOOF_+zb zUUxrtq7)7c{4`DE!7itcHQPPYUPXf;ND672+tie%{ROmdob24PSH57baI2XED+eeK z3|mi7rZdk@bN$QV!;g4vLB46Hk0rJ`wfJh{R)Pf(*=m6yl!$R6yB3vVISMDAMI&kr z+r_n`{(ik`SS#%;)d|0HvXyky%Qy(Dk1c23U@kXmz_L`B+3*wr5OAiAlELe1;MucWAXL7_a!^-=hFx5+}v>H6}Ft{#EFw zuF5?VkiWw9#?>V3p&pJal$^0lgAdg&cP=zfIMX%Ror?*{ zyT1f~>EgRemZ0@agRTHJ-p=QN1bGFi6v=b~+fl)>t@=oi6y??<_r7ReLA> z$w>1qCVl4)k-hUZk$fE_e>3w(_z5Rp2*_P==LVA^Z}Tzd_=Z%q*Vnff_##gwadQyr zI|4U4ZfZsBbt*bmdNs>XO(WFuRB7@%+BK%B#jQ9%8q$j3dWS7y&4*tTH!S*aG}Rk_ ztG>BhU5LQ}rZr2FYy0j8f@m7c1Se@uydeejqlfhpR_dA#TcfdvpBW9SROM)u0pIE1 zUmpV#U`O`Ux#h-hD#HU+m?P=y${DY^nsbKVbZR7ZBcys3D-%X=yd0tr0H%pl6cU9B zI82^fp_lxIVvpLE82U*%M}?8Z#I^MPS%r}{GO1tw8=4Gl%7#1(WNme*gkW8I2lL6b+@8CvA$2y- zvpS2V73fE&-pg(9*X@3~ETvYZAJrNASButgKg7-WgC%_b0{j*&Od&&u1F`6tH415L^2-aPR#R1-?|)tkKmFMb*EQ)`t~@E^&! zr-KD5nmnLB={6)^mW1!O7h3@tAEh_7PMOVt|BTWl-TW0Quu-%`t3yIO?&CP37Of$< zaC|={AR@_0AgWrP%$^jiEmyR_f^r>2lJoe3TOmO;!3-k#^{RGwciQ~8zoL=uk-^|= z{hcPJONzrC?9X7N&`{e%#yCqm;9JQYdK>M|B@_48fx67P`WE4zJsaYc!}YwmsMM zZ%LhjS?TS@E<}sW0W4^GSE+z_#saUxQc0|7;5LI6;0V(yj6%+LPtbQo^JcbK zGD=7#|2UaPVW;xJO+zN@fRL?Bb^z1KoC}dyTsfsi4%FS0r|vljXakHM&lmsr*TSAU zm;QIX47D|Cu}hcTrk9GrbT0T55aGJTAoe%i@(3q2kmew4u8f#FsMD~JNEPcIv`{I- zW_JS`C~o+Rp6ZF%j+%Bzr}tvF^|Oed5#=C#QjQB~$J*&o=kHh>E?;ZsY+ZR*>?ZkO zdpFjEYh%}0tDMXSS2JB}8BDZ)i~zg%!Q8)-|Ldxru1jo6&c_6wpSpenm9ZD$UcEJ{oF1SBu!W(fN#dmUSg_UmtW;dM(3u;kpXQ%M8d?!!wV- z2C>ZW7G)S(ct!Wae=;xQaKqSw^o!J)B#$C#gx-3x`^dPYbT%=NXY!@h5Ak2<-YpHu zS*@Y?VNj^q>W*Puu{F1r_2!v?RoBiTjHrpfTK+z%YALgiiM!EuAw>>ADt4DM4DxG@ zB&W-SfYs*yh7`FH2ebV`wf2m`Er^rfzfAq!|scr_?s4w1Z;$h*y3( zgx!&zM{@ZqM=-`!PHiK5@=WI5v-X}tPF{NhTAbbegw$}Oa7rX;2R{wLUIka&NnJ)+ zP8yOIVR+k`c|pfgb>Tl5WpFg07Br&1G$z+&{>ZuNf43 zeHwn|(1y5E5q_{z8i75BA0aG2Mkui#1vbD_<_(r9V8gMD)_Z%Nk18W?xaNYLk(gHyLf2{iFQ$HB1PPjA7$OBDQ>* zQn!5{N*W>0V0t1;$^P~v&I7meS*o>T|&)6DACD(FcSWvzb7-SATrJ041$Y(BG= z5)w^*FPfss>Eic!veBnaee-bkQ2{u|0QfDwU}X^|8VWiiw2W936O>!kQJZ**68FMp zdhrT(Ax>!&pip8mFiO0d!~9!r+@egmWz6DkAGBm?D5vdOT4_k$tw|+>dQ=|;NkG|T z5_<)%4yrh)%6u2a$A8$y2ZRzgh}T1MKAX4$f{X#F;TYlarx8b}3bKW_xLUq0cDjN1 z(f4JNWU?|9^wj|MKIX%Q4>6;c#Xq21i-pD1j1?e8Fgqg~QYW#iiM3uI=Bsi75?yX) zMeH@{a+wz`0-YNgFhZD{Ocs&?QWGk$Z!!d88@T+}hO_Y7`sL;k0k_(6l{Mj2XyIA+ zMUbnn=D{td5>&(G0ESNceUSh)mg!HXl6P&3Bvz&j%Oaz4!6y?vLy*LXhz+HKT?F3t5XF$mGrPzEFtMl%z&rz^@aK>=M*09E~xGnPlw7tLu%E?(-L8A(m|MbeqDLgKwiteV6G zy1vp^9P5%_LIa<%Jd#47odK56s<1ORvR0a!`M~wwvpL2TMS!FWWud>a;g8wNfUZ_D1B7`UM#-HHe&P=I?n5p6DP{6>PcgAk6J@ls9+E=bjz$U zk)V-4RZGw)-^j93m?!m8A#ieW3v0K6H8T*CFsbz>ZoG==NPxsRCbG}_aJn##47#+E zOx7G<*qqt9M~tW&f7EBZDiIfhLdvUDff$9p*siiitXz(-49z#zJYy@?H?l=8cKrqpzYhKwjJ@OFL8t>Gq5sy*37mk6R#g)5ZF@B{ zYeR{FWX5_I29Mc3C*R9F!6J%hH0%dhQyJz@n@5&l$vwhmgC6lKPDS$lvV(#k>>hsM zn4%#FX!>$JjCXf-g>wnTCsnB5x3CPs#XN`BKGpv`k(80-V!As!18VGV_yv4IGc3h^64NVu)zW*S2wahHCIYTm{H&`le>!%c$gDhC6RCB2%%Wxxk}`yNK(yxF*iH|}=@wPN$APgIQC6bc zDpeDpzTL)9{9!|Q6L7;zG}zEkgTOBN{@vJ>Ja6(m@P(g<0k(N{HP5QBjdgL)uf`Gp zr;&@B+f}Q_nx;;H4O0p@@8TxTVl1u8^*-xprE**C@#1obOXv3?PQ>BTbWU39A{C-8OPUAUz~+O+`<~b4G|Gna2dI6 zyP%`&(*Mu5>fT-gjLh?U-}moh=IVHV6h*UjUNKwy%3i za{Mc@{SC&O^d7r7BCMdMtXtmc21Zg_7wTq}OB|1zs+NKzsC#ATB#A0`rMGLU(v4hl zcIIh`_ltt&VH>Z8qLrTvkRH4v;D6(vMFKBr5_PfmjdNtk=Vtbr{@0d=j>br+c;oNz z`|&EFJn-(Sb%N{V?C#y5uuR}^cLK~J9aj3BT1_;u%SOknB^7NoF)G?TIvDedBC{;) z{*9{Pxv?>8O{x&5{!nUF$7iBiU7E+~Jv!q7q?Z-2o;vqx*;HdQ9>_o>@vw5ej}$D* zhD)CZ`g8S()^XqZA4I02N%<-mb5P=!5I+-X?3nm9d#nZeW4@{DP+Ob<9_D;9&)C4z zFns*E5lXSakVyh#p&1&bd>EwFd59@e?PRULUYc857|sGa3TgWGu&_);JkT!2Dax*G z6D)*LX+@U(%GevjKS_?Qiq6>6er1e>p}y8ixY=3>6Sh?uu&UUQt=eRf>$Eqj(*?PR zhWhSJ?SQ??X;=u;7p`QLO1!|2rYc;{wQm5*0p5=cS`*JvM8Y|Q#b6$`#MSU9+i^lB zM{l~!<4i3q6}%Jmc~wD}A*qJvs0cDdj-d;5mIZ%@iNDoC;lA*#IY-jOXm90j<=!wF zgG+ zb^#ER1GV@hr6rPBNdq;}r>24;e{vprIW0Nq28g{*5DD>AB@Lws((>o((o6$7$L(XT z)V=O6Yi3`Ba_sD70M!%GzR_$OZIO;9Mq9*yyqVJk8`IYTHYr3U_fU-1XicT7=T`Qg)p_~>O+$_N5{wN( zAP6TD8l<(~Rrg0``7d>9GPfaV&Cn0U7*Ek|EY6E+slNn?ah~ZZ^h8ZOP;Bd0_P@=u zUsXrhcAD9*SZcogic|Us)FhY#tRYPHE46CvQR(#=NTKm1q*22TVtg~gmF!ntKw#o* z{^7U^v)B^4Tub->-A7##x;mh%DD0&Jha)HabS1VUBTr%p6F z64w7N>pjPl8lJO7$b9sPiGm!TWk8bAkd?6s2up+*U1`ig2$7NO^s$x-sPk?pOJc3a z`vCc*m@z1mmj5>=>$kq|&)=K0qd(tYF?)aQj-pMj&qM+00`$&^W{UqBb-SEgmj@Q@ zJ{o;>5U9f)Ow=@@xA(wm)OE>-M4cn*PM4DZHlyUf%_#YAE65|)KRXU5of0AYG@lts z`Z2Qn!C`#=Xx_W61QubG7;Z~#bJ8;DcmML9_}sFx#_Grqq?8M^;_P~T z&^kP4M~@IFF#B$~`ZrP9t!ZmC-}uX?+&lv9#d#Rav&q>~@}pnG)Zz;l!VdzQ{0i|9 z+7{VWG2{zE zPsWnO#5oLFYyrXP5a?1)h9PK+c9zXs~OK`15^LqPW=SBcRxQ+@Tz2JUE)%n zFpFhd-1h$#l<;kSqg@UBP!NG9$_9XQ3l{)C=iY0Y4XxMftaVC>xPqPn&U zghtWqG*r@YWi-4t@g)0^Cc8ap(-N=nLmJbo<~a@bYKME#JiswYKc3+qE31Ly`GXq2qE@uDHW6gBQ!xo&>T{R(X0mx>8^o{QM9NzF z^1+YpbT8jV1bM7?Q_;v(-SeQ}Jog-)U}_))*VIV!1LL^uj_}Ku`sD}hv%D_RgJlqt z4$)!@494+(_>qhc-%QWRg>dXr9R!NJT`?zyCrnIS$|mDa3K85U%8vy6IyA;oCX~VtcEfC9>UEW zt{&}f4&?3OV712ksuka~PZFZOo94yRItS_omQD?K3e-(6h4mezPncgTeHP?}CeHPP zUk_8uWo;g7YO8K7pWe}D=1>@O;|hJXBu_F68wxRJX$=7M>0kt0tC-(M+d+1Kt%2bl4+ z_VD~k^2{`}*9mkVS|E*g*mhqQkd>x)Gb>}cZXO(j|CpT4J@d&aC?c9DYWw`Mtl52h z>OV*n@}X>O?x5!V_r?F&V(W9JAAP)c-v^TFj!Kgr{N?uG`Ui;@8ntbsul2Tl`Ypj>9K@?96{7vZLpe;^-RU za8^%TqXpU93);}!wPP6Ef)x*y67Id#J*S08Mb6Jo{bocW+p+?k8g$v!u=uInf|2F8 z@`hIrb>`pD4b{vKy_^QkR7KtNZUuaoWqw$^kaCUaaIiGT!&pByxPR-%fTGL9`HCoF zZu1PcjZ8ybctBG{cjN>V3NlRNQ^)a?)rD|y?;6lbgwu=55q?VBFi=a=h4pW-_Xi}gp>CRR9mhSIfw=*nkPA&A(Z);p%wb3Mmo3Cg2X{IatNJPryzVDytF3{|9Ymu;p>U7B|!_lTil}u`Y3Dyg(*;|$Xg$U+nk6T zH`S|$zJ;d)D83}u(3b5(;QfvUHJ{%3#=;Y>{x`53XJL6WF;1{}6!dc%XSEhH=)^EJ ziGeLK1ppvwY*zXbo5n%RI?JE>yePXC*Xm12EKu2F%a)2DCg+n4z)C{+3H~mL6a4Bp zU*}Z+{yAHHcj)Ns>XLw1C{+K0sz0MzO%-Cvd5JIh2kKaz7%!j&(`EXS1=GW(QHh*2 z{l9kVAEx$;>}M&UtI7YWrpIR%M%_z54};Z%gaI&L7#_1@N;ii+}fBV zqx)9(l-=|YtpLF=2dNtA=zxKP!aeLfrs;Cp$(nnuXU+ZZ4N_*THif%{OPdwG`;<)K zFd4C`Ry*_a>giyPFbmr_s!Rp=k(a#4;1nr62d6a_T8q=ra4t;kvD>1tmXe@xXD868 zcmWOKrXMncFj{(#`_j{2nZ-&@DY-Fa>Dj$kBaZw~7`_OmSlO*$&zT71Vs}!RDOl)F z#m`Z1{F|wm#Opn#;^t?wrvl}iX zJ%e&a8L0hHUJaa8=YujMv`dL&Vp<+rV8)XrD#ZOKo&7S?{3q$9e69ba+%I#1|D@P| zQsqA>@t>UHKarNl^nQ&0MBtexj(rj@YRMPIY_@yy(ArNEZ>gf`B*&aU;zb|-BWLR$ zv|n4lLdQ5dlI370#vj-i3WBZf>#RTTG)2NM|Cp2`uM3}J63eM%_j|!}vmOS{KTqDI z2BdRxK??2{NVrgh3hRPFLJob{F3W=Ll)b{Xhx~#7$yDFn#!NN(7KvFP>4o_qX$0R1 zRxTrgkR^R1W#j-5>w*x9I-MYuue7L~`E5$;SB9oTX6U)@qhhju;03i4h=(PXc-IsC zg!%*otz}uY8AwC5@tgB2=CnCeLdw8=%z893FgUJiEmufQ|G1%X=E5^rae2<{e;!E1 z_;|tyzkD_?uOzRsMgmn1QM& z{yR97JcQpV1V&H6z=wJi!OKj&$reByf-^b9(zo+4rIctXbJ_|^^ zgW@N}n(rIe(%m_l@OWVLz}6GHMpu~Q#o>}C7DXC2M7E8Y(wmd;f_ALoafNpT^H--f z3Lr>esYpQJ0w&69(ZC31BRMF0I=|cJbS~Y?bSD1`owa6e$&4bLqLZL&=5Zdf%YZl@ z%zr~cI;3e^)qiokvN#{JG-`*6&B>w{#akHURe$l+-&W7+)Nhj+cpP_?RTi;*ZReLi zGpAEDXJ%g2fiG)jmR%iaDs0)Smx!+>c135{l)bjWV^NOr80Ns0Enf%XV+^e!E;na42wrPDvj znt{zs|9?0Go#v%y;4iAp40sGO8AUc)LC%tfl{0Gt@v7FbQEU6Jo_?%9z61R6-9|m$ z$q*H*#?-OAdfX2+`CBV{d>$P4trTH*m>bbP`$v0n7rGn)!IYW~#zrIVfPm3(q+ra5 zEai-`ZhNmVvsjUD^#D^&U{!y=q~00 zJ`K(zQ-^bRYE}H2{K^4WWk`BzKS&xY4y<^nCFucdC(wH$$frN$hp+(~lAaBnSC4xg zf*xe-kFg7fgLUCsE?~9ecEK>gI<$~H68>H>r{+w~0SAhWk`z=4Vc5nnmidy)hgn2? z0&V|?Tz}+K=}Gt!aK-g~BCZF9M2WhJoX!x*#E^oP%@FSj@xvclf20FU(i&aEWjs&( zfrf}xNvz$LDRfAtP`xsUF=b+(g_eDKG+p9hqLW%rEdl#FFT`)Q6t^sg;sy!Y|B&LY zc``lNJTut+p*Soa9n8Jt{tgE~P+U{Cj02&3C!*68d5Md$g=f><)_v}BO?%tr{@~;F zBla{yG9w`_NCx$D7S;6ga3xW|(hHPn5w&#s=q#c*l-mCxQ4IQX8c*wKK8q;+&B`nT zPp84pvR39b&GEEJNE#_5Ex@bz{{cyVg1$tax6q8m*CzKF*a9R2%CJ_jV#d8SrwtS} zEPqZ-Nj{$~@art#I|rDhiGy{1ChyG?o;-j=UlVmX$aTz8F|+11N!s7>+{F{ z{5t#d>9>{L$Ntp&-z?*g^?$REeYLH=f6XF#$@}xa>+{F{{5t#d>9?h_ejm-h?@v4b zlV!5u_@8Wdf7(*t&w%^ay+8lEKKJL(ueU#+*=_sf{b&B~|I5$+zx#d@UzzW`KmWTv z_vg>A_hX-aTPoMQj~}c1KlMJl-)uPkr`rEd+7T=xzDcES@&5ep`rMyCzuu31`faJ4 zzWbm4cm4j~{`324{`tS%@7~%M48Fd~Q%&N(5^IUj((MfC;HC_!j8EV!@l{3%Q`zI& zK>l*0MEi!b#EYK#zeL#zVtXlCpoN+uw;VhkKLF?L-olv<`Fn ze_=q67oAE&96eQAY38^Q$Ldd2zbxy1c%#0QRSRo|PN*yR8jNm_Z0i+15GRxTgbAwHM80zZ{~haXMTBpb*gb-uh4gu&iobln)h@XWKP107bGre zJEpfkA#|x=3v#NpsBa9fF$M`^4>P6a^;EUcDS?GZv6vZD=QX~e+AJ)o&0npaWxgBO zGdw8yYL0$b_2nVoE7Yn={y~4IT3vtY>nI)2T~m2g>+M+#%<~7yfK{elK377W<9dsYVW} zPOZp1n!+Qr*awn@drHgY`8_F*!;8H87ciETd>YOC%1mhp$G8vR*JU;54O?cB<7)#x zU2gs3?TvCex(&6sGv^d4Aa`=PQoQKfheVPyElJ|$qE{r;k-=Uk>asQbN*?9YKcai( zoPz?X*MP;8*(fweb{CX8_bz8}?E`f?y4oPit>ty8{P1f$tjG_XtkksN1qKP#*(el0na_b>5gC1s?=+w4?)s}hqxtc(wRh|Ft6Q`)L*c^*JD z{a3OBk13z~cqQIxNZf(Q@*m3QlzHQ8kfSb0PZnXp$Z?p+thcoPrwlJn{UWf23kQh| zocepscOo3Ny60{JQPHJ=$kIc=`t;v*=Zv(=Q($V@;oO1k+8D=`@vuG>9-NSkFIS%t zd^StvyvT|C1@udukXlwbXJ7%uJd@SBTv0{O#Jg2iW0U9*|+E(X|$smgF zjRTKx?zql=e`w(0&K;NPyEA`ldPy0YFk>wXUDAjIMEzstkaq+7q5+#g&GtIzE>7zbYWD}HJ#i5GqMYwaK8 z+VxzW!tIi^#uwQ-J_V@s!A&%Kq~C0aKII)6@8ee3wBe7aZFw5)c7Ks>_cvMXb_OHd zo&x{f6IQ zD4{GhNbs7$dRz==W&XJokFPk0l0BzmM-e z^9Q%%SDBeF7a}#+M+&^&e$_ctwR2tQ_563LduVzh-O{{tOJMnvjq(%ToV*Hxl71J{ zg4TDu&wOaleGAc5ijFed?87|MfG*86dtSQP3;kvjN2>+eo=w}(Y`J-Xhbw$I*7n$A zwDMX72h?svlrf4Bb8(gX;zRBC%unv;zwYk&S1Am-%&f>awOsvslK}e&J!RLu4sO(~ zsvc8rctz+|x?lM~?t)ed42H8`#%P1PE$)ZwjoV25*;K{d4F!4P5mtjx(f)mP3vH~? zoa?6IFB5aM&i#Dd@uFLyG|y|g&qlBGf2(x7=%fs6URDn^B~=;}y}u9=FSEiO;DMN+ zVO)_w0M41jSqjF~hj>dcwGZIvzomFR{+r_8T;dgYp3>-0e<03|J`;cBAJ2iJ>{H{U zYk%3*C!YT|n(~5A19fLY>Mzm&eZ1I(PxsIbW_WV2!t#KrFtz3GT4xFM zBLb_;;AmHqY^KcpaD#|^hh6{yQ>6B3IkAYkJY8Wn{dd$*1t6#ZM9-a21&O6R)c!6c zCwj3P(LXK$7B3n&w4TDB=LBg8HF@^77Sm*?Rb4+M+Iwk&F(jhd*p>H+&`r3FS0z~j>bVDD?R5P=Sf?QH~1<2+zqp>PJGc~wwdcRb!(2T0+Zals6XC*ZR ziSfkz_s%0;BQlFv^n*+W0PNngWcMB{!jEqE;)9N4NLSvkAsO83iuwZ#iauuC!Pu=< z>@w~+@uC5_1&0ndc@W*TA5b*jwuraUs^>YHRgc@9I>@y0C9rtsE9QnS+^N55FIue0 zPsOU9C#^k~@o+7gd%1nA9VE|vRJoe)t^`@oN3B!2n^ZoVDYsh814y^JbsyP=A%T`P z(NjbX@uQ~QC-%F^1VN?RC!T^57=p2g9mP1O^EzvsLwNx$h(;-8CRnA;Z&r#Il+r?> zREZ|Dlv2Gjr9`hL<5YMe$)}kaa~h(s*-E+3tEiw^VO}(b56dj#4H5*%bbD*}ywFVM zhD3&))lnzvHr<;GG%s{Jr0+-*)dn$^rSg>q?SN z8U`9nj0OyOWctx%SJHjZwCN4dX{cs?60aDxs&Xd8tOLc4@rqg6`LjsOkm*!|u%VPl z{oJe@iz%n!4)B?XWXcIw_M&0RzUh}0qMU}uRQ7`0vN*|;A3mpC{i&HE27M|r$1fth zaX+?KBv#%%5FHBkw3enC465`+erb!oPs|#?vp8n1MRn?L(1Uoty7$(8LEF1dEGm-eT}_+bkHgP4OuR$U+duQ>+nYqi+c^z=A?3tiss~66fkR^( zx7~y0)X9meMfNC)-S2+0C8z2_X_=z#uO`19&muy-YKZR0lA4J^wG zH<;A2mMoJb6g9~2XNW`RB8D`7u182bZ48m*%@ir8h)@NUBc^LTkr&F8G;}|d_fXPi z#MunVYlpfL;VI{d*GOWRUJ!4{ox)tgh_OvG$8?oj()cC7c>bn$@s$H$_)^aoNG?YD zDmSCh5H&#UUVd#V*`pu2oD-8I5;hb#TqA6FL~z>&X2=an|ByJGf0#X>V9y0+vYS-L zCiD*Gzc#0kXo{gAwXy_N$YNUjERdfKGD*RE6W?~;muC5EXo)WoM6=@&bbno(`>uOw zZ_K^#+E;s|T6rQp1^iA>e|{2$<#?>1iG! zq5E_AQbXg^&SXo1mxmC`*d*k{$Cm$`TJH2-;x~LmmuN$4;+u(Jyqzy>m-wXTg{IOT zE~}&d=2p>I*?UayoL})id^sU>3#v&B&2Ss5W2d=)gm=Slhk8+rlRLWi+9!Pign)Ms z3>eZqe0;3xZv(34;p0lEV{Za+db=;0Le><|os4~d*)=aQLUP1=zyz$MH6+Q(mhd6k zjNQ&}D_(Os!72=)(|bsAq4s*g)iS+-DlgfNTG1uu5$}pz=PluT#sVguU-|sPnc5M3 z$F+$eiBq9A8YZr!nM|OtZa|!A2~jv9ot%|4Bm)ujO!!^VTbD|ptzvH)LSbrHS7-@4 z+~P=H&$`olkr%7q8=(t=O^Xa2 zA`d}9YEl0c;Y4(Oj}^(`TBLRT@MWcBM zq(*sZ<%Mv>aC^n62_*Y>;ZyNG?Ep-pd6i|ax<`Up0&?7=UN)+jF^IXayCnBnf0~pC zvg}R|Ru!J6(@C_+%8Aol@wH8E3x-Y(TKxEoi~83HpElYc)Xf|YKjTI}H}$jjtU^UA z&@fE#Oezzo#U$#EybbQmH#C7oef{3j*HfjlAq$|lvi!-!AJg~OzOGaFp8V7Jvxj;Z zU+X@HjN!Pxj(toc`3ZA$Fc0Y4=*z|=+F+6J)Sz5+lzu$wRcYTVtr|L~y``7X-cPgI zTg-z@#pgfYYkNh0d)jmAw=%mL2n|OGk{u*|r0}j@QEjv7^td*kwO$ZINBpZ4Drdv8koI`{c+O1wAPEDg}N z`EUApZ-)DC#`UKhv$q*Fo6ijWV}>Gea!u^C!1IZ7^43+qpg0-*CH?zk$;H0C zD*?r|@l%JO7-nZu;8V8)5R$m#i!JBFcmTcZ(Nl(!V`AyV)sbz5;eos&7H7B#xH~8l z)KQAh^s-4m(c2c@Thy|Cq)tn{op_J3vxV8y)8oB!w!wI80JmiJ*d5q1G{}u;ivQUp z+8)}^zo-LiL~^kE?DDZPGR3Cfv$59o9rT{7_%p`@JGSR3{vsv8VZ zU2R_bRP|s6DYb^GqRA>~G+d4>W0gFJ7M=ZN)1yK7!Aemn@)`npT>_bdppBmjK|?6m z7``;o9ucfIx=UeobarHEWaq`yhna*wERv6FU$T6d9M7 zSj)jix{#JDgk{q*dLtexY`xrRSVkdXC999nZ{iiJ2exu%A4v;zTmuqtPj5A4NZrof zPJK)JbYy9Z_+V2^B4uP}S?J)%&Xb)6l)QO$+xs}PQLm6LsIpWxN8I=8FsHURsdvl> z_~|rAC=KFNP9m=rvdx;%1v-9|=g$1$E#ebpf@?KpSY$gL%X`zAKcS$!Zn=GYZDjk& zq3a^s%R)CN$>E0#&sAt1qR=f!emEKVNBy;kz99g9o*YIPEa9?ME9(07g%cS*WJ} zC!yn`A6EAG%u*y{+5|x#Ie8ijNoSb+{Wcy{Io{{Y{Lp`_% zWy|9HaH}ng3&TH=%|5c|kCE-iPWdjlkjTS4c3?%cuA21em?d@FkD0zbvgkut&J@m| z9b{t)j%#x#0V1i7uAaMr*V%#t&;lm}`|Qxjj*~-2Ms}2ix*#ZX z?u&9~Dc>TCV3_7B4V^l&?ktYOekvTjYoC#X1|! zKahpe$&Ye6dP@6+N(10Ly4ZEBP;%ofSXwdYA@_MqQA#CZ#CE8}jMpwM;LU`@KwjR$KhJa`f;v_^b%-0l|g7 zR0_Q>o^%4m{Ehzh4Cg&0x0rLs70iHEyK?zAHWOm#&S^M7Q(Oxa(egY5|`ga(EmIAG4wAGVeqN>iLlI)v*goBr!|OKZiOnITia0t#GCpi}`rG zJB70P;m*8Bm4?zkMP{oAEnNs0mdm3i3Qb?$Fr1?~qSJ1g9$piFK|i647w0f|n_lb! z#Nk@*M6Myr%-N;tk^>$9EZhzNWCE4owY{wIx;#!U!vp7Dmv>#Bd+(j}7FpWc8)5Pg zDFMzlV@;M)-5o4&nkvTgVSzoSKTtPSjMW42?O5BR3w#ssdQx58zEsn3v46XeJOB3f z_P%&w_oyvHd$$O4Dehq;St>3|J$o3fpf?_qSa{E_R9T7>_L|JI$9Tp$b4=ol>vx+Q z<sMFG}SzLs4OI=H8WPY0{jXMber*a6C3THwsyMSmEe@(_fGIJh1CQEHxm4&4VkS=*#4tAZ^dK^Q)I_&~QMdYZ^;9vww*MxAD5;USlz;p&ZoWvz zfAV}v!$fyXX~xk_4`fh72~bKQi&6|e^ZkX`Jn*+Li>$;97AA4iADe~MF zi8#y1UqbGccX4G#&{Q1YZoOu=MwIB6ZfMeY02od?QWDznm96pUr&T`-mw6$-*Tvq8 z&3zet|4pHTYX{6MHo|1jDN?)P#bn2jc$T46YXs{?n3lrxlV~nAuUE~n zcNw_P^Trm$SKwp!DSjptN#;FEVm?#(Fy%>3u-LsTU7jn-zjbG*{D^dUlK+mH!AXo(w**~ zX0})STA&Bcg}cgTkr8NDk#m;}AEefUgDWveI~e74K*ips#q0LW}pkdT8K3DZkrEh3FSl&#dAc0WhR*+4oO zcc{pgYVD|oA776}oGNFI(34#tfId5)*jbf3Qdv9sBP@`_pZI5A4Q>(PQ2hm}`wh+- z8QEUs#5-w)y-r<4eTkpB3H&|qtl@iiap42q^?HymA4%K-TXVJXjA3eQf(J{_o;{aW z78K?sqFSfCxWIccJp*Jx=s-pos*c)lMf_)M%x`r=+W`>-do}|*Ym(h0yl=_@IEK(@EV*hx6{_r?WG5*ON65sO+MY;EY zv>5!Zj_#tL5)DS@Kr(7e$7pIpAislbgDcr4^} zwa#{+CDDfQyJsD*G?lv7$=@ z%|Liqb?lPTYzW`4?!gvhu^CCuZMNzm-eCgMTN`ODZmP2nuhwhy+aRnYH74;o8;LnS zX6CE;{=pn8Uzs^V?lw!aHuj`QVDX|)Zq8jS6DSQvl$y;lfzB)%!o<7(P4^Gc>o=J?XD%%{+DLC55UwINTCyR{gskraG@w zy~eBl6~`E=>#0-U*Q*YRZc&OaOs?&iUEYgmt;YySzdP5)ZYbq8KrXa?Of|UL4FpB-J7cLvgbW9#cxh$X;TNNc&PkRhyWL-bJl1}zP(dCa9~|GKfe5xKo3?%RugWPMK| zg>jnxb%iW_nzeAKpbhx+-)~>^f9PK5-(R6&(Z9LAR>n?w6l8CJS^7A$A8`7TX;A8N zHX5^}yB^J$yu}piVEM<7t>?@9@f2F-V+Uk@Xhm1oU^~>%$~B_PyanBqS9QYMn>Wa|ECsk9iSVtrnh=WuzF4YDYnynoHrw1;1c#q+9g<82ACZe z6D}%V#3ifmCElb-g>`68bY{x|$auB1eJcCJMh2+RK+fvXL8W;~^4~Uwd-Cfo{W@H~ zI`iv${c59<2lFe=ud??zn{8!zB+ug}DzdOCqvVWv()cPDZI^#~r^ZhVdx5Eu@Z8Tzug$heqyy)?3 z4WINJ)wvTjNV@X{U=9_Js>~CHTW%@mu*lT~#+R&|K`(GGQ?ia|g=QdpxlL{w3y#Kq z)yN%Nqp3`7t~`iUQ@on#!PD1H5&k#PCGJ>)#93CZ@SeEBBIZb7;aRFA$xbaQrS+!r z&cH56pjbAKSg<_EMUi}Vme&u0)P=oy&@WlYcF|E=hL7|5h>mjcqUGZ?!bDiV=x~Z{ z&5Y|HGp-YZu@bWGqgYC|%vC127tH`li!FAt(JXQj;JBZTX1RDXTVJ7v@uIP|mpQ`!Jt&GQp)XjEjV}R573(cIE-GM!V-GLPP(VZk;mY&m{(y|5n z0)o6r6W!{-7E+?PACi)b8=3ZC64b+RE)Ax1T)|a(FCUY@t*bM#kPzL8tTmgG9())+nQTrvQWx-VO>`$aB=>C_X~#$klHk+(zuKGN zw`ZJOheO+F*vsBa9z=gi{T}@RSS77YuZ>+@Y(~WB4yRK)utChV9a}m*`XAA@p!jD< zjUhDe+cfCE)kbG9p!LZ=6Y5F>Z;bPlYkGM!!)VlleZarRAv9|~(&u+J$A0L}Ivep+ z=PfYmjR!tMPKDcaYBkPi--grJ)}*(DqfWeM7FhdBH!{013h9r#3@0f ziruRgc0USXy${@;%ul-`+luji(6$wC+lYxGtRfr=G(J6?#=y@rA07nyPd6S^kfHd2_#%IJ6m4aq6Un9+)I2#x#6AjMzv5*8)d#U66cK0?4$l-P%jp8ki2iJwx`h08s`i7WJ zW-M@DepHx4vXjU6i~Iok$9XV;kkS$}TTZ>n>#a_t5Ef6sTL(E5R^y-sx1*SJIO-}U zev^BvZIR8VQIqY|Po!3?sw7%5k}J8#O2E&q?`gqK@#>=wv-KIWS6|s9EFFz%bd~Dd z4EQZDhD$V(46vU!J{TJ|f$kozj;?qA^sp5{6`_ZPrA2s*06{)Awjwvk;)QgAP-M4L zEk&yNg~c?TCQ0s_U0_U*9$8|7EV)T=>`9hpH;(s-b;Wb_l0T?z@@@*c$nw$T3$KnH z06v5G&sZWf5@lyYh_dkd(xl`_bJXE%n)sI_M_`_M8bFRbML+`J_Y^l;WY!FqA=R>O zIQnD&y!l3&W6dNlrwvWdLB@*NHEqS*L)iRs;x185*pZ=PkUR72-6opv7zn7vrQE@_ zPyVE>Ug4%jmY~mmIcKeDOnxdv-hw`pOF_y|W)!3}pclo5d~0>2X*4%)jb@9ufY(W) zXDUmBOe%CC+`UXOlqVdqSSVZER;G6NUC?ZrMN7EF{qFZ_GQ13b^B|sviC~+;UXotV62@ml%22A3L9vZ8`E@P+$2Tp$VbEpmT1r& zWS*CAK3d2Ihbd0Tk`Hg1b4j*1%2nF?DT*3c#fLa%fANqgjOA^x+w9QRR1q|T)Xjij zaUqR+=5NiyxkvdJ7~y-6M(AKX(m08stU?(xd2c6{$0um%6FKLeS+iTZqNa>%OIo4t-T-s~L=$!2Sd)D1xX1R&Ai7#`INrS|TNSOYooRc)*n-V#Rh z3=@|Qh)mmP0>hs%unC3%*NXx`Tqhlzr?(eI!)+PFGc?9PC$Q6qXZa;A%9OarmMAeL zK!Vpszf6f6ZHe)w1RQcpXsmiU)Rq`YFT-q!(a9>kZNroPdZOSP!>7QAY$>HYG*7g% zm?sDOPYm`|CeJ>J7j3=Jpg$L9Qc0Dk)5us+ljSm6`@{9z4R(X^^n)Bu8ZkgwUUfxV zIfl07x-MDw#;M?VT|+m#Mc7qBB;tWTsX@P3%MFcYi;RAl;mK)MH_Et!)Q<{ z=Vs?g@_qR6W%x(ysQ$v}Y%znYFbw0j;U#&96cRD^u#`0uGg#IfL@I{YdKYTls;f&7 zdR8lXE0w-~`!vGr5#Y6Shtoz9cC^%&@xN5|{jc3ch%0He@=TPE z+Nu8=zenW#?JM;dx}L{|tT_ML z$NXvxjpVVRzfo?beXLDCJTy4~l-+TGF>G;Z2|W>5a?~w6U_a_hrD87}n$1MJ*XIVf zri!ruAU@%4fy4VdQ;wtR{+)McqqfYPcRTIkPIwNQ-*iZycRxEnecqk6YbT8SfA+k4 zfY*P{dG}j(41sZ728_!xV0^dpN5IHBU+J`@GxT!&m3HRudEU)Fe?hxQ;&2XGX=bb!vc}~&FpM-3ka0NyhMXeeS~MC8so+LIVVAX z2fPtZt*2kRYwgowk>LgG$wqnkuKNL?(ZW5;XkqFgWUqe5TLF>dQ#)Tgtc`9qDpyvy zRXcATM;Auzyutl5H51wbtTeA>@}pANHP<2x5PRt)&z2Onjms3ZZw9h%rV5Sp_%#q) zetm`i#H=^*>*4lEyl5z2%yzlgHITEPiT_!B+*xLy9OO4&;5VP{@SF2I5YioPuutMe z>-a*4W`oE!$lLuP&0h!Iy;uIa*Zw1ZZ|VMT@t>sozs&Fd{Qc=4etoy$*Q3$$XX{5Z z`ahbES4dxxAsok5$=`tvAAKF`0fEd zntyX{^Lf3~f07>NRQn`ebTeNv^J+ABzPz)C{w!O6_m6M0`Pr`=tG_#j!~syhGCv$j z!=C=`&s%?lk>tul`_+HnocpoVfe*MZxZGTB?77#eaC8(Q#=B}`Gp34*$VQDa()}6I0h$E0 zN@BqP1(V0{ygK%Gc!>;3y<_5QFs`bph7EO6Tk)br!=b9)6oG|BZ6-d6`t>P+EYnW~ zN`(qcQoItq8kLc3B^#eGQPM;lmGze@vm2ZCjK?` zGILC&4o>1kE{wy&cY0}e_iMEO(TdIr)*TKrV~@L}bgc0(PKL3Ac2_T)11EKq=y-ZC zwm<_@kH@-yD>ia0N6ASYlkK&~{(LM5=mBwGqM`p}i3mcjnFl!pq?D*BV8>}masBvrMsI{F%kJ%Z6UgX~P`fm?I(W60;@ z-N9^G<@F3r8GcpB4-Uzgv6Sv6nKwQ$+xrfaDC!35W$P zXW%`K(T|O@C!j&@z(KOpRBeP6awOhUKdTX?`C+f$$zC{RU64=VQ~bJM%_KF)h)%C6vQ*0$kGw0%eaMG*JXT{Q0H?sNANJHE(FI!-~8 zwa?rfnO2-1z7~X$HsOpp6m;*W6vQyf92w$$Zq>8fb7~GuqMDt^zQ2WRqp`CgnU$dq zb^Wdlm()$ozcSpqZfc7>6hW$6|y-pb6Okl(ydns)3z0iw@8bOikAT(w4-55Fz@?fwys*pfot~R6Sa_gTg zk9)--+pZ2*|0#M83JtpWEKIHyHY)-y=xEV*offsB};e z(JFY~n8bnKnuJ5<44?wGyAS*sQ4=tpY!#PX2ebCS? z+IBSYEjjhMemORj8jYb7RD(^8vpXRMhax!WDc1WS^N%%mebA{|WaSOgZ2Flt$D}!i zW$R?9GN1t+=(A_OL-mrHg z&F*$T#-my|I|KwOD(B}IV~i#V&u2PR_E=ljoP8F%s~I_G@*og(IJ(Unwhwn=zbpNGlP!jDK(hGGv-Ac5*;U{T%?vZLw^kI}%q`k*Ji5LBp zFBw6{Omo_o%@`2V_7+PfZq=Xc0PvT@*>GyQk2Ch@;{?BtV{IRnhSGQ3sl9Ca*6%dG zo)y_EzZUo{$0VNNfVM~cz1SEm+7+^~+i{EhZRMi*`gJ@}>N19N@XU5;W_p4 zc@YOkd@)tGFpsgphUFd1?Kq%pWw<0+S++8v!wzyj%Z&^lfzg`AlURU+Op0;9qu@L^ zo02;O=hS!I>mdtd3~UHuU=RdL?wrv^s0p2c%LEOiLOdIclyNo!3+|2Yx~GYjJkdv9 z$tz;RhesCwP}XR)(UBebQ(8n<_`XV-p!ED`tj+j?(}4e3&e+M5Yzhrh}$6fZh*s84B)N%^L<@qhdBNlAKGCcZdGxRhb1!2#zNhw=@-G1<+|gt@y< zvyMbPneiL?$-zOG`FlxY6`~=V(-8tp9os$+yQmVpFLoIuAlrAc&+ULFIjuMEDAvQ zI4#*VVb60mn=MwbDv>p;NnMPROqL(%P1ZQ!kXwe z?gtDWAYHZ49w6gY86Z(W3qB@*Y~}ub5k|Wlz7Juv?7iyf>V=(hCDOU)DeVs^@g+;-HesL}6v{*julw7yT#cS;K;VlZxI%H)Ax#HVnJ_c^5~SUHT=<5;V9}GMtx#Br}RE{%PpldTZy7XA~pSa`P@r zG*_S6;jCpiqv)OC!0|UQoIgQ0l3O-_k=lGbokmecRZ;lu5yaPbVU z#r_xlK}B~fSSc^N^w+lZ7Y0+|^(t-j`;lFp!`D*F(>K;ezfgS>?9@0|K;1}J|BP(w z9QxF@(AoeO>TQb6RX-0N9d!_!0yw>77KGOeSF0A(K+<7+Lqcoef-sy&ZUpvnHv8u^kFDs3Z#-|43Kl<={;rJMyk zu6Av8{_AI`+H_X-24binpZCrctu3G_q!_i%8*ZU?mzd|FzJ{za^FfA{G<`ixs~%kG z=I%1pL|<2}C5RVroCZt<1PBwk+Bw|GBd(9#acPiV6>)8Xz09%%R&AQ#f;XzAi=Rqy zY*9Hh)|rCQMOccErGAm7Um_(8{eSG{rQy?3p*F+)VuXepUQeyWW$ZTW%iie2MOIG8 zH8f%e`!XB9nkG|DTU@1`wn)?NNUB&@j~0~)f#cdN2$Bl!BZX|7D9Z+)-+7;HkZAP# zR8Cc1J9F0qhyKWdo&Ma+canpdKfTM|PqX#?_)!#XTb9u;G*!(3guJVR&P|K;&udpZ zH>|k2+PSGwzuw^2>Z_fv8?SaI^EsJ&Fm7JtEMNSAvwYPYXXFZJ_#*gDLpSy@qtQ3V zbCIF~L*o;GLx=2+|1jGDIh`RTB|L-KAA}F*Bi9<(zL% z_y_#m?k8ibqm5FRX`2sqF-K_PDGm1_UTTSZGq)K?6~x;jL?OlERhF8aoP#&#?VQ4c zy~0s4r%=PWe^C;V*Z^cwLSy}3$(}a1xE5-Io6rKLq`;} zkX*gDDA{=*^wc}hR8>x=(pK(1-3LrUbFA%$ zi!+ebUriJ4?E%6ov+$F{c9A$Bs-nkDEf`_dYAu@O`sa1Cj`*@jzt-%}Itq0~9*(uW zC<}&$f3ae~&gd-c1fwte%CU2O#Ws-w2FdNQXz zp%YyeJbQvdmMzM_ascOT_sPGQu{!nh^f0II5qtK%yPwy$Q(s4Ug8j9^!m5e}eC~9R zuSIyJd7!m5ci-zNbc)tesa9VWy=0f)%X)&yneuBIv&gP3ow=^aT<6ukZLtj3>F< zxAH)o_9}~(29aap4}hkJ6Y%Hi~Po!mhh*3Js0B zvcQ|!{+V7n9JjAta-1oTWp!z=Yy(?QHQT@nvbaTNVve|?mT(SPhAx~dXA~_VcO0Q- z2!cavV}p1T-sJT+x;A#cW}}7aQ%4@{L|ZZlW?XXMJWnxf?$Z^vPfmr=FKlMYZ1)xB zA1Y7j?_T6R|8pQne4O9<)3a>;qWwsU|8R>`OY~Bi^RSKu>`xt~I)q*PGs7-&c*U!h zSA=?7VU{h7$q!;yoPqA-ONLP$yA*MYRkD#E=|aho=Cq!;Tl~ZKrDWiW2lOZl4Lt*g zNsK$N)Wq)vM!8Q9w&TypC&Ado9ZmDMQ!_GvFY`K=)0k;15{L{eZQ;}_2vOo#i6L(JM$)CzaJi-OVsW3V zrfT*>OKq{()b0kt*o5xxF#F~SJ*eTzbhGHNVBkjOG+e4C_J*F?Xu>ebQ3gZd))qt8 zF&zG4_BzAMP#Z`ay zw$Y22fNZvG;%hBiZWLl}m~7edeTxt-WP({0o(^jm%bFP*?7x+GK;-h{fYwCKaPYcR zv5_H`&5#nvGb=W-(rC;+EZT?0%txaTt0k5WkhIp^k>!@MJl^ZnzaZks0(42ZIiPsD zVJA}l^*sQdJUIjKGy`yWUjU!9FMt(3zjs(4A7o)ob~X;<&{Yo;G>Ao>69_?r7^jjy z5qR*Xi+da1)ToT6JCi2vL45Yv!+@LkSogpxgzeS}A$+9sZk*zb!YrGq`Cug< zOR9=r#tL9wRr89Fj6bMKEMU~IEo)+9I~v89XYw#+3>_J3>w?ky?UC?%GcU&W>zP+L z4s!BBkhD_mw~tw=f$Z8iUQ^que}gg8fGo4t!YZR%9cV2vCXs}D)+7kQ7E(3OGCS1Q z`4Wz0>v3evsdw-m^%N1a)qUcxI%y=96x#R|b7EW;I;v*CbtRZm;zbYk)QRzHY>1go z*+HasCB>4{pe!>&w6Itc6S~$Y-|t`}LUczuuQ3rJHPJ=xJ$Un{TLD*quV&bmDn0O> z`iCvvRGaGt&5v8?`m7Su(#P(weYFv~R;Y-L7%N5pakMv;CUDG*O_Wu)as&JXqst|C z2!wRs0VIPc+uQT7i99P;IZ764MI-+(5?L($Mef-VgZJFRtcNK2D5zD zEyU^3-#Bu1>FEQF0fK!xak<))xci~IPR(@mek900mpst>(S{%LIW))(#{Mp13ZG#+ zH0Q(oCA8gWc1wsA%>@3(Rq-UiXN@teo``yaU~ zkoCU|9f5_oSg3jK7PC`rO4g{AM4jdWLS}2qfO$TcKMEi5JrIHN1P5y*mh(J;<%iR$ zE+L7wfM6u~z{UcB!EaSvLPCHv;_tep@3RV|N2zowKN3}#B3hNTb@Aap$b{HI2}b;@@$enqwD%6aE=YHP7o4T&Jvw;2 ze-2Wa*7_c7z81d?51V}$=M0AP)ytF&o9{Q>YtjGtDNBtE8QI?2<{*k}Zy73A4zGq^f;!`=@Rj?D+y%wT6dM(pdI@>x zZq?g(hbknyviCTLVVzo-;%$PsWvYGcuD^kis?NhaHEeIj0+O-zGq<1;08F89V_K9A zK1&+GJWA$ewB!UxoF%s`@F6u4vf?6h#lTI7fyrLYD;x&E`=~H8EjPL&`!ZCflre?^m2(D2hn60g zfOTdQil5T`%}pr#mrrWXb%ryz8+MsqPIeO}MXDB;%`r&062eR3Bca9DxE}5=HK~i- zE3Pw3&1uNiW-9NFUA3mnnSPmNw8NZL0C0NFv;vbCNv~Ajz-!}#=QRAx^k^ONBnO3! z`Y%6xl)M^icb02q_MOL!R+8pCW-*=ND zXfNW`lLI7PRpYfi_^`Bi)jdlcXUHRSo@q|+lCS1GotCe32A6=9SFCZoTr0PYw~h8D z)LO^NPKnPBkn#qce83k^$Xdf!1Y2`HxoU11WD{Em6z+3baLT3 zr`{w4o1~q3Je-w8cm~9t+pu2O@^ce6#7uH$%QAh)>nX5G%QjB;Mg? zUvyy|;gg9D?<2RBCe_rVCsB-)5NY}2Pv(cwZ4f=c4^9I3Wp=1FZ8XUdl?<0!ow^-O za<9`co^PJ)t4Q`#qDl|w98WXL<&TjklWjqHAwP2K&=9>x$NEMUUkkw9c#gY#(f-1Z{81Gq+P;{wl6u3XtkWfUX3~IJw?5}`sy}s ztTwvB9`kNI8L(h;iI)(!J&|uEU?t1FAMbz64_&R;OojAmUsOnUsuCxqDeX}q&B(Oy z_D2#Lg%PJg&Uo45JAv`_HRJnK)SxjILY;;q=ju6=)q5M`Y5N}Io(-nQ*??itle_8y zkr~Kc=Kgwec^MgW-8C9WxDH8YiUsyE)Pkc#hypTnJh6@6?-DWYHnI2y+S=$lMz}i< zd3_`zuryL!{uuAt4iKA_Twfb~!;J1+baJ*Z0n%Uh&VmZcF|yn5Kv+%mj}o+NV^@uZ z5ceX!^|{hp4&oPML}K1@&FmMFciO3#ikwanG;C1W1v)=k-n3jj#gBT3fd!iy)FBTH zm3%PcS7pi1rKQpMnJ8o zjhO+3Kg&X2h$!=;zTBxrnx?sZil@oG-l+K!KdL2Q?xWToAg=CdK@?LF_JsHPIYzQI z6#NLYg*JIA&XVQy+;Q(c?|o&wb&`g|erV51b!M+XB@(M0&b~0>pA5p$y>{!}B$fm`DXNb7 zCz8QUk6Fop4A$X;mBYN5v|Z2@>*L(piDTDaM|=YO&x`op)%(-4eahQSRnKdc$l5ka zvPN`gz+6Noe|_if;hd?-q=}pVmri{JKdNI@lx255jUTb$WbWEg=*;fH4?+i|;|PvB ziD=s%2>u-sfrh>1?K7e1bVy-LyZ~9+*^;YK7d^8r-ARF8kCR)o zPTaS8n3}zTy)hWCeICldqttyGxMJrGj}05yf7OhZq%awsys=^B%$~PzPpy1#fHV6Q zjVQXbGJb7-;EgXAnx=>Uc+a z_t1v%4B=2=62NC=f4{mXrznj21KeG$cBi<&hqV@~ppjuT(xy7tRJo#r>W~WxFT zJz_OV-+fX^@29yx60ds!k{^o0#He-gu0F*r~xVRSG`N+iK&`j}1+pZSX<( zBr!y_v2ydJ7wT_L4K@hKdhrt*?H)Wv?(Muuph{CpAR+k#dhR~K zYf;up&x@iBFA;b3B}h{^AK|mpUgg>~)`)X*`cl&Ng+%ft0OnwXblNcNb1J_w4oRJ0;&{`9l1>v&wwc!V;jjV=5lq}7R zWf!yg=IJ9sGi?ltVGg|>QsnX0hQ?Xr*(1Y;^4*O+XhJfGYZ^&D+JBguQ}&9B9s9-w z4J}Obcss%4O0{KgMbk_O)40?M<>w*LB^W=XJQzFm4TI>yH&C{Oj?wixVFKPN-|zxv z+{wQ+fa1R}S`IP6aah{mWliuh03=sz!<+d2Y`OwTq+kFoYGZ!{40zgi?TwV!2X(S?+q;aX>Gu9%iiSSn0X`~|OWeoI0|wwU^f%R& zgyw8~sldo44t)#LM+;wgKw=mJJzBva@vh(kT3~5-2UyOhJy|w1Hl18D=P`x6%HA1f z#KFj|+^hbj&IO6ZMyqdHs89=sWVN7W=hKs4UtyhIeT^a*yQRe4c!4lrMlnf34R-&% zZI)xc-3Kek40k6zW^I{=9iz3hDRByq>1{m2{M~whyxDX}EWGJ$90WmttoE?Li(oO8 z>~i&7q=t^}P|Dd;h4!d6yppWgTj%OAk_MUOaEW`2X)W|AKcEz7;1imaR2YQqhUX&? zm@iLNHEdZxA9X4(Z_O<9*+zYWS~gv40i@4iw357Y9~E|JdYV z_|1RKWh8MPpip}`Vz1j7@D1#bz#4R~Jf}UbUzwfaC#Tj9=CoIMM=*bNYU93$?a{CH zO>F8ly)7qWz8%4-*VrQcU{v$?0vDp7y9BPoR}s)GajJa~_oqe)ycbE(5Vx6P9T<~m zk=_u-S%1pEY3s@#Z>!lk@vEc$Ki1v_KC0^4<4+)wfWQeaK}8K3BvesQP{KnqlE6d~ zK@qX7C@mGKPlOo(5lEcm%5fa^wpz8V);_e>TBWrjqDTTr0zL?6eNlP%z%!2WP#a## z{J+1w&rBwO-uu7*Kc7#SbN1Pfwbx#I?X}ikd+nA1e-Ev@66dSA71?(C+o^pX6i1ee z?b5d=d4Z;TLEKJ_RG#1qDba!JUZN!f&4(HlnUmL6Y+^jp+{Vzy&XTi&k%I-ZH?gy{ zdsb%z3P0tR9SKg6H-}e#7jh^>Th?x_i2m(kwzNHm~04?gX4GnCG+U&4TZ z)Y`f-XWTa&^I>q}pAeuMb3rd-E_jz0yLk9^wZv$c$dMH+*y)2{+n+ao?kzNaO1lNV zt%-#Nc7Y8dqDmw4i3RXET0XL8Srxxx48F{rIwfE?9p+aB)29Ts)%ZS3Wl$9hTQF*D zggT{`$WaIY7M@T!mm6u(>ZwJGP*V$VwA$|d*Ck>s+!M-?XZ(_Q>}?g)Mif-8Pe3bG zwySKmXVE$nZ39cPQ@aOTKy@`|^VOwT?vow0l~h*KY|K{Hfaug5Z<5h<9}lO5p1srAbo7IkWJHN%PM;M5*~z*C!IsEpAodoAS~x)bWqUw8<-{2=eq zu^1|d&MWIX7mvR&bE?@0UZ|YWvCN^hp^MFO7`o7$XWZ;X3e>kbKQmJd{mRseG9#(# z?}=#}#k}xF~Wk zJG_cvUadQEIh;?$4P$6h@Qf=GJjcT#U+z!@rEvy(JqK^Qy62?K{OyAjitWL3a!UN(bQ-#scc6b-ZjEFSdRNaAePB?kDnT$@&ewe+HnMW09#Wmvm zu6xn-HSs=WglOmq8ZxTE!@O6-xPOOB##lCxS#!hZi@jTBw^dk*%n|$jmmXyc+3syT z(JbDJWOz1tn!^Yc82bK;RuZT(pL$z+7~kp|fva!f8^6omamxwVJ0SMqUdMgkCKID|s|{Sl z0D=**9nI6WspAKUWtsN;VD!=r(CdU=Ze2yRupWA?r+s#WKMwsMB~$^EX8F{KjLyad z-C`Fg=nvDR2rrxP%Y!y1R=`!&XMi>X`2a>!DP*IpmwPQ09&V#q*&CR{rbld69%{k) z6JO*1V(5TdxEKs=0ROk4qxe5NT&`!k43;@%+lgRNAh5AiF;|ex?xbY=SNtcO?A`Ni zGVb~>30{26cw6pileMN+LkW5P%?{~KbFU~~Zz6Le6nT5$NNZ{r^KN&_AJQljjvOt4 zVoG1OsGFGOPOq_3H`4k}qp?IECb8GMyT&jyF~uJ$qYGJhm}_#uSBYyKE?r}6st-=0 z4kz|C-N2tIPuE#>ncY-AHHVG$t;wyaCTMsvOgjEY@@1rrQkqO+IFRRi)5ZyL?6_MK zLw8Bdny5#675b9BI*?qwR4|wJ-xY|g?oe91)w$~~AWD^BSKFM93-w90;eFAuTV{h# zzm0(W9%=Zgv4U}!Dmum>Bil0YIuOpab1NA0wO}9 z9f#RN1F_LbE)RSuSNJ`Hn^Iy!E<}+@boO)b{zPY(SZsNE0K5ySjI&tcRmS2~5xZX> z5r2R0*=;3EjnU`C)>_ninim;OISk{fHZq%HV z6y+1c*7fpd(rxlR{+g-*>9td;0=%nRZx8J1VyY-$2%c-HB7B;$@&z*} z1yR}ij4{_}*OSq5i~*nRcLnR)e2ZJog`qx-F~9Hq~BDO$U~ z=o8T{=r)xq=)r2Z7BL>$rYUSXL{{f#oOf?^ez7%uCq>_<0g(wN zebC7tJ>KbGRu^> zsf?b7gEAN$<73pcTkc*tG|(_Qh0-kyxdL41yqb;v1vYOQTE^8SWqm$%ipmrW5)L^p z-Jvg@0pPsv?$G?_`0fo=htnOhM&DMXlwp4a6GufB4$f*FUva$Mgkg>Fmen9|Rj+y! z)ELQJp+8#mmH1ftkl|lq84>tR-(O!x!St$hDK{&z1AD-aqaUi^28f{zqYOf1`Ge1J zi3B5apK0!WuVXp;MzMJ%`Zy)^@p(vEM7T4^wh)Ky7>aP8#5j8xZ>*|R5x-hLLueq% zd%F4|e)WH&2GY|tPxfoRpY%Yqop^L>`@MQrGlX2}F8T2^6g~#mXuOkW&s;i1+7Y`u z({WJ&x;jO^n>AFo6N{XBy(!WPcZ$p_$qu&zOn|`-xnN2n2YdJV+UO!OfX&aX?Bm(x z#8rQbw{@c__it0~ZNFUNb!CxLbnU%{SudC&G%X<1cL}ZpP2M_=CyT)tn-WJh#3@H$ zBY}Z%*8s~e1g2{U4th-`&<%hQqOA-}9rE=b{@0VR4Ire~g>J?U)mb;PwbXDod00^r zKcwGUY4f+rz|`tecC$nxs#%NZ>ugg)Oa;bG(5x{}gA1#6$lW2WSj2zJn;eT`xpNQn zBYl384uO9hx=>41kfrJu28rpT#{0IUnm!0xjBI?#cn&`4pci3J8Sa%Nm^rP(ecWs4NB$6zh4`k|ZsaLNNp_3P5AszbJXAzy zmq6PozgXia=hWC;Q|mR|&FK1@z1)#z^H-xchw(9^*PD?+9AlO6Gi@D_&NLylv6u>j z`j_?JVG6`glHxi+cPvzI@W>Kqje$;kvCD%nG=SAOrnkG&{NJ#C7={h8)0u3o*T+=n zjeTRh#8aZ%FN99l;yW7+K-W-l6;;@C>$L+!@5JSe-30Tu_j8m-pI<}< zR+c6fD(}20nla8I8T!#eL({DkSfU~By%~+`165&8!Pgq$Cf1|*17A2~>6Guk>2Zr_ z7q6!TmZj9bKe_}h-Qu)mVsiiWv(xn*Nu_b+0DXOHz`;r+j+i(sK(;4CE|3<-g_b`yT zEd#lhO_db)8F!SS+JcnqlHT|Seckq?xJ+W}Av3&@EA0|QY&p-n@>2$@9*R!WyRd)A zddl9pxjK&vUJ!T<7_mLjNP zBau5!v~6*t6MK^<>(dcEH~B{7AXm4(tVX7?O>N_PP96MwF#BL~yQ~Y$Wfo5qD(@a6 z0xu*Yv{KNny!oWWZ>KDq=^tr)ne*$_pYP3+{Rz^cO2GXVW-ODN0FK>Jf* zfAZ~5mi^g9PEcabHoCPQZx!xyi}_aRl>P^Qf+84OiSPOnbYqPwWlr)DdNW_cU-K)C zWb|^_AR3sfm)LVl$>#080ZtM%MVR5Co3VK*9numXX)7Vo#gSyWNrWsFqHpBaVhU8{ z7Yq$2rqUvniaso;Oe4>k7Do*XVk;)i?6W8hUNV%l2T6-BHhB!_T5Oz})l9K8%(zRm zz>MWD4f9CBtnBklj!b|rk<@w^z;}IR0QP|t+y+jkW|*nK?>!eK>0)-gyah0zF;tww z@Y4ja(wzK2FNSM8ugH8zM?K2lGTwS9hikomaF7m;1$1`AYZRLR^l3A4vue;LurIplu- z6MBo*THGT+#hzTtla|b&PQ%ug=}_n$SHjcvStZij-1! z`Jebn3^dDY3qSD-_|7?JS-zNlWMOoRn!$unExxlQxGGh{Nvc7oo9z^t z0VQ4I!L8~%O;-;3L06O(}!f8x4etP(P; zTpD~~AkFKKx5Wv}^r%+%+guaLHIZBsmj;)$)?nV0;~2)F|iNFhCuaDqg`w?%JQ%$m72U@R_(c9X8n8qe)0 zm*F%a=h?snZ*@Cndt~*L+Pb-?1)8yevKEDkD5GN)Ml&u-6xDEV!x!i<~yFEv-saO@#DT1*|sH8zrt)} zM)n=&-2N=7kpsi*eNK@BS2;DmCuN#*z?IOVG{dFaIdrI=&Dal)3n%Cge{22g^iZppu z^#L*Ig%`!5C6KY-tDZBrrTR%1XTBAS7V$B%?*%X^}))cCab$0rs~F9$ftT z6NA-L9u}Vnbu{Dbj~ZW}vR`w}SM$xOEux0ROLjjkC9mS-Kc*l$uCTaKe*WW%5)1J2 zKhgU6-_l1M-KjN`Bid73HE#A`-LC>KGc~ga1mk{H8oQZ806Q`d0el(Ae#0CB*agSA z_OpIS;c(1_MWx91_1oJT$Nvyw9l2%LH6>02jysYzxFiB;O8*lcGI^9^V zoZ4?;Rh3kN-Ww_`LI>>sy6hTTmi6L;QNSzR-Nur0Sy~G$-wTbi?HAW~!tKis;W<6J zx+8Xv_X3A~)OyVzlh}X@cTrglzDF_*wH%r+e<|gzWjs#iH&(g_6M3APzmZe(YXVi@ z+VlUf*z^AZ6UonXD|Ae%FP)@a4?E5DMYlqgHKED&Dvge3gk(g^=)^wM*h#FUSw$p#-@##iQY;*)uf)^l1e<~ zR@+olxWg}T#vzGgmDpA0#cgk6d~o#i4p07w)42>wzhOYE+AaSy4JXI9_m^CdiWfN{ zSpE5fWU)sa*r|VRI(_hF?&yk&Kc|=TeC_qT_s!a+EF-f@v`(%{uEJ_ky;|k8CT7aR zrqx6CbL@Uqr9`mUke&?4GB!uEyllF+!eSfWd*ah@JXojPAEgyW5Q|vV@#u`MB zz{}t)86F>yB|`pK-lbHgr4SA^^<#6Uk~(!z2j|Jp`358v#am~c2ok)$LQ%PFe90^w#@HJdLUJsiJr};(*$JgpW&ELhv*yUBsa+G`QT6y!t)3#6IS` zV+Z=;N4kntr8pj4Me#TnN-Ml$3;!1Wy`Bdvh& zeK-2+r4#TKD-M{5v8B;|gEm=yQR;qKs@pLSdS}vU*u!z5)3aB(_0@z#NG>G)d!YZC zh3_W_Bbn%4y9rmOD?LNNm*S)1uG&MmxeV$}jlZ#wB0SthUOUY9Z>OTOG#R+m;mi?kwNayD7c~o)cL0zjrGjGC%a@Ix9W1Yp1F@{#SFAs zZ~k1}ZiqJ=l=@TJ3f%zX=RWdYc5J=*YmJmQ$#+Z(b@Il}qeO#s147^tt>=1g|C_|9 zRqS%%$VnEic~pwd<{f7X6abTW$%|Y4o`IorjMI6jqlIPn95}n~IB`eWi|X3aPQHpY zjFRm@%?>rSr0mt{>*QLGubuCcjk>!KbyrZ_$3^EUuz?M|RB)g8J#hcjmMAbS#&X+Q zxb3V@Uyo$thh8>8meyGAr&~b2K=hF>&rD2uX0IuYcrL<1)5^+P@hI*G-Z!&b2H=P6 z+~VQc96H&4+iJfVg?%v8V0fmt?k&)im+A|sQDvryG`=`{f2_or_kwqXs6rgSkEL;%J(Dp##E$Q`YE?9V}~L>n+*u!I#t& zj-R>{8(EmJZ+SOPz%!{qq6P+Lbe4~K5G%h$mRhL@=HM60$<*9uxn5gIcfUa%Zsu;P zh|S3k)^E*aef~4wDq@54$|Gyg)J_PpGGVwi@)+xfj7$?(dl&RFqEYF{uVK^So6D2y z$-cEb!da33x|2HD=G_%6emC^Gc-J<{8RF!6o=*eG%l4YxKt43a-!$zfh7r`DFr)|mH;@dw%JfenTM zL0f!%xXDr@#T9g5;{^8c62;mQgx-%rvfxwrJSJg$q0KLO8)ol4SuBwoT4T^YHM68Q z86-w!^|4B0BWHG0@k&X&?$u93Pk5V)%h}$#TVdFeuhO4KxFG$0?m zy%oh?=ww0lfh2wL`L}ooBr!?5M=AZ4_|M22s5*Z@_}77|h1pr*U+S1D2Tiv~YI%jh z>FUkk8OUydC=&NEk9ACMHF7Y9aiS7y^RVb z+eK_OXHpG^tQN;dYxPf~VJUZ@XP}|I8XNAfXRZ6+&Gdi9_w^s}{+InvT*E%>k@~+m z)&HZt89XOWQOAvt&OCne9lPU*hhq(GsdtZ&2_#+2#&=wrrD)^h6Lo3`w}9rvS;6{` z;F7r;xN+PmDs7w3G#|~FpO+QxhKHh=DI&rEJdFGm(W{>z_f*;u6U>LK$6RH}T3ZB{ zTAgiZ#uh=TTck0K7^FOWqS_D0sSnoja-|@LX?EU(K zuC4D|zy{2YjKV5-?{)AQNN?{AJw`Q~7-si;KX=%2w;Y>N_W0AKMn*g))Iq;VHI43^>d% zDvWh1`O?(QAz@>K&b&r7t>06hlX5rCt2gRg_)^vHD-Gt(T%3Bu5%BbU!&heCB~8>CfMte^#sfwaGb`o9+h~|w=)Cokh$EwNMjGDgx)W*mn-@rGCeo)J@w>Lk8b0UuGosc zQmEwi089REQ)DYACfkI^g3&82H2&>_Z0^T$SG@^D(rTb!zT5xqbB_Q@2CIU>H!cKiu$HCQhi~i&3dh@N7m^^+Y$u;AX(+pjVav%_kb1(PTPm1-|muTF?C%q zO6bK&dBNg!&IRi#rV?d`c->!mqu+-mW}jWre^y>f-fxj-XrY!Cj);FdAF+x*h|Q_R zhZkyqs-=@npkxcuLYVrkI(7tn!Bp)M-t#(PiqJss2Hmkx?b=YT)u&>ezS*Fzas!Z0 zBN~5~uo>bCf&w?CkRfgy23gFrCdb(jID&tZ_RK7R|DeVU05ASN@K7wgeeZS6+K`BR z#IRykTZ0?$^lJ^c6)sXQr!lz+SHyiu3#2W4cNO#Hhw1qub~s!NwPnH-3YzbnFdRwL zEYXDd2xfhR36n7&u%Dd!J@%7Y`XTe`Vb+s>C%R)B$oC50F>LWTj<+3RImukl;Vq}9 z8qMHmOulV9Y|s7|?5s6E6Dv2%kGG>O9-~nXylLP2@2rCrPU$XeuXvACIAsUCH}qW- zIMXb8IDatq9j~6ulBNks5gYO|cr8`z^8Tt+w-;3y5^L!S)U@cAnqQJhpXR};s(<2% z?Qm_=gy#9%`QcL_r4>4$pi5f+a*edk{g4N%wu}e%sz0*mVl}oXVEOC3Za0ZvhsWbN zx>d{qUuo84CV)4WSWm|EDFGK*bZc-V-xn%rVbHnY-9&$er!R+hnP1U`1*X20-l`i_ zU#Js4i-Se@riNCAnuamvL*f$`DY!CY&2p%5jp!C!_vjr_YGdl}PQP?<@km#ux%?2q34T*L>itKz%dt;JS zdOU7X)?$e^UQswW@oI{{+PjD)U0Qqc3OW zm2qRa)iPJawd#G^AD_w5+4)TvS$0Er8W>$DBZpwTjPtU>$GCMtx2!R72AWPIHRs_3 zv^`Xy^CeIXb35r3sm76_ky!3gO3TZ0o-42%~{FeY2`8!~peMfwf>8U}eG^zLuhV3w;jS%bx2gfnJ zBoDo>sQU9Fcr_CL#0hCd_5~`1qbEbeH0Q31=Fr(<9AOUfyoozr;{oZ6N$=S=5kbwo zo;e(kF(ossGl9aEkLI@Vt2DurdIO)^$bOgF%TOgyBUAU2zhZU(La%VRyouU;eawx$ zq^fI~07;x9(0~56nnxiy_M8&oRCsPFE%~NKUy}`jtC;$j(xN7hybyWuqz@|39ptIZ z{xoXp&aSWQ|4I0PcsVuDvzH}Xd1d$?XjQ9}DUL;R^~O|pok%C)$JQwQd%yQCOPP_) zPFm;hbAi#em$c|mecX5Bc>Yi-g(z{j_(n#=t&cbHzhbGtIG$720)>4Rns$mEZy^}wDF(6F}mt(J(?Mx9Et3A4?%=jD^t z{~kJe394!y;7wTRr_?04F{!eju?W@PWcC4l?c_@jiI2lZeP@u2)oGNoB^vDT zJ~dBv376A2ml2FTEJxF2NjX}u_4pVgxu$HVd5-Z+z{R3qb-i&$Wy}a;QLAU4zN?DQ zhQtufdm=gV9b8j}ts{ z%iI<2!1NNBY$toe-Z)gh_V=g-wZDtJ;i=Rv^7Eb3yU1^lNH6Rz^6zZAc9B>3=^7R9 z4x7#{a<>zw}D8E-MknQH~4HUub())CW$#dQ{rt;h-@(}^5{lc@PEYxqqXw&00 zrNxGXxo`)6qAtlgYXt3r!J@Q_G2OVi50WQy|JUnDebL-%k|D4T$woIfY_b^0jmnvp zmxLeh)8Pk`4Y`%%(ZBE7zifq%e^LZ|-0)ra_}Ta1V<_Lg7ax;M<+=IfI}{%sl@=Rv zb^Lqq@$RAcXh(g~-1-cBJoml$cxRaK5&IwU@n*oB`ES6h6L4Zf?)gh<28E9{GiV5j zcaaE@+8H$2rWcYv-%mF)sLZB!lTo>AmnO?1^H$&bw^waZ;*8AgzjO>4@^~UNy3yPtd$bp@+%Y(6b z`Q9R0R9A*KWIgsHam&jwLl5~mc&8WAP2V_&{MjcwKuR!a=$sGUyQHF=1|xO(W`8$r z1LB!dKFSM6#TV>=z4gmvTK%kq)-3W1!HS-Z|tHFVwbO|P)}(hOUs$4A>>qKrK`Z*m=Enqd{304|4WAnS&ha|n-A2n0 zI$BP)_Y=shJaTa7?8!JxAj&1Z>8hOhQDHGEyB=VcTQFe*-qU9@8ref;lLAKP`0y_! zl|2~-^;67Kh(9Uh#m}U|3|#v1jBx0r(1rKxn7!7cC?$VJbC02BIiRKGQKXNRBWaZ7 z`UkXD8Xa=Le;uw@-}vCy&w|owz4?Fp?u~&&tWs+B7r}QKtxb-fdDcOT?(eUk{5?$m z^AFGef4nff)ERG$^<@l?H8Q|3&qOQ^x_tytfn-RqZLhPPu#`NuC^Q69wGf(PKrFqiE&sd%+ zT&S#tUQ+$bSEg3|J1Uiu?%y{r`FYa)`!bcnfhFugG~wBla@z_MIO88}`Igf|?6)!( z$8ztdsC*A%3$m@f9PdCBI!zMowK}VraPGSotjReWtrRB_-zwWeU+T-XhNEDFYgna) zzZE~Ky6vQF7{ZUU!~dz>G^bc9S}gY@TCM(wtP5e384cTWqDaEtuU5f<++Be+IVZMi zm@R|W{Ny|OXDp0%!=3h?c~QOGQyyFBH+G6#X?hM?JB|C(yC1!%dtS{(OfSj$^5XU3 zFX-*itV0*ll>aGa71;};pxE@9hO{-&(4d|(zui@_QP~$%-G-}RCC!C*B96?tOlOy3 zxkZ3c<{k`GZ&%k_aDFa%2ny?Nvp5+ClC2Sb%S*Oe0OJ9_s-#twz6HR8dCTY7-3V2vKtk|9-o6et|I=mk3k%?+Xa&i zsr;qG+TfY)ZYbkW8x!f`Z2vp&;g723W)BPZHFp10erL&T#~dsfHlj-yEpcT@O)~t2 z4??i`ZH{CKUbS4}+}ALO!$IrAg@PMlp~lP-XUT}p6z`zo^(wx@6oO>6tf|g5;*(!jvo^d-9nhps;hXZ%h#j`p8%{7Rb^vAFnN%mPRBz2|&I5JCx4uvNLTvJ} zIR=+h`_=oBm!y$?xqiK)2O|xtt-x_1e`T3TEm$}3OSl`&$Q4?{((@dbj$lA(jU zgx<|)DIAYqYS?25;+w|T-Ozq_%Z?%^fPdibCgQ!L2xoPz>bsz7M7yleziAvG!fa>m z8B|xhan7qkDZ5Bj=VuXk$1E!Ap6i!|3HCb{l(OYVUQi%aOKg@!z#yDj`Drre7v0{P z$;-CLiaS%58n+?9PS6`v%8sT#q$?369$d)|hP6=_?<#Q~;Dq1XTtFV{F#_m_X;J1T z$HoTt3nnp)?n!o4eV3HSM&y_!cfXe0-@epUwpUpEa4o%w0A#}2@%cYh{_U1Uqo1|B z+O^u7Mk!RJQEPI}B?Kb*|4Du|!Jc}0kx22I^nL|C%_azndqCZlE@bS@3XFrsARB* z6tMI1YJf0MFo>MdSMUY{xN!`QP0|*+G?$_nn|4vM# zn$V>3hBd0!?C-FY6NSDqc1v~`%R=)UPLkwKq1wY1@jk5tr?;0GxXaytS7v{W6vIWT z^1N3-8k&|sVM9L2y!;o900A!2@&NPURKWRJA@Bqu-w>?u3$m2Qt|(N5 zFp#illiuYiWDN7C34w+{1I`$H$e4A@$WT?<|HI-Y=K+qoIkjqp9p{JsArgDV3LzK9 z2A^iAG@0xoD!q;FRb-oSakIAk^zb1s4Tv^}xe+eX&{Yf4W{BKvpxc4*2z z?IA){uNKf|3G*I&UKVPfU;3g`AP@f@)2h*v1yo~m?6KU=R5n8xTWMS zGL3?qGJA?xy{eIoENESiY0as5ocD5?~Tx$FkyYoQR1-!6;Nd8t-B`%7@QDrppV+U|^y)*NU%(ckLt{^V|l{ zE&FZYCFN>OAJ{Pr_;T+x@GBU!XF}$6Tjq>mUZ{g(OgiwCxu4U=@Gs0*C9dRCA)hFm%s1j?Vr(F}zSP~X zekD3EkK5?!wo~1v*!UzXcQ?fSxjllsVDD`+lii=DlB$DsGq)w$Dul??m6~&=rl5N) zWVGa*6d5&{E3$*)`ID{b-@E8J0v4p?70r*9$)V}Mx|FRQ zp7KI(FU6S4Q%>l$r7{dQ*S86+jBLx^^Il|aJGb$0d%v1O z{r$2Oqz^1hK^j|@f;5tbbX`2efSCTvc3|bxG;VHnmh?nxy;%cFSWs+!4ba9xBy@yc z#3a659*oV-uGu&@;Ve0)q-uLzN%Q8LFL9P^47g2!JxzRU6KJm68h$O~^Vy{A3O^h9 zaBubgUEnT!va@7T+o})in%}>CVU^DN4=t=d-C{NBy|8jjMqMYgu9 z-`X`@RmWKO?CO$AdXm-u%8(EjzUI?O^tLMt8OeKF;ud?krj7u8VxQwR(RY zIYNzk*;T!ND=*<=1*z@8Sb+j+UJynSPe-=xt=?zGSe^9q^y9IO&EA{r8u=I0>-cRc zQe2fL$STt9`c%&VKVq!{KQ#n^?@hkdH@LxocC@aA z9^RC(ephi6;MG`nY!h|N8h1l@N8+Vm|GntMEs9g-dN1tVXFLNBw{J0Zr%QzHkx~Em z!6O?%N!~mE3wVf6*lYm`K3L(5-epb;!R1H)g_sE6VM|-J*ocG(_J`QgTFEb_Kf))T zj_c&gy!sdFBxe*%ms7((wB`6{3R>KN;og+}&7 zf!i3~;ghWkF;w77)PI*hQV-PYKUI7Ga%3>>{} z=^cfXOJl&r7;r>RvJ{korcV>kp|)NZn!#u$y0_pnc43lR65MsI=SPY2(3f|q_SK|hx0f^bbYlBs61)V< zc)5BJ&&gRbOtzdNnMU0;sJ_WHBw15nsB6ih^Qvx5_6^6^jLz=MCqgZ5RHclWcA2tw zahKGZ(zeO0#8GCecpGipN*lo__u{>(^Z0u!+~H{j8qm@xF!2EK_rqh2_##On4?$Y& z6s!!?tPGvbax@!_<{;ra@!I3!>fE`GZiDFb^_^^tA#61-`&~QO>IE!)e2hhv${F%m z>OJSO*2aIp7jx6^#JV!~b+?k!LU`Q8azFYL4#eZh=8MrrdGpV}X4$jH<|u3l&jUGV zQ0h*{`E^QOFj`h8fAl;oDl%d;HM`Vok$Z#nk}mdh!>I#F;1qIDi~AvYUL=ni`6X3=sk1s8H12m(XnX`T4#K$S{ge|mRysQ5AYXJ6 zr#o}^butBaQIP6z9#EMxD5J{Hq4LDa!|WqzpCD`@75Mbn69^nF>Ugbcncq0=UPY9& zg1EMy6#kMZ%tefEazk14qP#%tV&-=@=Z*suN0CR(p6Sr$)b9Ts%wCuLkl@kses*dK zNyU;f_nG){Jf+aPwPmCfPe;i8G4lr1DuFH^swd3gc*ria^kuE--a>UpZ0a8d*oI(q za-+__GBdnY?@GqUSwi#ZSAWn@C#scK=Zyl9g+*-uz5qXrp2AQpx1)aW#`f-{pgx(4 z3+I~`6DT-TDP9!xOKq#=K7^~x*9GHW);M~*yNVnR7=UJ`0J6ZGV}VgK1`0R;NJ zu)wLwM}h*7Ha8MLw~S{&`6f<)WKry#M*Elsc-A5V@HG7h;P>7Uz!ot>j9%(Z0~7E^3HI&U>11WI`TlE8$%@Qx0vlhEix^9%yJb&65qU)0ErN3xOak{UV;Gjkw}p_= zL2A_{Q(TZZjdp>tj~!ROaGQ(wdy~!3!0s6o`)~i<415hQpk-(_0de!3+F#PDx!SWD zi1F7a-tVyt`GRKS7D})s{CAidB@JbD00ZvVq8ToAYR*HLtcVtybD(GO+)%by-vi4L z#(9yppOq-gSbt%!jbNnYqUeaC^2jS3>qc50rdseMj|~}e2Y_Dwi9ugCo3!zXLi|%r zQm6y<3mY(ZMdV~!%7b#KB5{I?Kgx4RH{ZmD44+0l`KFLs4E0bvc$nK}@4&~Z+^L-n z;sGR21-S%7VQvXj`=w5D+B?ZfgWqoV-pBM~hh{4+mq)K+G;!j6lNUo05?F~Ir`rJU zTR9N(o6N1zZqczFi#Is;#CWM)>z1uvaK_8DNB}wA%#8v0z$5*#W}iw-EqpSI;_ysGS*VM>0R;>A#%v3Ey)=)>`KTz1eUi*q1dha4K1uF`#6*NXm0|QvbYuS|iVZ7TqT>us&GXO- zSr&5>*px(jVAlPxg@pNi)A1frFO zWE6sgou0D-#71o+&L=oEN?br1G- z!XxCxrdR4#uCAQAFGV=r3{pHM8>jt&e?C1un6#-IZ7LMAI^dRd6r_R)A;S<&?1Zm= z3Oly6!+j}~6zv4uO1OCo#q3~kEXBOVG&2fqZ5`|DQxqnKFO&lL#Ak_OVneEL$C-KP zpgNytxMgqQwH0J=!Z#+yMkhCEmhA za9qvsu~YjQh2!J-&vFI)A*jJJl0%8&B%dh6rOtwSGjX-@OX14rChrR#Eij@E_7w~_ zsUL``vW_p&qobob61e-GM|pwIk20)eq&Nv({`;>Xj1hQPZP;PxqW|vfHO2L5PI9vP zlLlTf@XeifhQCmH7NyK6VINb24^GTN2?7gTRlZY!hz93 zmgKP_l6n)K+c1%OGlN<)ku+TQtD9!biOM;@4ql2n8=d$TrtACbIAvXuw=1vl3ARV3 z&^n3O2sYuyQ&3-0u)AtsHit@0SP4#40-=5i19I#=F~n+ zH&VoK*s4-xJ}w-nSc5^4#7PLwB7sWqqc)tH&J0hQ4pye=U<0gl-GlvbaMbP)Q)CkC}_o(a_AjS8B$t4o=-r;f{F6Cz}lfmF#BFVu(U3Qm6~F$3#3_ zL<|e_jgjr;UCaE;fbuP=R#0OcpE}MW&TkAfj5(4Xa`scKBUI}R<8%UGv=D9{@~M%$_j_CR=t$J3-W?!7)S{IFYLj>w z>J-h3j?Z()=WsSZ(K|!=gmezxmRf~7A$}uPvF33OHj9N7N?0@#!Z(1#hV&XqU5~02$0g~>#exp0Pb{S{>3EBfJYR(_z!@S-_{8I+q@J)Ut&dyA7%5z&s1OP=8Oa7 zKT4h~^FU|%VsWwT$FznWK++*`waJoazhNc#BjQ@QD=Fy)EpS$W@Us7XFd2WF>_$e` zE^msIO+Z{!&uM@WbUPMO*$7n@>l7d*F^*4TTR!!%pJwROU_K3O`E->1bcR0Ro7|~K z%clg7RCua>xMi(*+3%&8*Lkm^nPSX~b&B7bZv2CEra#zBk1NxOGA=Zf9BJ|`dE!7a z@DfPIl~y+)K1k;>wz%leSZcTJ&f=Rfp(Lt(i$db>$a$b##}YQ~vVRkJK7qeuIG+6y ze@~PC0Dp)6DlPRll>DjZl>Oef9R}^f9Qvtjadus1Uuf#Aj*5%xJ^c`--&k(JFJ)Pp zmk)pQqX4vIiP|jR{=#JGmCQFLm{FKu+EQ}6l3jKWaev_T9bUD|n=G*L6No=l>eiLJ zjhvL{XdfdC6MzS>onP-=_yw%P4@XqON^R7S+GZKw&Dx4LUic#JM(*m=ZkgTAIuu7f zAE^D&{EPCP{L2cQ{u6gO{VTV!MmDiG9O@XkHLs0qL6;Qa)j&`lF2m7HP>)iamIfZ` z&z!mcO#C&BfWHH_3is83xl35ik#1#UDKc1j12aO>yO;A1poUFNHQqKk>QDgm!_DBQ zrEa_eS}8?1^seLhI&e9IMo+(A1vUhg#*J=L5i(;6&3NuMf2ZRsa-4sen(tGAJ@_pFYf9 zNdH4ORJyO1y4W~nD>X*G`AyM@`L!G8w)3LDJD7A=vY*U}-6RsdkZ@^`=uTkZe3mHI zpeBy};ZVbMhyk5AU*%ns01_9kq%GcEf7h8w#c~s1PI-!Pr8 zZt?r6fRXo?B3{<&`lkkCx|C?7u+g>TJie0i`wRI;1gGX|tC<2s$ z{GpdYW&Ckae0&A}rNUnoDHc|N+x@WcSE;C13WFsHL~*h5#KY})n+qkI0x!rI>D_Mv z>(4z&f*Uuc9f=2>^(P4cxnp};xQCb76NN&AP853g8-I;f<2_!pt>$TR$j916iODUz z2lK55yYq#d|K=k@h}#^yxlYZELMyul%asKPxhc~8OYg5XWS@FHm#ReSb5w+^Dm7fC z+}Fzc|2q&XYYskGUc7q2m1VVGEEo`|{#GKMbH@n+f?G5;cvj}GFU|H!i&iyl@Xdza}(1UZQX5{#5NhHQY^^Lb=ott|QY=ZkUm{LN63M*%Z4u zeABdsK#uA6QSr96y>O?h{q4hf6gXv0apE);Af+2 zFkI}$k9^~Ub~WH+#2Lq*gOm16+$K(v>=iZgyF;?6?Tf4>G4=Aqu`T$4(MaKVttpfvkmwsZ* ze~0%UM5gy-ESxYtCtb)mP-)Y#-iqfO|u6_x-_ooNgQW`!|f*8Z6k zb3LqV7Bvs?#?h+6ixKpCrIOW&z;cj=d1ao-em%s>ZfQn;E2B5aeieGPG8)zZg?4Cg zM8f~z+6%C>*&mcQ$J3)nY#LFEw5aXy4sJE`CHz7v;q`5-#eYrrv4vF3xR_q2g#hZLxw0JIw}NY$M8 zV_Wkt$*7$K(b{!@z)m0G90GVO{r)1DmivMlNnE(I5()&I9M*NShqa*OYJxsL~>Np`qWH)S&qL%2X zHvsfJ{EnroqDY*I?!IPi32Ddef&9FU_g z@csd=OG~<^cF(8M^LKhX2kD^f9`6BK#?d0Ox~R2CT59*N<$=NOt*$6$g5p285Zlx( zy(Y(3pzt2~L`izR*80a;V#zfMePKAub9#tgM3p+Za>+e~+J|FK<7ru2$K%SXVrIr4 zOs+}!EUxGf^MX@n=S0sh-m~E5;8aW(rTg~=lj|!%{P0R$iCv7Apavu^_b-#{T@7zp zS@?_e?lNXr?=ImE<#M~*6#gPHlPZh%EL@g2bx&yh&zL}x#HLBZr3^fi;89;v?_g2e|Hz7!t4PRwK8LH2Io-8Vmo(q zCZXJh~E{;~dax@Xv{>5d+5z8*z5H2e-PE={#Gj{m}Br z{x;?Ay7|w>!wj}S5C2}(4WyI#6~%SBX{;_(sH^+d%AZ00Re58Eb!X33oZ5G2uv|Gg)IlevjLCwjiLeH>#5<9xmHc69%s4~Q%yABL$ABD)Het8@agggq zTjVp@w~ZiuuGzpTk4|EZJZMDezFK*;WBi$S%p$V_h8<*37Vc!YG0~3TTsYjWI$Tru zK6nnBR(&C$d#?G8X>||Ar?>|nu~wx5X3bo7LA^aoG+8n?^zUD*MQZ`qfNP@p8~ROT zGxI4nqyRzAtMlJA&%9?_J`R}9$3L2P1eaTS*?DHy{?7}oL~xGw%H0Nx636YPah#`O z1IAaGn$y{hM&az+A`n#@pR{#0B)>S#8Hc!-zI!cWMtDf zg0?3OY`$9Jn|J|r{XlimNOa*8#@WCdo4CuHz|9#(#xYmswUSTL=Nq1r#Wv{PFOx&i znyD)O@24Fu&P`dYj(pmIor{#EYOA6>dIPaqV=+3`FE_lb)oF+4BOJ>}-ERRNw2zCP zrhUxtY!;{~62xx|Mi$J-3SAQ&*DF&06}R!t`pL2+iUjxh&6?y&+!|ZKT|6D{bjQK7 z2~L{9LD@|?ZvzkzNiurbjG#M#7)fYtnm4do*zy4Rce)5_?Rg9TC+M~@M~Ao%jyc=o z50bLo1|`Ltn^eyQHhOQX`H-GQiqCt{oBSkqXi6noY({OB2)WtY^C(p*bv3Dmk!-{OS3`L5O~GvMBIwk4 z=iQ6?d4jZpoZrB_P$84v`*#62meva>Lkavy(*B{~vlTv)-nUd_dcK(l3_zvPzPHX} z(!_F)=V!$nQhh_9(7MG@9M9hr{)`_dzXkl|6P0@g`G`hC53{UuAeJTyj=H?m zojQGmUc5Qa3T30F7_`ab$!vFe0q|o389i{T7&nJQF@n!6hY|Tqb<2LnDTAs0jv#B^ z{(s7*zB!S)86bt5lkcGwZM^t(NnCn20~J;TT&_WWb7r=R`oS4z4AqtNHj|O_Qu;@# zKdEa5ha%cIHL`$V$9F5A!Ygz!Vx#vlyHx;al>8scKITrY3%;1T!H>nW8;xO1%_+n; z4QKnBg{mTUTVWd_)#QvAF#CMV-#MYpg?_7M_2l$O5`S{8GaoVFmUyBd?axoyQEDKv zcR;v9)xzWer)HxTju!KpF{^QS2eU%B(YvtN&Oyz`h8#33EDq5d8jaJ@^Sm3NsExb( zKIB!cJCS#JGVAAvg4xHUVCqEH(n9X=TRo!Smd{W?;m^DAr|}w=oBaq{sQodZ*e{#) z)Pxq6*NGJbZ$rt;{AEc9ispMgP^6Tm3 zS|y+2C--9}*(PTzxuc(~n<8xT&z1ZihEs62ODF$G$(#LTUFB_a2bJ7FvdGrmS?PX@ zdBa%8PnJnd+w+XUnsem_`E7IrP7GwB2^k~4h>mE) zIgZ!_>`r6>37PWG2*~0T(tH|lBFFHONeA2GIi!f90Kz+AxM?r+TX>%YrlUv%A%Uv% zvYeVz4Kl-x@p(+s!$9WTNY#i!T=nZBb&fifC|asJ{phW>Ht8)+72n_6tIqLz`xPM3 zTW5);2|f6eN*cocHBYVkHyahq&bjk4`}JAkOmnC=(QPS?S%N~FWI&<)qwm4_Qm;LO zZE${rg+jQE?CTLe6_Ze%1R%sgT&Uz*@-&(hb=4W6AN(t8EbtZE1hc2JN7|%Jm@n(R z+E09woXlC^R``~MNoQHzmF8v_6hQB8NJNn%jFaP>DSRz=UsrVM9!s>8&SgQ{>AiqS z1g2|AHF0xf-!Wn2+VIlI{HL-)!&j)As6m4^rfVHat+MK+Yc*cAE1xq}4$>MqjVcrU zsMG-a?#EV#;2u&jN8(4X@qTuyYTe`aT(h7B4Q5JgWi8Uz_&Yq9oIO*-93RVsusg4b zDgN&mx%hG(zUUWZC+UfP+fuwVI^Y-j7VCRZk$d7gH-c)zN%(PmB`CusFmwnatg~+o4yJG-b;0mTv}Kl zMfF^f=&0u)QC;zQh{!kgzYzJ(JtXlQC7wvY3HF@_mbup#4psdls5MIuK|NHu(`JB@ z;Wzt)gJ5`w#&-Dm63!19SAep&nBCN_t32&JgWt^XJBP4?kwJLtLoX`a8yXGb(s$97 za;F_Top~XSksr);YR@1Sopfr-d4PfSEMK0IIyf|XOL=Tewh;$c2q)9-9n@U4zhL%x zI*(WB{PC55v9HWJovVa3M~W>U^P28HblAj<%)v z5dM_^;MnLf_oK()XlY#>h478fO4?g9sM$0D#c|$WN3|l!f?JMH6#DW^Me%N;_G7E` zKg~Xx+k(HH>52M=vskrXlU-oWI1ejXRLlc8v7GtUyp{Oikb#?Db08U)C6+y2?M8L8 zE00pRp_qGw-b!?V2Fz{}SW`l%x1cQqT4ZSGrI{PN~8b&!^@ z1D6m5`ha~llCuaF2}h}`aLTr(^Q`mp@SVe1*9}D#F@(hBgh6Xe%5)?dRF~0e_|C+A z(!LJSrxgc6nxhsIqd$gHH(FxGjf|Z0h4kvR5z0u%@m6H2xOV{{1A=)=X@Mj%~*t?j?4;B2Hko29x_u>0#OaoD)DklEk z%0AeNKcbk+vmzMH&27f)uF!Y#C`u}F)2?9qrts-Nz}HXzW51fv=zH)w)Lg1wt+E+> zxE$GPNj5!4!zackB;OyJ=JdNfYSN;%{3EK&aBnM*A+5F$zmaD%=)VGi;Qx2}uXJB? zYNf4;Jk~GP6u^jPra+~;wZh%U6y>%0Fsz%xygol>@W%g+;_7kL*1GA2) zDBi~`u`;1wdV3;QnCt?Pzi8)(DW7=SP(G2qK`obS0mlJGb#tNz3mzuS&iqukANl7W z*%29X4?Ey!LCYjrdf!5 zQ!|5{gX+YO2>Mjpoh%6^g}Jdl87x6x)^&Z6IVEQ~_lzVfyT?pZ90UgP65z~3wr+#0 z%$I$F2%tN?`Gd7InS{{BXrLsr_iX1bp_i~E1%@$}7G7>#gs@;-%1&L-y%bL&IDe5+ z3t-kIRWO^= zNY9v`Fv&m({m1-NWN>#GG%6}@sO`nO+GZ27p%yc%dEwY;!U-ffmvp>w@P3*M(NVo3 zb>Bwz9yL489>xy93QojmiK%gJ=Jt7arS}1CM)qb$ z?lPZ&5J%oW>o< z7I3bTT4fdd(#y6L)@%w*jrZf3+_#e(OC~ulC8uf!VlTO9nu$;{!A(9*xVwKS@OX{@_w=EU+0);wvzeRruoF)#;#$g+hd4!5<_Xv;CaDn(iA1J&P$v4pl)hU&R z!7%G92g3$l82#seN;H3-fgb2y2X;0qioXhfl<4d?9lk%V?Q`zn;TCyAEDO7`G7K!A zx;~IZSgCnk<#osm8V~mXGu|vwjDJ3^W*9w=jJ)S#GuFrPQXUNtWb{CE0vn+68Mi^d zLr0Ut?@$(xsSb^2dYj?U{B7%ZMh95pGsxuw7JoyK*a&fPqP$w<8dgVwaEq7`S@)g{y5cslNeu`!2-iVi?3A%nKcwNU2ehWRZo5wJ|BzU zOJ?D7!C8q5)a~|8?VOCh{_N1Ux|z0|+6iR(A5g#d^bFLWLjK6e?jIS{C(S5@#WK!@ z)ItCC#JV-;#|Dr>n_)Wf^JLZfe^@&c_^68H@h6Z#xP0LhJRqC~K?NmBfJg=sm}sI= zJkS+IQC3|K7G?yMBRGlh90zgL)m2yVTy+mwbrE5O1ds%D5%9v3Lxnev3SQhY|8G^l zH!}%Q*Zu$aFnRCwS>0V-T~%FOouE)~1JV8_#6)-F!@1{L*(3P|Wc}X#9nC}<#!tSd z^Hl!wC;8t{!L8ebo*c{USbAoX`TY*aHgeh*G-f^s|9h11zo>{eI%lA}5Z+f-!vyc^ zsAi4T^=WTQr0HM8VuG~=zEg~AJw{hH<5-nM`Vv-jSyK4IXpS-Q<6K2~P@n#VwGaVp z1QOOp1h98~y6rlps}aD|`7}#M&U*Y)t;aD+kyX3~cOu2?rypfxuwq-B_%22W)11Tv zxATVuM?|rN6!wZ#Et^B!Be2IA^|Z8|^>j1BB_Mbr8OE>x(y}D9FXyH%u^!UCd_sz= z5wpCAsjYx>-Q?|JuyVWzgDnlaDe*;faZDmI8D)CuZ(1M25AI6XP@#8&Xzx%U`GoG> z<^1DGBS~=E_#`Yi+Z?)Fg=c`m>!2-nYabD47wh5N?xyRE#BoHBg{N?UYV6mU^hvLP z-i`J3_5)pe5EU_ykJvSoLEB1?RAO`azUi4zK{+?jr(tXFic-eMU$7In{D%@j9W1!t z18*MuRGC=#Y?g_FdrVXeKimZmns+X26HUQ;&LmQ*0m1&z(DWetbGJ@%^!`j+US8_u9wHdVdVHIlq0vbfcvL z89UE~1IQc1PiHc#yZ`-5^X1IV-hA2YuRmWN=Qw6^z8o#{#W`L3E~Pbz2_vvQ=d63& z0JcB@d(e{tL%2N5{FI)gRWlTdyG zD#L2pe^zFpYLh~=*;&({7;az&eirBc6K!2SOzSD*n&6)hIdRMBFtxMEE5f5TOqa|!(-{x8yl zUt#);rP6K@h~mz5w9X9(yyXkc6iLe12P-d%n2`hFrBe`bRP2&hIiL0+0-Q)7Y(?#* zu_|qDLR7R*E7302l}wlaQT=-TL<6nKm69ViQP zoD9e#%&b&il)q)>U&}7_;?SoM<-Y*N^ z0N=6Gd6p4RER$c8Nc7RHmgi8S$Rcm0MNc8QWb4NhK2pl1VS#C(g5P~ahq06kwgliY zXilWy29^dep?b5dRWk=T$2}&q4B#RHKb*R-cL=lm#Up0=CnAT8WbMWHQ>E1liHM3} zbOV7f{(A&M?#|&9p~uZu9hCkT?$(S?_|Gy3!QRpKJfOFrq2s_DQ@vF+N+*L61iynO zu#ep|h_gyP_Z>tRwb{82Jof@t#x{lu?)Xq9u-CE^Ov|=^;?JyN7PC}KRW_dP zy&NE752~JB8!a2_yPX57NlqB>9N6_HOF(fg#g>_aM+wGOn9JKL*z5m{n?TpFGL zI_z*B6q4w1P20hPlE($J%+Y5sy}rH-UPSn(&kN~pryJvJ_|a8m`P+pbEeVg4Whyu? zDom{KPwv3Y9F573*U>k`F02PpF=&&Z5#JhewqC&< zWk~m$ysf0`Yj8pZJ|bxZD~3SAHHn^4J{DM{zy1W5XNsMEy7%sX>^J zQsJ-S&h(vX_7QkTSieYK*BnS`q~i;$H7x&IR43J`1^3_ig>6ch~t&=#-g86!09beMwYG?Ij*gcFO!IuQZj^pL6M9Qvw{ZoRo zd3ixzIEr5Ik;J#47G*VI7M-UlLsyEuou{7mO4|5c>DCi9Q#gBh0dzI5n)Ifwi>0nk zucTF2m@d^!!C%PC9GF1oEHA$_y7^Sx?p(m}jl4S(8M~7}ty)N}Ttg=U0A+-Lg4qL? zdVH2LPb zaRm}5yXKPbUK1B^gxg%Z;F^}EovnOfvd=bWzIh@g8eegg=0 zxey!&1Yqw{G^qkamB?>IZ_;SpWnb?!l{9R>FBK${L?XD8sN)DmlAU&E+k^5VJ0NaT<|(yvQrONL+16 z&bBQhqb8;CjaTXEzV><_T$~meWNTu`mpJee3d=e^n zZzHRl9Jnc{9Vw(-^kah{!+ z?#BSwjTrk{pM?s3pQvSaqLy{Y!9xY#a`Af_PGMjmp{Sq$ zjXay`V{+Uh*?->psy4m4rDse+7YkLgs%oTWcU66Wcdp%r8o{zZ!-tPEIPoK94b|fZ}BQcR2iHYRM0sJtL z9EpkK$Vntht8EGyNHH8>yA>13TV3;q^SMQr5C}|l@LEG)Mz}$-i$zaS_}|u|xUg7i zOd4X|dUdShVdjS4a#6S0-*t~v;F7n{J!MaS7rVQ#XHOE-+`-{*=I8FuOkBE;X! zvT`r?fQSt=X^#>taePn7rRO#Ky1Aa#xD6K{=>kuK`{h8m9{HH1WsYU(WXiRy&Aj~0 zPObZ!nBHl4Sc6rRRifh4;ejU$Ii}wR>OWp3Qi=AbIxnLhYFrG z>BUBmnAZnW(Zd(2F=qJKdF48qC-F$Nb$yJq+P`TSwf=&ktM;!jkZ%9_+1@Lzv&UWy zl7h$AQy;f}ayQeWW@1K}FJOerxXHX`l2}ZYyqey}H3m*>3CG$c=NtJ(VZpc>yGERP z*Rj3&`s5fRU{!ufm9nMOsownLUgn}c#^iD8cNmIco$%TeY#;X&;Q;ThKQMjLx6{Xl zHi|)N_yqfG*OUV))|+>zm@!r)HMr_HBONWu36LW{LJ62k#G zdf=08J`YNTNTSITIuIzG@+M*&)f5sb;AUQ2=1RHtrkcIoqL0xGEViB`l*nQpu6JG$ zaDd;nNNQG6J1U!`_~aR->jwirN`}Q*GGA&Q#rRIds%VsJR5Z#ji8Z$9Md!1Br`mCGG)fY^kCjeij&yn@hbGKMgPn|zQy!o?w3YY(E zmicq`T?n!1pGvolW9~SYtq`edr?aD4(Wg)#0e#r@JmmBQUnTB0HJI-Al#cxUCWA3t z)|J_jq!L>|?CQ)z+&iCcroh8z$`nYp#~**avG#=a4heiOELl%h#QIX58uZoEFN5!q zf}^R!IUPVl1r~B{?IR>&~D=u~?^+u5Kx{nU!lyd8;!sB@8%b5@O~= zv>za*;7bEE5bQWvNGRu_jGECL+yxJ=pf2_kKc`8t&TM{53xs!98&0eNglV{?EPOf$ z?%}W^e5hbgo%9E1^HH|R$tYf6>-1!l?9i3aAe9FxKuux!P`1zeP=@c1YOFb*#@u2$ zwOuk>a82xW*NQ4sU~6qTx;ZVi@uIbfn4D{}zGu1=XbdAcr$mt=<lT|D0e# znE$-c=k<}oJ!O`>b%n|{o}z<)ZL+f9J+TLMIwaR0>3`CGTq}$4^RND-zQ8_EQE-~H zESTwx-7C7Dyn%cQQ*>{&5G-})@7aBY3PKcUcuFJi|_*SsKPTI^k9LO!J`{6{?%{vTBsKNL2{FYC8Q?9l|?IRXRlKC?&Roz3S@ z!u!^058n0(cn36uSDLZWdG`Ir;04r4A~*u-bI6Gb-en$mjld^&Lst}hCe;FBCLl6H zKjG6zmzsY$TZD~_m)E|(xS8vAYx^GK^C#Q)-D_U^=A7h>M>&T!lj9+s{q=UhNZlP0 zFpz{(#^YA|XU5}(I~tCMjAMU6GF_A_3KR|++yR(r@K=CE#V&2=_GLWYz+Ty-4`a$b zHAvj4`?s#_eaY*TE>+U}l$1kN5Mp)}Kai`O9!EK>>wCJSdx>=Rrs&y1TOxVKwH7%q z?mWT(3A6MsC<)B}Wut_|Uu;B! z_-z+qQ{00(awS@J421`n*$5AiIhr!K?0OP};wA2&cYce_)9TDzLOmYHT=fLMqvPey zzvfakW1agw_{q^>y}o{r^QTvI$iC%MaPAJaylgqB=VVi?dxK_zzI)~2Vy>cK5m}_b z_r3#ppr!)LnUn@GdQuAyI!jfA6ygqMHwm5)D%iJzOu8C2@2>>$qSZ?8$Abrlc*WO= z<5Y2&j8j*Ug11TS%%MzW1``Jk>#eup%ppaPOO>AtYQxAC+%5G$3`6k@>mE5oc_c+d z+Kg%60R`$X%;mVl)cF|ow|(q^ku=?~j&A$HnV%8{0uK)r_!Zy*p?G?reJI`}*dId} zQC1i11^!rT?`xH5O_&lnw#+pL1q&!bCv1cDHJrJ-<~TeJvACZ7rrjpWhX7w8<1Y2f z#A!AzdilzDTra1B}nj!SF00`nmEBCAdrf~0U zX=WtzpCb}HsZhu3;jGML;xwVI(6xm|qfuL!BOnPX^uAp%U2YuM<8(3aAm?vE&e87T zKH4y-OpSr4(h~TBFAeWJX{kLp*0}-Fe{}&!-&Ae|NI5H*+FsWXxG5F`IyCCaAyn{E zwY2gSxCYgUxO?-*)4Hen$u>1q6Eh)bI$Q6~UOG~^J#&pU8jzteznDF%G9yxOA2~>? zTlSBe_r#X{t7O%DSf}riaj~2sqQi)AVJ(W4sn~X#h2K3C2yNIM@ViJ9Drlo%inYmO zee3U@U#T!5a(rS+&DdJMRK**&(Jra>Yhl@;g5ffc105kl2m&*mi+0JD(D6FCW1!`p z<8@kX``v+fJhKDAhF*m;vnfDE94a+!k}>SQeq*BACR2v7T@zLjcq%GTlE>XB5S-hOv{U{pjO{+62R|2^^(?F3T zgYRzpt|oh0fcBlOyo%etYn+>RO8bT=^C0ajHtlPo?dxTjh`tE{`E^P6eo4}B(BL3WpecdJ>7x5QX>^pLDH@l`{tDN|$*mu-q(KWWBtZpHp?lb2vj6k73CX#dGmo1yX8F!@=3 z#JIke)f*lA9&ljAY`W6MH}|d*n=8jQ`NM6_UM-Dl;U8MICD7N|UDPfuwC^&mMiJom z4dEOrFZ61-O`l}K;Cpo1G|=038N@B2?@OqZ3{^|^RcP;VfwrN&J%SwbSc$Lx9<(dQ zeb@5N2&64c=gfczmThZ`*Yhp6M&$7Dy+nUxzE%LOJDE>&M)+Kzb&Fbs_6^4?IG#4X zPV2y_%3)co6Dx97glD{+|0eXLNAdcs^?^b*tBY)hD@U8B6<1=9`yspv7c0XW=UBIY z%SW^cIz*N7sFJ~%ed29SjJi{;s zfXnS}p?vYr=*wRt4M#3Ytu zbB1l2KoIhF>wfeCZI$-N+oz43+xjFzOZ%m3x2(|qDCC@v_C-njwZh78t*RB2M!#5< zXELVImr`1@TM#mVk7bb?(n}_+D#?Fs`h@yme=d3C#KnILHpACxmqWZh z-BF*~(f>-yDAi2KEVECK4Ft{$h6+Q5l`Rvr`(YT7RSxxxWDfKa8Le-snk?CWkX)-$ z>~q{TgcV-SY$fz9oY|V`kBl@XgXYXuz5Y)*zk*rbYM<(V{}0b^1+Njtx6@1}_HNx3 z=XfdCtLuT4j=H+|;XIcpyGBV&XeGvbZGI=Ok&e~+_*V`hwp5Vbnf1_TSFf%tkm^nfRX5>Y-~s6;(MM6)tBre6LJFg|Nfn9IL%h^<>dM$P zT#r826eFJ(y8!I<1$qO*h`>?&9Tm*R_B$8f_ukK@+}-1JJPpsNUWW2;c;9E zvaok7%eSy!tf_C|NnV+8R?3x4b^WH~rZ`yUnpmsg4La1i{R8F_XWrs&Gw8%ur8&;F zTUkHCZH}(?){I?N3W&Sr)-{Pk)itYMm;`j$oXRDO?8c2}Om_{sA$ z_bKnc^z$`HGm7t2dWVCv0cnAm9&hR`W77mTBL$rnA-&}P4f!f`D_b^IPp~W?BHl%? z@K%?K@s9J#&2sd1bvSbicf!NT2L{5U$zh5Cl<-&&W#;l;VM)CeYrhO`2t#8(CEL|+ zMawC2WlDxQCPM}pu!tJKo~2dUn>0n?TL+6}%5RnkCr@pYO>I`CEM*iR`>hX#T57%aMU1d&b*P#N$p)l|)!uw2ohrMs_&&-|R^vJDjk2mrLn<_UME_B2F|BPK;0z z$%}WFhN1t(WwwIJCGIo_bD_K#Q7}1958(63oh7mb?jz!T&9lR4p%J8)h+ID>!FZ)v zZI+WjPRo70B!J}r$M@ujz0i#!R#%!=m?x_OM@Dcr9(|2GDrRvYt<=71Hgn!UIayzQA=1arQ^#d9DEx)47ud6&YTe!^F=B2NeUGN<%Yyy1};sr*-g!6 z3t7(e2+BV!m~nxKLjjBY3J($D z+vVyY9M)JSqNm?bp|H$eTZVo~yvV=H#if3GA5dn)hG&^R^127<`CQ+aG1sPDn`U0+ z?b;^33!K6+02Nv_2`@9Pl(RQXYJHJ484(@eKVZ%qq}VIZmFD8T-o_kLW{VLJ-ZmTvtSlY!%P0+n8kfkSN1ZK^t}w3MIZzC8F1Vwgg@sOU|G3?ql{RfFYL)sqVCX5By02!rKT zm8@SLw)*W$6kFGK6{tOImC#>>tujYUSE3DMvUua+tT_k2I~=9P|HP5?``TGI%0!7I z8Ohu=P`JS&wkwP6ov|YA2?71;sfG?2(j!}z88~Fyq2^%V3p4d%K!@GEghy1&Sh4TW zOPUr|*T#*&6YhZsP&PWQ9U{m2vWECnXNwGk+LG zCE~+mR(u^0nMqEKe-$`Y=+DO{fVsZY*}!}3_y#Ths2GKa?D3mi!L`^gxC%jnI3Rv& z;()kccV|cXsR*#%8X%n`emX#~uEzpCTLD#VX{;()U9>s&$75s=KXC@#r09gtkz{`{ zK*W7Of1O5t`l~+=z0k(Xoh#*%dv{QC_e{;r;_v3pHo1@G5&ONDRlX$aQj-RR%t=&9xG>=ueK+>h;6FVsb@SkZG0B`=GhZ5gnd!O?-O=px?YSz;utw|u( z$LOa?C2-tmz4cdzug6jmJw_(;B>~gGF_jN{@I=ZF8Z7f5iqaVr;wa}&*ch|^)&B(F2?)cnmveoP1 zR*B}eG0j~{9kF~bYjZd2MJ8)$YK>y{WqN8OdFZKseV^>9X&R|LrNU{!=A(dJ&}ZWz zu?>dOx^?6|Pi|-L6loHj`ZDR*Jw6~5IystXeq`=cl9c;uCQ<~rj3_hI-}iDll<07Koe?9{sWOYDv*%PL z6@ok0;gZ!z$E=Hm@B&UtcA{lU+Qo~-U^&LsfI{|3jCCWuAgfZS;Jd#bm(~PN)793U zvP4R6S9X<`NWpc|`=Fcdn`~tX9Q-_^AbyqH2A)a+LpB;V@4Qm%(|ltRO+3%1>)f?X zOcO6F^%l6B8!`u1`iNq3uos;>|^z!jDQ=fG}Dm)*@odmg-2_h3bPYda$$+ z!07dQfXR0Puqo2_tIzZRlPv$+mKMdx(J7r^B$hAa!o5*p!jx7i<%lh7q^!T5o z`0xyZP>zY$8Aya9hyJ7Y@+Se}Dg$Dx1c+?&)#@>jM3R>&jy`nWZP4DBRCBUcqXP?z zv6Z0cNvY+xHs$}dVdkcU$F4}!aimFB*)Um4!ku-4639axf0H_1=lD({^Xmq{(G4@- zAOPN7FY}XM_ehK4gHt%*RMiwbHe0oE`F#cWXYi8+K@LK_L0B2@_>733z&q;{%V^7ziPiTKmpcAJWr$$EX8P;TS ztI+;7!LCL>vOI^Pg^%cZ5;leLRrvVI4X%xKt=u>*!+tM@J?&;#LMX%D6O-}JK{s}Q zt;=L-5+C9P>*P!L9q;6VtWBblj($cy@59wkqi$L#wT3eP3J03cvlPzb_jHwO683^IIeLIlX}@dJKtJgUgaK*-FYQ3C+w-3-*kM z^n3#sS48rTp{D?{>Xijh6U0|pTwdZ9yykxg1ND4leu4(+@OeyIWI_VlP*x?504!$tM1T- zRX$$7${D80HpwalBj(-6D&MnV*2?yMYKw<`Hg zs?ihU^N^d(E|&2LOkr#qACwW_{ZE5}STB3PN0&za<#wb{tMicz{NdkVJ@#SnO9)BF zp9Fqk-^1`LiCmvN=ONe6(Vw~B{$J3a$!=W@=uh_H@QFQw`txOG8J+y82{^Y%XOt>! zda;7>C<@Qa@zFb$;|!rHKm^9rm_@HTFO zKo7cc-LoDJY_Y2T$+;%*-~pg;w-;q9i(hM{DcBH3aCr^OkiOc#=My0$k?E&g2Q}F* zLdDy;g2k=ST(6S07-!RCrCJeAfyAz9@p zSM;1@3#_UsZo&%_32jLzVr^8;*d|Im!%cXiCd4WK1)_xA4^@kvoRY4x86BpP;)M8G zR8NT#D)^8~d?IKOY%AEp@6*xvTVfTR{o)xXFk&l8ShUL7b&<*)(p{b zMDHaaQTSiooOhAaG`L(6YJ0o~T()Sw4ic&KrG_D_}<= zqP58m6C%WlNLnsj&I)+-Tr>mSM`SLIx;1CrdUWJ zHC>@VVM~&km0D7a83v}^0gwiP=8@jZT=_6aqU*a=IRxV2g;z~y&X|$;Tr$cGGGpJ7 zsy()*P-{G8xiDDHC%tHXm(((FVXA1fIdXWsZ!N)yjDfTc@QlQ3s|uLwvAb>X7d7}%MyLNANYQbgta9nbw$@m zLYJlDPUX0p|5It-&#+V*dvbP{y9=|e%Ecs2Vn8@^;*%*IQ9|n(>$=v;Q>|dgmn;A| zqT6YkSM6BD1Gm~TUVpCI-?-IUR%H*8rdGT2iIi$L(sxvQj;Z#;c2xTq)y5`!bsq^Dj+SKjR)W>xm)W`2TDJ}_jB=7Gg{rdu=%Whsn z5-9T|an_RKVNF#X)6Y=lw@C|C9zn8LJ=XPdQ`WzwELqw}bS#MyMO0A)C$7uo*l`PM z&QO^MT*NGZC!GQczyB;HsK^s-Lz+}@54F_h{Y$2~3+|HcDOGd>hpEDkIx)!&@mGGB&k_YOtIdE|ErLT62z;NfT)^`0y$gN+V(4>`SIkIlhJQO{^4!TzKk zt5VcDAoBe68<8pRTRCW{qEzrpHv={=vbN)NaEcHLtiB7KaXa>#{mZaK8CJ=vk`vCD z#R#2|qc-mXuRHnv!|Tt|jE}pe4D_j#BLw7ra?wfUEc`uF>*-|TwUQ^3l|1xIl}w`& z%8dnu8yn<;CcE-K3)%>iD;gvrBYlHDeBlc(iHv}K1QTBna3USAV%-H3Z}X?G#{p)f z;6*`0*6%bORMfN~PV2iQ!ncAS_=;$(JY_hRRn`Ub?NTE}>j4 zB~0Ap>K-zuO9&(c*=b{-YXT968kI50rc(yVdYEKaAC@deWhA@OBs*}dmNxCMWSKOW zWM`OUZ%eY!tnxI>oKx8D?wY6|UmBb2wAZ!FspYbE@0R7>n^N}@f!NJLKKzjsdfh`N zu@SKAF#~r9PbazrF&l7k;b@U&lS|W{4}?3%_AoNNy)fweoKvYoIhYH_N52-tkpiU6 zZUO1PRKO3%cm-S?ecLPGpUDDtNZ8Lo@KU0HY0)KGz?#6VZtZVs=ARKRrfTL^(WfI$NPDH#y^=s~RMRrP{c5=AkNDm=QMBs`8HKM!#s#Eg(d*5o^p zMNzjqV>u?Eg#-wlj9G5U5|HE-4;^h5!1TaiRwc9r8v`e;t}5re2b)8$$`c*pvbn4` z#`Q^WX6x$WO)}_2IDa8~a{T2(2io}uJW}*ZXql;Wh+vRVPuz-V`?4iK!NRYjG`N)n z2sh;c{-MB$JeUBywWOd}mjV}484w>nPaYu&m>nZO{ks1sO?LP^S!|NMZIZoZk{v!z zHkxEhO|rj9GD8nLF%YS9Wn z2JvTV0j;9%Cs1`5tbih7^mgew#e$0^OvoxLga%|u0Q5uL*J!|@7H+QC7!!~!DWt2^U?b8n$kZ$!N`R)wF#&H&fY38DQRYyv& z>Pbx@?hvfXNWBD4m;|9jf&laoIsHP(eY=UAn2e+mUgR|T~I{bnad-b92=M1n3%=Oem(pHi$V5aJ+Q zx0%P|@>mmH#lxxYB+9z9lXu1A5AeHXXFUEAzmEL6a1yDM-^aYS;PhB6@hf?L$xptU z@!b_-Q$YO7Jip?5E9r0HeZvp&_zTcZF6@dw#%~tCmHfW{J|2IIIlO@1Kl$Ct?=yaK zhcKs$Xt!Y?+XZ?uNTTfEFla*gZpGP` zHu_@cBw6UgnR8L6h?AC~#ZK_|shU)@l@^ybY_uMc^HV+ik*abkU4A7v(Dyy`@#(c3 z8DM?hD=N-=I?C8*4oxYn7r6Q*mNZHFjr~MQ1<|SGah7#JPfR4>yt<6Hn5bj^WwI)p`ZpX^DtS*uOHnPyeQs5g`Mz%To$^=sWYj36xVXy)GlPw~ctWRb&r9Z>rr? z&T5L_A88+9(xHMzA2BmbyP4E9yXOwEp|Iit`1K13^o$;OX101W@1EW)V;8m6& zVCR0bJSFw^hh%=c0fNjEK*L~K$t?5582kaL;E z+)U7?`u+7CHQwPCmeOB$X%IA}nlgywgs;>;~lO+w)EId zNZ?F54zq^q_w*36h9o@o>>E}eZr>oJBZs`s=~SJxZ{Q;5Q<5eQxDpi;hmI*p{$glT zI`tVY*p>S}-%UV*oAV%z9%>_8SCLDm)qdIlUy7EAJ7#mSj@cV1@w1COsU$q-0U+W% z*e0}8UeOCxNl56{X}sR0uc|ZQ>mI(?Y{@0LRrNprM3X>|btOYWam=DC%Y2Fn#$Q=KQ`>MkhN|ScpU09vnNB#imG-laPSq7G zJK7D>DfpX|5t~XFeKOXnyq>qz(%PHSu9wmtq_k);2{g}0lczKGtPS#P{!Ss7n8?F@ z!6<7vhBGwp*s$!5Zf=0#H^_+n%>X;8rHasC*f$gqT|iR0oxBCnJIH%426_cM^bH={ z^~o7l%w_eGTe|Y@k%~oircZp3{z^tV5autEa2FzpXD#c&5sD_4Ks8gt_X@g$8 zSgL!*RCh_Dy7vr-kN`hn(RD~(6X(bHE59uiaSr2multj*Ckbmv-<2M#+bZ=*azOR% zm=}cTvsaGj!RcOH%-$?l>HS+*(#|RC14{Q?ZOl6)HbzV2tdkGxiB*%gywW61^IM@O z2`h`-IKKY+;+kUyUM|c3!M)F#^D!cKIU6`D1||5I-tU~~co~K~2K7uC)Td2%lx2A% z<^$DIdOvaWrk|wX9Mw09iC=%4$ZLirk*+D}UP(hnmliiG%bz+vFzB%AIk1}d1{E#& zi>ZEHbFcbi2#rn`YN?~`6)U%ZeSrNX({Qa+E%{<0L=UZGAG)V0DT%xiop zx2pV6dSRe1e!JLXm4wChXqi<1oT>h7syCt1E{vW*hM^j!Y`4q;cBVgZURb0?6ld%1%0!k1=6MzR^v+M08rp>7A|;cdLd=NJ>T;S~C(o!Wt4N{ymdjsA(0sR^c; z1nZ=0{vZjaHArxwNzhyp+$jm9O3&V}>QbpVY{3ndlE7@8OIT_Z9Zd;Cq=flWLdOOP zzQ+iZQNCG#yh9RvPa{)Hc*`WXUJ`^Q!CMUyykHVMEeY@lga2fosV50*;rCe|(FMGEoQyi7$r)5V)9B`H2?eSaeEtJfFvtV}4l6H$~P^~yV0|aJDTdKug z;}T<2#im$};6bF#CGzm_(C~(f1!z^R-^h$eJ>h2JLlQ3rDk>8v34U}#Qy~x{zOE!m z%vD0pljL+&LUn()1ay!9m-oC_0uo}0a|gwEs{6Jygwb3;3K~&rVpLo2#z^VJoNQu* z`QU)6=BY3-or#fkKRs|1g_frz_?=0xr-?vqj960|vz_1sH3ozg;Kh=Ab;zAnnneKH z2}w~y7H1)0F4SDWz^Fyf<lzF47s8+Ki$vv>C?7Mj${N1DKa{88KHL0mWnv0qt?VEl~C*gOgXM z$hlPl<;+qsPPXzc7}#5Cj*k|!p0R$(KB-;QUQRld%eYvcKQCi;4^w$%FHFoo3oNY9 zUzwu}9QJXEalZPu5Rv|TU71yqg>oXsiotrIKQvPcO!N;*8G&8| zoVKRGHlE$ZX9KIbzFixfWOiYMWw&P*!4<=Aw5o2Bms#mIU=qNa)Cr~L-U`m3VEXEO zJPf6LjTV9>M%4(?5Syj3o1Nvvidfvo1RvZlnKnDmxWVFTmDJz3!FRjCdu0eBd7rt# zx46MuP4N3}@B}w_tqES|2A8_QFPh*LZg8F(EG9CPy~qvj>ISQHAIW>x4Q{T%!O5h2 z#0~m>sE)>E1pUDc+G>K(3FggpgVvd#e1c}UK}$_gAA)}G20def+7t9!H|RbM3balR zhj{hPD|e%B(rAqgZYA93hF?m!c$}i|@2ig0*2ia1fm@Cu1^YVnR-KIE|C!;yuq;96 zo(2f3gySzIvkxw28V0w;nw6zlNk3~c0OgB0~b2&01Blzy);mhJ}4KGDm z)r&!94kC)D2O+hUdjyy4;4_>>pYNZ5OSbbD_;C$o&sZ!+(a})NdqYx$$1RZii^9dv z_$P2O@v6n8>06k?50G_ktPlNHI`pastkAzm>zkX=%cmY^O4ARZneyj7l44PUJ+MG5_K zBXaFx*>@?jMmWCi|0%&Xyd-krAvHU|lU38n=gJX~(6)mE*U0*6Eef^*D9lChs3ow|49b7p9WhIg6z0&bI)vHfQ&7mA~LyhxC>KnPbXi)mlvh?qz^TWZp$zizd zW?(Anch%w|drPdNaWlpV*y`Ak61W(#8LD0OOp)EaALAV^n{VS}q0Qa5cv{r;LAj}C ztY`CHKlS$X|q3nk9FrQQk1o5X1dSb>kDnE_RZc~ zK^m*-S|WUi+mR}vEL1CubzF#WXh9F zB;DRDw2h?ifT)#gg1t$2R#SUzXj}E{{U%{+saBAAE6DsGr6<17kF%x@DH?kH!$L4Z zKlV|_z(uwb+A<@wcNl>j$U2HyKHmxn7g{{Gf3N7v%FIfc)O-B{^=85Y%!HTQ>P+BB zZrmBLr?HtJ=T+foI-UlfX=cK0du1m4C^O+NysEik@qC>LJL=7ZecntMoH`TYW+v=+ z?xa3u!Z4W$2N~_saM|3b?AnXR)PtZ!tc$GU@&#Q3iNfbU!)#gT=d`b{@4+IwkFW2$ zMK(qvZRSt?nF)})TP8sLZA>`_y|Yi+@`ZGaI+U|2Kj2lj)Yx+^9PeIVP{?zV)wW`R zM7FIEA&y6dJ_@;+qA-VFc5Pvn*frsG^J*UQEZ3)9SjhV%c_-P}+QL$XHLbQ#xHnSC zUx}Djs|9OZ#oq{9z>od)(iY#~BtMcDz_1W88TM2*-R+gm<+JcZWY>uIG0ZUg;YafO zKJ029D{*-}pmQ+r#!ugQyKBZdL?@&Ep`RP!TFiW z+2-BAV_dYFQIYk{Fq@TtXE>&Y-+F!s2`(NOC&!0Evw#DS(VLwc$pEi7x+wgj+?mTE zv%#g|X*sFMZB<(*k{AMj5o zA0&%!tb<0@7VG{Adua+Y4;{xcNdN?3?50l za{Ybl`~|{H&tlt({`vf1yteq?nqQS|mllvwmjBI=q!RlbWyvFXZzH@Q_Bg{&hCM3& z+wRb5)S6*cy{7eS7Ow?^3Z+VYeZ?nx_0r*KGj9Ts`YjV7XQz`+Zbsv}Qv14IIZTBg z<*qjT!5EVkuJx(KiIKpDX|NjBb>W>?MLp{59n0i`jJyxX*oZBtd zyf7a)zuzcnB6-DBEsiMKPMlxD_0$tW(s|(EjNkghA+a$&BRp)c9N=FzKw-uoHeFb1 z2mYtN>(UU|*d2llll63q;J+eau0MYR-eA_V4Z{fdBa?DKx;nrH;DV>W=);g+KRoS-+i}FM9ldQ`7NF0ivZtg)2EftE^#EJ~#;qSZFKYPF8bauFK zd}vFdIcs&#wu5pS#~%qUbf)@9CuqH!Vz?|6SyUX3gsL-&@^@MD2D5~7C6FNd%=3J+ z55>3qz#)Q{B`udLi@V9KKVd!>SR-m;Cn^1^xH%oDL^?81rCWDB&4*6M zBbbfRE)>3)8Be{?(nJ25?HMd3)!_fl(LQDs2%If^ogLC?8(u2?c}8T=tK^~IGtW&V z>qQ!gEKbKm^irAG;v!UBK12#SkSZ$f@FYmOb;|n(anfCI&|##+8Ay1>LZv=7X?*b` zqcjtyj!xLybowQ%6oKCk)V_nehhkp;P1}nZ`3;!FR zHS*n({EWT94{%+J37}PV1CN-Mx_%VdN0dz9#OL{RddAu_!bRJltVL_H@bTCr6|TF) zE(UIuA-QIUX@)g#2HjF**Reh@L8`5`88x99e`7!JD($Y~>e4WKOE?NT-}{bC!>7t_ zcOJ%ii$5C(8h(46j1z@2lvzdJOOKR=Pbvx3vS;^IV0u~jw|hk~5;LjCNa}Y_kkrBU z#;e7d60}l-0zQ+fSb`RsRDU9sbLH`%3t2jVTV?k5Agt5&JuuAPh3^xYX9Sdge=r`a z06M@;iw0cfD&??#IXZy;kyj_L}mlpWh5y`paB=IiJHF+!!H(){0+**`X zu`egkF>=ng75fh$>Q(G(B@Sf^v)&CJO~%lFgtFpaJ|$zGW-17t;K#Wn-uPbXYv4Ts z*aX-s-&J5;NT2@hpjH{0@$a<2nG68}0#`iEs1_Nv&Z^u3guc+hCf1$r%A4}@v9==5 zlt9Lc^S`&|$@DaMHf)!$9^08zv90dX$c&pGDECjeM+iBTXJ!#+PHZLX{%}|*I#+Q8 zyA0gOX%FUV?K{vaulC^WVzhkThFyO99#O{I)wm3-p*AKL>+5dkS?tbS?PM;C`Xu^| z-V5~NQWrE4yW!1p1Rs6&y7+oL$wzY-2rh1lcIIi1evbX7H zW%2wu)}pqd{ig>m2<<aBvfU82y|q9q-c+(?4%YlbZSq z{r&u%{oxm-8ozxOa1_F+jmq)coj6mNWXDk7eW7~P4VZE!^{BGfJ?IOSeXL4{A}gk~ zqVr`N84#(nI&h70g%kYYGg*|+DfBlzC&%CKoMfG-i6@j$=ULR*Oz`M3P8WFH!9exJ zU4yLyIdBd38c;*+nMg*3KT@<}q2CgpTiQ)7+6Iqu@e1#ug=XK=Al0GxdjEv5LFP3x zM@D=uG7C!^zDMzSAHGEgu8C#(!=-CM-EabRSI{E1bi##&wsNaxHi4KJUd<&cMQh;6 zOP;=fJuGFDm4QX&;LoMcbNsXSC0W?NeQ*#P0^tW(dDB?LeE+Wa;{y<#6Pj#e=biJnSF8yQVNW1g4%bde`5Nv*WW5{Xov? zSi?_(AAgtc@A#h8Ny?fBYbHRE&t0F$ZdHwD$~I`dbr;;UFgVV0EcDUdd~oX=mwJc@ zuqQM~T7l((Z?Q~D?m%BfQnC8NqnT63adj7|aw>+LjtLygo_$VE+-C%m5n7$PK7gp$ z3n)n>zRsra(opt5X(0H})%XTh0O+k;rW9>SKG9kX6<+Hit=w~^j!e^HAzJ3-5`%3fW zymxdy%uL0aoQj%9EB1A<<_Mc7aW{U;eo;Uf9pP&y*l<7Q220wlR>QP=QW@etwp-|pbsiPiWqxpQoIY2fh zDcSgi5g3&(;uY+%VYe&tbr3uWP8Uh8Zeq-J4+@_H6=o3d>+1~yBw5Klgxp-26%CWa ztv4r)o7VC=Zt0O0JT+8s*=CW`Pk{cK&E3357GkS58P~`V87>&rRb|S_+Gn!B>^+57 zQ}uCPbpnITR-sE!2gW~=cTE-hPc%FY>L4wN7Yg!4n+oK@dFQ>C-N7UP>BbC}^6{uv z9qbj|g1DrMs_KLN>3Gm6&q-frPeKaFynF|TWoWx1Aa^d@ZQ8yuaI^;u0G!Eg9J}~$ zf`;*pUJuw5>-8zddAU;#zd+nI74sC8Q8DOA<>z+Kab?>!G)$Ba=5nT@@XJE|kP@tU zJIUgUuZ|rj{6OO?wJ*NT7dn(~-M*5vK&0gSIwj{5|9f&iKf4||@1G#&O3ckNXNY;t z!dIv$Erp`H9 zi^HM*jHb(;$s!yZ&#K=?*|VuP6-B1On(a(w&G!4!Yn3(I2Ww`Ih(aAi3X3XQvoTe7*Ldx$rEpU!_`m6+{oh z8hX8>y92t~i}KE@+$bssLxBW?#|lOn4Cd@HUQNJY26dN+_z)i0gY+PSH7$o8yXYN= zk_fZT-P&itHY7C|l`X3)`Q|1|hnXrIFQr=^ZD}SG#Sk?1W^#NLGt!2kcV}hb(w-cs z>i$+8;46AR@xHPb-}4n4kla7ewXg${`evuQRs&s>e95A^Q55Ug`RSqkuoKgPG_=3F zRh1>d6*HQ!%G^eG^V*bGbqf*+vQP~Smk$oD$gsy$7$fo5jCqQn_yvHDy}l*LL; zCsT~9R-%&7h05sAqHpkR_Aj`fqtx!(fM-Z8n3k|6DBmz&mI%X)Hp(~X5m6=ER;Ab~ zC3y#{@-x2OF6L%)jdXd4*Mx^~qALl5|I_LY>8b)X>8gv8EE-njvzkd3h2Vhb1AOS( z;nPjrQd~0G1JmELr^y16vo*PZ7!%&)B4SmJBWnVZaxGLAlOL0d$ zmeXE8i-YZIL)i>C|CMJsTCfJ}`4oIGF@nryRV%04LB`{V0t556pg%P@IkAP{a`Y4P7fUd8D~*b=n%*#irtD^1Q*4Cqv94p z#eeeVkK6((=7%Q@MoYqKonV<#F^28aGcvf}0=-xZ^6*CPw<)^f7Qsa^FjxX8u0A)q zi`SI-BdloK{|+D121tWcIQl=sM>bpK?ooROo>m1hx0Kljam30cF!b;9@1SIR4%|qA zSJyZXUvD@Gh?L2(Dn-#|n3!Py=rlewk%zCS$@0^`Wj0fV$Lv^@=V%^};jt=xd?#>q zkVc-Fl>*|yB+~9k1+G;~4@@R7}5634-!e_Gn`h?#xYS=G1q&u)0 zKiKJAs?EaSv0h30A>A*L^DwR$_k)6~l3!7H+Tfz_q(KSAaNo742xBKEqUO`r55i`-)=WwKk`+?#H`_f&rIW=n(+tTkvWluPTpG@z zH!-t=46st^FZ%!VtWeYpoa>_HO;MvPHA0B15E&NQe?qXGC_{p+b(*S_6FZ4ovP>-= zvd@-^3`hSD9olg#HA>OTk3|A~kX|kX`K}L>1Uc~29KA^UE=Nye$Txf=tcmazICmqLlyZv+3elXhp})oWE0);V*1@ff~^H#^-@YD6!;n9Rd^IY(`hC8a!g3!j*3I4$-2{NT0v-Nf1d zp2Uv`Uaa3uoiFseG29Q=RKs^D7d)QnjrW#)@fHT>d z@VZZHrZ9S)uNCC+xn=CXhp!%wMNVO?i^spzmc-w1C%Y9{Map7S=J!_P*w<(Mp(dxM z0?pLPQgW#cgJ^H|!M5L2hyE94S3-qtWLlQ?-HY@3lKfRzMzKM(N>t`I)2im7y)6S> znEau=rw6W*96FWf#Ew?^1EOH?-Hm`%gaRnJ;#{C?;?CYL`X1}fb2Z$Z|Dy7-?iwyZ z-1HfI!?~B~9}d0|?N5;G0lM%i2|+4WIxQv$c=twgdsVe~Q&3?XXFozV(}EqXv$|Y6fsvzHc2{G@|O4Kh(F0DV5@XJzj4=TwRtQwj|Jv2-#jYxF&cx5(c}4*^DE?soyF)Je%V&r<^bi4I90rK<0pPf z*77W}7}!Q)S`K7A1oFj%x_sP7OfW{G@c zbZ!y#61<8Z+>*TnyLKR3xBH8^f(?n`BasP-?Pl~3vMrTANc0cWG8w!WGhmN%wJQg% z-z`c4GVe;jGJo+Dfm%#<_rWzKpmD9&{A{YIPWo&#rFcrKC5=CvPkOPZtlH?a9;hjn zV|f1{LO$=SHnx^E#g&9UKxl%fRcVqyL23BroYH)Go}NdE(WeA{s0ZXB>>s3PP^9J-#j8c{ zBIXutGkP6vVqqjhx)7_wJ^wa>a<`SJmOrK%h@-y%^d0_+{PA05H7Ubgg`LTa_14^% zNSG4LU_)Y^19|E=yz&5YO@d5+%?G!O4Ti{Cm0S5>qZZ_euL$jLXU+MNCX|Frd*z5( zxbRCQ6VAIYtu+0M_%^|yHMml#7p>FbAuZo%?<*_v5tDwTL_exBt7;-hEvmc9wt$#q ziId9GU_q!Le{CPMq;Q&^mUP-fFtw$YC}*H=b!&Tw36QY++nrIzQnYz}y{q)Mve7@Q zEFCtfQDtcqCOu`4#>9VikkqO*b)?FcV!@;GqfnRVLzG5CgTh%lzdu8Je(o{!jC^1I zp+|qfleFlKZrYagQHFKfZp2JU_0R^x2-j1pWUTU(D#NrtQj{u7S-9NxpUSd_s%8zo zagBob(f^>T+2F?hq^boA5=JVtw{0K`GFZiZ32u{woJ8yROf>0{64Rs-7uc^)OdZb@ z{psY``|m8Qp+4Wnk~#Pw!(?%o=> z0xo#XT3fLnWZDG-10#Zsx6B1a{W+Nw7hNMIFFHr`I)UkID+b1?VrK_sNdw$|o=2*R zn?%9FrL!CN=P0DJ_ItEmksb6=4}$R>xK5%TA{pXV{yKw)ht5m3~*%(PUB;vg^Ix zy6r<*-IFvwswW)Q=2WbB6fW4B^Tz~)Ou2dq_h9wu^+yZqwwY3na694g!j4Yw)x_EN zhU#3bsu#$rVNIRSHSBDw>Jcw2!`Yx=Be{^%3(Is~)v)ti=DL~lcm2M|Wv-h$f7b7E zm$}YzBKln%JVQY0u4nR$>9`0W9qsEN0r~ELye+-S47r&R2rX5ejaAi=Kr<4erimU! zaIJCTtHUw(G%3hz zz%<-@Qjt-FMSw@j8M3E+nhJ_FP%gQ`P!|h-&S9HSkmfU`enZ@TZWC&|u$o+imN{>g zSOY~nkS*B@(M&!CA1nVrPne!#eXZg{y7P|h)Zw-I*kuW7@?R(=?qV84{1Zxvn){%h zk&XkF%cD&kyF=%1lBA(h;=-3wsb*L`JHQnCg$-~G*h}pvgc!uOp_7!;RUgoXFxwO3 zdCJdw{|E02-0;~9z8BxvvpeziIEwOOdmVRG`Rq5bwR9fijGeG9B!RKn<9zcJr}gzh zISed0Oz4c?Oy`h*LrJj2n}xR-*H3wyb|Ra0BTTmME<(7Gjf}W6OY9x!;vHYPJeLa7 z$wnT&-wwOQL3gejgyTXZTE@-T_+5kSWVpah0XW@LQLgKYQhO_mm$Qgnc!8`;@X0{7 z@_2=ey9sv(ApJvKmYoCLYaud_5WhW=P2|na7duoe&yLA;QNp0@aBAPu-5ZJR59o;v zmc;1P*N?dRBjFX#XIBa{ZjVWGbU7%2NzllPHpP^Y<24@mZB8v}KFsrnoR zY>EQ5TL6nYCn{i+v)Q@lOEoA;aE=Z26X%T)crG$$-q2ZTFD?@W9Pr@$LM~O*4S6&P z0?Zg0E22^u*NVO#i>;uk+TzTwOxs{5qb1zG%U>^znVKPl%U7t7M{E52pRStO9vvNYNUO2aR! zgiKf607l4|$Yn-myhX5nV7)<$auXYj)s8Hb&i6#n+R!venTCWzy5dkov)VMs8ss4~ ziOAZKV>R9tIBV5Z&4eUG%i7SvL@`rx6|}RoGZY0hKY$}-o6aQo!s^Y z2sx(WDY^A?^i1Z+CC7OwIdV;o7spebWa=f=sO0wbOE2TM5( zP^)ViWdXxn5BA!SVkUw9$gPu{ws5i{?Fg39-Xq}+=Iz}dcP`pP9cUB?mwzP5b(6GX zAsA|6B}o3&hQ4>-LQ5}^w?KRU?3H6BgDAIgqw&RC`%~-(6tV9i9n$VMv$uZU_nHj* zE>Yjgmvn-?w&shh(6{OK{eHe`=T!4o8h}{A0e<{bbhJ^c`t7^t6ZovyIEiJ;U3H`p z(WARAuuLG;C1Y01A~`rHorW}uoPX)K+Bt(=h->GFDnttJ-TB==9$2ZPxKcOp6Z_Q) z?*fw8yI#36=bE&h2H0t#wxqa;Z&3pPQ!ex;C#FAx~rb3upkA6TDrZe_i) z9|?tz9aAEY`VY`qI2Y?U=oS@6!we3!z!E_Qf`e(j6^m+WqFJGD((SNfQ7C~$a!)x7 zZKTb}V`U)b3KseHyry^rSy^iEC?04Is;5oa4;!1n7FC=HwrJnT-G5kZuQ$@T^OA_o zkW~@kgfSDVGx{ag6jnrf>&nk`IcdOvrRY=D|B%l+B#$au8HNZpqZDgZ{;QF;Z*AE2 zE&pZPM<0{Y_En9xJ!5j?ZI?T61vRiFbQzZiMFBv|#nM_@F8VpS=$%I2_^CA7Y1rsS z^?j~}tux3v_d^<6Y_Im&KfucgLWq5AG$apRpE8t;nQj9v_ip%`DwQA)Z)P=r&b)y^d0+fryoA+jXQw$O~vq+ubDlzH`hP<=TXjz9jK=Yz?)t_%gEoN_ zx;dpHPX+6Pw7&}xP>%X!B6>1XwdY1IA^4f~P3*%}zOpLi7>3V&LuDN9Og`W&MNqBR z(*K!Cc0V$urHxa`PpH z11qvtaHF;kE+hNBayT6IVacvV%&phv|CtP)ASVdCRZ+=c8Gbym!AmTREm;_M{BD1o z@W$qTJ!0q*LjRhd8|O3dihkHQC-{k9!f$qSWz=6ooUPXb|1)3EuVYFNG>+fSwLdfJ zLiF5~G0j~W8^`Z+cQ?}RBz}AAp-$1$8+gXDUT(9l_God@F zLrU8lCv;z4(|Fq(5;~L!&^c&bS)DWYuzpBc>!N2snsoeA0>zn$k#7_1ytqpDhlk^9 z$a!dSTRGNk9ID}d6}#psVtzX=I-8gV{Cjw`7$XIyNkM8z6Pm?Q>tAuGrX@_w|8%HE z=~-j?0o7OO_?q+K{%~gTX-O9?oC`0)DiyyEg;l#>$zkZ16!6C?+F;*z9wIxpva(+> zEw`-i9$hH(*5BA5x~vF=?u1nnYb!IylWym7Uuad+)s>mz`RG;OtHQb#*`LB9m!z*Q z>09fwKPpCMpI#D*cbj^X*viQ95w&i>`}{Y0ylM76J*?8#SV-*h7wB0PjtQBohCg|( zp?$9x)8Q}lnn|T41G8HO+yFKZ-O_ML=pdTdqo9%7WYZvg>%cTbUx zBqN*7Y&Z{Pe*>%Q^Ep=Rvp0>59MeB^;L4f3W0SoUBz8KHh*;7MBb}RQ*4fnOj||K8 z&#sm6DzcH2b{E^L9OosD#o~%(V6t{{&+7KegX?yA^hiLhH=-vu7||Z}M-iQRH6+z->-zRy;AM6OmCau7B5Q`Dfu$zQwF0+-ByWq(_W)hoA3q+P`?% z{FHqG*7_$Z6y@p}dm z2nakuK=BR`m3W4QutW|skbxP^D2gZ^L0s@&6=8;;5D3nU@HmdLF1oruUGdt*YsCxk zPPh^jF`y!f%Hj1qjwfokl=*+F`@R&DC6781q zfwQR*-403M&LM#Y;Y>NqHVa?rX*g$$V^)VRQRuAj&_f?T2IY>=*?)*7J5zfm98ZIA zk&{yqAH+~_55}P~W<%#mV6Nkx;=g<+#e=7$g&y)ck_S0i&1;2d)m8dSjC8V$^l9fR z`HLAinBcXVSm?xZFm6{*?8M7HcwBe(plUgEF%4evgig9$CrL!Hu8--YnL0@#iaqzJ zPP$SjNklQrsd%nV62PyZV;Rc`l@#=cXAEd#+%ETVWR4|MAU`M#Ii^68rJ*+>63WvV zL5UhcYTU^!RDaNc;7gt)ldEt2h*;U;f9Wopo#bYjvUu5JPV$RR@{1$`O$X!lr{tRx zapPR}1iTccKSCSvq0ik2faw?#TSTREqf`AxX*D}*uXVALbun2IBq|k%V#Umt>Hlb& zl);v~B^9+rR+(xd79i!%Jx!3n1@In1jjW*#K`H{@PJ|;YSQc+Sc zsdTLBwH?pNSOcgg#Y#TxpHRfYAH+);LpIkOMr^b;Q?3yI*1E0rOW`rDmJf_MHB8y^ zA>Y;bRp>OoWH)Rrkcd(p-k7tkzTV{`R#Y9rNHjpnhAo918c^F787?C-goLHoEV$Ug)371-)TQPu*;xyD8u0ntXr=H@_G+NhDO`g^_J8Yp5?U zUTD_+)w|T?iF8^=lb6t>En#tU<-?S}Fz=ekmSM)l3fsI>LN55s=g09;6`MWC%kzj; zv47PuAJ`oc3rw_i6X(w4x6h1Dr&MB~*Ce(RkplDAJc2o+$Ue#$%80CKF9f<`=}(!` zY{?SI!)tCQ?)L!V;&_?Cv=RFlDtN-%Dk|A^%&2UiFY|{|^P4L3)yaI*@|s!wE`e0) zs?1C0Ii|3I*mXq`Rosd45GeVs-zG27*jj*U>HB1h#<0m+_i!dCgD6Ty_*&Jhoh3^G7ebF2)>)kspZ7Ebd$7PEdWm-@FJTvsm?F z!1baUEGJkNCNpS)l!PZs6$#Ia(If)fsWsP`=x1>izfpn5mpM_Iu+&JKLp5x&Gy^md0Cd1iWTU2MZVfc zkd7q#r&RBDClV^n>NSKX@Y3)le3LN=>rs#Qd0vsPwx9Hj{w*=%kMZ`m@mj-*g= zczGRbqT{dupeb>PdJJytxv+%#Hol!?fJ47Z2pOdR@mD zzLu84Z**zcmS-+EH@;(aXrAZ2mp}=|lKLy_AE#V{)iJM4{H(pklJ&kT*FXN)W5@aC zEK?s1Ulp;1gd2AEYncyI@w~IsQ@7<*PhC@8<-E!=9%@@fp09j+XdBTlE6t7ml9gh$ z6_+T7-i0Wh=LgL1{3Scgz?!OwKLp&IlNG~3*;psmlRQj2SKa|9S-6}|66%=dsWdlv zQ4+e>5^yLa41>v^2U0&?cub1(9YM{2o72woQW>4C*WBhYH%YgN{P{#WnJ2{NFb=WU zBzoG|d%qxXbW7Y2pu?peJp+}Ce!QP2;gdVc8 zX(GD>Z+$4!xNIx^nMHCe0zu9}x2yHb|45`5SqK4mWNke!+L>2^s1!fFbuda>6-ggT z6$IL630)I!V=y%4cfaLvziAI6iEKsQf6(7Ss>?n`eQ%a(aC=E|oC)Ij3;!fry?3GA zCR0Xd3Ong&M7VvUO*x&xoAU4RImIz~kgnilF1wNM(ncj$6F4wbqAO2a3`?#We9e4g zE(g7&*IXmovll21Kd6eSjR_eq7(h+QR9l@Nmctz}N%NomhfqXf80=zy@P2=Z20(C> z!#sx5jauH;UmlR7WniAnucz+J2?Qh=fu>_LnvQK1bm}#?GLe3BO&YhGcfw9K&FTWl zyGIpy+#iT){F|8f36D8yfY-zbJD%SMxP;4Tr{%+xRSi1kueb=Y7Cr)*m>Y+$$~)eb zxLiQ9F1k%_9}^d-A7|2`lbjat9fjq%@Rn0K$5tQ+FZ^>i+_0AX zxwMVA%<0VS&53!I@uayfC2D>;BmflY~m=dBoKen-#AZKS0w*{N+{Lu^cH# zBDW?C?u)(V=ic&fjj)^_2u$WJ`HApDUiVMKjTbsiWRw1yJ_#+x7gd?n3#;6leK^`kj93D9RGDuE%HK9_enR?=jh$!t9u_5|pU{`e z>xQ_u@gtJ2aCVSb$HW6QB7*^8KD*sK5nF=z2d?m%UkGqFO(elSob1cy2i$x5tqzpG zIkh`bbUKP~t8WyAm)*Y@E8#<@bclPJaW69Ns1NTNTooJJ9Gc+c?VJ{VNxZAm;+;;Z zA~r^HSrsvsiJhlRN&V(}PfLA8C}1xER$?6|t$QDmfT-D!j?ID;5Y1|VNaAUF5f>_H zc-U$2?!#yqA%@0+*8-x)_-no2y}gS2)&XupLWio2C2a`i1S+p=<8^CxiD$}LL6w!P{)>SNLlcha78(g;RXR_LVN zKfe#1vN^o$ z9UeWcY3DN@S~q2#*k(ko1T(}v!y2;Dy7wamgb_Vg5;<)@PEZqj6hCzP>E*`#7h%I0 zLF3%-Wn#jFvU;!5c=i`r1bdQ^(i_-D(dH4LNUquGp=2#_n0E1Pc1{q2pDRT#_|91jV?MCinf_IAFR^oP%FLeAz8Ar;nIeImd zMR!a$nrvYb6~>Ys;k~%NiwaJItAtFq#2j+M&6KMa=JCJFxJ^_BV0QZzS`X9&O`u{s zj-&_vs*|4ANn*3L&MEbvN-`oxP&pZsmTWnLWURqkxeinH+TW*JD$qO%zu)H2vc!{jL0@*8%I0#r&>HrgpPAcZ_z>7 zUXpSuC|gHmT9JjgNTY&FYoqQ^?I{&0_T^6F=)7{ zv$f<#^FbonUo@Z4`2@GQ0FF^AQDcjco2KH=^q)rGZ8Z$*{hJl&7532r-+qJS*zL$_ zZQq4{W*!(ZLw8rjE*=z9j7tPm4-s$7sBEKNQBDl<~*L6!~Kp_HFboke|MicTkIp1(9e`T=$6GRo``FP@nmx5pR;?a(GWg%{6jgb}>hu#r_g(;>2};(3GoNrY-|U3l+_HN!pT_f=+moJ@Ya z^ZgxNt}}V4D&WSbMkRWZTat>HQVJUMQj0irEL9-dkFfT`dxxM@j(Han{nm(xsNU#Z z!~aNpM&xM@{HiFAPKZj&h-x3Dz(R9vC3o6F9*8MR!?lFp>By_+i;VELR2|({pbq;a zRy?MEfra)$TCRw+80Nuz@wAMPlTV1#>exj_SSaNbqS#fbSmauxOQP1{puj&kjh51i2em?o}mF= z&eJC&0dvje+p&RHJo*tAH*hZsq6Jnv?d?JM7rJtmltPKxw^)m!3lj1ZF!VB{YXE(9 znFdtwCXfhlDLe(h)8n#~0NfTnpeCSxS9s^IHN~p++(D%VtVZ-8EH^bNk`e4XqHg9; z6oWB!%8B3T@F3cgOh0>jXkOC*z!B@V~2zLe&r&Eo^OY7~Sbi#tE z9O@p@$A~5XQ4d=7C63MIySaO2;1oG$C{HX@)P1oTIR+KpQy}(5IlmLdby1!S-neNO zX{uz~_!MNpy&b0*a@Q>WacJ~5k&E7Az6tWSD z&C`?l7Io@&^)jL>q;ekrr7&Sc&X<7*HZYA2EDar|npLn(G5WPgPTLdZgb`j#W6(=K z$}7T%e91SZb>w)riSN$3;B^Way-$!P*hupqxKsCbf{Kh-S7H2hRo$}Yy4@X7iB&{4 z7*Vw^$49$;gU2qK+5vylqY&_?tdO6^O(A-*#|vGUtNPW~5nA)kWx@%Q_2Iqw6~>*b zDk9qj$k1?EYEy^!VlN0C`{Ui)p=ln;+3p|?HNmU+*6BbGQ6Rm8nnD-BGYa7ue$GzR znz9kpElP|58!Tdb7<-d~kh8m{)0(q0lOf|%8Vik2hk}jhDFW!J{O2{BtK7}Q>xQ&* z1y9yZLi)>I*Q9T6K?#~7HqLTtz3rt(6&O+Qbs%>l!(fL2QNHk89>VbRm@dytr<$-9D?7=Y9_>>(`7nlO;yAMF=UQw_;_(B@2 z<7vtcP?#yJgp5}sU?x_tKUi}l__9GmFCP%|(%L`UH z`mMJ);EIFVPAbc2q%bPGqj`k||NQXQEpv9&ksK<<9HkDi@O)nV8)>{=d`V*1W%EtA zwz_h#kEGqp<|FEfX!hBx%cfKdZz56ZdT~XnpG@6fD zN0xqfPwMfJ6s`9m=%~+zK1Ln;r9y&0WA!nF@1yyl1K0rmI;g6kOlL z(P@?hpT;6n)qP0U{Y5DbL|)N>PACx?ydOm=QGBu$r9G{8kO}Ggm2`czEzDPb$fCFK znmZX$TD5l$)^GNsq@NidaPk@PPHgZ5KW9U&Vz&y7O3~r!!~nJ*D1%*SfpAF_$6og? zu8|yKQ*#daA=X_J5Q$c8O)>Ag_AqKWb=6kUQC6H{&C%0UmLgV7GZ&K3Z?)4YdPt#< zHv6WDWM}t+0|12svoz>0|5&l)oBOFMKtulye5%T~tp4og?YJ6Ht8qEv%WneF+f2!ku#Am>8BqN8sLMgBiFNu=m%A1~{ul=JBVOG8?%X!*Oa1 z8aI#|8>>q0Mlw?)y=Kn{)6wn^+3>2uU%-YOB==jb;Z6Jl0U9la)&EBgZz6edTu1!|ZGHblH4S|K~yP z|J&abEFn{6@J>m8Po@3e{+#wZF!jVLSM(d{c(}JR%(U`JbiJb;dk&7vJD7f+xA4aP zDFlsQA%Biw^=d3pp-5ld4 zDZN^i4z`Zvi*?IWw<^Q0>2Wy;ocPkqJT5y(sRuLt|CariGM$pvuhy_U=#RZ7Vuh%8 zF3gV7u57?!w<;U3pr>wnhqj@?fta!<6EhO$eVQt;qYwJ%VdbDd#xvGA=Ra1X&-r@) z{qGIPZd&^PivihIq|N;skUdQf24rGvl)Fh3&Eg=-h9^qtvn8{AAAzKCWjt`yaWWSur{$8@*2lLcE)XBa# zlf6~4<;sQZ_|3ZS+D!I($-YNtKkHPTB=q#dBZb#FG$`c{9z z_qgPF$!nRd^Ezho?w7n0@_IV;zHQnI6XK}1prK)F9=(3^CI*zeF`K^-z+x2zD2RxD z*Xn)NwEggCU#z__%nq8@x%OEj;azQ}6ryo!l3~(KbcWzMrXP8Z)-!GRP6W?HD7*O_ zfwobZ_pSBt*3%e3Lj)6+YPMk0&Pkm;|8=!S;$;_6+$!Cth!H`ABJh!M+f>-AiDIKA z(IbZ+NRayS?}JB(+&<7#x3eht4gVhyG(2_n+xfO<>c@$;IBg;~1S`U?`+ygK+ov8z zNa+rPhY@(1sI5EjkU;v8hb}azd$FRGw*}m*DoeHtAYVbQS(c2W!$U{kbPNjaQMz(w zNah%ZRLzMCgo>K`eMQZitqa~`FNl{lIX^dTQ2UfJ)}^zp8ubLJlLvJ$a$+tN&_NC= z#0^UUjO`Nn0<@aa^d%s6fHCzBo0^_u!J0jk|3EuaA@gv&ARsddvmVvWir*qtLQmQq z_F0ckJOrK{8E7{1CIc7jX?`r~=kliD5gbFh)a@<`e)lrHB{?_*C)6#I=T^Y~x%wi3 zSLT+Weh3cZoxe~;hH8Q*$}7I{vN2~XlCerwDQQs^xKpb z@+(uc8QV|m%awc4lvb-lPL7vpCDMoCrt*A6a84c?3d@IeLYnR zQ6OJITAL-L?NxnJD`?WtXyS}fX|7cxA?kEf-MH13XTJ+V=LKTZ2td^m`ktGj(c$U5 z1kMX@(?_EnIUAJ7Q?F%5t}Z6T)@Q!hi$eFO#|8|r1_P_Cq?0H^z{LWB;w&PM=0h9p zLJ~!PJnhzBRiO?}Lqp$dQVn6YTSzNIte;otRxBnjtCi3v_FFQYnfY>Vw4C6dD!gST zo{Mwe;}|;*+}GG~{L`W*-J`<2tluc~ms0Te=FvONTXIYt%d#pwkW=B<(A!)Wrk;t= zRnvEAx$vs%xY!F7phaE6#m?$m8Pn1c4h?d=CQ$ua>^pd3%up8~uW|U`YsHQ(eeva2_K`YwVB|60@CQ%97g0Pl0Yp~DS#qmU=Or+v+e+P#NbN}loJLd_n*9m*(yhoL9e8qD5w%ffsCP8zq29U7Tl9toeRUiA zA<9S#!9JOi8Kih#lo?U|iJh=x=yqNDtL|Iugfq2U_p{ysx@4U#mq(m_snwg9cnu@t zo#G7MWgnSNKUUI5#XFfC?dod-H!?>ru((t{pW24W!U1+aTOXS@2u2$w6t&`_lv{DAb<@MXw`8%0EneChTbMq(i zZ)=g4XdIU#{LhsR94p9RabJ%Lt}d+Vz*h37__(;glenAlve?tGs5TKTN)E&`TGh%p}gIjTy&;<@8l zz2!Nru#7f6Mgouns(NxpJed{Xa6J_~Eq8NU-rB$2FOiN=ito`ojG871jjw`}@i>~{ z#e5AvS$z67SQ~Frm=ZH8C;nP+0Pe6=CErWOAH* zhOarKUKZ1-iG$NP=2cRSWTUE~-)i0{QXEQHS$aEO-fF((w(n}V3bW3;6GX%sFF)dC z=bf(1>&L2(6Q8>Q=Y&o2Ko~SJshF5YlvFma;hDt+?$B3$Ol8%!wneTDcfO`J%wm;_ z`rZ0cCroh?zO^=}gy1uYYf|sp6G5LjpyzF;sh&N$%PyJzR>>Ugx4(J1dCpWoA^p|p z=9ec_bv>{s)fvMPt>`z?{$=}ZAF^ID9kMRgP4e>G%;IqpqjePuRb|N|GT?z}evV}` z=t&0u<&TZ(DGTOXcHXujVgyVtb%pR5ykfLpJt&DZw!GqOENL4?4*r=pKZ4(jprk|eW>aPE{+gEmxlg^Dh3|{y=O?++Bp@)uT%|8c zJ+CrOMg{?k1*fy@;+T`_F~0Mvl0YIZG6hq;FrGRBFn#e?MR)YtBpSwN4A=dF*f8 z^?v&6xu5>zu=*x9laS#Nss3J8zeA{5NA>lYY6OYynC#0?(mjd-kse*Jf?41dhKpY`F3jbnY(^5jn0 zEssI-opo!UOlO*kaSq6L zrt}X6w>j7w1FsWg*uu_4UnUEeI}|(gI*{?*@P}{~Q)^UR<5HGtL-zutKCm*PJ7DzaYcv(oLDySrN7OB#C zEPutGG+?1YxX7Z%lckllGsq}j;|@qZZ*G^WcWb6zu*vFwV{89LwVy~lp1mCV-=}w> z>GNLNn^eal@D;&v13-r^ck%|4C&{1bWbLHZVih~da_dLwN1W1~on$cuBzb|8+=6eI zG;+UAzS&9M>?F_A$$|_4r%5FTLo^^*lceW$(w|7W*hzX&C!s7DI@U?LT_+78=`<&4 zrcM$i$1o@9N}Ysj)X)J=(zzt{+XRFQu$6Znmx=&XD;1--feHtc7)I-n0}2|{Y1N3n zM@dlXH-APE-DJ(Rl%m)&2s|k?sG{XaP``M7-W2?J22otw2T#R^M}^zfPpBJ67>yOB z(y>H`Pk&8XkwOW(gabSF=-e*E#x^0os;$J5M@{^VD zuwBTHT+dT1gj_F7w0Fuh4JffC36_W!QjP*Z% z*H(^2Dy*N}{v+&(4ai1I@E}F*#-SyF*<@|A^(5}Ts)^QuU5cOQz{h;`tu(#wV)d{X zZ$2?u6)3GQ1WK7k;UqGRN>P;^)FcqAkteUg;! z#?AxnSaM4s78(tMFd}EGt%%U0W!vyMwNCoyUK|Ifv2lE9^_K7ArE5gKV8%$IRHKwC z(50@BQdPRtWJ-w^o9A68Q*0u-AY07&{dA;kG!Icw9t?G$G+SQX*N6!BrfN?*cvzDIgwrJUJjx@;`>xfIQ0IYo ztC$?Sg>kB0jYuLrEyoCKe|uPuSbC1}T?2KH^ejFvILK5;^mFwuQ;EEu6&2KEnsq8uF~#4UcptR?^h?Rg(kd>uKp0pR|iy zR)T}Ev-ffwr+My+6l$Ue@xva^dX-JpdMC&iJyG$pi@Vd+)4HZS2p});^3*3=*um1p z-^kHpwuVx0p86SDnwqZZsv@@ElrNC-HP|#k;>ZUry!X%)?3I!K$kt2NIvYwsg@v@x z60)JbrLtpA^wiBd07~3*%&UC@9Zs!es(2b0m?ZH?JV+4wV$D)Nn3(kfe)g z4&*knI*gxctQQEw;4B&QmdYNnK>WT&>hYfnx4{xD z_{Z?hd=;{P6UqqvatOArBNP%20|~){bf3pVm5zO#_3{IIlY#PIL%nRGZzkvK0*Y)O zkQ|o?-eGG(>210ZgIG* z;4^8F3#9$vql_F~@aDl3JVq7NeDKh8*2`VVI)Ig5xc&fwC-OH6|C%TGY~b%23KC5-S;O~FC?>yCfATw9-y_$X?{H{B)ot}9 z4i{VOBZVSSj@_cS*X8G{P^3d)k6UE>AMsbcul5nSLqhwa=J+rzGrYG`;7(dYZ((c2 z>Zo>$b@rvOe5HIC@3~l_!`100jd3rFT8H@*) zz5>&`Mo?VPS+&McaIxAZ*XNeCo10>s^gKBG!Mu{zDGZT=4`NXzXR5wDOC}7tD_ZWp z3Bruf`nXBElL;QEUxA74>>}%s_PdE!(as;^ZKRKh74C@*1cBBaj{}3*R+?;Y@-ZO0O=5Q`|FFaf*r*r#R$xM!}>Uqd5f!*lbSmIHtEk^WAQi zS725-%;IQ-dAZii5KJq60lW^RvLfWblmq0|%2*?5Rc~Sxz}q7Y@7o8ohSxqv!HbqV zv?VsiC%~+VYmkfg3-V6LSxQZaDv)#GROruLZ)yD%?B%@_Ps;+EKzE+k1-C|2jQJJl zzwq*obw2^y^>ODApw2z+^w&^di=k(;V3+6urHS?s(}s?E0WJF)dgKtqt_nY7q5W{H z9@HN=303l7N#h8IXTZPpl*lV^hcUQHQ=ZlO(wZHS0XC{RX}11yvCIj&_#6fDe#YzY z7r9c%&Lo_~X9R|54Y<-j zi3~cx;iVdm-1Q@z#>7LxQ2JB%d}N@%XC>Zi|x_^0I%V(1KEsQNb87rqf*Sim*x zfx*s+vX^Dkk{`G@g=ZDIf_;3kk;s*uf9}gHqd5_~1mYbM_3(Y`<$o!*)gkRs2IZlr z6xGy$JRaO8)Jd!jUDl@qVSp%bd;HBS^Z&NqweMRXoOot2-S;8SeUAvPJCvcVHAnKe z{SZEEefBptYN;Lq1}P#}P}X>gkXGf_X{w{soN63Tc9PHaBF;Z`hH^!u_Z;gq10ANy)O0yN=1@-m}9w8lhoCb zZgj1f%-;n5M)Oy}Unze@{1u`>5!Bbf!hGRnMNEc9^uwcti3G>=OUfB(w}l5bq_`K{M?k!YfmK?Da*R7vYSPB5UPC%mkZt+0h%CapDh#Hk37 zJMxs)zbX9w!Y(2BJ=4K2lLj$=A@WWRhM&7I7sJn8oC|NTPGR^ylGRp}!Eo++ah8&z zVwvBWPXDR-6=m7&Ua>q)s0t z|F#bR&tH%`{O8B#4nL#;_y@_^!%sb5n&J1k9M0ERuUK3s0n;jEetdVU2#NdbzxqXR z)LdK^R>iLCS%regx}P^PJhAf%lt#*27hatg-qqQN%Ke6qCuTXSe;vx>dC$pabNKtt zvC~V1--X=+c4&ZtGjt*!eO%6q)vaLJYu%1)VqZ>bO9R6cV;9wy5tYLl?Fqbl`ibvYx{W!y zjZ|=qkAw!}J?{T&7E|rOUk**f*OLfMXC=Z=`BQgGyn3F!Pug2wb zm1E$qukR=I!|aHb6cGxSThr?y0%oEhT>p(+zrP1k6t4qymKSIe%W0=j+Ub^|f2X}* zX>MCT8#!%#klofUgsntlW$e;-6|pr`7Ht*37r1d^rL@q$AF6W_4j?L<8 zUHbDb!dmznNA?te`RuCz=jliFxsQmFUT^zfpX1VOL|OXAMwKoq44tI*>tVjw85QDA zUl#KdVZXDvEWE9#!y70}7FC3fj_?A5)sw*`@+}Wj?^p!dA8@1SJH&Y!!~a(Ihw(C0UYx~L;rs68KjUBRo>{S}Iz<(0AFr37Dv=vjlDbGKUu zLNtU1W}vISckf852n!9jiF2sIU{sj?oO#Py!wYs*5>#t!v*&p?Yul6%Z1xID<;qj3@ER!97bzxkHfM3R6>Pw8v=LO+ zomq+qKyV9r%PxNA5VY{~kggXB1rUx`>HFVQTAc%Uptt;@;=V>yu5u+14>HeI5idTp zh1F%9k3|XJ)tLJTsR(4>cH$$YgjY=A3+psADt1)bL9W7jE^-6k!JTqRW7QU)%3<#4 zn3kzD#vlD&A3lmhmt(h3hhA{Va?%G1hRc0}q!#12ZTC`%O1pHFM= zqhhcRr0`|)k$^#3WLF;RiLhF3e{$uJ>X0o#a;ig^me#fz7W%>;UP*wDq!-x_~Rz6StMjZ7MDU-sWdyWP-H_OgMa@G%0_Ss6|2jDOE&jm)K{Vc z_m@`|7aGy=e3jM~vXJeoDJvu_BUh3so#%-P^g!B`CXpMHR*0fdX|O~iQQnuf(*rF8xQhF4nvzjaELxJTKu=lE|PS;%XEAT(&)KvX+c4`8Yp#HfMYMN40c0Ol9 zOSmqxpOfE%FI^P$Vl6qy9U6wFvh-0GI1lxI)CIOf9jyuG?qqn7E7+U70iM{jndRRb zRo@Fg@)%Dosgo`wl1hmHpvWz97yWAS+lbu4cc(`JttS&+=f~f5c}4kO6!OM!HyM z%=?cpaxlFK1YlEU;WAO1v{evjUH8$c+k?X?Wu4E`$b$EgqbdE~T!B{<-bt%GH~+mo zjOTgs8TUdXx(7~zBN&hmN-+cbOT}Gef<$DpH=_^D>fjD(AgDU1EcVGpnhVcrXOTEG z)I;F$s^ShYFQR~u`~;!sV{+wTp$-);Zo9RPxaF8TAXt49vb2R%ylk(u_WnKUjCYif z3Tmr^<#U3*X5~PCSBxT+P9I`J!GZ++D6iqiUT#&q^4XMDEI3}a{FXzGc-=0R&%^+A zUe*Q@qh%pX(A{20bj5l_f8Sb1l*#HILKhcahKl88pCh|bxot$+vW8?5I|z_GjuG#%(qtZ=Kqo^oD7U(@4yA;{b{9)_ zoNSx-0@=~7nXgd!4%3RKa6KI~>90+2HwhljNSc(Q52Z!q2}y{T70C$tDgce>8oEvw zVxGp3*U8BflxO9&8%^H#oUvuOEy&HAa29#*syt%Vq&kh4^^q%(S9JQMtn_sO(jV68 zRaxn0OCPuDbS`Ec;NxXyNqSJHAD)%|wxnOA(uwz}V5(iJ&ix?suv^Hy82t~_&{H8w zA_O`#SD&b0ORrbbAZ5qy1hf4qD=Snoth-nq*(HV)(^^;E8W(uq1W)(Gyj(%$2VdYG zDrC#enXD8KP#^E^1>@iofj9yA-KA_eu!@K*WkgU9U)hZ5XlV+RF*^Wd zQJCJtwTrp5y9Tox@mG}*L?29z@c{V}?=jQ_su@x^=EdFj3O&wa=+_x%bE1;QwS58Q zuaU-2r@S-HX6xT5Ewf9UBRf+GC34nKYOK%7SU>;mu@)6F)|Cp`%DzR0U_=FcHAJyc z28zGVqmPw0rDrBMJq@kQN*dbNTY&aH%B7)owSxAay=DBmB{eH2r6B#^W@Va4ysH&d zGD|@9;m`DTGHoGkV(kzkX(`xUyx1c+xW6N?geAIa;yB#t5m>xiymgO|kR#+y?Y?g-S(2 zzLh;yj+S@UYXNtqIvJ;U#98=0SyxI^59sV@~CvAnalM627Ijgztq3uHYCff?k6XpOult zJCgo{ll~~_vgh<%?1s@Qnh~oHe;nCXc7;SyD_zpFf8q_36lXtMb*EUaCEVm()oO6F zHajf}OYCuiW7JvIL&ULT`iY?xOm-dPFvzp!qSxb1BAt|P21HjPEHv~FyQ{#3f^3eb zag@HG#BP8=v$oeeYkQe{y;X)VZ&z?h)-Zdymke>Jzj`2tco1?(Wcr4q-2{S0R-!VIU$vrNkLpv=Am^Nvu}?pav`6Gv?*)DbUx zLlAui-DL|V5At^hf2sS* zoP)L4ONo(`BK|81AXn!+_Z;@|BE&Sy_5>&HTNS^22!hK}O&T_L>6p?xXSu@z<+U8; zt{2N8j7H@&2}k(IoL?5~5w{NXmxEDcGY%-#^Qpny4Ta}-|N*O3gMKr49 zJL1XC?|ZrIyOmrQNS_A@$HaM%peC?&j-o>GKh41mXfoxr{j75yjM0j*WP}^@YL0_n z)y?2E_+8x$4udv!*F+hl0g;qA4c61h-|HZeIcQb5_=%0JFWDdktjQ#(k`73-q|}9y zXnv4*K*24i!JTAt8od0VZX5u`BJ45}vUlihQa~E~j^S!l_X1#hnw%5=7gYZ;<3B<5 zDs&LcJ$Mu@r19)5@w-n3&;L~U2qWORBTG*Uc#+2_Jz$g=rUhg|n;n)P_yZ;CQ zyt>)`iKJA!uu8%Em@@eM^h1pG!x*Sf%9MX#M4E+g9OBGcdq>tX>U_q**tnmqx2{wxEX@cC z$hU8&t|HISbXtcr^JqHpXSJ%zDX%B%O-k8C^yg_AS(*oP8e~NKgKX5bp^m!VnnU$e zzDBZ;@r|?4*K#_@nTSbp8k{mc!<#lh=ZX_JwO$ZQsB$Zp|5p_tV* ziej|tVhD}ksL?k(1I@+LT7pPvE=2s-4W62%z389TUd-o7X3^*TK3l(l?vRU82z>N- zP%)A;WZYInODXGKR;QG9hHQzUWKUIkD$0xRteNCHd}k}$uOe4DU%tzaRHawGVU(Xv zqhM^>066Y#FObR5W~zOER^Q5!ZwYZ8_&%j|lFjEir??dJxL110f1dfS4d+7_@|>d! zQJbla9wFP`mI<)`2U%B01{JeI$anS3kS^pUgwy^;Msfzduv%J;TNdlXnxIod_`VyZ zi&Sa7SR>?Xf_4bAzv?1$TudC8+^i}z<7^eLE~$3{QHd~j=(&M(l z74(4VZZ{|cj@OV~T-HZHDK)J?ip|%>Fc=t$c`pV?JswAUM2_Y@Pg693Hm1jS)3X}* zA8CNnPFGd3{Ng=s0t~4rlPlH?c93EMuZ-tNJ)ZN>s^@n)n%NhgxytGl-_4y%W^i@X zRrVcHha=zYzVfAAwcU5(UTs8Qy*%|kv*Zj{evrCUrPY=~*!yJPkym(fFI`=K5?J#~ zNCsA13)*>J<1m2=TfeTXe>p9LUQ4XZioYrLY{}r+PM6m6m+0P{+X;+XJUcdf8ulKQ z>YvE6`SlgK{nt^++Y3jQ{C|>@20@l~Ynz|+{gOf*Js;pRUzkV1G{VHEHa!wWkqz+8kTY-O^%TEdPf_?b0J+ zG%fbVoTbHE?*{z$rw|}3K6JEv7?nG5D(~Zsd=@%~O0PJT#6c-~D?@9wRMdMCzKMZX zeC{`^;vspi2vV5VMXr8o7mKaKc5BcSt`&5f!6)dlO{fI6lISG#g#Y!Wa!Emd(|_c~ zVujauZ9se6ucm*V=+AtSZp^!dfl))sXBWBQz^vLgeC~P9ec60sPfhE}9YzU)E2G8$ z<)!PHCED{!QxUnXlF5m8{u866uL57Zdx3cJQ{1|ZvH(+MCLDN;s5r3OKMGS+^Fvmn z_Z4InnQvb_^on)W9dZ(%e&SH^?5imY?>x$wo1lESeutO|hd<;yZ{EZ^A5Dj`ygBw- z5)|5$9hAMCpF>?6kI?eh-!t5j3uB-&SiclZ=s&dQp5Ya>GSeu!hX#+!qErza*3^y5-5PQ2tAb zuj`xMf5GS&u65nT6h=yF$1_{5Agm<+$)Olq#s)F#*6>fc-=}EW#OI>%%ETv! z{?qnu+MYKJWtK}*=xUCBjyqE&9yC_vW?B~m8m{d;;pC*HlYo_Ybd4C@GDv@uzW(I2 zg1xJhm!XK+G425^)&^?xq)gh$NVr7W$!POFPDWYxeXY-@1Y>CA#!T6g{0#T74trK? z@U)2`*R|L|`WshB`$qIGV32sU;3+;N$L;FRfVO|PE1x+o<#r_)@4QKn%9&TGtXWLF zmn`GMf!|WETPee0LyB(ukTLRJ*zuP((P4Kwq!LWATPsD+8(1RgSCo5ZdGn>@h?^^sR;^Ecrc<*LQtOY-t=UxlF4de zvd~8XW7W1^VzjtYFi{enAUG>g;p+Gh=SQQ&ONMtbe+&7W&tDx`6=dOndIcY5Jjnk( z?Urh}cyt5R<_bX_LWZh_JTigIySwp;3-oTe{v=*T{ho0 zk=L9iwqPu{7h^%?TC&Z$Vz?L!uBsdydS&J(jq0L7mWuTh4?7e3agd7(`gmTv7io#0 z^vsn>(kg7#^T^-pIfeiI_HTKz`nSq+do7|@9pw+VP?;NR-W%H9qc_)P+TP=`ps49) zb@jzx73|3g=h-b>782JdoiIfL&c*WIY|wKG!{<@UIV*$!vgpK`P$4X5pY=Qj+g0(x zlO<8GSS^WQDP#$YDQ72_rQ1vKnPjk7+<-fWipfoR+gW4Z0h;5r1*WoBQ&kh~vdU-! zWg+}=njK|I+b!qAWfXrFju&|ym~#jj;P#rKcdbu&s2K{8DY`(W=o%24O&ngY`Oi8k zXVC}3v9c%aw@J0{*pvDuZt@vV?a7dc7qsxm*x>OqXMUu%YMyRsSBAad? zT;|9@JC=*_cc{<`0SE!1G1!85ydQYX@uhk=>Bmr7*)uEqFXufsX6fkq^vc7nyp%bm z7lgBx;PuXff?{3Tu2DJ{nazk?&R5=2^_h1Hjj)Wx=R_ifn6DP`E!3GPHIw;mL{0)k zU;IvKRJM?mspz(2l?CI`DGSEoG-!9_Kk-FXZ|dpR>{Y36-FKdvdn59-z+oyQ z%JfqIoi#q0%Fa?}b<0wBT+Xe-cx+lPa_yWab9s@>5)n2Ufr(#I(t7$_J=SPMf-A*6br04rxaMSBe^M_XOHVw^!*E%E@$|8Do_=B_9W6#TsTMxW1bg$>f!i z&DNg<39tGQf6&X(C+ zp~H26=FkX&olzbSz|go+>^>Z9rL2=PMeXzO=FQ?~3ZK=s9F_I?&%~L$+U}bA4YsT8 zw*1OEuci2(Y)8WXUe4x!LX|Qk?IodZ59G*8fMRX3UlqG{G9m|d8*#<`QVJNWOCQD4 zI4b9V`Uj41u^E$-g<|*?Q&NtIwKFbqIM|xY!IJ z7q)P&u!T*h@ajF8r^WW9&#(9h>f)AX~H`Cuc2;fRBHBgH2WTEH+y;+m}yF ziyfE9B)1I8o#d+hPO>k4pW3R#U?@Gltoio2wfwZrY61!A{mMFo-m+$SUwmugwjNj~ zyXp?>#nHmDJFkC1P}0FSIM5tAVLzC@gJK$2vCjX@lnuUX1qKQ;*YuDhb|J4g_ztNG zAl2KP!MChgg>T{7X~$n9`mv<{-$TForu7BxVk|d#Nn|mN%W4}Z6LAfQ%ie@X{N6-p z`BI$^)c%{nzYheY@t?KcZl~LGz| zcb8?sYuOWWpezD}-1X_u%k^(-dNI#X^zz{I8Mq@pVQSt1U310mtoc`k)%+u`4x%)@ zxKc5hxHSAk@#{4zN(QdIdl4$pH^Yw^yAe4|7G#QdYdyGi_7Pedc$hrg@}lc+=xF_W z@UKa8nTg^(!@v)9#Inc?3jP<%8P1)c2?F6%S1VeuWmnqX1R{;f5t`5%jUmJ0nB z`r7D=a=qJ#0rWn?8H9+_*<-bTx3%$|ip@9>wYWY2>psd~x5e+-grbhTA|R@&^1= zOy#2Q@r4?9(x=qmnwK~>q~XIdS{zX#&*=gNhScdM`_LqJ4#6qK=oh~b4QeD#F;A?W z(}|b1gVUs^s4!T@!2OXkhI0i*hq~$cK{32sj}9nx6E=|iM779&f$}ecef_aBio}KD zsKM?~du|!?+(B7Zinyp=vzzLFmFnDssaHfL(=n&KNHV99ndq$A8yqSmo=b2d4FfTN z1!C70Df_y${78ZCuIc3&>~-z0w<#z0!T*B>L12CqI z+=ACp<9mbWw!uz>Dh-JX?kOZlQAMJg#tGajS@7QmVgm)L=u_~2iglo)bc@eXOd?mL zdMwWEQ8a-)KCI4aH%tZ69(|xlHR2E?;TcjID*F;jq~k-R{Sy_lg}udl+~YK-$Z&vw z8LNR|Y_{%ZRgvGNLl%W~*ZEhZr+BI^)QAaOdJx~CsStJ|jf^25@3YZj(8cTN=O_~E zoDZC7gD(2W!$L1n!v1c8U$vm^_M7b&bRTyet_O!n6GkyQ&2-X)vTTRl+)Y~IFo3$m$&^X8 z=jaMoIB8-Xk`Nx3DkNMpaWM%M7wK+fdaPz79cL)P{tV+lGE?vur{I^@L^SCL?DwfE z8g}j}dc1c+7R}U=35w5w?t&)>1s)_;00Swu@j~6LOl_iwg!N8>f>dbfO(#LdYWmXFSwkgq6X3c-90Lp~vGr#Dqp>Mj@=7 zZgqkX`b)aiE%}*N6&SZ=S9tFn1;!Op!K8xjvaRxQ9qkGdez3;{7i4wth%V2@))LAC z{?Ss=m9Ch_6AHrFEnp>AilOVF$cYrO*ON-5tsu#KfJSdunYIDPHO0@#Ob20=|YcO<;o8G z^B8&nJF=XhTXAsA$$u{=f48-g-$YL4b%n%VS`~dvipG2RvthhTr@eCTZ_-pycfe($ zy#unzm5dwo?bhM$A-_q$paAr=G17bcL7W znfReHvM}gG$Bsy!_k}*StwKe=dKEskABuV=1jkZ>$)mX{du*Ic>>h~`^zTa*ns~pm zo&wmlON^avO7L^+GMw)|qW??m8|0d;#*%}1fjhk0sM$bkKMG6}yWiz=Z%=OH_umeK zaW+a~yzB$`k^QoKH{axE)OoVhR z+v&jic~Vv@M)b5yQL%fqHsoeY%y_4zVbtd0h*-taWN*Iw@hH9=!j~I__X{wM@4jEZnIRzoXpCG}sDze*IvTCOd2=7hKyqX8t1M$n$X{6t#9Q}{* z$&yc69Y?lV@1dNL+N}GT?&VZ3|8eHKi9Tw0=^dM=B6{587P(s6pIP@)D;2yc+tB?x zz1~aOd-a;#*#4_2OTr}v5iCrA=a#5!k?HXjVS;!qrj9!}71&rpI$0MyD#56X$S7u2pIyGQ zzCa}j@_H%2fYX8%>QM^wE7>7*&|BjzasgskpFnTe6uG>B#=*)ye(+1icKuU zKOQH?5rxvV9MO(~deoY4OJNB$$nwv(cD=Jp^nfI>rc?+O$>w{hHN zDldvP>kbOY&?Y%n5FqTMKzVDDrD|mgH!woMOW7X}125e){$${UA>1PhkFHfQ-sYQu zte*^iL2_cNZcW`+Xnadk%I>Dx7M7JOzjDGIq(ItER(LiBH}hl%^uX=7?S-MBQ4k|@&2mWo5_ zf^Xx-q@zs3uvcPJjLZ?MD8wOYQs{sd7dESjJV|JAx-oAaKjG_V7RJwVo97iaRyIiiHAp}< zZWn6T_Hx>EZBRzgU+#&Y{;P`0z525G1pY5jQT1J+uJ~juatG11oyuTCpC?v^XP=OS zQh17_z|&G$d`LymV+P)hjVd&~i&;{)J%Dg4hmf9f#Ug$lylsqe+9KYpu1}lcWn7DdG?C^O3U?{94fUDPht7_t#{)Vmjqhj3) zD$TJEkBXgB(9>JayCUK{4oH)*J*NHBW0eI3XOhYKfjM*|NoZn0Lm;}Ir#kAP3$MA0 zfQ;0tENvjhE?^wWXSj zuc!*IlQ$ne<{&GHk||}SAa$d%A$!g3_F&q`;Sa;(ovtB|xL1eI6P7!#k&Vn;?TuB= z%L6%{@+RZ<=STuy3JXSj55IV%O(UTble^U&EVe%<)f3)5DL8@u+l0C)RLcmX)5x*^ zp-{fdM)0Gl?ViPMTg%or>QnsHmZ}0sg(gzb}j-tTq?vS0H8a;n{ER5bJn_X)cgr$m+#b7wN5#AkH(2?E+aMjX_n6Ca=2tBr zhwg2uEn-ZAr=W?q!r({mkOcNvEIe>|<_Y#{DPnGC;u0(Q)O03fq+t~(nXbg6x{Q4N zM`i51p5FLyfwrdvpJ1Vscv@EG8PNk}w%~;^PeB#WQpNl|nW?^>mWKAhlfBG*5yjl{ ztnJP=vLwi8$6GxOUWspQe=H*;1sM5Oi33FRZ)(u8!tUmqT32tWFM3@iopq%xQq-b4 z8VH<4btqqjcd()qE0F~PJE<}^S&QVXYZBBXXU(YK{H(R0gwGJ7oU^}Tp+dV}gViQ@ zVtCi2P=Bv!3t@ujU=3yfiYW?2ew*USr>EUV2s|QASPxeaT@R74v zp6ZrfEQ@Fp;RO9OYh_0F*y%ZIMR;&(gOX-{d!23MjOj&hEQ{lxj^0rjXw#S_w5IW312wu*kgGe?a#<=-uENLGh=a(L` z?4F+vCE+VrS7nBln(OAQ7W)q~@PzuxL~6UtMz1J-IXHH{t?Mx^|7)&eoY<%F6GJ38 zaVLmFWsXGz_>|swx4e(Ay9|6-Xoh#r4BjxDXXsDGP`nX;#{{8&U$3C$WLL#B$^j1# z(94YPL^Q4tf!HNqL`JSC;$>Qa)^B>zkzUHME=7V0)G1T96*Qu*RG8cd-)WK~VL2fC z3qwEI{~`e%GmmY+>V_KQMcR>JH>+-61Qpz@DM;8M9xF@}eS^yRe*}NtY*_Y zkKF|D)(1x@(0JD?3m@6=c}(vhzd5!vaa_OpfcqC@^Ldhlk8B;IEd{ZOLQkW2kc`XU z=q;5`9%5gVf_SGP5c|z~ryubXXNv#ST@EMU(xY%F{}-w6h4McwRj8IcjTL>RMa(t? zzYt0l-j+o4HK!L?mo{>yhs z1*g`%CutchrcH#&Ga@1x!~##yBU|Mfe%Noz0O-t!9N&$+u}^r+(M5HGPBNkoAp>xp z^P2%k-|Y+UJlUAro-(w*YHAN4$;z4bmHqXBG$hzt0LiP;(GN(PpIgwFTS>Fh#FQ^| zp#jp(5)Z)dEUM`!ImS&3smQ6i%3Sr592Awgl(qbD9qKMjrau;N)s-XpeuE%vf53k- zD$`e_&=4P;B&6R-!4oTtC2PaGP7at~hQ`zlI+;}yy2=yt6?=H9yu87P9}B2{0w&mJ zvwA5&LCO%(iyNG}>F$&99p_ti4dMN&hP-zGVN#r~DuNf%)haU{>;hd$}W)8N)Z)A_+Sf6s^OV#R87E zkS3z*2>c=B18EpbeBh5r%H^C#jfd7w#fUK{W15{fGBb?YKPgx~0tUqdA|P2>KMM?} zTF0+Kwa$y5_C@3i(4rZ0fIj*6fNrLolpmQHA%varwoaG`@Kd2)YA7Gp%Mp&YCmh8nm9iRyJ?n6W-l1EUY zmy#p|!Qj(e>uS={`De43}T$Om#@t>UM#gg0KX5}xbBwiHCPRMRgE^jp4V z?e}#zxKGA8-I;|@A@gvE%!B$DX zQNCCMLjoIbP3UL@c7yzO@&I3)wY9o*IZE(E_p zRb3^1qMq+j`_6Z$zkMbRx0X+Nz?!Y+V9KwZ+SL(M5>`r{6!O@KIG7@jpFy8P9z&2i zj(|2<%F#xWm1-9W%UddPmRHGKUL|vRl{gST@zi$5U}rei6|Q~G6&yfNs-YrxSH%ML z!Apmt3UdWdb!2Y6S%iX~n7?SLi#6S=fs{+PcsEm5i+_FB+J z)ne`xLkEY;+i*Au-+)pXbMhPDT9M)o92u53B#v?~U$=(`hm}*SQMK{)B7qe>c)pDL zV72D%69k$olbBd#iTj)|8@|k^4D_S9$~}}r_+?Cdn$?|Txc=A;qH-1)wbo@dydpBH zAmOZ{9m`~Zbk$6?V7{j=a7dj%EUV;us6~YzdPT&4f^g#wu-=Iwl|bjQ3`M|tdZ{$G z5njbV(Nipj=jF?dxEo6M$IgQJ54bQkWP9@MaI$^PMt*JgPaN`k6#?+S6dhgkO?nVR zms>aR#TTE_Gd4_>HRg(*k*@gP^|JiOo6u(n0wxYwTIFt}dTbWDn32fTyQ?}Z3zhrg zW865KmM>HK7a-$VpJhh$5&)MmRd`~70!Xowh<_2-l^8gDoPc;8+xa zPCW^TL%sND_qwmNOt{XyEG-i@D?Otornbhdk5D)^r8I>i2I5Mt4exDl%)OSC#q$#t zkd|-afERr3y-7swWvQYlfb7>Pk~MB#T=5nru7`=@%d6Ce{r`g@Bsu`BM|lKGkitsy z3p9rLavp5sa73XoFY^;0Iu&X(A|JCHg-pHXrJbaEBm>ms1-^-I5Cc~#SDM{Rg}QAC z9`YNlSMA$+4_ZgThl)Nb#^**P_@8v-GiR0-RGF2fh4v4ikM3S#)DT8(p*eQGxxpFD zNA7(_^9XC57mA!tVuAULf&oEb;Wy-(!DEhPQ0u^S_MkYESC$quR+jdhL^tS(=Sj@n zmjD>8%z97w7XW`At)+v=x=y7<_9Ohxox2~6>qbJaDPpeGb2lT3mUy;zetH6@=n?%c z4gTf?+UP5kh?1GWXIRje8Q0;88abGzGqb1$U9jIlX9i3SHDEb;qy}uSM&`&Jo2==5 z$npkRW?7_pH*GLi|J}%peMhzt&SOM|(`sf=OdsU5U-HC@s8q35A)i8R^pvx>oC!)Z z6+J;UttV(5DEY64;Y%LfdV;z#3^hT)8?(o;-!Wt+hEZd@FntsRh}iu{)VBRr%NDuV z{eOsi6Ywaitl>X_1R@41Y#I?HC@8p1qM$@zOp`QJO9$ct;*2OxTyPzPZV9plI~gd; zR%X;0+(sN{aW+MLbwE@SP&z@yh#R5;qGI*5j#0-D7U}=*+*{S@gy8%9zvp|NkD1t2 zx9)oGx#ygF?z!i*Z0H`YWHRz$5RkoakENS6`J0+A&TumpPhh8$XP;HlvMDosUSvzA zbynhRSvwqZu97Me(^c^0+#+^wZc1NaINqCTBJ_w9@fDZnEFNJsnjA+}=NetmbakyZ zvdXRNM{`hKdT1E*SlIQF^R?OnMs^f*=2SkQ#jzJ5z{_#&sn5%C?$;E_lOl)}u{f@GtdJszdB+eKRw7g$@C?twfmF{4$Zi?+7OuSVWP z$LKwgOXw4KaYE3*Ja-3>6CDy-N!SH0cQGTeLbLe2Fq_gjxS`Js4S?s}##Zbiva|ma z$T#KKBb%*}P1a_gb>UFa75Gp}EV@B;4%bR|1LzEEvl*S86O4|>$;Q}=_#?VZtQqZX zYCl7T-HbfL=-rD-yO-J1TFdM!>BtrQUD_Jp6p54Ra#SJ|bjphx%B`j)uL(dBvsq2U zTtfc}OY=yZ;6%2&FPz_gXTJI1Ft%B5x|^7<)XJI2uKdd3K6?tg&wVoe0M)qX9B7*m zLdM^=3zj`-U?^KthRL~EGSr<@0huk<+;XLCMQ=KdHjLU`|7KgVhz68A#98zCB&!o& zqvmeaZhKYZ`f937_LT*oL?+2Yb z^_0b$m;dNTl#2R*A?BA`QW;98EZ2J2aVmT-LFj7#MJ10&oFpHF%&Yg~l(Oj~&fPj@ z|4}iRu#OtHN77@3+t#O|V9Y_O=&atM&s#i5(pTD|XD92hL{({L+9;bPMr2|`N>X)G zSU21D56ejWRX+3b8ZY!U@c`eI+n$# zXXG>c2)NKzukJ*A7jwAcVf-zA$e;K$db>55ypHD?{5{19$UqJ;DqunDc;{kk@>#w= zgRjbQyx+t3gFMT&xRKwJ`CiNSQ}{NBzpMEE5$WmrdO1&!ivq!@ztws43lQHXvL06Z zu74YPt1b}e2v4DIs>i7)Jo=U=UTbju5g`0+v}Z^pVh$M;p7|nGLonlH=%i75I%)Ci_>b~+ZqyEx_eS;zm;2Bz=% zkZMHCnNp1$uNzU_RxugljeOS7>@%;|+Kdyk6eRC>hGdxyWr+PJRi8W9ZM>un*5cK9 zWo!yy7t#$4Fr(&XRZb7Kwl8an(THAbD(UeR{70sh8wGxqPt_kfEv@b|NOxwn)%`ua z_v-Gg%J_9KKsGJfr-97IxqOiUJ7>0)T$fhzeW9VEb9G@GllLyXgOv>~_SBGH4gGI? z^uRNu4LtXfoFHnX45#KpUSNS`DTsyUDSQn#r!Y$pBm9Z;@R$^wMx=Ta!eO>{()N@) zPoaN_?VFI2C!OotinK@{H7@=(alL$=qSPO4@&FbenhA+xm%DEvzc0BZh=Zm2U0FVR zU>AH4cggXMnLgCG>nfyp#m3U<1>~Vg{ANf7iIE$>RH}%a-z`jROn*~Zar5F6Ec|1{ z;&ONsoh0-`rH+!cS4o47U#8gjynnfe*CzI;+RCHVm-%LKLz|VyIb_xvvUzvn>GXfi zKUSN9Xfu#3F=mNoZ~d<@gm)C*5B(y*b;Az?M6BEfnW9nD+exX1(@ULBDYwjhlz|Ti zDmN*JS-D+sG*fN?GoRF^nLtaEa8f$zVQ0-AZZ_kE`ba^m0buy#JCoDJ=li}~A_n=AR-&+~A8 zGx?SCIYo^gq}9|xvc7Le=sEE8B|a2=$j&!Pdx-()D}DB~0_(!z!q${K7|4*6=Bf~0 zSw0I*TVKeV*7bw}Or%`c>|Nr8RwFol0pBG^J!*JVBPD2F8L>4pCsv}2>V!o;WZLM_ zIoiij+O|Zrh+u)Fj>K^A(6`c!cKG+>_Jc)r=~wz#Z?XnXOh0dzfrgH8jj6-U3Z$M7 zJUM#ruwJD6B~qRV=`H%(dSgnxTbkPu_N6fSt)bCoKl#`yv3P)B+d;UAtm@ zOI8;ZVymtoSiC9RoOpQ+$dL3}RI^8;Df@A0?=)#|&E%_IBt8tr@9t1dC;e9qNTnxBrPQa!pWiZZNT#@uQ8Xm{Zw8m`LZZX?rBuq^prXv4Jk*{r z+=t*#5a0=g@n12UtkJVvxm_2zjWuCs6_^)rx<#JkAGPQlb%}JcgG4(K`<^sqgPv?eDG3`kOExrPnSvK5WN}vR7 z*OmBSG&CX@cSrvkSpef${t3L&w2wBP*On7jPd&tX9);u7s?3Oa#}~Xuhocl zr!iG?FS592DADX?{~1Q=VotvHvFVp1-+*E(M8uQ)V58q6co4noc#^5eB710`HF;<} zmpXNW(-_9&;l-O5UCoup!ua{r64_sX5mZUe7Buhs5noRt^ICxlh-O6k^X39X$%pj< z6c9`4=Z#vC$yi3QuUJMBb~Qb+YjOxD2Y3GwNG;u`)unrRW&Pn`3?uWRSyJkj2AjPU`=)bKMlaUC+i~J1B9|~a?pK!qqt9UgU^~aR3Qs6 z6nYT<7sNQ#&J$mD)(!-BaOYE-LksZ&YH({yoC8)716E-a*BS~~1}{h^Wf9VTHsX)N zti73RH&293)g|=+>R}_!ZDh9U87xy+n@fv{*W9v*Zt-u~{nHdv>5M~t&N3yZuX^mz zs_-$`h1?v@tFG#DQ@BTURabcYWz0il!RU;|BiSJ<-#L>CFR&LDvnAi;3@TESReP6A zfx0R%IuMEFaaDjSxuMdP0u+lsq=38Ad4u8oFYP7W_Kdrqr%)$t{eWuuv;zM#**_azg>qVSe973RaKKXC8 zFp|C1{biBd(}68vc&%3FD7MM6UEnqpVpv?HL_O-nw9dM5w(iDvf212f(HW}rrf;Xz zA04%C^$@K-cHSgU{9Q?qi=C{z zMtk0>a&ln-b}VSOKqZeG!0?>x)L_!e{SE-eiBUscGJ6Ut+-{4G`r;`MA}>7-)R>uZ z?~vrIKivaKS}kHQprQ_{{nHWnb0EbBQ^rNdYkM=}@i*tfk2HVQc1O4UTlmu-;4PZT z-or{0-X_c{QvpDz?npth%H5k!6>lkszXxEIs4y(|AMO#cJpvZviSv^e(wRWMQQ-Z7=* z;ts3v6;@+x;rsCfO^-b_d{!GLfgx^Kbh%<%ckvN#EO5#xu$YO$Df@Ggs!&~Hc99|e zJ2IsliTlagq*`GwHeC9uy= z+Obce;1i+X*C|bG8|y?&0%V~kq2NuLf@2FYWoUed)cTUFenn0Q27V)E${uSGN#xZ% zE^CoZ%F(HBp|e;4uImUdzfpS+)lufxstk(fc3iW7FZ0xwFR<(4-pr=NFH#)iHa;BA zncx`MMtZ^kJjp&f#g>|7P{|Ei9uRY{X<{FP!KGpviSEpejH`VoHtJ#GPNO{;9Fj%` z=dSX|;QSG?ie4!jvw(oBi<*q+$KDc532J}-JOv(ZVR^Jf!+T$1uuE!AZzDByK$w~2 zC~7RXb~&FBCq_{7VOgD%>9LDcWK^z>bx+%t{D1%ME&0ulx=a2OlAPCvtInoo$f&)Z z%Hq@c&*AGGMJ4)HJeZt~D<68|L=q9&yk*n|Sfwr`x_T zqf_5Ohs>&4`}qO!SEl?wFB$ENE)y54k=A49gxJDIcI}MRt&g-0m~$;|i?8H5&C;aE zNlPx}W41U>#9{Q@(u4^aYua%VMg;h8{p~(r5gtWVKbwgv_GeGh`QOx0vOBwDu6vYO z`PhREMW2RIK2?=Y=j%u37;7 zMIUMG>v5mLzO#-vfeQONOks`qSgfz0%0x_;>T}|F`&e|A$~j zj_Tb1E&MxGs(kJ@8l~ItuT+wM?>}Bv)2}s zIbMUq{N$X_HACsqW?{UIyV6Jv%*vhO_1@q^jqPxco;5jSbByJLyASTTwSNNF3#oi;LggH~ZE>earNV_pLBpxzi)vM_flAy$BZITps zwmXPV-KLx4}FxP5UFCA zPQD2m8MVJAMWpM{0OFuz;s%Kt>fCv`+O5Hk$RQRIXkT^1yJflfUjI3MDFLL(D^hIMO{Svf{OCt)mCkw2K z8fCaAXf!vL$tlm%nZ7l$WPnkt5fKhWlc5=u$qcLRW1N@M5H{!_CKqm#i)VHmo(IueUKz!1Nl zYEtM23;Tr!h09zhge!vfRZ2fUQJrA+3Y{-E<>qt=pUeFf1K?cD?#$)YMCv$hrdR$uMf~)V-!eRi1NgWj2H`BA-7`BoAl>*}ZnJ0hM;saTE`1T1IVccV?-g&sM0~TiD^c zIqZ9t1uS{{+szM&qG z)_&pJ*Rtr6nYLhle2dzuv-M$eu8LF*%nWm%Yyt$gE_4xa?HvxPV5uR$^x#VAFbxZq z-lPhW)1D0loI5))K;iQs$#)9+XUU^n$4E!z^jV}}aJ9ElD|#H$jyxehGM+!nL-Fi< zb_4QM8Ih$})WWyLMNuQt!JeUtHHEKZZKn68+ZXyb7vWJxLco5z$duyCs&X0*JHg+yfr*k#0yU?TBhBsE_9YZDTOHAWsdmNP!lt z6Cm29`B?LY28rJ+vFC7>p}N)-8OPn-68}=g)}2mPw(e~ujGBQYQA+z4fB@=Qjf^Jw z0()$;^YRYaFJNz+peUOXU?a5ziZ>C>OR0xqrL+0uG7*_qTtzlP#{ME>UnissTz*i! zNW;7kwTUk>SYE1mkt0T?x|dNi$9-!lnZq#yZk)wth84codG+`13U6`tLlomU_~zTp z0%hs&1}~aEKzQT>B;h`Mh=FfpU6vjMk(lWBIuUV|w1YF9q*Fbo3%g6E^1gb@mW=W9bT(PH z&Tit-v29z=BFR8g`Q0Z|*;x43;< z=*e7QP&bHC(B`V62w?kY)U*!BEoA>Ycmk2h@x3Ir_UZBzsxv1)yf0}_x~VKPZWcGp z9j8!sjFjdU&C7%V!XHwOkgrhp`2}$qvK0C>qzX}jU<_nVoew9S2K`CbH)jGaS_-7Z z(z%qFdy|>T-u8QYJQeKK=O#vo8lr6s5Oj&%bq=wx$`~*91d+)koOG^kS);?@q{E$`6#gjxswFtdz2I_O@Y$e>e;a;cXWLWq#E4L< z4!Pd1DCA!mQe=%#N504p7cl`Vysj~Hy=mVHy>1E31U%RTgjeA}ZS7fPa-N!WP!zKg zNLtquC)E^(ic4dwE}=?7V};{2gt$kLU3C@RD^|P}QW-u($+D9DS^$PzKV&?TJ{=>|JqI1-l!XH9f(axm4PPs(p zq%C*y3aG9hzoDczy(Yd#)_Pa~mR@Z`7jXo3E{bcx^3a*~^07>8WMAbH z9AvC0;Td@pnRlf;;wMFY_JEF)wCwkrq?0MQCkxuyQxJd+=tAoCPGW()h|OFmb0fb6 z8KIj5Da6b36tH#TY;V_dopff17I=SdS{sx;68jFd5k5+9u$mK?;7%WNPc@Y;JSTE~ zSA0WS2OP9@0I}*AIt4cf|4B72dgU1#v2JNc^jHM?gS&qPpvSrW(QY~f!~>kG>sSUS zyS|^@We|VrvwdFR8gFGhOj5i0-P*nVq*QI%%S`gi@Oqf``Ud(wBe{#zI-Vr;IJ;M5 z-?5?W$iA$wfx-FWJa#A>k{c4e=)dy1NGN?ke4V~d8nl%d zieO?^HksB)?zJdsGr7UKS9*n0ZrAfzcc=UMPH440)jcDR1Jg1N`x z)@*dr9=tJA$Z;JcD38EhsOdx>3`U`4Dm4s1;p?64?pNO%5r> z-+k25j%i<9FyzjaRHO=b36}!gZPG4ShY8C%MdHI}sFKc`BQya&AsG?+oj3;nVu!NYE~+wKsB)Vp6K|brEteZR}5&wh1{_y68kxUb=AGDFn!4RCkve^5Q#B# zm#<~woGzh{`*$&c9J>^96v(-Z}(VXU7L|79YOI8;g)P z_G@|L{8Hgpf;`OI*HVT6WNqRGatW)q$5K(RTzR&i!K}t@#iiLxLvD`%c`y*L3JtoggYKQ2`FrtR1iO# zhxP>C*|MWk8f5o;_rkVu)QqN}Q^g5sJ95SOQZ^ryy048Ndh*Z3752nDSu!Q?CYkWi zELT7cUukBqV)WwOMMT8!d}y+-d4@T+c9dD)vH${*eJyADR3%-AKVvV`RHH6bU`Eyp z^fQoiz*bZ%D~D;a;Y~vu3FW+wg#+hRyg4)x=ZcYiN8!wPU$0PBwK=C-a+z#_QM$r( z8?|9%qJO%)gSJ+EGPt0e8UCg^FsDl*0guI|GPQoBjV!ynYb>cuJX88l1rMT`ktI({ z1$~ukUnDs#P%8hvBG`*f>Q7Wy@)7I_MOQFH1jT_ZrDz3@L6UR|9d@{Y^mPQVK=Bo` z!~a%Q9Y=DC-r*Tcp?>IUuVr`aXTxkmgHeDfs&*C|PPW~U{{zkkax25ysa0PlpBqEy zMW@V0O=nN!%3q1FkkHwHoNP5p_PL@`cKvHHz<(vExEi93vuy{%Q|E4xuAfp;xTIufOU%toR9G< zh8?mrZ@o?x;1H=2wWU$3&Kbl*UuymjTfW2EmhS*r zzWrtSZu(lubLWaM_j@%FjH@;u_#_HFZrk0dd%hDf;%Fkmc3786B-26Jw7i5r>hVYk z-+}^jv(>ysTDeJsqx|skraityd#6=aWZb1wNR@S`Acfw*%pt@3FUZt8Z~PGo&VWyb ze3H5ovDGUE*qkjym?c%!aL05V^^>cJiy;66%y5#=F@RIs(;$_10M zgdNxfwE~^r5t4+TC>qWsd^yj!P-yrmnuagO>Z=_-XnKNt zKc54X=EN|&WcnDTk6hS>56^$qh7W)6D0%I99q?iG|8#;62e$nvJ}4-OjOY<+3FIOG zTLQpXaQOm2yR?c)4jTSe2(T+&8j+QhTCem5$6l_^z>S*!kR-B~)e0v^XRjsEx!ko1 z4XuzxwCL)6$@1cZ;X#R?2<7S>5O1&49e&f*cknSp_OhXhEUlI6QNyLcmr{Dl3{lJA zF^t9VzJjw7JdGNGzw`97@X=>^f*ecbGu0{$T_&>;ac9EEa-A(y=~%Zt1$JyGHCYcg zw?Y3HS&iznVf2@6(0_&-HtdIMy8?Si+Ldjq@hqA@OmVR2{llQ2IIRO_U)C{?4D}U2 z3K&znPLF=cG$|oYRuJxLWM7GUH#*gYz&<)eYluZ0P;@&_=qwZ#py->JGd9kM$B`jZ z{2_hXWxjvSe6#<78RLU{Z~d^0^)pHFCHoB(sMpL9bg(wQEQ!KuFKNgVeL{{jplpoX zP0)fujTt{&CTm5ZiYk9H<<@6-HChBEl3GRgN^ROr_zNeCP*^(L$SawR6`;7aFfDVo z(Yq84Q%|vF=Cz8(maY*I;Hj6t+F@MU7(U6!Q_h&lBcVA>%X`CplDsub4|}5mvzIUD zkrS=yWL=~`>XhXx#Z4yI5ka?DfU+;b(PLzghGCsK;|rO0qD z{|<`aeg7rjv5F&>Q)|{7T6T*Khk$605EMg+Z*5pBWfPowuaiaP#C}G-(d^lEO3jeW zEBX*3lR^c#wkqYg--u~cv$OV`1HibnAXYky!faDf%R-W`NIM?3Rz3k3W7Yq|fB8R8 zqZzn*D(%M;qplWZ6jQZIK1bAvntGB$HTh@8)G>oR#%}@ny}lk#U%5H+%6i!+FBpzj zZKx>!jf=-<9e)e%9kl-?JrFuLo{hn1>7ZyXl1)Gb)-ES~_9T)UkzesiBM-f+i+>W% z#lg`}!`-fpm4JFe_@z6V@VCiZ_A;qlYRN@BDUpY+C8pHrc04m3jWW#r5I9*-^=k~rWT zN<~Kv*7<}fZbt(u@@Zl$M?YjIX@`TFRB?W^C7Y_vewm5E)Y^fvE!j#J>_v_<;bYMl zHv?Frb6t^s|46}S>6e0KrgeXbAeO}$p6pmvjQ5}woz5GMKTjs!v+P}r5z*P|XDY$o zXAt!L8iK#$^?O9P&KR)>e99i~_qMC8X4DPN=WjNnTz3~htjS|6h5Yfhi_~2b9Jj)> zLPNbszy;RW!(sqq6Z#`@afLSM!7K5{r%wmgzavV>`F%TpW!75)mh7cvx|?JCt>L2x zbtf>GvC$d45e0HP{E_(;+T5!8%!Yh@lRU{5!pUXZ%+izu`NyghqA1^vi>%uvN#Shg zx)U=clYEhXWXe(NMJl+FLomKcg8NN1qbpD(f(lyxqbiUuZl-2_br1G`uD(fv{G;*$ zxAJUfOIfxgjCK>I*y=cvbK0>WOQIL{JL-_KAyB+!5!}m`j2vWsBQ<~SH2Id3*RYkL>u9N$Tkuh2NX8&(h!&(ak^HW639J8!}>OxnyK;rOw(Ps~2Qiw5ii(iaB zstT%HDTPVQcBV{~iBZZvFV~kZNJAv9O67`k5=8l}d|~NG$C3p=P%_9rX)gTh#C>}I zLOn)}=-33&m6B4i$%(UL-PL}}Uaxo$&F48Y%i(;nh$P5ASF~_Fo!QIuywruRi>&{z zd|!M;WW9p=;=(q3aW!w6J2)p07E!8kGcNfAQlhGf_Ux;=j}g9VCZ>|5*4Q#tIZw_m zF7`tQ3)GPclzUKQ-3rOV)duz`<9SkF-Mg05Q;1GUMgj;jIZLSc;0I)+A8CR28Nb0Pg_?ly-|~R-fa1x zBxuq&UtNH8O6BzO;x9N2k@M&K#KbWl-H|i6S^!=;UFGBbo{6$WVd>CJg_EhjR_L!u z=T^RQ(2y^>EOnTYF;-2|ENNFTs&0+O743gPQl9iukU4dphrN`Uy0 zZk9B}yp6+6zFf%{g>sjx-5lo*E2I{nGKB_oK%c6NP*((Cjcy2rs43jAvC#`>Uc17F z_b^K4!)ehQlGN#x&t*sqM!q5 zEPwqldD%rCPhJ;1Osyrk*Rp@V>Lf=CsRIvDod~KF2y_NIEB`{Dhj(b{)7P+@?AZJk z56eFm&JY`OF%oXz;0RSp4PV*HP_3F3ME;41&`VY zNz~W^qNsnHgvXTsZ*p3HqQDfo8s520hA>C88PQP#7Gc1ikU1gltxT0@+u5njOmr=` z&zH*{YRlF@DjQYcpA3B=%KSAHfvY1#?v#Kvj@$?q-lt>CX)qZza%Sw@{Ac!1&+tTZ ze_trUeUi@nv=*P1Pu2%G%b6)Nem-rmXOKh{ljAF;inZQ!h)8aZu^=ehPGTa=6zVdP@4ui2jMs}bv;d*X>8*-} zZ(=UVrZcRe@r>}cL>Hjlrp8oI(-0F)Rx9Ro9?(wzOhI>7qKVyUj;<>7iiZyPjBauM zx=#*cR0Ob{w4wqgrs|GA#Er;w{RAEUC8M655t?O2C(c&fD%Fi7yU-36We;bAV zHllg7rc|wg8Xy9i_65_0lQxFFlT4qN34!&-@9ykB5GtG4oPPeN>1Tn~fT|li{>-|1Vz06N}~?8WEzCmDY~B6&IvXog;iD6q zIXNtp9dNxm2(u13gHwIeM`c)1i)GOJ942a9NL%yn{Co6D$5WNwwlN&APZ@k@lOm(` zOBsgf@_vaf?<=F`Bi@m_UC%{ZcngT%1fnvJs+&KAtnWL~?-jJJC+Q!z>6y5lBu&1q zhkQ|uI9&jcEP*m%G>a6Yna_!>GewmS-;A3cw)$U%cPAib>M>O5Wh7kVA?IY>%~VJM4>*&#u}a(h9d1N*?TXa;Eb@e; z;Q~S}7>Ty~!kK`L{g*4zU}=A;w+;p0Qsp@$)I!$ewDPDEFRhYAf2=(xCvvDu=!D3j zKH<}lu%yxf38_}qk8S^j(mtGyQvND3sya4vZnE=7&UCazKJM%`}_T`@{WI$vSBJ3eA8 zplnDwHP<15^MNI*%xYW;hQZi*k95L4YU?{t1DAiR!@zX@A)5*1D#h*J`c=${ZnC%A zWV&E$YnisY^UA}Wv}zo=)pU4VCupG=%vAy!7l}>7zgeAX`ibxaI)uhW_Ge+%2j)uV zts&X3a~WLYaQJ(iZ_i9NDD-Vxzc4qlFuN_C@Ao|XHfsLFKvNwtYGe!V&XYZnHC4xj zfqFSYzz53yzVk*vkwQ|!3q1-6p1I~IQWBS~!OT1(anTx4WF{u5781Cd%qMvEUop*1 zt~L0emI_rSPE~mMrgP#Nkru?he!*<#PY)iHqtrFn5NDt#!8?r-aP(Y1M;(9Rk6VdJ zbmxctJwV#3v~=(Ne$u=@Depa}&%Xx`@$IA6`#6DuyWR`6yg5R1yAzd`f1u`5qWKT! zMb-}&nPEwH<6pixxZOSszg&=tn7*2fsoluq>SYRmZe-r;IS7wl2%x*n9m*1r^=s5r zOBTHnfBTgdpH6hKKeDe+_+KGL%B@HcHd14MH12%H!+F*uDl{s#NP95fG`Bl~;LBY5(tpUPJc zb5vMd8Q=$P1AOjFH9*bWQcK*YB!)LV0~Wl|$YwS#zTPbu9XDb(O+Y$b?Oeudybo0EB+m&hP9Gfpi_<78$liquHBu+m7Og8 zelHyfatxO_!r)ZZJnG$Bgwx05@W5F=SV?+DZ4YSx(fmK}vnP5LfITI-^vvDaEPDL! z*wD$}DddzJ({gpl`@5L;-fr4B_t$&>l6>BukhQe=drY9{?T+tLFP-2cNAlpKUWkHr z?CZ4w0zTq3CDz@nR23{+B|p(oU*9aF6fUA^>8n>M@*RGy4UBjEt`ji6vF=DFV|SfHMCu>T|2zqo#o{^hFopalDNNvTh>^o ztopdlMs)SsBaNuNJSfx^(No(iDbbUL?M~JS=A|WyT(Moz3*;sNJYv)b+Ab!1g$Lxb zonPGtkk6NMa#mEC+I57+N%rWt=Wc-V{MjvnB&(=-_>B&P`cG?qwEJoD0R5lk_5VtD z^>@~P_@C-O-XhgXRyKo1%~E+up{riZt(XA_Uf>% z;;-V9a!^Ja@= zJy`TY82bDQ3tBW}#U8@sIW^Ro7?^@mhYoLgt^)=rm69f&hp*4+0>-vOSahJO@kKQ5 z(D+fR@hTQ**pW_wKI$|{5Bs#Ai%rWW!DqH#T6UV~e7=J2T@1v$r0&jEN$Pl%IOy<> zsz$;OAs-iZ7*+HCbR1PQJ9iaF3LMeOm7QjYXUFqdfhgjq=>LX;HX>hX|CVwiw*iaJ zKH&lOvT9(Ttn(y(x~-GkpGaL7QJ_mEHA82n#Qu=ljJx$Bhc>AF$MLR&JAZmcD95W5 z5qKsFAJ^q%O8+MPJG-sEGh`~#>cfjAByQfZs>T-{&JP)P|-69g(?7$+GJ?+D5$%5 zRcj7d>VstVDeJr)&Lga)fUW9qJ~>abfm1}uKJpEGIqQm?<>&58>be$FSHSv}RPRE` zJrS@=DiU9den&tdpOTr!fxp^b#Cwl4oY?7pN;-r>N2{K@&4{l=?<|<4^pkwMI1RqS zx^%{3XEyoNyvlm-pkEkzsb|_2KG(yUUe1oYz?lIo<+H$>4y*aZr#s*Q+R2|j?D|>4 zbWBM?dbnx&A^M)CF-$uu6r4*JMDLxZMVb^6xwNUf6Oc}IBt-YE?$DbQR<@^?>F|y; z9KK)m_QU#;DhsiI9jZ}tfEkp)Jzb4uLg=NIU^H7UZUibvt`25yO70Oy({el&S-YB5 ziHY7T@^U^8FY!=qjYK_=6Ze^#K^9%)9WNZHeQikKcuQqDFxM!Nhc0eKPJU~ zNaRI!?R6_sDF+0#R-B0JxspHe%(sxg^!>00vrw4}uh19;t9c_qiSucMt?z0xb3<~s zB7-h~!gxiBtd+syGKwZJt>-_!f&51Ssov?L6$&dh(;l0^U77z$}FI%Oa`aUMOuW>W2Y<)XZ87xG{HqsrB>(LA=hIu?cql|`?T zaE9s5OL18Dx^iB+D}gaoOk4H=`NoyPT9xsO4p!7vUmauPFIUH;`ZDWrX;3#VYqpXb zFInFtH(Ae0cen|WZtvZ+ypx4@JXeC+ol~I&Pnim8dJlKPZXbri1w{AAPF?v!!V~rr zGGfbC00J7!Q1=BjEz{@S9&Qm0f$uUKDQLr^*u-w26y9^NPUkzrZQ@-}f*T4g&MknJ zLm134Bw7BEW1IYtW3XqSbhKpM_=A^KzRSN@WS9CSHw6CPgYt{%nKEOd+p1@-4gTS7 zGYmxj>X>LXwmWMWT1?#_llp?u#~pt3O$6WiJf0IsuBnc5KHf-EopyORg{6CPin4we z58e>6ov!qsNTNeY%phcxmtlf6a!i_&@U-(f&kq2axqAe3!t;S=9?uG%w`+KcVU`Qe zz0S*oV&hPR&PlTTqvNgeqv5IMFbq8RI;DhB&{^fX{EI19Gx9{CX8F<6wAVSe&dV#w z@=qXs)#+Gftw47(){RG=x5_hYp#2(isVc@fnxbNaEro0!6VOda>DCaN2DwJe_sYOX)9N86 z=~|II4YOe~5KMqWzC-GK_f6-cxk?6E+$~^b4JC9D34NV5;R6HKdxFm!0}A!!23)6;Kg^r_-fn^Mn-o&(0!Ksy>95yBA?{Wc z7^Ddg5rDF#C(V@p$r620W{fVCY|KQa-P8$ob1XKh%SJI`h1YC%x(}DW?{yyC0iIZ@ zYI$vbh1b+lG-|#_*W@I~o4)Ekmd4Un-SMN+S6#T$!{50tw!>c!HOIc%4mIPyrVXx% z3OqDDkXh69PXMW~VcFoUK^|#a*rD6qx){@$%G1eq=UV7cC&Nx#)L9$!qQ3l;P8KyV zmA$kli|YmW43_?BaF3&c5<9-bXD2;ebf0APr(z>Jq_2H!O zSY(BF>jYdPm?(VNFZ-1?mIWAuWywFtqEHF(m(7 z&QC!wgtFVdpvcr(i2ojT0u65r#}off<>l~UN`>U@r9^UU{C<7&LE?&#JR02$hmrYa z<)tOWp6If$7l~YV3BJ`V1!NllC~DLsc=CJ@Eg98eNn4bh5uZRujnPP* zWQaw)cC_7`G$W?jPHp=Jj#7zv?W;B<(XF(N{6++~!-%VFP;K;J44uGi&|0Qp}x<#Ia z@X^{Od;;rry&@3_Lm@ku6j`sNP}Z}>kV^exSPFnCD5I5SY*0%5$Csd&3&EX!-K`a_bNH$=#R)_+{D#a#9S&C8+gA`dAu8V zt@^E*x(~mBcR+t!&PQ(Pt&pE~H}Kr=GWl&c@RXhN1q>l*-P}Z13(=GoI2pqkc>C*k zZ>*DxN8viPz5(mC&TGa2t`<9g{L2qXZwJQU;|!dr;gZdXW4E9n@<-Yfy(3P|)s#NQ zyOTI{FsbyMy0q%a-zhCdi&sAJLo)t*UQ%7U`4qVUI9J~Qgk`urJ^5sA0Ip|Vgwv(n z0L;bLlS+}}d4h8ho6>PUI3zBa((d;yko$dj+N%p+5IZ{a+r$4}j4OPA7D2ov2+Yptrqdf*8vi!DvvJzSc)d$=^^ zzqaktP@eNZUbYaJ0!qohz5drFE@^m9`mMy_?niK6^e6~6|Kz{=vXCeoQ~D+_`|d~h zlx(onO(IvXW7=a0=aLNVa_k$xZmsNby(M~%QxY4?t-ZQYX~F|ebkv1)LtJ-_`Uq#1 z4+vNhr2vtYBsw+U(pNGLO32PbvB+Vatrd$2hA6_A{PVTEk{1#O;<_DiF1W|JDE|PL zQCj3mqfB0l^E9#6#59uKU@4POf{smjzmRB1#}W0^X>NpHstIB@Xt76BjMBZGD;w!< z8fW$17VG#nKb0V_vWNIgh@1chg1e+(M*N3iLOIjm>3#19JBDK}oi5leHbiChS}`Zk zPs?w{kZc(&Ri31ZKuHIvO580i!^7yWtFOR3&6kfjLB`wVyDQH|v**;hmyosgY^Hbj z7}?T!*UsZo=&x35FiKF=LIqE~tA^+LjNTJwkz6Io{Pv@}tm!LqoQucEq!HtWV}`p| zk*b6|QL}5IFwNdWX}qR#F&=>>8M@9VRQ>&c_1}Q?gYz((5hB=GUn`a3WN}*p)crzB zsWt>bZf5mwX=vTQ}^tE#g_qNofU&R*N z=E=R!%F&Is|Mgp&hcV43JOIUA< z+HRpJZy4Um>VSr!6>Y`AfzR6J>~2-J=Ce_2ihuH?Q$z!GkRH)F!Kl*wtbk zU`RgeP3ZK&$Ml#;7KmCq^hMP_J*upNCn24D^x_a=2wJg3FJ4|&FYD(rpIlmfl+Gpk z0~>W)THVR?lkEIXrhZ!o*aiqROfe|3x+U?c?8q1xW)bQ0hCiB5`~>aEQlp!{rbE?H zRrOBCzlpJan0Vl@wr?VX%5acBBG zI;ZgLkaM=%-;o;0Bw6rmKeJJ0``>XcCXg?eBL*|@1R?4+N}!Sf^u(8ZM}p%Kp9V-v zT%(=Na=_+IREk8T&TqNggghjINlv{Im>=+!JVbcmN@Y;y4+5s&Cg*UH zuUrHOq`7*hmaEIxC6Z(5tkj*pn7)~gEj&k&(D_wT1gk?Ka1asbhNbum`;;C|?YU|- zlhLvzqgu>|;}-%M!e!b3!V}A^jcPR$Pxzp0TVI2S0v5HLmprMF??f&OvNhMx1h?B` zPQ3MdSBKUvv~GHs^5NYMUzK+yj!cg!)sWY0Z!f z5jN#6c@9k`IJEa~)1xHSrG`)bSBI3UZJeLqVP&0t-@a5hMT`{m4akiteY#6OT8sx| zs6NK(W3WOJ{!Ytku!-~Jg6Tmu+gp&x3D4p@@`#?{^7iCq!b7U#qE1+(I!>=Is;AeZ z+o%Lr>$uC1T3_GcaZBH-I;`te_qKH%tykKTDrq}WQTzpDNa3dsH1*}wh5D&Q)&aGG z)@uyK_UADSa7XPjP?O?UX*1*@_D?;klRPZqROthu0_|&wpHH7fqId-I>`T6zxWA3R z$etN!9rCtSeS+FuX=L2|$S~Gnufz~!)yTLT^TEs?w2t~1dmq(hj+ zB%QxtMnGqNr@ln9w=Hx#lc;Tvf7}TrGC5j22%x?ihep(Otl@7E-#Ox9sV_#+F~4kR z@DQkyLIvV~>3A`wIi2?C%ZvsrkM=o_B^8QURNmJp>)^Ov29ut z8D}rm^ZQ4F!MpSO0FN}2%+q!Dn|Ex=M{7@N;I`9e#7Re# zYo{{jARe3qyj?>h#Yrq1A{-ghBrL>ha?9DONnBF#Ni})!c4<--RBmF8+P{g$TqJH0 z?H}PK51?f67$KSP8M_tQ5^^vv{lGBqET7L8u&58>oJlRe03I>i73@N1F!D@l z4I204iQuo-VZgd%DBxL@z7XBv*JhF}j@k~LehD}eqIP=zH|Y}hiN1D5&?S2Ffc~Vq zGfj0T@hflQnPj6@JhR6qC|LTNgdPwozJ*;!vVqIaO$h(_vOw6=#lL1}W+q9q-i&`L z&+Qf*y8O0(!Z&NajrLo*AoN!6*4=#!6<&uXn*|t-MedRE>F^oLorKI|w=!Gr= zY=2^&AWzyUc-sCj4Xm$!MraC36Fi2PzLy2rL_rcAHjNq+eHFe) zn8JoYWK({i_{~LwVH=}yAW|gdKatsEW@3}aZyQFlpLy-QqT3@X;_Hi8A(LjMwRbE96CY7AccrtRrkr@JNcpCa`=bx* z#Ax=tJg_*uuHnlerJpZqoDoOkfs6hQ@&Wv1x|Rm$q#Qg21X}ah=yNLUv%4~@2Dm)E zjd_S{Yep8E86hOfot%XtXtmD~zdaX^v9GaIg4T?N(7C?If?&qti7L19leqm7@X4M5 z`=b8vs(@WOD^NM90yN1A+Cy`K_QIT;#DD-|`5M8^NHLM2WMd;B>&hC}@nqm8%dHvz zNDN>C`I6#Ss#c?R0tIA8(k;MIXJUwu#oBH?goVs4i2Rfq_i@!LAou{c%4TiFN>3ML z^P#3p97ly7|C5Adoe!m^6tySk6Yn5;X$}Z0UI#0ig3+vtE~vaD2F$irZJiwL7b~~B z1TFt+zdb%Lu(b8kk$qSQQ`dxU^DS+i#*0xqjbTYBZ{jRQNG^<3%l*|p_ zs`r@Gc(@&zZx(aJe*?L>CK>vV9Xd9*wxw!MIUdW)?MWr&xZL#HxljAYA+tIqALS8D<|X= zx|2omsuAT_f#F|KFu8K!Nx29$RojD^p9YKH4sB&JbC|gjTrd<)ut%<)FebD=YY_^j zu0kOYq97smqB_%>TJ6kPD?7QV)j@kI%yvct>`;Xkm@sDK{)NkR$)&oaQ4^-OUTJ4E zCDTgPOy^_#Trk|vhIAZ5?-mic(+mW4+dD&QZ5#N&++Itp3fZ$A58od{;=Gh@ejgMt`hm6%)a@Z z_;xtW_%UVmS;p^G{lBIDGUNFvnPxIpZZ!pxb>-Fue@$bk(m%RK=yPB7zEepK8fCG7 zQMR9)hJ38>5gb^vp$MW&wMM9xe=o)MVBUTqqAZWPCQ@uFSKByp2AD!31kOtBqe;`DOo5M4 z^BdA-fPqSU&qXUTX|O_#hz%?>?O15qu{jFOCXjQ?HfDpwZJ z!f-kS(EZf`6!ab>|E%+^poxb)hytaBqYeY33qs)wvpx7t1zQ_D7=paA0GjoOD;r3&QVSYKbfHPtTF)LY}~aPT!pgS{RF?%Yt){$pSdTH-DmOpNs_{SJ6YAzS%?Ud zqxhnQ9l}Dyu!ne^V!lJa285^FgFE;ayyK|UnZvwweUkcOz|1O2M zZgHp5sIjTYo65uRR`?w<@fXf$w8q}D@YyU2u>#$T$R;a;(BpOCle~GHDoZDorx{k1 z#c_q8d4{YcwSdgZ4OsD*;Id+b?o#TJ_v0(*-pP6e33vjPD&n86E;S18a`_{Nk5jc9rf@SfYv?yiW2-Lv^y z$s{p_nvB}%03N4|Qa(+b7F&cHOzS0)%5YRES0`8`p*8j>Qsg~#!shY zYpPUyhxIJ}HoClE^tnRmN~Ht}LOm(AWUt@K5!t4!6BZ^nnw1BFnR^43@0j*;(q(5g z-vX7f@tex9LN{$aD%nq{9YcYbPTFdW&QxhQ8kO4d)fgTTe?#Oibp7`I0t3ZkoeH|p zmI;qyvKNo`{Ok4VQi`=P*&A2c_mSv~nT?N2wcl}P1JSMpfCq^gQ7%hIj7bsiW-6KP zBVO9gE3pJB%IwM0#lp1aL%N0*)GtEvrR4PCTZw5lozE-hl+s><3BPqJo{Z7R>_a*DgjHem`m5q4MkBnWe)__=``jRNI=T!vl zOY?wI2z#q5D(I!{Z;~Lb6Z0um{*FKMgYwGy$#xd!L2@yTBP4__Hiba~vm`mH+Yg$S zqtP*a{AYTyF42_=e8y9o%#}U_a)~%_72b=(m1vJ~_#f-eY=iP_JJ(k$u*g-YX!fhu zi4U0fwV{%zq46Lk#(XWbWI$cwr1IarYpj=i12S{{T`X;a;rXIp|l}{jxsFH zp&@U7cdcpMvO)gy=C#I64c7*YTk7QPE#8{0HNLL9*0_z|+ZwJlepzp9+VHWlY4dX9 zl7=#);w^=0en6Gk-fE}J?o(z>1%b92caLW?pv|yNtGofTZvF-BD-i}i3=Ip|KQ{#r z2G+I^FHU1DcReGAl1A-UWQ}K0y1cfLkLZoyqL?i1gR zEW=v?Z$`}u8OG;2!bO@j`iVyhZT03nfe zL!-slI=+`a3!Qm^opmhRu_sE$lzo}CaB$*0Tu71Ut58vZBZtZM&|Q*kDCjseD>R6Z zI>R$USrV4UtZXT_#x^H65$IXp_bjixH9uJV-6A6p*_dx8Hx_R(?lM6kejl!5u^EpN zp1!EAdOxBbrpg&F$UuE+q>4Vs_{uAl*c9904C$t`@{J<>UwkWIk&4rH@zp235sK5$P z!*hzZw%eb=uuWg|GPXQBXuqYrvL%rD-UK_en)X|y{kf|BEqwegP`M*Ac4bzW4QJ_0 z1iYqT<(tXaBnwH8{X#NBx?m$z>9s`t={+qhAYb%c-uyf;-Dw;j161A(Cc_=dxqeiy2 zu-E}duoEF?=5AFjt;3lY*iVbWa6;&d9Ou29Ldni~Zk2@|)t z^cpKIkjltY)Z*M*CJ2T@^jLS+G>;eO_^Qua7@naNHmUK5>Nq?#klYZoF2?i&iK(p? zvDgZ#YA6fufp;_|hA9)3_-oQoG`sv4N{cAk)%Y4|##&GySz$VJg7)!r|8FRg{3Eub zI?TaMd;LR0&{2=`{~ntO*M&S55x%v(D9i6O-=#NX_%7n_V&AkIGWcKDSyF*>|57<* zc!g7e`2(m_M&zE)N=Y%Qxc9bwNi`UbSAYc@D}3`6#FV}|CWkcw7w4aVOLB$K*GJD zO=kV+T!lHJqIcEuC53zgV=^G}87RtwmI*3fT)|?t=CUcz+N_KYI)_d;Oh6NDSk!+0 z)6p2tiQ;E)84?(lpnBqV=!qM5@57zX*hOl_m@8(Cd25@oPnUQzR;8VyXq%f(yVnN& z`0Js4?e;gw$Q1}oTR{S^tLmtoZei;BgFO*NxpDU&fr&C*ytyH9x(bjfu#$+Glw(`% z4|@DNp!N08spv_vbnNm9_{lyYzn@9$@chj1M?O1uDDgl;Z%2s7x+P#=GPJ;CrxxB= zW{=N7XINsa@IHWQw`#nSWv?hYL<%4P;nOKi1?kn2XycI}i z=>JWaS)llhMNbFp+Y0R939TGHgg=XwozSYpBm0sQn$;mAa;k(sH|^YpGP~@AgJpK^ z31Z5(=S+dhg!}|bzOh4zo!o#_EM3sKvl?Rhj`Gta3w+C1KUkSX$IZBcgOAhG&QG9R zzT~m0Xe>);o`N=vR(e_d+M+WA!i?twISX@qk)LLVcY6VNx~h_k3xz)ai|52M($+Sn z1?v~gPp@gfriz>K0xsk3?bI!5i6k@Pr^=gJ;mKcxb}+jDO4H26O)jWd+ual$ZN zr5KiG#Pj6^92HqiMkyc@NK6x`Z%yR0VpH@QerFbMFlwF`XxKz~1gr_Oaxtf^$;G=- zG!+1_@EK6Vl9pIzsA*DkY>62ednhA}Tdk3Y2yK}$e;qLhGpe48O~?^!N&LfS?ei`D zgi$UDMyCy@Xxe!~0OuKj$cDo5;`-1(1CiUeWrPR%BDXsk^ZWC&E2FBHFE)Oc)Z@Z8 zayzhW^s!#%ZzM-+M%9btP&1HtMvrW<-`Zfk7unNQLWxIuFyBSVXEJ3ZXPVKm`=kST zZR@ozNEp}tlkMD?h&*5WV!m7fZQ^q-C)jAG;KyUAA~rFX|AILk;-$%VIT_`X8d;c^ zmr=E2vON%F{xI||Csnx_;Vq~%RLkfx6ZI>KFxpFoxwYD5VU17kE85K;I(NpSeb;hu zr;LCTktMmA;m_k0Kym4AB#(uIA@-tTVneF7C|80Gr*{AL z%n{wt5ci(5qFX|NmM4B9Xgx)-IGh8;By=i(%3`=lj#4HfwnuzF-8Xk>mC zF<6Bci4MxFtja;Z+rZBkz9sb6Cz4%;|F}ANiF}y! z9o1ZBWvx)6_xv1AVT$)Jd^>T*5xThlCe_80q>J2Sh+j${#J1Sa#$kTG7zVh#Flw?{ zr|K4j>Y;q|dT7-2Q{S-Qo3R4vze)6wT)tSz$q2mR6h4aSZ-TpReqpVv%dD}hF$uP| zOJ*3zO2unU&PKNw(ftg^dK=Yo0lSLkNR@!*3aY?k47^bL#OLtAo`v#$>KbCC5?e-2 zSyr-QThVJddma}1yzp<3CW6HW!k=+`utv9BE-fz?vyt5oqE1D>QMp-~4_IYyXsxVp z28g)o7Ir}NHLYuRT?2FyGmZetr*0Hb4%#+NfwC@s8)DB9vexLV|0FA&N%j@a6rVtT zS^EAJvS)0Kjz>|o+^lRit+!tgPUQRtEL0niFQAnH!SER}E})%5H8Y+t?*1iJn3d%# z%t|>xxnH!V=pkpxkMR@=ll!aWF`E0XpbR>U>Y}Ys*SN(SYx)GY=csCA(y`)_AKCf! zLmr=GFZ^Duc7n37XAffsjd?JUzd#%LWsf1&GDjHkZ*GvQE;F8>Iq5Q*L7}I@FDuO) zvslf75#7hY5OV75k-tW7LVqPwh!SQV)b%Z&jmhdaqPkj+#>;2*vpEIL&sh94C(j@P zQr{}0=2TF{ci-p zR)B5X(E{N%m%b$6mR*Oj>~l%57gZ~Wi9n>xy5#y$xZ1R8r<2hc_H)t1-!IPsD&Vx5 z4viLgapJowXsc0}m5mIy&d$Aae6-)uX606&9lAFwSuaKr11ajWg7;>L&X1e=<*P5S zgT<{JoKNpM<1VxE?SPfLHGYV`$h1o*mBBm$a8apMW>ZWSQj<|r4dBYH{pD5`UGcAC z4FoE2=BmsW4jIvLlnijGDp;9&Ed07^C#>#Rq_euvTY*X;Bn@We*k$E}%agRDl%Doe z4xr1iH?jS7Xpfc$qq)XJ`x2XDDABrA`-;`7qfu!v6io=EHnH?U%pWq6eOc}0#fQS*cnhj2*%y?o;D9hzA{#e!Z(C6>M6=JOvY_?8 zI&OM`%1=@6JM}75|80=}`q#)cs+(6X?(ef=oGjAJ=c<`k2o@9mhQsT)x|LTl8iZoZ zRe6Hjh!>0EqRNLZ;W=QZaPCpBUWESBUidM~=NN?VK=ICSGYvmUvcM|YH{OC~pKf|J zDY@x@^x1j`afky>=A|T02%KXcEv;dmCVM6LC~B2YrFA(NF1L0wz-j*b>Rdqf3 z2_!IJ-~@?^iV!7KP*kF*L>^|$3`}4GK|rvAO^s4~W0(PySHdKP)8i=hRr+gHZtX*V zzqV4XiZ4t6li)K@tq)YdS~=tRz(*kg$=vVS=bU+j3F^J~&&!9&IcLB3+H0@9_S$Q& zMY-krw*XtTJQ>8jHafK;(O^d{dRa0vq|vNJ6kWbCSVmQ9GQna>Wd*9TrTtPXW3pJ4 z6-Z^&^yxG?rjXW18B&Y1pt+<#N^dS1Adi4P+aJ&?DgyfaO5H!3gllzw37H=yGu>~X zfJn~o_)u!2QdqRAO{T!|mgr;K3E!B)9DFh zZ6a~8la#zq@k^ri6-ewfm$N8MW|uh%-ifG?sv91+EAdn(&GfGQ<+di`tFe~R(rDMyAo0hR7A$26@IVWqS)A0YcLLxc;ix)&5 z!c>U%alAnIT~ev*2-b~&jQ|5-@>^)RBmiL_WIYU^l?*?$uGuHdiObd@_ABl{;gw!L zTJp<93Pq(js45|V(*Q=0+=>YKo0S_iDFOZA_3~`?)yTtNwQ?BkS#b{i_;xCdW&LO9 z^!g=Ih>)~XX_&1x*iF<1XI_@)iWO6$q>v(FBRI*L&4(bGRws>4mv@ApmnL$QIS9!B zr%RGnF-d*Zw{IN_+nO3-_wqx@(bb&u?5#P{=;@0Jtu$-c; zTEPBgWq4T!+pFVcyX7}75N(*y5ZQOFIEC?Ru9w3_X(}e{Z-0fS^lKtv&GlpLm_!?I zqjDp11HmFlm_`fnMWVi0-7Lt~tA)Wn0rl%oNPjr$bffOZBUgVymL>ZXRJ%jyvU3uu z)%BxzPTiJB3fU&@mRLE(9spRQJNTk)YhGi;Pwqc00K^+~1qd5}V>|=2$S5hsz;zq!2k;bq%_0{U(vAlT)3z<5b1V`EiDm+zrih;*VO)gz7k*NR)0JzxZ0%PwwNd0$aO z@k?^vI+ztZS%0~44P^Est!|t!BF1vY%g1>2WU$q3oUCR>OL!T7B=m-P6Sse)4{`;W zZ8|shev3AtK>Rn1sM7B^k!GuXnn-_8Tw3!eG4hT|?Hv#C&WuCDpxmuA40PqrudK?a z^b@e48nHSAm%T-C7S^!Hx!~jKTlrhi>i)=wky|?^uo*=u<^RY+J>y;(pZMnDX#-ZA z;*TigG}oL(+Z;SyL{5YQxok4Ha-^!$8_g9R zA=vECdP`pppndCV#*PL~bBL28<9WdwYhp@!7^so*uxG5@u6-j?+Jz|@ zbeOE{y2>s!P$%`Jjln1Cs+_haOh>FS7%*MXTIK zK8*a@F*BMNvurAl-3-tFIF%s=KrKqq8H;1Gba?JuN)KXp=`+_T@G81!%xu*Ht#Y(C z5Z2W}92Wm1;{1Yo;<>d73Bz&@wT_h0VFI4bdOpf8K1cZ_C0lTotpw8vJ(J@|sc5XK z<7UxY^qTK*L+dE0BtPp03hOk?bcvOeuWC}5Qr`=?V~B&V%REq`H! zRm$$GL!;D6{Emt?R6F7QP6)TfLnp8$?~g+T;Ge6sGVq|l*vdv))_|0awADZEoyMal z^T+z+B_7S?&su3!FD!vjXt3v&jS`W5TFY^d?6~L9h}7dAY{#&`S;svhw9n&L$uCV> z0pGHKbaL$Dwx5bwQq^!2ppJKbu~#OZZ4_aZDmHnxW1nY~TEz&rAcT7fsVs(MK5S$l za@Lc6hT%k6-xWVHQ({pT5Y2J4#xJ({kv*OHk(uz234UazE4EoRELJTCCI3SI#`>(X zxs+*CqOPh8(BVPk!jWEDA~#obDw8FjvyJ9b*{vYmoH$l5T_EL%tPZZ4W2O=d1f><~ zNmLD0a5vniFUF!v+5vr%{tH{e-LOc`&WY&*P)jL^Ba0d8g(0x*7e;EyhQOVC5dg0C7C z*<+?L)@ja7E+oc)VI6m$w{g3& z$>aVY^g4jZFE?%*P(EhGfS`Y^1&_CbPkD`HgE?%9FZqlC7%T(uk*|2EJM=+$@mIkD zpK)8ZZ_JA9(CeOXJSVt`zuiJx2pvJ_h|tqMV_~j$OmJ{+=&!uy$!lKdp=R%3)y3Ga z1wp2;AlnEdYON>R`_%$WB4h9lXw!n3e-U%P z%EpAV)MlR$TIkI+>te%_^VLqsfXEi#y1HU2A>3mMzseajct#6dw!4Dw`Xd9kTVO&q z&aOnfJaO}$>=8`O=)R4f7xGm-p|!Em^VZSoBJ;_BExs4^NI{kN3-f z2zG|s^0^qHhS%4Ec&}h`m3Q1XbZE)jla1iGPt~$7%dy`WW0|tP@ls(IgHNOOi89K# zZ$ft(rMdFf7OeLh-6tCXa&g6@PL_TO((07joJd`-BE&*fu5j=Ux@vV{3I}Didar5) zXbZ~j_x+t!zlJC?vPLni^&^Om7VrmM;|3+jHbymaQ6XS-hoXD+56!PH7dnb(#R(OY zs{-@_4M?I$zt%i(1wyHMq>|UH)9Mex$JALh;){<3Exay3>ALPBW3^XPVT>vtxZO2=zHwH zoxSlE$g;=`ZgT1qAx5w`ycAL38Q^;=ewT_IW1smr!)NWsnf%s%9JJ0E;gkSRX$cZe zazgx0h}#K~)m*Iv`%CP8s$KlW;ia=(D@IZ;9M(>v(&%?-rPMD+sWQJxXG`r-+FwcS zf3?bqi)dY%rt?DY*p`RaJK||7VV7wo&w6nj0i$f+|_YF9m&b3 zjPQ|Z{Nqe2W&~aP$>Q{GteEMiF=!{a!wrW)6m9j# zvel3DDOSRt>{@?TOMEvSaLa&DAUcB}?sbyLE-m^H9|5Dk*SNyn?5H69!&X7seXCMI z+RUSC6{P8+hLo;?bgZ~-K?TY49AsJcO!~`kSQM}DLMbyTH6~aAv&W_F@#vq4rkjX= ze#`@j(sb47P=NM^PNJ=1R`$0$q48oJx0wOd>Z-}ZV(fGz4J<`{WO-dWj6pW>+8vKh zCUI(673pRDNa_cSdSiG`2h!Sbdv*vjHvge;b0|qwg?OZ3q_z^KjFNXAisy5!GC1_| zJdwpU_R#58m*CYV{l2KY2l?`d48$+EpffG|!Wbg>OKpd(q-b~ zNX%6TWAa#7w%g_KEk#YOJ1z1?mY}BgMXH+GK~Gk*Qd4V3O-&htgC5;;5tI~BG8!!j zH8riSQ9+}W(4|sGY^e>d58h6|n+>Zp7BAO;Ee+ncs(ZA+T6FS+I;@2Nk+{>aTuL(0 zp#IvUt(-8D>+&V^BH5|PnanzoetY!3k!e}_^syXM1@uuZOqZyK_N8=KF0FVYm0~DA zwZj<7->N#ihYmjns)2Gj4;8BpSEicC*Xmj&QZFU6N{wV$)hpa{YLS_*DxX6?*1Sj} zufB%{ub7aF()YG`S_g{X|IBSssFgcAczpV9 zn<8F7P7BLUEsx9_U1p3bR4F=7*S?Yty#*R3*2dI4k*OAa2#{wcRacZ_3Q&4|{|NtMFm(+_FJGYlUF-Niu1l`t?C!L>+sWmr84m?n#T!Ik44|0TUo7`|xRJ5f ztmL&^F4g!NyjdoCS#stO$=Pv%;uU4}cu-aRA_rg%mqfCQ#5PYA7Unod|5(xZ&F1*= zd9KnX?@-q=4|hkZz71r3QC5ZMjhcoKg)sdMPm?z%%N09DsvLBiFc&YD>o*TzVx*^&}RvIfo44H1&1%b7H~pPBVl>W;VizIgcB}hh;5BXO3(m#8+1!CG?Q+x z%070IYmqV}M$V4s6IZA*^H`i)r3}#X`#=sgMo+vg^d^$?$oc8RBr5oshN%Gds)XN2 zVYfzHn;a<ablyfYEw@7i+Zc;Z2R!6LNlHJ~J*cV2|>u2CUvBCUwjP(GFNa6W%txLJqqVJn|ocNjg<3xGI?;jx#*%i z*Ae!QM$7C>vfy}OktOFIGyo8}tlR9tD~o%FRxex%myq*(=w`@0kk7OSNUGKE19h>= zA_f`6)EyHXEtlmjobg_No{c@Stx>F#m{mdb{^;!br}HfQVZ*#Z+ErHz;A^;su*_UL z#>SMmS`pZ95*jnB)Bm_HV#_dKKjDsMWuSeYLS@Ym|C=#;KwOOfPki8pb#!=IhT{Yg(b0{ zru6L&iuk^LJG02@TS@1>1t+5`R#+navg(52faj6^I4n<@KBsV4c=;O)S!iR(LyLv$ zdMAzR>P~xOKN4N|OdFJId}68TfO+_fBq<)9GVR#x*U=eU4dAlr#?;xy#5u(+%h@+i zt)$c2R`laa2Dcy<<#AU)2!8C^XN9GT4d8aq_E^$E(eQV(UgjkClD8qj7mSo^dK`}wFG!#B_xkgw9CUpbqN!5le7ZqD8=3>T+hv*xI~4Z_-}wIzMGwa#nXw^NCZ zvOkqe18R%9KR5%j5M1skPG8Mk&jNpBVpbqBaOKoU{=29=w}}$63+qk1n}`mx7L6|} zzI0#cGoSI}Ht(3r^Fsgf6<_jg=zU*t@6g-Pmw|UzTh2SLKNZacV?sVt=O#ba;-bPL zbd~xxQX}X&mB$a-px!dY+Mt5W`Z!t>oyZ@sbF_V1Wi+3N?prVfF0`#ofA#2hSJ>;M zCiZNSGb;x1ma}CL;RIF`c(HAO4mGL{W%hC=X7<#6c47hNIQbr9C2CYrFS8GBmQx1Q zw$PZ#UpYz7Z^eo5CHjn3_XOSBnma)cwB}vX>}?h4t}EWGuNdIPeu&{2W=tDVyjc*> zGctB1{rQpV&*B^GnWiq?Y_e|NbjX&biJLbEWlLkXxA81#FD%5ZBdxh6N+mF|XR_mx zsWwuk8oSXMg(J{)^XjA4#yuloO;Qz=GxE;qX`=luX;VqZVbz3~{ zEk5@@LdW{pq{*tolJtNiO1QyGNKyn-S%-Z3D_&(`F~se5gamH_{tri_vN6FG&Z(mu zIcAg?U0dm}Fmbbdfm|}<22Z`}_ z;2;|>Dg8Z#y_i;em)Vh|{ZFxJAUQyQB2N+GFXF?J*cBGXZZ|}}IQ}wQ-Q}2Qk z=oW!1M;lYe$~N;DeU^}Xzdn#v$qeW>J4C-Fbn%h>jN3=;RQc5K1e<*b43K?M);oInD;Ox?#Fkx=8tAR&up(unV4!28 zK$xcn(eu`bfOmSu;hlb_c&EK)j~f$0SFLWnDq47_iL*VpRzOVYVjtwuC+5l#qdw7| zJ@Z5!wERb$*i^Q3b`BGM zVnyn@YB_OtR#l%dZ%(=x!=qoOIHX;Iy80f!I`X12pqkGWv*oR>kesKOZ5We=J8e}{ zo zU1l*uV>v$*Ly8j+Zg&CGTgM8o6F2*zSfLO8PlewLpNt=6z^5pK|6TY5O0;Ne>e=xxK%L9qPZIq z2!*itprX5u*z02Unn%N@x!;XXTZgJ{q~Q~`;*Ww)P}*|+rmoTH4BY+-I&JxEJA|F!_bjQh5!hX7~@9UU%?#_K~XA+2_%8uYrZn1iv7DI%d@H^0g?M{oN zEq}&BHf@PrjCp*!E$?7XTiPWWZJRQ)Q2~6tH{5T z+Y;|^R5cmw6xdK6c|(VTl7vwS77Ad~te({_+vH16f+v;BF0Oqy08T zkY@lxrN3?MSY@<2zOqKENZIO)n`dJ~TzYj12n=Ci#KgrD@_Hwe`3gBu-s*CEoDoU1 zXGiE1yYTG|R^ireE?It}@Z^|BuaSnOpg>~$k0?K(p3Yp!*k1)ckN%GIw{NHZj<=h1 zM1P%3=6hd$cYiJYt^Dvx&hWTjc*y)2U{qEFda3oqeFc6)5LzoWt>_ue$f48vW77i< zXG^NyRC^Lmf0ccH?gDZ04t57nC~aK!V^+@L*2=kv4cF06G;sZUAaSViT7f#$m~S0w zWLx{4bK&d>sq)LI82UtKaGTm3d$njwO`f08?Axif>BHjG6d8=Z1&iFNM&Ho?Q$jIs z@1&u8N1h=1C3W>oQAGu96`!I8lzf&5D6}V!AALgTB<;z|vP4}7W3ebB>95!A2$``f zM4z$dAkA*a-OFqI4v?J7tCMPq&j|S}{e+r>%YrA{*~zspc3QeT;IUB`I-#XVa%n6|_i;@mSpbj5iOGy~}^%@IDdCmH?8sraFBRAShC)0P<;#<6|>NN(DNPKgN&VOWVi*Gm zHW!ZKh^2s08!gg(wDBmdnPb>I$W~0wGBChA(iK|Ng`i~U1G7X>a#q{rnFS>? zJTt{Cg*wTfk!qU450g!kD!Vo&G69p{vR68rb1Uhl_9Sfz_71l#3+9B|hKB-jBwNNo z0PZYE_SUnC@@aK9fDD#37FOh4>NHfm(OA?*DK}W4a9l7%=Y9r{!0p4yBdzr2pFdE# z1#9|Il9+EnPgcQ2$cOCxLWsBOqtV9(#N#UKl;8pTi~pzQ^)I$W2(jQ z1-x{mIcX=rc6zOr5C@v@I7yB{>EkeGn|-Znhh(gDb0wtJyy9l-N{ARU*Bfj1a0a+C z+&)aJ8wB(s{y&7OdV^oxvv5I)h-%bU?G<20 zasnr(^84rmQt8o820onPF#M$>(a*CQiIH5nxANpoRwI8(s33=qK#HR|e^#kG`X?&P zIILU)e0YWu9exacWEy14yiNw)mJ8r%k3J(todee^HiWb%dt+y+{l_bFfm*OA+bO9^!aqvk3mQ6M+P%N zHx*m`&>Pj1!;MRAW}Lm|Y@6?j&IPmVHZhF#C#DbMSO)&+!+2(Sau}uGW7z)N#+K%4 z_3D_yGOiT;T#nNWEG&`g4^LUVQUT=LA{90xmO?7bl4vqR@cU%YREDa8fN_0-aKuZ4 z%$5R>23CF-hM{g_*^&k-<4|G|YCJ1sf?p9j6B?GV2c^n!GDFYIv372;B^5+xMbwd& z7Q`n92^g-`KS4Ei=ICj_E;ID^j$oMVDI9%^)A&rLqAt8t;lN3tbl13?0SB%VS4>cM zaDWDg<#hd4s>s}uxwIrYGjj{Z{c2j~s%Igc4}+meV^x93ao3SJ-2&1caU*loWoDPS zMvUa-osf!j!P286+)I;^2!|5jNc=2}hDLxz{*r)RRS^9-BrS7qZj~e>b~KFOM_#=2QSv@D_Q8( z!p8{J;m;er=!-GsqF&C_>dsUzXK3|ByeR7g(L&w=n9C06(rlbu;`_)z1IzrhA)^@!5$}73cXO(DA#(i1)Cq}ZL^gMkIXRw~(?cwHDAIjTP z+qJ1*dGron796K6Tcf>^iLF?+^VGEUu6{n;k;Szqu@(61&2`&@gQM#xH@V(P26s)P zq_1i3UH!S780nicVDz{lpa`qCpNHtot3#}!ZRiUo?4Ql@z^K^v&#;7+w5D}0zG-Y+ zI>jE_L=g$C!?nW`G`bs_yJJUa^{>#>vdARve@qtAeM#0ckh_&*ms?KcBYq2jYa~^8 za{_?0%;}#EzS&s9&)V+=c$)-v!<)hlt)Ak2+SEpE>OsB9n}tCv%SfCzQo_aY%?fmz zkpuub2XcjF@s~XMf!IYU^v21SEt$sluKt4FiRCdoUwtzb_6Ayc`QsMBQYPq@hc4nU zH-HpHHU@M`e=GVcn*R)WT(O*IMEg*m=4LQtC_Ak^83twO@2CYP@^K!SM#SQaEHabE zM0Uq@&(UXhmbimuO8|e3J|S@DHir*;`iRTrWXh{A;Amw@o-&A%;GdlNqxKtEV#5KI z%pZ46`<0=gcCWZ=0!Z&^(B(qJF7q8$9Jp;#TBz7QSF8I26<93eZ+NR|KP%LuW=Yl+ zxYZ=X!(@nI*#!+EVAO2+xs!;MG*U`()~`@6o0i9BX8_&#PX!c#STZFu=(;rK*8;<6 zf$j&HhWi^%YZLk&3O-^$Q?Mf3HYa#dxb18VzSCPaB)}XUoxZ}gt;ohT&u}P*F&$6E zpStPi)AXMi*uagQXf`or7(x+9;J2YV%M+QfE8I3$Tm2}g#9nBpJRFtOMA34(ZWa9+ zZ=P^R5S1Y7tr}G>>+L43eyzN1LbK5ot>-lp6nz#H&n^FfP+T?Afug31dTK_JZ%qIZ zvqh_ik{*4>yu@N-8Gp9Cp|R$OuhKmJ^pKYd`BNf)M#>*`7*pv*B*+K4=L<(hMah)% zrYFl2$rQ3dJTU-+}O$M-asj1Z{Bsxx-W{6nD# zVHb{!U79(*o_J@ai=FM+gzB`5BjHWxN2VeIgOBK*8;4(B=Q97stOQf}`fwRGOe39ptdJ&!Qq1wL3J(vkARlSL}G} zY(Cc4vkBemL`Q|BtKzH)5j@MnSh-e|oD3Y{TT=lDJWAt%z<{W@#A0k0_f6o6jO_(# zY~_qu?pLPWOrZBT0!UK|Wk!vR{#|syGO5Ued**H&g)3ANH)d(z2n|gIg((9AajO}< zJT$_lfzcmZCEIJopfWTp22qp!eTIDZO@8O{4OCO6L-)Uk!!Os_a4wyHuhM(@bq??R z?WFszr;{&JnNHr<=#F&B`F5VyWe9WPZcB-4N_m&~t2%uaGSqqOx{=AAsQl2I0obrR=b9WSaopW%zhuD;Dsc>WT{P1~?eD`N?DH z!OD|4%$mVVaB6IYh)>G0l$B%U(m}d8I(%?W=xTpt8gzKN8=pyK-8_-9x5Mo-wbcV? z8Y;7*N9C__)+EagOl+Fp=+1d~i%niU!#4|KkpW2pkyD~HIyO6PpvY;yIp;8Dv>FXx z3tALQFqI7RT->-23n|~bhgv+-qj>N@aTwjMtd|_Zf8^*zOMsO_8lj1i z^KZH2RG0bHpG1kCD9_W1U+K&2_B<^sUR1ndc%GJ3Fe=7S)AGJ2d7qTLSwzX|k*Y?x z_7wtCe{WEf^DXcHH$N%o|0e$r&Fi$=dH&G+sp;9%q_usM{VgYnsp(SRoqFZz(3?#3 zQ4V)z$XXw|0e=?769U@pV(5e^c}D|dXseaXBuJmx>akSJP+&N!HemGENEs>m`Q^*SgsKYujJ6||tqdd>6 z7qC;~BzL~sKp6*bS_}?@b_5oux0}RAAJ03=UiTPNJ`g{rSEVh2Ifd z<7smwjea`gnZCbics|^A!s3~1N4Cn|*x8Oa8yB^+_iPH&aQ6M-hWo>9x!US?MR*+P z^Ho|7nKj_QRLkMES~Y7vA!wmkvc_O$PT(kGXIH-qIUkfB){Cx4ZStI!)%=Z zQQowM2B|Ik8`3kqIeHiCTpFaa?{97KY+7OUa%>N6^#W4BL;&okrLT$MNp!MCIytd( zIpMY&w7P}#Gu*a7tDB`>==o~tc_Qml$(q|at7p^IR`ubAd+_P3t^SP0Qva_r*FR4R zk&B6G{Rp>JX>}K=YOm4i4wEL_#-4RbTK(5c*4vNFdb3t1*X&du{-v^Bt<|5Jmi1=I zI*qJQ27Ar$4R@~LJ~d9E*qHH0L$M4;6~?Jak`Sb9d$w6<>P$7Jn5|tBU3xqYg%i-k z9Fi-#DXo>uBn8acotAv11c}f*S;A@w655>%s+FK``I8K)m!N1`&?*V~XIjwDC1?wO67(sa4m7jk zIQYunr)9cUGKrpVvbOnr#a1N0e;0%lx2Cu_T;VH-sj|LQweM#|u;M+n4;{ zEiJB%F(l%k*OrXvXZFZXiXW)-bSg#TCYdc>;Nu+S7a4xXqQvWjpUXaIF~7(7N!Y6? z=^sr==cIR@2Z%d?v|`d-ejmaWI`5gD&k^q?T?P66!qfTvhVT%-Mt)N%Px!`4ehc{h z(*D(?ygOm?9N8(ng6{>CFLjmk{)qjQcjxyCVej!v-A|;gho<?&R?u%VE07fL#METIJaUSI4cnu6&og)vsL)UnTKlOD1_Rf?%tY zvslh5vLe;3Pjh`KUQ*N1ySgA!{le42JMz_|`q)VIgHJzVe`(>JbUkZ3jz(EbXSwm4 ztUbA)n>R9{RfO~lduF{BZfcDc67T{6J*XMZx)jjKnt zOC{ZXtfM%n?Y*#o0@MLS~k|)W7Gf(xC^X!x4)X_`shaLR2^j&oE z#WAr$551v(iEG0aWyx|keNa}yNAQiBdn?}O_ZD@fm`EY{aE&3L3Ctl6(Yn{Z$KB1zIj+yyJuxWykTO=D-jOtI6;?dld z;vGfN*m}N=5R7)Cd58%fyVn9vLQx|Fe6aT;kO3^C0K@*^EbhB!cdM>IsT-a~Afo)C zlrYYY)$NazKNQO!x|rF1W%+5?b-={-sAAj{|9et+S#g^yq#PmM%VdxeVy`C^EtS11 z86S9UD!ftDE1OXmR2n%4@F23;-|HoR<0l2dT(ndUeCaQ4TX;-tLeUQOT0IVf=Lh_? z9dh$%@kl2MptrWY zV=z?mE-r0&B5(jgHeJYH?!v5=Do6}CPB4~cM<)I@+%~j&U}R!E+@@)(H&V3lv+{8G z*#M4}*jwQoV4L|75z0?lucB>iEbnQ=VP3j2jw(KeQ$26-LNUJ<+Z7hi?pLvPqpo^l z{Wh;|DqR}Q*QjhYO%7oEdHK`W2!Ni8xv@5{QD7{|HY(hPKR1ANYAQE{AJsXH{V4dn zC;V-^YwXKnUB}UFv=}PCfBauPrbbGlXv-5{D7b68#?1Ly`rcg{(5a(2#&V; zLHKdp5l7A+3T|SEe3L)xKzy(2aWpJ(yUg?Y!#ORs-h0aX_16336L|lL^&UT-_vzOA zbnd;WEdqH@$Lv9Lj1_*pyq{yeKPc}*toQDc{sim&LU}*NdN-u}IHX}CY$FW;^EMLA zs)_T<1%yla(w?2x`(=|tl2`25lBQJJ_7v~oL%rEzg%9=NBsaE{m|shd`>j-` zNJ+o6-hU}M>aF+xa6A&d)p~zg!f&+RUpOWMOs!JF?I&of3<}WS3GdkzZhRr! zev-E8HUh$jexlXg!b|O;12Tkn%5AiWF^sI?_Q4BhL`58e8Ft1r`o(DifZzS8%`O%E zu0Kzbhh2{`f&Ms6W>iD%p`Igoxu-F5_paB)yjw4sRm>}|&K;;8q*uMTNG$zDrB2E` zTR?XeQM{ifpp&Z^yzdvVOyXVjjAjsa7x56>V|o1**?8^A>oRCOV@Vy+hpaN65m5Ja z^4d_jxD!;WZ%u(n^$n59tzmQ(uC{=&a!vveTG*u2*klV5yFOQtSog;yBtmC>PbgSg zzO+*mC!G`P*tM`Zv8(AvA+3&G!OL&xX6#a41RgQ$*_+!Ws)UysdAX36)ts2c&gNyj zq#4SK50s7#6cx)Rvl!^a@(5fYfdZsvUQ(80#&oXLlh^*x1{LCqQ?=I$L~^p<^1N8xQd_f{nh+V=Y0gfTd3pz|Nh^= z;1|Gk3O}*w+y}V6!TT&=Jcy?dgdg)P;(0P*8ox96zJT9-{4U`)n7Ctkw(vWi_fmd; z<^88T#k%A?-haohJ9z_q_uzduzczjs@%<^!gW$pE{A5gL@^j?5=*No#d2T(3ZoFL5 zK|Kih)CtDa>~MRJU~jK6A=?vf>k;aqXhq`gPU3*fTa5SGuLIh|U4Ct1hp{*tukzW& zuLVzmpU0MV8^`;1HFzUsytm7HqtD$KT#6-A%WzG&qq|nu1iptmFjM&|i%P&)JfIw_ zdtxA47QS-vEq#c~KAS{zV)h=*4FZMjn5KwZVf!ysVYJ>edMn1(r4%t}(_wxKsnk)o zXG>PD$D*@{DSpkP<7x#TxfA8;_G_MSBmT(RYbab>Emp$OuDhIdb}HvEg^S5;H*U!WsKI}Ga3>Pp-9ZDhgKw9^;^6ny^kGCIn;smd2i>b1 zy~U`Yy<@(!sK|du+3is%*E&H=zOmr}XhdX*> zC6kxf_2G`bTAdj4k5z^{a2W5Z3g;f`#9&_#TQ zo9>Mbs%=_E*J8(qn|>MV7jBA#J9YVVd_#HaWIc^l;h<1nl@{jKEDe6@ntg z_J9mw`j2kAeD1Jk+mOY1WzWR9cPQ#B<*3D1Z)E7cB4fAezeb-UDX%NERgX!g`+2X{ zR{c@le@Ov%$aJk<&D(vvWoxT$RU_X90B@GTT`t*c+bnR2oJbwgIvKT9lSq`zJl4t# z$j_C`C6YPZcAK{90?9cjnRbx2D$h#WTdO}&B1N->wAX2?ddufmz;^B5c+2MZ58~wc zYx_CaepcE~iTnS*|2Ht$2TZ>N_?>^*ox)}6r>5cJaN2ijoV@!x#mn$_PO~~gn1`{Q z$-3P6cT1!(T#E zw{qjZg%u!+czx#I`#3I zmR>Y3EqcS!2e<5*zo*qzP;9wza{)G#PI0rNTE|+q&ob|gefMRj$c4b*{bG#6n7P&b zkFqirL_q>IV={y;$J!EU#`s!-GvPLZWrjan%*l+IlpPu;&Uy#I0ff#|mf&fO-&imp zy!`JNFfcDV>o647@M8zw$}l39!>euQI@Hr%O1PnWNOIQ`W zo*X8HE9f+|wAnjUH8l1RXW49XQq38x6-m}YM`*#NQY2A=1EE>XrEWThutFJsSus(J z9H=24TOz%~)=`bbOMCG?7CappOu?k^*aBDR(@9m{0yjWkg6CdbYBK6DbLL%uDb|`0 zr`h=vRV({_%AVpkQWf?;K2AM((dy)2EMdDQO_|VAFod_sgE!(MyR2$iVL+JTJ1Qj; zE+Z-L-G3L#F+ENuXd14eh!IaQ0i>aFrit~$R&mT18TSYYF-zZ~fE1lS6SsU22~hM)Q0&sU{?pr8hJ8Ex0uHa7uyVQ6x52#8HUNRahGnC zs{6qh!0NV18iV{Va)3sjsZr^@@fE?{%J>?1!CS=bc4&{Q(9@L1Co#9RA|Uj6!j z7%MBQ@)jaCdr`Z6hzy4}VFIJGUFRUBb!j1E7LjsB^2I}Jd@>kj6vVLq$@sF@4V^Q@ zH8;BQzBXkYzLWeijBByG8p6%B;rOXAFm93CD3=&s>qbBM%T@l1Gv~L&JM^coYsE>m zNO?3@Ft0X#7|tW;mT#^o9XodmZ@ZZn`)k=*@%+4~LDC@Hlxw`s{FC;QP>zc3X^7le zTL|X`qkmUpOb%AM^m= zCr0iZvP*)ptNYeA58{=JfpJ(?xka4{f8cYzDt~I`@`*o7#h=q<{7@CIWsZ;Bd2G~n z;w77?QIR`aL?FlO@W`D?HrL*(dQfd#Z*M+Rs62Z@%H?BmMI-Pxi)@c0nh%m z@l)ifU~&;}7EIKA3%ft2>Q7JlBf4PH9~%2VygH37PGn2&hi!PiKSJO+HFJCD&zma# zoG#;ERq$go=N(%lN;j_&d9d|AUIZIdi-Wg&;|Eo5B4uuAH*2aG# z&wofYE->#1d`h!Ar{gqET{qCDka`;2)hi#1-o^Z!I=Py?V@B{I8YezQ~Ay zp6#Du;kJzP;K|0Q@ye#=Uq03lRtL3P-|)mSiu5UISG*A$T%L7+!$M&rg8dEZfX_%O zipF=Vvy=2dXn6Ac#o>1cq_=N~(?0Kc-?x2z{-3umycZ!Iu!VEBW=*H{u(sKiV-d#+ zi#T?U-|LD?JViiPts+wCXKnKawnY-Z0rvYy`dZt(7V07KYXL{5_*S$xgg94buD^K! z6j9=RYZ4`=^DlzG;k`8~2@1j)`ybxBTgAi0W{d|u&}*rGWXAgK{OgOl$Zyy0 z>azSKe1|zjq{7!Ozl77T%ENWQ=#+->&e_g(SXf{HtvqX)_mH;m1B{d;zzlGLPSP=C|f=VVC(UzoULDzuQ5M zPVi5`k8G!kbo_9^T6K;8;0G4LB|bC#aPYsY`A7bD?D0#?;`I90@3i9AXO2(e_qkoP z|8pyU4I8hF_{*AfwDGNc*NX27KPtaCdVH%5pMs3^1Nw+$MNp&*`k3tB;LQCydVXjA zacv?a{yF2*RXpQk)9-zm;osc)+EM$TY(FQaRuj_4*M%5O`oH1KOygtIzk)95U-JTc z{m8sNIQ`%0RNfi?n_V2MN&PjP$YjEg2AlqOHU4t1Merwce0Z;#|NGAAqJBGmZCC3D z@ZWB=Z+AxcJN@HsWybNXZLU0O`pU;9}UddK06*A#mw=v&z}FH z_GuN7+CC>gcbzhppOPO*>XG?V`hS{JL~8l9%_VPJ{TrE)J~{mx?qu#ff0Fb2M^2Ko z@{{p0zts?T5f8tdA->sVTI0LnyT*5W#{M_E5a$bitk0akd4tVgmUPj7i$7!|lNtWa zuEW;)*A@I!{*tcNZ)bdu>9YJ9bTqnNf1UiNbdf)@T2$@Exh&=gY=E5yr(Ivh5Ae4` z&hi}@nA%ShM|y#mou!k!1bTuzqicG{e&GAGx28*cZr<=6{Ah(uZ@Ze07QJ<36G!rs zYrlhEvhtta1w0*ie~&mXe65tR^9O>r_7dQ~L$@g)a6x3V&Q(^ZTy&mumdG;-?l|VZk>uJ)-_f3xC#T z)BYpmnKpbH%I(fU^b-GMKc(a;%73YCi%+Z|VSbK-fE+VMQta5NV}TrVZq*@fvS7`T zxO|a!1dI#Khk+U=EMi`89|prli#qhB9AxZ%r4XgJ^!+#KI8`XuKSq^2cviWAdGQsh zCOL9&PAziG|6Fy5(})!XQqL>qtT<-{XxT3H%lEnAr6sNvL(o|he-}onU->hAh;~{Y zMN1rirhZOG=FkRCU9kJX3ugw-`A@vSc}utFEAUWv8Iurzwn?9D2(a^zuhG(yo|q>y zdZ2i3Lqxup%6BB^qnA~6N~;f-*lF`MA-}4i5UuV{0%847_~M?SQ@JHXANL3Gv#UI+ z2Q_YN0_|<$0Cb5vzFiEmsiUARJ@na*h(YIU%mw76dguUZF4RUxTtz>0g+4XPYMR8J)20VhNu zf`r`egv^)T=9tUYFdUJb5W$RYdiwx-!oKzCCHtGQg1u_)Yy)mt@z?n57VK?(b_+Lk z6g3?9;6x4rlw7aV`i3s`&YE#Qorb(7wCn|TMK1HjZZuubxr{K85dPF38F##f$&SeR z&%%{*5&t9g=rb3B5QyZ=V;_M6(#<(!og~8@8Mir4CBK0nlE0`PeFh;NFn9T?6;t6g1ba{0GpSVMURe#*ojC-D3^ZeRP&GX8%n_ioz-OwUmjq+y) zU#;S}5D$Pn7vTZ$=0>e)^ZQ!UOKY?lEm}n*Cxxe!>#t+myi7;B`Zd-XIjPf*d!~05#d? z@dBo&H#1&f-KcpQrChY=8q#B<-QGtSzg8yS5FrABRDHz=3zs9B%%Okn&TAu?u>N+h zgC|=o{IcmM{!hrw^1*c!#X_ku3;A*CCSZ)@3{95z=3gieNS^OPS3My(-ugx;7f4aM3nEnm5m|NeI zu1mB;<(NUDlz9GnUQielhX&z89af`#Z>yP*B9KozKTU&2nf{hFD&-&y-q|rXFyes?nPl={1Zmb50V8uGk<=} z;rP=glbq=KfkYE`jU9KL#6|5m*|QRNnH@Jp;y$tCK9W(3oIlZyJ6GajcAN|)aii_H z<0bA>JMPa`+%P-tU~l3+v*XrUaVOhxA4uHiDlW8y@{X~?nXc+|A!quPr|=e;lWcqdTwF49xTjb1xLvM z{1KzNmR^TZ^i@Y!thRmBN@6buTSzwEPWD@s zOms2j9=~-rDz!dwNRU>3M{leAs-u_xft_sZca$$px9LqlJb%0;CaELmuOquq)WZng z0U?%)S)M*{=tcBnf=P9&o$4)>N~_D2)k&o^w-S`-zE*!gQ2aTNM-Wy#3FA|jmm+S> zwFyI)pg(x-s~HVymSly#VGjLgGwX0;yue-o#J7Riq}V8zzN929E}Y;GA1qjWlH=b& zd#H6`A2gPom_?sK32P1(EE*W=^;kg+mSTT!Eb(jlVL?$65x4jY?a8mW`mk@|9{GA( zdvXU~tqV2cXJwIC@_!N&LL7RQi7pQGilTjJ04OIPg;8k@AL%QC+3`;lOQ1D%dsJt+ z{?@5~$EEjAST|OD`C( z*2?!=PXGF9PfpJwvE+{>yJzv+w!V4~PF{9GhVBJxbFOT(8L-%eoqC#2PbFV2sYW}g z6!YUC%@#!{i9S(9bL|5+8XpS&nX4b@?y4DA;fFK9F}N{qDsP}y9{n|!9%_p><%}Z4 z1skRKFS)OXFLl4sQ~cje-eto>&Go1#L* zii)d|RD4$Sg33_TYGVtaIMD$``(p!a_}TgAM*r~!1sDt5ej_hl;(a<1{w(1s^Mi)7 zaQTcBX+0X^bO+2bho~O4mFrKfr{x~~!H1xNN$*xMu$N9=33d!wmoqz?Y_1J|D}fzKb^H7VZXEh5AmWy#Q=|U&4@tJm96s|9jk6%F@WG~E0-CzZ)yT2me4&P- zrM1Gy{c)3GD%vicWvYeDckJyh6ASuKU^j7=PCD#0i zjH{s3|HJ+0H8WE8qs@p9Wk33CLrR;!dfsmH+bMzXJAv!$!13W{WleUi7~$++!xE*o zu)Dt$)80+~D&?F(cCmY?L*J~Ra zubuTHZI3G;KhG}T+2<~#Otx(W0=zlw?bJ3f$DBhjUw&ypF;|}1i4W&SN@9PDuN>Kr zx0(G_XPV2ZCa@oGUba_disZcVoXw(d;e|m`?D|u@3mH(mOEF@JLSCvrPx0?I1J%$& zHRE=cLRdlztzFo~{}Bw!X(dcBdM?|lwOK5;=x==^+rs0nRF6Jy@n9FW8&y?yVbKG) zJ>L42Bp5fgyGs5sK_tJpO&)#L@v!$`_miwCXC4(~sdYi|TE^owY79_Y{=K|_<8pOJ z76M@p$0+oBR>nzq!`mKG^G&N$7)&qOPCSVmP~xt1;;d<);{FR6jw;9EJGqu)A-zVo zUSofV4>+?&&vqQTcu^g_&%$+c6epVg=CD*M|D^E0O*Bn%%X*mIU25Pxxm*l;>Y+6c!<4bpZ}5g$7SA2Fw%&-IW?yEz2;TB4#Ew< zzQKbXRQ9p3PiuZ!b&xF`n}WUO_J1E_iD8b4uZ*o~Y%B>yWl4P|7p9AJ~eA=`+ z`Xc8x0YlMk#^H;2fm*P|4E!_00ZUX|%h~a<@c~1@SgNSMos8rdNENcgs~jDSNpv5* z^xGxYH-H2eNh>sVE|EU{5~;cA^^7E!9vqauT1A^TVPYf&s?onlX~5wIuAtoDBq~c! zBp|OR9Ww#u1+dC|7yWKBYIR{g!sGLUi&PuU!<>PG5LQ|yT5}Jsf|AhhVR+;|sQm?f$an>5M;x*7e(&y8TPGGWew@3}tK8GBzxuOl=>aUStl~4Z#HL@?Y(=Vy5WNrtKfq-gpZeV~@V0j1>>AMTjph z3kG2QB6tGB9Xp;sMLR@$Ti+MxWd`$O6Cj;-l~!126he}QfIhPY)$Z1E{k?J)_g$Z~ zf$k&C13b%h7WsdnBhBRm5^?1V&O+^f_H)5cwVRujvn&gWVuMrOl`YP0o=eT4Z-ENR z@Z6Hl!kHfe7K%vOR7byfIir4J0zMUtDJ%?v#=}p-!c;K+A}3T>|0W-dkY!#u;Um)) z0+k!{D$1HCNCq_cSNdJSX`b*(cUEYU5;#WA-whczM}GUQY-TP%56_SE6IsIfw;wKW znFFlwL1kR@6bu+pB2_U-m~HhtC^Gs}Q1Jyqo#q7RSnU`|{Ru%7Im(Tj@JE0^^|Rn* zSQDWHoGho|uU~)3ZKQN*2UZStAY%M_(sjneKC^XtG|OlCl!mb75@vL1WqQaN636|mMqr^ejq$L9=gOIUQ*%; zj`15MkQ|jNv^R#&^h8S&?Rh(#vP^Xv5PYtTcaGCc|PG z5J}366?sV3@6ZQFUvhSMh1aD7jI)Js&9&rR$RL$mbK*!Q8Md_OMt@8HnKqaB!_6}G zA-3~5!M_WCA<`Kky-#-+C|%P6zK!I-0om0Ctep##^tlaE)J`TElP%QX@EgvpiV>=u z$$It|s-SBrh-t_b*6xV*vR)481**RvKf@Nc*J~6=~&66&9_2@HF@kaTBd~U7Wjwr`B{PK0NLbacwzw4-WjX&;Ni@_K! zRr!lwwrUzqA^ObUml@gbk@aErnH0H(Xf3>m@&d+}{~NJUx4Iy#UN~`kVgfhVj zA9#BskanHe;O(_fiV&b7 znh|d%UvPYG^a~%%4i+d8K=@$y(0ft>_Iwb}#hyvdSF!M8+>+-vrm2-1Fzg84U2~Bf z6uCq9#OUvi;IZ~Mt8wJ~EyrSOwU0#e@gyHsi-PPS%ed{_<`3Um;Zla0y_)aK=w-mJ zT>M(jZTKMzm+5^`^KQ3KV}G95mS& z`;DySUx>>J!OMRkLLgqkV{b7N3&;o~%c7VvgnE>i=Bxmihn|c36c`%MYsgHYJa|y@LYM<|LVc9H?Tzhm_ zp~NzB-xlG%9O26m{G!Nv;FTYw77H*Qqp#i)ZpjO`<%iCKz?yH;0v5fiN`krb*o9W^ z)#{g$nso8TaQktK`f=*1=nqzqBw+EeEa3gijYTX2`$HRSK^L;0XF7VKQo-Gjv+c;B z*=haXCAP}N`U)$|Dyrh+6q~d_{=sCEM|u}M{7cJ%dt$wn@fM`(FUI6B zZwD7Xae$RcbL3?bf4U$+PlcI!51CZ*&hT|pM^JJae1c!^m-)O>F{qS3Kn*9jt)Gza zr|Z;@U(l!2u1^1zZ0OSBZF$=q8!2hXWo(tt`ntfS{86YCMySf=e@8rIOn?S71bu#E zt?cM%s#f=iyhP5w@UjAd1=6--ND1g5`1?cSqU(va&?GYM@vkU#2mw*K1W7fm5|B$k zqMENBPh<|xCq+BQXq()rDOyQEL{uV1q((eN1ZLCby`3YC&HCP`5~b;UeN zlz_0@ydhanXdP*$OG{2BO;iw(fELMw@g7$Usp+^@`5MeuqM>D0Gn!5x>=Fwe$%v6e zl!aFea0SmJ<3A;1TKn@P-dLn|XV+uXfke+pA~uIsU3Kz>u41yt_a9STzF#7aNr_Nc z*Fa>-en#-&B=JJZNaA&85d*eAcp7d>=SEM#Cj-u4Bje8e(jEuZo4L{DR{57maAGv( zM&}WnK0lOvQ$}5s#?*As>gBqxP1t0=6n;T4!xg;TuiyPJFU{fg^36ZLQ4O!g-R?{L z&^1W~E@6K~MOA?A@0eFyEHh7A^DDv(l~GdsnXksS!Qqyy=tC;8r7bZDc2JRW4=;+_ z9nHpJz5oD^w0IUh|IZAJNKj@Jv0m#6=jH!M(%dO8QPxK`I zgjZI$g8j-+@X)_8x2_T=OCQVWjxn)PLG>&t++gYULlIj2y;?%hlwZk!lf8ZzhKuQt<>s1kVMTB{X7Jc zw7{pgr8NC^AMmnDEsf;%(x=XVe{6l2;xO|$IWkJ8Sam9pTBt6CR&jqnlCK9Mxvgd4 z#4tc&AdIl3%v9#YK4ytlcBdDrPb|#vx>gKAHb;)YdlNa_%i*n0YwdE`jrKU zNLc6;q=PwHo-5WJ;wz#Q_5+Lkaln=aU4$`^f8&Js!~(jTqLGlIUK3sLTw7dCBawgF z_tWPKQ5K#}<`}3FFrJnXY_46;UxF+GEUulx7iDYpzvo5mtT8+*8I0RxGH7*Q@nKDf z-zZ#v*DO7ozR3{EDFN6lV#*iPWMMJuFzc8aD*8d914dOP8}3SImR}~%SeZPJ-A7e= z6TmI7CO8E%HCW-88nVP$^XL$&&FoPvlw2Wop_M?a)M*`1T+URdl-|iw;2@}v`HAEz z{Oo!X$3uPhmyXT~fyukfw^s_XRaL4JKJypvuthZnn7>m`urpXFID0@HuzCwE9>-fa zOd<;W_&J{~weu{opQG)kwJZ~nX9p-_64)`N(d_S8GE|Q|YV@EG{|gor0O~OM_Vzzz zT<0fuv(a_WV4tpL{1Au^zKdyBtS2w`ss@UvQ_5#b%6{s`dU}kYH*{umsdp&hG3fTL zO(#edHevd2_{k(JURmMLR{=jrms0VT6vC_DEo3S70iXpVM3N0|dquCU3U;Fb0lzWD zZOOC@Vec4hdz{Luki^u-SCp0`E})H*!(&@qq26Wj#z67wp)X-UF=ku5*`wY4VrWaO zyXT24?AJl4@dGV`Wq>A9X?VPA#mInuW2;X;j6}kZv~-d&s!fuJ|0E7^Rnw|^$mziz zABq6Rjja;`seay?I+pLVSdL6mwU#PHI zG1VXxt8LEs-`nD=G!A*TVXT$-s>g-@F8*@anUUNJbi-oC`;BkpSZ)01G^T!SZP`N^d zJN(wE0*w=SthxJ4Wt!v|i&20!#Rk*-tNuqFLu^8O-){c=Huh?p)O-QI{_6~JDu~!z zPKFcbmD4Ni6tEK42|s>~#o5R#as_9LmXT3COz`u^1b|^dFP7IKPon|P{z>CdZE0R?hyuF6 z_r#)YBEOkVC$c$$0O;XHw*WUrK?7o=LEBM|9JxQ-ME|6~wSc%|Qa&BZV9x!!EN(fA zPPYbnjIT9xoXA*fw+bR8*O)D)`;W2*=!7rfPe)tFFt8u2!25$J<1C$zwGzR~TM$n; zc!&6EK1Uq$k&GiWZ3&)j1)0I-8iZvKmmZI&%hsRS_izJj|v_xIzdp#t`*Af-GhW^N8 z8h{AXUJi`uqcIM(ElZy~8Wgyr8%gw;e=gc)n@kd&X#K_pq@nAR^Fg|zmv8VJ*)6I+ zc2RaG-hV(k!BPKoz_lZIVj0U86iC>kf0`xI1bd>gR3dabRT3?;qfb?>_ibe1YKV&b zQ`Vz$<7{O#{Z9Z&$*UgcMQo(IR-6twSSz5NWaRHt^?z*s<$0iuyo#Z~^tnrP0>nPJ zfP4pJ0?9QC3rhFYAO|1w$bSJalmJAq2)mmJm8JGa6t3i_);cTYn>8|_(KiqlK-O8& zHz4@dWWiC|CCvRW`lr&~a{Vv~*T+UkBc;0-UiVkM7H`7JykF06VRScAn4=#i9grbsh~kX` z1AlR&cDoO5CDu!ocw<9skKy|>?1eP6#I9I*3ev0QW3zX<@EroN^-OZVQD=;wPA*_7t@SIs4W1%+DHtxo9qa?EW)o}m zz@p@%Q>JKAzsc5b5ZwQ*n}!i#UdhfF@s}K(>Jx=f2aiSM zRbI6s+gseed;}EJ9jie2#yqkVY3BaMR=1eJ3$2psEd`4*gshRAnFK?F*rW*gv?Pkb zLSL3_irhYZ1!gJx!pV5_<7E$}4{UJ?;OKR?n2TaB6c5^bjI9in!pB&ZMgR1u@G>>@ zz+qC53Pf^R{)<(ifk10jSfq%*I-j$pk2SJjYW-@~TMwbxeATE&$pWO5BB_)!y*x}_ z`Ssbuc;$4uWHu>SgLejd7?TI!X58PbGD&E2iB}#ZoUe9dn5|JKbqYdu3Nbo^WN!2~gm)@0d7etiTAfJ|Fc*wI_=(x88)|7)AnHp&reZSTyUBaxtoJAEsX$RL)Y5!FIqN+s<<73;-kQ^8Vas7g zESB9E+XBPbLXZp5m!-PkH@q$EFO-f9g@#7Nu11)e{2myaA?vjz<2GO}lX1a|%rI9V zEo}&!LA9joPV%t{1~v3tTB%JS`dhzZ@>z0w`V`4&mT%UOw9bppQ;EY0t94M$gDqO1 z)$NjZV|9%@nD*!Us}?USUc5xRT?mG-zL7q@#uLz2;|QDM-AdLgxahb!3Img@vMjHaGX7_mVLH(5!t}N!@U+A>PuFA( za6L0X#&?sWaUDHRR(WG8yL_ZZ>-Y#4qc?UfFJ4w$7^0SEW$Ob4@jNg2%Mw{*gXgUk z9h#) zfYNLW&N8OC!w2&hKj_gOdb2vp-`0h9d-hKpogI3Q!^H|=r2YZ_sy%s;U+mDVUMNh5 z_E5*d|3dN7m-3n%j2ac>dzC+C%NtpJ)#qTzIb4IwU4bXk9$`rdq#oE7Y?r+*Yu-e?WVv zY2j1;c(W6<2qVQE2&!TQ8?x$8LHc*T_^S;4%hKwG_Q^|GN>Yo~9m)=$ho`quNS(fZXd02DkpmP2d9 z*FZXa@Cpuq5!RU}>zIlzGk!w7ZLxvoq$XLopb#qD9HTx5nA3UE5TE{y_zCdq_s^F? zRl3>o7I|JC!e(82*SGj&$>M&5(gWdGcaPVN&+?B$yH3gZHGesHRGKF2csEK@1X6ze%T)Z$YOeX;%)NPh zl-2deA24VT;{yVUdsI+x1(ga4nvp~%kU(5eT+k?rRO`wNpb{2mMj6KeT8p*TDpgzk zR;}1p#HbiRND!BZNU<(}3p|e_Zq)!T%QXthpQXlozlF5cQM6`IKX>|ygOUWN!RE`t5tY)oU&owVh51WwgXg&!bFgmCh! z{P`-0$JtiV^q20&3ot3>^YJ5oAm;h-5~9cxO>j~*w8)BT3^xQk%ZXnEM>XrqSyU2^ z?O^@alL87}bz+0{E!*hlO#aNf!W7mrW+5kkGRM^O5X5LVOuJ6}M%AzeWF!BD0olLa zkzpBzsifDWri?AXHyCo_Z!10doo<8?{MCl;&5QwB7uElrsULZ}Dpk*RK5+6?q{VF7 z3@ja#_M7#j-C)w9PxxsInn}CZrv1)OyWw5ZhTF85pLUtjPBLju;#yXfpZ@oGq#x*~ zpG&&8iV?TvuRch%YT_#%iU9P1e)U=FNPFL;aYdJJwx7zr;-?dsDC^xtvwlPC>&Se} zHguxj&@a`{J${ANR0z^FzT5otf9Ql(;kU9{*tp!bGR80cveIh&(usU4*D8IWpMD+b zHL1$3HL1}EgE$9bVYX*6vzaa(Vd}2~+_0%^rs75=ZhzZOFNYh2pS z`nK3TRVDcfu1k)2zfAVZp>=-d0_y1;7d=W{2i;z3yRD#ynj{Bh{9)gGM=Vf>KH1Op zDJ(tCmOjmvMs4OkOyTOJi@wO+}-0@Zi$va#U+tJOr^8hY%8tRHV%8J6d{e5!0JxD6- zqlv^^9}WEQ`xtr!eT*=DL{Cu#KmA{ClYXL2KeQnInKbDKkPfg~zcMnLD|@1!yb4p)EX}{F^|f!AwQ&yHDy#JoB`uxr&;KSy3vqaR!Nd!CKS{km zVCscXgTNx3iZ996b)b{j3PpJz5@Nz9?@Lxt`g&8^iT{z7Y)`Ytk$geW(+@Bj33?Ez zF4=CFg>yx6rEHG4eLMR{;DO9&9(kpOgCV8K&IXgx%}&aEnypE?nwQ_MVP4SJ?4`k` zJ3V`sD3#9L%v)xY@Sf?CuYQoNUPN`@&Tx#XCx0>;v|pAQw0JWLYWN*?%y)Q%?T|x8 z6@AH%nef*8vK@IxP~V@l_ciY4-xO&?c`MM6%V*0-`?E=N;ulLdX3#L}GLq-qD{w0-ed-*%`wT*eJtVro3!fWNA4Kt6F^%y zi8cx<=JNzA(m_gqbcaUta+$KyoyrZsBsS`|;NHM=hEj9Pu*WZYkz{WF25|cqO3u+Q z|CmkP9=p_iBwyED{qag>@^1^Ng!^jJG${;vKTs@K9zK0<1$gM&tKddgh-R3Fvm zEu4*7I2RMT&90eQccOUt73)%VMj*-(o`E&j>Lj|Bz(&3j(L>^9LG-*4zMwz=_YbpO zVaC1!0?YIcdXpbH`}R!Jn;FYodJ8R*@v~i1$BDIk52$ntms@a$<#IMN`#PBi`{U?W z8E=hpF#eJOZYBdE8oF>V{f+9P+{TWxM{lnV9ksuTdo$VYhShdE3zKf^{#A9;Jx9&O z-QX}Pd&iL#9F+pDIejqW@b#Chm-WnLoAol=Hk*)xv0X1>w(Zx;H^d}0#HCXONPW4l z2r1oZSt7SwJ|QQ!Rsf^Cj3PiKlvD7bC>% zb)Cij(Ya;P^co`{ld{VMr!LXNKS;v}yU{)F*J3JWNV}|&ghmmpk%J7BRmq^J6W2{l zD3vxt9slfF$`1b8Ne^TQhQ8&30gf?e=wZ3{i&~nZ{20H<5qbBsS+|qr{fh7YWc9fS zkWYNouwj(ih7Dh*=|9A#1+4k$)uiod(wuk_lC+faj*`dUWk&2I=E8$mB+yfBymzqQ zIq@q<_Seb>G0|SdEBqyP?T?>CK(^cCf}R2A;(tl_~ z<qLdS$6%2wvwr``JBNrAY<{f_r#3Mx&Yyf>MD!m0D#!dA@;HQ5;0 z1gJ)LJ60#YkJ`rnBQ`b6R{RxYsAo1x7?hTmec~cLaE8d~%_hqaR+|iLgM9h($W>(i z-Ov1+$@B%^v}O;H$2NY6X}n%-?`cXIfqt`yC_-%%87!*E=y2*59SK{&jeCh{6<>DU zouKO5YY&+MIEh!mQoty6wFE8?0z{v3Cai*T9;1kwM1;2i3b-~@*KG}k9yEh6GpX^wmrw#6hH0v3h*E$_;?g!Tv$#=II*01)@&@aEqHrudwMT zCn~KxeXlvxRrVLlOeFbHle(^N&ETJ+pLxYBfB>L_Ev^O_fM6~DCYP$e_j?yAM@!1d_*#GQcl3(F?l4IM9)8=+Exi!ft6MgnHzn($N zSYheuE!G*nGl)rIAZKZOS0*K~y@Iv@w?STuW+Tn?LBCm*y0ky7y04ri)&h*Jt~W$Q zS5)y5Ggny0Nd$V=Ab8-9D{1Mjgk|L`Y?@1&B%bkb_{=jZ?2FV_%rML&HN1|#7y&L7 zFpnW-<&Cz-Kj`RKEvnekx}1JT{%)OhO8gFE^FRYh3!ZM%eJpmjn7xOA`B+@BG9ZG? zbakr=#NMQWFi-)E?6U5i?6NOFaz4&ZKab&ze8u7{WSY;qt0!B&9q%tlS;u?BwI?B2;xQ4lQx5-IsIyadL}D{fI_ZZ~wQQN{desX07*3!36mq9cVUvH>ELN)p zI~Nh43EZ8Ts-<7K7^G# zKSqde3>flp%Pm9hy}~f$g%s9*{zR78i5vrx{E7UL2g@KLj!B2t=Z{GYj|g!4GOL_- z?UFnKIBQZfI6ShC$<(20@P_CnZ$G{nCJhiR;pDyQFwdxM%gxh%rsW$Zde5LXfU7W_ zX029P;BEsr3BFeV88`y6+0@q!QL28SIl}1x86izE=!~ooqKf;?QcGAD8-vrzme%qR zPm9%N_2K?Km%U(o$j#-Dius`tk)-!0+8_hpH9{#NMK5MjiAaH|}RV_G7{+~q9=n?;i(uOVa zRFh#*GQ^_fF)Dg4UC=ayxwGMDn|_JXTS(6h<0o9_eCSt0YVI_@H+=ui@XQE0dV^6n z=JdepLx$J63rZpXcJG0;Q7fYrhPd_-&z4&Lry#Z&vRrIwSy9!?fxF6K&3QhklLczA0Vz^$#^;0i)e?ap zITjGzD|vKMpU3X?gS{Bx#NOh4$W*q|AIGLkY*HWwA1MuHx(!lh|DB%hH?7(yLkoMm z-A})Z^7z|gyYNW_cW(Rg7|kNvYE<|H@(+|F2yX(q_!OoMnki^n%e?{(J5bYhxo5a#6UbW z&g2ypDZbKs#9JbH+oXM*MQ3xnved>`dd1v7v02w;p4WBcB{W0Wn|AFdoBhd}vQbS_ zfY$T(ON4Gx7wb*8LHjn55Kc?y(J&#ZMBp2Y{^uo2al8R&rt7KG?X*SrESxrn++Z!3 z{iTf|L7R9+W(9r4KyhU#ung`&lmNi$G0)fomojE&kxiStRZCF!Z;^0^9l_WJnR5MX zJgQ@Y_*B7oEWS)USaTnR+Y9*G&)^UI@5iO6-_YdeGF%Vs$c$5gi|H0CV<9wWFHB|- zB5afTB!AM|1^EF!4LtOJ0rN%ULUI854MS@+4DHo!awD%!9wEveCFj)=p=n4v_}TG;cmc7- zu9?H7FNq%p4>Qy&hISA?5Hj{xTDpQ(lK9UXF;Hy&evJF|6FXNenCQ{St4&MG#Aw8c z9XPSC1g(8Zyw{Y%1sopdYhEwqE9+u`xqijC@o{vw4D&wR$TNo<^3CHr^diiKv)qhw zvyorTFH|}V44~(cgZX7&!<-W4hgrIvq)WMn*WgtWOkEdFP46q7xrFrFrdyfjW*w6x zyAABfrr};macz5Ab+P)#a~|rCJBfdjWcqBXe4MWuXhW^tZ#urJy}!|Wnq)hl3K_Vq z!9O82a8C1aQSl<1qx^rd`8IH=*dex+eQYb*;18ylXZ*9xA?UCbfVS#+!zKp-RtL#g zB8kVN!v#_SC{yx%t$)FPy-fu(3PWKEUSxZ_m4d!D?P&@k%s^i6OfpijkLJYh#jsM9 z_ZcA(uH1^;0hZx$3j()S+#hNl&k}LeJpb32t|;mjO5(6J3|biu72eis(k=KDPArP< zXM-V~_@n{?oHfzR)@FtgN{y;Zhp^o{s}A9N3&MJJ`l^M@kx;w^hn2av$?b6ZL>P}u zn4iJ6IMH~^p=K`9rOQnPNn_g@_GM^7--iumS6>jA7%C>w_xr28Z`_-gNDQ`tk(K_@ zK{ov&(g|MyJ$HKfd%NUhwtS|wunQO5%KI@h3htuwih(QdRO_AU^DRV{xV>-D0HD@% z>1CX7^N=0qaFPR^2WKfzKe+6|Gp6ZbR*&QGGBc%4T(VXZZP1EbKz+;fbZHTEV#_uZ8T1Nh zmZ*CR>sF>1_3A|&=1RNYkMp6JvW(t(*xsUoa}&| zI!A)m$N-w1Xq#;~J2xWtuZw->kjy?*U~>;DMBn7xZFVkMr272YFrL2vL@+S~{tDSB zm&qS0(^q$BH-8v4>V5jYvL)#h+ukL5e8_ouR9$RGHge{GMyz5n^BY&Ne;8ns8!D0? zW%kzol>7a?I}7T7-=2#)9cI`XeKZrEfC+3}L6J*EK!iG~+o`EumpKZX3bKhKgBNtV zbT&%{JT{EMX^$@beGcMq_lS`U77vVFYm-2~dJ zv5h-p+xKx27n0*mqY0H&&mxY!{qAL70L-!NdqjGW2YnO%$sBSdw&#On zb7iV?W%_JfaK7Jj(xEkkTbDcODK*JgMISZEGY8c`@OYL+|C4!($KXq*92jcY!8)l_ z*no%H(PP!%ujGLmroyV|!??&{mEIWjs#9l`R}<+vH6t8OPA{**C$tc(gxrWN{&eqX zkJ$0O>BQgCC-4=tX$RFIMJ6sLMk%K1PQpbT%vE}iAF)C3tu=f>QgITxyw{N@@dIDH z$4D+%4=iz@xqx#K5G8No`)EGjVtqStnVy7`Z)k)$`U~@Z_Ak=YTVvThCmoKnAT5MBGf~A}f5wc9u*z=w)FG`P#Ytd?w8xEZ^SXSDpA2b(FeU6BvCE_5ww& ziGJ!Cvo!$@1!ujE{KJZ13}!U(Z#bss32*_)vBocjk2}|sBN=UPkivlQ?DU#Rsvdk> z|H!`l8;+dDzqL{PqlUx4hSg?H^1t=Z^uy=8y7ML{KErRM42q*rnCaUj0qRh{Xqc7i z-9#a|2Z<_eXZXy8pgql0LZkI+8Jn?0%MiLDxm#PC-{tcb(!xys-v9sbcY$9P;1tKKR1tG<% ziS6j_+x!D=wV3`Ke)k$u^460O0Ovk0KRn0WBt zA)?l%t_6k8vW50skt?+Q8Lg*$Z>jl0hx==$Gr2rHtb>T`_By%~Dmr3Lnm(vJyV@JI zD**j%+tvBDAE51a01UH*&dnE^(jI^@aksna-KfSkyZ4^TO8&6323*nmiZUnhqQ1xK z;f9H4_*%iMqg)O?u8WZoBP54y3Q3hpoOA4N*>>S@yr5peai@_K79RRu)N~vY!s#I^ z%oibIxtwx@_aCgn;awB2itnW>gMpTwx-DG|G=Tr<7-H0D!O#h_r6O|~_9kQ72+9pp&ruc%{9 zy9YnS2@po<emYvJoWaWo*}6i&P0h#5MA3Aw%Zma_${173mm`Tz>6sQd?%moBIug@v4kv+?p&6e zyX;dePKCyzh);d)A5dhz!2VU_T_1EQcu%&KpMNP=c^Q>`HkKF+tO^##brzj*yT@d! zR*kLol&$5xLvjwB(`??bJUBNB94)7~G~>B~18u?6^96@fFu&U3dQ2o&dbllpTE6uD zl=l8qSZ1&-GoUdynD3qpy5^;UeAg%1g2(0yzGe$Ts4g&?O*+^0F}C!MJdPf;rHh?- zhr(umVGHie7mV71W!PpH4*m393Rc^KAw&3CUf^XS^2w z59C(F&?teJezXd)Sfk_Z$LNiY{(iF-jF|zqD34e0=C%`;12CGh6?|AFN6jBl10*XK zhhEl_M1|5^ovK4%N53@=+4xHmWpi|7?tDXBfb2>WcQGVObbM5wk{DwhV}Tmo2fSt7 zKumg)#kgHIMtJiEf8P?eM6<(1UM746Uf&&}i>hKPP&FL<^>W6X9$Nl~GW=~*NCU-@ zezEPlMX%(JS}l~T%&gwUw|%I`CBZ2?n8V!6s#bZHdX!;L=0G(M9!__y~TC zYA~`{aSkIJt$%T3eO3BW^p?g6r0w~DIz1KdaM&+oIS_eNK@nriOET0aJY(b!3U8=J zQl@U^O@-jJ(~b74In&D=4+_p3M4=o!?mj(;Rf(uFWIYFq$Y)SqtWLs7W&;QlAx6y4 zSp7dS?bMyq6~(do-x5-xesS~!^BXgoX}$Yqj^}(%@k&#?u;-w7gtL!*+3T766{dxz z$})9UHC0$Xb4G*gaoMv58-i~X%`bV(uBBkxN{zYOy$EM6-1#|TM7a>Ys(BZimt;ht z5cA@ugxfw;N-no_8{64!%6M~xKz^1V`H1m*{XN{_tt?(wV;01|mqU|8@&bpn$yC=J zu`>8qk-0R%PGX$Nkj!c%SNqOmheofG^#9sgUoz1G)uJXC68olO>_41jI=?Nsx41mp z>~;QMbwB@)soR4`H(p7$cpvbSnOxwl9n1wCDIdh`J*va|FfO|4sZa>4oLqLG-?i zx+(tJsBQG#7M{B14E8dd6BQx?G4G}vXNfbz_6 zCR7)HR1;SJ_-Cfe*FWCRS6}mY_hV@I4tOM|f8;LB=pQFPN_t1qS#XDL-02P_# z9-PS*2L9Zhnd>VRo%osgE>6jJvGp%B_1wd`E~fcibXOM-kxmy^qOU>UIO9Z;?w;V^ zc^;@Xk}J%eXLTp^7F>Gn2mf&%d2Nzo?>qhE$(ib;XHFF}sADcX-Q0zzY$&~L&}t6O z`}yIc!sxi{{p?Tk>D;mKVCj@of?oRw>D&Hf;2cl-MAOk$+R(kDyVV+}B2lrW7)Eby zG;R`Nmzr8me4(;%88OrP53yK8OO%g*z4kNf-I zBfj#_>qT9^Osi!X*HFe&)K#Nrc~MK#zmMcfY?u4WwUgH8_A^~3`uCOJ{0I5%0@5CoWX_;bDwyTwgzuT9_`;+ zv>y#5zBt%JkQk%$d;^DA8N)N?IoP9fd!^=+ruoPyd>e@d?%st6f_5i)8j?`Zz1$vu z^8lg=11fL3A10S_^F5Xf^yM-IN`}UIMXG9Nh!?H2hdj=*12S*9XrfKdLUO>K_kZ~u z--zv&zu7u-^EtlB+pT_1lpN!>14n=z5HWOvtH89xbAl4Gs8JKsbW+*&f%h(-z%ldB zWg>U1T1%NcA6M=IpOWF!y$U$B=%+nDMETcg{l@&wYHY?>-Trq%H=XSa2AIgI+9S@D;XGHu6j|SdVBtB1Z(} zO=161Q8)%d?fT6RfkShT8unpJTnTzJW7)?HtVt}R58GNUqjW15sw>@T>!byBXlrs;!YbDjOqJIxNvM|M_z(ONv9~M z+#FW3PsNrietPEAg7w{0v=8~Z%!bgz{j1V7Xs)-0or=}Es7bDN`{b5F8RME@q>Of8KOF?GR|NhZipw7Xqicg>XuKTM1{GBq_)SCq+N zb%-{VPe`tAb3?iHQimmf+eH6{(&1$mZYcBfkuE*hR3f@J*Dk>=<)8=DDt4Q0Q19a# zvnaEw+y2Nx7<<1~q!XWM(uak8i=c%ZsN5kYFT#w2oDbc4wWqQ&wMP9e8?u24msA`* zl5%m_3&pgUIwou;Tpt3cvnU;sEOBShD2VEn4jY}c_bYwUY4-;BmXR%a`zWi+p6=Ca}B?o<4F^Ch-uJih`~ zRoqV%y~{P0Hv5y%F_Kh9AtbdmB!Yjnp%44MY%BEWRv_KTP{#TvI&oRaH|@rr0WTq9 zt)elduE*4QE1ni)8S7iN&wsl=>}y9Jv%P zEQV-H!}pd?KfS3V+W;BcgME(obo!$ z(NW&`;#uJi&Do9OZKI{ID~_HHD{ihHToxVZ_v&6~MFr#y-hgV1lv>&f#~R_780+bI z9nL}MR99_+Vk`h808wrl!%O5Y**#`w|= zug&Q4{h0@~vT3`uVF#GvzCB&~Mn3IF()=BL-`{KY^fX7|elDw&@BP0Rod)0a`G^OKJk%X-VeBpD^G$~;BjjCjqq;4p$NV>XX(~>qOI3EfC#mcgH^BA>>3r-%)F+P}`$r0L)T|B#}4E#4cvB#)K;Zp$HA5(^= zj3+=l+&MZ^`Rx0iWyxzDlxzNbf${Fn~J(JVg|1 zrJfEoP%;oL+yl^>h&sFr)MdK#X?TYZrd|p5$)b2ZrBGFK37cqS&3NnW!pY-3LHxH3 zNZoX1@%}mA><~H~%MeUp$(8PHzY+FFi&i7+^jChQjngLs=(%pib{ zLD-WDV$=q2D6GTR0@zAU9D6UnbGiQeoj=b$?03HN*SXFw^gF*uoo^y z13c$jcuw}=*#>ttbGMRK3^E|H02y`>bL(KCa)}Lc)x$C>-Xz zB87t!mt{+h-p5Fnu0V>i%{NrDWWDudwL{rSxe@fskKl;>2o}!`Q0BxHJBS z^3>ygN>2}Tp)LWMz`Mc#+1Z?QR}@-XW9ffhDbfs@o1VsO8FiRmGWHXt4*QJF3En87 zt;4H`cg~^{6*U_NO-fIf{+q4PAK56&j3fb#{IwP?yw&jhE%`E|^JR{rjIXK~ zBu$C0=qSQdC9mXAjuStaadA*UM*ZGhaF^9|93q-Q2Q(UPjExW4m)6+YeL1J)S&ad#jmxL#V6o^J(UO zb)`*q4`rqYj)ST-72oshrI`vD3Cjzk!0;Wc_t?u?zMGlY+s=edlOx7QW+JYUrb!dA zDK`;iBzc{co-Y0EeVpWmi{7{d!=WKv`Y0(J1^n3=A-dayR*RgukYhgQd^;!JD>n14 zU8=#2y~oIGhr?MUSsYGLO)DDpb`J?~xO+N}!-?=JpJFea0}emC+v2cV=|2BM1~4c( zUtI}@7$_8Zco33|BKi(tG`p;P_5kqXqhQJxVC<_&8x&P)%13^zs_l~;XVJ}=;4d@5 zro7B5U7n1{C^k9lU*`R|NZHb;WqHg|PT4HHEh8aMdC(2LUGVE4Q9#;CM!mjB=DkN-gi zYR1pR($U1O)@zM7_EJM{eZ6-P@vnVKpJPfYLQNQM#A5xka74rmEr{$}GRR9jrKI%0 z(o`8a^)2p=DT{es#`zvuX9BxB)}-eGjGU~r3cu`Z$~MI|@-IuFC^L=w#@|;eC}Mp} z7U%shv)!R4?{4M`;<^g@@oDZ7FoEwJ;kQ<~gVL~srr~{QpeS=H<*v+^>zFT>IlyO` zQ}WptnCzMs_uIR5&+DJy4yHRDY-5es2BK^6L&u0KU_7<{t+avYvw`k2g(5Kh8{Z-~KIgW>1?0VCRh1 zRJ1$ev--7f_otr@qgePUOq`M-Z5Yu&4Sj^|aCbWNg`NHAkBaVPc-nH3rpB3N)NBl= zL*+~^+Ak@Tx`vZu8iP}6VqbPbuLrPkBK0m8mv1WydYuhsZ@+!vz8JUawh`w8By|J_%hI{kK*{gVwIk#p)>$EZxAv%a`mU7b)V_QbOi+n1apsR8)&RcwPGI zd^hLOjy8}9mf)|alb{VB5UqZ{r|#;>o*ANl0S_Zb;GF$oTzD8>=E-zN+;k|GOiBAc3%0{;noe^DapLn$ zcSiIcO$qls@)(Rx7~sC$I{TaC!F1_Y$Q~yDi-z4T6e6HacZwzYTxw51F!A2frH#md zerXx}c#8g{cy-W08I7HpAJTy|=B?h9cRWmz9nXDy^LfX-dAac%l^ajwB7bX--N9HK zI~H7NG!}Q^Tz^}ep)RzoYY*`c-WRH6*e9z>&Mv?_+4^^}BMm4O; zm+|ivo!CQO5Z&UN1OQDZT})m*q{jEJ#vQ0SIUb>LaxeF00xd|Rft4URywPF^U@ai> zzGg4)dA_uRB1MvgViwA z!E}A0dK8wJbWk(!>F(@E(ERhm3l1G-GvmicVbd_XX*4Sh7n zC#vbTD3zYb4o2dD9n8SrO31AORFPtGgZRq5CO3DV{f9Uujw&b-A=;18r07jne@I86 zXEH;&gUSpkxH;%&FKL1^-aO<2ibeJbE`4yN(47xkgWu5geu)#B^fm)SFeS=WBEth) zTy>{QTM;UeK^iVLr2a8RgsTTY3LN9jFmY1@9oQKn8~2DiY{UgY@6N7`g9 zvT3xIKh6xum+6}?GnO)(N4Qv-YK}7p*o&nB(cm~Uh&x;5L~fRJW_B#;EOn-Nt=^t# zbOjK!Va_yfPq&g@;#e#~{Aqm^*)cD?_XYxQPt{A8ZfcVw^?+8AVgQmEVT*2CO__3= z&%Y;Q-t(%3kQKYzsz68Sbdh`;&z%ys+O!NyyU8JQVK4MMDN6Cvgy8{kFbwsF1~J!-C|* zXBL4?t8%$OjeSfWop7;~b+0B2DBHb3#aS@8c1OhEwBC#R4~{1-vS0bx1AC*EVA})x zjU;)7vjjt*0&)28{)(?YynAZwqjT^^7`WM^o|sAcDHhiLcp&qH92U}0XfH#?(gzG! z6FaRl?>~uGJ*5_Lf2j4$6?@RFcJZ$E$olw8C@zKl1J8;rkh4jPkI;J54OMm>ZPkL%l{jJ-%1FJ_=fM6FY&CO zUd9^#R##w$+$K?Bv(~5;cye;WBwf&CZ}qbyQ)rU|g5=MyVb*4!-Q3WL3G~-!k9v)1 zvoA}iYs$FSC;Sa!B?%n&g7f2*68AreoX+}2Nhseh?H0*OoQ!y%Oc$Y*&AJCajsC8t zKN;3wkWrlP@6l=MPmh@u-Z)N}7aTw%A6XdPj5c?OX${!tEgt#2OS(1JV=r@+p}beC z(v`lMiVVaC5tk^lh^`0ayVg~-2k+owyESu1em%Fxe_wwKPIWr`#hlfWJ3&&2zd=lb zqZ5*}sJl>7=y1RER|O9DJIQV9a6ehRv8_4FAwvor?mu(O5m+#1;Z42L^G#vnY_A{> zJq_Ykx!2z&=6JQeG<=;ds*@RO#go;OD-|Mmdr|1bR~z`I^~Tm0L0U_~oFy1$mJuC0R|EJ*u0`LcuIEfmJdwQPZ_sSE?lv~M6b(+_ZH{cIK9w(_1u_ZB4R6dpX63;M#FjqfVGE!s!M*k&v06rSw&5*lHTO%c`V&g7L9Xu3_#nMPM@fX_!4$nDjosLSMW74C4&hrSix+ zMsb02<5*Kn_n*4i=nfpOB7J}{f6_8~9C2-%Bs-6)=QY9xeulr|Ivj0Ln1+2Kopm}% z9Eo@dY>6B#bq zG$K9ZCzGkCAc?;znWVA>PoXfon7m*kV+Xv4>6Ee0h*KaC!rxzq&oxk+U$$3xx958RaWrVe1|OGi%RM{_uR(9#0?uzY`Yln*C~ zDs&|g;P8=A`y0eqQAs=T&l!ON{2IxnzA@9&#$Ujn2alJU!T3-;ObOh0J}SZ$gSy7g zBl@BC;Q<=2Z<@Kdp)1P`&M#W&qG|NB8`1?FQB6Sd#knhRg=j4y%_=CR= zPaSTQ`wqX>SEp}7aZ|rgKk)B4-5t+df!uqmQm4qJNB%w2OPJO%&5Y#8+|OJv6=@8KN- z8pV$C>$@uXTB@^*Ya@MR-x_iB1o6@7vwwAkK4C~Vfg zCO|L`oyq_nJxZtF9OHXwN9rp(Y|ZTd@>rEhce*B*enIG^JxMcz3Xfl%g~jydd9;dV zu65eK9|B=T*fpx5NB~QevQ3LV2Z=U6ZYRB&ORm+|a6o%O99|>`xTUCNj37|>z6|f5 z`cL8I<$+(H0Ru{RYB<_lZ#84Fub_qo-FbWm{D0QxI$0|uSHDDmfO7ir&{4n21^mt9 zZzjpcudknZwTNG}rck2Xew5jduJ)sdkEX;R{_P9`h(^*#!cojH=Z6G`=ROPA>qYF59Daykmm+noYyY7@J<#W7h+#*;m5j zht;wp^)=7deMH*m^pHoZc)_`Px7)^sVm}o-bMGd#B6%Ih*{hm`LY}tVnkf}M;<`)J z=`SL~xz89&T$f>|6(1Y@3}P!8d0NG>CD9EKWUrB_$g#cLS=Sn>e4UgsrIfih*pwIK z_F0j-Zf)kMc_FsT)CHW4%>7U=?up7ksoRIas6;5cLWOy1t{Cmv8DK)~Yo&K)CC}|6 zR?q!lFEcdyAqT$?ijjMh$@OG~+cd0JCED@pUDp@L#ORu500)0H{nqO7^=n6__jHyF=vD zk*R4%ZWs8)dWmEtmA0$W$kdP{mHLc%Z}bVy`S@}r8fTR74sTcmfV@yE({4Y|d$}f_ z7k)o&}L7ASF=pnozOldA?U5kmvcfW>ROTZP zl&f`+QdShy>gmNuSyEW$N~Qd%pv=&>)O)AGme;F{+%1BZhwiVDZYoSUOet>`r1X4j zW?4~YOi-m~13yi;+FZ(;YWB#2DnpM@BX<>~^xUCZ9Sc*QpbVRz=4Oe!tO-{5(a}!P zOp{WCSVCb>0&=dOj%uULpf{l_O4}M0=GG_<94*dZj z+;vwK=0{G#IL1U1itd)Ytb!8>ONC=gE6V~m6g_^n+{8&F|^_P2&4JKKCJg zA3p!W?_Ktrn&g``$>oUsp(HBFucU~8aqZ2E>_*YNA6rmnU;g0%c)8GgRo&T*^vIkA z6XXcgJv*$-rbSQY^we0L9D}~{s+OAM`!(p5Hk_l7;r-ohd>U`Zk752CH7TU12C1&h z1864zmwBEVIq`zfxuNr}DboM?ZoYL4o#XbAEFF8Lj=h-3zHM}@+K`dSZ~Hl!Jb*Fx z`gNPC-j^rot!n+3^DheIad0rfd2aL-txGX{OG69Jb8ii3Y+cqddR}S|<7MOAx!jOQ z&sF*ufAa0&4(k?!>1=j0&S|KeevoSINq$2=5}$6T90E$TyV^xcvV!AOQ^!|dT-INs zISg$_aK6&!IgvJ0aWKjkjxFy+KQmR^)IraS9%6$#gtCZ)d;3LEZ$>}OC;%Z4B&L&T zJ14@ygyiTeOcQwkkYOt+qW(7de}&(JwBS=^3;(9@TYpMh$ltVrzhFE*eodbuwEa{2 z8Hm0wj&^Td+87yzd@lJXadEU^sIoet=v z`P!RzvYA2WPCb3W^vw%*SeE@LoHF56qZ_=_R6gD5gIl{5RgbTj{bN;!Moysr zv{BMA@BCQh4jj*^ybI;4Q`6B~Atb&}mh>;WOxfvP1(;d}ek{?b>%B-m4@`RscUDc8 zM$08PLR2CJ>)+IGb^B01h?Dv`kD6fW^TJp3ZEESYo`9bH^521)T={V7A1dpGOji@= z3YVxG&5tLPp}V`DL4#6`nF8miK++U2jc#@q(5Pm-f*i&ec`clJ!KJ?6zRW#_(sAx= zvees~l5GotIqw~YUTt9#x=@>{wX*zqXQw;Rz;A+NM?Vjz?h%-NLpp)4V_Q*6zo(^K z`!PEf+$D8)M*xF`l#+E$(&U(sFO0-}m#rsP$+ny>{pg%B=4u`KI=AZIJG0&fIy4P! zbKhkP*Geem7Ij+hx-Cv(G~KHE0}8rl1gvMH1Bmnqr%abv@Ng@@l?c$(BzL$^Z`#S~ zfG?Wd*0ZjFnarT0>eM5`qnF5!(rLm~2-f^?gk_leN*$k$% zKS4PJe(%nwE&pfctO>E{bz6KGEO!%{KKiAWvH>h}iccK^-p;uuyEfklaFPekhbYaC z&z_?TuOA^aQ_zrZW&`A3owVQIcDlxd0#r+lHUQ-o;Kx16JqG0r4H`N_FYs43G2@u; zo{1X92gW^1-)*lVr2`EXcDi3ObHR#(iVW8MD!Se6U(iCN)r1&EId4eX?agmp{Z9-M z#=o;O>-4Q`bh;T>H2nv%D63Oug-jqu@fJXcHj1L%$!`_{?Zg#CAP0EzUwOdO`tAeH zgBb;|pY*?Bj}$aAl|_f*yOCR^iQ41GzOJ!ES<=@va?Li;ZCcAF@h_Dqlic;d%I6 zR5-1XO0-R>-6g*Cm@+sl)?6AhtU2%#wu~`ya85sAX13LRwvZp%!>b8u&4KaSTgr-- z>;)~EO9rAQx`!R?pnNebHgbd!uiCAbR>Pn3{4zcCBsC&!l=mJ&HLZTF1Zj11t*$;u zm&-tAMnGXit-A8bQ^SAqb}!IwE8KUurL zH?UoMGgMtfXP(*vu-{J(I#8fJ5^f>EOWN<32BmYIO)u!ItWciu;a_3+xs8+lIj!|m z3x+;VvrQhUgf@sch9(Q?b1zEx^S{5L&pC72)8}?Fg2`=5pOx+Ue^tZNa_|(Z6M&9W zg0%a&Aw5yh*e>|{4wg_jzuTihd)M0bzEOfEDA(S_|Es+}w`=c3RTns(;$#8e2*KBe z{<{3H_QtepZ+}&nn%G;-KeV@29k4X>@V(HhExsS1gvEC~IaE0$-5%ew$@qDEzXJ;{ z#LqSAPScwiMN4j^{a{WnXzAzZC(*9Gf2xJO3&79wQ16@loT7PmP~ko%eH4WLB;SYf z$vd6qyMF8MQSx=>e~Q0@VaRMdS)Q26ZWXcc_Z(%8Bdwml`^hil|JEK?w}SD-fME?s zIME~Run%SWwUK8xyHTh-7nL#8*+!U@=(C@qNOX=6DU5jrnO1s@dayBWX)7^IQ8d2j zMWGh~Ae%YVtf!ssm>VU^RyyHtiEbMi;tL6plR~jGI!C_7h`p{Tx|R=~`*@uyOLnL6 z0c-3X!T%x5+fzkmH(=fMx}b3V>U_ywml|HjC3}gveEhS}KC?rwqR`@A8v66O@O1~) zaG1VGJ5=uculUirGP<@Rb#@6M^LtD=wZeI1&COeghTk&jcr>rYQ6f5?jfQ_sWX*(m zmCm9As$xHsP3d0aJo4_Ozg1yiWg z=4Q~`wzo~(i;|1S5oXZFuR1rJ8a>gV;`bCxb>K`fM>lhTQqmysLeH0<>2SjzTD;(n z4n`rK!w2CPF$-E2nziFkWQCkRF0U+k+o@bvsXM{!$}rCX99Ny{@wL$LWmSx@($Uu{ zQ|I)u^_Eu-e#?1ic}2;4ku}M0DwA(zRu5h_Y2Rc^I90qfoE%Ghm!%v|7r$&V^_D~ z99?5L3E@JwC#hj|LVVTW*PL{ z-+J^fnP=6%)VAvXfP(&YKjXLlg#jU~);Fp3kBwE-7j84vIPmZ+w<$2JF0)GU`4A<` z4L5eTE#z%Txjmd)7fy{R;R>Lc&oW&FR41?DH|~8+XZEEwD{_WC5BJwlP|1n93+daQ zk7WVPaR9>V90=^6`puW2n?paRr}y;i9^QhLJEW;8Kd-SD-d+DsC1GLTNUv+sIvyBy9(Fhz9mwMkV`129_Jp?X>L67dzegjPAd|56sn0 zB^+sM1cTpBrvfv2Mrm+`K6(0qI^#z_Nq-QbjDpABl zcMQ6S{xS1JA$|k>p&wrgcLGqyDQ8kwH&kl~FTRX{1jd9XK}NFaiX4XW2@;A+p|2Ra zZg)y2c^-+VE_-|TDwXWS9j5Lmgn7#9_?qfQkpAzxMb5xJBYJan>Zbli|04^RE4BVn z4(X~|`VeW5m~AW6Y*E{|+6BrkYQoZ5Bf1PL9FJdL`Vgam%GLLEZB~QGY}otsd1m~9 zK8X8m5Gn=GRv5bBXQ9;IgwEV&%8}vmd(9>`W_FXmAxA$I-`AEK@cm~l3j$1TM9@~T z0gXog@)kea>{qRB0{9^2mH<50`>UyAlta6m*V&IudGKjUS-l5lfFmK0v&%FUJP#r- zc>)zX@yq#Aks8nC_C`gs88(Q0)05+z2%YO=XmoOaEEp-U-4-xl>WzRv9@cq7TC zq~4Sb4_?Rfnra0$AGaa9Z|a~T$*&`s%=H#OQ_HYpfLUbSGmuh*g@;TZ?K=J=ED6gX z+wnYp7<$>YzgJaR{VA|GP~`dxXWs}DkLnrI_^>(#ZDx`&Eg*>qtZ_y(U%B}0FA0PI z0**R&&PSrM=R-n&;s~+MiQQ)FP*zXxx!%nb3C2^Mn%LLAm6vr>Bh1xOv?p@n76%{G zTWtd7YqTKy$Gp6;k3s5uXq*70L4RFMyaq)dcx%#zd1S0C&jPJY-l*i8Kg(dFCsLcr zh$Lyn-vB=$cn9yS@~sVVg6#fGw0koWVFk{KC~>%{p>}d=nG7H#{;`kVEp$t#>aN-d zZ@W!DMgPfAUk;f0^MCsM4&lKoBPE!ips$O38O{>%;OsKVf+KYvrDb6{d|J3~lHP?k z(MsV$o;K&asvTg7dPB>Fz45u7xXSN0k1P0rf?a@2)XY*cSk#{z=15UvZFs~W>PJ}DbcfzNg z-8V9TGrcz8$i@PF!%kPv#loMu1e>jkkB*s>i5NGJ5609=uZ3fpQOuZR?vj@${@=$} zk6mJJY%);zpJRL4jP3sdg9_g~A#;O&lC?&dSOBpsS-8Cn=4zRJrml-Ygd#+Y;_oc@ znY=Y7K@PtNCr|DTwHU|f9ii+R>ds!z;QYX>+IQkVaB|6X4mSzaT!7mHQGp52n#kwg zPdl_Jf0Efvue+o>-LRXs@@?A6Ej?qc-xsc&Stbr6qA$Hk7b-aZ!RU6nSxz0SK+$R+ zZEi3jUK(z7p9_l*g`D|KnVzCIoi@CL>C3qA?nPg_nZCC0)HfBC@J&a*b8~l98RwCX zK@7pOE@0@TXb8qyPiwpu?eAPDzcC z-Bmr?Von@|t_!(VX>q_Rzs;#nN`mNPp7fGil{wCv0wE)_xQ!S1Pbg&WMRIMf0W8#G ztRN)K7BjI62l7`US6>f0vKK`4YNr)ue@!Up=owXoJq6`RymOAC@}N^Yi?c(}%`XRgo&^ z{E41c=g*}#Z{go%`SWKv!WuqA=KNVwrC8+MtFgkJ^!KVPr|d;0Y4X-_%Xz z+<7iXKj#^rgVdcf1rBW;lo~lTv1)qH?5gC*snw~In?p@n7De8l`G*kwp%=UIrNXJ% z%-axOWREBc@w6)j7(-SlJ(S%~jT~ous`w43t}Ct}b}D9QjL{m&-Vr3>M`$05KfF$mO^WTw>zO>aoSRz-Z?` zGwbvFNYkA3WHnT$Zm5I1XrUZl!|Mf^L)*xo0}vAZz31*8+Gbj_!-rrZ=imNENvlp@ zjr3GH_YfMju{I%G9{$3gwZD7$Wm+-yE$}WcOkrIkLe5TnC=!!dt^}8*|2F(xuTVt0 za0Nb*z5RD+{KT*JqoqV+g%kc5?THiLhFzw=6x4onwdawd7jaGV1E^J%+`d@U_Pn9C zhCtZyJc!mW7Gp!!pDlkRpc)jlMh;qh9}FKKuoBsPcOx?3q`f4XYK3|r zrzROK@#+Mc#&{GOjRB3-$^T@IZZkfnvQ~E{zm=2(@Z5Go??k))8qq&Bvcy`N-@KkJ zBKk2)xi11atabU4g8K6~jcRQtrnf{pbJqc_N_m6!QNm8Q4|ne-Aij`~fh2s39dsc`N{y}!k3X>@ zHH9O<@GCgWkD2YnXE9Bjo%Lkh@Ii`now$J%5h}h3(GvGB>>jux%vK+`L9uYo>=ous zm(&1ExD}H+x(8|ue)o6Zy96puzQW?;n0}zlOLQ6~ivUiSshQ+3^P?g;WsvtL;5XCv z8f)K9-!YoL0(|-Nx0WqbZRF-}J~jT={2k2vWk#9CKg~6Mz`TGS;-)>H7G7@C*8A;g zLH#aa$@wtQ)4)jl~Rm0NjwciUb+r3NLeG=nVPg zZBmJJ&+9Hjq1=bDc(*8lGeKf4>pn;Iioh1aNinvwhjV8GKL|s1^Cst!ubtSF%4#h~ zk@*&4Q?0mRbBGxEfe3LT>aO67M z#H~&hubk80d-ONy)KF`PzTTAmyrk)sZRn}OS+qx0Y`fjRmQ8w;*Up2a$y;$iQTvHG zG+BXZS2S=v zNEar$D~g4URECdNCRWYp=8c4pqxq~s;~C!uGQmi%5vk&hbWYs+)L3f27x~clj9QmE zvjn+~*Prm?7O|jF9qlXU2y4~Is!VPo+ z6n-^NK&yIy`G&wTA!tg8%m2lYQOmisvn$XiRT56GLK+W_QI%>v$ zVlX1Fp5(vW^Wk2>7?>eDs7j||lPXq9bNn=ykyYc-CO>N?JGIwJ-oIsMTNO^F&2*;s zT8bu>=Tp0dlRZ{SV=AJdF{k1)UWldo67AIlwZ>BeImw8DPW)mr6c=j;%XlUE&LgXD z{s>9Wi7n(;sC5`ii$}G>EUi_MRTJj<56w*3hX+m8P5KAw3qNJDxQM5FUVXmKCkRL5 z1flaZEl567t3n=qwO!psr|yEWgdF`1X{n?xAJPMxE+{MF+1EAh9Y0h4!JvG4;6s%2 zCh=QcpW}ltLZ4<^N)P?pPK|*T;orFcf3!6DmVz>HZ>PHcjL5 zl`5|zj!c<(8gw-6g!focgpEmF0TeB4K`mFU!7!Jmi1<8(-Z$$<2fi;5>0Mww8S^EQ5t* z&F*4xc@(1}#e0i^nOo>gr#3fqdyp?g_|0BSvzEsVpBNidG=0E;_pCH>@_P4I``J8y zS9=N`Y>INbl^Q-VJia76c=fcmQo{%FR<8*ijvx(csZ$@`W zdb|!BT?C|@946%u?w!t@8Kj0%l|?+MHvl&v&=6gaIlm3?BdWxvuHzi|3&UdeqHbOu zFb2!KGJ7&-Q?v()8dJw?%g>Y3@PhF8-NS>IJLyV8N*r=h#p?xAxM}!AQw%d9-?i3> z&m%?58=^lnvkzNtg(hrwGk($nq2p3`oP>_U&K0pg&#x?^zt>jkXyU3>P)9ka@2g|~ zUV*WI`hD?(kq;b?cQ*j((YRz@0eL;(hp{d+Q~jOtaWa1k=Us<%fx!}leAOn zOS*dJ91Kq%Y!5Iw`3d*zk`bvM8wExS(xfW+-J8ocs*m3tPo-(=+^;Y6+w^heJx-h4 zN2kUuRuqoJp5WAz$iy-Pi4&3E2z$^U8mvif8}Ocwzryp<#S~V@TaXCVv5!3`{H`y< zUx&oDHM&}|T7}Ajz))%&lfdb9O@x=1zi&^HjiRQ;|NXCpGl9{$nm)SbgScSs0p>9K2IB0RDW7{{CkD-Z5C0A zh4`_*^5_Gu?DqCy1<0!Ll_=PkPs3a6>WOA?#UG)^%<*lOP4}~M6jA5yi#4<%4Fwz>HP-`n{#~= zvbs)3T_Z##z8cLIzTl1GdB)b@Btm@UcK#NrkkbQWeZ|AJur+0{@n^*eIw$cuoXOin z5uL?TqX#9MEsv>A&j5cMTRYRGB=Ka;;$)PNFFelCcD*38u^nu1d$TzCl-O0BP_OGCGg+ew3SrY+LrxeA^|(TfI{w} zP3(qMe5=wk=}jSyyTXZ7xsPtu9B)J135*}oo#vUeERNC&6f0I+rV4r=ot;Uo_>%kK zIyWN|l|~Q554Ma`Ojq~g6NwFkcn^k&^;FJjm}l8dJ{RfaFVE-i?^8-0Bl&q~~%~uioJDe__8BSL$U5Jk{elmcN?(v(}inrvzs2Fg#6LOBS zA;2PgpzIP90$$=AFi-1UrsJ7;dB@N7%EV56wnwN?J)M#Kk=-1;JMp=&!N+O*B|bd| zVg-=s2^s{7NPBeYqca~e7$olQIlvIO6K^qzFzYRRktTG>kpSRbxE-s1QgYLE;hA>2 zd=-W-=XZ9`<{Tk1VV%}hB+;-$7cl9*nrNezBkU63#(`VPoH1-VTVJJ&@j~_+(_EXw*fECu5QcS&v?=q!d_0Q$+ z?dO{*jYh-cd1^<~`GuX%T7!lG}HgP!`|MO=w?=OYcdpcM#vj zEymk5eJ@g*S#e2Z8V#=u0!ECLZb*l5)nWeX?B|UWRo4BJ$uP^bj_KmWd$ksxordY1 z-NRPcMzf=bp!UN5Ow7K(|13Bp@;)zuUvMtuecfZ&--nJ4^aVquen@V}EareXQ=$0{ zfq?ixq>D5j}EwIy`(cI9`c9enrmEzyI)jcuDv+v~jdYLRD!MqF+Ux6ZYQ8->_4r1c zarbcQR_N>{b&GLJe|1d}bCya`Bw5Qo%gad{+#N=Mqjgv_xK4Cb<1ot3)u{<3o(>Y^ zFb%NxleI(;*+d$3`i?epz?vm=Vc$~6G2=AWYQ?`p#CUKjwhi9qjQY1zz0C>%b}8UW zMN2CLPIg&xXGM~0aPBVuX;4%AR!yp|1l}5YS<63qKxQF#Wzh);D{+2n<6AIC=T`e9D_gNts6;RB#@7I` z5xILbLlwl-2n>9tOib9$9hoW}|ivu6Goi zOG`7d+RAU-RfNq`s+?EfnDh+S-+s#EFC-u#`dbqEC&xKMLaAYOCY)8%usXWux>7^h9)I`3LC%^D4l?;K8@*Sy^hsiJwi8q(PT z)A;7(?uo|fhwH>5K@LIbRC+dRSrUIveB0=r$c}CxY8qxleIzw0{hauLTI;EVC1#>S zr;3{m(Z7Wd1qB;|PS*DKmhzi^SV~%gnO{j{eBD1uY3?7hji#uI8 z#M{U%FknfAdjo;d2|(078PV%RM$E`IxF^7(LTHJgc||^R`r4>3-6*a4o81Q?CceU@ zw|%hxsxE0ubHnVOBad^q@B$sd2SWJv6mHa7oKU&yERnO*Pwpx^t}@PNoDOsb#}*R# zds@rP(SwN8`;+?|D(ruNkLW~XPx;zT&KR1x4WeR2;3-Sf?X_C zgPW#p^z@vJooh5!gz-W``0zUjxNTiYp!oezN@`+oyH-eXCO}e z0e%_bX8#;f&E`^9wY|qwvT3-Tea;wb5Pej$I>nh`Yoxd)S?}B9uF|AOyV~8l8O^Zj zD>m5|?g&oF`V~sPuc3J->qMdMcUcU(E;}dj|FHHZ@KILR|9=7mf&vp1G%Cs{QR5Cu z6ohDoBruUgX^}-MN=sGTDutPfvKX9+G9AbAb)}1zS{JId(z*pi2ucaKfw-ZlfLeUU zQ9-F}D)WDT?tPxg%;477|CiV6$vn?p&pr3tbI(2Z+;cZEOu_YIg~_{tPs3^-k8`;l zERHqA435+gRp98lMpftugwpEV-V$AB>GBEMWHHC?vh=U9x01^#nmPRzGa@#aa}bse zXAZCp-WxI(daopnK}H;*b82H#21t2$aQ(nta=c?b<#j=e#PqwYe&EhGY$S zKhY99#!)nvJb$GHrcfi$QY_62`-` z&0?zuye!#~e}V1)W<^!RoAVNQQ`IC({+9s+t7=4sQ+K)oU-Ed;T{34T_JdPM5dOqI z!{1M*58;`{qp;II#HRpH{DXA*Hqt7+_qC@wRf}n z-PaU_EHj1$a$GV3t=5DKI}BVm{Y>NHXb&sC=Qs4BkGUjzCcN!Ee0+7ljz^s@HEba> z%4VJaD-OgiDC-c2cR(3~ONQN&)SibrFnh;AT$S{0DtjerLSI5Mm--fSg>@|7F$9r8 zqBU)AS2w6*_!ZRJ#dU**7KdBeAq@-~S{hzb6CYAiJ7{Q0_;Di3i7XF4#G7AsBf)_y zUwhGkP%b0aJu`=UzR%nhH?X%`tQwov-M^Dk@qjjx~wQw+b&e`!EE6*O(1Cn7$ zokluq7`QV0F}6_77enMMGu0sA!9ltu){&in)n-d2b(+A(`IB`qn(bL^U&w?l9l09y zXRIGi-L@lbaGz&2g?+Sy)7QaEZhz1Vtl4Vfk2VWsiK0=Gk# z{)~;`qr6~PH{d{&{D*t;D=2Vwer#Mo2Q#MGO{8>9vWf$^4zk-YlV8=s^=;TY3s)9e z%$-c6TC`%4{Fd{J$vQQ8?V?m4)6;07-gdfOYQi2!^)iXxcD}j91l(bkvMHZwGrkM2 zFJE#rbsDw5razsfxZxB;3lxKRs&H12jmkr(BlsN+r#ySJpXP2bdwVwZxun)qgv}x> z?Ke-Fyv|GzVd_X7toC@i35spuo%g4;p6&e%Anoa*U+nju{1%8WjXI0 zd(?&dO*rbpW{b5`JFQZ@%{v`)s%Iqls#&>30v7foX>e*EwSw6(vzWj6WcY(wz+f-X zX8w}V$9nLX^PlJxgf;7wKR#@dx}-uF>x-;2UxeK#tedEtPWSuIV8MfTr5e7)Bx#)q_mRo9fRbI9qLS zM@kK*Lp601uI~|>hU?Cv(G(z)BQon8dNpzdZayVo)J~q*)^6Fs`K7G5OiVK|t<@+@g%Mpciz_E{8Www4gKzw~Mm`xY<9FuOQ-ZM< z#5K(>dF1pCz9#WA`cX`w62VbCa3eu6LtRM?Ug!R<2)Xf6=Na?MkO{GBN)+vPrk|X+ zH#Xf+Q&z4BKduhT#*(Xm-zxW4J*V$d{VIV0m)~N*}hrDOTgXe(7S-fk9 zM&(HBiqdN`Z5%I4|Ku~D?MMe2&*%f`;=}a}I72617V$!ZaH6VG8)94KqTyDis>rmO zf^etS>KYNRSIBeO`e@nO!?8XfK8sXixOug^F)c)3wHQAKbzWhZyFuJd=)lqnV#Huh z&;mI_@7jDGgh4Q7?*F&I>~V&VPldDM?Y?y>o5*_WIHem4)z--)J^tou!_JaU+1r(V zA@w)+UZM&GEmY0k{!QEFbf`3bK;&8cWOkhLv23~jV>vsXRG?I>dWF4bRa;hcs zgYB^DtL?qEp;bkE0mggT?aM+E1Lqo;IvzrpN7JD`_3<R3m&OCC5BKeS$Ngs9#0 z#udB(1n=>u=aWzgO6ks#gu{Pw;U~=kH%%F7P9lw{DXYKsO25)gU*r@5Ki$9k^{npq z?6W-*@V+@~Xz`cR{~Qj<;C`$C?Zk0|(@VtKEEVYcSo@89VB-(A-y^*5eeAbEvC{AM znf#Ny_>ax^({o(7mE=vEWW17Kx?RyqobxP!Ah(fsA$K@FnTrc%9?c3N8z1b)o)sUw z)47jiT53!f%4)cEx|7uv$=BSj*vO0971PR97uppK!Igx&WPBK;^}YC>Hr4st%{zqW zTgmm^!z>%WA;iU}ROQoCnb(LzvKnFf;5cW1%@fSe zv-MTC;UDpm({S;VwFKt#lgPBe1@c8@M|qL6=d#`D$e=xA67 z1f(v@Vr-PxU*)l>mMO0-Iysp1BspoXD*LpRY%ZQVP$}4K%@pDklEV0>SG{k%B?!&= zicmefS;fxZp65^`a?6nbeBbE?V0LKPJOu1}Ne#9xS*s3Urwj#rqVi@EnZxp#X8+j~ z3R_B497oL(tsgrSdl{MFB`V5oP*!7B9EKw_vwto8j!Qmp+jr2{iAWD}M`@2YCMy3q zh^pCRX=_{=RdYGc*mfa-_It(W^hgH-8iq58iW`Zvz45}jZd!AMkl`w(+E5(~XmS2U zL-4P8XhWA`fhZ;Fo(9XlWAm^*-JKC+f>qMu*lYwD-pLIYEsGVYrV^Q7!r zYWb*(-)Z@Yb>-0~1LzSZa`Z&)P*ehJX(p!F#=w>k^*$Ab6BWgo*pJ^-p`)h~8}5^I zi1ua;Ery*_Z&iSa_C<|&n5cNoc0EXV#ALp+QH82>v+PY&Jme+F>c|^N3T}pqB8o|$ zp@;+#1~=TE^$FSu5*@2V`(!7Y=q2JzH6C@8s6>f!kX7v^LMQPL5+$GIU#5>b5$m<2 z=~R}$ZNNR}C29*wu6v`KnUON`o5tf1Gsw-Q2u{6eH1|5NK6M^#+QB;Y#<>cUeE;4e zDquazbSPC-Y-?TY>=EWfsyX*y-o$;g<~z4&_|>ti5y9jU8&<91cD=W{De!e~+)5Sv zY_vaX>`#mRSzv$W*&opfDI3jC)g+|1@jN4Vs(3OIoxQ7YX$i^P`i`>a$y{Gcsp@{~ zU71^Hw4nXT2P{C2&kGz(V6fG=L?W4Z!TB#6&Y3RJUV^<#A*;RM6MYRL_C2U;LhZqe zivcr&9dn}_jX#{r5hljQP67u`UDTjcOPg3+xOOpWe$+pws|Xg9Yyv+bDk1-|n8=M) z<}Ue}*{1==68`0q!1Rmzp5ZqEx>|yoP-z7ct(&Za;B0_*B){s zxGT^0aDFL;Mr(Cnq6%882k3{%UIQ(E7CZJOcNROG7f+H>GaWzV1wuO$4qH-l(|(tR zGW2P%r}NF{TA1G@qk(zpY`uY4Kw`j%S?cETZwB{Kq}ex#$EU|ecRAmQ>PTVA5*JhB^caHm`0#gV5)*99|2 z+VTgGAl1wA_ng9vKlp*IGs>t{nY~)oj5nND7PKo!t3); zu*d$x<#q7NTW5Sn+4g-p|(%% z%amsb8_csp+a4;1@U$@$27;WfQfBo#o}leWNEH2?B1|_7pt5`_uQ8DTG>8{UGQh|; zuqDxo8zA-gwDJ0q=k1;(*AGbpU}S<{`FkjmtXMF#80+g za+fprd#V29rSgWd@^pbvZvGD7umJ1@1}u9{^9U@&)($0hrw@yg|DPH z;l`#g)Ro2HB%~OwyD+KV!Hhn9n)BOThDBmq)`2dJEq9VLHH$^d=87~D747~YuU@To zDmx*fIJU@9DWf(0O?gA&R|BNA5$(8I8}>X9jv8W4KzGU(IM)IREO?S{_AjW{oPpWO zO9Lfv!+CjK{33pr+OZ07!{Lo1 z)$y#qiND6)OU~za>ETqHy2ON6sHr+LgCj%Lo~S%cg@#pVqT0-ym)zQv@wNLe1O#fELvlKP5&F5LD;C&9^Sp^hISHHMPRP zF_N6f9a!R>UBW#PiP`Ej(`9o(QjB{dDn38#2!>{tmy>c6V=`Ssejsk@2gJ|qV<298 z0=vlXQ!i{wGHQ?6To-s#m#@ox&?;*ej5m&j@36STS~po!ypasnKcrfv))|qZ1EzGY zJ`bPoZyhfbZ&;#ioJMsXE-5Hz)`zc#{6g?O6MUcH{L%*raC-^2dBQ^3y-5^J zq7G+0;=`eoQ&kmCge-V)wJ^Ypr8A z;Vw15(S5n^R+{$w-30CV;+j~);s$vydZ7+aSF!K{oo2^6Z$6z!v7Fu-Wo`qWQ4n~l zP}7&+x%k0$NIJ<468L%a$UpnJZ;apZ%04TVu8N(E|W>?F+Qn zSNoi<-U-BwF1%YH(mapsCt8130nAr-ER&SyYx)DhoTHd)JZgW$M|71hwrn2-wq&gr zBAh2Ws6IYlumY;DX_5w2yoz;gOZa`AmuD*H_>^Y9MCEV>qZ2DK9q_ZOZ3issW@X^G+`I>Mi z4r~3aB!n%0VHiWR#(99)Z@tn+PX|bfS9a?gC9?Cx@TlagVtlQ&b4X}v7=an%R0g5G z37@UsBuj5r##LkIS1EhAcWl|LO^t;Gg|l{9#C-&ye23Vz+j6wEww9{@EkUS zLdORqGv*iQ3N>R_;#_;LmQ&$5_y~xF=X?DbjG2>m8Z>R_iOh%=gu@MmpJ67zHWg&Q z1wMqtEz&9WMRmzK%x-?Xb}>R06IHJ=iR1J;4OgS;W6$=(Hb^%ZY;?wVXlLf{FCaqI zbL}xO)<0hXnJ0P~QMgTgjIpKT`-9eY0OAd1S`o0$=no-Ws1V6bD^k52o|P zfr#wmZ-88KX?UBZVPAB;c{$P7)I^%3o!8S&pZhI`U@S5G>wUFfVSuY~Z^uiBHPUkQ z|1!ly2>mb=kfX-J@D?-h3PYnc`kHk6N+2FBn@4v>cJ}l|_lJlhJA3(>G}lYsq%Y^w zu6}D%0W;87k)sZQI#GF1Ib;3qLNmCZx*QgNstSuNHic2bowkH3mB17f9|eUptf9y6 zb9ST)4Fd7lW*n~lT}flypQk)voC=Uc;T@V-taMgN&-H?SWEFB%p& zFRX4S&vNQ-!5Zaciy@GyTySyKT*g*@Ew-yin}QfTIPA79Po6=LMP1p^pzaq126cbB z%(BVbtRQw6QJS`*`t;bOowq8#ZQf$E9BN9)5>@E0Tmj*!8{u#6R6c02D<0IdE#QV7 z?S(Ox=LeS3LXl)0P;|y9xXOaKAk9i&eJF43j^U!I?V=K4EZq}4x%INY@pLQ>M+j4QdKZ9 zZhnsbt}Z^RSeyn5^43Fb45E`AG9L-;xjKi$MKyazmLzMeh4oz z0;i4}u&C@pQ`R`$1%}WG+oWmEE1hf^@fomDc&qd#qF}9AEk*-Ehk`noakImD`5P4> zO{#O-J#g7r?fj(f){yhjs89?~_+EU&(}nNt*qmVub&`i`PCd(q zIdUE2=KIFST@e7-qMkPM{@!!`MsVQH`3<~EXg_5hbAFHyst2>?Tv`fizq!d* zm;&NM#wtBi%&$lSW0MhQ^fB`;%T|MonOFQ?%;LAyU>SqPanZgMFJ%$L{}P2S?Mbto-`v&Cu5WVJ zp1-3vEHh9O4Of7RqKVgf;s|xHmWnvO7mOe+xr$dIb2P*zvJm;2$?O9=<^-I$w|HoM zY?JdVIK71}Q8f7}x7`@phxVny`A|6rX}PqR*Ldw*29FHFIWv<+d}azFU6HfzpW1Pf zX6N3=Ww0pJ-F&2j`tSIoOZ`U!;gK{Mp>C@PSmxd{ie+oPc4wg>SgK*9*86HV8h6CD zbK69U%#?*;MtFf1y&Iha@m&XShfh5kJV3U2qoV0!NHN)iP8Lab5jRd$oHl`uOY|H` z|0VZ<%CqdtX0;mSMta|Cd^9Z4wY1w2K-OW|xaa7$Icfw z6Aj-?jcxXmGF9Fj)X(9SYaqDrVa?_G+P}H4e!4VI)K5`!6+iXy;|o;|=Af7s4sWu> zfD8YHwDJW>L}H;_zzmfvHlLjCN>o($_8V0F%?(1#53m6rTSm%>^S&)21mq=7BaBiDrgyXq%%X(MW!e~mihm_cd@hgq%Optagd5pcbc?7{( z#PEB=2q6ulGHKNE>__aN$=oph^js1LV^gO(r(CfkP5z+0_dVRljdKPG0BiZ@i{3|S zc7v!*?5jT37azbQ@VYRzK3+YsCO&*pFrk$fDj#3>EY`*-#fqeR2P5Q!egVWyUs48K zX`R`CwZ4@Z$=jVz_Opp9Bh$)QZivIfi`KDX_M_JG$E(K&VmHpF{`%OIDg&%>DYC)Y z|9F$p*CZJQzUMsq4&&bLXK@R6Rl2$iG{QqEZzQIfQ{c(d$`_Uppv}Sltww?0SCrDj zxx^uF?(P=@`H@}>r7ig`sq=+{lp||a1xamF zl@w%w>8^C~UjdycTJ}#@NtXOT?dL{cqa1HkY-{aCen>TABkY%9b#9EWIA4j1B5HC% zM!2cf_g9Db4Xi(RgFHoBeQfaFDx5{{c0_!UK`^7s`NuT3RIqd^Ae{(E7)=~!ipc74 z^mWThRm+AYCcp7KzsmT>Qon}E)j@}^&vXzd-=IMn z$D>E;W-shr7qkrv&4ewXAqKLD9TJg7O7eBDW>dL(JP(`7)m1!fDp&h?O0cS}i!ZT5 z0z)GIL^h017C$~1Yw@x`>rg{KD2~~L)9K-v<*AOE@%pXJ(9tK$p(A(OI7k1+e6a_? zvx7hsqcoytOXvcoYy)H4*0!|+t|Co=yT5IG0oIvh;Iz6lJNQ?bV30bcM2MYu>5gR?Vi zn632eDJ!@CGWx#ACsVmOk@%F_FuifTOF!N>H>^kM!lj*a(}n8eXURyrHa6omRx%tfo9~N8;B(B9!T5gtwr9~- zFy8Bn`hiyh)CdU$1%bC6P(hzWXcGLpy#;uD#UM3D@3~^uN zr7pfyG-Y?B{s>Xw#vr+f5LAU+|7bTfR3wVd7Pk;*+xlIy1mNU1b-+?Ex<-GCXn>JE z^3x9I-n(~Ufb`T>TT=)hqIc~yW$tixyh2pOR9X-!gQQoAjWT9SotrPFdJ9io%!E4w z2{H<+8)49t%%Lp^TAb49Hp&9g)J#9 z!{+HAvv7*Y*rs0d=0BP)xd=Kgk9WDR2M3X%SgXT?c>UO z0?PH_T4ww}b@$yQo!_GI1&HK-ecl&2pRYFlLSp6wCah|LS&l)HpN5JW27Vd-Po^CM zrr35A&<<`i3c{N&p3hQdo-Z1ues>@18`!Rs`1~stcA-he^8ibDX|n zU#1e%zfTy{qC|lR3P5i#zRf$~$LYB4WZdR?&SA8?CzbuWQRCDNBrKdJEb~ zmMU*+?MmT-34*1Q`ezU0i(dG`IX0rA(Lp9Eel8Ht;!rHom%d-=={GaQX>O2VH7E<} zW5>766rLEi=o4m&w}1kf)A*Vm0!lfhlQnXzwSn^q39fW&;J(1vPSMnFi8ruk;|9xB zP6*K>Epe`JiH|DLnkVV>`L7Pk?MyyifD(X)8nIXfb+R4NL)O{ElWF6?}|X#;hQgE!s^T9!$69aUckj* zK(FB*t1xWoDZDtth#Ed)r(f;bMJPCiYMY(mp8yrkR)Vovh6%@3lAkf+On#M2TCl;k z4wz%vR#TnU``_N&{TegPEk%}bPL|+j$Hl_^Y;$|NL2h%uV_a;2Fxpyv#pF!O&5|Vb zpjyrR0cxAW`#Hy*A<)*d@^+kJf&1p zfTkmki40a8m!-oBuW6sI9$-*9g`$G73rmxi3%8J&#*97D%UwobQafx&D(3^((yU_ z2JRv{a&2(|m(lPRZ+MMkD;^en(Nm!u9VdOAIQZ2@$dWbw>5LyUzi`Gf*%^Xns%G#p zHG`)XmJ$dIQ^C_7=QAxnML}ImyKzS|v^1y%g|^6Qn|)DTrmhL)^^_SwO(!c|yxMHZ zBaY1NL4}}j$-YP!8mm~5XjgtS%1X=wyH!n z&b}D}#*FxRwA4B2i_8#s(nre~@x9!j?;iqRA(U}O(H;B~uXc+?5YVdJxlcntfWN$Y z7m@(s*_(JNGLW87-r4%VJCEOt~11Wv&Qabch+FgVT=4Iz20wqP3%3> zM<1eBHQmK!$PM%n4#`T9gQsS>nQ?1iFvE*!o~>6k`l9!f9oU}{ze)6b(YD?x*98+J zQMBhlG6_h$1yfRYKjbSfTMV1FC@rOx!e!fG9vT?HORId6*&6GaVwbtQjeUqK6f zO_c~jNo`}n&||Melnv5>F4u`8Xhe6zv&4b+(IPU1XJ|NWE!H7FpGmk@7N zW>~$#M6bZ%Z@4(JyYM%YweF7$Xg9e7FC*X`BO2n*G?E9|E9`epIvpUjkwx{0Lv^tx z#hC%UA7yeZQpabz3|#5Eo;> z>#>(yYZ=pZyyLS%V8a@5HG`8KEocVx4MZmv&L7h`71C zIuILE9GrlEq+TmMzF!YxZtJ$0^hPHm7P1*K%mpa=g8=o(Bet(n;Mp{T_5Smgh>IMxMRw=QHcyS)Vl1 zI%A#lU#T<<2?qVaSi+bFB!1O5eqrAo&$0bm5b6?p3v&sI55yY4dOhb>W5zWuvy8tk z#&<*Pk}BP=riC8Gn#&kqxracs&;N%-M*-9bPue%odZVhPP#iEu1Ln!G=MF_i z6{j4^5UjMuMSi*%+CP#J!9s6NL;UKoM)d8WfW(o14D~KZ9cT<(GwXYnOntRvIt`=7 zU_p2*!wXKo*@=E=>fQ*~my?h$Z=&w!*a8igJPm&VlUKDQ*D||PtyjxG_@tIpH-AB! z{e@S4O*OI>($@OCpRtv(O7J+0?vjgLlZ^YGEZDpCx+Y`qVBL^gaYJ}wyT;p1O_{GaeKash|zJCykW1A1XL<6*cu$k6aW^&(qTV}?45*-FR|J+Ti2ag@)@M@D-{1!T-2 z>35BvjntbrekMTz9zOniYa4xR{44mF^4_=Nqkfc!k9+^{KjEYLQG*XL8xJ2 zk5I0gZJwKr@zP5o-ZGmNcc7W9Q){-eVi$_HNC%9*F9j>_WaAy>HT?^@V|{-x#N}I1<9L8 z?CKj`aKePR;B16moF-E(^N-Nm2GS`4=`uI&VdCuMD`Qv5VbaXZbAP4WfE!;rHg#0? z{3QRVYP9bvO6qMJH_-DZ=!-5QliWQ8X;kP{x5J-#m$cis?Vd1nG)%~woyVVrp70E1 zOIsuqzG6~@|K$Zfs=&oI@NUvt;))|sdc^f-6X}amR(2~JRA0o6dw{sw_$Ago(PtdQ zu$t%BzzkA{F^c*RNs{FyY=@Z1eBf_n#DE;~xr)WQRrY^dc-P#79|`^C%OAezag69J zeglgBqmKA`y=nG3(`>4LpL@F&IGeB0Ndt{rU(*KFzDrfv20mmK@`kc{?L!HRObNc| zttLD}T@&A-k@vW9zacKSk<6Y2&XyIKMn;1^dONa2Gj)NlsRLiNgTPAS$m-Q-y`%jl zOn%nQkbohD)L9-;s6Sb*Hsj0>uHSsAw?}o+r@Cf$ z$P~Wt#D>eOGGFvTjO~*@C&YtVSYj-~qOnqXSinc7mjw$#T&ZRU&{v&6#hkYcD*D&~ zXqj!`ZOG&r8)5FiL0bMWbOi;k5fWr4p&N2{JAHo1m)zVYy4JeL=<9ZR&k@El;#{o& z@PX>}c!>(I!+FtLSv`L3&sCB&ifEU{n~&zBDt|p!KWoLRO{P=1CZ-|wqVpcd2pVFW z7z&9apMJ4-LFkw)EbgRCeB;Vqxu~U>nSK)fRY`cO5)K6=^Cb8W;0Q%?UEDonTss%u zP$kvdW3$Z!AF6|#jP7=YGpD_zuYXGj-aj==X4avs>UL#Sw~*>&C->Ph+jYrdhQAK0 z3dR?RF-ig-VoL)CVA}HnFdagx=d#+c8*%$JuwaXAp+aiIH7f4^xqT1Jmow3yX2_ef!2bG!5aprFNf$1?x2M!fADg}>J_ zw^NpM=^`+9)1AZI#{Bcw>KBul%b_FJ`3piFBiHpW2p#Os?-}+G)#a(ljr+LW`6d&{_yD z%^vL7z{t4jj!WG!ny7eAn%5_oEoBmg{6Sd*cT?8AZjj692k59+xyUuM*A^75wVm}c z>)%CN8Aiqjr;Gaa1|eoTo7$e5Qm#z2arszN;(J--ICPHE@j-=jt1|3)izkOURG)IRoZ)Q+>^wiF$DFCCJ@|d zuHjw2=q7_a*J8vj>a|7WJQ*~b{L-CEI=qTQJ{$;<`D#P_A1f(rX*vH`^naO<>f1;& zk3$otFSV~y(prGL4*ujYI0)~V=r>o<#IN-SBdt~DVpOvk6?@i6ZZpI-``!+ge3RFa zGqc~WyYX@+6X&gQwqxpJxX$+EhOP&xN01v7`Ylt)o}4M<)SsycGlk#-uZX?a{gY4f z<7s1#vSY?*dqCO^zW`~st~E@C)xXjBv_}G9urXzVnd``FvV?vjWeQoIFQ98KvRNwG zg!dLF-C6ZG!)*AGwkRvK-*&Bst^mh#jnc^%J%Vy`TQ7=OcY%@`%G_P(&!xX^*sLMeHplK+^fapvPpay9K16pPnPq%oy1HWCezYj@iY2y2g=KY% z8TPxKdnOPpye2CCii*Zr!u;Dd?1yZp?GL*_E>y)#AVVqHobB|^-+>pS)?f5FqlgLG z-FMkK$}JrwAQQhPdmOU}JcGzxE-Q`U%g)f2c;ZH=5?rlp3{EW+Z=}1zrz#+r2o7M2 z90IDHo3z?>({Xo>2gnE3n7%$O(-CT~=|u#{H{4~%P?k#0ber-Gmnte5X{l8%4Qw1v zf}fcL;V0a%4*P(XVK(e9ZrD+(@)R4EaKod(eg^Vb!+a0=lqbW6 zF3cs{K`LYY>I~U}uojpzgK|Do`&?&Dm%@Tn)emaw-ns{KES*+RMgW-W-q9Rvg&)`< z+Xh13$O&Eq2xHqokgjyqWRDr`fQ>rW-DU-;>OAct9W&oKp~hoG^VQ*WTWnb{vTQ7V ztmng2)SaQj72X)Uwp~hhFJz}+Q+MoMAo`*)b*5408r3Jndg@t#`OlXPrm)qlUAevL z+IHa~Zl_MY!fiX+wh=#wu7~bMqXXki&S!64l$?Tyk&9MhY=On7@6p2I`V2^&B`L7*x_y6<_d4PE2XR;|i?4r_jh zc|sO%3ai|QZTN7oFjpS7L%r38EoLC@woTZL_C%87ntwvWBHPqAJ7=#XMt4*%BA0$ zrlKPXUFXh_)nC!1buX%^W;GnWmmSHhS@H%JOir_j$4vuqrpsMDN~1L@q61xf3Xtaj6(w2X+7=Q!y}z?mhr; zW8Vy`QAALtUZkwBc z5rIxBW5jaHDJk_pb!Pjm|@)2eS^*#IFO-2Z?x5+-hZS%#LDEno*tV^me zuP%4KTb>UGYmBdeU0>F2Z@vMN4L-2!Njyr>epQkt3>yHap zCKtiZjK3kOGADJa!lPIZ7gSN#SaxqGnO;1G?j%8QesTD;$VyyM4`mv6;kcjF+HdtN zPfZ402A-?Ganpy+7Hpq-u-#2&*v<<*vtSci1b&bWX~6$4&0~1HMyFyhidzw`kv4}I zl3yt%UR|73;x*?`+NSK%OiPou7$C8!5l#LK;1>D`NnN)vsGlk3D+oFsrbrW}k9+u- zwac7>7_(4vUh1r7WlP>bggGRUoSI4EYnrUQN~XNO&_`e8Ibh*L{rDDE@zb{cB9VrX zYqA9WIM4rFyX#!s;LKsVvkQdVv5azN@yY(3G5N!?{$%8F$*?56G?VbfV@NpGB;*cF zEInM4j8PFolG&C~*d@3pKXxMAn8b;SN7*=WN=~b?lH=Lg|xW&3KymEtJV3S?g_cg7N2JB7#8T^2U)BuAby`5;Vq_;AA{KWq~$4?5K zdhYE&LojDN)#FRCK0X1n&JP)v3-B(SsC@SmTF`!lL8f$}1DJnp+y1SXW9%m!GsL;q zJ*fjV%(zoP{|OB52NKShOu_;ZqTNs*>=>m^xZI34Aod%6S%%o^f4qeJ&zr&$5U_H+7epQ$I@HXnx_=pE--*U~Tjj zm^H&M^`I}>*qu_iqh-)Ij2O0|Yhc1b?isue`vk*&4#*6$SNA8@^WB4!6>ftoO?8$r z6???LQ5V|94-QVS>RKQAc-9~@Jl?Ns9a<{1F|k&e6B#2hzZ&j$9=O`FyE7vLx`&@~ zjln{XMFtj!|Hc7qjCj3azJS~eb93)y?l6Cf7}EAzYm;7cB;E3{0kp#{!NRRBH%Ogi z#^#Sdy;K+P(UH?Gu!P~9Mk#S^FpAX*x`Rn2(e(u>9B$)!95p_vJ0JhYIvV1GJLZs# zUCB0%Q70I4tk*;?^O$$r$ffLKYE8I79=TWpOqWXbH^a?a4ZL6HmZq716hJZ)U3k5r z6oD;`8ur#6J#b6Bujx~%ohCAynL)<2v2d$OU1I8MT1%9A(R!yJQ|kQH&R7`1nwT-g z4e?6qR!W1Sy!|KVcEr){JdoOf04ux zpXm3@daIt3T;_|W%o^?KIi}dn&cGG~XobH@IL07BU1^j&kjw#$3h~{k(~dL|nWw31 zUKJg{RYsA$gDfnXGWD%_)`+~^wKy#(zPJW^X-P?E6ENPn^_3mi+_&@H+@fsQAr)%@9J@I~(?iLvO4Pc+-Yr0;$47=gQkm=ZHi*L4?HNVOl;?jSzFqh>s zBs$)Z=qKhwV-Q4DUt$oY#)mvIeSrkIWGa0{UF)y`luS8ef{-ig4sI;V8l*I`ja)~! zhw(f`*}1S{Tb)jx4XkyEo92@qwluFH{-9pX;h6CG$%lErWR5DyFC3mmXKOIrN$JLf z_H$<>b-+>D)wKU%^I!k6bNP_EO`>NDhuDKRqhO1 zd9fX+v&}#a9YE`SX20DVsL?C5QH)9l{?wld^@9deYWN}MumPUz5xo^2Vg11v+hsr) zY(Ow1F@i`d$(WQ6K+kOO|l<_;v1#*I2XWkQ9KEovt+6> z6=<}Tz3h&*o2jyGv|Yj^;ElF^i_P#1U1djFv&SC}BHUvOqK@+-1B`&nc=I*MWc=Gm zwiuH2HhWfgvLrj_ISo#BJX@;_Lr*e_ZOPu%r13G79N?+*XLmYF3K*G2xXT1j6jlBF zmv}W{%wJ0>fpjhB?P}fgcB8NZ441$M+Rkf66I`5-Gh9ZHE%^d~faG#-hR&|loopmBT?D-J~Eb-j;9)|gD~=2=k%u>L=vr|k_AKLSpuj8yOmD2`qOK>UCFd}YYwxk& z4|V^RA`Q|_&i^eV*NZIF%@OtbOR1t8(6d7Qp%NaU1RWYd+cXeYxI z8;H!%5#S~o?k$^9w)71~rlM7m&cHddiqXxA+oTL%6q&Jra{`yZuQ)rjza6iVU%^KpN zB8eY!F5lZ)0=H8AU0q(>%h>&lVz^2zDmf1efb)>!gr~6(4g(W)Mg7 z#+5G`$R!`{{*=_&%)cLp2D@zE<4ilJjUfxH!mQRD;)3ML%5}E0I{Uq3CHzKVayjJIg7W3Imls*sR)73~B z|3k|u*qo;LoELkV;z{1{q*&tGLvdg;IEZ!jPk?HEFy3WZynEz*6iyw(|1p=>axkxF z>g~4mD1jw9$j9}EoST8rKZ~5+J)8{2f6CT8AvQX^DfNIZxocRH>g{!bzI@RygaB`h z-ea&)OTA(MCE?BJreez?o4QAKnNb>koeOLkC#yn>WIKztDlvKl14~vS+nAkFfAz4| zv<1P`D*DD2OOm`rLk+zGOH)`+8~k{gt{?bS=-T1&!FwWSb_;LWKJ5Gsoa;dvE<|t; zkRHI{S5w0><^PWI;WOO-=<=_HIk0yKZ{9xiXa4X;F|UzcvUrRs+h-PG3nsuPpW?%Ft$X{+FLqkaCc1{N_d5hmUUyY{I-{Y2o#?bR>?7qKt zfn1y^6o2NlY`Zx$lnFfB*3{W6n;WmQ$~K(;LzOrD=se`ToLr*Cusb}OI_8yA-d2uJ z2f?>EolnUt;kD%S>=Ksiq)v8M!bH?9Zc7giZw^LgGNo-$VS~}ec*qIAD?KzXu{6^u=M3=zyEyr9z~RYZ3p`p)$-wqgVIw{B=v;Zmuud zMxi9GGSWZSopCT}VwTM*vEJn8Yf2-18h?^4sSHX_X=_A@x@#yyrzNxrZF;-usL6LY z=TxXMGmSziyefI3y1K1U@f%;$0}^Y?_;<;io1v6rQ|a_W{Ob8@)}!$JM`^kd-jkpcq3f0V9{X9UlXym*zB{S4w;PXQ~2^Pt`%_|;RI z^(Z|5QJNs(=kNsb;#F4mb2{;QN)XRK<9Wn=9^lt|6rTSm-Mc&=^1P83ud=eA)qLym z_5h{DZBz5YigBiG*$@j4EGJ)JtVH`-2P6wC1`lBWmsdyrpdmpppC&91wm8FMW z**a{ZU^A%T5>zhg^DlI(8<(6y(XKC^OxQ()neD_}`oLvXqG;Mg5K%&L#{4SO)oqi2 z;98X&#}J^%=^@BnAnCV^J)~e8V2~^)L>vOe9o+Tf6{?fX-KfNDVTJb5eE8_YU!_y( z(O9*){*nHVEY-@Wz}`~ib(SteiGAg}_h{sI?>I}ciU`1QiF0>yB z46I6Uo0p(m35YSWQdejAa8xL*!;FB0a(kj#@yN_m?tj7b&&XFF3$IL-n*HsD*grNZ zKdwckFWzd(8dr>=31$sL*mLg7(bUtqK3XfB8WN_Lb0IMozRx(aF5Xbiy(XK~7Kh=6 z@}iszvQ-=^-P*pXSLx4&_{FFhKMY-f4#Qs>J}rRZIM?W%M7P`W;vLG=W9kQPuQ?C} zsESIjP{z6m=q+_q%3F1De0RCMmZ&884T3CZbJu{eiAs!CbUb2yh&}#qkSb5S7_vMpn|~u(O1|8| zDH(fbO`nGNQN{xf^<#9#{RV!mpx@z~umE0-@ijgQw*bX>RX|4}MW#btogwTtQSBr* zbgDvo)J0mS(z#FmQei|_F3Cl+9poy9`wFE9T8C;!D` zUt~`^-)+wt_FVjYxH$II?Lgj->IBeU@D#^yQhXLLQ8mSQy6c^Lj|` zAjIq{_8a;#CryIq&s8Bwj2sHwiNQ2Kx4!K0HvkJ4=V%nLD0^lPft^Adc1WMe2dC>* zpmwplJD;Cr&i@*MSW9G}wdwCfLK>V2FzLjbVmsvtr)|EKYlWu1j3nmoRQ z%;xcglNib+vF#=wgc}|12wL%#kQNzBT=L5Ha6!fc#O1?gq-wrD)G4xY12!X7ax%!; z$Q;LWZ4`^}PoNh3g88pv;0^hcy){@dTCT>QRwM&h&fW!!ZbMy5eKH7Qi^_+(y7 zL>_(A#hVO5290I=N(x9Ju64b|?sNJc_?10227(|IR%{1RN-+Mjaupp~n)=8PUb0oR z6nUnUECr!SD&-ZZ3_dq8`FBxP>`S`V_6Ch56&j`sIF;f2;Xb2M+GfnwTAddjF`uJ} zl1Q?cPZV=9?lBss^T}y6OgHgjHB9xq8N)wc^bslt^&{XFR^jd(|K!dz9-jec2;jul z!Ni+-0Z;N5kOf-KHRJ#kc{RoBnR6&S6HS^7aL?6#bfut?-F>2B`STW{W8EM#TX8r< zGi|^<72!-GxA(Mo@HatamYNqU56nKaY{FItg zm*wky8Vo$&t@qjG{OK6OE{vNAKdln~*Tmh!ynmGZtF3>ss#^;^(1kT1Cgo4fOR4{vb?V@%S)b)0t!NP2o|9_`1h3U(3A>3<9u>) z<-bAhZ4ZIXSgpPfhH{0`MmH#%QYqP*42@vBr_C$X>qon#G094B9W_o9Y`l7+2Zpzv z(8y|Q6blu;idVK>gy@NSBkJR)ikBY~n6+dbkUzTQ)AzW033go4f(wdsUn0UTFUj54G9v$6s?fgZO8~h`q~g#P}Ro*V+ldmr!?L7H6h~( zsj?v_*^r5Z1Z_xz4Vgs92pe*R4Z%ib=GcNzH_C~bkXemjlECpc@ID(j+XhawflF=R z9200xt=RB4ZFq|fpU004w^qxuR@(6Snea~RLDa`)t+C+?GT~`kj{2LvZO(-6u;CkR zcuOYSEnnn7dKG8ub<5w$H|;yd)~0G2;nT7t-Kk1jg-^kT*JvEBZO$bT`LX5Ev{^d-&fW` zaZd9bymDb8fp@bjSuSE>A%h8jps~KJ%8&PW-!1QwO!tj6@>?7Kdzoa}y2y%Rwj0hi z3^F7C%2;D69&~pm`gaaaV7=UfrA^1i0aSZ#+0;a+bD=_gH4O6(U!<1>Vzy8UD+#1m zhpq_>eChh9Ynwiuc|u^;H`YVZBj7QRs4aZOF%F83Syof%)C^3T58n`q_%|+!bV$iG zZ7TQi;GL)gTxRxIv$#a2iDG|6M*&)G%tG(ArXOZ45+G;1$kwu&iOh>3az@@A8lS$+ zKlHfi+e!n8VZe_6_#%H>xpI94hGtF-B*uK*Z&knTXUA){l@5F(ww;?#2KSxOx2iFH zRQTAcr;Z9VPVZaTC)``V?M?|FsNePj!oI*lL9VY3gF>C=1kBOQs@a2!YYJZurkB+W z+%x^*)V>*b`mKtsTG64H-Z;0cO|r*5Wz-086LKA97|{|7UDuRhcd{kA&$TQTk)qV(B;PqQ;< zt|ScgM_dVCY-jTmREyTU69eg*vMU4FD;T|wC;&r`P#ID(nuydn`SW|VsVZlF&$`J? zyHMQ@7jxLokrupYj@Hc1MV+brbM-<_5Lf4>R$7d-4!P$xF*)p|ootYN7$RPcsC+J-lwctV?oxu+ZGsb(;4?|tp8VY=5BTe1FEjJUf2YA2*_93*9*=0W z4qQ32YkFnOyeD5IpfzHKFVX;BB@6w4X;M`L`)8D=jGX0qKGY=V7FHz{4BavuQ` zbfN8Kd*)gzc6A_1J##sq^vsWNQGSpqAPe7~@Q~T{yQjCsx9+$*Vw8(-QaMelwSt)1 z8N}Kve}U~+OM&h0a}6dh1yKU#-M^U6iA3r1Wj=o#Y%>l~0pAB(yDZplJlBP-wOY=8 zLF_d3xdAPC-V5fk_O|u6?oW<<;`oRX&(AZT zqlgkpp62t%q2!MT{}4*H9NN}hQ$~IdN`^k4eSiJ`JA5#Wdibb*{#)=d{2A~OyVD@x zjE)u`i~ejrYl#v*9_I7M;Uj*~58-1&uQq&4`04lH2c|e-=Y6Ok7Luro2!bmGZ>XoX1?^w&q;48@+$Zd>7^dY0Q4i$qh&R6*|ex#AYXqXeMJ;%Ltj< z0$wV-2c4jEg*Ypt<9IB#5Xv-^q&i{k*Z~C6{SN=aJAJ?89 z{2u-1^lzi7BWFl@`1G)r*}oA2!e|ry@)7Lvu=@J=9EUPA_Nr>)VdpeC!o0U(e@$W@0^@3(jOP9mV3K9VuyWH1fH$qy?+DfuS z>rEMPt@WlVWIO$F$`q!!Ds(V5IODY*{16)<9K_DJi(Sf=_N1%J$zbf$Lq9mPk<@3i$CtXYd-zUHZdbj|IFrt^x*`RZA zohznTt-G$k)&e^-UzuQy(NVL`lvXUEZP_*{K!3!o86g~q#4DCG#RNULn9Vl&5ui82nQixY)L;Q&~)I)1S_2A+jm48rP`Pz!z zBYk&oHeVUYGZ6>PUM#OjR_V}s=YZqCNvB=|T`Ej}04_o)2%$UO;*8lqZ$_S3pqNm9 z;+pj~^Vojtg)armqE>u`{pl@EG5*Ohy98TG7aaJU_NVXGkj&cI-!{}JUs|;|;@DMn zv3}{L5|TNaaYXHO|7JU5sYwA4)_6gxjr~O?qqkqoj^#!&(mY>OKEFAg&t}b`caTUs z^6=IfD^28PQ@qS;YFDNX$jA$3zncAL%m6~KVh;d*tPR0nj(uov|77j$_sFDg+u!fW z8Jf2J{q+7ypW3MG+U35~?COu^#2vf(XS%!k5E^$2=`S;0>$3OvMY;GsGrRkjw(ah( z_jdQ$Keq|+p$0m9(K8HFs5tP??CGD}h?|iU6kx168-*7AMw`1moLjEmmDc5yMvBX*O|$Wxna}a0AVZYa8&Qbaxe6@w1g?kh zoX=bnI`JHfrgEV>{Rhi4T+gSG6VyS zw-{Y7Ep)DAY=93oh|eB@Ny4?I1>r7nb5jW#vd9|v+|U8ShHKj}Ymi~$qq#xZ9Vzjr zH835nPNJjp88;i@980#0`_^jLbw4{=sh+vz4h`CQA|vf0?Bwb{+Pay&t8k5(F?Mi? zUzc?|O(&krsiMBBA&v%mMY_A&iA*QzoI9C#wd=aW`TD9|Y4C`F^6zgG{dHv4 z8G+#yl7G(lFvnD*U%@53p0ko@;+JI(GQ97W=ZUI|_m=;@#F6(5(d@`U2rhq5d5k-! zJZ@^Qn=lNcnSc8dzauMbHYM))5l6+nGv#lqGZQ^yL}$+2XfiNEE`^Fb81Jna!B_;X z*LQ4#hd(chX%#o#pC=a^jk?Jj;$~GZU+4gnrz%sFo1SVAW$g0Nuff9g_N~saBQb>` zMwPJ|+v$Ljg34UwJWk@E-Ub?WaUdG3-!q7UN2)5OB!}yofmdd>IvAZY#Ff zmB%-)3fWuD&^G{N_@x)=;r9X(P>IcbPG0Ua-Q3>|f3jCw{vX*t;=JKu$qyhBD%KZb zNL$DL5Wbd(SCYvW{R=P7;xcUL?y_Z>ox(w2GZGb7kqV?>1{*N3cS^33RWAZe#m_U5Wkd$z2PB4Ipwz{8Q)LHb?%JRq?~X&N zJY7`^D@nY3Y8~=5w*fC5Y_izp|)Vsh{$7 zsJ?_8-A!D1d*(~<{Z(J45rS`4CZFJ&nMo=5Hr_`{L7M^Nk?L**OH~VM0_O%k=;HC} z!IS90pOD#h?=Ni3MAP{z#BMMa$;8$Wi!CWTsZXw>D6@`xmP7&B8*pc$NnhDppa_kb z%KMVfrn%)d(ntdB@9M*hEPI$TGQzlqmb|z$gwWOv0mk(5x(Te+2QK#|&L>S>yyMyN z!5eXrV3>Yrh++8QX-i5nwb4_wU3C1k3P=nq!Nihd&nMN#nPxu43js+}v5fqi1!$tO z$<5kmvzC(8pUD~yCeMMi#J#fkujwn>&_w0AZvHCrbL6wst$tFb3@+xf)Oi-|bSBMY z>8%U2KXdNC$-IJrlNEEP{RBCf1K^UUvpLzh@K~az5ryKwqU)2ts;$q0uD=DSQe1c? z17F&pMA6Sr*NP8IpX0p#&I%JbU*gzik<3{ts6eZOT`)Y8v39n zYNoQmltKjsRG{@nNkFHM@z-aUU#CN279%-gYw|B(9@^aV7sx|r*E8hNo#IqP=$YJs z&n+Sqm)okTw6&^RLGt&oxjBnD`IppC=Q)FbBX}n8{FM`94=FoArriH&m)HPH9-;FkD#$7aWg0K0rLe^?XScu8xtNRgRT8j;)26vY^a+oAFRYp6)e z9(d?AXTUJ}Ng~VQpOHzp=g3vN{X#Y zAtvK>0T(o=p>0ND$r%7d%BPjmupBX&`@DRH2|l1Nm|3P2CrGA1e2kx~uFTBvf26Q{ zWgi<;HfuNOtEO{hPvs0{&!L0PBuX9NWxgtt`K2?mc-IO(EP{4pKzR*18rS5%OCT}2 zP`DfjJtHKdGAufKso(S#4KvT)K4O*#`kKoE+4X~%;D7hcveH$AT-3UEE})Y zHC}_!COSaF<&0sMMk2y$3+xL+5!o+FS{N zKLZSAu)lZ|gZ&nAa)O>@y=^i(*cX%1*!llX5GRVRIaLF33xj%P8T3)5?j=NnC zIWMLsBa+xQ>@aYYah1OwjXn2)mgq{n!q8E&vu(PI6t%GxweYvGEX|PlT0+;LUKiOX zUlmbAK0)XF%d)C^jDiN#qv!8ij~mKz`AYXItVCg~&RHkLUgl1n7DrZoE)^vQX=I@* z7-Z~eS~8Zn@tcemLUzU)vdOnp6-z_ae*s&iz~2%YS?aDQ_C&4OqI5)c z##tChTNQrL%f*#QHW%B2?6k&zXOnWb#y;=Y5;zwDbY$SCfidau8vE)5)Wpd0JY$vT zYT}dntA%jbY{2b}#i>7i8-003n!#D0?4;0WOQ^zkOjC(S`j~{@{M^yk{M=n-e(sl^ z*>}t%;={gUo|t5QW;DVvdOhu@e++-;d__B7bTb7ua0no_IuKinP6*9`al44;oluu_ z^_4ZTkrSIW0=O4oF4pu=U;p;?F{i%s)^Kkwzn)hg8#mwik~^t6P49H&f@82XAFRzw zVY4NL2j<018ySMMxFwjVRm+=$&3!3S4_^xmE)%s27~^Oo5N_y~fH^8QA|39_F`N;X z7jS=II$X+qW9jf*Fg+&lQ)d#x5d@EKYPq%#kYX!hhx14(odSEDmN{l23@f!GTMS6jgqeQ`2)H zw|=o#PN`EaFECvK-H*|$Xy4h07j?Hk;6Lj_rRnN2F2&93l=4(Z*S^c;tIot)(>b%6 zb2~?<&hDH8PU}`+&R@oT{aUPNQ8@|hT1UFzgdYDFEPOuwzCm89FGV3A9Mc@s&k?S7 z&%?gklSwe%p_6o|1uYB}#IN+r?$gSz_b5F7Q96!Fr@!F&FfU$ZWk2uot>-)G3mxYh zs5zy)^G-V0CAci~C!dt1fH$IWH`tkR?wF43I!LG}dH_)d$Bwguh0D^L5M7;d##*)2 z`OoC9(#aE92^PtjoK~;sE=ilHyx|Yb!z+jm#@a7bWT3q-vTNUK_KU4Tr}oT^hr=%V zKc8G@*18q9u17X|k$1G`<>{50dT!dmG!bKDcb#-g3AGPq<+nPS)ZD=ZuausAf_DM4 zh7~0Of-~0E#;pIR-%B`Xb#9y{Ibb;;*rOeLoc_lA`XecmFC)^#RJ{D!VJ7()H$(?N z7(1F|&plDtF)OxnXr@+U?Qjhi##i_yT1`GqP0og2yK9T~_DUC~i2#nska>QI;DgM= z-Ex(`n@h?kNC{wCx_m6TCsA=7!2)%?6@z3=c$ECtu(Qd&In7J$t1!J0>^yFgwNIU5 zgq;oS06Tvn$f(FEd4+SEO`tqJ;J^I@i8#7VFb)nXK77@-^eUT*-KgaC7Nge+yaOzF zU|*HZ{}HW#Z03^ot3CCb9oh!*>pVvBlEzo|Sa7j>W^ zNIk~&ER=IP2@ zkg2i8&2(aZjZ$uL1A@(T|D9xd;cB<1_c#T%#yJEF+|Q?)7*pf^hJ-&$?#C~OSMec( zZz82jRDRLlPvdKLIb;f3Bxl-6jum8=WTGB&3%{Mhb18lZJHBooU1MriN$EL2F#Ot+7OyNZr9;aSQO0Mys>S>|6z=JCFE7J}5IUBoUnYuCjtL@9^!*vY*=>JFA zxxiUHr+<8=8VWguk|-k~EDA#?W;DYYX2`u|t#;kAL^wx zUO?hxeYuz~FYqOEC%GUraE)_*Q?a18Od_qpAIBPTq7DEB5#GflIJ$p*iG#u!E+u=R zt%+>Su4uNBfqZfk1Nm_@H@2boik&na7V%fEia|%=sVa8&8C$yyrp6H?{?ED2&hwpp zd6{)@Bjr3_yOD8~NYlHTo{p0%M$uTXn1XI8u`om>)E?)UiX!O}dkR!}a(8b@VBj$s zzV^^yhcj6E)?Ay^S-PSJ$r{aN);Gw$Cbkz6QFfnNLcB89;B+^r?9s|nicW8$KGE@- zOShxx3X(YIp@4YJZmxjph}R6T(cozfuVH%h8~=|3@cOvTIUIKD06%P5V~UL(u|IzA z^tevy?m6mVI`Q4j%(dblOKWOV1eVjUV=8A<}>kGWw-YJU@4 zOch(+^})R{Tiyf6(enP4pMcA-xSLv0yx`W1UPMT3qKkrwd5p}4a--qe)y?`n7(MUb z?)MBnbd%)F>!ecErTQ#vhbW z!^WWTribpSLMJOtXGema{17U@!)iM*882MmN;|kebY$TF&IC>E8In4&xF|#*G zlxw2gpjP}>H0#eM(rrO<=qLJ%-N-Khi4!gB;R=YPB zk8~@{7O&Q#OyHs^THddN8_x4ETpzSZph;JpLXH}qY0a>_AL=oQl`L)cS0*KBrrT+z z1N=$rKNca=if_k@}EaVVnBU zU5ciDheyDkt`t4q=uU{68{_ zO<>Fzx2myWB4X?1GJ07QeUlW5qhGxHN z`PAS4_^!Y3_r^XqrEFLJO8I-2@?W>9qqOiZwAaa>O-NDwq##4H90cF{R6{(nq9v{^<++vHpD&cc(nkV&+`8Bd$Drc|ZIz zo(h)oT=?bA@JqL_%o}0K;P6XH_~n)G%VFV{0b#9SVM_K|-^I8vC0A=jnDY1V%XjwW zoGH7qeoylE8~zsY_jmqY;g92z*1P#rdfUGnNPCdKvHZ>B??V0x%QW!&E&h0*v6WC= zt!MKWJ(CbtO^YWE(?%XmZ42|Y(ZdE6@#uo+shyn*1m3z(0wAR~qH2iAgC|x+eP<}aK zc{>qJCi>Wj{RqhkV$A?hJ$xr-o8d)ih;`5F4}lS zv?TsHRy%f$HNiG#+4Z>)Z!;QC;uEy7phgg6#4@T%PLL2&cF*xcCdJJu)n7Q^BAQ7@5k1-@8}9u0>TL)pOI zX#1B&`}ZDpPH7L#U$ZMi?aff5si!nf*q)6OCKr75hK4Ffy;>V8$CeBrj$yPt8=O5I zudrXA43dBL=LO5C;cPe_gE#i)$#g3-I6z6hfY_Ie^Ii(!p$Y?YtO>6ES`_0%yOXgv z;^u}GVwy=zMmxJ*<7g-ClYGd7N`=H4s5eva3$M<ZFC4bC4!E<(-b} z*o^AbgoRZ|YuJ2c(RiCMMzMntodHCpWD~?PeMG8156XRKsm;^J{m{6s!zJa7YqyLY z*0?qjuUs>1TxtB^>^9VlV>783MWt-dHN!@9j`!#1;riK+FQfSqykTNc%@PY>Lzxb- zcgWgZ{jd_Vd~WLKP~+YTjXT~mv3NW-x{5zvO;Z^3DlXBNP5Bb%iz+sR6~poM(D=63 z_{wd3y)%l4YlDhdq!9J(-gjdK770i+ccgD_kZ-Q>scFiIB+hUm^VRGC>aM%b7s&++ ze`|bgkvxCcEf)nhN-sd|V`mLa%&>Jk#@BrzGikVI%d7bJXM`=XF6oL9 z)C_L7Y6~0#rIWq$oiw&-QLdd=S&O-;$xk+2(bo+jix6iGFJFMotH#$h4ZS+K4xF1o z{`B}x1<0hE3?Pag23FAZfr3i5w@1Bp5pGTRKIAtayDcCzH-5C8G42hHP@@)|sAgte z>MqHR3}I1N~(-qs0thI^jCt_JDK2s88uPoOT zC-4;&;5Z~Cli&W-g(m8(U=6)+0(Y2YW%l8t{quhO|ErVrORL$ylJzed(`;6w3V2dA zM=US?JAw=}99F_J4aqghlNRaLP`ctFL!VqB5S*5#rr{38m z{u}h&OgGu?#rz1D8E)cp4Wse9^0AvlBX>P>$tMKs?HkOJcD_)}i}ACWDu;#5$x>=%PjO{$QrS9Jc3Z;y0*Mo-UZOhkS_ch$DD&8>ULvs9T zPh#ncD<(6-OU^X@A(CI);HcsZpH#G4L^-x@hj^>zy=zfSv}erL*N~L=;F!i(hb+N= zWI%wkXHO$%Jb=e%8c0Hs(-nZ>9b#v}1A_y9WW)ezD<=yCJ#y#x#No!mwmgV0T*q9; z{W`wCn`6y4?auMSPjhoTpB*r>tw1c7iMLJNxIS#y`NWSXIQ?Qok|%m^?0VD&hiY5z z!J!%xnx1`h=CgeiJi1RB)@Mws5E|lYSJ{|(D+@S$Tj^)=2Qx2#xu(! zcRd$rv>+J@Pw^5L2W0w5zHU#A1g*dcDAYv~?@*bS;;PovjKJ2caE-QRio8{h{SBbn zXQo_T%5nG}D!>-kO=RT)OTWQlPOF+uLR zUl1+f6x$|$>O$9;<@Vf#EzQ?kOa8-V>fq&~PI2ulFJrQX8D=n8W4!DX8&rkgiT^p$ ze(vkXIF+A>Y;KI021?_iIam_KCXd7nZx+YD1;}cmM z{PSH+1az;_Qm4yyv&nEE$WLRhd3ldLA!b+5a+gTF(8qRAoBR={9&Qqpt=_6A7;A+s zE`wm>T?UFLIsNm2lx+kZGF#OJVTPGCJ`H@${q z?L-pNo{0ha-;ew9vCBOpB%~GZ7f2$a&zpyWUS8EWkWE?^T(m~4x7vLF$#S6$yqbMy ziqk*V2RB(#tl5{1FU>alUSkK+<4AhUZV>_SzR`X~`pf#@L|1=>@Bdj$)SN{2GE3}k zn;3_2671+j*#QOew!-rybakYPK!d;t^X)*Z^d+a7weTbYO>s-$%KqAxcLBeGRVQok zk;I)cv6|VES&{kIQ^pot&^#-d!Wi?)*RcAZpOGY;OT9r+?IkPjN3Gp9rW1 zC~n1ClhtbSa%&P7ucgw*xap#G_Sn_r;0_jsYL(K3Cb=B)TW;9?_G_i;%d~)^<`cIs75}hmy~uj^|Dv_ zJq&Lc^Tdj=ho#G|HSrI{N2v&^yrqSp+KmeIK4B3Ch}mx!Y-MLa*QC#oh>53q*|8DZ z#yrT4;(rxx`(^uDW@dX5c+9vSOc~kk7&>n-28Phfh7gX0O|R`xPJCNSdiFFwoJfQOz&5v?obZ9)f5s>f9Y733O|3MkDzB`;-aw3_V_{`;&`J4X6UVfF) zPb2sIdU|@h=O97MObd}jHOL>*{IR~-@oM(r6O8U(5YdBMJ>No;_`9XWp0NsU2s3dz z2pV14`?I$d(TlHo*B@zaq$_ewMg}f#j|Tsn4R+}X&5FMIWw05$ii9l;GJm9&o_-!E zWta8uaiIA_gs<6$Ox@O>iAj=qvtap00(0PP5`5xC4!b~sx~@Wj%v^q%J!W1XdJmrb z!~_^b#Oaa5a#E1fi0X|qArkJ}{MK0!d;t$D^y#IUCaODmk5hX14*`*JG?0D998c2~ zk4<8}V~!X2V(%bU248oQQRP|Ksu@+@?SZ9Y%y3cBv-Ap$Sok_&4Y1+7rezJ%=x0P3 zbvBEex6rUzohT)(wpZg_<7JXs~5?%Q`s|vK~LnwOojI%BCwOtzd`S za*AL7NFqV9!vP+VzshWMT3@aIJUL?r$FBm%()Cl3?N}P-5$Ih8F}$b9Mmw5Xmul3A zct=!Q8|7pco6Ww4MJW?pQbU?Jz_ld6eA%2(*?8HoL?s%cBRXfA`O=o4Rt2-((jpr{ zz4)F?`2_@Z8>~gIt(Pk`x7v=OYMiTQzf-W@Et_g0QP)Ck@|q?5A}Tmg=V6=CGcaaN zizN2Ox2iUM2MkPw!-u2rb*@= zx&Y@yvYEf}eWoMcnkktSzZxe@fwO?UsR`3k^$)~3fvH)NsvMYT9$!|~aO|m_r&q@8 zODBB1!_4WOE$!Exrgw5*Iv{ou{$w;)|9Q_$jujRlI`(TXUS1%nuhixot1*eu?aYtW zk)C1Un9N=Vht+oFBd8$$3=c03lsKz)>yl;!rTO?4nU4(xEWyX2{+AJqNwS@pd+-;U zB=onjqn8*BQDGz?RI9tot-DC#5z^%wH@#GW&(~KaA=*^qG};#Jwp9LxmCkcjNuO}l z0dJe8^8L}QC=V7?QkKPAAAB=VW!+ZG1F1(A>nEF9m!_YDy!Y#)jF?}@&Xp6n2YF<2 z6130gKR;pYIT+D0FEdup;A4Xs#vMpH*{nqqwn=W8CXgKy=;g0 zy;K8<1F@65IPl^)0P{OnrOJly)@N(x8PvM;-Q85(`rHh^fYZ*Dc{y`_Ztju9CbSJ( zwdNy(IWpDJMY5b@RYcCKZ^|00 z2ejBapovg-=`W4_ZeRNjOGhPfEg!FzC;Pl6@;LR;6=rb$u4#PacFj?OFuoq?U3R2O zb|*jT!EeIIqEDYZ)*x!E(!-TlH49TlNv2hBa-s(A zxv~90#%-2?j9oT{h?~37g@|}1&ejOZhakQipzzPVM!QO^yAFXM#&~C*0#x+d5jlK) zRNyvz9q!?|!(|Nfkt1#1LkjTq%qZ!9bLnEkkfy`ekHCL3^*t9Hd|nP+W2fa&b%+7T zQ1$vat(;)3L=qysf*wlM!+}2U(Lw;No2Bck!4*(LW(65Q=D{m>^fJcWw@d(8m5bzA zy{EypZF)xB$`jB1*0YW1s~Ek`Kf)1@9!D-1(M3m+caP7zjJ({EIpXmTAsZgVPW24A zqZITpR0_FdCDZpf&{qwf=o{oFd%nR{v#@-y6VXXY@aUloBYEUt*g5(SC-CX(mys@+ zDt*!-KZq*Dh9+%saOp8JM@r70;qF7R!%E8gRX$g{#tSuW)-PLnj%AEL zg1m=)o)9EI>tMiHnoIq(XmXMyXJMv-RM3ZAOLVAotnXy%r8IPoH55Cf0Ia>#!7(m< z=X~RCkvEy(nyq?Tppbsmr!L)fF5{)-T6&}g@ zW?`P2SG)Cl*V1#0c~&*x{cM=an@?U2#H#|``pDct8uq#N^dkVLv7l|>1G*0@P?dbq z=}g)gNOt*C|9p$KYF^?ds)W7dgPm#OOnkiPr}sZ8)Jmbojfb4}nFn z5nPv^2`b-n`j8-3ojm*2#;0a#7!IX-m4V+5sFB2HY*l%{K8{63WC3b~u+0dz(|*Mk z5ob2is?fbAERuy`RWRKak2Aq^*Qd}mixi@3;e5YbAzJ^up~0dM4OagkG+-?4uA%0X z*YPwaH)>4BvzZW2Y)D7?(}99~bL?r;@!~UDBode8UKGKX=U*2ZXzJ3@5Bzx={h^P&2&xhT9q&({t8di+P|E>Zc?-qn8#d++1Ur{lRqHA zHfQnG_#vmACHgz{%y2;4)X>okMcb6qhme;F^r7{^S=OAFm*}5Ern;1gm~}~$6f_}8 zxrm@{!L{~I%00^C`_&hL)7!g-4k(avU#cR#qf75==;Wo`W3i(u3c888G#oqEZ~b3` z!@kiDYV^5EM<~yzPZ?aDbQ4p<_xrqi$#b|04$ab<1fQJ+Z9G{T+Ju{?(QIZ*ar9!G zox*dgVoc*kstsO2jyIhS+8RB6M?=D;C@NP|Jie~2In@E0= zYj5EI_5H)#Bp)fB!pEGkNgxDPict#$!fQ&O_4TJ&reJf{D*00MwYARQEM= z1^q6;JwDq<3}2%0cxx+mU{0LV1$N{7VD(!bwA-oIV_ivZ*GrNJy6pnJF82Gz9I0`8 zTY>l!*>@c#9Of78oY7=$19NTUh;E@wzPk&TR{~mc;f8nb%FtRh^%U_s_GhChIfg zT{KqBV9e<(Bj>vD;;3#EQF-C%k3)AEdkW%r0`Ttz(P0HTYT zvi3VlQ?rPt$aEBKY?eu-S&`#Xqv7kQng5_67Wd|t+~VpE<(lBup$1$QcQkenN_8+S zgJcR8cZddgi7V+AcXhDe&Ter{iL9p0;5aJ~f5NY6-NjTI>eBAWVu1JCi%dMQmn}7n zjk1?A*VziU=D}qvq>jZ=I5}}kkK@&s=8;TqNtWww#-AdIQjObqlYG4QU2siQg5(F-{P z;c-fAH1xhC+BhCX_5v@vcKm~!`(F-jKE%Luyxr(=%<{+~fNz3JCj^K#b*CFF{=l%; zblEH6usw>~g?zQ&O4PS5s*8^nqmTR*+F z@z9d^@%fe}7PZ9H`RTs31AS}RT|%_M+79YK#tBZgkneAl zpNccfp@s$jdmR!?{+M~5!-oQUYFl|zqFHsa<|gx&gjd9;>Jb+Jw26xkXcDI02dhtA z=-AR4AxU~@>0%@we4vPZ$-kWZ5|Z6{MP#==_;9cx>d7 z224PDk!l(ZvrioX{=-+t)2D?{n5<;Ta7M#~H?SIfOge=dl$tpW8uTs3eup6m>c1T_ z$Zo~qP(mfhbw;c0Oe=EdL*k<+`tiu)3nQV%!s(|x%_i5p(RsdD>rKs*>l+gD!I4SIj=<)>2%FBubROEvocuSSNd8+HoOPgszYc_5_rWV*lJ13& zz!XEkC$7Od2ja^@bH1@SJ6iwK*_utwj9`1$P%YEuu)H!9)fOKJ8_VCl@Z>lSifX2# z)D6nz4EnEZW|hG$my<`VNREKl5Jw<6KUj99h$@`7(r$~<3MX?DC$g%tQ{M_!HB4yG z6dNLm#rzUn+`-XyOQa~J$5F*vT`gT{oBtXa?#7x;M*CoL)Tp%darqALmD?Bvxf-&XFtBs>)U5)-2H*|76E;q0^u1MfR}H?RcU!F>M;nkf`^%WlLB z)}CLvc{99Mpnq)gw1H%1sQ0ekm(nWW3rQXucgtJo6OQv7f#L8eZ%Z$(Qr^+jzL9|@6ubPeD z=hJ3LUHHJI-R#ruRocrgZ4$s3h2l^9b5}4;MYezS3en&MnUb{lvGMN6cqZdxnZ$ri zclga52Vz>72sRdivqut>{6=gE^d#G9p|`@LY`BFS_|8uRQ)n&KByO>J4Xz1W37=9o z3yC~7Fe;6DOGXuxgAbH94iK=dGr$I7+V_ekFURL>;=pKQ%K(cs7+ih4Y{KI^Q#II( zYf54tneOZC%hWZM154xENL9)aNxVFltK|UjrxI!JJ%YMlAE19P}hAa+r z9bfTnfbu~;Uhl3c(SLf!yJn>RtK>h6w5`u+6xOFieSJ2#&zdp(s2Q&R2I{|Z0(v)- zrGHf|H6@yKZSr*RE z8i7>48CXav{RSGT?4KhQS>`q%l^^!8Qjm_K1}_hu?g6d57}Cme%Z)$nX<+s|XytjA zhA3=KK8O5^v?pEKO+M`mMDXBFm)79ZUOtnw>ntr|+{&kqmA&^epRNc$O1H&02#$TO zYK`!@I??vEj!=4~Pw$;i-LNrx17^Xg5dCw(Sw2ZNbV+OcK*)G^8_`r z5su(kXGd*#UV;MQwJC3Yp&jpZY}mY8ZVI2daj*LwQ=b`fQUWP5^ zp>+I;{EmL;7eMuuJ=@>W@4Hhw`a(wA`f+YY|1gdyQnRSFvVTjZ?dZ|e@XpcHm~PNw zemlSYhc)?Ae>sNFE&;~KO+0KplT~l^hyLfhEa^rx9jnT3=mVLLpzII-*J(R{Ke!wl zY?f!_YNkpP?iB0PaCB+IbSZJo`re_`6Pe%^NjUAHqMLyxM@lOIO&ek7v0pjRNZ2{> zy*y}k{v6Oexkvk;x$cfo*!gf}4m7`5*lGA49I(Y6Ga~03(K=gd*DNV!RWnP<`QQ0P z48w@+K~$Fq(f4?uZXocCz`m8$@(4l+UKwA?bQPyY7-6An3|P1!)c?91EPEW2s7ab@ zf(Joi_Ge{{2mu*81)BJ5AJK#dV63LdyLU}+8K^@wXhNtP9aERC9Ob1M?XBmIlEha( z#w9f?=|iMugsM6BBA&>@2-bD+XD)6&CrNF^%9f!2w9q7S`A42)vb$>n-I!QnH=zd2Ftfk6<>79gsgmr+ z&h8(57P<7J<)cGGqe{?g(`EhHSs;GvK#r8D(@_uKlxa5L z053a4yxt|ER|Ge`A;ansgB7KJU3MSgMphmj#ig#}*h6Q!o^C+=NJ2J*=%kwI%}h$D zneOw~3$*R91OO+H=C>_+`FcjjtzZ{U2d&aiz&& za=gk{QqUF;{MRr;1m}b$Tm-+u9*@pvnPpXY!iC#NVi!o&+JF`88WxI`7uJoqqg})47B%p)OP-~BS871-Jmed=%QpbBMl_3Qu5@xf&Fdtz z(em@aYal=QjKu<032coY5Vev*nP019k@bcv*TUsshetF>v*0{xLoA7Ouk6O(NdCAuT{)b;f&7(6TPnwB7A@LTLAFt#$Vm1ly>W`yWi;9$ zJgZ5ZPGg*TE}@)kL-PjfOS)`J`t;p>NJ_4|a+*itk-0b8nNK)IbRZp+)4>3qGC^+` zKNvQ+O3kE>tIT|)F9mn~oLO(vhi!G1pj7sLyp35e3p3joQaGb~p~vp&4;W5x@!R<+ z)GxPzH!d7iVA1=TEAW{w*R*C|{Fy=vDa7ky%b1V;0izGzbw?H1>!a!8*m!s)yb1tY zk(S04R!mSBX6jl_QA14QlGF#m|ArY!PTo!IF?B_u#`Qt)oMkGi=}%tX--khYyK%gq z9WwZ{riG899lX-da1XaLcT!Vn2Z?aR@QgCPGerL{SIW7atqv|g7?azX9b;XtX8vRa zX|S`;or2OV{%pH04a4XCUa=c{hazg0IaeBGJbAV|2-;F3p8zm0e$Dk#&N@_rRfo zXTW2d=;8BdH=Ktei4U-K>9F7E>@|M45!Lw-Rh&%Ef7{96CdVY(H<;Yf8qp+|UhaC) zxohiTkUZz_!^0zo#2;l#+W_CUH7wQb^{!2)3B3!LVD-5_jqmONHnTYm{|q+l^tq{6 zN4+hf>*LHf*iz2fAl?c0b&cO?_cZ_ikiqqZ6L8BD26xR27+jC={eqlR`Hx`q2K;Sw zc_Dv0mSuv!!5u>WR*xY~{B2)jX=l$gUaIS$b*X=O{&weE@V81D_Y9{exyEoR;Wj0n zr-_4=x0H0J7<^1?j|?|zz()93bI8YzpzWXHW0(+(k2O0!HWEHI>i@yV4!b|c$Buuv z4IkV2gr8p5;^T>%|5?n(n%?w$tnd!-&#nLJU%rGeMZvX#$j3xLW*d7{S zg(qLt9v@qekDrUq{F8j_Tn3dp|GHCaoZe+wU&KOuE{qX<{UFSxw6EdXJ|P3bakJo9 zUbi6L>tiLjL}Tn{8ghB|72m`|YGSSxh|du(vyEMsIrqAetn6mtP9E#;v3zxI7r(}} z`>w<5;5*Ke#MX?7j6n2pOyoG<(>ysnpXjO}_2-z#Cd?=p;?i}HnqzB|7i#g*rIJ;h zbDTuM`mNna;sqe$+rH*F+W2k$2 z)xBGFF%7a@@xg9Br}%o?s?UV0H>{w~161z<)!W4C@dSbKUpS+VdjGqPHa43TQ3!3% zh$QxCmp^`cZ;p<>kqxAfKlV4P^Upb`Wc2pM&(KF{*7)NduRH#DTf6)*TAzHWj)&pw zVtX>a)CIQ@tAex1!7eyTgXJ12FDli@0X247q0-s4-?452Y!h7JL`SkxpT>??(TNJQ z5BIX$eqC$*T;Vzww7Hz*?C04li<)jZrl{#UQ|ayC)Hk?W*r4%G#l%T+n}jo{ff|4I z{-WMr;P^<8-dOKM+F2UVKsIbP?`Dz} z8ek`WVy|Zl!aKpDG-r3yk6c7Xe#FbEFleEU-di#MaK)PML}dJkeU7@A1^uMhkj~N# z-Cy;`(cv|mx^+qc3oNk9j2vxyQFr7)s^ zJFn!%{iW>y>HEgZ_b-Y4#*B(~)VYjp6Bx`I3JkvOX%qjd)=hi>gJ&v_ktH~6XLE5O z5+aEcsi%;mO_TUyVF)ru@J+Y&8!s@$1M0F^AGA_ zE4&?jWHys7PMlYzuinPe_prWOf*sjGL4^Cs5+ZctCzh075G&&*T>5`{@YR3kW|&r2 z2gXeY^Cfcx|7{?Dj^ZhC-X}13nAehV{I2t~ZsGQjPM;4}PO~{IzM4II56!`a0XT!r zlku-Q*o}WUKXvrZA-9}M@F-ENxnEbMYDxyZ?H-4;sQ5Am=Rz{N2Lkq)Sx#bhfO{Zd zCkw_H-*M&uC2l=a%bDF`<|uuX&8*~utG{LJW{B|eAA@Uxu=OAe2hCyxj-@NqRJrj7 z$91DrMEms@_=S$Vo_^AcAyd8Mwqow^=l`_F9j+iXOZDhxi3IMEEC5HLGA$*2^ zN^kq8a%b>&4u2!~E6i70g@4EKTYqg?&!}WQ0pAGuLSU+-qH&5MrQ+KbNyKE#C3sC< z0w>@AkVB@S6?fa2-yj_Jv8$I7vNpSO-k(V_hIn;kPN-$%d3ih{*PheY+n=4U{-Fucz%1~dIGM7_fJgB$bbzR4QQ z`E!^3TOwcB*iEjn-wtCg+ZOYeus!6jBLDDe-=4dVzw3jhC?WA71+I}Hs6QqVDN6sM zX~~_Hn$=-U(kx+Ou3PBf)B2xGx{ ze|(|wJeisw?Bk|gZVlj|#EPHq0(hRKg^#8l6Jo*gBZ*7FDTORcEa0j{@KtxMc%$?AD41v{^Jc zrq`h6L5s7U9x26X4(mTA(%5hbA@QS3P>2oV&Ev%4IPam>%;X&Iz7nLN_>Num!NJ2! z?#IFTd;Nq*R)gRQdi|LU*(Q`mG|W+eWGP^cxUM~QOCts219)M#iYxh%vXO+ z+O7jhgR1v%Jr>Qa)KzV|FRvNjp074i)s}bN5G2oATBK=szS_C@YJF7gpRU?og&?`n zrES-CiBnI*I(E&srFc4iIm$;ks{Lq43h)by^VI!-dxnQPAHINb9^uP9ctm%HfQZ{C={)k+rMjV zZL3mjYPFoKS+v>YREmeL&#M&sK3k|#WF7)r-=8AT<3R%>ez_?ob&8f2Cs?l{vyn>J(r1X-Ag3#-kAIG2!u*#%RY^|KiKW zA}#`$Z^_25dmzjW?DRf$^7hz-vj>(gDK@;Z`Z?FA^9VAX<*oQ5a2Gzpdkdf3Sueie z)R^}zI$W`+TdrP4llsRRj9h@_HpzRE1VFvA;1H@dX{D-Ku3lk9iw5c31vSXuaj}>| zPCgC%DsrDxX0>IGCmPew4C`OSohCrH3m~0~hM3debNF@Q3B2;x&7ufW{@Y-|Y-C%a zg{CWJ8o^_q>HK}GyAX{euh)X^cAOySOa^jc8MjHZzb5?#pZ@u#N{=5w(j=esE=g5} z6lCl*FT(tTS?o=opzY>r>)c0?Hs$h=oNwaTn?cWepk{1(QoT2O#zU32Y?)OzS-9*; zgwP)2-i^sk?4&BDHtCb8bj1UlyrXF9(f7JSoP1uwW^I(!0?>&~k*9Es;h{;`(Nxr< zDbXbFtRGp`*xH%ke2}^!Ub^e;Rbm~;N-N#D*KW0-5wH31;1=6eL5@isndx+my~gyD zmYQp!1*9b8D*qh1}{xQPMx zKiOqP_Bwahd1VlD*-vF3;!tZ%Wo}g967QEVV>jL}b2;Jlfzyss9tHqSk7sHThNsqv zclh4`;^}I<*oPhCq4vsV+Q8!10<&ox&tOXq)iCFOz1Py4O~L%dL7F&f=N~&KF6ok zC~dS$yD!T z(6M=KvmbM~&s7~{5OH_iPAen*&n}&yJ=UZ{#drP%vq;u(PI1}5L2nClBTZY=t|8p3 zoi)eNQ)?Ta?Zvep*izqGUMR-^SW7_Yx@0*rdk*XFrLc`AlIWP zr!5%Ovz^HMzN01@N&H9pE+fNPXohH+V@I7jT&WxD;4#;%7B)_pS=#t^E6+i6xNgSa>U3R03#H({{X0aiJrr!-Sj8)S#ooT61vSfTK@E0> z#f;8C(#)4UNIo>E=r}xkayx^cXXo1(w3;z!F?udd%`cxHd2)W~0<-m>2;ZtrV^6}s z;UGGgJL@@!o+xl>eYvrrk`AJQ*7Dg7h8{Z85NQ9j zVbvk3dWo+(J74vTeAV)2DRS(FRZCU%KwtH$eAWK>s*i3?k!@U66adK7A?0%&NdKW! z6TuJO&GXZ;+&ssijtB2rHuJaiT-B7S&i8q<$jj|dzMb1liZ_~=DJ;9$%Ek)eb)?#u z;M%ZA>%Qvm^HoPr2VST8ZZ;LQW;UosS3yHdMg01oZ;osgO2MJX=<@wV+KSI zy0|>jG>c#AolaIT5YVM#Cy1%w;&|{ZLT8Bhc?0HW7JtGnenL4fqo>H0aZs5Cf!iK- zd0B=Q&z9r`rfZBDUBU8@8f*hJAeSxx8UsrlDZj=T3Lv$-guHEh9=_8^cJ5B&u9*r0 z##Lg;%dZF4t6LRzJ2p7y1HRmva^bTqvJxe)Q+o?tNz%m3)(44?VDF3kUHr>2Ct zve0%mdI3eg2zFz?H{@Jp3CMMJB@AmNxDu=-mr=6~d90NevW+{Ec-Gd-7IgBj=(LZG zJ(76P(sR*zURck(N`2m+$@6n_ds0-=8`l**P~ul)zEX7u`~~ZBz&~bRdU)zbL5JvA zmY(Af=c$K(_`K`MD+E5I8AdJo;T!FLEtv}_5~BDOD&A5vol-?q zOLEeQ`4iwHhbe(GSf!e|GIv}M&alt}@}edimr>f*P}&*)7Nuo7hn=TRnw(scs!w!T zqzGS55cr<;(uTObm<#UexaW3`rfhxq+AH$_iZ2t%JjrKO<5lGf3fmmtl&}3uV}H|u7smAj~eOXu~deF^K;WLiq#7IQY?=vmr)1n`j%wwQ-1|Uct=6YY*ox2RZiie1%BF-RJl^yP>`uQ zMTZ!B|IyYr=Pug!afg%^hE}kCT{SkPOd`dcu^|Wib|j@UiiI47;ZeJP7)5xm6si> zJ|lP-y;J^;UTaiUs%2P9&(ArFp{$`*j3`FN$D8G5bdu|yC`LIRzUf-c-=(`KMn1M) z@*nD*gQ9R#Q)DxlazL^POB@QkW(#L1@a=AGXuN}&!P=*BO}Y3Q>Yfe7D6svbLV-ce z)tirgu`W`*BJ%}OO8$JZm>ws6@*TsfOQr`S=2s_cP)eWBDQYy;!2mgp+lcOHoA<^z z?YwT4&~=X8XHC{m#op#`{*P5byOXdc*!^2$Vx_j{{&ywNu%&CMndU#I=+in| zf;}eGYvad9lea|$JI~+{KP6B26J>W(IxQG9;O3%GSy$i_>6uQ-ZnsIzD$G0|VoynY_b;tzQQF z*SMTMi3IZOgZ*iT#FCt4rC6N18q%FZaYMgqN=SrHd}a)4-}T@r9pD&m3zCKQuEuZKkfpzC{FD)poA2!wwQh@`Dx7-W%Y z-pV~!m#2>${Er4ICUWKAANvmY*L~eC{L=y=OE5h+WRS^|bZbd^E>|Img2#qw0T|R# z3@Xxew#s88YXjr>z|4I}XZi8?wf{~Sy5uYCn^;aBM-HZ_3EYrcs2z;?nv(arlCL{? zH#qlA7&n2s42|NA6lk&n3e+{DRAHpFWCZ{1kB;xrm>XGT>~T%MVW1wBS?h89Vl_jo zZmDU|uMp+A3)P#}?E>(Y>ZY%C6G{A=LVheMPKt}-zxcUaD!t0vu=em8^4(w2##>+7 zti!qdeVOKx*LRsz!-VFN7_4h~@Q{^_-|wgTr$18v7MIrG(^jq_?P^Qo84NfC<7$VJ zV*5amXE0k|ZLF#t>#8*=$hS}bSO+Et`sV&-)BHC0_ubAG45!nJjMJW=%zmHSYf}D2 zMg}{ZMp}a{&Xu|OJ+mB_o$-JW7sGA*=U-&(INw!FSDg0)LwlUyKuyguT&Mf|WfB;c zh%-08a2ef`@&n#{wB~Q62?jtJUIf7~lbETmexIuKUW#~!{_09crKUW)kQAkRx(!Pq zDdt>IOu1zx=p-B7QFLoRJIgs)=q+^V_#Dx4%NJsl{Qob_KNIlFkM?$1Y?c8!v27Oz z`rBZ{UHQwK)sF+)H&zR_H7v0`cNvB=2_8OMFj|1v-azZ6*7`4l?mhED=swYh4o>zX zkQQ5;x<>5U>x57MY3%HM${P^)_R>U>O~YAQ&dQLIUf7%J+xqJ7P|{l^G9B}ioS{pN zUw!5#*+VV3CE1Raf!thyEi~`doiFLK>jYIm~pF*Yc- zDaFPxWi_1XrXtDP9B|$PIA`uasdX%#haw1^?z3#7_~VCow0MJ!K;Yt35ugh_zq&Vy z(*X{pf?HE+(+02IHuFGnz146?7_iklkulY!(&|3=hH9Ax$~I1zQOfxsuj_O$<7C5H zTt{EBjWI8?A#=%NqM<=k#6+8}*i-YH#$HU{9OY@H`)^HB+9!E(8LS5y2Ncmmlu<%x zIVU<6Z{_Sy3JBxW%X}|QywnkIFcZ`f)3r!`fl|hks}4D`KiBveX{HuC=|)+!1P{MP zu1Y6W=_z}zm_DJeiuQBAx5T&&CE4S1PluHgpu^!{6g!Qg4@jb#KgIcw$sQj`D5{>- zi5>3iT(dm@*}>J>yTC5=@~5=b$))#nZ4LTwZ~{l`VdonN%pU}Ij}=r*T6hkuDUudm zF`lz=X<A1&^N<7Hy+%Lux>mgX*iuar)HN(0fT%9Dvx#dDL10xWG)6@Y=TYUzb+3?8o9fg*(}4GnLm8oy z;Xu>2Z0*`LBMJGE>Hsg@XXF(UmY&cKA4!~oSx63^>@G56ot$;d=~3DOn@mhZnPKWi z80StfkWQ!g1$$8Z8umjbINcG(=JHF5`6ScrPonm=Xd-!q<*u+=fqFPcfzV)b(`#%1IpqZUKzYW7y z1}C`1L7gx`qi89fq1B)tH0WaNv$cu|TQZpWepyzsWn<^JnVG?8xEEGRINQ zbE1K4*0-}ZBAk#R*@cQN%$Ko}!FWi`{-S^xy>u~r@}D#_=xwnJk$S4;+DKwP#T~VW zM{j@ImQ~(6V;rLJ=xs#?YVYaNx64uc*~U9e^rO9)f<6~%$R7lcT`HV1f`6@78-icK zrutLrK{3I1VGhQ&y*+>Joq{;sHh$L?R|_G_cir&u=1Wu|6&)nzZV6?=^?}=0Xy_Iu z_$i8jlSBHuC&yX6XRR$M6p6_a#<`zT#XT9`9ETR`l3%grr}9Jw&yQD`MU)4~xkVmL zZnG%!2cj=pm(aVoe8%N<<-2u`9vFSoa91#`5ZA%+oUDBxf zr}({lucc7mL7E=p9_pk|r}kUaYnlNe3(%we5britUCDV}{6 zhlx)c)QaJ|wIzDccd_5qr8=#xnl$(ocIkMw_K@M-;vdx>GNxDjy+S|)pJ&!@WOysF zpVTI8cyC{o!~RH9PZlQAoBvqI;Lis$5Mgc?5?E8VDlF-YJ9&6FKBp-Z8FS)xOmn<1 z#<2Y66|7Z`GN+@qzlKSr`gE)ARBy_zwn%lh6X63%qym%7$j6D%A;MP<4kPzLWl$8344k5w?1 z^%%k6^V?d}?yOJdBmS#uxn>0e1S|$R+pAgZV00Lm`yRX`iex)`?88ZQRgh08s70Di zptsD4>Jp%3_cc`WM%I%S!9Vu6-LhD*TI|wQBUMacjt!l5pxy>EnbeP&Y^|C6h?r(a zFW)iHf}3|;`0ZYbKYCwHXtNXHoIUNQ_i11+$*tij+MxtKfEk$eY#7$C)-k+ zI%5P(mm|PMu}Y2_#*bknJy3XZ@rT2RSjF$%693k9y+T4TBYBN|&PK6X$4TGrA@--B z&Y#xVk7xS}_ro4D*#dg{QIapR1N^ zKa3>$i;tztUizC6R8M}n^~>Q0PJ+o-iJpo?7~6)MgJ>Q{pmQ{Z3I z#-C}!q+OY|Kp0Uz4#Xz(@MY8LgtC}0vcW`e7S4ZmCvq$zfNq6k=)#-7iRj+(D zOTz7J{a3yFx~kHagPy659KM!r4yK#h)S=Z}z?;9aqxBW*Q=8nRCOHvt?3m6bgPq(t ziU^k6VbP%G?EY;C;*kg2p^{rk^r6;`N(#Iu8<>aDiyT4QxEXIEPs`I6q&Br{d)tm3 z!T`I7t)IEY%a)62%or?@MsvB=H9%mD@T(O&y0uD<*~%G*ktU-)bA z;6bf(^&;t@|H`X-aqusQIyYF??5zwV^mlA`eyJlXt$%q{{Icx1u4$ z`sD1(?e?D>i!7w|_3-`q@;umJ<&$G8-M4Fz3_juSY5#W)pW&a<+x~401rFiwFZ=i-`;R_~)JqJ~KQd9BI(uOx@em0N>7G9@i0grF8)=d z8_u#R;D^(7>Dk3r_#T&a-6-R?Q}g0yVuRnQC39^-q+_9BR!eU*#;@y6GfIhgAHL)y zmn|feC2OiibdDtcz%K$+HvJZCB)I=ft{*Ouqkpi=o3{Lf^q7cW!IMiEdC=XZb)nGA z%s^_^r*_l#+SHcX+bTFI`QMM4Nd45H>7*{v=$T_fPCof;>~aC+!?nhXB%y6M&4g291J zpct!VObwngWJJM9cO7F`Tp-Wp3(Uo-lPkqX4a|)mysk$0>%^2k?vKw23xt z3-Y?DaRBeHuT7zVpW26X8k_{C;Im-6&h-@YN>N8m?I7|*?w2Y(2npy1MoH!rr=edXP5tTy|96<`m?5~3q_Nf zsiz?}oy<@&mt(-v+a!@DQ`3qWh^NLYzu+80PcOe{1n`0 z7Xtm(({n(N?F_)Lz1|~uyl>_e4Ca8mOKdaYRitT8MrnYbX_jli zri{xe^3CsllvLXHCS>p0I>{AXds3-cAf z;)%oHmV8Bya2x_$Z56p694`Jr`AW~K(%J1)I?h!h1eRaFPWei=sM0_y5J`MuZW;MI zuDyKvQqt!|87p*g<+Y*<<0rqym=@|ocAbx_f(nEQ;#XEu8k`?BK%VvKiVM|Em%rFa zZzR#sNsI`?Q`?xu3xg+)P)Hi~Pu(d$oktV(@&)HQ^;GeMvYEPdgrWk$C9h}|P?n>5rTtpT~3mp@5QQn zL_2l&Rozd0U3WFfo-MvJU$;^b z>h*1>-e#(&*J_aOD}B9*`TmBh-pZGaBP7nT^gJcKO!};^+;|hUWL71&l<2OzWC@W< zUS8k5NM46_Cj6F$)7Eh8O=K{xt*;Dz~l5(=+E`JdiDba zeO^ePs0f244wnT3iG9K^bbI|ezUD{emCLe{D5K-L)M1{BeQO8nT=%X-zakN!!`J5+%Y4G2cQ)% zvmW~*c}?UAD&pw{GAkQeNoHYD8WQGY%#cpPtQr&Q2ok%RqHY+#cnMEbjllhZ^0($voxXmaaehQO-UZEa1yr=YH`M_c$JS@6U9NcBn`qGFcqEDvs6;dnUc z^qEqR)D60Dq$#Cp>B-CNgUBsT8qPnh-Ro7E%fK2bt0+!RVRD)WEIF171gQ&3S3Gaz zRu~9!uNO!^)1~j3hfc5}2;t!0(RS*RNuXWn%4FN)rBuYnw4>R)50WqkX*N5R;=*)c z4mV}i+X!ynrb!lw?xrQ(oNwy@wYA|rlIg+4`7#~3tXCw_-)^R`c3ozeup)UDT83MW z)5zW3m)#Iv{@vKGB)dZ!#JprG=LzYuzmqqJ2T|I468lhSIka;`66K%i@FKJf3s_w_~NboMn>Ul}x6rjIWMeQ<8G08BF%0dwaPyM%zFjN%u? z7u(mDpmEaI)6!x`oBg2Mc7qI>9!kn^OyYqycFY}#Dy}bk9BIZNsX@6#fUY*p7~aZ6>GOS-hGzztzE0#3tH7~ljV5KT8jO_ zk8PZ;IefG*99txD1LO8B-Kdrplc&AwGNtFo$aWlFYTRv(KxCnM?JBLnQy6{IM{|GugBcleL|4c zpr1?MBR|C8A0KF3scCeBwMIq_PrmL@Ob#SFw?)!b!F`5WhNO?d2D3YDSmf$_k+02y zSzx?*>?e&kZLbr(!@UPseGZRboM3ut@m(^%WRb*Uh6#qpRhZ#}y%)Hp`Mag(;PK7^ z^0xAMSCLl$leb%7`^S>j=6uo4D;ht6de||O^-Jvd`Rm%`k5S&BIXS)-(U&(XJrX-*$~)hGW`pCrWOOLo68j=vxCpD}*qw7rA zZDEjS^PbM=8Ij3Nv;e7+^6^}}d{FKaU1b@JT^3Epu~mN@Z1c@`Kxlr8hvBaN^&;=IJnZqv2pmJK`(NUg$gv&I@bf=exbD z9NWbn8{_bJhlWE-^ghi~S~l0jw-tb%=fARBRjsKR)Inck_(?S`qJ)zUt+2S0tMD8Z z7&eCijSI)+7iWP3<>XUwI4j`&9O$!`1dkl=Q9WI8mw3)AY+*VDgDH=|@knP0bA6Im zqAKOi4YNI~f9OJqq#C+%vr0lbd8&sh)%AGAd18trju+mi%dWUw1eK}gS9|^zy+D&% zj?I$<+`;qU^yg;wp<03OF(>2$?@-{^#*HMn)&y4I>W z!Y^p0CmB2Xj7Bm*2>CEnn-4<)QY(tlkL*io;4~{2f19LFd{Wffq)D}j+Zd6M*95Pg zibR!s&V?xI`Rg3=(!TJwC`6H7b-naA$Rkbpc)BN^(0;Hko$|RzT}<(0c5Vc+m{lhSEB+M+jW}qv31C(8p8OQYjj3w~TIwQ%25s1?Q=4`N;t18=-h+ zi5Eoo;d>Y2A^7JcPa>8rOe)V z4Abn1;7=Ris0~kP6P_!&3>kzRrGLGzuukuxJv`Qr8WkvZMs#M$uA$3R25z~inuzO> zC%Z=**OiYw-{0?zM($d2>6Xz}qQF?jWvjEpaWRS?9XbgRc3dRHX{w$J%mhb+SDE*P z1RGhG94Nm?p&9?1lIcs95CNdFVSSgeXX0%DK(ESf@B5EDd0Z*ISEo;1Qk~?@x$if- z^suUVDl)0{#TQ@P;;QA5C;wa8njSK*>M5>(HB{~XYV|?yRj02!cu95S*!9_YW?smj zcWWB0^Bb69Un**Lmwcl6bgOA<9k+Q^(NF*_eA643AgaWo{ru&TI(pt8+Mx4{Xa{|X;%9~)Q`;F}2 zAj25e8>+I`CBGh>M}`#>L553bwTldO1BJg`?}-~GfF7;gWd=A7cJbu5VxsQ_+qq`X zw>V~%Rg->Xu9d8%s^hxCQ`3XR*NI)5cx;FBa zqSuXJmq{+_vV{9jlh$fcDkC?wRCaEOG~uCL(zJNuX$L*;E*luS$=Foq9+HnFWNbn| z?8cUm?Nx+d{yt~!K9IXVE1!^Vppft@b0?K)GPb{OF19C4J@pk$@oKBpk8S64!OvmS zD6H#)r8BMWifl)Bp=kw8pv~B83M;s$m>8z5wq^xU4_(AYyQ|n^2)mt>xWR?ikFx76 zmDpI3?i+64AHsdIXSLr;-}Zay*lsVo|5PvkD-aV#;ew2qup-nQM_l4%?h&WCNPPwH zE!q#@oT!zGA48bFsw9tld;|T%nETSgJXw0(K6x2**;29{zd2I!*4$A8s@W{7ljHgu zrdJ~TwzF?`U8a3={BM4i`eIkxK6cy1en%iy+HM=GRIpzTRkaPHE#~=EZoc>%HhwbF z-Z6PS`BiQ52PC>>%m~%+_yKsuECUIysxl;4R)_=&Vqd)ae+JQtuHnkQubV8h2X7W;PyU%VV%|;Q)40#i6q-pP z2KfF(UOzH1x^oWqiuRA(dU*Y8uv2TArI-;{@JoUAjo_T|Yq`@YSA~+Qr!m$@_lP8> z@x`lWh4Ms~Jwhq=3`W3?A%~b&?gXVP603GFA@udfMg8&Lll1CaPA*+QQZBk?EgAEC z#{HIotln%NBgmms^rcopAA4s8C7BSR1Uw4&(>Q^Rf`e05gK!OCz}uFETVC zR_%kgy&13CoHtD-N^qYXY-s$h zeC#H*k-MI|oNw%mPJUl-0>hG_LLXFu2~&?@a^Iz+UZ~|IO&!8yx-uPg+5TBY4x2_P zKy}No!CK?YJu;M22Mz%BOrYCXulDO_H)(UCP_A}3sBPUka|W&C<4K*2uBdJ9Kw8Ut zmd7@CxCi6HGdD?y$u+T!>TDZy_SjVd8=4!H3-GTUe#aXxulZMkzZ~ojhJlx` zxh6em3jKXNLo~B8=uM82w^efT^hIu;tt{unY(Tc7qRb#KZ{K)D=hCq}T6`(e&d0&Z zhi&~o$b0|@QLcdgqS{E!yW#~W6`C^8yrsLYw2cp|UAp2e-LBr`-#Jr8dZ}1)OYqf` zF3Dh2@QOt*ty1==b{hS2zR`#peZ;pwCxd;Xw--0cPR9Mmc~6`E=AVz!5BGeOVk>^S zpRgfsUL1X%(9mfct}a*m>vn|o8}+tgCho7Ez4 z9@fQn2Ve8U|zTu-5Lj3=|E;H0dxGokVyUlx}!>g13N7k>Ce07<|tA}VSX-0(d{WzmL3&HTUT-1lZC0d4<$K11Ge-(Ai<_uRAJa+qv{Z2tE~ z`>!h)tq@>k>jKe-xXc{!G>^lp3G}I};~lq?_?3Eq05$6t@>u@0WCi#iZ?CH)ksx?m z@R-s^`sQfDbyujYn;94 zhl-~N{|&e3BrLu>JqdjcP0zsoGo_FZoeh600tE@M4%>N zvOc6bb^Pp?|3~b3dbW0a<1f-3|Nq9Go1_kZ14-#y{Pkhh^Sm$S(_>@xU4mFpX+N`s z2f=w0tLyl?dJHi?5v9O$k5#V%)FN@fVw&%k`yaA8u6p@NnAo&A5T+@un98 z>BawKA3n&mwu!|l+T!|3FYK0RVRh01+KB3ei z@?z?%nFqnlw#?{VkzAE}b~CJPd*}E>;cth*->&Y$-=h8%fdpou3`pGkFoP8lE6lVA zAF1&uThy8nYnIe-94FoC{G24g!ir!mO!|!A*Oz*XU?6%m9~(-Ij|nlTL_DLp$8Azz zvDCS77h8ig@-t~f4@;NGYfU&H-eQYGF}%#G8r-cgyxuk?MkqZ5N$2oa{v3uU`+gm7 z!C35Ks$fKezIP!E#$O)g`6l!H1jQLIW?_)Avo}f1LS3sMk%u3r_;njsFR%W?r2fTP z52`RmlF`O^Y2#M!-8>nk%zu1Z349#bxDg&Fv45)mH|1mTBn@Ba<6o)iAIE>Ed_;CU zLY)K6^WyYGU{fA`>?(8t)#zSGp&t>B4YTL5#oMGfdi=lYgMQY({V3z<%$oCv zdG%1@CG5d?FF<2Q(MvCUE)YFgx*Q*U{V8;?t9`)J-}a(NCx4s{XOh<)lg8txL?ERO zM@I@-;S3#X$P4=yieigx11fbYH&ENVr?Qn#0%<%pNjzfxTsOiM3SWfyVE7^-DixB` zqY4hTkhDdu<*{K!Rh%k}QcQC4sCCcqLWt$gl`FO3`I4G280jEEe37(j&q-VOq*$pI zn|+3fO{8E@~V;$%F!QUKTlV4Z5LgBe!7}vgi2~C zuyf!Ju^%^ya|6quO7@xW*+(kkp~8wtQ$IsXmx|r~-4DcfPu7YZD%7qJl)u9{cN?o? zugqavoNG?XA*HS1^OE0>TI^;njY7^wHAwa7GOZ3bxj^(&5X2kudPy(x@&jhbujuoXi_z={dH00Av*x)^@N(}3 zJM9D?6ZRiRRh{8Y~o708YW0yXe=OiK7 z{IN8%(aTICiD4x_#{|g+?F;tmoXFnaX{OMly^ znEpEMZ^?m3zb0n%OWM{G*PJKji3W9(8 z%TPw+iHWdL%A#&ItZYz@!REgn6)h7ArTb*oYy+td5^Pj|uB+%SyQ4&B(#ctpspQa1 z{d0={`-rc*$RDMqhbn)pnwXY9mi`?1W8CldA%CoLj_4!uha7c#(*;U{#n{0%Ig<`o z#b)-y4Gm35ZK)HPyc>mLyN*UgJ`q|XC0XIac$lG|JS%eRH~Cpr@td=)Z^p%QPlZ!& z48=#ilrMvg=k=u#Z^xAuBqx-#mgJg46xl1uc6mED#cSLa{(yuT3@GHFO(^R?#U6EB zA9+%jb<^whA73}^ohazsV|Cr6c+g)?*6zuqgeRE^sgZp*JWiryPGmB$Ez7@@Jnh?u zJ8w;!ohJb|5Apxk9fc!$PNc-|* zBxiFcAN+C0q(L{5nLXuv0bOFKu*v=Q%@FLR<9_`r;ef@OJpZ9|-uNX-ZdzAlgN>|FcL=+-Mt=-nc-}Eth7=3q(vun3cT?^+057&8@-fLI zSBE02`{4j$k9s8}8(nO!<6Xl7%-zP+y@JfCFLwDiycrXUH0SUs=L4;^Ry1UX3oGng zYLKH5*M^!uCapy-nqPf{i(OekK5;=6ExAp!ESwN7nj-R@dkiYGg9%EgUBy{dO;DSD z@q@&RI!jo-kDQ|}{EpQ-p%9b`|Afyd%(h!7i?se!OcYi2+5OTmTJu2{7&*%zsP6t0 zG+P8sUPqHDP@haPXj7|J0t3uMZz0LX-sWn>%CGd`O<#3;bRm^5WuM7hFP-&IkuOR@ zvD&uaGGrcPc__hZct&)Y_W0Ze@|8WsU8FUKvyF2ZS7~HcepXg~IJ(OQD;5Y3H|4ALv7++4JuAIQ_a`2LvUU=Y_#=w^44&Xl;E&&#Y z1ES2H2JRFc4qw#xUr3;{9?j$JDx~AXHZZ#T^H7hnd&S%$=EfLU;8<*T=JxyNxV?f~ zA?R#DHrXdR2;eE|;;5`Z{U==Lw5rrW2lDqCeYM{KQSm0MObE>!K?h6Cd+XN3g^VS{ ziFV>&omnh!r`U}t3J)!?vd&2i@j&Z;JT}4M4GQdDqtoNjlC z`cnZPjY#F1y+h=ro;Xqqle!kX_erFn2t-1&BlVj(fU|@q$o4Zm_pVTOTXIvQP@BXl zjfL`OP-95`3>YmI9qZV}fpT|9<52li(0G;n>DgGyAHh#3avlqMt8%@JJt|NyYt`)|9XGJ>;1#Gp66|ny z0@kjF*hWbA>1Uo)nkj0AWlBQGcZ78Fk>m7ZHbisKC`@7ObN5mHf}aD0mJc_JGtt}l z!+Z9@{#XY4iOba1_VKwnmMfJyg?vVwoAtgrMX{gnWw;-GOC#9U_-l|aKKy9yS&QQe zXA&PC=9L%*-{V%;CLb5W2Y`pB6IPiy*>^u3q8w{`}4S<{Ma4|F?p7*-T9vf91C( z<0sF%{-n$o{J!S*8NbZ;Qdj2hL+<70+baZ16yG}$+DYh%>@(*7lq5M1a-ATj5x5@zsx9hdNe`Qn1Tf%${C3Beam62;Gk z1uQiloTeniYU8k6d)!s>1g!~bLn3JH^J;`gS#~NF%G&!_YaWMu)}Dw^&opQHl6H_M z2TgI?t=EY@l80c$!!NK30_8iM8PW(ZrP@k1Qt0F?ZT+H*7pKqz5ZIACmzA-Hgc|4o ztt*6`D&bqJdih|UI=LsIEy5`zaZ;r{ZdRpj*Gbv<{@JQ1ksN!)>#oVVrnmFHbeUvX z3`xH~f(oz7BI{0M&a5n}x2f&v6HRRt#7`+@L}J(>s=unzKC6a}>edb5XMF!}?nMAW z?-%=U(ly!69O?%O(u8|6-4^aLgTIt%6i>5(Facx8QSvO^v?o^YZ02uap#CrXiLITt zR_{3Cy3*gOEM3Ml*?syJ}=acKF!#;KhX&aa3Q2Vy|%?pBGeg0#i4FLpAcclE%Zj(u*`%6vF!kInIOv z%Av)RD?Ce_>6RHs-NXyCy&(Y_kh}tJW@3r=Xy5Te)#_}U)VZ7oi(-#wRc4iDS(lXA zGm8lLD$Ru$I)#(ZXbUUv$B~h_>e&Tb8@jOHxON; z7X&1~%mp*yJi&1?t~kE3wYdihL&WsNNB`%a5aCnqR=~OM%A~b8zgVQVTX`k7ljSzH z5y{Z*_Fv%`)jD!D^hCFqG#slqy)brw&|G-CU$g4~%bp?6(B;KbUqLY zAJ{(OqEZXzCacx?cgQ?%pHN*FUU$X9$k<%#<&b@+R4U|wu%6ohc+%ROcv4ROtcXp7 zzh|p^Km6w^drVdAo6hUgiyxnC$eLJmSVweTzC~OJ0KJ1!tNc}4L>=RFa>K>Ai9i4B&4_Yk1Sil<={a5-}oag7S1e0uQXSGhfozH zpCUBLM*x-f`b|@Us;5_-nh@dY zcAAi`n&ZIBM$RY-L`Bb4W=}8F+{c{XQ{B)MxOYXYCGvT0^VXcw{C5oYTZc2$@WGOA zzy4`HF+&^@sAnl;J=O%d3EyIf9IGHupQ$~A`Gg8~NI65LCQ!P8yXQNf=}nRz`;(;8+H2f*h2gY%2;Gyp_yQp({lPJO z;NBouEAQGmy{q*qTfscJ=TRo`3~RrXZkxlk(CmQL?7Gc7lC3r}COiDTtaa-xiMn4Gb>KJ)Sd#9sfVeBgKV8bPE+t6LFvGEp?nGEMj@q_| zv0S%0&!akq#porf6zrm(LI@Bp$Tgr5-&;iL4qYhSbA6s*9s}P3 zRJmw`N3QWuJ3InH4|>iQ-L}H_Uq@vk&O%*fcy_)Cfbns0e@E|&n;=Lhyl#IA z2HVSuK)hZsQ;K*Gk3$ZHg{pq6j$VWriVQF?|F2Am4s%Slb%Qrn>x##+@@3s-sR)k# z&koIk5ixY_=Pc=YBP7R9p#C^5P{DxKSP8dWWOCMxsACr$nF*cKg(;J~F{Me|43XHL^{{Z+8(ud}M%fE%6dhXKb$gP@$Fh{9{gF<46C0ib zGhp?tv>#V`1xEMsHhpWe1S`gmjJ~NF>wBnP@(F;8CSY`EZHHuC@#{H-)ezpK7*b(R z&JT@v8$WGIL#r>S7Vq;weVaB5oiR8ixlEvx_B2^>W_p^~k~yqV=(^pjDDqLqyxo$7 z$O@#^!{@FhX_4u8%NwUGic_X|i(;uRie`5^tH2xWTL258LMXw*092{mcwVbwW>;?b z5G6*#NBZsq<5wi5?4)rbuS*G=HjJVN`CmR4zn(KZ>QNxRI<^iiztelqlB{ z?qQiFB21Q+cv(u2a?l|3s!oNHV%-r4wt5hh5VL0I^6L>Pg|F* z@d`UFlLaJDnz~)Q>H-2oBBbk+XcHjFUP3n-oKn5-423~F%Y?!d9}4TGvDkYi9^t<0 zQynwsKaDX|zP3S_f5|T~%zv`fe{zgIk#T5O-y(HT7i!dk9y8J#yM|%T0nZ} z2pVJOGqXEXtT^aj0Cic`qZc@Pnatod@D;C$^U!{E%2%vaxwyp{ z71P@mUI||@SZ-r0C0!ZP))?vE4My5JZ69s)p^{|>&<5^1v3G@uoO%b!t;niEb#w2! zTc4(FX6)AVJuO;@h&H4hI-oMPycWR`{pE_R`ro;gj0O7iL=n(uANu`?h0QcNvW)4!?;M zGcHLI5J_$>MLsTNe|SWu!JcHMUHs!z_HN^lGaokr0chAxyHIJ)+$v_sWt(A+}<9$cKM9LAE6XsAnsuEhHab)+>Wsh^4Ga<4_{ zv5#JEhC(julZh%nMJ!BzxAF({lunOnuB-Wqq6YX1_UGm%_-{ypVOxv_X zkBs%&Vi!dAsCnjGb>Floh_PM_e*(J!L{X!$B6j+3>2Wu)vPN=b`}xN-1_#eP;y0R z#2eNZ!PxkGm7t9A9EP3I|1eaZ?dwD3wx|!4$e(~pjozlAQYZ=^n^P!(%K53H&wc<@ z%DL?Vl`~HJU!l?g(;!f(nb!#_*;)ZdL_lu^F5#;^nA|ok)+Cga^cZ>nK2=&#uuW;g zbD(=-IH0mPMPg!p;>1qCsuMK$A$SSZW4V{NxrZqSyDC%S?V@iCvR`qeLZw|(!U_ss z!s<_OR)N+ZY6%udJ5{#mS;D1+wnA3dgtrp=oNwpEYrf;WjGNx&1T*X3mG&7mm39T1 z-;J74CVAtel#wo#- z_MDn|Xur^%omH*s{Xf%1u~QLnrOF>k$p|A@&dk1t!nHiM4c3DA!i03fNyktdrRYUG6m1evQ6TJcS;frQ7Po_3e3jdnvEvwuM`;&#=p$ zQgieVC^ce#>JS%pPGVVl67t zmZI>4uXo4tw|!i7j*)}DocpGBtQL{x+Uj8YOU!0p;ErmLZ8sVIY2>y-QA`U-XV*Ly>u1v*Sed|`TN!y- zto#+{cbADytNaz=5F{`F>92_%LiTETavzMT$sYbN&w`?L&XauN%a?FF*gCt)ezb`z zMjQSKBSd#KlIA-XIx!fz?UHVm_x^eW#Ln;K1qR@s>WkcPKu{50&@`dd^7a}a)EUW| zAv=1siL&v|x=Z0t`a)qV?dhy*fGV2dU}RL$q1Fjv9_eT2M|O9|+_pPEynkeO59`1n z=~)W1(1}K7^@E#vlMWO&wT!!j5oFnCVQt8@NGWpLB`^xJc-G#`${hr}DMD}P&K$O2 ztd;|KrV(9-c?9Xpou7Y5=@{;}*^1C9!druu-FsA6HUg-`T|5P%I*UQoZ^WC0#l99F{@R zBLnFKQ>-(Al5s)oVbE{A_~Gmcdzii1Iob4lIgh31h1{a+RRZ(@9YlE|82E24Q#Ebj z21LF~YFe7E>9bmef#ucoHn)CFB9VJFE$32jGGyyW1Y>)LTJ)NM0b)e0nWbO>R;P%p z9q+6_VE-J0g;8~`yH+@65O+VsE@0du`UvWuR)c|S7QQ62h?JehBk()2ls^@*JLM0} z$RGJv8Pf$In3G)<`%m~lPg%5|_VO!dcCnO^I6h|#0Ujza-H!~*q{_1`o4sc-o^b+w zY^SU)4*Own?q?7Jh2MePrCG{1L1m$1xx#MvLTUETLFm&c2i}EycsR#U{WJbNA!!8u z24W3h7b<6x4o~ir-`s<1totvDbrnKZi9DZ4Pu<5w#56ES;nFzi{Gp(yF0AQ@Vd>64 zN(O0YtlxRDxR3uCUH_+siA{kHArpQjl*PUYDg?{=E;o@e^jq{bUcaY9wY#!l>?9NL zcN4MbK!wZ-BZ~Au-Ybvoa_{GJ?a;||2zzLg%$~m}7xL1*0xZbDCRyn<>{wUwt}`*2 zNS0prab^rt5)-7Sj$D0|wEej=5TP5~1Dn!=K7?&3aZ*O%QC{J1Z`r%>X*#_-eFzzy z!!c78nhY*be=(qPMg0MH8y~*?NEXk(w4RI>pHN6-;D~5cKDb2YYOA7DOmqn3y%_9m zc|3P5o*6n4ZDepuv!Q^2=%F%B(jV9}Lb7xKMwStzdq3~3o>x)4Fl0JMD!EeG5T8R` zO@4!a$#ZC_uCYfcRT-OJvNW55rHMG=dI~yAP=k##9fwBg`G3e z9<>eJwNCN_!`?edbxuONH8d=z_LK4 zOG?Azyu*r7w^I!ti>XuGPFOo)#Z7tHxP!kcC+n8Qz7ly2ytyXb{;7N?=Dl(v zK)j=MX+7gm-OAXg){HUTJ%|t70tUv2!WW1lJwI0dWa2z_f~yvR%#Zw1$$vd4EE~x? z!r@@m;Fa9Aa+|Ro?x7yt4#_0xZHHg*sHOZ#ooMV79@NF0QfAjaiH?}~&Bnw@U7$Z{ ztbsn5(nwwlGXpOL;_B@#GjN0>JpSn-mVnQ5IYt>>*?9=AHx-cbqL=#LnG<3UuH=N+ zijg%1)>oDGnKe}-L;b8z6Mu=+KAAO_I6A!P?Xy(i1qlwy^lu0;^CNc3spg}$rNrGW zDRFm`I~~vo9t^LFedwdY>nEg8!4hoQ2P#x?*0Df>^IrWk$UTgh!AOR92#^qVC^ogB zEMCJ|eqZTXeq|?Y4`#3SPOauFzikOTE2lODm5DYe(e9zd6u%#p_S6QG+RC6sI0G%D z8=X+&h*oz2?FuRs3x}1TCH&W@B42Up4%E@b^EPn-eZ`#Cq9`uYtE^yjk+p#?cbYwjDsz_2-@QY1xJ<-VgPM{deYfLFs2WsWzqr>C&E=Su&BADHO$ni zdkfZD(onM{ttkyGT3uSQgG`vR;v~Z{8VAyL1oLq;;7%%u-lZrFw>^&wyhl%^#|gBz zW0l};&6AZp0R{U<@?98Hl9*kr9iGwrV6FOmDl;n?rHES2#Zx(CpE*?HH_sTFc%m}) zR~eT302OxKpNrU-I_=L;I(NUXq0=qpOZbQ$T!6Es9{Hs_Xzdbb!i}lDlr3~Pj?h)Y zn`CT2vUmLP0@)kUCV49EbB$8c5|*8M*aw*oC~yhPneBYO81&H`Mfb4s!rpg`0);ynWm;bn!zugll|>6maj6 z7sy+>A#OSt8uF(ZhG&d*q<7&#c$8e3t?JNTodq#jjr?d z|II44|0jT|VLxYtD3-A=wzKW&WIjc{bY97(DEWg@`{KiY6N*`Gw#9{7=vvfU?62_X z_M}2PgzYML91yL=PePoK2XX0-p+?YLl6xvt9_9jGh`q}k z3><5&ihW+Zj?EL_jeMg_fixaul&+zXH|B@Cm6HoM)|$vuxRlqUty$DuzmES`sP>~g z`i4dL?%1+eQ|xV-@uy(*DA7cL!nJjK5M_qRj_Dzbc4HSDCCKtl5z`NaV1huHs@Qk( z`B$zwurk&e+>(1VavPN7O1`TKlz*3)3w>zPL4~OG2V6ZQNLd>y6EYevA1X^VwjyL7 zpiHTp^C{-h(8!zeF^`(nyU{&_!di20UH%|Zo;(q70i{6wPHH7aOn~>P0946=^`#(m z{rjfH@BoIDwa;PE?>3^4bsdO|?fkY;dSMdgYqOKK^1w?R=iWtyJ|OR1k-s(&T|t{b z_1_%K*an-#HX<{6!0+xYnz_Sf#>vH*)Q1Mk#q~GqDv@M z?-D|(n!m#Alb*Hk2@Gw(vHO~_&O@6eh$;gML|>9d<9S<;;mFw7Ywkh7ooUgU-sjog zbH9(80`>jC7KPZA&v0Q}XhSh=CQ$z^b-H>iHMmoa{{DOpESw~*$W&PWLn};0Z>czQ zDqi|h7p$yLk95~I!Dl+3Rd`I}pD945B>-wqe#Pop+%$4ZQMhL!SM*I1sn9t;a+mxj z*la>d1|RDQM#VbRg%yDnqdyPCGZk?$7pvU(YGn5k6h>zE10w~`SS~QmZB7F$1P&hU zXAuIKW&Hq#?4hlsm zF@?fGVdk>aG)V#ew(8$_ZIdXz+P=eL5%XNWH#^OGYtT$-5qnRRK4|lYFUTxrym8PPx7SP zJ{~<>UicEx#)S#d_%7QTpHh;IBFw0heRY66G}4h5KFVIWgo-1328RzM_zZi^9_%#- zv-Bf-u-9mfX>HylQ7jn+u~p`2l}v)Tl>NrH=ly8?BK~Rps38Tx;R8WVjP!ZmZ#)u0 zFnow8Gy`SZoDYf!T5brNNYNerxdKjQnGbAf3Fe@|B5xfRQAJOysC=d(I{9YVEws*H4TunGXTN? zQUBLO!7j#0GH!r#pL^EH!553}`BQ7GYg1HCLcbiwbS=%exBuX`cr|*Te|3?59zKU0 zOjQU^C(VcPx%yI-mIgi}q`bxX-8FhUfLC&BbBpJjaXdqrD2}x%J;k^%%EXCW_1qAs zAE)4YKvt8w*PX(>OiV$bejt~)nqF|E95mBOelZ-kmhVO(lnIYz72+_n8Ru`fk(*0G zBX*#37Mxupj$6O1h;7TTw^Z1(YAQzT3QsAwORIyiH9@gs6wGErrMu!Bl^y~cg4c>Z!No+#CX-PWj^^sU<=c`LhD3N6M03H8O4?4d0Gw@GMuT!w* zIQw1Y-`*o{zq?Yj+aDpa+S#hy7A=Y5;}|XsK8h1Gp!{8n|^-(xp@tr3j`p+mYI8uRToYaQe%` z`7%)veBmHIt`P23?bnd1i?pC@faF~5Q69SzO~~2#vY=JkogydOSH_05rmJd$1p&Eo zB<{pr$xWBeG!2295;D+L#@mVX(MiVBEi*@k+Kq6@MS;gfSd^uA3ZSwzTAC@QnOrX3TpP^<$4?9mE1ng zZJM~5Xonv%3_iS~3lR6}$fl1?N5%k&Uzm=-pM}x^E%?-=oE@KO_6G0x`@Hz`Tu;J? z&fk!{GLv4aKNR@{)gLD^mSBZ`V0$Q{i7P?WpKvFe?4}M`9rfZGRRLbMjGx67WB%IN z^18&l5{5r+mLT7C0*8Y)$g?|yM?gL+5m9nnga)a3h% z<@@$mzr#+E9iG8f(|L|RR;X=U*1;$fmH#c{97Fg0TE0F>VrRS5R^!$7J+%qA#*7DC zEfMLU{3AomENAWd?|A0fU{dw%)8A!MDE2b6UvoUKL*U784%CkZfaE~d?2IsxM8pd1 zdz;#cX29iX!OPlEK`>rcL`ZDTP2jbX+hDI!Iqnx$Leb5vS`2GZ@ z{0J!@De0r-3yCKjQ9aQ5Bw-adNp^vZ{SrMP^&Zc(>4(qPcB$S`Gp=CLfWJZ=#iMBHR`VjJ9?IGxi$?V-i>O4J7t5XY1xTsW4z(KeX zYXt0oWLTF#^&|f%ZHsJgh?aOehIABM6zGM>Sg{YElbo==sDJH8$TJK~aHS8mTzsI) zB_04|xDpRm&Qm%9o$j2wKx2Z97&cncK;l6kA3mWD<=N|^YK{-TO0HL$>qi)#`;58X z4xrq>n(LAB{6TYlpFF?ET<6R4JI(bBxt?RL3*}m(PN@GRxt?yW@0aT-=DJquIm=uh zApSK7Ddzgua(%qH9wKE9H`k}jb--L#%XK%d@qPOlf9@CAnx9UtgYDOtLS?Vj6L~7i-$XluRuq`8;DW zIp=j*Od0ek-M>gjZ`?ybvq(puKz$t-qEpizbFRhe()`uZ=QmS*ew~B2oZ1UgeXfx{ z*U}U;LkIwU9=ms+>*slWKF^d)^;tg8=<~3yebz+1Ycopxi5X1mU;YR9zA-t~*HY>0 z8|P;B^`2ks1>bkUeU0UP)Rav1RX)$?$$VaSB351CtGojRAh$z+VC3}d@NuCLABG3w zlOn=Vc9wOp$V%Db4u&{USkOd(VG2;UbVXyuZd6Ui0si&|GXNRo9PEF3}vrK zer!VhPi`#aVPc>O^*`E#`X6ON{SP#u{=H17e_ms$d~WyZp5~2Ce?TbDj%Cf>Ha}%d zGTA6MUi++_-_oNu`913WmhjjA$@8v1DMz}vY?Em$I_`2AFxU2h92gDIJ)Oo=!n z`&|ukw1&5q50=#Whh(mQGV6z?H&={kshxhNox5jVi6&uiBeMz$tWSt!5q>}Xo+$3h z#mpLN$hBS(WmOHsXW6whkbddjreOXq>#0a!K3E2k{#hLg` zag#8=+S}qLSgsR^U?}=CJKj6|R6+e(@S{cPU4* zo6ob-`ESRp7mD8}Rby-7xGtj5Q%8$ww!CR!65sj>`6E^a$kHhc?byocUr)4)c1m>4 zyxlVR@Thsab*ouVgMsKovR|k?>Fue%?M6I9T{boUVt@p6izNq9z6ppB zk{bOB)o_3$Qyj#S0`%5|OPJyq3C`kECRN40kOMaC+NJFE;!*G!#Do11ovEoq+EZVJ z%cv)0Pk~zF{{S}B3SWwUZm9g!SkGqABgSE<_4?rjspO{@~SMG$P>V?z8YoX*?%Lz?y zt2p89ib%(IGtcUTPxfgc`_|V`UXOaM!dw^NZ2TJXzP`Zfbn9!_5A~){Zu*7dH|B<7 z+tKS-cnM6%Pre+4wGP^)XeZ|8RoGdHpJQS^(UaVd1u{QwUheLjmP<&T>sgk+|A{|q z;A0>v86GNPxUJS8WQX!qz@B6T?4nk818+mI0{AQUJFHtULJMsq<1~dEz60DLnaO>) zoBPR3_DSK6`7sJd`dzyhkiC4VET~e%$LIPo4^9l)qrI#V3>B*%jF?@(dtn6M=b>W4 zxzYz4w$jeFO9uvHQ_9-v!gPsK|8zU-Kx$Sp&&FiW?TmUIN7 zkJ2=eP3Sh;S<#rRZs9U1Hn;K;j6m~wEfkNXd^7f|nfcc2mhuK3C zhRUkLNX~M6f-;r%rKne~Z*@gwP@JzwfwwquROa~Ppdz~m zEV<6uB#J^X1JTw&1KzvzM2J|M(=tL?kO0t#jK zVIEUQ2c<7&z`GzTylg05BmLL;8yJdlFe)ZU47^r?5ryo41l_Rdqw>VEvtCO&Y0Jh55PpENNszx)Q#Eq#LY6@Q(vRSQSA`>b& zU2u<+%%T^`g+w8p!v(mUX_yTCpD(<0F-$IfE*q5crIi=gcj!JLfDo*!gjQEcmFl={ z2O(o)g@B0uxdQvG-SbR{qNrBx{?l%DX!e$J`-Wc6^BRI$)s`46qt93m74{+VpzQ2W z)1tMZ5__3X>_<9k_M`Ax_M>yykKPE4 zX!G|YcQp}+f@}4k%GOZ7#zznAqPkav^4)b@fEf>>i~v7&2qBn$72MO8fd`uEwrA%z zo4gL;f$oEXrt!RUb2|fr6%PN#APANEmwe8VQOQ>YjI58jpI6xVXWEt9cWGBIEI_H` z`HP`X+7<>`M{&B0gg5=Xz{20_(tedAudJ~)!3}}LS1TKL3VMZ0e~Dh<&Iz7gVYBnX zB)vphtB^N;_ikZW3EccJaqx1cAxrF4tgQE~0OeMGP5e&1mg5v2wx*WyCHg*IyF@lK z6Ibk<^1jObOZejKd#L$5{i1w{kV-r-uIEvy_edFiT=+t26(@t`0M}VdDowm3-Sx` zJH5+$sVnn$3itApRVFfhyJ{iuRDl-MU&BnLq#6I*d+hLkeXM^duuNA~2Y?#E# zdhQ*G6X|Q4lo-EnkD|n|3Y2Rl^pi-D#1y~u;2$V`co%<~We1Ux|A;ORALC)FR)w&N z(djf>nfsOQunx_q;I9WY!W^R$5(SZ_(@pbxzOSN?3^5laPVBsXB$udoPCE}Vv%$J> z(M8z^h_#XpfsOXl@Nx}<2sc)Y_;+}$X2^&&&-AySzjbXuw%@->OZE&_^omeqMM20!5i4)FQD4nLf*(hs zSlokV7gXULSrNN2zsinD4e)zm?mInSVxC*@)6{Cu8HgX4(SzrNVm0|`{13%u^9~n3 zdtRzHlILE_a|D8vPIt#@b2stQ+WY> z9FH>o)L+FPLj6-P`=_Zv873blzwWKI3Z$j>h2G!A@|Tq(4b{)09`n4O>^6FdnI?g$ z%UZZk_}oe9AB4~1#0KV5>P(;ow;B?l`F|3tfOTRy_l=2Xc`fZEa8hbd{FSEYgWqsQ zcZ%PqgY;SZn$dTEEILk6YQhwi*M;^gpC3LB4d%c!<*kGAiXgS@DUmW`dxY>V9tfgE zYL}waYvS(`{x%mj*O8pBS**cuqee@`j|UwgeK z9#!n3193=P((T~L9(hl&Y~L4nkL`1X@7|CvP{obzmzn**A%3ZIfayi6*NdifFRr5D z3u)NvOjWDhQ#vzz8g+Y}=^jG{&|b8fUeq>qJ4AZXT}19&&ha&b*l&KT5N96@G~epn zbG-PZCc{^V3G7laP>S}&sqCE%Q;I(3S`>Z4Ij9sGBXc&sjA#Zu$F077;3Gxd(G}hs zG8^*XJ&>k=b6r+=ruaqNq;rKyQ@p91_xJZ2+|yGVJRhBlB^kOHhgFA7R|9>5>Z0v6 zzPLadpG)KDA|}_NWyzTEDVZIR+5XFM+WP#g@Xf++-bm~7)u3w6C~>Cxt>0=|KTkwO zzxDZS3(tb3$xf{wM(dF|UgOl1+4y@4GQcd252kUTj0d-ek215Qtu+6b21QA7lHB(GBB-&?V-e(@7MFF){~v8r}KEE zo+|1|NdgjH$UL=X$x&IvQ>)&uro<`n>ziWwB_CKQx1?`!id1Wt&g~L{gVK`Dg=d(W zsAUIfaul=rP>X0>rY@9n10xyG3z85&Qrnmw zLnCCW#0mSY--EVA)p|6YF@{E+=rn>4Ghh0|JFbMu!@-s&t1Ql6vf^OPFf%(3qKOqEeUvnJM7OC>QvZ z{#`s$cd?yLU1DZREOMN?kz08BK~`r@L&Vdgm}4~FKK=xjf zcbJBCws1NWh)_|TU(th|Dq1XdN?|KL>;mFasYRwr=c3$$h?%ZoeqHCK>q<|Vfo|6+ z+ol%6S}1$;Atp=~`$#iY9_`x*AE`Ldfyb&&*W{(5s#V(8S3wMu4)hsvgOx- zZLY-myZlGd+0nnfkY_0c*A zC&-3FinM;D-})#W0dvJXDHcnJ>)sRfftru1n5eEbz1hQq#ST^2(H_exUDI9zxJPP| zg)ZnJx?aCml1hikkNEZd$*+%M{e9R{d)F0zhN+PKIJ#Jt7bI0?t2MP(6#F$!@@t&D zBsE*DnX|Q%%>;PqC}p<1I)K-g#I5Lz_o5D+t;L;Z3r2Duv-JoSre=%UGH2@waf}9K zl*5tPdbWS(*-E#5gWviBRDim&GG@z+pc9guY{sEjH>T4#2KY5eppmJmAuPEJW!EgJb-))%t71K6w&LwrszQ`sc;#tf4h0mq4{%#{o&E6<_I*sg;wfX*z3_yFVP1R9i%Xn&%_K3akeqqRs zF)I{#QMqb!kfzx}l+RZ5{501acQ@^&_oY`=mp+q}Cfh0!;Oc(6NUGht-?y`mgluiW z(BICR<=D=>*Uj=*M`my*^j<&4qhqx|bi8bRLENcj0f;hK*u%l5+u=z%=Q>*JNVZ-g zOQe4py%J2^s+OIuZN zO34e0RVgt}27ngwuG96lFR$cwj@(D=%SQ-9Pcez_~H`wQ@W=5 z2{}D{-l_)5r#)v^N?b7RYGV3EloNPusIaHz+uaZr{3M&@^Od`YcpkPQ)HS@bB8T%C6hC*g)LptpM;k!XuXGUT2v zAC`F^F5yG>Op1C+NQu`CpG~lIKFrgPj?WLyD-j@v z%R(d{BT6noXoX*273w-HCgF2~>AZ}D8I@#PpOKy~{1dWYg-?`7q7$Xb?8LD_d(yDv z+C~|BWKXvlhpE)}&ocvA_CdE3D@vi9o^Qm?t}bqxii3%&E%Dl|!H;bg5h1bWX7}*e zismgx9KE*L>3ei5!Cd70jeLx!tM*sroOC3vTDuucVVT`MIB%B@Bf-U18PA(sNHW*( z>xp&vPUY5}eQ^Op=}%Z)Y=xX5P?!~)QVq8Kf>W!jVf700V?Cy>wcjqlJ`?gAAIupMX!tJ%^{deKTopu5W7;5j@yU@=EVFGZ1UygoP`E zh0y(?=0p#U?HYR@tJvhdD_-Y(@vIm6T~<^odMmt9e<3cT*Qv|si+DHgQ=0`LeeNY# zAYYwTE(>3FFC=17P7ZgE#vGCAZ$_lv;pR6X8+u(CH_C>-H z55fi;!Pd;uaISt@$F@PnCMK&39UWU_RcT7;ypR5iesvN7LV@TXsV4R^<_y7FrD+^=V7^&P42!^bk93iXS~I^lUqGoFS>~lapImDMoYc7SM!#w z^^LnV^zaPt>3KXQ#Vv92IL3O<#+qu!pJQ4X%B_clRoWGUh3;s3W9(efMWoji{I}=zu+4QgM<$Ob)Ei0 za497p3q}-mJ>TiIa@64d(GnW5J6v69kJ?j3mOsr=gTFyYa@62&Ae=jC z2d{4k)ZZpxkK8si+nQx}D{ePufd%f|$&Sf#J4IxhPUnHS9r7kpE2;6G;#+8PzsRgX z1TjqjRMH7DXtI&Z-&sQivR5-6f*ILNoO=h#WCiMvps1H1;Zsfvwri$ozF;;GaG%3$ zrBHMyJX2Bz3vID1 zWE^|8G+n3fDLBgbLJv2-&_j$b^hw53u+aEIA8LG|4^Us|PU{D_cS~=Jq+gG1bOttc z;PWpi`_&L(#>K&6MxUoSW1b>AaLGc;b90;nQA0ZwHoL>R%t3 zUFr*CMk2zalEb?50sm6v;eqI~4x`go1yStXs+hw`vv1bY@NqKg$f`n>C1QBSNVE9= zXr1mX9vB#u8W@bwJGX;r&I zea>XK9Vc}f?h~-F9)f?$8?zEB>KX1^UcrpvuKV!6BlvfU;9JsQ=y40-I!7`TC6u3o zroqT;;;$D-PFj!n9=BA9?|6!)i7(scfaTF6J@zNfui$@QF&($U5iNnZ5Sj`*`%L?Y zUC5W&)+ZtuO%zhiE;qLv4={o+pv0OjEEOLJJ>C@_SZN=$yUISdU&V-<@c-h9^w#G> zj}s3F`AE0tgI4IqHn78h^=Y;`1Fh#mfb~&sGw4P&T(Sq&xG9KVx^QE+=^f{Brt$UG z4-~4Npn=~^BfF_{N2;aDR7;bjrP1k@q{6QnQ!O3qxAcX0J@@s>WwdmFX({prPyuC= zOciPH?W!e+>VKFmX2W-{F(~UyAP7rP)u~_mDszi!|N!aQXPywnLuZ3UH0{&1>%)}KYP`1mTnuLfzW{;2=xGvqw1JV}(ZJyVP z3rHsgF9=R?H!`XUIi3w!_7FB$F4mDcRlJ%;o=HM8@p$0MZ78`b8y`6AzjMw}zrr;H224)z0{TYyO8(tdz&1EAH&9M+GT!k^Vk2$^R7QB zC+Fn6%kSkb@1?HH-zwhA??T#M!tYA&H;upkPo8)ENx2BWyZGJO<-OFE`Lno}pRfPv z%J258ioH_B-W;2Zx3S0%%x){#mRc`{nRyM>!;OzIK{7e<;h!+s&h`O9#Kb3+M|vtO zg({_xxKOQj-q82F1gAw7Cjd6@)eRoGGHr-6UVQkjd0-H7$rAqqJ?hr^wSOBQ-ftYp zMTxoIBasS3+AoXkfC3?BHQ^_av<4=v!lczNX%#r@2?taXg#1sN(r*oRCA>Wq7`-q3 z4g5bN6bBvlg7YM!bZ(e~?Xs;2M(7zF9)ozDW0m+apC5}0nRw$?_Uz@F<5$2G+pY;= z)$7F>b_^M~wyDrywN~1Xi)utS?qguWtEN|Z$2%OvU3vUQ|9A({?PVnngA_KF$EsM6 z9H;%7obT`*PRowaM6^wEzC$_E#I2h+;DM|`qcoJc5f+q_lH$;kaD%a1AN|!K} zEJPs5!(B1GT*S1%uf}sDhdQhZL~n!SrOzq&Smq*Qf5%Dya&>l4P7oZF@1Dzd>U0w= z2D}K6oxt+xzkIO#c z@V>(279%IHthWmL?xMx~t>UlThhpl4OdedgGB8KZq{p)Jk@GnT`moIzwj*7Abf{F% zp$W-W8V=k!id&?9NtKRdZO&pB7g{+~ArQTZ#~h|0Co0ezxkHv~<&5tp?aIAH^4D*9 zjWZ)*WaU@rw!eP(yM#hwCk#Zx@q z>W=%a;a}R8eQTIDksWtCe>_q#tI5}s7Qd;qIU{3efA2|oTbVtp&=oxH3h`Iyib9-Z zC!cc-#GCjd)U$WR$f$NJ5EW`~PMgTKdIbsDB;|fo18~HHI}mmCePz)lzyo3%h_2>A zakFGm*_P9jn6Rh0XEh%K`yi;`Mz}{M8R|*^DD!v9Bo6<9m)9!)F%{yxQGr3Uizw;t zJE8*@+Bt=ZbE-x>(nQ_iKGakoH63AyMB+3vN-K{pCQjt-P#8O}`QY-M=v|fg$OTwr zJf8b!i@WHqE{qG`7V~Xlh4(f0FX{wkc56rC7KKx{@UnEV({%g0K?I-2 zir_bjqoQ^;i#TGPEPX$35>_SdVj(yOwB4&6iEzjHS_sX&8>{7=);M(~o}IC6`x?J@ zO3N5sUYRH64&n)Z@M4s}Z;x{Va;(vENJaeuDn|ueCP+s%Gbzx)BQcuE0^P#@ETRv1 z=Q5m9)GbeSnsKu(4)NJ8?F1p`zJhd=4f%<>&C{?qZy5`r6zENtPpHcIxZasHe zEAZm`Tnir%?N#_9|2s#%3!m@z=G_FRpf+{4DieLX%n#ppw}$)QAJ+N%gVNt0B=$ya z)O%*-OkFwE9lZI!_|J#rC@@sQ(f8&WjBsJF=@`4fbp4t z!fhv1T;S&P)7u%mlG}H=mHc>e@OU8lF~gEIQ@#TlsOi^}!kV}lnDfm>z{%Vj@!PJ3ixxPu43273ynMVI5 z1uA17JB768gRjM1K?_6>rQlC|$xQH7)Psuj&&y}dD-TQdvDFzh5%b}^&C|pP)fZ4U zh=sQ>NW#`4=K~#*o$AtN3O`G&VpC$;Xk!u0R>rhXlA^F5lr|8(jEC)|7v-S-Bv7mn ztwxE+U~6PcEXuZ;P4l6GO1pHL*t_$+BO_5Sv-t+q{m=R6K{|Dx_sN1E|3vpY)}N;9 zuZ(MVh=h@T+ZV8|q3L4-3+~WK_>UgMcQDslXKXcM?^}Nsu(V_>-=K{w`r30;Q0lw` zcsN+om{hJxLHJ9If`ZOpITw7JM(68+i<}CrK-bAskLW#RFs2VEIE5H|RlH6f)IORb z1I9oa1Gn7epc*wlN3WAaTtqT+;JJeQ7>t?{0a*1Wce^6P^-amOcCe6qR>kd1z}efS zd8a@9j+MWl*Kav93cz;;$NqFB!!fU?QovrYyHMhG1sGF zL-tvCK84*uU^cnYhN_S(ULPpXG`@e%tSY-4LlCqR>ip?%k3g$U(Jr8CNEou z^^*IpOc`S~L#>h3Em_Vd;t4iq7M=^v$;_fZ-cSA{<7L4Chj;Y)C1Zq`rCDW4`B?D1 z^Lkr=#^{IF+fu4Ytv3Y@?q5RV|Bn|Qbhzcnix_Y5Ij8=VKpjK#p* zx$;(K&(@W|qvCaNgf~!)+gIkI(+Zi#sAYM`c^w|PVA@#~%m0|UDh&Eh1AHv|1_&F6 zdbD}uJspoE3u3cVjvCFU?}k#4uTQi^_E7g`rHMI#`&Y<9yuUgBjX-pu?A3Ibp5nD( zS`X5?DyPzWYS5kuVVY8e5@oFu6rji%IJLx@ih1cOx<<^xxzrzAE?j}|e`bL`g$hmk zvkP=G3p5x>W(V$N1szBE%#5EH?#M-hbZn!0&|R|g=J=6w+CteJfr1hd^xrSZ=J>h$ zA)|%_gY&%Wr8qAL;!z_(d`KNMPBDMWeFpQl#P`PhEl$}I!8JX3-Fnf*jUNS1P9VO4 zeUm8?I!_}4oj~*-yyW}{4}T2&UqV&c75*fVaTic_w9qu5ubX&IY2Xj5OAFsFts{=U z{6p%a_1`mJ>>^U&ND8>`Q42GIlDkP3J?=t+>2i8UeE)A2DaC=x zOYyl2I*nb)|HuCNxl!g{*9b<3>sdcrWc{4Qtb-*Yi+5coTb$l(83qpCx^_kC_MhPL zDJyP|^Z5g&W^Z{2{g)>O&#=fNg4B=nLBR=XlFVXtc@y5^GL95YN990mLdI7!Tc&c4 zu0O1}>+ynBt&!QtRsX0{*ZPkEf8t?(wsa?^1LyD60oi(_P8^imG;3v9{HmnR9c+_R z7QYl53y0bVCT=pN6w9dTC*I)5FbVxDgFN*Us7PKMsAMLqBr|ZGof^1SBTOrF%5}cx zqfQme!`Eb-{&FIz&(xGeb zPBq~kL@CzrS>Gg+KLw%v-*5htOy0}=i7aaWS?1pa@BMq?arc?O&%|@8zu)V@*m^x+ zSQNWAV=uhZuscvI7?%SG#vO|50=Ai?b&R{*n-wSW2SXdvZv)Xk@sc1htlbw(bI$Fq z>F5Gc@$*E!5C@?7k7rD(;Q(x=%iE`LfQFA9KTY@CJyo&o&TBDVvRfLT6;WS-`XX_q z>8<#o^is|c0}`JHr$K)O$$ul?a=HP(>dCEkPTIlbb?Fm!XKGI+ zzibq#KphNZq1lr>(I^mohUd!AN+2sIIf7B9)8Yig)E2LkejiNR#w$pAGVAx5qqv4H zFbY5a>fZe~)i~?sdk82fxyLz0D(O6&mF`j0WqcvdldF()x`Hb;oCoU9kcR{1;V`b) zC>j&p<^QB4C}_hZ6|aH6f?t|w_@%A15i2)5!$ZGg2)bhY@i8KJ7KR7^>wL|me%=t3lJsxB+(1>qCb`^+miEC(%3fiURX^Mku4FHUNuvmhEfun4y6W}R$*HuyRh%f z`G>pF%*D3B!(NqPxw&HDgXp3D%Gx}iB`&`+Gk)^C>rcx3p67q!cW;;XQdj2hYRbyb z*KctSuXTuh=Ms7-T8vYW>o|B7pSHrp@yMGErPd*uG$A`Hd*1FWFvTiB2F=Q`_9qp= zRQJ$_MT;{PLm4->{uyOU#`^R1Up_r4Q4d;NPrp$`1>S6-$ee+wNKd8SvRKu6Nx%o< zsMf+6Gjf+uW-ze+MOl8n?Jf=}LWJEqTtea|V|^oSQz;dQ7mnxz&XB@lrF+|XUgUF; zxg?1bNF^+gWccNXExfLRFY*<;*&q2|9C!28;Md|T%F*1J2Z*|{S_JZYlm8)-pvgK_ zo=kHl_c7TsuhUy`hMHjtgfGG(H3@pCU;WH`SRSceFKRoy(VF<%jCU(3QPo%QtSphH z;SV-CrCfS`5!MoVjpYw-eF>&!TFMXo5_|sjFQ~1rRda+S4|E@;`$+98k3(wD=5tuy z%M=A$oS*j8+ZTByQv0#o(vBy!OTK!Q+LK%useKh!B6D-{m;gtURSSVou#gks_UtYo znb%=JGTwmXi~opT?gvRF3{Htzit%j8mpK^=Xy$DpzR_oc?!?!@Z}e0Y)dqCTvb z@XdbDs}C!=YbJ^g_9Oy*O2w<|p69_ew50e7kaa;Y@(Gp|AH4URnUaL08GBW3V((yz zjpsFUjoU=Bz#y4%XD-QD<^K8$l5gE#AL#EaF{CYJIM9nvaakryE#ivieI|D2ck%`# z$D-YeEw_myQqC|A|5jm3xug@xYA6lV3t#8X2j#tI?yb5c-nQ>sLJ1Bx37?3McB#eo zjVcor^k(~@0rIN2o$bpy0EZj+bE*m-kr?;f5H3?stW)G{lp*4`eS+tg6K$Gx6qmFq zXI5loTLlC7lT%DW((K5(9FD$Z*@ZbNbE`9l(-TOAq^6?9-dF%fFl{53`*lTQqBzCHdmoF_NGno_I0&5#v z#Ze^3x(CoTcHh`qd|MFYgHZn+TfNtT`QK1;c#-iGJyFFHoD%JE5Cfv_$w4iLDiFQK zx7hkxoy914?DF*@TbHehm9KZFfIl9bWnk^4)ukNZOOuuMQW+wM6o`^tobFF8FwOjp zyWk7bc}h{4SK^=cCr0wDcD3h1&z-*5^er1hVb07%s%sy9YPu$>W&I)y^SH`=n_DsM$u2L8 z>fGWriGb1XW9uN7SbO-i;Jh&s1Y;4GHn&7{Mmg(bVza}iskVz*T<^XNP`c>T$)#7n zRI9(ZMpEh!>lGQ9Z7oXVsa8;oY4}LnkCP1VbgFV-O&3 z0^VizVu+94SqS)t?@{o-e>qmI^0unfQ2Vl!0TQ*d+){4KV&t&F=RXz!YU!%OTtJX8wWv%cC?g?-& zTEke$@fLjHWc`#n%tg8mR_hjsr=mX^;!W;Zv>kjw>T^r=QoeA9b7}B#FMb8*Ay^jp zP<0>r4$|BP{Fwj|ZZ|u^zlmX#werUCjPnI{Nl`ll?W(N!Ip0P0kF%d7J(c8TE*UnP3WeNK2A_n&Ytb2lShkr?DaF6)5y=2Gk6=$hFB zD0gOmzLN@0k_!Tgh7aLFz8sPM((l5-(uE_X3&)x+9N~_lMz0s{Q12Rb{s1l!0h-dR z*6^K2nR-7G{z8Tpd2xw=Al$!Pys(F#>h-wUdyjiXfh$PL>nhtq=K8yU3@E9alz1UO z@-H#P%bpMfL5`#WXWULXNsCyAubf$ik;(Vky;|#SkyUhxBHyQOhEVl{-4*!Fz0mF>8`G>uCA`GuFh&&`&)R@``mJw z_0!spnxTgJ7R3DC5-=UN0`?ZNvxKer3#2FkP7$;g$ixQ4A@(c^wwdVFQRdQg!QCe1s)(ZiVkb`d)@jMAKdu2nUUYBG`1HtQpLPn$)j zV7ZYv>D`c$14FL7<605f=2%iMA_q@nD?LrDt09Tg*#KQytBmPyqiN|p#JQ6PCr)>z zZr9R9QhfeOb|n5{t4PhS1sX2Ek?6&#Z>Jv(C!Q|>1LIXAeN2M$iaooeHwEj?RZimh zj{0#{8QVRw$?bRJ_eTFs7Tx`%CseAOt*OHSlYSIQR(bfo5}eIR@W9|i`%e>htKRxI zrf*c}m{;5DTXwWNrd;He)QYOgB(!Cltp)0P&xwo(&^@7kd^H z0kmMPW5OtUautEccDj!}X|z2*z)N|@lq1;WfXj}6SAYAR5oTv!<@Nt=^LjB^Vu!`N z`kMC7U-|R#mHNU)_Sv_7`o(Rsq}scO#x3wPQ|37Ze`|L1%jw^I`8DJ72S6LuvC65} zDSHm?)N*=L=NAbxTomjp2LMoj*S%0$#_=|iJg+zraLn=XbXOZqim$QD_wUZCFT&X; zLplZ^dWOW;WSp-Y@oamg&dsJKlA;|sXDf#Wg=Xye7Cf=?^Bpe= zalw^G!TR@kr{1rq-hE0Mlv6n?r8_1naZ9$8xLA4jdwIaNI$S)^D~fA(fh20nQhnTf9Z(y_3R(ShS*T=>C5nf2`%Ltd{W9|=QEAet(jb5rX>4J^-5eSC5F()WT}FG@G9Gm4a6Z& zE;Ov?LC%<7KIg%jUjEJ#pVfcOxX!Q_&+}1w{?;C1`7`nHmjpaJ? zLGB!uFmY0v7Z1|nkBxfsnCx5Ve}A$1-^Wq!KEV4-LUTt{B6)tI%*1^f=T?`hvZ6Wl z5vCZQ33Zv2IbC?IIbCQA%E8HJDDPYM&I#UT-z2l8Mm z%G}N0P5f~TAVbiM%tQQL#r`m1q%*hj*T~=1{QVmE408NQ}OB44J`L)7l%97EvIvi;e%RRwF+!hr3BMKm;)Ax`b6AvINfvb}!{r zZ`U(4$Q=Wa2D!Ab3m5`Q`);ea@Z?EXIgOFvqpwaFuEeWU8aA#IU|P0+Vb?Hl}IXQN0XjMg8JZa zeiMa_hI_A<0FO`BiSAk>%Ll9pQqO+2o-3&*eLvw7rcXmD^X@qStTK7ME@oX6c1?8}&B?T4&^SAb4PHEeXK`%g6FJn!lm^mGjq& zzf%4>HpzYwNF?;3@gz1&ucmkpCG%hJ4XNk&$qc%&Y0wzvaHw`M`sV~D_$j4}!H|EEEOFV1yk@!VXJgzyKKws*`&CH( z=Ou!Iu&3eWDKtkHlGgoZF^we{=^XJ8dFS@+eX@9vd`UQiDo}YtI668%NUxm8|<(= z6{xq;GLvj{$Jx67%d2~=>K>)Kg?LIQ8d?sl_fa(R1{MORSx=CWarb{U8yRQ((MTaL zptzZHs6BOv%Gl_B`>AO;r$tT6zmf56BWa5@5vKbA`FB&>Mt2RVf`U#5le}7x&j^y$ zRUl1tpgwQh!1VBll2|-pTaR+_*aahH%8xkb_2A;t@>X)azkOst7lxfTLJW!A&NtT7 zk=W8+Gvo7jNwbUXM|xxz+liU7xG6ebyHHtOca4f^<8=;4@(Ivh>bh%qNLI`) zRsyGbN=fMb1`;_d@2e8OScgZ>$kx&6;mmT~G4h1~_R22|*7a3s7ABQiC`~yz;Ldgs zWA(pH?|(4N_Wq8~&1)T5VusK3DrQgwCscyfplF13A0kj${Z(T(lB^sVcIstRtxinq z5y8&XgLkc*F~;b=kYN44fD11U9LI`@r%-U=e(c_rPRg~%hl7tcPUyfXc0YF_?Fla~ z@V?pW{#d{&4lSTr{Yxi(8(c{AT)zLzYzV(eC6QzoZep>kMhqQ^$&C2Dhy}; z!$zl7S~h?6i0?D0-ON$)M@X11i}yvvn+3|)Y+0ger@hv+AUUm;Ack6tqcpMq_-6{Ey)GJJ^ zxPYQ`Z8->xbf(@|1A_I}nQm2p&{FrfgpQ8e zBW+=4;7|?9`f*S&XOvDqAv;1d&EtnQo(xb0YKc^!1`MRd>!QjQ$cK~J`^9dxLd zl-og}rG4lb_}vKP=k$^)L(o+_FW6dhIl@%*M;1BU4M*KIs|uFGeK=?{t>{Ep1_aH4%<#IeK(Id$D7Olu>dRtV})0qnwc+R$1L&i!aP zM)cgJ_Vj_B5*LolycFNb@x~|BA;~^NNaS`=?%w+*^HEd%NLU@ZA}bcJ){k`mFIiQY zn0mqblPM>XxN0mmnpii!t~%dx;KxVdVFXvy-HEh7TrzQt&30H~9Bg!9siv(PPrR;62Q#)S9eILJ9{dy&X3F*YcU__F-!r|W zT(W0-_S9U0C$DWL_!THL9eyW*r-z1`ELp(-N)&y1RI`z#?)o64{H4%uzPH&Zlbp8e z3P!4_yDGt@TIUW9%vO>ZpN59j{Vl}-RM39)9`rhdNK7dCTp+J?{nw0=Ba^R zQf{L|OM8Pzw&{9F&0z%|PTXf~gag?&PfEBIgl-Hs4IH87=C5}ohnmGW2R9Mj)dgEZ zwdXKT4`(5tD?sr`v7a}Oe}5XAbTE>A#a=Vp|7Jc*VN$4czN=22zK<#GyNl3`o2MJc z%t3g-RXU%lL0Ns^XYo-?FWu&v13!rQ_4ooj?DFPU4XL_D%pS}QW`<9+@#a=6!R{Dv zi6jmo5Gm|iABMc5tC^E$qH_^A4x*VU#m49~+Ue&gB;0qT3l~pA%2z8knYk4s?A%vM z5jJii9;enff(?zaq(;2si%xwH-U#|p&V{1xeo5=AGbx-{A}IEoP(}3QzQ3Osr(V79 zXC{jd{jFKlC!R6M==Rr=rvjhj4{($2-hCk?w#ATbK$Au9->*-r5~lR@DRWt;q$5Nkc685+W~559%4aDDE8?;gJ<$W;>Bq zIm?aM)Cl|Kmfe)`HMSPbOA#2{3p@-7ZA&2S76v7)jWMW{c zncno0GXA(ZA}H+Z0^higuo&`&kq;Ly}`c$v)=>NcxAD;Xcy$|8G9 zgbDEWZ}&DYU&c3i8hN^VOQrpcmLPkrDjy#OBN^8f6RWAmZwwzL2i&zA3|?Aq$o2kU zORilT&Finp61hItYkmjVJ>9eHg1D$9Hq%TwIZKHA1#TBmvHM77m#2-H|BLRw$6M+$ z*WPw`&Rn~b8OuHLzuOy!ui41O5PPrEazn%(klqvR)vRTNj9OSXgyeYNRXci{z0{I^Kw68GPhAl%b;a#fE17{#Bk%CVL#ee{2QF-vTW`GY0m1qL zpm2Tb7(189d3}vgKZ^ksskzHr$gT;_E|C03kv0q2ZCdXnx_lF#G7?TSmJKcsE&Dq5 zS!ygz-CGMd1+QgKS^CPes9$JWhis(N8bX9y5%drqL)9>c&=F*{4D&q3hQ0(+-ciabM?7Buex0w9 z!!fhslv+hp?)t|qQ{ehWB-VXtjQ_AgW9gY{kEbzic~A4%_LPo7YBdJ(I?lc1`ul&9G`;b-+;RdC39ucQD33eZvgu?MRB$IcRWMKF&(F?)@ZF*?9^+GtA?9WeCk_(QKoG~Ru7p!sa z-EDv3kC}u4N*EXlnH4r=D!1!7tW%`^`hzsfOWl!$;Y2_8$8nOn5F|P;MrrWX;Lp#QIX3Dv)JOnREl9sIt{(gCR@i(=& zJ-2qk+}`&I20PCiG*~wey|lUvcL?s4c8oR|Nj$Vk#WSIBMs*NZSJs>Nra`6~(gQtb zn`L1^;Rk+~nQnx~o5I6cVS29FIvzX_`A}XCG`0$3tU>!FBz3%@IK6*$qP~F)UdBY6 z61&Y8O4JK;XHa34Q&Xa2<#N{95S392v>yDvn9nNJOLvT+Y z4b~HsD$4{_&bYoDz}LCN=7`q@<5t;EreMnBb_@lU8p z7c+aNuQHW*o^=`B13kc2@|ai2&9;&qWT}#?@+5jlF8-~C>{yyxD1G5z8> zG$H_xVb0)gUC+FXCf`L8p@{)s_j*g@%KcCh6G zP_Gz$a^Qa>x`j3VtgSROxbW2Sx^2knrusD@1wAU>9(&1I6IujlAiT`CsOQ-$IHz42 zoLLM+d2r!2Y%V_p<2wN#7+iRID@uzx)yZ%{MOkCDvpV)hN^3MzD_gld)}AGa!~^ti z%@y;BNvPD=6ED=19$z|_f&ds~-fr&^h{o#I+lK^_F@}X%OUkhvd`13yWHY8 zv)wxfw;+wQ?}emO>F%?8PlIhzL=?-oL)PZCg?11bmk-~G@5n@t#1hkC&5lg!0MW}4 zew%j~{95VTi(PkH^9zh64zbM>SdC`H$FutN`DkE+Du4YWDFtcN%u%K4X#Zq1P_ zte6SQ{}MpRhna-iuU#d>IU&bhG+f#1#eCD9VuBRVy%`i1ZQHu@QuaEKK&83+9r8xe@?t&ydj@? zf81e8Z8G>#nsy)F>r7Je8I^?=+1~tAqdwM`@aPsDsROeV543_)xZid7QNGu+Zss#A z!~AOsqolggyrk~rpF~_*L1wiR*UFGD6HhAvMws=`{+j>(z8!M+ds?}%c1;F~%HZ0^ zdhw(AK{Gi<6wlt^p6zRNho}M8Bt@|NF=!CcQr<=5_@Z+4LTpI(y~4b6!IR0^Zm)dZLS8BA& zzDvX`H9FnDH?e7Yzu|j3Fqyg+{8ko6?k2u+bo$7>vwQj3=IScLXg43KR#kK_Rl~b; z-!Us$ZXE@0&s;hcb0QpDiUG1`>I~MXEbf}G>J`f=Ke|cWRnm5Au&ymI>I-#2;zCS# zZGF2e0V@(1I308U7bC}r#GGytSV?HaAhY9ZpeGuBN9)sB8tCnh&*Ma*2;-{)re%G-#Uk4o(!hoY&+P z?j2j~KJlQwrIvFw#ue#p0;NSVFIJWPT{y>BSxn`qXs~XXI@?x89i~3q*8-e}kfpm= zH1624^r4i_7_FBjgEAvERo#i7{_511(-X}p`q=}>@GygjJ*be=2&={AYKMt;cYdOf zh}DyeDI2$6P~P=B59Qx5!s*llEw-Wp^VdHmZWFesOk=p8@l=mJXbvko&K(aKkN9oL4sj^xzxv)9Kj$KAc8q0%k&yM9~0I>A5gHKYq)HJ=qNmnHb z-wkns67z2Rvenbsv!Hu(S<{@K@$B;i&A;74cfrB`0p0W!p(Xwt!6~^f!;PP|2B{aP zXOX%W%ZqSfxBJHKT@(ild6!#t^1_0Ua|x+6YpR^nSEtrv%?>T0DRkV~%|(uHF7j}& zia2d5otxXMQTfGrdlaVBO9_@hp@qmu5YYUHG}pkEdEW4CIC079?O~_WyU7qI@R0?n z;Z#ox($mxeBtqYCg7thA@`5F}9!fm=CT=fBgy6*@n?thC3GXnURpPo8X*#bQ6v~r=RbpWt(XQB-LfS1IfC1)LI16=9?=hYg)#`6Fv7;%VMmOa2EN6wWOS8d zHlP?het9>sbooLzNr%Gev4tT-c`F{k@!9YvEM6KFb&ScT){PiD6Mw ziMUu9On#=eB`PIfHsO9C^(KsIiE*ysWYu5yH-iEJpVhB2MM&h=V%n>~gG_ZW|EfFr zTK=wYE(LcW5WVzPu*#wT%=|1vZT&a^jWQf*Lb;>u<|CbinzH13#_D8Lf@Wwbz~ zX%9^q=q!>yinHg_2S<>65{(@w6+?tDb{kRQRaygya>###hY~lZ$Crq{f$5_}e6$D# zP$6dq#}Yg>%U{0yS0&S4?BbyrrxL~;}I9DDwGs}Xz&^A(9+uN#Ny2TY#F0I4Nnzh?5JMhKU$zZ$I3;| zSlv=1;~631^}GOwB%x2v-{WQAkMeDCq0cT)^_9}OQ#n*fH&&LXw;7{ ziXlRc(dVVt&HjM*`14lL?ht?8=)J+ZCPCSyWL4rE*=|WHC%Bp+sc~41JPUU96I-R`YEnF|0T{9?bn2ER=N% zYV4czQEd7nXxBuak)|G7nf^QHd$bsQtexry8sIg3qK6E>)uV7y_$@B??syiFRvr~Ob%{@{xt%QE`m?v0w z9I`I{e3&sRl56v~af@c@peZln5Z6a#7OG=vhLwbzOS|xgD$J3`#*cK8z@0X|eIWIr zK5h1N=>3Mej4CZn!#Me<`siMg#>56K-1z1Tz4yZXfaZ; zv0{(m2T0kY=z9shtdeXryD%ws@@ZC`C(O0V?Pf~C9i+PK)7Xj}hQcu=^$}TK)hxE_ zhf;a!7Ze3@v$fpX3jr$6R%I6`2*7-OJ_kREMNjnuQ2V2V;8U$cUN(i%73w_Xdq@6$u# zOOy$X=VD>^TGYJa$}OO>?7Qe0DedCsrnW5B^bX3J`id`t^`rPeZZE_5rPu1$cs&@L z{i)x*a?>Dg3b|Fzit~x5l|weO_Muy?a))Ie;R&7)ZQg9#rClh-b}5?!u_ZaZn#Ijf zl6h2|6C(UMjNPTIF?iRld@5$wxlT_tezC1oE-rr~QJkkg2We_xR3=B7P zqTrwFwpPRX~vyGO^92aS-RK=R_ncmLr5^AdKV%{C1$2V1$=vlg|W|{%jiRti5CfXyC zI7d?{@vm+V! zD!Z^_?AxYLN0BZ4S(CMt3YX^dM5tf``8vnWmU;`3YwWBYw7;o!aCzcBm61qVi*27T zPetWTvek_VdT2LbmH-I3D!TV&8^`S8O#DnN_WUdn0LpcACtu7{Q(Ou9PryS(+$rJ2 zthpr6+TeZ5O}#j#ecGL!0Bu-jqtHGC%>3eD{fSg1rM+*W6?d>iOR-XM)ADok$UZ4{ zP-5W7_-ZoRC<5s{4J3LrUUR0JLcC3O3?CucjN|5Vf^~l;QDt@T$_m!sOG>u9VEvy` zYLO{Zwu2sC!DA^nr4_O4+qJ+n7y8r49lno(j&H`WPMg6&=Tz00>~no@vo-et?&YZ| zMyyi1E-ooo6nv0u;k*K3y|*!7g>rfqSxT+aJ|NH^zd#=1Z28AEAb+i{5n>NEH0CzPrScjSaB0s2QU$Mclsrh9JJ z?Ql95rxyL~MsIAS?kBsNQ!=>@HzcW-dT;YwZT|2V_8v|AL#LNPa=?4**(E)60oyLg zKI@C@R&02&d3}>CsibARQkfU&@9z|8%$J&Nt%IMm>0I1ts8P6I-IB@3$s|~}f+s`P z+F<=9AZ7WvoQr?j5Sf4k-T71JmEVug(uRn+gOqBqhF4FmXk_d|_$tVs zuZr7SdvTrEk`OYUCNI@*Y6>N`bf_DDidvbokt=Ar$8NCeaQW&!_HcO=`d0mycOox+ zJrp`a-zGc1-JQ+mAuWXLH@APENr!Uj;6B{Nbd#(DCh{Yj=iA)FPuY$WDfezr-lr#< z^3F0SZFDbq9Iqg|3({0RMxter{s|{KJo62JEQ|p8QQvc2d)J;s$Pr@IQZE*wnr6^TNTI)A>AqzO=YDpC7iLH=iH&-#da&wrQL%HhMz#{BTM) znEVI3;Ml2*z~N2WuMK1x9n=5mNXzjbI8t5xv*W+F7hFQ)|CiqQH$jq`PvrXc2Js_K z7DK}c<3?vD{{?OEAF-GJdC)`E-ry$ z>-gS=*5)icThwM%6I+dFbz*O&PgHR0y12kIE49GQmhZ>035~I(WMT+b~SpG}ptJ*nIHBnKb%I8O2wa%YlL?QHqkwOqO_5SD+zyToeoL|`g<|GKOe{}uJ2 z1(sAg*LN3k)y|KEuz$;LM3kZtCx)$cI?eJu($~Q_X4G!xfzoM157zo<{-e1>a%EqU zuetqx)W)vF6}NiGN9OFa`z`zkO=2IW?ooHyI#Wr?^oYxL!nQ`#h8tAhnvPDmJcZkf z>>}x3A#KLKjwFHZ$lqTBfS%w zm+m2Z63(UQ&=fpkC}-Bgx*HLDXjQ*y4-kE5o{fqcPPAbykIJr_6vvb>Hl|PJjkrQT7UOkrI z($prnEtHrcGHb${jJb&jMDxS(#Sid#fUqc%8V@foeF@jpV0~}eB03{dyogNcu4;e& zcwnQoy6lr+-FKAg(QrtjzsuqE5d|^!N7TW!DqjnZH{x709jtp?uO?@fau#9k48(8k zf!Zsbk(@=C-O%JFf&1`M_g1{2Sdxd0lB>0dj>KC+mqT3dUW@8QvC`Dd&?g^1!)9vk z#bhP$#Vrcfh4naVo@(83Qm{_`6N*9nR(!{y!8%DV-QBt>C1P`7UMc(Kz2vu7ZToME z(b7KtvsHXf2gn8tj(wc^z>M~6lY6h>L|*f3Z1BJ2huQcb`af&Mr1fw@r@ zqqBLLY>Kb^fNvJnv7GuXnPJr9S;|Vw#_grnCR(|gtGvkNX^|^3x)C52S@paV3c5`-6@PbvK)aVxr;Bp zz1Ut|xR#@ReFISf{ZucicAjid3Gv06i2_khWW8<_dUwutiGIj5YC*6rNwutfRE)l& zsMuLLGjnf)R<8Fp)^X9PuHa~ow|@KZK^-s)*6pSS8m4yeqJ}|6DSfFM891t&7K{iN859%5$RE-p z;!KCj*)IAJaQ8lr0U)B)UZHJX0IPw<9>4Q(o

    raJJRC|6|vZuyb@Z3sif@Wv6&+U%REndyx}b)NMFp;Y2nxhAP_`GTQwC8@*afooY@CJse~1;j<6zE+`koVCt^5|O5_ZvyQ`HKsdOB}l4gibQ6giZs1%(uQc9Ro zvicvq96d3Vcv3e`2m>h!f2`{_QbSe23`P3qTrO-x&) zwVOTTcs6*L`FIQucJ0=SC&{BTC|10dJob2+99mL1x>X>30?i$IMYg$F{aTx`zKmi6 zZKoM-Mzt86=(IgC5D|6Yq&A`8qc4tsE8^VUAhHnDyzQh_k($D~s)9|KP4U}MKb|!c zWZd)Y1 z$R6_|bh*n*@@yj=6=%;ka6EUeO25<$k{MQbJ63q(xZ})FeH?y8#~Mu^k5!oQV4Ykf zp!rKHDSPWfs?J0^JJwXAgSk&B=Mej9-0wAXI9-wIZidk1J*pDpNAJ}d?qPSIWSK=L zsWl%QMr+nFCpNccn95!1CHbwXa?F()U5H`joxQk-CS^}X1iO1BB9(Q(cg|&g2hzEN z>F(43&6@inZTDtmW(%2$JY-g{0GZw`kjW$MrMY;_JSK}ru>NV;$DidNBQI?@Pn~8x zh;|OtZ*(`EeaTQ2e-5Syx8w*Y`3Wh^&iiH~7R!ka z1)H=c@N8Bq#nbth(!f?%EC>1jevtMMxHf1PMKAryybdEvL-~1LW$I)!dqep}_|y#L zZ+Wmo`B}rd(-twoLAM8jb($6^e^(>rm)Y_+n?O%Z?hUs5@5xg6SLiiQRD9=`uYfML z{7+SriLha#LE^4b)Y_DnvyjR+BF2yMLb`P`s1&sx?imt{L`%I7BepE@NfYMtCrj*2 ziNe&3OVM2&qlpFLP1L8!&Sl_4U&qXm>}D9LZ&Bb-k0(1{Y@SFUH>E)@?cQTjBilZ_;{Yfene6x!jMIlj~hJ2M+d zu_RdouhD1cHPC1E|81C{lR=@$UHFK34Uwfjdz{x?p0FpV&$f%@y-rYH-sxxC@vm-s zwPW}&za30~!MX|*4kp0VDg3&dt}yXC`?0ATluQpHJd56j;U*)A& zV|1{z^meXr`*q@iwlN&D^HsK1LUiMyR8)5fHpdd+jLk86LQ}cf!m4s`!Lsf+VF!bH zd^I4RgHZHnQ>MD~q=4rg%BX@xR3a_w!7%1G)-Hy*lRm{PP)FD_GLx zTv~&|x*j@3972~OH$;MG8~EY==^k{1Ju*0c-49Y4qNibEx@8&8%mKICX_i{05_HU% zJLEZb+aJ&(gj4EW{xPh!0yf*U1UBpXTf^DZa<#<8#Tx%#aiOzZM_P`xsU2GI zs4uk5+@?*5(>RY}kZ1Vg6qqb}>Y`kM;_qF0d!1U2*&WOl<{-kAJqH_S+=G48h&+4X`mnV*9UFP3gWLc$xQyefzx%y zBYs#FDCQ6cvEA8E?7e2&z{?AxMaGh5QpoN|b{o=*qpjm>xM+GKgmaI)LoOtXC=R8l z2Y5yjZLB`BXsxM(;dr2MNe(#k1v+rlee{?OJ$=s~Oy?a7HYYe4S(!*-NiRw-44uKmtOf!u;Cu9-%xl z|5^b~^MFVeJ#Qj!Y+P!2l4H%fbn#Nwaf`&+sS4%+_7kO-7+E9Z<~~YUwZHgd1S8=`v#>KYWVdWEP*6X{%Y(yvbsBlL7jMFT~< z!;ET&@s2?3&)L=v^pbvFr|2()2+PzE;lTEs!@W{Hogz5YGEl|@w=Cd|UNC3%B2B2; zG`|v=Z|>UY0MhR3zc(1~tNkW;U{3p0T}Kv#5X}_+rwf`3Pkn`M;@#gM`4@IB9EuIz z$lI3oON<4T^KfbLtQ@1VEREVNDg>UKf#La?xXjZBGy{(J#0!foO!QYK)gIn4k)-R z|B7B4A_C^EpE9ee%k~gHSc4!@Id5>H%T`I|Ey0DY``;WZUvP8nl!9AgV``_gipH*~ zozl8AHnJ*NLoj2yKH2B;b&z?C6e29O!G${T{cjo6vhzj%6F7aA9I&JtZ~;)1NXV?H z?H7n$q#}%8P%b=1oI0Gqv+F@ZCdRicl+qoh)@C|s>xV9BA&WKQ%xfPOj`fYZG?>R1cXFE&3Izl`B%KJ4@PXVkD$ zstnMnkkV~86suXAy$lpFCWvWkxzApZ8->SC2F3#Aa|&KdZ+^gi@%gAS_dLd_N1|Z;FQA8#a8zml#+Y1hqI9Hs>|j3v2fOx);&u>5E*dglGPTON zZmo$5o2cv+sfp=uMPaz-YvGy^Jf7F9j5C9yRtN9u#~N0XghW9Rk2564*E>!iO$YK9|H4K2ex*M-V1 z=rw&}I4KW26mb#nBCHLGtIF#)Oz+IOxp@E8wBT9N!knrrdpY)nIq+29pj#I)JBwt_ zD6rh+AHjJ)YzDiE`ZoK1gN?2Q6;pm$XoL%mM zSNl0vd4k_CR^%Ps-;EDuuJWn9)zP?S$%6$y_HrWeh7!Gf%66WYLcEOw;T=s}f<{>n_S9Fvp|bwbOI5 z=uM!wB~1d4#-S?cQXIeU5jGg8t9$wTxZ%WQC4S2lV__lQ0-AJIxl`|zOqHRk_9pJ*G75k9twyt7qXOdn--nN7?I({NbRAr+q!&iyP(S1Lg_kV#_ZTHd)Nl19 z)c?vT6f4Dd=v3}J3>x&7y6NpK;8~wT{Z$@d4)s@5iffvw-#wrDJD`5eewO;%3CRi0 z{#_uB`BFDfi`i0tmFC2aRXR$MD93|#a=CX*V~hbb!?z85jN;r3#1;Z4F2!4o=eJku zdoXG+vw0oFmL?roUuMdVE$~P2AAnUlC6HdOJC(m-t76y{@xtacapXQZy1Y3fS!^{^^70(t>Nm^Q`SLJ*u&Ma>*fWf$ukmI^ZqtfI!Y%90 zd`3l9YE3rn)!Nda@BBecvdt4ur61)%VqX?ybC1W+-6-jz_y`CLd~N z@)FL&p|+^&Qrl@o5WhY51$@g4319c1cb9b)C%VS_{j6&xW%o@Qo6 zrZS3=Q(au`{LOpbXzCrOdY_d1wn~M@qrBlB9d1sz_T`nmor>-^hS(l6cNu5_&A5x| zpzYM|&DR->(-%5N%e2xwNd)Ugnu&ATd}3UgX|PUfOmil(PBJJGzrCD6ZMNq9$s=)) zW>}5QWURq?FJ$Z|?%1|!%I)QJ665z%cWs@l8@l^C3OukyT?Zb;uz5ww@@6J;^`=1u zSr(njTsYaxa`F?Yo)xZ@*j>it1%maVW(K7sus4H-?Ur&8 zo-!)u$NNI3>b0?+Sp~#%o6_j7GHi{5(ZEjN6YHM(h+@=uoPc{lXoKWx6^$2C)4)zB zTXc%zb~s$@ZtuAx)`K&!Bk0n03?Bm-{h+T;rp?n8<0Firw|ah}ttqXL(&9TxVjVQ- z)!x)Pc*dXZO!vhOSO(_?+$(VeVgf0*V{<5zRDKUWfmrip({1G*DWYdX2KBc{MQ3oR z!+FIC!D6>l(5MEf2cO(wPx~Tt;p+A*PfJDQl(oFj;xCAG(@(Zo>$JbvcQs4j-y-X4!Ze${D>~F5VkSvC5%ps= zmExAF5>pIjw1d0_)UygUtyM6093_k18(@0L3r%M~I-Za0_l$&~Iby6Un^-)u6?5d{ z`a-uDafAJCv&#Od*80&<+zJy@`gUZ$7NTbyEjE^NI?`0`q57n^Vo!54^Bw9r$JWD5 z!bD8S9PAmfJm5Njub;cudO6~}t?M!Oz~q_=knJC9v5zM>Hl-&zHJjm}z5AMD)l<*7 zP+6V0u7q`0saDvh!DoXW2k2L|W_pt`;t#;neJ7JF9-E_A87M!Fo`rx;_t(?O{z%E& z6ogo264MRfj54X|>)H@*46k+yy!{u{6EeuG^gqwr3fjl#_wc#Vx}f4A{k4$Yg7WU! zFSHw5K%%iT^5 z*17FT)Ny-h>TW>M>OxC9@sKX%u_8H^GlO56qhinLs-}vb--HTQl`SXw$Jd~9XFttV z@ZB<1Q^9Q%D0tSCa0&cl(M zlbD-tZ=RahU+}%}$*{7RjgZh)`?N<6*p5KVz z&^ujZItH>Id=_L+Obg(~oJ;a+F#qUE?vPW>p||Q{!|(^=E!wkYzeYRNIYrm#tci}$@(mX$!u0k&PNu#9o>#mLa8 z|8(C|xWcJ?1$(s|=(LJqw(>33Y@NCc+rd*M1X`O@39XU#o`HFhYQa}TERa0utq;3! znWole@O1R#Xz?`wb_|>w^)Af4M3FZb54}8Cznll7o2O>-bUjl( zv8M}Sy{s3S+4&QkxgHSTGynQayvrT~-iWl+e4g;#GT6>957s}xJ2$SPe#{n(MwN}f zRySSmYRg~vD~OFhmLHC|6)fd9pet8Lw_FXpP59pyk}z*I@U?qqfp>bq?uqYzrjdG@ z+4}tHaBfjO-r(ii&qPgDWtHC1>1WK0(@!fzw~;spbR@pc*t@C+e2)Le&4s zW_t3KklOk%S@h5ucJ8!uO{%LYjt~TWt}=Kxxi@;>#aD)ZlHazSTi%f?&~?zwy58Lo zxFO(PGXZ5PzOv&e?jqSr_}E_lsWNg4+=^_jwiBAyZ@zpFHQzVx8IO_g4Nw_fgb0;K zhvGjLMYD$uFVNw@?QP@)Ul<*z@Pzdn4eV{ka@;F^dubqg0_QGD5O&d{;E{%%GCs~+ z3=}i2`!*q=*rXJ9{+T37FN_|`zc~jQ56L-zxW^QJPXj+WPKaU*G(X+>zJut>o*Uxj zaOo+7=HK8&>0_Oal0zC++MG` zvLjiW5empTyM0zaXs=Q91jR6jwUg7wEBQe$$(yG09lpNBmYUt2`+ASjhRuG0=Hqdv zi&qv2hH1YcJ-8*WpGlQ@yuOzO`wtYU{b=1?cQnOG1w#QPNJzfv`ltD!g#9-f%Th=? zaN8u$k^|0HTkn`;B?ha9XQlBZ`waYpCFXpi0IjG@+{`Rn*InWkF%hX5(n(?ZxSnaX z3Hc|5=^#FF``VIZa=OWuwEH1j(v?<~QWMTVBTFmFeDnk~7b8fXGuF&Cm5z%|?}k7o zwl))69lF$=zuSz2(saZqOBWqO9i1o%f%dbX4s~z#zVvDK<*6RcwBd9(MErmREjHr0 zmw0AM^WRtGjITwKW>R9S!p_&>w(Fu5^7h^@Z1-2?UBR<-en@0v9v-71X@L~G!D)lD z7GV3E{F=z=VW-`w75c#Z*Sn#1z~hUL!UA1CseR6MbR551WFfZS9(Ib7fcObpl~9+P z0M8apA;H8~aTnhpvq8~#L1=)U2?OGA_E~QLHc@6M(@9Yj+2bSwni|gLK<5|A_j+e`ZnNaI;7CX4K(dp}tr^iO#>%PjWzHZQ)w^ z<=tW7{%YY?2#yr|Z!O$MN>_Q8Tev$c+&sZCvjp4&E!=V+?o<(jbGk#7Ve{`3|+bk?nuGu zKm_#;^x^KYaL-w|-2^gUdZOSW7Vc;t?gk5YkA>SPxL*kFC=1uhhf}Z+@VmjnJ=+;L z#Z7T{sC`b6!AI>l+`cL!XVGO>i?+#nvU|U}wfgY>+$}F5o7u@BxtYw!0 zaEw=0XA5((U!TrofYbM119Q9w^R?RT6dmNl9AshM5ll}H=2Z)`?MaUYUoufagN1re z4!CA8wW4k^|}c#e3(Zp%&mgy?!k<*Fn{o2rdyb+1=GWW zDYGzl_%N4SnDYg5f(O&t!d&meoFSM*=QH%49Ps%ZK#vC0)kZ%wIM;_ez`~uqz`zx$ zNKyjsix%!UAMR6T0t%Xc3jiIxI{#>4+W9ccEzCMXwlC!jkKu!97UoNpufF?(U=p3z z+xj+a>=H0`9api$!0R<1ZlZ;I!otl|ksOB)xF=aSldkfHSh)Kw++Wj_MlTC-pPp|&E%rfP0YuwBhCWS2TmNQ}Gm)@ceA>}|`nC^pxB2u3_g&Gesc4Cxe(T<~ zzFlSze#8g4)cf=y`{|v2`q`xawEYD8Fa}Z<$ui+R*FCEe4YS>StpPBg)-g=R-&FZH5#K*-e3O5d+@J#%tp$ zvhtQEtfbUq#x*M0x%`VG&7d#_N=C%{C$`5(vob_nq=InG_A1A%PTYb&+_bgq(BoZb zs+-04?0yyUEw&oj-u%xW(&zD&yKzZW=&~+qdis~WH?&}kAI>D|szaV>m1+4MI_A#> z9QtpxrN;QP5`#vVz+X^;Bd1O0nE?ZNw;ZiiAD+<~CmFXGI>=1xOqpDJ{uRYli75=S z0R#W4vC_HYue3+Ehbyn$(br1#oZPm;d3qkzq}wqb-ShdFt#s#IYzDa|WzW7}JK(*) zAU}fji}*s8fQyGRfWIV7f@z8`Ek5DWPtw;b{Xa;H=LDK8I)*mvB`pph(MXFT<$BU$ zh6?qh1;gfMUiBYMyh=IAKC5p)9Mp_7BPQ(St|~f8Po_~VbsXhFf>~L1y0f9u}j8Q9QtU8Dc zMP6wWyr)6$0@Qn3@7?DApon)F&W(e|+?RP=!`}w}p88+>rM>U;ZSJ36UT&KIth}Gg z_u=;S>wjyz+}Tw-VBO=;GuGBU{7YH!Cm)-6Ilk+pNnPp62Q>ZEw0|I6(3sf_7iuChsko_jW*tPR?CB!HiTvHeAE#I|-|^R#u`!Qd#r~+`_bh(dT+e7i)d{!le5W1y_xXK^ zU&RsrJ%69_tP=;d{7H^*i)Mz)FEd+tZUA-_zi;t(DSt1JuB#^4=+3-G-k(MuEXQ$CMP8-2-spURWLHxn(6 zpA(F=HhDSrWJQMRXN3gYiwJ{@yP#&so{1c1hB!Jq`J!NymwIMXXN1lVNG|kl;D7Hq z*sa{u7t*Om@BCq5esM>&EKVz444~AtAzDb(pAvKSFn|Bx;-T!3Sny zAE&$NgKU3QC2Fv_ZwzG);pB~R&q%aZfPj-GeL{eMnNjX9`RDRl!v55|@KatcJq+o5{-_<$|Bp;c z(Id<@3<|*zUfGwy0B=r@-g4#l8TST4L82r+&;XDbK+*NR8w{8D{3hBRBUPYlV&*)$ zAin(w#S1+wdQg1(!LhbXB_}^}RKN}iUdqf14}HzU-u*kpj)&(9D2roF-7?AW5AhwX z#&@9)7!p^-w|C&ALuN%z-h?gg@{w#>kzLN-LAc<(;KFE0=D7gIh4djg{19B&nD_t`eeDs?MpRNjJO4FNuYCOqPzAp%IxZrseYH>R~Xs&rXvYFoy0pCvO zKo#6qXEG;#5l$8cIb#-)252qB$<7`49`9gGKk38s_%*n2d;#%=BALeOf>pW*I+{5V+r^#^H zUoPK~nfo{RwD>>#^(9|_K3wjzpZ5Rvw+H2B`8oc1N6()!yCv_+P=JI(vgh?zRup;r z?IS7)s>gPv9|XH6W3^x;UvP%JU&kyAODfX;ltP+b<04q z!zfsFa80K_h6`TGd?eZ5ljtQ`2CfV)>=}vgE}hgR+y7O;N7EBJzEA2X$)A784{viy4gwv&$J$(*kk#Gvj?4vf~n-FS}O{!m@;I`c;V z&PU-{Go2}F+FXK}a7^0SgkM%z(O970!pixDTJ}>E;F?UX~c(3L_jPq6cNHg4<@z*%dtg0g< zQa!cQ?(8t>i{QdHf{(77@X4xk5qKM`lfNp8EcHd&+(@!=mxxMJd4MF7_KyS~T{WSI zz;x-42#P~rEI2q<5eU|u#H1|-xgG|)o{dNT%1iYRwgUY}S=GC0cW@$YW3c`_QKWX- zk0%kln#V}8dN+eb^;Pdy__+EMn+|x65U9BIQ6`_Yx6R*^yl_?R&TYXvJ}Wha4$CCR zOGc9NE-A_OPV)4@Ggf_t3K-whH8^vHNgUj`x$wCl9dgXjl7e+J8Uq_M8N&EgIX9U~ z1IY{Dj_*7Lw??yj5~ww5-1_mwgd?k;K?yQZ#G4pQu?CDi?yhS&e?gr7ca?v&@)Ok! zwPozrU&6~`?COD3X9$941^othyNd~1fu8~{pLA1d4|^}-*!nUY;Sw|E0UvMJ9N)fw zaOON@3}g1XWB92loDnWqjc&r(o!;Br$F4CIE#SjB0X|Gcfvs&^Tf3v}q${hEG8)AY zxVS{T=q|nvSZAYr%w6Q~IYKbFufUcwmHjI?=X-=&xL~Jp zgA1oHNv^0!UbQxqX$aNsYB&D5ki3@eSPM|JU8we)Ubw9B40f^o!aw{=Uon9N>;9oq zoKduB|DPa;>G)ZI$+go^^((hc4PO~D9;g9(820PhohNaIhO>k!5J$zZGmJtnEXcAX zcg%z6xK_#O@Ms1eJ)H!am+XJ(j3&dQ8TuUIPU5BE z3Pm5|0V#9Q*aHHod3w%Xt6Sx)4lZP!v8!~_VZQ%t@X_}sv=3)!w8@zGWqj9=*zL8G z3$BjcT06Pbkl2La!f@-}4RyriZ%7uF#~TX6@o8%V!Mf|2tw^oElHYLIfaUieMdB}z z>&uXo0k_q+a`d6Mhp>V3O|WCpVF09(Y)RPf?`a)$ zJHyGL1+}}7d8-IN@J(>x*)TyQc75&if+4ZdwbNUj8@oKXaL7Ipdlejn*o*vDm38iE z5PKtw*ss3v5PJrbKUD#9u3o@3}5me-?XNDwVpcoOh5NJCPkV zNJzOpr$+I>3~Fbu)MmD%nF-p``npTmM2~O{hQc!(6e`$8H?{&ZWt~k{aN)Y-ZOfJ} zLx_Os;p4j`Z+&>_V*l-cR_crNtJezG7S7 z(4=<9iP8I*45D-6I|l^orUOUiaTZ4PP?oT8I%LGbiAAyo54@ZvIPWT_@Y^$!_mh2f{@N%b3IJl!+LKr7g{ej_um)D;f))uI9b9?U!2CUfvLVaj4C$->$mVrq+QiH52?+B;|^s# zOr*hbyynEmdthr894rbnkNu{BxP!!b-XBww!?sfDGqPb~MIB>a4!HNs$>PRU0bVUL z_|#IH zI_W!NSs3uNd;bvbmflP?J~q|PtM1pce$Ya+{%FvH3X4qbZ>x4YHKYEx55<3gN<=Rs zaARUvgN{=DZhsqK@jfn>F-Mb_u8Lpcjs$%vx?ILiDMt5>sf&1MuJARt^d4K`!JxW8ende`57DwFOj|vqHOP9cg9Q8DzRN z?3BPCM+5KGw1HYAQ?z@WYENZw;g)-HxUhPWyKK0)iZ=XVMQRH&2;_|?^L`g2+E=9B zGGa;UqwUc^Gm0L!0T6Hx^HIBQ|17t~s`I!lOYRwbA#S^sUyd}wY^Q-|eDcs%&r=lg zb2J&^y|+eyv8a0q`4FBVLy&+KZu>>V(d02%bWK~I^U6(AoAE53X-MgJaa(jM)v2hP z)y{4^e{FG}n&EXd-K?2iSL_+uA(OPJ#L>O!ZqKmJ29tZ2_uddHeuq25+X!(d+Kc;q>-{w*A!W}9ExKmjETHID3wZpxgRHI2)eSl2U^ za6k|(+N`16o73JXI*!s{C#{f)FdwYQKs5ajZ$9-cC>bFQ)6`uLHsSab_0AP~Obl2) zr+E+|#;06j`fT=duupu7IQnWTZ5lL`CP`$^LE6C)F~3LBU$p2ED>L1v8FS+I@je>{ z>q;nSnLImERiFgDn@J+dUAn>$}z5-+jl+@I`s)Ru16sfC#wuI<(y-de+*i?m`AOPqd(d zr_j=q#6<&@C?2~T-b5|p{K9#{W@+$Cs5R1q&|&>~ zQxOY@p!cKZ4Qde0*;Bl9glHOfP#0vs=!OK@Z#_upDEpn(PWH=SU94ok%GMJqx+~Go z_e#a9);0zqa-Hu*kwB|9sjmzDPg^y-IqbGOSAD^5X(YNOVYhG|qReTd9TNXLcz^(r zrK`61hRCje6vE2bBg9(1h!Oaw@}B5d0_#2r9KLRLhCa3DFO}+TV?p#r5x|jOxA;F0 zGOQRAyHe_!P7e1M1$&hoEEnM*;Aw%IaSmpm(;J(Zq=ej6m=ebcS%C1JWUZHW~kB;)UB?3#bV$v4TimZw0zf}Oc21vt z3Vnd4$$ORy7DIK>JN?^%hxSxYJk` zUIJqD&^=LE+gHCr+M>|C*yY?6)$@1F>Wc8kodZR|!~R`ps;s{XMKOWhq*aUA8m~w1 zz#+Q*8m-W(-&C*pOl4u-@^&i4O{5Diusb2f3F$}Z;)}d4ya|PWs632r@KI9Ixr%?X zP!c^l;j_N9locW}NVT67PIrl^_ge6eJ)CHHW!RYe8#S>_}`4Rty}2tJ~Q3LYZ8 zfaWhy0J3H#k-NN>ba9<^rj@=AQA^b_IcfnFwKWM+ki!2+u1BsmH`0 zQQ)+thQ!O^dSQ*6fLpzs0P_>Jq3K%jTi`og%V9D>>1ln|Rce#E=xDZx!Yr+t6zQhU zj0M=c0E1z#3fl_>)cSE!?}g{6w$*Nw{c-C7K@l?K07&bzk|E`cnpi~WsRU! zKO~JY=9Gy8l934SijqGSY^X`1I!k(M5X7paVrvS?MF^Wg{Y7J@G^3Xhs076z2ctjZ zZp5Pe-CU1SwMnb)r@)ssC{fxF$vo(?)YG1WGKshSeU|TbhSBR_1@_D^*sSo{H%n@M zlH#6Xnjnok$wtT)5$t6f=50#`FE`Ay`ZXW>2Wybp(PiLKB)q>#qK%!|F?lvvi1nqM zkIS+~36^8XFy>5R`-Wq)(jihY)*)`~5nQwHHP3sQ6~<0M%OhcURZ$f@`)dw}%J}ai zJ-fQmsrX$?o7B?J^#%AZS#M?*s55NLB5KE5?v4@8{EQNp^epT5yr43%uA`YqQkhyn zRMdG}Wa}f)*isbS7ueU*yvyqnmrh>S#%bDJsUKa}9sT>P`bdTzSjUar4G3^#|3+KT zCzJU1rfEAGrfI(Srn$8RTii`s)sc~pT zR~+s_n#~fsrHeq)sy5QY)GH3Hi!w}g24xbX(rP|TccND~v0g3TUIDw(t3oSU)lEqM za^DfMlsFf&m&lorQf)yQeJA85H=K(KO~~?xNV2mb%}%ejU07~}9rZE-j1f+QZgVr}K&Q)Yb@y5dQmvr_}%)sY>cD`b07K2joYuPFcn zf!5)^HsTLV1hguWJ4nYqPW`W7)oz5iVXPn*1Wkm`y_!XDH?R z1!b9&Arj+m6umVb$|d`o*}xo01(paUc*4AJiHu@fof%voq%P`SsF9RWh$Cnm>M}v>j z{0&;ws}PVoc*Gh>fasKj{l4;yQYgu8IOZwnUY`WU()F&L)(4V}*-s8jf7jGD>A_`T z<#R|z#79I}xASh-BqLUnYa}|cF}Kyo9&$6A$0^aSvu)8^F7q}r+4IfK6j_BM zbi~!sysl=bCs|!&|4+sNSHCbh?a^7V%Jt}X;lCdd2u8gz6B`K@XpEC@w7a_H61RDp z_K7fRo(n!KXsV^SiOjUZlIz@+HSo-oTz9a=9X8Y4O*PmT>%`gt@M;38u8k+hbF-bL zD7RDi>nsILrJts~ak!HWe3GPw4~9F%lBR^ePP3DGB#xxXc2Wgck|xKJc8(_%*A$X8 z#ZG#C3~EX&X<9sKsgpF-PI^NuX=*I#3Gt+tJ4rj+NvFh;c8(?O7EgMGleCMS^yXO7 zF0rKDV@a#TJW+|JiFmA9MW~HsL9N1cQ7(6b3#x6*R{xnKF+AGowFU1=KxJ)X!P0rT z%w;jWRzIuK+K8-rSzv2#w9*S{vBXSQG0ZxEoy6jrLJ}t@Bxbsb;kNqeM9BYg601&g zs*x^S9|9=i7oxCA11#0NWg*4*`>GtwRh5c;e%t-7H}#wgohTk(g@ zz-!*&b?tF;tOF$koDZX~x72#;!6`t-SfcXqP30!y&C-)2yTz>P0YJiNs~N~rVqaC_ zwKd;JsmytZiY~4`ypu+hkMEu|bl$Qf+Tx`ocGOUsH;9!?{g3S6%HI1K_U^?l5eT0w zk}X+fTOek0N_xf*71c26~-lSJEnQ@DAyDw$*iKID|_U%Z;tXp&U|7>B9d4U$$Pl_psi5l9v0UrD38VII_?Et%km z{H%<%mFq$?KL{KZvx>E_dFFjMw)RwWc?x%Coo1`>zcybi6h1E232MW-MbaI{?Ii3| zP8hMi7SoSd>fKHq)rR#x$SxLfn*)aQ?7$-~F`M) z&c&0c)lj|snn2n2-h{d?x+!oNg`;Tl9fhE0RczzitG$T0yIX{P}!Ka0I?zgYj@eg%_29|ysil($+|z1C*g2Twe{iFltp2kNmqM$^J*4i zNsA8428p)#5-Ml?oTSSJ%TP9>(eda8Pv9A5F8rxqh>NMGvLKS*Qu18M_vhqmKuK~t z@9%sXjo!%n5B!$%eG%WEvC94oh1a8`(|OkOdtv*3|2q%;^_9T-ma^sdG~!nktmJ%t zCBjnJY+25855GJ4J;OF^FP;IO`-mI*Wi&dn!|!-u9n<``-!Yi6=YO03|2BQB+>X!x zTe$y8ekn8d>tK(`tbier{1v~tfvph3@$E77Hv3KGjK^^#bVWjrw*3~71=A3`+8J_| zVV#NIQ%n!e=&(OyT!%~9BeHKN+eBU#Mj!Ynfv+waoDnBypE#^5^Po&aR0l3A3~<)zi)Q zFO(HRNhXXziT^)`UzP^v_2)p_0qbf$Vx=bkzby3zr_`8@{36q9hh$k531vPMQA-`s zc|Jp}X>$D7fp|D!k(iUvROMkt=XgU|HpkRdJFT-5pt%(^8*f@wD;)9|+_`JJoKHd4 zD@4W`enPu!>wYAgY;0H}38IO%MApFo95LT~_+5EbM;hAP#Ib*QF-c;-zEtwR`Ni_d z6ZWRy{51hyVz|V2j7!Y_KfR0o?N9t-<;3i9Jy?H0@@>aqe4Rz&jjk8^*Mmomk=(Ky zl}mO52b0kwXhrW98ytVeyiV>%4;)D54YQx3n!GJ#cqv1Nsn-9PMIYzBgus_sZaKvh z4VBpAV>OeBH`MDQF_4^^a-Y}LfQh48xRs*%lH)mA_2Z;r-^HEJJYB)a#E9B!;Cd=F znR{IZH~mvo61&Xb9PEnl)5?XynIcknLJK5z8?okGJ4(p!&nfT>_7&w%=OOFZ5CkAc z3=dzx0=FnMteCoS=Prxt^W9l~_FdVT-^B?C^HN~AwHIqGwG+oGl4Ctrab2Gn`7pRW z#@(49Vvx32zN|e!bq8vW5X64x`+O;ZLN*yxmUR-c>NMXgSB;zVXO5{}YR>xG?+unvGM_=706>WkYF7cqZ5A|x42RDsrB?sM2;bdJaKBTYN=2!vh`3>T0Z(Q=g zjn?PX5(^F+!%M2EPEioU!-Qj2wOZg81RKR!=##Mgh6=?3yK1$1VL4nztXPCr^@5tz z9lD=@9h(?s@m@Vx8X5f1oH4LfHrK0b*l5~N?D|ZtKHQ;#J%N@CE$}|M+tc5y6*m2y z{PpqZZxXTQ_xn|pbjdkVXafCJRU$MEXb6VGt^FwSRVtBGq>bBc{aGq5^*xass=nV4 zmr&no66oCe7u>o=GFd%UIx?Id%djgkgMU(D;$BjXK+QKo3$uqno4$)72j3v0v$V1* zS>dR=|8zvc(V5l{r0y^%t9r+Qi@)PQqPsM%36dcWY>gYrf&H<--e!!;bPx4pYE@^F z6$}SjUB#z-X6;f{Y3as|tR(%XomVpag$j#_T|3y;N0&*>Th8|{mE?hVa;?fonHYq+ z4Nq47xGb%Dn$+4@z^ZI!rdfPmEB=cGQS`z?9+GO+GLo%F2m;C!xB%V0O@VpvZF?n8zF$B-+u>oHSya)G!T2DxidZ*Vbu0#Ka_jj^cjUN7@ zlRjW8e2P;absoigN?mVr69*iJlOJI{Tn~?`OYykA^X8+j&gfUH_{k@#2<2NnV!tZ z#WrDSOAe9IaYnt5S9&miojdS8BjGKV^#LK@s0=`q6XwW((eBUX=HV# zE%}QACG^U7n5|~M4{7VdDbqaKf(`D#XD(q)iwN~hZxu{=LLL{4JcS)2UGp5&*EoFq zdd0}U(8EVNl?FF?nzwjd>=(V~2}eeH!iPKSYq~mSD0aG1*N$?lfrh{$z>R1P2>kT2)fo(8e&#VyZPhs1k)IS$0#7B8}g70~?l5N7^y;|{(LnO7aU3Pi$K)D924xW>`*xeofQP~cWT_;4r1rp&eQ)qd=QrG8Rql_u7fGI8P z4(w)1YjwE;o0-zKXb%ooBvk&BQ`#p#HKj41d0ji&&1dUGWrURvwi_Mivv;7gmq$r+ z2WLvFVaZxaE1O3`XV?kF$Xizw*E%M-=ew4$rqbzkcE9QL+st^s(N7)wt~9vL?l*6+ z!S0g7ojnK%9lDNw>(svAybOuv?F^2$bT(F)j-{)lGxK-QN1arqdtJvP-bXd6kG%N@ zrDwdZckG@iO&*9(?D-ASGoOv5XU^^C^h`Yjii8o4qy#?g=W%^3qJ}XiOSGdMC5<;! zw_xPfHl*8xjpjYx>6`67NgU()>bS7cyxR-mYAE^Lk~=Y+-6LwzVZMV9;Jz6nk8SqA{hoolzT?34!c^Lkhc{Rs zAGdERt)j44MtuAzjh@dHx~T;t6jA(^N1P}Wm;KZeCw6asy$}Uwh2uoAA=nb92~YkT z^G}2D&41U3;L>cqz|)z-&O%kLRo5`Y-8gy{y7&(}y~ZXLDJG$JzV05_2X&20=$h6T zan-jYlLl?cbc8M;llem-lU1iWWU^AN0}{#PZ$IluCZ7DawFhOd)1H;B@hB*zf$y!g z8~7$q))I9^1}+`uHyFL$BGP@L7}>WXV-c>tg)xRc1q_-VD+YB}sDNACJ{(FP#&e()`gnu4sJNaic zt;0|5RfcO)K5O_7IXo3OoTlA(w!-NFV;slH!u!)vgv%Kk z<4R>3n;zKL&n7;uSiSEl@@R|2{?X~mUlCw~w3hJ74Q*UPSb&%fu|&Q3qcgP!?ovr; zZjz>UaI6~$`2QdxU4n>&Me6~3SRX}1;_JT>kph>gKKzJBeER(<5oxQ()pU9CB`Blz zDJp%6gB3~y-%MlK5^`f%>;`|OuzwD(6tQt3);VllxG{uu2eu#`?bIGzNor51;>V;T zPq4u|{ivG$!Y1;<{$m#WUhcqi9d1XhZ1Mk;boASNY3blYJn&r4+=XfRdYQX6wv&!v zQ(>m82CGpuXqR~g{c20x)?>1(Fnzr}Ested#;eLt3A(#H%^!MPABti&{3SAM^q6V6 z)U{6-VmrR8rsZuvH7$Eyjmf#*{6;wN>qEMW>+Ie%tu**KO*5rwoX-E!^GZzIU@1b= zOzg1)Z!#@U_<+f`F3y8v^m{;i;31m6psPdf_wk|9wJ}jFk(i`+k8D3j*T>i>^E6Ym zQJ7_n{ZDx{;`XGm*@v|2!%&qhOX_B4FxlFxjta(=`MWj31$UXh;%RAYeH1ZxMe3IE zSBwy@L_IYIiy7+oeaQ}qvm#pj<@e;-Nq}(RD*zy}7I!`ZgXJ_BATJC6HoP#)wv4xt zA-cZ%o=O1pPXY8R0_@4Z64d-Rlaf9A$L6K~Mrx4RpWmc0*$!6R+HW?nS)8|1nzUfG zYLYpNC(Woa@8qRwr_3)}H5U~}txwn|qSNTJ@E6?!dq(DO*6!Oxn6~6xn#2@WkS(TB zyy!|w(w4kHte`)J!*6J{^(cXM1?t_};~T6cd@s9)(=YP-5G(a3c>dI{o(vVXSYYml2JLX-Lgh@Pt9j{c6HR!=<^fH zk8ktfI*5#~$W)^2fo^3IF}e&F4~X_0sOzU-(MN{jSUS$$croa1Ee?qv19K}^u4%oQ@@o|R;S zElwZOKOSb*;AZqgz8-FaHA1sojZ#0^DnPoB{_kx(x?I|AN7Sge{=}`4&ODBP7Rl4o zcGf|j;6{}}48z+p#LE}0lx&VfQ3tPcr>#>-IQmAvd1}c5A6lcKhAB$!%{atekgJ zC(bmSPnaOOhiIP$`cg&fnQFMRA`8VQV&BQ^ai<82K&&~WPdNwf$w7%EW&+m5*j$_ z@<$|a&zY^lzCFQh5}xIZ4c~ccK={jI)Np$nHJJ9;8@NqKE*#+kdwZCUKPE}Kon)2J zAtya%<#7bia6_1V3G8W1iDjEO*CE?Phpf+uhOK zh|#^IM_c-6)qR11Yv8#&oa;U~56Xv;7udK9OlnJ}@D;d6y8J5ML?o*!RjIGZL~&g? zudDR@P(s{B*U;U20!P_&5G%q(hrc8cKS2ETaNNzyj9f-H$;NY(#h~A$w{3jCx>ez4 z)de8almCvk_;!YYwT}VenQ!am%_(EoUsk9m)qab&CS4E7QQ>A`TI zCAw-LrD;X$1Fc83>baB^x$W4}{1g6nCB2N%N30~*s`C>{>Jb~}miTOrm&l!{v`>z8 zSAQ*o?0?lhcf3?ZB+{}fS?@)|Y%Q%iPiir0z4Z_w$18XC|GM0IIYgrH+E(rWph>9% zoKjEW+aX=HD7x}RzxoDx3{H>3C(m*lCxy!Kdef=ASE&56z|n8T8$;l5FUK^9`xH6- zzOX=UO}%#R_t9W@m9R3@s9Kc}M6eG0RsWl}RTomC^&D@-!q6&t(T%H_U`vJ=L)@O1* z=pUmS<(Y85%6TVu%Jp+|;!fYaZX-F)`R;Th*IM@cwva|feJ4rnU(IZd)gDu|)KyYw zoD&oq6@5H1?9>%xf*d#gv@7SO$ski3XdQ;mqW8KcSwk7?YJ@(L0i^WayIxX;p zG#!@NUGdp>&2{JU9o%JTi&{lU@ZB6-zo^v~4(wVwQ?jTQj^SZh6ht3b>m{;7C5~*d z$L}DTf+|)EJSW2Mxgwa@R|siUj}d|~vT;v)&bZFW>9A34@l472mFyxp+;sXeHF?m^ z6S+cdoIWBMWbX?fma-Em4L_BV>3rWPTCY`glX?WdXR>3fM2^=)E^Xt(IF&dIfVl*> zabY}6OW-uAhGdl8Rx!-bIY=4mgJXx4e7pwI-=pwip11JaOcwcF%s$JX_-*2SO~Nl` zzaSiYvh_1!j%RPM=*wpn_EWzrJDpAdN!F-_}jFvZ89|FyVyRY zj-n+Xu`&O}UMviXSg*JPwVgP%dJd#gc|;tbc?KYGrao1dE&3*oR0NaN`n(sJ^Xlz1 zVULUbf?jW5)Q8eIEp!y&3k&j<4UpYeV4Mzyw865;hY}*+HSd2>P^Dm1*Gn*4h}J5V zcwwgXA}>uPIiS>HmXQ2K#^tR1>(r_Y{V6N8eiS+8muypqUYkPisaq&0wLnU}jr=S= zO0duGHJ3Er&`T;!4tTTnz*qFZ!Ym}<7ew_IN*#4kU%Otl=FMH@cxf_6q_b#?`RktQ zlZAdZX(VKk9TH1W&Zm{C>P;i5(XzJEPNy_v3o7;eyHrX}5!#KqG2W=FX;gzWsyMir zWw2^jaqvBNIFjyW^MPYXtc>@Jl(u98Jp}YvdoKd|3YYsz_juDQ;Acu(9853WO9@4y z-vl)Ic0a@!h0mnaQ0lgg+0t0-3}E{7XRXa~gF}_PPz)5*nOUra$#qd4MprwH4l7AYI+ml@L&RrPt?sI|N~i`BgG{`*aFUPE@Vd94 z`9ZwfWAy??t&AiHJ8Y%Hc4Avfu`&7718rkc2oI`zl?GU!kuk6^6E0IJ3@0>v)(8N9 zV80*ubDt!$sk34(%GlEqPmW3jx?HLuRtTw*R9QKR(z7G$8_}Uy2$rESN=jZ!EflDU zF;GnS4ydyKnBkzp3H55lOIjksp%jPbY}>?qjyxBcP@MNwao!dwP}%pWlEq?FM7AQ? z&psaN6!eJ+&;@l0y14+zj9Ih!&&;n+5Ud!K^}K4s!c22*szMtp5~WyZ-k!kdzP!J! zzGsmElw1h7$hF}A`(fOZI?1l8=mbMD_+W_Ft318R<)M`{=z-T{%=50Z>2J?1KL?b3 zT_-NQ6apriAJ*FlaMGALv1;jop{`SEbR**`k4(?$`q0t_|B2k5F0*#R^{8f~wkvnh zimlXiWP=$zf6MP%X8k+)J;SdSWt*307SFHv{fcx$`Ay`{}t0NuBFP%MuY17oi*bRc8`(~);~$omPuM#HKf3bXM>S0k1Jwp{>D)!Uldqj-h1;o-it;eid(hRj||_@FJK1urG``b~nKuvio{ zc6TrOJUa3~)ae+7H(T7183xM$2eK+$K`s}_7den``4Pzd!j0(yIgDDz?s8io;qIB} zJ8j|=qm(*jMEi74(v~bztgSI&`@Z=L4wWVQ&)^7In{`KL*{Pqg?&or{bHF-ZO7K8r zn7qgBW3ioT(Bn=pB{fbk(bm1bHE#NPXciX)_nJcGBVBkomlq@DzWng)G?wUr&8*L{ zf5qXLH~F3{53{ov9oy?49Qhno#M3q;!e;;V3eOpr(hk8OopcQd%bW6>u++etY=(KTYcknv9x%z2#b89*@H#2oKOCOruAWhyRQaHoko&L5A zeUEEH9_q0s?MeQAriY4>5NeAWWz-i3_qzjoHSq`c;KQt0@!7)pZ}_l{&FgWP7!ZrF`a$oAOR=G8pKH&XjssoR|o;2NZM>+;a3BY~D5xc4WmP6;(QHHF4w z>+>$kB3kycl{VpHA;{R8WtW7CazgCHMI~ST7%%FuByNkcI04yAX!%+8c#*~S^OCSN zc85SbNyVoueLP}fU2FP}IG#@YQVW!4uI)v*s&sA?Pz(9y$Vr?BAr$XP;bL*Hl|@=S zqh`$h0zE;EC{rpLIbfO{+2K^ySB64_y80(j2G+sm6Hf}lxNo7e%+b}~(-do;G)~`y%<5pfC#aB~qtkM0JNqa>-CGEw( z_{PNO3b+{Hn$0DElRK`MGj5@=Ue`gA&C7MJPdF`6pRTRUr?~~<&TBNIvY;}WF*}0K zim3L~;fg(~wI8+B^L0fCyW&m6(==-e?3kVFvJH+i1-~)7P(G>!rPJpQE&B$#8VAel z8AaHv?pH-CY>mAr15=DHZMU-=qwin2>&P+s{zp*SDSdxTI+M;2U0mF2B7IpAMSqw* zyKlTa{NW#{=uWbDXEPIL^)KFZq*M+eNsnwlcXmZ>ML~)dQ}0XL((5mS?rFKK?DN0v&ha5P<2t zN)Cs3*2a8k%Y-#e)`d*0zc^XhCzSN=E-I+N_o>>F#Uvg!!GDU{f?6_%FKuO^IQ(+I zLRGoMOhr`PNCa;D^P0rdhTgUx=fyJTAz2Og*Tg-mt4R-CvXavt-n>nE{#M&SPj_u} zV5*A%PAK2xevP4!6rnL+4IAVy)mDy|5{p7tHn;`;7^H{o_ISjf7UIEd)AF z-vjUd6jr^VeV!A_w>mJp2iGOiV{vdt;blF~^ueCK=lq8Z{o>Gg>|ef5MO)-=v=$RG zY!IC#LAV|&t^P`@x|_i42CaHJPZhr8Jl>dj#p&UgsVTq0e3FAntDdv}ouKD$)E*ip zNy2+Op|hB!bM($?qK#szz9w+Nw)^D2-uy+ITp(7TjQcDx!yd53%NFoMhsofbzGXQY zAuopQt_Hwl=`%JCL$0xu=925dttTC@tDm<^KTmJA+Q4FytiX!V;DF6?Sm>drBtxj% z2R!8I(FeQ}gFXEnQAI>97Y5|TD0sjjHgy;J@d_<1h+InbJtc;G8Ug)91 zqDV=ouuASYlod$Zs7&*0{>ZoaC6=kXD6ja+1OQVjtl^N+xW?=sovs0 zxu9~R?R#ZKWaH*%i3`y_KZ0%C*xfmOsN1@?5%U;iW?}|xi)uw=gdmi z5Cb9m(mb`r((6fj{00Z5@)i$;C#iGr{iQYu_0i>6ytXk}b|IWJvm#iUwE1LipI$t} z)gYFhTR+7pNDIegKbx`RQ^tGCh`ySL^%{<+WZ8^ z?~H*0mmdc!;_oyDKP?8I#Gd?*{eOvcrPYzn4!T3B@2$wDdU8)?5t3b?-2tZr{3!wv zHZC$CG_P(LQZyZGVTxd{mMb%|+xnmRV_iir<%cI6i=PFT*cj=>AFK`h8J=KGq*rWy z^M*#d@DUF#Hu}FQm6I{3Tl2%S(CN=~A#WgA^zev-gHwULrJ|=NT2D0h?c^Qsq}TaR zlxxRu>&ihYJH)r&xoT&!<^HB6s^~4)64KXJwu*bDnzfVHhx8|%kJa*_?vx-VW%BxA z=lf2B>jzqf`?>{MF7)?WHIi0OUcaiqevGgmLa;w}{u3XW2SuZ7q~3gK+m=kISI#|L z9J&*~TFaGFO0-dbe3=uM_&_oS;n!8pa&{w$7+t+U1b9kcH2;segL0ENmc!j7P~ha{ zL$>+zVFr1LQ8n4}NA> zW@vGPzlV}kha~EsX1s*{shzy4^f_h7Y;qZ_aiMbZl5%yLt06UM7aNWxX?n1`eFI+HJ_g3*J5#p{ z#LI_WAe*)ePb#LNW$gBy?>j$IqGb5>d|lw{%inALgI6Vq;-n-iS~E>1J#p;;Y!Ryx zs;Tkm=~RqYVC00Rij4Ks7Tuba)MzIy38hq)gu1!ii(1bWF=okn8c}&q@@4*QMd)X; zif7c%p?#HX!?+jiZs{kFMpq*O0lJwY)KCDwBBeNVEx^BTDt}6b@_53~7=^CeNYHhY z%O5NZrThw%6b>It5wm~Jt2?z3y`r0wM~ilg2FTsKom(}Ea1M*HrqT(y(`$gwmJ$ad z+}heOYOYkRc*=-7b{|>$Xao6Sqh?PM6-^9#_{@-qYza z|3o_09qL)O=x`1do^^r(rp#hEtP$Rm?p`H2kD{}nu=ED^q610UXdmfWL3DBq(dB~o zHq=z4DgfX*SQ6?v#=YpUAVXU+8b0R^??FYgE(tgGvN4BOks6J+_3lNVirS_){mr7_ zjcJwlq+aGFlby!A5GXI9>m zHpqVyiPMWh*C8%`&xJB~+K5XRQFm^QE$RlkqN7>4Gq4_sVJCtkzZ*6oJZ?v5upt3M zmS&U*7+hm-wt-~>EW#Zi;WT%1gS)v#9HRJg+>7?mL?{LsO-u5LfRW%vW$OtdYG5mS z3h|lfCP!dQ<TZSFwq6nrBf>K6=TI+;_fhGeW}Ct!f z@E(z#uha*GSs?v`hU&?~>b%L`x+$LgR)2RTN|j<%tk#yi%s;RBli&ea=*1c&%FzK- zQ9?L4`5UfuQ9N|=daj#MXw@#?s3xN}{>ar6RW-iLkm4dehGqLl;Ow>i?gazv7^n|KXi_X7Oc3(xZXc{R;DoPhTH zIDTObkUJ}h;ItUOX4VeN{eT-|QM0&@tBa@-?D}deOCZ`>iKTX<;pN%0FJaQJBV&EV z*;K#1{G{5_SzXk%0dXr_zCZFR#!$X-MMG-Jhx<#2k364OgMPwyzQ+Cp-wNiL%tkF_ zXswX3-`NSCWku4FhyBd`6&)Jf*3@?Lnb5DcvNx`he@?aAE&++0WY3?fe!T_P7?em! ziac$cC|qQUgo*p;b*lCa5e}x;_;ACOJ)7&wS8kGKm-i?dvab9b-@gR$mvue`>XjvU z%NckzaJqx^PERP37GvSI{%@<5RNz1B52?59FF&zArYYPN#rtFMuxxFKxGDxP*E7fs z?2GwIkQY_^?f&_<(?2gr|4ie;6KSC8*YZ))TQ~UbSICjp9n)Xu{8)cwNPlrtt>B|P zGm%J#^_z7dfkYj|DDr8OPo@9b@Iy~3$$em)o~iTZioW!p6E$mpL<2;dJkUnTvpWk0 z%D0Qe6KOE^J1aqgqTwz~?XWf>Z$`rURRAy zd$=VzfnHhd4>>;3pR4$zbTRtAM0%GVK0)MQ)yIs^NH=e=R<0ZqzGvb$745*B!BDBT zL`WSn!HTbvH}P}7T8vRc)K+q*O z*0`It-94sh-2wlwz}}WcM-Kqhe;JvQ?MxSPHDQV+Zuw&Op^4Szm3&**^M`g=$}J!M zf32|u#F#*Q{0b_JtGh9^`OnjhOS$wqufB3#XG}6rN7e4{rW?KUYAWw_<%&bto@u1( z0cDdcxS4ogV?@j(6aEQ2bv3i>f1~4PrF^f*0;pPTYZ6~qZdpn4r<(t~v>AJ%xsynv z{EuJxN98msIJv=VROrg76(SkkefAJyBB$8%3FZ&(XwUwo!Tm~+S3?R}F&Yc9<&u^% zCBn#*kN3@yz=*ut*X z>0D@lc#SiZ8uP?|e;bY5&>`@Le*sZ;4L|{elqG~TeXt@VKuT9XF9jr!)%Q;*vNGo8 znn_nHL|KHe%8q8De+X_<@^~A65c*Yg>$|c&&8nh~UXoZ;L5HH2wH5WqRjQ~3c2TMi zCOdth4yeTGEjC{4vGQ;Yn_^Gk1ZC#{Q{hi2IH%UkQR6pTc z4bqsCT@;*|W1crbbxJOsq5Uh~8U9%vF#YR)9*gPd#fi;#OYQ_r|MCxUO#eoRA(Xn1 z5QS+=mcGaIaIO@glrOPOA4ebR&}S`8wCQsMfD#-3kHp5)x}R!1xS{d*1j-w4H@vnm zOGr)(sk$SlIMnF(cR;|e{&6euRJ{p~QzAYfFYuamrk%xCxggf0oy@rV~M?Z+H_T1`EBLNQ$*sHlV@_lAc5Vs{^q5;gDHB{$P3FSfwQWgE^O%~uFb z2Mg2OFR_B0%0)}f<_NCaQ>CUUC>!v1k7e?*45|>A*I#EptV-TtQ&<2Gp3zmDV>Py* zwAHF+sEDHey0TCY4Ze07k)NnYe_szC*H z3f}pw?2sLYOF;zE7(F;A$6R&gw@6@f^-|-G{#o3ml_>9(8h7TZnPodAxdZdlT>dXT zMlsz`WjBfql@jTW~oRyvJ@Kps)V1W!hJ8Ryju0URMHceFR0icuf}{yS@{Qy z91ntL!NdzHoFZ%}R|u33RGdE%`VA8PnhN*LlU;T<*Aq&$&Qa4)aHHxa)cnvw?9I1Q9NL}{Se zTcxC1R6x9CvjxicpGd3yeG>3iCE=f{aINZ8^3Xa#=Tw5bJw+pFlO43P8oagsNwu5M z245;_eZ1(3&UF8pU06XQBpT$C31kuNR9i!w&K0#~D$98PneEg1owO6%r{&m;kTnMe zHv30GHwEl?nm7GH;MoGpP@}Mn>r3^i6%+Cg%)5$hK`y{e{mWyD0Z6B=rBfL!lQvJC zz%Cmh)v~;?gdA!MC!D}z?Q;H*>1I0%9bl{;19G=7U~ahI>$j#3}6&+W8}tHsfYG z_v?=oEhJL;90~tWg*#&iN_hH{K8&B=peJ-j)$gdJLv(V4-WgRNd<=CwZs(nwKwr_D zfEhTptZq`)3_JZ;o51ut0P)yzp*gv6(SM>He2D}va)OQVaDy%EC<}L$GbO&c@>acr znpg(f=Tf6Mvpt!eD>XQXQFDGfa zb5ppgoTOC;qe6pkl+2IEGiz06sW@t218=^O__^_Te|AE_Ns{-*c$`+XPw>Qjs-eMU z5>@qt;zO&{81*W+#j9XGTGiF+qfi%qq+;(;0TsDwnBlHnZ6=%V{qC?ZGKfZ6%<@8p zt;4aZ=~cWdtTNvLacc{tP3DqU)0_qR8Yr|)((qR&=!l0xmcKS`X6IJ?VzFIc%ky7a$wuA<>~=-5YUt13lm z+AjPn!iBLv3!jgid@QLp{zZ}MDv+1@Ye8PL_yy}S1`tHlKvlq`?Xf@RW|*MIbR!L# z^??8y;*od%x7VZg2&u=Kg%Vm5-38Cf438@sQrDX*^4LB>=U-x z6J9)ljgp5XZvTf8=X<)gSgF2a(6s7*z_(R~z#JiR`@n;guLO-WLFYeJUaiVX>|;B= zyTJaHiWeeR_12xdy(MmzllN~b@B9X+{EAy)aZfz1sC>Dil@smuEmu^2s`^kVw{Ih* z&rT`itW}9eMT)6WDF>(zrc7*t%|D#Rc06|wsbm)u6Jt|%{8CZ3QMw%71a3>0DIR4^ zq>sk1X^e6R&jfN|b0~F)N^6NxY8!^+9Bu-J)JYQ6QiY)^Y2~u5iicJ`eJqB)ypH&p zDqi?cI}Cj%aT8RW?~bTkho+i6L_p5}okEb^?2ugryV;M+hoaX8^JUx{OD^*XuB;}| z-bztzD@D6eT;mCb%~6g+pX%LI&*~v$>JXDw&B7wFaZklQ?e6{NdTR2o5A4P*iO0o9 zni=OXEbuuufO? z_tU!345eK$Msm5pZHWS0!;Vg-R9Y%e79bOGzcXWdGq~04<^t5%dVc zs2XAgFD6hJRa6nk{N>i|>>zhYbDw1VQ-{E%PR5OP#u_W{*o@Vr5gM{?p;>|XFC_U- z1xkYaTXl^i{{lo!*7OrL3x#RO#(9&J!2%Wrs;=f_(%|{LxC&b$SNkjQj)ZcW$4FnW$OXy)lkMW@d(ic$t)-6I3N%x z=`JbhUuQK7*vQRj`|zvX`9S}0G}NP zPq}+#Ht0#{35lVfIAzWw4?gnIz~~7E9`3-xVM#tNw3?k+IsaMQEM$O7H%|6JN0ODq z2eS*c>mUm){NuM#tAPp2Y5%>HsBrIKQzLhgap$wv3#762l`;KJ>^#t%QVuBw-wAHU zH5My+HFr5YgwooIp%DhM(O+XdL2?$sBorkV9odQ z{$*Y+2-B6A#~~sK$qshwuh$m!SGeL?0MsO&L!4)pJS9P<6ChU~$f_Uu(K*q-TEgin z|MTB6G0oAy+nfZv9W&>)kGF1E{;gQoCF~DL`(<^Y>A~5zshXBd(t}r*9b+#vVZ2+M zjfgg~%wTmtlZrU2`}=u~sk)Lx<;ZVJ$3=c^lHHP-Stz!Cwp?-)qVSr_&P9#yX71!G z!$sV~Kyt4|M28+fQ~OgMWAvmk#)Kg)V!$?MjB&{jSt{Pe!YbRG$=U*o9M9CzWu8E7 zrYC>HyoShxxEiO20#jp7e-cII@D8Lhfj|KwZBI2%miicTkuKzG&M4y8kGVfvs3)$6 zyxz8kJQjbpJ~GeNM>-HUJedgb92=r$Lp)a9*j~Tcre9k0KWMs?q*&Pu|SM~#gDHsI^|h8 zIzZSXHB~I-U)rYF+8-KtXw|Z1MmJ>XwyYCH%EXbIZd9vO$X>cJNslD!21X9JPx%_O zgG{1%MHBC57KK65>KS!fwPCy5! zDk(lDIkFD-i{@=%EoAP1+^A{wpQRqg&&cTCght3~s9JlC*95BXY-hg(PC!j?^0Z%h zd)jS2ud>}K=9&IIUSqCoGq6g5qr~erGr>O4W6Y*#%t*P?WQtR=T#Q$0jFkO}Mk|$^ zr6|y{6kFzWTpIqUeJ2E3dic_;_u(_*^bU1dF1M~mQkQuQ)ILyrFA#4OkhX{KOUba$ zk}Hpd6B-3MBygIeRmn|z%)#ks9(V(BsK27V?bbaccSlEOstrw!Y(Aw4g{UK&TGd@7 zwN~;+^_wxNK=n+q1cXMH>A~t%;K&<%Z3T}=zeIju_aa!7D7@X1#oaVICBuIy%cV?_ z#0R~;4>{2rdPdByWJ4p=YuiMO1#8TO+(pZQhlK}EgJbO_V5BIJjj%l+Nk=}oD zA0)U18LCTr+N$a-oU zS&~UY1(5Y;T2&6^#`MQZ*GdJqUcEq!ckb8&DhuCPowjE)jV4R7?p3Yhee>WB*&GYO+y_5<6n&j4Sz zSRucA4EcMk9}qpg#xv43ca&LK_5mk}URn^2EZ`tqtM-!6y!>Sw+qd$xE+gcBYWq8n zkx-6K$L%@b4>0o@-?@g)*}@(U8;G^OU!WCV_jQQUjG0Ie_+|S99^>*{cnBAW#^{Y> z`0$&0UvJjWES~`hziAbIb01K!7Y~Lzj7Jab2OZHjG#OYpavmC#dQ{pmLB?u!dTE8fRXhosk^$MY0UY7C#Aqqmxh7+6<^1%A+gg7kUzfB`} zT>D;q(U&@9l**~7x0ub;SA?LjshB|@MzefDIS z)dEqQ#GJ=~9^p>Dae@3K|Hw!Q27U!up5VubGp74e94}!8%k9q*v<~O1xVW9$iE~el z;5ScazOVObT%+yd29{dWTZpt3$Qqht%1EbY;yUTY&!Y7TML-#m8)Mr zFPuB6x|1i2U>Ue}cs=ae9CMQ_3#*@1q5I79Z&uO(caR6Gm#dyGgw5CZPq*Hc>4KSy zS|!G4RWh-7joX;h=8%@LlV@iZ;xeZd4&qcuO~nf;Rae0?T?s%5S$LQZC_}Rk&7l3b1T7blQDX z_b?U#NHNf%;>DaIFfTp9MGa?3kT5stWuNBAH)4oPfRV&S%Psf;Q$qtTC`?aVa=Xkr zp^QTyFMeMcXN90yx8i&v&Kql~FiZr=83fy*R%K^NV)lKNhx+fpk&X2`7L_i2GBupoGcePg5#-zw^# zCU|ziPzAxW$J#rY)in;{eP@9{7;uyCRB6$;H^HLpr3`*|9GDu(Ck>Q2Lm~M>`#Z+Z z%0|k#P_0rp_?E$HeGk7B17emvc^uB`HN)Fi9IcFB+%*YftEnv%{Fu;0D7AIIQn^Xb z-bYT*;!Gck@kpzTm+Yjy&ar`X2Z(ee{`sHYq^f4O{VxhqWTdde$jRa_ffnq8W+~-! zd1yrkq`> zYY&bGkC2d4GFazzW3eg*gJJ{xUxC?7oXh9+A-Dd38wEjV985J zKdOL6>&P-|v$c@0SS6PXORVH?=Lue^9VSzA)7W)OT|P!oG5iI2XhkjNxivQOLcLT_ z=(R?G*Btg`{g#3ukRgN<+aD8Xnwx8ZcjyoFphl)onEh zvkrdj5PGRGZIZ{h0l}eBPv7R(%rjmTX(8Lp=NU;+g1He|L4F*)sI4a?HRa`W(V;)0 zxsZ8=!`##5gLl>MRrK%GtdG#^U<65!ReJGkkDWN||ea5>d-kx-OlKX0} z-5PWq&zsh)6~?}seGSITZNv$0zJvM zGqz}5yH?FqIjp*LQRl;-%;!~@|K(Vi4Aw{Ou%`r`!1P?Iaqz*iAFdl9t}QKi*=EW=750x=5xtRMr!wzdqGUJH;;{fj(AZPu9x^4iEn4nCeEDs7nkST^_QHVlpFJu zCtC!#iFNMacXDTIjGa(lnjQ81x9x{bMD3UkyX zfXc#)P1B&xwi-EP{76@ zZ%D%!m{VuYdAiB#+s@yDPnln?WEk3tHr69i-&KLRIY|pjxCoY!zhYLl=ndP^A8P8{ zF~+c5^V4of9qMCVZkjvVTaNNgEzI!*HfD+9@I%%MVvlnNFrt|V-qke=CL`CvNv_+V*N-0CDY>XL&+0q_bKx=qSY)uxeQEE_xH$a6_pNPSo zB5+g9LG0=fyo6w8jactTvtw^IzHNQx7=`AMl2NKSJ_brPNJ8APka#mLv_nFv=hy|< z9U8T5Ty1-MAstm0qoe?X?Ft$t2AA#lg~THQ&PFo4dZseRJT{)I_GZt~gO66)0FKGu zB{*A;n}jQ6wvE47`J8Qpt3Of`EV({0*)rwOfeL650QA!er2AC zWGjV+#`v@F^U2mB=B{Z8{k=|FtNMEtZ?XO!Uk2Bv-ADkB63iZm-Phjoc+=y3ea|uN zPwZ#u_x63Ao6!EE;GARnc?!Jlf9mIt*zyc+R|8FPa(cQQ|7)XX@qb{q@5NaAu7Z{m z8fUkx{O)60c5Ki7=Pj!(&?zseBM}D(`=1&YZMBK`hRM<_wI8&~rr0Lm&jJge6Ez8( zuLl+sB>DPFpQ&ldJO|)VOg9-E4z=JaOU*8Voeun_ZNCNigc;it{6U|3BjWY&Nq@z7 zQik{Z21NpY7a@>>F`|8nS0#(ddhItdE7_a>sbn!%zNAzJabZn%H2$Q*w5r=<8I^!| z)VyDX`|x=+zbMI{=B_O+A_cHHNDvx4QNZ2mWPO-$Jvh0JWdv(f_FAM|He@3wC+PdG z11Chu&ao_a#KsHbCx)kO;}se_^;tmnv}@Q?MLIP}vTO3Hnwdg_cSyP~|1B-isy%>n z!cQE*BL02C5nbJZkE0R8scY+SB3`jGR7wAWpByQ9yfPvvMMa@1Qskg&$6qMGQ0c{z zmb8vVlAx0vVWfu_NprZnIlob>KFC@m zQsEB#&45ks;$na0_fo+1b&mYmBAr<7W*Fsmq6~*f!pOL9@LPb}KL_(=%QU770tsWwq z4YzrW3H@j0H7QqOBEVjIA5t9pm49|_MgYCDLHTZ^INeCuP7MKoRl-L6h#I0VMv?2pU~9yO|CC;D2A zvDYJI$!hEK3sh>(Btk5S{6eL#ktVW$ROunZz2-}?^rB%MLS3n&^omru^=MPnnoJQw zPmsRIcz8l0z#Nn+x>-{>@=T>LEY{iz`v=Eun0?VDnOtEQ-ibukAy{v%J*CJX7g#YJ9iJ(j1{>%RgJ9 z!#A_&o;TYYyamJCjG66fU+mQW-VEuv&#VU++sr+=48c|QQvJw~8;<8LT!0{ zB()9=|64hXvEXl_<`TVtRuxgj~NaxaPo0){?TJ$bf^wVJ=oiF6;%*_o%_Ekth_D8u;x^g*;8^kanL;I{6G>!L%#lD%H-23S zdL;arpoACVaH;xP8JVhUd_U8TRgVe&8SdwKjA~&fLfa<_zdN%patG<`B%a`F5^KKI z2h+I$=db}VNp-EFoxfh9ID?`cYY5@^V$a4) z0^7Ge6$fl0%w0LGe-gZzS4OTim4%on$862o-H4xRfvN24vgN>BZ9%Ryh@+!fMlrh7 z(K)5TL8a`UH5HCYVT>YdEdztt6cng-1^8&He2y$!2s?`pEOu9@mcM?zxbeCvK=h1mJ7I)+l@B` z=W;bkDB~L^Zz;pb{5^l9gL?J{(D_2wqXP4CB#h!^ii5 zy%p%Jg}(iZn(I!92Qul60j{}IMfm=rM(JK}@Q`^86DK<-rL6SghOJ<=%Uf{ra2_(>4 z3A3cgeX^ft6{9Q=B2GqMf)X)@af;S_saE|C5tg_>lCH4Io18&tj+oFSs5#ATJRe!r++ZDt*(72^W5z%upw1cIX=>5CDYArl3UBTM-41yEOs4_WMR@EO;)(t!= zyZNq>>@a!Y>&CsXlBj6vCO-?H<&aEBguY-gM_wi_C#b=E0ChhVT_w8}LYtFR(VK;H zqxqTw-M=BsCk6q_F4=9qO<QK~&7qa|5sYBos1e$pYZPf05k8xF| zJ$}tsnYd*t7Q$cFk1=wE;DmE!(pgdFX(0ebLKmnmp2>oNwdQyGoheBPe~>_oJfvT3 z4T05HEy#vnbSq`Q@kbH8;jgsDhsmQ+*bVPl+<0EdH6#p>bJPHteN9nlj?2CI>$c%h zG~lq?Rhz%ANE_9PH%z&G8-%8QJUJWO&BYhyQBH~`+cN}Ke}#n1?M4BiE4bOAXMcTa zy*GGCU1TWDHD@a7klhKGWB>@hWTZ2cH}qsQ_D&V;cZ>mu6;9$v(IOEia|iu@LNVXn z&0$oA8aP7=7QPO`)wQ~XX}Dkv(kphUI|CNR-NP{gGbYmHLdTB|rC zF04)>P9Fzp)oNQ?t=4LHwJpV!u$YBf4Y*OMh_=;d92W$IfR=f`zk8o&mVi|I{@>S+ zL!Re8cRlyqbI&>VoO6kVA-5(1^0rpD8O}h&P#sD>&8_Hm|JXzRL`h1x^5{vQ0}A_?l!xLkG5cMOWO*+WnD_2r@D?m53I{GK4H;z-1is%5 z=B)6@>iGM_;;&i(<-`Acmf^b^Pht`SvHBRv!pk+=;27lV)N#qdB-wZoxgVyCGcL+g zF5~9GD`5+>RAy$f`MV#b19(q|wS3u1_lVIC--fuEsNW1jCfnILwMHI-A+6c0wb>*| zF*Q23ozLRSE})G}v7b^*f@tI%+PK_r!)SoEd;8lqAoHe;XoM?J^9GnBKxPNweR@Tv z{%A;Dw$)Xv_g00~>Fb7lo?4a#wV1PYb3(hf^Bd07#~3rnPwU*{1J=zK5}#viVtF`8 zJc&gNW%m6kzz#%*k+8)dc>V6NJIZ=8Lwjo*KoEO|*gE$mSYA}89DMwnuK;N4QHnqD zws*eKPqFEpEqbDpdYl3^lit)?iy40rTszC1#v%|#QdCp9_RI|U{)>ik`FB1_d)*d8 zVXc4|otxZkh%fy~^nL6YMW&@Ak56H95gT$IB-_RbHqw#CYI%I_*b)E6ct?%&4)5g) zWQUk;b8kgW2mPc5B&zMn$)xhvb8pJD>ohK=S-bzj3-mLZROmyScTyN}f}K}#Q5^{p z^pLj?T}W+G_}x5rJByV635fg{eZ-&h+x~VT<$ql~3d!RVA=g@#*su)qfwhikH!2DW zY&p9{Sumxw41}&-)YBiqk#+=DKfU8=MZEN;e@|1n-~;F8)qE8L0bwuqMtruX!>G1{BtScWnBH`+8(I+`v z3=wEOv7c#x7;0PMHBZ(y><)KB!;^?U>3#5P_2Jrs#-|zdocm&(ht6pwTH~BvCg7ri z&q42N9x^6&iN044m)*tNkB0#fd%yR6F`+X6kfx!iDLV~K@^$zx*t0Qb4PN_g7@jt| z5OVJnBUiC;(XNe*s6PH5=T^B&ii?wC%-lxt6c0k{vtWt|>a&K|eYX`LPEL#6X)%L) zgf1zEev21!XLk7B3E;s8f(f4pN8T^i~5^~>=$zvKw zr^1VN{e!_o4q3G8BmF&qH8CJ4WBKwal%e^Qv@zcGfRz_r0fbtqO?|R$3RP!%0`u$M z$Fqd8A9ynt^6t)i*gN}7&@fRvb4EG9*%1WY9+M}wkTZHOYKk$_3}rKdb{|@kndOjB zCA371(%?hREjJ46&GV=Eg1~*eRwD_7az%ma;afuz2H60rk{TPn;We zh}jXZK2ah^q%^%U#I_6wzSEXWB3h`j_vtoJ?P2hp-PNlFUY?fBFbb;aTw_?^`(Og}5!x*m3AtHZf8%Y&a5Z*o< zKdroV(g>DX!DrfQovj&S7fPoKb0y}>^w6})J?Tx<@SDK)w3RD<>L)VDs*5dQ_! zl>CY*&Nve@sNyZ}`Bes7oI{uvqf6lH_}KRcM`YB<(sFxqU`LY&t1wIT*RbHpQeMn{ z9$zvvB(y4=_o@CJ4PmDih z4fm9ph@aBI1NC~J`ACb3EL3} zls`D!S|QH-2w>>aL(RAMdJ^j856r;S;C)8XqVWarGB!>oM$C2-BgSd^p#d8z%nMn+ z_D**DDZ0R&azU(_`;WTKju>mjIi*5?M-BmN8S=OBEDX6*FBluXAX|G3hfP2~nv7O!LT)g}mI^^8ncJ+DL z<-0oMo_hgKJ{Q%yQ&0@d!5?YrH1*|U)Trvqm!NPkCF#gtVa&X#w;8FEgy!hu5~VV9 zAbAOZt8*t|V$ku;0&V{0}H4 zO=9Cm<^jxJAcTx9z~uH?#!25tpL8=Sx$y0VT3mvjGwhodELcqXNRuwpf=v2irI*?K zqrjPF9k1M_i{;hdQu>QSUAZ~1kn708d9M`Mpe)ISnvFxDE;a0EBMp1^j`8jB;s^U| z*#ndiKa6ljb+t}4_>3jSry21|mPLjn6Dg z-I<-wy!JDV-T0Y}-95w~>~gZ~y)oh^+s5EwY3$d$MvCg=+z&%n1W|5^w~$ABY6iMk zJPx)=#gU%A|66oQU3^=8<=c@nNEoTfX?l}Abkn$elX%D2(}7dgbJEvad2kBqHNH?JrjWbiixty=kJaj0+?-V-~k3tv$tzLPKd z&)C}&7mgc@qN+1`bm{@KznF5_^vJD-J;~xvEIl!?pwOxBtZQiH_@Xo&eJ^<#^f)(u zfQbd)?EIJry~duG>yPI*haV%C{#y_}jO)YBg)Jds{7UD}W;PTt>HG)0DnHbXe@@LR znU7Xmcr)1jt|p{&IKI_HZkd^cYyr05d#*hfoA`|7x(O>b$E;3_yU>1JvpaQ&&3}4) z+zvRyHtl=qmb$T*E+;4~Ij6dpzukGdJLGLRJ(=2%dPBr$xAl_$5x3w)xzmPX?#bGd z56Jy`4__ljaTb3)`K)@DW6w~Zhzw=YXLe9b*7*O}Z0o&4-uHJkH?FiQ=j82_lM)@& ztzD-hKh}fxxYRNE{2>?X)D7PpM#$FJ*{9++7+&Yq6>dgJe!4sA1Mi0127aanFudPT zH{7F4;Wd5gw5pTD0hdiT5%q3}~}ghUCu#m{xHHMu0fAGJ0$ zlL%PYQks?rk3xl`$oUIDCw?bRQ2^ucRL&50Ow zEtC8mG^5seswK8%;v=UF<5*)$_ysTAx=M%}e&Nk95up#;N=Wv?*oHLL;D%pqA`Tre zFnuHl;Lvd54yDv3nmWv%gvOC+6l$Niiv;gYHXm(5C#dSrU)r^$f8m$@(7^w5x9yjn z<(K{=r3wGA)O66m8^oG-e(wC|^vuEyuQZe2juJ<3jV6DvUU$F#>FS*uR6O@lr|A~W zCqW_9;@BpSKXdL5-ra=W!-*kvS5+Xe5*% z^u4Zj+g!x?)v(${Yt;Wb_qWBWJ?!3Htkz?@ie?{pTB6UfwUu>4+p3%&wx;@I*JDMx zI?he5wzC>$U1HQB^_3roxOyX0(iz&`To~;cuH5Dw%WI*sS?q@Y>mJ7eB1_ z;!QW3@_*{9ZsZT7I=(9y%Q1|lt}rFWVME9n#uK@qEPJlf$jii|@K92)ncNtb}$2Ex@={>^E8U6>cIZ>FOE zP~mb3>a&)f?#_L;glgU`^+HsmDl(**lQ$JkKT}u9HLNdDpxJ15D6?h@W!7|JD7>6* zHhaZ>Srb&{N6n$9SO5iV8o1y=%Irbqnk_sm=ZtbqXX;eC6uGc#ci=a3`tt|@Ea^|= z1hmM<2EBM)y-1hPl;(D2(IrPvtO?t{x?-jS0CmcIBx#kP*<6{=Frm<qV= z#7*cm?6Cxgn)O^P$u?BWaX6S=AMew{e*|TEz-x35cRMf~KhW@Cb(O25@a+dcLfOD( zJ~Vs0uqB7yAf&^hb1dOKx(V3ZI@xr3bk-Ku_3%_Qy|xyYe`xTW^_=^Zz^5VqINk@f zy~Q%Mp2{x3Oy~1%Z2pZ(Hu9kI9|_YFcJJJvtnP}<{Ha~k?C}RJA4h}*Gn0oCoqVQ= z&#wF@woWPKfE7IB21yvs!)x88DqQO>->g4f3jdqf%;I1&@@F7Fou|t7hkFY>c@X7L zeZ3`rU2{fsUe4xK9p?sgBHbfMqjOQfq!sUyf4|5pwTTt#r+4`II&xdAXLYzH@!O#f zqYSEv|3Mk~(#ap8p@5`TCwV6{Nj_YxQ#vF69BS6q22SxyQo}P9ppCtT17JC;Einhd z9AAEU_;ctN;e9q#w7VmRKNpJsWta-PRVch3uHvyi{+BT(#lS=DJr*t&gp6L4W6I6g zJJ0_`--6nPqJ`W9!Q15DC~yqXUZ9j?jHwGzvvjtFPHzOMoOI=!Xq8bHtiT?jvUXRr znHN&Y!6F#T?CtQD3t@ginAaa?QyqZrWzVyIF==@|uX?+kq~ zr^+i)hs~?y#JB`6zs+a|BA&cJ21MTwL1Oh|8jlJ53&szEf#dr(14>|zbbh0k)B|*` zoX$lDrH)iZ=v@lw0V>9Hf0PMK!`AWT^b=A?#4-9o%4SmXJD4udg9q_Yt~G0{_5iNAVkL>=;arr)`dB9dZmNhvnYzbXv4VL(XD(a({Y2 z>HBj_FSMnfr2bSh=FiKzR^z>Ps&0h*MoA3^oJ(X7=mY36b;p_1R-KsJle^HK+7iG9 z?VR^1g}A_dR%>Ii(jjs(KHCKtLoT)!iP0wvlq&6Y$j

    HHHe#l3l7jw!3V(st!4m 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%0D*?x3}ylvIj+ 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%0D@=0F38|}_f)tOF795J37cjRe|SsX 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

    N0TP0gPbw!>l1s@SlRqGTNPeGONx@UfD0P&* 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

    8?Q1*v6_?>>=P3c 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

    NMl=m>+ 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

    eteNi<#!f?G_ICDBWxmqjn9-4)=KRIehumO9+e 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

    x3 zEEDt4<7r6mnfbZ-1x;QOeihQkeKhrNS^m!a9{L0GcrI-IBysrcRN}tS?5nf~ox$>x z1<#UEcx(vFOqA$?M^+0SA)?Zba7&~`Zc$jGEE?omORPm_(L>?&G)p|#g0iA)sI
    y7;+B$@QkK$|vX-AMr({hTIN~iTNYRrS{4z%*s=t?6t&E<+_KWL3Tm}wjb)u>J=6wN+Od(i zO$5bsvt^5At7RMfcFPXnPRlN!c| zg@|v@EYB@3EHB|+S>9OQf#0J(T0Q|kqrO_!a;=fHL$&8R06TJ>=tO65S5!At zcdjSbi|ftx;rep@xc=M#ZXh?98^R6chI1phk=!V5H1`)bh8xR`l&ERHov$%h_Brcgt;ZnKT+#GH$H;C4OXMoWR16)trjb1wOVafyVYS8|1a?bMI~6>Oyl=sp#7+9G|L6fLv#Ua5t@|% zm!w+UA$XJu$x2(xz?Tila9ud{KU>RND?nGI{ueg)D^w*^Wos2{Rcm#c*Ra;K)}p#L z;X2meto5u7tPQP=txc@WXb*pR%i6-)(%Q<}+S)|iqjeK; zn+a~UZUb+(?f~wz?gs8d?YABP9<&~|9t9t>9tVoYDeGzL8S6RgdFuu1W$P8|HS53D z8`fLa+txeQd)E8b2iAwyN7l#IC)TId7uHwS*VZ@Ix7K&o_tp>AkJeAt&(<&2uhwt) zBl17lGTJiPGTUUfFk84S!WL3|stW9Ur+YC0NEzV}LnQayuXS3RD zHoMJX6Ko!v*XFbNZP{$uZGl@RVovB>)aSP4p(?K}ADhWfRS|GeTQQag_b4t&N>E*r zaw$nKI3M8B=#-(|vV_Y4%iAi@Oq{An^e>V`Onb4rT znzmZD+O|5j-)wbl_1JWMro{6#gllALY->u>W+C1Gb&K#FU~6G(X=`O`P1d!swWaIZ zQU2pU?AJad?_ldld?#RMTNk3chD-!*NjI8xxApizSDfkfUEasmk7Wah8fY728%)#? z;85Ez+eq+e)ELxQ+c?{J@C1~2{B4^AHJLiR+h&_e^mN+{n#~0NV@m`k*^+H@Y;$e% zXeY3P6z9{<0`Ow0m)lm@R@zokx7xM_YAtnvwd<(cNO+TNi)~v-e>-zKpmtHW8@$i9 z-*&)u(00gn*me~D80j4co4?Bc&JBSAc3 zw`ZYBOtTU#V@ViM;lN0{9H>Ak?J9eeU2TuH$JjOYSi8Y)w43Z^yT#7gt#+H;Zg<$7 zcHS=76YOrg$L_WJ?0$PTdv<#cdro_9dmei}dwzRCdm(#adl7q4dogHvhgY84?L+!)t!&!HfeKgg7 z*~dVQC2kze#@qj<3hxWsXEH7Bh_6+9B1@9&$@UcLQYp{2&xM*#-6G0?wE9mp z>8uXvud%PQuMbH!*f-fX+qbZ6D^=Sl@38N(?;(0GP4?OM+Yi}~z#T;$Bk6JA3Dhb3 z8RGF3OZgmS@s^z@`T|WZ*)Q9#Kwm{&qZz)QiMmek2Joi+4(r{wKd?W9d&H8ofA!<< zvL{HMlJuGVIrznQ6XNQZ_E+}T=)9xd_dlHag!HrhtNoijgX1ShCP&(N(^O_h){tE0 z2y=voBx$pF@52%4kUOIOV;|24NH5wEn{LJkALlSRxOBO6_kgxaCTv98fjmk;B{*Cp za|1mNFVW(VWG?`t>CmKSdplm~X32v4G|)94nz#IaWK?IMza~cWj`YjgC!F zTO3;*+rZl$J7^x*S;U==-N^Q!_M-MV_B#$Z4mu8zxg)@1s1wAUbetl5+HuBl)^U#L z^NtISi;hb~<2kb9y5ol9rsJ05Hj+E2d$e<(@&oWgq8~XP6MhPOhI)>Ag?fW}ixQ9b zjt^9Q3@|X06F*f@S)5sc;vsW}5fu)Mpjn`UpDWbM!3vbp84Zjz2Sz6^9({%4Yu=PUA+_{w}0zA9ghug=%tYx1@D+I$`UH@+@kkFU=+ z;2ZM4^Nsk%d=tJY-;8h0x8U3GZTWWmAAEbh1K*MF#CPVq@ZI?Cd=I`S-;3|f_u>2U z{rLg>Kz2!Db<$)Dm+^Jn<8{CWNYf0@6+U*)gy|MEBZoBS>QHh+h| z$KU54@DKSX{8RoJ|D1opzvN%>Z}@lod;SCek^jVh=D+Y?`EPs%;U^)ZkV(iaWD&9o zG9gR|7a|0?pb(UTN{ABFLX4mhv_h<)6ZC>XFbZ*kNr)HBf<@p2t6&oxf>YoHK}Zl> zf?MziUco2$g=|80A*YZ_$Svd%@(THd0zyHduuw!ODijln3;3BXloUz{rG+v=S)rWp zvrt~BAXF575q=da3sr=wLN%efP(!FG)Dmh7b%fu9xun20guvAzk zEGO9t;7VbYuv%CntQFP?8-;Bo-viu_Iv^Yr4uKB~M@W8DI3^qyP6#K3Q^FbHtZ+`a zAY2qK3s;1z!ZqPv;ks}`xFy^c?g)2e?n1u1b38;w)lM<$YrzIo-lM_;a_-RB? zRCr&5>UrS#s0FBnG+UgojHu-aD-u>FtWH>)ur6U^!X{)}61LKrz%EkUhR*hc9i+cA zVHe@u3400eBY1#d+M0t2heDF05_gQ~;|ZrE+1Z41%$;WnSJp~&+U}Q0dIfl$_HQKI zgt~>gjk=R?Kj8uCJV|(+@ChoTE3+%BOXdph5#fr2mb(-ziE_odbS?wD(G^Eh6VOci zR>BVIg1dbC<|GOKU7mo%?ee(1U?0lw%J$t%aF^^MogA)Qt~?=0UbuX&0wgO)JB270 zb`>G2D6lwDC4ePerGaH!WnJY+7C2LRiLO9&MOP(PWl35Ex|*xHtCp*d>o-?jS3Oq) zS3}qDNE^8tlewmn>1I?n2e)vwbhQGvcC~S}b+v=~1JxeY!S$!BqpOpvv#Sf7cyx7j zgX)gzNfSIvA*#2lud6@H@IBxfn(Lb9THsm)x7fABwbZqoBiCcs6W3GMbJq*kOV=yc8`oRcJJ);H2iHf}C)a0s z3-C zcP@8scOG|McRqIkcR_a{cVYC4xQn_=xJ$dsxXZ$oBP)M)mv>iiS9JgCuH>%luIjFi zv<9jcsy3MrZwc=G+`qZ&y6e%TzPo|DA=QoCO`%%2Te@4hTf5u1+q&Di+au}V{u9^{ z)!E&}-4)!;-QC^8-3uys^mg~BZXinsK@D~faSwG5caMM`=^o`C?f%O>#y!?O4t_jp z0%{@(kLTT!+>_l?+*94t+|x-i!#$I**qsIS4{?dWBzH2=v)yyt^W6*Fi`)h+z8{8Y+o7|h-TijdS+ub|dJKek7yWM-;``r882i%9;N8Cr< z$K1!=C)_99r`%`UXWi%A=iL|F7u}cKm)%#~SKZg#|GKY}bBVViaNKa;bl*ys1$($J zbKh~_CEa`O2kwXNM{M#jRZqZAQO}8g;eO?Q1NDKpw0*v~zq-F6%i#IRBc>TWnZTJ* zSv)dNI5@(iAf2=|0j2b)kVkpcv>Q!W6EdrXi}mP8rVr`htI=ch#Cc3eEgmcBIS4w_ z%>~ZJ(^Mdw0CaiWKp)M;DZeM1CkK2^n&krL_T=&8_2l#9_Y{CDLOMl(#XQA`WmY!Cg z)}A(=ww`vLKRoR{e|kE4I(fQyx_Y{KdU|?!dVBhK`g;0#`g;a=26_g0hI)p1hI>YM zMtVkhMq}r{JYzg#J>xtRJQF>WJd-_BJkvbWJu^HrJ+nOjcoIEHo)k|ixdnm$A|lR~ z_&E|Cm{=f57J3#Fw}jwQ&oaWx39j(0^sM%*_iXfR@@(~NBU#!xM77S&|7V3v=R3gjWu)@G3)6l{bobH87fWVxTm{ zX}vnHo+txBBf;P~4&y0o{9x11!yV_LZK-U{A|-e0_xNT)Ke zDxIn3tqxU#xSHNt;M(3gw1-c#rmC*D9#nnQ?=)!yZtQL1ZRTz6Z9y|!ALwmObQ^Cw zaC>hDU`KB!)(_my&fcyh>E`WDxQDl=w--@;y#45GfA0XI26_j1hd>QQ4MPq0j_{81 zj)wXRB_3l4k0m(HJD#S2o``>YCwV6$o#LJ9oyNM;y)(Qsy|ak_$D2qv$(zjjDc)4? zZ05v$=6L6O7b02gUE*EoUFBWvUE^KrUFThoY=d{BcawLscZ+wMce{6occ*ukcaL|k zcb|8^_kj1H_mKCn_lWnX_n7y%_k{PP_mua%_k#D5_p+B*?*s2c?<4PH?-TD+?=$an?+fos?+^?`Q89?^o|PZwB8_ zzKp)izN|i(FU%M2i||GI<=8^8&3Npd?S6Me4~l`%Qwb1);G>K-Zue$ z5^Az>zhxLg}z0;#Y8U! zE~9C1pXJ1_0Io!>@~xKi*Z9`MZ9{GM?eOjL?e^{Q?e*>V9q=9Y9q}FY9rK;=o${Ub zo$;OZo%3DrUG!b@UG`n^UG-h_{p-8#yWzX(yXCv>yW_j-yXU*_d*FNMd*plUd*XZQ zd**xYd*OTKd+mGcd*^%a`{4WN`{euV`{MiR`{v8w|H+@xpUI!upT(cmFY|}_Bm9wm zxnJQ|`c?iYzuF(|kMSG*aek9O-f#9>{G8wFxB2nAGk%BP>F51|Kf&*U$9IR{@6Ybf z;m_&Moe2&A|bw*#1;3K^p_&Ow7;ysqQ8p28eDZYQQKdS z=tlk~{uchW{`US(tcOR^{_f26B&rv|-oQTozO0A$hW-8F29RVR!NCNF5X8N#f0%zb z(IaRw(mx7nH0m$^7~oi%kMocBPayhl|0J4C_D}WCfS&1}<^RW@NYW&KvOfiCHfjz@ z0{?9SIL|+yWeb>+P6YHq)?36>=+t7CFY&J+dX;~Tf1Q7me~W*cf4hGtvR$a%{yo6G zWMZFxzyE;$kpBpANBzgZC;g{@XGnr~h5hIJ=c&HnzerW^%$NLE{8#N*=biU&zzVgv0IAlR3>}o+0o`%jcY$rCua19Ezy}P&W=TK z=C1Sq*m%>PuV8qgU1vM4fB#FTqc-!l*?Qy-bL$ifbom=rsMYp0^HR8O{;P);&3&6e zJkP{@jqI&6;6SVp(&~J7rLD-d_Hp{w?jdK3#bH_>R5>GH%jYGUO>*uIz;+b1T(qaITIy zd}D3Y_!h}6R^B~z{9U8)#u1IRH{P$SV?DfZ{gpxH>o@wD2bzWLJ%OG4!uDAmlAiU% zZ!iekJEFHC@6!EkulRP2HlLn73Z+;+&OF|{W$joI^RzsN-&;tGx4gVtI`Q(nf!^A_ zPK7#eo}M@@ah5qTaRzE+o>6%!bg1%dhIv+^YUeg{ixgWK``)r;s+DO_rs3Y@i7OHp zCoV}eEnIZ5(8X2rHYaXMOunw#nu(pd|Up`mbkoL zlG!pXd5T5vGc5gR4o}{jc!+zMET8ivvCHzx*6z)FH1B^WLy9{2T4Fiw=TJF}vWUlQl^|GpF@xPWk#knxk`ZdW}k|&rs%j)D4=4o85 zW95_Mq38_m|Y9`N9}xqr6T59=l`PRe1)X)#+@FI<{@FnQGSvxO_}$jEhD zHez|511M+>hsQCPkLwmki0JGPI6bS{Pix$CzHcc zv?&dfGN$OQMV8#%KRc=Tl6-q_C4D;bIq|0y&A)|r*)4ysXp?gDP;!#z($l1!N!BDy zO5v2O$@x;kk_&L*DK{;ylX;V8a6hl@$xX*~va*G9e<}3nT6X(pXA-V#U)GCT>6kdz zl=L9!QPOGi$HaEYZ!@^f!w;7|8I`O`ew^52*~aAA*$$rlX3o05aPnH)kyJjZZt9Dq zCzkM(waJd;ZOJcAJ-_la(UKI|K-Zwb)xk+SlIthUSzh>h1h+hSe$qTk`RhHByC;9L z1kRp`oNrgM+j1@WJ}zqSTKPunK@XQK|8u>NbOOJuRqNO%OHXn;Us`@%yL5S!rR^@> zEPfBITdYheloFMaGo@fk)1-Fzzr`iRB^69|B$<+eLy#MaS#0Sam%qd9^6AvXV zN!p&c5dXFU$#atDC*Mt4l(f`RWQq9yu~Ab0#Y=A6^pu=^6GJ!C7E3{&ppN!u?t!q!M)zS$M>0j zZS9|9!pi%&Vm8owk@74hC!Rm%_2%( z^9?WEU7V?mTaqi;nA$wGYPzYC*tbFI#^&L7ho}CTI@VeRJGI7C(~!OX4^{60)>P6x z4%_S6yRIOFDo7I%0TBzRbX1h4RD18e_rCVB3PK2|`yZ#h1$O@{v!3g^T)&iHj6dRk3pFKgOPZWS z)08ECMfSdlDEU~@SVDI-xRz09j?`nCuw)6tQbH|BV#r)m8Eqx4ps(UUUqP<#T;G>? zOkUuy7pnI02G%zD-k$&bW(c+Xc@(iT3^DY94~nc>}yL(KrI9` z*wc3<6hQ4N>4pAYGSaCZdZbfdbmi-3{(%9dWCCqw$~svn`q}JXtX_V6<21Ar+8aFs zEfCB~l_*`%9_T>y;s{NhA9^NgzV}9akRzRpjzYJGSD3@lysl;Ffg#^Ryw|O<-R>Cg zG@&pVJ#nevYE;6q<_m^pxW&(GSq>ZP&WTS3W^C5)m)RZ*H#C zd=-5M`<_^gz5{4Q=q4f^eGbKAwh^^NiJz2s1+6EZ`=72;z`{keyAvIQM*maS*iMmh zasqPEHV&GNc6M?t{A)2vJlJVCi3?&9ThR8%M0?8plQyH5cExmkMQ@r=l-LUurRX9dDiD)H8~* zzvbIXR7(%0acuFW+dL_=NnzhoC{L3>gz58HOBLCwHq-!*L{0z(@Aj#5SA1D_h5I5* zDKeI%cS==XArU`$`WN?E%c5$OB-m|f=}Z90 zr4wWcrLm>sN>2qfHG!DB(s`vX&~r;eN}WnWF>RQ!7!0Ognc1x(v{tGi_<1(M|rGV839>KuefytlyqNmq&eD6pbm#QfhjFrjd@hs3wXM*`-J;a zbzg8s=4RP$mr;Uixd&gT1C3=E>{_oEzq`h}uW?sJ_u=JWW??>+t;2YG%!hViu3*+; zsAXvm`)5l-v^gLysL(^^d5PT14PTm3dOzbWw5k*(E0iB9LzT(gdUW`^*(08MTKYGJ zU*?B-RyMBeVOe3Bb16ytku(G|9eR!UyG{HbmmOdJM`9^vDMkh^F1LHrLto+Ovv+R) z*6WsoWfMxXN;j5nGUrsx#<-R>Vzy%xr2)`aR3ay;thVfkTLiSdEVXof z**_jreVtmCW2Tfbflr-o^Py>F2i(0dKhZLbyc8*0R=THbA2}K7>sC>gidpHts%!&h zA|@K+?B=4~=KcV42lKeJqjWQ*FIAQ%W3r+5WtviwJF6@!Ejw+w$*XiZ=|O;1>$%MIEsQy3Yl}|Mo ztH(soH$sae{Wy0>cHxeN9}hnnUR>jEJL@~$w%wEOW$^N`VTr!B%h&)1-)9&5IKWidA4djp z0Igm+uO&%|5(|#w`3#rmb=OAos<1us8r0qg*B8g3T){=Hn{PvE-k`q8l01PX?0M`& zr)h;H*b-a=)`&CVw7A{a<=FXNYFuv1VcSFW0Gz**?6$U*wg3=!L#yn0`h^~s$Z~84b{Vd~ON^U}YxWYG-Mr4& zDzW$4)>DVi$eMAd?KCYPTY$~L-o?I<+Xqpx#uVV=RbiG%{w-q)kNM7ZJ6#+eq^9o!cj^h>6l;b(5{T}X+ zXED~UI~JFY8wdOu=S9S2dNTg>ZMJ8HEz^_yr$oY@H$1>$usTn%ITM@YX~0piFzz5u z<5_^KZN7~1#NPCrkDG%%hqcQv98V|7laD)tJ&QG?j<;rEg}D1D@M~pW?a9H$;JUp& z;$8#TCH;z9iL&ZA;WeDsUfsCQUf*z^0Q|tc1^H*(@#)#pFSuUbzbk%v{jL~-x4ERM z5^O!Ae(n=B>JrokkM|##eoiw6KLS5rwH*~for0f?rxZ@ZJL6H_m?~EQlZtk^*vHut z^a+md?1bt!H8Y+nIOnyI_)YjZU<5D|wGp3;@8G{-ZNnR0FTkH);BDFfA_Ee+gGm`J z>%HlfYXPiuQ0(0E@nQItAci238;7TP@ci|ssgZqsqVI&wF1je^J}Ne6G{I*T2gH zd}4f#RSc~Nt@!ShEj{nOu3U-_Dz7Q;TT$WNtKwgLdHDsrXW&-pRG+izpbD3Y=@sSp z*Z4^lc>L|aMZ933KY04L^4;YX-lX!%@&V;D%J-HJEPq=rn0{8>Q9g0{!iw1y9@7=& z`@CuRJ=5*Bq~&fE`^q~g6Q{dY?3o?_D1*wES1ha8TwyIc;Inbo*;(giU0@uqNUvD; zztYi)BM#n}icJ+FA0^n6)A@Ay__y3E&RMW`Wvl8SABR23zbJj|@~Gmm%Rd!bpZ7kW zd|+;okFBB*z{d)f&$Eg|0+m9nsHnJ-Sz|7FQVb}(3XDr@1?2OxLY}42wVF$dIe=1A zk+ObS7kgd8`o#4~>*-s^Z!36>D?a&u?A8)b`iTDLg&kM^@#yg%%e6pf553djsik7Q zwlbE<*8_iw{#T;|Z7B9(fjJt!xYh@%cl@iL`cFCr;CpeC36ls*oO;%gCV&`B zrxtPJG~dIkiJK2^dGylxf0%vu4@Gt*fPFx+2f>~2(9_>*2Em8mO^AA7$2|$}T>hJ* zy>kirGh4G`!H#)b4;BJua0!G;=ZP+69?SWMzJRctfE33PVhG3<=hvYGd_|`365>Ka z5=gfY@(8(v1%wTRb%Zs9n#ym*_ndlGnU?4lcuu#4>CRV9sQf@kuKeVCdB&9))ic(u zd%TWhyHLDr#&WOIof$pM;XiCzY<19hJ`rPYG`b z-Guvuv>8=1c$NSD|MZv%_bP`~5-SH)4y;T>b<6u!{_wPaU*AICBgmX4Rmz+uRw9I2 z!s!`jDkXx41ibHZU=Q!R45as*PWsx{!#?`j_1*@O!-3?<$}y4Ub-OFKSFWy{0sij# z(h2{9U9kV>yS;Sze=W(V>{AI7tb}F)m(Z`$==f#fW7m4G$A9!qbNJiS@}72A5{?sS z1hD5&X|JuStOp)}KB%-2S}I#A@xDTU+f!Agz7ncT$66~bmDju8RCZTxL$6f-tlW>< zMf+AMTK}|bDvEF1fy(zQATB9#C;AX4i53!P62})^Rxc4n3U3Os{nU*9M1Q{!Via)@ zk*8Sfm*KaW=mE;h{11d*;B6s}1SNlB8gUizbg?I~+~u@n1kumImE@aA@DfK5!$Em5 zkop_w(lB}hTfP1A9kdK$An};fK|f42zum)WuX7)89(+Go@!3Zl|6~`SE-5q>pY~(; zS&8RdKPtYakBEX43*AVe2DF6u(hrW+OyylvT=m&oF#8W6T z`UbJcj|()NCO#)VBmP4?i#iE>I15gZo%DM{d~URlx?XzpjMWx8efv}X?ijmWMy!jj zrWa>pvvJw@Y?5(DOLNtbnX_nV_+Euq3I{tiqKR3wtN{IJ(kRl9(pjW<|A2;tqM4*b z(sa@^QWz=He+g+eDS{MEngcMOk~#j7BzKZODTS0w3h=Y{`$3O=Evcjx{{8ZUtnUOF z{)=nU0c82F_FqHF{F7!ma6lvFkh1+Tigl#5q+KM2NNM}&bcL~jWGVdZMDVvhuO?+W ze|Ub{|D}=aPxoh#PLOW+zt0|LLYnp$Z7LdGnr%DdD2ts6Nix?bB(c*ikl!I;+MTDh zE7v|5=6z1{+v)1G{iH`vPLi6Ru>AJ~(8wo&rj!1363tOMOll@^NlZ#(;fKI)lq=d} z|Og@Ne*M&N2vRY3Vd*-fc%*w{mUL+Vfdj5NC}Czk|dJip|2 z#jT|Kr!$JP*TG#*4ksT6wti63DDNnjD1TGZ12ZTODfyHm0eJFJva9+qd5Gq2z+LU& zm}R^Z0dpvu$bn5S$qAH`lv2t~O8xY$(jSxoz=@0l&9oZ0U4B60qK+j0cR=Qav$?)O3Z8XTRnugn-q z&QnO_zU1->9%&sGF4+y9sE#y`tf2G?_)M`q;0N2DI59){@f9mX@P-=(+W>e3gBdOa_6Y=5HcxoK=w%9b&PecuF zqc>5#uoE%}Py%fO)n@*TZAax$%|RdNn`x(MS{jD_i(bQ6;?~qOwxohCpem@H)Mr7h z^nLW*^fR;&`hF-7XQms1_AJnAQmG31L;Ane-&FeSZ!9nRBdUt3q&}wVsX8iy`jWnF zrk1`jsNc*F^bw2(S|fcd;|;wm_zU(84d3@-*s-K>lsm0X4X((DBTB}(`AA){GYo@2r-_g58qivz)(GsatnmbJ>9Ur_Hgoe<-rSos;+4Q;M`7`Zy&#VHv?C_D0nJk0f2stZ^2tM-)4@dcY{N9bXpCq554D8`jI}F z_JmqQWz)X9@af|jYiJ&{8}xtZ(`Ym3ZB$p9h+ai|8gz)aotm1kFF`MUNKFZ9qyMH2 zV0@d1WwaJ*81Tguw2k(i?nWa} z^J%_}NT*3N2X`2$OXywH-gFjiV~{zhA8jL@6}(jwLpRW?=tCGgs40cfbXNw0?#%c} zw@_UfHtHFAFFKnZ7ObQ%2wp@V#OOb3G-C=amLBBrq?#Vw^m8UcpT!7fh|zteo^dPV z*2JL&@0=e_t1h`#w3nelr7_YO&0vL7R?>U5yIzAzU?eh5&C2AcpRDsa;L|gzJ-~=( zv@>=vcpKX`MsM2rd^IN%aA-Joj*X6SZx41BjDY{c&M_W4_3X4gcDm@;al1AvuKTCc z77|-?8DMu9g|kXKZ!y$s-8Xw|p1yg;i`$@_ZYD7B3>JgIpfW1Kp5Aq*z0?lIenuN3 zFg{86w_i3f*AD`}%r1>zFhe@*D{r6BUuP{~7P!m{F){BmKLPv%)16ts3}-GzeP?XA+A0Fjt3sVLoN1ZrJ|x^DGmP{K{CwT*!=LE@i%C zo(yScHZw0UFETeWBLH6xGn@IFVPs}7A2Dx+Ol!Hv{Ki-gcq*8ilAah-nJbxlm@DV6 zYCIhBK4Vf=>)I=}FN|%>=$6V5QAio@99S9UF;&b)<|$^Wz3f!kies)|o-`1ISJ{qbvm1O*bvU~Dqrln*^}m+W8+Nn>c>@mtIicIWyMyPS8ogD@@_o& zyO8boXJisO-~|t1`yKl;PT5;MAXreHQ|;pfc8uo)R6}b%t=Vdn%loreS2?jetDdv} zs=8mLsaN)uDhs&;Fe`ifW z{bDV__YOT2x{ckx>NiUoL}wwai|i?=XRNQR+g0yp+iMDDE7-(2`SOpfN$l*ZfCypJ)e#CxX-J3m=y?r*9*O&c% zc5h&RKz$5rRM-UJ+v?qOWYs5QPVstGVOhDXO4fhv>14Tjqwp~^Z-|KOPgQXB-*ZaX zPMp3ZK`14xA?#!u|4$xLjbou%L#u|c2Zd%={mnlrLV@F}P2 z!{YO88P!W!4WT7$J-e&=Q}qbWJa!)YCTp8QS(TyKzzXvAWmT~9*h<#K>g%j?)$6L) zR_9eMYWPIl_UUQjijL#@uOe#zFdzl8PYEN|N7caRuK-^l70nlC?F z9m~FNE~wH}dnMnro?(@<&W7%;&S8C>vxB`H6<@VBG>$zx6vb|>E@E@Sj*Ev>(b!$= z`l@Nwy{peuZK!gsR#b1SN_IM0eXy#Kg;dGeCqpL)8`(#y2UeA^W{0Y)7tS8S*&HTj zqr>t#vO>SJH&sn>vY_k{3&FFmvqx2}t6D7asSXHD6699VtJj8hR)?`>%t@`D4rJ`T zf2c}Pjj-im`O7wS7}$9BPxeyQPgd~kQ1)zLe%1Z3395VS^;Mgzwbc!we^>jnPqK%H z8re1A-J`Jhp@L9*?;ER4EU4OEqJYw9_SY&M>wR&6bb8ok_Q=5nvO)vdr`aPyCvYxS+r6J1)}&lG+neoHtz=L2jI26S zJtDLpr!gmBbKr}n&~NOpqzK8+z~$k&;Txa-bT$OIYO^`XoMV(oP8tUT&*J*RyBsa<1nw#~S7gyd66ZRx?XE_c?_eBIh!v zf^(BY;+*F^zl*BWBh3qtg8|{55s1^-xVlL~c#G12Ss1 z*X^mbUjW>vi={Bj3*ZSdXqhHSf&4F%7rt7uGfvb6r;9j@13C8CpVa5fD36?%6{YK4Z|50OH?-kh9lBOhjH@ZGt*DEu9atBT6KhJT zl@%|ky;56H^S<_MZF6mYLrtWz-W>V6PFo*evZMZ6{gC=m^*R;3sG-S=S_hMQDroKF)pU_Y@p{@W0b}btYM=YyNuGQ9^h&0!Y zts~VQsXAYGu`actv}S2dQLR0G=wuTflHv~1j0sdZz zJjClg*S{8DlQr*seI@`*?TXsdbsi0!bx&&T{WUc5WPMM5qJI5c`!ifW_Y_DM*FLU& zRJXAfTT81iKz)wv(-7GJZrmy!RhM5|67jRu6yOZMP zk?SATqialcJDrs^(e=j26%DH!M%Nl66C1|VKdVb>I96w?GuE}#Ep@rq(AGF){gm}f zy121z{9jQ8`eRY5hNA#%jawS+pHx)M$OYbcn_v+NLSJvV*09uNTf?%(dyO+fgdsg+ zm>_UXreFKphB*?$>oBLFrls?J^JA@bjl-Hc=TBMS*7TuaOJjWD(Wqk$uNu=EH#SDh z?`UkCKcVSEqo(mkBc+klz-=gLJPd5JEC!dJcnzwhs4!rDbK}}ZUE{At;e2_ci7~S= zuW>A(c{FxKbu?yLYohFSwlzL#Ft`kIM0wn0-enpaX3QTaAJ-@hDUGs^uxt${qWBFd zjmPGnZQSF4V~uwHR1g*Ny>Y#q8F}0K*r5SfZv5Hss^QvGpL%vUV;&JMMX4Do-b^C^QtMz-nthJ;~Sao?uz3LjU1@KZgoAd`mEu4S?o&iP;>2hu4`7YnCQ!kpL*K8&!Y*-hf2(Y|0^l;B%zWP$+9i-aX{u}@G)d8ibceGp>l&K&bLVqIxXh*{Zl|a}n~v$cx!brqxEFQj zNgKFdo1(d!xRbbxxZ&Jk3$N;4HMKQO21^NCbQMP2eAczUsw1#otwrz>T-Y^ z#C_S+-PCvCN$w%8zRA!ui_4M^U+BzbH63-y=WgZtamR80)wystb0g9o=^o%S(dcGI z^Rnhm&60(CnqNrbn#s+-xVxGcH`g`~UX-(_q*=@radDt7tXa|=yl76deRUeFt5?jx zRy1E*q-dVke7lXzeaYG0ELdpgKfS1}xw!da^QcAn%_9LQZRUk@n#~J?o26WS^E)n$ zOXNP}lG+3d2LtYri$*o?1N_zUf4Tjeh0PRTOTsN^#xz$o4+EO&n@2RiU8ratzGwr` z4?JqV!QJ2dhD+i;;2vm}da=3Z0pICnZF5BPj%FEG3%IW}w=e2w?oA4njBEL;rFV3s zWI)TOMUj%v&E3sMyzDz-ubUSn#Y^lf;Zq5Fm{=!N3)acCB|cWNY@q7D^?I+7?o*!>e;4&VmrMTsJ%Z)N8UN~<8kJu8;d)89alFi%68^Eh<3Fl3W3FB?!ZROo- z`854D#f|s0AUT+~BZez`(z1~^o%f-oqh$;4ZcA$mp=Awk7cZYTj~B@^$l9V) z0gn%FGjA_%4^M`A8dMf+|K&)TcO+(A?!8=!S@%SSI>fsgdJ4cv-W8pF zC|aPq%zMb|aJj)N<`wcvLn@ihOiFY?7n@fVQ_UOGO$8}6riw=acpGo~>}^#dUL$XA zQeGYdzSq{uGw?Qg9er($IRfRa5GZs!m^V$>$&>Kr5@dj;=H;Ptm;0-JJE5K|Xst(~ z#P?h8rt>t36z5i_R*zQqR{vIctIy)|tvRhJP7|DC)T#rY3l|@bLK7aSh_P0%FBc++iKcc+pe`8@fsej zZQIbcy{)S48oInq)kci6|6)vU4{Y~o|GoG>{^9LeZGkL&TS42;Hd$N0_W5lq+a|P) zZs&QjmvGu9x0%`wY&*Q|$m^c*djosIoDZ#&+Q+qh=Gyl*vMOz!z2*pcU2Rs!4#@IK zTvuwFXIrnf^yb=`K86E8^6$27K-0D*?6%(ETGr8R>1}sx=h40n568FdZF|)AZ`-F< zY2%01=GUv*u(cU&8`m9LS8mJke1wZ?+thXycpkLGuf4JD_u_d=dRiIQ7TNZz)lPSA z4{r-^4{Im3)7$aw#qD?TciPqM9d|F~NKu*cng{E5=R zu{7an;dAthr>X4;{J_{v{2BZ_ehxn}c3x~_>U-sI{+igWd|ta^DNWd)&yKHDhQ%)B zzpXma9?$>ME^2?XbQ3?EpA@^1KN--2_`Uzg^^T2n$jjqr>sIm4$D4$cW5f6!(J04v zPj`FphPzK^0x7{#;nK(5x%>tE0l?0$_7wil_8t5=8^Sis-Ea$VB=O%Y4dk=r17g?k z^ZCSB9{*fLpzTrxpHJhr@ELqkESaCUAa#NL_syQ)0(mnZ&u8;n`H%S#wE+f`50k$i z$lU;(H)2WrOZ;nmlTRhT><|Av{=TR-{v-ZXz(wR^K#9vg&0mav9-FB1DZ0A-X19kE zh;%1@iv7Yj7S&`7(WyX<0hA1}pZL!Oupl9hEZFTrEsYVZ7hDxwa=GHNcvjDf+47?1 zxV~~rYutj%f>&`l!i_?zV5~r)844rc7OWObCZ-F|l-RWZn}S~PAxeQD zOSnzwB=iwNf}wE6qI?#&RahSJvMx;^7mSUUDa?XVAl)Q989IvXBP}*~5vmQCDuu)hpjE+4oOngQJQAbwHW8og*BjHr1ukpJQio{>y zi^RW#P0kY&;}SQEi(G#Tzr{ZR`43^a7%j$#OT@+EGBHkEA+8qF#I@o&F-c4o*N7?N z%LzpBP`@fMR?HGtiW3s(Vz!ti@)PkzB?(`|i9zn7pv0?*j}tv5JaLn_QQRWth(+QC zu|O;p3&m|>4^p%Eo`|1#v}+s@FTRx^5lx0t6D>ND*d@M__$tw?`yn1CktO^V`vRZ8iwpi(_$2-y{wn?@rixP} z3ll$!m8f2ljU<$aDgGvYE&eIaOnf7LA^s?SC+;or6m^TuqVRy6VEbOyeDM>YO_lIm z{8oHg+yU~wl75mtl7fT^hwmT7gC)ZyVN56YS5SRO}(ypHTa3oWxxc9?&;9Cm5{ki=K(4 zB3i;{@s30v$#ls&@wLReiKXK2@e_gG+(fI`U(zb}li1I3e3#6S)QXhi1V9}sIVD~q z?gxC@o1k`Z$B7RlcrG8U4oGV3nkk7+^hpYm1WUx?2MJwbH%XubsxA;KMd+u#lKY9X zBr6l};)eqL2?-J)EP{hiwIM+G@?$CTJ%!%Nc>gwPNWz8NEj#16Wc_EB7^ADpSIMC--?Ec zpNUMO+2W@nv&fpzTRd1iMeHn|D)tdi5Ic#lC*Bm#5DyWL5+4=M5zhi|L{Q>m@h!2R zI7S?uxI=tFoFPsXFB5M|Z0tHF-XY#D-VOTyzBoK7IcYC^0R6O?+%PRXLJ}gG79J)M z#MsAPpBD9Q86O=lnal2p6w`SnO2$Voko;F#A_)Pq)P!Y8%OxoeSSH!w-tKW$vLoq$ zWWVHe$P;4*z|KjoOtYUOIU&iDWF+m7?2}wbx}+(0SwLJR*#q#4>6bJolg>Nx3mq3a z?n!P){*hdflu2-sQVCj8Bq^3GEj;Wp#@>0SB zt-;J{wgQNqcL#0Ecu`SYe3acODCv8{yO55h#w0cTi zr9$>@>00taX{8}Pd4F=VVV^WYdQ2MSzz<3{ByX3ZJn`7?$ak;fhYrq;GJcQeU^D(v8vuKwmu2odC*vq;b-JAvo`r;~w}C&{;Dc-d)L!1K1}|HzKYPRS&)C&>q7 zX|hu3Cuz1!`SP$V;aT%0`Ez;_Gl`d6E8`_|WW!`*WCB@g%nn{Gun;P1kd2hxNKrl? zkz$m^rnJdkNuNtCvhBPPDRS9ZnNGGI9bEWE_Dc3$^aiyJJI^@0a8I#)|FDbR#n>=& zc*n4gF&!g2CM)E@$)RTdbpE>G1fhm~|I7B~$mS*Q4<#L5RMY7|`^LJyDV_}GCf$EZ@| zmJV}=xFhNbHFc=!mi&!;R$Q~-pi7YauH04Sp^U@jHLj4`f9=+aLKO#HhR9>(cjPhh zB4>+SC;t?wgHCD-x55y&+hR#S<{_Gf_Wcb-*d7>4< z4vKu5;;kZQIa}FFHBV*lk*a0(@%vv2(aMrern|9mka(*SuN+u-JoQKFNY!dNT^=CM zP=1wvLNh>2&@07Oc~q-RKBxMYQWYamY?HTkTvBG^f~&X7-IWFMaY~*bMwzEXDV>ym z(`%N0P|)Otz&nx9yw=vcbI;0iNHdjH9iom+@)<-`OrBy?{8*t#c}W?j+OI@gqn_Js z#44!r#*Sf%n&r0?Jsvs(53}S~y}#h!$`8w9)mbQo{FLH~;<93=Vw8Ndyha(W#3?}F zD@vlo#Pt&1RywJ^DT9<>Q#U~yf~T7-_fn8khpDzNN0bxz zE8?FkKPxLh`RlTws+{HH6#h#4%w%5ddu7kA%V3W4G_=2)RIk`5_d?B5PX5#9VAiCV zyD~+yne*>i|a{f$w3;z!k@VKa&-)sd36(N=%3S{gf;lquP`D zLHTtV71$;#*KR6%Zs+@^GxSR^;^}r&c|6(^)u82)v3wMYi0_elkCse-Naq45LovL}S3LoX%0|#ek!}b7<## z)m_!K&a1#b&$Kz}Q>sfUw-qAQ?OBD4Zk4$6Nau;pOR6`j)=pmMl1^NwmwLE*n`&w2 zJJ2?X%0)dweRAdf&eN*JtB!XbSBY1m)QO!PonJdYsE&0WNZ6X_DSxcW1zyB;e(C&7 z7Oxzrez0neTGH94ldrn7GI~`QD0`~yScv+y>h!`#x<%>*>PEADRU@*Ml8S%6#_~05eNBqt>YRdf3lMP>VOCG69|i@B($dTBi=NZr2p5pQ9P-l^VhH zsXl!5M9pa8ru4S;aMw1qJe(@srTO|gNwYcuRv%Rh)p~V~<}b}zHA`(y6PiuxwdxkN zMlAvOOwDQyJuOJHSTj0(p=KbQGI*=9L;& zThs-bDH=whEp4<0QBTz@V3E{&G-uRSHBTL^d8Ce^f2MCySE^Ifk81X43Q%j&!@TjN z(5}#`^GWs*gni5aMh}`sO>Yuc)2w-z&Q5RF+)i)N+yXFsF;CN~`Nu&MXha&ZrZ&Az zbFh_K$k#|TER9stklv)J(U8++nya(^Nq-1rnHntM5o&0l<}yedG}koOfwtQ~+g;6l zP+JQ$0y`RlrdU(2DbrvyBn=g4ixYIGf6_3hA2eN>FB+?+6O;@f?bg^dKhu9}zB=Hi zrgz2@s26||2n`iOBnS)PAPhu-Dxq?y0!j-aKxC*+TMaRx8VFw8)#~iF5`*G2KrDy@ z)kEf@I;aZTo+*Rcp_gu4hz~ujS(@1hZP7MC>)iya0evXF+}}d4p;wR=dI7zJx}i7FN9Y|y$oK%212~cS9SSV#t@{NH)cu5> z#SYO;*7nu)(+$uK(*1@$LoYH$=wRq8^aJXzV?%v(y>!ELPP(bO9~t9ylQZ3QPob-s zV|7z>BXwDj5b*wY&MDhngN)Nn)X|`OA(M1}>1s1^PrpEubtWM94H~K&tsA4eq!mKL zb@80Pb)LFWIv1Ur&O>L;aMw{GSDi~HN;eIFvo5CWWaf08ukKi;kM3(39rDs0aT|s4 z)^)mhVWM=y5c#ywthHfRmo@E5;Hen)CzbbP*X6YI^J2Ujq%nTVs zgwB-R%EUv#x>D+MNETz|UDVD1nkzHDLq5RH!^}1M!8$c+Fg{yL_o*?%8Dn*=PXA<@ zY}Wux(Cq@U0lE;~X}~v4H&fRuqqlaDwvV>2_Nq1s8mawDJ6yXf^AG(kH? zy8{}cov0nHodUhp&V&-RQ?>5eK<$)_cu1x7);eoHXW*XB&`#H)v=JFgq50YnZK^g? zn+C1YCToMWVcHmNe`t+1O}j$7QadwaJG4xDPJ2eXPJ3LtQM*HXMEgX0R=ZcbGh-Wc zN4rJ4O}kZ_r_I-jw7ayswVSmTZLanJRIEj73$+img#o47GHoyS`&yj#hPFI|pv7o! zY6aRaT8Z|Twld=cB-2)FsoHieq$O)Rwe{L2ZHKm5%ha}N&w{t3M%xSeq}6GMfM@+e z+oc@~^@jRDaK=^WmDZH;Lwgf?t-TlWUi(dJ(+-CWTD{f_dIXJ!rb5#)U7!h&Ib$+3 z2^tB_fg(X~pM+*)9)o7HWC&w)hU8>mU`~as|`;8A>yO-HVfA+DTV-%>%(530l>n;>d=kC*O(@~q~O}BOJ zO&;9^It##0^S&$?#vQ`-=lXDu=#sd{xThCJsDG}Us&~?-fg9te>7DgcgIx74dLO-~ z-djIi@2^Mc@3=nC^3sp=x5pOy>1XPr08Rw>EWNLON8xOJh<=VfSRbwr)raYW^b7R! z_4D-6`bGLk{XtX#IYNJpH)i!hJ)9M&U!srI*ZC-Pbv|<4Vt_BxFVzoOy+)s`Ptvc@ zgV|`}a{cJlZu*s|cQ`TNkJG0D>y^NIg5Efxu0Bnltlf;Ou6(R3)IHJJ z+gzk8(M=61))91;tkFPMZ~aRhPG>)xjMtUxusWvhhmNGH(AnEe(}}ZSU89bytI^SQ zY+aSEUdPcj=xTL*T|a%Bu31Obadk~PQP!B%f9qRyExM6N>JiI-Bm5?nTz; zti`po%sP{O^xA^L&(LHc3( z5&9|m!%@E)w&*wMcj|ZOAL#Gs?RTs7oUSw42KMu!$*CA z;R672+bzQl1A#xpz|kie4z4LNH0u``iVgXOFZ!>>Wd^S`(+!u_+%d#~8p7(&`ZB{O z{Tah)Llv${|JRxwpyvB(qK&kgt1mbB8w`41!;UqV4CRKedW<0jNF6jBH#{<2U-Q5~ zGt8AX7?wNt+s++h5-c$0CXakKHkYfj8n4D z8AFVdvdz{^R-vB1wMMi1bbw{fb`-RNZWH97;Y!i+PG;YNRBgfY~Z zYm7E7GDaF>j0wgS#=`8<>`LQfM`}S%hisDc_P3uQc${4b=!GS{{c*4bw$$BZUY%`N==6iQ(bR23j0a3v zre&rvIcrR#0ob|U8#PA5|4u=Ga}maWOe^rAk+G%?rI4{Mdj+s{-L&b+c9SuCmkHy% z_ep4EfJtg>H4Zn~M~&I)y6ib7z44G~t!W6*I>w~S4l|MJ(oKC#^MOpXNhxkL4mPba z)f(H3YU49wbv8TO-b&C~Q@F{)G}Y#54l`GnQdk(%So25IXmhZ+3S~dlE;LOIx5tXr znmTd}CZj3DJi{yo*Z`1rm?Wkp=JDoFCZg%7Dc7mz=A0n29N?dG?6>IH^T8mmHW3^& zz+vt(-7?8cRVYpl+V;i7Hc3q}=EtVRX8S1;h`881+Z>U*z-%+23U8Y)nu{2Ba|_Lv z&4*&_`+ntZndSf9331!?hxs0S6}|&s12S5m=l@^(#!P}M;QMeTTn1<6wZISI zC-7sqAP*0h!f5ygTnH24!aOW&H5b7TU;_LvTn@9X7o#@8p#@@iL!JeMW2LNkqc!^aIGdGC= z4+j>hupiJ@1J}a#y+n_t_8rsR`6&g<3bw!!_zm0vzl5K|pWyef4%Wg}_$j=o#|mtL zU%?9tUcj|^HrPJjNmDynWbkMBEo_88!U(K{yJ0Epnoog`!3KCpBwz37|F?- z>EQCU_D7IALKH zvJzQ<{LCMOL|QYDF1P^Mgdm`V$xlKw@CP^_`3kQ?a*+{85xf~0k*|PH7UUrt9R2(> z&;I@l2X%{p%`5pkknPCUKQ%6(W*g$0Cx#(-C-N}w1^hnm3!G!!f~){7-<@xNXY6?j z$je!~5KrW20q1|u6|xN8nEh-I(hToLyh_$07o)r_J>1LS^@!0@2Jc1oAt6c0l2~LT zf(igsJ34_mC8Z8tH#)7q`S?^kIScDe5n#i3dff6bZy|ixa^Eu9T46b0 zDYNuEW25!7WhjiZoUouR@ADSHqaB}#=N646)cV%aW?hrtX=PdNTHja@i%T+WiL-WF zo?5E&ko=$48jIf2VQH~^vUtD~3OvE*=#_Y zTgO?wtv*&i>k)Wj{zL0XtA{leo^KswU6{Y!I?(EDoo3x@{bHGEon&2WU1D8qRa^I2 zS6BMXKR+*J+ZM90RW8tXvVh2Hpwpg1elOT599=L3OS9O zLoNW1FCs~0D&+dwCrBIc_9k)-xrN+Dt{_bEedGb~8HcQqA_yL-MC3>rf<}sw3dHXP zWi1sUAR?p=S?5}fFcI$;AS4&zAT@{?5hC=pT*L%CqEt~mGH&Ng%1QCp~OF=~x%f#+F5lIM183N{LRlo65NtwXJEKG-rSJU-jhlwi{NYS7F3Y zBJmfI@|onj0pW@z|BD&fk%y$WbMM(W<=G#ZkWUw9`>jl0=&zQ3An9QG{%kprekm98 z6Y%~uSJGdl2f7PFZ>&EDkEK6Nf0ACY*!StF`3h9-6&im3>Ka4%M$*1e#bUS8ucgmO zJDX+;@7^}O)yyYHE*(!d9G$hiVumK8LWaJpJEO=s_**99eYk&lcs};{(U0lBB>k4; z&gfrWM7TY}mH}L!Q8{D6Be;g-NJ*)TOSujw{qb3S zV0C`~mV}2n?HH7xA3N}U1xi9 z6K#sOZq51Lxn4c-ORgDtx+uA3iR`D0W_jPYZu%5hN%G85$iKkbnbQHoBYM1Ig!;FDBpe-uC{qChzJy-tEA>cfB{f_r3SL54>lS zA9~La*`9nL`2z8L8??H`JpUBtU_cpnQ@2BK6U#2%q zw0^#2zCOO*zFxk*z9GIrK5NP_U+d28QU>~l+BVkWEeV4CZxZ2g%g4o=?NxlibuDAH1); zFOwViUV96rl<@hyrG0*Hz#H;H_HVsr-*4VuyuZpjxuePtF z@1u8xZ@I6#Kh3{m)!CFD{x<$z{+|9m{-W1AcI)Tw>)%Cet-6i$4=ED)eC3cL=d(X& zIn+PgKiJ>jKf*uCKgd78|14#g|0$7y{;Agf^~U%o`X`Xu-}%S;^LA_CpYEUGpW>h9 zpW~nHpXHzFS9ELEZK}Umx5fU1Y;B2ufq#*|p?|r5q5nflgKo?GBW?}x%SrhPe>MLq zfBM~}{`vlu{?#N8_@rlVe@Xv1|2+R_{~F?-m-u`9V@WIpiF$eGxs;Lq-M+Q{6Dj@t z8~l={>-_8e_9K)1ll)tK+kCrH&ieNH_V^-^s`wB2PNjr>CsI24@A{tjp8Fp8Zu^S* zZ~C65JoDZ1-SeGGY2|j$zO8&C`%Km!(b|kYd{v>~A|Mu)xe9pJM z>t7i>*x-);hJVzp`~G|WbAIWqgALC5;q@T$I+6cIgRA~4{@%&sl2zeH{)hf7^S~XS z{h5AVsy%QkG3+nvy_M+qzxKDXjNI{~ztU=7Hw7sfap1*G@EGb%4EVb7QWXJ1U_p_G zT_T@s1VN%gR_GRgAY;%bYC8v7n~nFkoqYwH@GA?i+rmJe8UQS zzsj88=HLcmUlvRXJPd3It_*UzZwjsrUP;{;yd2mXJQCay+!kDxIv{vFFp0FWH@GXf zFSt9nCwMTpJ-9QtKX^D;l0-O=dNMd8uqAjocp-ID@M3U5@O04A{aElqFuwb_U>uR- z!GYb+2G0l21oL)36}%n16}%E$7wj8o+xrlL*wS=Xpj~iUU}j)jU~yniU~^!3V10lWTpDN}Obc!aM8=N1Y8pHofN^OaJl^e8 zU`Vh)ute}uATd}hcrtJ~a3Pfw+#mQga6526;1Apj1OhJtKL#=aPXjLlKar7q67U8} z2Hywn2HFG#L1{2K_$gor@`DPJ^*}OS@j-b|7~}>k2D1XzU_mk_d4e9&*Bn7h&=@Qf zEE` zj_{AG62tcJh4dU@53vP;1JYdK2Wif5{&10Sp|G(>!EoMi<+K9foZ)=oJmJHQc7%$D zON7saN`=dXe+iWdmkgHbo27=D%n*ITv1Ey7L0&B9H?BSNjht-~!s6G*w6w3R#5E=;e4 z>UD*0Gpt?fwD+Ob zp$?%wp*f*$p%I~;q3)p`p-G|1p@E@5XklnoXmRLBXnyEK+R4zm(5BGk(CN_b(4o-b z(5cYT&>y2jzWFJ9Kco-e2|W&FhSEcCLqCUp3B`s#hvLH0upz7^-|jhZst*ev zbWDFSB0TUSutDJz&&~5g!sEhg!{3ExgeQc{n0s!ne`Z1LvElXMb>Xc&4u;Ry?$|Z* z3@&J!v!BM@MC{RP-WJ_j8$P2BxPxG%d<%J6cT+AH_)+*__)WNWjzcFuhC|_&t3HL_hJOq{3H!ssj2B^F zc>BZO!oP;oQpK5ZnUC%l?0%KZ{`=jRXU1n{g?X96Oj)KRbJeQ@Rga$Ex@uI{Ng1|G zYv!2mOJo+#Jhu+Mmtb;{`k7OTOyBpZb)BbgyEe#-*lK3J?OHFhXQ`2yjWZkd9F;jb zb4=#A%wCxjGQZ0lpV_hJ*vt+@epxj!6TT7g&ibuud-t52IVrPcX1B~KnQ6DClDZ8^ z-6omMGTRZ~j+reo+h?}UOv>y|Jo;n~%>2IQ?4FA}tSliXo+IVRIZBR}qvx17 zHcle3x;Z&Hc{v3*g*inz#W^K8r8#9e6*!eRRXMde4LQv@EjevC?Kzz|Nt_f;DyIjh z7pE_00B0~~7-uAB3}+l?B4;vZ8fPYF4re}R5oal9IcFtj4QD-P6K5-D2WK~DALk(F z2CjprtCu<_H#aXgKer&aFt;eTIJYFXG`B3bJhvjZ zGPf$XI=3dbHn$$PA-6HNDYrSdCAT%VEw=-=GdGEw%uVI?;P&G7;r8PW;11#r;SS@D z;Ev{wHqmoAFztS9^YEelousVQKu{{Qd+7@`vz;@kj7S@yGDT^2hTh@+b4B68m)i%1^UvKje3BmOF)ttGOa$Y!)|@)2QZAEu-2*b%^R5l@yf{l@`?} zs(;kLs3B1!qDDoHi5eUAM;m{xjgOiXH6?0#)XabC`RDcjUF%%Z?gAo<8SO96)>lTY zj#^8!^^6kQ+QxsdZI0STY`dcNGD;3I^ur9DM)_CpD8@Mvb^7;+=b|qDuKyA9%AeT& zD5cu9s9S%_FXDeM>QU75sMk?}s5eocqJE9yM+>7R(TZqov?VT9EyhX7T?WE{$Fpy*7Gd^w#K| z(R-s0Mjwqn8GSDLa`cVpyU~xLpGCil_C|-I-$s9m{t}%P&5wzV5ywbl6fv3@LyR@X z8Iv<6Urgbc5;0|CD#cWfsT0#Mrb$fmm{u`uV>-lij_DfHEha6dcTE47!7;;QM#qec znG`cEW>(C+m_;$mVphehi`f*jEoN8DzL-NX$6`*!oQt^>b1mjp%)OXLF;8P&#JrC2 z#e`zs#C(YP9P?`oH#Ry}7#klei&e$yVokBOSZ8dG*gUZXVvEF0;?cV;1c8zr@rg6x1QCiJ+OFg`kz7ji8;NgP@b3i=e9@MUW~; z6Z91H7W5VL7Yq~(77P^(7mO5)7JMfdCzv3ZB$y(YCYT|ZC72_aCs-g@Bv>L?CRia@ zC0HX^C)gm^B-kR@CfFg^CD`NC*ntWYQv z3*&`Sp-iX{s)QP$PG}ICgchMqm?(4#-NGEgT*5rUe8K|4Lc$`#V!{%_Qo=IAa>5G2 zO2R6_YQh@ATEaTQdcp?6M#3h-X2KT2R>C&IcES$APQot2uEG>ysxVF1Q`lSBSJ+=T zP&imPR5)BXQaD=pop79Rf^d>>ig226hH#c}j&PoEfpC#4g>aQ{jc}cCgK(2@ zi*TE8hj5p0k8q#xfbfv;i13*3gz%K`jPRWBg7A{?itw87hVYi~j_{uFf$)*=iSViL zx$uSXmGHGNL+BF*gdt(3@Qv`D@PqJ^@U!rX@K<4$5RNZb^zBjpf0OY^ThXFek?@-~ z#e~HZkrI&+Q4sm>{!{%6b(n2vNPQg{6$L^~8SF65kU)5aZTb{#0q?M)BrFEnYq)nu)r0u1hrODFn(%#Ym(jn3j($Ug! z(#g^p(%I65(iPHm(oNDG(tXmy(&N(8((}?Q(p%E|(#O(g(wEZLQm-^14NKojKT5w! zISJ7T;sj}eJVBLUNU$cj5^^TwPbid7ETL3Fxr9my)e>qa)K6%X&@7=0WEEvqWHn`VWc6i@WKCr)WUXcGWF2KmvQ$|QS#McC*+AJ4*>Kq?*;v^` z*;Ls~*&Nw?*&^9e*$UZe**e)q*%sM$*&f+J*-_ak*?HL&*$vrU*+bbg*$dfgnNJpy zy^(#AWyyH*Xt_WxmP_O^xl*o?>*Xf7Rh}q!$#cka%k#+#%8STL%FD>h%PYyN%WKQ) z$s5X>$eYVs$=k|1$UDos%Dc&X$a~BC$p^`Y$w$h^$|uUF$fwI^$>+)!%9qMl$XCnP z$v4Wk$hXUP$@j_+$dAZR$j`_x$gjw6$nVG>$e+lc%U{Ve;v#RA15#S+Cb#R|nL#Tvyr#RkPD#TLaj z#SX3wi zl!uf@l*g1Ol&6$ul;@Nel$VrOl-HCul(&?3l=qYml#i58luwnI z3@I~}ZrtnbE*rfOR6iXYpNTnTjUGk?qz55K=nxVMDpn!2aDx4N&ozj~m0uzIL^xO${|wE8>sIQ0beB=r>a zH1!PiEcG1qJoN(gBJ~pWGW81eD)k!mI`sziCN&%v`frPRyLy*;ulk_+sQSb=CDa4( zwECR-qWX&Z`agKwQr}VEQ$J8YQa@2YRX23J4QQJJ6=0cJ6StbJ6$_dJ6k(fJ72p{ zyI8wayIi|cyIQ+e8#xB+wHvjYwOh5@wL7ydyS00@`?UwPhqXtu`El(@?P={Y8z7p05Q33MV|oKB)k(8+a5om!{W>2*e(S!dPRbq<|N=h5ZV<<{lZ<<}L| z71kBi71x#2mDZKjmDg3&Rn}G2RoB(j)z;P3)z>xDHP$uNHP?}^_tv%5wbymjb=D>6 zl6Bp5-E}>5y>xwa{d5C#gLFf5!*nBbqjY0*V|C+o6LphyQ+3mIGj+3db9Gr+^K}b# zi*-wN%XKStt95I2>vbD-n{``t+jTp2yLEeY`*jC(hjmAF$8{%lr*&s_=XDo#mvvWl z*L62_w{>@Q_jM0-k9FVcp6Pzjz101vOV@dIeqB%(*8QY=t9!5esQX#>i|(uLHyuaM z(?{uJ^a8y|AE%e-6ZCSuQm@u)^?JQgZ`ND&c70ZsL+>Jgv$8zde{9-)VC(Qt-ig!qrS5~ zNuR9mrthxrq3@;dqwl95pdX|kq93Lop&z9mqaUjuub-%&te>i%uAix&t)Hu(uV1KN ztY4~Mt|vb>s$Z*LuivQOtlz5NuHUKOt>3HPuRo|itUszhu0N?itv{aQOVhjR<$Pj0c7!nL}gVLZjXbpOU(O@=M4R(XW;4*j&ISsiDc@6mu1r3D_ zMGeIbB@LxX&9a8_hKh#DM5}73Zm4OfZKzB1`b6MgLqlUjQ$uq@OG9fzTViW(=xFF{ zNHQcFx*56~dKh{c`WX5d1{ekzh8Tt!Mi@pJ#u&yL#v3LYCL5+2rWZw8K$ zXN)q&7zIX=G0rG4CK%;LrBQ9v8udn_(QLFD?M8>uW%L+x8gm=-8uJ?q8VehX8jBlC z8cQ3?8p|6i8Y>&C8mk*?8fzQt8tWSy8XFs%8k-wi8e1FN8rvH?8ao@4jLF7s#_q-* z#$Lug#(u^D#zDp*#$m=0#!<#G#<9ln#)-zs#;L~X#+k<1#<|A%#)Zbk#-+yP#+Am^ z#yzQk%3Uy~$`Yo2(|g$zgJtJf@tc+@`#y{HB7Y!lt68;--?O(x$Se@}`QW z%BHHO>ZY2e+NQdu`lg1a#-^sG=BAdW)~2?m_NI=e&ZZ<&vZ8v)*hpo6T0U-Rv;C%pP-2b8d59bAEF{b76B)b8&M?b7^x~b9r+`b7gZ?b9Hk~ zb8T~7bA59|b7ON;b8~Y`b8B;3b9-|~b7ymsIoaIJ+}+&6+{@g@+|N9~Jjgu6JdAul z^CM)VDOW zG`2LgG`F<0w6?S*zsugy(%F(^Nw##ebhq@d^s@A^^s@}G46+Qd46}@|jIxZejJ1rn zOteh4Otnn6%(Tq5%(cw7EVL}PEVV4RthB7QthKDSY_x2)Y_)8+?6mB*?6vH-9JCy^ z9JL&`oV1*_oVA>{T(n%aT(w-c+_c=b+_l`dJhVKvd~bPX`N8ti@}ni);3 zljW`Dz2&3jXUi{^ua@5|94pTnWsR{4tRiciRboxB%B@PP+N!nctwyWaYPH&}4y()R zvF5bqw&u0ww-&S(widM(x0bY)wwAS)w^p=PwpO)Px7M`Qw$`=6T&izvXl-n5YHe<9 zX>Dz7Yi)1sXzgrGvL;)*S-V?%SbJIfSo>KASO<~cUL9r~VI5^1V;yT9Z=GnJY@KSI zZk=hJZJleKZ(V3zY+Y(yZe3|zZCz_!Z{29!Y~57i}y=}c~y>ESJeQf>S`po)+^`-SkYr55I^;^k9aMqtly|>o) zR>XO-)2gBgwT-t;w86JoPPfgp&9=?8&9^PIEhawn-%{Ih+e*S$+t%9F+cw%Z6Md^~yKSd! zw{5R&zwMyyu8*^Ajr*~{50*{j)WkzXBe zL_R&&%HGc2$)0Q{?^@e?+I!pk+WV7F)D5-|wGX$Cw2vm=Z#>RE!9K}8h5SnR4Erqm z9Q!=`0{bHS68kdy3i~SiTKfk3X8Sh#F8cxdVf!%>=db>qu%EJ@v7fVFuwSxYv0t;_ zu-~%ZvEQ>lus0%*d&r(?e`9}V|6u=Q|7`za|J9yl=O*$K zqZ4Bjg^A+C_(W-Vq8tK8oFkG+fG=XB(C6mk@Elya0qYb8fDM=gd&q&B&WcQhi_R*p7~c8(5?PL3{)u8tH( zsw3?i-=2=%j=qlmj)9KBj-ig>j**Vhj_(}f91|Rq98()99taQ96KDl9D5x590weS97i0-948#79A_No92Xpy99JCI95)=d z9CsY|91k3i98Vli9nZ=4F}!lTc4Rnwj({WN$aK7MymNeTd~$qtd~y8h$Z~LE@vKRK4$@EA!iY1F=q*9DQ6jH zIcEiDC1(|9HD?WHEoU8PJ!b>*i`7k>&73Wqt(3!ICbOPtG`E1avGYn*743?330-1Wyi4km zxfCvyOXJeH3@($);#|fO>@m~&2r6g&2ueqEpjb!Epx4Kt#Yk#t#fT~ZE|gKZFB8# z?Q-pL?Q$X&!;%w57=%3a1?&RxM>$z8==&0WJ?%U#D^&)vY? z$lb)<%-zD>%H77@&fUS?$=$`>)t%x_b*H&|x_i6(y8F8ax(B<5x`(?*x<|XebB}XR za8GhiaZhv4aL;njanExva4&K%aW8YPaIbQ&aj$c4aBp&Nac^_)aPM;Oaqn{QFv4yjYsD(cuXFP$L2}&I6ZDp z4o@ym9#1|`0Z$=M5l=Bs2~R0c8BaM+1y3bU6;Cx!4Nomk9Zx+^15YDQ6HhZw3r{Og z8&5k=2TvzY7f)ADiYL{R=IQC_?dj|3?-}SB>>27A?iuMB?fK3#&NIO?$uq?>%`?L@ z%QMF_&$Ga@$g{+=%(KF?%Cp9^&a=U@$pbqLmplV&B?4$e>_m`9q&tz0MEFDo6X`;v z2NCccMg)8sp@7X!1bjV2AVv-%VDCT#d`qK%55(z7q#qIR$wP$JZAf&e3w7EO$xj5z zauLZ#1bk^(L55z4=n%_|*dMVzVqV1D1Zn*OXoFn9Ru(0ipp^)eS0+kff&@5hJG9TV zLk>)T$fq0;h~1t@1=RO4bm$|sk6`F_)FB2f2dqK_Y?X-6xL^Yv;!+*rQXOJZy*<%u z5TX852Y;%AKj_d-Wg_4Uxz$CzJ;8dYLz`5G7*q#;szW@`A>JTFTGpQMCPZjHREJnp zhx$~9_FABSV}i-3_az7daoQ83#7utq>{PoyX6&?eO(2Gzlz>JSffh*uJkmbE9m zKM{xvv8WEQs1Eh14nDBmT=rxFd zALw+hfDQcq6%MhWeoZ2vLyT^yL;t89bg)A^wGdMgYa_yW0?Pj#4mrSFp!*BxF#jPg z*Z}{GLp?X6tRDK(`OoCwYbOFWs)HZsV1s!9I@n;YfDU=U`H1R$hz_}c4e-x6^=(AT zsZRGL7*A>k9qbSXY=D2o8)JDt#Q%yzn#0T1ibw8X`p!x{ZVPBv+%uzbG zL8o&b<}lS^zEL00sU7AY)uAn#Gw6^bvsnc z1MWbCu>#&6u^l2Dd$7Y81McQW@F3zk#Pf(J5f3AtLOhMQ5OE*k8iLT*F3}v$7$TtG zBM3tEN2mi*-5o>DAw;<=&MlQE#+`V69NA{ zs6+oN5&`Up2*(RJ94o-8h)IZb5YrIp^VPjD6DFto^Gs5a)}iwo+J|`b`htycy#eKJ@&}M!Ur>bo66~;70@fu0Z2;2aLf1+I zVgn!OBO4*##zbHoAST2BPS+jmtKd_Q2;@Z9A=n{SMZ`*oU04Wl!H@Qv#-%yljE00`5VagSZk=W{;FtM*q%;T@WEI)Q58jX^LYdLhmmN zA*c3&gjXgGY&-QY@)!PWJJg3bY{IIK@5XU1qw${+?H!~mo*s16)_=(?zc zoF0EV?^1{zVgQ0b-c)Cn4Ip-i2MGRv)Sv3WA#WyU#u!A(AO;}B0HiUf4jg>wI_`v= z&R1A(P#5;dJXi+TAYcOydnVLxN(A~s{pel_ybKZW2LwCyqa5nf*szZQZ;hA}(Qb{{ zhY=33a@ivSv8WCRWqApLKpE8mA#O<`fFp>2FSJMVW9ABZGVM?X{$K}z7zc>ZzCbw$ zaOwjH9Q>%jnr=k|_Q@thV7{~=LhX%+4tpK!$80<7i_KZ~Dun-)9pXS4GYuQp>4<=e3^EL3wza{*u<4VVn$!YtvoaR6|&4Y593*|H) z%4trN)4V9Bxlzu{kB$w^57t-*BFr_}k;OZ)cxM*x!s1CR4)-_A_{l7u!s2ip&h&@Z zHJQ9Si>I-84;Jsq;=NeBH;eaS@xCnHkH!15_y86k$l`-od@zd-VR1P3F#8YZ7ACL6 z;+0t(_I{>6oO75Q&Nob6oyBXgcuf|E^DDDHoKu(_&XG)Bm&M`Qh*@5r#T&4ALl$qu z;*D9n35z#n@n$UEoW)zPINa|t^Zn2I`F1@1x2+#~PUDi}M(=Hg5gmB0{~?4u2KL*0 zMBv^7_Vjgbo2g)gj`oL*DdOYAb!8x9JKM6XF zDcm=Llpz8+0D?dn)d3;yHFEL*T(Kg8a1jsOgIE-?9AXQ^afnk9S0WxpEJrpah|>iz z6>$vWe8eq?KOlZUgqta-zXb6!qKa&0VAmrSN34L@8ZiZN4B~#oi-@lfACS!s;#tTh z2N)om8z7HtYJeg{6`~ulFJgT%LBVen;?Iauasa_@M=Xxm1F<*alQt2**NATre?#ok zHc~zWaU|k<#NQCb?IL~(#QKOG5c?vYK@@b1_!mX2g_w_AtU%s{5hoxnKwOJ>2=NER z*N8uniyNpfAQv@&9z-k7vyO;^5%(ZoLA;Cj1W`&ZW+09du`psPxkv$fPO_N-7DX(D zSProoVhhAph+7fI_Knn^g!nzVc!7B7h+1;-0^EY=LClXh1#vdw0CI5y{=*TsBc4DE zBR(M)J>d5e@f~8x!4dmG#N&wl$VCtMUqSqg*mqdO{xf3HkrCbiaWdjDMDD0ac^sk~ zu@K@T&+Mo0Ypi0=@;BF2r0lp7E&h&>SdAPz#TGd|*GGmO`wF*bs3V;!?zwh*nM{j+|V?aEVUO1C?=quYy<+%PS*RMXZik6R|VmSj6#& z6A@=1)2@F&0sPXhRetiV($!aftDV5=1Ft z0-_92j@SXQBVs4S&WK$QlMuTiCL^XGc0)`>?2ed**aNXAVlTwrh#u5aflNUXCcl;oQt>|aTVeYoc9M14xZ*ZGvgT4QopV@wWfjLBimFnKJC!kv=lgp9Q*AAh7u+G4T+MthY8}yTHgTAtD&|kI<`pmXLzu7kE zJKF~RXWL*5*ftmkwhhLDZG-V(+h9!CHfXm85g3^zo{M=L;GT-fg)IK0>9^%`S>+Gx=LCWW_5>u8Eoc@#F<9CLc_$?U{TPxfW#dHmv%48-82< ziZvd?Sn-ouvHS;o%enQw?et3F(NGxLXQa3-(U7_FF*w9Fl}IG+Y0@Ot?-}Niu{SK=%3h%{fVvkpV&&UY>)@p zn!m{pZ0vr3jolBhfiU|9oZSzw(SH2Cc4d)zl>g@(@W-6_zjzLy`#n9+93aOZ&XchB z!~CK9Jv~Rjc>%5onR5$l|2YSIyKersodX`S_T*yyzs(n3|AS*n*L}*&Z+XrJ-|}9K zzU5xlIWwBvzcb^p&jE{A@j8*$$C&;i);TK=i;G!2oxHxttpARcUnv$}z&hvsPoD#g zIG*%;1A8s|`~Wug`2lR~^8?t}=Lgu!+2;qa6(9oRK-&cy`}_blh{^T?JKILj56pP% zen35TKfuQB2iVyCfcUf@@VW!!57%d)L*7(}e5np`s1Eh14tY@>@}WB9L3L;!beQMR z7PQUew0&C6%%M8*B~`QQl1q^6&zt~DVrCWqHkm>ga&Ve)M(4)>GHa(G>Z$@j4MJ{E`jP^LfJk23iY7C*+~Cs_Oxi^J0v_Fs!GY0rEIn;-KfE~scVjU#{V+6TCu3!fp z+Pg*sV!~booR0GzqC?%wM4%ivls_c`F<@_kn6wRO4{Xpj9Cz@AK7udoTQF8oM&}6Z zRn!;aQx5q9r{f6uz*t@&2ysA%`2x9tPWuZwG6k{-~;Ov_63MR zb?7V2nZ1r7ryof<^pp0Ra@q$tkHCDQF(3yzcWJv|gX0bHzy@;*VnQCkL5I0Q{b9bs z`h&iJ4zVH6-KayHOrq0vpl^h74)u!k6Rx?a4%b^$hig8n!!;k(;hK-?aLq?`xaOle zT=P*KuK7TRJm@talhgKTIn9A`ng``H7s_cql+&Cjr+HCMbEBM@AGA%!2Figm^8?Pz z4|HaJz?u1xmmWFwiF6Cxvpz2XY6!2SYzfIzsJ`7u8AF-}PEVuSf*y)?w(?P=~STM5F@|l0tS4P@id| zIaDQfngi7#2c}N%4vi&HBdNc;p#*78?2mF|^ zz{ZS4eVDPBKKY1$Um~zKL2Sxto6ujnJ|Hf{Ve&dG4#$954(kQx3XCzd!Q`~9*uStd zbAUMwdD3G+bvUL}hy1Ay{&ao98lXD(Qyu)N4*jA!^oi;arx_92F4Z9p)gcbmAr93c z4%NZFiPV8OUX0+&&|W{(VVywUUPAE#fOa?@ zw0$_nFoqBl;#FX9hzI8tc%2gTf(*S7Lx*z-^?~yo)r&IpVhkP5ZPW+OYoJ5h&{lpH zr|r{nngiuD56WpSl+%1Dr#Vqh^P-&QMmaM-Xq%ZIaAtnMnfZav%nvv-Kj6&#fHU(0 z&dd)uGe6+W{Gcsne!!Xe!CZtn4|9u4r25ndz9*YJaQa->MYP`{lNsy<>qhW(qX^RH zktjB35wXL2p5Q-$oXE4|qy1xt2u~Rh!EqxaSZ;g-4^N2T&l4j!V`c<-x=m^m$CFSsCrJ@I+9UzSDqRI<@P+>wJLxN>*|FMSums5w}U{lAaT z7d~%}<>Xol>bJ+|D@R}+9WlSl82>cRlTY&_{tvN!$JP<9z5uhCO^KA>z~e>d&u`ej5;!k1$m{wLN8Uby z6L3ED>L1}6oHt*vpC|D6ZNhPh$2eEXLV`Gh$o>Ur$Gpy9{kAwB+<}qu8aQ7!V?Kv* zUef0;Ct|$cF#p$MBK1~wieMMaZ#%Z1g5z=taddLTF9VNTPpp3%+q*FIIgRU> z9*;|y-v>O-Lvh@H3;UtN@$_T-oOs-);Bl&q^GSpApRVWe7$*njE5+mAh3-$-uezB3My&rD z*I^9i{}S^bjK}Q;&YKsASMhkf#P-VJ`ag{0V8V89;&?B_xW9~x^m`4~Yqm7PSKxYD zfO%cPetSnp%IWj))}awT8|UT8J`uhM=Xdid5q=WqMN!-@OX7IMBCf}Ie+S3^KE@k| z^T&+s@5S*dh2u}3U$2At)92RjV7&5pTu(NMKMX#IH2Y^R|dx z5N9AZ#r1I<^Q6!D%g0CR|BU-xah%7$B4*-z`-!I*?VGqoNu}MM7SO2-K;?o4zHDP$-1k7SRJuGVpBwTb`1QcAx=jukLTmj zh$nGA(tTPuB2upcVjo;T(W4^v(C7#b9`h|W!+j_LufOIaa&bPFK)i$3AA)I-`ulNS z(&PRLk7E|jmr{7#`{Q_rEAq1YrN#DLxK8%raZW}Yh4Y{a<%k#X`2T`PkAD-K2a6D2 zvgZFDoClR#MDlKo*s*1VdvM*|#&z@vF$MR#ZHN!CA9XuK>Io3({ArJz&ZEM(zR%)$ z&4}mShKNmZe$n+wuWJps4>d)kpJSreFLd93g7fbRYaW_=Me^&>JAxYqMz9FZ%N>Yx zALxzqG^T&VuN0zbK!jTm>3;D9`Fq5g7{5H?Rh;j;So3r!?jNo2d_?DMS=Kyli2G(H z;@}C9JSyP49M77kCvg9v`_L8SbpF!o(Gc3{e4dQ+n$G9qIG_9AzO-moq}>6tBRFh+ z1nE4d`{EOv?{waaao*GUFJBU=NAKTidLrwfGUB~L5x%Tc#7|p3f+Y}ZG>nwrY#qTn zBO+L7MZ|A9K^}+0A=^~8P(6iks*_V}_B}uKfigb&gAK~TcO>_Z*ILMF;`i5G`bCr1 zGl+l>=yz$f z=T!i)AYviJ!iYr>iy{_7ERI+Lk&Zh(4s<-|IM8vS$E7^htAJP$u@Yis#43nY5vw6q zN34NZ6R{RzZNxf=brH!aD!bqH5y>ekn>R!xmnhl1F=7+MrijfD$*C#ZkDQXSu_a<3`)hM0;7`vTb0 z5Xq?~oA*TQg-A{{*>-Yj$wt^apdLB3WFtAHWFt9cWFt9MWFt9MWaD7OA&7K64MR@X z)d=L{$ijp8;}Lnd&f*Z`5$XE=f*hWSfqJVD*C4J#g!kB?d=ug;M0nl5k;2A`C z-xc_3M0md%I6U(LxDjzP;ugehi0~dX_`&lRfO`=4A;SCCV2AhQ0goUaLxlI@!F~$y zEaG`Yc+VHgFC$(-yow0Ve?U1rF9CQ15uOnM4$m0?!m|Q^PY~gKb>Q$mIp7b7FA!fM z!h79N4)1XTdJ*BhY~X%Gc;6a0yjKkf@Am`3d(?pN{x#qt#Knl6$;Jcr0*D0>3n3Op zEP_}Ru^3`;#1e=l5lbPKMl6F^7O@;+dBh5c6%i{TRz|FXSQW7vVs*qCh&2&wA=XB$ zgIE`_9%6mO28az28zDAEY=YPnu^D1>#1@Dx5nCa)Mr?!F7O@>-d&CZi9T7Vrt|rF@ zj?Wszt%y4i*CVb&++>aT^T-Jf>dmx8a2Dch#5ss7(eDak1&s55<`}2QgU;oeX ziOlE!ZRgwn9GA#(`QLgyM2`>qx|UwYvaehJ*>&o_dwu%P_9c3q`LB*=FWmQfBmPfa zSJH9(&-vm1AN$ZhyMJTP`~U9!+P}IE{@MKRg7cQHga7UKTkLi5&&It5_V1sKKfO=> z&p1cc8+)AD`*{@{*Q$tgf3J@G|6%Vvpqg5`zv0k3(ga1(fYPK2*ib>bAWe#0A&>xp zkYEzJ9Se5F-VnrsdM$u}2#OTNf=Uq(5IZV%z+T@yIdd=|Joo;~`quk?>mKjEzq4od zw7sX#aHjN38pz)1exxLugvjd2gg zy%_glOvSh#V;aVEjPEgKU_6MC{CjqXF@FSOCPws|08}0+9Rjm3PySsz^6xpF!S3YW zxy!?RK1TBI=M`X{{CjzYm`A@|K|T!X5=Qdx^HpH}3dXA# zMR9pPV7?mTJB)Q0+cDN-{MVj@o&L2ap@)ClC!vRb+b5xifBRcP5C8VJgdYAKKM6gM zDVJ^pv~hyKnmnui%+G{$I#5&i99Kq%aRf#qjHVdPF^iNH0uK%ri+_RqkSKHwy~FS$_-3*lAY2jb;_Z9z z4n25u?j#dpAq&)=QP5+YY!fsmkIl$d#1v?dk-GnI~Ddv=Q_eXI$sm! z(LRnakM?tfd9<%1%%gKTVIJLA73R^sRbd|8Ulr!jJyu~J-Def%(Y;n-9^G#xd2~k_ zrH}4NllJOq4>G^vAZGxC{7D;;ZYU3Ayl8AfHb;nTjSz)Fh{7TKf9Z@3wI5WcklxWd zst@El(j6)ja!2ookRDMTKt#$R{U9A6IV6wjAbL*(y-xu`@95rX&%3AO-|LwUmywa) zDj@-c6(Ea{{JTt=U}VHUUQM7O<_|(;MefuP0=4H8Nd7L;1B@$(2QD)sTo08M;b}NL zN2rDU?cwq-;>o{{b95cSzk|w#+{ZzsMW_ask`ZnTBG5mWK=SWRp>Lccf45BpqVJO; z9(~6YA^JWkLMa@M{Ch6GIKHLvgnt5DQbuypINc>|f=AyF7r0}zgiFte=i+kYL1RL^ z$7aG#sQw#+ELr~%kMbzYBOMF#tHBn+d_h!q9_3ZozageO-vW6N_MZ;ACVAApc7QF& z_KEnnpc9fe10LPSw*-;^B6);}kUOe>r2GF@yfvh00)*-|O20Q=|8M;NEUp%SzzWmAX zJ#|CYSLBY$iR4fo&49@M;_rCWFPZ}(9Es5aBgzkQNAp62XikW5G)6QRMEqEc<1kLd z{rXspb{KU;h~Lizf^tzf@^{CDm=}eLhTIb{?!rj^F8LPbQz1i0&RUX)kNmx|A0Ef( z7-KOGmLTK`aJW$HPL6w1u>TJ1eh_0N&i7xKCx0({e=`x^2kb6}$C>*${_&W1#z>As zv6$}v7h+u82O7%~`HzYs;=hLF z?qdv=Bm7@tz8#~aA0eNLaV*Bk7{f3g!1xiP5*~kjF$QB?-UG?s5g*6yIT%YZs>6f| z>5cr|xd!H^!h{RC^D&aY_kV-uDL1j)dyER$eJ$oKFh3IGUl^aL5qjvr{3nc(c%DVh zzjQDkgfUZ{2=@ZxE9^dK7LiYVjCZhm27=-1Lw2AZf{GB_=Ct!E-_wL!4KZx-Z#(Nma-@%jLqe9=96ahUC!Z;UW7RK8cy@Lrk zc_Si!6EXiC`-fpZ3F8xt?O0xiPK3XN-H&24g@FmB&&2+VFz&+`i;;!p$nQX~@Onhn zoXF2(%zwt~8dr=1#}o0B-;0pO{1Y5s3ht-3VMIT|METi^!=G3}@Qn@x_WT}zHkO}< z(HCPG{{6TsPVXyrUxCpljEIl?enq4eQH~AReJDmXCLu@u4&5H}85mz+%nT>wGDZ{e zG-H0i1j1b#emsiGb!05z?}vF$?9avgevIUI4GJ;8-kJy}huig6%zLtkc#9?w{>7NT zfiV=LdL$udgOU7R2l?FuCU%d*xB}yliA20sm^Wh+@vt#kP9%8pdkYoVz1oHd_XUSz za|pS4wuJizjF++dI6U5w-)R_x`RzDn@)MqNBlaGFn~6O6yBu^02l9f))d zxP(9X-3M>XOFI+(OEABUlyf26)g}{|z$3!1#rT&E!5_l>S$kg zFb=XM0dx)&jT7Nes#!GFi`N>3u>$nR5hV*d_| z1E&)5mKcY`67oZPAo*R3aX7xU*xwoR+c9eS5%GD(5%G}UgCoC#@fgeLO(W#U@4W2B z{3VR!_jX>!6Y-eY5lDXb#Vvv0y)Z7ANyK+#R(E`Z{YC8w`Qx(*|I3L4e-)!X_Fsa( zD^at6@K3|;&oTcPBl-j7s9moNAmR(gya?{EDljj)h>&x_XodX^f{1V~7-wSlM$9KH zCc@bS6LQ-y{}rRe62iZUPWTUF5EzKvGcg~9`2gH6*5P!rvAft(A|5$MoF3+LXA%67 zP$J%3j9YL#dohYEBjm&}rfwwo2pr#L?7kM`K8!g&gj^QMV?2-XDaHvAMEb3m*GeM7 z1!FvcaTvz)7@uJzzvttN`D+*}G2X>Ennk1&5lLVj=8c?)^xBsb*ojen1;JnA5%P4* z8*>PLE)GY-n1$VIqHsFc-Fqbw5BYt?P|Pzi9>ti9QE?R^{|NKs_h1aLdj!Un7#nap z4>2#2Ooab}-2*XC{vGJh7$ScS*!>>HQO-oTub3~iBKVVWgnURmffuk`1;(^BgdF+3 z8S**7B%9{z^bQK{t2=il82=@kzcQ7u%_+<;>-+wECU)K`h*KQ;DZLS37V?K60 z;r|r7mu)Bfv*!{xYzKi(cs;9`Lddh-2=^F_53roD6UyKl!l`Q0574R;dZ^R^N4 zk1%gJg>X0AMc@&Pr5Jx=you$^_YmR9@AypGON9G@@uoY$zs4B4kKk{n5#c^#cZqa@ zS3E%AW{l+DwVvidgd_is_0Lp-m)K8WKaA=a$-iSwIV?&+&yQasqGI9_l2S6Va`Fm_ z{gjk{sHpW<*BGEVXt36hA3u;3N#gKM66uDJbVG>xdeV<{2d6$CB##~x20dZ~{PBMj zS8tC*4@XJNOJTJ0@$hwUbv1$I4@LOho*6od3~KM{^7|d!Cr|Np@C8rgZh{#H?~xw}p zkzMjcoPzkr2)hGA$|GHL$pbUR%XO@Y4>-c}$Gg9fJAU`}8#xN)1wF#=zHVrziFkY` zyE^*?5Dtv!UCMY z2laIL!v-&d@tO`5iorFtoMz&Mf9BePPY{5@6Mc9ef%%;Mm=lFy{Sv4He6AsRTbyHKchXYJZIXj zZRc+FJ9PeR>RC|(>12t3>_+*^!`tMtRxRL|DZ7WwIIcx2nP?J{x5@5qjO6@gks@l% z&Jjv=Z&$C#nm;r*Px8={;{BGl&ptnJRVvQv3#H$NOquNib}BwBR0v;eGBHwm2aCR0 zWKQtY_fsPxf@rM4cgz_pJSjmPueV9E5)X+CDtRxxYg8ffuHx<9!o4507qoK~us?%pO8!9v==FD8GaK?1}ob&l7Sm!QMlg_@* z%PHP1|0qvvcTE<5oJ-w{Q?WJs`s+1|C#m1%smKLCUOt|_)h0YrA#+xEg4`{|C;NWM zY|%eSkE)$&#`<*J_i7IUtl_~%b{J@ObRJVtKsO5 zeiY`jEmpo#%tfxSKwVVFOi#l9!uWHS4CKydr_4HIBp#kK=g6;y(vi2Sa@VFd8f%@r zH~VxceUDy8@T>4hHo zq#ju1;lgxnj%C}pg;$@vJFC7iMDG5icjNDj@jY2|^ipc!y~Ve(UD|%-nh)+ce|}Br zxohTIa(oYGoEa`Ly6W5($%Y$-)9?8ehc*r~O$f<3>qaXV9TYZqs}X0!m#wD}gtMw+>dZ6N z{)!!W%bQx-aV*q3PvY$ZS+h*8=aQdd&)yeo&X_$?d+7X_oZ0uZCxr|TsnJ>cCMY%i z=({FGk!4#qI4f0Y-%Ml4t+p^Yc(0`Yyj{Li+m#=`Zk{&(DF3`jvBlRd-}Z5bx#W+^ z5*rX?Z}UjED#7DK;N{R49m}rRFR7d?p_2S;{Q;{R*UY1W_LX_;Y?>-F#^+(KR)1c5 zwu9)5%i@~`GuKhm9Ij_Sd9~NBQN6i#Ux9D1_7=5v#)P6IqoQxh993R=?KmsT9ovI1 z9%Bvnc~kb+BcHaHQR>bKVv86PH(OuJ)n56aBd65s(4=3vk7^1v!-J&EP2M@|U$IPK zXsMFxX}7cwyA>?5M$IZ&cT3CnO5nD~e7BnU=B>*qyEd1lwAf71p{dEA)-9avFu-nb z^_(|bX%EV4I}b)AE`9NGg6c`fGZ`xE&XLnf4wgoYf9!3xHR8dR3&z|A%$}utr zPR#K%Z|fXbc3WoN)J=!$9~!UQ%nKX#Uh@03oULaWu_IvfCH=>R z%W{vX)j98elXCl>ohCgfV(v%1@(;_y9JAGpUs@mDRI~ z`d@IBa`>#cO=|i|-NJ<>`M+wmJC+($*RNbPGsODDkoSv3^H!QKYggl>sOWixtR zCi7C3kLmOeKMp^0ZFm^15SK50Q2yQa2N`jCb5`4)8K4*DqI=h@j>bE}Xqoxo)2@$` zS2V=*)7a9u>WEqA#ZeJ+c3hrzxXMpOX2z3~!-mGZShqo9*{Q2LE>rCE6Glkp+N_xB zEVfwvwo6qi{oB{ZkH!A;m$%oAR?khia9GCsiO_}k+xzUy5YP9XC`j-x}lRw|3{E%Yq?RH!apqZyu;K$7g!uq^rxLrcAoVUcBLR1z##}LfBr$ zw&4Co>ZkH7ix1i^bUb_TdzsU)E?Z^9HjtH+-8r?2hFfq>Ey1m(N%U2)Gu%}nAmb{*{ z>VD&f+?X1vZ|iUFH5h)We@ND0ipPKxtHmB?sZ_b0+mq_I!T+xxEABp$+~qRzRLaf8 zGm4sSw&(jUkv)FJHzUnX>-^S@W@`H-X>XJEWjiOUeXo5f(va{VdDqhKs%{3Kc8iZL z^BcYYN=qF5w)62D_iwd&sey^9Y~QilqImVm@9U|DqRW~K-Y9XiL+7O7vsfDUei{*~}wcuNfcwpe66A^7Ma{}}mHT_QRcsO~K<79^- z+3OpJsst_HK4;fXg^lK0mY0|waqC?5SK0Xmr)RM13ahD;?v%^5)p|Io+^XMRSRQca z(7Vkq=KK7QRp)fJ zUD~`h)%||dvIhCa)${Bt&Tooh=MA*kc5K<}3F&ef#gUnX>LJ=6UMHN>bDVD(Hp}V9 ztWHn9nzYZ`iu9QwtF5eg2lkEp`YObPW_wHT`TS}XJ%_k~rxcPTtnQ6aJilqhUfx^d z9beNM*3!6(mOP&jct%h0YVoN_78zDs_GF%KQM{h{@yJE(vvcmAi}=uBsr&Nr4-Nb0 z{Fw#s->y;m6;i&%^3q-FvF<4)!yD|v%;xor8MJBQeWiiC;quGU)=SB~89*O$DT}jX zts75%#^bmZ>Ob744EJ*>4L>z|W`U%4)hE>cAN@#Pzv4&W#Zby~k10P+&pt{STYr;M zwa56!4E60_Y59DqHfvQWk=XXHtG~8LRll99|ATU)d@c9G3YyVUZ=d~Gll-iIMY!fZ zH|J-U<{Z@Ys4mkyv{(C?iSkF)!xCz5Op|$UE??TL>N)xR8})ess;RYORbx_{-uzSz zY^%s#rsieas&<4c)n@j~NG-PQYTGaR$uk4thPILarN}c6GfWLCbsRkagnB}Y4t+! z^VOiF{a4rATxccgF!kyz{~W8DJGZT@w~fA!e2EVVc9REo7 zW)8>x=4_ob%i2?~^xfqYo61)*n~blo)j#Nbr0Ki2lRm#iM}P08i%n`;2`wf?JE@08 z{-Sy;&}zBVK9aicS#3-I@G~tx^rumyk1f6Z^Fpv;jHS3?>Y}H&)jO{ldbS$ez8vOt z+f?_2;o(dT)BQ3ZYBiRp)c(2}Wg6=^r`F8pg6Wa_jiz2H<7+F1o=U!W!XtVzbwqSp z0sO5E+gdS$6YM55bG`NRWBZ&r>_^joTWDsRCao$3WVj^)yC zkA^PDc>iOmQT*y(QbgW4>3~MRl*q>)&Tzq?GBil+qnhT6qUaDX}A0H;3Adi#GK50N-~IN;!{*$svhXWP>6AI@*@wb5TvU4IKGVW#(TT%%Y3HWR5m_-ta!5+W z%93v%vhTf;u((i54@)oK`uzNy2|)*@R6Z)@pE%nRJa??9EJt2TYyY~1IzQ`!Mk!7! zYu|n0?$dCaqDDD-#?5a}796=+6?}S}x7fxRvV(VKu1P3}D`%Wcc9huMHbisfz|>1` zjgB>aZXR*2;#}0BDz|syYWD^V5;NVIIKiuwZd7bvXum$@p#Q>;<>7<>YF_xtxT4kZ z@STn`dS`FcEjZGo`#mm4<4WkkXOpG&tW+7WE+(a5@}q>q0V-cF2lCPyW0Rg%U|MqEI6w6+8pK8byFV7$m-Ni>_2MQ`Dj`DXW?4eq1PMrHr~Ej z-|tL&zW?#3E_M8k(GMebuAs>sc`{z@v|6FGLD5JJTlcJ3ipk3`jh2Y~MQ;N#nM3bi znYL&}gWU#+m=&6@nJF(KWlpReqkb+i|7xvkQhj-WSl+igGmpM9XuNgf^Ny+}X^qpZ zOpj9?yKY47IsS#SZgzaX9P4392d`yh14f7e5A`?3ri|0?bG(v`g?1Iw^5En|9q$4zhv`5*?QWKx?HNeB?c*vR`k#@I*mm2Re(So)RAQ;UM+fX4nAb0SSG~Dh*;V~-4(gs)V`XNJ`@{^HaUy1Zriqew zTyQ^^SvkB1c9yF5sZ9ARRc-h8EqQ-&O%y*ZSJ|}qz{8V)*K2n#zB^2F z=wtgy6zBlb&Gif}=+|f78<5{o($9rEBqZ8dWd4fFG|wk{7tK@~98^-YePO-3x>lQs zyXeoB%9b5(KbI~U8h4<0kh4ROc zkh-7^@uew>1Alb-Nxl%f-Iz4;!R4(bfrpQ$Sr-&}pF{0`+wG`r5vMk_|4e*9-!a2` ztmr!GiSM5-US$qDJ;yXFEvk@F)#^0Fd}-CSNooac?#Bm3`}2f-CDVE-W!^G+P0+zqwLzxb88=NekQe9eBgrlf$K6wKD4gQIvN#F zGGW7T?HPmKO}+7@v~wU!J!xG^WbkvT0@a1=-5J{7JZsNLjIylS^w7bnf#=J#KQiol z(cQA0hSE6-lZ?cs*8Sp@8!g|GcES7cs6%PlpUwHDkvIG4$jol`*=zCn(ZwUiDgHC3 zb4OMti5^_5tdczBQjpez3(Ml8!#iFLcC|mfdY^@E?9&>%M-~TecdU4389H^x;awwQ zOXYt`O=q;^%e|ASNLA$Of8BF7M=G~&P8oR>K1x&;Pd6pA11u_+&k1!MdrHIs!ykHKMtZF zjM4pgX4Yw)@>`xWhaYcV?`M;J@=1_>!q!iXBK$4xb>foKb^WiGw(HKDu;!BW(RsP2 zE-sYl^gkLMa=d1nsEuUnj`*z~3SUiHVX88o;kN2+|NCz$4<#iIs8e4NJc_H7@AT}6 z+^x|0_lr9=9t_y78D%EM^W7itbk*By1HSh=x!z#$_-p4l8UgH~%hLP*dTLd&kvI2| zt=W~)Ugt)8oVu5>>;9Cp%nd6|&i_=cy*wn=S%v+`Y~;NQ=UPu1Ii`AgRlC_VZNFps zQA~bb@Q>IFQehc4Q$&_5y1CU)adx=b8{1#sQjcj}ovDXW!Lavzg^Q z)M;?-L!0mU{1@wVEaUbn9{%gqo*51Dzix;v2tP=z96j=VQO2!}b1G)dHukYQzHtBT z#R{?`#9y*H8zqS9Yp#6RD+_{2`>v9`&>PrUq` z<-2J>-DStHNvA^Z47Se6TwQ);^6ROC$Huw6V&>i-JCLgF@Wt`Wj@8vIXU46c9OybA zZ_v>N!)`UU(dS;h)V{jFyI;pltKE8r>75e0euPxFh3z~hBd@S1e%D*~W{-rO$L}nE zJf%o^%y`2%wYAZ&FMfNVS~zI=N=}4w$&bW|eui?H$9!H^+OFI)bJ`&ziL&{#H%*i+ ze4lymIxWX^_g{KnUyQr6RCcb1f5MgFG1sa`LIowg)g_qD!Ss3Xm zCXw!R?M7a+#O-A14TJY@w>#2t-=5QIw?3vhMe;(W{M9LrU%iri=}UG;=F9mc7?!tb z1XgHMeoBwEF-dx7nB(r*TD)Rqhql}P%8ruh%`C-NdAbV2(+f){?hkYQkoDW8YAYlOV=H_Q#)XJeE(tV5snvbEr3M{~HYb>;gPOViJ{x#kVi^G<$uV&{H# z(6{}pM9bukGb8e}Z6Bo?HxX(&#OnRukX^EkTqL;aCOZjI52f2^7jUR0cT*m3EZ_R!yBD>I! z<*;aPVoTNvCq-)ypG_Y+Q}<37zgo0YC%-!GMVeRs)Ekm#b~WuxT=HbfyUCq(w&xRv z6-2Yn$FO|ZKMp#*vlwbnFtWi`|L0AS_tBE-;pfgah47+od@f>k8jVSRlKWY6_4jWz zsXW#05nw#k14E3;#6Cxj3 zXKZz!EifoFsBr2q>gU6HqRuMj+)LC>GwN*6d;G+;?Q7HSAd4H~CswCPN(&0(U0FTT)Ajb$`u>OK3>QAgWUzWni-{Gxtm zmcCxUsdQddKR!23>iiwgX|~@hAD$NbdfGxi&EbsOc9qfWbs=}QpBpnP z`$75L`}N!Aee=_)77yb2QpYZ-^H8jw&Rp15$eeO^oQLw1VyeF8MDar>vcGNlZhrr2 zzr5^U^Q^X;EoSEoG<~(hp(Xrl^t$KwK8+96E(vdy*t07*?8KFa?)Tm)xIa(d7?$lL zBe7g;t+wPD@q6a2E5C-TQ+CX8S(bCn|4hqw@zGxne>%N&(_xF#D$3`*hEKAMSd>#B zIr-6eo$*Z#Itpw3C8x*LM$inWdOc3Pce?J@oUI!tG<_+EoYBH`7yTTdwdAhE8_6uo z;^o`2q~*d_&0Ou-a;)F5m*3V)?JT27olJQ=>^+U)S!(_)eA240Z87yPvW#_EcQyHM zKF_>f@{zOKp=K2GQnA=rmpw~2UZLmTbDQ+qq|&5$fu~HS;{ln}{#29lr-vtX#&UDj zj3>%(mk#}<#F%yI2?P&KEr#v~$hr zT}4$6L#v;v?E64Jq$2C{ev_%Rl)8h>w5_2Y?6}ZG+piAVl?zNY_WhLIHe&vVjKt3B zFUiqGw;Dg~tUEC<@bULOjs^X+3^&f(ub|MT)G_{SNhB%jLwE|;#{4PQE+burqylXQ|JD z)c0c4bF0Qzp34gNOntfQN@vRzw}yblmkgMuF7lQwB{Ye34o+KAqm2&T`qb~&g!w^N zBRid^G%YAtxc28h{kXVy%7$NW2Pv!H_M5czonns4zOj!s6`b=Q>by>TUr_z0LH!;a z-eqLIs!UO`-fqMk-3pg*zEkk?vTJ+qrA$7%oOZJ=+uXgqL^`eg(@dSZ6UPpo-SbUv z??CsX!Dd=UF41pJjBqG6Q$+2*b5qCYr`oNN{j)S?S2|p4wyhoedPI;}USmhglE$U2 ztP#o@vux8SB9-PHWBQkxf*OtIv#BW}3TGstXy9Xj-*-@RNbVZ(p)o<=U zuZ$`xVSf6ZWt+W8j%LmM75##*-22{X{rvk9QSp9C?<_RapE27;_75;GyK=wG*Sf9# zkx8~L_x|!7qQ+VDlzXe@54(DIS~Gap)J$xx2_A7lyYo)6_L2wm8YLBMA&lKt}9-;c{%R$1qyV)NKL!|-f|W%GuF z5=q6?Q{Ru>``~dfCra7Bh4LuSbd%Dw@!C=fSF%2gS9UNcd{o=C{!`G6dhrh12jWYm z&I|DyL$|3KaRx~-uidg)s? z-`=s5T2{$DJ@V^u`&S7wYKFLb1j}e#==`RAVM*By52cacW~A$-9UpmnJGV?meW~v^ z>effE5<|H|Jl=`PDv53Rtf#K?c42F_)|3g8E?&P?W$~klrhC}1y7pwTB3VIeQLvIu?H8gy^FH=Zciz>nzhOD*Fo*!2d8J%r)+SLQ(0L1Wq4T7 zo5g!N2D|QAI{Z+Wa{ZQtX(d-Sm|OmwUixOyL7%~svmVQ-+Vj5X?xzep%Gy-DKVJKC z`I;=N4qt~hZd&ai$J`+Kq0SvY4V{->zLcwM)tQzyEYsJ#I(1E{{E&E`p9ZYQZM^-w zK~5%O&!g^b(R9A9qo6f5`s;c%$(JC&#Zy)vU{W68YQYybjsAN2!|Ikv? zWo$`B#r3J9+V4HkIc5@7wfCGzd5P=h(Vyl}wf{PGx^{Pry`tudn(xM&f?vi4cMh}* zUZQS5fJUHYhI9r$0F7;<3^1 zr%XSS6-k@(IR|%76W$ED;7tQt2k${ZI=~+UgqxAl%Yf{Ge*)n_fzoj4Tv`+Ot&qM0 zye9xz1AiORSB3X*pb@~I0Yv&-1LOw$dk7CVCZ(qVsZ%CFQB~qS_)(b{3Gy3`I!kc5cm{q|9Ifx z@}Nu&wm%biL*TOkQGQngxdPt-;gLPz@}P8oijuUb2x0$d;75V~HEe$d@VdYs14Q~- z0ptYy3kZ+w50{XowSeD^?Y|KCiNH5u`$qx~SA%5E0iyb}9>@duuMi&DAJrLED1RBY zKM#1YgUn@Yf27l4z#j%g@<~7rz(0lX$o{i|K+iJUvHcT)9}j#zwtochP_Je308xEf z3p54zj}RV}XF8CEko{wU9|Qh3vHe4V*9ZP2AWCl)kPGl{AUx7%01&iWnN)25#lYJF ze-GQA1H2jV7XVRx+6d$Y{LjB>|0NJVvVR~D739+aDL_krCISBt{E_`<01W_sGq!&m z@MD3m#`X^b-T?S4KxD6Epvl0uLwIEWzS{p)Y=1h)3!uAgW86A*61Skr$49Fh%RtS&m zF$+i&_-)w!3BX$eUx)1<4*Uq^~1^2=M!`{TBgm1AGg%KO1;c;Ligp0&M{D1pde0wEr@QALW+@qz&>JfFeLk zf$V^P4E`uTGl2#Izokd}uf_Ifg1;g7=K!Mot_E@iz7xVDd-m1-uVec&Kt>njj{_oo ztpIWY{w0J*_MZ!+1^gat|AoL$1il&DKN5Ho;0pm2fYt+f0RIibBm4K&{wuKkgF$8( z$R7bj@<~7rz(0fV$o{i|1_8eV+dmQb@xV7=`$qt81bjZA9MD>zDZqb%@Tfd}wf|e# z{-GeF5Avq~QF^O@T!4QI;gLSc_P-z7pKSm4vHdw9V+QiYfc=0r0(k-di=reULeY>A z5mA*;rVNr0QBszWpbV4{5$`WyN70fHnWQ8kMj0R>BB~~#O&Kg9LRFEFp=e5oNUKXM zrA&gXD@lk#_@xkjAPPS~Swaehm+UV=L*WCJB*am8F*S*iD7?9fgaQgLuP#v{2v23Q z|8D-%ZK@*%a~TPk*GNG}Bm=V>IhfV-gE@~XMT0VkGL$lmVg%o?84uq|@}&e*c$6ec z2BnPhm?8?*7ClNm{wYIP^iKuQ5lIng5qS|MkpUu95p$7AB7wr`lWCG^p;Xl2p9cIJ z0RINUKTY^I2>uO*e_HTw68xi}pNEJ`NJzpX1&=g-Wbh-~D=R05 z%!y2f%z{*_gdb)6sNhExKS*!F57MFVL+X=6Qm-WTQ12gX4|V^+{*C%I|4#k#(C*g( zxdT-K83X+V!~%K+G#F?v&>Wz9;8YmAa2IwP@BG3jPPoSGXra&iw*g$W9h5)4k%>%jzWCL^o zXc5p)AQ7OYKz2YSK-xg*Ks2C-K*~Uyfu;gg1C0dA0-6uh4kQD#8psvsDv%-2Q6MHz z8_+L`&^91{pgN#YK)FECKp%h< zfYt+f0NnsG0XhK`3G^CB3uqtET%Z=9i9qLp76SbMQiT$zipWs50B!;F1@r~11*`?M z1hfRq0n7p90rCJl0XqTZ0ObJJ01pEg04G@hwQh=)fR{^>Jx&T%HRsiY)>H{7D zJOUUB7z+3d@EM>6pa$R$z#V|o0jC2t05$-Q0UQIE510=a3m6Od3GfqOKfr!~8v!>0 zdI5R?-U7S@Xa;Bocna_oAP0~G_!jUj;84J!fcpXW0|o#F0Nw|@4`>T$3s?+T47eC@ zG2k!2Ux4ssKNtn2C{&<8ASEDgAakH7Ahho>8OQ)A3}^t*44|<f zen3`0d>}E1AqmI$cwAPt~@`k-l!Fo^;$OfY+eG=3Az zzM%}i38f@V!2ZXFOp8o|jGK&;j0;u?pbKO@f{-Q=A&Vk{Rfd3Ib_`{wfIbO=**fUv zfA2y0M0rH{Ko86&U`j0`DJvl-E-$7aswe{U;~ozxBN+~r3zdm13n~N3JIXJtte|ed zH!Oi*H4OEELZJc~0KsmW;M-=C0KI_%fuewx0Hpz41bP6}+tV|Blr~BirHRr*X;Fbt z8k2wmftCPW1VWWz$$uvT6c!#a0JoG89i9G}5u(^&r@;$9++q(v}rtimxOXW!_c zUVL~AK6Z6r(Re&}4kscy$~l@H%x7}g!cnjUK8}nKS~RO$kUxbyX^{+G6fKy{i6CZS zv0xgDg;RpJAX;#QurD2}(u?auWAYfnUR(}`kM!M(7oQQw_X-gf=EL@IcNZwEz*-)0zA*r^qONyl1yEu*u3n@2RowKD}#|P#lNr3$2Oa*~4H=HWLgP>OtcN zhY9B>gvq9RaqXi)1IYZIJZ~-|gc&C+642GmT-ijsqXyA<3~DfH6jVNk3LzL=20NHR zrBPYTAm|3DWIZ*Nm8HTnJ&eJI*Ml;H;#p0BleO z4rCusjp8ubd?q^-m6k>&_0d%#V{rVf1jt|nBZ?2Qg3NWPk4ocFSsaK25DEvS21y<# zl0ifd$1{T1QOXcAmC5hBaH!kttL8b9NK^qoFei}< zYIn;}sdg+DH8`5b=R|h*f|?Qu&Y%p?7LzZ?c?fhUMCE}5O{r8r4mFs|K&N6fHW}KO z8V%JG)t^9|l!0L&HG~*uNTa&CP@V1ksMGCyJY79qtf?>r2r4urIzo^to5LSL<-{!R0Y|d>A6B&}yJ~&}ML9uvTy!pf)a~ z1QiAq7$wbR#iOb^!^0ioat)!zbE2Vk!eA0lWivn&Y8fgElg45u5cxnM$wm+Y)qqeb z406VTz`>weW@HpGAR|2o1`4!cz+!Ssk&4DLNRZCtLi<9e zk%Gu^N`k^e8rUF0`tRO7pwxp#vO#?bhAJZ2DRCz!*AVVR9=M-${%Fi$|1g-HT8 zLJ%{R3e}Q^<{xB~o^~EizTS2YPB>y*a-j(PeO#eMuvrY~t)sY{7^u#4RBxkbT%t`; zi4K_|$X-{jv0X+`C;NH0L*%gc8cJoNo|{x2s`YHb8;S#60rc!qP#nTQ;009>=NkM$ zg?>n995$*6QBf==G&QJ;(QK10rGp)4p(s|UG=v3_2#zC!#)5VPSBRk9L(GO<8T7XE z@pbaS<%d%7P*;lTAug?;yG7~^VTMM-+yb;m4S_aC+8mV1h(!hvbR>{8o@7Cey#@Lb zm;{#;$1mu=QCr5DBh?NCA}ThrA`ZeJS`N;yD;srB(8i#j$Dq3xsD>z^@G`t}keY7AX95mqpg;PK-hWP)gpG&&tRHx4Wh zy5jjWcLCj2C&ELXzz0ovDLihl8CuhrK{bktH;sy8{&u28F~b-v)JY56CqU;7m4wHM z<_0rtev8F1*}<%6m`QXC3~};;!x)jY?#_f8i~{WL?!>5R0!a$oiSE-hfEa)bW*Ayi zXP^NHItJ)B`0-I)eYpV|l>NZLuv-CfbtG3vFi0_3Q0GABMAzdT4?Q^>4+s=be|L8n zK6ucZ3iQi?*#wM<(0rguddGVS=1u_)5V~6;Tt+AwzPQkU;<_#E0-(|{xO`YQFan;pxL-z7<5B)B66H4 zB_JS}n0`>8v9lO79v{6#asU}|Ft1_7Q*=!&%*}ZQhR}mXK)0Z4Iu`zyKu5z1*&Jxx zP{BG}*K{P0YJfaLnK2BuAw?I33q|z@`X)9bE((Tg=yM_%@v$5(j1@Q>RbSU%|DP44 zE5E|U#ucxRHGhEc`kc?4`!=q z9ON@X8C*)25S~r;+U9x+^fKfYPnF;McdF zU@gdu>(d)_Bxr`F0ic@lR|1l^(FxNW>jYd^%NO_%a|VxD5=7B*>7LyO_479(3Smw+ygM2|?|XYR zpy&Q5R2vvP@lc1_4bPZL4&|^sfZEhOWnyPauo3D+2GfNwXxt=B*G(R`?VcN_0mLE$ zIrpsQs5|IwuQi~n$wO1?zS-KttM`3D)Z2Hp-G=lf#N(OhxVZ59Rf>j~md(Ms5I1X(RsF-VNzx_clPVeTxSZ zv;l$U(L!?qJan)aY=iEzX(lfq2F+yypg9nc2!+C=E#{XApeCa< z-KQUno@l#<*QWrO1q>hA!=K4>j%KlZVK=t>$b;LDpt8coYPVt{MG8Fr4EDPq(8PL4 z4$97g)22TI!T{0h*+;F)@rC_E z$V<0SFn#OIxvLa(I@Ev|CMUYP%5b&#PeCCMaA?*oF*4R*Sete`K%;p!W9a7XY(GGaGnkD(K;1720bU)IxgqfmC3N zPH;@pYe?{qg5AFE=i*%o1K&TaO=bs$?9F(jKWczzPZ{b&?^#0vf}=P%Jm^~j&Lo1t z(lDv+TjuXdh-O{rNCtDbC{)i) zE;2=w&pu;~i=y$n9l3UyIG6`pVdzAgT>Zi}E(g|Qf`y%~FS#j;_QdfnGnLxq*flAG z;}vp`$`l>6!kiu-DZutJItYW>Vqj=Yh5ihnDyz1Sm+PU=In_-NA5D z%cEjN{$6(CtzLAhOF{E9SXV%IO&qs{Fu8DC2kQ|W%9PmghuH%I4oYB)n^?0zuZd65 zP%P*?L68Z-?AsV0F+hy5#QqDi2(f|tJ5AysNpJ*%lhTJY&@m1=hd>9OLb*cC2v%l- z=FnCA-@#x-h1S66fI@IS(Q9sd$z@kmsAPTRwre#CXAx*q03G$AMTSs;h)rW?s|<=4 z{GN}AHm(szWk*K_A)WDv)d)2R9Xf$=i31KC5mHw)JA%!Lg@ZJtAoNgrCTn5?hd{6j zjEX`-mOvVYB|&s(8iN#s=Dct`(-nskKD5B6Hh3c&&Sv0nvTH&K3(en)YtR%rsd%Q%2nj($Cag?Rp~>8IMMd!gSHU#|R5C&4 zyQHWFFc-HLc!HJ$$5&Vq9U2iTq6*AFo@aGM1jg^urjR6=ZIl(FuEGi%>Ki)E66DX{ z$DPasxds&!v@4D<4hy7(it@XjLz=M7*p=nq4V#F&D+;UwRE3y)bVvpb6!qUi3if62 ziE9ezpbd^K;D7?Q5Clig(EQO3!aq*N7|a5*U&^z5ixCMDwNtRm2ix}0+PZD?MYDNu41zW|!(gEK+wH6# z;=SohuzTBUCI!nnyt^wDl3aXyaZx|mYh^*LvDYY}w}EX)XE?4xJy5r>-M!FC*qy-+ z?PK7@3H)gyxWv>~0@%Fh@WNEk1$Rqc;6kowyZONA>+I_dQvfe6aaPvNw_7;0{DzLC zR{`XQ3f|4Ldv^W(oX1kgeMdSohDq$}anX&1Xch;J9|;UDr&|ny^gUqLm5bIwf85dx zfX>(n=>)oJq5af9qkzD@ItCEiJVN#lAd4K(+b&F(CkznWplBEaxD=zHR#=8dCg}rNGUk=j{*yK6_^$b`^LoKoi(*<%TYg3AK#`MU0PadK z*VR{$zNm)*sV;Xxmjez^Lxr6Q79COuyJB8&U9LAj!KnrWKssYF;^1-$hlZjhk#whX z;NlwtJi1;;A3@YzuH87nEHxUY<=s!hY2-aU3OS9XM6*$aMd!O{TMU#>Zisc63$7WG zbrg-Ff`cWff4GJwL{qvXb$H#9Y+_(Rfo)4W@e(b*Xn zj-nY{Yp5{6`VFmi zAP>DN*MJAh9-@N~E)7~ffQ7mhN~k628|qUW$<9J>%}$`b-^C_dInfroMFYwMjTn-~ z6r9L(bsvHoKd5U!GjX_}1UoXQNNB3aLnnM)mBN$^4+elsR&aMTI*2ek(ZFF6iH^cx z)lLc$u|~oI6jndz`WxI z;vuW#RYJNp1&RO*z^*2bsszOgsw3)r(TR#+zKnZAyudV|#X$|$g}&PEcYg;dM-wMm z&?*cBt05#25D*m~7Z5_@)1aG#D=2>sIcSJvF(Vkg4rb)wLXM?Kkb-6sP+|SyxS1FT zh}O~T4uN3sM`sy-TtE??9l#9q&)vrE0z#Xh!b2l*DT$3`@g7|Fy%)i*CBN_7#ll6| z?vsRWDH9iECewI=ZO{KaS%jXsn}YeA5UgE?kyf7Fyh32h1;zNsG=K#=`Il0gtuq0I|SfOT25Y8@N^2P90~j01>- z-F~!L^GNdw^ZQ3c=FWXP0977Od%C+_)WOckdt_wX^TnB@7c_NUy>=>Q;HT2$v|SD| z(T$6Lp<|E|WYBWau6b?GIvy7f} ze~Ov90l}0uPA@O6%?Y8~09>m(h@@5;l{o>e(rGvc-(sm`?WdC-4|(pqy2< zKm2m#3Y|V#I6YhV>Bl=jlpn9BGyke0{%`b`JJnzLZ;8Mcit^dU`yCS_E+F7acdyC= z&MFP58;y8T9*b>YzDF<+QsBu~q>veLA7~7v)s4>IWI%CiT?t^Y0n?VY&k6*Xt3ww| z$78PSTy?dDa~mH})#j1e0Lqmvdl*#NT6N*?VBZ(hQMQZSs%UPdRc-KveXZun&})M3?-a2?ZgP#np%6L7weATQ)sfd^63Q zw6184QKg3R|@-QHBuT4sw-Jj$CXPD;+?ISERfQkG?x zk$jIZ;WGSvqn6_f*f)e3W|K;o(Ri$-8B6;jtNFLf50@WTYI;=fAS|Rw!6Q{qUIxiW z2?iB@5N0wa4gq;&o`Q(D(PN^=VrK6ha6sUb#mt&uT#aLRUymP+znPrO;Q8gFE{VL_ zhFN84o!q;r)~(HHx((;*Ip9)Y>-f2{LJ}Wt{ukGXKt4BgDf0#bQ)BM`V`&j)W8!Ki zrcanN_T`usx-~NevCgVTJWln){&o5~y7yL+;=S`9kz4IH8#$1>FKuH3k~gh1%h~0( z8nm;!o3Z?3!{|M${8Kn28}_BL>M3z(^4+p15*aiuSm@^|ls2~&y%lX{gFjo`a9K|7 z&TtwilrbE*C}e2%3#a7lLj4sVKTanfxI}9>t&2RyD*Is?MgK%&{_WlI84sMQgZM@3 z1|mKY=Q#@ES1yp7qy&Hi9dmCk0$DW7_-TXu@UiiyNy|jWw^W$adWFZtsmF|&9Y_bt#Fe<6q(6xH7-DIY$`$R7#f(cg z45iV6nN5E6U>f^5e>8JX_Sps3xr~EHr_BFIq#fq8P)y)l5)dM1iyXQ8qop4~7;g9l z{t7?p4EEqo_>-Cr$=NvQp38!qOJg}l+m~nOekFr3%hlz39Ao9C`j-a){^h~t-e3P} z-8l60(>@We;y&1yzpW<|!5~FU!Q1|Mdg`3!#wSHuqV6ReGxxE~g%R_|H0dgd>8I(w z*6dyNF4gTRtLtGBu^7rmPetASa&BEA$Hi1O_SE&{pH=$-oU~gXHJ1JyDuWF& z3nn#UK6N9w>6~8XS*0135nSEom|59C0Z^`43G#Wr?^mtJlA?#aj^$S6kIk8VZm+OQ z(b9SyQTsgcw!fTlXD^PYzW*N5fVihrGULR-l>OsfKA26W9$8~0>(338^LAcA^1BIV zdg3VRR?0ORmYRhS#R`{uSl~Sl9WP~*oX#pR#yoTUNIZb*%UA&HI+B-KYkjyc^@b@h zQea(gcg0fNucVk)`#m-CZZ|3*tnEmb)EwM>MO;}BuoO)s$|)A7F*(bk0|FS4=(f|` zSm1JeNA~jhgT-|8wQrj#uNFJAMYz?~R2Ga#tJ7bzwUj+;izd)LW^xjrqNQS?^Rkt5 zDSzKD7pK3X(TZ+>NYP#xxvS}=8js3PQ94L-KZB}&O4Osu?bI#auh0OQmv-z! z8}$W)tQavENy=)b^fDz=@(ll*FcLJ>^^+;rH8PjA7$8z7}z3I zrTJ&EA9aK6etS^enTj$o($K=W#Dfsjq;2^O+y{+{JW4~&>?ERR4b@|Hoajrpq^&3M zOi6ir32J9{oe#-Mx=+3UQzS(63rY#}A$+NIZOd*;kUlQ5Ib`u9Y@w6U`|0G|7ruxNY5bwr_~|5 zLQKPGVLVn3C=f?aMt@meUjA#YU=uN2Susar)XZ4?5?dsVCP}rJw$xEp#dGhR(fdUW zDRVp@5I)66W$=?~5f zIs>?%0JqpmQvoI&vm;osY2%HwP3vI=CCZnnjZG!coZS#;-s#57y3p3-_~_7knBR7z zsfG#lXK5Oanoe*DpjtyM{|;sAY^EG%=(W%W3zwv6oeDQq9&Wt(#+pUeprurC)lNlf zMU08z;u3bA6HwcW{r70|*@#>!m;)dzk(1Fd>Y0JgJPR0B1Wt0RS)J{n+B?!nr@F@| z1&+P4W@mp6)_fu0x+?mtsA-N!WEOdUg!i3JeVU(JQOsp%5g&IhE>KK%!PGze#MMd6 zI{O}hi~1@(_pfMD1{+;DJ~sVI$`UhbLq2{*qcXInWpX1A^rUFW5kO`)KNvRKUvMI> zCp&<481^l&aff9q2kR|5CJ;@&J&Xsn<*vUC;m1G=h6^{4P7z)_;vLU~MJJ!>@8z-ny+UFe807&jLeH6Y4Xddrd zvajb`X1HH7j2>jbmwq`6!VeZO{~j~=3k+Su3s9C;nk5bc3#20Pq`qk=HFKP9%(fJN&u@B+;OKZp-m< zYy{z0-}J<5v=neU>Ni6f_TLO94qRn~gf-XX*DwIw!p|#MxDD9ymMQLAH)iYx!Gf>}Ln;?y0H;eiS*&9oD zdHQZ{A8nvUFYUtKezvZ22kq;kDC(JCU5XkkwGn9yK^{_7*_9qSas5lAHB=zt+Q>Z% zYVrzm0`nSJVa9TCD-nq>yF4Jlvapgk(RcV~nL|-X4r1Y`=PDaX%S7;=Tc99mIc1zx zV5to$xJ5uXD3gPu<7+zrg^kWIpTWbb%5Jxxav?`L*^k78>j1e)-}!bpbd70mdihcQ zL!JowG0q0QDkcCR@EpWktj9DFr-*g7S^RrE$0`zxDv}%SymVS{;YkkgmLjNFfN&oYuJ}T_~&aoYcCq`>u)x9 zHrLl)cvx)8DvO&54qIHlmTg#D9==tZ!i)_E9^wZKyJ%t+F! z^t{VVY?1%Td->v;)SBieXgRY^m9q8X-6Q+=@q-b%l$6ZZA_Z7MB1lm>5dC_9&%Kk_ z@@&-@uWqWmRRb{fHBn2Yjl7QN`*nks)f?qhGk3>=R-z@7^CO`d;p-@Xg}JgydTK_}Vz0ppO7WPIltg1vjMW$c1}&rq(>fH~-x8tRt`Tcq%g zMNSHFBWD)beBXU(+@o2(ScN#VeT|L%2zBgJ^?-Zbyp|A++WVy&VlkH+n8+}y`&SUu?3Tezc8}%tZafa zX=hfB?;#i23WT4nZdmRlO(&>9-J-PF_GZa#TTB%OTkKN7X zA06Bli1{>`#?;cx2+8>BqBkPG7izGr;(*T^S30`$V8D)>i+v#fas`?njO&S%cTbwY zfnQ5?n>cF|_>t(YMa*V_gDb)lq7Z6@U(UYD$Odik0}?P2*MhDd=ZSyfQZs1N?8avxNP=i4byGDnlt zV)7ysP0QZpidt~H!Y!I*TYIGbz9|8DV|%bb_@Uhh29w-QZq?T#C-TOoIw`378qy4# z;&>mF-!(V(D6zT^NpMcFH*Q{E*h@9HZKrd<8QMwv6r^wT+oLgx3d?2Ekn#|0c?<*6 z#%4%6;7ntDy%7DK^@D1w5?`o*WIVnQt)z95j3Y<(^V#(3;B0A<{4&HpN^n!Se|>XX zWDW7vGWO_VHXVjq;(YlX!Q}~?s+bJ5EOATzWlX4M*y^j;c^8P8^Xybmi#cMx8*4B4nH+Na2PEeYGwZ}3WpTo zK?-X92b!tlMU2VhUHM>w0TYBG8V0Kf!%{m(8aBw)A|&T6E-PdXT5y&aic5+i#7A*r z=nUar@K+mnO{8$h8W=P7Uls>>x8>Ml;hVJf) zr-NC9P4x6);EYGL346nH`{>l#AXzSRszl9^-}{p{}koBlLMm49cj`h)u1S{A^;u*IwH?q(p3|MbAdxVx)ZFALeI zyVOG->eP-kIaifnL5ZayA1H8wm!u84LnyE7$vo6Sv%NhQ0FF zu;D->k3a`D*LAo_OI%(RHa$)(#8W|c1%*o`SoPoUpPZ{OZCoE%kMh(_`B?vTe=ONV zK(SBQ{+Wsh`0j4vubfWR2oY4b>}c%G^;4%@)?;4DWnIHcE{e;ooy<0uL#BJ!s$9%A z&nt$nn^#<{aKQoR#;Mx2(L~wRIlORFT@)qQ8T!_)t546pNbTE1fO^4`D|ml-o2`o` zJDgm6*K~%;dRVri*4)w-Ls(SP(l@!qCEpY#$G=$qsh zJA!(v=nFe?bJ_xNyYaj!7v@g(m|He;X8OC)UL2M*I8qnLC+HRDy8-r6*@ROAB#F?; zOn=S=^!5O4u2#FfeZ3mcfkNS|Z#!rIB+_U;&udC`Gp|QCav3btGKULH-oa<0TnYQD z$UiMRKRB;BR=7tdOiOIt)N5C+%Ct97zg3}sv13mfu#GYH0ra9m0GelO8fMk^F{~@j z&9X9KTS?h~pP25AjZd-c}{Zn5{ zDJb*YJ70PyKeRbHrDbz>MvKOacpSYZ;lCp1WI60k_x?EQ1)tj*&eEa{zx zoYi^XOE%*tQs?%~tKKtXh?Kb#72#4Ko_H11{9(Ir{&kVuqL=S&f1TrH3zw`YJ$Y9P zgeJq8?I5Hx3E#m?Za#i#3#oQw!4#j*JPl=qg< zraG&_Nh|mhYa|#`k0(WRj7dqT%z?`OUY`^?rkvGK^oJ{z5B*Kf9LkI4o4duh*#>_^ z#~C#9PBtJLM`rE8jj)!XnZ}GC+nK~13@d6jTSMXeZBn^D15YFMr}>W3t><%8{;DvR zs-e7z#*Cg=20?uAF~hr8 z;2I9nh;TB-fO1qaWrd^GK@F zOVc>w+0iBDe>;fy&0;UYUx7r4(uG0NE-$Z_ur5QRPT%#A@}5nZhYaOL^%MQ|XPDwE z`*%_1?08qwJ|yargwjN8T&W=f3elMS3kEyYOjxBk+Cs!(OlfnpxG{WN9&44id1u6F z6Hje#I!SU>ID5lA;m03;W` z;`G?S>oyX259}px3d--6O1eNIpVi&a1>e12H}XS_?Z+R!_=^qWFR9tT{`JQnR#K_; zQJyw~?cbvD8@y3oLt#8OqV#t zMneSQHUJxiVyTBqn`r~mWLc-e#ArS09B22e-ulsCoYU>odmkDjR)j#JoDEcJp-%tSU9>l=gQ zu%S@CJ^XMA`qy@6>;Cv-WautQWv7%bd$hO?hGpHX4$;uy-P8}=V@JNQCp8Hz?+&Te z*Ub>iP?ffw`lY#0uGgmgp%Z7Eni)*s^Od40nHP=4h-aFixX)@8-jI4_z02 zkc`cp!ipOWlL^2$Z(~Y0c!Orw#+na}WFEDSV^HgTOhWRfTcsIyYgt#6#Pw5*J6VQjl?rWP^^a2phCxBC^Dq z5UhH{igEua(+S$)BqawPHS{?z8cCqa9JSFk212m zhc1RsbAm*`%C=BhFG+CcV%5jM?6qgRYnucpEt{KdN$)Vqh!I$q0 zTCOm6w6sxJGWg?bAtFmbTd0{N2eUxOjLcprM=;mbqBOp|EUk4({@RDIvet|Z4rOfZ z1y6W%oEKJkch@c_Q$olSJlw;L^jW65@H&z6H#!%8AuBpbO?RnyOiu{Wd;4u|wsD1P zBvH-HnqB-GP^WINY_AWIwl;-Bwpjn=@CZ?^P68;il@j@7it%3=g&akCvBxi%HU|0E zzX4;Sfj$)RqW_wt#>Osnea`v$|0_K4tr?^=!CgFaW%qiWNh{d^yCqG9Y?uM}-TV=M zogc-Isd;1#ZR~&ds6(1W``y|spp(*You|X?Eur(p(+q3 z*q+_p2mBEO^gqszAm!5M17sr%`sT4)c2N|@gvOZVyd^zf6|Ki>>Ct@rMGhFdVJmMv z{wO$(XtKMwy$3qG185r$qHeiYHvzv-yWLiaSBMD;3Si?>@3kH>SmALg$1d+v_t)=wEtG1dW{2S^X%zCplTKKoq z*}v|0h)wDJ=H<%Vc`^*1Zs!dk8Brx`5 zDqfj~pIq^~=2kY4Fs_%t?=*0CKWdj@SWEt{0~pYYHuW?0 zj)0N>u1d~m4dI5>k&F9OV!NSMS-NG}>j(O-=9UKPr3EZ-tFa4)LtxOJMZHIKhge)R ziFkQ;SHvGQBSEX-uy0E_i6KMgE?&3e7X$1A3dF9fjUp?2l1;&PGbi^ zK}*0aL|dc-)yrQpbL;2Urv7ybw_s=JD-Cbes*Y;&x{-DDqwZxB3_roI>UpfzKM69* z4~trb9AKn5MEA25>FBL%Zf?I>yt(MzC`e2%M(^Nw!kR(6Y)m#qc|@Cs6%G5D&f>)7 z)x~L4a({9eSuM@G8>X!N)|v}3KNVS?g#7Yi`5$v4NKk#N7LL~#M5 zCr?IqXJ;Vn}JGQJfFBUg>+~ zeQ1pcJa8;**fZX+;JV0uDx5ueDZYD8(7*mo;Ib-ximU=ji{a1HST9sF%ctG-$v;HB zIr7ulvorI=A!gl2at32PJ*Ts571F96T>|L|GxSO@34S%F5Xrgj>4 z3NmNX_B5$QHyxtf1DI{{o7p3Ot&!bYde|LlAd9|Qvymm((#r%gM!;O^DJN;gxGr_> zt+p(;jtG8p&5}=>vXs5QygZ-rVwm-CFkP8C>)@9U^3MCqlVfY8yrG83R^MfI?yu7r zNli>>J)LE8gHNY#3LgaS^s=iL5bZb9cjyQ&Jl6;BnxJNM3e8HXDOT43Jf%kx(u!VQ zAxM}jOtUG=G7+^&CCIv!fj73O%D&mX&BvVvmt}`82R;DuTscwI)qL3M8IDGDJ-N8{ zCbH*|Pq2`|XK4=mKe64eaUZPrcm+t{b<|?XTOsoGJF3?>+Za>-8BoE~5k7L4>sZ z#;~4dtjPS5pL9DP&670l?*K&Uiw70U(-{n~*f+F#&HW03nBlD_XLzbPW&M1qQJX|! zZ60w&HB?3Zh!@ndxG;k%aAEM#n#m)|BqC`0TLq*=mKwQt#XXkYz;eZ6DvK^_2%NVv zgHX;aEsc0nxdWJP#g6IXT~SsDtFgwD<+F=-D=$fnGQArTo0|EDq8%GA`CyXvK(LFk z3rjiX@y!FFMe#{M3u?W3kSG}tF%mg+D{X-?(0JJLDhoS@?GbI+JjoWYfUj` z^;wV3W)dU#j~FCc30+)V-4PBsG)fJlBz!T3zU^x6$89Sm1K921llyB)4bxKud0gq< zDVrS+LiL}?dEmXra%|keV6`fbPQIb|e;7&0w_f%T=jFO`Zu1*m&aNaKOXzr0&nP8N zi?Yo(Q;dq(mdQ5)FU9}qP)i$B-}8u;oM8uR4k%S#j^e(CQ`wk=&CLMhcI`;hG`A7l z_AO`ajX)&;Zh}7ggbI3eu9YRT4RS;MKEmB@qLZ1r18~8!*sYgpJ1{qnG%fH=Bknsa zsW)?+!B?+)^R^($PF(+Jj9j&e*sXJ!_?@~AVzM-KyurKLMb9Qx?ImJJt@RJ&$(UfmQJ;C-N zuMwk!*>q~1pVZ~%Je!iV7pqa@!JQ4Qq?idz;nN3LUd+R{umA&B5U zJvg4)N5)JHXk#EZ0k=1Eerx7(ZcTRQ)aYs67rkCUHRPHExy+A1-dOHvwr*tRgY=OKw_`CY;7wLPPiQ!w5E$s zh8);48yFMqk0~f-6ur2Hr6~xH3$Q*(V*H$RKU;3qJmhC%1Bx=c6$c9EtfI_ik#+gL z)-o-Gx=PcpEfb}F)7fsj8>BuJ|6NovcZtjQSK`f064dsPF4lqt{EZgK1*&Y-!>vMF ztd+#k0%fo#cnPPDVtdPdDCVwy>x9*PtDLD%m4AgQAxBT?RwnOKvYh7!Z$0oEQ|jfx zIIP|vO!}&6HU6KT_8tS908YBTwx}oaG!a9#k=LQm?g-bmc+S__sHDxVJv^u1ldcJA9U(76l)+ER_p5a z`TQtPO3oMO<9$Wq#l$;}#kEL|!&!j1VKrtWUcBjCSv;Gz`TggAMQ#D zO7X{XLY3FD=|qofV+gpaq=09jrV#?e&ck>JFsssqY0{gcIw3QRyE{d9b<5}OtcfT@TM~onWR&PR&hasZ*&rl3S66`SJRKtWu9E}jg{xqDItnH zziurkK9q29l+h=Xf96PYl9%jlcHs@LLmh+?OAFZbeRB1etV-+}dCh1*h7pCrV2Id* zyTSLMR*^;0=(HSEJ3M-KBNrs1f>he-ko>H)0 zObO}4;g<&*uTGCSH!|Y*g6HjJn2-&n5tikrJ{V@jt)GJ_?R&Y;ytonpCjdA zns7nkQ(dis5k3!jmV_YkJCm&_r6uOlG5fW!s@}>vF$XK}gg-T^|1Kd4kZJ9N+efc* zfqk?0dQPYrang%|#VwOs^i#mId889Ve=N}g{!bUn<}*F;L?S+MnC_HvJ5<#8m9^2b zD(eqRH(OgtSXZr)rM>87MdVMlL4-2e5_xvbq^pu|PP9&IrteSQ5Y`%0kZXh*nr+%Wuh3QEa>%z_ zm_l183s+(DE~JYROt{pP*;IOUeC!QXjlYsqVXB^BWO=3(BUSq2E4?ZE4lhI-JeBii zx@Ql8LAt1H0}muWtFa(;UI2cs&BCIZCZyJm?I8YCs6gQxkWPf=*dq2taJOQ za!Vr0E}Wj7F6|-Geb9~uS2*K6QEeah4Or(Rx~@TT(hv`anRa!RDM zEb(mdes+S?RRU_*2q3#clXTX(@95x??KCE7V!Xwoxij3)u~?XOa^}-uYoCf9TK*lw zr&tvpH3_d)JY8aegh9oND3eQf4T^N9JSo&j44?V?w7k$5$1_A$+u{@n5dP__$g&HT z&S$|x2XJ^jNI&iS(y1N~@LqwXIKHT6T+XS&`4PVo=T26zaQzWqrxnX2jZ{?fURN z?)6JGXD6`gd`X8VB{aE7B+u7e`q5;clW>~&;PiLR=njhHsloi;#3H6Pg4;`fx=0>J zJXcCT@_nDcJG@?9U`qC;ZuAynxU(P8tfX-NP_St6B@HS6_a>2h%|l0nRQQuK4c|Iv z*DjC5MJ4KsJd1aOlPS533CZ%lhlN&@iB}xSp-~6sK!TzprODQ#It^_@j>4%Y$|qM2 zn-CQJ8w%^bb_JBBALinYlxxOM46wFc?C&5c>ZR_D@cW&N)%DMQbROLMye0LI*8FQM zX=_{RD*4wfu)MMNgIcBBV*MjwzhhbO>w#efza3v%=pQ%V4mhE;iXBg6PHaNpK=_8I z3&Mbn04p@X-Z;_iDIOTA{?uJi8`l8xn<^(+3pR@Z$Ej03XoSpkjVIXgUziG7YjAsVjN zTb$aBqNJ@)zeH8zLT>Ya5z)SL+CtPasoGpSx>7ZqXcnm>je$5;bdgF8RtiyxH*cL& zEpjbIC%R*(CHW~G-eND3oIgO8qG}wpT+h1s)q-&x6`sws_q^09q6Lq)^OQm;JX4YG zd)eolSJz_rE6Bkj&cVEb8l+)=mh?TKBFH2qH5v>4Kc57h>ldIavL(81uXn;x$QquZfhd^kl@yW>SJ!l+mlaz9(T}Ov|<+A1qTQ(+lHrX`t+EK z%h-K9?KPC+_VdHC14Qk~r)~psUZZ_0*wA?`<<4*@YWDx?>{ZjvCJ%C>^-ff@&1Y{J zv8ClF5fHCt31+zE;clN{1)VqQXFX49<}v%yY;DeKVBKY#eh^zZy0Fw}b3o=44Nq-u z&s&?_4a>;1uB>NDw1~85U_IVX9pIVpt9LFPQ0?3;x<4c}xWhGfw%s016o@A76;Gi4 zY_z@hr^kuV!}_P&z>Y|K@7`_oS<2Jz^)R#ZW}}~NhT6|^bnz^X1nuT6lK=_@rhEN0 z8{jro63wPAa{#c%>sEnuPp^pEB)+~rcAfLu57s(({Xp$w(hs$DR3d6WOb=Ez0BzF; znyCuN#3qDiQzSGBp1^n9|1gfGW=8!N{JJ!v<5sn!=7-*@C`oy&4u5Y?6CaXvd`$|T zkfaS)$O-S5XY7a(T}6FfvT2dbSxPs@OLIuD_tmr14Nzoxrxha<*({z6A-vg(O+JNj zrfgNn#x{X)*dn2Q8uNTTi&ZN1wzsKf>TO>Hv-ifURFcLu(X8DjQzv{EmYekS6pV+o zouOw&2Fs5(>3P#y0i|$Uxr5uXaopVniN0t+y^t72-p7`@$U`H$N4!8E9Mgdwz_Vqk z&xgv4&J7h#o3~im;rZaNsZhLOF9{gy&7yjr(?0 zxWyYo@2v%I;NNcoz3F<^>D}0A-O^?W8*)$N{!QK^=B@}q zz$SLKZ|;-%{RGH%U9U?V#&X1!f9)R?QrdL&ZWc-Q?FzN)kG^^3Ref1_^VR+G6NfF6 zaXF7}g@3sjq;oxvr}&|qPu-tyy#MH%)3OhLcNRX*rv9(CWbThyrQ0M@9e$P5rX{%; zZicipXq05^*=fBi-yM9=#mULuL4$SO9)A|J6u^g6;e0C%t z(DtP}SGEixShjIw+lM0qD&z*o2D>QLwfY)8mdAkIhMd9LkuH?VwU^mf_69_{CBItL zt0HFOld}m8#?vzS`n4vPMqjxNNSt=Jj+Nnu%UnECJiUsH?Zk6amfFtL;>FZv9~d{C zb=yroBDKSm%YUo`7wO&8e>=T8*_&Sc``y%};pBLNWgG`qE@YY{EejJw^KNA$E0Z1F zy|84RcE7&VvNhG_!2IuUSb!hMewor-vLvh)LddB<5W!-V;}C)j4kY{Y{fglv2Z~#a zCtiuM)Lf0RwEH(&6!X++qU}V9rfzCycB!@Y3fHT8b2H_+Z=V%jh2v1v5zVs-Hf(_~ z4j2~mEzlW{jBjA6h#Hh_&0o*U1CQiwlBXXytTr=r8^5;q-tt zmOUoBxn%DY0w_GSQtGaUIHt)qvs5iEa23#VY;GQ4cW!vY=?-+yt#Gu_b7p#DsCNGe z&!e9Hb!VpKff~mto{p7-CZ|eR>|Fv4=F6JC<+}N8Gr1!{VB$(PFQT3isF`{dT*M!k zH)D<2>e)(GJ*_#mVSG_7JqzMC_f@iPsPX#$)!j(4Oew(@!&L~kth>66gxk$>2uI$F zO;YO>XP1CE3HUk}O%JIDS=0?_)lMsp5^@c+WiY8!#yktyn|{K`jqzKnW!E9sB32r* zU0j%5Jy`>jtKRYSFh6IeI%gkwyjdK`fmVE?WKVxoAfS(p!Cb!AP^CtAFfu1e^HIzR zm~2Q;bs??BA;%4kFpWtwWU%s4qiK5&*$3_?Ajs9|c*@Jw))t+?I+T4W0S){Ho`krT z`Rp}vYng#7;0*67?7nEC{qZQ&-0tJ1L#8gcpv(gloQGk^{GC6^j007ojOLRbi#;+v z+7{L~E|{t*Z7Ql7I?HgSV8Je7jN z7K>}}lwSN%?|}8zY;2~;2wh3&d90%BHYMg_$m8?YS5`edak^4Z zP;x{Xc&&Eoo}v&;Y-%U#vPr7>IV{Eg8P4_~cS9m7+w=}Im?p6|)ofrlA#UvXu{=nw ztFf3w-+XpD(g7vt-{g|%gYGE?FKHb;-x&U{yKV@Hq zmQXu$GEXj&<~&LFuI*hNc@HIrQm;W*#2j<0es^}E>%Xj`v|c(eRr>M&89QOd*Csy! z$sxkdy&1#x)$lmY)Bfn24Jg^O0-H7S@|-FE(S*I_u^p|k%g@RhTsBCmlI#H6t>b)T zTz9vxmbv^Hn|V+&)}^X{ZqG#%W_bI}myaHPU-9qP`uBI=-+nNh{&X+jrjk-sgot<> z@Z>0xMaBltJ+!(;%Wu=XE385(apU6MqD9Arg15YVogNKk{}#H2`4$b|9(ywD*F@8#=l!D}v%rK#zb%+~WzO}_g_tvrfq{2t zXd~){?snY^w){D5c14-n_7Zs7gLA_(7+S*qdGuiEoTmFwbq+ZGKP?N2<4gZg=4@;QXY!7P^;9UVfvLX>KsL!v@ZQo4XIxJ z-y7o`I?k}Ehq{gQtL4#}0Bh&f^d4$e#LVggQnjD&?K zAfX4rG^BLPUVoq@_QuUAY{x~n1-4gz#n7H3l|>aBkBX^9%ih1YE%`IfxJk_>B^nZ4 zm|E00R`;^)iiC)EbuTs$N%I=9&v~@yTJZ-X(&H03 zwZkH`(-EXCn6XarKsI|QYXE5g77=QDk_K$* zh&1FlLr$p7yCN@uX>qm5 zfwl_e%ot$UTAu@Rwb8ElDyNh%NfG823~-Xk<>VAGi(7?x7?CYru*npEmG+2NN$&UgN~n`Y z*oYKd&`1=qTIYyZ#7$rM9YhRL4vSDWFIgTAC8t@n=-R$C5Hwu}i>%%{{X-g7=Z0Aw zOkvQfk_?-5h4iHbi1SkoGcR6(shEA!jn-P)UzeJ|`{zi?&?>DpmktEdqRPmN0wKTn zc*NTdX`Ip+g6tS*mA&lqQx-68PKUCa<}ko|@Guk}l4J*|8Fk+qD?w}dp0#!R4-1iy zIYh$_)()wgCJxr!)&ul@Z4@J9P*X3!PUrSclb{^8B1P9hhMCp22jD}jgWSNmcy1SiFL~_DWx`%} zy*KMpG~P)&6EPE>pt`@UGR0a-MT7?pcT;b!wMLe(U{XFe@fulxCM4Ckxv4^usi$Y- z8cMZRgT=wQn6X-@@SDoy8Vt0xEd6tV`ZR-T;^XA_!!nvJ%Z)NVJv&Y7-q2XpkhW`f z!?yRrTXNl?(z=x8VaJUwkCcVo_I(^ z$<|%ttJQ>zJYrZ9hk(A{Do3pTDT-=5`^9oNE6%m8h%v)xCC(7us8l?=4DW8_-QCq# z4X4a#;mp8)$1xzKFK9s^MGf&raDDKJ^nt+$Mn~om=krV6QEXDWto5ub#8!%Qjck(n z_RPZ9%#8P2K7ed9BN@*=I{>qrbB9Gfy~H>rU)1ovAYqacKxx)y{;^h|=E*qYvzvV& zdm70Iw3KtbP18;fk_Sh!UM=@C1Lxcqi8*& z(5Ssh@r!oYhqqAqe-eIbzE1N=yViQpf+P1y<~vVYV65{we)yZu>il8G0B44^W-RNb zvICmZP_S!9mcz>I?^^lNMlVStQmq^aYkj{Xj^aUmWf(&4P2W#`A*&qeG_ZB1caGti zL&$?2AD!_I>rg!Cyj zaK`h7hKlP53rOT<{}V1F8wbMk6%Ibl<5k(AzSZ86yTMf37oJ99*h*}&+UbP@Z(&nLX}SH| zbnImhT?gcT6T9Xu>bWG#6r0$ko=p_w8*_&)M`orcyIxXba4EQvbQ>6Ne3~7T%lqBN zr}JeVTK#}_(p*c*@i&1aY{)2EFilN>loy!`0E*5;TWOAqE(aTtOVD7U=`RLLL+BbT z?>$YH6L&p@hjj>u@FkaCp2=R_Xdf9(>C7EX%yYY3oks1vI(bUBtW zYhmNwNZq7kH`q(Fj|6`6bgi44!Rtw^9Up%VxX=0K8hBO0jql^%Lq_7mNGdxxTkdL- zOk#uJjymU$!{LdJ-=7^fb3Zp$THU6oNHoK*Bo4@ZIwh-{9sMiWdC1ldS<_W*v~g%$ zRU8JFDnv_f#QL&EpXk*%(PfJ_0{7h=S$&ejrHHGIIHwaDq<@o*H9Jj z>)LH;jQG~|A0`*C_bgkh9E+7B6nC%%cbZu+^<{AMsIM~%)>u3vHHjoi67M4?pX3Rb zk-~1(MkDoMOo-EkxjNO$ht{1BxzU1ik=p$2sh)&rKd*3gielsMFaJ{FLQUp5gUmZ*$cEY;(_7Zo~IIuUNS`)nG!uE1KK0ZGbx!J1sYm$~7xOm}v7G~H?OVS4r z3_rpMBc7e0R0`J8Q^mQYQ418-Yht4jN~QLO7mg#F)F*rPGzei&!6GL=MNF=y)Y5HZ zlJ&w0PWoYHORdUtvCw%+_zrThgMcjMPmcdU5Jp;*XzuEe)PGL@-EY*)5zGqxrMo2L zo9wfoTfw-rg^s~VQv08_U+ge~cUPE53m43E0azR1IGZ0uNgXIPn3j8?lRP|WrKP8^ zCZwFcPk-CmlI%#HmMX7+*55?|*#^LD=@^s?-5OC+R!io%yW8B_a7OM*8ogN6FY0M; zV`DN_l1xNzYLu})UU-H@jx>QI;?MrWP-M-*a}do@`nAZTtI>maNVx(G%9_9#R}>Zql(f}P zr+ODjUISf<+e+%{-XBYu#2J+!X^9eXd;9rZUegIGyfQSS<@VX{BS-O3ep? zkH??R&+k2a+>jMFb^?l|BA+fVVC;Ei1U|J;iv$Qcc@4C2BI?}??!s@k8<>gh^XWd3 z@G;3kYab$%BXWV~qkls_{ddV*CY{#RXo1mVqv| zrwMnLi6uI&IR|o(%jhIuC1)4hWMv`9YEUa?_5B|H&%d+tF7_~);#OSeD8{{|Ocf_% ztZYxNJyqP25#DRtzu8iUG41jiF5~?aRl-3SK6;o5+E`N(_OcDk!x7Cjq7RH7lhYZ8 zPVav7J&$e^NK~WIPH-AYfohXUfZef-)eu6dJr0{Ba-_W0v#B~QJ&@&z=xeDgSM{}h zY4p|2#o2My`4XqdoJbG33gX1LOLp>p|D6cKl+E6;Z?y|Y^8oc&dnJbz5*rAn&>oh-ES-0MqYae%( z5;&jARJG^HcXe=lpUiJkr{)kN;@L-iY*X%Z4CPm*gBVVVR65BHP~1r7y^+1z{LO}- z1@e=@Jy-LxSuA;sX8ECB*?=flwoxgTz%47!nHmd^v51JV?uvD657v|P6p`=1p*hr+QA={WDFBip~S2?kre&QKTJD~ZVO>#C$ z$k_tnd$WrFyB&0Zg{oI0LU>K4tEuY?k^~MRE(sd$MIRyKI5WKF*{>}b+uOyj&nGzH z)<9Av{t<;FuDuP=a_J5cyt10D;5f?FAFSLb{#nI;y22S5?-*&cxxXeSWap8lkdegU z6o;1?Ev-Yy&uE{J{9(YvfZ#+R4^Cd4u+#D{YIS6}ZfJMlcIk92=B^(J$dO8(~0MR({ z>(ByGX3tda8gLnn-G7JW3q*CzjpL~tz!5(fe3`Ey&v5i4BG?77$ehg0D4h6UY+ZQ) zRIaf|h-LuU7W`vJ!hmk5PflJEOY-wX^+G9MjGR0~Y3#Y7-QC5^Ji1V)u<7&3k@nqn zFQa;)3n_?s=VsGEbYVdEjnq1;5$FsYo2HKgN+S6!8#`??8}<5U;s$INc-l`U;uktg z1$M{tPeSH&N%J#bd85(n8a>Pj3b#bQ*GisbH!x#xRkcs%F_71kn@i0LbMr(E$_s=r z67no&Ui%X8wYPCh7P@bqUcokME%cy+S-uU~qawSKgt&M%D#kqEf~kAN@B!>K&zNA> z!E$}BIaV8oGx2fYy9Zhmt7Ws2I)QFnx|d@gYNi?G5w@~7HY2GldyE&>JlkkAr>yD) z7>fKkNNoL|Z~{MX%47y~dFFJhQ+rH>>A5)DmTyK(sZ(HFu-dG4SSQ|E)k;of9~YOR z$E_~Jv6~xPq8D*F!LfJxx0*xBaU1#|@vhg5^gnSKuM+}u^SBVQp<*7Bc zp*s?3^m&+tzlUx6TZABF_e;_UfnS^$GBjLc(6K+|g1WnVCw?8_tw_K7_(cPj#H}YBi&S)kVVA9KlDrYi_D3>(4jhVVq}m z;7+x5?S{bd&(lkP!=7M(qv^*dPhP!!!6Rp(s1fgaCVf3O-<^)S)#3ZE3M_ z<%CAYUXYF{ZmgQ@?b}&4etNz6tWiAN8sjS=1-OxJdm<9uhZPtDd^G9StVQX5=%ooLRm@f&~MJf7QSxy@lQV7ELQ>vtdPDUmfty zVCBZ?80e~mh-s<}zCmq^wgY0Q>tdGuy)HY%xV!!!&W)6(Qej37<=+Z<~KE-b|f6v=I@ zX)gqXC@FwRl2lzBy?ft09{sh%sAd{g(!63rsi|Xii!sQFrMI>I&MkrIXTU4{(`SI& z8Vtd&rO>**V7DImvWt{@ z4zjOVu)pGep|%i5Tlk#Fi+X%?5K0N%r)w6C8g#gJU59g5x&n`dQ6nM8<_ADM7}|5l zX(7bK3(n*9J$05}25ZM0`Z~#(#6jaKlZlY~!{tBt05L`}M&tt6^m-5;DYQ>u5_f#> zHN$FjUEx`ZWuf=fuZsrSs%+@H)zR*thc9u!>I{4zLI|r<&un1VZr)cacJ)}83Soes zT^>Bgi|;u*Aos{}#BX?T^wX=KdvE+G8@%1vZn`& zGU*^hItuYqGBJ@z8CD(hr}4J>Z6~KN;&TM_8Qf2&BMFFu)9l6inEJ9jXvY_)`+ac= zp;%S%rOy$<6)^$mN*q z!=KZ`vXt?>yAVB*6S-gu^Ej&f>(}C7Ih_AV(9m{s3)FYjwDc^QDaeTuK;UFgbZe38 zsFPCC+K6+5tqsvHnG{zJk3Jzc<`yGUY}`+jBqGyiD^Hl&dnEE+^ixqo#S7o~*i}R> zlP-7zsbiqN3Hb?~hfY`xF-W*;kk3D3b={zC-Gfkr{0(O< zsDx&Ei;k+g&zn!vTG@6WPiAg}ywGh6B`rucEKfnT#eA5aOo@BUL{pwW~3&2E17)lX0i2=X`2+f3|tT7jy{}{)z_cXu@n#I zXN0L9UXk&~FQa2}fnA{k!FInx(ABFOPnRNg`;T&Ys*2&;O4fKcYU_-(SWKvqI5|L#e^LqaTavqtOre3W?7&=b|Tf zj}B}}n3|=T?FVbnJUL9R^$wO4-B*wIk!9Ub6WVDKyLfzr;352Rhf zcjnOi`ol^rPx<+WSkCeryHe*N*A}wsm=1e3Y;A2V)_O9Tw3g|Ir%#`b5J~Rt?d{!d z4gH7p^>r5b`0V1z-TnRjX7LXzG}|7J`-hB1O^jMj_lK;R$=dm_gq=HdjqdXY-k8hZ zI@m`(*qR1n4uT}G=a#`MJ$!?qBywk9DfxE!@goSp(<$h|vksn9!@ht0*i9wxAsc*u zP@Ffifb4&u(eMH^eTcJ|D&%B3!sQ?Eo^sUkG?V3nj61iy&nD6;HN#hmYyAZ^sPfDZOR`oi!O7D&MvO) z=j7sIa=k2%YEGhTre*I|SoK#iBPpn^Y#2ij82WC2pkqyZP8+2u*802w{$hG?wU4tE zu7YGGnmac94=-=bPlrvdB&rY3_D7n&{?l>kiRLx zfqGn~dV6Xa${_sGMd>XyLYL2v_CJ`f5{>nwv)|x7sZPuK$k1alFsI}3Ar3DRzbcTS2D(7bi0<1tA_WU{w@)(_`pE|`wQ-nUxj*rM@ z+is$ONr-8HW69a?PW4J@r;hgAflSL2$j||+n>Gg%^JAj9SqD%iQO`IqRCsXI-9>Y7 zk+;B@Ob?2a?{Al+g+7o@v3Z)EE7LCk{i~yqb?VjgQXH&pP`PB>loPPLQ;h|c+mP$A z`43AgyzeeYPy4pN-0E|74`~4Q2Tws!A6CQ_;qz+v4toXL$f#1>cJ)x^HIyxIbA~sM zGLElK5iO6-XGe$E6 zB)b7p(ru39zMTn7J^VgQzYVXUWhUISuyTiFI!_+j))bDaQWhc9v)Fd3y6!fIgL5f#%uX z1{!qGReIBmWe7WwTyYy%U*y|B-nPZs4ZD2;;%*R{8{(mFy%ibl)Vl>SZP%$bb*U@6 zln0H7gOO!N5@C%je)>#t=AMWh#A&z7%H71;^i&akH`yG|bFv(-5OKss8}I?w8;fyO zG(of-DY480RDahnYDknyMYhM|*%$iTa^PMb?H%EPf4w{+pB2;ussG-;hRJt*^q!Cx zsRrKBUH+WB8N0JL;i18N;q7;Fai$!(ie(y)J$}za<~0E&f9IaLocOx4<%jS_a!JrM{v87<<^#sih|!@iIFzUSpsX|zF@PuQk-rfj5R4GkgpISLfq z>B9rF6fVY_Cnr~z;&WfRySg6(yVS=w0oM0aafSD_s8`QiNVx$}D#pItf89a$BoOQZ z(P`U#_39iJZko(DwT2+)oihHUVF*uAH8pT!Vc;f|%!b4D2Uk4xl|Cb5#PpyKz^)f` zE#pIS=%H(#RXxH>Pzfoa(8)Y^$cn&i-ZV{Rr_*=nZd}*o5AhbD#k$Y&3Ph8W{J_s$ zbi)IoIUPw68*GJvgt%f%>)nUUdwpQbI_{rR ze%Zd-i$2P)g{h;jYvp1t!BOg#e;Uiiv1^)*>#z2dRWTEsL0u;#vAN}R6_wxgH0sJIRP4&b#~BC#vhr2Nh=69?XmV&&boFd{I3d2mA<9Zx;^ZoLE`wsj4ME)Ca4EaGea*G< zY0U&;7Rl&W?~0E7Eqfn;JHGeB4XdLe%ISgj#=IHEX2Un154cM;zuDZ58rt-f?omi> znA5-zIIoklU#4$QrG(GOCAU9(%`C*VTt)-|%cT5j#!5HZ5h*yYv(@#K1zBs9crZvp z{zj2Es1(dbPr}gWCRjU zvOl{d$%mWk8lNi8mMqN#EEC_uri*2@Yo9yaLm}_INsqG|T+Y7)AD7#N)l<~PMj95; zg<@gfq~XQ=C6;(!N%4wbfV(y_ zj4q31*k?~6qj#!@j~;*h&2PW``|p0Y_H_N(#`EgHvHHz1 z&Hv_|{-N;u>W7MNL7trI*dO2N-|wom?e)#gYUAzt(tqEo>@wfnem#1zwzK(a^r)(T zKi2Q1M-RXK=G!Hau=d~ISo!0Jj~}Zc!G8DMZ+}OH;qu^z)-Np@&`osuHD`E?bVWgZhHXyVDFnOOLK0n7EJb7)XMD2;Fe5hKRJ>mui| zx_^+VZ*_E6A|rDUP0}2 zj{GO`TW$Y&duQXNweDic#pX6?DX^cVM8vO7cFsQV_F0w*m~kK6!u@88zMs9IFXiWHO!g@Z?*1_=wD3O` z3`OsMRbXi!+?QMH&(?O<3aDm7wt_--CYXj?H9;00EY$rKP{qkyt=+2Z=4Q?$f&4($ zd98QfIZ1tu*f0713Ok_xuts3}j8n(2(ZXMf?qX0uH|arotM>8h1xk?;(t7EP_I0)S zYW>CAXB%47&7WVrezTE164OWrM`^e%*N1~ja(0Pg9QB{T?0To|>Jw4YlnY#BM}Gow zb6?AQ16L!^uQ<)OPjs-NShA}j~i{h9Y z0p~$YJ71x8nCod+wup1RJwwix(ZcnVxA%mZr)SXboO*$$dQQ$SuPgZZ3%x*Zqzn3K z=nz+mFe6_p`|qz#KZt^9pUfkaZ1T#PeZWkI+h{Xi%08Fshx?-~7%~jc< zGT(tSv_5I>W{J^RAd|Tg*Fi4RQV@R;IjN41aHyooeRQsjFxAbP*Vlg0M~IPR5Iy-i z%Yf+7YYM09mAEk^Tlla*qHDYoa^~-P0I8!qHo(ktcC`80k6g7S%;6H?U|MFTgL>35 zW}jV<@R^OfI?a#u5j2aA9Dbe(vwh@4q#^s3!jAK__pWBw)oVN~+<6Qv--ly75T9g` z)#3&eXU$A=9sF|-TfW_AGBGh z$~llE$VO~SEMV}C=%wG0JVWEu0dXG`;ZaY+!X`%rt<>yl|2-@Hi@YY%pSJp(qI!HA zBxw@4CVgEj6|m-JSLZ#Uy7;rRt9S2txD=7p+S3mls}Joxmgpy`rR;P;W?Xe?J113K z-R(28!it&KIrKBEUNSDWRz;k?PPY=lp(}|r|7lv@yU0V@*BW`$J{8^d$0H zNM1^IV>JA_vNNxsvsdO z%;gzsAqYmYbBNZM=$_8GNqtG5$}@!~;LV~n?kkZUS;GPtt2afzL1Y_Zg4_O`iZ^i{Kl*F zWCgi}u%S(HSSFvY4^oyLJg@)&uCDRc+MDf-@$cXM45MIroI9Cu%@R_kpoN4H;@O@w z!bpFK+0Av1;(D}clBC2eIf9jJRO3nud?g$eK0;Ms=&U;p=xq2wf zC|1RqMnd#8a$-}{m?XY%%teLj0+%vE9H|oH|E$_JqL3Nxi9JKN;F83`oq=MPA%==| zd#4VvWN`E&ui*Yd#O~j`4Qo z#hFAiqYc4aIM2_OTCx}QW^Qe|>bDl7jeL9D_Gm$D;2jPn3>)_aPUetc4@B_5;Sg?S zmMHH|6~fS}gdVov_d9WB6x-M4Qnad^%3YmZKTsgZo$72dJDM#qilvAP9@v{7zDTmt z+~GoG{i@a^)1Z5iL93OTH2IBoA^cvuTyv$ql{wPaS-~=BPtQe|)Wl48p=0e3TK(|F z#;cvpoj(sD*22Ae6s5zrVJF}j+)~yc%-bL9T8Tz)HDMp9!7|#lKiN@b%I-y=hw%yy zRS9U?yRqk?01K~7dK;Q?D(#z73<2cPg;=xwNs$Dt%c3yk?r&WCFQ0GzY2z8o3kC7q z2()jum>_0lZ+1C2%?j?B8-sn$Xi@;*scH!UeR*y@BcRjDOS|Qn>;b+K=D~r&xW8s0 zt;g7UEB7MvLK@;Aj{WdONc&m~*AHa;(wR?za|5M$`3AOJN1<2*4#lkiY`#G4GvD};MQFrJT%otT1b+54Q-9M8`La9g_N{@lnS=e~7 z@e;C95)N-o%?#{pFY0LLk``Pvq%%BkaAD)+79{M!XkqQupIrbhR5KjjSj%6zhCVcT z^l;?jwssvwT7W2%GL3ttR@vsc-hoqxoS>*-5%1lbeV>>Udtj8AxnwIIEVbF@51zSR zqF7V}{2g#K_6F`ZK#YWrz6h1Jsq4i7caH$->O-A-@w;fDP zt4HiB7cValb56pHbEgXTqgR9|Y@F`vv{g@8#}6d*>_ezh(-69mU~d|=9Mx?8)?;FG zI0K<6H(u{f_0+;D2#v*Suc;_ORgGlXWYO(E{+%nFMKV2CR~E=9#jfPLFI$i8|>(6)SJ2ym<(sJ3DzIb#banZjnnPpSI@r)n-vb;eK1h z9cZ_vA&r$ZZ}AL=CS{5+-A{?a7)KX3l@S*q;|-dfd5WF&v#a9+$)k`A>re#PFDY*6 z#{ z^V)Y9gQ1O?{F+0F_B%shiKIu|kom_9N%;`xC3E_p7F5tTthqpjmOUM8jJtZ0g5}C2 zU8g22G`d%v*^6goEvVnEnQVE zLnIdVOXZlUp6g~=bwkYLgqzu(1V>Ww=M~}llu`iAC?{9!R{r}=^+(V7{TY>chM0X7 zdlXJ*Nn}cclbMu$MKj&fM%965>@HgqB56ss@quanuZF61p86P%% zunkVS7Uzp5SC?l?UWJVmfQX)J-CfULa?vmf)>4cr{z}Ka5-RIPMalS1j(AWnWEJ@y z3d_BcQ}4xlv->qKCSyIl*Z5}dtAx~6ToDPgiZ+trA@EyhY?5xeRc>D;V??U58C;J} zM6Igg9CFf9cT?A6YuzwF8Ifr6@wT0+-k2vO`7YayIR(Z?b8Nr9%sh8x}4=HQ5v{%23?B*s1$X`Lk`-pSiVJkHHopCMUO#@-z%L$dK z$vpmLE^FO!XOL5Adef2YlTxdd>Z=MmCxgY}{Yvij{3oo9(2ONH>ne*Bij75~`Tc6; zAF6vZ{eSz0;4CZs{;Ng)+XZ60X5fkL_r`Rorb#rcy$v+##<{Rhik4qW*?(*3P@(q1C&|_o)Nfi$VbXww zT*)-@0ToTG1zxY73$>cIrZq%tcM`_ipmk4N2VqOk)F@V3tx9E}R&&v1#I%GX1w zN4U};61XhLJ2AK~zpNO1zF@gUT&8CM8;fT2HBVUMX`roXD9{ZTi_#C1!yUfde7W^v zbHnbkFW+qZ)7#BA!m8G4-#ch5bYACRG+JCkC~ZHljrMBz7Pi_bCsyxvdlWKT_BZ#e zVGhg6;^76IoJg!PW-9zk**i!vwrrtVmYmoKVP!Ua!SaP?bL`M(SK!+HoI0S{uc%<1 zypr}Oqh2qDO~c@!E^5qlBo~z%UckV}KiCyL40>>+WIZCD#wsS9*AAiaw#v8Jt(XXV zlurHU)b87BxE`}-F!9?Dqpu-Dnz_r6-i!4~&)pR$n@Qq2{pH@=T&Z1S{%)52)U@vx zajG{Vf>rIMPJN5e?dTR5(L|?KhqtKTgHhdaINR$73@^w@+hE*XhQd9RIkgZOjhW5O~5A1q$u zf^5_Y_RwT^0Z$Mexcw2xxsmvR6KB#oh(3J2j17nb)hc!M@Okhcb{0ahTBsexaQ&FB@)9w5orS~$R($2afMRC40lT=fRQ;s8q-q9A?3dQLB&6E13{F>Xt^Z`DoA2N zm5k7BO4?tYuvEDQRzLmpQ`KOBlL@Sl0*c)QMSN#BpS{_5KCnp0sWm<{YYgpXf0g1i zttE=Hj>=RK?$V!j%>d<8R@nk6Vs%)}2q(&F=f6A*{TrT%ur|yu)3Op&Lgc_SmpB)h z&BFTet(P9%4qZ!3_NA+#&A-1#-5qOuu~#4!K@1)aa;I9ZKJJBb2~`oX7n9@nZeyaGtpMKQOFP4@d$NWR&zd3S`al3r8 z`Sk71#`x9iXB&ks)h=22dXHbNz1-N|T3g>3KigP;VV|7TjX4!wOJl)g;}P{F_2AF=F44o~kF6|9Vp0y<2@5*J3$R;8vxrDNg8; z%)XwkDR>a|{^Ty+)Jl+G>Y<)4-v(WGO|(*>-NRsLx0c@w+&DOAnfKE6&*+82%R^hp z8r)%iZb2eC23jEEm%f}|?KQt(e2Y&COByQ64Xk#d!W!!v`E29q+n>klkhvGHf3{7E z-{a?-FE+OGae0e7-i-A81iW0Vd_24Ou%b*J{*N8#mCKX!l?W(VnO^SOqX#W-7ToH1 z?eg^M9M#8>JPw48X)UhwN+ymB;z$j96{6@tD+z{mX%c!@(e^g{_RV&;)5W;QXgf zzuzum<=-o7gkPI>&O1eu^caGLy?KqA{c#_Fgo7I87Ct!TanLs{gawtX8r@sH++TIJ zOi8YwaiU<+282y`T!Adm_sW+q({)rjn9y|HI+#0cu)ACUTG9|xVM!oIqrj*;)w_$c ztMh1U^MK(TMf?15F|}AaU%C9*d>3T#!Z;ofTMX>zQw6WB+;>yotK4W=A%R4jQ6EM@ zJ@)~s2!r%x3|Kqjn5$PKQp8N!y>L`<8Kn8q_R?#btP(Q8PfomZVhax46`O>#FYzNB z^z4ur3i#58%p*3`{U-$Q*0$Csq)oP`T2!!eajn0jYFYrcB#AP$UC8Z8vakxKv^U?#f)k3)C6?0n2CGQ0d`O;?c}ePzG5& zY%`s$I8yT{47bBYbJs+~UF$U$Om)UZsNtkGFQ&hkGQeeu3u0Qhvm|tw z-mBRe zD`M;l;F=#c!Zio7zgcLR@&G7@+RZIDfSt&vo5TIAgjgqZ0M4U48$ zDLz?1INI*|%iCA^Tdv3#HhcX0h8{5gIn?UDVd zsU`eoG9J0c*c6fRK&gd-5+52%;9)nh(V*sjrQa1wmlGU~A-sXYax}1=A8&80EAJ9b z+hr$NSz4U*H+-TTn1S})K!NYafxh;=UVe2RSUE#$yd4Gtc(U+MAvF z&y81TiMO7E7X4FTaq}9h7R}D$xzN zv!#`=u$Y|{T;F8o+H}{5{L%2+SEMT3e6^ttO%cU^@wMS!w=E)X8g=_No;povE_j-w4SD2>MFg6P|FOsMpY3Yo`yT&AMO%$tz1(^Ik9d!z z9|i>D^2RcB@j0nutN+1BMl8Nqg!|Pf`Fqf~dx2CV3n6YVM;wWAEqoNQ{(fSM;@0vf zGJJ;x&s38u7e^Rw{KrJ#m4u0O{VF1zfj2a*l^&*F^@ON!&O{TqwS3vX%>+ zxDA&l@|PhJ5mfFBCxW>sQPI94CopKLzF-i|b#@6XmN~7mX7r?rAj2mW)Xe;(pHy$( zym(UhNj<@O$3@5auL-4IkBkqNm*x(f>{Y){;+u{2B{?#F^ANQyDZfkvC|xVfv+^4} zD?471in5hLcT2c;VR?i+o^k7H_CC^{R`5IT5rnJB-rj}%LpC4Nr63-kRFi!oEw1@* zKzxjqjxY|EH;y5kM$d*J(Ff>{SR_CTG9#wnQ z9{%udDxO_l!N4=qK%7+d6KY8JTNaaFqIE{y}Gr!peJ+#mB|)DtfT40Ejhv7vB_ z9Pk&15gmCXp~e5yQUmh&IQ4OMe06e4-Lqp?NJQ8E**=Y`g9Dws2f1ncOFAuoLt}*Ab$Wb&cZ9Y@K^mT=DJE<9!3LAZ zk9bHTA|z@Wsu(zPqd)iyY0RyDWp2F{oGz->gR_scAAY`7e4r>ly7vB&IXFumt=>Pb z-hW-a|E7BX+v@$dHQLAnn$nAk4L|xD6j~M%Ud(@zem;a5&wsv32w8=5+bV! zDaQ_uCU};ktfdltX)K&6$iV=SsIY`z**}2N96v878AJ3(NsOw~Ge@D*GvCW<5-&ib z#$`X$W5!Tn0gONDOKtm-NXx+p(O(alXqQjVJa^o!+HlZrBk}wT=l1M^hUvklK^iAN zxAFFRAhG{d=_!W&IMMIV$g4)~Vp)ME?=B|iBpx=7q0-%RfbtUU(FL|}*JoCEzX;Wl z(qS4*18TH7A;YNp7bi%gz_5Y{@`4k*-G}KCsjw^uTY;A3!8xz=4+un;4CEwiX#|Q| zjy;rnm^hY6P^^r5@gFEJ_lVpXW}}!^6;t*}>f#_&n=E7YZ+Q~nQcm+$578NF+nD=? zu5XG+^8bu7nel_>1WV{c8&`Y!Um7|JUh>0X+_9;QoaR=Sc1ifQ=qKMfJ7#vY#08`d zggmqIcIlmv^6hBJ&YCTwAD!y*Q1ND7OFyCU6>0bUgFomq@`fy)qVc`L=b^kH?N6#- zMD+_1u}fMPVYkPJE^zb*j}^e#xMQz~^#UC=y5{#|3YwVu%j81jgpfecP)aKhjRb-+ zwz78#6_Sw@PJ;(L6hgqdpPh2^?|Cf% zmY+P;4n~to(SAk?e6;$qh*NNN8b1zkq7mfY@s*lnZ_%srg9f-i7?@oVDzT$xIozEU zP)r(6WUp<#F#ceQB=vU>Nv=L3vpW9JO8X9CaGOWLx2YhgYw#Bglw*W~M~_1~=-2BB zlx&Zjii-}wMgln9o1H^|mYnBscKwNnm46X^vX9>I+QU@ui1^TBwo8Upc2<8)f>G1F z0{l?|7YAZoN-@f{JkxWD>y`ammVcdVUtK^hEA;;|6<(Jx-z1o80QjGk{Xf*bdvN91 zcGtI$S{hv%W6S=-1aRQoJ8r9cX4HE0yYKYeQA_F>dAcQ8QqSX#=gu5Sr@M7WFLb1S z-0N|sLMo{^P8cv%1Q&z}$sYkjstr|vqzIW%6-ZLX9v~P3iJ1@rwlQP^dHfZ~=ezdv zch2vq)pJdiGc~Pq&hNLMYp>T{Ywfjx>Z8L7M5Zsqa%=nd*Fd!3WZ)}>!w+*3ORV!W zOd!J?HVIqt=>{{Tr!U2J4>_2G%z^WwU(lwYCg`PSp~cIpz(c27jqDG`j(buwn@y1zs%XEsR(Bz)UVY(4l_z* zY7I@*Hgu1q&)u!)S!g2BteQC2I~HX#O`{R#OQWp53C$%E-Xtqb=OBv^`>8Es;7#20 zCY_=3IJ99DED<@4c8K&=c#?d=d)nl%F~G06-<-B&nN2IfsQQ$#*S^^qqkPIux@k`` zed<{Hnu2a-jP8BK_04^5zB*z&DsZ@cXRG}%^lIepD<2S*dGu_oM5t)=?JG+79Mx%; z5SlSU_f`G9ZAT2)_9(42cxZnps!SM_@d)4a9L9$}!v_VaSvbd2owkvOB68H;Vbq>fl#jp1&ojQ%6(Td z)a_nL;}lkP{)m~*_s-NfgRgkROhd(Trp6C^#r`u5^?GJ%)bA_zooT4sJyRokt2(#( zYCT%2s2beP`)En?(0whgg7n^@|pI(boBW zOh$E_XptLtsUOxh)9CFZmgsix5@i~_eZ&%liscez`n=tLi9)@eCCU_fyDy|e-EK%{ z8hz`C0l(#ES`IkV=<2JB%M0~@w^sqCnJzou%3RwUN`gG)X6yaErUblpoE)|eRWn`6 zXq2RJrEaTOjKul7N?2vz=3FM0=2+N%*n(Y1BtJFXvR4cH2F4Hr)QR|mG)aqI8OwsK= z>lSKuuUn?(&Av0-?44nzF=-)Y(vd%*k%fR)OVaUy%#?%Fo5coom%0k<6#uTNw;p zy#?K+3W&)|5gn)89UN>GpmPLM9o^gAmGL~?UBuTde4*X|K6%9;!;OgdaCgHuIi>2a z?+dz2Myp^DvzboK7y%O8jsHXJ%+D~B_SCFl5u@cK44z~{^kuT@no~=!tif|vCf#*& zBi5B8VQ?%qVS)-{Z~J+*8s%qq<7b+}m)KUZCq zwtpFVx!}Iuchuj?qrT|o`VAo$y5v^x^=*tZLusV8<~GuKf+gyQxyhR_#w+eMLO=SH zdta|Y!D)B1wY|2{rzBnr-i4yZTx_F0`>|1wm%;g^>Y2>$mkFzcgZbv`V?XWp-@J> z#chts)VH~-SD}K*JsW}%d#F%hhy@q$egP{kny)LJd4V_oTVW&oy3z$lxSkiq$iA+4 z!4Ixyeh^5$u0n+yTsL*b1|-_AE1!A7^}INJ$|i!pZir$s)XE-b!vq5~vi84C1;;3M)uhiRZbMf{{&JHx0uI zK8%Q3DHxf>^~@{+Kd5WA!YZ!cXfDjXfup9bWMmfJ>}>;pC)AZptil_*t*G&ZJ9VXr z#KH()@QoQ0#<^BqrLuU&Xt+sT;lyOT9ot$C{G_gUh1JYtY~D-UqXKUpdn)(ZfVB=k zscSj08Er_L}^? zP$K}MP@pOmrUJ4Ma%V)aw^?nlAnhH32IOm3xQDtU_qHvvRHyc~UT#-j+LD@Hb&m*- zJ>pPwqNb>Avs8g^J=poC`&N5jTbK>>JVhdWljHww^LEqEj7Hg9zA@9Bnz%GU`AYFh z(b;OzEdorhduOC`KY!Jw%Moi%k3qw#hhj9ux90SG-z z+tP1sq~Z9>tLqCJXS3m0c|V+^Dj0`Tg*B@XigM7r-*9X(+(zr}U4?hrhac#aZAApS z+3?bbH&n{lzC&f!bfu*JaL+bD9y*rV^rSBRe=A_F54chJdxTYO?v*i>_Q1z;cxR>@ z*Bp~1qA3h0?HKGr&Mn6WE^N9?TlT4mb2e(*jc_9>XC~|~LsC?MZt-FB()Ljo6wX_h zB`X}j73&a#LX?y0Y!X}4CKgWQcg_&by4dQNGdeDMB^snI3?wvQIHM>l(qa)BOW;v~ zI-V4gyZVS`Y-^vJ*_Hp?72y~$EJlwwQnw!#;hbM`T*n;(K%<*GStzC^&Yw3FZ66Ys zpg#q8r;Q~z3sfcYnpKXwyx8m6?iJG<^do6Fsa#q(i! zaYMI6#XWAY-&a;jzQ3VTSXEk`PLm5g0Q{sUPHTfuE6}Z`|7EJ`rFC8KRB9V7S#1k= zA;PXF>}6`Q2nmA~NdmO^3=PrtolPC{r0Pz`!l0E&z{d*tF`m2N$(t-&#sGENl5{p4 zU3%aPTrmU{g%VS+kId*TVW2XiR|&(1-a>}DDzu?O3Y_aLV~DCUxD)(vnx2F3GmXbM zBw!&*ic6P!nhLfC>@h5jxU=u(n&wT!AQ9Qb9}zy_XNLkSNw_r~`tyYYSrxr{r=ii% zp~^-u)Rc^f({lSNS%9Oaa3<&t70zI(DQnQNiQiLMlRO?gHH9;j2ryP)tKy9Lv}q&L zWYhCp2b5PR@4+TWm{OYYBb3Y|wELtH&qQSr(?@ug=Pxjr)R>(hLBQ*h3Qrb0u-Nbw z(}*1~3JsgGW$3M>IV}Q-`3uJC46&9W4JN1&i1gHtV#!?$VBfW9l&Pr+lZ7@Z0@5fD z4!8D{Gj`_7Z4zY{as_gRcw2GXo_kMC*oNPOXn&WC#AC)F_^JeW(0=%acIt6yYQju( z%t%qZ+o1yAeCVT}bz&=PggX&7=Kp4f1wp4tA_O@k?f6G5c<23a5ws8Oo0^);(10&& zt~=yEfnq-Y8$3nNysjOFH)-rQPKZcuv2&K4P&~ruZNpEiCXcNUG0DKt{*vS@4Bz${ z_8LCVfgK_T{TnZRZ8?cbt5$gu5t1F#@$#h51Z@IV=-{^TJwH8mdOhVw`B+b zAcj|pjy-#wn#$|APWbKNjy%k~KaB75glWQ>!G0a^dMYokVf1;A*icCr#5toNn`&os z+Z+AT5@@ve<`B0UN9^+i#|OIwFEg$tZi{HEE3b+d(j0DXV(h|z3&T)z@o*P3HV z6z1S{MuOP&OUPjdl_HM_wX7Fo4~zwT-D@65>%7K*a1-1ObjWYL)*Vko82Vu2sVQ#- z^Yc-xq;yT4xM*=6$~W&fuh#t5dYfQKlMni)P+w4JZ>z~B4}-Nw@N=GBQeoY3yo?H7 zmV_GC=(!0aF)Oo$4P6wAM3p(+C{}GsWR`9^Cr&^0tXE_G+f zeDgI5O4l_rexx~=(yN@g9<@Wg%mKD`zXiAX78iT_>0g+!cT(+9J2{o+wKHuzi7oCN zZu%xJdNNY6^)1jXl+t%A#3+e<*xu(zpLKchq9L;1F(6c91&&d0bC8;)24`eMiaxY- z>f%L1CsmPzj5YKfWxgCr+E3^Z=8{fQTIwB=+g)Y55HrrcsJAbHTX%I6){zC-tQ@PL z&Y6UQ^Wj>WwMW{VD6$e=jmT4!t$6J8r1OV>;>90IWU#t4rWE(Db2^@c0t@7qdR4c{ zuU@m0remH%&G^GnW2>ARacUQWHTkRq0_FWSfQ9LWta6LcicnmlMpVOBy#yd|+x2_O ztYYtUO1bW^>SWLD9{w1sJ$|+BwkFc;h0$HJ>yQ}cwfLl&9_gE(B;R!SW?ZZBRRfr6 zoa(rjNUO&_4RqgNo|brkpmV$5Kt~kYD1?@bjn|sZUJR!azt`DiK6IquTlx(e;+5Lj zeZb)?!slBXy1kuK2|B|?{TIJXjsZ&=;hH{`n$FpBrsb-VKdnC+v;GfUOa(wlxE&_# z;350Uvp;z1hn|07{OVUn~{Okr@Se_+dBBviof=JX8 zv4LcNI<~XX!oSqM_LZGo-jxp?wUyS#n!5It-m6f!u<(k%Glf#)FS!?W+ODZ`9%-(9 zrNo-A5H4;9?5=&K2IB53*20(Hwln^3zw^#Zj{mthJ>`nf?ZZy_37RCUX#QB!J?g$;z&Q=)3ptJP3QRiUEm}tQo|uFQZs+21!XR6av|o!sS95v&BO*t3^jkd zlULt$$c)dUVBrLbbXPqiBl%;$o3g+#9F{JnK&WY${lK~LH#u6rbqEgicFg<9{GPOF zEgjg}l>}1O?BWKo_~coE-#xpoBWYtncjKqO>4%(;A-iz?Hpa%@A1}sqVZ(^o9Mhq$ zJ1Ir2^>WAVu(~mAjBvSqckbcdRV6vtZyAXhPwGGr2O6R2ke11qj3-pMz1vN+*6!{6_E$D`QRJELisZ5-|7u{0h_c7RyK&lot<*yG$-5WY7NerRQFjjv-yqx2D8kT0@rOoJ%0+P;~{1;khC1WNTo2T|S zI+B_I$9@WFiX}nG?y$FPhJ)>F91en6-8 zpJ(J0BSp#b8R{aIc?*o)S`t7RQWYtkfuOEbwDwVfad^R^+(jdb(4XeCb8Bb8Zf5NG zXajtNo9jzDSIBNwjQa-XZF#rfT4QVIKYCt|P4|6soj079QHxmA0pmrFTssS@kFJ= zd))SO{n57s;GOm@67#V`SutNl$IblkYcmpy2b*iCu`Ffx13$~|r}Gun*w zS~*mr2%QO5tIK}7+^Ied<3M^X9qz0j92#U*{PvEpVI#Lz`_KTOtc24?zrKzZfmh@G zc3WA?)^wup?kur@```=ExW$=CX43BsberH6%e=}Fovv#3vr^U)7c7y_i<1?!9}ZBa zwm9dC({$z076kZVmC&;t4cvk6ckzP2WV;yOtw*h$ZB8P49`7-6@h%%p zx45-2Z`nGy5!z;-Fku=}0WtXcAmkS>3nX4~MF5r^vz>LT~f z4$(h59QSLv6(vW|I9}JWd-5o_l|4Jd%vB907y}XWn0V{f7Vq zYiGg*xMwU&53g4qmdlk_w5YcKs5+BsqdjkF9fsCgcWj^8o7~msS_`LyzI6Drd9$s* zZzB7D7+w|^dbNe6&#kt%4(}ckzSa_L@v-SvHx;O}<8F)niO&Qf!A-OXm)6eono`#( z0K%iUxS&B(M6zQ0wB7x>M|qC!txVu=ZDx-1*5)@E;j8V3I%TmPM>^wXVAgk#9Bu~E zayPf!&23UxyPF5@=ApagZmqjpZFg(OEx3gZx3ER_F}LKF*4@&ETWY(dJ8p>wEEqwd zZsmQDz^&}Mue+~rxv%fJuh-o%?{^BfZmzJc-ENIZ{Z|f%jx=GVihsE9is|d+?FM#_ zfOh9cl)*uP0@$%uDY?1xF1C770ln+<_V-pr9VLzcQ^2k~YaOy)^|X#%0FBC=6u*rZe`Nrn1bzKcdx1CfR0bP=IY|?B3XHQuKKJ3Ozr^N zhIL2~q#twSPCzHpkJiKDP3vKPgI&0_H7kvkomg;>hM%ZT(z6`tYhdxPOJY3j;vG@1 zOujpJiQ}i~o8jB$PWTo^O5)$v!=1a97MRP^@o(b&KwnTMV8<^&><-XecD&6e`DB2t zuJlmGei#ReH=0LVXTgIrhBa)5#AakoYWud%C=8Q(*3Sd{ga&w6S!MBd%`)_qgRyo~NyFam*FCrvHnsgG zaw9d@-Epi~(`orCfVIes94??}`DC`NM<{XjgN-tYkh|K*_NJyf9xYKe&gZ zCC&6T$F=$0<5FDzvj8o|7mp!YU6sK@^vF8%*{s8_)Ij)M--55w)#Z^j7_K~-sPrtq z3}}ikn2zYeJVfu>ry5GKf5TrSxQavJmL_lz=%aq)F3>jE9_ii9O>E0(z?G(CXlfC# z1U?1x0Vt@}Dy4*JMZarYCSjOcS4b&{TRI_cb`Y{p%BJJ5C2EzE2%}EP&qTSQ!upv44?%rsT3Gl?I-gDoh)sb{)* z)sC*&tb*j4&R7Hmk?(r6l0EF3s}L^SwRWJn|& zw%l+j7qI`C7=k20G*N0qFW`$Yk?=w@Gzyanb*|>?!BQP#ZW1MsyOMPKh0tKln(A4( za^2%rhdH2+5MCEntE`czD`Jo7eOGzReNmp~LJwxS)}OkUiauT3zlAcBChXaJP$St} zhu1#89Gg6(QtVs+;7&ds=vzMo(EC8Nq17J3qzLvFrH2oMX}PB1+IP^qQohT0&qn=A zY0*$K5sPS-@X~3&*ag=%GQ2828{S2F>KYzq4G}%`5IOLs_0x|DFs%5z_o zbRMhM!}pE9^b`}OG)FNUv`0ho^WLsu21Gjme5Ll(xVrkWjN6uXd&RAGzqc)g&@t>O ztO1Y$6atY^bnJVCfG4)dbQ5e!LF##dNvo^l&wefXt&f0L>2C?lMy$enW2DD6Uo^+e zOKFMY0K)S)y7c~ay8x)dqB&T<7jrWbQWS?%66~psLe-ZG0hDE{D=Cflo3x0mK#;SU z$_tF24|<-Vdmu%D$o5pG0rseNHLv>UZ&i9UzXs|}JB&x`XF`{YM7?Ijbvz_CX2aeb z^^#!J9QLDmJ%C?8q~r<~;$|!!d&PwWgwPgal|)T!67%lMLYvSi zBV+XZHG*{3wiMj6Vv6;#hFw$T$%8k(1#b`i>~N`J-@$hS8ybvyBMK0Xh4uHBqqHzg z@2-$!iJ@k|OF&Rpw`?h5GFDhv5T=&QZ4uL9u}y$=iHrEIkSkSw?v*&Z!Pt^lDx+EE za(TPbTz^EQzJ?0+r)j_k{MHfZg)M*J;Z0$9+I&)>U~FCuls(7R*g)^)jY-ufl%y?# zt)g9z*LwiMX=-wXM85$mhw1qE@yaHI?yfKw)&d}Fbjwc#MKcu^Jz@EXBuay{ME)`dBq9m~xEa8OqkQ9PJgUiKH8)vBB%& zH9sDvK`(xqDo;T5)K~w72opF)Nsp8I+rPC6_>L|bIzGBkr9E5!{(XlApHRxrB4b7W z_9}fJr$QLOQxsJf{Ra@*GrgjJDL)}8BmK)%rgC)A(CN{ID(!{6Nht>I*xVNc<${M?+IM6?;(@TGdaNQMT}6X8`=gR_Avs;_oCWpR5+ zZcg@A_AR-!`HG3dbXtb)t<9zJ!`^sdQ3wa)pg#0t&O(s{B27%on$?p&t0fIIK7Z8Ms!zPWUdB*=HHS@ANiM+-4a z5S6q$zvE8|A45a&+D9erDqxy3qs~1>aYWB)d_89AT)ZrnpGpzdB?3ByQkIY@A&Ei{ z2}Uc`rB5kcR_+O4F4ZgiK?xHRrv?T`mrPoJQA{Ehs#VyUm&FlHto}Qm>{{#r7Ec>$Vli?HfLJ7rF@|$2uBPT|?gB5a45~VH~H> zq~|RKxdXNKmB{8Keo59edDDW5#wA2m5|efxcP-g;ZWP(%VytLO_YZWz3HL5?+YKT* zts3Apq^$6ylIZ&Jt+I@4jrKbmO1ZOFU;r`2Z~WD+es`|bbFdK?&Booaut#~|2o??7 znQLG1JE!tV9urS>NjRhOI29^|Z4+0Ahxc*gAfp)AV=TL=*U5zaA}&De?-;XRL=Q9P zroJ1kjq6)h>g}hXXf6n zR%6*7rSEzH{LJFcE0%%HOa6)J6-Sm`xoIvTg-OaL;rWR4fIjb|)arWeAE(Wl=O_^0 zZEnQ0cit1A<#39?9n-wVh*sw|1difsRlVDv7*~H;SNiF6=`Mu~6#g4%q?krkGhrgT zqc3lJ)*Cn>aUXiQjD3JBiwGRMkLAetowyFot#Mt7J?QdGTy560_Cg)+7Sr(j$4oG& z9C~s`y?dJ((IL8c!XD@DR*$GAJ%quTQD_qm%r>j84Qt-{zB%Y;HN+k84{j(gQvYFv z?QAyFt0=M;8=g)KN&XVir@%usuOSF4{G6=6qnpQy5|KdHY^Y_yhUME?FdxtT_B=|G z&fjw$9Hhv<_uE|jLSO+GLT$CUsrKlOEE2&MKXqu`6$Tkyr6gSsaBQ;lfKgBC{gauY2Sj>!iPHpfgX&Y4cFRhZaCi~*)Uti5=t}lp6KAM^A{LS5B z=C%yPxnzB%b5%dzCWQJP;k#i_Jb>eQ--R{xHnhzbfrT4b=T}zdk)Cis0;n+U!0fgW z^PJD{%e=xNqPc~cIZoCr&hs(N9m}^DURzjvgL^sQe><$h247kauVN$Qc#Fj!``^5n z^K;@L$K9>nwbs@d7p@+UtH6L2@(lxXZxi~^7M$LO_qDi2M_*fOK$!S*ecvG(R+d)2 zH$AQT#x!d|xvObC)`3N?%dtzjU(pTjv5e>``&ETZFv+tg-M8HbPJ#D2_i1L}hn%+& z11^3NuLt|juRZn@5BzmxWjncvaB5byT~X0BN5K!nJ!^={(ZwNKtveks=1lQI2ba#e zKo@Xp{bFn!0iZE%$nk%XV|>91vbCW^1TJt|W#QvCFO$U*HfQ5nQ5ctD>V4F8-X{$Q zK}jwuGb|YP6Wd4XYAp_976UH?S2@4(aXDg(3x;jx4w-K8tGukTYsBG#yP|NKOne2O z@E-Dh&76*Vy)zYfoc3x<*F#AEj65DjDG}0}cF6ZY$GH@W1Is)x*!Ou3Eph+}s1aOW zR*B2gJPpa8ac`6Tgdp0cAdB9$1O9t|aiZnAA{8Ktt|A{mpT~t0d3kEoje_*K(x@AS z3?&GCY+iHw5WJUMg+1a#8V`{gs8CUJIpDb4ow7<|-^O;=iHW8dkgsv{z`+R#GAKnX0dMi#=4NBrVw?P6@m+# zz9op&J^L(7mMy2Pgk$_Tc#x~g%g7tRebx&;cUtKR?7$00A`f@cuY&uR#VyHO zs{1T_ifT6{w7~4sNh{%Dp|J4F@981ptPd0d*4=7r+H^uYBUv2X0sR|tS3 znwE`OX^>$4%}sAN=x!oz8@u~0B%qh^C3PC5n8*{f&Nh>NkO!IN^pXVVLi@oiVw&A| zkdKMhSy$fxKTr3JerPNx?Iq&!Q(xL57k)Vz~u9T)29=s zHzmpM^C%gcKci+&XAl)^--`r9NjZYAo#8*|3Qo=cttfwt+{BAXXQV}cu@E;ec|IA! zL9j|E8`4YK{+I#UtVk~@u4rdt%p5{&3D6wwaL}*qCf!8$%!^i-O_Iipt*^*n!!ezh z{0oK}c58POI#IHL7Xjb$#Cl>f7AMvG&+=_?bd8tQ z!)rY7=hIXzo=fvh_%mvJFV3~`E1zuRM=+A31U)?s{r`NzrG^yI-LF~R$QVhCMtNw* z=sMVTBsL54LSd*vtU`GBeb8Ox90{&pbOxiV>|ga3y#wjBa_O2GwOXeP!Bz0Gv<_)e zC%fVPF9Da&^z@O*?ik$Fot^-u$f?+unPN$su>7SZ`$;FO|rsi(XcO4 zSm87GP%N-E&`PlGN25a27&t7J4kmm@{Yk1FqYrNuYKZ}PK#pgS67^v`moZOv<3rwI zMMl~qm03@jMS)vdR-2Juy|`Q- z3|v;pHucgudS3IyUb^MDT0uKDGi4CrAt1tR$`-%`J4rh@vJhkH1U86F5-lfVtsaGY z&ZQ)B?g#sr88{WOaNu*gJXNm(cJ9_#=6BM!d5KwB=vOXGKmFE_^)0y5qQ1At#W_xB^-z1x%+Jk`P zXiJ$F6tkef;%-)(C*;{%X3D>g?0wnqK5g5f4Slln6k3SwP(;efmf5>QD=orCCTVZd zBgU&cjS4c9b9xUJ709^3-z~iN5 z;-m1GgLD{0))RRWTwK6bQM|J%11Z0QTryXR!M&ME`kk+(~3-LsHm8R0eal;vlHloob8w z`LeaK@p=PeR$wdRzhfCOm=87|GS*uzvk?*EF>mtZmiv&LWn{cCrt zE0t;qb@2isOs1n5&m_Y#XW`-MiOkeesge>;Ky+AE3$s$1`|Q$UjVnZaQMf+2hmz5x zTFc*$k-{MYn-Jj?K(NImLMPvD5S0EDeiImLIf zVO88E)zB}Sr?Y)yy3^I4iM0d+3Jx}IPR8MdA|@Eo=Dl+UXEhznU%}5Hk%dpp;O9B+ zw0*q568TVL_Lc@qG$ETLnP_E;Fnw8=m5mtAB(fhdf`!kLND`y zh$X;sV2NV6_qdY^*&{ZrHOw!`3c5w|RGtGgA)_xK=itSnCft?nUOyPjVZ zfK|(F@6JGEdT0vw<;6%W$*LCf?`b7W8Wqe)Zd4>~CS}MaWGE2`(;WRU;}RAqK<@lI zCe8^Ov{cCgPSKs&o77CtY|vDWf|BU7%KM8}(LB^oby-h!(pEpjyOu1-&q~S2gdlP? zdG3>zM322j6P>1gTR}YZ^|1K}wAW`29v&?2Ea{^>5s|bwNNVqF+AH_~wGcu#Z{`~o z*GJ0BPVLh+ct?%j_y!aJ2-^91SB-5ZwfBoZy=vW2vQQ;bw#l=q9Vx3e-ONi}-(Cg8 zWGZ=@7ApvZX6V7mnuC}GeX~zZu2OL>IqqQlB1w-^9*N823%-~nsUHU}Y_eqW9T35& z$@3Zw&cck*-m2X=lO^D+p#!_P*ZuabeMA12j!Vm6DlOXq=iNCr0PbH1BXAYFGPeuJ z?k01+)7VlelROoy4aWhYM<#%wAFS8j*`#Jb*S}+k0P4l;c$yk%;XY;g7R7qu9 zQo#L;&*Nw(mgIs;^8a-&(q5lgNqWxd8Z)Vfe;#XvfNi<5epWiQv|_Ee?7`q(v{E2K zt}m_75CZSFQm$AYg@8FdP1H>IcBfsLv#_yW^~g(Vde1KE{IZxDXx#%8(?N1TBy5Sk zS<{5xrZIJSbZT3-afvO3(8=a$R=l#@;l-J=P0bIkcz<(u3mrhn2Wn&>dt%Dw?A)IK zO;JKfz`+n`yQVwD4<5BZ=GI6~Y8m{m8>75y{Yv}f**5rB=d^eQ|DfSVh2LbBR9 z6+$<;?_won(-LL*F)Tz8iDxP#PRX^Hag>H8wxPAHShTb{EdY~;X(Q1j zqYWx7kx_$0cRcJ7`_Af=uWFh^Rae8j&;`|4IYKiYCQ}$D+F}4%E>%Fugt|5NZOrnO z;~gKwioK&$M-SSq_e)kQ!(|CE-!cac5{I$fg)AQ&v|5$k+E;+;t(ATO?8}E^tjVjk z#KA;5MBP}3kFr^hs+ozGfSNtF z6D_V3vTv7z`@qx!li}H>QsGXd#pUv7JEmv)M%>jKZ+c)cb08j9JxX~Wjp0}o6Oe%e zxhQdEl-6w{`f9J)s8@s{t%<@V3qBJuUzqnz7giGzJ9?F^C$B=I_AG1?d>N7D-GJy4($b#L492Rk12gdvTPJKJJOChJf6iNB!8F;Z=pIyZbj$ zYFoaNl8)J{OhGAWK=&DDqkKFlXuYtCM73IEyyhkPgD@5LQo=-+%6WV>cL*69#ZSAY zgkIN0qhhTHC&iEY2)8_d48d~74iI*?p}LixVypduSQs13A63Jj>~w0Hzxs$aUrPb9 zsWY1W;13h9)Ro-7$Ud;&kQ0r)^i%`x*J%|Yy9AMB%yg8$Xo@;opI5uIshG_(%W7{L z_O`el(duPN)r`n&dq}531%6cuj!c@xd%GK6TT}=_f{QnP6ubIr!+9?kc_urpuF56; z`X2n|P+48$dYVE|Dab^%0rev_IidBh7|?>vE@)kBNwvWfE{%prpE2c*^)sln9VuCJ z322y*-bRvsC5Uvb?5gyyY}Y#mP%4HVRqqmOXkQTGk*aALl{_Bhgg%f4tTxMsQJG4$ z(p-|7+C_p9f&#R*pqv*s5<7a8s&kK^4SjeqtR)Gnqs>HIo{nFiGsb!9I}DgynHOLR ztr>E7-Lg_55J}g}ZBHlU_Cqm%8Da&vMcyMM#8lqSA>O#4O64(TPCdsNNz=wL$;_C2q+rapX^h71o-H`IA|&>QEE%sZ`}8Z# z5lZ@$RbK+yQdmV~V+@MtD%e~eRU+65?>@e;%7=uuN^%UIB2BzRIA3$Ac-RCr~DK&+ycaD3M z_A?-AtY$7pFUVJ847v8`sk#pNYFasROC;z5YHM_6GBvwLja#TF2eO<01iUvNKp{=) zo9ij%Rhpt+T(83F-YMJi&uwfyb<1*j3cbY=b{oz^%?t6IC(E*~yN$G9aze6TvfP8Y zan{*no)_fi?(X}CdkdJieX=8zI!nE5Y;wa6m~enTEp4tC`?t`C(mYyu5lo^z@eyfr zP-_}w^(sqI1qC*$C7wE z?BZDO8xaf9C}Is|rrF;=Byo;VC)I(W>9X0LI$iS%Ot6q7G_QtaSo`*=&IfeUr-~&P z2Wf*akH8FL>#dah1=hYet_BD)0P8sH)2e-@w$Hnk_CV2#3|1qIU^!k3QB$lmQ$N;N zG!MAGoR;3sEZ8Nl-WO5{LF_l1R56}Y!o%~lZG?q0QW-Ofsz9ywbs|Wl2i9IA$2mx4 z#coIU_f!e8D3!Z*uOR62Q1=TGArzuQy@rADpkOS7i*Uygj3|Z*OX&ziUIbvw?ykSl z+IqiaJuAilU-#~)-TkG*&b_o{g%i>9mPAAvcPQDG+Qj?(ynowjbCoS3#hDr|3LsMn zMn%9YzA1+r1tYJx>evC7fHp0Q*Ukav`?a=)6Q{Ouwu$>iHf249Zmg+I)ac+_pev>Q z@Uv9Nkji@fQYviB{7$O>%mX2b6b zHz2&b*8{r}`g+-0{gw8?4Yn!w^md*k043^I&2mceU!~w^e4j~|$DI?ON^bDlL zIVNXkV8-ltNbjgtz?T*`TUvB%dqzjn1T$9AqhD9-uC_+DqIN(zhCGM@OL?le2R$>< zs4B2##WEy+P1qlnb&~u~?wgD`c3D>M*yE@*;5Qh!BKjEhku*4q+DVz!IJ#mpAu_WA z^+&w53~0SQSUB+<16HI@F$67?-PaeOmiM(P8=Q}5{qpdcqRsh?HiyFvcwI>%?UYWk zLB<2ldl=;eZr_UaIon=A8DNKoOmj;vwJ0KD!UlDV(AR0+Xc^abWLC&C)h8n~BsLRyn}0&1YniB(ki(`Urx15dM7((=fayLpv%xRkwj1R}E9QN;b4;WzvSk28xDbh1pzF#@6J7J zuZuN7>EOYXA-~2N=E+yhFFFpsqxo02;hD6as!;=9t}E&1H$c7EH0S4V{gJ3T+7gP{ zdxmN|gE=!9dlU#z_n6d$1M1xyM0++ALCqAV*cfxoY8DYDJe$nxP%fV zC3sw@F%CQ#CyXzM{l$UMxoE~1kE{_MVdC5DtBjlpT_~}xpwDr_MfY%liiiP803T=M z*N_*AFWan;vY)_HvNVs*2|yfhfg>oKWdD|8Cy2f_2*ZTY6F?fBfH5h}8{uDM3&AMo z8Qe_DtAYvEEa5op#kLu@8Ze`WCcw5wzcXHD6yiB1GxYF-_TrjstTk+HCmhoW^ePi& zWN~Rm70Fv!Z&Pf<_~3Y{`0LY2BF}hlzhiVVFpifCLQjl66S%~VM2%LRZG~)hma4di z*UaqhJ%Tz8OC^BLNm9hZ?J<(QQyVVJ%FvK(g-Ys&N&BI#JUIm`qe>&G%+MryRe@Vc zkz#Qa!rN5c%WBp&h76T?W(*TNLW(WbB?Kv9R3NR2*U^9Ow_rd|%K_5b*-{7me-#|& z3hW9v{DK>l?XVb=G;Yi`mq3H&%xW-jR|7g?o)lgKml%vMp1QJHVy@umPmLLHpcU?` z)!8zU%_+?9N}rO0z15?)a&h4y;TDAr&%M#wSL6iQk)@6#YzoXEDZ<|5?H5GI7hDoC z_bc?(;kGYqFw*W|AH-Q={2`!%+>^GJM-4ZQGyuo8jxwOwKCqhBz}pmyWP+TH8H-^d z7BQwvs91l~k~~n%0tr-NH+~7!i;c`SBh6hS{J>iFxzk)J&I51N0{A?RVRI`pTBc)f z4uFZg)_$}?_?wQ2l^nzqC~J@n0|rRl()}tch9A#flw~XNDL*f`vn56-Q-OEU{QUkU)wORd^#UQ0Zzx zjH5-UEik4!T(et&q`n9{)4*6Ug)(~*{34Bjrr@A-8ff%imR~EdmIy_<>*QKK$?= z7UJCPW&`BaQrG1MJ6)F_Y!ON2I`_!K>ee1HVvPBg^Z2Ho#~=6x)AtoC8{b@49rpXo zc=KCr)Ibpknay|Z9&(P)E!Er+$X-R>FU;e4RSHs#5x+j=t{Vuk?r~G;USx zkBU?tbpKxMX`-h}ZxI@qWB_e<>iLbc6YkXdsk6^@&gzdlb;q3|1NigygjdgRsl){T z5>o3hhIzHG!eV1?L`!Qesw;PRwIvOagU#NXlcGB^*@Z+=Yy?7~BQi+qm2X2`CBu^Q zO0e!E2u;N<4)-@}o`&Ln?T6p=&MZHPuaa-79`4?d;uW0qHy-5x9c@L<( zXsk3`t=e7SMoPyW10OUFwwWmFN~J1IQdUbC3+E-s?`PlRK=r5?Xz?EN_AQWim}~0E`Eu#^0_36dG!J*6sb1YTg)b3 zEflQM0xdE+0DYr`_#ImkF60iD0Z&F+7M(zqeWMwR*p9~k2vCHJlf)=I3cyD1$K=eR zkF5>m=hEHONUc?6&Aee*Gfz$2CCNf|CcU{dz4~fXQcmoUlQd)nRIb@eBlcc39MWiQFGSb!z;SlrYbw8i6 zUD^e0xQ@sZ6oZw4Wz^nrdrbAM^&r52HR%=PlAcqKxcxECsL?0SPm$0!E7wD1jkF*Zj;F@+x2l1XEHK)5U zb=CGs_vX^B6cl^}iCI*(P?`{$Hr^~Dj_m7Y9f8ffF9BDtHlNkVQloJ@*0g-_sH(`hOh4Aw;w@*4#H~bXk)(EquEip|teNbV%sgFG~D>UzE+84aq z^aoV@&;^@B(sm)cf%D0gz`Ruar1J(yQNNd$rxh&6&D8lhK~jE0DQ&fU(ZYrU|05is z$(2vJ0$k(Gm7A-9TEWV8*Zbymub4O4>nNYF@v06VMH}(#_SGi-Vhw;RJ!@Z&J>mUA zYoSlgi9XNa(A76#56fut2(XIk;G^Pj0m_UIWZaM=AqsOakQMeip|K-B4VMNChkjq@ zYJuU_=Gw!~mZCB+5+SFgN1_hKXoWH9N&8$CqJ7wUn;>C!3@{c7fjQ*L*M}jHV*^A$ zAb0bj0RWfcq#zIh<9g`22y3qqSRPAgyKkG3clEg2fMml3+OU~jG zZP${%jroX&{A#Rq2Y$u!wstxYCpw@g+gypt?Jtu62)Db>b_~wNCk8xmb#{H2_V&40 zO+j{u(L=l=h<3O5KBg&uoP-^jcW2#P(ykOt$24veak`=7u0YNEo#( z-un9pWuZf82s_f+<_4El6-%=yxY=B`lzIfQs5m@P;&Kwm-EY43wPy7y*spy}H_UFd z#lYZycHbrU#p-7yCy}sdQg_;^Xj!f~9&gTF6^kodIB4(1-vDM|)=l$oaoKT;-So}o zweb1nYi{P&Qu9Uo`;uF_d3&Wf=0BeIABVD5#Ao3LKX2WJyFTYXHa743F9?0E#G7AS zaLbE}t8QU&cFwIVF0Z|dP*HK0n&z{T_YaC-R_x3oMrJ3lkMI_GZ8-&~zrj#n1i{lll8JNq1_ zjES?)-Emj1VxuD33_h5?M5v;3Mz|MW)7u1Lm%c@4$)4chm%pWVf})HgQiilm;!f{w zHBm;}{r17(KD$OQrf`y&Cp=f$H8%#vaAFVdC|#tFP@yTfdTSk?{UADgEc^w31x=wi zNt}pV;`AS%)CPfV(xfbA>4qb-Gx)gPlkW1=c@vJ{=|MC*v?VglE};cQZu!AjPNt+t zBN_GX?7Fk!f!=R~V;@(WH}al5SuRD+ao2z~=hzj+DJ&A#4}_}At2d~QVA9PM#qjXE zM@iT;`Ad`ME=*m#ICcKYrArqsO`X3qapl~_D_5>ubSpoyyu3Q)CeNOq zJU>OLi&N(=otvPC^OrAAOkTWvnbK1eS1w+;JaOsrg^O1%(#NF>=Pq5ma^Brsn8L`% z5GLv1^2CJ;SFW7DK;`q3m!{55O-@aozi@f-(!~oGse9qV#Kh#e^BT_uDo#!?+;e=t zd|~3s6!jSH#mna>rrgcNSElAPp7T=|HH?YNmoHzrc<%hfrSlr>>g2oX^wPwo$%%`X zE>BKgzIfsMr3+K%&rhVxL-^xtyVeP&^9}a&NINjM)frQ+n=zpZR;ZSEos=Z6(t+F^ z79b?~&G;P^2W7I*w#B2Qka!-=7*z?1YVYilIY5bykoEy-RU%q8^!+OI?ClE{EUrD2 zg2rKW)!fDH1-JBdCNeqsnr#l4{yq zQ-*r*Ds23EfFo$L@5T9pIv#8ll|quY9)engOf-f8VO}wdsRXmDg&Jpf zJRIRS_nZE(NCcKQ9U`%m^*5VfE5pePiYkytlMs8kTypGH>{TKQI@o=`y~F0jb-0NU zo}~>!+cuyeZ~VH)aY6Aw%rAF$Kmctk#RGrSZ#miYxOz3V1K5nKrc4RVgLRUbbq#6- zea3pay}x-QkX)8~w^whBT|Vi?-aY%}AAfEXFKPPr)$y-geffv3J^zC*y#0>vn3iU3 zphvZ2fS}}(pGV&wW4UR~vu`OHar_%g{!Dp8$IKJD(Rwu2T3g$1-w!8*^#gljyf6)vh97$`UWSdqLo3iC=*=2J>`}rX5F`Y zM5zGU8AB);R68H&cSaBWlC@5pl4B=gsw<1PmuKeQokfj*cW!BM=G9{~ss%f;(J(CT zIzNkkI^JnLM19Rh1t(qH{wSv2$)PajH8y8Ofs}n-E#9C~<}MMxO`4PXVKPqYhE!)* zth(x3uc8)`5z8zSUVZi3aVU_6(YsgF&P@ikU^a@3(KD8NUK@w~&9+j4VqC|Z>UnkA zmch$6l+(6%eI8L}bKPw`+PP{w5}he1W>z!8R1{cQS$4{D+#qDao<;&6Iq98ja`$3r z_x&WCd$e<_wU=j6VRq_ZyCt@LH2>*aE|?P#H%SMYmpMOkvIy4Mu5mmaG_^8rI&R?2 zuj?<{i{e0L-e?R1JeIFUMk_%}wj|rZ*gYDcB}cOjdv==BudlLJbq(_-I-m}BEKUaL zKKgC>({FQPkP{stHr9f>Uv2lOtpqSNt>rS&!=Y>3YYL)=@WL0` z#YQ&9W}no*`K&$%FaO+0=0i!n6 zVs|mNG4r$PGwfwGOlUoK*h}m^MJxfA#Sc@{RvXUfy^1H@3+|K~b3$tQxFRW}*mA?| zusML)*&*?0iU-YAFD&$NtJsy1NN=zDkNNE$+<{?V?emT~YJ+ILsqBz7xUxFM#-oY3 zTjnVO*pwB|Gwbdy>R%txhdKXjDRBVwHPmbT0o}Bykb?vf;n{T1BMcoQkb# z$GX3{7u;ylFxva~;qH6TweuVZo2>;mj(L^RtxKe zAsz0#zq9*5kp+1Si5&}*jaB7J>`XTIRx}=jTyRvy^5u@t=8<__ZeS@)>KJWkMTvt2 zGt6_7`qSJLJ;IqA!`iws_SROSwh6qg{$&-Kat<3O2sebpNWLl@ggR{G6k>YA^m@rO z*u6NF5-DPw6UTXCCC>tycJNS&(GDqYs^d|Bh+plFuH;q_xwGrGD9(DvN5Qc52u$8N z#2~1hFLZ);14&XN?+4% z={5KIg1bKFW)=ukQQ9MI2bs@RfMY&Vj!VbqL9-WTj-tQ#Tz5OW>~%;tq8+J+ha4hk z_Jt?d$^J{`G0Rk}9c2-?U_zsOAElRsboo5gt332S${MEbnXxfG)uN}hZW6pex}4Z4 zRBC1O`!(b5AeI+{R&b%bICs?$V!=?&PU+YWyPhfQ#`NZDXN0sZ9p9=Ti#XXgB#!ZpCH_EYy?WMbKBR zBg43S_8j5b7dOUF(NFo`sjD4gP*#6BAGVfezu!g&O0g;d0O-!Dpw?E1t_ZCaF?Z~z_s}-YWEG*y`h;Lv zmchb1f(Z4EX;_saesyFw3UkSjk{C4AYCmoWYGt8q7{X-fvtqd{#Zl<`W{AjPl=q5} z3gXdBQyHqJ{`BQwTt{=z0$yD`pkD~&#~ICNh!la1jV5#%Jyy+k>WbMCgJ@60iyPU4 z?-x|UAqH#}Q=8v-t^FuN?{xjpx=Jk^34yM{H+=}&u^K*etWqx=tCYtT6a26OFZgx8 zrbH{_w-b*QJn9;EQK;7}86gO(!Wae!JEzYSMn%GBbpChUqSG+Tk(WoOfp50gVQ|pl^*m z2oCy07o;#YRLmYf7MGA514)}oJi>-OQW*njA=K$jX^mtMoLo=#P>#DC?DNXPba?bge4?D zK}3(%b;lB3jnM=Bb-%Jmw91%fmbx(haMh-GL^JVyzc!8q<=4YOrz(i!Bz}DW+}%f- z=MkVh*5FylZsahakijFnI(ilg!9Zfvb#erPA#rSpcbP*=ym3vb?tD9K;ZiOsPX?$T z?32|}M#Y2>t|e-2A}j$6oACuaCS~1;Yn*t{M~eP;HFf{rW(?%_VbmpPt+O%nOob|QXQQa!_Te9 z(sA^H7@G4DP+0a6V7QZwgQUYi8g(DhghWHLu)}Du-2}8?5a>Qt)C*e<(#XIcD_(6b zcU^8D-`FMr?6Benjoz{uNZCXg44vGLhnh5s7swyol`T9jla|G{lG@>6)<*Sl>G6f8`R$Xu-0Zh_e{I`=~;F79@^7FAPAFL!|cw zK03wSv+wZOj|#h6#2{0zUmsl)p=3ZV20qKt!yfv|4dE`?uxs246!Y^d8(Fo`f}Unv z`cDMLfgJnKyVlbyV!O>sdvL?b_{a(NlhF;5<*jd|d3quozrp6fIVPJ+-@4xDjv5gv zd*#;F`UMQE;vmdS>7GvJ5jp1rDo#5RMRhnWj(J z*0c3v4osqjRhs>grFjrJ#%DGa-46Q|Ka=bDcw%5zq6!fF|g8V%c)$_N;M;Rl)e& z*}dZf&qed&E{1}_S50+Diel$ve-o4rl%N>;a9nC8KU~_3z}t$JYus@~){Z!JSuOQIcbHliis*^(1Yi615-sMn zy;R{+1Cl^}wUyHlt6khl2B5oysA^l9Vj{fM3ub_0RS$3zO#76-%SwBw)RpZT|C5Fi zisNOSL1U|(brod&Q0W*ArTNsCEq7AgMUOGCC$D9)_+*O-NZAdrf_?NGg+@w}3}O~Y zV6>^E0by5-JzK`gz`*UtgI(RN5sn6+Zv@M@4)i^H3V(=gyHH`0av$F4;68c!_t2r6 zU;Z+_P_IKfv_3gesJLrYdG}@Kj}GdarY0kT#UXm5ib9HXhCx;?pP}6B-$lvj3+j&A z^mXjqLW-2^kvCH`Kw}cDko3Xjpveu}XW@rmN}923RV> zh?EOJ{Qjgvd#<^W#nW*< zHDM@(h8p`%KFwIioSEH*G?3q9N>{%#%4sCiW#oYXQl>ev!%BxpDl_yPYc|%Z$+n`| zswr1@pZSBx^vG-ucMSX+W9U#sB`Ds%0(=oeP)%6_PA!-!mlaPGnuOA*vIH_oq0Ipv zoXTWLE48fs;z&#g!caX_@qh$(NexLv2664=Nk`=OTkS}fCtbf9H6h5AqPcBD06COx zx8GH0uXBGh8O99vy9%%eA}i!RC;A_B4W`SpL<*D*4$Aw#I~)!7v$My=gZ;X_L=zF`8r?@G4LTK zE#=@i5|`>rKWRGsYK1+8r}plnCbi@02ZZQe7(3oH`V@cCJo;4YHz@Y&I(J=1uvPLZ z385RedKbu!AxSpp$##o?qX65*|0_iw?j7t`PyAF$r+hXkC3!sAWIRtSI3$}1Q#V|S zP*V(&40=g2paFOY1$I#xN*6kGQg~vBo;1C}#4^ro0{Y}@il^&Bk-}>d?6*YfcrC#% zS#KMa*LC$1=ZwAP#W#ay44=_S&Q$hfZ$}{>_o(nKK;=^ydL7%SXMZ@E|lbt%iq>H5%k~!3x32B3#Fs4-M34==IYPyR_!MKjnjMUYXV1D~q zbAA`q-NOy_n#@`TfIh{1@41*Jn4l|N8(}V77Rn?-sw*U0qZJg!L>;U9AVOWm!I{!`AcG;{uRgTL$iy#FDi-E&n4JVZ0b5+n?t=a;x?NF2O2#pp)eO}%=M0OX zVqmq)+WK|MHmf+V^g>aS&5(GDHyMX8|DFY+5dHadYyGnX zmc?J?Hvp2f33-u9*(89N5KkV&%55cTsxVjRGGM|V8R<^v-N_v2Ol<8#f?9)w%n~Qu z6r`RxO6v!hdyHf66+Q{ElE;!R6(db{h7^b)z9>Tb4v4CUFkrODV}-C7*;l6-&5H5o zE~jSkehx*tuL1UUr+ri#8TA7T?PgM_F^WTC7nP+=4UFn|e$V7Z;O62&bfq*DeDBB2 zY8r^LvTSINK&0rYkb={%x$XA;T``@YZ3rtuQ9*h(24K}1s?E2mLl8%ny1Cohh$Wt+ zfgGr?nIex>12dnGoM~#gwdpW)9a9jMj=yj|JVkU^a`W^CE8N~A<&E9E<8&^SjpXib zr>zajSQ{%=Xvjl_oiD+(&JsI3*rZfc5s^N2tWR;JNV@pd z1_R6{89dKj5G}J@2hptHY&g9_3Q?iWGw$tU;t`p1MC~Z(Qt%eNH)Ebx&4X2`w}#Y! zZpmEC3?^$O!XkvZaY1Of^6VjiFeiFOhDv@W6m%U@=r@a=sv5T0rf}wwu3b65IZI|9 z^L(E|G4aqa!xVg2mmqU&>fj*j`}Ai=3H8AW>TBl?Dtc;BA}5P|(wYem18p#_J}u`0 zPuDQIhz^!uOAl*iF7c?_+1uU*z3sglw=_;WP<}*%;r-Jz8R@_O+=~*u4Iha^39-MR zLUkmmNWdR&^U>YAYI0ZK@$@U4d{uHJ(R(ITZG95Gtw1`Kit>c|pjnmBW4_YGOzx%j=> z*zsvdOJ&DWu!DP!Z!8y^JqeLXGsCG|Gg72bliG1wIlrS#1$9S8!LKDu5d2@(>+Y)t;-;Z^Pe4v3OOsSuGH2m&Y$u}4E>lEs#Ze~!5SMk}d z*!cxXR*pqT*jGx$s&l4!$|^aI)p2Y&+9?dpL!N}I<7dD2f@!|hTC&Ya6*=UxLiyW+ zlU1A~YQfbCKIvZn{14UAl><^98r1@2G`&xx(zMf&$1KZo%G^!5$e&ADy1wY#q=> z#n3JC#dFDANEkA`u`#2wA$3JOVJD^FHME>}7}79gtShXmsnY$ZcE;>iF;d7QvZ_B3aq5}Cyz!EZjGIrU zdQOBW3ZVuOZhVL=R{aq&ErL)J^9w|A#4=?*v2;xk)E0tb!OW#r=K1hgZQn6 z(yaR}uNXdQwoQl2%potZ{MRK<7Ne9?NS99ebBrB-z=|{wK_=jDk(Y&rVM?AW9GJ8c zRj&r--bG+vZN6+gRa;|S{;m{3I)tVP)YP3Oz!lTEUFVw8VMnH1I3y$wqEn&=H~=G# z=5-3Bz=I!ok7b)$#EnRN%;Fe67@o$=yDqH5nTW zd|zb54}Tn?GI?-ZE3uF+1ZJ^C2|vvdVZNne`=HUo8v9p(re*$Qeo2P2D-< zqoQPETao*oi1Z~4VcO4BB$c1QjP7rLLrL+vtb1lU*B;99m3kYOv>CdjH7rALWg{D( zM-3g#N3ECdjYieMGH)R*ENhCC7oi58>|2BlwK<`%|Naqe&Ds$SUF(%BLfyj|UtOMF zSh-0;y7Bo}78aN1-1x%c!n^acx>b5+`sTZ{^D8sc%d>9$>x(Pza&y$1ZoH@yD@8lC zt}xLZMXZT#uH0{~ak}00yj5QR^fX++Htw&GgCE+vpq?B5;jAV)Ee*xYW)gDIIqi`x zwZ^4V9A2}tovFBZ7;Wf4z3t|>ngom*VovVp;7%*K!k zRYB+H76%;%6Q4$adIjFVsjJm&6BNhbp=?RMNR1>DqV7S51#xAlM&K;}pM@x;r*;*m zHR_+UCFXeT=K3ZIX-HM(!hzdZK+A`k1KBmhp=K`9;W8oj;++jrr@f@4sFFE88q)2z z3A9ChPXXI^fEp?(LV1%Qk-o1D3Em~r!?#8l2U`V_0v?iPa-DnUTI|F3J77uLlQTv2 zYbo1kx(H>|X$|m)Jyn2)Aji2c7f})XD1Z>g#KW+<-DB6l#o8h3BT96S0S9~C7&!F& z>BwIl`D-J8edK=_`5Pm@IP!mt{LPWSHS#}>{OysyGx9%;{L_*DdE}Qy{_eztXpFW4|n``{lH-{Px*Jym%4K!Mf6OHfv&4Z1{6#kRE*S`<{%@OyQmh}DQ zANU>aanpZ){8vu7#}oehe|=??xBk0*`GS8=Q183k|9isO$lc(F4FB=48*pE8ZsJRB z@Y653#?tq=6CaMaf#&zR#>e09+-KkKPCWjtuJQQ~x)UFta&GB|+`vZ+=jGF`G4VX# zpLg!@^X|mb3vS@U7u?X}aX0YUIBlP!?0Gk|bcs1$a)W!9-N46}-HDg4xPiSZ?nLvd z8~o_2Zs3!z0>YQw;CnB-#;4bLKI0ni%{te71rWaK240@$-7V@YxW>yr;@qb{!kFKn zuQ&PrraSTJTeSV7)O&~a-*JPV^ZCg)oEv$U&v)J6$a~a%k1_E1IiC|P#@C|FH8=3- znj0cJrTeJuPP})=4LrW%+-G;(;K!S8sQEsh@4FKtTW;|27US7=jnDrr%KzK$#OFWZ zhCcas==Ucn|C8>-NB=Hu{d<)Cy|ndv-QXu5xf7rKKF09--Oy*hA2|G!Ydrn~wDAXB zW8$Z2;}5$NpZ#Gsxb#Qdfcp<<_dns?pJ0AJLt8)N8tyS;dCXe;Ip;q8b8cYiKj-;> zLES%3+y5nf{{{N`uUuo~FS^0ze?$4d>;~TZ%kIR|e@k1x#=Cz)*?;PWmi`&z`)984 z>HqEAd;bs5nhp0^v(eZaZwxf28t%RGjmF4I!+pHc82WIvG4RRl#?Z%aG~DNJG){c_ zW~1@>*BdAH-sbt+jmBs1GzKQV(HQ*X8;yaF-{tpv{05<$YmK4L)*6kE+l_&xJB`Mt zKTiFBtWJ5_sfmJkAJz*c=;bS2AjVIIsEg+;D`U5&)+mKxO8%$@!|Ik z3_kw;fuX(MI?(v!%LDGC9~>BX`P4w;e4Z6KQJJ{I!^MgYp ze_^ok$jGv?(^>(YJBwlLxZ3ED?>w{{End$6F)H2X#UP2_u=mv z8u;XQ4Y`k+Lnq$*%Fxi#vqO#derU*j`a|?FI@DNtj{46HHC~>e{Ry5=3^hJGH#GFw zdHQG#op^a|sPXYVKEFx5hqU)_sPXv6hE6npoU#4*(7_<&m4Yk$nE_ceg7Qg zetzi0(l0Q^Um7~${_arY^IslvOTR*UzcMti_p7w`k7(~74>g*47uO=OyiTUz=WTDrt#Tx&$y-MpJ{wL{>;$co+ke`C1u^8I1=>HXmoOAm${AAc}> z;-ep@{zugPeT?DvF^1niJm7w6*foD@c<7@)Fx>d$kJI*_pzfa-9$5OBVfV?;Fuupb z1I?eM-JcykG4iK}2R{7MwE3rpPdxrJ!;KF=q26C)PJfxX{CC5RPk)|1eqngv^G}D} zM}Lj>{sv?G#bM}Sc<|$2q_4lpJpT5ubAM-eVDIk?H}-ysvHlWe{_b#N=^qTA_~ai_ z=SyF5dndm%@c87H+(*CpON~#z`%CV#@A=Ze(#V(oKla`ON{TA` z-WFM19Ev(av)0=&tNb0awme_%l-1R}QFos#*8i*?+yzKF4JRyW2W&x7FqpsW)f zjF?AejXEle`3`w>)-4}{oR7<@(s5Z`IV-DbXJt)rcGjs*MtdeBhx4;Gz7Td7XPwd| zSvR;et83S0RsA~Tdo%L96)|e4Gd-)S)3Z)|N7h7lW*u(^a+rZQGqN`P8~Woe#J?+R zyt|R_-B}afoplQ{r&Lq0Q&N=tc@N=ohP!cdOqtEpGW>LWNqQatd8D8 zd*-6vr)cMAS>w;kYX2+5|0Zi|-=Iz3!N(6-UBUv~P&v1zb0#o3CpI}#usOHn=5&zH znMyvVORYIKSR|(#%jAq&F6Y#i%bD`>Iqj{IQ^i$sCSE1yR94Lyf3=(r*MV-`oURSX znc@~XUDzh)RJTElZE`l+Hs=%u=5%>jPWi)OJ0fSi-EwYuclg*n=afgnZe-4l_R8sa z|D5#?$eHQ^IaiI&nbPQ-6F~-J5OWMZk42v2;Cmc&2jx_F5MurX{!Yo+>M3Z)DbSyq z(`s@~RVU}Pe+hEGH0P8qMSho|{$Yn^>65lhjXg(NY2KO8t9$K|zuXx@|#%j?Ri zc~d_v@5HBr&&)gFnRyeO1$h>Hot@Xwg?YDh3GA=PoA64MUxip#quvX7<-G{MujXy# zHTZlz?=)VAk2mruV1sN^lMh$dd=F~z1fs^Z+0R`e`K?&jf8#=*zeP93Zt5x>ZoSZ z7}cyQ`!?(Nz-EkNvnq^%{$ThU5B>ONw>F_!7Y=DQ#fi;MV`8(34sBNTLz{K+s%BHY zx>-)!plH`{vD?8N_s z{z1fgtXZk2kmpmVS4X?*sPi21eIEMf5&H|o{1R>W3O>GR*1-xbco1l@MZd+={T8RN zVv7k^Y*Aj%7VY&z`T8xUx_*l;4{A~2pcbbzxW&{4w>S;RVEYzb-Lb`#c4~2IJGB^Z zSc|Ug+G2_$z$03Wzgvrr4{kB>_!b==+M*hV!v2^RM@?!mg-Ni1jE-y3jWb%D;@K^# za(0XMX118h%oZnxEZozg{eL0eqb-jA7;>1?qSQOc=baW^oZI3=b7AvAi;g~QvHnLW z`?AH>zHGrY+-mBrt+?m4s`ARMI#{z+MQgU2pjWG^^lH_W4O>lVqgGuS(5h+!T8+PD zs~c_Is?;{Erm{_|j(39nuvY62NBJ(TrmZ4k1uwSdL9svIbwz~e9RuhhC z)%EeM%AWxLhqUV2IjyRAZmX?TTUFt_RvVt*itDje#h0|2+NFqnJK{`l)!|+6cUP;4 z?nWN>wK}!?T8(!<^p7H+$B_49t)}=m;=Y2qui^6>t&aC5Y~E^hDsLgyEX0}BYN~Oo zL^ZQmCeUE&8fd3y_-SDSY75v<)i@KXzZ|bH?J-a!TmAmQMYTZnI^=_`$ zyPNg;psY_fqt@=G{I$E;^4i^WedlhD+NGO{M|9J{-mo17+XK3p@&Vm!a9}qbjqYY@ zW4bxP*ls2o+f9|mq0V95O!e??Dmt>8_Ai6q%e&$Ez0G+0wCTpg;(2b1u}XS{$6Wy z{MXx5{Cb-%zS-uKXSM129Ps;XraZSz)#jq?gEm|Hpv@_Lh`Jx6{NpyQ=0VPDQ{lWe zTlyR^KSwT*wJ+Lq^;^XLu1yC&wwdBjZ94oJ`d`{?{7W0Qv7W8q|ErFtqr9i$X3xeg zo)fovs?g>+%JW=r5l^edV6&K~gC#vvUlO)GVB5oUyrogTw5PolJUlC-d_|P6=-I+b zo>N~5pCQ%Cp02J1ziWA}TH7;G;Ms6p?Ah0a@4krD&%?8w=R_N#{>JdPiD%23dZxCi zry84jHt6p;wf?Bz-_zyoP(IMpwH-XSIK)%M9X(yx*;A#R!Nbv-y^za3;C(z(fviHt zkZP1?EBiu!fQS17+H;U+f`dF=I@nX;!Je*9^pt<7XS~DE?!!DC9}a&@C0kKY0 zx^S_=yrVsUihcL3N|$CRxB558dzA6+#UA;7WhxIS zTlpt^JfuwZVdVV;{62-fbX^(soWipdV!oiPdP$k^Wqf`WKHfyUIZ6d{6xI*q`w{$o zjJgf%{ToVGzJt&2vCscW*-&XG#&*TeVE^2#okBP4nY(FQY}2OhVIRD@cB9p`X{@ew zWex56y|r!hMtL8tN`17`=!5-rUyKj-=amh$jW*G`wxzb!t*~$2TC3{T+QwUJH{3=$ z^=-ATZl`T!Aokk>VLwPa-e9eQ!P@b6z+Qid#(qQVsEED#j#?FV#AnC`WPp8tb!XJy z3;y=j?fZCjfHt)Q5$|H~C9u5)w%5RKh`QHm6I~0wPP=M~R`C@0pNe)&)lN7K`}~`c z|IJ$aw`!+)o3`HV+SF_CeFy5y(5`?f7qN6Tgjo=4e};qm6o3EB{^XR^}q_4^aOD#QOw& z_o=p}&$Oz1hB))kA75x&|4J+MHR^nWy5DGBFvdg%=L()N#YK!NEn;-AxKV}0ajvig z&L5V6uBUNvrh#W>oMk{(AtOk&D$X!gHMX>xG2v>kT?6M2YZ}{F6SlpKje8kg+03}r z{_xcw_M020v^i`FIOo^`_FEv}!$JdFZS24bBnJzRQeTxWd@*3ZuhoP(L)b zj5D3^I-JkoOs9GiY;Ho1Q;qee8MiUb*!o|Mqi!=cxXn1>?MAEVM)}iGKHZq|9q@mL zaicqpYTRjb{ciZ2iT;?0zP%TA|A4RijEe3CRp zjeh$Wem_B5=b=sWj0!$C+WXF!_&fCL_ePb!H@5LT%G#{0wOJJ|VjaJ`)!yP3=QdVX zRzlfIR!6H@6|ZJZbq%Z4I#yNs!e%|2iPp!l?FLpiHnS?|Z%w7Y)!_iEq5)P{x3X?& zYuIj$&jYPdgRJrg!3Sh*5aJECPHlUu>f2ir>|mYB4p!GjSfzG@kKL^kj)b3)DBIH- zZ!h@Y3w0rDds$oF+dAbEe3z^)j6%-)T2q)IA734z_M(yj9il)-6u3m>*V$WyC03w|od}4zW5o6nv=F)#K0?$05e$ z@N^o5ZPU|%8v~GQd)!yAGpNV$Qw7T(kYlC~yC;zZI zj;v}#=-&seEj)zwJqo+W5aUV2c+#2%q+sONE>-cl5F1>?X-a%X5N1eG=S3a<&@FC)Sg!&($&5-4f z(cd4#?i1@)KSi#e!bbys8`kyaS*JG7s%Rei`*YZQZfycdS|gTe1~!?zy64sw=^^ z7tTR@Ii|Q4bbavX)yHuvYvar`aCE$mV}f-Zt@=8~>*uJbAI?J8bKGzP2j2^zd`riM zTS5*&`4Gnmc64mCBfcjY;n>3NI5XYLaiWrgvm2bH?&}zTKgSLBM;-?_rf?w6Tt_=9 z9*vyGIi@tualC_ZK6|i(e~cIU@$fw!`tgoipWrx^GTKviOl6|u)DA`6BODta>6r2{ zj&2->e2zzqGaR>grlT5Xg3pHiIcVFt_Y^|03G( zlH-*A4IghhZuKq4gtJgS$5H+q$5rn+y7rmlMh)aR&vE_Fk;CWk@r|RypB%UHGs?AV z>)JIXg9nf%*DdF9R@#hn*CkyO;(WNcoU4Pi@VO7pRs+|qu8XtRzBo%>ALqgSU0c~4 z^|o`J%66zT5ND|aU8M%OPBa*Ddl%ouz-|c6e}}+ssA~&D5u@nZxad0d9bFskgc!qI zRT<{$!UWh&aIIH{EF-V7>l6>cIq)IyeJJb>bG3gI&S-J2T$%(O&XX(0LsxNCRB>_k z0ozkuw{V*4sMB3HJOlRUA;Lwx?owbhT&=13@OxU!d(@BK}w4Z(LLO)^#i2xu)^G>%>2~M*RYtUl2oQbZ9cFYBM%; zGN$HcRFKJ-LQ_VCO&OyW$=G-ioJ%hXUH6O=EtXN<;u)v5c*X=vWK^X`#toD6Yd2UX z`#sj4BU;%@D{T8K+dt*ust(C*Bdd zQpPRp13zOjrZF~y?_`ksgpBJ?M6MGvx^!a3`X^;{_3VtQot?44 zcf+}PTa#1pnzXk>ldG1*nf{VZ%IndjeS9xaSf)wgdjVZt0cYVp>TQg3_f5c?H#zB@CDSD(_P)lJA@D&pY` zKb+p=R_{RBOOP)$IXI8E!7K3nYLhCx+T>JTgYJzcr}QS`zS-nNv(ScF$oFl;n$u*0 zcOc(sa^v@#@ZDUKE_~YLG(K(8r5~DbKGEa^KO(=M;7{Y6S>w#wWOaF)tSW4Wo54Vw zqYuRS^uVl*2WH)9P*xXqfNluRsE1{3b!VJGkA%c|d$2FgruWOL%6>S5-aqREqqC+u z8fVW3q3ocnE*_k9%H!c@JT{#ZvN}9As~X4R9D5RMPDK5avbr)E=iujN@jM9o^Rte+ zAZtq(Am)Wxr+#5pd!J?9_%p~JIoDq*hknoL(uO&=vQbXgH^Vvc<~dy{KsO+V?+0=k zXVO+}i*w#VIY$k~ne_HKTi5~Tw>WdIjmFvSVL2Nd0o$YC=h&Q$kIiYE89V9<_`z9m z@v59wA^19+3tyje>enOI6!^O%=lXZHpB3u>XTo)y3D+OXxz)#Wy7&|%&V}o7PI=vdS_A+IVY(ki%OZQQZ~jb3%)G!AtS!e07drAp(Ki6$spJ4E4( zLK**P=#GK^Nr-uzGPUEB6CJN?bb``VBv-2{w|*Y%&R06VUb)c}Wr7=&8{Vjlx=E?< zCgoP9;(TBl{7q9%`DXaJ1?|Fj>5T}vVXt3!5V<_2obWN_7N1bM@+4wBrF8vSh!_h8s`iO?*K$=qi&#WZ5@qgGkor=-D*E={qBIC0`=YSc@N}|_W^KrpaQ%PP%go4l*V@&$Q@@0 zg$dA2K%2|jRTDAw4%JTO2>3ZtJH=z6KTbR91dVY<`H9-~&&21mwGOIURjS$+&(}s> zq_uasHvW~`DO`m**K4OV1#Osum^W%&zFE8FTePa(qIK<7OXAb?k4~FmAaw_LJ)vC+Z9CZ(P4% zv>Jf&EsarIW3RlmF~K(Qvkm+WF;0xVdJ%i|(r{xc*soXc&V#~Uym*i?g@fT6d+G|_ zdnlh^oYG0ye!PalI<`+vgd)dtz*KA^c$P9N`@Z z?>b`|HyEpKH5&V1d>4t_u@8=BU@wh5Z~Y&r`v77*W^CmNWAJ{2_MSJk`hqdRi$;}R zG&+9CnDW0-|5ancSB8t6k@I^-HQqB$`F-Q6xkh<& zjV*nEe*3`K`iI61J~g_~YHhXEYV3<0&$G7fS*@0^wy}iO)fKG@SF|?jg*{X+>sESO zRqc)aS0C(s)<)SnR@eJs|FS;zNE^a-L+kh(VL!An_D-8(zq1*94TOH6)y18lA7)Ku zXOtaitv4Ea>|KkatuBm#uQApsj z`>a#B53wK{km`Qq`=E8OXSIcgp~t?}d)(^U6Y!5cYyC;=)1HAn_Nh@Fxjkof_!8Rk zveofhu$hHEh!HzRTXT*r<{TZgVV}_*`<%tGzgf~T)g>Jr_rShpDU>bkn8GrS3YT$A z12R|^dzWQVzMP}f@{TDj58d*PTUfzCr#Vj5cT^p+z9RM{E8+7>*z>IHxTRIFhgsE8 z(Q1yZtPWpmfP0}{Z^!Z1a#W#@WBdU77VHas><4Rov1eJ&vGIEFQ-I$A$a6c#tqsDS zW_!npcW_LEePLm!qod)7GaPbPa)2-!a|+ z@OJ>p4slH75XVsy(VoK{w{is9f_-0jq=S1rVjTs$V;vPA>$qj?0lniL*FPQg&T^da zYadrqyxyp{ zu8VaZyfJ)k>T0h)c>cbxKZ4g+JQ%z^>TK_t`u6aT z{a!c>JlxgQ-C#G;wY9xmRXzmqCc;&Sz0VZ*nd&;hEr^YGd&;-L|8&&*8}?P$pM^6K z^Ip`y&&4^LtJPE3gW%cHYRR~9YsM*d&*%d7QRO8vI>7rsrKRy~kNr~t?*PTC!FG*| zEw7PrYHMYb7i4S$?*IksVQ;Wr#%XK-pBrZIo+9MPjH&OD!Fz_NHyUxqz|Yu>DwZ=^ z9gVt2XPo*mh%*U$lM^zwd?MnWj=ja1@O5U!#U4lpSHj0t;A@a?n9=3iu-B+zzw-qA zKb3Kd&tMOdymO>#^WQuAI%B+VGEVeO#tpwky>BzN@&ooZKOn|W;GZ+P*wmz=9G?A~ zn^e$>y~nCestKJdE_mH0y-4LD%CUDTWAD<~8n)Xs>DsoC+cjB# zV3XSz*ksFtn%vspCMVh+`ebEkaG*oIqS9LRHX%bk`;1z zNQQ2Wobr3+oTwLMZ|rl{!aiqM&c?%FvtQ1M56rnmymwQ<`!?ZWusb}buusBwi>P18 z*?I+gsFQQ5@RywRPsu5~hokGK=2YploYOcRd!Ta=?;Px*F3PFeMLAu#B4@)ZQ5Wyt z#8;utHPGR`niBR-rAHC}shll5gU`?89QABY2eVLr7UIT;{~r9mhke@nIh;Y}Rc%G= zd3xqmwP)T$ka5qv_9pLgR8z#Hdn3BPnw+c|?wWUk5qVV_ zf&Js|h%qv6)gG|L{;|48-Yw$y->ZA(Z5^_)XWj|-LhQYfLn-gzy(H7v7k#l`-W2d> zI3>s`WDPQgRCs48z&lI$_mNEvvavtvACT8I{Psk3Y+hH!!Ou9@;E$Nf2O%HG(!sDj z7(U16-THXc8J{=)guDtT^{&jj-c@;9xe5&F zU7dIE&Xx+U$=mQ6#0yd9TJUw?>(IvQ^R_T0?-p*rytxs5awGa^YTgv4!pBs6o|d=4 zG~^6fy%}x1IdA=2FdnzS7P9o$Jf27Ms!_}1L4E%Ie=~#Sp#8r{E4O0s(JHO*_=3ke zgLeY(+=0C<_Wvq_eQgGN-6mzDCZ*yg?60xh6?3pg=pc{1FC@AQ`&;atAp^W}Rcyxo zyBS_vlqujfrMN}eBHkq^x57UpUfXc-HwLPL_EC@K>|`a3FH*%5M>!n^(GgIy5|?^hJ?w-fXKh9VlF z+z5X|5$q11yQ4m29WodR9tno5LpC6TJrwr*sEfa`sO+imzKSwM{H;a2m(n5r-Xh*x z*+NOFS_v`sK}`IuMQI;pBYX}JsoXh2tE{jaHzs}%}^bQe2+pu zABC|vTG`2`%=f>koZ?CRk~U^m1~r#Ujw@k{T{++i2B!puR}YpgKx+fvU)x2u16nD zQ8tiF!K73B${WsX*e6aWue9nI# z2(kg`zl^@cyFvB0kn=2ME3=TtEX0W6D@LA>!ECf`wo>YC^yAxzHAk899N6I>Kr#sjkPo^q=1Bj@*()A&H?_!H&SK2^5yDf;X)Jo@IU*bHqj`LKn2R`@E zw%$YI??ANUC+DfUu`GNpuXSSu$Q8A%t%&+7As)_8D=WcYPvp@P^&lHP5pOl*ySld3 z)lrUf)y5j&HDQml)mkrYgWj;~jlNh*>!6ReWt_8Cao!pw=dC!eLRml9;EXj`4?fmI z?(1nCZ-n?8Yd73jJEcvK<0fd|rqFMyt-l#$e{CE6wJB_lc5SYWUqF5Z)P+<7;ByPD zYlBd45ZXFetKwj7LdXbGZLgKTy|yLD5Hf}=>;V5efFWbZ`ViC`iX3ppTONwK_*^M! zBkzoU-X7Ym@1b>lPvo-~+PoKX$KR1vOX$1#XUF@XulCWpJW4zDQRpL_BM19xJclCY zej3lA+N%B0rv1^T15oDx?KBS1Hat+{-DB8|#yE~ftT8AX1K(q{DUL;~aoU!~!S^_A zV#xpf_rD(aUl07R2maRs|LcMO^}v7jKy|Z?+Y76kCUR|ejdu1aSD}mozrDOp*$)5v zzyII#Kz#e6?QMDNh)p}SseV|(r%&iyE=E0$VfoR&uAh5QQXZd>$R3r>cHfc<$966k z|DsnX|GK`%AxZhACwA6I>$YV`FQ1|ymV9|%WU7J(hka3 zq+i&-^@9`p2J6pc|2Ixb%BQlwyx$m)xg3wm_{9Dd#+S|zeY#xq8~!D!7qA^_Y{H_S zIjJ)@q&+lvh4#^J%m?p^)!Hqs^4n#1aua!JvO`|JgOdO&k&P;}7p&Z&FSYaPWPIb7 z;zS=%zcu|=$h(tc^7G{A$|T=Ku`{0kUrb(stgcG*!alht>lev^#3QdvouSju?ydvvAm(ss} z!RqbpHq^(|Tk;&TrU zmhr5o^bPVdwD)dG@{#ch$Wp&Z7XM}PDvTd?*!%4*iaIzMznJVZo|>A(lkv}xrG0JW zX4=1fSz_PJcs}(q{zbAw`x1FMa)m7I3CYs_h%D>>EVAS?m)vpvPfPNV>unLTtWV3Q zI7rzSQ(Pv?{Hc;nwIKOjqg7Re)bNaj=R+r*!&M|HB?Ki(!we>BLOFrQxE zCH3><^~iF470I%`mC4foRkBdr(=l#&RKLfJ#XOY~|A1V7PS=v)0%k@=H)o-NO`!R_x@dC2sUnI+X ztdOODLbBu+rSvgb=9~Jdb9;TV+^-5`@n0fKew9@HkSyz2M3(vY7+Kbjm@NL(RK za=ip8{WfHIzA2GcGRgH(RmuHB=3k90{Zl84|ClWIC!>@4;@>Are-y}4zetw#rbL$b z6|%JVe6q~fkSzH{DUQkFPnje?$;T&4{X&X|lchanvgBVO%lcO%FUR$vPL}oTW3u@B zfh_I!Y?7bMr-0lspUHB4l*!UR)s%fLrGJnt@#jkun1ZD-zSTGK$iAx zPL}ywB+Gv2&<^`#{+-mps{Oz0^dx>0@4x3!FZc5+$#T8a$nt#l23hV8Z<8he&&aYK z{y>)do#%F*F9BKFTOdn&i)0y(QDn)tOkSGvqe_X+IrVzT5v^qVA}^vCDaOMj}SL@)b`fGp2LMe^z#zoW>~-&L~2 ztC8h;t&`>Xt3j6gg_lj@FUj@?Wa-Zmc`>%X_*!Bw@ha5I^%|0Y)_!}9FrU~jLH~D= z<@$M;Ec5RvvdpjXZzT1_KBivgPlGJi$GLYV_IR_qJ^pvp%lz@06TS4uT4b4j1+w&i zi7e~Mkz~pD1hV9NHd)3eBul89&vMNxVd#jZ4$%~Tq(s~{of$V_;`ya?UC^f$g*GFiY)6}i7e|^g)G-!NS5=!dWzK& zNj!PJ_sMczTO`Z zA6eoR$nyMAB=_KaD3fJ9tdeE?YbmaG)KB6!$g*GadnEbE{N9u-^J6es`lm!*Rr~E3 zRhLTY%lZ|OW&B?vx5#=!miwi*bW&g1qmE8k+7nPOTugD9Ec2zB@^>@28{e;Z`EzWrK~Pse)UCoI>8PnPwnK<-Ze!^qP95?R)xGFj$hrK5hb9)x6R ze?*q`G$zY_$y&r^|wKm_IRr#_Hz9OWVwG8$x^>emikq) z^jD26?Ww2i8)Wg{WbEbXt7rTsN>$9Rxs|IkR)_f}8x5&r>M>KDn9?`U#c``rNVJd>sU zVXFR*yuNdCeNZp?#AMlDs5O#&WPW>OncqHH`m;cm{7Ph5fBr(2{;ZPa`J+ab@0S{6 z$HPOrWr*}{<-^WDcj`fo)*Qe^8)R+D#kSED_lJ}8()=KQ<`JhCW=Z`A+ z9NPPR5_{SI>_C?Gm&h`|DrCtwB+Gb3WSK8@vRv;mS^TNBJLemauVsGEk(i6IsqDdhVFq4>OGC1xbFgJ`~8yaQ>CZGJh&$*?)v7ebm9F zb`Noi)jCPMV;H|emi<{smi`cORsN|kIbKy$>LvKk@UaJpS7u%@hy;Lz1)Q?*Z1CJx&F&!xgJ8Y zv^OHl`R(8TmgFbb=Lhd6EdI;yB`o*nIjk@9u|bypXz7>OOFlkX*7uFcQhzJ5TrVZE z^jC!}{TY&F{f)@7f16Dfe~lEY^^*K#{rAc8ycCd6GJgAni8e^=<@$Y|EbGNvDgH3U zUz4T()P{*aX^&5q@hyId1P6C)W)6rcTuwBzam-YOE0p_uZ>b%Buo3sWZ9oo z$dUJF>%n zlHbu}@i!@@e<3v=7I0F(LVNi>=R$HmNnc%^@)ve+Vz7V{e`lrIyA+>iH2p2$#Qu~H z`@~=YC;ICcPws~|lI4C}Bg^&qAX(OnwHaUPKS#Yhf88}Kx!;RFGdy9rpM=jQ<16>K z{g|(;_Y=sn9=?{!uR)geM{Sn)llzZHmh+Zt=}(^jHtDcW`eOiD+B2N_%lSR2o5`P+5+H*Tu`s*&Tel00plq~(fELp~DHM02cLza9uNa_2h>JLou&MEtmDIP_Z z{u-a+W5|-lCEr`g(w-S)IWPKWik~6Nc)gs`&moJy56RM>^M5#W{(l?E z^8>l#`H(EngCV)&`&+V{FRB5FKiPlzWZB;p$nt$*iQJFz{dbc3(%uWHm-byrmiawv zT4FEyYpIv{G?~|{@J-Z9f7i&;9}!u`$NNj-U+xbtQZMtp@KK_d_3F`GlJ!X9Rk@zZ z^SF0&VlV4YgZ`v`ymz9P`iq^Nu#BJGB4L?d-N>?@FG`mFS&}UEmnX~oT{Fc6vgEgb z{q}iMq+ZV3PaV@azne}?SjMA7d-?vfn(`l##lA+C?@QiG=|3e){@;^jJg(m-i7)e~ z%=KOT?{;11{IXjnEdAM?EbCW5mib&H%lXA*wqN>tx1Eylm-#i8_A;JjvcwyBSYj{p z?^x=^Uxh5^L637jivA?(<@=pe$THr`^7%#hZ0aTcD$G~XM*&!=^=d_VIIS)MO{CQH1&sq5$C z37zwMmir@_zb)G&dhyr2gOmB_lVyHvN|yHSp4tywk!s)8DSz9OWqdB@e3JI>Oubw$ zdy!?njU|hH{p2Knu`g3E_v>>mPu<_Bm+QaQVV|`3L9+OJiY)8VB3wTtpBJc?{&*v0 zKPRRCm@Mu8k}TtAwoTe2^Q(<4`D}MdqL=!29GtMkUyAnfK0-j2{m0H^x!y+b{x9t- zQ7_L&hmvLet&rvYtQuLq@0h~zm;QN;dhtJ-Ecb^~I6jj97aUK??_085e{)!0^mEDC z_SF;r1`>G#u3xKd*SUZ8;e3;L0rk?Kw@@$f!h@3f^1S1xxH2x$%kx&i@sRK9o*J9z zWqf;)WqdbG<@fdYWWGxKb~q$q$#2ne!jk_0)|dX?fh^a@uoPc?R+5kCcT2^;V``$8 z@w%5R@kh~K=3|ZHE$inNGn4uT{Cz$b4Ap=!7M|mpkGm>q&zw?fK^(Njw=Z zZ(wJC&WVX$`YWJb&WpDpi~h6J^GWr9&i+r{D`6SGCbIP3PA7EMzfZm7H{*!T`VIE) z?0-1(k@-A|Ec0g!S^94xS>jLXu=m@q;FD8aCCmC1k>&fr*<_i|?|0Ns`v1z)lKz$R z#ZRf1>+7|NiC+5Wi zuSi~nTp_PWuBA98_om((oa7_*7qH)MQJ{V~+Ly@Ue*q`{~gZ$6Z;!zFYC*lWbq%7Wxw|d zS=#f&u1S2cZ%{Amwb~)cSL~a~(m&nF(*H}6Wxg((;uXnqeFS9rKD?OX1IV&|RLIhv zkSy<`MPzv%jmh%;zZ#OXN4`(>$kN{dS)OmUB+Gnyl=~~`?;`c`{q;DqwC^yo*zd*h zm-%uM^|Bv5n=IpT0a^O{3bOR?8mam4#MGpH5d6qhF^_7cC&^;Y`lA7s%#!1jo~PL}Jf zLGH=#qet%DIltV&o#S;Mn)H|WdxY_1zAs0;#1F_4ZyU1o=U}p|S4Fb)=RRbKH;ydV z&jhlpFCX&xL;N3~vOkF|^W_Y(#6OEH`G;dVU(dU6{AGR}OTDZwLsIq^rs9QU`Tl%5 zS?-4?vcBYh5B2hW&I2ibb+TOF4YFL{UNOmEzTfXd7XN+8(%wzU(w;{SPU=g4-%FP9 z*n;-bp99JAyfd6E{dGLoXR+Ti74MSN^Toc@OTMGYay^aZ^(^zRLcN?P&q&q3hb-;g zmHPw9w@$tE=ah4j_RGIlvw%zOckH>em-aPM{4H7f%j}r=6ZxrzifUe*@}ez7@&xKJ*x}jQ0ex}pNz*Pv=OZ-XH3!g}q^NYzH z^^@nvkSymrcaSCDd&zP?zdxnlpVy!42Og!qsr~9u>Y0@NzdQ8F_48JW|DO83=3VM# zygo?TH^}mR{CDJT?7yaAGX6<>3%sAod~c&(`e&IGuSS;n*M}_m9G;pl>ryY{vl&^& zqezzXo(fssAHO$c@4X}QBk8Y*dWk>!TxnmTf0%k1uSdx;zn>+`dhuq;-|UqA(Oi$^ zdiWq!{~NNjZ>2js=krKvJiom#(aZe$G3D=*4?5R>jOVM;|K84tUY>tfB}@AP@@^de zv8ns%8Pv=8pGy}1Az7YJTTe~mNq^o;y^Qa_QuUuEOMl#!x_`#h%lY%9H#+x6)9{4l zesL%JPx4!YdYSJ_lO>;3$TD8*bl4~JV`H*BUv5j5{_w6&;>+{b4k>@TrTqPSLg#!( zQ7_*|mC5q_cPd%d(~HQG@3mxUPt5gPzK{KDs(wV4^TL=c`A_HlMcP~Ce3tY1d9;`N z(-$fKW|yRW(mz?U%!g*OjL)KE{HmCm|KAcR`=!V-KUVrn;$PaoJoPgEtCJ=F0$JW4 zDU%0tzMr3ZKDd{9xjrsR-M`E1U+KU1sF(Gj&ie9w&nMH8e5JiVQ!m$Z#OEbB&-1xH z$@n$xn$#Eje&;3o3(0TLh=j$zM|;UHAj|Vcfh_Z71)dM!SD4!Se*u@;c`)szzjq}| zKELq%QS#|^Y?80^M~U__zba&TfAjt05_=i1cv8Y*e<%IP{QvaC&i(OP>id_F{^k9N z8D#07D3#B1Wa*DL$kN`qDOMwr_;TLz>;(yneQ)ZeKl+iSzy8YpmiBH&y|k}LmiF&M zmid1GS+1{xI`n?~cpXBP_(!MsCEg#z--*;q|C~;i^Mdm`^ojq=$nyNMG@oZ=JqW2^ zgV)#NWXb0_vh2@dvV4E@F~?K#`I6U<8*QbhfY4zwBR&>H5@* zKaVWyOF(;h-=Ivru*dv5@=f`xktKeE`N{KInf|4HwGLuRXRR4a(=MU-c z3sTRUU(jBjmv-d+U&impl)t9kJI|NJ$TA<7AgE0vr0myA@y01GkfpsP zvV1>NCCm8TM3(l|$dYg6hon97JR4Cj^XECTtY@#1WxhwBB=u$em_@zZA1?na(aU=O zuP+jo@%V`L!eu@WNdJFLz2y5-ik*>(KRJI{z{&cUr(UkFZXNcCezBB(c51#{&GD1^ zJ!miExg1&6+tpI~+fw5{Wnz-Aw5NB<{u(|X;TKuk*Y^dSQ~zc<>I zEcdrES>88#jrRu`&l>e|-v3lb{UpB6`@h7$`O&03vVOk$Si;gDFSEXk?`*R4UxO^y z!x5?dnA#)pC+DA8vOJ$}MSF?YM!mFW@eX_DPnPrh0(n*5zsqE4|Jh{me=%9EhpSUu zBg=YuGsjcT8y==!&ZA?pT(94dWqznVllDu#K3Sf(|CX9hL#UVb?@X5YJAy3vms0xT ztw}uDk6GTYWxV$5@b9bn|BWTf^U$??e^TbhK^^v`cKh>3Cif$0-$d%AeScYofGqJZ>hR~c$Ge6s`A;EBez%fkzg8p5{r~&-6NCsNPmBSQNogc zjrSATpNu^{>0j~xGX07D>{PrrdA-T?wdUDLed)i?XfNxz)-8Iz?y71u9e zpDg#Ib;z<_yvF^2+%FbzlK)%hC+(H-z3kA= z9@+VK1jMeM`W=VWQ+7R4JVN69hTlT!cM?is&wxSd_YtZo|H9HP$x~!V`%6>xI`pab zT*}{Z5WW1qM!H<|Mb_{59Sl)Wjy}F~Kh{&f-{Ga=kKpezh`(y;_bbx+^zU+{^{I4J zT3_SuSBRbbj)=rh|E`CWr}L8f(mwfp3n>>FrN(l z$I`!JK|i9easK>=9S!~?UaEeHzvt0ld5>|O$MaI=D|(45a>La4HR$hB{@zHs-lc4> z)St=U3-NjVtj_sV=kI*HLRqEXGV}B~ujF@5dZh9Y|DumM4n>a7QM_J??B6o;s**=D zuH-X<^XM^_dyLoBJZGl*{r~Iyr2o=+%{{#H_)GsuyML>_>3$Ud(tpyQ60gesmhqJS zm3|Rf<9aFAWqN)}x#GM{|L#ke>UYUY{7+4-zf)7!TiUf%q?`=DFBs`Fl0# z-_x1O`4scId4)1&`{$CT$}go|+FzaRdWG__R6C`9x}Nw~TnB#hcYpZ%M(KI=PxO`4{Y&m|qEG*hQbT?Ziw0q;Kfk5F(>cGykI0DYtnlio z>!XrdXE#jg<^K3v&lB?dRNJNQgB|hY_o!0$A<sW*1Ka=w~y^cy8krGe% zG5#LhZ?(6a>W5mYeYMp6VFd3l>HDU{lkrZom+~X}bY4>bx1Rqy^r`Do{K>qpr|yHI zuX5c!itXNz*MaC|9!Xy5`>*0W_$~cZ&X=PoqtyOD^yzsb@jLV^IGyXwXy$qPZ}g+o z54KL$b z0a^xX8ahD*e4I_0o^>_jA%e@_Pxg zu1dbLUbe8ltb_9J-OBnV&kwS0$-h4=>ygZ2S!d+m=auzC{{7yyc)dw`HzG?uTa)GA zLzDZC^yjLn{v1ww(eF-{{+8=o##gS}CD~qiPLSsvdES`+d^P_+d2W&GX*B&y{dJj- z=*7QC$ya2l%$}h1DGQX9Llb>`810ToWZ~FE7LOtyL!Ok#GG#^NaY?y%d?KUM6R9pq zWIZLLE0Xf+m9!tu5lzYX@I+s_Dv{nHiInzNCnOwFMwBsSNBfiR{muXHX#a0obXIh^ z{j4VKud&@-ZNEg7_6tkH8NqlL6x|M)>CKT6F@aaTQM`z`1Xt&M71h`Z`z zzTL7?&zSb1UQzguIDzeEQm=i%(Q0$yKj`PD9NWcwhW<%Eqof}E;6F7^_z&iDY^Qo! zozRK9?(9OT$Nsrgxa%(3EvKdG%}8-q-95~D=$iKb9{-bmUXgn3!d*>-cAs{t zXVh1nxa-c$dJC?L`L*&X?y9Gyq#nj=ejoY5U3EaaLZ^CKZQY5x?rcb>dRh(Z#9j9@ zR_gKk8ZX>+7wwjlq#ozbDZ+oy&t+1Ney$Y$gMMaos%O;9PTX~8|LRmvt0y{f*ZsV; zP`&x{=k0~K>t1kwNY(o)#a;Ea#QL4Di=|TBRX=M=J&xB}!d-PhyZ%y->)`<5Kj>$W z)I&b=*ZHFGAM`UORc~U7yXx*#sn@=4XmyrwR~^vq+El$8Q`}W|Go{}A>m3jO_X~H` z0qq`NxE}mGzcBv+U+<>s%}sGvUCm3?`?G$&{4e?W=D+0U`~Q-kpZ-gJn)1oIAhZp_sBl*u(9Wdt>G>!9Y%KM-{%k7z2lE-Sa6LSS4_}zO;7j)72TDCY zM~xHif{%qwj+J`!bE5FVMqT)PZl`)iUC@cU;D9FANCO&rhj(&W3;Px>#K5@dE21-1Su2Eqh8mUSF#V|3N=nbgF06Hl4WZ z&h}iW-u$MGT8O*uMX7_O9@{lh_>VY&?OCaMe{}yY*Xacd`$6HA3v(B85VS6pOFjBIQ1}n} znJD!*504W5gMQ9Q)%&CKu>E$0n3tyfT$$porgeL#dRk5I#9j5G)Z$As@~lx{ylfHu0AC7n9rlazvm#Oe@^Pr z&r8B7yWhjNQ}zDn^)CMB(hrw0^=Znl*!`aTK2`6h6#rg*(YJ20@b#tD5}jDAep^aA z){=TG=qvo&Rex18KHq({o94RNIm-5L-@C={HkVbs$Mz8zglUbLg}AMs@};d zUZ~3d`SBd7$N7Ao@c&usKb3b%J@}dbK>s)4|1|wSOP);CdnU#IXXS;IZ*{7t)!UtT zA;aJK*t%)w`TR$(Q(1o`%u4cs-=jWE|Ese6o41nh(>`T+^4DBS{lN03&60eQx8`sT z$nwQl-k*NHWcj)*4_Q8pJc9CCmY+pFkMcT}|DF5*<@GFoh5Q!f6qbKW{)O@emM_~s z$=j#Ak>y*F2UFg}@?*&r%Bd{Bk{nV_WBJqM=P7Syd4v2FqZ)5orvXL-Jm_-~`Ev3w|b80B=9k0&2ac?ZjvnYlJZlQ|3KD5lQ^HTe0}mJlns_oARkIOkL8z>uciE)<8I!%l9LXqx_!bSCFrx z{DI}qlV73yk>#yBCH@zs{E6k;kawW`ndQfkPp15Z<GUa)AuAcwj zS;Ca(vwQ~m9?A5~xc?HWiAor)dlI3H`2UA|f^2y{X<<%^&k?*9uhUKr5=TL?$ z&+eM!(@c3S%lncypuC>tBglJEPGR{O3aiU6ePnd=B}2%9~j3jbQ&!PG$Le zALPG%-G-2Gpoy$7tgJ-gmxdcJAFb%nZBu_V>r- z!=Cw`@44rmd(UmJ>@M^d1787oH}ro7eiCvS^q&BK33(6nJ03;x`~>n|=*Iz1g1irU z3;0aP`=P%b_%6uh(60ji2jm0L_d1&D-5l~k==TFY1o9#1?>&yLr)r>o82W|4HsmAF z-v|5v0XzX~}RdZUWs*#vT1==TOb5ORCytAG=b zJ3xOS@THJDLZ1TuD`Xh@kAc60+!^}K;uOyS$X%e{3wS@spF&>+JQXqm{nfzNL+%6p zpMh6E?hE}Vz+XZh4E=7^6wl8g4}pFLa1-QjpuZbg@w*L;oA#VUQO=KLvOiKL`E>G7bH&Ybnm5kS{=A2iySpBJ>vnFNS;x`V{aJ zkS|03Z{SZMja{h!2PdfBFl0CA4+8!*WOwMN08fYP3H_PC=Rx*@{vqH$LH36JBjC>< z`#|4!D#f!UfzZDQ{5s^e(0>bDHjU!i z0s5ZLNorqcR@Y@{rkWlL-yX4=Hq5FsNVjNeW3pp@KDH2p^pPkgWL@IMZi}; z?g9M~J;?vP(Ekkj)xiITe0n#EXQz6y-xV^B8|a4smqXS-UkyABG6DTXz>6WLLjO4M z(~wlFVerqN=#t+PWj#ONW6aOj81r)}{<#l+-FoUoF9I_oAr!2pNw^T&L{JnKQ6~SW6n4G;rbY}9lv+X z`De`eX3Y8KdRX#%xjeoZ>paK9JdYQ~JRkV|T=vJ9{V`^LjM*P!_Q&t(vOmV`k1_jW z%>Ed&KYm}A{V`^LjM*P!_Q#n0@q4@Mk1_jW%>Ed&KgR5j-`{0_jM*P!_Q#n0F=l`K z9xty;jQQu|81v7$G3IrKF|Qkp`RAh;^E$$qe_n|(|J)H{{`n!sJboGT&(ARCpMzn{ z^MNt{{03v5XN>vhDH!woVa)T2G5?$bW6lR-o>z=HKa8I`fc7uOJWm+&IA_e`lrfJp z#yn0KbDkOVd)AEkJ!i)JzA|He@0c;aXUmw!7vuiFQrpAti!#seeKO|v3K{eGWX$ie zG3NKt81s8&jNdz0t)JhkVxHfRV$AP3F~0H;wLG8WnCJ5wV}1Vcdlby``w)zIJ!j1C zCotys3^-3bUN}!Y{uy(BG3NE0F|X(RJ~EF_&Kpa9KbYqqV;-NJSDsIdd44eF`N5d$ zW6bSg%=I(o{$$Mk#p8zSXUz38=K2|PeT>;3k2~i1eNuiul`%{1XI}3Z^ZLb@`;Rf7 zYZ!C>81uP=F`p|Kb9)){dc~O6E5_U(jJZD;b3Pbz`x$e-8S{F?nAao5ydE*;^@uUA zM~rzrV$9>7F^_x3JU$rnxM0kAWz2bHtV>=`SkL2)F`xe!^L%2==Rw9izZmm-U5xpB z$e70;W1jCE7w4NX=aVt#lQHL$G3S#p_a9?!FJq2}*HMm(-;d?@(0LtYp4UjXalq>vV;)b8dA>2`am3@8$1h_Zzl?eO zGUoBinCoNA=ATRB_d5`lQHu|J?&kA0pR+k1{QDgEe9h--K2P&L#P#ye%k%!j`wpL%`JBx8 z;-8P^^D*})=Znv|e4gbz@&3U30`CXB5AgcW>pq`L_?*c?8D1U}qNT&lmdbg+9_uI6jZ$RzfGgq9+n2 zB<@3r50bN?cTXmMQy5sMsP<hkyZ%GsOoPQ}`mXi(X8!FAQ8@w%-OH!k555a70L%0}J}- zrNmEz-Ug0bPWnD5X8{9d#fL34&WvLQdb)3b!@yg&5+4QL9ykts0I&rd19pIq0!{(P zfnDGkz{asue=~3x_%z@s@VUTo;H!Ww;G2LQ;QN46zz+kvz%K(E$5H+70f&LV1&#u5 zGn@J&4tyxE1$;QL1AGE-3iwoD7kCM*et4IBmj z8aNJY%tiZwdjUJZeSuTJ+X1`4KLa+Zss4R|!@$1+jsi~xjsrIWTfip*JHY1xr+^m$ zyTDff8&jzMyMe>Ne*%sIKMfoQUIlCce*^3Q8}q1tQo#LyUEqPhMh(>;1`Y!s3LFJ4 z2aW@e2eyDKfF0mk;1qBRunT+!uu)6(Ukw}vz5_T4d=GFO_z7SO_*GyB_+8)>@W;R| z@D?Xh{}>6X|0lp<;8DO);1hu3z^4LRz}Emf!1n;BfFA{Rfu997rc(W{0EdC!295%+ z295)N3Ty%QJ&D@m0B;GL0^SbT1^y+lF^%d!7&rnv8aM_#9@qpv0@wyV9ykeX0;hq` z2F?K6z@h0>{~v)Pz>fjPfL{hSfxiN_fw%fSwLb~G4{#dzVBidJIdG_s>i->Z1b6~) z40sZ-2|OLx25taO0=ELEfqxI20X_{lbOP0X4sZl`5pWFnUSJb=1+WeL9&i#k1Dpo_ z4mblm;AHBrOeNL76>w-G;eo&r;BA3pz&ijtz+qq)co$$}68YN=I1D@lI10Qca2)uT zz!vb{zz*=fz$xGZfL-8&fx|~o{l5Z^0{;d$4qOgw0S^aG1CIjE0FMCF56xBBaI1JnX z90i^Q90&gEDKvgwf^;E2g8UEUCa03!){r|u?g6-&NB9EZ^!0@KeWJ`V!j0&!@STL0u>V^Kdr1=CE;^#nr8srU-F63?vWcMNCx#VXr$Ri*x%%i^q zdiJvf_;JntS>VqgIi7EUcb%u!%l5lNzb7QOFM4d4KYB8*VJ0nIf4o{w-XoA-X-#ie zg*`ogW4nx|kHr1$EKQ%%F#9vA)$&OVvz@EyjVWq5whL?esD{}tuIa5Be}C~>RiDu? z`!f@oorbe^Q&qdjG!?U5O4Em@``ekCKBZyy7phasoArdxhdq1# z=4hDh!l$TqF%7d_O4DaF%=N@iRsA^{W;?@D?V=i%_on1mM$<=53rltOi{ne47S8JA z2luqF6xlDfi=H0N>f{Gg!%U_%eRzJDDMiLLy{+M_@fm9Qh=$oNqv^wEs^!?u(e!Bz zvz>XCTHetx+r`dS?JNzmU1Wi37uPV`h0am!q8jcFLs!#>&Q;6tI7n*xjE32tbDmm0 ztzq_Oov+$uG|c{z3&}2WA;}n|fiJd;TtqzU;~HkW&?40?s$sTsq33$jm#F2~E`6D* zH!fE(k9%9wr!;KfE45fHZ(N~a*d;Z+t6`WK)|G1cq=va2$*WX5SHqk)=W5k1t>LV{ zYg9W|!)%vYqS|FN%y#K(RXf8`F~^tI^u~4mcCMxm{lVWZsp&Hs&c=7WTHd%p#T;K! z)4Lir@D;mJEpKU<{Y7um>@>{dFMP9V7t=7?g_de|8aD9dYWnakYB}x~TZhG|cUFHGTLtwH(_eHNC50wzF?n%cnJL;46HGYG-Sh?TkBByQqfQ&eHTr4Rd^v zr0UPqF#F3u&+Ap@ZnYfe*Sbg5r!>s|Lieh6F%7d_Qq#xoQ_FEX()X+S^l}xmzsv)w zKJuW7+0N4R;fGW^uGe~4)rTHcG210ms^0vQiZ?OnEAwYnpLtxxJf6)bRekI!6|-IP zX;mM8R>f@RzM$%@mk1|cCYiMZHt-dFjrjEIs-5u$VVD`#TZE%)NcI9@0%z_1O}zC5 zNv<~u%y#CN#53l8b~SzED}Osj(`UZ+w+nrv>chs)T%{+u9qI0>KDwET+0NcV)w=@- zhqorl?T7($W%hO`x3g*&0cN{2e(=uORkce4XYGCpe^79}=I*LKIz+{6=V_zsPu1Z^j6lhm&MGj-#`s^Dx!Z!{}l37XRIqjC&d(p=raL z7-8|R4fhay$cVj2_Pxbt$>(CruySx%Aft?ViNtSQ@6m?4l?fbTY|S~qYpV5olJa-t z?kN8E;|_q{IGK2<-ckI|^02dV_$`Hw`}5}~)BUesLGtf28wFeeSp|6l9rf zA$ty^_Vj}s47n@hFCY(uEQdS{auQ?}WF2HP@%F&vnAvXkh??f4S6W!2*?SLM?uy=&V-x;c^c$-ke5PU3waCVGRQ|D zpM-n?@^6suL4E@H4P^Hb)Q-N810i>Y+!Jzt$lpSafvki)4ssggEXb1}&xE`X@(Rf7 zA#aDgAM#I-&p^Hc`4;5+ke@^T7qZt#YEOU2?I3r9+zawx$YGGiK#J;+ZWzk%%jJ8DN? z$bpbML+%N=Kjd#A$3Rv>9tSxMau(!CkY_?(2zdqM^^mti-VgaF$Y&s5fqV<{eaO!t z{|njcFltYK$n79^gWL=9V8~&R;~*zPRzuE!Y=t}(@*K#EA+LeF8S*a3hajJTd>-<3 z$af$=g8Uk?Y%H~7GsvwWcY@pla$m?_Lym$x9P${*1Y{%RJjnTw3n4FuybkhK$a^6l zgM13|CCE1+S3`aZ`7LD6anzoEkb@z2h5QBNfso~phe1w)tb(kAY=%4;@@z;PC$mBv zMSZ^>Ii1d<2Wf2lcuny`!DtV!rWX-cq>3tzK^6)lvhY`hOiJRdhFwas56U?GOi(p% z*m{%C`uh((=dWjNJP)7s_dHp;FOh6LoaU#2Ux0Wz99husRzbY2@loiHg0%ii1-bkb z=x0HO{-^R5^ph&de@b3BiZ3o7hyFgua8H#_LEmi>l@Iq(`4IF+9s$0u%EzI964D-^ z@@eRwo=oKp`GXKS@ww3d?nvV89aKIJ{Z2;_AKO{wBhcRl>HbvZQ_#NzY45J`F6)m* z`}R?J2l@|~KS1R(&@VcM%BMBnfqoB+1M5JweDqi<_b156K`NhyzSr^aukm5%=R?L1 zR?FMauZGNMyirAdkAbvrqxFi%j|u&y)nsSft@26e?J2}Z?^AgP`nPI`&pfE|8R!p8 z5N{o##vg;;o{IWYYIz6xGwXJ~B$>9l7^xmXf$G0V| zR6cV&mA9%$mOE|tn`H4nz|N^Ao?RZH+1&!W@D#E$A^)k_-2pqhmh4=}Bd$~HT?RWd zq1M~u531enz!})Zrjq?Wn%#q3?=;olNt#^>b}sDF)5-q$>(zR>9|}pewl?b-^U2?k zHkJ3uD)QW7#i`)hyAJU_3|dBVIwY|rd>>Bds8bPFddj-XhHF&b(&~*ar+8GW;!&)T z#(nW6O0#?dc}}%ZhE7NEH1x6AG(Wc^MR9x?>Pta zf1z4F1bxM+#9NoEd>r~7rx9;oqw-r*gc$hW@4U;j@V6^)Ysv>OXll z@w`4J?^F5A0^)gnjQv^VW9Jgj>tk}I%G>7=&+B9KLp474eBxU!Bx!u6@)q>_T||87 zHPwF<`hP4UKJ$jkXP{qr3GwNFsJsLHe=a3H{+7yzE+ajUgM6~)K3X5buXIyPW0lA>*IHTn$y^(w8c1MN$0APv?l_8zOHPYxvO^U3H#>iXn9qT+nz z^68xoyk6zYOB{L8ug8r_2sEp6UM68#urD^0GY7dRXlFO>aksG4k>} z;wp50)9Z_M@IF{%mO9Ti^!Y7cUM@J7=HVc%ff0;DO}*h^4?0)hhPXC-h3l@t`GNXj zi!13|l6sxaFQ+4{F!bw=(lE}1U55WqMxg=)_#v+Zsh4i66l1yJqJgs(y z5r%$Y9r5O>UcUXa2z>N)f1b;S-&Xm^t>pJsln;HT@+s)|Jb~=u->7^P`jwDL@h+JB z;{3VLTQjJ<(Ocym=)?8IoBdQi3cUkqgj7BSePkw;w>3Tn{oRn6!BoDx7iHTwz7KC~ zqxxqH?!RPzmABD<+5R7(@?4(z*tYBD&9Fbu^@o0{@-FIs0_}7DK=Y3KKMg(C?`Y-2 zw+-<^jNMK$<*4J+gr55+b%0vG(@5ohe2%lb*N;Cw$Dtj7$KQ|7acErp_#B7&A*km# z{2Vxaq`H2EOj^JAIdJS)m5+eWK8LMR`51WnXlkD|MdeNK{2VkoRpl3g=jWVhjb99& zpOeO?spW6b>W?<4d-1&xx#C{LAzFP2J|t^Zd;u{rQK{KI<-jp674qZh!s>ly~y*ygo#h z`Ik?ld@2wB2Kd-L{^h6NImGK~v-@tIt@y_@(*Z?%3CdXCSyPc0u=M&&MqjHOiGf&RgJ$u9M@%BP?ox*YYtqVh55fA%2p z)<0A}3jOg95pVxXSt?{82s-M?~^w(^_|LxTFHNQe*ahHE%1Cku@6%FKk_P-=l9H_-TJ8gua!3sRm+=N`OxNS z`PgjoU+6yMDY%~$K3FZEM0w`pQI&VWhfk+^V!u)2OCmnrKQa^4^07Hoj{7fsw951P z#p`1-56}Bc^ces0#yqMgorfO`-b|?FBhbg*pz`)mwS6Y^-?Lq_T0V3lmD}?ql9|~m zABBDyq&rXL)2#nJm5-mQ^2W)eUkz!_*Z5P2-|KKXKV{BW`OK-r^LvPtJz<&;zd{!-QBW|~f{5UVEeCjRIht5F%yr=TqKf8dBzwggm zZ=-yT+I|Q6^gG1c(}?H$lNso_{!EWemGq^Hn|#zVBIN7e7noL(tz089P_ylhAYf()DWpa{EH> zl7G|m=fmJL1JwOJ0)6ysDj&I9t)KmK|2X&g^M|8+<^g}6`!DpEKhOPVJ?_tQ|3p^$ z^W4A2i~c<4Kk|k@e>CEcec;dY{a-f^UyJhAC;sId!G|*be3I+$zM0qMJbu&ApRj=P z6Wd(nE$BanG(#$HoI~YiV*I8n)%7C2m0xlmNn?N2e-e5wA3i|k zdHity#}4-A*?;6vf1dj%m51m4vCI9-b9}}~;<0U!KQjTX+Nr#x1l%xMRsXhUH>fT-(O99=xUWW){y?A_eo~1*Z2=e z&-=5zLam?k&*%H_Q~rGR`gx^4&*%Tx^Zq>FA8_;Ve10|4{^j}nYP{gj^Yw+Dhv)kX z;TQeOM?a)~O5dXPUmW_>N5mUpb^mdpzw#5}&7Z1#68gV>PQ1If%Dd1{_!{*;pw$ok zwRnCSe%(L*8^EXD^5^;IV4|P;^L+gn8?w1KBzXPi@xjk;lL?h~Ij#%Q{yLS9Lchs3 z#5*h0_JyH0VQ0OmmbajHZIpLa-nfYLe19PIj><xob8uktbIpJ4tFl}|%&!cTZlmFNDo!MhQEp691~lt0h&+dSHz z{{q(snd$y~;s$D;Rp-yo1#e#J&z}k2xXhow0DSy%fBq`)nLKIj?xqxP6gd77wR<$M z1I+C=KPH}g&EDLzF0!S6y-y-<4(z!9*H`ZGD7DvygR&@HCnP_m zb!1hF`0V?};GHkj^_SPX&@5`_IQTPxd3`gtrglZaM?X{7Gvjl@yq=|j)!GarhVsTX z{{8d{`YAG)_};Leh4#Jy-bH-8eyjw~*VXYRY7gx?hH=1S)F1ysc~kSxVP)4>@LZnx zLh>i*cV1t)y&UH+z#pdB&4pvO<9hV<+GIkL`uZ9Lulv)A6@fpw*KqOPth*!e|>$iz;plW>&yG#H}v|l z?O&+>^Q|wtgFiyE`@gon9FO=pv-#GS7R~MwV10eLMzgzzah`d~^%g66p6d3OucUlM zHNv^8m!|n)U>SanBU{_u6h{OytN(#M#5Qof`SNnovt2VUHsUf$%uD>{j{UwUQ_Dk8 zc^UFt*UZbW5m%{s>Fj9~R9>z?`@>p;?E8aR#|->_H4Z0@I5I`XV|8%8gqN%BPAyT- zvm94v$$abB{m;{U32POlwD>f7!$EC-i9A646ocGw75jV61eBLAk(am@mQ`dw#2-}0 zm*!?YAYI9Bnx^qmssl%xFZMTwmg+*Wch~Ns;LZm5=F9%CQv3P-BiC#Gy}16)6luqP z3rcOi7;j>}Ko%+=TB`2*_P3?wC8^cpTpP?fb^-Eokk;TNe!fyuw}at-Qom);{?LD^ zAL5Yh3KUWxFE=Bu@O$Lng4By=G`+RH^}=y#wKg7|VDhrd>;C7laDTotr}xAL#@P3N zgSg^|!wM*#WI*fKm^b|MVs8^rUi5WrAjM%q>Z2l~>68B}ZQauS#qe{;tak&NFaP;J zo)_*vmT_%=VSTaE-a#FYnd^dC$BzFS&6m+y;Qv?Vix#$Fbucf?jCA<8ol8l*0BuYvVMj*_AD-*$oJ|xW@@-l;Eq;LB%r(;{T9uaupaOM z#r1<;tiu?2nTfbe#9f&Q?Q!Q|z;H66=Ktge&iRR{Bu*7g^tgL%mWl$UYucPuZ?30*5M z87&Wy9vz!6vAcqqFBgBt58oUi#g zL&I9VMi07gS%J88I#i&1RLhImQ_Yj^Psiq2YJ4I5J_X&bvv9t1@|T}bUb63Vp~Cng zHNJdtxUdUtLhX6N$`qs-1H5q<0nET1M zeC9u2ocq=GYT<4;h`c&>+OJ=~WQy#ELhUM<*Uri2gOryPr0y^L z5b+U6r?cnF!@!kwI#%wK|; zFRy*oHRsD0h^y5367A%?*eim`%Y|S2=Oy+qt=sL^i4Cp5{&ElE${>!|BmVKEpto02 z|GAyKzoecGCNE3ArM!f-{!ZuV2hFVWA@cGF;)*>=`EVfHRrnb3A;|Eiox8t8UkN5J zXZ)A)qF+}gANL=h?fRv&6}aBN6mga6KHkaeSmbZPpeAw0Uf_;DU9yDG`&5OB( zIzK}FRIJa3LfCmTn7qvHN$uD7{pk1p>v^GebePA!zrKEw9o+YmTDxNg{a(NIji*q# zeE0cx--PlK)*J+u7o*glfK~%fc5r^E-o)>#csrQ+(uBOM?|DpbM~5-iu}cwGsq2_t zf2M^n=ZmZ5A>6-X^Tl{Kn7l05l-jR9e_7x6`tIoa7ojj!bOXnhM{9UG0flDkqI@t+hIPejX$S>pZ@-c76LU%||m6$2OJ_^Zl0)SE0P< z-E4RAc`Wr&FnOup)ju!cYXAP$`=Qe@=F7#1%SIgGDaFMT+De@M@qr4W0p(>8@}loA=B%!fmki>FHWe37x|8RN6;NJoLtaAK0CbA%2d!Ag zVGnxmB89k0b(rel{Lt$Q;qR#G`p&}n-iKK>gx0YkT1D~miW{G)BJDKy`JuLsMbD== zY{>itiLtis*uJm(k7R<`U!K{6@}fVlOkY!6Ui5Z!7;F1}atXx|x;B`2LZ1hdm(|Ei zRO`^7@5B5Lac!*E`;B}0&lfA8@u~m*(hl-0Qyp1|v91Rx=%zjv{ zC*41oygauLza8v32~L0m(cAU%S+~cb-qM)2qrIw zj_jIwIUI47nwNM7=ZpQ7nwMlid3kgf)NRKAUQq9|+=p@()y7!D^g<^3r!A(0u86Jk1wd zn=k3>it7ihSjQpu{hbk4?0SkLc0+OTgtzV3zHi^C*5i&0W*vKBivK!hy{*RA@eVK2 z1M>1N$MsK&!+|X1CAwY5@)CMSYxn42^0MQMu9=s8IIa@&;&yNyv&U$82q-UK%%r@; zw1McBZKi2{AgJvxPB)4p16e2vw}bOV_a7On9{4a$e2|CNHNqQC{@-lVj7nMqVb!S%~<#AVE*I2=el zFXoBFTP%0d*0HdLbq5-boT;^YQ80OV#-hCF=j7OpT6|jk8w?^ZZy~P8O%z8GQm;35 zGx2fAbcuP;{W(ds-RY}>$xFk0%1cBGF!Z<@pGIe4^lCNk$j&rAEy%24u^*6^(-2n@ zafF`mkH^yV&idAyC%(`%YWwn-t+zVg^~<4WQC^~&=b-N=PefcB>;2@D(EdSMUXm~R zk5A34Xc&H}NPn0w7xH*{jmpI#3so4pQ{7M8T~u7Cysgy}ae|pIGtcgt^JPBb+F0ky z(k_~pKX2rD`BxXsOOFLKk2lu&5=Hx?+QJ+3zJKgSo|iAs{;-x8dsUJ9v{tUeP`lnw zBaTdw@kDklZJ#d`uNBa~|K0htURYXynQsEh3(jL4j`=OcVSZO!JTdJ&mMk$($^WYL z#IFnHJa)@M%8UN~n6dlj+%-R_EYs$Tg*Y-r#uNF8I$ul;m)aONpuGIqro8As#}}#y zCNKKWBgYYkQ)E1*HeX84gH})I`e5eEKQ5xY=ij8GKBd*;1eBLk7g1iKTEO=4otu})jYW<-ecaYZVvHw_P z+`6M8alZRwOE3Ll@{%gHeaOqxh^y4Rq_z1{WcOsvLXGNpOx+bsUOv8@@-jLb8GW!@ z)c7GYlsJEEtv%NL!Q|z;D?65#Y=S!L1M<@EDvGPr zyo8da?I(KJ&WY+ci!2W&FArVqzm5fcy`M&08|!_TFS=-6wz!7I%f_0QgV6q{Hlc%_ zFBKbkUVh(2^K#`zo|i3_`0p>t)7AY?n@$@JYWHC>h$B8fn0VZP_WfTXFJY~x-6H$J zJfoBQt49wnlEV;}7FIzR(avMhpO$t$v(8o96MZ0<^H{_6G+!2LElxeE#;4I64r=oy z@)*ToLl&wyrsbuOz0PN}dcqF|lb4&37yZ49^vZzpV$ghf3UTTERtTFN%!}?n{!lP^ znR$c%{t|n(bMq2@w8-}1y7FYi6@RX@IBad*itS$7I+oVzOTArW+5xl4FbO@5s2~?DDm{onFg}eH8gm zLbkKl6?>|Ah-+9kFNCA-26O$g*)spUgyY5aw^O8Dq4sBaEicY=iX#P?wafFt`nj4H zSHpRV?P_tt1WXS!}zsD`osQmDB>!0 ze@T_NpXd#V+^EKBuL)+~KkI?6xxYAwYh&GCp6a4`S+kMnCH!F5oG(KW*Ty7W8`e>*UGJw6N2bVlLU)yR9a$*ca6oxkjl4v)0E51+?DhzamyLBD`#IX*)$%fS zBhSluT{JIuY~*?Q0`1q|9}7R<$@@#XNPpP(H+_`Wu~M%qBOTmd>{rw{tq+3P_s2d? z?GNeu%Uf!E8odEw56^cs;wrhKNaybR$qn2O`QC&5AKI_K2W!6_OkVWgLt?xWOdLEO zoPg%bFP@s~%F#VzD#SJJ99;Wet{?5ob zdFG4pudb1o=;~nN;Jjo4%F8n3CCq_9rUU8^eZDwrg3U|lv|!e;*O3?fK1?d0{?PN{ zz8`E}oPhGO*&s6X_)7+(aNmry|O^Iw=JFR_67L(fb2 z%V6^oTOG_g_DG(*m;v>No|ouX!R93sP+t13pnc!eu3zl+><XW&27t+;s7 z9nFJQkNIsddHH4~<;CHSfQn@Nn2gpHl5^*X+i#-E*qlN1EMHu#2TtyS-jg z>#c^J^@?g2)9g;*dS6xT&e!Z3VVBhGl*S4$8+L91yOUrSdQGiY_jek{SHSKp*qH_F zbpQGA_XWNjj{og-$Dhx?uGg#6`Sr#hbxwTiUH=9uXy<8H_~UiA*UZDSKRe@J{#MTS zXT+b5cBP=-^8{KC;@_xz6#A8rNu#${lFPf$TQhKF-CN}y=)?8IoBdQi3cUkqgj7BS zePkw;w>3TneW5hzpnm5M^!uo|ztcsw_tSsSdYajv z;@83}89sT$?p~SH0cw7nM#@VehfCg~cwz^t@hpX&%fAMH&cS4tIh5pkn%&!6ZOL~=ZNj19#B)7ILC^T#T<-|g?gGv3OV~w5sdi6jc4Ob6dQI4+A!E>Uy@ol& z`TH^%nUdHCe5Xi>34Ena?NLU-(G!8;6`# zLE|e8%;P*`h)dUtuiJBfM;p}s zNEO*19FNnq?s#r!8sb$QZYDbiQjdrGH`b#1OTDkI7kasTnBz;HqWaVAKf03g8ab8h zl90L`=QVuVy8Y?p@?rKDS+s6{FMNXaqJZ74=;v4gyL|C%F#VP5Zr=v0cLO_i^0X^@ z)w77x{Ie=k=AFc?ZrN=H2Rfgn2$6#Fvs^z5G#F-!pmm-N0M7 z_?PGPBz2oV&+B(4>CZom_E~rN^SpkC?)K-OKzSz*&-+7UnSc2-%BS-1Z-9^8<6oZp zIiHN)PxCnW0Oe^EA6;b^3LRujGPVyg>8~ z|8_Zf^o5$k{xW&W^L&r|lj`O5B7=GGRt@c&?z<`|yE}0Hihr)!9dVuNZ}&f7o@;jd zK+kcMDzoRPFVy-=^~3gj?s3@Pm;Qdae_~(x^X#uw$@@k6Yx2wED~9~?ev$rG~8O`n_&5p|#l1Jit zKiaL2XUhGTDYAW>*HAAi??4u+Hy_XAGv1s0xsdtn3+Z`$+I`4h2C`7Od_4D8Xmho{ zVzX(yo8!1=msGgVp{`7TLdCJ|0!eyG54I7gu=vy8Aa@Io6vfADW>0;q!O~dY*r=IqLco zK3Z)L`{nayG7rz^_2@DF<&Ak%PdXpZ6%{E{RciYN!@rqO>yJQRq}!}nbf)T`*G;z( zdXwZYcVEBx^gZH=HLLN4PNaU?^CXg)*(x7}ei@`YPvz6B|2>tDpQ`f4$)sNmY0lU9 zQ;5%YM4k_O;r>Ut0V*}`Uc6Ppi&sLP@LmGCGmE@nzuH9Q5FK@uk*6g0s z?B0c)quG6{+3~*SJge5r^_MD(y$)K-?R{CT_q~18_7+=z=nTq3>OCz#MLO68<>T+K zUwN)S(_>SwM$Uhc)f-&D-FyAY^YwGIfy$S9{p?mz|E1@t^+(VzPeF!GQu#FW7vuaF zJw@fMGs*AsSJ3i`ZzSEWJ`kn&ca}JH~_*PWDI|6ggrSeP8BWdif`cJZ+89%1?Q;L}9KSJ=c=lOnH?0J8lpU=2?c)s6erv1zF^{(-PKhO8` z>^wX_-wD6yU!MCRpXBi!Sw-dZ*%hn5_5zxB;n)4+xdD9YEq{Keqi8>je(KK`iesyV zG%wxV{Qo{Im){w7$)B#L7 z{;qSWbp(b^es%eG~0S19N;C=(!#F^~9(4SL=^K{{-`gsC*jwd~va!$6G}E zJAPl`@3yjCv67#^#79#+`O5M8_{jox(Hp4!(J|}xHyMAQFkQgzSpGYPhppRRzPNQg zpD$eeeLKz{+wu9tJX*~U^Mxe$r+M7E;~j~9N)@o<`{$wK*X@t<6E9#lf%8+fZhw;@x>E)03dOJUbJ0)cr4*+=|IP$&T;|VT06u=XKYtbY zOdej3N5|RzzGmJ1cmwJQ75IBZ+>fyWc6uE7a1!+nU;znMHd zj|aQTzkI%aVEtb(9^3?#XT2^{xbGgTTesb9@MjgUD@#+q#!pzczdo=tXRO=q3Dlda zU$@=Uu*(#%+Y0rDXRh1dOWc12?DYQ3hdJNoEUGu(dFnsC@c~2Q{lzC)0X)eXjuhUc0oO>bD$C=d&rDd4wwJTi1_D}nUTuj z)@I_H(DXLLR?moekhNRy_)g`MhMPksl}{GsC;%_kh%ox`{}0~>Q88wJ{I^l|K>YLW zLwqMR=`Ct7fW-9dbN!rBE?T)o8zMWV}>+R9YQMc0eC z?GtN$2w7^avX^rIx3#1#T7Q0GIsI$1lVvN$io{nAtr#Zq_}6YDgleu>rbo+VJ6$Hw zATu;cYyh3}&kRo#)&3%eOXxD!zs|MC>$YxV zxn!HhKgXnHYnCV_4n=ZV6oXJMqM?eBR`;+O6XP!DU&XMtf2@}!WLa?PL#9 zH{291c%D@J&e}veruv3fj1=|BsWM2M$O@E{qg#yW@t76E+h#x|>AY#f(;{mrD)$ay zG@HU=O7aAevr9}?Z{6ZzbK88$<&@RB>)gt7-N_Y)CvK!-v^Nj46&H!3!b4buHb_W0 zj}hiXxv7jsr(Cr*$z%hZkyynueXy&3dx9AB8PanMoUGn9F*<8T)(pO zqhgeho#{n`6v>bEs;BZJfpV0SKFG~?n0lfZqyy3QxS06 z=8@BdIYG^>h|EP7=1eCpTsV>(SL^vv?r$PT81fR0;^8m0Mw+J_4}~Zum^M1p?&sOe zQV*js-l*+6r+`$nnn2ZPTd%@>-c)z;aF{>*AFW9NgOV{BcpPP_H?{MkFAI-Zae+zhdMJ> zFXzz#+S{7c#+%op;(Muw6s*wEz3)yn9 z^?4RvyG1tfPx@w`QYWKfJSN-E6b6>6&x! zu#v3|H7#|G4Y|6;rp6pNypHSfypEV#Q&ry5(o{F4wIwm4sj0Ea$m(Taf=`&zV~)CY z>eNJ&W;;A#HYIARrRC)6riQwPX>A33e2hvoB$}#Q8kAITVq2r})WK-jbiH6EVu1@rK&KMY5*PKg^ZETp9!;VX|OmD0mo@lOVYNyRnV90uz ztg9K7XpzInaO}5xIIDVlfxx0NQ9o67l3^|7=h0;zpAFGkb=kK>fvB&mN#s;Dv8lQy zF|@j7My{?^WHuLz?#+Rd8tQ5qYZFz&6H=5{4jVIuhM7u?6`5<;+mD$vapXa&-kY3W z;FFuGO*7G?cTv>@F;iyNotPL|U+oQlIk~g5HZjv|nOD^;rW(x+L861kC+4=OV^vlx z_^OfB_05UOmZsJkZ<0vW_{Ij-IBB=chULVjR#sciwg2PZ>@WOO&74wI)7n%uvwE&! zCBH)sWKUO#aAdF6OrKFTt+l$T)~i=2YZFaPW){>nw0&-DZSla=rbNPU)_l@OxS3g9 zU*A|`$WWRR_#CaBS6?eUwN9y;QYR*`$o|?3zv8g?sne{?%I@A8Sl%?PYEpwKf|)4u zYS_lCwKMcuE6^Z(UShpmzfEkg4=Bn~$ z(UGF>%NuGdMUJF_tr&=TGjn8P)68no*T$^H?KmnfIVTU7<9aDfhS6-Q8lRXWOD0)e zZ8o=x@hhgyutu>u&GqVB-kyy$)(KiH%Eqje#*S(BxOj%-;bqY;zqTBgSunc!$SQ~Gvt^`hrlztl(H zmh7YtD;tIt*qF6E*8n}AQgxKx6}d_;7gMWS>%GjUHHt%9<)d#4%~eNUB_Nv51O zUT&ZkVOM4Qb#0C^yGxI&ZmF4`pt48CP20B8WOA9Nx|YPG zhNkMe=ISX}2brE-T`yKQuPqa&i-p!}zm-OYBwJZMJJCihV2Y1$Rieqms#|GswEb9b zqn#pFVcEdNu%PordGqkbR)pjzjO@iWZYgt3Vj;;cds!tPO)yWEgBUetY-NiWsZ}Ey znp=@8oyu9+SAe7QVpVx8z%0q3{u;~fvYwHC{;~2Tvi<7kYPW>U5so^q$1#BTG(XF(i*id>0x^i(Q zY48TcVnyAY-BO)gJu%Fzy)h)z8m({IN^_wtZ*1)#Wl5B5EM<|gKJ_#+?Y~)#Ia-w=hMPjPA_oh@n=Nz*_W9q!dr&Vp+{<2CV zF#KIrJ!+V^R`S}prX4Y+nH%a2oJ>0dxo>!T#@cq&q=qbMy-ca2fho?u;vPWTm7~m` zZa~+Rg0X#>wmo-oJDx5FymYqX_;j_@kMTM)sWPLho4u=B5y|p)j5m~0s(MWGmuRPCYn*9T({`H4%MKOy znPmC3s)bndxy{xqO5(t@_5Ol(Jom7zF*u_x*3{z0fe64+wMU3MVvSAW)<`|CeoONi z(BzUnk}#=E%xX=D?r+;-m$zebt|L-9Dc0QVj<-T5<#sD8OY1~-r(Kn0T6np2b5z*_ z_?ld0PCS_$JvQpKIV!$@wO*yfc%h1lPH1gz5x2|4PVEh?1quy6wxXnqZrPZ{tQY67 zx*GM0&jCk=%k0TS7F+61amtbkT~%E(tF^8vQ6=sdcl*-Gy4q0x6 z$~Do2f0j-&oxOQ9Izdxm6*Jx~BzeNZ1){4H*&(n-VbGHqMH%BIkhSuLl-w8Sm?-ZJ zmw_*qpZmrO7aeXyj?{3$|c4$%l%83$Rc(wBOKOEt)uIo#kqtyuhi1@6tsM^ ztGwB}1J*WUm$ow)(I9V>)wY==+nLB)Vr~5L_B`EllkuiBQrzSad6mqH_DuF_KCN+d z^G=pp<;&o%jVojTC5p)R|pNVo;F90X7f97(@0joRMTVJCN;IVsKmOwTw^ND zdOEG9K#1!zas5$|5EtOh;v%1qMk|;WqxlGN1>xNd7waRd$0o#;X}eOZJQ_Dio2xi# zm(_9&k@G`N&#J>38s{{Kec0P=*C-~US+ORs4RkzF$XKJeUnw>>MJaZ$_D-$WWiCZH z-rWxG#)j=pBZEbm=w%j+Sr*3#kDW{xhFX$+Ou&Xy;D+R5^w-rM08vzfdfUx}+V zhn4b3T$QirQnteBNp^kqX^-3wCdy_eH7jwdL(-S)jB@&tH?(-kPPMc2EA(I-!ou--@iCU3m8NQn}l1t;*#>N?~UiQ|o z9`lTPR2*2dmu(q;3vq6(qam}_-+(X2tT&MS>`bn17Pn^2dBdhx%WDWRsAYf)bhGS| zYcoS94o;1jTe!uyIBSp-wv$zk6e|in1d>H%T}!i^NTIkY5W5%7lgT%_dpGuT?(A22 z>&Ac0ox;MGhQ^+%;d>|p_1K|Y4w_Kp|sv3E?sG62&kq`Q$QHqS} z8|lu+3W5|&+5@#T3|D!8d(j1=5$b`d!Z$g|5YG1STEbK7TWL62f0s=b4hbe|$ zKdi_ic?!ebG}6tHOPH*0mB8h)^TgwqCNVvv&h>0O-E4{15S}Ufx5~?R#?yFS+mvge z^zIZ{Z_56tZ=AMwM7mf&usM7^%` z#T4>##&TfG3RA3Trj=+#4z4n*Tf_p;u=fGdGEK~kTDt6B<-vx;oVGr5NmXA}U)?aR zu3@S-6W4f3PHDzNW~28|Xf1&j@1|~5O=ClCRjqtX)9CfC^$*!IW-cohHE+3G(4LaF z(dg`9wg^0RE9OpZnn`=rVo!y2-64Pld8voQgNn8}mGo4zr?z$1a?<49xJ!Ae zIW;1TnNwt3D@cLGOFVTht1{lateauVR_eADj6b1_2;n`(93}=-Ld+}!(DVBfhiPYDgNPp#t z1rizE_N$VswOGApR#h4AlpRo2CDJZFiZ*qRREp!5c;4M4UKkQ~a@!QlD`k75V%a9~ zl7O6#LjU)&{p20-TKPCP*3>w6UX}4_nTUa^Tl%F}t1E@keeVd@^I|tqrC3xE!Ry@) z$_>cvCi-HnS8|OMBBrV;?}>Ori>wEMR2i$g?R})YZ41Q+aRhIb?+;Z8?}+>2?)!Kq z+^KCg`($md?B3I`|KruS>KjiuBvz1eEp?&&xyP*_VSVAD4(e21F4K?+x;@Nv!yRPj~mF=oqg$qlL2&4Dj#=>s<7}s^* zclgBNReQIkcz%!lj;tIzv1;$O-aEI){$B6p41|k%94MZ-RnHwiDyNqh_c+LSyywA3 zjvGsZCx==sViPAA`Cda^vv@0{uA!=a?o2VH5-swc^K)f$=)IXKa#-!>QTuyTmGN=U z9)`WD+~{E>UoIEjBhI?h#7(wD(|B=HLOeE+&&iLFuS6MF^=cF!>T2s+#L-+_JBT+r z6#lwi!qaQgQ|8t3GH)n}XJzjRZQAqf-E$UKc;ejkVXqdv(bcTD_*S}j)pH?=h|e#0 z-(Qj6lj6Hr3!3qg+C9DN8WQv~Xa>7sb7J+yTwH`jajGA;t zAK{mD53+8t(4}Qv-Kxa{xu!7Ea{V|trqXB)xn~7b>G$jo4E}vW=TJ zKhanwEu~47@!;m&4eRHEuTM1XyN~ho=8dL!2SFYetLu3sGG5tSbj~$pa{OFXRyMH_ zbJh5dD4aP}{OT9ylrnj*VSKaqIB!v3qkFCK#^&Aw-C@(~>T3j*uFw( z;h9^UXk5BQPa`FqiWXkIg&6Sl^|Yh$)?}>g+gM*O?$FC;1m4S|&BhCTn`%TC+_pt` zBiUC3Wq;{Klf3k78Di>w4_Qx<@-9MB7~S@%G8Xi!GA`^VVoMYMEQ#kN-(6gS&uliX z>nHA*$hYT~_G^+Sg*%0L>S@o;eL#dN2bYnSJ|6Ovsg<&DH+f$}q_j?esf+^-L&Z~#-fl|JuZ`Zw=OQr&G2U-qkF=X zhH`7((v$ z!eFGhRUzN0d8NOc42<6BFUHCIRvE^nv&HxHce8~p&nDyh{zr-{oaw__=C)*YXATex z!-sQ(nT+PV0rMKN)NO)F|EX2<*_!1yd71sx+aEK8{w;;r2&XJ4A>TSg66^|mDnv55> zk@rYG6y@_ZkjBbrnjoSu#`}Q)yS+9GXb-O1r zPfC;S-p!R21H}eeYdk-Y?peGljqmiV>^rs;Ggm6oLP+v z1~nRw4l<3828oAa=MEMbxqEO+LJY^J2e&o|gBs)2!LxC-_2yve%XbHha9WJ(w>i=H zaIhG4Vr3HxQ>*xvY!Q*lyMQllD_>1|ZCf6VE4Q6!{C!(7ls}y$#$M|CiNa3o7-HNv z&Jji~gExxT zRG-{_lK24+fM>TC!@|37vTQrqnwNyL%wK*d@@ap&rmW1{TQ1o_#O+KL6}UG{7B$l& z0X{?RfDd;FD{G0g$~cqD$i{e1DsP-zhUj13LF7SPhKO}#c&oU$zh?(eyd%_fD6K3% zav$ZrM?+C+B&caHjm85#Nn& zxMTm~j^5G%v}#9rc&g1F8;zAaiu2<<;~zqgHSf+H8;t))+j|FAaa{Z3v-hgdY|Bpa z@{;$>vywcg*iNk2N$k87i)v$2t`Uw)>{v1qSh0+5Bw>T=QU#KLMfAS(W?<7xHWpR;?fgzew=_t(!PqkCu0oH=vm%$YN1&dj|5jQTd>((lS>2^rqK=ESZu z-|5EQI_=R?xOPl#veoRz8NrZ~)tZV+EqpwqML}+~(k@ZU9#T0J>#l(Jo^ za*z~^*$I@{7*kXzdn439ol6Z0I{Xx!6pqUPb^WQ&YBl($7^zRx*Qwy28UY})k8o%Y zxt)cBxR%QPX`9v`s#Pd+7qHAa_qrxd1+^b}mcScGs|+h;h`*{?gwha zPdNpS{ItHh^Ha__RsJ&&b(=Vi4jd8hF0p{&KV#`alp38R|2{FWvh}kDYT3^)e>VLr zCV|9phB)jCtWBt^KNIw~P~=P?Gu&}Zt+8)}a0;z@{yY%ozCTBQJAjknfa^;CInEKK z^5CB%t}~}7OOCB+6IuY)pZ(pZsi(UAcYW3KaYp@B8MoG% z!RW{Rnb7p3TzGr4X`-u%k2h2|9&e&%{8Xx{0Y7WTwPo+mGSuv!Lt_OJe&FXkYHptY zauIhKKgY-vsxv?4m(xGTIynpLE;RhCuKZ(I_yaEH6hbS&y!;so8*tXxmbfY;QSL} zCtn4$K3<^hsq)8~*j3ZyzJcoV3o805t#|)LJB-ZWU!XTL1laH!a!!cx?CmMmYSztH zU7w)IJn#wdR!w5b>)9rtNFha*PJ@=OANp;f0+C~-YFes3nBvZ)rem~f62MdpkIk? zGX7T>&B+37Pm@0;Duv%ubAHvLC{L~VRUYGmHUr8lfkg%T{7oeGI~0Fk?FRclaqOvU zzv8B^5SL$a9>~ANc5w`Yb2M)4i`0c*$$98~$pIEt-75(=Z3NKaFN4G$o>IuOjs-UezRq>^=&N(sX1w zCF!6qzlHc#+Ntv26lDh=2mA&U4Pa$^Dj`jskXTT8mnkn7s%GJDWIbK>o0y76+)SRD z{hQWm4eELurS6udT1HmO9CaC4##vHfuV@NHU`IwSdIlBR`M3Fu{mO4G^-F&Xs;&@F z-z%hFeO0OI@u3o!n@{_Dj?TZSr-uEezB=?91gls4ritqHTkJbNjv=1%TP`*;e;fNg zAE;gHHK2AUzXlroMKFli?DmYWrmMKTRL^HH5?5uZnEVc^S(pBS)SM!p$1&BrF*W+V zqdlv31KOR)@6Okfj(+dGHOXn^7mi75ibE*> z^RzSxpi>Ld$OtNjeLC{?Hrz3Q2U@R>nfL0wY4Yja>K)^_X{8sAt(-1@Zw{jfS_Ziqm83?x(-PgRWf9nj?&VL4m6pjG0)X3j~ zYm;7N=e}lY9B9PPH^_BotmI6QbR|=J@rxQ4B(>~WfUaehzJ{r}f~hlK(uFT#bQxQ8 z@>z*0RObQh&U;bIP*9Fv#&O5zI3)$ga^k>Bj|2Wm&(VmT zhBC`$nYVS4ssD6LMwMmdu2xBr^jr=1y(u`==Gc`9g zgCA2Z`y+AR)M@0q`$?O7hHBM4nxQpf zsDUbe9*Fl!-pLHqqdAY5i^0#sKbwOqCL>U89%Z>LHN8{(YdS|eWwG`{hI zukpvf0NR8q`!uWSNL7e_7mU+^7Yfx;)Of#5E4Awd|GwYp7obNWLfxOQtHwPqW4z#b zN{`QrrO(rR+VgzFLSM2fo|k;ckt{HsUcid7>IKaEdO4~BsKus{D-G?R_h)|BKcT~? zxLD}hm>QDmi}=|&g5gb+Ew=<3o&HmU+=9ZQMl`}M{;6SW$-MR_D3`hjCeHsTwmrQ4 zCunIYZ2!N&yF=#RP3mwRDby;4Yvcd6=AUhFN79fl{@;eJn;`@uN3H%}%pSD|70uD2 z_NO;QMSD&iR^L;bp9fK@?9KS4k|Y}Vt$DVg|M}(S%_Fi9s4jnQ1XOLZTIFDgDf_b@ zMJ9})&4+AN{$~mJ9PsCuGjZ6TF`BorYQcZ3PW>;hT8;iQ#5`tQh`ROwWGo?Pyh%(g zhvYcNNv3&4h&{>FImpT89(V=EYK#esZ9D@06so>|iDk*Ej0o((e|ae{|9xEHRwMo* zov54TpX(N?aeoPB%;dlLi(h5YjKBEnpPKg7nP43w6V!FBo7~5c}JE)Ns=ZmkI z%Krug6C0sD?AZqVtteYfMULB_;KoSW4l!l5^l#0@E4Uiv-e_`BAJ20Cn8+_X{5R;4 z;Gu+*#52A&1}515Z~R+pb^dSS-0SutcwH<@PnSl%l{5xf`c!7wQ-Z9y$THTk4eW~- zzv!DSenU{@aZ^=IdNE&ZVf7xY?sW~S-*{0a#Vr}X+SR{uRF(d>dTQ3+uwi_$i8}Ej zj=7`iE)a?q%6O@9quAnl;jh@Ym86418`xia%1cO1>AO4NCD4WQF(2`P^mbK7=qRc< zV*}=>)9IB==~NqJqAJKd*Z`?s4#^EQtx|V%?bbyw>bVt|2w-b0f$akX_PoYO%|UFr zq1siuQI%>3YJaE1sx9f*j8}o%m0rKKDyhxcS0#5?Z5X+=eQB+dy}tH0Ty9%xzurt8 z1zGkM%9*~I;_8))(8_0X3q&^z%M@N<*tWg_t1i^x)yu1Of>RB3s}4lNAqLx$?rkCl z(_yL)l&&6tjtI5RhK&Q2wXiNcQfgUUjCtFbs+wIF=FFP9sD{}BbORco0t}yQ@gJVA z8yrPksf$(Ex0MRjsXFzPUbj*FV0U!gn8$8nUF=*x<|&9(H$&~M`*(E{sNJnXkxWO~ z_OGc2J%j_hY_+K#NWDQ)jbZG9?y~(@2hZa~SY4@CIeAXjtEX3=lqqwPgRYpj&i=8$Krb@gd8Iw-YQGv8T;eDTSwsNX>CtPh{e zk@|toIRz*~M#-Kk1m0^9s_c2!1{_({qXBr9tuEB3xb*c&nysj+Z{n==W;3;WFf}AK z2l2DhvoQ#6f#@|1ZQMmrsKzwphLdZ6n$%Dw*3Qn4is`7jH^j1za0Hh0Zb*Yd1b$#c zF6Ja)ctdn)T0@NGAv(&82~3?4&3!dn_6lg(FIhX8x_g`ZCjOXLuuZC`hQAV%;YoxX zLl}EIQ*TRvVlxVyiOe!gc=|=hs`6LrskyJnZWc;Wt$jrZ-0@1&fYc+e1g7;Vv?DPc z&`Z#Z*qwBFMXaL>M7va+jam-3~FaS@J6F0aycSt)gqirRbBGcb)a|m(*kl2GBplhN7Lqg zwYgE;a$VcVANd`Pz@Cc&>N2&CGWCv2wIfVTPf{s0{8Hp|uru2ua8>r%~Cq8dO z9N(~r-`MDgsyr7}i<;I~Wf45tx}-iac0FTxvQ*rHR|3RS1qghb(0MMSP=sgl=FeLU5f zCx)~>Ml95^u)q_0PT8W&6mm04Sn^SmgPBu zbf7t^lHIiR^-*+wS^WZE-6R&Q zWh+px)0$T`ITxcyFD0~556O*@m++Xk!szveehF(@0&8VskIab`Fxp#tgJp z`(Mv#>G$yntIwAie&a0?dSCQnx28Cfa6C_YsJ2?yEN0xEXjV`4%&4y}XEapZoBOE7 zG;gBLHIFCeBt8T;|*v4>|gr4fqu-RUbNTCjG4_D6O&Nu4RL1729VpGYh;z}m|FM- z{AUF@c(aJh;AX%WfB~ZJL_z0Tyzt5!G6&nKo5XaCa$z53Y6z^qH{nA?YWSOSWTwWv z3Bcn5xLZZ4oWj@J)!87J9RM{hx^x|}?r?$Qo-y`fsp@Z!&1aB7hT3A}? z;<@;iKM2=QYFOOe!RA%B-(nnNzUuxqTJJ=D>C;)p_z_6gsJFARCxAO^0?W@)!Rr?r z`3|S$$Z}Byr@!<|u6-O?;!S+VMs`)%z`Q`mKiQ;9HGU@3$ee zZ%35*4=jO&YR*6CA)wMzi_jJ>Ie(-Ey#cvfjwVds8*xt-#yir*E28rx)vwBDNu4XT zagH|ko#)izcTn$C?J$Dg@NN1#pc=tfE0C-Ar8V#MtK&226tb-2L}47Mt%=Qh`yFry zo5&0A_?ANZcTsqXAg%!Fw*>qqQ|}sEocQi@$^w+rfw=8EQhtXeD^RiyCHheDs0?E2 z4y=QcgD5%1I%QJQUDA>|+H>k2bBpVQo>LX?HfV#uk52yt(t+51;MA$L>hVvS9mA1l zTw_DFr0!KVH{+krsd@iY;>6nePF>aG-N5@6+-wsYY}mVEqKtkwFhgd)8#73pckyZm zOjw^=XWq?FWB%zsRORO^`KL-~fEbB31vzTaJAnwA?}%KDedhzU5k#3sWiPmx66I_b z9$ls`(RWePItyi!ao`nAbkK?SPywDBf{Jqwwj1U{;3vQTo{UH5Ol$|MXp%tN6sk?{ z#oRdC-wUjry?BPN(gowb=WmSFx%Xloo15?Xo_1B5iGjbzJrhm2Os+FIMKQB8rqHft z;#~DOY*~=G^F<9FWaz+wSs23-QC+)UM1%sg*_15K)*KOFGmBxgYGxMV#r>)t!2GJ) zk?HxF?(SITX;h}<@5_=vRv!3xfa`if+I9BC;Cj8EsLUX9Z!B}{``{f`PTVyJiuT2d zmcCEDv*7*gocvZWfj+<)*&0BbE;sSsN!BrMjAGx6{V!^aA5+&*ZXBZ8vX09#bQZd~ zW*xOOtFF42RWG*{cs1bt`p|gtvdiT68>*e}H^PNItT3nFZ=BoKKc3Hl&2;g7czdz4 zXV31u&ts-;E#7>&dE-W^XN!Eex_{R^U%Y~STd1(_OR0yg%$oOFt6Q1y(6s=M4^mc) zyHu2>e73*lB=|;Hw;GoHA(kk)_&qusl#k$4vPVfelc|BpN6QM7Y{s4pL73V8%-%=v zkpyuq!7O*9D(EV`ha!7Ci}VRBl9%*)quXj=4#1_vG6nl|rq)D3Be0AHR7-LYHw-?k zB={sjgj9v3aDPyEB!{X-t4w>;nLQ!=N6J zovXID%2#VX0F~!n!whj@g7GLs^~wdgXPC7cS)tnSz+*!7&t=(YlUHvPFRxJ9x%F}jqj_2F)P`Kq{(Ew1)*Z?X z9Llva8W8w%*CR; z>tABtPWTYixjYKCTuxtm1IOKlv54O9p+WRbO?#BjE2-0{r=MXt=IX@{X%=n^AO`pjWYz0-eESUmr#Wn+xgjbBwdfaPF9har@M)94Rf94 zY^tHUkVjkUD(i0*=!24OgE^0#TG>bF#^6^amYD6V%8PBnf|J4xc@1#RAvA2wll9YnJtieCzU&ghG@9kBiL+rhE%E>?0VKCy--`m*1Q$t7B<80u3v-Joi3|?UG{K zu8NQRiO{Jva7|<#Z=`6+nKVqZ)!f$fHZDe~K9>3V5T=$XkT}~>i4D|>*6=wxt%DVF zXY0Vpv%mH01$pWSs@i>sjMpC2DVB2gRhkc%cU9V^w-yg*uY9m^7pI@KnM^a+RWf=h z$}F%L>|S1(y9>E`*H<;M8V&$07tqzX)P0@>>j>^`5PBDZSK)@LOH?k1!F%B`4v?kz2T zVLz1IJf}aoz3n#C&WHJjpG#1w+i{ze&%=;5-o7;4b4B*QU_6_Z(@AU*frcl zf#)*I+#ogKJA*G1J1G#@CP!T=&;HY^(As8rD zl7n#sW%Jk-Oo>3;_6D#1Di?sxHa6uRW2ztZiSJkh1j~hxtwIQ>Rf*F0-NIPo-HN2~ zzW&S}T=XCfBZ?3PlARp`-hlA?mQZ`K@4E{`Umh!AM-fS zVSUZBaRZ(m6~aDRc3eZ-ynz_;{7(wy+b9HEH-Q29z{5xV6yY1WF>LT6#T21T1X*afgO%h;$yiYtARN-eBgKQ(p?PztZ)a?R;PR{dOwcyq-GW&JR(PvMcSdhIIZEI8G9W zd#xS_q#FUp7{O=?+?-GSp_MO-0B=-Cn}?Vh=j(^-s^ysVzaft{tK zIVW4j_v~kg0+LUcK2r%T)Tqz0dGBEEXJYm_pZT6cbpy4XNuQUo{#2i#Xa#RC61bm-pCfoLR8rCdYO`y|qv zz|SVmH(}-!z><_M8DIDhB=`Fw7e1&VU!cxO_F2Cs7WPoqbQiIE0>17G>6!Bda3FYM z@fRNz<$nnEyyAq@fW!zboC}7L}Ps=F^eOa-HlcC4xBrTeNgkhf^%IhL6M2GN~E&$ z*C&cEV27+y+Z)2p;R{VC0HwR4iKP3kuaz9;n7DHr(|u*ShMp}ClwY~7QTuh#HLAZ+ zEOR%rio0IY4Nz(c4b(JUqYl)0=|Zj4HAdpWW?xTubDK0OO4i#!c>nsY+M;u>{)xJ% zYjWIr4ZjY=aJt)4>m*lY=P*;_44elEfhKF;-mf*Ly$?O9y%xlVAr2XIm9X<}H}Upy zMnd2YvCdClkP)LIB?lc(1({+<{h|I%wDy+<`j)!{XntRwW&@;I!QAfco|% z)TG-d=KfH7zw+_8Um*&yGW*d=ujKJq0<))Ta!Fd zFL zy>&q3b_2cWMt7QSW;dqJU2)x)z6Hkdjr8rEw6^#?6u3+Gb|TZtSdaxLsg~Q?&%eTa z0~LT2l~qy@^eNK5tdrand!oG&S!Pha1X6BTGqwQVM>gXx3-Q}8GKaQh5bap6DJu%4|;V8 zOu0&!(S&iuU*b*T9DX3`J7zbo>4M%Ckq}(r#hhPxr+vi8yAiX z6fWY%!yPJE*O7A#cVB#4II~y__J#BL7KSH52JT$yP-})C53#pcCaueIV*yKy*`jYx zyp7NHX{O$(w?RE7cvpcf>&mvyO36u-Nc4o>RcoNiyK;t`U1Qg(#uC!JKuG;EQ^WHy zIXYD6hu z+$boJRroqZmjR@5GQtiQ@=`E$cs!_WH*@ASEeN2@3iMY&HN_u(O zw}gK{8L2qRD!POgc$h^v=>E3jSe2E<*Og&qzOJi8y|PSPy0L|4{kL1%Qikh7(v8a) z44#;cE2T8VS+k^6BVP`bYS>6%D0Y!F`@*{*%^L_8x-$Fy6LIgTc%nN>^eZee7O}+G z`L1uT6bO@Vb>~YIL41_n1Hfiqg6S*%i3#F6w?)k(aIuFE8ho8hU~9j$c}Y@_sdbU5 zz5vxZi+l{y)ZQKxs45w553S0#69IaGJtxR-2r8k+MH(zzILzruC5T_f!Jfl^qoPnP z>dB7wBklHbrY_QWjnm&z<6=*Us84`e-V?I+KtR#ao_=rDH6pgb4)pD7rrt#PJb|e* zh>g@0i+__5JM3254a}JQXt1AFYYeJ2wHLWE#TFVGk6mKL%M2}|JQP;1>jyv6m@C8q zrzX3{bKQLz=+c7=f|}6-0)?AU0<*n`Xo<@KC)G6c6EJ-ZpLlM_y7U75-oAH1XOoxI zs9pij0mjE?)g398BTY-rn2p2mGK68`4legSC?m?jo0e)^Imf#L6^A{XwuCvX(w10P zF78O*AaKexm7JySbi?AL7MEzP)YR@89+OcW$~Cz#Gr3%Yr?7<|uC$_DD+I|qQ7Le< z*wAI_0-JL@8guS^I%1ru;*Obzr9gIAzN2nlZw>P=c%^YkZ!PfDt?bP~yUN=7ZKlR# z*0y`SqCtV(?Txk|oc`|TM(_Bzb?k#``Z?dz)w7RAdtVHJ@hmWopdc{oCHGQqt+krc zM+>&(!H!@lVckPQ`Dv!M|M{%YfzR9lK5*1|zmFz2j2dRE&V5nYBoHEK^4fgV+J9k2 zo$rI#9NB(c>4PwV*vm8OW*^8-XGz@Kkj6CTx1W4zpuZ+j@c~1FSf%h(*1~ z(9h_t)m7^uIcjwunU+>x4Nugn8(^>M*dH3+j}j5!sr@;V@G>`Eglomu;izhgfQklhW~3@^9eAUqU^#eqc-YyMrSI$BJFNo-OK?CFDI<#PwI z;RO(s#Anr#0kP#SWaw(NsFtrf z3`5)QTDGk(Woqq^&&!y)JDHmE^U%A%vN%0VgJ<`m)LJ5tE3*=P^t*;twE_+d)5Mi= zbQo7Kb!`}=IWM~fUOrL9!_lU-h<$XX5;wi58M*wKM^%HckW^@Mltk{i~+ z7&opl(_H7Xs0E2#NL1qg4-6Z_#BgN$F)D(vpUFBMS`&I)3qVWmVpGeF86PW57;Job z1-JIpmf_e{;bxA{%-h2?>8E_FUHCd*dR8SSwDu5Fy10(m^)hzLoXpggHUeyH%IDL_ z5rIK6egsrj+yWRf0wY9tJM;M2n}O0(XxJnC<}#hdgS)C=F9$@MyGaU{0Z}<#|x{ zO`unl!(2xwaL|?#+=GH*Q%pL0k3o@lnssIB4ICre)A7u9wi4Ef z!!BW_3rWWM#ruZ7l&nOJs^Rv=szu{CAo5I_>N8H3;N{2) z`^|IO$jRu=(U_%5A4m1GO%N?+i~0=CW@A57dvfclJEF4jRYbOYJlmd4Fi)zumVDmR z`dizZdE>t^ZJzQ5k}J{x$2WDZ~7Y!Y*y1=WuOML zSg#;9bG?{WlIoH=^WQ{(3h>%f{P?t0Q_y?3@#asFt?mX^HsLOXdRIrLz{iE3Y^~i| zp1f0+roh`gwPdsg)b3>l$OXH$9#gd{*JS!mC4Kwkzyn9Sm@2#F{9-PTd0|(>(JO<@ zn@y7;K{aR^rg43FntRh^$Rq_pZJ(^Y=I8C5to=R6bSG;q)QBm9 zZ{`$@VH7u}!1>xr6V~w zdd3<+U!gul+^}@1GIo+S^Q9SD)vN6{W^i0?(;2T%72tkMtr3!b&D0zDwkA7OR<~vn zeu9+Fm2@UkV-8bu8yk{nfxR>18y$D1rm8Wffc1yX9sP*di`T^aM{ zVH1xFJe7q@=83ITnX!`Syou7{&?<7>bw2aY%AwSux3t(nj>&9mAi=G9BoObF!mv_v z=4*baiGK-d(R|1p&z*bBhg-Txjh&Aj;&Rq?$FZ)ySTG!X3*+kFXt{|RAxim``EdR* znCCvh9T_;I55e8Tq{cWQ=^>`}DLG7o={SD@SI1V$UO@V+w<`JJPn7Wspro+ebGA%b zKu5m+dFBGlDYc5VT$w+{CZW%H+Yc7+y%1!<*z%vZR^1oItt&N_aD#|nK2Hy1}I1$=v*$JcZ)Re?R9m3Axx{P23>4AbY4W<`v95r zz%)6zQ2W;H*z*g6*&=q;m4)Bj!Q5O31Fv)u3>Gz&Xe~-`ShTX(lr+WnEaawU)*|@4 zVlKEYivjCkolHrB6WS~|La2R zQ!#285uklO1Acu8B_Iedc#uKU?7)}C^Qji6IDg0U;EQbRF*n=6Vch?XMV*$y zr_ygJ1mRoH$E$4EP5edB>JvT2p7OUjoY)+*W7U4Rb~lu+f-M>$6e}z?GUx$WymgX!58^*Ogin(W^aH(u_lqK%QCw<)Nmo1VZlu!_qlxOs_e0qn-&+Zy;(_d)RsqZ?rWIv&=0&aG`puYC z{z^V+6`IadC9CATW*WYQu?DVKq2XecIynml=RpbKWK29R?xf@1w{1WO)&^kxGy zy{pW$F2ynruLi-fQ=YA>HN-=oB%IlmaL(BXw*h&o+ZyD#$G9uA?laZTux;loQ?ufg zAS`gg8m-Wm^r@_1UJx=bl0ZLuQygGB0W8oCTqoi^xVVO{aCL1BHz8ss3|*r&ZZ3gZ zqt<9#KxeJdg2=7CYqVJC)@@?54bp6^W@?VYa|TuKB(IIz0L#}_nlftbT2@@aD)us| zIFs)n_@|Y3Dl2tZM?S?*E3dDWEnL@isBN5+E$d0tt5#`7%v-Htim#5h+H(zbdD&Vz zg4E`D2t2AGez=hk!j@$g7Z-5`emF5CAG{Tn^7_Y|-nJ^0z__-~AEa9+4~ zV{H52Ze(W$5W?!qR4)ZwIOb=&vr!AGbl4QD(sdI%*hd;T6s<%$H4ZUr6GR4<5*G3v zn|MgGfK_y9#S=PW_>aWvoMIgeyV|k|uhhPuQ-Eq0Sk3gSp%W?;t2S_RjI80C0b&h_ zlKy@q$(#U^;`A?!11{Sfqkqk2=+6KX8aaV3s?M=eWQ^HNk~a~tdtMkgh3Ivg=)K;B zNbR15fQeBv&LIuR%C7%0w9RA(J;7TxBBglZ$q_HDO(0_`@#!;|n!U+8{1!?f9Y}KE zch;eW493;Pqjq=+omE`vzAb%o9vGVeTVhNYwk6iL(OaN*Vf@5V9(S-!zTq$=2af}7 z0YbfF0T_g(V6y|Iync&@w+Z9mrwQS8D#U(_H@z-y*6<5?Fs9=GeI0l#xIEyFoC8Dt z;uZ}d#kh-;1`#4}~$u{sf2k&-B48+c@S}xu)W7{J|EYHa-AIzaL`V?wB zT7{ExDKln?H7tmywZkK?BRogVY zS-)YM#xcJts9lkkb4)FdBzS`^L39PKe;hov>UE2(BYCG47FRRb;Ny zE=WMUPRTB`s-G13IT@FnDajVYGILfB- zvx6bMxJ!%uoDkekyWl0P@P9z7>m3g)#Sa^hKJT9Jr#%tqGQfeAXYGbhMPh4b?+>Jfc!rfRM^09;2x;ria2X}vC*w5{b&H2l_ zecMMOx+;j@_K4p-|8K1+-uA?=*=^WEdMbpkSS#p#*t$oHy9ti4*g3^Y-WjIW>3G%R zy(sf8F;~Cx@A&3_D6^ew%r?$RmCFKhwGzbM>+3-TzR%wad24|Qh9OzSnkE{<-57Oh zj}}~L+6!~5umvu-^1+ktd+}~PJpA|-gjQDaIjWyT`rx=WclOQJAf)scO$)N}EJ@ahyHtOYEyW>MNPlGM$r z(|(XnwNSDj5YB4BJX6x;lCEQFB1i7}s0;frg2sOICQpssFGnnB=(V+8abJ7szNF zz#IsPFF26uWd{&X7OXI94siZqCvhO~(NyY)g$J-wY$7`Ij&y0a3{ex+Z@1R4Ndq-> zH;qu=+1adN9OLNS@ZHYaho#``0j)65Bc%uZH9QX0>mZqTLpnK`?0x@Vd^?Gkp^qNa za6hR4Zc1AEh3fP{%sbtmaIVLW>G#yq_tt7BL4n?tLU(GlaKMGRw4nZ-kKNOozE}g=#KRTpiY%y|Xaqk~ zp@YFbg}@U=A}olN>u9XUPrj}`nzbg=P>%-{|NgoGtY)*!%+EZ zP++T5`hq`LjF`u zJoo$&-}Be`DB7?O6T7pT*zIMKE?^tATVe@|SJ`z`tCNGP=auu|z|qQvj~+!c!8F=( z6swRp7EcqscarSEam+F8qc}F}7=#0&x8Vrp(u|{88#VhFV9oO~j_2dEdp~pat;FC@ z<0Hqk)`k3ir#Pz1Qt==`vQ)(}oKVM$`W|O(vGCn~#BB8!v5%_R$7OT2@HohBke3_H zz>mJ-DFvv^Wvt^~C$w>0tb}6(;*H8J$H6tqO(3Xe2x@j9_jDNRK<}`ctz&2t>IQ3F z&l8mssPBpRGBx-_Y#NO?K`gL>)c0^LB;etujasnM#XGZoBlKmjt=KZo*oJtyvD*ZgLkPN9L=GT@j)btO6DJ?oO`W7z!CSfS1pOzM`8D`WMb&*H z+A6^%m><+uM}JpGjsIg^wfp6IYImf*n)+G;b?xsjsj_z%Z~da3D$j1D zu6@v0E%>mBy3nSn+WbjG-RlD4NA(8rr*TxK2EuiuwgsLK)qcJ9rW$w(`p^svkiDOA zJjOD0XMYN5el@3^8gm*<4}KL?4mc;B#?d4qpSWyIJB@L$OWUG`S=!e3WFKIj+wV)9 zf9gYZS~l*SukgjQ@dZC1v`hYY%XCe`^Hk{>@BzP_lEE9cSQ5`@?Vx2o$bqDnp8<8= zNK~kDf9mH2A7R!b>hU!9{&9-#kz6(6Oq@nMRDT8pd4_bZl&RiP(r%ymdu#qE zDjrVu_qkoq`u0xjd!MuHVcD19274cCCsy3yY`h^g@~q|`F)abYgzRiJ=d|2=0HY8Y zP>`jLp4Ot#ymo5&8CpkbB~d$v+JPzdF!m9@uYjj&b82V*3vVE+LFY95%0R$q^Bnu& zeGclKOZ>5c;HYlZIgOWHHk<>aj1$6H_b_o`>j4w*ocN~I@pBMYL|3SDgmb4zV|{26Q(1Hl|B}A`+WQ?ru_vVw59`LSe}pLa)Y-oxIMjgUN*MH7c`up za03@?l!jcuV99F)7tZ6Lb|f(v$4Iz->I;9jbMOLa&`X5|bI?~TI{#I;^6cx_1+8gI zb@>8%hW)Uwb#DqS z=7CP@OQxHI=F!5xX+VpeAvW*jm&CuO23)FKt%h9^i!zRK^d$^LKzk7CQhF))380BA zF9rgsTbqdil1x}>$=(*8G_ew|>;g%hWePU@?Lo&R?c}#MMg*_k%QDxg4K7F)_Ow3rXhBs@=b= z$&cTvp;t)!z0WZ`6@HJc`<&{gj^8PXnRwH#(v86J!Hfu2(>YgBQNJOu9RVATohvW8 z8Xvg;^uSd(m7X8qV2K1x1?Wd##o9UOn&yjpl|b`g-p)srgy358(90Tsp*|-TkUjUZxH)ED;ZAG& zHBjJ8rF6PiMJ{i!W3b@;D-+grkxf}G#N)|b-0bUdPOF92fxrauSlH%@5-`S#-dsRt z!g_?aN<23AuE%+#I^6&->hsqgZd}(emjXMnYPO#xi4R+E)meKAeWu-M2m&CQ1z*Rt71?EsXr(q zx`P0^tjf-9&H&wL2au%%u}=waMzEXuA-|iWM9RmJb=C}~Mh~cvIO_`4*@~D5fd;fD zi|maGih=*L-$Ll|3a-HFF7xpV3RpPs!`pnw?qCHB&k;A#x`RamX}_;Pd$2?WJaBGm zcvZ9zM(;;4AJWd7h$*VA*4}_EcH;&E=lqd0D`@4xwOsM7Nj9qjhjL)af(k!S1s4@6 zD);HN`TH|Lds{0s=;1=Oy8>3uyqmCc_LD$&3)$liDAy#A?}f zoUXSLT$ipRR$q<1A#SV=w_|4l%)?^euyXIS{F+q!Z&oqJa+$Zv^2Ubez@h~Ch}#NkXP4tHWZT*|zD z7tAqTUr-M)-`K}_{Xhqb?`hw#Cwkn|{Ksi4+v{@=sCAnHXbmDFb3i-oZMBQp23B8$ zp$4ai)*U!X)bcwNGj-z*MM`zM3-f;PU0B;I?)r-=?Wa3;Aw7fcRVtA!_q6z& zvEM!q);o3T9!3vQ9;*KxOoN+oOUvLG;FCN%ztKiBH$5?L(q4 zA`R87_Ms{T6WWI|il{Lb5QE;0CWSqlssTZnXWNH(|L_uvY)CbCy#9tTqHeSg@kR(w zj=OXK#;k0$y*ChW=Dy!nm9 z5piy-wsZ)=C(qZZk1=20Mecf6L?Mh}$LtE>I#en|@}LZxH&zO|KG5S49sM4waUDU< z1ClzZV@SF@xg+~UspCCsU$1rT_`rzu=!g{@oq}JiV<Kf7VGOBBLlLiYMngh2%U83Iiy;jTBg8Ef;>o!NIy2*Ev33YI0|Q7!|_DRU+BD zxF|WhWog>R)PoJ4lMPO&BLwy?Pz%XMO`YnDd)#?#;2yjoMSZ01cEK}|Y?t;1bO{9; zv0+_+z!9w0W)M{Q8(`c3uIn7)t!BC11u~t^5{bC8vwv4?e`maVcDHlz3ty_d3nGC> zcL~vGQ**mi_GmHNut#tP`DUJ)(b<<}e`#FNB@{C-Wif;Ect|msaEk(@j}`T1a&v2U zg&5)1Mew-%zG4JAJaFCaa4}@q;NBMjc|1@(Rg5UKJjj21w_Dsbgz*myJg5qsQ^O>S zS#+C;(hpONEv!3=F*$mm7I=qE+{j}50P@7HpwF5~wmPtbg``Sj^SaHInJY;ZS-S^<0wBf0^=+|Hum8b5g2 z@l-dAxvU2l2(Nb#J$LoRBx)@n6-X2E8b}^j-fva+ODVT>yTLNw>4ax1$%Aw`fAZ9G8?WK?e z+xLQ4doXWBEPItv#i+fdp*DDxE-n)T%Glmv3Yxc@slG$fUG4mrvevO0URWrTpM7-* z?d}zb4ejttuLsK@&f-=)M?iQjAagXB7ng;6!yJdvm&-^HtqeTM?He0dhww_w$nJc0 zAMfy}3EhF%Tu;O{Qm9SBfw}nd4b8ZvJJgJ9{M7F55NKcb1+fW*YGrq7mdalyUf&%$ z@nClzuSH=wvA|4M2;nz@tdm)<)(AlY1NS;@MB;lFTqIT?{Oz?ia zfY61{1SFMlVKVQ6nTQ0SpD^YvJJ~B#D98DgyQ1^Gplj3>sVyd_Hw=Rq&QUjeg>tco z6=lIX7*me9?QbfCt!h#^nE}%WotVe@ZM2tVNp`|IY+uMn6*Y6XJg&Ob$?{5NbG{q` ziXS$_Mbq+7lwujY3=W$x-m`sZJUEevD`j=191`mc@F&3{>J~V=;Z@q%8?MYM?(PG< zL8#q@46%zp4dQa{G1D0(R6jf)<>eMGrZLqoS-G^ffr_qBOtATyuOIZLo zyiW)oadqV^wp#Np!tGsp8E)@lY;@J|+wecB9rxkvc6Dwz;~lKfLygp|4xz^COovbt zb*U3pOSfxC!eZ6!uAydX9Mp&!(hK@;T6yU8)@n#^G^~#H4*BkjF=*AFtHJhU*lAeB z=b4Kr=t-xvLi|2)Uogz=g$H3UX*a;-3-41+P%F0y?Zun{Msp48$YQjS;)SQR2 z)#68U)Zibq~J zEohdNi*@z(lZCK2`aXr9%6O_xQOg%p+0$*+&Ay>e@l*66h5xLFbj?X}lOt)SW+u4R ztdObcHPao26I&OHI>5PC~@e@QPPvG!^wDnJLk9evg`o zsg4#&^E4-{hxuKLIzQFFuZQs=8qKt{XnNER0l5{@LlH-dx*;QkKcLJ@uoI$DU5JU}FwoK>c0y*vN=k8`^2R564?7QgVZD|)OEW?)TCgx|HO#Q}6Ft#+!qp;H z$gE*#na-0sdKO7dPet3|R6T4bq@+huFm@@a?2(S#6m%Z+rbW{sB;Ym%Lr;i$;Ycd} zrKP8)rKY5$J7G^Vk}y(EVwznunrbBJAv3{9a1-p`m_IfX9L=g`C759|!Ai2KISEce zs#VPkrAN|@WIdFck{0z6yvK}3Z1e`b(yT-$K~G9ev67smG&f{x>5#2RG>YD%FBbZp zmY#|6{4o%vdm#s1`R`2pP0$mqjA~|0HzD14+-qz^(xC_Rh@$4hAb`BOx7`kX_={jt?4CMkENwV zKxQpZ^TPOlid`M-(lXNx4WkjVYM9l{>UNTz98HNtQZmz0Q_UJ+wuyO>=4#H5j0C-g zkzm!ZF=6?igW*cIFexy4ZW0D9swZVeov@8@HYmD!*nw0)E*@qETSIf@A11%IS<qN#bwLY4`Q zVSPVnFd1qLMCq6j=ELbxFO&(r=!HmcG~G=067`x!HSZDW4wy#;@;w(lG69h8hAq@I zF)-GD+93lCc$mme$j0>WerN(1EC{8zi3UDGWtiZ-`6KuL@Nd+7%6izVZd5m_u?Joa zHw>mGJD3<=lAYpJGpkux59|;b>V+d16if$9Jt$RZ&Ll6{O>`s=lM_;;nU9)@b~4z8 zI_8hOYEF__!>eIE;?%Uj6TPMf-2=jH$dw5;>NTzEo(LwG@|ao8OEznZv{|4PBLJp2 zHN9$jbu-BZpq_~TC+o?XkxcZ;OE4jL{7BiM;JFZkOcR1*KtLTW#rlE$M+1u$CWR|J zO0gd^|0@Fah1_IQ7zhMW16;F`bh0)6FKSlf1j^(zdPx7d_mIs63B9tg>O^!cie}T( zQqt0>Thomn>LKUhG+V>M=hgr~rdKU9ni+L#+R0{3^b*~!9x)&CLYM(;qn26AO-M7| zh-PNGKSE9@YW)PgN%E3BEJ10Rb~51faHji^laT2>Za)fotVhihD{R*UqtV%99qSV$ zLQhFeV~;ckW1fy#@}I&9ENSSri+m7E7;q=#CPX4&3>Y=B=k1zNi zzM#;FrTBpVVO|j};Imy)A$yN>PkM@7KkGM6R z|6_%npXe!;=0Q%t2K)L8sxj7C#ZFikVTL@gyf9X*evrealL4|M!G^EaT;$B zWexHTYar36q0@lSbdYNY|MZZ@1k!85~Y zRwyOSs*#o|OH!H{#ym_jl2Q$H%)&%VNyC~AVMU+95V%yIM}`(9?>r4n08wUUYQzd# zn2wnDAR5vO34qZ-PFvtarkk8%B&O@3G_#iZkONb}Of>%60)u~)W;|hIVNcPMoCLdu zSKSJ`Sb^O{H^F_>Oz^@^*n@4M*UZ$h@R`+8jEAj|lWbuf2PYHUFjANVdNmhBnqec9 z?uMKs6ZRs+$*PgA!(_7_)k78(hnHyANKJLH>^q5>AS>+s7JP-AhINPu`e=Z)=o2W1 zdIu>XC(JQPi2w(N5}#5XbUPJ1f}ojHwQeXg6(gNuLm!a)8x>dPLhvdZL?T zHZZ~-N{mFO2AZ`gB57$BM8pCa$-0J*f=Um2DAP3?f{^Y&EPeu{afiOl_%SxExeS^)O59nr5OEcEzLx9}HM9n)gt; z5ze$>5xE>X3rJnfO%lS;8~~69wE&bH-83_l<|bR{b|R(=%FqJmKRL$XL#OOV>@a9c zHhyS_+=mSq8EI)Su1qZ*ONORvDd@njGSgFBN?+JYw!fF^C0nEzb-`k@nhqh>p)oBi z>(#u3bm(n2oa!Vx7(Js}G&KsgfMHrHtRu~<KR2ZUX>~v0tevUP4-W z#0y6-tl)^~j#Sk5(8ClPK$2SFl&F&gMpa8Ot9iA&MEy}4W(O*SjK|QUTKJ6J8JH1q zH9G-=0w!1?y?TlfwlQHWWEsgR{)(O&#jvENM$?TD*yDnUb{L`u)?#F$5l9AXXfu=! zGBBc<$iZ?3I#L{n5ju>HLHS0~Qu%+#9rlJLqe=fsvP)2@=2&N6XYX-@sA}^)WO6GV#rRC>>c^iWf3a-R2(F(lRa0 z29pfH7_}@|&!*;n9|IUho`yw_77ca?dMGo^h6nx^NJ!JcMM7HOm_t(`463XIa9#(8@tGu>*!bItc0#5FsY}J87)dcvq-*I; zTB-wcH9Z&H-bsd&g&W&WTRON_O1&~rda=BL9?e?$>}iXT}Xmk(|FXw zF4RPyV56pbAz-J6#(v1aTyr5dUV@$IVc|r-OwfcL8=5!)*y{wnrVZ)vYMM2iu<^ZDm5L8dN>mUpJ0Pg{0Ckqn91aN#DI9iNO2&; z?$ZvP9Mz04#tNPY*i(QvAwT+)dM!Qdgk4`i5^YGAk>G@EnuZ}a1i`7PW8ZIt&^Wva z?!#8t&^*ogn+eU1Pa!J=DW#O?VK)S+V1hjZM96|whE}lq@ct8Pm>G7kU&848xBIO1 zN2tflbVw;yu>XgvJAu1m{{R0!*HTxTI#Fb83Z-l#qO4=zGsu#pQ9_nXWG6$mBF4Vg zkkEoj_AF!jSUzZ!Euyhx3ysJc{_i=@$9%uv-~U`!*LB^`^YwnsdzLe2&bhi(Zk`rx z*Refw(Q97)B`szV<-wv0v;X~S9vJ3)YA!irbH-}rXDxPS+L>3XZ4$FS=9$I3ZxA(Q z4SC0+C2NY!Ow1G#a|vd)O4^c{M^4 zls5R8>22N)D9p|@58l{p;JiHjH<~NvxY1mDg^gMFu+feiy{KbkUJn*#TLeX;Uq7_X zW--s+<`^??;D`rk)t{a(` z-VNoIsW}~m=5*qjr#~~3)Vwoi9!b9W^z6u7Mm5>yX~NuNt`5xf&FW>1wwZ>)SR_RTcS33>mW00ReluZ%VV=)vWC1dF&)G5-bQnlZcZA_jyCVeMCLFzt70}) z*pfCxZ3=UEm|fizn$?b4#pXq$mo=F+H6@9;+BZL$rg39feV@V$^Xe}&v#yz)Xj-(+IwWoLE`_-kj@p^G6n(R#c7+*ec4pgJ+{HG{@+PzS z8Vj@g;-;*@NzJEuW4WO)Po#;t;BJX(<|U==hHXPrWX3jnzUP|5JKrESk8`sqr`*Ir zzJ}?Qc8S}jZOdaWF^6d0suq`fy*X}|wlB=ijLTDF*t!-Z<}_NE*M#P!xnrL13v-^X zUwNB@E^F=A%o~K}g;>~_w@T_;Ej1TG=1p&N;o!z?6H}BoCFYH*@}m~FWaZlc%~4Cf zUe?OX+b8DCVYX^&Cgqyl(GWJ7r#;WSNDmup<^dX;tA?n-F;|#*gMqo&Ebl3=VAF)o%9ZxQc12@uUS!72W;>X}B{d~x<;}B9)?l8*T_!PI z56zg+?Bcjx-0X(ts4p5)li1}7$L26d&56V8$f!$bo}TjZh^Rjh%oZ}=v4Zoc_ubosOIm;=h(la?rZ`V|ruTgnldAIq&G%zzTzoc+ovt%=s zusLcnYvspgS)ti3t`|4t@vy;t3C~fr8rm%~bo0p_! zj~3>%-?@BvHbmybGjHa^=4dF)uN2KXGzOvhVpd|kxG8Lin}V=I*g9%wmfy~tH(RA$ zb2C0|D!-b>`^M%FOH3y(YGvNwG4F$Wxw)(<%#mC)n%|gpF4|<~y-0IGVm{5UhwQ*? z*w~D&x#rzxvqjCUO?6Y!WVUB)W}7ybPjaSfRvs*kQDfFBF;8&~sky_PT0-;e zyO(KeHdfMN*3+!E**RwMxjCtr`8WD;Q`+QbX3A+Ba~5cqn0Lnub9zrZBxcRbdYQ|u zq+QZ3T`gJ7F^$Z?)I4LvO;M|`A$!4`=c4jS-h4F^Fnh}!d*%wkeD}@9F&7H4nU?ux z-aIb*CFV*jG?PrspYnTHzsyX`FO=f;j(O+H56vUiy!g#KL>=tB(cEE9FXlH6jvKY| z&6&JyXxfCUx#p!xd$SYFYL_2oJL$tz=Vlf~L-`T4w$n!2P0Z^gKkr!djhe!S!d#M= zuWie(`tFwJoFMDv59yiQEb^iy%qug`aq=eHd@Wz;nDdcs7v?gs z$t=FSqiijiAI&mdbBB2|%Y1Ph^Hn$3jH`c^ODumh-+kMZlru7ye^ni+o0|10XB#(G zP0S%_K2meGtUgSEsVLXdEU~Hl5*dREUJPao!TiXnFS z49?K9Z~2RVGiBdw9kT@Utkz(@dzwgDSKZ8tnR990s{B=kTizKqn7Nq^QNJ0xp*d(m zvl6CBy}%9|P5*Kkg*nxjAL=)AW_Nv4?U#$l8_IXk%@JF(ec!w*&}iP&%FOzht7!A& zZ-xf-N44qamP1T~a+FzunX>twm?@NHo=MY|dRi{MtkS1B?HZL6F;6vaeG6m_QG;(s z+& zo@;J?eVV3?Ir*8NtDBvf`IuwbY?#LC{-V*$r@W(R^d0kto*7hb2y>1y-%NFVPNsqB zRlm9Q>6xjUKITh#Uwwtj1G)U6?EXL1Ca$lYV@{yu&*emPS2?gg+-ykK_BGVh)*s3B zgRj0I*Z0gJVtzE=%-8Z}o(auY*SvLWmgAdy^$|9eA7zpGH2br>xBS_BxNcBBg3M>y z;F_lkv*F4w<@WQ<(|q~Il>VQa`#(Rs^&iv5`W@zZz|7N3v6NX~*PL-0OPS*{HRmM# zDVJv!rN^t8u49%|-MrKk>CmX|vfc7gubVoX_XW(+S>0!5X?}Fd4dMFc!`ytc>UMpL z`o3#I6PtS1HE&CqX9TloyBuEsZX$gzk0NtjQD&yt^!cB2p;ulgnJ@M4WybC^EzGT) zzBw$MdR{+{%r+?ZqWLhDW=UpTdGHv8d$Jn_!r1A!{xy}03 z%_%;?;_>C{xS&Fsqgm4~P4ZO(RP4?BL|=$JW}BgFU1-RhJlwwig1*EBFy<%ju9 z%!OzDNAt|kR8A&oDmQA`$}^X7kIdzagw{L$2#<&`&chn?5g*Zl06z+4%X_ZQ}|75$*hBC zzSL*nmTO~fWr6uqels_<*1=dGY?fwbRkWA_w>*{9tMtX38_ZTL&wb@g>Nj(gCQbZo zzNP=?XV(LtD@^R-ReMCPw~m22six%n)AF#E=AQoCH9xtRv#^2*a@`IB$X zaITx1>6$FSDnfc|()HR2Qxw+=JueUK1aLuRb zV=k>M-L`qavO1c-qvy29N8zxYJO(En<(sO!lY9@3JIk+OXD#_doZ)}5zk%|eGqikn zV|gEJZ7!dSi!J2o*zYI5ik+?HA91v;+;yhr&&KXx56(u&y&qJ6+x(vm1zBG)qjceSZ;Y-%eSY=+u#Tvj;(u@pM?_~SDvo?bsU@HzP$a0 zGjp7lw~kL}d4)O7%G(~;HT%81jliCnXL-8{C#Fw%n}&V!y}Z4N<0s|6a5hig)ZAY_ z|5(q!YSUn@@vY^#`b^ZYjB32#>rdC zm*U#na=WLsJRfh5t@+AF<9wlf3yy!5pT_Ag@@LrpRcNB9yP z{;m8WT>D3U52p>~POz+hvE54U`;3-nx0Q!fUQM2Wqt)fv*jh_|zslE_t!LHG>mhH7 z^Iq~$9QBqj#dbe=R^@Hwg*d~1<8V9Wy`IzZoB{G-IK!7+*o`ll_331jl$Xb|x#Ihr?UsWjMM^?rF|TWj}AWd=O3^m%}Q5QBJDd{OiBv?K52a zK<@k>wy*gY56j!`IQvu{hy5k;{W$tceiM5?%AeyL|5N45mG^#?<^3cdinA5+g_X^} zkx<_5$4PTz-3zeON?uXro#fW$JY8-dtFycw4px`9#m?IDLD*kkJ{j8^$djt^o5(Y; zyQw@67x*LWZ?3$}>#Xkxc}HxIlm}yXlsp`xyutRk zPCg#n_-Y*DM{#n!>K9`F2KgTx;6CPguiV}tJ_1{}ss1wTPmv$O=J!|i{rMFxaJx5| z@7>C`#`$#lXdK)pUy7ak!H`sqm?rfgt>&sgx?~0vw<&&`eo_rUM@O+%%waj%wz2Ez)-x_Cl2(B$seijaJ zgj2i#7mHQjC1d-4BKO7l3VAo|?cH48e@EkVkbE|-9WLL3ts~`mm5-4Z;rL1Udz?Ha zx4fhIM9;`Qu=}jM9WJns?dOzFt^AVw8ctu9f5E{*dHn@iUh$s14^F?40~~)VKT*}= z&#=8rdCgqMmHW&54U78r>Wedc3U+={egh6$x2o6A#8!*^I=0)&pWy(vd6(^ld*Tca z#04IV^LFZYN;STN9O4Aug`JMdU%)|4{s@Qo7o6fw?`eLu&Z^%6+xS2n;1h9%FUL8) z7Z><>Tw7iJKgBWr7dvYx@A1Coli-2a@2dQ89N++3Ybw7C+iS^p;;5VaBu?>%*jZco zk2t^`KhS()+zY$wtA1CUZy=AvL3jE5%C>xc<(~56)p)!ZXB#PR{!q)eHfhuc_+$%J)?H z=JM+}#4E6~h4R)PX+G9g^0wH)V{oyp@)_9QPF{jTyw1n!7j3V6@5(#Kr{iQNImYQO z^847{P42vy?RBI)2wTU?=VJRrIl%>fzsgTl{x9}UlQ;cD{fj_81lP`xFTw6v@}oF7 zPhO1E3*?qh)vtDuyft?4aO_V|eihCxm7l7{UnYN1)nj{!`p0+(b|$KRa^*?#3)sW| zVjpjlt6zkV!`4+Ae>)B)%WvcSX1U=rjSr^C{jf7tJ`tP0!eo`VdvJ<#?BA_??a$eM z_sT=Df1f4jj`0Ls;Ag6Q zuIiUnIo@>X?}g(hluyLolX8lK=j7(EH9p3>;T)fZ-RD(5 zv+_&w=QzpaPT#1XwNTy{+xSxK_m;oKK_9typ?(P-io-3HPsY(!a*C~f^0KOa8@caK>KAS+ z55x8V`7Z42DCaoaRqnQe_1i-}76*7LwgxMokBjrP7mc@9$XDR_5BVh={3Ews$@(|9)Q`6vvD;cc7yEc7&RUc&#c>4RtJKdQCJ)Csz8Sm2mCwfo?(nz9*L>xB;AEtH zK6Z|i=i&md!1gHR8~vkxAwCkv_!gX;r26-;7s$1LH9kE<-USyI$m6hmp*$BmcqtBX z&1$H>-nGW7elP4@DxZVh%j9X;xkApdeWh%h-#e7ApQA`V0(&>fS7HAa`C07VD*u6F zykDdG<#-|vZd3h#ae~`5sXoISV)qW!Z-;|eJ_^^S%V*+bh8*K!w)}QAK9LKY;||SQ zeu1~g?i|&R#u1)`WBf3-9#Q>!*ulSJ53k)y%M0+HIC?_kPs16$9T)gTY(L5R;1u_0 zt^Uqa$`8dJz66JO7WSW3{Rh?fSLJps>gT>L?@;+|`Bdy>@|`$*Pkytie_#F`C%9J| z_0RDkxb}hSFTyUKg#)|@$9QFxf2i?2&F{_1R;fku(!7A zhgCVASmo;|pNFIM^-pnc>}{vJlwkRQdCD}Pyy-%ak)Mg41g z$a~^=Px%b&>@82j0e%x_cqLBvQGL&@Y@fsBL$Ql5#UZ{Ar$?y%Q=H*WYpS1pr1Jf+ zk1xd;egb<(tNsUU4VO1GzsE24UpPWO0=vh{SK|!7i2W0k|A7mGRhP=4Sakq6e-W#WUFwQTO$KzzYd_Q(BlHaLv z{1>(_R=)AN>YrR9?}vj4^6A*RRK5+@@XJ;G<;uUu&h>Ja^;mzrEB5dh?Bknoj?-%V zjq3MJRgYIQe{ZAQ{}J8+d$+0ncx>G+Ux#Bn54(3LUxFjtaRc?Y?^3=au1%3g;~Zay z{Rfmkg$w*84reG|qr3X2v*m%fNMs*}bL6XV?GgD=9OKWhGgo{)H=*OSw5gkP%0H!A-N*IMLlH&_1{kHvl)0eBzxH2Se}g2P346+!)tG)@i{&U z7kDbRHq-beIKt)M`|D_)f1J&gAA>V|5B9fKp5c5O`48+3kT=>|{p^AC!|qP5p%4=`K_Sr|?A6xs%m*C<6`7!JsC@;k^UbjE<8>D%E)s(ggDtQo2@uk?kTKVHR!b@?1yY8fZ zL8SV9aB_ouE_QE}AH>;R@+VdO6uINhtp8MbM;zjD*pBIk1N;Tf@M^oz?{3xi$HhJJ z2{@i6-;V8j{BJ*~vTDF!p=N3$eGU{1?u*l%Wel2_pjZ@jnqd;2Lr z0NV%3XW`6~@5Sz5`3)Q%E&qZGUtVt?^{` zVHcl=J^Vd%*(_g6o6yzF3mf_xPA zFPAUH-XwW0b|%Z;;_w>TK0y7WYvsW>jpQqF?K=5c?BVZmj@LU-{k-c{zc0@4={UGU z`7~UcA}_A$@0L3ZqTe)mr^@%rC*x?kJQX{S$RFeQak>3L>Q_7^?}BU3%VTl)vV1po z-jv_N)&jZNQ$Ob;d0U*~Q?U7$pzDwK8>;%xF7TR%s=wEyd`RUE z@~t?i$r<*$$ZZB|ywzWJafAb$;k$8x=VNE7H2!#;kjDBa!TVU&4`A8h& zi*RtB@>w{ z#J6DkKgwUgDPCHQe^vP!Bh){{E{BP5?k-c zLve&J#r6W_b8vzeRrx~Y&Bv*~`+>YU_VJ-O!sD^?q3R#1a{Lj_aLXw5FYs2_UZnBE zaJodEgx#Oyr?B(8{5dZEkUJjF_W4WRq4Fwu6i)t@Z^HS;9qPx+TUFjmZgqnCS-s`$ zafkz4;Az;~MD?HG9It(%`q_PyAB8=93-<9s9O1^%8lU3LD{rrUhhu9e`9kdCdvUgp z^7)nbmH)u@!E)b|v^)=w!nH$`Uyps9;utT(IbQE%^>dHZ_=9k5h&%!N_)(nVW!O1J z^}SC~zX%_Vt>MfM`}h@{;eT*4O7***s(!&}`7&HPO@1Cn`m1lkG;3#N!Wi|ez>Z~@8j?j<$vQimz~qKyz~qCNbGzq zUyAMTjEAL1OhoS}a1GSv^n)^d3?4)D#mz;9RiPpWS>PW>{xD^7k_egU@rlApjn zUWvW-tJTj>Tb!wWwJ!2d9OA2R+)epQ*y|y$!a*Oo`&sH|Zz>;&b4R`w2V2QcRXP3= zTm6)GI9vT3ygg3)D?hpN_VTS&{Vwu#wKJ4QhCwQH6)z95m z;}63bz82g2D}MtA2gtwR1aEvE>wBQ`qi{4tz8WWZ9?tO!92}+kjn7xVY^Xd82gBs+ zaf<(oYsV;Wyg=i9yd8FjD?bfq_(5#@$`|7pce+sh;uDqcf>V47F7R|5j#m9gI6q0Q zjaNVSRC#wC;d5|~XW?*+>OaHTX>zBF)Xxj#U2%A(9N-w=i8K5*w$D<%6{=r=`{NX! zifd=9KEgSE0!I^+e}es+nA8(DVo0Sj2{w?wZT;Q45yG{8*9Ni(W!r5JN&r7tt z+7$U9?BVlqgzv%rRMo$Uo$2zgl^>UTO;G;=55>t7%CE-Wv+`UV;>9?4PI;S4neWST zKkVZnIK`J?=WW%$gfrZD8S}$CV|%{pPsCOx-;AU80ME(IMc=gNGKgBLK|G8HE ze0>_WK9ldn5nfW|Unt-33i{y_D}Sl{A?z%bf5183{7Q|tzEXZNcE6Tq;tYR@<8PFA zpUCp@D4c(*d^YyJlYhkSGI^^>8Xw`4arV9P`>^+e{28`?l-IvX<6V3-t}R!7CwB11 z*e;Z>d$q;~cqq1iVmx;7$GEnF@sl;)$3t=LXU1a(=h*s1`Nr31yoXP~_Dbb5ae%+a z?yt(XyjJ5Qym#f_l>0cxSL5P$<#%K25BYKI;rZD9Q~3`#$L1f_)!$e8OZob^_P4wj zPVm^O-s)IC-fzSyo`Zu1<*(r!e~Amc3fCG{-}ySt$HkjtuUYxgILDK*)kgV)xYkZy zgu{;VA64F0UgLT#&)HPo9M?9N2V;K=c`OdMl&{8VKl#4O+sH3tr@y=eC-@JX;Wcj1 zeC%yizZG`(mj~n8!SV>49xji^;ZXT@oE$AbiQQrH`#8ftVE-899d6Wo3cNjzhATe_ z2P5RGaXw0Z2z#UDg*e22VE-iLJ#NzSf|KO~aC(Y778m$-Y@e$91)SrraB!OPwl}N4 zAIO{I91p_5SmhVv+UfG+xELod!{PaI!!7FXULbFb3%m!8FI0XWw#LgdaDYF<*~QA& zzE%CB%jAP`aiu&7yI0H4Vf$M77i?WGcfU>jvZ?akIF02qaWPH43EMN|m$1t}c=-jk z9#`J^cJ(*^DQf+Eu_aF5kPpDldvbuoPvu*2^p!jhTi?kaSNRIL@eVDo_*LElXTQq_ zRCz#+&@%fB#c|15R^1Ha!R{kEl?d894 z&{-{V;t-vZ&P_!c{iNngRs4u@)6j@XH@y_$}cu9 zw~vo+!!dr0`e;wpe@Jd0DF0B6_vF8^b)?+kUag-sMBWhBhRTj{xqRzrd1vg6ln=uJ zJ{dd5DL>P=JYG{gq4EjJZ=gQMv&d^FDu15b#UGI;_!siv6xFw#uKC8er*XM_Z=CXh zIJj8e8;6(3L$G_fJO(@0%Hxge`{NFIGPbA4ah2maIL5EyXsYTz!TCM%*SMG_7ghai zdA0jkpJ(Ouu=}FCU6sEh?}e=e^5IoI9*gZI%CEw~uk!S2d_(8@`Qcd{c965mo#mxC z-BezM?aky4_iO(4mU4HTZ7uJO^L^!GaBYA2d>nc5WjH=uj&XQ|{3Onfl;6a`QS#?F z9V-8glhJa=2Q=U4bh$V7&X#w=Rwy5gi|gdkIK5FGkL{b}>u~Ux{1A3ul%L1WtMYtY zTPlBv{jcO-tNO3y)n;h^{?wiUuX6D@o_Uw z*Og~g`TFv6*xx{Y8yB{`1iQWDRXD(_&(!)@y_IidTs|IyK5{=C_LX9Y@>9j~my|cRR@os`_2!rMR}2{C72eUwMsLS|4wJc~hL= z9gWNW?jYp{liLT$r{KVoufW;C@)YbJDnEw9!Sa8M%l^qw`74|qEw`Gj+&@NM7pIZD zCC+b=cf{5`@*o_|kcS(W`!Ag(pNg&7@^~EL$yNTK@~OtC`%!IhWDqj0`L9#?s#JOO8a$v0!iTBH8>xgST( z@-xQe_K92M`Piz-IrT|r`4{rq8gl!GG=ICB+{3u+XZMr`kVjj}2jO@-c@*{r$fsj_ zpd8}bF7mCF50Gc!e6ai`jt`f=#OV=o+lMt@_e9yo@o92@oSz}@gS~O`Xq=oQPr%Vl z^3B-2S$+W59+c-{>tXq|Do^B(u{THl3Hy)8=6}ztA1~qKa?i?7$Xi!_O5PV+Ps_(( z_gVQ=9N~$$mMOmt$9OJw-ckNi<%ROcl|PnOVCxgPd5-2Ed@8Spy(RJh9OC`3{kihv zu=AySCQfiv{jjgZcxj6k+ehn8t%I{)#xx5qy_%H1Lqm@C&-;}ev-VdaoIl|Bkxo>kPpPkSov7STjS(YaP3U_0_wwa)h$yZ|gIr&ap z;5pcNRrxDb{TuQUY`rJ{j@=LCu1{*d&Jww=asBb~wY(d7{GB`)r$5MJsdtylSCd!I42~M_B{wof)mD@k9`4`*C>*IWYycKqLkPpDtPO^`ko#nG| zvWt90mG3T3!RdbTlh`_3eglU`$)6h6_xDhF6?t~F+-@H8A1QBuYolZb+sDg$Rr!_j zFzn$AagHNgT&4P{IGilc!P&L)D^-0Ye}J9q<*#sbhrFuFr^p?j(fZg^#y=*XiQOmU5Z9iPZ^J2`jiYCjzlyVG#$t#S@{S&??w>+!)r??yT zKTy62wmy<~#kD2!;W++{ozrOqj z&i0bqJkR#uTkekigX96&J5=7Usy|FV3dhIFV{txGzN*Sc$@gIQMEU6|#~)&AwDKQu zf!n8=uXl>_UN{~j?}qI_9)g|IW%J+j)t{f^bL6YAb*_AGH6FiE)#JrD#Vc`+JHEj7 zJWu`X%H!p2aS_UURQXl%!8o`<9**N_@|ifgSH2y4Ps?*~I8S~P=P$_L;q+~}=|#=A z$mDJ~e_!r{(+}jGvHzj$WA{^e9L~OyCt>?1`3@Z5$8h?a@>h&EFz1K(cljf7>reR? zoVTo1fBdz6iT&9|UKbZ#<=wEmmOLCg-Q=@zjwe?6I?C_B(R%VMT zehK?{0S@tJIL6CyivPhmUhO|xKl^Yke?9EtzSzeDaftW989o9RcoeQ3q5k7=fG6Mx zUyl=fH_mZ_?IYFidFIdJhySvCya9Ic_Snb!;}8$WF&>Lk9N`>4hAm&q ze-+#K3+&=m*vB2;)Ot!2b~wfR;T(^^*6~{Y8Q8{GVi(_neLNF~cs`Etr#QvSagN)+qxo7VX!+f- zgSW#T-U|o#2pr)Pae^n{3{S%aehk-6)bc;X4qk~py!ryoH^4jL2#>@Go`^Gi8!qrn zTpO+BKZ70o2KMkG9N?8W!p#dczXW%|8QusNxF4>aq~-699efz}@WnX5ci{-XgcJM$ z&hYoR!0q4FeC<=T{GK?(18_D*`4LrpAdkf!zPhT%GjKju^{?RK3^~WvIQcK^2WP9^!}dAy2yC4%pI6miBwtnrbpQ+zBA zH&=ds zzC!ui*u7F-j{S*p>rXU4|5mv-PUpzG;&8rvIL`2C)%Z;LmDqhpj&b^~JO@Xg$_uOd z&*h(S@rB&}Q}*Xlc~e~bO5O+Cco_C@fJ1yGPH>EK{3xz{t^O}#7r%!C{0)xrDxBet zOPK#R>em}P*u_2`j3azJPVptUz!A2;RsSj2!85Uo=V1@Oi$nY^PVk?&_MQ57$+bQK z-WX@NKem>sem@-JVO9RU@-eu!Tn=&evwSxWSIW=g;tx5)_P_G7%8hH+&)?Q(%olHf zQ@j<W@eX03*qvZkEIYZtLM`y~%WB)8U#OWn+ zjJ?a{=W%eS{4tKE$UotDs@!%d+v^^ABb+=Y?}Uq|@ju89V`sViPUXMkpK!8D?(&W1 z6a6Fi!+FCx_4CW2*l(1_R9;iQ9=o>u5Dt6DZ((agc?r(&A60!Xee9ekpNk`WBTg<<{$MqJg8T~3E|ZsF>q>b=HU3(;%MY4QdY#+{ zTi46GVILoZqnngpgu`3pn{oWGJQJsn$gfo6-2z2&9C;Z@_yL;LLP)u zJQDj~D!-u0zmf068GZ~~-z$FyM?c8RvGbSQX1V6$uaY;Z{I~4l@L%~@Tr{j(fBavB z-9~u|_L}8KaNJ5>fWtN9?{Ku9{5MW|$XyHe=ODR14o{GWVt=%JepP?Ad;@kbmSSxK{SNUUd{`eb+(~h!-!`0<6xVD~rDbBZ-@526}@}oF9LY`mcL*!++HdJo?tLEb! zCwIrS6XhLoa)Epp4z83>!{Jr(MC?qK@5br%^3&M8Ltco3+48qId`fQmP4g>Wmb>BP zKk_zJ{;GTk&fbtm;`lB3TyFAh7)qp;guo`9pC@&h>7NPZF5HkLobIsOM{y_B!}C-dtq_rtYK<^6H7r92X6 z+sGGGABVfh&tQ8uc|n!$E-%Byo^r!q?BBiRwQ#bJycPEMm-oZ9LGmbU zdGh$G9^Z%yJPU^htNtbI9wINo8D5V4LzTB$rTG?kT^tTp-VfIfllR3j_OWxg@^f&3 zuc>^L@>w_;D!+mKqvfU89VR#b&HV5NI6Fr9KjC)@9L|v6sp@CSE3r3QUZX+t zi5`@<#P-9ohuuUz30rgI5Ic{`x8Ue8`7xY5DKDtTKP!J*jek-81G}%wof|bj|1G%} z&hReSp09i`w%(CXt?C!bmtgNb`4*gfAkW6NMe-|E{m1hARsAROavXmtH#BL!=~r@B zT>Dnu6nj6)JK>;^55yrp7RUG^Y_CxLEjY(>aq_eBcd)-w{-LVJ?V2@T=U3$$VfQzA zAWp3f>d%k+V81~=27Ar&xj5@C-%*Y4DL;jSjpT*c?I(YWovr1DR+?YFjl3Z)2FNb1 z9V`#4e29D=_J_!~;pkF%cIC_E=dpdA{4uWGC;x_B+_tsmSKyx5nxXpsIG-hZRXq-H z^q}%fu=lWheU;CVXW`^=`6V1YBfpE&cjT{e?S1)Q?0zJ#-=g`4OXMvo=dz2vrSj3Z z_KkcxPQQ~U;ru80c5MG8&%)U%`2`$W-RsBK$JlEl|B0QB@_KEUZ%rPEgP!tW>~1WN z!Vw-{)o-f&CY^&&Ys{D{V4;T1NY$eJ+$1z@sgGZFN?x6LH9+kV`VjoqV__pfriI}Wjjtz%R_3}^Tx91T}~HZJhx*zuL$ghPBUPVro99jp2j zC-^O#0`8r^p}U{51J%><03xsvdXltnHEDjd6hoVt1^@ z55hhki9>ugj`3AE#kb(>O!b?Fopa;_7w5^(;^+c-K92EcIK@9#<8iCi+20px{8~81 zeXu=V`3~5_2jUbD#|0jX{fjhy0*>)b*b0^3k6ru}F7SV`bFu0_!3kcDbKJ0o*3Z2} z^<8j)d*cjmkL?MnKM?!a#}PgQTbHW-a_r#SaDiuI`!dx(k7K+5XLuMz6zz8V+!4jf;l`k6SxPvP)t z<*!xa@dr4Wto$qNTr2;IJ>06Rwuf__@-EoN8{*pa%KKp#?}~Hm;p|4$53hW)d@8nX zk-gM_(h!KcW`*S>c6P!@yg0~DDSkUwnvWp;P_7EJK+o; zjFY>RA772f=i_vW@@sK{r{Qd>@+Yws%WqXV{;bOHR=yJFxMeNPzjlxEwQ+(s#|7RM zXVX+a7+d$sCsgC{c{sg=D|f{??t|SA zl<$PIMY4yjkL2TUxI{ju8vl)a6%KG*)qk)2vC6;5uj2%NSdCw){A--!->~zm@|N|q zelhNfi{F%Qh--hy1F&uNu*%zkIBSweV7I+|HjX;VlW@Med>6L6$`4}?|EC(iuJTW? z)kFRnJNO@*^;F(=eb#S7c@3Q5o|Stk-wFr4<=wEmiF_!I@v+$MtNb(^Z7E-dYg@^8 z;IN;Z-~|5%r`srBjDv0EpKy#@HsJUipnQGoZ7=u3A>OB|-%0RU;mSY6DPD#hU-`c{#vN>}uXn8SwQ)W| z?u*@#@{U#gaq=LX954G-K1Mzr+o#EwV)tzMW}M+iaP3^>Z(|>Shwbx~H}uf_?aSo# zE5~wQ96Tv+i=AiWJ#mZ=tNfz!k(FPO1DwApkH_8u`Dz?4lJ79ypuw`-#qtdDq;t>u z`RoaDXI=R}INDIou+>Zc90$GS6;-~8{CAc2kvsO(_Nn!k*TKb3a$na^nezEKnk~=5)+6%bs{V1gQ*SM= zcv{{CTd6z-J1@&OWB)bzMeMyL|5Vj4kk{FS?faoT0LP1D4~HMiCt&*%c~a$Eo{4k( zDXx8`ynP=n&s`=D#MTP=c%1w$UypPAM3w)cd;xaFTu$u<@aFg1o;^p;SaDoTKNw+JV|b~speNZS?+;zylv%Elpltz zG4g5H!;^4?r{fgAjBBT9{8H@Wmd!Mu8280#p!)rAZLB;J2lzak1>H?}6j9lpltzv*jCcaK8LizxWBSMxMN{h&lkfITl7EIZq)U?PJjkPF@B~hs{=3-zLEem`?Q)50)F14S>tk=H+!;HW zJQiEKyY@w4XF;0zBc_!rF= z;dGyz;bgyj&UNaG56Cy+JM9K{|;>5Dv!YFUGhro^q2Qz%a^Si`M!_J_hIuXc`}ah7VJE&d4-#l z&+sic7@_&o*cvIni^EZJUYI{8U)EH83GRTcv6_#-Stx&m{R#3(Y`iE}X{J6O--e^9 znm>TUY4TGzeOZ1TJFmzoHeZ#GVmp%0Yp(MG+yr|wHNPKccq+DLX}%ttv+2hXuGT{R z_UoE=!r=mW3{LRcf){K4C(g0elKZ}=`Hk3qU+#f3JPA7=X#O#_K9u)kZ@GNo&FZ&S z$xU&xOYV-N-SRkW?2*@C4<9b<<0`Gx@BF0wTXBY;!~R~)-@)e3@=+Y)%B|Js{-Sv& zoa1RY+^6{pZ0(ni;}ltjop#Cp z{`1+qmG56vu8)0uFSbi+9u($f<+(U7FK@txB_A*B*N`u2qw}(Q@=Z8yBzMIr4sed& z#9 zcq}%X>--O~jeo%|KIcyL`}jH>;(Kw7pTH?zhP@U#ZyOHqzc|7d+@j*9&>PsgS=}`673#-^N#92Y1FPehBCIC2Y1*UxICX0K52{wtPR_7>D=)oZv@r zhDT$owa%M?UHlFX@j9H~9XP|K@6mmXTh!MOo43jxu#H3P;&s?-qy1etz^8D8%eT{c z3BD9(xG6SnQ{NzL;n~>1o3MwAwpV|EE8_^a!RGDiAC7JOCU)^B*vE%)hR^T7_jA?P z6g#*b_V82KyhHm7v5h~&F5ZTHdZcotIz> zXV}3;XZ3sd3LN1sIL8yP)lU6uu!B$F0NY*EpWyqk(O&u2u!DEv09U_{_v2e|iXX!{ zegoSb)&CoI@pYd1d^{S5_(vS$I$gD&;`?yWN&ORWj8|jhUd<0-7gy+}z5qAHF&==! zF3Qg+%<(#G+^6{wY~w23)$ifkafqM9DSjL0IK!r={&GFk=ir-gfS<()PH~3M>#4la zRejBI-c25Yt?u%?!hTPAYhjK{_u@YIG92RDae@b7qnG-|VGA$94&IJqeCGW+FU3u8 zj(u$2ul_mM!CSG_Tk|pxaDRLi_HaiW<6$_(Zx-@>)VHgU#}#_3-+xf^Ryf4Naf%mV z=OOL?fIVEQkNN_9Esk+lZ1h!rDz@;~*uljgRG)_%;SdkT37&^@ybYWEbY7W<)Mw+S z*u}%JkKe{2{t3tUg1+iY@ohNABe2H$$7;O0J`vlwgPweBm z{nZ!Z9yr0%afVZD3{Zb@UwH@LfIU1AhxiSg;vG20=L}GvHBkMnv4aEb;bl0&hp;h7 z`Dz2{!|kzypT$02iX*%mC%Eh&_4$L<-w=oRejMTH*d3z%&kA{bypYE=2dh8E9dL?A z;~cNV<|FDqifw%9!|HSKeb~n#j`7Dh#lK@?DBp94`W$>G_V5TC;-xsndkgbn>O1!l z-iuq}6c5AZquO7HZM+?4_?)5YGalD|Gi>9**u%4MfKwdd|8RosVd~GYhmGMn?Hx8fB{*{mNJ)e{xz(ycX!8TrtUHlgg@a2!GFT!1LibI^^)!2GU=N-omuKl?B zeB1?x_(dG!6*$K~WAkbC7a6WT8`s7eZi@2}+8>O~k@D+>Jl>2WT;d7!C%7TbaBplp zqrO+Mg}=rQF7l-MJX{}#xF?SB6rAJFvH7gd`v=>&UcmRoy>WU%?h$i+!Bq5LbL!{V{HajpxC z=)CFJ#v8GV|1Io~*8Y_v)oe zieJGwPO&py=NEfHc^}_^6FeB_cp-K|<#!kIxZ-H_CHPL9m#v#6Vyv~bpYncaR-gHr&TE5hJRH0DP3+_C zIK*eaq`nw8$0>dU=Xe%2XXyMdv5k!>d{0~-hqxDx@id&_4cLs-|6gGrUo}Kn{zbZi6dNQn(`UG78|c?zZbUf+t|ThVGke187?!O_s><|W!T59afo~41dqcx z{uW#F)K~Fk`mu)tJQv6K5YF(GuPAT6p}ua|#xG(Qe~NwlCyw!Dud2_Uuf8rg#uEzj z1)8tH9{vLdxW;ShOK^K^EL8plY~c^Ehxg$CUp+&85gv&X{4UP$L2NA2c~v9jjW^|X z*urD6i#KB*7n`Ykh_A#k_Hc&BVdE{Gw*u#QH+J6Ey!b5j`?wws@m)B^!*GgU#yMVz z%~<_=u#HR4)_E>&fPH)yj&Xl%E>_>TLLM)|AzqJje6WyTqP#gr=i9gr_VLX)#Y3?1 zj`DM`g}=ovF7`U#6F0@~Qstj1%<(!L<1^+eZ@sJi>v4$tVsDw|vv7*H;0&KLPkqLF z+P?>Tcq&ftk2u3;zM;JPzVc4NAIN=if~VsQuP^L>sQr`JS}xa_uYL!6*vI2=hF9Qx zh4M$Sw^F`*f%;P16C0~Ee+^rB3wE%%P4)8Yxf2{fGi_{n4hB(6y zV&fC;haJ2Td$`z}>I-l)oZykzTCKh{*ull#Qr^Q&aDX4jG5!Fj_z!HXQU4WhtIxuH zu!HB|0PnyFK0j7I$9H1uQ}vI*4ql6WTzawcA-)C2_!;c2Ro@Dn;-fgnb(Zjb)@lEK z9O9Wc#y{W`SAK`@@tN`+v4^MN0Dp@kTy81nZ&3a=9N;lH!s~H@i@d9Rj&H=~m+E^2 z+jtfBaj9j>$G9y{afEYx44Yr6-+51Y2am=c-i!lmz0Y~L4^Ht?oa3X|`da7J{DAXt zC+y?#*iN;-8oT&JVISA|P<}NJNYy0SOxwKx}-kzIU*L_uv?x{fY89 zZi?+~$`8UWo{fFH35U4YYV{dED1Q~Ua4#I;c{stlafU0eq3=iawZ}G|jD7qK4sqE} zm5=ewIK?Ayj#pu8yUsg-9c-^vpNIS45YNFe-i)0c>MOZU`4nG=jh&i5ge^Q5J2=B0 zuKAhzB0KlKfyl! z3rE=A$o=sBIK!`D^Pu{^!!|DSjq*NjiWB@OHV!Gj7&~|$4sexC+y}SA)?wwxV;8T( z0saq1_?mCkm*W07$MdlDtNM3h2Uq(}c@Ou*0giEwPhtCr`dVyOKExAnf`7z0uDM0~ zR<8U&?BbO;z$Le8KgM_C3`f}fO?`*3gByOYypKoX5T`i%UHMAev~L`h2Vn=V$38yq z2kwV^;0!Ou<{#?&7u&eykIF~KG!JoxzsJsT&8uzaJ@`Q!;iWjm|6t>U@-22KZ{zXU z!{6idPwiLQseR)w`95sn`Pjuraey0TybnK$%~R@o4+nqCf8Y$)+oinukLLZbjhEmM zAHW%|wwwIF%6G&zeg(UDGq(QIzPU$v2e-jKegQ{#WnpgA_}~BDJ}=~p$QS>lJ`dl6 z1N=Nr@CuycBiKAmebx4=&&F-Bho8j}eh(*jAI|XwKda9ws{Yp4#gAbhzllSf;TV_y zMSUr5hI2d=o5ghAB5dOW*u^#XsV~GHj`1{{;w{)XUHujI^FDkRcJKu3;qP#SD;`ij z!S~@D&%t(aowpx*xb8vaBRmKv_(PoIB8RkZmQdf#*v8|ri#KB*S3JylxC>73ES%%L z*et2@YW=FbjUU7wUW`M00;jmq5#@6{0$XRO|5NPXV!8Hx+!TlSVVvT5*eIpG@3D|%fJ+Y1FVHa=5AwK(r@(FH* zbNnQ>&Qkxo*u@92k1PMFz7TiBDV~B&Q+?lJ8<+b_c^9|F0Um)PyaFfqSDaz%r24FK zI`2;G;FqwEx8o33Kc#$(yJ4%m`XcP$9}4^U+`pC2@g3MaTlp8Tk5}Um7yn231h>K& z9*@m))b}>F@p|mzy*S2a{mcFEwb-nnzV0}|W3hj(=I`MUe~)8)3R~xCzs`R;&%yU$ z4^O}WUW*fa1ZTLqQB?mvk@MBx5!*P#E?$8{d<5sXS`qa*7pSi-_V9B!z$&t^+#P#(I*##1oZ@0dmCx}t*uGHx4`CP2z&_rDLtMO=`eJ-NPH}&nf4W9T=NX&ecT(zcp*;luh^}ozIvtj9v90)uzQL8I`;8a z9OKfZ$>ZyBjvvH!4fV~yF5ZNFT)YhLsj2;IafSzCtCr>qv4elc0j^qBeG%@A6Fd%Q z_!Dehs{Z5H!8OlRUx4q$5q<$@_aQ+_Ru@ZUJW^-c8| zmutTd_VG*{;;q=NqkXfS@*%z%=QzM#UG2Y*W1Qm@Us7Iu<`vrSh^ZSDVs zU3}@;+z)raF&>L^{4sXxsqa`}A76S7_q$T_&e+EjaD+d_Mt$ywJzS@P`V!m?=XeTs z8z{d4`?%P-ybs@iW84#`cplF2er#T){@UlM&&EBli)UgV@4z9hc)s#6ZiiDm8Rz(0 zY+kMN&%Qu;7vG6}JOPJzHI8wyiprd$dUY+k4R(b&c-u#5NM2%mS6`i$$9Z-HI> zF!u2r9N|qk!KYPGUxu&2;SK8Vi(@<+r+6#QaaoJ+*+}^tv4bDRKAwYPyb0&{w5sY0 z8>_DYPMXMluaDv~$89s=O+jM^II?7wPH}>!w*m1T0OJR;{)a85No;bpDaDsQ@3}1K!-{TJT zcfb~Y2|IWz&T&OsdGk)?@5MHL4ZAqQKCW6%`4D%(DSie=cd367PVhl&+^uL07rNQ zHacp*#I@SD@SQloui^;r!wIf`o$_WU^>xP~o`@5?8fW+@cJ5XF;_KBH;k$5#pTS0F z?Z1aDydOLG!W;M=xD5{QlQ_nUaf%OMvy0BVw2}I3+ynb~29EJIoZ$+Ml{fBFpNm~j z9*={r@_L-(^P4DdcGJ8gw()D&#k;YOt2@faxIa$uhd9Suu-9GZRk~660C&d`UVsz) z8_saUn|OZ@^^L?9UV&Zw2Tt(iO_k4Z4{Y>Q-)q>y-(m-sX{Nk~o8kyRiZi?jhrQIl z11H#Qu0H>M%^Tqm_rWoqf>XQ(=lC~lJ)pjeTBzT_ci{k!!UuS9>x})kA1uyhxp7p)EDE%IK%z0`LOz5!w%kneS8u} z__90IpW}P6IYfP9v4dA(A0NRHw(e42itoTVega#MsDB}L@K)?&^KRacZ^tPfhmE1? z`x0BYOk3q0+!zOVFi!CToZ}3e!_c^#Ec z@txQhq5N2E;}ko%L?`8a+!%*=7`8^LZz*>05$xew_o^?%U2%-3;}mbi#xv?K*;#oT zH^Ckrf&;v`Fn?D0Lxub(xmp+Xna|1Xv5&{$6tBU?^V&aIm}C1s_2sxXHeS&FTx{Vz z*u&*KNYv5#ls1b>4wT(m3SYmD*@u#0HG*gct7^=mHjvmKZawx24}cz zf9;zy)PE1Q@eJ(YJ%#<5+OOd&Z_JkaU<<#6U3?V#xZwchBRm|ZcscgxsQ(x?=gM^l zDj&>~@5lKY@+=%Jkhd517s(e4;(UA$PVhvWep~r**u`tHkN?FXZZJfBG476YJQgN7Bew7uoZ;2j{7CyJ zv5o6KsXh;P!x5f}GyEAg67`+J4!$x_Uxa((1V=c-Td?)9`p$exc^5awK7JfW_#K?# z1K9dRebt^;pMyJM55I^*ydKB+v=Pc@_&RK^R{t<;g$H|1oy(5gg*GqtqYc zJ8_CfU}L@dmSP+4#R)$5IrU|@B{sfLekiu^eC*(D*xsQ1GS92e#f@=*hu{pqg^e$j z--jJs;|0Da?tv3L59jy*HosC|jnUi(_rfuri;b_f|1-95jWNnQxEJ&Bh`67U>jE&r~MFj#uCoM zeY_VZxavgpWwKK=`b z*!qy~hg)L1l=}N&7f;3^{-BV@+i;Hm!(M6iRbQ_20^ABmxF62&L~N8%{(WrW?byYq ztx$i2YvLTY!DdzN(+9&%rMC z@H05Ti*SrLVc$~UNgU!U)~Y|pJ+N6-`_r(Ezrr3qh7(+A9pAH>@=bA!``{c;!1l%3 zUyIG^@~OfcTc6R7n`8SD?e{Ls@z}z=hUV`V=6FwGUQ_e(pQ}H_H{ujOfO9+^TeXy5 zi5+|pd-#I&>JRX(IKofj1kb@4{u~>Z>b&0z`}o2y=&!ALvw|;^`{EeCgj4(x4lmdK zzQR5(w}JjTnqP;Vx^h?S;pcFImtgA(?QbpQ@xO(+t@+hoavtu7BRm&pcn3D>DPQ?3 zToix8ndmjU)UnPVr7`UaP({zM~J<$06?g ze=cSiuB-jNIJiTejGa5>PjNT=dto0}+^jzDF6}qR2_AsWwwli@?B64Az$reCvv!(S z-9mqR`BrRqkcZ*~&&L_wgrknyKZSE#eXIJTPMY72oqOeBIK}g^)kX6yIK)N2SD$g8 z=Jm0Ud*HaM<}cx>o4g+9-Q`o*?I~Zjjr;bJJK?amJQ7=d<;B?VC-1^Wf4S5T>a%?L z8te^_yW?P>9O7t@{9$2#u)Gha56cyPRKGVwZh;d#1e=d&{(52mNqIB2o|4bpu0Cgk z+!R~S$^&pbN}hr9=j4speL+5Lhx)?Nas!-=k-OtyygUIr6XZ{D{-S&gN0a25JJoN# zBzMBrbon(L;dO=mmo?vr&&E4&MZ6zhgpc8B*vNE#bzBzL!j-U%Ti^iuIK$I$@`~=a zv@pj%VB=NIPur#QEqpn4@jW=e0iJ`W;sn2kGrSoauj#!1u#Ic(*7+WGafpZE1kb`b z{sLPw)c*%|ag{yl4{&Qd3wOjZ9)MFk2JgkQ@KL-B8ggU|bk_v0(^ zAlwoMxDy_O2jZ9U^Ek%u;EnhToZ&5a4?c{InR-vjz5IOO8rZ>2u!rx%0e%k0cnQw% zkGR+@op%_Q!N$*=k1JvaH^V;eha)@=Cpg9#{u&#zIUifN)Gs>U!L_l6+u%oWH~cIf zfXCnwcru=fWBduu@gZ!@(fumy(|ugr1P8b`j`52)$1AY?y83>`9xlFL{Q<6tBis}x zxG&D}6l~8`|1#VHe}V7BJFtgK9?Ni~qnrzUZLN zkFbLi+zn@VG&bJQdDC%mya=C#6KvrF*u^KXhbtZ8*8?}g5%zF`N8z1#8s3LvY|Phv z*W+?H!x!P>*v95z-N(hXv5#Bg5O>Eheina$r{GQaExZdSILF^(V}b5p^jF=-#&xiV z+u;yDiDNt;XZR~@EL8u`*u=$;=)79E0=DrL*u@=i{HOYc;uKHByYU>H}F} zcpA>}Qf!{meZIjqK7?I-#_xO&ToZ@*798V;aEhPDIi8Enzjc0sZM*}!xa3j32fi3b z*ue?zgfsjYHvZ9h)3Al##}3|vJ$x7kxbz>oPlW5^1h>GS;SM;%1F-S0?lTr!cn-F4 zf?MD%*uzDR={})R>wkaVR1wGc8l2+WafXND(~2lR9$R<;cJL?I#apq5f5QPTeO&i@ z5nDLIjc|fH;tUVM#%a3WIBemC*ukISws#>7-Vjqvg5uT3|ycTCT!@uJr*eId%FZi49 zfg52L-;aml$8d-z;~2k%Q@j!9_$W3@^6T*r=i}?Jhws4weiTP|PQj(r_c30Ex8gN; zKi+`K3D;t1b`Gu#&&Wp%$7 zv4xjm2XDh3K7|8(VG*4l;U;($z8k0b32dFI^WVTO{sss5FC5{EPSbe_ZiX}58yjcw zK5XOVxGnw~d-y00aOI*pFUBo#hWlc})Ok~|jX%a7K8QnHp&0MMO>mC8V6&Y1AHg<` zu!q;;0RN05T>5m*!?kdRn_;88&g+FOJQ6#2Cid`Z9N?We!vEj|Tg7#s47bMS**d>B zw()4};u!n*a~$9tN4Rte-j8eJ3^&8ZIXd6NkhI9KPrfi1id zyZ9LP@%g26-e_DKPsUB~OnfJfabKL`XK{w-VB6EYhxd` z#v$&FWBeRW@m!qa6kF%(e#h_)xNI4ne+#y-hg;+D0_6we1W&-<;W;?RYp_{S`Jb_k zOO)mPxEA(t8yw>PcmW=X6Z{6w@Oo@qsPp$@3zz)=f0+1xf1c>zEAbHQ;Ky)#9O8#@ zj9TaQAOvs!WQm>ufJf4gr9ODFkfit`x8#Q%)sdIH73pcju!Wmp z2lvDt9*;-jnK;BNag2Y#IWBSm=hxPKDq$O6ja}Rc_s0YAa6A%+IKs2>yZCMV1y1lW zY+t7PRIRA{`M5P+g4^K)55gIK2^*KIFToBzP{`wRF4TE;9ql*99)19acmlTSYJWLy zgVRDDAH)GJSxM(bxEfCI^*F_M;SBe~#uYk$Dz@>*cq9HAZ^gUuZhRCU!2jYy_^ish z?;p4-{s&)yOdw3cS@CP`;-{A!RhBI9DBHhQRr}M7C0lp1KxIa$t3pm5` zuyLjOKE)RP2|KuW6`k+lOYl(K5Qq3a9ODr<#joNVufS$~-DfMVjt}80@ITnWl`P%w z9$XjShnr&`_s0>Qh!eaL@4+d~@ljl+f$m$Xs?N7?9ef>ja5LNv$GU@G6|)ZP>V0=l_8%d}ejs$H5J8fbYW*9)%Me;|za>f5V%xah=Zp z4VTAfT%z-<;!5~3d?mgXx56&&iG4fmCFTtbnIvn8)C%AY`ouA=L@k#7p^9J3oH@0zr8{@Iq!PD_gcph$sm*STAW84~l zh1=jC@a^~)dW3*Ls? z;~WpbMkD<^1=z&%@%4Baz7em+&GAO;;#2r3T&lM2KMGgH5pIOv!MEZB55YNp5f^Q& z`!B#2UX5-14Za%h!VW%)Tj3&?>Ao(mji1EV<59Q`4sjnm2M@*b@hJQ@o`RR*MfelE z60gT!<4rijKj0I1KR)Ynz2{F{1^pa2s46d$OBr_g#$bQNBCu&;rFm{qx!zX7S6GQOI@M!JX{}7!p-n2 z_)a_%cg6GYARObTae`;yZ}Bpm<8N@$n{@x5@tOE2E{}`Yy8n5&9KHzGz_s!9*ui&W z7x%y(9*09b4@dZY9OK>iXM7a@ic8kheNW;lxMWkkw>~}_--2!22fKJQ?t`aeAHRhk z!z*xr*Wst}Cj1QEg~#AycnU6grQQ?csyM+7af+McZ}FXYEAEanJQ(l8PvOHj#J}O0 z_%FN^muRN%yAE6U5O#2d`g+emTmuLAW;`8xIL5Day(tXdym9T}I;2ZEA zxH;~MZ^Z-g-S`>Y1y9F)@OAh&SUaaE_Z}vz5MQS8U?|x4=_zTf6{w!yn?_ zcmp1YcjCwKAv^+~!XduoI=v^sx8Ys5GyVnl#lPam@KO9c{u58bMr*x)AwC0th|j_s z@cDQfws5iQbzc|P#Q|=MWBfSI@LX)(qWh%S!GB;6SGqyxg}4PyaDRLVKaG#$vG`9s z9siBz;{Wh_xWuix-&$N2Z^q}~UvMRS0@uW48|nU6V+%XD3HESr9O7s2>-c5-4qkv0 z`~}{Mcj6rXiH$aTUzx_bUuj$!pN(td^Ke643AeyiaeHjzhp~%a#J%tvIKZFb2p`4? zF4si&&9IG)+w`7R*u*`tjRV{ikHIc}3qObxJQ#1mPvZl43_gh`;?j=p`!c=&N4PQm z2;YHI?Bg6y$7kQJ_r8fM)&Qi*LpCaTk08?u&22 z!*MJ8B5sRkq?v4}u0N#oNT%@V)yA+>=Kfsl7iW}p7_;#FQ5C4e=;nVKW_ZW#w z;TN%q7h(&q!ZzNDU3?Pv#%DCsd-~(^@L*gM2lxgY;?{UFZi}bj`*DPa;Cc83yaZ3f z%kToc3V(vX#GCMUco*J=f5*FVvF3VjjxB87sqcL)ws3ds;o*1yo`4_6Gw@S*2_B2r z;Rx@>OYk3fIW}78zN>I~ybf2xU*QIL3vPyY;Jfi&+!gP~18|N<;J@%xT;eW$uX*?^ z`~kiYug5jKjj#mjLYoZv_B8ax88!;|pWcp=`7-^aV~ z=XgKP@Cp1Y{u`fuv+i%+t?yMG*T7d{8{dbY$Nlkm{3MR>Y`h%5hu7kD_)Gj9&hUS@ zL|fgjd@J3z9IlQl;)eJV+zQvjo$z&d0B(r`+y+m^9q>Hd4ZnkZyb(W(f5DUSNjwvm zX|4Ax!WK^O^*FnewX9&xB;$?8{r$Ui|@f*aSz-J55f=O01v+y|v6L#_UxCj0T z55W8IGx#W;g8#+~afy!lz8~Xr@J4(g-ia;zC$@2MSMR+FpM$T%)v$vbV-NSjAs&h6 z;Ym2gb8&({z#H*;{5}32@5I02gZQ*NbpPYHJpKn)!^Jx3d)33GaASNXZiCOk9r5|N zH?E8y$2IW__;NfI*TZjM2Y-g!;UBP%Prp;|i*YTy0bh?d-9z7&taH{h3W8~hsXf@3@sufY>=hGTpJC)nt$_x+45 zY~HQ=-G-~+yYc0?J#K_O+#2`59dIAq13!cZ;eq&B{5XCYkHT-^iTDFN8L!1N@V9sl z{u#fCf5$OCqpjY*3R`#su8%k4mUtKLf{);b@E`aQY;@80pN#9^nYccF6JLuzz)kR4 z+!SxcF8&RB_^f;Mz6e`5#`SQ5Z^0SvfsOlg|KZrelW{#f2RFp;P6bd<^HfZddw-X#W8mei}QE$?xD4 z@5RP&%`fP#KH~|w70#cOAI0HQ@>_Tw&TxV&^iZGkwD#L#V}$$^cJWedkJNk@PI1|u z>IwiaoBDE8l!=i%UO`mqzsC$PC#KEJQ}{UvfEY`-Hvh$H+0PTtl04eY)z ze~FzB<>T02E?4Zw_rlj>dzI!LafXNEWS!=7aQ>P6Ss}kc-dEWFTCUh%=PmzEz741N z8EkLXd?ohraUA1IeZJQ=?YG6@k8*&Mo${MF+9hwp`5w8%0QK2F$#rq?vwRyi_Q?YZ zJ|IuT-a$FR*o56P9Q`d%$4mc{7vL56L!9ETv0LQQ z|NZllG92PR@cX#*Al)y;6>(5h`Pzkfak)NrOUgIk_P9Cr@EzF4_hI`C<@@8PgZu<` z`pBbkfM*x>@gn>NehX6*zra^VS8wB6q^}tMUUl z!V__V=VR+N?Jvgx-i%Xx`XjoZIYax^@hi9~wr6R6D>gooyJG80*~jKr@(TrjEl(-z zaE5;>%r|QO2TpMDp?aV7jppa$skk?eD`OK8bT&W|-bi0`PRZ5-;1q%nm<(7$B*L-KVR^E?N7zY0eN0wj+YjEQ1jI|I3%Zq zJl=`T!zeL8zb~S^J49n!#1vpb6g+0)wSOohqxV%aj$|e(f%XYtRX*# z13V2!cmdvum*Wh7QP{7kzU?^1zu^=Y9m(&vTH3FGU3@7HaU&e#JFs!7@;$MIhhY~# zTX1ddPsTo;gT2c%k8y@qVdHYmH{cv^!)6`Le<`@Gd>q^O^k?*b>k7@!!7jcS`?wyq zZSCKPL+oO+p62%z_HjS#;NdvJ&lh~9@>8%;Uw$21cq#Vq>Vg|+|C@rZl6T<*|B7>b zs^F`&U*=i9&o%M|*l8%&z&@^zQ``(^xFdEOEAJQV%P-&%&%rrfg_8l=--@k)^6%Kg zWk%`#>9d;Gz~(6V+QNK=d^-+S%0031k^C68Kb9xp@K<>ue&dMzDNgXtLcZ8#|NHBg zzp#tTJoo?Tx)1O;s;dFR0~nBLUb-M)2FHNt3j{DVOA87Cv0#c!2}>7!=}u@Hz##h4 zO-va=6MexH)7O+l^bN(B7M3nz%2H!O@jqwIcjUwUqj?@%dO7!-J2P5IHqCmy^>uU} zu0G?#_@`)WgoB0abYEO4L3W~Pn%a(zlV#Ps(-Nl_v*#Y(d(_^)o}y=7RP7n z`kiqTA7bmz)A?U)UR6)R#S7K<;xc{_SMVn|x=7d0b*^4t>tgk?IJrcBQv_S@q;?!iU;7hJ`E#Wg$`Psh*U zCVmeuaGUNo6UTVL3wWMy>U?KIlc(j@g2A@QrAC&Yip}N z!qGbFIjed-6}%)aU8nO7o8t{|4R3>+cpn^HuYJY(8`Rg}dY}4kT)IX5oUOlA{SJ)|He8Atc%`Xlh!_-7|t zTO)gYu8J%8_c)4meju*l3Anht&acB8Z=#-RbNmi&;Gc2zKwZD=C3-z^kNP*b`DH%4 zzHM=9`L#0dhjXLVC*snI>PvBc6ZIXqQc(W`*Y{C3adLoqCT`xW&Rwe4SGir?fy?ix z3pjaC-Hq#uubo|Q88_S2XW>>+{WtSY>U(hGboJkHQdhr)oA@hSnX2=)%k+BlkE=)G z2L6rt37v0)H=eF8;Q~GmCwKy`ZLv;ve-m;3cj`NE75C#heh;_skJfLk{Sue!^%rp- zSMWBthWEoQd@_zZ^?VmvkNa?WJDop^ph58*_!vDo_OXrLKRj;pr^SF%vfSdRL-1t)azu@>Q^_4im_v6LB z(fLcZ9)E_5xNVYNPZ|FjH}Iyo_^qCA9Ikw)J_Z;5qrL!_W~y(&34RK-ryX z5ifD2US9*Rg{%M7_1og&59$MP1)pZ?@in-PAHpsC8qWQw=ldGRc*IqDJqcbF*YVc) z6}&HQ;!|+`Cq4fpT*mj~1iy@1_zN8Uto{5~>-EHVC0xW?;GOVZxP(u@O?)}d&C>Hv z#(DfAF5(%uj%VRo^qcJK;}X~C_2uVKua3)es<*_gxzu~%+%WZ@ZH_O+@!UGE;Szq< z=JV+MBV3qQJx8L~Q^w2SCSD)s+I0PnxQGw4ez?wiaRpz8oA^;2&8O>M$3^^&^?1a! zdVO`g3U1-8aBhA*UkMlR3AlnU!%cjb^$TeKJTBo+a0Ac%H@%(~ULF?~)P56O!Q*fZ zAB!9KLfpQP_P660KaHFCL!29->*u&muP4UK;v(Jvm+{WHfse#dPS1B9F5sJS9Y1CB zg?0VAxQu_oRlL;oeEjgbxQTbbg+=syhv70l3n%ynT*nWX7uCLDUQGQlt}d>g^9H@1 z23{7o@EDw5Lf4PQWqdep;LsD#dba4seeW){(0QO1J?ga=X2e}_27|sKfD1x6z_y9_z0ZfvvCvOh;z&6 zevjik{ueIe|KbK-qED}{u&nlL;}Y(|2|gG%@EN#?|AuoT^?Z-w)$kiQkH5oZJm1ZF zJ<)R7kHQtaF0QYu^X+hHJ#`5uo2!qJsFqwQ}jP)hFTH1a%eX z&r)BDTj#3p#?|xGPv9ng1=lao`A6o<)iZJ73iW)q>-{$IGPwFzov(pwxPS|jbiN}l z;{9+LA7}kFx_$yKZN6Uidb|)9woqS({qG?xQQFKew_9n;?j}oZ*i-lo~NeQSNn^4QCvPvy)^E{qi}*(##8WG zxPdpo19&qWov!EqJ#NQ4;5_ceow$TcxCf8NN8la2=n4r{nW+3txuYCg}BE zjbnTx9)oYkMSL&r!H?hyehN>-&*K_?1vl_V=JWJ=zQg6Jdf~hD;~8J1UL6-_!aV)JNeu{tIs6DvoZ{^@(_aC@R*}cjB3Msh`8`lhreD4<3HE_7%JaE=S&Ue81r`3nxB0d#Y@O`)!e}?<;O84k~ z{djLYfG6R$XLP@5xC1Y6ulAjIOWcEx#JzYD?!(XEe*7aIz^mS;=Wlyf_un6P;Hz*a zo`!qyqW5dxi+8|%_yXLIU&I6WC*1a&?%y#*&)0!>#GSZ;d+^Y!D+ z@Bl93w&!*K1b5)4aVP#7_u$bFa{qWo+=oxV{rDz4fM3CFFX;YrKg9jxwQwii1NY!w z+>7tQefVA6j~9Jd&p&`S#ceO@{s-a?d?D_{kKi6WfP3*$kLdaPa3}7^$KnBeBW`Qx z{?l;>p8rujUnef$9()w;#n<6J{0i>JKjQ(s+GBeDwtwpWd*Tj!Cho-d;vW1S?!~z} z_m4Ni{rF%!fUm)AFX{fT;SM}vD)*1K!aevz+>7tQefTrnk4HbQ=NrIdaofwf{{-BD z@57yV0QcaLPw4r2aTo5x72J;>zytUT-1ds@Kk`XEUkBa}cj5}}!FS_c{2}hci$A63 z>&IK-0el>8dsX+p1$W@LaVK8n?|Qx-yfN;@`{F))9`45v;sHDZw@uUi7ySqKk2l7h z_#oVaFTuU|3EYQ2!~OVI{d)cZyftonP4_BKCw>z5;E!-GUf^jxe;?i$_v1bA z06qb?P1pUe#U1!r+=*Mb2QTuB?$?Xg#(j7f+>cMj1Nb`J_PXx>B<{ff!JRn&tnSx? z_s6~XYTSpX;eNcpb3A`sz-@2n{^M~6z5{pS4{;A3`8@ZJcfozQiu>_1cmU7+g7$51 z>iz}XfltSsxQ=`9kGL1-U)1yU;RA3#z7h}MX}Ikz-G70G_8oX<+=(aQ9^8+6@i({+ zulP?rUq2p)2k`m0?QPxvG2DT_#GQD>m-Ku+cpUD<7vMfT1^43tJb>F@*7LRfOZV@@ z9r!HViSNTb_}{n}FZqg|uMcm9`|*i*0N;(<-qHP=xC1Zrs-CYCZ;gBK$+#Ckg!}L( zxF0VuP0u%gx5RBt-M@@G@Rhg|KaYFxJg@2bdhuGg50AtB_;NgepT=$P>i)BE2VQ47 z_mB6-J$MrC#n0hB9KEi6KVBUV;BmO^J>CCI+=1`Ko%p}F2XFKS_m7XoefTcik7wWk zyuzE>x4p0XcjFFx4(`NH;~u=|TkMa=;XXVO_v7h!05A16`+uPOkHZ~!BJRY~a1S2w zFZRb<<34;M?#DGefIq@*AL{G?YGs<;R5fP3+A zxDQ{C`|-;|{#g zhulBz!aevb+>0N=efV44kMket`37(axBXl9zZiGm$8jhA3HRXjKGyT~;v;Y$z8?4E zcklpSVnF-08M^=GxC0-IJMm?>2S0{;@mIJHkNQN<-;c-Q0emWM`&9S84(IVxxQGXE zg6I0Tp1+Ql!==ykd>h~dkHzKBb$%G``BHr*?!`Ca8lH*^U+MZcaP+nMJKVq{X6W@L z-{^cz9Dl3c4ma_EIQmZKr&^CE;nII}em~C7R5$QU`~|N5SLcg-s@K!;qk3Ij|4BU# zch6Fvj$6@2+260e3HQ#aegVhB)ZgMMbE}v8O!q6!tKJ+Bw5bolmHE_{;r0d858~Q_ z>Nj!c2=(yK_5978dR07r5%umkx0w1w+_$)T5-u#Mei%=0SAU4hORGnGq5HKhquvxJ z%c>8>W0q52h8wYZD()Gj{su>*)yuc^{1aDD7jS+hbr0_EP+x>gtElUE=Bny1aCLR{ zufEjtcdV)Y1Fq-QC*tn4)pz37I_i&b@4D(GztZ!?>#H}$Q^u(Gz{L&K6+Ez!dNQtT zqW%oG|5iQvYdwFhpxy;{Zm#ad%`McE@%XLO@8jIo>Ji`Q`T9E51zgxheJq~-2ldUk zyq)?z+_t@XzHjw>Nm0ET9b<_y}x>W+&VzLH}36GpNr#z)eqt+hp6Ad z#lzGiX6pF|4p(o2D`oXTxcw;gMYwjf`eEF8tom)-JWf3ej~}mI^LyPd_h+cMk9QnxObBJ zE*xK_ehE*xTKz39ChBE>()|XmRd0nW*Qt-h?Kh~e!L=LJ&*08JbqhCdR>wc<`N!X? z-W2C2h?}to`=+L zND~9f2kkBxu*ID-1n|}{b72(!u#q&@bnMV2`+!6ejT?Bs27=A`{Wb# zws_18^#t7bRQ(w4`CR=Ij#}ys=F#&_{8D`^&VQ{w5BGngz7?0gQ}^SU|55)NSHD;1 z=GF6;e^Bp$$K#7}1;2)S@iJ}NSMl#~6Q79(@YA@3XW-mVy8npbdcG1~8&~ii=AU)_ zMBIyC#Z|o6d^|tA6Hf4jxDUUGYxrB-!W+%6=bMR7#nCLio@a0yUUUInpTj%gc3j0V z{wMCRenIW?ctc#qd*eEufTL*R?APZv<0*5fU%_p2s(;2YUNildS>&JB$8+htgcCdg zx9}94AExVP;07KsLeE#8Tjy)x+&t>-aDHC(A-IBjae{BdrQy1My3O%?Io+>`3pk!n z*Oze#--mnfPq>VKx3HdXJg(vjo{W?ExgOlYKjPd1I$voKJzoRwh?50%ehO~k8ZIxS z^Y?IWgnF?>^?WtFHE!V(a3QDb@4{6)12^$@u zT+f$VRM&5b^Z01o!ilY4OxI7xMclT8p0A9z#C3cSt}L$o^;S5>N8!S+ zbbc9b;-_(a8J&M;{j%ycme%vfBh~xh3ce6G@xwM>PS?MOqgdVcD?MKWua5J}>--P6 zf_rd+&&ADAy8bTff31E4*YI4+=zjUpI$sx;@qxIog3d3;Rs1w=;Tg7mMP0wtvU>h1 z-Vhg8()sQLp`6UkPu8E34`JNZiC%;o9mt@3-}9s0VPorn+r;J%1gqhNHaBcf=KZ zIBwvx@BqHf`n9y5Vm*G|di*ZVudVCn9HrM&!K3g*ycw?H-Enmt?T^Lry6Q`D1^*4_ z*3EZhe}yY}v0v-;pN`kWy?7g3#fRXD_#B+ziMWRE!uQ~( z@f7?XZsKq70G?yCUSHb=di{&09=Tu?wKh?&Ec1n;D8H$CE8Kpb`j0rq<83{@8W-{3 zaT)&ykH@1{(ETd7fP3+dxQZ)yBEA(TxQYAloGa@2Yj|Be1@D6E_;B2h6WqX0;_3L` zxQU0Yr27rvm2eC1h-czsaCE*t-^*|teii5NQXP8!cKll$;~w0B&%k+n10I8)#RdEw z?!@!0tos%5^0*s!;SxRu_u#8=89$82*-VH}peIDoIHvBNo;SX^;UUm&VUyQfG z9r!?;$EV^k_%>X?Q*kH$5*P8W*VO&G@gHyrkH-a|8k6*; z`te_T9X)>=-W%uerMMkGiDNtyci=UCqvy-x9q|}^7%t!|aVKuzA|Alqc*%A3{3ZMc z+=Ks&%lHmF9yf3W&%(WUo%QtmRs2Uh5ub|_{0P1te}Vh(g6r$~YIp-Y1^3`Oz7qH2 zr*H$mkEi3GaTAXmqx%iu0&d~`@l1R%jxN&I!{2cm{ubx(${XnU+wo2~#wXzpd_B(N zXYm;PJucv-H`McY;`MM5cjIn+A}-T2)#wXzpJPGIVWIP5xjSKiw+=+*6qSsTz zYv69Y6Yjw$;4<#R6Y(^h;2&@ekKR=GYvL|EfRD%Rm+14l7RUH`oX20`3U2?cp1&7w zfv4jma1&pN^Ox#=&)_k*g?sRl1wDTmZ;WerFFXaGhU<7T?#CbC25#F-&p#cnj+=Nd zJb*94EqpJYiC@RjWqN;eY_8{T!)xLk-W|vIOkBqI;PLoF+>4joLeF2tTjK!c%ek<$C|0;uyDYrRVF!Tj3%;7LUg_;tGBZ_u=8c z)AQBvM!1d-$Nl(P+`_NonRvdf^?dO}z5W93z<L_r#hc-Y_#m9% zb8sKN1K02?cnbaj*YR4v*ZunOVYq>B!PD{kxQUnDM*9K018(6n@J#$5j;_$>^#yLj z%l<*n*N(TvF+K(7@nk#({~H(ZsBQIpowyr!;|p*JKZDD7p6#?BkJrH!yf^N}=iwTD z5KqCM2M( zMfWRRr}OP`4?Y>yakT0*Yh8TJMiVWgde~?_$}Orqi#Ka4d-za z?~MoW`8c{k_kS3-;SX>fFE~!m-;YCW@i^R% z562CBCeGcY&*NI$j_bG^H*pEiv#0J~!K>p0kHvL-HSWis;|5;uk9xl8_%z(a4LpEH z?WOBm_y{}`PsLH6-rxKsUEhXx!#R99&f}Ny7`)Qn+86L~xD!8vi}+Xj==yGaBrf5r za0Ne$6FmRE+V|liuHj4Z6#NdZp&9I~cd&$8Zk6 zkMsD~2WUSA_uv9P8<+4j+=Ev-Q2R0-kH_N|a0M^dqw9O|vABx=jwj+}4$}1r-WT`b zJ8=#FfT!SX57xepC*pqm32xwZ57G70@fo;@C*$ZAecm777%zON_67V~T*7}@f6&7jGnKKPsjcEDcr#G9INZ6<85&hpM?kTv$%!7 z#<|<|c`tvQo-dEL$3=WT?#3_S5?IziXRxCeLOf8adMov7=_;6LI5z72Qc zS-6OIK1us-T*Dj&_zxP>plGx0PW-Kih{_S3a*!+YTz zJ{{-rlXwgsK0*5e?!ujTA}->oxQu7uD&FJ_J>Nup2~KQ1?!#N3sq1U_c-+9Z;1+%k z=W2TYEu66CZ$!coOc$ui_GZA1Anln>e~y&p&|MaSN}F zzrow#nRrheP1fgi3~s}x<4$}5F5=s9H+}(^@Br??3tytwdnjHNm+^M^7<@P$k1xh2 z;)ihszk_@6uuFBnDqaUq#CzfdABN+5^m&|yN8?Ly9{1q_ehgRc)&3P+#h>EFeLDXh z=k8a}dzoHuJ6;mUcxBvyH^hB-2VBGZ;VF1LuH#Fre@OSg-Fo~qp7@x~-@^(1$$H#= zxn6$_uY;%H&2SwTaX;P*H}KJTIzAUS@vV3OKY?5LEj$x{g`>LO&jJ(mdfV_QoWmR7 zcDyr=@nN_F_u@Rh8jrzKZ~?!FJMqW3h@&g?`nqvDF5z`>58eTn@gaCTJ{4E+B;1Q1 z!&ThG6Y+OA!SnxBudfe}!ZkbwPr-ZOI{q{6$Cu*9)o|z1>An6UT+b1;3{4hCwN<2!~5C% zDZQWZxDTIU>+uzM3ceNB@uRpOKaU&uH9Q@Ej+=On^uH?EPk!QPyK3%eKSTO<&A{uY zK47jnqT6SJM9{s~o%a>HcQxn?djA(wEYIM^%n#>3L*-v-L$C4_?2YOJAcuoc+z# z>z^Lv?w|ECm%fR&SX}ovTb~5``+0W%BXq%~cK_J=cut**yV9R3ZZ3VYu+INRZ??WP z?3cZtOCK$v^RwIZ`pwqo<(>49H?qH1&!;<7sbJ0E|u^#vUd?(gT)SLuITOz+=peIw}oT>8>TT`-I5H(Oug^Gn?2 z){CXD%R@?!;k41=II*ac>o;3pkayBU-gLS3@~-rSHDnq^@36mF=JNc) z>-Tf%Tl9Zof3x+i;PcPVrElsV*A;!v=a1R?QhE(?h~@hIJiC7V)8LGS0%=Qq;@+w%3(Y<*7NNe_9` zmHy=C(ib+*#?hR^b$_$0m#@F!{rkD}W%~2Ef3x+4VS0YC?C6_BYG=|MmR+T>1k2VtoBFTVK%A5AN^h(pTuadHtEKuLixJXV<^2 zE;yb(hpi8vpPx%#V*S4K=An8&m%c_n)?Pos{^9-GdO5%JE!GdaRX_e_+du66T>AWW zdjBQ+`iY0?ZN1E;FSC9n_BRjJ`+0W%^w;wBs}}4Z>*Wo8{QX?|2J5@n-)#FggWk`x z{kwF*9kxHVKK%Mob93pN_(-0g+4@Sbzn@EA+FlnN$@QD9F9p4yOJAq|mi^7vhu81t z(zocV_WHrr$HCV>TQ76zb35q;-pA|TY<;-DpG#k$zm5IPL-l?xeVM+_{^p^2KbJnC zUyA2vwm$s)_jBo6^efYwtqTEm%c!MHqYN|eIeN2&!w->|H%EDt#1au|Mqj~OMlc2^0(>z zo2_pI`}?`{`Mq?(IC``7anSp@^hNs9=*`xL&(F`Z`=`H!zJjgKK5*$Zh`Z9C{5`=9B}gT0Oi_xE$@<9&6(O1JCtH(Or|dOy$hryon-z}8oS z-p{2k?xzb*p*LG!40=D8KB2#j-fVp-=>1&!Xn$QWjoxg1Iq3ad`Z9gn9s2yt*2h8b z=h9c{H=;LNAHM$lT>3iwA@pYJ!;hbzXV-s#Uf?9Ver$dC{L5~h?T??c{oP!C{a6a_ zzwYMJ#|P?y|I(Xn|6I`fx%7GZHSW~sZ??W1^nNaVLSLdcTVDuzKbO8qe?Gn0`tbSt zx%AZ@-JqY|Y<(rz-_NDb9i$89N;i_i$op zZ)AV7^|6j4aaS;xzCiyc_BUJK2zo!4K6jWdxPsnneKF|$T>29IVR)H_Kc;|0?oMddM5;{apIS zk=Z!9hTbgevtGx8y`N|MAEgVX+Wy#jE^n|ey1DdqJV0-@J_+{sbLmS*>w@8T>+>^P zAMWqx+5OWmN8iBKH*$J@aaa12pG%*duJcZMv-MH>`)WC?_w%ejU*|_zkFBrBJLxgl zySemnRp)2Yo2^fDJlOlW^mY21>CM)M&(F`L&tIq;JWg*Os`vBk{^?)0`^Q7|iJMEG zyGZw6VzPex&DNKK`}cF{%k<;u&DPg~-p{3vFV+QD(3`CfpP!#gU!{MT-fVp#*x%2k zZ_vL_Z?-<%-_NBlT%sE+c#l3mv-JgeCq3j%ms>BEzH+Hdqv+T4W|_;^k3#xG4zcup zE`6PTdwTOwy`M`TT_$Ibq66v8L-l?xeS!W&yMJtb_|GqGz09RAU!H9rT}p4ZKD+UB ziMY$H7iaxM?eDf8TVK@i?B30#uj5zf&DJ-9-p{44UZD&ALvOY|eEs;j^wD3nA91g~ ze$3XF(BQM3!a+4}JQ{apIORob6S zZ?->PnE`5U={13g^ z`dHpc4|yZk@8{CD?#jl|Lig*(&n)ZZ^RKAm!QRiMFQnhM<=B$mY<+Gnor`6EKbO9k zzV77Mhu&;`N#02hc_Y1_OJ7UxTaI(+&9YvuKm7Rnx%8#9ljAyiv-JgeCq3ki?CJLijD`b-_ND5&==^< zL-l^1?O)do_Ot!5_2Ki2++6y?RPE2EH(Q^`JLw^By3(KgT>9qY**LnN-Yo0o`B&wg z^pH2w`?>VxC$n+%4!v2{OP__jk>1a<{wZ0M9uMgAFJtS&&mTXRzRvm;>CM(h!RyD* zv%X)q-`aX?eJS|*U3YWo<7c%$p5API`1#}K(l_YOr#D;Qly}lY-gLS3V(Dwo%QT7} zq&Lf4o_{I*A%|FcKbJoDLN<;*q&Hh1K7T)#zC=IYgZl9^Tc7CZ2lw}L>7y5Q!G`o^ z>zl)LE|%-}bLpEe>-->kv-S1me%brE^vTyczntD|eJt;!hrE&f{apI`H`zFPklrlo z<@qP_PI|~2>HS>#(s$W7dY9fT>!q&*uRlMRzByC-dDE|h^7YGXeR%zTE`9W0?N_BY zTVK`j;Pv~t^i}$?^k(Z@!S(yO^p&4vPD; zD0-FNY<(%{{apHZZtZ8$o2`$6*RP*v{c!D9d00PwF&?sa^Q>P``*GG|>&tWN^AUHY zKgDh?eRUC;MA2#VX8Eh~`YQ+5@8{A-OKN``z1jNeJn5SB5X=64p6$P^_S0;CY<)d= z{#7@ZzPP;h(Ifi&%+{y>nNxO%Wq&`HzPX}IqG(lmv;0-L|M2U#pJ&&jPSF!cg z;O}4gx%Bbc+MiBuw*LRUepoN_tY1(2Tdl{|hu0swx%3VEGQD}I-p{44tS>90=qGyf zP`#f^pC6Um$~#wLHl*+&4YdL`RnJ> z=eO2=FM9J}AMEev($}}u{%m^lP`#f^U))jq`{>Qq7lZ5f^X&e+wSV94A6p;mc<}32 z;^x`)k7GeypMM=&AHM$lJiGqgv|on4h^=p?A167)UFlDLE`5G?nMBcV>wNxT`K$8v zix2N$Z|h|)eR&V<$I_dvFY0)(_jBp%d+LH?>CM*vzx!vs%%v~>QTwy$&DIywb#jQi z+7v+LhqRz}h1^kr;)C7aHEets@}i}k;HT%VuW`clyQx%9;Y zWMvd>OK-NmpyR=hpPx%#Jx~{%L~pkK|2;p}%Ut>f!WfZ+a zZ??XkO=tIho?ZVz+Rw7<$AkTxdIE7*`cu`-r7s>TlPDVXgg$?>{MG;U>n}f-J~~YM zt?A9yr+>_6c8FzvKbO9Br2NGwI-cGve^vGm|NS#Rm%e_C_E*uHtuHMoXP^E2{9O9@ zRPCRkHxKr6{j&FS=_?bo|1Z7S`tb9|&!x|uq5aBF>hm`b)%&^h3H>;F^I#u*|Mhd} z^XJLRC_0bcY<)5K^|PO6_kX$e57Ae!^|@ewKbO9GmG(2}&DPf!`sMTYbLoq>X+P>I zUccD-QgHpYUgpx*?$dsIdb9P({J-4a&$Im>*S=!=W9$Fl{%)S_|D5(U+aC|r*W6tC z+-usuOK-Nm8N7b|T>91<+AsWfeSYSldOw%G@h|PSpf_6|zW)7O`qDevA46}p-v9Tq zU2eV1rLTS@??ur~^k(_1qthStd1X5L^WV>6`TL(VML=>-K}+ zfBZb#{|nv!XWJiJp9EjOi*7D`>r3rd`Uf9BY<)A>-`2}q`o>q9EDy}_I4&DK{mAAEj(E`5G(?O&od5B9<5zn^FOkI?=r z+aFsW{`$M-=F&$CYd=r=_oe0ce`f2;@=kilo33CkeR&b>m!UVy((Ls+kJPh!KbJng zl=kbxh$uiww5k4I{M2))_*@cW0KXZPQs{n>W^*!uAM zf7#8YFRZHl&GcsLYr*yVx%4gihv?1LhaW#bm%g;R?*9h8+4}0bzkL3Fp6$Pn_H9q| z`HQX32iM9q2S1m-u)p>@(VMMr1^fHC^!Wp{KZxFJeLnZg`}cF{ zOUG${0lnG!at!x|%=!c9&DNKK-p{44(4R|hwmvWKq=&rea_hy?*DliQ zzlYu|bNTvJOMl2Amfp{$FI}wt%k*aJdHD?XelC5P{%3mgP`#f^U!h+){pW!4_1kQH zW$x_q2lw}L>7y(3`gf%_TVD!#KhN&}YV9Y`m$3CUc_%&OO;`GppG%+P7YKLIn`ON` z|7Q9_4(t6~`WF31^k(bB=kMpzN7w50&-Wa!Uu=Cd`2J(-WiEa52JP3OH(Otoi%$=6 zms>BEzI>zhd(fL@F85zgf5;)0-p{4a-=zKN^k(Z5iPOV+KbO8i-$!q@KCh=A?EPH& z=oVe@8ok;2ioBB^@4I12&DJOBbCN^c<<^U(FFmjQ0KHk}+4B#6{pRP{^}nqB!s$Q9&pv+G`ta*##m%M9 zzoPwS^k(Z@>2=5Ela6ejWNo(1))dKbJoDy6!)Y-fa7qg8ltm`ZE1- z^k(bB>-Tf%>-1H6v-RP>zvbuBx8Bn0e~I2~eJ;3uKbJmwTl>Wt`uxq-Hy76vh`Zc+ zc~|=8SK6;aZFZtE??G?2z8Lg= zE`4qn?T@E7Ti=p*(nH>Kx%Fb{qi*dlr#H)JbowI-zJB<5*8frahpfleC%ef>X73-l zx%9~t?O&%iTc4A6T<_=7*B;RRQ+l)YP5tl=-oKwqUwv5ndH<=eAG7r}-F~q5bLk6@ zXumwYd8ppcrEk%%Pj9w9AMEev(#MbL{v~>|^-=KaKR?gNm(wAS>{u_F;^_Ae`=jYP5Ue$itm-ORjw!X1QdJ*X% zmi_%)`uc$Oo%Ckwn-Zso^?ojW@`?6kdb9OK%?Eow&#s^Te!G5befayImYYi-&(Qtf zr8iq2haZ2|%Ut>b|9~Wy{(E%t{mX29HMo91m%c&23ccC-QqcRk^!YFK`VXZyTVD*m ze*3xfm3j0AFQzwJUkUd2bLpdbwZDblY<(QO{`_3}c)0dY)0?diKmL9$eVP7Udb9O$ zaQ%KReU-lL6~2C9>kC0|>t!x|gY~26&DO_3@8{CD=r^D@Ti*(LKbO8apFY2x=*`xL zzyI{}?EV+f{!qJrY<+nBRX3NuhAZ@D>nrk3ddQnDw_YrLej(leDtfcbN2fmu!RyD* zr7!2SpGt4GK9`-68yo2?IDzkV)#ZehLt5wGg& z&uo2o{eCWeOuq)b+4^vQKbO8tza72V`ciiHv+v)}rEk#p(3`Cfuiww5Z_=MZZyu`m zbLm_3*VCJ=kAv&?bLn%7=<|P!-fVp}=>1&!g#KUj=An8∓F!?mvs(Y<(rz-_ND5 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

This is another paragraph, with link.

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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/dateutil/zoneinfo/rebuild.py b/.venv/lib/python3.9/site-packages/dateutil/zoneinfo/rebuild.py new file mode 100644 index 0000000..684c658 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/zoneinfo/rebuild.py @@ -0,0 +1,75 @@ +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/.venv/lib/python3.9/site-packages/distutils-precedence.pth b/.venv/lib/python3.9/site-packages/distutils-precedence.pth new file mode 100644 index 0000000..6de4198 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/distutils-precedence.pth @@ -0,0 +1 @@ +import os; var = 'SETUPTOOLS_USE_DISTUTILS'; enabled = os.environ.get(var, 'stdlib') == 'local'; enabled and __import__('_distutils_hack').add_shim(); diff --git a/.venv/lib/python3.9/site-packages/dns/__init__.py b/.venv/lib/python3.9/site-packages/dns/__init__.py new file mode 100644 index 0000000..a4249b9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/__init__.py @@ -0,0 +1,70 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009, 2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""dnspython DNS toolkit""" + +__all__ = [ + "asyncbackend", + "asyncquery", + "asyncresolver", + "dnssec", + "dnssecalgs", + "dnssectypes", + "e164", + "edns", + "entropy", + "exception", + "flags", + "immutable", + "inet", + "ipv4", + "ipv6", + "message", + "name", + "namedict", + "node", + "opcode", + "query", + "quic", + "rcode", + "rdata", + "rdataclass", + "rdataset", + "rdatatype", + "renderer", + "resolver", + "reversename", + "rrset", + "serial", + "set", + "tokenizer", + "transaction", + "tsig", + "tsigkeyring", + "ttl", + "rdtypes", + "update", + "version", + "versioned", + "wire", + "xfr", + "zone", + "zonetypes", + "zonefile", +] + +from dns.version import version as __version__ # noqa diff --git a/.venv/lib/python3.9/site-packages/dns/_asyncbackend.py b/.venv/lib/python3.9/site-packages/dns/_asyncbackend.py new file mode 100644 index 0000000..f6760fd --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/_asyncbackend.py @@ -0,0 +1,100 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# This is a nullcontext for both sync and async. 3.7 has a nullcontext, +# but it is only for sync use. + + +class NullContext: + def __init__(self, enter_result=None): + self.enter_result = enter_result + + def __enter__(self): + return self.enter_result + + def __exit__(self, exc_type, exc_value, traceback): + pass + + async def __aenter__(self): + return self.enter_result + + async def __aexit__(self, exc_type, exc_value, traceback): + pass + + +# These are declared here so backends can import them without creating +# circular dependencies with dns.asyncbackend. + + +class Socket: # pragma: no cover + def __init__(self, family: int, type: int): + self.family = family + self.type = type + + async def close(self): + pass + + async def getpeername(self): + raise NotImplementedError + + async def getsockname(self): + raise NotImplementedError + + async def getpeercert(self, timeout): + raise NotImplementedError + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc_value, traceback): + await self.close() + + +class DatagramSocket(Socket): # pragma: no cover + async def sendto(self, what, destination, timeout): + raise NotImplementedError + + async def recvfrom(self, size, timeout): + raise NotImplementedError + + +class StreamSocket(Socket): # pragma: no cover + async def sendall(self, what, timeout): + raise NotImplementedError + + async def recv(self, size, timeout): + raise NotImplementedError + + +class NullTransport: + async def connect_tcp(self, host, port, timeout, local_address): + raise NotImplementedError + + +class Backend: # pragma: no cover + def name(self): + return "unknown" + + async def make_socket( + self, + af, + socktype, + proto=0, + source=None, + destination=None, + timeout=None, + ssl_context=None, + server_hostname=None, + ): + raise NotImplementedError + + def datagram_connection_required(self): + return False + + async def sleep(self, interval): + raise NotImplementedError + + def get_transport_class(self): + raise NotImplementedError + + async def wait_for(self, awaitable, timeout): + raise NotImplementedError diff --git a/.venv/lib/python3.9/site-packages/dns/_asyncio_backend.py b/.venv/lib/python3.9/site-packages/dns/_asyncio_backend.py new file mode 100644 index 0000000..6ab168d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/_asyncio_backend.py @@ -0,0 +1,275 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +"""asyncio library query support""" + +import asyncio +import socket +import sys + +import dns._asyncbackend +import dns._features +import dns.exception +import dns.inet + +_is_win32 = sys.platform == "win32" + + +def _get_running_loop(): + try: + return asyncio.get_running_loop() + except AttributeError: # pragma: no cover + return asyncio.get_event_loop() + + +class _DatagramProtocol: + def __init__(self): + self.transport = None + self.recvfrom = None + + def connection_made(self, transport): + self.transport = transport + + def datagram_received(self, data, addr): + if self.recvfrom and not self.recvfrom.done(): + self.recvfrom.set_result((data, addr)) + + def error_received(self, exc): # pragma: no cover + if self.recvfrom and not self.recvfrom.done(): + self.recvfrom.set_exception(exc) + + def connection_lost(self, exc): + if self.recvfrom and not self.recvfrom.done(): + if exc is None: + # EOF we triggered. Is there a better way to do this? + try: + raise EOFError("EOF") + except EOFError as e: + self.recvfrom.set_exception(e) + else: + self.recvfrom.set_exception(exc) + + def close(self): + self.transport.close() + + +async def _maybe_wait_for(awaitable, timeout): + if timeout is not None: + try: + return await asyncio.wait_for(awaitable, timeout) + except asyncio.TimeoutError: + raise dns.exception.Timeout(timeout=timeout) + else: + return await awaitable + + +class DatagramSocket(dns._asyncbackend.DatagramSocket): + def __init__(self, family, transport, protocol): + super().__init__(family, socket.SOCK_DGRAM) + self.transport = transport + self.protocol = protocol + + async def sendto(self, what, destination, timeout): # pragma: no cover + # no timeout for asyncio sendto + self.transport.sendto(what, destination) + return len(what) + + async def recvfrom(self, size, timeout): + # ignore size as there's no way I know to tell protocol about it + done = _get_running_loop().create_future() + try: + assert self.protocol.recvfrom is None + self.protocol.recvfrom = done + await _maybe_wait_for(done, timeout) + return done.result() + finally: + self.protocol.recvfrom = None + + async def close(self): + self.protocol.close() + + async def getpeername(self): + return self.transport.get_extra_info("peername") + + async def getsockname(self): + return self.transport.get_extra_info("sockname") + + async def getpeercert(self, timeout): + raise NotImplementedError + + +class StreamSocket(dns._asyncbackend.StreamSocket): + def __init__(self, af, reader, writer): + super().__init__(af, socket.SOCK_STREAM) + self.reader = reader + self.writer = writer + + async def sendall(self, what, timeout): + self.writer.write(what) + return await _maybe_wait_for(self.writer.drain(), timeout) + + async def recv(self, size, timeout): + return await _maybe_wait_for(self.reader.read(size), timeout) + + async def close(self): + self.writer.close() + + async def getpeername(self): + return self.writer.get_extra_info("peername") + + async def getsockname(self): + return self.writer.get_extra_info("sockname") + + async def getpeercert(self, timeout): + return self.writer.get_extra_info("peercert") + + +if dns._features.have("doh"): + import anyio + import httpcore + import httpcore._backends.anyio + import httpx + + _CoreAsyncNetworkBackend = httpcore.AsyncNetworkBackend + _CoreAnyIOStream = httpcore._backends.anyio.AnyIOStream + + from dns.query import _compute_times, _expiration_for_this_attempt, _remaining + + class _NetworkBackend(_CoreAsyncNetworkBackend): + def __init__(self, resolver, local_port, bootstrap_address, family): + super().__init__() + self._local_port = local_port + self._resolver = resolver + self._bootstrap_address = bootstrap_address + self._family = family + if local_port != 0: + raise NotImplementedError( + "the asyncio transport for HTTPX cannot set the local port" + ) + + async def connect_tcp( + self, host, port, timeout, local_address, socket_options=None + ): # pylint: disable=signature-differs + addresses = [] + _, expiration = _compute_times(timeout) + if dns.inet.is_address(host): + addresses.append(host) + elif self._bootstrap_address is not None: + addresses.append(self._bootstrap_address) + else: + timeout = _remaining(expiration) + family = self._family + if local_address: + family = dns.inet.af_for_address(local_address) + answers = await self._resolver.resolve_name( + host, family=family, lifetime=timeout + ) + addresses = answers.addresses() + for address in addresses: + try: + attempt_expiration = _expiration_for_this_attempt(2.0, expiration) + timeout = _remaining(attempt_expiration) + with anyio.fail_after(timeout): + stream = await anyio.connect_tcp( + remote_host=address, + remote_port=port, + local_host=local_address, + ) + return _CoreAnyIOStream(stream) + except Exception: + pass + raise httpcore.ConnectError + + async def connect_unix_socket( + self, path, timeout, socket_options=None + ): # pylint: disable=signature-differs + raise NotImplementedError + + async def sleep(self, seconds): # pylint: disable=signature-differs + await anyio.sleep(seconds) + + class _HTTPTransport(httpx.AsyncHTTPTransport): + def __init__( + self, + *args, + local_port=0, + bootstrap_address=None, + resolver=None, + family=socket.AF_UNSPEC, + **kwargs, + ): + if resolver is None and bootstrap_address is None: + # pylint: disable=import-outside-toplevel,redefined-outer-name + import dns.asyncresolver + + resolver = dns.asyncresolver.Resolver() + super().__init__(*args, **kwargs) + self._pool._network_backend = _NetworkBackend( + resolver, local_port, bootstrap_address, family + ) + +else: + _HTTPTransport = dns._asyncbackend.NullTransport # type: ignore + + +class Backend(dns._asyncbackend.Backend): + def name(self): + return "asyncio" + + async def make_socket( + self, + af, + socktype, + proto=0, + source=None, + destination=None, + timeout=None, + ssl_context=None, + server_hostname=None, + ): + loop = _get_running_loop() + if socktype == socket.SOCK_DGRAM: + if _is_win32 and source is None: + # Win32 wants explicit binding before recvfrom(). This is the + # proper fix for [#637]. + source = (dns.inet.any_for_af(af), 0) + transport, protocol = await loop.create_datagram_endpoint( + _DatagramProtocol, + source, + family=af, + proto=proto, + remote_addr=destination, + ) + return DatagramSocket(af, transport, protocol) + elif socktype == socket.SOCK_STREAM: + if destination is None: + # This shouldn't happen, but we check to make code analysis software + # happier. + raise ValueError("destination required for stream sockets") + (r, w) = await _maybe_wait_for( + asyncio.open_connection( + destination[0], + destination[1], + ssl=ssl_context, + family=af, + proto=proto, + local_addr=source, + server_hostname=server_hostname, + ), + timeout, + ) + return StreamSocket(af, r, w) + raise NotImplementedError( + "unsupported socket " + f"type {socktype}" + ) # pragma: no cover + + async def sleep(self, interval): + await asyncio.sleep(interval) + + def datagram_connection_required(self): + return False + + def get_transport_class(self): + return _HTTPTransport + + async def wait_for(self, awaitable, timeout): + return await _maybe_wait_for(awaitable, timeout) diff --git a/.venv/lib/python3.9/site-packages/dns/_ddr.py b/.venv/lib/python3.9/site-packages/dns/_ddr.py new file mode 100644 index 0000000..bf5c11e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/_ddr.py @@ -0,0 +1,154 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license +# +# Support for Discovery of Designated Resolvers + +import socket +import time +from urllib.parse import urlparse + +import dns.asyncbackend +import dns.inet +import dns.name +import dns.nameserver +import dns.query +import dns.rdtypes.svcbbase + +# The special name of the local resolver when using DDR +_local_resolver_name = dns.name.from_text("_dns.resolver.arpa") + + +# +# Processing is split up into I/O independent and I/O dependent parts to +# make supporting sync and async versions easy. +# + + +class _SVCBInfo: + def __init__(self, bootstrap_address, port, hostname, nameservers): + self.bootstrap_address = bootstrap_address + self.port = port + self.hostname = hostname + self.nameservers = nameservers + + def ddr_check_certificate(self, cert): + """Verify that the _SVCBInfo's address is in the cert's subjectAltName (SAN)""" + for name, value in cert["subjectAltName"]: + if name == "IP Address" and value == self.bootstrap_address: + return True + return False + + def make_tls_context(self): + ssl = dns.query.ssl + ctx = ssl.create_default_context() + ctx.minimum_version = ssl.TLSVersion.TLSv1_2 + return ctx + + def ddr_tls_check_sync(self, lifetime): + ctx = self.make_tls_context() + expiration = time.time() + lifetime + with socket.create_connection( + (self.bootstrap_address, self.port), lifetime + ) as s: + with ctx.wrap_socket(s, server_hostname=self.hostname) as ts: + ts.settimeout(dns.query._remaining(expiration)) + ts.do_handshake() + cert = ts.getpeercert() + return self.ddr_check_certificate(cert) + + async def ddr_tls_check_async(self, lifetime, backend=None): + if backend is None: + backend = dns.asyncbackend.get_default_backend() + ctx = self.make_tls_context() + expiration = time.time() + lifetime + async with await backend.make_socket( + dns.inet.af_for_address(self.bootstrap_address), + socket.SOCK_STREAM, + 0, + None, + (self.bootstrap_address, self.port), + lifetime, + ctx, + self.hostname, + ) as ts: + cert = await ts.getpeercert(dns.query._remaining(expiration)) + return self.ddr_check_certificate(cert) + + +def _extract_nameservers_from_svcb(answer): + bootstrap_address = answer.nameserver + if not dns.inet.is_address(bootstrap_address): + return [] + infos = [] + for rr in answer.rrset.processing_order(): + nameservers = [] + param = rr.params.get(dns.rdtypes.svcbbase.ParamKey.ALPN) + if param is None: + continue + alpns = set(param.ids) + host = rr.target.to_text(omit_final_dot=True) + port = None + param = rr.params.get(dns.rdtypes.svcbbase.ParamKey.PORT) + if param is not None: + port = param.port + # For now we ignore address hints and address resolution and always use the + # bootstrap address + if b"h2" in alpns: + param = rr.params.get(dns.rdtypes.svcbbase.ParamKey.DOHPATH) + if param is None or not param.value.endswith(b"{?dns}"): + continue + path = param.value[:-6].decode() + if not path.startswith("/"): + path = "/" + path + if port is None: + port = 443 + url = f"https://{host}:{port}{path}" + # check the URL + try: + urlparse(url) + nameservers.append(dns.nameserver.DoHNameserver(url, bootstrap_address)) + except Exception: + # continue processing other ALPN types + pass + if b"dot" in alpns: + if port is None: + port = 853 + nameservers.append( + dns.nameserver.DoTNameserver(bootstrap_address, port, host) + ) + if b"doq" in alpns: + if port is None: + port = 853 + nameservers.append( + dns.nameserver.DoQNameserver(bootstrap_address, port, True, host) + ) + if len(nameservers) > 0: + infos.append(_SVCBInfo(bootstrap_address, port, host, nameservers)) + return infos + + +def _get_nameservers_sync(answer, lifetime): + """Return a list of TLS-validated resolver nameservers extracted from an SVCB + answer.""" + nameservers = [] + infos = _extract_nameservers_from_svcb(answer) + for info in infos: + try: + if info.ddr_tls_check_sync(lifetime): + nameservers.extend(info.nameservers) + except Exception: + pass + return nameservers + + +async def _get_nameservers_async(answer, lifetime): + """Return a list of TLS-validated resolver nameservers extracted from an SVCB + answer.""" + nameservers = [] + infos = _extract_nameservers_from_svcb(answer) + for info in infos: + try: + if await info.ddr_tls_check_async(lifetime): + nameservers.extend(info.nameservers) + except Exception: + pass + return nameservers diff --git a/.venv/lib/python3.9/site-packages/dns/_features.py b/.venv/lib/python3.9/site-packages/dns/_features.py new file mode 100644 index 0000000..fa6d495 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/_features.py @@ -0,0 +1,95 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import importlib.metadata +import itertools +import string +from typing import Dict, List, Tuple + + +def _tuple_from_text(version: str) -> Tuple: + text_parts = version.split(".") + int_parts = [] + for text_part in text_parts: + digit_prefix = "".join( + itertools.takewhile(lambda x: x in string.digits, text_part) + ) + try: + int_parts.append(int(digit_prefix)) + except Exception: + break + return tuple(int_parts) + + +def _version_check( + requirement: str, +) -> bool: + """Is the requirement fulfilled? + + The requirement must be of the form + + package>=version + """ + package, minimum = requirement.split(">=") + try: + version = importlib.metadata.version(package) + # This shouldn't happen, but it apparently can. + if version is None: + return False + except Exception: + return False + t_version = _tuple_from_text(version) + t_minimum = _tuple_from_text(minimum) + if t_version < t_minimum: + return False + return True + + +_cache: Dict[str, bool] = {} + + +def have(feature: str) -> bool: + """Is *feature* available? + + This tests if all optional packages needed for the + feature are available and recent enough. + + Returns ``True`` if the feature is available, + and ``False`` if it is not or if metadata is + missing. + """ + value = _cache.get(feature) + if value is not None: + return value + requirements = _requirements.get(feature) + if requirements is None: + # we make a cache entry here for consistency not performance + _cache[feature] = False + return False + ok = True + for requirement in requirements: + if not _version_check(requirement): + ok = False + break + _cache[feature] = ok + return ok + + +def force(feature: str, enabled: bool) -> None: + """Force the status of *feature* to be *enabled*. + + This method is provided as a workaround for any cases + where importlib.metadata is ineffective, or for testing. + """ + _cache[feature] = enabled + + +_requirements: Dict[str, List[str]] = { + ### BEGIN generated requirements + "dnssec": ["cryptography>=43"], + "doh": ["httpcore>=1.0.0", "httpx>=0.26.0", "h2>=4.1.0"], + "doq": ["aioquic>=1.0.0"], + "idna": ["idna>=3.7"], + "trio": ["trio>=0.23"], + "wmi": ["wmi>=1.5.1"], + ### END generated requirements +} diff --git a/.venv/lib/python3.9/site-packages/dns/_immutable_ctx.py b/.venv/lib/python3.9/site-packages/dns/_immutable_ctx.py new file mode 100644 index 0000000..ae7a33b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/_immutable_ctx.py @@ -0,0 +1,76 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# This implementation of the immutable decorator requires python >= +# 3.7, and is significantly more storage efficient when making classes +# with slots immutable. It's also faster. + +import contextvars +import inspect + +_in__init__ = contextvars.ContextVar("_immutable_in__init__", default=False) + + +class _Immutable: + """Immutable mixin class""" + + # We set slots to the empty list to say "we don't have any attributes". + # We do this so that if we're mixed in with a class with __slots__, we + # don't cause a __dict__ to be added which would waste space. + + __slots__ = () + + def __setattr__(self, name, value): + if _in__init__.get() is not self: + raise TypeError("object doesn't support attribute assignment") + else: + super().__setattr__(name, value) + + def __delattr__(self, name): + if _in__init__.get() is not self: + raise TypeError("object doesn't support attribute assignment") + else: + super().__delattr__(name) + + +def _immutable_init(f): + def nf(*args, **kwargs): + previous = _in__init__.set(args[0]) + try: + # call the actual __init__ + f(*args, **kwargs) + finally: + _in__init__.reset(previous) + + nf.__signature__ = inspect.signature(f) + return nf + + +def immutable(cls): + if _Immutable in cls.__mro__: + # Some ancestor already has the mixin, so just make sure we keep + # following the __init__ protocol. + cls.__init__ = _immutable_init(cls.__init__) + if hasattr(cls, "__setstate__"): + cls.__setstate__ = _immutable_init(cls.__setstate__) + ncls = cls + else: + # Mixin the Immutable class and follow the __init__ protocol. + class ncls(_Immutable, cls): + # We have to do the __slots__ declaration here too! + __slots__ = () + + @_immutable_init + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + if hasattr(cls, "__setstate__"): + + @_immutable_init + def __setstate__(self, *args, **kwargs): + super().__setstate__(*args, **kwargs) + + # make ncls have the same name and module as cls + ncls.__name__ = cls.__name__ + ncls.__qualname__ = cls.__qualname__ + ncls.__module__ = cls.__module__ + return ncls diff --git a/.venv/lib/python3.9/site-packages/dns/_trio_backend.py b/.venv/lib/python3.9/site-packages/dns/_trio_backend.py new file mode 100644 index 0000000..0ed904d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/_trio_backend.py @@ -0,0 +1,253 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +"""trio async I/O library query support""" + +import socket + +import trio +import trio.socket # type: ignore + +import dns._asyncbackend +import dns._features +import dns.exception +import dns.inet + +if not dns._features.have("trio"): + raise ImportError("trio not found or too old") + + +def _maybe_timeout(timeout): + if timeout is not None: + return trio.move_on_after(timeout) + else: + return dns._asyncbackend.NullContext() + + +# for brevity +_lltuple = dns.inet.low_level_address_tuple + +# pylint: disable=redefined-outer-name + + +class DatagramSocket(dns._asyncbackend.DatagramSocket): + def __init__(self, sock): + super().__init__(sock.family, socket.SOCK_DGRAM) + self.socket = sock + + async def sendto(self, what, destination, timeout): + with _maybe_timeout(timeout): + if destination is None: + return await self.socket.send(what) + else: + return await self.socket.sendto(what, destination) + raise dns.exception.Timeout( + timeout=timeout + ) # pragma: no cover lgtm[py/unreachable-statement] + + async def recvfrom(self, size, timeout): + with _maybe_timeout(timeout): + return await self.socket.recvfrom(size) + raise dns.exception.Timeout(timeout=timeout) # lgtm[py/unreachable-statement] + + async def close(self): + self.socket.close() + + async def getpeername(self): + return self.socket.getpeername() + + async def getsockname(self): + return self.socket.getsockname() + + async def getpeercert(self, timeout): + raise NotImplementedError + + +class StreamSocket(dns._asyncbackend.StreamSocket): + def __init__(self, family, stream, tls=False): + super().__init__(family, socket.SOCK_STREAM) + self.stream = stream + self.tls = tls + + async def sendall(self, what, timeout): + with _maybe_timeout(timeout): + return await self.stream.send_all(what) + raise dns.exception.Timeout(timeout=timeout) # lgtm[py/unreachable-statement] + + async def recv(self, size, timeout): + with _maybe_timeout(timeout): + return await self.stream.receive_some(size) + raise dns.exception.Timeout(timeout=timeout) # lgtm[py/unreachable-statement] + + async def close(self): + await self.stream.aclose() + + async def getpeername(self): + if self.tls: + return self.stream.transport_stream.socket.getpeername() + else: + return self.stream.socket.getpeername() + + async def getsockname(self): + if self.tls: + return self.stream.transport_stream.socket.getsockname() + else: + return self.stream.socket.getsockname() + + async def getpeercert(self, timeout): + if self.tls: + with _maybe_timeout(timeout): + await self.stream.do_handshake() + return self.stream.getpeercert() + else: + raise NotImplementedError + + +if dns._features.have("doh"): + import httpcore + import httpcore._backends.trio + import httpx + + _CoreAsyncNetworkBackend = httpcore.AsyncNetworkBackend + _CoreTrioStream = httpcore._backends.trio.TrioStream + + from dns.query import _compute_times, _expiration_for_this_attempt, _remaining + + class _NetworkBackend(_CoreAsyncNetworkBackend): + def __init__(self, resolver, local_port, bootstrap_address, family): + super().__init__() + self._local_port = local_port + self._resolver = resolver + self._bootstrap_address = bootstrap_address + self._family = family + + async def connect_tcp( + self, host, port, timeout, local_address, socket_options=None + ): # pylint: disable=signature-differs + addresses = [] + _, expiration = _compute_times(timeout) + if dns.inet.is_address(host): + addresses.append(host) + elif self._bootstrap_address is not None: + addresses.append(self._bootstrap_address) + else: + timeout = _remaining(expiration) + family = self._family + if local_address: + family = dns.inet.af_for_address(local_address) + answers = await self._resolver.resolve_name( + host, family=family, lifetime=timeout + ) + addresses = answers.addresses() + for address in addresses: + try: + af = dns.inet.af_for_address(address) + if local_address is not None or self._local_port != 0: + source = (local_address, self._local_port) + else: + source = None + destination = (address, port) + attempt_expiration = _expiration_for_this_attempt(2.0, expiration) + timeout = _remaining(attempt_expiration) + sock = await Backend().make_socket( + af, socket.SOCK_STREAM, 0, source, destination, timeout + ) + return _CoreTrioStream(sock.stream) + except Exception: + continue + raise httpcore.ConnectError + + async def connect_unix_socket( + self, path, timeout, socket_options=None + ): # pylint: disable=signature-differs + raise NotImplementedError + + async def sleep(self, seconds): # pylint: disable=signature-differs + await trio.sleep(seconds) + + class _HTTPTransport(httpx.AsyncHTTPTransport): + def __init__( + self, + *args, + local_port=0, + bootstrap_address=None, + resolver=None, + family=socket.AF_UNSPEC, + **kwargs, + ): + if resolver is None and bootstrap_address is None: + # pylint: disable=import-outside-toplevel,redefined-outer-name + import dns.asyncresolver + + resolver = dns.asyncresolver.Resolver() + super().__init__(*args, **kwargs) + self._pool._network_backend = _NetworkBackend( + resolver, local_port, bootstrap_address, family + ) + +else: + _HTTPTransport = dns._asyncbackend.NullTransport # type: ignore + + +class Backend(dns._asyncbackend.Backend): + def name(self): + return "trio" + + async def make_socket( + self, + af, + socktype, + proto=0, + source=None, + destination=None, + timeout=None, + ssl_context=None, + server_hostname=None, + ): + s = trio.socket.socket(af, socktype, proto) + stream = None + try: + if source: + await s.bind(_lltuple(source, af)) + if socktype == socket.SOCK_STREAM or destination is not None: + connected = False + with _maybe_timeout(timeout): + await s.connect(_lltuple(destination, af)) + connected = True + if not connected: + raise dns.exception.Timeout( + timeout=timeout + ) # lgtm[py/unreachable-statement] + except Exception: # pragma: no cover + s.close() + raise + if socktype == socket.SOCK_DGRAM: + return DatagramSocket(s) + elif socktype == socket.SOCK_STREAM: + stream = trio.SocketStream(s) + tls = False + if ssl_context: + tls = True + try: + stream = trio.SSLStream( + stream, ssl_context, server_hostname=server_hostname + ) + except Exception: # pragma: no cover + await stream.aclose() + raise + return StreamSocket(af, stream, tls) + raise NotImplementedError( + "unsupported socket " + f"type {socktype}" + ) # pragma: no cover + + async def sleep(self, interval): + await trio.sleep(interval) + + def get_transport_class(self): + return _HTTPTransport + + async def wait_for(self, awaitable, timeout): + with _maybe_timeout(timeout): + return await awaitable + raise dns.exception.Timeout( + timeout=timeout + ) # pragma: no cover lgtm[py/unreachable-statement] diff --git a/.venv/lib/python3.9/site-packages/dns/asyncbackend.py b/.venv/lib/python3.9/site-packages/dns/asyncbackend.py new file mode 100644 index 0000000..0ec58b0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/asyncbackend.py @@ -0,0 +1,101 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +from typing import Dict + +import dns.exception + +# pylint: disable=unused-import +from dns._asyncbackend import ( # noqa: F401 lgtm[py/unused-import] + Backend, + DatagramSocket, + Socket, + StreamSocket, +) + +# pylint: enable=unused-import + +_default_backend = None + +_backends: Dict[str, Backend] = {} + +# Allow sniffio import to be disabled for testing purposes +_no_sniffio = False + + +class AsyncLibraryNotFoundError(dns.exception.DNSException): + pass + + +def get_backend(name: str) -> Backend: + """Get the specified asynchronous backend. + + *name*, a ``str``, the name of the backend. Currently the "trio" + and "asyncio" backends are available. + + Raises NotImplementedError if an unknown backend name is specified. + """ + # pylint: disable=import-outside-toplevel,redefined-outer-name + backend = _backends.get(name) + if backend: + return backend + if name == "trio": + import dns._trio_backend + + backend = dns._trio_backend.Backend() + elif name == "asyncio": + import dns._asyncio_backend + + backend = dns._asyncio_backend.Backend() + else: + raise NotImplementedError(f"unimplemented async backend {name}") + _backends[name] = backend + return backend + + +def sniff() -> str: + """Attempt to determine the in-use asynchronous I/O library by using + the ``sniffio`` module if it is available. + + Returns the name of the library, or raises AsyncLibraryNotFoundError + if the library cannot be determined. + """ + # pylint: disable=import-outside-toplevel + try: + if _no_sniffio: + raise ImportError + import sniffio + + try: + return sniffio.current_async_library() + except sniffio.AsyncLibraryNotFoundError: + raise AsyncLibraryNotFoundError("sniffio cannot determine async library") + except ImportError: + import asyncio + + try: + asyncio.get_running_loop() + return "asyncio" + except RuntimeError: + raise AsyncLibraryNotFoundError("no async library detected") + + +def get_default_backend() -> Backend: + """Get the default backend, initializing it if necessary.""" + if _default_backend: + return _default_backend + + return set_default_backend(sniff()) + + +def set_default_backend(name: str) -> Backend: + """Set the default backend. + + It's not normally necessary to call this method, as + ``get_default_backend()`` will initialize the backend + appropriately in many cases. If ``sniffio`` is not installed, or + in testing situations, this function allows the backend to be set + explicitly. + """ + global _default_backend + _default_backend = get_backend(name) + return _default_backend diff --git a/.venv/lib/python3.9/site-packages/dns/asyncquery.py b/.venv/lib/python3.9/site-packages/dns/asyncquery.py new file mode 100644 index 0000000..efad0fd --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/asyncquery.py @@ -0,0 +1,913 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Talk to a DNS server.""" + +import base64 +import contextlib +import random +import socket +import struct +import time +import urllib.parse +from typing import Any, Dict, Optional, Tuple, Union, cast + +import dns.asyncbackend +import dns.exception +import dns.inet +import dns.message +import dns.name +import dns.quic +import dns.rcode +import dns.rdataclass +import dns.rdatatype +import dns.transaction +from dns._asyncbackend import NullContext +from dns.query import ( + BadResponse, + HTTPVersion, + NoDOH, + NoDOQ, + UDPMode, + _check_status, + _compute_times, + _make_dot_ssl_context, + _matches_destination, + _remaining, + have_doh, + ssl, +) + +if have_doh: + import httpx + +# for brevity +_lltuple = dns.inet.low_level_address_tuple + + +def _source_tuple(af, address, port): + # Make a high level source tuple, or return None if address and port + # are both None + if address or port: + if address is None: + if af == socket.AF_INET: + address = "0.0.0.0" + elif af == socket.AF_INET6: + address = "::" + else: + raise NotImplementedError(f"unknown address family {af}") + return (address, port) + else: + return None + + +def _timeout(expiration, now=None): + if expiration is not None: + if not now: + now = time.time() + return max(expiration - now, 0) + else: + return None + + +async def send_udp( + sock: dns.asyncbackend.DatagramSocket, + what: Union[dns.message.Message, bytes], + destination: Any, + expiration: Optional[float] = None, +) -> Tuple[int, float]: + """Send a DNS message to the specified UDP socket. + + *sock*, a ``dns.asyncbackend.DatagramSocket``. + + *what*, a ``bytes`` or ``dns.message.Message``, the message to send. + + *destination*, a destination tuple appropriate for the address family + of the socket, specifying where to send the query. + + *expiration*, a ``float`` or ``None``, the absolute time at which + a timeout exception should be raised. If ``None``, no timeout will + occur. The expiration value is meaningless for the asyncio backend, as + asyncio's transport sendto() never blocks. + + Returns an ``(int, float)`` tuple of bytes sent and the sent time. + """ + + if isinstance(what, dns.message.Message): + what = what.to_wire() + sent_time = time.time() + n = await sock.sendto(what, destination, _timeout(expiration, sent_time)) + return (n, sent_time) + + +async def receive_udp( + sock: dns.asyncbackend.DatagramSocket, + destination: Optional[Any] = None, + expiration: Optional[float] = None, + ignore_unexpected: bool = False, + one_rr_per_rrset: bool = False, + keyring: Optional[Dict[dns.name.Name, dns.tsig.Key]] = None, + request_mac: Optional[bytes] = b"", + ignore_trailing: bool = False, + raise_on_truncation: bool = False, + ignore_errors: bool = False, + query: Optional[dns.message.Message] = None, +) -> Any: + """Read a DNS message from a UDP socket. + + *sock*, a ``dns.asyncbackend.DatagramSocket``. + + See :py:func:`dns.query.receive_udp()` for the documentation of the other + parameters, and exceptions. + + Returns a ``(dns.message.Message, float, tuple)`` tuple of the received message, the + received time, and the address where the message arrived from. + """ + + wire = b"" + while True: + (wire, from_address) = await sock.recvfrom(65535, _timeout(expiration)) + if not _matches_destination( + sock.family, from_address, destination, ignore_unexpected + ): + continue + received_time = time.time() + try: + r = dns.message.from_wire( + wire, + keyring=keyring, + request_mac=request_mac, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + raise_on_truncation=raise_on_truncation, + ) + except dns.message.Truncated as e: + # See the comment in query.py for details. + if ( + ignore_errors + and query is not None + and not query.is_response(e.message()) + ): + continue + else: + raise + except Exception: + if ignore_errors: + continue + else: + raise + if ignore_errors and query is not None and not query.is_response(r): + continue + return (r, received_time, from_address) + + +async def udp( + q: dns.message.Message, + where: str, + timeout: Optional[float] = None, + port: int = 53, + source: Optional[str] = None, + source_port: int = 0, + ignore_unexpected: bool = False, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + raise_on_truncation: bool = False, + sock: Optional[dns.asyncbackend.DatagramSocket] = None, + backend: Optional[dns.asyncbackend.Backend] = None, + ignore_errors: bool = False, +) -> dns.message.Message: + """Return the response obtained after sending a query via UDP. + + *sock*, a ``dns.asyncbackend.DatagramSocket``, or ``None``, + the socket to use for the query. If ``None``, the default, a + socket is created. Note that if a socket is provided, the + *source*, *source_port*, and *backend* are ignored. + + *backend*, a ``dns.asyncbackend.Backend``, or ``None``. If ``None``, + the default, then dnspython will use the default backend. + + See :py:func:`dns.query.udp()` for the documentation of the other + parameters, exceptions, and return type of this method. + """ + wire = q.to_wire() + (begin_time, expiration) = _compute_times(timeout) + af = dns.inet.af_for_address(where) + destination = _lltuple((where, port), af) + if sock: + cm: contextlib.AbstractAsyncContextManager = NullContext(sock) + else: + if not backend: + backend = dns.asyncbackend.get_default_backend() + stuple = _source_tuple(af, source, source_port) + if backend.datagram_connection_required(): + dtuple = (where, port) + else: + dtuple = None + cm = await backend.make_socket(af, socket.SOCK_DGRAM, 0, stuple, dtuple) + async with cm as s: + await send_udp(s, wire, destination, expiration) + (r, received_time, _) = await receive_udp( + s, + destination, + expiration, + ignore_unexpected, + one_rr_per_rrset, + q.keyring, + q.mac, + ignore_trailing, + raise_on_truncation, + ignore_errors, + q, + ) + r.time = received_time - begin_time + # We don't need to check q.is_response() if we are in ignore_errors mode + # as receive_udp() will have checked it. + if not (ignore_errors or q.is_response(r)): + raise BadResponse + return r + + +async def udp_with_fallback( + q: dns.message.Message, + where: str, + timeout: Optional[float] = None, + port: int = 53, + source: Optional[str] = None, + source_port: int = 0, + ignore_unexpected: bool = False, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + udp_sock: Optional[dns.asyncbackend.DatagramSocket] = None, + tcp_sock: Optional[dns.asyncbackend.StreamSocket] = None, + backend: Optional[dns.asyncbackend.Backend] = None, + ignore_errors: bool = False, +) -> Tuple[dns.message.Message, bool]: + """Return the response to the query, trying UDP first and falling back + to TCP if UDP results in a truncated response. + + *udp_sock*, a ``dns.asyncbackend.DatagramSocket``, or ``None``, + the socket to use for the UDP query. If ``None``, the default, a + socket is created. Note that if a socket is provided the *source*, + *source_port*, and *backend* are ignored for the UDP query. + + *tcp_sock*, a ``dns.asyncbackend.StreamSocket``, or ``None``, the + socket to use for the TCP query. If ``None``, the default, a + socket is created. Note that if a socket is provided *where*, + *source*, *source_port*, and *backend* are ignored for the TCP query. + + *backend*, a ``dns.asyncbackend.Backend``, or ``None``. If ``None``, + the default, then dnspython will use the default backend. + + See :py:func:`dns.query.udp_with_fallback()` for the documentation + of the other parameters, exceptions, and return type of this + method. + """ + try: + response = await udp( + q, + where, + timeout, + port, + source, + source_port, + ignore_unexpected, + one_rr_per_rrset, + ignore_trailing, + True, + udp_sock, + backend, + ignore_errors, + ) + return (response, False) + except dns.message.Truncated: + response = await tcp( + q, + where, + timeout, + port, + source, + source_port, + one_rr_per_rrset, + ignore_trailing, + tcp_sock, + backend, + ) + return (response, True) + + +async def send_tcp( + sock: dns.asyncbackend.StreamSocket, + what: Union[dns.message.Message, bytes], + expiration: Optional[float] = None, +) -> Tuple[int, float]: + """Send a DNS message to the specified TCP socket. + + *sock*, a ``dns.asyncbackend.StreamSocket``. + + See :py:func:`dns.query.send_tcp()` for the documentation of the other + parameters, exceptions, and return type of this method. + """ + + if isinstance(what, dns.message.Message): + tcpmsg = what.to_wire(prepend_length=True) + else: + # copying the wire into tcpmsg is inefficient, but lets us + # avoid writev() or doing a short write that would get pushed + # onto the net + tcpmsg = len(what).to_bytes(2, "big") + what + sent_time = time.time() + await sock.sendall(tcpmsg, _timeout(expiration, sent_time)) + return (len(tcpmsg), sent_time) + + +async def _read_exactly(sock, count, expiration): + """Read the specified number of bytes from stream. Keep trying until we + either get the desired amount, or we hit EOF. + """ + s = b"" + while count > 0: + n = await sock.recv(count, _timeout(expiration)) + if n == b"": + raise EOFError("EOF") + count = count - len(n) + s = s + n + return s + + +async def receive_tcp( + sock: dns.asyncbackend.StreamSocket, + expiration: Optional[float] = None, + one_rr_per_rrset: bool = False, + keyring: Optional[Dict[dns.name.Name, dns.tsig.Key]] = None, + request_mac: Optional[bytes] = b"", + ignore_trailing: bool = False, +) -> Tuple[dns.message.Message, float]: + """Read a DNS message from a TCP socket. + + *sock*, a ``dns.asyncbackend.StreamSocket``. + + See :py:func:`dns.query.receive_tcp()` for the documentation of the other + parameters, exceptions, and return type of this method. + """ + + ldata = await _read_exactly(sock, 2, expiration) + (l,) = struct.unpack("!H", ldata) + wire = await _read_exactly(sock, l, expiration) + received_time = time.time() + r = dns.message.from_wire( + wire, + keyring=keyring, + request_mac=request_mac, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + ) + return (r, received_time) + + +async def tcp( + q: dns.message.Message, + where: str, + timeout: Optional[float] = None, + port: int = 53, + source: Optional[str] = None, + source_port: int = 0, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + sock: Optional[dns.asyncbackend.StreamSocket] = None, + backend: Optional[dns.asyncbackend.Backend] = None, +) -> dns.message.Message: + """Return the response obtained after sending a query via TCP. + + *sock*, a ``dns.asyncbacket.StreamSocket``, or ``None``, the + socket to use for the query. If ``None``, the default, a socket + is created. Note that if a socket is provided + *where*, *port*, *source*, *source_port*, and *backend* are ignored. + + *backend*, a ``dns.asyncbackend.Backend``, or ``None``. If ``None``, + the default, then dnspython will use the default backend. + + See :py:func:`dns.query.tcp()` for the documentation of the other + parameters, exceptions, and return type of this method. + """ + + wire = q.to_wire() + (begin_time, expiration) = _compute_times(timeout) + if sock: + # Verify that the socket is connected, as if it's not connected, + # it's not writable, and the polling in send_tcp() will time out or + # hang forever. + await sock.getpeername() + cm: contextlib.AbstractAsyncContextManager = NullContext(sock) + else: + # These are simple (address, port) pairs, not family-dependent tuples + # you pass to low-level socket code. + af = dns.inet.af_for_address(where) + stuple = _source_tuple(af, source, source_port) + dtuple = (where, port) + if not backend: + backend = dns.asyncbackend.get_default_backend() + cm = await backend.make_socket( + af, socket.SOCK_STREAM, 0, stuple, dtuple, timeout + ) + async with cm as s: + await send_tcp(s, wire, expiration) + (r, received_time) = await receive_tcp( + s, expiration, one_rr_per_rrset, q.keyring, q.mac, ignore_trailing + ) + r.time = received_time - begin_time + if not q.is_response(r): + raise BadResponse + return r + + +async def tls( + q: dns.message.Message, + where: str, + timeout: Optional[float] = None, + port: int = 853, + source: Optional[str] = None, + source_port: int = 0, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + sock: Optional[dns.asyncbackend.StreamSocket] = None, + backend: Optional[dns.asyncbackend.Backend] = None, + ssl_context: Optional[ssl.SSLContext] = None, + server_hostname: Optional[str] = None, + verify: Union[bool, str] = True, +) -> dns.message.Message: + """Return the response obtained after sending a query via TLS. + + *sock*, an ``asyncbackend.StreamSocket``, or ``None``, the socket + to use for the query. If ``None``, the default, a socket is + created. Note that if a socket is provided, it must be a + connected SSL stream socket, and *where*, *port*, + *source*, *source_port*, *backend*, *ssl_context*, and *server_hostname* + are ignored. + + *backend*, a ``dns.asyncbackend.Backend``, or ``None``. If ``None``, + the default, then dnspython will use the default backend. + + See :py:func:`dns.query.tls()` for the documentation of the other + parameters, exceptions, and return type of this method. + """ + (begin_time, expiration) = _compute_times(timeout) + if sock: + cm: contextlib.AbstractAsyncContextManager = NullContext(sock) + else: + if ssl_context is None: + ssl_context = _make_dot_ssl_context(server_hostname, verify) + af = dns.inet.af_for_address(where) + stuple = _source_tuple(af, source, source_port) + dtuple = (where, port) + if not backend: + backend = dns.asyncbackend.get_default_backend() + cm = await backend.make_socket( + af, + socket.SOCK_STREAM, + 0, + stuple, + dtuple, + timeout, + ssl_context, + server_hostname, + ) + async with cm as s: + timeout = _timeout(expiration) + response = await tcp( + q, + where, + timeout, + port, + source, + source_port, + one_rr_per_rrset, + ignore_trailing, + s, + backend, + ) + end_time = time.time() + response.time = end_time - begin_time + return response + + +def _maybe_get_resolver( + resolver: Optional["dns.asyncresolver.Resolver"], +) -> "dns.asyncresolver.Resolver": + # We need a separate method for this to avoid overriding the global + # variable "dns" with the as-yet undefined local variable "dns" + # in https(). + if resolver is None: + # pylint: disable=import-outside-toplevel,redefined-outer-name + import dns.asyncresolver + + resolver = dns.asyncresolver.Resolver() + return resolver + + +async def https( + q: dns.message.Message, + where: str, + timeout: Optional[float] = None, + port: int = 443, + source: Optional[str] = None, + source_port: int = 0, # pylint: disable=W0613 + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + client: Optional["httpx.AsyncClient"] = None, + path: str = "/dns-query", + post: bool = True, + verify: Union[bool, str] = True, + bootstrap_address: Optional[str] = None, + resolver: Optional["dns.asyncresolver.Resolver"] = None, + family: int = socket.AF_UNSPEC, + http_version: HTTPVersion = HTTPVersion.DEFAULT, +) -> dns.message.Message: + """Return the response obtained after sending a query via DNS-over-HTTPS. + + *client*, a ``httpx.AsyncClient``. If provided, the client to use for + the query. + + Unlike the other dnspython async functions, a backend cannot be provided + in this function because httpx always auto-detects the async backend. + + See :py:func:`dns.query.https()` for the documentation of the other + parameters, exceptions, and return type of this method. + """ + + try: + af = dns.inet.af_for_address(where) + except ValueError: + af = None + if af is not None and dns.inet.is_address(where): + if af == socket.AF_INET: + url = f"https://{where}:{port}{path}" + elif af == socket.AF_INET6: + url = f"https://[{where}]:{port}{path}" + else: + url = where + + extensions = {} + if bootstrap_address is None: + # pylint: disable=possibly-used-before-assignment + parsed = urllib.parse.urlparse(url) + if parsed.hostname is None: + raise ValueError("no hostname in URL") + if dns.inet.is_address(parsed.hostname): + bootstrap_address = parsed.hostname + extensions["sni_hostname"] = parsed.hostname + if parsed.port is not None: + port = parsed.port + + if http_version == HTTPVersion.H3 or ( + http_version == HTTPVersion.DEFAULT and not have_doh + ): + if bootstrap_address is None: + resolver = _maybe_get_resolver(resolver) + assert parsed.hostname is not None # for mypy + answers = await resolver.resolve_name(parsed.hostname, family) + bootstrap_address = random.choice(list(answers.addresses())) + return await _http3( + q, + bootstrap_address, + url, + timeout, + port, + source, + source_port, + one_rr_per_rrset, + ignore_trailing, + verify=verify, + post=post, + ) + + if not have_doh: + raise NoDOH # pragma: no cover + # pylint: disable=possibly-used-before-assignment + if client and not isinstance(client, httpx.AsyncClient): + raise ValueError("session parameter must be an httpx.AsyncClient") + # pylint: enable=possibly-used-before-assignment + + wire = q.to_wire() + headers = {"accept": "application/dns-message"} + + h1 = http_version in (HTTPVersion.H1, HTTPVersion.DEFAULT) + h2 = http_version in (HTTPVersion.H2, HTTPVersion.DEFAULT) + + backend = dns.asyncbackend.get_default_backend() + + if source is None: + local_address = None + local_port = 0 + else: + local_address = source + local_port = source_port + + if client: + cm: contextlib.AbstractAsyncContextManager = NullContext(client) + else: + transport = backend.get_transport_class()( + local_address=local_address, + http1=h1, + http2=h2, + verify=verify, + local_port=local_port, + bootstrap_address=bootstrap_address, + resolver=resolver, + family=family, + ) + + cm = httpx.AsyncClient(http1=h1, http2=h2, verify=verify, transport=transport) + + async with cm as the_client: + # see https://tools.ietf.org/html/rfc8484#section-4.1.1 for DoH + # GET and POST examples + if post: + headers.update( + { + "content-type": "application/dns-message", + "content-length": str(len(wire)), + } + ) + response = await backend.wait_for( + the_client.post( + url, + headers=headers, + content=wire, + extensions=extensions, + ), + timeout, + ) + else: + wire = base64.urlsafe_b64encode(wire).rstrip(b"=") + twire = wire.decode() # httpx does a repr() if we give it bytes + response = await backend.wait_for( + the_client.get( + url, + headers=headers, + params={"dns": twire}, + extensions=extensions, + ), + timeout, + ) + + # see https://tools.ietf.org/html/rfc8484#section-4.2.1 for info about DoH + # status codes + if response.status_code < 200 or response.status_code > 299: + raise ValueError( + f"{where} responded with status code {response.status_code}" + f"\nResponse body: {response.content!r}" + ) + r = dns.message.from_wire( + response.content, + keyring=q.keyring, + request_mac=q.request_mac, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + ) + r.time = response.elapsed.total_seconds() + if not q.is_response(r): + raise BadResponse + return r + + +async def _http3( + q: dns.message.Message, + where: str, + url: str, + timeout: Optional[float] = None, + port: int = 853, + source: Optional[str] = None, + source_port: int = 0, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + verify: Union[bool, str] = True, + backend: Optional[dns.asyncbackend.Backend] = None, + hostname: Optional[str] = None, + post: bool = True, +) -> dns.message.Message: + if not dns.quic.have_quic: + raise NoDOH("DNS-over-HTTP3 is not available.") # pragma: no cover + + url_parts = urllib.parse.urlparse(url) + hostname = url_parts.hostname + if url_parts.port is not None: + port = url_parts.port + + q.id = 0 + wire = q.to_wire() + (cfactory, mfactory) = dns.quic.factories_for_backend(backend) + + async with cfactory() as context: + async with mfactory( + context, verify_mode=verify, server_name=hostname, h3=True + ) as the_manager: + the_connection = the_manager.connect(where, port, source, source_port) + (start, expiration) = _compute_times(timeout) + stream = await the_connection.make_stream(timeout) + async with stream: + # note that send_h3() does not need await + stream.send_h3(url, wire, post) + wire = await stream.receive(_remaining(expiration)) + _check_status(stream.headers(), where, wire) + finish = time.time() + r = dns.message.from_wire( + wire, + keyring=q.keyring, + request_mac=q.request_mac, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + ) + r.time = max(finish - start, 0.0) + if not q.is_response(r): + raise BadResponse + return r + + +async def quic( + q: dns.message.Message, + where: str, + timeout: Optional[float] = None, + port: int = 853, + source: Optional[str] = None, + source_port: int = 0, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + connection: Optional[dns.quic.AsyncQuicConnection] = None, + verify: Union[bool, str] = True, + backend: Optional[dns.asyncbackend.Backend] = None, + hostname: Optional[str] = None, + server_hostname: Optional[str] = None, +) -> dns.message.Message: + """Return the response obtained after sending an asynchronous query via + DNS-over-QUIC. + + *backend*, a ``dns.asyncbackend.Backend``, or ``None``. If ``None``, + the default, then dnspython will use the default backend. + + See :py:func:`dns.query.quic()` for the documentation of the other + parameters, exceptions, and return type of this method. + """ + + if not dns.quic.have_quic: + raise NoDOQ("DNS-over-QUIC is not available.") # pragma: no cover + + if server_hostname is not None and hostname is None: + hostname = server_hostname + + q.id = 0 + wire = q.to_wire() + the_connection: dns.quic.AsyncQuicConnection + if connection: + cfactory = dns.quic.null_factory + mfactory = dns.quic.null_factory + the_connection = connection + else: + (cfactory, mfactory) = dns.quic.factories_for_backend(backend) + + async with cfactory() as context: + async with mfactory( + context, + verify_mode=verify, + server_name=server_hostname, + ) as the_manager: + if not connection: + the_connection = the_manager.connect(where, port, source, source_port) + (start, expiration) = _compute_times(timeout) + stream = await the_connection.make_stream(timeout) + async with stream: + await stream.send(wire, True) + wire = await stream.receive(_remaining(expiration)) + finish = time.time() + r = dns.message.from_wire( + wire, + keyring=q.keyring, + request_mac=q.request_mac, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + ) + r.time = max(finish - start, 0.0) + if not q.is_response(r): + raise BadResponse + return r + + +async def _inbound_xfr( + txn_manager: dns.transaction.TransactionManager, + s: dns.asyncbackend.Socket, + query: dns.message.Message, + serial: Optional[int], + timeout: Optional[float], + expiration: float, +) -> Any: + """Given a socket, does the zone transfer.""" + rdtype = query.question[0].rdtype + is_ixfr = rdtype == dns.rdatatype.IXFR + origin = txn_manager.from_wire_origin() + wire = query.to_wire() + is_udp = s.type == socket.SOCK_DGRAM + if is_udp: + udp_sock = cast(dns.asyncbackend.DatagramSocket, s) + await udp_sock.sendto(wire, None, _timeout(expiration)) + else: + tcp_sock = cast(dns.asyncbackend.StreamSocket, s) + tcpmsg = struct.pack("!H", len(wire)) + wire + await tcp_sock.sendall(tcpmsg, expiration) + with dns.xfr.Inbound(txn_manager, rdtype, serial, is_udp) as inbound: + done = False + tsig_ctx = None + while not done: + (_, mexpiration) = _compute_times(timeout) + if mexpiration is None or ( + expiration is not None and mexpiration > expiration + ): + mexpiration = expiration + if is_udp: + timeout = _timeout(mexpiration) + (rwire, _) = await udp_sock.recvfrom(65535, timeout) + else: + ldata = await _read_exactly(tcp_sock, 2, mexpiration) + (l,) = struct.unpack("!H", ldata) + rwire = await _read_exactly(tcp_sock, l, mexpiration) + r = dns.message.from_wire( + rwire, + keyring=query.keyring, + request_mac=query.mac, + xfr=True, + origin=origin, + tsig_ctx=tsig_ctx, + multi=(not is_udp), + one_rr_per_rrset=is_ixfr, + ) + done = inbound.process_message(r) + yield r + tsig_ctx = r.tsig_ctx + if query.keyring and not r.had_tsig: + raise dns.exception.FormError("missing TSIG") + + +async def inbound_xfr( + where: str, + txn_manager: dns.transaction.TransactionManager, + query: Optional[dns.message.Message] = None, + port: int = 53, + timeout: Optional[float] = None, + lifetime: Optional[float] = None, + source: Optional[str] = None, + source_port: int = 0, + udp_mode: UDPMode = UDPMode.NEVER, + backend: Optional[dns.asyncbackend.Backend] = None, +) -> None: + """Conduct an inbound transfer and apply it via a transaction from the + txn_manager. + + *backend*, a ``dns.asyncbackend.Backend``, or ``None``. If ``None``, + the default, then dnspython will use the default backend. + + See :py:func:`dns.query.inbound_xfr()` for the documentation of + the other parameters, exceptions, and return type of this method. + """ + if query is None: + (query, serial) = dns.xfr.make_query(txn_manager) + else: + serial = dns.xfr.extract_serial_from_query(query) + af = dns.inet.af_for_address(where) + stuple = _source_tuple(af, source, source_port) + dtuple = (where, port) + if not backend: + backend = dns.asyncbackend.get_default_backend() + (_, expiration) = _compute_times(lifetime) + if query.question[0].rdtype == dns.rdatatype.IXFR and udp_mode != UDPMode.NEVER: + s = await backend.make_socket( + af, socket.SOCK_DGRAM, 0, stuple, dtuple, _timeout(expiration) + ) + async with s: + try: + async for _ in _inbound_xfr( + txn_manager, s, query, serial, timeout, expiration + ): + pass + return + except dns.xfr.UseTCP: + if udp_mode == UDPMode.ONLY: + raise + + s = await backend.make_socket( + af, socket.SOCK_STREAM, 0, stuple, dtuple, _timeout(expiration) + ) + async with s: + async for _ in _inbound_xfr(txn_manager, s, query, serial, timeout, expiration): + pass diff --git a/.venv/lib/python3.9/site-packages/dns/asyncresolver.py b/.venv/lib/python3.9/site-packages/dns/asyncresolver.py new file mode 100644 index 0000000..8f5e062 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/asyncresolver.py @@ -0,0 +1,475 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Asynchronous DNS stub resolver.""" + +import socket +import time +from typing import Any, Dict, List, Optional, Union + +import dns._ddr +import dns.asyncbackend +import dns.asyncquery +import dns.exception +import dns.name +import dns.query +import dns.rdataclass +import dns.rdatatype +import dns.resolver # lgtm[py/import-and-import-from] + +# import some resolver symbols for brevity +from dns.resolver import NXDOMAIN, NoAnswer, NoRootSOA, NotAbsolute + +# for indentation purposes below +_udp = dns.asyncquery.udp +_tcp = dns.asyncquery.tcp + + +class Resolver(dns.resolver.BaseResolver): + """Asynchronous DNS stub resolver.""" + + async def resolve( + self, + qname: Union[dns.name.Name, str], + rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, + rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, + tcp: bool = False, + source: Optional[str] = None, + raise_on_no_answer: bool = True, + source_port: int = 0, + lifetime: Optional[float] = None, + search: Optional[bool] = None, + backend: Optional[dns.asyncbackend.Backend] = None, + ) -> dns.resolver.Answer: + """Query nameservers asynchronously to find the answer to the question. + + *backend*, a ``dns.asyncbackend.Backend``, or ``None``. If ``None``, + the default, then dnspython will use the default backend. + + See :py:func:`dns.resolver.Resolver.resolve()` for the + documentation of the other parameters, exceptions, and return + type of this method. + """ + + resolution = dns.resolver._Resolution( + self, qname, rdtype, rdclass, tcp, raise_on_no_answer, search + ) + if not backend: + backend = dns.asyncbackend.get_default_backend() + start = time.time() + while True: + (request, answer) = resolution.next_request() + # Note we need to say "if answer is not None" and not just + # "if answer" because answer implements __len__, and python + # will call that. We want to return if we have an answer + # object, including in cases where its length is 0. + if answer is not None: + # cache hit! + return answer + assert request is not None # needed for type checking + done = False + while not done: + (nameserver, tcp, backoff) = resolution.next_nameserver() + if backoff: + await backend.sleep(backoff) + timeout = self._compute_timeout(start, lifetime, resolution.errors) + try: + response = await nameserver.async_query( + request, + timeout=timeout, + source=source, + source_port=source_port, + max_size=tcp, + backend=backend, + ) + except Exception as ex: + (_, done) = resolution.query_result(None, ex) + continue + (answer, done) = resolution.query_result(response, None) + # Note we need to say "if answer is not None" and not just + # "if answer" because answer implements __len__, and python + # will call that. We want to return if we have an answer + # object, including in cases where its length is 0. + if answer is not None: + return answer + + async def resolve_address( + self, ipaddr: str, *args: Any, **kwargs: Any + ) -> dns.resolver.Answer: + """Use an asynchronous resolver to run a reverse query for PTR + records. + + This utilizes the resolve() method to perform a PTR lookup on the + specified IP address. + + *ipaddr*, a ``str``, the IPv4 or IPv6 address you want to get + the PTR record for. + + All other arguments that can be passed to the resolve() function + except for rdtype and rdclass are also supported by this + function. + + """ + # We make a modified kwargs for type checking happiness, as otherwise + # we get a legit warning about possibly having rdtype and rdclass + # in the kwargs more than once. + modified_kwargs: Dict[str, Any] = {} + modified_kwargs.update(kwargs) + modified_kwargs["rdtype"] = dns.rdatatype.PTR + modified_kwargs["rdclass"] = dns.rdataclass.IN + return await self.resolve( + dns.reversename.from_address(ipaddr), *args, **modified_kwargs + ) + + async def resolve_name( + self, + name: Union[dns.name.Name, str], + family: int = socket.AF_UNSPEC, + **kwargs: Any, + ) -> dns.resolver.HostAnswers: + """Use an asynchronous resolver to query for address records. + + This utilizes the resolve() method to perform A and/or AAAA lookups on + the specified name. + + *qname*, a ``dns.name.Name`` or ``str``, the name to resolve. + + *family*, an ``int``, the address family. If socket.AF_UNSPEC + (the default), both A and AAAA records will be retrieved. + + All other arguments that can be passed to the resolve() function + except for rdtype and rdclass are also supported by this + function. + """ + # We make a modified kwargs for type checking happiness, as otherwise + # we get a legit warning about possibly having rdtype and rdclass + # in the kwargs more than once. + modified_kwargs: Dict[str, Any] = {} + modified_kwargs.update(kwargs) + modified_kwargs.pop("rdtype", None) + modified_kwargs["rdclass"] = dns.rdataclass.IN + + if family == socket.AF_INET: + v4 = await self.resolve(name, dns.rdatatype.A, **modified_kwargs) + return dns.resolver.HostAnswers.make(v4=v4) + elif family == socket.AF_INET6: + v6 = await self.resolve(name, dns.rdatatype.AAAA, **modified_kwargs) + return dns.resolver.HostAnswers.make(v6=v6) + elif family != socket.AF_UNSPEC: + raise NotImplementedError(f"unknown address family {family}") + + raise_on_no_answer = modified_kwargs.pop("raise_on_no_answer", True) + lifetime = modified_kwargs.pop("lifetime", None) + start = time.time() + v6 = await self.resolve( + name, + dns.rdatatype.AAAA, + raise_on_no_answer=False, + lifetime=self._compute_timeout(start, lifetime), + **modified_kwargs, + ) + # Note that setting name ensures we query the same name + # for A as we did for AAAA. (This is just in case search lists + # are active by default in the resolver configuration and + # we might be talking to a server that says NXDOMAIN when it + # wants to say NOERROR no data. + name = v6.qname + v4 = await self.resolve( + name, + dns.rdatatype.A, + raise_on_no_answer=False, + lifetime=self._compute_timeout(start, lifetime), + **modified_kwargs, + ) + answers = dns.resolver.HostAnswers.make( + v6=v6, v4=v4, add_empty=not raise_on_no_answer + ) + if not answers: + raise NoAnswer(response=v6.response) + return answers + + # pylint: disable=redefined-outer-name + + async def canonical_name(self, name: Union[dns.name.Name, str]) -> dns.name.Name: + """Determine the canonical name of *name*. + + The canonical name is the name the resolver uses for queries + after all CNAME and DNAME renamings have been applied. + + *name*, a ``dns.name.Name`` or ``str``, the query name. + + This method can raise any exception that ``resolve()`` can + raise, other than ``dns.resolver.NoAnswer`` and + ``dns.resolver.NXDOMAIN``. + + Returns a ``dns.name.Name``. + """ + try: + answer = await self.resolve(name, raise_on_no_answer=False) + canonical_name = answer.canonical_name + except dns.resolver.NXDOMAIN as e: + canonical_name = e.canonical_name + return canonical_name + + async def try_ddr(self, lifetime: float = 5.0) -> None: + """Try to update the resolver's nameservers using Discovery of Designated + Resolvers (DDR). If successful, the resolver will subsequently use + DNS-over-HTTPS or DNS-over-TLS for future queries. + + *lifetime*, a float, is the maximum time to spend attempting DDR. The default + is 5 seconds. + + If the SVCB query is successful and results in a non-empty list of nameservers, + then the resolver's nameservers are set to the returned servers in priority + order. + + The current implementation does not use any address hints from the SVCB record, + nor does it resolve addresses for the SCVB target name, rather it assumes that + the bootstrap nameserver will always be one of the addresses and uses it. + A future revision to the code may offer fuller support. The code verifies that + the bootstrap nameserver is in the Subject Alternative Name field of the + TLS certficate. + """ + try: + expiration = time.time() + lifetime + answer = await self.resolve( + dns._ddr._local_resolver_name, "svcb", lifetime=lifetime + ) + timeout = dns.query._remaining(expiration) + nameservers = await dns._ddr._get_nameservers_async(answer, timeout) + if len(nameservers) > 0: + self.nameservers = nameservers + except Exception: + pass + + +default_resolver = None + + +def get_default_resolver() -> Resolver: + """Get the default asynchronous resolver, initializing it if necessary.""" + if default_resolver is None: + reset_default_resolver() + assert default_resolver is not None + return default_resolver + + +def reset_default_resolver() -> None: + """Re-initialize default asynchronous resolver. + + Note that the resolver configuration (i.e. /etc/resolv.conf on UNIX + systems) will be re-read immediately. + """ + + global default_resolver + default_resolver = Resolver() + + +async def resolve( + qname: Union[dns.name.Name, str], + rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, + rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, + tcp: bool = False, + source: Optional[str] = None, + raise_on_no_answer: bool = True, + source_port: int = 0, + lifetime: Optional[float] = None, + search: Optional[bool] = None, + backend: Optional[dns.asyncbackend.Backend] = None, +) -> dns.resolver.Answer: + """Query nameservers asynchronously to find the answer to the question. + + This is a convenience function that uses the default resolver + object to make the query. + + See :py:func:`dns.asyncresolver.Resolver.resolve` for more + information on the parameters. + """ + + return await get_default_resolver().resolve( + qname, + rdtype, + rdclass, + tcp, + source, + raise_on_no_answer, + source_port, + lifetime, + search, + backend, + ) + + +async def resolve_address( + ipaddr: str, *args: Any, **kwargs: Any +) -> dns.resolver.Answer: + """Use a resolver to run a reverse query for PTR records. + + See :py:func:`dns.asyncresolver.Resolver.resolve_address` for more + information on the parameters. + """ + + return await get_default_resolver().resolve_address(ipaddr, *args, **kwargs) + + +async def resolve_name( + name: Union[dns.name.Name, str], family: int = socket.AF_UNSPEC, **kwargs: Any +) -> dns.resolver.HostAnswers: + """Use a resolver to asynchronously query for address records. + + See :py:func:`dns.asyncresolver.Resolver.resolve_name` for more + information on the parameters. + """ + + return await get_default_resolver().resolve_name(name, family, **kwargs) + + +async def canonical_name(name: Union[dns.name.Name, str]) -> dns.name.Name: + """Determine the canonical name of *name*. + + See :py:func:`dns.resolver.Resolver.canonical_name` for more + information on the parameters and possible exceptions. + """ + + return await get_default_resolver().canonical_name(name) + + +async def try_ddr(timeout: float = 5.0) -> None: + """Try to update the default resolver's nameservers using Discovery of Designated + Resolvers (DDR). If successful, the resolver will subsequently use + DNS-over-HTTPS or DNS-over-TLS for future queries. + + See :py:func:`dns.resolver.Resolver.try_ddr` for more information. + """ + return await get_default_resolver().try_ddr(timeout) + + +async def zone_for_name( + name: Union[dns.name.Name, str], + rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, + tcp: bool = False, + resolver: Optional[Resolver] = None, + backend: Optional[dns.asyncbackend.Backend] = None, +) -> dns.name.Name: + """Find the name of the zone which contains the specified name. + + See :py:func:`dns.resolver.Resolver.zone_for_name` for more + information on the parameters and possible exceptions. + """ + + if isinstance(name, str): + name = dns.name.from_text(name, dns.name.root) + if resolver is None: + resolver = get_default_resolver() + if not name.is_absolute(): + raise NotAbsolute(name) + while True: + try: + answer = await resolver.resolve( + name, dns.rdatatype.SOA, rdclass, tcp, backend=backend + ) + assert answer.rrset is not None + if answer.rrset.name == name: + return name + # otherwise we were CNAMEd or DNAMEd and need to look higher + except (NXDOMAIN, NoAnswer): + pass + try: + name = name.parent() + except dns.name.NoParent: # pragma: no cover + raise NoRootSOA + + +async def make_resolver_at( + where: Union[dns.name.Name, str], + port: int = 53, + family: int = socket.AF_UNSPEC, + resolver: Optional[Resolver] = None, +) -> Resolver: + """Make a stub resolver using the specified destination as the full resolver. + + *where*, a ``dns.name.Name`` or ``str`` the domain name or IP address of the + full resolver. + + *port*, an ``int``, the port to use. If not specified, the default is 53. + + *family*, an ``int``, the address family to use. This parameter is used if + *where* is not an address. The default is ``socket.AF_UNSPEC`` in which case + the first address returned by ``resolve_name()`` will be used, otherwise the + first address of the specified family will be used. + + *resolver*, a ``dns.asyncresolver.Resolver`` or ``None``, the resolver to use for + resolution of hostnames. If not specified, the default resolver will be used. + + Returns a ``dns.resolver.Resolver`` or raises an exception. + """ + if resolver is None: + resolver = get_default_resolver() + nameservers: List[Union[str, dns.nameserver.Nameserver]] = [] + if isinstance(where, str) and dns.inet.is_address(where): + nameservers.append(dns.nameserver.Do53Nameserver(where, port)) + else: + answers = await resolver.resolve_name(where, family) + for address in answers.addresses(): + nameservers.append(dns.nameserver.Do53Nameserver(address, port)) + res = dns.asyncresolver.Resolver(configure=False) + res.nameservers = nameservers + return res + + +async def resolve_at( + where: Union[dns.name.Name, str], + qname: Union[dns.name.Name, str], + rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, + rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, + tcp: bool = False, + source: Optional[str] = None, + raise_on_no_answer: bool = True, + source_port: int = 0, + lifetime: Optional[float] = None, + search: Optional[bool] = None, + backend: Optional[dns.asyncbackend.Backend] = None, + port: int = 53, + family: int = socket.AF_UNSPEC, + resolver: Optional[Resolver] = None, +) -> dns.resolver.Answer: + """Query nameservers to find the answer to the question. + + This is a convenience function that calls ``dns.asyncresolver.make_resolver_at()`` + to make a resolver, and then uses it to resolve the query. + + See ``dns.asyncresolver.Resolver.resolve`` for more information on the resolution + parameters, and ``dns.asyncresolver.make_resolver_at`` for information about the + resolver parameters *where*, *port*, *family*, and *resolver*. + + If making more than one query, it is more efficient to call + ``dns.asyncresolver.make_resolver_at()`` and then use that resolver for the queries + instead of calling ``resolve_at()`` multiple times. + """ + res = await make_resolver_at(where, port, family, resolver) + return await res.resolve( + qname, + rdtype, + rdclass, + tcp, + source, + raise_on_no_answer, + source_port, + lifetime, + search, + backend, + ) diff --git a/.venv/lib/python3.9/site-packages/dns/dnssec.py b/.venv/lib/python3.9/site-packages/dns/dnssec.py new file mode 100644 index 0000000..b69d0a1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/dnssec.py @@ -0,0 +1,1247 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Common DNSSEC-related functions and constants.""" + + +import base64 +import contextlib +import functools +import hashlib +import struct +import time +from datetime import datetime +from typing import Callable, Dict, List, Optional, Set, Tuple, Union, cast + +import dns._features +import dns.exception +import dns.name +import dns.node +import dns.rdata +import dns.rdataclass +import dns.rdataset +import dns.rdatatype +import dns.rrset +import dns.transaction +import dns.zone +from dns.dnssectypes import Algorithm, DSDigest, NSEC3Hash +from dns.exception import ( # pylint: disable=W0611 + AlgorithmKeyMismatch, + DeniedByPolicy, + UnsupportedAlgorithm, + ValidationFailure, +) +from dns.rdtypes.ANY.CDNSKEY import CDNSKEY +from dns.rdtypes.ANY.CDS import CDS +from dns.rdtypes.ANY.DNSKEY import DNSKEY +from dns.rdtypes.ANY.DS import DS +from dns.rdtypes.ANY.NSEC import NSEC, Bitmap +from dns.rdtypes.ANY.NSEC3PARAM import NSEC3PARAM +from dns.rdtypes.ANY.RRSIG import RRSIG, sigtime_to_posixtime +from dns.rdtypes.dnskeybase import Flag + +PublicKey = Union[ + "GenericPublicKey", + "rsa.RSAPublicKey", + "ec.EllipticCurvePublicKey", + "ed25519.Ed25519PublicKey", + "ed448.Ed448PublicKey", +] + +PrivateKey = Union[ + "GenericPrivateKey", + "rsa.RSAPrivateKey", + "ec.EllipticCurvePrivateKey", + "ed25519.Ed25519PrivateKey", + "ed448.Ed448PrivateKey", +] + +RRsetSigner = Callable[[dns.transaction.Transaction, dns.rrset.RRset], None] + + +def algorithm_from_text(text: str) -> Algorithm: + """Convert text into a DNSSEC algorithm value. + + *text*, a ``str``, the text to convert to into an algorithm value. + + Returns an ``int``. + """ + + return Algorithm.from_text(text) + + +def algorithm_to_text(value: Union[Algorithm, int]) -> str: + """Convert a DNSSEC algorithm value to text + + *value*, a ``dns.dnssec.Algorithm``. + + Returns a ``str``, the name of a DNSSEC algorithm. + """ + + return Algorithm.to_text(value) + + +def to_timestamp(value: Union[datetime, str, float, int]) -> int: + """Convert various format to a timestamp""" + if isinstance(value, datetime): + return int(value.timestamp()) + elif isinstance(value, str): + return sigtime_to_posixtime(value) + elif isinstance(value, float): + return int(value) + elif isinstance(value, int): + return value + else: + raise TypeError("Unsupported timestamp type") + + +def key_id(key: Union[DNSKEY, CDNSKEY]) -> int: + """Return the key id (a 16-bit number) for the specified key. + + *key*, a ``dns.rdtypes.ANY.DNSKEY.DNSKEY`` + + Returns an ``int`` between 0 and 65535 + """ + + rdata = key.to_wire() + assert rdata is not None # for mypy + if key.algorithm == Algorithm.RSAMD5: + return (rdata[-3] << 8) + rdata[-2] + else: + total = 0 + for i in range(len(rdata) // 2): + total += (rdata[2 * i] << 8) + rdata[2 * i + 1] + if len(rdata) % 2 != 0: + total += rdata[len(rdata) - 1] << 8 + total += (total >> 16) & 0xFFFF + return total & 0xFFFF + + +class Policy: + def __init__(self): + pass + + def ok_to_sign(self, _: DNSKEY) -> bool: # pragma: no cover + return False + + def ok_to_validate(self, _: DNSKEY) -> bool: # pragma: no cover + return False + + def ok_to_create_ds(self, _: DSDigest) -> bool: # pragma: no cover + return False + + def ok_to_validate_ds(self, _: DSDigest) -> bool: # pragma: no cover + return False + + +class SimpleDeny(Policy): + def __init__(self, deny_sign, deny_validate, deny_create_ds, deny_validate_ds): + super().__init__() + self._deny_sign = deny_sign + self._deny_validate = deny_validate + self._deny_create_ds = deny_create_ds + self._deny_validate_ds = deny_validate_ds + + def ok_to_sign(self, key: DNSKEY) -> bool: + return key.algorithm not in self._deny_sign + + def ok_to_validate(self, key: DNSKEY) -> bool: + return key.algorithm not in self._deny_validate + + def ok_to_create_ds(self, algorithm: DSDigest) -> bool: + return algorithm not in self._deny_create_ds + + def ok_to_validate_ds(self, algorithm: DSDigest) -> bool: + return algorithm not in self._deny_validate_ds + + +rfc_8624_policy = SimpleDeny( + {Algorithm.RSAMD5, Algorithm.DSA, Algorithm.DSANSEC3SHA1, Algorithm.ECCGOST}, + {Algorithm.RSAMD5, Algorithm.DSA, Algorithm.DSANSEC3SHA1}, + {DSDigest.NULL, DSDigest.SHA1, DSDigest.GOST}, + {DSDigest.NULL}, +) + +allow_all_policy = SimpleDeny(set(), set(), set(), set()) + + +default_policy = rfc_8624_policy + + +def make_ds( + name: Union[dns.name.Name, str], + key: dns.rdata.Rdata, + algorithm: Union[DSDigest, str], + origin: Optional[dns.name.Name] = None, + policy: Optional[Policy] = None, + validating: bool = False, +) -> DS: + """Create a DS record for a DNSSEC key. + + *name*, a ``dns.name.Name`` or ``str``, the owner name of the DS record. + + *key*, a ``dns.rdtypes.ANY.DNSKEY.DNSKEY`` or ``dns.rdtypes.ANY.DNSKEY.CDNSKEY``, + the key the DS is about. + + *algorithm*, a ``str`` or ``int`` specifying the hash algorithm. + The currently supported hashes are "SHA1", "SHA256", and "SHA384". Case + does not matter for these strings. + + *origin*, a ``dns.name.Name`` or ``None``. If *key* is a relative name, + then it will be made absolute using the specified origin. + + *policy*, a ``dns.dnssec.Policy`` or ``None``. If ``None``, the default policy, + ``dns.dnssec.default_policy`` is used; this policy defaults to that of RFC 8624. + + *validating*, a ``bool``. If ``True``, then policy is checked in + validating mode, i.e. "Is it ok to validate using this digest algorithm?". + Otherwise the policy is checked in creating mode, i.e. "Is it ok to create a DS with + this digest algorithm?". + + Raises ``UnsupportedAlgorithm`` if the algorithm is unknown. + + Raises ``DeniedByPolicy`` if the algorithm is denied by policy. + + Returns a ``dns.rdtypes.ANY.DS.DS`` + """ + + if policy is None: + policy = default_policy + try: + if isinstance(algorithm, str): + algorithm = DSDigest[algorithm.upper()] + except Exception: + raise UnsupportedAlgorithm(f'unsupported algorithm "{algorithm}"') + if validating: + check = policy.ok_to_validate_ds + else: + check = policy.ok_to_create_ds + if not check(algorithm): + raise DeniedByPolicy + if not isinstance(key, (DNSKEY, CDNSKEY)): + raise ValueError("key is not a DNSKEY/CDNSKEY") + if algorithm == DSDigest.SHA1: + dshash = hashlib.sha1() + elif algorithm == DSDigest.SHA256: + dshash = hashlib.sha256() + elif algorithm == DSDigest.SHA384: + dshash = hashlib.sha384() + else: + raise UnsupportedAlgorithm(f'unsupported algorithm "{algorithm}"') + + if isinstance(name, str): + name = dns.name.from_text(name, origin) + wire = name.canonicalize().to_wire() + kwire = key.to_wire(origin=origin) + assert wire is not None and kwire is not None # for mypy + dshash.update(wire) + dshash.update(kwire) + digest = dshash.digest() + + dsrdata = struct.pack("!HBB", key_id(key), key.algorithm, algorithm) + digest + ds = dns.rdata.from_wire( + dns.rdataclass.IN, dns.rdatatype.DS, dsrdata, 0, len(dsrdata) + ) + return cast(DS, ds) + + +def make_cds( + name: Union[dns.name.Name, str], + key: dns.rdata.Rdata, + algorithm: Union[DSDigest, str], + origin: Optional[dns.name.Name] = None, +) -> CDS: + """Create a CDS record for a DNSSEC key. + + *name*, a ``dns.name.Name`` or ``str``, the owner name of the DS record. + + *key*, a ``dns.rdtypes.ANY.DNSKEY.DNSKEY`` or ``dns.rdtypes.ANY.DNSKEY.CDNSKEY``, + the key the DS is about. + + *algorithm*, a ``str`` or ``int`` specifying the hash algorithm. + The currently supported hashes are "SHA1", "SHA256", and "SHA384". Case + does not matter for these strings. + + *origin*, a ``dns.name.Name`` or ``None``. If *key* is a relative name, + then it will be made absolute using the specified origin. + + Raises ``UnsupportedAlgorithm`` if the algorithm is unknown. + + Returns a ``dns.rdtypes.ANY.DS.CDS`` + """ + + ds = make_ds(name, key, algorithm, origin) + return CDS( + rdclass=ds.rdclass, + rdtype=dns.rdatatype.CDS, + key_tag=ds.key_tag, + algorithm=ds.algorithm, + digest_type=ds.digest_type, + digest=ds.digest, + ) + + +def _find_candidate_keys( + keys: Dict[dns.name.Name, Union[dns.rdataset.Rdataset, dns.node.Node]], rrsig: RRSIG +) -> Optional[List[DNSKEY]]: + value = keys.get(rrsig.signer) + if isinstance(value, dns.node.Node): + rdataset = value.get_rdataset(dns.rdataclass.IN, dns.rdatatype.DNSKEY) + else: + rdataset = value + if rdataset is None: + return None + return [ + cast(DNSKEY, rd) + for rd in rdataset + if rd.algorithm == rrsig.algorithm + and key_id(rd) == rrsig.key_tag + and (rd.flags & Flag.ZONE) == Flag.ZONE # RFC 4034 2.1.1 + and rd.protocol == 3 # RFC 4034 2.1.2 + ] + + +def _get_rrname_rdataset( + rrset: Union[dns.rrset.RRset, Tuple[dns.name.Name, dns.rdataset.Rdataset]], +) -> Tuple[dns.name.Name, dns.rdataset.Rdataset]: + if isinstance(rrset, tuple): + return rrset[0], rrset[1] + else: + return rrset.name, rrset + + +def _validate_signature(sig: bytes, data: bytes, key: DNSKEY) -> None: + # pylint: disable=possibly-used-before-assignment + public_cls = get_algorithm_cls_from_dnskey(key).public_cls + try: + public_key = public_cls.from_dnskey(key) + except ValueError: + raise ValidationFailure("invalid public key") + public_key.verify(sig, data) + + +def _validate_rrsig( + rrset: Union[dns.rrset.RRset, Tuple[dns.name.Name, dns.rdataset.Rdataset]], + rrsig: RRSIG, + keys: Dict[dns.name.Name, Union[dns.node.Node, dns.rdataset.Rdataset]], + origin: Optional[dns.name.Name] = None, + now: Optional[float] = None, + policy: Optional[Policy] = None, +) -> None: + """Validate an RRset against a single signature rdata, throwing an + exception if validation is not successful. + + *rrset*, the RRset to validate. This can be a + ``dns.rrset.RRset`` or a (``dns.name.Name``, ``dns.rdataset.Rdataset``) + tuple. + + *rrsig*, a ``dns.rdata.Rdata``, the signature to validate. + + *keys*, the key dictionary, used to find the DNSKEY associated + with a given name. The dictionary is keyed by a + ``dns.name.Name``, and has ``dns.node.Node`` or + ``dns.rdataset.Rdataset`` values. + + *origin*, a ``dns.name.Name`` or ``None``, the origin to use for relative + names. + + *now*, a ``float`` or ``None``, the time, in seconds since the epoch, to + use as the current time when validating. If ``None``, the actual current + time is used. + + *policy*, a ``dns.dnssec.Policy`` or ``None``. If ``None``, the default policy, + ``dns.dnssec.default_policy`` is used; this policy defaults to that of RFC 8624. + + Raises ``ValidationFailure`` if the signature is expired, not yet valid, + the public key is invalid, the algorithm is unknown, the verification + fails, etc. + + Raises ``UnsupportedAlgorithm`` if the algorithm is recognized by + dnspython but not implemented. + """ + + if policy is None: + policy = default_policy + + candidate_keys = _find_candidate_keys(keys, rrsig) + if candidate_keys is None: + raise ValidationFailure("unknown key") + + if now is None: + now = time.time() + if rrsig.expiration < now: + raise ValidationFailure("expired") + if rrsig.inception > now: + raise ValidationFailure("not yet valid") + + data = _make_rrsig_signature_data(rrset, rrsig, origin) + + # pylint: disable=possibly-used-before-assignment + for candidate_key in candidate_keys: + if not policy.ok_to_validate(candidate_key): + continue + try: + _validate_signature(rrsig.signature, data, candidate_key) + return + except (InvalidSignature, ValidationFailure): + # this happens on an individual validation failure + continue + # nothing verified -- raise failure: + raise ValidationFailure("verify failure") + + +def _validate( + rrset: Union[dns.rrset.RRset, Tuple[dns.name.Name, dns.rdataset.Rdataset]], + rrsigset: Union[dns.rrset.RRset, Tuple[dns.name.Name, dns.rdataset.Rdataset]], + keys: Dict[dns.name.Name, Union[dns.node.Node, dns.rdataset.Rdataset]], + origin: Optional[dns.name.Name] = None, + now: Optional[float] = None, + policy: Optional[Policy] = None, +) -> None: + """Validate an RRset against a signature RRset, throwing an exception + if none of the signatures validate. + + *rrset*, the RRset to validate. This can be a + ``dns.rrset.RRset`` or a (``dns.name.Name``, ``dns.rdataset.Rdataset``) + tuple. + + *rrsigset*, the signature RRset. This can be a + ``dns.rrset.RRset`` or a (``dns.name.Name``, ``dns.rdataset.Rdataset``) + tuple. + + *keys*, the key dictionary, used to find the DNSKEY associated + with a given name. The dictionary is keyed by a + ``dns.name.Name``, and has ``dns.node.Node`` or + ``dns.rdataset.Rdataset`` values. + + *origin*, a ``dns.name.Name``, the origin to use for relative names; + defaults to None. + + *now*, an ``int`` or ``None``, the time, in seconds since the epoch, to + use as the current time when validating. If ``None``, the actual current + time is used. + + *policy*, a ``dns.dnssec.Policy`` or ``None``. If ``None``, the default policy, + ``dns.dnssec.default_policy`` is used; this policy defaults to that of RFC 8624. + + Raises ``ValidationFailure`` if the signature is expired, not yet valid, + the public key is invalid, the algorithm is unknown, the verification + fails, etc. + """ + + if policy is None: + policy = default_policy + + if isinstance(origin, str): + origin = dns.name.from_text(origin, dns.name.root) + + if isinstance(rrset, tuple): + rrname = rrset[0] + else: + rrname = rrset.name + + if isinstance(rrsigset, tuple): + rrsigname = rrsigset[0] + rrsigrdataset = rrsigset[1] + else: + rrsigname = rrsigset.name + rrsigrdataset = rrsigset + + rrname = rrname.choose_relativity(origin) + rrsigname = rrsigname.choose_relativity(origin) + if rrname != rrsigname: + raise ValidationFailure("owner names do not match") + + for rrsig in rrsigrdataset: + if not isinstance(rrsig, RRSIG): + raise ValidationFailure("expected an RRSIG") + try: + _validate_rrsig(rrset, rrsig, keys, origin, now, policy) + return + except (ValidationFailure, UnsupportedAlgorithm): + pass + raise ValidationFailure("no RRSIGs validated") + + +def _sign( + rrset: Union[dns.rrset.RRset, Tuple[dns.name.Name, dns.rdataset.Rdataset]], + private_key: PrivateKey, + signer: dns.name.Name, + dnskey: DNSKEY, + inception: Optional[Union[datetime, str, int, float]] = None, + expiration: Optional[Union[datetime, str, int, float]] = None, + lifetime: Optional[int] = None, + verify: bool = False, + policy: Optional[Policy] = None, + origin: Optional[dns.name.Name] = None, + deterministic: bool = True, +) -> RRSIG: + """Sign RRset using private key. + + *rrset*, the RRset to validate. This can be a + ``dns.rrset.RRset`` or a (``dns.name.Name``, ``dns.rdataset.Rdataset``) + tuple. + + *private_key*, the private key to use for signing, a + ``cryptography.hazmat.primitives.asymmetric`` private key class applicable + for DNSSEC. + + *signer*, a ``dns.name.Name``, the Signer's name. + + *dnskey*, a ``DNSKEY`` matching ``private_key``. + + *inception*, a ``datetime``, ``str``, ``int``, ``float`` or ``None``, the + signature inception time. If ``None``, the current time is used. If a ``str``, the + format is "YYYYMMDDHHMMSS" or alternatively the number of seconds since the UNIX + epoch in text form; this is the same the RRSIG rdata's text form. + Values of type `int` or `float` are interpreted as seconds since the UNIX epoch. + + *expiration*, a ``datetime``, ``str``, ``int``, ``float`` or ``None``, the signature + expiration time. If ``None``, the expiration time will be the inception time plus + the value of the *lifetime* parameter. See the description of *inception* above + for how the various parameter types are interpreted. + + *lifetime*, an ``int`` or ``None``, the signature lifetime in seconds. This + parameter is only meaningful if *expiration* is ``None``. + + *verify*, a ``bool``. If set to ``True``, the signer will verify signatures + after they are created; the default is ``False``. + + *policy*, a ``dns.dnssec.Policy`` or ``None``. If ``None``, the default policy, + ``dns.dnssec.default_policy`` is used; this policy defaults to that of RFC 8624. + + *origin*, a ``dns.name.Name`` or ``None``. If ``None``, the default, then all + names in the rrset (including its owner name) must be absolute; otherwise the + specified origin will be used to make names absolute when signing. + + *deterministic*, a ``bool``. If ``True``, the default, use deterministic + (reproducible) signatures when supported by the algorithm used for signing. + Currently, this only affects ECDSA. + + Raises ``DeniedByPolicy`` if the signature is denied by policy. + """ + + if policy is None: + policy = default_policy + if not policy.ok_to_sign(dnskey): + raise DeniedByPolicy + + if isinstance(rrset, tuple): + rdclass = rrset[1].rdclass + rdtype = rrset[1].rdtype + rrname = rrset[0] + original_ttl = rrset[1].ttl + else: + rdclass = rrset.rdclass + rdtype = rrset.rdtype + rrname = rrset.name + original_ttl = rrset.ttl + + if inception is not None: + rrsig_inception = to_timestamp(inception) + else: + rrsig_inception = int(time.time()) + + if expiration is not None: + rrsig_expiration = to_timestamp(expiration) + elif lifetime is not None: + rrsig_expiration = rrsig_inception + lifetime + else: + raise ValueError("expiration or lifetime must be specified") + + # Derelativize now because we need a correct labels length for the + # rrsig_template. + if origin is not None: + rrname = rrname.derelativize(origin) + labels = len(rrname) - 1 + + # Adjust labels appropriately for wildcards. + if rrname.is_wild(): + labels -= 1 + + rrsig_template = RRSIG( + rdclass=rdclass, + rdtype=dns.rdatatype.RRSIG, + type_covered=rdtype, + algorithm=dnskey.algorithm, + labels=labels, + original_ttl=original_ttl, + expiration=rrsig_expiration, + inception=rrsig_inception, + key_tag=key_id(dnskey), + signer=signer, + signature=b"", + ) + + data = dns.dnssec._make_rrsig_signature_data(rrset, rrsig_template, origin) + + # pylint: disable=possibly-used-before-assignment + if isinstance(private_key, GenericPrivateKey): + signing_key = private_key + else: + try: + private_cls = get_algorithm_cls_from_dnskey(dnskey) + signing_key = private_cls(key=private_key) + except UnsupportedAlgorithm: + raise TypeError("Unsupported key algorithm") + + signature = signing_key.sign(data, verify, deterministic) + + return cast(RRSIG, rrsig_template.replace(signature=signature)) + + +def _make_rrsig_signature_data( + rrset: Union[dns.rrset.RRset, Tuple[dns.name.Name, dns.rdataset.Rdataset]], + rrsig: RRSIG, + origin: Optional[dns.name.Name] = None, +) -> bytes: + """Create signature rdata. + + *rrset*, the RRset to sign/validate. This can be a + ``dns.rrset.RRset`` or a (``dns.name.Name``, ``dns.rdataset.Rdataset``) + tuple. + + *rrsig*, a ``dns.rdata.Rdata``, the signature to validate, or the + signature template used when signing. + + *origin*, a ``dns.name.Name`` or ``None``, the origin to use for relative + names. + + Raises ``UnsupportedAlgorithm`` if the algorithm is recognized by + dnspython but not implemented. + """ + + if isinstance(origin, str): + origin = dns.name.from_text(origin, dns.name.root) + + signer = rrsig.signer + if not signer.is_absolute(): + if origin is None: + raise ValidationFailure("relative RR name without an origin specified") + signer = signer.derelativize(origin) + + # For convenience, allow the rrset to be specified as a (name, + # rdataset) tuple as well as a proper rrset + rrname, rdataset = _get_rrname_rdataset(rrset) + + data = b"" + wire = rrsig.to_wire(origin=signer) + assert wire is not None # for mypy + data += wire[:18] + data += rrsig.signer.to_digestable(signer) + + # Derelativize the name before considering labels. + if not rrname.is_absolute(): + if origin is None: + raise ValidationFailure("relative RR name without an origin specified") + rrname = rrname.derelativize(origin) + + name_len = len(rrname) + if rrname.is_wild() and rrsig.labels != name_len - 2: + raise ValidationFailure("wild owner name has wrong label length") + if name_len - 1 < rrsig.labels: + raise ValidationFailure("owner name longer than RRSIG labels") + elif rrsig.labels < name_len - 1: + suffix = rrname.split(rrsig.labels + 1)[1] + rrname = dns.name.from_text("*", suffix) + rrnamebuf = rrname.to_digestable() + rrfixed = struct.pack("!HHI", rdataset.rdtype, rdataset.rdclass, rrsig.original_ttl) + rdatas = [rdata.to_digestable(origin) for rdata in rdataset] + for rdata in sorted(rdatas): + data += rrnamebuf + data += rrfixed + rrlen = struct.pack("!H", len(rdata)) + data += rrlen + data += rdata + + return data + + +def _make_dnskey( + public_key: PublicKey, + algorithm: Union[int, str], + flags: int = Flag.ZONE, + protocol: int = 3, +) -> DNSKEY: + """Convert a public key to DNSKEY Rdata + + *public_key*, a ``PublicKey`` (``GenericPublicKey`` or + ``cryptography.hazmat.primitives.asymmetric``) to convert. + + *algorithm*, a ``str`` or ``int`` specifying the DNSKEY algorithm. + + *flags*: DNSKEY flags field as an integer. + + *protocol*: DNSKEY protocol field as an integer. + + Raises ``ValueError`` if the specified key algorithm parameters are not + unsupported, ``TypeError`` if the key type is unsupported, + `UnsupportedAlgorithm` if the algorithm is unknown and + `AlgorithmKeyMismatch` if the algorithm does not match the key type. + + Return DNSKEY ``Rdata``. + """ + + algorithm = Algorithm.make(algorithm) + + # pylint: disable=possibly-used-before-assignment + if isinstance(public_key, GenericPublicKey): + return public_key.to_dnskey(flags=flags, protocol=protocol) + else: + public_cls = get_algorithm_cls(algorithm).public_cls + return public_cls(key=public_key).to_dnskey(flags=flags, protocol=protocol) + + +def _make_cdnskey( + public_key: PublicKey, + algorithm: Union[int, str], + flags: int = Flag.ZONE, + protocol: int = 3, +) -> CDNSKEY: + """Convert a public key to CDNSKEY Rdata + + *public_key*, the public key to convert, a + ``cryptography.hazmat.primitives.asymmetric`` public key class applicable + for DNSSEC. + + *algorithm*, a ``str`` or ``int`` specifying the DNSKEY algorithm. + + *flags*: DNSKEY flags field as an integer. + + *protocol*: DNSKEY protocol field as an integer. + + Raises ``ValueError`` if the specified key algorithm parameters are not + unsupported, ``TypeError`` if the key type is unsupported, + `UnsupportedAlgorithm` if the algorithm is unknown and + `AlgorithmKeyMismatch` if the algorithm does not match the key type. + + Return CDNSKEY ``Rdata``. + """ + + dnskey = _make_dnskey(public_key, algorithm, flags, protocol) + + return CDNSKEY( + rdclass=dnskey.rdclass, + rdtype=dns.rdatatype.CDNSKEY, + flags=dnskey.flags, + protocol=dnskey.protocol, + algorithm=dnskey.algorithm, + key=dnskey.key, + ) + + +def nsec3_hash( + domain: Union[dns.name.Name, str], + salt: Optional[Union[str, bytes]], + iterations: int, + algorithm: Union[int, str], +) -> str: + """ + Calculate the NSEC3 hash, according to + https://tools.ietf.org/html/rfc5155#section-5 + + *domain*, a ``dns.name.Name`` or ``str``, the name to hash. + + *salt*, a ``str``, ``bytes``, or ``None``, the hash salt. If a + string, it is decoded as a hex string. + + *iterations*, an ``int``, the number of iterations. + + *algorithm*, a ``str`` or ``int``, the hash algorithm. + The only defined algorithm is SHA1. + + Returns a ``str``, the encoded NSEC3 hash. + """ + + b32_conversion = str.maketrans( + "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", "0123456789ABCDEFGHIJKLMNOPQRSTUV" + ) + + try: + if isinstance(algorithm, str): + algorithm = NSEC3Hash[algorithm.upper()] + except Exception: + raise ValueError("Wrong hash algorithm (only SHA1 is supported)") + + if algorithm != NSEC3Hash.SHA1: + raise ValueError("Wrong hash algorithm (only SHA1 is supported)") + + if salt is None: + salt_encoded = b"" + elif isinstance(salt, str): + if len(salt) % 2 == 0: + salt_encoded = bytes.fromhex(salt) + else: + raise ValueError("Invalid salt length") + else: + salt_encoded = salt + + if not isinstance(domain, dns.name.Name): + domain = dns.name.from_text(domain) + domain_encoded = domain.canonicalize().to_wire() + assert domain_encoded is not None + + digest = hashlib.sha1(domain_encoded + salt_encoded).digest() + for _ in range(iterations): + digest = hashlib.sha1(digest + salt_encoded).digest() + + output = base64.b32encode(digest).decode("utf-8") + output = output.translate(b32_conversion) + + return output + + +def make_ds_rdataset( + rrset: Union[dns.rrset.RRset, Tuple[dns.name.Name, dns.rdataset.Rdataset]], + algorithms: Set[Union[DSDigest, str]], + origin: Optional[dns.name.Name] = None, +) -> dns.rdataset.Rdataset: + """Create a DS record from DNSKEY/CDNSKEY/CDS. + + *rrset*, the RRset to create DS Rdataset for. This can be a + ``dns.rrset.RRset`` or a (``dns.name.Name``, ``dns.rdataset.Rdataset``) + tuple. + + *algorithms*, a set of ``str`` or ``int`` specifying the hash algorithms. + The currently supported hashes are "SHA1", "SHA256", and "SHA384". Case + does not matter for these strings. If the RRset is a CDS, only digest + algorithms matching algorithms are accepted. + + *origin*, a ``dns.name.Name`` or ``None``. If `key` is a relative name, + then it will be made absolute using the specified origin. + + Raises ``UnsupportedAlgorithm`` if any of the algorithms are unknown and + ``ValueError`` if the given RRset is not usable. + + Returns a ``dns.rdataset.Rdataset`` + """ + + rrname, rdataset = _get_rrname_rdataset(rrset) + + if rdataset.rdtype not in ( + dns.rdatatype.DNSKEY, + dns.rdatatype.CDNSKEY, + dns.rdatatype.CDS, + ): + raise ValueError("rrset not a DNSKEY/CDNSKEY/CDS") + + _algorithms = set() + for algorithm in algorithms: + try: + if isinstance(algorithm, str): + algorithm = DSDigest[algorithm.upper()] + except Exception: + raise UnsupportedAlgorithm(f'unsupported algorithm "{algorithm}"') + _algorithms.add(algorithm) + + if rdataset.rdtype == dns.rdatatype.CDS: + res = [] + for rdata in cds_rdataset_to_ds_rdataset(rdataset): + if rdata.digest_type in _algorithms: + res.append(rdata) + if len(res) == 0: + raise ValueError("no acceptable CDS rdata found") + return dns.rdataset.from_rdata_list(rdataset.ttl, res) + + res = [] + for algorithm in _algorithms: + res.extend(dnskey_rdataset_to_cds_rdataset(rrname, rdataset, algorithm, origin)) + return dns.rdataset.from_rdata_list(rdataset.ttl, res) + + +def cds_rdataset_to_ds_rdataset( + rdataset: dns.rdataset.Rdataset, +) -> dns.rdataset.Rdataset: + """Create a CDS record from DS. + + *rdataset*, a ``dns.rdataset.Rdataset``, to create DS Rdataset for. + + Raises ``ValueError`` if the rdataset is not CDS. + + Returns a ``dns.rdataset.Rdataset`` + """ + + if rdataset.rdtype != dns.rdatatype.CDS: + raise ValueError("rdataset not a CDS") + res = [] + for rdata in rdataset: + res.append( + CDS( + rdclass=rdata.rdclass, + rdtype=dns.rdatatype.DS, + key_tag=rdata.key_tag, + algorithm=rdata.algorithm, + digest_type=rdata.digest_type, + digest=rdata.digest, + ) + ) + return dns.rdataset.from_rdata_list(rdataset.ttl, res) + + +def dnskey_rdataset_to_cds_rdataset( + name: Union[dns.name.Name, str], + rdataset: dns.rdataset.Rdataset, + algorithm: Union[DSDigest, str], + origin: Optional[dns.name.Name] = None, +) -> dns.rdataset.Rdataset: + """Create a CDS record from DNSKEY/CDNSKEY. + + *name*, a ``dns.name.Name`` or ``str``, the owner name of the CDS record. + + *rdataset*, a ``dns.rdataset.Rdataset``, to create DS Rdataset for. + + *algorithm*, a ``str`` or ``int`` specifying the hash algorithm. + The currently supported hashes are "SHA1", "SHA256", and "SHA384". Case + does not matter for these strings. + + *origin*, a ``dns.name.Name`` or ``None``. If `key` is a relative name, + then it will be made absolute using the specified origin. + + Raises ``UnsupportedAlgorithm`` if the algorithm is unknown or + ``ValueError`` if the rdataset is not DNSKEY/CDNSKEY. + + Returns a ``dns.rdataset.Rdataset`` + """ + + if rdataset.rdtype not in (dns.rdatatype.DNSKEY, dns.rdatatype.CDNSKEY): + raise ValueError("rdataset not a DNSKEY/CDNSKEY") + res = [] + for rdata in rdataset: + res.append(make_cds(name, rdata, algorithm, origin)) + return dns.rdataset.from_rdata_list(rdataset.ttl, res) + + +def dnskey_rdataset_to_cdnskey_rdataset( + rdataset: dns.rdataset.Rdataset, +) -> dns.rdataset.Rdataset: + """Create a CDNSKEY record from DNSKEY. + + *rdataset*, a ``dns.rdataset.Rdataset``, to create CDNSKEY Rdataset for. + + Returns a ``dns.rdataset.Rdataset`` + """ + + if rdataset.rdtype != dns.rdatatype.DNSKEY: + raise ValueError("rdataset not a DNSKEY") + res = [] + for rdata in rdataset: + res.append( + CDNSKEY( + rdclass=rdataset.rdclass, + rdtype=rdataset.rdtype, + flags=rdata.flags, + protocol=rdata.protocol, + algorithm=rdata.algorithm, + key=rdata.key, + ) + ) + return dns.rdataset.from_rdata_list(rdataset.ttl, res) + + +def default_rrset_signer( + txn: dns.transaction.Transaction, + rrset: dns.rrset.RRset, + signer: dns.name.Name, + ksks: List[Tuple[PrivateKey, DNSKEY]], + zsks: List[Tuple[PrivateKey, DNSKEY]], + inception: Optional[Union[datetime, str, int, float]] = None, + expiration: Optional[Union[datetime, str, int, float]] = None, + lifetime: Optional[int] = None, + policy: Optional[Policy] = None, + origin: Optional[dns.name.Name] = None, + deterministic: bool = True, +) -> None: + """Default RRset signer""" + + if rrset.rdtype in set( + [ + dns.rdatatype.RdataType.DNSKEY, + dns.rdatatype.RdataType.CDS, + dns.rdatatype.RdataType.CDNSKEY, + ] + ): + keys = ksks + else: + keys = zsks + + for private_key, dnskey in keys: + rrsig = dns.dnssec.sign( + rrset=rrset, + private_key=private_key, + dnskey=dnskey, + inception=inception, + expiration=expiration, + lifetime=lifetime, + signer=signer, + policy=policy, + origin=origin, + deterministic=deterministic, + ) + txn.add(rrset.name, rrset.ttl, rrsig) + + +def sign_zone( + zone: dns.zone.Zone, + txn: Optional[dns.transaction.Transaction] = None, + keys: Optional[List[Tuple[PrivateKey, DNSKEY]]] = None, + add_dnskey: bool = True, + dnskey_ttl: Optional[int] = None, + inception: Optional[Union[datetime, str, int, float]] = None, + expiration: Optional[Union[datetime, str, int, float]] = None, + lifetime: Optional[int] = None, + nsec3: Optional[NSEC3PARAM] = None, + rrset_signer: Optional[RRsetSigner] = None, + policy: Optional[Policy] = None, + deterministic: bool = True, +) -> None: + """Sign zone. + + *zone*, a ``dns.zone.Zone``, the zone to sign. + + *txn*, a ``dns.transaction.Transaction``, an optional transaction to use for + signing. + + *keys*, a list of (``PrivateKey``, ``DNSKEY``) tuples, to use for signing. KSK/ZSK + roles are assigned automatically if the SEP flag is used, otherwise all RRsets are + signed by all keys. + + *add_dnskey*, a ``bool``. If ``True``, the default, all specified DNSKEYs are + automatically added to the zone on signing. + + *dnskey_ttl*, a``int``, specifies the TTL for DNSKEY RRs. If not specified the TTL + of the existing DNSKEY RRset used or the TTL of the SOA RRset. + + *inception*, a ``datetime``, ``str``, ``int``, ``float`` or ``None``, the signature + inception time. If ``None``, the current time is used. If a ``str``, the format is + "YYYYMMDDHHMMSS" or alternatively the number of seconds since the UNIX epoch in text + form; this is the same the RRSIG rdata's text form. Values of type `int` or `float` + are interpreted as seconds since the UNIX epoch. + + *expiration*, a ``datetime``, ``str``, ``int``, ``float`` or ``None``, the signature + expiration time. If ``None``, the expiration time will be the inception time plus + the value of the *lifetime* parameter. See the description of *inception* above for + how the various parameter types are interpreted. + + *lifetime*, an ``int`` or ``None``, the signature lifetime in seconds. This + parameter is only meaningful if *expiration* is ``None``. + + *nsec3*, a ``NSEC3PARAM`` Rdata, configures signing using NSEC3. Not yet + implemented. + + *rrset_signer*, a ``Callable``, an optional function for signing RRsets. The + function requires two arguments: transaction and RRset. If the not specified, + ``dns.dnssec.default_rrset_signer`` will be used. + + *deterministic*, a ``bool``. If ``True``, the default, use deterministic + (reproducible) signatures when supported by the algorithm used for signing. + Currently, this only affects ECDSA. + + Returns ``None``. + """ + + ksks = [] + zsks = [] + + # if we have both KSKs and ZSKs, split by SEP flag. if not, sign all + # records with all keys + if keys: + for key in keys: + if key[1].flags & Flag.SEP: + ksks.append(key) + else: + zsks.append(key) + if not ksks: + ksks = keys + if not zsks: + zsks = keys + else: + keys = [] + + if txn: + cm: contextlib.AbstractContextManager = contextlib.nullcontext(txn) + else: + cm = zone.writer() + + if zone.origin is None: + raise ValueError("no zone origin") + + with cm as _txn: + if add_dnskey: + if dnskey_ttl is None: + dnskey = _txn.get(zone.origin, dns.rdatatype.DNSKEY) + if dnskey: + dnskey_ttl = dnskey.ttl + else: + soa = _txn.get(zone.origin, dns.rdatatype.SOA) + dnskey_ttl = soa.ttl + for _, dnskey in keys: + _txn.add(zone.origin, dnskey_ttl, dnskey) + + if nsec3: + raise NotImplementedError("Signing with NSEC3 not yet implemented") + else: + _rrset_signer = rrset_signer or functools.partial( + default_rrset_signer, + signer=zone.origin, + ksks=ksks, + zsks=zsks, + inception=inception, + expiration=expiration, + lifetime=lifetime, + policy=policy, + origin=zone.origin, + deterministic=deterministic, + ) + return _sign_zone_nsec(zone, _txn, _rrset_signer) + + +def _sign_zone_nsec( + zone: dns.zone.Zone, + txn: dns.transaction.Transaction, + rrset_signer: Optional[RRsetSigner] = None, +) -> None: + """NSEC zone signer""" + + def _txn_add_nsec( + txn: dns.transaction.Transaction, + name: dns.name.Name, + next_secure: Optional[dns.name.Name], + rdclass: dns.rdataclass.RdataClass, + ttl: int, + rrset_signer: Optional[RRsetSigner] = None, + ) -> None: + """NSEC zone signer helper""" + mandatory_types = set( + [dns.rdatatype.RdataType.RRSIG, dns.rdatatype.RdataType.NSEC] + ) + node = txn.get_node(name) + if node and next_secure: + types = ( + set([rdataset.rdtype for rdataset in node.rdatasets]) | mandatory_types + ) + windows = Bitmap.from_rdtypes(list(types)) + rrset = dns.rrset.from_rdata( + name, + ttl, + NSEC( + rdclass=rdclass, + rdtype=dns.rdatatype.RdataType.NSEC, + next=next_secure, + windows=windows, + ), + ) + txn.add(rrset) + if rrset_signer: + rrset_signer(txn, rrset) + + rrsig_ttl = zone.get_soa().minimum + delegation = None + last_secure = None + + for name in sorted(txn.iterate_names()): + if delegation and name.is_subdomain(delegation): + # names below delegations are not secure + continue + elif txn.get(name, dns.rdatatype.NS) and name != zone.origin: + # inside delegation + delegation = name + else: + # outside delegation + delegation = None + + if rrset_signer: + node = txn.get_node(name) + if node: + for rdataset in node.rdatasets: + if rdataset.rdtype == dns.rdatatype.RRSIG: + # do not sign RRSIGs + continue + elif delegation and rdataset.rdtype != dns.rdatatype.DS: + # do not sign delegations except DS records + continue + else: + rrset = dns.rrset.from_rdata(name, rdataset.ttl, *rdataset) + rrset_signer(txn, rrset) + + # We need "is not None" as the empty name is False because its length is 0. + if last_secure is not None: + _txn_add_nsec(txn, last_secure, name, zone.rdclass, rrsig_ttl, rrset_signer) + last_secure = name + + if last_secure: + _txn_add_nsec( + txn, last_secure, zone.origin, zone.rdclass, rrsig_ttl, rrset_signer + ) + + +def _need_pyca(*args, **kwargs): + raise ImportError( + "DNSSEC validation requires python cryptography" + ) # pragma: no cover + + +if dns._features.have("dnssec"): + from cryptography.exceptions import InvalidSignature + from cryptography.hazmat.primitives.asymmetric import dsa # pylint: disable=W0611 + from cryptography.hazmat.primitives.asymmetric import ec # pylint: disable=W0611 + from cryptography.hazmat.primitives.asymmetric import ed448 # pylint: disable=W0611 + from cryptography.hazmat.primitives.asymmetric import rsa # pylint: disable=W0611 + from cryptography.hazmat.primitives.asymmetric import ( # pylint: disable=W0611 + ed25519, + ) + + from dns.dnssecalgs import ( # pylint: disable=C0412 + get_algorithm_cls, + get_algorithm_cls_from_dnskey, + ) + from dns.dnssecalgs.base import GenericPrivateKey, GenericPublicKey + + validate = _validate # type: ignore + validate_rrsig = _validate_rrsig # type: ignore + sign = _sign + make_dnskey = _make_dnskey + make_cdnskey = _make_cdnskey + _have_pyca = True +else: # pragma: no cover + validate = _need_pyca + validate_rrsig = _need_pyca + sign = _need_pyca + make_dnskey = _need_pyca + make_cdnskey = _need_pyca + _have_pyca = False + +### BEGIN generated Algorithm constants + +RSAMD5 = Algorithm.RSAMD5 +DH = Algorithm.DH +DSA = Algorithm.DSA +ECC = Algorithm.ECC +RSASHA1 = Algorithm.RSASHA1 +DSANSEC3SHA1 = Algorithm.DSANSEC3SHA1 +RSASHA1NSEC3SHA1 = Algorithm.RSASHA1NSEC3SHA1 +RSASHA256 = Algorithm.RSASHA256 +RSASHA512 = Algorithm.RSASHA512 +ECCGOST = Algorithm.ECCGOST +ECDSAP256SHA256 = Algorithm.ECDSAP256SHA256 +ECDSAP384SHA384 = Algorithm.ECDSAP384SHA384 +ED25519 = Algorithm.ED25519 +ED448 = Algorithm.ED448 +INDIRECT = Algorithm.INDIRECT +PRIVATEDNS = Algorithm.PRIVATEDNS +PRIVATEOID = Algorithm.PRIVATEOID + +### END generated Algorithm constants diff --git a/.venv/lib/python3.9/site-packages/dns/dnssecalgs/__init__.py b/.venv/lib/python3.9/site-packages/dns/dnssecalgs/__init__.py new file mode 100644 index 0000000..602367e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/dnssecalgs/__init__.py @@ -0,0 +1,121 @@ +from typing import Dict, Optional, Tuple, Type, Union + +import dns.name +from dns.dnssecalgs.base import GenericPrivateKey +from dns.dnssectypes import Algorithm +from dns.exception import UnsupportedAlgorithm +from dns.rdtypes.ANY.DNSKEY import DNSKEY + +if dns._features.have("dnssec"): + from dns.dnssecalgs.dsa import PrivateDSA, PrivateDSANSEC3SHA1 + from dns.dnssecalgs.ecdsa import PrivateECDSAP256SHA256, PrivateECDSAP384SHA384 + from dns.dnssecalgs.eddsa import PrivateED448, PrivateED25519 + from dns.dnssecalgs.rsa import ( + PrivateRSAMD5, + PrivateRSASHA1, + PrivateRSASHA1NSEC3SHA1, + PrivateRSASHA256, + PrivateRSASHA512, + ) + + _have_cryptography = True +else: + _have_cryptography = False + +AlgorithmPrefix = Optional[Union[bytes, dns.name.Name]] + +algorithms: Dict[Tuple[Algorithm, AlgorithmPrefix], Type[GenericPrivateKey]] = {} +if _have_cryptography: + # pylint: disable=possibly-used-before-assignment + algorithms.update( + { + (Algorithm.RSAMD5, None): PrivateRSAMD5, + (Algorithm.DSA, None): PrivateDSA, + (Algorithm.RSASHA1, None): PrivateRSASHA1, + (Algorithm.DSANSEC3SHA1, None): PrivateDSANSEC3SHA1, + (Algorithm.RSASHA1NSEC3SHA1, None): PrivateRSASHA1NSEC3SHA1, + (Algorithm.RSASHA256, None): PrivateRSASHA256, + (Algorithm.RSASHA512, None): PrivateRSASHA512, + (Algorithm.ECDSAP256SHA256, None): PrivateECDSAP256SHA256, + (Algorithm.ECDSAP384SHA384, None): PrivateECDSAP384SHA384, + (Algorithm.ED25519, None): PrivateED25519, + (Algorithm.ED448, None): PrivateED448, + } + ) + + +def get_algorithm_cls( + algorithm: Union[int, str], prefix: AlgorithmPrefix = None +) -> Type[GenericPrivateKey]: + """Get Private Key class from Algorithm. + + *algorithm*, a ``str`` or ``int`` specifying the DNSKEY algorithm. + + Raises ``UnsupportedAlgorithm`` if the algorithm is unknown. + + Returns a ``dns.dnssecalgs.GenericPrivateKey`` + """ + algorithm = Algorithm.make(algorithm) + cls = algorithms.get((algorithm, prefix)) + if cls: + return cls + raise UnsupportedAlgorithm( + f'algorithm "{Algorithm.to_text(algorithm)}" not supported by dnspython' + ) + + +def get_algorithm_cls_from_dnskey(dnskey: DNSKEY) -> Type[GenericPrivateKey]: + """Get Private Key class from DNSKEY. + + *dnskey*, a ``DNSKEY`` to get Algorithm class for. + + Raises ``UnsupportedAlgorithm`` if the algorithm is unknown. + + Returns a ``dns.dnssecalgs.GenericPrivateKey`` + """ + prefix: AlgorithmPrefix = None + if dnskey.algorithm == Algorithm.PRIVATEDNS: + prefix, _ = dns.name.from_wire(dnskey.key, 0) + elif dnskey.algorithm == Algorithm.PRIVATEOID: + length = int(dnskey.key[0]) + prefix = dnskey.key[0 : length + 1] + return get_algorithm_cls(dnskey.algorithm, prefix) + + +def register_algorithm_cls( + algorithm: Union[int, str], + algorithm_cls: Type[GenericPrivateKey], + name: Optional[Union[dns.name.Name, str]] = None, + oid: Optional[bytes] = None, +) -> None: + """Register Algorithm Private Key class. + + *algorithm*, a ``str`` or ``int`` specifying the DNSKEY algorithm. + + *algorithm_cls*: A `GenericPrivateKey` class. + + *name*, an optional ``dns.name.Name`` or ``str``, for for PRIVATEDNS algorithms. + + *oid*: an optional BER-encoded `bytes` for PRIVATEOID algorithms. + + Raises ``ValueError`` if a name or oid is specified incorrectly. + """ + if not issubclass(algorithm_cls, GenericPrivateKey): + raise TypeError("Invalid algorithm class") + algorithm = Algorithm.make(algorithm) + prefix: AlgorithmPrefix = None + if algorithm == Algorithm.PRIVATEDNS: + if name is None: + raise ValueError("Name required for PRIVATEDNS algorithms") + if isinstance(name, str): + name = dns.name.from_text(name) + prefix = name + elif algorithm == Algorithm.PRIVATEOID: + if oid is None: + raise ValueError("OID required for PRIVATEOID algorithms") + prefix = bytes([len(oid)]) + oid + elif name: + raise ValueError("Name only supported for PRIVATEDNS algorithm") + elif oid: + raise ValueError("OID only supported for PRIVATEOID algorithm") + algorithms[(algorithm, prefix)] = algorithm_cls diff --git a/.venv/lib/python3.9/site-packages/dns/dnssecalgs/base.py b/.venv/lib/python3.9/site-packages/dns/dnssecalgs/base.py new file mode 100644 index 0000000..752ee48 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/dnssecalgs/base.py @@ -0,0 +1,89 @@ +from abc import ABC, abstractmethod # pylint: disable=no-name-in-module +from typing import Any, Optional, Type + +import dns.rdataclass +import dns.rdatatype +from dns.dnssectypes import Algorithm +from dns.exception import AlgorithmKeyMismatch +from dns.rdtypes.ANY.DNSKEY import DNSKEY +from dns.rdtypes.dnskeybase import Flag + + +class GenericPublicKey(ABC): + algorithm: Algorithm + + @abstractmethod + def __init__(self, key: Any) -> None: + pass + + @abstractmethod + def verify(self, signature: bytes, data: bytes) -> None: + """Verify signed DNSSEC data""" + + @abstractmethod + def encode_key_bytes(self) -> bytes: + """Encode key as bytes for DNSKEY""" + + @classmethod + def _ensure_algorithm_key_combination(cls, key: DNSKEY) -> None: + if key.algorithm != cls.algorithm: + raise AlgorithmKeyMismatch + + def to_dnskey(self, flags: int = Flag.ZONE, protocol: int = 3) -> DNSKEY: + """Return public key as DNSKEY""" + return DNSKEY( + rdclass=dns.rdataclass.IN, + rdtype=dns.rdatatype.DNSKEY, + flags=flags, + protocol=protocol, + algorithm=self.algorithm, + key=self.encode_key_bytes(), + ) + + @classmethod + @abstractmethod + def from_dnskey(cls, key: DNSKEY) -> "GenericPublicKey": + """Create public key from DNSKEY""" + + @classmethod + @abstractmethod + def from_pem(cls, public_pem: bytes) -> "GenericPublicKey": + """Create public key from PEM-encoded SubjectPublicKeyInfo as specified + in RFC 5280""" + + @abstractmethod + def to_pem(self) -> bytes: + """Return public-key as PEM-encoded SubjectPublicKeyInfo as specified + in RFC 5280""" + + +class GenericPrivateKey(ABC): + public_cls: Type[GenericPublicKey] + + @abstractmethod + def __init__(self, key: Any) -> None: + pass + + @abstractmethod + def sign( + self, + data: bytes, + verify: bool = False, + deterministic: bool = True, + ) -> bytes: + """Sign DNSSEC data""" + + @abstractmethod + def public_key(self) -> "GenericPublicKey": + """Return public key instance""" + + @classmethod + @abstractmethod + def from_pem( + cls, private_pem: bytes, password: Optional[bytes] = None + ) -> "GenericPrivateKey": + """Create private key from PEM-encoded PKCS#8""" + + @abstractmethod + def to_pem(self, password: Optional[bytes] = None) -> bytes: + """Return private key as PEM-encoded PKCS#8""" diff --git a/.venv/lib/python3.9/site-packages/dns/dnssecalgs/cryptography.py b/.venv/lib/python3.9/site-packages/dns/dnssecalgs/cryptography.py new file mode 100644 index 0000000..5a31a81 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/dnssecalgs/cryptography.py @@ -0,0 +1,68 @@ +from typing import Any, Optional, Type + +from cryptography.hazmat.primitives import serialization + +from dns.dnssecalgs.base import GenericPrivateKey, GenericPublicKey +from dns.exception import AlgorithmKeyMismatch + + +class CryptographyPublicKey(GenericPublicKey): + key: Any = None + key_cls: Any = None + + def __init__(self, key: Any) -> None: # pylint: disable=super-init-not-called + if self.key_cls is None: + raise TypeError("Undefined private key class") + if not isinstance( # pylint: disable=isinstance-second-argument-not-valid-type + key, self.key_cls + ): + raise AlgorithmKeyMismatch + self.key = key + + @classmethod + def from_pem(cls, public_pem: bytes) -> "GenericPublicKey": + key = serialization.load_pem_public_key(public_pem) + return cls(key=key) + + def to_pem(self) -> bytes: + return self.key.public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo, + ) + + +class CryptographyPrivateKey(GenericPrivateKey): + key: Any = None + key_cls: Any = None + public_cls: Type[CryptographyPublicKey] + + def __init__(self, key: Any) -> None: # pylint: disable=super-init-not-called + if self.key_cls is None: + raise TypeError("Undefined private key class") + if not isinstance( # pylint: disable=isinstance-second-argument-not-valid-type + key, self.key_cls + ): + raise AlgorithmKeyMismatch + self.key = key + + def public_key(self) -> "CryptographyPublicKey": + return self.public_cls(key=self.key.public_key()) + + @classmethod + def from_pem( + cls, private_pem: bytes, password: Optional[bytes] = None + ) -> "GenericPrivateKey": + key = serialization.load_pem_private_key(private_pem, password=password) + return cls(key=key) + + def to_pem(self, password: Optional[bytes] = None) -> bytes: + encryption_algorithm: serialization.KeySerializationEncryption + if password: + encryption_algorithm = serialization.BestAvailableEncryption(password) + else: + encryption_algorithm = serialization.NoEncryption() + return self.key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=encryption_algorithm, + ) diff --git a/.venv/lib/python3.9/site-packages/dns/dnssecalgs/dsa.py b/.venv/lib/python3.9/site-packages/dns/dnssecalgs/dsa.py new file mode 100644 index 0000000..adca3de --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/dnssecalgs/dsa.py @@ -0,0 +1,106 @@ +import struct + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import dsa, utils + +from dns.dnssecalgs.cryptography import CryptographyPrivateKey, CryptographyPublicKey +from dns.dnssectypes import Algorithm +from dns.rdtypes.ANY.DNSKEY import DNSKEY + + +class PublicDSA(CryptographyPublicKey): + key: dsa.DSAPublicKey + key_cls = dsa.DSAPublicKey + algorithm = Algorithm.DSA + chosen_hash = hashes.SHA1() + + def verify(self, signature: bytes, data: bytes) -> None: + sig_r = signature[1:21] + sig_s = signature[21:] + sig = utils.encode_dss_signature( + int.from_bytes(sig_r, "big"), int.from_bytes(sig_s, "big") + ) + self.key.verify(sig, data, self.chosen_hash) + + def encode_key_bytes(self) -> bytes: + """Encode a public key per RFC 2536, section 2.""" + pn = self.key.public_numbers() + dsa_t = (self.key.key_size // 8 - 64) // 8 + if dsa_t > 8: + raise ValueError("unsupported DSA key size") + octets = 64 + dsa_t * 8 + res = struct.pack("!B", dsa_t) + res += pn.parameter_numbers.q.to_bytes(20, "big") + res += pn.parameter_numbers.p.to_bytes(octets, "big") + res += pn.parameter_numbers.g.to_bytes(octets, "big") + res += pn.y.to_bytes(octets, "big") + return res + + @classmethod + def from_dnskey(cls, key: DNSKEY) -> "PublicDSA": + cls._ensure_algorithm_key_combination(key) + keyptr = key.key + (t,) = struct.unpack("!B", keyptr[0:1]) + keyptr = keyptr[1:] + octets = 64 + t * 8 + dsa_q = keyptr[0:20] + keyptr = keyptr[20:] + dsa_p = keyptr[0:octets] + keyptr = keyptr[octets:] + dsa_g = keyptr[0:octets] + keyptr = keyptr[octets:] + dsa_y = keyptr[0:octets] + return cls( + key=dsa.DSAPublicNumbers( # type: ignore + int.from_bytes(dsa_y, "big"), + dsa.DSAParameterNumbers( + int.from_bytes(dsa_p, "big"), + int.from_bytes(dsa_q, "big"), + int.from_bytes(dsa_g, "big"), + ), + ).public_key(default_backend()), + ) + + +class PrivateDSA(CryptographyPrivateKey): + key: dsa.DSAPrivateKey + key_cls = dsa.DSAPrivateKey + public_cls = PublicDSA + + def sign( + self, + data: bytes, + verify: bool = False, + deterministic: bool = True, + ) -> bytes: + """Sign using a private key per RFC 2536, section 3.""" + public_dsa_key = self.key.public_key() + if public_dsa_key.key_size > 1024: + raise ValueError("DSA key size overflow") + der_signature = self.key.sign(data, self.public_cls.chosen_hash) + dsa_r, dsa_s = utils.decode_dss_signature(der_signature) + dsa_t = (public_dsa_key.key_size // 8 - 64) // 8 + octets = 20 + signature = ( + struct.pack("!B", dsa_t) + + int.to_bytes(dsa_r, length=octets, byteorder="big") + + int.to_bytes(dsa_s, length=octets, byteorder="big") + ) + if verify: + self.public_key().verify(signature, data) + return signature + + @classmethod + def generate(cls, key_size: int) -> "PrivateDSA": + return cls( + key=dsa.generate_private_key(key_size=key_size), + ) + + +class PublicDSANSEC3SHA1(PublicDSA): + algorithm = Algorithm.DSANSEC3SHA1 + + +class PrivateDSANSEC3SHA1(PrivateDSA): + public_cls = PublicDSANSEC3SHA1 diff --git a/.venv/lib/python3.9/site-packages/dns/dnssecalgs/ecdsa.py b/.venv/lib/python3.9/site-packages/dns/dnssecalgs/ecdsa.py new file mode 100644 index 0000000..86d5764 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/dnssecalgs/ecdsa.py @@ -0,0 +1,97 @@ +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import ec, utils + +from dns.dnssecalgs.cryptography import CryptographyPrivateKey, CryptographyPublicKey +from dns.dnssectypes import Algorithm +from dns.rdtypes.ANY.DNSKEY import DNSKEY + + +class PublicECDSA(CryptographyPublicKey): + key: ec.EllipticCurvePublicKey + key_cls = ec.EllipticCurvePublicKey + algorithm: Algorithm + chosen_hash: hashes.HashAlgorithm + curve: ec.EllipticCurve + octets: int + + def verify(self, signature: bytes, data: bytes) -> None: + sig_r = signature[0 : self.octets] + sig_s = signature[self.octets :] + sig = utils.encode_dss_signature( + int.from_bytes(sig_r, "big"), int.from_bytes(sig_s, "big") + ) + self.key.verify(sig, data, ec.ECDSA(self.chosen_hash)) + + def encode_key_bytes(self) -> bytes: + """Encode a public key per RFC 6605, section 4.""" + pn = self.key.public_numbers() + return pn.x.to_bytes(self.octets, "big") + pn.y.to_bytes(self.octets, "big") + + @classmethod + def from_dnskey(cls, key: DNSKEY) -> "PublicECDSA": + cls._ensure_algorithm_key_combination(key) + ecdsa_x = key.key[0 : cls.octets] + ecdsa_y = key.key[cls.octets : cls.octets * 2] + return cls( + key=ec.EllipticCurvePublicNumbers( + curve=cls.curve, + x=int.from_bytes(ecdsa_x, "big"), + y=int.from_bytes(ecdsa_y, "big"), + ).public_key(default_backend()), + ) + + +class PrivateECDSA(CryptographyPrivateKey): + key: ec.EllipticCurvePrivateKey + key_cls = ec.EllipticCurvePrivateKey + public_cls = PublicECDSA + + def sign( + self, + data: bytes, + verify: bool = False, + deterministic: bool = True, + ) -> bytes: + """Sign using a private key per RFC 6605, section 4.""" + algorithm = ec.ECDSA( + self.public_cls.chosen_hash, deterministic_signing=deterministic + ) + der_signature = self.key.sign(data, algorithm) + dsa_r, dsa_s = utils.decode_dss_signature(der_signature) + signature = int.to_bytes( + dsa_r, length=self.public_cls.octets, byteorder="big" + ) + int.to_bytes(dsa_s, length=self.public_cls.octets, byteorder="big") + if verify: + self.public_key().verify(signature, data) + return signature + + @classmethod + def generate(cls) -> "PrivateECDSA": + return cls( + key=ec.generate_private_key( + curve=cls.public_cls.curve, backend=default_backend() + ), + ) + + +class PublicECDSAP256SHA256(PublicECDSA): + algorithm = Algorithm.ECDSAP256SHA256 + chosen_hash = hashes.SHA256() + curve = ec.SECP256R1() + octets = 32 + + +class PrivateECDSAP256SHA256(PrivateECDSA): + public_cls = PublicECDSAP256SHA256 + + +class PublicECDSAP384SHA384(PublicECDSA): + algorithm = Algorithm.ECDSAP384SHA384 + chosen_hash = hashes.SHA384() + curve = ec.SECP384R1() + octets = 48 + + +class PrivateECDSAP384SHA384(PrivateECDSA): + public_cls = PublicECDSAP384SHA384 diff --git a/.venv/lib/python3.9/site-packages/dns/dnssecalgs/eddsa.py b/.venv/lib/python3.9/site-packages/dns/dnssecalgs/eddsa.py new file mode 100644 index 0000000..604bcbf --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/dnssecalgs/eddsa.py @@ -0,0 +1,70 @@ +from typing import Type + +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import ed448, ed25519 + +from dns.dnssecalgs.cryptography import CryptographyPrivateKey, CryptographyPublicKey +from dns.dnssectypes import Algorithm +from dns.rdtypes.ANY.DNSKEY import DNSKEY + + +class PublicEDDSA(CryptographyPublicKey): + def verify(self, signature: bytes, data: bytes) -> None: + self.key.verify(signature, data) + + def encode_key_bytes(self) -> bytes: + """Encode a public key per RFC 8080, section 3.""" + return self.key.public_bytes( + encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw + ) + + @classmethod + def from_dnskey(cls, key: DNSKEY) -> "PublicEDDSA": + cls._ensure_algorithm_key_combination(key) + return cls( + key=cls.key_cls.from_public_bytes(key.key), + ) + + +class PrivateEDDSA(CryptographyPrivateKey): + public_cls: Type[PublicEDDSA] + + def sign( + self, + data: bytes, + verify: bool = False, + deterministic: bool = True, + ) -> bytes: + """Sign using a private key per RFC 8080, section 4.""" + signature = self.key.sign(data) + if verify: + self.public_key().verify(signature, data) + return signature + + @classmethod + def generate(cls) -> "PrivateEDDSA": + return cls(key=cls.key_cls.generate()) + + +class PublicED25519(PublicEDDSA): + key: ed25519.Ed25519PublicKey + key_cls = ed25519.Ed25519PublicKey + algorithm = Algorithm.ED25519 + + +class PrivateED25519(PrivateEDDSA): + key: ed25519.Ed25519PrivateKey + key_cls = ed25519.Ed25519PrivateKey + public_cls = PublicED25519 + + +class PublicED448(PublicEDDSA): + key: ed448.Ed448PublicKey + key_cls = ed448.Ed448PublicKey + algorithm = Algorithm.ED448 + + +class PrivateED448(PrivateEDDSA): + key: ed448.Ed448PrivateKey + key_cls = ed448.Ed448PrivateKey + public_cls = PublicED448 diff --git a/.venv/lib/python3.9/site-packages/dns/dnssecalgs/rsa.py b/.venv/lib/python3.9/site-packages/dns/dnssecalgs/rsa.py new file mode 100644 index 0000000..27537aa --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/dnssecalgs/rsa.py @@ -0,0 +1,124 @@ +import math +import struct + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import padding, rsa + +from dns.dnssecalgs.cryptography import CryptographyPrivateKey, CryptographyPublicKey +from dns.dnssectypes import Algorithm +from dns.rdtypes.ANY.DNSKEY import DNSKEY + + +class PublicRSA(CryptographyPublicKey): + key: rsa.RSAPublicKey + key_cls = rsa.RSAPublicKey + algorithm: Algorithm + chosen_hash: hashes.HashAlgorithm + + def verify(self, signature: bytes, data: bytes) -> None: + self.key.verify(signature, data, padding.PKCS1v15(), self.chosen_hash) + + def encode_key_bytes(self) -> bytes: + """Encode a public key per RFC 3110, section 2.""" + pn = self.key.public_numbers() + _exp_len = math.ceil(int.bit_length(pn.e) / 8) + exp = int.to_bytes(pn.e, length=_exp_len, byteorder="big") + if _exp_len > 255: + exp_header = b"\0" + struct.pack("!H", _exp_len) + else: + exp_header = struct.pack("!B", _exp_len) + if pn.n.bit_length() < 512 or pn.n.bit_length() > 4096: + raise ValueError("unsupported RSA key length") + return exp_header + exp + pn.n.to_bytes((pn.n.bit_length() + 7) // 8, "big") + + @classmethod + def from_dnskey(cls, key: DNSKEY) -> "PublicRSA": + cls._ensure_algorithm_key_combination(key) + keyptr = key.key + (bytes_,) = struct.unpack("!B", keyptr[0:1]) + keyptr = keyptr[1:] + if bytes_ == 0: + (bytes_,) = struct.unpack("!H", keyptr[0:2]) + keyptr = keyptr[2:] + rsa_e = keyptr[0:bytes_] + rsa_n = keyptr[bytes_:] + return cls( + key=rsa.RSAPublicNumbers( + int.from_bytes(rsa_e, "big"), int.from_bytes(rsa_n, "big") + ).public_key(default_backend()) + ) + + +class PrivateRSA(CryptographyPrivateKey): + key: rsa.RSAPrivateKey + key_cls = rsa.RSAPrivateKey + public_cls = PublicRSA + default_public_exponent = 65537 + + def sign( + self, + data: bytes, + verify: bool = False, + deterministic: bool = True, + ) -> bytes: + """Sign using a private key per RFC 3110, section 3.""" + signature = self.key.sign(data, padding.PKCS1v15(), self.public_cls.chosen_hash) + if verify: + self.public_key().verify(signature, data) + return signature + + @classmethod + def generate(cls, key_size: int) -> "PrivateRSA": + return cls( + key=rsa.generate_private_key( + public_exponent=cls.default_public_exponent, + key_size=key_size, + backend=default_backend(), + ) + ) + + +class PublicRSAMD5(PublicRSA): + algorithm = Algorithm.RSAMD5 + chosen_hash = hashes.MD5() + + +class PrivateRSAMD5(PrivateRSA): + public_cls = PublicRSAMD5 + + +class PublicRSASHA1(PublicRSA): + algorithm = Algorithm.RSASHA1 + chosen_hash = hashes.SHA1() + + +class PrivateRSASHA1(PrivateRSA): + public_cls = PublicRSASHA1 + + +class PublicRSASHA1NSEC3SHA1(PublicRSA): + algorithm = Algorithm.RSASHA1NSEC3SHA1 + chosen_hash = hashes.SHA1() + + +class PrivateRSASHA1NSEC3SHA1(PrivateRSA): + public_cls = PublicRSASHA1NSEC3SHA1 + + +class PublicRSASHA256(PublicRSA): + algorithm = Algorithm.RSASHA256 + chosen_hash = hashes.SHA256() + + +class PrivateRSASHA256(PrivateRSA): + public_cls = PublicRSASHA256 + + +class PublicRSASHA512(PublicRSA): + algorithm = Algorithm.RSASHA512 + chosen_hash = hashes.SHA512() + + +class PrivateRSASHA512(PrivateRSA): + public_cls = PublicRSASHA512 diff --git a/.venv/lib/python3.9/site-packages/dns/dnssectypes.py b/.venv/lib/python3.9/site-packages/dns/dnssectypes.py new file mode 100644 index 0000000..02131e0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/dnssectypes.py @@ -0,0 +1,71 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Common DNSSEC-related types.""" + +# This is a separate file to avoid import circularity between dns.dnssec and +# the implementations of the DS and DNSKEY types. + +import dns.enum + + +class Algorithm(dns.enum.IntEnum): + RSAMD5 = 1 + DH = 2 + DSA = 3 + ECC = 4 + RSASHA1 = 5 + DSANSEC3SHA1 = 6 + RSASHA1NSEC3SHA1 = 7 + RSASHA256 = 8 + RSASHA512 = 10 + ECCGOST = 12 + ECDSAP256SHA256 = 13 + ECDSAP384SHA384 = 14 + ED25519 = 15 + ED448 = 16 + INDIRECT = 252 + PRIVATEDNS = 253 + PRIVATEOID = 254 + + @classmethod + def _maximum(cls): + return 255 + + +class DSDigest(dns.enum.IntEnum): + """DNSSEC Delegation Signer Digest Algorithm""" + + NULL = 0 + SHA1 = 1 + SHA256 = 2 + GOST = 3 + SHA384 = 4 + + @classmethod + def _maximum(cls): + return 255 + + +class NSEC3Hash(dns.enum.IntEnum): + """NSEC3 hash algorithm""" + + SHA1 = 1 + + @classmethod + def _maximum(cls): + return 255 diff --git a/.venv/lib/python3.9/site-packages/dns/e164.py b/.venv/lib/python3.9/site-packages/dns/e164.py new file mode 100644 index 0000000..453736d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/e164.py @@ -0,0 +1,116 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2006-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS E.164 helpers.""" + +from typing import Iterable, Optional, Union + +import dns.exception +import dns.name +import dns.resolver + +#: The public E.164 domain. +public_enum_domain = dns.name.from_text("e164.arpa.") + + +def from_e164( + text: str, origin: Optional[dns.name.Name] = public_enum_domain +) -> dns.name.Name: + """Convert an E.164 number in textual form into a Name object whose + value is the ENUM domain name for that number. + + Non-digits in the text are ignored, i.e. "16505551212", + "+1.650.555.1212" and "1 (650) 555-1212" are all the same. + + *text*, a ``str``, is an E.164 number in textual form. + + *origin*, a ``dns.name.Name``, the domain in which the number + should be constructed. The default is ``e164.arpa.``. + + Returns a ``dns.name.Name``. + """ + + parts = [d for d in text if d.isdigit()] + parts.reverse() + return dns.name.from_text(".".join(parts), origin=origin) + + +def to_e164( + name: dns.name.Name, + origin: Optional[dns.name.Name] = public_enum_domain, + want_plus_prefix: bool = True, +) -> str: + """Convert an ENUM domain name into an E.164 number. + + Note that dnspython does not have any information about preferred + number formats within national numbering plans, so all numbers are + emitted as a simple string of digits, prefixed by a '+' (unless + *want_plus_prefix* is ``False``). + + *name* is a ``dns.name.Name``, the ENUM domain name. + + *origin* is a ``dns.name.Name``, a domain containing the ENUM + domain name. The name is relativized to this domain before being + converted to text. If ``None``, no relativization is done. + + *want_plus_prefix* is a ``bool``. If True, add a '+' to the beginning of + the returned number. + + Returns a ``str``. + + """ + if origin is not None: + name = name.relativize(origin) + dlabels = [d for d in name.labels if d.isdigit() and len(d) == 1] + if len(dlabels) != len(name.labels): + raise dns.exception.SyntaxError("non-digit labels in ENUM domain name") + dlabels.reverse() + text = b"".join(dlabels) + if want_plus_prefix: + text = b"+" + text + return text.decode() + + +def query( + number: str, + domains: Iterable[Union[dns.name.Name, str]], + resolver: Optional[dns.resolver.Resolver] = None, +) -> dns.resolver.Answer: + """Look for NAPTR RRs for the specified number in the specified domains. + + e.g. lookup('16505551212', ['e164.dnspython.org.', 'e164.arpa.']) + + *number*, a ``str`` is the number to look for. + + *domains* is an iterable containing ``dns.name.Name`` values. + + *resolver*, a ``dns.resolver.Resolver``, is the resolver to use. If + ``None``, the default resolver is used. + """ + + if resolver is None: + resolver = dns.resolver.get_default_resolver() + e_nx = dns.resolver.NXDOMAIN() + for domain in domains: + if isinstance(domain, str): + domain = dns.name.from_text(domain) + qname = dns.e164.from_e164(number, domain) + try: + return resolver.resolve(qname, "NAPTR") + except dns.resolver.NXDOMAIN as e: + e_nx += e + raise e_nx diff --git a/.venv/lib/python3.9/site-packages/dns/edns.py b/.venv/lib/python3.9/site-packages/dns/edns.py new file mode 100644 index 0000000..f7d9ff9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/edns.py @@ -0,0 +1,572 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2009-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""EDNS Options""" + +import binascii +import math +import socket +import struct +from typing import Any, Dict, Optional, Union + +import dns.enum +import dns.inet +import dns.rdata +import dns.wire + + +class OptionType(dns.enum.IntEnum): + #: NSID + NSID = 3 + #: DAU + DAU = 5 + #: DHU + DHU = 6 + #: N3U + N3U = 7 + #: ECS (client-subnet) + ECS = 8 + #: EXPIRE + EXPIRE = 9 + #: COOKIE + COOKIE = 10 + #: KEEPALIVE + KEEPALIVE = 11 + #: PADDING + PADDING = 12 + #: CHAIN + CHAIN = 13 + #: EDE (extended-dns-error) + EDE = 15 + #: REPORTCHANNEL + REPORTCHANNEL = 18 + + @classmethod + def _maximum(cls): + return 65535 + + +class Option: + """Base class for all EDNS option types.""" + + def __init__(self, otype: Union[OptionType, str]): + """Initialize an option. + + *otype*, a ``dns.edns.OptionType``, is the option type. + """ + self.otype = OptionType.make(otype) + + def to_wire(self, file: Optional[Any] = None) -> Optional[bytes]: + """Convert an option to wire format. + + Returns a ``bytes`` or ``None``. + + """ + raise NotImplementedError # pragma: no cover + + def to_text(self) -> str: + raise NotImplementedError # pragma: no cover + + @classmethod + def from_wire_parser(cls, otype: OptionType, parser: "dns.wire.Parser") -> "Option": + """Build an EDNS option object from wire format. + + *otype*, a ``dns.edns.OptionType``, is the option type. + + *parser*, a ``dns.wire.Parser``, the parser, which should be + restructed to the option length. + + Returns a ``dns.edns.Option``. + """ + raise NotImplementedError # pragma: no cover + + def _cmp(self, other): + """Compare an EDNS option with another option of the same type. + + Returns < 0 if < *other*, 0 if == *other*, and > 0 if > *other*. + """ + wire = self.to_wire() + owire = other.to_wire() + if wire == owire: + return 0 + if wire > owire: + return 1 + return -1 + + def __eq__(self, other): + if not isinstance(other, Option): + return False + if self.otype != other.otype: + return False + return self._cmp(other) == 0 + + def __ne__(self, other): + if not isinstance(other, Option): + return True + if self.otype != other.otype: + return True + return self._cmp(other) != 0 + + def __lt__(self, other): + if not isinstance(other, Option) or self.otype != other.otype: + return NotImplemented + return self._cmp(other) < 0 + + def __le__(self, other): + if not isinstance(other, Option) or self.otype != other.otype: + return NotImplemented + return self._cmp(other) <= 0 + + def __ge__(self, other): + if not isinstance(other, Option) or self.otype != other.otype: + return NotImplemented + return self._cmp(other) >= 0 + + def __gt__(self, other): + if not isinstance(other, Option) or self.otype != other.otype: + return NotImplemented + return self._cmp(other) > 0 + + def __str__(self): + return self.to_text() + + +class GenericOption(Option): # lgtm[py/missing-equals] + """Generic Option Class + + This class is used for EDNS option types for which we have no better + implementation. + """ + + def __init__(self, otype: Union[OptionType, str], data: Union[bytes, str]): + super().__init__(otype) + self.data = dns.rdata.Rdata._as_bytes(data, True) + + def to_wire(self, file: Optional[Any] = None) -> Optional[bytes]: + if file: + file.write(self.data) + return None + else: + return self.data + + def to_text(self) -> str: + return "Generic %d" % self.otype + + @classmethod + def from_wire_parser( + cls, otype: Union[OptionType, str], parser: "dns.wire.Parser" + ) -> Option: + return cls(otype, parser.get_remaining()) + + +class ECSOption(Option): # lgtm[py/missing-equals] + """EDNS Client Subnet (ECS, RFC7871)""" + + def __init__(self, address: str, srclen: Optional[int] = None, scopelen: int = 0): + """*address*, a ``str``, is the client address information. + + *srclen*, an ``int``, the source prefix length, which is the + leftmost number of bits of the address to be used for the + lookup. The default is 24 for IPv4 and 56 for IPv6. + + *scopelen*, an ``int``, the scope prefix length. This value + must be 0 in queries, and should be set in responses. + """ + + super().__init__(OptionType.ECS) + af = dns.inet.af_for_address(address) + + if af == socket.AF_INET6: + self.family = 2 + if srclen is None: + srclen = 56 + address = dns.rdata.Rdata._as_ipv6_address(address) + srclen = dns.rdata.Rdata._as_int(srclen, 0, 128) + scopelen = dns.rdata.Rdata._as_int(scopelen, 0, 128) + elif af == socket.AF_INET: + self.family = 1 + if srclen is None: + srclen = 24 + address = dns.rdata.Rdata._as_ipv4_address(address) + srclen = dns.rdata.Rdata._as_int(srclen, 0, 32) + scopelen = dns.rdata.Rdata._as_int(scopelen, 0, 32) + else: # pragma: no cover (this will never happen) + raise ValueError("Bad address family") + + assert srclen is not None + self.address = address + self.srclen = srclen + self.scopelen = scopelen + + addrdata = dns.inet.inet_pton(af, address) + nbytes = int(math.ceil(srclen / 8.0)) + + # Truncate to srclen and pad to the end of the last octet needed + # See RFC section 6 + self.addrdata = addrdata[:nbytes] + nbits = srclen % 8 + if nbits != 0: + last = struct.pack("B", ord(self.addrdata[-1:]) & (0xFF << (8 - nbits))) + self.addrdata = self.addrdata[:-1] + last + + def to_text(self) -> str: + return f"ECS {self.address}/{self.srclen} scope/{self.scopelen}" + + @staticmethod + def from_text(text: str) -> Option: + """Convert a string into a `dns.edns.ECSOption` + + *text*, a `str`, the text form of the option. + + Returns a `dns.edns.ECSOption`. + + Examples: + + >>> import dns.edns + >>> + >>> # basic example + >>> dns.edns.ECSOption.from_text('1.2.3.4/24') + >>> + >>> # also understands scope + >>> dns.edns.ECSOption.from_text('1.2.3.4/24/32') + >>> + >>> # IPv6 + >>> dns.edns.ECSOption.from_text('2001:4b98::1/64/64') + >>> + >>> # it understands results from `dns.edns.ECSOption.to_text()` + >>> dns.edns.ECSOption.from_text('ECS 1.2.3.4/24/32') + """ + optional_prefix = "ECS" + tokens = text.split() + ecs_text = None + if len(tokens) == 1: + ecs_text = tokens[0] + elif len(tokens) == 2: + if tokens[0] != optional_prefix: + raise ValueError(f'could not parse ECS from "{text}"') + ecs_text = tokens[1] + else: + raise ValueError(f'could not parse ECS from "{text}"') + n_slashes = ecs_text.count("/") + if n_slashes == 1: + address, tsrclen = ecs_text.split("/") + tscope = "0" + elif n_slashes == 2: + address, tsrclen, tscope = ecs_text.split("/") + else: + raise ValueError(f'could not parse ECS from "{text}"') + try: + scope = int(tscope) + except ValueError: + raise ValueError("invalid scope " + f'"{tscope}": scope must be an integer') + try: + srclen = int(tsrclen) + except ValueError: + raise ValueError( + "invalid srclen " + f'"{tsrclen}": srclen must be an integer' + ) + return ECSOption(address, srclen, scope) + + def to_wire(self, file: Optional[Any] = None) -> Optional[bytes]: + value = ( + struct.pack("!HBB", self.family, self.srclen, self.scopelen) + self.addrdata + ) + if file: + file.write(value) + return None + else: + return value + + @classmethod + def from_wire_parser( + cls, otype: Union[OptionType, str], parser: "dns.wire.Parser" + ) -> Option: + family, src, scope = parser.get_struct("!HBB") + addrlen = int(math.ceil(src / 8.0)) + prefix = parser.get_bytes(addrlen) + if family == 1: + pad = 4 - addrlen + addr = dns.ipv4.inet_ntoa(prefix + b"\x00" * pad) + elif family == 2: + pad = 16 - addrlen + addr = dns.ipv6.inet_ntoa(prefix + b"\x00" * pad) + else: + raise ValueError("unsupported family") + + return cls(addr, src, scope) + + +class EDECode(dns.enum.IntEnum): + OTHER = 0 + UNSUPPORTED_DNSKEY_ALGORITHM = 1 + UNSUPPORTED_DS_DIGEST_TYPE = 2 + STALE_ANSWER = 3 + FORGED_ANSWER = 4 + DNSSEC_INDETERMINATE = 5 + DNSSEC_BOGUS = 6 + SIGNATURE_EXPIRED = 7 + SIGNATURE_NOT_YET_VALID = 8 + DNSKEY_MISSING = 9 + RRSIGS_MISSING = 10 + NO_ZONE_KEY_BIT_SET = 11 + NSEC_MISSING = 12 + CACHED_ERROR = 13 + NOT_READY = 14 + BLOCKED = 15 + CENSORED = 16 + FILTERED = 17 + PROHIBITED = 18 + STALE_NXDOMAIN_ANSWER = 19 + NOT_AUTHORITATIVE = 20 + NOT_SUPPORTED = 21 + NO_REACHABLE_AUTHORITY = 22 + NETWORK_ERROR = 23 + INVALID_DATA = 24 + + @classmethod + def _maximum(cls): + return 65535 + + +class EDEOption(Option): # lgtm[py/missing-equals] + """Extended DNS Error (EDE, RFC8914)""" + + _preserve_case = {"DNSKEY", "DS", "DNSSEC", "RRSIGs", "NSEC", "NXDOMAIN"} + + def __init__(self, code: Union[EDECode, str], text: Optional[str] = None): + """*code*, a ``dns.edns.EDECode`` or ``str``, the info code of the + extended error. + + *text*, a ``str`` or ``None``, specifying additional information about + the error. + """ + + super().__init__(OptionType.EDE) + + self.code = EDECode.make(code) + if text is not None and not isinstance(text, str): + raise ValueError("text must be string or None") + self.text = text + + def to_text(self) -> str: + output = f"EDE {self.code}" + if self.code in EDECode: + desc = EDECode.to_text(self.code) + desc = " ".join( + word if word in self._preserve_case else word.title() + for word in desc.split("_") + ) + output += f" ({desc})" + if self.text is not None: + output += f": {self.text}" + return output + + def to_wire(self, file: Optional[Any] = None) -> Optional[bytes]: + value = struct.pack("!H", self.code) + if self.text is not None: + value += self.text.encode("utf8") + + if file: + file.write(value) + return None + else: + return value + + @classmethod + def from_wire_parser( + cls, otype: Union[OptionType, str], parser: "dns.wire.Parser" + ) -> Option: + code = EDECode.make(parser.get_uint16()) + text = parser.get_remaining() + + if text: + if text[-1] == 0: # text MAY be null-terminated + text = text[:-1] + btext = text.decode("utf8") + else: + btext = None + + return cls(code, btext) + + +class NSIDOption(Option): + def __init__(self, nsid: bytes): + super().__init__(OptionType.NSID) + self.nsid = nsid + + def to_wire(self, file: Any = None) -> Optional[bytes]: + if file: + file.write(self.nsid) + return None + else: + return self.nsid + + def to_text(self) -> str: + if all(c >= 0x20 and c <= 0x7E for c in self.nsid): + # All ASCII printable, so it's probably a string. + value = self.nsid.decode() + else: + value = binascii.hexlify(self.nsid).decode() + return f"NSID {value}" + + @classmethod + def from_wire_parser( + cls, otype: Union[OptionType, str], parser: dns.wire.Parser + ) -> Option: + return cls(parser.get_remaining()) + + +class CookieOption(Option): + def __init__(self, client: bytes, server: bytes): + super().__init__(dns.edns.OptionType.COOKIE) + self.client = client + self.server = server + if len(client) != 8: + raise ValueError("client cookie must be 8 bytes") + if len(server) != 0 and (len(server) < 8 or len(server) > 32): + raise ValueError("server cookie must be empty or between 8 and 32 bytes") + + def to_wire(self, file: Any = None) -> Optional[bytes]: + if file: + file.write(self.client) + if len(self.server) > 0: + file.write(self.server) + return None + else: + return self.client + self.server + + def to_text(self) -> str: + client = binascii.hexlify(self.client).decode() + if len(self.server) > 0: + server = binascii.hexlify(self.server).decode() + else: + server = "" + return f"COOKIE {client}{server}" + + @classmethod + def from_wire_parser( + cls, otype: Union[OptionType, str], parser: dns.wire.Parser + ) -> Option: + return cls(parser.get_bytes(8), parser.get_remaining()) + + +class ReportChannelOption(Option): + # RFC 9567 + def __init__(self, agent_domain: dns.name.Name): + super().__init__(OptionType.REPORTCHANNEL) + self.agent_domain = agent_domain + + def to_wire(self, file: Any = None) -> Optional[bytes]: + return self.agent_domain.to_wire(file) + + def to_text(self) -> str: + return "REPORTCHANNEL " + self.agent_domain.to_text() + + @classmethod + def from_wire_parser( + cls, otype: Union[OptionType, str], parser: dns.wire.Parser + ) -> Option: + return cls(parser.get_name()) + + +_type_to_class: Dict[OptionType, Any] = { + OptionType.ECS: ECSOption, + OptionType.EDE: EDEOption, + OptionType.NSID: NSIDOption, + OptionType.COOKIE: CookieOption, + OptionType.REPORTCHANNEL: ReportChannelOption, +} + + +def get_option_class(otype: OptionType) -> Any: + """Return the class for the specified option type. + + The GenericOption class is used if a more specific class is not + known. + """ + + cls = _type_to_class.get(otype) + if cls is None: + cls = GenericOption + return cls + + +def option_from_wire_parser( + otype: Union[OptionType, str], parser: "dns.wire.Parser" +) -> Option: + """Build an EDNS option object from wire format. + + *otype*, an ``int``, is the option type. + + *parser*, a ``dns.wire.Parser``, the parser, which should be + restricted to the option length. + + Returns an instance of a subclass of ``dns.edns.Option``. + """ + otype = OptionType.make(otype) + cls = get_option_class(otype) + return cls.from_wire_parser(otype, parser) + + +def option_from_wire( + otype: Union[OptionType, str], wire: bytes, current: int, olen: int +) -> Option: + """Build an EDNS option object from wire format. + + *otype*, an ``int``, is the option type. + + *wire*, a ``bytes``, is the wire-format message. + + *current*, an ``int``, is the offset in *wire* of the beginning + of the rdata. + + *olen*, an ``int``, is the length of the wire-format option data + + Returns an instance of a subclass of ``dns.edns.Option``. + """ + parser = dns.wire.Parser(wire, current) + with parser.restrict_to(olen): + return option_from_wire_parser(otype, parser) + + +def register_type(implementation: Any, otype: OptionType) -> None: + """Register the implementation of an option type. + + *implementation*, a ``class``, is a subclass of ``dns.edns.Option``. + + *otype*, an ``int``, is the option type. + """ + + _type_to_class[otype] = implementation + + +### BEGIN generated OptionType constants + +NSID = OptionType.NSID +DAU = OptionType.DAU +DHU = OptionType.DHU +N3U = OptionType.N3U +ECS = OptionType.ECS +EXPIRE = OptionType.EXPIRE +COOKIE = OptionType.COOKIE +KEEPALIVE = OptionType.KEEPALIVE +PADDING = OptionType.PADDING +CHAIN = OptionType.CHAIN +EDE = OptionType.EDE +REPORTCHANNEL = OptionType.REPORTCHANNEL + +### END generated OptionType constants diff --git a/.venv/lib/python3.9/site-packages/dns/entropy.py b/.venv/lib/python3.9/site-packages/dns/entropy.py new file mode 100644 index 0000000..4dcdc62 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/entropy.py @@ -0,0 +1,130 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2009-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import hashlib +import os +import random +import threading +import time +from typing import Any, Optional + + +class EntropyPool: + # This is an entropy pool for Python implementations that do not + # have a working SystemRandom. I'm not sure there are any, but + # leaving this code doesn't hurt anything as the library code + # is used if present. + + def __init__(self, seed: Optional[bytes] = None): + self.pool_index = 0 + self.digest: Optional[bytearray] = None + self.next_byte = 0 + self.lock = threading.Lock() + self.hash = hashlib.sha1() + self.hash_len = 20 + self.pool = bytearray(b"\0" * self.hash_len) + if seed is not None: + self._stir(seed) + self.seeded = True + self.seed_pid = os.getpid() + else: + self.seeded = False + self.seed_pid = 0 + + def _stir(self, entropy: bytes) -> None: + for c in entropy: + if self.pool_index == self.hash_len: + self.pool_index = 0 + b = c & 0xFF + self.pool[self.pool_index] ^= b + self.pool_index += 1 + + def stir(self, entropy: bytes) -> None: + with self.lock: + self._stir(entropy) + + def _maybe_seed(self) -> None: + if not self.seeded or self.seed_pid != os.getpid(): + try: + seed = os.urandom(16) + except Exception: # pragma: no cover + try: + with open("/dev/urandom", "rb", 0) as r: + seed = r.read(16) + except Exception: + seed = str(time.time()).encode() + self.seeded = True + self.seed_pid = os.getpid() + self.digest = None + seed = bytearray(seed) + self._stir(seed) + + def random_8(self) -> int: + with self.lock: + self._maybe_seed() + if self.digest is None or self.next_byte == self.hash_len: + self.hash.update(bytes(self.pool)) + self.digest = bytearray(self.hash.digest()) + self._stir(self.digest) + self.next_byte = 0 + value = self.digest[self.next_byte] + self.next_byte += 1 + return value + + def random_16(self) -> int: + return self.random_8() * 256 + self.random_8() + + def random_32(self) -> int: + return self.random_16() * 65536 + self.random_16() + + def random_between(self, first: int, last: int) -> int: + size = last - first + 1 + if size > 4294967296: + raise ValueError("too big") + if size > 65536: + rand = self.random_32 + max = 4294967295 + elif size > 256: + rand = self.random_16 + max = 65535 + else: + rand = self.random_8 + max = 255 + return first + size * rand() // (max + 1) + + +pool = EntropyPool() + +system_random: Optional[Any] +try: + system_random = random.SystemRandom() +except Exception: # pragma: no cover + system_random = None + + +def random_16() -> int: + if system_random is not None: + return system_random.randrange(0, 65536) + else: + return pool.random_16() + + +def between(first: int, last: int) -> int: + if system_random is not None: + return system_random.randrange(first, last + 1) + else: + return pool.random_between(first, last) diff --git a/.venv/lib/python3.9/site-packages/dns/enum.py b/.venv/lib/python3.9/site-packages/dns/enum.py new file mode 100644 index 0000000..71461f1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/enum.py @@ -0,0 +1,116 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import enum +from typing import Type, TypeVar, Union + +TIntEnum = TypeVar("TIntEnum", bound="IntEnum") + + +class IntEnum(enum.IntEnum): + @classmethod + def _missing_(cls, value): + cls._check_value(value) + val = int.__new__(cls, value) + val._name_ = cls._extra_to_text(value, None) or f"{cls._prefix()}{value}" + val._value_ = value + return val + + @classmethod + def _check_value(cls, value): + max = cls._maximum() + if not isinstance(value, int): + raise TypeError + if value < 0 or value > max: + name = cls._short_name() + raise ValueError(f"{name} must be an int between >= 0 and <= {max}") + + @classmethod + def from_text(cls: Type[TIntEnum], text: str) -> TIntEnum: + text = text.upper() + try: + return cls[text] + except KeyError: + pass + value = cls._extra_from_text(text) + if value: + return value + prefix = cls._prefix() + if text.startswith(prefix) and text[len(prefix) :].isdigit(): + value = int(text[len(prefix) :]) + cls._check_value(value) + try: + return cls(value) + except ValueError: + return value + raise cls._unknown_exception_class() + + @classmethod + def to_text(cls: Type[TIntEnum], value: int) -> str: + cls._check_value(value) + try: + text = cls(value).name + except ValueError: + text = None + text = cls._extra_to_text(value, text) + if text is None: + text = f"{cls._prefix()}{value}" + return text + + @classmethod + def make(cls: Type[TIntEnum], value: Union[int, str]) -> TIntEnum: + """Convert text or a value into an enumerated type, if possible. + + *value*, the ``int`` or ``str`` to convert. + + Raises a class-specific exception if a ``str`` is provided that + cannot be converted. + + Raises ``ValueError`` if the value is out of range. + + Returns an enumeration from the calling class corresponding to the + value, if one is defined, or an ``int`` otherwise. + """ + + if isinstance(value, str): + return cls.from_text(value) + cls._check_value(value) + return cls(value) + + @classmethod + def _maximum(cls): + raise NotImplementedError # pragma: no cover + + @classmethod + def _short_name(cls): + return cls.__name__.lower() + + @classmethod + def _prefix(cls): + return "" + + @classmethod + def _extra_from_text(cls, text): # pylint: disable=W0613 + return None + + @classmethod + def _extra_to_text(cls, value, current_text): # pylint: disable=W0613 + return current_text + + @classmethod + def _unknown_exception_class(cls): + return ValueError diff --git a/.venv/lib/python3.9/site-packages/dns/exception.py b/.venv/lib/python3.9/site-packages/dns/exception.py new file mode 100644 index 0000000..223f2d6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/exception.py @@ -0,0 +1,169 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Common DNS Exceptions. + +Dnspython modules may also define their own exceptions, which will +always be subclasses of ``DNSException``. +""" + + +from typing import Optional, Set + + +class DNSException(Exception): + """Abstract base class shared by all dnspython exceptions. + + It supports two basic modes of operation: + + a) Old/compatible mode is used if ``__init__`` was called with + empty *kwargs*. In compatible mode all *args* are passed + to the standard Python Exception class as before and all *args* are + printed by the standard ``__str__`` implementation. Class variable + ``msg`` (or doc string if ``msg`` is ``None``) is returned from ``str()`` + if *args* is empty. + + b) New/parametrized mode is used if ``__init__`` was called with + non-empty *kwargs*. + In the new mode *args* must be empty and all kwargs must match + those set in class variable ``supp_kwargs``. All kwargs are stored inside + ``self.kwargs`` and used in a new ``__str__`` implementation to construct + a formatted message based on the ``fmt`` class variable, a ``string``. + + In the simplest case it is enough to override the ``supp_kwargs`` + and ``fmt`` class variables to get nice parametrized messages. + """ + + msg: Optional[str] = None # non-parametrized message + supp_kwargs: Set[str] = set() # accepted parameters for _fmt_kwargs (sanity check) + fmt: Optional[str] = None # message parametrized with results from _fmt_kwargs + + def __init__(self, *args, **kwargs): + self._check_params(*args, **kwargs) + if kwargs: + # This call to a virtual method from __init__ is ok in our usage + self.kwargs = self._check_kwargs(**kwargs) # lgtm[py/init-calls-subclass] + self.msg = str(self) + else: + self.kwargs = dict() # defined but empty for old mode exceptions + if self.msg is None: + # doc string is better implicit message than empty string + self.msg = self.__doc__ + if args: + super().__init__(*args) + else: + super().__init__(self.msg) + + def _check_params(self, *args, **kwargs): + """Old exceptions supported only args and not kwargs. + + For sanity we do not allow to mix old and new behavior.""" + if args or kwargs: + assert bool(args) != bool( + kwargs + ), "keyword arguments are mutually exclusive with positional args" + + def _check_kwargs(self, **kwargs): + if kwargs: + assert ( + set(kwargs.keys()) == self.supp_kwargs + ), f"following set of keyword args is required: {self.supp_kwargs}" + return kwargs + + def _fmt_kwargs(self, **kwargs): + """Format kwargs before printing them. + + Resulting dictionary has to have keys necessary for str.format call + on fmt class variable. + """ + fmtargs = {} + for kw, data in kwargs.items(): + if isinstance(data, (list, set)): + # convert list of to list of str() + fmtargs[kw] = list(map(str, data)) + if len(fmtargs[kw]) == 1: + # remove list brackets [] from single-item lists + fmtargs[kw] = fmtargs[kw].pop() + else: + fmtargs[kw] = data + return fmtargs + + def __str__(self): + if self.kwargs and self.fmt: + # provide custom message constructed from keyword arguments + fmtargs = self._fmt_kwargs(**self.kwargs) + return self.fmt.format(**fmtargs) + else: + # print *args directly in the same way as old DNSException + return super().__str__() + + +class FormError(DNSException): + """DNS message is malformed.""" + + +class SyntaxError(DNSException): + """Text input is malformed.""" + + +class UnexpectedEnd(SyntaxError): + """Text input ended unexpectedly.""" + + +class TooBig(DNSException): + """The DNS message is too big.""" + + +class Timeout(DNSException): + """The DNS operation timed out.""" + + supp_kwargs = {"timeout"} + fmt = "The DNS operation timed out after {timeout:.3f} seconds" + + # We do this as otherwise mypy complains about unexpected keyword argument + # idna_exception + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + +class UnsupportedAlgorithm(DNSException): + """The DNSSEC algorithm is not supported.""" + + +class AlgorithmKeyMismatch(UnsupportedAlgorithm): + """The DNSSEC algorithm is not supported for the given key type.""" + + +class ValidationFailure(DNSException): + """The DNSSEC signature is invalid.""" + + +class DeniedByPolicy(DNSException): + """Denied by DNSSEC policy.""" + + +class ExceptionWrapper: + def __init__(self, exception_class): + self.exception_class = exception_class + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + if exc_type is not None and not isinstance(exc_val, self.exception_class): + raise self.exception_class(str(exc_val)) from exc_val + return False diff --git a/.venv/lib/python3.9/site-packages/dns/flags.py b/.venv/lib/python3.9/site-packages/dns/flags.py new file mode 100644 index 0000000..4c60be1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/flags.py @@ -0,0 +1,123 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2001-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Message Flags.""" + +import enum +from typing import Any + +# Standard DNS flags + + +class Flag(enum.IntFlag): + #: Query Response + QR = 0x8000 + #: Authoritative Answer + AA = 0x0400 + #: Truncated Response + TC = 0x0200 + #: Recursion Desired + RD = 0x0100 + #: Recursion Available + RA = 0x0080 + #: Authentic Data + AD = 0x0020 + #: Checking Disabled + CD = 0x0010 + + +# EDNS flags + + +class EDNSFlag(enum.IntFlag): + #: DNSSEC answer OK + DO = 0x8000 + + +def _from_text(text: str, enum_class: Any) -> int: + flags = 0 + tokens = text.split() + for t in tokens: + flags |= enum_class[t.upper()] + return flags + + +def _to_text(flags: int, enum_class: Any) -> str: + text_flags = [] + for k, v in enum_class.__members__.items(): + if flags & v != 0: + text_flags.append(k) + return " ".join(text_flags) + + +def from_text(text: str) -> int: + """Convert a space-separated list of flag text values into a flags + value. + + Returns an ``int`` + """ + + return _from_text(text, Flag) + + +def to_text(flags: int) -> str: + """Convert a flags value into a space-separated list of flag text + values. + + Returns a ``str``. + """ + + return _to_text(flags, Flag) + + +def edns_from_text(text: str) -> int: + """Convert a space-separated list of EDNS flag text values into a EDNS + flags value. + + Returns an ``int`` + """ + + return _from_text(text, EDNSFlag) + + +def edns_to_text(flags: int) -> str: + """Convert an EDNS flags value into a space-separated list of EDNS flag + text values. + + Returns a ``str``. + """ + + return _to_text(flags, EDNSFlag) + + +### BEGIN generated Flag constants + +QR = Flag.QR +AA = Flag.AA +TC = Flag.TC +RD = Flag.RD +RA = Flag.RA +AD = Flag.AD +CD = Flag.CD + +### END generated Flag constants + +### BEGIN generated EDNSFlag constants + +DO = EDNSFlag.DO + +### END generated EDNSFlag constants diff --git a/.venv/lib/python3.9/site-packages/dns/grange.py b/.venv/lib/python3.9/site-packages/dns/grange.py new file mode 100644 index 0000000..a967ca4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/grange.py @@ -0,0 +1,72 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2012-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS GENERATE range conversion.""" + +from typing import Tuple + +import dns + + +def from_text(text: str) -> Tuple[int, int, int]: + """Convert the text form of a range in a ``$GENERATE`` statement to an + integer. + + *text*, a ``str``, the textual range in ``$GENERATE`` form. + + Returns a tuple of three ``int`` values ``(start, stop, step)``. + """ + + start = -1 + stop = -1 + step = 1 + cur = "" + state = 0 + # state 0 1 2 + # x - y / z + + if text and text[0] == "-": + raise dns.exception.SyntaxError("Start cannot be a negative number") + + for c in text: + if c == "-" and state == 0: + start = int(cur) + cur = "" + state = 1 + elif c == "/": + stop = int(cur) + cur = "" + state = 2 + elif c.isdigit(): + cur += c + else: + raise dns.exception.SyntaxError(f"Could not parse {c}") + + if state == 0: + raise dns.exception.SyntaxError("no stop value specified") + elif state == 1: + stop = int(cur) + else: + assert state == 2 + step = int(cur) + + assert step >= 1 + assert start >= 0 + if start > stop: + raise dns.exception.SyntaxError("start must be <= stop") + + return (start, stop, step) diff --git a/.venv/lib/python3.9/site-packages/dns/immutable.py b/.venv/lib/python3.9/site-packages/dns/immutable.py new file mode 100644 index 0000000..36b0362 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/immutable.py @@ -0,0 +1,68 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import collections.abc +from typing import Any, Callable + +from dns._immutable_ctx import immutable + + +@immutable +class Dict(collections.abc.Mapping): # lgtm[py/missing-equals] + def __init__( + self, + dictionary: Any, + no_copy: bool = False, + map_factory: Callable[[], collections.abc.MutableMapping] = dict, + ): + """Make an immutable dictionary from the specified dictionary. + + If *no_copy* is `True`, then *dictionary* will be wrapped instead + of copied. Only set this if you are sure there will be no external + references to the dictionary. + """ + if no_copy and isinstance(dictionary, collections.abc.MutableMapping): + self._odict = dictionary + else: + self._odict = map_factory() + self._odict.update(dictionary) + self._hash = None + + def __getitem__(self, key): + return self._odict.__getitem__(key) + + def __hash__(self): # pylint: disable=invalid-hash-returned + if self._hash is None: + h = 0 + for key in sorted(self._odict.keys()): + h ^= hash(key) + object.__setattr__(self, "_hash", h) + # this does return an int, but pylint doesn't figure that out + return self._hash + + def __len__(self): + return len(self._odict) + + def __iter__(self): + return iter(self._odict) + + +def constify(o: Any) -> Any: + """ + Convert mutable types to immutable types. + """ + if isinstance(o, bytearray): + return bytes(o) + if isinstance(o, tuple): + try: + hash(o) + return o + except Exception: + return tuple(constify(elt) for elt in o) + if isinstance(o, list): + return tuple(constify(elt) for elt in o) + if isinstance(o, dict): + cdict = dict() + for k, v in o.items(): + cdict[k] = constify(v) + return Dict(cdict, True) + return o diff --git a/.venv/lib/python3.9/site-packages/dns/inet.py b/.venv/lib/python3.9/site-packages/dns/inet.py new file mode 100644 index 0000000..4a03f99 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/inet.py @@ -0,0 +1,197 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Generic Internet address helper functions.""" + +import socket +from typing import Any, Optional, Tuple + +import dns.ipv4 +import dns.ipv6 + +# We assume that AF_INET and AF_INET6 are always defined. We keep +# these here for the benefit of any old code (unlikely though that +# is!). +AF_INET = socket.AF_INET +AF_INET6 = socket.AF_INET6 + + +def inet_pton(family: int, text: str) -> bytes: + """Convert the textual form of a network address into its binary form. + + *family* is an ``int``, the address family. + + *text* is a ``str``, the textual address. + + Raises ``NotImplementedError`` if the address family specified is not + implemented. + + Returns a ``bytes``. + """ + + if family == AF_INET: + return dns.ipv4.inet_aton(text) + elif family == AF_INET6: + return dns.ipv6.inet_aton(text, True) + else: + raise NotImplementedError + + +def inet_ntop(family: int, address: bytes) -> str: + """Convert the binary form of a network address into its textual form. + + *family* is an ``int``, the address family. + + *address* is a ``bytes``, the network address in binary form. + + Raises ``NotImplementedError`` if the address family specified is not + implemented. + + Returns a ``str``. + """ + + if family == AF_INET: + return dns.ipv4.inet_ntoa(address) + elif family == AF_INET6: + return dns.ipv6.inet_ntoa(address) + else: + raise NotImplementedError + + +def af_for_address(text: str) -> int: + """Determine the address family of a textual-form network address. + + *text*, a ``str``, the textual address. + + Raises ``ValueError`` if the address family cannot be determined + from the input. + + Returns an ``int``. + """ + + try: + dns.ipv4.inet_aton(text) + return AF_INET + except Exception: + try: + dns.ipv6.inet_aton(text, True) + return AF_INET6 + except Exception: + raise ValueError + + +def is_multicast(text: str) -> bool: + """Is the textual-form network address a multicast address? + + *text*, a ``str``, the textual address. + + Raises ``ValueError`` if the address family cannot be determined + from the input. + + Returns a ``bool``. + """ + + try: + first = dns.ipv4.inet_aton(text)[0] + return first >= 224 and first <= 239 + except Exception: + try: + first = dns.ipv6.inet_aton(text, True)[0] + return first == 255 + except Exception: + raise ValueError + + +def is_address(text: str) -> bool: + """Is the specified string an IPv4 or IPv6 address? + + *text*, a ``str``, the textual address. + + Returns a ``bool``. + """ + + try: + dns.ipv4.inet_aton(text) + return True + except Exception: + try: + dns.ipv6.inet_aton(text, True) + return True + except Exception: + return False + + +def low_level_address_tuple( + high_tuple: Tuple[str, int], af: Optional[int] = None +) -> Any: + """Given a "high-level" address tuple, i.e. + an (address, port) return the appropriate "low-level" address tuple + suitable for use in socket calls. + + If an *af* other than ``None`` is provided, it is assumed the + address in the high-level tuple is valid and has that af. If af + is ``None``, then af_for_address will be called. + """ + address, port = high_tuple + if af is None: + af = af_for_address(address) + if af == AF_INET: + return (address, port) + elif af == AF_INET6: + i = address.find("%") + if i < 0: + # no scope, shortcut! + return (address, port, 0, 0) + # try to avoid getaddrinfo() + addrpart = address[:i] + scope = address[i + 1 :] + if scope.isdigit(): + return (addrpart, port, 0, int(scope)) + try: + return (addrpart, port, 0, socket.if_nametoindex(scope)) + except AttributeError: # pragma: no cover (we can't really test this) + ai_flags = socket.AI_NUMERICHOST + ((*_, tup), *_) = socket.getaddrinfo(address, port, flags=ai_flags) + return tup + else: + raise NotImplementedError(f"unknown address family {af}") + + +def any_for_af(af): + """Return the 'any' address for the specified address family.""" + if af == socket.AF_INET: + return "0.0.0.0" + elif af == socket.AF_INET6: + return "::" + raise NotImplementedError(f"unknown address family {af}") + + +def canonicalize(text: str) -> str: + """Verify that *address* is a valid text form IPv4 or IPv6 address and return its + canonical text form. IPv6 addresses with scopes are rejected. + + *text*, a ``str``, the address in textual form. + + Raises ``ValueError`` if the text is not valid. + """ + try: + return dns.ipv6.canonicalize(text) + except Exception: + try: + return dns.ipv4.canonicalize(text) + except Exception: + raise ValueError diff --git a/.venv/lib/python3.9/site-packages/dns/ipv4.py b/.venv/lib/python3.9/site-packages/dns/ipv4.py new file mode 100644 index 0000000..65ee69c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/ipv4.py @@ -0,0 +1,77 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""IPv4 helper functions.""" + +import struct +from typing import Union + +import dns.exception + + +def inet_ntoa(address: bytes) -> str: + """Convert an IPv4 address in binary form to text form. + + *address*, a ``bytes``, the IPv4 address in binary form. + + Returns a ``str``. + """ + + if len(address) != 4: + raise dns.exception.SyntaxError + return "%u.%u.%u.%u" % (address[0], address[1], address[2], address[3]) + + +def inet_aton(text: Union[str, bytes]) -> bytes: + """Convert an IPv4 address in text form to binary form. + + *text*, a ``str`` or ``bytes``, the IPv4 address in textual form. + + Returns a ``bytes``. + """ + + if not isinstance(text, bytes): + btext = text.encode() + else: + btext = text + parts = btext.split(b".") + if len(parts) != 4: + raise dns.exception.SyntaxError + for part in parts: + if not part.isdigit(): + raise dns.exception.SyntaxError + if len(part) > 1 and part[0] == ord("0"): + # No leading zeros + raise dns.exception.SyntaxError + try: + b = [int(part) for part in parts] + return struct.pack("BBBB", *b) + except Exception: + raise dns.exception.SyntaxError + + +def canonicalize(text: Union[str, bytes]) -> str: + """Verify that *address* is a valid text form IPv4 address and return its + canonical text form. + + *text*, a ``str`` or ``bytes``, the IPv4 address in textual form. + + Raises ``dns.exception.SyntaxError`` if the text is not valid. + """ + # Note that inet_aton() only accepts canonial form, but we still run through + # inet_ntoa() to ensure the output is a str. + return dns.ipv4.inet_ntoa(dns.ipv4.inet_aton(text)) diff --git a/.venv/lib/python3.9/site-packages/dns/ipv6.py b/.venv/lib/python3.9/site-packages/dns/ipv6.py new file mode 100644 index 0000000..4dd1d1c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/ipv6.py @@ -0,0 +1,217 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""IPv6 helper functions.""" + +import binascii +import re +from typing import List, Union + +import dns.exception +import dns.ipv4 + +_leading_zero = re.compile(r"0+([0-9a-f]+)") + + +def inet_ntoa(address: bytes) -> str: + """Convert an IPv6 address in binary form to text form. + + *address*, a ``bytes``, the IPv6 address in binary form. + + Raises ``ValueError`` if the address isn't 16 bytes long. + Returns a ``str``. + """ + + if len(address) != 16: + raise ValueError("IPv6 addresses are 16 bytes long") + hex = binascii.hexlify(address) + chunks = [] + i = 0 + l = len(hex) + while i < l: + chunk = hex[i : i + 4].decode() + # strip leading zeros. we do this with an re instead of + # with lstrip() because lstrip() didn't support chars until + # python 2.2.2 + m = _leading_zero.match(chunk) + if m is not None: + chunk = m.group(1) + chunks.append(chunk) + i += 4 + # + # Compress the longest subsequence of 0-value chunks to :: + # + best_start = 0 + best_len = 0 + start = -1 + last_was_zero = False + for i in range(8): + if chunks[i] != "0": + if last_was_zero: + end = i + current_len = end - start + if current_len > best_len: + best_start = start + best_len = current_len + last_was_zero = False + elif not last_was_zero: + start = i + last_was_zero = True + if last_was_zero: + end = 8 + current_len = end - start + if current_len > best_len: + best_start = start + best_len = current_len + if best_len > 1: + if best_start == 0 and (best_len == 6 or best_len == 5 and chunks[5] == "ffff"): + # We have an embedded IPv4 address + if best_len == 6: + prefix = "::" + else: + prefix = "::ffff:" + thex = prefix + dns.ipv4.inet_ntoa(address[12:]) + else: + thex = ( + ":".join(chunks[:best_start]) + + "::" + + ":".join(chunks[best_start + best_len :]) + ) + else: + thex = ":".join(chunks) + return thex + + +_v4_ending = re.compile(rb"(.*):(\d+\.\d+\.\d+\.\d+)$") +_colon_colon_start = re.compile(rb"::.*") +_colon_colon_end = re.compile(rb".*::$") + + +def inet_aton(text: Union[str, bytes], ignore_scope: bool = False) -> bytes: + """Convert an IPv6 address in text form to binary form. + + *text*, a ``str`` or ``bytes``, the IPv6 address in textual form. + + *ignore_scope*, a ``bool``. If ``True``, a scope will be ignored. + If ``False``, the default, it is an error for a scope to be present. + + Returns a ``bytes``. + """ + + # + # Our aim here is not something fast; we just want something that works. + # + if not isinstance(text, bytes): + btext = text.encode() + else: + btext = text + + if ignore_scope: + parts = btext.split(b"%") + l = len(parts) + if l == 2: + btext = parts[0] + elif l > 2: + raise dns.exception.SyntaxError + + if btext == b"": + raise dns.exception.SyntaxError + elif btext.endswith(b":") and not btext.endswith(b"::"): + raise dns.exception.SyntaxError + elif btext.startswith(b":") and not btext.startswith(b"::"): + raise dns.exception.SyntaxError + elif btext == b"::": + btext = b"0::" + # + # Get rid of the icky dot-quad syntax if we have it. + # + m = _v4_ending.match(btext) + if m is not None: + b = dns.ipv4.inet_aton(m.group(2)) + btext = ( + f"{m.group(1).decode()}:{b[0]:02x}{b[1]:02x}:{b[2]:02x}{b[3]:02x}" + ).encode() + # + # Try to turn '::' into ':'; if no match try to + # turn '::' into ':' + # + m = _colon_colon_start.match(btext) + if m is not None: + btext = btext[1:] + else: + m = _colon_colon_end.match(btext) + if m is not None: + btext = btext[:-1] + # + # Now canonicalize into 8 chunks of 4 hex digits each + # + chunks = btext.split(b":") + l = len(chunks) + if l > 8: + raise dns.exception.SyntaxError + seen_empty = False + canonical: List[bytes] = [] + for c in chunks: + if c == b"": + if seen_empty: + raise dns.exception.SyntaxError + seen_empty = True + for _ in range(0, 8 - l + 1): + canonical.append(b"0000") + else: + lc = len(c) + if lc > 4: + raise dns.exception.SyntaxError + if lc != 4: + c = (b"0" * (4 - lc)) + c + canonical.append(c) + if l < 8 and not seen_empty: + raise dns.exception.SyntaxError + btext = b"".join(canonical) + + # + # Finally we can go to binary. + # + try: + return binascii.unhexlify(btext) + except (binascii.Error, TypeError): + raise dns.exception.SyntaxError + + +_mapped_prefix = b"\x00" * 10 + b"\xff\xff" + + +def is_mapped(address: bytes) -> bool: + """Is the specified address a mapped IPv4 address? + + *address*, a ``bytes`` is an IPv6 address in binary form. + + Returns a ``bool``. + """ + + return address.startswith(_mapped_prefix) + + +def canonicalize(text: Union[str, bytes]) -> str: + """Verify that *address* is a valid text form IPv6 address and return its + canonical text form. Addresses with scopes are rejected. + + *text*, a ``str`` or ``bytes``, the IPv6 address in textual form. + + Raises ``dns.exception.SyntaxError`` if the text is not valid. + """ + return dns.ipv6.inet_ntoa(dns.ipv6.inet_aton(text)) diff --git a/.venv/lib/python3.9/site-packages/dns/message.py b/.venv/lib/python3.9/site-packages/dns/message.py new file mode 100644 index 0000000..e978a0a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/message.py @@ -0,0 +1,1933 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2001-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Messages""" + +import contextlib +import enum +import io +import time +from typing import Any, Dict, List, Optional, Tuple, Union, cast + +import dns.edns +import dns.entropy +import dns.enum +import dns.exception +import dns.flags +import dns.name +import dns.opcode +import dns.rcode +import dns.rdata +import dns.rdataclass +import dns.rdatatype +import dns.rdtypes.ANY.OPT +import dns.rdtypes.ANY.TSIG +import dns.renderer +import dns.rrset +import dns.tsig +import dns.ttl +import dns.wire + + +class ShortHeader(dns.exception.FormError): + """The DNS packet passed to from_wire() is too short.""" + + +class TrailingJunk(dns.exception.FormError): + """The DNS packet passed to from_wire() has extra junk at the end of it.""" + + +class UnknownHeaderField(dns.exception.DNSException): + """The header field name was not recognized when converting from text + into a message.""" + + +class BadEDNS(dns.exception.FormError): + """An OPT record occurred somewhere other than + the additional data section.""" + + +class BadTSIG(dns.exception.FormError): + """A TSIG record occurred somewhere other than the end of + the additional data section.""" + + +class UnknownTSIGKey(dns.exception.DNSException): + """A TSIG with an unknown key was received.""" + + +class Truncated(dns.exception.DNSException): + """The truncated flag is set.""" + + supp_kwargs = {"message"} + + # We do this as otherwise mypy complains about unexpected keyword argument + # idna_exception + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def message(self): + """As much of the message as could be processed. + + Returns a ``dns.message.Message``. + """ + return self.kwargs["message"] + + +class NotQueryResponse(dns.exception.DNSException): + """Message is not a response to a query.""" + + +class ChainTooLong(dns.exception.DNSException): + """The CNAME chain is too long.""" + + +class AnswerForNXDOMAIN(dns.exception.DNSException): + """The rcode is NXDOMAIN but an answer was found.""" + + +class NoPreviousName(dns.exception.SyntaxError): + """No previous name was known.""" + + +class MessageSection(dns.enum.IntEnum): + """Message sections""" + + QUESTION = 0 + ANSWER = 1 + AUTHORITY = 2 + ADDITIONAL = 3 + + @classmethod + def _maximum(cls): + return 3 + + +class MessageError: + def __init__(self, exception: Exception, offset: int): + self.exception = exception + self.offset = offset + + +DEFAULT_EDNS_PAYLOAD = 1232 +MAX_CHAIN = 16 + +IndexKeyType = Tuple[ + int, + dns.name.Name, + dns.rdataclass.RdataClass, + dns.rdatatype.RdataType, + Optional[dns.rdatatype.RdataType], + Optional[dns.rdataclass.RdataClass], +] +IndexType = Dict[IndexKeyType, dns.rrset.RRset] +SectionType = Union[int, str, List[dns.rrset.RRset]] + + +class Message: + """A DNS message.""" + + _section_enum = MessageSection + + def __init__(self, id: Optional[int] = None): + if id is None: + self.id = dns.entropy.random_16() + else: + self.id = id + self.flags = 0 + self.sections: List[List[dns.rrset.RRset]] = [[], [], [], []] + self.opt: Optional[dns.rrset.RRset] = None + self.request_payload = 0 + self.pad = 0 + self.keyring: Any = None + self.tsig: Optional[dns.rrset.RRset] = None + self.request_mac = b"" + self.xfr = False + self.origin: Optional[dns.name.Name] = None + self.tsig_ctx: Optional[Any] = None + self.index: IndexType = {} + self.errors: List[MessageError] = [] + self.time = 0.0 + self.wire: Optional[bytes] = None + + @property + def question(self) -> List[dns.rrset.RRset]: + """The question section.""" + return self.sections[0] + + @question.setter + def question(self, v): + self.sections[0] = v + + @property + def answer(self) -> List[dns.rrset.RRset]: + """The answer section.""" + return self.sections[1] + + @answer.setter + def answer(self, v): + self.sections[1] = v + + @property + def authority(self) -> List[dns.rrset.RRset]: + """The authority section.""" + return self.sections[2] + + @authority.setter + def authority(self, v): + self.sections[2] = v + + @property + def additional(self) -> List[dns.rrset.RRset]: + """The additional data section.""" + return self.sections[3] + + @additional.setter + def additional(self, v): + self.sections[3] = v + + def __repr__(self): + return "" + + def __str__(self): + return self.to_text() + + def to_text( + self, + origin: Optional[dns.name.Name] = None, + relativize: bool = True, + **kw: Dict[str, Any], + ) -> str: + """Convert the message to text. + + The *origin*, *relativize*, and any other keyword + arguments are passed to the RRset ``to_wire()`` method. + + Returns a ``str``. + """ + + s = io.StringIO() + s.write("id %d\n" % self.id) + s.write(f"opcode {dns.opcode.to_text(self.opcode())}\n") + s.write(f"rcode {dns.rcode.to_text(self.rcode())}\n") + s.write(f"flags {dns.flags.to_text(self.flags)}\n") + if self.edns >= 0: + s.write(f"edns {self.edns}\n") + if self.ednsflags != 0: + s.write(f"eflags {dns.flags.edns_to_text(self.ednsflags)}\n") + s.write("payload %d\n" % self.payload) + for opt in self.options: + s.write(f"option {opt.to_text()}\n") + for name, which in self._section_enum.__members__.items(): + s.write(f";{name}\n") + for rrset in self.section_from_number(which): + s.write(rrset.to_text(origin, relativize, **kw)) + s.write("\n") + # + # We strip off the final \n so the caller can print the result without + # doing weird things to get around eccentricities in Python print + # formatting + # + return s.getvalue()[:-1] + + def __eq__(self, other): + """Two messages are equal if they have the same content in the + header, question, answer, and authority sections. + + Returns a ``bool``. + """ + + if not isinstance(other, Message): + return False + if self.id != other.id: + return False + if self.flags != other.flags: + return False + for i, section in enumerate(self.sections): + other_section = other.sections[i] + for n in section: + if n not in other_section: + return False + for n in other_section: + if n not in section: + return False + return True + + def __ne__(self, other): + return not self.__eq__(other) + + def is_response(self, other: "Message") -> bool: + """Is *other*, also a ``dns.message.Message``, a response to this + message? + + Returns a ``bool``. + """ + + if ( + other.flags & dns.flags.QR == 0 + or self.id != other.id + or dns.opcode.from_flags(self.flags) != dns.opcode.from_flags(other.flags) + ): + return False + if other.rcode() in { + dns.rcode.FORMERR, + dns.rcode.SERVFAIL, + dns.rcode.NOTIMP, + dns.rcode.REFUSED, + }: + # We don't check the question section in these cases if + # the other question section is empty, even though they + # still really ought to have a question section. + if len(other.question) == 0: + return True + if dns.opcode.is_update(self.flags): + # This is assuming the "sender doesn't include anything + # from the update", but we don't care to check the other + # case, which is that all the sections are returned and + # identical. + return True + for n in self.question: + if n not in other.question: + return False + for n in other.question: + if n not in self.question: + return False + return True + + def section_number(self, section: List[dns.rrset.RRset]) -> int: + """Return the "section number" of the specified section for use + in indexing. + + *section* is one of the section attributes of this message. + + Raises ``ValueError`` if the section isn't known. + + Returns an ``int``. + """ + + for i, our_section in enumerate(self.sections): + if section is our_section: + return self._section_enum(i) + raise ValueError("unknown section") + + def section_from_number(self, number: int) -> List[dns.rrset.RRset]: + """Return the section list associated with the specified section + number. + + *number* is a section number `int` or the text form of a section + name. + + Raises ``ValueError`` if the section isn't known. + + Returns a ``list``. + """ + + section = self._section_enum.make(number) + return self.sections[section] + + def find_rrset( + self, + section: SectionType, + name: dns.name.Name, + rdclass: dns.rdataclass.RdataClass, + rdtype: dns.rdatatype.RdataType, + covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, + deleting: Optional[dns.rdataclass.RdataClass] = None, + create: bool = False, + force_unique: bool = False, + idna_codec: Optional[dns.name.IDNACodec] = None, + ) -> dns.rrset.RRset: + """Find the RRset with the given attributes in the specified section. + + *section*, an ``int`` section number, a ``str`` section name, or one of + the section attributes of this message. This specifies the + the section of the message to search. For example:: + + my_message.find_rrset(my_message.answer, name, rdclass, rdtype) + my_message.find_rrset(dns.message.ANSWER, name, rdclass, rdtype) + my_message.find_rrset("ANSWER", name, rdclass, rdtype) + + *name*, a ``dns.name.Name`` or ``str``, the name of the RRset. + + *rdclass*, an ``int`` or ``str``, the class of the RRset. + + *rdtype*, an ``int`` or ``str``, the type of the RRset. + + *covers*, an ``int`` or ``str``, the covers value of the RRset. + The default is ``dns.rdatatype.NONE``. + + *deleting*, an ``int``, ``str``, or ``None``, the deleting value of the + RRset. The default is ``None``. + + *create*, a ``bool``. If ``True``, create the RRset if it is not found. + The created RRset is appended to *section*. + + *force_unique*, a ``bool``. If ``True`` and *create* is also ``True``, + create a new RRset regardless of whether a matching RRset exists + already. The default is ``False``. This is useful when creating + DDNS Update messages, as order matters for them. + + *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA + encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder + is used. + + Raises ``KeyError`` if the RRset was not found and create was + ``False``. + + Returns a ``dns.rrset.RRset object``. + """ + + if isinstance(section, int): + section_number = section + section = self.section_from_number(section_number) + elif isinstance(section, str): + section_number = self._section_enum.from_text(section) + section = self.section_from_number(section_number) + else: + section_number = self.section_number(section) + if isinstance(name, str): + name = dns.name.from_text(name, idna_codec=idna_codec) + rdtype = dns.rdatatype.RdataType.make(rdtype) + rdclass = dns.rdataclass.RdataClass.make(rdclass) + covers = dns.rdatatype.RdataType.make(covers) + if deleting is not None: + deleting = dns.rdataclass.RdataClass.make(deleting) + key = (section_number, name, rdclass, rdtype, covers, deleting) + if not force_unique: + if self.index is not None: + rrset = self.index.get(key) + if rrset is not None: + return rrset + else: + for rrset in section: + if rrset.full_match(name, rdclass, rdtype, covers, deleting): + return rrset + if not create: + raise KeyError + rrset = dns.rrset.RRset(name, rdclass, rdtype, covers, deleting) + section.append(rrset) + if self.index is not None: + self.index[key] = rrset + return rrset + + def get_rrset( + self, + section: SectionType, + name: dns.name.Name, + rdclass: dns.rdataclass.RdataClass, + rdtype: dns.rdatatype.RdataType, + covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, + deleting: Optional[dns.rdataclass.RdataClass] = None, + create: bool = False, + force_unique: bool = False, + idna_codec: Optional[dns.name.IDNACodec] = None, + ) -> Optional[dns.rrset.RRset]: + """Get the RRset with the given attributes in the specified section. + + If the RRset is not found, None is returned. + + *section*, an ``int`` section number, a ``str`` section name, or one of + the section attributes of this message. This specifies the + the section of the message to search. For example:: + + my_message.get_rrset(my_message.answer, name, rdclass, rdtype) + my_message.get_rrset(dns.message.ANSWER, name, rdclass, rdtype) + my_message.get_rrset("ANSWER", name, rdclass, rdtype) + + *name*, a ``dns.name.Name`` or ``str``, the name of the RRset. + + *rdclass*, an ``int`` or ``str``, the class of the RRset. + + *rdtype*, an ``int`` or ``str``, the type of the RRset. + + *covers*, an ``int`` or ``str``, the covers value of the RRset. + The default is ``dns.rdatatype.NONE``. + + *deleting*, an ``int``, ``str``, or ``None``, the deleting value of the + RRset. The default is ``None``. + + *create*, a ``bool``. If ``True``, create the RRset if it is not found. + The created RRset is appended to *section*. + + *force_unique*, a ``bool``. If ``True`` and *create* is also ``True``, + create a new RRset regardless of whether a matching RRset exists + already. The default is ``False``. This is useful when creating + DDNS Update messages, as order matters for them. + + *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA + encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder + is used. + + Returns a ``dns.rrset.RRset object`` or ``None``. + """ + + try: + rrset = self.find_rrset( + section, + name, + rdclass, + rdtype, + covers, + deleting, + create, + force_unique, + idna_codec, + ) + except KeyError: + rrset = None + return rrset + + def section_count(self, section: SectionType) -> int: + """Returns the number of records in the specified section. + + *section*, an ``int`` section number, a ``str`` section name, or one of + the section attributes of this message. This specifies the + the section of the message to count. For example:: + + my_message.section_count(my_message.answer) + my_message.section_count(dns.message.ANSWER) + my_message.section_count("ANSWER") + """ + + if isinstance(section, int): + section_number = section + section = self.section_from_number(section_number) + elif isinstance(section, str): + section_number = self._section_enum.from_text(section) + section = self.section_from_number(section_number) + else: + section_number = self.section_number(section) + count = sum(max(1, len(rrs)) for rrs in section) + if section_number == MessageSection.ADDITIONAL: + if self.opt is not None: + count += 1 + if self.tsig is not None: + count += 1 + return count + + def _compute_opt_reserve(self) -> int: + """Compute the size required for the OPT RR, padding excluded""" + if not self.opt: + return 0 + # 1 byte for the root name, 10 for the standard RR fields + size = 11 + # This would be more efficient if options had a size() method, but we won't + # worry about that for now. We also don't worry if there is an existing padding + # option, as it is unlikely and probably harmless, as the worst case is that we + # may add another, and this seems to be legal. + for option in self.opt[0].options: + wire = option.to_wire() + # We add 4 here to account for the option type and length + size += len(wire) + 4 + if self.pad: + # Padding will be added, so again add the option type and length. + size += 4 + return size + + def _compute_tsig_reserve(self) -> int: + """Compute the size required for the TSIG RR""" + # This would be more efficient if TSIGs had a size method, but we won't + # worry about for now. Also, we can't really cope with the potential + # compressibility of the TSIG owner name, so we estimate with the uncompressed + # size. We will disable compression when TSIG and padding are both is active + # so that the padding comes out right. + if not self.tsig: + return 0 + f = io.BytesIO() + self.tsig.to_wire(f) + return len(f.getvalue()) + + def to_wire( + self, + origin: Optional[dns.name.Name] = None, + max_size: int = 0, + multi: bool = False, + tsig_ctx: Optional[Any] = None, + prepend_length: bool = False, + prefer_truncation: bool = False, + **kw: Dict[str, Any], + ) -> bytes: + """Return a string containing the message in DNS compressed wire + format. + + Additional keyword arguments are passed to the RRset ``to_wire()`` + method. + + *origin*, a ``dns.name.Name`` or ``None``, the origin to be appended + to any relative names. If ``None``, and the message has an origin + attribute that is not ``None``, then it will be used. + + *max_size*, an ``int``, the maximum size of the wire format + output; default is 0, which means "the message's request + payload, if nonzero, or 65535". + + *multi*, a ``bool``, should be set to ``True`` if this message is + part of a multiple message sequence. + + *tsig_ctx*, a ``dns.tsig.HMACTSig`` or ``dns.tsig.GSSTSig`` object, the + ongoing TSIG context, used when signing zone transfers. + + *prepend_length*, a ``bool``, should be set to ``True`` if the caller + wants the message length prepended to the message itself. This is + useful for messages sent over TCP, TLS (DoT), or QUIC (DoQ). + + *prefer_truncation*, a ``bool``, should be set to ``True`` if the caller + wants the message to be truncated if it would otherwise exceed the + maximum length. If the truncation occurs before the additional section, + the TC bit will be set. + + Raises ``dns.exception.TooBig`` if *max_size* was exceeded. + + Returns a ``bytes``. + """ + + if origin is None and self.origin is not None: + origin = self.origin + if max_size == 0: + if self.request_payload != 0: + max_size = self.request_payload + else: + max_size = 65535 + if max_size < 512: + max_size = 512 + elif max_size > 65535: + max_size = 65535 + r = dns.renderer.Renderer(self.id, self.flags, max_size, origin) + opt_reserve = self._compute_opt_reserve() + r.reserve(opt_reserve) + tsig_reserve = self._compute_tsig_reserve() + r.reserve(tsig_reserve) + try: + for rrset in self.question: + r.add_question(rrset.name, rrset.rdtype, rrset.rdclass) + for rrset in self.answer: + r.add_rrset(dns.renderer.ANSWER, rrset, **kw) + for rrset in self.authority: + r.add_rrset(dns.renderer.AUTHORITY, rrset, **kw) + for rrset in self.additional: + r.add_rrset(dns.renderer.ADDITIONAL, rrset, **kw) + except dns.exception.TooBig: + if prefer_truncation: + if r.section < dns.renderer.ADDITIONAL: + r.flags |= dns.flags.TC + else: + raise + r.release_reserved() + if self.opt is not None: + r.add_opt(self.opt, self.pad, opt_reserve, tsig_reserve) + r.write_header() + if self.tsig is not None: + (new_tsig, ctx) = dns.tsig.sign( + r.get_wire(), + self.keyring, + self.tsig[0], + int(time.time()), + self.request_mac, + tsig_ctx, + multi, + ) + self.tsig.clear() + self.tsig.add(new_tsig) + r.add_rrset(dns.renderer.ADDITIONAL, self.tsig) + r.write_header() + if multi: + self.tsig_ctx = ctx + wire = r.get_wire() + self.wire = wire + if prepend_length: + wire = len(wire).to_bytes(2, "big") + wire + return wire + + @staticmethod + def _make_tsig( + keyname, algorithm, time_signed, fudge, mac, original_id, error, other + ): + tsig = dns.rdtypes.ANY.TSIG.TSIG( + dns.rdataclass.ANY, + dns.rdatatype.TSIG, + algorithm, + time_signed, + fudge, + mac, + original_id, + error, + other, + ) + return dns.rrset.from_rdata(keyname, 0, tsig) + + def use_tsig( + self, + keyring: Any, + keyname: Optional[Union[dns.name.Name, str]] = None, + fudge: int = 300, + original_id: Optional[int] = None, + tsig_error: int = 0, + other_data: bytes = b"", + algorithm: Union[dns.name.Name, str] = dns.tsig.default_algorithm, + ) -> None: + """When sending, a TSIG signature using the specified key + should be added. + + *key*, a ``dns.tsig.Key`` is the key to use. If a key is specified, + the *keyring* and *algorithm* fields are not used. + + *keyring*, a ``dict``, ``callable`` or ``dns.tsig.Key``, is either + the TSIG keyring or key to use. + + The format of a keyring dict is a mapping from TSIG key name, as + ``dns.name.Name`` to ``dns.tsig.Key`` or a TSIG secret, a ``bytes``. + If a ``dict`` *keyring* is specified but a *keyname* is not, the key + used will be the first key in the *keyring*. Note that the order of + keys in a dictionary is not defined, so applications should supply a + keyname when a ``dict`` keyring is used, unless they know the keyring + contains only one key. If a ``callable`` keyring is specified, the + callable will be called with the message and the keyname, and is + expected to return a key. + + *keyname*, a ``dns.name.Name``, ``str`` or ``None``, the name of + this TSIG key to use; defaults to ``None``. If *keyring* is a + ``dict``, the key must be defined in it. If *keyring* is a + ``dns.tsig.Key``, this is ignored. + + *fudge*, an ``int``, the TSIG time fudge. + + *original_id*, an ``int``, the TSIG original id. If ``None``, + the message's id is used. + + *tsig_error*, an ``int``, the TSIG error code. + + *other_data*, a ``bytes``, the TSIG other data. + + *algorithm*, a ``dns.name.Name`` or ``str``, the TSIG algorithm to use. This is + only used if *keyring* is a ``dict``, and the key entry is a ``bytes``. + """ + + if isinstance(keyring, dns.tsig.Key): + key = keyring + keyname = key.name + elif callable(keyring): + key = keyring(self, keyname) + else: + if isinstance(keyname, str): + keyname = dns.name.from_text(keyname) + if keyname is None: + keyname = next(iter(keyring)) + key = keyring[keyname] + if isinstance(key, bytes): + key = dns.tsig.Key(keyname, key, algorithm) + self.keyring = key + if original_id is None: + original_id = self.id + self.tsig = self._make_tsig( + keyname, + self.keyring.algorithm, + 0, + fudge, + b"\x00" * dns.tsig.mac_sizes[self.keyring.algorithm], + original_id, + tsig_error, + other_data, + ) + + @property + def keyname(self) -> Optional[dns.name.Name]: + if self.tsig: + return self.tsig.name + else: + return None + + @property + def keyalgorithm(self) -> Optional[dns.name.Name]: + if self.tsig: + return self.tsig[0].algorithm + else: + return None + + @property + def mac(self) -> Optional[bytes]: + if self.tsig: + return self.tsig[0].mac + else: + return None + + @property + def tsig_error(self) -> Optional[int]: + if self.tsig: + return self.tsig[0].error + else: + return None + + @property + def had_tsig(self) -> bool: + return bool(self.tsig) + + @staticmethod + def _make_opt(flags=0, payload=DEFAULT_EDNS_PAYLOAD, options=None): + opt = dns.rdtypes.ANY.OPT.OPT(payload, dns.rdatatype.OPT, options or ()) + return dns.rrset.from_rdata(dns.name.root, int(flags), opt) + + def use_edns( + self, + edns: Optional[Union[int, bool]] = 0, + ednsflags: int = 0, + payload: int = DEFAULT_EDNS_PAYLOAD, + request_payload: Optional[int] = None, + options: Optional[List[dns.edns.Option]] = None, + pad: int = 0, + ) -> None: + """Configure EDNS behavior. + + *edns*, an ``int``, is the EDNS level to use. Specifying ``None``, ``False``, + or ``-1`` means "do not use EDNS", and in this case the other parameters are + ignored. Specifying ``True`` is equivalent to specifying 0, i.e. "use EDNS0". + + *ednsflags*, an ``int``, the EDNS flag values. + + *payload*, an ``int``, is the EDNS sender's payload field, which is the maximum + size of UDP datagram the sender can handle. I.e. how big a response to this + message can be. + + *request_payload*, an ``int``, is the EDNS payload size to use when sending this + message. If not specified, defaults to the value of *payload*. + + *options*, a list of ``dns.edns.Option`` objects or ``None``, the EDNS options. + + *pad*, a non-negative ``int``. If 0, the default, do not pad; otherwise add + padding bytes to make the message size a multiple of *pad*. Note that if + padding is non-zero, an EDNS PADDING option will always be added to the + message. + """ + + if edns is None or edns is False: + edns = -1 + elif edns is True: + edns = 0 + if edns < 0: + self.opt = None + self.request_payload = 0 + else: + # make sure the EDNS version in ednsflags agrees with edns + ednsflags &= 0xFF00FFFF + ednsflags |= edns << 16 + if options is None: + options = [] + self.opt = self._make_opt(ednsflags, payload, options) + if request_payload is None: + request_payload = payload + self.request_payload = request_payload + if pad < 0: + raise ValueError("pad must be non-negative") + self.pad = pad + + @property + def edns(self) -> int: + if self.opt: + return (self.ednsflags & 0xFF0000) >> 16 + else: + return -1 + + @property + def ednsflags(self) -> int: + if self.opt: + return self.opt.ttl + else: + return 0 + + @ednsflags.setter + def ednsflags(self, v): + if self.opt: + self.opt.ttl = v + elif v: + self.opt = self._make_opt(v) + + @property + def payload(self) -> int: + if self.opt: + return self.opt[0].payload + else: + return 0 + + @property + def options(self) -> Tuple: + if self.opt: + return self.opt[0].options + else: + return () + + def want_dnssec(self, wanted: bool = True) -> None: + """Enable or disable 'DNSSEC desired' flag in requests. + + *wanted*, a ``bool``. If ``True``, then DNSSEC data is + desired in the response, EDNS is enabled if required, and then + the DO bit is set. If ``False``, the DO bit is cleared if + EDNS is enabled. + """ + + if wanted: + self.ednsflags |= dns.flags.DO + elif self.opt: + self.ednsflags &= ~int(dns.flags.DO) + + def rcode(self) -> dns.rcode.Rcode: + """Return the rcode. + + Returns a ``dns.rcode.Rcode``. + """ + return dns.rcode.from_flags(int(self.flags), int(self.ednsflags)) + + def set_rcode(self, rcode: dns.rcode.Rcode) -> None: + """Set the rcode. + + *rcode*, a ``dns.rcode.Rcode``, is the rcode to set. + """ + (value, evalue) = dns.rcode.to_flags(rcode) + self.flags &= 0xFFF0 + self.flags |= value + self.ednsflags &= 0x00FFFFFF + self.ednsflags |= evalue + + def opcode(self) -> dns.opcode.Opcode: + """Return the opcode. + + Returns a ``dns.opcode.Opcode``. + """ + return dns.opcode.from_flags(int(self.flags)) + + def set_opcode(self, opcode: dns.opcode.Opcode) -> None: + """Set the opcode. + + *opcode*, a ``dns.opcode.Opcode``, is the opcode to set. + """ + self.flags &= 0x87FF + self.flags |= dns.opcode.to_flags(opcode) + + def get_options(self, otype: dns.edns.OptionType) -> List[dns.edns.Option]: + """Return the list of options of the specified type.""" + return [option for option in self.options if option.otype == otype] + + def extended_errors(self) -> List[dns.edns.EDEOption]: + """Return the list of Extended DNS Error (EDE) options in the message""" + return cast(List[dns.edns.EDEOption], self.get_options(dns.edns.OptionType.EDE)) + + def _get_one_rr_per_rrset(self, value): + # What the caller picked is fine. + return value + + # pylint: disable=unused-argument + + def _parse_rr_header(self, section, name, rdclass, rdtype): + return (rdclass, rdtype, None, False) + + # pylint: enable=unused-argument + + def _parse_special_rr_header(self, section, count, position, name, rdclass, rdtype): + if rdtype == dns.rdatatype.OPT: + if ( + section != MessageSection.ADDITIONAL + or self.opt + or name != dns.name.root + ): + raise BadEDNS + elif rdtype == dns.rdatatype.TSIG: + if ( + section != MessageSection.ADDITIONAL + or rdclass != dns.rdatatype.ANY + or position != count - 1 + ): + raise BadTSIG + return (rdclass, rdtype, None, False) + + +class ChainingResult: + """The result of a call to dns.message.QueryMessage.resolve_chaining(). + + The ``answer`` attribute is the answer RRSet, or ``None`` if it doesn't + exist. + + The ``canonical_name`` attribute is the canonical name after all + chaining has been applied (this is the same name as ``rrset.name`` in cases + where rrset is not ``None``). + + The ``minimum_ttl`` attribute is the minimum TTL, i.e. the TTL to + use if caching the data. It is the smallest of all the CNAME TTLs + and either the answer TTL if it exists or the SOA TTL and SOA + minimum values for negative answers. + + The ``cnames`` attribute is a list of all the CNAME RRSets followed to + get to the canonical name. + """ + + def __init__( + self, + canonical_name: dns.name.Name, + answer: Optional[dns.rrset.RRset], + minimum_ttl: int, + cnames: List[dns.rrset.RRset], + ): + self.canonical_name = canonical_name + self.answer = answer + self.minimum_ttl = minimum_ttl + self.cnames = cnames + + +class QueryMessage(Message): + def resolve_chaining(self) -> ChainingResult: + """Follow the CNAME chain in the response to determine the answer + RRset. + + Raises ``dns.message.NotQueryResponse`` if the message is not + a response. + + Raises ``dns.message.ChainTooLong`` if the CNAME chain is too long. + + Raises ``dns.message.AnswerForNXDOMAIN`` if the rcode is NXDOMAIN + but an answer was found. + + Raises ``dns.exception.FormError`` if the question count is not 1. + + Returns a ChainingResult object. + """ + if self.flags & dns.flags.QR == 0: + raise NotQueryResponse + if len(self.question) != 1: + raise dns.exception.FormError + question = self.question[0] + qname = question.name + min_ttl = dns.ttl.MAX_TTL + answer = None + count = 0 + cnames = [] + while count < MAX_CHAIN: + try: + answer = self.find_rrset( + self.answer, qname, question.rdclass, question.rdtype + ) + min_ttl = min(min_ttl, answer.ttl) + break + except KeyError: + if question.rdtype != dns.rdatatype.CNAME: + try: + crrset = self.find_rrset( + self.answer, qname, question.rdclass, dns.rdatatype.CNAME + ) + cnames.append(crrset) + min_ttl = min(min_ttl, crrset.ttl) + for rd in crrset: + qname = rd.target + break + count += 1 + continue + except KeyError: + # Exit the chaining loop + break + else: + # Exit the chaining loop + break + if count >= MAX_CHAIN: + raise ChainTooLong + if self.rcode() == dns.rcode.NXDOMAIN and answer is not None: + raise AnswerForNXDOMAIN + if answer is None: + # Further minimize the TTL with NCACHE. + auname = qname + while True: + # Look for an SOA RR whose owner name is a superdomain + # of qname. + try: + srrset = self.find_rrset( + self.authority, auname, question.rdclass, dns.rdatatype.SOA + ) + min_ttl = min(min_ttl, srrset.ttl, srrset[0].minimum) + break + except KeyError: + try: + auname = auname.parent() + except dns.name.NoParent: + break + return ChainingResult(qname, answer, min_ttl, cnames) + + def canonical_name(self) -> dns.name.Name: + """Return the canonical name of the first name in the question + section. + + Raises ``dns.message.NotQueryResponse`` if the message is not + a response. + + Raises ``dns.message.ChainTooLong`` if the CNAME chain is too long. + + Raises ``dns.message.AnswerForNXDOMAIN`` if the rcode is NXDOMAIN + but an answer was found. + + Raises ``dns.exception.FormError`` if the question count is not 1. + """ + return self.resolve_chaining().canonical_name + + +def _maybe_import_update(): + # We avoid circular imports by doing this here. We do it in another + # function as doing it in _message_factory_from_opcode() makes "dns" + # a local symbol, and the first line fails :) + + # pylint: disable=redefined-outer-name,import-outside-toplevel,unused-import + import dns.update # noqa: F401 + + +def _message_factory_from_opcode(opcode): + if opcode == dns.opcode.QUERY: + return QueryMessage + elif opcode == dns.opcode.UPDATE: + _maybe_import_update() + return dns.update.UpdateMessage + else: + return Message + + +class _WireReader: + """Wire format reader. + + parser: the binary parser + message: The message object being built + initialize_message: Callback to set message parsing options + question_only: Are we only reading the question? + one_rr_per_rrset: Put each RR into its own RRset? + keyring: TSIG keyring + ignore_trailing: Ignore trailing junk at end of request? + multi: Is this message part of a multi-message sequence? + DNS dynamic updates. + continue_on_error: try to extract as much information as possible from + the message, accumulating MessageErrors in the *errors* attribute instead of + raising them. + """ + + def __init__( + self, + wire, + initialize_message, + question_only=False, + one_rr_per_rrset=False, + ignore_trailing=False, + keyring=None, + multi=False, + continue_on_error=False, + ): + self.parser = dns.wire.Parser(wire) + self.message = None + self.initialize_message = initialize_message + self.question_only = question_only + self.one_rr_per_rrset = one_rr_per_rrset + self.ignore_trailing = ignore_trailing + self.keyring = keyring + self.multi = multi + self.continue_on_error = continue_on_error + self.errors = [] + + def _get_question(self, section_number, qcount): + """Read the next *qcount* records from the wire data and add them to + the question section. + """ + assert self.message is not None + section = self.message.sections[section_number] + for _ in range(qcount): + qname = self.parser.get_name(self.message.origin) + (rdtype, rdclass) = self.parser.get_struct("!HH") + (rdclass, rdtype, _, _) = self.message._parse_rr_header( + section_number, qname, rdclass, rdtype + ) + self.message.find_rrset( + section, qname, rdclass, rdtype, create=True, force_unique=True + ) + + def _add_error(self, e): + self.errors.append(MessageError(e, self.parser.current)) + + def _get_section(self, section_number, count): + """Read the next I{count} records from the wire data and add them to + the specified section. + + section_number: the section of the message to which to add records + count: the number of records to read + """ + assert self.message is not None + section = self.message.sections[section_number] + force_unique = self.one_rr_per_rrset + for i in range(count): + rr_start = self.parser.current + absolute_name = self.parser.get_name() + if self.message.origin is not None: + name = absolute_name.relativize(self.message.origin) + else: + name = absolute_name + (rdtype, rdclass, ttl, rdlen) = self.parser.get_struct("!HHIH") + if rdtype in (dns.rdatatype.OPT, dns.rdatatype.TSIG): + ( + rdclass, + rdtype, + deleting, + empty, + ) = self.message._parse_special_rr_header( + section_number, count, i, name, rdclass, rdtype + ) + else: + (rdclass, rdtype, deleting, empty) = self.message._parse_rr_header( + section_number, name, rdclass, rdtype + ) + rdata_start = self.parser.current + try: + if empty: + if rdlen > 0: + raise dns.exception.FormError + rd = None + covers = dns.rdatatype.NONE + else: + with self.parser.restrict_to(rdlen): + rd = dns.rdata.from_wire_parser( + rdclass, rdtype, self.parser, self.message.origin + ) + covers = rd.covers() + if self.message.xfr and rdtype == dns.rdatatype.SOA: + force_unique = True + if rdtype == dns.rdatatype.OPT: + self.message.opt = dns.rrset.from_rdata(name, ttl, rd) + elif rdtype == dns.rdatatype.TSIG: + if self.keyring is None or self.keyring is True: + raise UnknownTSIGKey("got signed message without keyring") + elif isinstance(self.keyring, dict): + key = self.keyring.get(absolute_name) + if isinstance(key, bytes): + key = dns.tsig.Key(absolute_name, key, rd.algorithm) + elif callable(self.keyring): + key = self.keyring(self.message, absolute_name) + else: + key = self.keyring + if key is None: + raise UnknownTSIGKey(f"key '{name}' unknown") + if key: + self.message.keyring = key + self.message.tsig_ctx = dns.tsig.validate( + self.parser.wire, + key, + absolute_name, + rd, + int(time.time()), + self.message.request_mac, + rr_start, + self.message.tsig_ctx, + self.multi, + ) + self.message.tsig = dns.rrset.from_rdata(absolute_name, 0, rd) + else: + rrset = self.message.find_rrset( + section, + name, + rdclass, + rdtype, + covers, + deleting, + True, + force_unique, + ) + if rd is not None: + if ttl > 0x7FFFFFFF: + ttl = 0 + rrset.add(rd, ttl) + except Exception as e: + if self.continue_on_error: + self._add_error(e) + self.parser.seek(rdata_start + rdlen) + else: + raise + + def read(self): + """Read a wire format DNS message and build a dns.message.Message + object.""" + + if self.parser.remaining() < 12: + raise ShortHeader + (id, flags, qcount, ancount, aucount, adcount) = self.parser.get_struct( + "!HHHHHH" + ) + factory = _message_factory_from_opcode(dns.opcode.from_flags(flags)) + self.message = factory(id=id) + self.message.flags = dns.flags.Flag(flags) + self.message.wire = self.parser.wire + self.initialize_message(self.message) + self.one_rr_per_rrset = self.message._get_one_rr_per_rrset( + self.one_rr_per_rrset + ) + try: + self._get_question(MessageSection.QUESTION, qcount) + if self.question_only: + return self.message + self._get_section(MessageSection.ANSWER, ancount) + self._get_section(MessageSection.AUTHORITY, aucount) + self._get_section(MessageSection.ADDITIONAL, adcount) + if not self.ignore_trailing and self.parser.remaining() != 0: + raise TrailingJunk + if self.multi and self.message.tsig_ctx and not self.message.had_tsig: + self.message.tsig_ctx.update(self.parser.wire) + except Exception as e: + if self.continue_on_error: + self._add_error(e) + else: + raise + return self.message + + +def from_wire( + wire: bytes, + keyring: Optional[Any] = None, + request_mac: Optional[bytes] = b"", + xfr: bool = False, + origin: Optional[dns.name.Name] = None, + tsig_ctx: Optional[Union[dns.tsig.HMACTSig, dns.tsig.GSSTSig]] = None, + multi: bool = False, + question_only: bool = False, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + raise_on_truncation: bool = False, + continue_on_error: bool = False, +) -> Message: + """Convert a DNS wire format message into a message object. + + *keyring*, a ``dns.tsig.Key``, ``dict``, ``bool``, or ``None``, the key or keyring + to use if the message is signed. If ``None`` or ``True``, then trying to decode + a message with a TSIG will fail as it cannot be validated. If ``False``, then + TSIG validation is disabled. + + *request_mac*, a ``bytes`` or ``None``. If the message is a response to a + TSIG-signed request, *request_mac* should be set to the MAC of that request. + + *xfr*, a ``bool``, should be set to ``True`` if this message is part of a zone + transfer. + + *origin*, a ``dns.name.Name`` or ``None``. If the message is part of a zone + transfer, *origin* should be the origin name of the zone. If not ``None``, names + will be relativized to the origin. + + *tsig_ctx*, a ``dns.tsig.HMACTSig`` or ``dns.tsig.GSSTSig`` object, the ongoing TSIG + context, used when validating zone transfers. + + *multi*, a ``bool``, should be set to ``True`` if this message is part of a multiple + message sequence. + + *question_only*, a ``bool``. If ``True``, read only up to the end of the question + section. + + *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own RRset. + + *ignore_trailing*, a ``bool``. If ``True``, ignore trailing junk at end of the + message. + + *raise_on_truncation*, a ``bool``. If ``True``, raise an exception if the TC bit is + set. + + *continue_on_error*, a ``bool``. If ``True``, try to continue parsing even if + errors occur. Erroneous rdata will be ignored. Errors will be accumulated as a + list of MessageError objects in the message's ``errors`` attribute. This option is + recommended only for DNS analysis tools, or for use in a server as part of an error + handling path. The default is ``False``. + + Raises ``dns.message.ShortHeader`` if the message is less than 12 octets long. + + Raises ``dns.message.TrailingJunk`` if there were octets in the message past the end + of the proper DNS message, and *ignore_trailing* is ``False``. + + Raises ``dns.message.BadEDNS`` if an OPT record was in the wrong section, or + occurred more than once. + + Raises ``dns.message.BadTSIG`` if a TSIG record was not the last record of the + additional data section. + + Raises ``dns.message.Truncated`` if the TC flag is set and *raise_on_truncation* is + ``True``. + + Returns a ``dns.message.Message``. + """ + + # We permit None for request_mac solely for backwards compatibility + if request_mac is None: + request_mac = b"" + + def initialize_message(message): + message.request_mac = request_mac + message.xfr = xfr + message.origin = origin + message.tsig_ctx = tsig_ctx + + reader = _WireReader( + wire, + initialize_message, + question_only, + one_rr_per_rrset, + ignore_trailing, + keyring, + multi, + continue_on_error, + ) + try: + m = reader.read() + except dns.exception.FormError: + if ( + reader.message + and (reader.message.flags & dns.flags.TC) + and raise_on_truncation + ): + raise Truncated(message=reader.message) + else: + raise + # Reading a truncated message might not have any errors, so we + # have to do this check here too. + if m.flags & dns.flags.TC and raise_on_truncation: + raise Truncated(message=m) + if continue_on_error: + m.errors = reader.errors + + return m + + +class _TextReader: + """Text format reader. + + tok: the tokenizer. + message: The message object being built. + DNS dynamic updates. + last_name: The most recently read name when building a message object. + one_rr_per_rrset: Put each RR into its own RRset? + origin: The origin for relative names + relativize: relativize names? + relativize_to: the origin to relativize to. + """ + + def __init__( + self, + text, + idna_codec, + one_rr_per_rrset=False, + origin=None, + relativize=True, + relativize_to=None, + ): + self.message = None + self.tok = dns.tokenizer.Tokenizer(text, idna_codec=idna_codec) + self.last_name = None + self.one_rr_per_rrset = one_rr_per_rrset + self.origin = origin + self.relativize = relativize + self.relativize_to = relativize_to + self.id = None + self.edns = -1 + self.ednsflags = 0 + self.payload = DEFAULT_EDNS_PAYLOAD + self.rcode = None + self.opcode = dns.opcode.QUERY + self.flags = 0 + + def _header_line(self, _): + """Process one line from the text format header section.""" + + token = self.tok.get() + what = token.value + if what == "id": + self.id = self.tok.get_int() + elif what == "flags": + while True: + token = self.tok.get() + if not token.is_identifier(): + self.tok.unget(token) + break + self.flags = self.flags | dns.flags.from_text(token.value) + elif what == "edns": + self.edns = self.tok.get_int() + self.ednsflags = self.ednsflags | (self.edns << 16) + elif what == "eflags": + if self.edns < 0: + self.edns = 0 + while True: + token = self.tok.get() + if not token.is_identifier(): + self.tok.unget(token) + break + self.ednsflags = self.ednsflags | dns.flags.edns_from_text(token.value) + elif what == "payload": + self.payload = self.tok.get_int() + if self.edns < 0: + self.edns = 0 + elif what == "opcode": + text = self.tok.get_string() + self.opcode = dns.opcode.from_text(text) + self.flags = self.flags | dns.opcode.to_flags(self.opcode) + elif what == "rcode": + text = self.tok.get_string() + self.rcode = dns.rcode.from_text(text) + else: + raise UnknownHeaderField + self.tok.get_eol() + + def _question_line(self, section_number): + """Process one line from the text format question section.""" + + section = self.message.sections[section_number] + token = self.tok.get(want_leading=True) + if not token.is_whitespace(): + self.last_name = self.tok.as_name( + token, self.message.origin, self.relativize, self.relativize_to + ) + name = self.last_name + if name is None: + raise NoPreviousName + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + # Class + try: + rdclass = dns.rdataclass.from_text(token.value) + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + except dns.exception.SyntaxError: + raise dns.exception.SyntaxError + except Exception: + rdclass = dns.rdataclass.IN + # Type + rdtype = dns.rdatatype.from_text(token.value) + (rdclass, rdtype, _, _) = self.message._parse_rr_header( + section_number, name, rdclass, rdtype + ) + self.message.find_rrset( + section, name, rdclass, rdtype, create=True, force_unique=True + ) + self.tok.get_eol() + + def _rr_line(self, section_number): + """Process one line from the text format answer, authority, or + additional data sections. + """ + + section = self.message.sections[section_number] + # Name + token = self.tok.get(want_leading=True) + if not token.is_whitespace(): + self.last_name = self.tok.as_name( + token, self.message.origin, self.relativize, self.relativize_to + ) + name = self.last_name + if name is None: + raise NoPreviousName + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + # TTL + try: + ttl = int(token.value, 0) + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + except dns.exception.SyntaxError: + raise dns.exception.SyntaxError + except Exception: + ttl = 0 + # Class + try: + rdclass = dns.rdataclass.from_text(token.value) + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + except dns.exception.SyntaxError: + raise dns.exception.SyntaxError + except Exception: + rdclass = dns.rdataclass.IN + # Type + rdtype = dns.rdatatype.from_text(token.value) + (rdclass, rdtype, deleting, empty) = self.message._parse_rr_header( + section_number, name, rdclass, rdtype + ) + token = self.tok.get() + if empty and not token.is_eol_or_eof(): + raise dns.exception.SyntaxError + if not empty and token.is_eol_or_eof(): + raise dns.exception.UnexpectedEnd + if not token.is_eol_or_eof(): + self.tok.unget(token) + rd = dns.rdata.from_text( + rdclass, + rdtype, + self.tok, + self.message.origin, + self.relativize, + self.relativize_to, + ) + covers = rd.covers() + else: + rd = None + covers = dns.rdatatype.NONE + rrset = self.message.find_rrset( + section, + name, + rdclass, + rdtype, + covers, + deleting, + True, + self.one_rr_per_rrset, + ) + if rd is not None: + rrset.add(rd, ttl) + + def _make_message(self): + factory = _message_factory_from_opcode(self.opcode) + message = factory(id=self.id) + message.flags = self.flags + if self.edns >= 0: + message.use_edns(self.edns, self.ednsflags, self.payload) + if self.rcode: + message.set_rcode(self.rcode) + if self.origin: + message.origin = self.origin + return message + + def read(self): + """Read a text format DNS message and build a dns.message.Message + object.""" + + line_method = self._header_line + section_number = None + while 1: + token = self.tok.get(True, True) + if token.is_eol_or_eof(): + break + if token.is_comment(): + u = token.value.upper() + if u == "HEADER": + line_method = self._header_line + + if self.message: + message = self.message + else: + # If we don't have a message, create one with the current + # opcode, so that we know which section names to parse. + message = self._make_message() + try: + section_number = message._section_enum.from_text(u) + # We found a section name. If we don't have a message, + # use the one we just created. + if not self.message: + self.message = message + self.one_rr_per_rrset = message._get_one_rr_per_rrset( + self.one_rr_per_rrset + ) + if section_number == MessageSection.QUESTION: + line_method = self._question_line + else: + line_method = self._rr_line + except Exception: + # It's just a comment. + pass + self.tok.get_eol() + continue + self.tok.unget(token) + line_method(section_number) + if not self.message: + self.message = self._make_message() + return self.message + + +def from_text( + text: str, + idna_codec: Optional[dns.name.IDNACodec] = None, + one_rr_per_rrset: bool = False, + origin: Optional[dns.name.Name] = None, + relativize: bool = True, + relativize_to: Optional[dns.name.Name] = None, +) -> Message: + """Convert the text format message into a message object. + + The reader stops after reading the first blank line in the input to + facilitate reading multiple messages from a single file with + ``dns.message.from_file()``. + + *text*, a ``str``, the text format message. + + *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA + encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder + is used. + + *one_rr_per_rrset*, a ``bool``. If ``True``, then each RR is put + into its own rrset. The default is ``False``. + + *origin*, a ``dns.name.Name`` (or ``None``), the + origin to use for relative names. + + *relativize*, a ``bool``. If true, name will be relativized. + + *relativize_to*, a ``dns.name.Name`` (or ``None``), the origin to use + when relativizing names. If not set, the *origin* value will be used. + + Raises ``dns.message.UnknownHeaderField`` if a header is unknown. + + Raises ``dns.exception.SyntaxError`` if the text is badly formed. + + Returns a ``dns.message.Message object`` + """ + + # 'text' can also be a file, but we don't publish that fact + # since it's an implementation detail. The official file + # interface is from_file(). + + reader = _TextReader( + text, idna_codec, one_rr_per_rrset, origin, relativize, relativize_to + ) + return reader.read() + + +def from_file( + f: Any, + idna_codec: Optional[dns.name.IDNACodec] = None, + one_rr_per_rrset: bool = False, +) -> Message: + """Read the next text format message from the specified file. + + Message blocks are separated by a single blank line. + + *f*, a ``file`` or ``str``. If *f* is text, it is treated as the + pathname of a file to open. + + *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA + encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder + is used. + + *one_rr_per_rrset*, a ``bool``. If ``True``, then each RR is put + into its own rrset. The default is ``False``. + + Raises ``dns.message.UnknownHeaderField`` if a header is unknown. + + Raises ``dns.exception.SyntaxError`` if the text is badly formed. + + Returns a ``dns.message.Message object`` + """ + + if isinstance(f, str): + cm: contextlib.AbstractContextManager = open(f) + else: + cm = contextlib.nullcontext(f) + with cm as f: + return from_text(f, idna_codec, one_rr_per_rrset) + assert False # for mypy lgtm[py/unreachable-statement] + + +def make_query( + qname: Union[dns.name.Name, str], + rdtype: Union[dns.rdatatype.RdataType, str], + rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, + use_edns: Optional[Union[int, bool]] = None, + want_dnssec: bool = False, + ednsflags: Optional[int] = None, + payload: Optional[int] = None, + request_payload: Optional[int] = None, + options: Optional[List[dns.edns.Option]] = None, + idna_codec: Optional[dns.name.IDNACodec] = None, + id: Optional[int] = None, + flags: int = dns.flags.RD, + pad: int = 0, +) -> QueryMessage: + """Make a query message. + + The query name, type, and class may all be specified either + as objects of the appropriate type, or as strings. + + The query will have a randomly chosen query id, and its DNS flags + will be set to dns.flags.RD. + + qname, a ``dns.name.Name`` or ``str``, the query name. + + *rdtype*, an ``int`` or ``str``, the desired rdata type. + + *rdclass*, an ``int`` or ``str``, the desired rdata class; the default + is class IN. + + *use_edns*, an ``int``, ``bool`` or ``None``. The EDNS level to use; the + default is ``None``. If ``None``, EDNS will be enabled only if other + parameters (*ednsflags*, *payload*, *request_payload*, or *options*) are + set. + See the description of dns.message.Message.use_edns() for the possible + values for use_edns and their meanings. + + *want_dnssec*, a ``bool``. If ``True``, DNSSEC data is desired. + + *ednsflags*, an ``int``, the EDNS flag values. + + *payload*, an ``int``, is the EDNS sender's payload field, which is the + maximum size of UDP datagram the sender can handle. I.e. how big + a response to this message can be. + + *request_payload*, an ``int``, is the EDNS payload size to use when + sending this message. If not specified, defaults to the value of + *payload*. + + *options*, a list of ``dns.edns.Option`` objects or ``None``, the EDNS + options. + + *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA + encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder + is used. + + *id*, an ``int`` or ``None``, the desired query id. The default is + ``None``, which generates a random query id. + + *flags*, an ``int``, the desired query flags. The default is + ``dns.flags.RD``. + + *pad*, a non-negative ``int``. If 0, the default, do not pad; otherwise add + padding bytes to make the message size a multiple of *pad*. Note that if + padding is non-zero, an EDNS PADDING option will always be added to the + message. + + Returns a ``dns.message.QueryMessage`` + """ + + if isinstance(qname, str): + qname = dns.name.from_text(qname, idna_codec=idna_codec) + rdtype = dns.rdatatype.RdataType.make(rdtype) + rdclass = dns.rdataclass.RdataClass.make(rdclass) + m = QueryMessage(id=id) + m.flags = dns.flags.Flag(flags) + m.find_rrset(m.question, qname, rdclass, rdtype, create=True, force_unique=True) + # only pass keywords on to use_edns if they have been set to a + # non-None value. Setting a field will turn EDNS on if it hasn't + # been configured. + kwargs: Dict[str, Any] = {} + if ednsflags is not None: + kwargs["ednsflags"] = ednsflags + if payload is not None: + kwargs["payload"] = payload + if request_payload is not None: + kwargs["request_payload"] = request_payload + if options is not None: + kwargs["options"] = options + if kwargs and use_edns is None: + use_edns = 0 + kwargs["edns"] = use_edns + kwargs["pad"] = pad + m.use_edns(**kwargs) + m.want_dnssec(want_dnssec) + return m + + +class CopyMode(enum.Enum): + """ + How should sections be copied when making an update response? + """ + + NOTHING = 0 + QUESTION = 1 + EVERYTHING = 2 + + +def make_response( + query: Message, + recursion_available: bool = False, + our_payload: int = 8192, + fudge: int = 300, + tsig_error: int = 0, + pad: Optional[int] = None, + copy_mode: Optional[CopyMode] = None, +) -> Message: + """Make a message which is a response for the specified query. + The message returned is really a response skeleton; it has all of the infrastructure + required of a response, but none of the content. + + Response section(s) which are copied are shallow copies of the matching section(s) + in the query, so the query's RRsets should not be changed. + + *query*, a ``dns.message.Message``, the query to respond to. + + *recursion_available*, a ``bool``, should RA be set in the response? + + *our_payload*, an ``int``, the payload size to advertise in EDNS responses. + + *fudge*, an ``int``, the TSIG time fudge. + + *tsig_error*, an ``int``, the TSIG error. + + *pad*, a non-negative ``int`` or ``None``. If 0, the default, do not pad; otherwise + if not ``None`` add padding bytes to make the message size a multiple of *pad*. Note + that if padding is non-zero, an EDNS PADDING option will always be added to the + message. If ``None``, add padding following RFC 8467, namely if the request is + padded, pad the response to 468 otherwise do not pad. + + *copy_mode*, a ``dns.message.CopyMode`` or ``None``, determines how sections are + copied. The default, ``None`` copies sections according to the default for the + message's opcode, which is currently ``dns.message.CopyMode.QUESTION`` for all + opcodes. ``dns.message.CopyMode.QUESTION`` copies only the question section. + ``dns.message.CopyMode.EVERYTHING`` copies all sections other than OPT or TSIG + records, which are created appropriately if needed. ``dns.message.CopyMode.NOTHING`` + copies no sections; note that this mode is for server testing purposes and is + otherwise not recommended for use. In particular, ``dns.message.is_response()`` + will be ``False`` if you create a response this way and the rcode is not + ``FORMERR``, ``SERVFAIL``, ``NOTIMP``, or ``REFUSED``. + + Returns a ``dns.message.Message`` object whose specific class is appropriate for the + query. For example, if query is a ``dns.update.UpdateMessage``, the response will + be one too. + """ + + if query.flags & dns.flags.QR: + raise dns.exception.FormError("specified query message is not a query") + opcode = query.opcode() + factory = _message_factory_from_opcode(opcode) + response = factory(id=query.id) + response.flags = dns.flags.QR | (query.flags & dns.flags.RD) + if recursion_available: + response.flags |= dns.flags.RA + response.set_opcode(opcode) + if copy_mode is None: + copy_mode = CopyMode.QUESTION + if copy_mode != CopyMode.NOTHING: + response.question = list(query.question) + if copy_mode == CopyMode.EVERYTHING: + response.answer = list(query.answer) + response.authority = list(query.authority) + response.additional = list(query.additional) + if query.edns >= 0: + if pad is None: + # Set response padding per RFC 8467 + pad = 0 + for option in query.options: + if option.otype == dns.edns.OptionType.PADDING: + pad = 468 + response.use_edns(0, 0, our_payload, query.payload, pad=pad) + if query.had_tsig: + response.use_tsig( + query.keyring, + query.keyname, + fudge, + None, + tsig_error, + b"", + query.keyalgorithm, + ) + response.request_mac = query.mac + return response + + +### BEGIN generated MessageSection constants + +QUESTION = MessageSection.QUESTION +ANSWER = MessageSection.ANSWER +AUTHORITY = MessageSection.AUTHORITY +ADDITIONAL = MessageSection.ADDITIONAL + +### END generated MessageSection constants diff --git a/.venv/lib/python3.9/site-packages/dns/name.py b/.venv/lib/python3.9/site-packages/dns/name.py new file mode 100644 index 0000000..f79f0d0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/name.py @@ -0,0 +1,1284 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2001-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Names. +""" + +import copy +import encodings.idna # type: ignore +import functools +import struct +from typing import Any, Callable, Dict, Iterable, Optional, Tuple, Union + +import dns._features +import dns.enum +import dns.exception +import dns.immutable +import dns.wire + +if dns._features.have("idna"): + import idna # type: ignore + + have_idna_2008 = True +else: # pragma: no cover + have_idna_2008 = False + +CompressType = Dict["Name", int] + + +class NameRelation(dns.enum.IntEnum): + """Name relation result from fullcompare().""" + + # This is an IntEnum for backwards compatibility in case anyone + # has hardwired the constants. + + #: The compared names have no relationship to each other. + NONE = 0 + #: the first name is a superdomain of the second. + SUPERDOMAIN = 1 + #: The first name is a subdomain of the second. + SUBDOMAIN = 2 + #: The compared names are equal. + EQUAL = 3 + #: The compared names have a common ancestor. + COMMONANCESTOR = 4 + + @classmethod + def _maximum(cls): + return cls.COMMONANCESTOR # pragma: no cover + + @classmethod + def _short_name(cls): + return cls.__name__ # pragma: no cover + + +# Backwards compatibility +NAMERELN_NONE = NameRelation.NONE +NAMERELN_SUPERDOMAIN = NameRelation.SUPERDOMAIN +NAMERELN_SUBDOMAIN = NameRelation.SUBDOMAIN +NAMERELN_EQUAL = NameRelation.EQUAL +NAMERELN_COMMONANCESTOR = NameRelation.COMMONANCESTOR + + +class EmptyLabel(dns.exception.SyntaxError): + """A DNS label is empty.""" + + +class BadEscape(dns.exception.SyntaxError): + """An escaped code in a text format of DNS name is invalid.""" + + +class BadPointer(dns.exception.FormError): + """A DNS compression pointer points forward instead of backward.""" + + +class BadLabelType(dns.exception.FormError): + """The label type in DNS name wire format is unknown.""" + + +class NeedAbsoluteNameOrOrigin(dns.exception.DNSException): + """An attempt was made to convert a non-absolute name to + wire when there was also a non-absolute (or missing) origin.""" + + +class NameTooLong(dns.exception.FormError): + """A DNS name is > 255 octets long.""" + + +class LabelTooLong(dns.exception.SyntaxError): + """A DNS label is > 63 octets long.""" + + +class AbsoluteConcatenation(dns.exception.DNSException): + """An attempt was made to append anything other than the + empty name to an absolute DNS name.""" + + +class NoParent(dns.exception.DNSException): + """An attempt was made to get the parent of the root name + or the empty name.""" + + +class NoIDNA2008(dns.exception.DNSException): + """IDNA 2008 processing was requested but the idna module is not + available.""" + + +class IDNAException(dns.exception.DNSException): + """IDNA processing raised an exception.""" + + supp_kwargs = {"idna_exception"} + fmt = "IDNA processing exception: {idna_exception}" + + # We do this as otherwise mypy complains about unexpected keyword argument + # idna_exception + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + +class NeedSubdomainOfOrigin(dns.exception.DNSException): + """An absolute name was provided that is not a subdomain of the specified origin.""" + + +_escaped = b'"().;\\@$' +_escaped_text = '"().;\\@$' + + +def _escapify(label: Union[bytes, str]) -> str: + """Escape the characters in label which need it. + @returns: the escaped string + @rtype: string""" + if isinstance(label, bytes): + # Ordinary DNS label mode. Escape special characters and values + # < 0x20 or > 0x7f. + text = "" + for c in label: + if c in _escaped: + text += "\\" + chr(c) + elif c > 0x20 and c < 0x7F: + text += chr(c) + else: + text += "\\%03d" % c + return text + + # Unicode label mode. Escape only special characters and values < 0x20 + text = "" + for uc in label: + if uc in _escaped_text: + text += "\\" + uc + elif uc <= "\x20": + text += "\\%03d" % ord(uc) + else: + text += uc + return text + + +class IDNACodec: + """Abstract base class for IDNA encoder/decoders.""" + + def __init__(self): + pass + + def is_idna(self, label: bytes) -> bool: + return label.lower().startswith(b"xn--") + + def encode(self, label: str) -> bytes: + raise NotImplementedError # pragma: no cover + + def decode(self, label: bytes) -> str: + # We do not apply any IDNA policy on decode. + if self.is_idna(label): + try: + slabel = label[4:].decode("punycode") + return _escapify(slabel) + except Exception as e: + raise IDNAException(idna_exception=e) + else: + return _escapify(label) + + +class IDNA2003Codec(IDNACodec): + """IDNA 2003 encoder/decoder.""" + + def __init__(self, strict_decode: bool = False): + """Initialize the IDNA 2003 encoder/decoder. + + *strict_decode* is a ``bool``. If `True`, then IDNA2003 checking + is done when decoding. This can cause failures if the name + was encoded with IDNA2008. The default is `False`. + """ + + super().__init__() + self.strict_decode = strict_decode + + def encode(self, label: str) -> bytes: + """Encode *label*.""" + + if label == "": + return b"" + try: + return encodings.idna.ToASCII(label) + except UnicodeError: + raise LabelTooLong + + def decode(self, label: bytes) -> str: + """Decode *label*.""" + if not self.strict_decode: + return super().decode(label) + if label == b"": + return "" + try: + return _escapify(encodings.idna.ToUnicode(label)) + except Exception as e: + raise IDNAException(idna_exception=e) + + +class IDNA2008Codec(IDNACodec): + """IDNA 2008 encoder/decoder.""" + + def __init__( + self, + uts_46: bool = False, + transitional: bool = False, + allow_pure_ascii: bool = False, + strict_decode: bool = False, + ): + """Initialize the IDNA 2008 encoder/decoder. + + *uts_46* is a ``bool``. If True, apply Unicode IDNA + compatibility processing as described in Unicode Technical + Standard #46 (https://unicode.org/reports/tr46/). + If False, do not apply the mapping. The default is False. + + *transitional* is a ``bool``: If True, use the + "transitional" mode described in Unicode Technical Standard + #46. The default is False. + + *allow_pure_ascii* is a ``bool``. If True, then a label which + consists of only ASCII characters is allowed. This is less + strict than regular IDNA 2008, but is also necessary for mixed + names, e.g. a name with starting with "_sip._tcp." and ending + in an IDN suffix which would otherwise be disallowed. The + default is False. + + *strict_decode* is a ``bool``: If True, then IDNA2008 checking + is done when decoding. This can cause failures if the name + was encoded with IDNA2003. The default is False. + """ + super().__init__() + self.uts_46 = uts_46 + self.transitional = transitional + self.allow_pure_ascii = allow_pure_ascii + self.strict_decode = strict_decode + + def encode(self, label: str) -> bytes: + if label == "": + return b"" + if self.allow_pure_ascii and is_all_ascii(label): + encoded = label.encode("ascii") + if len(encoded) > 63: + raise LabelTooLong + return encoded + if not have_idna_2008: + raise NoIDNA2008 + try: + if self.uts_46: + # pylint: disable=possibly-used-before-assignment + label = idna.uts46_remap(label, False, self.transitional) + return idna.alabel(label) + except idna.IDNAError as e: + if e.args[0] == "Label too long": + raise LabelTooLong + else: + raise IDNAException(idna_exception=e) + + def decode(self, label: bytes) -> str: + if not self.strict_decode: + return super().decode(label) + if label == b"": + return "" + if not have_idna_2008: + raise NoIDNA2008 + try: + ulabel = idna.ulabel(label) + if self.uts_46: + ulabel = idna.uts46_remap(ulabel, False, self.transitional) + return _escapify(ulabel) + except (idna.IDNAError, UnicodeError) as e: + raise IDNAException(idna_exception=e) + + +IDNA_2003_Practical = IDNA2003Codec(False) +IDNA_2003_Strict = IDNA2003Codec(True) +IDNA_2003 = IDNA_2003_Practical +IDNA_2008_Practical = IDNA2008Codec(True, False, True, False) +IDNA_2008_UTS_46 = IDNA2008Codec(True, False, False, False) +IDNA_2008_Strict = IDNA2008Codec(False, False, False, True) +IDNA_2008_Transitional = IDNA2008Codec(True, True, False, False) +IDNA_2008 = IDNA_2008_Practical + + +def _validate_labels(labels: Tuple[bytes, ...]) -> None: + """Check for empty labels in the middle of a label sequence, + labels that are too long, and for too many labels. + + Raises ``dns.name.NameTooLong`` if the name as a whole is too long. + + Raises ``dns.name.EmptyLabel`` if a label is empty (i.e. the root + label) and appears in a position other than the end of the label + sequence + + """ + + l = len(labels) + total = 0 + i = -1 + j = 0 + for label in labels: + ll = len(label) + total += ll + 1 + if ll > 63: + raise LabelTooLong + if i < 0 and label == b"": + i = j + j += 1 + if total > 255: + raise NameTooLong + if i >= 0 and i != l - 1: + raise EmptyLabel + + +def _maybe_convert_to_binary(label: Union[bytes, str]) -> bytes: + """If label is ``str``, convert it to ``bytes``. If it is already + ``bytes`` just return it. + + """ + + if isinstance(label, bytes): + return label + if isinstance(label, str): + return label.encode() + raise ValueError # pragma: no cover + + +@dns.immutable.immutable +class Name: + """A DNS name. + + The dns.name.Name class represents a DNS name as a tuple of + labels. Each label is a ``bytes`` in DNS wire format. Instances + of the class are immutable. + """ + + __slots__ = ["labels"] + + def __init__(self, labels: Iterable[Union[bytes, str]]): + """*labels* is any iterable whose values are ``str`` or ``bytes``.""" + + blabels = [_maybe_convert_to_binary(x) for x in labels] + self.labels = tuple(blabels) + _validate_labels(self.labels) + + def __copy__(self): + return Name(self.labels) + + def __deepcopy__(self, memo): + return Name(copy.deepcopy(self.labels, memo)) + + def __getstate__(self): + # Names can be pickled + return {"labels": self.labels} + + def __setstate__(self, state): + super().__setattr__("labels", state["labels"]) + _validate_labels(self.labels) + + def is_absolute(self) -> bool: + """Is the most significant label of this name the root label? + + Returns a ``bool``. + """ + + return len(self.labels) > 0 and self.labels[-1] == b"" + + def is_wild(self) -> bool: + """Is this name wild? (I.e. Is the least significant label '*'?) + + Returns a ``bool``. + """ + + return len(self.labels) > 0 and self.labels[0] == b"*" + + def __hash__(self) -> int: + """Return a case-insensitive hash of the name. + + Returns an ``int``. + """ + + h = 0 + for label in self.labels: + for c in label.lower(): + h += (h << 3) + c + return h + + def fullcompare(self, other: "Name") -> Tuple[NameRelation, int, int]: + """Compare two names, returning a 3-tuple + ``(relation, order, nlabels)``. + + *relation* describes the relation ship between the names, + and is one of: ``dns.name.NameRelation.NONE``, + ``dns.name.NameRelation.SUPERDOMAIN``, ``dns.name.NameRelation.SUBDOMAIN``, + ``dns.name.NameRelation.EQUAL``, or ``dns.name.NameRelation.COMMONANCESTOR``. + + *order* is < 0 if *self* < *other*, > 0 if *self* > *other*, and == + 0 if *self* == *other*. A relative name is always less than an + absolute name. If both names have the same relativity, then + the DNSSEC order relation is used to order them. + + *nlabels* is the number of significant labels that the two names + have in common. + + Here are some examples. Names ending in "." are absolute names, + those not ending in "." are relative names. + + ============= ============= =========== ===== ======= + self other relation order nlabels + ============= ============= =========== ===== ======= + www.example. www.example. equal 0 3 + www.example. example. subdomain > 0 2 + example. www.example. superdomain < 0 2 + example1.com. example2.com. common anc. < 0 2 + example1 example2. none < 0 0 + example1. example2 none > 0 0 + ============= ============= =========== ===== ======= + """ + + sabs = self.is_absolute() + oabs = other.is_absolute() + if sabs != oabs: + if sabs: + return (NameRelation.NONE, 1, 0) + else: + return (NameRelation.NONE, -1, 0) + l1 = len(self.labels) + l2 = len(other.labels) + ldiff = l1 - l2 + if ldiff < 0: + l = l1 + else: + l = l2 + + order = 0 + nlabels = 0 + namereln = NameRelation.NONE + while l > 0: + l -= 1 + l1 -= 1 + l2 -= 1 + label1 = self.labels[l1].lower() + label2 = other.labels[l2].lower() + if label1 < label2: + order = -1 + if nlabels > 0: + namereln = NameRelation.COMMONANCESTOR + return (namereln, order, nlabels) + elif label1 > label2: + order = 1 + if nlabels > 0: + namereln = NameRelation.COMMONANCESTOR + return (namereln, order, nlabels) + nlabels += 1 + order = ldiff + if ldiff < 0: + namereln = NameRelation.SUPERDOMAIN + elif ldiff > 0: + namereln = NameRelation.SUBDOMAIN + else: + namereln = NameRelation.EQUAL + return (namereln, order, nlabels) + + def is_subdomain(self, other: "Name") -> bool: + """Is self a subdomain of other? + + Note that the notion of subdomain includes equality, e.g. + "dnspython.org" is a subdomain of itself. + + Returns a ``bool``. + """ + + (nr, _, _) = self.fullcompare(other) + if nr == NameRelation.SUBDOMAIN or nr == NameRelation.EQUAL: + return True + return False + + def is_superdomain(self, other: "Name") -> bool: + """Is self a superdomain of other? + + Note that the notion of superdomain includes equality, e.g. + "dnspython.org" is a superdomain of itself. + + Returns a ``bool``. + """ + + (nr, _, _) = self.fullcompare(other) + if nr == NameRelation.SUPERDOMAIN or nr == NameRelation.EQUAL: + return True + return False + + def canonicalize(self) -> "Name": + """Return a name which is equal to the current name, but is in + DNSSEC canonical form. + """ + + return Name([x.lower() for x in self.labels]) + + def __eq__(self, other): + if isinstance(other, Name): + return self.fullcompare(other)[1] == 0 + else: + return False + + def __ne__(self, other): + if isinstance(other, Name): + return self.fullcompare(other)[1] != 0 + else: + return True + + def __lt__(self, other): + if isinstance(other, Name): + return self.fullcompare(other)[1] < 0 + else: + return NotImplemented + + def __le__(self, other): + if isinstance(other, Name): + return self.fullcompare(other)[1] <= 0 + else: + return NotImplemented + + def __ge__(self, other): + if isinstance(other, Name): + return self.fullcompare(other)[1] >= 0 + else: + return NotImplemented + + def __gt__(self, other): + if isinstance(other, Name): + return self.fullcompare(other)[1] > 0 + else: + return NotImplemented + + def __repr__(self): + return "" + + def __str__(self): + return self.to_text(False) + + def to_text(self, omit_final_dot: bool = False) -> str: + """Convert name to DNS text format. + + *omit_final_dot* is a ``bool``. If True, don't emit the final + dot (denoting the root label) for absolute names. The default + is False. + + Returns a ``str``. + """ + + if len(self.labels) == 0: + return "@" + if len(self.labels) == 1 and self.labels[0] == b"": + return "." + if omit_final_dot and self.is_absolute(): + l = self.labels[:-1] + else: + l = self.labels + s = ".".join(map(_escapify, l)) + return s + + def to_unicode( + self, omit_final_dot: bool = False, idna_codec: Optional[IDNACodec] = None + ) -> str: + """Convert name to Unicode text format. + + IDN ACE labels are converted to Unicode. + + *omit_final_dot* is a ``bool``. If True, don't emit the final + dot (denoting the root label) for absolute names. The default + is False. + *idna_codec* specifies the IDNA encoder/decoder. If None, the + dns.name.IDNA_2003_Practical encoder/decoder is used. + The IDNA_2003_Practical decoder does + not impose any policy, it just decodes punycode, so if you + don't want checking for compliance, you can use this decoder + for IDNA2008 as well. + + Returns a ``str``. + """ + + if len(self.labels) == 0: + return "@" + if len(self.labels) == 1 and self.labels[0] == b"": + return "." + if omit_final_dot and self.is_absolute(): + l = self.labels[:-1] + else: + l = self.labels + if idna_codec is None: + idna_codec = IDNA_2003_Practical + return ".".join([idna_codec.decode(x) for x in l]) + + def to_digestable(self, origin: Optional["Name"] = None) -> bytes: + """Convert name to a format suitable for digesting in hashes. + + The name is canonicalized and converted to uncompressed wire + format. All names in wire format are absolute. If the name + is a relative name, then an origin must be supplied. + + *origin* is a ``dns.name.Name`` or ``None``. If the name is + relative and origin is not ``None``, then origin will be appended + to the name. + + Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is + relative and no origin was provided. + + Returns a ``bytes``. + """ + + digest = self.to_wire(origin=origin, canonicalize=True) + assert digest is not None + return digest + + def to_wire( + self, + file: Optional[Any] = None, + compress: Optional[CompressType] = None, + origin: Optional["Name"] = None, + canonicalize: bool = False, + ) -> Optional[bytes]: + """Convert name to wire format, possibly compressing it. + + *file* is the file where the name is emitted (typically an + io.BytesIO file). If ``None`` (the default), a ``bytes`` + containing the wire name will be returned. + + *compress*, a ``dict``, is the compression table to use. If + ``None`` (the default), names will not be compressed. Note that + the compression code assumes that compression offset 0 is the + start of *file*, and thus compression will not be correct + if this is not the case. + + *origin* is a ``dns.name.Name`` or ``None``. If the name is + relative and origin is not ``None``, then *origin* will be appended + to it. + + *canonicalize*, a ``bool``, indicates whether the name should + be canonicalized; that is, converted to a format suitable for + digesting in hashes. + + Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is + relative and no origin was provided. + + Returns a ``bytes`` or ``None``. + """ + + if file is None: + out = bytearray() + for label in self.labels: + out.append(len(label)) + if canonicalize: + out += label.lower() + else: + out += label + if not self.is_absolute(): + if origin is None or not origin.is_absolute(): + raise NeedAbsoluteNameOrOrigin + for label in origin.labels: + out.append(len(label)) + if canonicalize: + out += label.lower() + else: + out += label + return bytes(out) + + labels: Iterable[bytes] + if not self.is_absolute(): + if origin is None or not origin.is_absolute(): + raise NeedAbsoluteNameOrOrigin + labels = list(self.labels) + labels.extend(list(origin.labels)) + else: + labels = self.labels + i = 0 + for label in labels: + n = Name(labels[i:]) + i += 1 + if compress is not None: + pos = compress.get(n) + else: + pos = None + if pos is not None: + value = 0xC000 + pos + s = struct.pack("!H", value) + file.write(s) + break + else: + if compress is not None and len(n) > 1: + pos = file.tell() + if pos <= 0x3FFF: + compress[n] = pos + l = len(label) + file.write(struct.pack("!B", l)) + if l > 0: + if canonicalize: + file.write(label.lower()) + else: + file.write(label) + return None + + def __len__(self) -> int: + """The length of the name (in labels). + + Returns an ``int``. + """ + + return len(self.labels) + + def __getitem__(self, index): + return self.labels[index] + + def __add__(self, other): + return self.concatenate(other) + + def __sub__(self, other): + return self.relativize(other) + + def split(self, depth: int) -> Tuple["Name", "Name"]: + """Split a name into a prefix and suffix names at the specified depth. + + *depth* is an ``int`` specifying the number of labels in the suffix + + Raises ``ValueError`` if *depth* was not >= 0 and <= the length of the + name. + + Returns the tuple ``(prefix, suffix)``. + """ + + l = len(self.labels) + if depth == 0: + return (self, dns.name.empty) + elif depth == l: + return (dns.name.empty, self) + elif depth < 0 or depth > l: + raise ValueError("depth must be >= 0 and <= the length of the name") + return (Name(self[:-depth]), Name(self[-depth:])) + + def concatenate(self, other: "Name") -> "Name": + """Return a new name which is the concatenation of self and other. + + Raises ``dns.name.AbsoluteConcatenation`` if the name is + absolute and *other* is not the empty name. + + Returns a ``dns.name.Name``. + """ + + if self.is_absolute() and len(other) > 0: + raise AbsoluteConcatenation + labels = list(self.labels) + labels.extend(list(other.labels)) + return Name(labels) + + def relativize(self, origin: "Name") -> "Name": + """If the name is a subdomain of *origin*, return a new name which is + the name relative to origin. Otherwise return the name. + + For example, relativizing ``www.dnspython.org.`` to origin + ``dnspython.org.`` returns the name ``www``. Relativizing ``example.`` + to origin ``dnspython.org.`` returns ``example.``. + + Returns a ``dns.name.Name``. + """ + + if origin is not None and self.is_subdomain(origin): + return Name(self[: -len(origin)]) + else: + return self + + def derelativize(self, origin: "Name") -> "Name": + """If the name is a relative name, return a new name which is the + concatenation of the name and origin. Otherwise return the name. + + For example, derelativizing ``www`` to origin ``dnspython.org.`` + returns the name ``www.dnspython.org.``. Derelativizing ``example.`` + to origin ``dnspython.org.`` returns ``example.``. + + Returns a ``dns.name.Name``. + """ + + if not self.is_absolute(): + return self.concatenate(origin) + else: + return self + + def choose_relativity( + self, origin: Optional["Name"] = None, relativize: bool = True + ) -> "Name": + """Return a name with the relativity desired by the caller. + + If *origin* is ``None``, then the name is returned. + Otherwise, if *relativize* is ``True`` the name is + relativized, and if *relativize* is ``False`` the name is + derelativized. + + Returns a ``dns.name.Name``. + """ + + if origin: + if relativize: + return self.relativize(origin) + else: + return self.derelativize(origin) + else: + return self + + def parent(self) -> "Name": + """Return the parent of the name. + + For example, the parent of ``www.dnspython.org.`` is ``dnspython.org``. + + Raises ``dns.name.NoParent`` if the name is either the root name or the + empty name, and thus has no parent. + + Returns a ``dns.name.Name``. + """ + + if self == root or self == empty: + raise NoParent + return Name(self.labels[1:]) + + def predecessor(self, origin: "Name", prefix_ok: bool = True) -> "Name": + """Return the maximal predecessor of *name* in the DNSSEC ordering in the zone + whose origin is *origin*, or return the longest name under *origin* if the + name is origin (i.e. wrap around to the longest name, which may still be + *origin* due to length considerations. + + The relativity of the name is preserved, so if this name is relative + then the method will return a relative name, and likewise if this name + is absolute then the predecessor will be absolute. + + *prefix_ok* indicates if prefixing labels is allowed, and + defaults to ``True``. Normally it is good to allow this, but if computing + a maximal predecessor at a zone cut point then ``False`` must be specified. + """ + return _handle_relativity_and_call( + _absolute_predecessor, self, origin, prefix_ok + ) + + def successor(self, origin: "Name", prefix_ok: bool = True) -> "Name": + """Return the minimal successor of *name* in the DNSSEC ordering in the zone + whose origin is *origin*, or return *origin* if the successor cannot be + computed due to name length limitations. + + Note that *origin* is returned in the "too long" cases because wrapping + around to the origin is how NSEC records express "end of the zone". + + The relativity of the name is preserved, so if this name is relative + then the method will return a relative name, and likewise if this name + is absolute then the successor will be absolute. + + *prefix_ok* indicates if prefixing a new minimal label is allowed, and + defaults to ``True``. Normally it is good to allow this, but if computing + a minimal successor at a zone cut point then ``False`` must be specified. + """ + return _handle_relativity_and_call(_absolute_successor, self, origin, prefix_ok) + + +#: The root name, '.' +root = Name([b""]) + +#: The empty name. +empty = Name([]) + + +def from_unicode( + text: str, origin: Optional[Name] = root, idna_codec: Optional[IDNACodec] = None +) -> Name: + """Convert unicode text into a Name object. + + Labels are encoded in IDN ACE form according to rules specified by + the IDNA codec. + + *text*, a ``str``, is the text to convert into a name. + + *origin*, a ``dns.name.Name``, specifies the origin to + append to non-absolute names. The default is the root name. + + *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA + encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder + is used. + + Returns a ``dns.name.Name``. + """ + + if not isinstance(text, str): + raise ValueError("input to from_unicode() must be a unicode string") + if not (origin is None or isinstance(origin, Name)): + raise ValueError("origin must be a Name or None") + labels = [] + label = "" + escaping = False + edigits = 0 + total = 0 + if idna_codec is None: + idna_codec = IDNA_2003 + if text == "@": + text = "" + if text: + if text in [".", "\u3002", "\uff0e", "\uff61"]: + return Name([b""]) # no Unicode "u" on this constant! + for c in text: + if escaping: + if edigits == 0: + if c.isdigit(): + total = int(c) + edigits += 1 + else: + label += c + escaping = False + else: + if not c.isdigit(): + raise BadEscape + total *= 10 + total += int(c) + edigits += 1 + if edigits == 3: + escaping = False + label += chr(total) + elif c in [".", "\u3002", "\uff0e", "\uff61"]: + if len(label) == 0: + raise EmptyLabel + labels.append(idna_codec.encode(label)) + label = "" + elif c == "\\": + escaping = True + edigits = 0 + total = 0 + else: + label += c + if escaping: + raise BadEscape + if len(label) > 0: + labels.append(idna_codec.encode(label)) + else: + labels.append(b"") + + if (len(labels) == 0 or labels[-1] != b"") and origin is not None: + labels.extend(list(origin.labels)) + return Name(labels) + + +def is_all_ascii(text: str) -> bool: + for c in text: + if ord(c) > 0x7F: + return False + return True + + +def from_text( + text: Union[bytes, str], + origin: Optional[Name] = root, + idna_codec: Optional[IDNACodec] = None, +) -> Name: + """Convert text into a Name object. + + *text*, a ``bytes`` or ``str``, is the text to convert into a name. + + *origin*, a ``dns.name.Name``, specifies the origin to + append to non-absolute names. The default is the root name. + + *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA + encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder + is used. + + Returns a ``dns.name.Name``. + """ + + if isinstance(text, str): + if not is_all_ascii(text): + # Some codepoint in the input text is > 127, so IDNA applies. + return from_unicode(text, origin, idna_codec) + # The input is all ASCII, so treat this like an ordinary non-IDNA + # domain name. Note that "all ASCII" is about the input text, + # not the codepoints in the domain name. E.g. if text has value + # + # r'\150\151\152\153\154\155\156\157\158\159' + # + # then it's still "all ASCII" even though the domain name has + # codepoints > 127. + text = text.encode("ascii") + if not isinstance(text, bytes): + raise ValueError("input to from_text() must be a string") + if not (origin is None or isinstance(origin, Name)): + raise ValueError("origin must be a Name or None") + labels = [] + label = b"" + escaping = False + edigits = 0 + total = 0 + if text == b"@": + text = b"" + if text: + if text == b".": + return Name([b""]) + for c in text: + byte_ = struct.pack("!B", c) + if escaping: + if edigits == 0: + if byte_.isdigit(): + total = int(byte_) + edigits += 1 + else: + label += byte_ + escaping = False + else: + if not byte_.isdigit(): + raise BadEscape + total *= 10 + total += int(byte_) + edigits += 1 + if edigits == 3: + escaping = False + label += struct.pack("!B", total) + elif byte_ == b".": + if len(label) == 0: + raise EmptyLabel + labels.append(label) + label = b"" + elif byte_ == b"\\": + escaping = True + edigits = 0 + total = 0 + else: + label += byte_ + if escaping: + raise BadEscape + if len(label) > 0: + labels.append(label) + else: + labels.append(b"") + if (len(labels) == 0 or labels[-1] != b"") and origin is not None: + labels.extend(list(origin.labels)) + return Name(labels) + + +# we need 'dns.wire.Parser' quoted as dns.name and dns.wire depend on each other. + + +def from_wire_parser(parser: "dns.wire.Parser") -> Name: + """Convert possibly compressed wire format into a Name. + + *parser* is a dns.wire.Parser. + + Raises ``dns.name.BadPointer`` if a compression pointer did not + point backwards in the message. + + Raises ``dns.name.BadLabelType`` if an invalid label type was encountered. + + Returns a ``dns.name.Name`` + """ + + labels = [] + biggest_pointer = parser.current + with parser.restore_furthest(): + count = parser.get_uint8() + while count != 0: + if count < 64: + labels.append(parser.get_bytes(count)) + elif count >= 192: + current = (count & 0x3F) * 256 + parser.get_uint8() + if current >= biggest_pointer: + raise BadPointer + biggest_pointer = current + parser.seek(current) + else: + raise BadLabelType + count = parser.get_uint8() + labels.append(b"") + return Name(labels) + + +def from_wire(message: bytes, current: int) -> Tuple[Name, int]: + """Convert possibly compressed wire format into a Name. + + *message* is a ``bytes`` containing an entire DNS message in DNS + wire form. + + *current*, an ``int``, is the offset of the beginning of the name + from the start of the message + + Raises ``dns.name.BadPointer`` if a compression pointer did not + point backwards in the message. + + Raises ``dns.name.BadLabelType`` if an invalid label type was encountered. + + Returns a ``(dns.name.Name, int)`` tuple consisting of the name + that was read and the number of bytes of the wire format message + which were consumed reading it. + """ + + if not isinstance(message, bytes): + raise ValueError("input to from_wire() must be a byte string") + parser = dns.wire.Parser(message, current) + name = from_wire_parser(parser) + return (name, parser.current - current) + + +# RFC 4471 Support + +_MINIMAL_OCTET = b"\x00" +_MINIMAL_OCTET_VALUE = ord(_MINIMAL_OCTET) +_SUCCESSOR_PREFIX = Name([_MINIMAL_OCTET]) +_MAXIMAL_OCTET = b"\xff" +_MAXIMAL_OCTET_VALUE = ord(_MAXIMAL_OCTET) +_AT_SIGN_VALUE = ord("@") +_LEFT_SQUARE_BRACKET_VALUE = ord("[") + + +def _wire_length(labels): + return functools.reduce(lambda v, x: v + len(x) + 1, labels, 0) + + +def _pad_to_max_name(name): + needed = 255 - _wire_length(name.labels) + new_labels = [] + while needed > 64: + new_labels.append(_MAXIMAL_OCTET * 63) + needed -= 64 + if needed >= 2: + new_labels.append(_MAXIMAL_OCTET * (needed - 1)) + # Note we're already maximal in the needed == 1 case as while we'd like + # to add one more byte as a new label, we can't, as adding a new non-empty + # label requires at least 2 bytes. + new_labels = list(reversed(new_labels)) + new_labels.extend(name.labels) + return Name(new_labels) + + +def _pad_to_max_label(label, suffix_labels): + length = len(label) + # We have to subtract one here to account for the length byte of label. + remaining = 255 - _wire_length(suffix_labels) - length - 1 + if remaining <= 0: + # Shouldn't happen! + return label + needed = min(63 - length, remaining) + return label + _MAXIMAL_OCTET * needed + + +def _absolute_predecessor(name: Name, origin: Name, prefix_ok: bool) -> Name: + # This is the RFC 4471 predecessor algorithm using the "absolute method" of section + # 3.1.1. + # + # Our caller must ensure that the name and origin are absolute, and that name is a + # subdomain of origin. + if name == origin: + return _pad_to_max_name(name) + least_significant_label = name[0] + if least_significant_label == _MINIMAL_OCTET: + return name.parent() + least_octet = least_significant_label[-1] + suffix_labels = name.labels[1:] + if least_octet == _MINIMAL_OCTET_VALUE: + new_labels = [least_significant_label[:-1]] + else: + octets = bytearray(least_significant_label) + octet = octets[-1] + if octet == _LEFT_SQUARE_BRACKET_VALUE: + octet = _AT_SIGN_VALUE + else: + octet -= 1 + octets[-1] = octet + least_significant_label = bytes(octets) + new_labels = [_pad_to_max_label(least_significant_label, suffix_labels)] + new_labels.extend(suffix_labels) + name = Name(new_labels) + if prefix_ok: + return _pad_to_max_name(name) + else: + return name + + +def _absolute_successor(name: Name, origin: Name, prefix_ok: bool) -> Name: + # This is the RFC 4471 successor algorithm using the "absolute method" of section + # 3.1.2. + # + # Our caller must ensure that the name and origin are absolute, and that name is a + # subdomain of origin. + if prefix_ok: + # Try prefixing \000 as new label + try: + return _SUCCESSOR_PREFIX.concatenate(name) + except NameTooLong: + pass + while name != origin: + # Try extending the least significant label. + least_significant_label = name[0] + if len(least_significant_label) < 63: + # We may be able to extend the least label with a minimal additional byte. + # This is only "may" because we could have a maximal length name even though + # the least significant label isn't maximally long. + new_labels = [least_significant_label + _MINIMAL_OCTET] + new_labels.extend(name.labels[1:]) + try: + return dns.name.Name(new_labels) + except dns.name.NameTooLong: + pass + # We can't extend the label either, so we'll try to increment the least + # signficant non-maximal byte in it. + octets = bytearray(least_significant_label) + # We do this reversed iteration with an explicit indexing variable because + # if we find something to increment, we're going to want to truncate everything + # to the right of it. + for i in range(len(octets) - 1, -1, -1): + octet = octets[i] + if octet == _MAXIMAL_OCTET_VALUE: + # We can't increment this, so keep looking. + continue + # Finally, something we can increment. We have to apply a special rule for + # incrementing "@", sending it to "[", because RFC 4034 6.1 says that when + # comparing names, uppercase letters compare as if they were their + # lower-case equivalents. If we increment "@" to "A", then it would compare + # as "a", which is after "[", "\", "]", "^", "_", and "`", so we would have + # skipped the most minimal successor, namely "[". + if octet == _AT_SIGN_VALUE: + octet = _LEFT_SQUARE_BRACKET_VALUE + else: + octet += 1 + octets[i] = octet + # We can now truncate all of the maximal values we skipped (if any) + new_labels = [bytes(octets[: i + 1])] + new_labels.extend(name.labels[1:]) + # We haven't changed the length of the name, so the Name constructor will + # always work. + return Name(new_labels) + # We couldn't increment, so chop off the least significant label and try + # again. + name = name.parent() + + # We couldn't increment at all, so return the origin, as wrapping around is the + # DNSSEC way. + return origin + + +def _handle_relativity_and_call( + function: Callable[[Name, Name, bool], Name], + name: Name, + origin: Name, + prefix_ok: bool, +) -> Name: + # Make "name" absolute if needed, ensure that the origin is absolute, + # call function(), and then relativize the result if needed. + if not origin.is_absolute(): + raise NeedAbsoluteNameOrOrigin + relative = not name.is_absolute() + if relative: + name = name.derelativize(origin) + elif not name.is_subdomain(origin): + raise NeedSubdomainOfOrigin + result_name = function(name, origin, prefix_ok) + if relative: + result_name = result_name.relativize(origin) + return result_name diff --git a/.venv/lib/python3.9/site-packages/dns/namedict.py b/.venv/lib/python3.9/site-packages/dns/namedict.py new file mode 100644 index 0000000..ca8b197 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/namedict.py @@ -0,0 +1,109 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2017 Nominum, Inc. +# Copyright (C) 2016 Coresec Systems AB +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND CORESEC SYSTEMS AB DISCLAIMS ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL CORESEC +# SYSTEMS AB BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS name dictionary""" + +# pylint seems to be confused about this one! +from collections.abc import MutableMapping # pylint: disable=no-name-in-module + +import dns.name + + +class NameDict(MutableMapping): + """A dictionary whose keys are dns.name.Name objects. + + In addition to being like a regular Python dictionary, this + dictionary can also get the deepest match for a given key. + """ + + __slots__ = ["max_depth", "max_depth_items", "__store"] + + def __init__(self, *args, **kwargs): + super().__init__() + self.__store = dict() + #: the maximum depth of the keys that have ever been added + self.max_depth = 0 + #: the number of items of maximum depth + self.max_depth_items = 0 + self.update(dict(*args, **kwargs)) + + def __update_max_depth(self, key): + if len(key) == self.max_depth: + self.max_depth_items = self.max_depth_items + 1 + elif len(key) > self.max_depth: + self.max_depth = len(key) + self.max_depth_items = 1 + + def __getitem__(self, key): + return self.__store[key] + + def __setitem__(self, key, value): + if not isinstance(key, dns.name.Name): + raise ValueError("NameDict key must be a name") + self.__store[key] = value + self.__update_max_depth(key) + + def __delitem__(self, key): + self.__store.pop(key) + if len(key) == self.max_depth: + self.max_depth_items = self.max_depth_items - 1 + if self.max_depth_items == 0: + self.max_depth = 0 + for k in self.__store: + self.__update_max_depth(k) + + def __iter__(self): + return iter(self.__store) + + def __len__(self): + return len(self.__store) + + def has_key(self, key): + return key in self.__store + + def get_deepest_match(self, name): + """Find the deepest match to *name* in the dictionary. + + The deepest match is the longest name in the dictionary which is + a superdomain of *name*. Note that *superdomain* includes matching + *name* itself. + + *name*, a ``dns.name.Name``, the name to find. + + Returns a ``(key, value)`` where *key* is the deepest + ``dns.name.Name``, and *value* is the value associated with *key*. + """ + + depth = len(name) + if depth > self.max_depth: + depth = self.max_depth + for i in range(-depth, 0): + n = dns.name.Name(name[i:]) + if n in self: + return (n, self[n]) + v = self[dns.name.empty] + return (dns.name.empty, v) diff --git a/.venv/lib/python3.9/site-packages/dns/nameserver.py b/.venv/lib/python3.9/site-packages/dns/nameserver.py new file mode 100644 index 0000000..b02a239 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/nameserver.py @@ -0,0 +1,363 @@ +from typing import Optional, Union +from urllib.parse import urlparse + +import dns.asyncbackend +import dns.asyncquery +import dns.inet +import dns.message +import dns.query + + +class Nameserver: + def __init__(self): + pass + + def __str__(self): + raise NotImplementedError + + def kind(self) -> str: + raise NotImplementedError + + def is_always_max_size(self) -> bool: + raise NotImplementedError + + def answer_nameserver(self) -> str: + raise NotImplementedError + + def answer_port(self) -> int: + raise NotImplementedError + + def query( + self, + request: dns.message.QueryMessage, + timeout: float, + source: Optional[str], + source_port: int, + max_size: bool, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + ) -> dns.message.Message: + raise NotImplementedError + + async def async_query( + self, + request: dns.message.QueryMessage, + timeout: float, + source: Optional[str], + source_port: int, + max_size: bool, + backend: dns.asyncbackend.Backend, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + ) -> dns.message.Message: + raise NotImplementedError + + +class AddressAndPortNameserver(Nameserver): + def __init__(self, address: str, port: int): + super().__init__() + self.address = address + self.port = port + + def kind(self) -> str: + raise NotImplementedError + + def is_always_max_size(self) -> bool: + return False + + def __str__(self): + ns_kind = self.kind() + return f"{ns_kind}:{self.address}@{self.port}" + + def answer_nameserver(self) -> str: + return self.address + + def answer_port(self) -> int: + return self.port + + +class Do53Nameserver(AddressAndPortNameserver): + def __init__(self, address: str, port: int = 53): + super().__init__(address, port) + + def kind(self): + return "Do53" + + def query( + self, + request: dns.message.QueryMessage, + timeout: float, + source: Optional[str], + source_port: int, + max_size: bool, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + ) -> dns.message.Message: + if max_size: + response = dns.query.tcp( + request, + self.address, + timeout=timeout, + port=self.port, + source=source, + source_port=source_port, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + ) + else: + response = dns.query.udp( + request, + self.address, + timeout=timeout, + port=self.port, + source=source, + source_port=source_port, + raise_on_truncation=True, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + ignore_errors=True, + ignore_unexpected=True, + ) + return response + + async def async_query( + self, + request: dns.message.QueryMessage, + timeout: float, + source: Optional[str], + source_port: int, + max_size: bool, + backend: dns.asyncbackend.Backend, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + ) -> dns.message.Message: + if max_size: + response = await dns.asyncquery.tcp( + request, + self.address, + timeout=timeout, + port=self.port, + source=source, + source_port=source_port, + backend=backend, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + ) + else: + response = await dns.asyncquery.udp( + request, + self.address, + timeout=timeout, + port=self.port, + source=source, + source_port=source_port, + raise_on_truncation=True, + backend=backend, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + ignore_errors=True, + ignore_unexpected=True, + ) + return response + + +class DoHNameserver(Nameserver): + def __init__( + self, + url: str, + bootstrap_address: Optional[str] = None, + verify: Union[bool, str] = True, + want_get: bool = False, + http_version: dns.query.HTTPVersion = dns.query.HTTPVersion.DEFAULT, + ): + super().__init__() + self.url = url + self.bootstrap_address = bootstrap_address + self.verify = verify + self.want_get = want_get + self.http_version = http_version + + def kind(self): + return "DoH" + + def is_always_max_size(self) -> bool: + return True + + def __str__(self): + return self.url + + def answer_nameserver(self) -> str: + return self.url + + def answer_port(self) -> int: + port = urlparse(self.url).port + if port is None: + port = 443 + return port + + def query( + self, + request: dns.message.QueryMessage, + timeout: float, + source: Optional[str], + source_port: int, + max_size: bool = False, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + ) -> dns.message.Message: + return dns.query.https( + request, + self.url, + timeout=timeout, + source=source, + source_port=source_port, + bootstrap_address=self.bootstrap_address, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + verify=self.verify, + post=(not self.want_get), + http_version=self.http_version, + ) + + async def async_query( + self, + request: dns.message.QueryMessage, + timeout: float, + source: Optional[str], + source_port: int, + max_size: bool, + backend: dns.asyncbackend.Backend, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + ) -> dns.message.Message: + return await dns.asyncquery.https( + request, + self.url, + timeout=timeout, + source=source, + source_port=source_port, + bootstrap_address=self.bootstrap_address, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + verify=self.verify, + post=(not self.want_get), + http_version=self.http_version, + ) + + +class DoTNameserver(AddressAndPortNameserver): + def __init__( + self, + address: str, + port: int = 853, + hostname: Optional[str] = None, + verify: Union[bool, str] = True, + ): + super().__init__(address, port) + self.hostname = hostname + self.verify = verify + + def kind(self): + return "DoT" + + def query( + self, + request: dns.message.QueryMessage, + timeout: float, + source: Optional[str], + source_port: int, + max_size: bool = False, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + ) -> dns.message.Message: + return dns.query.tls( + request, + self.address, + port=self.port, + timeout=timeout, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + server_hostname=self.hostname, + verify=self.verify, + ) + + async def async_query( + self, + request: dns.message.QueryMessage, + timeout: float, + source: Optional[str], + source_port: int, + max_size: bool, + backend: dns.asyncbackend.Backend, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + ) -> dns.message.Message: + return await dns.asyncquery.tls( + request, + self.address, + port=self.port, + timeout=timeout, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + server_hostname=self.hostname, + verify=self.verify, + ) + + +class DoQNameserver(AddressAndPortNameserver): + def __init__( + self, + address: str, + port: int = 853, + verify: Union[bool, str] = True, + server_hostname: Optional[str] = None, + ): + super().__init__(address, port) + self.verify = verify + self.server_hostname = server_hostname + + def kind(self): + return "DoQ" + + def query( + self, + request: dns.message.QueryMessage, + timeout: float, + source: Optional[str], + source_port: int, + max_size: bool = False, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + ) -> dns.message.Message: + return dns.query.quic( + request, + self.address, + port=self.port, + timeout=timeout, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + verify=self.verify, + server_hostname=self.server_hostname, + ) + + async def async_query( + self, + request: dns.message.QueryMessage, + timeout: float, + source: Optional[str], + source_port: int, + max_size: bool, + backend: dns.asyncbackend.Backend, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + ) -> dns.message.Message: + return await dns.asyncquery.quic( + request, + self.address, + port=self.port, + timeout=timeout, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + verify=self.verify, + server_hostname=self.server_hostname, + ) diff --git a/.venv/lib/python3.9/site-packages/dns/node.py b/.venv/lib/python3.9/site-packages/dns/node.py new file mode 100644 index 0000000..de85a82 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/node.py @@ -0,0 +1,359 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2001-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS nodes. A node is a set of rdatasets.""" + +import enum +import io +from typing import Any, Dict, Optional + +import dns.immutable +import dns.name +import dns.rdataclass +import dns.rdataset +import dns.rdatatype +import dns.renderer +import dns.rrset + +_cname_types = { + dns.rdatatype.CNAME, +} + +# "neutral" types can coexist with a CNAME and thus are not "other data" +_neutral_types = { + dns.rdatatype.NSEC, # RFC 4035 section 2.5 + dns.rdatatype.NSEC3, # This is not likely to happen, but not impossible! + dns.rdatatype.KEY, # RFC 4035 section 2.5, RFC 3007 +} + + +def _matches_type_or_its_signature(rdtypes, rdtype, covers): + return rdtype in rdtypes or (rdtype == dns.rdatatype.RRSIG and covers in rdtypes) + + +@enum.unique +class NodeKind(enum.Enum): + """Rdatasets in nodes""" + + REGULAR = 0 # a.k.a "other data" + NEUTRAL = 1 + CNAME = 2 + + @classmethod + def classify( + cls, rdtype: dns.rdatatype.RdataType, covers: dns.rdatatype.RdataType + ) -> "NodeKind": + if _matches_type_or_its_signature(_cname_types, rdtype, covers): + return NodeKind.CNAME + elif _matches_type_or_its_signature(_neutral_types, rdtype, covers): + return NodeKind.NEUTRAL + else: + return NodeKind.REGULAR + + @classmethod + def classify_rdataset(cls, rdataset: dns.rdataset.Rdataset) -> "NodeKind": + return cls.classify(rdataset.rdtype, rdataset.covers) + + +class Node: + """A Node is a set of rdatasets. + + A node is either a CNAME node or an "other data" node. A CNAME + node contains only CNAME, KEY, NSEC, and NSEC3 rdatasets along with their + covering RRSIG rdatasets. An "other data" node contains any + rdataset other than a CNAME or RRSIG(CNAME) rdataset. When + changes are made to a node, the CNAME or "other data" state is + always consistent with the update, i.e. the most recent change + wins. For example, if you have a node which contains a CNAME + rdataset, and then add an MX rdataset to it, then the CNAME + rdataset will be deleted. Likewise if you have a node containing + an MX rdataset and add a CNAME rdataset, the MX rdataset will be + deleted. + """ + + __slots__ = ["rdatasets"] + + def __init__(self): + # the set of rdatasets, represented as a list. + self.rdatasets = [] + + def to_text(self, name: dns.name.Name, **kw: Dict[str, Any]) -> str: + """Convert a node to text format. + + Each rdataset at the node is printed. Any keyword arguments + to this method are passed on to the rdataset's to_text() method. + + *name*, a ``dns.name.Name``, the owner name of the + rdatasets. + + Returns a ``str``. + + """ + + s = io.StringIO() + for rds in self.rdatasets: + if len(rds) > 0: + s.write(rds.to_text(name, **kw)) # type: ignore[arg-type] + s.write("\n") + return s.getvalue()[:-1] + + def __repr__(self): + return "" + + def __eq__(self, other): + # + # This is inefficient. Good thing we don't need to do it much. + # + for rd in self.rdatasets: + if rd not in other.rdatasets: + return False + for rd in other.rdatasets: + if rd not in self.rdatasets: + return False + return True + + def __ne__(self, other): + return not self.__eq__(other) + + def __len__(self): + return len(self.rdatasets) + + def __iter__(self): + return iter(self.rdatasets) + + def _append_rdataset(self, rdataset): + """Append rdataset to the node with special handling for CNAME and + other data conditions. + + Specifically, if the rdataset being appended has ``NodeKind.CNAME``, + then all rdatasets other than KEY, NSEC, NSEC3, and their covering + RRSIGs are deleted. If the rdataset being appended has + ``NodeKind.REGULAR`` then CNAME and RRSIG(CNAME) are deleted. + """ + # Make having just one rdataset at the node fast. + if len(self.rdatasets) > 0: + kind = NodeKind.classify_rdataset(rdataset) + if kind == NodeKind.CNAME: + self.rdatasets = [ + rds + for rds in self.rdatasets + if NodeKind.classify_rdataset(rds) != NodeKind.REGULAR + ] + elif kind == NodeKind.REGULAR: + self.rdatasets = [ + rds + for rds in self.rdatasets + if NodeKind.classify_rdataset(rds) != NodeKind.CNAME + ] + # Otherwise the rdataset is NodeKind.NEUTRAL and we do not need to + # edit self.rdatasets. + self.rdatasets.append(rdataset) + + def find_rdataset( + self, + rdclass: dns.rdataclass.RdataClass, + rdtype: dns.rdatatype.RdataType, + covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, + create: bool = False, + ) -> dns.rdataset.Rdataset: + """Find an rdataset matching the specified properties in the + current node. + + *rdclass*, a ``dns.rdataclass.RdataClass``, the class of the rdataset. + + *rdtype*, a ``dns.rdatatype.RdataType``, the type of the rdataset. + + *covers*, a ``dns.rdatatype.RdataType``, the covered type. + Usually this value is ``dns.rdatatype.NONE``, but if the + rdtype is ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, + then the covers value will be the rdata type the SIG/RRSIG + covers. The library treats the SIG and RRSIG types as if they + were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). + This makes RRSIGs much easier to work with than if RRSIGs + covering different rdata types were aggregated into a single + RRSIG rdataset. + + *create*, a ``bool``. If True, create the rdataset if it is not found. + + Raises ``KeyError`` if an rdataset of the desired type and class does + not exist and *create* is not ``True``. + + Returns a ``dns.rdataset.Rdataset``. + """ + + for rds in self.rdatasets: + if rds.match(rdclass, rdtype, covers): + return rds + if not create: + raise KeyError + rds = dns.rdataset.Rdataset(rdclass, rdtype, covers) + self._append_rdataset(rds) + return rds + + def get_rdataset( + self, + rdclass: dns.rdataclass.RdataClass, + rdtype: dns.rdatatype.RdataType, + covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, + create: bool = False, + ) -> Optional[dns.rdataset.Rdataset]: + """Get an rdataset matching the specified properties in the + current node. + + None is returned if an rdataset of the specified type and + class does not exist and *create* is not ``True``. + + *rdclass*, an ``int``, the class of the rdataset. + + *rdtype*, an ``int``, the type of the rdataset. + + *covers*, an ``int``, the covered type. Usually this value is + dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or + dns.rdatatype.RRSIG, then the covers value will be the rdata + type the SIG/RRSIG covers. The library treats the SIG and RRSIG + types as if they were a family of + types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This makes RRSIGs much + easier to work with than if RRSIGs covering different rdata + types were aggregated into a single RRSIG rdataset. + + *create*, a ``bool``. If True, create the rdataset if it is not found. + + Returns a ``dns.rdataset.Rdataset`` or ``None``. + """ + + try: + rds = self.find_rdataset(rdclass, rdtype, covers, create) + except KeyError: + rds = None + return rds + + def delete_rdataset( + self, + rdclass: dns.rdataclass.RdataClass, + rdtype: dns.rdatatype.RdataType, + covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, + ) -> None: + """Delete the rdataset matching the specified properties in the + current node. + + If a matching rdataset does not exist, it is not an error. + + *rdclass*, an ``int``, the class of the rdataset. + + *rdtype*, an ``int``, the type of the rdataset. + + *covers*, an ``int``, the covered type. + """ + + rds = self.get_rdataset(rdclass, rdtype, covers) + if rds is not None: + self.rdatasets.remove(rds) + + def replace_rdataset(self, replacement: dns.rdataset.Rdataset) -> None: + """Replace an rdataset. + + It is not an error if there is no rdataset matching *replacement*. + + Ownership of the *replacement* object is transferred to the node; + in other words, this method does not store a copy of *replacement* + at the node, it stores *replacement* itself. + + *replacement*, a ``dns.rdataset.Rdataset``. + + Raises ``ValueError`` if *replacement* is not a + ``dns.rdataset.Rdataset``. + """ + + if not isinstance(replacement, dns.rdataset.Rdataset): + raise ValueError("replacement is not an rdataset") + if isinstance(replacement, dns.rrset.RRset): + # RRsets are not good replacements as the match() method + # is not compatible. + replacement = replacement.to_rdataset() + self.delete_rdataset( + replacement.rdclass, replacement.rdtype, replacement.covers + ) + self._append_rdataset(replacement) + + def classify(self) -> NodeKind: + """Classify a node. + + A node which contains a CNAME or RRSIG(CNAME) is a + ``NodeKind.CNAME`` node. + + A node which contains only "neutral" types, i.e. types allowed to + co-exist with a CNAME, is a ``NodeKind.NEUTRAL`` node. The neutral + types are NSEC, NSEC3, KEY, and their associated RRSIGS. An empty node + is also considered neutral. + + A node which contains some rdataset which is not a CNAME, RRSIG(CNAME), + or a neutral type is a a ``NodeKind.REGULAR`` node. Regular nodes are + also commonly referred to as "other data". + """ + for rdataset in self.rdatasets: + kind = NodeKind.classify(rdataset.rdtype, rdataset.covers) + if kind != NodeKind.NEUTRAL: + return kind + return NodeKind.NEUTRAL + + def is_immutable(self) -> bool: + return False + + +@dns.immutable.immutable +class ImmutableNode(Node): + def __init__(self, node): + super().__init__() + self.rdatasets = tuple( + [dns.rdataset.ImmutableRdataset(rds) for rds in node.rdatasets] + ) + + def find_rdataset( + self, + rdclass: dns.rdataclass.RdataClass, + rdtype: dns.rdatatype.RdataType, + covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, + create: bool = False, + ) -> dns.rdataset.Rdataset: + if create: + raise TypeError("immutable") + return super().find_rdataset(rdclass, rdtype, covers, False) + + def get_rdataset( + self, + rdclass: dns.rdataclass.RdataClass, + rdtype: dns.rdatatype.RdataType, + covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, + create: bool = False, + ) -> Optional[dns.rdataset.Rdataset]: + if create: + raise TypeError("immutable") + return super().get_rdataset(rdclass, rdtype, covers, False) + + def delete_rdataset( + self, + rdclass: dns.rdataclass.RdataClass, + rdtype: dns.rdatatype.RdataType, + covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, + ) -> None: + raise TypeError("immutable") + + def replace_rdataset(self, replacement: dns.rdataset.Rdataset) -> None: + raise TypeError("immutable") + + def is_immutable(self) -> bool: + return True diff --git a/.venv/lib/python3.9/site-packages/dns/opcode.py b/.venv/lib/python3.9/site-packages/dns/opcode.py new file mode 100644 index 0000000..78b43d2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/opcode.py @@ -0,0 +1,117 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2001-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Opcodes.""" + +import dns.enum +import dns.exception + + +class Opcode(dns.enum.IntEnum): + #: Query + QUERY = 0 + #: Inverse Query (historical) + IQUERY = 1 + #: Server Status (unspecified and unimplemented anywhere) + STATUS = 2 + #: Notify + NOTIFY = 4 + #: Dynamic Update + UPDATE = 5 + + @classmethod + def _maximum(cls): + return 15 + + @classmethod + def _unknown_exception_class(cls): + return UnknownOpcode + + +class UnknownOpcode(dns.exception.DNSException): + """An DNS opcode is unknown.""" + + +def from_text(text: str) -> Opcode: + """Convert text into an opcode. + + *text*, a ``str``, the textual opcode + + Raises ``dns.opcode.UnknownOpcode`` if the opcode is unknown. + + Returns an ``int``. + """ + + return Opcode.from_text(text) + + +def from_flags(flags: int) -> Opcode: + """Extract an opcode from DNS message flags. + + *flags*, an ``int``, the DNS flags. + + Returns an ``int``. + """ + + return Opcode((flags & 0x7800) >> 11) + + +def to_flags(value: Opcode) -> int: + """Convert an opcode to a value suitable for ORing into DNS message + flags. + + *value*, an ``int``, the DNS opcode value. + + Returns an ``int``. + """ + + return (value << 11) & 0x7800 + + +def to_text(value: Opcode) -> str: + """Convert an opcode to text. + + *value*, an ``int`` the opcode value, + + Raises ``dns.opcode.UnknownOpcode`` if the opcode is unknown. + + Returns a ``str``. + """ + + return Opcode.to_text(value) + + +def is_update(flags: int) -> bool: + """Is the opcode in flags UPDATE? + + *flags*, an ``int``, the DNS message flags. + + Returns a ``bool``. + """ + + return from_flags(flags) == Opcode.UPDATE + + +### BEGIN generated Opcode constants + +QUERY = Opcode.QUERY +IQUERY = Opcode.IQUERY +STATUS = Opcode.STATUS +NOTIFY = Opcode.NOTIFY +UPDATE = Opcode.UPDATE + +### END generated Opcode constants diff --git a/.venv/lib/python3.9/site-packages/dns/py.typed b/.venv/lib/python3.9/site-packages/dns/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.9/site-packages/dns/query.py b/.venv/lib/python3.9/site-packages/dns/query.py new file mode 100644 index 0000000..0d8a977 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/query.py @@ -0,0 +1,1665 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Talk to a DNS server.""" + +import base64 +import contextlib +import enum +import errno +import os +import os.path +import random +import selectors +import socket +import struct +import time +import urllib.parse +from typing import Any, Dict, Optional, Tuple, Union, cast + +import dns._features +import dns.exception +import dns.inet +import dns.message +import dns.name +import dns.quic +import dns.rcode +import dns.rdataclass +import dns.rdatatype +import dns.serial +import dns.transaction +import dns.tsig +import dns.xfr + + +def _remaining(expiration): + if expiration is None: + return None + timeout = expiration - time.time() + if timeout <= 0.0: + raise dns.exception.Timeout + return timeout + + +def _expiration_for_this_attempt(timeout, expiration): + if expiration is None: + return None + return min(time.time() + timeout, expiration) + + +_have_httpx = dns._features.have("doh") +if _have_httpx: + import httpcore._backends.sync + import httpx + + _CoreNetworkBackend = httpcore.NetworkBackend + _CoreSyncStream = httpcore._backends.sync.SyncStream + + class _NetworkBackend(_CoreNetworkBackend): + def __init__(self, resolver, local_port, bootstrap_address, family): + super().__init__() + self._local_port = local_port + self._resolver = resolver + self._bootstrap_address = bootstrap_address + self._family = family + + def connect_tcp( + self, host, port, timeout, local_address, socket_options=None + ): # pylint: disable=signature-differs + addresses = [] + _, expiration = _compute_times(timeout) + if dns.inet.is_address(host): + addresses.append(host) + elif self._bootstrap_address is not None: + addresses.append(self._bootstrap_address) + else: + timeout = _remaining(expiration) + family = self._family + if local_address: + family = dns.inet.af_for_address(local_address) + answers = self._resolver.resolve_name( + host, family=family, lifetime=timeout + ) + addresses = answers.addresses() + for address in addresses: + af = dns.inet.af_for_address(address) + if local_address is not None or self._local_port != 0: + source = dns.inet.low_level_address_tuple( + (local_address, self._local_port), af + ) + else: + source = None + sock = _make_socket(af, socket.SOCK_STREAM, source) + attempt_expiration = _expiration_for_this_attempt(2.0, expiration) + try: + _connect( + sock, + dns.inet.low_level_address_tuple((address, port), af), + attempt_expiration, + ) + return _CoreSyncStream(sock) + except Exception: + pass + raise httpcore.ConnectError + + def connect_unix_socket( + self, path, timeout, socket_options=None + ): # pylint: disable=signature-differs + raise NotImplementedError + + class _HTTPTransport(httpx.HTTPTransport): + def __init__( + self, + *args, + local_port=0, + bootstrap_address=None, + resolver=None, + family=socket.AF_UNSPEC, + **kwargs, + ): + if resolver is None and bootstrap_address is None: + # pylint: disable=import-outside-toplevel,redefined-outer-name + import dns.resolver + + resolver = dns.resolver.Resolver() + super().__init__(*args, **kwargs) + self._pool._network_backend = _NetworkBackend( + resolver, local_port, bootstrap_address, family + ) + +else: + + class _HTTPTransport: # type: ignore + def connect_tcp(self, host, port, timeout, local_address): + raise NotImplementedError + + +have_doh = _have_httpx + +try: + import ssl +except ImportError: # pragma: no cover + + class ssl: # type: ignore + CERT_NONE = 0 + + class WantReadException(Exception): + pass + + class WantWriteException(Exception): + pass + + class SSLContext: + pass + + class SSLSocket: + pass + + @classmethod + def create_default_context(cls, *args, **kwargs): + raise Exception("no ssl support") # pylint: disable=broad-exception-raised + + +# Function used to create a socket. Can be overridden if needed in special +# situations. +socket_factory = socket.socket + + +class UnexpectedSource(dns.exception.DNSException): + """A DNS query response came from an unexpected address or port.""" + + +class BadResponse(dns.exception.FormError): + """A DNS query response does not respond to the question asked.""" + + +class NoDOH(dns.exception.DNSException): + """DNS over HTTPS (DOH) was requested but the httpx module is not + available.""" + + +class NoDOQ(dns.exception.DNSException): + """DNS over QUIC (DOQ) was requested but the aioquic module is not + available.""" + + +# for backwards compatibility +TransferError = dns.xfr.TransferError + + +def _compute_times(timeout): + now = time.time() + if timeout is None: + return (now, None) + else: + return (now, now + timeout) + + +def _wait_for(fd, readable, writable, _, expiration): + # Use the selected selector class to wait for any of the specified + # events. An "expiration" absolute time is converted into a relative + # timeout. + # + # The unused parameter is 'error', which is always set when + # selecting for read or write, and we have no error-only selects. + + if readable and isinstance(fd, ssl.SSLSocket) and fd.pending() > 0: + return True + sel = selectors.DefaultSelector() + events = 0 + if readable: + events |= selectors.EVENT_READ + if writable: + events |= selectors.EVENT_WRITE + if events: + sel.register(fd, events) + if expiration is None: + timeout = None + else: + timeout = expiration - time.time() + if timeout <= 0.0: + raise dns.exception.Timeout + if not sel.select(timeout): + raise dns.exception.Timeout + + +def _wait_for_readable(s, expiration): + _wait_for(s, True, False, True, expiration) + + +def _wait_for_writable(s, expiration): + _wait_for(s, False, True, True, expiration) + + +def _addresses_equal(af, a1, a2): + # Convert the first value of the tuple, which is a textual format + # address into binary form, so that we are not confused by different + # textual representations of the same address + try: + n1 = dns.inet.inet_pton(af, a1[0]) + n2 = dns.inet.inet_pton(af, a2[0]) + except dns.exception.SyntaxError: + return False + return n1 == n2 and a1[1:] == a2[1:] + + +def _matches_destination(af, from_address, destination, ignore_unexpected): + # Check that from_address is appropriate for a response to a query + # sent to destination. + if not destination: + return True + if _addresses_equal(af, from_address, destination) or ( + dns.inet.is_multicast(destination[0]) and from_address[1:] == destination[1:] + ): + return True + elif ignore_unexpected: + return False + raise UnexpectedSource( + f"got a response from {from_address} instead of " f"{destination}" + ) + + +def _destination_and_source( + where, port, source, source_port, where_must_be_address=True +): + # Apply defaults and compute destination and source tuples + # suitable for use in connect(), sendto(), or bind(). + af = None + destination = None + try: + af = dns.inet.af_for_address(where) + destination = where + except Exception: + if where_must_be_address: + raise + # URLs are ok so eat the exception + if source: + saf = dns.inet.af_for_address(source) + if af: + # We know the destination af, so source had better agree! + if saf != af: + raise ValueError( + "different address families for source and destination" + ) + else: + # We didn't know the destination af, but we know the source, + # so that's our af. + af = saf + if source_port and not source: + # Caller has specified a source_port but not an address, so we + # need to return a source, and we need to use the appropriate + # wildcard address as the address. + try: + source = dns.inet.any_for_af(af) + except Exception: + # we catch this and raise ValueError for backwards compatibility + raise ValueError("source_port specified but address family is unknown") + # Convert high-level (address, port) tuples into low-level address + # tuples. + if destination: + destination = dns.inet.low_level_address_tuple((destination, port), af) + if source: + source = dns.inet.low_level_address_tuple((source, source_port), af) + return (af, destination, source) + + +def _make_socket(af, type, source, ssl_context=None, server_hostname=None): + s = socket_factory(af, type) + try: + s.setblocking(False) + if source is not None: + s.bind(source) + if ssl_context: + # LGTM gets a false positive here, as our default context is OK + return ssl_context.wrap_socket( + s, + do_handshake_on_connect=False, # lgtm[py/insecure-protocol] + server_hostname=server_hostname, + ) + else: + return s + except Exception: + s.close() + raise + + +def _maybe_get_resolver( + resolver: Optional["dns.resolver.Resolver"], +) -> "dns.resolver.Resolver": + # We need a separate method for this to avoid overriding the global + # variable "dns" with the as-yet undefined local variable "dns" + # in https(). + if resolver is None: + # pylint: disable=import-outside-toplevel,redefined-outer-name + import dns.resolver + + resolver = dns.resolver.Resolver() + return resolver + + +class HTTPVersion(enum.IntEnum): + """Which version of HTTP should be used? + + DEFAULT will select the first version from the list [2, 1.1, 3] that + is available. + """ + + DEFAULT = 0 + HTTP_1 = 1 + H1 = 1 + HTTP_2 = 2 + H2 = 2 + HTTP_3 = 3 + H3 = 3 + + +def https( + q: dns.message.Message, + where: str, + timeout: Optional[float] = None, + port: int = 443, + source: Optional[str] = None, + source_port: int = 0, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + session: Optional[Any] = None, + path: str = "/dns-query", + post: bool = True, + bootstrap_address: Optional[str] = None, + verify: Union[bool, str] = True, + resolver: Optional["dns.resolver.Resolver"] = None, + family: int = socket.AF_UNSPEC, + http_version: HTTPVersion = HTTPVersion.DEFAULT, +) -> dns.message.Message: + """Return the response obtained after sending a query via DNS-over-HTTPS. + + *q*, a ``dns.message.Message``, the query to send. + + *where*, a ``str``, the nameserver IP address or the full URL. If an IP address is + given, the URL will be constructed using the following schema: + https://:/. + + *timeout*, a ``float`` or ``None``, the number of seconds to wait before the query + times out. If ``None``, the default, wait forever. + + *port*, a ``int``, the port to send the query to. The default is 443. + + *source*, a ``str`` containing an IPv4 or IPv6 address, specifying the source + address. The default is the wildcard address. + + *source_port*, an ``int``, the port from which to send the message. The default is + 0. + + *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own RRset. + + *ignore_trailing*, a ``bool``. If ``True``, ignore trailing junk at end of the + received message. + + *session*, an ``httpx.Client``. If provided, the client session to use to send the + queries. + + *path*, a ``str``. If *where* is an IP address, then *path* will be used to + construct the URL to send the DNS query to. + + *post*, a ``bool``. If ``True``, the default, POST method will be used. + + *bootstrap_address*, a ``str``, the IP address to use to bypass resolution. + + *verify*, a ``bool`` or ``str``. If a ``True``, then TLS certificate verification + of the server is done using the default CA bundle; if ``False``, then no + verification is done; if a `str` then it specifies the path to a certificate file or + directory which will be used for verification. + + *resolver*, a ``dns.resolver.Resolver`` or ``None``, the resolver to use for + resolution of hostnames in URLs. If not specified, a new resolver with a default + configuration will be used; note this is *not* the default resolver as that resolver + might have been configured to use DoH causing a chicken-and-egg problem. This + parameter only has an effect if the HTTP library is httpx. + + *family*, an ``int``, the address family. If socket.AF_UNSPEC (the default), both A + and AAAA records will be retrieved. + + *http_version*, a ``dns.query.HTTPVersion``, indicating which HTTP version to use. + + Returns a ``dns.message.Message``. + """ + + (af, _, the_source) = _destination_and_source( + where, port, source, source_port, False + ) + if af is not None and dns.inet.is_address(where): + if af == socket.AF_INET: + url = f"https://{where}:{port}{path}" + elif af == socket.AF_INET6: + url = f"https://[{where}]:{port}{path}" + else: + url = where + + extensions = {} + if bootstrap_address is None: + # pylint: disable=possibly-used-before-assignment + parsed = urllib.parse.urlparse(url) + if parsed.hostname is None: + raise ValueError("no hostname in URL") + if dns.inet.is_address(parsed.hostname): + bootstrap_address = parsed.hostname + extensions["sni_hostname"] = parsed.hostname + if parsed.port is not None: + port = parsed.port + + if http_version == HTTPVersion.H3 or ( + http_version == HTTPVersion.DEFAULT and not have_doh + ): + if bootstrap_address is None: + resolver = _maybe_get_resolver(resolver) + assert parsed.hostname is not None # for mypy + answers = resolver.resolve_name(parsed.hostname, family) + bootstrap_address = random.choice(list(answers.addresses())) + return _http3( + q, + bootstrap_address, + url, + timeout, + port, + source, + source_port, + one_rr_per_rrset, + ignore_trailing, + verify=verify, + post=post, + ) + + if not have_doh: + raise NoDOH # pragma: no cover + if session and not isinstance(session, httpx.Client): + raise ValueError("session parameter must be an httpx.Client") + + wire = q.to_wire() + headers = {"accept": "application/dns-message"} + + h1 = http_version in (HTTPVersion.H1, HTTPVersion.DEFAULT) + h2 = http_version in (HTTPVersion.H2, HTTPVersion.DEFAULT) + + # set source port and source address + + if the_source is None: + local_address = None + local_port = 0 + else: + local_address = the_source[0] + local_port = the_source[1] + + if session: + cm: contextlib.AbstractContextManager = contextlib.nullcontext(session) + else: + transport = _HTTPTransport( + local_address=local_address, + http1=h1, + http2=h2, + verify=verify, + local_port=local_port, + bootstrap_address=bootstrap_address, + resolver=resolver, + family=family, + ) + + cm = httpx.Client(http1=h1, http2=h2, verify=verify, transport=transport) + with cm as session: + # see https://tools.ietf.org/html/rfc8484#section-4.1.1 for DoH + # GET and POST examples + if post: + headers.update( + { + "content-type": "application/dns-message", + "content-length": str(len(wire)), + } + ) + response = session.post( + url, + headers=headers, + content=wire, + timeout=timeout, + extensions=extensions, + ) + else: + wire = base64.urlsafe_b64encode(wire).rstrip(b"=") + twire = wire.decode() # httpx does a repr() if we give it bytes + response = session.get( + url, + headers=headers, + timeout=timeout, + params={"dns": twire}, + extensions=extensions, + ) + + # see https://tools.ietf.org/html/rfc8484#section-4.2.1 for info about DoH + # status codes + if response.status_code < 200 or response.status_code > 299: + raise ValueError( + f"{where} responded with status code {response.status_code}" + f"\nResponse body: {response.content}" + ) + r = dns.message.from_wire( + response.content, + keyring=q.keyring, + request_mac=q.request_mac, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + ) + r.time = response.elapsed.total_seconds() + if not q.is_response(r): + raise BadResponse + return r + + +def _find_header(headers: dns.quic.Headers, name: bytes) -> bytes: + if headers is None: + raise KeyError + for header, value in headers: + if header == name: + return value + raise KeyError + + +def _check_status(headers: dns.quic.Headers, peer: str, wire: bytes) -> None: + value = _find_header(headers, b":status") + if value is None: + raise SyntaxError("no :status header in response") + status = int(value) + if status < 0: + raise SyntaxError("status is negative") + if status < 200 or status > 299: + error = "" + if len(wire) > 0: + try: + error = ": " + wire.decode() + except Exception: + pass + raise ValueError(f"{peer} responded with status code {status}{error}") + + +def _http3( + q: dns.message.Message, + where: str, + url: str, + timeout: Optional[float] = None, + port: int = 853, + source: Optional[str] = None, + source_port: int = 0, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + verify: Union[bool, str] = True, + hostname: Optional[str] = None, + post: bool = True, +) -> dns.message.Message: + if not dns.quic.have_quic: + raise NoDOH("DNS-over-HTTP3 is not available.") # pragma: no cover + + url_parts = urllib.parse.urlparse(url) + hostname = url_parts.hostname + if url_parts.port is not None: + port = url_parts.port + + q.id = 0 + wire = q.to_wire() + manager = dns.quic.SyncQuicManager( + verify_mode=verify, server_name=hostname, h3=True + ) + + with manager: + connection = manager.connect(where, port, source, source_port) + (start, expiration) = _compute_times(timeout) + with connection.make_stream(timeout) as stream: + stream.send_h3(url, wire, post) + wire = stream.receive(_remaining(expiration)) + _check_status(stream.headers(), where, wire) + finish = time.time() + r = dns.message.from_wire( + wire, + keyring=q.keyring, + request_mac=q.request_mac, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + ) + r.time = max(finish - start, 0.0) + if not q.is_response(r): + raise BadResponse + return r + + +def _udp_recv(sock, max_size, expiration): + """Reads a datagram from the socket. + A Timeout exception will be raised if the operation is not completed + by the expiration time. + """ + while True: + try: + return sock.recvfrom(max_size) + except BlockingIOError: + _wait_for_readable(sock, expiration) + + +def _udp_send(sock, data, destination, expiration): + """Sends the specified datagram to destination over the socket. + A Timeout exception will be raised if the operation is not completed + by the expiration time. + """ + while True: + try: + if destination: + return sock.sendto(data, destination) + else: + return sock.send(data) + except BlockingIOError: # pragma: no cover + _wait_for_writable(sock, expiration) + + +def send_udp( + sock: Any, + what: Union[dns.message.Message, bytes], + destination: Any, + expiration: Optional[float] = None, +) -> Tuple[int, float]: + """Send a DNS message to the specified UDP socket. + + *sock*, a ``socket``. + + *what*, a ``bytes`` or ``dns.message.Message``, the message to send. + + *destination*, a destination tuple appropriate for the address family + of the socket, specifying where to send the query. + + *expiration*, a ``float`` or ``None``, the absolute time at which + a timeout exception should be raised. If ``None``, no timeout will + occur. + + Returns an ``(int, float)`` tuple of bytes sent and the sent time. + """ + + if isinstance(what, dns.message.Message): + what = what.to_wire() + sent_time = time.time() + n = _udp_send(sock, what, destination, expiration) + return (n, sent_time) + + +def receive_udp( + sock: Any, + destination: Optional[Any] = None, + expiration: Optional[float] = None, + ignore_unexpected: bool = False, + one_rr_per_rrset: bool = False, + keyring: Optional[Dict[dns.name.Name, dns.tsig.Key]] = None, + request_mac: Optional[bytes] = b"", + ignore_trailing: bool = False, + raise_on_truncation: bool = False, + ignore_errors: bool = False, + query: Optional[dns.message.Message] = None, +) -> Any: + """Read a DNS message from a UDP socket. + + *sock*, a ``socket``. + + *destination*, a destination tuple appropriate for the address family + of the socket, specifying where the message is expected to arrive from. + When receiving a response, this would be where the associated query was + sent. + + *expiration*, a ``float`` or ``None``, the absolute time at which + a timeout exception should be raised. If ``None``, no timeout will + occur. + + *ignore_unexpected*, a ``bool``. If ``True``, ignore responses from + unexpected sources. + + *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own + RRset. + + *keyring*, a ``dict``, the keyring to use for TSIG. + + *request_mac*, a ``bytes`` or ``None``, the MAC of the request (for TSIG). + + *ignore_trailing*, a ``bool``. If ``True``, ignore trailing + junk at end of the received message. + + *raise_on_truncation*, a ``bool``. If ``True``, raise an exception if + the TC bit is set. + + Raises if the message is malformed, if network errors occur, of if + there is a timeout. + + If *destination* is not ``None``, returns a ``(dns.message.Message, float)`` + tuple of the received message and the received time. + + If *destination* is ``None``, returns a + ``(dns.message.Message, float, tuple)`` + tuple of the received message, the received time, and the address where + the message arrived from. + + *ignore_errors*, a ``bool``. If various format errors or response + mismatches occur, ignore them and keep listening for a valid response. + The default is ``False``. + + *query*, a ``dns.message.Message`` or ``None``. If not ``None`` and + *ignore_errors* is ``True``, check that the received message is a response + to this query, and if not keep listening for a valid response. + """ + + wire = b"" + while True: + (wire, from_address) = _udp_recv(sock, 65535, expiration) + if not _matches_destination( + sock.family, from_address, destination, ignore_unexpected + ): + continue + received_time = time.time() + try: + r = dns.message.from_wire( + wire, + keyring=keyring, + request_mac=request_mac, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + raise_on_truncation=raise_on_truncation, + ) + except dns.message.Truncated as e: + # If we got Truncated and not FORMERR, we at least got the header with TC + # set, and very likely the question section, so we'll re-raise if the + # message seems to be a response as we need to know when truncation happens. + # We need to check that it seems to be a response as we don't want a random + # injected message with TC set to cause us to bail out. + if ( + ignore_errors + and query is not None + and not query.is_response(e.message()) + ): + continue + else: + raise + except Exception: + if ignore_errors: + continue + else: + raise + if ignore_errors and query is not None and not query.is_response(r): + continue + if destination: + return (r, received_time) + else: + return (r, received_time, from_address) + + +def udp( + q: dns.message.Message, + where: str, + timeout: Optional[float] = None, + port: int = 53, + source: Optional[str] = None, + source_port: int = 0, + ignore_unexpected: bool = False, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + raise_on_truncation: bool = False, + sock: Optional[Any] = None, + ignore_errors: bool = False, +) -> dns.message.Message: + """Return the response obtained after sending a query via UDP. + + *q*, a ``dns.message.Message``, the query to send + + *where*, a ``str`` containing an IPv4 or IPv6 address, where + to send the message. + + *timeout*, a ``float`` or ``None``, the number of seconds to wait before the + query times out. If ``None``, the default, wait forever. + + *port*, an ``int``, the port send the message to. The default is 53. + + *source*, a ``str`` containing an IPv4 or IPv6 address, specifying + the source address. The default is the wildcard address. + + *source_port*, an ``int``, the port from which to send the message. + The default is 0. + + *ignore_unexpected*, a ``bool``. If ``True``, ignore responses from + unexpected sources. + + *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own + RRset. + + *ignore_trailing*, a ``bool``. If ``True``, ignore trailing + junk at end of the received message. + + *raise_on_truncation*, a ``bool``. If ``True``, raise an exception if + the TC bit is set. + + *sock*, a ``socket.socket``, or ``None``, the socket to use for the + query. If ``None``, the default, a socket is created. Note that + if a socket is provided, it must be a nonblocking datagram socket, + and the *source* and *source_port* are ignored. + + *ignore_errors*, a ``bool``. If various format errors or response + mismatches occur, ignore them and keep listening for a valid response. + The default is ``False``. + + Returns a ``dns.message.Message``. + """ + + wire = q.to_wire() + (af, destination, source) = _destination_and_source( + where, port, source, source_port + ) + (begin_time, expiration) = _compute_times(timeout) + if sock: + cm: contextlib.AbstractContextManager = contextlib.nullcontext(sock) + else: + cm = _make_socket(af, socket.SOCK_DGRAM, source) + with cm as s: + send_udp(s, wire, destination, expiration) + (r, received_time) = receive_udp( + s, + destination, + expiration, + ignore_unexpected, + one_rr_per_rrset, + q.keyring, + q.mac, + ignore_trailing, + raise_on_truncation, + ignore_errors, + q, + ) + r.time = received_time - begin_time + # We don't need to check q.is_response() if we are in ignore_errors mode + # as receive_udp() will have checked it. + if not (ignore_errors or q.is_response(r)): + raise BadResponse + return r + assert ( + False # help mypy figure out we can't get here lgtm[py/unreachable-statement] + ) + + +def udp_with_fallback( + q: dns.message.Message, + where: str, + timeout: Optional[float] = None, + port: int = 53, + source: Optional[str] = None, + source_port: int = 0, + ignore_unexpected: bool = False, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + udp_sock: Optional[Any] = None, + tcp_sock: Optional[Any] = None, + ignore_errors: bool = False, +) -> Tuple[dns.message.Message, bool]: + """Return the response to the query, trying UDP first and falling back + to TCP if UDP results in a truncated response. + + *q*, a ``dns.message.Message``, the query to send + + *where*, a ``str`` containing an IPv4 or IPv6 address, where to send the message. + + *timeout*, a ``float`` or ``None``, the number of seconds to wait before the query + times out. If ``None``, the default, wait forever. + + *port*, an ``int``, the port send the message to. The default is 53. + + *source*, a ``str`` containing an IPv4 or IPv6 address, specifying the source + address. The default is the wildcard address. + + *source_port*, an ``int``, the port from which to send the message. The default is + 0. + + *ignore_unexpected*, a ``bool``. If ``True``, ignore responses from unexpected + sources. + + *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own RRset. + + *ignore_trailing*, a ``bool``. If ``True``, ignore trailing junk at end of the + received message. + + *udp_sock*, a ``socket.socket``, or ``None``, the socket to use for the UDP query. + If ``None``, the default, a socket is created. Note that if a socket is provided, + it must be a nonblocking datagram socket, and the *source* and *source_port* are + ignored for the UDP query. + + *tcp_sock*, a ``socket.socket``, or ``None``, the connected socket to use for the + TCP query. If ``None``, the default, a socket is created. Note that if a socket is + provided, it must be a nonblocking connected stream socket, and *where*, *source* + and *source_port* are ignored for the TCP query. + + *ignore_errors*, a ``bool``. If various format errors or response mismatches occur + while listening for UDP, ignore them and keep listening for a valid response. The + default is ``False``. + + Returns a (``dns.message.Message``, tcp) tuple where tcp is ``True`` if and only if + TCP was used. + """ + try: + response = udp( + q, + where, + timeout, + port, + source, + source_port, + ignore_unexpected, + one_rr_per_rrset, + ignore_trailing, + True, + udp_sock, + ignore_errors, + ) + return (response, False) + except dns.message.Truncated: + response = tcp( + q, + where, + timeout, + port, + source, + source_port, + one_rr_per_rrset, + ignore_trailing, + tcp_sock, + ) + return (response, True) + + +def _net_read(sock, count, expiration): + """Read the specified number of bytes from sock. Keep trying until we + either get the desired amount, or we hit EOF. + A Timeout exception will be raised if the operation is not completed + by the expiration time. + """ + s = b"" + while count > 0: + try: + n = sock.recv(count) + if n == b"": + raise EOFError("EOF") + count -= len(n) + s += n + except (BlockingIOError, ssl.SSLWantReadError): + _wait_for_readable(sock, expiration) + except ssl.SSLWantWriteError: # pragma: no cover + _wait_for_writable(sock, expiration) + return s + + +def _net_write(sock, data, expiration): + """Write the specified data to the socket. + A Timeout exception will be raised if the operation is not completed + by the expiration time. + """ + current = 0 + l = len(data) + while current < l: + try: + current += sock.send(data[current:]) + except (BlockingIOError, ssl.SSLWantWriteError): + _wait_for_writable(sock, expiration) + except ssl.SSLWantReadError: # pragma: no cover + _wait_for_readable(sock, expiration) + + +def send_tcp( + sock: Any, + what: Union[dns.message.Message, bytes], + expiration: Optional[float] = None, +) -> Tuple[int, float]: + """Send a DNS message to the specified TCP socket. + + *sock*, a ``socket``. + + *what*, a ``bytes`` or ``dns.message.Message``, the message to send. + + *expiration*, a ``float`` or ``None``, the absolute time at which + a timeout exception should be raised. If ``None``, no timeout will + occur. + + Returns an ``(int, float)`` tuple of bytes sent and the sent time. + """ + + if isinstance(what, dns.message.Message): + tcpmsg = what.to_wire(prepend_length=True) + else: + # copying the wire into tcpmsg is inefficient, but lets us + # avoid writev() or doing a short write that would get pushed + # onto the net + tcpmsg = len(what).to_bytes(2, "big") + what + sent_time = time.time() + _net_write(sock, tcpmsg, expiration) + return (len(tcpmsg), sent_time) + + +def receive_tcp( + sock: Any, + expiration: Optional[float] = None, + one_rr_per_rrset: bool = False, + keyring: Optional[Dict[dns.name.Name, dns.tsig.Key]] = None, + request_mac: Optional[bytes] = b"", + ignore_trailing: bool = False, +) -> Tuple[dns.message.Message, float]: + """Read a DNS message from a TCP socket. + + *sock*, a ``socket``. + + *expiration*, a ``float`` or ``None``, the absolute time at which + a timeout exception should be raised. If ``None``, no timeout will + occur. + + *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own + RRset. + + *keyring*, a ``dict``, the keyring to use for TSIG. + + *request_mac*, a ``bytes`` or ``None``, the MAC of the request (for TSIG). + + *ignore_trailing*, a ``bool``. If ``True``, ignore trailing + junk at end of the received message. + + Raises if the message is malformed, if network errors occur, of if + there is a timeout. + + Returns a ``(dns.message.Message, float)`` tuple of the received message + and the received time. + """ + + ldata = _net_read(sock, 2, expiration) + (l,) = struct.unpack("!H", ldata) + wire = _net_read(sock, l, expiration) + received_time = time.time() + r = dns.message.from_wire( + wire, + keyring=keyring, + request_mac=request_mac, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + ) + return (r, received_time) + + +def _connect(s, address, expiration): + err = s.connect_ex(address) + if err == 0: + return + if err in (errno.EINPROGRESS, errno.EWOULDBLOCK, errno.EALREADY): + _wait_for_writable(s, expiration) + err = s.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) + if err != 0: + raise OSError(err, os.strerror(err)) + + +def tcp( + q: dns.message.Message, + where: str, + timeout: Optional[float] = None, + port: int = 53, + source: Optional[str] = None, + source_port: int = 0, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + sock: Optional[Any] = None, +) -> dns.message.Message: + """Return the response obtained after sending a query via TCP. + + *q*, a ``dns.message.Message``, the query to send + + *where*, a ``str`` containing an IPv4 or IPv6 address, where + to send the message. + + *timeout*, a ``float`` or ``None``, the number of seconds to wait before the + query times out. If ``None``, the default, wait forever. + + *port*, an ``int``, the port send the message to. The default is 53. + + *source*, a ``str`` containing an IPv4 or IPv6 address, specifying + the source address. The default is the wildcard address. + + *source_port*, an ``int``, the port from which to send the message. + The default is 0. + + *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own + RRset. + + *ignore_trailing*, a ``bool``. If ``True``, ignore trailing + junk at end of the received message. + + *sock*, a ``socket.socket``, or ``None``, the connected socket to use for the + query. If ``None``, the default, a socket is created. Note that + if a socket is provided, it must be a nonblocking connected stream + socket, and *where*, *port*, *source* and *source_port* are ignored. + + Returns a ``dns.message.Message``. + """ + + wire = q.to_wire() + (begin_time, expiration) = _compute_times(timeout) + if sock: + cm: contextlib.AbstractContextManager = contextlib.nullcontext(sock) + else: + (af, destination, source) = _destination_and_source( + where, port, source, source_port + ) + cm = _make_socket(af, socket.SOCK_STREAM, source) + with cm as s: + if not sock: + # pylint: disable=possibly-used-before-assignment + _connect(s, destination, expiration) + send_tcp(s, wire, expiration) + (r, received_time) = receive_tcp( + s, expiration, one_rr_per_rrset, q.keyring, q.mac, ignore_trailing + ) + r.time = received_time - begin_time + if not q.is_response(r): + raise BadResponse + return r + assert ( + False # help mypy figure out we can't get here lgtm[py/unreachable-statement] + ) + + +def _tls_handshake(s, expiration): + while True: + try: + s.do_handshake() + return + except ssl.SSLWantReadError: + _wait_for_readable(s, expiration) + except ssl.SSLWantWriteError: # pragma: no cover + _wait_for_writable(s, expiration) + + +def _make_dot_ssl_context( + server_hostname: Optional[str], verify: Union[bool, str] +) -> ssl.SSLContext: + cafile: Optional[str] = None + capath: Optional[str] = None + if isinstance(verify, str): + if os.path.isfile(verify): + cafile = verify + elif os.path.isdir(verify): + capath = verify + else: + raise ValueError("invalid verify string") + ssl_context = ssl.create_default_context(cafile=cafile, capath=capath) + ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2 + if server_hostname is None: + ssl_context.check_hostname = False + ssl_context.set_alpn_protocols(["dot"]) + if verify is False: + ssl_context.verify_mode = ssl.CERT_NONE + return ssl_context + + +def tls( + q: dns.message.Message, + where: str, + timeout: Optional[float] = None, + port: int = 853, + source: Optional[str] = None, + source_port: int = 0, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + sock: Optional[ssl.SSLSocket] = None, + ssl_context: Optional[ssl.SSLContext] = None, + server_hostname: Optional[str] = None, + verify: Union[bool, str] = True, +) -> dns.message.Message: + """Return the response obtained after sending a query via TLS. + + *q*, a ``dns.message.Message``, the query to send + + *where*, a ``str`` containing an IPv4 or IPv6 address, where + to send the message. + + *timeout*, a ``float`` or ``None``, the number of seconds to wait before the + query times out. If ``None``, the default, wait forever. + + *port*, an ``int``, the port send the message to. The default is 853. + + *source*, a ``str`` containing an IPv4 or IPv6 address, specifying + the source address. The default is the wildcard address. + + *source_port*, an ``int``, the port from which to send the message. + The default is 0. + + *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own + RRset. + + *ignore_trailing*, a ``bool``. If ``True``, ignore trailing + junk at end of the received message. + + *sock*, an ``ssl.SSLSocket``, or ``None``, the socket to use for + the query. If ``None``, the default, a socket is created. Note + that if a socket is provided, it must be a nonblocking connected + SSL stream socket, and *where*, *port*, *source*, *source_port*, + and *ssl_context* are ignored. + + *ssl_context*, an ``ssl.SSLContext``, the context to use when establishing + a TLS connection. If ``None``, the default, creates one with the default + configuration. + + *server_hostname*, a ``str`` containing the server's hostname. The + default is ``None``, which means that no hostname is known, and if an + SSL context is created, hostname checking will be disabled. + + *verify*, a ``bool`` or ``str``. If a ``True``, then TLS certificate verification + of the server is done using the default CA bundle; if ``False``, then no + verification is done; if a `str` then it specifies the path to a certificate file or + directory which will be used for verification. + + Returns a ``dns.message.Message``. + + """ + + if sock: + # + # If a socket was provided, there's no special TLS handling needed. + # + return tcp( + q, + where, + timeout, + port, + source, + source_port, + one_rr_per_rrset, + ignore_trailing, + sock, + ) + + wire = q.to_wire() + (begin_time, expiration) = _compute_times(timeout) + (af, destination, source) = _destination_and_source( + where, port, source, source_port + ) + if ssl_context is None and not sock: + ssl_context = _make_dot_ssl_context(server_hostname, verify) + + with _make_socket( + af, + socket.SOCK_STREAM, + source, + ssl_context=ssl_context, + server_hostname=server_hostname, + ) as s: + _connect(s, destination, expiration) + _tls_handshake(s, expiration) + send_tcp(s, wire, expiration) + (r, received_time) = receive_tcp( + s, expiration, one_rr_per_rrset, q.keyring, q.mac, ignore_trailing + ) + r.time = received_time - begin_time + if not q.is_response(r): + raise BadResponse + return r + assert ( + False # help mypy figure out we can't get here lgtm[py/unreachable-statement] + ) + + +def quic( + q: dns.message.Message, + where: str, + timeout: Optional[float] = None, + port: int = 853, + source: Optional[str] = None, + source_port: int = 0, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + connection: Optional[dns.quic.SyncQuicConnection] = None, + verify: Union[bool, str] = True, + hostname: Optional[str] = None, + server_hostname: Optional[str] = None, +) -> dns.message.Message: + """Return the response obtained after sending a query via DNS-over-QUIC. + + *q*, a ``dns.message.Message``, the query to send. + + *where*, a ``str``, the nameserver IP address. + + *timeout*, a ``float`` or ``None``, the number of seconds to wait before the query + times out. If ``None``, the default, wait forever. + + *port*, a ``int``, the port to send the query to. The default is 853. + + *source*, a ``str`` containing an IPv4 or IPv6 address, specifying the source + address. The default is the wildcard address. + + *source_port*, an ``int``, the port from which to send the message. The default is + 0. + + *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own RRset. + + *ignore_trailing*, a ``bool``. If ``True``, ignore trailing junk at end of the + received message. + + *connection*, a ``dns.quic.SyncQuicConnection``. If provided, the connection to use + to send the query. + + *verify*, a ``bool`` or ``str``. If a ``True``, then TLS certificate verification + of the server is done using the default CA bundle; if ``False``, then no + verification is done; if a `str` then it specifies the path to a certificate file or + directory which will be used for verification. + + *hostname*, a ``str`` containing the server's hostname or ``None``. The default is + ``None``, which means that no hostname is known, and if an SSL context is created, + hostname checking will be disabled. This value is ignored if *url* is not + ``None``. + + *server_hostname*, a ``str`` or ``None``. This item is for backwards compatibility + only, and has the same meaning as *hostname*. + + Returns a ``dns.message.Message``. + """ + + if not dns.quic.have_quic: + raise NoDOQ("DNS-over-QUIC is not available.") # pragma: no cover + + if server_hostname is not None and hostname is None: + hostname = server_hostname + + q.id = 0 + wire = q.to_wire() + the_connection: dns.quic.SyncQuicConnection + the_manager: dns.quic.SyncQuicManager + if connection: + manager: contextlib.AbstractContextManager = contextlib.nullcontext(None) + the_connection = connection + else: + manager = dns.quic.SyncQuicManager(verify_mode=verify, server_name=hostname) + the_manager = manager # for type checking happiness + + with manager: + if not connection: + the_connection = the_manager.connect(where, port, source, source_port) + (start, expiration) = _compute_times(timeout) + with the_connection.make_stream(timeout) as stream: + stream.send(wire, True) + wire = stream.receive(_remaining(expiration)) + finish = time.time() + r = dns.message.from_wire( + wire, + keyring=q.keyring, + request_mac=q.request_mac, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + ) + r.time = max(finish - start, 0.0) + if not q.is_response(r): + raise BadResponse + return r + + +class UDPMode(enum.IntEnum): + """How should UDP be used in an IXFR from :py:func:`inbound_xfr()`? + + NEVER means "never use UDP; always use TCP" + TRY_FIRST means "try to use UDP but fall back to TCP if needed" + ONLY means "raise ``dns.xfr.UseTCP`` if trying UDP does not succeed" + """ + + NEVER = 0 + TRY_FIRST = 1 + ONLY = 2 + + +def _inbound_xfr( + txn_manager: dns.transaction.TransactionManager, + s: socket.socket, + query: dns.message.Message, + serial: Optional[int], + timeout: Optional[float], + expiration: float, +) -> Any: + """Given a socket, does the zone transfer.""" + rdtype = query.question[0].rdtype + is_ixfr = rdtype == dns.rdatatype.IXFR + origin = txn_manager.from_wire_origin() + wire = query.to_wire() + is_udp = s.type == socket.SOCK_DGRAM + if is_udp: + _udp_send(s, wire, None, expiration) + else: + tcpmsg = struct.pack("!H", len(wire)) + wire + _net_write(s, tcpmsg, expiration) + with dns.xfr.Inbound(txn_manager, rdtype, serial, is_udp) as inbound: + done = False + tsig_ctx = None + while not done: + (_, mexpiration) = _compute_times(timeout) + if mexpiration is None or ( + expiration is not None and mexpiration > expiration + ): + mexpiration = expiration + if is_udp: + (rwire, _) = _udp_recv(s, 65535, mexpiration) + else: + ldata = _net_read(s, 2, mexpiration) + (l,) = struct.unpack("!H", ldata) + rwire = _net_read(s, l, mexpiration) + r = dns.message.from_wire( + rwire, + keyring=query.keyring, + request_mac=query.mac, + xfr=True, + origin=origin, + tsig_ctx=tsig_ctx, + multi=(not is_udp), + one_rr_per_rrset=is_ixfr, + ) + done = inbound.process_message(r) + yield r + tsig_ctx = r.tsig_ctx + if query.keyring and not r.had_tsig: + raise dns.exception.FormError("missing TSIG") + + +def xfr( + where: str, + zone: Union[dns.name.Name, str], + rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.AXFR, + rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, + timeout: Optional[float] = None, + port: int = 53, + keyring: Optional[Dict[dns.name.Name, dns.tsig.Key]] = None, + keyname: Optional[Union[dns.name.Name, str]] = None, + relativize: bool = True, + lifetime: Optional[float] = None, + source: Optional[str] = None, + source_port: int = 0, + serial: int = 0, + use_udp: bool = False, + keyalgorithm: Union[dns.name.Name, str] = dns.tsig.default_algorithm, +) -> Any: + """Return a generator for the responses to a zone transfer. + + *where*, a ``str`` containing an IPv4 or IPv6 address, where + to send the message. + + *zone*, a ``dns.name.Name`` or ``str``, the name of the zone to transfer. + + *rdtype*, an ``int`` or ``str``, the type of zone transfer. The + default is ``dns.rdatatype.AXFR``. ``dns.rdatatype.IXFR`` can be + used to do an incremental transfer instead. + + *rdclass*, an ``int`` or ``str``, the class of the zone transfer. + The default is ``dns.rdataclass.IN``. + + *timeout*, a ``float``, the number of seconds to wait for each + response message. If None, the default, wait forever. + + *port*, an ``int``, the port send the message to. The default is 53. + + *keyring*, a ``dict``, the keyring to use for TSIG. + + *keyname*, a ``dns.name.Name`` or ``str``, the name of the TSIG + key to use. + + *relativize*, a ``bool``. If ``True``, all names in the zone will be + relativized to the zone origin. It is essential that the + relativize setting matches the one specified to + ``dns.zone.from_xfr()`` if using this generator to make a zone. + + *lifetime*, a ``float``, the total number of seconds to spend + doing the transfer. If ``None``, the default, then there is no + limit on the time the transfer may take. + + *source*, a ``str`` containing an IPv4 or IPv6 address, specifying + the source address. The default is the wildcard address. + + *source_port*, an ``int``, the port from which to send the message. + The default is 0. + + *serial*, an ``int``, the SOA serial number to use as the base for + an IXFR diff sequence (only meaningful if *rdtype* is + ``dns.rdatatype.IXFR``). + + *use_udp*, a ``bool``. If ``True``, use UDP (only meaningful for IXFR). + + *keyalgorithm*, a ``dns.name.Name`` or ``str``, the TSIG algorithm to use. + + Raises on errors, and so does the generator. + + Returns a generator of ``dns.message.Message`` objects. + """ + + class DummyTransactionManager(dns.transaction.TransactionManager): + def __init__(self, origin, relativize): + self.info = (origin, relativize, dns.name.empty if relativize else origin) + + def origin_information(self): + return self.info + + def get_class(self) -> dns.rdataclass.RdataClass: + raise NotImplementedError # pragma: no cover + + def reader(self): + raise NotImplementedError # pragma: no cover + + def writer(self, replacement: bool = False) -> dns.transaction.Transaction: + class DummyTransaction: + def nop(self, *args, **kw): + pass + + def __getattr__(self, _): + return self.nop + + return cast(dns.transaction.Transaction, DummyTransaction()) + + if isinstance(zone, str): + zone = dns.name.from_text(zone) + rdtype = dns.rdatatype.RdataType.make(rdtype) + q = dns.message.make_query(zone, rdtype, rdclass) + if rdtype == dns.rdatatype.IXFR: + rrset = q.find_rrset( + q.authority, zone, dns.rdataclass.IN, dns.rdatatype.SOA, create=True + ) + soa = dns.rdata.from_text("IN", "SOA", ". . %u 0 0 0 0" % serial) + rrset.add(soa, 0) + if keyring is not None: + q.use_tsig(keyring, keyname, algorithm=keyalgorithm) + (af, destination, source) = _destination_and_source( + where, port, source, source_port + ) + (_, expiration) = _compute_times(lifetime) + tm = DummyTransactionManager(zone, relativize) + if use_udp and rdtype != dns.rdatatype.IXFR: + raise ValueError("cannot do a UDP AXFR") + sock_type = socket.SOCK_DGRAM if use_udp else socket.SOCK_STREAM + with _make_socket(af, sock_type, source) as s: + _connect(s, destination, expiration) + yield from _inbound_xfr(tm, s, q, serial, timeout, expiration) + + +def inbound_xfr( + where: str, + txn_manager: dns.transaction.TransactionManager, + query: Optional[dns.message.Message] = None, + port: int = 53, + timeout: Optional[float] = None, + lifetime: Optional[float] = None, + source: Optional[str] = None, + source_port: int = 0, + udp_mode: UDPMode = UDPMode.NEVER, +) -> None: + """Conduct an inbound transfer and apply it via a transaction from the + txn_manager. + + *where*, a ``str`` containing an IPv4 or IPv6 address, where + to send the message. + + *txn_manager*, a ``dns.transaction.TransactionManager``, the txn_manager + for this transfer (typically a ``dns.zone.Zone``). + + *query*, the query to send. If not supplied, a default query is + constructed using information from the *txn_manager*. + + *port*, an ``int``, the port send the message to. The default is 53. + + *timeout*, a ``float``, the number of seconds to wait for each + response message. If None, the default, wait forever. + + *lifetime*, a ``float``, the total number of seconds to spend + doing the transfer. If ``None``, the default, then there is no + limit on the time the transfer may take. + + *source*, a ``str`` containing an IPv4 or IPv6 address, specifying + the source address. The default is the wildcard address. + + *source_port*, an ``int``, the port from which to send the message. + The default is 0. + + *udp_mode*, a ``dns.query.UDPMode``, determines how UDP is used + for IXFRs. The default is ``dns.UDPMode.NEVER``, i.e. only use + TCP. Other possibilities are ``dns.UDPMode.TRY_FIRST``, which + means "try UDP but fallback to TCP if needed", and + ``dns.UDPMode.ONLY``, which means "try UDP and raise + ``dns.xfr.UseTCP`` if it does not succeed. + + Raises on errors. + """ + if query is None: + (query, serial) = dns.xfr.make_query(txn_manager) + else: + serial = dns.xfr.extract_serial_from_query(query) + + (af, destination, source) = _destination_and_source( + where, port, source, source_port + ) + (_, expiration) = _compute_times(lifetime) + if query.question[0].rdtype == dns.rdatatype.IXFR and udp_mode != UDPMode.NEVER: + with _make_socket(af, socket.SOCK_DGRAM, source) as s: + _connect(s, destination, expiration) + try: + for _ in _inbound_xfr( + txn_manager, s, query, serial, timeout, expiration + ): + pass + return + except dns.xfr.UseTCP: + if udp_mode == UDPMode.ONLY: + raise + + with _make_socket(af, socket.SOCK_STREAM, source) as s: + _connect(s, destination, expiration) + for _ in _inbound_xfr(txn_manager, s, query, serial, timeout, expiration): + pass diff --git a/.venv/lib/python3.9/site-packages/dns/quic/__init__.py b/.venv/lib/python3.9/site-packages/dns/quic/__init__.py new file mode 100644 index 0000000..0750e72 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/quic/__init__.py @@ -0,0 +1,80 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +from typing import List, Tuple + +import dns._features +import dns.asyncbackend + +if dns._features.have("doq"): + import aioquic.quic.configuration # type: ignore + + from dns._asyncbackend import NullContext + from dns.quic._asyncio import ( + AsyncioQuicConnection, + AsyncioQuicManager, + AsyncioQuicStream, + ) + from dns.quic._common import AsyncQuicConnection, AsyncQuicManager + from dns.quic._sync import SyncQuicConnection, SyncQuicManager, SyncQuicStream + + have_quic = True + + def null_factory( + *args, # pylint: disable=unused-argument + **kwargs, # pylint: disable=unused-argument + ): + return NullContext(None) + + def _asyncio_manager_factory( + context, *args, **kwargs # pylint: disable=unused-argument + ): + return AsyncioQuicManager(*args, **kwargs) + + # We have a context factory and a manager factory as for trio we need to have + # a nursery. + + _async_factories = {"asyncio": (null_factory, _asyncio_manager_factory)} + + if dns._features.have("trio"): + import trio + + from dns.quic._trio import ( # pylint: disable=ungrouped-imports + TrioQuicConnection, + TrioQuicManager, + TrioQuicStream, + ) + + def _trio_context_factory(): + return trio.open_nursery() + + def _trio_manager_factory(context, *args, **kwargs): + return TrioQuicManager(context, *args, **kwargs) + + _async_factories["trio"] = (_trio_context_factory, _trio_manager_factory) + + def factories_for_backend(backend=None): + if backend is None: + backend = dns.asyncbackend.get_default_backend() + return _async_factories[backend.name()] + +else: # pragma: no cover + have_quic = False + + from typing import Any + + class AsyncQuicStream: # type: ignore + pass + + class AsyncQuicConnection: # type: ignore + async def make_stream(self) -> Any: + raise NotImplementedError + + class SyncQuicStream: # type: ignore + pass + + class SyncQuicConnection: # type: ignore + def make_stream(self) -> Any: + raise NotImplementedError + + +Headers = List[Tuple[bytes, bytes]] diff --git a/.venv/lib/python3.9/site-packages/dns/quic/_asyncio.py b/.venv/lib/python3.9/site-packages/dns/quic/_asyncio.py new file mode 100644 index 0000000..f87515d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/quic/_asyncio.py @@ -0,0 +1,267 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import asyncio +import socket +import ssl +import struct +import time + +import aioquic.quic.configuration # type: ignore +import aioquic.quic.connection # type: ignore +import aioquic.quic.events # type: ignore + +import dns.asyncbackend +import dns.exception +import dns.inet +from dns.quic._common import ( + QUIC_MAX_DATAGRAM, + AsyncQuicConnection, + AsyncQuicManager, + BaseQuicStream, + UnexpectedEOF, +) + + +class AsyncioQuicStream(BaseQuicStream): + def __init__(self, connection, stream_id): + super().__init__(connection, stream_id) + self._wake_up = asyncio.Condition() + + async def _wait_for_wake_up(self): + async with self._wake_up: + await self._wake_up.wait() + + async def wait_for(self, amount, expiration): + while True: + timeout = self._timeout_from_expiration(expiration) + if self._buffer.have(amount): + return + self._expecting = amount + try: + await asyncio.wait_for(self._wait_for_wake_up(), timeout) + except TimeoutError: + raise dns.exception.Timeout + self._expecting = 0 + + async def wait_for_end(self, expiration): + while True: + timeout = self._timeout_from_expiration(expiration) + if self._buffer.seen_end(): + return + try: + await asyncio.wait_for(self._wait_for_wake_up(), timeout) + except TimeoutError: + raise dns.exception.Timeout + + async def receive(self, timeout=None): + expiration = self._expiration_from_timeout(timeout) + if self._connection.is_h3(): + await self.wait_for_end(expiration) + return self._buffer.get_all() + else: + await self.wait_for(2, expiration) + (size,) = struct.unpack("!H", self._buffer.get(2)) + await self.wait_for(size, expiration) + return self._buffer.get(size) + + async def send(self, datagram, is_end=False): + data = self._encapsulate(datagram) + await self._connection.write(self._stream_id, data, is_end) + + async def _add_input(self, data, is_end): + if self._common_add_input(data, is_end): + async with self._wake_up: + self._wake_up.notify() + + async def close(self): + self._close() + + # Streams are async context managers + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + await self.close() + async with self._wake_up: + self._wake_up.notify() + return False + + +class AsyncioQuicConnection(AsyncQuicConnection): + def __init__(self, connection, address, port, source, source_port, manager=None): + super().__init__(connection, address, port, source, source_port, manager) + self._socket = None + self._handshake_complete = asyncio.Event() + self._socket_created = asyncio.Event() + self._wake_timer = asyncio.Condition() + self._receiver_task = None + self._sender_task = None + self._wake_pending = False + + async def _receiver(self): + try: + af = dns.inet.af_for_address(self._address) + backend = dns.asyncbackend.get_backend("asyncio") + # Note that peer is a low-level address tuple, but make_socket() wants + # a high-level address tuple, so we convert. + self._socket = await backend.make_socket( + af, socket.SOCK_DGRAM, 0, self._source, (self._peer[0], self._peer[1]) + ) + self._socket_created.set() + async with self._socket: + while not self._done: + (datagram, address) = await self._socket.recvfrom( + QUIC_MAX_DATAGRAM, None + ) + if address[0] != self._peer[0] or address[1] != self._peer[1]: + continue + self._connection.receive_datagram(datagram, address, time.time()) + # Wake up the timer in case the sender is sleeping, as there may be + # stuff to send now. + await self._wakeup() + except Exception: + pass + finally: + self._done = True + await self._wakeup() + self._handshake_complete.set() + + async def _wakeup(self): + self._wake_pending = True + async with self._wake_timer: + self._wake_timer.notify_all() + + async def _wait_for_wake_timer(self): + async with self._wake_timer: + if not self._wake_pending: + await self._wake_timer.wait() + self._wake_pending = False + + async def _sender(self): + await self._socket_created.wait() + while not self._done: + datagrams = self._connection.datagrams_to_send(time.time()) + for datagram, address in datagrams: + assert address == self._peer + await self._socket.sendto(datagram, self._peer, None) + (expiration, interval) = self._get_timer_values() + try: + await asyncio.wait_for(self._wait_for_wake_timer(), interval) + except Exception: + pass + self._handle_timer(expiration) + await self._handle_events() + + async def _handle_events(self): + count = 0 + while True: + event = self._connection.next_event() + if event is None: + return + if isinstance(event, aioquic.quic.events.StreamDataReceived): + if self.is_h3(): + h3_events = self._h3_conn.handle_event(event) + for h3_event in h3_events: + if isinstance(h3_event, aioquic.h3.events.HeadersReceived): + stream = self._streams.get(event.stream_id) + if stream: + if stream._headers is None: + stream._headers = h3_event.headers + elif stream._trailers is None: + stream._trailers = h3_event.headers + if h3_event.stream_ended: + await stream._add_input(b"", True) + elif isinstance(h3_event, aioquic.h3.events.DataReceived): + stream = self._streams.get(event.stream_id) + if stream: + await stream._add_input( + h3_event.data, h3_event.stream_ended + ) + else: + stream = self._streams.get(event.stream_id) + if stream: + await stream._add_input(event.data, event.end_stream) + elif isinstance(event, aioquic.quic.events.HandshakeCompleted): + self._handshake_complete.set() + elif isinstance(event, aioquic.quic.events.ConnectionTerminated): + self._done = True + self._receiver_task.cancel() + elif isinstance(event, aioquic.quic.events.StreamReset): + stream = self._streams.get(event.stream_id) + if stream: + await stream._add_input(b"", True) + + count += 1 + if count > 10: + # yield + count = 0 + await asyncio.sleep(0) + + async def write(self, stream, data, is_end=False): + self._connection.send_stream_data(stream, data, is_end) + await self._wakeup() + + def run(self): + if self._closed: + return + self._receiver_task = asyncio.Task(self._receiver()) + self._sender_task = asyncio.Task(self._sender()) + + async def make_stream(self, timeout=None): + try: + await asyncio.wait_for(self._handshake_complete.wait(), timeout) + except TimeoutError: + raise dns.exception.Timeout + if self._done: + raise UnexpectedEOF + stream_id = self._connection.get_next_available_stream_id(False) + stream = AsyncioQuicStream(self, stream_id) + self._streams[stream_id] = stream + return stream + + async def close(self): + if not self._closed: + self._manager.closed(self._peer[0], self._peer[1]) + self._closed = True + self._connection.close() + # sender might be blocked on this, so set it + self._socket_created.set() + await self._wakeup() + try: + await self._receiver_task + except asyncio.CancelledError: + pass + try: + await self._sender_task + except asyncio.CancelledError: + pass + await self._socket.close() + + +class AsyncioQuicManager(AsyncQuicManager): + def __init__( + self, conf=None, verify_mode=ssl.CERT_REQUIRED, server_name=None, h3=False + ): + super().__init__(conf, verify_mode, AsyncioQuicConnection, server_name, h3) + + def connect( + self, address, port=853, source=None, source_port=0, want_session_ticket=True + ): + (connection, start) = self._connect( + address, port, source, source_port, want_session_ticket + ) + if start: + connection.run() + return connection + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + # Copy the iterator into a list as exiting things will mutate the connections + # table. + connections = list(self._connections.values()) + for connection in connections: + await connection.close() + return False diff --git a/.venv/lib/python3.9/site-packages/dns/quic/_common.py b/.venv/lib/python3.9/site-packages/dns/quic/_common.py new file mode 100644 index 0000000..ce575b0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/quic/_common.py @@ -0,0 +1,339 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import base64 +import copy +import functools +import socket +import struct +import time +import urllib +from typing import Any, Optional + +import aioquic.h3.connection # type: ignore +import aioquic.h3.events # type: ignore +import aioquic.quic.configuration # type: ignore +import aioquic.quic.connection # type: ignore + +import dns.inet + +QUIC_MAX_DATAGRAM = 2048 +MAX_SESSION_TICKETS = 8 +# If we hit the max sessions limit we will delete this many of the oldest connections. +# The value must be a integer > 0 and <= MAX_SESSION_TICKETS. +SESSIONS_TO_DELETE = MAX_SESSION_TICKETS // 4 + + +class UnexpectedEOF(Exception): + pass + + +class Buffer: + def __init__(self): + self._buffer = b"" + self._seen_end = False + + def put(self, data, is_end): + if self._seen_end: + return + self._buffer += data + if is_end: + self._seen_end = True + + def have(self, amount): + if len(self._buffer) >= amount: + return True + if self._seen_end: + raise UnexpectedEOF + return False + + def seen_end(self): + return self._seen_end + + def get(self, amount): + assert self.have(amount) + data = self._buffer[:amount] + self._buffer = self._buffer[amount:] + return data + + def get_all(self): + assert self.seen_end() + data = self._buffer + self._buffer = b"" + return data + + +class BaseQuicStream: + def __init__(self, connection, stream_id): + self._connection = connection + self._stream_id = stream_id + self._buffer = Buffer() + self._expecting = 0 + self._headers = None + self._trailers = None + + def id(self): + return self._stream_id + + def headers(self): + return self._headers + + def trailers(self): + return self._trailers + + def _expiration_from_timeout(self, timeout): + if timeout is not None: + expiration = time.time() + timeout + else: + expiration = None + return expiration + + def _timeout_from_expiration(self, expiration): + if expiration is not None: + timeout = max(expiration - time.time(), 0.0) + else: + timeout = None + return timeout + + # Subclass must implement receive() as sync / async and which returns a message + # or raises. + + # Subclass must implement send() as sync / async and which takes a message and + # an EOF indicator. + + def send_h3(self, url, datagram, post=True): + if not self._connection.is_h3(): + raise SyntaxError("cannot send H3 to a non-H3 connection") + url_parts = urllib.parse.urlparse(url) + path = url_parts.path.encode() + if post: + method = b"POST" + else: + method = b"GET" + path += b"?dns=" + base64.urlsafe_b64encode(datagram).rstrip(b"=") + headers = [ + (b":method", method), + (b":scheme", url_parts.scheme.encode()), + (b":authority", url_parts.netloc.encode()), + (b":path", path), + (b"accept", b"application/dns-message"), + ] + if post: + headers.extend( + [ + (b"content-type", b"application/dns-message"), + (b"content-length", str(len(datagram)).encode()), + ] + ) + self._connection.send_headers(self._stream_id, headers, not post) + if post: + self._connection.send_data(self._stream_id, datagram, True) + + def _encapsulate(self, datagram): + if self._connection.is_h3(): + return datagram + l = len(datagram) + return struct.pack("!H", l) + datagram + + def _common_add_input(self, data, is_end): + self._buffer.put(data, is_end) + try: + return ( + self._expecting > 0 and self._buffer.have(self._expecting) + ) or self._buffer.seen_end + except UnexpectedEOF: + return True + + def _close(self): + self._connection.close_stream(self._stream_id) + self._buffer.put(b"", True) # send EOF in case we haven't seen it. + + +class BaseQuicConnection: + def __init__( + self, + connection, + address, + port, + source=None, + source_port=0, + manager=None, + ): + self._done = False + self._connection = connection + self._address = address + self._port = port + self._closed = False + self._manager = manager + self._streams = {} + if manager.is_h3(): + self._h3_conn = aioquic.h3.connection.H3Connection(connection, False) + else: + self._h3_conn = None + self._af = dns.inet.af_for_address(address) + self._peer = dns.inet.low_level_address_tuple((address, port)) + if source is None and source_port != 0: + if self._af == socket.AF_INET: + source = "0.0.0.0" + elif self._af == socket.AF_INET6: + source = "::" + else: + raise NotImplementedError + if source: + self._source = (source, source_port) + else: + self._source = None + + def is_h3(self): + return self._h3_conn is not None + + def close_stream(self, stream_id): + del self._streams[stream_id] + + def send_headers(self, stream_id, headers, is_end=False): + self._h3_conn.send_headers(stream_id, headers, is_end) + + def send_data(self, stream_id, data, is_end=False): + self._h3_conn.send_data(stream_id, data, is_end) + + def _get_timer_values(self, closed_is_special=True): + now = time.time() + expiration = self._connection.get_timer() + if expiration is None: + expiration = now + 3600 # arbitrary "big" value + interval = max(expiration - now, 0) + if self._closed and closed_is_special: + # lower sleep interval to avoid a race in the closing process + # which can lead to higher latency closing due to sleeping when + # we have events. + interval = min(interval, 0.05) + return (expiration, interval) + + def _handle_timer(self, expiration): + now = time.time() + if expiration <= now: + self._connection.handle_timer(now) + + +class AsyncQuicConnection(BaseQuicConnection): + async def make_stream(self, timeout: Optional[float] = None) -> Any: + pass + + +class BaseQuicManager: + def __init__( + self, conf, verify_mode, connection_factory, server_name=None, h3=False + ): + self._connections = {} + self._connection_factory = connection_factory + self._session_tickets = {} + self._tokens = {} + self._h3 = h3 + if conf is None: + verify_path = None + if isinstance(verify_mode, str): + verify_path = verify_mode + verify_mode = True + if h3: + alpn_protocols = ["h3"] + else: + alpn_protocols = ["doq", "doq-i03"] + conf = aioquic.quic.configuration.QuicConfiguration( + alpn_protocols=alpn_protocols, + verify_mode=verify_mode, + server_name=server_name, + ) + if verify_path is not None: + conf.load_verify_locations(verify_path) + self._conf = conf + + def _connect( + self, + address, + port=853, + source=None, + source_port=0, + want_session_ticket=True, + want_token=True, + ): + connection = self._connections.get((address, port)) + if connection is not None: + return (connection, False) + conf = self._conf + if want_session_ticket: + try: + session_ticket = self._session_tickets.pop((address, port)) + # We found a session ticket, so make a configuration that uses it. + conf = copy.copy(conf) + conf.session_ticket = session_ticket + except KeyError: + # No session ticket. + pass + # Whether or not we found a session ticket, we want a handler to save + # one. + session_ticket_handler = functools.partial( + self.save_session_ticket, address, port + ) + else: + session_ticket_handler = None + if want_token: + try: + token = self._tokens.pop((address, port)) + # We found a token, so make a configuration that uses it. + conf = copy.copy(conf) + conf.token = token + except KeyError: + # No token + pass + # Whether or not we found a token, we want a handler to save # one. + token_handler = functools.partial(self.save_token, address, port) + else: + token_handler = None + + qconn = aioquic.quic.connection.QuicConnection( + configuration=conf, + session_ticket_handler=session_ticket_handler, + token_handler=token_handler, + ) + lladdress = dns.inet.low_level_address_tuple((address, port)) + qconn.connect(lladdress, time.time()) + connection = self._connection_factory( + qconn, address, port, source, source_port, self + ) + self._connections[(address, port)] = connection + return (connection, True) + + def closed(self, address, port): + try: + del self._connections[(address, port)] + except KeyError: + pass + + def is_h3(self): + return self._h3 + + def save_session_ticket(self, address, port, ticket): + # We rely on dictionaries keys() being in insertion order here. We + # can't just popitem() as that would be LIFO which is the opposite of + # what we want. + l = len(self._session_tickets) + if l >= MAX_SESSION_TICKETS: + keys_to_delete = list(self._session_tickets.keys())[0:SESSIONS_TO_DELETE] + for key in keys_to_delete: + del self._session_tickets[key] + self._session_tickets[(address, port)] = ticket + + def save_token(self, address, port, token): + # We rely on dictionaries keys() being in insertion order here. We + # can't just popitem() as that would be LIFO which is the opposite of + # what we want. + l = len(self._tokens) + if l >= MAX_SESSION_TICKETS: + keys_to_delete = list(self._tokens.keys())[0:SESSIONS_TO_DELETE] + for key in keys_to_delete: + del self._tokens[key] + self._tokens[(address, port)] = token + + +class AsyncQuicManager(BaseQuicManager): + def connect(self, address, port=853, source=None, source_port=0): + raise NotImplementedError diff --git a/.venv/lib/python3.9/site-packages/dns/quic/_sync.py b/.venv/lib/python3.9/site-packages/dns/quic/_sync.py new file mode 100644 index 0000000..473d1f4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/quic/_sync.py @@ -0,0 +1,295 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import selectors +import socket +import ssl +import struct +import threading +import time + +import aioquic.quic.configuration # type: ignore +import aioquic.quic.connection # type: ignore +import aioquic.quic.events # type: ignore + +import dns.exception +import dns.inet +from dns.quic._common import ( + QUIC_MAX_DATAGRAM, + BaseQuicConnection, + BaseQuicManager, + BaseQuicStream, + UnexpectedEOF, +) + +# Function used to create a socket. Can be overridden if needed in special +# situations. +socket_factory = socket.socket + + +class SyncQuicStream(BaseQuicStream): + def __init__(self, connection, stream_id): + super().__init__(connection, stream_id) + self._wake_up = threading.Condition() + self._lock = threading.Lock() + + def wait_for(self, amount, expiration): + while True: + timeout = self._timeout_from_expiration(expiration) + with self._lock: + if self._buffer.have(amount): + return + self._expecting = amount + with self._wake_up: + if not self._wake_up.wait(timeout): + raise dns.exception.Timeout + self._expecting = 0 + + def wait_for_end(self, expiration): + while True: + timeout = self._timeout_from_expiration(expiration) + with self._lock: + if self._buffer.seen_end(): + return + with self._wake_up: + if not self._wake_up.wait(timeout): + raise dns.exception.Timeout + + def receive(self, timeout=None): + expiration = self._expiration_from_timeout(timeout) + if self._connection.is_h3(): + self.wait_for_end(expiration) + with self._lock: + return self._buffer.get_all() + else: + self.wait_for(2, expiration) + with self._lock: + (size,) = struct.unpack("!H", self._buffer.get(2)) + self.wait_for(size, expiration) + with self._lock: + return self._buffer.get(size) + + def send(self, datagram, is_end=False): + data = self._encapsulate(datagram) + self._connection.write(self._stream_id, data, is_end) + + def _add_input(self, data, is_end): + if self._common_add_input(data, is_end): + with self._wake_up: + self._wake_up.notify() + + def close(self): + with self._lock: + self._close() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + with self._wake_up: + self._wake_up.notify() + return False + + +class SyncQuicConnection(BaseQuicConnection): + def __init__(self, connection, address, port, source, source_port, manager): + super().__init__(connection, address, port, source, source_port, manager) + self._socket = socket_factory(self._af, socket.SOCK_DGRAM, 0) + if self._source is not None: + try: + self._socket.bind( + dns.inet.low_level_address_tuple(self._source, self._af) + ) + except Exception: + self._socket.close() + raise + self._socket.connect(self._peer) + (self._send_wakeup, self._receive_wakeup) = socket.socketpair() + self._receive_wakeup.setblocking(False) + self._socket.setblocking(False) + self._handshake_complete = threading.Event() + self._worker_thread = None + self._lock = threading.Lock() + + def _read(self): + count = 0 + while count < 10: + count += 1 + try: + datagram = self._socket.recv(QUIC_MAX_DATAGRAM) + except BlockingIOError: + return + with self._lock: + self._connection.receive_datagram(datagram, self._peer, time.time()) + + def _drain_wakeup(self): + while True: + try: + self._receive_wakeup.recv(32) + except BlockingIOError: + return + + def _worker(self): + try: + sel = selectors.DefaultSelector() + sel.register(self._socket, selectors.EVENT_READ, self._read) + sel.register(self._receive_wakeup, selectors.EVENT_READ, self._drain_wakeup) + while not self._done: + (expiration, interval) = self._get_timer_values(False) + items = sel.select(interval) + for key, _ in items: + key.data() + with self._lock: + self._handle_timer(expiration) + self._handle_events() + with self._lock: + datagrams = self._connection.datagrams_to_send(time.time()) + for datagram, _ in datagrams: + try: + self._socket.send(datagram) + except BlockingIOError: + # we let QUIC handle any lossage + pass + finally: + with self._lock: + self._done = True + self._socket.close() + # Ensure anyone waiting for this gets woken up. + self._handshake_complete.set() + + def _handle_events(self): + while True: + with self._lock: + event = self._connection.next_event() + if event is None: + return + if isinstance(event, aioquic.quic.events.StreamDataReceived): + if self.is_h3(): + h3_events = self._h3_conn.handle_event(event) + for h3_event in h3_events: + if isinstance(h3_event, aioquic.h3.events.HeadersReceived): + with self._lock: + stream = self._streams.get(event.stream_id) + if stream: + if stream._headers is None: + stream._headers = h3_event.headers + elif stream._trailers is None: + stream._trailers = h3_event.headers + if h3_event.stream_ended: + stream._add_input(b"", True) + elif isinstance(h3_event, aioquic.h3.events.DataReceived): + with self._lock: + stream = self._streams.get(event.stream_id) + if stream: + stream._add_input(h3_event.data, h3_event.stream_ended) + else: + with self._lock: + stream = self._streams.get(event.stream_id) + if stream: + stream._add_input(event.data, event.end_stream) + elif isinstance(event, aioquic.quic.events.HandshakeCompleted): + self._handshake_complete.set() + elif isinstance(event, aioquic.quic.events.ConnectionTerminated): + with self._lock: + self._done = True + elif isinstance(event, aioquic.quic.events.StreamReset): + with self._lock: + stream = self._streams.get(event.stream_id) + if stream: + stream._add_input(b"", True) + + def write(self, stream, data, is_end=False): + with self._lock: + self._connection.send_stream_data(stream, data, is_end) + self._send_wakeup.send(b"\x01") + + def send_headers(self, stream_id, headers, is_end=False): + with self._lock: + super().send_headers(stream_id, headers, is_end) + if is_end: + self._send_wakeup.send(b"\x01") + + def send_data(self, stream_id, data, is_end=False): + with self._lock: + super().send_data(stream_id, data, is_end) + if is_end: + self._send_wakeup.send(b"\x01") + + def run(self): + if self._closed: + return + self._worker_thread = threading.Thread(target=self._worker) + self._worker_thread.start() + + def make_stream(self, timeout=None): + if not self._handshake_complete.wait(timeout): + raise dns.exception.Timeout + with self._lock: + if self._done: + raise UnexpectedEOF + stream_id = self._connection.get_next_available_stream_id(False) + stream = SyncQuicStream(self, stream_id) + self._streams[stream_id] = stream + return stream + + def close_stream(self, stream_id): + with self._lock: + super().close_stream(stream_id) + + def close(self): + with self._lock: + if self._closed: + return + self._manager.closed(self._peer[0], self._peer[1]) + self._closed = True + self._connection.close() + self._send_wakeup.send(b"\x01") + self._worker_thread.join() + + +class SyncQuicManager(BaseQuicManager): + def __init__( + self, conf=None, verify_mode=ssl.CERT_REQUIRED, server_name=None, h3=False + ): + super().__init__(conf, verify_mode, SyncQuicConnection, server_name, h3) + self._lock = threading.Lock() + + def connect( + self, + address, + port=853, + source=None, + source_port=0, + want_session_ticket=True, + want_token=True, + ): + with self._lock: + (connection, start) = self._connect( + address, port, source, source_port, want_session_ticket, want_token + ) + if start: + connection.run() + return connection + + def closed(self, address, port): + with self._lock: + super().closed(address, port) + + def save_session_ticket(self, address, port, ticket): + with self._lock: + super().save_session_ticket(address, port, ticket) + + def save_token(self, address, port, token): + with self._lock: + super().save_token(address, port, token) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + # Copy the iterator into a list as exiting things will mutate the connections + # table. + connections = list(self._connections.values()) + for connection in connections: + connection.close() + return False diff --git a/.venv/lib/python3.9/site-packages/dns/quic/_trio.py b/.venv/lib/python3.9/site-packages/dns/quic/_trio.py new file mode 100644 index 0000000..ae79f36 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/quic/_trio.py @@ -0,0 +1,246 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import socket +import ssl +import struct +import time + +import aioquic.quic.configuration # type: ignore +import aioquic.quic.connection # type: ignore +import aioquic.quic.events # type: ignore +import trio + +import dns.exception +import dns.inet +from dns._asyncbackend import NullContext +from dns.quic._common import ( + QUIC_MAX_DATAGRAM, + AsyncQuicConnection, + AsyncQuicManager, + BaseQuicStream, + UnexpectedEOF, +) + + +class TrioQuicStream(BaseQuicStream): + def __init__(self, connection, stream_id): + super().__init__(connection, stream_id) + self._wake_up = trio.Condition() + + async def wait_for(self, amount): + while True: + if self._buffer.have(amount): + return + self._expecting = amount + async with self._wake_up: + await self._wake_up.wait() + self._expecting = 0 + + async def wait_for_end(self): + while True: + if self._buffer.seen_end(): + return + async with self._wake_up: + await self._wake_up.wait() + + async def receive(self, timeout=None): + if timeout is None: + context = NullContext(None) + else: + context = trio.move_on_after(timeout) + with context: + if self._connection.is_h3(): + await self.wait_for_end() + return self._buffer.get_all() + else: + await self.wait_for(2) + (size,) = struct.unpack("!H", self._buffer.get(2)) + await self.wait_for(size) + return self._buffer.get(size) + raise dns.exception.Timeout + + async def send(self, datagram, is_end=False): + data = self._encapsulate(datagram) + await self._connection.write(self._stream_id, data, is_end) + + async def _add_input(self, data, is_end): + if self._common_add_input(data, is_end): + async with self._wake_up: + self._wake_up.notify() + + async def close(self): + self._close() + + # Streams are async context managers + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + await self.close() + async with self._wake_up: + self._wake_up.notify() + return False + + +class TrioQuicConnection(AsyncQuicConnection): + def __init__(self, connection, address, port, source, source_port, manager=None): + super().__init__(connection, address, port, source, source_port, manager) + self._socket = trio.socket.socket(self._af, socket.SOCK_DGRAM, 0) + self._handshake_complete = trio.Event() + self._run_done = trio.Event() + self._worker_scope = None + self._send_pending = False + + async def _worker(self): + try: + if self._source: + await self._socket.bind( + dns.inet.low_level_address_tuple(self._source, self._af) + ) + await self._socket.connect(self._peer) + while not self._done: + (expiration, interval) = self._get_timer_values(False) + if self._send_pending: + # Do not block forever if sends are pending. Even though we + # have a wake-up mechanism if we've already started the blocking + # read, the possibility of context switching in send means that + # more writes can happen while we have no wake up context, so + # we need self._send_pending to avoid (effectively) a "lost wakeup" + # race. + interval = 0.0 + with trio.CancelScope( + deadline=trio.current_time() + interval + ) as self._worker_scope: + datagram = await self._socket.recv(QUIC_MAX_DATAGRAM) + self._connection.receive_datagram(datagram, self._peer, time.time()) + self._worker_scope = None + self._handle_timer(expiration) + await self._handle_events() + # We clear this now, before sending anything, as sending can cause + # context switches that do more sends. We want to know if that + # happens so we don't block a long time on the recv() above. + self._send_pending = False + datagrams = self._connection.datagrams_to_send(time.time()) + for datagram, _ in datagrams: + await self._socket.send(datagram) + finally: + self._done = True + self._socket.close() + self._handshake_complete.set() + + async def _handle_events(self): + count = 0 + while True: + event = self._connection.next_event() + if event is None: + return + if isinstance(event, aioquic.quic.events.StreamDataReceived): + if self.is_h3(): + h3_events = self._h3_conn.handle_event(event) + for h3_event in h3_events: + if isinstance(h3_event, aioquic.h3.events.HeadersReceived): + stream = self._streams.get(event.stream_id) + if stream: + if stream._headers is None: + stream._headers = h3_event.headers + elif stream._trailers is None: + stream._trailers = h3_event.headers + if h3_event.stream_ended: + await stream._add_input(b"", True) + elif isinstance(h3_event, aioquic.h3.events.DataReceived): + stream = self._streams.get(event.stream_id) + if stream: + await stream._add_input( + h3_event.data, h3_event.stream_ended + ) + else: + stream = self._streams.get(event.stream_id) + if stream: + await stream._add_input(event.data, event.end_stream) + elif isinstance(event, aioquic.quic.events.HandshakeCompleted): + self._handshake_complete.set() + elif isinstance(event, aioquic.quic.events.ConnectionTerminated): + self._done = True + self._socket.close() + elif isinstance(event, aioquic.quic.events.StreamReset): + stream = self._streams.get(event.stream_id) + if stream: + await stream._add_input(b"", True) + count += 1 + if count > 10: + # yield + count = 0 + await trio.sleep(0) + + async def write(self, stream, data, is_end=False): + self._connection.send_stream_data(stream, data, is_end) + self._send_pending = True + if self._worker_scope is not None: + self._worker_scope.cancel() + + async def run(self): + if self._closed: + return + async with trio.open_nursery() as nursery: + nursery.start_soon(self._worker) + self._run_done.set() + + async def make_stream(self, timeout=None): + if timeout is None: + context = NullContext(None) + else: + context = trio.move_on_after(timeout) + with context: + await self._handshake_complete.wait() + if self._done: + raise UnexpectedEOF + stream_id = self._connection.get_next_available_stream_id(False) + stream = TrioQuicStream(self, stream_id) + self._streams[stream_id] = stream + return stream + raise dns.exception.Timeout + + async def close(self): + if not self._closed: + self._manager.closed(self._peer[0], self._peer[1]) + self._closed = True + self._connection.close() + self._send_pending = True + if self._worker_scope is not None: + self._worker_scope.cancel() + await self._run_done.wait() + + +class TrioQuicManager(AsyncQuicManager): + def __init__( + self, + nursery, + conf=None, + verify_mode=ssl.CERT_REQUIRED, + server_name=None, + h3=False, + ): + super().__init__(conf, verify_mode, TrioQuicConnection, server_name, h3) + self._nursery = nursery + + def connect( + self, address, port=853, source=None, source_port=0, want_session_ticket=True + ): + (connection, start) = self._connect( + address, port, source, source_port, want_session_ticket + ) + if start: + self._nursery.start_soon(connection.run) + return connection + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + # Copy the iterator into a list as exiting things will mutate the connections + # table. + connections = list(self._connections.values()) + for connection in connections: + await connection.close() + return False diff --git a/.venv/lib/python3.9/site-packages/dns/rcode.py b/.venv/lib/python3.9/site-packages/dns/rcode.py new file mode 100644 index 0000000..8e6386f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rcode.py @@ -0,0 +1,168 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2001-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Result Codes.""" + +from typing import Tuple + +import dns.enum +import dns.exception + + +class Rcode(dns.enum.IntEnum): + #: No error + NOERROR = 0 + #: Format error + FORMERR = 1 + #: Server failure + SERVFAIL = 2 + #: Name does not exist ("Name Error" in RFC 1025 terminology). + NXDOMAIN = 3 + #: Not implemented + NOTIMP = 4 + #: Refused + REFUSED = 5 + #: Name exists. + YXDOMAIN = 6 + #: RRset exists. + YXRRSET = 7 + #: RRset does not exist. + NXRRSET = 8 + #: Not authoritative. + NOTAUTH = 9 + #: Name not in zone. + NOTZONE = 10 + #: DSO-TYPE Not Implemented + DSOTYPENI = 11 + #: Bad EDNS version. + BADVERS = 16 + #: TSIG Signature Failure + BADSIG = 16 + #: Key not recognized. + BADKEY = 17 + #: Signature out of time window. + BADTIME = 18 + #: Bad TKEY Mode. + BADMODE = 19 + #: Duplicate key name. + BADNAME = 20 + #: Algorithm not supported. + BADALG = 21 + #: Bad Truncation + BADTRUNC = 22 + #: Bad/missing Server Cookie + BADCOOKIE = 23 + + @classmethod + def _maximum(cls): + return 4095 + + @classmethod + def _unknown_exception_class(cls): + return UnknownRcode + + +class UnknownRcode(dns.exception.DNSException): + """A DNS rcode is unknown.""" + + +def from_text(text: str) -> Rcode: + """Convert text into an rcode. + + *text*, a ``str``, the textual rcode or an integer in textual form. + + Raises ``dns.rcode.UnknownRcode`` if the rcode mnemonic is unknown. + + Returns a ``dns.rcode.Rcode``. + """ + + return Rcode.from_text(text) + + +def from_flags(flags: int, ednsflags: int) -> Rcode: + """Return the rcode value encoded by flags and ednsflags. + + *flags*, an ``int``, the DNS flags field. + + *ednsflags*, an ``int``, the EDNS flags field. + + Raises ``ValueError`` if rcode is < 0 or > 4095 + + Returns a ``dns.rcode.Rcode``. + """ + + value = (flags & 0x000F) | ((ednsflags >> 20) & 0xFF0) + return Rcode.make(value) + + +def to_flags(value: Rcode) -> Tuple[int, int]: + """Return a (flags, ednsflags) tuple which encodes the rcode. + + *value*, a ``dns.rcode.Rcode``, the rcode. + + Raises ``ValueError`` if rcode is < 0 or > 4095. + + Returns an ``(int, int)`` tuple. + """ + + if value < 0 or value > 4095: + raise ValueError("rcode must be >= 0 and <= 4095") + v = value & 0xF + ev = (value & 0xFF0) << 20 + return (v, ev) + + +def to_text(value: Rcode, tsig: bool = False) -> str: + """Convert rcode into text. + + *value*, a ``dns.rcode.Rcode``, the rcode. + + Raises ``ValueError`` if rcode is < 0 or > 4095. + + Returns a ``str``. + """ + + if tsig and value == Rcode.BADVERS: + return "BADSIG" + return Rcode.to_text(value) + + +### BEGIN generated Rcode constants + +NOERROR = Rcode.NOERROR +FORMERR = Rcode.FORMERR +SERVFAIL = Rcode.SERVFAIL +NXDOMAIN = Rcode.NXDOMAIN +NOTIMP = Rcode.NOTIMP +REFUSED = Rcode.REFUSED +YXDOMAIN = Rcode.YXDOMAIN +YXRRSET = Rcode.YXRRSET +NXRRSET = Rcode.NXRRSET +NOTAUTH = Rcode.NOTAUTH +NOTZONE = Rcode.NOTZONE +DSOTYPENI = Rcode.DSOTYPENI +BADVERS = Rcode.BADVERS +BADSIG = Rcode.BADSIG +BADKEY = Rcode.BADKEY +BADTIME = Rcode.BADTIME +BADMODE = Rcode.BADMODE +BADNAME = Rcode.BADNAME +BADALG = Rcode.BADALG +BADTRUNC = Rcode.BADTRUNC +BADCOOKIE = Rcode.BADCOOKIE + +### END generated Rcode constants diff --git a/.venv/lib/python3.9/site-packages/dns/rdata.py b/.venv/lib/python3.9/site-packages/dns/rdata.py new file mode 100644 index 0000000..8099c26 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdata.py @@ -0,0 +1,911 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2001-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS rdata.""" + +import base64 +import binascii +import inspect +import io +import itertools +import random +from importlib import import_module +from typing import Any, Dict, Optional, Tuple, Union + +import dns.exception +import dns.immutable +import dns.ipv4 +import dns.ipv6 +import dns.name +import dns.rdataclass +import dns.rdatatype +import dns.tokenizer +import dns.ttl +import dns.wire + +_chunksize = 32 + +# We currently allow comparisons for rdata with relative names for backwards +# compatibility, but in the future we will not, as these kinds of comparisons +# can lead to subtle bugs if code is not carefully written. +# +# This switch allows the future behavior to be turned on so code can be +# tested with it. +_allow_relative_comparisons = True + + +class NoRelativeRdataOrdering(dns.exception.DNSException): + """An attempt was made to do an ordered comparison of one or more + rdata with relative names. The only reliable way of sorting rdata + is to use non-relativized rdata. + + """ + + +def _wordbreak(data, chunksize=_chunksize, separator=b" "): + """Break a binary string into chunks of chunksize characters separated by + a space. + """ + + if not chunksize: + return data.decode() + return separator.join( + [data[i : i + chunksize] for i in range(0, len(data), chunksize)] + ).decode() + + +# pylint: disable=unused-argument + + +def _hexify(data, chunksize=_chunksize, separator=b" ", **kw): + """Convert a binary string into its hex encoding, broken up into chunks + of chunksize characters separated by a separator. + """ + + return _wordbreak(binascii.hexlify(data), chunksize, separator) + + +def _base64ify(data, chunksize=_chunksize, separator=b" ", **kw): + """Convert a binary string into its base64 encoding, broken up into chunks + of chunksize characters separated by a separator. + """ + + return _wordbreak(base64.b64encode(data), chunksize, separator) + + +# pylint: enable=unused-argument + +__escaped = b'"\\' + + +def _escapify(qstring): + """Escape the characters in a quoted string which need it.""" + + if isinstance(qstring, str): + qstring = qstring.encode() + if not isinstance(qstring, bytearray): + qstring = bytearray(qstring) + + text = "" + for c in qstring: + if c in __escaped: + text += "\\" + chr(c) + elif c >= 0x20 and c < 0x7F: + text += chr(c) + else: + text += "\\%03d" % c + return text + + +def _truncate_bitmap(what): + """Determine the index of greatest byte that isn't all zeros, and + return the bitmap that contains all the bytes less than that index. + """ + + for i in range(len(what) - 1, -1, -1): + if what[i] != 0: + return what[0 : i + 1] + return what[0:1] + + +# So we don't have to edit all the rdata classes... +_constify = dns.immutable.constify + + +@dns.immutable.immutable +class Rdata: + """Base class for all DNS rdata types.""" + + __slots__ = ["rdclass", "rdtype", "rdcomment"] + + def __init__(self, rdclass, rdtype): + """Initialize an rdata. + + *rdclass*, an ``int`` is the rdataclass of the Rdata. + + *rdtype*, an ``int`` is the rdatatype of the Rdata. + """ + + self.rdclass = self._as_rdataclass(rdclass) + self.rdtype = self._as_rdatatype(rdtype) + self.rdcomment = None + + def _get_all_slots(self): + return itertools.chain.from_iterable( + getattr(cls, "__slots__", []) for cls in self.__class__.__mro__ + ) + + def __getstate__(self): + # We used to try to do a tuple of all slots here, but it + # doesn't work as self._all_slots isn't available at + # __setstate__() time. Before that we tried to store a tuple + # of __slots__, but that didn't work as it didn't store the + # slots defined by ancestors. This older way didn't fail + # outright, but ended up with partially broken objects, e.g. + # if you unpickled an A RR it wouldn't have rdclass and rdtype + # attributes, and would compare badly. + state = {} + for slot in self._get_all_slots(): + state[slot] = getattr(self, slot) + return state + + def __setstate__(self, state): + for slot, val in state.items(): + object.__setattr__(self, slot, val) + if not hasattr(self, "rdcomment"): + # Pickled rdata from 2.0.x might not have a rdcomment, so add + # it if needed. + object.__setattr__(self, "rdcomment", None) + + def covers(self) -> dns.rdatatype.RdataType: + """Return the type a Rdata covers. + + DNS SIG/RRSIG rdatas apply to a specific type; this type is + returned by the covers() function. If the rdata type is not + SIG or RRSIG, dns.rdatatype.NONE is returned. This is useful when + creating rdatasets, allowing the rdataset to contain only RRSIGs + of a particular type, e.g. RRSIG(NS). + + Returns a ``dns.rdatatype.RdataType``. + """ + + return dns.rdatatype.NONE + + def extended_rdatatype(self) -> int: + """Return a 32-bit type value, the least significant 16 bits of + which are the ordinary DNS type, and the upper 16 bits of which are + the "covered" type, if any. + + Returns an ``int``. + """ + + return self.covers() << 16 | self.rdtype + + def to_text( + self, + origin: Optional[dns.name.Name] = None, + relativize: bool = True, + **kw: Dict[str, Any], + ) -> str: + """Convert an rdata to text format. + + Returns a ``str``. + """ + + raise NotImplementedError # pragma: no cover + + def _to_wire( + self, + file: Optional[Any], + compress: Optional[dns.name.CompressType] = None, + origin: Optional[dns.name.Name] = None, + canonicalize: bool = False, + ) -> None: + raise NotImplementedError # pragma: no cover + + def to_wire( + self, + file: Optional[Any] = None, + compress: Optional[dns.name.CompressType] = None, + origin: Optional[dns.name.Name] = None, + canonicalize: bool = False, + ) -> Optional[bytes]: + """Convert an rdata to wire format. + + Returns a ``bytes`` if no output file was specified, or ``None`` otherwise. + """ + + if file: + # We call _to_wire() and then return None explicitly instead of + # of just returning the None from _to_wire() as mypy's func-returns-value + # unhelpfully errors out with "error: "_to_wire" of "Rdata" does not return + # a value (it only ever returns None)" + self._to_wire(file, compress, origin, canonicalize) + return None + else: + f = io.BytesIO() + self._to_wire(f, compress, origin, canonicalize) + return f.getvalue() + + def to_generic( + self, origin: Optional[dns.name.Name] = None + ) -> "dns.rdata.GenericRdata": + """Creates a dns.rdata.GenericRdata equivalent of this rdata. + + Returns a ``dns.rdata.GenericRdata``. + """ + return dns.rdata.GenericRdata( + self.rdclass, self.rdtype, self.to_wire(origin=origin) + ) + + def to_digestable(self, origin: Optional[dns.name.Name] = None) -> bytes: + """Convert rdata to a format suitable for digesting in hashes. This + is also the DNSSEC canonical form. + + Returns a ``bytes``. + """ + wire = self.to_wire(origin=origin, canonicalize=True) + assert wire is not None # for mypy + return wire + + def __repr__(self): + covers = self.covers() + if covers == dns.rdatatype.NONE: + ctext = "" + else: + ctext = "(" + dns.rdatatype.to_text(covers) + ")" + return ( + "" + ) + + def __str__(self): + return self.to_text() + + def _cmp(self, other): + """Compare an rdata with another rdata of the same rdtype and + rdclass. + + For rdata with only absolute names: + Return < 0 if self < other in the DNSSEC ordering, 0 if self + == other, and > 0 if self > other. + For rdata with at least one relative names: + The rdata sorts before any rdata with only absolute names. + When compared with another relative rdata, all names are + made absolute as if they were relative to the root, as the + proper origin is not available. While this creates a stable + ordering, it is NOT guaranteed to be the DNSSEC ordering. + In the future, all ordering comparisons for rdata with + relative names will be disallowed. + """ + try: + our = self.to_digestable() + our_relative = False + except dns.name.NeedAbsoluteNameOrOrigin: + if _allow_relative_comparisons: + our = self.to_digestable(dns.name.root) + our_relative = True + try: + their = other.to_digestable() + their_relative = False + except dns.name.NeedAbsoluteNameOrOrigin: + if _allow_relative_comparisons: + their = other.to_digestable(dns.name.root) + their_relative = True + if _allow_relative_comparisons: + if our_relative != their_relative: + # For the purpose of comparison, all rdata with at least one + # relative name is less than an rdata with only absolute names. + if our_relative: + return -1 + else: + return 1 + elif our_relative or their_relative: + raise NoRelativeRdataOrdering + if our == their: + return 0 + elif our > their: + return 1 + else: + return -1 + + def __eq__(self, other): + if not isinstance(other, Rdata): + return False + if self.rdclass != other.rdclass or self.rdtype != other.rdtype: + return False + our_relative = False + their_relative = False + try: + our = self.to_digestable() + except dns.name.NeedAbsoluteNameOrOrigin: + our = self.to_digestable(dns.name.root) + our_relative = True + try: + their = other.to_digestable() + except dns.name.NeedAbsoluteNameOrOrigin: + their = other.to_digestable(dns.name.root) + their_relative = True + if our_relative != their_relative: + return False + return our == their + + def __ne__(self, other): + if not isinstance(other, Rdata): + return True + if self.rdclass != other.rdclass or self.rdtype != other.rdtype: + return True + return not self.__eq__(other) + + def __lt__(self, other): + if ( + not isinstance(other, Rdata) + or self.rdclass != other.rdclass + or self.rdtype != other.rdtype + ): + return NotImplemented + return self._cmp(other) < 0 + + def __le__(self, other): + if ( + not isinstance(other, Rdata) + or self.rdclass != other.rdclass + or self.rdtype != other.rdtype + ): + return NotImplemented + return self._cmp(other) <= 0 + + def __ge__(self, other): + if ( + not isinstance(other, Rdata) + or self.rdclass != other.rdclass + or self.rdtype != other.rdtype + ): + return NotImplemented + return self._cmp(other) >= 0 + + def __gt__(self, other): + if ( + not isinstance(other, Rdata) + or self.rdclass != other.rdclass + or self.rdtype != other.rdtype + ): + return NotImplemented + return self._cmp(other) > 0 + + def __hash__(self): + return hash(self.to_digestable(dns.name.root)) + + @classmethod + def from_text( + cls, + rdclass: dns.rdataclass.RdataClass, + rdtype: dns.rdatatype.RdataType, + tok: dns.tokenizer.Tokenizer, + origin: Optional[dns.name.Name] = None, + relativize: bool = True, + relativize_to: Optional[dns.name.Name] = None, + ) -> "Rdata": + raise NotImplementedError # pragma: no cover + + @classmethod + def from_wire_parser( + cls, + rdclass: dns.rdataclass.RdataClass, + rdtype: dns.rdatatype.RdataType, + parser: dns.wire.Parser, + origin: Optional[dns.name.Name] = None, + ) -> "Rdata": + raise NotImplementedError # pragma: no cover + + def replace(self, **kwargs: Any) -> "Rdata": + """ + Create a new Rdata instance based on the instance replace was + invoked on. It is possible to pass different parameters to + override the corresponding properties of the base Rdata. + + Any field specific to the Rdata type can be replaced, but the + *rdtype* and *rdclass* fields cannot. + + Returns an instance of the same Rdata subclass as *self*. + """ + + # Get the constructor parameters. + parameters = inspect.signature(self.__init__).parameters # type: ignore + + # Ensure that all of the arguments correspond to valid fields. + # Don't allow rdclass or rdtype to be changed, though. + for key in kwargs: + if key == "rdcomment": + continue + if key not in parameters: + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{key}'" + ) + if key in ("rdclass", "rdtype"): + raise AttributeError( + f"Cannot overwrite '{self.__class__.__name__}' attribute '{key}'" + ) + + # Construct the parameter list. For each field, use the value in + # kwargs if present, and the current value otherwise. + args = (kwargs.get(key, getattr(self, key)) for key in parameters) + + # Create, validate, and return the new object. + rd = self.__class__(*args) + # The comment is not set in the constructor, so give it special + # handling. + rdcomment = kwargs.get("rdcomment", self.rdcomment) + if rdcomment is not None: + object.__setattr__(rd, "rdcomment", rdcomment) + return rd + + # Type checking and conversion helpers. These are class methods as + # they don't touch object state and may be useful to others. + + @classmethod + def _as_rdataclass(cls, value): + return dns.rdataclass.RdataClass.make(value) + + @classmethod + def _as_rdatatype(cls, value): + return dns.rdatatype.RdataType.make(value) + + @classmethod + def _as_bytes( + cls, + value: Any, + encode: bool = False, + max_length: Optional[int] = None, + empty_ok: bool = True, + ) -> bytes: + if encode and isinstance(value, str): + bvalue = value.encode() + elif isinstance(value, bytearray): + bvalue = bytes(value) + elif isinstance(value, bytes): + bvalue = value + else: + raise ValueError("not bytes") + if max_length is not None and len(bvalue) > max_length: + raise ValueError("too long") + if not empty_ok and len(bvalue) == 0: + raise ValueError("empty bytes not allowed") + return bvalue + + @classmethod + def _as_name(cls, value): + # Note that proper name conversion (e.g. with origin and IDNA + # awareness) is expected to be done via from_text. This is just + # a simple thing for people invoking the constructor directly. + if isinstance(value, str): + return dns.name.from_text(value) + elif not isinstance(value, dns.name.Name): + raise ValueError("not a name") + return value + + @classmethod + def _as_uint8(cls, value): + if not isinstance(value, int): + raise ValueError("not an integer") + if value < 0 or value > 255: + raise ValueError("not a uint8") + return value + + @classmethod + def _as_uint16(cls, value): + if not isinstance(value, int): + raise ValueError("not an integer") + if value < 0 or value > 65535: + raise ValueError("not a uint16") + return value + + @classmethod + def _as_uint32(cls, value): + if not isinstance(value, int): + raise ValueError("not an integer") + if value < 0 or value > 4294967295: + raise ValueError("not a uint32") + return value + + @classmethod + def _as_uint48(cls, value): + if not isinstance(value, int): + raise ValueError("not an integer") + if value < 0 or value > 281474976710655: + raise ValueError("not a uint48") + return value + + @classmethod + def _as_int(cls, value, low=None, high=None): + if not isinstance(value, int): + raise ValueError("not an integer") + if low is not None and value < low: + raise ValueError("value too small") + if high is not None and value > high: + raise ValueError("value too large") + return value + + @classmethod + def _as_ipv4_address(cls, value): + if isinstance(value, str): + return dns.ipv4.canonicalize(value) + elif isinstance(value, bytes): + return dns.ipv4.inet_ntoa(value) + else: + raise ValueError("not an IPv4 address") + + @classmethod + def _as_ipv6_address(cls, value): + if isinstance(value, str): + return dns.ipv6.canonicalize(value) + elif isinstance(value, bytes): + return dns.ipv6.inet_ntoa(value) + else: + raise ValueError("not an IPv6 address") + + @classmethod + def _as_bool(cls, value): + if isinstance(value, bool): + return value + else: + raise ValueError("not a boolean") + + @classmethod + def _as_ttl(cls, value): + if isinstance(value, int): + return cls._as_int(value, 0, dns.ttl.MAX_TTL) + elif isinstance(value, str): + return dns.ttl.from_text(value) + else: + raise ValueError("not a TTL") + + @classmethod + def _as_tuple(cls, value, as_value): + try: + # For user convenience, if value is a singleton of the list + # element type, wrap it in a tuple. + return (as_value(value),) + except Exception: + # Otherwise, check each element of the iterable *value* + # against *as_value*. + return tuple(as_value(v) for v in value) + + # Processing order + + @classmethod + def _processing_order(cls, iterable): + items = list(iterable) + random.shuffle(items) + return items + + +@dns.immutable.immutable +class GenericRdata(Rdata): + """Generic Rdata Class + + This class is used for rdata types for which we have no better + implementation. It implements the DNS "unknown RRs" scheme. + """ + + __slots__ = ["data"] + + def __init__(self, rdclass, rdtype, data): + super().__init__(rdclass, rdtype) + self.data = data + + def to_text( + self, + origin: Optional[dns.name.Name] = None, + relativize: bool = True, + **kw: Dict[str, Any], + ) -> str: + return r"\# %d " % len(self.data) + _hexify(self.data, **kw) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + token = tok.get() + if not token.is_identifier() or token.value != r"\#": + raise dns.exception.SyntaxError(r"generic rdata does not start with \#") + length = tok.get_int() + hex = tok.concatenate_remaining_identifiers(True).encode() + data = binascii.unhexlify(hex) + if len(data) != length: + raise dns.exception.SyntaxError("generic rdata hex data has wrong length") + return cls(rdclass, rdtype, data) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + file.write(self.data) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + return cls(rdclass, rdtype, parser.get_remaining()) + + +_rdata_classes: Dict[Tuple[dns.rdataclass.RdataClass, dns.rdatatype.RdataType], Any] = ( + {} +) +_module_prefix = "dns.rdtypes" +_dynamic_load_allowed = True + + +def get_rdata_class(rdclass, rdtype, use_generic=True): + cls = _rdata_classes.get((rdclass, rdtype)) + if not cls: + cls = _rdata_classes.get((dns.rdatatype.ANY, rdtype)) + if not cls and _dynamic_load_allowed: + rdclass_text = dns.rdataclass.to_text(rdclass) + rdtype_text = dns.rdatatype.to_text(rdtype) + rdtype_text = rdtype_text.replace("-", "_") + try: + mod = import_module( + ".".join([_module_prefix, rdclass_text, rdtype_text]) + ) + cls = getattr(mod, rdtype_text) + _rdata_classes[(rdclass, rdtype)] = cls + except ImportError: + try: + mod = import_module(".".join([_module_prefix, "ANY", rdtype_text])) + cls = getattr(mod, rdtype_text) + _rdata_classes[(dns.rdataclass.ANY, rdtype)] = cls + _rdata_classes[(rdclass, rdtype)] = cls + except ImportError: + pass + if not cls and use_generic: + cls = GenericRdata + _rdata_classes[(rdclass, rdtype)] = cls + return cls + + +def load_all_types(disable_dynamic_load=True): + """Load all rdata types for which dnspython has a non-generic implementation. + + Normally dnspython loads DNS rdatatype implementations on demand, but in some + specialized cases loading all types at an application-controlled time is preferred. + + If *disable_dynamic_load*, a ``bool``, is ``True`` then dnspython will not attempt + to use its dynamic loading mechanism if an unknown type is subsequently encountered, + and will simply use the ``GenericRdata`` class. + """ + # Load class IN and ANY types. + for rdtype in dns.rdatatype.RdataType: + get_rdata_class(dns.rdataclass.IN, rdtype, False) + # Load the one non-ANY implementation we have in CH. Everything + # else in CH is an ANY type, and we'll discover those on demand but won't + # have to import anything. + get_rdata_class(dns.rdataclass.CH, dns.rdatatype.A, False) + if disable_dynamic_load: + # Now disable dynamic loading so any subsequent unknown type immediately becomes + # GenericRdata without a load attempt. + global _dynamic_load_allowed + _dynamic_load_allowed = False + + +def from_text( + rdclass: Union[dns.rdataclass.RdataClass, str], + rdtype: Union[dns.rdatatype.RdataType, str], + tok: Union[dns.tokenizer.Tokenizer, str], + origin: Optional[dns.name.Name] = None, + relativize: bool = True, + relativize_to: Optional[dns.name.Name] = None, + idna_codec: Optional[dns.name.IDNACodec] = None, +) -> Rdata: + """Build an rdata object from text format. + + This function attempts to dynamically load a class which + implements the specified rdata class and type. If there is no + class-and-type-specific implementation, the GenericRdata class + is used. + + Once a class is chosen, its from_text() class method is called + with the parameters to this function. + + If *tok* is a ``str``, then a tokenizer is created and the string + is used as its input. + + *rdclass*, a ``dns.rdataclass.RdataClass`` or ``str``, the rdataclass. + + *rdtype*, a ``dns.rdatatype.RdataType`` or ``str``, the rdatatype. + + *tok*, a ``dns.tokenizer.Tokenizer`` or a ``str``. + + *origin*, a ``dns.name.Name`` (or ``None``), the + origin to use for relative names. + + *relativize*, a ``bool``. If true, name will be relativized. + + *relativize_to*, a ``dns.name.Name`` (or ``None``), the origin to use + when relativizing names. If not set, the *origin* value will be used. + + *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA + encoder/decoder to use if a tokenizer needs to be created. If + ``None``, the default IDNA 2003 encoder/decoder is used. If a + tokenizer is not created, then the codec associated with the tokenizer + is the one that is used. + + Returns an instance of the chosen Rdata subclass. + + """ + if isinstance(tok, str): + tok = dns.tokenizer.Tokenizer(tok, idna_codec=idna_codec) + rdclass = dns.rdataclass.RdataClass.make(rdclass) + rdtype = dns.rdatatype.RdataType.make(rdtype) + cls = get_rdata_class(rdclass, rdtype) + with dns.exception.ExceptionWrapper(dns.exception.SyntaxError): + rdata = None + if cls != GenericRdata: + # peek at first token + token = tok.get() + tok.unget(token) + if token.is_identifier() and token.value == r"\#": + # + # Known type using the generic syntax. Extract the + # wire form from the generic syntax, and then run + # from_wire on it. + # + grdata = GenericRdata.from_text( + rdclass, rdtype, tok, origin, relativize, relativize_to + ) + rdata = from_wire( + rdclass, rdtype, grdata.data, 0, len(grdata.data), origin + ) + # + # If this comparison isn't equal, then there must have been + # compressed names in the wire format, which is an error, + # there being no reasonable context to decompress with. + # + rwire = rdata.to_wire() + if rwire != grdata.data: + raise dns.exception.SyntaxError( + "compressed data in " + "generic syntax form " + "of known rdatatype" + ) + if rdata is None: + rdata = cls.from_text( + rdclass, rdtype, tok, origin, relativize, relativize_to + ) + token = tok.get_eol_as_token() + if token.comment is not None: + object.__setattr__(rdata, "rdcomment", token.comment) + return rdata + + +def from_wire_parser( + rdclass: Union[dns.rdataclass.RdataClass, str], + rdtype: Union[dns.rdatatype.RdataType, str], + parser: dns.wire.Parser, + origin: Optional[dns.name.Name] = None, +) -> Rdata: + """Build an rdata object from wire format + + This function attempts to dynamically load a class which + implements the specified rdata class and type. If there is no + class-and-type-specific implementation, the GenericRdata class + is used. + + Once a class is chosen, its from_wire() class method is called + with the parameters to this function. + + *rdclass*, a ``dns.rdataclass.RdataClass`` or ``str``, the rdataclass. + + *rdtype*, a ``dns.rdatatype.RdataType`` or ``str``, the rdatatype. + + *parser*, a ``dns.wire.Parser``, the parser, which should be + restricted to the rdata length. + + *origin*, a ``dns.name.Name`` (or ``None``). If not ``None``, + then names will be relativized to this origin. + + Returns an instance of the chosen Rdata subclass. + """ + + rdclass = dns.rdataclass.RdataClass.make(rdclass) + rdtype = dns.rdatatype.RdataType.make(rdtype) + cls = get_rdata_class(rdclass, rdtype) + with dns.exception.ExceptionWrapper(dns.exception.FormError): + return cls.from_wire_parser(rdclass, rdtype, parser, origin) + + +def from_wire( + rdclass: Union[dns.rdataclass.RdataClass, str], + rdtype: Union[dns.rdatatype.RdataType, str], + wire: bytes, + current: int, + rdlen: int, + origin: Optional[dns.name.Name] = None, +) -> Rdata: + """Build an rdata object from wire format + + This function attempts to dynamically load a class which + implements the specified rdata class and type. If there is no + class-and-type-specific implementation, the GenericRdata class + is used. + + Once a class is chosen, its from_wire() class method is called + with the parameters to this function. + + *rdclass*, an ``int``, the rdataclass. + + *rdtype*, an ``int``, the rdatatype. + + *wire*, a ``bytes``, the wire-format message. + + *current*, an ``int``, the offset in wire of the beginning of + the rdata. + + *rdlen*, an ``int``, the length of the wire-format rdata + + *origin*, a ``dns.name.Name`` (or ``None``). If not ``None``, + then names will be relativized to this origin. + + Returns an instance of the chosen Rdata subclass. + """ + parser = dns.wire.Parser(wire, current) + with parser.restrict_to(rdlen): + return from_wire_parser(rdclass, rdtype, parser, origin) + + +class RdatatypeExists(dns.exception.DNSException): + """DNS rdatatype already exists.""" + + supp_kwargs = {"rdclass", "rdtype"} + fmt = ( + "The rdata type with class {rdclass:d} and rdtype {rdtype:d} " + + "already exists." + ) + + +def register_type( + implementation: Any, + rdtype: int, + rdtype_text: str, + is_singleton: bool = False, + rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, +) -> None: + """Dynamically register a module to handle an rdatatype. + + *implementation*, a module implementing the type in the usual dnspython + way. + + *rdtype*, an ``int``, the rdatatype to register. + + *rdtype_text*, a ``str``, the textual form of the rdatatype. + + *is_singleton*, a ``bool``, indicating if the type is a singleton (i.e. + RRsets of the type can have only one member.) + + *rdclass*, the rdataclass of the type, or ``dns.rdataclass.ANY`` if + it applies to all classes. + """ + + rdtype = dns.rdatatype.RdataType.make(rdtype) + existing_cls = get_rdata_class(rdclass, rdtype) + if existing_cls != GenericRdata or dns.rdatatype.is_metatype(rdtype): + raise RdatatypeExists(rdclass=rdclass, rdtype=rdtype) + _rdata_classes[(rdclass, rdtype)] = getattr( + implementation, rdtype_text.replace("-", "_") + ) + dns.rdatatype.register_type(rdtype, rdtype_text, is_singleton) diff --git a/.venv/lib/python3.9/site-packages/dns/rdataclass.py b/.venv/lib/python3.9/site-packages/dns/rdataclass.py new file mode 100644 index 0000000..89b85a7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdataclass.py @@ -0,0 +1,118 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2001-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Rdata Classes.""" + +import dns.enum +import dns.exception + + +class RdataClass(dns.enum.IntEnum): + """DNS Rdata Class""" + + RESERVED0 = 0 + IN = 1 + INTERNET = IN + CH = 3 + CHAOS = CH + HS = 4 + HESIOD = HS + NONE = 254 + ANY = 255 + + @classmethod + def _maximum(cls): + return 65535 + + @classmethod + def _short_name(cls): + return "class" + + @classmethod + def _prefix(cls): + return "CLASS" + + @classmethod + def _unknown_exception_class(cls): + return UnknownRdataclass + + +_metaclasses = {RdataClass.NONE, RdataClass.ANY} + + +class UnknownRdataclass(dns.exception.DNSException): + """A DNS class is unknown.""" + + +def from_text(text: str) -> RdataClass: + """Convert text into a DNS rdata class value. + + The input text can be a defined DNS RR class mnemonic or + instance of the DNS generic class syntax. + + For example, "IN" and "CLASS1" will both result in a value of 1. + + Raises ``dns.rdatatype.UnknownRdataclass`` if the class is unknown. + + Raises ``ValueError`` if the rdata class value is not >= 0 and <= 65535. + + Returns a ``dns.rdataclass.RdataClass``. + """ + + return RdataClass.from_text(text) + + +def to_text(value: RdataClass) -> str: + """Convert a DNS rdata class value to text. + + If the value has a known mnemonic, it will be used, otherwise the + DNS generic class syntax will be used. + + Raises ``ValueError`` if the rdata class value is not >= 0 and <= 65535. + + Returns a ``str``. + """ + + return RdataClass.to_text(value) + + +def is_metaclass(rdclass: RdataClass) -> bool: + """True if the specified class is a metaclass. + + The currently defined metaclasses are ANY and NONE. + + *rdclass* is a ``dns.rdataclass.RdataClass``. + """ + + if rdclass in _metaclasses: + return True + return False + + +### BEGIN generated RdataClass constants + +RESERVED0 = RdataClass.RESERVED0 +IN = RdataClass.IN +INTERNET = RdataClass.INTERNET +CH = RdataClass.CH +CHAOS = RdataClass.CHAOS +HS = RdataClass.HS +HESIOD = RdataClass.HESIOD +NONE = RdataClass.NONE +ANY = RdataClass.ANY + +### END generated RdataClass constants diff --git a/.venv/lib/python3.9/site-packages/dns/rdataset.py b/.venv/lib/python3.9/site-packages/dns/rdataset.py new file mode 100644 index 0000000..39cab23 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdataset.py @@ -0,0 +1,512 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2001-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS rdatasets (an rdataset is a set of rdatas of a given type and class)""" + +import io +import random +import struct +from typing import Any, Collection, Dict, List, Optional, Union, cast + +import dns.exception +import dns.immutable +import dns.name +import dns.rdata +import dns.rdataclass +import dns.rdatatype +import dns.renderer +import dns.set +import dns.ttl + +# define SimpleSet here for backwards compatibility +SimpleSet = dns.set.Set + + +class DifferingCovers(dns.exception.DNSException): + """An attempt was made to add a DNS SIG/RRSIG whose covered type + is not the same as that of the other rdatas in the rdataset.""" + + +class IncompatibleTypes(dns.exception.DNSException): + """An attempt was made to add DNS RR data of an incompatible type.""" + + +class Rdataset(dns.set.Set): + """A DNS rdataset.""" + + __slots__ = ["rdclass", "rdtype", "covers", "ttl"] + + def __init__( + self, + rdclass: dns.rdataclass.RdataClass, + rdtype: dns.rdatatype.RdataType, + covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, + ttl: int = 0, + ): + """Create a new rdataset of the specified class and type. + + *rdclass*, a ``dns.rdataclass.RdataClass``, the rdataclass. + + *rdtype*, an ``dns.rdatatype.RdataType``, the rdatatype. + + *covers*, an ``dns.rdatatype.RdataType``, the covered rdatatype. + + *ttl*, an ``int``, the TTL. + """ + + super().__init__() + self.rdclass = rdclass + self.rdtype: dns.rdatatype.RdataType = rdtype + self.covers: dns.rdatatype.RdataType = covers + self.ttl = ttl + + def _clone(self): + obj = super()._clone() + obj.rdclass = self.rdclass + obj.rdtype = self.rdtype + obj.covers = self.covers + obj.ttl = self.ttl + return obj + + def update_ttl(self, ttl: int) -> None: + """Perform TTL minimization. + + Set the TTL of the rdataset to be the lesser of the set's current + TTL or the specified TTL. If the set contains no rdatas, set the TTL + to the specified TTL. + + *ttl*, an ``int`` or ``str``. + """ + ttl = dns.ttl.make(ttl) + if len(self) == 0: + self.ttl = ttl + elif ttl < self.ttl: + self.ttl = ttl + + def add( # pylint: disable=arguments-differ,arguments-renamed + self, rd: dns.rdata.Rdata, ttl: Optional[int] = None + ) -> None: + """Add the specified rdata to the rdataset. + + If the optional *ttl* parameter is supplied, then + ``self.update_ttl(ttl)`` will be called prior to adding the rdata. + + *rd*, a ``dns.rdata.Rdata``, the rdata + + *ttl*, an ``int``, the TTL. + + Raises ``dns.rdataset.IncompatibleTypes`` if the type and class + do not match the type and class of the rdataset. + + Raises ``dns.rdataset.DifferingCovers`` if the type is a signature + type and the covered type does not match that of the rdataset. + """ + + # + # If we're adding a signature, do some special handling to + # check that the signature covers the same type as the + # other rdatas in this rdataset. If this is the first rdata + # in the set, initialize the covers field. + # + if self.rdclass != rd.rdclass or self.rdtype != rd.rdtype: + raise IncompatibleTypes + if ttl is not None: + self.update_ttl(ttl) + if self.rdtype == dns.rdatatype.RRSIG or self.rdtype == dns.rdatatype.SIG: + covers = rd.covers() + if len(self) == 0 and self.covers == dns.rdatatype.NONE: + self.covers = covers + elif self.covers != covers: + raise DifferingCovers + if dns.rdatatype.is_singleton(rd.rdtype) and len(self) > 0: + self.clear() + super().add(rd) + + def union_update(self, other): + self.update_ttl(other.ttl) + super().union_update(other) + + def intersection_update(self, other): + self.update_ttl(other.ttl) + super().intersection_update(other) + + def update(self, other): + """Add all rdatas in other to self. + + *other*, a ``dns.rdataset.Rdataset``, the rdataset from which + to update. + """ + + self.update_ttl(other.ttl) + super().update(other) + + def _rdata_repr(self): + def maybe_truncate(s): + if len(s) > 100: + return s[:100] + "..." + return s + + return "[" + ", ".join(f"<{maybe_truncate(str(rr))}>" for rr in self) + "]" + + def __repr__(self): + if self.covers == 0: + ctext = "" + else: + ctext = "(" + dns.rdatatype.to_text(self.covers) + ")" + return ( + "" + ) + + def __str__(self): + return self.to_text() + + def __eq__(self, other): + if not isinstance(other, Rdataset): + return False + if ( + self.rdclass != other.rdclass + or self.rdtype != other.rdtype + or self.covers != other.covers + ): + return False + return super().__eq__(other) + + def __ne__(self, other): + return not self.__eq__(other) + + def to_text( + self, + name: Optional[dns.name.Name] = None, + origin: Optional[dns.name.Name] = None, + relativize: bool = True, + override_rdclass: Optional[dns.rdataclass.RdataClass] = None, + want_comments: bool = False, + **kw: Dict[str, Any], + ) -> str: + """Convert the rdataset into DNS zone file format. + + See ``dns.name.Name.choose_relativity`` for more information + on how *origin* and *relativize* determine the way names + are emitted. + + Any additional keyword arguments are passed on to the rdata + ``to_text()`` method. + + *name*, a ``dns.name.Name``. If name is not ``None``, emit RRs with + *name* as the owner name. + + *origin*, a ``dns.name.Name`` or ``None``, the origin for relative + names. + + *relativize*, a ``bool``. If ``True``, names will be relativized + to *origin*. + + *override_rdclass*, a ``dns.rdataclass.RdataClass`` or ``None``. + If not ``None``, use this class instead of the Rdataset's class. + + *want_comments*, a ``bool``. If ``True``, emit comments for rdata + which have them. The default is ``False``. + """ + + if name is not None: + name = name.choose_relativity(origin, relativize) + ntext = str(name) + pad = " " + else: + ntext = "" + pad = "" + s = io.StringIO() + if override_rdclass is not None: + rdclass = override_rdclass + else: + rdclass = self.rdclass + if len(self) == 0: + # + # Empty rdatasets are used for the question section, and in + # some dynamic updates, so we don't need to print out the TTL + # (which is meaningless anyway). + # + s.write( + f"{ntext}{pad}{dns.rdataclass.to_text(rdclass)} " + f"{dns.rdatatype.to_text(self.rdtype)}\n" + ) + else: + for rd in self: + extra = "" + if want_comments: + if rd.rdcomment: + extra = f" ;{rd.rdcomment}" + s.write( + "%s%s%d %s %s %s%s\n" + % ( + ntext, + pad, + self.ttl, + dns.rdataclass.to_text(rdclass), + dns.rdatatype.to_text(self.rdtype), + rd.to_text(origin=origin, relativize=relativize, **kw), + extra, + ) + ) + # + # We strip off the final \n for the caller's convenience in printing + # + return s.getvalue()[:-1] + + def to_wire( + self, + name: dns.name.Name, + file: Any, + compress: Optional[dns.name.CompressType] = None, + origin: Optional[dns.name.Name] = None, + override_rdclass: Optional[dns.rdataclass.RdataClass] = None, + want_shuffle: bool = True, + ) -> int: + """Convert the rdataset to wire format. + + *name*, a ``dns.name.Name`` is the owner name to use. + + *file* is the file where the name is emitted (typically a + BytesIO file). + + *compress*, a ``dict``, is the compression table to use. If + ``None`` (the default), names will not be compressed. + + *origin* is a ``dns.name.Name`` or ``None``. If the name is + relative and origin is not ``None``, then *origin* will be appended + to it. + + *override_rdclass*, an ``int``, is used as the class instead of the + class of the rdataset. This is useful when rendering rdatasets + associated with dynamic updates. + + *want_shuffle*, a ``bool``. If ``True``, then the order of the + Rdatas within the Rdataset will be shuffled before rendering. + + Returns an ``int``, the number of records emitted. + """ + + if override_rdclass is not None: + rdclass = override_rdclass + want_shuffle = False + else: + rdclass = self.rdclass + if len(self) == 0: + name.to_wire(file, compress, origin) + file.write(struct.pack("!HHIH", self.rdtype, rdclass, 0, 0)) + return 1 + else: + l: Union[Rdataset, List[dns.rdata.Rdata]] + if want_shuffle: + l = list(self) + random.shuffle(l) + else: + l = self + for rd in l: + name.to_wire(file, compress, origin) + file.write(struct.pack("!HHI", self.rdtype, rdclass, self.ttl)) + with dns.renderer.prefixed_length(file, 2): + rd.to_wire(file, compress, origin) + return len(self) + + def match( + self, + rdclass: dns.rdataclass.RdataClass, + rdtype: dns.rdatatype.RdataType, + covers: dns.rdatatype.RdataType, + ) -> bool: + """Returns ``True`` if this rdataset matches the specified class, + type, and covers. + """ + if self.rdclass == rdclass and self.rdtype == rdtype and self.covers == covers: + return True + return False + + def processing_order(self) -> List[dns.rdata.Rdata]: + """Return rdatas in a valid processing order according to the type's + specification. For example, MX records are in preference order from + lowest to highest preferences, with items of the same preference + shuffled. + + For types that do not define a processing order, the rdatas are + simply shuffled. + """ + if len(self) == 0: + return [] + else: + return self[0]._processing_order(iter(self)) + + +@dns.immutable.immutable +class ImmutableRdataset(Rdataset): # lgtm[py/missing-equals] + """An immutable DNS rdataset.""" + + _clone_class = Rdataset + + def __init__(self, rdataset: Rdataset): + """Create an immutable rdataset from the specified rdataset.""" + + super().__init__( + rdataset.rdclass, rdataset.rdtype, rdataset.covers, rdataset.ttl + ) + self.items = dns.immutable.Dict(rdataset.items) + + def update_ttl(self, ttl): + raise TypeError("immutable") + + def add(self, rd, ttl=None): + raise TypeError("immutable") + + def union_update(self, other): + raise TypeError("immutable") + + def intersection_update(self, other): + raise TypeError("immutable") + + def update(self, other): + raise TypeError("immutable") + + def __delitem__(self, i): + raise TypeError("immutable") + + # lgtm complains about these not raising ArithmeticError, but there is + # precedent for overrides of these methods in other classes to raise + # TypeError, and it seems like the better exception. + + def __ior__(self, other): # lgtm[py/unexpected-raise-in-special-method] + raise TypeError("immutable") + + def __iand__(self, other): # lgtm[py/unexpected-raise-in-special-method] + raise TypeError("immutable") + + def __iadd__(self, other): # lgtm[py/unexpected-raise-in-special-method] + raise TypeError("immutable") + + def __isub__(self, other): # lgtm[py/unexpected-raise-in-special-method] + raise TypeError("immutable") + + def clear(self): + raise TypeError("immutable") + + def __copy__(self): + return ImmutableRdataset(super().copy()) + + def copy(self): + return ImmutableRdataset(super().copy()) + + def union(self, other): + return ImmutableRdataset(super().union(other)) + + def intersection(self, other): + return ImmutableRdataset(super().intersection(other)) + + def difference(self, other): + return ImmutableRdataset(super().difference(other)) + + def symmetric_difference(self, other): + return ImmutableRdataset(super().symmetric_difference(other)) + + +def from_text_list( + rdclass: Union[dns.rdataclass.RdataClass, str], + rdtype: Union[dns.rdatatype.RdataType, str], + ttl: int, + text_rdatas: Collection[str], + idna_codec: Optional[dns.name.IDNACodec] = None, + origin: Optional[dns.name.Name] = None, + relativize: bool = True, + relativize_to: Optional[dns.name.Name] = None, +) -> Rdataset: + """Create an rdataset with the specified class, type, and TTL, and with + the specified list of rdatas in text format. + + *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA + encoder/decoder to use; if ``None``, the default IDNA 2003 + encoder/decoder is used. + + *origin*, a ``dns.name.Name`` (or ``None``), the + origin to use for relative names. + + *relativize*, a ``bool``. If true, name will be relativized. + + *relativize_to*, a ``dns.name.Name`` (or ``None``), the origin to use + when relativizing names. If not set, the *origin* value will be used. + + Returns a ``dns.rdataset.Rdataset`` object. + """ + + rdclass = dns.rdataclass.RdataClass.make(rdclass) + rdtype = dns.rdatatype.RdataType.make(rdtype) + r = Rdataset(rdclass, rdtype) + r.update_ttl(ttl) + for t in text_rdatas: + rd = dns.rdata.from_text( + r.rdclass, r.rdtype, t, origin, relativize, relativize_to, idna_codec + ) + r.add(rd) + return r + + +def from_text( + rdclass: Union[dns.rdataclass.RdataClass, str], + rdtype: Union[dns.rdatatype.RdataType, str], + ttl: int, + *text_rdatas: Any, +) -> Rdataset: + """Create an rdataset with the specified class, type, and TTL, and with + the specified rdatas in text format. + + Returns a ``dns.rdataset.Rdataset`` object. + """ + + return from_text_list(rdclass, rdtype, ttl, cast(Collection[str], text_rdatas)) + + +def from_rdata_list(ttl: int, rdatas: Collection[dns.rdata.Rdata]) -> Rdataset: + """Create an rdataset with the specified TTL, and with + the specified list of rdata objects. + + Returns a ``dns.rdataset.Rdataset`` object. + """ + + if len(rdatas) == 0: + raise ValueError("rdata list must not be empty") + r = None + for rd in rdatas: + if r is None: + r = Rdataset(rd.rdclass, rd.rdtype) + r.update_ttl(ttl) + r.add(rd) + assert r is not None + return r + + +def from_rdata(ttl: int, *rdatas: Any) -> Rdataset: + """Create an rdataset with the specified TTL, and with + the specified rdata objects. + + Returns a ``dns.rdataset.Rdataset`` object. + """ + + return from_rdata_list(ttl, cast(Collection[dns.rdata.Rdata], rdatas)) diff --git a/.venv/lib/python3.9/site-packages/dns/rdatatype.py b/.venv/lib/python3.9/site-packages/dns/rdatatype.py new file mode 100644 index 0000000..aa9e561 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdatatype.py @@ -0,0 +1,336 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2001-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Rdata Types.""" + +from typing import Dict + +import dns.enum +import dns.exception + + +class RdataType(dns.enum.IntEnum): + """DNS Rdata Type""" + + TYPE0 = 0 + NONE = 0 + A = 1 + NS = 2 + MD = 3 + MF = 4 + CNAME = 5 + SOA = 6 + MB = 7 + MG = 8 + MR = 9 + NULL = 10 + WKS = 11 + PTR = 12 + HINFO = 13 + MINFO = 14 + MX = 15 + TXT = 16 + RP = 17 + AFSDB = 18 + X25 = 19 + ISDN = 20 + RT = 21 + NSAP = 22 + NSAP_PTR = 23 + SIG = 24 + KEY = 25 + PX = 26 + GPOS = 27 + AAAA = 28 + LOC = 29 + NXT = 30 + SRV = 33 + NAPTR = 35 + KX = 36 + CERT = 37 + A6 = 38 + DNAME = 39 + OPT = 41 + APL = 42 + DS = 43 + SSHFP = 44 + IPSECKEY = 45 + RRSIG = 46 + NSEC = 47 + DNSKEY = 48 + DHCID = 49 + NSEC3 = 50 + NSEC3PARAM = 51 + TLSA = 52 + SMIMEA = 53 + HIP = 55 + NINFO = 56 + CDS = 59 + CDNSKEY = 60 + OPENPGPKEY = 61 + CSYNC = 62 + ZONEMD = 63 + SVCB = 64 + HTTPS = 65 + SPF = 99 + UNSPEC = 103 + NID = 104 + L32 = 105 + L64 = 106 + LP = 107 + EUI48 = 108 + EUI64 = 109 + TKEY = 249 + TSIG = 250 + IXFR = 251 + AXFR = 252 + MAILB = 253 + MAILA = 254 + ANY = 255 + URI = 256 + CAA = 257 + AVC = 258 + AMTRELAY = 260 + RESINFO = 261 + WALLET = 262 + TA = 32768 + DLV = 32769 + + @classmethod + def _maximum(cls): + return 65535 + + @classmethod + def _short_name(cls): + return "type" + + @classmethod + def _prefix(cls): + return "TYPE" + + @classmethod + def _extra_from_text(cls, text): + if text.find("-") >= 0: + try: + return cls[text.replace("-", "_")] + except KeyError: # pragma: no cover + pass + return _registered_by_text.get(text) + + @classmethod + def _extra_to_text(cls, value, current_text): + if current_text is None: + return _registered_by_value.get(value) + if current_text.find("_") >= 0: + return current_text.replace("_", "-") + return current_text + + @classmethod + def _unknown_exception_class(cls): + return UnknownRdatatype + + +_registered_by_text: Dict[str, RdataType] = {} +_registered_by_value: Dict[RdataType, str] = {} + +_metatypes = {RdataType.OPT} + +_singletons = { + RdataType.SOA, + RdataType.NXT, + RdataType.DNAME, + RdataType.NSEC, + RdataType.CNAME, +} + + +class UnknownRdatatype(dns.exception.DNSException): + """DNS resource record type is unknown.""" + + +def from_text(text: str) -> RdataType: + """Convert text into a DNS rdata type value. + + The input text can be a defined DNS RR type mnemonic or + instance of the DNS generic type syntax. + + For example, "NS" and "TYPE2" will both result in a value of 2. + + Raises ``dns.rdatatype.UnknownRdatatype`` if the type is unknown. + + Raises ``ValueError`` if the rdata type value is not >= 0 and <= 65535. + + Returns a ``dns.rdatatype.RdataType``. + """ + + return RdataType.from_text(text) + + +def to_text(value: RdataType) -> str: + """Convert a DNS rdata type value to text. + + If the value has a known mnemonic, it will be used, otherwise the + DNS generic type syntax will be used. + + Raises ``ValueError`` if the rdata type value is not >= 0 and <= 65535. + + Returns a ``str``. + """ + + return RdataType.to_text(value) + + +def is_metatype(rdtype: RdataType) -> bool: + """True if the specified type is a metatype. + + *rdtype* is a ``dns.rdatatype.RdataType``. + + The currently defined metatypes are TKEY, TSIG, IXFR, AXFR, MAILA, + MAILB, ANY, and OPT. + + Returns a ``bool``. + """ + + return (256 > rdtype >= 128) or rdtype in _metatypes + + +def is_singleton(rdtype: RdataType) -> bool: + """Is the specified type a singleton type? + + Singleton types can only have a single rdata in an rdataset, or a single + RR in an RRset. + + The currently defined singleton types are CNAME, DNAME, NSEC, NXT, and + SOA. + + *rdtype* is an ``int``. + + Returns a ``bool``. + """ + + if rdtype in _singletons: + return True + return False + + +# pylint: disable=redefined-outer-name +def register_type( + rdtype: RdataType, rdtype_text: str, is_singleton: bool = False +) -> None: + """Dynamically register an rdatatype. + + *rdtype*, a ``dns.rdatatype.RdataType``, the rdatatype to register. + + *rdtype_text*, a ``str``, the textual form of the rdatatype. + + *is_singleton*, a ``bool``, indicating if the type is a singleton (i.e. + RRsets of the type can have only one member.) + """ + + _registered_by_text[rdtype_text] = rdtype + _registered_by_value[rdtype] = rdtype_text + if is_singleton: + _singletons.add(rdtype) + + +### BEGIN generated RdataType constants + +TYPE0 = RdataType.TYPE0 +NONE = RdataType.NONE +A = RdataType.A +NS = RdataType.NS +MD = RdataType.MD +MF = RdataType.MF +CNAME = RdataType.CNAME +SOA = RdataType.SOA +MB = RdataType.MB +MG = RdataType.MG +MR = RdataType.MR +NULL = RdataType.NULL +WKS = RdataType.WKS +PTR = RdataType.PTR +HINFO = RdataType.HINFO +MINFO = RdataType.MINFO +MX = RdataType.MX +TXT = RdataType.TXT +RP = RdataType.RP +AFSDB = RdataType.AFSDB +X25 = RdataType.X25 +ISDN = RdataType.ISDN +RT = RdataType.RT +NSAP = RdataType.NSAP +NSAP_PTR = RdataType.NSAP_PTR +SIG = RdataType.SIG +KEY = RdataType.KEY +PX = RdataType.PX +GPOS = RdataType.GPOS +AAAA = RdataType.AAAA +LOC = RdataType.LOC +NXT = RdataType.NXT +SRV = RdataType.SRV +NAPTR = RdataType.NAPTR +KX = RdataType.KX +CERT = RdataType.CERT +A6 = RdataType.A6 +DNAME = RdataType.DNAME +OPT = RdataType.OPT +APL = RdataType.APL +DS = RdataType.DS +SSHFP = RdataType.SSHFP +IPSECKEY = RdataType.IPSECKEY +RRSIG = RdataType.RRSIG +NSEC = RdataType.NSEC +DNSKEY = RdataType.DNSKEY +DHCID = RdataType.DHCID +NSEC3 = RdataType.NSEC3 +NSEC3PARAM = RdataType.NSEC3PARAM +TLSA = RdataType.TLSA +SMIMEA = RdataType.SMIMEA +HIP = RdataType.HIP +NINFO = RdataType.NINFO +CDS = RdataType.CDS +CDNSKEY = RdataType.CDNSKEY +OPENPGPKEY = RdataType.OPENPGPKEY +CSYNC = RdataType.CSYNC +ZONEMD = RdataType.ZONEMD +SVCB = RdataType.SVCB +HTTPS = RdataType.HTTPS +SPF = RdataType.SPF +UNSPEC = RdataType.UNSPEC +NID = RdataType.NID +L32 = RdataType.L32 +L64 = RdataType.L64 +LP = RdataType.LP +EUI48 = RdataType.EUI48 +EUI64 = RdataType.EUI64 +TKEY = RdataType.TKEY +TSIG = RdataType.TSIG +IXFR = RdataType.IXFR +AXFR = RdataType.AXFR +MAILB = RdataType.MAILB +MAILA = RdataType.MAILA +ANY = RdataType.ANY +URI = RdataType.URI +CAA = RdataType.CAA +AVC = RdataType.AVC +AMTRELAY = RdataType.AMTRELAY +RESINFO = RdataType.RESINFO +WALLET = RdataType.WALLET +TA = RdataType.TA +DLV = RdataType.DLV + +### END generated RdataType constants diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/AFSDB.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/AFSDB.py new file mode 100644 index 0000000..06a3b97 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/AFSDB.py @@ -0,0 +1,45 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.immutable +import dns.rdtypes.mxbase + + +@dns.immutable.immutable +class AFSDB(dns.rdtypes.mxbase.UncompressedDowncasingMX): + """AFSDB record""" + + # Use the property mechanism to make "subtype" an alias for the + # "preference" attribute, and "hostname" an alias for the "exchange" + # attribute. + # + # This lets us inherit the UncompressedMX implementation but lets + # the caller use appropriate attribute names for the rdata type. + # + # We probably lose some performance vs. a cut-and-paste + # implementation, but this way we don't copy code, and that's + # good. + + @property + def subtype(self): + "the AFSDB subtype" + return self.preference + + @property + def hostname(self): + "the AFSDB hostname" + return self.exchange diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/AMTRELAY.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/AMTRELAY.py new file mode 100644 index 0000000..ed2b072 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/AMTRELAY.py @@ -0,0 +1,91 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import struct + +import dns.exception +import dns.immutable +import dns.rdtypes.util + + +class Relay(dns.rdtypes.util.Gateway): + name = "AMTRELAY relay" + + @property + def relay(self): + return self.gateway + + +@dns.immutable.immutable +class AMTRELAY(dns.rdata.Rdata): + """AMTRELAY record""" + + # see: RFC 8777 + + __slots__ = ["precedence", "discovery_optional", "relay_type", "relay"] + + def __init__( + self, rdclass, rdtype, precedence, discovery_optional, relay_type, relay + ): + super().__init__(rdclass, rdtype) + relay = Relay(relay_type, relay) + self.precedence = self._as_uint8(precedence) + self.discovery_optional = self._as_bool(discovery_optional) + self.relay_type = relay.type + self.relay = relay.relay + + def to_text(self, origin=None, relativize=True, **kw): + relay = Relay(self.relay_type, self.relay).to_text(origin, relativize) + return "%d %d %d %s" % ( + self.precedence, + self.discovery_optional, + self.relay_type, + relay, + ) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + precedence = tok.get_uint8() + discovery_optional = tok.get_uint8() + if discovery_optional > 1: + raise dns.exception.SyntaxError("expecting 0 or 1") + discovery_optional = bool(discovery_optional) + relay_type = tok.get_uint8() + if relay_type > 0x7F: + raise dns.exception.SyntaxError("expecting an integer <= 127") + relay = Relay.from_text(relay_type, tok, origin, relativize, relativize_to) + return cls( + rdclass, rdtype, precedence, discovery_optional, relay_type, relay.relay + ) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + relay_type = self.relay_type | (self.discovery_optional << 7) + header = struct.pack("!BB", self.precedence, relay_type) + file.write(header) + Relay(self.relay_type, self.relay).to_wire(file, compress, origin, canonicalize) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + (precedence, relay_type) = parser.get_struct("!BB") + discovery_optional = bool(relay_type >> 7) + relay_type &= 0x7F + relay = Relay.from_wire_parser(relay_type, parser, origin) + return cls( + rdclass, rdtype, precedence, discovery_optional, relay_type, relay.relay + ) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/AVC.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/AVC.py new file mode 100644 index 0000000..a27ae2d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/AVC.py @@ -0,0 +1,26 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2016 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.immutable +import dns.rdtypes.txtbase + + +@dns.immutable.immutable +class AVC(dns.rdtypes.txtbase.TXTBase): + """AVC record""" + + # See: IANA dns parameters for AVC diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CAA.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CAA.py new file mode 100644 index 0000000..2e6a7e7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CAA.py @@ -0,0 +1,71 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import struct + +import dns.exception +import dns.immutable +import dns.rdata +import dns.tokenizer + + +@dns.immutable.immutable +class CAA(dns.rdata.Rdata): + """CAA (Certification Authority Authorization) record""" + + # see: RFC 6844 + + __slots__ = ["flags", "tag", "value"] + + def __init__(self, rdclass, rdtype, flags, tag, value): + super().__init__(rdclass, rdtype) + self.flags = self._as_uint8(flags) + self.tag = self._as_bytes(tag, True, 255) + if not tag.isalnum(): + raise ValueError("tag is not alphanumeric") + self.value = self._as_bytes(value) + + def to_text(self, origin=None, relativize=True, **kw): + return '%u %s "%s"' % ( + self.flags, + dns.rdata._escapify(self.tag), + dns.rdata._escapify(self.value), + ) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + flags = tok.get_uint8() + tag = tok.get_string().encode() + value = tok.get_string().encode() + return cls(rdclass, rdtype, flags, tag, value) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + file.write(struct.pack("!B", self.flags)) + l = len(self.tag) + assert l < 256 + file.write(struct.pack("!B", l)) + file.write(self.tag) + file.write(self.value) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + flags = parser.get_uint8() + tag = parser.get_counted_bytes() + value = parser.get_remaining() + return cls(rdclass, rdtype, flags, tag, value) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CDNSKEY.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CDNSKEY.py new file mode 100644 index 0000000..b613409 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CDNSKEY.py @@ -0,0 +1,33 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.immutable +import dns.rdtypes.dnskeybase # lgtm[py/import-and-import-from] + +# pylint: disable=unused-import +from dns.rdtypes.dnskeybase import ( # noqa: F401 lgtm[py/unused-import] + REVOKE, + SEP, + ZONE, +) + +# pylint: enable=unused-import + + +@dns.immutable.immutable +class CDNSKEY(dns.rdtypes.dnskeybase.DNSKEYBase): + """CDNSKEY record""" diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CDS.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CDS.py new file mode 100644 index 0000000..8312b97 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CDS.py @@ -0,0 +1,29 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.immutable +import dns.rdtypes.dsbase + + +@dns.immutable.immutable +class CDS(dns.rdtypes.dsbase.DSBase): + """CDS record""" + + _digest_length_by_type = { + **dns.rdtypes.dsbase.DSBase._digest_length_by_type, + 0: 1, # delete, RFC 8078 Sec. 4 (including Errata ID 5049) + } diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CERT.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CERT.py new file mode 100644 index 0000000..f369cc8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CERT.py @@ -0,0 +1,116 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import base64 +import struct + +import dns.dnssectypes +import dns.exception +import dns.immutable +import dns.rdata +import dns.tokenizer + +_ctype_by_value = { + 1: "PKIX", + 2: "SPKI", + 3: "PGP", + 4: "IPKIX", + 5: "ISPKI", + 6: "IPGP", + 7: "ACPKIX", + 8: "IACPKIX", + 253: "URI", + 254: "OID", +} + +_ctype_by_name = { + "PKIX": 1, + "SPKI": 2, + "PGP": 3, + "IPKIX": 4, + "ISPKI": 5, + "IPGP": 6, + "ACPKIX": 7, + "IACPKIX": 8, + "URI": 253, + "OID": 254, +} + + +def _ctype_from_text(what): + v = _ctype_by_name.get(what) + if v is not None: + return v + return int(what) + + +def _ctype_to_text(what): + v = _ctype_by_value.get(what) + if v is not None: + return v + return str(what) + + +@dns.immutable.immutable +class CERT(dns.rdata.Rdata): + """CERT record""" + + # see RFC 4398 + + __slots__ = ["certificate_type", "key_tag", "algorithm", "certificate"] + + def __init__( + self, rdclass, rdtype, certificate_type, key_tag, algorithm, certificate + ): + super().__init__(rdclass, rdtype) + self.certificate_type = self._as_uint16(certificate_type) + self.key_tag = self._as_uint16(key_tag) + self.algorithm = self._as_uint8(algorithm) + self.certificate = self._as_bytes(certificate) + + def to_text(self, origin=None, relativize=True, **kw): + certificate_type = _ctype_to_text(self.certificate_type) + return "%s %d %s %s" % ( + certificate_type, + self.key_tag, + dns.dnssectypes.Algorithm.to_text(self.algorithm), + dns.rdata._base64ify(self.certificate, **kw), + ) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + certificate_type = _ctype_from_text(tok.get_string()) + key_tag = tok.get_uint16() + algorithm = dns.dnssectypes.Algorithm.from_text(tok.get_string()) + b64 = tok.concatenate_remaining_identifiers().encode() + certificate = base64.b64decode(b64) + return cls(rdclass, rdtype, certificate_type, key_tag, algorithm, certificate) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + prefix = struct.pack( + "!HHB", self.certificate_type, self.key_tag, self.algorithm + ) + file.write(prefix) + file.write(self.certificate) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + (certificate_type, key_tag, algorithm) = parser.get_struct("!HHB") + certificate = parser.get_remaining() + return cls(rdclass, rdtype, certificate_type, key_tag, algorithm, certificate) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CNAME.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CNAME.py new file mode 100644 index 0000000..665e407 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CNAME.py @@ -0,0 +1,28 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.immutable +import dns.rdtypes.nsbase + + +@dns.immutable.immutable +class CNAME(dns.rdtypes.nsbase.NSBase): + """CNAME record + + Note: although CNAME is officially a singleton type, dnspython allows + non-singleton CNAME rdatasets because such sets have been commonly + used by BIND and other nameservers for load balancing.""" diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CSYNC.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CSYNC.py new file mode 100644 index 0000000..2f972f6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CSYNC.py @@ -0,0 +1,68 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2004-2007, 2009-2011, 2016 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import struct + +import dns.exception +import dns.immutable +import dns.name +import dns.rdata +import dns.rdatatype +import dns.rdtypes.util + + +@dns.immutable.immutable +class Bitmap(dns.rdtypes.util.Bitmap): + type_name = "CSYNC" + + +@dns.immutable.immutable +class CSYNC(dns.rdata.Rdata): + """CSYNC record""" + + __slots__ = ["serial", "flags", "windows"] + + def __init__(self, rdclass, rdtype, serial, flags, windows): + super().__init__(rdclass, rdtype) + self.serial = self._as_uint32(serial) + self.flags = self._as_uint16(flags) + if not isinstance(windows, Bitmap): + windows = Bitmap(windows) + self.windows = tuple(windows.windows) + + def to_text(self, origin=None, relativize=True, **kw): + text = Bitmap(self.windows).to_text() + return "%d %d%s" % (self.serial, self.flags, text) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + serial = tok.get_uint32() + flags = tok.get_uint16() + bitmap = Bitmap.from_text(tok) + return cls(rdclass, rdtype, serial, flags, bitmap) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + file.write(struct.pack("!IH", self.serial, self.flags)) + Bitmap(self.windows).to_wire(file) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + (serial, flags) = parser.get_struct("!IH") + bitmap = Bitmap.from_wire_parser(parser) + return cls(rdclass, rdtype, serial, flags, bitmap) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/DLV.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/DLV.py new file mode 100644 index 0000000..6c134f1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/DLV.py @@ -0,0 +1,24 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.immutable +import dns.rdtypes.dsbase + + +@dns.immutable.immutable +class DLV(dns.rdtypes.dsbase.DSBase): + """DLV record""" diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/DNAME.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/DNAME.py new file mode 100644 index 0000000..bbf9186 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/DNAME.py @@ -0,0 +1,27 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.immutable +import dns.rdtypes.nsbase + + +@dns.immutable.immutable +class DNAME(dns.rdtypes.nsbase.UncompressedNS): + """DNAME record""" + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + self.target.to_wire(file, None, origin, canonicalize) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/DNSKEY.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/DNSKEY.py new file mode 100644 index 0000000..6d961a9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/DNSKEY.py @@ -0,0 +1,33 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.immutable +import dns.rdtypes.dnskeybase # lgtm[py/import-and-import-from] + +# pylint: disable=unused-import +from dns.rdtypes.dnskeybase import ( # noqa: F401 lgtm[py/unused-import] + REVOKE, + SEP, + ZONE, +) + +# pylint: enable=unused-import + + +@dns.immutable.immutable +class DNSKEY(dns.rdtypes.dnskeybase.DNSKEYBase): + """DNSKEY record""" diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/DS.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/DS.py new file mode 100644 index 0000000..58b3108 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/DS.py @@ -0,0 +1,24 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.immutable +import dns.rdtypes.dsbase + + +@dns.immutable.immutable +class DS(dns.rdtypes.dsbase.DSBase): + """DS record""" diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/EUI48.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/EUI48.py new file mode 100644 index 0000000..c843be5 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/EUI48.py @@ -0,0 +1,30 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2015 Red Hat, Inc. +# Author: Petr Spacek +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.immutable +import dns.rdtypes.euibase + + +@dns.immutable.immutable +class EUI48(dns.rdtypes.euibase.EUIBase): + """EUI48 record""" + + # see: rfc7043.txt + + byte_len = 6 # 0123456789ab (in hex) + text_len = byte_len * 3 - 1 # 01-23-45-67-89-ab diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/EUI64.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/EUI64.py new file mode 100644 index 0000000..f6d7e25 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/EUI64.py @@ -0,0 +1,30 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2015 Red Hat, Inc. +# Author: Petr Spacek +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.immutable +import dns.rdtypes.euibase + + +@dns.immutable.immutable +class EUI64(dns.rdtypes.euibase.EUIBase): + """EUI64 record""" + + # see: rfc7043.txt + + byte_len = 8 # 0123456789abcdef (in hex) + text_len = byte_len * 3 - 1 # 01-23-45-67-89-ab-cd-ef diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/GPOS.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/GPOS.py new file mode 100644 index 0000000..d79f4a0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/GPOS.py @@ -0,0 +1,126 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import struct + +import dns.exception +import dns.immutable +import dns.rdata +import dns.tokenizer + + +def _validate_float_string(what): + if len(what) == 0: + raise dns.exception.FormError + if what[0] == b"-"[0] or what[0] == b"+"[0]: + what = what[1:] + if what.isdigit(): + return + try: + (left, right) = what.split(b".") + except ValueError: + raise dns.exception.FormError + if left == b"" and right == b"": + raise dns.exception.FormError + if not left == b"" and not left.decode().isdigit(): + raise dns.exception.FormError + if not right == b"" and not right.decode().isdigit(): + raise dns.exception.FormError + + +@dns.immutable.immutable +class GPOS(dns.rdata.Rdata): + """GPOS record""" + + # see: RFC 1712 + + __slots__ = ["latitude", "longitude", "altitude"] + + def __init__(self, rdclass, rdtype, latitude, longitude, altitude): + super().__init__(rdclass, rdtype) + if isinstance(latitude, float) or isinstance(latitude, int): + latitude = str(latitude) + if isinstance(longitude, float) or isinstance(longitude, int): + longitude = str(longitude) + if isinstance(altitude, float) or isinstance(altitude, int): + altitude = str(altitude) + latitude = self._as_bytes(latitude, True, 255) + longitude = self._as_bytes(longitude, True, 255) + altitude = self._as_bytes(altitude, True, 255) + _validate_float_string(latitude) + _validate_float_string(longitude) + _validate_float_string(altitude) + self.latitude = latitude + self.longitude = longitude + self.altitude = altitude + flat = self.float_latitude + if flat < -90.0 or flat > 90.0: + raise dns.exception.FormError("bad latitude") + flong = self.float_longitude + if flong < -180.0 or flong > 180.0: + raise dns.exception.FormError("bad longitude") + + def to_text(self, origin=None, relativize=True, **kw): + return ( + f"{self.latitude.decode()} {self.longitude.decode()} " + f"{self.altitude.decode()}" + ) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + latitude = tok.get_string() + longitude = tok.get_string() + altitude = tok.get_string() + return cls(rdclass, rdtype, latitude, longitude, altitude) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + l = len(self.latitude) + assert l < 256 + file.write(struct.pack("!B", l)) + file.write(self.latitude) + l = len(self.longitude) + assert l < 256 + file.write(struct.pack("!B", l)) + file.write(self.longitude) + l = len(self.altitude) + assert l < 256 + file.write(struct.pack("!B", l)) + file.write(self.altitude) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + latitude = parser.get_counted_bytes() + longitude = parser.get_counted_bytes() + altitude = parser.get_counted_bytes() + return cls(rdclass, rdtype, latitude, longitude, altitude) + + @property + def float_latitude(self): + "latitude as a floating point value" + return float(self.latitude) + + @property + def float_longitude(self): + "longitude as a floating point value" + return float(self.longitude) + + @property + def float_altitude(self): + "altitude as a floating point value" + return float(self.altitude) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/HINFO.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/HINFO.py new file mode 100644 index 0000000..06ad348 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/HINFO.py @@ -0,0 +1,64 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import struct + +import dns.exception +import dns.immutable +import dns.rdata +import dns.tokenizer + + +@dns.immutable.immutable +class HINFO(dns.rdata.Rdata): + """HINFO record""" + + # see: RFC 1035 + + __slots__ = ["cpu", "os"] + + def __init__(self, rdclass, rdtype, cpu, os): + super().__init__(rdclass, rdtype) + self.cpu = self._as_bytes(cpu, True, 255) + self.os = self._as_bytes(os, True, 255) + + def to_text(self, origin=None, relativize=True, **kw): + return f'"{dns.rdata._escapify(self.cpu)}" "{dns.rdata._escapify(self.os)}"' + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + cpu = tok.get_string(max_length=255) + os = tok.get_string(max_length=255) + return cls(rdclass, rdtype, cpu, os) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + l = len(self.cpu) + assert l < 256 + file.write(struct.pack("!B", l)) + file.write(self.cpu) + l = len(self.os) + assert l < 256 + file.write(struct.pack("!B", l)) + file.write(self.os) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + cpu = parser.get_counted_bytes() + os = parser.get_counted_bytes() + return cls(rdclass, rdtype, cpu, os) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/HIP.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/HIP.py new file mode 100644 index 0000000..f3157da --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/HIP.py @@ -0,0 +1,85 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2010, 2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import base64 +import binascii +import struct + +import dns.exception +import dns.immutable +import dns.rdata +import dns.rdatatype + + +@dns.immutable.immutable +class HIP(dns.rdata.Rdata): + """HIP record""" + + # see: RFC 5205 + + __slots__ = ["hit", "algorithm", "key", "servers"] + + def __init__(self, rdclass, rdtype, hit, algorithm, key, servers): + super().__init__(rdclass, rdtype) + self.hit = self._as_bytes(hit, True, 255) + self.algorithm = self._as_uint8(algorithm) + self.key = self._as_bytes(key, True) + self.servers = self._as_tuple(servers, self._as_name) + + def to_text(self, origin=None, relativize=True, **kw): + hit = binascii.hexlify(self.hit).decode() + key = base64.b64encode(self.key).replace(b"\n", b"").decode() + text = "" + servers = [] + for server in self.servers: + servers.append(server.choose_relativity(origin, relativize)) + if len(servers) > 0: + text += " " + " ".join(x.to_unicode() for x in servers) + return "%u %s %s%s" % (self.algorithm, hit, key, text) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + algorithm = tok.get_uint8() + hit = binascii.unhexlify(tok.get_string().encode()) + key = base64.b64decode(tok.get_string().encode()) + servers = [] + for token in tok.get_remaining(): + server = tok.as_name(token, origin, relativize, relativize_to) + servers.append(server) + return cls(rdclass, rdtype, hit, algorithm, key, servers) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + lh = len(self.hit) + lk = len(self.key) + file.write(struct.pack("!BBH", lh, self.algorithm, lk)) + file.write(self.hit) + file.write(self.key) + for server in self.servers: + server.to_wire(file, None, origin, False) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + (lh, algorithm, lk) = parser.get_struct("!BBH") + hit = parser.get_bytes(lh) + key = parser.get_bytes(lk) + servers = [] + while parser.remaining() > 0: + server = parser.get_name(origin) + servers.append(server) + return cls(rdclass, rdtype, hit, algorithm, key, servers) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/ISDN.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/ISDN.py new file mode 100644 index 0000000..6428a0a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/ISDN.py @@ -0,0 +1,78 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import struct + +import dns.exception +import dns.immutable +import dns.rdata +import dns.tokenizer + + +@dns.immutable.immutable +class ISDN(dns.rdata.Rdata): + """ISDN record""" + + # see: RFC 1183 + + __slots__ = ["address", "subaddress"] + + def __init__(self, rdclass, rdtype, address, subaddress): + super().__init__(rdclass, rdtype) + self.address = self._as_bytes(address, True, 255) + self.subaddress = self._as_bytes(subaddress, True, 255) + + def to_text(self, origin=None, relativize=True, **kw): + if self.subaddress: + return ( + f'"{dns.rdata._escapify(self.address)}" ' + f'"{dns.rdata._escapify(self.subaddress)}"' + ) + else: + return f'"{dns.rdata._escapify(self.address)}"' + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + address = tok.get_string() + tokens = tok.get_remaining(max_tokens=1) + if len(tokens) >= 1: + subaddress = tokens[0].unescape().value + else: + subaddress = "" + return cls(rdclass, rdtype, address, subaddress) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + l = len(self.address) + assert l < 256 + file.write(struct.pack("!B", l)) + file.write(self.address) + l = len(self.subaddress) + if l > 0: + assert l < 256 + file.write(struct.pack("!B", l)) + file.write(self.subaddress) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + address = parser.get_counted_bytes() + if parser.remaining() > 0: + subaddress = parser.get_counted_bytes() + else: + subaddress = b"" + return cls(rdclass, rdtype, address, subaddress) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/L32.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/L32.py new file mode 100644 index 0000000..09804c2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/L32.py @@ -0,0 +1,41 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import struct + +import dns.immutable +import dns.rdata + + +@dns.immutable.immutable +class L32(dns.rdata.Rdata): + """L32 record""" + + # see: rfc6742.txt + + __slots__ = ["preference", "locator32"] + + def __init__(self, rdclass, rdtype, preference, locator32): + super().__init__(rdclass, rdtype) + self.preference = self._as_uint16(preference) + self.locator32 = self._as_ipv4_address(locator32) + + def to_text(self, origin=None, relativize=True, **kw): + return f"{self.preference} {self.locator32}" + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + preference = tok.get_uint16() + nodeid = tok.get_identifier() + return cls(rdclass, rdtype, preference, nodeid) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + file.write(struct.pack("!H", self.preference)) + file.write(dns.ipv4.inet_aton(self.locator32)) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + preference = parser.get_uint16() + locator32 = parser.get_remaining() + return cls(rdclass, rdtype, preference, locator32) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/L64.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/L64.py new file mode 100644 index 0000000..fb76808 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/L64.py @@ -0,0 +1,47 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import struct + +import dns.immutable +import dns.rdtypes.util + + +@dns.immutable.immutable +class L64(dns.rdata.Rdata): + """L64 record""" + + # see: rfc6742.txt + + __slots__ = ["preference", "locator64"] + + def __init__(self, rdclass, rdtype, preference, locator64): + super().__init__(rdclass, rdtype) + self.preference = self._as_uint16(preference) + if isinstance(locator64, bytes): + if len(locator64) != 8: + raise ValueError("invalid locator64") + self.locator64 = dns.rdata._hexify(locator64, 4, b":") + else: + dns.rdtypes.util.parse_formatted_hex(locator64, 4, 4, ":") + self.locator64 = locator64 + + def to_text(self, origin=None, relativize=True, **kw): + return f"{self.preference} {self.locator64}" + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + preference = tok.get_uint16() + locator64 = tok.get_identifier() + return cls(rdclass, rdtype, preference, locator64) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + file.write(struct.pack("!H", self.preference)) + file.write(dns.rdtypes.util.parse_formatted_hex(self.locator64, 4, 4, ":")) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + preference = parser.get_uint16() + locator64 = parser.get_remaining() + return cls(rdclass, rdtype, preference, locator64) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/LOC.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/LOC.py new file mode 100644 index 0000000..1153cf0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/LOC.py @@ -0,0 +1,353 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import struct + +import dns.exception +import dns.immutable +import dns.rdata + +_pows = tuple(10**i for i in range(0, 11)) + +# default values are in centimeters +_default_size = 100.0 +_default_hprec = 1000000.0 +_default_vprec = 1000.0 + +# for use by from_wire() +_MAX_LATITUDE = 0x80000000 + 90 * 3600000 +_MIN_LATITUDE = 0x80000000 - 90 * 3600000 +_MAX_LONGITUDE = 0x80000000 + 180 * 3600000 +_MIN_LONGITUDE = 0x80000000 - 180 * 3600000 + + +def _exponent_of(what, desc): + if what == 0: + return 0 + exp = None + for i, pow in enumerate(_pows): + if what < pow: + exp = i - 1 + break + if exp is None or exp < 0: + raise dns.exception.SyntaxError(f"{desc} value out of bounds") + return exp + + +def _float_to_tuple(what): + if what < 0: + sign = -1 + what *= -1 + else: + sign = 1 + what = round(what * 3600000) + degrees = int(what // 3600000) + what -= degrees * 3600000 + minutes = int(what // 60000) + what -= minutes * 60000 + seconds = int(what // 1000) + what -= int(seconds * 1000) + what = int(what) + return (degrees, minutes, seconds, what, sign) + + +def _tuple_to_float(what): + value = float(what[0]) + value += float(what[1]) / 60.0 + value += float(what[2]) / 3600.0 + value += float(what[3]) / 3600000.0 + return float(what[4]) * value + + +def _encode_size(what, desc): + what = int(what) + exponent = _exponent_of(what, desc) & 0xF + base = what // pow(10, exponent) & 0xF + return base * 16 + exponent + + +def _decode_size(what, desc): + exponent = what & 0x0F + if exponent > 9: + raise dns.exception.FormError(f"bad {desc} exponent") + base = (what & 0xF0) >> 4 + if base > 9: + raise dns.exception.FormError(f"bad {desc} base") + return base * pow(10, exponent) + + +def _check_coordinate_list(value, low, high): + if value[0] < low or value[0] > high: + raise ValueError(f"not in range [{low}, {high}]") + if value[1] < 0 or value[1] > 59: + raise ValueError("bad minutes value") + if value[2] < 0 or value[2] > 59: + raise ValueError("bad seconds value") + if value[3] < 0 or value[3] > 999: + raise ValueError("bad milliseconds value") + if value[4] != 1 and value[4] != -1: + raise ValueError("bad hemisphere value") + + +@dns.immutable.immutable +class LOC(dns.rdata.Rdata): + """LOC record""" + + # see: RFC 1876 + + __slots__ = [ + "latitude", + "longitude", + "altitude", + "size", + "horizontal_precision", + "vertical_precision", + ] + + def __init__( + self, + rdclass, + rdtype, + latitude, + longitude, + altitude, + size=_default_size, + hprec=_default_hprec, + vprec=_default_vprec, + ): + """Initialize a LOC record instance. + + The parameters I{latitude} and I{longitude} may be either a 4-tuple + of integers specifying (degrees, minutes, seconds, milliseconds), + or they may be floating point values specifying the number of + degrees. The other parameters are floats. Size, horizontal precision, + and vertical precision are specified in centimeters.""" + + super().__init__(rdclass, rdtype) + if isinstance(latitude, int): + latitude = float(latitude) + if isinstance(latitude, float): + latitude = _float_to_tuple(latitude) + _check_coordinate_list(latitude, -90, 90) + self.latitude = tuple(latitude) + if isinstance(longitude, int): + longitude = float(longitude) + if isinstance(longitude, float): + longitude = _float_to_tuple(longitude) + _check_coordinate_list(longitude, -180, 180) + self.longitude = tuple(longitude) + self.altitude = float(altitude) + self.size = float(size) + self.horizontal_precision = float(hprec) + self.vertical_precision = float(vprec) + + def to_text(self, origin=None, relativize=True, **kw): + if self.latitude[4] > 0: + lat_hemisphere = "N" + else: + lat_hemisphere = "S" + if self.longitude[4] > 0: + long_hemisphere = "E" + else: + long_hemisphere = "W" + text = "%d %d %d.%03d %s %d %d %d.%03d %s %0.2fm" % ( + self.latitude[0], + self.latitude[1], + self.latitude[2], + self.latitude[3], + lat_hemisphere, + self.longitude[0], + self.longitude[1], + self.longitude[2], + self.longitude[3], + long_hemisphere, + self.altitude / 100.0, + ) + + # do not print default values + if ( + self.size != _default_size + or self.horizontal_precision != _default_hprec + or self.vertical_precision != _default_vprec + ): + text += ( + f" {self.size / 100.0:0.2f}m {self.horizontal_precision / 100.0:0.2f}m" + f" {self.vertical_precision / 100.0:0.2f}m" + ) + return text + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + latitude = [0, 0, 0, 0, 1] + longitude = [0, 0, 0, 0, 1] + size = _default_size + hprec = _default_hprec + vprec = _default_vprec + + latitude[0] = tok.get_int() + t = tok.get_string() + if t.isdigit(): + latitude[1] = int(t) + t = tok.get_string() + if "." in t: + (seconds, milliseconds) = t.split(".") + if not seconds.isdigit(): + raise dns.exception.SyntaxError("bad latitude seconds value") + latitude[2] = int(seconds) + l = len(milliseconds) + if l == 0 or l > 3 or not milliseconds.isdigit(): + raise dns.exception.SyntaxError("bad latitude milliseconds value") + if l == 1: + m = 100 + elif l == 2: + m = 10 + else: + m = 1 + latitude[3] = m * int(milliseconds) + t = tok.get_string() + elif t.isdigit(): + latitude[2] = int(t) + t = tok.get_string() + if t == "S": + latitude[4] = -1 + elif t != "N": + raise dns.exception.SyntaxError("bad latitude hemisphere value") + + longitude[0] = tok.get_int() + t = tok.get_string() + if t.isdigit(): + longitude[1] = int(t) + t = tok.get_string() + if "." in t: + (seconds, milliseconds) = t.split(".") + if not seconds.isdigit(): + raise dns.exception.SyntaxError("bad longitude seconds value") + longitude[2] = int(seconds) + l = len(milliseconds) + if l == 0 or l > 3 or not milliseconds.isdigit(): + raise dns.exception.SyntaxError("bad longitude milliseconds value") + if l == 1: + m = 100 + elif l == 2: + m = 10 + else: + m = 1 + longitude[3] = m * int(milliseconds) + t = tok.get_string() + elif t.isdigit(): + longitude[2] = int(t) + t = tok.get_string() + if t == "W": + longitude[4] = -1 + elif t != "E": + raise dns.exception.SyntaxError("bad longitude hemisphere value") + + t = tok.get_string() + if t[-1] == "m": + t = t[0:-1] + altitude = float(t) * 100.0 # m -> cm + + tokens = tok.get_remaining(max_tokens=3) + if len(tokens) >= 1: + value = tokens[0].unescape().value + if value[-1] == "m": + value = value[0:-1] + size = float(value) * 100.0 # m -> cm + if len(tokens) >= 2: + value = tokens[1].unescape().value + if value[-1] == "m": + value = value[0:-1] + hprec = float(value) * 100.0 # m -> cm + if len(tokens) >= 3: + value = tokens[2].unescape().value + if value[-1] == "m": + value = value[0:-1] + vprec = float(value) * 100.0 # m -> cm + + # Try encoding these now so we raise if they are bad + _encode_size(size, "size") + _encode_size(hprec, "horizontal precision") + _encode_size(vprec, "vertical precision") + + return cls(rdclass, rdtype, latitude, longitude, altitude, size, hprec, vprec) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + milliseconds = ( + self.latitude[0] * 3600000 + + self.latitude[1] * 60000 + + self.latitude[2] * 1000 + + self.latitude[3] + ) * self.latitude[4] + latitude = 0x80000000 + milliseconds + milliseconds = ( + self.longitude[0] * 3600000 + + self.longitude[1] * 60000 + + self.longitude[2] * 1000 + + self.longitude[3] + ) * self.longitude[4] + longitude = 0x80000000 + milliseconds + altitude = int(self.altitude) + 10000000 + size = _encode_size(self.size, "size") + hprec = _encode_size(self.horizontal_precision, "horizontal precision") + vprec = _encode_size(self.vertical_precision, "vertical precision") + wire = struct.pack( + "!BBBBIII", 0, size, hprec, vprec, latitude, longitude, altitude + ) + file.write(wire) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + ( + version, + size, + hprec, + vprec, + latitude, + longitude, + altitude, + ) = parser.get_struct("!BBBBIII") + if version != 0: + raise dns.exception.FormError("LOC version not zero") + if latitude < _MIN_LATITUDE or latitude > _MAX_LATITUDE: + raise dns.exception.FormError("bad latitude") + if latitude > 0x80000000: + latitude = (latitude - 0x80000000) / 3600000 + else: + latitude = -1 * (0x80000000 - latitude) / 3600000 + if longitude < _MIN_LONGITUDE or longitude > _MAX_LONGITUDE: + raise dns.exception.FormError("bad longitude") + if longitude > 0x80000000: + longitude = (longitude - 0x80000000) / 3600000 + else: + longitude = -1 * (0x80000000 - longitude) / 3600000 + altitude = float(altitude) - 10000000.0 + size = _decode_size(size, "size") + hprec = _decode_size(hprec, "horizontal precision") + vprec = _decode_size(vprec, "vertical precision") + return cls(rdclass, rdtype, latitude, longitude, altitude, size, hprec, vprec) + + @property + def float_latitude(self): + "latitude as a floating point value" + return _tuple_to_float(self.latitude) + + @property + def float_longitude(self): + "longitude as a floating point value" + return _tuple_to_float(self.longitude) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/LP.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/LP.py new file mode 100644 index 0000000..312663f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/LP.py @@ -0,0 +1,42 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import struct + +import dns.immutable +import dns.rdata + + +@dns.immutable.immutable +class LP(dns.rdata.Rdata): + """LP record""" + + # see: rfc6742.txt + + __slots__ = ["preference", "fqdn"] + + def __init__(self, rdclass, rdtype, preference, fqdn): + super().__init__(rdclass, rdtype) + self.preference = self._as_uint16(preference) + self.fqdn = self._as_name(fqdn) + + def to_text(self, origin=None, relativize=True, **kw): + fqdn = self.fqdn.choose_relativity(origin, relativize) + return "%d %s" % (self.preference, fqdn) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + preference = tok.get_uint16() + fqdn = tok.get_name(origin, relativize, relativize_to) + return cls(rdclass, rdtype, preference, fqdn) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + file.write(struct.pack("!H", self.preference)) + self.fqdn.to_wire(file, compress, origin, canonicalize) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + preference = parser.get_uint16() + fqdn = parser.get_name(origin) + return cls(rdclass, rdtype, preference, fqdn) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/MX.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/MX.py new file mode 100644 index 0000000..0c300c5 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/MX.py @@ -0,0 +1,24 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.immutable +import dns.rdtypes.mxbase + + +@dns.immutable.immutable +class MX(dns.rdtypes.mxbase.MXBase): + """MX record""" diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NID.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NID.py new file mode 100644 index 0000000..2f64917 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NID.py @@ -0,0 +1,47 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import struct + +import dns.immutable +import dns.rdtypes.util + + +@dns.immutable.immutable +class NID(dns.rdata.Rdata): + """NID record""" + + # see: rfc6742.txt + + __slots__ = ["preference", "nodeid"] + + def __init__(self, rdclass, rdtype, preference, nodeid): + super().__init__(rdclass, rdtype) + self.preference = self._as_uint16(preference) + if isinstance(nodeid, bytes): + if len(nodeid) != 8: + raise ValueError("invalid nodeid") + self.nodeid = dns.rdata._hexify(nodeid, 4, b":") + else: + dns.rdtypes.util.parse_formatted_hex(nodeid, 4, 4, ":") + self.nodeid = nodeid + + def to_text(self, origin=None, relativize=True, **kw): + return f"{self.preference} {self.nodeid}" + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + preference = tok.get_uint16() + nodeid = tok.get_identifier() + return cls(rdclass, rdtype, preference, nodeid) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + file.write(struct.pack("!H", self.preference)) + file.write(dns.rdtypes.util.parse_formatted_hex(self.nodeid, 4, 4, ":")) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + preference = parser.get_uint16() + nodeid = parser.get_remaining() + return cls(rdclass, rdtype, preference, nodeid) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NINFO.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NINFO.py new file mode 100644 index 0000000..b177bdd --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NINFO.py @@ -0,0 +1,26 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.immutable +import dns.rdtypes.txtbase + + +@dns.immutable.immutable +class NINFO(dns.rdtypes.txtbase.TXTBase): + """NINFO record""" + + # see: draft-reid-dnsext-zs-01 diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NS.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NS.py new file mode 100644 index 0000000..c3f34ce --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NS.py @@ -0,0 +1,24 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.immutable +import dns.rdtypes.nsbase + + +@dns.immutable.immutable +class NS(dns.rdtypes.nsbase.NSBase): + """NS record""" diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NSEC.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NSEC.py new file mode 100644 index 0000000..3c78b72 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NSEC.py @@ -0,0 +1,67 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.exception +import dns.immutable +import dns.name +import dns.rdata +import dns.rdatatype +import dns.rdtypes.util + + +@dns.immutable.immutable +class Bitmap(dns.rdtypes.util.Bitmap): + type_name = "NSEC" + + +@dns.immutable.immutable +class NSEC(dns.rdata.Rdata): + """NSEC record""" + + __slots__ = ["next", "windows"] + + def __init__(self, rdclass, rdtype, next, windows): + super().__init__(rdclass, rdtype) + self.next = self._as_name(next) + if not isinstance(windows, Bitmap): + windows = Bitmap(windows) + self.windows = tuple(windows.windows) + + def to_text(self, origin=None, relativize=True, **kw): + next = self.next.choose_relativity(origin, relativize) + text = Bitmap(self.windows).to_text() + return f"{next}{text}" + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + next = tok.get_name(origin, relativize, relativize_to) + windows = Bitmap.from_text(tok) + return cls(rdclass, rdtype, next, windows) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + # Note that NSEC downcasing, originally mandated by RFC 4034 + # section 6.2 was removed by RFC 6840 section 5.1. + self.next.to_wire(file, None, origin, False) + Bitmap(self.windows).to_wire(file) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + next = parser.get_name(origin) + bitmap = Bitmap.from_wire_parser(parser) + return cls(rdclass, rdtype, next, bitmap) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NSEC3.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NSEC3.py new file mode 100644 index 0000000..d71302b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NSEC3.py @@ -0,0 +1,126 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2004-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import base64 +import binascii +import struct + +import dns.exception +import dns.immutable +import dns.rdata +import dns.rdatatype +import dns.rdtypes.util + +b32_hex_to_normal = bytes.maketrans( + b"0123456789ABCDEFGHIJKLMNOPQRSTUV", b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" +) +b32_normal_to_hex = bytes.maketrans( + b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", b"0123456789ABCDEFGHIJKLMNOPQRSTUV" +) + +# hash algorithm constants +SHA1 = 1 + +# flag constants +OPTOUT = 1 + + +@dns.immutable.immutable +class Bitmap(dns.rdtypes.util.Bitmap): + type_name = "NSEC3" + + +@dns.immutable.immutable +class NSEC3(dns.rdata.Rdata): + """NSEC3 record""" + + __slots__ = ["algorithm", "flags", "iterations", "salt", "next", "windows"] + + def __init__( + self, rdclass, rdtype, algorithm, flags, iterations, salt, next, windows + ): + super().__init__(rdclass, rdtype) + self.algorithm = self._as_uint8(algorithm) + self.flags = self._as_uint8(flags) + self.iterations = self._as_uint16(iterations) + self.salt = self._as_bytes(salt, True, 255) + self.next = self._as_bytes(next, True, 255) + if not isinstance(windows, Bitmap): + windows = Bitmap(windows) + self.windows = tuple(windows.windows) + + def _next_text(self): + next = base64.b32encode(self.next).translate(b32_normal_to_hex).lower().decode() + next = next.rstrip("=") + return next + + def to_text(self, origin=None, relativize=True, **kw): + next = self._next_text() + if self.salt == b"": + salt = "-" + else: + salt = binascii.hexlify(self.salt).decode() + text = Bitmap(self.windows).to_text() + return "%u %u %u %s %s%s" % ( + self.algorithm, + self.flags, + self.iterations, + salt, + next, + text, + ) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + algorithm = tok.get_uint8() + flags = tok.get_uint8() + iterations = tok.get_uint16() + salt = tok.get_string() + if salt == "-": + salt = b"" + else: + salt = binascii.unhexlify(salt.encode("ascii")) + next = tok.get_string().encode("ascii").upper().translate(b32_hex_to_normal) + if next.endswith(b"="): + raise binascii.Error("Incorrect padding") + if len(next) % 8 != 0: + next += b"=" * (8 - len(next) % 8) + next = base64.b32decode(next) + bitmap = Bitmap.from_text(tok) + return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next, bitmap) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + l = len(self.salt) + file.write(struct.pack("!BBHB", self.algorithm, self.flags, self.iterations, l)) + file.write(self.salt) + l = len(self.next) + file.write(struct.pack("!B", l)) + file.write(self.next) + Bitmap(self.windows).to_wire(file) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + (algorithm, flags, iterations) = parser.get_struct("!BBH") + salt = parser.get_counted_bytes() + next = parser.get_counted_bytes() + bitmap = Bitmap.from_wire_parser(parser) + return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next, bitmap) + + def next_name(self, origin=None): + return dns.name.from_text(self._next_text(), origin) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NSEC3PARAM.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NSEC3PARAM.py new file mode 100644 index 0000000..d1e62eb --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NSEC3PARAM.py @@ -0,0 +1,69 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import binascii +import struct + +import dns.exception +import dns.immutable +import dns.rdata + + +@dns.immutable.immutable +class NSEC3PARAM(dns.rdata.Rdata): + """NSEC3PARAM record""" + + __slots__ = ["algorithm", "flags", "iterations", "salt"] + + def __init__(self, rdclass, rdtype, algorithm, flags, iterations, salt): + super().__init__(rdclass, rdtype) + self.algorithm = self._as_uint8(algorithm) + self.flags = self._as_uint8(flags) + self.iterations = self._as_uint16(iterations) + self.salt = self._as_bytes(salt, True, 255) + + def to_text(self, origin=None, relativize=True, **kw): + if self.salt == b"": + salt = "-" + else: + salt = binascii.hexlify(self.salt).decode() + return "%u %u %u %s" % (self.algorithm, self.flags, self.iterations, salt) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + algorithm = tok.get_uint8() + flags = tok.get_uint8() + iterations = tok.get_uint16() + salt = tok.get_string() + if salt == "-": + salt = "" + else: + salt = binascii.unhexlify(salt.encode()) + return cls(rdclass, rdtype, algorithm, flags, iterations, salt) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + l = len(self.salt) + file.write(struct.pack("!BBHB", self.algorithm, self.flags, self.iterations, l)) + file.write(self.salt) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + (algorithm, flags, iterations) = parser.get_struct("!BBH") + salt = parser.get_counted_bytes() + return cls(rdclass, rdtype, algorithm, flags, iterations, salt) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/OPENPGPKEY.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/OPENPGPKEY.py new file mode 100644 index 0000000..4d7a4b6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/OPENPGPKEY.py @@ -0,0 +1,53 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2016 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import base64 + +import dns.exception +import dns.immutable +import dns.rdata +import dns.tokenizer + + +@dns.immutable.immutable +class OPENPGPKEY(dns.rdata.Rdata): + """OPENPGPKEY record""" + + # see: RFC 7929 + + def __init__(self, rdclass, rdtype, key): + super().__init__(rdclass, rdtype) + self.key = self._as_bytes(key) + + def to_text(self, origin=None, relativize=True, **kw): + return dns.rdata._base64ify(self.key, chunksize=None, **kw) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + b64 = tok.concatenate_remaining_identifiers().encode() + key = base64.b64decode(b64) + return cls(rdclass, rdtype, key) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + file.write(self.key) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + key = parser.get_remaining() + return cls(rdclass, rdtype, key) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/OPT.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/OPT.py new file mode 100644 index 0000000..d343dfa --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/OPT.py @@ -0,0 +1,77 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2001-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import struct + +import dns.edns +import dns.exception +import dns.immutable +import dns.rdata + +# We don't implement from_text, and that's ok. +# pylint: disable=abstract-method + + +@dns.immutable.immutable +class OPT(dns.rdata.Rdata): + """OPT record""" + + __slots__ = ["options"] + + def __init__(self, rdclass, rdtype, options): + """Initialize an OPT rdata. + + *rdclass*, an ``int`` is the rdataclass of the Rdata, + which is also the payload size. + + *rdtype*, an ``int`` is the rdatatype of the Rdata. + + *options*, a tuple of ``bytes`` + """ + + super().__init__(rdclass, rdtype) + + def as_option(option): + if not isinstance(option, dns.edns.Option): + raise ValueError("option is not a dns.edns.option") + return option + + self.options = self._as_tuple(options, as_option) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + for opt in self.options: + owire = opt.to_wire() + file.write(struct.pack("!HH", opt.otype, len(owire))) + file.write(owire) + + def to_text(self, origin=None, relativize=True, **kw): + return " ".join(opt.to_text() for opt in self.options) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + options = [] + while parser.remaining() > 0: + (otype, olen) = parser.get_struct("!HH") + with parser.restrict_to(olen): + opt = dns.edns.option_from_wire_parser(otype, parser) + options.append(opt) + return cls(rdclass, rdtype, options) + + @property + def payload(self): + "payload size" + return self.rdclass diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/PTR.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/PTR.py new file mode 100644 index 0000000..98c3616 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/PTR.py @@ -0,0 +1,24 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.immutable +import dns.rdtypes.nsbase + + +@dns.immutable.immutable +class PTR(dns.rdtypes.nsbase.NSBase): + """PTR record""" diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/RESINFO.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/RESINFO.py new file mode 100644 index 0000000..76c8ea2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/RESINFO.py @@ -0,0 +1,24 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.immutable +import dns.rdtypes.txtbase + + +@dns.immutable.immutable +class RESINFO(dns.rdtypes.txtbase.TXTBase): + """RESINFO record""" diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/RP.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/RP.py new file mode 100644 index 0000000..a66cfc5 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/RP.py @@ -0,0 +1,58 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.exception +import dns.immutable +import dns.name +import dns.rdata + + +@dns.immutable.immutable +class RP(dns.rdata.Rdata): + """RP record""" + + # see: RFC 1183 + + __slots__ = ["mbox", "txt"] + + def __init__(self, rdclass, rdtype, mbox, txt): + super().__init__(rdclass, rdtype) + self.mbox = self._as_name(mbox) + self.txt = self._as_name(txt) + + def to_text(self, origin=None, relativize=True, **kw): + mbox = self.mbox.choose_relativity(origin, relativize) + txt = self.txt.choose_relativity(origin, relativize) + return f"{str(mbox)} {str(txt)}" + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + mbox = tok.get_name(origin, relativize, relativize_to) + txt = tok.get_name(origin, relativize, relativize_to) + return cls(rdclass, rdtype, mbox, txt) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + self.mbox.to_wire(file, None, origin, canonicalize) + self.txt.to_wire(file, None, origin, canonicalize) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + mbox = parser.get_name(origin) + txt = parser.get_name(origin) + return cls(rdclass, rdtype, mbox, txt) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/RRSIG.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/RRSIG.py new file mode 100644 index 0000000..8beb423 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/RRSIG.py @@ -0,0 +1,157 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import base64 +import calendar +import struct +import time + +import dns.dnssectypes +import dns.exception +import dns.immutable +import dns.rdata +import dns.rdatatype + + +class BadSigTime(dns.exception.DNSException): + """Time in DNS SIG or RRSIG resource record cannot be parsed.""" + + +def sigtime_to_posixtime(what): + if len(what) <= 10 and what.isdigit(): + return int(what) + if len(what) != 14: + raise BadSigTime + year = int(what[0:4]) + month = int(what[4:6]) + day = int(what[6:8]) + hour = int(what[8:10]) + minute = int(what[10:12]) + second = int(what[12:14]) + return calendar.timegm((year, month, day, hour, minute, second, 0, 0, 0)) + + +def posixtime_to_sigtime(what): + return time.strftime("%Y%m%d%H%M%S", time.gmtime(what)) + + +@dns.immutable.immutable +class RRSIG(dns.rdata.Rdata): + """RRSIG record""" + + __slots__ = [ + "type_covered", + "algorithm", + "labels", + "original_ttl", + "expiration", + "inception", + "key_tag", + "signer", + "signature", + ] + + def __init__( + self, + rdclass, + rdtype, + type_covered, + algorithm, + labels, + original_ttl, + expiration, + inception, + key_tag, + signer, + signature, + ): + super().__init__(rdclass, rdtype) + self.type_covered = self._as_rdatatype(type_covered) + self.algorithm = dns.dnssectypes.Algorithm.make(algorithm) + self.labels = self._as_uint8(labels) + self.original_ttl = self._as_ttl(original_ttl) + self.expiration = self._as_uint32(expiration) + self.inception = self._as_uint32(inception) + self.key_tag = self._as_uint16(key_tag) + self.signer = self._as_name(signer) + self.signature = self._as_bytes(signature) + + def covers(self): + return self.type_covered + + def to_text(self, origin=None, relativize=True, **kw): + return "%s %d %d %d %s %s %d %s %s" % ( + dns.rdatatype.to_text(self.type_covered), + self.algorithm, + self.labels, + self.original_ttl, + posixtime_to_sigtime(self.expiration), + posixtime_to_sigtime(self.inception), + self.key_tag, + self.signer.choose_relativity(origin, relativize), + dns.rdata._base64ify(self.signature, **kw), + ) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + type_covered = dns.rdatatype.from_text(tok.get_string()) + algorithm = dns.dnssectypes.Algorithm.from_text(tok.get_string()) + labels = tok.get_int() + original_ttl = tok.get_ttl() + expiration = sigtime_to_posixtime(tok.get_string()) + inception = sigtime_to_posixtime(tok.get_string()) + key_tag = tok.get_int() + signer = tok.get_name(origin, relativize, relativize_to) + b64 = tok.concatenate_remaining_identifiers().encode() + signature = base64.b64decode(b64) + return cls( + rdclass, + rdtype, + type_covered, + algorithm, + labels, + original_ttl, + expiration, + inception, + key_tag, + signer, + signature, + ) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + header = struct.pack( + "!HBBIIIH", + self.type_covered, + self.algorithm, + self.labels, + self.original_ttl, + self.expiration, + self.inception, + self.key_tag, + ) + file.write(header) + self.signer.to_wire(file, None, origin, canonicalize) + file.write(self.signature) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + header = parser.get_struct("!HBBIIIH") + signer = parser.get_name(origin) + signature = parser.get_remaining() + return cls(rdclass, rdtype, *header, signer, signature) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/RT.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/RT.py new file mode 100644 index 0000000..5a4d45c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/RT.py @@ -0,0 +1,24 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.immutable +import dns.rdtypes.mxbase + + +@dns.immutable.immutable +class RT(dns.rdtypes.mxbase.UncompressedDowncasingMX): + """RT record""" diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/SMIMEA.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/SMIMEA.py new file mode 100644 index 0000000..55d87bf --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/SMIMEA.py @@ -0,0 +1,9 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import dns.immutable +import dns.rdtypes.tlsabase + + +@dns.immutable.immutable +class SMIMEA(dns.rdtypes.tlsabase.TLSABase): + """SMIMEA record""" diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/SOA.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/SOA.py new file mode 100644 index 0000000..09aa832 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/SOA.py @@ -0,0 +1,86 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import struct + +import dns.exception +import dns.immutable +import dns.name +import dns.rdata + + +@dns.immutable.immutable +class SOA(dns.rdata.Rdata): + """SOA record""" + + # see: RFC 1035 + + __slots__ = ["mname", "rname", "serial", "refresh", "retry", "expire", "minimum"] + + def __init__( + self, rdclass, rdtype, mname, rname, serial, refresh, retry, expire, minimum + ): + super().__init__(rdclass, rdtype) + self.mname = self._as_name(mname) + self.rname = self._as_name(rname) + self.serial = self._as_uint32(serial) + self.refresh = self._as_ttl(refresh) + self.retry = self._as_ttl(retry) + self.expire = self._as_ttl(expire) + self.minimum = self._as_ttl(minimum) + + def to_text(self, origin=None, relativize=True, **kw): + mname = self.mname.choose_relativity(origin, relativize) + rname = self.rname.choose_relativity(origin, relativize) + return "%s %s %d %d %d %d %d" % ( + mname, + rname, + self.serial, + self.refresh, + self.retry, + self.expire, + self.minimum, + ) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + mname = tok.get_name(origin, relativize, relativize_to) + rname = tok.get_name(origin, relativize, relativize_to) + serial = tok.get_uint32() + refresh = tok.get_ttl() + retry = tok.get_ttl() + expire = tok.get_ttl() + minimum = tok.get_ttl() + return cls( + rdclass, rdtype, mname, rname, serial, refresh, retry, expire, minimum + ) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + self.mname.to_wire(file, compress, origin, canonicalize) + self.rname.to_wire(file, compress, origin, canonicalize) + five_ints = struct.pack( + "!IIIII", self.serial, self.refresh, self.retry, self.expire, self.minimum + ) + file.write(five_ints) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + mname = parser.get_name(origin) + rname = parser.get_name(origin) + return cls(rdclass, rdtype, mname, rname, *parser.get_struct("!IIIII")) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/SPF.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/SPF.py new file mode 100644 index 0000000..1df3b70 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/SPF.py @@ -0,0 +1,26 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.immutable +import dns.rdtypes.txtbase + + +@dns.immutable.immutable +class SPF(dns.rdtypes.txtbase.TXTBase): + """SPF record""" + + # see: RFC 4408 diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/SSHFP.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/SSHFP.py new file mode 100644 index 0000000..d2c4b07 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/SSHFP.py @@ -0,0 +1,68 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2005-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import binascii +import struct + +import dns.immutable +import dns.rdata +import dns.rdatatype + + +@dns.immutable.immutable +class SSHFP(dns.rdata.Rdata): + """SSHFP record""" + + # See RFC 4255 + + __slots__ = ["algorithm", "fp_type", "fingerprint"] + + def __init__(self, rdclass, rdtype, algorithm, fp_type, fingerprint): + super().__init__(rdclass, rdtype) + self.algorithm = self._as_uint8(algorithm) + self.fp_type = self._as_uint8(fp_type) + self.fingerprint = self._as_bytes(fingerprint, True) + + def to_text(self, origin=None, relativize=True, **kw): + kw = kw.copy() + chunksize = kw.pop("chunksize", 128) + return "%d %d %s" % ( + self.algorithm, + self.fp_type, + dns.rdata._hexify(self.fingerprint, chunksize=chunksize, **kw), + ) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + algorithm = tok.get_uint8() + fp_type = tok.get_uint8() + fingerprint = tok.concatenate_remaining_identifiers().encode() + fingerprint = binascii.unhexlify(fingerprint) + return cls(rdclass, rdtype, algorithm, fp_type, fingerprint) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + header = struct.pack("!BB", self.algorithm, self.fp_type) + file.write(header) + file.write(self.fingerprint) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + header = parser.get_struct("BB") + fingerprint = parser.get_remaining() + return cls(rdclass, rdtype, header[0], header[1], fingerprint) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/TKEY.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/TKEY.py new file mode 100644 index 0000000..75f6224 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/TKEY.py @@ -0,0 +1,142 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import base64 +import struct + +import dns.exception +import dns.immutable +import dns.rdata + + +@dns.immutable.immutable +class TKEY(dns.rdata.Rdata): + """TKEY Record""" + + __slots__ = [ + "algorithm", + "inception", + "expiration", + "mode", + "error", + "key", + "other", + ] + + def __init__( + self, + rdclass, + rdtype, + algorithm, + inception, + expiration, + mode, + error, + key, + other=b"", + ): + super().__init__(rdclass, rdtype) + self.algorithm = self._as_name(algorithm) + self.inception = self._as_uint32(inception) + self.expiration = self._as_uint32(expiration) + self.mode = self._as_uint16(mode) + self.error = self._as_uint16(error) + self.key = self._as_bytes(key) + self.other = self._as_bytes(other) + + def to_text(self, origin=None, relativize=True, **kw): + _algorithm = self.algorithm.choose_relativity(origin, relativize) + text = "%s %u %u %u %u %s" % ( + str(_algorithm), + self.inception, + self.expiration, + self.mode, + self.error, + dns.rdata._base64ify(self.key, 0), + ) + if len(self.other) > 0: + text += f" {dns.rdata._base64ify(self.other, 0)}" + + return text + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + algorithm = tok.get_name(relativize=False) + inception = tok.get_uint32() + expiration = tok.get_uint32() + mode = tok.get_uint16() + error = tok.get_uint16() + key_b64 = tok.get_string().encode() + key = base64.b64decode(key_b64) + other_b64 = tok.concatenate_remaining_identifiers(True).encode() + other = base64.b64decode(other_b64) + + return cls( + rdclass, rdtype, algorithm, inception, expiration, mode, error, key, other + ) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + self.algorithm.to_wire(file, compress, origin) + file.write( + struct.pack("!IIHH", self.inception, self.expiration, self.mode, self.error) + ) + file.write(struct.pack("!H", len(self.key))) + file.write(self.key) + file.write(struct.pack("!H", len(self.other))) + if len(self.other) > 0: + file.write(self.other) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + algorithm = parser.get_name(origin) + inception, expiration, mode, error = parser.get_struct("!IIHH") + key = parser.get_counted_bytes(2) + other = parser.get_counted_bytes(2) + + return cls( + rdclass, rdtype, algorithm, inception, expiration, mode, error, key, other + ) + + # Constants for the mode field - from RFC 2930: + # 2.5 The Mode Field + # + # The mode field specifies the general scheme for key agreement or + # the purpose of the TKEY DNS message. Servers and resolvers + # supporting this specification MUST implement the Diffie-Hellman key + # agreement mode and the key deletion mode for queries. All other + # modes are OPTIONAL. A server supporting TKEY that receives a TKEY + # request with a mode it does not support returns the BADMODE error. + # The following values of the Mode octet are defined, available, or + # reserved: + # + # Value Description + # ----- ----------- + # 0 - reserved, see section 7 + # 1 server assignment + # 2 Diffie-Hellman exchange + # 3 GSS-API negotiation + # 4 resolver assignment + # 5 key deletion + # 6-65534 - available, see section 7 + # 65535 - reserved, see section 7 + SERVER_ASSIGNMENT = 1 + DIFFIE_HELLMAN_EXCHANGE = 2 + GSSAPI_NEGOTIATION = 3 + RESOLVER_ASSIGNMENT = 4 + KEY_DELETION = 5 diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/TLSA.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/TLSA.py new file mode 100644 index 0000000..4dffc55 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/TLSA.py @@ -0,0 +1,9 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import dns.immutable +import dns.rdtypes.tlsabase + + +@dns.immutable.immutable +class TLSA(dns.rdtypes.tlsabase.TLSABase): + """TLSA record""" diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/TSIG.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/TSIG.py new file mode 100644 index 0000000..7942382 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/TSIG.py @@ -0,0 +1,160 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2001-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import base64 +import struct + +import dns.exception +import dns.immutable +import dns.rcode +import dns.rdata + + +@dns.immutable.immutable +class TSIG(dns.rdata.Rdata): + """TSIG record""" + + __slots__ = [ + "algorithm", + "time_signed", + "fudge", + "mac", + "original_id", + "error", + "other", + ] + + def __init__( + self, + rdclass, + rdtype, + algorithm, + time_signed, + fudge, + mac, + original_id, + error, + other, + ): + """Initialize a TSIG rdata. + + *rdclass*, an ``int`` is the rdataclass of the Rdata. + + *rdtype*, an ``int`` is the rdatatype of the Rdata. + + *algorithm*, a ``dns.name.Name``. + + *time_signed*, an ``int``. + + *fudge*, an ``int`. + + *mac*, a ``bytes`` + + *original_id*, an ``int`` + + *error*, an ``int`` + + *other*, a ``bytes`` + """ + + super().__init__(rdclass, rdtype) + self.algorithm = self._as_name(algorithm) + self.time_signed = self._as_uint48(time_signed) + self.fudge = self._as_uint16(fudge) + self.mac = self._as_bytes(mac) + self.original_id = self._as_uint16(original_id) + self.error = dns.rcode.Rcode.make(error) + self.other = self._as_bytes(other) + + def to_text(self, origin=None, relativize=True, **kw): + algorithm = self.algorithm.choose_relativity(origin, relativize) + error = dns.rcode.to_text(self.error, True) + text = ( + f"{algorithm} {self.time_signed} {self.fudge} " + + f"{len(self.mac)} {dns.rdata._base64ify(self.mac, 0)} " + + f"{self.original_id} {error} {len(self.other)}" + ) + if self.other: + text += f" {dns.rdata._base64ify(self.other, 0)}" + return text + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + algorithm = tok.get_name(relativize=False) + time_signed = tok.get_uint48() + fudge = tok.get_uint16() + mac_len = tok.get_uint16() + mac = base64.b64decode(tok.get_string()) + if len(mac) != mac_len: + raise SyntaxError("invalid MAC") + original_id = tok.get_uint16() + error = dns.rcode.from_text(tok.get_string()) + other_len = tok.get_uint16() + if other_len > 0: + other = base64.b64decode(tok.get_string()) + if len(other) != other_len: + raise SyntaxError("invalid other data") + else: + other = b"" + return cls( + rdclass, + rdtype, + algorithm, + time_signed, + fudge, + mac, + original_id, + error, + other, + ) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + self.algorithm.to_wire(file, None, origin, False) + file.write( + struct.pack( + "!HIHH", + (self.time_signed >> 32) & 0xFFFF, + self.time_signed & 0xFFFFFFFF, + self.fudge, + len(self.mac), + ) + ) + file.write(self.mac) + file.write(struct.pack("!HHH", self.original_id, self.error, len(self.other))) + file.write(self.other) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + algorithm = parser.get_name() + time_signed = parser.get_uint48() + fudge = parser.get_uint16() + mac = parser.get_counted_bytes(2) + (original_id, error) = parser.get_struct("!HH") + other = parser.get_counted_bytes(2) + return cls( + rdclass, + rdtype, + algorithm, + time_signed, + fudge, + mac, + original_id, + error, + other, + ) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/TXT.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/TXT.py new file mode 100644 index 0000000..6d4dae2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/TXT.py @@ -0,0 +1,24 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.immutable +import dns.rdtypes.txtbase + + +@dns.immutable.immutable +class TXT(dns.rdtypes.txtbase.TXTBase): + """TXT record""" diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/URI.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/URI.py new file mode 100644 index 0000000..2efbb30 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/URI.py @@ -0,0 +1,79 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# Copyright (C) 2015 Red Hat, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import struct + +import dns.exception +import dns.immutable +import dns.name +import dns.rdata +import dns.rdtypes.util + + +@dns.immutable.immutable +class URI(dns.rdata.Rdata): + """URI record""" + + # see RFC 7553 + + __slots__ = ["priority", "weight", "target"] + + def __init__(self, rdclass, rdtype, priority, weight, target): + super().__init__(rdclass, rdtype) + self.priority = self._as_uint16(priority) + self.weight = self._as_uint16(weight) + self.target = self._as_bytes(target, True) + if len(self.target) == 0: + raise dns.exception.SyntaxError("URI target cannot be empty") + + def to_text(self, origin=None, relativize=True, **kw): + return '%d %d "%s"' % (self.priority, self.weight, self.target.decode()) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + priority = tok.get_uint16() + weight = tok.get_uint16() + target = tok.get().unescape() + if not (target.is_quoted_string() or target.is_identifier()): + raise dns.exception.SyntaxError("URI target must be a string") + return cls(rdclass, rdtype, priority, weight, target.value) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + two_ints = struct.pack("!HH", self.priority, self.weight) + file.write(two_ints) + file.write(self.target) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + (priority, weight) = parser.get_struct("!HH") + target = parser.get_remaining() + if len(target) == 0: + raise dns.exception.FormError("URI target may not be empty") + return cls(rdclass, rdtype, priority, weight, target) + + def _processing_priority(self): + return self.priority + + def _processing_weight(self): + return self.weight + + @classmethod + def _processing_order(cls, iterable): + return dns.rdtypes.util.weighted_processing_order(iterable) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/WALLET.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/WALLET.py new file mode 100644 index 0000000..ff46476 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/WALLET.py @@ -0,0 +1,9 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import dns.immutable +import dns.rdtypes.txtbase + + +@dns.immutable.immutable +class WALLET(dns.rdtypes.txtbase.TXTBase): + """WALLET record""" diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/X25.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/X25.py new file mode 100644 index 0000000..2436ddb --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/X25.py @@ -0,0 +1,57 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import struct + +import dns.exception +import dns.immutable +import dns.rdata +import dns.tokenizer + + +@dns.immutable.immutable +class X25(dns.rdata.Rdata): + """X25 record""" + + # see RFC 1183 + + __slots__ = ["address"] + + def __init__(self, rdclass, rdtype, address): + super().__init__(rdclass, rdtype) + self.address = self._as_bytes(address, True, 255) + + def to_text(self, origin=None, relativize=True, **kw): + return f'"{dns.rdata._escapify(self.address)}"' + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + address = tok.get_string() + return cls(rdclass, rdtype, address) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + l = len(self.address) + assert l < 256 + file.write(struct.pack("!B", l)) + file.write(self.address) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + address = parser.get_counted_bytes() + return cls(rdclass, rdtype, address) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/ZONEMD.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/ZONEMD.py new file mode 100644 index 0000000..c90e3ee --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/ZONEMD.py @@ -0,0 +1,66 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import binascii +import struct + +import dns.immutable +import dns.rdata +import dns.rdatatype +import dns.zonetypes + + +@dns.immutable.immutable +class ZONEMD(dns.rdata.Rdata): + """ZONEMD record""" + + # See RFC 8976 + + __slots__ = ["serial", "scheme", "hash_algorithm", "digest"] + + def __init__(self, rdclass, rdtype, serial, scheme, hash_algorithm, digest): + super().__init__(rdclass, rdtype) + self.serial = self._as_uint32(serial) + self.scheme = dns.zonetypes.DigestScheme.make(scheme) + self.hash_algorithm = dns.zonetypes.DigestHashAlgorithm.make(hash_algorithm) + self.digest = self._as_bytes(digest) + + if self.scheme == 0: # reserved, RFC 8976 Sec. 5.2 + raise ValueError("scheme 0 is reserved") + if self.hash_algorithm == 0: # reserved, RFC 8976 Sec. 5.3 + raise ValueError("hash_algorithm 0 is reserved") + + hasher = dns.zonetypes._digest_hashers.get(self.hash_algorithm) + if hasher and hasher().digest_size != len(self.digest): + raise ValueError("digest length inconsistent with hash algorithm") + + def to_text(self, origin=None, relativize=True, **kw): + kw = kw.copy() + chunksize = kw.pop("chunksize", 128) + return "%d %d %d %s" % ( + self.serial, + self.scheme, + self.hash_algorithm, + dns.rdata._hexify(self.digest, chunksize=chunksize, **kw), + ) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + serial = tok.get_uint32() + scheme = tok.get_uint8() + hash_algorithm = tok.get_uint8() + digest = tok.concatenate_remaining_identifiers().encode() + digest = binascii.unhexlify(digest) + return cls(rdclass, rdtype, serial, scheme, hash_algorithm, digest) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + header = struct.pack("!IBB", self.serial, self.scheme, self.hash_algorithm) + file.write(header) + file.write(self.digest) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + header = parser.get_struct("!IBB") + digest = parser.get_remaining() + return cls(rdclass, rdtype, header[0], header[1], header[2], digest) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/__init__.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/__init__.py new file mode 100644 index 0000000..647b215 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/__init__.py @@ -0,0 +1,70 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Class ANY (generic) rdata type classes.""" + +__all__ = [ + "AFSDB", + "AMTRELAY", + "AVC", + "CAA", + "CDNSKEY", + "CDS", + "CERT", + "CNAME", + "CSYNC", + "DLV", + "DNAME", + "DNSKEY", + "DS", + "EUI48", + "EUI64", + "GPOS", + "HINFO", + "HIP", + "ISDN", + "L32", + "L64", + "LOC", + "LP", + "MX", + "NID", + "NINFO", + "NS", + "NSEC", + "NSEC3", + "NSEC3PARAM", + "OPENPGPKEY", + "OPT", + "PTR", + "RESINFO", + "RP", + "RRSIG", + "RT", + "SMIMEA", + "SOA", + "SPF", + "SSHFP", + "TKEY", + "TLSA", + "TSIG", + "TXT", + "URI", + "WALLET", + "X25", + "ZONEMD", +] diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/CH/A.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/CH/A.py new file mode 100644 index 0000000..832e8d3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/CH/A.py @@ -0,0 +1,59 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import struct + +import dns.immutable +import dns.rdtypes.mxbase + + +@dns.immutable.immutable +class A(dns.rdata.Rdata): + """A record for Chaosnet""" + + # domain: the domain of the address + # address: the 16-bit address + + __slots__ = ["domain", "address"] + + def __init__(self, rdclass, rdtype, domain, address): + super().__init__(rdclass, rdtype) + self.domain = self._as_name(domain) + self.address = self._as_uint16(address) + + def to_text(self, origin=None, relativize=True, **kw): + domain = self.domain.choose_relativity(origin, relativize) + return f"{domain} {self.address:o}" + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + domain = tok.get_name(origin, relativize, relativize_to) + address = tok.get_uint16(base=8) + return cls(rdclass, rdtype, domain, address) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + self.domain.to_wire(file, compress, origin, canonicalize) + pref = struct.pack("!H", self.address) + file.write(pref) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + domain = parser.get_name(origin) + address = parser.get_uint16() + return cls(rdclass, rdtype, domain, address) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/CH/__init__.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/CH/__init__.py new file mode 100644 index 0000000..0760c26 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/CH/__init__.py @@ -0,0 +1,22 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Class CH rdata type classes.""" + +__all__ = [ + "A", +] diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/A.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/A.py new file mode 100644 index 0000000..e09d611 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/A.py @@ -0,0 +1,51 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.exception +import dns.immutable +import dns.ipv4 +import dns.rdata +import dns.tokenizer + + +@dns.immutable.immutable +class A(dns.rdata.Rdata): + """A record.""" + + __slots__ = ["address"] + + def __init__(self, rdclass, rdtype, address): + super().__init__(rdclass, rdtype) + self.address = self._as_ipv4_address(address) + + def to_text(self, origin=None, relativize=True, **kw): + return self.address + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + address = tok.get_identifier() + return cls(rdclass, rdtype, address) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + file.write(dns.ipv4.inet_aton(self.address)) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + address = parser.get_remaining() + return cls(rdclass, rdtype, address) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/AAAA.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/AAAA.py new file mode 100644 index 0000000..0cd139e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/AAAA.py @@ -0,0 +1,51 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.exception +import dns.immutable +import dns.ipv6 +import dns.rdata +import dns.tokenizer + + +@dns.immutable.immutable +class AAAA(dns.rdata.Rdata): + """AAAA record.""" + + __slots__ = ["address"] + + def __init__(self, rdclass, rdtype, address): + super().__init__(rdclass, rdtype) + self.address = self._as_ipv6_address(address) + + def to_text(self, origin=None, relativize=True, **kw): + return self.address + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + address = tok.get_identifier() + return cls(rdclass, rdtype, address) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + file.write(dns.ipv6.inet_aton(self.address)) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + address = parser.get_remaining() + return cls(rdclass, rdtype, address) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/APL.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/APL.py new file mode 100644 index 0000000..44cb3fe --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/APL.py @@ -0,0 +1,150 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import binascii +import codecs +import struct + +import dns.exception +import dns.immutable +import dns.ipv4 +import dns.ipv6 +import dns.rdata +import dns.tokenizer + + +@dns.immutable.immutable +class APLItem: + """An APL list item.""" + + __slots__ = ["family", "negation", "address", "prefix"] + + def __init__(self, family, negation, address, prefix): + self.family = dns.rdata.Rdata._as_uint16(family) + self.negation = dns.rdata.Rdata._as_bool(negation) + if self.family == 1: + self.address = dns.rdata.Rdata._as_ipv4_address(address) + self.prefix = dns.rdata.Rdata._as_int(prefix, 0, 32) + elif self.family == 2: + self.address = dns.rdata.Rdata._as_ipv6_address(address) + self.prefix = dns.rdata.Rdata._as_int(prefix, 0, 128) + else: + self.address = dns.rdata.Rdata._as_bytes(address, max_length=127) + self.prefix = dns.rdata.Rdata._as_uint8(prefix) + + def __str__(self): + if self.negation: + return "!%d:%s/%s" % (self.family, self.address, self.prefix) + else: + return "%d:%s/%s" % (self.family, self.address, self.prefix) + + def to_wire(self, file): + if self.family == 1: + address = dns.ipv4.inet_aton(self.address) + elif self.family == 2: + address = dns.ipv6.inet_aton(self.address) + else: + address = binascii.unhexlify(self.address) + # + # Truncate least significant zero bytes. + # + last = 0 + for i in range(len(address) - 1, -1, -1): + if address[i] != 0: + last = i + 1 + break + address = address[0:last] + l = len(address) + assert l < 128 + if self.negation: + l |= 0x80 + header = struct.pack("!HBB", self.family, self.prefix, l) + file.write(header) + file.write(address) + + +@dns.immutable.immutable +class APL(dns.rdata.Rdata): + """APL record.""" + + # see: RFC 3123 + + __slots__ = ["items"] + + def __init__(self, rdclass, rdtype, items): + super().__init__(rdclass, rdtype) + for item in items: + if not isinstance(item, APLItem): + raise ValueError("item not an APLItem") + self.items = tuple(items) + + def to_text(self, origin=None, relativize=True, **kw): + return " ".join(map(str, self.items)) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + items = [] + for token in tok.get_remaining(): + item = token.unescape().value + if item[0] == "!": + negation = True + item = item[1:] + else: + negation = False + (family, rest) = item.split(":", 1) + family = int(family) + (address, prefix) = rest.split("/", 1) + prefix = int(prefix) + item = APLItem(family, negation, address, prefix) + items.append(item) + + return cls(rdclass, rdtype, items) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + for item in self.items: + item.to_wire(file) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + items = [] + while parser.remaining() > 0: + header = parser.get_struct("!HBB") + afdlen = header[2] + if afdlen > 127: + negation = True + afdlen -= 128 + else: + negation = False + address = parser.get_bytes(afdlen) + l = len(address) + if header[0] == 1: + if l < 4: + address += b"\x00" * (4 - l) + elif header[0] == 2: + if l < 16: + address += b"\x00" * (16 - l) + else: + # + # This isn't really right according to the RFC, but it + # seems better than throwing an exception + # + address = codecs.encode(address, "hex_codec") + item = APLItem(header[0], negation, address, header[1]) + items.append(item) + return cls(rdclass, rdtype, items) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/DHCID.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/DHCID.py new file mode 100644 index 0000000..723492f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/DHCID.py @@ -0,0 +1,54 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import base64 + +import dns.exception +import dns.immutable +import dns.rdata + + +@dns.immutable.immutable +class DHCID(dns.rdata.Rdata): + """DHCID record""" + + # see: RFC 4701 + + __slots__ = ["data"] + + def __init__(self, rdclass, rdtype, data): + super().__init__(rdclass, rdtype) + self.data = self._as_bytes(data) + + def to_text(self, origin=None, relativize=True, **kw): + return dns.rdata._base64ify(self.data, **kw) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + b64 = tok.concatenate_remaining_identifiers().encode() + data = base64.b64decode(b64) + return cls(rdclass, rdtype, data) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + file.write(self.data) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + data = parser.get_remaining() + return cls(rdclass, rdtype, data) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/HTTPS.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/HTTPS.py new file mode 100644 index 0000000..15464cb --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/HTTPS.py @@ -0,0 +1,9 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import dns.immutable +import dns.rdtypes.svcbbase + + +@dns.immutable.immutable +class HTTPS(dns.rdtypes.svcbbase.SVCBBase): + """HTTPS record""" diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/IPSECKEY.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/IPSECKEY.py new file mode 100644 index 0000000..e3a6615 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/IPSECKEY.py @@ -0,0 +1,91 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import base64 +import struct + +import dns.exception +import dns.immutable +import dns.rdtypes.util + + +class Gateway(dns.rdtypes.util.Gateway): + name = "IPSECKEY gateway" + + +@dns.immutable.immutable +class IPSECKEY(dns.rdata.Rdata): + """IPSECKEY record""" + + # see: RFC 4025 + + __slots__ = ["precedence", "gateway_type", "algorithm", "gateway", "key"] + + def __init__( + self, rdclass, rdtype, precedence, gateway_type, algorithm, gateway, key + ): + super().__init__(rdclass, rdtype) + gateway = Gateway(gateway_type, gateway) + self.precedence = self._as_uint8(precedence) + self.gateway_type = gateway.type + self.algorithm = self._as_uint8(algorithm) + self.gateway = gateway.gateway + self.key = self._as_bytes(key) + + def to_text(self, origin=None, relativize=True, **kw): + gateway = Gateway(self.gateway_type, self.gateway).to_text(origin, relativize) + return "%d %d %d %s %s" % ( + self.precedence, + self.gateway_type, + self.algorithm, + gateway, + dns.rdata._base64ify(self.key, **kw), + ) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + precedence = tok.get_uint8() + gateway_type = tok.get_uint8() + algorithm = tok.get_uint8() + gateway = Gateway.from_text( + gateway_type, tok, origin, relativize, relativize_to + ) + b64 = tok.concatenate_remaining_identifiers().encode() + key = base64.b64decode(b64) + return cls( + rdclass, rdtype, precedence, gateway_type, algorithm, gateway.gateway, key + ) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + header = struct.pack("!BBB", self.precedence, self.gateway_type, self.algorithm) + file.write(header) + Gateway(self.gateway_type, self.gateway).to_wire( + file, compress, origin, canonicalize + ) + file.write(self.key) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + header = parser.get_struct("!BBB") + gateway_type = header[1] + gateway = Gateway.from_wire_parser(gateway_type, parser, origin) + key = parser.get_remaining() + return cls( + rdclass, rdtype, header[0], gateway_type, header[2], gateway.gateway, key + ) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/KX.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/KX.py new file mode 100644 index 0000000..6073df4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/KX.py @@ -0,0 +1,24 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.immutable +import dns.rdtypes.mxbase + + +@dns.immutable.immutable +class KX(dns.rdtypes.mxbase.UncompressedDowncasingMX): + """KX record""" diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/NAPTR.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/NAPTR.py new file mode 100644 index 0000000..195d1cb --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/NAPTR.py @@ -0,0 +1,110 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import struct + +import dns.exception +import dns.immutable +import dns.name +import dns.rdata +import dns.rdtypes.util + + +def _write_string(file, s): + l = len(s) + assert l < 256 + file.write(struct.pack("!B", l)) + file.write(s) + + +@dns.immutable.immutable +class NAPTR(dns.rdata.Rdata): + """NAPTR record""" + + # see: RFC 3403 + + __slots__ = ["order", "preference", "flags", "service", "regexp", "replacement"] + + def __init__( + self, rdclass, rdtype, order, preference, flags, service, regexp, replacement + ): + super().__init__(rdclass, rdtype) + self.flags = self._as_bytes(flags, True, 255) + self.service = self._as_bytes(service, True, 255) + self.regexp = self._as_bytes(regexp, True, 255) + self.order = self._as_uint16(order) + self.preference = self._as_uint16(preference) + self.replacement = self._as_name(replacement) + + def to_text(self, origin=None, relativize=True, **kw): + replacement = self.replacement.choose_relativity(origin, relativize) + return '%d %d "%s" "%s" "%s" %s' % ( + self.order, + self.preference, + dns.rdata._escapify(self.flags), + dns.rdata._escapify(self.service), + dns.rdata._escapify(self.regexp), + replacement, + ) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + order = tok.get_uint16() + preference = tok.get_uint16() + flags = tok.get_string() + service = tok.get_string() + regexp = tok.get_string() + replacement = tok.get_name(origin, relativize, relativize_to) + return cls( + rdclass, rdtype, order, preference, flags, service, regexp, replacement + ) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + two_ints = struct.pack("!HH", self.order, self.preference) + file.write(two_ints) + _write_string(file, self.flags) + _write_string(file, self.service) + _write_string(file, self.regexp) + self.replacement.to_wire(file, compress, origin, canonicalize) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + (order, preference) = parser.get_struct("!HH") + strings = [] + for _ in range(3): + s = parser.get_counted_bytes() + strings.append(s) + replacement = parser.get_name(origin) + return cls( + rdclass, + rdtype, + order, + preference, + strings[0], + strings[1], + strings[2], + replacement, + ) + + def _processing_priority(self): + return (self.order, self.preference) + + @classmethod + def _processing_order(cls, iterable): + return dns.rdtypes.util.priority_processing_order(iterable) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/NSAP.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/NSAP.py new file mode 100644 index 0000000..d55edb7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/NSAP.py @@ -0,0 +1,60 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import binascii + +import dns.exception +import dns.immutable +import dns.rdata +import dns.tokenizer + + +@dns.immutable.immutable +class NSAP(dns.rdata.Rdata): + """NSAP record.""" + + # see: RFC 1706 + + __slots__ = ["address"] + + def __init__(self, rdclass, rdtype, address): + super().__init__(rdclass, rdtype) + self.address = self._as_bytes(address) + + def to_text(self, origin=None, relativize=True, **kw): + return f"0x{binascii.hexlify(self.address).decode()}" + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + address = tok.get_string() + if address[0:2] != "0x": + raise dns.exception.SyntaxError("string does not start with 0x") + address = address[2:].replace(".", "") + if len(address) % 2 != 0: + raise dns.exception.SyntaxError("hexstring has odd length") + address = binascii.unhexlify(address.encode()) + return cls(rdclass, rdtype, address) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + file.write(self.address) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + address = parser.get_remaining() + return cls(rdclass, rdtype, address) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/NSAP_PTR.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/NSAP_PTR.py new file mode 100644 index 0000000..ce1c663 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/NSAP_PTR.py @@ -0,0 +1,24 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import dns.immutable +import dns.rdtypes.nsbase + + +@dns.immutable.immutable +class NSAP_PTR(dns.rdtypes.nsbase.UncompressedNS): + """NSAP-PTR record""" diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/PX.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/PX.py new file mode 100644 index 0000000..cdca153 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/PX.py @@ -0,0 +1,73 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import struct + +import dns.exception +import dns.immutable +import dns.name +import dns.rdata +import dns.rdtypes.util + + +@dns.immutable.immutable +class PX(dns.rdata.Rdata): + """PX record.""" + + # see: RFC 2163 + + __slots__ = ["preference", "map822", "mapx400"] + + def __init__(self, rdclass, rdtype, preference, map822, mapx400): + super().__init__(rdclass, rdtype) + self.preference = self._as_uint16(preference) + self.map822 = self._as_name(map822) + self.mapx400 = self._as_name(mapx400) + + def to_text(self, origin=None, relativize=True, **kw): + map822 = self.map822.choose_relativity(origin, relativize) + mapx400 = self.mapx400.choose_relativity(origin, relativize) + return "%d %s %s" % (self.preference, map822, mapx400) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + preference = tok.get_uint16() + map822 = tok.get_name(origin, relativize, relativize_to) + mapx400 = tok.get_name(origin, relativize, relativize_to) + return cls(rdclass, rdtype, preference, map822, mapx400) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + pref = struct.pack("!H", self.preference) + file.write(pref) + self.map822.to_wire(file, None, origin, canonicalize) + self.mapx400.to_wire(file, None, origin, canonicalize) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + preference = parser.get_uint16() + map822 = parser.get_name(origin) + mapx400 = parser.get_name(origin) + return cls(rdclass, rdtype, preference, map822, mapx400) + + def _processing_priority(self): + return self.preference + + @classmethod + def _processing_order(cls, iterable): + return dns.rdtypes.util.priority_processing_order(iterable) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/SRV.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/SRV.py new file mode 100644 index 0000000..5adef98 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/SRV.py @@ -0,0 +1,75 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import struct + +import dns.exception +import dns.immutable +import dns.name +import dns.rdata +import dns.rdtypes.util + + +@dns.immutable.immutable +class SRV(dns.rdata.Rdata): + """SRV record""" + + # see: RFC 2782 + + __slots__ = ["priority", "weight", "port", "target"] + + def __init__(self, rdclass, rdtype, priority, weight, port, target): + super().__init__(rdclass, rdtype) + self.priority = self._as_uint16(priority) + self.weight = self._as_uint16(weight) + self.port = self._as_uint16(port) + self.target = self._as_name(target) + + def to_text(self, origin=None, relativize=True, **kw): + target = self.target.choose_relativity(origin, relativize) + return "%d %d %d %s" % (self.priority, self.weight, self.port, target) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + priority = tok.get_uint16() + weight = tok.get_uint16() + port = tok.get_uint16() + target = tok.get_name(origin, relativize, relativize_to) + return cls(rdclass, rdtype, priority, weight, port, target) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + three_ints = struct.pack("!HHH", self.priority, self.weight, self.port) + file.write(three_ints) + self.target.to_wire(file, compress, origin, canonicalize) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + (priority, weight, port) = parser.get_struct("!HHH") + target = parser.get_name(origin) + return cls(rdclass, rdtype, priority, weight, port, target) + + def _processing_priority(self): + return self.priority + + def _processing_weight(self): + return self.weight + + @classmethod + def _processing_order(cls, iterable): + return dns.rdtypes.util.weighted_processing_order(iterable) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/SVCB.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/SVCB.py new file mode 100644 index 0000000..ff3e932 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/SVCB.py @@ -0,0 +1,9 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import dns.immutable +import dns.rdtypes.svcbbase + + +@dns.immutable.immutable +class SVCB(dns.rdtypes.svcbbase.SVCBBase): + """SVCB record""" diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/WKS.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/WKS.py new file mode 100644 index 0000000..881a784 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/WKS.py @@ -0,0 +1,100 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import socket +import struct + +import dns.immutable +import dns.ipv4 +import dns.rdata + +try: + _proto_tcp = socket.getprotobyname("tcp") + _proto_udp = socket.getprotobyname("udp") +except OSError: + # Fall back to defaults in case /etc/protocols is unavailable. + _proto_tcp = 6 + _proto_udp = 17 + + +@dns.immutable.immutable +class WKS(dns.rdata.Rdata): + """WKS record""" + + # see: RFC 1035 + + __slots__ = ["address", "protocol", "bitmap"] + + def __init__(self, rdclass, rdtype, address, protocol, bitmap): + super().__init__(rdclass, rdtype) + self.address = self._as_ipv4_address(address) + self.protocol = self._as_uint8(protocol) + self.bitmap = self._as_bytes(bitmap) + + def to_text(self, origin=None, relativize=True, **kw): + bits = [] + for i, byte in enumerate(self.bitmap): + for j in range(0, 8): + if byte & (0x80 >> j): + bits.append(str(i * 8 + j)) + text = " ".join(bits) + return "%s %d %s" % (self.address, self.protocol, text) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + address = tok.get_string() + protocol = tok.get_string() + if protocol.isdigit(): + protocol = int(protocol) + else: + protocol = socket.getprotobyname(protocol) + bitmap = bytearray() + for token in tok.get_remaining(): + value = token.unescape().value + if value.isdigit(): + serv = int(value) + else: + if protocol != _proto_udp and protocol != _proto_tcp: + raise NotImplementedError("protocol must be TCP or UDP") + if protocol == _proto_udp: + protocol_text = "udp" + else: + protocol_text = "tcp" + serv = socket.getservbyname(value, protocol_text) + i = serv // 8 + l = len(bitmap) + if l < i + 1: + for _ in range(l, i + 1): + bitmap.append(0) + bitmap[i] = bitmap[i] | (0x80 >> (serv % 8)) + bitmap = dns.rdata._truncate_bitmap(bitmap) + return cls(rdclass, rdtype, address, protocol, bitmap) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + file.write(dns.ipv4.inet_aton(self.address)) + protocol = struct.pack("!B", self.protocol) + file.write(protocol) + file.write(self.bitmap) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + address = parser.get_bytes(4) + protocol = parser.get_uint8() + bitmap = parser.get_remaining() + return cls(rdclass, rdtype, address, protocol, bitmap) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/__init__.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/__init__.py new file mode 100644 index 0000000..dcec4dd --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/__init__.py @@ -0,0 +1,35 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Class IN rdata type classes.""" + +__all__ = [ + "A", + "AAAA", + "APL", + "DHCID", + "HTTPS", + "IPSECKEY", + "KX", + "NAPTR", + "NSAP", + "NSAP_PTR", + "PX", + "SRV", + "SVCB", + "WKS", +] diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/__init__.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/__init__.py new file mode 100644 index 0000000..3997f84 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/__init__.py @@ -0,0 +1,33 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS rdata type classes""" + +__all__ = [ + "ANY", + "IN", + "CH", + "dnskeybase", + "dsbase", + "euibase", + "mxbase", + "nsbase", + "svcbbase", + "tlsabase", + "txtbase", + "util", +] diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/dnskeybase.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/dnskeybase.py new file mode 100644 index 0000000..db300f8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/dnskeybase.py @@ -0,0 +1,87 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import base64 +import enum +import struct + +import dns.dnssectypes +import dns.exception +import dns.immutable +import dns.rdata + +# wildcard import +__all__ = ["SEP", "REVOKE", "ZONE"] # noqa: F822 + + +class Flag(enum.IntFlag): + SEP = 0x0001 + REVOKE = 0x0080 + ZONE = 0x0100 + + +@dns.immutable.immutable +class DNSKEYBase(dns.rdata.Rdata): + """Base class for rdata that is like a DNSKEY record""" + + __slots__ = ["flags", "protocol", "algorithm", "key"] + + def __init__(self, rdclass, rdtype, flags, protocol, algorithm, key): + super().__init__(rdclass, rdtype) + self.flags = Flag(self._as_uint16(flags)) + self.protocol = self._as_uint8(protocol) + self.algorithm = dns.dnssectypes.Algorithm.make(algorithm) + self.key = self._as_bytes(key) + + def to_text(self, origin=None, relativize=True, **kw): + return "%d %d %d %s" % ( + self.flags, + self.protocol, + self.algorithm, + dns.rdata._base64ify(self.key, **kw), + ) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + flags = tok.get_uint16() + protocol = tok.get_uint8() + algorithm = tok.get_string() + b64 = tok.concatenate_remaining_identifiers().encode() + key = base64.b64decode(b64) + return cls(rdclass, rdtype, flags, protocol, algorithm, key) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + header = struct.pack("!HBB", self.flags, self.protocol, self.algorithm) + file.write(header) + file.write(self.key) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + header = parser.get_struct("!HBB") + key = parser.get_remaining() + return cls(rdclass, rdtype, header[0], header[1], header[2], key) + + +### BEGIN generated Flag constants + +SEP = Flag.SEP +REVOKE = Flag.REVOKE +ZONE = Flag.ZONE + +### END generated Flag constants diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/dsbase.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/dsbase.py new file mode 100644 index 0000000..cd21f02 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/dsbase.py @@ -0,0 +1,85 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2010, 2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import binascii +import struct + +import dns.dnssectypes +import dns.immutable +import dns.rdata +import dns.rdatatype + + +@dns.immutable.immutable +class DSBase(dns.rdata.Rdata): + """Base class for rdata that is like a DS record""" + + __slots__ = ["key_tag", "algorithm", "digest_type", "digest"] + + # Digest types registry: + # https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml + _digest_length_by_type = { + 1: 20, # SHA-1, RFC 3658 Sec. 2.4 + 2: 32, # SHA-256, RFC 4509 Sec. 2.2 + 3: 32, # GOST R 34.11-94, RFC 5933 Sec. 4 in conjunction with RFC 4490 Sec. 2.1 + 4: 48, # SHA-384, RFC 6605 Sec. 2 + } + + def __init__(self, rdclass, rdtype, key_tag, algorithm, digest_type, digest): + super().__init__(rdclass, rdtype) + self.key_tag = self._as_uint16(key_tag) + self.algorithm = dns.dnssectypes.Algorithm.make(algorithm) + self.digest_type = dns.dnssectypes.DSDigest.make(self._as_uint8(digest_type)) + self.digest = self._as_bytes(digest) + try: + if len(self.digest) != self._digest_length_by_type[self.digest_type]: + raise ValueError("digest length inconsistent with digest type") + except KeyError: + if self.digest_type == 0: # reserved, RFC 3658 Sec. 2.4 + raise ValueError("digest type 0 is reserved") + + def to_text(self, origin=None, relativize=True, **kw): + kw = kw.copy() + chunksize = kw.pop("chunksize", 128) + return "%d %d %d %s" % ( + self.key_tag, + self.algorithm, + self.digest_type, + dns.rdata._hexify(self.digest, chunksize=chunksize, **kw), + ) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + key_tag = tok.get_uint16() + algorithm = tok.get_string() + digest_type = tok.get_uint8() + digest = tok.concatenate_remaining_identifiers().encode() + digest = binascii.unhexlify(digest) + return cls(rdclass, rdtype, key_tag, algorithm, digest_type, digest) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + header = struct.pack("!HBB", self.key_tag, self.algorithm, self.digest_type) + file.write(header) + file.write(self.digest) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + header = parser.get_struct("!HBB") + digest = parser.get_remaining() + return cls(rdclass, rdtype, header[0], header[1], header[2], digest) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/euibase.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/euibase.py new file mode 100644 index 0000000..a39c166 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/euibase.py @@ -0,0 +1,70 @@ +# Copyright (C) 2015 Red Hat, Inc. +# Author: Petr Spacek +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import binascii + +import dns.immutable +import dns.rdata + + +@dns.immutable.immutable +class EUIBase(dns.rdata.Rdata): + """EUIxx record""" + + # see: rfc7043.txt + + __slots__ = ["eui"] + # define these in subclasses + # byte_len = 6 # 0123456789ab (in hex) + # text_len = byte_len * 3 - 1 # 01-23-45-67-89-ab + + def __init__(self, rdclass, rdtype, eui): + super().__init__(rdclass, rdtype) + self.eui = self._as_bytes(eui) + if len(self.eui) != self.byte_len: + raise dns.exception.FormError( + f"EUI{self.byte_len * 8} rdata has to have {self.byte_len} bytes" + ) + + def to_text(self, origin=None, relativize=True, **kw): + return dns.rdata._hexify(self.eui, chunksize=2, separator=b"-", **kw) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + text = tok.get_string() + if len(text) != cls.text_len: + raise dns.exception.SyntaxError( + f"Input text must have {cls.text_len} characters" + ) + for i in range(2, cls.byte_len * 3 - 1, 3): + if text[i] != "-": + raise dns.exception.SyntaxError(f"Dash expected at position {i}") + text = text.replace("-", "") + try: + data = binascii.unhexlify(text.encode()) + except (ValueError, TypeError) as ex: + raise dns.exception.SyntaxError(f"Hex decoding error: {str(ex)}") + return cls(rdclass, rdtype, data) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + file.write(self.eui) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + eui = parser.get_bytes(cls.byte_len) + return cls(rdclass, rdtype, eui) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/mxbase.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/mxbase.py new file mode 100644 index 0000000..6d5e3d8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/mxbase.py @@ -0,0 +1,87 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""MX-like base classes.""" + +import struct + +import dns.exception +import dns.immutable +import dns.name +import dns.rdata +import dns.rdtypes.util + + +@dns.immutable.immutable +class MXBase(dns.rdata.Rdata): + """Base class for rdata that is like an MX record.""" + + __slots__ = ["preference", "exchange"] + + def __init__(self, rdclass, rdtype, preference, exchange): + super().__init__(rdclass, rdtype) + self.preference = self._as_uint16(preference) + self.exchange = self._as_name(exchange) + + def to_text(self, origin=None, relativize=True, **kw): + exchange = self.exchange.choose_relativity(origin, relativize) + return "%d %s" % (self.preference, exchange) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + preference = tok.get_uint16() + exchange = tok.get_name(origin, relativize, relativize_to) + return cls(rdclass, rdtype, preference, exchange) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + pref = struct.pack("!H", self.preference) + file.write(pref) + self.exchange.to_wire(file, compress, origin, canonicalize) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + preference = parser.get_uint16() + exchange = parser.get_name(origin) + return cls(rdclass, rdtype, preference, exchange) + + def _processing_priority(self): + return self.preference + + @classmethod + def _processing_order(cls, iterable): + return dns.rdtypes.util.priority_processing_order(iterable) + + +@dns.immutable.immutable +class UncompressedMX(MXBase): + """Base class for rdata that is like an MX record, but whose name + is not compressed when converted to DNS wire format, and whose + digestable form is not downcased.""" + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + super()._to_wire(file, None, origin, False) + + +@dns.immutable.immutable +class UncompressedDowncasingMX(MXBase): + """Base class for rdata that is like an MX record, but whose name + is not compressed when convert to DNS wire format.""" + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + super()._to_wire(file, None, origin, canonicalize) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/nsbase.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/nsbase.py new file mode 100644 index 0000000..904224f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/nsbase.py @@ -0,0 +1,63 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""NS-like base classes.""" + +import dns.exception +import dns.immutable +import dns.name +import dns.rdata + + +@dns.immutable.immutable +class NSBase(dns.rdata.Rdata): + """Base class for rdata that is like an NS record.""" + + __slots__ = ["target"] + + def __init__(self, rdclass, rdtype, target): + super().__init__(rdclass, rdtype) + self.target = self._as_name(target) + + def to_text(self, origin=None, relativize=True, **kw): + target = self.target.choose_relativity(origin, relativize) + return str(target) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + target = tok.get_name(origin, relativize, relativize_to) + return cls(rdclass, rdtype, target) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + self.target.to_wire(file, compress, origin, canonicalize) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + target = parser.get_name(origin) + return cls(rdclass, rdtype, target) + + +@dns.immutable.immutable +class UncompressedNS(NSBase): + """Base class for rdata that is like an NS record, but whose name + is not compressed when convert to DNS wire format, and whose + digestable form is not downcased.""" + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + self.target.to_wire(file, None, origin, False) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/svcbbase.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/svcbbase.py new file mode 100644 index 0000000..a2b15b9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/svcbbase.py @@ -0,0 +1,585 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import base64 +import enum +import struct + +import dns.enum +import dns.exception +import dns.immutable +import dns.ipv4 +import dns.ipv6 +import dns.name +import dns.rdata +import dns.rdtypes.util +import dns.renderer +import dns.tokenizer +import dns.wire + +# Until there is an RFC, this module is experimental and may be changed in +# incompatible ways. + + +class UnknownParamKey(dns.exception.DNSException): + """Unknown SVCB ParamKey""" + + +class ParamKey(dns.enum.IntEnum): + """SVCB ParamKey""" + + MANDATORY = 0 + ALPN = 1 + NO_DEFAULT_ALPN = 2 + PORT = 3 + IPV4HINT = 4 + ECH = 5 + IPV6HINT = 6 + DOHPATH = 7 + OHTTP = 8 + + @classmethod + def _maximum(cls): + return 65535 + + @classmethod + def _short_name(cls): + return "SVCBParamKey" + + @classmethod + def _prefix(cls): + return "KEY" + + @classmethod + def _unknown_exception_class(cls): + return UnknownParamKey + + +class Emptiness(enum.IntEnum): + NEVER = 0 + ALWAYS = 1 + ALLOWED = 2 + + +def _validate_key(key): + force_generic = False + if isinstance(key, bytes): + # We decode to latin-1 so we get 0-255 as valid and do NOT interpret + # UTF-8 sequences + key = key.decode("latin-1") + if isinstance(key, str): + if key.lower().startswith("key"): + force_generic = True + if key[3:].startswith("0") and len(key) != 4: + # key has leading zeros + raise ValueError("leading zeros in key") + key = key.replace("-", "_") + return (ParamKey.make(key), force_generic) + + +def key_to_text(key): + return ParamKey.to_text(key).replace("_", "-").lower() + + +# Like rdata escapify, but escapes ',' too. + +_escaped = b'",\\' + + +def _escapify(qstring): + text = "" + for c in qstring: + if c in _escaped: + text += "\\" + chr(c) + elif c >= 0x20 and c < 0x7F: + text += chr(c) + else: + text += "\\%03d" % c + return text + + +def _unescape(value): + if value == "": + return value + unescaped = b"" + l = len(value) + i = 0 + while i < l: + c = value[i] + i += 1 + if c == "\\": + if i >= l: # pragma: no cover (can't happen via tokenizer get()) + raise dns.exception.UnexpectedEnd + c = value[i] + i += 1 + if c.isdigit(): + if i >= l: + raise dns.exception.UnexpectedEnd + c2 = value[i] + i += 1 + if i >= l: + raise dns.exception.UnexpectedEnd + c3 = value[i] + i += 1 + if not (c2.isdigit() and c3.isdigit()): + raise dns.exception.SyntaxError + codepoint = int(c) * 100 + int(c2) * 10 + int(c3) + if codepoint > 255: + raise dns.exception.SyntaxError + unescaped += b"%c" % (codepoint) + continue + unescaped += c.encode() + return unescaped + + +def _split(value): + l = len(value) + i = 0 + items = [] + unescaped = b"" + while i < l: + c = value[i] + i += 1 + if c == ord("\\"): + if i >= l: # pragma: no cover (can't happen via tokenizer get()) + raise dns.exception.UnexpectedEnd + c = value[i] + i += 1 + unescaped += b"%c" % (c) + elif c == ord(","): + items.append(unescaped) + unescaped = b"" + else: + unescaped += b"%c" % (c) + items.append(unescaped) + return items + + +@dns.immutable.immutable +class Param: + """Abstract base class for SVCB parameters""" + + @classmethod + def emptiness(cls): + return Emptiness.NEVER + + +@dns.immutable.immutable +class GenericParam(Param): + """Generic SVCB parameter""" + + def __init__(self, value): + self.value = dns.rdata.Rdata._as_bytes(value, True) + + @classmethod + def emptiness(cls): + return Emptiness.ALLOWED + + @classmethod + def from_value(cls, value): + if value is None or len(value) == 0: + return None + else: + return cls(_unescape(value)) + + def to_text(self): + return '"' + dns.rdata._escapify(self.value) + '"' + + @classmethod + def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 + value = parser.get_bytes(parser.remaining()) + if len(value) == 0: + return None + else: + return cls(value) + + def to_wire(self, file, origin=None): # pylint: disable=W0613 + file.write(self.value) + + +@dns.immutable.immutable +class MandatoryParam(Param): + def __init__(self, keys): + # check for duplicates + keys = sorted([_validate_key(key)[0] for key in keys]) + prior_k = None + for k in keys: + if k == prior_k: + raise ValueError(f"duplicate key {k:d}") + prior_k = k + if k == ParamKey.MANDATORY: + raise ValueError("listed the mandatory key as mandatory") + self.keys = tuple(keys) + + @classmethod + def from_value(cls, value): + keys = [k.encode() for k in value.split(",")] + return cls(keys) + + def to_text(self): + return '"' + ",".join([key_to_text(key) for key in self.keys]) + '"' + + @classmethod + def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 + keys = [] + last_key = -1 + while parser.remaining() > 0: + key = parser.get_uint16() + if key < last_key: + raise dns.exception.FormError("manadatory keys not ascending") + last_key = key + keys.append(key) + return cls(keys) + + def to_wire(self, file, origin=None): # pylint: disable=W0613 + for key in self.keys: + file.write(struct.pack("!H", key)) + + +@dns.immutable.immutable +class ALPNParam(Param): + def __init__(self, ids): + self.ids = dns.rdata.Rdata._as_tuple( + ids, lambda x: dns.rdata.Rdata._as_bytes(x, True, 255, False) + ) + + @classmethod + def from_value(cls, value): + return cls(_split(_unescape(value))) + + def to_text(self): + value = ",".join([_escapify(id) for id in self.ids]) + return '"' + dns.rdata._escapify(value.encode()) + '"' + + @classmethod + def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 + ids = [] + while parser.remaining() > 0: + id = parser.get_counted_bytes() + ids.append(id) + return cls(ids) + + def to_wire(self, file, origin=None): # pylint: disable=W0613 + for id in self.ids: + file.write(struct.pack("!B", len(id))) + file.write(id) + + +@dns.immutable.immutable +class NoDefaultALPNParam(Param): + # We don't ever expect to instantiate this class, but we need + # a from_value() and a from_wire_parser(), so we just return None + # from the class methods when things are OK. + + @classmethod + def emptiness(cls): + return Emptiness.ALWAYS + + @classmethod + def from_value(cls, value): + if value is None or value == "": + return None + else: + raise ValueError("no-default-alpn with non-empty value") + + def to_text(self): + raise NotImplementedError # pragma: no cover + + @classmethod + def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 + if parser.remaining() != 0: + raise dns.exception.FormError + return None + + def to_wire(self, file, origin=None): # pylint: disable=W0613 + raise NotImplementedError # pragma: no cover + + +@dns.immutable.immutable +class PortParam(Param): + def __init__(self, port): + self.port = dns.rdata.Rdata._as_uint16(port) + + @classmethod + def from_value(cls, value): + value = int(value) + return cls(value) + + def to_text(self): + return f'"{self.port}"' + + @classmethod + def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 + port = parser.get_uint16() + return cls(port) + + def to_wire(self, file, origin=None): # pylint: disable=W0613 + file.write(struct.pack("!H", self.port)) + + +@dns.immutable.immutable +class IPv4HintParam(Param): + def __init__(self, addresses): + self.addresses = dns.rdata.Rdata._as_tuple( + addresses, dns.rdata.Rdata._as_ipv4_address + ) + + @classmethod + def from_value(cls, value): + addresses = value.split(",") + return cls(addresses) + + def to_text(self): + return '"' + ",".join(self.addresses) + '"' + + @classmethod + def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 + addresses = [] + while parser.remaining() > 0: + ip = parser.get_bytes(4) + addresses.append(dns.ipv4.inet_ntoa(ip)) + return cls(addresses) + + def to_wire(self, file, origin=None): # pylint: disable=W0613 + for address in self.addresses: + file.write(dns.ipv4.inet_aton(address)) + + +@dns.immutable.immutable +class IPv6HintParam(Param): + def __init__(self, addresses): + self.addresses = dns.rdata.Rdata._as_tuple( + addresses, dns.rdata.Rdata._as_ipv6_address + ) + + @classmethod + def from_value(cls, value): + addresses = value.split(",") + return cls(addresses) + + def to_text(self): + return '"' + ",".join(self.addresses) + '"' + + @classmethod + def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 + addresses = [] + while parser.remaining() > 0: + ip = parser.get_bytes(16) + addresses.append(dns.ipv6.inet_ntoa(ip)) + return cls(addresses) + + def to_wire(self, file, origin=None): # pylint: disable=W0613 + for address in self.addresses: + file.write(dns.ipv6.inet_aton(address)) + + +@dns.immutable.immutable +class ECHParam(Param): + def __init__(self, ech): + self.ech = dns.rdata.Rdata._as_bytes(ech, True) + + @classmethod + def from_value(cls, value): + if "\\" in value: + raise ValueError("escape in ECH value") + value = base64.b64decode(value.encode()) + return cls(value) + + def to_text(self): + b64 = base64.b64encode(self.ech).decode("ascii") + return f'"{b64}"' + + @classmethod + def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 + value = parser.get_bytes(parser.remaining()) + return cls(value) + + def to_wire(self, file, origin=None): # pylint: disable=W0613 + file.write(self.ech) + + +@dns.immutable.immutable +class OHTTPParam(Param): + # We don't ever expect to instantiate this class, but we need + # a from_value() and a from_wire_parser(), so we just return None + # from the class methods when things are OK. + + @classmethod + def emptiness(cls): + return Emptiness.ALWAYS + + @classmethod + def from_value(cls, value): + if value is None or value == "": + return None + else: + raise ValueError("ohttp with non-empty value") + + def to_text(self): + raise NotImplementedError # pragma: no cover + + @classmethod + def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 + if parser.remaining() != 0: + raise dns.exception.FormError + return None + + def to_wire(self, file, origin=None): # pylint: disable=W0613 + raise NotImplementedError # pragma: no cover + + +_class_for_key = { + ParamKey.MANDATORY: MandatoryParam, + ParamKey.ALPN: ALPNParam, + ParamKey.NO_DEFAULT_ALPN: NoDefaultALPNParam, + ParamKey.PORT: PortParam, + ParamKey.IPV4HINT: IPv4HintParam, + ParamKey.ECH: ECHParam, + ParamKey.IPV6HINT: IPv6HintParam, + ParamKey.OHTTP: OHTTPParam, +} + + +def _validate_and_define(params, key, value): + (key, force_generic) = _validate_key(_unescape(key)) + if key in params: + raise SyntaxError(f'duplicate key "{key:d}"') + cls = _class_for_key.get(key, GenericParam) + emptiness = cls.emptiness() + if value is None: + if emptiness == Emptiness.NEVER: + raise SyntaxError("value cannot be empty") + value = cls.from_value(value) + else: + if force_generic: + value = cls.from_wire_parser(dns.wire.Parser(_unescape(value))) + else: + value = cls.from_value(value) + params[key] = value + + +@dns.immutable.immutable +class SVCBBase(dns.rdata.Rdata): + """Base class for SVCB-like records""" + + # see: draft-ietf-dnsop-svcb-https-11 + + __slots__ = ["priority", "target", "params"] + + def __init__(self, rdclass, rdtype, priority, target, params): + super().__init__(rdclass, rdtype) + self.priority = self._as_uint16(priority) + self.target = self._as_name(target) + for k, v in params.items(): + k = ParamKey.make(k) + if not isinstance(v, Param) and v is not None: + raise ValueError(f"{k:d} not a Param") + self.params = dns.immutable.Dict(params) + # Make sure any parameter listed as mandatory is present in the + # record. + mandatory = params.get(ParamKey.MANDATORY) + if mandatory: + for key in mandatory.keys: + # Note we have to say "not in" as we have None as a value + # so a get() and a not None test would be wrong. + if key not in params: + raise ValueError(f"key {key:d} declared mandatory but not present") + # The no-default-alpn parameter requires the alpn parameter. + if ParamKey.NO_DEFAULT_ALPN in params: + if ParamKey.ALPN not in params: + raise ValueError("no-default-alpn present, but alpn missing") + + def to_text(self, origin=None, relativize=True, **kw): + target = self.target.choose_relativity(origin, relativize) + params = [] + for key in sorted(self.params.keys()): + value = self.params[key] + if value is None: + params.append(key_to_text(key)) + else: + kv = key_to_text(key) + "=" + value.to_text() + params.append(kv) + if len(params) > 0: + space = " " + else: + space = "" + return "%d %s%s%s" % (self.priority, target, space, " ".join(params)) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + priority = tok.get_uint16() + target = tok.get_name(origin, relativize, relativize_to) + if priority == 0: + token = tok.get() + if not token.is_eol_or_eof(): + raise SyntaxError("parameters in AliasMode") + tok.unget(token) + params = {} + while True: + token = tok.get() + if token.is_eol_or_eof(): + tok.unget(token) + break + if token.ttype != dns.tokenizer.IDENTIFIER: + raise SyntaxError("parameter is not an identifier") + equals = token.value.find("=") + if equals == len(token.value) - 1: + # 'key=', so next token should be a quoted string without + # any intervening whitespace. + key = token.value[:-1] + token = tok.get(want_leading=True) + if token.ttype != dns.tokenizer.QUOTED_STRING: + raise SyntaxError("whitespace after =") + value = token.value + elif equals > 0: + # key=value + key = token.value[:equals] + value = token.value[equals + 1 :] + elif equals == 0: + # =key + raise SyntaxError('parameter cannot start with "="') + else: + # key + key = token.value + value = None + _validate_and_define(params, key, value) + return cls(rdclass, rdtype, priority, target, params) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + file.write(struct.pack("!H", self.priority)) + self.target.to_wire(file, None, origin, False) + for key in sorted(self.params): + file.write(struct.pack("!H", key)) + value = self.params[key] + with dns.renderer.prefixed_length(file, 2): + # Note that we're still writing a length of zero if the value is None + if value is not None: + value.to_wire(file, origin) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + priority = parser.get_uint16() + target = parser.get_name(origin) + if priority == 0 and parser.remaining() != 0: + raise dns.exception.FormError("parameters in AliasMode") + params = {} + prior_key = -1 + while parser.remaining() > 0: + key = parser.get_uint16() + if key < prior_key: + raise dns.exception.FormError("keys not in order") + prior_key = key + vlen = parser.get_uint16() + pcls = _class_for_key.get(key, GenericParam) + with parser.restrict_to(vlen): + value = pcls.from_wire_parser(parser, origin) + params[key] = value + return cls(rdclass, rdtype, priority, target, params) + + def _processing_priority(self): + return self.priority + + @classmethod + def _processing_order(cls, iterable): + return dns.rdtypes.util.priority_processing_order(iterable) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/tlsabase.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/tlsabase.py new file mode 100644 index 0000000..a059d2c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/tlsabase.py @@ -0,0 +1,71 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2005-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import binascii +import struct + +import dns.immutable +import dns.rdata +import dns.rdatatype + + +@dns.immutable.immutable +class TLSABase(dns.rdata.Rdata): + """Base class for TLSA and SMIMEA records""" + + # see: RFC 6698 + + __slots__ = ["usage", "selector", "mtype", "cert"] + + def __init__(self, rdclass, rdtype, usage, selector, mtype, cert): + super().__init__(rdclass, rdtype) + self.usage = self._as_uint8(usage) + self.selector = self._as_uint8(selector) + self.mtype = self._as_uint8(mtype) + self.cert = self._as_bytes(cert) + + def to_text(self, origin=None, relativize=True, **kw): + kw = kw.copy() + chunksize = kw.pop("chunksize", 128) + return "%d %d %d %s" % ( + self.usage, + self.selector, + self.mtype, + dns.rdata._hexify(self.cert, chunksize=chunksize, **kw), + ) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + usage = tok.get_uint8() + selector = tok.get_uint8() + mtype = tok.get_uint8() + cert = tok.concatenate_remaining_identifiers().encode() + cert = binascii.unhexlify(cert) + return cls(rdclass, rdtype, usage, selector, mtype, cert) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + header = struct.pack("!BBB", self.usage, self.selector, self.mtype) + file.write(header) + file.write(self.cert) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + header = parser.get_struct("BBB") + cert = parser.get_remaining() + return cls(rdclass, rdtype, header[0], header[1], header[2], cert) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/txtbase.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/txtbase.py new file mode 100644 index 0000000..73db6d9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/txtbase.py @@ -0,0 +1,106 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2006-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""TXT-like base class.""" + +from typing import Any, Dict, Iterable, Optional, Tuple, Union + +import dns.exception +import dns.immutable +import dns.rdata +import dns.renderer +import dns.tokenizer + + +@dns.immutable.immutable +class TXTBase(dns.rdata.Rdata): + """Base class for rdata that is like a TXT record (see RFC 1035).""" + + __slots__ = ["strings"] + + def __init__( + self, + rdclass: dns.rdataclass.RdataClass, + rdtype: dns.rdatatype.RdataType, + strings: Iterable[Union[bytes, str]], + ): + """Initialize a TXT-like rdata. + + *rdclass*, an ``int`` is the rdataclass of the Rdata. + + *rdtype*, an ``int`` is the rdatatype of the Rdata. + + *strings*, a tuple of ``bytes`` + """ + super().__init__(rdclass, rdtype) + self.strings: Tuple[bytes] = self._as_tuple( + strings, lambda x: self._as_bytes(x, True, 255) + ) + if len(self.strings) == 0: + raise ValueError("the list of strings must not be empty") + + def to_text( + self, + origin: Optional[dns.name.Name] = None, + relativize: bool = True, + **kw: Dict[str, Any], + ) -> str: + txt = "" + prefix = "" + for s in self.strings: + txt += f'{prefix}"{dns.rdata._escapify(s)}"' + prefix = " " + return txt + + @classmethod + def from_text( + cls, + rdclass: dns.rdataclass.RdataClass, + rdtype: dns.rdatatype.RdataType, + tok: dns.tokenizer.Tokenizer, + origin: Optional[dns.name.Name] = None, + relativize: bool = True, + relativize_to: Optional[dns.name.Name] = None, + ) -> dns.rdata.Rdata: + strings = [] + for token in tok.get_remaining(): + token = token.unescape_to_bytes() + # The 'if' below is always true in the current code, but we + # are leaving this check in in case things change some day. + if not ( + token.is_quoted_string() or token.is_identifier() + ): # pragma: no cover + raise dns.exception.SyntaxError("expected a string") + if len(token.value) > 255: + raise dns.exception.SyntaxError("string too long") + strings.append(token.value) + if len(strings) == 0: + raise dns.exception.UnexpectedEnd + return cls(rdclass, rdtype, strings) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + for s in self.strings: + with dns.renderer.prefixed_length(file, 1): + file.write(s) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + strings = [] + while parser.remaining() > 0: + s = parser.get_counted_bytes() + strings.append(s) + return cls(rdclass, rdtype, strings) diff --git a/.venv/lib/python3.9/site-packages/dns/rdtypes/util.py b/.venv/lib/python3.9/site-packages/dns/rdtypes/util.py new file mode 100644 index 0000000..653a0bf --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rdtypes/util.py @@ -0,0 +1,257 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import collections +import random +import struct +from typing import Any, List + +import dns.exception +import dns.ipv4 +import dns.ipv6 +import dns.name +import dns.rdata + + +class Gateway: + """A helper class for the IPSECKEY gateway and AMTRELAY relay fields""" + + name = "" + + def __init__(self, type, gateway=None): + self.type = dns.rdata.Rdata._as_uint8(type) + self.gateway = gateway + self._check() + + @classmethod + def _invalid_type(cls, gateway_type): + return f"invalid {cls.name} type: {gateway_type}" + + def _check(self): + if self.type == 0: + if self.gateway not in (".", None): + raise SyntaxError(f"invalid {self.name} for type 0") + self.gateway = None + elif self.type == 1: + # check that it's OK + dns.ipv4.inet_aton(self.gateway) + elif self.type == 2: + # check that it's OK + dns.ipv6.inet_aton(self.gateway) + elif self.type == 3: + if not isinstance(self.gateway, dns.name.Name): + raise SyntaxError(f"invalid {self.name}; not a name") + else: + raise SyntaxError(self._invalid_type(self.type)) + + def to_text(self, origin=None, relativize=True): + if self.type == 0: + return "." + elif self.type in (1, 2): + return self.gateway + elif self.type == 3: + return str(self.gateway.choose_relativity(origin, relativize)) + else: + raise ValueError(self._invalid_type(self.type)) # pragma: no cover + + @classmethod + def from_text( + cls, gateway_type, tok, origin=None, relativize=True, relativize_to=None + ): + if gateway_type in (0, 1, 2): + gateway = tok.get_string() + elif gateway_type == 3: + gateway = tok.get_name(origin, relativize, relativize_to) + else: + raise dns.exception.SyntaxError( + cls._invalid_type(gateway_type) + ) # pragma: no cover + return cls(gateway_type, gateway) + + # pylint: disable=unused-argument + def to_wire(self, file, compress=None, origin=None, canonicalize=False): + if self.type == 0: + pass + elif self.type == 1: + file.write(dns.ipv4.inet_aton(self.gateway)) + elif self.type == 2: + file.write(dns.ipv6.inet_aton(self.gateway)) + elif self.type == 3: + self.gateway.to_wire(file, None, origin, False) + else: + raise ValueError(self._invalid_type(self.type)) # pragma: no cover + + # pylint: enable=unused-argument + + @classmethod + def from_wire_parser(cls, gateway_type, parser, origin=None): + if gateway_type == 0: + gateway = None + elif gateway_type == 1: + gateway = dns.ipv4.inet_ntoa(parser.get_bytes(4)) + elif gateway_type == 2: + gateway = dns.ipv6.inet_ntoa(parser.get_bytes(16)) + elif gateway_type == 3: + gateway = parser.get_name(origin) + else: + raise dns.exception.FormError(cls._invalid_type(gateway_type)) + return cls(gateway_type, gateway) + + +class Bitmap: + """A helper class for the NSEC/NSEC3/CSYNC type bitmaps""" + + type_name = "" + + def __init__(self, windows=None): + last_window = -1 + self.windows = windows + for window, bitmap in self.windows: + if not isinstance(window, int): + raise ValueError(f"bad {self.type_name} window type") + if window <= last_window: + raise ValueError(f"bad {self.type_name} window order") + if window > 256: + raise ValueError(f"bad {self.type_name} window number") + last_window = window + if not isinstance(bitmap, bytes): + raise ValueError(f"bad {self.type_name} octets type") + if len(bitmap) == 0 or len(bitmap) > 32: + raise ValueError(f"bad {self.type_name} octets") + + def to_text(self) -> str: + text = "" + for window, bitmap in self.windows: + bits = [] + for i, byte in enumerate(bitmap): + for j in range(0, 8): + if byte & (0x80 >> j): + rdtype = window * 256 + i * 8 + j + bits.append(dns.rdatatype.to_text(rdtype)) + text += " " + " ".join(bits) + return text + + @classmethod + def from_text(cls, tok: "dns.tokenizer.Tokenizer") -> "Bitmap": + rdtypes = [] + for token in tok.get_remaining(): + rdtype = dns.rdatatype.from_text(token.unescape().value) + if rdtype == 0: + raise dns.exception.SyntaxError(f"{cls.type_name} with bit 0") + rdtypes.append(rdtype) + return cls.from_rdtypes(rdtypes) + + @classmethod + def from_rdtypes(cls, rdtypes: List[dns.rdatatype.RdataType]) -> "Bitmap": + rdtypes = sorted(rdtypes) + window = 0 + octets = 0 + prior_rdtype = 0 + bitmap = bytearray(b"\0" * 32) + windows = [] + for rdtype in rdtypes: + if rdtype == prior_rdtype: + continue + prior_rdtype = rdtype + new_window = rdtype // 256 + if new_window != window: + if octets != 0: + windows.append((window, bytes(bitmap[0:octets]))) + bitmap = bytearray(b"\0" * 32) + window = new_window + offset = rdtype % 256 + byte = offset // 8 + bit = offset % 8 + octets = byte + 1 + bitmap[byte] = bitmap[byte] | (0x80 >> bit) + if octets != 0: + windows.append((window, bytes(bitmap[0:octets]))) + return cls(windows) + + def to_wire(self, file: Any) -> None: + for window, bitmap in self.windows: + file.write(struct.pack("!BB", window, len(bitmap))) + file.write(bitmap) + + @classmethod + def from_wire_parser(cls, parser: "dns.wire.Parser") -> "Bitmap": + windows = [] + while parser.remaining() > 0: + window = parser.get_uint8() + bitmap = parser.get_counted_bytes() + windows.append((window, bitmap)) + return cls(windows) + + +def _priority_table(items): + by_priority = collections.defaultdict(list) + for rdata in items: + by_priority[rdata._processing_priority()].append(rdata) + return by_priority + + +def priority_processing_order(iterable): + items = list(iterable) + if len(items) == 1: + return items + by_priority = _priority_table(items) + ordered = [] + for k in sorted(by_priority.keys()): + rdatas = by_priority[k] + random.shuffle(rdatas) + ordered.extend(rdatas) + return ordered + + +_no_weight = 0.1 + + +def weighted_processing_order(iterable): + items = list(iterable) + if len(items) == 1: + return items + by_priority = _priority_table(items) + ordered = [] + for k in sorted(by_priority.keys()): + rdatas = by_priority[k] + total = sum(rdata._processing_weight() or _no_weight for rdata in rdatas) + while len(rdatas) > 1: + r = random.uniform(0, total) + for n, rdata in enumerate(rdatas): # noqa: B007 + weight = rdata._processing_weight() or _no_weight + if weight > r: + break + r -= weight + total -= weight + ordered.append(rdata) # pylint: disable=undefined-loop-variable + del rdatas[n] # pylint: disable=undefined-loop-variable + ordered.append(rdatas[0]) + return ordered + + +def parse_formatted_hex(formatted, num_chunks, chunk_size, separator): + if len(formatted) != num_chunks * (chunk_size + 1) - 1: + raise ValueError("invalid formatted hex string") + value = b"" + for _ in range(num_chunks): + chunk = formatted[0:chunk_size] + value += int(chunk, 16).to_bytes(chunk_size // 2, "big") + formatted = formatted[chunk_size:] + if len(formatted) > 0 and formatted[0] != separator: + raise ValueError("invalid formatted hex string") + formatted = formatted[1:] + return value diff --git a/.venv/lib/python3.9/site-packages/dns/renderer.py b/.venv/lib/python3.9/site-packages/dns/renderer.py new file mode 100644 index 0000000..a77481f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/renderer.py @@ -0,0 +1,346 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2001-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Help for building DNS wire format messages""" + +import contextlib +import io +import random +import struct +import time + +import dns.exception +import dns.tsig + +QUESTION = 0 +ANSWER = 1 +AUTHORITY = 2 +ADDITIONAL = 3 + + +@contextlib.contextmanager +def prefixed_length(output, length_length): + output.write(b"\00" * length_length) + start = output.tell() + yield + end = output.tell() + length = end - start + if length > 0: + try: + output.seek(start - length_length) + try: + output.write(length.to_bytes(length_length, "big")) + except OverflowError: + raise dns.exception.FormError + finally: + output.seek(end) + + +class Renderer: + """Helper class for building DNS wire-format messages. + + Most applications can use the higher-level L{dns.message.Message} + class and its to_wire() method to generate wire-format messages. + This class is for those applications which need finer control + over the generation of messages. + + Typical use:: + + r = dns.renderer.Renderer(id=1, flags=0x80, max_size=512) + r.add_question(qname, qtype, qclass) + r.add_rrset(dns.renderer.ANSWER, rrset_1) + r.add_rrset(dns.renderer.ANSWER, rrset_2) + r.add_rrset(dns.renderer.AUTHORITY, ns_rrset) + r.add_rrset(dns.renderer.ADDITIONAL, ad_rrset_1) + r.add_rrset(dns.renderer.ADDITIONAL, ad_rrset_2) + r.add_edns(0, 0, 4096) + r.write_header() + r.add_tsig(keyname, secret, 300, 1, 0, '', request_mac) + wire = r.get_wire() + + If padding is going to be used, then the OPT record MUST be + written after everything else in the additional section except for + the TSIG (if any). + + output, an io.BytesIO, where rendering is written + + id: the message id + + flags: the message flags + + max_size: the maximum size of the message + + origin: the origin to use when rendering relative names + + compress: the compression table + + section: an int, the section currently being rendered + + counts: list of the number of RRs in each section + + mac: the MAC of the rendered message (if TSIG was used) + """ + + def __init__(self, id=None, flags=0, max_size=65535, origin=None): + """Initialize a new renderer.""" + + self.output = io.BytesIO() + if id is None: + self.id = random.randint(0, 65535) + else: + self.id = id + self.flags = flags + self.max_size = max_size + self.origin = origin + self.compress = {} + self.section = QUESTION + self.counts = [0, 0, 0, 0] + self.output.write(b"\x00" * 12) + self.mac = "" + self.reserved = 0 + self.was_padded = False + + def _rollback(self, where): + """Truncate the output buffer at offset *where*, and remove any + compression table entries that pointed beyond the truncation + point. + """ + + self.output.seek(where) + self.output.truncate() + keys_to_delete = [] + for k, v in self.compress.items(): + if v >= where: + keys_to_delete.append(k) + for k in keys_to_delete: + del self.compress[k] + + def _set_section(self, section): + """Set the renderer's current section. + + Sections must be rendered order: QUESTION, ANSWER, AUTHORITY, + ADDITIONAL. Sections may be empty. + + Raises dns.exception.FormError if an attempt was made to set + a section value less than the current section. + """ + + if self.section != section: + if self.section > section: + raise dns.exception.FormError + self.section = section + + @contextlib.contextmanager + def _track_size(self): + start = self.output.tell() + yield start + if self.output.tell() > self.max_size: + self._rollback(start) + raise dns.exception.TooBig + + @contextlib.contextmanager + def _temporarily_seek_to(self, where): + current = self.output.tell() + try: + self.output.seek(where) + yield + finally: + self.output.seek(current) + + def add_question(self, qname, rdtype, rdclass=dns.rdataclass.IN): + """Add a question to the message.""" + + self._set_section(QUESTION) + with self._track_size(): + qname.to_wire(self.output, self.compress, self.origin) + self.output.write(struct.pack("!HH", rdtype, rdclass)) + self.counts[QUESTION] += 1 + + def add_rrset(self, section, rrset, **kw): + """Add the rrset to the specified section. + + Any keyword arguments are passed on to the rdataset's to_wire() + routine. + """ + + self._set_section(section) + with self._track_size(): + n = rrset.to_wire(self.output, self.compress, self.origin, **kw) + self.counts[section] += n + + def add_rdataset(self, section, name, rdataset, **kw): + """Add the rdataset to the specified section, using the specified + name as the owner name. + + Any keyword arguments are passed on to the rdataset's to_wire() + routine. + """ + + self._set_section(section) + with self._track_size(): + n = rdataset.to_wire(name, self.output, self.compress, self.origin, **kw) + self.counts[section] += n + + def add_opt(self, opt, pad=0, opt_size=0, tsig_size=0): + """Add *opt* to the additional section, applying padding if desired. The + padding will take the specified precomputed OPT size and TSIG size into + account. + + Note that we don't have reliable way of knowing how big a GSS-TSIG digest + might be, so we we might not get an even multiple of the pad in that case.""" + if pad: + ttl = opt.ttl + assert opt_size >= 11 + opt_rdata = opt[0] + size_without_padding = self.output.tell() + opt_size + tsig_size + remainder = size_without_padding % pad + if remainder: + pad = b"\x00" * (pad - remainder) + else: + pad = b"" + options = list(opt_rdata.options) + options.append(dns.edns.GenericOption(dns.edns.OptionType.PADDING, pad)) + opt = dns.message.Message._make_opt(ttl, opt_rdata.rdclass, options) + self.was_padded = True + self.add_rrset(ADDITIONAL, opt) + + def add_edns(self, edns, ednsflags, payload, options=None): + """Add an EDNS OPT record to the message.""" + + # make sure the EDNS version in ednsflags agrees with edns + ednsflags &= 0xFF00FFFF + ednsflags |= edns << 16 + opt = dns.message.Message._make_opt(ednsflags, payload, options) + self.add_opt(opt) + + def add_tsig( + self, + keyname, + secret, + fudge, + id, + tsig_error, + other_data, + request_mac, + algorithm=dns.tsig.default_algorithm, + ): + """Add a TSIG signature to the message.""" + + s = self.output.getvalue() + + if isinstance(secret, dns.tsig.Key): + key = secret + else: + key = dns.tsig.Key(keyname, secret, algorithm) + tsig = dns.message.Message._make_tsig( + keyname, algorithm, 0, fudge, b"", id, tsig_error, other_data + ) + (tsig, _) = dns.tsig.sign(s, key, tsig[0], int(time.time()), request_mac) + self._write_tsig(tsig, keyname) + + def add_multi_tsig( + self, + ctx, + keyname, + secret, + fudge, + id, + tsig_error, + other_data, + request_mac, + algorithm=dns.tsig.default_algorithm, + ): + """Add a TSIG signature to the message. Unlike add_tsig(), this can be + used for a series of consecutive DNS envelopes, e.g. for a zone + transfer over TCP [RFC2845, 4.4]. + + For the first message in the sequence, give ctx=None. For each + subsequent message, give the ctx that was returned from the + add_multi_tsig() call for the previous message.""" + + s = self.output.getvalue() + + if isinstance(secret, dns.tsig.Key): + key = secret + else: + key = dns.tsig.Key(keyname, secret, algorithm) + tsig = dns.message.Message._make_tsig( + keyname, algorithm, 0, fudge, b"", id, tsig_error, other_data + ) + (tsig, ctx) = dns.tsig.sign( + s, key, tsig[0], int(time.time()), request_mac, ctx, True + ) + self._write_tsig(tsig, keyname) + return ctx + + def _write_tsig(self, tsig, keyname): + if self.was_padded: + compress = None + else: + compress = self.compress + self._set_section(ADDITIONAL) + with self._track_size(): + keyname.to_wire(self.output, compress, self.origin) + self.output.write( + struct.pack("!HHI", dns.rdatatype.TSIG, dns.rdataclass.ANY, 0) + ) + with prefixed_length(self.output, 2): + tsig.to_wire(self.output) + + self.counts[ADDITIONAL] += 1 + with self._temporarily_seek_to(10): + self.output.write(struct.pack("!H", self.counts[ADDITIONAL])) + + def write_header(self): + """Write the DNS message header. + + Writing the DNS message header is done after all sections + have been rendered, but before the optional TSIG signature + is added. + """ + + with self._temporarily_seek_to(0): + self.output.write( + struct.pack( + "!HHHHHH", + self.id, + self.flags, + self.counts[0], + self.counts[1], + self.counts[2], + self.counts[3], + ) + ) + + def get_wire(self): + """Return the wire format message.""" + + return self.output.getvalue() + + def reserve(self, size: int) -> None: + """Reserve *size* bytes.""" + if size < 0: + raise ValueError("reserved amount must be non-negative") + if size > self.max_size: + raise ValueError("cannot reserve more than the maximum size") + self.reserved += size + self.max_size -= size + + def release_reserved(self) -> None: + """Release the reserved bytes.""" + self.max_size += self.reserved + self.reserved = 0 diff --git a/.venv/lib/python3.9/site-packages/dns/resolver.py b/.venv/lib/python3.9/site-packages/dns/resolver.py new file mode 100644 index 0000000..3ba76e3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/resolver.py @@ -0,0 +1,2053 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS stub resolver.""" + +import contextlib +import random +import socket +import sys +import threading +import time +import warnings +from typing import Any, Dict, Iterator, List, Optional, Sequence, Tuple, Union +from urllib.parse import urlparse + +import dns._ddr +import dns.edns +import dns.exception +import dns.flags +import dns.inet +import dns.ipv4 +import dns.ipv6 +import dns.message +import dns.name +import dns.rdata +import dns.nameserver +import dns.query +import dns.rcode +import dns.rdataclass +import dns.rdatatype +import dns.rdtypes.svcbbase +import dns.reversename +import dns.tsig + +if sys.platform == "win32": # pragma: no cover + import dns.win32util + + +class NXDOMAIN(dns.exception.DNSException): + """The DNS query name does not exist.""" + + supp_kwargs = {"qnames", "responses"} + fmt = None # we have our own __str__ implementation + + # pylint: disable=arguments-differ + + # We do this as otherwise mypy complains about unexpected keyword argument + # idna_exception + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def _check_kwargs(self, qnames, responses=None): + if not isinstance(qnames, (list, tuple, set)): + raise AttributeError("qnames must be a list, tuple or set") + if len(qnames) == 0: + raise AttributeError("qnames must contain at least one element") + if responses is None: + responses = {} + elif not isinstance(responses, dict): + raise AttributeError("responses must be a dict(qname=response)") + kwargs = dict(qnames=qnames, responses=responses) + return kwargs + + def __str__(self) -> str: + if "qnames" not in self.kwargs: + return super().__str__() + qnames = self.kwargs["qnames"] + if len(qnames) > 1: + msg = "None of DNS query names exist" + else: + msg = "The DNS query name does not exist" + qnames = ", ".join(map(str, qnames)) + return f"{msg}: {qnames}" + + @property + def canonical_name(self): + """Return the unresolved canonical name.""" + if "qnames" not in self.kwargs: + raise TypeError("parametrized exception required") + for qname in self.kwargs["qnames"]: + response = self.kwargs["responses"][qname] + try: + cname = response.canonical_name() + if cname != qname: + return cname + except Exception: # pragma: no cover + # We can just eat this exception as it means there was + # something wrong with the response. + pass + return self.kwargs["qnames"][0] + + def __add__(self, e_nx): + """Augment by results from another NXDOMAIN exception.""" + qnames0 = list(self.kwargs.get("qnames", [])) + responses0 = dict(self.kwargs.get("responses", {})) + responses1 = e_nx.kwargs.get("responses", {}) + for qname1 in e_nx.kwargs.get("qnames", []): + if qname1 not in qnames0: + qnames0.append(qname1) + if qname1 in responses1: + responses0[qname1] = responses1[qname1] + return NXDOMAIN(qnames=qnames0, responses=responses0) + + def qnames(self): + """All of the names that were tried. + + Returns a list of ``dns.name.Name``. + """ + return self.kwargs["qnames"] + + def responses(self): + """A map from queried names to their NXDOMAIN responses. + + Returns a dict mapping a ``dns.name.Name`` to a + ``dns.message.Message``. + """ + return self.kwargs["responses"] + + def response(self, qname): + """The response for query *qname*. + + Returns a ``dns.message.Message``. + """ + return self.kwargs["responses"][qname] + + +class YXDOMAIN(dns.exception.DNSException): + """The DNS query name is too long after DNAME substitution.""" + + +ErrorTuple = Tuple[ + Optional[str], + bool, + int, + Union[Exception, str], + Optional[dns.message.Message], +] + + +def _errors_to_text(errors: List[ErrorTuple]) -> List[str]: + """Turn a resolution errors trace into a list of text.""" + texts = [] + for err in errors: + texts.append(f"Server {err[0]} answered {err[3]}") + return texts + + +class LifetimeTimeout(dns.exception.Timeout): + """The resolution lifetime expired.""" + + msg = "The resolution lifetime expired." + fmt = f"{msg[:-1]} after {{timeout:.3f}} seconds: {{errors}}" + supp_kwargs = {"timeout", "errors"} + + # We do this as otherwise mypy complains about unexpected keyword argument + # idna_exception + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def _fmt_kwargs(self, **kwargs): + srv_msgs = _errors_to_text(kwargs["errors"]) + return super()._fmt_kwargs( + timeout=kwargs["timeout"], errors="; ".join(srv_msgs) + ) + + +# We added more detail to resolution timeouts, but they are still +# subclasses of dns.exception.Timeout for backwards compatibility. We also +# keep dns.resolver.Timeout defined for backwards compatibility. +Timeout = LifetimeTimeout + + +class NoAnswer(dns.exception.DNSException): + """The DNS response does not contain an answer to the question.""" + + fmt = "The DNS response does not contain an answer to the question: {query}" + supp_kwargs = {"response"} + + # We do this as otherwise mypy complains about unexpected keyword argument + # idna_exception + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def _fmt_kwargs(self, **kwargs): + return super()._fmt_kwargs(query=kwargs["response"].question) + + def response(self): + return self.kwargs["response"] + + +class NoNameservers(dns.exception.DNSException): + """All nameservers failed to answer the query. + + errors: list of servers and respective errors + The type of errors is + [(server IP address, any object convertible to string)]. + Non-empty errors list will add explanatory message () + """ + + msg = "All nameservers failed to answer the query." + fmt = f"{msg[:-1]} {{query}}: {{errors}}" + supp_kwargs = {"request", "errors"} + + # We do this as otherwise mypy complains about unexpected keyword argument + # idna_exception + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def _fmt_kwargs(self, **kwargs): + srv_msgs = _errors_to_text(kwargs["errors"]) + return super()._fmt_kwargs( + query=kwargs["request"].question, errors="; ".join(srv_msgs) + ) + + +class NotAbsolute(dns.exception.DNSException): + """An absolute domain name is required but a relative name was provided.""" + + +class NoRootSOA(dns.exception.DNSException): + """There is no SOA RR at the DNS root name. This should never happen!""" + + +class NoMetaqueries(dns.exception.DNSException): + """DNS metaqueries are not allowed.""" + + +class NoResolverConfiguration(dns.exception.DNSException): + """Resolver configuration could not be read or specified no nameservers.""" + + +class Answer: + """DNS stub resolver answer. + + Instances of this class bundle up the result of a successful DNS + resolution. + + For convenience, the answer object implements much of the sequence + protocol, forwarding to its ``rrset`` attribute. E.g. + ``for a in answer`` is equivalent to ``for a in answer.rrset``. + ``answer[i]`` is equivalent to ``answer.rrset[i]``, and + ``answer[i:j]`` is equivalent to ``answer.rrset[i:j]``. + + Note that CNAMEs or DNAMEs in the response may mean that answer + RRset's name might not be the query name. + """ + + def __init__( + self, + qname: dns.name.Name, + rdtype: dns.rdatatype.RdataType, + rdclass: dns.rdataclass.RdataClass, + response: dns.message.QueryMessage, + nameserver: Optional[str] = None, + port: Optional[int] = None, + ) -> None: + self.qname = qname + self.rdtype = rdtype + self.rdclass = rdclass + self.response = response + self.nameserver = nameserver + self.port = port + self.chaining_result = response.resolve_chaining() + # Copy some attributes out of chaining_result for backwards + # compatibility and convenience. + self.canonical_name = self.chaining_result.canonical_name + self.rrset = self.chaining_result.answer + self.expiration = time.time() + self.chaining_result.minimum_ttl + + def __getattr__(self, attr): # pragma: no cover + if attr == "name": + return self.rrset.name + elif attr == "ttl": + return self.rrset.ttl + elif attr == "covers": + return self.rrset.covers + elif attr == "rdclass": + return self.rrset.rdclass + elif attr == "rdtype": + return self.rrset.rdtype + else: + raise AttributeError(attr) + + def __len__(self) -> int: + return self.rrset and len(self.rrset) or 0 + + def __iter__(self) -> Iterator[dns.rdata.Rdata]: + return self.rrset and iter(self.rrset) or iter(tuple()) + + def __getitem__(self, i): + if self.rrset is None: + raise IndexError + return self.rrset[i] + + def __delitem__(self, i): + if self.rrset is None: + raise IndexError + del self.rrset[i] + + +class Answers(dict): + """A dict of DNS stub resolver answers, indexed by type.""" + + +class HostAnswers(Answers): + """A dict of DNS stub resolver answers to a host name lookup, indexed by + type. + """ + + @classmethod + def make( + cls, + v6: Optional[Answer] = None, + v4: Optional[Answer] = None, + add_empty: bool = True, + ) -> "HostAnswers": + answers = HostAnswers() + if v6 is not None and (add_empty or v6.rrset): + answers[dns.rdatatype.AAAA] = v6 + if v4 is not None and (add_empty or v4.rrset): + answers[dns.rdatatype.A] = v4 + return answers + + # Returns pairs of (address, family) from this result, potentially + # filtering by address family. + def addresses_and_families( + self, family: int = socket.AF_UNSPEC + ) -> Iterator[Tuple[str, int]]: + if family == socket.AF_UNSPEC: + yield from self.addresses_and_families(socket.AF_INET6) + yield from self.addresses_and_families(socket.AF_INET) + return + elif family == socket.AF_INET6: + answer = self.get(dns.rdatatype.AAAA) + elif family == socket.AF_INET: + answer = self.get(dns.rdatatype.A) + else: # pragma: no cover + raise NotImplementedError(f"unknown address family {family}") + if answer: + for rdata in answer: + yield (rdata.address, family) + + # Returns addresses from this result, potentially filtering by + # address family. + def addresses(self, family: int = socket.AF_UNSPEC) -> Iterator[str]: + return (pair[0] for pair in self.addresses_and_families(family)) + + # Returns the canonical name from this result. + def canonical_name(self) -> dns.name.Name: + answer = self.get(dns.rdatatype.AAAA, self.get(dns.rdatatype.A)) + return answer.canonical_name + + +class CacheStatistics: + """Cache Statistics""" + + def __init__(self, hits: int = 0, misses: int = 0) -> None: + self.hits = hits + self.misses = misses + + def reset(self) -> None: + self.hits = 0 + self.misses = 0 + + def clone(self) -> "CacheStatistics": + return CacheStatistics(self.hits, self.misses) + + +class CacheBase: + def __init__(self) -> None: + self.lock = threading.Lock() + self.statistics = CacheStatistics() + + def reset_statistics(self) -> None: + """Reset all statistics to zero.""" + with self.lock: + self.statistics.reset() + + def hits(self) -> int: + """How many hits has the cache had?""" + with self.lock: + return self.statistics.hits + + def misses(self) -> int: + """How many misses has the cache had?""" + with self.lock: + return self.statistics.misses + + def get_statistics_snapshot(self) -> CacheStatistics: + """Return a consistent snapshot of all the statistics. + + If running with multiple threads, it's better to take a + snapshot than to call statistics methods such as hits() and + misses() individually. + """ + with self.lock: + return self.statistics.clone() + + +CacheKey = Tuple[dns.name.Name, dns.rdatatype.RdataType, dns.rdataclass.RdataClass] + + +class Cache(CacheBase): + """Simple thread-safe DNS answer cache.""" + + def __init__(self, cleaning_interval: float = 300.0) -> None: + """*cleaning_interval*, a ``float`` is the number of seconds between + periodic cleanings. + """ + + super().__init__() + self.data: Dict[CacheKey, Answer] = {} + self.cleaning_interval = cleaning_interval + self.next_cleaning: float = time.time() + self.cleaning_interval + + def _maybe_clean(self) -> None: + """Clean the cache if it's time to do so.""" + + now = time.time() + if self.next_cleaning <= now: + keys_to_delete = [] + for k, v in self.data.items(): + if v.expiration <= now: + keys_to_delete.append(k) + for k in keys_to_delete: + del self.data[k] + now = time.time() + self.next_cleaning = now + self.cleaning_interval + + def get(self, key: CacheKey) -> Optional[Answer]: + """Get the answer associated with *key*. + + Returns None if no answer is cached for the key. + + *key*, a ``(dns.name.Name, dns.rdatatype.RdataType, dns.rdataclass.RdataClass)`` + tuple whose values are the query name, rdtype, and rdclass respectively. + + Returns a ``dns.resolver.Answer`` or ``None``. + """ + + with self.lock: + self._maybe_clean() + v = self.data.get(key) + if v is None or v.expiration <= time.time(): + self.statistics.misses += 1 + return None + self.statistics.hits += 1 + return v + + def put(self, key: CacheKey, value: Answer) -> None: + """Associate key and value in the cache. + + *key*, a ``(dns.name.Name, dns.rdatatype.RdataType, dns.rdataclass.RdataClass)`` + tuple whose values are the query name, rdtype, and rdclass respectively. + + *value*, a ``dns.resolver.Answer``, the answer. + """ + + with self.lock: + self._maybe_clean() + self.data[key] = value + + def flush(self, key: Optional[CacheKey] = None) -> None: + """Flush the cache. + + If *key* is not ``None``, only that item is flushed. Otherwise the entire cache + is flushed. + + *key*, a ``(dns.name.Name, dns.rdatatype.RdataType, dns.rdataclass.RdataClass)`` + tuple whose values are the query name, rdtype, and rdclass respectively. + """ + + with self.lock: + if key is not None: + if key in self.data: + del self.data[key] + else: + self.data = {} + self.next_cleaning = time.time() + self.cleaning_interval + + +class LRUCacheNode: + """LRUCache node.""" + + def __init__(self, key, value): + self.key = key + self.value = value + self.hits = 0 + self.prev = self + self.next = self + + def link_after(self, node: "LRUCacheNode") -> None: + self.prev = node + self.next = node.next + node.next.prev = self + node.next = self + + def unlink(self) -> None: + self.next.prev = self.prev + self.prev.next = self.next + + +class LRUCache(CacheBase): + """Thread-safe, bounded, least-recently-used DNS answer cache. + + This cache is better than the simple cache (above) if you're + running a web crawler or other process that does a lot of + resolutions. The LRUCache has a maximum number of nodes, and when + it is full, the least-recently used node is removed to make space + for a new one. + """ + + def __init__(self, max_size: int = 100000) -> None: + """*max_size*, an ``int``, is the maximum number of nodes to cache; + it must be greater than 0. + """ + + super().__init__() + self.data: Dict[CacheKey, LRUCacheNode] = {} + self.set_max_size(max_size) + self.sentinel: LRUCacheNode = LRUCacheNode(None, None) + self.sentinel.prev = self.sentinel + self.sentinel.next = self.sentinel + + def set_max_size(self, max_size: int) -> None: + if max_size < 1: + max_size = 1 + self.max_size = max_size + + def get(self, key: CacheKey) -> Optional[Answer]: + """Get the answer associated with *key*. + + Returns None if no answer is cached for the key. + + *key*, a ``(dns.name.Name, dns.rdatatype.RdataType, dns.rdataclass.RdataClass)`` + tuple whose values are the query name, rdtype, and rdclass respectively. + + Returns a ``dns.resolver.Answer`` or ``None``. + """ + + with self.lock: + node = self.data.get(key) + if node is None: + self.statistics.misses += 1 + return None + # Unlink because we're either going to move the node to the front + # of the LRU list or we're going to free it. + node.unlink() + if node.value.expiration <= time.time(): + del self.data[node.key] + self.statistics.misses += 1 + return None + node.link_after(self.sentinel) + self.statistics.hits += 1 + node.hits += 1 + return node.value + + def get_hits_for_key(self, key: CacheKey) -> int: + """Return the number of cache hits associated with the specified key.""" + with self.lock: + node = self.data.get(key) + if node is None or node.value.expiration <= time.time(): + return 0 + else: + return node.hits + + def put(self, key: CacheKey, value: Answer) -> None: + """Associate key and value in the cache. + + *key*, a ``(dns.name.Name, dns.rdatatype.RdataType, dns.rdataclass.RdataClass)`` + tuple whose values are the query name, rdtype, and rdclass respectively. + + *value*, a ``dns.resolver.Answer``, the answer. + """ + + with self.lock: + node = self.data.get(key) + if node is not None: + node.unlink() + del self.data[node.key] + while len(self.data) >= self.max_size: + gnode = self.sentinel.prev + gnode.unlink() + del self.data[gnode.key] + node = LRUCacheNode(key, value) + node.link_after(self.sentinel) + self.data[key] = node + + def flush(self, key: Optional[CacheKey] = None) -> None: + """Flush the cache. + + If *key* is not ``None``, only that item is flushed. Otherwise the entire cache + is flushed. + + *key*, a ``(dns.name.Name, dns.rdatatype.RdataType, dns.rdataclass.RdataClass)`` + tuple whose values are the query name, rdtype, and rdclass respectively. + """ + + with self.lock: + if key is not None: + node = self.data.get(key) + if node is not None: + node.unlink() + del self.data[node.key] + else: + gnode = self.sentinel.next + while gnode != self.sentinel: + next = gnode.next + gnode.unlink() + gnode = next + self.data = {} + + +class _Resolution: + """Helper class for dns.resolver.Resolver.resolve(). + + All of the "business logic" of resolution is encapsulated in this + class, allowing us to have multiple resolve() implementations + using different I/O schemes without copying all of the + complicated logic. + + This class is a "friend" to dns.resolver.Resolver and manipulates + resolver data structures directly. + """ + + def __init__( + self, + resolver: "BaseResolver", + qname: Union[dns.name.Name, str], + rdtype: Union[dns.rdatatype.RdataType, str], + rdclass: Union[dns.rdataclass.RdataClass, str], + tcp: bool, + raise_on_no_answer: bool, + search: Optional[bool], + ) -> None: + if isinstance(qname, str): + qname = dns.name.from_text(qname, None) + rdtype = dns.rdatatype.RdataType.make(rdtype) + if dns.rdatatype.is_metatype(rdtype): + raise NoMetaqueries + rdclass = dns.rdataclass.RdataClass.make(rdclass) + if dns.rdataclass.is_metaclass(rdclass): + raise NoMetaqueries + self.resolver = resolver + self.qnames_to_try = resolver._get_qnames_to_try(qname, search) + self.qnames = self.qnames_to_try[:] + self.rdtype = rdtype + self.rdclass = rdclass + self.tcp = tcp + self.raise_on_no_answer = raise_on_no_answer + self.nxdomain_responses: Dict[dns.name.Name, dns.message.QueryMessage] = {} + # Initialize other things to help analysis tools + self.qname = dns.name.empty + self.nameservers: List[dns.nameserver.Nameserver] = [] + self.current_nameservers: List[dns.nameserver.Nameserver] = [] + self.errors: List[ErrorTuple] = [] + self.nameserver: Optional[dns.nameserver.Nameserver] = None + self.tcp_attempt = False + self.retry_with_tcp = False + self.request: Optional[dns.message.QueryMessage] = None + self.backoff = 0.0 + + def next_request( + self, + ) -> Tuple[Optional[dns.message.QueryMessage], Optional[Answer]]: + """Get the next request to send, and check the cache. + + Returns a (request, answer) tuple. At most one of request or + answer will not be None. + """ + + # We return a tuple instead of Union[Message,Answer] as it lets + # the caller avoid isinstance(). + + while len(self.qnames) > 0: + self.qname = self.qnames.pop(0) + + # Do we know the answer? + if self.resolver.cache: + answer = self.resolver.cache.get( + (self.qname, self.rdtype, self.rdclass) + ) + if answer is not None: + if answer.rrset is None and self.raise_on_no_answer: + raise NoAnswer(response=answer.response) + else: + return (None, answer) + answer = self.resolver.cache.get( + (self.qname, dns.rdatatype.ANY, self.rdclass) + ) + if answer is not None and answer.response.rcode() == dns.rcode.NXDOMAIN: + # cached NXDOMAIN; record it and continue to next + # name. + self.nxdomain_responses[self.qname] = answer.response + continue + + # Build the request + request = dns.message.make_query(self.qname, self.rdtype, self.rdclass) + if self.resolver.keyname is not None: + request.use_tsig( + self.resolver.keyring, + self.resolver.keyname, + algorithm=self.resolver.keyalgorithm, + ) + request.use_edns( + self.resolver.edns, + self.resolver.ednsflags, + self.resolver.payload, + options=self.resolver.ednsoptions, + ) + if self.resolver.flags is not None: + request.flags = self.resolver.flags + + self.nameservers = self.resolver._enrich_nameservers( + self.resolver._nameservers, + self.resolver.nameserver_ports, + self.resolver.port, + ) + if self.resolver.rotate: + random.shuffle(self.nameservers) + self.current_nameservers = self.nameservers[:] + self.errors = [] + self.nameserver = None + self.tcp_attempt = False + self.retry_with_tcp = False + self.request = request + self.backoff = 0.10 + + return (request, None) + + # + # We've tried everything and only gotten NXDOMAINs. (We know + # it's only NXDOMAINs as anything else would have returned + # before now.) + # + raise NXDOMAIN(qnames=self.qnames_to_try, responses=self.nxdomain_responses) + + def next_nameserver(self) -> Tuple[dns.nameserver.Nameserver, bool, float]: + if self.retry_with_tcp: + assert self.nameserver is not None + assert not self.nameserver.is_always_max_size() + self.tcp_attempt = True + self.retry_with_tcp = False + return (self.nameserver, True, 0) + + backoff = 0.0 + if not self.current_nameservers: + if len(self.nameservers) == 0: + # Out of things to try! + raise NoNameservers(request=self.request, errors=self.errors) + self.current_nameservers = self.nameservers[:] + backoff = self.backoff + self.backoff = min(self.backoff * 2, 2) + + self.nameserver = self.current_nameservers.pop(0) + self.tcp_attempt = self.tcp or self.nameserver.is_always_max_size() + return (self.nameserver, self.tcp_attempt, backoff) + + def query_result( + self, response: Optional[dns.message.Message], ex: Optional[Exception] + ) -> Tuple[Optional[Answer], bool]: + # + # returns an (answer: Answer, end_loop: bool) tuple. + # + assert self.nameserver is not None + if ex: + # Exception during I/O or from_wire() + assert response is None + self.errors.append( + ( + str(self.nameserver), + self.tcp_attempt, + self.nameserver.answer_port(), + ex, + response, + ) + ) + if ( + isinstance(ex, dns.exception.FormError) + or isinstance(ex, EOFError) + or isinstance(ex, OSError) + or isinstance(ex, NotImplementedError) + ): + # This nameserver is no good, take it out of the mix. + self.nameservers.remove(self.nameserver) + elif isinstance(ex, dns.message.Truncated): + if self.tcp_attempt: + # Truncation with TCP is no good! + self.nameservers.remove(self.nameserver) + else: + self.retry_with_tcp = True + return (None, False) + # We got an answer! + assert response is not None + assert isinstance(response, dns.message.QueryMessage) + rcode = response.rcode() + if rcode == dns.rcode.NOERROR: + try: + answer = Answer( + self.qname, + self.rdtype, + self.rdclass, + response, + self.nameserver.answer_nameserver(), + self.nameserver.answer_port(), + ) + except Exception as e: + self.errors.append( + ( + str(self.nameserver), + self.tcp_attempt, + self.nameserver.answer_port(), + e, + response, + ) + ) + # The nameserver is no good, take it out of the mix. + self.nameservers.remove(self.nameserver) + return (None, False) + if self.resolver.cache: + self.resolver.cache.put((self.qname, self.rdtype, self.rdclass), answer) + if answer.rrset is None and self.raise_on_no_answer: + raise NoAnswer(response=answer.response) + return (answer, True) + elif rcode == dns.rcode.NXDOMAIN: + # Further validate the response by making an Answer, even + # if we aren't going to cache it. + try: + answer = Answer( + self.qname, dns.rdatatype.ANY, dns.rdataclass.IN, response + ) + except Exception as e: + self.errors.append( + ( + str(self.nameserver), + self.tcp_attempt, + self.nameserver.answer_port(), + e, + response, + ) + ) + # The nameserver is no good, take it out of the mix. + self.nameservers.remove(self.nameserver) + return (None, False) + self.nxdomain_responses[self.qname] = response + if self.resolver.cache: + self.resolver.cache.put( + (self.qname, dns.rdatatype.ANY, self.rdclass), answer + ) + # Make next_nameserver() return None, so caller breaks its + # inner loop and calls next_request(). + return (None, True) + elif rcode == dns.rcode.YXDOMAIN: + yex = YXDOMAIN() + self.errors.append( + ( + str(self.nameserver), + self.tcp_attempt, + self.nameserver.answer_port(), + yex, + response, + ) + ) + raise yex + else: + # + # We got a response, but we're not happy with the + # rcode in it. + # + if rcode != dns.rcode.SERVFAIL or not self.resolver.retry_servfail: + self.nameservers.remove(self.nameserver) + self.errors.append( + ( + str(self.nameserver), + self.tcp_attempt, + self.nameserver.answer_port(), + dns.rcode.to_text(rcode), + response, + ) + ) + return (None, False) + + +class BaseResolver: + """DNS stub resolver.""" + + # We initialize in reset() + # + # pylint: disable=attribute-defined-outside-init + + domain: dns.name.Name + nameserver_ports: Dict[str, int] + port: int + search: List[dns.name.Name] + use_search_by_default: bool + timeout: float + lifetime: float + keyring: Optional[Any] + keyname: Optional[Union[dns.name.Name, str]] + keyalgorithm: Union[dns.name.Name, str] + edns: int + ednsflags: int + ednsoptions: Optional[List[dns.edns.Option]] + payload: int + cache: Any + flags: Optional[int] + retry_servfail: bool + rotate: bool + ndots: Optional[int] + _nameservers: Sequence[Union[str, dns.nameserver.Nameserver]] + + def __init__( + self, filename: str = "/etc/resolv.conf", configure: bool = True + ) -> None: + """*filename*, a ``str`` or file object, specifying a file + in standard /etc/resolv.conf format. This parameter is meaningful + only when *configure* is true and the platform is POSIX. + + *configure*, a ``bool``. If True (the default), the resolver + instance is configured in the normal fashion for the operating + system the resolver is running on. (I.e. by reading a + /etc/resolv.conf file on POSIX systems and from the registry + on Windows systems.) + """ + + self.reset() + if configure: + if sys.platform == "win32": # pragma: no cover + self.read_registry() + elif filename: + self.read_resolv_conf(filename) + + def reset(self) -> None: + """Reset all resolver configuration to the defaults.""" + + self.domain = dns.name.Name(dns.name.from_text(socket.gethostname())[1:]) + if len(self.domain) == 0: # pragma: no cover + self.domain = dns.name.root + self._nameservers = [] + self.nameserver_ports = {} + self.port = 53 + self.search = [] + self.use_search_by_default = False + self.timeout = 2.0 + self.lifetime = 5.0 + self.keyring = None + self.keyname = None + self.keyalgorithm = dns.tsig.default_algorithm + self.edns = -1 + self.ednsflags = 0 + self.ednsoptions = None + self.payload = 0 + self.cache = None + self.flags = None + self.retry_servfail = False + self.rotate = False + self.ndots = None + + def read_resolv_conf(self, f: Any) -> None: + """Process *f* as a file in the /etc/resolv.conf format. If f is + a ``str``, it is used as the name of the file to open; otherwise it + is treated as the file itself. + + Interprets the following items: + + - nameserver - name server IP address + + - domain - local domain name + + - search - search list for host-name lookup + + - options - supported options are rotate, timeout, edns0, and ndots + + """ + + nameservers = [] + if isinstance(f, str): + try: + cm: contextlib.AbstractContextManager = open(f) + except OSError: + # /etc/resolv.conf doesn't exist, can't be read, etc. + raise NoResolverConfiguration(f"cannot open {f}") + else: + cm = contextlib.nullcontext(f) + with cm as f: + for l in f: + if len(l) == 0 or l[0] == "#" or l[0] == ";": + continue + tokens = l.split() + + # Any line containing less than 2 tokens is malformed + if len(tokens) < 2: + continue + + if tokens[0] == "nameserver": + nameservers.append(tokens[1]) + elif tokens[0] == "domain": + self.domain = dns.name.from_text(tokens[1]) + # domain and search are exclusive + self.search = [] + elif tokens[0] == "search": + # the last search wins + self.search = [] + for suffix in tokens[1:]: + self.search.append(dns.name.from_text(suffix)) + # We don't set domain as it is not used if + # len(self.search) > 0 + elif tokens[0] == "options": + for opt in tokens[1:]: + if opt == "rotate": + self.rotate = True + elif opt == "edns0": + self.use_edns() + elif "timeout" in opt: + try: + self.timeout = int(opt.split(":")[1]) + except (ValueError, IndexError): + pass + elif "ndots" in opt: + try: + self.ndots = int(opt.split(":")[1]) + except (ValueError, IndexError): + pass + if len(nameservers) == 0: + raise NoResolverConfiguration("no nameservers") + # Assigning directly instead of appending means we invoke the + # setter logic, with additonal checking and enrichment. + self.nameservers = nameservers + + def read_registry(self) -> None: # pragma: no cover + """Extract resolver configuration from the Windows registry.""" + try: + info = dns.win32util.get_dns_info() # type: ignore + if info.domain is not None: + self.domain = info.domain + self.nameservers = info.nameservers + self.search = info.search + except AttributeError: + raise NotImplementedError + + def _compute_timeout( + self, + start: float, + lifetime: Optional[float] = None, + errors: Optional[List[ErrorTuple]] = None, + ) -> float: + lifetime = self.lifetime if lifetime is None else lifetime + now = time.time() + duration = now - start + if errors is None: + errors = [] + if duration < 0: + if duration < -1: + # Time going backwards is bad. Just give up. + raise LifetimeTimeout(timeout=duration, errors=errors) + else: + # Time went backwards, but only a little. This can + # happen, e.g. under vmware with older linux kernels. + # Pretend it didn't happen. + duration = 0 + if duration >= lifetime: + raise LifetimeTimeout(timeout=duration, errors=errors) + return min(lifetime - duration, self.timeout) + + def _get_qnames_to_try( + self, qname: dns.name.Name, search: Optional[bool] + ) -> List[dns.name.Name]: + # This is a separate method so we can unit test the search + # rules without requiring the Internet. + if search is None: + search = self.use_search_by_default + qnames_to_try = [] + if qname.is_absolute(): + qnames_to_try.append(qname) + else: + abs_qname = qname.concatenate(dns.name.root) + if search: + if len(self.search) > 0: + # There is a search list, so use it exclusively + search_list = self.search[:] + elif self.domain != dns.name.root and self.domain is not None: + # We have some notion of a domain that isn't the root, so + # use it as the search list. + search_list = [self.domain] + else: + search_list = [] + # Figure out the effective ndots (default is 1) + if self.ndots is None: + ndots = 1 + else: + ndots = self.ndots + for suffix in search_list: + qnames_to_try.append(qname + suffix) + if len(qname) > ndots: + # The name has at least ndots dots, so we should try an + # absolute query first. + qnames_to_try.insert(0, abs_qname) + else: + # The name has less than ndots dots, so we should search + # first, then try the absolute name. + qnames_to_try.append(abs_qname) + else: + qnames_to_try.append(abs_qname) + return qnames_to_try + + def use_tsig( + self, + keyring: Any, + keyname: Optional[Union[dns.name.Name, str]] = None, + algorithm: Union[dns.name.Name, str] = dns.tsig.default_algorithm, + ) -> None: + """Add a TSIG signature to each query. + + The parameters are passed to ``dns.message.Message.use_tsig()``; + see its documentation for details. + """ + + self.keyring = keyring + self.keyname = keyname + self.keyalgorithm = algorithm + + def use_edns( + self, + edns: Optional[Union[int, bool]] = 0, + ednsflags: int = 0, + payload: int = dns.message.DEFAULT_EDNS_PAYLOAD, + options: Optional[List[dns.edns.Option]] = None, + ) -> None: + """Configure EDNS behavior. + + *edns*, an ``int``, is the EDNS level to use. Specifying + ``None``, ``False``, or ``-1`` means "do not use EDNS", and in this case + the other parameters are ignored. Specifying ``True`` is + equivalent to specifying 0, i.e. "use EDNS0". + + *ednsflags*, an ``int``, the EDNS flag values. + + *payload*, an ``int``, is the EDNS sender's payload field, which is the + maximum size of UDP datagram the sender can handle. I.e. how big + a response to this message can be. + + *options*, a list of ``dns.edns.Option`` objects or ``None``, the EDNS + options. + """ + + if edns is None or edns is False: + edns = -1 + elif edns is True: + edns = 0 + self.edns = edns + self.ednsflags = ednsflags + self.payload = payload + self.ednsoptions = options + + def set_flags(self, flags: int) -> None: + """Overrides the default flags with your own. + + *flags*, an ``int``, the message flags to use. + """ + + self.flags = flags + + @classmethod + def _enrich_nameservers( + cls, + nameservers: Sequence[Union[str, dns.nameserver.Nameserver]], + nameserver_ports: Dict[str, int], + default_port: int, + ) -> List[dns.nameserver.Nameserver]: + enriched_nameservers = [] + if isinstance(nameservers, list): + for nameserver in nameservers: + enriched_nameserver: dns.nameserver.Nameserver + if isinstance(nameserver, dns.nameserver.Nameserver): + enriched_nameserver = nameserver + elif dns.inet.is_address(nameserver): + port = nameserver_ports.get(nameserver, default_port) + enriched_nameserver = dns.nameserver.Do53Nameserver( + nameserver, port + ) + else: + try: + if urlparse(nameserver).scheme != "https": + raise NotImplementedError + except Exception: + raise ValueError( + f"nameserver {nameserver} is not a " + "dns.nameserver.Nameserver instance or text form, " + "IP address, nor a valid https URL" + ) + enriched_nameserver = dns.nameserver.DoHNameserver(nameserver) + enriched_nameservers.append(enriched_nameserver) + else: + raise ValueError( + f"nameservers must be a list or tuple (not a {type(nameservers)})" + ) + return enriched_nameservers + + @property + def nameservers( + self, + ) -> Sequence[Union[str, dns.nameserver.Nameserver]]: + return self._nameservers + + @nameservers.setter + def nameservers( + self, nameservers: Sequence[Union[str, dns.nameserver.Nameserver]] + ) -> None: + """ + *nameservers*, a ``list`` of nameservers, where a nameserver is either + a string interpretable as a nameserver, or a ``dns.nameserver.Nameserver`` + instance. + + Raises ``ValueError`` if *nameservers* is not a list of nameservers. + """ + # We just call _enrich_nameservers() for checking + self._enrich_nameservers(nameservers, self.nameserver_ports, self.port) + self._nameservers = nameservers + + +class Resolver(BaseResolver): + """DNS stub resolver.""" + + def resolve( + self, + qname: Union[dns.name.Name, str], + rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, + rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, + tcp: bool = False, + source: Optional[str] = None, + raise_on_no_answer: bool = True, + source_port: int = 0, + lifetime: Optional[float] = None, + search: Optional[bool] = None, + ) -> Answer: # pylint: disable=arguments-differ + """Query nameservers to find the answer to the question. + + The *qname*, *rdtype*, and *rdclass* parameters may be objects + of the appropriate type, or strings that can be converted into objects + of the appropriate type. + + *qname*, a ``dns.name.Name`` or ``str``, the query name. + + *rdtype*, an ``int`` or ``str``, the query type. + + *rdclass*, an ``int`` or ``str``, the query class. + + *tcp*, a ``bool``. If ``True``, use TCP to make the query. + + *source*, a ``str`` or ``None``. If not ``None``, bind to this IP + address when making queries. + + *raise_on_no_answer*, a ``bool``. If ``True``, raise + ``dns.resolver.NoAnswer`` if there's no answer to the question. + + *source_port*, an ``int``, the port from which to send the message. + + *lifetime*, a ``float``, how many seconds a query should run + before timing out. + + *search*, a ``bool`` or ``None``, determines whether the + search list configured in the system's resolver configuration + are used for relative names, and whether the resolver's domain + may be added to relative names. The default is ``None``, + which causes the value of the resolver's + ``use_search_by_default`` attribute to be used. + + Raises ``dns.resolver.LifetimeTimeout`` if no answers could be found + in the specified lifetime. + + Raises ``dns.resolver.NXDOMAIN`` if the query name does not exist. + + Raises ``dns.resolver.YXDOMAIN`` if the query name is too long after + DNAME substitution. + + Raises ``dns.resolver.NoAnswer`` if *raise_on_no_answer* is + ``True`` and the query name exists but has no RRset of the + desired type and class. + + Raises ``dns.resolver.NoNameservers`` if no non-broken + nameservers are available to answer the question. + + Returns a ``dns.resolver.Answer`` instance. + + """ + + resolution = _Resolution( + self, qname, rdtype, rdclass, tcp, raise_on_no_answer, search + ) + start = time.time() + while True: + (request, answer) = resolution.next_request() + # Note we need to say "if answer is not None" and not just + # "if answer" because answer implements __len__, and python + # will call that. We want to return if we have an answer + # object, including in cases where its length is 0. + if answer is not None: + # cache hit! + return answer + assert request is not None # needed for type checking + done = False + while not done: + (nameserver, tcp, backoff) = resolution.next_nameserver() + if backoff: + time.sleep(backoff) + timeout = self._compute_timeout(start, lifetime, resolution.errors) + try: + response = nameserver.query( + request, + timeout=timeout, + source=source, + source_port=source_port, + max_size=tcp, + ) + except Exception as ex: + (_, done) = resolution.query_result(None, ex) + continue + (answer, done) = resolution.query_result(response, None) + # Note we need to say "if answer is not None" and not just + # "if answer" because answer implements __len__, and python + # will call that. We want to return if we have an answer + # object, including in cases where its length is 0. + if answer is not None: + return answer + + def query( + self, + qname: Union[dns.name.Name, str], + rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, + rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, + tcp: bool = False, + source: Optional[str] = None, + raise_on_no_answer: bool = True, + source_port: int = 0, + lifetime: Optional[float] = None, + ) -> Answer: # pragma: no cover + """Query nameservers to find the answer to the question. + + This method calls resolve() with ``search=True``, and is + provided for backwards compatibility with prior versions of + dnspython. See the documentation for the resolve() method for + further details. + """ + warnings.warn( + "please use dns.resolver.Resolver.resolve() instead", + DeprecationWarning, + stacklevel=2, + ) + return self.resolve( + qname, + rdtype, + rdclass, + tcp, + source, + raise_on_no_answer, + source_port, + lifetime, + True, + ) + + def resolve_address(self, ipaddr: str, *args: Any, **kwargs: Any) -> Answer: + """Use a resolver to run a reverse query for PTR records. + + This utilizes the resolve() method to perform a PTR lookup on the + specified IP address. + + *ipaddr*, a ``str``, the IPv4 or IPv6 address you want to get + the PTR record for. + + All other arguments that can be passed to the resolve() function + except for rdtype and rdclass are also supported by this + function. + """ + # We make a modified kwargs for type checking happiness, as otherwise + # we get a legit warning about possibly having rdtype and rdclass + # in the kwargs more than once. + modified_kwargs: Dict[str, Any] = {} + modified_kwargs.update(kwargs) + modified_kwargs["rdtype"] = dns.rdatatype.PTR + modified_kwargs["rdclass"] = dns.rdataclass.IN + return self.resolve( + dns.reversename.from_address(ipaddr), *args, **modified_kwargs + ) + + def resolve_name( + self, + name: Union[dns.name.Name, str], + family: int = socket.AF_UNSPEC, + **kwargs: Any, + ) -> HostAnswers: + """Use a resolver to query for address records. + + This utilizes the resolve() method to perform A and/or AAAA lookups on + the specified name. + + *qname*, a ``dns.name.Name`` or ``str``, the name to resolve. + + *family*, an ``int``, the address family. If socket.AF_UNSPEC + (the default), both A and AAAA records will be retrieved. + + All other arguments that can be passed to the resolve() function + except for rdtype and rdclass are also supported by this + function. + """ + # We make a modified kwargs for type checking happiness, as otherwise + # we get a legit warning about possibly having rdtype and rdclass + # in the kwargs more than once. + modified_kwargs: Dict[str, Any] = {} + modified_kwargs.update(kwargs) + modified_kwargs.pop("rdtype", None) + modified_kwargs["rdclass"] = dns.rdataclass.IN + + if family == socket.AF_INET: + v4 = self.resolve(name, dns.rdatatype.A, **modified_kwargs) + return HostAnswers.make(v4=v4) + elif family == socket.AF_INET6: + v6 = self.resolve(name, dns.rdatatype.AAAA, **modified_kwargs) + return HostAnswers.make(v6=v6) + elif family != socket.AF_UNSPEC: # pragma: no cover + raise NotImplementedError(f"unknown address family {family}") + + raise_on_no_answer = modified_kwargs.pop("raise_on_no_answer", True) + lifetime = modified_kwargs.pop("lifetime", None) + start = time.time() + v6 = self.resolve( + name, + dns.rdatatype.AAAA, + raise_on_no_answer=False, + lifetime=self._compute_timeout(start, lifetime), + **modified_kwargs, + ) + # Note that setting name ensures we query the same name + # for A as we did for AAAA. (This is just in case search lists + # are active by default in the resolver configuration and + # we might be talking to a server that says NXDOMAIN when it + # wants to say NOERROR no data. + name = v6.qname + v4 = self.resolve( + name, + dns.rdatatype.A, + raise_on_no_answer=False, + lifetime=self._compute_timeout(start, lifetime), + **modified_kwargs, + ) + answers = HostAnswers.make(v6=v6, v4=v4, add_empty=not raise_on_no_answer) + if not answers: + raise NoAnswer(response=v6.response) + return answers + + # pylint: disable=redefined-outer-name + + def canonical_name(self, name: Union[dns.name.Name, str]) -> dns.name.Name: + """Determine the canonical name of *name*. + + The canonical name is the name the resolver uses for queries + after all CNAME and DNAME renamings have been applied. + + *name*, a ``dns.name.Name`` or ``str``, the query name. + + This method can raise any exception that ``resolve()`` can + raise, other than ``dns.resolver.NoAnswer`` and + ``dns.resolver.NXDOMAIN``. + + Returns a ``dns.name.Name``. + """ + try: + answer = self.resolve(name, raise_on_no_answer=False) + canonical_name = answer.canonical_name + except dns.resolver.NXDOMAIN as e: + canonical_name = e.canonical_name + return canonical_name + + # pylint: enable=redefined-outer-name + + def try_ddr(self, lifetime: float = 5.0) -> None: + """Try to update the resolver's nameservers using Discovery of Designated + Resolvers (DDR). If successful, the resolver will subsequently use + DNS-over-HTTPS or DNS-over-TLS for future queries. + + *lifetime*, a float, is the maximum time to spend attempting DDR. The default + is 5 seconds. + + If the SVCB query is successful and results in a non-empty list of nameservers, + then the resolver's nameservers are set to the returned servers in priority + order. + + The current implementation does not use any address hints from the SVCB record, + nor does it resolve addresses for the SCVB target name, rather it assumes that + the bootstrap nameserver will always be one of the addresses and uses it. + A future revision to the code may offer fuller support. The code verifies that + the bootstrap nameserver is in the Subject Alternative Name field of the + TLS certficate. + """ + try: + expiration = time.time() + lifetime + answer = self.resolve( + dns._ddr._local_resolver_name, "SVCB", lifetime=lifetime + ) + timeout = dns.query._remaining(expiration) + nameservers = dns._ddr._get_nameservers_sync(answer, timeout) + if len(nameservers) > 0: + self.nameservers = nameservers + except Exception: # pragma: no cover + pass + + +#: The default resolver. +default_resolver: Optional[Resolver] = None + + +def get_default_resolver() -> Resolver: + """Get the default resolver, initializing it if necessary.""" + if default_resolver is None: + reset_default_resolver() + assert default_resolver is not None + return default_resolver + + +def reset_default_resolver() -> None: + """Re-initialize default resolver. + + Note that the resolver configuration (i.e. /etc/resolv.conf on UNIX + systems) will be re-read immediately. + """ + + global default_resolver + default_resolver = Resolver() + + +def resolve( + qname: Union[dns.name.Name, str], + rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, + rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, + tcp: bool = False, + source: Optional[str] = None, + raise_on_no_answer: bool = True, + source_port: int = 0, + lifetime: Optional[float] = None, + search: Optional[bool] = None, +) -> Answer: # pragma: no cover + """Query nameservers to find the answer to the question. + + This is a convenience function that uses the default resolver + object to make the query. + + See ``dns.resolver.Resolver.resolve`` for more information on the + parameters. + """ + + return get_default_resolver().resolve( + qname, + rdtype, + rdclass, + tcp, + source, + raise_on_no_answer, + source_port, + lifetime, + search, + ) + + +def query( + qname: Union[dns.name.Name, str], + rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, + rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, + tcp: bool = False, + source: Optional[str] = None, + raise_on_no_answer: bool = True, + source_port: int = 0, + lifetime: Optional[float] = None, +) -> Answer: # pragma: no cover + """Query nameservers to find the answer to the question. + + This method calls resolve() with ``search=True``, and is + provided for backwards compatibility with prior versions of + dnspython. See the documentation for the resolve() method for + further details. + """ + warnings.warn( + "please use dns.resolver.resolve() instead", DeprecationWarning, stacklevel=2 + ) + return resolve( + qname, + rdtype, + rdclass, + tcp, + source, + raise_on_no_answer, + source_port, + lifetime, + True, + ) + + +def resolve_address(ipaddr: str, *args: Any, **kwargs: Any) -> Answer: + """Use a resolver to run a reverse query for PTR records. + + See ``dns.resolver.Resolver.resolve_address`` for more information on the + parameters. + """ + + return get_default_resolver().resolve_address(ipaddr, *args, **kwargs) + + +def resolve_name( + name: Union[dns.name.Name, str], family: int = socket.AF_UNSPEC, **kwargs: Any +) -> HostAnswers: + """Use a resolver to query for address records. + + See ``dns.resolver.Resolver.resolve_name`` for more information on the + parameters. + """ + + return get_default_resolver().resolve_name(name, family, **kwargs) + + +def canonical_name(name: Union[dns.name.Name, str]) -> dns.name.Name: + """Determine the canonical name of *name*. + + See ``dns.resolver.Resolver.canonical_name`` for more information on the + parameters and possible exceptions. + """ + + return get_default_resolver().canonical_name(name) + + +def try_ddr(lifetime: float = 5.0) -> None: # pragma: no cover + """Try to update the default resolver's nameservers using Discovery of Designated + Resolvers (DDR). If successful, the resolver will subsequently use + DNS-over-HTTPS or DNS-over-TLS for future queries. + + See :py:func:`dns.resolver.Resolver.try_ddr` for more information. + """ + return get_default_resolver().try_ddr(lifetime) + + +def zone_for_name( + name: Union[dns.name.Name, str], + rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, + tcp: bool = False, + resolver: Optional[Resolver] = None, + lifetime: Optional[float] = None, +) -> dns.name.Name: + """Find the name of the zone which contains the specified name. + + *name*, an absolute ``dns.name.Name`` or ``str``, the query name. + + *rdclass*, an ``int``, the query class. + + *tcp*, a ``bool``. If ``True``, use TCP to make the query. + + *resolver*, a ``dns.resolver.Resolver`` or ``None``, the resolver to use. + If ``None``, the default, then the default resolver is used. + + *lifetime*, a ``float``, the total time to allow for the queries needed + to determine the zone. If ``None``, the default, then only the individual + query limits of the resolver apply. + + Raises ``dns.resolver.NoRootSOA`` if there is no SOA RR at the DNS + root. (This is only likely to happen if you're using non-default + root servers in your network and they are misconfigured.) + + Raises ``dns.resolver.LifetimeTimeout`` if the answer could not be + found in the allotted lifetime. + + Returns a ``dns.name.Name``. + """ + + if isinstance(name, str): + name = dns.name.from_text(name, dns.name.root) + if resolver is None: + resolver = get_default_resolver() + if not name.is_absolute(): + raise NotAbsolute(name) + start = time.time() + expiration: Optional[float] + if lifetime is not None: + expiration = start + lifetime + else: + expiration = None + while 1: + try: + rlifetime: Optional[float] + if expiration is not None: + rlifetime = expiration - time.time() + if rlifetime <= 0: + rlifetime = 0 + else: + rlifetime = None + answer = resolver.resolve( + name, dns.rdatatype.SOA, rdclass, tcp, lifetime=rlifetime + ) + assert answer.rrset is not None + if answer.rrset.name == name: + return name + # otherwise we were CNAMEd or DNAMEd and need to look higher + except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer) as e: + if isinstance(e, dns.resolver.NXDOMAIN): + response = e.responses().get(name) + else: + response = e.response() # pylint: disable=no-value-for-parameter + if response: + for rrs in response.authority: + if rrs.rdtype == dns.rdatatype.SOA and rrs.rdclass == rdclass: + (nr, _, _) = rrs.name.fullcompare(name) + if nr == dns.name.NAMERELN_SUPERDOMAIN: + # We're doing a proper superdomain check as + # if the name were equal we ought to have gotten + # it in the answer section! We are ignoring the + # possibility that the authority is insane and + # is including multiple SOA RRs for different + # authorities. + return rrs.name + # we couldn't extract anything useful from the response (e.g. it's + # a type 3 NXDOMAIN) + try: + name = name.parent() + except dns.name.NoParent: + raise NoRootSOA + + +def make_resolver_at( + where: Union[dns.name.Name, str], + port: int = 53, + family: int = socket.AF_UNSPEC, + resolver: Optional[Resolver] = None, +) -> Resolver: + """Make a stub resolver using the specified destination as the full resolver. + + *where*, a ``dns.name.Name`` or ``str`` the domain name or IP address of the + full resolver. + + *port*, an ``int``, the port to use. If not specified, the default is 53. + + *family*, an ``int``, the address family to use. This parameter is used if + *where* is not an address. The default is ``socket.AF_UNSPEC`` in which case + the first address returned by ``resolve_name()`` will be used, otherwise the + first address of the specified family will be used. + + *resolver*, a ``dns.resolver.Resolver`` or ``None``, the resolver to use for + resolution of hostnames. If not specified, the default resolver will be used. + + Returns a ``dns.resolver.Resolver`` or raises an exception. + """ + if resolver is None: + resolver = get_default_resolver() + nameservers: List[Union[str, dns.nameserver.Nameserver]] = [] + if isinstance(where, str) and dns.inet.is_address(where): + nameservers.append(dns.nameserver.Do53Nameserver(where, port)) + else: + for address in resolver.resolve_name(where, family).addresses(): + nameservers.append(dns.nameserver.Do53Nameserver(address, port)) + res = dns.resolver.Resolver(configure=False) + res.nameservers = nameservers + return res + + +def resolve_at( + where: Union[dns.name.Name, str], + qname: Union[dns.name.Name, str], + rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, + rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, + tcp: bool = False, + source: Optional[str] = None, + raise_on_no_answer: bool = True, + source_port: int = 0, + lifetime: Optional[float] = None, + search: Optional[bool] = None, + port: int = 53, + family: int = socket.AF_UNSPEC, + resolver: Optional[Resolver] = None, +) -> Answer: + """Query nameservers to find the answer to the question. + + This is a convenience function that calls ``dns.resolver.make_resolver_at()`` to + make a resolver, and then uses it to resolve the query. + + See ``dns.resolver.Resolver.resolve`` for more information on the resolution + parameters, and ``dns.resolver.make_resolver_at`` for information about the resolver + parameters *where*, *port*, *family*, and *resolver*. + + If making more than one query, it is more efficient to call + ``dns.resolver.make_resolver_at()`` and then use that resolver for the queries + instead of calling ``resolve_at()`` multiple times. + """ + return make_resolver_at(where, port, family, resolver).resolve( + qname, + rdtype, + rdclass, + tcp, + source, + raise_on_no_answer, + source_port, + lifetime, + search, + ) + + +# +# Support for overriding the system resolver for all python code in the +# running process. +# + +_protocols_for_socktype = { + socket.SOCK_DGRAM: [socket.SOL_UDP], + socket.SOCK_STREAM: [socket.SOL_TCP], +} + +_resolver = None +_original_getaddrinfo = socket.getaddrinfo +_original_getnameinfo = socket.getnameinfo +_original_getfqdn = socket.getfqdn +_original_gethostbyname = socket.gethostbyname +_original_gethostbyname_ex = socket.gethostbyname_ex +_original_gethostbyaddr = socket.gethostbyaddr + + +def _getaddrinfo( + host=None, service=None, family=socket.AF_UNSPEC, socktype=0, proto=0, flags=0 +): + if flags & socket.AI_NUMERICHOST != 0: + # Short circuit directly into the system's getaddrinfo(). We're + # not adding any value in this case, and this avoids infinite loops + # because dns.query.* needs to call getaddrinfo() for IPv6 scoping + # reasons. We will also do this short circuit below if we + # discover that the host is an address literal. + return _original_getaddrinfo(host, service, family, socktype, proto, flags) + if flags & (socket.AI_ADDRCONFIG | socket.AI_V4MAPPED) != 0: + # Not implemented. We raise a gaierror as opposed to a + # NotImplementedError as it helps callers handle errors more + # appropriately. [Issue #316] + # + # We raise EAI_FAIL as opposed to EAI_SYSTEM because there is + # no EAI_SYSTEM on Windows [Issue #416]. We didn't go for + # EAI_BADFLAGS as the flags aren't bad, we just don't + # implement them. + raise socket.gaierror( + socket.EAI_FAIL, "Non-recoverable failure in name resolution" + ) + if host is None and service is None: + raise socket.gaierror(socket.EAI_NONAME, "Name or service not known") + addrs = [] + canonical_name = None # pylint: disable=redefined-outer-name + # Is host None or an address literal? If so, use the system's + # getaddrinfo(). + if host is None: + return _original_getaddrinfo(host, service, family, socktype, proto, flags) + try: + # We don't care about the result of af_for_address(), we're just + # calling it so it raises an exception if host is not an IPv4 or + # IPv6 address. + dns.inet.af_for_address(host) + return _original_getaddrinfo(host, service, family, socktype, proto, flags) + except Exception: + pass + # Something needs resolution! + try: + answers = _resolver.resolve_name(host, family) + addrs = answers.addresses_and_families() + canonical_name = answers.canonical_name().to_text(True) + except dns.resolver.NXDOMAIN: + raise socket.gaierror(socket.EAI_NONAME, "Name or service not known") + except Exception: + # We raise EAI_AGAIN here as the failure may be temporary + # (e.g. a timeout) and EAI_SYSTEM isn't defined on Windows. + # [Issue #416] + raise socket.gaierror(socket.EAI_AGAIN, "Temporary failure in name resolution") + port = None + try: + # Is it a port literal? + if service is None: + port = 0 + else: + port = int(service) + except Exception: + if flags & socket.AI_NUMERICSERV == 0: + try: + port = socket.getservbyname(service) + except Exception: + pass + if port is None: + raise socket.gaierror(socket.EAI_NONAME, "Name or service not known") + tuples = [] + if socktype == 0: + socktypes = [socket.SOCK_DGRAM, socket.SOCK_STREAM] + else: + socktypes = [socktype] + if flags & socket.AI_CANONNAME != 0: + cname = canonical_name + else: + cname = "" + for addr, af in addrs: + for socktype in socktypes: + for proto in _protocols_for_socktype[socktype]: + addr_tuple = dns.inet.low_level_address_tuple((addr, port), af) + tuples.append((af, socktype, proto, cname, addr_tuple)) + if len(tuples) == 0: + raise socket.gaierror(socket.EAI_NONAME, "Name or service not known") + return tuples + + +def _getnameinfo(sockaddr, flags=0): + host = sockaddr[0] + port = sockaddr[1] + if len(sockaddr) == 4: + scope = sockaddr[3] + family = socket.AF_INET6 + else: + scope = None + family = socket.AF_INET + tuples = _getaddrinfo(host, port, family, socket.SOCK_STREAM, socket.SOL_TCP, 0) + if len(tuples) > 1: + raise OSError("sockaddr resolved to multiple addresses") + addr = tuples[0][4][0] + if flags & socket.NI_DGRAM: + pname = "udp" + else: + pname = "tcp" + qname = dns.reversename.from_address(addr) + if flags & socket.NI_NUMERICHOST == 0: + try: + answer = _resolver.resolve(qname, "PTR") + hostname = answer.rrset[0].target.to_text(True) + except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): + if flags & socket.NI_NAMEREQD: + raise socket.gaierror(socket.EAI_NONAME, "Name or service not known") + hostname = addr + if scope is not None: + hostname += "%" + str(scope) + else: + hostname = addr + if scope is not None: + hostname += "%" + str(scope) + if flags & socket.NI_NUMERICSERV: + service = str(port) + else: + service = socket.getservbyport(port, pname) + return (hostname, service) + + +def _getfqdn(name=None): + if name is None: + name = socket.gethostname() + try: + (name, _, _) = _gethostbyaddr(name) + # Python's version checks aliases too, but our gethostbyname + # ignores them, so we do so here as well. + except Exception: # pragma: no cover + pass + return name + + +def _gethostbyname(name): + return _gethostbyname_ex(name)[2][0] + + +def _gethostbyname_ex(name): + aliases = [] + addresses = [] + tuples = _getaddrinfo( + name, 0, socket.AF_INET, socket.SOCK_STREAM, socket.SOL_TCP, socket.AI_CANONNAME + ) + canonical = tuples[0][3] + for item in tuples: + addresses.append(item[4][0]) + # XXX we just ignore aliases + return (canonical, aliases, addresses) + + +def _gethostbyaddr(ip): + try: + dns.ipv6.inet_aton(ip) + sockaddr = (ip, 80, 0, 0) + family = socket.AF_INET6 + except Exception: + try: + dns.ipv4.inet_aton(ip) + except Exception: + raise socket.gaierror(socket.EAI_NONAME, "Name or service not known") + sockaddr = (ip, 80) + family = socket.AF_INET + (name, _) = _getnameinfo(sockaddr, socket.NI_NAMEREQD) + aliases = [] + addresses = [] + tuples = _getaddrinfo( + name, 0, family, socket.SOCK_STREAM, socket.SOL_TCP, socket.AI_CANONNAME + ) + canonical = tuples[0][3] + # We only want to include an address from the tuples if it's the + # same as the one we asked about. We do this comparison in binary + # to avoid any differences in text representations. + bin_ip = dns.inet.inet_pton(family, ip) + for item in tuples: + addr = item[4][0] + bin_addr = dns.inet.inet_pton(family, addr) + if bin_ip == bin_addr: + addresses.append(addr) + # XXX we just ignore aliases + return (canonical, aliases, addresses) + + +def override_system_resolver(resolver: Optional[Resolver] = None) -> None: + """Override the system resolver routines in the socket module with + versions which use dnspython's resolver. + + This can be useful in testing situations where you want to control + the resolution behavior of python code without having to change + the system's resolver settings (e.g. /etc/resolv.conf). + + The resolver to use may be specified; if it's not, the default + resolver will be used. + + resolver, a ``dns.resolver.Resolver`` or ``None``, the resolver to use. + """ + + if resolver is None: + resolver = get_default_resolver() + global _resolver + _resolver = resolver + socket.getaddrinfo = _getaddrinfo + socket.getnameinfo = _getnameinfo + socket.getfqdn = _getfqdn + socket.gethostbyname = _gethostbyname + socket.gethostbyname_ex = _gethostbyname_ex + socket.gethostbyaddr = _gethostbyaddr + + +def restore_system_resolver() -> None: + """Undo the effects of prior override_system_resolver().""" + + global _resolver + _resolver = None + socket.getaddrinfo = _original_getaddrinfo + socket.getnameinfo = _original_getnameinfo + socket.getfqdn = _original_getfqdn + socket.gethostbyname = _original_gethostbyname + socket.gethostbyname_ex = _original_gethostbyname_ex + socket.gethostbyaddr = _original_gethostbyaddr diff --git a/.venv/lib/python3.9/site-packages/dns/reversename.py b/.venv/lib/python3.9/site-packages/dns/reversename.py new file mode 100644 index 0000000..8236c71 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/reversename.py @@ -0,0 +1,105 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2006-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Reverse Map Names.""" + +import binascii + +import dns.ipv4 +import dns.ipv6 +import dns.name + +ipv4_reverse_domain = dns.name.from_text("in-addr.arpa.") +ipv6_reverse_domain = dns.name.from_text("ip6.arpa.") + + +def from_address( + text: str, + v4_origin: dns.name.Name = ipv4_reverse_domain, + v6_origin: dns.name.Name = ipv6_reverse_domain, +) -> dns.name.Name: + """Convert an IPv4 or IPv6 address in textual form into a Name object whose + value is the reverse-map domain name of the address. + + *text*, a ``str``, is an IPv4 or IPv6 address in textual form + (e.g. '127.0.0.1', '::1') + + *v4_origin*, a ``dns.name.Name`` to append to the labels corresponding to + the address if the address is an IPv4 address, instead of the default + (in-addr.arpa.) + + *v6_origin*, a ``dns.name.Name`` to append to the labels corresponding to + the address if the address is an IPv6 address, instead of the default + (ip6.arpa.) + + Raises ``dns.exception.SyntaxError`` if the address is badly formed. + + Returns a ``dns.name.Name``. + """ + + try: + v6 = dns.ipv6.inet_aton(text) + if dns.ipv6.is_mapped(v6): + parts = ["%d" % byte for byte in v6[12:]] + origin = v4_origin + else: + parts = [x for x in str(binascii.hexlify(v6).decode())] + origin = v6_origin + except Exception: + parts = ["%d" % byte for byte in dns.ipv4.inet_aton(text)] + origin = v4_origin + return dns.name.from_text(".".join(reversed(parts)), origin=origin) + + +def to_address( + name: dns.name.Name, + v4_origin: dns.name.Name = ipv4_reverse_domain, + v6_origin: dns.name.Name = ipv6_reverse_domain, +) -> str: + """Convert a reverse map domain name into textual address form. + + *name*, a ``dns.name.Name``, an IPv4 or IPv6 address in reverse-map name + form. + + *v4_origin*, a ``dns.name.Name`` representing the top-level domain for + IPv4 addresses, instead of the default (in-addr.arpa.) + + *v6_origin*, a ``dns.name.Name`` representing the top-level domain for + IPv4 addresses, instead of the default (ip6.arpa.) + + Raises ``dns.exception.SyntaxError`` if the name does not have a + reverse-map form. + + Returns a ``str``. + """ + + if name.is_subdomain(v4_origin): + name = name.relativize(v4_origin) + text = b".".join(reversed(name.labels)) + # run through inet_ntoa() to check syntax and make pretty. + return dns.ipv4.inet_ntoa(dns.ipv4.inet_aton(text)) + elif name.is_subdomain(v6_origin): + name = name.relativize(v6_origin) + labels = list(reversed(name.labels)) + parts = [] + for i in range(0, len(labels), 4): + parts.append(b"".join(labels[i : i + 4])) + text = b":".join(parts) + # run through inet_ntoa() to check syntax and make pretty. + return dns.ipv6.inet_ntoa(dns.ipv6.inet_aton(text)) + else: + raise dns.exception.SyntaxError("unknown reverse-map address family") diff --git a/.venv/lib/python3.9/site-packages/dns/rrset.py b/.venv/lib/python3.9/site-packages/dns/rrset.py new file mode 100644 index 0000000..6f39b10 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/rrset.py @@ -0,0 +1,285 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS RRsets (an RRset is a named rdataset)""" + +from typing import Any, Collection, Dict, Optional, Union, cast + +import dns.name +import dns.rdataclass +import dns.rdataset +import dns.renderer + + +class RRset(dns.rdataset.Rdataset): + """A DNS RRset (named rdataset). + + RRset inherits from Rdataset, and RRsets can be treated as + Rdatasets in most cases. There are, however, a few notable + exceptions. RRsets have different to_wire() and to_text() method + arguments, reflecting the fact that RRsets always have an owner + name. + """ + + __slots__ = ["name", "deleting"] + + def __init__( + self, + name: dns.name.Name, + rdclass: dns.rdataclass.RdataClass, + rdtype: dns.rdatatype.RdataType, + covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, + deleting: Optional[dns.rdataclass.RdataClass] = None, + ): + """Create a new RRset.""" + + super().__init__(rdclass, rdtype, covers) + self.name = name + self.deleting = deleting + + def _clone(self): + obj = super()._clone() + obj.name = self.name + obj.deleting = self.deleting + return obj + + def __repr__(self): + if self.covers == 0: + ctext = "" + else: + ctext = "(" + dns.rdatatype.to_text(self.covers) + ")" + if self.deleting is not None: + dtext = " delete=" + dns.rdataclass.to_text(self.deleting) + else: + dtext = "" + return ( + "" + ) + + def __str__(self): + return self.to_text() + + def __eq__(self, other): + if isinstance(other, RRset): + if self.name != other.name: + return False + elif not isinstance(other, dns.rdataset.Rdataset): + return False + return super().__eq__(other) + + def match(self, *args: Any, **kwargs: Any) -> bool: # type: ignore[override] + """Does this rrset match the specified attributes? + + Behaves as :py:func:`full_match()` if the first argument is a + ``dns.name.Name``, and as :py:func:`dns.rdataset.Rdataset.match()` + otherwise. + + (This behavior fixes a design mistake where the signature of this + method became incompatible with that of its superclass. The fix + makes RRsets matchable as Rdatasets while preserving backwards + compatibility.) + """ + if isinstance(args[0], dns.name.Name): + return self.full_match(*args, **kwargs) # type: ignore[arg-type] + else: + return super().match(*args, **kwargs) # type: ignore[arg-type] + + def full_match( + self, + name: dns.name.Name, + rdclass: dns.rdataclass.RdataClass, + rdtype: dns.rdatatype.RdataType, + covers: dns.rdatatype.RdataType, + deleting: Optional[dns.rdataclass.RdataClass] = None, + ) -> bool: + """Returns ``True`` if this rrset matches the specified name, class, + type, covers, and deletion state. + """ + if not super().match(rdclass, rdtype, covers): + return False + if self.name != name or self.deleting != deleting: + return False + return True + + # pylint: disable=arguments-differ + + def to_text( # type: ignore[override] + self, + origin: Optional[dns.name.Name] = None, + relativize: bool = True, + **kw: Dict[str, Any], + ) -> str: + """Convert the RRset into DNS zone file format. + + See ``dns.name.Name.choose_relativity`` for more information + on how *origin* and *relativize* determine the way names + are emitted. + + Any additional keyword arguments are passed on to the rdata + ``to_text()`` method. + + *origin*, a ``dns.name.Name`` or ``None``, the origin for relative + names. + + *relativize*, a ``bool``. If ``True``, names will be relativized + to *origin*. + """ + + return super().to_text( + self.name, origin, relativize, self.deleting, **kw # type: ignore + ) + + def to_wire( # type: ignore[override] + self, + file: Any, + compress: Optional[dns.name.CompressType] = None, # type: ignore + origin: Optional[dns.name.Name] = None, + **kw: Dict[str, Any], + ) -> int: + """Convert the RRset to wire format. + + All keyword arguments are passed to ``dns.rdataset.to_wire()``; see + that function for details. + + Returns an ``int``, the number of records emitted. + """ + + return super().to_wire( + self.name, file, compress, origin, self.deleting, **kw # type:ignore + ) + + # pylint: enable=arguments-differ + + def to_rdataset(self) -> dns.rdataset.Rdataset: + """Convert an RRset into an Rdataset. + + Returns a ``dns.rdataset.Rdataset``. + """ + return dns.rdataset.from_rdata_list(self.ttl, list(self)) + + +def from_text_list( + name: Union[dns.name.Name, str], + ttl: int, + rdclass: Union[dns.rdataclass.RdataClass, str], + rdtype: Union[dns.rdatatype.RdataType, str], + text_rdatas: Collection[str], + idna_codec: Optional[dns.name.IDNACodec] = None, + origin: Optional[dns.name.Name] = None, + relativize: bool = True, + relativize_to: Optional[dns.name.Name] = None, +) -> RRset: + """Create an RRset with the specified name, TTL, class, and type, and with + the specified list of rdatas in text format. + + *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA + encoder/decoder to use; if ``None``, the default IDNA 2003 + encoder/decoder is used. + + *origin*, a ``dns.name.Name`` (or ``None``), the + origin to use for relative names. + + *relativize*, a ``bool``. If true, name will be relativized. + + *relativize_to*, a ``dns.name.Name`` (or ``None``), the origin to use + when relativizing names. If not set, the *origin* value will be used. + + Returns a ``dns.rrset.RRset`` object. + """ + + if isinstance(name, str): + name = dns.name.from_text(name, None, idna_codec=idna_codec) + rdclass = dns.rdataclass.RdataClass.make(rdclass) + rdtype = dns.rdatatype.RdataType.make(rdtype) + r = RRset(name, rdclass, rdtype) + r.update_ttl(ttl) + for t in text_rdatas: + rd = dns.rdata.from_text( + r.rdclass, r.rdtype, t, origin, relativize, relativize_to, idna_codec + ) + r.add(rd) + return r + + +def from_text( + name: Union[dns.name.Name, str], + ttl: int, + rdclass: Union[dns.rdataclass.RdataClass, str], + rdtype: Union[dns.rdatatype.RdataType, str], + *text_rdatas: Any, +) -> RRset: + """Create an RRset with the specified name, TTL, class, and type and with + the specified rdatas in text format. + + Returns a ``dns.rrset.RRset`` object. + """ + + return from_text_list( + name, ttl, rdclass, rdtype, cast(Collection[str], text_rdatas) + ) + + +def from_rdata_list( + name: Union[dns.name.Name, str], + ttl: int, + rdatas: Collection[dns.rdata.Rdata], + idna_codec: Optional[dns.name.IDNACodec] = None, +) -> RRset: + """Create an RRset with the specified name and TTL, and with + the specified list of rdata objects. + + *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA + encoder/decoder to use; if ``None``, the default IDNA 2003 + encoder/decoder is used. + + Returns a ``dns.rrset.RRset`` object. + + """ + + if isinstance(name, str): + name = dns.name.from_text(name, None, idna_codec=idna_codec) + + if len(rdatas) == 0: + raise ValueError("rdata list must not be empty") + r = None + for rd in rdatas: + if r is None: + r = RRset(name, rd.rdclass, rd.rdtype) + r.update_ttl(ttl) + r.add(rd) + assert r is not None + return r + + +def from_rdata(name: Union[dns.name.Name, str], ttl: int, *rdatas: Any) -> RRset: + """Create an RRset with the specified name and TTL, and with + the specified rdata objects. + + Returns a ``dns.rrset.RRset`` object. + """ + + return from_rdata_list(name, ttl, cast(Collection[dns.rdata.Rdata], rdatas)) diff --git a/.venv/lib/python3.9/site-packages/dns/serial.py b/.venv/lib/python3.9/site-packages/dns/serial.py new file mode 100644 index 0000000..3417299 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/serial.py @@ -0,0 +1,118 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +"""Serial Number Arthimetic from RFC 1982""" + + +class Serial: + def __init__(self, value: int, bits: int = 32): + self.value = value % 2**bits + self.bits = bits + + def __repr__(self): + return f"dns.serial.Serial({self.value}, {self.bits})" + + def __eq__(self, other): + if isinstance(other, int): + other = Serial(other, self.bits) + elif not isinstance(other, Serial) or other.bits != self.bits: + return NotImplemented + return self.value == other.value + + def __ne__(self, other): + if isinstance(other, int): + other = Serial(other, self.bits) + elif not isinstance(other, Serial) or other.bits != self.bits: + return NotImplemented + return self.value != other.value + + def __lt__(self, other): + if isinstance(other, int): + other = Serial(other, self.bits) + elif not isinstance(other, Serial) or other.bits != self.bits: + return NotImplemented + if self.value < other.value and other.value - self.value < 2 ** (self.bits - 1): + return True + elif self.value > other.value and self.value - other.value > 2 ** ( + self.bits - 1 + ): + return True + else: + return False + + def __le__(self, other): + return self == other or self < other + + def __gt__(self, other): + if isinstance(other, int): + other = Serial(other, self.bits) + elif not isinstance(other, Serial) or other.bits != self.bits: + return NotImplemented + if self.value < other.value and other.value - self.value > 2 ** (self.bits - 1): + return True + elif self.value > other.value and self.value - other.value < 2 ** ( + self.bits - 1 + ): + return True + else: + return False + + def __ge__(self, other): + return self == other or self > other + + def __add__(self, other): + v = self.value + if isinstance(other, Serial): + delta = other.value + elif isinstance(other, int): + delta = other + else: + raise ValueError + if abs(delta) > (2 ** (self.bits - 1) - 1): + raise ValueError + v += delta + v = v % 2**self.bits + return Serial(v, self.bits) + + def __iadd__(self, other): + v = self.value + if isinstance(other, Serial): + delta = other.value + elif isinstance(other, int): + delta = other + else: + raise ValueError + if abs(delta) > (2 ** (self.bits - 1) - 1): + raise ValueError + v += delta + v = v % 2**self.bits + self.value = v + return self + + def __sub__(self, other): + v = self.value + if isinstance(other, Serial): + delta = other.value + elif isinstance(other, int): + delta = other + else: + raise ValueError + if abs(delta) > (2 ** (self.bits - 1) - 1): + raise ValueError + v -= delta + v = v % 2**self.bits + return Serial(v, self.bits) + + def __isub__(self, other): + v = self.value + if isinstance(other, Serial): + delta = other.value + elif isinstance(other, int): + delta = other + else: + raise ValueError + if abs(delta) > (2 ** (self.bits - 1) - 1): + raise ValueError + v -= delta + v = v % 2**self.bits + self.value = v + return self diff --git a/.venv/lib/python3.9/site-packages/dns/set.py b/.venv/lib/python3.9/site-packages/dns/set.py new file mode 100644 index 0000000..ae8f0dd --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/set.py @@ -0,0 +1,308 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import itertools + + +class Set: + """A simple set class. + + This class was originally used to deal with python not having a set class, and + originally the class used lists in its implementation. The ordered and indexable + nature of RRsets and Rdatasets is unfortunately widely used in dnspython + applications, so for backwards compatibility sets continue to be a custom class, now + based on an ordered dictionary. + """ + + __slots__ = ["items"] + + def __init__(self, items=None): + """Initialize the set. + + *items*, an iterable or ``None``, the initial set of items. + """ + + self.items = dict() + if items is not None: + for item in items: + # This is safe for how we use set, but if other code + # subclasses it could be a legitimate issue. + self.add(item) # lgtm[py/init-calls-subclass] + + def __repr__(self): + return f"dns.set.Set({repr(list(self.items.keys()))})" # pragma: no cover + + def add(self, item): + """Add an item to the set.""" + + if item not in self.items: + self.items[item] = None + + def remove(self, item): + """Remove an item from the set.""" + + try: + del self.items[item] + except KeyError: + raise ValueError + + def discard(self, item): + """Remove an item from the set if present.""" + + self.items.pop(item, None) + + def pop(self): + """Remove an arbitrary item from the set.""" + (k, _) = self.items.popitem() + return k + + def _clone(self) -> "Set": + """Make a (shallow) copy of the set. + + There is a 'clone protocol' that subclasses of this class + should use. To make a copy, first call your super's _clone() + method, and use the object returned as the new instance. Then + make shallow copies of the attributes defined in the subclass. + + This protocol allows us to write the set algorithms that + return new instances (e.g. union) once, and keep using them in + subclasses. + """ + + if hasattr(self, "_clone_class"): + cls = self._clone_class # type: ignore + else: + cls = self.__class__ + obj = cls.__new__(cls) + obj.items = dict() + obj.items.update(self.items) + return obj + + def __copy__(self): + """Make a (shallow) copy of the set.""" + + return self._clone() + + def copy(self): + """Make a (shallow) copy of the set.""" + + return self._clone() + + def union_update(self, other): + """Update the set, adding any elements from other which are not + already in the set. + """ + + if not isinstance(other, Set): + raise ValueError("other must be a Set instance") + if self is other: # lgtm[py/comparison-using-is] + return + for item in other.items: + self.add(item) + + def intersection_update(self, other): + """Update the set, removing any elements from other which are not + in both sets. + """ + + if not isinstance(other, Set): + raise ValueError("other must be a Set instance") + if self is other: # lgtm[py/comparison-using-is] + return + # we make a copy of the list so that we can remove items from + # the list without breaking the iterator. + for item in list(self.items): + if item not in other.items: + del self.items[item] + + def difference_update(self, other): + """Update the set, removing any elements from other which are in + the set. + """ + + if not isinstance(other, Set): + raise ValueError("other must be a Set instance") + if self is other: # lgtm[py/comparison-using-is] + self.items.clear() + else: + for item in other.items: + self.discard(item) + + def symmetric_difference_update(self, other): + """Update the set, retaining only elements unique to both sets.""" + + if not isinstance(other, Set): + raise ValueError("other must be a Set instance") + if self is other: # lgtm[py/comparison-using-is] + self.items.clear() + else: + overlap = self.intersection(other) + self.union_update(other) + self.difference_update(overlap) + + def union(self, other): + """Return a new set which is the union of ``self`` and ``other``. + + Returns the same Set type as this set. + """ + + obj = self._clone() + obj.union_update(other) + return obj + + def intersection(self, other): + """Return a new set which is the intersection of ``self`` and + ``other``. + + Returns the same Set type as this set. + """ + + obj = self._clone() + obj.intersection_update(other) + return obj + + def difference(self, other): + """Return a new set which ``self`` - ``other``, i.e. the items + in ``self`` which are not also in ``other``. + + Returns the same Set type as this set. + """ + + obj = self._clone() + obj.difference_update(other) + return obj + + def symmetric_difference(self, other): + """Return a new set which (``self`` - ``other``) | (``other`` + - ``self), ie: the items in either ``self`` or ``other`` which + are not contained in their intersection. + + Returns the same Set type as this set. + """ + + obj = self._clone() + obj.symmetric_difference_update(other) + return obj + + def __or__(self, other): + return self.union(other) + + def __and__(self, other): + return self.intersection(other) + + def __add__(self, other): + return self.union(other) + + def __sub__(self, other): + return self.difference(other) + + def __xor__(self, other): + return self.symmetric_difference(other) + + def __ior__(self, other): + self.union_update(other) + return self + + def __iand__(self, other): + self.intersection_update(other) + return self + + def __iadd__(self, other): + self.union_update(other) + return self + + def __isub__(self, other): + self.difference_update(other) + return self + + def __ixor__(self, other): + self.symmetric_difference_update(other) + return self + + def update(self, other): + """Update the set, adding any elements from other which are not + already in the set. + + *other*, the collection of items with which to update the set, which + may be any iterable type. + """ + + for item in other: + self.add(item) + + def clear(self): + """Make the set empty.""" + self.items.clear() + + def __eq__(self, other): + return self.items == other.items + + def __ne__(self, other): + return not self.__eq__(other) + + def __len__(self): + return len(self.items) + + def __iter__(self): + return iter(self.items) + + def __getitem__(self, i): + if isinstance(i, slice): + return list(itertools.islice(self.items, i.start, i.stop, i.step)) + else: + return next(itertools.islice(self.items, i, i + 1)) + + def __delitem__(self, i): + if isinstance(i, slice): + for elt in list(self[i]): + del self.items[elt] + else: + del self.items[self[i]] + + def issubset(self, other): + """Is this set a subset of *other*? + + Returns a ``bool``. + """ + + if not isinstance(other, Set): + raise ValueError("other must be a Set instance") + for item in self.items: + if item not in other.items: + return False + return True + + def issuperset(self, other): + """Is this set a superset of *other*? + + Returns a ``bool``. + """ + + if not isinstance(other, Set): + raise ValueError("other must be a Set instance") + for item in other.items: + if item not in self.items: + return False + return True + + def isdisjoint(self, other): + if not isinstance(other, Set): + raise ValueError("other must be a Set instance") + for item in other.items: + if item in self.items: + return False + return True diff --git a/.venv/lib/python3.9/site-packages/dns/tokenizer.py b/.venv/lib/python3.9/site-packages/dns/tokenizer.py new file mode 100644 index 0000000..ab205bc --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/tokenizer.py @@ -0,0 +1,708 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Tokenize DNS zone file format""" + +import io +import sys +from typing import Any, List, Optional, Tuple + +import dns.exception +import dns.name +import dns.ttl + +_DELIMITERS = {" ", "\t", "\n", ";", "(", ")", '"'} +_QUOTING_DELIMITERS = {'"'} + +EOF = 0 +EOL = 1 +WHITESPACE = 2 +IDENTIFIER = 3 +QUOTED_STRING = 4 +COMMENT = 5 +DELIMITER = 6 + + +class UngetBufferFull(dns.exception.DNSException): + """An attempt was made to unget a token when the unget buffer was full.""" + + +class Token: + """A DNS zone file format token. + + ttype: The token type + value: The token value + has_escape: Does the token value contain escapes? + """ + + def __init__( + self, + ttype: int, + value: Any = "", + has_escape: bool = False, + comment: Optional[str] = None, + ): + """Initialize a token instance.""" + + self.ttype = ttype + self.value = value + self.has_escape = has_escape + self.comment = comment + + def is_eof(self) -> bool: + return self.ttype == EOF + + def is_eol(self) -> bool: + return self.ttype == EOL + + def is_whitespace(self) -> bool: + return self.ttype == WHITESPACE + + def is_identifier(self) -> bool: + return self.ttype == IDENTIFIER + + def is_quoted_string(self) -> bool: + return self.ttype == QUOTED_STRING + + def is_comment(self) -> bool: + return self.ttype == COMMENT + + def is_delimiter(self) -> bool: # pragma: no cover (we don't return delimiters yet) + return self.ttype == DELIMITER + + def is_eol_or_eof(self) -> bool: + return self.ttype == EOL or self.ttype == EOF + + def __eq__(self, other): + if not isinstance(other, Token): + return False + return self.ttype == other.ttype and self.value == other.value + + def __ne__(self, other): + if not isinstance(other, Token): + return True + return self.ttype != other.ttype or self.value != other.value + + def __str__(self): + return '%d "%s"' % (self.ttype, self.value) + + def unescape(self) -> "Token": + if not self.has_escape: + return self + unescaped = "" + l = len(self.value) + i = 0 + while i < l: + c = self.value[i] + i += 1 + if c == "\\": + if i >= l: # pragma: no cover (can't happen via get()) + raise dns.exception.UnexpectedEnd + c = self.value[i] + i += 1 + if c.isdigit(): + if i >= l: + raise dns.exception.UnexpectedEnd + c2 = self.value[i] + i += 1 + if i >= l: + raise dns.exception.UnexpectedEnd + c3 = self.value[i] + i += 1 + if not (c2.isdigit() and c3.isdigit()): + raise dns.exception.SyntaxError + codepoint = int(c) * 100 + int(c2) * 10 + int(c3) + if codepoint > 255: + raise dns.exception.SyntaxError + c = chr(codepoint) + unescaped += c + return Token(self.ttype, unescaped) + + def unescape_to_bytes(self) -> "Token": + # We used to use unescape() for TXT-like records, but this + # caused problems as we'd process DNS escapes into Unicode code + # points instead of byte values, and then a to_text() of the + # processed data would not equal the original input. For + # example, \226 in the TXT record would have a to_text() of + # \195\162 because we applied UTF-8 encoding to Unicode code + # point 226. + # + # We now apply escapes while converting directly to bytes, + # avoiding this double encoding. + # + # This code also handles cases where the unicode input has + # non-ASCII code-points in it by converting it to UTF-8. TXT + # records aren't defined for Unicode, but this is the best we + # can do to preserve meaning. For example, + # + # foo\u200bbar + # + # (where \u200b is Unicode code point 0x200b) will be treated + # as if the input had been the UTF-8 encoding of that string, + # namely: + # + # foo\226\128\139bar + # + unescaped = b"" + l = len(self.value) + i = 0 + while i < l: + c = self.value[i] + i += 1 + if c == "\\": + if i >= l: # pragma: no cover (can't happen via get()) + raise dns.exception.UnexpectedEnd + c = self.value[i] + i += 1 + if c.isdigit(): + if i >= l: + raise dns.exception.UnexpectedEnd + c2 = self.value[i] + i += 1 + if i >= l: + raise dns.exception.UnexpectedEnd + c3 = self.value[i] + i += 1 + if not (c2.isdigit() and c3.isdigit()): + raise dns.exception.SyntaxError + codepoint = int(c) * 100 + int(c2) * 10 + int(c3) + if codepoint > 255: + raise dns.exception.SyntaxError + unescaped += b"%c" % (codepoint) + else: + # Note that as mentioned above, if c is a Unicode + # code point outside of the ASCII range, then this + # += is converting that code point to its UTF-8 + # encoding and appending multiple bytes to + # unescaped. + unescaped += c.encode() + else: + unescaped += c.encode() + return Token(self.ttype, bytes(unescaped)) + + +class Tokenizer: + """A DNS zone file format tokenizer. + + A token object is basically a (type, value) tuple. The valid + types are EOF, EOL, WHITESPACE, IDENTIFIER, QUOTED_STRING, + COMMENT, and DELIMITER. + + file: The file to tokenize + + ungotten_char: The most recently ungotten character, or None. + + ungotten_token: The most recently ungotten token, or None. + + multiline: The current multiline level. This value is increased + by one every time a '(' delimiter is read, and decreased by one every time + a ')' delimiter is read. + + quoting: This variable is true if the tokenizer is currently + reading a quoted string. + + eof: This variable is true if the tokenizer has encountered EOF. + + delimiters: The current delimiter dictionary. + + line_number: The current line number + + filename: A filename that will be returned by the where() method. + + idna_codec: A dns.name.IDNACodec, specifies the IDNA + encoder/decoder. If None, the default IDNA 2003 + encoder/decoder is used. + """ + + def __init__( + self, + f: Any = sys.stdin, + filename: Optional[str] = None, + idna_codec: Optional[dns.name.IDNACodec] = None, + ): + """Initialize a tokenizer instance. + + f: The file to tokenize. The default is sys.stdin. + This parameter may also be a string, in which case the tokenizer + will take its input from the contents of the string. + + filename: the name of the filename that the where() method + will return. + + idna_codec: A dns.name.IDNACodec, specifies the IDNA + encoder/decoder. If None, the default IDNA 2003 + encoder/decoder is used. + """ + + if isinstance(f, str): + f = io.StringIO(f) + if filename is None: + filename = "" + elif isinstance(f, bytes): + f = io.StringIO(f.decode()) + if filename is None: + filename = "" + else: + if filename is None: + if f is sys.stdin: + filename = "" + else: + filename = "" + self.file = f + self.ungotten_char: Optional[str] = None + self.ungotten_token: Optional[Token] = None + self.multiline = 0 + self.quoting = False + self.eof = False + self.delimiters = _DELIMITERS + self.line_number = 1 + assert filename is not None + self.filename = filename + if idna_codec is None: + self.idna_codec: dns.name.IDNACodec = dns.name.IDNA_2003 + else: + self.idna_codec = idna_codec + + def _get_char(self) -> str: + """Read a character from input.""" + + if self.ungotten_char is None: + if self.eof: + c = "" + else: + c = self.file.read(1) + if c == "": + self.eof = True + elif c == "\n": + self.line_number += 1 + else: + c = self.ungotten_char + self.ungotten_char = None + return c + + def where(self) -> Tuple[str, int]: + """Return the current location in the input. + + Returns a (string, int) tuple. The first item is the filename of + the input, the second is the current line number. + """ + + return (self.filename, self.line_number) + + def _unget_char(self, c: str) -> None: + """Unget a character. + + The unget buffer for characters is only one character large; it is + an error to try to unget a character when the unget buffer is not + empty. + + c: the character to unget + raises UngetBufferFull: there is already an ungotten char + """ + + if self.ungotten_char is not None: + # this should never happen! + raise UngetBufferFull # pragma: no cover + self.ungotten_char = c + + def skip_whitespace(self) -> int: + """Consume input until a non-whitespace character is encountered. + + The non-whitespace character is then ungotten, and the number of + whitespace characters consumed is returned. + + If the tokenizer is in multiline mode, then newlines are whitespace. + + Returns the number of characters skipped. + """ + + skipped = 0 + while True: + c = self._get_char() + if c != " " and c != "\t": + if (c != "\n") or not self.multiline: + self._unget_char(c) + return skipped + skipped += 1 + + def get(self, want_leading: bool = False, want_comment: bool = False) -> Token: + """Get the next token. + + want_leading: If True, return a WHITESPACE token if the + first character read is whitespace. The default is False. + + want_comment: If True, return a COMMENT token if the + first token read is a comment. The default is False. + + Raises dns.exception.UnexpectedEnd: input ended prematurely + + Raises dns.exception.SyntaxError: input was badly formed + + Returns a Token. + """ + + if self.ungotten_token is not None: + utoken = self.ungotten_token + self.ungotten_token = None + if utoken.is_whitespace(): + if want_leading: + return utoken + elif utoken.is_comment(): + if want_comment: + return utoken + else: + return utoken + skipped = self.skip_whitespace() + if want_leading and skipped > 0: + return Token(WHITESPACE, " ") + token = "" + ttype = IDENTIFIER + has_escape = False + while True: + c = self._get_char() + if c == "" or c in self.delimiters: + if c == "" and self.quoting: + raise dns.exception.UnexpectedEnd + if token == "" and ttype != QUOTED_STRING: + if c == "(": + self.multiline += 1 + self.skip_whitespace() + continue + elif c == ")": + if self.multiline <= 0: + raise dns.exception.SyntaxError + self.multiline -= 1 + self.skip_whitespace() + continue + elif c == '"': + if not self.quoting: + self.quoting = True + self.delimiters = _QUOTING_DELIMITERS + ttype = QUOTED_STRING + continue + else: + self.quoting = False + self.delimiters = _DELIMITERS + self.skip_whitespace() + continue + elif c == "\n": + return Token(EOL, "\n") + elif c == ";": + while 1: + c = self._get_char() + if c == "\n" or c == "": + break + token += c + if want_comment: + self._unget_char(c) + return Token(COMMENT, token) + elif c == "": + if self.multiline: + raise dns.exception.SyntaxError( + "unbalanced parentheses" + ) + return Token(EOF, comment=token) + elif self.multiline: + self.skip_whitespace() + token = "" + continue + else: + return Token(EOL, "\n", comment=token) + else: + # This code exists in case we ever want a + # delimiter to be returned. It never produces + # a token currently. + token = c + ttype = DELIMITER + else: + self._unget_char(c) + break + elif self.quoting and c == "\n": + raise dns.exception.SyntaxError("newline in quoted string") + elif c == "\\": + # + # It's an escape. Put it and the next character into + # the token; it will be checked later for goodness. + # + token += c + has_escape = True + c = self._get_char() + if c == "" or (c == "\n" and not self.quoting): + raise dns.exception.UnexpectedEnd + token += c + if token == "" and ttype != QUOTED_STRING: + if self.multiline: + raise dns.exception.SyntaxError("unbalanced parentheses") + ttype = EOF + return Token(ttype, token, has_escape) + + def unget(self, token: Token) -> None: + """Unget a token. + + The unget buffer for tokens is only one token large; it is + an error to try to unget a token when the unget buffer is not + empty. + + token: the token to unget + + Raises UngetBufferFull: there is already an ungotten token + """ + + if self.ungotten_token is not None: + raise UngetBufferFull + self.ungotten_token = token + + def next(self): + """Return the next item in an iteration. + + Returns a Token. + """ + + token = self.get() + if token.is_eof(): + raise StopIteration + return token + + __next__ = next + + def __iter__(self): + return self + + # Helpers + + def get_int(self, base: int = 10) -> int: + """Read the next token and interpret it as an unsigned integer. + + Raises dns.exception.SyntaxError if not an unsigned integer. + + Returns an int. + """ + + token = self.get().unescape() + if not token.is_identifier(): + raise dns.exception.SyntaxError("expecting an identifier") + if not token.value.isdigit(): + raise dns.exception.SyntaxError("expecting an integer") + return int(token.value, base) + + def get_uint8(self) -> int: + """Read the next token and interpret it as an 8-bit unsigned + integer. + + Raises dns.exception.SyntaxError if not an 8-bit unsigned integer. + + Returns an int. + """ + + value = self.get_int() + if value < 0 or value > 255: + raise dns.exception.SyntaxError( + "%d is not an unsigned 8-bit integer" % value + ) + return value + + def get_uint16(self, base: int = 10) -> int: + """Read the next token and interpret it as a 16-bit unsigned + integer. + + Raises dns.exception.SyntaxError if not a 16-bit unsigned integer. + + Returns an int. + """ + + value = self.get_int(base=base) + if value < 0 or value > 65535: + if base == 8: + raise dns.exception.SyntaxError( + f"{value:o} is not an octal unsigned 16-bit integer" + ) + else: + raise dns.exception.SyntaxError( + "%d is not an unsigned 16-bit integer" % value + ) + return value + + def get_uint32(self, base: int = 10) -> int: + """Read the next token and interpret it as a 32-bit unsigned + integer. + + Raises dns.exception.SyntaxError if not a 32-bit unsigned integer. + + Returns an int. + """ + + value = self.get_int(base=base) + if value < 0 or value > 4294967295: + raise dns.exception.SyntaxError( + "%d is not an unsigned 32-bit integer" % value + ) + return value + + def get_uint48(self, base: int = 10) -> int: + """Read the next token and interpret it as a 48-bit unsigned + integer. + + Raises dns.exception.SyntaxError if not a 48-bit unsigned integer. + + Returns an int. + """ + + value = self.get_int(base=base) + if value < 0 or value > 281474976710655: + raise dns.exception.SyntaxError( + "%d is not an unsigned 48-bit integer" % value + ) + return value + + def get_string(self, max_length: Optional[int] = None) -> str: + """Read the next token and interpret it as a string. + + Raises dns.exception.SyntaxError if not a string. + Raises dns.exception.SyntaxError if token value length + exceeds max_length (if specified). + + Returns a string. + """ + + token = self.get().unescape() + if not (token.is_identifier() or token.is_quoted_string()): + raise dns.exception.SyntaxError("expecting a string") + if max_length and len(token.value) > max_length: + raise dns.exception.SyntaxError("string too long") + return token.value + + def get_identifier(self) -> str: + """Read the next token, which should be an identifier. + + Raises dns.exception.SyntaxError if not an identifier. + + Returns a string. + """ + + token = self.get().unescape() + if not token.is_identifier(): + raise dns.exception.SyntaxError("expecting an identifier") + return token.value + + def get_remaining(self, max_tokens: Optional[int] = None) -> List[Token]: + """Return the remaining tokens on the line, until an EOL or EOF is seen. + + max_tokens: If not None, stop after this number of tokens. + + Returns a list of tokens. + """ + + tokens = [] + while True: + token = self.get() + if token.is_eol_or_eof(): + self.unget(token) + break + tokens.append(token) + if len(tokens) == max_tokens: + break + return tokens + + def concatenate_remaining_identifiers(self, allow_empty: bool = False) -> str: + """Read the remaining tokens on the line, which should be identifiers. + + Raises dns.exception.SyntaxError if there are no remaining tokens, + unless `allow_empty=True` is given. + + Raises dns.exception.SyntaxError if a token is seen that is not an + identifier. + + Returns a string containing a concatenation of the remaining + identifiers. + """ + s = "" + while True: + token = self.get().unescape() + if token.is_eol_or_eof(): + self.unget(token) + break + if not token.is_identifier(): + raise dns.exception.SyntaxError + s += token.value + if not (allow_empty or s): + raise dns.exception.SyntaxError("expecting another identifier") + return s + + def as_name( + self, + token: Token, + origin: Optional[dns.name.Name] = None, + relativize: bool = False, + relativize_to: Optional[dns.name.Name] = None, + ) -> dns.name.Name: + """Try to interpret the token as a DNS name. + + Raises dns.exception.SyntaxError if not a name. + + Returns a dns.name.Name. + """ + if not token.is_identifier(): + raise dns.exception.SyntaxError("expecting an identifier") + name = dns.name.from_text(token.value, origin, self.idna_codec) + return name.choose_relativity(relativize_to or origin, relativize) + + def get_name( + self, + origin: Optional[dns.name.Name] = None, + relativize: bool = False, + relativize_to: Optional[dns.name.Name] = None, + ) -> dns.name.Name: + """Read the next token and interpret it as a DNS name. + + Raises dns.exception.SyntaxError if not a name. + + Returns a dns.name.Name. + """ + + token = self.get() + return self.as_name(token, origin, relativize, relativize_to) + + def get_eol_as_token(self) -> Token: + """Read the next token and raise an exception if it isn't EOL or + EOF. + + Returns a string. + """ + + token = self.get() + if not token.is_eol_or_eof(): + raise dns.exception.SyntaxError( + 'expected EOL or EOF, got %d "%s"' % (token.ttype, token.value) + ) + return token + + def get_eol(self) -> str: + return self.get_eol_as_token().value + + def get_ttl(self) -> int: + """Read the next token and interpret it as a DNS TTL. + + Raises dns.exception.SyntaxError or dns.ttl.BadTTL if not an + identifier or badly formed. + + Returns an int. + """ + + token = self.get().unescape() + if not token.is_identifier(): + raise dns.exception.SyntaxError("expecting an identifier") + return dns.ttl.from_text(token.value) diff --git a/.venv/lib/python3.9/site-packages/dns/transaction.py b/.venv/lib/python3.9/site-packages/dns/transaction.py new file mode 100644 index 0000000..aa2e116 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/transaction.py @@ -0,0 +1,649 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import collections +from typing import Any, Callable, Iterator, List, Optional, Tuple, Union + +import dns.exception +import dns.name +import dns.node +import dns.rdataclass +import dns.rdataset +import dns.rdatatype +import dns.rrset +import dns.serial +import dns.ttl + + +class TransactionManager: + def reader(self) -> "Transaction": + """Begin a read-only transaction.""" + raise NotImplementedError # pragma: no cover + + def writer(self, replacement: bool = False) -> "Transaction": + """Begin a writable transaction. + + *replacement*, a ``bool``. If `True`, the content of the + transaction completely replaces any prior content. If False, + the default, then the content of the transaction updates the + existing content. + """ + raise NotImplementedError # pragma: no cover + + def origin_information( + self, + ) -> Tuple[Optional[dns.name.Name], bool, Optional[dns.name.Name]]: + """Returns a tuple + + (absolute_origin, relativize, effective_origin) + + giving the absolute name of the default origin for any + relative domain names, the "effective origin", and whether + names should be relativized. The "effective origin" is the + absolute origin if relativize is False, and the empty name if + relativize is true. (The effective origin is provided even + though it can be computed from the absolute_origin and + relativize setting because it avoids a lot of code + duplication.) + + If the returned names are `None`, then no origin information is + available. + + This information is used by code working with transactions to + allow it to coordinate relativization. The transaction code + itself takes what it gets (i.e. does not change name + relativity). + + """ + raise NotImplementedError # pragma: no cover + + def get_class(self) -> dns.rdataclass.RdataClass: + """The class of the transaction manager.""" + raise NotImplementedError # pragma: no cover + + def from_wire_origin(self) -> Optional[dns.name.Name]: + """Origin to use in from_wire() calls.""" + (absolute_origin, relativize, _) = self.origin_information() + if relativize: + return absolute_origin + else: + return None + + +class DeleteNotExact(dns.exception.DNSException): + """Existing data did not match data specified by an exact delete.""" + + +class ReadOnly(dns.exception.DNSException): + """Tried to write to a read-only transaction.""" + + +class AlreadyEnded(dns.exception.DNSException): + """Tried to use an already-ended transaction.""" + + +def _ensure_immutable_rdataset(rdataset): + if rdataset is None or isinstance(rdataset, dns.rdataset.ImmutableRdataset): + return rdataset + return dns.rdataset.ImmutableRdataset(rdataset) + + +def _ensure_immutable_node(node): + if node is None or node.is_immutable(): + return node + return dns.node.ImmutableNode(node) + + +CheckPutRdatasetType = Callable[ + ["Transaction", dns.name.Name, dns.rdataset.Rdataset], None +] +CheckDeleteRdatasetType = Callable[ + ["Transaction", dns.name.Name, dns.rdatatype.RdataType, dns.rdatatype.RdataType], + None, +] +CheckDeleteNameType = Callable[["Transaction", dns.name.Name], None] + + +class Transaction: + def __init__( + self, + manager: TransactionManager, + replacement: bool = False, + read_only: bool = False, + ): + self.manager = manager + self.replacement = replacement + self.read_only = read_only + self._ended = False + self._check_put_rdataset: List[CheckPutRdatasetType] = [] + self._check_delete_rdataset: List[CheckDeleteRdatasetType] = [] + self._check_delete_name: List[CheckDeleteNameType] = [] + + # + # This is the high level API + # + # Note that we currently use non-immutable types in the return type signature to + # avoid covariance problems, e.g. if the caller has a List[Rdataset], mypy will be + # unhappy if we return an ImmutableRdataset. + + def get( + self, + name: Optional[Union[dns.name.Name, str]], + rdtype: Union[dns.rdatatype.RdataType, str], + covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, + ) -> dns.rdataset.Rdataset: + """Return the rdataset associated with *name*, *rdtype*, and *covers*, + or `None` if not found. + + Note that the returned rdataset is immutable. + """ + self._check_ended() + if isinstance(name, str): + name = dns.name.from_text(name, None) + rdtype = dns.rdatatype.RdataType.make(rdtype) + covers = dns.rdatatype.RdataType.make(covers) + rdataset = self._get_rdataset(name, rdtype, covers) + return _ensure_immutable_rdataset(rdataset) + + def get_node(self, name: dns.name.Name) -> Optional[dns.node.Node]: + """Return the node at *name*, if any. + + Returns an immutable node or ``None``. + """ + return _ensure_immutable_node(self._get_node(name)) + + def _check_read_only(self) -> None: + if self.read_only: + raise ReadOnly + + def add(self, *args: Any) -> None: + """Add records. + + The arguments may be: + + - rrset + + - name, rdataset... + + - name, ttl, rdata... + """ + self._check_ended() + self._check_read_only() + self._add(False, args) + + def replace(self, *args: Any) -> None: + """Replace the existing rdataset at the name with the specified + rdataset, or add the specified rdataset if there was no existing + rdataset. + + The arguments may be: + + - rrset + + - name, rdataset... + + - name, ttl, rdata... + + Note that if you want to replace the entire node, you should do + a delete of the name followed by one or more calls to add() or + replace(). + """ + self._check_ended() + self._check_read_only() + self._add(True, args) + + def delete(self, *args: Any) -> None: + """Delete records. + + It is not an error if some of the records are not in the existing + set. + + The arguments may be: + + - rrset + + - name + + - name, rdatatype, [covers] + + - name, rdataset... + + - name, rdata... + """ + self._check_ended() + self._check_read_only() + self._delete(False, args) + + def delete_exact(self, *args: Any) -> None: + """Delete records. + + The arguments may be: + + - rrset + + - name + + - name, rdatatype, [covers] + + - name, rdataset... + + - name, rdata... + + Raises dns.transaction.DeleteNotExact if some of the records + are not in the existing set. + + """ + self._check_ended() + self._check_read_only() + self._delete(True, args) + + def name_exists(self, name: Union[dns.name.Name, str]) -> bool: + """Does the specified name exist?""" + self._check_ended() + if isinstance(name, str): + name = dns.name.from_text(name, None) + return self._name_exists(name) + + def update_serial( + self, + value: int = 1, + relative: bool = True, + name: dns.name.Name = dns.name.empty, + ) -> None: + """Update the serial number. + + *value*, an `int`, is an increment if *relative* is `True`, or the + actual value to set if *relative* is `False`. + + Raises `KeyError` if there is no SOA rdataset at *name*. + + Raises `ValueError` if *value* is negative or if the increment is + so large that it would cause the new serial to be less than the + prior value. + """ + self._check_ended() + if value < 0: + raise ValueError("negative update_serial() value") + if isinstance(name, str): + name = dns.name.from_text(name, None) + rdataset = self._get_rdataset(name, dns.rdatatype.SOA, dns.rdatatype.NONE) + if rdataset is None or len(rdataset) == 0: + raise KeyError + if relative: + serial = dns.serial.Serial(rdataset[0].serial) + value + else: + serial = dns.serial.Serial(value) + serial = serial.value # convert back to int + if serial == 0: + serial = 1 + rdata = rdataset[0].replace(serial=serial) + new_rdataset = dns.rdataset.from_rdata(rdataset.ttl, rdata) + self.replace(name, new_rdataset) + + def __iter__(self): + self._check_ended() + return self._iterate_rdatasets() + + def changed(self) -> bool: + """Has this transaction changed anything? + + For read-only transactions, the result is always `False`. + + For writable transactions, the result is `True` if at some time + during the life of the transaction, the content was changed. + """ + self._check_ended() + return self._changed() + + def commit(self) -> None: + """Commit the transaction. + + Normally transactions are used as context managers and commit + or rollback automatically, but it may be done explicitly if needed. + A ``dns.transaction.Ended`` exception will be raised if you try + to use a transaction after it has been committed or rolled back. + + Raises an exception if the commit fails (in which case the transaction + is also rolled back. + """ + self._end(True) + + def rollback(self) -> None: + """Rollback the transaction. + + Normally transactions are used as context managers and commit + or rollback automatically, but it may be done explicitly if needed. + A ``dns.transaction.AlreadyEnded`` exception will be raised if you try + to use a transaction after it has been committed or rolled back. + + Rollback cannot otherwise fail. + """ + self._end(False) + + def check_put_rdataset(self, check: CheckPutRdatasetType) -> None: + """Call *check* before putting (storing) an rdataset. + + The function is called with the transaction, the name, and the rdataset. + + The check function may safely make non-mutating transaction method + calls, but behavior is undefined if mutating transaction methods are + called. The check function should raise an exception if it objects to + the put, and otherwise should return ``None``. + """ + self._check_put_rdataset.append(check) + + def check_delete_rdataset(self, check: CheckDeleteRdatasetType) -> None: + """Call *check* before deleting an rdataset. + + The function is called with the transaction, the name, the rdatatype, + and the covered rdatatype. + + The check function may safely make non-mutating transaction method + calls, but behavior is undefined if mutating transaction methods are + called. The check function should raise an exception if it objects to + the put, and otherwise should return ``None``. + """ + self._check_delete_rdataset.append(check) + + def check_delete_name(self, check: CheckDeleteNameType) -> None: + """Call *check* before putting (storing) an rdataset. + + The function is called with the transaction and the name. + + The check function may safely make non-mutating transaction method + calls, but behavior is undefined if mutating transaction methods are + called. The check function should raise an exception if it objects to + the put, and otherwise should return ``None``. + """ + self._check_delete_name.append(check) + + def iterate_rdatasets( + self, + ) -> Iterator[Tuple[dns.name.Name, dns.rdataset.Rdataset]]: + """Iterate all the rdatasets in the transaction, returning + (`dns.name.Name`, `dns.rdataset.Rdataset`) tuples. + + Note that as is usual with python iterators, adding or removing items + while iterating will invalidate the iterator and may raise `RuntimeError` + or fail to iterate over all entries.""" + self._check_ended() + return self._iterate_rdatasets() + + def iterate_names(self) -> Iterator[dns.name.Name]: + """Iterate all the names in the transaction. + + Note that as is usual with python iterators, adding or removing names + while iterating will invalidate the iterator and may raise `RuntimeError` + or fail to iterate over all entries.""" + self._check_ended() + return self._iterate_names() + + # + # Helper methods + # + + def _raise_if_not_empty(self, method, args): + if len(args) != 0: + raise TypeError(f"extra parameters to {method}") + + def _rdataset_from_args(self, method, deleting, args): + try: + arg = args.popleft() + if isinstance(arg, dns.rrset.RRset): + rdataset = arg.to_rdataset() + elif isinstance(arg, dns.rdataset.Rdataset): + rdataset = arg + else: + if deleting: + ttl = 0 + else: + if isinstance(arg, int): + ttl = arg + if ttl > dns.ttl.MAX_TTL: + raise ValueError(f"{method}: TTL value too big") + else: + raise TypeError(f"{method}: expected a TTL") + arg = args.popleft() + if isinstance(arg, dns.rdata.Rdata): + rdataset = dns.rdataset.from_rdata(ttl, arg) + else: + raise TypeError(f"{method}: expected an Rdata") + return rdataset + except IndexError: + if deleting: + return None + else: + # reraise + raise TypeError(f"{method}: expected more arguments") + + def _add(self, replace, args): + try: + args = collections.deque(args) + if replace: + method = "replace()" + else: + method = "add()" + arg = args.popleft() + if isinstance(arg, str): + arg = dns.name.from_text(arg, None) + if isinstance(arg, dns.name.Name): + name = arg + rdataset = self._rdataset_from_args(method, False, args) + elif isinstance(arg, dns.rrset.RRset): + rrset = arg + name = rrset.name + # rrsets are also rdatasets, but they don't print the + # same and can't be stored in nodes, so convert. + rdataset = rrset.to_rdataset() + else: + raise TypeError( + f"{method} requires a name or RRset as the first argument" + ) + if rdataset.rdclass != self.manager.get_class(): + raise ValueError(f"{method} has objects of wrong RdataClass") + if rdataset.rdtype == dns.rdatatype.SOA: + (_, _, origin) = self._origin_information() + if name != origin: + raise ValueError(f"{method} has non-origin SOA") + self._raise_if_not_empty(method, args) + if not replace: + existing = self._get_rdataset(name, rdataset.rdtype, rdataset.covers) + if existing is not None: + if isinstance(existing, dns.rdataset.ImmutableRdataset): + trds = dns.rdataset.Rdataset( + existing.rdclass, existing.rdtype, existing.covers + ) + trds.update(existing) + existing = trds + rdataset = existing.union(rdataset) + self._checked_put_rdataset(name, rdataset) + except IndexError: + raise TypeError(f"not enough parameters to {method}") + + def _delete(self, exact, args): + try: + args = collections.deque(args) + if exact: + method = "delete_exact()" + else: + method = "delete()" + arg = args.popleft() + if isinstance(arg, str): + arg = dns.name.from_text(arg, None) + if isinstance(arg, dns.name.Name): + name = arg + if len(args) > 0 and ( + isinstance(args[0], int) or isinstance(args[0], str) + ): + # deleting by type and (optionally) covers + rdtype = dns.rdatatype.RdataType.make(args.popleft()) + if len(args) > 0: + covers = dns.rdatatype.RdataType.make(args.popleft()) + else: + covers = dns.rdatatype.NONE + self._raise_if_not_empty(method, args) + existing = self._get_rdataset(name, rdtype, covers) + if existing is None: + if exact: + raise DeleteNotExact(f"{method}: missing rdataset") + else: + self._checked_delete_rdataset(name, rdtype, covers) + return + else: + rdataset = self._rdataset_from_args(method, True, args) + elif isinstance(arg, dns.rrset.RRset): + rdataset = arg # rrsets are also rdatasets + name = rdataset.name + else: + raise TypeError( + f"{method} requires a name or RRset as the first argument" + ) + self._raise_if_not_empty(method, args) + if rdataset: + if rdataset.rdclass != self.manager.get_class(): + raise ValueError(f"{method} has objects of wrong RdataClass") + existing = self._get_rdataset(name, rdataset.rdtype, rdataset.covers) + if existing is not None: + if exact: + intersection = existing.intersection(rdataset) + if intersection != rdataset: + raise DeleteNotExact(f"{method}: missing rdatas") + rdataset = existing.difference(rdataset) + if len(rdataset) == 0: + self._checked_delete_rdataset( + name, rdataset.rdtype, rdataset.covers + ) + else: + self._checked_put_rdataset(name, rdataset) + elif exact: + raise DeleteNotExact(f"{method}: missing rdataset") + else: + if exact and not self._name_exists(name): + raise DeleteNotExact(f"{method}: name not known") + self._checked_delete_name(name) + except IndexError: + raise TypeError(f"not enough parameters to {method}") + + def _check_ended(self): + if self._ended: + raise AlreadyEnded + + def _end(self, commit): + self._check_ended() + try: + self._end_transaction(commit) + finally: + self._ended = True + + def _checked_put_rdataset(self, name, rdataset): + for check in self._check_put_rdataset: + check(self, name, rdataset) + self._put_rdataset(name, rdataset) + + def _checked_delete_rdataset(self, name, rdtype, covers): + for check in self._check_delete_rdataset: + check(self, name, rdtype, covers) + self._delete_rdataset(name, rdtype, covers) + + def _checked_delete_name(self, name): + for check in self._check_delete_name: + check(self, name) + self._delete_name(name) + + # + # Transactions are context managers. + # + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + if not self._ended: + if exc_type is None: + self.commit() + else: + self.rollback() + return False + + # + # This is the low level API, which must be implemented by subclasses + # of Transaction. + # + + def _get_rdataset(self, name, rdtype, covers): + """Return the rdataset associated with *name*, *rdtype*, and *covers*, + or `None` if not found. + """ + raise NotImplementedError # pragma: no cover + + def _put_rdataset(self, name, rdataset): + """Store the rdataset.""" + raise NotImplementedError # pragma: no cover + + def _delete_name(self, name): + """Delete all data associated with *name*. + + It is not an error if the name does not exist. + """ + raise NotImplementedError # pragma: no cover + + def _delete_rdataset(self, name, rdtype, covers): + """Delete all data associated with *name*, *rdtype*, and *covers*. + + It is not an error if the rdataset does not exist. + """ + raise NotImplementedError # pragma: no cover + + def _name_exists(self, name): + """Does name exist? + + Returns a bool. + """ + raise NotImplementedError # pragma: no cover + + def _changed(self): + """Has this transaction changed anything?""" + raise NotImplementedError # pragma: no cover + + def _end_transaction(self, commit): + """End the transaction. + + *commit*, a bool. If ``True``, commit the transaction, otherwise + roll it back. + + If committing and the commit fails, then roll back and raise an + exception. + """ + raise NotImplementedError # pragma: no cover + + def _set_origin(self, origin): + """Set the origin. + + This method is called when reading a possibly relativized + source, and an origin setting operation occurs (e.g. $ORIGIN + in a zone file). + """ + raise NotImplementedError # pragma: no cover + + def _iterate_rdatasets(self): + """Return an iterator that yields (name, rdataset) tuples.""" + raise NotImplementedError # pragma: no cover + + def _iterate_names(self): + """Return an iterator that yields a name.""" + raise NotImplementedError # pragma: no cover + + def _get_node(self, name): + """Return the node at *name*, if any. + + Returns a node or ``None``. + """ + raise NotImplementedError # pragma: no cover + + # + # Low-level API with a default implementation, in case a subclass needs + # to override. + # + + def _origin_information(self): + # This is only used by _add() + return self.manager.origin_information() diff --git a/.venv/lib/python3.9/site-packages/dns/tsig.py b/.venv/lib/python3.9/site-packages/dns/tsig.py new file mode 100644 index 0000000..780852e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/tsig.py @@ -0,0 +1,352 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS TSIG support.""" + +import base64 +import hashlib +import hmac +import struct + +import dns.exception +import dns.name +import dns.rcode +import dns.rdataclass + + +class BadTime(dns.exception.DNSException): + """The current time is not within the TSIG's validity time.""" + + +class BadSignature(dns.exception.DNSException): + """The TSIG signature fails to verify.""" + + +class BadKey(dns.exception.DNSException): + """The TSIG record owner name does not match the key.""" + + +class BadAlgorithm(dns.exception.DNSException): + """The TSIG algorithm does not match the key.""" + + +class PeerError(dns.exception.DNSException): + """Base class for all TSIG errors generated by the remote peer""" + + +class PeerBadKey(PeerError): + """The peer didn't know the key we used""" + + +class PeerBadSignature(PeerError): + """The peer didn't like the signature we sent""" + + +class PeerBadTime(PeerError): + """The peer didn't like the time we sent""" + + +class PeerBadTruncation(PeerError): + """The peer didn't like amount of truncation in the TSIG we sent""" + + +# TSIG Algorithms + +HMAC_MD5 = dns.name.from_text("HMAC-MD5.SIG-ALG.REG.INT") +HMAC_SHA1 = dns.name.from_text("hmac-sha1") +HMAC_SHA224 = dns.name.from_text("hmac-sha224") +HMAC_SHA256 = dns.name.from_text("hmac-sha256") +HMAC_SHA256_128 = dns.name.from_text("hmac-sha256-128") +HMAC_SHA384 = dns.name.from_text("hmac-sha384") +HMAC_SHA384_192 = dns.name.from_text("hmac-sha384-192") +HMAC_SHA512 = dns.name.from_text("hmac-sha512") +HMAC_SHA512_256 = dns.name.from_text("hmac-sha512-256") +GSS_TSIG = dns.name.from_text("gss-tsig") + +default_algorithm = HMAC_SHA256 + +mac_sizes = { + HMAC_SHA1: 20, + HMAC_SHA224: 28, + HMAC_SHA256: 32, + HMAC_SHA256_128: 16, + HMAC_SHA384: 48, + HMAC_SHA384_192: 24, + HMAC_SHA512: 64, + HMAC_SHA512_256: 32, + HMAC_MD5: 16, + GSS_TSIG: 128, # This is what we assume to be the worst case! +} + + +class GSSTSig: + """ + GSS-TSIG TSIG implementation. This uses the GSS-API context established + in the TKEY message handshake to sign messages using GSS-API message + integrity codes, per the RFC. + + In order to avoid a direct GSSAPI dependency, the keyring holds a ref + to the GSSAPI object required, rather than the key itself. + """ + + def __init__(self, gssapi_context): + self.gssapi_context = gssapi_context + self.data = b"" + self.name = "gss-tsig" + + def update(self, data): + self.data += data + + def sign(self): + # defer to the GSSAPI function to sign + return self.gssapi_context.get_signature(self.data) + + def verify(self, expected): + try: + # defer to the GSSAPI function to verify + return self.gssapi_context.verify_signature(self.data, expected) + except Exception: + # note the usage of a bare exception + raise BadSignature + + +class GSSTSigAdapter: + def __init__(self, keyring): + self.keyring = keyring + + def __call__(self, message, keyname): + if keyname in self.keyring: + key = self.keyring[keyname] + if isinstance(key, Key) and key.algorithm == GSS_TSIG: + if message: + GSSTSigAdapter.parse_tkey_and_step(key, message, keyname) + return key + else: + return None + + @classmethod + def parse_tkey_and_step(cls, key, message, keyname): + # if the message is a TKEY type, absorb the key material + # into the context using step(); this is used to allow the + # client to complete the GSSAPI negotiation before attempting + # to verify the signed response to a TKEY message exchange + try: + rrset = message.find_rrset( + message.answer, keyname, dns.rdataclass.ANY, dns.rdatatype.TKEY + ) + if rrset: + token = rrset[0].key + gssapi_context = key.secret + return gssapi_context.step(token) + except KeyError: + pass + + +class HMACTSig: + """ + HMAC TSIG implementation. This uses the HMAC python module to handle the + sign/verify operations. + """ + + _hashes = { + HMAC_SHA1: hashlib.sha1, + HMAC_SHA224: hashlib.sha224, + HMAC_SHA256: hashlib.sha256, + HMAC_SHA256_128: (hashlib.sha256, 128), + HMAC_SHA384: hashlib.sha384, + HMAC_SHA384_192: (hashlib.sha384, 192), + HMAC_SHA512: hashlib.sha512, + HMAC_SHA512_256: (hashlib.sha512, 256), + HMAC_MD5: hashlib.md5, + } + + def __init__(self, key, algorithm): + try: + hashinfo = self._hashes[algorithm] + except KeyError: + raise NotImplementedError(f"TSIG algorithm {algorithm} is not supported") + + # create the HMAC context + if isinstance(hashinfo, tuple): + self.hmac_context = hmac.new(key, digestmod=hashinfo[0]) + self.size = hashinfo[1] + else: + self.hmac_context = hmac.new(key, digestmod=hashinfo) + self.size = None + self.name = self.hmac_context.name + if self.size: + self.name += f"-{self.size}" + + def update(self, data): + return self.hmac_context.update(data) + + def sign(self): + # defer to the HMAC digest() function for that digestmod + digest = self.hmac_context.digest() + if self.size: + digest = digest[: (self.size // 8)] + return digest + + def verify(self, expected): + # re-digest and compare the results + mac = self.sign() + if not hmac.compare_digest(mac, expected): + raise BadSignature + + +def _digest(wire, key, rdata, time=None, request_mac=None, ctx=None, multi=None): + """Return a context containing the TSIG rdata for the input parameters + @rtype: dns.tsig.HMACTSig or dns.tsig.GSSTSig object + @raises ValueError: I{other_data} is too long + @raises NotImplementedError: I{algorithm} is not supported + """ + + first = not (ctx and multi) + if first: + ctx = get_context(key) + if request_mac: + ctx.update(struct.pack("!H", len(request_mac))) + ctx.update(request_mac) + ctx.update(struct.pack("!H", rdata.original_id)) + ctx.update(wire[2:]) + if first: + ctx.update(key.name.to_digestable()) + ctx.update(struct.pack("!H", dns.rdataclass.ANY)) + ctx.update(struct.pack("!I", 0)) + if time is None: + time = rdata.time_signed + upper_time = (time >> 32) & 0xFFFF + lower_time = time & 0xFFFFFFFF + time_encoded = struct.pack("!HIH", upper_time, lower_time, rdata.fudge) + other_len = len(rdata.other) + if other_len > 65535: + raise ValueError("TSIG Other Data is > 65535 bytes") + if first: + ctx.update(key.algorithm.to_digestable() + time_encoded) + ctx.update(struct.pack("!HH", rdata.error, other_len) + rdata.other) + else: + ctx.update(time_encoded) + return ctx + + +def _maybe_start_digest(key, mac, multi): + """If this is the first message in a multi-message sequence, + start a new context. + @rtype: dns.tsig.HMACTSig or dns.tsig.GSSTSig object + """ + if multi: + ctx = get_context(key) + ctx.update(struct.pack("!H", len(mac))) + ctx.update(mac) + return ctx + else: + return None + + +def sign(wire, key, rdata, time=None, request_mac=None, ctx=None, multi=False): + """Return a (tsig_rdata, mac, ctx) tuple containing the HMAC TSIG rdata + for the input parameters, the HMAC MAC calculated by applying the + TSIG signature algorithm, and the TSIG digest context. + @rtype: (string, dns.tsig.HMACTSig or dns.tsig.GSSTSig object) + @raises ValueError: I{other_data} is too long + @raises NotImplementedError: I{algorithm} is not supported + """ + + ctx = _digest(wire, key, rdata, time, request_mac, ctx, multi) + mac = ctx.sign() + tsig = rdata.replace(time_signed=time, mac=mac) + + return (tsig, _maybe_start_digest(key, mac, multi)) + + +def validate( + wire, key, owner, rdata, now, request_mac, tsig_start, ctx=None, multi=False +): + """Validate the specified TSIG rdata against the other input parameters. + + @raises FormError: The TSIG is badly formed. + @raises BadTime: There is too much time skew between the client and the + server. + @raises BadSignature: The TSIG signature did not validate + @rtype: dns.tsig.HMACTSig or dns.tsig.GSSTSig object""" + + (adcount,) = struct.unpack("!H", wire[10:12]) + if adcount == 0: + raise dns.exception.FormError + adcount -= 1 + new_wire = wire[0:10] + struct.pack("!H", adcount) + wire[12:tsig_start] + if rdata.error != 0: + if rdata.error == dns.rcode.BADSIG: + raise PeerBadSignature + elif rdata.error == dns.rcode.BADKEY: + raise PeerBadKey + elif rdata.error == dns.rcode.BADTIME: + raise PeerBadTime + elif rdata.error == dns.rcode.BADTRUNC: + raise PeerBadTruncation + else: + raise PeerError("unknown TSIG error code %d" % rdata.error) + if abs(rdata.time_signed - now) > rdata.fudge: + raise BadTime + if key.name != owner: + raise BadKey + if key.algorithm != rdata.algorithm: + raise BadAlgorithm + ctx = _digest(new_wire, key, rdata, None, request_mac, ctx, multi) + ctx.verify(rdata.mac) + return _maybe_start_digest(key, rdata.mac, multi) + + +def get_context(key): + """Returns an HMAC context for the specified key. + + @rtype: HMAC context + @raises NotImplementedError: I{algorithm} is not supported + """ + + if key.algorithm == GSS_TSIG: + return GSSTSig(key.secret) + else: + return HMACTSig(key.secret, key.algorithm) + + +class Key: + def __init__(self, name, secret, algorithm=default_algorithm): + if isinstance(name, str): + name = dns.name.from_text(name) + self.name = name + if isinstance(secret, str): + secret = base64.decodebytes(secret.encode()) + self.secret = secret + if isinstance(algorithm, str): + algorithm = dns.name.from_text(algorithm) + self.algorithm = algorithm + + def __eq__(self, other): + return ( + isinstance(other, Key) + and self.name == other.name + and self.secret == other.secret + and self.algorithm == other.algorithm + ) + + def __repr__(self): + r = f" Dict[dns.name.Name, dns.tsig.Key]: + """Convert a dictionary containing (textual DNS name, base64 secret) + pairs into a binary keyring which has (dns.name.Name, bytes) pairs, or + a dictionary containing (textual DNS name, (algorithm, base64 secret)) + pairs into a binary keyring which has (dns.name.Name, dns.tsig.Key) pairs. + @rtype: dict""" + + keyring = {} + for name, value in textring.items(): + kname = dns.name.from_text(name) + if isinstance(value, str): + keyring[kname] = dns.tsig.Key(kname, value).secret + else: + (algorithm, secret) = value + keyring[kname] = dns.tsig.Key(kname, secret, algorithm) + return keyring + + +def to_text(keyring: Dict[dns.name.Name, Any]) -> Dict[str, Any]: + """Convert a dictionary containing (dns.name.Name, dns.tsig.Key) pairs + into a text keyring which has (textual DNS name, (textual algorithm, + base64 secret)) pairs, or a dictionary containing (dns.name.Name, bytes) + pairs into a text keyring which has (textual DNS name, base64 secret) pairs. + @rtype: dict""" + + textring = {} + + def b64encode(secret): + return base64.encodebytes(secret).decode().rstrip() + + for name, key in keyring.items(): + tname = name.to_text() + if isinstance(key, bytes): + textring[tname] = b64encode(key) + else: + if isinstance(key.secret, bytes): + text_secret = b64encode(key.secret) + else: + text_secret = str(key.secret) + + textring[tname] = (key.algorithm.to_text(), text_secret) + return textring diff --git a/.venv/lib/python3.9/site-packages/dns/ttl.py b/.venv/lib/python3.9/site-packages/dns/ttl.py new file mode 100644 index 0000000..b9a99fe --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/ttl.py @@ -0,0 +1,92 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS TTL conversion.""" + +from typing import Union + +import dns.exception + +# Technically TTLs are supposed to be between 0 and 2**31 - 1, with values +# greater than that interpreted as 0, but we do not impose this policy here +# as values > 2**31 - 1 occur in real world data. +# +# We leave it to applications to impose tighter bounds if desired. +MAX_TTL = 2**32 - 1 + + +class BadTTL(dns.exception.SyntaxError): + """DNS TTL value is not well-formed.""" + + +def from_text(text: str) -> int: + """Convert the text form of a TTL to an integer. + + The BIND 8 units syntax for TTLs (e.g. '1w6d4h3m10s') is supported. + + *text*, a ``str``, the textual TTL. + + Raises ``dns.ttl.BadTTL`` if the TTL is not well-formed. + + Returns an ``int``. + """ + + if text.isdigit(): + total = int(text) + elif len(text) == 0: + raise BadTTL + else: + total = 0 + current = 0 + need_digit = True + for c in text: + if c.isdigit(): + current *= 10 + current += int(c) + need_digit = False + else: + if need_digit: + raise BadTTL + c = c.lower() + if c == "w": + total += current * 604800 + elif c == "d": + total += current * 86400 + elif c == "h": + total += current * 3600 + elif c == "m": + total += current * 60 + elif c == "s": + total += current + else: + raise BadTTL(f"unknown unit '{c}'") + current = 0 + need_digit = True + if not current == 0: + raise BadTTL("trailing integer") + if total < 0 or total > MAX_TTL: + raise BadTTL("TTL should be between 0 and 2**32 - 1 (inclusive)") + return total + + +def make(value: Union[int, str]) -> int: + if isinstance(value, int): + return value + elif isinstance(value, str): + return dns.ttl.from_text(value) + else: + raise ValueError("cannot convert value to TTL") diff --git a/.venv/lib/python3.9/site-packages/dns/update.py b/.venv/lib/python3.9/site-packages/dns/update.py new file mode 100644 index 0000000..bf1157a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/update.py @@ -0,0 +1,386 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Dynamic Update Support""" + +from typing import Any, List, Optional, Union + +import dns.message +import dns.name +import dns.opcode +import dns.rdata +import dns.rdataclass +import dns.rdataset +import dns.rdatatype +import dns.tsig + + +class UpdateSection(dns.enum.IntEnum): + """Update sections""" + + ZONE = 0 + PREREQ = 1 + UPDATE = 2 + ADDITIONAL = 3 + + @classmethod + def _maximum(cls): + return 3 + + +class UpdateMessage(dns.message.Message): # lgtm[py/missing-equals] + # ignore the mypy error here as we mean to use a different enum + _section_enum = UpdateSection # type: ignore + + def __init__( + self, + zone: Optional[Union[dns.name.Name, str]] = None, + rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, + keyring: Optional[Any] = None, + keyname: Optional[dns.name.Name] = None, + keyalgorithm: Union[dns.name.Name, str] = dns.tsig.default_algorithm, + id: Optional[int] = None, + ): + """Initialize a new DNS Update object. + + See the documentation of the Message class for a complete + description of the keyring dictionary. + + *zone*, a ``dns.name.Name``, ``str``, or ``None``, the zone + which is being updated. ``None`` should only be used by dnspython's + message constructors, as a zone is required for the convenience + methods like ``add()``, ``replace()``, etc. + + *rdclass*, an ``int`` or ``str``, the class of the zone. + + The *keyring*, *keyname*, and *keyalgorithm* parameters are passed to + ``use_tsig()``; see its documentation for details. + """ + super().__init__(id=id) + self.flags |= dns.opcode.to_flags(dns.opcode.UPDATE) + if isinstance(zone, str): + zone = dns.name.from_text(zone) + self.origin = zone + rdclass = dns.rdataclass.RdataClass.make(rdclass) + self.zone_rdclass = rdclass + if self.origin: + self.find_rrset( + self.zone, + self.origin, + rdclass, + dns.rdatatype.SOA, + create=True, + force_unique=True, + ) + if keyring is not None: + self.use_tsig(keyring, keyname, algorithm=keyalgorithm) + + @property + def zone(self) -> List[dns.rrset.RRset]: + """The zone section.""" + return self.sections[0] + + @zone.setter + def zone(self, v): + self.sections[0] = v + + @property + def prerequisite(self) -> List[dns.rrset.RRset]: + """The prerequisite section.""" + return self.sections[1] + + @prerequisite.setter + def prerequisite(self, v): + self.sections[1] = v + + @property + def update(self) -> List[dns.rrset.RRset]: + """The update section.""" + return self.sections[2] + + @update.setter + def update(self, v): + self.sections[2] = v + + def _add_rr(self, name, ttl, rd, deleting=None, section=None): + """Add a single RR to the update section.""" + + if section is None: + section = self.update + covers = rd.covers() + rrset = self.find_rrset( + section, name, self.zone_rdclass, rd.rdtype, covers, deleting, True, True + ) + rrset.add(rd, ttl) + + def _add(self, replace, section, name, *args): + """Add records. + + *replace* is the replacement mode. If ``False``, + RRs are added to an existing RRset; if ``True``, the RRset + is replaced with the specified contents. The second + argument is the section to add to. The third argument + is always a name. The other arguments can be: + + - rdataset... + + - ttl, rdata... + + - ttl, rdtype, string... + """ + + if isinstance(name, str): + name = dns.name.from_text(name, None) + if isinstance(args[0], dns.rdataset.Rdataset): + for rds in args: + if replace: + self.delete(name, rds.rdtype) + for rd in rds: + self._add_rr(name, rds.ttl, rd, section=section) + else: + args = list(args) + ttl = int(args.pop(0)) + if isinstance(args[0], dns.rdata.Rdata): + if replace: + self.delete(name, args[0].rdtype) + for rd in args: + self._add_rr(name, ttl, rd, section=section) + else: + rdtype = dns.rdatatype.RdataType.make(args.pop(0)) + if replace: + self.delete(name, rdtype) + for s in args: + rd = dns.rdata.from_text(self.zone_rdclass, rdtype, s, self.origin) + self._add_rr(name, ttl, rd, section=section) + + def add(self, name: Union[dns.name.Name, str], *args: Any) -> None: + """Add records. + + The first argument is always a name. The other + arguments can be: + + - rdataset... + + - ttl, rdata... + + - ttl, rdtype, string... + """ + + self._add(False, self.update, name, *args) + + def delete(self, name: Union[dns.name.Name, str], *args: Any) -> None: + """Delete records. + + The first argument is always a name. The other + arguments can be: + + - *empty* + + - rdataset... + + - rdata... + + - rdtype, [string...] + """ + + if isinstance(name, str): + name = dns.name.from_text(name, None) + if len(args) == 0: + self.find_rrset( + self.update, + name, + dns.rdataclass.ANY, + dns.rdatatype.ANY, + dns.rdatatype.NONE, + dns.rdataclass.ANY, + True, + True, + ) + elif isinstance(args[0], dns.rdataset.Rdataset): + for rds in args: + for rd in rds: + self._add_rr(name, 0, rd, dns.rdataclass.NONE) + else: + largs = list(args) + if isinstance(largs[0], dns.rdata.Rdata): + for rd in largs: + self._add_rr(name, 0, rd, dns.rdataclass.NONE) + else: + rdtype = dns.rdatatype.RdataType.make(largs.pop(0)) + if len(largs) == 0: + self.find_rrset( + self.update, + name, + self.zone_rdclass, + rdtype, + dns.rdatatype.NONE, + dns.rdataclass.ANY, + True, + True, + ) + else: + for s in largs: + rd = dns.rdata.from_text( + self.zone_rdclass, + rdtype, + s, # type: ignore[arg-type] + self.origin, + ) + self._add_rr(name, 0, rd, dns.rdataclass.NONE) + + def replace(self, name: Union[dns.name.Name, str], *args: Any) -> None: + """Replace records. + + The first argument is always a name. The other + arguments can be: + + - rdataset... + + - ttl, rdata... + + - ttl, rdtype, string... + + Note that if you want to replace the entire node, you should do + a delete of the name followed by one or more calls to add. + """ + + self._add(True, self.update, name, *args) + + def present(self, name: Union[dns.name.Name, str], *args: Any) -> None: + """Require that an owner name (and optionally an rdata type, + or specific rdataset) exists as a prerequisite to the + execution of the update. + + The first argument is always a name. + The other arguments can be: + + - rdataset... + + - rdata... + + - rdtype, string... + """ + + if isinstance(name, str): + name = dns.name.from_text(name, None) + if len(args) == 0: + self.find_rrset( + self.prerequisite, + name, + dns.rdataclass.ANY, + dns.rdatatype.ANY, + dns.rdatatype.NONE, + None, + True, + True, + ) + elif ( + isinstance(args[0], dns.rdataset.Rdataset) + or isinstance(args[0], dns.rdata.Rdata) + or len(args) > 1 + ): + if not isinstance(args[0], dns.rdataset.Rdataset): + # Add a 0 TTL + largs = list(args) + largs.insert(0, 0) # type: ignore[arg-type] + self._add(False, self.prerequisite, name, *largs) + else: + self._add(False, self.prerequisite, name, *args) + else: + rdtype = dns.rdatatype.RdataType.make(args[0]) + self.find_rrset( + self.prerequisite, + name, + dns.rdataclass.ANY, + rdtype, + dns.rdatatype.NONE, + None, + True, + True, + ) + + def absent( + self, + name: Union[dns.name.Name, str], + rdtype: Optional[Union[dns.rdatatype.RdataType, str]] = None, + ) -> None: + """Require that an owner name (and optionally an rdata type) does + not exist as a prerequisite to the execution of the update.""" + + if isinstance(name, str): + name = dns.name.from_text(name, None) + if rdtype is None: + self.find_rrset( + self.prerequisite, + name, + dns.rdataclass.NONE, + dns.rdatatype.ANY, + dns.rdatatype.NONE, + None, + True, + True, + ) + else: + rdtype = dns.rdatatype.RdataType.make(rdtype) + self.find_rrset( + self.prerequisite, + name, + dns.rdataclass.NONE, + rdtype, + dns.rdatatype.NONE, + None, + True, + True, + ) + + def _get_one_rr_per_rrset(self, value): + # Updates are always one_rr_per_rrset + return True + + def _parse_rr_header(self, section, name, rdclass, rdtype): + deleting = None + empty = False + if section == UpdateSection.ZONE: + if ( + dns.rdataclass.is_metaclass(rdclass) + or rdtype != dns.rdatatype.SOA + or self.zone + ): + raise dns.exception.FormError + else: + if not self.zone: + raise dns.exception.FormError + if rdclass in (dns.rdataclass.ANY, dns.rdataclass.NONE): + deleting = rdclass + rdclass = self.zone[0].rdclass + empty = ( + deleting == dns.rdataclass.ANY or section == UpdateSection.PREREQ + ) + return (rdclass, rdtype, deleting, empty) + + +# backwards compatibility +Update = UpdateMessage + +### BEGIN generated UpdateSection constants + +ZONE = UpdateSection.ZONE +PREREQ = UpdateSection.PREREQ +UPDATE = UpdateSection.UPDATE +ADDITIONAL = UpdateSection.ADDITIONAL + +### END generated UpdateSection constants diff --git a/.venv/lib/python3.9/site-packages/dns/version.py b/.venv/lib/python3.9/site-packages/dns/version.py new file mode 100644 index 0000000..9ed2ce1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/version.py @@ -0,0 +1,58 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""dnspython release version information.""" + +#: MAJOR +MAJOR = 2 +#: MINOR +MINOR = 7 +#: MICRO +MICRO = 0 +#: RELEASELEVEL +RELEASELEVEL = 0x0F +#: SERIAL +SERIAL = 0 + +if RELEASELEVEL == 0x0F: # pragma: no cover lgtm[py/unreachable-statement] + #: version + version = "%d.%d.%d" % (MAJOR, MINOR, MICRO) # lgtm[py/unreachable-statement] +elif RELEASELEVEL == 0x00: # pragma: no cover lgtm[py/unreachable-statement] + version = "%d.%d.%ddev%d" % ( + MAJOR, + MINOR, + MICRO, + SERIAL, + ) # lgtm[py/unreachable-statement] +elif RELEASELEVEL == 0x0C: # pragma: no cover lgtm[py/unreachable-statement] + version = "%d.%d.%drc%d" % ( + MAJOR, + MINOR, + MICRO, + SERIAL, + ) # lgtm[py/unreachable-statement] +else: # pragma: no cover lgtm[py/unreachable-statement] + version = "%d.%d.%d%x%d" % ( + MAJOR, + MINOR, + MICRO, + RELEASELEVEL, + SERIAL, + ) # lgtm[py/unreachable-statement] + +#: hexversion +hexversion = MAJOR << 24 | MINOR << 16 | MICRO << 8 | RELEASELEVEL << 4 | SERIAL diff --git a/.venv/lib/python3.9/site-packages/dns/versioned.py b/.venv/lib/python3.9/site-packages/dns/versioned.py new file mode 100644 index 0000000..fd78e67 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/versioned.py @@ -0,0 +1,318 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +"""DNS Versioned Zones.""" + +import collections +import threading +from typing import Callable, Deque, Optional, Set, Union + +import dns.exception +import dns.immutable +import dns.name +import dns.node +import dns.rdataclass +import dns.rdataset +import dns.rdatatype +import dns.rdtypes.ANY.SOA +import dns.zone + + +class UseTransaction(dns.exception.DNSException): + """To alter a versioned zone, use a transaction.""" + + +# Backwards compatibility +Node = dns.zone.VersionedNode +ImmutableNode = dns.zone.ImmutableVersionedNode +Version = dns.zone.Version +WritableVersion = dns.zone.WritableVersion +ImmutableVersion = dns.zone.ImmutableVersion +Transaction = dns.zone.Transaction + + +class Zone(dns.zone.Zone): # lgtm[py/missing-equals] + __slots__ = [ + "_versions", + "_versions_lock", + "_write_txn", + "_write_waiters", + "_write_event", + "_pruning_policy", + "_readers", + ] + + node_factory = Node + + def __init__( + self, + origin: Optional[Union[dns.name.Name, str]], + rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, + relativize: bool = True, + pruning_policy: Optional[Callable[["Zone", Version], Optional[bool]]] = None, + ): + """Initialize a versioned zone object. + + *origin* is the origin of the zone. It may be a ``dns.name.Name``, + a ``str``, or ``None``. If ``None``, then the zone's origin will + be set by the first ``$ORIGIN`` line in a zone file. + + *rdclass*, an ``int``, the zone's rdata class; the default is class IN. + + *relativize*, a ``bool``, determine's whether domain names are + relativized to the zone's origin. The default is ``True``. + + *pruning policy*, a function taking a ``Zone`` and a ``Version`` and returning + a ``bool``, or ``None``. Should the version be pruned? If ``None``, + the default policy, which retains one version is used. + """ + super().__init__(origin, rdclass, relativize) + self._versions: Deque[Version] = collections.deque() + self._version_lock = threading.Lock() + if pruning_policy is None: + self._pruning_policy = self._default_pruning_policy + else: + self._pruning_policy = pruning_policy + self._write_txn: Optional[Transaction] = None + self._write_event: Optional[threading.Event] = None + self._write_waiters: Deque[threading.Event] = collections.deque() + self._readers: Set[Transaction] = set() + self._commit_version_unlocked( + None, WritableVersion(self, replacement=True), origin + ) + + def reader( + self, id: Optional[int] = None, serial: Optional[int] = None + ) -> Transaction: # pylint: disable=arguments-differ + if id is not None and serial is not None: + raise ValueError("cannot specify both id and serial") + with self._version_lock: + if id is not None: + version = None + for v in reversed(self._versions): + if v.id == id: + version = v + break + if version is None: + raise KeyError("version not found") + elif serial is not None: + if self.relativize: + oname = dns.name.empty + else: + assert self.origin is not None + oname = self.origin + version = None + for v in reversed(self._versions): + n = v.nodes.get(oname) + if n: + rds = n.get_rdataset(self.rdclass, dns.rdatatype.SOA) + if rds and rds[0].serial == serial: + version = v + break + if version is None: + raise KeyError("serial not found") + else: + version = self._versions[-1] + txn = Transaction(self, False, version) + self._readers.add(txn) + return txn + + def writer(self, replacement: bool = False) -> Transaction: + event = None + while True: + with self._version_lock: + # Checking event == self._write_event ensures that either + # no one was waiting before we got lucky and found no write + # txn, or we were the one who was waiting and got woken up. + # This prevents "taking cuts" when creating a write txn. + if self._write_txn is None and event == self._write_event: + # Creating the transaction defers version setup + # (i.e. copying the nodes dictionary) until we + # give up the lock, so that we hold the lock as + # short a time as possible. This is why we call + # _setup_version() below. + self._write_txn = Transaction( + self, replacement, make_immutable=True + ) + # give up our exclusive right to make a Transaction + self._write_event = None + break + # Someone else is writing already, so we will have to + # wait, but we want to do the actual wait outside the + # lock. + event = threading.Event() + self._write_waiters.append(event) + # wait (note we gave up the lock!) + # + # We only wake one sleeper at a time, so it's important + # that no event waiter can exit this method (e.g. via + # cancellation) without returning a transaction or waking + # someone else up. + # + # This is not a problem with Threading module threads as + # they cannot be canceled, but could be an issue with trio + # tasks when we do the async version of writer(). + # I.e. we'd need to do something like: + # + # try: + # event.wait() + # except trio.Cancelled: + # with self._version_lock: + # self._maybe_wakeup_one_waiter_unlocked() + # raise + # + event.wait() + # Do the deferred version setup. + self._write_txn._setup_version() + return self._write_txn + + def _maybe_wakeup_one_waiter_unlocked(self): + if len(self._write_waiters) > 0: + self._write_event = self._write_waiters.popleft() + self._write_event.set() + + # pylint: disable=unused-argument + def _default_pruning_policy(self, zone, version): + return True + + # pylint: enable=unused-argument + + def _prune_versions_unlocked(self): + assert len(self._versions) > 0 + # Don't ever prune a version greater than or equal to one that + # a reader has open. This pins versions in memory while the + # reader is open, and importantly lets the reader open a txn on + # a successor version (e.g. if generating an IXFR). + # + # Note our definition of least_kept also ensures we do not try to + # delete the greatest version. + if len(self._readers) > 0: + least_kept = min(txn.version.id for txn in self._readers) + else: + least_kept = self._versions[-1].id + while self._versions[0].id < least_kept and self._pruning_policy( + self, self._versions[0] + ): + self._versions.popleft() + + def set_max_versions(self, max_versions: Optional[int]) -> None: + """Set a pruning policy that retains up to the specified number + of versions + """ + if max_versions is not None and max_versions < 1: + raise ValueError("max versions must be at least 1") + if max_versions is None: + + def policy(zone, _): # pylint: disable=unused-argument + return False + + else: + + def policy(zone, _): + return len(zone._versions) > max_versions + + self.set_pruning_policy(policy) + + def set_pruning_policy( + self, policy: Optional[Callable[["Zone", Version], Optional[bool]]] + ) -> None: + """Set the pruning policy for the zone. + + The *policy* function takes a `Version` and returns `True` if + the version should be pruned, and `False` otherwise. `None` + may also be specified for policy, in which case the default policy + is used. + + Pruning checking proceeds from the least version and the first + time the function returns `False`, the checking stops. I.e. the + retained versions are always a consecutive sequence. + """ + if policy is None: + policy = self._default_pruning_policy + with self._version_lock: + self._pruning_policy = policy + self._prune_versions_unlocked() + + def _end_read(self, txn): + with self._version_lock: + self._readers.remove(txn) + self._prune_versions_unlocked() + + def _end_write_unlocked(self, txn): + assert self._write_txn == txn + self._write_txn = None + self._maybe_wakeup_one_waiter_unlocked() + + def _end_write(self, txn): + with self._version_lock: + self._end_write_unlocked(txn) + + def _commit_version_unlocked(self, txn, version, origin): + self._versions.append(version) + self._prune_versions_unlocked() + self.nodes = version.nodes + if self.origin is None: + self.origin = origin + # txn can be None in __init__ when we make the empty version. + if txn is not None: + self._end_write_unlocked(txn) + + def _commit_version(self, txn, version, origin): + with self._version_lock: + self._commit_version_unlocked(txn, version, origin) + + def _get_next_version_id(self): + if len(self._versions) > 0: + id = self._versions[-1].id + 1 + else: + id = 1 + return id + + def find_node( + self, name: Union[dns.name.Name, str], create: bool = False + ) -> dns.node.Node: + if create: + raise UseTransaction + return super().find_node(name) + + def delete_node(self, name: Union[dns.name.Name, str]) -> None: + raise UseTransaction + + def find_rdataset( + self, + name: Union[dns.name.Name, str], + rdtype: Union[dns.rdatatype.RdataType, str], + covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, + create: bool = False, + ) -> dns.rdataset.Rdataset: + if create: + raise UseTransaction + rdataset = super().find_rdataset(name, rdtype, covers) + return dns.rdataset.ImmutableRdataset(rdataset) + + def get_rdataset( + self, + name: Union[dns.name.Name, str], + rdtype: Union[dns.rdatatype.RdataType, str], + covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, + create: bool = False, + ) -> Optional[dns.rdataset.Rdataset]: + if create: + raise UseTransaction + rdataset = super().get_rdataset(name, rdtype, covers) + if rdataset is not None: + return dns.rdataset.ImmutableRdataset(rdataset) + else: + return None + + def delete_rdataset( + self, + name: Union[dns.name.Name, str], + rdtype: Union[dns.rdatatype.RdataType, str], + covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, + ) -> None: + raise UseTransaction + + def replace_rdataset( + self, name: Union[dns.name.Name, str], replacement: dns.rdataset.Rdataset + ) -> None: + raise UseTransaction diff --git a/.venv/lib/python3.9/site-packages/dns/win32util.py b/.venv/lib/python3.9/site-packages/dns/win32util.py new file mode 100644 index 0000000..9ed3f11 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/win32util.py @@ -0,0 +1,242 @@ +import sys + +import dns._features + +if sys.platform == "win32": + from typing import Any + + import dns.name + + _prefer_wmi = True + + import winreg # pylint: disable=import-error + + # Keep pylint quiet on non-windows. + try: + _ = WindowsError # pylint: disable=used-before-assignment + except NameError: + WindowsError = Exception + + if dns._features.have("wmi"): + import threading + + import pythoncom # pylint: disable=import-error + import wmi # pylint: disable=import-error + + _have_wmi = True + else: + _have_wmi = False + + def _config_domain(domain): + # Sometimes DHCP servers add a '.' prefix to the default domain, and + # Windows just stores such values in the registry (see #687). + # Check for this and fix it. + if domain.startswith("."): + domain = domain[1:] + return dns.name.from_text(domain) + + class DnsInfo: + def __init__(self): + self.domain = None + self.nameservers = [] + self.search = [] + + if _have_wmi: + + class _WMIGetter(threading.Thread): + # pylint: disable=possibly-used-before-assignment + def __init__(self): + super().__init__() + self.info = DnsInfo() + + def run(self): + pythoncom.CoInitialize() + try: + system = wmi.WMI() + for interface in system.Win32_NetworkAdapterConfiguration(): + if interface.IPEnabled and interface.DNSServerSearchOrder: + self.info.nameservers = list(interface.DNSServerSearchOrder) + if interface.DNSDomain: + self.info.domain = _config_domain(interface.DNSDomain) + if interface.DNSDomainSuffixSearchOrder: + self.info.search = [ + _config_domain(x) + for x in interface.DNSDomainSuffixSearchOrder + ] + break + finally: + pythoncom.CoUninitialize() + + def get(self): + # We always run in a separate thread to avoid any issues with + # the COM threading model. + self.start() + self.join() + return self.info + + else: + + class _WMIGetter: # type: ignore + pass + + class _RegistryGetter: + def __init__(self): + self.info = DnsInfo() + + def _split(self, text): + # The windows registry has used both " " and "," as a delimiter, and while + # it is currently using "," in Windows 10 and later, updates can seemingly + # leave a space in too, e.g. "a, b". So we just convert all commas to + # spaces, and use split() in its default configuration, which splits on + # all whitespace and ignores empty strings. + return text.replace(",", " ").split() + + def _config_nameservers(self, nameservers): + for ns in self._split(nameservers): + if ns not in self.info.nameservers: + self.info.nameservers.append(ns) + + def _config_search(self, search): + for s in self._split(search): + s = _config_domain(s) + if s not in self.info.search: + self.info.search.append(s) + + def _config_fromkey(self, key, always_try_domain): + try: + servers, _ = winreg.QueryValueEx(key, "NameServer") + except WindowsError: + servers = None + if servers: + self._config_nameservers(servers) + if servers or always_try_domain: + try: + dom, _ = winreg.QueryValueEx(key, "Domain") + if dom: + self.info.domain = _config_domain(dom) + except WindowsError: + pass + else: + try: + servers, _ = winreg.QueryValueEx(key, "DhcpNameServer") + except WindowsError: + servers = None + if servers: + self._config_nameservers(servers) + try: + dom, _ = winreg.QueryValueEx(key, "DhcpDomain") + if dom: + self.info.domain = _config_domain(dom) + except WindowsError: + pass + try: + search, _ = winreg.QueryValueEx(key, "SearchList") + except WindowsError: + search = None + if search is None: + try: + search, _ = winreg.QueryValueEx(key, "DhcpSearchList") + except WindowsError: + search = None + if search: + self._config_search(search) + + def _is_nic_enabled(self, lm, guid): + # Look in the Windows Registry to determine whether the network + # interface corresponding to the given guid is enabled. + # + # (Code contributed by Paul Marks, thanks!) + # + try: + # This hard-coded location seems to be consistent, at least + # from Windows 2000 through Vista. + connection_key = winreg.OpenKey( + lm, + r"SYSTEM\CurrentControlSet\Control\Network" + r"\{4D36E972-E325-11CE-BFC1-08002BE10318}" + rf"\{guid}\Connection", + ) + + try: + # The PnpInstanceID points to a key inside Enum + (pnp_id, ttype) = winreg.QueryValueEx( + connection_key, "PnpInstanceID" + ) + + if ttype != winreg.REG_SZ: + raise ValueError # pragma: no cover + + device_key = winreg.OpenKey( + lm, rf"SYSTEM\CurrentControlSet\Enum\{pnp_id}" + ) + + try: + # Get ConfigFlags for this device + (flags, ttype) = winreg.QueryValueEx(device_key, "ConfigFlags") + + if ttype != winreg.REG_DWORD: + raise ValueError # pragma: no cover + + # Based on experimentation, bit 0x1 indicates that the + # device is disabled. + # + # XXXRTH I suspect we really want to & with 0x03 so + # that CONFIGFLAGS_REMOVED devices are also ignored, + # but we're shifting to WMI as ConfigFlags is not + # supposed to be used. + return not flags & 0x1 + + finally: + device_key.Close() + finally: + connection_key.Close() + except Exception: # pragma: no cover + return False + + def get(self): + """Extract resolver configuration from the Windows registry.""" + + lm = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) + try: + tcp_params = winreg.OpenKey( + lm, r"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" + ) + try: + self._config_fromkey(tcp_params, True) + finally: + tcp_params.Close() + interfaces = winreg.OpenKey( + lm, + r"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces", + ) + try: + i = 0 + while True: + try: + guid = winreg.EnumKey(interfaces, i) + i += 1 + key = winreg.OpenKey(interfaces, guid) + try: + if not self._is_nic_enabled(lm, guid): + continue + self._config_fromkey(key, False) + finally: + key.Close() + except OSError: + break + finally: + interfaces.Close() + finally: + lm.Close() + return self.info + + _getter_class: Any + if _have_wmi and _prefer_wmi: + _getter_class = _WMIGetter + else: + _getter_class = _RegistryGetter + + def get_dns_info(): + """Extract resolver configuration.""" + getter = _getter_class() + return getter.get() diff --git a/.venv/lib/python3.9/site-packages/dns/wire.py b/.venv/lib/python3.9/site-packages/dns/wire.py new file mode 100644 index 0000000..9f9b157 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/wire.py @@ -0,0 +1,89 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import contextlib +import struct +from typing import Iterator, Optional, Tuple + +import dns.exception +import dns.name + + +class Parser: + def __init__(self, wire: bytes, current: int = 0): + self.wire = wire + self.current = 0 + self.end = len(self.wire) + if current: + self.seek(current) + self.furthest = current + + def remaining(self) -> int: + return self.end - self.current + + def get_bytes(self, size: int) -> bytes: + assert size >= 0 + if size > self.remaining(): + raise dns.exception.FormError + output = self.wire[self.current : self.current + size] + self.current += size + self.furthest = max(self.furthest, self.current) + return output + + def get_counted_bytes(self, length_size: int = 1) -> bytes: + length = int.from_bytes(self.get_bytes(length_size), "big") + return self.get_bytes(length) + + def get_remaining(self) -> bytes: + return self.get_bytes(self.remaining()) + + def get_uint8(self) -> int: + return struct.unpack("!B", self.get_bytes(1))[0] + + def get_uint16(self) -> int: + return struct.unpack("!H", self.get_bytes(2))[0] + + def get_uint32(self) -> int: + return struct.unpack("!I", self.get_bytes(4))[0] + + def get_uint48(self) -> int: + return int.from_bytes(self.get_bytes(6), "big") + + def get_struct(self, format: str) -> Tuple: + return struct.unpack(format, self.get_bytes(struct.calcsize(format))) + + def get_name(self, origin: Optional["dns.name.Name"] = None) -> "dns.name.Name": + name = dns.name.from_wire_parser(self) + if origin: + name = name.relativize(origin) + return name + + def seek(self, where: int) -> None: + # Note that seeking to the end is OK! (If you try to read + # after such a seek, you'll get an exception as expected.) + if where < 0 or where > self.end: + raise dns.exception.FormError + self.current = where + + @contextlib.contextmanager + def restrict_to(self, size: int) -> Iterator: + assert size >= 0 + if size > self.remaining(): + raise dns.exception.FormError + saved_end = self.end + try: + self.end = self.current + size + yield + # We make this check here and not in the finally as we + # don't want to raise if we're already raising for some + # other reason. + if self.current != self.end: + raise dns.exception.FormError + finally: + self.end = saved_end + + @contextlib.contextmanager + def restore_furthest(self) -> Iterator: + try: + yield None + finally: + self.current = self.furthest diff --git a/.venv/lib/python3.9/site-packages/dns/xfr.py b/.venv/lib/python3.9/site-packages/dns/xfr.py new file mode 100644 index 0000000..520aa32 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/xfr.py @@ -0,0 +1,343 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2017 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +from typing import Any, List, Optional, Tuple, Union + +import dns.exception +import dns.message +import dns.name +import dns.rcode +import dns.rdataset +import dns.rdatatype +import dns.serial +import dns.transaction +import dns.tsig +import dns.zone + + +class TransferError(dns.exception.DNSException): + """A zone transfer response got a non-zero rcode.""" + + def __init__(self, rcode): + message = f"Zone transfer error: {dns.rcode.to_text(rcode)}" + super().__init__(message) + self.rcode = rcode + + +class SerialWentBackwards(dns.exception.FormError): + """The current serial number is less than the serial we know.""" + + +class UseTCP(dns.exception.DNSException): + """This IXFR cannot be completed with UDP.""" + + +class Inbound: + """ + State machine for zone transfers. + """ + + def __init__( + self, + txn_manager: dns.transaction.TransactionManager, + rdtype: dns.rdatatype.RdataType = dns.rdatatype.AXFR, + serial: Optional[int] = None, + is_udp: bool = False, + ): + """Initialize an inbound zone transfer. + + *txn_manager* is a :py:class:`dns.transaction.TransactionManager`. + + *rdtype* can be `dns.rdatatype.AXFR` or `dns.rdatatype.IXFR` + + *serial* is the base serial number for IXFRs, and is required in + that case. + + *is_udp*, a ``bool`` indidicates if UDP is being used for this + XFR. + """ + self.txn_manager = txn_manager + self.txn: Optional[dns.transaction.Transaction] = None + self.rdtype = rdtype + if rdtype == dns.rdatatype.IXFR: + if serial is None: + raise ValueError("a starting serial must be supplied for IXFRs") + elif is_udp: + raise ValueError("is_udp specified for AXFR") + self.serial = serial + self.is_udp = is_udp + (_, _, self.origin) = txn_manager.origin_information() + self.soa_rdataset: Optional[dns.rdataset.Rdataset] = None + self.done = False + self.expecting_SOA = False + self.delete_mode = False + + def process_message(self, message: dns.message.Message) -> bool: + """Process one message in the transfer. + + The message should have the same relativization as was specified when + the `dns.xfr.Inbound` was created. The message should also have been + created with `one_rr_per_rrset=True` because order matters. + + Returns `True` if the transfer is complete, and `False` otherwise. + """ + if self.txn is None: + replacement = self.rdtype == dns.rdatatype.AXFR + self.txn = self.txn_manager.writer(replacement) + rcode = message.rcode() + if rcode != dns.rcode.NOERROR: + raise TransferError(rcode) + # + # We don't require a question section, but if it is present is + # should be correct. + # + if len(message.question) > 0: + if message.question[0].name != self.origin: + raise dns.exception.FormError("wrong question name") + if message.question[0].rdtype != self.rdtype: + raise dns.exception.FormError("wrong question rdatatype") + answer_index = 0 + if self.soa_rdataset is None: + # + # This is the first message. We're expecting an SOA at + # the origin. + # + if not message.answer or message.answer[0].name != self.origin: + raise dns.exception.FormError("No answer or RRset not for zone origin") + rrset = message.answer[0] + rdataset = rrset + if rdataset.rdtype != dns.rdatatype.SOA: + raise dns.exception.FormError("first RRset is not an SOA") + answer_index = 1 + self.soa_rdataset = rdataset.copy() + if self.rdtype == dns.rdatatype.IXFR: + if self.soa_rdataset[0].serial == self.serial: + # + # We're already up-to-date. + # + self.done = True + elif dns.serial.Serial(self.soa_rdataset[0].serial) < self.serial: + # It went backwards! + raise SerialWentBackwards + else: + if self.is_udp and len(message.answer[answer_index:]) == 0: + # + # There are no more records, so this is the + # "truncated" response. Say to use TCP + # + raise UseTCP + # + # Note we're expecting another SOA so we can detect + # if this IXFR response is an AXFR-style response. + # + self.expecting_SOA = True + # + # Process the answer section (other than the initial SOA in + # the first message). + # + for rrset in message.answer[answer_index:]: + name = rrset.name + rdataset = rrset + if self.done: + raise dns.exception.FormError("answers after final SOA") + assert self.txn is not None # for mypy + if rdataset.rdtype == dns.rdatatype.SOA and name == self.origin: + # + # Every time we see an origin SOA delete_mode inverts + # + if self.rdtype == dns.rdatatype.IXFR: + self.delete_mode = not self.delete_mode + # + # If this SOA Rdataset is equal to the first we saw + # then we're finished. If this is an IXFR we also + # check that we're seeing the record in the expected + # part of the response. + # + if rdataset == self.soa_rdataset and ( + self.rdtype == dns.rdatatype.AXFR + or (self.rdtype == dns.rdatatype.IXFR and self.delete_mode) + ): + # + # This is the final SOA + # + if self.expecting_SOA: + # We got an empty IXFR sequence! + raise dns.exception.FormError("empty IXFR sequence") + if ( + self.rdtype == dns.rdatatype.IXFR + and self.serial != rdataset[0].serial + ): + raise dns.exception.FormError("unexpected end of IXFR sequence") + self.txn.replace(name, rdataset) + self.txn.commit() + self.txn = None + self.done = True + else: + # + # This is not the final SOA + # + self.expecting_SOA = False + if self.rdtype == dns.rdatatype.IXFR: + if self.delete_mode: + # This is the start of an IXFR deletion set + if rdataset[0].serial != self.serial: + raise dns.exception.FormError( + "IXFR base serial mismatch" + ) + else: + # This is the start of an IXFR addition set + self.serial = rdataset[0].serial + self.txn.replace(name, rdataset) + else: + # We saw a non-final SOA for the origin in an AXFR. + raise dns.exception.FormError("unexpected origin SOA in AXFR") + continue + if self.expecting_SOA: + # + # We made an IXFR request and are expecting another + # SOA RR, but saw something else, so this must be an + # AXFR response. + # + self.rdtype = dns.rdatatype.AXFR + self.expecting_SOA = False + self.delete_mode = False + self.txn.rollback() + self.txn = self.txn_manager.writer(True) + # + # Note we are falling through into the code below + # so whatever rdataset this was gets written. + # + # Add or remove the data + if self.delete_mode: + self.txn.delete_exact(name, rdataset) + else: + self.txn.add(name, rdataset) + if self.is_udp and not self.done: + # + # This is a UDP IXFR and we didn't get to done, and we didn't + # get the proper "truncated" response + # + raise dns.exception.FormError("unexpected end of UDP IXFR") + return self.done + + # + # Inbounds are context managers. + # + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + if self.txn: + self.txn.rollback() + return False + + +def make_query( + txn_manager: dns.transaction.TransactionManager, + serial: Optional[int] = 0, + use_edns: Optional[Union[int, bool]] = None, + ednsflags: Optional[int] = None, + payload: Optional[int] = None, + request_payload: Optional[int] = None, + options: Optional[List[dns.edns.Option]] = None, + keyring: Any = None, + keyname: Optional[dns.name.Name] = None, + keyalgorithm: Union[dns.name.Name, str] = dns.tsig.default_algorithm, +) -> Tuple[dns.message.QueryMessage, Optional[int]]: + """Make an AXFR or IXFR query. + + *txn_manager* is a ``dns.transaction.TransactionManager``, typically a + ``dns.zone.Zone``. + + *serial* is an ``int`` or ``None``. If 0, then IXFR will be + attempted using the most recent serial number from the + *txn_manager*; it is the caller's responsibility to ensure there + are no write transactions active that could invalidate the + retrieved serial. If a serial cannot be determined, AXFR will be + forced. Other integer values are the starting serial to use. + ``None`` forces an AXFR. + + Please see the documentation for :py:func:`dns.message.make_query` and + :py:func:`dns.message.Message.use_tsig` for details on the other parameters + to this function. + + Returns a `(query, serial)` tuple. + """ + (zone_origin, _, origin) = txn_manager.origin_information() + if zone_origin is None: + raise ValueError("no zone origin") + if serial is None: + rdtype = dns.rdatatype.AXFR + elif not isinstance(serial, int): + raise ValueError("serial is not an integer") + elif serial == 0: + with txn_manager.reader() as txn: + rdataset = txn.get(origin, "SOA") + if rdataset: + serial = rdataset[0].serial + rdtype = dns.rdatatype.IXFR + else: + serial = None + rdtype = dns.rdatatype.AXFR + elif serial > 0 and serial < 4294967296: + rdtype = dns.rdatatype.IXFR + else: + raise ValueError("serial out-of-range") + rdclass = txn_manager.get_class() + q = dns.message.make_query( + zone_origin, + rdtype, + rdclass, + use_edns, + False, + ednsflags, + payload, + request_payload, + options, + ) + if serial is not None: + rdata = dns.rdata.from_text(rdclass, "SOA", f". . {serial} 0 0 0 0") + rrset = q.find_rrset( + q.authority, zone_origin, rdclass, dns.rdatatype.SOA, create=True + ) + rrset.add(rdata, 0) + if keyring is not None: + q.use_tsig(keyring, keyname, algorithm=keyalgorithm) + return (q, serial) + + +def extract_serial_from_query(query: dns.message.Message) -> Optional[int]: + """Extract the SOA serial number from query if it is an IXFR and return + it, otherwise return None. + + *query* is a dns.message.QueryMessage that is an IXFR or AXFR request. + + Raises if the query is not an IXFR or AXFR, or if an IXFR doesn't have + an appropriate SOA RRset in the authority section. + """ + if not isinstance(query, dns.message.QueryMessage): + raise ValueError("query not a QueryMessage") + question = query.question[0] + if question.rdtype == dns.rdatatype.AXFR: + return None + elif question.rdtype != dns.rdatatype.IXFR: + raise ValueError("query is not an AXFR or IXFR") + soa = query.find_rrset( + query.authority, question.name, question.rdclass, dns.rdatatype.SOA + ) + return soa[0].serial diff --git a/.venv/lib/python3.9/site-packages/dns/zone.py b/.venv/lib/python3.9/site-packages/dns/zone.py new file mode 100644 index 0000000..844919e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/zone.py @@ -0,0 +1,1434 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Zones.""" + +import contextlib +import io +import os +import struct +from typing import ( + Any, + Callable, + Iterable, + Iterator, + List, + MutableMapping, + Optional, + Set, + Tuple, + Union, +) + +import dns.exception +import dns.grange +import dns.immutable +import dns.name +import dns.node +import dns.rdata +import dns.rdataclass +import dns.rdataset +import dns.rdatatype +import dns.rdtypes.ANY.SOA +import dns.rdtypes.ANY.ZONEMD +import dns.rrset +import dns.tokenizer +import dns.transaction +import dns.ttl +import dns.zonefile +from dns.zonetypes import DigestHashAlgorithm, DigestScheme, _digest_hashers + + +class BadZone(dns.exception.DNSException): + """The DNS zone is malformed.""" + + +class NoSOA(BadZone): + """The DNS zone has no SOA RR at its origin.""" + + +class NoNS(BadZone): + """The DNS zone has no NS RRset at its origin.""" + + +class UnknownOrigin(BadZone): + """The DNS zone's origin is unknown.""" + + +class UnsupportedDigestScheme(dns.exception.DNSException): + """The zone digest's scheme is unsupported.""" + + +class UnsupportedDigestHashAlgorithm(dns.exception.DNSException): + """The zone digest's origin is unsupported.""" + + +class NoDigest(dns.exception.DNSException): + """The DNS zone has no ZONEMD RRset at its origin.""" + + +class DigestVerificationFailure(dns.exception.DNSException): + """The ZONEMD digest failed to verify.""" + + +def _validate_name( + name: dns.name.Name, + origin: Optional[dns.name.Name], + relativize: bool, +) -> dns.name.Name: + # This name validation code is shared by Zone and Version + if origin is None: + # This should probably never happen as other code (e.g. + # _rr_line) will notice the lack of an origin before us, but + # we check just in case! + raise KeyError("no zone origin is defined") + if name.is_absolute(): + if not name.is_subdomain(origin): + raise KeyError("name parameter must be a subdomain of the zone origin") + if relativize: + name = name.relativize(origin) + else: + # We have a relative name. Make sure that the derelativized name is + # not too long. + try: + abs_name = name.derelativize(origin) + except dns.name.NameTooLong: + # We map dns.name.NameTooLong to KeyError to be consistent with + # the other exceptions above. + raise KeyError("relative name too long for zone") + if not relativize: + # We have a relative name in a non-relative zone, so use the + # derelativized name. + name = abs_name + return name + + +class Zone(dns.transaction.TransactionManager): + """A DNS zone. + + A ``Zone`` is a mapping from names to nodes. The zone object may be + treated like a Python dictionary, e.g. ``zone[name]`` will retrieve + the node associated with that name. The *name* may be a + ``dns.name.Name object``, or it may be a string. In either case, + if the name is relative it is treated as relative to the origin of + the zone. + """ + + node_factory: Callable[[], dns.node.Node] = dns.node.Node + map_factory: Callable[[], MutableMapping[dns.name.Name, dns.node.Node]] = dict + writable_version_factory: Optional[Callable[[], "WritableVersion"]] = None + immutable_version_factory: Optional[Callable[[], "ImmutableVersion"]] = None + + __slots__ = ["rdclass", "origin", "nodes", "relativize"] + + def __init__( + self, + origin: Optional[Union[dns.name.Name, str]], + rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, + relativize: bool = True, + ): + """Initialize a zone object. + + *origin* is the origin of the zone. It may be a ``dns.name.Name``, + a ``str``, or ``None``. If ``None``, then the zone's origin will + be set by the first ``$ORIGIN`` line in a zone file. + + *rdclass*, an ``int``, the zone's rdata class; the default is class IN. + + *relativize*, a ``bool``, determine's whether domain names are + relativized to the zone's origin. The default is ``True``. + """ + + if origin is not None: + if isinstance(origin, str): + origin = dns.name.from_text(origin) + elif not isinstance(origin, dns.name.Name): + raise ValueError("origin parameter must be convertible to a DNS name") + if not origin.is_absolute(): + raise ValueError("origin parameter must be an absolute name") + self.origin = origin + self.rdclass = rdclass + self.nodes: MutableMapping[dns.name.Name, dns.node.Node] = self.map_factory() + self.relativize = relativize + + def __eq__(self, other): + """Two zones are equal if they have the same origin, class, and + nodes. + + Returns a ``bool``. + """ + + if not isinstance(other, Zone): + return False + if ( + self.rdclass != other.rdclass + or self.origin != other.origin + or self.nodes != other.nodes + ): + return False + return True + + def __ne__(self, other): + """Are two zones not equal? + + Returns a ``bool``. + """ + + return not self.__eq__(other) + + def _validate_name(self, name: Union[dns.name.Name, str]) -> dns.name.Name: + # Note that any changes in this method should have corresponding changes + # made in the Version _validate_name() method. + if isinstance(name, str): + name = dns.name.from_text(name, None) + elif not isinstance(name, dns.name.Name): + raise KeyError("name parameter must be convertible to a DNS name") + return _validate_name(name, self.origin, self.relativize) + + def __getitem__(self, key): + key = self._validate_name(key) + return self.nodes[key] + + def __setitem__(self, key, value): + key = self._validate_name(key) + self.nodes[key] = value + + def __delitem__(self, key): + key = self._validate_name(key) + del self.nodes[key] + + def __iter__(self): + return self.nodes.__iter__() + + def keys(self): + return self.nodes.keys() + + def values(self): + return self.nodes.values() + + def items(self): + return self.nodes.items() + + def get(self, key): + key = self._validate_name(key) + return self.nodes.get(key) + + def __contains__(self, key): + key = self._validate_name(key) + return key in self.nodes + + def find_node( + self, name: Union[dns.name.Name, str], create: bool = False + ) -> dns.node.Node: + """Find a node in the zone, possibly creating it. + + *name*: the name of the node to find. + The value may be a ``dns.name.Name`` or a ``str``. If absolute, the + name must be a subdomain of the zone's origin. If ``zone.relativize`` + is ``True``, then the name will be relativized. + + *create*, a ``bool``. If true, the node will be created if it does + not exist. + + Raises ``KeyError`` if the name is not known and create was + not specified, or if the name was not a subdomain of the origin. + + Returns a ``dns.node.Node``. + """ + + name = self._validate_name(name) + node = self.nodes.get(name) + if node is None: + if not create: + raise KeyError + node = self.node_factory() + self.nodes[name] = node + return node + + def get_node( + self, name: Union[dns.name.Name, str], create: bool = False + ) -> Optional[dns.node.Node]: + """Get a node in the zone, possibly creating it. + + This method is like ``find_node()``, except it returns None instead + of raising an exception if the node does not exist and creation + has not been requested. + + *name*: the name of the node to find. + The value may be a ``dns.name.Name`` or a ``str``. If absolute, the + name must be a subdomain of the zone's origin. If ``zone.relativize`` + is ``True``, then the name will be relativized. + + *create*, a ``bool``. If true, the node will be created if it does + not exist. + + Returns a ``dns.node.Node`` or ``None``. + """ + + try: + node = self.find_node(name, create) + except KeyError: + node = None + return node + + def delete_node(self, name: Union[dns.name.Name, str]) -> None: + """Delete the specified node if it exists. + + *name*: the name of the node to find. + The value may be a ``dns.name.Name`` or a ``str``. If absolute, the + name must be a subdomain of the zone's origin. If ``zone.relativize`` + is ``True``, then the name will be relativized. + + It is not an error if the node does not exist. + """ + + name = self._validate_name(name) + if name in self.nodes: + del self.nodes[name] + + def find_rdataset( + self, + name: Union[dns.name.Name, str], + rdtype: Union[dns.rdatatype.RdataType, str], + covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, + create: bool = False, + ) -> dns.rdataset.Rdataset: + """Look for an rdataset with the specified name and type in the zone, + and return an rdataset encapsulating it. + + The rdataset returned is not a copy; changes to it will change + the zone. + + KeyError is raised if the name or type are not found. + + *name*: the name of the node to find. + The value may be a ``dns.name.Name`` or a ``str``. If absolute, the + name must be a subdomain of the zone's origin. If ``zone.relativize`` + is ``True``, then the name will be relativized. + + *rdtype*, a ``dns.rdatatype.RdataType`` or ``str``, the rdata type desired. + + *covers*, a ``dns.rdatatype.RdataType`` or ``str`` the covered type. + Usually this value is ``dns.rdatatype.NONE``, but if the + rdtype is ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, + then the covers value will be the rdata type the SIG/RRSIG + covers. The library treats the SIG and RRSIG types as if they + were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). + This makes RRSIGs much easier to work with than if RRSIGs + covering different rdata types were aggregated into a single + RRSIG rdataset. + + *create*, a ``bool``. If true, the node will be created if it does + not exist. + + Raises ``KeyError`` if the name is not known and create was + not specified, or if the name was not a subdomain of the origin. + + Returns a ``dns.rdataset.Rdataset``. + """ + + name = self._validate_name(name) + rdtype = dns.rdatatype.RdataType.make(rdtype) + covers = dns.rdatatype.RdataType.make(covers) + node = self.find_node(name, create) + return node.find_rdataset(self.rdclass, rdtype, covers, create) + + def get_rdataset( + self, + name: Union[dns.name.Name, str], + rdtype: Union[dns.rdatatype.RdataType, str], + covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, + create: bool = False, + ) -> Optional[dns.rdataset.Rdataset]: + """Look for an rdataset with the specified name and type in the zone. + + This method is like ``find_rdataset()``, except it returns None instead + of raising an exception if the rdataset does not exist and creation + has not been requested. + + The rdataset returned is not a copy; changes to it will change + the zone. + + *name*: the name of the node to find. + The value may be a ``dns.name.Name`` or a ``str``. If absolute, the + name must be a subdomain of the zone's origin. If ``zone.relativize`` + is ``True``, then the name will be relativized. + + *rdtype*, a ``dns.rdatatype.RdataType`` or ``str``, the rdata type desired. + + *covers*, a ``dns.rdatatype.RdataType`` or ``str``, the covered type. + Usually this value is ``dns.rdatatype.NONE``, but if the + rdtype is ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, + then the covers value will be the rdata type the SIG/RRSIG + covers. The library treats the SIG and RRSIG types as if they + were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). + This makes RRSIGs much easier to work with than if RRSIGs + covering different rdata types were aggregated into a single + RRSIG rdataset. + + *create*, a ``bool``. If true, the node will be created if it does + not exist. + + Raises ``KeyError`` if the name is not known and create was + not specified, or if the name was not a subdomain of the origin. + + Returns a ``dns.rdataset.Rdataset`` or ``None``. + """ + + try: + rdataset = self.find_rdataset(name, rdtype, covers, create) + except KeyError: + rdataset = None + return rdataset + + def delete_rdataset( + self, + name: Union[dns.name.Name, str], + rdtype: Union[dns.rdatatype.RdataType, str], + covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, + ) -> None: + """Delete the rdataset matching *rdtype* and *covers*, if it + exists at the node specified by *name*. + + It is not an error if the node does not exist, or if there is no matching + rdataset at the node. + + If the node has no rdatasets after the deletion, it will itself be deleted. + + *name*: the name of the node to find. The value may be a ``dns.name.Name`` or a + ``str``. If absolute, the name must be a subdomain of the zone's origin. If + ``zone.relativize`` is ``True``, then the name will be relativized. + + *rdtype*, a ``dns.rdatatype.RdataType`` or ``str``, the rdata type desired. + + *covers*, a ``dns.rdatatype.RdataType`` or ``str`` or ``None``, the covered + type. Usually this value is ``dns.rdatatype.NONE``, but if the rdtype is + ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, then the covers value will be + the rdata type the SIG/RRSIG covers. The library treats the SIG and RRSIG types + as if they were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This + makes RRSIGs much easier to work with than if RRSIGs covering different rdata + types were aggregated into a single RRSIG rdataset. + """ + + name = self._validate_name(name) + rdtype = dns.rdatatype.RdataType.make(rdtype) + covers = dns.rdatatype.RdataType.make(covers) + node = self.get_node(name) + if node is not None: + node.delete_rdataset(self.rdclass, rdtype, covers) + if len(node) == 0: + self.delete_node(name) + + def replace_rdataset( + self, name: Union[dns.name.Name, str], replacement: dns.rdataset.Rdataset + ) -> None: + """Replace an rdataset at name. + + It is not an error if there is no rdataset matching I{replacement}. + + Ownership of the *replacement* object is transferred to the zone; + in other words, this method does not store a copy of *replacement* + at the node, it stores *replacement* itself. + + If the node does not exist, it is created. + + *name*: the name of the node to find. + The value may be a ``dns.name.Name`` or a ``str``. If absolute, the + name must be a subdomain of the zone's origin. If ``zone.relativize`` + is ``True``, then the name will be relativized. + + *replacement*, a ``dns.rdataset.Rdataset``, the replacement rdataset. + """ + + if replacement.rdclass != self.rdclass: + raise ValueError("replacement.rdclass != zone.rdclass") + node = self.find_node(name, True) + node.replace_rdataset(replacement) + + def find_rrset( + self, + name: Union[dns.name.Name, str], + rdtype: Union[dns.rdatatype.RdataType, str], + covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, + ) -> dns.rrset.RRset: + """Look for an rdataset with the specified name and type in the zone, + and return an RRset encapsulating it. + + This method is less efficient than the similar + ``find_rdataset()`` because it creates an RRset instead of + returning the matching rdataset. It may be more convenient + for some uses since it returns an object which binds the owner + name to the rdataset. + + This method may not be used to create new nodes or rdatasets; + use ``find_rdataset`` instead. + + *name*: the name of the node to find. + The value may be a ``dns.name.Name`` or a ``str``. If absolute, the + name must be a subdomain of the zone's origin. If ``zone.relativize`` + is ``True``, then the name will be relativized. + + *rdtype*, a ``dns.rdatatype.RdataType`` or ``str``, the rdata type desired. + + *covers*, a ``dns.rdatatype.RdataType`` or ``str``, the covered type. + Usually this value is ``dns.rdatatype.NONE``, but if the + rdtype is ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, + then the covers value will be the rdata type the SIG/RRSIG + covers. The library treats the SIG and RRSIG types as if they + were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). + This makes RRSIGs much easier to work with than if RRSIGs + covering different rdata types were aggregated into a single + RRSIG rdataset. + + *create*, a ``bool``. If true, the node will be created if it does + not exist. + + Raises ``KeyError`` if the name is not known and create was + not specified, or if the name was not a subdomain of the origin. + + Returns a ``dns.rrset.RRset`` or ``None``. + """ + + vname = self._validate_name(name) + rdtype = dns.rdatatype.RdataType.make(rdtype) + covers = dns.rdatatype.RdataType.make(covers) + rdataset = self.nodes[vname].find_rdataset(self.rdclass, rdtype, covers) + rrset = dns.rrset.RRset(vname, self.rdclass, rdtype, covers) + rrset.update(rdataset) + return rrset + + def get_rrset( + self, + name: Union[dns.name.Name, str], + rdtype: Union[dns.rdatatype.RdataType, str], + covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, + ) -> Optional[dns.rrset.RRset]: + """Look for an rdataset with the specified name and type in the zone, + and return an RRset encapsulating it. + + This method is less efficient than the similar ``get_rdataset()`` + because it creates an RRset instead of returning the matching + rdataset. It may be more convenient for some uses since it + returns an object which binds the owner name to the rdataset. + + This method may not be used to create new nodes or rdatasets; + use ``get_rdataset()`` instead. + + *name*: the name of the node to find. + The value may be a ``dns.name.Name`` or a ``str``. If absolute, the + name must be a subdomain of the zone's origin. If ``zone.relativize`` + is ``True``, then the name will be relativized. + + *rdtype*, a ``dns.rdataset.Rdataset`` or ``str``, the rdata type desired. + + *covers*, a ``dns.rdataset.Rdataset`` or ``str``, the covered type. + Usually this value is ``dns.rdatatype.NONE``, but if the + rdtype is ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, + then the covers value will be the rdata type the SIG/RRSIG + covers. The library treats the SIG and RRSIG types as if they + were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). + This makes RRSIGs much easier to work with than if RRSIGs + covering different rdata types were aggregated into a single + RRSIG rdataset. + + *create*, a ``bool``. If true, the node will be created if it does + not exist. + + Returns a ``dns.rrset.RRset`` or ``None``. + """ + + try: + rrset = self.find_rrset(name, rdtype, covers) + except KeyError: + rrset = None + return rrset + + def iterate_rdatasets( + self, + rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.ANY, + covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, + ) -> Iterator[Tuple[dns.name.Name, dns.rdataset.Rdataset]]: + """Return a generator which yields (name, rdataset) tuples for + all rdatasets in the zone which have the specified *rdtype* + and *covers*. If *rdtype* is ``dns.rdatatype.ANY``, the default, + then all rdatasets will be matched. + + *rdtype*, a ``dns.rdataset.Rdataset`` or ``str``, the rdata type desired. + + *covers*, a ``dns.rdataset.Rdataset`` or ``str``, the covered type. + Usually this value is ``dns.rdatatype.NONE``, but if the + rdtype is ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, + then the covers value will be the rdata type the SIG/RRSIG + covers. The library treats the SIG and RRSIG types as if they + were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). + This makes RRSIGs much easier to work with than if RRSIGs + covering different rdata types were aggregated into a single + RRSIG rdataset. + """ + + rdtype = dns.rdatatype.RdataType.make(rdtype) + covers = dns.rdatatype.RdataType.make(covers) + for name, node in self.items(): + for rds in node: + if rdtype == dns.rdatatype.ANY or ( + rds.rdtype == rdtype and rds.covers == covers + ): + yield (name, rds) + + def iterate_rdatas( + self, + rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.ANY, + covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, + ) -> Iterator[Tuple[dns.name.Name, int, dns.rdata.Rdata]]: + """Return a generator which yields (name, ttl, rdata) tuples for + all rdatas in the zone which have the specified *rdtype* + and *covers*. If *rdtype* is ``dns.rdatatype.ANY``, the default, + then all rdatas will be matched. + + *rdtype*, a ``dns.rdataset.Rdataset`` or ``str``, the rdata type desired. + + *covers*, a ``dns.rdataset.Rdataset`` or ``str``, the covered type. + Usually this value is ``dns.rdatatype.NONE``, but if the + rdtype is ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, + then the covers value will be the rdata type the SIG/RRSIG + covers. The library treats the SIG and RRSIG types as if they + were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). + This makes RRSIGs much easier to work with than if RRSIGs + covering different rdata types were aggregated into a single + RRSIG rdataset. + """ + + rdtype = dns.rdatatype.RdataType.make(rdtype) + covers = dns.rdatatype.RdataType.make(covers) + for name, node in self.items(): + for rds in node: + if rdtype == dns.rdatatype.ANY or ( + rds.rdtype == rdtype and rds.covers == covers + ): + for rdata in rds: + yield (name, rds.ttl, rdata) + + def to_file( + self, + f: Any, + sorted: bool = True, + relativize: bool = True, + nl: Optional[str] = None, + want_comments: bool = False, + want_origin: bool = False, + ) -> None: + """Write a zone to a file. + + *f*, a file or `str`. If *f* is a string, it is treated + as the name of a file to open. + + *sorted*, a ``bool``. If True, the default, then the file + will be written with the names sorted in DNSSEC order from + least to greatest. Otherwise the names will be written in + whatever order they happen to have in the zone's dictionary. + + *relativize*, a ``bool``. If True, the default, then domain + names in the output will be relativized to the zone's origin + if possible. + + *nl*, a ``str`` or None. The end of line string. If not + ``None``, the output will use the platform's native + end-of-line marker (i.e. LF on POSIX, CRLF on Windows). + + *want_comments*, a ``bool``. If ``True``, emit end-of-line comments + as part of writing the file. If ``False``, the default, do not + emit them. + + *want_origin*, a ``bool``. If ``True``, emit a $ORIGIN line at + the start of the file. If ``False``, the default, do not emit + one. + """ + + if isinstance(f, str): + cm: contextlib.AbstractContextManager = open(f, "wb") + else: + cm = contextlib.nullcontext(f) + with cm as f: + # must be in this way, f.encoding may contain None, or even + # attribute may not be there + file_enc = getattr(f, "encoding", None) + if file_enc is None: + file_enc = "utf-8" + + if nl is None: + # binary mode, '\n' is not enough + nl_b = os.linesep.encode(file_enc) + nl = "\n" + elif isinstance(nl, str): + nl_b = nl.encode(file_enc) + else: + nl_b = nl + nl = nl.decode() + + if want_origin: + assert self.origin is not None + l = "$ORIGIN " + self.origin.to_text() + l_b = l.encode(file_enc) + try: + f.write(l_b) + f.write(nl_b) + except TypeError: # textual mode + f.write(l) + f.write(nl) + + if sorted: + names = list(self.keys()) + names.sort() + else: + names = self.keys() + for n in names: + l = self[n].to_text( + n, + origin=self.origin, + relativize=relativize, + want_comments=want_comments, + ) + l_b = l.encode(file_enc) + + try: + f.write(l_b) + f.write(nl_b) + except TypeError: # textual mode + f.write(l) + f.write(nl) + + def to_text( + self, + sorted: bool = True, + relativize: bool = True, + nl: Optional[str] = None, + want_comments: bool = False, + want_origin: bool = False, + ) -> str: + """Return a zone's text as though it were written to a file. + + *sorted*, a ``bool``. If True, the default, then the file + will be written with the names sorted in DNSSEC order from + least to greatest. Otherwise the names will be written in + whatever order they happen to have in the zone's dictionary. + + *relativize*, a ``bool``. If True, the default, then domain + names in the output will be relativized to the zone's origin + if possible. + + *nl*, a ``str`` or None. The end of line string. If not + ``None``, the output will use the platform's native + end-of-line marker (i.e. LF on POSIX, CRLF on Windows). + + *want_comments*, a ``bool``. If ``True``, emit end-of-line comments + as part of writing the file. If ``False``, the default, do not + emit them. + + *want_origin*, a ``bool``. If ``True``, emit a $ORIGIN line at + the start of the output. If ``False``, the default, do not emit + one. + + Returns a ``str``. + """ + temp_buffer = io.StringIO() + self.to_file(temp_buffer, sorted, relativize, nl, want_comments, want_origin) + return_value = temp_buffer.getvalue() + temp_buffer.close() + return return_value + + def check_origin(self) -> None: + """Do some simple checking of the zone's origin. + + Raises ``dns.zone.NoSOA`` if there is no SOA RRset. + + Raises ``dns.zone.NoNS`` if there is no NS RRset. + + Raises ``KeyError`` if there is no origin node. + """ + if self.relativize: + name = dns.name.empty + else: + assert self.origin is not None + name = self.origin + if self.get_rdataset(name, dns.rdatatype.SOA) is None: + raise NoSOA + if self.get_rdataset(name, dns.rdatatype.NS) is None: + raise NoNS + + def get_soa( + self, txn: Optional[dns.transaction.Transaction] = None + ) -> dns.rdtypes.ANY.SOA.SOA: + """Get the zone SOA rdata. + + Raises ``dns.zone.NoSOA`` if there is no SOA RRset. + + Returns a ``dns.rdtypes.ANY.SOA.SOA`` Rdata. + """ + if self.relativize: + origin_name = dns.name.empty + else: + if self.origin is None: + # get_soa() has been called very early, and there must not be + # an SOA if there is no origin. + raise NoSOA + origin_name = self.origin + soa: Optional[dns.rdataset.Rdataset] + if txn: + soa = txn.get(origin_name, dns.rdatatype.SOA) + else: + soa = self.get_rdataset(origin_name, dns.rdatatype.SOA) + if soa is None: + raise NoSOA + return soa[0] + + def _compute_digest( + self, + hash_algorithm: DigestHashAlgorithm, + scheme: DigestScheme = DigestScheme.SIMPLE, + ) -> bytes: + hashinfo = _digest_hashers.get(hash_algorithm) + if not hashinfo: + raise UnsupportedDigestHashAlgorithm + if scheme != DigestScheme.SIMPLE: + raise UnsupportedDigestScheme + + if self.relativize: + origin_name = dns.name.empty + else: + assert self.origin is not None + origin_name = self.origin + hasher = hashinfo() + for name, node in sorted(self.items()): + rrnamebuf = name.to_digestable(self.origin) + for rdataset in sorted(node, key=lambda rds: (rds.rdtype, rds.covers)): + if name == origin_name and dns.rdatatype.ZONEMD in ( + rdataset.rdtype, + rdataset.covers, + ): + continue + rrfixed = struct.pack( + "!HHI", rdataset.rdtype, rdataset.rdclass, rdataset.ttl + ) + rdatas = [rdata.to_digestable(self.origin) for rdata in rdataset] + for rdata in sorted(rdatas): + rrlen = struct.pack("!H", len(rdata)) + hasher.update(rrnamebuf + rrfixed + rrlen + rdata) + return hasher.digest() + + def compute_digest( + self, + hash_algorithm: DigestHashAlgorithm, + scheme: DigestScheme = DigestScheme.SIMPLE, + ) -> dns.rdtypes.ANY.ZONEMD.ZONEMD: + serial = self.get_soa().serial + digest = self._compute_digest(hash_algorithm, scheme) + return dns.rdtypes.ANY.ZONEMD.ZONEMD( + self.rdclass, dns.rdatatype.ZONEMD, serial, scheme, hash_algorithm, digest + ) + + def verify_digest( + self, zonemd: Optional[dns.rdtypes.ANY.ZONEMD.ZONEMD] = None + ) -> None: + digests: Union[dns.rdataset.Rdataset, List[dns.rdtypes.ANY.ZONEMD.ZONEMD]] + if zonemd: + digests = [zonemd] + else: + assert self.origin is not None + rds = self.get_rdataset(self.origin, dns.rdatatype.ZONEMD) + if rds is None: + raise NoDigest + digests = rds + for digest in digests: + try: + computed = self._compute_digest(digest.hash_algorithm, digest.scheme) + if computed == digest.digest: + return + except Exception: + pass + raise DigestVerificationFailure + + # TransactionManager methods + + def reader(self) -> "Transaction": + return Transaction(self, False, Version(self, 1, self.nodes, self.origin)) + + def writer(self, replacement: bool = False) -> "Transaction": + txn = Transaction(self, replacement) + txn._setup_version() + return txn + + def origin_information( + self, + ) -> Tuple[Optional[dns.name.Name], bool, Optional[dns.name.Name]]: + effective: Optional[dns.name.Name] + if self.relativize: + effective = dns.name.empty + else: + effective = self.origin + return (self.origin, self.relativize, effective) + + def get_class(self): + return self.rdclass + + # Transaction methods + + def _end_read(self, txn): + pass + + def _end_write(self, txn): + pass + + def _commit_version(self, _, version, origin): + self.nodes = version.nodes + if self.origin is None: + self.origin = origin + + def _get_next_version_id(self): + # Versions are ephemeral and all have id 1 + return 1 + + +# These classes used to be in dns.versioned, but have moved here so we can use +# the copy-on-write transaction mechanism for both kinds of zones. In a +# regular zone, the version only exists during the transaction, and the nodes +# are regular dns.node.Nodes. + +# A node with a version id. + + +class VersionedNode(dns.node.Node): # lgtm[py/missing-equals] + __slots__ = ["id"] + + def __init__(self): + super().__init__() + # A proper id will get set by the Version + self.id = 0 + + +@dns.immutable.immutable +class ImmutableVersionedNode(VersionedNode): + def __init__(self, node): + super().__init__() + self.id = node.id + self.rdatasets = tuple( + [dns.rdataset.ImmutableRdataset(rds) for rds in node.rdatasets] + ) + + def find_rdataset( + self, + rdclass: dns.rdataclass.RdataClass, + rdtype: dns.rdatatype.RdataType, + covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, + create: bool = False, + ) -> dns.rdataset.Rdataset: + if create: + raise TypeError("immutable") + return super().find_rdataset(rdclass, rdtype, covers, False) + + def get_rdataset( + self, + rdclass: dns.rdataclass.RdataClass, + rdtype: dns.rdatatype.RdataType, + covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, + create: bool = False, + ) -> Optional[dns.rdataset.Rdataset]: + if create: + raise TypeError("immutable") + return super().get_rdataset(rdclass, rdtype, covers, False) + + def delete_rdataset( + self, + rdclass: dns.rdataclass.RdataClass, + rdtype: dns.rdatatype.RdataType, + covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, + ) -> None: + raise TypeError("immutable") + + def replace_rdataset(self, replacement: dns.rdataset.Rdataset) -> None: + raise TypeError("immutable") + + def is_immutable(self) -> bool: + return True + + +class Version: + def __init__( + self, + zone: Zone, + id: int, + nodes: Optional[MutableMapping[dns.name.Name, dns.node.Node]] = None, + origin: Optional[dns.name.Name] = None, + ): + self.zone = zone + self.id = id + if nodes is not None: + self.nodes = nodes + else: + self.nodes = zone.map_factory() + self.origin = origin + + def _validate_name(self, name: dns.name.Name) -> dns.name.Name: + return _validate_name(name, self.origin, self.zone.relativize) + + def get_node(self, name: dns.name.Name) -> Optional[dns.node.Node]: + name = self._validate_name(name) + return self.nodes.get(name) + + def get_rdataset( + self, + name: dns.name.Name, + rdtype: dns.rdatatype.RdataType, + covers: dns.rdatatype.RdataType, + ) -> Optional[dns.rdataset.Rdataset]: + node = self.get_node(name) + if node is None: + return None + return node.get_rdataset(self.zone.rdclass, rdtype, covers) + + def keys(self): + return self.nodes.keys() + + def items(self): + return self.nodes.items() + + +class WritableVersion(Version): + def __init__(self, zone: Zone, replacement: bool = False): + # The zone._versions_lock must be held by our caller in a versioned + # zone. + id = zone._get_next_version_id() + super().__init__(zone, id) + if not replacement: + # We copy the map, because that gives us a simple and thread-safe + # way of doing versions, and we have a garbage collector to help + # us. We only make new node objects if we actually change the + # node. + self.nodes.update(zone.nodes) + # We have to copy the zone origin as it may be None in the first + # version, and we don't want to mutate the zone until we commit. + self.origin = zone.origin + self.changed: Set[dns.name.Name] = set() + + def _maybe_cow(self, name: dns.name.Name) -> dns.node.Node: + name = self._validate_name(name) + node = self.nodes.get(name) + if node is None or name not in self.changed: + new_node = self.zone.node_factory() + if hasattr(new_node, "id"): + # We keep doing this for backwards compatibility, as earlier + # code used new_node.id != self.id for the "do we need to CoW?" + # test. Now we use the changed set as this works with both + # regular zones and versioned zones. + # + # We ignore the mypy error as this is safe but it doesn't see it. + new_node.id = self.id # type: ignore + if node is not None: + # moo! copy on write! + new_node.rdatasets.extend(node.rdatasets) + self.nodes[name] = new_node + self.changed.add(name) + return new_node + else: + return node + + def delete_node(self, name: dns.name.Name) -> None: + name = self._validate_name(name) + if name in self.nodes: + del self.nodes[name] + self.changed.add(name) + + def put_rdataset( + self, name: dns.name.Name, rdataset: dns.rdataset.Rdataset + ) -> None: + node = self._maybe_cow(name) + node.replace_rdataset(rdataset) + + def delete_rdataset( + self, + name: dns.name.Name, + rdtype: dns.rdatatype.RdataType, + covers: dns.rdatatype.RdataType, + ) -> None: + node = self._maybe_cow(name) + node.delete_rdataset(self.zone.rdclass, rdtype, covers) + if len(node) == 0: + del self.nodes[name] + + +@dns.immutable.immutable +class ImmutableVersion(Version): + def __init__(self, version: WritableVersion): + # We tell super() that it's a replacement as we don't want it + # to copy the nodes, as we're about to do that with an + # immutable Dict. + super().__init__(version.zone, True) + # set the right id! + self.id = version.id + # keep the origin + self.origin = version.origin + # Make changed nodes immutable + for name in version.changed: + node = version.nodes.get(name) + # it might not exist if we deleted it in the version + if node: + version.nodes[name] = ImmutableVersionedNode(node) + # We're changing the type of the nodes dictionary here on purpose, so + # we ignore the mypy error. + self.nodes = dns.immutable.Dict( + version.nodes, True, self.zone.map_factory + ) # type: ignore + + +class Transaction(dns.transaction.Transaction): + def __init__(self, zone, replacement, version=None, make_immutable=False): + read_only = version is not None + super().__init__(zone, replacement, read_only) + self.version = version + self.make_immutable = make_immutable + + @property + def zone(self): + return self.manager + + def _setup_version(self): + assert self.version is None + factory = self.manager.writable_version_factory + if factory is None: + factory = WritableVersion + self.version = factory(self.zone, self.replacement) + + def _get_rdataset(self, name, rdtype, covers): + return self.version.get_rdataset(name, rdtype, covers) + + def _put_rdataset(self, name, rdataset): + assert not self.read_only + self.version.put_rdataset(name, rdataset) + + def _delete_name(self, name): + assert not self.read_only + self.version.delete_node(name) + + def _delete_rdataset(self, name, rdtype, covers): + assert not self.read_only + self.version.delete_rdataset(name, rdtype, covers) + + def _name_exists(self, name): + return self.version.get_node(name) is not None + + def _changed(self): + if self.read_only: + return False + else: + return len(self.version.changed) > 0 + + def _end_transaction(self, commit): + if self.read_only: + self.zone._end_read(self) + elif commit and len(self.version.changed) > 0: + if self.make_immutable: + factory = self.manager.immutable_version_factory + if factory is None: + factory = ImmutableVersion + version = factory(self.version) + else: + version = self.version + self.zone._commit_version(self, version, self.version.origin) + else: + # rollback + self.zone._end_write(self) + + def _set_origin(self, origin): + if self.version.origin is None: + self.version.origin = origin + + def _iterate_rdatasets(self): + for name, node in self.version.items(): + for rdataset in node: + yield (name, rdataset) + + def _iterate_names(self): + return self.version.keys() + + def _get_node(self, name): + return self.version.get_node(name) + + def _origin_information(self): + (absolute, relativize, effective) = self.manager.origin_information() + if absolute is None and self.version.origin is not None: + # No origin has been committed yet, but we've learned one as part of + # this txn. Use it. + absolute = self.version.origin + if relativize: + effective = dns.name.empty + else: + effective = absolute + return (absolute, relativize, effective) + + +def _from_text( + text: Any, + origin: Optional[Union[dns.name.Name, str]] = None, + rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, + relativize: bool = True, + zone_factory: Any = Zone, + filename: Optional[str] = None, + allow_include: bool = False, + check_origin: bool = True, + idna_codec: Optional[dns.name.IDNACodec] = None, + allow_directives: Union[bool, Iterable[str]] = True, +) -> Zone: + # See the comments for the public APIs from_text() and from_file() for + # details. + + # 'text' can also be a file, but we don't publish that fact + # since it's an implementation detail. The official file + # interface is from_file(). + + if filename is None: + filename = "" + zone = zone_factory(origin, rdclass, relativize=relativize) + with zone.writer(True) as txn: + tok = dns.tokenizer.Tokenizer(text, filename, idna_codec=idna_codec) + reader = dns.zonefile.Reader( + tok, + rdclass, + txn, + allow_include=allow_include, + allow_directives=allow_directives, + ) + try: + reader.read() + except dns.zonefile.UnknownOrigin: + # for backwards compatibility + raise dns.zone.UnknownOrigin + # Now that we're done reading, do some basic checking of the zone. + if check_origin: + zone.check_origin() + return zone + + +def from_text( + text: str, + origin: Optional[Union[dns.name.Name, str]] = None, + rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, + relativize: bool = True, + zone_factory: Any = Zone, + filename: Optional[str] = None, + allow_include: bool = False, + check_origin: bool = True, + idna_codec: Optional[dns.name.IDNACodec] = None, + allow_directives: Union[bool, Iterable[str]] = True, +) -> Zone: + """Build a zone object from a zone file format string. + + *text*, a ``str``, the zone file format input. + + *origin*, a ``dns.name.Name``, a ``str``, or ``None``. The origin + of the zone; if not specified, the first ``$ORIGIN`` statement in the + zone file will determine the origin of the zone. + + *rdclass*, a ``dns.rdataclass.RdataClass``, the zone's rdata class; the default is + class IN. + + *relativize*, a ``bool``, determine's whether domain names are + relativized to the zone's origin. The default is ``True``. + + *zone_factory*, the zone factory to use or ``None``. If ``None``, then + ``dns.zone.Zone`` will be used. The value may be any class or callable + that returns a subclass of ``dns.zone.Zone``. + + *filename*, a ``str`` or ``None``, the filename to emit when + describing where an error occurred; the default is ``''``. + + *allow_include*, a ``bool``. If ``True``, the default, then ``$INCLUDE`` + directives are permitted. If ``False``, then encoutering a ``$INCLUDE`` + will raise a ``SyntaxError`` exception. + + *check_origin*, a ``bool``. If ``True``, the default, then sanity + checks of the origin node will be made by calling the zone's + ``check_origin()`` method. + + *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA + encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder + is used. + + *allow_directives*, a ``bool`` or an iterable of `str`. If ``True``, the default, + then directives are permitted, and the *allow_include* parameter controls whether + ``$INCLUDE`` is permitted. If ``False`` or an empty iterable, then no directive + processing is done and any directive-like text will be treated as a regular owner + name. If a non-empty iterable, then only the listed directives (including the + ``$``) are allowed. + + Raises ``dns.zone.NoSOA`` if there is no SOA RRset. + + Raises ``dns.zone.NoNS`` if there is no NS RRset. + + Raises ``KeyError`` if there is no origin node. + + Returns a subclass of ``dns.zone.Zone``. + """ + return _from_text( + text, + origin, + rdclass, + relativize, + zone_factory, + filename, + allow_include, + check_origin, + idna_codec, + allow_directives, + ) + + +def from_file( + f: Any, + origin: Optional[Union[dns.name.Name, str]] = None, + rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, + relativize: bool = True, + zone_factory: Any = Zone, + filename: Optional[str] = None, + allow_include: bool = True, + check_origin: bool = True, + idna_codec: Optional[dns.name.IDNACodec] = None, + allow_directives: Union[bool, Iterable[str]] = True, +) -> Zone: + """Read a zone file and build a zone object. + + *f*, a file or ``str``. If *f* is a string, it is treated + as the name of a file to open. + + *origin*, a ``dns.name.Name``, a ``str``, or ``None``. The origin + of the zone; if not specified, the first ``$ORIGIN`` statement in the + zone file will determine the origin of the zone. + + *rdclass*, an ``int``, the zone's rdata class; the default is class IN. + + *relativize*, a ``bool``, determine's whether domain names are + relativized to the zone's origin. The default is ``True``. + + *zone_factory*, the zone factory to use or ``None``. If ``None``, then + ``dns.zone.Zone`` will be used. The value may be any class or callable + that returns a subclass of ``dns.zone.Zone``. + + *filename*, a ``str`` or ``None``, the filename to emit when + describing where an error occurred; the default is ``''``. + + *allow_include*, a ``bool``. If ``True``, the default, then ``$INCLUDE`` + directives are permitted. If ``False``, then encoutering a ``$INCLUDE`` + will raise a ``SyntaxError`` exception. + + *check_origin*, a ``bool``. If ``True``, the default, then sanity + checks of the origin node will be made by calling the zone's + ``check_origin()`` method. + + *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA + encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder + is used. + + *allow_directives*, a ``bool`` or an iterable of `str`. If ``True``, the default, + then directives are permitted, and the *allow_include* parameter controls whether + ``$INCLUDE`` is permitted. If ``False`` or an empty iterable, then no directive + processing is done and any directive-like text will be treated as a regular owner + name. If a non-empty iterable, then only the listed directives (including the + ``$``) are allowed. + + Raises ``dns.zone.NoSOA`` if there is no SOA RRset. + + Raises ``dns.zone.NoNS`` if there is no NS RRset. + + Raises ``KeyError`` if there is no origin node. + + Returns a subclass of ``dns.zone.Zone``. + """ + + if isinstance(f, str): + if filename is None: + filename = f + cm: contextlib.AbstractContextManager = open(f) + else: + cm = contextlib.nullcontext(f) + with cm as f: + return _from_text( + f, + origin, + rdclass, + relativize, + zone_factory, + filename, + allow_include, + check_origin, + idna_codec, + allow_directives, + ) + assert False # make mypy happy lgtm[py/unreachable-statement] + + +def from_xfr( + xfr: Any, + zone_factory: Any = Zone, + relativize: bool = True, + check_origin: bool = True, +) -> Zone: + """Convert the output of a zone transfer generator into a zone object. + + *xfr*, a generator of ``dns.message.Message`` objects, typically + ``dns.query.xfr()``. + + *relativize*, a ``bool``, determine's whether domain names are + relativized to the zone's origin. The default is ``True``. + It is essential that the relativize setting matches the one specified + to the generator. + + *check_origin*, a ``bool``. If ``True``, the default, then sanity + checks of the origin node will be made by calling the zone's + ``check_origin()`` method. + + Raises ``dns.zone.NoSOA`` if there is no SOA RRset. + + Raises ``dns.zone.NoNS`` if there is no NS RRset. + + Raises ``KeyError`` if there is no origin node. + + Raises ``ValueError`` if no messages are yielded by the generator. + + Returns a subclass of ``dns.zone.Zone``. + """ + + z = None + for r in xfr: + if z is None: + if relativize: + origin = r.origin + else: + origin = r.answer[0].name + rdclass = r.answer[0].rdclass + z = zone_factory(origin, rdclass, relativize=relativize) + for rrset in r.answer: + znode = z.nodes.get(rrset.name) + if not znode: + znode = z.node_factory() + z.nodes[rrset.name] = znode + zrds = znode.find_rdataset(rrset.rdclass, rrset.rdtype, rrset.covers, True) + zrds.update_ttl(rrset.ttl) + for rd in rrset: + zrds.add(rd) + if z is None: + raise ValueError("empty transfer") + if check_origin: + z.check_origin() + return z diff --git a/.venv/lib/python3.9/site-packages/dns/zonefile.py b/.venv/lib/python3.9/site-packages/dns/zonefile.py new file mode 100644 index 0000000..d74510b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/zonefile.py @@ -0,0 +1,744 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""DNS Zones.""" + +import re +import sys +from typing import Any, Iterable, List, Optional, Set, Tuple, Union + +import dns.exception +import dns.grange +import dns.name +import dns.node +import dns.rdata +import dns.rdataclass +import dns.rdatatype +import dns.rdtypes.ANY.SOA +import dns.rrset +import dns.tokenizer +import dns.transaction +import dns.ttl + + +class UnknownOrigin(dns.exception.DNSException): + """Unknown origin""" + + +class CNAMEAndOtherData(dns.exception.DNSException): + """A node has a CNAME and other data""" + + +def _check_cname_and_other_data(txn, name, rdataset): + rdataset_kind = dns.node.NodeKind.classify_rdataset(rdataset) + node = txn.get_node(name) + if node is None: + # empty nodes are neutral. + return + node_kind = node.classify() + if ( + node_kind == dns.node.NodeKind.CNAME + and rdataset_kind == dns.node.NodeKind.REGULAR + ): + raise CNAMEAndOtherData("rdataset type is not compatible with a CNAME node") + elif ( + node_kind == dns.node.NodeKind.REGULAR + and rdataset_kind == dns.node.NodeKind.CNAME + ): + raise CNAMEAndOtherData( + "CNAME rdataset is not compatible with a regular data node" + ) + # Otherwise at least one of the node and the rdataset is neutral, so + # adding the rdataset is ok + + +SavedStateType = Tuple[ + dns.tokenizer.Tokenizer, + Optional[dns.name.Name], # current_origin + Optional[dns.name.Name], # last_name + Optional[Any], # current_file + int, # last_ttl + bool, # last_ttl_known + int, # default_ttl + bool, +] # default_ttl_known + + +def _upper_dollarize(s): + s = s.upper() + if not s.startswith("$"): + s = "$" + s + return s + + +class Reader: + """Read a DNS zone file into a transaction.""" + + def __init__( + self, + tok: dns.tokenizer.Tokenizer, + rdclass: dns.rdataclass.RdataClass, + txn: dns.transaction.Transaction, + allow_include: bool = False, + allow_directives: Union[bool, Iterable[str]] = True, + force_name: Optional[dns.name.Name] = None, + force_ttl: Optional[int] = None, + force_rdclass: Optional[dns.rdataclass.RdataClass] = None, + force_rdtype: Optional[dns.rdatatype.RdataType] = None, + default_ttl: Optional[int] = None, + ): + self.tok = tok + (self.zone_origin, self.relativize, _) = txn.manager.origin_information() + self.current_origin = self.zone_origin + self.last_ttl = 0 + self.last_ttl_known = False + if force_ttl is not None: + default_ttl = force_ttl + if default_ttl is None: + self.default_ttl = 0 + self.default_ttl_known = False + else: + self.default_ttl = default_ttl + self.default_ttl_known = True + self.last_name = self.current_origin + self.zone_rdclass = rdclass + self.txn = txn + self.saved_state: List[SavedStateType] = [] + self.current_file: Optional[Any] = None + self.allowed_directives: Set[str] + if allow_directives is True: + self.allowed_directives = {"$GENERATE", "$ORIGIN", "$TTL"} + if allow_include: + self.allowed_directives.add("$INCLUDE") + elif allow_directives is False: + # allow_include was ignored in earlier releases if allow_directives was + # False, so we continue that. + self.allowed_directives = set() + else: + # Note that if directives are explicitly specified, then allow_include + # is ignored. + self.allowed_directives = set(_upper_dollarize(d) for d in allow_directives) + self.force_name = force_name + self.force_ttl = force_ttl + self.force_rdclass = force_rdclass + self.force_rdtype = force_rdtype + self.txn.check_put_rdataset(_check_cname_and_other_data) + + def _eat_line(self): + while 1: + token = self.tok.get() + if token.is_eol_or_eof(): + break + + def _get_identifier(self): + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + return token + + def _rr_line(self): + """Process one line from a DNS zone file.""" + token = None + # Name + if self.force_name is not None: + name = self.force_name + else: + if self.current_origin is None: + raise UnknownOrigin + token = self.tok.get(want_leading=True) + if not token.is_whitespace(): + self.last_name = self.tok.as_name(token, self.current_origin) + else: + token = self.tok.get() + if token.is_eol_or_eof(): + # treat leading WS followed by EOL/EOF as if they were EOL/EOF. + return + self.tok.unget(token) + name = self.last_name + if not name.is_subdomain(self.zone_origin): + self._eat_line() + return + if self.relativize: + name = name.relativize(self.zone_origin) + + # TTL + if self.force_ttl is not None: + ttl = self.force_ttl + self.last_ttl = ttl + self.last_ttl_known = True + else: + token = self._get_identifier() + ttl = None + try: + ttl = dns.ttl.from_text(token.value) + self.last_ttl = ttl + self.last_ttl_known = True + token = None + except dns.ttl.BadTTL: + self.tok.unget(token) + + # Class + if self.force_rdclass is not None: + rdclass = self.force_rdclass + else: + token = self._get_identifier() + try: + rdclass = dns.rdataclass.from_text(token.value) + except dns.exception.SyntaxError: + raise + except Exception: + rdclass = self.zone_rdclass + self.tok.unget(token) + if rdclass != self.zone_rdclass: + raise dns.exception.SyntaxError("RR class is not zone's class") + + if ttl is None: + # support for syntax + token = self._get_identifier() + ttl = None + try: + ttl = dns.ttl.from_text(token.value) + self.last_ttl = ttl + self.last_ttl_known = True + token = None + except dns.ttl.BadTTL: + if self.default_ttl_known: + ttl = self.default_ttl + elif self.last_ttl_known: + ttl = self.last_ttl + self.tok.unget(token) + + # Type + if self.force_rdtype is not None: + rdtype = self.force_rdtype + else: + token = self._get_identifier() + try: + rdtype = dns.rdatatype.from_text(token.value) + except Exception: + raise dns.exception.SyntaxError(f"unknown rdatatype '{token.value}'") + + try: + rd = dns.rdata.from_text( + rdclass, + rdtype, + self.tok, + self.current_origin, + self.relativize, + self.zone_origin, + ) + except dns.exception.SyntaxError: + # Catch and reraise. + raise + except Exception: + # All exceptions that occur in the processing of rdata + # are treated as syntax errors. This is not strictly + # correct, but it is correct almost all of the time. + # We convert them to syntax errors so that we can emit + # helpful filename:line info. + (ty, va) = sys.exc_info()[:2] + raise dns.exception.SyntaxError(f"caught exception {str(ty)}: {str(va)}") + + if not self.default_ttl_known and rdtype == dns.rdatatype.SOA: + # The pre-RFC2308 and pre-BIND9 behavior inherits the zone default + # TTL from the SOA minttl if no $TTL statement is present before the + # SOA is parsed. + self.default_ttl = rd.minimum + self.default_ttl_known = True + if ttl is None: + # if we didn't have a TTL on the SOA, set it! + ttl = rd.minimum + + # TTL check. We had to wait until now to do this as the SOA RR's + # own TTL can be inferred from its minimum. + if ttl is None: + raise dns.exception.SyntaxError("Missing default TTL value") + + self.txn.add(name, ttl, rd) + + def _parse_modify(self, side: str) -> Tuple[str, str, int, int, str]: + # Here we catch everything in '{' '}' in a group so we can replace it + # with ''. + is_generate1 = re.compile(r"^.*\$({(\+|-?)(\d+),(\d+),(.)}).*$") + is_generate2 = re.compile(r"^.*\$({(\+|-?)(\d+)}).*$") + is_generate3 = re.compile(r"^.*\$({(\+|-?)(\d+),(\d+)}).*$") + # Sometimes there are modifiers in the hostname. These come after + # the dollar sign. They are in the form: ${offset[,width[,base]]}. + # Make names + mod = "" + sign = "+" + offset = "0" + width = "0" + base = "d" + g1 = is_generate1.match(side) + if g1: + mod, sign, offset, width, base = g1.groups() + if sign == "": + sign = "+" + else: + g2 = is_generate2.match(side) + if g2: + mod, sign, offset = g2.groups() + if sign == "": + sign = "+" + width = "0" + base = "d" + else: + g3 = is_generate3.match(side) + if g3: + mod, sign, offset, width = g3.groups() + if sign == "": + sign = "+" + base = "d" + + ioffset = int(offset) + iwidth = int(width) + + if sign not in ["+", "-"]: + raise dns.exception.SyntaxError(f"invalid offset sign {sign}") + if base not in ["d", "o", "x", "X", "n", "N"]: + raise dns.exception.SyntaxError(f"invalid type {base}") + + return mod, sign, ioffset, iwidth, base + + def _generate_line(self): + # range lhs [ttl] [class] type rhs [ comment ] + """Process one line containing the GENERATE statement from a DNS + zone file.""" + if self.current_origin is None: + raise UnknownOrigin + + token = self.tok.get() + # Range (required) + try: + start, stop, step = dns.grange.from_text(token.value) + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + except Exception: + raise dns.exception.SyntaxError + + # lhs (required) + try: + lhs = token.value + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + except Exception: + raise dns.exception.SyntaxError + + # TTL + try: + ttl = dns.ttl.from_text(token.value) + self.last_ttl = ttl + self.last_ttl_known = True + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + except dns.ttl.BadTTL: + if not (self.last_ttl_known or self.default_ttl_known): + raise dns.exception.SyntaxError("Missing default TTL value") + if self.default_ttl_known: + ttl = self.default_ttl + elif self.last_ttl_known: + ttl = self.last_ttl + # Class + try: + rdclass = dns.rdataclass.from_text(token.value) + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + except dns.exception.SyntaxError: + raise dns.exception.SyntaxError + except Exception: + rdclass = self.zone_rdclass + if rdclass != self.zone_rdclass: + raise dns.exception.SyntaxError("RR class is not zone's class") + # Type + try: + rdtype = dns.rdatatype.from_text(token.value) + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + except Exception: + raise dns.exception.SyntaxError(f"unknown rdatatype '{token.value}'") + + # rhs (required) + rhs = token.value + + def _calculate_index(counter: int, offset_sign: str, offset: int) -> int: + """Calculate the index from the counter and offset.""" + if offset_sign == "-": + offset *= -1 + return counter + offset + + def _format_index(index: int, base: str, width: int) -> str: + """Format the index with the given base, and zero-fill it + to the given width.""" + if base in ["d", "o", "x", "X"]: + return format(index, base).zfill(width) + + # base can only be n or N here + hexa = _format_index(index, "x", width) + nibbles = ".".join(hexa[::-1])[:width] + if base == "N": + nibbles = nibbles.upper() + return nibbles + + lmod, lsign, loffset, lwidth, lbase = self._parse_modify(lhs) + rmod, rsign, roffset, rwidth, rbase = self._parse_modify(rhs) + for i in range(start, stop + 1, step): + # +1 because bind is inclusive and python is exclusive + + lindex = _calculate_index(i, lsign, loffset) + rindex = _calculate_index(i, rsign, roffset) + + lzfindex = _format_index(lindex, lbase, lwidth) + rzfindex = _format_index(rindex, rbase, rwidth) + + name = lhs.replace(f"${lmod}", lzfindex) + rdata = rhs.replace(f"${rmod}", rzfindex) + + self.last_name = dns.name.from_text( + name, self.current_origin, self.tok.idna_codec + ) + name = self.last_name + if not name.is_subdomain(self.zone_origin): + self._eat_line() + return + if self.relativize: + name = name.relativize(self.zone_origin) + + try: + rd = dns.rdata.from_text( + rdclass, + rdtype, + rdata, + self.current_origin, + self.relativize, + self.zone_origin, + ) + except dns.exception.SyntaxError: + # Catch and reraise. + raise + except Exception: + # All exceptions that occur in the processing of rdata + # are treated as syntax errors. This is not strictly + # correct, but it is correct almost all of the time. + # We convert them to syntax errors so that we can emit + # helpful filename:line info. + (ty, va) = sys.exc_info()[:2] + raise dns.exception.SyntaxError( + f"caught exception {str(ty)}: {str(va)}" + ) + + self.txn.add(name, ttl, rd) + + def read(self) -> None: + """Read a DNS zone file and build a zone object. + + @raises dns.zone.NoSOA: No SOA RR was found at the zone origin + @raises dns.zone.NoNS: No NS RRset was found at the zone origin + """ + + try: + while 1: + token = self.tok.get(True, True) + if token.is_eof(): + if self.current_file is not None: + self.current_file.close() + if len(self.saved_state) > 0: + ( + self.tok, + self.current_origin, + self.last_name, + self.current_file, + self.last_ttl, + self.last_ttl_known, + self.default_ttl, + self.default_ttl_known, + ) = self.saved_state.pop(-1) + continue + break + elif token.is_eol(): + continue + elif token.is_comment(): + self.tok.get_eol() + continue + elif token.value[0] == "$" and len(self.allowed_directives) > 0: + # Note that we only run directive processing code if at least + # one directive is allowed in order to be backwards compatible + c = token.value.upper() + if c not in self.allowed_directives: + raise dns.exception.SyntaxError( + f"zone file directive '{c}' is not allowed" + ) + if c == "$TTL": + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError("bad $TTL") + self.default_ttl = dns.ttl.from_text(token.value) + self.default_ttl_known = True + self.tok.get_eol() + elif c == "$ORIGIN": + self.current_origin = self.tok.get_name() + self.tok.get_eol() + if self.zone_origin is None: + self.zone_origin = self.current_origin + self.txn._set_origin(self.current_origin) + elif c == "$INCLUDE": + token = self.tok.get() + filename = token.value + token = self.tok.get() + new_origin: Optional[dns.name.Name] + if token.is_identifier(): + new_origin = dns.name.from_text( + token.value, self.current_origin, self.tok.idna_codec + ) + self.tok.get_eol() + elif not token.is_eol_or_eof(): + raise dns.exception.SyntaxError("bad origin in $INCLUDE") + else: + new_origin = self.current_origin + self.saved_state.append( + ( + self.tok, + self.current_origin, + self.last_name, + self.current_file, + self.last_ttl, + self.last_ttl_known, + self.default_ttl, + self.default_ttl_known, + ) + ) + self.current_file = open(filename) + self.tok = dns.tokenizer.Tokenizer(self.current_file, filename) + self.current_origin = new_origin + elif c == "$GENERATE": + self._generate_line() + else: + raise dns.exception.SyntaxError( + f"Unknown zone file directive '{c}'" + ) + continue + self.tok.unget(token) + self._rr_line() + except dns.exception.SyntaxError as detail: + (filename, line_number) = self.tok.where() + if detail is None: + detail = "syntax error" + ex = dns.exception.SyntaxError( + "%s:%d: %s" % (filename, line_number, detail) + ) + tb = sys.exc_info()[2] + raise ex.with_traceback(tb) from None + + +class RRsetsReaderTransaction(dns.transaction.Transaction): + def __init__(self, manager, replacement, read_only): + assert not read_only + super().__init__(manager, replacement, read_only) + self.rdatasets = {} + + def _get_rdataset(self, name, rdtype, covers): + return self.rdatasets.get((name, rdtype, covers)) + + def _get_node(self, name): + rdatasets = [] + for (rdataset_name, _, _), rdataset in self.rdatasets.items(): + if name == rdataset_name: + rdatasets.append(rdataset) + if len(rdatasets) == 0: + return None + node = dns.node.Node() + node.rdatasets = rdatasets + return node + + def _put_rdataset(self, name, rdataset): + self.rdatasets[(name, rdataset.rdtype, rdataset.covers)] = rdataset + + def _delete_name(self, name): + # First remove any changes involving the name + remove = [] + for key in self.rdatasets: + if key[0] == name: + remove.append(key) + if len(remove) > 0: + for key in remove: + del self.rdatasets[key] + + def _delete_rdataset(self, name, rdtype, covers): + try: + del self.rdatasets[(name, rdtype, covers)] + except KeyError: + pass + + def _name_exists(self, name): + for n, _, _ in self.rdatasets: + if n == name: + return True + return False + + def _changed(self): + return len(self.rdatasets) > 0 + + def _end_transaction(self, commit): + if commit and self._changed(): + rrsets = [] + for (name, _, _), rdataset in self.rdatasets.items(): + rrset = dns.rrset.RRset( + name, rdataset.rdclass, rdataset.rdtype, rdataset.covers + ) + rrset.update(rdataset) + rrsets.append(rrset) + self.manager.set_rrsets(rrsets) + + def _set_origin(self, origin): + pass + + def _iterate_rdatasets(self): + raise NotImplementedError # pragma: no cover + + def _iterate_names(self): + raise NotImplementedError # pragma: no cover + + +class RRSetsReaderManager(dns.transaction.TransactionManager): + def __init__( + self, origin=dns.name.root, relativize=False, rdclass=dns.rdataclass.IN + ): + self.origin = origin + self.relativize = relativize + self.rdclass = rdclass + self.rrsets = [] + + def reader(self): # pragma: no cover + raise NotImplementedError + + def writer(self, replacement=False): + assert replacement is True + return RRsetsReaderTransaction(self, True, False) + + def get_class(self): + return self.rdclass + + def origin_information(self): + if self.relativize: + effective = dns.name.empty + else: + effective = self.origin + return (self.origin, self.relativize, effective) + + def set_rrsets(self, rrsets): + self.rrsets = rrsets + + +def read_rrsets( + text: Any, + name: Optional[Union[dns.name.Name, str]] = None, + ttl: Optional[int] = None, + rdclass: Optional[Union[dns.rdataclass.RdataClass, str]] = dns.rdataclass.IN, + default_rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, + rdtype: Optional[Union[dns.rdatatype.RdataType, str]] = None, + default_ttl: Optional[Union[int, str]] = None, + idna_codec: Optional[dns.name.IDNACodec] = None, + origin: Optional[Union[dns.name.Name, str]] = dns.name.root, + relativize: bool = False, +) -> List[dns.rrset.RRset]: + """Read one or more rrsets from the specified text, possibly subject + to restrictions. + + *text*, a file object or a string, is the input to process. + + *name*, a string, ``dns.name.Name``, or ``None``, is the owner name of + the rrset. If not ``None``, then the owner name is "forced", and the + input must not specify an owner name. If ``None``, then any owner names + are allowed and must be present in the input. + + *ttl*, an ``int``, string, or None. If not ``None``, the the TTL is + forced to be the specified value and the input must not specify a TTL. + If ``None``, then a TTL may be specified in the input. If it is not + specified, then the *default_ttl* will be used. + + *rdclass*, a ``dns.rdataclass.RdataClass``, string, or ``None``. If + not ``None``, then the class is forced to the specified value, and the + input must not specify a class. If ``None``, then the input may specify + a class that matches *default_rdclass*. Note that it is not possible to + return rrsets with differing classes; specifying ``None`` for the class + simply allows the user to optionally type a class as that may be convenient + when cutting and pasting. + + *default_rdclass*, a ``dns.rdataclass.RdataClass`` or string. The class + of the returned rrsets. + + *rdtype*, a ``dns.rdatatype.RdataType``, string, or ``None``. If not + ``None``, then the type is forced to the specified value, and the + input must not specify a type. If ``None``, then a type must be present + for each RR. + + *default_ttl*, an ``int``, string, or ``None``. If not ``None``, then if + the TTL is not forced and is not specified, then this value will be used. + if ``None``, then if the TTL is not forced an error will occur if the TTL + is not specified. + + *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA + encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder + is used. Note that codecs only apply to the owner name; dnspython does + not do IDNA for names in rdata, as there is no IDNA zonefile format. + + *origin*, a string, ``dns.name.Name``, or ``None``, is the origin for any + relative names in the input, and also the origin to relativize to if + *relativize* is ``True``. + + *relativize*, a bool. If ``True``, names are relativized to the *origin*; + if ``False`` then any relative names in the input are made absolute by + appending the *origin*. + """ + if isinstance(origin, str): + origin = dns.name.from_text(origin, dns.name.root, idna_codec) + if isinstance(name, str): + name = dns.name.from_text(name, origin, idna_codec) + if isinstance(ttl, str): + ttl = dns.ttl.from_text(ttl) + if isinstance(default_ttl, str): + default_ttl = dns.ttl.from_text(default_ttl) + if rdclass is not None: + rdclass = dns.rdataclass.RdataClass.make(rdclass) + else: + rdclass = None + default_rdclass = dns.rdataclass.RdataClass.make(default_rdclass) + if rdtype is not None: + rdtype = dns.rdatatype.RdataType.make(rdtype) + else: + rdtype = None + manager = RRSetsReaderManager(origin, relativize, default_rdclass) + with manager.writer(True) as txn: + tok = dns.tokenizer.Tokenizer(text, "", idna_codec=idna_codec) + reader = Reader( + tok, + default_rdclass, + txn, + allow_directives=False, + force_name=name, + force_ttl=ttl, + force_rdclass=rdclass, + force_rdtype=rdtype, + default_ttl=default_ttl, + ) + reader.read() + return manager.rrsets diff --git a/.venv/lib/python3.9/site-packages/dns/zonetypes.py b/.venv/lib/python3.9/site-packages/dns/zonetypes.py new file mode 100644 index 0000000..195ee2e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dns/zonetypes.py @@ -0,0 +1,37 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +"""Common zone-related types.""" + +# This is a separate file to avoid import circularity between dns.zone and +# the implementation of the ZONEMD type. + +import hashlib + +import dns.enum + + +class DigestScheme(dns.enum.IntEnum): + """ZONEMD Scheme""" + + SIMPLE = 1 + + @classmethod + def _maximum(cls): + return 255 + + +class DigestHashAlgorithm(dns.enum.IntEnum): + """ZONEMD Hash Algorithm""" + + SHA384 = 1 + SHA512 = 2 + + @classmethod + def _maximum(cls): + return 255 + + +_digest_hashers = { + DigestHashAlgorithm.SHA384: hashlib.sha384, + DigestHashAlgorithm.SHA512: hashlib.sha512, +} diff --git a/.venv/lib/python3.9/site-packages/dnspython-2.7.0.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/dnspython-2.7.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dnspython-2.7.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/dnspython-2.7.0.dist-info/METADATA b/.venv/lib/python3.9/site-packages/dnspython-2.7.0.dist-info/METADATA new file mode 100644 index 0000000..ca4a4f4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dnspython-2.7.0.dist-info/METADATA @@ -0,0 +1,149 @@ +Metadata-Version: 2.3 +Name: dnspython +Version: 2.7.0 +Summary: DNS toolkit +Project-URL: homepage, https://www.dnspython.org +Project-URL: repository, https://github.com/rthalley/dnspython.git +Project-URL: documentation, https://dnspython.readthedocs.io/en/stable/ +Project-URL: issues, https://github.com/rthalley/dnspython/issues +Author-email: Bob Halley +License: ISC +License-File: LICENSE +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: ISC License (ISCL) +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX +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: Topic :: Internet :: Name Service (DNS) +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.9 +Provides-Extra: dev +Requires-Dist: black>=23.1.0; extra == 'dev' +Requires-Dist: coverage>=7.0; extra == 'dev' +Requires-Dist: flake8>=7; extra == 'dev' +Requires-Dist: hypercorn>=0.16.0; extra == 'dev' +Requires-Dist: mypy>=1.8; extra == 'dev' +Requires-Dist: pylint>=3; extra == 'dev' +Requires-Dist: pytest-cov>=4.1.0; extra == 'dev' +Requires-Dist: pytest>=7.4; extra == 'dev' +Requires-Dist: quart-trio>=0.11.0; extra == 'dev' +Requires-Dist: sphinx-rtd-theme>=2.0.0; extra == 'dev' +Requires-Dist: sphinx>=7.2.0; extra == 'dev' +Requires-Dist: twine>=4.0.0; extra == 'dev' +Requires-Dist: wheel>=0.42.0; extra == 'dev' +Provides-Extra: dnssec +Requires-Dist: cryptography>=43; extra == 'dnssec' +Provides-Extra: doh +Requires-Dist: h2>=4.1.0; extra == 'doh' +Requires-Dist: httpcore>=1.0.0; extra == 'doh' +Requires-Dist: httpx>=0.26.0; extra == 'doh' +Provides-Extra: doq +Requires-Dist: aioquic>=1.0.0; extra == 'doq' +Provides-Extra: idna +Requires-Dist: idna>=3.7; extra == 'idna' +Provides-Extra: trio +Requires-Dist: trio>=0.23; extra == 'trio' +Provides-Extra: wmi +Requires-Dist: wmi>=1.5.1; extra == 'wmi' +Description-Content-Type: text/markdown + +# dnspython + +[![Build Status](https://github.com/rthalley/dnspython/actions/workflows/ci.yml/badge.svg)](https://github.com/rthalley/dnspython/actions/) +[![Documentation Status](https://readthedocs.org/projects/dnspython/badge/?version=latest)](https://dnspython.readthedocs.io/en/latest/?badge=latest) +[![PyPI version](https://badge.fury.io/py/dnspython.svg)](https://badge.fury.io/py/dnspython) +[![License: ISC](https://img.shields.io/badge/License-ISC-brightgreen.svg)](https://opensource.org/licenses/ISC) +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) + +## INTRODUCTION + +dnspython is a DNS toolkit for Python. It supports almost all record types. It +can be used for queries, zone transfers, and dynamic updates. It supports TSIG +authenticated messages and EDNS0. + +dnspython provides both high and low level access to DNS. The high level classes +perform queries for data of a given name, type, and class, and return an answer +set. The low level classes allow direct manipulation of DNS zones, messages, +names, and records. + +To see a few of the ways dnspython can be used, look in the `examples/` +directory. + +dnspython is a utility to work with DNS, `/etc/hosts` is thus not used. For +simple forward DNS lookups, it's better to use `socket.getaddrinfo()` or +`socket.gethostbyname()`. + +dnspython originated at Nominum where it was developed +to facilitate the testing of DNS software. + +## ABOUT THIS RELEASE + +This is dnspython 2.7.0. +Please read +[What's New](https://dnspython.readthedocs.io/en/stable/whatsnew.html) for +information about the changes in this release. + +## INSTALLATION + +* Many distributions have dnspython packaged for you, so you should + check there first. +* To use a wheel downloaded from PyPi, run: + + pip install dnspython + +* To install from the source code, go into the top-level of the source code + and run: + +``` + pip install --upgrade pip build + python -m build + pip install dist/*.whl +``` + +* To install the latest from the main branch, run `pip install git+https://github.com/rthalley/dnspython.git` + +Dnspython's default installation does not depend on any modules other than +those in the Python standard library. To use some features, additional modules +must be installed. For convenience, pip options are defined for the +requirements. + +If you want to use DNS-over-HTTPS, run +`pip install dnspython[doh]`. + +If you want to use DNSSEC functionality, run +`pip install dnspython[dnssec]`. + +If you want to use internationalized domain names (IDNA) +functionality, you must run +`pip install dnspython[idna]` + +If you want to use the Trio asynchronous I/O package, run +`pip install dnspython[trio]`. + +If you want to use WMI on Windows to determine the active DNS settings +instead of the default registry scanning method, run +`pip install dnspython[wmi]`. + +If you want to try the experimental DNS-over-QUIC code, run +`pip install dnspython[doq]`. + +Note that you can install any combination of the above, e.g.: +`pip install dnspython[doh,dnssec,idna]` + +### Notices + +Python 2.x support ended with the release of 1.16.0. Dnspython 2.6.x supports +Python 3.8 and later, though support for 3.8 ends on October 14, 2024. +Dnspython 2.7.x supports Python 3.9 and later. Future support is aligned with the +lifetime of the Python 3 versions. + +Documentation has moved to +[dnspython.readthedocs.io](https://dnspython.readthedocs.io). diff --git a/.venv/lib/python3.9/site-packages/dnspython-2.7.0.dist-info/RECORD b/.venv/lib/python3.9/site-packages/dnspython-2.7.0.dist-info/RECORD new file mode 100644 index 0000000..58176ba --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dnspython-2.7.0.dist-info/RECORD @@ -0,0 +1,294 @@ +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/_asyncbackend.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/_asyncio_backend.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/_ddr.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/_features.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/_immutable_ctx.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/_trio_backend.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/asyncbackend.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/asyncquery.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/asyncresolver.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/dnssec.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/dnssecalgs/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/dnssecalgs/base.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/dnssecalgs/cryptography.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/dnssecalgs/dsa.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/dnssecalgs/ecdsa.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/dnssecalgs/eddsa.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/dnssecalgs/rsa.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/dnssectypes.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/e164.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/edns.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/entropy.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/enum.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/exception.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/flags.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/grange.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/immutable.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/inet.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/ipv4.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/ipv6.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/message.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/name.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/namedict.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/nameserver.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/node.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/opcode.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/query.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/quic/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/quic/_asyncio.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/quic/_common.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/quic/_sync.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/quic/_trio.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rcode.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdata.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdataclass.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdataset.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdatatype.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/AFSDB.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/AMTRELAY.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/AVC.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CAA.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CDNSKEY.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CDS.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CERT.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CNAME.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/CSYNC.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/DLV.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/DNAME.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/DNSKEY.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/DS.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/EUI48.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/EUI64.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/GPOS.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/HINFO.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/HIP.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/ISDN.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/L32.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/L64.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/LOC.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/LP.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/MX.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NID.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NINFO.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NS.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NSEC.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NSEC3.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/NSEC3PARAM.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/OPENPGPKEY.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/OPT.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/PTR.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/RESINFO.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/RP.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/RRSIG.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/RT.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/SMIMEA.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/SOA.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/SPF.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/SSHFP.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/TKEY.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/TLSA.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/TSIG.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/TXT.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/URI.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/WALLET.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/X25.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/ZONEMD.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/ANY/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/CH/A.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/CH/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/A.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/AAAA.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/APL.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/DHCID.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/HTTPS.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/IPSECKEY.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/KX.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/NAPTR.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/NSAP.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/NSAP_PTR.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/PX.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/SRV.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/SVCB.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/WKS.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/IN/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/dnskeybase.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/dsbase.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/euibase.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/mxbase.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/nsbase.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/svcbbase.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/tlsabase.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/txtbase.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rdtypes/util.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/renderer.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/resolver.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/reversename.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/rrset.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/serial.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/set.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/tokenizer.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/transaction.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/tsig.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/tsigkeyring.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/ttl.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/update.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/version.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/versioned.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/win32util.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/wire.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/xfr.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/zone.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/zonefile.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/dns/zonetypes.cpython-39.pyc,, +dns/__init__.py,sha256=YJZtDG14Idw5ui3h1nWooSwPM9gsxQgB8M0GBZ3aly0,1663 +dns/_asyncbackend.py,sha256=pamIAWJ73e7ic2u7Q3RJyG6_6L8t78ccttvi65682MM,2396 +dns/_asyncio_backend.py,sha256=iLqhcUXqnFWC_2tcAp9U00NOGxT5GKPn4qeXS4iKaro,9051 +dns/_ddr.py,sha256=rHXKC8kncCTT9N4KBh1flicl79nyDjQ-DDvq30MJ3B8,5247 +dns/_features.py,sha256=Ig_leAKUT9RDiOVOfA0nXmmqpiPfnOnP9TcxlISUGSk,2492 +dns/_immutable_ctx.py,sha256=gtoCLMmdHXI23zt5lRSIS3A4Ca3jZJngebdoFFOtiwU,2459 +dns/_trio_backend.py,sha256=IXNdUP1MUBPyZRgAFhGH71KHtUCh3Rm5dM8SX4bMj2U,8473 +dns/asyncbackend.py,sha256=82fXTFls_m7F_ekQbgUGOkoBbs4BI-GBLDZAWNGUvJ0,2796 +dns/asyncquery.py,sha256=PMZ_D4Z8vgSioWHftyxNw7eax1IqrPleqY5FIi40hd8,30821 +dns/asyncresolver.py,sha256=GD86dCyW9YGKs6SggWXwBKEXifW7Qdx4cEAGFKY6fA4,17852 +dns/dnssec.py,sha256=gXmIrbKK1t1hE8ht-WlhUc0giy1PpLYj07r6o0pVATY,41717 +dns/dnssecalgs/__init__.py,sha256=OWvTadxZ3oF5PxVGodNimxBt_-3YUNTOSV62HrIb4PQ,4331 +dns/dnssecalgs/base.py,sha256=jlTV_nd1Nqkvqyf-FVHIccXKFrE2LL6GVu6AW8QUh2E,2513 +dns/dnssecalgs/cryptography.py,sha256=3uqMfRm-zCkJPOrxUqlu9CmdxIMy71dVor9eAHi0wZM,2425 +dns/dnssecalgs/dsa.py,sha256=DNO68g_lbG7_oKcDN8c2xuzYRPbLaZc9Ns7oQoa0Vbc,3564 +dns/dnssecalgs/ecdsa.py,sha256=RfvFKRNExsYgd5SoXXRxMHkoBeF2Gktkz2rOwObEYAY,3172 +dns/dnssecalgs/eddsa.py,sha256=7VGARpVUzIYRjPh0gFapTPFzmsK8WJDqDZDLw2KLc8w,1981 +dns/dnssecalgs/rsa.py,sha256=_tNABpr6iwd8STBEHYIXfyLrgBpRNCj8K0UQj32_kOU,3622 +dns/dnssectypes.py,sha256=CyeuGTS_rM3zXr8wD9qMT9jkzvVfTY2JWckUcogG83E,1799 +dns/e164.py,sha256=EsK8cnOtOx7kQ0DmSwibcwkzp6efMWjbRiTyHZO8Q-M,3978 +dns/edns.py,sha256=-XDhC2jr7BRLsJrpCAWShxLn-3eG1oI0HhduWhLxdMw,17089 +dns/entropy.py,sha256=qkG8hXDLzrJS6R5My26iA59c0RhPwJNzuOhOCAZU5Bw,4242 +dns/enum.py,sha256=EepaunPKixTSrascy7iAe9UQEXXxP_MB5Gx4jUpHIhg,3691 +dns/exception.py,sha256=8vjxLf4T3T77vfANe_iKVeButAEhSJve6UrPjiBzht4,5953 +dns/flags.py,sha256=cQ3kTFyvcKiWHAxI5AwchNqxVOrsIrgJ6brgrH42Wq8,2750 +dns/grange.py,sha256=D016OrOv3i44G3mb_CzPFjDk61uZ6BMRib3yJnDQvbw,2144 +dns/immutable.py,sha256=InrtpKvPxl-74oYbzsyneZwAuX78hUqeG22f2aniZbk,2017 +dns/inet.py,sha256=j6jQs3K_ehVhDv-i4jwCKePr5HpEiSzvOXQ4uhgn1sU,5772 +dns/ipv4.py,sha256=qEUXtlqWDH_blicj6VMvyQhfX7-BF0gB_lWJliV-2FI,2552 +dns/ipv6.py,sha256=Ww8ayshM6FxtQsRYdXXuKkPFTad5ZcGbBd9lr1nFct4,6554 +dns/message.py,sha256=QOtdFBEAORhTKN0uQg86uSNvthdxJx40HhMQXYCBHng,68185 +dns/name.py,sha256=Bf3170QHhLFLDnMsWeJyik4i9ucBDbIY6Bydcz8H-2o,42778 +dns/namedict.py,sha256=hJRYpKeQv6Bd2LaUOPV0L_a0eXEIuqgggPXaH4c3Tow,4000 +dns/nameserver.py,sha256=hH4LLOkB4jeyO3VDUWK0lNpMJNNt_cFYf23-HdhpSmE,10115 +dns/node.py,sha256=NGZa0AUMq-CNledJ6wn1Rx6TFYc703cH2OraLysoNWM,12663 +dns/opcode.py,sha256=I6JyuFUL0msja_BYm6bzXHfbbfqUod_69Ss4xcv8xWQ,2730 +dns/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +dns/query.py,sha256=_Ev7EivZNEpgrUiPIn4BVnDRFCizcayHHcBXt0Ju3As,56298 +dns/quic/__init__.py,sha256=S5_2UuYzSU_LLtrLAf8DHh3KqNF2YHeKJ_-Wv991WlI,2272 +dns/quic/_asyncio.py,sha256=dnABPz5f-JOJsA7D_BdPfuyzpkL_87AaY4CUcmgNj-g,9870 +dns/quic/_common.py,sha256=koWf6rq9_gUorIOV60QZKAHwZF5MuSgQvBznzA5rGzk,10857 +dns/quic/_sync.py,sha256=QF-dW19NwiDW_BDoJZkSQmHz2uEpfgedsUKTPc0JAio,10436 +dns/quic/_trio.py,sha256=01HH4_hU1VRx-BWXl8bQo4-LZem_eKRBNy6RolTZZXY,9248 +dns/rcode.py,sha256=N6JjrIQjCdJy0boKIp8Hcky5tm__LSDscpDz3rE_sgU,4156 +dns/rdata.py,sha256=uk82eldqpWR8L2zp_CB8JG6wWWfK7zdYowWISfMC2XE,31022 +dns/rdataclass.py,sha256=TK4W4ywB1L_X7EZqk2Gmwnu7vdQpolQF5DtQWyNk5xo,2984 +dns/rdataset.py,sha256=BMNvGAzE4HfYHA-pnhsKwELfpr-saz73BzYwMucoKj0,16664 +dns/rdatatype.py,sha256=wgKWnu4mAbXnmG8wKHpV8dZHkhMqNeSsWWlWFo5HcDY,7448 +dns/rdtypes/ANY/AFSDB.py,sha256=k75wMwreF1DAfDymu4lHh16BUx7ulVP3PLeQBZnkurY,1661 +dns/rdtypes/ANY/AMTRELAY.py,sha256=19jfS61mT1CQT-8vf67ZylhDS9JVRVp4WCbFE-7l0jM,3381 +dns/rdtypes/ANY/AVC.py,sha256=SpsXYzlBirRWN0mGnQe0MdN6H8fvlgXPJX5PjOHnEak,1024 +dns/rdtypes/ANY/CAA.py,sha256=AHh59Is-4WiVWd26yovnPM3hXqKS-yx7IWfXSS0NZhE,2511 +dns/rdtypes/ANY/CDNSKEY.py,sha256=bJAdrBMsFHIJz8TF1AxZoNbdxVWBCRTG-bR_uR_r_G4,1225 +dns/rdtypes/ANY/CDS.py,sha256=Y9nIRUCAabztVLbxm2SXAdYapFemCOUuGh5JqroCDUs,1163 +dns/rdtypes/ANY/CERT.py,sha256=2Cu2LQM6-K4darqhHv1EM_blmpYpnrBIIX1GnL_rxKE,3533 +dns/rdtypes/ANY/CNAME.py,sha256=IHGGq2BDpeKUahTr1pvyBQgm0NGBI_vQ3Vs5mKTXO4w,1206 +dns/rdtypes/ANY/CSYNC.py,sha256=KkZ_rG6PfeL14il97nmJGWWmUGGS5o9nd2EqbJqOuYo,2439 +dns/rdtypes/ANY/DLV.py,sha256=J-pOrw5xXsDoaB9G0r6znlYXJtqtcqhsl1OXs6CPRU4,986 +dns/rdtypes/ANY/DNAME.py,sha256=yqXRtx4dAWwB4YCCv-qW6uaxeGhg2LPQ2uyKwWaMdXs,1150 +dns/rdtypes/ANY/DNSKEY.py,sha256=MD8HUVH5XXeAGOnFWg5aVz_w-2tXYwCeVXmzExhiIeQ,1223 +dns/rdtypes/ANY/DS.py,sha256=_gf8vk1O_uY8QXFjsfUw-bny-fm6e-QpCk3PT0JCyoM,995 +dns/rdtypes/ANY/EUI48.py,sha256=x0BkK0sY_tgzuCwfDYpw6tyuChHjjtbRpAgYhO0Y44o,1151 +dns/rdtypes/ANY/EUI64.py,sha256=1jCff2-SXHJLDnNDnMW8Cd_o-ok0P3x6zKy_bcCU5h4,1161 +dns/rdtypes/ANY/GPOS.py,sha256=u4qwiDBVoC7bsKfxDKGbPjnOKddpdjy2p1AhziDWcPw,4439 +dns/rdtypes/ANY/HINFO.py,sha256=D2WvjTsvD_XqT8BepBIyjPL2iYGMgYqb1VQa9ApO0qE,2217 +dns/rdtypes/ANY/HIP.py,sha256=c32Ewlk88schJ1nPOmT5BVR60ttIM-uH8I8LaRAkFOA,3226 +dns/rdtypes/ANY/ISDN.py,sha256=L4C2Rxrr4JJN17lmJRbZN8RhM_ujjwIskY_4V4Gd3r4,2723 +dns/rdtypes/ANY/L32.py,sha256=TMz2kdGCd0siiQZyiocVDCSnvkOdjhUuYRFyf8o622M,1286 +dns/rdtypes/ANY/L64.py,sha256=sb2BjuPA0PQt67nEyT9rBt759C9e6lH71d3EJHGGnww,1592 +dns/rdtypes/ANY/LOC.py,sha256=NZKIUJULZ3BcK1-gnb2Mk76Pc4UUZry47C5n9VBvhnk,11995 +dns/rdtypes/ANY/LP.py,sha256=wTsKIjtK6vh66qZRLSsiE0k54GO8ieVBGZH8dzVvFnE,1338 +dns/rdtypes/ANY/MX.py,sha256=qQk83idY0-SbRMDmB15JOpJi7cSyiheF-ALUD0Ev19E,995 +dns/rdtypes/ANY/NID.py,sha256=N7Xx4kXf3yVAocTlCXQeJ3BtiQNPFPQVdL1iMuyl5W4,1544 +dns/rdtypes/ANY/NINFO.py,sha256=bdL_-6Bejb2EH-xwR1rfSr_9E3SDXLTAnov7x2924FI,1041 +dns/rdtypes/ANY/NS.py,sha256=ThfaPalUlhbyZyNyvBM3k-7onl3eJKq5wCORrOGtkMM,995 +dns/rdtypes/ANY/NSEC.py,sha256=kicEYxcKaLBpV6C_M8cHdDaqBoiYl6EYtPvjyR6kExI,2465 +dns/rdtypes/ANY/NSEC3.py,sha256=696h-Zz30bmcT0n1rqoEtS5wqE6jIgsVGzaw5TfdGJo,4331 +dns/rdtypes/ANY/NSEC3PARAM.py,sha256=08p6NWS4DiLav1wOuPbxUxB9MtY2IPjfOMCtJwzzMuA,2635 +dns/rdtypes/ANY/OPENPGPKEY.py,sha256=Va0FGo_8vm1OeX62N5iDTWukAdLwrjTXIZeQ6oanE78,1851 +dns/rdtypes/ANY/OPT.py,sha256=W36RslT_Psp95OPUC70knumOYjKpaRHvGT27I-NV2qc,2561 +dns/rdtypes/ANY/PTR.py,sha256=5HcR1D77Otyk91vVY4tmqrfZfSxSXWyWvwIW-rIH5gc,997 +dns/rdtypes/ANY/RESINFO.py,sha256=Kf2NcKbkeI5gFE1bJfQNqQCaitYyXfV_9nQYl1luUZ0,1008 +dns/rdtypes/ANY/RP.py,sha256=8doJlhjYDYiAT6KNF1mAaemJ20YJFUPvit8LOx4-I-U,2174 +dns/rdtypes/ANY/RRSIG.py,sha256=O8vwzS7ldfaj_x8DypvEGFsDSb7al-D7OEnprA3QQoo,4922 +dns/rdtypes/ANY/RT.py,sha256=2t9q3FZQ28iEyceeU25KU2Ur0T5JxELAu8BTwfOUgVw,1013 +dns/rdtypes/ANY/SMIMEA.py,sha256=6yjHuVDfIEodBU9wxbCGCDZ5cWYwyY6FCk-aq2VNU0s,222 +dns/rdtypes/ANY/SOA.py,sha256=Cn8yrag1YvrvwivQgWg-KXmOCaVQVdFHSkFF77w-CE0,3145 +dns/rdtypes/ANY/SPF.py,sha256=rA3Srs9ECQx-37lqm7Zf7aYmMpp_asv4tGS8_fSQ-CU,1022 +dns/rdtypes/ANY/SSHFP.py,sha256=l6TZH2R0kytiZGWez_g-Lq94o5a2xMuwLKwUwsPMx5w,2530 +dns/rdtypes/ANY/TKEY.py,sha256=1ecTuBse2b4QPH2qmx3vn-gfPK0INcKXfxrIyAJxFHA,4927 +dns/rdtypes/ANY/TLSA.py,sha256=cytzebS3W7FFr9qeJ9gFSHq_bOwUk9aRVlXWHfnVrRs,218 +dns/rdtypes/ANY/TSIG.py,sha256=4fNQJSNWZXUKZejCciwQuUJtTw2g-YbPmqHrEj_pitg,4750 +dns/rdtypes/ANY/TXT.py,sha256=F1U9gIAhwXIV4UVT7CwOCEn_su6G1nJIdgWJsLktk20,1000 +dns/rdtypes/ANY/URI.py,sha256=dpcS8KwcJ2WJ7BkOp4CZYaUyRuw7U2S9GzvVwKUihQg,2921 +dns/rdtypes/ANY/WALLET.py,sha256=IaP2g7Nq26jWGKa8MVxvJjWXLQ0wrNR1IWJVyyMG8oU,219 +dns/rdtypes/ANY/X25.py,sha256=BzEM7uOY7CMAm7QN-dSLj-_LvgnnohwJDUjMstzwqYo,1942 +dns/rdtypes/ANY/ZONEMD.py,sha256=JQicv69EvUxh4FCT7eZSLzzU5L5brw_dSM65Um2t5lQ,2393 +dns/rdtypes/ANY/__init__.py,sha256=My5jT8T5bA66zBydmRSxkmDCFxwI81B4DBRA_S36IL8,1526 +dns/rdtypes/CH/A.py,sha256=-4G3ASZGj7oUlPfDxADibAB1WfTsZBavUO8ghDWarJ8,2212 +dns/rdtypes/CH/__init__.py,sha256=GD9YeDKb9VBDo-J5rrChX1MWEGyQXuR9Htnbhg_iYLc,923 +dns/rdtypes/IN/A.py,sha256=FfFn3SqbpneL9Ky63COP50V2ZFxqS1ldCKJh39Enwug,1814 +dns/rdtypes/IN/AAAA.py,sha256=AxrOlYy-1TTTWeQypDKeXrDCrdHGor0EKCE4fxzSQGo,1820 +dns/rdtypes/IN/APL.py,sha256=ppyFwn0KYMdyDzphxd0BUhgTmZv0QnDMRLjzQQM793U,5097 +dns/rdtypes/IN/DHCID.py,sha256=zRUh_EOxUPVpJjWY5m7taX8q4Oz5K70785ZtKv5OTCU,1856 +dns/rdtypes/IN/HTTPS.py,sha256=P-IjwcvDQMmtoBgsDHglXF7KgLX73G6jEDqCKsnaGpQ,220 +dns/rdtypes/IN/IPSECKEY.py,sha256=RyIy9K0Yt0uJRjdr6cj5S95ELHHbl--0xV-Qq9O3QQk,3290 +dns/rdtypes/IN/KX.py,sha256=K1JwItL0n5G-YGFCjWeh0C9DyDD8G8VzicsBeQiNAv0,1013 +dns/rdtypes/IN/NAPTR.py,sha256=SaOK-0hIYImwLtb5Hqewi-e49ykJaQiLNvk8ZzNoG7Q,3750 +dns/rdtypes/IN/NSAP.py,sha256=6YfWCVSIPTTBmRAzG8nVBj3LnohncXUhSFJHgp-TRdc,2163 +dns/rdtypes/IN/NSAP_PTR.py,sha256=iTxlV6fr_Y9lqivLLncSHxEhmFqz5UEElDW3HMBtuCU,1015 +dns/rdtypes/IN/PX.py,sha256=vHDNN2rfLObuUKwpYDIvpPB482BqXlHA-ZQpQn9Sb_E,2756 +dns/rdtypes/IN/SRV.py,sha256=a0zGaUwzvih_a4Q9BViUTFs7NZaCqgl7mls3-KRVHm8,2769 +dns/rdtypes/IN/SVCB.py,sha256=HeFmi2v01F00Hott8FlvQ4R7aPxFmT7RF-gt45R5K_M,218 +dns/rdtypes/IN/WKS.py,sha256=kErSG5AO2qIuot_hkMHnQuZB1_uUzUirNdqBoCp97rk,3652 +dns/rdtypes/IN/__init__.py,sha256=HbI8aw9HWroI6SgEvl8Sx6FdkDswCCXMbSRuJy5o8LQ,1083 +dns/rdtypes/__init__.py,sha256=NYizfGglJfhqt_GMtSSXf7YQXIEHHCiJ_Y_qaLVeiOI,1073 +dns/rdtypes/dnskeybase.py,sha256=FoDllfa9Pz2j2rf45VyUUYUsIt3kjjrwDy6LxrlPb5s,2856 +dns/rdtypes/dsbase.py,sha256=I85Aps1lBsiItdqGpsNY1O8icosfPtkWjiUn1J1lLUQ,3427 +dns/rdtypes/euibase.py,sha256=1yKWOM4xBwLLIFFfEj7M9JMankO2l8ljxhG-5OOxXDg,2618 +dns/rdtypes/mxbase.py,sha256=DzjbiKoAAgpqbhwMBIFGA081jR5_doqGAq-kLvy2mns,3196 +dns/rdtypes/nsbase.py,sha256=tueXVV6E8lelebOmrmoOPq47eeRvOpsxHVXH4cOFxcs,2323 +dns/rdtypes/svcbbase.py,sha256=YOH3Wz3fp5GQjdTF7hU-1ys9iDkYEC5p4d0F32ivv5g,17612 +dns/rdtypes/tlsabase.py,sha256=pIiWem6sF4IwyyKmyqx5xg55IG0w3K9r502Yx8PdziA,2596 +dns/rdtypes/txtbase.py,sha256=Dt9ptWSWtnq0Qwlni6IT6YUz_DCixQDDUl5d4P_AfqY,3696 +dns/rdtypes/util.py,sha256=c3eLaucwuxXZjXWuNyCGKzwltgub4AjT4uLVytEuxSk,9017 +dns/renderer.py,sha256=5THf1iKql2JPL2sKZt2-b4zqHKfk_vlx0FEfPtMJysY,11254 +dns/resolver.py,sha256=FH_hiMeCdVYonIYmE3QqEWJKgHOOxlTcHS0dwd_loGY,73730 +dns/reversename.py,sha256=zoqXEbMZXm6R13nXbJHgTsf6L2C6uReODj6mqSHrTiE,3828 +dns/rrset.py,sha256=J-oQPEPJuKueLLiz1FN08P-ys9fjHhPWuwpDdrL4UTQ,9170 +dns/serial.py,sha256=-t5rPW-TcJwzBMfIJo7Tl-uDtaYtpqOfCVYx9dMaDCY,3606 +dns/set.py,sha256=hublMKCIhd9zp5Hz_fvQTwF-Ze28jn7mjqei6vTGWfs,9213 +dns/tokenizer.py,sha256=65vVkEeTuml3l2AT-NePE6Gt6ucDQNvSpeIVgMpP6G0,23583 +dns/transaction.py,sha256=UhwD6CLQI51dguuz__dxJS8V91vKAoqHdQDCBErJWxE,22589 +dns/tsig.py,sha256=I-Y-c3WMBX11bVioy5puFly2BhlpptUz82ikahxuh1c,11413 +dns/tsigkeyring.py,sha256=Z0xZemcU3XjZ9HlxBYv2E2PSuIhaFreqLDlD7HcmZDA,2633 +dns/ttl.py,sha256=Y4inc4bvkfKpogZn5i1n-tpg1CAjDJxH4_HvfeVjVsM,2977 +dns/update.py,sha256=y9d6LOO8xrUaH2UrZhy3ssnx8bJEsxqTArw5V8XqBRs,12243 +dns/version.py,sha256=GTecBDFJx8cKnGiCmxJhSVjk1EkqnuNVt4xailIi3sk,1926 +dns/versioned.py,sha256=3YQj8mzGmZEsjnuVJJjcWopVmDKYLhEj4hEGTLEwzco,11765 +dns/win32util.py,sha256=r9dOvC0Tq288vwPk-ngugsVpwB5YnfW22DaRv6TTPcU,8874 +dns/wire.py,sha256=vy0SolgECbO1UXB4dnhXhDeFKOJT29nQxXvSfKOgA5s,2830 +dns/xfr.py,sha256=aoW0UtvweaE0NV8cmzgMKLYQOa3hwJ3NudRuqjit4SU,13271 +dns/zone.py,sha256=lLAarSxPtpx4Sw29OQ0ifPshD4QauGu8RnPh2dEropA,52086 +dns/zonefile.py,sha256=Y9lm6I7n4eRS35CyclooiQ_jxiOs3pSyH_0uD4FQyag,27926 +dns/zonetypes.py,sha256=HrQNZxZ_gWLWI9dskix71msi9wkYK5pgrBBbPb1T74Y,690 +dnspython-2.7.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +dnspython-2.7.0.dist-info/METADATA,sha256=1lF6uqZwb6RAQFYVtBkLic6pBCe9t14TQWtkK9U5eyY,5763 +dnspython-2.7.0.dist-info/RECORD,, +dnspython-2.7.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87 +dnspython-2.7.0.dist-info/licenses/LICENSE,sha256=w-o_9WVLMpwZ07xfdIGvYjw93tSmFFWFSZ-EOtPXQc0,1526 diff --git a/.venv/lib/python3.9/site-packages/dnspython-2.7.0.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/dnspython-2.7.0.dist-info/WHEEL new file mode 100644 index 0000000..cdd68a4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dnspython-2.7.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.25.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.9/site-packages/dnspython-2.7.0.dist-info/licenses/LICENSE b/.venv/lib/python3.9/site-packages/dnspython-2.7.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..390a726 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dnspython-2.7.0.dist-info/licenses/LICENSE @@ -0,0 +1,35 @@ +ISC License + +Copyright (C) Dnspython Contributors + +Permission to use, copy, modify, and/or distribute this software for +any purpose with or without fee is hereby granted, provided that the +above copyright notice and this permission notice appear in all +copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + + + +Copyright (C) 2001-2017 Nominum, Inc. +Copyright (C) Google Inc. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose with or without fee is hereby granted, +provided that the above copyright notice and this permission notice +appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/.venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/METADATA b/.venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/METADATA new file mode 100644 index 0000000..30d6653 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/METADATA @@ -0,0 +1,672 @@ +Metadata-Version: 2.4 +Name: frozenlist +Version: 1.8.0 +Summary: A list-like structure which implements collections.abc.MutableSequence +Home-page: https://github.com/aio-libs/frozenlist +Maintainer: aiohttp team +Maintainer-email: team@aiohttp.org +License: Apache-2.0 +Project-URL: Chat: Matrix, https://matrix.to/#/#aio-libs:matrix.org +Project-URL: Chat: Matrix Space, https://matrix.to/#/#aio-libs-space:matrix.org +Project-URL: CI: Github Actions, https://github.com/aio-libs/frozenlist/actions +Project-URL: Code of Conduct, https://github.com/aio-libs/.github/blob/master/CODE_OF_CONDUCT.md +Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/frozenlist +Project-URL: Docs: Changelog, https://github.com/aio-libs/frozenlist/blob/master/CHANGES.rst#changelog +Project-URL: Docs: RTD, https://frozenlist.aio-libs.org +Project-URL: GitHub: issues, https://github.com/aio-libs/frozenlist/issues +Project-URL: GitHub: repo, https://github.com/aio-libs/frozenlist +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Operating System :: POSIX +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Programming Language :: Cython +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Requires-Python: >=3.9 +Description-Content-Type: text/x-rst +License-File: LICENSE +Dynamic: license-file + +frozenlist +========== + +.. image:: https://github.com/aio-libs/frozenlist/workflows/CI/badge.svg + :target: https://github.com/aio-libs/frozenlist/actions + :alt: GitHub status for master branch + +.. image:: https://codecov.io/gh/aio-libs/frozenlist/branch/master/graph/badge.svg?flag=pytest + :target: https://codecov.io/gh/aio-libs/frozenlist?flags[]=pytest + :alt: codecov.io status for master branch + +.. image:: https://img.shields.io/pypi/v/frozenlist.svg?logo=Python&logoColor=white + :target: https://pypi.org/project/frozenlist + :alt: frozenlist @ PyPI + +.. image:: https://readthedocs.org/projects/frozenlist/badge/?version=latest + :target: https://frozenlist.aio-libs.org + :alt: Read The Docs build status badge + +.. image:: https://img.shields.io/matrix/aio-libs:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs:matrix.org + :alt: Matrix Room — #aio-libs:matrix.org + +.. image:: https://img.shields.io/matrix/aio-libs-space:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs-space%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs-space:matrix.org + :alt: Matrix Space — #aio-libs-space:matrix.org + +Introduction +------------ + +``frozenlist.FrozenList`` is a list-like structure which implements +``collections.abc.MutableSequence``. The list is *mutable* until ``FrozenList.freeze`` +is called, after which list modifications raise ``RuntimeError``: + + +>>> from frozenlist import FrozenList +>>> fl = FrozenList([17, 42]) +>>> fl.append('spam') +>>> fl.append('Vikings') +>>> fl + +>>> fl.freeze() +>>> fl + +>>> fl.frozen +True +>>> fl.append("Monty") +Traceback (most recent call last): + File "", line 1, in + File "frozenlist/_frozenlist.pyx", line 97, in frozenlist._frozenlist.FrozenList.append + self._check_frozen() + File "frozenlist/_frozenlist.pyx", line 19, in frozenlist._frozenlist.FrozenList._check_frozen + raise RuntimeError("Cannot modify frozen list.") +RuntimeError: Cannot modify frozen list. + + +FrozenList is also hashable, but only when frozen. Otherwise it also throws a RuntimeError: + + +>>> fl = FrozenList([17, 42, 'spam']) +>>> hash(fl) +Traceback (most recent call last): + File "", line 1, in + File "frozenlist/_frozenlist.pyx", line 111, in frozenlist._frozenlist.FrozenList.__hash__ + raise RuntimeError("Cannot hash unfrozen list.") +RuntimeError: Cannot hash unfrozen list. +>>> fl.freeze() +>>> hash(fl) +3713081631934410656 +>>> dictionary = {fl: 'Vikings'} # frozen fl can be a dict key +>>> dictionary +{: 'Vikings'} + + +Installation +------------ + +:: + + $ pip install frozenlist + + +Documentation +------------- + +https://frozenlist.aio-libs.org + +Communication channels +---------------------- + +We have a *Matrix Space* `#aio-libs-space:matrix.org +`_ which is +also accessible via Gitter. + +License +------- + +``frozenlist`` is offered under the Apache 2 license. + +Source code +----------- + +The project is hosted on GitHub_ + +Please file an issue in the `bug tracker +`_ if you have found a bug +or have some suggestions to improve the library. + +.. _GitHub: https://github.com/aio-libs/frozenlist + +========= +Changelog +========= + +.. + You should *NOT* be adding new change log entries to this file, this + file is managed by towncrier. You *may* edit previous change logs to + fix problems like typo corrections or such. + To add a new change log entry, please see + https://pip.pypa.io/en/latest/development/contributing/#news-entries + we named the news folder "changes". + + WARNING: Don't drop the next directive! + +.. towncrier release notes start + +v1.8.0 +====== + +*(2025-10-05)* + + +Contributor-facing changes +-------------------------- + +- The ``reusable-cibuildwheel.yml`` workflow has been refactored to + be more generic and ``ci-cd.yml`` now holds all the configuration + toggles -- by `@webknjaz `__. + + *Related issues and pull requests on GitHub:* + `#668 `__. + +- When building wheels, the source distribution is now passed directly + to the ``cibuildwheel`` invocation -- by `@webknjaz `__. + + *Related issues and pull requests on GitHub:* + `#669 `__. + +- Builds and tests have been added to + ``ci-cd.yml`` for arm64 Windows wheels -- by `@finnagin `__. + + *Related issues and pull requests on GitHub:* + `#677 `__. + +- Started building wheels for CPython 3.14 -- by `@kumaraditya303 `__. + + *Related issues and pull requests on GitHub:* + `#681 `__, `#682 `__. + +- Removed ``--config-settings=pure-python=false`` from ``requirements/dev.txt``. + Developers on CPython still get accelerated builds by default. To explicitly build + a pure Python wheel, use ``pip install -e . --config-settings=pure-python=true`` + -- by `@bdraco `__. + + *Related issues and pull requests on GitHub:* + `#687 `__. + + +---- + + +v1.7.0 +====== + +*(2025-06-09)* + + +Features +-------- + +- Added deepcopy support to FrozenList -- by `@bdraco `__. + + *Related issues and pull requests on GitHub:* + `#659 `__. + + +Packaging updates and notes for downstreams +------------------------------------------- + +- Fixed an issue where ``frozenlist`` binary wheels would be built with debugging symbols and line tracing enabled, which significantly impacted performance. Line tracing is now disabled by default and can only be enabled explicitly -- by `@bdraco `__. + + This change ensures that production builds are optimized for performance. Developers who need line tracing for debugging purposes can still enable it by: + + 1. Setting the ``FROZENLIST_CYTHON_TRACING`` environment variable + 2. Using the ``--config-setting=with-cython-tracing=true`` option with pip + + *Related issues and pull requests on GitHub:* + `#660 `__. + +- Enabled ``PIP_CONSTRAINT`` environment variable in the build configuration to ensure the pinned Cython version from ``requirements/cython.txt`` is used during wheel builds. + + *Related issues and pull requests on GitHub:* + `#661 `__. + + +---- + + +v1.6.2 +====== + +*(2025-06-03)* + + +No significant changes. + + +---- + + +v1.6.1 +====== + +*(2025-06-02)* + + +Bug fixes +--------- + +- Correctly use ``cimport`` for including ``PyBool_FromLong`` -- by `@lysnikolaou `__. + + *Related issues and pull requests on GitHub:* + `#653 `__. + + +Packaging updates and notes for downstreams +------------------------------------------- + +- Exclude ``_frozenlist.cpp`` from bdists/wheels -- by `@musicinmybrain `__. + + *Related issues and pull requests on GitHub:* + `#649 `__. + +- Updated to use Cython 3.1 universally across the build path -- by `@lysnikolaou `__. + + *Related issues and pull requests on GitHub:* + `#654 `__. + + +---- + + +v1.6.0 +====== + +*(2025-04-17)* + + +Bug fixes +--------- + +- Stopped implicitly allowing the use of Cython pre-release versions when + building the distribution package -- by `@ajsanchezsanz `__ and + `@markgreene74 `__. + + *Related commits on GitHub:* + `41591f2 `__. + + +Features +-------- + +- Implemented support for the free-threaded build of CPython 3.13 -- by `@lysnikolaou `__. + + *Related issues and pull requests on GitHub:* + `#618 `__. + +- Started building armv7l wheels -- by `@bdraco `__. + + *Related issues and pull requests on GitHub:* + `#642 `__. + + +Packaging updates and notes for downstreams +------------------------------------------- + +- Stopped implicitly allowing the use of Cython pre-release versions when + building the distribution package -- by `@ajsanchezsanz `__ and + `@markgreene74 `__. + + *Related commits on GitHub:* + `41591f2 `__. + +- Started building wheels for the free-threaded build of CPython 3.13 -- by `@lysnikolaou `__. + + *Related issues and pull requests on GitHub:* + `#618 `__. + +- The packaging metadata switched to including an SPDX license identifier introduced in `PEP 639 `__ -- by `@cdce8p `__. + + *Related issues and pull requests on GitHub:* + `#639 `__. + + +Contributor-facing changes +-------------------------- + +- GitHub Actions CI/CD is now configured to manage caching pip-ecosystem + dependencies using `re-actors/cache-python-deps`_ -- an action by + `@webknjaz `__ that takes into account ABI stability and the exact + version of Python runtime. + + .. _`re-actors/cache-python-deps`: + https://github.com/marketplace/actions/cache-python-deps + + *Related issues and pull requests on GitHub:* + `#633 `__. + +- Organized dependencies into test and lint dependencies so that no + unnecessary ones are installed during CI runs -- by `@lysnikolaou `__. + + *Related issues and pull requests on GitHub:* + `#636 `__. + + +---- + + +1.5.0 (2024-10-22) +================== + +Bug fixes +--------- + +- An incorrect signature of the ``__class_getitem__`` class method + has been fixed, adding a missing ``class_item`` argument under + Python 3.8 and older. + + This change also improves the code coverage of this method that + was previously missing -- by `@webknjaz `__. + + + *Related issues and pull requests on GitHub:* + `#567 `__, `#571 `__. + + +Improved documentation +---------------------- + +- Rendered issue, PR, and commit links now lead to + ``frozenlist``'s repo instead of ``yarl``'s repo. + + + *Related issues and pull requests on GitHub:* + `#573 `__. + +- On the ``Contributing docs`` page, + a link to the ``Towncrier philosophy`` has been fixed. + + + *Related issues and pull requests on GitHub:* + `#574 `__. + + +Packaging updates and notes for downstreams +------------------------------------------- + +- A name of a temporary building directory now reflects + that it's related to ``frozenlist``, not ``yarl``. + + + *Related issues and pull requests on GitHub:* + `#573 `__. + +- Declared Python 3.13 supported officially in the distribution package metadata. + + + *Related issues and pull requests on GitHub:* + `#595 `__. + + +---- + + +1.4.1 (2023-12-15) +================== + +Packaging updates and notes for downstreams +------------------------------------------- + +- Declared Python 3.12 and PyPy 3.8-3.10 supported officially + in the distribution package metadata. + + + *Related issues and pull requests on GitHub:* + `#553 `__. + +- Replaced the packaging is replaced from an old-fashioned ``setup.py`` to an + in-tree `PEP 517 `__ build backend -- by `@webknjaz `__. + + Whenever the end-users or downstream packagers need to build ``frozenlist`` + from source (a Git checkout or an sdist), they may pass a ``config_settings`` + flag ``pure-python``. If this flag is not set, a C-extension will be built + and included into the distribution. + + Here is how this can be done with ``pip``: + + .. code-block:: console + + $ python3 -m pip install . --config-settings=pure-python= + + This will also work with ``-e | --editable``. + + The same can be achieved via ``pypa/build``: + + .. code-block:: console + + $ python3 -m build --config-setting=pure-python= + + Adding ``-w | --wheel`` can force ``pypa/build`` produce a wheel from source + directly, as opposed to building an ``sdist`` and then building from it. + + + *Related issues and pull requests on GitHub:* + `#560 `__. + + +Contributor-facing changes +-------------------------- + +- It is now possible to request line tracing in Cython builds using the + ``with-cython-tracing`` `PEP 517 `__ config setting + -- `@webknjaz `__. + + This can be used in CI and development environment to measure coverage + on Cython modules, but is not normally useful to the end-users or + downstream packagers. + + Here's a usage example: + + .. code-block:: console + + $ python3 -Im pip install . --config-settings=with-cython-tracing=true + + For editable installs, this setting is on by default. Otherwise, it's + off unless requested explicitly. + + The following produces C-files required for the Cython coverage + plugin to map the measurements back to the PYX-files: + + .. code-block:: console + + $ python -Im pip install -e . + + Alternatively, the ``FROZENLIST_CYTHON_TRACING=1`` environment variable + can be set to do the same as the `PEP 517 `__ config setting. + + + *Related issues and pull requests on GitHub:* + `#560 `__. + +- Coverage collection has been implemented for the Cython modules + -- by `@webknjaz `__. + + It will also be reported to Codecov from any non-release CI jobs. + + + *Related issues and pull requests on GitHub:* + `#561 `__. + +- A step-by-step ``Release Guide`` guide has + been added, describing how to release *frozenlist* -- by `@webknjaz `__. + + This is primarily targeting the maintainers. + + + *Related issues and pull requests on GitHub:* + `#563 `__. + +- Detailed ``Contributing Guidelines`` on + authoring the changelog fragments have been published in the + documentation -- by `@webknjaz `__. + + + *Related issues and pull requests on GitHub:* + `#564 `__. + + +---- + + +1.4.0 (2023-07-12) +================== + +The published source distribution package became buildable +under Python 3.12. + + +---- + + +Bugfixes +-------- + +- Removed an unused ``typing.Tuple`` import + `#411 `_ + + +Deprecations and Removals +------------------------- + +- Dropped Python 3.7 support. + `#413 `_ + + +Misc +---- + +- `#410 `_, `#433 `_ + + +---- + + +1.3.3 (2022-11-08) +================== + +- Fixed CI runs when creating a new release, where new towncrier versions + fail when the current version section is already present. + + +---- + + +1.3.2 (2022-11-08) +================== + +Misc +---- + +- Updated the CI runs to better check for test results and to avoid deprecated syntax. `#327 `_ + + +---- + + +1.3.1 (2022-08-02) +================== + +The published source distribution package became buildable +under Python 3.11. + + +---- + + +1.3.0 (2022-01-18) +================== + +Bugfixes +-------- + +- Do not install C sources with binary distributions. + `#250 `_ + + +Deprecations and Removals +------------------------- + +- Dropped Python 3.6 support + `#274 `_ + + +---- + + +1.2.0 (2021-10-16) +================== + +Features +-------- + +- ``FrozenList`` now supports being used as a generic type as per PEP 585, e.g. ``frozen_int_list: FrozenList[int]`` (requires Python 3.9 or newer). + `#172 `_ +- Added support for Python 3.10. + `#227 `_ +- Started shipping platform-specific wheels with the ``musl`` tag targeting typical Alpine Linux runtimes. + `#227 `_ +- Started shipping platform-specific arm64 wheels for Apple Silicon. + `#227 `_ + + +---- + + +1.1.1 (2020-11-14) +================== + +Bugfixes +-------- + +- Provide x86 Windows wheels. + `#169 `_ + + +---- + + +1.1.0 (2020-10-13) +================== + +Features +-------- + +- Add support for hashing of a frozen list. + `#136 `_ + +- Support Python 3.8 and 3.9. + +- Provide wheels for ``aarch64``, ``i686``, ``ppc64le``, ``s390x`` architectures on + Linux as well as ``x86_64``. + + +---- + + +1.0.0 (2019-11-09) +================== + +Deprecations and Removals +------------------------- + +- Dropped support for Python 3.5; only 3.6, 3.7 and 3.8 are supported going forward. + `#24 `_ diff --git a/.venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/RECORD b/.venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/RECORD new file mode 100644 index 0000000..89e706c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/RECORD @@ -0,0 +1,12 @@ +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/frozenlist/__init__.cpython-39.pyc,, +frozenlist-1.8.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +frozenlist-1.8.0.dist-info/METADATA,sha256=lGwi3J9-LHby1pjwCkMG2-Sagg4rPxoOIv5ShsvsNfE,20333 +frozenlist-1.8.0.dist-info/RECORD,, +frozenlist-1.8.0.dist-info/WHEEL,sha256=bDWaFWigpG5bEpqw9IoRiyYs8MvmSFh0OhUAOoi_-KA,134 +frozenlist-1.8.0.dist-info/licenses/LICENSE,sha256=b9UkPpLdf5jsacesN3co50kFcJ_1J6W_mNbQJjwE9bY,11332 +frozenlist-1.8.0.dist-info/top_level.txt,sha256=jivtxsPXA3nK3WBWW2LW5Mtu_GHt8UZA13NeCs2cKuA,11 +frozenlist/__init__.py,sha256=xAIE2u9ncAbjATGIPfno_OJfe8AQ-1h7z_uc73dYsEA,2108 +frozenlist/__init__.pyi,sha256=vMEoES1xGegPtVXoCi9XydEeHsyuIq-KdeXwP5PdsaA,1470 +frozenlist/_frozenlist.cpython-39-darwin.so,sha256=DqAR7Yal5aquSUYBHvyVKSNrfAlLSTkpuXMSIjAg57A,144176 +frozenlist/_frozenlist.pyx,sha256=t-aGjuEiVt_MZPBJ0RnraavVmPBK6arz3i48ZvXuYsU,3708 +frozenlist/py.typed,sha256=sow9soTwP9T_gEAQSVh7Gb8855h04Nwmhs2We-JRgZM,7 diff --git a/.venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/WHEEL new file mode 100644 index 0000000..d7eb6c2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: false +Tag: cp39-cp39-macosx_11_0_arm64 +Generator: delocate 0.13.0 + diff --git a/.venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/licenses/LICENSE b/.venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..7082a2d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/licenses/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2013-2019 Nikolay Kim and Andrew Svetlov + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/top_level.txt new file mode 100644 index 0000000..52f13fc --- /dev/null +++ b/.venv/lib/python3.9/site-packages/frozenlist-1.8.0.dist-info/top_level.txt @@ -0,0 +1 @@ +frozenlist diff --git a/.venv/lib/python3.9/site-packages/frozenlist/__init__.py b/.venv/lib/python3.9/site-packages/frozenlist/__init__.py new file mode 100644 index 0000000..41c8595 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/frozenlist/__init__.py @@ -0,0 +1,86 @@ +import os +import types +from collections.abc import MutableSequence +from functools import total_ordering + +__version__ = "1.8.0" + +__all__ = ("FrozenList", "PyFrozenList") # type: Tuple[str, ...] + + +NO_EXTENSIONS = bool(os.environ.get("FROZENLIST_NO_EXTENSIONS")) # type: bool + + +@total_ordering +class FrozenList(MutableSequence): + __slots__ = ("_frozen", "_items") + __class_getitem__ = classmethod(types.GenericAlias) + + def __init__(self, items=None): + self._frozen = False + if items is not None: + items = list(items) + else: + items = [] + self._items = items + + @property + def frozen(self): + return self._frozen + + def freeze(self): + self._frozen = True + + def __getitem__(self, index): + return self._items[index] + + def __setitem__(self, index, value): + if self._frozen: + raise RuntimeError("Cannot modify frozen list.") + self._items[index] = value + + def __delitem__(self, index): + if self._frozen: + raise RuntimeError("Cannot modify frozen list.") + del self._items[index] + + def __len__(self): + return self._items.__len__() + + def __iter__(self): + return self._items.__iter__() + + def __reversed__(self): + return self._items.__reversed__() + + def __eq__(self, other): + return list(self) == other + + def __le__(self, other): + return list(self) <= other + + def insert(self, pos, item): + if self._frozen: + raise RuntimeError("Cannot modify frozen list.") + self._items.insert(pos, item) + + def __repr__(self): + return f"" + + def __hash__(self): + if self._frozen: + return hash(tuple(self)) + else: + raise RuntimeError("Cannot hash unfrozen list.") + + +PyFrozenList = FrozenList + + +if not NO_EXTENSIONS: + try: + from ._frozenlist import FrozenList as CFrozenList # type: ignore + except ImportError: # pragma: no cover + pass + else: + FrozenList = CFrozenList # type: ignore diff --git a/.venv/lib/python3.9/site-packages/frozenlist/__init__.pyi b/.venv/lib/python3.9/site-packages/frozenlist/__init__.pyi new file mode 100644 index 0000000..ae803ef --- /dev/null +++ b/.venv/lib/python3.9/site-packages/frozenlist/__init__.pyi @@ -0,0 +1,47 @@ +from typing import ( + Generic, + Iterable, + Iterator, + List, + MutableSequence, + Optional, + TypeVar, + Union, + overload, +) + +_T = TypeVar("_T") +_Arg = Union[List[_T], Iterable[_T]] + +class FrozenList(MutableSequence[_T], Generic[_T]): + def __init__(self, items: Optional[_Arg[_T]] = None) -> None: ... + @property + def frozen(self) -> bool: ... + def freeze(self) -> None: ... + @overload + def __getitem__(self, i: int) -> _T: ... + @overload + def __getitem__(self, s: slice) -> FrozenList[_T]: ... + @overload + def __setitem__(self, i: int, o: _T) -> None: ... + @overload + def __setitem__(self, s: slice, o: Iterable[_T]) -> None: ... + @overload + def __delitem__(self, i: int) -> None: ... + @overload + def __delitem__(self, i: slice) -> None: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[_T]: ... + def __reversed__(self) -> Iterator[_T]: ... + def __eq__(self, other: object) -> bool: ... + def __le__(self, other: FrozenList[_T]) -> bool: ... + def __ne__(self, other: object) -> bool: ... + def __lt__(self, other: FrozenList[_T]) -> bool: ... + def __ge__(self, other: FrozenList[_T]) -> bool: ... + def __gt__(self, other: FrozenList[_T]) -> bool: ... + def insert(self, pos: int, item: _T) -> None: ... + def __repr__(self) -> str: ... + def __hash__(self) -> int: ... + +# types for C accelerators are the same +CFrozenList = PyFrozenList = FrozenList diff --git a/.venv/lib/python3.9/site-packages/frozenlist/_frozenlist.cpython-39-darwin.so b/.venv/lib/python3.9/site-packages/frozenlist/_frozenlist.cpython-39-darwin.so new file mode 100755 index 0000000000000000000000000000000000000000..b2639e911b3278104f941a2e63f246d1404ef3a1 GIT binary patch literal 144176 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/frozenlist/_frozenlist.pyx b/.venv/lib/python3.9/site-packages/frozenlist/_frozenlist.pyx new file mode 100644 index 0000000..a82d8c8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/frozenlist/_frozenlist.pyx @@ -0,0 +1,148 @@ +# cython: freethreading_compatible = True +# distutils: language = c++ + +from cpython.bool cimport PyBool_FromLong +from libcpp.atomic cimport atomic + +import copy +import types +from collections.abc import MutableSequence + + +cdef class FrozenList: + __class_getitem__ = classmethod(types.GenericAlias) + + cdef atomic[bint] _frozen + cdef list _items + + def __init__(self, items=None): + self._frozen.store(False) + if items is not None: + items = list(items) + else: + items = [] + self._items = items + + @property + def frozen(self): + return PyBool_FromLong(self._frozen.load()) + + cdef object _check_frozen(self): + if self._frozen.load(): + raise RuntimeError("Cannot modify frozen list.") + + cdef inline object _fast_len(self): + return len(self._items) + + def freeze(self): + self._frozen.store(True) + + def __getitem__(self, index): + return self._items[index] + + def __setitem__(self, index, value): + self._check_frozen() + self._items[index] = value + + def __delitem__(self, index): + self._check_frozen() + del self._items[index] + + def __len__(self): + return self._fast_len() + + def __iter__(self): + return self._items.__iter__() + + def __reversed__(self): + return self._items.__reversed__() + + def __richcmp__(self, other, op): + if op == 0: # < + return list(self) < other + if op == 1: # <= + return list(self) <= other + if op == 2: # == + return list(self) == other + if op == 3: # != + return list(self) != other + if op == 4: # > + return list(self) > other + if op == 5: # => + return list(self) >= other + + def insert(self, pos, item): + self._check_frozen() + self._items.insert(pos, item) + + def __contains__(self, item): + return item in self._items + + def __iadd__(self, items): + self._check_frozen() + self._items += list(items) + return self + + def index(self, item): + return self._items.index(item) + + def remove(self, item): + self._check_frozen() + self._items.remove(item) + + def clear(self): + self._check_frozen() + self._items.clear() + + def extend(self, items): + self._check_frozen() + self._items += list(items) + + def reverse(self): + self._check_frozen() + self._items.reverse() + + def pop(self, index=-1): + self._check_frozen() + return self._items.pop(index) + + def append(self, item): + self._check_frozen() + return self._items.append(item) + + def count(self, item): + return self._items.count(item) + + def __repr__(self): + return ''.format(self._frozen.load(), + self._items) + + def __hash__(self): + if self._frozen.load(): + return hash(tuple(self._items)) + else: + raise RuntimeError("Cannot hash unfrozen list.") + + def __deepcopy__(self, memo): + cdef FrozenList new_list + obj_id = id(self) + + # Return existing copy if already processed (circular reference) + if obj_id in memo: + return memo[obj_id] + + # Create new instance and register immediately + new_list = self.__class__([]) + memo[obj_id] = new_list + + # Deep copy items + new_list._items[:] = [copy.deepcopy(item, memo) for item in self._items] + + # Preserve frozen state + if self._frozen.load(): + new_list.freeze() + + return new_list + + +MutableSequence.register(FrozenList) diff --git a/.venv/lib/python3.9/site-packages/frozenlist/py.typed b/.venv/lib/python3.9/site-packages/frozenlist/py.typed new file mode 100644 index 0000000..f5642f7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/frozenlist/py.typed @@ -0,0 +1 @@ +Marker diff --git a/.venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/METADATA b/.venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/METADATA new file mode 100644 index 0000000..8a2f639 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/METADATA @@ -0,0 +1,202 @@ +Metadata-Version: 2.4 +Name: h11 +Version: 0.16.0 +Summary: A pure-Python, bring-your-own-I/O implementation of HTTP/1.1 +Home-page: https://github.com/python-hyper/h11 +Author: Nathaniel J. Smith +Author-email: njs@pobox.com +License: MIT +Classifier: Development Status :: 3 - Alpha +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +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: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: System :: Networking +Requires-Python: >=3.8 +License-File: LICENSE.txt +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: home-page +Dynamic: license +Dynamic: license-file +Dynamic: requires-python +Dynamic: summary + +h11 +=== + +.. image:: https://travis-ci.org/python-hyper/h11.svg?branch=master + :target: https://travis-ci.org/python-hyper/h11 + :alt: Automated test status + +.. image:: https://codecov.io/gh/python-hyper/h11/branch/master/graph/badge.svg + :target: https://codecov.io/gh/python-hyper/h11 + :alt: Test coverage + +.. image:: https://readthedocs.org/projects/h11/badge/?version=latest + :target: http://h11.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +This is a little HTTP/1.1 library written from scratch in Python, +heavily inspired by `hyper-h2 `_. + +It's a "bring-your-own-I/O" library; h11 contains no IO code +whatsoever. This means you can hook h11 up to your favorite network +API, and that could be anything you want: synchronous, threaded, +asynchronous, or your own implementation of `RFC 6214 +`_ -- h11 won't judge you. +(Compare this to the current state of the art, where every time a `new +network API `_ comes along then someone +gets to start over reimplementing the entire HTTP protocol from +scratch.) Cory Benfield made an `excellent blog post describing the +benefits of this approach +`_, or if you like video +then here's his `PyCon 2016 talk on the same theme +`_. + +This also means that h11 is not immediately useful out of the box: +it's a toolkit for building programs that speak HTTP, not something +that could directly replace ``requests`` or ``twisted.web`` or +whatever. But h11 makes it much easier to implement something like +``requests`` or ``twisted.web``. + +At a high level, working with h11 goes like this: + +1) First, create an ``h11.Connection`` object to track the state of a + single HTTP/1.1 connection. + +2) When you read data off the network, pass it to + ``conn.receive_data(...)``; you'll get back a list of objects + representing high-level HTTP "events". + +3) When you want to send a high-level HTTP event, create the + corresponding "event" object and pass it to ``conn.send(...)``; + this will give you back some bytes that you can then push out + through the network. + +For example, a client might instantiate and then send a +``h11.Request`` object, then zero or more ``h11.Data`` objects for the +request body (e.g., if this is a POST), and then a +``h11.EndOfMessage`` to indicate the end of the message. Then the +server would then send back a ``h11.Response``, some ``h11.Data``, and +its own ``h11.EndOfMessage``. If either side violates the protocol, +you'll get a ``h11.ProtocolError`` exception. + +h11 is suitable for implementing both servers and clients, and has a +pleasantly symmetric API: the events you send as a client are exactly +the ones that you receive as a server and vice-versa. + +`Here's an example of a tiny HTTP client +`_ + +It also has `a fine manual `_. + +FAQ +--- + +*Whyyyyy?* + +I wanted to play with HTTP in `Curio +`__ and `Trio +`__, which at the time didn't have any +HTTP libraries. So I thought, no big deal, Python has, like, a dozen +different implementations of HTTP, surely I can find one that's +reusable. I didn't find one, but I did find Cory's call-to-arms +blog-post. So I figured, well, fine, if I have to implement HTTP from +scratch, at least I can make sure no-one *else* has to ever again. + +*Should I use it?* + +Maybe. You should be aware that it's a very young project. But, it's +feature complete and has an exhaustive test-suite and complete docs, +so the next step is for people to try using it and see how it goes +:-). If you do then please let us know -- if nothing else we'll want +to talk to you before making any incompatible changes! + +*What are the features/limitations?* + +Roughly speaking, it's trying to be a robust, complete, and non-hacky +implementation of the first "chapter" of the HTTP/1.1 spec: `RFC 7230: +HTTP/1.1 Message Syntax and Routing +`_. That is, it mostly focuses on +implementing HTTP at the level of taking bytes on and off the wire, +and the headers related to that, and tries to be anal about spec +conformance. It doesn't know about higher-level concerns like URL +routing, conditional GETs, cross-origin cookie policies, or content +negotiation. But it does know how to take care of framing, +cross-version differences in keep-alive handling, and the "obsolete +line folding" rule, so you can focus your energies on the hard / +interesting parts for your application, and it tries to support the +full specification in the sense that any useful HTTP/1.1 conformant +application should be able to use h11. + +It's pure Python, and has no dependencies outside of the standard +library. + +It has a test suite with 100.0% coverage for both statements and +branches. + +Currently it supports Python 3 (testing on 3.8-3.12) and PyPy 3. +The last Python 2-compatible version was h11 0.11.x. +(Originally it had a Cython wrapper for `http-parser +`_ and a beautiful nested state +machine implemented with ``yield from`` to postprocess the output. But +I had to take these out -- the new *parser* needs fewer lines-of-code +than the old *parser wrapper*, is written in pure Python, uses no +exotic language syntax, and has more features. It's sad, really; that +old state machine was really slick. I just need a few sentences here +to mourn that.) + +I don't know how fast it is. I haven't benchmarked or profiled it yet, +so it's probably got a few pointless hot spots, and I've been trying +to err on the side of simplicity and robustness instead of +micro-optimization. But at the architectural level I tried hard to +avoid fundamentally bad decisions, e.g., I believe that all the +parsing algorithms remain linear-time even in the face of pathological +input like slowloris, and there are no byte-by-byte loops. (I also +believe that it maintains bounded memory usage in the face of +arbitrary/pathological input.) + +The whole library is ~800 lines-of-code. You can read and understand +the whole thing in less than an hour. Most of the energy invested in +this so far has been spent on trying to keep things simple by +minimizing special-cases and ad hoc state manipulation; even though it +is now quite small and simple, I'm still annoyed that I haven't +figured out how to make it even smaller and simpler. (Unfortunately, +HTTP does not lend itself to simplicity.) + +The API is ~feature complete and I don't expect the general outlines +to change much, but you can't judge an API's ergonomics until you +actually document and use it, so I'd expect some changes in the +details. + +*How do I try it?* + +.. code-block:: sh + + $ pip install h11 + $ git clone git@github.com:python-hyper/h11 + $ cd h11/examples + $ python basic-client.py + +and go from there. + +*License?* + +MIT + +*Code of conduct?* + +Contributors are requested to follow our `code of conduct +`_ in +all project spaces. diff --git a/.venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/RECORD b/.venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/RECORD new file mode 100644 index 0000000..14aecd2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/RECORD @@ -0,0 +1,29 @@ +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/h11/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/h11/_abnf.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/h11/_connection.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/h11/_events.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/h11/_headers.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/h11/_readers.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/h11/_receivebuffer.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/h11/_state.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/h11/_util.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/h11/_version.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/h11/_writers.cpython-39.pyc,, +h11-0.16.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +h11-0.16.0.dist-info/METADATA,sha256=KPMmCYrAn8unm48YD5YIfIQf4kViFct7hyqcfVzRnWQ,8348 +h11-0.16.0.dist-info/RECORD,, +h11-0.16.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91 +h11-0.16.0.dist-info/licenses/LICENSE.txt,sha256=N9tbuFkm2yikJ6JYZ_ELEjIAOuob5pzLhRE4rbjm82E,1124 +h11-0.16.0.dist-info/top_level.txt,sha256=F7dC4jl3zeh8TGHEPaWJrMbeuoWbS379Gwdi-Yvdcis,4 +h11/__init__.py,sha256=iO1KzkSO42yZ6ffg-VMgbx_ZVTWGUY00nRYEWn-s3kY,1507 +h11/_abnf.py,sha256=ybixr0xsupnkA6GFAyMubuXF6Tc1lb_hF890NgCsfNc,4815 +h11/_connection.py,sha256=k9YRVf6koZqbttBW36xSWaJpWdZwa-xQVU9AHEo9DuI,26863 +h11/_events.py,sha256=I97aXoal1Wu7dkL548BANBUCkOIbe-x5CioYA9IBY14,11792 +h11/_headers.py,sha256=P7D-lBNxHwdLZPLimmYwrPG-9ZkjElvvJZJdZAgSP-4,10412 +h11/_readers.py,sha256=a4RypORUCC3d0q_kxPuBIM7jTD8iLt5X91TH0FsduN4,8590 +h11/_receivebuffer.py,sha256=xrspsdsNgWFxRfQcTXxR8RrdjRXXTK0Io5cQYWpJ1Ws,5252 +h11/_state.py,sha256=_5LG_BGR8FCcFQeBPH-TMHgm_-B-EUcWCnQof_9XjFE,13231 +h11/_util.py,sha256=LWkkjXyJaFlAy6Lt39w73UStklFT5ovcvo0TkY7RYuk,4888 +h11/_version.py,sha256=GVSsbPSPDcOuF6ptfIiXnVJoaEm3ygXbMnqlr_Giahw,686 +h11/_writers.py,sha256=oFKm6PtjeHfbj4RLX7VB7KDc1gIY53gXG3_HR9ltmTA,5081 +h11/py.typed,sha256=sow9soTwP9T_gEAQSVh7Gb8855h04Nwmhs2We-JRgZM,7 diff --git a/.venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/WHEEL new file mode 100644 index 0000000..1eb3c49 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (78.1.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/licenses/LICENSE.txt b/.venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/licenses/LICENSE.txt new file mode 100644 index 0000000..8f080ea --- /dev/null +++ b/.venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/licenses/LICENSE.txt @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2016 Nathaniel J. Smith and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/.venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/top_level.txt new file mode 100644 index 0000000..0d24def --- /dev/null +++ b/.venv/lib/python3.9/site-packages/h11-0.16.0.dist-info/top_level.txt @@ -0,0 +1 @@ +h11 diff --git a/.venv/lib/python3.9/site-packages/h11/__init__.py b/.venv/lib/python3.9/site-packages/h11/__init__.py new file mode 100644 index 0000000..989e92c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/h11/__init__.py @@ -0,0 +1,62 @@ +# A highish-level implementation of the HTTP/1.1 wire protocol (RFC 7230), +# containing no networking code at all, loosely modelled on hyper-h2's generic +# implementation of HTTP/2 (and in particular the h2.connection.H2Connection +# class). There's still a bunch of subtle details you need to get right if you +# want to make this actually useful, because it doesn't implement all the +# semantics to check that what you're asking to write to the wire is sensible, +# but at least it gets you out of dealing with the wire itself. + +from h11._connection import Connection, NEED_DATA, PAUSED +from h11._events import ( + ConnectionClosed, + Data, + EndOfMessage, + Event, + InformationalResponse, + Request, + Response, +) +from h11._state import ( + CLIENT, + CLOSED, + DONE, + ERROR, + IDLE, + MIGHT_SWITCH_PROTOCOL, + MUST_CLOSE, + SEND_BODY, + SEND_RESPONSE, + SERVER, + SWITCHED_PROTOCOL, +) +from h11._util import LocalProtocolError, ProtocolError, RemoteProtocolError +from h11._version import __version__ + +PRODUCT_ID = "python-h11/" + __version__ + + +__all__ = ( + "Connection", + "NEED_DATA", + "PAUSED", + "ConnectionClosed", + "Data", + "EndOfMessage", + "Event", + "InformationalResponse", + "Request", + "Response", + "CLIENT", + "CLOSED", + "DONE", + "ERROR", + "IDLE", + "MUST_CLOSE", + "SEND_BODY", + "SEND_RESPONSE", + "SERVER", + "SWITCHED_PROTOCOL", + "ProtocolError", + "LocalProtocolError", + "RemoteProtocolError", +) diff --git a/.venv/lib/python3.9/site-packages/h11/_abnf.py b/.venv/lib/python3.9/site-packages/h11/_abnf.py new file mode 100644 index 0000000..933587f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/h11/_abnf.py @@ -0,0 +1,132 @@ +# We use native strings for all the re patterns, to take advantage of string +# formatting, and then convert to bytestrings when compiling the final re +# objects. + +# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#whitespace +# OWS = *( SP / HTAB ) +# ; optional whitespace +OWS = r"[ \t]*" + +# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#rule.token.separators +# token = 1*tchar +# +# tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" +# / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" +# / DIGIT / ALPHA +# ; any VCHAR, except delimiters +token = r"[-!#$%&'*+.^_`|~0-9a-zA-Z]+" + +# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#header.fields +# field-name = token +field_name = token + +# The standard says: +# +# field-value = *( field-content / obs-fold ) +# field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] +# field-vchar = VCHAR / obs-text +# obs-fold = CRLF 1*( SP / HTAB ) +# ; obsolete line folding +# ; see Section 3.2.4 +# +# https://tools.ietf.org/html/rfc5234#appendix-B.1 +# +# VCHAR = %x21-7E +# ; visible (printing) characters +# +# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#rule.quoted-string +# obs-text = %x80-FF +# +# However, the standard definition of field-content is WRONG! It disallows +# fields containing a single visible character surrounded by whitespace, +# e.g. "foo a bar". +# +# See: https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4189 +# +# So our definition of field_content attempts to fix it up... +# +# Also, we allow lots of control characters, because apparently people assume +# that they're legal in practice (e.g., google analytics makes cookies with +# \x01 in them!): +# https://github.com/python-hyper/h11/issues/57 +# We still don't allow NUL or whitespace, because those are often treated as +# meta-characters and letting them through can lead to nasty issues like SSRF. +vchar = r"[\x21-\x7e]" +vchar_or_obs_text = r"[^\x00\s]" +field_vchar = vchar_or_obs_text +field_content = r"{field_vchar}+(?:[ \t]+{field_vchar}+)*".format(**globals()) + +# We handle obs-fold at a different level, and our fixed-up field_content +# already grows to swallow the whole value, so ? instead of * +field_value = r"({field_content})?".format(**globals()) + +# header-field = field-name ":" OWS field-value OWS +header_field = ( + r"(?P{field_name})" + r":" + r"{OWS}" + r"(?P{field_value})" + r"{OWS}".format(**globals()) +) + +# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#request.line +# +# request-line = method SP request-target SP HTTP-version CRLF +# method = token +# HTTP-version = HTTP-name "/" DIGIT "." DIGIT +# HTTP-name = %x48.54.54.50 ; "HTTP", case-sensitive +# +# request-target is complicated (see RFC 7230 sec 5.3) -- could be path, full +# URL, host+port (for connect), or even "*", but in any case we are guaranteed +# that it contists of the visible printing characters. +method = token +request_target = r"{vchar}+".format(**globals()) +http_version = r"HTTP/(?P[0-9]\.[0-9])" +request_line = ( + r"(?P{method})" + r" " + r"(?P{request_target})" + r" " + r"{http_version}".format(**globals()) +) + +# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#status.line +# +# status-line = HTTP-version SP status-code SP reason-phrase CRLF +# status-code = 3DIGIT +# reason-phrase = *( HTAB / SP / VCHAR / obs-text ) +status_code = r"[0-9]{3}" +reason_phrase = r"([ \t]|{vchar_or_obs_text})*".format(**globals()) +status_line = ( + r"{http_version}" + r" " + r"(?P{status_code})" + # However, there are apparently a few too many servers out there that just + # leave out the reason phrase: + # https://github.com/scrapy/scrapy/issues/345#issuecomment-281756036 + # https://github.com/seanmonstar/httparse/issues/29 + # so make it optional. ?: is a non-capturing group. + r"(?: (?P{reason_phrase}))?".format(**globals()) +) + +HEXDIG = r"[0-9A-Fa-f]" +# Actually +# +# chunk-size = 1*HEXDIG +# +# but we impose an upper-limit to avoid ridiculosity. len(str(2**64)) == 20 +chunk_size = r"({HEXDIG}){{1,20}}".format(**globals()) +# Actually +# +# chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) +# +# but we aren't parsing the things so we don't really care. +chunk_ext = r";.*" +chunk_header = ( + r"(?P{chunk_size})" + r"(?P{chunk_ext})?" + r"{OWS}\r\n".format( + **globals() + ) # Even though the specification does not allow for extra whitespaces, + # we are lenient with trailing whitespaces because some servers on the wild use it. +) diff --git a/.venv/lib/python3.9/site-packages/h11/_connection.py b/.venv/lib/python3.9/site-packages/h11/_connection.py new file mode 100644 index 0000000..e37d82a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/h11/_connection.py @@ -0,0 +1,659 @@ +# This contains the main Connection class. Everything in h11 revolves around +# this. +from typing import ( + Any, + Callable, + cast, + Dict, + List, + Optional, + overload, + Tuple, + Type, + Union, +) + +from ._events import ( + ConnectionClosed, + Data, + EndOfMessage, + Event, + InformationalResponse, + Request, + Response, +) +from ._headers import get_comma_header, has_expect_100_continue, set_comma_header +from ._readers import READERS, ReadersType +from ._receivebuffer import ReceiveBuffer +from ._state import ( + _SWITCH_CONNECT, + _SWITCH_UPGRADE, + CLIENT, + ConnectionState, + DONE, + ERROR, + MIGHT_SWITCH_PROTOCOL, + SEND_BODY, + SERVER, + SWITCHED_PROTOCOL, +) +from ._util import ( # Import the internal things we need + LocalProtocolError, + RemoteProtocolError, + Sentinel, +) +from ._writers import WRITERS, WritersType + +# Everything in __all__ gets re-exported as part of the h11 public API. +__all__ = ["Connection", "NEED_DATA", "PAUSED"] + + +class NEED_DATA(Sentinel, metaclass=Sentinel): + pass + + +class PAUSED(Sentinel, metaclass=Sentinel): + pass + + +# If we ever have this much buffered without it making a complete parseable +# event, we error out. The only time we really buffer is when reading the +# request/response line + headers together, so this is effectively the limit on +# the size of that. +# +# Some precedents for defaults: +# - node.js: 80 * 1024 +# - tomcat: 8 * 1024 +# - IIS: 16 * 1024 +# - Apache: <8 KiB per line> +DEFAULT_MAX_INCOMPLETE_EVENT_SIZE = 16 * 1024 + + +# RFC 7230's rules for connection lifecycles: +# - If either side says they want to close the connection, then the connection +# must close. +# - HTTP/1.1 defaults to keep-alive unless someone says Connection: close +# - HTTP/1.0 defaults to close unless both sides say Connection: keep-alive +# (and even this is a mess -- e.g. if you're implementing a proxy then +# sending Connection: keep-alive is forbidden). +# +# We simplify life by simply not supporting keep-alive with HTTP/1.0 peers. So +# our rule is: +# - If someone says Connection: close, we will close +# - If someone uses HTTP/1.0, we will close. +def _keep_alive(event: Union[Request, Response]) -> bool: + connection = get_comma_header(event.headers, b"connection") + if b"close" in connection: + return False + if getattr(event, "http_version", b"1.1") < b"1.1": + return False + return True + + +def _body_framing( + request_method: bytes, event: Union[Request, Response] +) -> Tuple[str, Union[Tuple[()], Tuple[int]]]: + # Called when we enter SEND_BODY to figure out framing information for + # this body. + # + # These are the only two events that can trigger a SEND_BODY state: + assert type(event) in (Request, Response) + # Returns one of: + # + # ("content-length", count) + # ("chunked", ()) + # ("http/1.0", ()) + # + # which are (lookup key, *args) for constructing body reader/writer + # objects. + # + # Reference: https://tools.ietf.org/html/rfc7230#section-3.3.3 + # + # Step 1: some responses always have an empty body, regardless of what the + # headers say. + if type(event) is Response: + if ( + event.status_code in (204, 304) + or request_method == b"HEAD" + or (request_method == b"CONNECT" and 200 <= event.status_code < 300) + ): + return ("content-length", (0,)) + # Section 3.3.3 also lists another case -- responses with status_code + # < 200. For us these are InformationalResponses, not Responses, so + # they can't get into this function in the first place. + assert event.status_code >= 200 + + # Step 2: check for Transfer-Encoding (T-E beats C-L): + transfer_encodings = get_comma_header(event.headers, b"transfer-encoding") + if transfer_encodings: + assert transfer_encodings == [b"chunked"] + return ("chunked", ()) + + # Step 3: check for Content-Length + content_lengths = get_comma_header(event.headers, b"content-length") + if content_lengths: + return ("content-length", (int(content_lengths[0]),)) + + # Step 4: no applicable headers; fallback/default depends on type + if type(event) is Request: + return ("content-length", (0,)) + else: + return ("http/1.0", ()) + + +################################################################ +# +# The main Connection class +# +################################################################ + + +class Connection: + """An object encapsulating the state of an HTTP connection. + + Args: + our_role: If you're implementing a client, pass :data:`h11.CLIENT`. If + you're implementing a server, pass :data:`h11.SERVER`. + + max_incomplete_event_size (int): + The maximum number of bytes we're willing to buffer of an + incomplete event. In practice this mostly sets a limit on the + maximum size of the request/response line + headers. If this is + exceeded, then :meth:`next_event` will raise + :exc:`RemoteProtocolError`. + + """ + + def __init__( + self, + our_role: Type[Sentinel], + max_incomplete_event_size: int = DEFAULT_MAX_INCOMPLETE_EVENT_SIZE, + ) -> None: + self._max_incomplete_event_size = max_incomplete_event_size + # State and role tracking + if our_role not in (CLIENT, SERVER): + raise ValueError(f"expected CLIENT or SERVER, not {our_role!r}") + self.our_role = our_role + self.their_role: Type[Sentinel] + if our_role is CLIENT: + self.their_role = SERVER + else: + self.their_role = CLIENT + self._cstate = ConnectionState() + + # Callables for converting data->events or vice-versa given the + # current state + self._writer = self._get_io_object(self.our_role, None, WRITERS) + self._reader = self._get_io_object(self.their_role, None, READERS) + + # Holds any unprocessed received data + self._receive_buffer = ReceiveBuffer() + # If this is true, then it indicates that the incoming connection was + # closed *after* the end of whatever's in self._receive_buffer: + self._receive_buffer_closed = False + + # Extra bits of state that don't fit into the state machine. + # + # These two are only used to interpret framing headers for figuring + # out how to read/write response bodies. their_http_version is also + # made available as a convenient public API. + self.their_http_version: Optional[bytes] = None + self._request_method: Optional[bytes] = None + # This is pure flow-control and doesn't at all affect the set of legal + # transitions, so no need to bother ConnectionState with it: + self.client_is_waiting_for_100_continue = False + + @property + def states(self) -> Dict[Type[Sentinel], Type[Sentinel]]: + """A dictionary like:: + + {CLIENT: , SERVER: } + + See :ref:`state-machine` for details. + + """ + return dict(self._cstate.states) + + @property + def our_state(self) -> Type[Sentinel]: + """The current state of whichever role we are playing. See + :ref:`state-machine` for details. + """ + return self._cstate.states[self.our_role] + + @property + def their_state(self) -> Type[Sentinel]: + """The current state of whichever role we are NOT playing. See + :ref:`state-machine` for details. + """ + return self._cstate.states[self.their_role] + + @property + def they_are_waiting_for_100_continue(self) -> bool: + return self.their_role is CLIENT and self.client_is_waiting_for_100_continue + + def start_next_cycle(self) -> None: + """Attempt to reset our connection state for a new request/response + cycle. + + If both client and server are in :data:`DONE` state, then resets them + both to :data:`IDLE` state in preparation for a new request/response + cycle on this same connection. Otherwise, raises a + :exc:`LocalProtocolError`. + + See :ref:`keepalive-and-pipelining`. + + """ + old_states = dict(self._cstate.states) + self._cstate.start_next_cycle() + self._request_method = None + # self.their_http_version gets left alone, since it presumably lasts + # beyond a single request/response cycle + assert not self.client_is_waiting_for_100_continue + self._respond_to_state_changes(old_states) + + def _process_error(self, role: Type[Sentinel]) -> None: + old_states = dict(self._cstate.states) + self._cstate.process_error(role) + self._respond_to_state_changes(old_states) + + def _server_switch_event(self, event: Event) -> Optional[Type[Sentinel]]: + if type(event) is InformationalResponse and event.status_code == 101: + return _SWITCH_UPGRADE + if type(event) is Response: + if ( + _SWITCH_CONNECT in self._cstate.pending_switch_proposals + and 200 <= event.status_code < 300 + ): + return _SWITCH_CONNECT + return None + + # All events go through here + def _process_event(self, role: Type[Sentinel], event: Event) -> None: + # First, pass the event through the state machine to make sure it + # succeeds. + old_states = dict(self._cstate.states) + if role is CLIENT and type(event) is Request: + if event.method == b"CONNECT": + self._cstate.process_client_switch_proposal(_SWITCH_CONNECT) + if get_comma_header(event.headers, b"upgrade"): + self._cstate.process_client_switch_proposal(_SWITCH_UPGRADE) + server_switch_event = None + if role is SERVER: + server_switch_event = self._server_switch_event(event) + self._cstate.process_event(role, type(event), server_switch_event) + + # Then perform the updates triggered by it. + + if type(event) is Request: + self._request_method = event.method + + if role is self.their_role and type(event) in ( + Request, + Response, + InformationalResponse, + ): + event = cast(Union[Request, Response, InformationalResponse], event) + self.their_http_version = event.http_version + + # Keep alive handling + # + # RFC 7230 doesn't really say what one should do if Connection: close + # shows up on a 1xx InformationalResponse. I think the idea is that + # this is not supposed to happen. In any case, if it does happen, we + # ignore it. + if type(event) in (Request, Response) and not _keep_alive( + cast(Union[Request, Response], event) + ): + self._cstate.process_keep_alive_disabled() + + # 100-continue + if type(event) is Request and has_expect_100_continue(event): + self.client_is_waiting_for_100_continue = True + if type(event) in (InformationalResponse, Response): + self.client_is_waiting_for_100_continue = False + if role is CLIENT and type(event) in (Data, EndOfMessage): + self.client_is_waiting_for_100_continue = False + + self._respond_to_state_changes(old_states, event) + + def _get_io_object( + self, + role: Type[Sentinel], + event: Optional[Event], + io_dict: Union[ReadersType, WritersType], + ) -> Optional[Callable[..., Any]]: + # event may be None; it's only used when entering SEND_BODY + state = self._cstate.states[role] + if state is SEND_BODY: + # Special case: the io_dict has a dict of reader/writer factories + # that depend on the request/response framing. + framing_type, args = _body_framing( + cast(bytes, self._request_method), cast(Union[Request, Response], event) + ) + return io_dict[SEND_BODY][framing_type](*args) # type: ignore[index] + else: + # General case: the io_dict just has the appropriate reader/writer + # for this state + return io_dict.get((role, state)) # type: ignore[return-value] + + # This must be called after any action that might have caused + # self._cstate.states to change. + def _respond_to_state_changes( + self, + old_states: Dict[Type[Sentinel], Type[Sentinel]], + event: Optional[Event] = None, + ) -> None: + # Update reader/writer + if self.our_state != old_states[self.our_role]: + self._writer = self._get_io_object(self.our_role, event, WRITERS) + if self.their_state != old_states[self.their_role]: + self._reader = self._get_io_object(self.their_role, event, READERS) + + @property + def trailing_data(self) -> Tuple[bytes, bool]: + """Data that has been received, but not yet processed, represented as + a tuple with two elements, where the first is a byte-string containing + the unprocessed data itself, and the second is a bool that is True if + the receive connection was closed. + + See :ref:`switching-protocols` for discussion of why you'd want this. + """ + return (bytes(self._receive_buffer), self._receive_buffer_closed) + + def receive_data(self, data: bytes) -> None: + """Add data to our internal receive buffer. + + This does not actually do any processing on the data, just stores + it. To trigger processing, you have to call :meth:`next_event`. + + Args: + data (:term:`bytes-like object`): + The new data that was just received. + + Special case: If *data* is an empty byte-string like ``b""``, + then this indicates that the remote side has closed the + connection (end of file). Normally this is convenient, because + standard Python APIs like :meth:`file.read` or + :meth:`socket.recv` use ``b""`` to indicate end-of-file, while + other failures to read are indicated using other mechanisms + like raising :exc:`TimeoutError`. When using such an API you + can just blindly pass through whatever you get from ``read`` + to :meth:`receive_data`, and everything will work. + + But, if you have an API where reading an empty string is a + valid non-EOF condition, then you need to be aware of this and + make sure to check for such strings and avoid passing them to + :meth:`receive_data`. + + Returns: + Nothing, but after calling this you should call :meth:`next_event` + to parse the newly received data. + + Raises: + RuntimeError: + Raised if you pass an empty *data*, indicating EOF, and then + pass a non-empty *data*, indicating more data that somehow + arrived after the EOF. + + (Calling ``receive_data(b"")`` multiple times is fine, + and equivalent to calling it once.) + + """ + if data: + if self._receive_buffer_closed: + raise RuntimeError("received close, then received more data?") + self._receive_buffer += data + else: + self._receive_buffer_closed = True + + def _extract_next_receive_event( + self, + ) -> Union[Event, Type[NEED_DATA], Type[PAUSED]]: + state = self.their_state + # We don't pause immediately when they enter DONE, because even in + # DONE state we can still process a ConnectionClosed() event. But + # if we have data in our buffer, then we definitely aren't getting + # a ConnectionClosed() immediately and we need to pause. + if state is DONE and self._receive_buffer: + return PAUSED + if state is MIGHT_SWITCH_PROTOCOL or state is SWITCHED_PROTOCOL: + return PAUSED + assert self._reader is not None + event = self._reader(self._receive_buffer) + if event is None: + if not self._receive_buffer and self._receive_buffer_closed: + # In some unusual cases (basically just HTTP/1.0 bodies), EOF + # triggers an actual protocol event; in that case, we want to + # return that event, and then the state will change and we'll + # get called again to generate the actual ConnectionClosed(). + if hasattr(self._reader, "read_eof"): + event = self._reader.read_eof() + else: + event = ConnectionClosed() + if event is None: + event = NEED_DATA + return event # type: ignore[no-any-return] + + def next_event(self) -> Union[Event, Type[NEED_DATA], Type[PAUSED]]: + """Parse the next event out of our receive buffer, update our internal + state, and return it. + + This is a mutating operation -- think of it like calling :func:`next` + on an iterator. + + Returns: + : One of three things: + + 1) An event object -- see :ref:`events`. + + 2) The special constant :data:`NEED_DATA`, which indicates that + you need to read more data from your socket and pass it to + :meth:`receive_data` before this method will be able to return + any more events. + + 3) The special constant :data:`PAUSED`, which indicates that we + are not in a state where we can process incoming data (usually + because the peer has finished their part of the current + request/response cycle, and you have not yet called + :meth:`start_next_cycle`). See :ref:`flow-control` for details. + + Raises: + RemoteProtocolError: + The peer has misbehaved. You should close the connection + (possibly after sending some kind of 4xx response). + + Once this method returns :class:`ConnectionClosed` once, then all + subsequent calls will also return :class:`ConnectionClosed`. + + If this method raises any exception besides :exc:`RemoteProtocolError` + then that's a bug -- if it happens please file a bug report! + + If this method raises any exception then it also sets + :attr:`Connection.their_state` to :data:`ERROR` -- see + :ref:`error-handling` for discussion. + + """ + + if self.their_state is ERROR: + raise RemoteProtocolError("Can't receive data when peer state is ERROR") + try: + event = self._extract_next_receive_event() + if event not in [NEED_DATA, PAUSED]: + self._process_event(self.their_role, cast(Event, event)) + if event is NEED_DATA: + if len(self._receive_buffer) > self._max_incomplete_event_size: + # 431 is "Request header fields too large" which is pretty + # much the only situation where we can get here + raise RemoteProtocolError( + "Receive buffer too long", error_status_hint=431 + ) + if self._receive_buffer_closed: + # We're still trying to complete some event, but that's + # never going to happen because no more data is coming + raise RemoteProtocolError("peer unexpectedly closed connection") + return event + except BaseException as exc: + self._process_error(self.their_role) + if isinstance(exc, LocalProtocolError): + exc._reraise_as_remote_protocol_error() + else: + raise + + @overload + def send(self, event: ConnectionClosed) -> None: + ... + + @overload + def send( + self, event: Union[Request, InformationalResponse, Response, Data, EndOfMessage] + ) -> bytes: + ... + + @overload + def send(self, event: Event) -> Optional[bytes]: + ... + + def send(self, event: Event) -> Optional[bytes]: + """Convert a high-level event into bytes that can be sent to the peer, + while updating our internal state machine. + + Args: + event: The :ref:`event ` to send. + + Returns: + If ``type(event) is ConnectionClosed``, then returns + ``None``. Otherwise, returns a :term:`bytes-like object`. + + Raises: + LocalProtocolError: + Sending this event at this time would violate our + understanding of the HTTP/1.1 protocol. + + If this method raises any exception then it also sets + :attr:`Connection.our_state` to :data:`ERROR` -- see + :ref:`error-handling` for discussion. + + """ + data_list = self.send_with_data_passthrough(event) + if data_list is None: + return None + else: + return b"".join(data_list) + + def send_with_data_passthrough(self, event: Event) -> Optional[List[bytes]]: + """Identical to :meth:`send`, except that in situations where + :meth:`send` returns a single :term:`bytes-like object`, this instead + returns a list of them -- and when sending a :class:`Data` event, this + list is guaranteed to contain the exact object you passed in as + :attr:`Data.data`. See :ref:`sendfile` for discussion. + + """ + if self.our_state is ERROR: + raise LocalProtocolError("Can't send data when our state is ERROR") + try: + if type(event) is Response: + event = self._clean_up_response_headers_for_sending(event) + # We want to call _process_event before calling the writer, + # because if someone tries to do something invalid then this will + # give a sensible error message, while our writers all just assume + # they will only receive valid events. But, _process_event might + # change self._writer. So we have to do a little dance: + writer = self._writer + self._process_event(self.our_role, event) + if type(event) is ConnectionClosed: + return None + else: + # In any situation where writer is None, process_event should + # have raised ProtocolError + assert writer is not None + data_list: List[bytes] = [] + writer(event, data_list.append) + return data_list + except: + self._process_error(self.our_role) + raise + + def send_failed(self) -> None: + """Notify the state machine that we failed to send the data it gave + us. + + This causes :attr:`Connection.our_state` to immediately become + :data:`ERROR` -- see :ref:`error-handling` for discussion. + + """ + self._process_error(self.our_role) + + # When sending a Response, we take responsibility for a few things: + # + # - Sometimes you MUST set Connection: close. We take care of those + # times. (You can also set it yourself if you want, and if you do then + # we'll respect that and close the connection at the right time. But you + # don't have to worry about that unless you want to.) + # + # - The user has to set Content-Length if they want it. Otherwise, for + # responses that have bodies (e.g. not HEAD), then we will automatically + # select the right mechanism for streaming a body of unknown length, + # which depends on depending on the peer's HTTP version. + # + # This function's *only* responsibility is making sure headers are set up + # right -- everything downstream just looks at the headers. There are no + # side channels. + def _clean_up_response_headers_for_sending(self, response: Response) -> Response: + assert type(response) is Response + + headers = response.headers + need_close = False + + # HEAD requests need some special handling: they always act like they + # have Content-Length: 0, and that's how _body_framing treats + # them. But their headers are supposed to match what we would send if + # the request was a GET. (Technically there is one deviation allowed: + # we're allowed to leave out the framing headers -- see + # https://tools.ietf.org/html/rfc7231#section-4.3.2 . But it's just as + # easy to get them right.) + method_for_choosing_headers = cast(bytes, self._request_method) + if method_for_choosing_headers == b"HEAD": + method_for_choosing_headers = b"GET" + framing_type, _ = _body_framing(method_for_choosing_headers, response) + if framing_type in ("chunked", "http/1.0"): + # This response has a body of unknown length. + # If our peer is HTTP/1.1, we use Transfer-Encoding: chunked + # If our peer is HTTP/1.0, we use no framing headers, and close the + # connection afterwards. + # + # Make sure to clear Content-Length (in principle user could have + # set both and then we ignored Content-Length b/c + # Transfer-Encoding overwrote it -- this would be naughty of them, + # but the HTTP spec says that if our peer does this then we have + # to fix it instead of erroring out, so we'll accord the user the + # same respect). + headers = set_comma_header(headers, b"content-length", []) + if self.their_http_version is None or self.their_http_version < b"1.1": + # Either we never got a valid request and are sending back an + # error (their_http_version is None), so we assume the worst; + # or else we did get a valid HTTP/1.0 request, so we know that + # they don't understand chunked encoding. + headers = set_comma_header(headers, b"transfer-encoding", []) + # This is actually redundant ATM, since currently we + # unconditionally disable keep-alive when talking to HTTP/1.0 + # peers. But let's be defensive just in case we add + # Connection: keep-alive support later: + if self._request_method != b"HEAD": + need_close = True + else: + headers = set_comma_header(headers, b"transfer-encoding", [b"chunked"]) + + if not self._cstate.keep_alive or need_close: + # Make sure Connection: close is set + connection = set(get_comma_header(headers, b"connection")) + connection.discard(b"keep-alive") + connection.add(b"close") + headers = set_comma_header(headers, b"connection", sorted(connection)) + + return Response( + headers=headers, + status_code=response.status_code, + http_version=response.http_version, + reason=response.reason, + ) diff --git a/.venv/lib/python3.9/site-packages/h11/_events.py b/.venv/lib/python3.9/site-packages/h11/_events.py new file mode 100644 index 0000000..ca1c3ad --- /dev/null +++ b/.venv/lib/python3.9/site-packages/h11/_events.py @@ -0,0 +1,369 @@ +# High level events that make up HTTP/1.1 conversations. Loosely inspired by +# the corresponding events in hyper-h2: +# +# http://python-hyper.org/h2/en/stable/api.html#events +# +# Don't subclass these. Stuff will break. + +import re +from abc import ABC +from dataclasses import dataclass +from typing import List, Tuple, Union + +from ._abnf import method, request_target +from ._headers import Headers, normalize_and_validate +from ._util import bytesify, LocalProtocolError, validate + +# Everything in __all__ gets re-exported as part of the h11 public API. +__all__ = [ + "Event", + "Request", + "InformationalResponse", + "Response", + "Data", + "EndOfMessage", + "ConnectionClosed", +] + +method_re = re.compile(method.encode("ascii")) +request_target_re = re.compile(request_target.encode("ascii")) + + +class Event(ABC): + """ + Base class for h11 events. + """ + + __slots__ = () + + +@dataclass(init=False, frozen=True) +class Request(Event): + """The beginning of an HTTP request. + + Fields: + + .. attribute:: method + + An HTTP method, e.g. ``b"GET"`` or ``b"POST"``. Always a byte + string. :term:`Bytes-like objects ` and native + strings containing only ascii characters will be automatically + converted to byte strings. + + .. attribute:: target + + The target of an HTTP request, e.g. ``b"/index.html"``, or one of the + more exotic formats described in `RFC 7320, section 5.3 + `_. Always a byte + string. :term:`Bytes-like objects ` and native + strings containing only ascii characters will be automatically + converted to byte strings. + + .. attribute:: headers + + Request headers, represented as a list of (name, value) pairs. See + :ref:`the header normalization rules ` for details. + + .. attribute:: http_version + + The HTTP protocol version, represented as a byte string like + ``b"1.1"``. See :ref:`the HTTP version normalization rules + ` for details. + + """ + + __slots__ = ("method", "headers", "target", "http_version") + + method: bytes + headers: Headers + target: bytes + http_version: bytes + + def __init__( + self, + *, + method: Union[bytes, str], + headers: Union[Headers, List[Tuple[bytes, bytes]], List[Tuple[str, str]]], + target: Union[bytes, str], + http_version: Union[bytes, str] = b"1.1", + _parsed: bool = False, + ) -> None: + super().__init__() + if isinstance(headers, Headers): + object.__setattr__(self, "headers", headers) + else: + object.__setattr__( + self, "headers", normalize_and_validate(headers, _parsed=_parsed) + ) + if not _parsed: + object.__setattr__(self, "method", bytesify(method)) + object.__setattr__(self, "target", bytesify(target)) + object.__setattr__(self, "http_version", bytesify(http_version)) + else: + object.__setattr__(self, "method", method) + object.__setattr__(self, "target", target) + object.__setattr__(self, "http_version", http_version) + + # "A server MUST respond with a 400 (Bad Request) status code to any + # HTTP/1.1 request message that lacks a Host header field and to any + # request message that contains more than one Host header field or a + # Host header field with an invalid field-value." + # -- https://tools.ietf.org/html/rfc7230#section-5.4 + host_count = 0 + for name, value in self.headers: + if name == b"host": + host_count += 1 + if self.http_version == b"1.1" and host_count == 0: + raise LocalProtocolError("Missing mandatory Host: header") + if host_count > 1: + raise LocalProtocolError("Found multiple Host: headers") + + validate(method_re, self.method, "Illegal method characters") + validate(request_target_re, self.target, "Illegal target characters") + + # This is an unhashable type. + __hash__ = None # type: ignore + + +@dataclass(init=False, frozen=True) +class _ResponseBase(Event): + __slots__ = ("headers", "http_version", "reason", "status_code") + + headers: Headers + http_version: bytes + reason: bytes + status_code: int + + def __init__( + self, + *, + headers: Union[Headers, List[Tuple[bytes, bytes]], List[Tuple[str, str]]], + status_code: int, + http_version: Union[bytes, str] = b"1.1", + reason: Union[bytes, str] = b"", + _parsed: bool = False, + ) -> None: + super().__init__() + if isinstance(headers, Headers): + object.__setattr__(self, "headers", headers) + else: + object.__setattr__( + self, "headers", normalize_and_validate(headers, _parsed=_parsed) + ) + if not _parsed: + object.__setattr__(self, "reason", bytesify(reason)) + object.__setattr__(self, "http_version", bytesify(http_version)) + if not isinstance(status_code, int): + raise LocalProtocolError("status code must be integer") + # Because IntEnum objects are instances of int, but aren't + # duck-compatible (sigh), see gh-72. + object.__setattr__(self, "status_code", int(status_code)) + else: + object.__setattr__(self, "reason", reason) + object.__setattr__(self, "http_version", http_version) + object.__setattr__(self, "status_code", status_code) + + self.__post_init__() + + def __post_init__(self) -> None: + pass + + # This is an unhashable type. + __hash__ = None # type: ignore + + +@dataclass(init=False, frozen=True) +class InformationalResponse(_ResponseBase): + """An HTTP informational response. + + Fields: + + .. attribute:: status_code + + The status code of this response, as an integer. For an + :class:`InformationalResponse`, this is always in the range [100, + 200). + + .. attribute:: headers + + Request headers, represented as a list of (name, value) pairs. See + :ref:`the header normalization rules ` for + details. + + .. attribute:: http_version + + The HTTP protocol version, represented as a byte string like + ``b"1.1"``. See :ref:`the HTTP version normalization rules + ` for details. + + .. attribute:: reason + + The reason phrase of this response, as a byte string. For example: + ``b"OK"``, or ``b"Not Found"``. + + """ + + def __post_init__(self) -> None: + if not (100 <= self.status_code < 200): + raise LocalProtocolError( + "InformationalResponse status_code should be in range " + "[100, 200), not {}".format(self.status_code) + ) + + # This is an unhashable type. + __hash__ = None # type: ignore + + +@dataclass(init=False, frozen=True) +class Response(_ResponseBase): + """The beginning of an HTTP response. + + Fields: + + .. attribute:: status_code + + The status code of this response, as an integer. For an + :class:`Response`, this is always in the range [200, + 1000). + + .. attribute:: headers + + Request headers, represented as a list of (name, value) pairs. See + :ref:`the header normalization rules ` for details. + + .. attribute:: http_version + + The HTTP protocol version, represented as a byte string like + ``b"1.1"``. See :ref:`the HTTP version normalization rules + ` for details. + + .. attribute:: reason + + The reason phrase of this response, as a byte string. For example: + ``b"OK"``, or ``b"Not Found"``. + + """ + + def __post_init__(self) -> None: + if not (200 <= self.status_code < 1000): + raise LocalProtocolError( + "Response status_code should be in range [200, 1000), not {}".format( + self.status_code + ) + ) + + # This is an unhashable type. + __hash__ = None # type: ignore + + +@dataclass(init=False, frozen=True) +class Data(Event): + """Part of an HTTP message body. + + Fields: + + .. attribute:: data + + A :term:`bytes-like object` containing part of a message body. Or, if + using the ``combine=False`` argument to :meth:`Connection.send`, then + any object that your socket writing code knows what to do with, and for + which calling :func:`len` returns the number of bytes that will be + written -- see :ref:`sendfile` for details. + + .. attribute:: chunk_start + + A marker that indicates whether this data object is from the start of a + chunked transfer encoding chunk. This field is ignored when when a Data + event is provided to :meth:`Connection.send`: it is only valid on + events emitted from :meth:`Connection.next_event`. You probably + shouldn't use this attribute at all; see + :ref:`chunk-delimiters-are-bad` for details. + + .. attribute:: chunk_end + + A marker that indicates whether this data object is the last for a + given chunked transfer encoding chunk. This field is ignored when when + a Data event is provided to :meth:`Connection.send`: it is only valid + on events emitted from :meth:`Connection.next_event`. You probably + shouldn't use this attribute at all; see + :ref:`chunk-delimiters-are-bad` for details. + + """ + + __slots__ = ("data", "chunk_start", "chunk_end") + + data: bytes + chunk_start: bool + chunk_end: bool + + def __init__( + self, data: bytes, chunk_start: bool = False, chunk_end: bool = False + ) -> None: + object.__setattr__(self, "data", data) + object.__setattr__(self, "chunk_start", chunk_start) + object.__setattr__(self, "chunk_end", chunk_end) + + # This is an unhashable type. + __hash__ = None # type: ignore + + +# XX FIXME: "A recipient MUST ignore (or consider as an error) any fields that +# are forbidden to be sent in a trailer, since processing them as if they were +# present in the header section might bypass external security filters." +# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#chunked.trailer.part +# Unfortunately, the list of forbidden fields is long and vague :-/ +@dataclass(init=False, frozen=True) +class EndOfMessage(Event): + """The end of an HTTP message. + + Fields: + + .. attribute:: headers + + Default value: ``[]`` + + Any trailing headers attached to this message, represented as a list of + (name, value) pairs. See :ref:`the header normalization rules + ` for details. + + Must be empty unless ``Transfer-Encoding: chunked`` is in use. + + """ + + __slots__ = ("headers",) + + headers: Headers + + def __init__( + self, + *, + headers: Union[ + Headers, List[Tuple[bytes, bytes]], List[Tuple[str, str]], None + ] = None, + _parsed: bool = False, + ) -> None: + super().__init__() + if headers is None: + headers = Headers([]) + elif not isinstance(headers, Headers): + headers = normalize_and_validate(headers, _parsed=_parsed) + + object.__setattr__(self, "headers", headers) + + # This is an unhashable type. + __hash__ = None # type: ignore + + +@dataclass(frozen=True) +class ConnectionClosed(Event): + """This event indicates that the sender has closed their outgoing + connection. + + Note that this does not necessarily mean that they can't *receive* further + data, because TCP connections are composed to two one-way channels which + can be closed independently. See :ref:`closing` for details. + + No fields. + """ + + pass diff --git a/.venv/lib/python3.9/site-packages/h11/_headers.py b/.venv/lib/python3.9/site-packages/h11/_headers.py new file mode 100644 index 0000000..31da3e2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/h11/_headers.py @@ -0,0 +1,282 @@ +import re +from typing import AnyStr, cast, List, overload, Sequence, Tuple, TYPE_CHECKING, Union + +from ._abnf import field_name, field_value +from ._util import bytesify, LocalProtocolError, validate + +if TYPE_CHECKING: + from ._events import Request + +try: + from typing import Literal +except ImportError: + from typing_extensions import Literal # type: ignore + +CONTENT_LENGTH_MAX_DIGITS = 20 # allow up to 1 billion TB - 1 + + +# Facts +# ----- +# +# Headers are: +# keys: case-insensitive ascii +# values: mixture of ascii and raw bytes +# +# "Historically, HTTP has allowed field content with text in the ISO-8859-1 +# charset [ISO-8859-1], supporting other charsets only through use of +# [RFC2047] encoding. In practice, most HTTP header field values use only a +# subset of the US-ASCII charset [USASCII]. Newly defined header fields SHOULD +# limit their field values to US-ASCII octets. A recipient SHOULD treat other +# octets in field content (obs-text) as opaque data." +# And it deprecates all non-ascii values +# +# Leading/trailing whitespace in header names is forbidden +# +# Values get leading/trailing whitespace stripped +# +# Content-Disposition actually needs to contain unicode semantically; to +# accomplish this it has a terrifically weird way of encoding the filename +# itself as ascii (and even this still has lots of cross-browser +# incompatibilities) +# +# Order is important: +# "a proxy MUST NOT change the order of these field values when forwarding a +# message" +# (and there are several headers where the order indicates a preference) +# +# Multiple occurences of the same header: +# "A sender MUST NOT generate multiple header fields with the same field name +# in a message unless either the entire field value for that header field is +# defined as a comma-separated list [or the header is Set-Cookie which gets a +# special exception]" - RFC 7230. (cookies are in RFC 6265) +# +# So every header aside from Set-Cookie can be merged by b", ".join if it +# occurs repeatedly. But, of course, they can't necessarily be split by +# .split(b","), because quoting. +# +# Given all this mess (case insensitive, duplicates allowed, order is +# important, ...), there doesn't appear to be any standard way to handle +# headers in Python -- they're almost like dicts, but... actually just +# aren't. For now we punt and just use a super simple representation: headers +# are a list of pairs +# +# [(name1, value1), (name2, value2), ...] +# +# where all entries are bytestrings, names are lowercase and have no +# leading/trailing whitespace, and values are bytestrings with no +# leading/trailing whitespace. Searching and updating are done via naive O(n) +# methods. +# +# Maybe a dict-of-lists would be better? + +_content_length_re = re.compile(rb"[0-9]+") +_field_name_re = re.compile(field_name.encode("ascii")) +_field_value_re = re.compile(field_value.encode("ascii")) + + +class Headers(Sequence[Tuple[bytes, bytes]]): + """ + A list-like interface that allows iterating over headers as byte-pairs + of (lowercased-name, value). + + Internally we actually store the representation as three-tuples, + including both the raw original casing, in order to preserve casing + over-the-wire, and the lowercased name, for case-insensitive comparisions. + + r = Request( + method="GET", + target="/", + headers=[("Host", "example.org"), ("Connection", "keep-alive")], + http_version="1.1", + ) + assert r.headers == [ + (b"host", b"example.org"), + (b"connection", b"keep-alive") + ] + assert r.headers.raw_items() == [ + (b"Host", b"example.org"), + (b"Connection", b"keep-alive") + ] + """ + + __slots__ = "_full_items" + + def __init__(self, full_items: List[Tuple[bytes, bytes, bytes]]) -> None: + self._full_items = full_items + + def __bool__(self) -> bool: + return bool(self._full_items) + + def __eq__(self, other: object) -> bool: + return list(self) == list(other) # type: ignore + + def __len__(self) -> int: + return len(self._full_items) + + def __repr__(self) -> str: + return "" % repr(list(self)) + + def __getitem__(self, idx: int) -> Tuple[bytes, bytes]: # type: ignore[override] + _, name, value = self._full_items[idx] + return (name, value) + + def raw_items(self) -> List[Tuple[bytes, bytes]]: + return [(raw_name, value) for raw_name, _, value in self._full_items] + + +HeaderTypes = Union[ + List[Tuple[bytes, bytes]], + List[Tuple[bytes, str]], + List[Tuple[str, bytes]], + List[Tuple[str, str]], +] + + +@overload +def normalize_and_validate(headers: Headers, _parsed: Literal[True]) -> Headers: + ... + + +@overload +def normalize_and_validate(headers: HeaderTypes, _parsed: Literal[False]) -> Headers: + ... + + +@overload +def normalize_and_validate( + headers: Union[Headers, HeaderTypes], _parsed: bool = False +) -> Headers: + ... + + +def normalize_and_validate( + headers: Union[Headers, HeaderTypes], _parsed: bool = False +) -> Headers: + new_headers = [] + seen_content_length = None + saw_transfer_encoding = False + for name, value in headers: + # For headers coming out of the parser, we can safely skip some steps, + # because it always returns bytes and has already run these regexes + # over the data: + if not _parsed: + name = bytesify(name) + value = bytesify(value) + validate(_field_name_re, name, "Illegal header name {!r}", name) + validate(_field_value_re, value, "Illegal header value {!r}", value) + assert isinstance(name, bytes) + assert isinstance(value, bytes) + + raw_name = name + name = name.lower() + if name == b"content-length": + lengths = {length.strip() for length in value.split(b",")} + if len(lengths) != 1: + raise LocalProtocolError("conflicting Content-Length headers") + value = lengths.pop() + validate(_content_length_re, value, "bad Content-Length") + if len(value) > CONTENT_LENGTH_MAX_DIGITS: + raise LocalProtocolError("bad Content-Length") + if seen_content_length is None: + seen_content_length = value + new_headers.append((raw_name, name, value)) + elif seen_content_length != value: + raise LocalProtocolError("conflicting Content-Length headers") + elif name == b"transfer-encoding": + # "A server that receives a request message with a transfer coding + # it does not understand SHOULD respond with 501 (Not + # Implemented)." + # https://tools.ietf.org/html/rfc7230#section-3.3.1 + if saw_transfer_encoding: + raise LocalProtocolError( + "multiple Transfer-Encoding headers", error_status_hint=501 + ) + # "All transfer-coding names are case-insensitive" + # -- https://tools.ietf.org/html/rfc7230#section-4 + value = value.lower() + if value != b"chunked": + raise LocalProtocolError( + "Only Transfer-Encoding: chunked is supported", + error_status_hint=501, + ) + saw_transfer_encoding = True + new_headers.append((raw_name, name, value)) + else: + new_headers.append((raw_name, name, value)) + return Headers(new_headers) + + +def get_comma_header(headers: Headers, name: bytes) -> List[bytes]: + # Should only be used for headers whose value is a list of + # comma-separated, case-insensitive values. + # + # The header name `name` is expected to be lower-case bytes. + # + # Connection: meets these criteria (including cast insensitivity). + # + # Content-Length: technically is just a single value (1*DIGIT), but the + # standard makes reference to implementations that do multiple values, and + # using this doesn't hurt. Ditto, case insensitivity doesn't things either + # way. + # + # Transfer-Encoding: is more complex (allows for quoted strings), so + # splitting on , is actually wrong. For example, this is legal: + # + # Transfer-Encoding: foo; options="1,2", chunked + # + # and should be parsed as + # + # foo; options="1,2" + # chunked + # + # but this naive function will parse it as + # + # foo; options="1 + # 2" + # chunked + # + # However, this is okay because the only thing we are going to do with + # any Transfer-Encoding is reject ones that aren't just "chunked", so + # both of these will be treated the same anyway. + # + # Expect: the only legal value is the literal string + # "100-continue". Splitting on commas is harmless. Case insensitive. + # + out: List[bytes] = [] + for _, found_name, found_raw_value in headers._full_items: + if found_name == name: + found_raw_value = found_raw_value.lower() + for found_split_value in found_raw_value.split(b","): + found_split_value = found_split_value.strip() + if found_split_value: + out.append(found_split_value) + return out + + +def set_comma_header(headers: Headers, name: bytes, new_values: List[bytes]) -> Headers: + # The header name `name` is expected to be lower-case bytes. + # + # Note that when we store the header we use title casing for the header + # names, in order to match the conventional HTTP header style. + # + # Simply calling `.title()` is a blunt approach, but it's correct + # here given the cases where we're using `set_comma_header`... + # + # Connection, Content-Length, Transfer-Encoding. + new_headers: List[Tuple[bytes, bytes]] = [] + for found_raw_name, found_name, found_raw_value in headers._full_items: + if found_name != name: + new_headers.append((found_raw_name, found_raw_value)) + for new_value in new_values: + new_headers.append((name.title(), new_value)) + return normalize_and_validate(new_headers) + + +def has_expect_100_continue(request: "Request") -> bool: + # https://tools.ietf.org/html/rfc7231#section-5.1.1 + # "A server that receives a 100-continue expectation in an HTTP/1.0 request + # MUST ignore that expectation." + if request.http_version < b"1.1": + return False + expect = get_comma_header(request.headers, b"expect") + return b"100-continue" in expect diff --git a/.venv/lib/python3.9/site-packages/h11/_readers.py b/.venv/lib/python3.9/site-packages/h11/_readers.py new file mode 100644 index 0000000..576804c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/h11/_readers.py @@ -0,0 +1,250 @@ +# Code to read HTTP data +# +# Strategy: each reader is a callable which takes a ReceiveBuffer object, and +# either: +# 1) consumes some of it and returns an Event +# 2) raises a LocalProtocolError (for consistency -- e.g. we call validate() +# and it might raise a LocalProtocolError, so simpler just to always use +# this) +# 3) returns None, meaning "I need more data" +# +# If they have a .read_eof attribute, then this will be called if an EOF is +# received -- but this is optional. Either way, the actual ConnectionClosed +# event will be generated afterwards. +# +# READERS is a dict describing how to pick a reader. It maps states to either: +# - a reader +# - or, for body readers, a dict of per-framing reader factories + +import re +from typing import Any, Callable, Dict, Iterable, NoReturn, Optional, Tuple, Type, Union + +from ._abnf import chunk_header, header_field, request_line, status_line +from ._events import Data, EndOfMessage, InformationalResponse, Request, Response +from ._receivebuffer import ReceiveBuffer +from ._state import ( + CLIENT, + CLOSED, + DONE, + IDLE, + MUST_CLOSE, + SEND_BODY, + SEND_RESPONSE, + SERVER, +) +from ._util import LocalProtocolError, RemoteProtocolError, Sentinel, validate + +__all__ = ["READERS"] + +header_field_re = re.compile(header_field.encode("ascii")) +obs_fold_re = re.compile(rb"[ \t]+") + + +def _obsolete_line_fold(lines: Iterable[bytes]) -> Iterable[bytes]: + it = iter(lines) + last: Optional[bytes] = None + for line in it: + match = obs_fold_re.match(line) + if match: + if last is None: + raise LocalProtocolError("continuation line at start of headers") + if not isinstance(last, bytearray): + # Cast to a mutable type, avoiding copy on append to ensure O(n) time + last = bytearray(last) + last += b" " + last += line[match.end() :] + else: + if last is not None: + yield last + last = line + if last is not None: + yield last + + +def _decode_header_lines( + lines: Iterable[bytes], +) -> Iterable[Tuple[bytes, bytes]]: + for line in _obsolete_line_fold(lines): + matches = validate(header_field_re, line, "illegal header line: {!r}", line) + yield (matches["field_name"], matches["field_value"]) + + +request_line_re = re.compile(request_line.encode("ascii")) + + +def maybe_read_from_IDLE_client(buf: ReceiveBuffer) -> Optional[Request]: + lines = buf.maybe_extract_lines() + if lines is None: + if buf.is_next_line_obviously_invalid_request_line(): + raise LocalProtocolError("illegal request line") + return None + if not lines: + raise LocalProtocolError("no request line received") + matches = validate( + request_line_re, lines[0], "illegal request line: {!r}", lines[0] + ) + return Request( + headers=list(_decode_header_lines(lines[1:])), _parsed=True, **matches + ) + + +status_line_re = re.compile(status_line.encode("ascii")) + + +def maybe_read_from_SEND_RESPONSE_server( + buf: ReceiveBuffer, +) -> Union[InformationalResponse, Response, None]: + lines = buf.maybe_extract_lines() + if lines is None: + if buf.is_next_line_obviously_invalid_request_line(): + raise LocalProtocolError("illegal request line") + return None + if not lines: + raise LocalProtocolError("no response line received") + matches = validate(status_line_re, lines[0], "illegal status line: {!r}", lines[0]) + http_version = ( + b"1.1" if matches["http_version"] is None else matches["http_version"] + ) + reason = b"" if matches["reason"] is None else matches["reason"] + status_code = int(matches["status_code"]) + class_: Union[Type[InformationalResponse], Type[Response]] = ( + InformationalResponse if status_code < 200 else Response + ) + return class_( + headers=list(_decode_header_lines(lines[1:])), + _parsed=True, + status_code=status_code, + reason=reason, + http_version=http_version, + ) + + +class ContentLengthReader: + def __init__(self, length: int) -> None: + self._length = length + self._remaining = length + + def __call__(self, buf: ReceiveBuffer) -> Union[Data, EndOfMessage, None]: + if self._remaining == 0: + return EndOfMessage() + data = buf.maybe_extract_at_most(self._remaining) + if data is None: + return None + self._remaining -= len(data) + return Data(data=data) + + def read_eof(self) -> NoReturn: + raise RemoteProtocolError( + "peer closed connection without sending complete message body " + "(received {} bytes, expected {})".format( + self._length - self._remaining, self._length + ) + ) + + +chunk_header_re = re.compile(chunk_header.encode("ascii")) + + +class ChunkedReader: + def __init__(self) -> None: + self._bytes_in_chunk = 0 + # After reading a chunk, we have to throw away the trailing \r\n. + # This tracks the bytes that we need to match and throw away. + self._bytes_to_discard = b"" + self._reading_trailer = False + + def __call__(self, buf: ReceiveBuffer) -> Union[Data, EndOfMessage, None]: + if self._reading_trailer: + lines = buf.maybe_extract_lines() + if lines is None: + return None + return EndOfMessage(headers=list(_decode_header_lines(lines))) + if self._bytes_to_discard: + data = buf.maybe_extract_at_most(len(self._bytes_to_discard)) + if data is None: + return None + if data != self._bytes_to_discard[: len(data)]: + raise LocalProtocolError( + f"malformed chunk footer: {data!r} (expected {self._bytes_to_discard!r})" + ) + self._bytes_to_discard = self._bytes_to_discard[len(data) :] + if self._bytes_to_discard: + return None + # else, fall through and read some more + assert self._bytes_to_discard == b"" + if self._bytes_in_chunk == 0: + # We need to refill our chunk count + chunk_header = buf.maybe_extract_next_line() + if chunk_header is None: + return None + matches = validate( + chunk_header_re, + chunk_header, + "illegal chunk header: {!r}", + chunk_header, + ) + # XX FIXME: we discard chunk extensions. Does anyone care? + self._bytes_in_chunk = int(matches["chunk_size"], base=16) + if self._bytes_in_chunk == 0: + self._reading_trailer = True + return self(buf) + chunk_start = True + else: + chunk_start = False + assert self._bytes_in_chunk > 0 + data = buf.maybe_extract_at_most(self._bytes_in_chunk) + if data is None: + return None + self._bytes_in_chunk -= len(data) + if self._bytes_in_chunk == 0: + self._bytes_to_discard = b"\r\n" + chunk_end = True + else: + chunk_end = False + return Data(data=data, chunk_start=chunk_start, chunk_end=chunk_end) + + def read_eof(self) -> NoReturn: + raise RemoteProtocolError( + "peer closed connection without sending complete message body " + "(incomplete chunked read)" + ) + + +class Http10Reader: + def __call__(self, buf: ReceiveBuffer) -> Optional[Data]: + data = buf.maybe_extract_at_most(999999999) + if data is None: + return None + return Data(data=data) + + def read_eof(self) -> EndOfMessage: + return EndOfMessage() + + +def expect_nothing(buf: ReceiveBuffer) -> None: + if buf: + raise LocalProtocolError("Got data when expecting EOF") + return None + + +ReadersType = Dict[ + Union[Type[Sentinel], Tuple[Type[Sentinel], Type[Sentinel]]], + Union[Callable[..., Any], Dict[str, Callable[..., Any]]], +] + +READERS: ReadersType = { + (CLIENT, IDLE): maybe_read_from_IDLE_client, + (SERVER, IDLE): maybe_read_from_SEND_RESPONSE_server, + (SERVER, SEND_RESPONSE): maybe_read_from_SEND_RESPONSE_server, + (CLIENT, DONE): expect_nothing, + (CLIENT, MUST_CLOSE): expect_nothing, + (CLIENT, CLOSED): expect_nothing, + (SERVER, DONE): expect_nothing, + (SERVER, MUST_CLOSE): expect_nothing, + (SERVER, CLOSED): expect_nothing, + SEND_BODY: { + "chunked": ChunkedReader, + "content-length": ContentLengthReader, + "http/1.0": Http10Reader, + }, +} diff --git a/.venv/lib/python3.9/site-packages/h11/_receivebuffer.py b/.venv/lib/python3.9/site-packages/h11/_receivebuffer.py new file mode 100644 index 0000000..e5c4e08 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/h11/_receivebuffer.py @@ -0,0 +1,153 @@ +import re +import sys +from typing import List, Optional, Union + +__all__ = ["ReceiveBuffer"] + + +# Operations we want to support: +# - find next \r\n or \r\n\r\n (\n or \n\n are also acceptable), +# or wait until there is one +# - read at-most-N bytes +# Goals: +# - on average, do this fast +# - worst case, do this in O(n) where n is the number of bytes processed +# Plan: +# - store bytearray, offset, how far we've searched for a separator token +# - use the how-far-we've-searched data to avoid rescanning +# - while doing a stream of uninterrupted processing, advance offset instead +# of constantly copying +# WARNING: +# - I haven't benchmarked or profiled any of this yet. +# +# Note that starting in Python 3.4, deleting the initial n bytes from a +# bytearray is amortized O(n), thanks to some excellent work by Antoine +# Martin: +# +# https://bugs.python.org/issue19087 +# +# This means that if we only supported 3.4+, we could get rid of the code here +# involving self._start and self.compress, because it's doing exactly the same +# thing that bytearray now does internally. +# +# BUT unfortunately, we still support 2.7, and reading short segments out of a +# long buffer MUST be O(bytes read) to avoid DoS issues, so we can't actually +# delete this code. Yet: +# +# https://pythonclock.org/ +# +# (Two things to double-check first though: make sure PyPy also has the +# optimization, and benchmark to make sure it's a win, since we do have a +# slightly clever thing where we delay calling compress() until we've +# processed a whole event, which could in theory be slightly more efficient +# than the internal bytearray support.) +blank_line_regex = re.compile(b"\n\r?\n", re.MULTILINE) + + +class ReceiveBuffer: + def __init__(self) -> None: + self._data = bytearray() + self._next_line_search = 0 + self._multiple_lines_search = 0 + + def __iadd__(self, byteslike: Union[bytes, bytearray]) -> "ReceiveBuffer": + self._data += byteslike + return self + + def __bool__(self) -> bool: + return bool(len(self)) + + def __len__(self) -> int: + return len(self._data) + + # for @property unprocessed_data + def __bytes__(self) -> bytes: + return bytes(self._data) + + def _extract(self, count: int) -> bytearray: + # extracting an initial slice of the data buffer and return it + out = self._data[:count] + del self._data[:count] + + self._next_line_search = 0 + self._multiple_lines_search = 0 + + return out + + def maybe_extract_at_most(self, count: int) -> Optional[bytearray]: + """ + Extract a fixed number of bytes from the buffer. + """ + out = self._data[:count] + if not out: + return None + + return self._extract(count) + + def maybe_extract_next_line(self) -> Optional[bytearray]: + """ + Extract the first line, if it is completed in the buffer. + """ + # Only search in buffer space that we've not already looked at. + search_start_index = max(0, self._next_line_search - 1) + partial_idx = self._data.find(b"\r\n", search_start_index) + + if partial_idx == -1: + self._next_line_search = len(self._data) + return None + + # + 2 is to compensate len(b"\r\n") + idx = partial_idx + 2 + + return self._extract(idx) + + def maybe_extract_lines(self) -> Optional[List[bytearray]]: + """ + Extract everything up to the first blank line, and return a list of lines. + """ + # Handle the case where we have an immediate empty line. + if self._data[:1] == b"\n": + self._extract(1) + return [] + + if self._data[:2] == b"\r\n": + self._extract(2) + return [] + + # Only search in buffer space that we've not already looked at. + match = blank_line_regex.search(self._data, self._multiple_lines_search) + if match is None: + self._multiple_lines_search = max(0, len(self._data) - 2) + return None + + # Truncate the buffer and return it. + idx = match.span(0)[-1] + out = self._extract(idx) + lines = out.split(b"\n") + + for line in lines: + if line.endswith(b"\r"): + del line[-1] + + assert lines[-2] == lines[-1] == b"" + + del lines[-2:] + + return lines + + # In theory we should wait until `\r\n` before starting to validate + # incoming data. However it's interesting to detect (very) invalid data + # early given they might not even contain `\r\n` at all (hence only + # timeout will get rid of them). + # This is not a 100% effective detection but more of a cheap sanity check + # allowing for early abort in some useful cases. + # This is especially interesting when peer is messing up with HTTPS and + # sent us a TLS stream where we were expecting plain HTTP given all + # versions of TLS so far start handshake with a 0x16 message type code. + def is_next_line_obviously_invalid_request_line(self) -> bool: + try: + # HTTP header line must not contain non-printable characters + # and should not start with a space + return self._data[0] < 0x21 + except IndexError: + return False diff --git a/.venv/lib/python3.9/site-packages/h11/_state.py b/.venv/lib/python3.9/site-packages/h11/_state.py new file mode 100644 index 0000000..3ad444b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/h11/_state.py @@ -0,0 +1,365 @@ +################################################################ +# The core state machine +################################################################ +# +# Rule 1: everything that affects the state machine and state transitions must +# live here in this file. As much as possible goes into the table-based +# representation, but for the bits that don't quite fit, the actual code and +# state must nonetheless live here. +# +# Rule 2: this file does not know about what role we're playing; it only knows +# about HTTP request/response cycles in the abstract. This ensures that we +# don't cheat and apply different rules to local and remote parties. +# +# +# Theory of operation +# =================== +# +# Possibly the simplest way to think about this is that we actually have 5 +# different state machines here. Yes, 5. These are: +# +# 1) The client state, with its complicated automaton (see the docs) +# 2) The server state, with its complicated automaton (see the docs) +# 3) The keep-alive state, with possible states {True, False} +# 4) The SWITCH_CONNECT state, with possible states {False, True} +# 5) The SWITCH_UPGRADE state, with possible states {False, True} +# +# For (3)-(5), the first state listed is the initial state. +# +# (1)-(3) are stored explicitly in member variables. The last +# two are stored implicitly in the pending_switch_proposals set as: +# (state of 4) == (_SWITCH_CONNECT in pending_switch_proposals) +# (state of 5) == (_SWITCH_UPGRADE in pending_switch_proposals) +# +# And each of these machines has two different kinds of transitions: +# +# a) Event-triggered +# b) State-triggered +# +# Event triggered is the obvious thing that you'd think it is: some event +# happens, and if it's the right event at the right time then a transition +# happens. But there are somewhat complicated rules for which machines can +# "see" which events. (As a rule of thumb, if a machine "sees" an event, this +# means two things: the event can affect the machine, and if the machine is +# not in a state where it expects that event then it's an error.) These rules +# are: +# +# 1) The client machine sees all h11.events objects emitted by the client. +# +# 2) The server machine sees all h11.events objects emitted by the server. +# +# It also sees the client's Request event. +# +# And sometimes, server events are annotated with a _SWITCH_* event. For +# example, we can have a (Response, _SWITCH_CONNECT) event, which is +# different from a regular Response event. +# +# 3) The keep-alive machine sees the process_keep_alive_disabled() event +# (which is derived from Request/Response events), and this event +# transitions it from True -> False, or from False -> False. There's no way +# to transition back. +# +# 4&5) The _SWITCH_* machines transition from False->True when we get a +# Request that proposes the relevant type of switch (via +# process_client_switch_proposals), and they go from True->False when we +# get a Response that has no _SWITCH_* annotation. +# +# So that's event-triggered transitions. +# +# State-triggered transitions are less standard. What they do here is couple +# the machines together. The way this works is, when certain *joint* +# configurations of states are achieved, then we automatically transition to a +# new *joint* state. So, for example, if we're ever in a joint state with +# +# client: DONE +# keep-alive: False +# +# then the client state immediately transitions to: +# +# client: MUST_CLOSE +# +# This is fundamentally different from an event-based transition, because it +# doesn't matter how we arrived at the {client: DONE, keep-alive: False} state +# -- maybe the client transitioned SEND_BODY -> DONE, or keep-alive +# transitioned True -> False. Either way, once this precondition is satisfied, +# this transition is immediately triggered. +# +# What if two conflicting state-based transitions get enabled at the same +# time? In practice there's only one case where this arises (client DONE -> +# MIGHT_SWITCH_PROTOCOL versus DONE -> MUST_CLOSE), and we resolve it by +# explicitly prioritizing the DONE -> MIGHT_SWITCH_PROTOCOL transition. +# +# Implementation +# -------------- +# +# The event-triggered transitions for the server and client machines are all +# stored explicitly in a table. Ditto for the state-triggered transitions that +# involve just the server and client state. +# +# The transitions for the other machines, and the state-triggered transitions +# that involve the other machines, are written out as explicit Python code. +# +# It'd be nice if there were some cleaner way to do all this. This isn't +# *too* terrible, but I feel like it could probably be better. +# +# WARNING +# ------- +# +# The script that generates the state machine diagrams for the docs knows how +# to read out the EVENT_TRIGGERED_TRANSITIONS and STATE_TRIGGERED_TRANSITIONS +# tables. But it can't automatically read the transitions that are written +# directly in Python code. So if you touch those, you need to also update the +# script to keep it in sync! +from typing import cast, Dict, Optional, Set, Tuple, Type, Union + +from ._events import * +from ._util import LocalProtocolError, Sentinel + +# Everything in __all__ gets re-exported as part of the h11 public API. +__all__ = [ + "CLIENT", + "SERVER", + "IDLE", + "SEND_RESPONSE", + "SEND_BODY", + "DONE", + "MUST_CLOSE", + "CLOSED", + "MIGHT_SWITCH_PROTOCOL", + "SWITCHED_PROTOCOL", + "ERROR", +] + + +class CLIENT(Sentinel, metaclass=Sentinel): + pass + + +class SERVER(Sentinel, metaclass=Sentinel): + pass + + +# States +class IDLE(Sentinel, metaclass=Sentinel): + pass + + +class SEND_RESPONSE(Sentinel, metaclass=Sentinel): + pass + + +class SEND_BODY(Sentinel, metaclass=Sentinel): + pass + + +class DONE(Sentinel, metaclass=Sentinel): + pass + + +class MUST_CLOSE(Sentinel, metaclass=Sentinel): + pass + + +class CLOSED(Sentinel, metaclass=Sentinel): + pass + + +class ERROR(Sentinel, metaclass=Sentinel): + pass + + +# Switch types +class MIGHT_SWITCH_PROTOCOL(Sentinel, metaclass=Sentinel): + pass + + +class SWITCHED_PROTOCOL(Sentinel, metaclass=Sentinel): + pass + + +class _SWITCH_UPGRADE(Sentinel, metaclass=Sentinel): + pass + + +class _SWITCH_CONNECT(Sentinel, metaclass=Sentinel): + pass + + +EventTransitionType = Dict[ + Type[Sentinel], + Dict[ + Type[Sentinel], + Dict[Union[Type[Event], Tuple[Type[Event], Type[Sentinel]]], Type[Sentinel]], + ], +] + +EVENT_TRIGGERED_TRANSITIONS: EventTransitionType = { + CLIENT: { + IDLE: {Request: SEND_BODY, ConnectionClosed: CLOSED}, + SEND_BODY: {Data: SEND_BODY, EndOfMessage: DONE}, + DONE: {ConnectionClosed: CLOSED}, + MUST_CLOSE: {ConnectionClosed: CLOSED}, + CLOSED: {ConnectionClosed: CLOSED}, + MIGHT_SWITCH_PROTOCOL: {}, + SWITCHED_PROTOCOL: {}, + ERROR: {}, + }, + SERVER: { + IDLE: { + ConnectionClosed: CLOSED, + Response: SEND_BODY, + # Special case: server sees client Request events, in this form + (Request, CLIENT): SEND_RESPONSE, + }, + SEND_RESPONSE: { + InformationalResponse: SEND_RESPONSE, + Response: SEND_BODY, + (InformationalResponse, _SWITCH_UPGRADE): SWITCHED_PROTOCOL, + (Response, _SWITCH_CONNECT): SWITCHED_PROTOCOL, + }, + SEND_BODY: {Data: SEND_BODY, EndOfMessage: DONE}, + DONE: {ConnectionClosed: CLOSED}, + MUST_CLOSE: {ConnectionClosed: CLOSED}, + CLOSED: {ConnectionClosed: CLOSED}, + SWITCHED_PROTOCOL: {}, + ERROR: {}, + }, +} + +StateTransitionType = Dict[ + Tuple[Type[Sentinel], Type[Sentinel]], Dict[Type[Sentinel], Type[Sentinel]] +] + +# NB: there are also some special-case state-triggered transitions hard-coded +# into _fire_state_triggered_transitions below. +STATE_TRIGGERED_TRANSITIONS: StateTransitionType = { + # (Client state, Server state) -> new states + # Protocol negotiation + (MIGHT_SWITCH_PROTOCOL, SWITCHED_PROTOCOL): {CLIENT: SWITCHED_PROTOCOL}, + # Socket shutdown + (CLOSED, DONE): {SERVER: MUST_CLOSE}, + (CLOSED, IDLE): {SERVER: MUST_CLOSE}, + (ERROR, DONE): {SERVER: MUST_CLOSE}, + (DONE, CLOSED): {CLIENT: MUST_CLOSE}, + (IDLE, CLOSED): {CLIENT: MUST_CLOSE}, + (DONE, ERROR): {CLIENT: MUST_CLOSE}, +} + + +class ConnectionState: + def __init__(self) -> None: + # Extra bits of state that don't quite fit into the state model. + + # If this is False then it enables the automatic DONE -> MUST_CLOSE + # transition. Don't set this directly; call .keep_alive_disabled() + self.keep_alive = True + + # This is a subset of {UPGRADE, CONNECT}, containing the proposals + # made by the client for switching protocols. + self.pending_switch_proposals: Set[Type[Sentinel]] = set() + + self.states: Dict[Type[Sentinel], Type[Sentinel]] = {CLIENT: IDLE, SERVER: IDLE} + + def process_error(self, role: Type[Sentinel]) -> None: + self.states[role] = ERROR + self._fire_state_triggered_transitions() + + def process_keep_alive_disabled(self) -> None: + self.keep_alive = False + self._fire_state_triggered_transitions() + + def process_client_switch_proposal(self, switch_event: Type[Sentinel]) -> None: + self.pending_switch_proposals.add(switch_event) + self._fire_state_triggered_transitions() + + def process_event( + self, + role: Type[Sentinel], + event_type: Type[Event], + server_switch_event: Optional[Type[Sentinel]] = None, + ) -> None: + _event_type: Union[Type[Event], Tuple[Type[Event], Type[Sentinel]]] = event_type + if server_switch_event is not None: + assert role is SERVER + if server_switch_event not in self.pending_switch_proposals: + raise LocalProtocolError( + "Received server _SWITCH_UPGRADE event without a pending proposal" + ) + _event_type = (event_type, server_switch_event) + if server_switch_event is None and _event_type is Response: + self.pending_switch_proposals = set() + self._fire_event_triggered_transitions(role, _event_type) + # Special case: the server state does get to see Request + # events. + if _event_type is Request: + assert role is CLIENT + self._fire_event_triggered_transitions(SERVER, (Request, CLIENT)) + self._fire_state_triggered_transitions() + + def _fire_event_triggered_transitions( + self, + role: Type[Sentinel], + event_type: Union[Type[Event], Tuple[Type[Event], Type[Sentinel]]], + ) -> None: + state = self.states[role] + try: + new_state = EVENT_TRIGGERED_TRANSITIONS[role][state][event_type] + except KeyError: + event_type = cast(Type[Event], event_type) + raise LocalProtocolError( + "can't handle event type {} when role={} and state={}".format( + event_type.__name__, role, self.states[role] + ) + ) from None + self.states[role] = new_state + + def _fire_state_triggered_transitions(self) -> None: + # We apply these rules repeatedly until converging on a fixed point + while True: + start_states = dict(self.states) + + # It could happen that both these special-case transitions are + # enabled at the same time: + # + # DONE -> MIGHT_SWITCH_PROTOCOL + # DONE -> MUST_CLOSE + # + # For example, this will always be true of a HTTP/1.0 client + # requesting CONNECT. If this happens, the protocol switch takes + # priority. From there the client will either go to + # SWITCHED_PROTOCOL, in which case it's none of our business when + # they close the connection, or else the server will deny the + # request, in which case the client will go back to DONE and then + # from there to MUST_CLOSE. + if self.pending_switch_proposals: + if self.states[CLIENT] is DONE: + self.states[CLIENT] = MIGHT_SWITCH_PROTOCOL + + if not self.pending_switch_proposals: + if self.states[CLIENT] is MIGHT_SWITCH_PROTOCOL: + self.states[CLIENT] = DONE + + if not self.keep_alive: + for role in (CLIENT, SERVER): + if self.states[role] is DONE: + self.states[role] = MUST_CLOSE + + # Tabular state-triggered transitions + joint_state = (self.states[CLIENT], self.states[SERVER]) + changes = STATE_TRIGGERED_TRANSITIONS.get(joint_state, {}) + self.states.update(changes) + + if self.states == start_states: + # Fixed point reached + return + + def start_next_cycle(self) -> None: + if self.states != {CLIENT: DONE, SERVER: DONE}: + raise LocalProtocolError( + f"not in a reusable state. self.states={self.states}" + ) + # Can't reach DONE/DONE with any of these active, but still, let's be + # sure. + assert self.keep_alive + assert not self.pending_switch_proposals + self.states = {CLIENT: IDLE, SERVER: IDLE} diff --git a/.venv/lib/python3.9/site-packages/h11/_util.py b/.venv/lib/python3.9/site-packages/h11/_util.py new file mode 100644 index 0000000..6718445 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/h11/_util.py @@ -0,0 +1,135 @@ +from typing import Any, Dict, NoReturn, Pattern, Tuple, Type, TypeVar, Union + +__all__ = [ + "ProtocolError", + "LocalProtocolError", + "RemoteProtocolError", + "validate", + "bytesify", +] + + +class ProtocolError(Exception): + """Exception indicating a violation of the HTTP/1.1 protocol. + + This as an abstract base class, with two concrete base classes: + :exc:`LocalProtocolError`, which indicates that you tried to do something + that HTTP/1.1 says is illegal, and :exc:`RemoteProtocolError`, which + indicates that the remote peer tried to do something that HTTP/1.1 says is + illegal. See :ref:`error-handling` for details. + + In addition to the normal :exc:`Exception` features, it has one attribute: + + .. attribute:: error_status_hint + + This gives a suggestion as to what status code a server might use if + this error occurred as part of a request. + + For a :exc:`RemoteProtocolError`, this is useful as a suggestion for + how you might want to respond to a misbehaving peer, if you're + implementing a server. + + For a :exc:`LocalProtocolError`, this can be taken as a suggestion for + how your peer might have responded to *you* if h11 had allowed you to + continue. + + The default is 400 Bad Request, a generic catch-all for protocol + violations. + + """ + + def __init__(self, msg: str, error_status_hint: int = 400) -> None: + if type(self) is ProtocolError: + raise TypeError("tried to directly instantiate ProtocolError") + Exception.__init__(self, msg) + self.error_status_hint = error_status_hint + + +# Strategy: there are a number of public APIs where a LocalProtocolError can +# be raised (send(), all the different event constructors, ...), and only one +# public API where RemoteProtocolError can be raised +# (receive_data()). Therefore we always raise LocalProtocolError internally, +# and then receive_data will translate this into a RemoteProtocolError. +# +# Internally: +# LocalProtocolError is the generic "ProtocolError". +# Externally: +# LocalProtocolError is for local errors and RemoteProtocolError is for +# remote errors. +class LocalProtocolError(ProtocolError): + def _reraise_as_remote_protocol_error(self) -> NoReturn: + # After catching a LocalProtocolError, use this method to re-raise it + # as a RemoteProtocolError. This method must be called from inside an + # except: block. + # + # An easy way to get an equivalent RemoteProtocolError is just to + # modify 'self' in place. + self.__class__ = RemoteProtocolError # type: ignore + # But the re-raising is somewhat non-trivial -- you might think that + # now that we've modified the in-flight exception object, that just + # doing 'raise' to re-raise it would be enough. But it turns out that + # this doesn't work, because Python tracks the exception type + # (exc_info[0]) separately from the exception object (exc_info[1]), + # and we only modified the latter. So we really do need to re-raise + # the new type explicitly. + # On py3, the traceback is part of the exception object, so our + # in-place modification preserved it and we can just re-raise: + raise self + + +class RemoteProtocolError(ProtocolError): + pass + + +def validate( + regex: Pattern[bytes], data: bytes, msg: str = "malformed data", *format_args: Any +) -> Dict[str, bytes]: + match = regex.fullmatch(data) + if not match: + if format_args: + msg = msg.format(*format_args) + raise LocalProtocolError(msg) + return match.groupdict() + + +# Sentinel values +# +# - Inherit identity-based comparison and hashing from object +# - Have a nice repr +# - Have a *bonus property*: type(sentinel) is sentinel +# +# The bonus property is useful if you want to take the return value from +# next_event() and do some sort of dispatch based on type(event). + +_T_Sentinel = TypeVar("_T_Sentinel", bound="Sentinel") + + +class Sentinel(type): + def __new__( + cls: Type[_T_Sentinel], + name: str, + bases: Tuple[type, ...], + namespace: Dict[str, Any], + **kwds: Any + ) -> _T_Sentinel: + assert bases == (Sentinel,) + v = super().__new__(cls, name, bases, namespace, **kwds) + v.__class__ = v # type: ignore + return v + + def __repr__(self) -> str: + return self.__name__ + + +# Used for methods, request targets, HTTP versions, header names, and header +# values. Accepts ascii-strings, or bytes/bytearray/memoryview/..., and always +# returns bytes. +def bytesify(s: Union[bytes, bytearray, memoryview, int, str]) -> bytes: + # Fast-path: + if type(s) is bytes: + return s + if isinstance(s, str): + s = s.encode("ascii") + if isinstance(s, int): + raise TypeError("expected bytes-like object, not int") + return bytes(s) diff --git a/.venv/lib/python3.9/site-packages/h11/_version.py b/.venv/lib/python3.9/site-packages/h11/_version.py new file mode 100644 index 0000000..76e7327 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/h11/_version.py @@ -0,0 +1,16 @@ +# This file must be kept very simple, because it is consumed from several +# places -- it is imported by h11/__init__.py, execfile'd by setup.py, etc. + +# We use a simple scheme: +# 1.0.0 -> 1.0.0+dev -> 1.1.0 -> 1.1.0+dev +# where the +dev versions are never released into the wild, they're just what +# we stick into the VCS in between releases. +# +# This is compatible with PEP 440: +# http://legacy.python.org/dev/peps/pep-0440/ +# via the use of the "local suffix" "+dev", which is disallowed on index +# servers and causes 1.0.0+dev to sort after plain 1.0.0, which is what we +# want. (Contrast with the special suffix 1.0.0.dev, which sorts *before* +# 1.0.0.) + +__version__ = "0.16.0" diff --git a/.venv/lib/python3.9/site-packages/h11/_writers.py b/.venv/lib/python3.9/site-packages/h11/_writers.py new file mode 100644 index 0000000..939cdb9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/h11/_writers.py @@ -0,0 +1,145 @@ +# Code to read HTTP data +# +# Strategy: each writer takes an event + a write-some-bytes function, which is +# calls. +# +# WRITERS is a dict describing how to pick a reader. It maps states to either: +# - a writer +# - or, for body writers, a dict of framin-dependent writer factories + +from typing import Any, Callable, Dict, List, Tuple, Type, Union + +from ._events import Data, EndOfMessage, Event, InformationalResponse, Request, Response +from ._headers import Headers +from ._state import CLIENT, IDLE, SEND_BODY, SEND_RESPONSE, SERVER +from ._util import LocalProtocolError, Sentinel + +__all__ = ["WRITERS"] + +Writer = Callable[[bytes], Any] + + +def write_headers(headers: Headers, write: Writer) -> None: + # "Since the Host field-value is critical information for handling a + # request, a user agent SHOULD generate Host as the first header field + # following the request-line." - RFC 7230 + raw_items = headers._full_items + for raw_name, name, value in raw_items: + if name == b"host": + write(b"%s: %s\r\n" % (raw_name, value)) + for raw_name, name, value in raw_items: + if name != b"host": + write(b"%s: %s\r\n" % (raw_name, value)) + write(b"\r\n") + + +def write_request(request: Request, write: Writer) -> None: + if request.http_version != b"1.1": + raise LocalProtocolError("I only send HTTP/1.1") + write(b"%s %s HTTP/1.1\r\n" % (request.method, request.target)) + write_headers(request.headers, write) + + +# Shared between InformationalResponse and Response +def write_any_response( + response: Union[InformationalResponse, Response], write: Writer +) -> None: + if response.http_version != b"1.1": + raise LocalProtocolError("I only send HTTP/1.1") + status_bytes = str(response.status_code).encode("ascii") + # We don't bother sending ascii status messages like "OK"; they're + # optional and ignored by the protocol. (But the space after the numeric + # status code is mandatory.) + # + # XX FIXME: could at least make an effort to pull out the status message + # from stdlib's http.HTTPStatus table. Or maybe just steal their enums + # (either by import or copy/paste). We already accept them as status codes + # since they're of type IntEnum < int. + write(b"HTTP/1.1 %s %s\r\n" % (status_bytes, response.reason)) + write_headers(response.headers, write) + + +class BodyWriter: + def __call__(self, event: Event, write: Writer) -> None: + if type(event) is Data: + self.send_data(event.data, write) + elif type(event) is EndOfMessage: + self.send_eom(event.headers, write) + else: # pragma: no cover + assert False + + def send_data(self, data: bytes, write: Writer) -> None: + pass + + def send_eom(self, headers: Headers, write: Writer) -> None: + pass + + +# +# These are all careful not to do anything to 'data' except call len(data) and +# write(data). This allows us to transparently pass-through funny objects, +# like placeholder objects referring to files on disk that will be sent via +# sendfile(2). +# +class ContentLengthWriter(BodyWriter): + def __init__(self, length: int) -> None: + self._length = length + + def send_data(self, data: bytes, write: Writer) -> None: + self._length -= len(data) + if self._length < 0: + raise LocalProtocolError("Too much data for declared Content-Length") + write(data) + + def send_eom(self, headers: Headers, write: Writer) -> None: + if self._length != 0: + raise LocalProtocolError("Too little data for declared Content-Length") + if headers: + raise LocalProtocolError("Content-Length and trailers don't mix") + + +class ChunkedWriter(BodyWriter): + def send_data(self, data: bytes, write: Writer) -> None: + # if we encoded 0-length data in the naive way, it would look like an + # end-of-message. + if not data: + return + write(b"%x\r\n" % len(data)) + write(data) + write(b"\r\n") + + def send_eom(self, headers: Headers, write: Writer) -> None: + write(b"0\r\n") + write_headers(headers, write) + + +class Http10Writer(BodyWriter): + def send_data(self, data: bytes, write: Writer) -> None: + write(data) + + def send_eom(self, headers: Headers, write: Writer) -> None: + if headers: + raise LocalProtocolError("can't send trailers to HTTP/1.0 client") + # no need to close the socket ourselves, that will be taken care of by + # Connection: close machinery + + +WritersType = Dict[ + Union[Tuple[Type[Sentinel], Type[Sentinel]], Type[Sentinel]], + Union[ + Dict[str, Type[BodyWriter]], + Callable[[Union[InformationalResponse, Response], Writer], None], + Callable[[Request, Writer], None], + ], +] + +WRITERS: WritersType = { + (CLIENT, IDLE): write_request, + (SERVER, IDLE): write_any_response, + (SERVER, SEND_RESPONSE): write_any_response, + SEND_BODY: { + "chunked": ChunkedWriter, + "content-length": ContentLengthWriter, + "http/1.0": Http10Writer, + }, +} diff --git a/.venv/lib/python3.9/site-packages/h11/py.typed b/.venv/lib/python3.9/site-packages/h11/py.typed new file mode 100644 index 0000000..f5642f7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/h11/py.typed @@ -0,0 +1 @@ +Marker diff --git a/.venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/METADATA b/.venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/METADATA new file mode 100644 index 0000000..117c915 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/METADATA @@ -0,0 +1,234 @@ +Metadata-Version: 2.4 +Name: icalendar +Version: 6.3.2 +Summary: iCalendar parser/generator +Project-URL: Homepage, https://icalendar.readthedocs.io/ +Project-URL: Repository, https://github.com/collective/icalendar/ +Project-URL: source_archive, https://github.com/collective/icalendar/archive/2f0683a03c1d2182a04ae35a1fcbaad59672888d.zip +Project-URL: Issues, https://github.com/collective/icalendar/issues +Project-URL: Documentation, https://icalendar.readthedocs.io/ +Project-URL: Changelog, https://icalendar.readthedocs.io/en/latest/changelog.html +Author-email: Plone Foundation +Maintainer: Christian Geier +Maintainer-email: Nicco Kunzmann , Jaca +License-Expression: BSD-2-Clause +License-File: LICENSE.rst +Keywords: calendar,calendaring,event,ical,icalendar,journal,recurring,rfc5545,todo +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +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 :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Requires-Python: >=3.8 +Requires-Dist: backports-zoneinfo; python_version < '3.9' +Requires-Dist: python-dateutil +Requires-Dist: tzdata +Provides-Extra: test +Requires-Dist: coverage; extra == 'test' +Requires-Dist: hypothesis; extra == 'test' +Requires-Dist: pytest; extra == 'test' +Requires-Dist: pytz; extra == 'test' +Description-Content-Type: text/x-rst + +========================================================== +Internet Calendaring and Scheduling (iCalendar) for Python +========================================================== + +The `icalendar`_ package is a :rfc:`5545` compatible parser/generator for iCalendar +files. + +---- + +:Homepage: https://icalendar.readthedocs.io +:Community Discussions: https://github.com/collective/icalendar/discussions +:Issue Tracker: https://github.com/collective/icalendar/issues +:Code: https://github.com/collective/icalendar +:Dependencies: `python-dateutil`_ and `tzdata`_. +:License: `BSD`_ + +---- + +.. image:: https://badge.fury.io/py/icalendar.svg + :target: https://pypi.org/project/icalendar/ + :alt: Python Package Version on PyPI + +.. image:: https://img.shields.io/pypi/pyversions/icalendar + :target: https://pypi.org/project/icalendar/ + :alt: PyPI - Python Version + +.. image:: https://img.shields.io/pypi/dm/icalendar.svg + :target: https://pypi.org/project/icalendar/#files + :alt: Downloads from PyPI + +.. image:: https://img.shields.io/github/actions/workflow/status/collective/icalendar/tests.yml?branch=main&label=main&logo=github + :target: https://github.com/collective/icalendar/actions/workflows/tests.yml?query=branch%3Amain + :alt: GitHub Actions build status for main + +.. image:: https://readthedocs.org/projects/icalendar/badge/?version=latest + :target: https://icalendar.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +.. image:: https://coveralls.io/repos/github/collective/icalendar/badge.svg + :target: https://coveralls.io/github/collective/icalendar + :alt: Test Coverage + + +.. _`icalendar`: https://pypi.org/project/icalendar/ +.. _`python-dateutil`: https://github.com/dateutil/dateutil/ +.. _`tzdata`: https://pypi.org/project/tzdata/ +.. _`BSD`: https://github.com/collective/icalendar/issues/2 + +Quick start guide +================= + +``icalendar`` enables you to **create**, **inspect** and **modify** +calendaring information with Python. + +To **install** the package, run:: + + pip install icalendar + + +Inspect Files +------------- + +You can open an ``.ics`` file and see all the events: + +.. code:: python + + >>> import icalendar + >>> from pathlib import Path + >>> ics_path = Path("src/icalendar/tests/calendars/example.ics") + >>> calendar = icalendar.Calendar.from_ical(ics_path.read_bytes()) + >>> for event in calendar.events: + ... print(event.get("SUMMARY")) + New Year's Day + Orthodox Christmas + International Women's Day + +Modify Content +-------------- + +Such a calendar can then be edited and saved again. + +.. code:: python + + >>> calendar.calendar_name = "My Modified Calendar" # modify + >>> print(calendar.to_ical()[:121]) # save modification + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:collective/icalendar + CALSCALE:GREGORIAN + METHOD:PUBLISH + NAME:My Modified Calendar + + +Create Events, TODOs, Journals, Alarms, ... +------------------------------------------- + +``icalendar`` supports the creation and parsing of all kinds of objects +in the iCalendar (:rfc:`5545`) standard. + +.. code:: python + + >>> icalendar.Event() # events + VEVENT({}) + >>> icalendar.FreeBusy() # free/busy times + VFREEBUSY({}) + >>> icalendar.Todo() # Todo list entries + VTODO({}) + >>> icalendar.Alarm() # Alarms e.g. for events + VALARM({}) + >>> icalendar.Journal() # Journal entries + VJOURNAL({}) + + +Have a look at `more examples +`_. + +Use timezones of your choice +---------------------------- + +With ``icalendar``, you can localize your events to take place in different +timezones. +``zoneinfo``, ``dateutil.tz`` and ``pytz`` are compatible with ``icalendar``. +This example creates an event that uses all of the timezone implementations +with the same result: + +.. code:: python + + >>> import pytz, zoneinfo, dateutil.tz # timezone libraries + >>> import datetime, icalendar + >>> e = icalendar.Event() + >>> tz = dateutil.tz.tzstr("Europe/London") + >>> e["X-DT-DATEUTIL"] = icalendar.vDatetime(datetime.datetime(2024, 6, 19, 10, 1, tzinfo=tz)) + >>> tz = pytz.timezone("Europe/London") + >>> e["X-DT-USE-PYTZ"] = icalendar.vDatetime(datetime.datetime(2024, 6, 19, 10, 1, tzinfo=tz)) + >>> tz = zoneinfo.ZoneInfo("Europe/London") + >>> e["X-DT-ZONEINFO"] = icalendar.vDatetime(datetime.datetime(2024, 6, 19, 10, 1, tzinfo=tz)) + >>> print(e.to_ical()) # the libraries yield the same result + BEGIN:VEVENT + X-DT-DATEUTIL;TZID=Europe/London:20240619T100100 + X-DT-USE-PYTZ;TZID=Europe/London:20240619T100100 + X-DT-ZONEINFO;TZID=Europe/London:20240619T100100 + END:VEVENT + +Version 6 with zoneinfo +----------------------- + +Version 6 of ``icalendar`` switches the timezone implementation to ``zoneinfo``. +This only affects you if you parse ``icalendar`` objects with ``from_ical()``. +The functionality is extended and is tested since 6.0.0 with both timezone +implementations ``pytz`` and ``zoneinfo``. + +By default and since 6.0.0, ``zoneinfo`` timezones are created. + +.. code:: python + + >>> dt = icalendar.Calendar.example("timezoned").events[0].start + >>> dt.tzinfo + ZoneInfo(key='Europe/Vienna') + +If you would like to continue to receive ``pytz`` timezones in parse results, +you can receive all the latest updates, and switch back to earlier behavior: + +.. code:: python + + >>> icalendar.use_pytz() + >>> dt = icalendar.Calendar.example("timezoned").events[0].start + >>> dt.tzinfo + + +Version 6 is on `branch main `_. +It is compatible with Python versions 3.8 - 3.13, and PyPy3. +We expect the ``main`` branch with versions ``6+`` to receive the latest updates and features. + +Related projects +================ + +* `icalevents `_. It is built on top of icalendar and allows you to query iCal files and get the events happening on specific dates. It manages recurrent events as well. +* `recurring-ical-events `_. Library to query an ``icalendar.Calendar`` object for events and other components happening at a certain date or within a certain time. +* `x-wr-timezone `_. Library and command line tool to make ``icalendar.Calendar`` objects and files from Google Calendar (using the non-standard ``X-WR-TIMEZONE`` property) compliant with the standard (:rfc:`5545`). +* `ics-query `_. Command line tool to query iCalendar files for occurrences of events and other components. +* `icalendar-compatibility `_ - access to event data compatible with RFC5545 and different implementations + +Further Reading +=============== + +You can find out more about this project: + +* `Contributing`_ +* `Changelog`_ +* `License`_ + +.. _`Contributing`: https://icalendar.readthedocs.io/en/latest/contributing.html +.. _`Changelog`: https://icalendar.readthedocs.io/en/latest/changelog.html +.. _`License`: https://icalendar.readthedocs.io/en/latest/license.html diff --git a/.venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/RECORD b/.venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/RECORD new file mode 100644 index 0000000..8f53b95 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/RECORD @@ -0,0 +1,305 @@ +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/_version.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/alarms.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/attr.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/cal.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/caselessdict.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/cli.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/enums.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/error.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/param.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/parser.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/parser_tools.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/prop.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/attr/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/attr/test_alarm.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/attr/test_component.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/attr/test_exdates.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/attr/test_rdate.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/attr/test_rrule.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/conftest.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/fuzzed/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/fuzzed/test_fuzzed_calendars.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/hypothesis/test_fuzzing.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/prop/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_constructors.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_identity_and_equality.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_property_values.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_unit.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vBinary.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vBoolean.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vCalAddress.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vDDDTypes.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vDatetime.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vPeriod.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_vWeekday.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/prop/test_windows_to_olson_mapping.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_bom_calendar.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_cli_tool.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_components_break_on_bad_ics.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_encoding.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_equality.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_examples.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_icalendar.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_116.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_165_missing_event.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_168_parsing_invalid_calendars_no_warning.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_218_parse_calendar.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_27_period.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_301_add_rrule_as_string.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_318_skip_default_parameters.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_322_single_strings_characters_split_into_multiple_categories.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_336_dateutil_timezone.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_348_exception_parsing_value.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_350.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_500_vboolean_for_parameter.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_557_encode_native_parameters.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_662_component_properties.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_716_alarm_time_computation.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_720_uid_property.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_722_generate_vtimezone.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_798_property_parameters.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_802.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_828.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_issue_836_do_not_quote_tzid.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_multiple.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_oss_fuzz_errors.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_parsing.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_period.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_property_params.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_pytz_zoneinfo_integration.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_recurrence.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_6868.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_7529.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_7986.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_7986_categories.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_rfc_9074.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_time.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_timezone_identification.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_timezoned.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_unit_cal.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_unit_caselessdict.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_unit_parser_tools.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_unit_tools.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_with_doctest.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/timezone_ids.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/timezone/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/timezone/equivalent_timezone_ids.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/timezone/equivalent_timezone_ids_result.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/timezone/provider.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/timezone/pytz.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/timezone/tzid.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/timezone/tzp.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/timezone/windows_to_olson.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/timezone/zoneinfo.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tools.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/hyungiahn/Documents/code/syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/version.cpython-39.pyc,, +../../../bin/icalendar,sha256=291EKPNxZdT0RTIXCcCTipcDqW-1QKXc00j_AOjNJXQ,258 +icalendar-6.3.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +icalendar-6.3.2.dist-info/METADATA,sha256=MDET7PfG0RdjeOyrEyev7upDhbeHtir4fD7gcd9V_08,9034 +icalendar-6.3.2.dist-info/RECORD,, +icalendar-6.3.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87 +icalendar-6.3.2.dist-info/entry_points.txt,sha256=B-H9JDTr-StnCaknEjA2a7tCIYsp1C_hP8gO-6owqz8,49 +icalendar-6.3.2.dist-info/licenses/LICENSE.rst,sha256=xhB-8DVauOCBzobxbGqj-ZBoljxnZTDYOjOAnJPOtfQ,1326 +icalendar/__init__.py,sha256=lkfDec8Oz01dLIrR1sZWYXRzZfiXNDNOSuKOnnUmIuM,2306 +icalendar/_version.py,sha256=GLo8v0i7JGFXrDc3OJO2lhZbIbt_uwLZe8vB1PmsLhc,704 +icalendar/alarms.py,sha256=oO-dvp3rP-UOuc-y5tIbf_WGnNj6zO3fFCq_1bajWlE,13612 +icalendar/attr.py,sha256=1PdmAlZ9F-ys8ytZNqzGMWmvaDRWmPVyH_3kgzyPxT0,26124 +icalendar/cal.py,sha256=aneVWu6KrUpetxmqIm9nEQ3fLixaH_7hV52KmIctJ-Y,76175 +icalendar/caselessdict.py,sha256=zu7ZcBNOXennD0RGcbEvHwt7mHZqRM8GvgOplai9tO4,3555 +icalendar/cli.py,sha256=-6oOlgrNL96AKMOBejo8HtJqUxUffHw37d6l4YwZ41U,2980 +icalendar/enums.py,sha256=_uPQVRLvBJGbHC1ADR8oi_pEa-ofMOOKNgGNwGmx090,2433 +icalendar/error.py,sha256=LmT1UKMJg1R4_cBQ3gSRDah7VoSXR1eeg5L9v6baXw0,1767 +icalendar/param.py,sha256=3L_Ua3mCLkk-SrYagKyDrkRxXI2zrBoSPdrFA44jg04,17076 +icalendar/parser.py,sha256=QvDTGRN80S5690HYSgt_aw0lbG_8KZWS_VUJ4NeTboI,15024 +icalendar/parser_tools.py,sha256=dC-fh2TQl7W1qpqiFbWAtDtcw8HOaf8SEMX59xEAHz4,1867 +icalendar/prop.py,sha256=uFYZVf1kWSEk3wGc1a_olBO-Fj94bS07pdyy45SL0JM,62487 +icalendar/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +icalendar/tests/alarms/rfc_5545_absolute_alarm_example.ics,sha256=OEtl5s3VH5i4bf2b_Fa1ZCXXmteGBw_5Rn3XnM1x210,170 +icalendar/tests/alarms/rfc_5545_end.ics,sha256=E03TaFWnle3vCAQ9y2jQjgZH1hF_2JX9YghcqenPs7k,409 +icalendar/tests/alarms/start_date.ics,sha256=2YUvmY0m232ukF2SG67UejQlikCf3n6pr7Ychlte4Cs,411 +icalendar/tests/attr/__init__.py,sha256=Xd9NJyKNCyr67nf9acCWPdfSKCv76hbexvzcRxkrpKY,45 +icalendar/tests/attr/test_alarm.py,sha256=P2WXeC0aiPTyZh5CxMn6E3jQg4bNK16ZL3qjQt_iTEk,1111 +icalendar/tests/attr/test_component.py,sha256=88na2F19J55JrNcq3cKTvOGMPeGuvQUR1jr6esKrFEA,3077 +icalendar/tests/attr/test_exdates.py,sha256=BBQlE5JlNFhR_-GfzpRNXobherwB4tW_suJ6X_Bi7E4,1734 +icalendar/tests/attr/test_rdate.py,sha256=KGsXW383ApOfjQ7VmxDpcE1CpPi8mH8SpnZEYewGPfM,3261 +icalendar/tests/attr/test_rrule.py,sha256=4q4G1lkZcUJas2ZwPoomofS8rmxSIoJmfRr0I2Rg0iA,1284 +icalendar/tests/calendars/alarm_etar_future.ics,sha256=8cKaydJ5UAjuvGwYZFRkgvxEiX5HyA7F0N6OxvPP0eE,5178 +icalendar/tests/calendars/alarm_etar_notification.ics,sha256=eBNu9cK4GjCGtUVOeWjXxu28LbkcESSFxFyDCn9UAdw,5178 +icalendar/tests/calendars/alarm_etar_notification_clicked.ics,sha256=Eo26cFPcfnyu_knNtMmPuWXF1N7kD8NAtTC5DYTt1hQ,4993 +icalendar/tests/calendars/alarm_google_acknowledged.ics,sha256=pDBop7zq9RKja0Usms82gRecW_rPp5dF72_k6XmOwMA,1326 +icalendar/tests/calendars/alarm_google_future.ics,sha256=YUO9_kz_WIZptsWC3LZIE0dhjBaANNhBMqXc_HiZJ9A,1326 +icalendar/tests/calendars/alarm_thunderbird_2_future.ics,sha256=JsAO2aUaXBOVGK3Ak8kwbuD0xnyea-MYiJa-9_4E6Hg,14188 +icalendar/tests/calendars/alarm_thunderbird_2_notification_5_min_postponed.ics,sha256=kCZ97a3HiISUjB5AT4wuPFSIkf_kBb0ZaLjyarE7MqE,14256 +icalendar/tests/calendars/alarm_thunderbird_2_notification_5_min_postponed_and_closed.ics,sha256=3tjy0qsXjgaeORsXgIueHnJ2lMEXU5TifykVPT9HjuQ,14220 +icalendar/tests/calendars/alarm_thunderbird_2_notification_5_min_postponed_and_popped_up.ics,sha256=kCZ97a3HiISUjB5AT4wuPFSIkf_kBb0ZaLjyarE7MqE,14256 +icalendar/tests/calendars/alarm_thunderbird_2_notification_popped_up.ics,sha256=JsAO2aUaXBOVGK3Ak8kwbuD0xnyea-MYiJa-9_4E6Hg,14188 +icalendar/tests/calendars/alarm_thunderbird_closed.ics,sha256=IO_aPY2Gl-84swNLw50HJrJIGWrVFfHDsAE92snVwTs,14233 +icalendar/tests/calendars/alarm_thunderbird_future.ics,sha256=WfEAUs7flHVOYkv5ooH3HzbO7c7R0JMTEeODH_JpsvI,14201 +icalendar/tests/calendars/alarm_thunderbird_snoozed_until_1457.ics,sha256=ZtS77-V8BD4Lb2UBDmhLkoqOgsKeLYYNSfkfmPvKybQ,14269 +icalendar/tests/calendars/america_new_york.ics,sha256=bmv-Plq4X0cq5SdEpgf8Ia8vayKwzyNQRlyCw4wl2zA,1372 +icalendar/tests/calendars/america_new_york_forward_reference.ics,sha256=cgp8EZHxrZJw4fCZ93RyOyEzQSnJ6X8-y1AF6vI1BZk,1428 +icalendar/tests/calendars/big_bad_calendar.ics,sha256=6PfpckE9rLs6mLfIvn9BvT7ULB6xLkGJYytcL2fwrZ8,496 +icalendar/tests/calendars/bom_calendar.ics,sha256=zqiTmYM_OEQmNhE1QfQnO3brsLhQy9Tivgl12uvC_1g,35 +icalendar/tests/calendars/broken_ical.ics,sha256=TXWDdsRdEMjokn6C9KPrPwI9RjTmOnSgzT8-FDEy2C8,144 +icalendar/tests/calendars/calendar_with_unicode.ics,sha256=TQ6GyZV9CtMGqodlke4OxhMlnJ0Ku4dZW2PN820Pl_A,198 +icalendar/tests/calendars/created_calendar_with_unicode_fields.ics,sha256=0SNDIvg0MtpR5wYkq3DwJgVplC6Kg4xYcclfRZlATM0,563 +icalendar/tests/calendars/example.ics,sha256=w5DDci3CJyzph1te8B-V7vHVEO6HTMsYKyJuFktFSQY,839 +icalendar/tests/calendars/issue_104_broken_calendar.ics,sha256=LihNyMsVnDtBWSo6m7mJduVOzE1lKyv8iZsaocEoW2k,235 +icalendar/tests/calendars/issue_156_RDATE_with_PERIOD_TZID_khal.ics,sha256=v6lJ3mah714hnf8bOW6Ws0sFjYf9V9P-LdsYEhrJxWM,1211 +icalendar/tests/calendars/issue_156_RDATE_with_PERIOD_TZID_khal_2.ics,sha256=nS_80qbyV3GUQY_z97lJ4HLCjsYhsfpHcFQ38dLNKIw,2126 +icalendar/tests/calendars/issue_165_missing_event.ics,sha256=X2kkhgFsDM07MAAOJwUDx4tJoNYoJzY8bKm6kZiH-2U,788 +icalendar/tests/calendars/issue_168_expected_output.ics,sha256=YCE6F_c2YmKglhi-zYQVBCWF0dUR1Yb5UV-177T9FgY,117 +icalendar/tests/calendars/issue_168_input.ics,sha256=e0nYj-_NZRO8vTn66PnEnSIvHfa6Ys1cl2MSE9QENuo,151 +icalendar/tests/calendars/issue_178_component_with_invalid_name_represented.ics,sha256=2mv6UPQFHXKADU_3NQ-vvkoat9pfKH7oM-OVmyZ-gQQ,26 +icalendar/tests/calendars/issue_178_custom_component_contains_other.ics,sha256=61Ly68x8ht3WYIveh5q99rHvo_sGdCeiUJoULX9ceds,112 +icalendar/tests/calendars/issue_178_custom_component_inside_other.ics,sha256=cJ-zxlSKsClJ4NrLl9gF2ySn5ot120pv6PmpgN3GCqM,70 +icalendar/tests/calendars/issue_218_bad_tzid.ics,sha256=QCV2z0ytgId0eiMPU4BWupoupM4u0QcxA1qOSpgSOd8,446 +icalendar/tests/calendars/issue_237_fail_to_parse_timezone_with_non_ascii_tzid.ics,sha256=P-WlWKenJ3bonDAOPxl0bQcj71yAyU2D5vAfc-L0HE0,565 +icalendar/tests/calendars/issue_27_multiple_periods_in_freebusy_multiple_freebusies.ics,sha256=vHCx9MgVXSxTE7C0q4ea11dT49kM1r7mCOBgeLxj61E,740 +icalendar/tests/calendars/issue_27_multiple_periods_in_freebusy_one_freebusy.ics,sha256=GndHfm7VbYcn3O0n8tnnq3B7Dd0JysiJU8CLvF8xTIg,593 +icalendar/tests/calendars/issue_322_expected_calendar.ics,sha256=FhHi-xDErrR8uFs2Vqy5bKtKnKKjlAqMnl5pRi7szWo,137 +icalendar/tests/calendars/issue_348_exception_parsing_value.ics,sha256=KHQ-hJEeDtOtOm6zle16Vl-NWwWibgW14jmnWSAFZbM,1442 +icalendar/tests/calendars/issue_350.ics,sha256=CVQ1mWSo8l8kSxlqd_AJMFhbC45_KyX09YKzvl7mpfw,1350 +icalendar/tests/calendars/issue_466_convert_tzid_with_slash.ics,sha256=v80zLk1HtVKmtiqtjuxvflOqp4WE_UaKManzMKKJKuU,325 +icalendar/tests/calendars/issue_466_respect_unique_timezone.ics,sha256=EZ-wXiru9oEWTotrvF5TjXsQHQS2H_Db802g-DOHcMc,635 +icalendar/tests/calendars/issue_526_calendar_with_different_events.ics,sha256=NvwZMuODENZyNzGx93HHXior0t8qBfwgtAA05C4_LmM,560 +icalendar/tests/calendars/issue_526_calendar_with_event_subset.ics,sha256=jbnzcpLWKqjQ9cOE_sd_I8xiHxtsF1-N2GuzXhFRGEk,281 +icalendar/tests/calendars/issue_526_calendar_with_events.ics,sha256=ZAMoku6MRa_ygxjTJWHFixLYxksnVIEyWnR_UuhQ7_o,500 +icalendar/tests/calendars/issue_526_calendar_with_shuffeled_events.ics,sha256=Z9QWtL6fzYNbbjSKIlzqvd9RRCxsAylvPIK_T5AqdM8,500 +icalendar/tests/calendars/issue_722_missing_VTIMEZONE_custom.ics,sha256=GY12TCaiYufIjgblrBQPr_1gLGoMKh8xkCZRj1GlngE,111 +icalendar/tests/calendars/issue_722_missing_timezones.ics,sha256=cpkodPHNJgYEo9wlFfftss8m-MipN4_pnOQ9tf5CCWA,649 +icalendar/tests/calendars/issue_722_timezone_transition_ambiguity.ics,sha256=1JqmhFRPeRL-TyxGI1X-9cO_UTSCcNk6QlQMB9Rs91M,1049 +icalendar/tests/calendars/issue_798_freebusy.ics,sha256=4yTDpQrT427UIOELr5Uo2MvtvawHIF3Q3N8uygh3MUA,331 +icalendar/tests/calendars/issue_798_related_to.ics,sha256=25PnhBEKf5KEw-rkgJavKnh48_1Q8-kIf9vHNdZvwKA,204 +icalendar/tests/calendars/issue_836_do_not_quote_tzid.ics,sha256=-LZzbPhNI8R4V6RzOa4667S3WAdOHY57gNSvg4L_NaI,662 +icalendar/tests/calendars/multiple_calendar_components.ics,sha256=nvZimBFaXEVyTfqUJNA6l5iqT3014e2dQNGW8Wcr754,948 +icalendar/tests/calendars/pacific_fiji.ics,sha256=jeW74IQzVzg7PZ9QhgY3LOiMhRt9zmIbvcih5dU0nKA,1222 +icalendar/tests/calendars/parsing_error.ics,sha256=SSUZZxW8aOD__J0wdcD5vFI_OV-31HszeflxBKNYM3w,490 +icalendar/tests/calendars/parsing_error_in_UTC_offset.ics,sha256=c72-fHFVs6-fyJWQFfkHNl2glhGpKDbo8YcocMBh7rY,189 +icalendar/tests/calendars/period_with_timezone.ics,sha256=8_utcIlfMhbTVW9TE2OB3MUspKGvlmmbV9X1UPdKA5U,744 +icalendar/tests/calendars/pr_480_summary_with_colon.ics,sha256=53HNwW5fIhCq-1rC3XFppiB0jrSObUWi9gpaLyoex6I,165 +icalendar/tests/calendars/property_params.ics,sha256=4lfRhUIIE7TaZ19K95yr8q0D0IpjmYVC3Yl1AN4kvFQ,660 +icalendar/tests/calendars/rfc_5545_RDATE_example.ics,sha256=QPAM2WRTfIVjXyUBepNstvhDJcjr6GyZlL-I51hFb9g,756 +icalendar/tests/calendars/rfc_6868.ics,sha256=nURkH2HnHZXnkSSTirypQLmyFr-IIeVlpsZpfA1G1So,169 +icalendar/tests/calendars/rfc_7529.ics,sha256=VEPf9NFl2hJgJ7hvTTr0LAbz42B-MxK3g8yuOPUj5P4,635 +icalendar/tests/calendars/small_bad_calendar.ics,sha256=FfwBkz9k5fOKf4QzgieFV2Hpk71Z1GkBK6-5HxVCUHE,40 +icalendar/tests/calendars/time.ics,sha256=Rmdx4dNgyxkdOU-ZCndNZreq8uqz9WYaJmz_QIrQ0yI,59 +icalendar/tests/calendars/timezone_rdate.ics,sha256=9wZ1zds7ha4vC8so4AzF71yz0Yw1YCKi5LTaBKsfkq8,1109 +icalendar/tests/calendars/timezone_same_start.ics,sha256=ehLTQtA_mwgZk2NLXDOzr9_K8yYdYAGthc8Br-DsvUY,685 +icalendar/tests/calendars/timezone_same_start_and_offset.ics,sha256=IrMUtrcECSl1iIb6cJ9AVy1MQw81U7qYTNTEJuaVsmA,475 +icalendar/tests/calendars/timezoned.ics,sha256=AP5V2Qe15tWKjM8wddVDiFSQgJftVTNiclIEYGv4vko,812 +icalendar/tests/calendars/x_location.ics,sha256=mEvrn2sny3FRmLKe81b2534C-d1eUqarENtuZvd9sTw,1402 +icalendar/tests/conftest.py,sha256=AdpAuWQXuo-gGjhKLruuXXr-QU0Ki7MlA_Zb750sb6k,9355 +icalendar/tests/events/event_with_escaped_character1.ics,sha256=zF8gT7zwQpd6M5nzhUgOKZ5o-BlQpqCkI7B7UULzdvw,60 +icalendar/tests/events/event_with_escaped_character2.ics,sha256=XNIPhLa6q4ndJqxbR7ePeUcQGrUGXsAlDe9FK7iJl50,60 +icalendar/tests/events/event_with_escaped_character3.ics,sha256=bw9rORvFz9QFqt0QjM_18-ltthQEcCugmKSGbzElUN8,60 +icalendar/tests/events/event_with_escaped_character4.ics,sha256=dPnEcJG-dsq_Q_wxnpEJNqiQs2aWHOxGKNUyDyAxWBA,60 +icalendar/tests/events/event_with_escaped_characters.ics,sha256=ckmvjK8xXcvm71THlyrW8uojsDE7z6TENIepjhxPbNY,104 +icalendar/tests/events/event_with_recurrence.ics,sha256=xcz-snNFFX7m3B-M6MtrbRZglBbzIWxIrCwwnK-VoXc,200 +icalendar/tests/events/event_with_recurrence_exdates_on_different_lines.ics,sha256=isbt8cZbZ9uHnxXk3-YsSAAlp1xPtDT2C_NFPwY00rE,456 +icalendar/tests/events/event_with_rsvp.ics,sha256=e3k2PahxC7XNkHXXXvxU8Q1OKbGzEMd0_gjrV3J3upc,73 +icalendar/tests/events/event_with_unicode_fields.ics,sha256=WH2XqEfW0iKrouy3jBawpyfClDN6R7zISjRNgJUq4kk,254 +icalendar/tests/events/event_with_unicode_organizer.ics,sha256=3T_e94HikWETtTxcv-WINKxOrwl-ObZ5S342FoTAP1E,86 +icalendar/tests/events/issue_100_transformed_doctests_into_unittests.ics,sha256=TByjhHIJJRBIP0vNUIgy2D3TFHFhglKjOgjSbU3sswI,53 +icalendar/tests/events/issue_101_icalendar_chokes_on_umlauts_in_organizer.ics,sha256=yfrDws5GOzxFM0XLkBQFQ_n9H9vyMXWEQx-DQYnvQiY,359 +icalendar/tests/events/issue_104_mark_events_broken.ics,sha256=FhIt8k7HQNf-UYAstJUq1WEjad0jaUCWTKYmkVCJogI,164 +icalendar/tests/events/issue_112_missing_tzinfo_on_exdate.ics,sha256=IenYrPhj9BaX0Ojmu8seECuHpW74jEe8iROcalMrgf4,670 +icalendar/tests/events/issue_156_RDATE_with_PERIOD.ics,sha256=pAk4wI1ivi65cBrGwt7HxvRyKQFgGvGsNFNglVQVsSA,170 +icalendar/tests/events/issue_156_RDATE_with_PERIOD_list.ics,sha256=u8FPZwmJFWZC4dXNemgw85a4URfZC9PN2qpnOc9m-RM,198 +icalendar/tests/events/issue_157_removes_trailing_semicolon.ics,sha256=6ZGaQltWRyUBc2ZCnrgSqU4NYj1kxij-BxAHVlkTsnk,92 +icalendar/tests/events/issue_184_broken_representation_of_period.ics,sha256=xGHlViVfPzQPjKnsG8Ntr0dnWPkXu6pFtw6q3r96XmE,131 +icalendar/tests/events/issue_464_invalid_rdate.ics,sha256=DQm7c4S2Wgfl69Gvaumj-zADZJzZ5tkzQvPNXMNQJ74,193 +icalendar/tests/events/issue_53_description_parsed_properly.ics,sha256=HSjknOesLHfr9b0MP8f97-5k7qmoe9Mf5-7lrr1wC5w,739 +icalendar/tests/events/issue_64_event_with_ascii_summary.ics,sha256=d1NMR5EImhYrzR7oUNmPvK5Jxa5LDd8roN9aB-ck0oc,42 +icalendar/tests/events/issue_64_event_with_non_ascii_summary.ics,sha256=ir4PpRbsIGtXdssK-jru6ZgmY31XJQINaq6HvhMxvGU,42 +icalendar/tests/events/issue_70_rrule_causes_attribute_error.ics,sha256=7qtWoq75oBuEM9dauNSSZnsgeebVvSg_GxuIE0USC4k,279 +icalendar/tests/events/issue_82_expected_output.ics,sha256=N7aB6IoCpleUVcnOYSinEDOl9haMnc4ZxQJLcr4h9aU,91 +icalendar/tests/events/rfc_9074_example_1.ics,sha256=hztVnUDZ3e6Kp6DSkwKwvt7fJ9KodBd78ta9YKPfZhQ,344 +icalendar/tests/events/rfc_9074_example_2.ics,sha256=StG7r8cvBmniI6WKkaBH15DfQ-i8Dso3TtZdoffPmlQ,587 +icalendar/tests/events/rfc_9074_example_3.ics,sha256=Jj1KlOo34uUkmkVwY4t5dl-ct1prpj9oGasg1gxbvx4,587 +icalendar/tests/events/rfc_9074_example_4.ics,sha256=MRt9Wt7GjfMqV-B_0ezCCFMuuQzPMozvK_LRTocJG1M,616 +icalendar/tests/events/rfc_9074_example_proximity.ics,sha256=rr29kD4Yd8bRuMPdDKRYXM2g7KZzZXavHmDicwKenQ4,292 +icalendar/tests/fuzzed/__init__.py,sha256=O1_cgbjDwb5mzs-tYy8NtUznwF2vkZmAumss1owgJSo,721 +icalendar/tests/fuzzed/generate_python_test_cases_from_downloaded_clusterfuzz_test_cases.sh,sha256=0Jc9Msyj_w4skwALytFuJ0vyzlybIjmxbf_m7jlbLPQ,1644 +icalendar/tests/fuzzed/test_fuzzed_calendars.py,sha256=Q0qdUcKyC_vUmc-dA4pP21P4nlZjuiEHpT5NXy5NCVg,343 +icalendar/tests/hypothesis/test_fuzzing.py,sha256=p5nKOC4LdPn07CwGxDvYNERFqqetANh262N3g9lFW9Y,1021 +icalendar/tests/prop/__init__.py,sha256=H1sqpnGs6CFBQOUoLw7rDL15JS8VzAIZ_N3JUCn3pgs,42 +icalendar/tests/prop/test_constructors.py,sha256=HFSfvxzHvf1ZHrkFbXgSG8B4XLk2QBh5rLqX1yLjtPM,3258 +icalendar/tests/prop/test_identity_and_equality.py,sha256=-QlTNk9mlipYQ-cvQkABJMK450jdqeLP8quMSfCZKN4,1869 +icalendar/tests/prop/test_property_values.py,sha256=ycucOYR-FQjnfPJ7wn4-pKzBacIBbAcHR--S929geEk,649 +icalendar/tests/prop/test_unit.py,sha256=gAaxfaMk_4kGiL-694pknm7tyuoRo7ewKacTFLk3-q8,13900 +icalendar/tests/prop/test_vBinary.py,sha256=5hiuexUU-li2cdcfB47spjvEROjvQesxkRTBmJqlOzM,1214 +icalendar/tests/prop/test_vBoolean.py,sha256=eGXDzrosXvjPi97cLHqrpE5B_qXEZjpT0YaWWkyeHjo,439 +icalendar/tests/prop/test_vCalAddress.py,sha256=pVXohea8hgAMx-u12AZkns5XTGZOUm4uV2mBYwaT4v4,1420 +icalendar/tests/prop/test_vDDDTypes.py,sha256=Ai8T1HdlPV9oslmBvk7Tae573Tyxyj94I_qUXUSTpJg,989 +icalendar/tests/prop/test_vDatetime.py,sha256=d_9qXDqxAjUy--wUQCp3dlBfdidyLdFNC2VpOWvJaKY,1417 +icalendar/tests/prop/test_vPeriod.py,sha256=gG7F12q-bG67f9fAOiltuNTEmJhdFG1_9-EHQxE0Gak,2155 +icalendar/tests/prop/test_vWeekday.py,sha256=OAHnACj-_ySU8iUMlA1hFI1awAbxwM08qklYTJ-0_7A,622 +icalendar/tests/prop/test_windows_to_olson_mapping.py,sha256=xxEsJ02BZSTgcn1TMU6LTTBDge48iIqaDbyFSpx9CDc,722 +icalendar/tests/test_bom_calendar.py,sha256=BnnM8ROzW-cqWhWwDSZvE7tMuKYeYsHd4EoNtuuLHPA,159 +icalendar/tests/test_cli_tool.py,sha256=hdyPqMGmHlMuuIrYmCBZbdCxTTEJNQhn8lYxPbsMKmw,2728 +icalendar/tests/test_components_break_on_bad_ics.py,sha256=RLuNP0bjgqtebwe4Au6ggTpED84oz7Bf5F7YNfVrdVg,1602 +icalendar/tests/test_create_release.sh,sha256=x5BL5AsjUnCkfpTI8HwqBGQ49hhIk5EhgH52UEvXUU8,713 +icalendar/tests/test_encoding.py,sha256=zK21I-d90fjamKBDFxvbk_MUPxjysTgOOuRBo73MW4Q,3605 +icalendar/tests/test_equality.py,sha256=lkge7ERgQBXT4mIorc1olBhSJEfLj-auuTGa_JWRFoY,5644 +icalendar/tests/test_examples.py,sha256=XC1QrEHOxCcKKq-mxcumZJ0t4XHW00nut8CgvV5xEFc,2582 +icalendar/tests/test_icalendar.py,sha256=hcCa_5MtjZPXT2mYVaqWnEOEmx8nnIuvokLc0pZt8ZU,10974 +icalendar/tests/test_issue_116.py,sha256=x7ysJmtrOer1eTGnBHA1m87iCykag1AU5Z97uQbvJBc,949 +icalendar/tests/test_issue_165_missing_event.py,sha256=cwEsiKRt63nBwx8VrWMNBGTNCQBZW3b4WvG8fuxtHWg,331 +icalendar/tests/test_issue_168_parsing_invalid_calendars_no_warning.py,sha256=fnapZJX2PDXSc_IbQSYXQw3Mwu5UsAn7ZtKZ4ABaCAA,561 +icalendar/tests/test_issue_218_parse_calendar.py,sha256=83tl7V0Vas_fYQZkBITzfKR1I7_5FptsQbu-cK7Xfpw,983 +icalendar/tests/test_issue_27_period.py,sha256=_jEjai72oRjlptTy587wfA9LtQ_jcn9j5e1LVw25G2M,648 +icalendar/tests/test_issue_301_add_rrule_as_string.py,sha256=Gq-dDIojqgPR_7cefDRUVGADbYQBTP1VXMQtE5u6n_Y,410 +icalendar/tests/test_issue_318_skip_default_parameters.py,sha256=rlh7e5-pbz2-tWUdaBNguL55g1Zh_HifkmsBhp0pgr4,674 +icalendar/tests/test_issue_322_single_strings_characters_split_into_multiple_categories.py,sha256=2Dz4NsZ3iJM9tFlMiay99xjB2sjbG41Yrpzjl92BY0c,392 +icalendar/tests/test_issue_336_dateutil_timezone.py,sha256=ZdTe_MDYGS9Q7sadv88zWxEuQCuaQuOe3El7NfSiaDo,861 +icalendar/tests/test_issue_348_exception_parsing_value.py,sha256=2uKTqZwkdLepBtuFx6yzKWEBI_Ek0wN2fuzF2-bCsi0,829 +icalendar/tests/test_issue_350.py,sha256=0F2iMhb_EVG8zLIqnYkaRhQRz7aTJgwlAfY2mpdpafM,286 +icalendar/tests/test_issue_500_vboolean_for_parameter.py,sha256=fFRFMuVleqOM2R5ZTO2yLsMybVuNTvQwhHXU3CEGevo,395 +icalendar/tests/test_issue_557_encode_native_parameters.py,sha256=IJl4AFkJ_HRkD5eYW_fTZTwE5KQnKPZgD-SUrOElwTE,5061 +icalendar/tests/test_issue_662_component_properties.py,sha256=AEnCt60InY3b5DBTM2DGEO4CFWIBz0WP9NK1ieknHiA,21494 +icalendar/tests/test_issue_716_alarm_time_computation.py,sha256=q5hwwxaqf_NJnE34KnSJv429HKHobpsP-Y9tQYMoSqo,15232 +icalendar/tests/test_issue_720_uid_property.py,sha256=Xci4300VUPAFq0OBAnx4VdLCx5-bcsUGyGXLr593ppE,245 +icalendar/tests/test_issue_722_generate_vtimezone.py,sha256=niIdyVJqRe5iFAY-hSFIj5rpeKw8ujK5K75Rocf-mKI,16520 +icalendar/tests/test_issue_798_property_parameters.py,sha256=k4v5jVYYlTMh1_2f_l3_Ns3WaVaoyDvs4TwkKTWQ978,4722 +icalendar/tests/test_issue_802.py,sha256=BrNoIF4Ymteu_WOTZ3Qooh7Oa9g7KEQcIHEnEAKRtXs,1425 +icalendar/tests/test_issue_828.py,sha256=zuRCCFPsO1ZGUt8O_zWQSwLudzyNYiAZ2r2KB_LMYzc,4349 +icalendar/tests/test_issue_836_do_not_quote_tzid.py,sha256=MR-SXMDB4HN52YdV2tau88yGB_JqZb8HkdJ8EnRJpVg,2381 +icalendar/tests/test_multiple.py,sha256=kykaUsa97DjBdTs0nsRrs5GOGFME9hJFkeZYMgYlZJs,1001 +icalendar/tests/test_oss_fuzz_errors.py,sha256=KIxpAryRmU69__IcobgsgHoPipwZbyA6_pajLmTQKeQ,583 +icalendar/tests/test_parsing.py,sha256=GNAuEiF4f1p_IkCtuZhMJw7ONektdmjb07cwM2i0yrg,9158 +icalendar/tests/test_period.py,sha256=KV5ehuPoQqXB-lwk3AmxzzG6ryoaBjtWzIEXMSU4xN8,4002 +icalendar/tests/test_property_params.py,sha256=HJTXzMha3ZdrUK5upoQCNgdxqNHkYxwV2HpFvVyXTlQ,5182 +icalendar/tests/test_pytz_zoneinfo_integration.py,sha256=2g4GQPI_PO4bRvjvJCH-nfhzCoeTwTfQo5guY4D2MCU,3261 +icalendar/tests/test_recurrence.py,sha256=4UCIjK96r5_Lh783HhtVT4lIzSXea1lNzp8iuOsTFTA,5091 +icalendar/tests/test_rfc_6868.py,sha256=c4ceCXLfgm7UPCDLweQN_3VlfFdYWKIsAS3H1zcKFQ8,3110 +icalendar/tests/test_rfc_7529.py,sha256=T_hXtTVCkbSrp9E30YBQk0eeo4j6lEGSghXHg0u4_d8,2507 +icalendar/tests/test_rfc_7986.py,sha256=FyNLIJUDAIK2GbFZaRzIobZdNZi6gm71LQA-qoLnfj8,5890 +icalendar/tests/test_rfc_7986_categories.py,sha256=hGhDrb_3EcC4IX80DGUZdrIRltJhzN-DUKFZtaSnmEM,2245 +icalendar/tests/test_rfc_9074.py,sha256=WQ6KSrbAKJTvNyACq8f1WhRxHrSOxFD2UH_ZxiiwyZ0,1477 +icalendar/tests/test_time.py,sha256=zSkeKbTPZKTD0JRoBZt3jKBZ2Ug0kvqZb4IOARlO23w,883 +icalendar/tests/test_timezone_identification.py,sha256=dDx4y84cGbP-2Q81hTKpItKTWpzEdD7HGDroyAmt5_g,1180 +icalendar/tests/test_timezoned.py,sha256=9vOiXVCc7tlzn4jGs-2ntdykWy1NXfzt-s5Pu3g_AYo,18400 +icalendar/tests/test_unit_cal.py,sha256=ctQe3UqMe8eUhhcfK51EvFmgAuuXmRr8INHiOHzofBw,16585 +icalendar/tests/test_unit_caselessdict.py,sha256=pQmXjBf6GHeie6IDocw5qfutfCFHKWx2srgTYiT56QA,3608 +icalendar/tests/test_unit_parser_tools.py,sha256=xsihLP91N_oOV5ms9CAKy04CkiIM_5lk7rNkmgvnjAc,1190 +icalendar/tests/test_unit_tools.py,sha256=sLtZDJx7POpUiwVSUWRR2VzVPaHl5qT49ptfZ_MKfBc,2386 +icalendar/tests/test_with_doctest.py,sha256=9jHCwwNdzk9CdgPXWUQRDjdluRXTYR6fHh-N0AMo4W4,3024 +icalendar/tests/timezone_ids.py,sha256=GFQM-hHagXTdnJd4-Ik4HjfJb9I1V9mYN85UEzrFyBE,13308 +icalendar/tests/timezones/issue_237_brazilia_standard.ics,sha256=67bg-GY1F6EwBrwwXPBJ-i5m1IgTWa4oWlMfrvagPgg,400 +icalendar/tests/timezones/issue_53_tzid_parsed_properly.ics,sha256=S9eBMuhGxVgqzcjqcFXRnVDtQemXRUm-KwmZYfYOZwo,434 +icalendar/tests/timezones/issue_55_parse_error_on_utc_offset_with_seconds.ics,sha256=OQzQAWv7uAqCxpLyMztp2TyBalYZKTlUYlEH-COoZag,188 +icalendar/tests/timezones/pacific_fiji.ics,sha256=KmWDaMGEE6M6pkwnF-nqAV7pTGgaRqMoOuSMfW9VRhs,929 +icalendar/timezone/__init__.py,sha256=TWse38Yp5FnR2rOmDn0K9Pbj24Pv8_kXPCbMjJFTMaU,549 +icalendar/timezone/equivalent_timezone_ids.py,sha256=2dkQg8AMyDBNqtF9MiirYY66SZoujRzJ2Yvgb7HWjxM,4665 +icalendar/timezone/equivalent_timezone_ids_result.py,sha256=TZlo7AABJ2ePXhQakXIcXuwj_wE5G99vxeWsMO54kIA,219402 +icalendar/timezone/provider.py,sha256=zk9q9YpFChFdYa--Q_wZkuAp7rcII5HLeW9gVAzo_fM,1598 +icalendar/timezone/pytz.py,sha256=ut8Pizhs2EN_v8G4HtA43pcEK_iSZuXkI1rLpt9qdTI,2317 +icalendar/timezone/tzid.py,sha256=YB9uJoy1gEICWaDp124UmrRP8kzaUn1BdkL4Hvm1MMM,4093 +icalendar/timezone/tzp.py,sha256=e0AXHXbWrvSzLWX0okLBJDs2PcKPpqsmxdhhd5JTGek,4861 +icalendar/timezone/windows_to_olson.py,sha256=n7pHvSiBR-C2K-SHp8lKMjF6Fdz8_4lZKUrkft0hEho,5632 +icalendar/timezone/zoneinfo.py,sha256=k-xrYuUIxXGlfbviJ0YE5cEpyxlfLXgUpJZ7bYch5QQ,5033 +icalendar/tools.py,sha256=zKUieyh5A_jCc5KW6bDCMGhEed__Q7dXK8qw3Ykm0AU,3059 +icalendar/version.py,sha256=B7rIFvfKIhqRt7FiUM8UL60St7aOmvXZX21mpPVJHNU,385 diff --git a/.venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/WHEEL new file mode 100644 index 0000000..12228d4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.27.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/entry_points.txt b/.venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/entry_points.txt new file mode 100644 index 0000000..30fcf77 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +icalendar = icalendar.cli:main diff --git a/.venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/licenses/LICENSE.rst b/.venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/licenses/LICENSE.rst new file mode 100644 index 0000000..e4e241c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar-6.3.2.dist-info/licenses/LICENSE.rst @@ -0,0 +1,26 @@ +License +======= + +Copyright (c) 2012-2013, Plone Foundation +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. + +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/.venv/lib/python3.9/site-packages/icalendar/__init__.py b/.venv/lib/python3.9/site-packages/icalendar/__init__.py new file mode 100644 index 0000000..902a736 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/__init__.py @@ -0,0 +1,128 @@ +from icalendar.alarms import ( + Alarms, + AlarmTime, +) +from icalendar.cal import ( + Alarm, + Calendar, + Component, + ComponentFactory, + Event, + FreeBusy, + Journal, + Timezone, + TimezoneDaylight, + TimezoneStandard, + Todo, +) +from icalendar.enums import CUTYPE, FBTYPE, PARTSTAT, RANGE, RELATED, RELTYPE, ROLE +from icalendar.error import ( + ComponentEndMissing, + ComponentStartMissing, + FeatureWillBeRemovedInFutureVersion, + IncompleteAlarmInformation, + IncompleteComponent, + InvalidCalendar, + LocalTimezoneMissing, +) + +# Parameters and helper methods for splitting and joining string with escaped +# chars. +from icalendar.parser import ( + Parameters, + q_join, + q_split, +) + +# Property Data Value Types +from icalendar.prop import ( + TypesFactory, + vBinary, + vBoolean, + vCalAddress, + vDate, + vDatetime, + vDDDLists, + vDDDTypes, + vDuration, + vFloat, + vFrequency, + vGeo, + vInt, + vMonth, + vPeriod, + vRecur, + vSkip, + vText, + vTime, + vUri, + vUTCOffset, + vWeekday, +) + +# Switching the timezone provider +from icalendar.timezone import use_pytz, use_zoneinfo + +from .version import __version__, __version_tuple__, version, version_tuple + +__all__ = [ + "Calendar", + "Event", + "Todo", + "Journal", + "Timezone", + "TimezoneStandard", + "TimezoneDaylight", + "FreeBusy", + "Alarm", + "ComponentFactory", + "vBinary", + "vBoolean", + "vCalAddress", + "vDatetime", + "vDate", + "vDDDLists", + "vDDDTypes", + "vDuration", + "vFloat", + "vInt", + "vPeriod", + "vWeekday", + "vFrequency", + "vRecur", + "vText", + "vTime", + "vUri", + "vGeo", + "vUTCOffset", + "Parameters", + "q_split", + "q_join", + "use_pytz", + "use_zoneinfo", + "__version__", + "version", + "__version_tuple__", + "version_tuple", + "TypesFactory", + "Component", + "vMonth", + "IncompleteComponent", + "InvalidCalendar", + "Alarms", + "AlarmTime", + "ComponentEndMissing", + "ComponentStartMissing", + "IncompleteAlarmInformation", + "LocalTimezoneMissing", + "CUTYPE", + "FBTYPE", + "PARTSTAT", + "RANGE", + "vSkip", + "RELATED", + "vSkip", + "RELTYPE", + "ROLE", + "FeatureWillBeRemovedInFutureVersion" +] diff --git a/.venv/lib/python3.9/site-packages/icalendar/_version.py b/.venv/lib/python3.9/site-packages/icalendar/_version.py new file mode 100644 index 0000000..b0a4328 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/_version.py @@ -0,0 +1,34 @@ +# file generated by setuptools-scm +# don't change, don't track in version control + +__all__ = [ + "__version__", + "__version_tuple__", + "version", + "version_tuple", + "__commit_id__", + "commit_id", +] + +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import Tuple + from typing import Union + + VERSION_TUPLE = Tuple[Union[int, str], ...] + COMMIT_ID = Union[str, None] +else: + VERSION_TUPLE = object + COMMIT_ID = object + +version: str +__version__: str +__version_tuple__: VERSION_TUPLE +version_tuple: VERSION_TUPLE +commit_id: COMMIT_ID +__commit_id__: COMMIT_ID + +__version__ = version = '6.3.2' +__version_tuple__ = version_tuple = (6, 3, 2) + +__commit_id__ = commit_id = None diff --git a/.venv/lib/python3.9/site-packages/icalendar/alarms.py b/.venv/lib/python3.9/site-packages/icalendar/alarms.py new file mode 100644 index 0000000..1cf511a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/alarms.py @@ -0,0 +1,378 @@ +"""Compute the times and states of alarms. + +This takes different calendar software into account and the RFC 9074 (Alarm Extension). + +- RFC 9074 defines an ACKNOWLEDGED property in the VALARM. +- Outlook does not export VALARM information. +- Google Calendar uses the DTSTAMP to acknowledge the alarms. +- Thunderbird snoozes the alarms with a X-MOZ-SNOOZE-TIME attribute in the event. +- Thunderbird acknowledges the alarms with a X-MOZ-LASTACK attribute in the event. +- Etar deletes alarms that are acknowledged. +- Nextcloud's Webinterface does not do anything with the alarms when the time passes. +""" + +from __future__ import annotations + +from datetime import date, timedelta, tzinfo +from typing import TYPE_CHECKING, Generator, Optional, Union + +from icalendar.cal import Alarm, Event, Todo +from icalendar.error import ( + ComponentEndMissing, + ComponentStartMissing, + IncompleteAlarmInformation, + LocalTimezoneMissing, +) +from icalendar.timezone import tzp +from icalendar.tools import is_date, normalize_pytz, to_datetime + +if TYPE_CHECKING: + from datetime import datetime + +Parent = Union[Event, Todo] + + +class AlarmTime: + """An alarm time with all the information.""" + + def __init__( + self, + alarm: Alarm, + trigger: datetime, + acknowledged_until: Optional[datetime] = None, + snoozed_until: Optional[datetime] = None, + parent: Optional[Parent] = None, + ): + """Create a new AlarmTime. + + alarm + the Alarm component + + trigger + a date or datetime at which to trigger the alarm + + acknowledged_until + an optional datetime in UTC until when all alarms + have been acknowledged + + snoozed_until + an optional datetime in UTC until which all alarms of + the same parent are snoozed + + parent + the optional parent component the alarm refers to + + local_tzinfo + the local timezone that events without tzinfo should have + """ + self._alarm = alarm + self._parent = parent + self._trigger = trigger + self._last_ack = acknowledged_until + self._snooze_until = snoozed_until + + @property + def acknowledged(self) -> Optional[datetime]: + """The time in UTC at which this alarm was last acknowledged. + + If the alarm was not acknowledged (dismissed), then this is None. + """ + ack = self.alarm.ACKNOWLEDGED + if ack is None: + return self._last_ack + if self._last_ack is None: + return ack + return max(ack, self._last_ack) + + @property + def alarm(self) -> Alarm: + """The alarm component.""" + return self._alarm + + @property + def parent(self) -> Optional[Parent]: + """This is the component that contains the alarm. + + This is None if you did not use Alarms.set_component(). + """ + return self._parent + + def is_active(self) -> bool: + """Whether this alarm is active (True) or acknowledged (False). + + For example, in some calendar software, this is True until the user looks + at the alarm message and clicked the dismiss button. + + Alarms can be in local time (without a timezone). + To calculate if the alarm really happened, we need it to be in a timezone. + If a timezone is required but not given, we throw an IncompleteAlarmInformation. + """ + acknowledged = self.acknowledged + if not acknowledged: + # if nothing is acknowledged, this alarm counts + return True + if self._snooze_until is not None and self._snooze_until > acknowledged: + return True + trigger = self.trigger + if trigger.tzinfo is None: + raise LocalTimezoneMissing( + "A local timezone is required to check if the alarm is still active. " + "Use Alarms.set_local_timezone()." + ) + return trigger > acknowledged + + @property + def trigger(self) -> date: + """This is the time to trigger the alarm. + + If the alarm has been snoozed, this can differ from the TRIGGER property. + """ + if self._snooze_until is not None and self._snooze_until > self._trigger: + return self._snooze_until + return self._trigger + + +class Alarms: + """Compute the times and states of alarms. + + This is an example using RFC 9074. + One alarm is 30 minutes before the event and acknowledged. + Another alarm is 15 minutes before the event and still active. + + >>> from icalendar import Event, Alarms + >>> event = Event.from_ical( + ... '''BEGIN:VEVENT + ... CREATED:20210301T151004Z + ... UID:AC67C078-CED3-4BF5-9726-832C3749F627 + ... DTSTAMP:20210301T151004Z + ... DTSTART;TZID=America/New_York:20210302T103000 + ... DTEND;TZID=America/New_York:20210302T113000 + ... SUMMARY:Meeting + ... BEGIN:VALARM + ... UID:8297C37D-BA2D-4476-91AE-C1EAA364F8E1 + ... TRIGGER:-PT30M + ... ACKNOWLEDGED:20210302T150004Z + ... DESCRIPTION:Event reminder + ... ACTION:DISPLAY + ... END:VALARM + ... BEGIN:VALARM + ... UID:8297C37D-BA2D-4476-91AE-C1EAA364F8E1 + ... TRIGGER:-PT15M + ... DESCRIPTION:Event reminder + ... ACTION:DISPLAY + ... END:VALARM + ... END:VEVENT + ... ''') + >>> alarms = Alarms(event) + >>> len(alarms.times) # all alarms including those acknowledged + 2 + >>> len(alarms.active) # the alarms that are not acknowledged, yet + 1 + >>> alarms.active[0].trigger # this alarm triggers 15 minutes before 10:30 + datetime.datetime(2021, 3, 2, 10, 15, tzinfo=ZoneInfo(key='America/New_York')) + + RFC 9074 specifies that alarms can also be triggered by proximity. + This is not implemented yet. + """ + + def __init__(self, component: Optional[Alarm | Event | Todo] = None): + """Start computing alarm times.""" + self._absolute_alarms: list[Alarm] = [] + self._start_alarms: list[Alarm] = [] + self._end_alarms: list[Alarm] = [] + self._start: Optional[date] = None + self._end: Optional[date] = None + self._parent: Optional[Parent] = None + self._last_ack: Optional[datetime] = None + self._snooze_until: Optional[datetime] = None + self._local_tzinfo: Optional[tzinfo] = None + + if component is not None: + self.add_component(component) + + def add_component(self, component: Alarm | Parent): + """Add a component. + + If this is an alarm, it is added. + Events and Todos are added as a parent and all + their alarms are added, too. + """ + if isinstance(component, (Event, Todo)): + self.set_parent(component) + self.set_start(component.start) + self.set_end(component.end) + if component.is_thunderbird(): + self.acknowledge_until(component.X_MOZ_LASTACK) + self.snooze_until(component.X_MOZ_SNOOZE_TIME) + else: + self.acknowledge_until(component.DTSTAMP) + + for alarm in component.walk("VALARM"): + self.add_alarm(alarm) + + def set_parent(self, parent: Parent): + """Set the parent of all the alarms. + + If you would like to collect alarms from a component, use add_component + """ + if self._parent is not None and self._parent is not parent: + raise ValueError("You can only set one parent for this alarm calculation.") + self._parent = parent + + def add_alarm(self, alarm: Alarm) -> None: + """Optional: Add an alarm component.""" + trigger = alarm.TRIGGER + if trigger is None: + return + if isinstance(trigger, date): + self._absolute_alarms.append(alarm) + elif alarm.TRIGGER_RELATED == "START": + self._start_alarms.append(alarm) + else: + self._end_alarms.append(alarm) + + def set_start(self, dt: Optional[date]): + """Set the start of the component. + + If you have only absolute alarms, this is not required. + If you have alarms relative to the start of a compoment, set the start here. + """ + self._start = dt + + def set_end(self, dt: Optional[date]): + """Set the end of the component. + + If you have only absolute alarms, this is not required. + If you have alarms relative to the end of a compoment, set the end here. + """ + self._end = dt + + def _add(self, dt: date, td: timedelta): + """Add a timedelta to a datetime.""" + if is_date(dt): + if td.seconds == 0: + return dt + td + dt = to_datetime(dt) + return normalize_pytz(dt + td) + + def acknowledge_until(self, dt: Optional[date]) -> None: + """This is the time in UTC when all the alarms of this component were acknowledged. + + Only the last call counts. + + Since RFC 9074 (Alarm Extension) was created later, + calendar implementations differ in how they acknowledge alarms. + For example, Thunderbird and Google Calendar store the last time + an event has been acknowledged because of an alarm. + All alarms that happen before this time count as acknowledged. + """ + self._last_ack = tzp.localize_utc(dt) if dt is not None else None + + def snooze_until(self, dt: Optional[date]) -> None: + """This is the time in UTC when all the alarms of this component were snoozed. + + Only the last call counts. + + The alarms are supposed to turn up again at dt when they are not acknowledged + but snoozed. + """ + self._snooze_until = tzp.localize_utc(dt) if dt is not None else None + + def set_local_timezone(self, tzinfo: Optional[tzinfo | str]): + """Set the local timezone. + + Events are sometimes in local time. + In order to compute the exact time of the alarm, some + alarms without timezone are considered local. + + Some computations work without setting this, others don't. + If they need this information, expect a LocalTimezoneMissing exception + somewhere down the line. + """ + self._local_tzinfo = tzp.timezone(tzinfo) if isinstance(tzinfo, str) else tzinfo + + @property + def times(self) -> list[AlarmTime]: + """Compute and return the times of the alarms given. + + If the information for calculation is incomplete, this will raise a + IncompleteAlarmInformation exception. + + Please make sure to set all the required parameters before calculating. + If you forget to set the acknowledged times, that is not problem. + """ + return ( + self._get_end_alarm_times() + + self._get_start_alarm_times() + + self._get_absolute_alarm_times() + ) + + def _repeat(self, first: datetime, alarm: Alarm) -> Generator[datetime]: + """The times when the alarm is triggered relative to start.""" + yield first # we trigger at the start + repeat = alarm.REPEAT + duration = alarm.DURATION + if repeat and duration: + for i in range(1, repeat + 1): + yield self._add(first, duration * i) + + def _alarm_time(self, alarm: Alarm, trigger: date): + """Create an alarm time with the additional attributes.""" + if getattr(trigger, "tzinfo", None) is None and self._local_tzinfo is not None: + trigger = normalize_pytz(trigger.replace(tzinfo=self._local_tzinfo)) + return AlarmTime( + alarm, trigger, self._last_ack, self._snooze_until, self._parent + ) + + def _get_absolute_alarm_times(self) -> list[AlarmTime]: + """Return a list of absolute alarm times.""" + return [ + self._alarm_time(alarm, trigger) + for alarm in self._absolute_alarms + for trigger in self._repeat(alarm.TRIGGER, alarm) + ] + + def _get_start_alarm_times(self) -> list[AlarmTime]: + """Return a list of alarm times relative to the start of the component.""" + if self._start is None and self._start_alarms: + raise ComponentStartMissing( + "Use Alarms.set_start because at least one alarm is relative to the start of a component." + ) + return [ + self._alarm_time(alarm, trigger) + for alarm in self._start_alarms + for trigger in self._repeat(self._add(self._start, alarm.TRIGGER), alarm) + ] + + def _get_end_alarm_times(self) -> list[AlarmTime]: + """Return a list of alarm times relative to the start of the component.""" + if self._end is None and self._end_alarms: + raise ComponentEndMissing( + "Use Alarms.set_end because at least one alarm is relative to the end of a component." + ) + return [ + self._alarm_time(alarm, trigger) + for alarm in self._end_alarms + for trigger in self._repeat(self._add(self._end, alarm.TRIGGER), alarm) + ] + + @property + def active(self) -> list[AlarmTime]: + """The alarm times that are still active and not acknowledged. + + This considers snoozed alarms. + + Alarms can be in local time (without a timezone). + To calculate if the alarm really happened, we need it to be in a timezone. + If a timezone is required but not given, we throw an IncompleteAlarmInformation. + """ + return [alarm_time for alarm_time in self.times if alarm_time.is_active()] + + +__all__ = [ + "Alarms", + "AlarmTime", + "IncompleteAlarmInformation", + "ComponentEndMissing", + "ComponentStartMissing", +] diff --git a/.venv/lib/python3.9/site-packages/icalendar/attr.py b/.venv/lib/python3.9/site-packages/icalendar/attr.py new file mode 100644 index 0000000..9c9f5f5 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/attr.py @@ -0,0 +1,700 @@ +"""Attributes of Components and properties.""" +from __future__ import annotations + +import itertools +from datetime import date, datetime, timedelta +from typing import TYPE_CHECKING, Optional, Union + +from icalendar.error import InvalidCalendar +from icalendar.prop import vCategory, vDDDTypes, vRecur, vText +from icalendar.timezone import tzp + +if TYPE_CHECKING: + from icalendar.cal import Component + + +def _get_rdates(self: Component) -> list[ + Union[tuple[date, None], + tuple[datetime, None], + tuple[datetime, datetime]]]: + """The RDATE property defines the list of DATE-TIME values for recurring components. + + RDATE is defined in :rfc:`5545`. + The return value is a list of tuples ``(start, end)``. + + ``start`` can be a :class:`datetime.date` or a :class:`datetime.datetime`, + with and without timezone. + + ``end`` is :obj:`None` if the end is not specified and a :class:`datetime.datetime` + if the end is specified. + + Value Type: + The default value type for this property is DATE-TIME. + The value type can be set to DATE or PERIOD. + + Property Parameters: + IANA, non-standard, value data type, and time + zone identifier property parameters can be specified on this + property. + + Conformance: + This property can be specified in recurring "VEVENT", + "VTODO", and "VJOURNAL" calendar components as well as in the + "STANDARD" and "DAYLIGHT" sub-components of the "VTIMEZONE" + calendar component. + + Description: + This property can appear along with the "RRULE" + property to define an aggregate set of repeating occurrences. + When they both appear in a recurring component, the recurrence + instances are defined by the union of occurrences defined by both + the "RDATE" and "RRULE". + + The recurrence dates, if specified, are used in computing the + recurrence set. The recurrence set is the complete set of + recurrence instances for a calendar component. The recurrence set + is generated by considering the initial "DTSTART" property along + with the "RRULE", "RDATE", and "EXDATE" properties contained + within the recurring component. The "DTSTART" property defines + the first instance in the recurrence set. The "DTSTART" property + value SHOULD match the pattern of the recurrence rule, if + specified. The recurrence set generated with a "DTSTART" property + value that doesn't match the pattern of the rule is undefined. + The final recurrence set is generated by gathering all of the + start DATE-TIME values generated by any of the specified "RRULE" + and "RDATE" properties, and then excluding any start DATE-TIME + values specified by "EXDATE" properties. This implies that start + DATE-TIME values specified by "EXDATE" properties take precedence + over those specified by inclusion properties (i.e., "RDATE" and + "RRULE"). Where duplicate instances are generated by the "RRULE" + and "RDATE" properties, only one recurrence is considered. + Duplicate instances are ignored. + + Example: + Below, we set one RDATE in a list and get the resulting tuple of start and end. + + .. code-block:: pycon + + >>> from icalendar import Event + >>> from datetime import datetime + >>> event = Event() + + # Add a list of recurrence dates + >>> event.add("RDATE", [datetime(2025, 4, 28, 16, 5)]) + >>> event.rdates + [(datetime.datetime(2025, 4, 28, 16, 5), None)] + + .. note:: + + You cannot modify the RDATE value by modifying the result. + Use :func:`icalendar.cal.Component.add` to add values. + + If you want to compute recurrences, have a look at :ref:`Related projects`. + + """ + result = [] + rdates = self.get("RDATE", []) + for rdates in (rdates,) if not isinstance(rdates, list) else rdates: + for dts in rdates.dts: + rdate = dts.dt + if isinstance(rdate, tuple): + # we have a period as rdate + if isinstance(rdate[1], timedelta): + result.append((rdate[0], rdate[0] + rdate[1])) + else: + result.append(rdate) + else: + # we have a date/datetime + result.append((rdate, None)) + return result + + +rdates_property = property(_get_rdates) + + +def _get_exdates(self: Component) -> list[date|datetime]: + """EXDATE defines the list of DATE-TIME exceptions for recurring components. + + EXDATE is defined in :rfc:`5545`. + + Value Type: + The default value type for this property is DATE-TIME. + The value type can be set to DATE. + + Property Parameters: + IANA, non-standard, value data type, and time + zone identifier property parameters can be specified on this + property. + + Conformance: + This property can be specified in recurring "VEVENT", + "VTODO", and "VJOURNAL" calendar components as well as in the + "STANDARD" and "DAYLIGHT" sub-components of the "VTIMEZONE" + calendar component. + + Description: + The exception dates, if specified, are used in + computing the recurrence set. The recurrence set is the complete + set of recurrence instances for a calendar component. The + recurrence set is generated by considering the initial "DTSTART" + property along with the "RRULE", "RDATE", and "EXDATE" properties + contained within the recurring component. The "DTSTART" property + defines the first instance in the recurrence set. The "DTSTART" + property value SHOULD match the pattern of the recurrence rule, if + specified. The recurrence set generated with a "DTSTART" property + value that doesn't match the pattern of the rule is undefined. + The final recurrence set is generated by gathering all of the + start DATE-TIME values generated by any of the specified "RRULE" + and "RDATE" properties, and then excluding any start DATE-TIME + values specified by "EXDATE" properties. This implies that start + DATE-TIME values specified by "EXDATE" properties take precedence + over those specified by inclusion properties (i.e., "RDATE" and + "RRULE"). When duplicate instances are generated by the "RRULE" + and "RDATE" properties, only one recurrence is considered. + Duplicate instances are ignored. + + The "EXDATE" property can be used to exclude the value specified + in "DTSTART". However, in such cases, the original "DTSTART" date + MUST still be maintained by the calendaring and scheduling system + because the original "DTSTART" value has inherent usage + dependencies by other properties such as the "RECURRENCE-ID". + + Example: + Below, we add an exdate in a list and get the resulting list of exdates. + + .. code-block:: pycon + + >>> from icalendar import Event + >>> from datetime import datetime + >>> event = Event() + + # Add a list of excluded dates + >>> event.add("EXDATE", [datetime(2025, 4, 28, 16, 5)]) + >>> event.exdates + [datetime.datetime(2025, 4, 28, 16, 5)] + + .. note:: + + You cannot modify the EXDATE value by modifying the result. + Use :func:`icalendar.cal.Component.add` to add values. + + If you want to compute recurrences, have a look at :ref:`Related projects`. + + """ + result = [] + exdates = self.get("EXDATE", []) + for exdates in (exdates,) if not isinstance(exdates, list) else exdates: + for dts in exdates.dts: + exdate = dts.dt + # we have a date/datetime + result.append(exdate) + return result + + +exdates_property = property(_get_exdates) + + +def _get_rrules(self: Component) -> list[vRecur]: + """RRULE defines a rule or repeating pattern for recurring components. + + RRULE is defined in :rfc:`5545`. + :rfc:`7529` adds the ``SKIP`` parameter :class:`icalendar.prop.vSkip`. + + Property Parameters: + IANA and non-standard property parameters can + be specified on this property. + + Conformance: + This property can be specified in recurring "VEVENT", + "VTODO", and "VJOURNAL" calendar components as well as in the + "STANDARD" and "DAYLIGHT" sub-components of the "VTIMEZONE" + calendar component, but it SHOULD NOT be specified more than once. + The recurrence set generated with multiple "RRULE" properties is + undefined. + + Description: + The recurrence rule, if specified, is used in computing + the recurrence set. The recurrence set is the complete set of + recurrence instances for a calendar component. The recurrence set + is generated by considering the initial "DTSTART" property along + with the "RRULE", "RDATE", and "EXDATE" properties contained + within the recurring component. The "DTSTART" property defines + the first instance in the recurrence set. The "DTSTART" property + value SHOULD be synchronized with the recurrence rule, if + specified. The recurrence set generated with a "DTSTART" property + value not synchronized with the recurrence rule is undefined. The + final recurrence set is generated by gathering all of the start + DATE-TIME values generated by any of the specified "RRULE" and + "RDATE" properties, and then excluding any start DATE-TIME values + specified by "EXDATE" properties. This implies that start DATE- + TIME values specified by "EXDATE" properties take precedence over + those specified by inclusion properties (i.e., "RDATE" and + "RRULE"). Where duplicate instances are generated by the "RRULE" + and "RDATE" properties, only one recurrence is considered. + Duplicate instances are ignored. + + The "DTSTART" property specified within the iCalendar object + defines the first instance of the recurrence. In most cases, a + "DTSTART" property of DATE-TIME value type used with a recurrence + rule, should be specified as a date with local time and time zone + reference to make sure all the recurrence instances start at the + same local time regardless of time zone changes. + + If the duration of the recurring component is specified with the + "DTEND" or "DUE" property, then the same exact duration will apply + to all the members of the generated recurrence set. Else, if the + duration of the recurring component is specified with the + "DURATION" property, then the same nominal duration will apply to + all the members of the generated recurrence set and the exact + duration of each recurrence instance will depend on its specific + start time. For example, recurrence instances of a nominal + duration of one day will have an exact duration of more or less + than 24 hours on a day where a time zone shift occurs. The + duration of a specific recurrence may be modified in an exception + component or simply by using an "RDATE" property of PERIOD value + type. + + Examples: + Daily for 10 occurrences: + + .. code-block:: pycon + + >>> from icalendar import Event + >>> from datetime import datetime + >>> from zoneinfo import ZoneInfo + >>> event = Event() + >>> event.start = datetime(1997, 9, 2, 9, 0, tzinfo=ZoneInfo("America/New_York")) + >>> event.add("RRULE", "FREQ=DAILY;COUNT=10") + >>> print(event.to_ical()) + BEGIN:VEVENT + DTSTART;TZID=America/New_York:19970902T090000 + RRULE:FREQ=DAILY;COUNT=10 + END:VEVENT + >>> event.rrules + [vRecur({'FREQ': ['DAILY'], 'COUNT': [10]})] + + Daily until December 24, 1997: + + .. code-block:: pycon + + >>> from icalendar import Event, vRecur + >>> from datetime import datetime + >>> from zoneinfo import ZoneInfo + >>> event = Event() + >>> event.start = datetime(1997, 9, 2, 9, 0, tzinfo=ZoneInfo("America/New_York")) + >>> event.add("RRULE", vRecur({"FREQ": ["DAILY"]}, until=datetime(1997, 12, 24, tzinfo=ZoneInfo("UTC")))) + >>> print(event.to_ical()) + BEGIN:VEVENT + DTSTART;TZID=America/New_York:19970902T090000 + RRULE:FREQ=DAILY;UNTIL=19971224T000000Z + END:VEVENT + >>> event.rrules + [vRecur({'FREQ': ['DAILY'], 'UNTIL': [datetime.datetime(1997, 12, 24, 0, 0, tzinfo=ZoneInfo(key='UTC'))]})] + + .. note:: + + You cannot modify the RRULE value by modifying the result. + Use :func:`icalendar.cal.Component.add` to add values. + + If you want to compute recurrences, have a look at :ref:`Related projects`. + + """ + rrules = self.get("RRULE", []) + if not isinstance(rrules, list): + return [rrules] + return rrules + + +rrules_property = property(_get_rrules) + +def multi_language_text_property(main_prop:str, compatibility_prop:str, doc:str) -> property: + """This creates a text property. + + This property can be defined several times with different ``LANGUAGE`` parameters. + + Args: + main_prop (str): The property to set and get, such as ``NAME`` + compatibility_prop (str): An old property used before, such as ``X-WR-CALNAME`` + doc (str): The documentation string + """ + def fget(self: Component) -> Optional[str]: + """Get the property""" + result = self.get(main_prop, self.get(compatibility_prop)) + if isinstance(result, list): + for item in result: + if "LANGUAGE" not in item.params: + return item + return result + + def fset(self: Component, value:str): + """Set the property.""" + fdel(self) + self.add(main_prop, value) + + def fdel(self: Component): + """Delete the property.""" + self.pop(main_prop, None) + self.pop(compatibility_prop, None) + + return property(fget, fset, fdel, doc) + + +def single_int_property(prop:str, default:int, doc:str) -> property: + """Create a property for an int value that exists only once. + + Args: + prop: The name of the property + default: The default value + doc: The documentation string + """ + def fget(self: Component) -> int: + """Get the property""" + try: + return int(self.get(prop, default)) + except ValueError as e: + raise InvalidCalendar(f"{prop} must be an int") from e + + def fset(self: Component, value:int): + """Set the property.""" + fdel(self) + self.add(prop, value) + + def fdel(self: Component): + """Delete the property.""" + self.pop(prop, None) + + return property(fget, fset, fdel, doc) + + +def single_utc_property(name: str, docs: str) -> property: + """Create a property to access a value of datetime in UTC timezone. + + Args: + name: name of the property + docs: documentation string + """ + docs = ( + f"""The {name} property. datetime in UTC + + All values will be converted to a datetime in UTC. + """ + + docs + ) + + def fget(self: Component) -> Optional[datetime]: + """Get the value.""" + if name not in self: + return None + dt = self.get(name) + if isinstance(dt, vText): + # we might be in an attribute that is not typed + value = vDDDTypes.from_ical(dt) + else: + value = getattr(dt, "dt", None) + if value is None or not isinstance(value, date): + raise InvalidCalendar(f"{name} must be a datetime in UTC, not {value}") + return tzp.localize_utc(value) + + def fset(self: Component, value: datetime): + """Set the value""" + if not isinstance(value, date): + raise TypeError(f"{name} takes a datetime in UTC, not {value}") + fdel(self) + self.add(name, tzp.localize_utc(value)) + + def fdel(self: Component): + """Delete the property.""" + self.pop(name, None) + + return property(fget, fset, fdel, doc=docs) + + +def single_string_property(name: str, docs: str, other_name:Optional[str]=None) -> property: + """Create a property to access a single string value.""" + + def fget(self: Component) -> str: + """Get the value.""" + result = self.get(name, None if other_name is None else self.get(other_name, None)) + if result is None or result == []: + return "" + if isinstance(result, list): + return result[0] + return result + + def fset(self: Component, value: str): + """Set the value""" + fdel(self) + self.add(name, value) + + def fdel(self: Component): + """Delete the property.""" + self.pop(name, None) + if other_name is not None: + self.pop(other_name, None) + + return property(fget, fset, fdel, doc=docs) + +color_property = single_string_property( + "COLOR", + """This property specifies a color used for displaying the component. + + This implements :rfc:`7986` ``COLOR`` property. + + Property Parameters: + IANA and non-standard property parameters can + be specified on this property. + + Conformance: + This property can be specified once in an iCalendar + object or in ``VEVENT``, ``VTODO``, or ``VJOURNAL`` calendar components. + + Description: + This property specifies a color that clients MAY use + when presenting the relevant data to a user. Typically, this + would appear as the "background" color of events or tasks. The + value is a case-insensitive color name taken from the CSS3 set of + names, defined in Section 4.3 of `W3C.REC-css3-color-20110607 `_. + + Example: + ``"turquoise"``, ``"#ffffff"`` + + .. code-block:: pycon + + >>> from icalendar import Todo + >>> todo = Todo() + >>> todo.color = "green" + >>> print(todo.to_ical()) + BEGIN:VTODO + COLOR:green + END:VTODO + """ +) + +sequence_property = single_int_property( + "SEQUENCE", + 0, + """This property defines the revision sequence number of the calendar component within a sequence of revisions. + +Value Type: + INTEGER + +Property Parameters: + IANA and non-standard property parameters can be specified on this property. + +Conformance: + The property can be specified in "VEVENT", "VTODO", or + "VJOURNAL" calendar component. + +Description: + When a calendar component is created, its sequence + number is 0. It is monotonically incremented by the "Organizer's" + CUA each time the "Organizer" makes a significant revision to the + calendar component. + + The "Organizer" includes this property in an iCalendar object that + it sends to an "Attendee" to specify the current version of the + calendar component. + + The "Attendee" includes this property in an iCalendar object that + it sends to the "Organizer" to specify the version of the calendar + component to which the "Attendee" is referring. + + A change to the sequence number is not the mechanism that an + "Organizer" uses to request a response from the "Attendees". The + "RSVP" parameter on the "ATTENDEE" property is used by the + "Organizer" to indicate that a response from the "Attendees" is + requested. + + Recurrence instances of a recurring component MAY have different + sequence numbers. + +Examples: + The following is an example of this property for a calendar + component that was just created by the "Organizer": + + .. code-block:: pycon + + >>> from icalendar import Event + >>> event = Event() + >>> event.sequence + 0 + + The following is an example of this property for a calendar + component that has been revised 10 different times by the + "Organizer": + + .. code-block:: pycon + + >>> from icalendar import Calendar + >>> calendar = Calendar.example("issue_156_RDATE_with_PERIOD_TZID_khal") + >>> event = calendar.events[0] + >>> event.sequence + 10 + """ +) + +def _get_categories(component: Component) -> list[str]: + """Get all the categories.""" + categories : Optional[vCategory|list[vCategory]] = component.get("CATEGORIES") + if isinstance(categories, list): + _set_categories(component, list(itertools.chain.from_iterable(cat.cats for cat in categories))) + return _get_categories(component) + if categories is None: + categories = vCategory([]) + component.add("CATEGORIES", categories) + return categories.cats + +def _set_categories(component: Component, cats: list[str]) -> None: + """Set the categories.""" + component["CATEGORIES"] = categories = vCategory(cats) + cats.clear() + cats.extend(categories.cats) + categories.cats = cats + + +def _del_categories(component: Component) -> None: + """Delete the categories.""" + component.pop("CATEGORIES", None) + + +categories_property = property( + _get_categories, + _set_categories, + _del_categories, + """This property defines the categories for a component. + +Property Parameters: + IANA, non-standard, and language property parameters can be specified on this + property. + +Conformance: + The property can be specified within "VEVENT", "VTODO", or "VJOURNAL" calendar + components. + Since :rfc:`7986` it can also be defined on a "VCALENDAR" component. + +Description: + This property is used to specify categories or subtypes + of the calendar component. The categories are useful in searching + for a calendar component of a particular type and category. + Within the "VEVENT", "VTODO", or "VJOURNAL" calendar components, + more than one category can be specified as a COMMA-separated list + of categories. + +Example: + Below, we add the categories to an event: + + .. code-block:: pycon + + >>> from icalendar import Event + >>> event = Event() + >>> event.categories = ["Work", "Meeting"] + >>> print(event.to_ical()) + BEGIN:VEVENT + CATEGORIES:Work,Meeting + END:VEVENT + >>> event.categories.append("Lecture") + >>> event.categories == ["Work", "Meeting", "Lecture"] + True + +.. note:: + + At present, we do not take the LANGUAGE parameter into account. +""" +) + +uid_property = single_string_property( + "UID", """UID specifies the persistent, globally unique identifier for a component. + +We recommend using :func:`uuid.uuid4` to generate new values. + +Returns: + The value of the UID property as a string or ``""`` if no value is set. + +Description: + The "UID" itself MUST be a globally unique identifier. + The generator of the identifier MUST guarantee that the identifier + is unique. + + This is the method for correlating scheduling messages with the + referenced "VEVENT", "VTODO", or "VJOURNAL" calendar component. + The full range of calendar components specified by a recurrence + set is referenced by referring to just the "UID" property value + corresponding to the calendar component. The "RECURRENCE-ID" + property allows the reference to an individual instance within the + recurrence set. + + This property is an important method for group-scheduling + applications to match requests with later replies, modifications, + or deletion requests. Calendaring and scheduling applications + MUST generate this property in "VEVENT", "VTODO", and "VJOURNAL" + calendar components to assure interoperability with other group- + scheduling applications. This identifier is created by the + calendar system that generates an iCalendar object. + + Implementations MUST be able to receive and persist values of at + least 255 octets for this property, but they MUST NOT truncate + values in the middle of a UTF-8 multi-octet sequence. + + :rfc:`7986` states that UID can be used, for + example, to identify duplicate calendar streams that a client may + have been given access to. It can be used in conjunction with the + "LAST-MODIFIED" property also specified on the "VCALENDAR" object + to identify the most recent version of a calendar. + +Conformance: + :rfc:`5545` states that the "UID" property can be specified on "VEVENT", "VTODO", + and "VJOURNAL" calendar components. + :rfc:`7986` modifies the definition of the "UID" property to + allow it to be defined in an iCalendar object. + :rfc:`9074` adds a "UID" property to "VALARM" components to allow a unique + identifier to be specified. The value of this property can then be used + to refer uniquely to the "VALARM" component. + + This property can be specified once only. + +Security: + :rfc:`7986` states that UID values MUST NOT include any data that + might identify a user, host, domain, or any other security- or + privacy-sensitive information. It is RECOMMENDED that calendar user + agents now generate "UID" values that are hex-encoded random + Universally Unique Identifier (UUID) values as defined in + Sections 4.4 and 4.5 of :rfc:`4122`. + You can use the :mod:`uuid` module to generate new UUIDs. + +Compatibility: + For Alarms, ``X-ALARMUID`` is also considered. + +Examples: + The following is an example of such a property value: + ``5FC53010-1267-4F8E-BC28-1D7AE55A7C99``. + + Set the UID of a calendar: + + .. code-block:: pycon + + >>> from icalendar import Calendar + >>> from uuid import uuid4 + >>> calendar = Calendar() + >>> calendar.uid = uuid4() + >>> print(calendar.to_ical()) + BEGIN:VCALENDAR + UID:d755cef5-2311-46ed-a0e1-6733c9e15c63 + END:VCALENDAR + +""" +) + + + +__all__ = [ + "categories_property", + "color_property", + "exdates_property", + "multi_language_text_property", + "rdates_property", + "rrules_property", + "sequence_property", + "single_int_property", + "single_utc_property", + "uid_property", +] diff --git a/.venv/lib/python3.9/site-packages/icalendar/cal.py b/.venv/lib/python3.9/site-packages/icalendar/cal.py new file mode 100644 index 0000000..e641975 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/cal.py @@ -0,0 +1,2201 @@ +"""Calendar is a dictionary like Python object that can render itself as VCAL +files according to RFC 5545. + +These are the defined components. +""" + +from __future__ import annotations + +import os +from collections import defaultdict +from datetime import date, datetime, timedelta, tzinfo +from typing import TYPE_CHECKING, List, NamedTuple, Optional, Tuple, Union + +import dateutil.rrule +import dateutil.tz + +from icalendar.attr import ( + categories_property, + color_property, + exdates_property, + multi_language_text_property, + rdates_property, + rrules_property, + sequence_property, + single_int_property, + single_string_property, + single_utc_property, + uid_property, +) +from icalendar.caselessdict import CaselessDict +from icalendar.error import IncompleteComponent, InvalidCalendar +from icalendar.parser import Contentline, Contentlines, Parameters, q_join, q_split +from icalendar.parser_tools import DEFAULT_ENCODING +from icalendar.prop import ( + TypesFactory, + tzid_from_tzinfo, + vDDDLists, + vDDDTypes, + vDuration, + vText, + vUTCOffset, +) +from icalendar.timezone import TZP, tzp +from icalendar.tools import is_date, to_datetime + +if TYPE_CHECKING: + from icalendar.alarms import Alarms + + +def get_example(component_directory: str, example_name: str) -> bytes: + """Return an example and raise an error if it is absent.""" + here = os.path.dirname(__file__) + examples = os.path.join(here, "tests", component_directory) + if not example_name.endswith(".ics"): + example_name = example_name + ".ics" + example_file = os.path.join(examples, example_name) + if not os.path.isfile(example_file): + raise ValueError( + f"Example {example_name} for {component_directory} not found. You can use one of {', '.join(os.listdir(examples))}" + ) + with open(example_file, "rb") as f: + return f.read() + + +###################################### +# The component factory + + +class ComponentFactory(CaselessDict): + """All components defined in RFC 5545 are registered in this factory class. + To get a component you can use it like this. + """ + + def __init__(self, *args, **kwargs): + """Set keys to upper for initial dict.""" + super().__init__(*args, **kwargs) + self["VEVENT"] = Event + self["VTODO"] = Todo + self["VJOURNAL"] = Journal + self["VFREEBUSY"] = FreeBusy + self["VTIMEZONE"] = Timezone + self["STANDARD"] = TimezoneStandard + self["DAYLIGHT"] = TimezoneDaylight + self["VALARM"] = Alarm + self["VCALENDAR"] = Calendar + + +# These Properties have multiple property values inlined in one propertyline +# seperated by comma. Use CaselessDict as simple caseless set. +INLINE = CaselessDict( + { + "CATEGORIES": 1, + "RESOURCES": 1, + "FREEBUSY": 1, + } +) + +_marker = [] + + +class Component(CaselessDict): + """Component is the base object for calendar, Event and the other + components defined in RFC 5545. Normally you will not use this class + directly, but rather one of the subclasses. + """ + + name = None # should be defined in each component + required = () # These properties are required + singletons = () # These properties must only appear once + multiple = () # may occur more than once + exclusive = () # These properties are mutually exclusive + inclusive = () # if any occurs the other(s) MUST occur + # ('duration', 'repeat') + ignore_exceptions = False # if True, and we cannot parse this + # component, we will silently ignore + # it, rather than let the exception + # propagate upwards + # not_compliant = [''] # List of non-compliant properties. + + def __init__(self, *args, **kwargs): + """Set keys to upper for initial dict.""" + super().__init__(*args, **kwargs) + # set parameters here for properties that use non-default values + self.subcomponents = [] # Components can be nested. + self.errors = [] # If we ignored exception(s) while + # parsing a property, contains error strings + + # def is_compliant(self, name): + # """Returns True is the given property name is compliant with the + # icalendar implementation. + # + # If the parser is too strict it might prevent parsing erroneous but + # otherwise compliant properties. So the parser is pretty lax, but it is + # possible to test for non-compliance by calling this method. + # """ + # return name in not_compliant + + def __bool__(self): + """Returns True, CaselessDict would return False if it had no items.""" + return True + + # python 2 compatibility + __nonzero__ = __bool__ + + def is_empty(self): + """Returns True if Component has no items or subcomponents, else False.""" + return True if not (list(self.values()) + self.subcomponents) else False # noqa + + ############################# + # handling of property values + + @staticmethod + def _encode(name, value, parameters=None, encode=1): + """Encode values to icalendar property values. + + :param name: Name of the property. + :type name: string + + :param value: Value of the property. Either of a basic Python type of + any of the icalendar's own property types. + :type value: Python native type or icalendar property type. + + :param parameters: Property parameter dictionary for the value. Only + available, if encode is set to True. + :type parameters: Dictionary + + :param encode: True, if the value should be encoded to one of + icalendar's own property types (Fallback is "vText") + or False, if not. + :type encode: Boolean + + :returns: icalendar property value + """ + if not encode: + return value + if isinstance(value, types_factory.all_types): + # Don't encode already encoded values. + obj = value + else: + klass = types_factory.for_property(name) + obj = klass(value) + if parameters: + if not hasattr(obj, "params"): + obj.params = Parameters() + for key, item in parameters.items(): + if item is None: + if key in obj.params: + del obj.params[key] + else: + obj.params[key] = item + return obj + + def add(self, name, value, parameters=None, encode=1): + """Add a property. + + :param name: Name of the property. + :type name: string + + :param value: Value of the property. Either of a basic Python type of + any of the icalendar's own property types. + :type value: Python native type or icalendar property type. + + :param parameters: Property parameter dictionary for the value. Only + available, if encode is set to True. + :type parameters: Dictionary + + :param encode: True, if the value should be encoded to one of + icalendar's own property types (Fallback is "vText") + or False, if not. + :type encode: Boolean + + :returns: None + """ + if isinstance(value, datetime) and name.lower() in ( + "dtstamp", + "created", + "last-modified", + ): + # RFC expects UTC for those... force value conversion. + value = tzp.localize_utc(value) + + # encode value + if ( + encode + and isinstance(value, list) + and name.lower() not in ["rdate", "exdate", "categories"] + ): + # Individually convert each value to an ical type except rdate and + # exdate, where lists of dates might be passed to vDDDLists. + value = [self._encode(name, v, parameters, encode) for v in value] + else: + value = self._encode(name, value, parameters, encode) + + # set value + if name in self: + # If property already exists, append it. + oldval = self[name] + if isinstance(oldval, list): + if isinstance(value, list): + value = oldval + value + else: + oldval.append(value) + value = oldval + else: + value = [oldval, value] + self[name] = value + + def _decode(self, name, value): + """Internal for decoding property values.""" + + # TODO: Currently the decoded method calls the icalendar.prop instances + # from_ical. We probably want to decode properties into Python native + # types here. But when parsing from an ical string with from_ical, we + # want to encode the string into a real icalendar.prop property. + if isinstance(value, vDDDLists): + # TODO: Workaround unfinished decoding + return value + decoded = types_factory.from_ical(name, value) + # TODO: remove when proper decoded is implemented in every prop.* class + # Workaround to decode vText properly + if isinstance(decoded, vText): + decoded = decoded.encode(DEFAULT_ENCODING) + return decoded + + def decoded(self, name, default=_marker): + """Returns decoded value of property.""" + # XXX: fail. what's this function supposed to do in the end? + # -rnix + + if name in self: + value = self[name] + if isinstance(value, list): + return [self._decode(name, v) for v in value] + return self._decode(name, value) + else: + if default is _marker: + raise KeyError(name) + else: + return default + + ######################################################################## + # Inline values. A few properties have multiple values inlined in in one + # property line. These methods are used for splitting and joining these. + + def get_inline(self, name, decode=1): + """Returns a list of values (split on comma).""" + vals = [v.strip('" ') for v in q_split(self[name])] + if decode: + return [self._decode(name, val) for val in vals] + return vals + + def set_inline(self, name, values, encode=1): + """Converts a list of values into comma separated string and sets value + to that. + """ + if encode: + values = [self._encode(name, value, encode=1) for value in values] + self[name] = types_factory["inline"](q_join(values)) + + ######################### + # Handling of components + + def add_component(self, component: Component): + """Add a subcomponent to this component.""" + self.subcomponents.append(component) + + def _walk(self, name, select): + """Walk to given component.""" + result = [] + if (name is None or self.name == name) and select(self): + result.append(self) + for subcomponent in self.subcomponents: + result += subcomponent._walk(name, select) + return result + + def walk(self, name=None, select=lambda c: True) -> list[Component]: + """Recursively traverses component and subcomponents. Returns sequence + of same. If name is passed, only components with name will be returned. + + :param name: The name of the component or None such as ``VEVENT``. + :param select: A function that takes the component as first argument + and returns True/False. + :returns: A list of components that match. + :rtype: list[Component] + """ + if name is not None: + name = name.upper() + return self._walk(name, select) + + ##################### + # Generation + + def property_items(self, recursive=True, sorted=True) -> list[tuple[str, object]]: + """Returns properties in this component and subcomponents as: + [(name, value), ...] + """ + vText = types_factory["text"] + properties = [("BEGIN", vText(self.name).to_ical())] + if sorted: + property_names = self.sorted_keys() + else: + property_names = self.keys() + + for name in property_names: + values = self[name] + if isinstance(values, list): + # normally one property is one line + for value in values: + properties.append((name, value)) + else: + properties.append((name, values)) + if recursive: + # recursion is fun! + for subcomponent in self.subcomponents: + properties += subcomponent.property_items(sorted=sorted) + properties.append(("END", vText(self.name).to_ical())) + return properties + + @classmethod + def from_ical(cls, st, multiple=False): + """Populates the component recursively from a string.""" + stack = [] # a stack of components + comps = [] + for line in Contentlines.from_ical(st): # raw parsing + if not line: + continue + + try: + name, params, vals = line.parts() + except ValueError as e: + # if unable to parse a line within a component + # that ignores exceptions, mark the component + # as broken and skip the line. otherwise raise. + component = stack[-1] if stack else None + if not component or not component.ignore_exceptions: + raise + component.errors.append((None, str(e))) + continue + + uname = name.upper() + # check for start of component + if uname == "BEGIN": + # try and create one of the components defined in the spec, + # otherwise get a general Components for robustness. + c_name = vals.upper() + c_class = component_factory.get(c_name, Component) + # If component factory cannot resolve ``c_name``, the generic + # ``Component`` class is used which does not have the name set. + # That's opposed to the usage of ``cls``, which represents a + # more concrete subclass with a name set (e.g. VCALENDAR). + component = c_class() + if not getattr(component, "name", ""): # undefined components + component.name = c_name + stack.append(component) + # check for end of event + elif uname == "END": + # we are done adding properties to this component + # so pop it from the stack and add it to the new top. + if not stack: + # The stack is currently empty, the input must be invalid + raise ValueError("END encountered without an accompanying BEGIN!") + + component = stack.pop() + if not stack: # we are at the end + comps.append(component) + else: + stack[-1].add_component(component) + if vals == "VTIMEZONE" and "TZID" in component: + tzp.cache_timezone_component(component) + # we are adding properties to the current top of the stack + else: + factory = types_factory.for_property(name) + component = stack[-1] if stack else None + if not component: + # only accept X-COMMENT at the end of the .ics file + # ignore these components in parsing + if uname == "X-COMMENT": + break + else: + raise ValueError( + f'Property "{name}" does not have a parent component.' + ) + datetime_names = ( + "DTSTART", + "DTEND", + "RECURRENCE-ID", + "DUE", + "RDATE", + "EXDATE", + ) + try: + if name == "FREEBUSY": + vals = vals.split(",") + if "TZID" in params: + parsed_components = [ + factory(factory.from_ical(val, params["TZID"])) + for val in vals + ] + else: + parsed_components = [ + factory(factory.from_ical(val)) for val in vals + ] + elif name in datetime_names and "TZID" in params: + parsed_components = [ + factory(factory.from_ical(vals, params["TZID"])) + ] + else: + parsed_components = [factory(factory.from_ical(vals))] + except ValueError as e: + if not component.ignore_exceptions: + raise + component.errors.append((uname, str(e))) + else: + for parsed_component in parsed_components: + parsed_component.params = params + component.add(name, parsed_component, encode=0) + + if multiple: + return comps + if len(comps) > 1: + raise ValueError( + cls._format_error( + "Found multiple components where only one is allowed", st + ) + ) + if len(comps) < 1: + raise ValueError( + cls._format_error( + "Found no components where exactly one is required", st + ) + ) + return comps[0] + + @staticmethod + def _format_error(error_description, bad_input, elipsis="[...]"): + # there's three character more in the error, ie. ' ' x2 and a ':' + max_error_length = 100 - 3 + if len(error_description) + len(bad_input) + len(elipsis) > max_error_length: + truncate_to = max_error_length - len(error_description) - len(elipsis) + return f"{error_description}: {bad_input[:truncate_to]} {elipsis}" + else: + return f"{error_description}: {bad_input}" + + def content_line(self, name, value, sorted=True): + """Returns property as content line.""" + params = getattr(value, "params", Parameters()) + return Contentline.from_parts(name, params, value, sorted=sorted) + + def content_lines(self, sorted=True): + """Converts the Component and subcomponents into content lines.""" + contentlines = Contentlines() + for name, value in self.property_items(sorted=sorted): + cl = self.content_line(name, value, sorted=sorted) + contentlines.append(cl) + contentlines.append("") # remember the empty string in the end + return contentlines + + def to_ical(self, sorted=True): + """ + :param sorted: Whether parameters and properties should be + lexicographically sorted. + """ + + content_lines = self.content_lines(sorted=sorted) + return content_lines.to_ical() + + def __repr__(self): + """String representation of class with all of it's subcomponents.""" + subs = ", ".join(str(it) for it in self.subcomponents) + return f"{self.name or type(self).__name__}({dict(self)}{', ' + subs if subs else ''})" + + def __eq__(self, other): + if len(self.subcomponents) != len(other.subcomponents): + return False + + properties_equal = super().__eq__(other) + if not properties_equal: + return False + + # The subcomponents might not be in the same order, + # neither there's a natural key we can sort the subcomponents by nor + # are the subcomponent types hashable, so we cant put them in a set to + # check for set equivalence. We have to iterate over the subcomponents + # and look for each of them in the list. + for subcomponent in self.subcomponents: + if subcomponent not in other.subcomponents: + return False + + return True + + DTSTAMP = single_utc_property( + "DTSTAMP", + """RFC 5545: + + Conformance: This property MUST be included in the "VEVENT", + "VTODO", "VJOURNAL", or "VFREEBUSY" calendar components. + + Description: In the case of an iCalendar object that specifies a + "METHOD" property, this property specifies the date and time that + the instance of the iCalendar object was created. In the case of + an iCalendar object that doesn't specify a "METHOD" property, this + property specifies the date and time that the information + associated with the calendar component was last revised in the + calendar store. + + The value MUST be specified in the UTC time format. + + In the case of an iCalendar object that doesn't specify a "METHOD" + property, this property is equivalent to the "LAST-MODIFIED" + property. + """, + ) + LAST_MODIFIED = single_utc_property( + "LAST-MODIFIED", + """RFC 5545: + + Purpose: This property specifies the date and time that the + information associated with the calendar component was last + revised in the calendar store. + + Note: This is analogous to the modification date and time for a + file in the file system. + + Conformance: This property can be specified in the "VEVENT", + "VTODO", "VJOURNAL", or "VTIMEZONE" calendar components. + """, + ) + + def is_thunderbird(self) -> bool: + """Whether this component has attributes that indicate that Mozilla Thunderbird created it.""" + return any(attr.startswith("X-MOZ-") for attr in self.keys()) + + +####################################### +# components defined in RFC 5545 + + +def create_single_property( + prop: str, + value_attr: Optional[str], + value_type: tuple[type], + type_def: type, + doc: str, + vProp: type = vDDDTypes, # noqa: N803 +): + """Create a single property getter and setter. + + :param prop: The name of the property. + :param value_attr: The name of the attribute to get the value from. + :param value_type: The type of the value. + :param type_def: The type of the property. + :param doc: The docstring of the property. + :param vProp: The type of the property from :mod:`icalendar.prop`. + """ + + def p_get(self: Component): + default = object() + result = self.get(prop, default) + if result is default: + return None + if isinstance(result, list): + raise InvalidCalendar(f"Multiple {prop} defined.") + value = result if value_attr is None else getattr(result, value_attr, result) + if not isinstance(value, value_type): + raise InvalidCalendar( + f"{prop} must be either a {' or '.join(t.__name__ for t in value_type)}, not {value}." + ) + return value + + def p_set(self: Component, value) -> None: + if value is None: + p_del(self) + return + if not isinstance(value, value_type): + raise TypeError( + f"Use {' or '.join(t.__name__ for t in value_type)}, not {type(value).__name__}." + ) + self[prop] = vProp(value) + if prop in self.exclusive: + for other_prop in self.exclusive: + if other_prop != prop: + self.pop(other_prop, None) + + p_set.__annotations__["value"] = p_get.__annotations__["return"] = Optional[ + type_def + ] + + def p_del(self: Component): + self.pop(prop) + + p_doc = f"""The {prop} property. + + {doc} + + Accepted values: {', '.join(t.__name__ for t in value_type)}. + If the attribute has invalid values, we raise InvalidCalendar. + If the value is absent, we return None. + You can also delete the value with del or by setting it to None. + """ + return property(p_get, p_set, p_del, p_doc) + + +_X_MOZ_SNOOZE_TIME = single_utc_property( + "X-MOZ-SNOOZE-TIME", "Thunderbird: Alarms before this time are snoozed." +) +_X_MOZ_LASTACK = single_utc_property( + "X-MOZ-LASTACK", "Thunderbird: Alarms before this time are acknowledged." +) + + +def _get_duration(self: Component) -> Optional[timedelta]: + """Getter for property DURATION.""" + default = object() + duration = self.get("duration", default) + if isinstance(duration, vDDDTypes): + return duration.dt + if isinstance(duration, vDuration): + return duration.td + if duration is not default and not isinstance(duration, timedelta): + raise InvalidCalendar( + f"DURATION must be a timedelta, not {type(duration).__name__}." + ) + return None + + +def _set_duration(self: Component, value: Optional[timedelta]): + """Setter for property DURATION.""" + if value is None: + self.pop("duration", None) + return + if not isinstance(value, timedelta): + raise TypeError(f"Use timedelta, not {type(value).__name__}.") + self["duration"] = vDuration(value) + self.pop("DTEND") + self.pop("DUE") + + +def _del_duration(self: Component): + """Delete property DURATION.""" + self.pop("DURATION") + + +_doc_duration = """The DURATION property. + +The "DTSTART" property for a "{component}" specifies the inclusive start of the event. +The "DURATION" property in conjunction with the DTSTART property +for a "{component}" calendar component specifies the non-inclusive end +of the event. + +If you would like to calculate the duration of a {component}, do not use this. +Instead use the duration property (lower case). +""" + + +class Event(Component): + """ + A "VEVENT" calendar component is a grouping of component + properties that represents a scheduled amount of time on a + calendar. For example, it can be an activity, such as a one-hour + long department meeting from 8:00 AM to 9:00 AM, tomorrow. + """ + + name = "VEVENT" + + canonical_order = ( + "SUMMARY", + "DTSTART", + "DTEND", + "DURATION", + "DTSTAMP", + "UID", + "RECURRENCE-ID", + "SEQUENCE", + "RRULE", + "RDATE", + "EXDATE", + ) + + required = ( + "UID", + "DTSTAMP", + ) + singletons = ( + "CLASS", + "CREATED", + "COLOR", + "DESCRIPTION", + "DTSTART", + "GEO", + "LAST-MODIFIED", + "LOCATION", + "ORGANIZER", + "PRIORITY", + "DTSTAMP", + "SEQUENCE", + "STATUS", + "SUMMARY", + "TRANSP", + "URL", + "RECURRENCE-ID", + "DTEND", + "DURATION", + "UID", + ) + exclusive = ( + "DTEND", + "DURATION", + ) + multiple = ( + "ATTACH", + "ATTENDEE", + "CATEGORIES", + "COMMENT", + "CONTACT", + "EXDATE", + "RSTATUS", + "RELATED", + "RESOURCES", + "RDATE", + "RRULE", + ) + ignore_exceptions = True + + @property + def alarms(self) -> Alarms: + """Compute the alarm times for this component. + + >>> from icalendar import Event + >>> event = Event.example("rfc_9074_example_1") + >>> len(event.alarms.times) + 1 + >>> alarm_time = event.alarms.times[0] + >>> alarm_time.trigger # The time when the alarm pops up + datetime.datetime(2021, 3, 2, 10, 15, tzinfo=ZoneInfo(key='America/New_York')) + >>> alarm_time.is_active() # This alarm has not been acknowledged + True + + Note that this only uses DTSTART and DTEND, but ignores + RDATE, EXDATE, and RRULE properties. + """ + from icalendar.alarms import Alarms + + return Alarms(self) + + @classmethod + def example(cls, name: str = "rfc_9074_example_3") -> Event: + """Return the calendar example with the given name.""" + return cls.from_ical(get_example("events", name)) + + DTSTART = create_single_property( + "DTSTART", + "dt", + (datetime, date), + date, + 'The "DTSTART" property for a "VEVENT" specifies the inclusive start of the event.', + ) + DTEND = create_single_property( + "DTEND", + "dt", + (datetime, date), + date, + 'The "DTEND" property for a "VEVENT" calendar component specifies the non-inclusive end of the event.', + ) + + def _get_start_end_duration(self): + """Verify the calendar validity and return the right attributes.""" + start = self.DTSTART + end = self.DTEND + duration = self.DURATION + if duration is not None and end is not None: + raise InvalidCalendar( + "Only one of DTEND and DURATION may be in a VEVENT, not both." + ) + if ( + isinstance(start, date) + and not isinstance(start, datetime) + and duration is not None + and duration.seconds != 0 + ): + raise InvalidCalendar( + "When DTSTART is a date, DURATION must be of days or weeks." + ) + if start is not None and end is not None and is_date(start) != is_date(end): + raise InvalidCalendar( + "DTSTART and DTEND must be of the same type, either date or datetime." + ) + return start, end, duration + + DURATION = property( + _get_duration, + _set_duration, + _del_duration, + _doc_duration.format(component="VEVENT"), + ) + + @property + def duration(self) -> timedelta: + """The duration of the VEVENT. + + This duration is calculated from the start and end of the event. + You cannot set the duration as it is unclear what happens to start and end. + """ + return self.end - self.start + + @property + def start(self) -> date | datetime: + """The start of the component. + + Invalid values raise an InvalidCalendar. + If there is no start, we also raise an IncompleteComponent error. + + You can get the start, end and duration of an event as follows: + + >>> from datetime import datetime + >>> from icalendar import Event + >>> event = Event() + >>> event.start = datetime(2021, 1, 1, 12) + >>> event.end = datetime(2021, 1, 1, 12, 30) # 30 minutes + >>> event.duration # 1800 seconds == 30 minutes + datetime.timedelta(seconds=1800) + >>> print(event.to_ical()) + BEGIN:VEVENT + DTSTART:20210101T120000 + DTEND:20210101T123000 + END:VEVENT + """ + start = self._get_start_end_duration()[0] + if start is None: + raise IncompleteComponent("No DTSTART given.") + return start + + @start.setter + def start(self, start: Optional[date | datetime]): + """Set the start.""" + self.DTSTART = start + + @property + def end(self) -> date | datetime: + """The end of the component. + + Invalid values raise an InvalidCalendar error. + If there is no end, we also raise an IncompleteComponent error. + """ + start, end, duration = self._get_start_end_duration() + if end is None and duration is None: + if start is None: + raise IncompleteComponent("No DTEND or DURATION+DTSTART given.") + if is_date(start): + return start + timedelta(days=1) + return start + if duration is not None: + if start is not None: + return start + duration + raise IncompleteComponent("No DTEND or DURATION+DTSTART given.") + return end + + @end.setter + def end(self, end: date | datetime | None): + """Set the end.""" + self.DTEND = end + + X_MOZ_SNOOZE_TIME = _X_MOZ_SNOOZE_TIME + X_MOZ_LASTACK = _X_MOZ_LASTACK + color = color_property + sequence = sequence_property + categories = categories_property + rdates = rdates_property + exdates = exdates_property + rrules = rrules_property + uid = uid_property + + +class Todo(Component): + """ + A "VTODO" calendar component is a grouping of component + properties that represents an action item or assignment. For + example, it can be used to represent an item of work assigned to + an individual, such as "Prepare for the upcoming conference + seminar on Internet Calendaring". + """ + + name = "VTODO" + + required = ( + "UID", + "DTSTAMP", + ) + singletons = ( + "CLASS", + "COLOR", + "COMPLETED", + "CREATED", + "DESCRIPTION", + "DTSTAMP", + "DTSTART", + "GEO", + "LAST-MODIFIED", + "LOCATION", + "ORGANIZER", + "PERCENT-COMPLETE", + "PRIORITY", + "RECURRENCE-ID", + "SEQUENCE", + "STATUS", + "SUMMARY", + "UID", + "URL", + "DUE", + "DURATION", + ) + exclusive = ( + "DUE", + "DURATION", + ) + multiple = ( + "ATTACH", + "ATTENDEE", + "CATEGORIES", + "COMMENT", + "CONTACT", + "EXDATE", + "RSTATUS", + "RELATED", + "RESOURCES", + "RDATE", + "RRULE", + ) + DTSTART = create_single_property( + "DTSTART", + "dt", + (datetime, date), + date, + 'The "DTSTART" property for a "VTODO" specifies the inclusive start of the Todo.', + ) + DUE = create_single_property( + "DUE", + "dt", + (datetime, date), + date, + 'The "DUE" property for a "VTODO" calendar component specifies the non-inclusive end of the Todo.', + ) + DURATION = property( + _get_duration, + _set_duration, + _del_duration, + _doc_duration.format(component="VTODO"), + ) + + def _get_start_end_duration(self): + """Verify the calendar validity and return the right attributes.""" + start = self.DTSTART + end = self.DUE + duration = self.DURATION + if duration is not None and end is not None: + raise InvalidCalendar( + "Only one of DUE and DURATION may be in a VTODO, not both." + ) + if ( + isinstance(start, date) + and not isinstance(start, datetime) + and duration is not None + and duration.seconds != 0 + ): + raise InvalidCalendar( + "When DTSTART is a date, DURATION must be of days or weeks." + ) + if start is not None and end is not None and is_date(start) != is_date(end): + raise InvalidCalendar( + "DTSTART and DUE must be of the same type, either date or datetime." + ) + return start, end, duration + + @property + def start(self) -> date | datetime: + """The start of the VTODO. + + Invalid values raise an InvalidCalendar. + If there is no start, we also raise an IncompleteComponent error. + + You can get the start, end and duration of a Todo as follows: + + >>> from datetime import datetime + >>> from icalendar import Todo + >>> todo = Todo() + >>> todo.start = datetime(2021, 1, 1, 12) + >>> todo.end = datetime(2021, 1, 1, 12, 30) # 30 minutes + >>> todo.duration # 1800 seconds == 30 minutes + datetime.timedelta(seconds=1800) + >>> print(todo.to_ical()) + BEGIN:VTODO + DTSTART:20210101T120000 + DUE:20210101T123000 + END:VTODO + """ + start = self._get_start_end_duration()[0] + if start is None: + raise IncompleteComponent("No DTSTART given.") + return start + + @start.setter + def start(self, start: Optional[date | datetime]): + """Set the start.""" + self.DTSTART = start + + @property + def end(self) -> date | datetime: + """The end of the component. + + Invalid values raise an InvalidCalendar error. + If there is no end, we also raise an IncompleteComponent error. + """ + start, end, duration = self._get_start_end_duration() + if end is None and duration is None: + if start is None: + raise IncompleteComponent("No DUE or DURATION+DTSTART given.") + if is_date(start): + return start + timedelta(days=1) + return start + if duration is not None: + if start is not None: + return start + duration + raise IncompleteComponent("No DUE or DURATION+DTSTART given.") + return end + + @end.setter + def end(self, end: date | datetime | None): + """Set the end.""" + self.DUE = end + + @property + def duration(self) -> timedelta: + """The duration of the VTODO. + + This duration is calculated from the start and end of the Todo. + You cannot set the duration as it is unclear what happens to start and end. + """ + return self.end - self.start + + X_MOZ_SNOOZE_TIME = _X_MOZ_SNOOZE_TIME + X_MOZ_LASTACK = _X_MOZ_LASTACK + + @property + def alarms(self) -> Alarms: + """Compute the alarm times for this component. + + >>> from datetime import datetime + >>> from icalendar import Todo + >>> todo = Todo() # empty without alarms + >>> todo.start = datetime(2024, 10, 26, 10, 21) + >>> len(todo.alarms.times) + 0 + + Note that this only uses DTSTART and DUE, but ignores + RDATE, EXDATE, and RRULE properties. + """ + from icalendar.alarms import Alarms + + return Alarms(self) + + color = color_property + sequence = sequence_property + categories = categories_property + rdates = rdates_property + exdates = exdates_property + rrules = rrules_property + uid = uid_property + + +class Journal(Component): + """A descriptive text at a certain time or associated with a component. + + A "VJOURNAL" calendar component is a grouping of + component properties that represent one or more descriptive text + notes associated with a particular calendar date. The "DTSTART" + property is used to specify the calendar date with which the + journal entry is associated. Generally, it will have a DATE value + data type, but it can also be used to specify a DATE-TIME value + data type. Examples of a journal entry include a daily record of + a legislative body or a journal entry of individual telephone + contacts for the day or an ordered list of accomplishments for the + day. + """ + + name = "VJOURNAL" + + required = ( + "UID", + "DTSTAMP", + ) + singletons = ( + "CLASS", + "COLOR", + "CREATED", + "DTSTART", + "DTSTAMP", + "LAST-MODIFIED", + "ORGANIZER", + "RECURRENCE-ID", + "SEQUENCE", + "STATUS", + "SUMMARY", + "UID", + "URL", + ) + multiple = ( + "ATTACH", + "ATTENDEE", + "CATEGORIES", + "COMMENT", + "CONTACT", + "EXDATE", + "RELATED", + "RDATE", + "RRULE", + "RSTATUS", + "DESCRIPTION", + ) + + DTSTART = create_single_property( + "DTSTART", + "dt", + (datetime, date), + date, + 'The "DTSTART" property for a "VJOURNAL" that specifies the exact date at which the journal entry was made.', + ) + + @property + def start(self) -> date: + """The start of the Journal. + + The "DTSTART" + property is used to specify the calendar date with which the + journal entry is associated. + """ + start = self.DTSTART + if start is None: + raise IncompleteComponent("No DTSTART given.") + return start + + @start.setter + def start(self, value: datetime | date) -> None: + """Set the start of the journal.""" + self.DTSTART = value + + end = start + + @property + def duration(self) -> timedelta: + """The journal has no duration: timedelta(0).""" + return timedelta(0) + + color = color_property + sequence = sequence_property + categories = categories_property + rdates = rdates_property + exdates = exdates_property + rrules = rrules_property + uid = uid_property + + +class FreeBusy(Component): + """ + A "VFREEBUSY" calendar component is a grouping of component + properties that represents either a request for free or busy time + information, a reply to a request for free or busy time + information, or a published set of busy time information. + """ + + name = "VFREEBUSY" + + required = ( + "UID", + "DTSTAMP", + ) + singletons = ( + "CONTACT", + "DTSTART", + "DTEND", + "DTSTAMP", + "ORGANIZER", + "UID", + "URL", + ) + multiple = ( + "ATTENDEE", + "COMMENT", + "FREEBUSY", + "RSTATUS", + ) + uid = uid_property + + +class Timezone(Component): + """ + A "VTIMEZONE" calendar component is a grouping of component + properties that defines a time zone. It is used to describe the + way in which a time zone changes its offset from UTC over time. + """ + + subcomponents: list[TimezoneStandard|TimezoneDaylight] + + name = "VTIMEZONE" + canonical_order = ("TZID",) + required = ("TZID",) # it also requires one of components DAYLIGHT and STANDARD + singletons = ( + "TZID", + "LAST-MODIFIED", + "TZURL", + ) + + _DEFAULT_FIRST_DATE = date(1970, 1, 1) + _DEFAULT_LAST_DATE = date(2038, 1, 1) + + @classmethod + def example(cls, name: str = "pacific_fiji") -> Calendar: + """Return the timezone example with the given name.""" + return cls.from_ical(get_example("timezones", name)) + + @staticmethod + def _extract_offsets(component: TimezoneDaylight | TimezoneStandard, tzname: str): + """extract offsets and transition times from a VTIMEZONE component + :param component: a STANDARD or DAYLIGHT component + :param tzname: the name of the zone + """ + offsetfrom = component.TZOFFSETFROM + offsetto = component.TZOFFSETTO + dtstart = component.DTSTART + + # offsets need to be rounded to the next minute, we might loose up + # to 30 seconds accuracy, but it can't be helped (datetime + # supposedly cannot handle smaller offsets) + offsetto_s = int((offsetto.seconds + 30) / 60) * 60 + offsetto = timedelta(days=offsetto.days, seconds=offsetto_s) + offsetfrom_s = int((offsetfrom.seconds + 30) / 60) * 60 + offsetfrom = timedelta(days=offsetfrom.days, seconds=offsetfrom_s) + + # expand recurrences + if "RRULE" in component: + # to be paranoid about correct weekdays + # evaluate the rrule with the current offset + tzi = dateutil.tz.tzoffset("(offsetfrom)", offsetfrom) + rrstart = dtstart.replace(tzinfo=tzi) + + rrulestr = component["RRULE"].to_ical().decode("utf-8") + rrule = dateutil.rrule.rrulestr(rrulestr, dtstart=rrstart) + tzp.fix_rrule_until(rrule, component["RRULE"]) + + # constructing the timezone requires UTC transition times. + # here we construct local times without tzinfo, the offset to UTC + # gets subtracted in to_tz(). + transtimes = [dt.replace(tzinfo=None) for dt in rrule] + + # or rdates + elif "RDATE" in component: + if not isinstance(component["RDATE"], list): + rdates = [component["RDATE"]] + else: + rdates = component["RDATE"] + transtimes = [dtstart] + [leaf.dt for tree in rdates for leaf in tree.dts] + else: + transtimes = [dtstart] + + transitions = [ + (transtime, offsetfrom, offsetto, tzname) for transtime in set(transtimes) + ] + + if component.name == "STANDARD": + is_dst = 0 + elif component.name == "DAYLIGHT": + is_dst = 1 + return is_dst, transitions + + @staticmethod + def _make_unique_tzname(tzname, tznames): + """ + :param tzname: Candidate tzname + :param tznames: Other tznames + """ + # TODO better way of making sure tznames are unique + while tzname in tznames: + tzname += "_1" + tznames.add(tzname) + return tzname + + def to_tz(self, tzp: TZP = tzp, lookup_tzid: bool = True): + """convert this VTIMEZONE component to a timezone object + + :param tzp: timezone provider to use + :param lookup_tzid: whether to use the TZID property to look up existing + timezone definitions with tzp. + If it is False, a new timezone will be created. + If it is True, the existing timezone will be used + if it exists, otherwise a new timezone will be created. + """ + if lookup_tzid: + tz = tzp.timezone(self.tz_name) + if tz is not None: + return tz + return tzp.create_timezone(self) + + @property + def tz_name(self) -> str: + """Return the name of the timezone component. + + Please note that the names of the timezone are different from this name + and may change with winter/summer time. + """ + try: + return str(self["TZID"]) + except UnicodeEncodeError: + return self["TZID"].encode("ascii", "replace") + + def get_transitions( + self, + ) -> Tuple[List[datetime], List[Tuple[timedelta, timedelta, str]]]: + """Return a tuple of (transition_times, transition_info) + + - transition_times = [datetime, ...] + - transition_info = [(TZOFFSETTO, dts_offset, tzname)] + + """ + zone = self.tz_name + transitions = [] + dst = {} + tznames = set() + for component in self.walk(): + if type(component) == Timezone: + continue + if is_date(component["DTSTART"].dt): + component.DTSTART = to_datetime(component["DTSTART"].dt) + assert isinstance( + component["DTSTART"].dt, datetime + ), "VTIMEZONEs sub-components' DTSTART must be of type datetime, not date" + try: + tzname = str(component["TZNAME"]) + except UnicodeEncodeError: + tzname = component["TZNAME"].encode("ascii", "replace") + tzname = self._make_unique_tzname(tzname, tznames) + except KeyError: + # for whatever reason this is str/unicode + tzname = ( + f"{zone}_{component['DTSTART'].to_ical().decode('utf-8')}_" + + f"{component['TZOFFSETFROM'].to_ical()}_" + + f"{component['TZOFFSETTO'].to_ical()}" + ) + tzname = self._make_unique_tzname(tzname, tznames) + + dst[tzname], component_transitions = self._extract_offsets( + component, tzname + ) + transitions.extend(component_transitions) + + transitions.sort() + transition_times = [ + transtime - osfrom for transtime, osfrom, _, _ in transitions + ] + + # transition_info is a list with tuples in the format + # (utcoffset, dstoffset, name) + # dstoffset = 0, if current transition is to standard time + # = this_utcoffset - prev_standard_utcoffset, otherwise + transition_info = [] + for num, (transtime, osfrom, osto, name) in enumerate(transitions): + dst_offset = False + if not dst[name]: + dst_offset = timedelta(seconds=0) + else: + # go back in time until we find a transition to dst + for index in range(num - 1, -1, -1): + if not dst[transitions[index][3]]: # [3] is the name + dst_offset = osto - transitions[index][2] # [2] is osto # noqa + break + # when the first transition is to dst, we didn't find anything + # in the past, so we have to look into the future + if not dst_offset: + for index in range(num, len(transitions)): + if not dst[transitions[index][3]]: # [3] is the name + dst_offset = ( + osto - transitions[index][2] + ) # [2] is osto # noqa + break + assert dst_offset is not False + transition_info.append((osto, dst_offset, name)) + return transition_times, transition_info + + # binary search + _from_tzinfo_skip_search = [ + timedelta(days=days) for days in (64, 32, 16, 8, 4, 2, 1) + ] + [ + # we know it happens in the night usually around 1am + timedelta(hours=4), + timedelta(hours=1), + # adding some minutes and seconds for faster search + timedelta(minutes=20), + timedelta(minutes=5), + timedelta(minutes=1), + timedelta(seconds=20), + timedelta(seconds=5), + timedelta(seconds=1), + ] + + @classmethod + def from_tzinfo( + cls, + timezone: tzinfo, + tzid: Optional[str] = None, + first_date: date = _DEFAULT_FIRST_DATE, + last_date: date = _DEFAULT_LAST_DATE, + ) -> Timezone: + """Return a VTIMEZONE component from a timezone object. + + This works with pytz and zoneinfo and any other timezone. + The offsets are calculated from the tzinfo object. + + Parameters: + + :param tzinfo: the timezone object + :param tzid: the tzid for this timezone. If None, it will be extracted from the tzinfo. + :param first_date: a datetime that is earlier than anything that happens in the calendar + :param last_date: a datetime that is later than anything that happens in the calendar + :raises ValueError: If we have no tzid and cannot extract one. + + .. note:: + This can take some time. Please cache the results. + """ + if tzid is None: + tzid = tzid_from_tzinfo(timezone) + if tzid is None: + raise ValueError( + f"Cannot get TZID from {timezone}. Please set the tzid parameter." + ) + normalize = getattr(timezone, "normalize", lambda dt: dt) # pytz compatibility + first_datetime = datetime(first_date.year, first_date.month, first_date.day) # noqa: DTZ001 + last_datetime = datetime(last_date.year, last_date.month, last_date.day) # noqa: DTZ001 + if hasattr(timezone, "localize"): # pytz compatibility + first_datetime = timezone.localize(first_datetime) + last_datetime = timezone.localize(last_datetime) + else: + first_datetime = first_datetime.replace(tzinfo=timezone) + last_datetime = last_datetime.replace(tzinfo=timezone) + # from, to, tzname, is_standard -> start + offsets: dict[ + tuple[Optional[timedelta], timedelta, str, bool], list[datetime] + ] = defaultdict(list) + start = first_datetime + offset_to = None + while start < last_datetime: + offset_from = offset_to + end = start + offset_to = end.utcoffset() + for add_offset in cls._from_tzinfo_skip_search: + last_end = end # we need to save this as we might be left and right of the time change + end = normalize(end + add_offset) + try: + while end.utcoffset() == offset_to: + last_end = end + end = normalize(end + add_offset) + except OverflowError: + # zoninfo does not go all the way + break + # retract if we overshoot + end = last_end + # Now, start (inclusive) -> end (exclusive) are one timezone + is_standard = start.dst() == timedelta() + name = start.tzname() + if name is None: + name = str(offset_to) + key = (offset_from, offset_to, name, is_standard) + # first_key = (None,) + key[1:] + # if first_key in offsets: + # # remove the first one and claim it changes at that day + # offsets[first_key] = offsets.pop(first_key) + offsets[key].append(start.replace(tzinfo=None)) + start = normalize(end + cls._from_tzinfo_skip_search[-1]) + tz = cls() + tz.add("TZID", tzid) + tz.add("COMMENT", f"This timezone only works from {first_date} to {last_date}.") + for (offset_from, offset_to, tzname, is_standard), starts in offsets.items(): + first_start = min(starts) + starts.remove(first_start) + if first_start.date() == last_date: + first_start = datetime(last_date.year, last_date.month, last_date.day) # noqa: DTZ001 + subcomponent = TimezoneStandard() if is_standard else TimezoneDaylight() + if offset_from is None: + offset_from = offset_to # noqa: PLW2901 + subcomponent.TZOFFSETFROM = offset_from + subcomponent.TZOFFSETTO = offset_to + subcomponent.add("TZNAME", tzname) + subcomponent.DTSTART = first_start + if starts: + subcomponent.add("RDATE", starts) + tz.add_component(subcomponent) + return tz + + @classmethod + def from_tzid( + cls, + tzid: str, + tzp: TZP = tzp, + first_date: date = _DEFAULT_FIRST_DATE, + last_date: date = _DEFAULT_LAST_DATE, + ) -> Timezone: + """Create a VTIMEZONE from a tzid like ``"Europe/Berlin"``. + + :param tzid: the id of the timezone + :param tzp: the timezone provider + :param first_date: a datetime that is earlier than anything that happens in the calendar + :param last_date: a datetime that is later than anything that happens in the calendar + :raises ValueError: If the tzid is unknown. + + >>> from icalendar import Timezone + >>> tz = Timezone.from_tzid("Europe/Berlin") + >>> print(tz.to_ical()[:36]) + BEGIN:VTIMEZONE + TZID:Europe/Berlin + + .. note:: + This can take some time. Please cache the results. + """ + tz = tzp.timezone(tzid) + if tz is None: + raise ValueError(f"Unkown timezone {tzid}.") + return cls.from_tzinfo(tz, tzid, first_date, last_date) + + @property + def standard(self) -> list[TimezoneStandard]: + """The STANDARD subcomponents as a list.""" + return self.walk("STANDARD") + + @property + def daylight(self) -> list[TimezoneDaylight]: + """The DAYLIGHT subcomponents as a list. + + These are for the daylight saving time. + """ + return self.walk("DAYLIGHT") + + +class TimezoneStandard(Component): + """ + The "STANDARD" sub-component of "VTIMEZONE" defines the standard + time offset from UTC for a time zone. It represents a time zone's + standard time, typically used during winter months in locations + that observe Daylight Saving Time. + """ + + name = "STANDARD" + required = ("DTSTART", "TZOFFSETTO", "TZOFFSETFROM") + singletons = ( + "DTSTART", + "TZOFFSETTO", + "TZOFFSETFROM", + ) + multiple = ("COMMENT", "RDATE", "TZNAME", "RRULE", "EXDATE") + + DTSTART = create_single_property( + "DTSTART", + "dt", + (datetime,), + datetime, + """The mandatory "DTSTART" property gives the effective onset date + and local time for the time zone sub-component definition. + "DTSTART" in this usage MUST be specified as a date with a local + time value.""", + ) + TZOFFSETTO = create_single_property( + "TZOFFSETTO", + "td", + (timedelta,), + timedelta, + """The mandatory "TZOFFSETTO" property gives the UTC offset for the + time zone sub-component (Standard Time or Daylight Saving Time) + when this observance is in use. + """, + vUTCOffset, + ) + TZOFFSETFROM = create_single_property( + "TZOFFSETFROM", + "td", + (timedelta,), + timedelta, + """The mandatory "TZOFFSETFROM" property gives the UTC offset that is + in use when the onset of this time zone observance begins. + "TZOFFSETFROM" is combined with "DTSTART" to define the effective + onset for the time zone sub-component definition. For example, + the following represents the time at which the observance of + Standard Time took effect in Fall 1967 for New York City: + + DTSTART:19671029T020000 + TZOFFSETFROM:-0400 + """, + vUTCOffset, + ) + rdates = rdates_property + exdates = exdates_property + rrules = rrules_property + + +class TimezoneDaylight(Component): + """ + The "DAYLIGHT" sub-component of "VTIMEZONE" defines the daylight + saving time offset from UTC for a time zone. It represents a time + zone's daylight saving time, typically used during summer months + in locations that observe Daylight Saving Time. + """ + + name = "DAYLIGHT" + required = TimezoneStandard.required + singletons = TimezoneStandard.singletons + multiple = TimezoneStandard.multiple + + DTSTART = TimezoneStandard.DTSTART + TZOFFSETTO = TimezoneStandard.TZOFFSETTO + TZOFFSETFROM = TimezoneStandard.TZOFFSETFROM + + rdates = rdates_property + exdates = exdates_property + rrules = rrules_property + +class Alarm(Component): + """ + A "VALARM" calendar component is a grouping of component + properties that defines an alarm or reminder for an event or a + to-do. For example, it may be used to define a reminder for a + pending event or an overdue to-do. + """ + + name = "VALARM" + # some properties MAY/MUST/MUST NOT appear depending on ACTION value + required = ( + "ACTION", + "TRIGGER", + ) + singletons = ( + "ATTACH", + "ACTION", + "DESCRIPTION", + "SUMMARY", + "TRIGGER", + "DURATION", + "REPEAT", + "UID", + "PROXIMITY", + "ACKNOWLEDGED", + ) + inclusive = ( + ( + "DURATION", + "REPEAT", + ), + ( + "SUMMARY", + "ATTENDEE", + ), + ) + multiple = ("ATTENDEE", "ATTACH", "RELATED-TO") + + REPEAT = single_int_property( + "REPEAT", 0, + """The REPEAT property of an alarm component. + + The alarm can be defined such that it triggers repeatedly. A + definition of an alarm with a repeating trigger MUST include both + the "DURATION" and "REPEAT" properties. The "DURATION" property + specifies the delay period, after which the alarm will repeat. + The "REPEAT" property specifies the number of additional + repetitions that the alarm will be triggered. This repetition + count is in addition to the initial triggering of the alarm. + """ + ) + + DURATION = property( + _get_duration, + _set_duration, + _del_duration, + """The DURATION property of an alarm component. + + The alarm can be defined such that it triggers repeatedly. A + definition of an alarm with a repeating trigger MUST include both + the "DURATION" and "REPEAT" properties. The "DURATION" property + specifies the delay period, after which the alarm will repeat. + """, + ) + + ACKNOWLEDGED = single_utc_property( + "ACKNOWLEDGED", + """This is defined in RFC 9074: + + Purpose: This property specifies the UTC date and time at which the + corresponding alarm was last sent or acknowledged. + + This property is used to specify when an alarm was last sent or acknowledged. + This allows clients to determine when a pending alarm has been acknowledged + by a calendar user so that any alerts can be dismissed across multiple devices. + It also allows clients to track repeating alarms or alarms on recurring events or + to-dos to ensure that the right number of missed alarms can be tracked. + + Clients SHOULD set this property to the current date-time value in UTC + when a calendar user acknowledges a pending alarm. Certain kinds of alarms, + such as email-based alerts, might not provide feedback as to when the calendar user + sees them. For those kinds of alarms, the client SHOULD set this property + when the alarm is triggered and the action is successfully carried out. + + When an alarm is triggered on a client, clients can check to see if an "ACKNOWLEDGED" + property is present. If it is, and the value of that property is greater than or + equal to the computed trigger time for the alarm, then the client SHOULD NOT trigger + the alarm. Similarly, if an alarm has been triggered and + an "alert" has been presented to a calendar user, clients can monitor + the iCalendar data to determine whether an "ACKNOWLEDGED" property is added or + changed in the alarm component. If the value of any "ACKNOWLEDGED" property + in the alarm changes and is greater than or equal to the trigger time of the alarm, + then clients SHOULD dismiss or cancel any "alert" presented to the calendar user. + """, + ) + + TRIGGER = create_single_property( + "TRIGGER", + "dt", + (datetime, timedelta), + Optional[Union[timedelta, datetime]], + """Purpose: This property specifies when an alarm will trigger. + + Value Type: The default value type is DURATION. The value type can + be set to a DATE-TIME value type, in which case the value MUST + specify a UTC-formatted DATE-TIME value. + + Either a positive or negative duration may be specified for the + "TRIGGER" property. An alarm with a positive duration is + triggered after the associated start or end of the event or to-do. + An alarm with a negative duration is triggered before the + associated start or end of the event or to-do.""", + ) + + @property + def TRIGGER_RELATED(self) -> str: + """The RELATED parameter of the TRIGGER property. + + Values are either "START" (default) or "END". + + A value of START will set the alarm to trigger off the + start of the associated event or to-do. A value of END will set + the alarm to trigger off the end of the associated event or to-do. + + In this example, we create an alarm that triggers two hours after the + end of its parent component: + + >>> from icalendar import Alarm + >>> from datetime import timedelta + >>> alarm = Alarm() + >>> alarm.TRIGGER = timedelta(hours=2) + >>> alarm.TRIGGER_RELATED = "END" + """ + trigger = self.get("TRIGGER") + if trigger is None: + return "START" + return trigger.params.get("RELATED", "START") + + @TRIGGER_RELATED.setter + def TRIGGER_RELATED(self, value: str): + """Set "START" or "END".""" + trigger = self.get("TRIGGER") + if trigger is None: + raise ValueError( + "You must set a TRIGGER before setting the RELATED parameter." + ) + trigger.params["RELATED"] = value + + class Triggers(NamedTuple): + """The computed times of alarm triggers. + + start - triggers relative to the start of the Event or Todo (timedelta) + + end - triggers relative to the end of the Event or Todo (timedelta) + + absolute - triggers at a datetime in UTC + """ + + start: tuple[timedelta] + end: tuple[timedelta] + absolute: tuple[datetime] + + @property + def triggers(self): + """The computed triggers of an Alarm. + + This takes the TRIGGER, DURATION and REPEAT properties into account. + + Here, we create an alarm that triggers 3 times before the start of the + parent component: + + >>> from icalendar import Alarm + >>> from datetime import timedelta + >>> alarm = Alarm() + >>> alarm.TRIGGER = timedelta(hours=-4) # trigger 4 hours before START + >>> alarm.DURATION = timedelta(hours=1) # after 1 hour trigger again + >>> alarm.REPEAT = 2 # trigger 2 more times + >>> alarm.triggers.start == (timedelta(hours=-4), timedelta(hours=-3), timedelta(hours=-2)) + True + >>> alarm.triggers.end + () + >>> alarm.triggers.absolute + () + """ + start = [] + end = [] + absolute = [] + trigger = self.TRIGGER + if trigger is not None: + if isinstance(trigger, date): + absolute.append(trigger) + add = absolute + elif self.TRIGGER_RELATED == "START": + start.append(trigger) + add = start + else: + end.append(trigger) + add = end + duration = self.DURATION + if duration is not None: + for _ in range(self.REPEAT): + add.append(add[-1] + duration) + return self.Triggers( + start=tuple(start), end=tuple(end), absolute=tuple(absolute) + ) + + uid = single_string_property( + "UID", + uid_property.__doc__, + "X-ALARMUID", + ) + + +class Calendar(Component): + """ + The "VCALENDAR" object is a collection of calendar information. + This information can include a variety of components, such as + "VEVENT", "VTODO", "VJOURNAL", "VFREEBUSY", "VTIMEZONE", or any + other type of calendar component. + """ + + name = "VCALENDAR" + canonical_order = ( + "VERSION", + "PRODID", + "CALSCALE", + "METHOD", + "DESCRIPTION", + "X-WR-CALDESC", + "NAME", + "X-WR-CALNAME", + ) + required = ( + "PRODID", + "VERSION", + ) + singletons = ( + "PRODID", + "VERSION", + "CALSCALE", + "METHOD", + "COLOR", # RFC 7986 + ) + multiple = ( + "CATEGORIES", # RFC 7986 + "DESCRIPTION", # RFC 7986 + "NAME", # RFC 7986 + ) + + @classmethod + def example(cls, name: str = "example") -> Calendar: + """Return the calendar example with the given name.""" + return cls.from_ical(get_example("calendars", name)) + + @classmethod + def from_ical(cls, st, multiple=False): + comps = Component.from_ical(st, multiple=True) + all_timezones_so_far = True + for comp in comps: + for component in comp.subcomponents: + if component.name == 'VTIMEZONE': + if all_timezones_so_far: + pass + else: + # If a preceding component refers to a VTIMEZONE defined later in the source st + # (forward references are allowed by RFC 5545), then the earlier component may have + # the wrong timezone attached. + # However, during computation of comps, all VTIMEZONEs observed do end up in + # the timezone cache. So simply re-running from_ical will rely on the cache + # for those forward references to produce the correct result. + # See test_create_america_new_york_forward_reference. + return Component.from_ical(st, multiple) + else: + all_timezones_so_far = False + + # No potentially forward VTIMEZONEs to worry about + if multiple: + return comps + if len(comps) > 1: + raise ValueError(cls._format_error( + 'Found multiple components where only one is allowed', st)) + if len(comps) < 1: + raise ValueError(cls._format_error( + 'Found no components where exactly one is required', st)) + return comps[0] + + @property + def events(self) -> list[Event]: + """All event components in the calendar. + + This is a shortcut to get all events. + Modifications do not change the calendar. + Use :py:meth:`Component.add_component`. + + >>> from icalendar import Calendar + >>> calendar = Calendar.example() + >>> event = calendar.events[0] + >>> event.start + datetime.date(2022, 1, 1) + >>> print(event["SUMMARY"]) + New Year's Day + """ + return self.walk("VEVENT") + + @property + def todos(self) -> list[Todo]: + """All todo components in the calendar. + + This is a shortcut to get all todos. + Modifications do not change the calendar. + Use :py:meth:`Component.add_component`. + """ + return self.walk("VTODO") + + @property + def freebusy(self) -> list[FreeBusy]: + """All FreeBusy components in the calendar. + + This is a shortcut to get all FreeBusy. + Modifications do not change the calendar. + Use :py:meth:`Component.add_component`. + """ + return self.walk("VFREEBUSY") + + def get_used_tzids(self) -> set[str]: + """The set of TZIDs in use. + + This goes through the whole calendar to find all occurrences of + timezone information like the TZID parameter in all attributes. + + >>> from icalendar import Calendar + >>> calendar = Calendar.example("timezone_rdate") + >>> calendar.get_used_tzids() + {'posix/Europe/Vaduz'} + + Even if you use UTC, this will not show up. + """ + result = set() + for name, value in self.property_items(sorted=False): + if hasattr(value, "params"): + result.add(value.params.get("TZID")) + return result - {None} + + def get_missing_tzids(self) -> set[str]: + """The set of missing timezone component tzids. + + To create a :rfc:`5545` compatible calendar, + all of these timezones should be added. + """ + tzids = self.get_used_tzids() + for timezone in self.timezones: + tzids.remove(timezone.tz_name) + return tzids + + @property + def timezones(self) -> list[Timezone]: + """Return the timezones components in this calendar. + + >>> from icalendar import Calendar + >>> calendar = Calendar.example("pacific_fiji") + >>> [timezone.tz_name for timezone in calendar.timezones] + ['custom_Pacific/Fiji'] + + .. note:: + + This is a read-only property. + """ + return self.walk("VTIMEZONE") + + def add_missing_timezones( + self, + first_date: date = Timezone._DEFAULT_FIRST_DATE, + last_date: date = Timezone._DEFAULT_LAST_DATE, + ): + """Add all missing VTIMEZONE components. + + This adds all the timezone components that are required. + + .. note:: + + Timezones that are not known will not be added. + + :param first_date: earlier than anything that happens in the calendar + :param last_date: later than anything happening in the calendar + + >>> from icalendar import Calendar, Event + >>> from datetime import datetime + >>> from zoneinfo import ZoneInfo + >>> calendar = Calendar() + >>> event = Event() + >>> calendar.add_component(event) + >>> event.start = datetime(1990, 10, 11, 12, tzinfo=ZoneInfo("Europe/Berlin")) + >>> calendar.timezones + [] + >>> calendar.add_missing_timezones() + >>> calendar.timezones[0].tz_name + 'Europe/Berlin' + >>> calendar.get_missing_tzids() # check that all are added + set() + """ + for tzid in self.get_missing_tzids(): + try: + timezone = Timezone.from_tzid( + tzid, first_date=first_date, last_date=last_date + ) + except ValueError: + continue + self.add_component(timezone) + + calendar_name = multi_language_text_property( + "NAME", "X-WR-CALNAME", + """This property specifies the name of the calendar. + + This implements :rfc:`7986` ``NAME`` and ``X-WR-CALNAME``. + + Property Parameters: + IANA, non-standard, alternate text + representation, and language property parameters can be specified + on this property. + + Conformance: + This property can be specified multiple times in an + iCalendar object. However, each property MUST represent the name + of the calendar in a different language. + + Description: + This property is used to specify a name of the + iCalendar object that can be used by calendar user agents when + presenting the calendar data to a user. Whilst a calendar only + has a single name, multiple language variants can be specified by + including this property multiple times with different "LANGUAGE" + parameter values on each. + + Example: + Below, we set the name of the calendar. + + .. code-block:: pycon + + >>> from icalendar import Calendar + >>> calendar = Calendar() + >>> calendar.calendar_name = "My Calendar" + >>> print(calendar.to_ical()) + BEGIN:VCALENDAR + NAME:My Calendar + END:VCALENDAR + """) + + description = multi_language_text_property( + "DESCRIPTION", "X-WR-CALDESC", + """This property specifies the description of the calendar. + + This implements :rfc:`7986` ``DESCRIPTION`` and ``X-WR-CALDESC``. + + Conformance: + This property can be specified multiple times in an + iCalendar object. However, each property MUST represent the + description of the calendar in a different language. + + Description: + This property is used to specify a lengthy textual + description of the iCalendar object that can be used by calendar + user agents when describing the nature of the calendar data to a + user. Whilst a calendar only has a single description, multiple + language variants can be specified by including this property + multiple times with different "LANGUAGE" parameter values on each. + + Example: + Below, we add a description to a calendar. + + .. code-block:: pycon + + >>> from icalendar import Calendar + >>> calendar = Calendar() + >>> calendar.description = "This is a calendar" + >>> print(calendar.to_ical()) + BEGIN:VCALENDAR + DESCRIPTION:This is a calendar + END:VCALENDAR + """) + + color = single_string_property( + "COLOR", + """This property specifies a color used for displaying the calendar. + + This implements :rfc:`7986` ``COLOR`` and ``X-APPLE-CALENDAR-COLOR``. + Please note that since :rfc:`7986`, subcomponents can have their own color. + + Property Parameters: + IANA and non-standard property parameters can + be specified on this property. + + Conformance: + This property can be specified once in an iCalendar + object or in ``VEVENT``, ``VTODO``, or ``VJOURNAL`` calendar components. + + Description: + This property specifies a color that clients MAY use + when presenting the relevant data to a user. Typically, this + would appear as the "background" color of events or tasks. The + value is a case-insensitive color name taken from the CSS3 set of + names, defined in Section 4.3 of `W3C.REC-css3-color-20110607 `_. + + Example: + ``"turquoise"``, ``"#ffffff"`` + + .. code-block:: pycon + + >>> from icalendar import Calendar + >>> calendar = Calendar() + >>> calendar.color = "black" + >>> print(calendar.to_ical()) + BEGIN:VCALENDAR + COLOR:black + END:VCALENDAR + + """, + "X-APPLE-CALENDAR-COLOR", + ) + categories = categories_property + uid = uid_property + +# These are read only singleton, so one instance is enough for the module +types_factory = TypesFactory() +component_factory = ComponentFactory() + +__all__ = [ + "INLINE", + "Alarm", + "Calendar", + "Component", + "ComponentFactory", + "Event", + "FreeBusy", + "IncompleteComponent", + "Journal", + "Timezone", + "TimezoneDaylight", + "TimezoneStandard", + "Todo", + "component_factory", + "get_example", +] diff --git a/.venv/lib/python3.9/site-packages/icalendar/caselessdict.py b/.venv/lib/python3.9/site-packages/icalendar/caselessdict.py new file mode 100644 index 0000000..16d5a54 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/caselessdict.py @@ -0,0 +1,108 @@ +from icalendar.parser_tools import to_unicode + +from collections import OrderedDict + + +def canonsort_keys(keys, canonical_order=None): + """Sorts leading keys according to canonical_order. Keys not specified in + canonical_order will appear alphabetically at the end. + """ + canonical_map = {k: i for i, k in enumerate(canonical_order or [])} + head = [k for k in keys if k in canonical_map] + tail = [k for k in keys if k not in canonical_map] + return sorted(head, key=lambda k: canonical_map[k]) + sorted(tail) + + +def canonsort_items(dict1, canonical_order=None): + """Returns a list of items from dict1, sorted by canonical_order.""" + return [(k, dict1[k]) for k in canonsort_keys(dict1.keys(), canonical_order)] + + +class CaselessDict(OrderedDict): + """A dictionary that isn't case sensitive, and only uses strings as keys. + Values retain their case. + """ + + def __init__(self, *args, **kwargs): + """Set keys to upper for initial dict.""" + super().__init__(*args, **kwargs) + for key, value in self.items(): + key_upper = to_unicode(key).upper() + if key != key_upper: + super().__delitem__(key) + self[key_upper] = value + + def __getitem__(self, key): + key = to_unicode(key) + return super().__getitem__(key.upper()) + + def __setitem__(self, key, value): + key = to_unicode(key) + super().__setitem__(key.upper(), value) + + def __delitem__(self, key): + key = to_unicode(key) + super().__delitem__(key.upper()) + + def __contains__(self, key): + key = to_unicode(key) + return super().__contains__(key.upper()) + + def get(self, key, default=None): + key = to_unicode(key) + return super().get(key.upper(), default) + + def setdefault(self, key, value=None): + key = to_unicode(key) + return super().setdefault(key.upper(), value) + + def pop(self, key, default=None): + key = to_unicode(key) + return super().pop(key.upper(), default) + + def popitem(self): + return super().popitem() + + def has_key(self, key): + key = to_unicode(key) + return super().__contains__(key.upper()) + + def update(self, *args, **kwargs): + # Multiple keys where key1.upper() == key2.upper() will be lost. + mappings = list(args) + [kwargs] + for mapping in mappings: + if hasattr(mapping, "items"): + mapping = iter(mapping.items()) + for key, value in mapping: + self[key] = value + + def copy(self): + return type(self)(super().copy()) + + def __repr__(self): + return f"{type(self).__name__}({dict(self)})" + + def __eq__(self, other): + return self is other or dict(self.items()) == dict(other.items()) + + def __ne__(self, other): + return not self == other + + # A list of keys that must appear first in sorted_keys and sorted_items; + # must be uppercase. + canonical_order = None + + def sorted_keys(self): + """Sorts keys according to the canonical_order for the derived class. + Keys not specified in canonical_order will appear at the end. + """ + return canonsort_keys(self.keys(), self.canonical_order) + + def sorted_items(self): + """Sorts items according to the canonical_order for the derived class. + Items not specified in canonical_order will appear at the end. + """ + return canonsort_items(self, self.canonical_order) + + +__all__ = ["canonsort_keys", "canonsort_items", "CaselessDict"] diff --git a/.venv/lib/python3.9/site-packages/icalendar/cli.py b/.venv/lib/python3.9/site-packages/icalendar/cli.py new file mode 100755 index 0000000..c832953 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/cli.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +"""utility program that allows user to preview calendar's events""" + +import sys +import pathlib +import argparse +from datetime import datetime + +from icalendar import Calendar, __version__ + + +def _format_name(address): + """Retrieve the e-mail and the name from an address. + + :arg an address object, e.g. mailto:test@test.test + + :returns str: The name and the e-mail address. + """ + email = address.split(":")[-1] + name = email.split("@")[0] + if not email: + return "" + return f"{name} <{email}>" + + +def _format_attendees(attendees): + """Format the list of attendees. + + :arg any attendees: Either a list, a string or a vCalAddress object. + + :returns str: Formatted list of attendees. + """ + if isinstance(attendees, str): + attendees = [attendees] + return "\n".join(map(lambda s: s.rjust(len(s) + 5), map(_format_name, attendees))) + + +def view(event): + """Make a human readable summary of an iCalendar file. + + :returns str: Human readable summary. + """ + summary = event.get("summary", default="") + organizer = _format_name(event.get("organizer", default="")) + attendees = _format_attendees(event.get("attendee", default=[])) + location = event.get("location", default="") + comment = event.get("comment", "") + description = event.get("description", "").split("\n") + description = "\n".join(map(lambda s: s.rjust(len(s) + 5), description)) + + start = event.decoded("dtstart") + if "duration" in event: + end = event.decoded("dtend", default=start + event.decoded("duration")) + else: + end = event.decoded("dtend", default=start) + duration = event.decoded("duration", default=end - start) + if isinstance(start, datetime): + start = start.astimezone() + start = start.strftime("%c") + if isinstance(end, datetime): + end = end.astimezone() + end = end.strftime("%c") + + return f""" Organizer: {organizer} + Attendees: +{attendees} + Summary : {summary} + Starts : {start} + End : {end} + Duration : {duration} + Location : {location} + Comment : {comment} + Description: +{description}""" + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("calendar_files", nargs="+", type=pathlib.Path) + parser.add_argument( + "--output", + "-o", + type=argparse.FileType("w"), + default=sys.stdout, + help="output file", + ) + parser.add_argument( + "-v", + "--version", + action="version", + version=f"{parser.prog} version {__version__}", + ) + argv = parser.parse_args() + + for calendar_file in argv.calendar_files: + with open(calendar_file, encoding="utf-8-sig") as f: + calendar = Calendar.from_ical(f.read()) + for event in calendar.walk("vevent"): + argv.output.write(view(event) + "\n\n") + + +__all__ = ["main", "view"] + +if __name__ == "__main__": + main() diff --git a/.venv/lib/python3.9/site-packages/icalendar/enums.py b/.venv/lib/python3.9/site-packages/icalendar/enums.py new file mode 100644 index 0000000..c4f0aa7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/enums.py @@ -0,0 +1,129 @@ +"""Enumerations for different types in the RFCs.""" +from enum import Enum as _Enum + + +class Enum(_Enum): + """Enum class that can be pickled.""" + + def __reduce_ex__(self, _p): + """For pickling.""" + return self.__class__, (self._name_,) + + +class StrEnum(str, Enum): + """Enum for strings.""" + + +class PARTSTAT(StrEnum): + """Enum for PARTSTAT from :rfc:`5545`. + + Attributes: + ``NEEDS_ACTION``, + ``ACCEPTED``, + ``DECLINED``, + ``TENTATIVE``, + ``DELEGATED``, + ``COMPLETED``, + ``IN_PROCESS`` + """ + NEEDS_ACTION = "NEEDS-ACTION" + ACCEPTED = "ACCEPTED" + DECLINED = "DECLINED" + TENTATIVE = "TENTATIVE" + DELEGATED = "DELEGATED" + COMPLETED = "COMPLETED" + IN_PROCESS = "IN-PROCESS" + + +class FBTYPE(StrEnum): + """Enum for FBTYPE from :rfc:`5545`. + + Attributes: + ``FREE``, + ``BUSY``, + ``BUSY-UNAVAILABLE``, + ``BUSY-TENTATIVE`` + """ + FREE = "FREE" + BUSY = "BUSY" + BUSY_UNAVAILABLE = "BUSY-UNAVAILABLE" + BUSY_TENTATIVE = "BUSY-TENTATIVE" + + +class CUTYPE(StrEnum): + """Enum for CTYPE from :rfc:`5545`. + + Attributes: + ``INDIVIDUAL``, + ``GROUP``, + ``RESOURCE``, + ``ROOM``, + ``UNKNOWN`` + """ + INDIVIDUAL = "INDIVIDUAL" + GROUP = "GROUP" + RESOURCE = "RESOURCE" + ROOM = "ROOM" + UNKNOWN = "UNKNOWN" + + +class RELTYPE(StrEnum): + """Enum for RELTYPE from :rfc:`5545`. + + Attributes: + ``PARENT``, + ``CHILD``, + ``SIBLING`` + """ + PARENT = "PARENT" + CHILD = "CHILD" + SIBLING = "SIBLING" + + +class RANGE(StrEnum): + """Enum for RANGE from :rfc:`5545`. + + Attributes: + ``THISANDFUTURE``, + ``THISANDPRIOR`` + """ + + THISANDFUTURE = "THISANDFUTURE" + THISANDPRIOR = "THISANDPRIOR" # deprecated + + +class RELATED(StrEnum): + """Enum for RELATED from :rfc:`5545`. + + Attributes: + ``START``, + ``END`` + """ + START = "START" + END = "END" + + +class ROLE(StrEnum): + """Enum for ROLE from :rfc:`5545`. + + Attributes: + ``CHAIR``, + ``REQ-PARTICIPANT``, + ``OPT-PARTICIPANT``, + ``NON-PARTICIPANT`` + """ + CHAIR = "CHAIR" + REQ_PARTICIPANT = "REQ-PARTICIPANT" + OPT_PARTICIPANT = "OPT-PARTICIPANT" + NON_PARTICIPANT = "NON-PARTICIPANT" + + +__all__ = [ + "PARTSTAT", + "FBTYPE", + "CUTYPE", + "RANGE", + "RELATED", + "ROLE", + "RELTYPE", +] diff --git a/.venv/lib/python3.9/site-packages/icalendar/error.py b/.venv/lib/python3.9/site-packages/icalendar/error.py new file mode 100644 index 0000000..04dac19 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/error.py @@ -0,0 +1,67 @@ +"""Errors thrown by icalendar.""" + + +class InvalidCalendar(ValueError): + """The calendar given is not valid. + + This calendar does not conform with RFC 5545 or breaks other RFCs. + """ + + +class IncompleteComponent(ValueError): + """The component is missing attributes. + + The attributes are not required, otherwise this would be + an InvalidCalendar. But in order to perform calculations, + this attribute is required. + + This error is not raised in the UPPERCASE properties like .DTSTART, + only in the lowercase computations like .start. + """ + + +class IncompleteAlarmInformation(ValueError): + """The alarms cannot be calculated yet because information is missing.""" + + +class LocalTimezoneMissing(IncompleteAlarmInformation): + """We are missing the local timezone to compute the value. + + Use Alarms.set_local_timezone(). + """ + + +class ComponentEndMissing(IncompleteAlarmInformation): + """We are missing the end of a component that the alarm is for. + + Use Alarms.set_end(). + """ + + +class ComponentStartMissing(IncompleteAlarmInformation): + """We are missing the start of a component that the alarm is for. + + Use Alarms.set_start(). + """ + +class FeatureWillBeRemovedInFutureVersion(DeprecationWarning): + """This feature will be removed in a future version.""" + + +class WillBeRemovedInVersion7(FeatureWillBeRemovedInFutureVersion): + """This feature will be removed in icalendar version 7. + + Suppress FeatureWillBeRemovedInFutureVersion instead. + """ + +__all__ = [ + "InvalidCalendar", + "IncompleteComponent", + "IncompleteAlarmInformation", + "LocalTimezoneMissing", + "ComponentEndMissing", + + "ComponentStartMissing", + "FeatureWillBeRemovedInFutureVersion", + "WillBeRemovedInVersion7", +] diff --git a/.venv/lib/python3.9/site-packages/icalendar/param.py b/.venv/lib/python3.9/site-packages/icalendar/param.py new file mode 100644 index 0000000..2a79f2e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/param.py @@ -0,0 +1,454 @@ +"""Parameter access for icalendar. + +Related: + +- :rfc:`5545`, Section 3.2. Property Parameters +- :rfc:`7986`, Section 6. Property Parameters +- https://github.com/collective/icalendar/issues/798 +""" +from __future__ import annotations + +import functools +from typing import TYPE_CHECKING, Callable, Optional, TypeVar, Union + +from icalendar import enums + +if TYPE_CHECKING: + from enum import Enum + + from icalendar.parser import Parameters + + +class IcalendarProperty: + """Interface provided by properties in icalendar.prop.""" + params: Parameters + + +def _default_return_none() -> Optional[str]: + """Return None by default.""" + return None + + +def _default_return_string() -> str: + """Return None by default.""" + return "" + +T = TypeVar("T") + +def string_parameter( + name:str, + doc:str, + default:Callable = _default_return_none, + convert:Optional[Callable[[str], T]] = None, + convert_to:Optional[Callable[[T], str]] = None + ) -> property: + """Return a parameter with a quoted value (case sensitive).""" + + if convert_to is None: + convert_to = convert + + @functools.wraps(default) + def fget(self: IcalendarProperty) -> Optional[str]: + value = self.params.get(name) + if value is None: + return default() + return convert(value) if convert else value + + def fset(self: IcalendarProperty, value: str): + self.params[name] = convert_to(value) if convert_to else value + + def fdel(self: IcalendarProperty): + self.params.pop(name, None) + + return property(fget, fset, fdel, doc=doc) + + +ALTREP = string_parameter( + "ALTREP", + """ALTREP - Specify an alternate text representation for the property value. + +Description: + This parameter specifies a URI that points to an + alternate representation for a textual property value. A property + specifying this parameter MUST also include a value that reflects + the default representation of the text value. The URI parameter + value MUST be specified in a quoted-string. + +.. note:: + + While there is no restriction imposed on the URI schemes + allowed for this parameter, Content Identifier (CID) :rfc:`2392`, + HTTP :rfc:`2616`, and HTTPS :rfc:`2818` are the URI schemes most + commonly used by current implementations. +""") + +CN = string_parameter( + "CN", + """Specify the common name to be associated with the calendar user specified. + +Description: + This parameter can be specified on properties with a + CAL-ADDRESS value type. The parameter specifies the common name + to be associated with the calendar user specified by the property. + The parameter value is text. The parameter value can be used for + display text to be associated with the calendar address specified + by the property. +""", + default=_default_return_string +) + +def _default_return_individual() -> enums.CUTYPE|str: + """Default value.""" + return enums.CUTYPE.INDIVIDUAL + +def _convert_enum(enum: type[Enum]) -> Callable[[str], Enum]: + + def convert(value: str) -> str: + """Convert if possible.""" + try: + return enum(value.upper()) + except ValueError: + return value + return convert + +CUTYPE = string_parameter( + "CUTYPE", + """Identify the type of calendar user specified by the property. + +Description: + This parameter can be specified on properties with a + CAL-ADDRESS value type. The parameter identifies the type of + calendar user specified by the property. If not specified on a + property that allows this parameter, the default is INDIVIDUAL. + Applications MUST treat x-name and iana-token values they don't + recognize the same way as they would the UNKNOWN value. +""", default=_default_return_individual, convert=_convert_enum(enums.CUTYPE)) + + +def quoted_list_parameter(name: str, doc: str) -> property: + """Return a parameter that contains a quoted list.""" + + def fget(self: IcalendarProperty) -> tuple[str]: + value = self.params.get(name) + if value is None: + return () + if isinstance(value, str): + return tuple(value.split(",")) + return value + + def fset(self: IcalendarProperty, value: str|tuple[str]): + if value == (): + fdel(self) + else: + self.params[name] = (value,) if isinstance(value, str) else value + + def fdel(self: IcalendarProperty): + self.params.pop(name, None) + + return property(fget, fset, fdel, doc=doc) + + +DELEGATED_FROM = quoted_list_parameter( + "DELEGATED-FROM", + """Specify the calendar users that have delegated their participation to the calendar user specified by the property. + +Description: + This parameter can be specified on properties with a + CAL-ADDRESS value type. This parameter specifies those calendar + users that have delegated their participation in a group-scheduled + event or to-do to the calendar user specified by the property. + The individual calendar address parameter values MUST each be + specified in a quoted-string. +""") + +DELEGATED_TO = quoted_list_parameter( + "DELEGATED-TO", + """Specify the calendar users to whom the calendar user specified by the property has delegated participation. + +Description: + This parameter can be specified on properties with a + CAL-ADDRESS value type. This parameter specifies those calendar + users whom have been delegated participation in a group-scheduled + event or to-do by the calendar user specified by the property. + The individual calendar address parameter values MUST each be + specified in a quoted-string. + """) + +DIR = string_parameter( + "DIR", + """Specify reference to a directory entry associated with the calendar user specified by the property. + +Description: + This parameter can be specified on properties with a + CAL-ADDRESS value type. The parameter specifies a reference to + the directory entry associated with the calendar user specified by + the property. The parameter value is a URI. The URI parameter + value MUST be specified in a quoted-string. + +.. note:: + + While there is no restriction imposed on the URI schemes + allowed for this parameter, CID :rfc:`2392`, DATA :rfc:`2397`, FILE + :rfc:`1738`, FTP :rfc:`1738`, HTTP :rfc:`2616`, HTTPS :rfc:`2818`, LDAP + :rfc:`4516`, and MID :rfc:`2392` are the URI schemes most commonly + used by current implementations. +""") + +def _default_return_busy() -> enums.FBTYPE|str: + """Default value.""" + return enums.FBTYPE.BUSY + +FBTYPE = string_parameter( + "FBTYPE", + """Specify the free or busy time type. + +Description: + This parameter specifies the free or busy time type. + The value FREE indicates that the time interval is free for + scheduling. The value BUSY indicates that the time interval is + busy because one or more events have been scheduled for that + interval. The value BUSY-UNAVAILABLE indicates that the time + interval is busy and that the interval can not be scheduled. The + value BUSY-TENTATIVE indicates that the time interval is busy + because one or more events have been tentatively scheduled for + that interval. If not specified on a property that allows this + parameter, the default is BUSY. Applications MUST treat x-name + and iana-token values they don't recognize the same way as they + would the BUSY value. +""", default=_default_return_busy, convert=_convert_enum(enums.FBTYPE)) + +LANGUAGE = string_parameter( + "LANGUAGE", + """Specify the language for text values in a property or property parameter. + +Description: + This parameter identifies the language of the text in + the property value and of all property parameter values of the + property. The value of the "LANGUAGE" property parameter is that + defined in :rfc:`5646`. + + For transport in a MIME entity, the Content-Language header field + can be used to set the default language for the entire body part. + Otherwise, no default language is assumed. +""") + +MEMBER = quoted_list_parameter( + "MEMBER", + """Specify the group or list membership of the calendar user specified by the property. + +Description: + This parameter can be specified on properties with a + CAL-ADDRESS value type. The parameter identifies the groups or + list membership for the calendar user specified by the property. + The parameter value is either a single calendar address in a + quoted-string or a COMMA-separated list of calendar addresses, + each in a quoted-string. The individual calendar address + parameter values MUST each be specified in a quoted-string. +""" +) + +def _default_return_needs_action() -> enums.PARTSTAT|str: + """Default value.""" + return enums.PARTSTAT.NEEDS_ACTION + +PARTSTAT = string_parameter( + "PARTSTAT", + """Specify the participation status for the calendar user specified by the property. + +Description: + This parameter can be specified on properties with a + CAL-ADDRESS value type. The parameter identifies the + participation status for the calendar user specified by the + property value. The parameter values differ depending on whether + they are associated with a group-scheduled "VEVENT", "VTODO", or + "VJOURNAL". The values MUST match one of the values allowed for + the given calendar component. If not specified on a property that + allows this parameter, the default value is NEEDS-ACTION. + Applications MUST treat x-name and iana-token values they don't + recognize the same way as they would the NEEDS-ACTION value. +""", default=_default_return_needs_action, convert=_convert_enum(enums.PARTSTAT)) + +def _default_range_none() -> Optional[enums.RANGE|str]: + return None + +RANGE = string_parameter( + "RANGE", + """Specify the effective range of recurrence instances from the instance specified by the recurrence identifier specified by the property. + +Description: + This parameter can be specified on a property that + specifies a recurrence identifier. The parameter specifies the + effective range of recurrence instances that is specified by the + property. The effective range is from the recurrence identifier + specified by the property. If this parameter is not specified on + an allowed property, then the default range is the single instance + specified by the recurrence identifier value of the property. The + parameter value can only be "THISANDFUTURE" to indicate a range + defined by the recurrence identifier and all subsequent instances. + The value "THISANDPRIOR" is deprecated by this revision of + iCalendar and MUST NOT be generated by applications. +""", default=_default_range_none, convert=_convert_enum(enums.RANGE)) + +def _default_related() -> enums.RELATED|str: + return enums.RELATED.START + +RELATED = string_parameter( + "RELATED", + """Specify the relationship of the alarm trigger with respect to the start or end of the calendar component. + +Description: + This parameter can be specified on properties that + specify an alarm trigger with a "DURATION" value type. The + parameter specifies whether the alarm will trigger relative to the + start or end of the calendar component. The parameter value START + will set the alarm to trigger off the start of the calendar + component; the parameter value END will set the alarm to trigger + off the end of the calendar component. If the parameter is not + specified on an allowable property, then the default is START. + +""", default=_default_related, convert=_convert_enum(enums.RANGE)) + + +def _default_req_participant() -> enums.ROLE|str: + return enums.ROLE.REQ_PARTICIPANT + +ROLE = string_parameter( + "ROLE", + """Specify the participation role for the calendar user specified by the property. + +Description: + This parameter can be specified on properties with a + CAL-ADDRESS value type. The parameter specifies the participation + role for the calendar user specified by the property in the group + schedule calendar component. If not specified on a property that + allows this parameter, the default value is REQ-PARTICIPANT. + Applications MUST treat x-name and iana-token values they don't + recognize the same way as they would the REQ-PARTICIPANT value. +""", default=_default_req_participant, convert=_convert_enum(enums.ROLE) +) + +def boolean_parameter(name: str, default:bool, doc: str) -> property: + + def _default() -> bool: + return default + + return string_parameter( + name, + doc, + default=_default, + convert=lambda x: x.upper() == "TRUE", + convert_to=lambda x: "TRUE" if x else "FALSE", + ) + +RSVP = boolean_parameter( + "RSVP", + False, + """Specify whether there is an expectation of a favor of anreply from the calendar user specified by the property value. + +Description: + This parameter can be specified on properties with a + CAL-ADDRESS value type. The parameter identifies the expectation + of a reply from the calendar user specified by the property value. + This parameter is used by the "Organizer" to request a + participation status reply from an "Attendee" of a group-scheduled + event or to-do. If not specified on a property that allows this + parameter, the default value is ``False``. +""" +) + +SENT_BY = string_parameter( + "SENT-BY", + """Specify the calendar user that is acting on behalf of the calendar user specified by the property. + +Description: + This parameter can be specified on properties with a + CAL-ADDRESS value type. The parameter specifies the calendar user + that is acting on behalf of the calendar user specified by the + property. The parameter value MUST be a mailto URI as defined in + :rfc:`2368`. The individual calendar address parameter values MUST + each be specified in a quoted-string. +""" +) + +TZID = string_parameter( + "TZID", + """Specify the identifier for the time zone definition for a time component in the property value. + +Description: + This parameter MUST be specified on the "DTSTART", + "DTEND", "DUE", "EXDATE", and "RDATE" properties when either a + DATE-TIME or TIME value type is specified and when the value is + neither a UTC or a "floating" time. Refer to the DATE-TIME or + TIME value type definition for a description of UTC and "floating + time" formats. This property parameter specifies a text value + that uniquely identifies the "VTIMEZONE" calendar component to be + used when evaluating the time portion of the property. The value + of the "TZID" property parameter will be equal to the value of the + "TZID" property for the matching time zone definition. An + individual "VTIMEZONE" calendar component MUST be specified for + each unique "TZID" parameter value specified in the iCalendar + object. + + The parameter MUST be specified on properties with a DATE-TIME + value if the DATE-TIME is not either a UTC or a "floating" time. + Failure to include and follow VTIMEZONE definitions in iCalendar + objects may lead to inconsistent understanding of the local time + at any given location. + + The presence of the SOLIDUS character as a prefix, indicates that + this "TZID" represents a unique ID in a globally defined time zone + registry (when such registry is defined). + +.. note:: + + This document does not define a naming convention for + time zone identifiers. Implementers may want to use the naming + conventions defined in existing time zone specifications such + as the public-domain TZ database (TZDB). The specification of + globally unique time zone identifiers is not addressed by this + document and is left for future study. +""" +) + +def _default_return_parent() -> enums.RELTYPE: + return enums.RELTYPE.PARENT + +RELTYPE = string_parameter( + "RELTYPE", + """Specify the type of hierarchical relationship associated with the calendar component specified by the property. + +Description: + This parameter can be specified on a property that + references another related calendar. The parameter specifies the + hierarchical relationship type of the calendar component + referenced by the property. The parameter value can be PARENT, to + indicate that the referenced calendar component is a superior of + calendar component; CHILD to indicate that the referenced calendar + component is a subordinate of the calendar component; or SIBLING + to indicate that the referenced calendar component is a peer of + the calendar component. If this parameter is not specified on an + allowable property, the default relationship type is PARENT. + Applications MUST treat x-name and iana-token values they don't + recognize the same way as they would the PARENT value. +""", default=_default_return_parent, convert=_convert_enum(enums.RELTYPE)) + +__all__ = [ + "string_parameter", + "quoted_list_parameter", + "ALTREP", + "CN", + "CUTYPE", + "DELEGATED_FROM", + "DELEGATED_TO", + "DIR", + "FBTYPE", + "LANGUAGE", + "MEMBER", + "PARTSTAT", + "RANGE", + "RELATED", + "ROLE", + "RSVP", + "SENT_BY", + "TZID", +] diff --git a/.venv/lib/python3.9/site-packages/icalendar/parser.py b/.venv/lib/python3.9/site-packages/icalendar/parser.py new file mode 100644 index 0000000..569b479 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/parser.py @@ -0,0 +1,482 @@ +"""This module parses and generates contentlines as defined in RFC 5545 +(iCalendar), but will probably work for other MIME types with similar syntax. +Eg. RFC 2426 (vCard) + +It is stupid in the sense that it treats the content purely as strings. No type +conversion is attempted. +""" +from __future__ import annotations + +import os +from icalendar.caselessdict import CaselessDict +from icalendar.parser_tools import DEFAULT_ENCODING, ICAL_TYPE +from icalendar.parser_tools import SEQUENCE_TYPES +from icalendar.parser_tools import to_unicode + +import re + + +def escape_char(text): + """Format value according to iCalendar TEXT escaping rules.""" + assert isinstance(text, (str, bytes)) + # NOTE: ORDER MATTERS! + return ( + text.replace(r"\N", "\n") + .replace("\\", "\\\\") + .replace(";", r"\;") + .replace(",", r"\,") + .replace("\r\n", r"\n") + .replace("\n", r"\n") + ) + + +def unescape_char(text): + assert isinstance(text, (str, bytes)) + # NOTE: ORDER MATTERS! + if isinstance(text, str): + return ( + text.replace("\\N", "\\n") + .replace("\r\n", "\n") + .replace("\\n", "\n") + .replace("\\,", ",") + .replace("\\;", ";") + .replace("\\\\", "\\") + ) + elif isinstance(text, bytes): + return ( + text.replace(b"\\N", b"\\n") + .replace(b"\r\n", b"\n") + .replace(b"\\n", b"\n") + .replace(b"\\,", b",") + .replace(b"\\;", b";") + .replace(b"\\\\", b"\\") + ) + + +def foldline(line, limit=75, fold_sep="\r\n "): + """Make a string folded as defined in RFC5545 + Lines of text SHOULD NOT be longer than 75 octets, excluding the line + break. Long content lines SHOULD be split into a multiple line + representations using a line "folding" technique. That is, a long + line can be split between any two characters by inserting a CRLF + immediately followed by a single linear white-space character (i.e., + SPACE or HTAB). + """ + assert isinstance(line, str) + assert "\n" not in line + + # Use a fast and simple variant for the common case that line is all ASCII. + try: + line.encode("ascii") + except (UnicodeEncodeError, UnicodeDecodeError): + pass + else: + return fold_sep.join( + line[i : i + limit - 1] for i in range(0, len(line), limit - 1) + ) + + ret_chars = [] + byte_count = 0 + for char in line: + char_byte_len = len(char.encode(DEFAULT_ENCODING)) + byte_count += char_byte_len + if byte_count >= limit: + ret_chars.append(fold_sep) + byte_count = char_byte_len + ret_chars.append(char) + + return "".join(ret_chars) + + +################################################################# +# Property parameter stuff + + +def param_value(value, always_quote=False): + """Returns a parameter value.""" + if isinstance(value, SEQUENCE_TYPES): + return q_join(map(rfc_6868_escape, value), always_quote=always_quote) + if isinstance(value, str): + return dquote(rfc_6868_escape(value), always_quote=always_quote) + return dquote(rfc_6868_escape(value.to_ical().decode(DEFAULT_ENCODING))) + + +# Could be improved + +# [\w-] because of the iCalendar RFC +# . because of the vCard RFC +NAME = re.compile(r"[\w.-]+") + +UNSAFE_CHAR = re.compile('[\x00-\x08\x0a-\x1f\x7f",:;]') +QUNSAFE_CHAR = re.compile('[\x00-\x08\x0a-\x1f\x7f"]') +FOLD = re.compile(b"(\r?\n)+[ \t]") +uFOLD = re.compile("(\r?\n)+[ \t]") +NEWLINE = re.compile(r"\r?\n") + + +def validate_token(name): + match = NAME.findall(name) + if len(match) == 1 and name == match[0]: + return + raise ValueError(name) + + +def validate_param_value(value, quoted=True): + validator = QUNSAFE_CHAR if quoted else UNSAFE_CHAR + if validator.findall(value): + raise ValueError(value) + + +# chars presence of which in parameter value will be cause the value +# to be enclosed in double-quotes +QUOTABLE = re.compile("[,;:’]") + + +def dquote(val, always_quote=False): + """Enclose parameter values containing [,;:] in double quotes.""" + # a double-quote character is forbidden to appear in a parameter value + # so replace it with a single-quote character + val = val.replace('"', "'") + if QUOTABLE.search(val) or always_quote: + return f'"{val}"' + return val + + +# parsing helper +def q_split(st, sep=",", maxsplit=-1): + """Splits a string on char, taking double (q)uotes into considderation.""" + if maxsplit == 0: + return [st] + + result = [] + cursor = 0 + length = len(st) + inquote = 0 + splits = 0 + for i, ch in enumerate(st): + if ch == '"': + inquote = not inquote + if not inquote and ch == sep: + result.append(st[cursor:i]) + cursor = i + 1 + splits += 1 + if i + 1 == length or splits == maxsplit: + result.append(st[cursor:]) + break + return result + + +def q_join(lst, sep=",", always_quote=False): + """Joins a list on sep, quoting strings with QUOTABLE chars.""" + return sep.join(dquote(itm, always_quote=always_quote) for itm in lst) + + +class Parameters(CaselessDict): + """Parser and generator of Property parameter strings. It knows nothing of + datatypes. Its main concern is textual structure. + """ + + # The following paremeters must always be enclosed in double quotes + always_quoted = ( + "ALTREP", + "DELEGATED-FROM", + "DELEGATED-TO", + "DIR", + "MEMBER", + "SENT-BY", + # Part of X-APPLE-STRUCTURED-LOCATION + "X-ADDRESS", + "X-TITLE", + ) + # this is quoted should one of the values be present + quote_also = { + # This is escaped in the RFC + "CN" : " '", + } + + def params(self): + """In RFC 5545 keys are called parameters, so this is to be consitent + with the naming conventions. + """ + return self.keys() + + # TODO? + # Later, when I get more time... need to finish this off now. The last major + # thing missing. + # def _encode(self, name, value, cond=1): + # # internal, for conditional convertion of values. + # if cond: + # klass = types_factory.for_property(name) + # return klass(value) + # return value + # + # def add(self, name, value, encode=0): + # "Add a parameter value and optionally encode it." + # if encode: + # value = self._encode(name, value, encode) + # self[name] = value + # + # def decoded(self, name): + # "returns a decoded value, or list of same" + + def to_ical(self, sorted=True): + result = [] + items = list(self.items()) + if sorted: + items.sort() + + for key, value in items: + upper_key = key.upper() + check_quoteable_characters = self.quote_also.get(key.upper()) + always_quote = ( + upper_key in self.always_quoted or ( + check_quoteable_characters and + any(c in value for c in check_quoteable_characters) + ) + ) + quoted_value = param_value(value, always_quote=always_quote) + if isinstance(quoted_value, str): + quoted_value = quoted_value.encode(DEFAULT_ENCODING) + # CaselessDict keys are always unicode + result.append(upper_key.encode(DEFAULT_ENCODING) + b"=" + quoted_value) + return b";".join(result) + + @classmethod + def from_ical(cls, st, strict=False): + """Parses the parameter format from ical text format.""" + + # parse into strings + result = cls() + for param in q_split(st, ";"): + try: + key, val = q_split(param, "=", maxsplit=1) + validate_token(key) + # Property parameter values that are not in quoted + # strings are case insensitive. + vals = [] + for v in q_split(val, ","): + if v.startswith('"') and v.endswith('"'): + v = v.strip('"') + validate_param_value(v, quoted=True) + vals.append(rfc_6868_unescape(v)) + else: + validate_param_value(v, quoted=False) + if strict: + vals.append(rfc_6868_unescape(v.upper())) + else: + vals.append(rfc_6868_unescape(v)) + if not vals: + result[key] = val + else: + if len(vals) == 1: + result[key] = vals[0] + else: + result[key] = vals + except ValueError as exc: + raise ValueError(f"{param!r} is not a valid parameter string: {exc}") + return result + + +def escape_string(val): + # f'{i:02X}' + return ( + val.replace(r"\,", "%2C") + .replace(r"\:", "%3A") + .replace(r"\;", "%3B") + .replace(r"\\", "%5C") + ) + + +def unescape_string(val): + return ( + val.replace("%2C", ",") + .replace("%3A", ":") + .replace("%3B", ";") + .replace("%5C", "\\") + ) + + +RFC_6868_UNESCAPE_REGEX = re.compile(r"\^\^|\^n|\^'") + + +def rfc_6868_unescape(param_value: str) -> str: + """Take care of :rfc:`6868` unescaping. + + - ^^ -> ^ + - ^n -> system specific newline + - ^' -> " + - ^ with others stay intact + """ + replacements = { + "^^": "^", + "^n": os.linesep, + "^'": '"', + } + return RFC_6868_UNESCAPE_REGEX.sub(lambda m: replacements.get(m.group(0), m.group(0)), param_value) + + +RFC_6868_ESCAPE_REGEX = re.compile(r'\^|\r\n|\r|\n|"') + +def rfc_6868_escape(param_value: str) -> str: + """Take care of :rfc:`6868` escaping. + + - ^ -> ^^ + - " -> ^' + - newline -> ^n + """ + replacements = { + "^": "^^", + "\n": "^n", + "\r": "^n", + "\r\n": "^n", + '"': "^'", + } + return RFC_6868_ESCAPE_REGEX.sub(lambda m: replacements.get(m.group(0), m.group(0)), param_value) + +def unescape_list_or_string(val): + if isinstance(val, list): + return [unescape_string(s) for s in val] + else: + return unescape_string(val) + + +######################################### +# parsing and generation of content lines + + +class Contentline(str): + """A content line is basically a string that can be folded and parsed into + parts. + """ + + def __new__(cls, value, strict=False, encoding=DEFAULT_ENCODING): + value = to_unicode(value, encoding=encoding) + assert "\n" not in value, ( + "Content line can not contain unescaped " "new line characters." + ) + self = super().__new__(cls, value) + self.strict = strict + return self + + @classmethod + def from_parts(cls, name:ICAL_TYPE, params: Parameters, values, sorted=True): + """Turn a parts into a content line.""" + assert isinstance(params, Parameters) + if hasattr(values, "to_ical"): + values = values.to_ical() + else: + from icalendar.prop import vText + values = vText(values).to_ical() + # elif isinstance(values, basestring): + # values = escape_char(values) + + # TODO: after unicode only, remove this + # Convert back to unicode, after to_ical encoded it. + name = to_unicode(name) + values = to_unicode(values) + if params: + params = to_unicode(params.to_ical(sorted=sorted)) + return cls(f"{name};{params}:{values}") + return cls(f"{name}:{values}") + + def parts(self): + """Split the content line up into (name, parameters, values) parts.""" + try: + st = escape_string(self) + name_split = None + value_split = None + in_quotes = False + for i, ch in enumerate(st): + if not in_quotes: + if ch in ":;" and not name_split: + name_split = i + if ch == ":" and not value_split: + value_split = i + if ch == '"': + in_quotes = not in_quotes + name = unescape_string(st[:name_split]) + if not name: + raise ValueError("Key name is required") + validate_token(name) + if not value_split: + value_split = i + 1 + if not name_split or name_split + 1 == value_split: + raise ValueError("Invalid content line") + params = Parameters.from_ical( + st[name_split + 1 : value_split], strict=self.strict + ) + params = Parameters( + (unescape_string(key), unescape_list_or_string(value)) + for key, value in iter(params.items()) + ) + values = unescape_string(st[value_split + 1 :]) + return (name, params, values) + except ValueError as exc: + raise ValueError( + f"Content line could not be parsed into parts: '{self}': {exc}" + ) from exc + + @classmethod + def from_ical(cls, ical, strict=False): + """Unfold the content lines in an iCalendar into long content lines.""" + ical = to_unicode(ical) + # a fold is carriage return followed by either a space or a tab + return cls(uFOLD.sub("", ical), strict=strict) + + def to_ical(self): + """Long content lines are folded so they are less than 75 characters + wide. + """ + return foldline(self).encode(DEFAULT_ENCODING) + + +class Contentlines(list): + """I assume that iCalendar files generally are a few kilobytes in size. + Then this should be efficient. for Huge files, an iterator should probably + be used instead. + """ + + def to_ical(self): + """Simply join self.""" + return b"\r\n".join(line.to_ical() for line in self if line) + b"\r\n" + + @classmethod + def from_ical(cls, st): + """Parses a string into content lines.""" + st = to_unicode(st) + try: + # a fold is carriage return followed by either a space or a tab + unfolded = uFOLD.sub("", st) + lines = cls(Contentline(line) for line in NEWLINE.split(unfolded) if line) + lines.append("") # '\r\n' at the end of every content line + return lines + except Exception: + raise ValueError("Expected StringType with content lines") + + +__all__ = [ + "Contentline", + "Contentlines", + "FOLD", + "NAME", + "NEWLINE", + "Parameters", + "QUNSAFE_CHAR", + "QUOTABLE", + "UNSAFE_CHAR", + "dquote", + "escape_char", + "escape_string", + "foldline", + "param_value", + "q_join", + "q_split", + "rfc_6868_escape", + "rfc_6868_unescape", + "uFOLD", + "unescape_char", + "unescape_list_or_string", + "unescape_string", + "validate_param_value", + "validate_token", +] diff --git a/.venv/lib/python3.9/site-packages/icalendar/parser_tools.py b/.venv/lib/python3.9/site-packages/icalendar/parser_tools.py new file mode 100644 index 0000000..702e027 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/parser_tools.py @@ -0,0 +1,63 @@ +from typing import List, Union + +SEQUENCE_TYPES = (list, tuple) +DEFAULT_ENCODING = "utf-8" +ICAL_TYPE = Union[str, bytes] + + +def from_unicode(value: ICAL_TYPE, encoding="utf-8") -> bytes: + """ + Converts a value to bytes, even if it already is bytes + :param value: The value to convert + :param encoding: The encoding to use in the conversion + :return: The bytes representation of the value + """ + if isinstance(value, bytes): + return value + elif isinstance(value, str): + try: + return value.encode(encoding) + except UnicodeEncodeError: + return value.encode("utf-8", "replace") + else: + return value + + +def to_unicode(value: ICAL_TYPE, encoding="utf-8-sig") -> str: + """Converts a value to unicode, even if it is already a unicode string.""" + if isinstance(value, str): + return value + elif isinstance(value, bytes): + try: + return value.decode(encoding) + except UnicodeDecodeError: + return value.decode("utf-8-sig", "replace") + else: + return value + + +def data_encode( + data: Union[ICAL_TYPE, dict, list], encoding=DEFAULT_ENCODING +) -> Union[bytes, List[bytes], dict]: + """Encode all datastructures to the given encoding. + Currently unicode strings, dicts and lists are supported. + """ + # https://stackoverflow.com/questions/1254454/fastest-way-to-convert-a-dicts-keys-values-from-unicode-to-str + if isinstance(data, str): + return data.encode(encoding) + elif isinstance(data, dict): + return dict(map(data_encode, iter(data.items()))) + elif isinstance(data, list) or isinstance(data, tuple): + return list(map(data_encode, data)) + else: + return data + + +__all__ = [ + "DEFAULT_ENCODING", + "SEQUENCE_TYPES", + "ICAL_TYPE", + "data_encode", + "from_unicode", + "to_unicode", +] diff --git a/.venv/lib/python3.9/site-packages/icalendar/prop.py b/.venv/lib/python3.9/site-packages/icalendar/prop.py new file mode 100644 index 0000000..2ed4aa4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/prop.py @@ -0,0 +1,2044 @@ +"""This module contains the parser/generators (or coders/encoders if you +prefer) for the classes/datatypes that are used in iCalendar: + +########################################################################### + +# This module defines these property value data types and property parameters + +4.2 Defined property parameters are: + +.. code-block:: text + + ALTREP, CN, CUTYPE, DELEGATED-FROM, DELEGATED-TO, DIR, ENCODING, FMTTYPE, + FBTYPE, LANGUAGE, MEMBER, PARTSTAT, RANGE, RELATED, RELTYPE, ROLE, RSVP, + SENT-BY, TZID, VALUE + +4.3 Defined value data types are: + +.. code-block:: text + + BINARY, BOOLEAN, CAL-ADDRESS, DATE, DATE-TIME, DURATION, FLOAT, INTEGER, + PERIOD, RECUR, TEXT, TIME, URI, UTC-OFFSET + +########################################################################### + +iCalendar properties have values. The values are strongly typed. This module +defines these types, calling val.to_ical() on them will render them as defined +in rfc5545. + +If you pass any of these classes a Python primitive, you will have an object +that can render itself as iCalendar formatted date. + +Property Value Data Types start with a 'v'. they all have an to_ical() and +from_ical() method. The to_ical() method generates a text string in the +iCalendar format. The from_ical() method can parse this format and return a +primitive Python datatype. So it should always be true that: + +.. code-block:: python + + x == vDataType.from_ical(VDataType(x).to_ical()) + +These types are mainly used for parsing and file generation. But you can set +them directly. +""" + +from __future__ import annotations + +import base64 +import binascii +import re +from datetime import date, datetime, time, timedelta +from typing import Union + +from icalendar.caselessdict import CaselessDict +from icalendar.enums import Enum +from icalendar.parser import Parameters, escape_char, unescape_char +from icalendar.parser_tools import ( + DEFAULT_ENCODING, + ICAL_TYPE, + SEQUENCE_TYPES, + from_unicode, + to_unicode, +) + +from .timezone import tzid_from_dt, tzid_from_tzinfo, tzp + +DURATION_REGEX = re.compile( + r"([-+]?)P(?:(\d+)W)?(?:(\d+)D)?" r"(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$" +) + +WEEKDAY_RULE = re.compile( + r"(?P[+-]?)(?P[\d]{0,2})" r"(?P[\w]{2})$" +) + + +class vBinary: + """Binary property values are base 64 encoded.""" + + params: Parameters + + def __init__(self, obj): + self.obj = to_unicode(obj) + self.params = Parameters(encoding="BASE64", value="BINARY") + + def __repr__(self): + return f"vBinary({self.to_ical()})" + + def to_ical(self): + return binascii.b2a_base64(self.obj.encode("utf-8"))[:-1] + + @staticmethod + def from_ical(ical): + try: + return base64.b64decode(ical) + except (ValueError, UnicodeError): + raise ValueError("Not valid base 64 encoding.") + + def __eq__(self, other): + """self == other""" + return isinstance(other, vBinary) and self.obj == other.obj + + +class vBoolean(int): + """Boolean + + Value Name: BOOLEAN + + Purpose: This value type is used to identify properties that contain + either a "TRUE" or "FALSE" Boolean value. + + Format Definition: This value type is defined by the following + notation: + + .. code-block:: text + + boolean = "TRUE" / "FALSE" + + Description: These values are case-insensitive text. No additional + content value encoding is defined for this value type. + + Example: The following is an example of a hypothetical property that + has a BOOLEAN value type: + + .. code-block:: python + + TRUE + + .. code-block:: pycon + + >>> from icalendar.prop import vBoolean + >>> boolean = vBoolean.from_ical('TRUE') + >>> boolean + True + >>> boolean = vBoolean.from_ical('FALSE') + >>> boolean + False + >>> boolean = vBoolean.from_ical('True') + >>> boolean + True + """ + + params: Parameters + + BOOL_MAP = CaselessDict({"true": True, "false": False}) + + def __new__(cls, *args, params={}, **kwargs): + self = super().__new__(cls, *args, **kwargs) + self.params = Parameters(params) + return self + + def to_ical(self): + return b"TRUE" if self else b"FALSE" + + @classmethod + def from_ical(cls, ical): + try: + return cls.BOOL_MAP[ical] + except Exception: + raise ValueError(f"Expected 'TRUE' or 'FALSE'. Got {ical}") + + +class vText(str): + """Simple text.""" + + params: Parameters + + def __new__(cls, value, encoding=DEFAULT_ENCODING, params={}): + value = to_unicode(value, encoding=encoding) + self = super().__new__(cls, value) + self.encoding = encoding + self.params = Parameters(params) + return self + + def __repr__(self) -> str: + return f"vText({self.to_ical()!r})" + + def to_ical(self) -> bytes: + return escape_char(self).encode(self.encoding) + + @classmethod + def from_ical(cls, ical: ICAL_TYPE): + ical_unesc = unescape_char(ical) + return cls(ical_unesc) + + from icalendar.param import ALTREP, LANGUAGE, RELTYPE + + +class vCalAddress(str): + """Calendar User Address + + Value Name: + CAL-ADDRESS + + Purpose: + This value type is used to identify properties that contain a + calendar user address. + + Description: + The value is a URI as defined by [RFC3986] or any other + IANA-registered form for a URI. When used to address an Internet + email transport address for a calendar user, the value MUST be a + mailto URI, as defined by [RFC2368]. + + Example: + ``mailto:`` is in front of the address. + + .. code-block:: text + + mailto:jane_doe@example.com + + Parsing: + + .. code-block:: pycon + + >>> from icalendar import vCalAddress + >>> cal_address = vCalAddress.from_ical('mailto:jane_doe@example.com') + >>> cal_address + vCalAddress('mailto:jane_doe@example.com') + + Encoding: + + .. code-block:: pycon + + >>> from icalendar import vCalAddress, Event + >>> event = Event() + >>> jane = vCalAddress("mailto:jane_doe@example.com") + >>> jane.name = "Jane" + >>> event["organizer"] = jane + >>> print(event.to_ical()) + BEGIN:VEVENT + ORGANIZER;CN=Jane:mailto:jane_doe@example.com + END:VEVENT + """ + + params: Parameters + + def __new__(cls, value, encoding=DEFAULT_ENCODING, params={}): + value = to_unicode(value, encoding=encoding) + self = super().__new__(cls, value) + self.params = Parameters(params) + return self + + def __repr__(self): + return f"vCalAddress('{self}')" + + def to_ical(self): + return self.encode(DEFAULT_ENCODING) + + @classmethod + def from_ical(cls, ical): + return cls(ical) + + @property + def email(self) -> str: + """The email address without mailto: at the start.""" + if self.lower().startswith("mailto:"): + return self[7:] + return str(self) + + from icalendar.param import ( + CN, + CUTYPE, + DELEGATED_FROM, + DELEGATED_TO, + DIR, + LANGUAGE, + PARTSTAT, + ROLE, + RSVP, + SENT_BY, + ) + + name = CN + + +class vFloat(float): + """Float + + Value Name: + FLOAT + + Purpose: + This value type is used to identify properties that contain + a real-number value. + + Format Definition: + This value type is defined by the following notation: + + .. code-block:: text + + float = (["+"] / "-") 1*DIGIT ["." 1*DIGIT] + + Description: + If the property permits, multiple "float" values are + specified by a COMMA-separated list of values. + + Example: + + .. code-block:: text + + 1000000.0000001 + 1.333 + -3.14 + + .. code-block:: pycon + + >>> from icalendar.prop import vFloat + >>> float = vFloat.from_ical('1000000.0000001') + >>> float + 1000000.0000001 + >>> float = vFloat.from_ical('1.333') + >>> float + 1.333 + >>> float = vFloat.from_ical('+1.333') + >>> float + 1.333 + >>> float = vFloat.from_ical('-3.14') + >>> float + -3.14 + """ + + params: Parameters + + def __new__(cls, *args, params={}, **kwargs): + self = super().__new__(cls, *args, **kwargs) + self.params = Parameters(params) + return self + + def to_ical(self): + return str(self).encode("utf-8") + + @classmethod + def from_ical(cls, ical): + try: + return cls(ical) + except Exception: + raise ValueError(f"Expected float value, got: {ical}") + + +class vInt(int): + """Integer + + Value Name: + INTEGER + + Purpose: + This value type is used to identify properties that contain a + signed integer value. + + Format Definition: + This value type is defined by the following notation: + + .. code-block:: text + + integer = (["+"] / "-") 1*DIGIT + + Description: + If the property permits, multiple "integer" values are + specified by a COMMA-separated list of values. The valid range + for "integer" is -2147483648 to 2147483647. If the sign is not + specified, then the value is assumed to be positive. + + Example: + + .. code-block:: text + + 1234567890 + -1234567890 + +1234567890 + 432109876 + + .. code-block:: pycon + + >>> from icalendar.prop import vInt + >>> integer = vInt.from_ical('1234567890') + >>> integer + 1234567890 + >>> integer = vInt.from_ical('-1234567890') + >>> integer + -1234567890 + >>> integer = vInt.from_ical('+1234567890') + >>> integer + 1234567890 + >>> integer = vInt.from_ical('432109876') + >>> integer + 432109876 + """ + + params: Parameters + + def __new__(cls, *args, params={}, **kwargs): + self = super().__new__(cls, *args, **kwargs) + self.params = Parameters(params) + return self + + def to_ical(self) -> bytes: + return str(self).encode("utf-8") + + @classmethod + def from_ical(cls, ical: ICAL_TYPE): + try: + return cls(ical) + except Exception: + raise ValueError(f"Expected int, got: {ical}") + + +class vDDDLists: + """A list of vDDDTypes values.""" + + params: Parameters + dts: list + + def __init__(self, dt_list): + if not hasattr(dt_list, "__iter__"): + dt_list = [dt_list] + vDDD = [] + tzid = None + for dt in dt_list: + dt = vDDDTypes(dt) + vDDD.append(dt) + if "TZID" in dt.params: + tzid = dt.params["TZID"] + + params = {} + if tzid: + # NOTE: no support for multiple timezones here! + params["TZID"] = tzid + self.params = Parameters(params) + self.dts = vDDD + + def to_ical(self): + dts_ical = (from_unicode(dt.to_ical()) for dt in self.dts) + return b",".join(dts_ical) + + @staticmethod + def from_ical(ical, timezone=None): + out = [] + ical_dates = ical.split(",") + for ical_dt in ical_dates: + out.append(vDDDTypes.from_ical(ical_dt, timezone=timezone)) + return out + + def __eq__(self, other): + if isinstance(other, vDDDLists): + return self.dts == other.dts + if isinstance(other, (TimeBase, date)): + return self.dts == [other] + return False + + def __repr__(self): + """String representation.""" + return f"{self.__class__.__name__}({self.dts})" + + +class vCategory: + params: Parameters + + def __init__(self, c_list: list[str] | str, params={}): + if not hasattr(c_list, "__iter__") or isinstance(c_list, str): + c_list = [c_list] + self.cats: list[vText | str] = [vText(c) for c in c_list] + self.params = Parameters(params) + + def __iter__(self): + return iter(vCategory.from_ical(self.to_ical())) + + def to_ical(self): + return b",".join( + [ + c.to_ical() if hasattr(c, "to_ical") else vText(c).to_ical() + for c in self.cats + ] + ) + + @staticmethod + def from_ical(ical): + ical = to_unicode(ical) + out = unescape_char(ical).split(",") + return out + + def __eq__(self, other): + """self == other""" + return isinstance(other, vCategory) and self.cats == other.cats + + +class TimeBase: + """Make classes with a datetime/date comparable.""" + + params: Parameters + ignore_for_equality = {"TZID", "VALUE"} + + def __eq__(self, other): + """self == other""" + if isinstance(other, date): + return self.dt == other + if isinstance(other, TimeBase): + default = object() + for key in ( + set(self.params) | set(other.params) + ) - self.ignore_for_equality: + if key[:2].lower() != "x-" and self.params.get( + key, default + ) != other.params.get(key, default): + return False + return self.dt == other.dt + if isinstance(other, vDDDLists): + return other == self + return False + + def __hash__(self): + return hash(self.dt) + + from icalendar.param import RANGE, RELATED, TZID + + def __repr__(self): + """String representation.""" + return f"{self.__class__.__name__}({self.dt}, {self.params})" + + +class vDDDTypes(TimeBase): + """A combined Datetime, Date or Duration parser/generator. Their format + cannot be confused, and often values can be of either types. + So this is practical. + """ + + params: Parameters + + def __init__(self, dt): + if not isinstance(dt, (datetime, date, timedelta, time, tuple)): + raise ValueError( + "You must use datetime, date, timedelta, " "time or tuple (for periods)" + ) + if isinstance(dt, (datetime, timedelta)): + self.params = Parameters() + elif isinstance(dt, date): + self.params = Parameters({"value": "DATE"}) + elif isinstance(dt, time): + self.params = Parameters({"value": "TIME"}) + else: # isinstance(dt, tuple) + self.params = Parameters({"value": "PERIOD"}) + + tzid = tzid_from_dt(dt) if isinstance(dt, (datetime, time)) else None + if tzid is not None and tzid != "UTC": + self.params.update({"TZID": tzid}) + + self.dt = dt + + def to_ical(self): + dt = self.dt + if isinstance(dt, datetime): + return vDatetime(dt).to_ical() + elif isinstance(dt, date): + return vDate(dt).to_ical() + elif isinstance(dt, timedelta): + return vDuration(dt).to_ical() + elif isinstance(dt, time): + return vTime(dt).to_ical() + elif isinstance(dt, tuple) and len(dt) == 2: + return vPeriod(dt).to_ical() + else: + raise ValueError(f"Unknown date type: {type(dt)}") + + @classmethod + def from_ical(cls, ical, timezone=None): + if isinstance(ical, cls): + return ical.dt + u = ical.upper() + if u.startswith(("P", "-P", "+P")): + return vDuration.from_ical(ical) + if "/" in u: + return vPeriod.from_ical(ical, timezone=timezone) + + if len(ical) in (15, 16): + return vDatetime.from_ical(ical, timezone=timezone) + elif len(ical) == 8: + return vDate.from_ical(ical) + elif len(ical) in (6, 7): + return vTime.from_ical(ical) + else: + raise ValueError(f"Expected datetime, date, or time, got: '{ical}'") + + +class vDate(TimeBase): + """Date + + Value Name: + DATE + + Purpose: + This value type is used to identify values that contain a + calendar date. + + Format Definition: + This value type is defined by the following notation: + + .. code-block:: text + + date = date-value + + date-value = date-fullyear date-month date-mday + date-fullyear = 4DIGIT + date-month = 2DIGIT ;01-12 + date-mday = 2DIGIT ;01-28, 01-29, 01-30, 01-31 + ;based on month/year + + Description: + If the property permits, multiple "date" values are + specified as a COMMA-separated list of values. The format for the + value type is based on the [ISO.8601.2004] complete + representation, basic format for a calendar date. The textual + format specifies a four-digit year, two-digit month, and two-digit + day of the month. There are no separator characters between the + year, month, and day component text. + + Example: + The following represents July 14, 1997: + + .. code-block:: text + + 19970714 + + .. code-block:: pycon + + >>> from icalendar.prop import vDate + >>> date = vDate.from_ical('19970714') + >>> date.year + 1997 + >>> date.month + 7 + >>> date.day + 14 + """ + + params: Parameters + + def __init__(self, dt): + if not isinstance(dt, date): + raise ValueError("Value MUST be a date instance") + self.dt = dt + self.params = Parameters({"value": "DATE"}) + + def to_ical(self): + s = f"{self.dt.year:04}{self.dt.month:02}{self.dt.day:02}" + return s.encode("utf-8") + + @staticmethod + def from_ical(ical): + try: + timetuple = ( + int(ical[:4]), # year + int(ical[4:6]), # month + int(ical[6:8]), # day + ) + return date(*timetuple) + except Exception: + raise ValueError(f"Wrong date format {ical}") + + +class vDatetime(TimeBase): + """Render and generates icalendar datetime format. + + vDatetime is timezone aware and uses a timezone library. + When a vDatetime object is created from an + ical string, you can pass a valid timezone identifier. When a + vDatetime object is created from a python datetime object, it uses the + tzinfo component, if present. Otherwise a timezone-naive object is + created. Be aware that there are certain limitations with timezone naive + DATE-TIME components in the icalendar standard. + """ + + params: Parameters + + def __init__(self, dt, params={}): + self.dt = dt + self.params = Parameters(params) + + def to_ical(self): + dt = self.dt + tzid = tzid_from_dt(dt) + + s = f"{dt.year:04}{dt.month:02}{dt.day:02}T{dt.hour:02}{dt.minute:02}{dt.second:02}" + if tzid == "UTC": + s += "Z" + elif tzid: + self.params.update({"TZID": tzid}) + return s.encode("utf-8") + + @staticmethod + def from_ical(ical, timezone=None): + """Create a datetime from the RFC string. + + Format: + + .. code-block:: text + + YYYYMMDDTHHMMSS + + .. code-block:: pycon + + >>> from icalendar import vDatetime + >>> vDatetime.from_ical("20210302T101500") + datetime.datetime(2021, 3, 2, 10, 15) + + >>> vDatetime.from_ical("20210302T101500", "America/New_York") + datetime.datetime(2021, 3, 2, 10, 15, tzinfo=ZoneInfo(key='America/New_York')) + + >>> from zoneinfo import ZoneInfo + >>> timezone = ZoneInfo("Europe/Berlin") + >>> vDatetime.from_ical("20210302T101500", timezone) + datetime.datetime(2021, 3, 2, 10, 15, tzinfo=ZoneInfo(key='Europe/Berlin')) + """ + tzinfo = None + if isinstance(timezone, str): + tzinfo = tzp.timezone(timezone) + elif timezone is not None: + tzinfo = timezone + + try: + timetuple = ( + int(ical[:4]), # year + int(ical[4:6]), # month + int(ical[6:8]), # day + int(ical[9:11]), # hour + int(ical[11:13]), # minute + int(ical[13:15]), # second + ) + if tzinfo: + return tzp.localize(datetime(*timetuple), tzinfo) + elif not ical[15:]: + return datetime(*timetuple) + elif ical[15:16] == "Z": + return tzp.localize_utc(datetime(*timetuple)) + else: + raise ValueError(ical) + except Exception as e: + raise ValueError(f"Wrong datetime format: {ical}") from e + + +class vDuration(TimeBase): + """Duration + + Value Name: + DURATION + + Purpose: + This value type is used to identify properties that contain + a duration of time. + + Format Definition: + This value type is defined by the following notation: + + .. code-block:: text + + dur-value = (["+"] / "-") "P" (dur-date / dur-time / dur-week) + + dur-date = dur-day [dur-time] + dur-time = "T" (dur-hour / dur-minute / dur-second) + dur-week = 1*DIGIT "W" + dur-hour = 1*DIGIT "H" [dur-minute] + dur-minute = 1*DIGIT "M" [dur-second] + dur-second = 1*DIGIT "S" + dur-day = 1*DIGIT "D" + + Description: + If the property permits, multiple "duration" values are + specified by a COMMA-separated list of values. The format is + based on the [ISO.8601.2004] complete representation basic format + with designators for the duration of time. The format can + represent nominal durations (weeks and days) and accurate + durations (hours, minutes, and seconds). Note that unlike + [ISO.8601.2004], this value type doesn't support the "Y" and "M" + designators to specify durations in terms of years and months. + The duration of a week or a day depends on its position in the + calendar. In the case of discontinuities in the time scale, such + as the change from standard time to daylight time and back, the + computation of the exact duration requires the subtraction or + addition of the change of duration of the discontinuity. Leap + seconds MUST NOT be considered when computing an exact duration. + When computing an exact duration, the greatest order time + components MUST be added first, that is, the number of days MUST + be added first, followed by the number of hours, number of + minutes, and number of seconds. + + Example: + A duration of 15 days, 5 hours, and 20 seconds would be: + + .. code-block:: text + + P15DT5H0M20S + + A duration of 7 weeks would be: + + .. code-block:: text + + P7W + + .. code-block:: pycon + + >>> from icalendar.prop import vDuration + >>> duration = vDuration.from_ical('P15DT5H0M20S') + >>> duration + datetime.timedelta(days=15, seconds=18020) + >>> duration = vDuration.from_ical('P7W') + >>> duration + datetime.timedelta(days=49) + """ + + params: Parameters + + def __init__(self, td, params={}): + if not isinstance(td, timedelta): + raise ValueError("Value MUST be a timedelta instance") + self.td = td + self.params = Parameters(params) + + def to_ical(self): + sign = "" + td = self.td + if td.days < 0: + sign = "-" + td = -td + timepart = "" + if td.seconds: + timepart = "T" + hours = td.seconds // 3600 + minutes = td.seconds % 3600 // 60 + seconds = td.seconds % 60 + if hours: + timepart += f"{hours}H" + if minutes or (hours and seconds): + timepart += f"{minutes}M" + if seconds: + timepart += f"{seconds}S" + if td.days == 0 and timepart: + return str(sign).encode("utf-8") + b"P" + str(timepart).encode("utf-8") + else: + return ( + str(sign).encode("utf-8") + + b"P" + + str(abs(td.days)).encode("utf-8") + + b"D" + + str(timepart).encode("utf-8") + ) + + @staticmethod + def from_ical(ical): + match = DURATION_REGEX.match(ical) + if not match: + raise ValueError(f"Invalid iCalendar duration: {ical}") + + sign, weeks, days, hours, minutes, seconds = match.groups() + value = timedelta( + weeks=int(weeks or 0), + days=int(days or 0), + hours=int(hours or 0), + minutes=int(minutes or 0), + seconds=int(seconds or 0), + ) + + if sign == "-": + value = -value + + return value + + @property + def dt(self) -> timedelta: + """The time delta for compatibility.""" + return self.td + + +class vPeriod(TimeBase): + """Period of Time + + Value Name: + PERIOD + + Purpose: + This value type is used to identify values that contain a + precise period of time. + + Format Definition: + This value type is defined by the following notation: + + .. code-block:: text + + period = period-explicit / period-start + + period-explicit = date-time "/" date-time + ; [ISO.8601.2004] complete representation basic format for a + ; period of time consisting of a start and end. The start MUST + ; be before the end. + + period-start = date-time "/" dur-value + ; [ISO.8601.2004] complete representation basic format for a + ; period of time consisting of a start and positive duration + ; of time. + + Description: + If the property permits, multiple "period" values are + specified by a COMMA-separated list of values. There are two + forms of a period of time. First, a period of time is identified + by its start and its end. This format is based on the + [ISO.8601.2004] complete representation, basic format for "DATE- + TIME" start of the period, followed by a SOLIDUS character + followed by the "DATE-TIME" of the end of the period. The start + of the period MUST be before the end of the period. Second, a + period of time can also be defined by a start and a positive + duration of time. The format is based on the [ISO.8601.2004] + complete representation, basic format for the "DATE-TIME" start of + the period, followed by a SOLIDUS character, followed by the + [ISO.8601.2004] basic format for "DURATION" of the period. + + Example: + The period starting at 18:00:00 UTC, on January 1, 1997 and + ending at 07:00:00 UTC on January 2, 1997 would be: + + .. code-block:: text + + 19970101T180000Z/19970102T070000Z + + The period start at 18:00:00 on January 1, 1997 and lasting 5 hours + and 30 minutes would be: + + .. code-block:: text + + 19970101T180000Z/PT5H30M + + .. code-block:: pycon + + >>> from icalendar.prop import vPeriod + >>> period = vPeriod.from_ical('19970101T180000Z/19970102T070000Z') + >>> period = vPeriod.from_ical('19970101T180000Z/PT5H30M') + """ + + params: Parameters + + def __init__(self, per: tuple[datetime, Union[datetime, timedelta]]): + start, end_or_duration = per + if not (isinstance(start, datetime) or isinstance(start, date)): + raise ValueError("Start value MUST be a datetime or date instance") + if not ( + isinstance(end_or_duration, datetime) + or isinstance(end_or_duration, date) + or isinstance(end_or_duration, timedelta) + ): + raise ValueError( + "end_or_duration MUST be a datetime, " "date or timedelta instance" + ) + by_duration = 0 + if isinstance(end_or_duration, timedelta): + by_duration = 1 + duration = end_or_duration + end = start + duration + else: + end = end_or_duration + duration = end - start + if start > end: + raise ValueError("Start time is greater than end time") + + self.params = Parameters({"value": "PERIOD"}) + # set the timezone identifier + # does not support different timezones for start and end + tzid = tzid_from_dt(start) + if tzid: + self.params["TZID"] = tzid + + self.start = start + self.end = end + self.by_duration = by_duration + self.duration = duration + + def overlaps(self, other): + if self.start > other.start: + return other.overlaps(self) + if self.start <= other.start < self.end: + return True + return False + + def to_ical(self): + if self.by_duration: + return ( + vDatetime(self.start).to_ical() + + b"/" + + vDuration(self.duration).to_ical() + ) + return vDatetime(self.start).to_ical() + b"/" + vDatetime(self.end).to_ical() + + @staticmethod + def from_ical(ical, timezone=None): + try: + start, end_or_duration = ical.split("/") + start = vDDDTypes.from_ical(start, timezone=timezone) + end_or_duration = vDDDTypes.from_ical(end_or_duration, timezone=timezone) + return (start, end_or_duration) + except Exception: + raise ValueError(f"Expected period format, got: {ical}") + + def __repr__(self): + if self.by_duration: + p = (self.start, self.duration) + else: + p = (self.start, self.end) + return f"vPeriod({p!r})" + + @property + def dt(self): + """Make this cooperate with the other vDDDTypes.""" + return (self.start, (self.duration if self.by_duration else self.end)) + + from icalendar.param import FBTYPE + + +class vWeekday(str): + """Either a ``weekday`` or a ``weekdaynum`` + + .. code-block:: pycon + + >>> from icalendar import vWeekday + >>> vWeekday("MO") # Simple weekday + 'MO' + >>> vWeekday("2FR").relative # Second friday + 2 + >>> vWeekday("2FR").weekday + 'FR' + >>> vWeekday("-1SU").relative # Last Sunday + -1 + + Definition from `RFC 5545, Section 3.3.10 `_: + + .. code-block:: text + + weekdaynum = [[plus / minus] ordwk] weekday + plus = "+" + minus = "-" + ordwk = 1*2DIGIT ;1 to 53 + weekday = "SU" / "MO" / "TU" / "WE" / "TH" / "FR" / "SA" + ;Corresponding to SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, + ;FRIDAY, and SATURDAY days of the week. + + """ + + params: Parameters + + week_days = CaselessDict( + { + "SU": 0, + "MO": 1, + "TU": 2, + "WE": 3, + "TH": 4, + "FR": 5, + "SA": 6, + } + ) + + def __new__(cls, value, encoding=DEFAULT_ENCODING, params={}): + value = to_unicode(value, encoding=encoding) + self = super().__new__(cls, value) + match = WEEKDAY_RULE.match(self) + if match is None: + raise ValueError(f"Expected weekday abbrevation, got: {self}") + match = match.groupdict() + sign = match["signal"] + weekday = match["weekday"] + relative = match["relative"] + if weekday not in vWeekday.week_days or sign not in "+-": + raise ValueError(f"Expected weekday abbrevation, got: {self}") + self.weekday = weekday or None + self.relative = relative and int(relative) or None + if sign == "-" and self.relative: + self.relative *= -1 + self.params = Parameters(params) + return self + + def to_ical(self): + return self.encode(DEFAULT_ENCODING).upper() + + @classmethod + def from_ical(cls, ical): + try: + return cls(ical.upper()) + except Exception: + raise ValueError(f"Expected weekday abbrevation, got: {ical}") + + +class vFrequency(str): + """A simple class that catches illegal values.""" + + params: Parameters + + frequencies = CaselessDict( + { + "SECONDLY": "SECONDLY", + "MINUTELY": "MINUTELY", + "HOURLY": "HOURLY", + "DAILY": "DAILY", + "WEEKLY": "WEEKLY", + "MONTHLY": "MONTHLY", + "YEARLY": "YEARLY", + } + ) + + def __new__(cls, value, encoding=DEFAULT_ENCODING, params={}): + value = to_unicode(value, encoding=encoding) + self = super().__new__(cls, value) + if self not in vFrequency.frequencies: + raise ValueError(f"Expected frequency, got: {self}") + self.params = Parameters(params) + return self + + def to_ical(self): + return self.encode(DEFAULT_ENCODING).upper() + + @classmethod + def from_ical(cls, ical): + try: + return cls(ical.upper()) + except Exception: + raise ValueError(f"Expected frequency, got: {ical}") + + +class vMonth(int): + """The number of the month for recurrence. + + In :rfc:`5545`, this is just an int. + In :rfc:`7529`, this can be followed by `L` to indicate a leap month. + + .. code-block:: pycon + + >>> from icalendar import vMonth + >>> vMonth(1) # first month January + vMonth('1') + >>> vMonth("5L") # leap month in Hebrew calendar + vMonth('5L') + >>> vMonth(1).leap + False + >>> vMonth("5L").leap + True + + Definition from RFC: + + .. code-block:: text + + type-bymonth = element bymonth { + xsd:positiveInteger | + xsd:string + } + """ + + params: Parameters + + def __new__(cls, month: Union[str, int], params={}): + if isinstance(month, vMonth): + return cls(month.to_ical().decode()) + if isinstance(month, str): + if month.isdigit(): + month_index = int(month) + leap = False + else: + if month[-1] != "L" and month[:-1].isdigit(): + raise ValueError(f"Invalid month: {month!r}") + month_index = int(month[:-1]) + leap = True + else: + leap = False + month_index = int(month) + self = super().__new__(cls, month_index) + self.leap = leap + self.params = Parameters(params) + return self + + def to_ical(self) -> bytes: + """The ical representation.""" + return str(self).encode("utf-8") + + @classmethod + def from_ical(cls, ical: str): + return cls(ical) + + def leap(): + doc = "Whether this is a leap month." + + def fget(self) -> bool: + return self._leap + + def fset(self, value: bool) -> None: + self._leap = value + + return locals() + + leap = property(**leap()) + + def __repr__(self) -> str: + """repr(self)""" + return f"{self.__class__.__name__}({str(self)!r})" + + def __str__(self) -> str: + """str(self)""" + return f"{int(self)}{'L' if self.leap else ''}" + + +class vSkip(vText, Enum): + """Skip values for RRULE. + + These are defined in :rfc:`7529`. + + OMIT is the default value. + + Examples: + + .. code-block:: pycon + + >>> from icalendar import vSkip + >>> vSkip.OMIT + vSkip('OMIT') + >>> vSkip.FORWARD + vSkip('FORWARD') + >>> vSkip.BACKWARD + vSkip('BACKWARD') + """ + + OMIT = "OMIT" + FORWARD = "FORWARD" + BACKWARD = "BACKWARD" + + __reduce_ex__ = Enum.__reduce_ex__ + + def __repr__(self): + return f"{self.__class__.__name__}({self._name_!r})" + + +class vRecur(CaselessDict): + """Recurrence definition. + + Property Name: + RRULE + + Purpose: + This property defines a rule or repeating pattern for recurring events, to-dos, + journal entries, or time zone definitions. + + Value Type: + RECUR + + Property Parameters: + IANA and non-standard property parameters can be specified on this property. + + Conformance: + This property can be specified in recurring "VEVENT", "VTODO", and "VJOURNAL" + calendar components as well as in the "STANDARD" and "DAYLIGHT" sub-components + of the "VTIMEZONE" calendar component, but it SHOULD NOT be specified more than once. + The recurrence set generated with multiple "RRULE" properties is undefined. + + Description: + The recurrence rule, if specified, is used in computing the recurrence set. + The recurrence set is the complete set of recurrence instances for a calendar component. + The recurrence set is generated by considering the initial "DTSTART" property along + with the "RRULE", "RDATE", and "EXDATE" properties contained within the + recurring component. The "DTSTART" property defines the first instance in the + recurrence set. The "DTSTART" property value SHOULD be synchronized with the + recurrence rule, if specified. The recurrence set generated with a "DTSTART" property + value not synchronized with the recurrence rule is undefined. + The final recurrence set is generated by gathering all of the start DATE-TIME + values generated by any of the specified "RRULE" and "RDATE" properties, and then + excluding any start DATE-TIME values specified by "EXDATE" properties. + This implies that start DATE- TIME values specified by "EXDATE" properties take + precedence over those specified by inclusion properties (i.e., "RDATE" and "RRULE"). + Where duplicate instances are generated by the "RRULE" and "RDATE" properties, + only one recurrence is considered. Duplicate instances are ignored. + + The "DTSTART" property specified within the iCalendar object defines the first + instance of the recurrence. In most cases, a "DTSTART" property of DATE-TIME value + type used with a recurrence rule, should be specified as a date with local time + and time zone reference to make sure all the recurrence instances start at the + same local time regardless of time zone changes. + + If the duration of the recurring component is specified with the "DTEND" or + "DUE" property, then the same exact duration will apply to all the members of the + generated recurrence set. Else, if the duration of the recurring component is + specified with the "DURATION" property, then the same nominal duration will apply + to all the members of the generated recurrence set and the exact duration of each + recurrence instance will depend on its specific start time. For example, recurrence + instances of a nominal duration of one day will have an exact duration of more or less + than 24 hours on a day where a time zone shift occurs. The duration of a specific + recurrence may be modified in an exception component or simply by using an + "RDATE" property of PERIOD value type. + + Examples: + The following RRULE specifies daily events for 10 occurrences. + + .. code-block:: text + + RRULE:FREQ=DAILY;COUNT=10 + + Below, we parse the RRULE ical string. + + .. code-block:: pycon + + >>> from icalendar.prop import vRecur + >>> rrule = vRecur.from_ical('FREQ=DAILY;COUNT=10') + >>> rrule + vRecur({'FREQ': ['DAILY'], 'COUNT': [10]}) + + You can choose to add an rrule to an :class:`icalendar.cal.Event` or + :class:`icalendar.cal.Todo`. + + .. code-block:: pycon + + >>> from icalendar import Event + >>> event = Event() + >>> event.add('RRULE', 'FREQ=DAILY;COUNT=10') + >>> event.rrules + [vRecur({'FREQ': ['DAILY'], 'COUNT': [10]})] + """ + + params: Parameters + + frequencies = [ + "SECONDLY", + "MINUTELY", + "HOURLY", + "DAILY", + "WEEKLY", + "MONTHLY", + "YEARLY", + ] + + # Mac iCal ignores RRULEs where FREQ is not the first rule part. + # Sorts parts according to the order listed in RFC 5545, section 3.3.10. + canonical_order = ( + "RSCALE", + "FREQ", + "UNTIL", + "COUNT", + "INTERVAL", + "BYSECOND", + "BYMINUTE", + "BYHOUR", + "BYDAY", + "BYWEEKDAY", + "BYMONTHDAY", + "BYYEARDAY", + "BYWEEKNO", + "BYMONTH", + "BYSETPOS", + "WKST", + "SKIP", + ) + + types = CaselessDict( + { + "COUNT": vInt, + "INTERVAL": vInt, + "BYSECOND": vInt, + "BYMINUTE": vInt, + "BYHOUR": vInt, + "BYWEEKNO": vInt, + "BYMONTHDAY": vInt, + "BYYEARDAY": vInt, + "BYMONTH": vMonth, + "UNTIL": vDDDTypes, + "BYSETPOS": vInt, + "WKST": vWeekday, + "BYDAY": vWeekday, + "FREQ": vFrequency, + "BYWEEKDAY": vWeekday, + "SKIP": vSkip, + } + ) + + def __init__(self, *args, params={}, **kwargs): + if args and isinstance(args[0], str): + # we have a string as an argument. + args = (self.from_ical(args[0]),) + args[1:] + for k, v in kwargs.items(): + if not isinstance(v, SEQUENCE_TYPES): + kwargs[k] = [v] + super().__init__(*args, **kwargs) + self.params = Parameters(params) + + def to_ical(self): + result = [] + for key, vals in self.sorted_items(): + typ = self.types.get(key, vText) + if not isinstance(vals, SEQUENCE_TYPES): + vals = [vals] + vals = b",".join(typ(val).to_ical() for val in vals) + + # CaselessDict keys are always unicode + key = key.encode(DEFAULT_ENCODING) + result.append(key + b"=" + vals) + + return b";".join(result) + + @classmethod + def parse_type(cls, key, values): + # integers + parser = cls.types.get(key, vText) + return [parser.from_ical(v) for v in values.split(",")] + + @classmethod + def from_ical(cls, ical: str): + if isinstance(ical, cls): + return ical + try: + recur = cls() + for pairs in ical.split(";"): + try: + key, vals = pairs.split("=") + except ValueError: + # E.g. incorrect trailing semicolon, like (issue #157): + # FREQ=YEARLY;BYMONTH=11;BYDAY=1SU; + continue + recur[key] = cls.parse_type(key, vals) + return cls(recur) + except ValueError: + raise + except: + raise ValueError(f"Error in recurrence rule: {ical}") + + +class vTime(TimeBase): + """Time + + Value Name: + TIME + + Purpose: + This value type is used to identify values that contain a + time of day. + + Format Definition: + This value type is defined by the following notation: + + .. code-block:: text + + time = time-hour time-minute time-second [time-utc] + + time-hour = 2DIGIT ;00-23 + time-minute = 2DIGIT ;00-59 + time-second = 2DIGIT ;00-60 + ;The "60" value is used to account for positive "leap" seconds. + + time-utc = "Z" + + Description: + If the property permits, multiple "time" values are + specified by a COMMA-separated list of values. No additional + content value encoding (i.e., BACKSLASH character encoding, see + vText) is defined for this value type. + + The "TIME" value type is used to identify values that contain a + time of day. The format is based on the [ISO.8601.2004] complete + representation, basic format for a time of day. The text format + consists of a two-digit, 24-hour of the day (i.e., values 00-23), + two-digit minute in the hour (i.e., values 00-59), and two-digit + seconds in the minute (i.e., values 00-60). The seconds value of + 60 MUST only be used to account for positive "leap" seconds. + Fractions of a second are not supported by this format. + + In parallel to the "DATE-TIME" definition above, the "TIME" value + type expresses time values in three forms: + + The form of time with UTC offset MUST NOT be used. For example, + the following is not valid for a time value: + + .. code-block:: text + + 230000-0800 ;Invalid time format + + **FORM #1 LOCAL TIME** + + The local time form is simply a time value that does not contain + the UTC designator nor does it reference a time zone. For + example, 11:00 PM: + + .. code-block:: text + + 230000 + + Time values of this type are said to be "floating" and are not + bound to any time zone in particular. They are used to represent + the same hour, minute, and second value regardless of which time + zone is currently being observed. For example, an event can be + defined that indicates that an individual will be busy from 11:00 + AM to 1:00 PM every day, no matter which time zone the person is + in. In these cases, a local time can be specified. The recipient + of an iCalendar object with a property value consisting of a local + time, without any relative time zone information, SHOULD interpret + the value as being fixed to whatever time zone the "ATTENDEE" is + in at any given moment. This means that two "Attendees", may + participate in the same event at different UTC times; floating + time SHOULD only be used where that is reasonable behavior. + + In most cases, a fixed time is desired. To properly communicate a + fixed time in a property value, either UTC time or local time with + time zone reference MUST be specified. + + The use of local time in a TIME value without the "TZID" property + parameter is to be interpreted as floating time, regardless of the + existence of "VTIMEZONE" calendar components in the iCalendar + object. + + **FORM #2: UTC TIME** + + UTC time, or absolute time, is identified by a LATIN CAPITAL + LETTER Z suffix character, the UTC designator, appended to the + time value. For example, the following represents 07:00 AM UTC: + + .. code-block:: text + + 070000Z + + The "TZID" property parameter MUST NOT be applied to TIME + properties whose time values are specified in UTC. + + **FORM #3: LOCAL TIME AND TIME ZONE REFERENCE** + + The local time with reference to time zone information form is + identified by the use the "TZID" property parameter to reference + the appropriate time zone definition. + + Example: + The following represents 8:30 AM in New York in winter, + five hours behind UTC, in each of the three formats: + + .. code-block:: text + + 083000 + 133000Z + TZID=America/New_York:083000 + """ + + def __init__(self, *args): + if len(args) == 1: + if not isinstance(args[0], (time, datetime)): + raise ValueError(f"Expected a datetime.time, got: {args[0]}") + self.dt = args[0] + else: + self.dt = time(*args) + self.params = Parameters({"value": "TIME"}) + + def to_ical(self): + return self.dt.strftime("%H%M%S") + + @staticmethod + def from_ical(ical): + # TODO: timezone support + try: + timetuple = (int(ical[:2]), int(ical[2:4]), int(ical[4:6])) + return time(*timetuple) + except Exception: + raise ValueError(f"Expected time, got: {ical}") + + +class vUri(str): + """URI + + Value Name: + URI + + Purpose: + This value type is used to identify values that contain a + uniform resource identifier (URI) type of reference to the + property value. + + Format Definition: + This value type is defined by the following notation: + + .. code-block:: text + + uri = scheme ":" hier-part [ "?" query ] [ "#" fragment ] + + Description: + This value type might be used to reference binary + information, for values that are large, or otherwise undesirable + to include directly in the iCalendar object. + + Property values with this value type MUST follow the generic URI + syntax defined in [RFC3986]. + + When a property parameter value is a URI value type, the URI MUST + be specified as a quoted-string value. + + Example: + The following is a URI for a network file: + + .. code-block:: text + + http://example.com/my-report.txt + + .. code-block:: pycon + + >>> from icalendar.prop import vUri + >>> uri = vUri.from_ical('http://example.com/my-report.txt') + >>> uri + 'http://example.com/my-report.txt' + """ + + params: Parameters + + def __new__(cls, value, encoding=DEFAULT_ENCODING, params={}): + value = to_unicode(value, encoding=encoding) + self = super().__new__(cls, value) + self.params = Parameters(params) + return self + + def to_ical(self): + return self.encode(DEFAULT_ENCODING) + + @classmethod + def from_ical(cls, ical): + try: + return cls(ical) + except Exception: + raise ValueError(f"Expected , got: {ical}") + + +class vGeo: + """Geographic Position + + Property Name: + GEO + + Purpose: + This property specifies information related to the global + position for the activity specified by a calendar component. + + Value Type: + FLOAT. The value MUST be two SEMICOLON-separated FLOAT values. + + Property Parameters: + IANA and non-standard property parameters can be specified on + this property. + + Conformance: + This property can be specified in "VEVENT" or "VTODO" + calendar components. + + Description: + This property value specifies latitude and longitude, + in that order (i.e., "LAT LON" ordering). The longitude + represents the location east or west of the prime meridian as a + positive or negative real number, respectively. The longitude and + latitude values MAY be specified up to six decimal places, which + will allow for accuracy to within one meter of geographical + position. Receiving applications MUST accept values of this + precision and MAY truncate values of greater precision. + + Example: + + .. code-block:: text + + GEO:37.386013;-122.082932 + + Parse vGeo: + + .. code-block:: pycon + + >>> from icalendar.prop import vGeo + >>> geo = vGeo.from_ical('37.386013;-122.082932') + >>> geo + (37.386013, -122.082932) + + Add a geo location to an event: + + .. code-block:: pycon + + >>> from icalendar import Event + >>> event = Event() + >>> latitude = 37.386013 + >>> longitude = -122.082932 + >>> event.add('GEO', (latitude, longitude)) + >>> event['GEO'] + vGeo((37.386013, -122.082932)) + """ + + params: Parameters + + def __init__(self, geo: tuple[float | str | int, float | str | int], params={}): + """Create a new vGeo from a tuple of (latitude, longitude). + + Raises: + ValueError: if geo is not a tuple of (latitude, longitude) + """ + try: + latitude, longitude = (geo[0], geo[1]) + latitude = float(latitude) + longitude = float(longitude) + except Exception as e: + raise ValueError( + "Input must be (float, float) for " "latitude and longitude" + ) from e + self.latitude = latitude + self.longitude = longitude + self.params = Parameters(params) + + def to_ical(self): + return f"{self.latitude};{self.longitude}" + + @staticmethod + def from_ical(ical): + try: + latitude, longitude = ical.split(";") + return (float(latitude), float(longitude)) + except Exception as e: + raise ValueError(f"Expected 'float;float' , got: {ical}") from e + + def __eq__(self, other): + return self.to_ical() == other.to_ical() + + def __repr__(self): + """repr(self)""" + return f"{self.__class__.__name__}(({self.latitude}, {self.longitude}))" + + +class vUTCOffset: + """UTC Offset + + Value Name: + UTC-OFFSET + + Purpose: + This value type is used to identify properties that contain + an offset from UTC to local time. + + Format Definition: + This value type is defined by the following notation: + + .. code-block:: text + + utc-offset = time-numzone + + time-numzone = ("+" / "-") time-hour time-minute [time-second] + + Description: + The PLUS SIGN character MUST be specified for positive + UTC offsets (i.e., ahead of UTC). The HYPHEN-MINUS character MUST + be specified for negative UTC offsets (i.e., behind of UTC). The + value of "-0000" and "-000000" are not allowed. The time-second, + if present, MUST NOT be 60; if absent, it defaults to zero. + + Example: + The following UTC offsets are given for standard time for + New York (five hours behind UTC) and Geneva (one hour ahead of + UTC): + + .. code-block:: text + + -0500 + + +0100 + + .. code-block:: pycon + + >>> from icalendar.prop import vUTCOffset + >>> utc_offset = vUTCOffset.from_ical('-0500') + >>> utc_offset + datetime.timedelta(days=-1, seconds=68400) + >>> utc_offset = vUTCOffset.from_ical('+0100') + >>> utc_offset + datetime.timedelta(seconds=3600) + """ + + params: Parameters + + ignore_exceptions = False # if True, and we cannot parse this + + # component, we will silently ignore + # it, rather than let the exception + # propagate upwards + + def __init__(self, td, params={}): + if not isinstance(td, timedelta): + raise ValueError("Offset value MUST be a timedelta instance") + self.td = td + self.params = Parameters(params) + + def to_ical(self): + if self.td < timedelta(0): + sign = "-%s" + td = timedelta(0) - self.td # get timedelta relative to 0 + else: + # Google Calendar rejects '0000' but accepts '+0000' + sign = "+%s" + td = self.td + + days, seconds = td.days, td.seconds + + hours = abs(days * 24 + seconds // 3600) + minutes = abs((seconds % 3600) // 60) + seconds = abs(seconds % 60) + if seconds: + duration = f"{hours:02}{minutes:02}{seconds:02}" + else: + duration = f"{hours:02}{minutes:02}" + return sign % duration + + @classmethod + def from_ical(cls, ical): + if isinstance(ical, cls): + return ical.td + try: + sign, hours, minutes, seconds = ( + ical[0:1], + int(ical[1:3]), + int(ical[3:5]), + int(ical[5:7] or 0), + ) + offset = timedelta(hours=hours, minutes=minutes, seconds=seconds) + except Exception: + raise ValueError(f"Expected utc offset, got: {ical}") + if not cls.ignore_exceptions and offset >= timedelta(hours=24): + raise ValueError(f"Offset must be less than 24 hours, was {ical}") + if sign == "-": + return -offset + return offset + + def __eq__(self, other): + if not isinstance(other, vUTCOffset): + return False + return self.td == other.td + + def __hash__(self): + return hash(self.td) + + def __repr__(self): + return f"vUTCOffset({self.td!r})" + + +class vInline(str): + """This is an especially dumb class that just holds raw unparsed text and + has parameters. Conversion of inline values are handled by the Component + class, so no further processing is needed. + """ + + params: Parameters + + def __new__(cls, value, encoding=DEFAULT_ENCODING, params={}): + value = to_unicode(value, encoding=encoding) + self = super().__new__(cls, value) + self.params = Parameters(params) + return self + + def to_ical(self): + return self.encode(DEFAULT_ENCODING) + + @classmethod + def from_ical(cls, ical): + return cls(ical) + + +class TypesFactory(CaselessDict): + """All Value types defined in RFC 5545 are registered in this factory + class. + + The value and parameter names don't overlap. So one factory is enough for + both kinds. + """ + + def __init__(self, *args, **kwargs): + "Set keys to upper for initial dict" + super().__init__(*args, **kwargs) + self.all_types = ( + vBinary, + vBoolean, + vCalAddress, + vDDDLists, + vDDDTypes, + vDate, + vDatetime, + vDuration, + vFloat, + vFrequency, + vGeo, + vInline, + vInt, + vPeriod, + vRecur, + vText, + vTime, + vUTCOffset, + vUri, + vWeekday, + vCategory, + ) + self["binary"] = vBinary + self["boolean"] = vBoolean + self["cal-address"] = vCalAddress + self["date"] = vDDDTypes + self["date-time"] = vDDDTypes + self["duration"] = vDDDTypes + self["float"] = vFloat + self["integer"] = vInt + self["period"] = vPeriod + self["recur"] = vRecur + self["text"] = vText + self["time"] = vTime + self["uri"] = vUri + self["utc-offset"] = vUTCOffset + self["geo"] = vGeo + self["inline"] = vInline + self["date-time-list"] = vDDDLists + self["categories"] = vCategory + + ################################################# + # Property types + + # These are the default types + types_map = CaselessDict( + { + #################################### + # Property value types + # Calendar Properties + "calscale": "text", + "method": "text", + "prodid": "text", + "version": "text", + # Descriptive Component Properties + "attach": "uri", + "categories": "categories", + "class": "text", + "comment": "text", + "description": "text", + "geo": "geo", + "location": "text", + "percent-complete": "integer", + "priority": "integer", + "resources": "text", + "status": "text", + "summary": "text", + # Date and Time Component Properties + "completed": "date-time", + "dtend": "date-time", + "due": "date-time", + "dtstart": "date-time", + "duration": "duration", + "freebusy": "period", + "transp": "text", + # Time Zone Component Properties + "tzid": "text", + "tzname": "text", + "tzoffsetfrom": "utc-offset", + "tzoffsetto": "utc-offset", + "tzurl": "uri", + # Relationship Component Properties + "attendee": "cal-address", + "contact": "text", + "organizer": "cal-address", + "recurrence-id": "date-time", + "related-to": "text", + "url": "uri", + "uid": "text", + # Recurrence Component Properties + "exdate": "date-time-list", + "exrule": "recur", + "rdate": "date-time-list", + "rrule": "recur", + # Alarm Component Properties + "action": "text", + "repeat": "integer", + "trigger": "duration", + "acknowledged": "date-time", + # Change Management Component Properties + "created": "date-time", + "dtstamp": "date-time", + "last-modified": "date-time", + "sequence": "integer", + # Miscellaneous Component Properties + "request-status": "text", + #################################### + # parameter types (luckily there is no name overlap) + "altrep": "uri", + "cn": "text", + "cutype": "text", + "delegated-from": "cal-address", + "delegated-to": "cal-address", + "dir": "uri", + "encoding": "text", + "fmttype": "text", + "fbtype": "text", + "language": "text", + "member": "cal-address", + "partstat": "text", + "range": "text", + "related": "text", + "reltype": "text", + "role": "text", + "rsvp": "boolean", + "sent-by": "cal-address", + "value": "text", + } + ) + + def for_property(self, name): + """Returns a the default type for a property or parameter""" + return self[self.types_map.get(name, "text")] + + def to_ical(self, name, value): + """Encodes a named value from a primitive python type to an icalendar + encoded string. + """ + type_class = self.for_property(name) + return type_class(value).to_ical() + + def from_ical(self, name, value): + """Decodes a named property or parameter value from an icalendar + encoded string to a primitive python type. + """ + type_class = self.for_property(name) + decoded = type_class.from_ical(value) + return decoded + + +__all__ = [ + "DURATION_REGEX", + "TimeBase", + "TypesFactory", + "WEEKDAY_RULE", + "tzid_from_dt", + "vBinary", + "vBoolean", + "vCalAddress", + "vCategory", + "vDDDLists", + "vDDDTypes", + "vDate", + "vDatetime", + "vDuration", + "vFloat", + "vFrequency", + "vGeo", + "vInline", + "vInt", + "vMonth", + "vPeriod", + "vRecur", + "vText", + "vTime", + "vUTCOffset", + "vUri", + "vWeekday", + "tzid_from_tzinfo", + "vSkip", +] diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/__init__.py b/.venv/lib/python3.9/site-packages/icalendar/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/alarms/rfc_5545_absolute_alarm_example.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/alarms/rfc_5545_absolute_alarm_example.ics new file mode 100644 index 0000000..9f7f71f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/alarms/rfc_5545_absolute_alarm_example.ics @@ -0,0 +1,7 @@ +BEGIN:VALARM +TRIGGER;VALUE=DATE-TIME:19970317T133000Z +REPEAT:4 +DURATION:PT15M +ACTION:AUDIO +ATTACH;FMTTYPE=audio/basic:ftp://example.com/pub/sounds/bell-01.aud +END:VALARM diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/alarms/rfc_5545_end.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/alarms/rfc_5545_end.ics new file mode 100644 index 0000000..a9f52dd --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/alarms/rfc_5545_end.ics @@ -0,0 +1,8 @@ +BEGIN:VALARM +TRIGGER;RELATED=END:-P2D +ACTION:EMAIL +ATTENDEE:mailto:john_doe@example.com +SUMMARY:*** REMINDER: SEND AGENDA FOR WEEKLY STAFF MEETING *** +DESCRIPTION:A draft agenda needs to be sent out to the attendees to the weekly managers meeting (MGR-LIST). Attached is a pointer the document template for the agenda file. +ATTACH;FMTTYPE=application/msword:http://example.com/templates/agenda.doc +END:VALARM diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/alarms/start_date.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/alarms/start_date.ics new file mode 100644 index 0000000..cc87c94 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/alarms/start_date.ics @@ -0,0 +1,8 @@ +BEGIN:VALARM +TRIGGER;RELATED=START:-P2D +ACTION:EMAIL +ATTENDEE:mailto:john_doe@example.com +SUMMARY:*** REMINDER: SEND AGENDA FOR WEEKLY STAFF MEETING *** +DESCRIPTION:A draft agenda needs to be sent out to the attendees to the weekly managers meeting (MGR-LIST). Attached is a pointer the document template for the agenda file. +ATTACH;FMTTYPE=application/msword:http://example.com/templates/agenda.doc +END:VALARM diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/attr/__init__.py b/.venv/lib/python3.9/site-packages/icalendar/tests/attr/__init__.py new file mode 100644 index 0000000..3beb504 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/attr/__init__.py @@ -0,0 +1 @@ +"""Test the getters/setters of components.""" \ No newline at end of file diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/attr/test_alarm.py b/.venv/lib/python3.9/site-packages/icalendar/tests/attr/test_alarm.py new file mode 100644 index 0000000..d897af4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/attr/test_alarm.py @@ -0,0 +1,54 @@ +"""Test the properties of the alarm.""" + +import pytest + +from icalendar.cal import Alarm +from icalendar.error import InvalidCalendar + + +def test_repeat_absent(): + """Test the absence of REPEAT.""" + assert Alarm().REPEAT == 0 + + +def test_repeat_number(): + """Test the absence of REPEAT.""" + assert Alarm({"REPEAT": 10}).REPEAT == 10 + + +def test_set_REPEAT(): + """Check setting the value.""" + a = Alarm() + a.REPEAT = 10 + assert a.REPEAT == 10 + + +def test_set_REPEAT_twice(): + """Check setting the value.""" + a = Alarm() + a.REPEAT = 10 + a.REPEAT = 20 + assert a.REPEAT == 20 + + +def test_add_REPEAT(): + """Check setting the value.""" + a = Alarm() + a.add("REPEAT", 10) + assert a.REPEAT == 10 + + +def test_invalid_repeat_value(): + """Check setting the value.""" + a = Alarm() + with pytest.raises(ValueError): + a.REPEAT = "asd" + a["REPEAT"] = "asd" + with pytest.raises(InvalidCalendar): + a.REPEAT # noqa: B018 + + +def test_alarm_to_string(): + a = Alarm() + a.REPEAT = 11 + assert a.to_ical() == b"BEGIN:VALARM\r\nREPEAT:11\r\nEND:VALARM\r\n" diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/attr/test_component.py b/.venv/lib/python3.9/site-packages/icalendar/tests/attr/test_component.py new file mode 100644 index 0000000..e8fd0ac --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/attr/test_component.py @@ -0,0 +1,92 @@ +"""Test common properties of components.""" + +from datetime import date, datetime, timedelta + +import pytest + +from icalendar import Event, FreeBusy, Journal, Todo, vDDDTypes +from icalendar.cal import Component +from icalendar.error import InvalidCalendar + + +@pytest.fixture(params=[Event, Todo, Journal, FreeBusy]) +def dtstamp_comp(request): + """a component to test""" + return request.param() + + +def test_no_dtstamp(dtstamp_comp): + """We have None as a value.""" + assert dtstamp_comp.DTSTAMP is None + + +def set_dtstamp_attribute(component: Component, value: date): + """Use the setter.""" + component.DTSTAMP = value + + +def set_dtstamp_item(component: Component, value: date): + """Use setitem.""" + component["DTSTAMP"] = vDDDTypes(value) + + +def set_dtstamp_add(component: Component, value: date): + """Use add.""" + component.add("DTSTAMP", value) + + +@pytest.mark.parametrize( + ("value", "timezone", "expected"), + [ + (datetime(2024, 10, 11, 23, 1), None, datetime(2024, 10, 11, 23, 1)), + (datetime(2024, 10, 11, 23, 1), "Europe/Berlin", datetime(2024, 10, 11, 21, 1)), + (datetime(2024, 10, 11, 22, 1), "UTC", datetime(2024, 10, 11, 22, 1)), + (date(2024, 10, 10), None, datetime(2024, 10, 10)), + ], +) +@pytest.mark.parametrize( + "set_dtstamp", [set_dtstamp_add, set_dtstamp_attribute, set_dtstamp_item] +) +def test_set_value_and_get_it( + dtstamp_comp, value, timezone, expected, tzp, set_dtstamp +): + """Set and get the DTSTAMP value.""" + dtstamp = value if timezone is None else tzp.localize(value, timezone) + set_dtstamp(dtstamp_comp, dtstamp) + in_utc = tzp.localize_utc(expected) + get_value = dtstamp_comp.get("DTSTAMP").dt + assert in_utc == get_value or set_dtstamp != set_dtstamp_attribute + assert in_utc == dtstamp_comp.DTSTAMP + + +@pytest.mark.parametrize("invalid_value", [None, timedelta()]) +def test_set_invalid_value(invalid_value, dtstamp_comp): + """Check handling of invalid values.""" + with pytest.raises(TypeError) as e: + dtstamp_comp.DTSTAMP = invalid_value + assert e.value.args[0] == f"DTSTAMP takes a datetime in UTC, not {invalid_value}" + + +@pytest.mark.parametrize("invalid_value", [None, vDDDTypes(timedelta())]) +def test_get_invalid_value(invalid_value, dtstamp_comp): + """Check handling of invalid values.""" + dtstamp_comp["DTSTAMP"] = invalid_value + with pytest.raises(InvalidCalendar) as e: + dtstamp_comp.DTSTAMP # noqa: B018 + assert ( + e.value.args[0] + == f"DTSTAMP must be a datetime in UTC, not {getattr(invalid_value, 'dt', invalid_value)}" + ) + + +def test_set_twice(dtstamp_comp, tzp): + """Set the value twice.""" + dtstamp_comp.DTSTAMP = date(2014, 1, 1) + dtstamp_comp.DTSTAMP = date(2014, 1, 2) + assert tzp.localize_utc(datetime(2014, 1, 2)) == dtstamp_comp.DTSTAMP + + +def test_last_modified(dtstamp_comp, tzp): + """Check we can set LAST_MODIFIED in the same way as DTSTAMP""" + dtstamp_comp.LAST_MODIFIED = date(2014, 1, 2) + assert tzp.localize_utc(datetime(2014, 1, 2)) == dtstamp_comp.LAST_MODIFIED diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/attr/test_exdates.py b/.venv/lib/python3.9/site-packages/icalendar/tests/attr/test_exdates.py new file mode 100644 index 0000000..3b32aff --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/attr/test_exdates.py @@ -0,0 +1,62 @@ +"""This tests the exdate property. +""" +from __future__ import annotations + +from datetime import date, datetime +from pprint import pprint +from typing import Union + +import pytest + +from icalendar import ( + Calendar, + Event, + Journal, + TimezoneDaylight, + TimezoneStandard, + Todo, +) + +C_EXDATE = Union[Event, Todo, Journal, TimezoneDaylight, TimezoneStandard] + +@pytest.fixture(params = [Event, Todo, Journal, TimezoneDaylight, TimezoneStandard]) +def c_exdate(request) -> C_EXDATE: + """Return a component that uses exdate.""" + return request.param() + + + +@pytest.fixture( + params=[ + lambda _tzp: date(2019, 10, 11), + lambda _tzp: datetime(2000, 1, 13, 12, 1), + lambda tzp: tzp.localize_utc(datetime(2031, 12, 1, 23, 59)), + lambda tzp: tzp.localize(datetime(1984, 1, 13, 13, 1), "Europe/Athens"), + ] +) +def exdate(request, tzp): + """Possible values for an exdate.""" + return request.param(tzp) + +def test_no_exdates_by_default(c_exdate): + """We expect no exdate by default.""" + assert c_exdate.exdates == [] + +def test_set_and_retrieve_exdate(exdate, c_exdate): + """Set the attribute and get the value.""" + c_exdate.add("exdate", [exdate]) + result = [exdate] + assert c_exdate.exdates == result + +def test_set_and_retrieve_exdates_in_list(exdate, c_exdate): + """Set the attribute and get the value.""" + c_exdate.add("exdate", [exdate, exdate]) + result = [exdate, exdate] + assert c_exdate.exdates == result + +def test_set_and_retrieve_exdates_twice(exdate, c_exdate): + """Set the attribute and get the value.""" + c_exdate.add("exdate", [exdate]) + c_exdate.add("exdate", [exdate]) + result = [exdate, exdate] + assert c_exdate.exdates == result diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/attr/test_rdate.py b/.venv/lib/python3.9/site-packages/icalendar/tests/attr/test_rdate.py new file mode 100644 index 0000000..0f5b7ae --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/attr/test_rdate.py @@ -0,0 +1,111 @@ +"""This tests the RDATE property. +""" +from __future__ import annotations + +from datetime import date, datetime +from pprint import pprint +from typing import Union + +import pytest + +from icalendar import ( + Calendar, + Event, + Journal, + TimezoneDaylight, + TimezoneStandard, + Todo, +) + +C_RDATE = Union[Event, Todo, Journal, TimezoneDaylight, TimezoneStandard] + +@pytest.fixture(params = [Event, Todo, Journal, TimezoneDaylight, TimezoneStandard]) +def c_rdate(request) -> C_RDATE: + """Return a component that uses RDATE.""" + return request.param() + + +@pytest.fixture( + params=[ + lambda _tzp: date(2019, 10, 11), + lambda _tzp: datetime(2000, 1, 13, 12, 1), + lambda tzp: tzp.localize_utc(datetime(2031, 12, 1, 23, 59)), + lambda tzp: tzp.localize(datetime(1984, 1, 13, 13, 1), "Europe/Athens"), + lambda _tzp: ((datetime(2000, 1, 13, 12, 1), datetime(2000, 1, 13, 12, 2))), + lambda tzp: ((tzp.localize_utc(datetime(2001, 1, 13, 12, 1)), tzp.localize_utc(datetime(2001, 1, 13, 12, 2)))), + ] +) +def rdate(request, tzp): + """Possible values for an rdate.""" + return request.param(tzp) + +def test_no_rdates_by_default(c_rdate): + """We expect no rdate by default.""" + assert c_rdate.rdates == [] + + +def test_set_and_retrieve_rdate(rdate, c_rdate): + """Set the attribute and get the value.""" + c_rdate.add("RDATE", [rdate]) + result = [rdate if isinstance(rdate, tuple) else (rdate, None)] + assert c_rdate.rdates == result + + +def test_get_example_0(calendars): + """Test an example rdate.""" + cal : Calendar = calendars.rfc_5545_RDATE_example + event = cal.events[0] + assert event.rdates == [(datetime(1997, 7, 14, 12, 30), None)] + + +def test_get_example_1(calendars, tzp): + """Test an example rdate.""" + cal : Calendar = calendars.rfc_5545_RDATE_example + event = cal.events[1] + assert event.rdates == [(tzp.localize_utc(datetime(1997, 7, 14, 12, 30)), None)] + +def test_get_example_2(calendars, tzp): + """Test an example rdate.""" + cal : Calendar = calendars.rfc_5545_RDATE_example + event = cal.events[2] + assert event.rdates == [(tzp.localize(datetime(1997, 7, 14, 8, 30), "America/New_York"),None)] + +def test_get_example_3(calendars, tzp): + """Test an example rdate.""" + cal : Calendar = calendars.rfc_5545_RDATE_example + event = cal.events[3] + rdates_3 = [ + (tzp.localize_utc(datetime(1996, 4, 3, 2)), tzp.localize_utc(datetime(1996, 4, 3, 4))), + (tzp.localize_utc(datetime(1996, 4, 4, 1)), tzp.localize_utc(datetime(1996, 4, 4, 4))), + ] + pprint(event.rdates) + pprint(rdates_3) + assert event.rdates == rdates_3 + +def d(i:int) -> tuple[date, None]: + s = str(i) + return (date(int(s[:4]), int(s[4:6].lstrip("0")), int(s[6:].lstrip("0"))), None) + + +RDATES_4 = list(map(d, ( + 19970101, + 19970120, + 19970217, + 19970421, + 19970526, + 19970704, + 19970901, + 19971014, + 19971128, + 19971129, + 19971225, + ))) + + +def test_get_example_4(calendars, tzp): + """Test an example rdate.""" + cal : Calendar = calendars.rfc_5545_RDATE_example + event = cal.events[4] + pprint(event.rdates) + pprint(RDATES_4) + assert event.rdates == RDATES_4 diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/attr/test_rrule.py b/.venv/lib/python3.9/site-packages/icalendar/tests/attr/test_rrule.py new file mode 100644 index 0000000..641b5c0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/attr/test_rrule.py @@ -0,0 +1,45 @@ +"""Test getting the rrules from a component.""" + +import pytest + +from icalendar import ( + Component, + Event, + Journal, + TimezoneDaylight, + TimezoneStandard, + Todo, + vRecur, +) + +RRULE_0 = vRecur.from_ical("FREQ=DAILY;COUNT=10") +RRULE_1 = vRecur.from_ical("FREQ=DAILY;UNTIL=19971224T000000Z") +RRULE_2 = vRecur.from_ical("FREQ=DAILY;INTERVAL=2") +RRULE_3 = vRecur.from_ical("FREQ=DAILY;INTERVAL=10;COUNT=5") +RRULE_4 = vRecur.from_ical("FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA") + +@pytest.fixture(params=[RRULE_0, RRULE_1, RRULE_2, RRULE_3, RRULE_4]) +def rrule(request) -> str: + """An rrule.""" + return request.param + +@pytest.fixture(params = [Event, Todo, Journal, TimezoneDaylight, TimezoneStandard]) +def c_rrule(request) -> Component: + """Return a component that uses RDATE.""" + return request.param() + +def test_no_rrules_by_default(c_rrule): + """We expect no rdate by default.""" + assert c_rrule.rrules == [] + + +def test_one_rrule(c_rrule, rrule): + """Add one rrule.""" + c_rrule.add("rrule", rrule) + assert c_rrule.rrules == [rrule] + +def test_two_rrules(c_rrule, rrule): + """Add two rrules.""" + c_rrule.add("rrule", rrule) + c_rrule.add("rrule", RRULE_3) + assert c_rrule.rrules == [rrule, RRULE_3] diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_etar_future.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_etar_future.ics new file mode 100644 index 0000000..d2735f5 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_etar_future.ics @@ -0,0 +1,235 @@ +BEGIN:VCALENDAR +PRODID:-//Offline Calendar//iCal Import/Export 2.8.1//EN +VERSION:2.0 +METHOD:PUBLISH +CALSCALE:GREGORIAN +BEGIN:VTIMEZONE +TZID:Europe/London +TZURL:http://tzurl.org/zoneinfo/Europe/London +X-LIC-LOCATION:Europe/London +BEGIN:DAYLIGHT +TZOFFSETFROM:+0000 +TZOFFSETTO:+0100 +TZNAME:BST +DTSTART:19810329T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0100 +TZOFFSETTO:+0000 +TZNAME:GMT +DTSTART:19961027T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +BEGIN:STANDARD +TZOFFSETFROM:-000115 +TZOFFSETTO:+0000 +TZNAME:GMT +DTSTART:18471201T000115 +RDATE:18471201T000115 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETFROM:+0000 +TZOFFSETTO:+0100 +TZNAME:BST +DTSTART:19160521T020000 +RDATE:19160521T020000 +RDATE:19170408T020000 +RDATE:19180324T020000 +RDATE:19190330T020000 +RDATE:19200328T020000 +RDATE:19210403T020000 +RDATE:19220326T020000 +RDATE:19230422T020000 +RDATE:19240413T020000 +RDATE:19250419T020000 +RDATE:19260418T020000 +RDATE:19270410T020000 +RDATE:19280422T020000 +RDATE:19290421T020000 +RDATE:19300413T020000 +RDATE:19310419T020000 +RDATE:19320417T020000 +RDATE:19330409T020000 +RDATE:19340422T020000 +RDATE:19350414T020000 +RDATE:19360419T020000 +RDATE:19370418T020000 +RDATE:19380410T020000 +RDATE:19390416T020000 +RDATE:19400225T020000 +RDATE:19460414T020000 +RDATE:19470316T020000 +RDATE:19480314T020000 +RDATE:19490403T020000 +RDATE:19500416T020000 +RDATE:19510415T020000 +RDATE:19520420T020000 +RDATE:19530419T020000 +RDATE:19540411T020000 +RDATE:19550417T020000 +RDATE:19560422T020000 +RDATE:19570414T020000 +RDATE:19580420T020000 +RDATE:19590419T020000 +RDATE:19600410T020000 +RDATE:19610326T020000 +RDATE:19620325T020000 +RDATE:19630331T020000 +RDATE:19640322T020000 +RDATE:19650321T020000 +RDATE:19660320T020000 +RDATE:19670319T020000 +RDATE:19680218T020000 +RDATE:19720319T020000 +RDATE:19730318T020000 +RDATE:19740317T020000 +RDATE:19750316T020000 +RDATE:19760321T020000 +RDATE:19770320T020000 +RDATE:19780319T020000 +RDATE:19790318T020000 +RDATE:19800316T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0100 +TZOFFSETTO:+0000 +TZNAME:GMT +DTSTART:19161001T030000 +RDATE:19161001T030000 +RDATE:19170917T030000 +RDATE:19180930T030000 +RDATE:19190929T030000 +RDATE:19201025T030000 +RDATE:19211003T030000 +RDATE:19221008T030000 +RDATE:19230916T030000 +RDATE:19240921T030000 +RDATE:19251004T030000 +RDATE:19261003T030000 +RDATE:19271002T030000 +RDATE:19281007T030000 +RDATE:19291006T030000 +RDATE:19301005T030000 +RDATE:19311004T030000 +RDATE:19321002T030000 +RDATE:19331008T030000 +RDATE:19341007T030000 +RDATE:19351006T030000 +RDATE:19361004T030000 +RDATE:19371003T030000 +RDATE:19381002T030000 +RDATE:19391119T030000 +RDATE:19451007T030000 +RDATE:19461006T030000 +RDATE:19471102T030000 +RDATE:19481031T030000 +RDATE:19491030T030000 +RDATE:19501022T030000 +RDATE:19511021T030000 +RDATE:19521026T030000 +RDATE:19531004T030000 +RDATE:19541003T030000 +RDATE:19551002T030000 +RDATE:19561007T030000 +RDATE:19571006T030000 +RDATE:19581005T030000 +RDATE:19591004T030000 +RDATE:19601002T030000 +RDATE:19611029T030000 +RDATE:19621028T030000 +RDATE:19631027T030000 +RDATE:19641025T030000 +RDATE:19651024T030000 +RDATE:19661023T030000 +RDATE:19671029T030000 +RDATE:19711031T030000 +RDATE:19721029T030000 +RDATE:19731028T030000 +RDATE:19741027T030000 +RDATE:19751026T030000 +RDATE:19761024T030000 +RDATE:19771023T030000 +RDATE:19781029T030000 +RDATE:19791028T030000 +RDATE:19801026T030000 +RDATE:19811025T020000 +RDATE:19821024T020000 +RDATE:19831023T020000 +RDATE:19841028T020000 +RDATE:19851027T020000 +RDATE:19861026T020000 +RDATE:19871025T020000 +RDATE:19881023T020000 +RDATE:19891029T020000 +RDATE:19901028T020000 +RDATE:19911027T020000 +RDATE:19921025T020000 +RDATE:19931024T020000 +RDATE:19941023T020000 +RDATE:19951022T020000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +TZNAME:BDST +DTSTART:19410504T010000 +RDATE:19410504T010000 +RDATE:19420405T010000 +RDATE:19430404T010000 +RDATE:19440402T010000 +RDATE:19450402T010000 +RDATE:19470413T010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +TZNAME:BST +DTSTART:19410810T030000 +RDATE:19410810T030000 +RDATE:19420809T030000 +RDATE:19430815T030000 +RDATE:19440917T030000 +RDATE:19450715T030000 +RDATE:19470810T030000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0100 +TZOFFSETTO:+0100 +TZNAME:BST +DTSTART:19681026T230000 +RDATE:19681026T230000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETFROM:+0000 +TZOFFSETTO:+0000 +TZNAME:GMT +DTSTART:19960101T000000 +RDATE:19960101T000000 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DTSTAMP:20241005T112701Z +UID:17281276213728ad54d03afa44d1ca60b8c52afaece9e@sufficientlysecure.org +SUMMARY:event with alarms android +STATUS:CONFIRMED +DTSTART;TZID=Europe/London:20241005T130000 +DTEND:20241005T130000Z +LAST-MODIFIED:20241005T112701Z +BEGIN:VALARM +TRIGGER:-PT30M +ACTION:DISPLAY +DESCRIPTION:event with alarms android +END:VALARM +BEGIN:VALARM +TRIGGER:-PT25M +ACTION:DISPLAY +DESCRIPTION:event with alarms android +END:VALARM +BEGIN:VALARM +TRIGGER:-PT5M +ACTION:DISPLAY +DESCRIPTION:event with alarms android +END:VALARM +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_etar_notification.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_etar_notification.ics new file mode 100644 index 0000000..bbee664 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_etar_notification.ics @@ -0,0 +1,235 @@ +BEGIN:VCALENDAR +PRODID:-//Offline Calendar//iCal Import/Export 2.8.1//EN +VERSION:2.0 +METHOD:PUBLISH +CALSCALE:GREGORIAN +BEGIN:VTIMEZONE +TZID:Europe/London +TZURL:http://tzurl.org/zoneinfo/Europe/London +X-LIC-LOCATION:Europe/London +BEGIN:DAYLIGHT +TZOFFSETFROM:+0000 +TZOFFSETTO:+0100 +TZNAME:BST +DTSTART:19810329T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0100 +TZOFFSETTO:+0000 +TZNAME:GMT +DTSTART:19961027T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +BEGIN:STANDARD +TZOFFSETFROM:-000115 +TZOFFSETTO:+0000 +TZNAME:GMT +DTSTART:18471201T000115 +RDATE:18471201T000115 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETFROM:+0000 +TZOFFSETTO:+0100 +TZNAME:BST +DTSTART:19160521T020000 +RDATE:19160521T020000 +RDATE:19170408T020000 +RDATE:19180324T020000 +RDATE:19190330T020000 +RDATE:19200328T020000 +RDATE:19210403T020000 +RDATE:19220326T020000 +RDATE:19230422T020000 +RDATE:19240413T020000 +RDATE:19250419T020000 +RDATE:19260418T020000 +RDATE:19270410T020000 +RDATE:19280422T020000 +RDATE:19290421T020000 +RDATE:19300413T020000 +RDATE:19310419T020000 +RDATE:19320417T020000 +RDATE:19330409T020000 +RDATE:19340422T020000 +RDATE:19350414T020000 +RDATE:19360419T020000 +RDATE:19370418T020000 +RDATE:19380410T020000 +RDATE:19390416T020000 +RDATE:19400225T020000 +RDATE:19460414T020000 +RDATE:19470316T020000 +RDATE:19480314T020000 +RDATE:19490403T020000 +RDATE:19500416T020000 +RDATE:19510415T020000 +RDATE:19520420T020000 +RDATE:19530419T020000 +RDATE:19540411T020000 +RDATE:19550417T020000 +RDATE:19560422T020000 +RDATE:19570414T020000 +RDATE:19580420T020000 +RDATE:19590419T020000 +RDATE:19600410T020000 +RDATE:19610326T020000 +RDATE:19620325T020000 +RDATE:19630331T020000 +RDATE:19640322T020000 +RDATE:19650321T020000 +RDATE:19660320T020000 +RDATE:19670319T020000 +RDATE:19680218T020000 +RDATE:19720319T020000 +RDATE:19730318T020000 +RDATE:19740317T020000 +RDATE:19750316T020000 +RDATE:19760321T020000 +RDATE:19770320T020000 +RDATE:19780319T020000 +RDATE:19790318T020000 +RDATE:19800316T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0100 +TZOFFSETTO:+0000 +TZNAME:GMT +DTSTART:19161001T030000 +RDATE:19161001T030000 +RDATE:19170917T030000 +RDATE:19180930T030000 +RDATE:19190929T030000 +RDATE:19201025T030000 +RDATE:19211003T030000 +RDATE:19221008T030000 +RDATE:19230916T030000 +RDATE:19240921T030000 +RDATE:19251004T030000 +RDATE:19261003T030000 +RDATE:19271002T030000 +RDATE:19281007T030000 +RDATE:19291006T030000 +RDATE:19301005T030000 +RDATE:19311004T030000 +RDATE:19321002T030000 +RDATE:19331008T030000 +RDATE:19341007T030000 +RDATE:19351006T030000 +RDATE:19361004T030000 +RDATE:19371003T030000 +RDATE:19381002T030000 +RDATE:19391119T030000 +RDATE:19451007T030000 +RDATE:19461006T030000 +RDATE:19471102T030000 +RDATE:19481031T030000 +RDATE:19491030T030000 +RDATE:19501022T030000 +RDATE:19511021T030000 +RDATE:19521026T030000 +RDATE:19531004T030000 +RDATE:19541003T030000 +RDATE:19551002T030000 +RDATE:19561007T030000 +RDATE:19571006T030000 +RDATE:19581005T030000 +RDATE:19591004T030000 +RDATE:19601002T030000 +RDATE:19611029T030000 +RDATE:19621028T030000 +RDATE:19631027T030000 +RDATE:19641025T030000 +RDATE:19651024T030000 +RDATE:19661023T030000 +RDATE:19671029T030000 +RDATE:19711031T030000 +RDATE:19721029T030000 +RDATE:19731028T030000 +RDATE:19741027T030000 +RDATE:19751026T030000 +RDATE:19761024T030000 +RDATE:19771023T030000 +RDATE:19781029T030000 +RDATE:19791028T030000 +RDATE:19801026T030000 +RDATE:19811025T020000 +RDATE:19821024T020000 +RDATE:19831023T020000 +RDATE:19841028T020000 +RDATE:19851027T020000 +RDATE:19861026T020000 +RDATE:19871025T020000 +RDATE:19881023T020000 +RDATE:19891029T020000 +RDATE:19901028T020000 +RDATE:19911027T020000 +RDATE:19921025T020000 +RDATE:19931024T020000 +RDATE:19941023T020000 +RDATE:19951022T020000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +TZNAME:BDST +DTSTART:19410504T010000 +RDATE:19410504T010000 +RDATE:19420405T010000 +RDATE:19430404T010000 +RDATE:19440402T010000 +RDATE:19450402T010000 +RDATE:19470413T010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +TZNAME:BST +DTSTART:19410810T030000 +RDATE:19410810T030000 +RDATE:19420809T030000 +RDATE:19430815T030000 +RDATE:19440917T030000 +RDATE:19450715T030000 +RDATE:19470810T030000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0100 +TZOFFSETTO:+0100 +TZNAME:BST +DTSTART:19681026T230000 +RDATE:19681026T230000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETFROM:+0000 +TZOFFSETTO:+0000 +TZNAME:GMT +DTSTART:19960101T000000 +RDATE:19960101T000000 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DTSTAMP:20241005T113036Z +UID:17281276213728ad54d03afa44d1ca60b8c52afaece9e@sufficientlysecure.org +SUMMARY:event with alarms android +STATUS:CONFIRMED +DTSTART;TZID=Europe/London:20241005T130000 +DTEND:20241005T130000Z +LAST-MODIFIED:20241005T112701Z +BEGIN:VALARM +TRIGGER:-PT30M +ACTION:DISPLAY +DESCRIPTION:event with alarms android +END:VALARM +BEGIN:VALARM +TRIGGER:-PT25M +ACTION:DISPLAY +DESCRIPTION:event with alarms android +END:VALARM +BEGIN:VALARM +TRIGGER:-PT5M +ACTION:DISPLAY +DESCRIPTION:event with alarms android +END:VALARM +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_etar_notification_clicked.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_etar_notification_clicked.ics new file mode 100644 index 0000000..c61e048 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_etar_notification_clicked.ics @@ -0,0 +1,225 @@ +BEGIN:VCALENDAR +PRODID:-//Offline Calendar//iCal Import/Export 2.8.1//EN +VERSION:2.0 +METHOD:PUBLISH +CALSCALE:GREGORIAN +BEGIN:VTIMEZONE +TZID:Europe/London +TZURL:http://tzurl.org/zoneinfo/Europe/London +X-LIC-LOCATION:Europe/London +BEGIN:DAYLIGHT +TZOFFSETFROM:+0000 +TZOFFSETTO:+0100 +TZNAME:BST +DTSTART:19810329T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0100 +TZOFFSETTO:+0000 +TZNAME:GMT +DTSTART:19961027T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +BEGIN:STANDARD +TZOFFSETFROM:-000115 +TZOFFSETTO:+0000 +TZNAME:GMT +DTSTART:18471201T000115 +RDATE:18471201T000115 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETFROM:+0000 +TZOFFSETTO:+0100 +TZNAME:BST +DTSTART:19160521T020000 +RDATE:19160521T020000 +RDATE:19170408T020000 +RDATE:19180324T020000 +RDATE:19190330T020000 +RDATE:19200328T020000 +RDATE:19210403T020000 +RDATE:19220326T020000 +RDATE:19230422T020000 +RDATE:19240413T020000 +RDATE:19250419T020000 +RDATE:19260418T020000 +RDATE:19270410T020000 +RDATE:19280422T020000 +RDATE:19290421T020000 +RDATE:19300413T020000 +RDATE:19310419T020000 +RDATE:19320417T020000 +RDATE:19330409T020000 +RDATE:19340422T020000 +RDATE:19350414T020000 +RDATE:19360419T020000 +RDATE:19370418T020000 +RDATE:19380410T020000 +RDATE:19390416T020000 +RDATE:19400225T020000 +RDATE:19460414T020000 +RDATE:19470316T020000 +RDATE:19480314T020000 +RDATE:19490403T020000 +RDATE:19500416T020000 +RDATE:19510415T020000 +RDATE:19520420T020000 +RDATE:19530419T020000 +RDATE:19540411T020000 +RDATE:19550417T020000 +RDATE:19560422T020000 +RDATE:19570414T020000 +RDATE:19580420T020000 +RDATE:19590419T020000 +RDATE:19600410T020000 +RDATE:19610326T020000 +RDATE:19620325T020000 +RDATE:19630331T020000 +RDATE:19640322T020000 +RDATE:19650321T020000 +RDATE:19660320T020000 +RDATE:19670319T020000 +RDATE:19680218T020000 +RDATE:19720319T020000 +RDATE:19730318T020000 +RDATE:19740317T020000 +RDATE:19750316T020000 +RDATE:19760321T020000 +RDATE:19770320T020000 +RDATE:19780319T020000 +RDATE:19790318T020000 +RDATE:19800316T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0100 +TZOFFSETTO:+0000 +TZNAME:GMT +DTSTART:19161001T030000 +RDATE:19161001T030000 +RDATE:19170917T030000 +RDATE:19180930T030000 +RDATE:19190929T030000 +RDATE:19201025T030000 +RDATE:19211003T030000 +RDATE:19221008T030000 +RDATE:19230916T030000 +RDATE:19240921T030000 +RDATE:19251004T030000 +RDATE:19261003T030000 +RDATE:19271002T030000 +RDATE:19281007T030000 +RDATE:19291006T030000 +RDATE:19301005T030000 +RDATE:19311004T030000 +RDATE:19321002T030000 +RDATE:19331008T030000 +RDATE:19341007T030000 +RDATE:19351006T030000 +RDATE:19361004T030000 +RDATE:19371003T030000 +RDATE:19381002T030000 +RDATE:19391119T030000 +RDATE:19451007T030000 +RDATE:19461006T030000 +RDATE:19471102T030000 +RDATE:19481031T030000 +RDATE:19491030T030000 +RDATE:19501022T030000 +RDATE:19511021T030000 +RDATE:19521026T030000 +RDATE:19531004T030000 +RDATE:19541003T030000 +RDATE:19551002T030000 +RDATE:19561007T030000 +RDATE:19571006T030000 +RDATE:19581005T030000 +RDATE:19591004T030000 +RDATE:19601002T030000 +RDATE:19611029T030000 +RDATE:19621028T030000 +RDATE:19631027T030000 +RDATE:19641025T030000 +RDATE:19651024T030000 +RDATE:19661023T030000 +RDATE:19671029T030000 +RDATE:19711031T030000 +RDATE:19721029T030000 +RDATE:19731028T030000 +RDATE:19741027T030000 +RDATE:19751026T030000 +RDATE:19761024T030000 +RDATE:19771023T030000 +RDATE:19781029T030000 +RDATE:19791028T030000 +RDATE:19801026T030000 +RDATE:19811025T020000 +RDATE:19821024T020000 +RDATE:19831023T020000 +RDATE:19841028T020000 +RDATE:19851027T020000 +RDATE:19861026T020000 +RDATE:19871025T020000 +RDATE:19881023T020000 +RDATE:19891029T020000 +RDATE:19901028T020000 +RDATE:19911027T020000 +RDATE:19921025T020000 +RDATE:19931024T020000 +RDATE:19941023T020000 +RDATE:19951022T020000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +TZNAME:BDST +DTSTART:19410504T010000 +RDATE:19410504T010000 +RDATE:19420405T010000 +RDATE:19430404T010000 +RDATE:19440402T010000 +RDATE:19450402T010000 +RDATE:19470413T010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +TZNAME:BST +DTSTART:19410810T030000 +RDATE:19410810T030000 +RDATE:19420809T030000 +RDATE:19430815T030000 +RDATE:19440917T030000 +RDATE:19450715T030000 +RDATE:19470810T030000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0100 +TZOFFSETTO:+0100 +TZNAME:BST +DTSTART:19681026T230000 +RDATE:19681026T230000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETFROM:+0000 +TZOFFSETTO:+0000 +TZNAME:GMT +DTSTART:19960101T000000 +RDATE:19960101T000000 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DTSTAMP:20241005T130738Z +UID:17281336589228ad54d03afa44d1ca60b8c52afaece9e@sufficientlysecure.org +SUMMARY:event with alarm acknowledged +STATUS:CONFIRMED +DTSTART;TZID=Europe/London:20241005T141700 +DTEND:20241005T141700Z +LAST-MODIFIED:20241005T130738Z +BEGIN:VALARM +TRIGGER:-PT10M +ACTION:DISPLAY +DESCRIPTION:event with alarm acknowledged +END:VALARM +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_google_acknowledged.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_google_acknowledged.ics new file mode 100644 index 0000000..013c240 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_google_acknowledged.ics @@ -0,0 +1,60 @@ +BEGIN:VCALENDAR +PRODID:-//Google Inc//Google Calendar 70.9054//EN +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:PUBLISH +X-WR-CALNAME:Nicco Kunzmann +X-WR-TIMEZONE:Europe/London +BEGIN:VTIMEZONE +TZID:Europe/Berlin +X-LIC-LOCATION:Europe/Berlin +BEGIN:DAYLIGHT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +TZNAME:GMT+2 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +TZNAME:GMT+1 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DTSTART:20241004T181500Z +DTEND:20241004T190000Z +DTSTAMP:20241004T180026Z +UID:79fs7pkqvht9m5igs0vjv1sfra@google.com +CREATED:20241004T175920Z +LAST-MODIFIED:20241004T175928Z +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:event with alarms +TRANSP:OPAQUE +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-P0DT0H10M0S +DESCRIPTION:This is an event reminder +END:VALARM +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-P0DT0H14M0S +DESCRIPTION:This is an event reminder +END:VALARM +BEGIN:VALARM +ACTION:EMAIL +ATTENDEE:mailto:niccokunzmann@googlemail.com +TRIGGER:-P0DT0H15M0S +DESCRIPTION:This is an event reminder +SUMMARY:Alarm notification +END:VALARM +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-P0DT0H15M0S +DESCRIPTION:This is an event reminder +END:VALARM +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_google_future.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_google_future.ics new file mode 100644 index 0000000..f7e4e7e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_google_future.ics @@ -0,0 +1,60 @@ +BEGIN:VCALENDAR +PRODID:-//Google Inc//Google Calendar 70.9054//EN +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:PUBLISH +X-WR-CALNAME:Nicco Kunzmann +X-WR-TIMEZONE:Europe/London +BEGIN:VTIMEZONE +TZID:Europe/Berlin +X-LIC-LOCATION:Europe/Berlin +BEGIN:DAYLIGHT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +TZNAME:GMT+2 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +TZNAME:GMT+1 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DTSTART:20241004T181500Z +DTEND:20241004T190000Z +DTSTAMP:20241004T175945Z +UID:79fs7pkqvht9m5igs0vjv1sfra@google.com +CREATED:20241004T175920Z +LAST-MODIFIED:20241004T175928Z +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:event with alarms +TRANSP:OPAQUE +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-P0DT0H10M0S +DESCRIPTION:This is an event reminder +END:VALARM +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-P0DT0H14M0S +DESCRIPTION:This is an event reminder +END:VALARM +BEGIN:VALARM +ACTION:EMAIL +ATTENDEE:mailto:niccokunzmann@googlemail.com +TRIGGER:-P0DT0H15M0S +DESCRIPTION:This is an event reminder +SUMMARY:Alarm notification +END:VALARM +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-P0DT0H15M0S +DESCRIPTION:This is an event reminder +END:VALARM +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_2_future.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_2_future.ics new file mode 100644 index 0000000..6d25d53 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_2_future.ics @@ -0,0 +1,624 @@ +BEGIN:VCALENDAR +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:Europe/London +X-TZINFO:Europe/London[2024a] +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:-000115 +TZNAME:Europe/London(STD) +DTSTART:18471201T000000 +RDATE:18471201T000000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19160521T020000 +RDATE:19160521T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19161001T030000 +RDATE:19161001T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19170408T020000 +RDATE:19170408T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19170917T030000 +RDATE:19170917T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19180324T020000 +RDATE:19180324T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19180930T030000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1MO;UNTIL=19190929T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19190330T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19200328T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19201025T030000 +RDATE:19201025T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19210403T020000 +RDATE:19210403T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19211003T030000 +RDATE:19211003T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19220326T020000 +RDATE:19220326T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19221008T030000 +RDATE:19221008T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19230422T020000 +RDATE:19230422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19240413T020000 +RDATE:19240413T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19230916T030000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=3SU;UNTIL=19240921T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19250419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19260418T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19270410T020000 +RDATE:19270410T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19280422T020000 +RDATE:19280422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19290421T020000 +RDATE:19290421T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19300413T020000 +RDATE:19300413T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19310419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19320417T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19251004T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19321002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19330409T020000 +RDATE:19330409T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19331008T030000 +RDATE:19331008T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19340422T020000 +RDATE:19340422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19350414T020000 +RDATE:19350414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19360419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19370418T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19380410T020000 +RDATE:19380410T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19341007T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19381002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19390416T020000 +RDATE:19390416T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19400225T020000 +RDATE:19400225T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19410504T020000 +RDATE:19410504T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19410810T030000 +RDATE:19410810T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19420405T020000 +RDATE:19420405T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19420809T030000 +RDATE:19420809T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19430404T020000 +RDATE:19430404T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19430815T030000 +RDATE:19430815T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19440402T020000 +RDATE:19440402T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19440917T030000 +RDATE:19440917T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19450402T020000 +RDATE:19450402T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19391119T030000 +RDATE:19391119T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19450715T030000 +RDATE:19450715T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19460414T020000 +RDATE:19460414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19470316T020000 +RDATE:19470316T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19470413T020000 +RDATE:19470413T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19451007T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19461006T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19470810T030000 +RDATE:19470810T030000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19471102T030000 +RDATE:19471102T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19480314T020000 +RDATE:19480314T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19490403T020000 +RDATE:19490403T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19481031T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19491030T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19501022T030000 +RDATE:19501022T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19511021T030000 +RDATE:19511021T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19521026T030000 +RDATE:19521026T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19500416T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19530419T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19540411T020000 +RDATE:19540411T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19550417T020000 +RDATE:19550417T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19560422T020000 +RDATE:19560422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19570414T020000 +RDATE:19570414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19580420T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19590419T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19600410T020000 +RDATE:19600410T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19531004T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19601002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19610326T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19630331T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19640322T020000 +RDATE:19640322T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19611029T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19641025T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19651024T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19661023T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19650321T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=3SU;UNTIL=19670319T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19671029T030000 +RDATE:19671029T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+010000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19681027T000000 +RDATE:19681027T000000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19680218T020000 +RDATE:19680218T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19711031T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19751026T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19761024T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19771023T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19720319T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=3SU;UNTIL=19800316T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19781029T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19801026T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19811025T020000 +RDATE:19811025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19821024T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19831023T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19841028T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19871025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19881023T020000 +RDATE:19881023T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19891029T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19921025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19931024T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19951022T020000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19810329T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19960331T010000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19961027T020000 +RDATE:19961027T020000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:(DST) +DTSTART:19970330T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:(STD) +DTSTART:19971026T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +CREATED:20241023T173412Z +LAST-MODIFIED:20241023T173453Z +DTSTAMP:20241023T173453Z +UID:731b9b91-cf72-499b-bbc9-c53c28e21fc7 +SUMMARY:event +DTSTART;TZID=Europe/London:20241023T190000 +DTEND;TZID=Europe/London:20241023T200000 +TRANSP:OPAQUE +X-MOZ-GENERATION:2 +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-PT1M +DESCRIPTION:Mozilla Standardbeschreibung +END:VALARM +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-PT24M +DESCRIPTION:Mozilla Standardbeschreibung +END:VALARM +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_2_notification_5_min_postponed.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_2_notification_5_min_postponed.ics new file mode 100644 index 0000000..851a8db --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_2_notification_5_min_postponed.ics @@ -0,0 +1,626 @@ +BEGIN:VCALENDAR +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:Europe/London +X-TZINFO:Europe/London[2024a] +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:-000115 +TZNAME:Europe/London(STD) +DTSTART:18471201T000000 +RDATE:18471201T000000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19160521T020000 +RDATE:19160521T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19161001T030000 +RDATE:19161001T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19170408T020000 +RDATE:19170408T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19170917T030000 +RDATE:19170917T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19180324T020000 +RDATE:19180324T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19180930T030000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1MO;UNTIL=19190929T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19190330T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19200328T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19201025T030000 +RDATE:19201025T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19210403T020000 +RDATE:19210403T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19211003T030000 +RDATE:19211003T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19220326T020000 +RDATE:19220326T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19221008T030000 +RDATE:19221008T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19230422T020000 +RDATE:19230422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19240413T020000 +RDATE:19240413T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19230916T030000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=3SU;UNTIL=19240921T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19250419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19260418T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19270410T020000 +RDATE:19270410T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19280422T020000 +RDATE:19280422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19290421T020000 +RDATE:19290421T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19300413T020000 +RDATE:19300413T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19310419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19320417T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19251004T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19321002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19330409T020000 +RDATE:19330409T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19331008T030000 +RDATE:19331008T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19340422T020000 +RDATE:19340422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19350414T020000 +RDATE:19350414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19360419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19370418T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19380410T020000 +RDATE:19380410T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19341007T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19381002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19390416T020000 +RDATE:19390416T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19400225T020000 +RDATE:19400225T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19410504T020000 +RDATE:19410504T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19410810T030000 +RDATE:19410810T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19420405T020000 +RDATE:19420405T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19420809T030000 +RDATE:19420809T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19430404T020000 +RDATE:19430404T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19430815T030000 +RDATE:19430815T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19440402T020000 +RDATE:19440402T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19440917T030000 +RDATE:19440917T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19450402T020000 +RDATE:19450402T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19391119T030000 +RDATE:19391119T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19450715T030000 +RDATE:19450715T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19460414T020000 +RDATE:19460414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19470316T020000 +RDATE:19470316T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19470413T020000 +RDATE:19470413T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19451007T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19461006T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19470810T030000 +RDATE:19470810T030000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19471102T030000 +RDATE:19471102T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19480314T020000 +RDATE:19480314T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19490403T020000 +RDATE:19490403T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19481031T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19491030T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19501022T030000 +RDATE:19501022T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19511021T030000 +RDATE:19511021T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19521026T030000 +RDATE:19521026T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19500416T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19530419T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19540411T020000 +RDATE:19540411T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19550417T020000 +RDATE:19550417T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19560422T020000 +RDATE:19560422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19570414T020000 +RDATE:19570414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19580420T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19590419T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19600410T020000 +RDATE:19600410T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19531004T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19601002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19610326T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19630331T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19640322T020000 +RDATE:19640322T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19611029T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19641025T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19651024T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19661023T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19650321T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=3SU;UNTIL=19670319T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19671029T030000 +RDATE:19671029T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+010000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19681027T000000 +RDATE:19681027T000000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19680218T020000 +RDATE:19680218T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19711031T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19751026T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19761024T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19771023T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19720319T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=3SU;UNTIL=19800316T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19781029T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19801026T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19811025T020000 +RDATE:19811025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19821024T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19831023T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19841028T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19871025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19881023T020000 +RDATE:19881023T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19891029T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19921025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19931024T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19951022T020000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19810329T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19960331T010000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19961027T020000 +RDATE:19961027T020000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:(DST) +DTSTART:19970330T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:(STD) +DTSTART:19971026T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +CREATED:20241023T173412Z +LAST-MODIFIED:20241023T173630Z +DTSTAMP:20241023T173630Z +UID:731b9b91-cf72-499b-bbc9-c53c28e21fc7 +SUMMARY:event +X-MOZ-LASTACK:20241023T173630Z +DTSTART;TZID=Europe/London:20241023T190000 +DTEND;TZID=Europe/London:20241023T200000 +TRANSP:OPAQUE +X-MOZ-GENERATION:3 +X-MOZ-SNOOZE-TIME:20241023T174130Z +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-PT1M +DESCRIPTION:Mozilla Standardbeschreibung +END:VALARM +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-PT24M +DESCRIPTION:Mozilla Standardbeschreibung +END:VALARM +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_2_notification_5_min_postponed_and_closed.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_2_notification_5_min_postponed_and_closed.ics new file mode 100644 index 0000000..cfa7035 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_2_notification_5_min_postponed_and_closed.ics @@ -0,0 +1,625 @@ +BEGIN:VCALENDAR +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:Europe/London +X-TZINFO:Europe/London[2024a] +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:-000115 +TZNAME:Europe/London(STD) +DTSTART:18471201T000000 +RDATE:18471201T000000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19160521T020000 +RDATE:19160521T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19161001T030000 +RDATE:19161001T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19170408T020000 +RDATE:19170408T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19170917T030000 +RDATE:19170917T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19180324T020000 +RDATE:19180324T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19180930T030000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1MO;UNTIL=19190929T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19190330T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19200328T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19201025T030000 +RDATE:19201025T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19210403T020000 +RDATE:19210403T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19211003T030000 +RDATE:19211003T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19220326T020000 +RDATE:19220326T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19221008T030000 +RDATE:19221008T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19230422T020000 +RDATE:19230422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19240413T020000 +RDATE:19240413T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19230916T030000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=3SU;UNTIL=19240921T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19250419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19260418T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19270410T020000 +RDATE:19270410T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19280422T020000 +RDATE:19280422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19290421T020000 +RDATE:19290421T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19300413T020000 +RDATE:19300413T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19310419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19320417T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19251004T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19321002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19330409T020000 +RDATE:19330409T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19331008T030000 +RDATE:19331008T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19340422T020000 +RDATE:19340422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19350414T020000 +RDATE:19350414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19360419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19370418T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19380410T020000 +RDATE:19380410T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19341007T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19381002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19390416T020000 +RDATE:19390416T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19400225T020000 +RDATE:19400225T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19410504T020000 +RDATE:19410504T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19410810T030000 +RDATE:19410810T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19420405T020000 +RDATE:19420405T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19420809T030000 +RDATE:19420809T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19430404T020000 +RDATE:19430404T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19430815T030000 +RDATE:19430815T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19440402T020000 +RDATE:19440402T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19440917T030000 +RDATE:19440917T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19450402T020000 +RDATE:19450402T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19391119T030000 +RDATE:19391119T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19450715T030000 +RDATE:19450715T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19460414T020000 +RDATE:19460414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19470316T020000 +RDATE:19470316T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19470413T020000 +RDATE:19470413T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19451007T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19461006T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19470810T030000 +RDATE:19470810T030000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19471102T030000 +RDATE:19471102T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19480314T020000 +RDATE:19480314T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19490403T020000 +RDATE:19490403T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19481031T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19491030T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19501022T030000 +RDATE:19501022T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19511021T030000 +RDATE:19511021T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19521026T030000 +RDATE:19521026T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19500416T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19530419T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19540411T020000 +RDATE:19540411T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19550417T020000 +RDATE:19550417T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19560422T020000 +RDATE:19560422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19570414T020000 +RDATE:19570414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19580420T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19590419T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19600410T020000 +RDATE:19600410T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19531004T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19601002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19610326T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19630331T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19640322T020000 +RDATE:19640322T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19611029T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19641025T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19651024T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19661023T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19650321T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=3SU;UNTIL=19670319T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19671029T030000 +RDATE:19671029T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+010000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19681027T000000 +RDATE:19681027T000000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19680218T020000 +RDATE:19680218T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19711031T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19751026T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19761024T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19771023T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19720319T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=3SU;UNTIL=19800316T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19781029T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19801026T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19811025T020000 +RDATE:19811025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19821024T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19831023T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19841028T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19871025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19881023T020000 +RDATE:19881023T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19891029T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19921025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19931024T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19951022T020000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19810329T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19960331T010000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19961027T020000 +RDATE:19961027T020000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:(DST) +DTSTART:19970330T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:(STD) +DTSTART:19971026T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +CREATED:20241023T173412Z +LAST-MODIFIED:20241023T174207Z +DTSTAMP:20241023T174207Z +UID:731b9b91-cf72-499b-bbc9-c53c28e21fc7 +SUMMARY:event +X-MOZ-LASTACK:20241023T174207Z +DTSTART;TZID=Europe/London:20241023T190000 +DTEND;TZID=Europe/London:20241023T200000 +TRANSP:OPAQUE +X-MOZ-GENERATION:4 +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-PT1M +DESCRIPTION:Mozilla Standardbeschreibung +END:VALARM +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-PT24M +DESCRIPTION:Mozilla Standardbeschreibung +END:VALARM +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_2_notification_5_min_postponed_and_popped_up.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_2_notification_5_min_postponed_and_popped_up.ics new file mode 100644 index 0000000..851a8db --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_2_notification_5_min_postponed_and_popped_up.ics @@ -0,0 +1,626 @@ +BEGIN:VCALENDAR +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:Europe/London +X-TZINFO:Europe/London[2024a] +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:-000115 +TZNAME:Europe/London(STD) +DTSTART:18471201T000000 +RDATE:18471201T000000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19160521T020000 +RDATE:19160521T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19161001T030000 +RDATE:19161001T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19170408T020000 +RDATE:19170408T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19170917T030000 +RDATE:19170917T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19180324T020000 +RDATE:19180324T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19180930T030000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1MO;UNTIL=19190929T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19190330T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19200328T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19201025T030000 +RDATE:19201025T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19210403T020000 +RDATE:19210403T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19211003T030000 +RDATE:19211003T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19220326T020000 +RDATE:19220326T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19221008T030000 +RDATE:19221008T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19230422T020000 +RDATE:19230422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19240413T020000 +RDATE:19240413T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19230916T030000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=3SU;UNTIL=19240921T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19250419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19260418T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19270410T020000 +RDATE:19270410T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19280422T020000 +RDATE:19280422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19290421T020000 +RDATE:19290421T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19300413T020000 +RDATE:19300413T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19310419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19320417T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19251004T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19321002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19330409T020000 +RDATE:19330409T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19331008T030000 +RDATE:19331008T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19340422T020000 +RDATE:19340422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19350414T020000 +RDATE:19350414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19360419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19370418T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19380410T020000 +RDATE:19380410T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19341007T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19381002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19390416T020000 +RDATE:19390416T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19400225T020000 +RDATE:19400225T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19410504T020000 +RDATE:19410504T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19410810T030000 +RDATE:19410810T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19420405T020000 +RDATE:19420405T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19420809T030000 +RDATE:19420809T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19430404T020000 +RDATE:19430404T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19430815T030000 +RDATE:19430815T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19440402T020000 +RDATE:19440402T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19440917T030000 +RDATE:19440917T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19450402T020000 +RDATE:19450402T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19391119T030000 +RDATE:19391119T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19450715T030000 +RDATE:19450715T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19460414T020000 +RDATE:19460414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19470316T020000 +RDATE:19470316T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19470413T020000 +RDATE:19470413T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19451007T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19461006T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19470810T030000 +RDATE:19470810T030000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19471102T030000 +RDATE:19471102T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19480314T020000 +RDATE:19480314T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19490403T020000 +RDATE:19490403T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19481031T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19491030T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19501022T030000 +RDATE:19501022T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19511021T030000 +RDATE:19511021T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19521026T030000 +RDATE:19521026T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19500416T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19530419T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19540411T020000 +RDATE:19540411T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19550417T020000 +RDATE:19550417T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19560422T020000 +RDATE:19560422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19570414T020000 +RDATE:19570414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19580420T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19590419T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19600410T020000 +RDATE:19600410T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19531004T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19601002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19610326T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19630331T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19640322T020000 +RDATE:19640322T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19611029T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19641025T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19651024T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19661023T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19650321T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=3SU;UNTIL=19670319T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19671029T030000 +RDATE:19671029T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+010000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19681027T000000 +RDATE:19681027T000000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19680218T020000 +RDATE:19680218T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19711031T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19751026T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19761024T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19771023T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19720319T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=3SU;UNTIL=19800316T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19781029T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19801026T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19811025T020000 +RDATE:19811025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19821024T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19831023T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19841028T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19871025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19881023T020000 +RDATE:19881023T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19891029T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19921025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19931024T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19951022T020000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19810329T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19960331T010000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19961027T020000 +RDATE:19961027T020000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:(DST) +DTSTART:19970330T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:(STD) +DTSTART:19971026T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +CREATED:20241023T173412Z +LAST-MODIFIED:20241023T173630Z +DTSTAMP:20241023T173630Z +UID:731b9b91-cf72-499b-bbc9-c53c28e21fc7 +SUMMARY:event +X-MOZ-LASTACK:20241023T173630Z +DTSTART;TZID=Europe/London:20241023T190000 +DTEND;TZID=Europe/London:20241023T200000 +TRANSP:OPAQUE +X-MOZ-GENERATION:3 +X-MOZ-SNOOZE-TIME:20241023T174130Z +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-PT1M +DESCRIPTION:Mozilla Standardbeschreibung +END:VALARM +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-PT24M +DESCRIPTION:Mozilla Standardbeschreibung +END:VALARM +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_2_notification_popped_up.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_2_notification_popped_up.ics new file mode 100644 index 0000000..6d25d53 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_2_notification_popped_up.ics @@ -0,0 +1,624 @@ +BEGIN:VCALENDAR +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:Europe/London +X-TZINFO:Europe/London[2024a] +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:-000115 +TZNAME:Europe/London(STD) +DTSTART:18471201T000000 +RDATE:18471201T000000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19160521T020000 +RDATE:19160521T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19161001T030000 +RDATE:19161001T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19170408T020000 +RDATE:19170408T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19170917T030000 +RDATE:19170917T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19180324T020000 +RDATE:19180324T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19180930T030000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1MO;UNTIL=19190929T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19190330T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19200328T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19201025T030000 +RDATE:19201025T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19210403T020000 +RDATE:19210403T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19211003T030000 +RDATE:19211003T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19220326T020000 +RDATE:19220326T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19221008T030000 +RDATE:19221008T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19230422T020000 +RDATE:19230422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19240413T020000 +RDATE:19240413T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19230916T030000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=3SU;UNTIL=19240921T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19250419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19260418T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19270410T020000 +RDATE:19270410T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19280422T020000 +RDATE:19280422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19290421T020000 +RDATE:19290421T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19300413T020000 +RDATE:19300413T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19310419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19320417T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19251004T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19321002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19330409T020000 +RDATE:19330409T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19331008T030000 +RDATE:19331008T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19340422T020000 +RDATE:19340422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19350414T020000 +RDATE:19350414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19360419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19370418T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19380410T020000 +RDATE:19380410T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19341007T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19381002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19390416T020000 +RDATE:19390416T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19400225T020000 +RDATE:19400225T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19410504T020000 +RDATE:19410504T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19410810T030000 +RDATE:19410810T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19420405T020000 +RDATE:19420405T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19420809T030000 +RDATE:19420809T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19430404T020000 +RDATE:19430404T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19430815T030000 +RDATE:19430815T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19440402T020000 +RDATE:19440402T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19440917T030000 +RDATE:19440917T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19450402T020000 +RDATE:19450402T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19391119T030000 +RDATE:19391119T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19450715T030000 +RDATE:19450715T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19460414T020000 +RDATE:19460414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19470316T020000 +RDATE:19470316T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19470413T020000 +RDATE:19470413T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19451007T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19461006T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19470810T030000 +RDATE:19470810T030000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19471102T030000 +RDATE:19471102T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19480314T020000 +RDATE:19480314T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19490403T020000 +RDATE:19490403T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19481031T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19491030T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19501022T030000 +RDATE:19501022T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19511021T030000 +RDATE:19511021T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19521026T030000 +RDATE:19521026T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19500416T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19530419T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19540411T020000 +RDATE:19540411T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19550417T020000 +RDATE:19550417T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19560422T020000 +RDATE:19560422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19570414T020000 +RDATE:19570414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19580420T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19590419T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19600410T020000 +RDATE:19600410T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19531004T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19601002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19610326T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19630331T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19640322T020000 +RDATE:19640322T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19611029T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19641025T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19651024T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19661023T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19650321T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=3SU;UNTIL=19670319T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19671029T030000 +RDATE:19671029T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+010000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19681027T000000 +RDATE:19681027T000000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19680218T020000 +RDATE:19680218T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19711031T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19751026T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19761024T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19771023T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19720319T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=3SU;UNTIL=19800316T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19781029T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19801026T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19811025T020000 +RDATE:19811025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19821024T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19831023T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19841028T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19871025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19881023T020000 +RDATE:19881023T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19891029T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19921025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19931024T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19951022T020000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19810329T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19960331T010000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19961027T020000 +RDATE:19961027T020000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:(DST) +DTSTART:19970330T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:(STD) +DTSTART:19971026T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +CREATED:20241023T173412Z +LAST-MODIFIED:20241023T173453Z +DTSTAMP:20241023T173453Z +UID:731b9b91-cf72-499b-bbc9-c53c28e21fc7 +SUMMARY:event +DTSTART;TZID=Europe/London:20241023T190000 +DTEND;TZID=Europe/London:20241023T200000 +TRANSP:OPAQUE +X-MOZ-GENERATION:2 +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-PT1M +DESCRIPTION:Mozilla Standardbeschreibung +END:VALARM +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-PT24M +DESCRIPTION:Mozilla Standardbeschreibung +END:VALARM +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_closed.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_closed.ics new file mode 100644 index 0000000..524f069 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_closed.ics @@ -0,0 +1,625 @@ +BEGIN:VCALENDAR +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:Europe/London +X-TZINFO:Europe/London[2024a] +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:-000115 +TZNAME:Europe/London(STD) +DTSTART:18471201T000000 +RDATE:18471201T000000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19160521T020000 +RDATE:19160521T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19161001T030000 +RDATE:19161001T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19170408T020000 +RDATE:19170408T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19170917T030000 +RDATE:19170917T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19180324T020000 +RDATE:19180324T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19180930T030000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1MO;UNTIL=19190929T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19190330T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19200328T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19201025T030000 +RDATE:19201025T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19210403T020000 +RDATE:19210403T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19211003T030000 +RDATE:19211003T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19220326T020000 +RDATE:19220326T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19221008T030000 +RDATE:19221008T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19230422T020000 +RDATE:19230422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19240413T020000 +RDATE:19240413T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19230916T030000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=3SU;UNTIL=19240921T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19250419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19260418T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19270410T020000 +RDATE:19270410T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19280422T020000 +RDATE:19280422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19290421T020000 +RDATE:19290421T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19300413T020000 +RDATE:19300413T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19310419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19320417T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19251004T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19321002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19330409T020000 +RDATE:19330409T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19331008T030000 +RDATE:19331008T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19340422T020000 +RDATE:19340422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19350414T020000 +RDATE:19350414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19360419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19370418T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19380410T020000 +RDATE:19380410T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19341007T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19381002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19390416T020000 +RDATE:19390416T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19400225T020000 +RDATE:19400225T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19410504T020000 +RDATE:19410504T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19410810T030000 +RDATE:19410810T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19420405T020000 +RDATE:19420405T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19420809T030000 +RDATE:19420809T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19430404T020000 +RDATE:19430404T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19430815T030000 +RDATE:19430815T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19440402T020000 +RDATE:19440402T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19440917T030000 +RDATE:19440917T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19450402T020000 +RDATE:19450402T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19391119T030000 +RDATE:19391119T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19450715T030000 +RDATE:19450715T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19460414T020000 +RDATE:19460414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19470316T020000 +RDATE:19470316T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19470413T020000 +RDATE:19470413T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19451007T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19461006T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19470810T030000 +RDATE:19470810T030000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19471102T030000 +RDATE:19471102T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19480314T020000 +RDATE:19480314T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19490403T020000 +RDATE:19490403T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19481031T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19491030T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19501022T030000 +RDATE:19501022T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19511021T030000 +RDATE:19511021T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19521026T030000 +RDATE:19521026T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19500416T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19530419T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19540411T020000 +RDATE:19540411T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19550417T020000 +RDATE:19550417T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19560422T020000 +RDATE:19560422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19570414T020000 +RDATE:19570414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19580420T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19590419T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19600410T020000 +RDATE:19600410T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19531004T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19601002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19610326T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19630331T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19640322T020000 +RDATE:19640322T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19611029T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19641025T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19651024T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19661023T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19650321T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=3SU;UNTIL=19670319T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19671029T030000 +RDATE:19671029T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+010000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19681027T000000 +RDATE:19681027T000000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19680218T020000 +RDATE:19680218T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19711031T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19751026T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19761024T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19771023T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19720319T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=3SU;UNTIL=19800316T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19781029T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19801026T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19811025T020000 +RDATE:19811025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19821024T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19831023T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19841028T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19871025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19881023T020000 +RDATE:19881023T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19891029T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19921025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19931024T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19951022T020000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19810329T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19960331T010000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19961027T020000 +RDATE:19961027T020000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:(DST) +DTSTART:19970330T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:(STD) +DTSTART:19971026T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +CREATED:20241023T131035Z +LAST-MODIFIED:20241023T141941Z +DTSTAMP:20241023T141941Z +UID:b9a23b47-f109-4e7a-908c-75e925b27def +SUMMARY:event with alarms +X-MOZ-LASTACK:20241023T141941Z +DTSTART;TZID=Europe/London:20241023T150000 +DTEND;TZID=Europe/London:20241023T160000 +TRANSP:OPAQUE +X-MOZ-GENERATION:6 +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-PT15M +DESCRIPTION:Mozilla Standardbeschreibung +END:VALARM +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-PT45M +DESCRIPTION:Mozilla Standardbeschreibung +END:VALARM +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_future.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_future.ics new file mode 100644 index 0000000..5c491a6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_future.ics @@ -0,0 +1,624 @@ +BEGIN:VCALENDAR +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:Europe/London +X-TZINFO:Europe/London[2024a] +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:-000115 +TZNAME:Europe/London(STD) +DTSTART:18471201T000000 +RDATE:18471201T000000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19160521T020000 +RDATE:19160521T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19161001T030000 +RDATE:19161001T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19170408T020000 +RDATE:19170408T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19170917T030000 +RDATE:19170917T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19180324T020000 +RDATE:19180324T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19180930T030000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1MO;UNTIL=19190929T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19190330T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19200328T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19201025T030000 +RDATE:19201025T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19210403T020000 +RDATE:19210403T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19211003T030000 +RDATE:19211003T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19220326T020000 +RDATE:19220326T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19221008T030000 +RDATE:19221008T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19230422T020000 +RDATE:19230422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19240413T020000 +RDATE:19240413T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19230916T030000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=3SU;UNTIL=19240921T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19250419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19260418T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19270410T020000 +RDATE:19270410T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19280422T020000 +RDATE:19280422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19290421T020000 +RDATE:19290421T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19300413T020000 +RDATE:19300413T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19310419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19320417T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19251004T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19321002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19330409T020000 +RDATE:19330409T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19331008T030000 +RDATE:19331008T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19340422T020000 +RDATE:19340422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19350414T020000 +RDATE:19350414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19360419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19370418T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19380410T020000 +RDATE:19380410T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19341007T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19381002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19390416T020000 +RDATE:19390416T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19400225T020000 +RDATE:19400225T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19410504T020000 +RDATE:19410504T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19410810T030000 +RDATE:19410810T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19420405T020000 +RDATE:19420405T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19420809T030000 +RDATE:19420809T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19430404T020000 +RDATE:19430404T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19430815T030000 +RDATE:19430815T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19440402T020000 +RDATE:19440402T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19440917T030000 +RDATE:19440917T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19450402T020000 +RDATE:19450402T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19391119T030000 +RDATE:19391119T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19450715T030000 +RDATE:19450715T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19460414T020000 +RDATE:19460414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19470316T020000 +RDATE:19470316T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19470413T020000 +RDATE:19470413T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19451007T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19461006T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19470810T030000 +RDATE:19470810T030000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19471102T030000 +RDATE:19471102T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19480314T020000 +RDATE:19480314T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19490403T020000 +RDATE:19490403T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19481031T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19491030T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19501022T030000 +RDATE:19501022T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19511021T030000 +RDATE:19511021T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19521026T030000 +RDATE:19521026T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19500416T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19530419T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19540411T020000 +RDATE:19540411T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19550417T020000 +RDATE:19550417T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19560422T020000 +RDATE:19560422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19570414T020000 +RDATE:19570414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19580420T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19590419T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19600410T020000 +RDATE:19600410T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19531004T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19601002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19610326T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19630331T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19640322T020000 +RDATE:19640322T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19611029T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19641025T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19651024T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19661023T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19650321T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=3SU;UNTIL=19670319T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19671029T030000 +RDATE:19671029T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+010000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19681027T000000 +RDATE:19681027T000000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19680218T020000 +RDATE:19680218T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19711031T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19751026T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19761024T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19771023T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19720319T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=3SU;UNTIL=19800316T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19781029T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19801026T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19811025T020000 +RDATE:19811025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19821024T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19831023T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19841028T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19871025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19881023T020000 +RDATE:19881023T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19891029T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19921025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19931024T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19951022T020000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19810329T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19960331T010000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19961027T020000 +RDATE:19961027T020000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:(DST) +DTSTART:19970330T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:(STD) +DTSTART:19971026T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +CREATED:20241023T131035Z +LAST-MODIFIED:20241023T131141Z +DTSTAMP:20241023T131141Z +UID:b9a23b47-f109-4e7a-908c-75e925b27def +SUMMARY:event with alarms +DTSTART;TZID=Europe/London:20241023T150000 +DTEND;TZID=Europe/London:20241023T160000 +TRANSP:OPAQUE +X-MOZ-GENERATION:2 +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-PT15M +DESCRIPTION:Mozilla Standardbeschreibung +END:VALARM +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-PT45M +DESCRIPTION:Mozilla Standardbeschreibung +END:VALARM +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_snoozed_until_1457.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_snoozed_until_1457.ics new file mode 100644 index 0000000..89aa1f3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/alarm_thunderbird_snoozed_until_1457.ics @@ -0,0 +1,626 @@ +BEGIN:VCALENDAR +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:Europe/London +X-TZINFO:Europe/London[2024a] +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:-000115 +TZNAME:Europe/London(STD) +DTSTART:18471201T000000 +RDATE:18471201T000000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19160521T020000 +RDATE:19160521T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19161001T030000 +RDATE:19161001T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19170408T020000 +RDATE:19170408T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19170917T030000 +RDATE:19170917T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19180324T020000 +RDATE:19180324T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19180930T030000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1MO;UNTIL=19190929T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19190330T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19200328T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19201025T030000 +RDATE:19201025T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19210403T020000 +RDATE:19210403T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19211003T030000 +RDATE:19211003T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19220326T020000 +RDATE:19220326T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19221008T030000 +RDATE:19221008T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19230422T020000 +RDATE:19230422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19240413T020000 +RDATE:19240413T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19230916T030000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=3SU;UNTIL=19240921T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19250419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19260418T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19270410T020000 +RDATE:19270410T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19280422T020000 +RDATE:19280422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19290421T020000 +RDATE:19290421T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19300413T020000 +RDATE:19300413T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19310419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19320417T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19251004T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19321002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19330409T020000 +RDATE:19330409T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19331008T030000 +RDATE:19331008T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19340422T020000 +RDATE:19340422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19350414T020000 +RDATE:19350414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19360419T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19370418T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19380410T020000 +RDATE:19380410T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19341007T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19381002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19390416T020000 +RDATE:19390416T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19400225T020000 +RDATE:19400225T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19410504T020000 +RDATE:19410504T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19410810T030000 +RDATE:19410810T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19420405T020000 +RDATE:19420405T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19420809T030000 +RDATE:19420809T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19430404T020000 +RDATE:19430404T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19430815T030000 +RDATE:19430815T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19440402T020000 +RDATE:19440402T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19440917T030000 +RDATE:19440917T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19450402T020000 +RDATE:19450402T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19391119T030000 +RDATE:19391119T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19450715T030000 +RDATE:19450715T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19460414T020000 +RDATE:19460414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19470316T020000 +RDATE:19470316T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(DST) +DTSTART:19470413T020000 +RDATE:19470413T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19451007T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19461006T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/London(DST) +DTSTART:19470810T030000 +RDATE:19470810T030000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19471102T030000 +RDATE:19471102T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19480314T020000 +RDATE:19480314T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19490403T020000 +RDATE:19490403T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19481031T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19491030T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19501022T030000 +RDATE:19501022T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19511021T030000 +RDATE:19511021T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19521026T030000 +RDATE:19521026T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19500416T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19530419T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19540411T020000 +RDATE:19540411T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19550417T020000 +RDATE:19550417T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19560422T020000 +RDATE:19560422T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19570414T020000 +RDATE:19570414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19580420T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3SU;UNTIL=19590419T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19600410T020000 +RDATE:19600410T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19531004T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19601002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19610326T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19630331T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19640322T020000 +RDATE:19640322T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19611029T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19641025T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19651024T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19661023T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19650321T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=3SU;UNTIL=19670319T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19671029T030000 +RDATE:19671029T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+010000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19681027T000000 +RDATE:19681027T000000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19680218T020000 +RDATE:19680218T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19711031T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19751026T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19761024T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19771023T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19720319T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=3SU;UNTIL=19800316T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19781029T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19801026T030000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19811025T020000 +RDATE:19811025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19821024T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19831023T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19841028T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19871025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19881023T020000 +RDATE:19881023T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19891029T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19921025T020000 +END:STANDARD +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19931024T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=4SU;UNTIL=19951022T020000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:Europe/London(DST) +DTSTART:19810329T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19960331T010000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:Europe/London(STD) +DTSTART:19961027T020000 +RDATE:19961027T020000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+010000 +TZOFFSETFROM:+000000 +TZNAME:(DST) +DTSTART:19970330T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+000000 +TZOFFSETFROM:+010000 +TZNAME:(STD) +DTSTART:19971026T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +CREATED:20241023T131035Z +LAST-MODIFIED:20241023T135202Z +DTSTAMP:20241023T135202Z +UID:b9a23b47-f109-4e7a-908c-75e925b27def +SUMMARY:event with alarms +X-MOZ-LASTACK:20241023T135202Z +DTSTART;TZID=Europe/London:20241023T150000 +DTEND;TZID=Europe/London:20241023T160000 +TRANSP:OPAQUE +X-MOZ-GENERATION:4 +X-MOZ-SNOOZE-TIME:20241023T135702Z +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-PT15M +DESCRIPTION:Mozilla Standardbeschreibung +END:VALARM +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER:-PT45M +DESCRIPTION:Mozilla Standardbeschreibung +END:VALARM +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/america_new_york.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/america_new_york.ics new file mode 100644 index 0000000..48338ac --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/america_new_york.ics @@ -0,0 +1,61 @@ +BEGIN:VCALENDAR +BEGIN:VTIMEZONE +TZID:custom_America/New_York +LAST-MODIFIED:20050809T050000Z +BEGIN:DAYLIGHT +DTSTART:19670430T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=-1SU;UNTIL=19730429T070000Z +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=20061029T060000Z +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19740106T020000 +RDATE:19750223T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19760425T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=-1SU;UNTIL=19860427T070000Z +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU;UNTIL=20060402T070000Z +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:20070311T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +BEGIN:STANDARD +DTSTART:20071104T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +UID:noend123 +DTSTART;TZID=custom_America/New_York;VALUE=DATE-TIME:20140829T080000 +DTEND;TZID=custom_America/New_York;VALUE=DATE-TIME:20140829T100000 +SUMMARY:an event with a custom tz name +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/america_new_york_forward_reference.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/america_new_york_forward_reference.ics new file mode 100644 index 0000000..6dbdfcb --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/america_new_york_forward_reference.ics @@ -0,0 +1,61 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:noend123 +DTSTART;TZID=custom_America/New_York_Forward_reference;VALUE=DATE-TIME:20140829T080000 +DTSTART;TZID=custom_America/New_York_Forward_reference;VALUE=DATE-TIME:20140829T100000 +SUMMARY:an event with a custom tz name +END:VEVENT +BEGIN:VTIMEZONE +TZID:custom_America/New_York_Forward_reference +LAST-MODIFIED:20050809T050000Z +BEGIN:DAYLIGHT +DTSTART:19670430T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=-1SU;UNTIL=19730429T070000Z +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=20061029T060000Z +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19740106T020000 +RDATE:19750223T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19760425T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=-1SU;UNTIL=19860427T070000Z +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU;UNTIL=20060402T070000Z +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:20070311T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +BEGIN:STANDARD +DTSTART:20071104T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +END:VTIMEZONE +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/big_bad_calendar.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/big_bad_calendar.ics new file mode 100644 index 0000000..88fbc6f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/big_bad_calendar.ics @@ -0,0 +1,41 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +END:VEVENT +BEGIN:VEVENT +END:VEVENT +BEGIN:VEVENT +END:VEVENT +BEGIN:VEVENT +END:VEVENT +BEGIN:VEVENT +END:VEVENT +BEGIN:VEVENT +END:VEVENT +BEGIN:VEVENT +END:VEVENT +BEGIN:VEVENT +END:VEVENT +BEGIN:VEVENT +END:VEVENT +BEGIN:VEVENT +END:VEVENT +BEGIN:VEVENT +END:VEVENT +BEGIN:VEVENT +END:VEVENT +BEGIN:VEVENT +END:VEVENT +BEGIN:VEVENT +END:VEVENT +BEGIN:VEVENT +END:VEVENT +BEGIN:VEVENT +END:VEVENT +BEGIN:VEVENT +END:VEVENT +BEGIN:VEVENT +END:VEVENT +BEGIN:VEVENT +END:VEVENT +BEGIN:VEVENT +END:VEVENT diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/bom_calendar.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/bom_calendar.ics new file mode 100644 index 0000000..7a9872c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/bom_calendar.ics @@ -0,0 +1,2 @@ +BEGIN:VCALENDAR +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/broken_ical.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/broken_ical.ics new file mode 100644 index 0000000..71604a2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/broken_ical.ics @@ -0,0 +1,7 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +SUMMARY:An Event with too many semicolons +DTSTART;;VALUE=DATE-TIME:20140409T093000 +UID:abc +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/calendar_with_unicode.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/calendar_with_unicode.ics new file mode 100644 index 0000000..e5e2f41 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/calendar_with_unicode.ics @@ -0,0 +1,7 @@ +BEGIN:VCALENDAR +PRODID:-//Plönë.org//NONSGML plone.app.event//EN +VERSION:2.0 +X-WR-CALNAME:äöü ÄÖÜ € +X-WR-CALDESC:test non ascii: äöü ÄÖÜ € +X-WR-RELCALID:12345 +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/created_calendar_with_unicode_fields.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/created_calendar_with_unicode_fields.ics new file mode 100644 index 0000000..b70e8de --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/created_calendar_with_unicode_fields.ics @@ -0,0 +1,23 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Plönë.org//NONSGML plone.app.event//EN +X-WR-CALDESC:test non ascii: äöü ÄÖÜ € +X-WR-CALNAME:äöü ÄÖÜ € +X-WR-RELCALID:12345 +BEGIN:VEVENT +SUMMARY:Non-ASCII Test: ÄÖÜ äöü € +DTSTART:20101010T100000Z +DTEND:20101010T120000Z +UID:123456 +CREATED:20101010T000000Z +DESCRIPTION:icalendar should be able to de/serialize non-ascii. +LOCATION:Tribstrül +END:VEVENT +BEGIN:VEVENT +SUMMARY:åäö +DTSTART:20101010T000000Z +END:VEVENT +BEGIN:VEVENT +DESCRIPTION:äöüßÄÖÜ +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/example.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/example.ics new file mode 100644 index 0000000..a01c8f4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/example.ics @@ -0,0 +1,40 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:collective/icalendar +CALSCALE:GREGORIAN +METHOD:PUBLISH +X-WR-CALNAME:Holidays +X-WR-TIMEZONE:Etc/GMT +BEGIN:VEVENT +SUMMARY:New Year's Day +DTSTART:20220101 +DTEND:20220101 +DESCRIPTION:Happy New Year! +UID:636a0cc1dbd5a1667894465@icalendar +DTSTAMP:20221108T080105Z +STATUS:CONFIRMED +TRANSP:TRANSPARENT +SEQUENCE:0 +END:VEVENT +BEGIN:VEVENT +SUMMARY:Orthodox Christmas +DTSTART:20220107 +DTEND:20220107 +LOCATION:Russia +DESCRIPTION:It is Christmas again! +UID:636a0cc1dbfd91667894465@icalendar +STATUS:CONFIRMED +TRANSP:TRANSPARENT +SEQUENCE:0 +END:VEVENT +BEGIN:VEVENT +SUMMARY:International Women's Day +DTSTART:20220308 +DTEND:20220308 +DESCRIPTION:May the feminine be honoured! +UID:636a0cc1dc0f11667894465@icalendar +STATUS:CONFIRMED +TRANSP:TRANSPARENT +SEQUENCE:0 +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_104_broken_calendar.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_104_broken_calendar.ics new file mode 100644 index 0000000..117afdf --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_104_broken_calendar.ics @@ -0,0 +1,14 @@ +BEGIN:VCALENDAR +VERSION:2.0 +METHOD:PUBLISH +BEGIN:VEVENT +DTSTART:20140401T000000Z +DTEND:20140401T010000Z +DTSTAMP:20140401T000000Z +SUMMARY:Broken Eevnt +CLASS:PUBLIC +STATUS:CONFIRMED +TRANSP:OPAQUE +END:VEVENT +X +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_156_RDATE_with_PERIOD_TZID_khal.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_156_RDATE_with_PERIOD_TZID_khal.ics new file mode 100644 index 0000000..9d62893 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_156_RDATE_with_PERIOD_TZID_khal.ics @@ -0,0 +1,25 @@ +BEGIN:VCALENDAR +X-SOURCE-URL:https://github.com/pimutils/khal/issues/152#issuecomment-387410353 +VERSION:2.0 +PRODID:-//PIMUTILS.ORG//NONSGML khal / icalendar //EN +BEGIN:VEVENT +SUMMARY:Event +DTSTART;TZID=America/Chicago;VALUE=DATE-TIME:20180327T080000 +DTEND;TZID=America/Chicago;VALUE=DATE-TIME:20180327T090000 +DTSTAMP:20180323T200333Z +RECURRENCE-ID;RANGE=THISANDFUTURE:20180327T130000Z +SEQUENCE:10 +RDATE;TZID="Central Standard Time";VALUE=PERIOD:20180327T080000/20180327T0 + 90000,20180403T080000/20180403T090000,20180410T080000/20180410T090000,2018 + 0417T080000/20180417T090000,20180424T080000/20180424T090000,20180501T08000 + 0/20180501T090000,20180508T080000/20180508T090000,20180515T080000/20180515 + T090000,20180522T080000/20180522T090000,20180529T080000/20180529T090000,20 + 180605T080000/20180605T090000,20180612T080000/20180612T090000,20180619T080 + 000/20180619T090000,20180626T080000/20180626T090000,20180703T080000/201807 + 03T090000,20180710T080000/20180710T090000,20180717T080000/20180717T090000, + 20180724T080000/20180724T090000,20180731T080000/20180731T090000 +ATTENDEE;CN="XYZ";PARTSTAT=ACCEPTED;ROLE=CHAIR;RSVP= + FALSE:mailto:xyz@xyz.com +CLASS:PUBLIC +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_156_RDATE_with_PERIOD_TZID_khal_2.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_156_RDATE_with_PERIOD_TZID_khal_2.ics new file mode 100644 index 0000000..9c5288a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_156_RDATE_with_PERIOD_TZID_khal_2.ics @@ -0,0 +1,55 @@ +BEGIN:VCALENDAR +X-SOURCE-URL:https://github.com/pimutils/khal/issues/152#issuecomment-933635248 +VERSION:2.0 +PRODID:-//PIMUTILS.ORG//NONSGML khal / icalendar //EN +BEGIN:VTIMEZONE +TZID:Western/Central Europe +BEGIN:STANDARD +DTSTART:19501029T020000 +RRULE:FREQ=YEARLY;BYMINUTE=0;BYHOUR=2;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19500326T020000 +RRULE:FREQ=YEARLY;BYMINUTE=0;BYHOUR=2;BYDAY=-1SU;BYMONTH=3 +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +SUMMARY:(omitted) +DTSTART;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T160000 +DTEND;TZID="Western/Central Europe";VALUE=DATE-TIME:20211101T163000 +DTSTAMP:20211004T150245Z +UID:BF5109494E67AAE20025875100566D31-Lotus_Notes_Generated +RECURRENCE-ID;RANGE=THISANDFUTURE:20211101T150000Z +SEQUENCE:0 +RDATE;TZID="Western/Central Europe";VALUE=PERIOD:20211101T160000/20211101T + 163000,20211206T160000/20211206T163000,20220103T160000/20220103T163000,202 + 20207T160000/20220207T163000 +ATTENDEE;CN="(omitted)";PARTSTAT=ACCEPTED;ROLE=CHAIR;RSVP=FAL + SE:mailto:omitted@example.com +CLASS:PUBLIC +TRANSP:OPAQUE +X-LOTUS-APPTTYPE:3 +X-LOTUS-AUDIOVIDEOFLAGS:0 +X-LOTUS-BROADCAST:FALSE +X-LOTUS-CHANGE-INST-DATES:20211101T150000Z\,20211206T150000Z\,20220103T150 + 000Z\,20220207T150000Z +X-LOTUS-CHILD-UID:567EFBAF6CBD07FC0025875100566D3B +X-LOTUS-INITIAL-RDATES:20211101T150000Z\,20211206T150000Z\,20220103T150000 + Z\,20220207T150000Z +X-LOTUS-LASTALL-RDATES;TZID="Western/Central Europe":20211101T160000\,2021 + 1206T160000\,20220103T160000\,20220207T160000 +X-LOTUS-NOTESVERSION:2 +X-LOTUS-NOTICETYPE:I +X-LOTUS-RECURID;RANGE=THISANDFUTURE:20211101T150000Z +X-LOTUS-UPDATE-SEQ:2 +X-LOTUS-UPDATE-WISL:$W:1\;$O:1\;$M:1\;RequiredAttendees:1\;INetRequiredNam + es:1\;AltRequiredNames:1\;StorageRequiredNames:1\;OptionalAttendees:1\;INe + tOptionalNames:1\;AltOptionalNames:1\;StorageOptionalNames:1\;ApptUNIDURL: + 1\;STUnyteConferenceURL:1\;STUnyteConferenceID:1\;SametimeType:1\;WhiteBoa + rdContent:1\;STRoomName:1\;$S:2\;$B:2\;$L:2\;$E:2\;$R:2 +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_165_missing_event.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_165_missing_event.ics new file mode 100644 index 0000000..b9675cf --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_165_missing_event.ics @@ -0,0 +1,28 @@ +BEGIN:VCALENDAR +METHOD:REQUEST +PRODID:Microsoft CDO for Microsoft Exchange +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:GMT +0100 (Standard) / GMT +0200 (Daylight) +BEGIN:STANDARD +DTSTART:16010101T030000 +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=10;BYDAY=-1SU +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:16010101T020000 +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +DTSTAMP:20150703T071009Z +DTSTART;TZID="GMT +0100 (Standard) / GMT +0200 (Daylight)":20150703T100000 +SUMMARY:Sprint 25 Daily Standup +DTEND;TZID="GMT +0100 (Standard) / GMT +0200 (Daylight)":20150703T103000 +RRULE:FREQ=DAILY;UNTIL=20150722T080000Z;INTERVAL=1;BYDAY=MO, TU, WE, TH, FR + ;WKST=SU +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_168_expected_output.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_168_expected_output.ics new file mode 100644 index 0000000..16e9249 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_168_expected_output.ics @@ -0,0 +1,7 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +DTSTART:20150905T090000Z +DTEND:20150905T100000Z +UID:123 +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_168_input.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_168_input.ics new file mode 100644 index 0000000..466f8d1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_168_input.ics @@ -0,0 +1,8 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +DTSTART:20150905T090000Z +DTEND:20150905T100000Z +UID:123 +X-APPLE-RADIUS=49.91307046514149 +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_178_component_with_invalid_name_represented.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_178_component_with_invalid_name_represented.ics new file mode 100644 index 0000000..bf93e0a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_178_component_with_invalid_name_represented.ics @@ -0,0 +1,2 @@ +BEGIN:MYCOMP +END:MYCOMP diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_178_custom_component_contains_other.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_178_custom_component_contains_other.ics new file mode 100644 index 0000000..c330e59 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_178_custom_component_contains_other.ics @@ -0,0 +1,7 @@ +BEGIN:MYCOMPTOO +DTSTAMP:20150121T080000 +BEGIN:VEVENT +DTSTART:20150122 +UID:12345 +END:VEVENT +END:MYCOMPTOO diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_178_custom_component_inside_other.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_178_custom_component_inside_other.ics new file mode 100644 index 0000000..f70df39 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_178_custom_component_inside_other.ics @@ -0,0 +1,5 @@ +BEGIN:VCALENDAR +BEGIN:UNKNOWN +UID:1234 +END:UNKNOWN +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_218_bad_tzid.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_218_bad_tzid.ics new file mode 100644 index 0000000..f37af5e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_218_bad_tzid.ics @@ -0,0 +1,24 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//TEST//TEST//EN +BEGIN:VTIMEZONE +TZID:UTC+11 +BEGIN:STANDARD +DTSTART;VALUE=DATE:20170101 +TZNAME:UTC+11 +TZOFFSETFROM:+1100 +TZOFFSETTO:+1100 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DESCRIPTION:TESTING +DTEND;TZID=UTC+11:20170228T233000 +DTSTAMP:20170227T064302Z +DTSTART;TZID=UTC+11:20170228T230000 +RESOURCES:Court 4 +SEQUENCE:0 +STATUS:Confirmed +SUMMARY:TESTIN +UID:1961094_636238800000000000 +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_237_fail_to_parse_timezone_with_non_ascii_tzid.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_237_fail_to_parse_timezone_with_non_ascii_tzid.ics new file mode 100644 index 0000000..9b2e8f6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_237_fail_to_parse_timezone_with_non_ascii_tzid.ics @@ -0,0 +1,23 @@ +BEGIN:VCALENDAR +BEGIN:VTIMEZONE +TZID:(UTC-03:00) Brasília +BEGIN:STANDARD +TZNAME:Brasília standard +DTSTART:16010101T235959 +TZOFFSETFROM:-0200 +TZOFFSETTO:-0300 +RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=3SA;BYMONTH=2 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:Brasília daylight +DTSTART:16010101T235959 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0200 +RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SA;BYMONTH=10 +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +DTSTART;TZID="(UTC-03:00) Brasília":20170511T133000 +DTEND;TZID="(UTC-03:00) Brasília":20170511T140000 +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_27_multiple_periods_in_freebusy_multiple_freebusies.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_27_multiple_periods_in_freebusy_multiple_freebusies.ics new file mode 100644 index 0000000..5986b9f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_27_multiple_periods_in_freebusy_multiple_freebusies.ics @@ -0,0 +1,21 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//davmail.sf.net/NONSGML DavMail Calendar V1.1//EN +METHOD:REPLY +BEGIN:VFREEBUSY +DTSTAMP:20120131T123000Z +ORGANIZER:MAILTO:organizer@domain.tld +DTSTART:20120101T000000Z +DTEND:20120201T000000Z +UID:null +ATTENDEE:MAILTO:attendee@domain.tld +FREEBUSY;FBTYPE=BUSY:20120103T091500Z/20120103T101500Z +FREEBUSY;FBTYPE=BUSY:20120113T130000Z/20120113T150000Z +FREEBUSY;FBTYPE=BUSY:20120116T130000Z/20120116T150000Z +FREEBUSY;FBTYPE=BUSY:20120117T091500Z/20120117T101500Z +FREEBUSY;FBTYPE=BUSY:20120118T160000Z/20120118T163000Z +FREEBUSY;FBTYPE=BUSY:20120124T083000Z/20120124T093000Z +FREEBUSY;FBTYPE=BUSY:20120124T123000Z/20120124T143000Z +FREEBUSY;FBTYPE=BUSY:20120131T091500Z/20120131T101500Z +END:VFREEBUSY +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_27_multiple_periods_in_freebusy_one_freebusy.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_27_multiple_periods_in_freebusy_one_freebusy.ics new file mode 100644 index 0000000..d4f500b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_27_multiple_periods_in_freebusy_one_freebusy.ics @@ -0,0 +1,14 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//davmail.sf.net/NONSGML DavMail Calendar V1.1//EN +METHOD:REPLY +BEGIN:VFREEBUSY +DTSTAMP:20120131T123000Z +ORGANIZER:MAILTO:organizer@domain.tld +DTSTART:20120101T000000Z +DTEND:20120201T000000Z +UID:null +ATTENDEE:MAILTO:attendee@domain.tld +FREEBUSY;FBTYPE=BUSY:20120103T091500Z/20120103T101500Z,20120113T130000Z/20120113T150000Z,20120116T130000Z/20120116T150000Z,20120117T091500Z/20120117T101500Z,20120118T160000Z/20120118T163000Z,20120124T083000Z/20120124T093000Z,20120124T123000Z/20120124T143000Z,20120131T091500Z/20120131T101500Z +END:VFREEBUSY +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_322_expected_calendar.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_322_expected_calendar.ics new file mode 100644 index 0000000..cdfbe6f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_322_expected_calendar.ics @@ -0,0 +1,6 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +SUMMARY:Event with bare string as argument for categories +CATEGORIES:Lecture +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_348_exception_parsing_value.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_348_exception_parsing_value.ics new file mode 100644 index 0000000..51c8309 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_348_exception_parsing_value.ics @@ -0,0 +1,32 @@ +BEGIN:VCALENDAR +CALSCALE:GREGORIAN +METHOD:PUBLISH +VERSION:2.0 +PRODID:-//Sixt//RAC//EN +BEGIN:VFREEBUSY +FREEBUSY;FBTYPE=FREE:20190624T063000Z/20190624T163000Z +ORGANIZER;CN=Sixt SE +X-ORGANIZER2;CN=Sixt SE;CN2=Test! +UID:SIXT_9879691160 +DTSTAMP:20190612T104813Z +END:VFREEBUSY +BEGIN:VEVENT +CLASS:PUBLIC +DESCRIPTION:\nVotre véhicule avec le numéro de réservation xxxxxxxxxxx sera à votre disposition le 24.06.2019 à 08:30 heures (heure locale). Retour prévu : Ferney-Voltaire AP de Genève Cointrin Sect(F), le 24.06.2019 à 18:30 heures.\nVous trouverez des informations relatives à la modification ou à l'annulation de votre réservation sous http://www.sixt.fr\n +DTSTART:20190624T063000Z +DTSTAMP:20190612T104813Z +DTEND:20190624T163000Z +LOCATION:Sixt Genève Aéroport secteur France, AP de Genève Cointrin Sect(F), 01210 Ferney-Voltaire, FR +PRIORITY:5 +TRANSP:TRANSPARENT +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:Sixt : détails de votre réservation +UID:SIXT_9879691160 +BEGIN:VALARM +TRIGGER:-PT30M +ACTION:DISPLAY +DESCRIPTION:\nLe véhicule avec la n° de réservation xxxxxxxxxxx sera à votre disposition entre 24.06.2019 et 08:30 heures (heure locale) à l'agence Genève Aéroport secteur France. Retour prévu : Ferney-Voltaire AP de Genève Cointrin Sect(F), le 24.06.2019 à 18:30 heures.\nTrouver des informations de localisation et modifier / annuler votre réservation ici: http://www.sixt.fr\n +END:VALARM +END:VEVENT +END:VCALENDAR diff --git a/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_350.ics b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_350.ics new file mode 100644 index 0000000..a66f737 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/icalendar/tests/calendars/issue_350.ics @@ -0,0 +1,36 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Podio API//EN//view-exporter//52593453 +METHOD:REQUEST +X-WR-CALNAME:view52593586 +X-WR-TIMEZONE:Europe/Berlin +CALSCALE:GREGORIAN +X-COMMENT-USAGE:This calendar does not contain recurring events! +X-COMMENT-GENERATOR:PHP Version 8.0.16 +BEGIN:VEVENT +STATUS:CONFIRMED +SEQUENCE:0 +TRANSP:OPAQUE +CLASS:PUBLIC +UID:20055546456446 +SUMMARY:Termin 4353 und"so" +DESCRIPTION;ALTREP="data:text/html,%3Cbody%3E%3Cp%3EToller%20%3Cstron + g%20class%3D%22text-bold%22%3ETermin%3C%2Fstrong%3E%20f%C3%BCr%3C%2Fp + %3E%3Cblockquote%3E%3Cp%3Emal%20%3Cem%20class%3D%22text-italic%22%3Ez + u%22gucken%22%3C%2Fem%3E%3C%2Fp%3E%3C%2Fblockquote%3E%3Cp%3E%3Cu%20cl + ass%3D%22text-underline%22%3Eund%3C%2Fu%3E%20%3Cdel%3Eso%3C%2Fdel%3E% + 3Cbr%2F%3E%3C%2Fp%3E%3C%2Fbody%3E":Toller Termin fürmal zu\"gucken\" + und so +X-ALT-DESC;FMTTYPE=text/html:

  • {html_escape(file_name)}