ALPHA: wire is a tool to deploy nixos systems wire.althaea.zone/
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

extract python test scripts into their own files (#379)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

authored by

marshmallow
autofix-ci[bot]
and committed by
GitHub
ef99c385 5c9ab149

+581 -128
+3
.gitignore
··· 10 10 run.json 11 11 stats.md 12 12 .nixos-test-history 13 + .venv/ 14 + .ruff_cache/ 15 + __pycache__/
+1
flake.nix
··· 89 89 prettier.enable = true; 90 90 protolint.enable = true; 91 91 taplo.enable = true; 92 + ruff-format.enable = true; 92 93 }; 93 94 settings.formatter = { 94 95 nixfmt.excludes = [ "doc/snippets/*.nix" ];
+13
nix/hooks.nix
··· 4 4 toolchain, 5 5 config, 6 6 lib, 7 + pkgs, 7 8 ... 8 9 }: 9 10 { ··· 23 24 inherit (toolchain) cargo clippy; 24 25 }; 25 26 }; 27 + ruff.enable = true; 26 28 cargo-check = { 27 29 enable = true; 28 30 package = toolchain.cargo; ··· 31 33 enable = true; 32 34 name = "nix fmt"; 33 35 entry = "${lib.getExe config.formatter} --no-cache"; 36 + }; 37 + ty = { 38 + enable = true; 39 + name = "ty check"; 40 + files = "\\.py$"; 41 + entry = lib.getExe ( 42 + pkgs.writeShellScriptBin "ty-check" '' 43 + cd tests/nix 44 + ${lib.getExe pkgs.uv} run ty check 45 + '' 46 + ); 34 47 }; 35 48 typos = { 36 49 enable = true;
+1
nix/shells.nix
··· 24 24 pkgs.sqlite 25 25 pkgs.turso 26 26 pkgs.zstd 27 + pkgs.uv 27 28 ]; 28 29 29 30 PROTOC = lib.getExe pkgs.protobuf;
+1
tests/nix/.python-version
··· 1 + 3.13
+9 -1
tests/nix/default.nix
··· 23 23 lazyAttrsOf 24 24 ; 25 25 cfg = config.wire.testing; 26 + 27 + stripTyping = 28 + value: 29 + let 30 + split = builtins.split "(from typing import TYPE_CHECKING|# typing-end)" value; 31 + in 32 + (builtins.elemAt split 0) + (builtins.elemAt split 4); 26 33 in 27 34 { 28 35 imports = [ ··· 44 51 type = lines; 45 52 default = ''''; 46 53 description = "test script for runNixOSTest"; 54 + apply = stripTyping; 47 55 }; 48 56 testDir = mkOption { 49 57 default = "${self}/tests/nix/suite/${name}"; ··· 163 171 164 172 TEST_DIR="${injectedFlakeDir}/${path}" 165 173 166 - ${builtins.readFile ./tools.py} 174 + ${stripTyping (builtins.readFile ./tools/__init__.py)} 167 175 '' 168 176 + lib.concatStringsSep "\n" (mapAttrsToList (_: value: value._wire.testScript) value.nodes) 169 177 + opts.testScript;
+18
tests/nix/pyproject.toml
··· 1 + [project] 2 + name = "wire-vm-tests" 3 + version = "0.0.0" 4 + requires-python = ">=3.13" 5 + dependencies = [ 6 + "colorama>=0.4.6", 7 + "ipython>=9.8.0", 8 + "junit-xml>=1.9", 9 + "nixos-test-driver", 10 + "ptpython>=3.0.32", 11 + "remote-pdb>=2.1.0", 12 + ] 13 + 14 + [tool.uv.sources] 15 + nixos-test-driver = { git = "https://github.com/NixOS/nixpkgs", subdirectory = "nixos/lib/test-driver/src", branch = "nixos-25.11" } 16 + 17 + [dependency-groups] 18 + dev = ["ty>=0.0.4"]
+1 -82
tests/nix/suite/test_keys/default.nix
··· 10 10 nodes.receiver = { 11 11 _wire.receiver = true; 12 12 }; 13 - testScript = '' 14 - deployer_so = collect_store_objects(deployer) 15 - receiver_so = collect_store_objects(receiver) 16 - 17 - # build receiver with no keys 18 - deployer.succeed(f"wire apply --no-progress --on receiver --path {TEST_DIR}/hive.nix --no-keys --ssh-accept-host -vvv >&2") 19 - 20 - receiver.wait_for_unit("sshd.service") 21 - 22 - # --no-keys should never push a key 23 - receiver.fail("test -f /run/keys/source_string_name") 24 - deployer.fail("test -f /run/keys/source_string_name") 25 - 26 - # key services are created 27 - receiver.succeed("systemctl cat source_string_name-key.service") 28 - 29 - _, is_failed = receiver.execute("systemctl is-failed source_string_name-key.service") 30 - assert is_failed == "inactive\n", f"source_string_name-key.service must be inactive before key exists ({is_failed})" 31 - 32 - def test_keys(target, target_object, non_interactive): 33 - if non_interactive: 34 - deployer.succeed(f"wire apply keys --on {target} --no-progress --path {TEST_DIR}/hive.nix --non-interactive --ssh-accept-host -vvv >&2") 35 - else: 36 - deployer.succeed(f"wire apply keys --on {target} --no-progress --path {TEST_DIR}/hive.nix --ssh-accept-host -vvv >&2") 37 - 38 - keys = [ 39 - ("/run/keys/source_string_name", "hello_world_source", "root root 600", "source_string_name"), 40 - ("/etc/keys/file", "hello_world_file", "root root 644", "file"), 41 - ("/home/owner/some/deep/path/command", "hello_world_command", "owner owner 644", "command"), 42 - ("/run/keys/environment", "string_from_environment", "root root 600", "environment"), 43 - ] 44 - 45 - for path, value, permissions, name in keys: 46 - # test existence & value 47 - source_string = target_object.succeed(f"cat {path}") 48 - assert value in source_string, f"{path} has correct contents ({target})" 49 - 50 - stat = target_object.succeed(f"stat -c '%U %G %a' {path}").rstrip() 51 - assert permissions == stat, f"{path} has correct permissions ({target})" 52 - 53 - def perform_routine(target, target_object, non_interactive): 54 - test_keys(target, target_object, non_interactive) 55 - 56 - # only check systemd units on receiver since deployer apply's are one time only 57 - if target == "receiver": 58 - target_object.succeed("systemctl start source_string_name-key.path") 59 - target_object.succeed("systemctl start command-key.path") 60 - target_object.wait_for_unit("source_string_name-key.service") 61 - target_object.wait_for_unit("command-key.service") 62 - 63 - # Mess with the keys to make sure that every push refreshes the permissions 64 - target_object.succeed("echo 'incorrect_value' > /run/keys/source_string") 65 - target_object.succeed("chown 600 /etc/keys/file") 66 - # Test having a key that doesn't exist mixed with keys that do 67 - target_object.succeed("rm /home/owner/some/deep/path/command") 68 - 69 - if target == "receiver": 70 - _, is_failed = target_object.execute("systemctl is-active command-key.service") 71 - assert is_failed == "failed\n", f"command-key.service is failed after deletion ({is_failed})" 72 - 73 - # Test keys twice to ensure the operation is idempotent, 74 - # especially around directory creation. 75 - test_keys(target, target_object, non_interactive) 76 - 77 - perform_routine("receiver", receiver, True) 78 - perform_routine("deployer", deployer, True) 79 - perform_routine("receiver", receiver, False) 80 - perform_routine("deployer", deployer, False) 81 - 82 - new_deployer_store_objects = collect_store_objects(deployer).difference(deployer_so) 83 - new_receiver_store_objects = collect_store_objects(receiver).difference(receiver_so) 84 - 85 - # no one should have any keys introduced by the operation 86 - for node, objects in [ 87 - (deployer, new_deployer_store_objects), 88 - (receiver, new_receiver_store_objects), 89 - ]: 90 - assert_store_not_poisoned(node, "hello_world_source", objects) 91 - assert_store_not_poisoned(node, "hello_world_file", objects) 92 - assert_store_not_poisoned(node, "hello_world_command", objects) 93 - assert_store_not_poisoned(node, "string_from_environment", objects) 94 - ''; 13 + testScript = builtins.readFile ./script.py; 95 14 }; 96 15 }
+123
tests/nix/suite/test_keys/script.py
··· 1 + # SPDX-License-Identifier: AGPL-3.0-or-later 2 + # Copyright 2024-2025 wire Contributors 3 + 4 + from typing import TYPE_CHECKING 5 + 6 + if TYPE_CHECKING: 7 + from test_driver.machine import Machine 8 + from tools import collect_store_objects, assert_store_not_poisoned 9 + 10 + deployer: Machine = None # type: ignore[invalid-assignment] 11 + receiver: Machine = None # type: ignore[invalid-assignment] 12 + TEST_DIR = "" 13 + 14 + # typing-end 15 + 16 + deployer_so = collect_store_objects(deployer) 17 + receiver_so = collect_store_objects(receiver) 18 + 19 + # build receiver with no keys 20 + deployer.succeed( 21 + f"wire apply --no-progress --on receiver --path {TEST_DIR}/hive.nix --no-keys --ssh-accept-host -vvv >&2" 22 + ) 23 + 24 + receiver.wait_for_unit("sshd.service") 25 + 26 + # --no-keys should never push a key 27 + receiver.fail("test -f /run/keys/source_string_name") 28 + deployer.fail("test -f /run/keys/source_string_name") 29 + 30 + # key services are created 31 + receiver.succeed("systemctl cat source_string_name-key.service") 32 + 33 + _, is_failed = receiver.execute("systemctl is-failed source_string_name-key.service") 34 + assert is_failed == "inactive\n", ( 35 + f"source_string_name-key.service must be inactive before key exists ({is_failed})" 36 + ) 37 + 38 + 39 + def test_keys(target, target_object, non_interactive): 40 + if non_interactive: 41 + deployer.succeed( 42 + f"wire apply keys --on {target} --no-progress --path {TEST_DIR}/hive.nix --non-interactive --ssh-accept-host -vvv >&2" 43 + ) 44 + else: 45 + deployer.succeed( 46 + f"wire apply keys --on {target} --no-progress --path {TEST_DIR}/hive.nix --ssh-accept-host -vvv >&2" 47 + ) 48 + 49 + keys = [ 50 + ( 51 + "/run/keys/source_string_name", 52 + "hello_world_source", 53 + "root root 600", 54 + "source_string_name", 55 + ), 56 + ("/etc/keys/file", "hello_world_file", "root root 644", "file"), 57 + ( 58 + "/home/owner/some/deep/path/command", 59 + "hello_world_command", 60 + "owner owner 644", 61 + "command", 62 + ), 63 + ( 64 + "/run/keys/environment", 65 + "string_from_environment", 66 + "root root 600", 67 + "environment", 68 + ), 69 + ] 70 + 71 + for path, value, permissions, name in keys: 72 + # test existence & value 73 + source_string = target_object.succeed(f"cat {path}") 74 + assert value in source_string, f"{path} has correct contents ({target})" 75 + 76 + stat = target_object.succeed(f"stat -c '%U %G %a' {path}").rstrip() 77 + assert permissions == stat, f"{path} has correct permissions ({target})" 78 + 79 + 80 + def perform_routine(target, target_object, non_interactive): 81 + test_keys(target, target_object, non_interactive) 82 + 83 + # only check systemd units on receiver since deployer apply's are one time only 84 + if target == "receiver": 85 + target_object.succeed("systemctl start source_string_name-key.path") 86 + target_object.succeed("systemctl start command-key.path") 87 + target_object.wait_for_unit("source_string_name-key.service") 88 + target_object.wait_for_unit("command-key.service") 89 + 90 + # Mess with the keys to make sure that every push refreshes the permissions 91 + target_object.succeed("echo 'incorrect_value' > /run/keys/source_string") 92 + target_object.succeed("chown 600 /etc/keys/file") 93 + # Test having a key that doesn't exist mixed with keys that do 94 + target_object.succeed("rm /home/owner/some/deep/path/command") 95 + 96 + if target == "receiver": 97 + _, is_failed = target_object.execute("systemctl is-active command-key.service") 98 + assert is_failed == "failed\n", ( 99 + f"command-key.service is failed after deletion ({is_failed})" 100 + ) 101 + 102 + # Test keys twice to ensure the operation is idempotent, 103 + # especially around directory creation. 104 + test_keys(target, target_object, non_interactive) 105 + 106 + 107 + perform_routine("receiver", receiver, True) 108 + perform_routine("deployer", deployer, True) 109 + perform_routine("receiver", receiver, False) 110 + perform_routine("deployer", deployer, False) 111 + 112 + new_deployer_store_objects = collect_store_objects(deployer).difference(deployer_so) 113 + new_receiver_store_objects = collect_store_objects(receiver).difference(receiver_so) 114 + 115 + # no one should have any keys introduced by the operation 116 + for node, objects in [ 117 + (deployer, new_deployer_store_objects), 118 + (receiver, new_receiver_store_objects), 119 + ]: 120 + assert_store_not_poisoned(node, "hello_world_source", objects) 121 + assert_store_not_poisoned(node, "hello_world_file", objects) 122 + assert_store_not_poisoned(node, "hello_world_command", objects) 123 + assert_store_not_poisoned(node, "string_from_environment", objects)
+1 -4
tests/nix/suite/test_local_deploy/default.nix
··· 7 7 _wire.deployer = true; 8 8 _wire.receiver = true; 9 9 }; 10 - testScript = '' 11 - deployer.succeed(f"wire apply --on deployer --no-progress --path {TEST_DIR}/hive.nix --no-keys -vvv >&2") 12 - deployer.succeed("test -f /etc/a") 13 - ''; 10 + testScript = builtins.readFile ./script.py; 14 11 }; 15 12 }
+17
tests/nix/suite/test_local_deploy/script.py
··· 1 + # SPDX-License-Identifier: AGPL-3.0-or-later 2 + # Copyright 2024-2025 wire Contributors 3 + 4 + from typing import TYPE_CHECKING 5 + 6 + if TYPE_CHECKING: 7 + from test_driver.machine import Machine 8 + 9 + deployer: Machine = None # type: ignore[invalid-assignment] 10 + TEST_DIR = "" 11 + 12 + # typing-end 13 + 14 + deployer.succeed( 15 + f"wire apply --on deployer --no-progress --path {TEST_DIR}/hive.nix --no-keys -vvv >&2" 16 + ) 17 + deployer.succeed("test -f /etc/a")
+1 -36
tests/nix/suite/test_remote_deploy/default.nix
··· 9 9 nodes.receiver = { 10 10 _wire.receiver = true; 11 11 }; 12 - testScript = '' 13 - with subtest("Test unreachable hosts"): 14 - deployer.fail(f"wire apply --on receiver-unreachable --no-progress --path {TEST_DIR}/hive.nix --no-keys -vvv >&2") 15 - 16 - with subtest("Check basic apply: Interactive"): 17 - deployer.succeed(f"wire apply --on receiver --no-progress --path {TEST_DIR}/hive.nix --no-keys --ssh-accept-host -vvv >&2") 18 - 19 - identity = receiver.succeed("cat /etc/identity") 20 - assert identity == "first", "Identity of first apply wasn't as expected" 21 - 22 - with subtest("Check basic apply: NonInteractive"): 23 - deployer.succeed(f"wire apply --on receiver-third --no-progress --path {TEST_DIR}/hive.nix --no-keys --ssh-accept-host --non-interactive -vvv >&2") 24 - 25 - identity = receiver.succeed("cat /etc/identity") 26 - assert identity == "third", "Identity of non-interactive apply wasn't as expected" 27 - 28 - with subtest("Check boot apply"): 29 - first_system = receiver.succeed("readlink -f /run/current-system") 30 - 31 - deployer.succeed(f"wire apply boot --on receiver-second --no-progress --path {TEST_DIR}/hive.nix --no-keys --ssh-accept-host -vvv >&2") 32 - 33 - _first_system = receiver.succeed("readlink -f /run/current-system") 34 - assert first_system == _first_system, "apply boot without --reboot changed /run/current-system" 35 - 36 - # with subtest("Check /etc/identity after reboot"): 37 - # receiver.reboot() 38 - # 39 - # identity = receiver.succeed("cat /etc/identity") 40 - # assert identity == "second", "Identity didn't change after second apply" 41 - 42 - # with subtest("Check --reboot"): 43 - # deployer.succeed(f"wire apply boot --on receiver-third --no-progress --path {TEST_DIR}/hive.nix --reboot --no-keys -vvv >&2") 44 - # 45 - # identity = receiver.succeed("cat /etc/identity") 46 - # assert identity == "third", "Identity didn't change after third apply" 47 - ''; 12 + testScript = builtins.readFile ./script.py; 48 13 }; 49 14 }
+63
tests/nix/suite/test_remote_deploy/script.py
··· 1 + # SPDX-License-Identifier: AGPL-3.0-or-later 2 + # Copyright 2024-2025 wire Contributors 3 + 4 + from typing import TYPE_CHECKING 5 + from typing import Callable, ContextManager 6 + 7 + if TYPE_CHECKING: 8 + from test_driver.machine import Machine 9 + 10 + deployer: Machine = None # type: ignore[invalid-assignment] 11 + receiver: Machine = None # type: ignore[invalid-assignment] 12 + 13 + TEST_DIR = "" 14 + 15 + # https://github.com/NixOS/nixpkgs/blob/d10d9933b1c206f9b2950e5e1d68268c5ed0a3c7/nixos/lib/test-script-prepend.py#L43 16 + subtest: Callable[[str], ContextManager[None]] = None # type: ignore[invalid-assignment] 17 + 18 + # typing-end 19 + 20 + with subtest("Test unreachable hosts"): 21 + deployer.fail( 22 + f"wire apply --on receiver-unreachable --no-progress --path {TEST_DIR}/hive.nix --no-keys -vvv >&2" 23 + ) 24 + 25 + with subtest("Check basic apply: Interactive"): 26 + deployer.succeed( 27 + f"wire apply --on receiver --no-progress --path {TEST_DIR}/hive.nix --no-keys --ssh-accept-host -vvv >&2" 28 + ) 29 + 30 + identity = receiver.succeed("cat /etc/identity") 31 + assert identity == "first", "Identity of first apply wasn't as expected" 32 + 33 + with subtest("Check basic apply: NonInteractive"): 34 + deployer.succeed( 35 + f"wire apply --on receiver-third --no-progress --path {TEST_DIR}/hive.nix --no-keys --ssh-accept-host --non-interactive -vvv >&2" 36 + ) 37 + 38 + identity = receiver.succeed("cat /etc/identity") 39 + assert identity == "third", "Identity of non-interactive apply wasn't as expected" 40 + 41 + with subtest("Check boot apply"): 42 + first_system = receiver.succeed("readlink -f /run/current-system") 43 + 44 + deployer.succeed( 45 + f"wire apply boot --on receiver-second --no-progress --path {TEST_DIR}/hive.nix --no-keys --ssh-accept-host -vvv >&2" 46 + ) 47 + 48 + _first_system = receiver.succeed("readlink -f /run/current-system") 49 + assert first_system == _first_system, ( 50 + "apply boot without --reboot changed /run/current-system" 51 + ) 52 + 53 + # with subtest("Check /etc/identity after reboot"): 54 + # receiver.reboot() 55 + # 56 + # identity = receiver.succeed("cat /etc/identity") 57 + # assert identity == "second", "Identity didn't change after second apply" 58 + 59 + # with subtest("Check --reboot"): 60 + # deployer.succeed(f"wire apply boot --on receiver-third --no-progress --path {TEST_DIR}/hive.nix --reboot --no-keys -vvv >&2") 61 + # 62 + # identity = receiver.succeed("cat /etc/identity") 63 + # assert identity == "third", "Identity didn't change after third apply"
+1 -4
tests/nix/suite/test_stdin/default.nix
··· 7 7 _wire.deployer = true; 8 8 _wire.receiver = true; 9 9 }; 10 - testScript = '' 11 - deployer.succeed(f"echo @tag | wire apply --on deployer --no-progress --path {TEST_DIR}/hive.nix --no-keys -vvv >&2") 12 - deployer.succeed("test -f /etc/a") 13 - ''; 10 + testScript = builtins.readFile ./script.py; 14 11 }; 15 12 }
+17
tests/nix/suite/test_stdin/script.py
··· 1 + # SPDX-License-Identifier: AGPL-3.0-or-later 2 + # Copyright 2024-2025 wire Contributors 3 + 4 + from typing import TYPE_CHECKING 5 + 6 + if TYPE_CHECKING: 7 + from test_driver.machine import Machine 8 + 9 + deployer: Machine = None # type: ignore[invalid-assignment] 10 + TEST_DIR = "" 11 + 12 + # typing-end 13 + 14 + deployer.succeed( 15 + f"echo @tag | wire apply --on deployer --no-progress --path {TEST_DIR}/hive.nix --no-keys -vvv >&2" 16 + ) 17 + deployer.succeed("test -f /etc/a")
+8 -1
tests/nix/tools.py tests/nix/tools/__init__.py
··· 1 1 # SPDX-License-Identifier: AGPL-3.0-or-later 2 2 # Copyright 2024-2025 wire Contributors 3 3 4 + from typing import TYPE_CHECKING 5 + 6 + if TYPE_CHECKING: 7 + from test_driver.machine import Machine 8 + 9 + # typing-end 10 + 4 11 5 12 def collect_store_objects(machine: Machine) -> set[str]: 6 13 return set(machine.succeed("ls /nix/store").strip().split("\n")) ··· 10 17 paths = list(map(lambda n: f"/nix/store/{n}", objects)) 11 18 12 19 machine.succeed("which rg") 13 - machine.fail(f"rg '{poison}' {" ".join(paths)}") 20 + machine.fail(f"rg '{poison}' {' '.join(paths)}")
+303
tests/nix/uv.lock
··· 1 + version = 1 2 + revision = 3 3 + requires-python = ">=3.13" 4 + 5 + [[package]] 6 + name = "appdirs" 7 + version = "1.4.4" 8 + source = { registry = "https://pypi.org/simple" } 9 + sdist = { url = "https://files.pythonhosted.org/packages/d7/d8/05696357e0311f5b5c316d7b95f46c669dd9c15aaeecbb48c7d0aeb88c40/appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", size = 13470, upload-time = "2020-05-11T07:59:51.037Z" } 10 + wheels = [ 11 + { url = "https://files.pythonhosted.org/packages/3b/00/2344469e2084fb287c2e0b57b72910309874c3245463acd6cf5e3db69324/appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128", size = 9566, upload-time = "2020-05-11T07:59:49.499Z" }, 12 + ] 13 + 14 + [[package]] 15 + name = "asttokens" 16 + version = "3.0.1" 17 + source = { registry = "https://pypi.org/simple" } 18 + sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" } 19 + wheels = [ 20 + { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, 21 + ] 22 + 23 + [[package]] 24 + name = "colorama" 25 + version = "0.4.6" 26 + source = { registry = "https://pypi.org/simple" } 27 + sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } 28 + wheels = [ 29 + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, 30 + ] 31 + 32 + [[package]] 33 + name = "decorator" 34 + version = "5.2.1" 35 + source = { registry = "https://pypi.org/simple" } 36 + sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } 37 + wheels = [ 38 + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, 39 + ] 40 + 41 + [[package]] 42 + name = "executing" 43 + version = "2.2.1" 44 + source = { registry = "https://pypi.org/simple" } 45 + sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } 46 + wheels = [ 47 + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, 48 + ] 49 + 50 + [[package]] 51 + name = "ipython" 52 + version = "9.8.0" 53 + source = { registry = "https://pypi.org/simple" } 54 + dependencies = [ 55 + { name = "colorama", marker = "sys_platform == 'win32'" }, 56 + { name = "decorator" }, 57 + { name = "ipython-pygments-lexers" }, 58 + { name = "jedi" }, 59 + { name = "matplotlib-inline" }, 60 + { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, 61 + { name = "prompt-toolkit" }, 62 + { name = "pygments" }, 63 + { name = "stack-data" }, 64 + { name = "traitlets" }, 65 + ] 66 + sdist = { url = "https://files.pythonhosted.org/packages/12/51/a703c030f4928646d390b4971af4938a1b10c9dfce694f0d99a0bb073cb2/ipython-9.8.0.tar.gz", hash = "sha256:8e4ce129a627eb9dd221c41b1d2cdaed4ef7c9da8c17c63f6f578fe231141f83", size = 4424940, upload-time = "2025-12-03T10:18:24.353Z" } 67 + wheels = [ 68 + { url = "https://files.pythonhosted.org/packages/f1/df/8ee1c5dd1e3308b5d5b2f2dfea323bb2f3827da8d654abb6642051199049/ipython-9.8.0-py3-none-any.whl", hash = "sha256:ebe6d1d58d7d988fbf23ff8ff6d8e1622cfdb194daf4b7b73b792c4ec3b85385", size = 621374, upload-time = "2025-12-03T10:18:22.335Z" }, 69 + ] 70 + 71 + [[package]] 72 + name = "ipython-pygments-lexers" 73 + version = "1.1.1" 74 + source = { registry = "https://pypi.org/simple" } 75 + dependencies = [ 76 + { name = "pygments" }, 77 + ] 78 + sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } 79 + wheels = [ 80 + { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, 81 + ] 82 + 83 + [[package]] 84 + name = "jedi" 85 + version = "0.19.2" 86 + source = { registry = "https://pypi.org/simple" } 87 + dependencies = [ 88 + { name = "parso" }, 89 + ] 90 + sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } 91 + wheels = [ 92 + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, 93 + ] 94 + 95 + [[package]] 96 + name = "junit-xml" 97 + version = "1.9" 98 + source = { registry = "https://pypi.org/simple" } 99 + dependencies = [ 100 + { name = "six" }, 101 + ] 102 + sdist = { url = "https://files.pythonhosted.org/packages/98/af/bc988c914dd1ea2bc7540ecc6a0265c2b6faccc6d9cdb82f20e2094a8229/junit-xml-1.9.tar.gz", hash = "sha256:de16a051990d4e25a3982b2dd9e89d671067548718866416faec14d9de56db9f", size = 7349, upload-time = "2023-01-24T18:42:00.836Z" } 103 + wheels = [ 104 + { url = "https://files.pythonhosted.org/packages/2a/93/2d896b5fd3d79b4cadd8882c06650e66d003f465c9d12c488d92853dff78/junit_xml-1.9-py2.py3-none-any.whl", hash = "sha256:ec5ca1a55aefdd76d28fcc0b135251d156c7106fa979686a4b48d62b761b4732", size = 7130, upload-time = "2020-02-22T20:41:37.661Z" }, 105 + ] 106 + 107 + [[package]] 108 + name = "matplotlib-inline" 109 + version = "0.2.1" 110 + source = { registry = "https://pypi.org/simple" } 111 + dependencies = [ 112 + { name = "traitlets" }, 113 + ] 114 + sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110, upload-time = "2025-10-23T09:00:22.126Z" } 115 + wheels = [ 116 + { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" }, 117 + ] 118 + 119 + [[package]] 120 + name = "nixos-test-driver" 121 + version = "0.0.0" 122 + source = { git = "https://github.com/NixOS/nixpkgs?subdirectory=nixos%2Flib%2Ftest-driver%2Fsrc&branch=nixos-25.11#c6f52ebd45e5925c188d1a20119978aa4ffd5ef6" } 123 + 124 + [[package]] 125 + name = "parso" 126 + version = "0.8.5" 127 + source = { registry = "https://pypi.org/simple" } 128 + sdist = { url = "https://files.pythonhosted.org/packages/d4/de/53e0bcf53d13e005bd8c92e7855142494f41171b34c2536b86187474184d/parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a", size = 401205, upload-time = "2025-08-23T15:15:28.028Z" } 129 + wheels = [ 130 + { url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668, upload-time = "2025-08-23T15:15:25.663Z" }, 131 + ] 132 + 133 + [[package]] 134 + name = "pexpect" 135 + version = "4.9.0" 136 + source = { registry = "https://pypi.org/simple" } 137 + dependencies = [ 138 + { name = "ptyprocess" }, 139 + ] 140 + sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } 141 + wheels = [ 142 + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, 143 + ] 144 + 145 + [[package]] 146 + name = "prompt-toolkit" 147 + version = "3.0.52" 148 + source = { registry = "https://pypi.org/simple" } 149 + dependencies = [ 150 + { name = "wcwidth" }, 151 + ] 152 + sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } 153 + wheels = [ 154 + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, 155 + ] 156 + 157 + [[package]] 158 + name = "ptpython" 159 + version = "3.0.32" 160 + source = { registry = "https://pypi.org/simple" } 161 + dependencies = [ 162 + { name = "appdirs" }, 163 + { name = "jedi" }, 164 + { name = "prompt-toolkit" }, 165 + { name = "pygments" }, 166 + ] 167 + sdist = { url = "https://files.pythonhosted.org/packages/b6/8c/7e904ceeb512b4530c7ca1d918d3565d694a1fa7df337cdfc36a16347d68/ptpython-3.0.32.tar.gz", hash = "sha256:11651778236de95c582b42737294e50a66ba4a21fa01c0090ea70815af478fe0", size = 74080, upload-time = "2025-11-20T21:20:48.27Z" } 168 + wheels = [ 169 + { url = "https://files.pythonhosted.org/packages/4c/ac/0e35e5d7afd47ab0e2c71293ed2ad18df91a2a4a008c0ff59c2f22def377/ptpython-3.0.32-py3-none-any.whl", hash = "sha256:16435d323e5fc0a685d5f4dc5bb4494fb68ac68736689cd1247e1eda9369b616", size = 68099, upload-time = "2025-11-20T21:20:46.634Z" }, 170 + ] 171 + 172 + [[package]] 173 + name = "ptyprocess" 174 + version = "0.7.0" 175 + source = { registry = "https://pypi.org/simple" } 176 + sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } 177 + wheels = [ 178 + { 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" }, 179 + ] 180 + 181 + [[package]] 182 + name = "pure-eval" 183 + version = "0.2.3" 184 + source = { registry = "https://pypi.org/simple" } 185 + sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } 186 + wheels = [ 187 + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, 188 + ] 189 + 190 + [[package]] 191 + name = "pygments" 192 + version = "2.19.2" 193 + source = { registry = "https://pypi.org/simple" } 194 + sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } 195 + wheels = [ 196 + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, 197 + ] 198 + 199 + [[package]] 200 + name = "remote-pdb" 201 + version = "2.1.0" 202 + source = { registry = "https://pypi.org/simple" } 203 + sdist = { url = "https://files.pythonhosted.org/packages/e4/b5/4944cac06fd9fc4a2e168313ec220aa25ed96ce83947b63eea5b4045b22d/remote-pdb-2.1.0.tar.gz", hash = "sha256:2d70c6f41e0eabf0165e8f1be58f82aa7a605aaeab8f2aefeb9ce246431091c1", size = 22295, upload-time = "2020-07-24T13:31:32.985Z" } 204 + wheels = [ 205 + { url = "https://files.pythonhosted.org/packages/71/c5/d208c66344bb785d800adb61aef512290d3473052b9e7697890f0547aff2/remote_pdb-2.1.0-py2.py3-none-any.whl", hash = "sha256:94f73a92ac1248cf16189211011f97096bdada8a7baac8c79372663bbb57b5d0", size = 6304, upload-time = "2020-07-24T13:31:31.535Z" }, 206 + ] 207 + 208 + [[package]] 209 + name = "six" 210 + version = "1.17.0" 211 + source = { registry = "https://pypi.org/simple" } 212 + 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" } 213 + wheels = [ 214 + { 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" }, 215 + ] 216 + 217 + [[package]] 218 + name = "stack-data" 219 + version = "0.6.3" 220 + source = { registry = "https://pypi.org/simple" } 221 + dependencies = [ 222 + { name = "asttokens" }, 223 + { name = "executing" }, 224 + { name = "pure-eval" }, 225 + ] 226 + sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } 227 + wheels = [ 228 + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, 229 + ] 230 + 231 + [[package]] 232 + name = "traitlets" 233 + version = "5.14.3" 234 + source = { registry = "https://pypi.org/simple" } 235 + sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } 236 + wheels = [ 237 + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, 238 + ] 239 + 240 + [[package]] 241 + name = "ty" 242 + version = "0.0.4" 243 + source = { registry = "https://pypi.org/simple" } 244 + sdist = { url = "https://files.pythonhosted.org/packages/48/d9/97d5808e851f790e58f8a54efb5c7b9f404640baf9e295f424846040b316/ty-0.0.4.tar.gz", hash = "sha256:2ea47a0089d74730658ec4e988c8ef476a1e9bd92df3e56709c4003c2895ff3b", size = 4780289, upload-time = "2025-12-19T00:13:53.12Z" } 245 + wheels = [ 246 + { url = "https://files.pythonhosted.org/packages/b1/94/b32a962243cc8a16e8dc74cf1fe75e8bb013d0e13e71bb540e2c86214b61/ty-0.0.4-py3-none-linux_armv6l.whl", hash = "sha256:5225da65a8d1defeb21ee9d74298b1b97c6cbab36e235a310c1430d9079e4b6a", size = 9762399, upload-time = "2025-12-19T00:14:11.261Z" }, 247 + { url = "https://files.pythonhosted.org/packages/d1/d2/7c76e0c22ddfc2fcd4a3458a65f87ce074070eb1c68c07ee475cc2b6ea68/ty-0.0.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f87770d7988f470b795a2043185082fa959dbe1979a11b4bfe20f1214d37bd6e", size = 9590410, upload-time = "2025-12-19T00:13:55.759Z" }, 248 + { url = "https://files.pythonhosted.org/packages/a5/84/de4b1fc85669faca3622071d5a3f3ec7bfb239971f368c28fae461d3398a/ty-0.0.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:ecf68b8ea48674a289d733b4786aecc259242a2d9a920b3ec8583db18c67496a", size = 9131113, upload-time = "2025-12-19T00:14:08.593Z" }, 249 + { url = "https://files.pythonhosted.org/packages/a7/ff/b5bf385b6983be56a470856bbcbac1b7e816bcd765a7e9d39ab2399e387d/ty-0.0.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efc396d76a57e527393cae4ee8faf23b93be3df9e93202f39925721a7a2bb7b8", size = 9599152, upload-time = "2025-12-19T00:13:40.484Z" }, 250 + { url = "https://files.pythonhosted.org/packages/36/d6/9880ba106f2f20d13e6a5dca5d5ca44bfb3782936ee67ff635f89a2959c0/ty-0.0.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c893b968d2f9964a4d4db9992c9ba66b01f411b1f48dffcde08622e19cd6ab97", size = 9585368, upload-time = "2025-12-19T00:14:00.994Z" }, 251 + { url = "https://files.pythonhosted.org/packages/3f/53/503cfc18bc4c7c4e02f89dd43debc41a6e343b41eb43df658dfb493a386d/ty-0.0.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:526c925b80d68a53c165044d2370fcfc0def1f119f7b7e483ee61d24da6fb891", size = 9998412, upload-time = "2025-12-19T00:14:18.653Z" }, 252 + { url = "https://files.pythonhosted.org/packages/1d/bd/dd2d3e29834da5add2eda0ab5b433171ce9ce9a248c364d2e237f82073d7/ty-0.0.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:857f605a7fa366b6c6e6f38abc311d0606be513c2bee8977b5c8fd4bde1a82d5", size = 10853890, upload-time = "2025-12-19T00:13:50.891Z" }, 253 + { url = "https://files.pythonhosted.org/packages/07/fe/28ba3be1672e6b8df46e43de66a02dc076ffba7853d391a5466421886225/ty-0.0.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4cc981aa3ebdac2c233421b1e58c80b0df6a8e6e6fa8b9e69fbdfd2f82768af", size = 10587263, upload-time = "2025-12-19T00:14:21.577Z" }, 254 + { url = "https://files.pythonhosted.org/packages/26/9c/bb598772043f686afe5bc26cb386020709c1a0bcc164bc22ad9da2b4f55d/ty-0.0.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b03b2708b0bf67c76424a860f848aebaa4772c05529170c3761bfcaea93ec199", size = 10401204, upload-time = "2025-12-19T00:13:43.453Z" }, 255 + { url = "https://files.pythonhosted.org/packages/ac/18/71765e9d63669bf09461c3fea84a7a63232ccb0e83b84676f07b987fc217/ty-0.0.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:469890e885544beb129c21e2f8f15321f0573d094aec13da68593c5f86389ff9", size = 10129713, upload-time = "2025-12-19T00:14:13.725Z" }, 256 + { url = "https://files.pythonhosted.org/packages/c3/2d/c03eba570aa85e9c361de5ed36d60b9ab139e93ee91057f455ab4af48e54/ty-0.0.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:abfd928d09567e12068aeca875e920def3badf1978896f474aa4b85b552703c4", size = 9586203, upload-time = "2025-12-19T00:14:03.423Z" }, 257 + { url = "https://files.pythonhosted.org/packages/61/f1/8c3c82a8df69bd4417c77be4f895d043db26dd47bfcc90b33dc109cd0096/ty-0.0.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:44b8e94f9d64df12eae4cf8031c5ca9a4c610b57092b26ad3d68d91bcc7af122", size = 9608230, upload-time = "2025-12-19T00:13:58.252Z" }, 258 + { url = "https://files.pythonhosted.org/packages/51/0c/d8ba3a85c089c246ef6bd49d0f0b40bc0f9209bb819e8c02ccbea5cb4d57/ty-0.0.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9d6a439813e21a06769daf858105818c385d88018929d4a56970d4ddd5cd3df2", size = 9725125, upload-time = "2025-12-19T00:14:05.996Z" }, 259 + { url = "https://files.pythonhosted.org/packages/4d/38/e30f64ad1e40905c766576ec70cffc69163591a5842ce14652672f6ab394/ty-0.0.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:c3cfcf26cfe6c828e91d7a529cc2dda37bc3b51ba06909c9be07002a6584af52", size = 10237174, upload-time = "2025-12-19T00:14:23.858Z" }, 260 + { url = "https://files.pythonhosted.org/packages/cb/d7/8d650aa0be8936dd3ed74e2b0655230e2904caa6077c30c16a089b523cff/ty-0.0.4-py3-none-win32.whl", hash = "sha256:58bbf70dd27af6b00dedbdebeec92d5993aa238664f96fa5c0064930f7a0d30b", size = 9188434, upload-time = "2025-12-19T00:13:45.875Z" }, 261 + { url = "https://files.pythonhosted.org/packages/82/d7/9fc0c81cf0b0d281ac9c18bfbdb4d6bae2173503ba79e40b210ab41c2c8b/ty-0.0.4-py3-none-win_amd64.whl", hash = "sha256:7c2db0f96218f08c140bd9d3fcbb1b3c8c5c4f0c9b0a5624487f0a2bf4b76163", size = 10019313, upload-time = "2025-12-19T00:14:15.968Z" }, 262 + { url = "https://files.pythonhosted.org/packages/5f/b8/3e3246738eed1cd695c5964a401f3b9c757d20ac21fdae06281af9f40ef6/ty-0.0.4-py3-none-win_arm64.whl", hash = "sha256:69f14fc98e4a847afa9f8c5d5234d008820dbc09c7dcdb3ac1ba16628f5132df", size = 9561857, upload-time = "2025-12-19T00:13:48.382Z" }, 263 + ] 264 + 265 + [[package]] 266 + name = "wcwidth" 267 + version = "0.2.14" 268 + source = { registry = "https://pypi.org/simple" } 269 + sdist = { url = "https://files.pythonhosted.org/packages/24/30/6b0809f4510673dc723187aeaf24c7f5459922d01e2f794277a3dfb90345/wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605", size = 102293, upload-time = "2025-09-22T16:29:53.023Z" } 270 + wheels = [ 271 + { url = "https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1", size = 37286, upload-time = "2025-09-22T16:29:51.641Z" }, 272 + ] 273 + 274 + [[package]] 275 + name = "wire-vm-tests" 276 + version = "0.0.0" 277 + source = { virtual = "." } 278 + dependencies = [ 279 + { name = "colorama" }, 280 + { name = "ipython" }, 281 + { name = "junit-xml" }, 282 + { name = "nixos-test-driver" }, 283 + { name = "ptpython" }, 284 + { name = "remote-pdb" }, 285 + ] 286 + 287 + [package.dev-dependencies] 288 + dev = [ 289 + { name = "ty" }, 290 + ] 291 + 292 + [package.metadata] 293 + requires-dist = [ 294 + { name = "colorama", specifier = ">=0.4.6" }, 295 + { name = "ipython", specifier = ">=9.8.0" }, 296 + { name = "junit-xml", specifier = ">=1.9" }, 297 + { name = "nixos-test-driver", git = "https://github.com/NixOS/nixpkgs?subdirectory=nixos%2Flib%2Ftest-driver%2Fsrc&branch=nixos-25.11" }, 298 + { name = "ptpython", specifier = ">=3.0.32" }, 299 + { name = "remote-pdb", specifier = ">=2.1.0" }, 300 + ] 301 + 302 + [package.metadata.requires-dev] 303 + dev = [{ name = "ty", specifier = ">=0.0.4" }]