diff --git a/.dockerignore b/.dockerignore index 1604063..59c5db8 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,6 +3,7 @@ __pycache__ .vscode .coverage .pytest_cache +.venv # git subrepos # (uncomment below if you explicitely want Docker/Podman to not use locally downloaded dirs) diff --git a/.github/workflows/github-ci.yml b/.github/workflows/github-ci.yml index ea76894..651e7aa 100644 --- a/.github/workflows/github-ci.yml +++ b/.github/workflows/github-ci.yml @@ -29,17 +29,24 @@ jobs: uses: actions/checkout@v3 with: submodules: recursive + - name: Setup Python uses: actions/setup-python@v4 with: python-version: "3.12" - - name: Build And Assemble + + - name: Activate .venv run: | python3 -m pip install -r requirement-uv.txt uv sync --frozen --no-install-project + source .venv/bin/activate + + - name: Build And Assemble + run: | export PYTHONPATH=$(pwd) - uv run scripts/common/py/run_tests.py - uv run scripts/multi_build.py + python3 scripts/common/py/run_tests.py + python3 scripts/multi_build.py + - name: Publish Artifacts uses: actions/upload-artifact@v3 with: @@ -47,23 +54,31 @@ jobs: path: "multi-build" retention-days: 1 if-no-files-found: "error" + + - name: Deactivate .venv + run: deactivate + release: runs-on: ubuntu-latest needs: build steps: - name: Checkout uses: actions/checkout@v3 + - name: Retrieve artifacts uses: actions/download-artifact@v3 with: name: build-artifacts path: "multi-build" + - name: Get current version id: version - run: echo "version=$(python3 scripts/get_version.py)" >> $GITHUB_OUTPUT + run: echo "version=$(python3 scripts/common/py/get_version.py)" >> $GITHUB_OUTPUT + - name: Form a tag name id: tagname run: echo "tagname=v${{ steps.version.outputs.version }}" >> $GITHUB_OUTPUT + - name: Release uses: ncipollo/release-action@v1 with: diff --git a/.gitignore b/.gitignore index 5753a49..5b822d5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ __pycache__ .vscode .coverage .pytest_cache +.ruff_cache # git subrepos /android_* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ed59bf0..1d3e452 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,9 +14,11 @@ job-build: - apk update && apk add python3 py3-pip - python3 -m pip install -r requirement-uv.txt --break-system-packages - uv sync --frozen --no-install-project + - source .venv/bin/activate - export PYTHONPATH=$(pwd) - - uv run scripts/common/py/run_tests.py - - uv run scripts/multi_build.py + - python3 scripts/common/py/run_tests.py + - python3 scripts/multi_build.py + - deactivate artifacts: paths: - "multi-build/zero-*.zip" @@ -36,6 +38,7 @@ job-tag: - git config --global user.email "${EMAIL}" - git remote remove origin - git remote add origin https://${USERNAME}:${PASSWORD}@gitlab.com/${CI_PROJECT_PATH} + - git fetch --force --tags - if [ $(git tag | grep "$TAGNAME") ]; then echo "[ * ] Tag already exists, skipping.."; else git tag $TAGNAME && git push origin $TAGNAME; fi rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH diff --git a/README.md b/README.md index a3cb437..7a29389 100755 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ An advanced Android kernel builder with assets collection and Kali NetHunter sup - [zero\_kernel](#zero_kernel) - [Contents](#contents) - - [**Important to Read**](#important-to-read) + - [**Important**](#important) - [Description](#description) - [Kernel Features](#kernel-features) - [Supported Devices \& ROMs](#supported-devices--roms) @@ -19,7 +19,7 @@ An advanced Android kernel builder with assets collection and Kali NetHunter sup - [See also](#see-also) - [Credits](#credits) -## **Important to Read** +## **Important** > [!IMPORTANT] > **\- DISCLAIMER \-** @@ -92,7 +92,7 @@ Commands: - `bundle`. ```help -$ uv run builder --help +$ python3 builder --help usage: builder [-h] [--clean] {kernel,assets,bundle} ... A custom builder for the zero_kernel. @@ -117,26 +117,28 @@ optional arguments: To run this tool in a `local` environment, you will need: -- a Debian-based Linux distribution (other types of distros are untested); -- a few [packages](Dockerfile#L15) installed in your system. - -You will also need to install [uv](https://github.com/astral-sh/uv). Please refer to [installation guide](https://docs.astral.sh/uv/getting-started/installation/) to choose an option fit for you. It is recommended to use `pip` to install the version of uv used in the project. +- a Debian-based Linux distribution (other distribution families are untested); +- a few [packages](Dockerfile#L15) installed in your system; +- a configured Python environment with Python 3.12+. ```sh -# making "builder" executable from source -export PYTHONPATH=$(pwd) -# installing uv via pip +# install uv version from project file python3 -m pip install -r requirement-uv.txt -# collecting project dependencies into a local .venv +# make builder/ internal imports visible to itself +export PYTHONPATH=$(pwd) +# prepare and activate dev environment uv sync --frozen --no-install-project +source .venv/bin/activate ``` +Once you are finished working with the project, don't forget to disable the virtual environment (venv) via simple `deactivate`. + ### Kernel Kernel build process can be launched using the `kernel` subcommand. ```help -$ uv run builder kernel --help +$ python3 builder kernel --help usage: builder kernel [-h] --build-env {local,docker,podman} --base {los,pa,x,aosp} --codename CODENAME --lkv LKV [-c] [--clean-image] [--ksu] @@ -159,7 +161,7 @@ options: As mentioned, there is also an asset downloader, which can collect latest versions of ROM, TWRP, Magisk and it's modules, Kali Chroot etc. ```help -$ uv run builder assets --help +$ python3 builder assets --help usage: builder assets [-h] --build-env {local,docker,podman} --base {los,pa,x,aosp} --codename CODENAME --chroot {full,minimal} [--rom-only] [--clean-image] [--clean] [--ksu] @@ -201,7 +203,7 @@ Options `full` and `conan` collect all of the assets required to successfuly fla Option named `slim` is a much lighter version of `full` packaging, as only the ROM is collected from the asset list. This is done to reduce package sizes while ensuring the kernel+ROM compatibility. ```help -$ uv run builder bundle --help +$ python3 builder bundle --help usage: builder bundle [-h] --build-env {local,docker,podman} --base {los,pa,x,aosp} --codename CODENAME --lkv LKV --package-type {conan,slim,full} [--conan-upload] [--clean-image] [--ksu] @@ -229,19 +231,19 @@ Here are some examples of commands: **(Recommended)** Build kernel and collect ROM via Docker: ```sh -uv run builder bundle --build-env=docker --base=los --codename=dumpling --lkv=4.4 --package-type=slim +python3 builder bundle --build-env=docker --base=los --codename=dumpling --lkv=4.4 --package-type=slim ``` Build kernel locally: ```sh -uv run builder kernel --build-env=local --base=los --codename=dumpling --lkv=4.4 +python3 builder kernel --build-env=local --base=los --codename=dumpling --lkv=4.4 ``` Collect all of the assets locally: ```sh -uv run builder assets --build-env=local --base=los --codename=dumpling --package-type=full +python3 builder assets --build-env=local --base=los --codename=dumpling --package-type=full ``` ## See also diff --git a/builder/__main__.py b/builder/__main__.py index d23ad6d..5ac73ca 100644 --- a/builder/__main__.py +++ b/builder/__main__.py @@ -4,6 +4,7 @@ import json import argparse from pathlib import Path +from importlib.metadata import version from builder.tools import cleaning as cm, commands as ccmd from builder.configs import ArgumentConfig, DirectoryConfig as dcfg @@ -15,29 +16,34 @@ def parse_args() -> argparse.Namespace: """Parse the script arguments.""" # show the 'help' message if no arguments supplied args = None if sys.argv[1:] else ["-h"] + # parser and subparsers - parser_parent = argparse.ArgumentParser(description="A custom builder for the zero kernel.") + 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") - # add a single argument for the main parser + + # main parser arguments parser_parent.add_argument( "--clean", dest="clean_root", action="store_true", help="clean the root directory" ) + parser_parent.add_argument("-v", "--version", action="version", version=version("zero-kernel")) + # 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") + 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" + # kernel parser_kernel.add_argument( "--build-env", @@ -90,6 +96,7 @@ def parse_args() -> argparse.Namespace: dest="defconfig", help=help_defconfig ) + # assets parser_assets.add_argument( "--build-env", @@ -143,6 +150,7 @@ def parse_args() -> argparse.Namespace: dest="ksu", help=help_ksu ) + # bundle parser_bundle.add_argument( "--build-env", @@ -176,7 +184,7 @@ def parse_args() -> argparse.Namespace: type=str, required=True, dest="package_type", - choices=("conan", "slim", "full"), + choices={"conan", "slim", "full"}, help="select package type of the bundle" ) parser_bundle.add_argument( @@ -212,20 +220,24 @@ def main(args: argparse.Namespace) -> None: if args.clean_root: cm.root() sys.exit(0) + # define env variable with kernel version with open(dcfg.root / "pyproject.toml", encoding="utf-8") as f: - os.environ["KVERSION"] = f.read().split("version = \"")[1].split("\"")[0] + os.environ["KVERSION"] = f.read().split('version = "')[1].split('"')[0] + # create a config for checking and storing arguments if args.command != "assets" and args.defconfig: args.defconfig = args.defconfig if args.defconfig.is_absolute() else Path.cwd() / args.defconfig arguments = vars(args) acfg = ArgumentConfig(**arguments) acfg.check_settings() - # determine the build + + # determine the build variation match args.benv: case "docker" | "podman": with GenericContainerEngine(**json.loads(acfg.model_dump_json())) as engined_cmd: ccmd.launch(engined_cmd) + case "local": match args.command: case "kernel": diff --git a/builder/clients/github.py b/builder/clients/github.py index e99e749..d34dbf4 100644 --- a/builder/clients/github.py +++ b/builder/clients/github.py @@ -30,6 +30,7 @@ def direct_url(self) -> str: def run(self) -> str | None: """Get the latest version of an artifact from GitHub project.""" response = requests.get(self.endpoint).json() + # this will check whether the GitHub API usage is exceeded try: data = response["message"] @@ -40,14 +41,17 @@ def run(self) -> str | None: ) except Exception: pass + try: # get direct download URL and optionally filter it with the given parameter data = response["assets"] browser_download_urls = [] + for elem in data: url_dto = elem["browser_download_url"] if url_dto and self.file_filter in url_dto: browser_download_urls.append(url_dto) + # if there is more than one fitting response -- throw an error if len(browser_download_urls) > 1: msg.error( @@ -56,16 +60,24 @@ def run(self) -> str | None: ) else: data = "".join(browser_download_urls) + except Exception: # if not available via API -- use regular "git clone" - rdir = Path(dcfg.assets, self.direct_url.rsplit("/", 1)[1]) msg.note(f"Non-API GitHub resolution for {self.project}") + + rdir = Path(dcfg.assets, self.direct_url.rsplit("/", 1)[1]) + cm.remove(rdir) - ccmd.launch(f"git clone --depth 1 --remote-submodules --recurse-submodules --shallow-submodules {self.direct_url} {rdir}") + ccmd.launch( + "git clone --depth 1 --remote-submodules --recurse-submodules --shallow-submodules {} {}" + .format(self.direct_url, rdir) + ) os.chdir(rdir) cm.remove(".git*") os.chdir(dcfg.assets) shutil.make_archive(str(rdir), "zip", rdir) cm.remove(rdir) + return None + return data diff --git a/builder/clients/rom_api.py b/builder/clients/rom_api.py index 0823f70..bc39e36 100644 --- a/builder/clients/rom_api.py +++ b/builder/clients/rom_api.py @@ -30,6 +30,7 @@ def map_codename(self) -> str: def run(self) -> str: data = requests.get(self.endpoint) + try: data = data.json()[self.json_key][0]["url"] except Exception: @@ -38,4 +39,5 @@ def run(self) -> str: f"Could not connect to {self.rom_name} API, HTTP status code: {data.status_code}", dont_exit=exit_flag ) + return str(data) diff --git a/builder/commands/bundle.py b/builder/commands/bundle.py index ff251d1..d70281e 100644 --- a/builder/commands/bundle.py +++ b/builder/commands/bundle.py @@ -14,8 +14,7 @@ class BundleCommand(BaseModel, ICommand, IBundleCommand): - """Command that packages the artifacts produced both - by 'kernel_builder' and 'assets_collector' core modules. + """Command that packages the artifacts produced both by 'kernel_builder' and 'assets_collector' core modules. :param str base: Kernel source base. :param str lkv: Linux kernel version. @@ -60,10 +59,12 @@ def collect_assets(self, rom_name: str, chroot: Literal["full", "minimal"]) -> N ac.run() def conan_sources(self) -> None: - sourcedir = dcfg.root / "source" print("\n", end="") msg.note("Copying sources for Conan packaging..") + + sourcedir = dcfg.root / "source" cm.remove(str(sourcedir)) + # NOTE: .venv is intentionally kept here, for Python environment repoducibility fo.ucopy( dcfg.root, sourcedir, @@ -72,25 +73,32 @@ def conan_sources(self) -> None: dcfg.assets, "__pycache__", ".vscode", + ".coverage", + ".pytest_cache", + ".ruff_cache", "source", "localversion", - "conanfile.py", + "conanfile.py" ) ) + msg.done("Done!") @staticmethod def conan_options(json_file: str) -> dict: with open(json_file, encoding="utf-8") as f: json_data = json.load(f) + return json_data def conan_package(self, options: tuple[str, ...], reference: str) -> None: cmd = f"conan export-pkg . {reference}" + for option_value in options: # not the best solution, but will work temporarily for 'rom' and 'chroot' options option_name = "rom" if not any(c.isalpha() for c in option_value) else "chroot" cmd += f" -o {option_name}={option_value}" + # add codename as an option separately cmd += f" -o codename={self.codename}" ccmd.launch(cmd) @@ -103,16 +111,20 @@ def conan_upload(reference: str) -> None: cmd = f"conan remote add {alias} {url} && "\ f"conan user -p {os.getenv('CONAN_PASSWORD')} -r {alias} {os.getenv('CONAN_LOGIN_USERNAME')} && "\ f"conan upload -f {reference} -r {alias}" + ccmd.launch(cmd) def execute(self) -> None: os.chdir(dcfg.root) + # determine the bundle type and process it match self.package_type: case "slim" | "full": self.build_kernel(self.base) + # "full" chroot is hardcoded here self.collect_assets(self.base, "full") + # clean up if dcfg.bundle.is_dir(): contents = dcfg.bundle.glob("*") @@ -120,27 +132,34 @@ def execute(self) -> None: os.remove(f) else: os.makedirs(dcfg.bundle) + # copy kernel kfn = "".join(os.listdir(dcfg.kernel)) shutil.copy(dcfg.kernel / kfn, dcfg.bundle / kfn) + # move assets (and not copy because they are way too big) for afn in os.listdir(dcfg.assets): # here, because of their size assets are moved and not copied shutil.move(dcfg.assets / afn, dcfg.bundle / afn) + case "conan": # form Conan reference name = "zero_kernel" version = os.getenv("KVERSION") user = self.codename channel = "" + if ccmd.launch("git branch --show-current", get_output=True) == "main": channel = "stable" else: channel = "testing" + reference = f"{name}/{version}@{user}/{channel}" + # form option sets chroot = ("minimal", "full") option_sets = list(itertools.product([self.base], chroot)) + # build and upload Conan packages for opset in option_sets: self.build_kernel(opset[0]) @@ -148,8 +167,10 @@ def execute(self) -> None: self.conan_sources() self.collect_assets(opset[0], opset[1]) self.conan_package(opset, reference) + # upload packages if os.getenv("CONAN_UPLOAD_CUSTOM") == "1": self.conan_upload(reference) + # navigate back to root directory os.chdir(dcfg.root) diff --git a/builder/configs/argument.py b/builder/configs/argument.py index 1384099..813edfb 100644 --- a/builder/configs/argument.py +++ b/builder/configs/argument.py @@ -43,7 +43,7 @@ class ArgumentConfig(BaseModel): def check_settings(self) -> None: """Run settings validations.""" # allow only asset colletion on a non-Linux machine - if self.benv == "local" and self.command in ("kernel", "bundle"): + if self.benv == "local" and self.command in {"kernel", "bundle"}: if not platform.system() == "Linux": msg.error("Can't build kernel on a non-Linux machine.") else: @@ -52,15 +52,20 @@ def check_settings(self) -> None: ccmd.launch("apt --version", loglvl="quiet") except Exception: msg.error("Detected Linux distribution is not Debian-based.") + # check if specified device is supported - with open(Path(__file__).absolute().parents[2] / "builder" / "manifests" / "devices.json", encoding="utf-8") as f: + with open( + Path(__file__).absolute().parents[2] / "builder" / "manifests" / "devices.json", encoding="utf-8" + ) as f: devices = json.load(f) + if self.codename not in devices.keys(): msg.error("Unsupported device codename specified.") if self.command == "bundle": # check Conan-related argument usage if self.package_type != "conan" and self.conan_upload: msg.error("Cannot use Conan-related arguments with non-Conan packaging\n") + # check that the provided defconfig file is valid if self.defconfig and not self.defconfig.is_file(): msg.error("Provided path to defconfig is invalid.") diff --git a/builder/core/assets_collector.py b/builder/core/assets_collector.py index b8f141f..f12dee9 100644 --- a/builder/core/assets_collector.py +++ b/builder/core/assets_collector.py @@ -41,6 +41,7 @@ def assets(self) -> list: # define Disable_Dm-Verity_ForceEncrypt and SU manager dfd = GithubApiClient(project="seppzer0/Disable_Dm-Verity_ForceEncrypt") su_manager = "tiann/KernelSU" if self.ksu else "topjohnwu/Magisk" + # process the "ROM-only" download for non-universal kernel bases if self.rom_only: if not self.rom_collector_dto: @@ -50,6 +51,7 @@ def assets(self) -> list: print("\n", end="") msg.done("ROM-only asset collection specified") return [self.rom_collector_dto.run(), dfd] + # process the full download else: assets = [ @@ -90,14 +92,18 @@ def assets(self) -> list: "https://github.com/mozilla-mobile/firefox-android/releases/download/fenix-v117.1.0/fenix-117.1.0-arm64-v8a.apk", "https://f-droid.org/F-Droid.apk", ] + # add ROM if kernel base is not universal if self.rom_collector_dto: assets.append(self.rom_collector_dto.run()) # type: ignore + return assets + return None def _check(self) -> None: os.chdir(dcfg.root) + # directory check if not dcfg.assets.is_dir(): os.makedirs(dcfg.assets) @@ -105,6 +111,7 @@ 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": msg.note("Cleaning 'assets' directory..") @@ -116,21 +123,25 @@ def _check(self) -> None: msg.cancel("Cancelling asset download.") case _: msg.error("Invalid option selected.") + print("\n", end="") def run(self) -> None: - os.chdir(dcfg.root) msg.banner("zero asset collector") + + os.chdir(dcfg.root) self._check() os.chdir(dcfg.assets) # NOTE: call "self.assets" only once! assets = self.assets + if isinstance(assets, list) or isinstance(assets, tuple): for e in assets: if isinstance(e, GithubApiClient): e.run() else: fo.download(e) + print("\n", end="") msg.done("Assets collected!") os.chdir(dcfg.root) diff --git a/builder/core/kernel_builder.py b/builder/core/kernel_builder.py index 8f16b5c..e1e9e77 100644 --- a/builder/core/kernel_builder.py +++ b/builder/core/kernel_builder.py @@ -51,6 +51,7 @@ def _defconfig(self) -> Path: # return custom defconfig if it is specified if self.defconfig: return Path(self.defconfig.name) + # list of the available defconfigs op7_defconfigs = { "los": "lineage_sm8150_defconfig", @@ -60,6 +61,7 @@ def _defconfig(self) -> Path: "pa": "vendor/paranoid_defconfig" if self.lkv == "4.14" else "paranoid_defconfig", "x": "msm8998_oneplus_android_defconfig" if self.lkv == "4.14" else "oneplus5_defconfig" } + # convert output to path object if "guacamole" in self.codename: return Path(op7_defconfigs[self.base]) @@ -71,16 +73,20 @@ def _defconfig(self) -> Path: def clean_build(self) -> None: print("\n", end="") msg.note("Cleaning the build environment..") + cm.git(self.rmanager.paths[self.codename]) cm.git(self.rmanager.paths["AnyKernel3"]) cm.git(self.rmanager.paths["KernelSU"]) + for fn in os.listdir(): if fn == "localversion" or fn.endswith(".zip"): cm.remove(fn) + msg.done("Done!") def patch_strict_prototypes(self) -> None: msg.note("Patching sources for Clang 15+ compatibility..") + data = { self.rmanager.paths[self.codename] /\ "drivers" /\ @@ -195,8 +201,9 @@ def patch_strict_prototypes(self) -> None: "mdss_util.c": ("struct mdss_util_intf *mdss_get_util_intf()",) } + # the following files are not present in 4.14 - if self._linux_kernel_version != "4.14": + if self._lkv_src != "4.14": extra_non_414 = { self.rmanager.paths[self.codename] /\ "drivers" /\ @@ -213,6 +220,7 @@ def patch_strict_prototypes(self) -> None: ("int msm_thermal_ioctl_init()", "void msm_thermal_ioctl_cleanup()",), } data.update(extra_non_414) + # PA needs this, LineageOS does not if self.base == "pa": extra_pa = { @@ -235,21 +243,23 @@ def patch_strict_prototypes(self) -> None: ("struct wlan_cfg_dp_soc_ctxt *wlan_cfg_soc_attach()",), } data.update(extra_pa) + # start the patching process contents = "" for fname, funcnames in data.items(): with open(fname, "r", encoding="utf-8") as f: contents = f.read() - # replace: "()" -> "(void)" for func in funcnames: contents = contents.replace(func, func.replace("()", "(void)")) with open(fname, "w") as f: f.write(contents) + msg.done("Done!") def patch_anykernel3(self) -> None: cm.remove(self.rmanager.paths["AnyKernel3"] / "ramdisk") cm.remove(self.rmanager.paths["AnyKernel3"] / "models") + fo.ucopy( dcfg.root / "builder" / "modifications" / self._ucodename / "anykernel3" / "ramdisk", self.rmanager.paths["AnyKernel3"] / "ramdisk" @@ -272,18 +282,20 @@ def patch_rtl8812au_source_mod_v5642(self) -> None: ), ( "EXTRA_CFLAGS += -Wno-parentheses-equality", - "EXTRA_CFLAGS += -Wno-pointer-bool-conversion\nEXTRA_CFLAGS += -Wno-pointer-bool-conversion\nEXTRA_CFLAGS += -Wno-pragma-pack\nEXTRA_CFLAGS += -Wno-unused-variable", - '$(MAKE) ARCH=$(ARCH) SUBARCH=$(ARCH) REAL_CC=${CC_DIR}/clang CLANG_TRIPLE=aarch64-linux-gnu- CROSS_COMPILE=$(CROSS_COMPILE) -C $(KSRC) M=$(shell pwd) O="$(KBUILD_OUTPUT)" modules', + "EXTRA_CFLAGS += -Wno-pointer-bool-conversion\nEXTRA_CFLAGS += -Wno-pointer-bool-conversion\nEXTRA_CFLAGS += -Wno-pragma-pack\nEXTRA_CFLAGS += -Wno-unused-variable", # noqa: E501 + '$(MAKE) ARCH=$(ARCH) SUBARCH=$(ARCH) REAL_CC=${CC_DIR}/clang CLANG_TRIPLE=aarch64-linux-gnu- CROSS_COMPILE=$(CROSS_COMPILE) -C $(KSRC) M=$(shell pwd) O="$(KBUILD_OUTPUT)" modules', # noqa: E501 "CONFIG_PLATFORM_I386_PC = n", "CONFIG_PLATFORM_ANDROID_ARM64 = y\nCONFIG_CONCURRENT_MODE = n", ) ) + # ioctl_cfg80211.h fo.replace_lines( Path("os_dep", "linux", "ioctl_cfg80211.h").absolute(), ("#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)) && (LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0))",), ("#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)) && (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0))",) ) + # ioctl_cfg80211.c fo.replace_lines( Path("os_dep", "linux", "ioctl_cfg80211.c").absolute(), @@ -308,6 +320,7 @@ def update_defconfig(self) -> None: "arm64" /\ "configs" /\ self._defconfig + # base changes (Wi-Fi + RTL8812AU) extra_configs = [ "CONFIG_88XXAU=y", @@ -320,6 +333,7 @@ def update_defconfig(self) -> None: "CONFIG_RTL8187=y", "CONFIG_RTLWIFI=y", ] + # KernelSU changes if self.ksu: extra_configs.extend([ @@ -332,6 +346,7 @@ def update_defconfig(self) -> None: "CONFIG_HAVE_KPROBES=y", "CONFIG_KPROBE_EVENTS=y", ]) + # apply changes with open(defconfig, "a", encoding="utf-8") as f: f.write("\n".join(extra_configs)) @@ -349,6 +364,7 @@ def patch_rtl8812au(self) -> None: "realtek" /\ "rtl8812au" ) + # modify sources depending on driver version os.chdir( self.rmanager.paths[self.codename] /\ @@ -361,6 +377,7 @@ def patch_rtl8812au(self) -> None: self.patch_rtl8812au_source_mod_v5642() cm.remove(".git*") os.chdir(dcfg.root) + # include the driver into build process makefile = self.rmanager.paths[self.codename] /\ "drivers" /\ @@ -378,12 +395,13 @@ def patch_rtl8812au(self) -> None: fo.insert_before_line( kconfig, "endif", - "source \"drivers/net/wireless/realtek/rtl8812au/Kconfig\"" + 'source "drivers/net/wireless/realtek/rtl8812au/Kconfig"' ) def patch_ksu(self) -> None: msg.note("Adding KernelSU into the kernel..") - # extract KSU_GIT_VERSION environment variable manually + + # extract KSU version manually and include it via symlink goback = Path.cwd() os.chdir(self.rmanager.paths["KernelSU"]) os.environ["KSU_GIT_VERSION"] = str( @@ -391,43 +409,48 @@ def patch_ksu(self) -> None: 10000 + int(ccmd.launch("git rev-list --count HEAD", get_output=True)) + 200 # type: ignore ) os.chdir(goback) + makefile = self.rmanager.paths[self.codename] /\ "drivers" /\ "Makefile" kconfig = self.rmanager.paths[self.codename] /\ "drivers" /\ "Kconfig" - # include into the build process via symlink + os.symlink( self.rmanager.paths["KernelSU"] / "kernel", self.rmanager.paths[self.codename] /\ "drivers" /\ "kernelsu" ) + with open(makefile, "a", encoding="utf-8") as f: f.write("obj-$(CONFIG_KSU) += kernelsu/\n") fo.insert_before_line( kconfig, "endmenu", - "source \"drivers/kernelsu/Kconfig\"" + 'source "drivers/kernelsu/Kconfig"' ) + # either patch kernel or KernelSU sources, depending on Linux kernel version - target_dir = dcfg.root / "KernelSU" if self._linux_kernel_version == "4.14" else self.rmanager.paths[self.codename] + target_d = dcfg.root / "KernelSU" if self._lkv_src == "4.14" else self.rmanager.paths[self.codename] fo.ucopy( - dcfg.root / "builder" / "modifications" / self._ucodename / self._linux_kernel_version / "kernelsu-compat.patch", - target_dir + dcfg.root / "builder" / "modifications" / self._ucodename / self._lkv_src / "kernelsu-compat.patch", + target_d ) - os.chdir(target_dir) + os.chdir(target_d) fo.apply_patch("kernelsu-compat.patch") os.chdir(goback) def patch_qcacld(self) -> None: goback = Path.cwd() + fo.ucopy( - dcfg.root / "builder" / "modifications" / self._ucodename / self._linux_kernel_version / "qcacld_pa.patch", + dcfg.root / "builder" / "modifications" / self._ucodename / self._lkv_src / "qcacld_pa.patch", self.rmanager.paths[self.codename] ) os.chdir(self.rmanager.paths[self.codename]) + fo.apply_patch("qcacld_pa.patch") os.chdir(goback) @@ -439,6 +462,7 @@ def patch_ioctl(self) -> None: "ipa" /\ "ipa_v3" /\ "ipa.c" + fo.replace_lines( ioctl.absolute(), (" u8 header[128] = { 0 };",), @@ -449,43 +473,53 @@ def patch_kernel(self) -> None: # -Wstrict-prototypes patch to build with Clang 15+ clang_cmd = f'{self.rmanager.paths["clang"] / "bin" / "clang"} --version' clang_ver = str(ccmd.launch(clang_cmd, get_output=True)).split("clang version ")[1].split(".")[0] + if int(clang_ver) >= 15: self.patch_strict_prototypes() + # apply .patch files fo.ucopy( - dcfg.root / "builder" / "modifications" / self._ucodename / self._linux_kernel_version, + dcfg.root / "builder" / "modifications" / self._ucodename / self._lkv_src, self.rmanager.paths[self.codename], ("kernelsu-compat.patch", "qcacld_pa.patch") ) os.chdir(self.rmanager.paths[self.codename]) + for pf in Path.cwd().glob("*.patch"): fo.apply_patch(pf) + # add support for CONFIG_MAC80211 kernel option data = "" files = ("tx.c", "mlme.c") + for fn in files: with open(Path("net", "mac80211", fn), "r", encoding="utf-8") as f: data = f.read().replace("case IEEE80211_BAND_60GHZ:", "case NL80211_BAND_60GHZ:") with open(Path("net", "mac80211", fn), "w", encoding="utf-8") as f: f.write(data) + # some patches only for ParanoidAndroid if self.base == "pa": - if self._linux_kernel_version == "4.4": + if self._lkv_src == "4.4": self.patch_qcacld() self.patch_ioctl() + os.chdir(dcfg.root) def patch_all(self) -> None: self.patch_anykernel3() self.patch_kernel() + # optionally include KernelSU support if self.ksu: self.patch_ksu() + self.patch_rtl8812au() + if self.defconfig: msg.note("Custom defconfig provided, copying..") fo.ucopy( - self.defconfig, + self.defconfig, self.rmanager.paths[self.codename] /\ "arch" /\ "arm64" /\ @@ -494,12 +528,15 @@ def patch_all(self) -> None: ) else: self.update_defconfig() + msg.done("Patches added!") def build(self) -> None: print("\n", end="") msg.note("Launching the build..") + os.chdir(self.rmanager.paths[self.codename]) + # launch "make" punits = ccmd.launch("nproc --all", get_output=True) cmd1 = "make -j{} O=out {} "\ @@ -519,30 +556,38 @@ def build(self) -> None: "CXX=clang++ "\ "AS=llvm-as"\ .format(punits) + # for PA's 4.14, extend the "make" command with additional variables - if (self.base, self._linux_kernel_version) == ("pa", "4.14"): + if (self.base, self._lkv_src) == ("pa", "4.14"): cmd2 = f"{cmd2} LEX=flex YACC=bison" + # launch and time the build process time_start = time.time() ccmd.launch(cmd1) ccmd.launch(cmd2) time_stop = time.time() time_elapsed = time_stop - time_start + # convert elapsed time into human readable format secs = time_elapsed % (24 * 3600) hours = secs // 3600 secs %= 3600 mins = secs // 60 secs %= 60 + msg.done("Done! Time spent for the build: %02d:%02d:%02d" % (hours, mins, secs)) @property - def _linux_kernel_version(self) -> str: + def _lkv_src(self) -> str: + """Linux kernel version in kernel source.""" data = "" version = [] + with open(self.rmanager.paths[self.codename] / "Makefile", encoding="utf-8") as f: data = f.read() + params = ("VERSION", "PATCHLEVEL") + # find the required lines in a single data run-through for line in data.splitlines(): for p in params: @@ -551,11 +596,13 @@ def _linux_kernel_version(self) -> str: # stop the loop when all values are found if len(version) == len(params): break + return ".".join(version) def create_zip(self) -> None: print("\n", end="") msg.note("Forming final ZIP file..") + fo.ucopy( self.rmanager.paths[self.codename] /\ "out" /\ @@ -565,37 +612,48 @@ def create_zip(self) -> None: "Image.gz-dtb", self.rmanager.paths["AnyKernel3"] / "Image.gz-dtb" ) + # define kernel versions: Linux and internal - verbase = self._linux_kernel_version + verbase = self._lkv_src ver_int = os.getenv("KVERSION") + # create the final ZIP file name_suffix = "-ksu" if self.ksu else "" name_full = f'{os.getenv("KNAME", "zero")}-{ver_int}-{self._ucodename}-{self.base}-{verbase}{name_suffix}' kdir = dcfg.root / dcfg.kernel + if not kdir.is_dir(): os.makedirs(kdir) + os.chdir(self.rmanager.paths["AnyKernel3"]) + # this is not the best solution, but is the easiest cmd = f"zip -r9 {kdir / name_full}.zip . -x *.git* *README* *LICENSE* *placeholder" ccmd.launch(cmd) os.chdir(dcfg.root) + msg.done("Done!") def run(self) -> None: os.chdir(dcfg.root) msg.banner("zero kernel builder") msg.note("Setting up tools and links..") + self.rmanager.read_data() self.rmanager.generate_paths() self.rmanager.download() self.rmanager.export_path() self.clean_build() + if self.clean_kernel: sys.exit(0) + self.write_localversion() msg.done("Done! Tools are configured!") - if self.lkv != self._linux_kernel_version: + + if self.lkv != self._lkv_src: msg.error("Linux kernel version in sources is different what was specified in arguments") + self.patch_all() self.build() self.create_zip() diff --git a/builder/engines/generic_container.py b/builder/engines/generic_container.py index 1c72d84..1f075f4 100644 --- a/builder/engines/generic_container.py +++ b/builder/engines/generic_container.py @@ -70,12 +70,13 @@ def dir_bundle_conan(self) -> Path: def check_cache(self) -> bool: img_cache_cmd = f'{self.benv} images --format {"{{.Repository}}"}' img_cache = str(ccmd.launch(img_cache_cmd, get_output=True)) + return True if self._name_image in img_cache else False @property def builder_cmd(self) -> str: # prepare launch command - cmd = f"uv run {Path('builder', 'utils', 'bridge.py')}" + cmd = f"python3 {Path('builder', 'utils', 'bridge.py')}" arguments = { "--command": self.command, "--codename": self.codename, @@ -89,6 +90,7 @@ def builder_cmd(self) -> str: "--clean-assets": self.clean_assets, "--defconfig": self.defconfig, } + # extend the command with given arguments for arg, value in arguments.items(): # arguments that have a string value @@ -97,12 +99,14 @@ def builder_cmd(self) -> str: # arguments that act like boolean switches elif value: cmd += f" {arg}" + # extend the command with the selected packaging option if self.command == "bundle": if self.package_type in ("slim", "full"): cmd += f" && chmod 777 -R {self._wdir_container / dcfg.bundle.name}" else: cmd += " && chmod 777 -R /root/.conan" + return cmd @property @@ -115,8 +119,10 @@ def container_options(self) -> list[str]: "-e LOGLEVEL={}".format(os.getenv("LOGLEVEL")), "-w {}".format(self._wdir_container), ] + # define volume mounting template v_template = "-v {}:{}/{}" + # mount directories match self.command: case "kernel": @@ -135,6 +141,7 @@ def container_options(self) -> list[str]: options.append(f'-v {self.dir_bundle_conan}:/"/root/.conan"') else: msg.error("Could not find Conan local cache on the host machine.") + return options def create_dirs(self) -> None: @@ -154,16 +161,19 @@ def create_dirs(self) -> None: def build_image(self) -> str | None | CompletedProcess: print("\n") msg.note(f"Building the {self.benv.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._wdir_local / 'Dockerfile', + self._wdir_local / "Dockerfile", self._name_image ) + res = ccmd.launch(cmd) msg.done("Done!") print("\n") + return res @property @@ -179,16 +189,19 @@ def __enter__(self) -> str: # prepare Docker if selected if self.benv == "docker": self._force_buildkit() + # build the image and prepare directories if not self.check_cache(): self.build_image() else: msg.note(f"\n{self.benv.capitalize()} image already in local cache, skipping it's build..\n") + self.create_dirs() return self.get_container_cmd def __exit__(self, exc_type, exc_value, traceback) -> None: # navigate to root directory and clean image from host machine os.chdir(self._wdir_local) + if self.clean_image: ccmd.launch(f"{self.benv} rmi {self._name_image}") diff --git a/builder/managers/resource.py b/builder/managers/resource.py index 3a8b222..cf14211 100644 --- a/builder/managers/resource.py +++ b/builder/managers/resource.py @@ -26,20 +26,20 @@ class ResourceManager(BaseModel, IResourceManager): lkv: Optional[str] = None base: Optional[str] = None - def __getitem__(self, arg: Path) -> slice: - """Custom getitem implementation for accessing data via Path-type indexes.""" - return slice(*[{True: lambda n: None, False: int}[x == ""](x) for x in (str(arg).split(":") + ["", "", ""])[:3]]) - def read_data(self) -> None: os.chdir(dcfg.root) + # define paths tools = "" device = "" + # load JSON data with open(dcfg.root / "builder" / "manifests" / "tools.json", encoding="utf-8") as f: tools = json.load(f) + # codename and ROM are undefined only when the Docker/Podman image is being prepared if self.codename and self.base: + with open(dcfg.root / "builder" / "manifests" / "devices.json", encoding="utf-8") as f: data = json.load(f) # load data only for the required codename + linux kernel version combination @@ -48,8 +48,10 @@ def read_data(self) -> None: except Exception: msg.error("Arguments were specified for an unsupported build, exiting..") device = {self.codename: data[self.codename][self.lkv][self.base]} + # join tools and devices manifests self._data = {**tools, **device} + else: self._data = tools msg.note("Only shared tools are installed.") @@ -64,6 +66,7 @@ def download(self) -> None: # break data into individual required vars path = Path(self._data[e]["path"]) # type: ignore url = self._data[e]["url"] # type: ignore + # break further processing into "generic" and "git" groups ftype = self._data[e]["type"] # type: ignore match ftype: @@ -73,20 +76,30 @@ def download(self) -> None: if path.name not in os.listdir(): fn = url.split("/")[-1] dn = fn.split(".")[0] + if fn not in os.listdir() and dn not in os.listdir(): fo.download(url) + msg.note(f"Unpacking {fn}..") + with tarfile.open(fn) as f: f.extractall(path) + cm.remove(fn) + msg.done("Done!") + else: msg.note(f"Found an existing path: {path}") + case "git": # break data into individual vars branch = self._data[e]["branch"] # type: ignore commit = self._data[e]["commit"] # type: ignore - cmd = f"git clone -b {branch} --depth 1 --remote-submodules --recurse-submodules --shallow-submodules {url} {path}" + cmd = \ + "git clone -b {} --depth 1 --remote-submodules --recurse-submodules --shallow-submodules {} {}"\ + .format(branch, url, path) + # full commit history is required in two instances: # - for KernelSU -- to define it's version based on *full* commit history; # - for commit checkout -- to checkout a specific commit in the history. @@ -102,6 +115,7 @@ def download(self) -> None: os.chdir(dcfg.root) else: msg.note(f"Found an existing path: {path}") + case _: msg.error("Invalid resource type detected. Use only: generic, git.") @@ -109,4 +123,5 @@ def export_path(self) -> None: for elem in self.paths: p = self.paths[elem] pathenv = str(f"{p}/bin/") + os.environ["PATH"] = pathenv + os.pathsep + os.getenv("PATH") # type: ignore diff --git a/builder/modifications/dumplinger/anykernel3/anykernel.sh b/builder/modifications/dumplinger/anykernel3/anykernel.sh index 52e8422..b751bd0 100644 --- a/builder/modifications/dumplinger/anykernel3/anykernel.sh +++ b/builder/modifications/dumplinger/anykernel3/anykernel.sh @@ -12,50 +12,36 @@ do.cleanup=1 do.cleanuponabort=0 device.name1=dumpling device.name2=cheeseburger -supported.versions=13 - 14 +supported.versions=14 supported.patchlevels= '; } # end properties + ### AnyKernel install -# begin attributes -attributes() { -set_perm_recursive 0 0 755 644 $ramdisk/*; -set_perm_recursive 0 0 750 750 $ramdisk/init* $ramdisk/sbin; +## boot files attributes +boot_attributes() { +set_perm_recursive 0 0 755 644 $RAMDISK/*; +set_perm_recursive 0 0 750 750 $RAMDISK/init* $RAMDISK/sbin; } # end attributes - -## boot shell variables -block=/dev/block/bootdevice/by-name/boot; -is_slot_device=0; -ramdisk_compression=auto; -patch_vbmeta_flag=auto; +# boot shell variables +BLOCK=/dev/block/bootdevice/by-name/boot; +IS_SLOT_DEVICE=0; +RAMDISK_COMPRESSION=auto; +PATCH_VBMETA_FLAG=auto; # import functions/variables and setup patching - see for reference (DO NOT REMOVE) -. tools/ak3-core.sh && attributes; +. tools/ak3-core.sh; # boot install dump_boot; # use split_boot to skip ramdisk unpack, e.g. for devices with init_boot ramdisk -# patch dtbo.img -username="$(file_getprop /system/build.prop "ro.build.user")"; -echo "Found user: $username"; -case "$username" in - "android-build") user=google;; - *) user=custom;; -esac; -hostname="$(file_getprop /system/build.prop "ro.build.host")"; -echo "Found host: $hostname"; -case "$hostname" in - *corp.google.com|abfarm*) host=google;; - *) host=custom;; -esac; - # begin ramdisk changes -backup_file init.rc; -insert_line init.rc "init.nethunter.rc" after "import /init.usb.configfs.rc" "import /init.nethunter.rc"; +#backup_file init.rc; +#insert_line init.rc "init.nethunter.rc" after "import /init.usb.configfs.rc" "import /init.nethunter.rc"; -backup_file ueventd.rc; -insert_line ueventd.rc "/dev/hidg" after "/dev/pmsg0" "/dev/hidg* 0666 root root"; +#backup_file ueventd.rc; +#insert_line ueventd.rc "/dev/hidg" after "/dev/pmsg0" "/dev/hidg* 0666 root root"; # end ramdisk changes write_boot; diff --git a/builder/tools/cleaning.py b/builder/tools/cleaning.py index bcb2e5b..d64a0f5 100644 --- a/builder/tools/cleaning.py +++ b/builder/tools/cleaning.py @@ -20,16 +20,19 @@ def remove(elements: str | Path | list[Path | str]) -> None: # if a given argument is a string --> convert it into a one-element list if isinstance(elements, str) or isinstance(elements, Path): elements = [str(elements)] + for e in elements: # force convert into str e = str(e) - # a simple list-through removal + + # list-through removal if "*" not in e: if os.path.isdir(e): shutil.rmtree(e, onerror=on_rm_error) elif os.path.isfile(e): os.remove(e) - # a recursive "glob" removal + + # recursive "glob" removal else: for fn in glob.glob(e): remove(fn) @@ -37,7 +40,7 @@ def remove(elements: str | Path | list[Path | str]) -> None: 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. :exc_info param: Misc info. @@ -52,6 +55,7 @@ def git(directory: Path | str) -> None: :param Path/str directory: Path to the directory. """ goback = Path.cwd() + os.chdir(directory) ccmd.launch("git clean -fdx") ccmd.launch("git reset --hard HEAD") @@ -78,12 +82,15 @@ def root(extra: Optional[list[str]] = []) -> None: "multi-build", ".coverage", ".vscode", - ".pytest_cache" + ".pytest_cache", + ".ruff_cache" ] + # add extra elements to clean up from root directory if extra: for e in extra: trsh.append(dcfg.root / e) - # clean + + # clean, with __pycache__ always remove(trsh) [remove(p) for p in Path(".").rglob("__pycache__")] diff --git a/builder/tools/commands.py b/builder/tools/commands.py index cb12019..0dabba4 100644 --- a/builder/tools/commands.py +++ b/builder/tools/commands.py @@ -21,17 +21,21 @@ def launch( cstdout = subprocess.DEVNULL if loglvl == "quiet" else os.getenv("OSTREAM", None) if get_output is True: cstdout = subprocess.PIPE + # if output stream is a file -- open it if isinstance(cstdout, str): cstdout = open(cstdout, "a") if loglvl == "quiet" and os.getenv("OSTREAM"): msg.error("Cannot run 'quiet' build with file logging") + try: result = subprocess.run(cmd, shell=True, check=True, stdout=cstdout, stderr=subprocess.STDOUT) + # return only output if required if get_output is True: return result.stdout.decode("utf-8").rstrip() else: return result + except Exception as e: msg.error(f"Error executing command: {cmd} -> {e}") diff --git a/builder/tools/fileoperations.py b/builder/tools/fileoperations.py index f096608..997cf73 100644 --- a/builder/tools/fileoperations.py +++ b/builder/tools/fileoperations.py @@ -16,8 +16,10 @@ def ucopy(src: Path, dst: Path, exceptions: Optional[tuple[str | Path, ...]] = ( """ # for a directory (it's contents) if src.is_dir(): + if not dst.is_dir(): os.makedirs(dst) + contents = os.listdir(src) for e in contents: # do not copy restricted files @@ -28,6 +30,7 @@ def ucopy(src: Path, dst: Path, exceptions: Optional[tuple[str | Path, ...]] = ( shutil.copytree(src_e, dst_e) elif src_e.is_file(): shutil.copy(src_e, dst_e) + # for a single file elif src.is_file(): shutil.copy(src, dst) @@ -39,21 +42,28 @@ def download(url: str) -> None: :param str url: URL to the file. """ fn = url.split("/")[-1] + msg.note(f"Downloading {fn} ..") print(f" URL: {url}") + try: if "sourceforge" in url: msg.note("Sorceforge URL detected, using wget..") + fn = url.split("/download")[0].split("/")[-1] ccmd.launch(f"wget -O {fn} {url}") + else: with requests.get(url, stream=True, headers={"referer": url}) as r: r.raise_for_status() + with open(fn, "wb") as f: for chunk in r.iter_content(chunk_size=8192): f.write(chunk) + except Exception as e: msg.error(f"Download failed: {e}") + msg.done("Done!") @@ -65,6 +75,7 @@ def replace_lines(filename: Path, og_lines: tuple[str, ...], nw_lines: tuple[str :param tuple[str,...] nw_lines: New lines in place of original lines. """ filename_new = Path(str(filename) + "_new") + with open(filename, encoding="utf-8") as data: with open(filename_new, "w", encoding="utf-8") as new_data: for line in data: @@ -73,6 +84,7 @@ def replace_lines(filename: Path, og_lines: tuple[str, ...], nw_lines: tuple[str msg.note(f"Replacing {key} with {nw_lines[indx]}") line = line.replace(key, nw_lines[indx]) new_data.write(line) + os.replace(filename_new, filename) @@ -85,6 +97,7 @@ def replace_nth(filename: Path, og_string: str, nw_string: str, occurence: int) :param int occurence: The index of occurence to replace. """ filename_new = Path(str(filename) + "_new") + with open(filename, encoding="utf-8") as data: with open(filename_new, "w", encoding="utf-8") as new_data: counter = 0 @@ -95,6 +108,7 @@ def replace_nth(filename: Path, og_string: str, nw_string: str, occurence: int) msg.note(f"Replacing {og_string} with {nw_string}") line = line.replace(og_string, nw_string) new_data.write(line) + os.replace(filename_new, filename) @@ -108,13 +122,16 @@ def insert_before_line(filename: str | Path, pointer_line: str, new_line: str) - with open(filename, "r+", encoding="utf-8") as f: a = [x.rstrip() for x in f] index = 0 + for item in a: if item.startswith(pointer_line): a.insert(index, new_line) break index += 1 + f.seek(0) f.truncate() + for line in a: f.write(line + "\n") @@ -125,5 +142,6 @@ def apply_patch(filename: str | Path) -> None: :param str/Path filename: Name of the .patch file. """ msg.note(f"Applying patch: {filename}") + ccmd.launch(f"patch -p1 -s --no-backup-if-mismatch -i {filename}") os.remove(filename) diff --git a/builder/tools/messages.py b/builder/tools/messages.py index bf809f2..0a6b240 100644 --- a/builder/tools/messages.py +++ b/builder/tools/messages.py @@ -30,6 +30,7 @@ def error(text: str, dont_exit: bool = False) -> None: :param str text: Text to wrap. """ print(f"[ ! ] {text}", file=sys.stderr) + if not dont_exit: sys.exit(1) diff --git a/builder/utils/bridge.py b/builder/utils/bridge.py index 7d433cd..064e0f6 100644 --- a/builder/utils/bridge.py +++ b/builder/utils/bridge.py @@ -1,3 +1,10 @@ +""" +Bridge-connector to launch shift the build process from host to Docker/Podman container. + +Essentially it re-launches the same builder, but the one that is a copy inside the container. +Complete chain is: builder in host -> this bridge -> builder in container. +""" + import sys import argparse @@ -16,12 +23,13 @@ def parse_args() -> argparse.Namespace: Because of that, all of the arguments are technically optional. Making any of the arguments mandatory would not allow it to be dual-use. """ - parser = argparse.ArgumentParser() args = None if sys.argv[1:] else ["-h"] + + parser = argparse.ArgumentParser() parser.add_argument( "--command", help="select builder command", - choices=("kernel", "assets", "bundle") + choices={"kernel", "assets", "bundle"} ) parser.add_argument( "--codename", @@ -38,13 +46,13 @@ def parse_args() -> argparse.Namespace: parser.add_argument( "--chroot", help="select chroot type", - choices=("full", "minimal") + choices={"full", "minimal"} ) parser.add_argument( "--package-type", dest="package_type", help="select bundle packaging type", - choices=("conan", "slim", "full") + choices={"conan", "slim", "full"} ) parser.add_argument( "--clean-kernel", @@ -79,11 +87,13 @@ def parse_args() -> argparse.Namespace: help="only setup the shared tools in the environment", action="store_true" ) + return parser.parse_args(args) def main(args: argparse.Namespace) -> None: match args.command: + case "kernel": kc = KernelCommand( codename = args.codename, @@ -94,6 +104,7 @@ def main(args: argparse.Namespace) -> None: defconfig = args.defconfig, ) kc.execute() + case "assets": ac = AssetsCommand( codename = args.codename, @@ -104,6 +115,7 @@ def main(args: argparse.Namespace) -> None: ksu = args.ksu, ) ac.execute() + case "bundle": bc = BundleCommand( codename = args.codename, @@ -114,6 +126,7 @@ def main(args: argparse.Namespace) -> None: defconfig = args.defconfig, ) bc.execute() + case _: # if no command was selected, then shared tools are (supposed to be) installed if args.shared: @@ -121,6 +134,7 @@ def main(args: argparse.Namespace) -> None: rm.read_data() rm.generate_paths() rm.download() + else: # technically this part of code cannot be reached and is just an extra precaution msg.error("Invalid argument set specified, please review") diff --git a/conanfile.py b/conanfile.py index b6dd80d..ab6123e 100644 --- a/conanfile.py +++ b/conanfile.py @@ -10,9 +10,9 @@ class ZeroKernelConan(ConanFile): topics = ("zero_kernel", "kali-nethunter", "nethunter") settings = None options = { - "base": ("los", "pa", "x", "aosp"), - "chroot": ("minimal", "full"), - "codename": ("dumpling", "cheeseburger") + "base": {"los", "pa", "x", "aosp"}, + "chroot": {"minimal", "full"}, + "codename": {"dumpling", "cheeseburger"} } def export_sources(self): @@ -24,8 +24,8 @@ def build(self): self.options.codename, self.options.chroot ) - cmd = "uv run builder kernel {0} &&"\ - "uv run builder assets {0} --clean"\ + cmd = "python3 builder kernel {0} &&"\ + "python3 builder assets {0} --clean"\ .format(shared_args) print(f"[cmd] {cmd}") self.run(cmd) diff --git a/docs/TODO.md b/docs/TODO.md index bab013e..afc542f 100755 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -2,14 +2,20 @@ ## Kernel features +### Done + - [x] add monitor mode support for internal Wi-Fi chipset (with Wi-Fi packet injection); -- [ ] add DM-Verity and Force Encrypt disabler; -- [x] add KernelSU support; -- [ ] add DriveDroid(-like) support; -- [ ] add RTL88X2BU driver support. +- [x] add KernelSU support. +- [x] add DM-Verity and Force Encrypt disabler. + +### Left + +- [ ] add DriveDroid(-like) support. ## Build process features +### Done + - [x] add an asset downloader (ROM, Magisk, etc.); - [x] add CI/CD pipelines; - [x] add option to save logs to a file; @@ -19,7 +25,6 @@ - [x] improve file/directory cleaning mechanism in a form of a dedicated module; - [x] improve Clang download mechanism; - [x] add build counter mechanism for CI/CD pipelines; -- [ ] add published Conan package validator; - [x] add codename specific elements to final kernel zip; - [x] add a simpler and slimer bundle creator (kernel+ROM); - [x] add return types to functions; @@ -30,32 +35,33 @@ - [x] improve documentation; - [x] apply OOP paradigm; - [x] switch to uv for dependency management; -- [ ] create a commit-based lockfile system for reproducible kernel builds; - [x] switch to `pathlib`; -- [ ] switch to `raise` instead of `sys.exit`; - [x] add a FAQ page; - [x] refactor Docker/Podman command formation; -- [ ] refactor logging mechanism; - [x] fix Podman usage (.dockerignore); -- [ ] dedicate kernel source patchers as separate modules (LineageOS, AOSP, AOSPA etc); - [x] add a separate method for multiline patching in files; -- [ ] make kernel building and assets collection processes asynchronous when launching the `bundle` option; - [x] add a dataclass for wrapper's arguments; - [x] add PA ROM support; -- [ ] add a GH workflow for checking PRs; - [x] decompose `run()` method in `ContainerEngine`; - [x] skip building Docker/Podman image if it's already present in local cache; - [x] for containerized build, download the contents of manifests during image build; -- [ ] add system app debloater; - [x] add a new argument responsible for Linux kernel version selection; - [x] add 4.14 Linux kernel version builds; -- [ ] decompose `run` methods into separate methods as much as possible; +- [x] decompose `run` methods into separate methods as much as possible; - [x] switch to pydantic; -- [ ] new device support: OP9; -- [ ] add type checks with pyright; -- [ ] add unit tests with coverage checks; +- [x] add type checks with pyright; +- [x] add unit tests with coverage checks; +- [x] switch to `__enter__` and `__exit__` Python's magic methods for container engines. + +### Left + +- [ ] add published Conan package validator; +- [ ] create a commit-based lockfile system for reproducible kernel builds; +- [ ] dedicate kernel source patchers as separate modules (LineageOS, AOSP, AOSPA etc); +- [ ] make kernel building and assets collection processes asynchronous when launching the `bundle` option; +- [ ] add a GitHub workflow for checking PRs; +- [ ] add system app debloater; - [ ] move device-specific modifications into appropriate folder with custom Modificator (sub)classes; -- [ ] switch to `__enter__` and `__exit__` Python's magic methods for container engines; - [ ] switch to `exec` commands for Docker and Podman instead of a single `run` command; - [ ] replace `ccmd.launch()` to just `launch()` (or any other name); - [ ] embed newlines usage (from both sides) into `messages` functions as arguments; @@ -64,6 +70,4 @@ - [ ] investigate project restructuring to avoid circular import; - [ ] consider creating a separate "errors" subpackage for all errors; - [ ] add GKI kernels support; -- [ ] separate "standard" kernel building from NetHunter-specific modifications; -- [ ] add a differentiator/parameter to indicate whether the specified kernel source already has NetHunter patches; - [ ] use logging facility for logs. diff --git a/pyproject.toml b/pyproject.toml index 397c894..4d3818b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,8 @@ dev-dependencies = [ "pytest ~=8.0", "pytest-cov ~=4.1", "pyright ~=1.1", - "pylint ~=3.1" + "pylint ~=3.1", + "ruff ~=0.7" ] @@ -41,6 +42,31 @@ source = [ show_missing = true [tool.pyright] -include=["builder"] +include = ["builder"] pythonVersion = "3.12" +[tool.ruff] +include = ["builder/**/*.py", "scripts/**/*.py"] +indent-width = 4 +line-length = 120 +exclude = [ + ".git", + ".pytest_cache", + ".venv" +] +output-format = "full" + +[tool.ruff.lint] +select = ["E", "Q", "D", "E501"] +ignore = ["D1", "D200", "D212"] +task-tags = ["TODO", "NOTE"] + +[tool.ruff.lint.per-file-ignores] +"__init__.py" = ["F401"] + +[tool.ruff.lint.pydocstyle] +convention = "google" + +[tool.ruff.lint.flake8-quotes] +docstring-quotes = "double" +inline-quotes = "double" diff --git a/requirement-uv.txt b/requirement-uv.txt index e4e5976..c314288 100644 --- a/requirement-uv.txt +++ b/requirement-uv.txt @@ -1 +1 @@ -uv==0.4.10 +uv==0.5.21 diff --git a/scripts/common b/scripts/common index 783f5b0..e8d2136 160000 --- a/scripts/common +++ b/scripts/common @@ -1 +1 @@ -Subproject commit 783f5b0166891973497b1cd03652fd0e72284c2f +Subproject commit e8d2136c4c12d917a4fd7fb110382600545c51cc diff --git a/scripts/multi_build.py b/scripts/multi_build.py index b182687..b64f64c 100644 --- a/scripts/multi_build.py +++ b/scripts/multi_build.py @@ -1,3 +1,7 @@ +""" +Wrapper-script to launch the builder for multiple setting combinations. +""" + import os import shutil import argparse @@ -10,9 +14,10 @@ def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser() parser.add_argument( "--env", - choices=("docker", "podman", "local"), + choices={"docker", "podman", "local"}, default="docker" ) + return parser.parse_args() @@ -22,10 +27,12 @@ def rmove(src: Path, dst: Path) -> None: :param Path src: Source path. :param Path dst: Destination path. """ - # for a directory (it's contents) + # for a directory if src.is_dir(): + if not dst.is_dir(): os.makedirs(dst) + contents = os.listdir(src) for e in contents: # do not copy restricted files @@ -33,6 +40,7 @@ def rmove(src: Path, dst: Path) -> None: src_e = src / e dst_e = dst / e shutil.move(src_e, dst_e) + # for a single file elif src.is_file(): shutil.move(src, dst) @@ -84,10 +92,12 @@ def main(args: argparse.Namespace) -> None: "ksu": True }, ) + os.chdir(rootpath) dir_shared = rootpath / "multi-build" shutil.rmtree(dir_shared, ignore_errors=True) os.makedirs(dir_shared) + for count, argset in enumerate(argsets, 1): # define some of the values individually benv = f"--build-env {args.env}" @@ -97,13 +107,16 @@ def main(args: argparse.Namespace) -> None: ksu = "--ksu" if argset["ksu"] else "" size = "--package-type slim" if argset["command"] == "bundle" else "" extra = "--chroot minimal --rom-only --clean" if argset["command"] == "assets" else "" + # if the build is last, make it automatically remove the Docker/Podman image from runner clean_image = "--clean-image" if count == len(argsets) and args.env in ("docker", "podman") else "" + # form and launch the command - cmd = f"uv run builder {argset['command']} {benv} {base} {codename} {lkv} {size} {ksu} {clean_image} {extra}" + cmd = f"python3 builder {argset['command']} {benv} {base} {codename} {lkv} {size} {ksu} {clean_image} {extra}" print(f"[CMD]: {cmd}") subprocess.run(cmd.strip(), shell=True, check=True) - # copy artifacts into the shared directory + + # define directory to copy artifacts to out = "" match argset["command"]: case "bundle": diff --git a/uv.lock b/uv.lock index 42b9b92..c07eba3 100644 --- a/uv.lock +++ b/uv.lock @@ -1,9 +1,5 @@ version = 1 requires-python = ">=3.12" -resolution-markers = [ - "python_full_version < '3.13'", - "python_full_version >= '3.13'", -] [[package]] name = "annotated-types" @@ -25,26 +21,26 @@ wheels = [ [[package]] name = "astroid" -version = "3.2.4" +version = "3.3.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/53/1067e1113ecaf58312357f2cd93063674924119d80d173adc3f6f2387aa2/astroid-3.2.4.tar.gz", hash = "sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a", size = 397576 } +sdist = { url = "https://files.pythonhosted.org/packages/80/c5/5c83c48bbf547f3dd8b587529db7cf5a265a3368b33e85e76af8ff6061d3/astroid-3.3.8.tar.gz", hash = "sha256:a88c7994f914a4ea8572fac479459f4955eeccc877be3f2d959a33273b0cf40b", size = 398196 } wheels = [ - { url = "https://files.pythonhosted.org/packages/80/96/b32bbbb46170a1c8b8b1f28c794202e25cfe743565e9d3469b8eb1e0cc05/astroid-3.2.4-py3-none-any.whl", hash = "sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25", size = 276348 }, + { url = "https://files.pythonhosted.org/packages/07/28/0bc8a17d6cd4cc3c79ae41b7105a2b9a327c110e5ddd37a8a27b29a5c8a2/astroid-3.3.8-py3-none-any.whl", hash = "sha256:187ccc0c248bfbba564826c26f070494f7bc964fd286b6d9fff4420e55de828c", size = 275153 }, ] [[package]] name = "bandit" -version = "1.7.9" +version = "1.8.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "platform_system == 'Windows'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "pyyaml" }, { name = "rich" }, { name = "stevedore" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1c/a4/ee391b0f046a6d8919eef246aed7c39849e299cc2e50d918b54add397de6/bandit-1.7.9.tar.gz", hash = "sha256:7c395a436743018f7be0a4cbb0a4ea9b902b6d87264ddecf8cfdc73b4f78ff61", size = 4225771 } +sdist = { url = "https://files.pythonhosted.org/packages/9b/e2/c229cdb4eefc124e5b77ac2557eb0a3cb5b9fc89bc465dd2b8dc1033dbb8/bandit-1.8.2.tar.gz", hash = "sha256:e00ad5a6bc676c0954669fe13818024d66b70e42cf5adb971480cf3b671e835f", size = 4228832 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/a3/05820b7ce584a1fa01d887ec5e3274bee9f9e02a53aa63de3cb1a5ad7d24/bandit-1.7.9-py3-none-any.whl", hash = "sha256:52077cb339000f337fb25f7e045995c4ad01511e716e5daac37014b9752de8ec", size = 127957 }, + { url = "https://files.pythonhosted.org/packages/1c/c1/991a7a1404626558cc7db0cc34243e13e5e336eba053bf6979e9fd6006f7/bandit-1.8.2-py3-none-any.whl", hash = "sha256:df6146ad73dd30e8cbda4e29689ddda48364e36ff655dbfc86998401fcf1721f", size = 127049 }, ] [[package]] @@ -58,35 +54,46 @@ wheels = [ [[package]] name = "certifi" -version = "2024.8.30" +version = "2024.12.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 } +sdist = { url = "https://files.pythonhosted.org/packages/0f/bd/1d41ee578ce09523c81a15426705dd20969f5abf006d1afe8aeff0dd776a/certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db", size = 166010 } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, + { url = "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", size = 164927 }, ] [[package]] name = "charset-normalizer" -version = "3.3.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892 }, - { url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213 }, - { url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404 }, - { url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275 }, - { url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518 }, - { url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182 }, - { url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869 }, - { url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042 }, - { url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275 }, - { url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819 }, - { url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415 }, - { url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212 }, - { url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 }, - { url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041 }, - { url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397 }, - { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 }, +version = "3.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 }, + { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 }, + { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 }, + { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 }, + { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 }, + { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 }, + { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 }, + { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 }, + { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 }, + { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 }, + { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 }, + { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 }, + { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 }, + { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 }, + { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 }, + { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 }, + { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 }, + { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 }, + { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 }, + { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 }, + { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 }, + { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 }, + { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 }, + { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 }, + { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 }, + { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 }, + { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, ] [[package]] @@ -100,7 +107,7 @@ wheels = [ [[package]] name = "conan" -version = "1.65.0" +version = "1.66.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "bottle" }, @@ -120,53 +127,53 @@ dependencies = [ { name = "tqdm" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7b/00/40fa960783926ccc478707cc294f645636df5ecd7eb29edc87f6280a4898/conan-1.65.0.tar.gz", hash = "sha256:2fc5c8d681d94ccb9c0f273c29e4a8c78a8ea1b25295dc6ca17f9a715935c2df", size = 789719 } +sdist = { url = "https://files.pythonhosted.org/packages/ee/e8/0893952ee0c0f78100a7da72aeb81f68925933ecbb730bfd664e7a4c6f1f/conan-1.66.0.tar.gz", hash = "sha256:04d2ad0f6ec6f055d95fa0a157b55fdea9328ae67f51b11e7d26e9d9d2f76ab9", size = 789962 } [[package]] name = "coverage" -version = "7.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f7/08/7e37f82e4d1aead42a7443ff06a1e406aabf7302c4f00a546e4b320b994c/coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d", size = 798791 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/d4/300fc921dff243cd518c7db3a4c614b7e4b2431b0d1145c1e274fd99bd70/coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778", size = 206983 }, - { url = "https://files.pythonhosted.org/packages/e1/ab/6bf00de5327ecb8db205f9ae596885417a31535eeda6e7b99463108782e1/coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391", size = 207221 }, - { url = "https://files.pythonhosted.org/packages/92/8f/2ead05e735022d1a7f3a0a683ac7f737de14850395a826192f0288703472/coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8", size = 240342 }, - { url = "https://files.pythonhosted.org/packages/0f/ef/94043e478201ffa85b8ae2d2c79b4081e5a1b73438aafafccf3e9bafb6b5/coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d", size = 237371 }, - { url = "https://files.pythonhosted.org/packages/1f/0f/c890339dd605f3ebc269543247bdd43b703cce6825b5ed42ff5f2d6122c7/coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca", size = 239455 }, - { url = "https://files.pythonhosted.org/packages/d1/04/7fd7b39ec7372a04efb0f70c70e35857a99b6a9188b5205efb4c77d6a57a/coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163", size = 238924 }, - { url = "https://files.pythonhosted.org/packages/ed/bf/73ce346a9d32a09cf369f14d2a06651329c984e106f5992c89579d25b27e/coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a", size = 237252 }, - { url = "https://files.pythonhosted.org/packages/86/74/1dc7a20969725e917b1e07fe71a955eb34bc606b938316bcc799f228374b/coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d", size = 238897 }, - { url = "https://files.pythonhosted.org/packages/b6/e9/d9cc3deceb361c491b81005c668578b0dfa51eed02cd081620e9a62f24ec/coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5", size = 209606 }, - { url = "https://files.pythonhosted.org/packages/47/c8/5a2e41922ea6740f77d555c4d47544acd7dc3f251fe14199c09c0f5958d3/coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb", size = 210373 }, - { url = "https://files.pythonhosted.org/packages/8c/f9/9aa4dfb751cb01c949c990d136a0f92027fbcc5781c6e921df1cb1563f20/coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106", size = 207007 }, - { url = "https://files.pythonhosted.org/packages/b9/67/e1413d5a8591622a46dd04ff80873b04c849268831ed5c304c16433e7e30/coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9", size = 207269 }, - { url = "https://files.pythonhosted.org/packages/14/5b/9dec847b305e44a5634d0fb8498d135ab1d88330482b74065fcec0622224/coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c", size = 239886 }, - { url = "https://files.pythonhosted.org/packages/7b/b7/35760a67c168e29f454928f51f970342d23cf75a2bb0323e0f07334c85f3/coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a", size = 237037 }, - { url = "https://files.pythonhosted.org/packages/f7/95/d2fd31f1d638df806cae59d7daea5abf2b15b5234016a5ebb502c2f3f7ee/coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060", size = 239038 }, - { url = "https://files.pythonhosted.org/packages/6e/bd/110689ff5752b67924efd5e2aedf5190cbbe245fc81b8dec1abaffba619d/coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862", size = 238690 }, - { url = "https://files.pythonhosted.org/packages/d3/a8/08d7b38e6ff8df52331c83130d0ab92d9c9a8b5462f9e99c9f051a4ae206/coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388", size = 236765 }, - { url = "https://files.pythonhosted.org/packages/d6/6a/9cf96839d3147d55ae713eb2d877f4d777e7dc5ba2bce227167d0118dfe8/coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155", size = 238611 }, - { url = "https://files.pythonhosted.org/packages/74/e4/7ff20d6a0b59eeaab40b3140a71e38cf52547ba21dbcf1d79c5a32bba61b/coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a", size = 209671 }, - { url = "https://files.pythonhosted.org/packages/35/59/1812f08a85b57c9fdb6d0b383d779e47b6f643bc278ed682859512517e83/coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129", size = 210368 }, - { url = "https://files.pythonhosted.org/packages/9c/15/08913be1c59d7562a3e39fce20661a98c0a3f59d5754312899acc6cb8a2d/coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e", size = 207758 }, - { url = "https://files.pythonhosted.org/packages/c4/ae/b5d58dff26cade02ada6ca612a76447acd69dccdbb3a478e9e088eb3d4b9/coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962", size = 208035 }, - { url = "https://files.pythonhosted.org/packages/b8/d7/62095e355ec0613b08dfb19206ce3033a0eedb6f4a67af5ed267a8800642/coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb", size = 250839 }, - { url = "https://files.pythonhosted.org/packages/7c/1e/c2967cb7991b112ba3766df0d9c21de46b476d103e32bb401b1b2adf3380/coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704", size = 246569 }, - { url = "https://files.pythonhosted.org/packages/8b/61/a7a6a55dd266007ed3b1df7a3386a0d760d014542d72f7c2c6938483b7bd/coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b", size = 248927 }, - { url = "https://files.pythonhosted.org/packages/c8/fa/13a6f56d72b429f56ef612eb3bc5ce1b75b7ee12864b3bd12526ab794847/coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f", size = 248401 }, - { url = "https://files.pythonhosted.org/packages/75/06/0429c652aa0fb761fc60e8c6b291338c9173c6aa0f4e40e1902345b42830/coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223", size = 246301 }, - { url = "https://files.pythonhosted.org/packages/52/76/1766bb8b803a88f93c3a2d07e30ffa359467810e5cbc68e375ebe6906efb/coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3", size = 247598 }, - { url = "https://files.pythonhosted.org/packages/66/8b/f54f8db2ae17188be9566e8166ac6df105c1c611e25da755738025708d54/coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f", size = 210307 }, - { url = "https://files.pythonhosted.org/packages/9f/b0/e0dca6da9170aefc07515cce067b97178cefafb512d00a87a1c717d2efd5/coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657", size = 211453 }, +version = "7.6.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/84/ba/ac14d281f80aab516275012e8875991bb06203957aa1e19950139238d658/coverage-7.6.10.tar.gz", hash = "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23", size = 803868 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/77/19d09ea06f92fdf0487499283b1b7af06bc422ea94534c8fe3a4cd023641/coverage-7.6.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853", size = 208281 }, + { url = "https://files.pythonhosted.org/packages/b6/67/5479b9f2f99fcfb49c0d5cf61912a5255ef80b6e80a3cddba39c38146cf4/coverage-7.6.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078", size = 208514 }, + { url = "https://files.pythonhosted.org/packages/15/d1/febf59030ce1c83b7331c3546d7317e5120c5966471727aa7ac157729c4b/coverage-7.6.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0", size = 241537 }, + { url = "https://files.pythonhosted.org/packages/4b/7e/5ac4c90192130e7cf8b63153fe620c8bfd9068f89a6d9b5f26f1550f7a26/coverage-7.6.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50", size = 238572 }, + { url = "https://files.pythonhosted.org/packages/dc/03/0334a79b26ecf59958f2fe9dd1f5ab3e2f88db876f5071933de39af09647/coverage-7.6.10-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022", size = 240639 }, + { url = "https://files.pythonhosted.org/packages/d7/45/8a707f23c202208d7b286d78ad6233f50dcf929319b664b6cc18a03c1aae/coverage-7.6.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b", size = 240072 }, + { url = "https://files.pythonhosted.org/packages/66/02/603ce0ac2d02bc7b393279ef618940b4a0535b0868ee791140bda9ecfa40/coverage-7.6.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0", size = 238386 }, + { url = "https://files.pythonhosted.org/packages/04/62/4e6887e9be060f5d18f1dd58c2838b2d9646faf353232dec4e2d4b1c8644/coverage-7.6.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852", size = 240054 }, + { url = "https://files.pythonhosted.org/packages/5c/74/83ae4151c170d8bd071924f212add22a0e62a7fe2b149edf016aeecad17c/coverage-7.6.10-cp312-cp312-win32.whl", hash = "sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359", size = 210904 }, + { url = "https://files.pythonhosted.org/packages/c3/54/de0893186a221478f5880283119fc40483bc460b27c4c71d1b8bba3474b9/coverage-7.6.10-cp312-cp312-win_amd64.whl", hash = "sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247", size = 211692 }, + { url = "https://files.pythonhosted.org/packages/25/6d/31883d78865529257bf847df5789e2ae80e99de8a460c3453dbfbe0db069/coverage-7.6.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9", size = 208308 }, + { url = "https://files.pythonhosted.org/packages/70/22/3f2b129cc08de00c83b0ad6252e034320946abfc3e4235c009e57cfeee05/coverage-7.6.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b", size = 208565 }, + { url = "https://files.pythonhosted.org/packages/97/0a/d89bc2d1cc61d3a8dfe9e9d75217b2be85f6c73ebf1b9e3c2f4e797f4531/coverage-7.6.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690", size = 241083 }, + { url = "https://files.pythonhosted.org/packages/4c/81/6d64b88a00c7a7aaed3a657b8eaa0931f37a6395fcef61e53ff742b49c97/coverage-7.6.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18", size = 238235 }, + { url = "https://files.pythonhosted.org/packages/9a/0b/7797d4193f5adb4b837207ed87fecf5fc38f7cc612b369a8e8e12d9fa114/coverage-7.6.10-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c", size = 240220 }, + { url = "https://files.pythonhosted.org/packages/65/4d/6f83ca1bddcf8e51bf8ff71572f39a1c73c34cf50e752a952c34f24d0a60/coverage-7.6.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd", size = 239847 }, + { url = "https://files.pythonhosted.org/packages/30/9d/2470df6aa146aff4c65fee0f87f58d2164a67533c771c9cc12ffcdb865d5/coverage-7.6.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e", size = 237922 }, + { url = "https://files.pythonhosted.org/packages/08/dd/723fef5d901e6a89f2507094db66c091449c8ba03272861eaefa773ad95c/coverage-7.6.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694", size = 239783 }, + { url = "https://files.pythonhosted.org/packages/3d/f7/64d3298b2baf261cb35466000628706ce20a82d42faf9b771af447cd2b76/coverage-7.6.10-cp313-cp313-win32.whl", hash = "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6", size = 210965 }, + { url = "https://files.pythonhosted.org/packages/d5/58/ec43499a7fc681212fe7742fe90b2bc361cdb72e3181ace1604247a5b24d/coverage-7.6.10-cp313-cp313-win_amd64.whl", hash = "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e", size = 211719 }, + { url = "https://files.pythonhosted.org/packages/ab/c9/f2857a135bcff4330c1e90e7d03446b036b2363d4ad37eb5e3a47bbac8a6/coverage-7.6.10-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe", size = 209050 }, + { url = "https://files.pythonhosted.org/packages/aa/b3/f840e5bd777d8433caa9e4a1eb20503495709f697341ac1a8ee6a3c906ad/coverage-7.6.10-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273", size = 209321 }, + { url = "https://files.pythonhosted.org/packages/85/7d/125a5362180fcc1c03d91850fc020f3831d5cda09319522bcfa6b2b70be7/coverage-7.6.10-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8", size = 252039 }, + { url = "https://files.pythonhosted.org/packages/a9/9c/4358bf3c74baf1f9bddd2baf3756b54c07f2cfd2535f0a47f1e7757e54b3/coverage-7.6.10-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098", size = 247758 }, + { url = "https://files.pythonhosted.org/packages/cf/c7/de3eb6fc5263b26fab5cda3de7a0f80e317597a4bad4781859f72885f300/coverage-7.6.10-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb", size = 250119 }, + { url = "https://files.pythonhosted.org/packages/3e/e6/43de91f8ba2ec9140c6a4af1102141712949903dc732cf739167cfa7a3bc/coverage-7.6.10-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0", size = 249597 }, + { url = "https://files.pythonhosted.org/packages/08/40/61158b5499aa2adf9e37bc6d0117e8f6788625b283d51e7e0c53cf340530/coverage-7.6.10-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf", size = 247473 }, + { url = "https://files.pythonhosted.org/packages/50/69/b3f2416725621e9f112e74e8470793d5b5995f146f596f133678a633b77e/coverage-7.6.10-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2", size = 248737 }, + { url = "https://files.pythonhosted.org/packages/3c/6e/fe899fb937657db6df31cc3e61c6968cb56d36d7326361847440a430152e/coverage-7.6.10-cp313-cp313t-win32.whl", hash = "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312", size = 211611 }, + { url = "https://files.pythonhosted.org/packages/1c/55/52f5e66142a9d7bc93a15192eba7a78513d2abf6b3558d77b4ca32f5f424/coverage-7.6.10-cp313-cp313t-win_amd64.whl", hash = "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d", size = 212781 }, ] [[package]] name = "dill" -version = "0.3.8" +version = "0.3.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/17/4d/ac7ffa80c69ea1df30a8aa11b3578692a5118e7cd1aa157e3ef73b092d15/dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca", size = 184847 } +sdist = { url = "https://files.pythonhosted.org/packages/70/43/86fe3f9e130c4137b0f1b50784dd70a5087b911fe07fa81e53e0c4c47fea/dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c", size = 187000 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/7a/cef76fd8438a42f96db64ddaa85280485a9c395e7df3db8158cfec1eee34/dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7", size = 116252 }, + { url = "https://files.pythonhosted.org/packages/46/d1/e73b6ad76f0b1fb7f23c35c6d95dbc506a9c8804f43dda8cb5b0fa6331fd/dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a", size = 119418 }, ] [[package]] @@ -189,11 +196,11 @@ wheels = [ [[package]] name = "idna" -version = "3.9" +version = "3.10" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/00/6f/93e724eafe34e860d15d37a4f72a1511dd37c43a76a8671b22a15029d545/idna-3.9.tar.gz", hash = "sha256:e5c5dafde284f26e9e0f28f6ea2d6400abd5ca099864a67f576f3981c6476124", size = 191636 } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/15/61933d1999bc5ad8cad612d67f02fa5b16a423076ea0816e39c2e797af12/idna-3.9-py3-none-any.whl", hash = "sha256:69297d5da0cc9281c77efffb4e730254dd45943f45bbfb461de5991713989b1e", size = 71671 }, + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, ] [[package]] @@ -216,14 +223,14 @@ wheels = [ [[package]] name = "jinja2" -version = "3.1.4" +version = "3.1.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 } +sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674 } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 }, + { url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 }, ] [[package]] @@ -240,20 +247,40 @@ wheels = [ [[package]] name = "markupsafe" -version = "2.1.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/87/5b/aae44c6655f3801e81aa3eef09dbbf012431987ba564d7231722f68df02d/MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", size = 19384 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/53/bd/583bf3e4c8d6a321938c13f49d44024dbe5ed63e0a7ba127e454a66da974/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1", size = 18215 }, - { url = "https://files.pythonhosted.org/packages/48/d6/e7cd795fc710292c3af3a06d80868ce4b02bfbbf370b7cee11d282815a2a/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4", size = 14069 }, - { url = "https://files.pythonhosted.org/packages/51/b5/5d8ec796e2a08fc814a2c7d2584b55f889a55cf17dd1a90f2beb70744e5c/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee", size = 29452 }, - { url = "https://files.pythonhosted.org/packages/0a/0d/2454f072fae3b5a137c119abf15465d1771319dfe9e4acbb31722a0fff91/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5", size = 28462 }, - { url = "https://files.pythonhosted.org/packages/2d/75/fd6cb2e68780f72d47e6671840ca517bda5ef663d30ada7616b0462ad1e3/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b", size = 27869 }, - { url = "https://files.pythonhosted.org/packages/b0/81/147c477391c2750e8fc7705829f7351cf1cd3be64406edcf900dc633feb2/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a", size = 33906 }, - { url = "https://files.pythonhosted.org/packages/8b/ff/9a52b71839d7a256b563e85d11050e307121000dcebc97df120176b3ad93/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f", size = 32296 }, - { url = "https://files.pythonhosted.org/packages/88/07/2dc76aa51b481eb96a4c3198894f38b480490e834479611a4053fbf08623/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169", size = 33038 }, - { url = "https://files.pythonhosted.org/packages/96/0c/620c1fb3661858c0e37eb3cbffd8c6f732a67cd97296f725789679801b31/MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad", size = 16572 }, - { url = "https://files.pythonhosted.org/packages/3f/14/c3554d512d5f9100a95e737502f4a2323a1959f6d0d01e0d0997b35f7b10/MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb", size = 17127 }, +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, ] [[package]] @@ -294,11 +321,11 @@ wheels = [ [[package]] name = "packaging" -version = "24.1" +version = "24.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/51/65/50db4dda066951078f0a96cf12f4b9ada6e4b811516bf0262c0f4f7064d4/packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", size = 148788 } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } wheels = [ - { url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985 }, + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, ] [[package]] @@ -327,11 +354,11 @@ wheels = [ [[package]] name = "platformdirs" -version = "4.3.3" +version = "4.3.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f5/19/f7bee3a71decedd8d7bc4d3edb7970b8e899f3caef257b0f0d623f2f7b11/platformdirs-4.3.3.tar.gz", hash = "sha256:d4e0b7d8ec176b341fb03cb11ca12d0276faa8c485f9cd218f613840463fc2c0", size = 21304 } +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/e6/7c8e8c326903bd97c6c0c47e0a3c5de815faaae986cab7defdeddf5fddcd/platformdirs-4.3.3-py3-none-any.whl", hash = "sha256:50a5450e2e84f44539718293cbb1da0a0885c9d14adf21b77bae4e66fc99d9b5", size = 18437 }, + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, ] [[package]] @@ -351,74 +378,78 @@ sdist = { url = "https://files.pythonhosted.org/packages/f3/07/753451e80d2b0bf3b [[package]] name = "pydantic" -version = "2.9.1" +version = "2.10.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, { name = "pydantic-core" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/14/15/3d989541b9c8128b96d532cfd2dd10131ddcc75a807330c00feb3d42a5bd/pydantic-2.9.1.tar.gz", hash = "sha256:1363c7d975c7036df0db2b4a61f2e062fbc0aa5ab5f2772e0ffc7191a4f4bce2", size = 768511 } +sdist = { url = "https://files.pythonhosted.org/packages/6a/c7/ca334c2ef6f2e046b1144fe4bb2a5da8a4c574e7f2ebf7e16b34a6a2fa92/pydantic-2.10.5.tar.gz", hash = "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff", size = 761287 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/28/fff23284071bc1ba419635c7e86561c8b9b8cf62a5bcb459b92d7625fd38/pydantic-2.9.1-py3-none-any.whl", hash = "sha256:7aff4db5fdf3cf573d4b3c30926a510a10e19a0774d38fc4967f78beb6deb612", size = 434363 }, + { url = "https://files.pythonhosted.org/packages/58/26/82663c79010b28eddf29dcdd0ea723439535fa917fce5905885c0e9ba562/pydantic-2.10.5-py3-none-any.whl", hash = "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53", size = 431426 }, ] [[package]] name = "pydantic-core" -version = "2.23.3" +version = "2.27.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5c/cc/07bec3fb337ff80eacd6028745bd858b9642f61ee58cfdbfb64451c1def0/pydantic_core-2.23.3.tar.gz", hash = "sha256:3cb0f65d8b4121c1b015c60104a685feb929a29d7cf204387c7f2688c7974690", size = 402277 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/35/6d81bc4aa7d06e716f39e2bffb0eabcbcebaf7bab94c2f8278e277ded0ea/pydantic_core-2.23.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e0ec50663feedf64d21bad0809f5857bac1ce91deded203efc4a84b31b2e4305", size = 1845250 }, - { url = "https://files.pythonhosted.org/packages/18/42/0821cd46f76406e0fe57df7a89d6af8fddb22cce755bcc2db077773c7d1a/pydantic_core-2.23.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:db6e6afcb95edbe6b357786684b71008499836e91f2a4a1e55b840955b341dbb", size = 1769993 }, - { url = "https://files.pythonhosted.org/packages/e5/55/b969088e48bd8ea588548a7194d425de74370b17b385cee4d28f5a79013d/pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ccd69edcf49f0875d86942f4418a4e83eb3047f20eb897bffa62a5d419c8fa", size = 1791250 }, - { url = "https://files.pythonhosted.org/packages/43/c1/1d460d09c012ac76b68b2a1fd426ad624724f93b40e24a9a993763f12c61/pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a678c1ac5c5ec5685af0133262103defb427114e62eafeda12f1357a12140162", size = 1802530 }, - { url = "https://files.pythonhosted.org/packages/70/8e/fd3c9eda00fbdadca726f17a0f863ecd871a65b3a381b77277ae386d3bcd/pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01491d8b4d8db9f3391d93b0df60701e644ff0894352947f31fff3e52bd5c801", size = 1997848 }, - { url = "https://files.pythonhosted.org/packages/f0/67/13fa22d7b09395e83721edc31bae2bd5c5e2c36a09d470c18f5d1de46958/pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fcf31facf2796a2d3b7fe338fe8640aa0166e4e55b4cb108dbfd1058049bf4cb", size = 2662790 }, - { url = "https://files.pythonhosted.org/packages/fa/1b/1d689c53d15ab67cb0df1c3a2b1df873b50409581e93e4848289dce57e2f/pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7200fd561fb3be06827340da066df4311d0b6b8eb0c2116a110be5245dceb326", size = 2074114 }, - { url = "https://files.pythonhosted.org/packages/3d/d9/b565048609db77760b9a0900f6e0a3b2f33be47cd3c4a433f49653a0d2b5/pydantic_core-2.23.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc1636770a809dee2bd44dd74b89cc80eb41172bcad8af75dd0bc182c2666d4c", size = 1918153 }, - { url = "https://files.pythonhosted.org/packages/41/94/8ee55c51333ed8df3a6f1e73c6530c724a9a37d326e114c9e3b24faacff9/pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:67a5def279309f2e23014b608c4150b0c2d323bd7bccd27ff07b001c12c2415c", size = 1969019 }, - { url = "https://files.pythonhosted.org/packages/f7/49/0233bae5778a5526cef000447a93e8d462f4f13e2214c13c5b23d379cb25/pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:748bdf985014c6dd3e1e4cc3db90f1c3ecc7246ff5a3cd4ddab20c768b2f1dab", size = 2121325 }, - { url = "https://files.pythonhosted.org/packages/42/a1/2f262db2fd6f9c2c9904075a067b1764cc6f71c014be5c6c91d9de52c434/pydantic_core-2.23.3-cp312-none-win32.whl", hash = "sha256:255ec6dcb899c115f1e2a64bc9ebc24cc0e3ab097775755244f77360d1f3c06c", size = 1725252 }, - { url = "https://files.pythonhosted.org/packages/9a/00/a57937080b49500df790c4853d3e7bc605bd0784e4fcaf1a159456f37ef1/pydantic_core-2.23.3-cp312-none-win_amd64.whl", hash = "sha256:40b8441be16c1e940abebed83cd006ddb9e3737a279e339dbd6d31578b802f7b", size = 1920660 }, - { url = "https://files.pythonhosted.org/packages/e1/3c/32958c0a5d1935591b58337037a1695782e61261582d93d5a7f55441f879/pydantic_core-2.23.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6daaf5b1ba1369a22c8b050b643250e3e5efc6a78366d323294aee54953a4d5f", size = 1845068 }, - { url = "https://files.pythonhosted.org/packages/92/a1/7e628e19b78e6ffdb2c92cccbb7eca84bfd3276cee4cafcae8833452f458/pydantic_core-2.23.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d015e63b985a78a3d4ccffd3bdf22b7c20b3bbd4b8227809b3e8e75bc37f9cb2", size = 1770095 }, - { url = "https://files.pythonhosted.org/packages/bb/17/d15fd8ce143cd1abb27be924eeff3c5c0fe3b0582f703c5a5273c11e67ce/pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3fc572d9b5b5cfe13f8e8a6e26271d5d13f80173724b738557a8c7f3a8a3791", size = 1790964 }, - { url = "https://files.pythonhosted.org/packages/24/cc/37feff1792f09dc33207fbad3897373229279d1973c211f9562abfdf137d/pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f6bd91345b5163ee7448bee201ed7dd601ca24f43f439109b0212e296eb5b423", size = 1802384 }, - { url = "https://files.pythonhosted.org/packages/44/d8/ca9acd7f5f044d9ff6e43d7f35aab4b1d5982b4773761eabe3317fc68e30/pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc379c73fd66606628b866f661e8785088afe2adaba78e6bbe80796baf708a63", size = 1997824 }, - { url = "https://files.pythonhosted.org/packages/35/0f/146269dba21b10d5bf86f9a7a7bbeab4ce1db06f466a1ab5ec3dec68b409/pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbdce4b47592f9e296e19ac31667daed8753c8367ebb34b9a9bd89dacaa299c9", size = 2662907 }, - { url = "https://files.pythonhosted.org/packages/5a/7d/9573f006e39cd1a7b7716d1a264e3f4f353cf0a6042c04c01c6e31666f62/pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3cf31edf405a161a0adad83246568647c54404739b614b1ff43dad2b02e6d5", size = 2073953 }, - { url = "https://files.pythonhosted.org/packages/7e/a5/25200aaafd1e97e2ec3c1eb4b357669dd93911f2eba252bc60b6ba884fff/pydantic_core-2.23.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8e22b477bf90db71c156f89a55bfe4d25177b81fce4aa09294d9e805eec13855", size = 1917822 }, - { url = "https://files.pythonhosted.org/packages/3e/b4/ac069c58e3cee70c69f03693222cc173fdf740d20d53167bceafc1efc7ca/pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0a0137ddf462575d9bce863c4c95bac3493ba8e22f8c28ca94634b4a1d3e2bb4", size = 1968838 }, - { url = "https://files.pythonhosted.org/packages/d1/3d/9f96bbd6212b4b0a6dc6d037e446208d3420baba2b2b81e544094b18a859/pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:203171e48946c3164fe7691fc349c79241ff8f28306abd4cad5f4f75ed80bc8d", size = 2121468 }, - { url = "https://files.pythonhosted.org/packages/ac/50/7399d536d6600d69059a87fff89861332c97a7b3471327a3663c7576e707/pydantic_core-2.23.3-cp313-none-win32.whl", hash = "sha256:76bdab0de4acb3f119c2a4bff740e0c7dc2e6de7692774620f7452ce11ca76c8", size = 1725373 }, - { url = "https://files.pythonhosted.org/packages/24/ba/9ac8744ab636c1161c598cc5e8261379b6b0f1d63c31242bf9d5ed41ed32/pydantic_core-2.23.3-cp313-none-win_amd64.whl", hash = "sha256:37ba321ac2a46100c578a92e9a6aa33afe9ec99ffa084424291d84e456f490c1", size = 1920594 }, +sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/74/51c8a5482ca447871c93e142d9d4a92ead74de6c8dc5e66733e22c9bba89/pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0", size = 1893127 }, + { url = "https://files.pythonhosted.org/packages/d3/f3/c97e80721735868313c58b89d2de85fa80fe8dfeeed84dc51598b92a135e/pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef", size = 1811340 }, + { url = "https://files.pythonhosted.org/packages/9e/91/840ec1375e686dbae1bd80a9e46c26a1e0083e1186abc610efa3d9a36180/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7", size = 1822900 }, + { url = "https://files.pythonhosted.org/packages/f6/31/4240bc96025035500c18adc149aa6ffdf1a0062a4b525c932065ceb4d868/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934", size = 1869177 }, + { url = "https://files.pythonhosted.org/packages/fa/20/02fbaadb7808be578317015c462655c317a77a7c8f0ef274bc016a784c54/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6", size = 2038046 }, + { url = "https://files.pythonhosted.org/packages/06/86/7f306b904e6c9eccf0668248b3f272090e49c275bc488a7b88b0823444a4/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c", size = 2685386 }, + { url = "https://files.pythonhosted.org/packages/8d/f0/49129b27c43396581a635d8710dae54a791b17dfc50c70164866bbf865e3/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2", size = 1997060 }, + { url = "https://files.pythonhosted.org/packages/0d/0f/943b4af7cd416c477fd40b187036c4f89b416a33d3cc0ab7b82708a667aa/pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4", size = 2004870 }, + { url = "https://files.pythonhosted.org/packages/35/40/aea70b5b1a63911c53a4c8117c0a828d6790483f858041f47bab0b779f44/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3", size = 1999822 }, + { url = "https://files.pythonhosted.org/packages/f2/b3/807b94fd337d58effc5498fd1a7a4d9d59af4133e83e32ae39a96fddec9d/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4", size = 2130364 }, + { url = "https://files.pythonhosted.org/packages/fc/df/791c827cd4ee6efd59248dca9369fb35e80a9484462c33c6649a8d02b565/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57", size = 2158303 }, + { url = "https://files.pythonhosted.org/packages/9b/67/4e197c300976af185b7cef4c02203e175fb127e414125916bf1128b639a9/pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc", size = 1834064 }, + { url = "https://files.pythonhosted.org/packages/1f/ea/cd7209a889163b8dcca139fe32b9687dd05249161a3edda62860430457a5/pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9", size = 1989046 }, + { url = "https://files.pythonhosted.org/packages/bc/49/c54baab2f4658c26ac633d798dab66b4c3a9bbf47cff5284e9c182f4137a/pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b", size = 1885092 }, + { url = "https://files.pythonhosted.org/packages/41/b1/9bc383f48f8002f99104e3acff6cba1231b29ef76cfa45d1506a5cad1f84/pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", size = 1892709 }, + { url = "https://files.pythonhosted.org/packages/10/6c/e62b8657b834f3eb2961b49ec8e301eb99946245e70bf42c8817350cbefc/pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", size = 1811273 }, + { url = "https://files.pythonhosted.org/packages/ba/15/52cfe49c8c986e081b863b102d6b859d9defc63446b642ccbbb3742bf371/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", size = 1823027 }, + { url = "https://files.pythonhosted.org/packages/b1/1c/b6f402cfc18ec0024120602bdbcebc7bdd5b856528c013bd4d13865ca473/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", size = 1868888 }, + { url = "https://files.pythonhosted.org/packages/bd/7b/8cb75b66ac37bc2975a3b7de99f3c6f355fcc4d89820b61dffa8f1e81677/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", size = 2037738 }, + { url = "https://files.pythonhosted.org/packages/c8/f1/786d8fe78970a06f61df22cba58e365ce304bf9b9f46cc71c8c424e0c334/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", size = 2685138 }, + { url = "https://files.pythonhosted.org/packages/a6/74/d12b2cd841d8724dc8ffb13fc5cef86566a53ed358103150209ecd5d1999/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", size = 1997025 }, + { url = "https://files.pythonhosted.org/packages/a0/6e/940bcd631bc4d9a06c9539b51f070b66e8f370ed0933f392db6ff350d873/pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", size = 2004633 }, + { url = "https://files.pythonhosted.org/packages/50/cc/a46b34f1708d82498c227d5d80ce615b2dd502ddcfd8376fc14a36655af1/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", size = 1999404 }, + { url = "https://files.pythonhosted.org/packages/ca/2d/c365cfa930ed23bc58c41463bae347d1005537dc8db79e998af8ba28d35e/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", size = 2130130 }, + { url = "https://files.pythonhosted.org/packages/f4/d7/eb64d015c350b7cdb371145b54d96c919d4db516817f31cd1c650cae3b21/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", size = 2157946 }, + { url = "https://files.pythonhosted.org/packages/a4/99/bddde3ddde76c03b65dfd5a66ab436c4e58ffc42927d4ff1198ffbf96f5f/pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", size = 1834387 }, + { url = "https://files.pythonhosted.org/packages/71/47/82b5e846e01b26ac6f1893d3c5f9f3a2eb6ba79be26eef0b759b4fe72946/pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", size = 1990453 }, + { url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 }, ] [[package]] name = "pygments" -version = "2.18.0" +version = "2.19.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 }, + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, ] [[package]] name = "pyjwt" -version = "2.9.0" +version = "2.10.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fb/68/ce067f09fca4abeca8771fe667d89cc347d1e99da3e093112ac329c6020e/pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c", size = 78825 } +sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785 } wheels = [ - { url = "https://files.pythonhosted.org/packages/79/84/0fdf9b18ba31d69877bd39c9cd6052b47f3761e9910c15de788e519f079f/PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850", size = 22344 }, + { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997 }, ] [[package]] name = "pylint" -version = "3.2.7" +version = "3.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "astroid" }, @@ -429,26 +460,27 @@ dependencies = [ { name = "platformdirs" }, { name = "tomlkit" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cf/e8/d59ce8e54884c9475ed6510685ef4311a10001674c28703b23da30f3b24d/pylint-3.2.7.tar.gz", hash = "sha256:1b7a721b575eaeaa7d39db076b6e7743c993ea44f57979127c517c6c572c803e", size = 1511922 } +sdist = { url = "https://files.pythonhosted.org/packages/17/fd/e9a739afac274a39596bbe562e9d966db6f3917fdb2bd7322ffc56da0ba2/pylint-3.3.3.tar.gz", hash = "sha256:07c607523b17e6d16e2ae0d7ef59602e332caa762af64203c24b41c27139f36a", size = 1516550 } wheels = [ - { url = "https://files.pythonhosted.org/packages/42/4d/c73bc0fca447b918611985c325cd7017fb762050eb9c6ac6fa7d9ac6fbe4/pylint-3.2.7-py3-none-any.whl", hash = "sha256:02f4aedeac91be69fb3b4bea997ce580a4ac68ce58b89eaefeaf06749df73f4b", size = 519906 }, + { url = "https://files.pythonhosted.org/packages/91/e1/26d55acea92b1ea4d33672e48f09ceeb274e84d7d542a4fb9a32a556db46/pylint-3.3.3-py3-none-any.whl", hash = "sha256:26e271a2bc8bce0fc23833805a9076dd9b4d5194e2a02164942cb3cdc37b4183", size = 521918 }, ] [[package]] name = "pyright" -version = "1.1.380" +version = "1.1.392.post0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nodeenv" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fa/bc/3bb71d02125dae6730d64bb32571c0eda5e5d86483b198b7f6dd82c49c6f/pyright-1.1.380.tar.gz", hash = "sha256:e6ceb1a5f7e9f03106e0aa1d6fbb4d97735a5e7ffb59f3de6b2db590baf935b2", size = 17487 } +sdist = { url = "https://files.pythonhosted.org/packages/66/df/3c6f6b08fba7ccf49b114dfc4bb33e25c299883fd763f93fad47ef8bc58d/pyright-1.1.392.post0.tar.gz", hash = "sha256:3b7f88de74a28dcfa90c7d90c782b6569a48c2be5f9d4add38472bdaac247ebd", size = 3789911 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/fc/527c58a3b66d5a16dbb124e1c40cf9f207147f5a36f224fb29eb786a0763/pyright-1.1.380-py3-none-any.whl", hash = "sha256:a6404392053d8848bacc7aebcbd9d318bb46baf1a1a000359305481920f43879", size = 18219 }, + { url = "https://files.pythonhosted.org/packages/e7/b1/a18de17f40e4f61ca58856b9ef9b0febf74ff88978c3f7776f910071f567/pyright-1.1.392.post0-py3-none-any.whl", hash = "sha256:252f84458a46fa2f0fd4e2f91fc74f50b9ca52c757062e93f6c250c0d8329eb2", size = 5595487 }, ] [[package]] name = "pytest" -version = "8.3.3" +version = "8.3.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -456,9 +488,9 @@ dependencies = [ { name = "packaging" }, { name = "pluggy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487 } +sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341 }, + { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 }, ] [[package]] @@ -529,15 +561,40 @@ wheels = [ [[package]] name = "rich" -version = "13.8.1" +version = "13.9.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/92/76/40f084cb7db51c9d1fa29a7120717892aeda9a7711f6225692c957a93535/rich-13.8.1.tar.gz", hash = "sha256:8260cda28e3db6bf04d2d1ef4dbc03ba80a824c88b0e7668a0f23126a424844a", size = 222080 } +sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/11/dadb85e2bd6b1f1ae56669c3e1f0410797f9605d752d68fb47b77f525b31/rich-13.8.1-py3-none-any.whl", hash = "sha256:1760a3c0848469b97b558fc61c85233e3dafb69c7a071b4d60c38099d3cd4c06", size = 241608 }, + { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 }, +] + +[[package]] +name = "ruff" +version = "0.9.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/80/63/77ecca9d21177600f551d1c58ab0e5a0b260940ea7312195bd2a4798f8a8/ruff-0.9.2.tar.gz", hash = "sha256:b5eceb334d55fae5f316f783437392642ae18e16dcf4f1858d55d3c2a0f8f5d0", size = 3553799 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/b9/0e168e4e7fb3af851f739e8f07889b91d1a33a30fca8c29fa3149d6b03ec/ruff-0.9.2-py3-none-linux_armv6l.whl", hash = "sha256:80605a039ba1454d002b32139e4970becf84b5fee3a3c3bf1c2af6f61a784347", size = 11652408 }, + { url = "https://files.pythonhosted.org/packages/2c/22/08ede5db17cf701372a461d1cb8fdde037da1d4fa622b69ac21960e6237e/ruff-0.9.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b9aab82bb20afd5f596527045c01e6ae25a718ff1784cb92947bff1f83068b00", size = 11587553 }, + { url = "https://files.pythonhosted.org/packages/42/05/dedfc70f0bf010230229e33dec6e7b2235b2a1b8cbb2a991c710743e343f/ruff-0.9.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fbd337bac1cfa96be615f6efcd4bc4d077edbc127ef30e2b8ba2a27e18c054d4", size = 11020755 }, + { url = "https://files.pythonhosted.org/packages/df/9b/65d87ad9b2e3def67342830bd1af98803af731243da1255537ddb8f22209/ruff-0.9.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b35259b0cbf8daa22a498018e300b9bb0174c2bbb7bcba593935158a78054d", size = 11826502 }, + { url = "https://files.pythonhosted.org/packages/93/02/f2239f56786479e1a89c3da9bc9391120057fc6f4a8266a5b091314e72ce/ruff-0.9.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b6a9701d1e371bf41dca22015c3f89769da7576884d2add7317ec1ec8cb9c3c", size = 11390562 }, + { url = "https://files.pythonhosted.org/packages/c9/37/d3a854dba9931f8cb1b2a19509bfe59e00875f48ade632e95aefcb7a0aee/ruff-0.9.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9cc53e68b3c5ae41e8faf83a3b89f4a5d7b2cb666dff4b366bb86ed2a85b481f", size = 12548968 }, + { url = "https://files.pythonhosted.org/packages/fa/c3/c7b812bb256c7a1d5553433e95980934ffa85396d332401f6b391d3c4569/ruff-0.9.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8efd9da7a1ee314b910da155ca7e8953094a7c10d0c0a39bfde3fcfd2a015684", size = 13187155 }, + { url = "https://files.pythonhosted.org/packages/bd/5a/3c7f9696a7875522b66aa9bba9e326e4e5894b4366bd1dc32aa6791cb1ff/ruff-0.9.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3292c5a22ea9a5f9a185e2d131dc7f98f8534a32fb6d2ee7b9944569239c648d", size = 12704674 }, + { url = "https://files.pythonhosted.org/packages/be/d6/d908762257a96ce5912187ae9ae86792e677ca4f3dc973b71e7508ff6282/ruff-0.9.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a605fdcf6e8b2d39f9436d343d1f0ff70c365a1e681546de0104bef81ce88df", size = 14529328 }, + { url = "https://files.pythonhosted.org/packages/2d/c2/049f1e6755d12d9cd8823242fa105968f34ee4c669d04cac8cea51a50407/ruff-0.9.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c547f7f256aa366834829a08375c297fa63386cbe5f1459efaf174086b564247", size = 12385955 }, + { url = "https://files.pythonhosted.org/packages/91/5a/a9bdb50e39810bd9627074e42743b00e6dc4009d42ae9f9351bc3dbc28e7/ruff-0.9.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d18bba3d3353ed916e882521bc3e0af403949dbada344c20c16ea78f47af965e", size = 11810149 }, + { url = "https://files.pythonhosted.org/packages/e5/fd/57df1a0543182f79a1236e82a79c68ce210efb00e97c30657d5bdb12b478/ruff-0.9.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b338edc4610142355ccf6b87bd356729b62bf1bc152a2fad5b0c7dc04af77bfe", size = 11479141 }, + { url = "https://files.pythonhosted.org/packages/dc/16/bc3fd1d38974f6775fc152a0554f8c210ff80f2764b43777163c3c45d61b/ruff-0.9.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:492a5e44ad9b22a0ea98cf72e40305cbdaf27fac0d927f8bc9e1df316dcc96eb", size = 12014073 }, + { url = "https://files.pythonhosted.org/packages/47/6b/e4ca048a8f2047eb652e1e8c755f384d1b7944f69ed69066a37acd4118b0/ruff-0.9.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:af1e9e9fe7b1f767264d26b1075ac4ad831c7db976911fa362d09b2d0356426a", size = 12435758 }, + { url = "https://files.pythonhosted.org/packages/c2/40/4d3d6c979c67ba24cf183d29f706051a53c36d78358036a9cd21421582ab/ruff-0.9.2-py3-none-win32.whl", hash = "sha256:71cbe22e178c5da20e1514e1e01029c73dc09288a8028a5d3446e6bba87a5145", size = 9796916 }, + { url = "https://files.pythonhosted.org/packages/c3/ef/7f548752bdb6867e6939489c87fe4da489ab36191525fadc5cede2a6e8e2/ruff-0.9.2-py3-none-win_amd64.whl", hash = "sha256:c5e1d6abc798419cf46eed03f54f2e0c3adb1ad4b801119dedf23fcaf69b55b5", size = 10773080 }, + { url = "https://files.pythonhosted.org/packages/0e/4e/33df635528292bd2d18404e4daabcd74ca8a9853b2e1df85ed3d32d24362/ruff-0.9.2-py3-none-win_arm64.whl", hash = "sha256:a1b63fa24149918f8b37cef2ee6fff81f24f0d74b6f0bdc37bc3e1f2143e41c6", size = 10001738 }, ] [[package]] @@ -551,14 +608,14 @@ wheels = [ [[package]] name = "stevedore" -version = "5.3.0" +version = "5.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pbr" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/59/f8aefa21020054f553bf7e3b405caec7f8d1f432d9cb47e34aaa244d8d03/stevedore-5.3.0.tar.gz", hash = "sha256:9a64265f4060312828151c204efbe9b7a9852a0d9228756344dbc7e4023e375a", size = 513768 } +sdist = { url = "https://files.pythonhosted.org/packages/4a/e9/4eedccff8332cc40cc60ddd3b28d4c3e255ee7e9c65679fa4533ab98f598/stevedore-5.4.0.tar.gz", hash = "sha256:79e92235ecb828fe952b6b8b0c6c87863248631922c8e8e0fa5b17b232c4514d", size = 513899 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/50/70762bdb23f6c2b746b90661f461d33c4913a22a46bb5265b10947e85ffb/stevedore-5.3.0-py3-none-any.whl", hash = "sha256:1efd34ca08f474dad08d9b19e934a22c68bb6fe416926479ba29e5013bcc8f78", size = 49661 }, + { url = "https://files.pythonhosted.org/packages/8f/73/d0091d22a65b55e8fb6aca7b3b6713b5a261dd01cec4cfd28ed127ac0cfc/stevedore-5.4.0-py3-none-any.whl", hash = "sha256:b0be3c4748b3ea7b854b265dcb4caa891015e442416422be16f8b31756107857", size = 49534 }, ] [[package]] @@ -572,14 +629,14 @@ wheels = [ [[package]] name = "tqdm" -version = "4.66.5" +version = "4.67.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "platform_system == 'Windows'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/58/83/6ba9844a41128c62e810fddddd72473201f3eacde02046066142a2d96cc5/tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad", size = 169504 } +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } wheels = [ - { url = "https://files.pythonhosted.org/packages/48/5d/acf5905c36149bbaec41ccf7f2b68814647347b72075ac0b1fe3022fdc73/tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd", size = 78351 }, + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, ] [[package]] @@ -629,6 +686,7 @@ dev = [ { name = "pyright" }, { name = "pytest" }, { name = "pytest-cov" }, + { name = "ruff" }, ] [package.metadata] @@ -648,4 +706,5 @@ dev = [ { name = "pyright", specifier = "~=1.1" }, { name = "pytest", specifier = "~=8.0" }, { name = "pytest-cov", specifier = "~=4.1" }, + { name = "ruff", specifier = "~=0.7" }, ]