From c8d50d3349397cb7b44e67d3575b60a1a36a59b7 Mon Sep 17 00:00:00 2001 From: Lina Date: Thu, 12 Feb 2026 17:26:01 +0100 Subject: [PATCH 1/7] fix[qepest]: self.noError is a mistake --- pythonQEPest/cli/cli.py | 16 +++++++++++----- pythonQEPest/core/qepest_meta.py | 3 --- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/pythonQEPest/cli/cli.py b/pythonQEPest/cli/cli.py index b7f4915..493c013 100644 --- a/pythonQEPest/cli/cli.py +++ b/pythonQEPest/cli/cli.py @@ -21,6 +21,8 @@ def __init__(self, qepest: QEPestMeta, qepest_file: QEPestFile): self.qepest = qepest self.qepest_file = qepest_file + self.error: bool = False + def read_file_and_compute_params(self): try: with open(self.qepest_file.input_file, "r") as file: @@ -32,8 +34,10 @@ def read_file_and_compute_params(self): if get_num_of_cols(line) != self.qepest.col_number: er = f"Error: Line {index} does not have seven elements." logger.error(er) - self.qepest.noError = False + + self.error = True break + wr.write("Name QEH QEI QEF\n") else: if get_num_of_cols(line) == self.qepest.col_number: @@ -48,14 +52,16 @@ def read_file_and_compute_params(self): else: er = f"Error: Line {index} does not have seven elements." logger.error(er) - self.qepest.noError = False - if self.qepest.noError: + + self.error = True + if not self.error: logger.info("Computation completed") else: logger.warning("Finished with errors") except FileNotFoundError as e: - self.qepest.noError = False + self.error = True + logger.error("Error: can't find : %s", self.qepest_file.input_file) logger.exception(e) @@ -123,4 +129,4 @@ def main(argv: Sequence[str] | None = None) -> int: ) cli.read_file_and_compute_params() - return 0 if cli.qepest and cli.qepest.noError else 1 + return 0 if not cli.error else 1 diff --git a/pythonQEPest/core/qepest_meta.py b/pythonQEPest/core/qepest_meta.py index d260674..f7f9d8b 100644 --- a/pythonQEPest/core/qepest_meta.py +++ b/pythonQEPest/core/qepest_meta.py @@ -1,6 +1,5 @@ import os from abc import abstractmethod, ABC - from pythonQEPest.dto import QEPestInput, QEPestOutput, QEPestData @@ -17,8 +16,6 @@ def __init__(self): self.initialize_coefficients() - self.noError: bool = True - @abstractmethod def compute_params(self, data_input: QEPestInput) -> QEPestOutput: pass From d8c833b10dcd9e16f3eb29f40f90ee148310ef62 Mon Sep 17 00:00:00 2001 From: Lina Date: Sun, 1 Mar 2026 01:18:19 +0100 Subject: [PATCH 2/7] refactor[QEPest]: Management initialisation Refactor QEPestMeta and related classes for improved coefficient handling and normalization - Updated QEPestMeta to accept coefficients and normalizers as optional parameters during initialization. - Introduced QEPestCoefficient, QEPestCoefficientNumerics, and QEPestCoefficientList for structured coefficient management. - Added Normaliser class for normalization functionality. - Modified QEPestOutput to use BaseModel for data representation. - Enhanced tests for coefficients and normalization to ensure robustness. - Removed deprecated normalization helper functions and consolidated tests. --- .github/workflows/python-package.yml | 26 +- pyproject.toml | 6 +- pythonQEPest/cli/cli.py | 7 +- pythonQEPest/config/__init__.py | 7 + pythonQEPest/config/normalise.py | 12 + pythonQEPest/config/qepest.py | 26 ++ pythonQEPest/core/qepest.py | 114 +++--- pythonQEPest/core/qepest_meta.py | 20 +- pythonQEPest/dto/QEPestOutput.py | 6 +- pythonQEPest/dto/__init__.py | 2 +- .../dto/coefficients/QEPestCoefficient.py | 57 +++ .../dto/coefficients/QEPestCoefficientList.py | 29 ++ .../coefficients/QEPestCoefficientNumerics.py | 44 +++ pythonQEPest/dto/coefficients/__init__.py | 7 + pythonQEPest/dto/normalisation/Normaliser.py | 16 + pythonQEPest/dto/normalisation/__init__.py | 3 + tests/config/__init__.py | 0 tests/config/test_coefficients.py | 369 ++++++++++++++++++ tests/config/test_normalisation.py | 36 ++ tests/core/test_qepest.py | 101 ++--- tests/dto/test_norm.py | 74 ++++ tests/helpers/test_norm.py | 47 --- tests/public_api/test_import_contract.py | 9 +- 23 files changed, 852 insertions(+), 166 deletions(-) create mode 100644 pythonQEPest/config/__init__.py create mode 100644 pythonQEPest/config/normalise.py create mode 100644 pythonQEPest/config/qepest.py create mode 100644 pythonQEPest/dto/coefficients/QEPestCoefficient.py create mode 100644 pythonQEPest/dto/coefficients/QEPestCoefficientList.py create mode 100644 pythonQEPest/dto/coefficients/QEPestCoefficientNumerics.py create mode 100644 pythonQEPest/dto/coefficients/__init__.py create mode 100644 pythonQEPest/dto/normalisation/Normaliser.py create mode 100644 pythonQEPest/dto/normalisation/__init__.py create mode 100644 tests/config/__init__.py create mode 100644 tests/config/test_coefficients.py create mode 100644 tests/config/test_normalisation.py create mode 100644 tests/dto/test_norm.py delete mode 100644 tests/helpers/test_norm.py diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 21b923b..b8eb251 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -12,36 +12,32 @@ on: jobs: build: - - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: - python-version: [ "3.10", "3.11", "3.12" ] + python-version: [ "3.12", "3.13", "3.14" ] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} + cache: poetry + cache-dependency-path: poetry.lock - - name: Install poetry + - name: Install Poetry (pipx) run: | python -m pip install --upgrade pip - python -m pip install poetry + python -m pip install pipx + pipx install poetry - name: Install dependencies run: | - poetry install --with dev - - # - name: Lint with flake8 - # run: | - # # stop the build if there are Python syntax errors or undefined names - # flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + poetry install --with dev --no-interaction + - name: Test with pytest (coverage) run: | poetry run pytest -q --cov=pythonQEPest --cov-report=term-missing --cov-fail-under=40 diff --git a/pyproject.toml b/pyproject.toml index 4a9f523..9d0b121 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,9 +1,9 @@ [project] name = "pythonQEPest" -version = "1.1.5" -description = "The rewritten version of Java QEPest" +version = "1.3.0" +description = "Java QEPest in Python" readme = "README.md" -requires-python = ">=3.10,<3.13" +requires-python = ">=3.12,<3.15" authors = [{ name = "Lina", email = "knocker767@gmail.com" }] dependencies = ["pydantic==2.12.5", "python-dotenv>=1.2.1,<2.0.0"] diff --git a/pythonQEPest/cli/cli.py b/pythonQEPest/cli/cli.py index 493c013..86eca20 100644 --- a/pythonQEPest/cli/cli.py +++ b/pythonQEPest/cli/cli.py @@ -44,9 +44,12 @@ def read_file_and_compute_params(self): d_values = get_values_from_line(line.split("\t")) self.qepest.get_qex_values(d_values) splitted_line = line.split("\t")[0] + + # TODO: BUG, AFTER REWORK NEEDS TO BE FIXED !!!!!!!!!!! wr.write( - f"{splitted_line} {self.qepest.qex.qe_h} " - + f"{self.qepest.qex.qe_i} {self.qepest.qex.qe_f}" + f"{splitted_line} {self.qepest.qex.qe_herb} " + + f"{self.qepest.qex.qe_insect} " + + f"{self.qepest.qex.qe_fung}" + f"{chr(10)}" ) else: diff --git a/pythonQEPest/config/__init__.py b/pythonQEPest/config/__init__.py new file mode 100644 index 0000000..0d59601 --- /dev/null +++ b/pythonQEPest/config/__init__.py @@ -0,0 +1,7 @@ +from .qepest import qepest_default +from .normalise import normalise_default + +__all__ = [ + "normalise_default", + "qepest_default", +] diff --git a/pythonQEPest/config/normalise.py b/pythonQEPest/config/normalise.py new file mode 100644 index 0000000..666d681 --- /dev/null +++ b/pythonQEPest/config/normalise.py @@ -0,0 +1,12 @@ +normalise_default = { + "herb": (69.5849922, 94.4228257, 120.4572352, 228.1589796, 89.7012502, 276.9634213), + "insect": ( + 78.2919965, + 71.2829691, + 133.9224801, + 331.170104, + 70.5540709, + 193.0023343, + ), + "fung": (53.3719946, 52.773116, 73.7976536, 144.9887053, 41.4385926, 102.3024319), +} diff --git a/pythonQEPest/config/qepest.py b/pythonQEPest/config/qepest.py new file mode 100644 index 0000000..93293c7 --- /dev/null +++ b/pythonQEPest/config/qepest.py @@ -0,0 +1,26 @@ +qepest_default = { + "herb": [ + (70.77, 283.0, 84.97, -1.185), # mwH + (93.81, 3.077, 1.434, 0.6164), # logpH + (117.6, 2.409, 1.567, 7.155), # hbaH + (233.4, 0.4535, -1.48, 4.47), # hbdH + (84.7, 4.758, -2.423, 5.437), # rbH + (301.8, 1.101, 0.8869, -22.81), # arRCH + ], + "insect": [ + (76.38, 298.3, 83.64, 1.912), # mwI + (74.27, 4.555, -2.193, -2.987), # logpI + (139.4, 1.363, 1.283, 0.5341), # hbaI + (670.6, -1.163, 0.7856, 0.7951), # hbdI + (65.49, 6.219, -2.448, 5.318), # rbI + (287.5, 0.305, 1.554, -88.64), # arRCI + ], + "fung": [ + (51.03, 314.2, -56.31, 2.342), # mwF + (50.73, 3.674, -1.238, 2.067), # logpF + (73.79, 1.841, 1.326, 0.5158), # hbaF + (164.7, -0.9762, -2.027, 1.384), # hbdF + (40.91, 1.822, 2.582, 0.6235), # rbF + (134.4, 0.8383, 1.347, -31.17), # arRCF + ], +} diff --git a/pythonQEPest/core/qepest.py b/pythonQEPest/core/qepest.py index 4086ce0..09fa569 100644 --- a/pythonQEPest/core/qepest.py +++ b/pythonQEPest/core/qepest.py @@ -1,74 +1,98 @@ +import logging import math +from typing import Optional + +from pydantic import create_model, BaseModel from pythonQEPest.core.qepest_meta import QEPestMeta -from pythonQEPest.dto.QEPestData import QEPestData from pythonQEPest.dto.QEPestInput import QEPestInput from pythonQEPest.dto.QEPestOutput import QEPestOutput +from pythonQEPest.dto.normalisation.Normaliser import Normaliser from pythonQEPest.helpers.check_nan import check_nan from pythonQEPest.helpers.compute_df import compute_df from pythonQEPest.helpers.get_values_from_line import get_values_from_line -from pythonQEPest.helpers.norm import norm_h, norm_f, norm_i from pythonQEPest.helpers.round_to_4digs import round_to_4digs +logger = logging.getLogger(__name__) + class QEPest(QEPestMeta): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def _log_compute_df(self, func, index, lst, data_lst) -> float: + df_result = compute_df(lst[index], *data_lst[index]) + return math.log(func(df_result, index)) + def compute_params(self, data_input: QEPestInput) -> QEPestOutput: self.get_qex_values( get_values_from_line(list(data_input.model_dump().values())) ) return QEPestOutput(data=self.qex, name=data_input.name) - def get_qex_values(self, d) -> None: - def log_compute_df(func, index, lst, data_lst) -> float: - df_result = compute_df(lst[index], *data_lst[index]) - return math.log(func(df_result, index)) + def get_qex_values(self, d) -> BaseModel: + names = [n.split("_")[1] for n in dir(self) if n.startswith("coefficient_")] + + # Coefficients names = ("coefficients_fung, coefficient_herb...) + coefficients_names = [n for n in dir(self) if n.startswith("coefficient_")] + if len(coefficients_names) == 0: + raise ValueError( + "No coefficient_ keys, needs to call " + + "initialize_coefficient and workable config to work" + ) - qe_h = 0.0 - qe_i = 0.0 - qe_f = 0.0 + # coefficients = [{i: getattr(self, i)} for i in coefficients_names] + + # Splitting by _ to get (fung, herb, etc...) to form qe_fung, qe_herb... + # qe_lst = [] + for z in names: + name = f"qe_{z}" + setattr(self, name, 0.0) d_num = len(d) for i in range(d_num): - qe_h += log_compute_df(func=norm_h, index=i, lst=d, data_lst=self.herb) - qe_i += log_compute_df(func=norm_i, index=i, lst=d, data_lst=self.insect) - qe_f += log_compute_df(func=norm_f, index=i, lst=d, data_lst=self.fung) + for name in names: + self.__dict__[f"qe_{name}"] += self._log_compute_df( + func=self.__dict__[f"normaliser_{name}"].norm, + index=i, + lst=d, + data_lst=self.__dict__[f"coefficient_{name}"], + ) q = [ - round_to_4digs(math.exp(qe_h / d_num)), - round_to_4digs(math.exp(qe_i / d_num)), - round_to_4digs(math.exp(qe_f / d_num)), + round_to_4digs(math.exp(self.__dict__[f"qe_{name}"] / d_num)) + for name in names ] result = check_nan(q) - self.qex = QEPestData(qe_h=result[0], qe_i=result[1], qe_f=result[2]) - - def initialize_coefficients(self) -> None: - coefficients = { - "herb": [ - (70.77, 283.0, 84.97, -1.185), # mwH - (93.81, 3.077, 1.434, 0.6164), # logpH - (117.6, 2.409, 1.567, 7.155), # hbaH - (233.4, 0.4535, -1.48, 4.47), # hbdH - (84.7, 4.758, -2.423, 5.437), # rbH - (301.8, 1.101, 0.8869, -22.81), # arRCH - ], - "insect": [ - (76.38, 298.3, 83.64, 1.912), # mwI - (74.27, 4.555, -2.193, -2.987), # logpI - (139.4, 1.363, 1.283, 0.5341), # hbaI - (670.6, -1.163, 0.7856, 0.7951), # hbdI - (65.49, 6.219, -2.448, 5.318), # rbI - (287.5, 0.305, 1.554, -88.64), # arRCI - ], - "fung": [ - (51.03, 314.2, -56.31, 2.342), # mwF - (50.73, 3.674, -1.238, 2.067), # logpF - (73.79, 1.841, 1.326, 0.5158), # hbaF - (164.7, -0.9762, -2.027, 1.384), # hbdF - (40.91, 1.822, 2.582, 0.6235), # rbF - (134.4, 0.8383, 1.347, -31.17), # arRCF - ], - } + + fields = {f"qe_{name}": (float, 0.0) for name in names} + dynamic_model = create_model("QEPestData", **fields) + self.qex = dynamic_model( + **{f"qe_{names[idx]}": name for idx, name in enumerate(result)} + ) + + return self.qex + + # TODO: Coefficients must be in other class + def initialize_coefficients(self, coefficients: Optional = None) -> None: + coefficients = super().initialize_coefficients() for category, data in coefficients.items(): - getattr(self, category).extend(data) + setattr(self, f"coefficient_{category}", data) + + # TODO: Same with Normalisers + def initialize_normalisers(self, normalisers: Optional = None) -> None: + normalisers = super().initialize_normalisers() + + for category, data in normalisers.items(): + setattr(self, f"normaliser_{category}", Normaliser(data)) + + +if __name__ == "__main__": + qepest = QEPest() + qepest.initialize_coefficients() + qepest.initialize_normalisers() + qepest.get_qex_values(1) + print(1) diff --git a/pythonQEPest/core/qepest_meta.py b/pythonQEPest/core/qepest_meta.py index f7f9d8b..0151790 100644 --- a/pythonQEPest/core/qepest_meta.py +++ b/pythonQEPest/core/qepest_meta.py @@ -1,10 +1,11 @@ import os from abc import abstractmethod, ABC + from pythonQEPest.dto import QEPestInput, QEPestOutput, QEPestData class QEPestMeta(ABC): - def __init__(self): + def __init__(self, *args, **kwargs): self.qex: QEPestData | None = None self.herb: list[float] = [] @@ -15,6 +16,7 @@ def __init__(self): self.dir: str = os.getcwd() self.initialize_coefficients() + self.initialize_normalisers() @abstractmethod def compute_params(self, data_input: QEPestInput) -> QEPestOutput: @@ -25,5 +27,17 @@ def get_qex_values(self, d) -> None: pass @abstractmethod - def initialize_coefficients(self) -> None: - pass + def initialize_coefficients(self, coefficients=None) -> None: + if coefficients is None: + from pythonQEPest.config.qepest import qepest_default + + coefficients = qepest_default + return coefficients + + @abstractmethod + def initialize_normalisers(self, normalisers=None) -> None: + if normalisers is None: + from pythonQEPest.config.normalise import normalise_default + + normalisers = normalise_default + return normalisers diff --git a/pythonQEPest/dto/QEPestOutput.py b/pythonQEPest/dto/QEPestOutput.py index a8100f6..d5415b2 100644 --- a/pythonQEPest/dto/QEPestOutput.py +++ b/pythonQEPest/dto/QEPestOutput.py @@ -2,12 +2,10 @@ from pydantic import BaseModel -from pythonQEPest.dto import QEPestData - class QEPestOutput(BaseModel): - data: QEPestData + data: BaseModel name: Optional[str] = "" def to_array(self) -> list: - return [self.name, self.data.qe_h, self.data.qe_i, self.data.qe_f] + return [self.name] + list(self.data.model_dump().values()) diff --git a/pythonQEPest/dto/__init__.py b/pythonQEPest/dto/__init__.py index 45c75d6..aa218a7 100644 --- a/pythonQEPest/dto/__init__.py +++ b/pythonQEPest/dto/__init__.py @@ -1,6 +1,6 @@ from pythonQEPest.dto.QEPestData import QEPestData -from pythonQEPest.dto.QEPestFile import QEPestFile from pythonQEPest.dto.QEPestInput import QEPestInput +from pythonQEPest.dto.QEPestFile import QEPestFile from pythonQEPest.dto.QEPestOutput import QEPestOutput __all__ = [ diff --git a/pythonQEPest/dto/coefficients/QEPestCoefficient.py b/pythonQEPest/dto/coefficients/QEPestCoefficient.py new file mode 100644 index 0000000..27c450f --- /dev/null +++ b/pythonQEPest/dto/coefficients/QEPestCoefficient.py @@ -0,0 +1,57 @@ +from pydantic import BaseModel + +from pythonQEPest.dto.coefficients import QEPestCoefficientNumerics + + +class QEPestCoefficient(BaseModel): + name: str + coefficients: QEPestCoefficientNumerics + + def __init__(self, *args, **kwargs): + if args: + if len(args) != 1: + raise ValueError(f"Expected len(args) == 1, got {len(args)}") + + # In case of {"test": [[1,2,3,4],[1,2,3,4],[1,2,3,4]} + if len(args[0]) == 1: + must_be_dict = args[0] + if isinstance(must_be_dict, dict): + key = list(must_be_dict.keys())[0] + coefficients = QEPestCoefficientNumerics(must_be_dict[key]) + super().__init__(name=key, coefficients=coefficients) + + # In case of ["test", [[1,2,3,4],[1,2,3,4],[1,2,3,4]] + elif len(args[0]) == 2: + arr = args[0] + if ( + isinstance(arr, tuple) + or isinstance(arr, set) + or isinstance(arr, list) + ): + name = arr[0] + coefficients = QEPestCoefficientNumerics(arr[1]) + super().__init__(name=name, coefficients=coefficients) + + elif kwargs: + super().__init__(**kwargs) + else: + raise ValueError( + "No name and coefficients provided. Please provide either a " + + "name and coefficients tuple or keyword arguments." + ) + + +if __name__ == "__main__": + # Example usage + coefficients = QEPestCoefficient( + mw=[0.1, 0.2, 0.3, 0.4], + logp=[0.1, 0.2, 0.3, 0.4], + hba=[0.1, 0.2, 0.3, 0.4], + hbd=[0.1, 0.2, 0.3, 0.4], + rb=[0.1, 0.2, 0.3, 0.4], + ar=[0.1, 0.2, 0.3, 0.4], + ) + print(coefficients) + + coefficients_alternative = QEPestCoefficient([[0.1, 0.2, 0.3, 0.4] * 6]) + print(coefficients_alternative) diff --git a/pythonQEPest/dto/coefficients/QEPestCoefficientList.py b/pythonQEPest/dto/coefficients/QEPestCoefficientList.py new file mode 100644 index 0000000..4975a5d --- /dev/null +++ b/pythonQEPest/dto/coefficients/QEPestCoefficientList.py @@ -0,0 +1,29 @@ +from typing import Optional, List + +from pydantic import BaseModel + +from pythonQEPest.dto.coefficients import QEPestCoefficient + + +class QEPestCoefficientList(BaseModel): + name: Optional[str] + named_coefficients: List[QEPestCoefficient] + + # TODO: Done all the cases and make some sort of docs here, pls + def __init__(self, *args, **kwargs): + if args: + named_coeff = args[0] + if len(named_coeff) == 1: + pass + elif len(named_coeff) > 1: + if isinstance(named_coeff, dict): + lst = [QEPestCoefficient({i: z}) for i, z in named_coeff.items()] + super().__init__(name="generic", named_coefficients=lst) + + elif kwargs: + super().__init__(**kwargs) + else: + raise ValueError( + "No name and coefficients provided. Please provide either a " + + "name and coefficients tuple or keyword arguments." + ) diff --git a/pythonQEPest/dto/coefficients/QEPestCoefficientNumerics.py b/pythonQEPest/dto/coefficients/QEPestCoefficientNumerics.py new file mode 100644 index 0000000..8c51224 --- /dev/null +++ b/pythonQEPest/dto/coefficients/QEPestCoefficientNumerics.py @@ -0,0 +1,44 @@ +from pydantic import BaseModel + +CoefficientTuple = tuple[float, float, float, float] + + +class QEPestCoefficientNumerics(BaseModel): + mw: CoefficientTuple + logp: CoefficientTuple + hba: CoefficientTuple + hbd: CoefficientTuple + rb: CoefficientTuple + ar: CoefficientTuple + + def __init__(self, *args, **kwargs): + if args: + # In case of [[1,2,3,4],[1,2,3,4],[1,2,3,4]] + # I don't want it to be a pain to init D: + if len(args) != 1: + raise ValueError( + f"Expected a single list of coefficients, got {len(args)}" + ) + + coefficients = args[0] + if len(coefficients) != 6: + raise ValueError( + "Expected list with structure [[1,2,3,4,5,6], ", + "[1,2,3,4,5,6], [1,...] ...] got ", + f"{len(coefficients)} and {coefficients}", + ) + + coefficients_mapped = { + field: coefficients[i] + for i, field in enumerate(["mw", "logp", "hba", "hbd", "rb", "ar"]) + } + super().__init__(**coefficients_mapped) + + elif kwargs: + super().__init__(**kwargs) + + else: + raise ValueError( + "No coefficients provided. Please provide either a ", + "list of coefficient tuples or keyword arguments.", + ) diff --git a/pythonQEPest/dto/coefficients/__init__.py b/pythonQEPest/dto/coefficients/__init__.py new file mode 100644 index 0000000..94e829f --- /dev/null +++ b/pythonQEPest/dto/coefficients/__init__.py @@ -0,0 +1,7 @@ +from pythonQEPest.dto.coefficients.QEPestCoefficientNumerics import ( + QEPestCoefficientNumerics, +) +from pythonQEPest.dto.coefficients.QEPestCoefficient import QEPestCoefficient +from pythonQEPest.dto.coefficients.QEPestCoefficientList import QEPestCoefficientList + +__all__ = ["QEPestCoefficientNumerics", "QEPestCoefficient", "QEPestCoefficientList"] diff --git a/pythonQEPest/dto/normalisation/Normaliser.py b/pythonQEPest/dto/normalisation/Normaliser.py new file mode 100644 index 0000000..ace3606 --- /dev/null +++ b/pythonQEPest/dto/normalisation/Normaliser.py @@ -0,0 +1,16 @@ +from typing import Union + + +class Normaliser: + def __init__(self, arr: Union[tuple, list]): + # if len(arr) != 6: + # raise ValueError(f"Array must be exact length of 6. Given: {len(arr)}") + + self.arr = arr + + def norm(self, d: Union[int, float], descr: int) -> float: + if descr > len(self.arr): + raise KeyError(f"Out of array. {descr} > {len(self.arr)}") + + max_val = self.arr[int(descr)] + return d / max_val if max_val != 0 else 0.0 diff --git a/pythonQEPest/dto/normalisation/__init__.py b/pythonQEPest/dto/normalisation/__init__.py new file mode 100644 index 0000000..3c2db34 --- /dev/null +++ b/pythonQEPest/dto/normalisation/__init__.py @@ -0,0 +1,3 @@ +from pythonQEPest.dto.normalisation.Normaliser import Normaliser + +__all__ = ["Normaliser"] diff --git a/tests/config/__init__.py b/tests/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/config/test_coefficients.py b/tests/config/test_coefficients.py new file mode 100644 index 0000000..8816b23 --- /dev/null +++ b/tests/config/test_coefficients.py @@ -0,0 +1,369 @@ +from pythonQEPest.dto.coefficients import ( + QEPestCoefficient, + QEPestCoefficientNumerics, + QEPestCoefficientList, +) + + +class TestQEPestCoefficients: + def test_qepest_coefficient_numerics_strict(self): + qep_coeff_num = QEPestCoefficientNumerics( + mw=(0.1, 0.2, 0.3, 0.4), + logp=(0.1, 0.2, 0.3, 0.4), + hba=(0.1, 0.2, 0.3, 0.4), + hbd=(0.1, 0.2, 0.3, 0.4), + rb=(0.1, 0.2, 0.3, 0.4), + ar=(0.1, 0.2, 0.3, 0.4), + ) + + assert qep_coeff_num.mw == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff_num.logp == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff_num.hba == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff_num.hbd == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff_num.rb == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff_num.ar == (0.1, 0.2, 0.3, 0.4) + + def test_qepest_coefficient_numerics_unstrict(self): + qep_coeff_num = QEPestCoefficientNumerics([[0.1, 0.2, 0.3, 0.4]] * 6) + + assert qep_coeff_num.mw == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff_num.logp == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff_num.hba == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff_num.hbd == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff_num.rb == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff_num.ar == (0.1, 0.2, 0.3, 0.4) + + def test_qepest_coefficients_strict(self): + coefficients_name = "herb" + coefficients_dict = [ + [0.1, 0.2, 0.3, 0.4], + [0.1, 0.2, 0.3, 0.4], + [0.1, 0.2, 0.3, 0.4], + [0.1, 0.2, 0.3, 0.4], + [0.1, 0.2, 0.3, 0.4], + [0.1, 0.2, 0.3, 0.4], + ] + qep_coeff = QEPestCoefficient( + name=coefficients_name, + coefficients=QEPestCoefficientNumerics(coefficients_dict), + ) + + assert qep_coeff.name == "herb" + assert qep_coeff.coefficients.mw == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff.coefficients.logp == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff.coefficients.hba == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff.coefficients.hbd == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff.coefficients.rb == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff.coefficients.ar == (0.1, 0.2, 0.3, 0.4) + + def test_qepest_coefficients_unstrict(self): + coefficients_dict = { + "herb": [ + [0.1, 0.2, 0.3, 0.4], + [0.1, 0.2, 0.3, 0.4], + [0.1, 0.2, 0.3, 0.4], + [0.1, 0.2, 0.3, 0.4], + [0.1, 0.2, 0.3, 0.4], + [0.1, 0.2, 0.3, 0.4], + ] + } + qep_coeff = QEPestCoefficient(coefficients_dict) + + assert qep_coeff.name == "herb" + assert qep_coeff.coefficients.mw == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff.coefficients.logp == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff.coefficients.hba == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff.coefficients.hbd == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff.coefficients.rb == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff.coefficients.ar == (0.1, 0.2, 0.3, 0.4) + + def test_qepest_coefficients_unstrict_chaos(self): + coefficients_dict = [ + "herb", + [ + [0.1, 0.2, 0.3, 0.4], + [0.1, 0.2, 0.3, 0.4], + [0.1, 0.2, 0.3, 0.4], + [0.1, 0.2, 0.3, 0.4], + [0.1, 0.2, 0.3, 0.4], + [0.1, 0.2, 0.3, 0.4], + ], + ] + + qep_coeff = QEPestCoefficient(coefficients_dict) + + assert qep_coeff.name == "herb" + assert qep_coeff.coefficients.mw == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff.coefficients.logp == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff.coefficients.hba == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff.coefficients.hbd == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff.coefficients.rb == (0.1, 0.2, 0.3, 0.4) + assert qep_coeff.coefficients.ar == (0.1, 0.2, 0.3, 0.4) + + def test_qepest_coefficients_lst_basic(self): + coefficients_dict = { + "test1": [ + (0.1, 0.2, 0.3, 0.4), # mwI + (0.1, 0.2, 0.3, 0.4), # logpI + (0.1, 0.2, 0.3, 0.4), # hbaI + (0.1, 0.2, 0.3, 0.4), # hbdI + (0.1, 0.2, 0.3, 0.4), # rbI + (0.1, 0.2, 0.3, 0.4), # arRCI + ], + "test2": [ + (0.1, 0.2, 0.3, 0.4), # mwF + (0.1, 0.2, 0.3, 0.4), # logpF + (0.1, 0.2, 0.3, 0.4), # hbaF + (0.1, 0.2, 0.3, 0.4), # hbdF + (0.1, 0.2, 0.3, 0.4), # rbF + (0.1, 0.2, 0.3, 0.4), # arRCF + ], + } + + qep_coeff_lst = QEPestCoefficientList(coefficients_dict) + + assert qep_coeff_lst.name == "generic" + assert qep_coeff_lst.named_coefficients is not None + + assert len(qep_coeff_lst.named_coefficients) == 2 + + assert qep_coeff_lst.named_coefficients[0].name == "test1" + + assert qep_coeff_lst.named_coefficients[0].coefficients.mw == ( + 0.1, + 0.2, + 0.3, + 0.4, + ) + assert qep_coeff_lst.named_coefficients[0].coefficients.logp == ( + 0.1, + 0.2, + 0.3, + 0.4, + ) + assert qep_coeff_lst.named_coefficients[0].coefficients.hba == ( + 0.1, + 0.2, + 0.3, + 0.4, + ) + assert qep_coeff_lst.named_coefficients[0].coefficients.hbd == ( + 0.1, + 0.2, + 0.3, + 0.4, + ) + assert qep_coeff_lst.named_coefficients[0].coefficients.rb == ( + 0.1, + 0.2, + 0.3, + 0.4, + ) + assert qep_coeff_lst.named_coefficients[0].coefficients.ar == ( + 0.1, + 0.2, + 0.3, + 0.4, + ) + + assert qep_coeff_lst.named_coefficients[1].name == "test2" + + assert qep_coeff_lst.named_coefficients[1].coefficients.mw == ( + 0.1, + 0.2, + 0.3, + 0.4, + ) + assert qep_coeff_lst.named_coefficients[1].coefficients.logp == ( + 0.1, + 0.2, + 0.3, + 0.4, + ) + assert qep_coeff_lst.named_coefficients[1].coefficients.hba == ( + 0.1, + 0.2, + 0.3, + 0.4, + ) + assert qep_coeff_lst.named_coefficients[1].coefficients.hbd == ( + 0.1, + 0.2, + 0.3, + 0.4, + ) + assert qep_coeff_lst.named_coefficients[1].coefficients.rb == ( + 0.1, + 0.2, + 0.3, + 0.4, + ) + assert qep_coeff_lst.named_coefficients[1].coefficients.ar == ( + 0.1, + 0.2, + 0.3, + 0.4, + ) + + def test_qepest_coefficients_lst_w_import(self): + from pythonQEPest.config.qepest import qepest_default + + qep_coeff_lst = QEPestCoefficientList(qepest_default) + + assert qep_coeff_lst.name == "generic" + assert qep_coeff_lst.named_coefficients is not None + + assert len(qep_coeff_lst.named_coefficients) == 3 + + assert qep_coeff_lst.named_coefficients[0].name == "herb" + + assert qep_coeff_lst.named_coefficients[0].coefficients.mw == ( + 70.77, + 283.0, + 84.97, + -1.185, + ) + assert qep_coeff_lst.named_coefficients[0].coefficients.logp == ( + 93.81, + 3.077, + 1.434, + 0.6164, + ) + assert qep_coeff_lst.named_coefficients[0].coefficients.hba == ( + 117.6, + 2.409, + 1.567, + 7.155, + ) + assert qep_coeff_lst.named_coefficients[0].coefficients.hbd == ( + 233.4, + 0.4535, + -1.48, + 4.47, + ) + assert qep_coeff_lst.named_coefficients[0].coefficients.rb == ( + 84.7, + 4.758, + -2.423, + 5.437, + ) + assert qep_coeff_lst.named_coefficients[0].coefficients.ar == ( + 301.8, + 1.101, + 0.8869, + -22.81, + ) + + assert qep_coeff_lst.named_coefficients[1].name == "insect" + + assert qep_coeff_lst.named_coefficients[1].coefficients.mw == ( + 76.38, + 298.3, + 83.64, + 1.912, + ) + assert qep_coeff_lst.named_coefficients[1].coefficients.logp == ( + 74.27, + 4.555, + -2.193, + -2.987, + ) + assert qep_coeff_lst.named_coefficients[1].coefficients.hba == ( + 139.4, + 1.363, + 1.283, + 0.5341, + ) + assert qep_coeff_lst.named_coefficients[1].coefficients.hbd == ( + 670.6, + -1.163, + 0.7856, + 0.7951, + ) + assert qep_coeff_lst.named_coefficients[1].coefficients.rb == ( + 65.49, + 6.219, + -2.448, + 5.318, + ) + assert qep_coeff_lst.named_coefficients[1].coefficients.ar == ( + 287.5, + 0.305, + 1.554, + -88.64, + ) + + assert qep_coeff_lst.named_coefficients[2].name == "fung" + + assert qep_coeff_lst.named_coefficients[2].coefficients.mw == ( + 51.03, + 314.2, + -56.31, + 2.342, + ) + assert qep_coeff_lst.named_coefficients[2].coefficients.logp == ( + 50.73, + 3.674, + -1.238, + 2.067, + ) + assert qep_coeff_lst.named_coefficients[2].coefficients.hba == ( + 73.79, + 1.841, + 1.326, + 0.5158, + ) + assert qep_coeff_lst.named_coefficients[2].coefficients.hbd == ( + 164.7, + -0.9762, + -2.027, + 1.384, + ) + assert qep_coeff_lst.named_coefficients[2].coefficients.rb == ( + 40.91, + 1.822, + 2.582, + 0.6235, + ) + assert qep_coeff_lst.named_coefficients[2].coefficients.ar == ( + 134.4, + 0.8383, + 1.347, + -31.17, + ) + + def test_qepest_default_coefficients(self): + from pythonQEPest.config.qepest import qepest_default + + assert "fung" in qepest_default + assert "herb" in qepest_default + assert "insect" in qepest_default + + coefficients = { + "herb": [ + (70.77, 283.0, 84.97, -1.185), # mwH + (93.81, 3.077, 1.434, 0.6164), # logpH + (117.6, 2.409, 1.567, 7.155), # hbaH + (233.4, 0.4535, -1.48, 4.47), # hbdH + (84.7, 4.758, -2.423, 5.437), # rbH + (301.8, 1.101, 0.8869, -22.81), # arRCH + ], + "insect": [ + (76.38, 298.3, 83.64, 1.912), # mwI + (74.27, 4.555, -2.193, -2.987), # logpI + (139.4, 1.363, 1.283, 0.5341), # hbaI + (670.6, -1.163, 0.7856, 0.7951), # hbdI + (65.49, 6.219, -2.448, 5.318), # rbI + (287.5, 0.305, 1.554, -88.64), # arRCI + ], + "fung": [ + (51.03, 314.2, -56.31, 2.342), # mwF + (50.73, 3.674, -1.238, 2.067), # logpF + (73.79, 1.841, 1.326, 0.5158), # hbaF + (164.7, -0.9762, -2.027, 1.384), # hbdF + (40.91, 1.822, 2.582, 0.6235), # rbF + (134.4, 0.8383, 1.347, -31.17), # arRCF + ], + } + + assert qepest_default == coefficients diff --git a/tests/config/test_normalisation.py b/tests/config/test_normalisation.py new file mode 100644 index 0000000..88350bd --- /dev/null +++ b/tests/config/test_normalisation.py @@ -0,0 +1,36 @@ +class TestQEPestNormalisation: + def test_qepest_default_coefficients(self): + from pythonQEPest.config.normalise import normalise_default + + assert "fung" in normalise_default + assert "herb" in normalise_default + assert "insect" in normalise_default + + normalise = { + "herb": ( + 69.5849922, + 94.4228257, + 120.4572352, + 228.1589796, + 89.7012502, + 276.9634213, + ), + "insect": ( + 78.2919965, + 71.2829691, + 133.9224801, + 331.170104, + 70.5540709, + 193.0023343, + ), + "fung": ( + 53.3719946, + 52.773116, + 73.7976536, + 144.9887053, + 41.4385926, + 102.3024319, + ), + } + + assert normalise_default == normalise diff --git a/tests/core/test_qepest.py b/tests/core/test_qepest.py index 6549d40..eb7fc46 100644 --- a/tests/core/test_qepest.py +++ b/tests/core/test_qepest.py @@ -2,7 +2,6 @@ import pytest from pydantic import ValidationError - from pythonQEPest.core.qepest import QEPest from pythonQEPest.dto import QEPestInput @@ -21,39 +20,46 @@ def make_input(self, name="test", mw=120.5, logp=3.1, hba=2, hbd=1, rb=4, ar=1): ) def test_qepest_compute_params_basic(self): - qep = QEPest() - qep.initialize_coefficients() + qepest = QEPest() + + qepest.initialize_coefficients() + qepest.initialize_normalisers() + inp = self.make_input() - result = qep.compute_params(inp) + result = qepest.compute_params(inp) + assert hasattr(result, "data") - assert hasattr(result.data, "qe_f") - assert hasattr(result.data, "qe_h") - assert hasattr(result.data, "qe_i") - assert math.isfinite(result.data.qe_h) - assert math.isfinite(result.data.qe_i) - assert math.isfinite(result.data.qe_f) + assert hasattr(result.data, "qe_fung") + assert hasattr(result.data, "qe_herb") + assert hasattr(result.data, "qe_insect") + + assert math.isfinite(result.data.qe_herb) + assert math.isfinite(result.data.qe_insect) + assert math.isfinite(result.data.qe_fung) + assert hasattr(result, "name") assert result.name == inp.name def test_qepest_zero_input(self): - qep = QEPest() - qep.initialize_coefficients() + qepest = QEPest() + inp = self.make_input(mw=0, logp=0, hba=0, hbd=0, rb=0, ar=0) + with pytest.raises(ValueError): - qep.compute_params(inp) + qepest.compute_params(inp) def test_qepest_multiple_names(self): - qep = QEPest() - qep.initialize_coefficients() + qepest = QEPest() + for i in range(3): name = f"test_{i}" inp = self.make_input(name=name) - result = qep.compute_params(inp) + result = qepest.compute_params(inp) assert result.name == name def test_qepest_nan_protection(self): - qep = QEPest() - qep.initialize_coefficients() + # qepest = QEPest() + with pytest.raises(ValidationError): self.make_input( mw=float("nan"), @@ -65,50 +71,57 @@ def test_qepest_nan_protection(self): ) def test_qepest_compare_with_original(self): - qep = QEPest() + qepest = QEPest() - result = qep.compute_params( + result = qepest.compute_params( self.make_input("mol1", 240.2127, 3.2392, 5, 1, 4, 1) ) assert result.name == "mol1" - assert result.data.qe_h == 0.8511 - assert result.data.qe_i == 0.5339 - assert result.data.qe_f == 0.6224 - assert result.to_array() == ["mol1", 0.8511, 0.5339, 0.6224] + assert result.data.qe_herb == 0.8511 + assert result.data.qe_insect == 0.5339 + assert result.data.qe_fung == 0.6224 + + assert result.to_array() == ["mol1", 0.6224, 0.8511, 0.5339] - result = qep.compute_params( + result = qepest.compute_params( self.make_input("mol2", 249.091, 3.0273, 3, 1, 5, 1) ) assert result.name == "mol2" - assert result.data.qe_h == 0.975 - assert result.data.qe_i == 0.6913 - assert result.data.qe_f == 0.731 - assert result.to_array() == ["mol2", 0.975, 0.6913, 0.731] - result = qep.compute_params( + assert result.data.qe_herb == 0.975 + assert result.data.qe_insect == 0.6913 + assert result.data.qe_fung == 0.731 + + assert result.to_array() == ["mol2", 0.731, 0.975, 0.6913] + + result = qepest.compute_params( self.make_input("mol3", 308.354, 2.1086, 1, 0, 7, 1) ) assert result.name == "mol3" - assert result.data.qe_h == 0.798 - assert result.data.qe_i == 0.9018 - assert result.data.qe_f == 0.732 - assert result.to_array() == ["mol3", 0.798, 0.9018, 0.732] - result = qep.compute_params( + assert result.data.qe_herb == 0.798 + assert result.data.qe_insect == 0.9018 + assert result.data.qe_fung == 0.732 + + assert result.to_array() == ["mol3", 0.732, 0.798, 0.9018] + + result = qepest.compute_params( self.make_input("mol4", 360.444, 4.0137, 3, 0, 8, 0) ) assert result.name == "mol4" - assert result.data.qe_h == 0.5839 - assert result.data.qe_i == 0.8382 - assert result.data.qe_f == 0.6594 - assert result.to_array() == ["mol4", 0.5839, 0.8382, 0.6594] + assert result.data.qe_herb == 0.5839 + assert result.data.qe_insect == 0.8382 + assert result.data.qe_fung == 0.6594 - result = qep.compute_params( + assert result.to_array() == ["mol4", 0.6594, 0.5839, 0.8382] + + result = qepest.compute_params( self.make_input("mol5", 295.335, 4.9335, 2, 0, 1, 1) ) assert result.name == "mol5" - assert result.data.qe_h == 0.8099 - assert result.data.qe_i == 0.8118 - assert result.data.qe_f == 0.8742 - assert result.to_array() == ["mol5", 0.8099, 0.8118, 0.8742] + assert result.data.qe_herb == 0.8099 + assert result.data.qe_insect == 0.8118 + assert result.data.qe_fung == 0.8742 + + assert result.to_array() == ["mol5", 0.8742, 0.8099, 0.8118] diff --git a/tests/dto/test_norm.py b/tests/dto/test_norm.py new file mode 100644 index 0000000..2125c1a --- /dev/null +++ b/tests/dto/test_norm.py @@ -0,0 +1,74 @@ +import pytest + +from pythonQEPest.dto.normalisation.Normaliser import Normaliser + +# from pythonQEPest.helpers.norm import norm_i, norm_h, norm, norm_f + + +class TestNorm: + def test_norm_basic(self): + arr = [10, 20, 30] + d = 20 + + normaliser = Normaliser(arr) + + assert normaliser.norm(d, 1) == 1.0 + assert normaliser.norm(d, 2) == 20 / 30 + assert normaliser.norm(d, 0) == 2.0 + + def test_norm_zero_div(self): + arr = [10, 0, 30] + d = 5 + + normaliser = Normaliser(arr) + + assert normaliser.norm(d, 1) == 0.0 + + def test_norm_out_of_range(self): + arr = [1, 2, 3] + + normaliser = Normaliser(arr) + with pytest.raises(KeyError): + normaliser.norm(2, 5) + + def test_norm_negative_descr(self): + arr = [1, 2, 3] + + normaliser = Normaliser(arr) + + assert normaliser.norm(2, -1) == 2 / 3 + + def test_norm_h(self): + from pythonQEPest.config import normalise_default + + normaliser = Normaliser(normalise_default["herb"]) + + assert normaliser.norm(69.5849922, 0) == 1.0 + assert normaliser.norm(0, 2) == 0.0 + assert pytest.approx(normaliser.norm(60, 1), rel=1e-6) == 60 / 94.4228257 + + def test_norm_i(self): + from pythonQEPest.config import normalise_default + + normaliser = Normaliser(normalise_default["insect"]) + + assert normaliser.norm(78.2919965, 0) == 1.0 + assert normaliser.norm(0, 3) == 0.0 + assert pytest.approx(normaliser.norm(66, 1), rel=1e-6) == 66 / 71.2829691 + + def test_norm_f(self): + from pythonQEPest.config import normalise_default + + normaliser = Normaliser(normalise_default["fung"]) + + assert normaliser.norm(53.3719946, 0) == 1.0 + assert normaliser.norm(0, 5) == 0.0 + assert pytest.approx(normaliser.norm(70, 2), rel=1e-6) == 70 / 73.7976536 + + def test_norm_invalid_descr_type(self): + arr = [1, 2, 3] + + normaliser = Normaliser(arr) + + with pytest.raises(TypeError): + normaliser.norm(1, "not_an_int") diff --git a/tests/helpers/test_norm.py b/tests/helpers/test_norm.py deleted file mode 100644 index 6e1ebd3..0000000 --- a/tests/helpers/test_norm.py +++ /dev/null @@ -1,47 +0,0 @@ -import pytest - -from pythonQEPest.helpers.norm import norm_i, norm_h, norm, norm_f - - -class TestNorm: - def test_norm_basic(self): - arr = [10, 20, 30] - d = 20 - - assert norm(arr, d, 1) == 1.0 - assert norm(arr, d, 2) == 20 / 30 - assert norm(arr, d, 0) == 2.0 - - def test_norm_zero_div(self): - arr = [10, 0, 30] - d = 5 - assert norm(arr, d, 1) == 0.0 - - def test_norm_out_of_range(self): - arr = [1, 2, 3] - with pytest.raises(KeyError): - norm(arr, 2, 5) - - def test_norm_negative_descr(self): - arr = [1, 2, 3] - assert norm(arr, 2, -1) == 2 / 3 - - def test_norm_h(self): - assert norm_h(69.5849922, 0) == 1.0 - assert norm_h(0, 2) == 0.0 - assert pytest.approx(norm_h(60, 1), rel=1e-6) == 60 / 94.4228257 - - def test_norm_i(self): - assert norm_i(78.2919965, 0) == 1.0 - assert norm_i(0, 3) == 0.0 - assert pytest.approx(norm_i(66, 1), rel=1e-6) == 66 / 71.2829691 - - def test_norm_f(self): - assert norm_f(53.3719946, 0) == 1.0 - assert norm_f(0, 5) == 0.0 - assert pytest.approx(norm_f(70, 2), rel=1e-6) == 70 / 73.7976536 - - def test_norm_invalid_descr_type(self): - arr = [1, 2, 3] - with pytest.raises(TypeError): - norm(arr, 1, "not_an_int") diff --git a/tests/public_api/test_import_contract.py b/tests/public_api/test_import_contract.py index 1a53560..9880604 100644 --- a/tests/public_api/test_import_contract.py +++ b/tests/public_api/test_import_contract.py @@ -1,4 +1,5 @@ import pytest +from pydantic import BaseModel from pythonQEPest import QEPest, QEPestData, QEPestInput, QEPestOutput @@ -25,10 +26,14 @@ def test_public_api_compute_smoke(self): result = model.compute_params(payload) assert isinstance(result, QEPestOutput) - assert isinstance(result.data, QEPestData) + assert isinstance(result.data, BaseModel) assert result.name == "mol1" - assert result.data == QEPestData(qe_h=0.9357, qe_i=0.7146, qe_f=0.8022) + assert result.data.model_dump() == { + "qe_herb": 0.9357, + "qe_insect": 0.7146, + "qe_fung": 0.8022, + } @pytest.mark.skip(reason="Need to find optional approach") def test_helpers_imports(self): From 2bf62b91a1b952c698dbaa998587dd80f63d91e2 Mon Sep 17 00:00:00 2001 From: Lina Date: Sun, 1 Mar 2026 01:25:36 +0100 Subject: [PATCH 3/7] fix[python-package]: poetry --- .github/workflows/python-package.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index b8eb251..3453804 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -28,11 +28,12 @@ jobs: cache: poetry cache-dependency-path: poetry.lock - - name: Install Poetry (pipx) - run: | - python -m pip install --upgrade pip - python -m pip install pipx - pipx install poetry + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + version: latest + virtualenvs-create: true + virtualenvs-in-project: true - name: Install dependencies run: | From 725f77af3e71421e6146a20f159d204249077fea Mon Sep 17 00:00:00 2001 From: Lina Date: Sun, 1 Mar 2026 01:29:31 +0100 Subject: [PATCH 4/7] fix[workflow]: Again? --- .github/workflows/python-package.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 3453804..57b2ddc 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -20,13 +20,10 @@ jobs: steps: - uses: actions/checkout@v6 - - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - cache: poetry - cache-dependency-path: poetry.lock - name: Install Poetry uses: snok/install-poetry@v1 From 35ea8e468a9f1565ee484562898f3b3fd978edab Mon Sep 17 00:00:00 2001 From: Lina Date: Sun, 1 Mar 2026 01:30:53 +0100 Subject: [PATCH 5/7] fix[poetry]: lock ;/ --- poetry.lock | 166 ++-------------------------------------------------- 1 file changed, 5 insertions(+), 161 deletions(-) diff --git a/poetry.lock b/poetry.lock index 20db0cc..5910abe 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.3.1 and should not be changed by hand. [[package]] name = "altgraph" @@ -37,30 +37,12 @@ files = [ ] [package.dependencies] -exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] trio = ["trio (>=0.31.0) ; python_version < \"3.10\"", "trio (>=0.32.0) ; python_version >= \"3.10\""] -[[package]] -name = "backports-tarfile" -version = "1.2.0" -description = "Backport of CPython tarfile module" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -markers = "python_version < \"3.12\"" -files = [ - {file = "backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34"}, - {file = "backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["jaraco.test", "pytest (!=8.0.*)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)"] - [[package]] name = "build" version = "1.4.0" @@ -75,10 +57,8 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "os_name == \"nt\""} -importlib-metadata = {version = ">=4.6", markers = "python_full_version < \"3.10.2\""} packaging = ">=24.0" pyproject_hooks = "*" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [package.extras] uv = ["uv (>=0.1.18)"] @@ -482,9 +462,6 @@ files = [ {file = "coverage-7.13.3.tar.gz", hash = "sha256:f7f6182d3dfb8802c1747eacbfe611b669455b69b7c037484bb1efbbb56711ac"}, ] -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - [package.extras] toml = ["tomli ; python_full_version <= \"3.11.0a6\""] @@ -562,7 +539,6 @@ files = [ [package.dependencies] cffi = {version = ">=2.0.0", markers = "python_full_version >= \"3.9.0\" and platform_python_implementation != \"PyPy\""} -typing-extensions = {version = ">=4.13.2", markers = "python_full_version < \"3.11.0\""} [package.extras] docs = ["sphinx (>=5.3.0)", "sphinx-inline-tabs", "sphinx-rtd-theme (>=3.0.0)"] @@ -623,7 +599,6 @@ files = [ ] [package.dependencies] -typing_extensions = {version = ">=4.6.0", markers = "python_version < \"3.12\""} urllib3 = ">=2.2.2" [package.extras] @@ -638,25 +613,6 @@ paramiko = ["paramiko"] patiencediff = ["patiencediff"] pgp = ["gpg"] -[[package]] -name = "exceptiongroup" -version = "1.3.1" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -markers = "python_version == \"3.10\"" -files = [ - {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, - {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} - -[package.extras] -test = ["pytest (>=6)"] - [[package]] name = "fastjsonschema" version = "2.21.2" @@ -789,31 +745,6 @@ files = [ [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] -[[package]] -name = "importlib-metadata" -version = "8.7.1" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -markers = "python_version < \"3.12\"" -files = [ - {file = "importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151"}, - {file = "importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb"}, -] - -[package.dependencies] -zipp = ">=3.20" - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=3.4)"] -perf = ["ipython"] -test = ["flufl.flake8", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] -type = ["mypy (<1.19) ; platform_python_implementation == \"PyPy\"", "pytest-mypy (>=1.0.1)"] - [[package]] name = "iniconfig" version = "2.3.0" @@ -869,9 +800,6 @@ files = [ {file = "jaraco_context-6.1.0.tar.gz", hash = "sha256:129a341b0a85a7db7879e22acd66902fda67882db771754574338898b2d5d86f"}, ] -[package.dependencies] -"backports.tarfile" = {version = "*", markers = "python_version < \"3.12\""} - [package.extras] check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] @@ -933,7 +861,6 @@ files = [ ] [package.dependencies] -importlib_metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""} "jaraco.classes" = "*" "jaraco.context" = "*" "jaraco.functools" = "*" @@ -1184,7 +1111,6 @@ files = [ pastel = ">=0.2.1,<0.3.0" poetry = {version = ">=1.2.0,<3.0.0", optional = true, markers = "python_version < \"4.0\" and extra == \"poetry-plugin\""} pyyaml = ">=6.0.3,<7.0" -tomli = {version = ">=1.3.0", markers = "python_version < \"3.11\""} [package.extras] poetry-plugin = ["poetry (>=1.2.0,<3.0.0) ; python_version < \"4.0\""] @@ -1219,7 +1145,6 @@ pyproject-hooks = ">=1.0.0,<2.0.0" requests = ">=2.26,<3.0" requests-toolbelt = ">=1.0.0,<2.0.0" shellingham = ">=1.5,<2.0" -tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version < \"3.11\""} tomlkit = ">=0.11.4,<1.0.0" trove-classifiers = ">=2022.5.19" virtualenv = ">=20.26.6" @@ -1530,12 +1455,10 @@ files = [ [package.dependencies] colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} iniconfig = ">=1.0.1" packaging = ">=22" pluggy = ">=1.5,<2" pygments = ">=2.7.2" -tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] @@ -1854,64 +1777,6 @@ files = [ {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, ] -[[package]] -name = "tomli" -version = "2.4.0" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -markers = "python_full_version <= \"3.11.0a6\"" -files = [ - {file = "tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867"}, - {file = "tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9"}, - {file = "tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95"}, - {file = "tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76"}, - {file = "tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d"}, - {file = "tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576"}, - {file = "tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a"}, - {file = "tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa"}, - {file = "tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614"}, - {file = "tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1"}, - {file = "tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8"}, - {file = "tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a"}, - {file = "tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1"}, - {file = "tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b"}, - {file = "tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51"}, - {file = "tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729"}, - {file = "tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da"}, - {file = "tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3"}, - {file = "tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0"}, - {file = "tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e"}, - {file = "tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4"}, - {file = "tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e"}, - {file = "tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c"}, - {file = "tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f"}, - {file = "tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86"}, - {file = "tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87"}, - {file = "tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132"}, - {file = "tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6"}, - {file = "tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc"}, - {file = "tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66"}, - {file = "tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d"}, - {file = "tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702"}, - {file = "tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8"}, - {file = "tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776"}, - {file = "tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475"}, - {file = "tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2"}, - {file = "tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9"}, - {file = "tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0"}, - {file = "tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df"}, - {file = "tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d"}, - {file = "tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f"}, - {file = "tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b"}, - {file = "tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087"}, - {file = "tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd"}, - {file = "tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4"}, - {file = "tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a"}, - {file = "tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c"}, -] - [[package]] name = "tomlkit" version = "0.14.0" @@ -1947,6 +1812,7 @@ files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] +markers = {dev = "python_version == \"3.12\""} [[package]] name = "typing-inspection" @@ -1997,7 +1863,6 @@ files = [ distlib = ">=0.3.7,<1" filelock = {version = ">=3.20.1,<4", markers = "python_version >= \"3.10\""} platformdirs = ">=3.9.1,<5" -typing-extensions = {version = ">=4.13.2", markers = "python_version < \"3.11\""} [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] @@ -2070,27 +1935,6 @@ cffi = ">=1.16.0" [package.extras] test = ["pytest"] -[[package]] -name = "zipp" -version = "3.23.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -markers = "python_version < \"3.12\"" -files = [ - {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, - {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, -] - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] -type = ["pytest-mypy"] - [[package]] name = "zstandard" version = "0.25.0" @@ -2201,12 +2045,12 @@ files = [ ] [package.extras] -cffi = ["cffi (>=1.17,<2.0) ; platform_python_implementation != \"PyPy\" and python_version < \"3.14\"", "cffi (>=2.0.0b) ; platform_python_implementation != \"PyPy\" and python_version >= \"3.14\""] +cffi = ["cffi (>=1.17,<2.0) ; platform_python_implementation != \"PyPy\" and python_version < \"3.14\"", "cffi (>=2.0.0b0) ; platform_python_implementation != \"PyPy\" and python_version >= \"3.14\""] [extras] gui = ["pyperclip"] [metadata] lock-version = "2.1" -python-versions = ">=3.10,<3.13" -content-hash = "8ba93ad768359952eb49357899071f484c212fc3b6d29cd85926a49c5b1b6400" +python-versions = ">=3.12,<3.15" +content-hash = "29c0863bfefcb10cd0abfe597cd27281c5e3149ffbbe502e0fed03241e73826c" From 17f21a88b915ebe10ca8e981674066e9d4d8d6b5 Mon Sep 17 00:00:00 2001 From: Lina Date: Sun, 1 Mar 2026 16:11:47 +0100 Subject: [PATCH 6/7] feat[cli]: update version to 1.4.0 and refactor CLI and file handling --- pyproject.toml | 2 +- pythonQEPest/cli/cli.py | 82 +++++-------------- pythonQEPest/config/__init__.py | 2 +- .../config/{qepest.py => qepest_default.py} | 0 pythonQEPest/core/qepest.py | 42 ++++++++-- pythonQEPest/core/qepest_meta.py | 12 ++- pythonQEPest/dto/QEPestFile.py | 27 ++++-- pythonQEPest/dto/__init__.py | 9 +- pythonQEPest/gui/gui.py | 1 + pythonQEPest/logger.py | 3 + pythonQEPest/services/QEPestFileService.py | 66 +++++++++++++++ pythonQEPest/services/__init__.py | 3 + tests/cli/test_cli_contract.py | 78 ++++++++++++++++-- tests/config/test_coefficients.py | 4 +- tests/dto/test_qepest_file.py | 6 +- 15 files changed, 236 insertions(+), 101 deletions(-) rename pythonQEPest/config/{qepest.py => qepest_default.py} (100%) create mode 100644 pythonQEPest/services/QEPestFileService.py create mode 100644 pythonQEPest/services/__init__.py diff --git a/pyproject.toml b/pyproject.toml index 9d0b121..5f25736 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pythonQEPest" -version = "1.3.0" +version = "1.4.0" description = "Java QEPest in Python" readme = "README.md" requires-python = ">=3.12,<3.15" diff --git a/pythonQEPest/cli/cli.py b/pythonQEPest/cli/cli.py index 86eca20..80a01ca 100644 --- a/pythonQEPest/cli/cli.py +++ b/pythonQEPest/cli/cli.py @@ -6,69 +6,12 @@ from pathlib import Path from typing import Sequence -from pythonQEPest.core import QEPestMeta from pythonQEPest.dto import QEPestFile -from pythonQEPest.helpers import get_num_of_cols -from pythonQEPest.helpers.get_values_from_line import get_values_from_line +from pythonQEPest.services.QEPestFileService import QEPestFileService logger = logging.getLogger(__name__) -class CLI: - qepest: QEPestMeta | None = None - - def __init__(self, qepest: QEPestMeta, qepest_file: QEPestFile): - self.qepest = qepest - self.qepest_file = qepest_file - - self.error: bool = False - - def read_file_and_compute_params(self): - try: - with open(self.qepest_file.input_file, "r") as file: - lines = file.readlines() - - with open(self.qepest_file.output_file, "w") as wr: - for index, line in enumerate(lines): - if index == 0: - if get_num_of_cols(line) != self.qepest.col_number: - er = f"Error: Line {index} does not have seven elements." - logger.error(er) - - self.error = True - break - - wr.write("Name QEH QEI QEF\n") - else: - if get_num_of_cols(line) == self.qepest.col_number: - d_values = get_values_from_line(line.split("\t")) - self.qepest.get_qex_values(d_values) - splitted_line = line.split("\t")[0] - - # TODO: BUG, AFTER REWORK NEEDS TO BE FIXED !!!!!!!!!!! - wr.write( - f"{splitted_line} {self.qepest.qex.qe_herb} " - + f"{self.qepest.qex.qe_insect} " - + f"{self.qepest.qex.qe_fung}" - + f"{chr(10)}" - ) - else: - er = f"Error: Line {index} does not have seven elements." - logger.error(er) - - self.error = True - if not self.error: - logger.info("Computation completed") - else: - logger.warning("Finished with errors") - - except FileNotFoundError as e: - self.error = True - - logger.error("Error: can't find : %s", self.qepest_file.input_file) - logger.exception(e) - - def _resolve_package_version() -> str: try: return version("pythonQEPest") @@ -108,10 +51,17 @@ def build_parser() -> argparse.ArgumentParser: parser.add_argument( "-o", "--output", - default="data.txt.out", + default="data.out.txt", help="Path to output tab-separated file with " + "QEPest descriptors (default: data.txt.out).", ) + + parser.add_argument( + "-f", + "--format", + default="txt", + help="Format to output file with " + "QEPest (json, txt).", + ) return parser @@ -126,10 +76,16 @@ def main(argv: Sequence[str] | None = None) -> int: load_dotenv() init_logger() - cli = CLI( + logger.debug("CLI initiated") + logger.info(f"CLI args: {argv}") + + service = QEPestFileService( qepest=QEPest(), - qepest_file=QEPestFile(input_file=args.input, output_file=args.output), + qepest_file=QEPestFile( + input_file=args.input, output_file=args.output, format=args.format + ), ) - cli.read_file_and_compute_params() - return 0 if not cli.error else 1 + service.read_file_and_compute_params() + + return 0 if not service.error else 1 diff --git a/pythonQEPest/config/__init__.py b/pythonQEPest/config/__init__.py index 0d59601..1f050c1 100644 --- a/pythonQEPest/config/__init__.py +++ b/pythonQEPest/config/__init__.py @@ -1,4 +1,4 @@ -from .qepest import qepest_default +from .qepest_default import qepest_default from .normalise import normalise_default __all__ = [ diff --git a/pythonQEPest/config/qepest.py b/pythonQEPest/config/qepest_default.py similarity index 100% rename from pythonQEPest/config/qepest.py rename to pythonQEPest/config/qepest_default.py diff --git a/pythonQEPest/core/qepest.py b/pythonQEPest/core/qepest.py index 09fa569..f0716ef 100644 --- a/pythonQEPest/core/qepest.py +++ b/pythonQEPest/core/qepest.py @@ -1,6 +1,6 @@ import logging import math -from typing import Optional +from typing import Optional, List from pydantic import create_model, BaseModel @@ -19,12 +19,32 @@ class QEPest(QEPestMeta): def __init__(self, *args, **kwargs): + self.coefficients_names = None + self.names = None + + logger.debug("QEPest initialisation") + + logger.info(f"QEPest args: {args}") + logger.info(f"QEPest kwargs: {kwargs}") + super().__init__(*args, **kwargs) + logger.debug("QEPest initialisation successful") + def _log_compute_df(self, func, index, lst, data_lst) -> float: df_result = compute_df(lst[index], *data_lst[index]) return math.log(func(df_result, index)) + def get_names(self) -> List[str]: + self.names = [ + n.split("_")[1] for n in dir(self) if n.startswith("coefficient_") + ] + return self.names + + def get_coefficients_names(self) -> List[str]: + self.coefficients_names = [f"coefficient_{name}" for name in self.names] + return self.coefficients_names + def compute_params(self, data_input: QEPestInput) -> QEPestOutput: self.get_qex_values( get_values_from_line(list(data_input.model_dump().values())) @@ -32,10 +52,11 @@ def compute_params(self, data_input: QEPestInput) -> QEPestOutput: return QEPestOutput(data=self.qex, name=data_input.name) def get_qex_values(self, d) -> BaseModel: - names = [n.split("_")[1] for n in dir(self) if n.startswith("coefficient_")] + names = self.get_names() # Coefficients names = ("coefficients_fung, coefficient_herb...) - coefficients_names = [n for n in dir(self) if n.startswith("coefficient_")] + coefficients_names = self.get_coefficients_names() + if len(coefficients_names) == 0: raise ValueError( "No coefficient_ keys, needs to call " @@ -75,24 +96,31 @@ def get_qex_values(self, d) -> BaseModel: return self.qex - # TODO: Coefficients must be in other class + # TODO: Coefficients must be in other class. + # TODO: Ability to provide whatever we want is a good thingy def initialize_coefficients(self, coefficients: Optional = None) -> None: + logger.debug("QEPest coefficients initialisation") coefficients = super().initialize_coefficients() + logger.debug("QEPest coefficients initialisation successful") for category, data in coefficients.items(): setattr(self, f"coefficient_{category}", data) + logger.info(f"QEPest coefficients initialisation with {coefficients.items()}") + # TODO: Same with Normalisers def initialize_normalisers(self, normalisers: Optional = None) -> None: + logger.debug("QEPest normalisers initialisation") normalisers = super().initialize_normalisers() + logger.debug("QEPest normalisers initialisation successful") + for category, data in normalisers.items(): setattr(self, f"normaliser_{category}", Normaliser(data)) + logger.info(f"QEPest normalisers initialisation with {normalisers.items()}") + if __name__ == "__main__": qepest = QEPest() - qepest.initialize_coefficients() - qepest.initialize_normalisers() qepest.get_qex_values(1) - print(1) diff --git a/pythonQEPest/core/qepest_meta.py b/pythonQEPest/core/qepest_meta.py index 0151790..a808daf 100644 --- a/pythonQEPest/core/qepest_meta.py +++ b/pythonQEPest/core/qepest_meta.py @@ -1,16 +1,14 @@ import os from abc import abstractmethod, ABC -from pythonQEPest.dto import QEPestInput, QEPestOutput, QEPestData +from pydantic import BaseModel + +from pythonQEPest.dto import QEPestInput, QEPestOutput class QEPestMeta(ABC): def __init__(self, *args, **kwargs): - self.qex: QEPestData | None = None - - self.herb: list[float] = [] - self.insect: list[float] = [] - self.fung: list[float] = [] + self.qex: BaseModel | None = None self.col_number: int = 7 self.dir: str = os.getcwd() @@ -29,7 +27,7 @@ def get_qex_values(self, d) -> None: @abstractmethod def initialize_coefficients(self, coefficients=None) -> None: if coefficients is None: - from pythonQEPest.config.qepest import qepest_default + from pythonQEPest.config.qepest_default import qepest_default coefficients = qepest_default return coefficients diff --git a/pythonQEPest/dto/QEPestFile.py b/pythonQEPest/dto/QEPestFile.py index d68d76b..5ccf5c7 100644 --- a/pythonQEPest/dto/QEPestFile.py +++ b/pythonQEPest/dto/QEPestFile.py @@ -1,16 +1,33 @@ +from enum import Enum from typing import Optional -from pydantic import BaseModel +from pydantic import BaseModel, model_validator + + +class QEPestFormat(Enum): + TXT = "txt" + JSON = "json" class QEPestFile(BaseModel): input_file: Optional[str] = None output_file: Optional[str] = None + format: Optional[QEPestFormat] = QEPestFormat.TXT + + model_config = {"arbitrary_types_allowed": True} - def __init__(self, **data): - super().__init__(**data) + @model_validator(mode="after") + def set_defaults(self): if self.input_file is None: self.input_file = "data.txt" - if self.output_file is None: - self.output_file = f"{self.input_file}.out" + if self.format is None: + self.format = QEPestFormat.TXT + + formatted_output = self.input_file.replace(".txt", "").replace(".json", "") + if self.format == QEPestFormat.JSON: + self.output_file = f"{formatted_output}.out.json" + else: + self.output_file = f"{formatted_output}.out.txt" + + return self diff --git a/pythonQEPest/dto/__init__.py b/pythonQEPest/dto/__init__.py index aa218a7..3db3c11 100644 --- a/pythonQEPest/dto/__init__.py +++ b/pythonQEPest/dto/__init__.py @@ -1,11 +1,6 @@ from pythonQEPest.dto.QEPestData import QEPestData from pythonQEPest.dto.QEPestInput import QEPestInput -from pythonQEPest.dto.QEPestFile import QEPestFile +from pythonQEPest.dto.QEPestFile import QEPestFile, QEPestFormat from pythonQEPest.dto.QEPestOutput import QEPestOutput -__all__ = [ - "QEPestOutput", - "QEPestData", - "QEPestInput", - "QEPestFile", -] +__all__ = ["QEPestOutput", "QEPestData", "QEPestInput", "QEPestFile", "QEPestFormat"] diff --git a/pythonQEPest/gui/gui.py b/pythonQEPest/gui/gui.py index 9996522..d172cfb 100644 --- a/pythonQEPest/gui/gui.py +++ b/pythonQEPest/gui/gui.py @@ -20,6 +20,7 @@ from pythonQEPest.gui.gui_meta import QEPestMeta +# TODO: Outdated. Must be dynamic class GUI(QEPestMeta): def treeview_sort_column(self, treeview, col, reverse): treeview_lst = [(treeview.set(k, col), k) for k in treeview.get_children("")] diff --git a/pythonQEPest/logger.py b/pythonQEPest/logger.py index 5eaed76..575b247 100644 --- a/pythonQEPest/logger.py +++ b/pythonQEPest/logger.py @@ -53,3 +53,6 @@ def init_logger() -> None: root_logger.setLevel(level) root_logger.addHandler(file_handler) root_logger.addHandler(console_handler) + + root_logger.info("Logger started Successfully") + root_logger.info(f"Level: {level}") diff --git a/pythonQEPest/services/QEPestFileService.py b/pythonQEPest/services/QEPestFileService.py new file mode 100644 index 0000000..2796b2e --- /dev/null +++ b/pythonQEPest/services/QEPestFileService.py @@ -0,0 +1,66 @@ +import logging +import json + +from pythonQEPest.core import QEPestMeta +from pythonQEPest.dto import QEPestFile +from pythonQEPest.dto.QEPestFile import QEPestFormat +from pythonQEPest.helpers.get_values_from_line import get_values_from_line +from pythonQEPest.helpers.get_num_of_cols import get_num_of_cols + +logger = logging.getLogger(__name__) + + +class QEPestFileService: + + def __init__(self, qepest: QEPestMeta, qepest_file: QEPestFile): + self.qepest = qepest + self.qepest_file = qepest_file + self.error = False + + def _write_txt_line(self, line: str, file) -> None: + splitted_line = line.split("\t")[0] + qex_headers = " ".join(self.qepest.qex.model_dump().keys()) + qex_values = " ".join(str(x) for x in self.qepest.qex.model_dump().values()) + + file.write(f"Name {qex_headers.upper()}\n") + file.write(f"{splitted_line} {qex_values}\n") + + def _write_json_line(self, line: str, file) -> None: + splitted_line = line.split("\t")[0] + data = {"name": splitted_line, **self.qepest.qex.model_dump()} + file.write(json.dumps(data) + "\n") + + def read_file_and_compute_params(self) -> None: + try: + with open(self.qepest_file.input_file, "r") as f: + lines = f.readlines() + + with open(self.qepest_file.output_file, "w") as wr: + for index, line in enumerate(lines): + if get_num_of_cols(line) != self.qepest.col_number: + logger.error( + f"Error: Line {index} does not have the " + "expected number of columns." + ) + self.error = True + break + + if index == 0: + continue + + d_values = get_values_from_line(line.split("\t")) + self.qepest.get_qex_values(d_values) + + if self.qepest_file.format == QEPestFormat.TXT: + self._write_txt_line(line, wr) + else: + self._write_json_line(line, wr) + + if not self.error: + logger.info("Computation completed") + else: + logger.warning("Finished with errors") + + except FileNotFoundError: + self.error = True + logger.error(f"Error: can't find: {self.qepest_file.input_file}") diff --git a/pythonQEPest/services/__init__.py b/pythonQEPest/services/__init__.py new file mode 100644 index 0000000..7df49da --- /dev/null +++ b/pythonQEPest/services/__init__.py @@ -0,0 +1,3 @@ +from pythonQEPest.services.QEPestFileService import QEPestFileService + +__all__ = ["QEPestFileService"] diff --git a/tests/cli/test_cli_contract.py b/tests/cli/test_cli_contract.py index bdeb3a8..da70cc9 100644 --- a/tests/cli/test_cli_contract.py +++ b/tests/cli/test_cli_contract.py @@ -4,19 +4,25 @@ class TestCLIContract: - def test_cli_help_exits_with_zero(self): + def test_cli_help_exits_with_zero(self, monkeypatch): + monkeypatch.setenv("APP_DEBUG_ENABLE", "false") + with pytest.raises(SystemExit) as exc: main(["--help"]) assert exc.value.code == 0 - def test_cli_version_exits_with_zero(self): + def test_cli_version_exits_with_zero(self, monkeypatch): + monkeypatch.setenv("APP_DEBUG_ENABLE", "false") + with pytest.raises(SystemExit) as exc: main(["--version"]) assert exc.value.code == 0 - def test_cli_runs_with_explicit_input_file(self, tmp_path): + def test_cli_runs_with_explicit_input_file(self, monkeypatch, tmp_path): + monkeypatch.setenv("APP_DEBUG_ENABLE", "false") + data_file = tmp_path / "input.txt" - output_file = tmp_path / "input.txt.out" + output_file = tmp_path / "input.out.txt" data_file.write_text( "Name\tMW\tLogP\tHBA\tHBD\tRB\tarR\n" @@ -30,4 +36,66 @@ def test_cli_runs_with_explicit_input_file(self, tmp_path): assert output_file.exists() output_text = output_file.read_text(encoding="utf-8") - assert output_text == "Name QEH QEI QEF\nmol1 0.8511 0.5339 0.6224\n" + assert output_file.name == "input.out.txt" + assert ( + output_text == "Name QE_FUNG QE_HERB QE_INSECT\nmol1 0.6224 0.8511 0.5339\n" + ) + + def test_cli_runs_with_explicit_input_file_format(self, monkeypatch, tmp_path): + monkeypatch.setenv("APP_DEBUG_ENABLE", "false") + + data_file = tmp_path / "input.txt" + output_file = tmp_path / "input.out.txt" + + data_file.write_text( + "Name\tMW\tLogP\tHBA\tHBD\tRB\tarR\n" + "mol1\t240.2127\t3.2392\t5\t1\t4\t1\n", + encoding="utf-8", + ) + + exit_code = main( + ["--input", str(data_file), "--output", str(output_file), "--format=txt"] + ) + + assert exit_code == 0 + assert output_file.exists() + + output_text = output_file.read_text(encoding="utf-8") + + assert output_file.name == "input.out.txt" + assert isinstance(output_text, str) + + assert ( + output_text == "Name QE_FUNG QE_HERB QE_INSECT\nmol1 0.6224 0.8511 0.5339\n" + ) + + def test_cli_runs_with_input_file_format(self, monkeypatch, tmp_path): + monkeypatch.setenv("APP_DEBUG_ENABLE", "false") + + data_file = tmp_path / "input.txt" + output_file = tmp_path / "input.out.json" + + data_file.write_text( + "Name\tMW\tLogP\tHBA\tHBD\tRB\tarR\n" + "mol1\t240.2127\t3.2392\t5\t1\t4\t1\n", + encoding="utf-8", + ) + + exit_code = main(["--input", str(data_file), "--format=json"]) + + assert exit_code == 0 + assert output_file.exists() + + output_text = output_file.read_text(encoding="utf-8") + + assert output_file.name == "input.out.json" + assert isinstance(output_text, str) + + import json + + assert json.loads(output_text) == { + "name": "mol1", + "qe_fung": 0.6224, + "qe_herb": 0.8511, + "qe_insect": 0.5339, + } diff --git a/tests/config/test_coefficients.py b/tests/config/test_coefficients.py index 8816b23..7f564a3 100644 --- a/tests/config/test_coefficients.py +++ b/tests/config/test_coefficients.py @@ -206,7 +206,7 @@ def test_qepest_coefficients_lst_basic(self): ) def test_qepest_coefficients_lst_w_import(self): - from pythonQEPest.config.qepest import qepest_default + from pythonQEPest.config.qepest_default import qepest_default qep_coeff_lst = QEPestCoefficientList(qepest_default) @@ -333,7 +333,7 @@ def test_qepest_coefficients_lst_w_import(self): ) def test_qepest_default_coefficients(self): - from pythonQEPest.config.qepest import qepest_default + from pythonQEPest.config.qepest_default import qepest_default assert "fung" in qepest_default assert "herb" in qepest_default diff --git a/tests/dto/test_qepest_file.py b/tests/dto/test_qepest_file.py index fbb3f85..67a4bc6 100644 --- a/tests/dto/test_qepest_file.py +++ b/tests/dto/test_qepest_file.py @@ -22,7 +22,7 @@ def test_qepest_file_standard(self): ) assert qepest_output_file.input_file == "input_example.txt" - assert qepest_output_file.output_file == "output_example.txt" + assert qepest_output_file.output_file == "input_example.out.txt" def test_qepest_file_with_no_output_file(self): QEPestFile = self._import_qepest_file() @@ -30,7 +30,7 @@ def test_qepest_file_with_no_output_file(self): qepest_output_file = QEPestFile(input_file="input1.txt") assert qepest_output_file.input_file == "input1.txt" - assert qepest_output_file.output_file == "input1.txt.out" + assert qepest_output_file.output_file == "input1.out.txt" def test_qepest_file_with_no_input_file(self): QEPestFile = self._import_qepest_file() @@ -38,4 +38,4 @@ def test_qepest_file_with_no_input_file(self): qepest_output_file = QEPestFile() assert qepest_output_file.input_file == "data.txt" - assert qepest_output_file.output_file == "data.txt.out" + assert qepest_output_file.output_file == "data.out.txt" From 0d901b89be9f23fcb5a6634ad3812e749feca339 Mon Sep 17 00:00:00 2001 From: Lina Date: Sun, 1 Mar 2026 16:18:29 +0100 Subject: [PATCH 7/7] update[poetry.lock]: new versions --- poetry.lock | 444 +++++++++++++++++++++++++---------------------- requirements.txt | Bin 2740 -> 2658 bytes 2 files changed, 240 insertions(+), 204 deletions(-) diff --git a/poetry.lock b/poetry.lock index 5910abe..90ab610 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.3.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. [[package]] name = "altgraph" @@ -88,14 +88,14 @@ redis = ["redis (>=2.10.5)"] [[package]] name = "certifi" -version = "2026.1.4" +version = "2026.2.25" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" groups = ["dev"] files = [ - {file = "certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c"}, - {file = "certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120"}, + {file = "certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa"}, + {file = "certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7"}, ] [[package]] @@ -362,104 +362,118 @@ files = [ [[package]] name = "coverage" -version = "7.13.3" +version = "7.13.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "coverage-7.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0b4f345f7265cdbdb5ec2521ffff15fa49de6d6c39abf89fc7ad68aa9e3a55f0"}, - {file = "coverage-7.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:96c3be8bae9d0333e403cc1a8eb078a7f928b5650bae94a18fb4820cc993fb9b"}, - {file = "coverage-7.13.3-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d6f4a21328ea49d38565b55599e1c02834e76583a6953e5586d65cb1efebd8f8"}, - {file = "coverage-7.13.3-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fc970575799a9d17d5c3fafd83a0f6ccf5d5117cdc9ad6fbd791e9ead82418b0"}, - {file = "coverage-7.13.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:87ff33b652b3556b05e204ae20793d1f872161b0fa5ec8a9ac76f8430e152ed6"}, - {file = "coverage-7.13.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7df8759ee57b9f3f7b66799b7660c282f4375bef620ade1686d6a7b03699e75f"}, - {file = "coverage-7.13.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f45c9bcb16bee25a798ccba8a2f6a1251b19de6a0d617bb365d7d2f386c4e20e"}, - {file = "coverage-7.13.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:318b2e4753cbf611061e01b6cc81477e1cdfeb69c36c4a14e6595e674caadb56"}, - {file = "coverage-7.13.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:24db3959de8ee394eeeca89ccb8ba25305c2da9a668dd44173394cbd5aa0777f"}, - {file = "coverage-7.13.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:be14d0622125edef21b3a4d8cd2d138c4872bf6e38adc90fd92385e3312f406a"}, - {file = "coverage-7.13.3-cp310-cp310-win32.whl", hash = "sha256:53be4aab8ddef18beb6188f3a3fdbf4d1af2277d098d4e618be3a8e6c88e74be"}, - {file = "coverage-7.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:bfeee64ad8b4aae3233abb77eb6b52b51b05fa89da9645518671b9939a78732b"}, - {file = "coverage-7.13.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5907605ee20e126eeee2abe14aae137043c2c8af2fa9b38d2ab3b7a6b8137f73"}, - {file = "coverage-7.13.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a88705500988c8acad8b8fd86c2a933d3aa96bec1ddc4bc5cb256360db7bbd00"}, - {file = "coverage-7.13.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bbb5aa9016c4c29e3432e087aa29ebee3f8fda089cfbfb4e6d64bd292dcd1c2"}, - {file = "coverage-7.13.3-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0c2be202a83dde768937a61cdc5d06bf9fb204048ca199d93479488e6247656c"}, - {file = "coverage-7.13.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f45e32ef383ce56e0ca099b2e02fcdf7950be4b1b56afaab27b4ad790befe5b"}, - {file = "coverage-7.13.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6ed2e787249b922a93cd95c671cc9f4c9797a106e81b455c83a9ddb9d34590c0"}, - {file = "coverage-7.13.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:05dd25b21afffe545e808265897c35f32d3e4437663923e0d256d9ab5031fb14"}, - {file = "coverage-7.13.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:46d29926349b5c4f1ea4fca95e8c892835515f3600995a383fa9a923b5739ea4"}, - {file = "coverage-7.13.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:fae6a21537519c2af00245e834e5bf2884699cc7c1055738fd0f9dc37a3644ad"}, - {file = "coverage-7.13.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c672d4e2f0575a4ca2bf2aa0c5ced5188220ab806c1bb6d7179f70a11a017222"}, - {file = "coverage-7.13.3-cp311-cp311-win32.whl", hash = "sha256:fcda51c918c7a13ad93b5f89a58d56e3a072c9e0ba5c231b0ed81404bf2648fb"}, - {file = "coverage-7.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:d1a049b5c51b3b679928dd35e47c4a2235e0b6128b479a7596d0ef5b42fa6301"}, - {file = "coverage-7.13.3-cp311-cp311-win_arm64.whl", hash = "sha256:79f2670c7e772f4917895c3d89aad59e01f3dbe68a4ed2d0373b431fad1dcfba"}, - {file = "coverage-7.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ed48b4170caa2c4420e0cd27dc977caaffc7eecc317355751df8373dddcef595"}, - {file = "coverage-7.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8f2adf4bcffbbec41f366f2e6dffb9d24e8172d16e91da5799c9b7ed6b5716e6"}, - {file = "coverage-7.13.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:01119735c690786b6966a1e9f098da4cd7ca9174c4cfe076d04e653105488395"}, - {file = "coverage-7.13.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8bb09e83c603f152d855f666d70a71765ca8e67332e5829e62cb9466c176af23"}, - {file = "coverage-7.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b607a40cba795cfac6d130220d25962931ce101f2f478a29822b19755377fb34"}, - {file = "coverage-7.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:44f14a62f5da2e9aedf9080e01d2cda61df39197d48e323538ec037336d68da8"}, - {file = "coverage-7.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:debf29e0b157769843dff0981cc76f79e0ed04e36bb773c6cac5f6029054bd8a"}, - {file = "coverage-7.13.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:824bb95cd71604031ae9a48edb91fd6effde669522f960375668ed21b36e3ec4"}, - {file = "coverage-7.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8f1010029a5b52dc427c8e2a8dbddb2303ddd180b806687d1acd1bb1d06649e7"}, - {file = "coverage-7.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cd5dee4fd7659d8306ffa79eeaaafd91fa30a302dac3af723b9b469e549247e0"}, - {file = "coverage-7.13.3-cp312-cp312-win32.whl", hash = "sha256:f7f153d0184d45f3873b3ad3ad22694fd73aadcb8cdbc4337ab4b41ea6b4dff1"}, - {file = "coverage-7.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:03a6e5e1e50819d6d7436f5bc40c92ded7e484e400716886ac921e35c133149d"}, - {file = "coverage-7.13.3-cp312-cp312-win_arm64.whl", hash = "sha256:51c4c42c0e7d09a822b08b6cf79b3c4db8333fffde7450da946719ba0d45730f"}, - {file = "coverage-7.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:853c3d3c79ff0db65797aad79dee6be020efd218ac4510f15a205f1e8d13ce25"}, - {file = "coverage-7.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f75695e157c83d374f88dcc646a60cb94173304a9258b2e74ba5a66b7614a51a"}, - {file = "coverage-7.13.3-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2d098709621d0819039f3f1e471ee554f55a0b2ac0d816883c765b14129b5627"}, - {file = "coverage-7.13.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:16d23d6579cf80a474ad160ca14d8b319abaa6db62759d6eef53b2fc979b58c8"}, - {file = "coverage-7.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:00d34b29a59d2076e6f318b30a00a69bf63687e30cd882984ed444e753990cc1"}, - {file = "coverage-7.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ab6d72bffac9deb6e6cb0f61042e748de3f9f8e98afb0375a8e64b0b6e11746b"}, - {file = "coverage-7.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e129328ad1258e49cae0123a3b5fcb93d6c2fa90d540f0b4c7cdcdc019aaa3dc"}, - {file = "coverage-7.13.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2213a8d88ed35459bda71597599d4eec7c2ebad201c88f0bfc2c26fd9b0dd2ea"}, - {file = "coverage-7.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:00dd3f02de6d5f5c9c3d95e3e036c3c2e2a669f8bf2d3ceb92505c4ce7838f67"}, - {file = "coverage-7.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f9bada7bc660d20b23d7d312ebe29e927b655cf414dadcdb6335a2075695bd86"}, - {file = "coverage-7.13.3-cp313-cp313-win32.whl", hash = "sha256:75b3c0300f3fa15809bd62d9ca8b170eb21fcf0100eb4b4154d6dc8b3a5bbd43"}, - {file = "coverage-7.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:a2f7589c6132c44c53f6e705e1a6677e2b7821378c22f7703b2cf5388d0d4587"}, - {file = "coverage-7.13.3-cp313-cp313-win_arm64.whl", hash = "sha256:123ceaf2b9d8c614f01110f908a341e05b1b305d6b2ada98763b9a5a59756051"}, - {file = "coverage-7.13.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:cc7fd0f726795420f3678ac82ff882c7fc33770bd0074463b5aef7293285ace9"}, - {file = "coverage-7.13.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d358dc408edc28730aed5477a69338e444e62fba0b7e9e4a131c505fadad691e"}, - {file = "coverage-7.13.3-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5d67b9ed6f7b5527b209b24b3df9f2e5bf0198c1bbf99c6971b0e2dcb7e2a107"}, - {file = "coverage-7.13.3-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59224bfb2e9b37c1335ae35d00daa3a5b4e0b1a20f530be208fff1ecfa436f43"}, - {file = "coverage-7.13.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae9306b5299e31e31e0d3b908c66bcb6e7e3ddca143dea0266e9ce6c667346d3"}, - {file = "coverage-7.13.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:343aaeb5f8bb7bcd38620fd7bc56e6ee8207847d8c6103a1e7b72322d381ba4a"}, - {file = "coverage-7.13.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b2182129f4c101272ff5f2f18038d7b698db1bf8e7aa9e615cb48440899ad32e"}, - {file = "coverage-7.13.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:94d2ac94bd0cc57c5626f52f8c2fffed1444b5ae8c9fc68320306cc2b255e155"}, - {file = "coverage-7.13.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:65436cde5ecabe26fb2f0bf598962f0a054d3f23ad529361326ac002c61a2a1e"}, - {file = "coverage-7.13.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:db83b77f97129813dbd463a67e5335adc6a6a91db652cc085d60c2d512746f96"}, - {file = "coverage-7.13.3-cp313-cp313t-win32.whl", hash = "sha256:dfb428e41377e6b9ba1b0a32df6db5409cb089a0ed1d0a672dc4953ec110d84f"}, - {file = "coverage-7.13.3-cp313-cp313t-win_amd64.whl", hash = "sha256:5badd7e596e6b0c89aa8ec6d37f4473e4357f982ce57f9a2942b0221cd9cf60c"}, - {file = "coverage-7.13.3-cp313-cp313t-win_arm64.whl", hash = "sha256:989aa158c0eb19d83c76c26f4ba00dbb272485c56e452010a3450bdbc9daafd9"}, - {file = "coverage-7.13.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c6f6169bbdbdb85aab8ac0392d776948907267fcc91deeacf6f9d55f7a83ae3b"}, - {file = "coverage-7.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2f5e731627a3d5ef11a2a35aa0c6f7c435867c7ccbc391268eb4f2ca5dbdcc10"}, - {file = "coverage-7.13.3-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9db3a3285d91c0b70fab9f39f0a4aa37d375873677efe4e71e58d8321e8c5d39"}, - {file = "coverage-7.13.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:06e49c5897cb12e3f7ecdc111d44e97c4f6d0557b81a7a0204ed70a8b038f86f"}, - {file = "coverage-7.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb25061a66802df9fc13a9ba1967d25faa4dae0418db469264fd9860a921dde4"}, - {file = "coverage-7.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:99fee45adbb1caeb914da16f70e557fb7ff6ddc9e4b14de665bd41af631367ef"}, - {file = "coverage-7.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:318002f1fd819bdc1651c619268aa5bc853c35fa5cc6d1e8c96bd9cd6c828b75"}, - {file = "coverage-7.13.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:71295f2d1d170b9977dc386d46a7a1b7cbb30e5405492529b4c930113a33f895"}, - {file = "coverage-7.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5b1ad2e0dc672625c44bc4fe34514602a9fd8b10d52ddc414dc585f74453516c"}, - {file = "coverage-7.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b2beb64c145593a50d90db5c7178f55daeae129123b0d265bdb3cbec83e5194a"}, - {file = "coverage-7.13.3-cp314-cp314-win32.whl", hash = "sha256:3d1aed4f4e837a832df2f3b4f68a690eede0de4560a2dbc214ea0bc55aabcdb4"}, - {file = "coverage-7.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9f9efbbaf79f935d5fbe3ad814825cbce4f6cdb3054384cb49f0c0f496125fa0"}, - {file = "coverage-7.13.3-cp314-cp314-win_arm64.whl", hash = "sha256:31b6e889c53d4e6687ca63706148049494aace140cffece1c4dc6acadb70a7b3"}, - {file = "coverage-7.13.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c5e9787cec750793a19a28df7edd85ac4e49d3fb91721afcdc3b86f6c08d9aa8"}, - {file = "coverage-7.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e5b86db331c682fd0e4be7098e6acee5e8a293f824d41487c667a93705d415ca"}, - {file = "coverage-7.13.3-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:edc7754932682d52cf6e7a71806e529ecd5ce660e630e8bd1d37109a2e5f63ba"}, - {file = "coverage-7.13.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d3a16d6398666510a6886f67f43d9537bfd0e13aca299688a19daa84f543122f"}, - {file = "coverage-7.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:303d38b19626c1981e1bb067a9928236d88eb0e4479b18a74812f05a82071508"}, - {file = "coverage-7.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:284e06eadfe15ddfee2f4ee56631f164ef897a7d7d5a15bca5f0bb88889fc5ba"}, - {file = "coverage-7.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d401f0864a1d3198422816878e4e84ca89ec1c1bf166ecc0ae01380a39b888cd"}, - {file = "coverage-7.13.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3f379b02c18a64de78c4ccdddf1c81c2c5ae1956c72dacb9133d7dd7809794ab"}, - {file = "coverage-7.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:7a482f2da9086971efb12daca1d6547007ede3674ea06e16d7663414445c683e"}, - {file = "coverage-7.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:562136b0d401992118d9b49fbee5454e16f95f85b120a4226a04d816e33fe024"}, - {file = "coverage-7.13.3-cp314-cp314t-win32.whl", hash = "sha256:ca46e5c3be3b195098dd88711890b8011a9fa4feca942292bb84714ce5eab5d3"}, - {file = "coverage-7.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:06d316dbb3d9fd44cca05b2dbcfbef22948493d63a1f28e828d43e6cc505fed8"}, - {file = "coverage-7.13.3-cp314-cp314t-win_arm64.whl", hash = "sha256:299d66e9218193f9dc6e4880629ed7c4cd23486005166247c283fb98531656c3"}, - {file = "coverage-7.13.3-py3-none-any.whl", hash = "sha256:90a8af9dba6429b2573199622d72e0ebf024d6276f16abce394ad4d181bb0910"}, - {file = "coverage-7.13.3.tar.gz", hash = "sha256:f7f6182d3dfb8802c1747eacbfe611b669455b69b7c037484bb1efbbb56711ac"}, + {file = "coverage-7.13.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fc31c787a84f8cd6027eba44010517020e0d18487064cd3d8968941856d1415"}, + {file = "coverage-7.13.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a32ebc02a1805adf637fc8dec324b5cdacd2e493515424f70ee33799573d661b"}, + {file = "coverage-7.13.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e24f9156097ff9dc286f2f913df3a7f63c0e333dcafa3c196f2c18b4175ca09a"}, + {file = "coverage-7.13.4-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8041b6c5bfdc03257666e9881d33b1abc88daccaf73f7b6340fb7946655cd10f"}, + {file = "coverage-7.13.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a09cfa6a5862bc2fc6ca7c3def5b2926194a56b8ab78ffcf617d28911123012"}, + {file = "coverage-7.13.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:296f8b0af861d3970c2a4d8c91d48eb4dd4771bcef9baedec6a9b515d7de3def"}, + {file = "coverage-7.13.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e101609bcbbfb04605ea1027b10dc3735c094d12d40826a60f897b98b1c30256"}, + {file = "coverage-7.13.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aa3feb8db2e87ff5e6d00d7e1480ae241876286691265657b500886c98f38bda"}, + {file = "coverage-7.13.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4fc7fa81bbaf5a02801b65346c8b3e657f1d93763e58c0abdf7c992addd81a92"}, + {file = "coverage-7.13.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:33901f604424145c6e9c2398684b92e176c0b12df77d52db81c20abd48c3794c"}, + {file = "coverage-7.13.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:bb28c0f2cf2782508a40cec377935829d5fcc3ad9a3681375af4e84eb34b6b58"}, + {file = "coverage-7.13.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9d107aff57a83222ddbd8d9ee705ede2af2cc926608b57abed8ef96b50b7e8f9"}, + {file = "coverage-7.13.4-cp310-cp310-win32.whl", hash = "sha256:a6f94a7d00eb18f1b6d403c91a88fd58cfc92d4b16080dfdb774afc8294469bf"}, + {file = "coverage-7.13.4-cp310-cp310-win_amd64.whl", hash = "sha256:2cb0f1e000ebc419632bbe04366a8990b6e32c4e0b51543a6484ffe15eaeda95"}, + {file = "coverage-7.13.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d490ba50c3f35dd7c17953c68f3270e7ccd1c6642e2d2afe2d8e720b98f5a053"}, + {file = "coverage-7.13.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:19bc3c88078789f8ef36acb014d7241961dbf883fd2533d18cb1e7a5b4e28b11"}, + {file = "coverage-7.13.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3998e5a32e62fdf410c0dbd3115df86297995d6e3429af80b8798aad894ca7aa"}, + {file = "coverage-7.13.4-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8e264226ec98e01a8e1054314af91ee6cde0eacac4f465cc93b03dbe0bce2fd7"}, + {file = "coverage-7.13.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a3aa4e7b9e416774b21797365b358a6e827ffadaaca81b69ee02946852449f00"}, + {file = "coverage-7.13.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:71ca20079dd8f27fcf808817e281e90220475cd75115162218d0e27549f95fef"}, + {file = "coverage-7.13.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e2f25215f1a359ab17320b47bcdaca3e6e6356652e8256f2441e4ef972052903"}, + {file = "coverage-7.13.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d65b2d373032411e86960604dc4edac91fdfb5dca539461cf2cbe78327d1e64f"}, + {file = "coverage-7.13.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94eb63f9b363180aff17de3e7c8760c3ba94664ea2695c52f10111244d16a299"}, + {file = "coverage-7.13.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e856bf6616714c3a9fbc270ab54103f4e685ba236fa98c054e8f87f266c93505"}, + {file = "coverage-7.13.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:65dfcbe305c3dfe658492df2d85259e0d79ead4177f9ae724b6fb245198f55d6"}, + {file = "coverage-7.13.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b507778ae8a4c915436ed5c2e05b4a6cecfa70f734e19c22a005152a11c7b6a9"}, + {file = "coverage-7.13.4-cp311-cp311-win32.whl", hash = "sha256:784fc3cf8be001197b652d51d3fd259b1e2262888693a4636e18879f613a62a9"}, + {file = "coverage-7.13.4-cp311-cp311-win_amd64.whl", hash = "sha256:2421d591f8ca05b308cf0092807308b2facbefe54af7c02ac22548b88b95c98f"}, + {file = "coverage-7.13.4-cp311-cp311-win_arm64.whl", hash = "sha256:79e73a76b854d9c6088fe5d8b2ebe745f8681c55f7397c3c0a016192d681045f"}, + {file = "coverage-7.13.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:02231499b08dabbe2b96612993e5fc34217cdae907a51b906ac7fca8027a4459"}, + {file = "coverage-7.13.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40aa8808140e55dc022b15d8aa7f651b6b3d68b365ea0398f1441e0b04d859c3"}, + {file = "coverage-7.13.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5b856a8ccf749480024ff3bd7310adaef57bf31fd17e1bfc404b7940b6986634"}, + {file = "coverage-7.13.4-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c048ea43875fbf8b45d476ad79f179809c590ec7b79e2035c662e7afa3192e3"}, + {file = "coverage-7.13.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b7b38448866e83176e28086674fe7368ab8590e4610fb662b44e345b86d63ffa"}, + {file = "coverage-7.13.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:de6defc1c9badbf8b9e67ae90fd00519186d6ab64e5cc5f3d21359c2a9b2c1d3"}, + {file = "coverage-7.13.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7eda778067ad7ffccd23ecffce537dface96212576a07924cbf0d8799d2ded5a"}, + {file = "coverage-7.13.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e87f6c587c3f34356c3759f0420693e35e7eb0e2e41e4c011cb6ec6ecbbf1db7"}, + {file = "coverage-7.13.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8248977c2e33aecb2ced42fef99f2d319e9904a36e55a8a68b69207fb7e43edc"}, + {file = "coverage-7.13.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:25381386e80ae727608e662474db537d4df1ecd42379b5ba33c84633a2b36d47"}, + {file = "coverage-7.13.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ee756f00726693e5ba94d6df2bdfd64d4852d23b09bb0bc700e3b30e6f333985"}, + {file = "coverage-7.13.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fdfc1e28e7c7cdce44985b3043bc13bbd9c747520f94a4d7164af8260b3d91f0"}, + {file = "coverage-7.13.4-cp312-cp312-win32.whl", hash = "sha256:01d4cbc3c283a17fc1e42d614a119f7f438eabb593391283adca8dc86eff1246"}, + {file = "coverage-7.13.4-cp312-cp312-win_amd64.whl", hash = "sha256:9401ebc7ef522f01d01d45532c68c5ac40fb27113019b6b7d8b208f6e9baa126"}, + {file = "coverage-7.13.4-cp312-cp312-win_arm64.whl", hash = "sha256:b1ec7b6b6e93255f952e27ab58fbc68dcc468844b16ecbee881aeb29b6ab4d8d"}, + {file = "coverage-7.13.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b66a2da594b6068b48b2692f043f35d4d3693fb639d5ea8b39533c2ad9ac3ab9"}, + {file = "coverage-7.13.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3599eb3992d814d23b35c536c28df1a882caa950f8f507cef23d1cbf334995ac"}, + {file = "coverage-7.13.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:93550784d9281e374fb5a12bf1324cc8a963fd63b2d2f223503ef0fd4aa339ea"}, + {file = "coverage-7.13.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b720ce6a88a2755f7c697c23268ddc47a571b88052e6b155224347389fdf6a3b"}, + {file = "coverage-7.13.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7b322db1284a2ed3aa28ffd8ebe3db91c929b7a333c0820abec3d838ef5b3525"}, + {file = "coverage-7.13.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f4594c67d8a7c89cf922d9df0438c7c7bb022ad506eddb0fdb2863359ff78242"}, + {file = "coverage-7.13.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:53d133df809c743eb8bce33b24bcababb371f4441340578cd406e084d94a6148"}, + {file = "coverage-7.13.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76451d1978b95ba6507a039090ba076105c87cc76fc3efd5d35d72093964d49a"}, + {file = "coverage-7.13.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7f57b33491e281e962021de110b451ab8a24182589be17e12a22c79047935e23"}, + {file = "coverage-7.13.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1731dc33dc276dafc410a885cbf5992f1ff171393e48a21453b78727d090de80"}, + {file = "coverage-7.13.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:bd60d4fe2f6fa7dff9223ca1bbc9f05d2b6697bc5961072e5d3b952d46e1b1ea"}, + {file = "coverage-7.13.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9181a3ccead280b828fae232df12b16652702b49d41e99d657f46cc7b1f6ec7a"}, + {file = "coverage-7.13.4-cp313-cp313-win32.whl", hash = "sha256:f53d492307962561ac7de4cd1de3e363589b000ab69617c6156a16ba7237998d"}, + {file = "coverage-7.13.4-cp313-cp313-win_amd64.whl", hash = "sha256:e6f70dec1cc557e52df5306d051ef56003f74d56e9c4dd7ddb07e07ef32a84dd"}, + {file = "coverage-7.13.4-cp313-cp313-win_arm64.whl", hash = "sha256:fb07dc5da7e849e2ad31a5d74e9bece81f30ecf5a42909d0a695f8bd1874d6af"}, + {file = "coverage-7.13.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:40d74da8e6c4b9ac18b15331c4b5ebc35a17069410cad462ad4f40dcd2d50c0d"}, + {file = "coverage-7.13.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4223b4230a376138939a9173f1bdd6521994f2aff8047fae100d6d94d50c5a12"}, + {file = "coverage-7.13.4-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1d4be36a5114c499f9f1f9195e95ebf979460dbe2d88e6816ea202010ba1c34b"}, + {file = "coverage-7.13.4-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:200dea7d1e8095cc6e98cdabe3fd1d21ab17d3cee6dab00cadbb2fe35d9c15b9"}, + {file = "coverage-7.13.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8eb931ee8e6d8243e253e5ed7336deea6904369d2fd8ae6e43f68abbf167092"}, + {file = "coverage-7.13.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:75eab1ebe4f2f64d9509b984f9314d4aa788540368218b858dad56dc8f3e5eb9"}, + {file = "coverage-7.13.4-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c35eb28c1d085eb7d8c9b3296567a1bebe03ce72962e932431b9a61f28facf26"}, + {file = "coverage-7.13.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb88b316ec33760714a4720feb2816a3a59180fd58c1985012054fa7aebee4c2"}, + {file = "coverage-7.13.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7d41eead3cc673cbd38a4417deb7fd0b4ca26954ff7dc6078e33f6ff97bed940"}, + {file = "coverage-7.13.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:fb26a934946a6afe0e326aebe0730cdff393a8bc0bbb65a2f41e30feddca399c"}, + {file = "coverage-7.13.4-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:dae88bc0fc77edaa65c14be099bd57ee140cf507e6bfdeea7938457ab387efb0"}, + {file = "coverage-7.13.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:845f352911777a8e722bfce168958214951e07e47e5d5d9744109fa5fe77f79b"}, + {file = "coverage-7.13.4-cp313-cp313t-win32.whl", hash = "sha256:2fa8d5f8de70688a28240de9e139fa16b153cc3cbb01c5f16d88d6505ebdadf9"}, + {file = "coverage-7.13.4-cp313-cp313t-win_amd64.whl", hash = "sha256:9351229c8c8407645840edcc277f4a2d44814d1bc34a2128c11c2a031d45a5dd"}, + {file = "coverage-7.13.4-cp313-cp313t-win_arm64.whl", hash = "sha256:30b8d0512f2dc8c8747557e8fb459d6176a2c9e5731e2b74d311c03b78451997"}, + {file = "coverage-7.13.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:300deaee342f90696ed186e3a00c71b5b3d27bffe9e827677954f4ee56969601"}, + {file = "coverage-7.13.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:29e3220258d682b6226a9b0925bc563ed9a1ebcff3cad30f043eceea7eaf2689"}, + {file = "coverage-7.13.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:391ee8f19bef69210978363ca930f7328081c6a0152f1166c91f0b5fdd2a773c"}, + {file = "coverage-7.13.4-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0dd7ab8278f0d58a0128ba2fca25824321f05d059c1441800e934ff2efa52129"}, + {file = "coverage-7.13.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78cdf0d578b15148b009ccf18c686aa4f719d887e76e6b40c38ffb61d264a552"}, + {file = "coverage-7.13.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:48685fee12c2eb3b27c62f2658e7ea21e9c3239cba5a8a242801a0a3f6a8c62a"}, + {file = "coverage-7.13.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4e83efc079eb39480e6346a15a1bcb3e9b04759c5202d157e1dd4303cd619356"}, + {file = "coverage-7.13.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ecae9737b72408d6a950f7e525f30aca12d4bd8dd95e37342e5beb3a2a8c4f71"}, + {file = "coverage-7.13.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ae4578f8528569d3cf303fef2ea569c7f4c4059a38c8667ccef15c6e1f118aa5"}, + {file = "coverage-7.13.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:6fdef321fdfbb30a197efa02d48fcd9981f0d8ad2ae8903ac318adc653f5df98"}, + {file = "coverage-7.13.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b0f6ccf3dbe577170bebfce1318707d0e8c3650003cb4b3a9dd744575daa8b5"}, + {file = "coverage-7.13.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75fcd519f2a5765db3f0e391eb3b7d150cce1a771bf4c9f861aeab86c767a3c0"}, + {file = "coverage-7.13.4-cp314-cp314-win32.whl", hash = "sha256:8e798c266c378da2bd819b0677df41ab46d78065fb2a399558f3f6cae78b2fbb"}, + {file = "coverage-7.13.4-cp314-cp314-win_amd64.whl", hash = "sha256:245e37f664d89861cf2329c9afa2c1fe9e6d4e1a09d872c947e70718aeeac505"}, + {file = "coverage-7.13.4-cp314-cp314-win_arm64.whl", hash = "sha256:ad27098a189e5838900ce4c2a99f2fe42a0bf0c2093c17c69b45a71579e8d4a2"}, + {file = "coverage-7.13.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:85480adfb35ffc32d40918aad81b89c69c9cc5661a9b8a81476d3e645321a056"}, + {file = "coverage-7.13.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:79be69cf7f3bf9b0deeeb062eab7ac7f36cd4cc4c4dd694bd28921ba4d8596cc"}, + {file = "coverage-7.13.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:caa421e2684e382c5d8973ac55e4f36bed6821a9bad5c953494de960c74595c9"}, + {file = "coverage-7.13.4-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:14375934243ee05f56c45393fe2ce81fe5cc503c07cee2bdf1725fb8bef3ffaf"}, + {file = "coverage-7.13.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25a41c3104d08edb094d9db0d905ca54d0cd41c928bb6be3c4c799a54753af55"}, + {file = "coverage-7.13.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f01afcff62bf9a08fb32b2c1d6e924236c0383c02c790732b6537269e466a72"}, + {file = "coverage-7.13.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eb9078108fbf0bcdde37c3f4779303673c2fa1fe8f7956e68d447d0dd426d38a"}, + {file = "coverage-7.13.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e086334e8537ddd17e5f16a344777c1ab8194986ec533711cbe6c41cde841b6"}, + {file = "coverage-7.13.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:725d985c5ab621268b2edb8e50dfe57633dc69bda071abc470fed55a14935fd3"}, + {file = "coverage-7.13.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:3c06f0f1337c667b971ca2f975523347e63ec5e500b9aa5882d91931cd3ef750"}, + {file = "coverage-7.13.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:590c0ed4bf8e85f745e6b805b2e1c457b2e33d5255dd9729743165253bc9ad39"}, + {file = "coverage-7.13.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:eb30bf180de3f632cd043322dad5751390e5385108b2807368997d1a92a509d0"}, + {file = "coverage-7.13.4-cp314-cp314t-win32.whl", hash = "sha256:c4240e7eded42d131a2d2c4dec70374b781b043ddc79a9de4d55ca71f8e98aea"}, + {file = "coverage-7.13.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4c7d3cc01e7350f2f0f6f7036caaf5673fb56b6998889ccfe9e1c1fe75a9c932"}, + {file = "coverage-7.13.4-cp314-cp314t-win_arm64.whl", hash = "sha256:23e3f687cf945070d1c90f85db66d11e3025665d8dafa831301a0e0038f3db9b"}, + {file = "coverage-7.13.4-py3-none-any.whl", hash = "sha256:1af1641e57cf7ba1bd67d677c9abdbcd6cc2ab7da3bca7fa1e2b7e50e65f2ad0"}, + {file = "coverage-7.13.4.tar.gz", hash = "sha256:e5c8f6ed1e61a8b2dcdf31eb0b9bbf0130750ca79c1c49eb898e2ad86f5ccc91"}, ] [package.extras] @@ -479,62 +493,62 @@ files = [ [[package]] name = "cryptography" -version = "46.0.4" +version = "46.0.5" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.8" groups = ["dev"] markers = "sys_platform == \"linux\"" files = [ - {file = "cryptography-46.0.4-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:281526e865ed4166009e235afadf3a4c4cba6056f99336a99efba65336fd5485"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f14fba5bf6f4390d7ff8f086c566454bff0411f6d8aa7af79c88b6f9267aecc"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47bcd19517e6389132f76e2d5303ded6cf3f78903da2158a671be8de024f4cd0"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:01df4f50f314fbe7009f54046e908d1754f19d0c6d3070df1e6268c5a4af09fa"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5aa3e463596b0087b3da0dbe2b2487e9fc261d25da85754e30e3b40637d61f81"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0a9ad24359fee86f131836a9ac3bffc9329e956624a2d379b613f8f8abaf5255"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:dc1272e25ef673efe72f2096e92ae39dea1a1a450dd44918b15351f72c5a168e"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:de0f5f4ec8711ebc555f54735d4c673fc34b65c44283895f1a08c2b49d2fd99c"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:eeeb2e33d8dbcccc34d64651f00a98cb41b2dc69cef866771a5717e6734dfa32"}, - {file = "cryptography-46.0.4-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3d425eacbc9aceafd2cb429e42f4e5d5633c6f873f5e567077043ef1b9bbf616"}, - {file = "cryptography-46.0.4-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91627ebf691d1ea3976a031b61fb7bac1ccd745afa03602275dda443e11c8de0"}, - {file = "cryptography-46.0.4-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2d08bc22efd73e8854b0b7caff402d735b354862f1145d7be3b9c0f740fef6a0"}, - {file = "cryptography-46.0.4-cp311-abi3-win32.whl", hash = "sha256:82a62483daf20b8134f6e92898da70d04d0ef9a75829d732ea1018678185f4f5"}, - {file = "cryptography-46.0.4-cp311-abi3-win_amd64.whl", hash = "sha256:6225d3ebe26a55dbc8ead5ad1265c0403552a63336499564675b29eb3184c09b"}, - {file = "cryptography-46.0.4-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:485e2b65d25ec0d901bca7bcae0f53b00133bf3173916d8e421f6fddde103908"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:078e5f06bd2fa5aea5a324f2a09f914b1484f1d0c2a4d6a8a28c74e72f65f2da"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dce1e4f068f03008da7fa51cc7abc6ddc5e5de3e3d1550334eaf8393982a5829"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:2067461c80271f422ee7bdbe79b9b4be54a5162e90345f86a23445a0cf3fd8a2"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:c92010b58a51196a5f41c3795190203ac52edfd5dc3ff99149b4659eba9d2085"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:829c2b12bbc5428ab02d6b7f7e9bbfd53e33efd6672d21341f2177470171ad8b"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:62217ba44bf81b30abaeda1488686a04a702a261e26f87db51ff61d9d3510abd"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:9c2da296c8d3415b93e6053f5a728649a87a48ce084a9aaf51d6e46c87c7f2d2"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9b34d8ba84454641a6bf4d6762d15847ecbd85c1316c0a7984e6e4e9f748ec2e"}, - {file = "cryptography-46.0.4-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:df4a817fa7138dd0c96c8c8c20f04b8aaa1fac3bbf610913dcad8ea82e1bfd3f"}, - {file = "cryptography-46.0.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b1de0ebf7587f28f9190b9cb526e901bf448c9e6a99655d2b07fff60e8212a82"}, - {file = "cryptography-46.0.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9b4d17bc7bd7cdd98e3af40b441feaea4c68225e2eb2341026c84511ad246c0c"}, - {file = "cryptography-46.0.4-cp314-cp314t-win32.whl", hash = "sha256:c411f16275b0dea722d76544a61d6421e2cc829ad76eec79280dbdc9ddf50061"}, - {file = "cryptography-46.0.4-cp314-cp314t-win_amd64.whl", hash = "sha256:728fedc529efc1439eb6107b677f7f7558adab4553ef8669f0d02d42d7b959a7"}, - {file = "cryptography-46.0.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a9556ba711f7c23f77b151d5798f3ac44a13455cc68db7697a1096e6d0563cab"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8bf75b0259e87fa70bddc0b8b4078b76e7fd512fd9afae6c1193bcf440a4dbef"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3c268a3490df22270955966ba236d6bc4a8f9b6e4ffddb78aac535f1a5ea471d"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:812815182f6a0c1d49a37893a303b44eaac827d7f0d582cecfc81b6427f22973"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a90e43e3ef65e6dcf969dfe3bb40cbf5aef0d523dff95bfa24256be172a845f4"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a05177ff6296644ef2876fce50518dffb5bcdf903c85250974fc8bc85d54c0af"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:daa392191f626d50f1b136c9b4cf08af69ca8279d110ea24f5c2700054d2e263"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e07ea39c5b048e085f15923511d8121e4a9dc45cee4e3b970ca4f0d338f23095"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d5a45ddc256f492ce42a4e35879c5e5528c09cd9ad12420828c972951d8e016b"}, - {file = "cryptography-46.0.4-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:6bb5157bf6a350e5b28aee23beb2d84ae6f5be390b2f8ee7ea179cda077e1019"}, - {file = "cryptography-46.0.4-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd5aba870a2c40f87a3af043e0dee7d9eb02d4aff88a797b48f2b43eff8c3ab4"}, - {file = "cryptography-46.0.4-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:93d8291da8d71024379ab2cb0b5c57915300155ad42e07f76bea6ad838d7e59b"}, - {file = "cryptography-46.0.4-cp38-abi3-win32.whl", hash = "sha256:0563655cb3c6d05fb2afe693340bc050c30f9f34e15763361cf08e94749401fc"}, - {file = "cryptography-46.0.4-cp38-abi3-win_amd64.whl", hash = "sha256:fa0900b9ef9c49728887d1576fd8d9e7e3ea872fa9b25ef9b64888adc434e976"}, - {file = "cryptography-46.0.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:766330cce7416c92b5e90c3bb71b1b79521760cdcfc3a6a1a182d4c9fab23d2b"}, - {file = "cryptography-46.0.4-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c236a44acfb610e70f6b3e1c3ca20ff24459659231ef2f8c48e879e2d32b73da"}, - {file = "cryptography-46.0.4-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:8a15fb869670efa8f83cbffbc8753c1abf236883225aed74cd179b720ac9ec80"}, - {file = "cryptography-46.0.4-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:fdc3daab53b212472f1524d070735b2f0c214239df131903bae1d598016fa822"}, - {file = "cryptography-46.0.4-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:44cc0675b27cadb71bdbb96099cca1fa051cd11d2ade09e5cd3a2edb929ed947"}, - {file = "cryptography-46.0.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:be8c01a7d5a55f9a47d1888162b76c8f49d62b234d88f0ff91a9fbebe32ffbc3"}, - {file = "cryptography-46.0.4.tar.gz", hash = "sha256:bfd019f60f8abc2ed1b9be4ddc21cfef059c841d86d710bb69909a688cbb8f59"}, + {file = "cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0"}, + {file = "cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731"}, + {file = "cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82"}, + {file = "cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1"}, + {file = "cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48"}, + {file = "cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4"}, + {file = "cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0"}, + {file = "cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663"}, + {file = "cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826"}, + {file = "cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d"}, + {file = "cryptography-46.0.5-cp314-cp314t-win32.whl", hash = "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a"}, + {file = "cryptography-46.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4"}, + {file = "cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d"}, + {file = "cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c"}, + {file = "cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4"}, + {file = "cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9"}, + {file = "cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72"}, + {file = "cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:3b4995dc971c9fb83c25aa44cf45f02ba86f71ee600d81091c2f0cbae116b06c"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257"}, + {file = "cryptography-46.0.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7"}, + {file = "cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d"}, ] [package.dependencies] @@ -547,7 +561,7 @@ nox = ["nox[uv] (>=2024.4.15)"] pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.14)", "ruff (>=0.11.11)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi (>=2024)", "cryptography-vectors (==46.0.4)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] +test = ["certifi (>=2024)", "cryptography-vectors (==46.0.5)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] test-randomorder = ["pytest-randomly"] [[package]] @@ -564,38 +578,48 @@ files = [ [[package]] name = "dulwich" -version = "1.0.0" +version = "1.1.0" description = "Python Git Library" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "dulwich-1.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2881fe077792474cbd8cae140e7c2c2263fc07017ba37051946d869151f79624"}, - {file = "dulwich-1.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:3f090da8af238d7d9dba53a87b86a9e3e21529690b7536c253b1d5061bdb3a4d"}, - {file = "dulwich-1.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:af39c560b75f64ec2d44519baf51ed1871be69e7ed4b0a4314215fa1ffe71195"}, - {file = "dulwich-1.0.0-cp310-cp310-win32.whl", hash = "sha256:c2f2b2692524468bcd91bf79aa1f420bbbb59996506ec434ad6e72227f52365b"}, - {file = "dulwich-1.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:2ba6072e922b815006b036d0df59b25eb2523b89581fcf9f47feb43324629ba4"}, - {file = "dulwich-1.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1213da9832621b07dfaafdb651b74edb8966481475c52be0bff8dee352d75853"}, - {file = "dulwich-1.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:e370e3cdd0b00c059ebee8371cc1644aa61d6de3de0ca5c2f2a5f075bf4c53d9"}, - {file = "dulwich-1.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:86271e17d76a667abb1d68dad83b6324422a1ab20d60be30395fd60a37b735b1"}, - {file = "dulwich-1.0.0-cp311-cp311-win32.whl", hash = "sha256:3051007bc2792b5a72fee938842cf45b66924d6d5147d824f3e609eb75fc0322"}, - {file = "dulwich-1.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:5cf6e9b5620a3e842663b58ad534da29944db6a6016ba61fc9bbed24830cd85f"}, - {file = "dulwich-1.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6736abc2ce2994e38a00a3a4c80237b2b944e7c6f4e346119debdd2592312d83"}, - {file = "dulwich-1.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:06514b02da1e32a077062924d2c3b20a7bc76ab9b92eeac691f72b76b14111bc"}, - {file = "dulwich-1.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:32b6fb1205b1d9c0e43986f9e4e5e50a3670014440e61498eca2b8ab6b00129f"}, - {file = "dulwich-1.0.0-cp312-cp312-win32.whl", hash = "sha256:1a6583499b915fe5a8ac5595325f1e6a6a5a456de1575e0293e8a6ebb6915f3f"}, - {file = "dulwich-1.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:f90b54faeb995607c876cdb2c082c0f0af702e1ccb524c6126ce99a36536fa3f"}, - {file = "dulwich-1.0.0-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:ff94f47f0b5787d4e6a0105daf51ff9cdb4e5b9d4e9f8dd01b58ba9a5b79bbd9"}, - {file = "dulwich-1.0.0-cp313-cp313-android_21_x86_64.whl", hash = "sha256:1d95663441c930631d9d1765dc4f427dcc0662af45f42a0831357e60055ddb84"}, - {file = "dulwich-1.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:78542a62fabea894943a1d01c9c477a56eee5f7d58d3bdee42c7e0622ddf6893"}, - {file = "dulwich-1.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:d1c33f6456e4335dfe6f4d3917fa7d77050d6470bbbaf8054b5c5084ee8e8cd1"}, - {file = "dulwich-1.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:581330cf799577f194fda2b5384b7ba50e095de7ff088779c027a6de63642de2"}, - {file = "dulwich-1.0.0-cp313-cp313-win32.whl", hash = "sha256:276ff18ae734fe4a1be66d4267216a51d2deab0ac981d722db3d32fcc2ac4ff8"}, - {file = "dulwich-1.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:cc0ab4ba7fd8617bebe20294dedaa8f713d1767ce059bfbefd971b911b702726"}, - {file = "dulwich-1.0.0-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:1bd4c872b617edc20e0b726479ec25705a0107a4bd16f83cb6cd108d86950634"}, - {file = "dulwich-1.0.0-cp314-cp314-android_24_x86_64.whl", hash = "sha256:fc791771859258ffbe34a6579ece30684a535ae0341fd2f5adfa5f64cc885c13"}, - {file = "dulwich-1.0.0-py3-none-any.whl", hash = "sha256:221be803b71b060c928e9faae4ab3e259ff5beac6e0c251ba3c176b51b5c2ffb"}, - {file = "dulwich-1.0.0.tar.gz", hash = "sha256:3d07104735525f22bfec35514ac611cf328c89b7acb059316a4f6e583c8f09bc"}, + {file = "dulwich-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59e10ca543b752fa4b467a9ce420ad95b65e232f817f91809e64fe76eb8e27c6"}, + {file = "dulwich-1.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:be593608a57f5cfa2a1b9927c1b486c3007f5a6f34ff251feaeca3a6a43d4780"}, + {file = "dulwich-1.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:904f09ae3364dc8c026812b0478f2411a973f404aa2654ea18d9f340b3915872"}, + {file = "dulwich-1.1.0-cp310-cp310-win32.whl", hash = "sha256:6d5a0be4a84cc6ad23b6dcf2f9cbf2a0a65dd907612ad38312b2259ebe7bae56"}, + {file = "dulwich-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:6e318970e405987d10c1fd8d1e45f4e8c75874e771a5512f6fbb51b13d5a3108"}, + {file = "dulwich-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cb5e28210e34e6473d982cdf99e420dd2791e7af4d9be796fa760055951d82df"}, + {file = "dulwich-1.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:d491e05d434a403f2ed7454002f39ce6fb9ae8de93bded368721bdb9a1f41778"}, + {file = "dulwich-1.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:5a662942f123614077f14bc31e66f6adce09561cc25da1ef716c13be8dba56c5"}, + {file = "dulwich-1.1.0-cp311-cp311-win32.whl", hash = "sha256:b223d00cf564c99986945bd18a74e2e9ef85e713cfe5ad61d04184c386d52fed"}, + {file = "dulwich-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a1959be27d8201fcee8612da8afecd8e7992d8db8767dcef8704264db09db2ad"}, + {file = "dulwich-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f6dd0c5fc45c84790d4a48d168d07f0aa817fcb879d2632e6cee603e98a843c"}, + {file = "dulwich-1.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f8789e14981be2d33c3c36a14ec55ae06780c0a865e9df107016c4489a4a022a"}, + {file = "dulwich-1.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9a32f92c2eb86c84a175261f8fb983b6765bb31618d79d0c0dd68fab6f6ca94a"}, + {file = "dulwich-1.1.0-cp312-cp312-win32.whl", hash = "sha256:06c18293fb2c715f035052f0c74f56e5ff52925ad4d0b5a0ebf16118daa5e340"}, + {file = "dulwich-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:e738163865dfccf155ef5fa3a2b2c849f38dadc6f009d2be355864233899bb4b"}, + {file = "dulwich-1.1.0-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:3ba0cb28848dd8fd80d4389d1b83968da172376cea34f9bdb39043970fa1a045"}, + {file = "dulwich-1.1.0-cp313-cp313-android_21_x86_64.whl", hash = "sha256:8cf55f0de4cf90155aa3ab228c8ef9e7e10f7c785339f1688fb71f6adaae302c"}, + {file = "dulwich-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:49c39844b4abe53612d18add7762faf886ade70384a101912e0849f56f885913"}, + {file = "dulwich-1.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:941735c87b3657019d197bb72f0e9ec03cbdbf959dc0869e672f5c6871597442"}, + {file = "dulwich-1.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:37be136c7a85a64ae0cf8030f4fb2fa4860cff653ad3bcf13c49bf59fea2020c"}, + {file = "dulwich-1.1.0-cp313-cp313-win32.whl", hash = "sha256:2f5a455e67f9ddd018299ce8dd05861a2696d35c6af91e9acdb4af0767bc0b8b"}, + {file = "dulwich-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9b1bbb785f29f9eb51cddb9d80f82dac03939b7444961283b09adac19a823e88"}, + {file = "dulwich-1.1.0-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:fc38cc6f60c5e475fa61dcd2b743113f35377602c1ba1c82264898d97a7d3c48"}, + {file = "dulwich-1.1.0-cp314-cp314-android_24_x86_64.whl", hash = "sha256:c9752d25f01e92587f8db52e50daf3e970deb49555340653ea44ba5e60f0f416"}, + {file = "dulwich-1.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:693c450a5d327a6a5276f5292d3dd0bc473066d2fd2a2d69a990d7738535deb6"}, + {file = "dulwich-1.1.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:dff1b67e0f76fcaae8f7345c05b1c4f00c11a6c42ace20864e80e7964af31827"}, + {file = "dulwich-1.1.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:1b1b9adaf82301fd7b360a5fa521cec1623cb9d77a0c5a09d04396637b39eb48"}, + {file = "dulwich-1.1.0-cp314-cp314-win32.whl", hash = "sha256:eb5440145bb2bbab71cdfa149fd297a8b7d4db889ab90c58d7a07009a73c1d28"}, + {file = "dulwich-1.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:333b0f93b289b14f98870317fb0583fdf73d5341f21fd09c694aa88bb06ad911"}, + {file = "dulwich-1.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a0f3421802225caedd11e95ce40f6a8d3c7a5df906489b6a5f49a20f88f62928"}, + {file = "dulwich-1.1.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:518307ab080746ee9c32fc13e76ad4f7df8f7665bb85922e974037dd9415541a"}, + {file = "dulwich-1.1.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:0890fff677c617efbac0cd4584bec9753388e6cd6336e7131338ea034b47e899"}, + {file = "dulwich-1.1.0-cp314-cp314t-win32.whl", hash = "sha256:a05a1049b3928205672913f4c490cf7b08afaa3e7ee7e55e15476e696412672f"}, + {file = "dulwich-1.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:ba6f3f0807868f788b7f1d53b9ac0be3e425136b16563994f5ef6ecf5b7c7863"}, + {file = "dulwich-1.1.0-py3-none-any.whl", hash = "sha256:bcd67e7f9bdffb4b660330c4597d251cd33e74f5df6898a2c1e6a1730a62af06"}, + {file = "dulwich-1.1.0.tar.gz", hash = "sha256:9aa855db9fee0a7065ae9ffb38e14e353876d82f17e33e1a1fb3830eb8d0cf43"}, ] [package.dependencies] @@ -604,7 +628,7 @@ urllib3 = ">=2.2.2" [package.extras] aiohttp = ["aiohttp"] colordiff = ["rich"] -dev = ["codespell (==2.4.1)", "dissolve (>=0.1.1)", "mypy (==1.19.1)", "ruff (==0.14.10)"] +dev = ["codespell (==2.4.1)", "dissolve (>=0.1.1)", "mypy (==1.19.1)", "ruff (==0.14.14)"] fastimport = ["fastimport"] fuzzing = ["atheris"] https = ["urllib3 (>=2.2.2)"] @@ -630,14 +654,14 @@ devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benc [[package]] name = "filelock" -version = "3.20.3" +version = "3.25.0" description = "A platform independent file lock." optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1"}, - {file = "filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1"}, + {file = "filelock-3.25.0-py3-none-any.whl", hash = "sha256:5ccf8069f7948f494968fc0713c10e5c182a9c9d9eef3a636307a20c2490f047"}, + {file = "filelock-3.25.0.tar.gz", hash = "sha256:8f00faf3abf9dc730a1ffe9c354ae5c04e079ab7d3a683b7c32da5dd05f26af3"}, ] [[package]] @@ -1015,14 +1039,14 @@ files = [ [[package]] name = "pbs-installer" -version = "2026.2.3" +version = "2026.2.11" description = "Installer for Python Build Standalone" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "pbs_installer-2026.2.3-py3-none-any.whl", hash = "sha256:0225254ada5812c34761325edfd9c744317422c2767e434143307da171de0673"}, - {file = "pbs_installer-2026.2.3.tar.gz", hash = "sha256:1328eacb32b3a5bad59edeff942e16f4083c79e6dea46cdd8b98141b4030a93d"}, + {file = "pbs_installer-2026.2.11-py3-none-any.whl", hash = "sha256:0a1eb8bc6c0a53f381b8dc09c18c0d7aa9e6a2495b0bf02b27d48af6b6b4d01f"}, + {file = "pbs_installer-2026.2.11.tar.gz", hash = "sha256:7eb2730aaa8e2a9aa51db3871e494d058dbab64328deec1fc7bdbbc68578167f"}, ] [package.dependencies] @@ -1064,21 +1088,16 @@ testing = ["pytest", "pytest-cov", "wheel"] [[package]] name = "platformdirs" -version = "4.5.1" +version = "4.9.2" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31"}, - {file = "platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda"}, + {file = "platformdirs-4.9.2-py3-none-any.whl", hash = "sha256:9170634f126f8efdae22fb58ae8a0eaa86f38365bc57897a6c4f781d1f5875bd"}, + {file = "platformdirs-4.9.2.tar.gz", hash = "sha256:9a33809944b9db043ad67ca0db94b14bf452cc6aeaac46a88ea55b26e2e9d291"}, ] -[package.extras] -docs = ["furo (>=2025.9.25)", "proselint (>=0.14)", "sphinx (>=8.2.3)", "sphinx-autodoc-typehints (>=3.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.4.2)", "pytest-cov (>=7)", "pytest-mock (>=3.15.1)"] -type = ["mypy (>=1.18.2)"] - [[package]] name = "pluggy" version = "1.6.0" @@ -1097,14 +1116,14 @@ testing = ["coverage", "pytest", "pytest-benchmark"] [[package]] name = "poethepoet" -version = "0.41.0" +version = "0.42.1" description = "A task runner that works well with poetry and uv." optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "poethepoet-0.41.0-py3-none-any.whl", hash = "sha256:4bab9fd8271664c5d21407e8f12827daeb6aa484dc6cc7620f0c3b4e62b42ee4"}, - {file = "poethepoet-0.41.0.tar.gz", hash = "sha256:dcaad621dc061f6a90b17d091bebb9ca043d67bfe9bd6aa4185aea3ebf7ff3e6"}, + {file = "poethepoet-0.42.1-py3-none-any.whl", hash = "sha256:d8d1345a5ca521be9255e7c13bc2c4c8698ed5e5ac5e9e94890d239fcd423d0a"}, + {file = "poethepoet-0.42.1.tar.gz", hash = "sha256:205747e276062c2aaba8afd8a98838f8a3a0237b7ab94715fab8d82718aac14f"}, ] [package.dependencies] @@ -1402,14 +1421,14 @@ hook-testing = ["execnet (>=1.5.0)", "psutil", "pytest (>=2.7.3)"] [[package]] name = "pyinstaller-hooks-contrib" -version = "2026.0" +version = "2026.1" description = "Community maintained hooks for PyInstaller" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "pyinstaller_hooks_contrib-2026.0-py3-none-any.whl", hash = "sha256:0590db8edeba3e6c30c8474937021f5cd39c0602b4d10f74a064c73911efaca5"}, - {file = "pyinstaller_hooks_contrib-2026.0.tar.gz", hash = "sha256:0120893de491a000845470ca9c0b39284731ac6bace26f6849dea9627aaed48e"}, + {file = "pyinstaller_hooks_contrib-2026.1-py3-none-any.whl", hash = "sha256:66ad4888ba67de6f3cfd7ef554f9dd1a4389e2eb19f84d7129a5a6818e3f2180"}, + {file = "pyinstaller_hooks_contrib-2026.1.tar.gz", hash = "sha256:a5f0891a1e81e92406ab917d9e76adfd7a2b68415ee2e35c950a7b3910bc361b"}, ] [package.dependencies] @@ -1483,6 +1502,26 @@ pytest = ">=7" [package.extras] testing = ["process-tests", "pytest-xdist", "virtualenv"] +[[package]] +name = "python-discovery" +version = "1.1.0" +description = "Python interpreter discovery" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "python_discovery-1.1.0-py3-none-any.whl", hash = "sha256:a162893b8809727f54594a99ad2179d2ede4bf953e12d4c7abc3cc9cdbd1437b"}, + {file = "python_discovery-1.1.0.tar.gz", hash = "sha256:447941ba1aed8cc2ab7ee3cb91be5fc137c5bdbb05b7e6ea62fbdcb66e50b268"}, +] + +[package.dependencies] +filelock = ">=3.15.4" +platformdirs = ">=4.3.6,<5" + +[package.extras] +docs = ["furo (>=2025.12.19)", "sphinx (>=9.1)", "sphinx-autodoc-typehints (>=3.6.3)", "sphinxcontrib-mermaid (>=2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.5.4)", "pytest (>=8.3.5)", "pytest-mock (>=3.14)", "setuptools (>=75.1)"] + [[package]] name = "python-dotenv" version = "1.2.1" @@ -1849,24 +1888,21 @@ zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] [[package]] name = "virtualenv" -version = "20.36.1" +version = "21.1.0" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "virtualenv-20.36.1-py3-none-any.whl", hash = "sha256:575a8d6b124ef88f6f51d56d656132389f961062a9177016a50e4f507bbcc19f"}, - {file = "virtualenv-20.36.1.tar.gz", hash = "sha256:8befb5c81842c641f8ee658481e42641c68b5eab3521d8e092d18320902466ba"}, + {file = "virtualenv-21.1.0-py3-none-any.whl", hash = "sha256:164f5e14c5587d170cf98e60378eb91ea35bf037be313811905d3a24ea33cc07"}, + {file = "virtualenv-21.1.0.tar.gz", hash = "sha256:1990a0188c8f16b6b9cf65c9183049007375b26aad415514d377ccacf1e4fb44"}, ] [package.dependencies] distlib = ">=0.3.7,<1" -filelock = {version = ">=3.20.1,<4", markers = "python_version >= \"3.10\""} +filelock = {version = ">=3.24.2,<4", markers = "python_version >= \"3.10\""} platformdirs = ">=3.9.1,<5" - -[package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] +python-discovery = ">=1" [[package]] name = "xattr" diff --git a/requirements.txt b/requirements.txt index c5a430d1cb6bcbbc0333bef082ad3497bfc1333d..09d940e9718587c876fa032f175393bd7f62c785 100644 GIT binary patch delta 130 zcmdlY`bcEL4R#|2Jq9BN(}{Q2Gn!29WsGMuocxwCnAH?0Y_QptX*v_LA%o%MyDWvQ zmLQdrgIOI}L3#`~&tzq1WVD>Tkj)dQ=QZ1U$xMb~hGd3(hBAg!h9ZVa23sIB1Sy@| V$ZomWfy0i86U+q}J-L%h8vx*qAB+G1 delta 238 zcmaDPvPE>l4OT-2JqDAB_t!HTPwr=oXEd1no-vr!04Qj@*_~-R6HxdeOChT%P{43< zIIAOAy}{