diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index aa06060..2373a48 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -32,7 +32,7 @@ jobs: - name: Install UV run: | - UV_VERSION=$(cat ./requirement-uv.txt | awk -F'==' '{print $2}' | tr -d ' \n') + UV_VERSION=$(cat ./uv-version.txt | tr -d ' \n') curl -LsSf https://astral.sh/uv/${UV_VERSION}/install.sh | sh - name: Record original PATH @@ -80,7 +80,7 @@ jobs: - name: Install UV run: | - UV_VERSION=$(cat ./requirement-uv.txt | awk -F'==' '{print $2}' | tr -d ' \n') + UV_VERSION=$(cat ./uv-version.txt | tr -d ' \n') curl -LsSf https://astral.sh/uv/${UV_VERSION}/install.sh | sh - name: Get Current Version diff --git a/.gitignore b/.gitignore index f61a68a..eaeb236 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -# basic +# general __pycache__ /dist .venv @@ -8,7 +8,10 @@ __pycache__ .ruff_cache .ropeproject -# git subrepos +# misc +.DS_Store + +# subrepos /android_* /*_kernel_* /clang* @@ -16,7 +19,7 @@ __pycache__ /AnyKernel3 /KernelSU -# misc local artifacts +# local artifacts /*.log /kernel /assets diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1d3e452..cfff8f8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,7 +12,7 @@ job-build: - docker:dind script: - apk update && apk add python3 py3-pip - - python3 -m pip install -r requirement-uv.txt --break-system-packages + - python3 -m pip install uv==$(cat ./uv-version.txt | tr -d ' \n') --break-system-packages - uv sync --frozen --no-install-project - source .venv/bin/activate - export PYTHONPATH=$(pwd) diff --git a/Dockerfile b/Dockerfile index 52d32af..8a53612 100755 --- a/Dockerfile +++ b/Dockerfile @@ -34,7 +34,7 @@ RUN \ # This significantly reduces the total build time, as each time we make a build call for a device, # only device-specific kernel source is being downloaded into the container. # -RUN curl -LsSf https://astral.sh/uv/$(cat ./requirement-uv.txt | awk -F'==' '{print $2}' | tr -d ' \n')/install.sh | sh && \ +RUN curl -LsSf https://astral.sh/uv/$(cat ./uv-version.txt | tr -d ' \n')/install.sh | sh && \ . $HOME/.local/bin/env && \ uv sync --frozen --no-install-project && \ uv run ${WDIR}/zkb/utils/bridge.py --shared diff --git a/README.md b/README.md index e87ec6a..c7d142b 100755 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# zero_kernel +# zero-kernel An advanced Android kernel builder with assets collection and Kali NetHunter support. ## Contents -- [zero\_kernel](#zero_kernel) +- [zero-kernel](#zero-kernel) - [Contents](#contents) - [**Important**](#important) - [Description](#description) @@ -123,7 +123,7 @@ To run this tool in a `local` environment, you will need: ```sh # install uv version from project file -python3 -m pip install -r requirement-uv.txt +python3 -m pip install uv==$(cat ./uv-version.txt | tr -d ' \n') # make zkb/ internal imports visible to itself export PYTHONPATH=$(pwd) # prepare and activate dev environment diff --git a/conanfile.py b/conanfile.py index 23c3ff3..8006ab6 100644 --- a/conanfile.py +++ b/conanfile.py @@ -6,7 +6,7 @@ class ZeroKernelConan(ConanFile): author = "seppzer0" url = "https://gitlab.com/api/v4/projects/40803264/packages/conan" description = "An advanced Android kernel builder with Kali NetHunter support." - topics = ("zero_kernel", "kali-nethunter", "nethunter") + topics = ("zero-kernel", "kali-nethunter", "nethunter") settings = None options = { "base": {"los", "pa", "x", "aosp"}, diff --git a/docs/FLASHING.md b/docs/FLASHING.md index d44b368..25454aa 100755 --- a/docs/FLASHING.md +++ b/docs/FLASHING.md @@ -67,7 +67,9 @@ Before doing anything, please ensure that you have: - open NetHunter app (if seeing a Busybox-related error, press "OK" and re-open the app); - navigate to the `Kali Chroot Manager` submenu and install the chroot (if you downloaded it beforehand, use the "restore" option); - make sure that your NetHunter and NetHunter Terminal apps are properly configured to see the installed chroot directory (by default it may be `/data/local/nhsystem/kalifs`; if you see it anywhere, change it to `/data/local/nhsystem/kali-arm64`); -- in NetHunter Terminal app open `Kali` shell (if it opens properly, then congratulations, you have a working Kali NetHunter on your device). +- in NetHunter Terminal app open `Kali` shell (if it opens properly, then congratulations, you have a working Kali NetHunter on your device); +- unlock Developer Options in Settings; +- toggle "Wi-Fi non-persistent MAC randomisation" option or similar. #### For KernelSU users @@ -77,7 +79,9 @@ Before doing anything, please ensure that you have: - open the NetHunter app (if seeing a Busybox-related error, press "OK" and re-open the app); - navigate to the `Kali Chroot Manager` submenu and install the chroot (if you downloaded it beforehand, use the "restore" option); - make sure that your NetHunter and NetHunter Terminal apps are properly configured to see the installed chroot directory (by default it may be `/data/local/nhsystem/kalifs`; if you see it anywhere, change it to `/data/local/nhsystem/kali-arm64`); -- in NetHunter Terminal app open `Kali` shell (if it opens properly, then congratulations, you have a working Kali NetHunter on your device). +- in NetHunter Terminal app open `Kali` shell (if it opens properly, then congratulations, you have a working Kali NetHunter on your device); +- unlock Developer Options in Settings; +- toggle "Wi-Fi non-persistent MAC randomisation" option or similar. #### For x_kernel-based kernel + ParanoidAndroid users diff --git a/docs/architecture/clients/classes.puml b/docs/architecture/clients/classes.puml index 54b22ea..6ebc4a3 100644 --- a/docs/architecture/clients/classes.puml +++ b/docs/architecture/clients/classes.puml @@ -1,8 +1,6 @@ @startuml classes set namespaceSeparator none class "GithubApiClient" as clients.github.GithubApiClient { - direct_url : str - endpoint : str file_filter : Optional[str] project : str run() -> str | None @@ -10,19 +8,19 @@ class "GithubApiClient" as clients.github.GithubApiClient { class "LineageOsApiClient" as clients.los.LineageOsApiClient { endpoint : str json_key : str - rom_name : str + rom_name } class "ParanoidAndroidApiClient" as clients.pa.ParanoidAndroidApiClient { endpoint : str json_key : str - rom_name : str + rom_name map_codename() -> str } class "RomApiClient" as clients.rom_api.RomApiClient { codename : str endpoint : str json_key : str - rom_name : str + rom_name rom_only : bool map_codename() -> str run() -> str diff --git a/docs/architecture/commands/classes.puml b/docs/architecture/commands/classes.puml index 61fe861..28d0f99 100644 --- a/docs/architecture/commands/classes.puml +++ b/docs/architecture/commands/classes.puml @@ -1,16 +1,16 @@ @startuml classes set namespaceSeparator none class "AssetsCommand" as commands.assets.AssetsCommand { - assets_collector : AssetsCollector + assets_collector execute() -> None } class "BundleCommand" as commands.bundle.BundleCommand { - assets_collector : AssetsCollector + assets_collector base : str - kernel_builder : KernelBuilder - package_type : str - build_kernel(rom_name: str, clean_only: Optional[bool]) -> None - collect_assets(rom_name: str, chroot: Literal['full', 'minimal']) -> None + kernel_builder + package_type : Iterable[EnumPackageType] + build_kernel(clean_only: Optional[bool]) -> None + collect_assets() -> None conan_options(json_file: str) -> dict conan_package(options: tuple[str, ...], reference: str) -> None conan_sources() -> None @@ -18,7 +18,7 @@ class "BundleCommand" as commands.bundle.BundleCommand { execute() -> None } class "KernelCommand" as commands.kernel.KernelCommand { - kernel_builder : KernelBuilder + kernel_builder execute() -> None } @enduml diff --git a/docs/architecture/configs/classes.puml b/docs/architecture/configs/classes.puml index 4f4bfcb..d117471 100644 --- a/docs/architecture/configs/classes.puml +++ b/docs/architecture/configs/classes.puml @@ -2,18 +2,18 @@ set namespaceSeparator none class "ArgumentConfig" as configs.argument.ArgumentConfig { base : str - benv : Literal['docker', 'podman', 'local'] + benv chroot : Optional[str] clean_assets : Optional[bool] clean_image : Optional[bool] clean_kernel : Optional[bool] codename : str - command : Literal['kernel', 'assets', 'bundle'] + command conan_upload : Optional[bool] defconfig : Optional[Path] ksu : Optional[bool] lkv : Optional[str] - package_type : Optional[str] + package_type : Optional[EnumPackageType] rom_only : Optional[bool] check_settings() -> None } @@ -23,4 +23,7 @@ class "DirectoryConfig" as configs.directory.DirectoryConfig { kernel : Path root : Path } +class "ModelConfig" as configs.model.ModelConfig { + model_config +} @enduml diff --git a/docs/architecture/configs/packages.puml b/docs/architecture/configs/packages.puml index c3101df..54ab3ff 100644 --- a/docs/architecture/configs/packages.puml +++ b/docs/architecture/configs/packages.puml @@ -6,6 +6,9 @@ package "configs.argument" as configs.argument { } package "configs.directory" as configs.directory { } +package "configs.model" as configs.model { +} configs --> configs.argument configs --> configs.directory +configs --> configs.model @enduml diff --git a/docs/architecture/core/classes.puml b/docs/architecture/core/classes.puml index cd8cbb4..fa46937 100644 --- a/docs/architecture/core/classes.puml +++ b/docs/architecture/core/classes.puml @@ -3,7 +3,7 @@ set namespaceSeparator none class "AssetsCollector" as core.assets_collector.AssetsCollector { assets : list base : str - chroot : Optional[Literal['full', 'minimal']] + chroot : Optional[EnumChroot] clean_assets : bool codename : str ksu : bool @@ -20,7 +20,7 @@ class "KernelBuilder" as core.kernel_builder.KernelBuilder { ksu : bool lkv : str lkv_src : str - rmanager : ResourceManager + rmanager build() -> None clean_build() -> None create_zip() -> None diff --git a/docs/architecture/engines/classes.puml b/docs/architecture/engines/classes.puml index 4b0a7bb..f1e5e81 100644 --- a/docs/architecture/engines/classes.puml +++ b/docs/architecture/engines/classes.puml @@ -2,14 +2,14 @@ set namespaceSeparator none class "GenericContainerEngine" as engines.generic_container.GenericContainerEngine { base : str - benv : Literal['docker', 'podman'] + benv builder_cmd : str - chroot : Optional[Literal['full', 'minimal']] + chroot : Optional[EnumChroot] clean_assets : Optional[bool] clean_image : Optional[bool] clean_kernel : Optional[bool] codename : str - command : Literal['kernel', 'assets', 'bundle'] + command conan_upload : Optional[bool] container_options : list[str] defconfig : Optional[Path] diff --git a/pyproject.toml b/pyproject.toml index eee39bd..1d9b285 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,14 +1,14 @@ [project] name = "zero-kernel-builder" -version = "0.6.4" +version = "0.6.5" description = "Advanced Android kernel builder with Kali NetHunter support." authors = [{name = "seppzer0"}] readme = "README.md" requires-python = ">=3.12" [project.urls] -Repository = "https://github.com/seppzer0/zero_kernel" -Documentation = "https://github.com/seppzer0/zero_kernel/blob/main/README.md" +Repository = "https://github.com/seppzer0/zero-kernel" +Documentation = "https://github.com/seppzer0/zero-kernel/blob/main/README.md" [project.scripts] zkb = "zkb.__main__:main" diff --git a/requirement-uv.txt b/requirement-uv.txt deleted file mode 100644 index 94ee389..0000000 --- a/requirement-uv.txt +++ /dev/null @@ -1 +0,0 @@ -uv==0.9.22 diff --git a/scripts/multi_build.py b/scripts/multi_build.py index fe3a868..621696e 100644 --- a/scripts/multi_build.py +++ b/scripts/multi_build.py @@ -28,8 +28,8 @@ def parse_args() -> argparse.Namespace: def rmove(src: Path, dst: Path) -> None: """Recursively move files from one directory to another. - :param Path src: Source path. - :param Path dst: Destination path. + :param pathlib.Path src: Source path. + :param pathlib.Path dst: Destination path. :return: None """ # for a directory diff --git a/uv-version.txt b/uv-version.txt new file mode 100644 index 0000000..2d993c4 --- /dev/null +++ b/uv-version.txt @@ -0,0 +1 @@ +0.10.7 diff --git a/uv.lock b/uv.lock index ca73864..f2479bb 100644 --- a/uv.lock +++ b/uv.lock @@ -21,25 +21,25 @@ wheels = [ [[package]] name = "astroid" -version = "4.0.3" +version = "4.0.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/c17d0f83016532a1ad87d1de96837164c99d47a3b6bbba28bd597c25b37a/astroid-4.0.3.tar.gz", hash = "sha256:08d1de40d251cc3dc4a7a12726721d475ac189e4e583d596ece7422bc176bda3", size = 406224, upload-time = "2026-01-03T22:14:26.096Z" } +sdist = { url = "https://files.pythonhosted.org/packages/07/63/0adf26577da5eff6eb7a177876c1cfa213856be9926a000f65c4add9692b/astroid-4.0.4.tar.gz", hash = "sha256:986fed8bcf79fb82c78b18a53352a0b287a73817d6dbcfba3162da36667c49a0", size = 406358, upload-time = "2026-02-07T23:35:07.509Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/66/686ac4fc6ef48f5bacde625adac698f41d5316a9753c2b20bb0931c9d4e2/astroid-4.0.3-py3-none-any.whl", hash = "sha256:864a0a34af1bd70e1049ba1e61cee843a7252c826d97825fcee9b2fcbd9e1b14", size = 276443, upload-time = "2026-01-03T22:14:24.412Z" }, + { url = "https://files.pythonhosted.org/packages/b0/cf/1c5f42b110e57bc5502eb80dbc3b03d256926062519224835ef08134f1f9/astroid-4.0.4-py3-none-any.whl", hash = "sha256:52f39653876c7dec3e3afd4c2696920e05c83832b9737afc21928f2d2eb7a753", size = 276445, upload-time = "2026-02-07T23:35:05.344Z" }, ] [[package]] name = "bandit" -version = "1.9.2" +version = "1.9.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "rich", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "stevedore", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cf/72/f704a97aac430aeb704fa16435dfa24fbeaf087d46724d0965eb1f756a2c/bandit-1.9.2.tar.gz", hash = "sha256:32410415cd93bf9c8b91972159d5cf1e7f063a9146d70345641cd3877de348ce", size = 4241659, upload-time = "2025-11-23T21:36:18.722Z" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/c3/0cb80dfe0f3076e5da7e4c5ad8e57bac6ac357ff4a6406205501cade4965/bandit-1.9.4.tar.gz", hash = "sha256:b589e5de2afe70bd4d53fa0c1da6199f4085af666fde00e8a034f152a52cd628", size = 4242677, upload-time = "2026-02-25T06:44:15.503Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/55/1a/5b0320642cca53a473e79c7d273071b5a9a8578f9e370b74da5daa2768d7/bandit-1.9.2-py3-none-any.whl", hash = "sha256:bda8d68610fc33a6e10b7a8f1d61d92c8f6c004051d5e946406be1fb1b16a868", size = 134377, upload-time = "2025-11-23T21:36:17.39Z" }, + { url = "https://files.pythonhosted.org/packages/05/a4/a26d5b25671d27e03afb5401a0be5899d94ff8fab6a698b1ac5be3ec29ef/bandit-1.9.4-py3-none-any.whl", hash = "sha256:f89ffa663767f5a0585ea075f01020207e966a9c0f2b9ef56a57c7963a3f6f8e", size = 134741, upload-time = "2026-02-25T06:44:13.694Z" }, ] [[package]] @@ -53,11 +53,11 @@ wheels = [ [[package]] name = "certifi" -version = "2026.1.4" +version = "2026.2.25" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" } +sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" }, + { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, ] [[package]] @@ -152,70 +152,80 @@ sdist = { url = "https://files.pythonhosted.org/packages/ee/e8/0893952ee0c0f7810 [[package]] name = "coverage" -version = "7.13.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/23/f9/e92df5e07f3fc8d4c7f9a0f146ef75446bf870351cd37b788cf5897f8079/coverage-7.13.1.tar.gz", hash = "sha256:b7593fe7eb5feaa3fbb461ac79aac9f9fc0387a5ca8080b0c6fe2ca27b091afd", size = 825862, upload-time = "2025-12-28T15:42:56.969Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/8a/87af46cccdfa78f53db747b09f5f9a21d5fc38d796834adac09b30a8ce74/coverage-7.13.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6f34591000f06e62085b1865c9bc5f7858df748834662a51edadfd2c3bfe0dd3", size = 218927, upload-time = "2025-12-28T15:40:52.814Z" }, - { url = "https://files.pythonhosted.org/packages/82/a8/6e22fdc67242a4a5a153f9438d05944553121c8f4ba70cb072af4c41362e/coverage-7.13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b67e47c5595b9224599016e333f5ec25392597a89d5744658f837d204e16c63e", size = 219288, upload-time = "2025-12-28T15:40:54.262Z" }, - { url = "https://files.pythonhosted.org/packages/d0/0a/853a76e03b0f7c4375e2ca025df45c918beb367f3e20a0a8e91967f6e96c/coverage-7.13.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e7b8bd70c48ffb28461ebe092c2345536fb18bbbf19d287c8913699735f505c", size = 250786, upload-time = "2025-12-28T15:40:56.059Z" }, - { url = "https://files.pythonhosted.org/packages/ea/b4/694159c15c52b9f7ec7adf49d50e5f8ee71d3e9ef38adb4445d13dd56c20/coverage-7.13.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c223d078112e90dc0e5c4e35b98b9584164bea9fbbd221c0b21c5241f6d51b62", size = 253543, upload-time = "2025-12-28T15:40:57.585Z" }, - { url = "https://files.pythonhosted.org/packages/96/b2/7f1f0437a5c855f87e17cf5d0dc35920b6440ff2b58b1ba9788c059c26c8/coverage-7.13.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:794f7c05af0763b1bbd1b9e6eff0e52ad068be3b12cd96c87de037b01390c968", size = 254635, upload-time = "2025-12-28T15:40:59.443Z" }, - { url = "https://files.pythonhosted.org/packages/e9/d1/73c3fdb8d7d3bddd9473c9c6a2e0682f09fc3dfbcb9c3f36412a7368bcab/coverage-7.13.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0642eae483cc8c2902e4af7298bf886d605e80f26382124cddc3967c2a3df09e", size = 251202, upload-time = "2025-12-28T15:41:01.328Z" }, - { url = "https://files.pythonhosted.org/packages/66/3c/f0edf75dcc152f145d5598329e864bbbe04ab78660fe3e8e395f9fff010f/coverage-7.13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9f5e772ed5fef25b3de9f2008fe67b92d46831bd2bc5bdc5dd6bfd06b83b316f", size = 252566, upload-time = "2025-12-28T15:41:03.319Z" }, - { url = "https://files.pythonhosted.org/packages/17/b3/e64206d3c5f7dcbceafd14941345a754d3dbc78a823a6ed526e23b9cdaab/coverage-7.13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:45980ea19277dc0a579e432aef6a504fe098ef3a9032ead15e446eb0f1191aee", size = 250711, upload-time = "2025-12-28T15:41:06.411Z" }, - { url = "https://files.pythonhosted.org/packages/dc/ad/28a3eb970a8ef5b479ee7f0c484a19c34e277479a5b70269dc652b730733/coverage-7.13.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:e4f18eca6028ffa62adbd185a8f1e1dd242f2e68164dba5c2b74a5204850b4cf", size = 250278, upload-time = "2025-12-28T15:41:08.285Z" }, - { url = "https://files.pythonhosted.org/packages/54/e3/c8f0f1a93133e3e1291ca76cbb63565bd4b5c5df63b141f539d747fff348/coverage-7.13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8dca5590fec7a89ed6826fce625595279e586ead52e9e958d3237821fbc750c", size = 252154, upload-time = "2025-12-28T15:41:09.969Z" }, - { url = "https://files.pythonhosted.org/packages/a3/a4/e98e689347a1ff1a7f67932ab535cef82eb5e78f32a9e4132e114bbb3a0a/coverage-7.13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cb237bfd0ef4d5eb6a19e29f9e528ac67ac3be932ea6b44fb6cc09b9f3ecff78", size = 218951, upload-time = "2025-12-28T15:41:16.653Z" }, - { url = "https://files.pythonhosted.org/packages/32/33/7cbfe2bdc6e2f03d6b240d23dc45fdaf3fd270aaf2d640be77b7f16989ab/coverage-7.13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1dcb645d7e34dcbcc96cd7c132b1fc55c39263ca62eb961c064eb3928997363b", size = 219325, upload-time = "2025-12-28T15:41:18.609Z" }, - { url = "https://files.pythonhosted.org/packages/59/f6/efdabdb4929487baeb7cb2a9f7dac457d9356f6ad1b255be283d58b16316/coverage-7.13.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3d42df8201e00384736f0df9be2ced39324c3907607d17d50d50116c989d84cd", size = 250309, upload-time = "2025-12-28T15:41:20.629Z" }, - { url = "https://files.pythonhosted.org/packages/12/da/91a52516e9d5aea87d32d1523f9cdcf7a35a3b298e6be05d6509ba3cfab2/coverage-7.13.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fa3edde1aa8807de1d05934982416cb3ec46d1d4d91e280bcce7cca01c507992", size = 252907, upload-time = "2025-12-28T15:41:22.257Z" }, - { url = "https://files.pythonhosted.org/packages/75/38/f1ea837e3dc1231e086db1638947e00d264e7e8c41aa8ecacf6e1e0c05f4/coverage-7.13.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9edd0e01a343766add6817bc448408858ba6b489039eaaa2018474e4001651a4", size = 254148, upload-time = "2025-12-28T15:41:23.87Z" }, - { url = "https://files.pythonhosted.org/packages/7f/43/f4f16b881aaa34954ba446318dea6b9ed5405dd725dd8daac2358eda869a/coverage-7.13.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:985b7836931d033570b94c94713c6dba5f9d3ff26045f72c3e5dbc5fe3361e5a", size = 250515, upload-time = "2025-12-28T15:41:25.437Z" }, - { url = "https://files.pythonhosted.org/packages/84/34/8cba7f00078bd468ea914134e0144263194ce849ec3baad187ffb6203d1c/coverage-7.13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ffed1e4980889765c84a5d1a566159e363b71d6b6fbaf0bebc9d3c30bc016766", size = 252292, upload-time = "2025-12-28T15:41:28.459Z" }, - { url = "https://files.pythonhosted.org/packages/8c/a4/cffac66c7652d84ee4ac52d3ccb94c015687d3b513f9db04bfcac2ac800d/coverage-7.13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8842af7f175078456b8b17f1b73a0d16a65dcbdc653ecefeb00a56b3c8c298c4", size = 250242, upload-time = "2025-12-28T15:41:30.02Z" }, - { url = "https://files.pythonhosted.org/packages/f4/78/9a64d462263dde416f3c0067efade7b52b52796f489b1037a95b0dc389c9/coverage-7.13.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:ccd7a6fca48ca9c131d9b0a2972a581e28b13416fc313fb98b6d24a03ce9a398", size = 250068, upload-time = "2025-12-28T15:41:32.007Z" }, - { url = "https://files.pythonhosted.org/packages/69/c8/a8994f5fece06db7c4a97c8fc1973684e178599b42e66280dded0524ef00/coverage-7.13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0403f647055de2609be776965108447deb8e384fe4a553c119e3ff6bfbab4784", size = 251846, upload-time = "2025-12-28T15:41:33.946Z" }, - { url = "https://files.pythonhosted.org/packages/70/52/f2be52cc445ff75ea8397948c96c1b4ee14f7f9086ea62fc929c5ae7b717/coverage-7.13.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:67170979de0dacac3f3097d02b0ad188d8edcea44ccc44aaa0550af49150c7dc", size = 219643, upload-time = "2025-12-28T15:41:41.567Z" }, - { url = "https://files.pythonhosted.org/packages/47/79/c85e378eaa239e2edec0c5523f71542c7793fe3340954eafb0bc3904d32d/coverage-7.13.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f80e2bb21bfab56ed7405c2d79d34b5dc0bc96c2c1d2a067b643a09fb756c43a", size = 219997, upload-time = "2025-12-28T15:41:43.418Z" }, - { url = "https://files.pythonhosted.org/packages/fe/9b/b1ade8bfb653c0bbce2d6d6e90cc6c254cbb99b7248531cc76253cb4da6d/coverage-7.13.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f83351e0f7dcdb14d7326c3d8d8c4e915fa685cbfdc6281f9470d97a04e9dfe4", size = 261296, upload-time = "2025-12-28T15:41:45.207Z" }, - { url = "https://files.pythonhosted.org/packages/1f/af/ebf91e3e1a2473d523e87e87fd8581e0aa08741b96265730e2d79ce78d8d/coverage-7.13.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb3f6562e89bad0110afbe64e485aac2462efdce6232cdec7862a095dc3412f6", size = 263363, upload-time = "2025-12-28T15:41:47.163Z" }, - { url = "https://files.pythonhosted.org/packages/c4/8b/fb2423526d446596624ac7fde12ea4262e66f86f5120114c3cfd0bb2befa/coverage-7.13.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77545b5dcda13b70f872c3b5974ac64c21d05e65b1590b441c8560115dc3a0d1", size = 265783, upload-time = "2025-12-28T15:41:49.03Z" }, - { url = "https://files.pythonhosted.org/packages/9b/26/ef2adb1e22674913b89f0fe7490ecadcef4a71fa96f5ced90c60ec358789/coverage-7.13.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a4d240d260a1aed814790bbe1f10a5ff31ce6c21bc78f0da4a1e8268d6c80dbd", size = 260508, upload-time = "2025-12-28T15:41:51.035Z" }, - { url = "https://files.pythonhosted.org/packages/ce/7d/f0f59b3404caf662e7b5346247883887687c074ce67ba453ea08c612b1d5/coverage-7.13.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d2287ac9360dec3837bfdad969963a5d073a09a85d898bd86bea82aa8876ef3c", size = 263357, upload-time = "2025-12-28T15:41:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/1a/b1/29896492b0b1a047604d35d6fa804f12818fa30cdad660763a5f3159e158/coverage-7.13.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:0d2c11f3ea4db66b5cbded23b20185c35066892c67d80ec4be4bab257b9ad1e0", size = 260978, upload-time = "2025-12-28T15:41:54.589Z" }, - { url = "https://files.pythonhosted.org/packages/48/f2/971de1238a62e6f0a4128d37adadc8bb882ee96afbe03ff1570291754629/coverage-7.13.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:3fc6a169517ca0d7ca6846c3c5392ef2b9e38896f61d615cb75b9e7134d4ee1e", size = 259877, upload-time = "2025-12-28T15:41:56.263Z" }, - { url = "https://files.pythonhosted.org/packages/6a/fc/0474efcbb590ff8628830e9aaec5f1831594874360e3251f1fdec31d07a3/coverage-7.13.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d10a2ed46386e850bb3de503a54f9fe8192e5917fcbb143bfef653a9355e9a53", size = 262069, upload-time = "2025-12-28T15:41:58.093Z" }, - { url = "https://files.pythonhosted.org/packages/aa/8e/ba0e597560c6563fc0adb902fda6526df5d4aa73bb10adf0574d03bd2206/coverage-7.13.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:97ab3647280d458a1f9adb85244e81587505a43c0c7cff851f5116cd2814b894", size = 218996, upload-time = "2025-12-28T15:42:04.978Z" }, - { url = "https://files.pythonhosted.org/packages/6b/8e/764c6e116f4221dc7aa26c4061181ff92edb9c799adae6433d18eeba7a14/coverage-7.13.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8f572d989142e0908e6acf57ad1b9b86989ff057c006d13b76c146ec6a20216a", size = 219326, upload-time = "2025-12-28T15:42:06.691Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a6/6130dc6d8da28cdcbb0f2bf8865aeca9b157622f7c0031e48c6cf9a0e591/coverage-7.13.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d72140ccf8a147e94274024ff6fd8fb7811354cf7ef88b1f0a988ebaa5bc774f", size = 250374, upload-time = "2025-12-28T15:42:08.786Z" }, - { url = "https://files.pythonhosted.org/packages/82/2b/783ded568f7cd6b677762f780ad338bf4b4750205860c17c25f7c708995e/coverage-7.13.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d3c9f051b028810f5a87c88e5d6e9af3c0ff32ef62763bf15d29f740453ca909", size = 252882, upload-time = "2025-12-28T15:42:10.515Z" }, - { url = "https://files.pythonhosted.org/packages/cd/b2/9808766d082e6a4d59eb0cc881a57fc1600eb2c5882813eefff8254f71b5/coverage-7.13.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f398ba4df52d30b1763f62eed9de5620dcde96e6f491f4c62686736b155aa6e4", size = 254218, upload-time = "2025-12-28T15:42:12.208Z" }, - { url = "https://files.pythonhosted.org/packages/44/ea/52a985bb447c871cb4d2e376e401116520991b597c85afdde1ea9ef54f2c/coverage-7.13.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:132718176cc723026d201e347f800cd1a9e4b62ccd3f82476950834dad501c75", size = 250391, upload-time = "2025-12-28T15:42:14.21Z" }, - { url = "https://files.pythonhosted.org/packages/7f/1d/125b36cc12310718873cfc8209ecfbc1008f14f4f5fa0662aa608e579353/coverage-7.13.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9e549d642426e3579b3f4b92d0431543b012dcb6e825c91619d4e93b7363c3f9", size = 252239, upload-time = "2025-12-28T15:42:16.292Z" }, - { url = "https://files.pythonhosted.org/packages/6a/16/10c1c164950cade470107f9f14bbac8485f8fb8515f515fca53d337e4a7f/coverage-7.13.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:90480b2134999301eea795b3a9dbf606c6fbab1b489150c501da84a959442465", size = 250196, upload-time = "2025-12-28T15:42:18.54Z" }, - { url = "https://files.pythonhosted.org/packages/2a/c6/cd860fac08780c6fd659732f6ced1b40b79c35977c1356344e44d72ba6c4/coverage-7.13.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e825dbb7f84dfa24663dd75835e7257f8882629fc11f03ecf77d84a75134b864", size = 250008, upload-time = "2025-12-28T15:42:20.365Z" }, - { url = "https://files.pythonhosted.org/packages/f0/3a/a8c58d3d38f82a5711e1e0a67268362af48e1a03df27c03072ac30feefcf/coverage-7.13.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:623dcc6d7a7ba450bbdbeedbaa0c42b329bdae16491af2282f12a7e809be7eb9", size = 251671, upload-time = "2025-12-28T15:42:22.114Z" }, - { url = "https://files.pythonhosted.org/packages/16/61/d5b7a0a0e0e40d62e59bc8c7aa1afbd86280d82728ba97f0673b746b78e2/coverage-7.13.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:60cfb538fe9ef86e5b2ab0ca8fc8d62524777f6c611dcaf76dc16fbe9b8e698a", size = 219730, upload-time = "2025-12-28T15:42:29.306Z" }, - { url = "https://files.pythonhosted.org/packages/a3/2c/8881326445fd071bb49514d1ce97d18a46a980712b51fee84f9ab42845b4/coverage-7.13.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:57dfc8048c72ba48a8c45e188d811e5efd7e49b387effc8fb17e97936dde5bf6", size = 220001, upload-time = "2025-12-28T15:42:31.319Z" }, - { url = "https://files.pythonhosted.org/packages/b5/d7/50de63af51dfa3a7f91cc37ad8fcc1e244b734232fbc8b9ab0f3c834a5cd/coverage-7.13.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3f2f725aa3e909b3c5fdb8192490bdd8e1495e85906af74fe6e34a2a77ba0673", size = 261370, upload-time = "2025-12-28T15:42:32.992Z" }, - { url = "https://files.pythonhosted.org/packages/e1/2c/d31722f0ec918fd7453b2758312729f645978d212b410cd0f7c2aed88a94/coverage-7.13.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ee68b21909686eeb21dfcba2c3b81fee70dcf38b140dcd5aa70680995fa3aa5", size = 263485, upload-time = "2025-12-28T15:42:34.759Z" }, - { url = "https://files.pythonhosted.org/packages/fa/7a/2c114fa5c5fc08ba0777e4aec4c97e0b4a1afcb69c75f1f54cff78b073ab/coverage-7.13.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:724b1b270cb13ea2e6503476e34541a0b1f62280bc997eab443f87790202033d", size = 265890, upload-time = "2025-12-28T15:42:36.517Z" }, - { url = "https://files.pythonhosted.org/packages/65/d9/f0794aa1c74ceabc780fe17f6c338456bbc4e96bd950f2e969f48ac6fb20/coverage-7.13.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:916abf1ac5cf7eb16bc540a5bf75c71c43a676f5c52fcb9fe75a2bd75fb944e8", size = 260445, upload-time = "2025-12-28T15:42:38.646Z" }, - { url = "https://files.pythonhosted.org/packages/49/23/184b22a00d9bb97488863ced9454068c79e413cb23f472da6cbddc6cfc52/coverage-7.13.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:776483fd35b58d8afe3acbd9988d5de592ab6da2d2a865edfdbc9fdb43e7c486", size = 263357, upload-time = "2025-12-28T15:42:40.788Z" }, - { url = "https://files.pythonhosted.org/packages/7d/bd/58af54c0c9199ea4190284f389005779d7daf7bf3ce40dcd2d2b2f96da69/coverage-7.13.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b6f3b96617e9852703f5b633ea01315ca45c77e879584f283c44127f0f1ec564", size = 260959, upload-time = "2025-12-28T15:42:42.808Z" }, - { url = "https://files.pythonhosted.org/packages/4b/2a/6839294e8f78a4891bf1df79d69c536880ba2f970d0ff09e7513d6e352e9/coverage-7.13.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:bd63e7b74661fed317212fab774e2a648bc4bb09b35f25474f8e3325d2945cd7", size = 259792, upload-time = "2025-12-28T15:42:44.818Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c3/528674d4623283310ad676c5af7414b9850ab6d55c2300e8aa4b945ec554/coverage-7.13.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:933082f161bbb3e9f90d00990dc956120f608cdbcaeea15c4d897f56ef4fe416", size = 262123, upload-time = "2025-12-28T15:42:47.108Z" }, - { url = "https://files.pythonhosted.org/packages/cc/48/d9f421cb8da5afaa1a64570d9989e00fb7955e6acddc5a12979f7666ef60/coverage-7.13.1-py3-none-any.whl", hash = "sha256:2016745cb3ba554469d02819d78958b571792bb68e31302610e898f80dd3a573", size = 210722, upload-time = "2025-12-28T15:42:54.901Z" }, +version = "7.13.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/56/95b7e30fa389756cb56630faa728da46a27b8c6eb46f9d557c68fff12b65/coverage-7.13.4.tar.gz", hash = "sha256:e5c8f6ed1e61a8b2dcdf31eb0b9bbf0130750ca79c1c49eb898e2ad86f5ccc91", size = 827239, upload-time = "2026-02-09T12:59:03.86Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/81/4ce2fdd909c5a0ed1f6dedb88aa57ab79b6d1fbd9b588c1ac7ef45659566/coverage-7.13.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:02231499b08dabbe2b96612993e5fc34217cdae907a51b906ac7fca8027a4459", size = 219449, upload-time = "2026-02-09T12:56:54.889Z" }, + { url = "https://files.pythonhosted.org/packages/5d/96/5238b1efc5922ddbdc9b0db9243152c09777804fb7c02ad1741eb18a11c0/coverage-7.13.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40aa8808140e55dc022b15d8aa7f651b6b3d68b365ea0398f1441e0b04d859c3", size = 219810, upload-time = "2026-02-09T12:56:56.33Z" }, + { url = "https://files.pythonhosted.org/packages/78/72/2f372b726d433c9c35e56377cf1d513b4c16fe51841060d826b95caacec1/coverage-7.13.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5b856a8ccf749480024ff3bd7310adaef57bf31fd17e1bfc404b7940b6986634", size = 251308, upload-time = "2026-02-09T12:56:57.858Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a0/2ea570925524ef4e00bb6c82649f5682a77fac5ab910a65c9284de422600/coverage-7.13.4-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c048ea43875fbf8b45d476ad79f179809c590ec7b79e2035c662e7afa3192e3", size = 254052, upload-time = "2026-02-09T12:56:59.754Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ac/45dc2e19a1939098d783c846e130b8f862fbb50d09e0af663988f2f21973/coverage-7.13.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b7b38448866e83176e28086674fe7368ab8590e4610fb662b44e345b86d63ffa", size = 255165, upload-time = "2026-02-09T12:57:01.287Z" }, + { url = "https://files.pythonhosted.org/packages/2d/4d/26d236ff35abc3b5e63540d3386e4c3b192168c1d96da5cb2f43c640970f/coverage-7.13.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:de6defc1c9badbf8b9e67ae90fd00519186d6ab64e5cc5f3d21359c2a9b2c1d3", size = 257432, upload-time = "2026-02-09T12:57:02.637Z" }, + { url = "https://files.pythonhosted.org/packages/ec/55/14a966c757d1348b2e19caf699415a2a4c4f7feaa4bbc6326a51f5c7dd1b/coverage-7.13.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7eda778067ad7ffccd23ecffce537dface96212576a07924cbf0d8799d2ded5a", size = 251716, upload-time = "2026-02-09T12:57:04.056Z" }, + { url = "https://files.pythonhosted.org/packages/77/33/50116647905837c66d28b2af1321b845d5f5d19be9655cb84d4a0ea806b4/coverage-7.13.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e87f6c587c3f34356c3759f0420693e35e7eb0e2e41e4c011cb6ec6ecbbf1db7", size = 253089, upload-time = "2026-02-09T12:57:05.503Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b4/8efb11a46e3665d92635a56e4f2d4529de6d33f2cb38afd47d779d15fc99/coverage-7.13.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8248977c2e33aecb2ced42fef99f2d319e9904a36e55a8a68b69207fb7e43edc", size = 251232, upload-time = "2026-02-09T12:57:06.879Z" }, + { url = "https://files.pythonhosted.org/packages/51/24/8cd73dd399b812cc76bb0ac260e671c4163093441847ffe058ac9fda1e32/coverage-7.13.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:25381386e80ae727608e662474db537d4df1ecd42379b5ba33c84633a2b36d47", size = 255299, upload-time = "2026-02-09T12:57:08.245Z" }, + { url = "https://files.pythonhosted.org/packages/03/94/0a4b12f1d0e029ce1ccc1c800944a9984cbe7d678e470bb6d3c6bc38a0da/coverage-7.13.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ee756f00726693e5ba94d6df2bdfd64d4852d23b09bb0bc700e3b30e6f333985", size = 250796, upload-time = "2026-02-09T12:57:10.142Z" }, + { url = "https://files.pythonhosted.org/packages/73/44/6002fbf88f6698ca034360ce474c406be6d5a985b3fdb3401128031eef6b/coverage-7.13.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fdfc1e28e7c7cdce44985b3043bc13bbd9c747520f94a4d7164af8260b3d91f0", size = 252673, upload-time = "2026-02-09T12:57:12.197Z" }, + { url = "https://files.pythonhosted.org/packages/db/23/aad45061a31677d68e47499197a131eea55da4875d16c1f42021ab963503/coverage-7.13.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b66a2da594b6068b48b2692f043f35d4d3693fb639d5ea8b39533c2ad9ac3ab9", size = 219474, upload-time = "2026-02-09T12:57:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/a5/70/9b8b67a0945f3dfec1fd896c5cefb7c19d5a3a6d74630b99a895170999ae/coverage-7.13.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3599eb3992d814d23b35c536c28df1a882caa950f8f507cef23d1cbf334995ac", size = 219844, upload-time = "2026-02-09T12:57:20.66Z" }, + { url = "https://files.pythonhosted.org/packages/97/fd/7e859f8fab324cef6c4ad7cff156ca7c489fef9179d5749b0c8d321281c2/coverage-7.13.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:93550784d9281e374fb5a12bf1324cc8a963fd63b2d2f223503ef0fd4aa339ea", size = 250832, upload-time = "2026-02-09T12:57:22.007Z" }, + { url = "https://files.pythonhosted.org/packages/e4/dc/b2442d10020c2f52617828862d8b6ee337859cd8f3a1f13d607dddda9cf7/coverage-7.13.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b720ce6a88a2755f7c697c23268ddc47a571b88052e6b155224347389fdf6a3b", size = 253434, upload-time = "2026-02-09T12:57:23.339Z" }, + { url = "https://files.pythonhosted.org/packages/5a/88/6728a7ad17428b18d836540630487231f5470fb82454871149502f5e5aa2/coverage-7.13.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7b322db1284a2ed3aa28ffd8ebe3db91c929b7a333c0820abec3d838ef5b3525", size = 254676, upload-time = "2026-02-09T12:57:24.774Z" }, + { url = "https://files.pythonhosted.org/packages/7c/bc/21244b1b8cedf0dff0a2b53b208015fe798d5f2a8d5348dbfece04224fff/coverage-7.13.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f4594c67d8a7c89cf922d9df0438c7c7bb022ad506eddb0fdb2863359ff78242", size = 256807, upload-time = "2026-02-09T12:57:26.125Z" }, + { url = "https://files.pythonhosted.org/packages/97/a0/ddba7ed3251cff51006737a727d84e05b61517d1784a9988a846ba508877/coverage-7.13.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:53d133df809c743eb8bce33b24bcababb371f4441340578cd406e084d94a6148", size = 251058, upload-time = "2026-02-09T12:57:27.614Z" }, + { url = "https://files.pythonhosted.org/packages/9b/55/e289addf7ff54d3a540526f33751951bf0878f3809b47f6dfb3def69c6f7/coverage-7.13.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76451d1978b95ba6507a039090ba076105c87cc76fc3efd5d35d72093964d49a", size = 252805, upload-time = "2026-02-09T12:57:29.066Z" }, + { url = "https://files.pythonhosted.org/packages/13/4e/cc276b1fa4a59be56d96f1dabddbdc30f4ba22e3b1cd42504c37b3313255/coverage-7.13.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7f57b33491e281e962021de110b451ab8a24182589be17e12a22c79047935e23", size = 250766, upload-time = "2026-02-09T12:57:30.522Z" }, + { url = "https://files.pythonhosted.org/packages/94/44/1093b8f93018f8b41a8cf29636c9292502f05e4a113d4d107d14a3acd044/coverage-7.13.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1731dc33dc276dafc410a885cbf5992f1ff171393e48a21453b78727d090de80", size = 254923, upload-time = "2026-02-09T12:57:31.946Z" }, + { url = "https://files.pythonhosted.org/packages/8b/55/ea2796da2d42257f37dbea1aab239ba9263b31bd91d5527cdd6db5efe174/coverage-7.13.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:bd60d4fe2f6fa7dff9223ca1bbc9f05d2b6697bc5961072e5d3b952d46e1b1ea", size = 250591, upload-time = "2026-02-09T12:57:33.842Z" }, + { url = "https://files.pythonhosted.org/packages/d4/fa/7c4bb72aacf8af5020675aa633e59c1fbe296d22aed191b6a5b711eb2bc7/coverage-7.13.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9181a3ccead280b828fae232df12b16652702b49d41e99d657f46cc7b1f6ec7a", size = 252364, upload-time = "2026-02-09T12:57:35.743Z" }, + { url = "https://files.pythonhosted.org/packages/52/57/ee93ced533bcb3e6df961c0c6e42da2fc6addae53fb95b94a89b1e33ebd7/coverage-7.13.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:40d74da8e6c4b9ac18b15331c4b5ebc35a17069410cad462ad4f40dcd2d50c0d", size = 220165, upload-time = "2026-02-09T12:57:41.639Z" }, + { url = "https://files.pythonhosted.org/packages/c5/e0/969fc285a6fbdda49d91af278488d904dcd7651b2693872f0ff94e40e84a/coverage-7.13.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4223b4230a376138939a9173f1bdd6521994f2aff8047fae100d6d94d50c5a12", size = 220516, upload-time = "2026-02-09T12:57:44.215Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b8/9531944e16267e2735a30a9641ff49671f07e8138ecf1ca13db9fd2560c7/coverage-7.13.4-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1d4be36a5114c499f9f1f9195e95ebf979460dbe2d88e6816ea202010ba1c34b", size = 261804, upload-time = "2026-02-09T12:57:45.989Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f3/e63df6d500314a2a60390d1989240d5f27318a7a68fa30ad3806e2a9323e/coverage-7.13.4-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:200dea7d1e8095cc6e98cdabe3fd1d21ab17d3cee6dab00cadbb2fe35d9c15b9", size = 263885, upload-time = "2026-02-09T12:57:47.42Z" }, + { url = "https://files.pythonhosted.org/packages/f3/67/7654810de580e14b37670b60a09c599fa348e48312db5b216d730857ffe6/coverage-7.13.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8eb931ee8e6d8243e253e5ed7336deea6904369d2fd8ae6e43f68abbf167092", size = 266308, upload-time = "2026-02-09T12:57:49.345Z" }, + { url = "https://files.pythonhosted.org/packages/37/6f/39d41eca0eab3cc82115953ad41c4e77935286c930e8fad15eaed1389d83/coverage-7.13.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:75eab1ebe4f2f64d9509b984f9314d4aa788540368218b858dad56dc8f3e5eb9", size = 267452, upload-time = "2026-02-09T12:57:50.811Z" }, + { url = "https://files.pythonhosted.org/packages/50/6d/39c0fbb8fc5cd4d2090811e553c2108cf5112e882f82505ee7495349a6bf/coverage-7.13.4-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c35eb28c1d085eb7d8c9b3296567a1bebe03ce72962e932431b9a61f28facf26", size = 261057, upload-time = "2026-02-09T12:57:52.447Z" }, + { url = "https://files.pythonhosted.org/packages/a4/a2/60010c669df5fa603bb5a97fb75407e191a846510da70ac657eb696b7fce/coverage-7.13.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb88b316ec33760714a4720feb2816a3a59180fd58c1985012054fa7aebee4c2", size = 263875, upload-time = "2026-02-09T12:57:53.938Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d9/63b22a6bdbd17f1f96e9ed58604c2a6b0e72a9133e37d663bef185877cf6/coverage-7.13.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7d41eead3cc673cbd38a4417deb7fd0b4ca26954ff7dc6078e33f6ff97bed940", size = 261500, upload-time = "2026-02-09T12:57:56.012Z" }, + { url = "https://files.pythonhosted.org/packages/70/bf/69f86ba1ad85bc3ad240e4c0e57a2e620fbc0e1645a47b5c62f0e941ad7f/coverage-7.13.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:fb26a934946a6afe0e326aebe0730cdff393a8bc0bbb65a2f41e30feddca399c", size = 265212, upload-time = "2026-02-09T12:57:57.5Z" }, + { url = "https://files.pythonhosted.org/packages/ae/f2/5f65a278a8c2148731831574c73e42f57204243d33bedaaf18fa79c5958f/coverage-7.13.4-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:dae88bc0fc77edaa65c14be099bd57ee140cf507e6bfdeea7938457ab387efb0", size = 260398, upload-time = "2026-02-09T12:57:59.027Z" }, + { url = "https://files.pythonhosted.org/packages/ef/80/6e8280a350ee9fea92f14b8357448a242dcaa243cb2c72ab0ca591f66c8c/coverage-7.13.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:845f352911777a8e722bfce168958214951e07e47e5d5d9744109fa5fe77f79b", size = 262584, upload-time = "2026-02-09T12:58:01.129Z" }, + { url = "https://files.pythonhosted.org/packages/92/11/a9cf762bb83386467737d32187756a42094927150c3e107df4cb078e8590/coverage-7.13.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:300deaee342f90696ed186e3a00c71b5b3d27bffe9e827677954f4ee56969601", size = 219522, upload-time = "2026-02-09T12:58:08.623Z" }, + { url = "https://files.pythonhosted.org/packages/d3/28/56e6d892b7b052236d67c95f1936b6a7cf7c3e2634bf27610b8cbd7f9c60/coverage-7.13.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:29e3220258d682b6226a9b0925bc563ed9a1ebcff3cad30f043eceea7eaf2689", size = 219855, upload-time = "2026-02-09T12:58:10.176Z" }, + { url = "https://files.pythonhosted.org/packages/e5/69/233459ee9eb0c0d10fcc2fe425a029b3fa5ce0f040c966ebce851d030c70/coverage-7.13.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:391ee8f19bef69210978363ca930f7328081c6a0152f1166c91f0b5fdd2a773c", size = 250887, upload-time = "2026-02-09T12:58:12.503Z" }, + { url = "https://files.pythonhosted.org/packages/06/90/2cdab0974b9b5bbc1623f7876b73603aecac11b8d95b85b5b86b32de5eab/coverage-7.13.4-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0dd7ab8278f0d58a0128ba2fca25824321f05d059c1441800e934ff2efa52129", size = 253396, upload-time = "2026-02-09T12:58:14.615Z" }, + { url = "https://files.pythonhosted.org/packages/ac/15/ea4da0f85bf7d7b27635039e649e99deb8173fe551096ea15017f7053537/coverage-7.13.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78cdf0d578b15148b009ccf18c686aa4f719d887e76e6b40c38ffb61d264a552", size = 254745, upload-time = "2026-02-09T12:58:16.162Z" }, + { url = "https://files.pythonhosted.org/packages/99/11/bb356e86920c655ca4d61daee4e2bbc7258f0a37de0be32d233b561134ff/coverage-7.13.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:48685fee12c2eb3b27c62f2658e7ea21e9c3239cba5a8a242801a0a3f6a8c62a", size = 257055, upload-time = "2026-02-09T12:58:17.892Z" }, + { url = "https://files.pythonhosted.org/packages/c9/0f/9ae1f8cb17029e09da06ca4e28c9e1d5c1c0a511c7074592e37e0836c915/coverage-7.13.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4e83efc079eb39480e6346a15a1bcb3e9b04759c5202d157e1dd4303cd619356", size = 250911, upload-time = "2026-02-09T12:58:19.495Z" }, + { url = "https://files.pythonhosted.org/packages/89/3a/adfb68558fa815cbc29747b553bc833d2150228f251b127f1ce97e48547c/coverage-7.13.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ecae9737b72408d6a950f7e525f30aca12d4bd8dd95e37342e5beb3a2a8c4f71", size = 252754, upload-time = "2026-02-09T12:58:21.064Z" }, + { url = "https://files.pythonhosted.org/packages/32/b1/540d0c27c4e748bd3cd0bd001076ee416eda993c2bae47a73b7cc9357931/coverage-7.13.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ae4578f8528569d3cf303fef2ea569c7f4c4059a38c8667ccef15c6e1f118aa5", size = 250720, upload-time = "2026-02-09T12:58:22.622Z" }, + { url = "https://files.pythonhosted.org/packages/c7/95/383609462b3ffb1fe133014a7c84fc0dd01ed55ac6140fa1093b5af7ebb1/coverage-7.13.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:6fdef321fdfbb30a197efa02d48fcd9981f0d8ad2ae8903ac318adc653f5df98", size = 254994, upload-time = "2026-02-09T12:58:24.548Z" }, + { url = "https://files.pythonhosted.org/packages/f7/ba/1761138e86c81680bfc3c49579d66312865457f9fe405b033184e5793cb3/coverage-7.13.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b0f6ccf3dbe577170bebfce1318707d0e8c3650003cb4b3a9dd744575daa8b5", size = 250531, upload-time = "2026-02-09T12:58:26.271Z" }, + { url = "https://files.pythonhosted.org/packages/f8/8e/05900df797a9c11837ab59c4d6fe94094e029582aab75c3309a93e6fb4e3/coverage-7.13.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75fcd519f2a5765db3f0e391eb3b7d150cce1a771bf4c9f861aeab86c767a3c0", size = 252189, upload-time = "2026-02-09T12:58:27.807Z" }, + { url = "https://files.pythonhosted.org/packages/a7/e4/c884a405d6ead1370433dad1e3720216b4f9fd8ef5b64bfd984a2a60a11a/coverage-7.13.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:85480adfb35ffc32d40918aad81b89c69c9cc5661a9b8a81476d3e645321a056", size = 220246, upload-time = "2026-02-09T12:58:34.181Z" }, + { url = "https://files.pythonhosted.org/packages/81/5c/4d7ed8b23b233b0fffbc9dfec53c232be2e695468523242ea9fd30f97ad2/coverage-7.13.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:79be69cf7f3bf9b0deeeb062eab7ac7f36cd4cc4c4dd694bd28921ba4d8596cc", size = 220514, upload-time = "2026-02-09T12:58:35.704Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6f/3284d4203fd2f28edd73034968398cd2d4cb04ab192abc8cff007ea35679/coverage-7.13.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:caa421e2684e382c5d8973ac55e4f36bed6821a9bad5c953494de960c74595c9", size = 261877, upload-time = "2026-02-09T12:58:37.864Z" }, + { url = "https://files.pythonhosted.org/packages/09/aa/b672a647bbe1556a85337dc95bfd40d146e9965ead9cc2fe81bde1e5cbce/coverage-7.13.4-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:14375934243ee05f56c45393fe2ce81fe5cc503c07cee2bdf1725fb8bef3ffaf", size = 264004, upload-time = "2026-02-09T12:58:39.492Z" }, + { url = "https://files.pythonhosted.org/packages/79/a1/aa384dbe9181f98bba87dd23dda436f0c6cf2e148aecbb4e50fc51c1a656/coverage-7.13.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25a41c3104d08edb094d9db0d905ca54d0cd41c928bb6be3c4c799a54753af55", size = 266408, upload-time = "2026-02-09T12:58:41.852Z" }, + { url = "https://files.pythonhosted.org/packages/53/5e/5150bf17b4019bc600799f376bb9606941e55bd5a775dc1e096b6ffea952/coverage-7.13.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f01afcff62bf9a08fb32b2c1d6e924236c0383c02c790732b6537269e466a72", size = 267544, upload-time = "2026-02-09T12:58:44.093Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ed/f1de5c675987a4a7a672250d2c5c9d73d289dbf13410f00ed7181d8017dd/coverage-7.13.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eb9078108fbf0bcdde37c3f4779303673c2fa1fe8f7956e68d447d0dd426d38a", size = 260980, upload-time = "2026-02-09T12:58:45.721Z" }, + { url = "https://files.pythonhosted.org/packages/b3/e3/fe758d01850aa172419a6743fe76ba8b92c29d181d4f676ffe2dae2ba631/coverage-7.13.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e086334e8537ddd17e5f16a344777c1ab8194986ec533711cbe6c41cde841b6", size = 263871, upload-time = "2026-02-09T12:58:47.334Z" }, + { url = "https://files.pythonhosted.org/packages/b6/76/b829869d464115e22499541def9796b25312b8cf235d3bb00b39f1675395/coverage-7.13.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:725d985c5ab621268b2edb8e50dfe57633dc69bda071abc470fed55a14935fd3", size = 261472, upload-time = "2026-02-09T12:58:48.995Z" }, + { url = "https://files.pythonhosted.org/packages/14/9e/caedb1679e73e2f6ad240173f55218488bfe043e38da577c4ec977489915/coverage-7.13.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:3c06f0f1337c667b971ca2f975523347e63ec5e500b9aa5882d91931cd3ef750", size = 265210, upload-time = "2026-02-09T12:58:51.178Z" }, + { url = "https://files.pythonhosted.org/packages/3a/10/0dd02cb009b16ede425b49ec344aba13a6ae1dc39600840ea6abcb085ac4/coverage-7.13.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:590c0ed4bf8e85f745e6b805b2e1c457b2e33d5255dd9729743165253bc9ad39", size = 260319, upload-time = "2026-02-09T12:58:53.081Z" }, + { url = "https://files.pythonhosted.org/packages/92/8e/234d2c927af27c6d7a5ffad5bd2cf31634c46a477b4c7adfbfa66baf7ebb/coverage-7.13.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:eb30bf180de3f632cd043322dad5751390e5385108b2807368997d1a92a509d0", size = 262638, upload-time = "2026-02-09T12:58:55.258Z" }, + { url = "https://files.pythonhosted.org/packages/0d/4a/331fe2caf6799d591109bb9c08083080f6de90a823695d412a935622abb2/coverage-7.13.4-py3-none-any.whl", hash = "sha256:1af1641e57cf7ba1bd67d677c9abdbcd6cc2ab7da3bca7fa1e2b7e50e65f2ad0", size = 211242, upload-time = "2026-02-09T12:59:02.032Z" }, ] [[package]] name = "dill" -version = "0.4.0" +version = "0.4.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", size = 186976, upload-time = "2025-04-16T00:41:48.867Z" } +sdist = { url = "https://files.pythonhosted.org/packages/81/e1/56027a71e31b02ddc53c7d65b01e68edf64dea2932122fe7746a516f75d5/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa", size = 187315, upload-time = "2026-01-19T02:36:56.85Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668, upload-time = "2025-04-16T00:41:47.671Z" }, + { url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019, upload-time = "2026-01-19T02:36:55.663Z" }, ] [[package]] @@ -247,20 +257,20 @@ wheels = [ [[package]] name = "filelock" -version = "3.20.2" +version = "3.25.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c1/e0/a75dbe4bca1e7d41307323dad5ea2efdd95408f74ab2de8bd7dba9b51a1a/filelock-3.20.2.tar.gz", hash = "sha256:a2241ff4ddde2a7cebddf78e39832509cb045d18ec1a09d7248d6bfc6bfbbe64", size = 19510, upload-time = "2026-01-02T15:33:32.582Z" } +sdist = { url = "https://files.pythonhosted.org/packages/77/18/a1fd2231c679dcb9726204645721b12498aeac28e1ad0601038f94b42556/filelock-3.25.0.tar.gz", hash = "sha256:8f00faf3abf9dc730a1ffe9c354ae5c04e079ab7d3a683b7c32da5dd05f26af3", size = 40158, upload-time = "2026-03-01T15:08:45.916Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl", hash = "sha256:fbba7237d6ea277175a32c54bb71ef814a8546d8601269e1bfc388de333974e8", size = 16697, upload-time = "2026-01-02T15:33:31.133Z" }, + { url = "https://files.pythonhosted.org/packages/f9/0b/de6f54d4a8bedfe8645c41497f3c18d749f0bd3218170c667bf4b81d0cdd/filelock-3.25.0-py3-none-any.whl", hash = "sha256:5ccf8069f7948f494968fc0713c10e5c182a9c9d9eef3a636307a20c2490f047", size = 26427, upload-time = "2026-03-01T15:08:44.593Z" }, ] [[package]] name = "identify" -version = "2.6.15" +version = "2.6.16" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf", size = 99311, upload-time = "2025-10-02T17:43:40.631Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/8d/e8b97e6bd3fb6fb271346f7981362f1e04d6a7463abd0de79e1fda17c067/identify-2.6.16.tar.gz", hash = "sha256:846857203b5511bbe94d5a352a48ef2359532bc8f6727b5544077a0dcfb24980", size = 99360, upload-time = "2026-01-12T18:58:58.201Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183, upload-time = "2025-10-02T17:43:39.137Z" }, + { url = "https://files.pythonhosted.org/packages/b8/58/40fbbcefeda82364720eba5cf2270f98496bdfa19ea75b4cccae79c698e6/identify-2.6.16-py2.py3-none-any.whl", hash = "sha256:391ee4d77741d994189522896270b787aed8670389bfd60f326d677d64a6dfb0", size = 99202, upload-time = "2026-01-12T18:58:56.627Z" }, ] [[package]] @@ -283,11 +293,11 @@ wheels = [ [[package]] name = "isort" -version = "7.0.0" +version = "8.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/63/53/4f3c058e3bace40282876f9b553343376ee687f3c35a525dc79dbd450f88/isort-7.0.0.tar.gz", hash = "sha256:5513527951aadb3ac4292a41a16cbc50dd1642432f5e8c20057d414bdafb4187", size = 805049, upload-time = "2025-10-11T13:30:59.107Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ef/7c/ec4ab396d31b3b395e2e999c8f46dec78c5e29209fac49d1f4dace04041d/isort-8.0.1.tar.gz", hash = "sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d", size = 769592, upload-time = "2026-02-28T10:08:20.685Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/ed/e3705d6d02b4f7aea715a353c8ce193efd0b5db13e204df895d38734c244/isort-7.0.0-py3-none-any.whl", hash = "sha256:1bcabac8bc3c36c7fb7b98a76c8abb18e0f841a3ba81decac7691008592499c1", size = 94672, upload-time = "2025-10-11T13:30:57.665Z" }, + { url = "https://files.pythonhosted.org/packages/3e/95/c7c34aa53c16353c56d0b802fba48d5f5caa2cdee7958acbcb795c830416/isort-8.0.1-py3-none-any.whl", hash = "sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75", size = 89733, upload-time = "2026-02-28T10:08:19.466Z" }, ] [[package]] @@ -400,11 +410,11 @@ wheels = [ [[package]] name = "packaging" -version = "25.0" +version = "26.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, ] [[package]] @@ -424,20 +434,20 @@ wheels = [ [[package]] name = "pip" -version = "25.3" +version = "26.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/6e/74a3f0179a4a73a53d66ce57fdb4de0080a8baa1de0063de206d6167acc2/pip-25.3.tar.gz", hash = "sha256:8d0538dbbd7babbd207f261ed969c65de439f6bc9e5dbd3b3b9a77f25d95f343", size = 1803014, upload-time = "2025-10-25T00:55:41.394Z" } +sdist = { url = "https://files.pythonhosted.org/packages/48/83/0d7d4e9efe3344b8e2fe25d93be44f64b65364d3c8d7bc6dc90198d5422e/pip-26.0.1.tar.gz", hash = "sha256:c4037d8a277c89b320abe636d59f91e6d0922d08a05b60e85e53b296613346d8", size = 1812747, upload-time = "2026-02-05T02:20:18.702Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl", hash = "sha256:9655943313a94722b7774661c21049070f6bbb0a1516bf02f7c8d5d9201514cd", size = 1778622, upload-time = "2025-10-25T00:55:39.247Z" }, + { url = "https://files.pythonhosted.org/packages/de/f0/c81e05b613866b76d2d1066490adf1a3dbc4ee9d9c839961c3fc8a6997af/pip-26.0.1-py3-none-any.whl", hash = "sha256:bdb1b08f4274833d62c1aa29e20907365a2ceb950410df15fc9521bad440122b", size = 1787723, upload-time = "2026-02-05T02:20:16.416Z" }, ] [[package]] name = "platformdirs" -version = "4.5.1" +version = "4.9.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/04/fea538adf7dbbd6d186f551d595961e564a3b6715bdf276b477460858672/platformdirs-4.9.2.tar.gz", hash = "sha256:9a33809944b9db043ad67ca0db94b14bf452cc6aeaac46a88ea55b26e2e9d291", size = 28394, upload-time = "2026-02-16T03:56:10.574Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, + { url = "https://files.pythonhosted.org/packages/48/31/05e764397056194206169869b50cf2fee4dbbbc71b344705b9c0d878d4d8/platformdirs-4.9.2-py3-none-any.whl", hash = "sha256:9170634f126f8efdae22fb58ae8a0eaa86f38365bc57897a6c4f781d1f5875bd", size = 21168, upload-time = "2026-02-16T03:56:08.891Z" }, ] [[package]] @@ -556,16 +566,16 @@ wheels = [ [[package]] name = "pyjwt" -version = "2.10.1" +version = "2.11.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785, upload-time = "2024-11-28T03:43:29.933Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" }, + { url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" }, ] [[package]] name = "pylint" -version = "4.0.4" +version = "4.0.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "astroid", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, @@ -575,22 +585,22 @@ dependencies = [ { name = "platformdirs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "tomlkit", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5a/d2/b081da1a8930d00e3fc06352a1d449aaf815d4982319fab5d8cdb2e9ab35/pylint-4.0.4.tar.gz", hash = "sha256:d9b71674e19b1c36d79265b5887bf8e55278cbe236c9e95d22dc82cf044fdbd2", size = 1571735, upload-time = "2025-11-30T13:29:04.315Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/b6/74d9a8a68b8067efce8d07707fe6a236324ee1e7808d2eb3646ec8517c7d/pylint-4.0.5.tar.gz", hash = "sha256:8cd6a618df75deb013bd7eb98327a95f02a6fb839205a6bbf5456ef96afb317c", size = 1572474, upload-time = "2026-02-20T09:07:33.621Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/92/d40f5d937517cc489ad848fc4414ecccc7592e4686b9071e09e64f5e378e/pylint-4.0.4-py3-none-any.whl", hash = "sha256:63e06a37d5922555ee2c20963eb42559918c20bd2b21244e4ef426e7c43b92e0", size = 536425, upload-time = "2025-11-30T13:29:02.53Z" }, + { url = "https://files.pythonhosted.org/packages/d5/6f/9ac2548e290764781f9e7e2aaf0685b086379dabfb29ca38536985471eaf/pylint-4.0.5-py3-none-any.whl", hash = "sha256:00f51c9b14a3b3ae08cff6b2cdd43f28165c78b165b628692e428fb1f8dc2cf2", size = 536694, upload-time = "2026-02-20T09:07:31.028Z" }, ] [[package]] name = "pyright" -version = "1.1.407" +version = "1.1.408" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nodeenv", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/1b/0aa08ee42948b61745ac5b5b5ccaec4669e8884b53d31c8ec20b2fcd6b6f/pyright-1.1.407.tar.gz", hash = "sha256:099674dba5c10489832d4a4b2d302636152a9a42d317986c38474c76fe562262", size = 4122872, upload-time = "2025-10-24T23:17:15.145Z" } +sdist = { url = "https://files.pythonhosted.org/packages/74/b2/5db700e52554b8f025faa9c3c624c59f1f6c8841ba81ab97641b54322f16/pyright-1.1.408.tar.gz", hash = "sha256:f28f2321f96852fa50b5829ea492f6adb0e6954568d1caa3f3af3a5f555eb684", size = 4400578, upload-time = "2026-01-08T08:07:38.795Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/93/b69052907d032b00c40cb656d21438ec00b3a471733de137a3f65a49a0a0/pyright-1.1.407-py3-none-any.whl", hash = "sha256:6dd419f54fcc13f03b52285796d65e639786373f433e243f8b94cf93a7444d21", size = 5997008, upload-time = "2025-10-24T23:17:13.159Z" }, + { url = "https://files.pythonhosted.org/packages/0c/82/a2c93e32800940d9573fb28c346772a14778b84ba7524e691b324620ab89/pyright-1.1.408-py3-none-any.whl", hash = "sha256:090b32865f4fdb1e0e6cd82bf5618480d48eecd2eb2e70f960982a3d9a4c17c1", size = 6399144, upload-time = "2026-01-08T08:07:37.082Z" }, ] [[package]] @@ -634,6 +644,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, ] +[[package]] +name = "python-discovery" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "platformdirs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/bb/93a3e83bdf9322c7e21cafd092e56a4a17c4d8ef4277b6eb01af1a540a6f/python_discovery-1.1.0.tar.gz", hash = "sha256:447941ba1aed8cc2ab7ee3cb91be5fc137c5bdbb05b7e6ea62fbdcb66e50b268", size = 55674, upload-time = "2026-02-26T09:42:49.668Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/54/82a6e2ef37f0f23dccac604b9585bdcbd0698604feb64807dcb72853693e/python_discovery-1.1.0-py3-none-any.whl", hash = "sha256:a162893b8809727f54594a99ad2179d2ede4bf953e12d4c7abc3cc9cdbd1437b", size = 30687, upload-time = "2026-02-26T09:42:48.548Z" }, +] + [[package]] name = "pyyaml" version = "6.0.3" @@ -687,38 +710,37 @@ wheels = [ [[package]] name = "rich" -version = "14.2.0" +version = "14.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "pygments", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" }, + { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, ] [[package]] name = "ruff" -version = "0.14.10" +version = "0.15.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/08/52232a877978dd8f9cf2aeddce3e611b40a63287dfca29b6b8da791f5e8d/ruff-0.14.10.tar.gz", hash = "sha256:9a2e830f075d1a42cd28420d7809ace390832a490ed0966fe373ba288e77aaf4", size = 5859763, upload-time = "2025-12-18T19:28:57.98Z" } +sdist = { url = "https://files.pythonhosted.org/packages/da/31/d6e536cdebb6568ae75a7f00e4b4819ae0ad2640c3604c305a0428680b0c/ruff-0.15.4.tar.gz", hash = "sha256:3412195319e42d634470cc97aa9803d07e9d5c9223b99bcb1518f0c725f26ae1", size = 4569550, upload-time = "2026-02-26T20:04:14.959Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/60/01/933704d69f3f05ee16ef11406b78881733c186fe14b6a46b05cfcaf6d3b2/ruff-0.14.10-py3-none-linux_armv6l.whl", hash = "sha256:7a3ce585f2ade3e1f29ec1b92df13e3da262178df8c8bdf876f48fa0e8316c49", size = 13527080, upload-time = "2025-12-18T19:29:25.642Z" }, - { url = "https://files.pythonhosted.org/packages/df/58/a0349197a7dfa603ffb7f5b0470391efa79ddc327c1e29c4851e85b09cc5/ruff-0.14.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:674f9be9372907f7257c51f1d4fc902cb7cf014b9980152b802794317941f08f", size = 13797320, upload-time = "2025-12-18T19:29:02.571Z" }, - { url = "https://files.pythonhosted.org/packages/7b/82/36be59f00a6082e38c23536df4e71cdbc6af8d7c707eade97fcad5c98235/ruff-0.14.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d85713d522348837ef9df8efca33ccb8bd6fcfc86a2cde3ccb4bc9d28a18003d", size = 12918434, upload-time = "2025-12-18T19:28:51.202Z" }, - { url = "https://files.pythonhosted.org/packages/a6/00/45c62a7f7e34da92a25804f813ebe05c88aa9e0c25e5cb5a7d23dd7450e3/ruff-0.14.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6987ebe0501ae4f4308d7d24e2d0fe3d7a98430f5adfd0f1fead050a740a3a77", size = 13371961, upload-time = "2025-12-18T19:29:04.991Z" }, - { url = "https://files.pythonhosted.org/packages/40/31/a5906d60f0405f7e57045a70f2d57084a93ca7425f22e1d66904769d1628/ruff-0.14.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:16a01dfb7b9e4eee556fbfd5392806b1b8550c9b4a9f6acd3dbe6812b193c70a", size = 13275629, upload-time = "2025-12-18T19:29:21.381Z" }, - { url = "https://files.pythonhosted.org/packages/3e/60/61c0087df21894cf9d928dc04bcd4fb10e8b2e8dca7b1a276ba2155b2002/ruff-0.14.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7165d31a925b7a294465fa81be8c12a0e9b60fb02bf177e79067c867e71f8b1f", size = 14029234, upload-time = "2025-12-18T19:29:00.132Z" }, - { url = "https://files.pythonhosted.org/packages/44/84/77d911bee3b92348b6e5dab5a0c898d87084ea03ac5dc708f46d88407def/ruff-0.14.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c561695675b972effb0c0a45db233f2c816ff3da8dcfbe7dfc7eed625f218935", size = 15449890, upload-time = "2025-12-18T19:28:53.573Z" }, - { url = "https://files.pythonhosted.org/packages/e9/36/480206eaefa24a7ec321582dda580443a8f0671fdbf6b1c80e9c3e93a16a/ruff-0.14.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bb98fcbbc61725968893682fd4df8966a34611239c9fd07a1f6a07e7103d08e", size = 15123172, upload-time = "2025-12-18T19:29:23.453Z" }, - { url = "https://files.pythonhosted.org/packages/5c/38/68e414156015ba80cef5473d57919d27dfb62ec804b96180bafdeaf0e090/ruff-0.14.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f24b47993a9d8cb858429e97bdf8544c78029f09b520af615c1d261bf827001d", size = 14460260, upload-time = "2025-12-18T19:29:27.808Z" }, - { url = "https://files.pythonhosted.org/packages/b3/19/9e050c0dca8aba824d67cc0db69fb459c28d8cd3f6855b1405b3f29cc91d/ruff-0.14.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59aabd2e2c4fd614d2862e7939c34a532c04f1084476d6833dddef4afab87e9f", size = 14229978, upload-time = "2025-12-18T19:29:11.32Z" }, - { url = "https://files.pythonhosted.org/packages/51/eb/e8dd1dd6e05b9e695aa9dd420f4577debdd0f87a5ff2fedda33c09e9be8c/ruff-0.14.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:213db2b2e44be8625002dbea33bb9c60c66ea2c07c084a00d55732689d697a7f", size = 14338036, upload-time = "2025-12-18T19:29:09.184Z" }, - { url = "https://files.pythonhosted.org/packages/6a/12/f3e3a505db7c19303b70af370d137795fcfec136d670d5de5391e295c134/ruff-0.14.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b914c40ab64865a17a9a5b67911d14df72346a634527240039eb3bd650e5979d", size = 13264051, upload-time = "2025-12-18T19:29:13.431Z" }, - { url = "https://files.pythonhosted.org/packages/08/64/8c3a47eaccfef8ac20e0484e68e0772013eb85802f8a9f7603ca751eb166/ruff-0.14.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1484983559f026788e3a5c07c81ef7d1e97c1c78ed03041a18f75df104c45405", size = 13283998, upload-time = "2025-12-18T19:29:06.994Z" }, - { url = "https://files.pythonhosted.org/packages/12/84/534a5506f4074e5cc0529e5cd96cfc01bb480e460c7edf5af70d2bcae55e/ruff-0.14.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c70427132db492d25f982fffc8d6c7535cc2fd2c83fc8888f05caaa248521e60", size = 13601891, upload-time = "2025-12-18T19:28:55.811Z" }, - { url = "https://files.pythonhosted.org/packages/0d/1e/14c916087d8598917dbad9b2921d340f7884824ad6e9c55de948a93b106d/ruff-0.14.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5bcf45b681e9f1ee6445d317ce1fa9d6cba9a6049542d1c3d5b5958986be8830", size = 14336660, upload-time = "2025-12-18T19:29:16.531Z" }, + { url = "https://files.pythonhosted.org/packages/f2/82/c11a03cfec3a4d26a0ea1e571f0f44be5993b923f905eeddfc397c13d360/ruff-0.15.4-py3-none-linux_armv6l.whl", hash = "sha256:a1810931c41606c686bae8b5b9a8072adac2f611bb433c0ba476acba17a332e0", size = 10453333, upload-time = "2026-02-26T20:04:20.093Z" }, + { url = "https://files.pythonhosted.org/packages/ce/5d/6a1f271f6e31dffb31855996493641edc3eef8077b883eaf007a2f1c2976/ruff-0.15.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5a1632c66672b8b4d3e1d1782859e98d6e0b4e70829530666644286600a33992", size = 10853356, upload-time = "2026-02-26T20:04:05.808Z" }, + { url = "https://files.pythonhosted.org/packages/b1/d8/0fab9f8842b83b1a9c2bf81b85063f65e93fb512e60effa95b0be49bfc54/ruff-0.15.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a4386ba2cd6c0f4ff75252845906acc7c7c8e1ac567b7bc3d373686ac8c222ba", size = 10187434, upload-time = "2026-02-26T20:03:54.656Z" }, + { url = "https://files.pythonhosted.org/packages/85/cc/cc220fd9394eff5db8d94dec199eec56dd6c9f3651d8869d024867a91030/ruff-0.15.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2496488bdfd3732747558b6f95ae427ff066d1fcd054daf75f5a50674411e75", size = 10535456, upload-time = "2026-02-26T20:03:52.738Z" }, + { url = "https://files.pythonhosted.org/packages/fa/0f/bced38fa5cf24373ec767713c8e4cadc90247f3863605fb030e597878661/ruff-0.15.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3f1c4893841ff2d54cbda1b2860fa3260173df5ddd7b95d370186f8a5e66a4ac", size = 10287772, upload-time = "2026-02-26T20:04:08.138Z" }, + { url = "https://files.pythonhosted.org/packages/2b/90/58a1802d84fed15f8f281925b21ab3cecd813bde52a8ca033a4de8ab0e7a/ruff-0.15.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:820b8766bd65503b6c30aaa6331e8ef3a6e564f7999c844e9a547c40179e440a", size = 11049051, upload-time = "2026-02-26T20:04:03.53Z" }, + { url = "https://files.pythonhosted.org/packages/d2/ac/b7ad36703c35f3866584564dc15f12f91cb1a26a897dc2fd13d7cb3ae1af/ruff-0.15.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9fb74bab47139c1751f900f857fa503987253c3ef89129b24ed375e72873e85", size = 11890494, upload-time = "2026-02-26T20:04:10.497Z" }, + { url = "https://files.pythonhosted.org/packages/93/3d/3eb2f47a39a8b0da99faf9c54d3eb24720add1e886a5309d4d1be73a6380/ruff-0.15.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f80c98765949c518142b3a50a5db89343aa90f2c2bf7799de9986498ae6176db", size = 11326221, upload-time = "2026-02-26T20:04:12.84Z" }, + { url = "https://files.pythonhosted.org/packages/ff/90/bf134f4c1e5243e62690e09d63c55df948a74084c8ac3e48a88468314da6/ruff-0.15.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:451a2e224151729b3b6c9ffb36aed9091b2996fe4bdbd11f47e27d8f2e8888ec", size = 11168459, upload-time = "2026-02-26T20:04:00.969Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e5/a64d27688789b06b5d55162aafc32059bb8c989c61a5139a36e1368285eb/ruff-0.15.4-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a8f157f2e583c513c4f5f896163a93198297371f34c04220daf40d133fdd4f7f", size = 11104366, upload-time = "2026-02-26T20:03:48.099Z" }, + { url = "https://files.pythonhosted.org/packages/f1/f6/32d1dcb66a2559763fc3027bdd65836cad9eb09d90f2ed6a63d8e9252b02/ruff-0.15.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:917cc68503357021f541e69b35361c99387cdbbf99bd0ea4aa6f28ca99ff5338", size = 10510887, upload-time = "2026-02-26T20:03:45.771Z" }, + { url = "https://files.pythonhosted.org/packages/ff/92/22d1ced50971c5b6433aed166fcef8c9343f567a94cf2b9d9089f6aa80fe/ruff-0.15.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e9737c8161da79fd7cfec19f1e35620375bd8b2a50c3e77fa3d2c16f574105cc", size = 10285939, upload-time = "2026-02-26T20:04:22.42Z" }, + { url = "https://files.pythonhosted.org/packages/e6/f4/7c20aec3143837641a02509a4668fb146a642fd1211846634edc17eb5563/ruff-0.15.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:291258c917539e18f6ba40482fe31d6f5ac023994ee11d7bdafd716f2aab8a68", size = 10765471, upload-time = "2026-02-26T20:03:58.924Z" }, + { url = "https://files.pythonhosted.org/packages/d0/09/6d2f7586f09a16120aebdff8f64d962d7c4348313c77ebb29c566cefc357/ruff-0.15.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3f83c45911da6f2cd5936c436cf86b9f09f09165f033a99dcf7477e34041cbc3", size = 11263382, upload-time = "2026-02-26T20:04:24.424Z" }, ] [[package]] @@ -732,29 +754,29 @@ wheels = [ [[package]] name = "stevedore" -version = "5.6.0" +version = "5.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/5b/496f8abebd10c3301129abba7ddafd46c71d799a70c44ab080323987c4c9/stevedore-5.6.0.tar.gz", hash = "sha256:f22d15c6ead40c5bbfa9ca54aa7e7b4a07d59b36ae03ed12ced1a54cf0b51945", size = 516074, upload-time = "2025-11-20T10:06:07.264Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6d/90764092216fa560f6587f83bb70113a8ba510ba436c6476a2b47359057c/stevedore-5.7.0.tar.gz", hash = "sha256:31dd6fe6b3cbe921e21dcefabc9a5f1cf848cf538a1f27543721b8ca09948aa3", size = 516200, upload-time = "2026-02-20T13:27:06.765Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/40/8561ce06dc46fd17242c7724ab25b257a2ac1b35f4ebf551b40ce6105cfa/stevedore-5.6.0-py3-none-any.whl", hash = "sha256:4a36dccefd7aeea0c70135526cecb7766c4c84c473b1af68db23d541b6dc1820", size = 54428, upload-time = "2025-11-20T10:06:05.946Z" }, + { url = "https://files.pythonhosted.org/packages/69/06/36d260a695f383345ab5bbc3fd447249594ae2fa8dfd19c533d5ae23f46b/stevedore-5.7.0-py3-none-any.whl", hash = "sha256:fd25efbb32f1abb4c9e502f385f0018632baac11f9ee5d1b70f88cc5e22ad4ed", size = 54483, upload-time = "2026-02-20T13:27:05.561Z" }, ] [[package]] name = "tomlkit" -version = "0.13.3" +version = "0.14.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/18/0bbf3884e9eaa38819ebe46a7bd25dcd56b67434402b66a58c4b8e552575/tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1", size = 185207, upload-time = "2025-06-05T07:13:44.947Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/af/14b24e41977adb296d6bd1fb59402cf7d60ce364f90c890bd2ec65c43b5a/tomlkit-0.14.0.tar.gz", hash = "sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064", size = 187167, upload-time = "2026-01-13T01:14:53.304Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/75/8539d011f6be8e29f339c42e633aae3cb73bffa95dd0f9adec09b9c58e85/tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0", size = 38901, upload-time = "2025-06-05T07:13:43.546Z" }, + { url = "https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl", hash = "sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680", size = 39310, upload-time = "2026-01-13T01:14:51.965Z" }, ] [[package]] name = "tqdm" -version = "4.67.1" +version = "4.67.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } +sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, + { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, ] [[package]] @@ -789,21 +811,22 @@ wheels = [ [[package]] name = "virtualenv" -version = "20.35.4" +version = "21.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "filelock", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "platformdirs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "python-discovery", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/20/28/e6f1a6f655d620846bd9df527390ecc26b3805a0c5989048c210e22c5ca9/virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c", size = 6028799, upload-time = "2025-10-29T06:57:40.511Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/c9/18d4b36606d6091844daa3bd93cf7dc78e6f5da21d9f21d06c221104b684/virtualenv-21.1.0.tar.gz", hash = "sha256:1990a0188c8f16b6b9cf65c9183049007375b26aad415514d377ccacf1e4fb44", size = 5840471, upload-time = "2026-02-27T08:49:29.702Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b", size = 6005095, upload-time = "2025-10-29T06:57:37.598Z" }, + { url = "https://files.pythonhosted.org/packages/78/55/896b06bf93a49bec0f4ae2a6f1ed12bd05c8860744ac3a70eda041064e4d/virtualenv-21.1.0-py3-none-any.whl", hash = "sha256:164f5e14c5587d170cf98e60378eb91ea35bf037be313811905d3a24ea33cc07", size = 5825072, upload-time = "2026-02-27T08:49:27.516Z" }, ] [[package]] name = "zero-kernel-builder" -version = "0.6.4" +version = "0.6.5" source = { editable = "." } [package.dev-dependencies] diff --git a/zkb/__main__.py b/zkb/__main__.py index 83b13d8..eafe6cd 100644 --- a/zkb/__main__.py +++ b/zkb/__main__.py @@ -2,12 +2,15 @@ import io import sys import json +import logging import argparse +from typing import Any from pathlib import Path from importlib.metadata import version from zkb.core import KernelBuilder, AssetsCollector from zkb.tools import cleaning as cm, commands as ccmd, Logger as logger +from zkb.enums import EnumChroot, EnumCommand, EnumEnvironment, EnumKernelBase, EnumPackageType from zkb.configs import ArgumentConfig, DirectoryConfig as dcfg from zkb.engines import GenericContainerEngine from zkb.commands import KernelCommand, AssetsCommand, BundleCommand @@ -23,7 +26,7 @@ def __get_version() -> str: :return: App version. :rtype: str """ - msg = "zero_kernel {}" + msg = "zero-kernel {}" try: return msg.format(version("zero-kernel")) @@ -45,9 +48,9 @@ def parse_args() -> argparse.Namespace: # parser and subparsers parser_parent = argparse.ArgumentParser(description="Advanced Android kernel builder with Kali NetHunter support.") subparsers = parser_parent.add_subparsers(dest="command") - parser_kernel = subparsers.add_parser("kernel", help="build the kernel") - parser_assets = subparsers.add_parser("assets", help="collect assets") - parser_bundle = subparsers.add_parser("bundle", help="build the kernel + collect assets") + parser_kernel = subparsers.add_parser(EnumCommand.KERNEL.value, help="build the kernel") + parser_assets = subparsers.add_parser(EnumCommand.ASSETS.value, help="collect assets") + parser_bundle = subparsers.add_parser(EnumCommand.BUNDLE.value, help="build the kernel + collect assets") # main parser arguments parser_parent.add_argument( @@ -58,97 +61,97 @@ def parse_args() -> argparse.Namespace: ) parser_parent.add_argument("-v", "--version", action="version", version=__get_version()) - # common argument attributes for subparsers - help_base = "select a kernel base for the build" - help_codename = "select device codename" - help_benv = "select build environment" - help_clean = "remove Docker/Podman image from the host machine after build" - choices_benv = {"local", "docker", "podman"} - choices_base = {"los", "pa", "x", "aosp"} - help_defconfig = "specify path to custom defconfig" - help_ksu = "add KernelSU support" - help_lkv = "select Linux Kernel Version" + # common arguments + common_codename = { + "name_or_flags": ("--codename",), + "config": { + "required": True, + "dest": "codename", + "type": str, + "help": "device codename" + } + } + common_benv = { + "name_or_flags": ("--build-env",), + "config": { + "required": True, + "dest": "benv", + "type": EnumEnvironment, + "choices": tuple(EnumEnvironment), + "help": "build environment" + } + } + common_base = { + "name_or_flags": ("--base",), + "config": { + "required": True, + "dest": "base", + "type": EnumKernelBase, + "choices": tuple(EnumKernelBase), + "help": "kernel source base" + } + } + common_defconfig = { + "name_or_flags": ("--defconfig",), + "config": { + "required": False, + "dest": "defconfig", + "type": Path, + "help": "path to custom defconfig" + } + } + common_lkv = { + "name_or_flags": ("--lkv",), + "config": { + "required": True, + "dest": "lkv", + "type": str, + "help": "Linux kernel version" + } + } + common_ksu = { + "name_or_flags": ("--ksu",), + "config": { + "action": "store_true", + "dest": "ksu", + "help": "flag for KernelSU support" + } + } + common_clean_image = { + "name_or_flags": ("--clean-image",), + "config": { + "action": "store_true", + "dest": "clean_image", + "help": "clean Docker cache after the build" + } + } # kernel - parser_kernel.add_argument( - "--build-env", - type=str, - dest="benv", - required=True, - choices=choices_benv, - help=help_benv, - ) - parser_kernel.add_argument( - "--base", - type=str, - required=True, - help=help_base, - choices=choices_base - ) - parser_kernel.add_argument( - "--codename", - type=str, - required=True, - help=help_codename - ) - parser_kernel.add_argument( - "--lkv", - type=str, - required=True, - help=help_lkv - ) + parser_kernel.add_argument(*common_benv["name_or_flags"], **common_benv["config"]) + parser_kernel.add_argument(*common_base["name_or_flags"], **common_base["config"]) + parser_kernel.add_argument(*common_codename["name_or_flags"], **common_codename["config"]) + parser_kernel.add_argument(*common_lkv["name_or_flags"], **common_lkv["config"]) + parser_kernel.add_argument(*common_ksu["name_or_flags"], **common_ksu["config"]) + parser_kernel.add_argument(*common_defconfig["name_or_flags"], **common_defconfig["config"]) + parser_kernel.add_argument(*common_clean_image["name_or_flags"], **common_clean_image["config"]) parser_kernel.add_argument( "-c", "--clean", dest="clean_kernel", action="store_true", help="don't build anything, only clean kernel directories" ) - parser_kernel.add_argument( - "--clean-image", - action="store_true", - dest="clean_image", - help=help_clean - ) - parser_kernel.add_argument( - "--ksu", - action="store_true", - dest="ksu", - help=help_ksu - ) - parser_kernel.add_argument( - "--defconfig", - type=Path, - dest="defconfig", - help=help_defconfig - ) # assets - parser_assets.add_argument( - "--build-env", - type=str, - dest="benv", - required=True, - choices=choices_benv, - help=help_benv - ) - parser_assets.add_argument( - "--base", - type=str, - required=True, - help=help_base, - choices=choices_base - ) - parser_assets.add_argument( - "--codename", - type=str, - required=True, - help=help_codename - ) + parser_assets.add_argument(*common_benv["name_or_flags"], **common_benv["config"]) + parser_assets.add_argument(*common_base["name_or_flags"], **common_base["config"]) + parser_assets.add_argument(*common_codename["name_or_flags"], **common_codename["config"]) + parser_assets.add_argument(*common_ksu["name_or_flags"], **common_ksu["config"]) + parser_assets.add_argument(*common_clean_image["name_or_flags"], **common_clean_image["config"]) parser_assets.add_argument( "--chroot", type=str, required=True, - choices=("full", "minimal"), + choices=EnumChroot, help="select Kali chroot type" ) parser_assets.add_argument( @@ -157,59 +160,27 @@ def parse_args() -> argparse.Namespace: action="store_true", help="download only the ROM as an asset" ) - parser_assets.add_argument( - "--clean-image", - action="store_true", - dest="clean_image", - help=help_clean - ) parser_assets.add_argument( "--clean", dest="clean_assets", action="store_true", help="autoclean 'assets' folder if it exists" ) - parser_assets.add_argument( - "--ksu", - action="store_true", - dest="ksu", - help=help_ksu - ) # bundle - parser_bundle.add_argument( - "--build-env", - type=str, - dest="benv", - required=True, - choices=choices_benv, - help=help_benv - ) - parser_bundle.add_argument( - "--base", - type=str, - required=True, - help=help_base, - choices=choices_base - ) - parser_bundle.add_argument( - "--codename", - type=str, - required=True, - help=help_codename - ) - parser_bundle.add_argument( - "--lkv", - type=str, - required=True, - help=help_lkv - ) + parser_bundle.add_argument(*common_benv["name_or_flags"], **common_benv["config"]) + parser_bundle.add_argument(*common_base["name_or_flags"], **common_base["config"]) + parser_bundle.add_argument(*common_codename["name_or_flags"], **common_codename["config"]) + parser_bundle.add_argument(*common_lkv["name_or_flags"], **common_lkv["config"]) + parser_bundle.add_argument(*common_ksu["name_or_flags"], **common_ksu["config"]) + parser_bundle.add_argument(*common_defconfig["name_or_flags"], **common_defconfig["config"]) + parser_bundle.add_argument(*common_clean_image["name_or_flags"], **common_clean_image["config"]) parser_bundle.add_argument( "--package-type", - type=str, + type=EnumPackageType, required=True, dest="package_type", - choices={"conan", "slim", "full"}, + choices=tuple(EnumPackageType), help="select package type of the bundle" ) parser_bundle.add_argument( @@ -218,30 +189,13 @@ def parse_args() -> argparse.Namespace: dest="conan_upload", help="upload Conan packages to remote" ) - parser_bundle.add_argument( - "--clean-image", - action="store_true", - dest="clean_image", - help=help_clean - ) - parser_bundle.add_argument( - "--ksu", - action="store_true", - dest="ksu", - help=help_ksu - ) - parser_bundle.add_argument( - "--defconfig", - type=Path, - dest="defconfig", - help=help_defconfig - ) return parser_parent.parse_args(args) def main() -> None: args = parse_args() logger().get_logger() # type: ignore + log = logging.getLogger("ZeroKernelLogger") # start preparing the environment os.chdir(dcfg.root) @@ -257,16 +211,18 @@ def main() -> None: if args.command != "assets" and args.defconfig: args.defconfig = args.defconfig if args.defconfig.is_absolute() else Path.cwd() / args.defconfig arguments = vars(args) + log.debug(f"Arguments are: {arguments}") + acfg = ArgumentConfig(**arguments) acfg.check_settings() # determine the build variation match args.benv: - case "docker" | "podman": + case EnumEnvironment.DOCKER | EnumEnvironment.PODMAN: with GenericContainerEngine(**json.loads(acfg.model_dump_json())) as engined_cmd: ccmd.launch(engined_cmd) - case "local": + case EnumEnvironment.LOCAL: kernel_builder = KernelBuilder( codename = args.codename, base = args.base, @@ -290,15 +246,15 @@ def main() -> None: ) match args.command: - case "kernel": + case EnumCommand.KERNEL: kc = KernelCommand(kernel_builder=kernel_builder) kc.execute() - case "assets": + case EnumCommand.ASSETS: ac = AssetsCommand(assets_collector=assets_collector) ac.execute() - case "bundle": + case EnumCommand.BUNDLE: bc = BundleCommand( kernel_builder = kernel_builder, assets_collector = assets_collector, diff --git a/zkb/clients/github.py b/zkb/clients/github.py index e10623f..c78fd16 100644 --- a/zkb/clients/github.py +++ b/zkb/clients/github.py @@ -17,14 +17,14 @@ class GithubApiClient(ModelConfig): """Client for limited interaction with GitHub API. :param str project: GitHub project name (owner/repo). - :param Optional[str]=None file_filter: A filter to select specific files from project's artifacts. + :param typing.Optional[str]=None file_filter: A filter to select specific files from project's artifacts. """ project: str file_filter: Optional[str] = None @property - def endpoint(self) -> str: + def __releases_endpoint(self) -> str: """Formatted endpoint. :return: GitHub API endpoint for specified project's latest release. @@ -33,7 +33,7 @@ def endpoint(self) -> str: return f"https://api.github.com/repos/{self.project}/releases/latest" @property - def direct_url(self) -> str: + def __direct_url(self) -> str: """Direct URL to GitHub project. :return: URL to GitHub project. @@ -47,7 +47,7 @@ def run(self) -> str | None: :return: URL to download release artifact from if applicable. :rtype: str | None """ - response = requests.get(self.endpoint).json() + response = requests.get(self.__releases_endpoint).json() # check whether the GitHub API usage is exceeded try: @@ -81,12 +81,12 @@ def run(self) -> str | None: # if not available via API -- use regular "git clone" log.warning(f"Non-API GitHub resolution for {self.project}") - rdir = Path(dcfg.assets, self.direct_url.rsplit("/", 1)[1]) + rdir = Path(dcfg.assets, self.__direct_url.rsplit("/", 1)[1]) cm.remove(rdir) ccmd.launch( "git clone --depth 1 --remote-submodules --recurse-submodules --shallow-submodules {} {}" - .format(self.direct_url, rdir) + .format(self.__direct_url, rdir) ) os.chdir(rdir) cm.remove(".git*") diff --git a/zkb/clients/los.py b/zkb/clients/los.py index 713be0a..40a1edd 100644 --- a/zkb/clients/los.py +++ b/zkb/clients/los.py @@ -1,3 +1,4 @@ +from zkb.enums import EnumKernelBase from zkb.clients.rom_api import RomApiClient @@ -6,4 +7,4 @@ class LineageOsApiClient(RomApiClient): endpoint: str = "https://download.lineageos.org/api/v1/{}/nightly/ro.build.version.incremental" json_key: str = "response" - rom_name: str = "LOS" + rom_name: EnumKernelBase = EnumKernelBase.LOS diff --git a/zkb/clients/pa.py b/zkb/clients/pa.py index 6e3fb0c..a2d3595 100644 --- a/zkb/clients/pa.py +++ b/zkb/clients/pa.py @@ -1,5 +1,6 @@ from typing import override +from zkb.enums import EnumKernelBase from zkb.clients.rom_api import RomApiClient @@ -8,7 +9,7 @@ class ParanoidAndroidApiClient(RomApiClient): endpoint: str = "https://api.paranoidandroid.co/updates/{}" json_key: str = "updates" - rom_name: str = "PA" + rom_name: EnumKernelBase = EnumKernelBase.PA @override def map_codename(self) -> str: diff --git a/zkb/clients/rom_api.py b/zkb/clients/rom_api.py index a0e89f8..7edd7c9 100644 --- a/zkb/clients/rom_api.py +++ b/zkb/clients/rom_api.py @@ -2,6 +2,7 @@ import logging from zkb.configs import ModelConfig +from zkb.enums import EnumKernelBase from zkb.interfaces import IRomApiClient @@ -13,13 +14,13 @@ class RomApiClient(ModelConfig, IRomApiClient): :param str endpoint: API endpoint to interact with. :param str json_key: A JSON key to look for in the response data. - :param str rom_name: ROM project's name. + :param zkb.enums.EnumKernelBase rom_name: ROM project's name. :param bool rom_only: Flag indicating ROM-only asset collection. """ endpoint: str json_key: str - rom_name: str + rom_name: EnumKernelBase codename: str rom_only: bool @@ -38,6 +39,6 @@ def run(self) -> str: data = data.json()[self.json_key][0]["url"] except Exception: exit_flag = False if self.rom_only else True - log.error(f"Could not connect to {self.rom_name} API, HTTP status code: {data.status_code}") + log.error(f"Could not connect to {self.rom_name.value} API, HTTP status code: {data.status_code}") return str(data) diff --git a/zkb/commands/assets.py b/zkb/commands/assets.py index 792585b..aa9a2d0 100644 --- a/zkb/commands/assets.py +++ b/zkb/commands/assets.py @@ -13,7 +13,7 @@ class AssetsCommand(ModelConfig, ICommand): """Command responsible for launching the 'assets_collector' core module directly. - :param builder.core.AssetsCollector assets_collector: Assets collector object. + :param zkb.core.AssetsCollector assets_collector: Assets collector object. """ assets_collector: AssetsCollector diff --git a/zkb/commands/bundle.py b/zkb/commands/bundle.py index b5b4b1d..2a09431 100644 --- a/zkb/commands/bundle.py +++ b/zkb/commands/bundle.py @@ -3,10 +3,10 @@ import shutil import logging import itertools -from pathlib import Path -from typing import Literal, Optional +from typing import Optional from zkb.core import KernelBuilder, AssetsCollector +from zkb.enums import EnumPackageType from zkb.tools import cleaning as cm, commands as ccmd, fileoperations as fo from zkb.configs import DirectoryConfig as dcfg, ModelConfig from zkb.interfaces import ICommand @@ -18,30 +18,30 @@ class BundleCommand(ModelConfig, ICommand): """Command that packages the artifacts produced both by 'kernel_builder' and 'assets_collector' core modules. - :param builder.core.KernelBuilder kernel_builder: Kernel builder object. - :param builder.core.AssetsCollector assets_collector: Assets collector object. + :param zkb.core.KernelBuilder kernel_builder: Kernel builder object. + :param zkb.core.AssetsCollector assets_collector: Assets collector object. :param str package_type: Package type. :param str base: ROM base for the kernel. """ kernel_builder: KernelBuilder assets_collector: AssetsCollector - package_type: str + package_type: EnumPackageType base: str - def build_kernel(self, rom_name: str, clean_only: Optional[bool] = False) -> None: + def build_kernel(self, clean_only: Optional[bool] = False) -> None: if not dcfg.kernel.is_dir() or clean_only is True: self.kernel_builder.clean_kernel = clean_only # type: ignore self.kernel_builder.run() @property - def _rom_only_flag(self) -> bool: - return True if "full" not in self.package_type else False + def __rom_only_flag(self) -> bool: + return True if self.package_type != EnumPackageType.FULL else False - def collect_assets(self, rom_name: str, chroot: Literal["full", "minimal"]) -> None: + def collect_assets(self) -> None: self.assets_collector.clean_assets = True - self.assets_collector.rom_only = self._rom_only_flag + self.assets_collector.rom_only = self.__rom_only_flag self.assets_collector.run() @@ -107,11 +107,11 @@ def execute(self) -> None: # determine the bundle type and process it match self.package_type: - case "slim" | "full": - self.build_kernel(self.base) + case EnumPackageType.FULL | EnumPackageType.SLIM: + self.build_kernel() # "full" chroot is hardcoded here - self.collect_assets(self.base, "full") + self.collect_assets() # clean up if dcfg.bundle.is_dir(): @@ -130,9 +130,9 @@ def execute(self) -> None: # here, because of their size assets are moved and not copied shutil.move(dcfg.assets / afn, dcfg.bundle / afn) - case "conan": + case EnumPackageType.CONAN: # form Conan reference - name = "zero_kernel" + name = "zero-kernel" version = os.getenv("KVERSION") user = self.kernel_builder.codename channel = "" @@ -150,10 +150,10 @@ def execute(self) -> None: # build and upload Conan packages for opset in option_sets: - self.build_kernel(opset[0]) - self.build_kernel(opset[0], True) + self.build_kernel() + self.build_kernel(True) self.conan_sources() - self.collect_assets(opset[0], opset[1]) + self.collect_assets() self.conan_package(opset, reference) # upload packages diff --git a/zkb/commands/kernel.py b/zkb/commands/kernel.py index 639f4c7..0bf532a 100644 --- a/zkb/commands/kernel.py +++ b/zkb/commands/kernel.py @@ -12,7 +12,7 @@ class KernelCommand(ModelConfig, ICommand): """Command responsible for launching the 'kernel_builder' core module directly. - :param builder.core.KernelBuilder kernel_builder: Kernel builder object. + :param zkb.core.KernelBuilder kernel_builder: Kernel builder object. """ kernel_builder: KernelBuilder diff --git a/zkb/configs/argument.py b/zkb/configs/argument.py index 591c81d..aa3c425 100644 --- a/zkb/configs/argument.py +++ b/zkb/configs/argument.py @@ -3,41 +3,44 @@ import logging import platform from pathlib import Path -from typing import Optional, Literal +from typing import Optional +from pydantic import BaseModel +from zkb.enums import EnumEnvironment, EnumCommand, EnumPackageType from zkb.tools import commands as ccmd -from zkb.configs import ModelConfig log = logging.getLogger("ZeroKernelLogger") -class ArgumentConfig(ModelConfig): +class ArgumentConfig(BaseModel): """Variable storage for usage across the app. - :param Literal["docker","podman","local"] benv: Build environment. - :param Literal["kernel","assets","bundle"] command: Builder command to be launched. + Only model not using shared ModelConfig. + + :param zkb.enums.EnumEnvironment benv: Build environment. + :param zkb.enums.EnumCommand command: Builder command to be launched. :param str codename: Device codename. :param str base: Kernel source base. :param str lkv: Linux kernel version. - :param Optional[str]=None chroot: Chroot type. - :param Optional[str]=None package_type: Package type. - :param Optional[bool]=False clean_kernel: Flag to clean folder with kernel sources. - :param Optional[bool]=False clean_assets: Flag to clean folder for assets storage. - :param Optional[bool]=False clean_image: Flag to clean a Docker/Podman image from local cache. - :param Optional[bool]=False rom_only: Flag indicating ROM-only asset collection. - :param Optional[bool]=False conan_upload: Flag to enable Conan upload. - :param Optional[bool]=False ksu: Flag indicating KernelSU support. - :param Optional[Path]=None defconfig: Path to custom defconfig. + :param typing.Optional[str]=None chroot: Chroot type. + :param typing.Optional[str]=None package_type: Package type. + :param typing.Optional[bool]=False clean_kernel: Flag to clean folder with kernel sources. + :param typing.Optional[bool]=False clean_assets: Flag to clean folder for assets storage. + :param typing.Optional[bool]=False clean_image: Flag to clean a Docker/Podman image from local cache. + :param typing.Optional[bool]=False rom_only: Flag indicating ROM-only asset collection. + :param typing.Optional[bool]=False conan_upload: Flag to enable Conan upload. + :param typing.Optional[bool]=False ksu: Flag indicating KernelSU support. + :param typing.Optional[pathlib.Path]=None defconfig: Path to custom defconfig. """ - benv: Literal["docker", "podman", "local"] - command: Literal["kernel", "assets", "bundle"] + benv: EnumEnvironment + command: EnumCommand codename: str base: str lkv: Optional[str] = None chroot: Optional[str] = None - package_type: Optional[str] = None + package_type: Optional[EnumPackageType] = None clean_kernel: Optional[bool] = False clean_assets: Optional[bool] = False clean_image: Optional[bool] = False @@ -52,7 +55,7 @@ def check_settings(self) -> None: :return: None """ # allow only asset colletion on a non-Linux machine - if self.benv == "local" and self.command in {"kernel", "bundle"}: + if self.benv == EnumEnvironment.LOCAL and self.command in {EnumCommand.KERNEL, EnumCommand.BUNDLE}: if not platform.system() == "Linux": log.error("Can't build kernel on a non-Linux machine.") sys.exit(1) @@ -73,9 +76,9 @@ def check_settings(self) -> None: if self.codename not in devices.keys(): log.error("Unsupported device codename specified.") sys.exit(1) - if self.command == "bundle": + if self.command == EnumCommand.BUNDLE: # check Conan-related argument usage - if self.package_type != "conan" and self.conan_upload: + if self.package_type != EnumPackageType.CONAN and self.conan_upload: log.error("Cannot use Conan-related arguments with non-Conan packaging\n") sys.exit(1) diff --git a/zkb/configs/directory.py b/zkb/configs/directory.py index 50c2952..830c9b0 100644 --- a/zkb/configs/directory.py +++ b/zkb/configs/directory.py @@ -1,11 +1,13 @@ from pathlib import Path from pydantic.dataclasses import dataclass +from zkb.enums import EnumCommand + @dataclass class DirectoryConfig: """Config for key directory paths.""" root: Path = Path(__file__).absolute().parents[2] - kernel: Path = root / "kernel" - assets: Path = root / "assets" - bundle: Path = root / "bundle" + kernel: Path = root / EnumCommand.KERNEL.value + assets: Path = root / EnumCommand.ASSETS.value + bundle: Path = root / EnumCommand.BUNDLE.value diff --git a/zkb/core/assets_collector.py b/zkb/core/assets_collector.py index 62dbf47..5603f5c 100644 --- a/zkb/core/assets_collector.py +++ b/zkb/core/assets_collector.py @@ -1,8 +1,9 @@ import os import sys import logging -from typing import Literal, Optional +from typing import Optional +from zkb.enums import EnumChroot, EnumKernelBase from zkb.tools import banner, fileoperations as fo, cleaning as cm from zkb.clients import GithubApiClient, LineageOsApiClient, ParanoidAndroidApiClient from zkb.configs import DirectoryConfig as dcfg, ModelConfig @@ -17,14 +18,14 @@ class AssetsCollector(ModelConfig, IAssetsCollector): :param str codename: Device codename. :param str base: Kernel source base. - :param Optional[Literal["full","minimal"]]=None chroot: Chroot type. + :param typing.Optional[zkb.enums.EnumChroot]=None chroot: Chroot type. :param bool rom_only: Flag indicating ROM-only asset collection. :param bool ksu: Flag indicating KernelSU support. """ codename: str base: str - chroot: Optional[Literal["full", "minimal"]] = None + chroot: Optional[EnumChroot] = None clean_assets: bool rom_only: bool ksu: bool @@ -32,11 +33,11 @@ class AssetsCollector(ModelConfig, IAssetsCollector): @property def rom_collector_dto(self) -> LineageOsApiClient | ParanoidAndroidApiClient | None: match self.base: - case "los": + case EnumKernelBase.LOS: return LineageOsApiClient(codename=self.codename, rom_only=self.rom_only) - case "pa": + case EnumKernelBase.PA: return ParanoidAndroidApiClient(codename=self.codename, rom_only=self.rom_only) - case "x" | "aosp": + case EnumKernelBase.X | EnumKernelBase.AOSP: # selected kernel base is ROM-universal, no specific ROM image will be collected return None @@ -86,6 +87,10 @@ def assets(self) -> list: project="nfcgate/nfcgate", file_filter=".apk" ), + GithubApiClient( + project="ImranR98/Obtainium", + file_filter="app-arm64-v8a-release.apk" + ), # files from direct URLs "https://store.nethunter.com/NetHunter.apk", "https://store.nethunter.com/NetHunterKeX.apk", @@ -93,7 +98,7 @@ def assets(self) -> list: "https://store.nethunter.com/NetHunterTerminal.apk", "https://sourceforge.net/projects/op5-5t/files/Android-12/TWRP/twrp-3.7.0_12-5-dyn-cheeseburger_dumpling.img/download", "https://kali.download/nethunter-images/current/rootfs/kali-nethunter-rootfs-{}-arm64.tar.xz".format(self.chroot), - "https://github.com/mozilla-mobile/firefox-android/releases/download/fenix-v117.1.0/fenix-117.1.0-arm64-v8a.apk", + "https://ftp.mozilla.org/pub/fenix/releases/147.0.1/android/fenix-147.0.1-android-arm64-v8a/fenix-147.0.1.multi.android-arm64-v8a.apk", "https://f-droid.org/F-Droid.apk", ] @@ -115,7 +120,6 @@ def check(self) -> None: if len(os.listdir(dcfg.assets)) != 0: cmsg = f'[ ? ] Found an existing "{dcfg.assets.name}" folder, clean it? [Y/n]: ' ans = input(cmsg).lower() if not self.clean_assets else "y" - match ans: case "y": log.warning("Cleaning 'assets' directory..") @@ -128,7 +132,6 @@ def check(self) -> None: case _: log.error("Invalid option selected.") sys.exit(1) - print("\n", end="") def run(self) -> None: diff --git a/zkb/core/kernel_builder.py b/zkb/core/kernel_builder.py index c4133a8..a033c0d 100644 --- a/zkb/core/kernel_builder.py +++ b/zkb/core/kernel_builder.py @@ -22,7 +22,7 @@ class KernelBuilder(ModelConfig, IKernelBuilder): :param str lkv: Linux kernel version. :param bool clean_kernel: Flag to clean folder with kernel sources. :param bool ksu: Flag indicating KernelSU support. - :param Optional[Path]=None defconfig: Path to custom defconfig. + :param typing.Optional[Path]=None defconfig: Path to custom defconfig. """ codename: str @@ -36,7 +36,7 @@ class KernelBuilder(ModelConfig, IKernelBuilder): @staticmethod def write_localversion() -> None: with open("localversion", "w", encoding="utf-8") as f: - f.write("~zero_kernel") + f.write("~zero-kernel") @property def _ucodename(self) -> str: diff --git a/zkb/engines/generic_container.py b/zkb/engines/generic_container.py index 1af332e..512a6d7 100644 --- a/zkb/engines/generic_container.py +++ b/zkb/engines/generic_container.py @@ -6,6 +6,7 @@ from typing import Optional, Literal from subprocess import CompletedProcess +from zkb.enums import EnumChroot, EnumCommand, EnumEnvironment, EnumPackageType from zkb.tools import commands as ccmd from zkb.configs import DirectoryConfig as dcfg, ModelConfig from zkb.interfaces import IGenericContainerEngine @@ -23,20 +24,20 @@ class GenericContainerEngine(ModelConfig, IGenericContainerEngine): directory structure. We don't need to pass full paths relevant to local environment, only directories' names. - :param Literal["docker","podman"] benv: Build environment. - :param Literal["kernel","assets","bundle"] command: Builder command to be launched. + :param zkb.enums.EnumEnvironment benv: Build environment. + :param zkb.enums.EnumCommand command: Builder command to be launched. :param str codename: Device codename. :param str base: Kernel source base. :param str lkv: Linux kernel version. - :param Optional[Literal["full","minimal"]]=None chroot: Chroot type. - :param Optional[bool]=False package_type: Package type. - :param Optional[bool]=False clean_kernel: Flag to clean folder for kernel storage. - :param Optional[bool]=False clean_assets: Flag to clean folder for assets storage. - :param Optional[bool]=False clean_image: Flag to clean a Docker/Podman image from local cache. - :param Optional[bool]=False rom_only: Flag indicating ROM-only asset collection. - :param Optional[bool]=False conan_upload: Flag to enable Conan upload. - :param Optional[bool]=False ksu: Flag to add KernelSU support into the kernel. - :param Optional[Path]=None defconfig: Path to custom defconfig. + :param typing.Optional[zkb.enums.EnumChroot]=None chroot: Chroot type. + :param typing.Optional[zkb.enums.EnumPackageType]=False package_type: Package type. + :param typing.Optional[bool]=False clean_kernel: Flag to clean folder for kernel storage. + :param typing.Optional[bool]=False clean_assets: Flag to clean folder for assets storage. + :param typing.Optional[bool]=False clean_image: Flag to clean a Docker/Podman image from local cache. + :param typing.Optional[bool]=False rom_only: Flag indicating ROM-only asset collection. + :param typing.Optional[bool]=False conan_upload: Flag to enable Conan upload. + :param typing.Optional[bool]=False ksu: Flag to add KernelSU support into the kernel. + :param typing.Optional[pathlib.Path]=None defconfig: Path to custom defconfig. """ _name_image: str = "zero-kernel-image" @@ -44,13 +45,13 @@ class GenericContainerEngine(ModelConfig, IGenericContainerEngine): _wdir_container: Path = Path("/", "zero_build") _wdir_local: Path = dcfg.root - benv: Literal["docker", "podman"] - command: Literal["kernel", "assets", "bundle"] + benv: EnumEnvironment + command: EnumCommand codename: str base: str lkv: Optional[str] = None - chroot: Optional[Literal["full", "minimal"]] = None - package_type: Optional[str] = None + chroot: Optional[EnumChroot] = None + package_type: Optional[EnumPackageType] = None clean_kernel: Optional[bool] = False clean_assets: Optional[bool] = False clean_image: Optional[bool] = False @@ -72,7 +73,7 @@ def dir_bundle_conan(self) -> Path: return Path(os.getenv("HOME"), ".conan") # type: ignore def check_cache(self) -> bool: - img_cache_cmd = f'{self.benv} images --format {"{{.Repository}}"}' + img_cache_cmd = f'{self.benv.value} image list --format {"{{.Repository}}"}' img_cache = str(ccmd.launch(img_cache_cmd, get_output=True)) return True if self._name_image in img_cache else False @@ -105,8 +106,8 @@ def builder_cmd(self) -> str: cmd += f" {arg}" # extend the command with the selected packaging option - if self.command == "bundle": - if self.package_type in ("slim", "full"): + if self.command == EnumCommand.BUNDLE: + if self.package_type in (EnumPackageType.SLIM, EnumPackageType.FULL): cmd += f" && chmod 777 -R {self._wdir_container / dcfg.bundle.name}" else: cmd += " && chmod 777 -R /root/.conan" @@ -129,15 +130,15 @@ def container_options(self) -> list[str]: # mount directories match self.command: - case "kernel": + case EnumCommand.KERNEL: options.append(v_template.format(dcfg.kernel, self._wdir_container, dcfg.kernel.name)) - case "assets": + case EnumCommand.ASSETS: options.append(v_template.format(dcfg.assets, self._wdir_container, dcfg.assets.name)) - case "bundle": + case EnumCommand.BUNDLE: match self.package_type: - case "slim" | "full": + case EnumPackageType.SLIM | EnumPackageType.FULL: options.append(v_template.format(dcfg.bundle, self._wdir_container, dcfg.bundle.name)) - case "conan": + case EnumPackageType.CONAN: if self.conan_upload: options.append("-e CONAN_UPLOAD_CUSTOM=1") # determine the path to local Conan cache and check if it exists @@ -151,26 +152,26 @@ def container_options(self) -> list[str]: def create_dirs(self) -> None: match self.command: - case "kernel": + case EnumCommand.KERNEL: if not dcfg.kernel.is_dir(): os.makedirs(dcfg.kernel) - case "assets": + case EnumCommand.ASSETS: if not dcfg.assets.is_dir(): os.makedirs(dcfg.assets) - case "bundle": - if self.package_type in ("slim", "full"): + case EnumCommand.BUNDLE: + if self.package_type in (EnumPackageType.SLIM, EnumPackageType.FULL): # mount directory with release artifacts shutil.rmtree(dcfg.bundle, ignore_errors=True) os.makedirs(dcfg.bundle) def build_image(self) -> str | None | CompletedProcess: print("\n") - log.warning(f"Building the {self.benv.capitalize()} image..") + log.warning(f"Building the {self.benv.value.capitalize()} image..") os.chdir(self._wdir_local) # NOTE: this will crash in GitLab CI/CD (Docker-in-Docker), requires a workaround cmd = "{} build . -f {} -t {} --load".format( - self.benv, + self.benv.value, self._wdir_local / "Dockerfile", self._name_image ) @@ -184,7 +185,7 @@ def build_image(self) -> str | None | CompletedProcess: @property def get_container_cmd(self) -> str: return '{} run {} {} /bin/bash -c "source .venv/bin/activate && {}"'.format( - self.benv, + self.benv.value, " ".join(self.container_options), self._name_image, self.builder_cmd @@ -192,14 +193,14 @@ def get_container_cmd(self) -> str: def __enter__(self) -> str: # prepare Docker if selected - if self.benv == "docker": + if self.benv == EnumEnvironment.DOCKER: self._force_buildkit() # build the image and prepare directories if not self.check_cache(): self.build_image() else: - log.warning(f"{self.benv.capitalize()} image already in local cache, skipping it's build..\n") + log.warning(f"{self.benv.value.capitalize()} image already in local cache, skipping it's build..\n") self.create_dirs() return self.get_container_cmd @@ -209,4 +210,4 @@ def __exit__(self, exc_type, exc_value, traceback) -> None: os.chdir(self._wdir_local) if self.clean_image: - ccmd.launch(f"{self.benv} rmi {self._name_image}") + ccmd.launch(f"{self.benv.value} rmi {self._name_image}") diff --git a/zkb/enums.py b/zkb/enums.py new file mode 100644 index 0000000..deed6be --- /dev/null +++ b/zkb/enums.py @@ -0,0 +1,46 @@ +from enum import Enum + + +class EnumChroot(Enum): + FULL = "full" + MINIMAL = "minimal" + + def __str__(self): + return self.value + + +class EnumCommand(Enum): + KERNEL = "kernel" + ASSETS = "assets" + BUNDLE = "bundle" + + def __str__(self): + return self.value + + +class EnumEnvironment(Enum): + LOCAL = "local" + DOCKER = "docker" + PODMAN = "podman" + + def __str__(self): + return self.value + + +class EnumPackageType(Enum): + CONAN = "conan" + SLIM = "slim" + FULL = "full" + + def __str__(self): + return self.value + + +class EnumKernelBase(Enum): + LOS = "los" + PA = "pa" + X = "x" + AOSP = "aosp" + + def __str__(self): + return self.value diff --git a/zkb/managers/resource.py b/zkb/managers/resource.py index 9eea5c1..74531ed 100644 --- a/zkb/managers/resource.py +++ b/zkb/managers/resource.py @@ -17,9 +17,9 @@ class ResourceManager(ModelConfig, IResourceManager): """Build resource manager. - :param Optional[str]=None codename: Device codename. - :param Optional[str]=None base: Kernel source base. - :param Optional[str]=None lkv: Linux kernel version. + :param typing.Optional[str]=None codename: Device codename. + :param typing.Optional[str]=None base: Kernel source base. + :param typing.Optional[str]=None lkv: Linux kernel version. """ _data: dict[str, dict[str, str]] = {} diff --git a/zkb/tools/cleaning.py b/zkb/tools/cleaning.py index f197d35..039503b 100644 --- a/zkb/tools/cleaning.py +++ b/zkb/tools/cleaning.py @@ -15,7 +15,7 @@ def remove(elements: str | Path | list[Path | str]) -> None: Here, all Path() objects will have to be converted into str. Because of such specific as directories starting with a "." (e.g., .github). - :param str/Path/list[Path] elements: Files and/or directories to remove. + :param str/pathlib.Path/list[pathlib.Path] elements: Files and/or directories to remove. :return: None """ # if a given argument is a string --> convert it into a one-element list @@ -43,7 +43,7 @@ def on_rm_error(func, path: str, exc_info): """For Windows system to remove a .git folder. :param func: Function to be used along with. - :param Path path: Path that is being removed. + :param pathlib.Path path: Path that is being removed. :exc_info param: Misc info. """ os.chmod(path, stat.S_IWRITE) @@ -53,7 +53,7 @@ def on_rm_error(func, path: str, exc_info): def git(directory: Path | str) -> None: """Clean up a git directory. - :param Path/str directory: Path to the directory. + :param pathlib.Path/str directory: Path to the directory. """ goback = Path.cwd() @@ -66,7 +66,7 @@ def git(directory: Path | str) -> None: def root(extra: Optional[list[str]] = []) -> None: """Fully clean the root directory. - :param Optional[list[str]]=[] extra: Extra elements to be removed. + :param typing.Optional[list[str]]=[] extra: Extra elements to be removed. """ trsh = [ dcfg.kernel, diff --git a/zkb/tools/commands.py b/zkb/tools/commands.py index 0a62565..3577d33 100644 --- a/zkb/tools/commands.py +++ b/zkb/tools/commands.py @@ -17,8 +17,8 @@ def launch( """Custom subprocess wrapper to launch commands. :param str cmd: Command to launch. - :param Optional[bool]=False get_output: Switch to get the piped output of the command. - :param str loglvl: Log level. + :param typing.Optional[bool]=False get_output: Switch to get the piped output of the command. + :param typing.Optional[Literal["normal", "quiet"]] loglvl: Log level. :return: Result of command launch. :rtype: str | CompletedProcess | None """ diff --git a/zkb/tools/fileoperations.py b/zkb/tools/fileoperations.py index 367c55b..5afa543 100644 --- a/zkb/tools/fileoperations.py +++ b/zkb/tools/fileoperations.py @@ -15,9 +15,9 @@ def ucopy(src: Path, dst: Path, exceptions: Optional[tuple[str | Path, ...]] = ()) -> None: """Copy files and directories into desired destinations universally. - :param Path src: Source path. - :param Path dst: Destination path. - :param Optional[tuple[str/Path,...]]=() exceptions: Elements that will not be removed. + :param pathlib.Path src: Source path. + :param pathlib.Path dst: Destination path. + :param typing.Optional[tuple[str/pathlib.Path,...]]=() exceptions: Elements that will not be removed. :return: None """ # for a directory (it's contents) @@ -99,7 +99,7 @@ def replace_lines(filename: Path, og_lines: tuple[str, ...], nw_lines: tuple[str def replace_nth(filename: Path, og_string: str, nw_string: str, occurence: int) -> None: """Replace the n-th occurence of subtring in specified file. - :param Path filename: Path to the filename. + :param pathlib.Path filename: Path to the filename. :param str og_string: Original string to be replaced. :param str nw_string: New string used to replace the original one. :param int occurence: The index of occurence to replace. @@ -124,7 +124,7 @@ def replace_nth(filename: Path, og_string: str, nw_string: str, occurence: int) def insert_before_line(filename: str | Path, pointer_line: str, new_line: str) -> None: """Insert new line before the specified one. - :param str/Path filename: Name of the file. + :param str/pathlib.Path filename: Name of the file. :param str pointer_line: The line before which new line will be inserted. :param str new_line: The line being inserted. :return: None @@ -149,7 +149,7 @@ def insert_before_line(filename: str | Path, pointer_line: str, new_line: str) - def apply_patch(filename: str | Path) -> None: """Apply .patch file. - :param str/Path filename: Name of the .patch file. + :param str/pathlib.Path filename: Name of the .patch file. :return: None """ log.warning(f"Applying patch: {filename}") diff --git a/zkb/tools/logger.py b/zkb/tools/logger.py index a51da2b..cf76e64 100644 --- a/zkb/tools/logger.py +++ b/zkb/tools/logger.py @@ -40,7 +40,6 @@ def _init(self, level = logging.DEBUG) -> None: self.logger.propagate = False if not self.logger.handlers: - #formatter = logging.Formatter("[%(asctime)s] [%(levelname).1s] %(message)s") formatter = self._get_coloured_formatter() # direct DEBUG and INFO to stdout; WARNING, ERROR and CRITICAL to stderr diff --git a/zkb/utils/bridge.py b/zkb/utils/bridge.py index 6221147..614123b 100644 --- a/zkb/utils/bridge.py +++ b/zkb/utils/bridge.py @@ -10,6 +10,7 @@ import argparse from zkb.core import KernelBuilder, AssetsCollector +from zkb.enums import EnumCommand, EnumKernelBase, EnumPackageType, EnumChroot from zkb.managers import ResourceManager from zkb.commands import KernelCommand, AssetsCommand, BundleCommand @@ -35,8 +36,9 @@ def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser() parser.add_argument( "--command", + type=EnumCommand, help="select builder command", - choices={"kernel", "assets", "bundle"} + choices=tuple(EnumCommand) ) parser.add_argument( "--codename", @@ -44,6 +46,8 @@ def parse_args() -> argparse.Namespace: ) parser.add_argument( "--base", + type=EnumKernelBase, + choices=tuple(EnumKernelBase), help="select a kernel base for the build" ) parser.add_argument( @@ -52,14 +56,16 @@ def parse_args() -> argparse.Namespace: ) parser.add_argument( "--chroot", + type=EnumChroot, + choices=tuple(EnumChroot), help="select chroot type", - choices={"full", "minimal"} ) parser.add_argument( "--package-type", dest="package_type", - help="select bundle packaging type", - choices={"conan", "slim", "full"} + type=EnumPackageType, + choices=tuple(EnumPackageType), + help="select bundle packaging type" ) parser.add_argument( "--clean-kernel", @@ -99,10 +105,8 @@ def parse_args() -> argparse.Namespace: def main(args: argparse.Namespace) -> None: - match args.command: - - case "kernel": + case EnumCommand.KERNEL: kernel_builder = KernelBuilder( codename = args.codename, base = args.base, @@ -119,7 +123,7 @@ def main(args: argparse.Namespace) -> None: kc = KernelCommand(kernel_builder=kernel_builder) kc.execute() - case "assets": + case EnumCommand.ASSETS: assets_collector = AssetsCollector( codename = args.codename, base = args.base, @@ -131,7 +135,7 @@ def main(args: argparse.Namespace) -> None: ac = AssetsCommand(assets_collector=assets_collector) ac.execute() - case "bundle": + case EnumCommand.BUNDLE: kernel_builder = KernelBuilder( codename = args.codename, base = args.base,