From f94b562bad73093a6a87f2856138619e2f81df1b Mon Sep 17 00:00:00 2001 From: goodboy Date: Wed, 4 Feb 2026 09:10:21 -0500 Subject: [PATCH 01/11] Add `uds` to `._multiaddr`, tweak typing --- tractor/discovery/_multiaddr.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tractor/discovery/_multiaddr.py b/tractor/discovery/_multiaddr.py index e8713b407..b9f1c4edb 100644 --- a/tractor/discovery/_multiaddr.py +++ b/tractor/discovery/_multiaddr.py @@ -38,6 +38,7 @@ 'tcp': 4, 'udp': 4, + 'uds': 4, # TODO: support the next-gen shite Bo # 'quic': 4, @@ -51,6 +52,7 @@ 'tcp': ('port',), 'udp': ('port',), + 'uds': ('path',), # 'quic': ('port',), # 'ssh': ('port',), @@ -75,7 +77,7 @@ def iter_prot_layers( assert not root # there is a root '/' on LHS itokens = iter(tokens) - prot: str | None = None + prot: str|None = None params: list[str] = [] for token in itokens: # every prot path should start with a known @@ -98,7 +100,10 @@ def iter_prot_layers( def parse_maddr( multiaddr: str, -) -> dict[str, str | int | dict]: +) -> dict[ + str, + str|int|dict, +]: ''' Parse a libp2p style "multiaddress" into its distinct protocol segments where each segment is of the form: @@ -122,14 +127,17 @@ def parse_maddr( `'/wg/1.1.1.1/51820/'` ''' - layers: dict[str, str | int | dict] = {} + layers: dict[str, str|int|dict] = {} for ( prot_key, params, ) in iter_prot_layers(multiaddr): layer: int = prots[prot_key] # OSI layer used for sorting - ep: dict[str, int | str] = {'layer': layer} + ep: dict[str, int|str] = { + 'layer': layer, + 'proto': prot_key, + } layers[prot_key] = ep # TODO; validation and resolving of names: @@ -139,7 +147,7 @@ def parse_maddr( # any loaded network.resolv: dict[str, str] rparams: list = list(reversed(params)) for key in prot_params[prot_key]: - val: str | int = rparams.pop() + val: str|int = rparams.pop() # TODO: UGHH, dunno what we should do for validation # here, put it in the params spec somehow? From 7b220c9f4170b5ad94fa2a988fcbda72164271b2 Mon Sep 17 00:00:00 2001 From: goodboy Date: Wed, 25 Mar 2026 18:56:56 -0400 Subject: [PATCH 02/11] Add `multiaddr` usage snippet for IP4 and UDS (this commit msg was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- snippets/multiaddr_ex.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 snippets/multiaddr_ex.py diff --git a/snippets/multiaddr_ex.py b/snippets/multiaddr_ex.py new file mode 100644 index 000000000..ef9a9137b --- /dev/null +++ b/snippets/multiaddr_ex.py @@ -0,0 +1,11 @@ +from multiaddr import Multiaddr +# construct from a string +m1 = Multiaddr("/ip4/127.0.0.1/udp/1234") +m2 = Multiaddr("/unix/run/user/1000/sway-ipc.1000.1557.sock") +for key in m1.protocols(): + key + +uds_sock_path = Path(m2.values()[0]) +uds_sock_path +uds_sock_path.is_file() +uds_sock_path.is_socket() From 536110fc29f615f7ad2646aef6ff0acaae26681c Mon Sep 17 00:00:00 2001 From: goodboy Date: Wed, 25 Mar 2026 19:02:42 -0400 Subject: [PATCH 03/11] Add `multiaddr` dep to `pyproject.toml` Bump lock file to match obvi. (this commit msg was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- pyproject.toml | 1 + uv.lock | 284 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 285 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 92e8bc21e..fc82ccdfe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,6 +49,7 @@ dependencies = [ "msgspec>=0.19.0", "cffi>=1.17.1", "bidict>=0.23.1", + "multiaddr>=0.2.0", "platformdirs>=4.4.0", ] diff --git a/uv.lock b/uv.lock index f629e974a..9eb2f36a0 100644 --- a/uv.lock +++ b/uv.lock @@ -2,6 +2,15 @@ version = 1 revision = 3 requires-python = ">=3.12, <3.14" +[[package]] +name = "async-generator" +version = "1.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/b6/6fa6b3b598a03cba5e80f829e0dadbb49d7645f523d209b2fb7ea0bbb02a/async_generator-1.10.tar.gz", hash = "sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144", size = 29870, upload-time = "2018-08-01T03:36:21.69Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/52/39d20e03abd0ac9159c162ec24b93fbcaa111e8400308f2465432495ca2b/async_generator-1.10-py3-none-any.whl", hash = "sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b", size = 18857, upload-time = "2018-08-01T03:36:20.029Z" }, +] + [[package]] name = "attrs" version = "24.3.0" @@ -11,6 +20,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/89/aa/ab0f7891a01eeb2d2e338ae8fecbe57fcebea1a24dbb64d45801bfab481d/attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308", size = 63397, upload-time = "2024-12-16T06:59:26.977Z" }, ] +[[package]] +name = "base58" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/45/8ae61209bb9015f516102fa559a2914178da1d5868428bd86a1b4421141d/base58-2.1.1.tar.gz", hash = "sha256:c5d0cb3f5b6e81e8e35da5754388ddcc6d0d14b6c6a132cb93d69ed580a7278c", size = 6528, upload-time = "2021-10-30T22:12:17.858Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/45/ec96b29162a402fc4c1c5512d114d7b3787b9d1c2ec241d9568b4816ee23/base58-2.1.1-py3-none-any.whl", hash = "sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2", size = 5621, upload-time = "2021-10-30T22:12:16.658Z" }, +] + [[package]] name = "bidict" version = "0.23.1" @@ -20,6 +38,50 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/99/37/e8730c3587a65eb5645d4aba2d27aae48e8003614d6aaf15dda67f702f1f/bidict-0.23.1-py3-none-any.whl", hash = "sha256:5dae8d4d79b552a71cbabc7deb25dfe8ce710b17ff41711e13010ead2abfc3e5", size = 32764, upload-time = "2024-02-18T19:09:04.156Z" }, ] +[[package]] +name = "blake3" +version = "1.0.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/75/aa/abcd75e9600987a0bc6cfe9b6b2ff3f0e2cb08c170addc6e76035b5c4cb3/blake3-1.0.8.tar.gz", hash = "sha256:513cc7f0f5a7c035812604c2c852a0c1468311345573de647e310aca4ab165ba", size = 117308, upload-time = "2025-10-14T06:47:48.83Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/a0/b7b6dff04012cfd6e665c09ee446f749bd8ea161b00f730fe1bdecd0f033/blake3-1.0.8-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:d8da4233984d51471bd4e4366feda1d90d781e712e0a504ea54b1f2b3577557b", size = 347983, upload-time = "2025-10-14T06:45:47.214Z" }, + { url = "https://files.pythonhosted.org/packages/5b/a2/264091cac31d7ae913f1f296abc20b8da578b958ffb86100a7ce80e8bf5c/blake3-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1257be19f2d381c868a34cc822fc7f12f817ddc49681b6d1a2790bfbda1a9865", size = 325415, upload-time = "2025-10-14T06:45:48.482Z" }, + { url = "https://files.pythonhosted.org/packages/ee/7d/85a4c0782f613de23d114a7a78fcce270f75b193b3ff3493a0de24ba104a/blake3-1.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:269f255b110840e52b6ce9db02217e39660ebad3e34ddd5bca8b8d378a77e4e1", size = 371296, upload-time = "2025-10-14T06:45:49.674Z" }, + { url = "https://files.pythonhosted.org/packages/e3/20/488475254976ed93fab57c67aa80d3b40df77f7d9db6528c9274bff53e08/blake3-1.0.8-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66ca28a673025c40db3eba21a9cac52f559f83637efa675b3f6bd8683f0415f3", size = 374516, upload-time = "2025-10-14T06:45:51.23Z" }, + { url = "https://files.pythonhosted.org/packages/7b/21/2a1c47fedb77fb396512677ec6d46caf42ac6e9a897db77edd0a2a46f7bb/blake3-1.0.8-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcb04966537777af56c1f399b35525aa70a1225816e121ff95071c33c0f7abca", size = 447911, upload-time = "2025-10-14T06:45:52.637Z" }, + { url = "https://files.pythonhosted.org/packages/cb/7d/db0626df16029713e7e61b67314c4835e85c296d82bd907c21c6ea271da2/blake3-1.0.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5b5da177d62cc4b7edf0cea08fe4dec960c9ac27f916131efa890a01f747b93", size = 505420, upload-time = "2025-10-14T06:45:54.445Z" }, + { url = "https://files.pythonhosted.org/packages/5b/55/6e737850c2d58a6d9de8a76dad2ae0f75b852a23eb4ecb07a0b165e6e436/blake3-1.0.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:38209b10482c97e151681ea3e91cc7141f56adbbf4820a7d701a923124b41e6a", size = 394189, upload-time = "2025-10-14T06:45:55.719Z" }, + { url = "https://files.pythonhosted.org/packages/5b/94/eafaa5cdddadc0c9c603a6a6d8339433475e1a9f60c8bb9c2eed2d8736b6/blake3-1.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:504d1399b7fb91dfe5c25722d2807990493185faa1917456455480c36867adb5", size = 388001, upload-time = "2025-10-14T06:45:57.067Z" }, + { url = "https://files.pythonhosted.org/packages/17/81/735fa00d13de7f68b25e1b9cb36ff08c6f165e688d85d8ec2cbfcdedccc5/blake3-1.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c84af132aa09abeadf9a0118c8fb26f4528f3f42c10ef8be0fcf31c478774ec4", size = 550302, upload-time = "2025-10-14T06:45:58.657Z" }, + { url = "https://files.pythonhosted.org/packages/0e/c6/d1fe8bdea4a6088bd54b5a58bc40aed89a4e784cd796af7722a06f74bae7/blake3-1.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a25db3d36b55f5ed6a86470155cc749fc9c5b91c949b8d14f48658f9d960d9ec", size = 554211, upload-time = "2025-10-14T06:46:00.269Z" }, + { url = "https://files.pythonhosted.org/packages/55/d1/ca74aa450cbe10e396e061f26f7a043891ffa1485537d6b30d3757e20995/blake3-1.0.8-cp312-cp312-win32.whl", hash = "sha256:e0fee93d5adcd44378b008c147e84f181f23715307a64f7b3db432394bbfce8b", size = 228343, upload-time = "2025-10-14T06:46:01.533Z" }, + { url = "https://files.pythonhosted.org/packages/4d/42/bbd02647169e3fbed27558555653ac2578c6f17ccacf7d1956c58ef1d214/blake3-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:6a6eafc29e4f478d365a87d2f25782a521870c8514bb43734ac85ae9be71caf7", size = 215704, upload-time = "2025-10-14T06:46:02.79Z" }, + { url = "https://files.pythonhosted.org/packages/55/b8/11de9528c257f7f1633f957ccaff253b706838d22c5d2908e4735798ec01/blake3-1.0.8-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:46dc20976bd6c235959ef0246ec73420d1063c3da2839a9c87ca395cf1fd7943", size = 347771, upload-time = "2025-10-14T06:46:04.248Z" }, + { url = "https://files.pythonhosted.org/packages/50/26/f7668be55c909678b001ecacff11ad7016cd9b4e9c7cc87b5971d638c5a9/blake3-1.0.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d17eb6382634b3a5bc0c0e0454d5265b0becaeeadb6801ed25150b39a999d0cc", size = 325431, upload-time = "2025-10-14T06:46:06.136Z" }, + { url = "https://files.pythonhosted.org/packages/77/57/e8a85fa261894bf7ce7af928ff3408aab60287ab8d58b55d13a3f700b619/blake3-1.0.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19fc6f2b7edab8acff6895fc6e38c19bd79f4c089e21153020c75dfc7397d52d", size = 370994, upload-time = "2025-10-14T06:46:07.398Z" }, + { url = "https://files.pythonhosted.org/packages/62/cd/765b76bb48b8b294fea94c9008b0d82b4cfa0fa2f3c6008d840d01a597e4/blake3-1.0.8-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4f54cff7f15d91dc78a63a2dd02a3dccdc932946f271e2adb4130e0b4cf608ba", size = 374372, upload-time = "2025-10-14T06:46:08.698Z" }, + { url = "https://files.pythonhosted.org/packages/36/7a/32084eadbb28592bb07298f0de316d2da586c62f31500a6b1339a7e7b29b/blake3-1.0.8-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7e12a777f6b798eb8d06f875d6e108e3008bd658d274d8c676dcf98e0f10537", size = 447627, upload-time = "2025-10-14T06:46:10.002Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f4/3788a1d86e17425eea147e28d7195d7053565fc279236a9fd278c2ec495e/blake3-1.0.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddfc59b0176fb31168f08d5dd536e69b1f4f13b5a0f4b0c3be1003efd47f9308", size = 507536, upload-time = "2025-10-14T06:46:11.614Z" }, + { url = "https://files.pythonhosted.org/packages/fe/01/4639cba48513b94192681b4da472cdec843d3001c5344d7051ee5eaef606/blake3-1.0.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a2336d5b2a801a7256da21150348f41610a6c21dae885a3acb1ebbd7333d88d8", size = 394105, upload-time = "2025-10-14T06:46:12.808Z" }, + { url = "https://files.pythonhosted.org/packages/21/ae/6e55c19c8460fada86cd1306a390a09b0c5a2e2e424f9317d2edacea439f/blake3-1.0.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4072196547484c95a5a09adbb952e9bb501949f03f9e2a85e7249ef85faaba8", size = 386928, upload-time = "2025-10-14T06:46:16.284Z" }, + { url = "https://files.pythonhosted.org/packages/ee/6c/05b7a5a907df1be53a8f19e7828986fc6b608a44119641ef9c0804fbef15/blake3-1.0.8-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0eab3318ec02f8e16fe549244791ace2ada2c259332f0c77ab22cf94dfff7130", size = 550003, upload-time = "2025-10-14T06:46:17.791Z" }, + { url = "https://files.pythonhosted.org/packages/b4/03/f0ea4adfedc1717623be6460b3710fcb725ca38082c14274369803f727e1/blake3-1.0.8-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:a33b9a1fb6d1d559a8e0d04b041e99419a6bb771311c774f6ff57ed7119c70ed", size = 553857, upload-time = "2025-10-14T06:46:19.088Z" }, + { url = "https://files.pythonhosted.org/packages/cc/6f/e5410d2e2a30c8aba8389ffc1c0061356916bf5ecd0a210344e7b69b62ab/blake3-1.0.8-cp313-cp313-win32.whl", hash = "sha256:e171b169cb7ea618e362a4dddb7a4d4c173bbc08b9ba41ea3086dd1265530d4f", size = 228315, upload-time = "2025-10-14T06:46:20.391Z" }, + { url = "https://files.pythonhosted.org/packages/79/ef/d9c297956dfecd893f29f59e7b22445aba5b47b7f6815d9ba5dcd73fcae6/blake3-1.0.8-cp313-cp313-win_amd64.whl", hash = "sha256:3168c457255b5d2a2fc356ba696996fcaff5d38284f968210d54376312107662", size = 215477, upload-time = "2025-10-14T06:46:21.542Z" }, + { url = "https://files.pythonhosted.org/packages/20/ba/eaa7723d66dd8ab762a3e85e139bb9c46167b751df6e950ad287adb8fb61/blake3-1.0.8-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:b4d672c24dc15ec617d212a338a4ca14b449829b6072d09c96c63b6e6b621aed", size = 347289, upload-time = "2025-10-14T06:46:22.772Z" }, + { url = "https://files.pythonhosted.org/packages/47/b3/6957f6ee27f0d5b8c4efdfda68a1298926a88c099f4dd89c711049d16526/blake3-1.0.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:1af0e5a29aa56d4fba904452ae784740997440afd477a15e583c38338e641f41", size = 324444, upload-time = "2025-10-14T06:46:24.729Z" }, + { url = "https://files.pythonhosted.org/packages/13/da/722cebca11238f3b24d3cefd2361c9c9ea47cfa0ad9288eeb4d1e0b7cf93/blake3-1.0.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef153c5860d5bf1cc71aece69b28097d2a392913eb323d6b52555c875d0439fc", size = 370441, upload-time = "2025-10-14T06:46:26.29Z" }, + { url = "https://files.pythonhosted.org/packages/2e/d5/2f7440c8e41c0af995bad3a159e042af0f4ed1994710af5b4766ca918f65/blake3-1.0.8-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e8ae3689f0c7bfa6ce6ae45cab110e4c3442125c4c23b28f1f097856de26e4d1", size = 374312, upload-time = "2025-10-14T06:46:27.451Z" }, + { url = "https://files.pythonhosted.org/packages/a6/6c/fb6a7812e60ce3e110bcbbb11f167caf3e975c589572c41e1271f35f2c41/blake3-1.0.8-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fb83532f7456ddeb68dae1b36e1f7c52f9cb72852ac01159bbcb1a12b0f8be0", size = 447007, upload-time = "2025-10-14T06:46:29.056Z" }, + { url = "https://files.pythonhosted.org/packages/13/3b/c99b43fae5047276ea9d944077c190fc1e5f22f57528b9794e21f7adedc6/blake3-1.0.8-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6ae7754c7d96e92a70a52e07c732d594cf9924d780f49fffd3a1e9235e0f5ba7", size = 507323, upload-time = "2025-10-14T06:46:30.661Z" }, + { url = "https://files.pythonhosted.org/packages/fc/bb/ba90eddd592f8c074a0694cb0a744b6bd76bfe67a14c2b490c8bdfca3119/blake3-1.0.8-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4bacaae75e98dee3b7da6c5ee3b81ee21a3352dd2477d6f1d1dbfd38cdbf158a", size = 393449, upload-time = "2025-10-14T06:46:31.805Z" }, + { url = "https://files.pythonhosted.org/packages/25/ed/58a2acd0b9e14459cdaef4344db414d4a36e329b9720921b442a454dd443/blake3-1.0.8-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9456c829601d72852d8ba0af8dae0610f7def1d59f5942efde1e2ef93e8a8b57", size = 386844, upload-time = "2025-10-14T06:46:33.195Z" }, + { url = "https://files.pythonhosted.org/packages/4a/04/fed09845b18d90862100c8e48308261e2f663aab25d3c71a6a0bdda6618b/blake3-1.0.8-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:497ef8096ec4ac1ffba9a66152cee3992337cebf8ea434331d8fd9ce5423d227", size = 549550, upload-time = "2025-10-14T06:46:35.23Z" }, + { url = "https://files.pythonhosted.org/packages/d6/65/1859fddfabc1cc72548c2269d988819aad96d854e25eae00531517925901/blake3-1.0.8-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:511133bab85ff60ed143424ce484d08c60894ff7323f685d7a6095f43f0c85c3", size = 553805, upload-time = "2025-10-14T06:46:36.532Z" }, + { url = "https://files.pythonhosted.org/packages/c1/c7/2969352017f62378e388bb07bb2191bc9a953f818dc1cd6b9dd5c24916e1/blake3-1.0.8-cp313-cp313t-win32.whl", hash = "sha256:9c9fbdacfdeb68f7ca53bb5a7a5a593ec996eaf21155ad5b08d35e6f97e60877", size = 228068, upload-time = "2025-10-14T06:46:37.826Z" }, + { url = "https://files.pythonhosted.org/packages/d8/fc/923e25ac9cadfff1cd20038bcc0854d0f98061eb6bc78e42c43615f5982d/blake3-1.0.8-cp313-cp313t-win_amd64.whl", hash = "sha256:3cec94ed5676821cf371e9c9d25a41b4f3ebdb5724719b31b2749653b7cc1dfa", size = 215369, upload-time = "2025-10-14T06:46:39.054Z" }, +] + [[package]] name = "cffi" version = "1.17.1" @@ -74,6 +136,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e3/51/9b208e85196941db2f0654ad0357ca6388ab3ed67efdbfc799f35d1f83aa/colorlog-6.9.0-py3-none-any.whl", hash = "sha256:5906e71acd67cb07a71e779c47c4bcb45fb8c2993eebe9e5adcd6a6f1b283eff", size = 11424, upload-time = "2024-10-29T18:34:49.815Z" }, ] +[[package]] +name = "dnspython" +version = "2.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, +] + [[package]] name = "greenback" version = "1.2.1" @@ -130,6 +201,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, ] +[[package]] +name = "importlib-metadata" +version = "9.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/01/15bb152d77b21318514a96f43af312635eb2500c96b55398d020c93d86ea/importlib_metadata-9.0.0.tar.gz", hash = "sha256:a4f57ab599e6a2e3016d7595cfd72eb4661a5106e787a95bcc90c7105b831efc", size = 56405, upload-time = "2026-03-20T06:42:56.999Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/3d/2d244233ac4f76e38533cfcb2991c9eb4c7bf688ae0a036d30725b8faafe/importlib_metadata-9.0.0-py3-none-any.whl", hash = "sha256:2d21d1cc5a017bd0559e36150c21c830ab1dc304dedd1b7ea85d20f45ef3edd7", size = 27789, upload-time = "2026-03-20T06:42:55.665Z" }, +] + [[package]] name = "iniconfig" version = "2.0.0" @@ -139,6 +222,59 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892, upload-time = "2023-01-07T11:08:09.864Z" }, ] +[[package]] +name = "mmh3" +version = "5.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/1a/edb23803a168f070ded7a3014c6d706f63b90c84ccc024f89d794a3b7a6d/mmh3-5.2.1.tar.gz", hash = "sha256:bbea5b775f0ac84945191fb83f845a6fd9a21a03ea7f2e187defac7e401616ad", size = 33775, upload-time = "2026-03-05T15:55:57.716Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/94/bc5c3b573b40a328c4d141c20e399039ada95e5e2a661df3425c5165fd84/mmh3-5.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0cc21533878e5586b80d74c281d7f8da7932bc8ace50b8d5f6dbf7e3935f63f1", size = 56087, upload-time = "2026-03-05T15:54:21.92Z" }, + { url = "https://files.pythonhosted.org/packages/f6/80/64a02cc3e95c3af0aaa2590849d9ed24a9f14bb93537addde688e039b7c3/mmh3-5.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4eda76074cfca2787c8cf1bec603eaebdddd8b061ad5502f85cddae998d54f00", size = 40500, upload-time = "2026-03-05T15:54:22.953Z" }, + { url = "https://files.pythonhosted.org/packages/8b/72/e6d6602ce18adf4ddcd0e48f2e13590cc92a536199e52109f46f259d3c46/mmh3-5.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:eee884572b06bbe8a2b54f424dbd996139442cf83c76478e1ec162512e0dd2c7", size = 40034, upload-time = "2026-03-05T15:54:23.943Z" }, + { url = "https://files.pythonhosted.org/packages/59/c2/bf4537a8e58e21886ef16477041238cab5095c836496e19fafc34b7445d2/mmh3-5.2.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0d0b7e803191db5f714d264044e06189c8ccd3219e936cc184f07106bd17fd7b", size = 97292, upload-time = "2026-03-05T15:54:25.335Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e2/51ed62063b44d10b06d975ac87af287729eeb5e3ed9772f7584a17983e90/mmh3-5.2.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8e6c219e375f6341d0959af814296372d265a8ca1af63825f65e2e87c618f006", size = 103274, upload-time = "2026-03-05T15:54:26.44Z" }, + { url = "https://files.pythonhosted.org/packages/75/ce/12a7524dca59eec92e5b31fdb13ede1e98eda277cf2b786cf73bfbc24e81/mmh3-5.2.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:26fb5b9c3946bf7f1daed7b37e0c03898a6f062149127570f8ede346390a0825", size = 106158, upload-time = "2026-03-05T15:54:28.578Z" }, + { url = "https://files.pythonhosted.org/packages/86/1f/d3ba6dd322d01ab5d44c46c8f0c38ab6bbbf9b5e20e666dfc05bf4a23604/mmh3-5.2.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3c38d142c706201db5b2345166eeef1e7740e3e2422b470b8ba5c8727a9b4c7a", size = 113005, upload-time = "2026-03-05T15:54:29.767Z" }, + { url = "https://files.pythonhosted.org/packages/b6/a9/15d6b6f913294ea41b44d901741298e3718e1cb89ee626b3694625826a43/mmh3-5.2.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50885073e2909251d4718634a191c49ae5f527e5e1736d738e365c3e8be8f22b", size = 120744, upload-time = "2026-03-05T15:54:30.931Z" }, + { url = "https://files.pythonhosted.org/packages/76/b3/70b73923fd0284c439860ff5c871b20210dfdbe9a6b9dd0ee6496d77f174/mmh3-5.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b3f99e1756fc48ad507b95e5d86f2fb21b3d495012ff13e6592ebac14033f166", size = 99111, upload-time = "2026-03-05T15:54:32.353Z" }, + { url = "https://files.pythonhosted.org/packages/dd/38/99f7f75cd27d10d8b899a1caafb9d531f3903e4d54d572220e3d8ac35e89/mmh3-5.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:62815d2c67f2dd1be76a253d88af4e1da19aeaa1820146dec52cf8bee2958b16", size = 98623, upload-time = "2026-03-05T15:54:33.801Z" }, + { url = "https://files.pythonhosted.org/packages/fd/68/6e292c0853e204c44d2f03ea5f090be3317a0e2d9417ecb62c9eb27687df/mmh3-5.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8f767ba0911602ddef289404e33835a61168314ebd3c729833db2ed685824211", size = 106437, upload-time = "2026-03-05T15:54:35.177Z" }, + { url = "https://files.pythonhosted.org/packages/dd/c6/fedd7284c459cfb58721d461fcf5607a4c1f5d9ab195d113d51d10164d16/mmh3-5.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:67e41a497bac88cc1de96eeba56eeb933c39d54bc227352f8455aa87c4ca4000", size = 110002, upload-time = "2026-03-05T15:54:36.673Z" }, + { url = "https://files.pythonhosted.org/packages/3b/ac/ca8e0c19a34f5b71390171d2ff0b9f7f187550d66801a731bb68925126a4/mmh3-5.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d74a03fb57757ece25aa4b3c1c60157a1cece37a020542785f942e2f827eed5", size = 97507, upload-time = "2026-03-05T15:54:37.804Z" }, + { url = "https://files.pythonhosted.org/packages/df/94/6ebb9094cfc7ac5e7950776b9d13a66bb4a34f83814f32ba2abc9494fc68/mmh3-5.2.1-cp312-cp312-win32.whl", hash = "sha256:7374d6e3ef72afe49697ecd683f3da12f4fc06af2d75433d0580c6746d2fa025", size = 40773, upload-time = "2026-03-05T15:54:40.077Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3c/cd3527198cf159495966551c84a5f36805a10ac17b294f41f67b83f6a4d6/mmh3-5.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:3a9fed49c6ce4ed7e73f13182760c65c816da006debe67f37635580dfb0fae00", size = 41560, upload-time = "2026-03-05T15:54:41.148Z" }, + { url = "https://files.pythonhosted.org/packages/15/96/6fe5ebd0f970a076e3ed5512871ce7569447b962e96c125528a2f9724470/mmh3-5.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:bbfcb95d9a744e6e2827dfc66ad10e1020e0cac255eb7f85652832d5a264c2fc", size = 39313, upload-time = "2026-03-05T15:54:42.171Z" }, + { url = "https://files.pythonhosted.org/packages/25/a5/9daa0508a1569a54130f6198d5462a92deda870043624aa3ea72721aa765/mmh3-5.2.1-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:723b2681ed4cc07d3401bbea9c201ad4f2a4ca6ba8cddaff6789f715dd2b391e", size = 40832, upload-time = "2026-03-05T15:54:43.212Z" }, + { url = "https://files.pythonhosted.org/packages/0a/6b/3230c6d80c1f4b766dedf280a92c2241e99f87c1504ff74205ec8cebe451/mmh3-5.2.1-cp313-cp313-android_21_x86_64.whl", hash = "sha256:3619473a0e0d329fd4aec8075628f8f616be2da41605300696206d6f36920c3d", size = 41964, upload-time = "2026-03-05T15:54:44.204Z" }, + { url = "https://files.pythonhosted.org/packages/62/fb/648bfddb74a872004b6ee751551bfdda783fe6d70d2e9723bad84dbe5311/mmh3-5.2.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:e48d4dbe0f88e53081da605ae68644e5182752803bbc2beb228cca7f1c4454d6", size = 39114, upload-time = "2026-03-05T15:54:45.205Z" }, + { url = "https://files.pythonhosted.org/packages/95/c2/ab7901f87af438468b496728d11264cb397b3574d41506e71b92128e0373/mmh3-5.2.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a482ac121de6973897c92c2f31defc6bafb11c83825109275cffce54bb64933f", size = 39819, upload-time = "2026-03-05T15:54:46.509Z" }, + { url = "https://files.pythonhosted.org/packages/2f/ed/6f88dda0df67de1612f2e130ffea34cf84aaee5bff5b0aff4dbff2babe34/mmh3-5.2.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:17fbb47f0885ace8327ce1235d0416dc86a211dcd8cc1e703f41523be32cfec8", size = 40330, upload-time = "2026-03-05T15:54:47.864Z" }, + { url = "https://files.pythonhosted.org/packages/3d/66/7516d23f53cdf90f43fce24ab80c28f45e6851d78b46bef8c02084edf583/mmh3-5.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d51fde50a77f81330523562e3c2734ffdca9c4c9e9d355478117905e1cfe16c6", size = 56078, upload-time = "2026-03-05T15:54:48.9Z" }, + { url = "https://files.pythonhosted.org/packages/bc/34/4d152fdf4a91a132cb226b671f11c6b796eada9ab78080fb5ce1e95adaab/mmh3-5.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:19bbd3b841174ae6ed588536ab5e1b1fe83d046e668602c20266547298d939a9", size = 40498, upload-time = "2026-03-05T15:54:49.942Z" }, + { url = "https://files.pythonhosted.org/packages/d4/4c/8e3af1b6d85a299767ec97bd923f12b06267089c1472c27c1696870d1175/mmh3-5.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be77c402d5e882b6fbacfd90823f13da8e0a69658405a39a569c6b58fdb17b03", size = 40033, upload-time = "2026-03-05T15:54:50.994Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f2/966ea560e32578d453c9e9db53d602cbb1d0da27317e232afa7c38ceba11/mmh3-5.2.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fd96476f04db5ceba1cfa0f21228f67c1f7402296f0e73fee3513aa680ad237b", size = 97320, upload-time = "2026-03-05T15:54:52.072Z" }, + { url = "https://files.pythonhosted.org/packages/bb/0d/2c5f9893b38aeb6b034d1a44ecd55a010148054f6a516abe53b5e4057297/mmh3-5.2.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:707151644085dd0f20fe4f4b573d28e5130c4aaa5f587e95b60989c5926653b5", size = 103299, upload-time = "2026-03-05T15:54:53.569Z" }, + { url = "https://files.pythonhosted.org/packages/1c/fc/2ebaef4a4d4376f89761274dc274035ffd96006ab496b4ee5af9b08f21a9/mmh3-5.2.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3737303ca9ea0f7cb83028781148fcda4f1dac7821db0c47672971dabcf63593", size = 106222, upload-time = "2026-03-05T15:54:55.092Z" }, + { url = "https://files.pythonhosted.org/packages/57/09/ea7ffe126d0ba0406622602a2d05e1e1a6841cc92fc322eb576c95b27fad/mmh3-5.2.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2778fed822d7db23ac5008b181441af0c869455b2e7d001f4019636ac31b6fe4", size = 113048, upload-time = "2026-03-05T15:54:56.305Z" }, + { url = "https://files.pythonhosted.org/packages/85/57/9447032edf93a64aa9bef4d9aa596400b1756f40411890f77a284f6293ca/mmh3-5.2.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d57dea657357230cc780e13920d7fa7db059d58fe721c80020f94476da4ca0a1", size = 120742, upload-time = "2026-03-05T15:54:57.453Z" }, + { url = "https://files.pythonhosted.org/packages/53/82/a86cc87cc88c92e9e1a598fee509f0409435b57879a6129bf3b3e40513c7/mmh3-5.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:169e0d178cb59314456ab30772429a802b25d13227088085b0d49b9fe1533104", size = 99132, upload-time = "2026-03-05T15:54:58.583Z" }, + { url = "https://files.pythonhosted.org/packages/54/f7/6b16eb1b40ee89bb740698735574536bc20d6cdafc65ae702ea235578e05/mmh3-5.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7e4e1f580033335c6f76d1e0d6b56baf009d1a64d6a4816347e4271ba951f46d", size = 98686, upload-time = "2026-03-05T15:55:00.078Z" }, + { url = "https://files.pythonhosted.org/packages/e8/88/a601e9f32ad1410f438a6d0544298ea621f989bd34a0731a7190f7dec799/mmh3-5.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:2bd9f19f7f1fcebd74e830f4af0f28adad4975d40d80620be19ffb2b2af56c9f", size = 106479, upload-time = "2026-03-05T15:55:01.532Z" }, + { url = "https://files.pythonhosted.org/packages/d6/5c/ce29ae3dfc4feec4007a437a1b7435fb9507532a25147602cd5b52be86db/mmh3-5.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:c88653877aeb514c089d1b3d473451677b8b9a6d1497dbddf1ae7934518b06d2", size = 110030, upload-time = "2026-03-05T15:55:02.934Z" }, + { url = "https://files.pythonhosted.org/packages/13/30/ae444ef2ff87c805d525da4fa63d27cda4fe8a48e77003a036b8461cfd5c/mmh3-5.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fceef7fe67c81e1585198215e42ad3fdba3a25644beda8fbdaf85f4d7b93175a", size = 97536, upload-time = "2026-03-05T15:55:04.135Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f9/dc3787ee5c813cc27fe79f45ad4500d9b5437f23a7402435cc34e07c7718/mmh3-5.2.1-cp313-cp313-win32.whl", hash = "sha256:54b64fb2433bc71488e7a449603bf8bd31fbcf9cb56fbe1eb6d459e90b86c37b", size = 40769, upload-time = "2026-03-05T15:55:05.277Z" }, + { url = "https://files.pythonhosted.org/packages/43/67/850e0b5a1e97799822ebfc4ca0e8c6ece3ed8baf7dcdf64de817dfdda2ca/mmh3-5.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:cae6383181f1e345317742d2ddd88f9e7d2682fa4c9432e3a74e47d92dce0229", size = 41563, upload-time = "2026-03-05T15:55:06.283Z" }, + { url = "https://files.pythonhosted.org/packages/c0/cc/98c90b28e1da5458e19fbfaf4adb5289208d3bfccd45dd14eab216a2f0bb/mmh3-5.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:022aa1a528604e6c83d0a7705fdef0b5355d897a9e0fa3a8d26709ceaa06965d", size = 39310, upload-time = "2026-03-05T15:55:07.323Z" }, +] + +[[package]] +name = "morphys" +version = "1.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/4f/cb781d0ac5d079adabc77dc4f0bc99fc81c390029bd33c6e70552139e762/morphys-1.0-py2.py3-none-any.whl", hash = "sha256:76d6dbaa4d65f597e59d332c81da786d83e4669387b9b2a750cfec74e7beec20", size = 5618, upload-time = "2017-01-10T20:08:56.872Z" }, +] + [[package]] name = "msgspec" version = "0.19.0" @@ -161,6 +297,47 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/23/d8/f15b40611c2d5753d1abb0ca0da0c75348daf1252220e5dda2867bd81062/msgspec-0.19.0-cp313-cp313-win_amd64.whl", hash = "sha256:317050bc0f7739cb30d257ff09152ca309bf5a369854bbf1e57dffc310c1f20f", size = 187432, upload-time = "2024-12-27T17:40:16.256Z" }, ] +[[package]] +name = "multiaddr" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "base58" }, + { name = "dnspython" }, + { name = "idna" }, + { name = "netaddr" }, + { name = "psutil" }, + { name = "py-cid" }, + { name = "py-multibase" }, + { name = "py-multicodec" }, + { name = "py-multihash" }, + { name = "trio" }, + { name = "trio-typing" }, + { name = "varint" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/10/4e26a8577cfce1c0febc8d83087e1373e93c695c6e73ad010546fb67e229/multiaddr-0.2.0.tar.gz", hash = "sha256:acb6b25c332ec1b2f1f8fef8d03a8c63385d34a87d690df0f4bba43cdf6efe8d", size = 58356, upload-time = "2026-03-17T21:51:00.274Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/13/56e503d01218d1ca27ea9fda862045a4b400cae5e756f47315f5aaba0eee/multiaddr-0.2.0-py3-none-any.whl", hash = "sha256:bcff7bf3d7de3d6da0b865b25423bcb411de1d20d70cc6abfacf75170d17866c", size = 40424, upload-time = "2026-03-17T21:50:58.833Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "netaddr" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/90/188b2a69654f27b221fba92fda7217778208532c962509e959a9cee5229d/netaddr-1.3.0.tar.gz", hash = "sha256:5c3c3d9895b551b763779ba7db7a03487dc1f8e3b385af819af341ae9ef6e48a", size = 2260504, upload-time = "2024-05-28T21:30:37.743Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/cc/f4fe2c7ce68b92cbf5b2d379ca366e1edae38cccaad00f69f529b460c3ef/netaddr-1.3.0-py3-none-any.whl", hash = "sha256:c2c6a8ebe5554ce33b7d5b3a306b71bbb373e000bbbf2350dd5213cc56e3dbbe", size = 2262023, upload-time = "2024-05-28T21:30:34.191Z" }, +] + [[package]] name = "outcome" version = "1.3.0.post0" @@ -262,6 +439,64 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, ] +[[package]] +name = "py-cid" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "morphys" }, + { name = "py-multibase" }, + { name = "py-multicodec" }, + { name = "py-multihash" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/68c2bd0346247570e8e01e8c170a0237884e95cdfa43989527b71adaa978/py_cid-0.5.0.tar.gz", hash = "sha256:93c62586c672353a9862f3fce13c9848ea39a00378e0980e2f0eed91631f3d28", size = 38028, upload-time = "2026-02-13T19:03:28.603Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/18/eaea1571ae8b4fa490793a4b78a9641c4579a884f7a26f3d1b019d7e91c2/py_cid-0.5.0-py3-none-any.whl", hash = "sha256:2fbad437384534e2a0ab0c4068aac3e510c4cb710c89c8f6bf98f4b07ed54e3e", size = 16046, upload-time = "2026-02-13T19:03:27.516Z" }, +] + +[[package]] +name = "py-multibase" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "morphys" }, + { name = "python-baseconv" }, + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/52/5ed393ab49df7e3b03995d3c4e53bae1e8c2ca40909cf25a41b346c09a38/py_multibase-2.0.0.tar.gz", hash = "sha256:58c1a264195fa1ae29ea707c6fc8196446f4bdb92e0f9a0f131e0f280b238839", size = 26857, upload-time = "2025-12-18T02:24:49.132Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/c7/38035079d9978b32b962f996f1cccaa166ecfe38723ab4349ab32166c037/py_multibase-2.0.0-py3-none-any.whl", hash = "sha256:b29ce489b556134e73998a11712c406b70950812955df64084754e0774e40900", size = 10608, upload-time = "2025-12-18T02:24:47.827Z" }, +] + +[[package]] +name = "py-multicodec" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "varint" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/26/ef24db0fbfec080b72c5ac4a1000da3a4d696a1e31862c695d683097a1b5/py_multicodec-1.0.0.tar.gz", hash = "sha256:78e4e3e47b6288cf635c3ca987152e6cb5510bdcdab307e7690c76ec3d5bbfeb", size = 44668, upload-time = "2025-12-18T20:41:37.976Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/da/768d07490faeae88ac361184164be9c262fececc3c6241b5fc471be4f659/py_multicodec-1.0.0-py3-none-any.whl", hash = "sha256:ae2e687bac8fdf54e3f5b3feded36b61a304d5e3c3af9438f7481f543ec15b8d", size = 26200, upload-time = "2025-12-18T20:41:37.055Z" }, +] + +[[package]] +name = "py-multihash" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "base58" }, + { name = "blake3" }, + { name = "mmh3" }, + { name = "morphys" }, + { name = "six" }, + { name = "varint" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/11/3d/ed68b0eccd0654f7f3c163d9b3d428f903e5e3e884ab1f0d0a16ba6a4f11/py_multihash-3.0.0.tar.gz", hash = "sha256:2e848941de5ef0533ca26b81940e2ffcf7b4322a3f803e8c97f4f0eca8767aa7", size = 41630, upload-time = "2025-12-17T19:30:00.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/e2/d65606db8369916fb5a9b4fe14df7e6072970d919300f3fb1c989a1d8e7d/py_multihash-3.0.0-py3-none-any.whl", hash = "sha256:3863ec1313b4eac1e5169137c143d40bf77456e57388f839441deba089f87326", size = 21215, upload-time = "2025-12-17T19:29:59.322Z" }, +] + [[package]] name = "pycparser" version = "2.22" @@ -310,6 +545,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634, upload-time = "2025-03-02T12:54:52.069Z" }, ] +[[package]] +name = "python-baseconv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/33/d0/9297d7d8dd74767b4d5560d834b30b2fff17d39987c23ed8656f476e0d9b/python-baseconv-1.2.2.tar.gz", hash = "sha256:0539f8bd0464013b05ad62e0a1673f0ac9086c76b43ebf9f833053527cd9931b", size = 4929, upload-time = "2019-04-04T19:28:57.17Z" } + [[package]] name = "ruff" version = "0.14.14" @@ -336,6 +577,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9e/6a/40fee331a52339926a92e17ae748827270b288a35ef4a15c9c8f2ec54715/ruff-0.14.14-py3-none-win_arm64.whl", hash = "sha256:56e6981a98b13a32236a72a8da421d7839221fa308b223b9283312312e5ac76c", size = 10920448, upload-time = "2026-01-22T22:30:15.417Z" }, ] +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + [[package]] name = "sniffio" version = "1.3.1" @@ -384,6 +634,7 @@ dependencies = [ { name = "cffi" }, { name = "colorlog" }, { name = "msgspec" }, + { name = "multiaddr" }, { name = "pdbp" }, { name = "platformdirs" }, { name = "tricycle" }, @@ -428,6 +679,7 @@ requires-dist = [ { name = "cffi", specifier = ">=1.17.1" }, { name = "colorlog", specifier = ">=6.8.2,<7" }, { name = "msgspec", specifier = ">=0.19.0" }, + { name = "multiaddr", specifier = ">=0.2.0" }, { name = "pdbp", specifier = ">=1.8.2,<2" }, { name = "platformdirs", specifier = ">=4.4.0" }, { name = "tricycle", specifier = ">=0.4.1,<0.5" }, @@ -493,6 +745,23 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c9/55/c4d9bea8b3d7937901958f65124123512419ab0eb73695e5f382521abbfb/trio-0.29.0-py3-none-any.whl", hash = "sha256:d8c463f1a9cc776ff63e331aba44c125f423a5a13c684307e828d930e625ba66", size = 492920, upload-time = "2025-02-14T07:13:48.696Z" }, ] +[[package]] +name = "trio-typing" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "async-generator" }, + { name = "importlib-metadata" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "trio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/74/a87aafa40ec3a37089148b859892cbe2eef08d132c816d58a60459be5337/trio-typing-0.10.0.tar.gz", hash = "sha256:065ee684296d52a8ab0e2374666301aec36ee5747ac0e7a61f230250f8907ac3", size = 38747, upload-time = "2023-12-01T02:54:55.508Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/ff/9bd795273eb14fac7f6a59d16cc8c4d0948a619a1193d375437c7f50f3eb/trio_typing-0.10.0-py3-none-any.whl", hash = "sha256:6d0e7ec9d837a2fe03591031a172533fbf4a1a95baf369edebfc51d5a49f0264", size = 42224, upload-time = "2023-12-01T02:54:54.1Z" }, +] + [[package]] name = "typing-extensions" version = "4.14.1" @@ -502,6 +771,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" }, ] +[[package]] +name = "varint" +version = "1.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a8/fe/1ea0ba0896dfa47186692655b86db3214c4b7c9e0e76c7b1dc257d101ab1/varint-1.0.2.tar.gz", hash = "sha256:a6ecc02377ac5ee9d65a6a8ad45c9ff1dac8ccee19400a5950fb51d594214ca5", size = 1886, upload-time = "2016-02-24T20:42:38.5Z" } + [[package]] name = "wcwidth" version = "0.2.13" @@ -563,3 +838,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2e/c2/3dd498dc28d8f89cdd52e39950c5e591499ae423f61694c0bb4d03ed1d82/xonsh-0.22.4-py312-none-any.whl", hash = "sha256:4e538fac9f4c3d866ddbdeca068f0c0515469c997ed58d3bfee963878c6df5a5", size = 654300, upload-time = "2026-02-17T07:53:35.813Z" }, { url = "https://files.pythonhosted.org/packages/82/7d/1f9c7147518e9f03f6ce081b5bfc4f1aceb6ec5caba849024d005e41d3be/xonsh-0.22.4-py313-none-any.whl", hash = "sha256:cc5fabf0ad0c56a2a11bed1e6a43c4ec6416a5b30f24f126b8e768547c3793e2", size = 654818, upload-time = "2026-02-17T07:53:33.477Z" }, ] + +[[package]] +name = "zipp" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, +] From 2de1d03e8df65991f4ab6711953cf3c39609e2e1 Mon Sep 17 00:00:00 2001 From: goodboy Date: Wed, 25 Mar 2026 19:04:13 -0400 Subject: [PATCH 04/11] Use upstream `py-multiaddr` for `._multiaddr` Drop the NIH (notinventedhere) custom parser (`parse_maddr()`, `iter_prot_layers()`, `prots`/`prot_params` tables) which was never called anywhere in the codebase. Replace with a thin `mk_maddr()` factory that wraps the upstream `multiaddr.Multiaddr` type, dispatching on `Address.proto_key` to build spec-compliant paths. Deats, - `'tcp'` addrs detect ipv4 vs ipv6 via stdlib `ipaddress` (resolves existing TODO) - `'uds'` addrs map to `/unix/{path}` per the multiformats protocol registry (code 400) - fix UDS `.maddr` to include full sockpath (previously only used `filedir`, dropped filename) - standardize protocol names: `ipv4`->`ip4`, `uds`->`unix` - `.maddr` properties now return `Multiaddr` objs (`__str__()` gives the canonical path form so all existing f-string/log consumers work unchanged) - update `MsgTransport` protocol hint accordingly (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- tractor/discovery/_multiaddr.py | 176 +++++++++----------------------- tractor/ipc/_tcp.py | 19 +--- tractor/ipc/_transport.py | 4 +- tractor/ipc/_uds.py | 14 +-- tractor/runtime/_runtime.py | 2 +- 5 files changed, 58 insertions(+), 157 deletions(-) diff --git a/tractor/discovery/_multiaddr.py b/tractor/discovery/_multiaddr.py index b9f1c4edb..99dd620ef 100644 --- a/tractor/discovery/_multiaddr.py +++ b/tractor/discovery/_multiaddr.py @@ -15,145 +15,61 @@ # along with this program. If not, see . ''' -Multiaddress parser and utils according the spec(s) defined by -`libp2p` and used in dependent project such as `ipfs`: +Multiaddress support using the upstream `py-multiaddr` lib +(a `libp2p` community standard) instead of our own NIH parser. -- https://docs.libp2p.io/concepts/fundamentals/addressing/ -- https://github.com/libp2p/specs/blob/master/addressing/README.md +- https://github.com/multiformats/multiaddr +- https://github.com/multiformats/py-multiaddr +- https://github.com/multiformats/multiaddr/blob/master/protocols.csv +- https://github.com/multiformats/multiaddr/blob/master/protocols/unix.md ''' -from typing import Iterator +import ipaddress +from pathlib import Path +from typing import TYPE_CHECKING -from bidict import bidict +from multiaddr import Multiaddr -# TODO: see if we can leverage libp2p ecosys projects instead of -# rolling our own (parser) impls of the above addressing specs: -# - https://github.com/libp2p/py-libp2p -# - https://docs.libp2p.io/concepts/nat/circuit-relay/#relay-addresses -# prots: bidict[int, str] = bidict({ -prots: bidict[int, str] = { - 'ipv4': 3, - 'ipv6': 3, - 'wg': 3, +if TYPE_CHECKING: + from tractor.discovery._addr import Address - 'tcp': 4, - 'udp': 4, - 'uds': 4, - - # TODO: support the next-gen shite Bo - # 'quic': 4, - # 'ssh': 7, # via rsyscall bootstrapping +# map from tractor-internal `proto_key` identifiers +# to the standard multiaddr protocol name strings. +_tpt_proto_to_maddr: dict[str, str] = { + 'tcp': 'tcp', + 'uds': 'unix', } -prot_params: dict[str, tuple[str]] = { - 'ipv4': ('addr',), - 'ipv6': ('addr',), - 'wg': ('addr', 'port', 'pubkey'), - - 'tcp': ('port',), - 'udp': ('port',), - 'uds': ('path',), - - # 'quic': ('port',), - # 'ssh': ('port',), -} - -def iter_prot_layers( - multiaddr: str, -) -> Iterator[ - tuple[ - int, - list[str] - ] -]: +def mk_maddr( + addr: 'Address', +) -> Multiaddr: ''' - Unpack a libp2p style "multiaddress" into multiple "segments" - for each "layer" of the protocoll stack (in OSI terms). + Construct a `Multiaddr` from a tractor `Address` instance, + dispatching on the `.proto_key` to build the correct + multiaddr-spec-compliant protocol path. ''' - tokens: list[str] = multiaddr.split('/') - root, tokens = tokens[0], tokens[1:] - assert not root # there is a root '/' on LHS - itokens = iter(tokens) - - prot: str|None = None - params: list[str] = [] - for token in itokens: - # every prot path should start with a known - # key-str. - if token in prots: - if prot is None: - prot: str = token - else: - yield prot, params - prot = token - - params = [] - - elif token not in prots: - params.append(token) - - else: - yield prot, params - - -def parse_maddr( - multiaddr: str, -) -> dict[ - str, - str|int|dict, -]: - ''' - Parse a libp2p style "multiaddress" into its distinct protocol - segments where each segment is of the form: - - `..////../` - - and is loaded into a (order preserving) `layers: dict[str, - dict[str, Any]` which holds each protocol-layer-segment of the - original `str` path as a separate entry according to its approx - OSI "layer number". - - Any `paramN` in the path must be distinctly defined by a str-token in the - (module global) `prot_params` table. - - For eg. for wireguard which requires an address, port number and publickey - the protocol params are specified as the entry: - - 'wg': ('addr', 'port', 'pubkey'), - - and are thus parsed from a maddr in that order: - `'/wg/1.1.1.1/51820/'` - - ''' - layers: dict[str, str|int|dict] = {} - for ( - prot_key, - params, - ) in iter_prot_layers(multiaddr): - - layer: int = prots[prot_key] # OSI layer used for sorting - ep: dict[str, int|str] = { - 'layer': layer, - 'proto': prot_key, - } - layers[prot_key] = ep - - # TODO; validation and resolving of names: - # - each param via a validator provided as part of the - # prot_params def? (also see `"port"` case below..) - # - do a resolv step that will check addrs against - # any loaded network.resolv: dict[str, str] - rparams: list = list(reversed(params)) - for key in prot_params[prot_key]: - val: str|int = rparams.pop() - - # TODO: UGHH, dunno what we should do for validation - # here, put it in the params spec somehow? - if key == 'port': - val = int(val) - - ep[key] = val - - return layers + match addr.proto_key: + case 'tcp': + host, port = addr.unwrap() + ip = ipaddress.ip_address(host) + net_proto: str = ( + 'ip4' if ip.version == 4 + else 'ip6' + ) + return Multiaddr( + f'/{net_proto}/{host}/tcp/{port}' + ) + + case 'uds': + filedir, filename = addr.unwrap() + filepath = Path(filedir) / filename + return Multiaddr( + f'/unix/{filepath}' + ) + + case _: + raise ValueError( + f'Unsupported proto_key: {addr.proto_key!r}' + ) diff --git a/tractor/ipc/_tcp.py b/tractor/ipc/_tcp.py index b05f28294..293ae4be3 100644 --- a/tractor/ipc/_tcp.py +++ b/tractor/ipc/_tcp.py @@ -33,8 +33,10 @@ open_tcp_listeners, ) +from multiaddr import Multiaddr from tractor.msg import MsgCodec from tractor.log import get_logger +from tractor.discovery._multiaddr import mk_maddr from tractor.ipc._transport import ( MsgTransport, MsgpackTransport, @@ -198,21 +200,8 @@ class MsgpackTCPStream(MsgpackTransport): layer_key: int = 4 @property - def maddr(self) -> str: - host, port = self.raddr.unwrap() - return ( - # TODO, use `ipaddress` from stdlib to handle - # first detecting which of `ipv4/6` before - # choosing the routing prefix part. - f'/ipv4/{host}' - - f'/{self.address_type.proto_key}/{port}' - # f'/{self.chan.uid[0]}' - # f'/{self.cid}' - - # f'/cid={cid_head}..{cid_tail}' - # TODO: ? not use this ^ right ? - ) + def maddr(self) -> Multiaddr: + return mk_maddr(self.raddr) def connected(self) -> bool: return self.stream.socket.fileno() != -1 diff --git a/tractor/ipc/_transport.py b/tractor/ipc/_transport.py index a3f872937..0a38d14d7 100644 --- a/tractor/ipc/_transport.py +++ b/tractor/ipc/_transport.py @@ -27,6 +27,8 @@ ClassVar, TYPE_CHECKING, ) +if TYPE_CHECKING: + from multiaddr import Multiaddr from collections.abc import ( AsyncGenerator, AsyncIterator, @@ -118,7 +120,7 @@ def raddr(self) -> Address: ... @property - def maddr(self) -> str: + def maddr(self) -> Multiaddr|str: ... @classmethod diff --git a/tractor/ipc/_uds.py b/tractor/ipc/_uds.py index 26174d55e..51f213530 100644 --- a/tractor/ipc/_uds.py +++ b/tractor/ipc/_uds.py @@ -48,8 +48,10 @@ has_unix, ) +from multiaddr import Multiaddr from tractor.msg import MsgCodec from tractor.log import get_logger +from tractor.discovery._multiaddr import mk_maddr from tractor.ipc._transport import ( MsgpackTransport, ) @@ -442,19 +444,11 @@ class MsgpackUDSStream(MsgpackTransport): layer_key: int = 4 @property - def maddr(self) -> str: + def maddr(self) -> Multiaddr|str: if not self.raddr: return '' - filepath: Path = Path(self.raddr.unwrap()[0]) - return ( - f'/{self.address_type.proto_key}/{filepath}' - # f'/{self.chan.uid[0]}' - # f'/{self.cid}' - - # f'/cid={cid_head}..{cid_tail}' - # TODO: ? not use this ^ right ? - ) + return mk_maddr(self.raddr) def connected(self) -> bool: return self.stream.socket.fileno() != -1 diff --git a/tractor/runtime/_runtime.py b/tractor/runtime/_runtime.py index 0ffc61122..72b24e9dd 100644 --- a/tractor/runtime/_runtime.py +++ b/tractor/runtime/_runtime.py @@ -1370,7 +1370,7 @@ async def cancel_rpc_tasks( # - `Channel.maddr() -> str:` obvi! # - `Context.maddr() -> str:` tasks_str += ( - f' |_@ /ipv4/tcp/cid="{ctx.cid[-16:]} .."\n' + f' |_@ /ip4/tcp/cid="{ctx.cid[-16:]} .."\n' f' |>> {ctx._nsf}() -> dict:\n' ) From d09ecde7e749fbd1d3b241ae9b140e08ebffed46 Mon Sep 17 00:00:00 2001 From: goodboy Date: Wed, 25 Mar 2026 19:38:21 -0400 Subject: [PATCH 05/11] Use `_tpt_proto_to_maddr` lookup in `mk_maddr()` Address Copilot review: the mapping table was defined but never referenced. Now `mk_maddr()` resolves `proto_key` -> maddr protocol name via the table and rejects unknown keys upfront. Also add missing `Path` import to the `multiaddr` usage snippet. Review: PR #429 (Copilot) https://github.com/goodboy/tractor/pull/429#pullrequestreview-4010456884 (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- snippets/multiaddr_ex.py | 2 ++ tractor/discovery/_multiaddr.py | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/snippets/multiaddr_ex.py b/snippets/multiaddr_ex.py index ef9a9137b..5e1bb2f82 100644 --- a/snippets/multiaddr_ex.py +++ b/snippets/multiaddr_ex.py @@ -1,4 +1,6 @@ +from pathlib import Path from multiaddr import Multiaddr + # construct from a string m1 = Multiaddr("/ip4/127.0.0.1/udp/1234") m2 = Multiaddr("/unix/run/user/1000/sway-ipc.1000.1557.sock") diff --git a/tractor/discovery/_multiaddr.py b/tractor/discovery/_multiaddr.py index 99dd620ef..4f62d73a8 100644 --- a/tractor/discovery/_multiaddr.py +++ b/tractor/discovery/_multiaddr.py @@ -50,7 +50,14 @@ def mk_maddr( multiaddr-spec-compliant protocol path. ''' - match addr.proto_key: + proto_key: str = addr.proto_key + maddr_proto: str|None = _tpt_proto_to_maddr.get(proto_key) + if maddr_proto is None: + raise ValueError( + f'Unsupported proto_key: {proto_key!r}' + ) + + match proto_key: case 'tcp': host, port = addr.unwrap() ip = ipaddress.ip_address(host) @@ -59,17 +66,12 @@ def mk_maddr( else 'ip6' ) return Multiaddr( - f'/{net_proto}/{host}/tcp/{port}' + f'/{net_proto}/{host}/{maddr_proto}/{port}' ) case 'uds': filedir, filename = addr.unwrap() filepath = Path(filedir) / filename return Multiaddr( - f'/unix/{filepath}' - ) - - case _: - raise ValueError( - f'Unsupported proto_key: {addr.proto_key!r}' + f'/{maddr_proto}/{filepath}' ) From 3ed5328b0db459c2bca0d164f29cfd786b27dbc1 Mon Sep 17 00:00:00 2001 From: goodboy Date: Wed, 25 Mar 2026 19:48:00 -0400 Subject: [PATCH 06/11] Add `test_multiaddr.py` suite for `mk_maddr()` Cover `_tpt_proto_to_maddr` mapping, TCP (ipv4/ipv6), UDS, unsupported `proto_key` error, and round-trip re-parse for both transport types. Deats, - new `tests/discovery/` subpkg w/ empty `__init__.py` - `test_tpt_proto_to_maddr_mapping`: verify `tcp` and `uds` entries - `test_mk_maddr_tcp_ipv4`: full assertion on `/ip4/127.0.0.1/tcp/1234` incl protocol iteration - `test_mk_maddr_tcp_ipv6`: verify `/ip6/::1/tcp/5678` - `test_mk_maddr_uds`: relative `filedir` bc the multiaddr parser rejects double-slash from abs paths - `test_mk_maddr_unsupported_proto_key`: `ValueError` on `proto_key='quic'` via `SimpleNamespace` mock - `test_mk_maddr_roundtrip`: parametrized over tcp + uds, re-parse `str(maddr)` back through `Multiaddr` (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- tests/discovery/__init__.py | 0 tests/discovery/test_multiaddr.py | 136 ++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 tests/discovery/__init__.py create mode 100644 tests/discovery/test_multiaddr.py diff --git a/tests/discovery/__init__.py b/tests/discovery/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/discovery/test_multiaddr.py b/tests/discovery/test_multiaddr.py new file mode 100644 index 000000000..81eec2ceb --- /dev/null +++ b/tests/discovery/test_multiaddr.py @@ -0,0 +1,136 @@ +''' +Multiaddr construction and round-trip tests for +`tractor.discovery._multiaddr.mk_maddr()`. + +''' +from pathlib import Path +from types import SimpleNamespace + +import pytest +from multiaddr import Multiaddr + +from tractor.ipc._tcp import TCPAddress +from tractor.ipc._uds import UDSAddress +from tractor.discovery._multiaddr import ( + mk_maddr, + _tpt_proto_to_maddr, +) + + +def test_tpt_proto_to_maddr_mapping(): + ''' + `_tpt_proto_to_maddr` maps all supported `proto_key` + values to their correct multiaddr protocol names. + + ''' + assert _tpt_proto_to_maddr['tcp'] == 'tcp' + assert _tpt_proto_to_maddr['uds'] == 'unix' + assert len(_tpt_proto_to_maddr) == 2 + + +def test_mk_maddr_tcp_ipv4(): + ''' + `mk_maddr()` on a `TCPAddress` with an IPv4 host + produces the correct `/ip4//tcp/` multiaddr. + + ''' + addr = TCPAddress('127.0.0.1', 1234) + result: Multiaddr = mk_maddr(addr) + + assert isinstance(result, Multiaddr) + assert str(result) == '/ip4/127.0.0.1/tcp/1234' + + protos = result.protocols() + assert protos[0].name == 'ip4' + assert protos[1].name == 'tcp' + + assert result.value_for_protocol('ip4') == '127.0.0.1' + assert result.value_for_protocol('tcp') == '1234' + + +def test_mk_maddr_tcp_ipv6(): + ''' + `mk_maddr()` on a `TCPAddress` with an IPv6 host + produces the correct `/ip6//tcp/` multiaddr. + + ''' + addr = TCPAddress('::1', 5678) + result: Multiaddr = mk_maddr(addr) + + assert str(result) == '/ip6/::1/tcp/5678' + + protos = result.protocols() + assert protos[0].name == 'ip6' + assert protos[1].name == 'tcp' + + +def test_mk_maddr_uds(): + ''' + `mk_maddr()` on a `UDSAddress` produces a `/unix/` + multiaddr containing the full socket path. + + ''' + # NOTE, use a relative `filedir` since the multiaddr + # parser rejects the double-slash from absolute paths + # (i.e. `/unix//tmp/..` -> "empty protocol path"). + filedir = 'tractor_test' + filename = 'test_sock.sock' + addr = UDSAddress( + filedir=filedir, + filename=filename, + ) + result: Multiaddr = mk_maddr(addr) + + assert isinstance(result, Multiaddr) + + result_str: str = str(result) + assert result_str.startswith('/unix/') + + sockpath: str = str(Path(filedir) / filename) + # NOTE, the multiaddr lib prepends a `/` to the + # unix protocol value when parsing back out. + unix_val: str = result.value_for_protocol('unix') + assert unix_val.endswith(sockpath) + + +def test_mk_maddr_unsupported_proto_key(): + ''' + `mk_maddr()` raises `ValueError` for an unsupported + `proto_key`. + + ''' + fake_addr = SimpleNamespace(proto_key='quic') + with pytest.raises( + ValueError, + match='Unsupported proto_key', + ): + mk_maddr(fake_addr) + + +@pytest.mark.parametrize( + 'addr', + [ + pytest.param( + TCPAddress('127.0.0.1', 9999), + id='tcp-ipv4', + ), + pytest.param( + UDSAddress( + filedir='tractor_rt', + filename='roundtrip.sock', + ), + id='uds', + ), + ], +) +def test_mk_maddr_roundtrip(addr): + ''' + `mk_maddr()` output is valid multiaddr syntax that the + library can re-parse back into an equivalent `Multiaddr`. + + ''' + maddr: Multiaddr = mk_maddr(addr) + reparsed = Multiaddr(str(maddr)) + + assert reparsed == maddr + assert str(reparsed) == str(maddr) From 4d435eb4f8aab701213b885f4aae54cac37f8dea Mon Sep 17 00:00:00 2001 From: goodboy Date: Wed, 25 Mar 2026 19:54:41 -0400 Subject: [PATCH 07/11] Move `test_discovery` to `tests/discovery/test_registrar` All tests are registrar-actor integration scenarios sharing intertwined helpers + `enable_modules=[__name__]` task fns, so keep as one mod but rename to reflect content. Now lives alongside `test_multiaddr.py` in the new `tests/discovery/` subpkg. Also, - update 5 refs in `/run-tests` SKILL.md to match the new path - add `discovery/` subdir to the test directory layout tree (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- .claude/skills/run-tests/SKILL.md | 12 +++++++----- .../test_registrar.py} | 0 2 files changed, 7 insertions(+), 5 deletions(-) rename tests/{test_discovery.py => discovery/test_registrar.py} (100%) diff --git a/.claude/skills/run-tests/SKILL.md b/.claude/skills/run-tests/SKILL.md index a06126f74..597e9ebb6 100644 --- a/.claude/skills/run-tests/SKILL.md +++ b/.claude/skills/run-tests/SKILL.md @@ -34,7 +34,7 @@ treat it as the test target. Examples: - `/run-tests` → full suite - `/run-tests test_local.py` → single file -- `/run-tests test_discovery -v` → file + verbose +- `/run-tests test_registrar -v` → file + verbose - `/run-tests -k cancel` → keyword filter - `/run-tests tests/ipc/ --tpt-proto uds` → subdir + UDS @@ -79,7 +79,7 @@ python -m pytest tests/test_local.py tests/test_rpc.py -x --tb=short --no-header python -m pytest tests/ -x --tb=short --no-header # specific test with debug -python -m pytest tests/test_discovery.py::test_reg_then_unreg -x -s --tpdb --ll debug +python -m pytest tests/discovery/test_registrar.py::test_reg_then_unreg -x -s --tpdb --ll debug # run with UDS transport python -m pytest tests/ -x --tb=short --no-header --tpt-proto uds @@ -133,8 +133,10 @@ tests/ ├── devx/ # debugger/tooling tests ├── ipc/ # transport protocol tests ├── msg/ # messaging layer tests +├── discovery/ # discovery subsystem tests +│ ├── test_multiaddr.py # multiaddr construction +│ └── test_registrar.py # registry/discovery protocol ├── test_local.py # registrar + local actor basics -├── test_discovery.py # registry/discovery protocol ├── test_rpc.py # RPC error handling ├── test_spawning.py # subprocess spawning ├── test_multi_program.py # multi-process tree tests @@ -153,7 +155,7 @@ test subset first for fast feedback: | Changed module(s) | Run these tests first | |---|---| | `runtime/_runtime.py`, `runtime/_state.py` | `test_local.py test_rpc.py test_spawning.py test_root_runtime.py` | -| `discovery/` (`_registry`, `_discovery`, `_addr`) | `test_discovery.py test_multi_program.py test_local.py` | +| `discovery/` (`_registry`, `_discovery`, `_addr`) | `tests/discovery/ test_multi_program.py test_local.py` | | `_context.py`, `_streaming.py` | `test_context_stream_semantics.py test_advanced_streaming.py` | | `ipc/` (`_chan`, `_server`, `_transport`) | `tests/ipc/ test_2way.py` | | `runtime/_portal.py`, `runtime/_rpc.py` | `test_rpc.py test_cancellation.py` | @@ -172,7 +174,7 @@ test subset first for fast feedback: python -c 'import tractor' && python -m pytest tests/ -x -q --co 2>&1 | tail -3 # core subset (~10s) -python -m pytest tests/test_local.py tests/test_rpc.py tests/test_spawning.py tests/test_discovery.py -x --tb=short --no-header +python -m pytest tests/test_local.py tests/test_rpc.py tests/test_spawning.py tests/discovery/test_registrar.py -x --tb=short --no-header ``` ### Re-run last failures only: diff --git a/tests/test_discovery.py b/tests/discovery/test_registrar.py similarity index 100% rename from tests/test_discovery.py rename to tests/discovery/test_registrar.py From abff33ce11fe7a88575fd39b8c733c11f836ed78 Mon Sep 17 00:00:00 2001 From: goodboy Date: Thu, 26 Mar 2026 16:55:10 -0400 Subject: [PATCH 08/11] Add `parse_maddr()` + `str` arm in `wrap_address()` Inverse of `mk_maddr()`: parse a multiaddr string like `/ip4/127.0.0.1/tcp/1234` back into a tractor `Address`. Deats, - add `_maddr_to_tpt_proto` reverse mapping dict - add `parse_maddr()` fn dispatching on protocol combo: `[ip4|ip6, tcp]` -> `TCPAddress`, `[unix]` -> `UDSAddress` - strip leading `/` the multiaddr lib prepends to unix protocol values for correct round-trip - add `str` match case in `wrap_address()` for `/`-prefixed multiaddr strings, broaden type hint to `UnwrappedAddress|str` (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- tractor/discovery/_addr.py | 10 ++++++- tractor/discovery/_multiaddr.py | 49 +++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/tractor/discovery/_addr.py b/tractor/discovery/_addr.py index cb95f792e..2697c5c90 100644 --- a/tractor/discovery/_addr.py +++ b/tractor/discovery/_addr.py @@ -206,7 +206,7 @@ def mk_uuid() -> str: def wrap_address( - addr: UnwrappedAddress + addr: UnwrappedAddress|str, ) -> Address: ''' Wrap an `UnwrappedAddress` as an `Address`-type based @@ -257,6 +257,14 @@ def wrap_address( cls: Type[Address] = get_address_cls(_def_tpt_proto) addr: UnwrappedAddress = cls.get_root().unwrap() + # multiaddr-format string, e.g. + # '/ip4/127.0.0.1/tcp/1616' + case str() if addr.startswith('/'): + from tractor.discovery._multiaddr import ( + parse_maddr, + ) + return parse_maddr(addr) + case _: # import pdbp; pdbp.set_trace() # from tractor.devx import mk_pdb diff --git a/tractor/discovery/_multiaddr.py b/tractor/discovery/_multiaddr.py index 4f62d73a8..2a0ee7e60 100644 --- a/tractor/discovery/_multiaddr.py +++ b/tractor/discovery/_multiaddr.py @@ -40,6 +40,12 @@ 'uds': 'unix', } +# reverse mapping: multiaddr protocol name -> tractor proto_key +_maddr_to_tpt_proto: dict[str, str] = { + v: k for k, v in _tpt_proto_to_maddr.items() +} +# {'tcp': 'tcp', 'unix': 'uds'} + def mk_maddr( addr: 'Address', @@ -75,3 +81,46 @@ def mk_maddr( return Multiaddr( f'/{maddr_proto}/{filepath}' ) + + +def parse_maddr( + maddr_str: str, +) -> 'Address': + ''' + Parse a multiaddr string into a tractor `Address`. + + Inverse of `mk_maddr()`. + + ''' + # lazy imports to avoid circular deps + from tractor.ipc._tcp import TCPAddress + from tractor.ipc._uds import UDSAddress + + maddr = Multiaddr(maddr_str) + proto_names: list[str] = [ + p.name for p in maddr.protocols() + ] + + match proto_names: + case [('ip4' | 'ip6') as net_proto, 'tcp']: + host: str = maddr.value_for_protocol(net_proto) + port: int = int(maddr.value_for_protocol('tcp')) + return TCPAddress(host, port) + + case ['unix']: + # NOTE, the multiaddr lib prepends a `/` to the + # unix protocol value; strip it to recover the + # original relative path. + raw: str = maddr.value_for_protocol('unix') + sockpath = Path(raw.lstrip('/')) + return UDSAddress( + filedir=str(sockpath.parent), + filename=str(sockpath.name), + ) + + case _: + raise ValueError( + f'Unsupported multiaddr protocol combo: ' + f'{proto_names!r}\n' + f'from maddr: {maddr_str!r}\n' + ) From 59f5a9ed2e6b2934b1a7554f00d38588ed7a6705 Mon Sep 17 00:00:00 2001 From: goodboy Date: Thu, 26 Mar 2026 19:10:20 -0400 Subject: [PATCH 09/11] Add `parse_maddr()` tests + registrar maddr integ test Cover `parse_maddr()` with unit tests for tcp/ipv4, tcp/ipv6, uds, and unsupported-protocol error paths, plus full `addr -> mk_maddr -> str -> parse_maddr` roundtrip verification. Adds, - a `_maddr_to_tpt_proto` inverse-mapping assertion. - an `wrap_address()` maddr-string acceptance test. - a `test_reg_then_unreg_maddr` end-to-end suite which audits passing the registry addr as multiaddr str through the entire runtime. (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- tests/discovery/test_multiaddr.py | 115 +++++++++++++++++++++++++++++- tests/discovery/test_registrar.py | 45 ++++++++++++ 2 files changed, 158 insertions(+), 2 deletions(-) diff --git a/tests/discovery/test_multiaddr.py b/tests/discovery/test_multiaddr.py index 81eec2ceb..8329b0338 100644 --- a/tests/discovery/test_multiaddr.py +++ b/tests/discovery/test_multiaddr.py @@ -1,6 +1,7 @@ ''' -Multiaddr construction and round-trip tests for -`tractor.discovery._multiaddr.mk_maddr()`. +Multiaddr construction, parsing, and round-trip tests for +`tractor.discovery._multiaddr.mk_maddr()` and +`tractor.discovery._multiaddr.parse_maddr()`. ''' from pathlib import Path @@ -13,8 +14,11 @@ from tractor.ipc._uds import UDSAddress from tractor.discovery._multiaddr import ( mk_maddr, + parse_maddr, _tpt_proto_to_maddr, + _maddr_to_tpt_proto, ) +from tractor.discovery._addr import wrap_address def test_tpt_proto_to_maddr_mapping(): @@ -134,3 +138,110 @@ def test_mk_maddr_roundtrip(addr): assert reparsed == maddr assert str(reparsed) == str(maddr) + + +# ------ parse_maddr() tests ------ + +def test_maddr_to_tpt_proto_mapping(): + ''' + `_maddr_to_tpt_proto` is the exact inverse of + `_tpt_proto_to_maddr`. + + ''' + assert _maddr_to_tpt_proto == { + 'tcp': 'tcp', + 'unix': 'uds', + } + + +def test_parse_maddr_tcp_ipv4(): + ''' + `parse_maddr()` on an IPv4 TCP multiaddr string + produce a `TCPAddress` with the correct host and port. + + ''' + result = parse_maddr('/ip4/127.0.0.1/tcp/1234') + + assert isinstance(result, TCPAddress) + assert result.unwrap() == ('127.0.0.1', 1234) + + +def test_parse_maddr_tcp_ipv6(): + ''' + `parse_maddr()` on an IPv6 TCP multiaddr string + produce a `TCPAddress` with the correct host and port. + + ''' + result = parse_maddr('/ip6/::1/tcp/5678') + + assert isinstance(result, TCPAddress) + assert result.unwrap() == ('::1', 5678) + + +def test_parse_maddr_uds(): + ''' + `parse_maddr()` on a `/unix/...` multiaddr string + produce a `UDSAddress` with the correct dir and filename. + + ''' + result = parse_maddr('/unix/tractor_test/test.sock') + + assert isinstance(result, UDSAddress) + filedir, filename = result.unwrap() + assert filename == 'test.sock' + assert 'tractor_test' in str(filedir) + + +def test_parse_maddr_unsupported(): + ''' + `parse_maddr()` raise `ValueError` for an unsupported + protocol combination like UDP. + + ''' + with pytest.raises( + ValueError, + match='Unsupported multiaddr protocol combo', + ): + parse_maddr('/ip4/127.0.0.1/udp/1234') + + +@pytest.mark.parametrize( + 'addr', + [ + pytest.param( + TCPAddress('127.0.0.1', 9999), + id='tcp-ipv4', + ), + pytest.param( + UDSAddress( + filedir='tractor_rt', + filename='roundtrip.sock', + ), + id='uds', + ), + ], +) +def test_parse_maddr_roundtrip(addr): + ''' + Full round-trip: `addr -> mk_maddr -> str -> parse_maddr` + produce an `Address` whose `.unwrap()` matches the original. + + ''' + maddr: Multiaddr = mk_maddr(addr) + maddr_str: str = str(maddr) + parsed = parse_maddr(maddr_str) + + assert type(parsed) is type(addr) + assert parsed.unwrap() == addr.unwrap() + + +def test_wrap_address_maddr_str(): + ''' + `wrap_address()` accept a multiaddr-format string and + return the correct `Address` type. + + ''' + result = wrap_address('/ip4/127.0.0.1/tcp/9999') + + assert isinstance(result, TCPAddress) + assert result.unwrap() == ('127.0.0.1', 9999) diff --git a/tests/discovery/test_registrar.py b/tests/discovery/test_registrar.py index 0fbac8be4..18f94c43f 100644 --- a/tests/discovery/test_registrar.py +++ b/tests/discovery/test_registrar.py @@ -16,6 +16,8 @@ import tractor from tractor.trionics import collapse_eg from tractor._testing import tractor_test +from tractor.discovery._addr import wrap_address +from tractor.discovery._multiaddr import mk_maddr import trio @@ -53,6 +55,49 @@ async def test_reg_then_unreg( assert not sockaddrs +@tractor_test +async def test_reg_then_unreg_maddr( + reg_addr: tuple, +): + ''' + Same as `test_reg_then_unreg` but pass the registry + address as a multiaddr string to verify `wrap_address()` + multiaddr parsing end-to-end through the runtime. + + ''' + # tuple -> Address -> multiaddr string + addr_obj = wrap_address(reg_addr) + maddr_str: str = str(mk_maddr(addr_obj)) + + actor = tractor.current_actor() + assert actor.is_registrar + + async with tractor.open_nursery( + registry_addrs=[maddr_str], + ) as n: + + portal = await n.start_actor( + 'actor_maddr', + enable_modules=[__name__], + ) + uid = portal.channel.aid.uid + + async with tractor.get_registry(maddr_str) as aportal: + assert actor is aportal.actor + + async with tractor.wait_for_actor('actor_maddr'): + assert uid in aportal.actor._registry + sockaddrs = actor._registry[uid] + assert sockaddrs + + await n.cancel() + + await trio.sleep(0.1) + assert uid not in aportal.actor._registry + sockaddrs = actor._registry.get(uid) + assert not sockaddrs + + the_line = 'Hi my name is {}' From 3a7154b5909edac076f381d2fb20f973a974d2a0 Mon Sep 17 00:00:00 2001 From: goodboy Date: Thu, 26 Mar 2026 21:56:33 -0400 Subject: [PATCH 10/11] Fix `mk_maddr()` crash on absolute UDS paths Strip leading `/` from `filepath` before building the `/unix/{path}` multiaddr string; OW absolute sockpaths like `/run/user/1000/tractor/foo.sock` produce `/unix//run/..` which `py-multiaddr` rejects as "empty protocol path". Woops, missed this in the initial `mk_maddr()` impl bc the unit tests only used relative `filedir` values (which was even noted in a comment..). The bug only surfaces when the `.maddr` property on `UDSTransport` is hit during logging/repr with real runtime addrs. Found-via: cross-suite `pytest tests/ipc/ tests/msg/` where `tpt_proto='uds'` leaks into msg tests (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- tractor/discovery/_multiaddr.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tractor/discovery/_multiaddr.py b/tractor/discovery/_multiaddr.py index 2a0ee7e60..d9046e035 100644 --- a/tractor/discovery/_multiaddr.py +++ b/tractor/discovery/_multiaddr.py @@ -78,8 +78,13 @@ def mk_maddr( case 'uds': filedir, filename = addr.unwrap() filepath = Path(filedir) / filename + # NOTE, strip any leading `/` to avoid + # double-slash `/unix//run/..` which the + # multiaddr parser rejects as "empty + # protocol path". + fpath_str: str = str(filepath).lstrip('/') return Multiaddr( - f'/{maddr_proto}/{filepath}' + f'/{maddr_proto}/{fpath_str}' ) From 1e0bca227a6dc58e7044aa4d75c5637d2e86d019 Mon Sep 17 00:00:00 2001 From: goodboy Date: Fri, 27 Mar 2026 11:21:20 -0400 Subject: [PATCH 11/11] Preserve absolute UDS paths in `parse_maddr()` Drop the `.lstrip('/')` on the unix protocol value so the lib-prepended `/` restores the absolute-path semantics that `mk_maddr()` strips when encoding. Pass `Path` components (not `str`) to `UDSAddress`. Also, update all UDS test params to use absolute paths (`/tmp/tractor_test/...`, `/tmp/tractor_rt/...`) matching real runtime sockpath behavior; tighten `test_parse_maddr_uds` to assert exact `filedir`. Review: PR #429 (copilot-pull-request-reviewer[bot]) https://github.com/goodboy/tractor/pull/429#pullrequestreview-4018448152 (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- tests/discovery/test_multiaddr.py | 30 +++++++++++++++++------------- tractor/discovery/_multiaddr.py | 11 ++++++----- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/tests/discovery/test_multiaddr.py b/tests/discovery/test_multiaddr.py index 8329b0338..83da84ea4 100644 --- a/tests/discovery/test_multiaddr.py +++ b/tests/discovery/test_multiaddr.py @@ -74,10 +74,11 @@ def test_mk_maddr_uds(): multiaddr containing the full socket path. ''' - # NOTE, use a relative `filedir` since the multiaddr - # parser rejects the double-slash from absolute paths - # (i.e. `/unix//tmp/..` -> "empty protocol path"). - filedir = 'tractor_test' + # NOTE, use an absolute `filedir` to match real runtime + # UDS paths; `mk_maddr()` strips the leading `/` to avoid + # the double-slash `/unix//run/..` that py-multiaddr + # rejects as "empty protocol path". + filedir = '/tmp/tractor_test' filename = 'test_sock.sock' addr = UDSAddress( filedir=filedir, @@ -89,12 +90,14 @@ def test_mk_maddr_uds(): result_str: str = str(result) assert result_str.startswith('/unix/') + # verify the leading `/` was stripped to avoid double-slash + assert '/unix/tmp/tractor_test/' in result_str - sockpath: str = str(Path(filedir) / filename) - # NOTE, the multiaddr lib prepends a `/` to the - # unix protocol value when parsing back out. + sockpath_rel: str = str( + Path(filedir) / filename + ).lstrip('/') unix_val: str = result.value_for_protocol('unix') - assert unix_val.endswith(sockpath) + assert unix_val.endswith(sockpath_rel) def test_mk_maddr_unsupported_proto_key(): @@ -120,7 +123,7 @@ def test_mk_maddr_unsupported_proto_key(): ), pytest.param( UDSAddress( - filedir='tractor_rt', + filedir='/tmp/tractor_rt', filename='roundtrip.sock', ), id='uds', @@ -181,15 +184,16 @@ def test_parse_maddr_tcp_ipv6(): def test_parse_maddr_uds(): ''' `parse_maddr()` on a `/unix/...` multiaddr string - produce a `UDSAddress` with the correct dir and filename. + produce a `UDSAddress` with the correct dir and filename, + preserving absolute path semantics. ''' - result = parse_maddr('/unix/tractor_test/test.sock') + result = parse_maddr('/unix/tmp/tractor_test/test.sock') assert isinstance(result, UDSAddress) filedir, filename = result.unwrap() assert filename == 'test.sock' - assert 'tractor_test' in str(filedir) + assert str(filedir) == '/tmp/tractor_test' def test_parse_maddr_unsupported(): @@ -214,7 +218,7 @@ def test_parse_maddr_unsupported(): ), pytest.param( UDSAddress( - filedir='tractor_rt', + filedir='/tmp/tractor_rt', filename='roundtrip.sock', ), id='uds', diff --git a/tractor/discovery/_multiaddr.py b/tractor/discovery/_multiaddr.py index d9046e035..e310cf1c6 100644 --- a/tractor/discovery/_multiaddr.py +++ b/tractor/discovery/_multiaddr.py @@ -114,13 +114,14 @@ def parse_maddr( case ['unix']: # NOTE, the multiaddr lib prepends a `/` to the - # unix protocol value; strip it to recover the - # original relative path. + # unix protocol value which effectively restores + # the absolute-path semantics that `mk_maddr()` + # strips when building the multiaddr string. raw: str = maddr.value_for_protocol('unix') - sockpath = Path(raw.lstrip('/')) + sockpath = Path(raw) return UDSAddress( - filedir=str(sockpath.parent), - filename=str(sockpath.name), + filedir=sockpath.parent, + filename=sockpath.name, ) case _: