diff --git a/CHANGELOG.md b/CHANGELOG.md index 12029a2..3f21b54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ ## Unreleased +## 1.6.0 +### Added +- add regctl normalization schema + + ## 1.5.2 ### Fixed - issue introduced in 1.5.1 that could cause some contact points to be lost diff --git a/CHANGELOG.yaml b/CHANGELOG.yaml index b49935b..875cf03 100644 --- a/CHANGELOG.yaml +++ b/CHANGELOG.yaml @@ -5,6 +5,9 @@ Unreleased: deprecated: [] removed: [] security: [] +1.6.0: + added: + - add regctl normalization schema 1.5.2: fixed: - issue introduced in 1.5.1 that could cause some contact points to be lost diff --git a/Ctl/VERSION b/Ctl/VERSION index a73b432..ce6a70b 100644 --- a/Ctl/VERSION +++ b/Ctl/VERSION @@ -1 +1 @@ -1.5.2 \ No newline at end of file +1.6.0 \ No newline at end of file diff --git a/Ctl/config.yml b/Ctl/config.yml index 7166655..32f15cd 100644 --- a/Ctl/config.yml +++ b/Ctl/config.yml @@ -20,7 +20,7 @@ ctl: sign: true - name: version - type: version + type: semver2 config: branch_dev: main branch_release: main diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..03868c7 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,116 @@ +# API + +## Retrieve objects + +```python +import rdap + +# instantiate client +client = rdap.RdapClient() + +# asn +as63311 = client.get_asn(63311) + +# domain +domain = client.get_domain('example.com') + +# ip +ip = client.get_ip('206.41.110.0') + +# entity +entity = client.get_entity('NETWO7047-ARIN') +``` + +## Output normalized data (ASN example) + +```python +import json +import rdap + +# instantiate client +client = rdap.RdapClient() + +# request asn +as63311 = client.get_asn(63311) + +# normalized dict +print(json.dumps(as63311.normalized, indent=2)) +``` + +Output: +```json +{ + "created": "2014-11-17T14:28:43-05:00", + "updated": "2018-10-24T22:58:16-04:00", + "asn": 63311, + "name": "20C", + "organization": { + "name": "20C, LLC" + }, + "locations": [ + { + "updated": "2014-08-05T15:21:11-04:00", + "country": null, + "city": null, + "postal_code": null, + "address": "303 W Ohio #1701\nChicago\nIL\n60654\nUnited States", + "geo": null, + "floor": null, + "suite": null + }, + { + "updated": "2023-08-02T14:15:09-04:00", + "country": null, + "city": null, + "postal_code": null, + "address": "603 Discovery Dr\nWest Chicago\nIL\n60185\nUnited States", + "geo": null, + "floor": null, + "suite": null + } + ], + "contacts": [ + { + "created": "2014-07-03T23:22:49-04:00", + "updated": "2023-08-02T14:15:09-04:00", + "name": "Network Engineers", + "roles": [ + "abuse", + "admin", + "technical" + ], + "phone": "+1 978-636-0020", + "email": "neteng@20c.com" + } + ], + "sources": [ + { + "created": "2014-11-17T14:28:43-05:00", + "updated": "2018-10-24T22:58:16-04:00", + "handle": "AS63311", + "urls": [ + "https://rdap.org/autnum/63311", + "https://rdap.arin.net/registry/autnum/63311" + ], + "description": null + } + ] +} +``` + +## Work with normalized data through pydantic models + +```python +import rdap + +from rdap.schema.normalized import Network + +# instantiate client +client = rdap.RdapClient() + +# request asn +as63311 = Network(**client.get_asn(63311).normalized) + +for contact in as63311.contacts: + print(contact.name, contact.email) +``` \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 062a500..7633dec 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,35 +1,48 @@ -# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} [[package]] name = "black" -version = "24.3.0" +version = "24.8.0" description = "The uncompromising code formatter." -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "black-24.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"}, - {file = "black-24.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"}, - {file = "black-24.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"}, - {file = "black-24.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0"}, - {file = "black-24.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9"}, - {file = "black-24.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597"}, - {file = "black-24.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d"}, - {file = "black-24.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5"}, - {file = "black-24.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f"}, - {file = "black-24.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11"}, - {file = "black-24.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4"}, - {file = "black-24.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5"}, - {file = "black-24.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837"}, - {file = "black-24.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd"}, - {file = "black-24.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213"}, - {file = "black-24.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959"}, - {file = "black-24.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb"}, - {file = "black-24.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7"}, - {file = "black-24.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7"}, - {file = "black-24.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f"}, - {file = "black-24.3.0-py3-none-any.whl", hash = "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93"}, - {file = "black-24.3.0.tar.gz", hash = "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f"}, + {file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"}, + {file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"}, + {file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"}, + {file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"}, + {file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"}, + {file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"}, + {file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"}, + {file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"}, + {file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"}, + {file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"}, + {file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"}, + {file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"}, + {file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"}, + {file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"}, + {file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"}, + {file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"}, + {file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"}, + {file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"}, + {file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"}, + {file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"}, + {file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"}, + {file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"}, ] [package.dependencies] @@ -49,33 +62,30 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "cachetools" -version = "5.3.3" +version = "5.5.0" description = "Extensible memoizing collections and decorators" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, - {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, + {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, + {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, ] [[package]] name = "certifi" -version = "2024.2.2" +version = "2024.8.30" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] [[package]] name = "cfgv" version = "3.4.0" description = "Validate configuration and produce human readable error messages." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -87,7 +97,6 @@ files = [ name = "chardet" version = "5.2.0" description = "Universal encoding detector for Python 3" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -99,7 +108,6 @@ files = [ name = "charset-normalizer" version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -199,7 +207,6 @@ files = [ name = "click" version = "8.1.7" description = "Composable command line interface toolkit" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -214,7 +221,6 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -224,64 +230,83 @@ files = [ [[package]] name = "coverage" -version = "7.4.4" +version = "7.6.1" description = "Code coverage measurement for Python" -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"}, - {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"}, - {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"}, - {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"}, - {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"}, - {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"}, - {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"}, - {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"}, - {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"}, - {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"}, - {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"}, - {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"}, - {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"}, - {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"}, - {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"}, - {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"}, - {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"}, - {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"}, - {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"}, - {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"}, - {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"}, - {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"}, + {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"}, + {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"}, + {file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"}, + {file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, + {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, + {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, + {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, + {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, + {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, + {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, + {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, + {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, + {file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"}, + {file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"}, + {file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"}, + {file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"}, + {file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"}, + {file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"}, + {file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"}, + {file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"}, + {file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"}, + {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, ] [package.dependencies] @@ -292,21 +317,19 @@ toml = ["tomli"] [[package]] name = "decorator" -version = "4.4.2" +version = "5.1.1" description = "Decorators for Humans" -category = "dev" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*" +python-versions = ">=3.5" files = [ - {file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"}, - {file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"}, + {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, + {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, ] [[package]] name = "distlib" version = "0.3.8" description = "Distribution utilities" -category = "dev" optional = false python-versions = "*" files = [ @@ -316,14 +339,13 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.2.0" +version = "1.2.2" description = "Backport of PEP 654 (exception groups)" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, - {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, ] [package.extras] @@ -331,26 +353,24 @@ test = ["pytest (>=6)"] [[package]] name = "filelock" -version = "3.13.1" +version = "3.16.1" description = "A platform independent file lock." -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, - {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, + {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, + {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] -typing = ["typing-extensions (>=4.8)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] +typing = ["typing-extensions (>=4.12.2)"] [[package]] name = "flake8" version = "5.0.4" description = "the modular source code checker: pep8 pyflakes and co" -category = "dev" optional = false python-versions = ">=3.6.1" files = [ @@ -363,22 +383,10 @@ mccabe = ">=0.7.0,<0.8.0" pycodestyle = ">=2.9.0,<2.10.0" pyflakes = ">=2.5.0,<2.6.0" -[[package]] -name = "future" -version = "0.18.3" -description = "Clean single-source support for Python 3 and 2" -category = "dev" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "future-0.18.3.tar.gz", hash = "sha256:34a17436ed1e96697a86f9de3d15a3b0be01d8bc8de9c1dffd59fb8234ed5307"}, -] - [[package]] name = "ghp-import" version = "2.1.0" description = "Copy your docs directly to the gh-pages branch." -category = "dev" optional = false python-versions = "*" files = [ @@ -392,16 +400,28 @@ python-dateutil = ">=2.8.1" [package.extras] dev = ["flake8", "markdown", "twine", "wheel"] +[[package]] +name = "googlemaps" +version = "4.10.0" +description = "Python client library for Google Maps Platform" +optional = false +python-versions = ">=3.5" +files = [ + {file = "googlemaps-4.10.0.tar.gz", hash = "sha256:3055fcbb1aa262a9159b589b5e6af762b10e80634ae11c59495bd44867e47d88"}, +] + +[package.dependencies] +requests = ">=2.20.0,<3.0" + [[package]] name = "identify" -version = "2.5.35" +version = "2.6.1" description = "File identification library for Python" -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"}, - {file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"}, + {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, + {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, ] [package.extras] @@ -409,41 +429,45 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.6" +version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, - {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, ] +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + [[package]] name = "importlib-metadata" -version = "7.1.0" +version = "8.5.0" description = "Read metadata from Python packages" -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, - {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, + {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, + {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, ] [package.dependencies] -zipp = ">=0.5" +zipp = ">=3.20" [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -455,7 +479,6 @@ files = [ name = "isort" version = "5.13.2" description = "A Python utility / library to sort Python imports." -category = "dev" optional = false python-versions = ">=3.8.0" files = [ @@ -468,14 +491,13 @@ colors = ["colorama (>=0.4.6)"] [[package]] name = "jinja2" -version = "3.1.3" +version = "3.1.4" description = "A very fast and expressive template engine." -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, - {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, ] [package.dependencies] @@ -486,14 +508,13 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "markdown" -version = "3.6" +version = "3.7" description = "Python implementation of John Gruber's Markdown." -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "Markdown-3.6-py3-none-any.whl", hash = "sha256:48f276f4d8cfb8ce6527c8f79e2ee29708508bf4d40aa410fbc3b4ee832c850f"}, - {file = "Markdown-3.6.tar.gz", hash = "sha256:ed4f41f6daecbeeb96e576ce414c41d2d876daa9a16cb35fa8ed8c2ddfad0224"}, + {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, + {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, ] [package.dependencies] @@ -507,7 +528,6 @@ testing = ["coverage", "pyyaml"] name = "markdown-include" version = "0.8.1" description = "A Python-Markdown extension which provides an 'include' function" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -525,7 +545,6 @@ tests = ["pytest"] name = "markupsafe" version = "2.1.5" description = "Safely add untrusted strings to HTML/XML markup." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -595,7 +614,6 @@ files = [ name = "mccabe" version = "0.7.0" description = "McCabe checker, plugin for flake8" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -607,7 +625,6 @@ files = [ name = "mergedeep" version = "1.3.4" description = "A deep merge function for 🐍." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -617,41 +634,56 @@ files = [ [[package]] name = "mkdocs" -version = "1.5.3" +version = "1.6.1" description = "Project documentation with Markdown." -category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "mkdocs-1.5.3-py3-none-any.whl", hash = "sha256:3b3a78e736b31158d64dbb2f8ba29bd46a379d0c6e324c2246c3bc3d2189cfc1"}, - {file = "mkdocs-1.5.3.tar.gz", hash = "sha256:eb7c99214dcb945313ba30426c2451b735992c73c2e10838f76d09e39ff4d0e2"}, + {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, + {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, ] [package.dependencies] click = ">=7.0" colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} ghp-import = ">=1.0" -importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""} +importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} jinja2 = ">=2.11.1" -markdown = ">=3.2.1" +markdown = ">=3.3.6" markupsafe = ">=2.0.1" mergedeep = ">=1.3.4" +mkdocs-get-deps = ">=0.2.0" packaging = ">=20.5" pathspec = ">=0.11.1" -platformdirs = ">=2.2.0" pyyaml = ">=5.1" pyyaml-env-tag = ">=0.1" watchdog = ">=2.0" [package.extras] i18n = ["babel (>=2.9.0)"] -min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.3)", "jinja2 (==2.11.1)", "markdown (==3.2.1)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "packaging (==20.5)", "pathspec (==0.11.1)", "platformdirs (==2.2.0)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "typing-extensions (==3.10)", "watchdog (==2.0)"] +min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.4)", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.0" +description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, + {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""} +mergedeep = ">=1.3.4" +platformdirs = ">=2.2.0" +pyyaml = ">=5.1" [[package]] name = "munge" version = "1.3.0" description = "data manipulation library and client" -category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -671,45 +703,44 @@ yaml = ["PyYAML (>=5.1)"] [[package]] name = "mypy" -version = "1.9.0" +version = "1.11.2" description = "Optional static typing for Python" -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"}, - {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"}, - {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"}, - {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"}, - {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"}, - {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"}, - {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"}, - {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"}, - {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"}, - {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"}, - {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"}, - {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"}, - {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"}, - {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"}, - {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"}, - {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"}, - {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"}, - {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"}, - {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"}, - {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"}, - {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"}, - {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"}, - {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"}, - {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"}, - {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"}, - {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"}, - {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"}, + {file = "mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a"}, + {file = "mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef"}, + {file = "mypy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41ea707d036a5307ac674ea172875f40c9d55c5394f888b168033177fce47383"}, + {file = "mypy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6e658bd2d20565ea86da7d91331b0eed6d2eee22dc031579e6297f3e12c758c8"}, + {file = "mypy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:478db5f5036817fe45adb7332d927daa62417159d49783041338921dcf646fc7"}, + {file = "mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385"}, + {file = "mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca"}, + {file = "mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104"}, + {file = "mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4"}, + {file = "mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6"}, + {file = "mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318"}, + {file = "mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36"}, + {file = "mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987"}, + {file = "mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca"}, + {file = "mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70"}, + {file = "mypy-1.11.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:37c7fa6121c1cdfcaac97ce3d3b5588e847aa79b580c1e922bb5d5d2902df19b"}, + {file = "mypy-1.11.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a8a53bc3ffbd161b5b2a4fff2f0f1e23a33b0168f1c0778ec70e1a3d66deb86"}, + {file = "mypy-1.11.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ff93107f01968ed834f4256bc1fc4475e2fecf6c661260066a985b52741ddce"}, + {file = "mypy-1.11.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:edb91dded4df17eae4537668b23f0ff6baf3707683734b6a818d5b9d0c0c31a1"}, + {file = "mypy-1.11.2-cp38-cp38-win_amd64.whl", hash = "sha256:ee23de8530d99b6db0573c4ef4bd8f39a2a6f9b60655bf7a1357e585a3486f2b"}, + {file = "mypy-1.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:801ca29f43d5acce85f8e999b1e431fb479cb02d0e11deb7d2abb56bdaf24fd6"}, + {file = "mypy-1.11.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af8d155170fcf87a2afb55b35dc1a0ac21df4431e7d96717621962e4b9192e70"}, + {file = "mypy-1.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7821776e5c4286b6a13138cc935e2e9b6fde05e081bdebf5cdb2bb97c9df81d"}, + {file = "mypy-1.11.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539c570477a96a4e6fb718b8d5c3e0c0eba1f485df13f86d2970c91f0673148d"}, + {file = "mypy-1.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:3f14cd3d386ac4d05c5a39a51b84387403dadbd936e17cb35882134d4f8f0d24"}, + {file = "mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12"}, + {file = "mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79"}, ] [package.dependencies] mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=4.1.0" +typing-extensions = ">=4.6.0" [package.extras] dmypy = ["psutil (>=4.0)"] @@ -721,7 +752,6 @@ reports = ["lxml"] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -731,36 +761,30 @@ files = [ [[package]] name = "nodeenv" -version = "1.8.0" +version = "1.9.1" description = "Node.js virtual environment builder" -category = "dev" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ - {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, - {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, ] -[package.dependencies] -setuptools = "*" - [[package]] name = "packaging" -version = "24.0" +version = "24.1" description = "Core utilities for Python packages" -category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] name = "pathspec" version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -768,32 +792,42 @@ files = [ {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] +[[package]] +name = "phonenumbers" +version = "8.13.46" +description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers." +optional = false +python-versions = "*" +files = [ + {file = "phonenumbers-8.13.46-py2.py3-none-any.whl", hash = "sha256:519422d407af066fdbf98e179ea2e214487060f26526d67871f817eefbbb2134"}, + {file = "phonenumbers-8.13.46.tar.gz", hash = "sha256:94bf18ba9725bb6868d29473b13f78ef01e2585c5cb561ec0200be7676e77452"}, +] + [[package]] name = "platformdirs" -version = "4.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" +version = "4.3.6" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, - {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] [[package]] name = "pluggy" -version = "1.4.0" +version = "1.5.0" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, - {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] @@ -804,7 +838,6 @@ testing = ["pytest", "pytest-benchmark"] name = "pre-commit" version = "3.5.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -823,7 +856,6 @@ virtualenv = ">=20.10.0" name = "pycodestyle" version = "2.9.1" description = "Python style guide checker" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -831,11 +863,134 @@ files = [ {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, ] +[[package]] +name = "pydantic" +version = "2.9.2" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, + {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.23.4" +typing-extensions = [ + {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, + {version = ">=4.6.1", markers = "python_version < \"3.13\""}, +] + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] + +[[package]] +name = "pydantic-core" +version = "2.23.4" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, + {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, + {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, + {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, + {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, + {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, + {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, + {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, + {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, + {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, + {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, + {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, + {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, + {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + [[package]] name = "pyflakes" version = "2.5.0" description = "passive checker of Python programs" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -845,34 +1000,32 @@ files = [ [[package]] name = "pyproject-api" -version = "1.6.1" +version = "1.8.0" description = "API to interact with the python pyproject.toml based projects" -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "pyproject_api-1.6.1-py3-none-any.whl", hash = "sha256:4c0116d60476b0786c88692cf4e325a9814965e2469c5998b830bba16b183675"}, - {file = "pyproject_api-1.6.1.tar.gz", hash = "sha256:1817dc018adc0d1ff9ca1ed8c60e1623d5aaca40814b953af14a9cf9a5cae538"}, + {file = "pyproject_api-1.8.0-py3-none-any.whl", hash = "sha256:3d7d347a047afe796fd5d1885b1e391ba29be7169bd2f102fcd378f04273d228"}, + {file = "pyproject_api-1.8.0.tar.gz", hash = "sha256:77b8049f2feb5d33eefcc21b57f1e279636277a8ac8ad6b5871037b243778496"}, ] [package.dependencies] -packaging = ">=23.1" +packaging = ">=24.1" tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} [package.extras] -docs = ["furo (>=2023.8.19)", "sphinx (<7.2)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "setuptools (>=68.1.2)", "wheel (>=0.41.2)"] +docs = ["furo (>=2024.8.6)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "pytest (>=8.3.3)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "setuptools (>=75.1)"] [[package]] name = "pytest" -version = "8.1.1" +version = "8.3.3" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, - {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, + {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, + {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, ] [package.dependencies] @@ -880,22 +1033,21 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.4,<2.0" +pluggy = ">=1.5,<2" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-cov" -version = "4.1.0" +version = "5.0.0" description = "Pytest plugin for measuring coverage." -category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, - {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, + {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, + {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, ] [package.dependencies] @@ -903,18 +1055,17 @@ coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] -testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-django" -version = "4.8.0" +version = "4.9.0" description = "A Django plugin for pytest." -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-django-4.8.0.tar.gz", hash = "sha256:5d054fe011c56f3b10f978f41a8efb2e5adfc7e680ef36fb571ada1f24779d90"}, - {file = "pytest_django-4.8.0-py3-none-any.whl", hash = "sha256:ca1ddd1e0e4c227cf9e3e40a6afc6d106b3e70868fd2ac5798a22501271cd0c7"}, + {file = "pytest_django-4.9.0-py3-none-any.whl", hash = "sha256:1d83692cb39188682dbb419ff0393867e9904094a549a7d38a3154d5731b2b99"}, + {file = "pytest_django-4.9.0.tar.gz", hash = "sha256:8bf7bc358c9ae6f6fc51b6cebb190fe20212196e6807121f11bd6a3b03428314"}, ] [package.dependencies] @@ -926,25 +1077,23 @@ testing = ["Django", "django-configurations (>=2.0)"] [[package]] name = "pytest-filedata" -version = "0.4.0" -description = "easily load data from files" -category = "dev" +version = "1.0.0" +description = "easily load test data from files" optional = false -python-versions = "*" +python-versions = "<4.0,>=3.8" files = [ - {file = "pytest_filedata-0.4.0.tar.gz", hash = "sha256:3a2a3f346087ac82dfd313212cd2d61c5fcfd23b0aecaa2484e6c31cfcb32fd5"}, + {file = "pytest_filedata-1.0.0-py3-none-any.whl", hash = "sha256:95d624047546eb96871c19f4b690d23c6920da942e59e97f3de24ad0b81400da"}, + {file = "pytest_filedata-1.0.0.tar.gz", hash = "sha256:b92e62832ee29797725961808590ca74c634dfdd5ae297ac0fed261cd3170d03"}, ] [package.dependencies] -decorator = ">=4,<5" -future = ">=0.16.0,<1" -requests-mock = ">=1,<2" +decorator = ">=4" +requests-mock = ">=1" [[package]] name = "python-dateutil" version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" -category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -959,7 +1108,6 @@ six = ">=1.5" name = "pyupgrade" version = "3.8.0" description = "A tool to automatically upgrade syntax for newer versions." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -972,69 +1120,70 @@ tokenize-rt = ">=3.2.0" [[package]] name = "pyyaml" -version = "6.0.1" +version = "6.0.2" description = "YAML parser and emitter for Python" -category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, - {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, - {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, - {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] [[package]] name = "pyyaml-env-tag" version = "0.1" description = "A custom YAML tag for referencing environment variables in YAML files. " -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1047,14 +1196,13 @@ pyyaml = "*" [[package]] name = "requests" -version = "2.31.0" +version = "2.32.3" description = "Python HTTP for Humans." -category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] @@ -1069,46 +1217,25 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "requests-mock" -version = "1.11.0" +version = "1.12.1" description = "Mock out responses from the requests package" -category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.5" files = [ - {file = "requests-mock-1.11.0.tar.gz", hash = "sha256:ef10b572b489a5f28e09b708697208c4a3b2b89ef80a9f01584340ea357ec3c4"}, - {file = "requests_mock-1.11.0-py2.py3-none-any.whl", hash = "sha256:f7fae383f228633f6bececebdab236c478ace2284d6292c6e7e2867b9ab74d15"}, + {file = "requests-mock-1.12.1.tar.gz", hash = "sha256:e9e12e333b525156e82a3c852f22016b9158220d2f47454de9cae8a77d371401"}, + {file = "requests_mock-1.12.1-py2.py3-none-any.whl", hash = "sha256:b1e37054004cdd5e56c84454cc7df12b25f90f382159087f4b6915aaeef39563"}, ] [package.dependencies] -requests = ">=2.3,<3" -six = "*" +requests = ">=2.22,<3" [package.extras] fixture = ["fixtures"] -test = ["fixtures", "mock", "purl", "pytest", "requests-futures", "sphinx", "testtools"] - -[[package]] -name = "setuptools" -version = "69.2.0" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "dev" -optional = false -python-versions = ">=3.8" -files = [ - {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, - {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1118,21 +1245,19 @@ files = [ [[package]] name = "tokenize-rt" -version = "5.2.0" +version = "6.0.0" description = "A wrapper around the stdlib `tokenize` which roundtrips." -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "tokenize_rt-5.2.0-py2.py3-none-any.whl", hash = "sha256:b79d41a65cfec71285433511b50271b05da3584a1da144a0752e9c621a285289"}, - {file = "tokenize_rt-5.2.0.tar.gz", hash = "sha256:9fe80f8a5c1edad2d3ede0f37481cc0cc1538a2f442c9c2f9e4feacd2792d054"}, + {file = "tokenize_rt-6.0.0-py2.py3-none-any.whl", hash = "sha256:d4ff7ded2873512938b4f8cbb98c9b07118f01d30ac585a30d7a88353ca36d22"}, + {file = "tokenize_rt-6.0.0.tar.gz", hash = "sha256:b9711bdfc51210211137499b5e355d3de5ec88a85d2025c520cbb921b5194367"}, ] [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1142,37 +1267,35 @@ files = [ [[package]] name = "tox" -version = "4.14.1" +version = "4.20.0" description = "tox is a generic virtualenv management and test command line tool" -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "tox-4.14.1-py3-none-any.whl", hash = "sha256:b03754b6ee6dadc70f2611da82b4ed8f625fcafd247e15d1d0cb056f90a06d3b"}, - {file = "tox-4.14.1.tar.gz", hash = "sha256:f0ad758c3bbf7e237059c929d3595479363c3cdd5a06ac3e49d1dd020ffbee45"}, + {file = "tox-4.20.0-py3-none-any.whl", hash = "sha256:21a8005e3d3fe5658a8e36b8ca3ed13a4230429063c5cc2a2fdac6ee5aa0de34"}, + {file = "tox-4.20.0.tar.gz", hash = "sha256:5b78a49b6eaaeab3ae4186415e7c97d524f762ae967c63562687c3e5f0ec23d5"}, ] [package.dependencies] -cachetools = ">=5.3.2" +cachetools = ">=5.5" chardet = ">=5.2" colorama = ">=0.4.6" -filelock = ">=3.13.1" -packaging = ">=23.2" -platformdirs = ">=4.1" -pluggy = ">=1.3" -pyproject-api = ">=1.6.1" +filelock = ">=3.15.4" +packaging = ">=24.1" +platformdirs = ">=4.2.2" +pluggy = ">=1.5" +pyproject-api = ">=1.7.1" tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} -virtualenv = ">=20.25" +virtualenv = ">=20.26.3" [package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-argparse-cli (>=1.11.1)", "sphinx-autodoc-typehints (>=1.25.2)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.11)"] -testing = ["build[virtualenv] (>=1.0.3)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.2)", "devpi-process (>=1)", "diff-cover (>=8.0.2)", "distlib (>=0.3.8)", "flaky (>=3.7)", "hatch-vcs (>=0.4)", "hatchling (>=1.21)", "psutil (>=5.9.7)", "pytest (>=7.4.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-xdist (>=3.5)", "re-assert (>=1.1)", "time-machine (>=2.13)", "wheel (>=0.42)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-argparse-cli (>=1.17)", "sphinx-autodoc-typehints (>=2.4)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=24.8)"] +testing = ["build[virtualenv] (>=1.2.2)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.2)", "devpi-process (>=1)", "diff-cover (>=9.1.1)", "distlib (>=0.3.8)", "flaky (>=3.8.1)", "hatch-vcs (>=0.4)", "hatchling (>=1.25)", "psutil (>=6)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-xdist (>=3.6.1)", "re-assert (>=1.1)", "setuptools (>=74.1.2)", "time-machine (>=2.15)", "wheel (>=0.44)"] [[package]] name = "tox-gh-actions" version = "3.2.0" description = "Seamless integration of tox into GitHub Actions" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1188,26 +1311,24 @@ testing = ["black", "devpi-process", "flake8 (>=6,<7)", "mypy", "pytest (>=7,<8) [[package]] name = "typing-extensions" -version = "4.10.0" +version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, - {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] name = "urllib3" -version = "2.2.1" +version = "2.2.3" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, - {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, ] [package.extras] @@ -1218,14 +1339,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.25.1" +version = "20.26.6" description = "Virtual Python Environment builder" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"}, - {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"}, + {file = "virtualenv-20.26.6-py3-none-any.whl", hash = "sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2"}, + {file = "virtualenv-20.26.6.tar.gz", hash = "sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48"}, ] [package.dependencies] @@ -1234,46 +1354,51 @@ filelock = ">=3.12.2,<4" platformdirs = ">=3.9.1,<5" [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [[package]] name = "watchdog" -version = "4.0.0" +version = "4.0.2" description = "Filesystem events monitoring" -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:39cb34b1f1afbf23e9562501673e7146777efe95da24fab5707b88f7fb11649b"}, - {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c522392acc5e962bcac3b22b9592493ffd06d1fc5d755954e6be9f4990de932b"}, - {file = "watchdog-4.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6c47bdd680009b11c9ac382163e05ca43baf4127954c5f6d0250e7d772d2b80c"}, - {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8350d4055505412a426b6ad8c521bc7d367d1637a762c70fdd93a3a0d595990b"}, - {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c17d98799f32e3f55f181f19dd2021d762eb38fdd381b4a748b9f5a36738e935"}, - {file = "watchdog-4.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4986db5e8880b0e6b7cd52ba36255d4793bf5cdc95bd6264806c233173b1ec0b"}, - {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:11e12fafb13372e18ca1bbf12d50f593e7280646687463dd47730fd4f4d5d257"}, - {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5369136a6474678e02426bd984466343924d1df8e2fd94a9b443cb7e3aa20d19"}, - {file = "watchdog-4.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76ad8484379695f3fe46228962017a7e1337e9acadafed67eb20aabb175df98b"}, - {file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:45cc09cc4c3b43fb10b59ef4d07318d9a3ecdbff03abd2e36e77b6dd9f9a5c85"}, - {file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eed82cdf79cd7f0232e2fdc1ad05b06a5e102a43e331f7d041e5f0e0a34a51c4"}, - {file = "watchdog-4.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba30a896166f0fee83183cec913298151b73164160d965af2e93a20bbd2ab605"}, - {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d18d7f18a47de6863cd480734613502904611730f8def45fc52a5d97503e5101"}, - {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2895bf0518361a9728773083908801a376743bcc37dfa252b801af8fd281b1ca"}, - {file = "watchdog-4.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87e9df830022488e235dd601478c15ad73a0389628588ba0b028cb74eb72fed8"}, - {file = "watchdog-4.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6e949a8a94186bced05b6508faa61b7adacc911115664ccb1923b9ad1f1ccf7b"}, - {file = "watchdog-4.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6a4db54edea37d1058b08947c789a2354ee02972ed5d1e0dca9b0b820f4c7f92"}, - {file = "watchdog-4.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d31481ccf4694a8416b681544c23bd271f5a123162ab603c7d7d2dd7dd901a07"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8fec441f5adcf81dd240a5fe78e3d83767999771630b5ddfc5867827a34fa3d3"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:6a9c71a0b02985b4b0b6d14b875a6c86ddea2fdbebd0c9a720a806a8bbffc69f"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:557ba04c816d23ce98a06e70af6abaa0485f6d94994ec78a42b05d1c03dcbd50"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d0f9bd1fd919134d459d8abf954f63886745f4660ef66480b9d753a7c9d40927"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:f9b2fdca47dc855516b2d66eef3c39f2672cbf7e7a42e7e67ad2cbfcd6ba107d"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:73c7a935e62033bd5e8f0da33a4dcb763da2361921a69a5a95aaf6c93aa03a87"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6a80d5cae8c265842c7419c560b9961561556c4361b297b4c431903f8c33b269"}, - {file = "watchdog-4.0.0-py3-none-win32.whl", hash = "sha256:8f9a542c979df62098ae9c58b19e03ad3df1c9d8c6895d96c0d51da17b243b1c"}, - {file = "watchdog-4.0.0-py3-none-win_amd64.whl", hash = "sha256:f970663fa4f7e80401a7b0cbeec00fa801bf0287d93d48368fc3e6fa32716245"}, - {file = "watchdog-4.0.0-py3-none-win_ia64.whl", hash = "sha256:9a03e16e55465177d416699331b0f3564138f1807ecc5f2de9d55d8f188d08c7"}, - {file = "watchdog-4.0.0.tar.gz", hash = "sha256:e3e7065cbdabe6183ab82199d7a4f6b3ba0a438c5a512a68559846ccb76a78ec"}, + {file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ede7f010f2239b97cc79e6cb3c249e72962404ae3865860855d5cbe708b0fd22"}, + {file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a2cffa171445b0efa0726c561eca9a27d00a1f2b83846dbd5a4f639c4f8ca8e1"}, + {file = "watchdog-4.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c50f148b31b03fbadd6d0b5980e38b558046b127dc483e5e4505fcef250f9503"}, + {file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c7d4bf585ad501c5f6c980e7be9c4f15604c7cc150e942d82083b31a7548930"}, + {file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:914285126ad0b6eb2258bbbcb7b288d9dfd655ae88fa28945be05a7b475a800b"}, + {file = "watchdog-4.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:984306dc4720da5498b16fc037b36ac443816125a3705dfde4fd90652d8028ef"}, + {file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1cdcfd8142f604630deef34722d695fb455d04ab7cfe9963055df1fc69e6727a"}, + {file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d7ab624ff2f663f98cd03c8b7eedc09375a911794dfea6bf2a359fcc266bff29"}, + {file = "watchdog-4.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:132937547a716027bd5714383dfc40dc66c26769f1ce8a72a859d6a48f371f3a"}, + {file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd67c7df93eb58f360c43802acc945fa8da70c675b6fa37a241e17ca698ca49b"}, + {file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcfd02377be80ef3b6bc4ce481ef3959640458d6feaae0bd43dd90a43da90a7d"}, + {file = "watchdog-4.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:980b71510f59c884d684b3663d46e7a14b457c9611c481e5cef08f4dd022eed7"}, + {file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:aa160781cafff2719b663c8a506156e9289d111d80f3387cf3af49cedee1f040"}, + {file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f6ee8dedd255087bc7fe82adf046f0b75479b989185fb0bdf9a98b612170eac7"}, + {file = "watchdog-4.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0b4359067d30d5b864e09c8597b112fe0a0a59321a0f331498b013fb097406b4"}, + {file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:770eef5372f146997638d737c9a3c597a3b41037cfbc5c41538fc27c09c3a3f9"}, + {file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eeea812f38536a0aa859972d50c76e37f4456474b02bd93674d1947cf1e39578"}, + {file = "watchdog-4.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b2c45f6e1e57ebb4687690c05bc3a2c1fb6ab260550c4290b8abb1335e0fd08b"}, + {file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:10b6683df70d340ac3279eff0b2766813f00f35a1d37515d2c99959ada8f05fa"}, + {file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7c739888c20f99824f7aa9d31ac8a97353e22d0c0e54703a547a218f6637eb3"}, + {file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c100d09ac72a8a08ddbf0629ddfa0b8ee41740f9051429baa8e31bb903ad7508"}, + {file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:f5315a8c8dd6dd9425b974515081fc0aadca1d1d61e078d2246509fd756141ee"}, + {file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2d468028a77b42cc685ed694a7a550a8d1771bb05193ba7b24006b8241a571a1"}, + {file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f15edcae3830ff20e55d1f4e743e92970c847bcddc8b7509bcd172aa04de506e"}, + {file = "watchdog-4.0.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:936acba76d636f70db8f3c66e76aa6cb5136a936fc2a5088b9ce1c7a3508fc83"}, + {file = "watchdog-4.0.2-py3-none-manylinux2014_armv7l.whl", hash = "sha256:e252f8ca942a870f38cf785aef420285431311652d871409a64e2a0a52a2174c"}, + {file = "watchdog-4.0.2-py3-none-manylinux2014_i686.whl", hash = "sha256:0e83619a2d5d436a7e58a1aea957a3c1ccbf9782c43c0b4fed80580e5e4acd1a"}, + {file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64.whl", hash = "sha256:88456d65f207b39f1981bf772e473799fcdc10801062c36fd5ad9f9d1d463a73"}, + {file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:32be97f3b75693a93c683787a87a0dc8db98bb84701539954eef991fb35f5fbc"}, + {file = "watchdog-4.0.2-py3-none-manylinux2014_s390x.whl", hash = "sha256:c82253cfc9be68e3e49282831afad2c1f6593af80c0daf1287f6a92657986757"}, + {file = "watchdog-4.0.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c0b14488bd336c5b1845cee83d3e631a1f8b4e9c5091ec539406e4a324f882d8"}, + {file = "watchdog-4.0.2-py3-none-win32.whl", hash = "sha256:0d8a7e523ef03757a5aa29f591437d64d0d894635f8a50f370fe37f913ce4e19"}, + {file = "watchdog-4.0.2-py3-none-win_amd64.whl", hash = "sha256:c344453ef3bf875a535b0488e3ad28e341adbd5a9ffb0f7d62cefacc8824ef2b"}, + {file = "watchdog-4.0.2-py3-none-win_ia64.whl", hash = "sha256:baececaa8edff42cd16558a639a9b0ddf425f93d892e8392a56bf904f5eff22c"}, + {file = "watchdog-4.0.2.tar.gz", hash = "sha256:b4dfbb6c49221be4535623ea4474a4d6ee0a9cef4a80b20c28db4d858b64e270"}, ] [package.extras] @@ -1281,21 +1406,24 @@ watchmedo = ["PyYAML (>=3.10)"] [[package]] name = "zipp" -version = "3.18.1" +version = "3.20.2" description = "Backport of pathlib-compatible object wrapper for zip files" -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, - {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, + {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, + {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "745cf65e48c3128e1949b8330b392907847d63b691428513ecb00f39f52e5e06" +content-hash = "2a091ce786f0a25b4675f51141305dfa4573a527811f585d53fd75a69c398852" diff --git a/pyproject.toml b/pyproject.toml index a9953e6..e726d09 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,9 @@ rdap = "rdap.cli:main" python = "^3.8" requests = ">=2.25.1" munge = {version = ">=1.3", extras = ["yaml"]} - +pydantic = ">=2.8.2" +googlemaps = ">=4.10" +phonenumbers = ">=8.13.0" [tool.poetry.dev-dependencies] # testing diff --git a/rdap/cli.py b/rdap/cli.py index 8911086..08eaac8 100644 --- a/rdap/cli.py +++ b/rdap/cli.py @@ -6,6 +6,7 @@ import rdap from rdap.config import Config +from rdap.context import RdapRequestContext def add_options(parser, options): @@ -49,6 +50,9 @@ def main(argv=None): parser.add_argument( "--parse", action="store_true", help="parse data into object before display" ) + parser.add_argument( + "--normalize", action="store_true", help="normalize data before display" + ) parser.add_argument("--rir", action="store_true", help="display rir", default=False) parser.add_argument( "--write-bootstrap-data", @@ -75,13 +79,16 @@ def main(argv=None): codec = munge.get_codec(output_format)() for each in argd["query"]: - obj = client.get(each) - if argd.get("rir", False): - print(f"rir: {obj.get_rir()}") - if argd.get("parse", False): - print(codec.dumps(obj.parsed())) - else: - print(codec.dumps(obj.data)) + with RdapRequestContext(client=client): + obj = client.get(each) + if argd.get("rir", False): + print(f"rir: {obj.get_rir()}") + if argd.get("parse", False): + print(codec.dumps(obj.parsed())) + elif argd.get("normalize", False): + print(codec.dumps(obj.normalized)) + else: + print(codec.dumps(obj.data)) if argd.get("show_requests", False): print("# Requests") diff --git a/rdap/client.py b/rdap/client.py index a074637..73d2d6f 100644 --- a/rdap/client.py +++ b/rdap/client.py @@ -11,6 +11,7 @@ import rdap import rdap.bootstrap from rdap.config import Config +from rdap.context import RdapRequestContext from rdap.exceptions import RdapHTTPError, RdapNotFoundError from rdap.objects import RdapAsn, RdapDomain, RdapEntity, RdapNetwork @@ -129,20 +130,23 @@ def __init__(self, config=None, config_dir=None): ) def _get(self, url): - res = self.http.get(url, timeout=self.timeout) - for redir in res.history: - self._history.append((strip_auth(redir.url), redir.status_code)) - self._history.append((strip_auth(res.url), res.status_code)) + with RdapRequestContext(url) as ctx: + res = self.http.get(url, timeout=self.timeout) + for redir in res.history: + self._history.append((strip_auth(redir.url), redir.status_code)) + self._history.append((strip_auth(res.url), res.status_code)) - if res.status_code == 200: - return res + ctx.push_url(strip_auth(res.url)) - msg = "RDAP lookup to {} returned {}".format( - strip_auth(res.url), res.status_code - ) - if res.status_code == 404: - raise RdapNotFoundError(msg) - raise RdapHTTPError(msg) + if res.status_code == 200: + return res + + msg = "RDAP lookup to {} returned {}".format( + strip_auth(res.url), res.status_code + ) + if res.status_code == 404: + raise RdapNotFoundError(msg) + raise RdapHTTPError(msg) def asn_url(self, asn): """Gets the correct url for specified ASN.""" @@ -243,6 +247,8 @@ def get(self, query): if qstr.startswith("as"): return self.get_asn(qstr[2:]) + return self.get_entity(qstr, self.url) + raise NotImplementedError(f"unknown query {query}") @lru_cache(maxsize=1024) @@ -315,7 +321,7 @@ def get_ip(self, address): query = f"/ip/{address}" return RdapNetwork(self._rdap_get(query).json(), self) - def get_entity(self, handle, base_url): + def get_entity(self, handle, base_url=None): """ get entity information in object form """ diff --git a/rdap/context.py b/rdap/context.py new file mode 100644 index 0000000..f0231bc --- /dev/null +++ b/rdap/context.py @@ -0,0 +1,130 @@ +""" +Contact management for RDAP requests. +""" + +from contextvars import ContextVar +from datetime import datetime + +import pydantic + +from rdap.exceptions import RdapHTTPError + +__all__ = [ + "rdap_request", + "RdapRequestContext", + "RdapRequestState", +] + + +class RdapSource(pydantic.BaseModel): + """ + Describes a source of RDAP data. + """ + + # urls requested for this source + urls: list[str] = pydantic.Field(default_factory=list) + + # rdap object handle + handle: str | None = None + + # source creation date (if available) + created: datetime | None = None + + # source last update date (if available) + updated: datetime | None = None + + +class RdapRequestState(pydantic.BaseModel): + """ + Describe the current rdap request, tracking sources queried + and entities retrieved. + """ + + # list of sources for the current request + sources: list[RdapSource] = pydantic.Field(default_factory=list) + + # reference to the rdap client instance + client: object | None = None + + # cache of entities (to avoid duplicate requests to the same entity + # within the current request context) + entities: dict = pydantic.Field(default_factory=dict) + + def update_source( + self, handle: str, created: datetime | None, updated: datetime | None + ): + """ + Update the current source with the handle and dates. + """ + + self.sources[-1].handle = handle + self.sources[-1].created = created + self.sources[-1].updated = updated + + +# context that holds the currently requested rdap url + +rdap_request = ContextVar("rdap_request", default=RdapRequestState()) + +# context manager to set the rdap url +# can be nested + + +class RdapRequestContext: + """ + Opens a request context + + If no state is present, a new state is created. + + If a state is present, a new source is added to the state. + """ + + def __init__(self, url: str = None, client: object = None): + self.url = url + self.token = None + self.client = client + + def __enter__(self): + + # get existing state + + state = rdap_request.get() + + if state and self.url: + state.sources.append(RdapSource(urls=[self.url])) + else: + state = RdapRequestState( + sources=[RdapSource(urls=[self.url] if self.url else [])] + ) + self.token = rdap_request.set(state) + + if self.client: + state.client = self.client + + return self + + def __exit__(self, *exc): + if self.token: + rdap_request.reset(self.token) + + def push_url(self, url: str): + state = rdap_request.get() + state.sources[-1].urls.append(url) + + def get(self, typ: str, handle: str): + state = rdap_request.get() + client = state.client + + if typ not in ["entity", "ip", "domain", "autnum"]: + raise ValueError(f"Invalid type: {typ}") + + if state.entities.get(handle): + return state.entities[handle] + try: + get = getattr(client, f"get_{typ}") + r_entity = get(handle).normalized + state.entities[handle] = r_entity + return r_entity + except RdapHTTPError: + state.entities[handle] = {} + return {} diff --git a/rdap/normalize/__init__.py b/rdap/normalize/__init__.py new file mode 100644 index 0000000..16ef885 --- /dev/null +++ b/rdap/normalize/__init__.py @@ -0,0 +1,231 @@ +""" +Contains a set of functions to parse various rdap data properties +""" + +import json + +import rdap.normalize.afrinic as afrinic +import rdap.normalize.apnic as apnic +import rdap.normalize.arin as arin +import rdap.normalize.base as base +import rdap.normalize.geo as geo +import rdap.normalize.lacnic as lacnic +import rdap.normalize.ripe as ripe +import rdap.schema.normalized as schema +import rdap.schema.rdap as rdap_schema +from rdap.context import RdapRequestState, rdap_request +from rdap.schema.source import ( + autnum_model, + domain_model, + entity_model, + ip_network_model, +) + +__all__ = [ + "normalize", + "normalize_autnum", + "normalize_entity", + "normalize_ip", + "normalize_domain", + "get_sources", +] + +HANDLERS = { + "arin": arin.Handler(), + "ripe": ripe.Handler(), + "apnic": apnic.Handler(), + "afrinic": afrinic.Handler(), + "lacnic": lacnic.Handler(), + # other (verisign for domains etc.) + None: base.Handler(), +} + + +def get_sources( + state: RdapRequestState, + handle: str, + entity: schema.Network | schema.IPNetwork | schema.Domain | schema.Entity, +) -> list[schema.Source]: + + sources = [] + + for source in state.sources: + + if not source.urls or not source.handle: + continue + + source = schema.Source( + handle=source.handle, + created=source.created, + updated=source.updated, + urls=source.urls, + ) + + sources.append(source) + + return sources + + +def normalize(data: dict, rir: str, typ: str) -> dict: + """ + Normalize data based on RIR + + Will return a normalized dict based on the RIR + """ + + if typ == "autnum": + return normalize_autnum(data, rir) + elif typ == "entity": + return normalize_entity(data, rir) + elif typ == "ip": + return normalize_ip(data, rir) + elif typ == "domain": + return normalize_domain(data, rir) + else: + raise ValueError(f"Type {typ} not supported") + + +def normalize_autnum(data: dict, rir: str) -> dict: + """ + Normalize data based on RIR: Autnum + + Will return a normalized dict based on the RIR + """ + handler = HANDLERS.get(rir) + if not handler: + raise ValueError(f"RIR {rir} not supported") + + current_rdap_request = rdap_request.get() + + rdap_autnum = autnum_model(rir)(**data) + + current_rdap_request.update_source( + rdap_autnum.handle, **handler.dates(rdap_autnum.events) + ) + + org_name = handler.org_name(rdap_autnum) + org = schema.Organization(name=org_name) + + net = schema.Network( + name=rdap_autnum.name, + organization=org, + asn=rdap_autnum.startAutnum, + contacts=handler.contacts(rdap_autnum), + locations=handler.locations(rdap_autnum), + **handler.dates(rdap_autnum.events), + ) + + if current_rdap_request: + net.sources = get_sources(current_rdap_request, rdap_autnum.handle, net) + + return json.loads(net.model_dump_json()) + + +def normalize_ip(data: dict, rir: str) -> dict: + """ + Normalize data based on RIR: IPNetwork + + Will return a normalized dict based on the RIR + """ + handler = HANDLERS.get(rir) + if not handler: + raise ValueError(f"RIR {rir} not supported") + + current_rdap_request = rdap_request.get() + + rdap_ip_network = ip_network_model(rir)(**data) + + current_rdap_request.update_source( + rdap_ip_network.handle, **handler.dates(rdap_ip_network.events) + ) + + prefix = handler.prefix(rdap_ip_network) + + net = schema.IPNetwork( + name=rdap_ip_network.name, + prefix=prefix, + parent=handler.parent_prefix(rdap_ip_network), + version=handler.ip_version(rdap_ip_network), + type=rdap_ip_network.type, + # TODO: What happens if more than one status is in there? + status=rdap_ip_network.status[0] if rdap_ip_network.status else None, + contacts=handler.contacts(rdap_ip_network), + **handler.dates(rdap_ip_network.events), + ) + + if current_rdap_request: + net.sources = get_sources(current_rdap_request, rdap_ip_network.handle, net) + + return json.loads(net.model_dump_json()) + + +def normalize_domain(data: dict, rir: str) -> dict: + """ + Normalize data based on RIR: Domain + + Will return a normalized dict based on the RIR + """ + handler = HANDLERS.get(rir) + if not handler: + raise ValueError(f"RIR {rir} not supported") + + current_rdap_request = rdap_request.get() + + rdap_domain = domain_model(rir)(**data) + + current_rdap_request.update_source( + rdap_domain.handle, **handler.dates(rdap_domain.events) + ) + + net = schema.Domain( + name=rdap_domain.ldhName, + handle=rdap_domain.handle, + dns_sec=handler.secure_dns(rdap_domain), + contacts=handler.contacts(rdap_domain), + nameservers=handler.nameservers(rdap_domain), + **handler.dates(rdap_domain.events), + ) + + if current_rdap_request: + net.sources = get_sources(current_rdap_request, rdap_domain.handle, net) + + return json.loads(net.model_dump_json()) + + +def normalize_entity(data: dict, rir: str) -> dict: + """ + Normalize data based on RIR: Entity + + Will return a normalized dict based on the RIR + """ + handler = HANDLERS.get(rir) + if not handler: + raise ValueError(f"RIR {rir} not supported") + + current_rdap_request = rdap_request.get() + + rdap_entity = entity_model(rir)(**data) + + current_rdap_request.update_source( + rdap_entity.handle, **handler.dates(rdap_entity.events) + ) + + org_name = handler.org_name_from_entity(rdap_entity) + + if org_name: + org = schema.Organization(name=org_name) + else: + org = None + + entity = schema.Entity( + name=rdap_entity.handle, + organization=org, + contacts=handler.contacts_from_entity(rdap_entity), + locations=handler.locations_from_entity(rdap_entity), + **handler.dates(rdap_entity.events), + ) + + if current_rdap_request: + entity.sources = get_sources(current_rdap_request, rdap_entity.handle, entity) + + return json.loads(entity.model_dump_json()) diff --git a/rdap/normalize/afrinic.py b/rdap/normalize/afrinic.py new file mode 100644 index 0000000..833e5a2 --- /dev/null +++ b/rdap/normalize/afrinic.py @@ -0,0 +1,17 @@ +""" +Some case specific normalization functions for AFRINIC data. +""" + +import rdap.normalize.base as base + +__all__ = [ + "Handler", +] + + +class Handler(base.Handler): + """ + No known AFRINIC specific normalizations. + """ + + pass diff --git a/rdap/normalize/apnic.py b/rdap/normalize/apnic.py new file mode 100644 index 0000000..d6b93f7 --- /dev/null +++ b/rdap/normalize/apnic.py @@ -0,0 +1,37 @@ +""" +Some case specific normalization functions for APNIC data. +""" + +import rdap.normalize.base as base +import rdap.schema.rdap as schema + +__all__ = [ + "Handler", +] + + +class Handler(base.Handler): + """ + APNIC sometimes puts org name into the remarks + """ + + def org_name( + self, entity: schema.AutNum | schema.IPNetwork | schema.Domain + ) -> str | None: + """ + If super() return None or equal to entity.name try checking + remarks for an entry where title == "description" + + TODO: Sometimes description just contains an address and no org name + How to handle this? + """ + + org_name = super().org_name(entity) + + if org_name is None or org_name == entity.name: + for remark in entity.remarks: + if remark.title == "description": + org_name = remark.description[0] + break + + return org_name diff --git a/rdap/normalize/arin.py b/rdap/normalize/arin.py new file mode 100644 index 0000000..c26dcb1 --- /dev/null +++ b/rdap/normalize/arin.py @@ -0,0 +1,17 @@ +""" +Some case specific normalization functions for ARIN data. +""" + +import rdap.normalize.base as base + +__all__ = [ + "Handler", +] + + +class Handler(base.Handler): + """ + No known ARIN specific normalizations. + """ + + pass diff --git a/rdap/normalize/base.py b/rdap/normalize/base.py new file mode 100644 index 0000000..c5b35e9 --- /dev/null +++ b/rdap/normalize/base.py @@ -0,0 +1,408 @@ +""" +Rdap data parsers for ARIN data +""" + +import ipaddress +from datetime import datetime + +import phonenumbers + +import rdap.normalize.geo as geo +import rdap.schema.rdap as schema +from rdap.context import RdapRequestContext, RdapRequestState, rdap_request +from rdap.schema.normalized import DNSSEC, Contact, Nameserver + +__all__ = [ + "Handler", +] + + +class Handler: + + def locations_from_entity(self, entity: schema.Entity) -> list[str]: + """ + Will parse an address from an entity + + Will return the address if it can be found, otherwise None + """ + # address will be through entities + # looking for vcardArray with ["adr", {"label": address}, "text"] to + # establish the address entry + + locations = [] + + dates = self.dates(entity.events) + + updated = dates["updated"] + + for vcard in entity.vcardArray[1:]: + for vcard_entry in vcard: + if vcard_entry[0] == "adr": + address = vcard_entry[1].get("label") + locations.append(geo.normalize(address, updated)) + + if entity.entities: + for _entity in entity.entities: + locations.extend(self.locations_from_entity(_entity)) + + # remove dupes + locations = list(set(locations)) + + return locations + + def locations( + self, entity: schema.AutNum | schema.IPNetwork | schema.Domain + ) -> list[str]: + """ + Will parse an address from an object + + Will return the address if it can be found, if no address + can be found, will return None + """ + + locations = [] + + for _entity in entity.entities: + locations.extend(self.locations_from_entity(_entity)) + + # remove dupes + locations = list(set(locations)) + + # sort by address + locations.sort(key=lambda x: x.address) + + return locations + + def contacts_from_entity( + self, entity: schema.Entity, deep: bool = True + ) -> list[Contact]: + """ + Will parse contacts from an entity + + Will return a list of contacts if it can be found, otherwise an empty list + + vcard entriews that are considered: + + ["email", {}, "text", email] + ["tel", {}, "text", phone] + ["fn", {}, "text", name] + + A contact is considered if `fn` and either of `email` or `tel` is present + + If deep is set to True, will also check nessted entities. + """ + + contacts = [] + + for vcard in entity.vcardArray[1:]: + + contact = Contact( + name="", + roles=getattr(entity, "roles", []) or [], + **self.dates(entity.events), + ) + for vcard_entry in vcard: + if vcard_entry[0] == "fn": + contact.name = vcard_entry[3] + if vcard_entry[0] == "email": + contact.email = vcard_entry[3] + if vcard_entry[0] == "tel": + contact.phone = vcard_entry[3] + try: + phone_number = phonenumbers.parse(contact.phone, None) + contact.phone = phonenumbers.format_number( + phone_number, phonenumbers.PhoneNumberFormat.INTERNATIONAL + ) + except phonenumbers.phonenumberutil.NumberParseException: + # TODO: setting to allow for invalid phone numbers? + contact.phone = None + + if contact.name and (contact.email or contact.phone): + contacts.append(contact) + + if deep and getattr(entity, "entities", None): + for _entity in entity.entities: + contacts.extend(self.contacts_from_entity(_entity, deep=True)) + + # remove dupes + contacts = list(set(contacts)) + + self.recurse_contacts(entity, contacts, entity.roles) + + return contacts + + def contacts( + self, + entity: schema.AutNum | schema.IPNetwork | schema.Domain, + deep: bool = True, + ) -> list[Contact]: + """ + Will parse contacts from an object + + Will return a list of contacts if it can be found, otherwise an empty list + + If deep is set to True, will also check nessted entities. + """ + + contacts = [] + + for _entity in entity.entities: + contacts.extend(self.contacts_from_entity(_entity, deep=deep)) + + # remove dupes + contacts = list(set(contacts)) + + # now cycle through and combine contacts + # + # check `name`, `phone` and `email` and if all three + # are the same, combine the roles + + combined_contacts = {} + + # sort by name and email + contacts = sorted(contacts, key=lambda x: (x.name or "", x.email or "")) + + for _contact in contacts: + key = f"{_contact.name}" + + if key in combined_contacts: + combined_contacts[key].roles.extend(_contact.roles) + if ( + not combined_contacts[key].email + and _contact.email + and _contact.phone == combined_contacts[key].phone + ): + combined_contacts[key].email = _contact.email + if ( + not combined_contacts[key].phone + and _contact.phone + and _contact.email == combined_contacts[key].email + ): + combined_contacts[key].phone = _contact.phone + else: + combined_contacts[key] = _contact + + contacts = list(combined_contacts.values()) + + # sort roles + for contact in contacts: + contact.roles = sorted(list(set(contact.roles))) + + return contacts + + def recurse_contacts( + self, entity: schema.Entity, contacts: list[Contact], roles: list[str] + ) -> list[Contact]: + + request_state: RdapRequestState = rdap_request.get() + client = request_state.client + + # if role is in settings to recurse, try to do a lookup + if entity.handle and client: + + handle_url = entity.self_link + if not handle_url: + handle_url = client.get_entity_url(entity.handle) + + if not client.recurse_roles.isdisjoint(roles): + with RdapRequestContext(url=handle_url, client=client) as ctx: + contacts.extend( + [ + Contact(**contact) + for contact in ctx.get("entity", entity.handle).get( + "contacts", [] + ) + ] + ) + + def org_name_from_entity(self, entity: schema.Entity) -> str | None: + """ + Will parse an org name from an entity + + Will return the org name if it can be found, otherwise None + """ + # org name will be through entities + # looking for vcardArray with ["kind", {}, "text", org] to + # establish the org entry + # + # in the same vcardArray, looking for ["fn", {}, "text", name] + + kind_is_org = False + org_name = None + + for vcard in entity.vcardArray[1:]: + for vcard_entry in vcard: + if vcard_entry[0] == "kind" and vcard_entry[3] == "org": + kind_is_org = True + if vcard_entry[0] == "fn": + org_name = vcard_entry[3] + + if kind_is_org and org_name: + return org_name + + return None + + def org_name( + self, entity: schema.AutNum | schema.IPNetwork | schema.Domain + ) -> str | None: + """ + Will parse an org name from an object + + Will return the org name if it can be found, if no org name + can be found, will return entity name or None + """ + + for _entity in entity.entities: + name = self.org_name_from_entity(_entity) + if name: + return name + + return entity.name or None + + def prefix( + self, ip_network: schema.IPNetwork + ) -> ipaddress.IPv4Network | ipaddress.IPv6Network: + """ + Will return the CIDR of an IPNetwork object + "cidr0_cidrs" : [ { + "v4prefix" : "206.41.110.0", + "length" : 24 + } ], + """ + + if not ip_network.cidr0_cidrs: + + # try if handle can be coerced into a prefix + + if ip_network.handle: + try: + return ipaddress.ip_network(ip_network.handle) + except ValueError: + pass + + return None + + cidr = ip_network.cidr0_cidrs[0] + + if "v4prefix" in cidr: + return ipaddress.IPv4Network(f"{cidr['v4prefix']}/{cidr['length']}") + + if "v6prefix" in cidr: + return ipaddress.IPv6Network(f"{cidr['v6prefix']}/{cidr['length']}") + + return None + + def ip_version(self, ip_network: schema.IPNetwork) -> int | None: + """ + Will return the IP version of an IPNetwork object + """ + + prefix = self.prefix(ip_network) + + if prefix: + return prefix.version + + ip_version = ip_network.ipVersion + + if ip_version == "v4": + return 4 + if ip_version == "v6": + return 6 + + return None + + def parent_prefix( + self, ip_network: schema.IPNetwork + ) -> ipaddress.IPv4Network | ipaddress.IPv6Network | None: + """ + Parent network prefix from `parentHandle` + + "parentHandle" : "NET-206-0-0-0-0", + """ + + if not ip_network.parentHandle: + return None + + # Extract the IP address part from the parentHandle + ip_parts = ip_network.parentHandle.split("-")[1:-1] + + # Reconstruct the IP address string + ip_str = ".".join(ip_parts) + + # Determine the appropriate prefix length based on the number of non-zero octets + non_zero_octets = sum(1 for part in ip_parts if part != "0") + prefix_length = non_zero_octets * 8 + + # Construct the CIDR notation + cidr = f"{ip_str}/{prefix_length}" + + try: + # Attempt to create an IP network object + return ipaddress.ip_network(cidr, strict=False) + except ValueError: + # If the IP address is invalid, return None + return None + + def secure_dns(self, domain: schema.Domain) -> DNSSEC: + """ + Will determine if the domain has secure DNS + + This is determined by the `secureDNS` object + + If `delegationSigned` or `zeroSigned` is True, will return secure + + If both are False, will return insecure + + If secureDNS is not present, or both are None, will return unknown + """ + + if not domain.secureDNS: + return DNSSEC.unknown + + if ( + domain.secureDNS.delegationSigned is None + and domain.secureDNS.zeroSigned is None + ): + return DNSSEC.unknown + + if domain.secureDNS.delegationSigned or domain.secureDNS.zeroSigned: + return DNSSEC.secure + + return DNSSEC.insecure + + def nameservers(self, domain: schema.Domain) -> list[Nameserver]: + """ + Returns normalized nameservers from a domain object + """ + + nameservers = [] + + for nameserver in domain.nameservers: + nameservers.append(Nameserver(host=nameserver.ldhName)) + + return nameservers + + def dates(self, events: list[schema.Event]) -> dict[str, str]: + """ + Return the created and updated dates from the events + """ + + created = None + updated = None + + for event in events: + if event.eventAction == "registration": + created: datetime = event.eventDate + if event.eventAction == "last changed": + updated: datetime = event.eventDate + + # set utc timezone if no timezone is present + # created and du + + if created and not created.tzinfo: + created = created.replace(tzinfo=datetime.timezone.utc) + + if updated and not updated.tzinfo: + updated = updated.replace(tzinfo=datetime.timezone.utc) + + return {"created": created, "updated": updated} diff --git a/rdap/normalize/geo.py b/rdap/normalize/geo.py new file mode 100644 index 0000000..8e24e54 --- /dev/null +++ b/rdap/normalize/geo.py @@ -0,0 +1,135 @@ +""" +Uses googlemaps to resolve address to geolocation +and into fields +""" + +import os +from datetime import datetime + +import googlemaps + +from rdap.context import RdapRequestState, rdap_request +from rdap.schema.normalized import GeoLocation, Location + +GOOGLE_MAPS_API_KEY = os.getenv("GOOGLE_MAPS_API_KEY") + + +class RequestError(Exception): + pass + + +class Timeout(Exception): + pass + + +class NotFound(KeyError): + pass + + +class GoogleKeyNotSet(Exception): + pass + + +def get_client(key: str = GOOGLE_MAPS_API_KEY): + + if not key: + raise GoogleKeyNotSet("Google Maps API Key not set") + + return googlemaps.Client(key) + + +def lookup(formatted_address: str, client=None) -> dict: + """ + Return the latitude, longitude field values of the specified + location. + """ + + request: RdapRequestState = rdap_request.get() + + key = f"geo:{formatted_address}" + + if key in request.entities: + return request.entities[key] + + if not client: + client = get_client() + + try: + result = client.geocode( + formatted_address, + ) + except ( + googlemaps.exceptions.HTTPError, + googlemaps.exceptions.ApiError, + googlemaps.exceptions.TransportError, + ) as exc: + raise RequestError(exc) + except googlemaps.exceptions.Timeout: + raise Timeout() + + if not result: + raise NotFound() + + # cache to avoid duplicate lookups during the same + # request context + request.entities[key] = result[0] + + return result[0] + + +def normalize(formatted_address: str, date: datetime = None, client=None) -> Location: + """ + Takes a formatted address and returns a normalized location object + """ + + try: + result = lookup(formatted_address, client) + except (GoogleKeyNotSet, NotFound) as exc: + + # If a google maps key is not set, return a location object with + # only the address field set + return Location( + updated=date, + address=formatted_address, + ) + + city = None + postal_code = None + country = None + floor = None + suite = None + + address_components = result.get("address_components", []) + print("Address components:", address_components) + + for component in result.get("address_components", []): + types = component.get("types", []) + print("Component:", component) + if "country" in types: + country = component.get("short_name") + + if "postal_code" in types: + postal_code = component.get("long_name") + + if "locality" in types or "postal_town" in types: + city = component.get("long_name") + + if "floor" in types: + floor = component.get("long_name") + + if "subpremise" in types: + suite = component.get("long_name") + + return Location( + updated=date, + country=country, + city=city, + postal_code=postal_code, + floor=floor, + suite=suite, + address=result.get("formatted_address"), + geo=GeoLocation( + latitude=result.get("geometry").get("location").get("lat"), + longitude=result.get("geometry").get("location").get("lng"), + ), + ) diff --git a/rdap/normalize/lacnic.py b/rdap/normalize/lacnic.py new file mode 100644 index 0000000..d80b41f --- /dev/null +++ b/rdap/normalize/lacnic.py @@ -0,0 +1,17 @@ +""" +Some case specific normalization functions for LACNIC data. +""" + +import rdap.normalize.base as base + +__all__ = [ + "Handler", +] + + +class Handler(base.Handler): + """ + No known LACNIC specific normalizations. + """ + + pass diff --git a/rdap/normalize/ripe.py b/rdap/normalize/ripe.py new file mode 100644 index 0000000..8720b13 --- /dev/null +++ b/rdap/normalize/ripe.py @@ -0,0 +1,34 @@ +""" +Some case specific normalization functions for RIPE data. +""" + +import rdap.normalize.base as base +import rdap.schema.rdap as schema + +__all__ = [ + "Handler", +] + + +class Handler(base.Handler): + """ + RIPE sometimes puts org name into the remarks + """ + + def org_name( + self, entity: schema.AutNum | schema.IPNetwork | schema.Domain + ) -> str | None: + """ + If super() return None or equal to entity.name try checking + remarks for an entry where title == "description" + """ + + org_name = super().org_name(entity) + + if org_name is None or org_name == entity.name: + for remark in entity.remarks: + if remark.title == "description": + org_name = remark.description[0] + break + + return org_name diff --git a/rdap/objects.py b/rdap/objects.py index e197021..d0b51bb 100644 --- a/rdap/objects.py +++ b/rdap/objects.py @@ -1,4 +1,5 @@ from rdap.exceptions import RdapHTTPError, RdapNotFoundError +from rdap.normalize import normalize def rir_from_domain(domain): @@ -205,17 +206,33 @@ def __init__(self, data, rdapc=None): super().__init__(data, rdapc) + @property + def normalized(self) -> dict: + return normalize(self._data, self.get_rir(), "autnum") + class RdapNetwork(RdapObject): def __init__(self, data, rdapc=None): super().__init__(data, rdapc) + @property + def normalized(self) -> dict: + return normalize(self._data, self.get_rir(), "ip") + class RdapDomain(RdapObject): def __init__(self, data, rdapc=None): super().__init__(data, rdapc) + @property + def normalized(self) -> dict: + return normalize(self._data, self.get_rir(), "domain") + class RdapEntity(RdapObject): def __init__(self, data, rdapc=None): super().__init__(data, rdapc) + + @property + def normalized(self) -> dict: + return normalize(self._data, self.get_rir(), "entity") diff --git a/rdap/schema/__init__.py b/rdap/schema/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rdap/schema/normalized.py b/rdap/schema/normalized.py new file mode 100644 index 0000000..7c81de0 --- /dev/null +++ b/rdap/schema/normalized.py @@ -0,0 +1,242 @@ +""" +Pydantic schemas for normalized RDAP data +""" + +import enum +import ipaddress +from datetime import datetime +from typing import Any + +import pydantic + +__all__ = [ + "IP_VERSION", + "STATUS", + "ROLE", + "DNSSEC", + "GeoLocation", + "Location", + "Contact", + "Source", + "Organization", + "Network", + "IPNetwork", + "Entity", + "Nameserver", + "Domain", +] + + +class IP_VERSION(int, enum.Enum): + """ + Enum for IP version + """ + + ipv4 = 4 + ipv6 = 6 + + +class STATUS(str, enum.Enum): + active = "active" + inactive = "inactive" + + +NORMALIZED_STATUS = { + "administrative": "active", + "validated": "active", +} + + +class ROLE(str, enum.Enum): + abuse = "abuse" + admin = "admin" + policy = "policy" + technical = "technical" + registrant = "registrant" + billing = "billing" + sponsor = "sponsor" + + +NORMALIZED_ROLES = { + "administrative": "admin", + "noc": "technical", + "registrar": "registrant", + "routing": "technical", + "dns": "technical", +} + + +VALID_ROLES = [str(r) for r in ROLE] + + +class DNSSEC(str, enum.Enum): + secure = "secure" + insecure = "insecure" + unknown = "unknown" + + +class GeoLocation(pydantic.BaseModel): + """ + Describes geographic coordinates + """ + + latitude: float + longitude: float + + +class Location(pydantic.BaseModel): + """ + Describes a location + """ + + updated: datetime | None = None + country: str | None = None + city: str | None = None + postal_code: str | None = None + address: str | None = None + geo: GeoLocation | None = None + floor: str | None = None + suite: str | None = None + + def __hash__(self): + return f"{self.address}-{self.city}-{self.country}-{self.postal_code}-{self.floor}-{self.suite}".__hash__() + + +class Contact(pydantic.BaseModel): + """ + Describes a point of contact + """ + + created: datetime | None = None + updated: datetime | None = None + name: str + roles: list[ROLE] = pydantic.Field(default_factory=list) + phone: str | None = None + email: str | None = None + + @pydantic.model_validator(mode="before") + @classmethod + def normalize_roles(cls, data: Any) -> Any: + + roles = [] + + for role in data.get("roles", []): + role = NORMALIZED_ROLES.get(role, role) + roles.append(role) + + data["roles"] = roles + + # drop duplicates + data["roles"] = list(set(data["roles"])) + + # drop any invalid roles + data["roles"] = [ + role for role in data["roles"] if f"ROLE.{role}" in VALID_ROLES + ] + + return data + + def __hash__(self): + return f"{self.name}-{self.email}-{self.phone}: {self.roles}".__hash__() + + +class Source(pydantic.BaseModel): + """ + Describes a source of rdap data + + Will contain where the data was fetched from and when + """ + + created: datetime | None = None + updated: datetime | None = None + handle: str + urls: list[str] = pydantic.Field(default_factory=list) + description: str | None = None + + +class Organization(pydantic.BaseModel): + """ + Describes an organization + """ + + name: str + + +class Network(pydantic.BaseModel): + """ + Describes a network + """ + + created: datetime | None = None + updated: datetime | None = None + asn: int + name: str + organization: Organization + locations: list[Location] = pydantic.Field(default_factory=list) + contacts: list[Contact] = pydantic.Field(default_factory=list) + sources: list[Source] = pydantic.Field(default_factory=list) + + +class IPNetwork(pydantic.BaseModel): + """ + Describes an IP network + """ + + created: datetime | None = None + updated: datetime | None = None + prefix: ipaddress.IPv4Network | ipaddress.IPv6Network | None = None + version: IP_VERSION | None = None + name: str | None = None + type: str | None = None + status: STATUS + parent: ipaddress.IPv4Network | ipaddress.IPv6Network | None = None + contacts: list[Contact] = pydantic.Field(default_factory=list) + sources: list[Source] = pydantic.Field(default_factory=list) + + @pydantic.model_validator(mode="before") + @classmethod + def normalize_status(cls, data: Any) -> Any: + + status = data.get("status") + + if status: + data["status"] = NORMALIZED_STATUS.get(status, status) + + return data + + +class Entity(pydantic.BaseModel): + """ + Describes an entity + """ + + created: datetime | None = None + updated: datetime | None = None + name: str + organization: Organization | None = None + locations: list[Location] = pydantic.Field(default_factory=list) + contacts: list[Contact] = pydantic.Field(default_factory=list) + sources: list[Source] = pydantic.Field(default_factory=list) + + +class Nameserver(pydantic.BaseModel): + """ + Describes a nameserver + """ + + host: str + + +class Domain(pydantic.BaseModel): + """ + Describes a domain + """ + + created: datetime | None = None + updated: datetime | None = None + name: str + handle: str + dns_sec: DNSSEC + nameservers: list[Nameserver] = pydantic.Field(default_factory=list) + contacts: list[Contact] = pydantic.Field(default_factory=list) + sources: list[Source] = pydantic.Field(default_factory=list) diff --git a/rdap/schema/rdap.py b/rdap/schema/rdap.py new file mode 100644 index 0000000..1ea9409 --- /dev/null +++ b/rdap/schema/rdap.py @@ -0,0 +1,237 @@ +from datetime import datetime +from typing import Any + +from pydantic import BaseModel, Field + +__all__ = [ + "Link", + "Event", + "Notice", + "VCardValue", + "Remark", + "Entity", + "IPNetwork", +] + + +class Link(BaseModel): + """Represents a hyperlink in the RDAP response.""" + + # The label or description of the link + value: str | None = None + # The relationship of the link to the current object + rel: str | None = None + # The MIME type of the target resource + type: str | None = None + # The URL of the link + href: str | None = None + + +class Event(BaseModel): + """Represents a timestamped event in the lifecycle of an RDAP object.""" + + # The type of event (e.g., "registration", "last changed") + eventAction: str | None = None + # The date and time of the event + eventDate: datetime | None = None + + +class Notice(BaseModel): + """Represents a notice or message in the RDAP response.""" + + # The title of the notice + title: str | None = None + # A list of text lines comprising the notice + description: list[str] = Field(default_factory=list) + # Optional links related to the notice + links: list[Link] = Field(default_factory=list) + + +class VCardValue(BaseModel): + """Represents additional properties for vCard values.""" + + # Types associated with the vCard value (e.g., "work", "voice" for telephone) + type: list[str] | None = None + + +class Remark(BaseModel): + """Represents a remark or comment in the RDAP response.""" + + # The title of the remark + title: str | None = None + # A list of text lines comprising the remark + description: list[str] = Field(default_factory=list) + + +class Entity(BaseModel): + """Represents an entity (organization, individual, or role) in the RDAP response.""" + + # A unique identifier for the entity + handle: str = Field(default_factory=str) + # Contact information in vCard format + vcardArray: list[str | list[list[str | dict | list | None]]] = Field( + default_factory=list + ) + # Roles of the entity (e.g., registrant, technical, administrative) + roles: list[str] = Field(default_factory=list) + # Links related to the entity + links: list[Link] = Field(default_factory=list) + # Events associated with the entity + events: list[Event] = Field(default_factory=list) + # Status of the entity + status: list[str] = Field(default_factory=list) + # WHOIS server for the entity + port43: str = Field(default_factory=str) + # Type of the object (always "entity" for Entity) + objectClassName: str + # Additional remarks about the entity + remarks: list[Remark] = Field(default_factory=list) + # Nested entities (e.g., contacts within an organization) + entities: list["Entity"] = Field(default_factory=list) + + @property + def self_link(self) -> str | None: + """Returns the href of the link where rel == 'self'""" + for link in self.links: + if link.rel == "self": + return link.href + return None + + +class IPNetwork(BaseModel): + """Represents an IP network in the RDAP response.""" + + # list of conformance levels + rdapConformance: list[str] = Field(default_factory=list) + # Notices related to the IP network + notices: list[Notice] = Field(default_factory=list) + # A unique identifier for the IP network + handle: str | None = None + # The first IP address in the network range + startAddress: str | None = None + # The last IP address in the network range + endAddress: str | None = None + # IP version (v4 or v6) + ipVersion: str | None = None + # Name of the network + name: str | None = None + # Type of the network allocation + type: str | None = None + # Handle of the parent network + parentHandle: str | None = None + # Additional remarks about the network + remarks: list[Remark] = Field(default_factory=list) + # Events associated with the network + events: list[Event] = Field(default_factory=list) + # Links related to the network + links: list[Link] = Field(default_factory=list) + # Entities associated with the network + entities: list[Entity] = Field(default_factory=list) + # WHOIS server for the network + port43: str | None = None + # Status of the network + status: list[str] = Field(default_factory=list) + # Type of the object (always "ip network" for IPNetwork) + objectClassName: str | None = None + # CIDR notation for the network + cidr0_cidrs: list[dict] = Field(default_factory=list) + # Origin AS numbers for the network + arin_originas0_originautnums: list = Field(default_factory=list) + + +class DSData(BaseModel): + """Represents DS data for secure DNS in the RDAP response.""" + + # Key tag for the DS record + keyTag: int | None = None + # Algorithm number for the DS record + algorithm: int | None = None + # Digest type for the DS record + digestType: int | None = None + # Digest value for the DS record + digest: str | None = None + + +class SecureDNS(BaseModel): + # true if there are DS records in the parent, false otherwise. + delegationSigned: bool | None = None + # if the zone has been signed, false otherwise. + zeroSigned: bool | None = None + # DS data for secure DNS + dsData: list[DSData] = Field(default_factory=list) + + +class Nameserver(BaseModel): + objectClassName: str | None = None + ldhName: str | None = None + unicodeName: str | None = None + ipAddresses: dict[str, list[str]] = Field(default_factory=dict) + remarks: list[Remark] = Field(default_factory=list) + port43: str | None = None + events: list[Event] = Field(default_factory=list) + + +class Domain(BaseModel): + """Represents a domain name in the RDAP response.""" + + # list of conformance levels + rdapConformance: list[str] = Field(default_factory=list) + # Notices related to the domain + notices: list[Notice] = Field(default_factory=list) + # A unique identifier for the domain + handle: str | None = None + # The domain name in LDH (Letter Digit Hyphen) format + ldhName: str | None = None + # Events associated with the domain + events: list[Event] = Field(default_factory=list) + # Links related to the domain + links: list[Link] = Field(default_factory=list) + # Entities associated with the domain + entities: list[Entity] = Field(default_factory=list) + # WHOIS server for the domain + port43: str = Field(default_factory=str) + # Network information for the domain + network: IPNetwork | None = None + # Type of the object (always "domain" for Domain) + objectClassName: str | None = None + + secureDNS: SecureDNS | None = None + + nameservers: list[Nameserver] = Field(default_factory=list) + + +class AutNum(BaseModel): + """Represents an Autonomous System Number in the RDAP response.""" + + # list of conformance levels + rdapConformance: list[str] = Field(default_factory=list) + # Notices related to the AS number + notices: list[Notice] = Field(default_factory=list) + # A unique identifier for the AS number + handle: str | None = None + # The starting AS number in the range + startAutnum: int | None = None + # The ending AS number in the range (same as startAutnum for single AS) + endAutnum: int | None = None + # Name of the AS + name: str | None = None + # WHOIS server for the AS number + port43: str | None = None + + # Type of the object (always "autnum" for AutNum) + objectClassName: str | None = None + + # Events associated with the AS number + events: list[Event] = Field(default_factory=list) + # Links related to the AS number + links: list[Link] = Field(default_factory=list) + # Entities associated with the AS number + entities: list[Entity] = Field(default_factory=list) + # Status of the AS number + status: list[str] = Field(default_factory=list) + + # Remarks about the AS number + remarks: list[Remark] = Field(default_factory=list) + + +Entity.model_rebuild() diff --git a/rdap/schema/source.py b/rdap/schema/source.py new file mode 100644 index 0000000..d5de705 --- /dev/null +++ b/rdap/schema/source.py @@ -0,0 +1,62 @@ +import rdap.schema.rdap as rdap + +__all__ = [ + "SCHEMAS_BY_RIR", + "ip_network_model", + "domain_model", + "autnum_model", + "entity_model", +] + +SCHEMAS_BY_RIR = { + "arin": rdap, + "ripe": rdap, + "apnic": rdap, + "afrinic": rdap, + "lacnic": rdap, + None: rdap, +} + + +def ip_network_model(rir: str) -> rdap.IPNetwork: + """ + Returns pydantic model for IPNetwork for the given RIR + + Arguments: + + - rir: str: RIR name (e.g., "arin", "ripe", "apnic", "afrinic", "lacnic") + """ + return SCHEMAS_BY_RIR[rir].IPNetwork + + +def domain_model(rir: str) -> rdap.Domain: + """ + Returns pydantic model for Domain for the given RIR + + Arguments: + + - rir: str: RIR name (e.g., "arin", "ripe", "apnic", "afrinic", "lacnic") + """ + return SCHEMAS_BY_RIR[rir].Domain + + +def autnum_model(rir: str) -> rdap.AutNum: + """ + Returns pydantic model for AutNum for the given RIR + + Arguments: + + - rir: str: RIR name (e.g., "arin", "ripe", "apnic", "afrinic", "lacnic") + """ + return SCHEMAS_BY_RIR[rir].AutNum + + +def entity_model(rir: str) -> rdap.Entity: + """ + Returns pydantic model for Entity for the given RIR + + Arguments: + + - rir: str: RIR name (e.g., "arin", "ripe", "apnic", "afrinic", "lacnic") + """ + return SCHEMAS_BY_RIR[rir].Entity diff --git a/tests/data/iana/bootstrap/asn.json b/tests/data/iana/bootstrap/asn.json index 60d047e..2b6a43b 100644 --- a/tests/data/iana/bootstrap/asn.json +++ b/tests/data/iana/bootstrap/asn.json @@ -1 +1 @@ -{"description":"RDAP bootstrap file for Autonomous System Number allocations","publication":"2023-08-10T17:00:03Z","services":[[["36864-37887","327680-328703","328704-329727"],["https://rdap.afrinic.net/rdap/","http://rdap.afrinic.net/rdap/"]],[["4608-4865","7467-7722","9216-10239","17408-18431","23552-24575","37888-38911","45056-46079","55296-56319","58368-59391","63488-63999","64000-64098","64297-64395","131072-132095","132096-133119","133120-133631","133632-134556","134557-135580","135581-136505","136506-137529","137530-138553","138554-139577","139578-140601","140602-141625","141626-142649","142650-143673","143674-144697","144698-145721","145722-146745","146746-147769","147770-148793","148794-149817","149818-150841","150842-151865","151866-152889","152890-153913"],["https://rdap.apnic.net/"]],[["1-1876","1902-2042","2044-2046","2048-2106","2137-2584","2615-2772","2823-2829","2880-3153","3354-4607","4866-5376","5632-6655","6912-7466","7723-8191","10240-12287","13312-15359","16384-17407","18432-20479","21504-23455","23457-23551","25600-26623","26624-27647","29696-30719","31744-32767","32768-33791","35840-36863","39936-40959","46080-47103","53248-54271","54272-55295","62464-63487","64198-64296","393216-394239","394240-395164","395165-396188","396189-397212","397213-398236","398237-399260","399261-400284","400285-401308"],["https://rdap.arin.net/registry/","http://rdap.arin.net/registry/"]],[["1877-1901","2043","2047","2107-2136","2585-2614","2773-2822","2830-2879","3154-3353","5377-5631","6656-6911","8192-9215","12288-13311","15360-16383","20480-21503","24576-25599","28672-29695","30720-31743","33792-34815","34816-35839","38912-39935","40960-41983","41984-43007","43008-44031","44032-45055","47104-48127","48128-49151","49152-50175","50176-51199","51200-52223","56320-57343","57344-58367","59392-60415","60416-61439","61952-62463","64396-64495","196608-197631","197632-198655","198656-199679","199680-200191","200192-201215","201216-202239","202240-203263","203264-204287","204288-205211","205212-206235","206236-207259","207260-208283","208284-209307","209308-210331","210332-211355","211356-212379","212380-213403","213404-214427","214428-215451","215452-216475"],["https://rdap.db.ripe.net/"]],[["27648-28671","52224-53247","61440-61951","64099-64197","262144-263167","263168-263679","263680-264604","264605-265628","265629-266652","266653-267676","267677-268700","268701-269724","269725-270748","270749-271772","271773-272796","272797-273820"],["https://rdap.lacnic.net/rdap/"]]],"version":"1.0"} \ No newline at end of file +{"description":"RDAP bootstrap file for Autonomous System Number allocations","publication":"2024-04-10T20:00:01Z","services":[[["36864-37887","327680-328703","328704-329727"],["https://rdap.afrinic.net/rdap/","http://rdap.afrinic.net/rdap/"]],[["4608-4865","7467-7722","9216-10239","17408-18431","23552-24575","37888-38911","45056-46079","55296-56319","58368-59391","63488-63999","64000-64098","64297-64395","131072-132095","132096-133119","133120-133631","133632-134556","134557-135580","135581-136505","136506-137529","137530-138553","138554-139577","139578-140601","140602-141625","141626-142649","142650-143673","143674-144697","144698-145721","145722-146745","146746-147769","147770-148793","148794-149817","149818-150841","150842-151865","151866-152889","152890-153913"],["https://rdap.apnic.net/"]],[["1-1876","1902-2042","2044-2046","2048-2106","2137-2584","2615-2772","2823-2829","2880-3153","3354-4607","4866-5376","5632-6655","6912-7466","7723-8191","10240-12287","13312-15359","16384-17407","18432-20479","21504-23455","23457-23551","25600-26623","26624-27647","29696-30719","31744-32767","32768-33791","35840-36863","39936-40959","46080-47103","53248-54271","54272-55295","62464-63487","64198-64296","393216-394239","394240-395164","395165-396188","396189-397212","397213-398236","398237-399260","399261-400284","400285-401308","401309-402332"],["https://rdap.arin.net/registry/","http://rdap.arin.net/registry/"]],[["1877-1901","2043","2047","2107-2136","2585-2614","2773-2822","2830-2879","3154-3353","5377-5631","6656-6911","8192-9215","12288-13311","15360-16383","20480-21503","24576-25599","28672-29695","30720-31743","33792-34815","34816-35839","38912-39935","40960-41983","41984-43007","43008-44031","44032-45055","47104-48127","48128-49151","49152-50175","50176-51199","51200-52223","56320-57343","57344-58367","59392-60415","60416-61439","61952-62463","64396-64495","196608-197631","197632-198655","198656-199679","199680-200191","200192-201215","201216-202239","202240-203263","203264-204287","204288-205211","205212-206235","206236-207259","207260-208283","208284-209307","209308-210331","210332-211355","211356-212379","212380-213403","213404-214427","214428-215451","215452-216475"],["https://rdap.db.ripe.net/"]],[["27648-28671","52224-53247","61440-61951","64099-64197","262144-263167","263168-263679","263680-264604","264605-265628","265629-266652","266653-267676","267677-268700","268701-269724","269725-270748","270749-271772","271773-272796","272797-273820","273821-274844"],["https://rdap.lacnic.net/rdap/"]]],"version":"1.0"} \ No newline at end of file diff --git a/tests/data/normalize/autnum/63311.expected b/tests/data/normalize/autnum/63311.expected new file mode 100644 index 0000000..d03582f --- /dev/null +++ b/tests/data/normalize/autnum/63311.expected @@ -0,0 +1,63 @@ +{ + "created": "2014-11-17T14:28:43-05:00", + "updated": "2018-10-24T22:58:16-04:00", + "asn": 63311, + "name": "20C", + "organization": { + "name": "20C, LLC" + }, + "locations": [ + { + "updated": "2014-08-05T15:21:11-04:00", + "country": "US", + "city": "Mountain View", + "postal_code": "94043", + "address": "303 W Ohio #1701\nChicago\nIL\n60654\nUnited States", + "geo": { + "latitude": 37.4224764, + "longitude": -122.0842499 + }, + "floor": "4", + "suite": "Suite 200" + }, + { + "updated": "2023-08-02T14:15:09-04:00", + "country": "US", + "city": "Mountain View", + "postal_code": "94043", + "address": "603 Discovery Dr\nWest Chicago\nIL\n60185\nUnited States", + "geo": { + "latitude": 37.4224764, + "longitude": -122.0842499 + }, + "floor": "4", + "suite": "Suite 200" + } + ], + "contacts": [ + { + "created": "2014-07-03T23:22:49-04:00", + "updated": "2023-08-02T14:15:09-04:00", + "name": "Network Engineers", + "roles": [ + "abuse", + "admin", + "technical" + ], + "phone": "+1 978-636-0020", + "email": "neteng@20c.com" + } + ], + "sources": [ + { + "created": "2014-11-17T14:28:43-05:00", + "updated": "2018-10-24T22:58:16-04:00", + "handle": "AS63311", + "urls": [ + "https://rdap.org/autnum/63311", + "https://rdap.org/autnum/63311" + ], + "description": null + } + ] +} \ No newline at end of file diff --git a/tests/data/normalize/autnum/63311.input b/tests/data/normalize/autnum/63311.input new file mode 100644 index 0000000..fa38ec9 --- /dev/null +++ b/tests/data/normalize/autnum/63311.input @@ -0,0 +1,250 @@ +{ + "rdapConformance": [ + "nro_rdap_profile_0", + "rdap_level_0", + "nro_rdap_profile_asn_flat_0" + ], + "notices": [ + { + "title": "Terms of Service", + "description": [ + "By using the ARIN RDAP/Whois service, you are agreeing to the RDAP/Whois Terms of Use" + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/autnum/63311", + "rel": "terms-of-service", + "type": "text/html", + "href": "https://www.arin.net/resources/registry/whois/tou/" + } + ] + }, + { + "title": "Whois Inaccuracy Reporting", + "description": [ + "If you see inaccuracies in the results, please visit: " + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/autnum/63311", + "rel": "inaccuracy-report", + "type": "text/html", + "href": "https://www.arin.net/resources/registry/whois/inaccuracy_reporting/" + } + ] + }, + { + "title": "Copyright Notice", + "description": [ + "Copyright 1997-2023, American Registry for Internet Numbers, Ltd." + ] + } + ], + "handle": "AS63311", + "startAutnum": 63311, + "endAutnum": 63311, + "name": "20C", + "events": [ + { + "eventAction": "last changed", + "eventDate": "2018-10-24T22:58:16-04:00" + }, + { + "eventAction": "registration", + "eventDate": "2014-11-17T14:28:43-05:00" + } + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/autnum/63311", + "rel": "self", + "type": "application/rdap+json", + "href": "https://rdap.arin.net/registry/autnum/63311" + }, + { + "value": "https://rdap.arin.net/registry/autnum/63311", + "rel": "alternate", + "type": "application/xml", + "href": "https://whois.arin.net/rest/asn/AS63311" + } + ], + "entities": [ + { + "handle": "CL-672", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "20C, LLC" + ], + [ + "adr", + { + "label": "303 W Ohio #1701\nChicago\nIL\n60654\nUnited States" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "kind", + {}, + "text", + "org" + ] + ] + ], + "roles": [ + "registrant" + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/autnum/63311", + "rel": "self", + "type": "application/rdap+json", + "href": "https://rdap.arin.net/registry/entity/CL-672" + }, + { + "value": "https://rdap.arin.net/registry/autnum/63311", + "rel": "alternate", + "type": "application/xml", + "href": "https://whois.arin.net/rest/org/CL-672" + } + ], + "events": [ + { + "eventAction": "last changed", + "eventDate": "2014-08-05T15:21:11-04:00" + }, + { + "eventAction": "registration", + "eventDate": "2014-08-05T15:21:11-04:00" + } + ], + "entities": [ + { + "handle": "NETWO7047-ARIN", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "adr", + { + "label": "603 Discovery Dr\nWest Chicago\nIL\n60185\nUnited States" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "fn", + {}, + "text", + "Network Engineers" + ], + [ + "org", + {}, + "text", + "Network Engineers" + ], + [ + "kind", + {}, + "text", + "group" + ], + [ + "email", + {}, + "text", + "neteng@20c.com" + ], + [ + "tel", + { + "type": [ + "work", + "voice" + ] + }, + "text", + "+1-978-636-0020" + ] + ] + ], + "roles": [ + "technical", + "abuse", + "administrative" + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/autnum/63311", + "rel": "self", + "type": "application/rdap+json", + "href": "https://rdap.arin.net/registry/entity/NETWO7047-ARIN" + }, + { + "value": "https://rdap.arin.net/registry/autnum/63311", + "rel": "alternate", + "type": "application/xml", + "href": "https://whois.arin.net/rest/poc/NETWO7047-ARIN" + } + ], + "events": [ + { + "eventAction": "last changed", + "eventDate": "2023-08-02T14:15:09-04:00" + }, + { + "eventAction": "registration", + "eventDate": "2014-07-03T23:22:49-04:00" + } + ], + "status": [ + "validated" + ], + "port43": "whois.arin.net", + "objectClassName": "entity" + } + ], + "port43": "whois.arin.net", + "objectClassName": "entity" + } + ], + "port43": "whois.arin.net", + "status": [ + "active" + ], + "objectClassName": "autnum" +} \ No newline at end of file diff --git a/tests/data/normalize/autnum/8283.expected b/tests/data/normalize/autnum/8283.expected new file mode 100644 index 0000000..32f2c37 --- /dev/null +++ b/tests/data/normalize/autnum/8283.expected @@ -0,0 +1,307 @@ +{ + "created": "2003-06-23T14:40:34Z", + "updated": "2023-11-27T23:02:44Z", + "asn": 8283, + "name": "COLOCLUE-AS", + "organization": { + "name": "Netwerkvereniging Coloclue" + }, + "locations": [ + { + "updated": null, + "country": "US", + "city": "Mountain View", + "postal_code": "94043", + "address": "Bijsterveld 1\n4902 ZN Oosterhout\nThe Netherlands", + "geo": { + "latitude": 37.4224764, + "longitude": -122.0842499 + }, + "floor": "4", + "suite": "Suite 200" + }, + { + "updated": null, + "country": "US", + "city": "Mountain View", + "postal_code": "94043", + "address": "BytePark\nGruttostraat 63\nNL-2665 EL Bleiswijk\nThe Netherlands", + "geo": { + "latitude": 37.4224764, + "longitude": -122.0842499 + }, + "floor": "4", + "suite": "Suite 200" + }, + { + "updated": null, + "country": "US", + "city": "Mountain View", + "postal_code": "94043", + "address": "Frans Duwaerstraat 34\n1318 AC\nAlmere\nNETHERLANDS", + "geo": { + "latitude": 37.4224764, + "longitude": -122.0842499 + }, + "floor": "4", + "suite": "Suite 200" + }, + { + "updated": null, + "country": "US", + "city": "Mountain View", + "postal_code": "94043", + "address": "Frans Duwaerstraat 34\n1318 AC ALMERE\nThe Netherlands", + "geo": { + "latitude": 37.4224764, + "longitude": -122.0842499 + }, + "floor": "4", + "suite": "Suite 200" + }, + { + "updated": null, + "country": "US", + "city": "Mountain View", + "postal_code": "94043", + "address": "Frans Duwaerstraat 34\n1318AC Almere\nNetherlands", + "geo": { + "latitude": 37.4224764, + "longitude": -122.0842499 + }, + "floor": "4", + "suite": "Suite 200" + }, + { + "updated": null, + "country": "US", + "city": "Mountain View", + "postal_code": "94043", + "address": "Kanaalstraat 89-BS", + "geo": { + "latitude": 37.4224764, + "longitude": -122.0842499 + }, + "floor": "4", + "suite": "Suite 200" + }, + { + "updated": null, + "country": "US", + "city": "Mountain View", + "postal_code": "94043", + "address": "Kruier 1\n1567LA Assendelft\nNetherlands", + "geo": { + "latitude": 37.4224764, + "longitude": -122.0842499 + }, + "floor": "4", + "suite": "Suite 200" + }, + { + "updated": null, + "country": "US", + "city": "Mountain View", + "postal_code": "94043", + "address": "Maasstraat 16\n1442RV Purmerend\nNetherlands", + "geo": { + "latitude": 37.4224764, + "longitude": -122.0842499 + }, + "floor": "4", + "suite": "Suite 200" + }, + { + "updated": null, + "country": "US", + "city": "Mountain View", + "postal_code": "94043", + "address": "NETHERLANDS", + "geo": { + "latitude": 37.4224764, + "longitude": -122.0842499 + }, + "floor": "4", + "suite": "Suite 200" + }, + { + "updated": null, + "country": "US", + "city": "Mountain View", + "postal_code": "94043", + "address": "Purmer 60, 8244AT Lelystad", + "geo": { + "latitude": 37.4224764, + "longitude": -122.0842499 + }, + "floor": "4", + "suite": "Suite 200" + }, + { + "updated": null, + "country": "US", + "city": "Mountain View", + "postal_code": "94043", + "address": "Wamberg 26-1\n1083CV\nAmsterdam\nNETHERLANDS", + "geo": { + "latitude": 37.4224764, + "longitude": -122.0842499 + }, + "floor": "4", + "suite": "Suite 200" + }, + { + "updated": null, + "country": "US", + "city": "Mountain View", + "postal_code": "94043", + "address": "Wandeling 8\n4301 JT Zierikzee\nNetherlands", + "geo": { + "latitude": 37.4224764, + "longitude": -122.0842499 + }, + "floor": "4", + "suite": "Suite 200" + } + ], + "contacts": [ + { + "created": null, + "updated": null, + "name": "Jasper Backer", + "roles": [ + "technical" + ], + "phone": "+31 6 83708052", + "email": null + }, + { + "created": null, + "updated": null, + "name": "Jelle Luteijn", + "roles": [ + "admin", + "technical" + ], + "phone": null, + "email": "hostmaster@luje.net" + }, + { + "created": null, + "updated": null, + "name": "Jurrian van Iersel", + "roles": [ + "technical" + ], + "phone": "+31 85 876 8785", + "email": "ripe@jurrian.vaniersel.net" + }, + { + "created": null, + "updated": null, + "name": "Mark Scholten", + "roles": [ + "technical" + ], + "phone": "+31 6 42408602", + "email": null + }, + { + "created": null, + "updated": null, + "name": "Menno Wouter Thomas Spaans", + "roles": [ + "admin" + ], + "phone": "+31 6 24206858", + "email": "menno@someones.net" + }, + { + "created": null, + "updated": null, + "name": "Netwerkvereniging Coloclue", + "roles": [ + "abuse", + "admin", + "registrant", + "technical" + ], + "phone": "+31 6 51387718", + "email": "abuse@coloclue.net" + }, + { + "created": null, + "updated": null, + "name": "Niels Raijer", + "roles": [ + "technical" + ], + "phone": "+31 6 54918205", + "email": null + }, + { + "created": null, + "updated": null, + "name": "Paul de Weerd", + "roles": [ + "admin", + "technical" + ], + "phone": "+31 6 51387718", + "email": "weerd@weirdnet.nl" + }, + { + "created": null, + "updated": null, + "name": "Rogier Krieger", + "roles": [ + "technical" + ], + "phone": "+31 15 212 8820", + "email": "hostmaster@bytepark.net" + }, + { + "created": null, + "updated": null, + "name": "Tijn Buijs", + "roles": [ + "admin", + "technical" + ], + "phone": "+31 6 13962562", + "email": "contact@cybertinus.nl" + }, + { + "created": null, + "updated": null, + "name": "Tim de Boer", + "roles": [ + "technical" + ], + "phone": "+31 6 40282615", + "email": null + } + ], + "sources": [ + { + "created": "2014-11-17T14:28:43-05:00", + "updated": "2018-10-24T22:58:16-04:00", + "handle": "AS63311", + "urls": [ + "https://rdap.org/autnum/63311", + "https://rdap.org/autnum/63311" + ], + "description": null + }, + { + "created": "2003-06-23T14:40:34Z", + "updated": "2023-11-27T23:02:44Z", + "handle": "AS8283", + "urls": [ + "https://rdap.org/autnum/8283", + "https://rdap.org/autnum/8283" + ], + "description": null + } + ] +} \ No newline at end of file diff --git a/tests/data/normalize/autnum/8283.input b/tests/data/normalize/autnum/8283.input new file mode 100644 index 0000000..f39cdf0 --- /dev/null +++ b/tests/data/normalize/autnum/8283.input @@ -0,0 +1,1468 @@ +{ + "handle": "AS8283", + "startAutnum": 8283, + "endAutnum": 8283, + "name": "COLOCLUE-AS", + "status": [ + "active" + ], + "entities": [ + { + "handle": "CLUE1-RIPE", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "Netwerkvereniging Coloclue" + ], + [ + "kind", + {}, + "text", + "group" + ], + [ + "adr", + { + "label": "Frans Duwaerstraat 34\n1318AC Almere\nNetherlands" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "tel", + { + "type": "voice" + }, + "text", + "+31651387718" + ], + [ + "email", + { + "type": "email" + }, + "text", + "ops@coloclue.net" + ], + [ + "email", + { + "type": "email" + }, + "text", + "routers@coloclue.net" + ], + [ + "email", + { + "type": "abuse" + }, + "text", + "abuse@coloclue.net" + ] + ] + ], + "roles": [ + "technical", + "administrative" + ], + "links": [ + { + "value": "https://rdap.db.ripe.net/autnum/8283", + "rel": "self", + "href": "https://rdap.db.ripe.net/entity/CLUE1-RIPE" + }, + { + "value": "http://www.ripe.net/data-tools/support/documentation/terms", + "rel": "copyright", + "href": "http://www.ripe.net/data-tools/support/documentation/terms" + } + ], + "objectClassName": "entity" + }, + { + "handle": "COLOCLUE-MNT", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "coloclue-mnt" + ], + [ + "kind", + {}, + "text", + "individual" + ] + ] + ], + "roles": [ + "registrant" + ], + "links": [ + { + "value": "https://rdap.db.ripe.net/autnum/8283", + "rel": "self", + "href": "https://rdap.db.ripe.net/entity/COLOCLUE-MNT" + }, + { + "value": "http://www.ripe.net/data-tools/support/documentation/terms", + "rel": "copyright", + "href": "http://www.ripe.net/data-tools/support/documentation/terms" + } + ], + "objectClassName": "entity" + }, + { + "handle": "ORG-NC22-RIPE", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "Netwerkvereniging Coloclue" + ], + [ + "kind", + {}, + "text", + "org" + ], + [ + "adr", + { + "label": "Frans Duwaerstraat 34\n1318 AC\nAlmere\nNETHERLANDS" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "tel", + { + "type": "voice" + }, + "text", + "+31651387718" + ], + [ + "email", + { + "type": "email" + }, + "text", + "ops@coloclue.net" + ] + ] + ], + "roles": [ + "registrant" + ], + "links": [ + { + "value": "https://rdap.db.ripe.net/autnum/8283", + "rel": "self", + "href": "https://rdap.db.ripe.net/entity/ORG-NC22-RIPE" + }, + { + "value": "http://www.ripe.net/data-tools/support/documentation/terms", + "rel": "copyright", + "href": "http://www.ripe.net/data-tools/support/documentation/terms" + } + ], + "objectClassName": "entity" + }, + { + "handle": "RIPE-NCC-END-MNT", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "RIPE-NCC-END-MNT" + ], + [ + "kind", + {}, + "text", + "individual" + ], + [ + "org", + {}, + "text", + "ORG-NCC1-RIPE" + ] + ] + ], + "roles": [ + "registrant" + ], + "links": [ + { + "value": "https://rdap.db.ripe.net/autnum/8283", + "rel": "self", + "href": "https://rdap.db.ripe.net/entity/RIPE-NCC-END-MNT" + }, + { + "value": "http://www.ripe.net/data-tools/support/documentation/terms", + "rel": "copyright", + "href": "http://www.ripe.net/data-tools/support/documentation/terms" + } + ], + "objectClassName": "entity" + }, + { + "handle": "CLUE1-RIPE", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "Netwerkvereniging Coloclue" + ], + [ + "kind", + {}, + "text", + "group" + ], + [ + "adr", + { + "label": "Frans Duwaerstraat 34\n1318AC Almere\nNetherlands" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "tel", + { + "type": "voice" + }, + "text", + "+31651387718" + ], + [ + "email", + { + "type": "email" + }, + "text", + "ops@coloclue.net" + ], + [ + "email", + { + "type": "email" + }, + "text", + "routers@coloclue.net" + ], + [ + "email", + { + "type": "abuse" + }, + "text", + "abuse@coloclue.net" + ] + ] + ], + "roles": [ + "abuse" + ], + "entities": [ + { + "handle": "BCKR1-RIPE", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "Jasper Backer" + ], + [ + "kind", + {}, + "text", + "individual" + ], + [ + "adr", + { + "label": "Kanaalstraat 89-BS" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "tel", + { + "type": "voice" + }, + "text", + "+31683708052" + ] + ] + ], + "roles": [ + "technical" + ], + "links": [ + { + "value": "https://rdap.db.ripe.net/autnum/8283", + "rel": "self", + "href": "https://rdap.db.ripe.net/entity/BCKR1-RIPE" + }, + { + "value": "http://www.ripe.net/data-tools/support/documentation/terms", + "rel": "copyright", + "href": "http://www.ripe.net/data-tools/support/documentation/terms" + } + ], + "objectClassName": "entity" + }, + { + "handle": "COLOCLUE-MNT", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "coloclue-mnt" + ], + [ + "kind", + {}, + "text", + "individual" + ] + ] + ], + "roles": [ + "registrant" + ], + "links": [ + { + "value": "https://rdap.db.ripe.net/autnum/8283", + "rel": "self", + "href": "https://rdap.db.ripe.net/entity/COLOCLUE-MNT" + }, + { + "value": "http://www.ripe.net/data-tools/support/documentation/terms", + "rel": "copyright", + "href": "http://www.ripe.net/data-tools/support/documentation/terms" + } + ], + "objectClassName": "entity" + }, + { + "handle": "JVI-RIPE", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "Jurrian van Iersel" + ], + [ + "kind", + {}, + "text", + "individual" + ], + [ + "adr", + { + "label": "Wandeling 8\n4301 JT Zierikzee\nNetherlands" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "tel", + { + "type": "voice" + }, + "text", + "+31 85 8768785" + ], + [ + "email", + { + "type": "email" + }, + "text", + "ripe@jurrian.vaniersel.net" + ] + ] + ], + "roles": [ + "technical" + ], + "links": [ + { + "value": "https://rdap.db.ripe.net/autnum/8283", + "rel": "self", + "href": "https://rdap.db.ripe.net/entity/JVI-RIPE" + }, + { + "value": "http://www.ripe.net/data-tools/support/documentation/terms", + "rel": "copyright", + "href": "http://www.ripe.net/data-tools/support/documentation/terms" + } + ], + "objectClassName": "entity" + }, + { + "handle": "LUJE-RIPE", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "Jelle Luteijn" + ], + [ + "kind", + {}, + "text", + "individual" + ], + [ + "adr", + { + "label": "NETHERLANDS" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "tel", + { + "type": "voice" + }, + "text", + "+31" + ], + [ + "email", + { + "type": "email" + }, + "text", + "hostmaster@luje.net" + ] + ] + ], + "roles": [ + "technical", + "administrative" + ], + "links": [ + { + "value": "https://rdap.db.ripe.net/autnum/8283", + "rel": "self", + "href": "https://rdap.db.ripe.net/entity/LUJE-RIPE" + }, + { + "value": "http://www.ripe.net/data-tools/support/documentation/terms", + "rel": "copyright", + "href": "http://www.ripe.net/data-tools/support/documentation/terms" + } + ], + "objectClassName": "entity" + }, + { + "handle": "MS44437-RIPE", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "Mark Scholten" + ], + [ + "kind", + {}, + "text", + "individual" + ], + [ + "adr", + { + "label": "Purmer 60, 8244AT Lelystad" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "tel", + { + "type": "voice" + }, + "text", + "+31(0)642408602" + ] + ] + ], + "roles": [ + "technical" + ], + "links": [ + { + "value": "https://rdap.db.ripe.net/autnum/8283", + "rel": "self", + "href": "https://rdap.db.ripe.net/entity/MS44437-RIPE" + }, + { + "value": "http://www.ripe.net/data-tools/support/documentation/terms", + "rel": "copyright", + "href": "http://www.ripe.net/data-tools/support/documentation/terms" + } + ], + "objectClassName": "entity" + }, + { + "handle": "MWTS1-RIPE", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "Menno Wouter Thomas Spaans" + ], + [ + "kind", + {}, + "text", + "individual" + ], + [ + "adr", + { + "label": "Maasstraat 16\n1442RV Purmerend\nNetherlands" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "tel", + { + "type": "voice" + }, + "text", + "+31 6 24206858" + ], + [ + "email", + { + "type": "email" + }, + "text", + "menno@someones.net" + ] + ] + ], + "roles": [ + "administrative" + ], + "links": [ + { + "value": "https://rdap.db.ripe.net/autnum/8283", + "rel": "self", + "href": "https://rdap.db.ripe.net/entity/MWTS1-RIPE" + }, + { + "value": "http://www.ripe.net/data-tools/support/documentation/terms", + "rel": "copyright", + "href": "http://www.ripe.net/data-tools/support/documentation/terms" + } + ], + "objectClassName": "entity" + }, + { + "handle": "NMR5-RIPE", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "Niels Raijer" + ], + [ + "kind", + {}, + "text", + "individual" + ], + [ + "adr", + { + "label": "Kruier 1\n1567LA Assendelft\nNetherlands" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "tel", + { + "type": "voice" + }, + "text", + "+31 6 54918205" + ] + ] + ], + "roles": [ + "technical" + ], + "links": [ + { + "value": "https://rdap.db.ripe.net/autnum/8283", + "rel": "self", + "href": "https://rdap.db.ripe.net/entity/NMR5-RIPE" + }, + { + "value": "http://www.ripe.net/data-tools/support/documentation/terms", + "rel": "copyright", + "href": "http://www.ripe.net/data-tools/support/documentation/terms" + } + ], + "objectClassName": "entity" + }, + { + "handle": "PDW-RIPE", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "Paul de Weerd" + ], + [ + "kind", + {}, + "text", + "individual" + ], + [ + "adr", + { + "label": "Frans Duwaerstraat 34\n1318 AC ALMERE\nThe Netherlands" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "tel", + { + "type": "voice" + }, + "text", + "+31651387718" + ], + [ + "email", + { + "type": "email" + }, + "text", + "weerd@weirdnet.nl" + ] + ] + ], + "roles": [ + "technical", + "administrative" + ], + "links": [ + { + "value": "https://rdap.db.ripe.net/autnum/8283", + "rel": "self", + "href": "https://rdap.db.ripe.net/entity/PDW-RIPE" + }, + { + "value": "http://www.ripe.net/data-tools/support/documentation/terms", + "rel": "copyright", + "href": "http://www.ripe.net/data-tools/support/documentation/terms" + } + ], + "objectClassName": "entity" + }, + { + "handle": "RAK24-RIPE", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "Rogier Krieger" + ], + [ + "kind", + {}, + "text", + "individual" + ], + [ + "adr", + { + "label": "BytePark\nGruttostraat 63\nNL-2665 EL Bleiswijk\nThe Netherlands" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "tel", + { + "type": "voice" + }, + "text", + "+31 15 212 8820" + ], + [ + "email", + { + "type": "email" + }, + "text", + "hostmaster@bytepark.net" + ] + ] + ], + "roles": [ + "technical" + ], + "links": [ + { + "value": "https://rdap.db.ripe.net/autnum/8283", + "rel": "self", + "href": "https://rdap.db.ripe.net/entity/RAK24-RIPE" + }, + { + "value": "http://www.ripe.net/data-tools/support/documentation/terms", + "rel": "copyright", + "href": "http://www.ripe.net/data-tools/support/documentation/terms" + } + ], + "objectClassName": "entity" + }, + { + "handle": "TDB189-RIPE", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "Tim de Boer" + ], + [ + "kind", + {}, + "text", + "individual" + ], + [ + "adr", + { + "label": "Wamberg 26-1\n1083CV\nAmsterdam\nNETHERLANDS" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "tel", + { + "type": "voice" + }, + "text", + "+31640282615" + ] + ] + ], + "roles": [ + "technical" + ], + "links": [ + { + "value": "https://rdap.db.ripe.net/autnum/8283", + "rel": "self", + "href": "https://rdap.db.ripe.net/entity/TDB189-RIPE" + }, + { + "value": "http://www.ripe.net/data-tools/support/documentation/terms", + "rel": "copyright", + "href": "http://www.ripe.net/data-tools/support/documentation/terms" + } + ], + "objectClassName": "entity" + }, + { + "handle": "TIJN-RIPE", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "Tijn Buijs" + ], + [ + "kind", + {}, + "text", + "individual" + ], + [ + "adr", + { + "label": "Bijsterveld 1\n4902 ZN Oosterhout\nThe Netherlands" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "tel", + { + "type": "voice" + }, + "text", + "+31613962562" + ], + [ + "email", + { + "type": "email" + }, + "text", + "contact@cybertinus.nl" + ] + ] + ], + "roles": [ + "technical", + "administrative" + ], + "links": [ + { + "value": "https://rdap.db.ripe.net/autnum/8283", + "rel": "self", + "href": "https://rdap.db.ripe.net/entity/TIJN-RIPE" + }, + { + "value": "http://www.ripe.net/data-tools/support/documentation/terms", + "rel": "copyright", + "href": "http://www.ripe.net/data-tools/support/documentation/terms" + } + ], + "objectClassName": "entity" + } + ], + "objectClassName": "entity" + } + ], + "remarks": [ + { + "description": [ + "Netwerkvereniging Coloclue, Amsterdam, Netherlands", + "-----------------------------------------------------", + "Transit", + "Atom86", + "", + "True", + "Fiberring", + "Fusix Networks", + "Peering", + "AMS-IX Office", + "AMS-IX Route Servers", + "NL-IX Route Servers", + "OTEGLOBE", + "TalkTalk", + "Hurricane Electric", + "InterBox Internet", + "OpenCarrier", + "BIT", + "TNG", + "Opteamax", + "IP-Max SA", + "MaxiTEL", + "MCKAYCOM Ltd", + "Packet Clearing House", + "Liquid Telecom", + "MANDA", + "Colocenter.nl", + "Interconnect Services B.V.", + "OpenPeering", + "Solcon", + "Eurofiber", + "OpenDNS", + "Akamai", + "Previder", + "RIPE NCC RIS Project", + "Netflix", + "PT Comunicacoes S.A.", + "NTT", + "Not Surfnet", + "Breedband Nederland", + "Leaseweb", + "Init7", + "Apple Inc", + "Duocast", + "Iunxi", + "Tuxis Internet Engineering", + "DELTA Fiber Nederland BV", + "NORDUNet", + "Aire Networks del Mediterraneo S.L.U.", + "CESNET z.s.p.o.", + "Internet Systems Consortium, F-Root Operator", + "Telenor", + "T-mobile thuis", + "Emirates Telecommunications Corporation", + "Gelderland Internet Exchange", + "Jaguar Network", + "IP-Only", + "A2B Internet", + "Netnod", + "TransIP", + "Iceland Telecom Ltd.", + "Viatel Ltd", + "IT-Ernity Internet Services B.V.", + "MTS", + "Redraw Internet", + "Amazon", + "GTT", + "BT", + "Core Backbone GmbH", + "Booking.com", + "PCCW Global", + "Job Snijders", + "BGPMUX / PEERING TestBed", + "AS112 Project (Hosted by RIPE NCC at AMS-IX)", + "Claranet Ltd.", + "Microsoft", + "Nextlayer.at", + "Serverius", + "Wikimedia Foundation", + "Hostserver.de", + "Automattic Inc.", + "AFNIC", + "OVH", + "I3D", + "Vodafone Group", + "SOCO Network Solutions GmbH", + "Zayo France", + "Cloudflare", + "Facebook", + "Liberty Global", + "Zayo / Abovenet", + "Deutsche Telekom", + "TeliaSonera International Carrier", + "ColocationIX", + "NFOrce Entertainment B.V.", + "Knipp Medien und Kommunikation GmbH", + "Qonnected B.V.", + "Computerline Group", + "GlobalConnect Group", + "Micron21", + "Sprint", + "Twitch / Justin.tv", + "Global Network Management", + "SURF", + "NovoServe", + "Yahoo", + "PCextreme", + "Belgacom International Carrier Services (aka BICS)", + "Google", + "Zain Group", + "SIG-Telecom", + "Bandwidth Technologies", + "Vivor", + "Tata", + "Tele2 SWIPNet", + "Weservit B.V.", + "Fastly inc.", + "PeakFactory", + "Telecom Italia Sparkle", + "packet.net", + "Telxius", + "Asteroid Route Collector", + "Asteroid MLP Route Server", + "Twitter", + "SPEED-IX route server peers", + "Freifunk Nordwest e.V", + "e-utp.net", + "Belcloud", + "BlackGATE BV", + "T-2 d.o.o.", + "Canadian Internet Registration Authority", + "Dropbox Inc", + "Accenture BV", + "myLoc managed IT AG", + "Redhosting B.V.", + "Chronos", + "Servperso-Systems", + "Stichting Nationale Beheersorganisatie Internet Providers (NBIP)", + "Hetzner Online GmbH", + "Valve Corporation", + "Parknet F.M.B.A.", + "CLOUDITY Network", + "AltusHost B.V.", + "Mythic Beasts Ltd", + "CS Net", + "Cynthia Maja Revstrom", + "Stichting EventInfra", + "TRIX", + "EdgeCast Networks Inc", + "Prolocation B.V.", + "Speakup", + "Seacom", + "SAM Office B.V.", + "OSN Online Service Nuernberg GmbH", + "Tencent Holdings Ltd.", + "Green Mini host BV", + "WD6.net B.V.", + "Meanie", + "ADES / NetLogics", + "Nederlandse Publieke Omroep - NPO", + "Alpine North", + "Greenhost", + "Freedom Internet", + "Unithost Internet BV", + "Nedap NV", + "Serverion B.V.", + "Frys-IX Route Servers", + "IPng Networks GmbH", + "DE-CIX Frankfurt Route Servers", + "France-IX Paris Route Servers", + "SwissIX Route Servers", + "communityrack.org", + "Cato Networks Ltd", + "HostIn", + "equada network GmbH", + "NetActuate Inc.", + "Solutions4xs", + "Kviknet.dk ApS", + "NIKHEF", + "China Telecom", + "IONOS SE", + "Earthlink Telecommunications Equipment Trading & Services DMCC", + "OpenFiber", + "ESEVEN DevOps", + "LOCIX NL Route Servers", + "Sentia Labs", + "Gandi", + "Xyphen IT", + "NetOne NL", + "LUJE.net", + "INTERIX Route Servers", + "Ursin Filli", + "Apple Communications Ltd.", + "Sipartech SAS", + "Nick Bouwhuis", + "CBWS", + "Peering with Coloclue", + "Coloclue is present at the following exchanges:", + "AMS-IX: 80.249.211.161 / 2001:7f8:1::a500:8283:1", + "Asteroid Amsterdam: 185.1.94.15 / 2001:7f8:b6::205b:1", + "DE-CIX Frankfurt: 80.80.197.51 / 2001:7b8::205b:0:1", + "France-IX Paris: 37.49.238.29 / 2001:7b8:54::2:29", + "Frys-IX: 185.1.203.140 / 2001:7b8:10f::205b:140", + "Frys-IX: 185.1.203.187 / 2001:7f8:10f::205b:187", + "NL-IX: 193.239.117.111 / 2001:7f8:13::a500:8283:1", + "NL-IX: 193.239.117.203 / 2001:7f8:13::a500:8283:2", + "Speed-IX: 185.1.222.16 / 2001:7f8:b7::a500:8283:1", + "SwissIX: 91.206.53.24 / 2001:7b8:24::118", + "There are two methods:", + "Modern way:", + "- Send a pull request on github:", + "http://github.com/coloclue/peering", + "- Modify peers.yaml", + "- Wait for Travis' OK signal", + "- Peering establishes automatically!", + "Old-fashioned way:", + "- Send an email to routers@coloclue.net", + "- Please do include your as-set or route-set", + "- Wait for AS8283 engineer to update peers.yaml", + "Abuse issues should be reported to abuse@coloclue.net", + "Abuse related e-mail to any other address(es)", + "will *not* be read", + "Routing Policy", + "- All peering sessions are strictly filtered based on IRR data", + "- RPKI INVALID prefixes are rejected", + "BGP Communities and Traffic Engineering", + "=======================================", + "The following BGP Community scheme draws inspiration from:", + "draft-ietf-grow-large-communities-usage", + "https://tools.ietf.org/html/draft-ietf-grow-large-communities-usage", + "INFORMATIONAL COMMUNITIES:", + "==========================", + "RFC 1997 | Large | Meaning (Informational)", + "----------|-------------|-------------------------------------", + "8283:1 | 8283:0:1 | peering routes", + "8283:2 | 8283:0:2 | downstream routes", + "----------+-------------+-------------------------------------", + "8283:101 | 8283:5:1 | Accepted from peer because of valid IRR entry", + "8283:102 | 8283:5:2 | Accepted from peer because of valid ROA", + "8283:104 | 8283:5:4 | Accepted while RPKI invalid because it is added to our whitelist", + "8283:10 | 8283:6:10 | received from TRUE", + "8283:11 | 8283:6:11 | received from Atom86", + "8283:14 | 8283:6:14 | received from FiberRing", + "8283:15 | 8283:6:15 | received from Fusix", + "- | 8283:8:26 | received via AMS-IX", + "- | 8283:8:64 | received via NL-IX", + "- | 8283:8:1812 | received via Asteroid", + "- | 8283:8:1842 | received via SpeedIX", + "- | 8283:8:3512 | received via Frys-IX", + "- | 8283:8:31 | received via DE-CIX", + "- | 8283:8:359 | received via France-IX", + "- | 8283:8:60 | received via SwissIX", + "- | 8283:8:2601 | received via LocIX Netherlands", + "| Rejection Reasons", + "- | 8283:7:1 | More specifics covering AS8283 space are considered hijacks", + "- | 8283:7:2 | Somewhere in the AS_PATH a Bogon ASN is present (0, 23456, 64496..65534, 4200000000+)", + "- | 8283:7:3 | The prefix is Bogon garbage (rfc1918, rfc4291 etc)", + "- | 8283:7:4 | The prefix is an RPKI Invalid and as such rejected", + "- | 8283:7:5 | The route's prefix length is unacceptable (too small or too large)", + "- | 8283:7:6 | There is no IRR object that covers this route announcement", + "- | 8283:7:7 | Coloclue member is not authorized to announce this route", + "ACTION COMMUNITIES:", + "===================", + "RFC 1997 | Large | Meaning (Action)", + "8283:50 | 8283:1:50 | Set LOCAL_PREF to 50 (default is 100)", + "8283:150 | 8283:1:150 | Set LOCAL_PREF to 150 (default is 100)", + "8283:202 | 8283:2:0 | Prepend 8283 once to all eBGP peers", + "- | 8283:2:nnn | Prepend 8283 once to peer AS nnn", + "8283:203 | 8283:3:0 | Prepend 8283 twice to all eBGP peers", + "- | 8283:3:nnn | Prepend 8283 twice to peer AS nnn", + "8283:204 | 8283:4:0 | Do not export to eBGP peers", + "- | 8283:4:nnn | Do not export to peer AS nnn", + "65535:0 | - | G-SHUT [draft-ietf-grow-bgp-gshut]", + "65535:666 | - | BLACKHOLE [RFC 7999]", + "MEMBERS ONLY:", + "=============", + "----------+------------+-------------------------------------", + "8283:50 | 8283:1:50 | Set LOCAL_PREF to 50 (default is 500)", + "| *** WARNING: default is 100 for other routes, so your prefixes may be become unreachable!", + "8283:450 | 8283:1:450 | Set LOCAL_PREF to 450 (default is 500)", + "8283:550 | 8283:1:550 | Set LOCAL_PREF to 550 (default is 500)", + "8283:301 | 8283:301:0 | Do not export to other Coloclue members", + "8283:302 | 8283:302:0 | Do not export to transit providers", + "8283:303 | 8283:303:0 | Do not export to direct peers", + "Examples:", + "---------", + "8283:2:3320 - prepend 8283 to AS 3320 (path on export will be 8283_8283_you)", + "8283:3:200023 - prepend 8283 to AS 200023 (path on export will be 8283_8283_8283_you)", + "8283:4:198203 - do not announce the route over any BGP sessions with AS 198203", + "(notice how you can 32-bit ASNs? :-)" + ] + } + ], + "links": [ + { + "value": "https://rdap.db.ripe.net/autnum/8283", + "rel": "self", + "href": "https://rdap.db.ripe.net/autnum/8283" + }, + { + "value": "http://www.ripe.net/data-tools/support/documentation/terms", + "rel": "copyright", + "href": "http://www.ripe.net/data-tools/support/documentation/terms" + } + ], + "events": [ + { + "eventAction": "registration", + "eventDate": "2003-06-23T14:40:34Z" + }, + { + "eventAction": "last changed", + "eventDate": "2023-11-27T23:02:44Z" + } + ], + "rdapConformance": [ + "nro_rdap_profile_asn_flat_0", + "cidr0", + "rdap_level_0", + "nro_rdap_profile_0" + ], + "notices": [ + { + "title": "Filtered", + "description": [ + "This output has been filtered." + ] + }, + { + "title": "Whois Inaccuracy Reporting", + "description": [ + "If you see inaccuracies in the results, please visit:" + ], + "links": [ + { + "value": "https://rdap.db.ripe.net/autnum/8283", + "rel": "inaccuracy-report", + "href": "https://www.ripe.net/contact-form?topic=ripe_dbm&show_form=true", + "type": "text/html" + } + ] + }, + { + "title": "Source", + "description": [ + "Objects returned came from source", + "RIPE" + ] + }, + { + "title": "Terms and Conditions", + "description": [ + "This is the RIPE Database query service. The objects are in RDAP format." + ], + "links": [ + { + "value": "https://rdap.db.ripe.net/autnum/8283", + "rel": "terms-of-service", + "href": "http://www.ripe.net/db/support/db-terms-conditions.pdf", + "type": "application/pdf" + } + ] + } + ], + "port43": "whois.ripe.net", + "objectClassName": "autnum" +} \ No newline at end of file diff --git a/tests/data/normalize/domain/20c.com.expected b/tests/data/normalize/domain/20c.com.expected new file mode 100644 index 0000000..43ccd48 --- /dev/null +++ b/tests/data/normalize/domain/20c.com.expected @@ -0,0 +1,74 @@ +{ + "created": "2004-06-28T18:28:14Z", + "updated": "2024-06-25T03:31:37Z", + "name": "20C.COM", + "handle": "123664426_DOMAIN_COM-VRSN", + "dns_sec": "insecure", + "nameservers": [ + { + "host": "NS-1468.AWSDNS-55.ORG" + }, + { + "host": "NS-1771.AWSDNS-29.CO.UK" + }, + { + "host": "NS-327.AWSDNS-40.COM" + }, + { + "host": "NS-545.AWSDNS-04.NET" + } + ], + "contacts": [], + "sources": [ + { + "created": "2014-11-17T14:28:43-05:00", + "updated": "2018-10-24T22:58:16-04:00", + "handle": "AS63311", + "urls": [ + "https://rdap.org/autnum/63311", + "https://rdap.org/autnum/63311" + ], + "description": null + }, + { + "created": "2003-06-23T14:40:34Z", + "updated": "2023-11-27T23:02:44Z", + "handle": "AS8283", + "urls": [ + "https://rdap.org/autnum/8283", + "https://rdap.org/autnum/8283" + ], + "description": null + }, + { + "created": null, + "updated": "2017-09-21T23:55:48Z", + "handle": "CLUE1-RIPE", + "urls": [ + "https://rdap.org/entity/CLUE1-RIPE", + "https://rdap.org/entity/CLUE1-RIPE" + ], + "description": null + }, + { + "created": null, + "updated": "2017-06-13T22:21:32Z", + "handle": "DJVG", + "urls": [ + "https://rdap.org/entity/DJVG", + "https://rdap.org/entity/DJVG" + ], + "description": null + }, + { + "created": "2004-06-28T18:28:14Z", + "updated": "2024-06-25T03:31:37Z", + "handle": "123664426_DOMAIN_COM-VRSN", + "urls": [ + "https://rdap.org/domain/20c.com", + "https://rdap.org/domain/20c.com" + ], + "description": null + } + ] +} \ No newline at end of file diff --git a/tests/data/normalize/domain/20c.com.input b/tests/data/normalize/domain/20c.com.input new file mode 100644 index 0000000..03e8fe7 --- /dev/null +++ b/tests/data/normalize/domain/20c.com.input @@ -0,0 +1,31 @@ +{ + "name": "20C.COM", + "handle": "123664426_DOMAIN_COM-VRSN", + "dns_sec": "insecure", + "contacts": [], + "nameservers": [ + { + "host": "NS-1468.AWSDNS-55.ORG" + }, + { + "host": "NS-1771.AWSDNS-29.CO.UK" + }, + { + "host": "NS-327.AWSDNS-40.COM" + }, + { + "host": "NS-545.AWSDNS-04.NET" + } + ], + "sources": [ + { + "handle": "123664426_DOMAIN_COM-VRSN", + "urls": [ + "https://rdap.org/domain/20c.com", + "https://rdap.verisign.com/com/v1/domain/20c.com" + ], + "created": "2004-06-28T18:28:14Z", + "updated": "2024-06-25T03:31:37Z" + } + ] +} \ No newline at end of file diff --git a/tests/data/normalize/entity/CLUE1-RIPE.expected b/tests/data/normalize/entity/CLUE1-RIPE.expected new file mode 100644 index 0000000..b0dcaa2 --- /dev/null +++ b/tests/data/normalize/entity/CLUE1-RIPE.expected @@ -0,0 +1,63 @@ +{ + "created": null, + "updated": "2017-09-21T23:55:48Z", + "name": "CLUE1-RIPE", + "organization": null, + "locations": [ + { + "updated": "2017-09-21T23:55:48Z", + "country": "US", + "city": "Mountain View", + "postal_code": "94043", + "address": "Frans Duwaerstraat 34\n1318AC Almere\nNetherlands", + "geo": { + "latitude": 37.4224764, + "longitude": -122.0842499 + }, + "floor": "4", + "suite": "Suite 200" + } + ], + "contacts": [ + { + "created": null, + "updated": "2017-09-21T23:55:48Z", + "name": "Netwerkvereniging Coloclue", + "roles": [], + "phone": "+31 6 51387718", + "email": "routers@coloclue.net" + } + ], + "sources": [ + { + "created": "2014-11-17T14:28:43-05:00", + "updated": "2018-10-24T22:58:16-04:00", + "handle": "AS63311", + "urls": [ + "https://rdap.org/autnum/63311", + "https://rdap.org/autnum/63311" + ], + "description": null + }, + { + "created": "2003-06-23T14:40:34Z", + "updated": "2023-11-27T23:02:44Z", + "handle": "AS8283", + "urls": [ + "https://rdap.org/autnum/8283", + "https://rdap.org/autnum/8283" + ], + "description": null + }, + { + "created": null, + "updated": "2017-09-21T23:55:48Z", + "handle": "CLUE1-RIPE", + "urls": [ + "https://rdap.org/entity/CLUE1-RIPE", + "https://rdap.org/entity/CLUE1-RIPE" + ], + "description": null + } + ] +} \ No newline at end of file diff --git a/tests/data/normalize/entity/CLUE1-RIPE.input b/tests/data/normalize/entity/CLUE1-RIPE.input new file mode 100644 index 0000000..b3bd076 --- /dev/null +++ b/tests/data/normalize/entity/CLUE1-RIPE.input @@ -0,0 +1,85 @@ +{ + "handle" : "CLUE1-RIPE", + "vcardArray" : [ "vcard", [ [ "version", { }, "text", "4.0" ], [ "fn", { }, "text", "Netwerkvereniging Coloclue" ], [ "kind", { }, "text", "group" ], [ "adr", { + "label" : "Frans Duwaerstraat 34\n1318AC Almere\nNetherlands" + }, "text", null ], [ "tel", { + "type" : "voice" + }, "text", "+31651387718" ], [ "email", { }, "text", "ops@coloclue.net" ], [ "email", { }, "text", "routers@coloclue.net" ] ] ], + "entities" : [ { + "handle" : "COLOCLUE-MNT", + "roles" : [ "registrant" ], + "objectClassName" : "entity" + }, { + "handle" : "JB17421-RIPE", + "roles" : [ "technical" ], + "objectClassName" : "entity" + }, { + "handle" : "JL9785-RIPE", + "roles" : [ "technical" ], + "objectClassName" : "entity" + }, { + "handle" : "JVI-RIPE", + "roles" : [ "technical" ], + "objectClassName" : "entity" + }, { + "handle" : "MS44437-RIPE", + "roles" : [ "administrative", "technical" ], + "objectClassName" : "entity" + }, { + "handle" : "MWTS1-RIPE", + "roles" : [ "administrative" ], + "objectClassName" : "entity" + }, { + "handle" : "NMR5-RIPE", + "roles" : [ "technical" ], + "objectClassName" : "entity" + }, { + "handle" : "NT1031-RIPE", + "roles" : [ "administrative" ], + "objectClassName" : "entity" + }, { + "handle" : "PDW-RIPE", + "roles" : [ "administrative", "technical" ], + "objectClassName" : "entity" + }, { + "handle" : "PEER-RIPE", + "roles" : [ "technical" ], + "objectClassName" : "entity" + }, { + "handle" : "TIJN-RIPE", + "roles" : [ "administrative", "technical" ], + "objectClassName" : "entity" + } ], + "links" : [ { + "value" : "https://rdap.db.ripe.net/entity/CLUE1-RIPE", + "rel" : "self", + "href" : "https://rdap.db.ripe.net/entity/CLUE1-RIPE" + }, { + "value" : "http://www.ripe.net/data-tools/support/documentation/terms", + "rel" : "copyright", + "href" : "http://www.ripe.net/data-tools/support/documentation/terms" + } ], + "events" : [ { + "eventAction" : "last changed", + "eventDate" : "2017-09-21T23:55:48Z" + } ], + "rdapConformance" : [ "rdap_level_0" ], + "notices" : [ { + "title" : "Filtered", + "description" : [ "This output has been filtered." ] + }, { + "title" : "Source", + "description" : [ "Objects returned came from source", "RIPE" ] + }, { + "title" : "Terms and Conditions", + "description" : [ "This is the RIPE Database query service. The objects are in RDAP format." ], + "links" : [ { + "value" : "https://rdap.db.ripe.net/entity/CLUE1-RIPE", + "rel" : "terms-of-service", + "href" : "http://www.ripe.net/db/support/db-terms-conditions.pdf", + "type" : "application/pdf" + } ] + } ], + "port43" : "whois.ripe.net", + "objectClassName" : "entity" +} \ No newline at end of file diff --git a/tests/data/normalize/entity/DJVG.expected b/tests/data/normalize/entity/DJVG.expected new file mode 100644 index 0000000..a71d961 --- /dev/null +++ b/tests/data/normalize/entity/DJVG.expected @@ -0,0 +1,73 @@ +{ + "created": null, + "updated": "2017-06-13T22:21:32Z", + "name": "DJVG", + "organization": null, + "locations": [ + { + "updated": "2017-06-13T22:21:32Z", + "country": "US", + "city": "Mountain View", + "postal_code": "94043", + "address": "Postbus 8160\n1180LD Amstelveen\nthe Netherlands", + "geo": { + "latitude": 37.4224764, + "longitude": -122.0842499 + }, + "floor": "4", + "suite": "Suite 200" + } + ], + "contacts": [ + { + "created": null, + "updated": "2017-06-13T22:21:32Z", + "name": "Daan van Gorkum", + "roles": [], + "phone": "+31 20 308 0063", + "email": "daan.vangorkum@vusam.com" + } + ], + "sources": [ + { + "created": "2014-11-17T14:28:43-05:00", + "updated": "2018-10-24T22:58:16-04:00", + "handle": "AS63311", + "urls": [ + "https://rdap.org/autnum/63311", + "https://rdap.org/autnum/63311" + ], + "description": null + }, + { + "created": "2003-06-23T14:40:34Z", + "updated": "2023-11-27T23:02:44Z", + "handle": "AS8283", + "urls": [ + "https://rdap.org/autnum/8283", + "https://rdap.org/autnum/8283" + ], + "description": null + }, + { + "created": null, + "updated": "2017-09-21T23:55:48Z", + "handle": "CLUE1-RIPE", + "urls": [ + "https://rdap.org/entity/CLUE1-RIPE", + "https://rdap.org/entity/CLUE1-RIPE" + ], + "description": null + }, + { + "created": null, + "updated": "2017-06-13T22:21:32Z", + "handle": "DJVG", + "urls": [ + "https://rdap.org/entity/DJVG", + "https://rdap.org/entity/DJVG" + ], + "description": null + } + ] +} \ No newline at end of file diff --git a/tests/data/normalize/entity/DJVG.input b/tests/data/normalize/entity/DJVG.input new file mode 100644 index 0000000..01d656a --- /dev/null +++ b/tests/data/normalize/entity/DJVG.input @@ -0,0 +1,45 @@ +{ + "handle" : "DJVG", + "vcardArray" : [ "vcard", [ [ "version", { }, "text", "4.0" ], [ "fn", { }, "text", "Daan van Gorkum" ], [ "kind", { }, "text", "individual" ], [ "adr", { + "label" : "Postbus 8160\n1180LD Amstelveen\nthe Netherlands" + }, "text", null ], [ "tel", { + "type" : "voice" + }, "text", "+31203080063" ], [ "email", { }, "text", "daan.vangorkum@vusam.com" ] ] ], + "entities" : [ { + "handle" : "VUSAM", + "roles" : [ "registrant" ], + "objectClassName" : "entity" + } ], + "links" : [ { + "value" : "https://rdap.db.ripe.net/entity/DJVG", + "rel" : "self", + "href" : "https://rdap.db.ripe.net/entity/DJVG" + }, { + "value" : "http://www.ripe.net/data-tools/support/documentation/terms", + "rel" : "copyright", + "href" : "http://www.ripe.net/data-tools/support/documentation/terms" + } ], + "events" : [ { + "eventAction" : "last changed", + "eventDate" : "2017-06-13T22:21:32Z" + } ], + "rdapConformance" : [ "rdap_level_0" ], + "notices" : [ { + "title" : "Filtered", + "description" : [ "This output has been filtered." ] + }, { + "title" : "Source", + "description" : [ "Objects returned came from source", "RIPE" ] + }, { + "title" : "Terms and Conditions", + "description" : [ "This is the RIPE Database query service. The objects are in RDAP format." ], + "links" : [ { + "value" : "https://rdap.db.ripe.net/entity/DJVG", + "rel" : "terms-of-service", + "href" : "http://www.ripe.net/db/support/db-terms-conditions.pdf", + "type" : "application/pdf" + } ] + } ], + "port43" : "whois.ripe.net", + "objectClassName" : "entity" +} \ No newline at end of file diff --git a/tests/data/normalize/ip/206.41.110.0.expected b/tests/data/normalize/ip/206.41.110.0.expected new file mode 100644 index 0000000..67654d8 --- /dev/null +++ b/tests/data/normalize/ip/206.41.110.0.expected @@ -0,0 +1,116 @@ +{ + "created": "2014-10-17T18:32:20-04:00", + "updated": "2021-12-14T20:28:45-05:00", + "prefix": "206.41.110.0/24", + "version": 4, + "name": "CHIX", + "type": "DIRECT ALLOCATION", + "status": "active", + "parent": "206.0.0.0/8", + "contacts": [ + { + "created": "2022-07-06T18:47:31-04:00", + "updated": "2024-06-04T14:09:08-04:00", + "name": "Jordan Hazen", + "roles": [ + "technical" + ], + "phone": "+1 904-549-7540", + "email": "jhazen@sbaedge.com" + }, + { + "created": "2021-01-22T13:10:25-05:00", + "updated": "2024-01-23T08:35:36-05:00", + "name": "Network Operations", + "roles": [ + "admin", + "technical" + ], + "phone": null, + "email": "netops@sbaedge.com" + }, + { + "created": "2014-11-19T00:39:22-05:00", + "updated": "2023-09-05T13:27:59-04:00", + "name": "United IX Engineers", + "roles": [ + "abuse", + "technical" + ], + "phone": "+1 877-432-2656", + "email": "neteng@unitedix.net" + }, + { + "created": "2024-05-30T10:27:44-04:00", + "updated": "2024-05-30T10:42:04-04:00", + "name": "Vitalii Kukanov", + "roles": [ + "technical" + ], + "phone": "+1 877-432-2656", + "email": "vkukanov@sbaedge.com" + } + ], + "sources": [ + { + "created": "2014-11-17T14:28:43-05:00", + "updated": "2018-10-24T22:58:16-04:00", + "handle": "AS63311", + "urls": [ + "https://rdap.org/autnum/63311", + "https://rdap.org/autnum/63311" + ], + "description": null + }, + { + "created": "2003-06-23T14:40:34Z", + "updated": "2023-11-27T23:02:44Z", + "handle": "AS8283", + "urls": [ + "https://rdap.org/autnum/8283", + "https://rdap.org/autnum/8283" + ], + "description": null + }, + { + "created": null, + "updated": "2017-09-21T23:55:48Z", + "handle": "CLUE1-RIPE", + "urls": [ + "https://rdap.org/entity/CLUE1-RIPE", + "https://rdap.org/entity/CLUE1-RIPE" + ], + "description": null + }, + { + "created": null, + "updated": "2017-06-13T22:21:32Z", + "handle": "DJVG", + "urls": [ + "https://rdap.org/entity/DJVG", + "https://rdap.org/entity/DJVG" + ], + "description": null + }, + { + "created": "2004-06-28T18:28:14Z", + "updated": "2024-06-25T03:31:37Z", + "handle": "123664426_DOMAIN_COM-VRSN", + "urls": [ + "https://rdap.org/domain/20c.com", + "https://rdap.org/domain/20c.com" + ], + "description": null + }, + { + "created": "2014-10-17T18:32:20-04:00", + "updated": "2021-12-14T20:28:45-05:00", + "handle": "NET-206-41-110-0-1", + "urls": [ + "https://rdap.org/ip/206.41.110.0", + "https://rdap.org/ip/206.41.110.0" + ], + "description": null + } + ] +} \ No newline at end of file diff --git a/tests/data/normalize/ip/206.41.110.0.input b/tests/data/normalize/ip/206.41.110.0.input new file mode 100644 index 0000000..11008e0 --- /dev/null +++ b/tests/data/normalize/ip/206.41.110.0.input @@ -0,0 +1,584 @@ +{ + "rdapConformance": [ + "nro_rdap_profile_0", + "rdap_level_0", + "cidr0", + "arin_originas0" + ], + "notices": [ + { + "title": "Terms of Service", + "description": [ + "By using the ARIN RDAP/Whois service, you are agreeing to the RDAP/Whois Terms of Use" + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "terms-of-service", + "type": "text/html", + "href": "https://www.arin.net/resources/registry/whois/tou/" + } + ] + }, + { + "title": "Whois Inaccuracy Reporting", + "description": [ + "If you see inaccuracies in the results, please visit: " + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "inaccuracy-report", + "type": "text/html", + "href": "https://www.arin.net/resources/registry/whois/inaccuracy_reporting/" + } + ] + }, + { + "title": "Copyright Notice", + "description": [ + "Copyright 1997-2024, American Registry for Internet Numbers, Ltd." + ], + "links": [] + } + ], + "handle": "NET-206-41-110-0-1", + "startAddress": "206.41.110.0", + "endAddress": "206.41.110.255", + "ipVersion": "v4", + "name": "CHIX", + "type": "DIRECT ALLOCATION", + "parentHandle": "NET-206-0-0-0-0", + "remarks": [ + { + "title": "Registration Comments", + "description": [ + "https://unitedix.net/" + ] + } + ], + "events": [ + { + "eventAction": "last changed", + "eventDate": "2021-12-14T20:28:45-05:00" + }, + { + "eventAction": "registration", + "eventDate": "2014-10-17T18:32:20-04:00" + } + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "self", + "type": "application/rdap+json", + "href": "https://rdap.arin.net/registry/ip/206.41.110.0" + }, + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "alternate", + "type": "application/xml", + "href": "https://whois.arin.net/rest/net/NET-206-41-110-0-1" + } + ], + "entities": [ + { + "handle": "UIEL", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "United-IX" + ], + [ + "adr", + { + "label": "603 Discovery Dr\nWest Chicago\nIL\n60185\nUnited States" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "kind", + {}, + "text", + "org" + ] + ] + ], + "roles": [ + "registrant" + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "self", + "type": "application/rdap+json", + "href": "https://rdap.arin.net/registry/entity/UIEL" + }, + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "alternate", + "type": "application/xml", + "href": "https://whois.arin.net/rest/org/UIEL" + } + ], + "events": [ + { + "eventAction": "last changed", + "eventDate": "2024-06-04T14:12:37-04:00" + }, + { + "eventAction": "registration", + "eventDate": "2014-07-11T10:33:06-04:00" + } + ], + "status": [], + "port43": "whois.arin.net", + "objectClassName": "entity", + "remarks": [], + "entities": [ + { + "handle": "UIE-ARIN", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "adr", + { + "label": "603 Discovery Dr\nWest Chicago\nIL\n60185\nUnited States" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "fn", + {}, + "text", + "United IX Engineers" + ], + [ + "org", + {}, + "text", + "United IX Engineers" + ], + [ + "kind", + {}, + "text", + "group" + ], + [ + "email", + {}, + "text", + "neteng@unitedix.net" + ], + [ + "tel", + { + "type": [ + "work", + "voice" + ] + }, + "text", + "+1-877-432-2656" + ] + ] + ], + "roles": [ + "abuse", + "noc", + "technical" + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "self", + "type": "application/rdap+json", + "href": "https://rdap.arin.net/registry/entity/UIE-ARIN" + }, + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "alternate", + "type": "application/xml", + "href": "https://whois.arin.net/rest/poc/UIE-ARIN" + } + ], + "events": [ + { + "eventAction": "last changed", + "eventDate": "2023-09-05T13:27:59-04:00" + }, + { + "eventAction": "registration", + "eventDate": "2014-11-19T00:39:22-05:00" + } + ], + "status": [ + "validated" + ], + "port43": "whois.arin.net", + "objectClassName": "entity", + "remarks": [], + "entities": [] + }, + { + "handle": "HAZEN16-ARIN", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "adr", + { + "label": "421 W Church St\nJacksonville\nFL\n32202\nUnited States" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "fn", + {}, + "text", + "Jordan Hazen" + ], + [ + "n", + {}, + "text", + [ + "Hazen", + "Jordan", + "", + "", + "" + ] + ], + [ + "kind", + {}, + "text", + "individual" + ], + [ + "email", + {}, + "text", + "jhazen@sbaedge.com" + ], + [ + "tel", + { + "type": [ + "work", + "voice" + ] + }, + "text", + "+1-904-549-7540" + ] + ] + ], + "roles": [ + "technical" + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "self", + "type": "application/rdap+json", + "href": "https://rdap.arin.net/registry/entity/HAZEN16-ARIN" + }, + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "alternate", + "type": "application/xml", + "href": "https://whois.arin.net/rest/poc/HAZEN16-ARIN" + } + ], + "events": [ + { + "eventAction": "last changed", + "eventDate": "2024-06-04T14:09:08-04:00" + }, + { + "eventAction": "registration", + "eventDate": "2022-07-06T18:47:31-04:00" + } + ], + "status": [ + "validated" + ], + "port43": "whois.arin.net", + "objectClassName": "entity", + "remarks": [], + "entities": [] + }, + { + "handle": "KUKAN5-ARIN", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "adr", + { + "label": "603 Discovery Dr.\nWest Chicago\nIL\n60185\nUnited States" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "fn", + {}, + "text", + "Vitalii Kukanov" + ], + [ + "n", + {}, + "text", + [ + "Kukanov", + "Vitalii", + "", + "", + "" + ] + ], + [ + "kind", + {}, + "text", + "individual" + ], + [ + "email", + {}, + "text", + "vkukanov@sbaedge.com" + ], + [ + "tel", + { + "type": [ + "work", + "voice" + ] + }, + "text", + "+1-877-432-2656" + ] + ] + ], + "roles": [ + "technical" + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "self", + "type": "application/rdap+json", + "href": "https://rdap.arin.net/registry/entity/KUKAN5-ARIN" + }, + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "alternate", + "type": "application/xml", + "href": "https://whois.arin.net/rest/poc/KUKAN5-ARIN" + } + ], + "events": [ + { + "eventAction": "last changed", + "eventDate": "2024-05-30T10:42:04-04:00" + }, + { + "eventAction": "registration", + "eventDate": "2024-05-30T10:27:44-04:00" + } + ], + "status": [ + "validated" + ], + "port43": "whois.arin.net", + "objectClassName": "entity", + "remarks": [], + "entities": [] + }, + { + "handle": "NETWO9391-ARIN", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "adr", + { + "label": "8051 Congress Ave\nBoca Raton\nFL\n33487\nUnited States" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "fn", + {}, + "text", + "Network Operations" + ], + [ + "org", + {}, + "text", + "Network Operations" + ], + [ + "kind", + {}, + "text", + "group" + ], + [ + "email", + {}, + "text", + "netops@sbaedge.com" + ], + [ + "tel", + { + "type": [ + "work", + "voice" + ] + }, + "text", + "+1-877-432-2656;ext201" + ] + ] + ], + "roles": [ + "administrative", + "technical" + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "self", + "type": "application/rdap+json", + "href": "https://rdap.arin.net/registry/entity/NETWO9391-ARIN" + }, + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "alternate", + "type": "application/xml", + "href": "https://whois.arin.net/rest/poc/NETWO9391-ARIN" + } + ], + "events": [ + { + "eventAction": "last changed", + "eventDate": "2024-01-23T08:35:36-05:00" + }, + { + "eventAction": "registration", + "eventDate": "2021-01-22T13:10:25-05:00" + } + ], + "status": [ + "validated" + ], + "port43": "whois.arin.net", + "objectClassName": "entity", + "remarks": [], + "entities": [] + } + ] + } + ], + "port43": "whois.arin.net", + "status": [ + "active" + ], + "objectClassName": "ip network", + "cidr0_cidrs": [ + { + "v4prefix": "206.41.110.0", + "length": 24 + } + ], + "arin_originas0_originautnums": [] +} \ No newline at end of file diff --git a/tests/data/rdap/domain/20c.com.input b/tests/data/rdap/domain/20c.com.input new file mode 100644 index 0000000..be07625 --- /dev/null +++ b/tests/data/rdap/domain/20c.com.input @@ -0,0 +1,208 @@ +{ + "rdapConformance": [ + "rdap_level_0", + "icann_rdap_technical_implementation_guide_0", + "icann_rdap_response_profile_0" + ], + "notices": [ + { + "title": "Terms of Use", + "description": [ + "Service subject to Terms of Use." + ], + "links": [ + { + "value": null, + "rel": null, + "type": "text/html", + "href": "https://www.verisign.com/domain-names/registration-data-access-protocol/terms-service/index.xhtml" + } + ] + }, + { + "title": "Status Codes", + "description": [ + "For more information on domain status codes, please visit https://icann.org/epp" + ], + "links": [ + { + "value": null, + "rel": null, + "type": "text/html", + "href": "https://icann.org/epp" + } + ] + }, + { + "title": "RDDS Inaccuracy Complaint Form", + "description": [ + "URL of the ICANN RDDS Inaccuracy Complaint Form: https://icann.org/wicf" + ], + "links": [ + { + "value": null, + "rel": null, + "type": "text/html", + "href": "https://icann.org/wicf" + } + ] + } + ], + "handle": "123664426_DOMAIN_COM-VRSN", + "ldhName": "20C.COM", + "events": [ + { + "eventAction": "registration", + "eventDate": "2004-06-28T18:28:14Z" + }, + { + "eventAction": "expiration", + "eventDate": "2025-06-28T18:28:14Z" + }, + { + "eventAction": "last changed", + "eventDate": "2024-06-25T03:31:37Z" + }, + { + "eventAction": "last update of RDAP database", + "eventDate": "2024-07-24T18:48:30Z" + } + ], + "links": [ + { + "value": "https://rdap.verisign.com/com/v1/domain/20C.COM", + "rel": "self", + "type": "application/rdap+json", + "href": "https://rdap.verisign.com/com/v1/domain/20C.COM" + }, + { + "value": "https://rdap.joker.com/domain/20C.COM", + "rel": "related", + "type": "application/rdap+json", + "href": "https://rdap.joker.com/domain/20C.COM" + } + ], + "entities": [ + { + "handle": "113", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "CSL Computer Service Langenbach GmbH d/b/a joker.com" + ] + ] + ], + "roles": [ + "registrar" + ], + "links": [], + "events": [], + "status": [], + "port43": "", + "objectClassName": "entity", + "remarks": [], + "entities": [ + { + "handle": "", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "" + ], + [ + "tel", + { + "type": "voice" + }, + "uri", + "tel:+49.21186767447" + ], + [ + "email", + {}, + "text", + "abuse@joker.com" + ] + ] + ], + "roles": [ + "abuse" + ], + "links": [], + "events": [], + "status": [], + "port43": "", + "objectClassName": "entity", + "remarks": [], + "entities": [] + } + ] + } + ], + "port43": "", + "network": null, + "objectClassName": "domain", + "secureDNS": { + "delegationSigned": false, + "zeroSigned": null, + "dsData": [] + }, + "nameservers": [ + { + "objectClassName": "nameserver", + "ldhName": "NS-1468.AWSDNS-55.ORG", + "unicodeName": null, + "ipAddresses": {}, + "remarks": [], + "port43": null, + "events": [] + }, + { + "objectClassName": "nameserver", + "ldhName": "NS-1771.AWSDNS-29.CO.UK", + "unicodeName": null, + "ipAddresses": {}, + "remarks": [], + "port43": null, + "events": [] + }, + { + "objectClassName": "nameserver", + "ldhName": "NS-327.AWSDNS-40.COM", + "unicodeName": null, + "ipAddresses": {}, + "remarks": [], + "port43": null, + "events": [] + }, + { + "objectClassName": "nameserver", + "ldhName": "NS-545.AWSDNS-04.NET", + "unicodeName": null, + "ipAddresses": {}, + "remarks": [], + "port43": null, + "events": [] + } + ] +} \ No newline at end of file diff --git a/tests/data/rdap/ip/206.41.110.0.input b/tests/data/rdap/ip/206.41.110.0.input new file mode 100644 index 0000000..11008e0 --- /dev/null +++ b/tests/data/rdap/ip/206.41.110.0.input @@ -0,0 +1,584 @@ +{ + "rdapConformance": [ + "nro_rdap_profile_0", + "rdap_level_0", + "cidr0", + "arin_originas0" + ], + "notices": [ + { + "title": "Terms of Service", + "description": [ + "By using the ARIN RDAP/Whois service, you are agreeing to the RDAP/Whois Terms of Use" + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "terms-of-service", + "type": "text/html", + "href": "https://www.arin.net/resources/registry/whois/tou/" + } + ] + }, + { + "title": "Whois Inaccuracy Reporting", + "description": [ + "If you see inaccuracies in the results, please visit: " + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "inaccuracy-report", + "type": "text/html", + "href": "https://www.arin.net/resources/registry/whois/inaccuracy_reporting/" + } + ] + }, + { + "title": "Copyright Notice", + "description": [ + "Copyright 1997-2024, American Registry for Internet Numbers, Ltd." + ], + "links": [] + } + ], + "handle": "NET-206-41-110-0-1", + "startAddress": "206.41.110.0", + "endAddress": "206.41.110.255", + "ipVersion": "v4", + "name": "CHIX", + "type": "DIRECT ALLOCATION", + "parentHandle": "NET-206-0-0-0-0", + "remarks": [ + { + "title": "Registration Comments", + "description": [ + "https://unitedix.net/" + ] + } + ], + "events": [ + { + "eventAction": "last changed", + "eventDate": "2021-12-14T20:28:45-05:00" + }, + { + "eventAction": "registration", + "eventDate": "2014-10-17T18:32:20-04:00" + } + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "self", + "type": "application/rdap+json", + "href": "https://rdap.arin.net/registry/ip/206.41.110.0" + }, + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "alternate", + "type": "application/xml", + "href": "https://whois.arin.net/rest/net/NET-206-41-110-0-1" + } + ], + "entities": [ + { + "handle": "UIEL", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "United-IX" + ], + [ + "adr", + { + "label": "603 Discovery Dr\nWest Chicago\nIL\n60185\nUnited States" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "kind", + {}, + "text", + "org" + ] + ] + ], + "roles": [ + "registrant" + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "self", + "type": "application/rdap+json", + "href": "https://rdap.arin.net/registry/entity/UIEL" + }, + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "alternate", + "type": "application/xml", + "href": "https://whois.arin.net/rest/org/UIEL" + } + ], + "events": [ + { + "eventAction": "last changed", + "eventDate": "2024-06-04T14:12:37-04:00" + }, + { + "eventAction": "registration", + "eventDate": "2014-07-11T10:33:06-04:00" + } + ], + "status": [], + "port43": "whois.arin.net", + "objectClassName": "entity", + "remarks": [], + "entities": [ + { + "handle": "UIE-ARIN", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "adr", + { + "label": "603 Discovery Dr\nWest Chicago\nIL\n60185\nUnited States" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "fn", + {}, + "text", + "United IX Engineers" + ], + [ + "org", + {}, + "text", + "United IX Engineers" + ], + [ + "kind", + {}, + "text", + "group" + ], + [ + "email", + {}, + "text", + "neteng@unitedix.net" + ], + [ + "tel", + { + "type": [ + "work", + "voice" + ] + }, + "text", + "+1-877-432-2656" + ] + ] + ], + "roles": [ + "abuse", + "noc", + "technical" + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "self", + "type": "application/rdap+json", + "href": "https://rdap.arin.net/registry/entity/UIE-ARIN" + }, + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "alternate", + "type": "application/xml", + "href": "https://whois.arin.net/rest/poc/UIE-ARIN" + } + ], + "events": [ + { + "eventAction": "last changed", + "eventDate": "2023-09-05T13:27:59-04:00" + }, + { + "eventAction": "registration", + "eventDate": "2014-11-19T00:39:22-05:00" + } + ], + "status": [ + "validated" + ], + "port43": "whois.arin.net", + "objectClassName": "entity", + "remarks": [], + "entities": [] + }, + { + "handle": "HAZEN16-ARIN", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "adr", + { + "label": "421 W Church St\nJacksonville\nFL\n32202\nUnited States" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "fn", + {}, + "text", + "Jordan Hazen" + ], + [ + "n", + {}, + "text", + [ + "Hazen", + "Jordan", + "", + "", + "" + ] + ], + [ + "kind", + {}, + "text", + "individual" + ], + [ + "email", + {}, + "text", + "jhazen@sbaedge.com" + ], + [ + "tel", + { + "type": [ + "work", + "voice" + ] + }, + "text", + "+1-904-549-7540" + ] + ] + ], + "roles": [ + "technical" + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "self", + "type": "application/rdap+json", + "href": "https://rdap.arin.net/registry/entity/HAZEN16-ARIN" + }, + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "alternate", + "type": "application/xml", + "href": "https://whois.arin.net/rest/poc/HAZEN16-ARIN" + } + ], + "events": [ + { + "eventAction": "last changed", + "eventDate": "2024-06-04T14:09:08-04:00" + }, + { + "eventAction": "registration", + "eventDate": "2022-07-06T18:47:31-04:00" + } + ], + "status": [ + "validated" + ], + "port43": "whois.arin.net", + "objectClassName": "entity", + "remarks": [], + "entities": [] + }, + { + "handle": "KUKAN5-ARIN", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "adr", + { + "label": "603 Discovery Dr.\nWest Chicago\nIL\n60185\nUnited States" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "fn", + {}, + "text", + "Vitalii Kukanov" + ], + [ + "n", + {}, + "text", + [ + "Kukanov", + "Vitalii", + "", + "", + "" + ] + ], + [ + "kind", + {}, + "text", + "individual" + ], + [ + "email", + {}, + "text", + "vkukanov@sbaedge.com" + ], + [ + "tel", + { + "type": [ + "work", + "voice" + ] + }, + "text", + "+1-877-432-2656" + ] + ] + ], + "roles": [ + "technical" + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "self", + "type": "application/rdap+json", + "href": "https://rdap.arin.net/registry/entity/KUKAN5-ARIN" + }, + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "alternate", + "type": "application/xml", + "href": "https://whois.arin.net/rest/poc/KUKAN5-ARIN" + } + ], + "events": [ + { + "eventAction": "last changed", + "eventDate": "2024-05-30T10:42:04-04:00" + }, + { + "eventAction": "registration", + "eventDate": "2024-05-30T10:27:44-04:00" + } + ], + "status": [ + "validated" + ], + "port43": "whois.arin.net", + "objectClassName": "entity", + "remarks": [], + "entities": [] + }, + { + "handle": "NETWO9391-ARIN", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "adr", + { + "label": "8051 Congress Ave\nBoca Raton\nFL\n33487\nUnited States" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "fn", + {}, + "text", + "Network Operations" + ], + [ + "org", + {}, + "text", + "Network Operations" + ], + [ + "kind", + {}, + "text", + "group" + ], + [ + "email", + {}, + "text", + "netops@sbaedge.com" + ], + [ + "tel", + { + "type": [ + "work", + "voice" + ] + }, + "text", + "+1-877-432-2656;ext201" + ] + ] + ], + "roles": [ + "administrative", + "technical" + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "self", + "type": "application/rdap+json", + "href": "https://rdap.arin.net/registry/entity/NETWO9391-ARIN" + }, + { + "value": "https://rdap.arin.net/registry/ip/206.41.110.0", + "rel": "alternate", + "type": "application/xml", + "href": "https://whois.arin.net/rest/poc/NETWO9391-ARIN" + } + ], + "events": [ + { + "eventAction": "last changed", + "eventDate": "2024-01-23T08:35:36-05:00" + }, + { + "eventAction": "registration", + "eventDate": "2021-01-22T13:10:25-05:00" + } + ], + "status": [ + "validated" + ], + "port43": "whois.arin.net", + "objectClassName": "entity", + "remarks": [], + "entities": [] + } + ] + } + ], + "port43": "whois.arin.net", + "status": [ + "active" + ], + "objectClassName": "ip network", + "cidr0_cidrs": [ + { + "v4prefix": "206.41.110.0", + "length": 24 + } + ], + "arin_originas0_originautnums": [] +} \ No newline at end of file diff --git a/tests/test_geo.py b/tests/test_geo.py new file mode 100644 index 0000000..edc640d --- /dev/null +++ b/tests/test_geo.py @@ -0,0 +1,106 @@ +from datetime import datetime +from unittest.mock import patch + +import pytest + +from rdap.context import RdapRequestState + +# Import the functions and exceptions from your module +from rdap.normalize.geo import GoogleKeyNotSet, NotFound, normalize +from rdap.schema.normalized import GeoLocation, Location + +# Mock data +MOCK_GEOCODE_RESULT = [ + { + "formatted_address": "1600 Amphitheatre Parkway, Mountain View, CA 94043, USA", + "geometry": {"location": {"lat": 37.4224764, "lng": -122.0842499}}, + "address_components": [ + {"types": ["country"], "short_name": "US"}, + {"types": ["postal_code"], "long_name": "94043"}, + {"types": ["locality"], "long_name": "Mountain View"}, + {"types": ["floor"], "long_name": "4"}, + {"types": ["subpremise"], "long_name": "Suite 200"}, + ], + } +] + + +@pytest.fixture +def mock_google_client(): + with patch("googlemaps.Client") as mock_client: + yield mock_client + + +@pytest.fixture +def mock_rdap_request(): + with patch("rdap.context.rdap_request") as mock_request: + mock_request.get.return_value = RdapRequestState() + yield mock_request + + +@pytest.fixture(autouse=True) +def mock_google_api_key(): + with patch.dict("os.environ", {"GOOGLE_MAPS_API_KEY": "fake_api_key"}): + yield + + +def test_normalize_success(mock_google_client): + mock_client = mock_google_client.return_value + mock_client.geocode.return_value = MOCK_GEOCODE_RESULT + + date = datetime.now() + + # Mock the lookup function directly + with patch("rdap.normalize.geo.lookup") as mock_lookup: + # Return the first item of the list + mock_lookup.return_value = MOCK_GEOCODE_RESULT[0] + result = normalize("1600 Amphitheatre Parkway, Mountain View, CA", date) + + assert isinstance(result, Location) + assert result.updated == date + assert result.country == "US" + assert result.city == "Mountain View" + assert result.postal_code == "94043" + assert result.floor == "4" + assert result.suite == "Suite 200" + assert result.address == "1600 Amphitheatre Parkway, Mountain View, CA 94043, USA" + assert isinstance(result.geo, GeoLocation) + assert result.geo.latitude == 37.4224764 + assert result.geo.longitude == -122.0842499 + + # Verify that lookup was called + mock_lookup.assert_called_once_with( + "1600 Amphitheatre Parkway, Mountain View, CA", None + ) + + +def test_normalize_google_key_not_set(): + with patch("rdap.normalize.geo.lookup", side_effect=GoogleKeyNotSet): + date = datetime.now() + result = normalize("1600 Amphitheatre Parkway, Mountain View, CA", date) + + assert isinstance(result, Location) + assert result.updated == date + assert result.address == "1600 Amphitheatre Parkway, Mountain View, CA" + assert result.country is None + assert result.city is None + assert result.postal_code is None + assert result.floor is None + assert result.suite is None + assert result.geo is None + + +def test_normalize_not_found(): + with patch("rdap.normalize.geo.lookup", side_effect=NotFound): + date = datetime.now() + result = normalize("Non-existent Address", date) + + assert isinstance(result, Location) + assert result.updated == date + assert result.address == "Non-existent Address" + assert result.country is None + assert result.city is None + assert result.postal_code is None + assert result.floor is None + assert result.suite is None + assert result.geo is None diff --git a/tests/test_normalize.py b/tests/test_normalize.py new file mode 100644 index 0000000..e557f1b --- /dev/null +++ b/tests/test_normalize.py @@ -0,0 +1,78 @@ +import copy +import json +import os +from unittest.mock import patch + +import pytest +import pytest_filedata + +from tests.test_geo import MOCK_GEOCODE_RESULT + + +def dynamic_mock_address(formatted_address, client): + result = copy.deepcopy(MOCK_GEOCODE_RESULT[0]) + result["formatted_address"] = f"{formatted_address}" + return result + + +@pytest.fixture(autouse=True) +def set_google_maps_api_key(): + original_key = os.environ.get("GOOGLE_MAPS_API_KEY") + os.environ["GOOGLE_MAPS_API_KEY"] = "your_test_api_key" + yield + if original_key is not None: + os.environ["GOOGLE_MAPS_API_KEY"] = original_key + else: + del os.environ["GOOGLE_MAPS_API_KEY"] + + +@pytest_filedata.RequestsData("rdap") +def test_rdap_asn_lookup(rdapc, data_normalize_autnum): + with patch("rdap.normalize.geo.lookup") as mock_lookup: + mock_lookup.side_effect = dynamic_mock_address + asn = rdapc.get_asn(data_normalize_autnum.name) + # uncomment to write expected data + # with open(f"tests/data/normalize/autnum/{data_normalize_autnum.name}.expected", "w") as f: + # f.write(json.dumps(asn.normalized, indent=2)) + + assert json.dumps(data_normalize_autnum.expected, indent=2) == json.dumps( + asn.normalized, indent=2 + ) + + +@pytest_filedata.RequestsData("rdap") +def test_rdap_entity_lookup(rdapc, data_normalize_entity): + with patch("rdap.normalize.geo.lookup") as mock_lookup: + mock_lookup.side_effect = dynamic_mock_address + entity = rdapc.get_entity(data_normalize_entity.name) + # uncomment to write expected data + # with open(f"tests/data/normalize/entity/{data_normalize_entity.name}.expected", "w") as f: + # f.write(json.dumps(entity.normalized, indent=2)) + + assert json.dumps(data_normalize_entity.expected, indent=2) == json.dumps( + entity.normalized, indent=2 + ) + + +@pytest_filedata.RequestsData("rdap") +def test_rdap_domain_lookup(rdapc, data_normalize_domain): + entity = rdapc.get_domain(data_normalize_domain.name) + # uncomment to write expected data + # with open(f"tests/data/normalize/domain/{data_normalize_domain.name}.expected", "w") as f: + # f.write(json.dumps(entity.normalized, indent=2)) + + assert json.dumps(data_normalize_domain.expected, indent=2) == json.dumps( + entity.normalized, indent=2 + ) + + +@pytest_filedata.RequestsData("rdap") +def test_rdap_ip_lookup(rdapc, data_normalize_ip): + entity = rdapc.get_ip(data_normalize_ip.name) + # uncomment to write expected data + # with open(f"tests/data/normalize/ip/{data_normalize_ip.name}.expected", "w") as f: + # f.write(json.dumps(entity.normalized, indent=2)) + + assert json.dumps(data_normalize_ip.expected, indent=2) == json.dumps( + entity.normalized, indent=2 + )