refactor(core): restructure module organization and enhance documentation

Remove standalone DID, handle, NSID, rKey and TID modules and reorganize as submodules. Update URI module with comprehensive documentation explaining AT Protocol URI concepts. Improve import statements to use relative imports for better module encapsulation.

BREAKING CHANGE: Removed standalone modules for DID, handle, NSID, rKey and TID. These components are now accessible as submodules. Users must update their import statements to reflect the new module structure.

+127 -108
poetry.lock
··· 99 99 100 100 [[package]] 101 101 name = "cffi" 102 - version = "1.17.1" 102 + version = "2.0.0" 103 103 description = "Foreign Function Interface for Python calling C code." 104 104 optional = false 105 - python-versions = ">=3.8" 105 + python-versions = ">=3.9" 106 106 groups = ["main"] 107 107 markers = "platform_python_implementation != \"PyPy\"" 108 108 files = [ 109 - {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, 110 - {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, 111 - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, 112 - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, 113 - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, 114 - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, 115 - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, 116 - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, 117 - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, 118 - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, 119 - {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, 120 - {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, 121 - {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, 122 - {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, 123 - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, 124 - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, 125 - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, 126 - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, 127 - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, 128 - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, 129 - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, 130 - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, 131 - {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, 132 - {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, 133 - {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, 134 - {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, 135 - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, 136 - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, 137 - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, 138 - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, 139 - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, 140 - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, 141 - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, 142 - {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, 143 - {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, 144 - {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, 145 - {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, 146 - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, 147 - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, 148 - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, 149 - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, 150 - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, 151 - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, 152 - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, 153 - {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, 154 - {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, 155 - {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, 156 - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, 157 - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, 158 - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, 159 - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, 160 - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, 161 - {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, 162 - {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, 163 - {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, 164 - {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, 165 - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, 166 - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, 167 - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, 168 - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, 169 - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, 170 - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, 171 - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, 172 - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, 173 - {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, 174 - {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, 175 - {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, 109 + {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, 110 + {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, 111 + {file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"}, 112 + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"}, 113 + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"}, 114 + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"}, 115 + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"}, 116 + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"}, 117 + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"}, 118 + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"}, 119 + {file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"}, 120 + {file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"}, 121 + {file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"}, 122 + {file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"}, 123 + {file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"}, 124 + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"}, 125 + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"}, 126 + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"}, 127 + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"}, 128 + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"}, 129 + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"}, 130 + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"}, 131 + {file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"}, 132 + {file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"}, 133 + {file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"}, 134 + {file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"}, 135 + {file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"}, 136 + {file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"}, 137 + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"}, 138 + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"}, 139 + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"}, 140 + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"}, 141 + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"}, 142 + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"}, 143 + {file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"}, 144 + {file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"}, 145 + {file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"}, 146 + {file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"}, 147 + {file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"}, 148 + {file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"}, 149 + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"}, 150 + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"}, 151 + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"}, 152 + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"}, 153 + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"}, 154 + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"}, 155 + {file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"}, 156 + {file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"}, 157 + {file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"}, 158 + {file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"}, 159 + {file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"}, 160 + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"}, 161 + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"}, 162 + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"}, 163 + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"}, 164 + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"}, 165 + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"}, 166 + {file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"}, 167 + {file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"}, 168 + {file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"}, 169 + {file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"}, 170 + {file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"}, 171 + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"}, 172 + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"}, 173 + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"}, 174 + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"}, 175 + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"}, 176 + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"}, 177 + {file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"}, 178 + {file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"}, 179 + {file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"}, 180 + {file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"}, 181 + {file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"}, 182 + {file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"}, 183 + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"}, 184 + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"}, 185 + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"}, 186 + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"}, 187 + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"}, 188 + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"}, 189 + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"}, 190 + {file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"}, 191 + {file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"}, 192 + {file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"}, 176 193 ] 177 194 178 195 [package.dependencies] 179 - pycparser = "*" 196 + pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} 180 197 181 198 [[package]] 182 199 name = "charset-normalizer" ··· 329 346 330 347 [[package]] 331 348 name = "dnspython" 332 - version = "2.7.0" 349 + version = "2.8.0" 333 350 description = "DNS toolkit" 334 351 optional = false 335 - python-versions = ">=3.9" 352 + python-versions = ">=3.10" 336 353 groups = ["main"] 337 354 files = [ 338 - {file = "dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86"}, 339 - {file = "dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1"}, 355 + {file = "dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af"}, 356 + {file = "dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f"}, 340 357 ] 341 358 342 359 [package.extras] 343 - dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "hypercorn (>=0.16.0)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "quart-trio (>=0.11.0)", "sphinx (>=7.2.0)", "sphinx-rtd-theme (>=2.0.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] 344 - dnssec = ["cryptography (>=43)"] 345 - doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] 346 - doq = ["aioquic (>=1.0.0)"] 347 - idna = ["idna (>=3.7)"] 348 - trio = ["trio (>=0.23)"] 349 - wmi = ["wmi (>=1.5.1)"] 360 + dev = ["black (>=25.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "hypercorn (>=0.17.0)", "mypy (>=1.17)", "pylint (>=3)", "pytest (>=8.4)", "pytest-cov (>=6.2.0)", "quart-trio (>=0.12.0)", "sphinx (>=8.2.0)", "sphinx-rtd-theme (>=3.0.0)", "twine (>=6.1.0)", "wheel (>=0.45.0)"] 361 + dnssec = ["cryptography (>=45)"] 362 + doh = ["h2 (>=4.2.0)", "httpcore (>=1.0.0)", "httpx (>=0.28.0)"] 363 + doq = ["aioquic (>=1.2.0)"] 364 + idna = ["idna (>=3.10)"] 365 + trio = ["trio (>=0.30)"] 366 + wmi = ["wmi (>=1.5.1) ; platform_system == \"Windows\""] 350 367 351 368 [[package]] 352 369 name = "frozendict" ··· 585 602 586 603 [[package]] 587 604 name = "py-cid" 588 - version = "0.3.0" 605 + version = "0.3.1" 589 606 description = "Self-describing content-addressed identifiers for distributed systems" 590 607 optional = false 591 - python-versions = "*" 608 + python-versions = ">=3.10" 592 609 groups = ["main"] 593 610 files = [ 594 - {file = "py-cid-0.3.0.tar.gz", hash = "sha256:22f432cc6fb68d12a9c35dbdc92c95484fc49e31dfcb9e0efb0082233c5394e3"}, 595 - {file = "py_cid-0.3.0-py2.py3-none-any.whl", hash = "sha256:7c48a6ee0bc50fd114d4b24849cd689a31d3ad5bdf8fa073bf68f846fd58c5da"}, 611 + {file = "py_cid-0.3.1-py3-none-any.whl", hash = "sha256:bd60f54372704b7f2c0565432bf052e9ba7d42f260d39e9f7dc9566802a445a6"}, 612 + {file = "py_cid-0.3.1.tar.gz", hash = "sha256:eb0d14f35430787b978ffb66067d70ffaa9d85ecf62c950e792293193e705a75"}, 596 613 ] 597 614 598 615 [package.dependencies] ··· 600 617 morphys = ">=1.0,<2.0" 601 618 py-multibase = ">=1.0.0,<2.0.0" 602 619 py-multicodec = "<0.3.0" 603 - py-multihash = ">=0.2.0,<1.0.0" 620 + pymultihash = ">=0.8.0,<1.0.0" 621 + 622 + [package.extras] 623 + dev = ["build", "bump_my_version (>=0.19.0)", "codecov (>=2.0.0)", "coverage (>=7.0.0)", "cryptography (>=44.0.1)", "hypothesis (>=6.0.0)", "mypy (>=1.5.0)", "pre-commit (>=3.0.0)", "pyrefly (>=0.17.1,<0.18.0)", "pytest (>=7.0.0)", "pytest-cov (>=4.0.0)", "pytest-runner (>=6.0.0)", "ruff (>=0.1.0)", "sphinx (>=7.0.0)", "towncrier (>=24,<25)", "tox (>=4.0.0)", "twine (>=4.0.0)", "watchdog[watchmedo] (>=3.0.0)", "wheel"] 624 + docs = ["sphinx (>=7.0.0)"] 604 625 605 626 [[package]] 606 627 name = "py-multibase" ··· 637 658 varint = ">=1.0.2,<2.0.0" 638 659 639 660 [[package]] 640 - name = "py-multihash" 641 - version = "0.2.3" 642 - description = "Multihash implementation in Python" 643 - optional = false 644 - python-versions = "*" 645 - groups = ["main"] 646 - files = [ 647 - {file = "py-multihash-0.2.3.tar.gz", hash = "sha256:f0ade4de820afdc4b4aaa40464ec86c9da5cae3a4578cda2daab4b0eb7e5b18d"}, 648 - {file = "py_multihash-0.2.3-py2.py3-none-any.whl", hash = "sha256:a0602c99093587dfbf1634e2e8c7726de39374b0d68587a36093b4c237af6969"}, 649 - ] 650 - 651 - [package.dependencies] 652 - base58 = ">=1.0.2,<2.0" 653 - morphys = ">=1.0,<2.0" 654 - six = ">=1.10.0,<2.0" 655 - varint = ">=1.0.2,<2.0" 656 - 657 - [[package]] 658 661 name = "pycparser" 659 - version = "2.22" 662 + version = "2.23" 660 663 description = "C parser in Python" 661 664 optional = false 662 665 python-versions = ">=3.8" 663 666 groups = ["main"] 664 - markers = "platform_python_implementation != \"PyPy\"" 667 + markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" 665 668 files = [ 666 - {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, 667 - {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, 669 + {file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"}, 670 + {file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"}, 668 671 ] 669 672 670 673 [[package]] ··· 690 693 cachetools = ["cachetools"] 691 694 frozendict = ["frozendict"] 692 695 requests = ["requests"] 696 + 697 + [[package]] 698 + name = "pymultihash" 699 + version = "0.8.2" 700 + description = "Python implementation of the multihash specification" 701 + optional = false 702 + python-versions = "*" 703 + groups = ["main"] 704 + files = [ 705 + {file = "pymultihash-0.8.2-py3-none-any.whl", hash = "sha256:f7fa840b24bd6acbd6b073fcd330f10e15619387297babf1dd13ca4dae6e8209"}, 706 + {file = "pymultihash-0.8.2.tar.gz", hash = "sha256:49c75a1ae9ecc6d22d259064d4597b3685da3f0258f4ded632e03a3a79af215b"}, 707 + ] 708 + 709 + [package.extras] 710 + blake2 = ["pyblake2"] 711 + sha3 = ["pysha3"] 693 712 694 713 [[package]] 695 714 name = "python-baseconv"
-54
src/atpasser/did/__init__.py
··· 1 - import re 2 - from pyld import jsonld 3 - 4 - 5 - class DID: 6 - """ 7 - A class representing a DID. 8 - 9 - Attributes: 10 - uri (str): The DID URI. 11 - """ 12 - 13 - def __init__(self, uri: str) -> None: 14 - """ 15 - Initalizes an DID object. 16 - 17 - Parameters: 18 - uri (str): The DID URI. 19 - """ 20 - pattern = re.compile("^did:[a-z]+:[a-zA-Z0-9._:%-]*[a-zA-Z0-9._-]$") 21 - patternMatch = pattern.match(uri) 22 - if patternMatch and len(uri) <= 2048: 23 - self.uri = patternMatch[0] 24 - else: 25 - raise ValueError 26 - 27 - def __str__(self) -> str: 28 - """ 29 - Convert the TID to a string by given the URI. 30 - """ 31 - return self.uri 32 - 33 - def __eq__(self, value: object, /) -> bool: 34 - """ 35 - Check if the 2 values are exactly the same. 36 - """ 37 - if isinstance(value, DID): 38 - return str(self) == str(value) 39 - else: 40 - return False 41 - 42 - def fetch(self): 43 - """ 44 - Fetch the metadata of the DID. 45 - 46 - Returns: 47 - list: the pseudo-list which can be read by `PyLD` library 48 - """ 49 - if self.uri.startswith("did:plc:"): 50 - return jsonld.expand(f"https://plc.directory/{self.uri}") 51 - elif self.uri.startswith("did:web"): 52 - return jsonld.expand( 53 - f"https://{self.uri.replace("did:web:","")}/.well-known/did.json" 54 - )
-96
src/atpasser/handle/__init__.py
··· 1 - import dns.resolver, requests 2 - 3 - from atpasser import did 4 - 5 - 6 - class Handle: 7 - """ 8 - A class representing a Handle. 9 - 10 - 11 - Attributes: 12 - handle (str): The Handle. 13 - """ 14 - 15 - def __init__(self, handle: str) -> None: 16 - """ 17 - Initalizes an Handle object. 18 - 19 - Parameters: 20 - handle (str): The Handle. 21 - """ 22 - 23 - if len(handle) > 253: 24 - raise ValueError("handle is more than 253 chars") 25 - 26 - labels = handle.lower().split(".") 27 - 28 - if len(labels) < 2: 29 - raise ValueError("are you tld?") 30 - 31 - if labels[0] == "" or labels[-1] == "": 32 - raise ValueError("proceeding or tariling ascii periods") 33 - 34 - for label in labels: 35 - if len(label) not in range(1, 64): 36 - raise ValueError("two periods or segment longer than 63 char") 37 - charset = set(label) 38 - validset = set("abcdefghijklmnopqrstuvwxyz0123456789-") 39 - if not charset.issubset(validset): 40 - raise ValueError("invalid char used in segment") 41 - if label.startswith("-") or label.endswith("-"): 42 - raise ValueError("segments starts or ends with hyphen") 43 - 44 - tld = labels[-1] 45 - if tld[0] in "0123456789": 46 - raise ValueError("tld starts with digit") 47 - 48 - self.handle = handle 49 - 50 - def __str__(self) -> str: 51 - """ 52 - 53 - Convert the TID to a string by given the URI. 54 - """ 55 - return self.handle 56 - 57 - def __eq__(self, value: object, /) -> bool: 58 - """ 59 - 60 - Check if the 2 values are exactly the same. 61 - """ 62 - 63 - if isinstance(value, Handle): 64 - 65 - return str(self) == str(value) 66 - else: 67 - 68 - return False 69 - 70 - def toTID(self): 71 - """ 72 - Convert the handle to TID. 73 - 74 - Returns: 75 - An DID object, or `None` if the handle is invalid. 76 - """ 77 - try: 78 - answers = dns.resolver.resolve("_atproto." + self.handle, "TXT") 79 - except: 80 - answers = [] 81 - for answer in answers: 82 - if str(answer).startswith('"did='): 83 - try: 84 - uri = str(answer)[5:-1] 85 - return did.DID(uri) 86 - except: 87 - pass # cannot resolve via dns 88 - response = requests.get(f"https://{self.handle}/.well-known/atproto-did") 89 - if response.status_code // 100 != 2: 90 - return None 91 - if response.headers.get("Content-Type") != "text/plain": 92 - pass # Pass for now, because some sites like neocities, breaks this rule 93 - try: 94 - return did.DID(response.text) 95 - except: 96 - return None
+30 -15
src/atpasser/nsid/__init__.py src/atpasser/uri/nsid.py
··· 1 1 class NSID: 2 - """ 3 - A class representing a NSID. 2 + """A class representing a NSID (Namespace Identifier) in the AT Protocol. 4 3 4 + NSIDs are used to identify record types and methods in the AT Protocol's Lexicon 5 + schema system. They follow a reverse domain name notation format like 6 + 'com.example.recordName' which provides a hierarchical namespace for organizing 7 + schema definitions across different domains and applications. 5 8 6 9 Attributes: 7 - nsid (str): The NSID URI. 10 + nsid (str): The complete NSID string including optional fragment. 11 + domainAuthority (list): Domain authority segments as list (e.g., ['com', 'example']). 12 + domainAuthorityAsText (str): Domain authority as dot-separated text. 13 + name (str): The record name or method name segment. 14 + fragment (str or None): Optional fragment for variant specifications. 8 15 """ 9 16 10 17 def __init__(self, nsid: str) -> None: 11 - """ 12 - Initalizes an NSID object. 18 + """Initializes an NSID object with validation. 13 19 14 - Parameters: 15 - domainAuthority (list): Domain Authority as list. 16 - domainAuthorityAsText (str): Domain Authority as text. 17 - name (str): The name. 18 - fragment (str | none): The fragment. 19 - nsid (str): The NSID. 20 + Parses and validates an NSID string according to AT Protocol Lexicon specifications. 21 + NSIDs must follow reverse domain notation with proper segment structure, 22 + character restrictions, and length limits. 23 + 24 + Args: 25 + nsid (str): The NSID string to parse and validate. 26 + 27 + Raises: 28 + ValueError: If the NSID contains invalid characters, exceeds maximum length, 29 + has improper segment structure, or violates other naming rules. 20 30 """ 21 31 22 32 if "#" in nsid: ··· 94 104 self.nsid = ".".join(domainAuthority) + f".{name}" + f"#{fragment}" 95 105 96 106 def __str__(self) -> str: 97 - """ 107 + """Convert the NSID object to its string representation. 98 108 99 - Convert the NSID to a string by given the URI. 109 + Returns: 110 + str: The canonical NSID string including optional fragment. 100 111 """ 101 112 return self.nsid 102 113 103 114 def __eq__(self, value: object, /) -> bool: 104 - """ 115 + """Check if two NSID objects represent the same namespace identifier. 116 + 117 + Args: 118 + value (object): The object to compare with. 105 119 106 - Check if the 2 values are exactly the same. 120 + Returns: 121 + bool: True if the objects represent the same NSID, False otherwise. 107 122 """ 108 123 109 124 if isinstance(value, NSID):
-72
src/atpasser/rKey/__init__.py
··· 1 - class RKey: 2 - """ 3 - 4 - A class representing a RecordKey. 5 - 6 - 7 - 8 - Attributes: 9 - 10 - recordKey (str): The RecordKey URI. 11 - """ 12 - 13 - 14 - def __init__(self, recordKey: str) -> None: 15 - """ 16 - 17 - Initalizes an RecordKey object. 18 - 19 - 20 - Parameters: 21 - 22 - recordKey (str): The RecordKey. 23 - """ 24 - 25 - 26 - if recordKey == "" or len(recordKey) > 512: 27 - 28 - raise ValueError("null record key or record key longer than 512 chars") 29 - 30 - 31 - if recordKey == ".." or recordKey == ".": 32 - 33 - raise ValueError("reserved value . and ..") 34 - 35 - 36 - if not set(recordKey).issubset( 37 - 38 - set("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-_:~") 39 - ): 40 - 41 - raise ValueError("invalid char") 42 - 43 - 44 - self.recordKey = recordKey 45 - 46 - 47 - def __str__(self) -> str: 48 - """ 49 - 50 - 51 - Convert the RecordKey to a string by given the URI. 52 - """ 53 - return self.recordKey 54 - 55 - 56 - def __eq__(self, value: object, /) -> bool: 57 - """ 58 - 59 - 60 - Check if the 2 values are exactly the same. 61 - """ 62 - 63 - 64 - if isinstance(value, RKey): 65 - 66 - 67 - return str(self) == str(value) 68 - else: 69 - 70 - 71 - return False 72 -
-99
src/atpasser/tid/__init__.py
··· 1 - import datetime, random 2 - 3 - 4 - class TID: 5 - """ 6 - A class representing a TID. 7 - 8 - Attributes: 9 - timestamp (datetime.datetime): The time of a TID. 10 - clockIdentifier (int): what it said. 11 - """ 12 - 13 - def __init__( 14 - self, time: datetime.datetime | None = None, clockIdentifier: int | None = None 15 - ) -> None: 16 - """ 17 - Initalizes an TID object. 18 - 19 - Parameters: 20 - time (datetime.datetime): The time of a TID. 21 - clockIdentifier (int): what it said. 22 - """ 23 - if time == None: 24 - self.timestamp = datetime.datetime.now() 25 - else: 26 - self.timestamp = time 27 - if clockIdentifier == None: 28 - self.clockIdentifier = random.randrange(0, 1024) 29 - else: 30 - self.clockIdentifier = clockIdentifier 31 - 32 - def __int__(self): 33 - """ 34 - Convert the TID to integer. 35 - """ 36 - timestamp = int(self.timestamp.timestamp() * 1000000) 37 - return timestamp * 1024 + self.clockIdentifier 38 - 39 - def __str__(self): 40 - """ 41 - Convert the TID to a base32-sortable string. 42 - """ 43 - integer = int(self) 44 - binary = f"{integer:065b}" 45 - return "".join( 46 - [ 47 - "234567abcdefghijklmnopqrstuvwxyz"[int(binary[i : i + 5], base=2)] 48 - for i in range(0, len(binary), 5) 49 - ] 50 - ) 51 - 52 - def __eq__(self, value: object, /) -> bool: 53 - """ 54 - Check if the 2 values are exactly the same. 55 - """ 56 - if isinstance(value, TID): 57 - return int(self) == int(value) 58 - else: 59 - return False 60 - 61 - 62 - def importTIDfromInteger(value: int | None = None): 63 - """ 64 - This function imports from Integer to TID. 65 - 66 - Parameters: 67 - value (int): The integer. 68 - 69 - Returns: 70 - TID: The TID of given integer. 71 - """ 72 - if value == None: 73 - value = int(TID()) 74 - clockIdentifier = value % 1024 75 - timestamp = (value >> 10) / 1000000 76 - return TID(datetime.datetime.fromtimestamp(timestamp), clockIdentifier) 77 - 78 - 79 - def importTIDfromBase32(value: str | None = None): 80 - """ 81 - This function imports from Integer to TID. 82 - 83 - Parameters: 84 - value (int): The integer. 85 - 86 - Returns: 87 - TID: The TID of given integer. 88 - """ 89 - if value == None: 90 - value = str(TID()) 91 - b32s = "234567abcdefghijklmnopqrstuvwxyz" 92 - return importTIDfromInteger( 93 - sum( 94 - [ 95 - b32s.find(value[i]) * (32 ** (len(value) - i - 1)) 96 - for i in range(len(value)) 97 - ] 98 - ) 99 - )
+35 -24
src/atpasser/uri/__init__.py
··· 1 1 import urllib.parse as up 2 - from atpasser import handle, did 2 + from . import handle, did 3 3 import jsonpath_ng 4 4 5 5 6 6 class URI: 7 - """ 8 - A class representing an AT URI. 7 + """A class representing an AT Protocol URI. 9 8 10 - Attributes: 11 - uri (str): The AT URI. 9 + The AT Protocol URI scheme provides a standardized way to address content within the 10 + decentralized AT Protocol network. URIs follow the format 'at://authority/path' 11 + where authority can be either a DID (Decentralized Identifier) or a handle, and 12 + path specifies the location of content within a repository. 12 13 13 - fragment: Parsed fragment 14 - fragmentAsText (str): fragment as text 15 - 16 - query (dict): Parsed query as dict 17 - queryAsText (str): Query as text 18 - 19 - path (list): Parsed path as list 20 - pathAsText (str): Path as text 21 - 22 - authority (atpasser.handle.Handle | atpasser.did.DID | None): Domain authority as handle or DID 23 - authorityAsText (str): Domain authority as text 14 + Attributes: 15 + uri (str): The complete AT Protocol URI string. 16 + fragment: Parsed JSONPath fragment for querying specific data within records. 17 + fragmentAsText (str): URL-encoded fragment as text. 18 + query (dict): Parsed query parameters as dictionary. 19 + queryAsText (str): URL-encoded query string. 20 + path (list): Parsed path segments as list. 21 + pathAsText (str): URL-encoded path string. 22 + authority (atpasser.handle.Handle | atpasser.did.DID | None): Repository authority as handle or DID. 23 + authorityAsText (str): Authority as text string. 24 24 """ 25 25 26 26 def __init__(self, uri: str) -> None: 27 - """ 28 - Initalizes an AT URI. 27 + """Initializes an AT Protocol URI object. 28 + 29 + Parses and validates an AT Protocol URI string according to the AT Protocol specification. 30 + The URI must start with 'at://' and contain valid ASCII characters only. 31 + 32 + Args: 33 + uri (str): The AT Protocol URI string to parse. 29 34 30 - Parameters: 31 - uri (str): The AT URI. 35 + Raises: 36 + ValueError: If the URI contains invalid characters, exceeds maximum length, 37 + doesn't start with 'at://', or has other formatting issues. 32 38 """ 33 39 34 40 if not set(uri).issubset(set([chr(i) for i in range(0x80)])): ··· 105 111 ) 106 112 107 113 def __str__(self) -> str: 108 - """ 114 + """Convert the URI object to its string representation. 109 115 110 - Convert the RecordKey to a string by given the URI. 116 + Returns: 117 + str: The canonical AT Protocol URI string. 111 118 """ 112 119 return self.uri 113 120 114 121 def __eq__(self, value: object, /) -> bool: 115 - """ 122 + """Check if two URI objects represent the same AT Protocol URI. 123 + 124 + Args: 125 + value (object): The object to compare with. 116 126 117 - Check if the 2 values are exactly the same. 127 + Returns: 128 + bool: True if the objects represent the same URI, False otherwise. 118 129 """ 119 130 120 131 if isinstance(value, URI):
+79
src/atpasser/uri/did.py
··· 1 + import re 2 + from pyld import jsonld 3 + 4 + 5 + class DID: 6 + """A class representing a DID (Decentralized Identifier) in the AT Protocol. 7 + 8 + Decentralized Identifiers (DIDs) are a key component of the AT Protocol's identity system. 9 + They provide cryptographically verifiable, persistent identifiers for users and services 10 + in the decentralized network. DIDs follow the format 'did:method:specific-identifier' 11 + where method defines the resolution mechanism (e.g., 'plc' for PLC directory, 'web' for web-based). 12 + 13 + Attributes: 14 + uri (str): The complete DID URI string. 15 + """ 16 + 17 + def __init__(self, uri: str) -> None: 18 + """Initializes a DID object with validation. 19 + 20 + Parses and validates a DID URI string according to the W3C DID specification 21 + and AT Protocol requirements. The DID must follow the format 'did:method:identifier' 22 + and contain only valid characters. 23 + 24 + Args: 25 + uri (str): The DID URI string to validate. 26 + 27 + Raises: 28 + ValueError: If the URI doesn't match the DID format or exceeds maximum length. 29 + """ 30 + pattern = re.compile("^did:[a-z]+:[a-zA-Z0-9._:%-]*[a-zA-Z0-9._-]$") 31 + patternMatch = pattern.match(uri) 32 + if patternMatch and len(uri) <= 2048: 33 + self.uri = patternMatch[0] 34 + else: 35 + raise ValueError 36 + 37 + def __str__(self) -> str: 38 + """Convert the DID object to its string representation. 39 + 40 + Returns: 41 + str: The canonical DID URI string. 42 + """ 43 + return self.uri 44 + 45 + def __eq__(self, value: object, /) -> bool: 46 + """Check if two DID objects represent the same identifier. 47 + 48 + Args: 49 + value (object): The object to compare with. 50 + 51 + Returns: 52 + bool: True if the objects represent the same DID, False otherwise. 53 + """ 54 + if isinstance(value, DID): 55 + return str(self) == str(value) 56 + else: 57 + return False 58 + 59 + def fetch(self): 60 + """Fetch the DID document metadata from the appropriate resolver. 61 + 62 + Resolves the DID to its associated DID document by querying the appropriate 63 + resolver based on the DID method. Currently supports 'did:plc:' (resolved via 64 + PLC directory) and 'did:web:' (resolved via well-known HTTP endpoint). 65 + 66 + Returns: 67 + list: The expanded JSON-LD document representing the DID document, 68 + compatible with the PyLD library for further processing. 69 + 70 + Note: 71 + - For 'did:plc:' DIDs, queries https://plc.directory/{did} 72 + - For 'did:web:' DIDs, queries https://{domain}/.well-known/did.json 73 + """ 74 + if self.uri.startswith("did:plc:"): 75 + return jsonld.expand(f"https://plc.directory/{self.uri}") 76 + elif self.uri.startswith("did:web"): 77 + return jsonld.expand( 78 + f"https://{self.uri.replace("did:web:","")}/.well-known/did.json" 79 + )
+122
src/atpasser/uri/handle.py
··· 1 + import dns.resolver, requests 2 + 3 + from .did import DID 4 + 5 + 6 + class Handle: 7 + """A class representing a Handle in the AT Protocol. 8 + 9 + Handles are human-readable identifiers that map to DIDs in the AT Protocol. 10 + They provide a user-friendly way to reference accounts and repositories in the 11 + decentralized network. Handles follow DNS naming conventions and can be resolved 12 + to their corresponding DIDs through DNS TXT records or HTTP well-known endpoints. 13 + 14 + Attributes: 15 + handle (str): The handle string following DNS naming conventions. 16 + """ 17 + 18 + def __init__(self, handle: str) -> None: 19 + """Initializes a Handle object with validation. 20 + 21 + Parses and validates a handle string according to AT Protocol specifications. 22 + Handles must follow DNS naming conventions with proper domain structure, 23 + character restrictions, and length limits. 24 + 25 + Args: 26 + handle (str): The handle string to validate. 27 + 28 + Raises: 29 + ValueError: If the handle exceeds maximum length, has invalid domain structure, 30 + contains invalid characters, or violates other naming rules. 31 + """ 32 + 33 + if len(handle) > 253: 34 + raise ValueError("handle is more than 253 chars") 35 + 36 + labels = handle.lower().split(".") 37 + 38 + if len(labels) < 2: 39 + raise ValueError("are you tld?") 40 + 41 + if labels[0] == "" or labels[-1] == "": 42 + raise ValueError("proceeding or tariling ascii periods") 43 + 44 + for label in labels: 45 + if len(label) not in range(1, 64): 46 + raise ValueError("two periods or segment longer than 63 char") 47 + charset = set(label) 48 + validset = set("abcdefghijklmnopqrstuvwxyz0123456789-") 49 + if not charset.issubset(validset): 50 + raise ValueError("invalid char used in segment") 51 + if label.startswith("-") or label.endswith("-"): 52 + raise ValueError("segments starts or ends with hyphen") 53 + 54 + tld = labels[-1] 55 + if tld[0] in "0123456789": 56 + raise ValueError("tld starts with digit") 57 + 58 + self.handle = handle 59 + 60 + def __str__(self) -> str: 61 + """Convert the Handle object to its string representation. 62 + 63 + Returns: 64 + str: The canonical handle string. 65 + """ 66 + return self.handle 67 + 68 + def __eq__(self, value: object, /) -> bool: 69 + """Check if two Handle objects represent the same identifier. 70 + 71 + Args: 72 + value (object): The object to compare with. 73 + 74 + Returns: 75 + bool: True if the objects represent the same handle, False otherwise. 76 + """ 77 + 78 + if isinstance(value, Handle): 79 + 80 + return str(self) == str(value) 81 + else: 82 + 83 + return False 84 + 85 + def toTID(self): 86 + """Resolve the handle to its corresponding DID (Decentralized Identifier). 87 + 88 + Attempts to resolve the handle to a DID by first checking DNS TXT records 89 + at '_atproto.{handle}' and falling back to HTTP well-known endpoint at 90 + 'https://{handle}/.well-known/atproto-did'. This enables mapping from 91 + human-readable handles to cryptographically verifiable DIDs. 92 + 93 + Returns: 94 + atpasser.uri.did.DID or None: A DID object if resolution succeeds, 95 + None if resolution fails. 96 + 97 + Note: 98 + Resolution follows AT Protocol specifications: 99 + 1. First attempts DNS TXT record lookup for 'did={did}' value 100 + 2. Falls back to HTTP GET request to well-known endpoint 101 + 3. Returns None if both methods fail 102 + """ 103 + try: 104 + answers = dns.resolver.resolve("_atproto." + self.handle, "TXT") 105 + except: 106 + answers = [] 107 + for answer in answers: 108 + if str(answer).startswith('"did='): 109 + try: 110 + uri = str(answer)[5:-1] 111 + return DID(uri) 112 + except: 113 + pass # cannot resolve via dns 114 + response = requests.get(f"https://{self.handle}/.well-known/atproto-did") 115 + if response.status_code // 100 != 2: 116 + return None 117 + if response.headers.get("Content-Type") != "text/plain": 118 + pass # Pass for now, because some sites like neocities, breaks this rule 119 + try: 120 + return DID(response.text) 121 + except: 122 + return None
+22 -15
src/atpasser/uri/restricted.py
··· 1 - from atpasser import uri, handle, nsid, rKey 1 + from . import handle, nsid 2 + from . import rkey as rKey 3 + from . import URI 2 4 3 5 4 6 5 - class RestrictedURI(uri.URI): 6 - """ 7 + class RestrictedURI(URI): 8 + """A class representing a restricted AT Protocol URI for record access. 7 9 8 - A class representing a restricted URI. 9 - 10 + RestrictedURIs provide a specialized form of AT Protocol URIs that specifically 11 + address records within repositories. They follow the format 'at://authority/collection/rkey' 12 + where collection identifies the record type (via NSID) and rkey identifies the specific 13 + record instance. This format is commonly used for referencing specific social records 14 + like posts, profiles, and other user-generated content. 10 15 11 16 Attributes: 12 - 13 - collection (atpasser.nsid.NSID): Collection as NSID. 14 - 15 - rkey (atpasser.rKey.RKey): Record key as a RKey object. 17 + collection (atpasser.nsid.NSID): The record collection identified by NSID. 18 + rkey (atpasser.rKey.RKey): The record key identifying a specific record instance. 16 19 """ 17 - 18 20 19 21 def __init__(self, uri: str) -> None: 20 - """ 22 + """Initializes a restricted URI with validation. 21 23 22 - Initalizes a restricted URI. 23 - 24 + Parses and validates an AT Protocol URI specifically for record access. 25 + Restricted URIs must have a valid authority (DID or handle), may include 26 + a collection (NSID), and optionally a record key (rkey). Query parameters 27 + and fragments are not allowed in restricted URIs. 24 28 25 - Parameters: 29 + Args: 30 + uri (str): The AT Protocol URI string to parse as a restricted URI. 26 31 27 - uri (str): The AT URI. 32 + Raises: 33 + ValueError: If the URI has query parameters or fragments, invalid authority, 34 + or too many path segments for a restricted URI format. 28 35 """ 29 36 30 37 super().__init__(uri)
+74
src/atpasser/uri/rkey.py
··· 1 + class RKey: 2 + """A class representing a Record Key (RKey) in the AT Protocol. 3 + 4 + Record Keys are unique identifiers for individual records within a collection 5 + in the AT Protocol. They are used in URIs to address specific records and 6 + must follow specific character and length restrictions. RKeys can be 7 + user-defined or auto-generated and are essential for referencing specific 8 + content within repositories. 9 + 10 + Attributes: 11 + recordKey (str): The record key string identifying a specific record. 12 + """ 13 + 14 + def __init__(self, recordKey: str) -> None: 15 + """Initializes a Record Key object with validation. 16 + 17 + Parses and validates a record key string according to AT Protocol specifications. 18 + Record keys must not be empty, cannot exceed maximum length, cannot be 19 + reserved values ('.' or '..'), and must contain only valid characters. 20 + 21 + Args: 22 + recordKey (str): The record key string to validate. 23 + 24 + Raises: 25 + ValueError: If the record key is empty, exceeds maximum length, 26 + is a reserved value, or contains invalid characters. 27 + """ 28 + 29 + 30 + if recordKey == "" or len(recordKey) > 512: 31 + 32 + raise ValueError("null record key or record key longer than 512 chars") 33 + 34 + 35 + if recordKey == ".." or recordKey == ".": 36 + 37 + raise ValueError("reserved value . and ..") 38 + 39 + 40 + if not set(recordKey).issubset( 41 + 42 + set("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-_:~") 43 + ): 44 + 45 + raise ValueError("invalid char") 46 + 47 + 48 + self.recordKey = recordKey 49 + 50 + 51 + def __str__(self) -> str: 52 + """Convert the Record Key object to its string representation. 53 + 54 + Returns: 55 + str: The canonical record key string. 56 + """ 57 + return self.recordKey 58 + 59 + 60 + def __eq__(self, value: object, /) -> bool: 61 + """Check if two RKey objects represent the same record key. 62 + 63 + Args: 64 + value (object): The object to compare with. 65 + 66 + Returns: 67 + bool: True if the objects represent the same record key, False otherwise. 68 + """ 69 + 70 + if isinstance(value, RKey): 71 + return str(self) == str(value) 72 + else: 73 + return False 74 +
+136
src/atpasser/uri/tid.py
··· 1 + import datetime, random 2 + 3 + 4 + class TID: 5 + """A class representing a TID (Time-based Identifier) in the AT Protocol. 6 + 7 + TIDs are time-based identifiers used for ordering and uniquely identifying 8 + records in the AT Protocol. They combine a microsecond-precision timestamp 9 + with a clock identifier to ensure uniqueness even when multiple records 10 + are created in the same microsecond. TIDs are sortable and provide both 11 + temporal ordering and uniqueness guarantees. 12 + 13 + Attributes: 14 + timestamp (datetime.datetime): The timestamp component of the TID. 15 + clockIdentifier (int): Clock identifier (0-1023) for disambiguation. 16 + """ 17 + 18 + def __init__( 19 + self, time: datetime.datetime | None = None, clockIdentifier: int | None = None 20 + ) -> None: 21 + """Initializes a TID object with timestamp and clock identifier. 22 + 23 + Creates a new TID with the specified timestamp and clock identifier. 24 + If no timestamp is provided, uses the current time. If no clock identifier 25 + is provided, generates a random value between 0-1023. 26 + 27 + Args: 28 + time (datetime.datetime, optional): The timestamp for the TID. 29 + Defaults to current time if not provided. 30 + clockIdentifier (int, optional): Clock identifier (0-1023) for 31 + disambiguation. Defaults to random value if not provided. 32 + """ 33 + if time == None: 34 + self.timestamp = datetime.datetime.now() 35 + else: 36 + self.timestamp = time 37 + if clockIdentifier == None: 38 + self.clockIdentifier = random.randrange(0, 1024) 39 + else: 40 + self.clockIdentifier = clockIdentifier 41 + 42 + def __int__(self): 43 + """Convert the TID to its integer representation. 44 + 45 + Combines the timestamp (in microseconds) and clock identifier into 46 + a single 64-bit integer where the high bits represent the timestamp 47 + and the low 10 bits represent the clock identifier. 48 + 49 + Returns: 50 + int: The integer representation of the TID. 51 + """ 52 + timestamp = int(self.timestamp.timestamp() * 1000000) 53 + return timestamp * 1024 + self.clockIdentifier 54 + 55 + def __str__(self): 56 + """Convert the TID to a base32-sortable string representation. 57 + 58 + Encodes the TID as a base32 string using a custom character set that 59 + maintains lexicographical sort order corresponding to temporal order. 60 + This format is commonly used in the AT Protocol for compact, 61 + sortable identifiers. 62 + 63 + Returns: 64 + str: The base32 string representation of the TID. 65 + """ 66 + integer = int(self) 67 + binary = f"{integer:065b}" 68 + return "".join( 69 + [ 70 + "234567abcdefghijklmnopqrstuvwxyz"[int(binary[i : i + 5], base=2)] 71 + for i in range(0, len(binary), 5) 72 + ] 73 + ) 74 + 75 + def __eq__(self, value: object, /) -> bool: 76 + """Check if two TID objects represent the same identifier. 77 + 78 + Args: 79 + value (object): The object to compare with. 80 + 81 + Returns: 82 + bool: True if the objects represent the same TID, False otherwise. 83 + """ 84 + if isinstance(value, TID): 85 + return int(self) == int(value) 86 + else: 87 + return False 88 + 89 + 90 + def importTIDfromInteger(value: int | None = None): 91 + """Create a TID object from an integer representation. 92 + 93 + Converts a 64-bit integer back into a TID object by extracting the 94 + timestamp and clock identifier components. If no value is provided, 95 + creates a TID for the current time. 96 + 97 + Args: 98 + value (int, optional): The integer value to convert to a TID. 99 + Defaults to creating a TID for the current time if not provided. 100 + 101 + Returns: 102 + TID: The TID object created from the integer value. 103 + """ 104 + if value == None: 105 + value = int(TID()) 106 + clockIdentifier = value % 1024 107 + timestamp = (value >> 10) / 1000000 108 + return TID(datetime.datetime.fromtimestamp(timestamp), clockIdentifier) 109 + 110 + 111 + def importTIDfromBase32(value: str | None = None): 112 + """Create a TID object from a base32 string representation. 113 + 114 + Converts a base32-encoded TID string back into a TID object by decoding 115 + the string to its integer representation and then extracting the timestamp 116 + and clock identifier components. If no value is provided, creates a TID 117 + for the current time. 118 + 119 + Args: 120 + value (str, optional): The base32 string to convert to a TID. 121 + Defaults to creating a TID for the current time if not provided. 122 + 123 + Returns: 124 + TID: The TID object created from the base32 string. 125 + """ 126 + if value == None: 127 + value = str(TID()) 128 + b32s = "234567abcdefghijklmnopqrstuvwxyz" 129 + return importTIDfromInteger( 130 + sum( 131 + [ 132 + b32s.find(value[i]) * (32 ** (len(value) - i - 1)) 133 + for i in range(len(value)) 134 + ] 135 + ) 136 + )