diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 199a819..1368b3a 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -1,5 +1,5 @@ -# This workflow will install Python dependencies, run tests and lint with a variety of Python versions -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python +# This workflow installs Python dependencies, runs tests, and lints across Python versions. +# For details see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python name: Python package @@ -11,30 +11,59 @@ on: jobs: build: - runs-on: ubuntu-latest strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] + env: + UV_PYTHON: ${{ matrix.python-version }} steps: - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install flake8 pytest - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - - name: Lint with flake8 + + - name: Set up uv + uses: astral-sh/setup-uv@v6 + with: + version: "0.13.7" + enable-cache: true + + - name: Sync dependencies + run: uv sync --locked --extra dev --python ${{ matrix.python-version }} + + - name: Lint with ruff 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 + uv run --locked --extra dev ruff check . + - name: Test with pytest run: | - pytest + uv run --locked --extra dev pytest + + - name: Smoke test installed wheel + run: | + smoke_dir="$(mktemp -d "$RUNNER_TEMP/verbecc-wheel.XXXXXX")" + uv build --wheel --out-dir "$smoke_dir/dist" + uv venv --python "${{ matrix.python-version }}" "$smoke_dir/venv" + uv pip install --python "$smoke_dir/venv/bin/python" "$smoke_dir"/dist/*.whl + cd "$smoke_dir" + "$smoke_dir/venv/bin/python" -I - <<'PY' + from verbecc import CompleteConjugator + from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang + + for lang, infinitive in [ + (Lang.ca, "parlar"), + (Lang.fr, "parler"), + (Lang.it, "parlare"), + (Lang.pt, "falar"), + (Lang.ro, "vorbi"), + (Lang.es, "hablar"), + ]: + conjugation = CompleteConjugator(lang=lang).conjugate(infinitive) + assert conjugation, f"No conjugation for {lang}: {infinitive}" + print(f"Installed wheel smoke test passed: {lang} {infinitive}") + PY \ No newline at end of file diff --git a/.gitignore b/.gitignore index ccb3a8a..6c3ff17 100755 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,13 @@ .eggs *.egg-info .tox/ +.venv/ __pycache__ dist build .coverage coverage.xml .vscode -verbecc/data/models/*.zip +src/verbecc/data/models/*.zip *.log *.log.* \ No newline at end of file diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..6324d40 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.14 diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e0121d..9a8940d 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,79 @@ # verbecc Changelog +- 2.1.0 [7 November 2026] + - In a nutshell + - This PR (2.1.0) modernizes the project layout and drops Python 3.9 support. The biggest user-facing changes are a move to a standard src/ layout and a package rename/restructure (verbecc.src → verbecc.core). Development and CI now use uv with locked dependencies. Several __init__.py files were simplified, which may break some import paths, but `from verbecc import CompleteConjugator` remains supported. Fixed missing wheel data and added installed-wheel smoke tests. + - Summary (key data points) + - PR: 2.1.0 — "Misc. modernization" (author: bretttolbert) + - URL: https://github.com/bretttolbert/verbecc/pull/54 + - State: open (draft) + - Description highlights: + - Dropping support for Python 3.9 + - Restructured repository to standard src layout (verbecc/src/verbecc) + - Renamed package/namespace from verbecc.src to verbecc.core + - Simplified multiple __init__.py files (potential import breakages) + - Transitioned development and CI to uv with `uv.lock` + - Fixed wheel resource packaging and preserved the public `CompleteConjugator` import + - Added clean-environment wheel smoke tests for all six supported languages + - CHANGELOG link included in PR description + - Size: 252 files changed, +2,214 additions, −2,006 deletions (large refactor) + - Risk assessment: Medium–High + - Why: Large movement of files + package renames and __init__ simplifications are likely to break downstream imports, packaging, and third-party integrations. Dropping Python 3.9 reduces the test surface but may require CI and packaging updates. + - Validation: 36 targeted public API, conjugator, and import-style tests passed. The installed-wheel smoke test passed locally on Python 3.14 for all six languages; CI runs it across Python 3.10–3.14. Coverage and full matrix results are not reported here. + - Breaking-change likelihood: High for consumers who imported using old package paths or relied on package layout; moderate for internal code once imports are updated. + - Details + - Fixed packaging, misc. modernization + - **Dropping support for python3.9** (it has been EOL for nearly a year) + - Migrated from `importlib_resources`to `importlib.resources` + - Eliminates python39 import resource hacks + - Eliminates backports (e.g., `importlib_resources` for Python <3.13). Dependencies simplified and updated accordingly. + - Changed structure from `verbecc/verbecc/src` to more standard `verbecc/src/verbecc` + - Before data was at `verbecc/verbecc/data` + - After data is at `verbecc/src/verbecc/data` + - Source is now at `verbecc/src/verbecc/core` + - `verbecc.src` is now `verbecc.core` + - Modified `pyproject.toml` to scan for packages inside `src` + - Fixed wheels missing YAML configuration, XML conjugation/verb data, trained models, and `py.typed` + - Replaced the non-package `trained_models` package-data key with patterns relative to the `verbecc` package + - Preserved existing icon and font resources + - Removed `verbecc/__init__.py` which should not exist because this project is using a `src` layout + - Modified (simplified) contents of a many `__init__.py` files + - Restored `from verbecc import CompleteConjugator` through one-level re-exports via `core` and `core.conjugator`, preserving R005/R006 compliance + - Added public API regression tests for re-export identity and conjugation + - **This will likely break some things**, consequently bumping minor rev + - Doing this to comply with new style guide rule: + - [R006 - `__init__.py` files shall not contain imports deeper than one level](./CONTRIBUTING.md#r006---__init__py-files-shall-not-contain-imports-deeper-than-one-level) + - Changed `setup.cfg` `description-file` to `description_file` to fix warning + - Added numerous missing type annotations + - Fixed all VSCode warnings with strict type checking enabled + - Added `str` as optional type for public API methods, e.g. so that either specific types like `Mood` or generic strings are explicitly allowed e.g. + - Before: `mood: Mood` + - After: `mood: Mood | str` + - Contd. cleaning up (removing) excessive `__init__.py` imports + - Modernized Python syntax + - Systematic updates across the codebase + - Converted from `List` to modern `list` + - Converted from `Dict` to modern `dict` + - Modified build configuration to generate type stubs + - Transitioned development and GitHub Actions CI to uv + - Added `uv.lock` for reproducible development dependency resolution + - Use `uv sync --locked --extra dev` to set up the project-root `.venv` + - Run linting and tests with `uv run --locked --extra dev ruff check .` and `uv run --locked --extra dev pytest` + - Run `uv lock` after changing dependencies and commit the updated lockfile + - Updated the [development environment instructions](./CONTRIBUTING.md#development-environment) + - Modernized GitHub Actions CI config (`.github/workflows/python-package.yml`) + - Test Python 3.10–3.14 with uv-managed environments + - Build the wheel, install it and its runtime dependencies into a fresh virtual environment, and conjugate one verb per supported language + - Run the wheel smoke test outside the checkout with `python -I` so editable installs and source-tree imports cannot hide missing package data or broken public imports + - Migrated linter from `flake8` to `ruff` + - Updated dependencies to explicitly specify `pytest` and `ruff` as optional `dev` dependencies + - E.g. install verbecc including optional dev deps: + - From PyPi: `pip install "verbecc[dev]"` + - From GitHub: `pip install "git+https://github.com/bretttolbert/verbecc.git[dev]"` + - Local development: `uv sync --locked --extra dev` + - Updated dependencies to explicitly pull optional `yaml` deps for `dataclass-wizard` + - I.e. `"dataclass-wizard[yaml]>=0.39.1",` + - 2.0.3 [26 August 2026] - PR #51 [Peter Abolins] - Fixes [#49](https://github.com/bretttolbert/verbecc/issues/49) [ro] Four damaged templates in conjugations-ro.xml produce non-words: contraf:ace, a:vea, :vrea, dezmi:nţi diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..6c0969a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,71 @@ +# Contributing to verbecc + +First off, thank you for taking the time to contribute! 🎉 + +--- + +## Table of Contents + +- [Development environment](#development-environment) +- [Style Guide](#style-guide) + +## Development environment + +Install [uv](https://docs.astral.sh/uv/getting-started/installation/), then run +these commands from the repository root: + +```bash +uv sync --locked --extra dev +uv run --locked --extra dev ruff check . +uv run --locked --extra dev pytest +``` + +Use the project-root `.venv` as your editor's Python interpreter. Dependencies +are managed in `pyproject.toml` and `uv.lock`; run `uv lock` after changing +dependencies and commit the updated lockfile. + +CI also builds a wheel and installs it with its runtime dependencies into a +fresh virtual environment for each supported Python version. The smoke test +runs outside the checkout in Python isolated mode, imports the public +`CompleteConjugator` entry point, and conjugates one verb in each of the six +supported languages to catch missing package data and broken public imports. + +## Style Guide + +### R001 - The Python `@property` decorator shall not be used + +This is primarily because I intend to port verbecc to Java and other languages and I wish to keep the implementations as consistent as possible, preferably a _1:1_ equivalence in all symbol names, _mutata mutandis_. + +### R002 - There shall not be more than one class definition per file + +This simplifies porting to Java, allowing _1:1_ source file correspondence. This rule is enforced by the `test_ast` unit-tests. + +### R003 - Type annotations are required for all parameters and return statements + +This rule is enforced by the `test_ast` unit-tests. + +### R004 - Don't use parentheses in import statements + +Without parentheses, I can easily sort the import statements (_Ctrl+P Sort Lines Ascending_), +but with parentheses, I can't do that. + +### R005 - `__init__.py` files shall only use relative imports + +- Allowed: `from .module import SomeClass` +- Not allowed: `from verbecc.core.foo import bar` + +How to check: +```bash +uv run --locked --extra dev pytest -k test_STYLE_GUIDE_R005_only_relative_imports +``` + +### R006 - `__init__.py` files shall not contain imports deeper than one level + +- Allowed: `from .module import SomeClass` +- Not Allowed: `from .module.submodule import SomeClass` + +Why: +- Pollutes the namespace +- Hides where things come from +- Slows down import time by unnecessarily loading unused modules +- Causes circular imports diff --git a/README.md b/README.md index c14e925..32fc59a 100755 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +![Verbecc feature graphic](src/verbecc/resources/icons/feature-graphic.png) + # Python library for verb conjugation in French, Spanish, Catalan, Italian, Portuguese, and Romanian, enhanced by machine learning - `verbecc` python library @@ -56,25 +58,97 @@ * Meticulously organized source tree * Has a plethora of unit-tests to ensure correctness of verb conjugations * Continuous Integration with GitHub Actions CI/CD pipeline - * CI tests python 3.9, 3.10, 3.11, 3.12, 3.13 and 3.14. + * CI tests python 3.10, 3.11, 3.12, 3.13 and 3.14. * Dependencies: `scikit-learn`, `scipy`, `numpy`, `lxml`, `pyaml`, `jsbeautifier`, `importlib_resources` * **Trusted** * Cited in [academic publications](#academic-publications-referencing-verbecc) +## Installation + +Verbecc development and CI have transitioned to +[uv](https://docs.astral.sh/uv/), using `uv.lock` for reproducible development +environments. uv is optional for library users: Verbecc remains a standard +Python package that you can install with pip. + +Install [uv](https://docs.astral.sh/uv/getting-started/installation/) to follow +the examples below. + +### Compatibility with pip + +uv's pip-compatible interface lets you replace common `pip ...` commands with +`uv pip ...` commands while continuing to use standard Python packages and +virtual environments: -## Quick Start +| pip | uv equivalent | +| --- | --- | +| `pip install verbecc` | `uv pip install verbecc` | +| `pip install --upgrade verbecc` | `uv pip install --upgrade verbecc` | +| `pip install -e ".[dev]"` | `uv pip install -e ".[dev]"` | +| `pip install -r requirements.txt` | `uv pip install -r requirements.txt` | +| `pip uninstall verbecc` | `uv pip uninstall verbecc` | +| `pip list` | `uv pip list` | + +By default, `uv pip` installs into an active virtual environment or a discovered +`.venv`; create one with `uv venv` first. To target a specific interpreter, use +`uv pip install --python /path/to/venv/bin/python verbecc`. If you prefer pip, +use `python -m venv .venv`, activate it, and run `python -m pip install verbecc`. + +This compatibility covers common workflows, not every pip option or behavior. +uv uses its own configuration and environment variables rather than +`pip.conf` or `PIP_*` settings; see the +[uv/pip compatibility guide](https://docs.astral.sh/uv/pip/compatibility/). +For development in this repository, prefer `uv sync --locked --extra dev` +over `uv pip install -e ".[dev]"` so the committed lockfile is respected. + +### (User) Install from PyPi ```bash -git clone https://github.com/bretttolbert/verbecc.git -cd verbecc -pip install . +uv venv +uv pip install "verbecc" ``` -### Academic publications referencing verbecc +### (User) Install from GitHub repo +```bash +uv venv +uv pip install "git+https://github.com/bretttolbert/verbecc.git" +``` + +### (Developer) Clone GitHub repo and install +```bash +git clone git@github.com/bretttolbert/verbecc.git && cd verbecc +uv sync --locked --extra dev +``` + +This creates `.venv` in the project root and installs the project in editable +mode with its development dependencies. The default development Python version +is recorded in `.python-version`; Python 3.10 through 3.14 remain supported. +To use another supported version, pass `--python`, for example +`uv sync --locked --extra dev --python 3.10`. + +Run commands in this environment without activating it: +```bash +uv run --locked --extra dev pytest +uv run --locked --extra dev ruff check . +``` + +In VS Code, select `.venv/bin/python` (or `.venv\Scripts\python.exe` on +Windows) using **Python: Select Interpreter**. For an interactive shell, +activate the environment with `source .venv/bin/activate` on POSIX or +`.venv\Scripts\Activate.ps1` in PowerShell. + +Dependencies are declared in `pyproject.toml` and pinned in the committed +`uv.lock`, which replaces `requirements.txt`. After changing dependency +declarations, run `uv lock` and `uv sync --locked --extra dev`, and commit +both the manifest and lockfile. + +### Optional dependency extras +- `[dev]` - development dependencies (includes `pytest` and `ruff`) + +## Academic publications referencing verbecc - [Segura Lores, Alba. (2025) Estudio de la variación del sujeto pronominal en la ciudad de Málaga: sociolingüística cognitiva de su producción y percepción. Universität Heidelberg. pp. 69. [DOI:10.11588/heidok.00037508]](https://nbn-resolving.org/urn:nbn:de:bsz:16-heidok-375088) -### What's new in Verbecc 2.0 +## What's new in Verbecc 2.0 | verbecc 1.x | verbecc 2.x | | --- | --- | @@ -92,8 +166,8 @@ pip install . | Array index is used to determine `Person`, i.e. `1s, 2s, 3s, 1p, 2p, 3p` | Each `Conjugation` object in the `TenseConjugation` has `Person`, `Number` and `Gender` values (any of which may be `None` if not-applicable) | | Returned objects are primitive (`Dict`) data types | Returned wrapper objects are subclasses of `AbstractConjugation` (e.g. `CompleteConjugation`) with `get_data()` and `to_json()` methods | | `Conjugator` returns `CompleteConjugationData` | `CompleteConjugator` returns wrapper type `CompleteConjugation`, `CompleteConjugation.get_data()` returns `CompleteConjugationData` | -| (no wrapper types) | Wrapper types hierarchy: `CompleteConjugation` > `MoodsConjugation` > `MoodConjugation` > `TenseConjugation` > `Conjugation` -> `conjugations: List[str]` | -| Primitive data types hierarchy: `Conjugation` > `MoodsConjugation` > `MoodConjugation` > `TenseConjugation` > `PersonConjugation` | Primitive data types hierarchy: `CompleteConjugationData` > `MoodsConjugationData` > `MoodConjugationData` > `TenseConjugationData` > `ConjugationData` -> `conjugations: List[str]` | +| (no wrapper types) | Wrapper types hierarchy: `CompleteConjugation` > `MoodsConjugation` > `MoodConjugation` > `TenseConjugation` > `Conjugation` -> `conjugations: list[str]` | +| Primitive data types hierarchy: `Conjugation` > `MoodsConjugation` > `MoodConjugation` > `TenseConjugation` > `PersonConjugation` | Primitive data types hierarchy: `CompleteConjugationData` > `MoodsConjugationData` > `MoodConjugationData` > `TenseConjugationData` > `ConjugationData` -> `conjugations: list[str]` | | `pred_score` was always included in the output | `pred_score` is only included in output if `predicted` is `true` | | Only returned primitive Python data | `Conjugation` objects have both `.to_json()` and `.to_yaml()` methods | | diff --git a/STYLE_GUIDE.md b/STYLE_GUIDE.md deleted file mode 100644 index a503cc0..0000000 --- a/STYLE_GUIDE.md +++ /dev/null @@ -1,58 +0,0 @@ -# verbecc Style Guide - -## The Python `@property` decorator shall not be used - -This is primarily because I intend to port verbecc to Java and other languages and I wish to keep the implementations as consistent as possible, preferably a _1:1_ equivalence in all symbol names, _mutata mutandis_. - -## There shall not be more than class definition per file - -This simplifies porting to Java, allowing _1:1_ source file correspondence. This rule is enforced by the `test_ast` unit-tests. - -## Type annotations are required for all parameters and return statements - -This rule is enforced by the `test_ast` unit-tests. - -## Don't use parentheses in import statements - -Without parentheses, I can easily sort the import statements (_Ctrl+P Sort Lines Ascending_), -but with parentheses, I can't do that. - -Do this: -```python -from verbecc.src.conjugator.conjugation_object import ConjugationObjects -from verbecc.src.defs.types.data.person_ending import PersonEnding -from verbecc.src.defs.types.data.tense_template import TenseTemplate -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.defs.types.lang_specific_options import LangSpecificOptions -from verbecc.src.defs.types.lang_specific_options import LangSpecificOptionsEs -from verbecc.src.defs.types.lang_specific_options import LangSpecificOptionsFactory -from verbecc.src.defs.types.lang_specific_options import VoseoOptions -from verbecc.src.defs.types.mood import Mood, Moods -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.tense import Tense, Tenses -from verbecc.src.inflectors.inflector import Inflector -from verbecc.src.utils.string_utils import strip_accents -``` - -Don't do this: -```python -from verbecc.src.conjugator.conjugation_object import ConjugationObjects -from verbecc.src.defs.types.data.person_ending import PersonEnding -from verbecc.src.defs.types.data.tense_template import TenseTemplate -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.defs.types.lang_specific_options import ( - LangSpecificOptions, - LangSpecificOptionsEs, - LangSpecificOptionsFactory, - VoseoOptions, -) -from verbecc.src.defs.types.mood import Mood, Moods -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.tense import Tense, Tenses -from verbecc.src.inflectors.inflector import Inflector -from verbecc.src.utils.string_utils import strip_accents -``` \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 9e579a0..ff4d9d3 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,26 +4,8 @@ build-backend = "setuptools.build_meta" [project] name = "verbecc" -version = "2.0.3" -dependencies = [ - "backports.strenum (>=1.3.1,<2.0) ; python_version < '3.11'", - "cython>=3.1.4", - "dataclass-wizard==0.35.1", - "importlib_resources==6.4.5 ; python_version < '3.13'", - "importlib_resources==6.5.2 ; python_version >= '3.13'", - "jsbeautifier>=1.15.4,<2.0", - "lxml-stubs>=0.5.1", - "lxml>=6.0.2", - "numpy==1.26.2 ; python_version < '3.11'", - "numpy==2.3.3 ; python_version >= '3.11'", - "pyaml==25.7.0", - "scikit-learn==1.6.1 ; python_version < '3.10'", - "scikit-learn==1.7.2 ; python_version >= '3.10'", - "scipy==1.13.1 ; python_version < '3.10'", - "scipy==1.15.3 ; python_version == '3.10'", - "scipy==1.16.2 ; python_version >= '3.11'", -] -requires-python = ">=3.9" +version = "2.1.0" +requires-python = ">=3.10" authors = [{ name = "Brett Tolbert", email = "bretttolbert@gmail.com" }] maintainers = [{ name = "Brett Tolbert", email = "bretttolbert@gmail.com" }] description = "Verbs Completely Conjugated: machine learning conjugator for Catalan, French, Italian, Portuguese, Romanian and Spanish" @@ -71,7 +53,6 @@ classifiers = [ 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: 3.13', 'Programming Language :: Python :: 3.14', - 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3', 'Topic :: Education', 'Topic :: Scientific/Engineering :: Artificial Intelligence', @@ -80,6 +61,29 @@ classifiers = [ 'Topic :: Utilities', ] +dependencies = [ + "backports.strenum>=1.3.1,<2.0 ; python_version < '3.11'", + "cython>=3.0.11", + "dataclass-wizard[yaml]>=0.39.1", + "importlib_resources>=6.4.5 ; python_version < '3.13'", + "jsbeautifier>=1.15.4,<2.0", + "lxml-stubs>=0.5.1", + "lxml>=6.0.2", + "numpy>=1.26.4 ; python_version < '3.10'", + "numpy>=2.0.2 ; python_version >= '3.10'", + "pyaml>=25.7.0", + "scikit-learn>=1.6.1 ; python_version < '3.11'", + "scikit-learn>=1.7.2 ; python_version >= '3.11'", + "scipy>=1.13.1 ; python_version < '3.10'", + "scipy>=1.15.3 ; python_version >= '3.10'", +] + +[project.optional-dependencies] +dev = [ + "ruff>=0.1.0", + "pytest>=9.1.1", +] + [project.urls] Homepage = "https://github.com/bretttolbert/verbecc" Documentation = "https://github.com/bretttolbert/verbecc" @@ -88,13 +92,22 @@ Repository = "https://github.com/bretttolbert/verbecc.git" Changelog = "https://github.com/bretttolbert/verbecc/blob/master/CHANGELOG.md" [tool.setuptools.package-data] -trained_models = ['verbecc/data/models/*'] +verbecc = [ + "py.typed", + "config/*.yaml", + "data/xml/conjugations/*.xml", + "data/xml/verbs/*.xml", + "data/models/*", + "resources/icons/*.png", + "resources/icons/*.svg", + "resources/fonts/*.ttf", +] -[tool.setuptools.packages] -find = {} +[tool.setuptools.packages.find] +where = ["src"] # <-- This tells setuptools to scan inside src/ [project.scripts] -train-verb-models = 'verbecc.src.utils.utils:train_models' +train-verb-models = 'verbecc.core.utils.utils:train_models' [tool.pytest.ini_options] pythonpath = [ @@ -124,4 +137,24 @@ addopts = [ "--ignore=verbecc/data", "--ignore=data", "--ignore=doc" -] \ No newline at end of file +] + +[tool.ruff] +# Set the maximum line length to match your GitHub Actions setup +line-length = 127 + +[tool.ruff.lint] +# Enable Flake8 syntax/error equivalents (E9, F63, F7, F82) +# "E" and "F" enable pycodestyle errors and Pyflakes +select = ["E9", "F63", "F7", "F82"] + +# Enable McCabe complexity checking (equivalent to flake8 --max-complexity=10) +[tool.ruff.lint.mccabe] +max-complexity = 10 + +[tool.mypy] +mypy_path = "typings" + +[[tool.mypy.overrides]] +module = "*.pyi" +ignore_errors = true diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 3f942f3..0000000 --- a/requirements.txt +++ /dev/null @@ -1,18 +0,0 @@ -backports.strenum>=1.3.1,<2.0;python_version<"3.11" -Cython==3.1.4 -dataclass-wizard==0.35.1 -importlib_resources>=6.4.5,<=6.5.2 -iniconfig==2.1.0 -joblib==1.4.2 -jsbeautifier==1.15.4 -lxml-stubs==0.5.1 -lxml==6.1.0 -numpy>=2.0.2,<=2.3.3 -packaging==25.0 -pluggy==1.6.0 -pyaml==25.7.0 -pytest==8.4.2;python_version<="3.9" -pytest==9.0.3;python_version>"3.9" -scikit-learn>=1.6.1,<=1.7.2 -scipy>=1.13.1,<=1.16.1 -threadpoolctl==3.6.0 \ No newline at end of file diff --git a/verbecc/src/__init__.py b/scripts/__init__.py similarity index 100% rename from verbecc/src/__init__.py rename to scripts/__init__.py diff --git a/scripts/xml_modifier.py b/scripts/xml_modifier.py index 7b3a477..376d926 100755 --- a/scripts/xml_modifier.py +++ b/scripts/xml_modifier.py @@ -14,22 +14,22 @@ """ -from typing import Optional +from typing import Optional, cast from pathlib import Path -from lxml import etree, objectify -import os -from typing import List, Sequence, Tuple -from verbecc.src.defs.types.tense import Tense, Tenses -from verbecc.src.defs.types.mood import Mood, Moods -from verbecc.src.utils.logging_utils import LoggingUtils +from typing import Sequence, Tuple -INPUT_PATH = "../verbecc/data/xml/conjugations/conjugations-es.xml" -OUTPUT_PATH = "../verbecc/data/xml/conjugations/conjugations-es.mod.xml" +from verbecc.core.defs.types.data.xml_types import XmlElement, XmlElementTree, XmlParser +from verbecc.core.defs.types.mood import Mood, Moods +from verbecc.core.defs.types.tense import Tense, Tenses +from verbecc.core.utils.logging_utils import LoggingUtils +from verbecc.core.utils.xml_utils import xml_element_remove, xml_element_repr, xml_element_to_string, xml_element_deannotate, xml_element_xpath, xml_parse -logger = LoggingUtils.get_logger(__name__) +INPUT_PATH = "src/verbecc/data/xml/conjugations/conjugations-es.xml" +OUTPUT_PATH = "src/verbecc/data/xml/conjugations/conjugations-es.mod.xml" +logger = LoggingUtils.get_logger(__name__) -def remove_tenses(root: etree._Element, tenses_to_remove: List[Tense]) -> None: +def remove_tenses(root: XmlElement, tenses_to_remove: list[Tense]) -> None: removed_elem_cnt = 0 for template_elem in root: if template_elem.tag == "template": @@ -41,7 +41,7 @@ def remove_tenses(root: etree._Element, tenses_to_remove: List[Tense]) -> None: logger.info("removed {} elements".format(removed_elem_cnt)) -def remove_mood(root: etree._Element, moods_to_remove: List[Mood]) -> None: +def remove_mood(root: XmlElement, moods_to_remove: list[Mood]) -> None: removed_elem_cnt = 0 for template_elem in root: if template_elem.tag == "template": @@ -53,11 +53,11 @@ def remove_mood(root: etree._Element, moods_to_remove: List[Mood]) -> None: def find_tense( - template_elem: etree._Element, + template_elem: XmlElement, mood: Mood, tense: Tense, should_remove_mood: bool = False, -) -> Optional[etree._Element]: +) -> Optional[XmlElement]: # find tense to move for mood_elem in template_elem: if mood_elem.tag == mood: @@ -77,7 +77,7 @@ def find_tense( def move_tense( - root: etree._Element, + root: XmlElement, tense: Tense, old_mood: Mood, new_mood: Mood, @@ -100,34 +100,26 @@ def move_tense( logger.info("moved {} elements".format(moved_elem_cnt)) -def read_input_file(path: Path) -> etree._ElementTree: - parser = etree.XMLParser(dtd_validation=False, encoding="utf-8") - tree = etree.parse(path, parser) +def read_input_file(path: Path) -> XmlElementTree: + parser = XmlParser(dtd_validation=False, encoding="utf-8") + tree = xml_parse(path, parser) return tree -def elem_tobytes(elem: etree._Element) -> bytes: - return etree.tostring( +def elem_tobytes(elem: XmlElement) -> bytes: + return xml_element_to_string( elem, encoding="utf-8", method="xml", pretty_print=True, xml_declaration=True, - ) - - -def elem_tostring(elem: etree._Element) -> str: - return elem_tobytes(elem).decode("utf-8") - + ).encode("utf-8") -def repr_elem(elem: etree._Element) -> str: - return f"{elem.tag}: {elem} {elem_tostring(elem)}" - -def write_output_file(tree: etree._ElementTree, path: Path) -> None: +def write_output_file(tree: XmlElementTree, path: Path) -> None: root = tree.getroot() with open(path, "wb") as f: - objectify.deannotate(root, cleanup_namespaces=True) + xml_element_deannotate(root) xml = elem_tobytes(root) f.write(xml) @@ -201,7 +193,7 @@ def do_romanian_mods(input_path: Path, output_path: Path) -> None: def remove_nth_element_of_every_matching_great_grandchild( - root: etree._Element, + root: XmlElement, great_grandparent_elem_tag: str, grandparent_elem_tag: str, parent_elem_tag: str, @@ -210,52 +202,53 @@ def remove_nth_element_of_every_matching_great_grandchild( dry_run: bool = True, ) -> int: """ - n = the (1-based) indice of the element to remove - - returns number of elements removed + n = the 1-based index of the element to remove. + Returns the number of elements removed. """ if n < 1: - raise ValueError("n must be nonzero") + raise ValueError("n must be greater than or equal to 1") + + # Construct XPath path leading directly to parent elements. + # Note: Using translate() handles case-insensitivity for the grandparent tag. + xpath_query = ( + f"./{great_grandparent_elem_tag}/" + f"*[translate(name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = '{grandparent_elem_tag.lower()}']/" + f"{parent_elem_tag}" + ) + cnt = 0 - for great_grandparent_elem in root: - # E.g. template - if great_grandparent_elem.tag == great_grandparent_elem_tag: - # E.g. mood - for grandparent_elem in great_grandparent_elem: - if grandparent_elem.tag.lower() == grandparent_elem_tag: - # E.g. tense - for parent_elem in grandparent_elem: - if parent_elem.tag == parent_elem_tag: - elem_n = 1 - # E.g.

elem - for elem in parent_elem: - if elem.tag == elem_to_remove_tag: - if elem_n == n: - if dry_run is False: - parent_elem.remove(elem) - logger.info( - f"Removed elem {repr_elem(elem)}" - ) - else: - logger.info( - f"Would have removed elem {repr_elem(elem)}" - ) - cnt += 1 - elem_n += 1 - qualifier = "" - if dry_run: - qualifier = "would have " + # This XPath selects elements, but lxml's type stubs model ``xpath`` as + # potentially returning scalar XPath results as well. + parents = cast(list[XmlElement], xml_element_xpath(root, xpath_query)) # type: ignore + for parent in parents: + # Filter children matching the target tag + matching_children = [ + child for child in parent if child.tag == elem_to_remove_tag + ] + + # Check if the 1-based index exists + if len(matching_children) >= n: + target_elem = matching_children[n - 1] + action_str = "Would have removed" if dry_run else "Removed" + + if not dry_run: + xml_element_remove(parent, target_elem) + + logger.info(f"{action_str} elem {xml_element_repr(target_elem)}") + cnt += 1 + + qualifier = "would have " if dry_run else "" logger.info( f"{qualifier}removed {cnt} <{elem_to_remove_tag}> elements " - + f"descendent from parent element {parent_elem_tag}, " - + f"from grandparent element {grandparent_elem_tag}, " - + f"from great-grandparent element {great_grandparent_elem_tag}, " + f"descendent from parent element {parent_elem_tag}, " + f"from grandparent element {grandparent_elem_tag}, " + f"from great-grandparent element {great_grandparent_elem_tag}." ) return cnt def remove_second_p_element_from_every_matching_template( - root: etree._Element, mood: Mood, tense: Tense, dry_run: bool = True + root: XmlElement, mood: Mood, tense: Tense, dry_run: bool = True ) -> int: return remove_nth_element_of_every_matching_great_grandchild( root, diff --git a/setup.cfg b/setup.cfg index 224a779..0f94f37 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,2 @@ [metadata] -description-file = README.md \ No newline at end of file +description_file = README.md \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100755 index fc1f76c..0000000 --- a/setup.py +++ /dev/null @@ -1,3 +0,0 @@ -from setuptools import setup - -setup() \ No newline at end of file diff --git a/src/verbecc/__init__.py b/src/verbecc/__init__.py new file mode 100644 index 0000000..e336bb0 --- /dev/null +++ b/src/verbecc/__init__.py @@ -0,0 +1 @@ +from .core import CompleteConjugator as CompleteConjugator \ No newline at end of file diff --git a/verbecc/config/logging_config.yaml b/src/verbecc/config/logging_config.yaml similarity index 100% rename from verbecc/config/logging_config.yaml rename to src/verbecc/config/logging_config.yaml diff --git a/verbecc/config/verbecc_config.yaml b/src/verbecc/config/verbecc_config.yaml similarity index 100% rename from verbecc/config/verbecc_config.yaml rename to src/verbecc/config/verbecc_config.yaml diff --git a/src/verbecc/core/__init__.py b/src/verbecc/core/__init__.py new file mode 100644 index 0000000..e11a4d3 --- /dev/null +++ b/src/verbecc/core/__init__.py @@ -0,0 +1 @@ +from .conjugator import CompleteConjugator as CompleteConjugator \ No newline at end of file diff --git a/src/verbecc/core/conjugator/__init__.py b/src/verbecc/core/conjugator/__init__.py new file mode 100644 index 0000000..e0f6e82 --- /dev/null +++ b/src/verbecc/core/conjugator/__init__.py @@ -0,0 +1 @@ +from .complete_conjugator import CompleteConjugator as CompleteConjugator \ No newline at end of file diff --git a/verbecc/src/conjugator/abstract_conjugator.py b/src/verbecc/core/conjugator/abstract_conjugator.py similarity index 67% rename from verbecc/src/conjugator/abstract_conjugator.py rename to src/verbecc/core/conjugator/abstract_conjugator.py index f4274c2..2c62ba4 100644 --- a/verbecc/src/conjugator/abstract_conjugator.py +++ b/src/verbecc/core/conjugator/abstract_conjugator.py @@ -1,21 +1,20 @@ from abc import ABC -from typing import List, Optional +from typing import Optional -from verbecc.src.conjugator.conjugation_object import ConjugationObjects -from verbecc.src.defs.types.conjugation.conjugation import Conjugation -from verbecc.src.defs.types.conjugation.tense_conjugation import TenseConjugation -from verbecc.src.defs.types.data.conjugation_template import ConjugationTemplate -from verbecc.src.defs.types.data.person_ending import PersonEnding -from verbecc.src.defs.types.data.verb import Verb -from verbecc.src.defs.types.exceptions.verb_not_found_error import VerbNotFoundError -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.defs.types.lang_specific_options import LangSpecificOptions -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.person import Person -from verbecc.src.inflectors.inflector import Inflector -from verbecc.src.inflectors.inflector_factory import InflectorFactory -from verbecc.src.utils.logging_utils import LoggingUtils +from verbecc.core.conjugator.conjugation_object import ConjugationObjects +from verbecc.core.defs.types.conjugation.conjugation import Conjugation +from verbecc.core.defs.types.conjugation.tense_conjugation import TenseConjugation +from verbecc.core.defs.types.data.conjugation_template import ConjugationTemplate +from verbecc.core.defs.types.data.person_ending import PersonEnding +from verbecc.core.defs.types.data.verb import Verb +from verbecc.core.defs.types.exceptions.verb_not_found_error import VerbNotFoundError +from verbecc.core.defs.types.gender import Gender +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.defs.types.lang_specific_options import LangSpecificOptions +from verbecc.core.defs.types.number import Number +from verbecc.core.inflectors.inflector import Inflector +from verbecc.core.inflectors.inflector_factory import InflectorFactory +from verbecc.core.utils.logging_utils import LoggingUtils class AbstractConjugator(ABC): @@ -36,7 +35,7 @@ def __init__( self._logger = LoggingUtils.get_logger(subclass_name) super().__init__() - def _get_conj_obs(self, infinitive: str) -> ConjugationObjects: + def _get_conj_objs(self, infinitive: str) -> ConjugationObjects: infinitive = infinitive.lower() is_reflexive, infinitive = self._inflector.split_reflexive(infinitive) if is_reflexive and not self._inflector.verb_can_be_reflexive(infinitive): @@ -48,16 +47,16 @@ def _get_conj_obs(self, infinitive: str) -> ConjugationObjects: ) return ConjugationObjects(infinitive, verb, template, verb_stem, is_reflexive) - def get_verbs(self) -> List[Verb]: + def get_verbs(self) -> list[Verb]: return self._inflector.get_verbs() - def get_infinitives(self) -> List[str]: + def get_infinitives(self) -> list[str]: return self._inflector.get_infinitives() - def get_templates(self) -> List[ConjugationTemplate]: + def get_templates(self) -> list[ConjugationTemplate]: return self._inflector.get_templates() - def get_template_names(self) -> List[str]: + def get_template_names(self) -> list[str]: return self._inflector.get_template_names() def find_verb_by_infinitive(self, infinitive: str) -> Verb: @@ -66,14 +65,14 @@ def find_verb_by_infinitive(self, infinitive: str) -> Verb: def find_template(self, name: str) -> ConjugationTemplate: return self._inflector.find_template(name) - def get_verbs_that_start_with(self, query: str, max_results: int) -> List[str]: + def get_verbs_that_start_with(self, query: str, max_results: int) -> list[str]: return self._inflector.get_verbs_that_start_with(query, max_results) def get_person_ending_by_gender_and_number( self, - person_endings: List[PersonEnding], - gender: Optional[Gender], - number: Optional[Number], + person_endings: list[PersonEnding], + gender: Optional[Gender | str], + number: Optional[Number | str], ) -> PersonEnding: resolved_gender = Gender.m resolved_number = Number.Singular @@ -92,8 +91,8 @@ def get_person_ending_by_gender_and_number( def get_conjugation_by_gender_and_number( self, tense_conjugation: TenseConjugation, - gender: Optional[Gender], - number: Optional[Number], + gender: Optional[Gender | str], + number: Optional[Number | str], ) -> Conjugation: resolved_gender = Gender.m resolved_number = Number.Singular @@ -111,3 +110,6 @@ def get_conjugation_by_gender_and_number( self._logger.error("Failed to get conjugation, tense_conjugation is empty") raise Exception("Failed to get conjugation, tense_conjugation is empty") return tense_conjugation[0] + + def _get_inflector(self) -> Inflector: + return self._inflector diff --git a/verbecc/src/conjugator/complete_conjugator.py b/src/verbecc/core/conjugator/complete_conjugator.py similarity index 70% rename from verbecc/src/conjugator/complete_conjugator.py rename to src/verbecc/core/conjugator/complete_conjugator.py index 2c135b4..cbdecc0 100755 --- a/verbecc/src/conjugator/complete_conjugator.py +++ b/src/verbecc/core/conjugator/complete_conjugator.py @@ -1,16 +1,17 @@ from typing import Optional -from verbecc.src.conjugator.abstract_conjugator import AbstractConjugator -from verbecc.src.conjugator.mood_conjugator import MoodConjugator -from verbecc.src.defs.types.conjugation.complete_conjugation import CompleteConjugation -from verbecc.src.defs.types.conjugation.mood_conjugation import MoodConjugation -from verbecc.src.defs.types.conjugation.moods_conjugation import MoodsConjugation -from verbecc.src.defs.types.conjugation.verb_info import VerbInfo -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.defs.types.lang_specific_options import LangSpecificOptions -from verbecc.src.defs.types.mood import Mood -from verbecc.src.defs.types.tense import Tense -from verbecc.src.defs.types.conjugation.tense_conjugation import TenseConjugation +from verbecc.core.conjugator.abstract_conjugator import AbstractConjugator +from verbecc.core.conjugator.mood_conjugator import MoodConjugator +from verbecc.core.defs.types.conjugation.complete_conjugation import CompleteConjugation +from verbecc.core.defs.types.conjugation.mood_conjugation import MoodConjugation +from verbecc.core.defs.types.conjugation.moods_conjugation import MoodsConjugation +from verbecc.core.defs.types.conjugation.verb_info import VerbInfo +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.defs.types.lang_specific_options import LangSpecificOptions +from verbecc.core.defs.types.mood import Mood +from verbecc.core.defs.types.tense import Tense +from verbecc.core.defs.types.conjugation.tense_conjugation import TenseConjugation +from verbecc.core.inflectors.inflector import Inflector class CompleteConjugator(AbstractConjugator): @@ -47,10 +48,10 @@ def conjugate( :param lang_specific_options: options specific to certain languages. :type lang_specific_options: LangSpecificOptions """ - co = self._get_conj_obs(infinitive) + co = self._get_conj_objs(infinitive) moods = MoodsConjugation() for mood, _ in co.template.mood_templates.items(): - moods[mood] = self._mood_conjugator._conjugate_mood( + moods[mood] = self._mood_conjugator._conjugate_mood( # type: ignore co, mood, conjugate_pronouns, @@ -71,7 +72,7 @@ def conjugate( def conjugate_mood( self, infinitive: str, - mood: Mood, + mood: Mood | str, conjugate_pronouns: bool = True, ) -> MoodConjugation: return self._mood_conjugator.conjugate_mood( @@ -83,8 +84,8 @@ def conjugate_mood( def conjugate_mood_tense( self, infinitive: str, - mood: Mood, - tense: Tense, + mood: Mood | str, + tense: Tense | str, conjugate_pronouns: bool = True, ) -> TenseConjugation: return self._mood_conjugator.conjugate_mood_tense( @@ -93,3 +94,6 @@ def conjugate_mood_tense( tense, conjugate_pronouns=conjugate_pronouns, ) + + def _get_inflector(self) -> Inflector: + return self._inflector diff --git a/verbecc/src/conjugator/conjugation_object.py b/src/verbecc/core/conjugator/conjugation_object.py similarity index 86% rename from verbecc/src/conjugator/conjugation_object.py rename to src/verbecc/core/conjugator/conjugation_object.py index 4d74e9b..7aee758 100644 --- a/verbecc/src/conjugator/conjugation_object.py +++ b/src/verbecc/core/conjugator/conjugation_object.py @@ -1,5 +1,5 @@ -from verbecc.src.defs.types.data.verb import Verb -from verbecc.src.defs.types.data.conjugation_template import ConjugationTemplate +from verbecc.core.defs.types.data.verb import Verb +from verbecc.core.defs.types.data.conjugation_template import ConjugationTemplate class ConjugationObjects: diff --git a/verbecc/src/conjugator/conjugator.py b/src/verbecc/core/conjugator/conjugator.py similarity index 79% rename from verbecc/src/conjugator/conjugator.py rename to src/verbecc/core/conjugator/conjugator.py index 16a4687..ed02785 100644 --- a/verbecc/src/conjugator/conjugator.py +++ b/src/verbecc/core/conjugator/conjugator.py @@ -1,19 +1,19 @@ -from typing import List, Optional +from typing import Optional, cast -from verbecc.src.conjugator.abstract_conjugator import AbstractConjugator -from verbecc.src.defs.constants import grammar_defines -from verbecc.src.defs.types.conjugation.conjugation import Conjugation -from verbecc.src.defs.types.data.person_ending import PersonEnding -from verbecc.src.defs.types.data.tense_template import TenseTemplate -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.defs.types.lang_specific_options import LangSpecificOptions -from verbecc.src.defs.types.mood import Mood -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.tense import Tense -from verbecc.src.inflectors.inflector import Inflector -from verbecc.src.defs.types.pronoun import Pronoun +from verbecc.core.conjugator.abstract_conjugator import AbstractConjugator +from verbecc.core.defs.constants import grammar_defines +from verbecc.core.defs.types.conjugation.conjugation import Conjugation +from verbecc.core.defs.types.data.person_ending import PersonEnding +from verbecc.core.defs.types.data.tense_template import TenseTemplate +from verbecc.core.defs.types.gender import Gender +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.defs.types.lang_specific_options import LangSpecificOptions +from verbecc.core.defs.types.mood import Mood +from verbecc.core.defs.types.number import Number +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.tense import Tense +from verbecc.core.inflectors.inflector import Inflector +from verbecc.core.defs.types.pronoun import Pronoun class Conjugator(AbstractConjugator): @@ -42,17 +42,23 @@ def __init__( def conjugate( self, verb_stem: str, - mood: Mood, - tense: Tense, + mood: Mood | str, + tense: Tense | str, tense_template: TenseTemplate, - person: Optional[Person], + person: Optional[Person | str], person_ending: PersonEnding, - number: Optional[Number], - pronoun: Optional[Pronoun], + number: Optional[Number | str], + pronoun: Optional[Pronoun | str], is_reflexive: bool = False, - primary_tense: Optional[Tense] = None, + primary_tense: Optional[Tense | str] = None, tense_conjugated_with_pronoun: bool = True, ) -> Conjugation: + mood = cast(Mood, mood) + tense = cast(Tense, tense) + person = cast(Optional[Person], person) + number = cast(Optional[Number], number) + pronoun = cast(Optional[Pronoun], pronoun) + primary_tense = cast(Optional[Tense], primary_tense) # here's where the voseo magic happens person_ending = self._inflector.modify_person_ending_if_applicable( person_ending, mood, tense, tense_template, pronoun @@ -77,7 +83,7 @@ def conjugate( pronoun_str = self._inflector.make_pronoun_reflexive(pronoun) # get endings i.e. primary and optional alternate(s) - endings: List[str] = [] + endings: list[str] = [] endings.extend(person_ending.get_endings()) if len(endings) == 0: diff --git a/verbecc/src/conjugator/mood_conjugator.py b/src/verbecc/core/conjugator/mood_conjugator.py similarity index 64% rename from verbecc/src/conjugator/mood_conjugator.py rename to src/verbecc/core/conjugator/mood_conjugator.py index 34e263a..47e60af 100644 --- a/verbecc/src/conjugator/mood_conjugator.py +++ b/src/verbecc/core/conjugator/mood_conjugator.py @@ -1,17 +1,17 @@ -from typing import Optional +from typing import Optional, cast -from verbecc.src.conjugator.abstract_conjugator import AbstractConjugator -from verbecc.src.conjugator.conjugation_object import ConjugationObjects -from verbecc.src.conjugator.tense_conjugator import TenseConjugator -from verbecc.src.defs.types.conjugation.mood_conjugation import MoodConjugation -from verbecc.src.defs.types.conjugation.mood_conjugation_util import MoodConjugationUtil -from verbecc.src.defs.types.conjugation.tense_conjugation import TenseConjugation -from verbecc.src.defs.types.exceptions import InvalidMoodError -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.defs.types.lang_specific_options import LangSpecificOptions -from verbecc.src.defs.types.mood import Mood -from verbecc.src.defs.types.tense import Tense -from verbecc.src.inflectors.inflector import Inflector +from verbecc.core.conjugator.abstract_conjugator import AbstractConjugator +from verbecc.core.conjugator.conjugation_object import ConjugationObjects +from verbecc.core.conjugator.tense_conjugator import TenseConjugator +from verbecc.core.defs.types.conjugation.mood_conjugation import MoodConjugation +from verbecc.core.defs.types.conjugation.mood_conjugation_util import MoodConjugationUtil +from verbecc.core.defs.types.conjugation.tense_conjugation import TenseConjugation +from verbecc.core.defs.types.exceptions import InvalidMoodError +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.defs.types.lang_specific_options import LangSpecificOptions +from verbecc.core.defs.types.mood import Mood +from verbecc.core.defs.types.tense import Tense +from verbecc.core.inflectors.inflector import Inflector class MoodConjugator(AbstractConjugator): @@ -31,17 +31,17 @@ def __init__( def conjugate_mood( self, infinitive: str, - mood: Mood, + mood: Mood | str, conjugate_pronouns: bool = True, ) -> MoodConjugation: - co = self._get_conj_obs(infinitive) + co = self._get_conj_objs(infinitive) return self._conjugate_mood(co, mood, conjugate_pronouns) def conjugate_mood_tense( self, infinitive: str, - mood: Mood, - tense: Tense, + mood: Mood | str, + tense: Tense | str, conjugate_pronouns: bool = True, ) -> TenseConjugation: return self._tense_conjugator.conjugate_mood_tense( @@ -54,7 +54,7 @@ def conjugate_mood_tense( def _conjugate_mood( self, co: ConjugationObjects, - mood: Mood, + mood: Mood | str, conjugate_pronouns: bool = True, ) -> MoodConjugation: if mood not in co.template.mood_templates.keys(): @@ -77,13 +77,13 @@ def _conjugate_mood( def _get_simple_conjugations_for_mood( self, co: ConjugationObjects, - mood: Mood, + mood: Mood | str, conjugate_pronouns: bool = True, ) -> MoodConjugation: - ret = MoodConjugation(mood) - mood_template = co.template.mood_templates[mood] + ret = MoodConjugation(cast(Mood, mood)) + mood_template = co.template.mood_templates[cast(Mood, mood)] for tense in mood_template.tense_templates: - ret[tense] = self._tense_conjugator._conjugate_mood_tense( + ret[tense] = self._tense_conjugator._conjugate_mood_tense( # type: ignore co, mood, tense, @@ -94,14 +94,14 @@ def _get_simple_conjugations_for_mood( def _get_compound_conjugations_for_mood( self, co: ConjugationObjects, - mood: Mood, + mood: Mood | str, conjugate_pronouns: bool = True, ) -> MoodConjugation: - ret = MoodConjugation(mood) + ret = MoodConjugation(cast(Mood, mood)) comp_conj_map = self._inflector.get_compound_conjugations_aux_verb_map() - if mood in comp_conj_map: - for tense in comp_conj_map[mood]: - ret[tense] = self._tense_conjugator._conjugate_mood_tense( + if cast(Mood, mood) in comp_conj_map: + for tense in comp_conj_map[cast(Mood, mood)]: + ret[tense] = self._tense_conjugator._conjugate_mood_tense( # type: ignore co, mood, tense, diff --git a/verbecc/src/conjugator/tense_conjugator.py b/src/verbecc/core/conjugator/tense_conjugator.py similarity index 54% rename from verbecc/src/conjugator/tense_conjugator.py rename to src/verbecc/core/conjugator/tense_conjugator.py index b5ebfd3..1ea010b 100644 --- a/verbecc/src/conjugator/tense_conjugator.py +++ b/src/verbecc/core/conjugator/tense_conjugator.py @@ -1,16 +1,16 @@ -from typing import Optional +from typing import Optional, cast -from verbecc.src.conjugator.conjugation_object import ConjugationObjects -from verbecc.src.defs.types.mood import Mood -from verbecc.src.defs.types.tense import Tense -from verbecc.src.defs.types.exceptions import InvalidTenseError -from verbecc.src.defs.types.conjugation.tense_conjugation import TenseConjugation -from verbecc.src.defs.types.lang_specific_options import LangSpecificOptions -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.inflectors.inflector import Inflector -from verbecc.src.conjugator.abstract_conjugator import AbstractConjugator -from verbecc.src.conjugator.tense_conjugator_simple import TenseConjugatorSimple -from verbecc.src.conjugator.tense_conjugator_compound import TenseConjugatorCompound +from verbecc.core.conjugator.conjugation_object import ConjugationObjects +from verbecc.core.defs.types.mood import Mood +from verbecc.core.defs.types.tense import Tense +from verbecc.core.defs.types.exceptions import InvalidTenseError +from verbecc.core.defs.types.conjugation.tense_conjugation import TenseConjugation +from verbecc.core.defs.types.lang_specific_options import LangSpecificOptions +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.inflectors.inflector import Inflector +from verbecc.core.conjugator.abstract_conjugator import AbstractConjugator +from verbecc.core.conjugator.tense_conjugator_simple import TenseConjugatorSimple +from verbecc.core.conjugator.tense_conjugator_compound import TenseConjugatorCompound class TenseConjugator(AbstractConjugator): @@ -39,11 +39,11 @@ def __init__( def conjugate_mood_tense( self, infinitive: str, - mood: Mood, - tense: Tense, + mood: Mood | str, + tense: Tense | str, conjugate_pronouns: bool = True, ) -> TenseConjugation: - co = self._get_conj_obs(infinitive) + co = self._get_conj_objs(infinitive) return self._conjugate_mood_tense( co, mood, @@ -54,34 +54,36 @@ def conjugate_mood_tense( def _conjugate_mood_tense( self, co: ConjugationObjects, - mood: Mood, - tense: Tense, + mood: Mood | str, + tense: Tense | str, conjugate_pronouns: bool = True, ) -> TenseConjugation: comp_conj_map = self._inflector.get_compound_conjugations_aux_verb_map() - if mood in comp_conj_map and tense in comp_conj_map[mood]: - aux_mood, aux_tense = comp_conj_map[mood][tense] + mood_key = cast(Mood, mood) + tense_key = cast(Tense, tense) + if mood_key in comp_conj_map and tense_key in comp_conj_map[mood_key]: + aux_mood, aux_tense = comp_conj_map[mood_key][tense_key] aux_uses_alternate = ( - self._inflector.auxiliary_verb_uses_alternate_conjugation(tense) + self._inflector.auxiliary_verb_uses_alternate_conjugation(tense_key) ) - return self._tense_conjugator_compound._conjugate_compound_mood_tense( + return self._tense_conjugator_compound._conjugate_compound_mood_tense( # type: ignore co, - mood, - tense, + mood_key, + tense_key, aux_mood, aux_tense, aux_uses_alternate, conjugate_pronouns=conjugate_pronouns, ) else: - mood_template = co.template.mood_templates[mood] - if tense not in mood_template.tense_templates: + mood_template = co.template.mood_templates[mood_key] + if tense_key not in mood_template.tense_templates: raise InvalidTenseError() - tense_template = mood_template.tense_templates[tense] - return self._tense_conjugator_simple._conjugate_simple_mood_tense( + tense_template = mood_template.tense_templates[tense_key] + return self._tense_conjugator_simple.conjugate_simple_mood_tense( co.verb_stem, - mood, - tense, + mood_key, + tense_key, tense_template, is_reflexive=co.is_reflexive, modify_stem_strip_accents=bool( @@ -89,3 +91,9 @@ def _conjugate_mood_tense( ), conjugate_pronouns=conjugate_pronouns, ) + + def _get_tense_conjugator_simple(self) -> TenseConjugatorSimple: + return self._tense_conjugator_simple + + def _get_tense_conjugator_compound(self) -> TenseConjugatorCompound: + return self._tense_conjugator_compound diff --git a/verbecc/src/conjugator/tense_conjugator_compound.py b/src/verbecc/core/conjugator/tense_conjugator_compound.py similarity index 90% rename from verbecc/src/conjugator/tense_conjugator_compound.py rename to src/verbecc/core/conjugator/tense_conjugator_compound.py index 2c519ec..8fc2d97 100644 --- a/verbecc/src/conjugator/tense_conjugator_compound.py +++ b/src/verbecc/core/conjugator/tense_conjugator_compound.py @@ -1,20 +1,20 @@ import copy -from typing import List, Optional +from typing import Optional -from verbecc.src.conjugator.conjugation_object import ConjugationObjects -from verbecc.src.defs.constants import grammar_defines -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.mood import Mood -from verbecc.src.defs.types.tense import Tense -from verbecc.src.defs.types.conjugation.tense_conjugation import TenseConjugation -from verbecc.src.defs.types.conjugation.conjugation import Conjugation -from verbecc.src.defs.types.lang_specific_options import LangSpecificOptions -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.inflectors.inflector import Inflector -from verbecc.src.conjugator.abstract_conjugator import AbstractConjugator -from verbecc.src.conjugator.tense_conjugator_simple import TenseConjugatorSimple +from verbecc.core.conjugator.conjugation_object import ConjugationObjects +from verbecc.core.defs.constants import grammar_defines +from verbecc.core.defs.types.data.person_ending import PersonEnding +from verbecc.core.defs.types.gender import Gender +from verbecc.core.defs.types.number import Number +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.mood import Mood +from verbecc.core.defs.types.tense import Tense +from verbecc.core.defs.types.conjugation.tense_conjugation import TenseConjugation +from verbecc.core.defs.types.conjugation.conjugation import Conjugation +from verbecc.core.defs.types.lang_specific_options import LangSpecificOptions +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.conjugator.abstract_conjugator import AbstractConjugator +from verbecc.core.conjugator.tense_conjugator_simple import TenseConjugatorSimple class TenseConjugatorCompound(AbstractConjugator): @@ -58,18 +58,18 @@ def _conjugate_compound_mood_tense( .person_endings ) aux_verb = self._inflector.get_auxiliary_verb(co, mood, tense) - aux_co = self._get_conj_obs(aux_verb) + aux_co = self._get_conj_objs(aux_verb) aux_tense_template = copy.deepcopy( aux_co.template.mood_templates[aux_mood].tense_templates[aux_tense] ) - aux_person_endings = [] + aux_person_endings: list[PersonEnding] = [] for aux_pe in aux_tense_template.person_endings: if aux_pe.person is not None and aux_pe.person in [ pe.person for pe in person_endings if pe.person is not None ]: aux_person_endings.append(aux_pe) aux_tense_template.person_endings = aux_person_endings - aux_conj = self._tense_conjugator_simple._conjugate_simple_mood_tense( + aux_conj = self._tense_conjugator_simple.conjugate_simple_mood_tense( aux_co.verb_stem, aux_mood, # todo: investigate (used to be "") aux_tense, @@ -133,7 +133,7 @@ def _conjugate_compound_primary_verb( ils/elles sont allé(e)s """ ret = TenseConjugation(tense) - aux_conj_scalar: List[str] = [] + aux_conj_scalar: list[str] = [] for pc in aux_conj: conjugations = pc.get_conjugations() if aux_uses_alternate and len(conjugations) > 1: @@ -151,7 +151,7 @@ def _conjugate_compound_primary_verb( ): p_conj.append(Conjugation(None, None, None, None, [co.infinitive])) else: - p_conj = self._tense_conjugator_simple._conjugate_simple_mood_tense( + p_conj = self._tense_conjugator_simple.conjugate_simple_mood_tense( co.verb_stem, p_mood, p_tense, @@ -282,18 +282,13 @@ def _conjugate_compound_primary_verb( for i, hv in enumerate(aux_conj_scalar): aux_pc = aux_conj[i] # e.g. "suis" # need to get person_ending, e.g. "allé", based on gender and number of aux_pc - gender = Gender.m number = Number.Singular - aux_pc_gender = aux_pc.get_gender() - if aux_pc_gender is not None: - gender = aux_pc_gender - aux_pc_number = aux_pc.get_number() if aux_pc_number is not None: number = aux_pc_number - genders = [] + genders: list[Gender | None] = [] if ( aux_pc.get_person() == Person.Third and aux_pc.get_gender() is not None @@ -325,7 +320,7 @@ def _conjugate_compound_primary_verb( def _conjugate_compound_participle_inflected( self, - gender: Gender, + gender: Gender | None, number: Number, aux_pc: Conjugation, p_conj: TenseConjugation, diff --git a/verbecc/src/conjugator/tense_conjugator_simple.py b/src/verbecc/core/conjugator/tense_conjugator_simple.py similarity index 83% rename from verbecc/src/conjugator/tense_conjugator_simple.py rename to src/verbecc/core/conjugator/tense_conjugator_simple.py index 8b25f58..1395921 100644 --- a/verbecc/src/conjugator/tense_conjugator_simple.py +++ b/src/verbecc/core/conjugator/tense_conjugator_simple.py @@ -1,15 +1,15 @@ -from typing import Optional +from typing import Optional, cast -from verbecc.src.defs.types.mood import Mood -from verbecc.src.defs.types.tense import Tense -from verbecc.src.defs.types.conjugation.tense_conjugation import TenseConjugation -from verbecc.src.defs.types.lang_specific_options import LangSpecificOptions -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.defs.types.data.tense_template import TenseTemplate -from verbecc.src.inflectors.inflector import Inflector -from verbecc.src.utils.string_utils import strip_accents -from verbecc.src.conjugator.abstract_conjugator import AbstractConjugator -from verbecc.src.conjugator.conjugator import Conjugator +from verbecc.core.defs.types.mood import Mood +from verbecc.core.defs.types.tense import Tense +from verbecc.core.defs.types.conjugation.tense_conjugation import TenseConjugation +from verbecc.core.defs.types.lang_specific_options import LangSpecificOptions +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.defs.types.data.tense_template import TenseTemplate +from verbecc.core.inflectors.inflector import Inflector +from verbecc.core.utils.string_utils import strip_accents +from verbecc.core.conjugator.abstract_conjugator import AbstractConjugator +from verbecc.core.conjugator.conjugator import Conjugator class TenseConjugatorSimple(AbstractConjugator): @@ -25,7 +25,7 @@ def __init__( ) self._conjugator = Conjugator(lang, lang_specific_options, self._inflector) - def _conjugate_simple_mood_tense( + def conjugate_simple_mood_tense( self, verb_stem: str, mood: Mood, @@ -47,7 +47,7 @@ def _conjugate_simple_mood_tense( if modify_stem_strip_accents and mood != self._inflector.get_infinitive_mood(): verb_stem = strip_accents(verb_stem) ret = TenseConjugation(tense) - tense = tense_template.tense + tense = cast(Tense, tense_template.tense) tense_conjugated_with_pronoun = True if ( tense in self._inflector.get_tenses_conjugated_without_pronouns() diff --git a/verbecc/src/conjugator/__init__.py b/src/verbecc/core/defs/__init__.py similarity index 100% rename from verbecc/src/conjugator/__init__.py rename to src/verbecc/core/defs/__init__.py diff --git a/verbecc/src/defs/__init__.py b/src/verbecc/core/defs/constants/__init__.py similarity index 100% rename from verbecc/src/defs/__init__.py rename to src/verbecc/core/defs/constants/__init__.py diff --git a/verbecc/src/defs/constants/grammar_defines.py b/src/verbecc/core/defs/constants/grammar_defines.py similarity index 90% rename from verbecc/src/defs/constants/grammar_defines.py rename to src/verbecc/core/defs/constants/grammar_defines.py index 175fed6..9a3760b 100755 --- a/verbecc/src/defs/constants/grammar_defines.py +++ b/src/verbecc/core/defs/constants/grammar_defines.py @@ -1,22 +1,15 @@ -import sys +from typing import Tuple -if sys.version_info >= (3, 11): - from enum import StrEnum -else: - from backports.strenum import StrEnum - -from typing import Dict, List, Tuple - -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.defs.types.participle_inflection import ParticipleInflection -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.number import Number +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.defs.types.participle_inflection import ParticipleInflection +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.number import Number # placeholder used in instances where no value is available NO_VALUE = "-" # map of ISO 639-1 codes to long names (in target language) -SUPPORTED_LANGUAGES: Dict[LangCodeISO639_1, str] = { +SUPPORTED_LANGUAGES: dict[LangCodeISO639_1, str] = { LangCodeISO639_1.ca: "català", LangCodeISO639_1.es: "español", LangCodeISO639_1.fr: "français", @@ -82,7 +75,7 @@ (Person.Second, Number.Plural), ) -IMPERATIVE_PERSONS: Dict[LangCodeISO639_1, tuple[Tuple[Person, Number], ...]] = { +IMPERATIVE_PERSONS: dict[LangCodeISO639_1, tuple[Tuple[Person, Number], ...]] = { LangCodeISO639_1.fr: IMPERATIVE_PERSONS_FR, LangCodeISO639_1.es: IMPERATIVE_PERSONS_ES, LangCodeISO639_1.ca: IMPERATIVE_PERSONS_CA, @@ -115,7 +108,7 @@ ParticipleInflection.FemininePlural, ) -PARTICIPLE_INFLECTIONS: Dict[ +PARTICIPLE_INFLECTIONS: dict[ LangCodeISO639_1, tuple[ParticipleInflection, ...], ] = { diff --git a/verbecc/src/defs/constants/localization.py b/src/verbecc/core/defs/constants/localization.py similarity index 87% rename from verbecc/src/defs/constants/localization.py rename to src/verbecc/core/defs/constants/localization.py index ac198da..ee0ed4e 100644 --- a/verbecc/src/defs/constants/localization.py +++ b/src/verbecc/core/defs/constants/localization.py @@ -1,10 +1,10 @@ -from typing import Dict +from typing import cast -from verbecc.src.defs.types.mood import Mood, Moods -from verbecc.src.defs.types.tense import Tense, TenseEn, Tenses -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.defs.types.mood import Mood, Moods +from verbecc.core.defs.types.tense import Tense, Tenses +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 -MOOD_MAP: Dict[Mood, Dict[LangCodeISO639_1, Mood]] = { +MOOD_MAP: dict[Mood, dict[LangCodeISO639_1, Mood]] = { Moods.en.Indicative: { LangCodeISO639_1.ca: Moods.ca.Indicatiu, LangCodeISO639_1.es: Moods.es.Indicativo, @@ -35,7 +35,8 @@ LangCodeISO639_1.fr: Moods.fr.Conditionnel, LangCodeISO639_1.it: Moods.it.Condizionale, LangCodeISO639_1.pt: Moods.pt.Condicional, - LangCodeISO639_1.ro: Moods.ro.NA, # The conditional mood is formed by combining the conditional particle "ar" with the infinitive form of the verb + LangCodeISO639_1.ro: Moods.ro.NA, # The conditional mood is formed by + # combining the conditional particle "ar" with the infinitive form of the verb }, Moods.en.Infinitive: { LangCodeISO639_1.ca: Moods.ca.Infinitiu, @@ -67,7 +68,7 @@ }, } -TENSE_MAP: Dict[Tense, Dict[LangCodeISO639_1, Tense]] = { +TENSE_MAP: dict[Tense, dict[LangCodeISO639_1, Tense]] = { Tenses.en.Present: { LangCodeISO639_1.ca: Tenses.ca.Present, LangCodeISO639_1.es: Tenses.es.Presente, @@ -87,7 +88,8 @@ LangCodeISO639_1.ca: Tenses.ca.PassatSimple, LangCodeISO639_1.es: Tenses.es.PretéritoPerfectoSimple, LangCodeISO639_1.fr: Tenses.fr.PasséSimple, - LangCodeISO639_1.it: Tenses.it.PassatoRemoto, # The passato prossimo (compound tense) is roughly equivalent to the present perfect and past simple tenses + LangCodeISO639_1.it: Tenses.it.PassatoRemoto, # The passato prossimo + # (compound tense) is roughly equivalent to the present perfect and past simple tenses # however passato-remoto is the literal simple past tense e.g. 'io fui, tu fosti, lui fu, noi fummo', LangCodeISO639_1.pt: Tenses.pt.PretéritoPerfeito, LangCodeISO639_1.ro: Tenses.ro.PerfectSimplu, @@ -119,17 +121,17 @@ } -def xmood(lang: LangCodeISO639_1, m: Mood) -> Mood: +def xmood(lang: LangCodeISO639_1 | str, m: Mood | str) -> Mood: """Takes a mood name in EN and translates it to the specified language :raises: Exception if mood or lang doesn't exist """ - return MOOD_MAP[m][lang] + return MOOD_MAP[cast(Mood, m)][cast(LangCodeISO639_1, lang)] -def xtense(lang: LangCodeISO639_1, t: Tense) -> Tense: +def xtense(lang: LangCodeISO639_1 | str, t: Tense | str) -> Tense: """Takes a tense name in EN and translates it to the specified language :raises: Exception if tense or lang doesn't exist """ - return TENSE_MAP[t][lang] + return TENSE_MAP[cast(Tense, t)][cast(LangCodeISO639_1, lang)] \ No newline at end of file diff --git a/verbecc/src/defs/constants/__init__.py b/src/verbecc/core/defs/types/__init__.py similarity index 100% rename from verbecc/src/defs/constants/__init__.py rename to src/verbecc/core/defs/types/__init__.py diff --git a/verbecc/src/defs/types/config/jsbeautifier_opts.py b/src/verbecc/core/defs/types/config/jsbeautifier_opts.py similarity index 95% rename from verbecc/src/defs/types/config/jsbeautifier_opts.py rename to src/verbecc/core/defs/types/config/jsbeautifier_opts.py index 0dd21ea..bb82299 100644 --- a/verbecc/src/defs/types/config/jsbeautifier_opts.py +++ b/src/verbecc/core/defs/types/config/jsbeautifier_opts.py @@ -1,5 +1,5 @@ from dataclasses import dataclass, field -from dataclass_wizard import YAMLWizard +from dataclass_wizard.v0 import YAMLWizard @dataclass diff --git a/verbecc/src/defs/types/config/json_opts.py b/src/verbecc/core/defs/types/config/json_opts.py similarity index 86% rename from verbecc/src/defs/types/config/json_opts.py rename to src/verbecc/core/defs/types/config/json_opts.py index 69a3247..761b6be 100644 --- a/verbecc/src/defs/types/config/json_opts.py +++ b/src/verbecc/core/defs/types/config/json_opts.py @@ -1,6 +1,6 @@ from typing import Optional from dataclasses import dataclass, field -from dataclass_wizard import YAMLWizard +from dataclass_wizard.v0 import YAMLWizard @dataclass diff --git a/verbecc/src/defs/types/__init__.py b/src/verbecc/core/defs/types/config/logging/__init__.py similarity index 100% rename from verbecc/src/defs/types/__init__.py rename to src/verbecc/core/defs/types/config/logging/__init__.py diff --git a/verbecc/src/defs/types/config/logging/file_handler_config.py b/src/verbecc/core/defs/types/config/logging/file_handler_config.py similarity index 81% rename from verbecc/src/defs/types/config/logging/file_handler_config.py rename to src/verbecc/core/defs/types/config/logging/file_handler_config.py index fc009b6..0637648 100644 --- a/verbecc/src/defs/types/config/logging/file_handler_config.py +++ b/src/verbecc/core/defs/types/config/logging/file_handler_config.py @@ -1,6 +1,6 @@ from dataclasses import dataclass, field -from verbecc.src.defs.types.config.logging.handler_config import HandlerConfig +from verbecc.core.defs.types.config.logging.handler_config import HandlerConfig @dataclass diff --git a/verbecc/src/defs/types/config/logging/formatter_config.py b/src/verbecc/core/defs/types/config/logging/formatter_config.py similarity index 100% rename from verbecc/src/defs/types/config/logging/formatter_config.py rename to src/verbecc/core/defs/types/config/logging/formatter_config.py diff --git a/verbecc/src/defs/types/config/logging/handler_config.py b/src/verbecc/core/defs/types/config/logging/handler_config.py similarity index 100% rename from verbecc/src/defs/types/config/logging/handler_config.py rename to src/verbecc/core/defs/types/config/logging/handler_config.py diff --git a/verbecc/src/defs/types/config/logging/logger_config.py b/src/verbecc/core/defs/types/config/logging/logger_config.py similarity index 67% rename from verbecc/src/defs/types/config/logging/logger_config.py rename to src/verbecc/core/defs/types/config/logging/logger_config.py index 6c7460c..562c59d 100644 --- a/verbecc/src/defs/types/config/logging/logger_config.py +++ b/src/verbecc/core/defs/types/config/logging/logger_config.py @@ -1,9 +1,8 @@ from dataclasses import dataclass, field -from typing import List @dataclass class LoggerConfig: level: str = field(default="CRITICAL") - handlers: List[str] = field(default_factory=list) + handlers: list[str] = field(default_factory=list) # type: ignore propagate: bool = field(default=True) diff --git a/src/verbecc/core/defs/types/config/logging/logging_config.py b/src/verbecc/core/defs/types/config/logging/logging_config.py new file mode 100644 index 0000000..95b68dd --- /dev/null +++ b/src/verbecc/core/defs/types/config/logging/logging_config.py @@ -0,0 +1,25 @@ +from dataclasses import dataclass, field +from typing import Optional + +from verbecc.core.defs.types.config.logging.formatter_config import FormatterConfig +from verbecc.core.defs.types.config.logging.stream_handler_config import ( + StreamHandlerConfig, +) +from verbecc.core.defs.types.config.logging.file_handler_config import FileHandlerConfig +from verbecc.core.defs.types.config.logging.handler_config import HandlerConfig +from verbecc.core.defs.types.config.logging.logger_config import LoggerConfig + + +@dataclass +class LoggingConfig: + version: int = field(default=1) + disable_existing_loggers: bool = field(default=False) + formatters: dict[str, FormatterConfig] = field(default_factory=dict[str, FormatterConfig]) + handlers: dict[str, HandlerConfig] = field( + default_factory=lambda: { + "consoleHandler": StreamHandlerConfig(), + "fileHandler": FileHandlerConfig(), + } + ) + loggers: dict[str, LoggerConfig] = field(default_factory=dict[str, LoggerConfig]) + root: Optional[LoggerConfig] = field(default_factory=LoggerConfig) diff --git a/verbecc/src/defs/types/config/logging/stream_handler_config.py b/src/verbecc/core/defs/types/config/logging/stream_handler_config.py similarity index 77% rename from verbecc/src/defs/types/config/logging/stream_handler_config.py rename to src/verbecc/core/defs/types/config/logging/stream_handler_config.py index 539edea..c11547f 100644 --- a/verbecc/src/defs/types/config/logging/stream_handler_config.py +++ b/src/verbecc/core/defs/types/config/logging/stream_handler_config.py @@ -1,6 +1,6 @@ from dataclasses import dataclass, field -from verbecc.src.defs.types.config.logging.handler_config import HandlerConfig +from verbecc.core.defs.types.config.logging.handler_config import HandlerConfig @dataclass diff --git a/verbecc/src/defs/types/config/verbecc_config.py b/src/verbecc/core/defs/types/config/verbecc_config.py similarity index 66% rename from verbecc/src/defs/types/config/verbecc_config.py rename to src/verbecc/core/defs/types/config/verbecc_config.py index d4768a7..661be5a 100644 --- a/verbecc/src/defs/types/config/verbecc_config.py +++ b/src/verbecc/core/defs/types/config/verbecc_config.py @@ -1,8 +1,8 @@ from dataclasses import dataclass, field -from dataclass_wizard import YAMLWizard +from dataclass_wizard.v0 import YAMLWizard -from verbecc.src.defs.types.config.jsbeautifier_opts import JSBeautifierOpts -from verbecc.src.defs.types.config.json_opts import JSONOpts +from verbecc.core.defs.types.config.jsbeautifier_opts import JSBeautifierOpts +from verbecc.core.defs.types.config.json_opts import JSONOpts @dataclass diff --git a/src/verbecc/core/defs/types/conjugation/__init__.py b/src/verbecc/core/defs/types/conjugation/__init__.py new file mode 100644 index 0000000..7f30b72 --- /dev/null +++ b/src/verbecc/core/defs/types/conjugation/__init__.py @@ -0,0 +1,2 @@ +from .conjugation import Conjugation # type: ignore +from .tense_conjugation import TenseConjugation # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/conjugation/abstract_conjugation.py b/src/verbecc/core/defs/types/conjugation/abstract_conjugation.py similarity index 83% rename from verbecc/src/defs/types/conjugation/abstract_conjugation.py rename to src/verbecc/core/defs/types/conjugation/abstract_conjugation.py index 846b3fe..cfa5d7b 100644 --- a/verbecc/src/defs/types/conjugation/abstract_conjugation.py +++ b/src/verbecc/core/defs/types/conjugation/abstract_conjugation.py @@ -1,17 +1,12 @@ -from dataclasses import fields -from typing import cast, Optional +from typing import Optional from abc import ABC, abstractmethod import json -import yaml -from verbecc.src.utils.dict_utils import DictUtils -from verbecc.src.defs.types.config.verbecc_config import VerbeccConfig -from verbecc.src.defs.types.config.json_opts import JSONOpts -from verbecc.src.utils.config.verbecc_config_util import VerbeccConfigUtil -from verbecc.src.utils.jsbeautifier_utils import JSBeautifier -from verbecc.src.utils.json_utils import JSONUtils -from verbecc.src.utils.yaml_utils import YAMLUtils -from verbecc.src.defs.types.conjugation.conjugation_data import ConjugationKeyPerson +from verbecc.core.utils.dict_utils import DictUtils +from verbecc.core.utils.config.verbecc_config_util import VerbeccConfigUtil +from verbecc.core.utils.json_utils import JSONUtils +from verbecc.core.utils.yaml_utils import YAMLUtils +from verbecc.core.defs.types.conjugation.conjugation_data import ConjugationKeyPerson config = VerbeccConfigUtil().load_config() @@ -26,7 +21,7 @@ def get_parent(self) -> Optional[object]: def set_parent(self, value: object) -> None: if isinstance(value, AbstractConjugation): - self._parent = cast(AbstractConjugation, value) + self._parent = value else: raise TypeError() @@ -68,7 +63,7 @@ def to_yaml(self) -> str: # around all the string values, unlike YAML) # TODO: Figure out how to do this with custom yaml.Dumper - data = DictUtils.cast_values_recursive(data, ConjugationKeyPerson, int) + data = DictUtils.cast_values_recursive(data, ConjugationKeyPerson, int) # type: ignore # TODO: If possiuble, figure out how to prevent YAML from putting quotes around the # French pronoun "on" ("on" is considered an "ambiguous value" in YAML, by default) diff --git a/verbecc/src/defs/types/conjugation/complete_conjugation.py b/src/verbecc/core/defs/types/conjugation/complete_conjugation.py similarity index 77% rename from verbecc/src/defs/types/conjugation/complete_conjugation.py rename to src/verbecc/core/defs/types/conjugation/complete_conjugation.py index 5d12195..de40dff 100644 --- a/verbecc/src/defs/types/conjugation/complete_conjugation.py +++ b/src/verbecc/core/defs/types/conjugation/complete_conjugation.py @@ -1,15 +1,14 @@ -import json from typing import Iterable -from verbecc.src.defs.types.conjugation.abstract_conjugation import AbstractConjugation -from verbecc.src.defs.types.conjugation.verb_info import VerbInfo -from verbecc.src.defs.types.mood import Mood -from verbecc.src.defs.types.conjugation.mood_conjugation import MoodConjugation -from verbecc.src.defs.types.conjugation.moods_conjugation import MoodsConjugation -from verbecc.src.defs.types.conjugation.complete_conjugation_data import ( +from verbecc.core.defs.types.conjugation.abstract_conjugation import AbstractConjugation +from verbecc.core.defs.types.conjugation.verb_info import VerbInfo +from verbecc.core.defs.types.mood import Mood +from verbecc.core.defs.types.conjugation.mood_conjugation import MoodConjugation +from verbecc.core.defs.types.conjugation.moods_conjugation import MoodsConjugation +from verbecc.core.defs.types.conjugation.complete_conjugation_data import ( CompleteConjugationData, ) -from verbecc.src.utils.config.verbecc_config_util import VerbeccConfigUtil +from verbecc.core.utils.config.verbecc_config_util import VerbeccConfigUtil config = VerbeccConfigUtil().load_config() @@ -53,7 +52,7 @@ def __len__(self) -> int: return len(self._moods_conjugation) def __iter__(self) -> Iterable[Mood]: - return iter(self._moods_conjugation._data) + return iter(self._moods_conjugation.get_data().keys()) def __contains__(self, key: Mood) -> bool: return key in self._moods_conjugation diff --git a/src/verbecc/core/defs/types/conjugation/complete_conjugation_data.py b/src/verbecc/core/defs/types/conjugation/complete_conjugation_data.py new file mode 100644 index 0000000..68d5592 --- /dev/null +++ b/src/verbecc/core/defs/types/conjugation/complete_conjugation_data.py @@ -0,0 +1,8 @@ +from typing import Union + +from verbecc.core.defs.types.conjugation.verb_info_data import VerbInfoData +from verbecc.core.defs.types.conjugation.moods_conjugation_data import ( + MoodsConjugationData, +) + +CompleteConjugationData = dict[str, Union[VerbInfoData, MoodsConjugationData]] diff --git a/verbecc/src/defs/types/conjugation/conjugation.py b/src/verbecc/core/defs/types/conjugation/conjugation.py similarity index 77% rename from verbecc/src/defs/types/conjugation/conjugation.py rename to src/verbecc/core/defs/types/conjugation/conjugation.py index 12d1c74..6739d07 100644 --- a/verbecc/src/defs/types/conjugation/conjugation.py +++ b/src/verbecc/core/defs/types/conjugation/conjugation.py @@ -1,11 +1,15 @@ -from typing import cast, Dict, Iterator, List, Optional, Union - -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.pronoun import Pronoun -from verbecc.src.defs.types.conjugation.abstract_conjugation import AbstractConjugation -from verbecc.src.defs.types.conjugation.conjugation_data import ( +from typing import cast, Iterator, Optional, overload +try: + from typing import Self # type: ignore +except ImportError: + from typing_extensions import Self # type: ignore + +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.number import Number +from verbecc.core.defs.types.gender import Gender +from verbecc.core.defs.types.pronoun import Pronoun +from verbecc.core.defs.types.conjugation.abstract_conjugation import AbstractConjugation +from verbecc.core.defs.types.conjugation.conjugation_data import ( ConjugationData, ConjugationKeyPerson, ConjugationKeyNumber, @@ -33,7 +37,7 @@ def __init__( number: Optional[Number] = None, gender: Optional[Gender] = None, pronoun: Optional[Pronoun] = None, - conjugations: Optional[List[str]] = None, + conjugations: Optional[list[str]] = None, ) -> None: """ :param person (optional): The grammatical person, i.e. first, second or third person. @@ -50,10 +54,10 @@ def __init__( conjugating the auxiliary verb to form certain compound conjugations. """ super().__init__() - self._person = person - self._number = number - self._gender = gender - self._pronoun = pronoun + self._person: Optional[Person] = person + self._number: Optional[Number] = number + self._gender: Optional[Gender] = gender + self._pronoun: Optional[Pronoun] = pronoun if conjugations is not None: self._conjugations = conjugations else: @@ -86,28 +90,30 @@ def __hash__(self) -> int: ) ) - def __getitem__(self, index: int) -> Union[object, str]: - """ - Allows accessing elements using square bracket notation. + @overload + def __getitem__(self, index: int) -> str: ... + + @overload + def __getitem__(self, index: slice) -> Self: ... # type: ignore + + def __getitem__(self, index: int | slice) -> str | Self: # type: ignore + """Allows accessing elements using square bracket notation. + Handles both integer indexing and slicing. """ - if isinstance(index, slice): - # If a slice object is provided, return a new MyCustomList - # containing the sliced portion of the internal data. - return Conjugation( - self.person, - self.number, - self.gender, - self.pronoun, - self._conjugations[index], - ) - elif isinstance(index, int): - # If an integer index is provided, return the element at that index. - # This will automatically raise IndexError if the index is out of range. - return self._conjugations[index] - else: - # Raise a TypeError for unsupported index types. - raise TypeError("Index must be an integer or a slice object.") + match index: + case slice(): + return type(self)( + self._person, + self._number, + self._gender, + self._pronoun, + self._conjugations[index], + ) + case int(): + return self._conjugations[index] + case _: + raise TypeError("Index must be an integer or a slice object.") def __len__(self) -> int: return len(self._conjugations) @@ -142,10 +148,10 @@ def get_pronoun(self) -> Optional[Pronoun]: def set_pronoun(self, value: Pronoun) -> None: self._pronoun = value - def get_conjugations(self) -> List[str]: + def get_conjugations(self) -> list[str]: return self._conjugations - def set_conjugations(self, value: List[str]) -> None: + def set_conjugations(self, value: list[str]) -> None: self._conjugations = value def get_data(self) -> ConjugationData: diff --git a/verbecc/src/defs/types/conjugation/conjugation_data.py b/src/verbecc/core/defs/types/conjugation/conjugation_data.py similarity index 83% rename from verbecc/src/defs/types/conjugation/conjugation_data.py rename to src/verbecc/core/defs/types/conjugation/conjugation_data.py index 98df514..abc1240 100644 --- a/verbecc/src/defs/types/conjugation/conjugation_data.py +++ b/src/verbecc/core/defs/types/conjugation/conjugation_data.py @@ -1,11 +1,11 @@ -from typing import Dict, List, Optional, Tuple, Union +from typing import Optional, Union -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.pronoun import Pronoun, Pronouns +from verbecc.core.defs.types.gender import Gender +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.number import Number +from verbecc.core.defs.types.pronoun import Pronoun -# ConjugationData is a dict of {"p":Person, "n":Number, "g":Gender, "pr":Pronoun, "c":List[Conjugations]} +# ConjugationData is a dict of {"p":Person, "n":Number, "g":Gender, "pr":Pronoun, "c":list[Conjugations]} # # This allows us to have one Conjugation for "tú" and another for "vos" e.g. # {"p":Person.Second, "n":Number.Singular, "g":Gender.m, "pr":"tú", "c":["tú bebes"]], @@ -52,6 +52,6 @@ Optional[Number], Optional[Gender], Optional[Pronoun], - List[str], + list[str], ] -ConjugationData = Dict[ConjugationKey, ConjugationValue] +ConjugationData = dict[ConjugationKey, ConjugationValue] diff --git a/verbecc/src/defs/types/conjugation/mood_conjugation.py b/src/verbecc/core/defs/types/conjugation/mood_conjugation.py similarity index 76% rename from verbecc/src/defs/types/conjugation/mood_conjugation.py rename to src/verbecc/core/defs/types/conjugation/mood_conjugation.py index 502419c..4e4ce50 100644 --- a/verbecc/src/defs/types/conjugation/mood_conjugation.py +++ b/src/verbecc/core/defs/types/conjugation/mood_conjugation.py @@ -1,10 +1,10 @@ -from typing import cast, Dict, Iterable, Optional +from typing import cast, Iterable, Optional -from verbecc.src.defs.types.mood import Mood -from verbecc.src.defs.types.tense import Tense -from verbecc.src.defs.types.conjugation.tense_conjugation import TenseConjugation -from verbecc.src.defs.types.conjugation.abstract_conjugation import AbstractConjugation -from verbecc.src.defs.types.conjugation.mood_conjugation_data import MoodConjugationData +from verbecc.core.defs.types.mood import Mood +from verbecc.core.defs.types.tense import Tense +from verbecc.core.defs.types.conjugation.tense_conjugation import TenseConjugation +from verbecc.core.defs.types.conjugation.abstract_conjugation import AbstractConjugation +from verbecc.core.defs.types.conjugation.mood_conjugation_data import MoodConjugationData class MoodConjugation(AbstractConjugation): @@ -15,11 +15,11 @@ class MoodConjugation(AbstractConjugation): """ def __init__( - self, mood: Mood, data: Optional[Dict[Tense, TenseConjugation]] = None + self, mood: Mood, data: Optional[dict[Tense, TenseConjugation]] = None ) -> None: super().__init__() self._mood = mood - self._data: Dict[Tense, TenseConjugation] = {} + self._data: dict[Tense, TenseConjugation] = {} if data is not None: for k, v in data.items(): v.set_parent(self) @@ -54,6 +54,9 @@ def __iter__(self) -> Iterable[Tense]: def __contains__(self, key: Tense) -> bool: return key in self._data + def get_mood(self) -> Mood: + return self._mood + def get_data(self) -> MoodConjugationData: return {t: tc.get_data() for t, tc in self._data.items()} diff --git a/src/verbecc/core/defs/types/conjugation/mood_conjugation_data.py b/src/verbecc/core/defs/types/conjugation/mood_conjugation_data.py new file mode 100644 index 0000000..38234f2 --- /dev/null +++ b/src/verbecc/core/defs/types/conjugation/mood_conjugation_data.py @@ -0,0 +1,6 @@ +from verbecc.core.defs.types.tense import Tense +from verbecc.core.defs.types.conjugation.tense_conjugation_data import ( + TenseConjugationData, +) + +MoodConjugationData = dict[Tense, TenseConjugationData] diff --git a/verbecc/src/defs/types/conjugation/mood_conjugation_util.py b/src/verbecc/core/defs/types/conjugation/mood_conjugation_util.py similarity index 52% rename from verbecc/src/defs/types/conjugation/mood_conjugation_util.py rename to src/verbecc/core/defs/types/conjugation/mood_conjugation_util.py index d713cb3..da53e72 100644 --- a/verbecc/src/defs/types/conjugation/mood_conjugation_util.py +++ b/src/verbecc/core/defs/types/conjugation/mood_conjugation_util.py @@ -1,4 +1,6 @@ -from verbecc.src.defs.types.conjugation.mood_conjugation import MoodConjugation +from verbecc.core.defs.types.conjugation.mood_conjugation import MoodConjugation +from verbecc.core.defs.types.conjugation.tense_conjugation import TenseConjugation +from verbecc.core.defs.types.tense import Tense class MoodConjugationUtil: @@ -11,9 +13,10 @@ def combine(cls, a: MoodConjugation, b: MoodConjugation) -> MoodConjugation: (MoodConjugation isn't defined until MoodConjugation is initialized). Putting it in a separate file seems preferable to casting. """ - if a._mood != b._mood: + if a.get_mood() != b.get_mood(): raise TypeError("Cannot combine MoodsConjugations with different moods") - combined = {} - for mc in (a, b): - combined.update(mc._data) - return MoodConjugation(a._mood, combined) + combined: dict[Tense, TenseConjugation] = { + tense: a[tense] for tense in a.get_data() + } + combined.update({tense: b[tense] for tense in b.get_data()}) + return MoodConjugation(a.get_mood(), combined) diff --git a/verbecc/src/defs/types/conjugation/moods_conjugation.py b/src/verbecc/core/defs/types/conjugation/moods_conjugation.py similarity index 79% rename from verbecc/src/defs/types/conjugation/moods_conjugation.py rename to src/verbecc/core/defs/types/conjugation/moods_conjugation.py index e8616d6..9ec11e4 100644 --- a/verbecc/src/defs/types/conjugation/moods_conjugation.py +++ b/src/verbecc/core/defs/types/conjugation/moods_conjugation.py @@ -1,9 +1,9 @@ -from typing import cast, Iterable, Dict, Optional +from typing import cast, Iterable, Optional -from verbecc.src.defs.types.mood import Mood -from verbecc.src.defs.types.conjugation.abstract_conjugation import AbstractConjugation -from verbecc.src.defs.types.conjugation.mood_conjugation import MoodConjugation -from verbecc.src.defs.types.conjugation.moods_conjugation_data import ( +from verbecc.core.defs.types.mood import Mood +from verbecc.core.defs.types.conjugation.abstract_conjugation import AbstractConjugation +from verbecc.core.defs.types.conjugation.mood_conjugation import MoodConjugation +from verbecc.core.defs.types.conjugation.moods_conjugation_data import ( MoodsConjugationData, ) @@ -17,10 +17,10 @@ class MoodsConjugation(AbstractConjugation): def __init__( self, - data: Optional[Dict[Mood, MoodConjugation]] = None, + data: Optional[dict[Mood, MoodConjugation]] = None, ) -> None: super().__init__() - self._data: Dict[Mood, MoodConjugation] = {} + self._data: dict[Mood, MoodConjugation] = {} if data is not None: for k, v in data.items(): v.set_parent(self) diff --git a/src/verbecc/core/defs/types/conjugation/moods_conjugation_data.py b/src/verbecc/core/defs/types/conjugation/moods_conjugation_data.py new file mode 100644 index 0000000..35eaffc --- /dev/null +++ b/src/verbecc/core/defs/types/conjugation/moods_conjugation_data.py @@ -0,0 +1,4 @@ +from verbecc.core.defs.types.mood import Mood +from verbecc.core.defs.types.conjugation.mood_conjugation_data import MoodConjugationData + +MoodsConjugationData = dict[Mood, MoodConjugationData] # the "moods" section diff --git a/verbecc/src/defs/types/conjugation/tense_conjugation.py b/src/verbecc/core/defs/types/conjugation/tense_conjugation.py similarity index 83% rename from verbecc/src/defs/types/conjugation/tense_conjugation.py rename to src/verbecc/core/defs/types/conjugation/tense_conjugation.py index 4af05aa..d63f59c 100644 --- a/verbecc/src/defs/types/conjugation/tense_conjugation.py +++ b/src/verbecc/core/defs/types/conjugation/tense_conjugation.py @@ -1,9 +1,9 @@ -from typing import cast, Iterable, Iterator, List, Optional +from typing import cast, Iterable, Iterator, Optional -from verbecc.src.defs.types.tense import Tense -from verbecc.src.defs.types.conjugation.abstract_conjugation import AbstractConjugation -from verbecc.src.defs.types.conjugation.conjugation import Conjugation -from verbecc.src.defs.types.conjugation.tense_conjugation_data import ( +from verbecc.core.defs.types.tense import Tense +from verbecc.core.defs.types.conjugation.abstract_conjugation import AbstractConjugation +from verbecc.core.defs.types.conjugation.conjugation import Conjugation +from verbecc.core.defs.types.conjugation.tense_conjugation_data import ( TenseConjugationData, ) @@ -15,10 +15,10 @@ class TenseConjugation(AbstractConjugation): including alternate conjugations (if applicable). """ - def __init__(self, tense: Tense, data: Optional[List[Conjugation]] = None) -> None: + def __init__(self, tense: Tense, data: Optional[list[Conjugation]] = None) -> None: super().__init__() self._tense = tense - self._data: List[Conjugation] = [] + self._data: list[Conjugation] = [] if data is not None: for c in data: c.set_parent(self) diff --git a/src/verbecc/core/defs/types/conjugation/tense_conjugation_data.py b/src/verbecc/core/defs/types/conjugation/tense_conjugation_data.py new file mode 100644 index 0000000..6805fb7 --- /dev/null +++ b/src/verbecc/core/defs/types/conjugation/tense_conjugation_data.py @@ -0,0 +1,3 @@ +from verbecc.core.defs.types.conjugation.conjugation_data import ConjugationData + +TenseConjugationData = list[ConjugationData] diff --git a/verbecc/src/defs/types/conjugation/verb_info.py b/src/verbecc/core/defs/types/conjugation/verb_info.py similarity index 90% rename from verbecc/src/defs/types/conjugation/verb_info.py rename to src/verbecc/core/defs/types/conjugation/verb_info.py index ee4596a..69b8ec9 100644 --- a/verbecc/src/defs/types/conjugation/verb_info.py +++ b/src/verbecc/core/defs/types/conjugation/verb_info.py @@ -1,6 +1,6 @@ -from verbecc.src.defs.types.conjugation.abstract_conjugation import AbstractConjugation -from verbecc.src.defs.types.conjugation.verb_info_data import VerbInfoData -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.defs.types.conjugation.abstract_conjugation import AbstractConjugation +from verbecc.core.defs.types.conjugation.verb_info_data import VerbInfoData +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 class VerbInfo(AbstractConjugation): @@ -63,7 +63,7 @@ def __hash__(self) -> int: ) def get_data(self) -> VerbInfoData: - ret = { + ret: VerbInfoData = { "infinitive": self.infinitive, "lang": self.lang, "predicted": self.predicted, @@ -84,5 +84,4 @@ def get_str_id(self) -> str: E.g. "fr:parler" """ # This singleton info node represents the root node, so it has no parent. - _str_id = "" return ":".join([str(self.lang), str(self.infinitive)]) diff --git a/src/verbecc/core/defs/types/conjugation/verb_info_data.py b/src/verbecc/core/defs/types/conjugation/verb_info_data.py new file mode 100644 index 0000000..2ba8223 --- /dev/null +++ b/src/verbecc/core/defs/types/conjugation/verb_info_data.py @@ -0,0 +1,3 @@ +from typing import Union + +VerbInfoData = dict[str, Union[str, bool, float]] # the "verb" section diff --git a/verbecc/src/defs/types/data/conjugation_template.py b/src/verbecc/core/defs/types/data/conjugation_template.py similarity index 58% rename from verbecc/src/defs/types/data/conjugation_template.py rename to src/verbecc/core/defs/types/data/conjugation_template.py index 122fc41..4995b57 100644 --- a/verbecc/src/defs/types/data/conjugation_template.py +++ b/src/verbecc/core/defs/types/data/conjugation_template.py @@ -1,9 +1,7 @@ -from typing import Dict - -from verbecc.src.defs.types.data.element import Element -from verbecc.src.defs.types.data.mood_template import MoodTemplate -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang -from verbecc.src.defs.types.mood import Mood +from verbecc.core.defs.types.data.element import Element +from verbecc.core.defs.types.data.mood_template import MoodTemplate +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.defs.types.mood import Mood class ConjugationTemplate(Element): @@ -11,7 +9,7 @@ def __init__( self, lang: Lang, name: str, - mood_templates: Dict[Mood, MoodTemplate], + mood_templates: dict[Mood, MoodTemplate], modify_stem: str = "", ) -> None: self.lang = lang diff --git a/verbecc/src/defs/types/data/conjugations.py b/src/verbecc/core/defs/types/data/conjugations.py similarity index 63% rename from verbecc/src/defs/types/data/conjugations.py rename to src/verbecc/core/defs/types/data/conjugations.py index e9ceb82..e0732df 100644 --- a/verbecc/src/defs/types/data/conjugations.py +++ b/src/verbecc/core/defs/types/data/conjugations.py @@ -1,18 +1,18 @@ from bisect import bisect_left -from typing import List, Iterator +from typing import Iterator -from verbecc.src.defs.types.data.element import Element -from verbecc.src.defs.types.data.conjugation_template import ConjugationTemplate -from verbecc.src.defs.types.exceptions import TemplateNotFoundError -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.defs.types.data.element import Element +from verbecc.core.defs.types.data.conjugation_template import ConjugationTemplate +from verbecc.core.defs.types.exceptions import TemplateNotFoundError +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 class Conjugations(Element): def __init__( - self, lang: LangCodeISO639_1, templates: List[ConjugationTemplate] + self, lang: LangCodeISO639_1, templates: list[ConjugationTemplate] ) -> None: self.lang = lang - self._templates: List[ConjugationTemplate] = templates + self._templates: list[ConjugationTemplate] = templates self._keys = [template.name for template in self._templates] def __len__(self) -> int: diff --git a/verbecc/src/defs/types/data/element.py b/src/verbecc/core/defs/types/data/element.py similarity index 100% rename from verbecc/src/defs/types/data/element.py rename to src/verbecc/core/defs/types/data/element.py diff --git a/src/verbecc/core/defs/types/data/mood_template.py b/src/verbecc/core/defs/types/data/mood_template.py new file mode 100644 index 0000000..e38c17d --- /dev/null +++ b/src/verbecc/core/defs/types/data/mood_template.py @@ -0,0 +1,19 @@ +from verbecc.core.defs.types.data.element import Element +from verbecc.core.defs.types.data.tense_template import TenseTemplate +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.defs.types.mood import Mood +from verbecc.core.defs.types.tense import Tense + + +class MoodTemplate(Element): + def __init__( + self, lang: Lang, mood: Mood, tense_templates: dict[Tense, TenseTemplate] + ) -> None: + self.lang = lang + self.mood = mood + self.tense_templates = tense_templates + + def __repr__(self) -> str: + return ( + f"lang={self.lang} mood={self.mood} tense_templates={self.tense_templates}" + ) diff --git a/verbecc/src/defs/types/data/person_ending.py b/src/verbecc/core/defs/types/data/person_ending.py similarity index 72% rename from verbecc/src/defs/types/data/person_ending.py rename to src/verbecc/core/defs/types/data/person_ending.py index 8b0942b..81a0168 100644 --- a/verbecc/src/defs/types/data/person_ending.py +++ b/src/verbecc/core/defs/types/data/person_ending.py @@ -1,9 +1,9 @@ -from typing import List, Optional +from typing import Optional, cast -from verbecc.src.defs.types.data.element import Element -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.gender import Gender +from verbecc.core.defs.types.data.element import Element +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.number import Number +from verbecc.core.defs.types.gender import Gender class PersonEnding(Element): @@ -29,14 +29,14 @@ class PersonEnding(Element): def __init__( self, - person: Optional[Person], - number: Optional[Number], - gender: Optional[Gender], - endings: List[str], + person: Optional[Person | str], + number: Optional[Number | str], + gender: Optional[Gender | str], + endings: list[str], ) -> None: - self.person = person - self.number = number - self.gender = gender + self.person = cast(Optional[Person], person) + self.number = cast(Optional[Number], number) + self.gender = cast(Optional[Gender], gender) self.endings = endings def get_person(self) -> Optional[Person]: @@ -48,7 +48,7 @@ def get_number(self) -> Optional[Number]: def get_gender(self) -> Optional[Gender]: return self.gender - def get_endings(self) -> List[str]: + def get_endings(self) -> list[str]: return self.endings def get_ending(self) -> str: diff --git a/verbecc/src/defs/types/data/tense_template.py b/src/verbecc/core/defs/types/data/tense_template.py similarity index 51% rename from verbecc/src/defs/types/data/tense_template.py rename to src/verbecc/core/defs/types/data/tense_template.py index d2fb393..1740fdb 100644 --- a/verbecc/src/defs/types/data/tense_template.py +++ b/src/verbecc/core/defs/types/data/tense_template.py @@ -1,12 +1,10 @@ -from typing import List - -from verbecc.src.defs.types.data.element import Element -from verbecc.src.defs.types.data.person_ending import PersonEnding -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang -from verbecc.src.defs.types.mood import Mood -from verbecc.src.defs.types.tense import Tense -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.number import Number +from verbecc.core.defs.types.data.element import Element +from verbecc.core.defs.types.data.person_ending import PersonEnding +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.defs.types.mood import Mood +from verbecc.core.defs.types.tense import Tense +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.number import Number class TenseTemplate(Element): @@ -14,16 +12,16 @@ class TenseTemplate(Element): def __init__( self, lang: Lang, - mood: Mood, - tense: Tense, - person_endings: List[PersonEnding], + mood: Mood | str, + tense: Tense | str, + person_endings: list[PersonEnding], ) -> None: self.lang = lang self.mood = mood self.tense = tense self.person_endings = person_endings - def get_person_ending(self, person: Person, number: Number) -> PersonEnding: + def get_person_ending(self, person: Person | str, number: Number | str) -> PersonEnding: for pe in self.person_endings: if pe.person == person and pe.number == number: return pe diff --git a/verbecc/src/defs/types/data/verb.py b/src/verbecc/core/defs/types/data/verb.py similarity index 69% rename from verbecc/src/defs/types/data/verb.py rename to src/verbecc/core/defs/types/data/verb.py index c762a88..4c0269f 100644 --- a/verbecc/src/defs/types/data/verb.py +++ b/src/verbecc/core/defs/types/data/verb.py @@ -1,5 +1,5 @@ -from verbecc.src.utils.string_utils import strip_accents -from verbecc.src.defs.types.data.element import Element +from verbecc.core.utils.string_utils import strip_accents +from verbecc.core.defs.types.data.element import Element class Verb(Element): @@ -15,7 +15,10 @@ def __init__( self.impersonal = False def __repr__(self) -> str: - return "infinitive={} infinitive_no_accents={} template={} translation_en={} impersonal={} predicted={} pred_score={}".format( + return ( + "infinitive={} infinitive_no_accents={} template={} translation_en={} " + "impersonal={} predicted={} pred_score={}" + ).format( self.infinitive, self.infinitive_no_accents, self.template, diff --git a/verbecc/src/defs/types/data/verbs.py b/src/verbecc/core/defs/types/data/verbs.py similarity index 82% rename from verbecc/src/defs/types/data/verbs.py rename to src/verbecc/core/defs/types/data/verbs.py index 9fd34ae..2a5ada2 100644 --- a/verbecc/src/defs/types/data/verbs.py +++ b/src/verbecc/core/defs/types/data/verbs.py @@ -1,19 +1,19 @@ from bisect import bisect_left -from typing import Iterator, List +from typing import Iterator -from verbecc.src.utils.logging_utils import LoggingUtils -from verbecc.src.defs.types.data.verb import Verb -from verbecc.src.defs.types.exceptions import VerbNotFoundError -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.mlconjug.predictor import TemplatePredictor -from verbecc.src.utils import string_utils -from verbecc.src.utils.config.verbecc_config_util import VerbeccConfigUtil +from verbecc.core.utils.logging_utils import LoggingUtils +from verbecc.core.defs.types.data.verb import Verb +from verbecc.core.defs.types.exceptions import VerbNotFoundError +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.mlconjug.predictor import TemplatePredictor +from verbecc.core.utils import string_utils +from verbecc.core.utils.config.verbecc_config_util import VerbeccConfigUtil config = VerbeccConfigUtil().load_config() class Verbs: - def __init__(self, lang: LangCodeISO639_1, verbs: List[Verb]) -> None: + def __init__(self, lang: LangCodeISO639_1, verbs: list[Verb]) -> None: self._logger = LoggingUtils.get_logger(self.__class__.__name__) self.lang = lang self._verbs = verbs @@ -69,8 +69,8 @@ def find_verb_by_infinitive(self, infinitive: str) -> Verb: else: raise VerbNotFoundError() - def get_verbs_that_start_with(self, pre: str, max_results: int = 10) -> List[str]: - ret: List[str] = [] + def get_verbs_that_start_with(self, pre: str, max_results: int = 10) -> list[str]: + ret: list[str] = [] pre_no_accents = string_utils.strip_accents(pre.lower()) for verb in self._verbs: if verb.infinitive_no_accents.startswith(pre_no_accents): diff --git a/src/verbecc/core/defs/types/data/xml_types.py b/src/verbecc/core/defs/types/data/xml_types.py new file mode 100644 index 0000000..99c6381 --- /dev/null +++ b/src/verbecc/core/defs/types/data/xml_types.py @@ -0,0 +1,14 @@ +from typing import TypeAlias +from lxml import etree + +""" +Type alias for an XML element, which is an instance of lxml.etree._Element. +This alias is used to provide type hints for functions that work with XML elements. +The XmlElement type is used throughout the codebase to represent XML elements in a type-safe manner +and to improve code readability. +The XmlElementTree type is an alias for lxml.etree._ElementTree, which represents an entire XML document tree. +""" + +XmlParser : TypeAlias = etree.XMLParser +XmlElement : TypeAlias = etree._Element # type: ignore +XmlElementTree : TypeAlias = etree._ElementTree # type: ignore diff --git a/src/verbecc/core/defs/types/exceptions/__init__.py b/src/verbecc/core/defs/types/exceptions/__init__.py new file mode 100644 index 0000000..30c3221 --- /dev/null +++ b/src/verbecc/core/defs/types/exceptions/__init__.py @@ -0,0 +1,9 @@ +from .conjugator_error import ConjugatorError # type: ignore +from .conjugations_parser_error import ConjugationsParserError # type: ignore +from .conjugation_template_error import ConjugationTemplateError # type: ignore +from .invalid_lang_error import InvalidLangError # type: ignore +from .invalid_mood_error import InvalidMoodError # type: ignore +from .invalid_tense_error import InvalidTenseError # type: ignore +from .template_not_found_error import TemplateNotFoundError # type: ignore +from .verb_not_found_error import VerbNotFoundError # type: ignore +from .verbs_parser_error import VerbsParserError # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/exceptions/conjugation_template_error.py b/src/verbecc/core/defs/types/exceptions/conjugation_template_error.py similarity index 100% rename from verbecc/src/defs/types/exceptions/conjugation_template_error.py rename to src/verbecc/core/defs/types/exceptions/conjugation_template_error.py diff --git a/verbecc/src/defs/types/exceptions/conjugations_parser_error.py b/src/verbecc/core/defs/types/exceptions/conjugations_parser_error.py similarity index 100% rename from verbecc/src/defs/types/exceptions/conjugations_parser_error.py rename to src/verbecc/core/defs/types/exceptions/conjugations_parser_error.py diff --git a/verbecc/src/defs/types/exceptions/conjugator_error.py b/src/verbecc/core/defs/types/exceptions/conjugator_error.py similarity index 100% rename from verbecc/src/defs/types/exceptions/conjugator_error.py rename to src/verbecc/core/defs/types/exceptions/conjugator_error.py diff --git a/verbecc/src/defs/types/exceptions/invalid_lang_error.py b/src/verbecc/core/defs/types/exceptions/invalid_lang_error.py similarity index 100% rename from verbecc/src/defs/types/exceptions/invalid_lang_error.py rename to src/verbecc/core/defs/types/exceptions/invalid_lang_error.py diff --git a/verbecc/src/defs/types/exceptions/invalid_mood_error.py b/src/verbecc/core/defs/types/exceptions/invalid_mood_error.py similarity index 100% rename from verbecc/src/defs/types/exceptions/invalid_mood_error.py rename to src/verbecc/core/defs/types/exceptions/invalid_mood_error.py diff --git a/verbecc/src/defs/types/exceptions/invalid_tense_error.py b/src/verbecc/core/defs/types/exceptions/invalid_tense_error.py similarity index 100% rename from verbecc/src/defs/types/exceptions/invalid_tense_error.py rename to src/verbecc/core/defs/types/exceptions/invalid_tense_error.py diff --git a/verbecc/src/defs/types/exceptions/template_not_found_error.py b/src/verbecc/core/defs/types/exceptions/template_not_found_error.py similarity index 100% rename from verbecc/src/defs/types/exceptions/template_not_found_error.py rename to src/verbecc/core/defs/types/exceptions/template_not_found_error.py diff --git a/verbecc/src/defs/types/exceptions/verb_not_found_error.py b/src/verbecc/core/defs/types/exceptions/verb_not_found_error.py similarity index 100% rename from verbecc/src/defs/types/exceptions/verb_not_found_error.py rename to src/verbecc/core/defs/types/exceptions/verb_not_found_error.py diff --git a/verbecc/src/defs/types/exceptions/verbs_parser_error.py b/src/verbecc/core/defs/types/exceptions/verbs_parser_error.py similarity index 100% rename from verbecc/src/defs/types/exceptions/verbs_parser_error.py rename to src/verbecc/core/defs/types/exceptions/verbs_parser_error.py diff --git a/verbecc/src/defs/types/gender.py b/src/verbecc/core/defs/types/gender.py similarity index 100% rename from verbecc/src/defs/types/gender.py rename to src/verbecc/core/defs/types/gender.py diff --git a/verbecc/src/defs/types/lang_code.py b/src/verbecc/core/defs/types/lang_code.py similarity index 100% rename from verbecc/src/defs/types/lang_code.py rename to src/verbecc/core/defs/types/lang_code.py diff --git a/src/verbecc/core/defs/types/lang_specific_options/__init__.py b/src/verbecc/core/defs/types/lang_specific_options/__init__.py new file mode 100644 index 0000000..668a31c --- /dev/null +++ b/src/verbecc/core/defs/types/lang_specific_options/__init__.py @@ -0,0 +1,3 @@ +from .lang_specific_options import LangSpecificOptions # type: ignore +from .lang_specific_options_factory import LangSpecificOptionsFactory # type: ignore + diff --git a/verbecc/src/defs/types/mood/lang/__init__.py b/src/verbecc/core/defs/types/lang_specific_options/lang/__init__.py similarity index 100% rename from verbecc/src/defs/types/mood/lang/__init__.py rename to src/verbecc/core/defs/types/lang_specific_options/lang/__init__.py diff --git a/src/verbecc/core/defs/types/lang_specific_options/lang/es/__init__.py b/src/verbecc/core/defs/types/lang_specific_options/lang/es/__init__.py new file mode 100644 index 0000000..736e962 --- /dev/null +++ b/src/verbecc/core/defs/types/lang_specific_options/lang/es/__init__.py @@ -0,0 +1,2 @@ +from .lang_specific_options_es import LangSpecificOptionsEs # type: ignore +from .voseo_options import VoseoOptions # type: ignore diff --git a/verbecc/src/defs/types/lang_specific_options/lang/es/lang_specific_options_es.py b/src/verbecc/core/defs/types/lang_specific_options/lang/es/lang_specific_options_es.py similarity index 70% rename from verbecc/src/defs/types/lang_specific_options/lang/es/lang_specific_options_es.py rename to src/verbecc/core/defs/types/lang_specific_options/lang/es/lang_specific_options_es.py index 55d5ba9..05b9606 100644 --- a/verbecc/src/defs/types/lang_specific_options/lang/es/lang_specific_options_es.py +++ b/src/verbecc/core/defs/types/lang_specific_options/lang/es/lang_specific_options_es.py @@ -1,7 +1,7 @@ -from verbecc.src.defs.types.lang_specific_options import ( +from verbecc.core.defs.types.lang_specific_options import ( LangSpecificOptions, ) -from verbecc.src.defs.types.lang_specific_options.lang.es.voseo_options import ( +from verbecc.core.defs.types.lang_specific_options.lang.es.voseo_options import ( VoseoOptions, ) diff --git a/verbecc/src/defs/types/lang_specific_options/lang/es/voseo_options.py b/src/verbecc/core/defs/types/lang_specific_options/lang/es/voseo_options.py similarity index 100% rename from verbecc/src/defs/types/lang_specific_options/lang/es/voseo_options.py rename to src/verbecc/core/defs/types/lang_specific_options/lang/es/voseo_options.py diff --git a/verbecc/src/defs/types/lang_specific_options/lang_specific_options.py b/src/verbecc/core/defs/types/lang_specific_options/lang_specific_options.py similarity index 100% rename from verbecc/src/defs/types/lang_specific_options/lang_specific_options.py rename to src/verbecc/core/defs/types/lang_specific_options/lang_specific_options.py diff --git a/verbecc/src/defs/types/lang_specific_options/lang_specific_options_factory.py b/src/verbecc/core/defs/types/lang_specific_options/lang_specific_options_factory.py similarity index 60% rename from verbecc/src/defs/types/lang_specific_options/lang_specific_options_factory.py rename to src/verbecc/core/defs/types/lang_specific_options/lang_specific_options_factory.py index 78138d5..c6b2ab0 100644 --- a/verbecc/src/defs/types/lang_specific_options/lang_specific_options_factory.py +++ b/src/verbecc/core/defs/types/lang_specific_options/lang_specific_options_factory.py @@ -1,10 +1,10 @@ from typing import Optional -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.defs.types.lang_specific_options.lang_specific_options import ( +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.defs.types.lang_specific_options.lang_specific_options import ( LangSpecificOptions, ) -from verbecc.src.defs.types.lang_specific_options.lang.es.lang_specific_options_es import ( +from verbecc.core.defs.types.lang_specific_options.lang.es import ( LangSpecificOptionsEs, ) diff --git a/src/verbecc/core/defs/types/mood/__init__.py b/src/verbecc/core/defs/types/mood/__init__.py new file mode 100644 index 0000000..3a68ff4 --- /dev/null +++ b/src/verbecc/core/defs/types/mood/__init__.py @@ -0,0 +1,3 @@ +from .mood import Mood # type: ignore +from .moods import Moods # type: ignore +from .mood_factory import MoodFactory # type: ignore diff --git a/verbecc/src/defs/types/pronoun/lang/__init__.py b/src/verbecc/core/defs/types/mood/lang/__init__.py similarity index 100% rename from verbecc/src/defs/types/pronoun/lang/__init__.py rename to src/verbecc/core/defs/types/mood/lang/__init__.py diff --git a/src/verbecc/core/defs/types/mood/lang/ca/__init__.py b/src/verbecc/core/defs/types/mood/lang/ca/__init__.py new file mode 100644 index 0000000..26df36b --- /dev/null +++ b/src/verbecc/core/defs/types/mood/lang/ca/__init__.py @@ -0,0 +1 @@ +from .mood_ca import MoodCa # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/mood/lang/mood_ca.py b/src/verbecc/core/defs/types/mood/lang/ca/mood_ca.py similarity index 81% rename from verbecc/src/defs/types/mood/lang/mood_ca.py rename to src/verbecc/core/defs/types/mood/lang/ca/mood_ca.py index 3d78100..9d1d458 100644 --- a/verbecc/src/defs/types/mood/lang/mood_ca.py +++ b/src/verbecc/core/defs/types/mood/lang/ca/mood_ca.py @@ -1,4 +1,4 @@ -from verbecc.src.defs.types.mood.mood import Mood +from verbecc.core.defs.types.mood import Mood class MoodCa(Mood): diff --git a/src/verbecc/core/defs/types/mood/lang/en/__init__.py b/src/verbecc/core/defs/types/mood/lang/en/__init__.py new file mode 100644 index 0000000..a8f31cc --- /dev/null +++ b/src/verbecc/core/defs/types/mood/lang/en/__init__.py @@ -0,0 +1 @@ +from .mood_en import MoodEn # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/mood/lang/mood_en.py b/src/verbecc/core/defs/types/mood/lang/en/mood_en.py similarity index 82% rename from verbecc/src/defs/types/mood/lang/mood_en.py rename to src/verbecc/core/defs/types/mood/lang/en/mood_en.py index 86acc11..67165fc 100644 --- a/verbecc/src/defs/types/mood/lang/mood_en.py +++ b/src/verbecc/core/defs/types/mood/lang/en/mood_en.py @@ -1,4 +1,4 @@ -from verbecc.src.defs.types.mood.mood import Mood +from verbecc.core.defs.types.mood import Mood class MoodEn(Mood): diff --git a/src/verbecc/core/defs/types/mood/lang/es/__init__.py b/src/verbecc/core/defs/types/mood/lang/es/__init__.py new file mode 100644 index 0000000..9884404 --- /dev/null +++ b/src/verbecc/core/defs/types/mood/lang/es/__init__.py @@ -0,0 +1 @@ +from .mood_es import MoodEs # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/mood/lang/mood_es.py b/src/verbecc/core/defs/types/mood/lang/es/mood_es.py similarity index 82% rename from verbecc/src/defs/types/mood/lang/mood_es.py rename to src/verbecc/core/defs/types/mood/lang/es/mood_es.py index 0bbc778..a27d776 100644 --- a/verbecc/src/defs/types/mood/lang/mood_es.py +++ b/src/verbecc/core/defs/types/mood/lang/es/mood_es.py @@ -1,4 +1,4 @@ -from verbecc.src.defs.types.mood.mood import Mood +from verbecc.core.defs.types.mood import Mood class MoodEs(Mood): diff --git a/src/verbecc/core/defs/types/mood/lang/fr/__init__.py b/src/verbecc/core/defs/types/mood/lang/fr/__init__.py new file mode 100644 index 0000000..98aaf04 --- /dev/null +++ b/src/verbecc/core/defs/types/mood/lang/fr/__init__.py @@ -0,0 +1 @@ +from .mood_fr import MoodFr # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/mood/lang/mood_fr.py b/src/verbecc/core/defs/types/mood/lang/fr/mood_fr.py similarity index 79% rename from verbecc/src/defs/types/mood/lang/mood_fr.py rename to src/verbecc/core/defs/types/mood/lang/fr/mood_fr.py index 9751109..352a12a 100644 --- a/verbecc/src/defs/types/mood/lang/mood_fr.py +++ b/src/verbecc/core/defs/types/mood/lang/fr/mood_fr.py @@ -1,4 +1,4 @@ -from verbecc.src.defs.types.mood.mood import Mood +from verbecc.core.defs.types.mood import Mood class MoodFr(Mood): diff --git a/src/verbecc/core/defs/types/mood/lang/it/__init__.py b/src/verbecc/core/defs/types/mood/lang/it/__init__.py new file mode 100644 index 0000000..1bc0a42 --- /dev/null +++ b/src/verbecc/core/defs/types/mood/lang/it/__init__.py @@ -0,0 +1 @@ +from .mood_it import MoodIt # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/mood/lang/mood_it.py b/src/verbecc/core/defs/types/mood/lang/it/mood_it.py similarity index 80% rename from verbecc/src/defs/types/mood/lang/mood_it.py rename to src/verbecc/core/defs/types/mood/lang/it/mood_it.py index 3d445c1..dd2c6c7 100644 --- a/verbecc/src/defs/types/mood/lang/mood_it.py +++ b/src/verbecc/core/defs/types/mood/lang/it/mood_it.py @@ -1,4 +1,4 @@ -from verbecc.src.defs.types.mood.mood import Mood +from verbecc.core.defs.types.mood import Mood class MoodIt(Mood): diff --git a/src/verbecc/core/defs/types/mood/lang/pt/__init__.py b/src/verbecc/core/defs/types/mood/lang/pt/__init__.py new file mode 100644 index 0000000..b142f1c --- /dev/null +++ b/src/verbecc/core/defs/types/mood/lang/pt/__init__.py @@ -0,0 +1 @@ +from .mood_pt import MoodPt # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/mood/lang/mood_pt.py b/src/verbecc/core/defs/types/mood/lang/pt/mood_pt.py similarity index 82% rename from verbecc/src/defs/types/mood/lang/mood_pt.py rename to src/verbecc/core/defs/types/mood/lang/pt/mood_pt.py index 183f019..011b8a2 100644 --- a/verbecc/src/defs/types/mood/lang/mood_pt.py +++ b/src/verbecc/core/defs/types/mood/lang/pt/mood_pt.py @@ -1,4 +1,4 @@ -from verbecc.src.defs.types.mood.mood import Mood +from verbecc.core.defs.types.mood import Mood class MoodPt(Mood): diff --git a/src/verbecc/core/defs/types/mood/lang/ro/__init__.py b/src/verbecc/core/defs/types/mood/lang/ro/__init__.py new file mode 100644 index 0000000..0ae7a40 --- /dev/null +++ b/src/verbecc/core/defs/types/mood/lang/ro/__init__.py @@ -0,0 +1 @@ +from .mood_ro import MoodRo # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/mood/lang/mood_ro.py b/src/verbecc/core/defs/types/mood/lang/ro/mood_ro.py similarity index 87% rename from verbecc/src/defs/types/mood/lang/mood_ro.py rename to src/verbecc/core/defs/types/mood/lang/ro/mood_ro.py index fd48b36..c42dc3f 100644 --- a/verbecc/src/defs/types/mood/lang/mood_ro.py +++ b/src/verbecc/core/defs/types/mood/lang/ro/mood_ro.py @@ -1,4 +1,4 @@ -from verbecc.src.defs.types.mood.mood import Mood +from verbecc.core.defs.types.mood import Mood class MoodRo(Mood): diff --git a/verbecc/src/defs/types/mood/mood.py b/src/verbecc/core/defs/types/mood/mood.py similarity index 100% rename from verbecc/src/defs/types/mood/mood.py rename to src/verbecc/core/defs/types/mood/mood.py diff --git a/verbecc/src/defs/types/mood/mood_factory.py b/src/verbecc/core/defs/types/mood/mood_factory.py similarity index 80% rename from verbecc/src/defs/types/mood/mood_factory.py rename to src/verbecc/core/defs/types/mood/mood_factory.py index 6e756c2..54987b1 100644 --- a/verbecc/src/defs/types/mood/mood_factory.py +++ b/src/verbecc/core/defs/types/mood/mood_factory.py @@ -1,5 +1,5 @@ -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang -from verbecc.src.defs.types.mood import Mood, Moods +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.defs.types.mood import Mood, Moods class MoodFactory: diff --git a/src/verbecc/core/defs/types/mood/moods.py b/src/verbecc/core/defs/types/mood/moods.py new file mode 100644 index 0000000..328f357 --- /dev/null +++ b/src/verbecc/core/defs/types/mood/moods.py @@ -0,0 +1,17 @@ +from verbecc.core.defs.types.mood.lang.ca import MoodCa +from verbecc.core.defs.types.mood.lang.en import MoodEn +from verbecc.core.defs.types.mood.lang.es import MoodEs +from verbecc.core.defs.types.mood.lang.fr import MoodFr +from verbecc.core.defs.types.mood.lang.it import MoodIt +from verbecc.core.defs.types.mood.lang.pt import MoodPt +from verbecc.core.defs.types.mood.lang.ro import MoodRo + + +class Moods: + ca = MoodCa + en = MoodEn + es = MoodEs + fr = MoodFr + it = MoodIt + pt = MoodPt + ro = MoodRo diff --git a/verbecc/src/defs/types/number.py b/src/verbecc/core/defs/types/number.py similarity index 100% rename from verbecc/src/defs/types/number.py rename to src/verbecc/core/defs/types/number.py diff --git a/verbecc/src/defs/types/participle_inflection.py b/src/verbecc/core/defs/types/participle_inflection.py similarity index 70% rename from verbecc/src/defs/types/participle_inflection.py rename to src/verbecc/core/defs/types/participle_inflection.py index 8ccec6d..9c88bef 100644 --- a/verbecc/src/defs/types/participle_inflection.py +++ b/src/verbecc/core/defs/types/participle_inflection.py @@ -1,8 +1,7 @@ -import sys from enum import Enum -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.number import Number +from verbecc.core.defs.types.gender import Gender +from verbecc.core.defs.types.number import Number class ParticipleInflection(Enum): diff --git a/verbecc/src/defs/types/person.py b/src/verbecc/core/defs/types/person.py similarity index 100% rename from verbecc/src/defs/types/person.py rename to src/verbecc/core/defs/types/person.py diff --git a/src/verbecc/core/defs/types/pronoun/__init__.py b/src/verbecc/core/defs/types/pronoun/__init__.py new file mode 100644 index 0000000..2d3cd6b --- /dev/null +++ b/src/verbecc/core/defs/types/pronoun/__init__.py @@ -0,0 +1,4 @@ +from .pronoun import Pronoun # type: ignore +from .pronouns import Pronouns # type: ignore +from .pronoun_factory import PronounFactory # type: ignore +from .lang import * \ No newline at end of file diff --git a/src/verbecc/core/defs/types/pronoun/lang/ca/__init__.py b/src/verbecc/core/defs/types/pronoun/lang/ca/__init__.py new file mode 100644 index 0000000..a0d35be --- /dev/null +++ b/src/verbecc/core/defs/types/pronoun/lang/ca/__init__.py @@ -0,0 +1 @@ +from .pronoun_ca import PronounCa # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/pronoun/lang/pronoun_ca.py b/src/verbecc/core/defs/types/pronoun/lang/ca/pronoun_ca.py similarity index 75% rename from verbecc/src/defs/types/pronoun/lang/pronoun_ca.py rename to src/verbecc/core/defs/types/pronoun/lang/ca/pronoun_ca.py index 834d4c5..cfcdb4d 100644 --- a/verbecc/src/defs/types/pronoun/lang/pronoun_ca.py +++ b/src/verbecc/core/defs/types/pronoun/lang/ca/pronoun_ca.py @@ -1,4 +1,4 @@ -from verbecc.src.defs.types.pronoun.pronoun import Pronoun +from verbecc.core.defs.types.pronoun.pronoun import Pronoun class PronounCa(Pronoun): diff --git a/src/verbecc/core/defs/types/pronoun/lang/en/__init__.py b/src/verbecc/core/defs/types/pronoun/lang/en/__init__.py new file mode 100644 index 0000000..20d5caa --- /dev/null +++ b/src/verbecc/core/defs/types/pronoun/lang/en/__init__.py @@ -0,0 +1 @@ +from .pronoun_en import PronounEn # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/pronoun/lang/pronoun_en.py b/src/verbecc/core/defs/types/pronoun/lang/en/pronoun_en.py similarity index 69% rename from verbecc/src/defs/types/pronoun/lang/pronoun_en.py rename to src/verbecc/core/defs/types/pronoun/lang/en/pronoun_en.py index 9d2af4d..9a83208 100644 --- a/verbecc/src/defs/types/pronoun/lang/pronoun_en.py +++ b/src/verbecc/core/defs/types/pronoun/lang/en/pronoun_en.py @@ -1,4 +1,4 @@ -from verbecc.src.defs.types.pronoun.pronoun import Pronoun +from verbecc.core.defs.types.pronoun.pronoun import Pronoun class PronounEn(Pronoun): diff --git a/src/verbecc/core/defs/types/pronoun/lang/es/__init__.py b/src/verbecc/core/defs/types/pronoun/lang/es/__init__.py new file mode 100644 index 0000000..b3ccfbf --- /dev/null +++ b/src/verbecc/core/defs/types/pronoun/lang/es/__init__.py @@ -0,0 +1 @@ +from .pronoun_es import PronounEs # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/pronoun/lang/pronoun_es.py b/src/verbecc/core/defs/types/pronoun/lang/es/pronoun_es.py similarity index 80% rename from verbecc/src/defs/types/pronoun/lang/pronoun_es.py rename to src/verbecc/core/defs/types/pronoun/lang/es/pronoun_es.py index 507deb1..c3cd8cc 100644 --- a/verbecc/src/defs/types/pronoun/lang/pronoun_es.py +++ b/src/verbecc/core/defs/types/pronoun/lang/es/pronoun_es.py @@ -1,4 +1,4 @@ -from verbecc.src.defs.types.pronoun.pronoun import Pronoun +from verbecc.core.defs.types.pronoun.pronoun import Pronoun class PronounEs(Pronoun): diff --git a/src/verbecc/core/defs/types/pronoun/lang/fr/__init__.py b/src/verbecc/core/defs/types/pronoun/lang/fr/__init__.py new file mode 100644 index 0000000..bd6ded8 --- /dev/null +++ b/src/verbecc/core/defs/types/pronoun/lang/fr/__init__.py @@ -0,0 +1 @@ +from .pronoun_fr import PronounFr # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/pronoun/lang/pronoun_fr.py b/src/verbecc/core/defs/types/pronoun/lang/fr/pronoun_fr.py similarity index 74% rename from verbecc/src/defs/types/pronoun/lang/pronoun_fr.py rename to src/verbecc/core/defs/types/pronoun/lang/fr/pronoun_fr.py index ebe0369..f023a52 100644 --- a/verbecc/src/defs/types/pronoun/lang/pronoun_fr.py +++ b/src/verbecc/core/defs/types/pronoun/lang/fr/pronoun_fr.py @@ -1,4 +1,4 @@ -from verbecc.src.defs.types.pronoun.pronoun import Pronoun +from verbecc.core.defs.types.pronoun.pronoun import Pronoun class PronounFr(Pronoun): diff --git a/src/verbecc/core/defs/types/pronoun/lang/it/__init__.py b/src/verbecc/core/defs/types/pronoun/lang/it/__init__.py new file mode 100644 index 0000000..b4d8204 --- /dev/null +++ b/src/verbecc/core/defs/types/pronoun/lang/it/__init__.py @@ -0,0 +1 @@ +from .pronoun_it import PronounIt # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/pronoun/lang/pronoun_it.py b/src/verbecc/core/defs/types/pronoun/lang/it/pronoun_it.py similarity index 69% rename from verbecc/src/defs/types/pronoun/lang/pronoun_it.py rename to src/verbecc/core/defs/types/pronoun/lang/it/pronoun_it.py index e50a06a..9ad353b 100644 --- a/verbecc/src/defs/types/pronoun/lang/pronoun_it.py +++ b/src/verbecc/core/defs/types/pronoun/lang/it/pronoun_it.py @@ -1,4 +1,4 @@ -from verbecc.src.defs.types.pronoun.pronoun import Pronoun +from verbecc.core.defs.types.pronoun.pronoun import Pronoun class PronounIt(Pronoun): diff --git a/src/verbecc/core/defs/types/pronoun/lang/pt/__init__.py b/src/verbecc/core/defs/types/pronoun/lang/pt/__init__.py new file mode 100644 index 0000000..8645c10 --- /dev/null +++ b/src/verbecc/core/defs/types/pronoun/lang/pt/__init__.py @@ -0,0 +1 @@ +from .pronoun_pt import PronounPt # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/pronoun/lang/pronoun_pt.py b/src/verbecc/core/defs/types/pronoun/lang/pt/pronoun_pt.py similarity index 77% rename from verbecc/src/defs/types/pronoun/lang/pronoun_pt.py rename to src/verbecc/core/defs/types/pronoun/lang/pt/pronoun_pt.py index 992b75c..56a623d 100644 --- a/verbecc/src/defs/types/pronoun/lang/pronoun_pt.py +++ b/src/verbecc/core/defs/types/pronoun/lang/pt/pronoun_pt.py @@ -1,4 +1,4 @@ -from verbecc.src.defs.types.pronoun.pronoun import Pronoun +from verbecc.core.defs.types.pronoun.pronoun import Pronoun class PronounPt(Pronoun): diff --git a/src/verbecc/core/defs/types/pronoun/lang/ro/__init__.py b/src/verbecc/core/defs/types/pronoun/lang/ro/__init__.py new file mode 100644 index 0000000..c5cedf7 --- /dev/null +++ b/src/verbecc/core/defs/types/pronoun/lang/ro/__init__.py @@ -0,0 +1 @@ +from .pronoun_ro import PronounRo # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/pronoun/lang/pronoun_ro.py b/src/verbecc/core/defs/types/pronoun/lang/ro/pronoun_ro.py similarity index 70% rename from verbecc/src/defs/types/pronoun/lang/pronoun_ro.py rename to src/verbecc/core/defs/types/pronoun/lang/ro/pronoun_ro.py index 74d6e8a..6cd2c5d 100644 --- a/verbecc/src/defs/types/pronoun/lang/pronoun_ro.py +++ b/src/verbecc/core/defs/types/pronoun/lang/ro/pronoun_ro.py @@ -1,4 +1,4 @@ -from verbecc.src.defs.types.pronoun.pronoun import Pronoun +from verbecc.core.defs.types.pronoun.pronoun import Pronoun class PronounRo(Pronoun): diff --git a/verbecc/src/defs/types/pronoun/pronoun.py b/src/verbecc/core/defs/types/pronoun/pronoun.py similarity index 100% rename from verbecc/src/defs/types/pronoun/pronoun.py rename to src/verbecc/core/defs/types/pronoun/pronoun.py diff --git a/verbecc/src/defs/types/pronoun/pronoun_factory.py b/src/verbecc/core/defs/types/pronoun/pronoun_factory.py similarity index 73% rename from verbecc/src/defs/types/pronoun/pronoun_factory.py rename to src/verbecc/core/defs/types/pronoun/pronoun_factory.py index fc71a47..ddb2a2b 100644 --- a/verbecc/src/defs/types/pronoun/pronoun_factory.py +++ b/src/verbecc/core/defs/types/pronoun/pronoun_factory.py @@ -1,6 +1,6 @@ -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang -from verbecc.src.defs.types.pronoun.pronoun import Pronoun -from verbecc.src.defs.types.pronoun.pronouns import Pronouns +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.defs.types.pronoun.pronoun import Pronoun +from verbecc.core.defs.types.pronoun.pronouns import Pronouns class PronounFactory: diff --git a/src/verbecc/core/defs/types/pronoun/pronouns.py b/src/verbecc/core/defs/types/pronoun/pronouns.py new file mode 100644 index 0000000..763b2ba --- /dev/null +++ b/src/verbecc/core/defs/types/pronoun/pronouns.py @@ -0,0 +1,17 @@ +from verbecc.core.defs.types.pronoun.lang.ca import PronounCa +from verbecc.core.defs.types.pronoun.lang.en import PronounEn +from verbecc.core.defs.types.pronoun.lang.es import PronounEs +from verbecc.core.defs.types.pronoun.lang.fr import PronounFr +from verbecc.core.defs.types.pronoun.lang.it import PronounIt +from verbecc.core.defs.types.pronoun.lang.pt import PronounPt +from verbecc.core.defs.types.pronoun.lang.ro import PronounRo + + +class Pronouns: + ca = PronounCa + en = PronounEn + es = PronounEs + fr = PronounFr + it = PronounIt + pt = PronounPt + ro = PronounRo diff --git a/src/verbecc/core/defs/types/tense/__init__.py b/src/verbecc/core/defs/types/tense/__init__.py new file mode 100644 index 0000000..efd4ee1 --- /dev/null +++ b/src/verbecc/core/defs/types/tense/__init__.py @@ -0,0 +1,3 @@ +from .tense import Tense # type: ignore +from .tenses import Tenses # type: ignore +from .tense_factory import TenseFactory # type: ignore diff --git a/verbecc/src/defs/types/tense/lang/__init__.py b/src/verbecc/core/defs/types/tense/lang/__init__.py similarity index 100% rename from verbecc/src/defs/types/tense/lang/__init__.py rename to src/verbecc/core/defs/types/tense/lang/__init__.py diff --git a/src/verbecc/core/defs/types/tense/lang/ca/__init__.py b/src/verbecc/core/defs/types/tense/lang/ca/__init__.py new file mode 100644 index 0000000..c175773 --- /dev/null +++ b/src/verbecc/core/defs/types/tense/lang/ca/__init__.py @@ -0,0 +1 @@ +from .tense_ca import TenseCa # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/tense/lang/tense_ca.py b/src/verbecc/core/defs/types/tense/lang/ca/tense_ca.py similarity index 91% rename from verbecc/src/defs/types/tense/lang/tense_ca.py rename to src/verbecc/core/defs/types/tense/lang/ca/tense_ca.py index f620323..134a62c 100644 --- a/verbecc/src/defs/types/tense/lang/tense_ca.py +++ b/src/verbecc/core/defs/types/tense/lang/ca/tense_ca.py @@ -1,4 +1,4 @@ -from verbecc.src.defs.types.tense.tense import Tense +from verbecc.core.defs.types.tense import Tense class TenseCa(Tense): diff --git a/src/verbecc/core/defs/types/tense/lang/en/__init__.py b/src/verbecc/core/defs/types/tense/lang/en/__init__.py new file mode 100644 index 0000000..87e9828 --- /dev/null +++ b/src/verbecc/core/defs/types/tense/lang/en/__init__.py @@ -0,0 +1 @@ +from .tense_en import TenseEn # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/tense/lang/tense_en.py b/src/verbecc/core/defs/types/tense/lang/en/tense_en.py similarity index 92% rename from verbecc/src/defs/types/tense/lang/tense_en.py rename to src/verbecc/core/defs/types/tense/lang/en/tense_en.py index 86bf225..734f3ed 100644 --- a/verbecc/src/defs/types/tense/lang/tense_en.py +++ b/src/verbecc/core/defs/types/tense/lang/en/tense_en.py @@ -1,4 +1,4 @@ -from verbecc.src.defs.types.tense.tense import Tense +from verbecc.core.defs.types.tense import Tense class TenseEn(Tense): diff --git a/src/verbecc/core/defs/types/tense/lang/es/__init__.py b/src/verbecc/core/defs/types/tense/lang/es/__init__.py new file mode 100644 index 0000000..1e680d8 --- /dev/null +++ b/src/verbecc/core/defs/types/tense/lang/es/__init__.py @@ -0,0 +1 @@ +from .tense_es import TenseEs # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/tense/lang/tense_es.py b/src/verbecc/core/defs/types/tense/lang/es/tense_es.py similarity index 94% rename from verbecc/src/defs/types/tense/lang/tense_es.py rename to src/verbecc/core/defs/types/tense/lang/es/tense_es.py index 5bd3a2c..75ca480 100644 --- a/verbecc/src/defs/types/tense/lang/tense_es.py +++ b/src/verbecc/core/defs/types/tense/lang/es/tense_es.py @@ -1,4 +1,4 @@ -from verbecc.src.defs.types.tense.tense import Tense +from verbecc.core.defs.types.tense import Tense class TenseEs(Tense): diff --git a/src/verbecc/core/defs/types/tense/lang/fr/__init__.py b/src/verbecc/core/defs/types/tense/lang/fr/__init__.py new file mode 100644 index 0000000..8eb1e4e --- /dev/null +++ b/src/verbecc/core/defs/types/tense/lang/fr/__init__.py @@ -0,0 +1 @@ +from .tense_fr import TenseFr # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/tense/lang/tense_fr.py b/src/verbecc/core/defs/types/tense/lang/fr/tense_fr.py similarity index 91% rename from verbecc/src/defs/types/tense/lang/tense_fr.py rename to src/verbecc/core/defs/types/tense/lang/fr/tense_fr.py index 6767bed..8d135c4 100644 --- a/verbecc/src/defs/types/tense/lang/tense_fr.py +++ b/src/verbecc/core/defs/types/tense/lang/fr/tense_fr.py @@ -1,4 +1,4 @@ -from verbecc.src.defs.types.tense.tense import Tense +from verbecc.core.defs.types.tense import Tense class TenseFr(Tense): diff --git a/src/verbecc/core/defs/types/tense/lang/it/__init__.py b/src/verbecc/core/defs/types/tense/lang/it/__init__.py new file mode 100644 index 0000000..607c933 --- /dev/null +++ b/src/verbecc/core/defs/types/tense/lang/it/__init__.py @@ -0,0 +1 @@ +from .tense_it import TenseIt # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/tense/lang/tense_it.py b/src/verbecc/core/defs/types/tense/lang/it/tense_it.py similarity index 91% rename from verbecc/src/defs/types/tense/lang/tense_it.py rename to src/verbecc/core/defs/types/tense/lang/it/tense_it.py index 7cfd6b2..8809961 100644 --- a/verbecc/src/defs/types/tense/lang/tense_it.py +++ b/src/verbecc/core/defs/types/tense/lang/it/tense_it.py @@ -1,4 +1,4 @@ -from verbecc.src.defs.types.tense.tense import Tense +from verbecc.core.defs.types.tense import Tense class TenseIt(Tense): diff --git a/src/verbecc/core/defs/types/tense/lang/pt/__init__.py b/src/verbecc/core/defs/types/tense/lang/pt/__init__.py new file mode 100644 index 0000000..7552d6c --- /dev/null +++ b/src/verbecc/core/defs/types/tense/lang/pt/__init__.py @@ -0,0 +1 @@ +from .tense_pt import TensePt # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/tense/lang/tense_pt.py b/src/verbecc/core/defs/types/tense/lang/pt/tense_pt.py similarity index 95% rename from verbecc/src/defs/types/tense/lang/tense_pt.py rename to src/verbecc/core/defs/types/tense/lang/pt/tense_pt.py index facb96e..f2f961c 100644 --- a/verbecc/src/defs/types/tense/lang/tense_pt.py +++ b/src/verbecc/core/defs/types/tense/lang/pt/tense_pt.py @@ -1,4 +1,4 @@ -from verbecc.src.defs.types.tense.tense import Tense +from verbecc.core.defs.types.tense import Tense class TensePt(Tense): diff --git a/src/verbecc/core/defs/types/tense/lang/ro/__init__.py b/src/verbecc/core/defs/types/tense/lang/ro/__init__.py new file mode 100644 index 0000000..646341e --- /dev/null +++ b/src/verbecc/core/defs/types/tense/lang/ro/__init__.py @@ -0,0 +1 @@ +from .tense_ro import TenseRo # type: ignore \ No newline at end of file diff --git a/verbecc/src/defs/types/tense/lang/tense_ro.py b/src/verbecc/core/defs/types/tense/lang/ro/tense_ro.py similarity index 91% rename from verbecc/src/defs/types/tense/lang/tense_ro.py rename to src/verbecc/core/defs/types/tense/lang/ro/tense_ro.py index 94c705c..382ed1c 100644 --- a/verbecc/src/defs/types/tense/lang/tense_ro.py +++ b/src/verbecc/core/defs/types/tense/lang/ro/tense_ro.py @@ -1,4 +1,4 @@ -from verbecc.src.defs.types.tense.tense import Tense +from verbecc.core.defs.types.tense import Tense class TenseRo(Tense): diff --git a/verbecc/src/defs/types/tense/tense.py b/src/verbecc/core/defs/types/tense/tense.py similarity index 100% rename from verbecc/src/defs/types/tense/tense.py rename to src/verbecc/core/defs/types/tense/tense.py diff --git a/verbecc/src/defs/types/tense/tense_factory.py b/src/verbecc/core/defs/types/tense/tense_factory.py similarity index 74% rename from verbecc/src/defs/types/tense/tense_factory.py rename to src/verbecc/core/defs/types/tense/tense_factory.py index eb47afd..9ad9656 100644 --- a/verbecc/src/defs/types/tense/tense_factory.py +++ b/src/verbecc/core/defs/types/tense/tense_factory.py @@ -1,6 +1,6 @@ -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang -from verbecc.src.defs.types.tense.tense import Tense -from verbecc.src.defs.types.tense.tenses import Tenses +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.defs.types.tense import Tense +from verbecc.core.defs.types.tense import Tenses class TenseFactory: diff --git a/src/verbecc/core/defs/types/tense/tenses.py b/src/verbecc/core/defs/types/tense/tenses.py new file mode 100644 index 0000000..5a94c1d --- /dev/null +++ b/src/verbecc/core/defs/types/tense/tenses.py @@ -0,0 +1,17 @@ +from verbecc.core.defs.types.tense.lang.ca import TenseCa +from verbecc.core.defs.types.tense.lang.en import TenseEn +from verbecc.core.defs.types.tense.lang.es import TenseEs +from verbecc.core.defs.types.tense.lang.fr import TenseFr +from verbecc.core.defs.types.tense.lang.it import TenseIt +from verbecc.core.defs.types.tense.lang.pt import TensePt +from verbecc.core.defs.types.tense.lang.ro import TenseRo + + +class Tenses: + ca = TenseCa + en = TenseEn + es = TenseEs + fr = TenseFr + it = TenseIt + pt = TensePt + ro = TenseRo diff --git a/src/verbecc/core/inflectors/__init__.py b/src/verbecc/core/inflectors/__init__.py new file mode 100644 index 0000000..e66b7e0 --- /dev/null +++ b/src/verbecc/core/inflectors/__init__.py @@ -0,0 +1 @@ +from .lang import * \ No newline at end of file diff --git a/verbecc/src/inflectors/inflector.py b/src/verbecc/core/inflectors/inflector.py similarity index 84% rename from verbecc/src/inflectors/inflector.py rename to src/verbecc/core/inflectors/inflector.py index e85b90f..dbc76af 100755 --- a/verbecc/src/inflectors/inflector.py +++ b/src/verbecc/core/inflectors/inflector.py @@ -1,25 +1,25 @@ from abc import ABC, abstractmethod -from typing import Dict, List, Tuple, Optional - -from verbecc.src.utils.logging_utils import LoggingUtils -from verbecc.src.conjugator.conjugation_object import ConjugationObjects -from verbecc.src.defs.constants.grammar_defines import PARTICIPLE_INFLECTIONS -from verbecc.src.defs.types.data.conjugation_template import ConjugationTemplate -from verbecc.src.defs.types.data.person_ending import PersonEnding -from verbecc.src.defs.types.data.tense_template import TenseTemplate -from verbecc.src.defs.types.data.verb import Verb -from verbecc.src.defs.types.data.verbs import Verbs -from verbecc.src.defs.types.exceptions import ConjugatorError -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.defs.types.mood import Mood, Moods -from verbecc.src.defs.types.participle_inflection import ParticipleInflection -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.tense import Tense, Tenses -from verbecc.src.defs.types.pronoun import Pronoun, Pronouns -from verbecc.src.parsers.conjugations_parser import ConjugationsParser -from verbecc.src.parsers.verbs_parser import VerbsParser +from typing import Tuple, Optional + +from verbecc.core.utils.logging_utils import LoggingUtils +from verbecc.core.conjugator.conjugation_object import ConjugationObjects +from verbecc.core.defs.constants.grammar_defines import PARTICIPLE_INFLECTIONS +from verbecc.core.defs.types.data.conjugation_template import ConjugationTemplate +from verbecc.core.defs.types.data.person_ending import PersonEnding +from verbecc.core.defs.types.data.tense_template import TenseTemplate +from verbecc.core.defs.types.data.verb import Verb +from verbecc.core.defs.types.data.verbs import Verbs +from verbecc.core.defs.types.exceptions import ConjugatorError +from verbecc.core.defs.types.gender import Gender +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.defs.types.mood import Mood, Moods +from verbecc.core.defs.types.participle_inflection import ParticipleInflection +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.number import Number +from verbecc.core.defs.types.tense import Tense, Tenses +from verbecc.core.defs.types.pronoun import Pronoun +from verbecc.core.parsers.conjugations_parser import ConjugationsParser +from verbecc.core.parsers.verbs_parser import VerbsParser class Inflector(ABC): @@ -39,16 +39,16 @@ def __init__(self) -> None: def get_lang(self) -> LangCodeISO639_1: raise NotImplementedError() - def get_verbs(self) -> List[Verb]: + def get_verbs(self) -> list[Verb]: return list(self._verbs) - def get_infinitives(self) -> List[str]: + def get_infinitives(self) -> list[str]: return self._verbs.infinitives - def get_templates(self) -> List[ConjugationTemplate]: + def get_templates(self) -> list[ConjugationTemplate]: return list(self._conjugations) - def get_template_names(self) -> List[str]: + def get_template_names(self) -> list[str]: return [t.name for t in self._conjugations] def find_verb_by_infinitive(self, infinitive: str) -> Verb: @@ -57,7 +57,7 @@ def find_verb_by_infinitive(self, infinitive: str) -> Verb: def find_template(self, name: str) -> ConjugationTemplate: return self._conjugations.find_template(name) - def get_verbs_that_start_with(self, query: str, max_results: int) -> List[str]: + def get_verbs_that_start_with(self, query: str, max_results: int) -> list[str]: return self._verbs.get_verbs_that_start_with(query.lower(), max_results) def get_verb_stem_from_template_name( @@ -77,7 +77,7 @@ def get_verb_stem_from_template_name( return infinitive[: len(infinitive) - len(template_ending)] def verb_can_be_reflexive(self, infinitive: str) -> bool: - return not self._is_impersonal_verb(infinitive) + return not self.private_is_impersonal_verb(infinitive) def split_reflexive(self, infinitive: str) -> Tuple[bool, str]: """ @@ -105,13 +105,13 @@ def split_reflexive(self, infinitive: str) -> Tuple[bool, str]: def add_reflexive_pronoun(self, s: str) -> str: return s - def add_subjunctive_relative_pronoun(self, s: str, tense: Tense) -> str: + def add_subjunctive_relative_pronoun(self, s: str, tense: Tense | str | None) -> str: return s def auxiliary_verb_uses_alternate_conjugation(self, tense: Tense) -> bool: return False - def get_tenses_conjugated_without_pronouns(self) -> List[Tense]: + def get_tenses_conjugated_without_pronouns(self) -> list[Tense]: return [] def get_auxiliary_verb( @@ -155,7 +155,7 @@ def get_alternate_hv_inflection(self, s: str) -> str: @abstractmethod def get_compound_conjugations_aux_verb_map( self, - ) -> Dict[Mood, Dict[Tense, Tuple[Mood, Tense]]]: + ) -> dict[Mood, dict[Tense, Tuple[Mood, Tense]]]: """Returns a map of the tense of the helping verb for each compound mood and tense""" raise NotImplementedError() @@ -192,7 +192,7 @@ def get_pronouns( number: Optional[Number] = None, gender: Optional[Gender] = None, imperative: bool = False, - ) -> List[Pronoun]: + ) -> list[Pronoun]: """ Returns a list of all pronouns matching the provided filters, in the typical order, with the default pronoun first. @@ -283,8 +283,8 @@ def compound_primary_verb_conjugation_uses_infinitive( return False def modify_aux_verb_conj_if_applicable( - self, aux_conj: List[str], mood: Mood, tense: Tense - ) -> List[str]: + self, aux_conj: list[str], mood: Mood, tense: Tense + ) -> list[str]: """ Hook for certain languages e.g. Romanian that use a different aux_conj for certain tenses @@ -345,5 +345,8 @@ def _get_pronoun_suffix( ) -> str: return " " + self.get_pronouns(person, number, gender)[0] - def _is_impersonal_verb(self, infinitive: str) -> bool: + def private_is_impersonal_verb(self, infinitive: str) -> bool: return False + + def _get_verbs(self) -> Verbs: + return self._verbs diff --git a/verbecc/src/inflectors/inflector_factory.py b/src/verbecc/core/inflectors/inflector_factory.py similarity index 58% rename from verbecc/src/inflectors/inflector_factory.py rename to src/verbecc/core/inflectors/inflector_factory.py index 872c46f..08ae75c 100644 --- a/verbecc/src/inflectors/inflector_factory.py +++ b/src/verbecc/core/inflectors/inflector_factory.py @@ -1,15 +1,15 @@ from typing import Optional -from verbecc.src.defs.types.lang_specific_options import LangSpecificOptions -from verbecc.src.defs.types.exceptions import InvalidLangError -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.inflectors.inflector import Inflector -from verbecc.src.inflectors.lang.inflector_ca import InflectorCa -from verbecc.src.inflectors.lang.inflector_es import InflectorEs -from verbecc.src.inflectors.lang.inflector_fr import InflectorFr -from verbecc.src.inflectors.lang.inflector_it import InflectorIt -from verbecc.src.inflectors.lang.inflector_pt import InflectorPt -from verbecc.src.inflectors.lang.inflector_ro import InflectorRo +from verbecc.core.defs.types.lang_specific_options import LangSpecificOptions +from verbecc.core.defs.types.exceptions import InvalidLangError +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.inflectors.inflector import Inflector +from verbecc.core.inflectors.lang.ca import InflectorCa +from verbecc.core.inflectors.lang.es import InflectorEs +from verbecc.core.inflectors.lang.fr import InflectorFr +from verbecc.core.inflectors.lang.it import InflectorIt +from verbecc.core.inflectors.lang.pt import InflectorPt +from verbecc.core.inflectors.lang.ro import InflectorRo class InflectorFactory: diff --git a/verbecc/src/inflectors/__init__.py b/src/verbecc/core/inflectors/lang/__init__.py similarity index 100% rename from verbecc/src/inflectors/__init__.py rename to src/verbecc/core/inflectors/lang/__init__.py diff --git a/src/verbecc/core/inflectors/lang/ca/__init__.py b/src/verbecc/core/inflectors/lang/ca/__init__.py new file mode 100644 index 0000000..ce5955b --- /dev/null +++ b/src/verbecc/core/inflectors/lang/ca/__init__.py @@ -0,0 +1 @@ +from .inflector_ca import InflectorCa # type: ignore \ No newline at end of file diff --git a/verbecc/src/inflectors/lang/inflector_ca.py b/src/verbecc/core/inflectors/lang/ca/inflector_ca.py similarity index 88% rename from verbecc/src/inflectors/lang/inflector_ca.py rename to src/verbecc/core/inflectors/lang/ca/inflector_ca.py index fa33554..2d37ef6 100755 --- a/verbecc/src/inflectors/lang/inflector_ca.py +++ b/src/verbecc/core/inflectors/lang/ca/inflector_ca.py @@ -1,19 +1,19 @@ -from typing import Dict, List, Optional, Tuple - -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.mood import Mood, Moods -from verbecc.src.defs.types.tense import Tense, Tenses -from verbecc.src.defs.types import exceptions -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.defs.types.lang_specific_options import ( +from typing import Optional, Tuple + +from verbecc.core.defs.types.gender import Gender +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.number import Number +from verbecc.core.defs.types.mood import Mood, Moods +from verbecc.core.defs.types.tense import Tense, Tenses +from verbecc.core.defs.types import exceptions +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.defs.types.lang_specific_options import ( LangSpecificOptions, ) -from verbecc.src.inflectors import inflector -from verbecc.src.utils.string_utils import get_common_letter_count, strip_accents -from verbecc.src.conjugator.conjugation_object import ConjugationObjects -from verbecc.src.defs.types.pronoun import Pronoun, Pronouns +from verbecc.core.inflectors import inflector +from verbecc.core.utils.string_utils import get_common_letter_count, strip_accents +from verbecc.core.conjugator.conjugation_object import ConjugationObjects +from verbecc.core.defs.types.pronoun import Pronoun, Pronouns class InflectorCa(inflector.Inflector): @@ -41,8 +41,8 @@ def get_pronouns( number: Optional[Number] = None, gender: Optional[Gender] = None, imperative: bool = False, - ) -> List[Pronoun]: - ret = [] + ) -> list[Pronoun]: + ret : list[Pronoun] = [] if (person is None or person == Person.First) and ( number is None or number == Number.Singular ): @@ -97,7 +97,7 @@ def make_pronoun_reflexive(self, pronoun: Pronoun) -> str: else: return pronoun + " se" - def get_tenses_conjugated_without_pronouns(self) -> List[Tense]: + def get_tenses_conjugated_without_pronouns(self) -> list[Tense]: return [ Tenses.ca.Participi, Tenses.ca.Gerundi, @@ -141,7 +141,7 @@ def get_alternate_hv_inflection(self, s: str) -> str: def get_compound_conjugations_aux_verb_map( self, - ) -> Dict[Mood, Dict[Tense, Tuple[Mood, Tense]]]: + ) -> dict[Mood, dict[Tense, Tuple[Mood, Tense]]]: """ TODO: Implement all these compound tenses (Spanish compound tenses in this commment, for reference) return { @@ -189,7 +189,7 @@ def get_verb_stem_from_template_name( _, template_ending = template_name.split(":") infinitive_no_accents = strip_accents(infinitive) template_ending_no_accents = strip_accents(infinitive) - infinitive_ending_no_accents = infinitive_no_accents[-len(template_ending) :] + infinitive_ending_no_accents = infinitive_no_accents[-len(template_ending):] if ( not infinitive_ending_no_accents == template_ending_no_accents and not infinitive_no_accents[1:] == template_ending_no_accents[1:] diff --git a/src/verbecc/core/inflectors/lang/es/__init__.py b/src/verbecc/core/inflectors/lang/es/__init__.py new file mode 100644 index 0000000..765bdf9 --- /dev/null +++ b/src/verbecc/core/inflectors/lang/es/__init__.py @@ -0,0 +1 @@ +from .inflector_es import InflectorEs # type: ignore \ No newline at end of file diff --git a/verbecc/src/inflectors/lang/inflector_es.py b/src/verbecc/core/inflectors/lang/es/inflector_es.py similarity index 63% rename from verbecc/src/inflectors/lang/inflector_es.py rename to src/verbecc/core/inflectors/lang/es/inflector_es.py index 647fed2..ebfd586 100755 --- a/verbecc/src/inflectors/lang/inflector_es.py +++ b/src/verbecc/core/inflectors/lang/es/inflector_es.py @@ -1,22 +1,22 @@ import copy -from typing import cast, Dict, List, Optional, Tuple - -from verbecc.src.conjugator.conjugation_object import ConjugationObjects -from verbecc.src.defs.types.data.person_ending import PersonEnding -from verbecc.src.defs.types.data.tense_template import TenseTemplate -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.defs.types.lang_specific_options import LangSpecificOptions -from verbecc.src.defs.types.lang_specific_options import LangSpecificOptionsEs -from verbecc.src.defs.types.lang_specific_options import LangSpecificOptionsFactory -from verbecc.src.defs.types.lang_specific_options import VoseoOptions -from verbecc.src.defs.types.mood import Mood, Moods -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.tense import Tense, Tenses -from verbecc.src.inflectors.inflector import Inflector -from verbecc.src.utils.string_utils import strip_accents -from verbecc.src.defs.types.pronoun import Pronoun, Pronouns +from typing import cast, Optional, Tuple + +from verbecc.core.conjugator.conjugation_object import ConjugationObjects +from verbecc.core.defs.types.data.person_ending import PersonEnding +from verbecc.core.defs.types.data.tense_template import TenseTemplate +from verbecc.core.defs.types.gender import Gender +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.defs.types.lang_specific_options import LangSpecificOptions +from verbecc.core.defs.types.lang_specific_options.lang.es import LangSpecificOptionsEs +from verbecc.core.defs.types.lang_specific_options.lang.es import VoseoOptions +from verbecc.core.defs.types.lang_specific_options import LangSpecificOptionsFactory +from verbecc.core.defs.types.mood import Mood, Moods +from verbecc.core.defs.types.number import Number +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.tense import Tense, Tenses +from verbecc.core.inflectors.inflector import Inflector +from verbecc.core.utils.string_utils import strip_accents +from verbecc.core.defs.types.pronoun import Pronoun, Pronouns class InflectorEs(Inflector): @@ -55,8 +55,8 @@ def get_pronouns( number: Optional[Number] = None, gender: Optional[Gender] = None, imperative: bool = False, - ) -> List[Pronoun]: - ret = [] + ) -> list[Pronoun]: + ret : list[Pronoun] = [] if (person is None or person == Person.First) and ( number is None or number == Number.Singular ): @@ -111,7 +111,7 @@ def make_pronoun_reflexive(self, pronoun: str) -> str: else: return pronoun + " se" - def get_tenses_conjugated_without_pronouns(self) -> List[Tense]: + def get_tenses_conjugated_without_pronouns(self) -> list[Tense]: return [ Tenses.es.Participo, Tenses.es.Gerundio, @@ -156,7 +156,7 @@ def get_alternate_hv_inflection(self, s: str) -> str: def get_compound_conjugations_aux_verb_map( self, - ) -> Dict[Mood, Dict[Tense, Tuple[Mood, Tense]]]: + ) -> dict[Mood, dict[Tense, Tuple[Mood, Tense]]]: return { Moods.es.Indicativo: { Tenses.es.PretéritoPerfectoCompuesto: ( @@ -236,71 +236,66 @@ def modify_person_ending_if_applicable( """ # only need to accent 'a', 'e' and 'i', AFAIK VOWEL_ACCENT_MAP = {"a": "á", "e": "é", "i": "í"} - if self.lang_specific_options is not None: + if ( + pronoun is not None + and pronoun == "vos" + and person_ending.person == Person.Second + and person_ending.number == Number.Singular + ): + if self.lang_specific_options.get_voseo_options() != VoseoOptions.VoseoTipo3: + # only voseo tipo 3 (voseo típico aka Rioplatense) is supported at the moment + raise NotImplementedError() + if ( - pronoun is not None - and pronoun == "vos" - and person_ending.person == Person.Second - and person_ending.number == Number.Singular + (mood == Moods.es.Indicativo and tense == Tenses.es.Presente) + or (mood == Moods.es.Subjuntivo and tense == Tenses.es.Presente) + or (mood == Moods.es.Imperativo and tense == Tenses.es.Afirmativo) ): - if ( - self.lang_specific_options is not None - and self.lang_specific_options.get_voseo_options() - != VoseoOptions.VoseoTipo3 - ): - # only voseo tipo 3 (voseo típico aka Rioplatense) is supported at the moment - raise NotImplementedError() - - if ( - (mood == Moods.es.Indicativo and tense == Tenses.es.Presente) - or (mood == Moods.es.Subjuntivo and tense == Tenses.es.Presente) - or (mood == Moods.es.Imperativo and tense == Tenses.es.Afirmativo) - ): - # first replace with given p2s (tú) ending(s) - # with the p2p (vosotros) ending(s) - replacement_person_ending = copy.deepcopy( - tense_template.get_person_ending(Person.Second, Number.Plural) - ) - # change replacement PersonEnding Person from second person plural to singular - replacement_person_ending.person = Person.Second - replacement_person_ending.number = Number.Singular - - # modify the endings for voseo to form the vos endings - for i, ending in enumerate(replacement_person_ending.get_endings()): - - if mood in (Moods.es.Indicativo, Moods.es.Subjuntivo): - # step one for indicativo and subjuntivo presente: - # remove 'i' in the second-to-last letter position - if ending[-2] == "i": - ending = ending[:-2] + ending[-1] - if mood == Moods.es.Imperativo: - # step one for imperativo: remove the trailing 'd' - if ending[-1] == "d": - ending = ending[:-1] - - if mood == Moods.es.Subjuntivo: - # step two for subjunctivo is to strip any accents from vowels - # e.g. vosotros seáis -> vos seas - ending = strip_accents(ending) - else: - # step two for indicativo and imperativo is to accent the vowel which - # is now in the second-to-last or last letter position (if not already accented) - - # accent second-to-last letter, if vowel - if len(ending) > 1: - if ending[-2] in VOWEL_ACCENT_MAP: - ending = ( - ending[:-2] - + VOWEL_ACCENT_MAP[ending[-2]] - + ending[-1] - ) - # accent last letter, if vowel - if ending[-1] in VOWEL_ACCENT_MAP: - ending = ending[:-1] + VOWEL_ACCENT_MAP[ending[-1]] - - # update the replacement person ending with the modified ending - replacement_person_ending.endings[i] = ending - return replacement_person_ending + # first replace with given p2s (tú) ending(s) + # with the p2p (vosotros) ending(s) + replacement_person_ending = copy.deepcopy( + tense_template.get_person_ending(Person.Second, Number.Plural) + ) + # change replacement PersonEnding Person from second person plural to singular + replacement_person_ending.person = Person.Second + replacement_person_ending.number = Number.Singular + + # modify the endings for voseo to form the vos endings + for i, ending in enumerate(replacement_person_ending.get_endings()): + + if mood in (Moods.es.Indicativo, Moods.es.Subjuntivo): + # step one for indicativo and subjuntivo presente: + # remove 'i' in the second-to-last letter position + if ending[-2] == "i": + ending = ending[:-2] + ending[-1] + if mood == Moods.es.Imperativo: + # step one for imperativo: remove the trailing 'd' + if ending[-1] == "d": + ending = ending[:-1] + + if mood == Moods.es.Subjuntivo: + # step two for subjunctivo is to strip any accents from vowels + # e.g. vosotros seáis -> vos seas + ending = strip_accents(ending) + else: + # step two for indicativo and imperativo is to accent the vowel which + # is now in the second-to-last or last letter position (if not already accented) + + # accent second-to-last letter, if vowel + if len(ending) > 1: + if ending[-2] in VOWEL_ACCENT_MAP: + ending = ( + ending[:-2] + + VOWEL_ACCENT_MAP[ending[-2]] + + ending[-1] + ) + # accent last letter, if vowel + if ending[-1] in VOWEL_ACCENT_MAP: + ending = ending[:-1] + VOWEL_ACCENT_MAP[ending[-1]] + + # update the replacement person ending with the modified ending + replacement_person_ending.endings[i] = ending + return replacement_person_ending return person_ending def split_reflexive(self, infinitive: str) -> Tuple[bool, str]: diff --git a/src/verbecc/core/inflectors/lang/fr/__init__.py b/src/verbecc/core/inflectors/lang/fr/__init__.py new file mode 100644 index 0000000..fb3bd0a --- /dev/null +++ b/src/verbecc/core/inflectors/lang/fr/__init__.py @@ -0,0 +1 @@ +from .inflector_fr import InflectorFr # type: ignore \ No newline at end of file diff --git a/verbecc/src/inflectors/lang/inflector_fr.py b/src/verbecc/core/inflectors/lang/fr/inflector_fr.py similarity index 90% rename from verbecc/src/inflectors/lang/inflector_fr.py rename to src/verbecc/core/inflectors/lang/fr/inflector_fr.py index 1083242..38666a0 100755 --- a/verbecc/src/inflectors/lang/inflector_fr.py +++ b/src/verbecc/core/inflectors/lang/fr/inflector_fr.py @@ -1,19 +1,19 @@ -from typing import Dict, List, Optional, Tuple - -from verbecc.src.conjugator.conjugation_object import ConjugationObjects -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.defs.types.mood import Mood, Moods -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.constants.grammar_defines import PERSONS -from verbecc.src.defs.types.tense import Tense, Tenses -from verbecc.src.defs.types.lang_specific_options import ( +from typing import Optional, Tuple + +from verbecc.core.conjugator.conjugation_object import ConjugationObjects +from verbecc.core.defs.types.gender import Gender +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.defs.types.mood import Mood, Moods +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.number import Number +from verbecc.core.defs.constants.grammar_defines import PERSONS +from verbecc.core.defs.types.tense import Tense, Tenses +from verbecc.core.defs.types.lang_specific_options import ( LangSpecificOptions, ) -from verbecc.src.inflectors.inflector import Inflector -from verbecc.src.utils import string_utils -from verbecc.src.defs.types.pronoun import Pronoun, Pronouns +from verbecc.core.inflectors.inflector import Inflector +from verbecc.core.utils import string_utils +from verbecc.core.defs.types.pronoun import Pronoun, Pronouns """ DR & MRS VANDERTRAMPP verbs @@ -60,7 +60,7 @@ def __init__( def get_lang(self) -> LangCodeISO639_1: return LangCodeISO639_1.fr - def get_verbs_that_start_with(self, query: str, max_results: int) -> List[str]: + def get_verbs_that_start_with(self, query: str, max_results: int) -> list[str]: query = query.lower() is_reflexive, query = self.split_reflexive(query) matches = self._verbs.get_verbs_that_start_with(query, max_results) @@ -74,7 +74,7 @@ def get_verbs_that_start_with(self, query: str, max_results: int) -> List[str]: def verb_can_be_reflexive(self, infinitive: str) -> bool: return ( - not self._is_impersonal_verb(infinitive) + not self.private_is_impersonal_verb(infinitive) and infinitive not in VERBS_THAT_CANNOT_BE_REFLEXIVE_OTHER_THAN_IMPERSONAL_VERBS ) @@ -100,7 +100,7 @@ def add_reflexive_pronoun(self, s: str) -> str: else: return "se " + s - def add_subjunctive_relative_pronoun(self, s: str, tense: Tense) -> str: + def add_subjunctive_relative_pronoun(self, s: str, tense: Tense | str | None) -> str: if string_utils.starts_with_vowel(s, h_is_vowel=True): return "qu'" + s else: @@ -119,13 +119,13 @@ def get_pronouns( number: Optional[Number] = None, gender: Optional[Gender] = None, imperative: bool = False, - ) -> List[Pronoun]: + ) -> list[Pronoun]: """ Returns a list of all pronouns matching the provided filters, in the typical order, with the default pronoun first. E.g. Person.Second, Number.Singular => ["tú", "vos"] """ - ret = [] + ret: list[Pronoun] = [] if (person is None or person == Person.First) and ( number is None or number == Number.Singular ): @@ -180,7 +180,7 @@ def make_pronoun_reflexive(self, pronoun: Pronoun) -> str: else: return pronoun + " se" - def get_tenses_conjugated_without_pronouns(self) -> List[Tense]: + def get_tenses_conjugated_without_pronouns(self) -> list[Tense]: return [ Tenses.fr.InfinitifPrésent, Tenses.fr.ParticipePresent, @@ -289,7 +289,7 @@ def compound_conjugation_not_applicable( def get_compound_conjugations_aux_verb_map( self, - ) -> Dict[Mood, Dict[Tense, Tuple[Mood, Tense]]]: + ) -> dict[Mood, dict[Tense, Tuple[Mood, Tense]]]: """ compound conjugations are formed using an auxiliary verb (aka helping verb) @@ -329,7 +329,7 @@ def _get_pronoun_suffix( ) -> str: return "-" + self.get_pronouns(person, number, gender)[0].replace("tu", "toi") - def _is_impersonal_verb(self, infinitive: str) -> bool: + def private_is_impersonal_verb(self, infinitive: str) -> bool: ret = False verb = self.find_verb_by_infinitive(infinitive) template = self.find_template(verb.template) diff --git a/src/verbecc/core/inflectors/lang/it/__init__.py b/src/verbecc/core/inflectors/lang/it/__init__.py new file mode 100644 index 0000000..dc9b184 --- /dev/null +++ b/src/verbecc/core/inflectors/lang/it/__init__.py @@ -0,0 +1 @@ +from .inflector_it import InflectorIt # type: ignore \ No newline at end of file diff --git a/verbecc/src/inflectors/lang/inflector_it.py b/src/verbecc/core/inflectors/lang/it/inflector_it.py similarity index 87% rename from verbecc/src/inflectors/lang/inflector_it.py rename to src/verbecc/core/inflectors/lang/it/inflector_it.py index 0b51604..b78670d 100755 --- a/verbecc/src/inflectors/lang/inflector_it.py +++ b/src/verbecc/core/inflectors/lang/it/inflector_it.py @@ -1,18 +1,18 @@ -from typing import Dict, List, Optional, Tuple - -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.defs.types.mood import Mood, Moods -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.tense import Tense, Tenses -from verbecc.src.defs.types.lang_specific_options import ( +from typing import Optional, Tuple + +from verbecc.core.defs.types.gender import Gender +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.defs.types.mood import Mood, Moods +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.number import Number +from verbecc.core.defs.types.tense import Tense, Tenses +from verbecc.core.defs.types.lang_specific_options import ( LangSpecificOptions, ) -from verbecc.src.conjugator.conjugation_object import ConjugationObjects -from verbecc.src.inflectors.inflector import Inflector -from verbecc.src.utils import string_utils -from verbecc.src.defs.types.pronoun import Pronoun, Pronouns +from verbecc.core.conjugator.conjugation_object import ConjugationObjects +from verbecc.core.inflectors.inflector import Inflector +from verbecc.core.utils import string_utils +from verbecc.core.defs.types.pronoun import Pronoun, Pronouns VERBS_CONJUGATED_WITH_ESSERE = [ "essere", @@ -73,7 +73,7 @@ def add_reflexive_pronoun(self, s: str) -> str: else: return "si " + s - def add_subjunctive_relative_pronoun(self, s: str, tense: Tense) -> str: + def add_subjunctive_relative_pronoun(self, s: str, tense: Tense | str | None) -> str: return "che " + s def get_pronoun_gender(self, pronoun: Pronoun) -> Optional[Gender]: @@ -89,8 +89,8 @@ def get_pronouns( number: Optional[Number] = None, gender: Optional[Gender] = None, imperative: bool = False, - ) -> List[Pronoun]: - ret = [] + ) -> list[Pronoun]: + ret: list[Pronoun] = [] if (person is None or person == Person.First) and ( number is None or number == Number.Singular ): @@ -140,7 +140,7 @@ def make_pronoun_reflexive(self, pronoun: Pronoun) -> str: else: return pronoun + " si" - def get_tenses_conjugated_without_pronouns(self) -> List[Tense]: + def get_tenses_conjugated_without_pronouns(self) -> list[Tense]: return [ Tenses.it.Affermativo, Tenses.it.negativo, @@ -181,7 +181,7 @@ def get_participle_tense(self) -> Tense: def get_compound_conjugations_aux_verb_map( self, - ) -> Dict[Mood, Dict[Tense, Tuple[Mood, Tense]]]: + ) -> dict[Mood, dict[Tense, Tuple[Mood, Tense]]]: return { Moods.it.Indicativo: { Tenses.it.PassatoProssimo: (Moods.it.Indicativo, Tenses.it.Presente), diff --git a/src/verbecc/core/inflectors/lang/pt/__init__.py b/src/verbecc/core/inflectors/lang/pt/__init__.py new file mode 100644 index 0000000..2acca73 --- /dev/null +++ b/src/verbecc/core/inflectors/lang/pt/__init__.py @@ -0,0 +1 @@ +from .inflector_pt import InflectorPt # type: ignore \ No newline at end of file diff --git a/verbecc/src/inflectors/lang/inflector_pt.py b/src/verbecc/core/inflectors/lang/pt/inflector_pt.py similarity index 92% rename from verbecc/src/inflectors/lang/inflector_pt.py rename to src/verbecc/core/inflectors/lang/pt/inflector_pt.py index bdd87c3..c42401e 100755 --- a/verbecc/src/inflectors/lang/inflector_pt.py +++ b/src/verbecc/core/inflectors/lang/pt/inflector_pt.py @@ -1,17 +1,17 @@ -from typing import Dict, List, Optional, Tuple - -from verbecc.src.conjugator.conjugation_object import ConjugationObjects -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.defs.types.mood import Mood, Moods -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.tense import Tense, Tenses -from verbecc.src.defs.types.lang_specific_options import ( +from typing import Optional, Tuple + +from verbecc.core.conjugator.conjugation_object import ConjugationObjects +from verbecc.core.defs.types.gender import Gender +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.defs.types.mood import Mood, Moods +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.number import Number +from verbecc.core.defs.types.tense import Tense, Tenses +from verbecc.core.defs.types.lang_specific_options import ( LangSpecificOptions, ) -from verbecc.src.inflectors.inflector import Inflector -from verbecc.src.defs.types.pronoun import Pronoun, Pronouns +from verbecc.core.inflectors.inflector import Inflector +from verbecc.core.defs.types.pronoun import Pronoun, Pronouns class InflectorPt(Inflector): @@ -26,7 +26,7 @@ def __init__( def get_lang(self) -> LangCodeISO639_1: return LangCodeISO639_1.pt - def add_subjunctive_relative_pronoun(self, s: str, tense: Tense) -> str: + def add_subjunctive_relative_pronoun(self, s: str, tense: Tense | str | None) -> str: if tense == Tenses.pt.Presente: return "que " + s elif tense == Tenses.pt.PretéritoImperfeito: @@ -85,8 +85,8 @@ def get_pronouns( number: Optional[Number] = None, gender: Optional[Gender] = None, imperative: bool = False, - ) -> List[Pronoun]: - ret = [] + ) -> list[Pronoun]: + ret : list[Pronoun] = [] if (person is None or person == Person.First) and ( number is None or number == Number.Singular ): @@ -153,7 +153,7 @@ def make_pronoun_reflexive(self, pronoun: Pronoun) -> str: else: return pronoun + " se" - def get_tenses_conjugated_without_pronouns(self) -> List[Tense]: + def get_tenses_conjugated_without_pronouns(self) -> list[Tense]: """ Many tenses conjugated without pronouns in Spanish are conjugated with pronouns in Portuguese. @@ -201,7 +201,7 @@ def get_participle_tense(self) -> Tense: def get_compound_conjugations_aux_verb_map( self, - ) -> Dict[Mood, Dict[Tense, Tuple[Mood, Tense]]]: + ) -> dict[Mood, dict[Tense, Tuple[Mood, Tense]]]: return { Moods.pt.Indicativo: { Tenses.pt.PretéritoPerfeitoComposto: ( @@ -293,7 +293,7 @@ def _get_pronoun_suffix( # e.g. "se eu me vestisse" # e.g. "quando eu me vestir" # also Imperativo Negativo (not currently supported) - def get_unhyphenated_reflexive_mood_tenses(self) -> List[Tuple[Mood, Tense]]: + def get_unhyphenated_reflexive_mood_tenses(self) -> list[Tuple[Mood, Tense]]: return [ (Moods.pt.Subjuntivo, Tenses.pt.Presente), (Moods.pt.Subjuntivo, Tenses.pt.PretéritoImperfeito), diff --git a/src/verbecc/core/inflectors/lang/ro/__init__.py b/src/verbecc/core/inflectors/lang/ro/__init__.py new file mode 100644 index 0000000..25ac28d --- /dev/null +++ b/src/verbecc/core/inflectors/lang/ro/__init__.py @@ -0,0 +1 @@ +from .inflector_ro import InflectorRo # type: ignore \ No newline at end of file diff --git a/verbecc/src/inflectors/lang/inflector_ro.py b/src/verbecc/core/inflectors/lang/ro/inflector_ro.py similarity index 92% rename from verbecc/src/inflectors/lang/inflector_ro.py rename to src/verbecc/core/inflectors/lang/ro/inflector_ro.py index 518de26..6ce01f6 100755 --- a/verbecc/src/inflectors/lang/inflector_ro.py +++ b/src/verbecc/core/inflectors/lang/ro/inflector_ro.py @@ -1,17 +1,17 @@ -from typing import Dict, List, Optional, Tuple - -from verbecc.src.conjugator.conjugation_object import ConjugationObjects -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.defs.types.mood import Mood, Moods -from verbecc.src.defs.types.tense import Tense, Tenses -from verbecc.src.defs.types.lang_specific_options import ( +from typing import Optional, Tuple + +from verbecc.core.conjugator.conjugation_object import ConjugationObjects +from verbecc.core.defs.types.gender import Gender +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.number import Number +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.defs.types.mood import Mood, Moods +from verbecc.core.defs.types.tense import Tense, Tenses +from verbecc.core.defs.types.lang_specific_options import ( LangSpecificOptions, ) -from verbecc.src.inflectors.inflector import Inflector -from verbecc.src.defs.types.pronoun import Pronoun, Pronouns +from verbecc.core.inflectors.inflector import Inflector +from verbecc.core.defs.types.pronoun import Pronoun, Pronouns class InflectorRo(Inflector): @@ -23,7 +23,7 @@ def __init__( def get_lang(self) -> LangCodeISO639_1: return LangCodeISO639_1.ro - def add_subjunctive_relative_pronoun(self, s: str, tense: Tense) -> str: + def add_subjunctive_relative_pronoun(self, s: str, tense: Tense | str | None) -> str: """ :param s: a conjugation, perhaps with a pronoun e.g. "eu fac" :tense s: the verb tense @@ -48,7 +48,7 @@ def add_adverb_if_applicable(self, s: str, mood: Mood, tense: Tense) -> str: return "nu " + s return s - """TODO: There are two types of reflexive verbs in Romanian: + """TODO: There are two types of reflexive verbs in Romanian: preceded by the reflexive pronouns “se” (in the accusative) and “și” (in the dative). """ @@ -65,8 +65,8 @@ def get_pronouns( number: Optional[Number] = None, gender: Optional[Gender] = None, imperative: bool = False, - ) -> List[Pronoun]: - ret = [] + ) -> list[Pronoun]: + ret : list[Pronoun] = [] if (person is None or person == Person.First) and ( number is None or number == Number.Singular ): @@ -121,7 +121,7 @@ def make_pronoun_reflexive(self, pronoun: Pronoun) -> str: else: return pronoun + " se" - def get_tenses_conjugated_without_pronouns(self) -> List[Tense]: + def get_tenses_conjugated_without_pronouns(self) -> list[Tense]: return [ Tenses.ro.Participiu, Tenses.ro.Afirmativ, @@ -162,7 +162,7 @@ def get_participle_tense(self) -> Tense: def get_compound_conjugations_aux_verb_map( self, - ) -> Dict[Mood, Dict[Tense, Tuple[Mood, Tense]]]: + ) -> dict[Mood, dict[Tense, Tuple[Mood, Tense]]]: """ TODO: those last three don't actually use an auxiliary verb, refactor to make aux verb optional @@ -202,8 +202,8 @@ def compound_primary_verb_conjugation_uses_infinitive( return False def modify_aux_verb_conj_if_applicable( - self, aux_conj: List[str], mood: Mood, tense: Tense - ) -> List[str]: + self, aux_conj: list[str], mood: Mood, tense: Tense + ) -> list[str]: """E.g. for Romanian conditional present 'eu aş avea, tu ai avea, el ar avea, ...' and also the Romanian conditional present e.g. 'eu aş fi avut, tu ai fi avut, ...' although the ' fi' is added by add_compound_aux_verb_suffix_if_applicable diff --git a/verbecc/src/mlconjug/__init__.py b/src/verbecc/core/mlconjug/__init__.py similarity index 100% rename from verbecc/src/mlconjug/__init__.py rename to src/verbecc/core/mlconjug/__init__.py diff --git a/verbecc/src/mlconjug/data_set.py b/src/verbecc/core/mlconjug/data_set.py similarity index 81% rename from verbecc/src/mlconjug/data_set.py rename to src/verbecc/core/mlconjug/data_set.py index 80f69ba..2ea2a10 100644 --- a/verbecc/src/mlconjug/data_set.py +++ b/src/verbecc/core/mlconjug/data_set.py @@ -1,8 +1,7 @@ -from typing import Dict, List from collections import defaultdict import random -from verbecc.src.mlconjug.mltypes import VerbTemplatePair +from verbecc.core.mlconjug.mltypes import VerbTemplatePair class DataSet: @@ -11,7 +10,7 @@ class DataSet: | Defines helper methodss for managing Machine Learning tasks like constructing a training and testing set. """ - def __init__(self, verb_template_pairs: List[VerbTemplatePair]) -> None: + def __init__(self, verb_template_pairs: list[VerbTemplatePair]) -> None: self.verbs = [pair[0] for pair in verb_template_pairs] self.templates = sorted(set([pair[1] for pair in verb_template_pairs])) self.dict_conjug = self._construct_dict_conjug(verb_template_pairs) @@ -19,8 +18,8 @@ def __init__(self, verb_template_pairs: List[VerbTemplatePair]) -> None: return def _construct_dict_conjug( - self, verb_template_pairs: List[VerbTemplatePair] - ) -> Dict[str, List[str]]: + self, verb_template_pairs: list[VerbTemplatePair] + ) -> dict[str, list[str]]: """ | Populates the dictionary containing the conjugation templates. | Populates the lists containing the verbs and their templates. @@ -30,7 +29,7 @@ def _construct_dict_conjug( :return: defaultdict. defaultdict mapping each template to one or more verbs e.g. {'aim:er': ['abaisser', ...]} """ - ret = defaultdict(list) + ret: defaultdict[str, list[str]] = defaultdict(list) random.shuffle(verb_template_pairs) for verb, template in verb_template_pairs: ret[template].append(verb) @@ -55,8 +54,8 @@ def _split_test_train(self, threshold: int = 8, proportion: float = 0.5) -> None ) self.min_threshold = threshold self.split_proportion = proportion - train_set: List[VerbTemplatePair] = [] - test_set: List[VerbTemplatePair] = [] + train_set: list[VerbTemplatePair] = [] + test_set: list[VerbTemplatePair] = [] for template, lverbs in self.dict_conjug.items(): if len(lverbs) <= threshold: for verb in lverbs: @@ -69,11 +68,11 @@ def _split_test_train(self, threshold: int = 8, proportion: float = 0.5) -> None test_set.append((verb, template)) random.shuffle(train_set) random.shuffle(test_set) - self.train_input: List[str] = [elmt[0] for elmt in train_set] - self.train_labels: List[int] = [ + self.train_input: list[str] = [elmt[0] for elmt in train_set] + self.train_labels: list[int] = [ self.templates.index(elmt[1]) for elmt in train_set ] - self.test_input: List[str] = [elmt[0] for elmt in test_set] - self.test_labels: List[int] = [ + self.test_input: list[str] = [elmt[0] for elmt in test_set] + self.test_labels: list[int] = [ self.templates.index(elmt[1]) for elmt in test_set ] diff --git a/verbecc/src/mlconjug/feature_extract.py b/src/verbecc/core/mlconjug/feature_extract.py similarity index 93% rename from verbecc/src/mlconjug/feature_extract.py rename to src/verbecc/core/mlconjug/feature_extract.py index 71450f9..19ffb88 100644 --- a/verbecc/src/mlconjug/feature_extract.py +++ b/src/verbecc/core/mlconjug/feature_extract.py @@ -1,13 +1,13 @@ -from typing import List, Tuple +from typing import Tuple import re -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.defs.constants.grammar_defines import ALPHABET +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.defs.constants.grammar_defines import ALPHABET def extract_verb_features( verb: str, lang: LangCodeISO639_1, ngram_range: Tuple[int, int] -) -> List[str]: +) -> list[str]: """ | Custom Vectorizer optimized for extracting verbs features. | The Vectorizer subclasses sklearn.feature_extraction.text.CountVectorizer diff --git a/verbecc/src/mlconjug/mltypes.py b/src/verbecc/core/mlconjug/mltypes.py similarity index 100% rename from verbecc/src/mlconjug/mltypes.py rename to src/verbecc/core/mlconjug/mltypes.py diff --git a/verbecc/src/mlconjug/model.py b/src/verbecc/core/mlconjug/model.py similarity index 78% rename from verbecc/src/mlconjug/model.py rename to src/verbecc/core/mlconjug/model.py index 33449c7..69a6232 100644 --- a/verbecc/src/mlconjug/model.py +++ b/src/verbecc/core/mlconjug/model.py @@ -1,22 +1,21 @@ -from typing import List from functools import partial +from typing import Any, cast import numpy as np from sklearn.feature_selection import SelectFromModel -from sklearn.feature_extraction.text import CountVectorizer from sklearn.linear_model import SGDClassifier from sklearn.svm import LinearSVC from sklearn.pipeline import Pipeline -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.mlconjug.mltypes import ( +from verbecc.core.mlconjug.mltypes import ( Classifier, Vectorizor, FeatureSelector, CountVectorizer, ) -from verbecc.src.mlconjug.feature_extract import extract_verb_features +from verbecc.core.mlconjug.feature_extract import extract_verb_features class Model: @@ -67,9 +66,12 @@ def __init__( return def __repr__(self) -> str: - return "{0}.{1}({2}, {3}, {4})".format(__name__, self.__class__.__name__, *sorted(self.pipeline.named_steps)) + step_names = ", ".join( + sorted(cast(dict[str, object], self.pipeline.named_steps).keys()) + ) + return f"{__name__}.{self.__class__.__name__}({step_names})" - def train(self, samples: List[str], labels: List[int]) -> None: + def train(self, samples: list[str], labels: list[int]) -> None: """ Trains the pipeline on the supplied samples and labels. @@ -79,10 +81,10 @@ def train(self, samples: List[str], labels: List[int]) -> None: List of verb template indices. """ - self.pipeline = self.pipeline.fit(samples, labels) + self.pipeline = cast(Any, self.pipeline).fit(samples, labels) return - def predict(self, verbs: List[str]) -> List[np.int64]: + def predict(self, verbs: list[str]) -> list[np.int64]: """ Predicts the conjugation class of the provided list of verbs. @@ -92,5 +94,5 @@ def predict(self, verbs: List[str]) -> List[np.int64]: List of incices of predicted conjugation templates """ - prediction = self.pipeline.predict(verbs) - return list(prediction) + prediction = cast(Any, self.pipeline).predict(verbs) + return cast(list[np.int64], list(prediction)) diff --git a/verbecc/src/mlconjug/model_utils.py b/src/verbecc/core/mlconjug/model_utils.py similarity index 89% rename from verbecc/src/mlconjug/model_utils.py rename to src/verbecc/core/mlconjug/model_utils.py index d6f227b..c2f1808 100644 --- a/verbecc/src/mlconjug/model_utils.py +++ b/src/verbecc/core/mlconjug/model_utils.py @@ -1,12 +1,12 @@ from typing import Optional -from importlib_resources import as_file, files +from importlib.resources import as_file, files import os import pickle from zipfile import ZipFile -from verbecc.src.utils.logging_utils import LoggingUtils -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.mlconjug.model import Model +from verbecc.core.utils.logging_utils import LoggingUtils +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.mlconjug.model import Model _logger = LoggingUtils.get_logger(__name__) diff --git a/verbecc/src/mlconjug/predictor.py b/src/verbecc/core/mlconjug/predictor.py similarity index 61% rename from verbecc/src/mlconjug/predictor.py rename to src/verbecc/core/mlconjug/predictor.py index 87e6965..d79f85d 100755 --- a/verbecc/src/mlconjug/predictor.py +++ b/src/verbecc/core/mlconjug/predictor.py @@ -1,18 +1,18 @@ -from typing import List, Tuple +from typing import Callable, Tuple, cast import numpy as np -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.mlconjug.model import Model -from verbecc.src.mlconjug.model_utils import load_model, save_model -from verbecc.src.mlconjug.data_set import DataSet -from verbecc.src.mlconjug.mltypes import VerbTemplatePair -from verbecc.src.utils.logging_utils import LoggingUtils +from verbecc.core.mlconjug.model import Model +from verbecc.core.mlconjug.model_utils import load_model, save_model +from verbecc.core.mlconjug.data_set import DataSet +from verbecc.core.mlconjug.mltypes import VerbTemplatePair +from verbecc.core.utils.logging_utils import LoggingUtils class TemplatePredictor: def __init__( - self, verb_template_pairs: List[VerbTemplatePair], lang: LangCodeISO639_1 + self, verb_template_pairs: list[VerbTemplatePair], lang: LangCodeISO639_1 ) -> None: self._logger = LoggingUtils.get_logger(self.__class__.__name__) self.data_set = DataSet(verb_template_pairs) @@ -31,11 +31,15 @@ def __init__( return def predict(self, verb: str) -> Tuple[str, float]: - predict_results: List[np.int64] = self.model.predict([verb]) + predict_results: list[np.int64] = self.model.predict([verb]) if len(predict_results) == 0: raise Exception("Template prediction failed") prediction = predict_results[0] - predict_proba = self.model.pipeline.predict_proba([verb]) + predict_proba_fn = cast( + Callable[[list[str]], np.ndarray], + getattr(self.model.pipeline, "predict_proba"), + ) + predict_proba = predict_proba_fn([verb]) prediction_score = predict_proba[0][prediction] template = self.data_set.templates[prediction] return (template, prediction_score) diff --git a/verbecc/src/inflectors/lang/__init__.py b/src/verbecc/core/parsers/__init__.py similarity index 100% rename from verbecc/src/inflectors/lang/__init__.py rename to src/verbecc/core/parsers/__init__.py diff --git a/src/verbecc/core/parsers/conjugation_template_parser.py b/src/verbecc/core/parsers/conjugation_template_parser.py new file mode 100755 index 0000000..130a8bb --- /dev/null +++ b/src/verbecc/core/parsers/conjugation_template_parser.py @@ -0,0 +1,45 @@ +from typing import Optional + +from verbecc.core.defs.types.data.conjugation_template import ConjugationTemplate +from verbecc.core.defs.types.data.mood_template import MoodTemplate +from verbecc.core.defs.types.exceptions import ConjugationTemplateError +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.defs.types.mood import Mood, MoodFactory +from verbecc.core.parsers.mood_template_parser import MoodTemplateParser +from verbecc.core.parsers.parser import Parser +from verbecc.core.defs.types.data.xml_types import XmlElement +from verbecc.core.utils.xml_utils import xml_element_get_attr, xml_element_get_tag, xml_element_to_string + +class ConjugationTemplateParser(Parser): + def __init__(self, lang: Lang) -> None: + self.lang = lang + + def parse(self, elem: Optional[XmlElement] = None) -> ConjugationTemplate: + if elem is None: + raise ValueError("elem must not be None") + tag = xml_element_get_tag(elem) + if tag != "template": + raise ConjugationTemplateError("Unexpected element") + try: + name_attrib = xml_element_get_attr(elem, "name", default=None) + name = str(name_attrib) + mood_templates: dict[Mood, MoodTemplate] = {} + for mood_elem in elem: + mood_template = MoodTemplateParser(lang=self.lang).parse(mood_elem) + mood = MoodFactory.from_string(self.lang, xml_element_get_tag(mood_elem).lower()) + mood_templates[mood] = mood_template + modify_stem = "" + modify_stem_attrib = xml_element_get_attr(elem, "modify-stem", default=None) + if modify_stem_attrib is not None: + modify_stem = str(modify_stem_attrib) + if modify_stem not in ("strip-accents"): + raise ConjugationTemplateError( + f"Invalid 'modify-stem' attribute value '{modify_stem}'" + ) + else: + modify_stem = "" + return ConjugationTemplate(self.lang, name, mood_templates, modify_stem) + except AttributeError as e: + raise ConjugationTemplateError( + "Error parsing {}: {}".format(xml_element_to_string(elem), str(e)) + ) diff --git a/verbecc/src/parsers/conjugations_parser.py b/src/verbecc/core/parsers/conjugations_parser.py similarity index 73% rename from verbecc/src/parsers/conjugations_parser.py rename to src/verbecc/core/parsers/conjugations_parser.py index f41746a..0bca1f7 100755 --- a/verbecc/src/parsers/conjugations_parser.py +++ b/src/verbecc/core/parsers/conjugations_parser.py @@ -1,39 +1,29 @@ -from __future__ import print_function - -from bisect import bisect_left - -# try: from lxml import etree -# except ImportError: -# import xml.etree.ElementTree as etree -from importlib_resources import as_file, files - -# import gzip -import os +from importlib.resources import as_file, files -# import tempfile -from typing import cast, List, Optional +from typing import cast, Optional -from verbecc.src.defs.types.data.conjugation_template import ConjugationTemplate -from verbecc.src.defs.types.data.conjugations import Conjugations -from verbecc.src.defs.types.exceptions import ConjugationsParserError -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.parsers.conjugation_template_parser import ConjugationTemplateParser -from verbecc.src.parsers.parser import Parser +from verbecc.core.defs.types.data.conjugation_template import ConjugationTemplate +from verbecc.core.defs.types.data.conjugations import Conjugations +from verbecc.core.defs.types.data.xml_types import XmlElement +from verbecc.core.defs.types.exceptions import ConjugationsParserError +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.parsers.conjugation_template_parser import ConjugationTemplateParser +from verbecc.core.parsers.parser import Parser class ConjugationsParser(Parser): def __init__(self, lang: LangCodeISO639_1 = LangCodeISO639_1.fr) -> None: self.lang = lang - def parse(self, elem: Optional[etree._Element] = None) -> Conjugations: + def parse(self, elem: Optional[XmlElement] = None) -> Conjugations: if elem is not None: # this is the the only Parser that doesn't use the elem argument, # of the Parser.parser() interface, so this method must implement # it even though it isn't used. raise ValueError("elem must be None") - templates: List[ConjugationTemplate] = [] + templates: list[ConjugationTemplate] = [] parser = etree.XMLParser( dtd_validation=True, encoding="utf-8", remove_blank_text=True, remove_comments=True # type: ignore ) @@ -75,7 +65,7 @@ def parse(self, elem: Optional[etree._Element] = None) -> Conjugations: if child.tag == "template": templates.append( ConjugationTemplateParser(lang=self.lang).parse( - cast(etree._Element, child) + cast(XmlElement, child) ) ) return Conjugations(self.lang, sorted(templates, key=lambda x: x.name)) diff --git a/src/verbecc/core/parsers/mood_template_parser.py b/src/verbecc/core/parsers/mood_template_parser.py new file mode 100755 index 0000000..5e6f54e --- /dev/null +++ b/src/verbecc/core/parsers/mood_template_parser.py @@ -0,0 +1,29 @@ +from typing import Optional + +from verbecc.core.defs.types.data.mood_template import MoodTemplate +from verbecc.core.defs.types.data.tense_template import TenseTemplate +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.defs.types.mood import MoodFactory +from verbecc.core.defs.types.tense import TenseFactory +from verbecc.core.defs.types.tense import Tense +from verbecc.core.parsers.parser import Parser +from verbecc.core.parsers.tense_template_parser import TenseTemplateParser + +from verbecc.core.defs.types.data.xml_types import XmlElement +from verbecc.core.utils.xml_utils import xml_element_get_tag + +class MoodTemplateParser(Parser): + def __init__(self, lang: Lang) -> None: + self.lang = lang + + def parse(self, elem: Optional[XmlElement] = None) -> MoodTemplate: + if elem is None: + raise ValueError + mood = MoodFactory.from_string(self.lang, xml_element_get_tag(elem).lower()) + tense_templates: dict[Tense, TenseTemplate] = {} + for tense_elem in elem: + tense = TenseFactory.from_string(self.lang, xml_element_get_tag(tense_elem).lower()) + tense_templates[tense] = TenseTemplateParser( + lang=self.lang, mood=mood + ).parse(tense_elem) + return MoodTemplate(self.lang, mood, tense_templates) diff --git a/src/verbecc/core/parsers/parser.py b/src/verbecc/core/parsers/parser.py new file mode 100644 index 0000000..998542c --- /dev/null +++ b/src/verbecc/core/parsers/parser.py @@ -0,0 +1,11 @@ +from abc import ABC, abstractmethod +from typing import Optional + +from verbecc.core.defs.types.data.element import Element +from verbecc.core.defs.types.data.xml_types import XmlElement + +class Parser(ABC): + + @abstractmethod + def parse(self, elem: Optional[XmlElement] = None) -> Element: + pass diff --git a/verbecc/src/parsers/person_ending_parser.py b/src/verbecc/core/parsers/person_ending_parser.py similarity index 73% rename from verbecc/src/parsers/person_ending_parser.py rename to src/verbecc/core/parsers/person_ending_parser.py index a4f52bb..5adae9e 100755 --- a/verbecc/src/parsers/person_ending_parser.py +++ b/src/verbecc/core/parsers/person_ending_parser.py @@ -1,12 +1,13 @@ -from lxml import etree -from typing import List, Optional +from typing import Optional -from verbecc.src.utils.logging_utils import LoggingUtils -from verbecc.src.defs.types.data.person_ending import PersonEnding -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.gender import Gender -from verbecc.src.parsers.parser import Parser +from verbecc.core.defs.types.data.xml_types import XmlElement +from verbecc.core.utils.logging_utils import LoggingUtils +from verbecc.core.defs.types.data.person_ending import PersonEnding +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.number import Number +from verbecc.core.defs.types.gender import Gender +from verbecc.core.parsers.parser import Parser +from verbecc.core.utils.xml_utils import xml_element_findall, xml_element_get_text class PersonEndingParser(Parser): @@ -34,7 +35,7 @@ def __init__(self) -> None: def parse( self, - elem: Optional[etree._Element] = None, + elem: Optional[XmlElement] = None, person: Optional[Person] = None, number: Optional[Number] = None, gender: Optional[Gender] = None, @@ -60,13 +61,14 @@ def parse( """ if elem is None: raise ValueError("elem must not be None") - endings: List[str] = [] - i_elems = elem.findall("i", None) + endings: list[str] = [] + i_elems = xml_element_findall(elem, "i") if len(i_elems) == 0: self._logger.debug("Empty

element in conjugation template") for i_elem in i_elems: ending = str("") - if i_elem.text is not None: - ending += str(i_elem.text) + i_elem_text = xml_element_get_text(i_elem) + if i_elem_text is not None: + ending += i_elem_text endings.append(ending) return PersonEnding(person, number, gender, endings) diff --git a/verbecc/src/parsers/tense_template_parser.py b/src/verbecc/core/parsers/tense_template_parser.py similarity index 79% rename from verbecc/src/parsers/tense_template_parser.py rename to src/verbecc/core/parsers/tense_template_parser.py index b6f4cd7..6db565d 100755 --- a/verbecc/src/parsers/tense_template_parser.py +++ b/src/verbecc/core/parsers/tense_template_parser.py @@ -1,16 +1,17 @@ -from lxml import etree -from typing import List, Optional +from typing import Optional -from verbecc.src.utils.logging_utils import LoggingUtils -from verbecc.src.defs.constants import grammar_defines -from verbecc.src.defs.constants.localization import xmood -from verbecc.src.defs.types.data.person_ending import PersonEnding -from verbecc.src.defs.types.data.tense_template import TenseTemplate -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang -from verbecc.src.defs.types.mood import Mood, Moods -from verbecc.src.defs.types.tense import Tense, TenseFactory -from verbecc.src.parsers.parser import Parser -from verbecc.src.parsers.person_ending_parser import PersonEndingParser +from verbecc.core.defs.types.data.xml_types import XmlElement +from verbecc.core.utils.logging_utils import LoggingUtils +from verbecc.core.defs.constants import grammar_defines +from verbecc.core.defs.constants.localization import xmood +from verbecc.core.defs.types.data.person_ending import PersonEnding +from verbecc.core.defs.types.data.tense_template import TenseTemplate +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.defs.types.mood import Mood, Moods +from verbecc.core.defs.types.tense import TenseFactory +from verbecc.core.parsers.parser import Parser +from verbecc.core.parsers.person_ending_parser import PersonEndingParser +from verbecc.core.utils.xml_utils import xml_element_get_tag, xml_element_to_string, xml_element_findall class TenseTemplateParser(Parser): @@ -41,7 +42,7 @@ def __init__(self, lang: Lang, mood: Mood) -> None: self.lang = lang self.mood = mood - def parse(self, elem: Optional[etree._Element] = None) -> TenseTemplate: + def parse(self, elem: Optional[XmlElement] = None) -> TenseTemplate: """ Normally each

elem defines six grammatical persons: (see grammar_defines.PERSONS) @@ -93,12 +94,12 @@ def parse(self, elem: Optional[etree._Element] = None) -> TenseTemplate: """ if elem is None: raise ValueError("elem must not be None") - self.tense = TenseFactory.from_string(self.lang, elem.tag) - person_endings: List[PersonEnding] = [] + self.tense = TenseFactory.from_string(self.lang, xml_element_get_tag(elem)) + person_endings: list[PersonEnding] = [] person_num = 0 participle_inflections = grammar_defines.PARTICIPLE_INFLECTIONS[self.lang] imperative_persons = grammar_defines.IMPERATIVE_PERSONS[self.lang] - p_elems = elem.findall("p", namespaces=None) + p_elems = xml_element_findall(elem, "p") for p_elem in p_elems: gender = None person = None @@ -118,7 +119,7 @@ def parse(self, elem: Optional[etree._Element] = None) -> TenseTemplate: self._logger.warning( f"Unexpected number of participle inflections {len(p_elems)} " + f"on tense template for language {self.lang}: " - + f"{[etree.tostring(p) for p in p_elems]}" + + f"{[xml_element_to_string(p) for p in p_elems]}" ) elif self.mood == xmood(self.lang, Moods.en.Imperative): if len(p_elems) == len(imperative_persons): @@ -127,7 +128,7 @@ def parse(self, elem: Optional[etree._Element] = None) -> TenseTemplate: self._logger.warning( f"Unexpected number of imperative person inflections {len(p_elems)} " + f"on tense template for language {self.lang}: " - + f"{[etree.tostring(p) for p in p_elems]}" + + f"{[xml_element_to_string(p) for p in p_elems]}" ) else: # normal case: all persons diff --git a/src/verbecc/core/parsers/verb_parser.py b/src/verbecc/core/parsers/verb_parser.py new file mode 100755 index 0000000..97e42ca --- /dev/null +++ b/src/verbecc/core/parsers/verb_parser.py @@ -0,0 +1,42 @@ +from typing import Optional + +from verbecc.core.defs.types.data.verb import Verb +from verbecc.core.defs.types.data.xml_types import XmlElement +from verbecc.core.defs.types.exceptions import VerbsParserError +from verbecc.core.parsers.parser import Parser +from verbecc.core.utils.xml_utils import xml_element_get_tag, xml_element_find, xml_element_get_text, xml_element_to_string + + +class VerbParser(Parser): + def __init__(self) -> None: + pass + + def parse(self, elem: Optional[XmlElement] = None) -> Verb: + if elem is None: + raise ValueError("elem must not be None") + infinitive = "" + template = "" + translation_en = "" + if xml_element_get_tag(elem) != "v": + raise VerbsParserError("Unexpected element") + try: + infinitive : str = "" + e = xml_element_find(elem, "i") + if e is not None: + txt : str | None = xml_element_get_text(e) + infinitive = txt if txt is not None else "" + template : str = "" + e = xml_element_find(elem, "t") + if e is not None: + txt : str | None = xml_element_get_text(e) + template = txt if txt is not None else "" + translation_en : str = "" + e = xml_element_find(elem, "en") + if e is not None: + txt : str | None = xml_element_get_text(e) + translation_en = txt if txt is not None else "" + except AttributeError as e: + raise VerbsParserError( + "Error parsing {}: {}".format(xml_element_to_string(elem), str(e)) + ) + return Verb(infinitive, template, translation_en) diff --git a/src/verbecc/core/parsers/verbs_parser.py b/src/verbecc/core/parsers/verbs_parser.py new file mode 100755 index 0000000..c43d0dc --- /dev/null +++ b/src/verbecc/core/parsers/verbs_parser.py @@ -0,0 +1,40 @@ +from io import BytesIO +try: + # Python 3.11+ + from importlib.resources.abc import Traversable # type: ignore +except ImportError: + # Python 3.10 and earlier + from importlib.abc import Traversable # type: ignore +from lxml import etree +from importlib.resources import files + + +from verbecc.core.defs.types.data.verb import Verb +from verbecc.core.defs.types.data.verbs import Verbs +from verbecc.core.defs.types.exceptions import VerbsParserError +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 +from verbecc.core.parsers.verb_parser import VerbParser + + +class VerbsParser: + def __init__(self, lang: LangCodeISO639_1 = LangCodeISO639_1.fr) -> None: + self.lang = lang + + def parse(self) -> Verbs: + ret: list[Verb] = [] + parser = etree.XMLParser(encoding="utf-8", remove_blank_text=True, remove_comments=True) # type: ignore + source: Traversable = files("verbecc.data.xml.verbs").joinpath( + "verbs-{}.xml".format(self.lang) + ) + with BytesIO(source.read_bytes()) as fp: # type: ignore + tree = etree.parse(fp, parser) # type: ignore + root = tree.getroot() + root_tag = "verbs-{}".format(self.lang) + if root.tag != root_tag: + raise VerbsParserError("Root XML Tag {} Not Found".format(root_tag)) + for child in root: + if child.tag == "v": + ret.append(VerbParser().parse(child)) # type: ignore + + ret = sorted(ret, key=lambda v: v.infinitive) + return Verbs(self.lang, ret) diff --git a/verbecc/src/parsers/__init__.py b/src/verbecc/core/utils/__init__.py similarity index 100% rename from verbecc/src/parsers/__init__.py rename to src/verbecc/core/utils/__init__.py diff --git a/verbecc/src/utils/__init__.py b/src/verbecc/core/utils/config/__init__.py similarity index 100% rename from verbecc/src/utils/__init__.py rename to src/verbecc/core/utils/config/__init__.py diff --git a/verbecc/src/utils/config/base_config_util.py b/src/verbecc/core/utils/config/base_config_util.py similarity index 54% rename from verbecc/src/utils/config/base_config_util.py rename to src/verbecc/core/utils/config/base_config_util.py index 64c7858..cdc1da9 100644 --- a/verbecc/src/utils/config/base_config_util.py +++ b/src/verbecc/core/utils/config/base_config_util.py @@ -1,5 +1,11 @@ from abc import abstractmethod -from importlib_resources import abc, as_file, files +from importlib.resources import as_file, files +try: + # Python 3.11+ + from importlib.resources.abc import Traversable # type: ignore +except ImportError: + # Python 3.10 and earlier + from importlib.abc import Traversable # type: ignore from pathlib import Path from typing import Optional, TextIO, TypeVar, Generic @@ -21,24 +27,9 @@ def find_config_yaml_resource_path(self) -> None: try: # source path self._yaml_resource_path = files("verbecc.config") / self.yaml_filename - except KeyError as ex0: + except KeyError: # unit-tests path - try: - self._yaml_resource_path = files("verbecc.verbecc.config") / self.yaml_filename - except TypeError as ex1: - # python <=3.9 hack - try: - dir_path = Path(__file__).parent.parent.parent - self._yaml_resource_path = dir_path / "config" / self.yaml_filename - while self._yaml_resource_path is None or not Path.exists(self._yaml_resource_path): - self._yaml_resource_path = dir_path.parent / "config" / self.yaml_filename - - except Exception as ex2: - msg = ( - "You are likely running an incompatible python version. " - + f"_yaml_resource_path={self._yaml_resource_path} ex0={ex0} ex1={ex1} ex2={ex2}" - ) - raise Exception(msg) + self._yaml_resource_path = files("verbecc.verbecc.config") / self.yaml_filename def load_config(self) -> T: """Loads and returns the configuration.""" @@ -52,16 +43,10 @@ def _load_config_yaml_resource(self) -> T: ret: Optional[T] = None if self._yaml_resource_path is None: raise Exception(f"Failed to load yaml config file {self.yaml_filename}") - elif isinstance(self._yaml_resource_path, abc.Traversable): # type: ignore - # python >= 3.10 code path: + elif isinstance(self._yaml_resource_path, Traversable): # type: ignore with as_file(self._yaml_resource_path) as path: with path.open("r", encoding="utf-8") as f: ret = self._load_config_from_filestream(f) - elif isinstance(self._yaml_resource_path, Path): - # python <= 3.9 code path: - path = self._yaml_resource_path - with open(path, "r", encoding="utf-8") as f: - ret = self._load_config_from_filestream(f) if ret is not None: return ret else: diff --git a/verbecc/src/utils/config/logging_config_util.py b/src/verbecc/core/utils/config/logging_config_util.py similarity index 70% rename from verbecc/src/utils/config/logging_config_util.py rename to src/verbecc/core/utils/config/logging_config_util.py index 011b3c0..b6e961f 100644 --- a/verbecc/src/utils/config/logging_config_util.py +++ b/src/verbecc/core/utils/config/logging_config_util.py @@ -1,13 +1,11 @@ from dataclasses import asdict -from importlib_resources import abc, as_file, files -from pathlib import Path -from typing import Dict, Optional, TextIO +from typing import Any, TextIO, cast import logging import logging.config import yaml -from verbecc.src.utils.config.base_config_util import BaseConfigUtil -from verbecc.src.defs.types.config.logging.logging_config import ( +from verbecc.core.utils.config.base_config_util import BaseConfigUtil +from verbecc.core.defs.types.config.logging.logging_config import ( LoggingConfig, LoggerConfig, FormatterConfig, @@ -15,7 +13,7 @@ StreamHandlerConfig, FileHandlerConfig, ) -from verbecc.src.utils.dict_utils import DictUtils +from verbecc.core.utils.dict_utils import DictUtils class LoggingConfigUtil(BaseConfigUtil[LoggingConfig]): @@ -35,7 +33,7 @@ def apply_config(self, logging_config: LoggingConfig) -> None: try: data = asdict(logging_config) data = DictUtils.unmarshall_keys_recursive(data) - logging.config.dictConfig(data) + logging.config.dictConfig(cast(dict[str, object], data)) except ValueError as ex: raise ex @@ -44,12 +42,17 @@ def _load_config_from_filestream(self, filestream: TextIO) -> LoggingConfig: Manually convert the dictionary to our dataclass structure This part can be simplified with libraries like `dacite` or `marshmallow-dataclass` """ - data = yaml.safe_load(filestream) - data = DictUtils.marshall_keys_recursive(data, ["class"]) + loaded_data = yaml.safe_load(filestream) + data = cast(dict[str, Any], loaded_data) if isinstance(loaded_data, dict) else {} + data = cast( + dict[str, Any], DictUtils.marshall_keys_recursive(data, ["class"]) + ) + formatters_data = cast(dict[str, dict[str, Any]], data.get("formatters", {})) formatters = { - name: FormatterConfig(**d) for name, d in data.get("formatters", {}).items() + name: FormatterConfig(**formatter_data) + for name, formatter_data in formatters_data.items() } - handlers: Dict[str, HandlerConfig] = {} + handlers: dict[str, HandlerConfig] = {} for name, d in data.get("handlers", {}).items(): if name == "consoleHandler": handlers[name] = StreamHandlerConfig(**d) diff --git a/verbecc/src/utils/config/verbecc_config_util.py b/src/verbecc/core/utils/config/verbecc_config_util.py similarity index 65% rename from verbecc/src/utils/config/verbecc_config_util.py rename to src/verbecc/core/utils/config/verbecc_config_util.py index e4ea08b..fd410a1 100644 --- a/verbecc/src/utils/config/verbecc_config_util.py +++ b/src/verbecc/core/utils/config/verbecc_config_util.py @@ -1,7 +1,7 @@ from typing import TextIO -from verbecc.src.utils.config.base_config_util import BaseConfigUtil -from verbecc.src.defs.types.config.verbecc_config import VerbeccConfig +from verbecc.core.utils.config.base_config_util import BaseConfigUtil +from verbecc.core.defs.types.config.verbecc_config import VerbeccConfig class VerbeccConfigUtil(BaseConfigUtil[VerbeccConfig]): @@ -17,7 +17,9 @@ def __init__(self) -> None: def _load_config_from_filestream(self, filestream: TextIO) -> VerbeccConfig: ret = VerbeccConfig() - data = VerbeccConfig.from_yaml(filestream) + data: VerbeccConfig | list[VerbeccConfig] = VerbeccConfig.from_yaml( # pyright: ignore[reportUnknownMemberType] + filestream + ) if isinstance(data, list): ret = data[0] else: diff --git a/verbecc/src/utils/dict_utils.py b/src/verbecc/core/utils/dict_utils.py similarity index 68% rename from verbecc/src/utils/dict_utils.py rename to src/verbecc/core/utils/dict_utils.py index 7362d46..65c0b86 100644 --- a/verbecc/src/utils/dict_utils.py +++ b/src/verbecc/core/utils/dict_utils.py @@ -1,4 +1,4 @@ -from typing import cast, Dict, List, Any, Type +from typing import Any, Type, cast class DictUtils: @@ -6,16 +6,16 @@ class DictUtils: @staticmethod def marshall_keys_recursive( - data: Dict[str, Any], - values_to_marshall: List[str], + data: Any, + values_to_marshall: list[str], escape_str: str = DEFAULT_ESCAPE_STR, - ) -> Dict[str, Any]: + ) -> dict[str, Any] | list[Any] | Any: """ Recursively appends escape_str to all keys matching values_to_marshall """ if isinstance(data, dict): - new_data = {} - for k, v in data.items(): + new_data: dict[Any, Any] = {} + for k, v in cast(dict[Any, Any], data).items(): if k in values_to_marshall: new_k = k + escape_str new_data[new_k] = DictUtils.marshall_keys_recursive( @@ -28,21 +28,22 @@ def marshall_keys_recursive( return new_data elif isinstance(data, list): return [ - DictUtils.marshall_keys_recursive(v, values_to_marshall) for v in data + DictUtils.marshall_keys_recursive(v, values_to_marshall) + for v in cast(list[Any], data) ] else: return data @staticmethod def unmarshall_keys_recursive( - data: Dict[str, Any], escape_str: str = DEFAULT_ESCAPE_STR - ) -> Dict[str, Any]: + data: Any, escape_str: str = DEFAULT_ESCAPE_STR + ) -> dict[str, Any] | list[Any] | Any: """ Recursively strips trailing escape_str, if present, from all keys """ if isinstance(data, dict): - new_data = {} - for k, v in data.items(): + new_data: dict[Any, Any] = {} + for k, v in cast(dict[Any, Any], data).items(): if k.endswith(escape_str): new_k = k[:-1] new_data[new_k] = DictUtils.unmarshall_keys_recursive(v) @@ -50,24 +51,30 @@ def unmarshall_keys_recursive( new_data[k] = DictUtils.unmarshall_keys_recursive(v) return new_data elif isinstance(data, list): - return [DictUtils.unmarshall_keys_recursive(v) for v in data] + return [ + DictUtils.unmarshall_keys_recursive(v) + for v in cast(list[Any], data) + ] else: return data @staticmethod - def cast_values_recursive(data: object, key: str, type: Type) -> object: + def cast_values_recursive(data: object, key: str, type: Type[Any]) -> object: """ Recursively casts values to the given type for all items with matching keys """ if isinstance(data, dict): - new_data = {} - for k, v in data.items(): + new_data: dict[Any, Any] = {} + for k, v in cast(dict[Any, Any], data).items(): if k == key: new_data[k] = type(v) else: new_data[k] = DictUtils.cast_values_recursive(v, key, type) return new_data elif isinstance(data, list): - return [DictUtils.cast_values_recursive(v, key, type) for v in data] + return [ + DictUtils.cast_values_recursive(v, key, type) + for v in cast(list[Any], data) + ] else: return data diff --git a/verbecc/src/utils/jsbeautifier_utils.py b/src/verbecc/core/utils/jsbeautifier_utils.py similarity index 91% rename from verbecc/src/utils/jsbeautifier_utils.py rename to src/verbecc/core/utils/jsbeautifier_utils.py index 546e173..f86f27f 100644 --- a/verbecc/src/utils/jsbeautifier_utils.py +++ b/src/verbecc/core/utils/jsbeautifier_utils.py @@ -1,10 +1,9 @@ -import jsbeautifier from dataclasses import fields from typing import Optional import jsbeautifier -from verbecc.src.utils.config.verbecc_config_util import VerbeccConfigUtil +from verbecc.core.utils.config.verbecc_config_util import VerbeccConfigUtil class JSBeautifier: diff --git a/verbecc/src/utils/json_utils.py b/src/verbecc/core/utils/json_utils.py similarity index 84% rename from verbecc/src/utils/json_utils.py rename to src/verbecc/core/utils/json_utils.py index 51a63f3..61164c6 100644 --- a/verbecc/src/utils/json_utils.py +++ b/src/verbecc/core/utils/json_utils.py @@ -2,8 +2,8 @@ from dataclasses import fields import json -from verbecc.src.utils.config.verbecc_config_util import VerbeccConfigUtil -from verbecc.src.utils.jsbeautifier_utils import JSBeautifier +from verbecc.core.utils.config.verbecc_config_util import VerbeccConfigUtil +from verbecc.core.utils.jsbeautifier_utils import JSBeautifier config = VerbeccConfigUtil().load_config() @@ -29,7 +29,7 @@ def to_json( json_opts.indent = indent for field in fields(json_opts): kwargs[field.name] = getattr(json_opts, field.name) - ret = json.dumps(data, **kwargs) + ret = json.dumps(data, **kwargs) # type: ignore if beautify: ret = JSBeautifier.beautify(ret) return ret diff --git a/verbecc/src/utils/logging_utils.py b/src/verbecc/core/utils/logging_utils.py similarity index 87% rename from verbecc/src/utils/logging_utils.py rename to src/verbecc/core/utils/logging_utils.py index f12440c..c18566c 100644 --- a/verbecc/src/utils/logging_utils.py +++ b/src/verbecc/core/utils/logging_utils.py @@ -1,7 +1,7 @@ from typing import Optional import logging -from verbecc.src.utils.config.logging_config_util import LoggingConfigUtil +from verbecc.core.utils.config.logging_config_util import LoggingConfigUtil APP_NAME = "verbecc" diff --git a/verbecc/src/utils/string_utils.py b/src/verbecc/core/utils/string_utils.py similarity index 73% rename from verbecc/src/utils/string_utils.py rename to src/verbecc/core/utils/string_utils.py index 2fdfd23..5c1fcd7 100755 --- a/verbecc/src/utils/string_utils.py +++ b/src/verbecc/core/utils/string_utils.py @@ -1,5 +1,4 @@ from collections import Counter -from typing import Dict import unicodedata @@ -18,18 +17,10 @@ def starts_with_vowel(s: str, h_is_vowel: bool = False) -> bool: return strip_accents(s)[0] in vowels -def get_common_letters(s1: str, s2: str) -> Dict[str, int]: +def get_common_letters(s1: str, s2: str) -> dict[str, int]: common_letters = Counter(s1) & Counter(s2) # => {'q': 2, 'r': 1} return common_letters def get_common_letter_count(s1: str, s2: str) -> int: return sum(get_common_letters(s1, s2).values()) - - -def unicodefix(s: str) -> str: - # Fix Python 2.x. - try: - return s.decode("utf-8") - except AttributeError: - return s diff --git a/verbecc/src/utils/utils.py b/src/verbecc/core/utils/utils.py similarity index 82% rename from verbecc/src/utils/utils.py rename to src/verbecc/core/utils/utils.py index adfea78..d81d803 100644 --- a/verbecc/src/utils/utils.py +++ b/src/verbecc/core/utils/utils.py @@ -1,5 +1,5 @@ -from verbecc.src.conjugator.complete_conjugator import CompleteConjugator -from verbecc.src.defs.constants.grammar_defines import SUPPORTED_LANGUAGES +from verbecc.core.conjugator.complete_conjugator import CompleteConjugator +from verbecc.core.defs.constants.grammar_defines import SUPPORTED_LANGUAGES def train_models() -> None: diff --git a/src/verbecc/core/utils/xml_utils.py b/src/verbecc/core/utils/xml_utils.py new file mode 100644 index 0000000..7c942b4 --- /dev/null +++ b/src/verbecc/core/utils/xml_utils.py @@ -0,0 +1,131 @@ +from pathlib import Path +from typing import cast + +from lxml import etree +from lxml import objectify + +from verbecc.core.defs.types.data.xml_types import XmlElement, XmlElementTree, XmlParser + +""" +XML Utility functions for working with lxml.etree._Element objects in order +to abstract away the dependency on lxml.etree, improving readability and maintainability. +This module provides utility functions for converting XML elements to string representations. +This also defines type aliases for XML elements and XML element trees to improve code readability +and type safety. +Type Aliases: + XmlElement: An alias for lxml.etree._Element, representing an XML element. + XmlElementTree: An alias for lxml.etree._ElementTree, representing an entire XML document tree. +""" + +def xml_element_to_string(elem: XmlElement, + encoding: str="utf-8", + method: str="xml", + pretty_print: bool=True, + xml_declaration: bool=True) -> str: + """ + Converts an XML element to a string representation. + Args: + elem (XmlElement): The XML element to convert. + Returns: + str: A string representation of the XML element.""" + serialized = etree.tostring(cast(etree._Element, elem), encoding=encoding, method=method, xml_declaration=xml_declaration, pretty_print=pretty_print) # type: ignore + return serialized if isinstance(serialized, str) else serialized.decode(encoding) + + +def xml_parse(path: Path, parser: XmlParser) -> XmlElementTree: + """ + Parses an XML file and returns the corresponding XML element tree. + Args: + path (Path): The path to the XML file to parse. + parser (XmlParser): An instance of lxml.etree.XMLParser to use for parsing. + Returns: + XmlElementTree: The parsed XML element tree.""" + return etree.parse(path, parser) + + +def xml_element_deannotate(elem: XmlElement) -> None: + """ + Removes annotations from an XML element and its descendants. + Args: + elem (XmlElement): The XML element to deannotate. + """ + objectify.deannotate(elem, cleanup_namespaces=True) + + +def xml_element_get_tag(elem: XmlElement) -> str: + """ + Returns the tag name of an XML element. + Args: + elem (XmlElement): The XML element to get the tag name from. + Returns: + str: The tag name of the XML element.""" + return cast(str, elem.tag) # type: ignore + + +def xml_element_repr(elem: XmlElement) -> str: + return f"{xml_element_get_tag(elem)}: {elem} {xml_element_to_string(elem)}" + + +def xml_element_xpath(elem: XmlElement, xpath_expr: str) -> list[XmlElement]: + """ + Evaluates an XPath expression on an XML element and returns the matching elements. + Args: + elem (XmlElement): The XML element to evaluate the XPath expression on. + xpath_expr (str): The XPath expression to evaluate. + Returns: + list[XmlElement]: A list of matching XML elements.""" + return cast(list[XmlElement], elem.xpath(xpath_expr)) # type: ignore + + +def xml_element_remove(parent_elem: XmlElement, target_elem: XmlElement) -> None: + """ + Removes an XML element from its parent. + Args: + parent_elem (XmlElement): The parent XML element. + target_elem (XmlElement): The target XML element to remove. + """ + cast(etree._Element, parent_elem).remove(cast(etree._Element, target_elem)) # type: ignore + + +def xml_element_get_attr(elem: XmlElement, attr_name: str, default: str | None = None) -> str | None: + """ + Retrieves the value of an attribute from an XML element. + Args: + elem (XmlElement): The XML element to retrieve the attribute from. + attr_name (str): The name of the attribute to retrieve. + default (str, optional): The default value to return if the attribute is not found. Defaults to None. + Returns: + str: The value of the attribute, or the default value if not found.""" + return cast(str, elem.get(attr_name, default=default)) # type: ignore + + +def xml_element_find(elem: XmlElement, tag: str) -> XmlElement | None: + """ + Finds a child element with a specific tag name. + Args: + elem (XmlElement): The XML element to search within. + tag (str): The tag name to search for. + Returns: + list[XmlElement]: A list of matching child XML elements.""" + return cast(XmlElement | None, elem.find(tag)) # type: ignore + + +def xml_element_findall(elem: XmlElement, tag: str) -> list[XmlElement]: + """ + Finds all child elements with a specific tag name. + Args: + elem (XmlElement): The XML element to search within. + tag (str): The tag name to search for. + Returns: + list[XmlElement]: A list of matching child XML elements.""" + return cast(list[XmlElement], elem.findall(tag)) # type: ignore + + +def xml_element_get_text(elem: XmlElement) -> str | None: + """ + Retrieves the text content of an XML element. + Args: + elem (XmlElement): The XML element to retrieve the text from. + Returns: + str: The text content of the XML element.""" + return cast(str, elem.text) # type: ignore \ No newline at end of file diff --git a/verbecc/src/utils/yaml_utils.py b/src/verbecc/core/utils/yaml_utils.py similarity index 86% rename from verbecc/src/utils/yaml_utils.py rename to src/verbecc/core/utils/yaml_utils.py index 941f5dd..ec5d56a 100644 --- a/verbecc/src/utils/yaml_utils.py +++ b/src/verbecc/core/utils/yaml_utils.py @@ -1,8 +1,8 @@ import yaml """ -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.pronoun import Pronoun +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.pronoun import Pronoun class VerbeccYamlDumper(yaml.Dumper): diff --git a/verbecc/data/models/dummy-file-because-github-doesnt-like-large-files.md b/src/verbecc/data/models/dummy-file-because-github-doesnt-like-large-files.md similarity index 100% rename from verbecc/data/models/dummy-file-because-github-doesnt-like-large-files.md rename to src/verbecc/data/models/dummy-file-because-github-doesnt-like-large-files.md diff --git a/verbecc/data/xml/conjugations/conjugations-ca.xml b/src/verbecc/data/xml/conjugations/conjugations-ca.xml similarity index 100% rename from verbecc/data/xml/conjugations/conjugations-ca.xml rename to src/verbecc/data/xml/conjugations/conjugations-ca.xml diff --git a/verbecc/data/xml/conjugations/conjugations-es.xml b/src/verbecc/data/xml/conjugations/conjugations-es.xml similarity index 100% rename from verbecc/data/xml/conjugations/conjugations-es.xml rename to src/verbecc/data/xml/conjugations/conjugations-es.xml diff --git a/verbecc/data/xml/conjugations/conjugations-fr.xml b/src/verbecc/data/xml/conjugations/conjugations-fr.xml similarity index 100% rename from verbecc/data/xml/conjugations/conjugations-fr.xml rename to src/verbecc/data/xml/conjugations/conjugations-fr.xml diff --git a/verbecc/data/xml/conjugations/conjugations-it.xml b/src/verbecc/data/xml/conjugations/conjugations-it.xml similarity index 100% rename from verbecc/data/xml/conjugations/conjugations-it.xml rename to src/verbecc/data/xml/conjugations/conjugations-it.xml diff --git a/verbecc/data/xml/conjugations/conjugations-pt.xml b/src/verbecc/data/xml/conjugations/conjugations-pt.xml similarity index 100% rename from verbecc/data/xml/conjugations/conjugations-pt.xml rename to src/verbecc/data/xml/conjugations/conjugations-pt.xml diff --git a/verbecc/data/xml/conjugations/conjugations-ro.xml b/src/verbecc/data/xml/conjugations/conjugations-ro.xml similarity index 100% rename from verbecc/data/xml/conjugations/conjugations-ro.xml rename to src/verbecc/data/xml/conjugations/conjugations-ro.xml diff --git a/verbecc/data/xml/verbs/verbs-ca.xml b/src/verbecc/data/xml/verbs/verbs-ca.xml similarity index 100% rename from verbecc/data/xml/verbs/verbs-ca.xml rename to src/verbecc/data/xml/verbs/verbs-ca.xml diff --git a/verbecc/data/xml/verbs/verbs-es.xml b/src/verbecc/data/xml/verbs/verbs-es.xml similarity index 100% rename from verbecc/data/xml/verbs/verbs-es.xml rename to src/verbecc/data/xml/verbs/verbs-es.xml diff --git a/verbecc/data/xml/verbs/verbs-fr.xml b/src/verbecc/data/xml/verbs/verbs-fr.xml similarity index 100% rename from verbecc/data/xml/verbs/verbs-fr.xml rename to src/verbecc/data/xml/verbs/verbs-fr.xml diff --git a/verbecc/data/xml/verbs/verbs-it.xml b/src/verbecc/data/xml/verbs/verbs-it.xml similarity index 100% rename from verbecc/data/xml/verbs/verbs-it.xml rename to src/verbecc/data/xml/verbs/verbs-it.xml diff --git a/verbecc/data/xml/verbs/verbs-pt.xml b/src/verbecc/data/xml/verbs/verbs-pt.xml similarity index 100% rename from verbecc/data/xml/verbs/verbs-pt.xml rename to src/verbecc/data/xml/verbs/verbs-pt.xml diff --git a/verbecc/data/xml/verbs/verbs-ro.xml b/src/verbecc/data/xml/verbs/verbs-ro.xml similarity index 100% rename from verbecc/data/xml/verbs/verbs-ro.xml rename to src/verbecc/data/xml/verbs/verbs-ro.xml diff --git a/src/verbecc/py.typed b/src/verbecc/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/src/verbecc/resources/fonts/Spectral-Bold.ttf b/src/verbecc/resources/fonts/Spectral-Bold.ttf new file mode 100644 index 0000000..5019e08 Binary files /dev/null and b/src/verbecc/resources/fonts/Spectral-Bold.ttf differ diff --git a/src/verbecc/resources/icons/README.md b/src/verbecc/resources/icons/README.md new file mode 100644 index 0000000..5ed5848 --- /dev/null +++ b/src/verbecc/resources/icons/README.md @@ -0,0 +1,42 @@ +# Verbecc Icons + +> ⚠️ +> **Developer Only:** This section is intended exclusively for package maintainers and developers. Regular users can safely skip these instructions. + +### (Linux) commands used to convert SVG to PNG + +Best option is to install the TTF font, since inkscape and imagemagick don't support relative imports + +```bash +# Install Spectral TTF font +mkdir -p ~/.local/share/fonts +cp ../fonts/Spectral-Bold.ttf ~/.local/share/fonts/ +fc-cache -f -v + +# Verify that font was installed (verify installed font name/style) +$ fc-match "Spectral" +Spectral-Bold.ttf: "Spectral" "Bold" +$ fc-match "Spectral Bold" +NotoSans-Regular.ttf: "Noto Sans" "Regular" +$ fc-list | grep -i spectral +/home/brett/.local/share/fonts/Spectral-Bold.ttf: Spectral:style=Bold + +# Convert logo SVG to PNG (32-bit, with transparent background) +# 32-bit PNG (preserves the 24-bit RGB true color channels plus an 8-bit alpha transparency channel) +magick -background none -size 512x512 -depth 8 -define png:color-type=6 logo.svg logo.png + +# Convert feature graphic SVG to PNG (24-bit, with white background) +# 24-bit PNG (no-alpha) +# -flatten: Tells ImageMagick to merge the transparent layers of the SVG onto that background color, completely removing the alpha channel. +magick -background white -size 1024x500 -depth 8 -define png:color-type=2 feature-graphic.svg -flatten feature-graphic.png + +# Convert text elements to path and scale to 200x200 for android compatibility +inkscape logo.svg --export-type=svg --export-text-to-path --export-width=200 --export-height=200 -o logo-android.svg + +``` + +### About the Spectral Font + +> Spectral is an open-source, contemporary serif typeface commissioned by Google Fonts and designed by Production Type, a digital type foundry based in Paris led by Jean-Baptiste Levée. + +> Spectral (designed by Production Type) was designated as the official secondary typeface in the French government design system (Système de Design de l'État). diff --git a/src/verbecc/resources/icons/feature-graphic.png b/src/verbecc/resources/icons/feature-graphic.png new file mode 100644 index 0000000..e4c8ebe Binary files /dev/null and b/src/verbecc/resources/icons/feature-graphic.png differ diff --git a/src/verbecc/resources/icons/feature-graphic.svg b/src/verbecc/resources/icons/feature-graphic.svg new file mode 100644 index 0000000..8d7ac01 --- /dev/null +++ b/src/verbecc/resources/icons/feature-graphic.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + V + CC + + + + + Verbecc + Complete + Conjugator + + diff --git a/src/verbecc/resources/icons/logo-android.svg b/src/verbecc/resources/icons/logo-android.svg new file mode 100644 index 0000000..b8a7bbc --- /dev/null +++ b/src/verbecc/resources/icons/logo-android.svg @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + diff --git a/src/verbecc/resources/icons/logo.png b/src/verbecc/resources/icons/logo.png new file mode 100644 index 0000000..9fd6d1c Binary files /dev/null and b/src/verbecc/resources/icons/logo.png differ diff --git a/src/verbecc/resources/icons/logo.svg b/src/verbecc/resources/icons/logo.svg new file mode 100644 index 0000000..16930b8 --- /dev/null +++ b/src/verbecc/resources/icons/logo.svg @@ -0,0 +1,15 @@ + + + + + + + V + CC + + + diff --git a/tests/common.py b/tests/common.py index f596c0a..1830701 100644 --- a/tests/common.py +++ b/tests/common.py @@ -1,4 +1,5 @@ import json +from typing import Any def assert_json_str_equal(s1: str, s2: str): @@ -6,8 +7,8 @@ def assert_json_str_equal(s1: str, s2: str): ss1_original = json.loads(s1) ss2_original = json.loads(s2) - ss1_type = type(ss1_original) - ss2_type = type(ss2_original) + ss1_type: type[Any] = type(ss1_original) # type: ignore + ss2_type: type[Any] = type(ss2_original) # type: ignore # check type first, we don't want to recurse if for example one is Dict and the other is List is_type_match = ss1_type == ss2_type @@ -25,9 +26,9 @@ def assert_json_str_equal(s1: str, s2: str): for index, _ in enumerate(ss1): ss1_child_value = ss1[index] ss2_child_value = ss2[index] - ss1_child_type = type(ss1_child_value) - ss2_child_type = type(ss2_child_value) - RECURSE_TYPES = [ + ss1_child_type: type[Any] = type(ss1_child_value) # type: ignore + ss2_child_type: type[Any] = type(ss2_child_value) # type: ignore + RECURSE_TYPES: list[type[Any]] = [ type(tuple), type(list), type(dict), diff --git a/tests/scripts/test_xml_modifier.py b/tests/scripts/test_xml_modifier.py index 53b7a11..693834a 100644 --- a/tests/scripts/test_xml_modifier.py +++ b/tests/scripts/test_xml_modifier.py @@ -1,10 +1,14 @@ +from pathlib import Path + import pytest +from lxml import etree from scripts import xml_modifier +from verbecc.core.utils.xml_utils import xml_element_to_string """ The "tests" in this module are for development purposes only -and will normally be marked skip but may be unmarked skip +and will normally be marked skip but may be unmarked skip and ran with dry_run=True (test) or dry_run=False (do) """ @@ -15,8 +19,8 @@ def test_or_do_spanish_conjugation_xml_mods(): This is just a unit-test. (unless I change dry_run to False, that is) """ - INPUT_PATH = "verbecc/data/xml/conjugations/conjugations-es.xml" - OUTPUT_PATH = "verbecc/data/xml/conjugations/conjugations-es.mod.xml" + INPUT_PATH = Path("src/verbecc/data/xml/conjugations/conjugations-es.xml") + OUTPUT_PATH = Path("src/verbecc/data/xml/conjugations/conjugations-es.mod.xml") xml_modifier.do_spanish_mods(INPUT_PATH, OUTPUT_PATH, dry_run=True) @@ -26,6 +30,28 @@ def test_or_do_portuguese_conjugation_xml_mods(): This is just a unit-test. (unless I change dry_run to False, that is) """ - INPUT_PATH = "verbecc/data/xml/conjugations/conjugations-pt.xml" - OUTPUT_PATH = "verbecc/data/xml/conjugations/conjugations-pt.mod.xml" + INPUT_PATH = Path("src/verbecc/data/xml/conjugations/conjugations-pt.xml") + OUTPUT_PATH = Path("src/verbecc/data/xml/conjugations/conjugations-pt.mod.xml") xml_modifier.do_portuguese_mods(INPUT_PATH, OUTPUT_PATH, dry_run=True) + + +@pytest.mark.parametrize("encoding", ["utf-8", "iso-8859-1", "unicode"]) +def test_xml_element_to_string_decodes_serialized_xml(encoding): + elem = etree.fromstring("café") + + result = xml_element_to_string( + elem, encoding=encoding, pretty_print=False, xml_declaration=False + ) + + assert result == "café" + + +def test_write_output_file_produces_parseable_utf8(tmp_path): + root = etree.fromstring("café") + output = tmp_path / "output.xml" + + xml_modifier.write_output_file(etree.ElementTree(root), output) + + data = output.read_bytes() + assert b"caf\xc3\xa9" in data + assert etree.parse(str(output)).getroot().text == "café" diff --git a/tests/test_ast.py b/tests/test_code_style/test_ast.py similarity index 86% rename from tests/test_ast.py rename to tests/test_code_style/test_ast.py index 6b18377..570d356 100644 --- a/tests/test_ast.py +++ b/tests/test_code_style/test_ast.py @@ -25,7 +25,10 @@ import ast import os -from typing import List, Union +from pathlib import Path +from typing import Union + +from pytest import Metafunc EXCLUDE_DIRS = {".git", "__pycache__", "venv", ".venv", "env", "build", "dist", "tests"} @@ -35,13 +38,13 @@ class Finder(ast.NodeVisitor): - def __init__(self, filename): + def __init__(self, filename: str): self.filename = filename - self.class_stack = [] + self.class_stack: list[str] = [] self._class_count = 0 - self._reported_errors: List[str] = [] + self._reported_errors: list[str] = [] - def get_reported_errors(self) -> List[str]: + def get_reported_errors(self) -> list[str]: return self._reported_errors def visit_ClassDef(self, node: ast.ClassDef): @@ -56,9 +59,14 @@ def _base_errmsg(self, test_id: int) -> str: return f"Test AST test {test_name} failed: " def _report_error_param_or_return_missing_type_hint( - self, node, func_name: str, lineno, missing_params, missing_return - ): - parts = [] + self, + node: Union[ast.FunctionDef, ast.AsyncFunctionDef], + func_name: str, + lineno: int, + missing_params: list[str], + missing_return: bool, + ) -> None: + parts: list[str] = [] errmsg = self._base_errmsg(TEST_TYPE_ANNOTATIONS) if missing_params: parts.append(errmsg + "params missing type annotation: " + ", ".join(missing_params)) @@ -72,7 +80,7 @@ def _report_error_param_or_return_missing_type_hint( print(msg) self._reported_errors.append(msg) - def _report_error_multiple_class_definitions_in_same_file(self, node, class_name: str, lineno) -> None: + def _report_error_multiple_class_definitions_in_same_file(self, node: ast.ClassDef, class_name: str, lineno: int) -> None: errmsg = self._base_errmsg(TEST_ONE_CLASS_PER_FILE) msg = errmsg + f"multiple class definitions in same file: {self.filename}:{lineno} {class_name}" self._reported_errors.append(msg) @@ -83,9 +91,9 @@ def _check_class(self, node: ast.ClassDef) -> None: def _check_func(self, node: Union[ast.FunctionDef, ast.AsyncFunctionDef]) -> None: is_method = bool(self.class_stack) - missing = [] + missing: list[str] = [] # posonlyargs (py3.8+), args, kwonlyargs - all_args = [] + all_args: list[ast.arg] = [] if hasattr(node.args, "posonlyargs"): all_args.extend(node.args.posonlyargs) all_args.extend(node.args.args) @@ -118,7 +126,7 @@ def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef): self.generic_visit(node) -def scan_file(path: str) -> List[str]: +def scan_file(path: str) -> list[str]: print("Scanning", path) try: with open(path, "r", encoding="utf-8") as f: @@ -131,8 +139,8 @@ def scan_file(path: str) -> List[str]: return finder.get_reported_errors() -def walk_root(root) -> List[str]: - errors = [] +def walk_root(root: Path | str) -> list[str]: + errors: list[str] = [] for dirpath, dirnames, filenames in os.walk(root): # skip excluded dirs dirnames[:] = [d for d in dirnames if d not in EXCLUDE_DIRS and not d.startswith(".egg-info")] @@ -145,11 +153,11 @@ def walk_root(root) -> List[str]: NO_ERRORS = "(no errors)" -def pytest_generate_tests(metafunc): +def pytest_generate_tests(metafunc: Metafunc) -> None: """Indirect parametrization for pytest to run test_per_error for each missing type annotation found.""" if "error" in metafunc.fixturenames: - errors: List[str] = walk_root(metafunc.config.rootpath) + errors: list[str] = walk_root(metafunc.config.rootpath) # below is a bit hacky but allows test_ast module to # show as passing rather than as skipped due to an empty # errors list being sent to parametrize @@ -158,7 +166,7 @@ def pytest_generate_tests(metafunc): metafunc.parametrize("error", errors) -def test_per_error(error: str): +def test_per_error(error: str) -> None: """Test that fails for each AST error found. If there are no errors, it will run once and pass. Works with pytest using indirect parametrization. diff --git a/tests/test_code_style/test_imports.py b/tests/test_code_style/test_imports.py new file mode 100644 index 0000000..cef3aa0 --- /dev/null +++ b/tests/test_code_style/test_imports.py @@ -0,0 +1,66 @@ +import pytest +import subprocess +import ast +from pathlib import Path + +@pytest.fixture +def project_root(pytestconfig: pytest.Config) -> Path: + # Injects the built-in pytestconfig fixture + return pytestconfig.rootpath + + +def test_STYLE_GUIDE_R005_only_relative_imports(project_root: Path) -> None: + """Enforces STYLE_GUIDE rule R005 - `__init__.py` files shall only use relative imports""" + command: list[str] = [ + "find", + str(project_root), + "-path", + str(project_root / ".venv"), + "-prune", + "-o", + "-name", + "__init__.py", + "-exec", + "grep", + "-PHn", + r"^(?:from|import)(?! \.)", + "{}", + ";", + ] + + # Execute the command safely as a subprocess + result = subprocess.run( + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True + ) + + # Assert that stdout is completely empty + assert result.stdout == "", ( + f"Found violation of STYLE_GUIDE rule R005 - " + f"`__init__.py` files shall only use relative imports:\n" + + f"\n{result.stdout}\nErrors: {result.stderr}" + ) + + +def test_STYLE_GUIDE_R006_no_deep_relative_imports(project_root: Path) -> None: + """Enforces STYLE_GUIDE rule R006 - `__init__.py` imports shall be at most one level deep.""" + violations: list[str] = [] + for path in project_root.rglob("__init__.py"): + if any(part in {".git", "__pycache__", "venv", ".venv", "env", "build", "dist"} for part in path.parts): + continue + try: + tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) + except SyntaxError as error: + violations.append(f"{path}: parse error: {error}") + continue + for node in ast.walk(tree): + if not isinstance(node, ast.ImportFrom): + continue + module_depth = len(node.module.split(".")) if node.module else 0 + if node.level > 1 or module_depth > 1: + violations.append(f"{path}:{node.lineno}: {ast.unparse(node)}") + + assert violations == [], ( + "Found violation of STYLE_GUIDE rule R006 - " + f"`__init__.py` imports shall not be deeper than one level:\n" + + "\n".join(violations) + ) diff --git a/tests/test_conjugator.py b/tests/test_conjugator.py index 701dbd2..5da9cea 100755 --- a/tests/test_conjugator.py +++ b/tests/test_conjugator.py @@ -1,21 +1,22 @@ import pytest - -from verbecc.src.conjugator.complete_conjugator import CompleteConjugator -from verbecc.src.conjugator.mood_conjugator import MoodConjugator -from verbecc.src.conjugator.tense_conjugator import TenseConjugator -from verbecc.src.defs.types.exceptions import InvalidMoodError -from verbecc.src.defs.types.exceptions import InvalidTenseError -from verbecc.src.defs.types.exceptions import TemplateNotFoundError -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang -from verbecc.src.utils.config.verbecc_config_util import VerbeccConfigUtil -from verbecc.src.defs.types.tense import Tenses -from verbecc.src.defs.types.mood import Moods +from typing import Generator + +from verbecc.core.conjugator.complete_conjugator import CompleteConjugator +from verbecc.core.conjugator.mood_conjugator import MoodConjugator +from verbecc.core.conjugator.tense_conjugator import TenseConjugator +from verbecc.core.defs.types.exceptions import InvalidMoodError +from verbecc.core.defs.types.exceptions import InvalidTenseError +from verbecc.core.defs.types.exceptions import TemplateNotFoundError +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.utils.config.verbecc_config_util import VerbeccConfigUtil +from verbecc.core.defs.types.tense import Tenses +from verbecc.core.defs.types.mood import Moods config = VerbeccConfigUtil().load_config() @pytest.fixture(scope="module") -def ccg(): +def ccg() -> Generator[CompleteConjugator, None, None]: ccg = CompleteConjugator(lang=Lang.fr) yield ccg @@ -32,13 +33,13 @@ def tcg(): yield tcg -def test_get_infinitives(ccg): +def test_get_infinitives(ccg: CompleteConjugator): infinitives = ccg.get_infinitives() assert len(infinitives) > 7000 assert "parler" in infinitives -def test_get_template_names(ccg): +def test_get_template_names(ccg: CompleteConjugator): template_names = ccg.get_template_names() assert len(template_names) >= 146 assert "aim:er" in template_names @@ -56,12 +57,12 @@ def test_get_template_names(ccg): @pytest.mark.parametrize("infinitive", test_verbs) -def test_conjugate_basic(ccg, infinitive): +def test_conjugate_basic(ccg: CompleteConjugator, infinitive: str): cc = ccg.conjugate(infinitive) assert cc -def test_conjugator_predict_conjugation_er_verb_indicative_present(ccg): +def test_conjugator_predict_conjugation_er_verb_indicative_present(ccg: CompleteConjugator): if config.ENABLE_ML_PREDICTION: tc = ccg.conjugate_mood_tense("ubériser", Moods.fr.Indicatif, Tenses.fr.Présent) assert [c[0] for c in tc] == [ @@ -77,7 +78,7 @@ def test_conjugator_predict_conjugation_er_verb_indicative_present(ccg): ] -def test_conjugator_predict_conjugation_re_verb_indicative_present(ccg): +def test_conjugator_predict_conjugation_re_verb_indicative_present(ccg: CompleteConjugator): if config.ENABLE_ML_PREDICTION: tc = ccg.conjugate_mood_tense("brettre", Moods.fr.Indicatif, Tenses.fr.Présent) assert [c[0] for c in tc] == [ @@ -93,7 +94,7 @@ def test_conjugator_predict_conjugation_re_verb_indicative_present(ccg): ] -def test_conjugate_passe_compose_with_avoir(ccg): +def test_conjugate_passe_compose_with_avoir(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense("manger", Moods.fr.Indicatif, Tenses.fr.PasséComposé) assert [c[0] for c in tc] == [ "j'ai mangé", @@ -108,7 +109,7 @@ def test_conjugate_passe_compose_with_avoir(ccg): ] -def test_conjugate_passe_compose_with_etre(ccg): +def test_conjugate_passe_compose_with_etre(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense("aller", Moods.fr.Indicatif, Tenses.fr.PasséComposé) assert [c[0] for c in tc] == [ "je suis allée", @@ -128,7 +129,7 @@ def test_conjugate_passe_compose_with_etre(ccg): ] -def test_conjugate_subjonctif_passe_with_avoir(ccg): +def test_conjugate_subjonctif_passe_with_avoir(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense("manger", Moods.fr.Subjonctif, Tenses.fr.Passé) assert [c[0] for c in tc] == [ "que j'aie mangé", @@ -143,7 +144,7 @@ def test_conjugate_subjonctif_passe_with_avoir(ccg): ] -def test_conjugate_subjonctif_passe_with_etre(ccg): +def test_conjugate_subjonctif_passe_with_etre(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense("aller", Moods.fr.Subjonctif, Tenses.fr.Passé) assert [c[0] for c in tc] == [ "que je sois allée", @@ -163,7 +164,7 @@ def test_conjugate_subjonctif_passe_with_etre(ccg): ] -def test_conjugate_conditionnel_passe_with_avoir(ccg): +def test_conjugate_conditionnel_passe_with_avoir(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense("manger", Moods.fr.Conditionnel, Tenses.fr.Passé) assert [c[0] for c in tc] == [ "j'aurais mangé", @@ -178,7 +179,7 @@ def test_conjugate_conditionnel_passe_with_avoir(ccg): ] -def test_conjugate_conditionnel_passe_with_etre(ccg): +def test_conjugate_conditionnel_passe_with_etre(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense("aller", Moods.fr.Conditionnel, Tenses.fr.Passé) assert [c[0] for c in tc] == [ "je serais allée", @@ -198,7 +199,7 @@ def test_conjugate_conditionnel_passe_with_etre(ccg): ] -def test_conjugate_plusqueparfait_with_avoir(ccg): +def test_conjugate_plusqueparfait_with_avoir(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense( "manger", Moods.fr.Indicatif, Tenses.fr.PlusQueParfait ) @@ -215,7 +216,7 @@ def test_conjugate_plusqueparfait_with_avoir(ccg): ] -def test_conjugate_plusqueparfait_with_etre(ccg): +def test_conjugate_plusqueparfait_with_etre(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense("aller", Moods.fr.Indicatif, Tenses.fr.PlusQueParfait) assert [c[0] for c in tc] == [ "j'étais allée", @@ -235,7 +236,7 @@ def test_conjugate_plusqueparfait_with_etre(ccg): ] -def test_conjugate_subjonctif_plusqueparfait_with_avoir(ccg): +def test_conjugate_subjonctif_plusqueparfait_with_avoir(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense( "manger", Moods.fr.Subjonctif, Tenses.fr.PlusQueParfait ) @@ -252,7 +253,7 @@ def test_conjugate_subjonctif_plusqueparfait_with_avoir(ccg): ] -def test_conjugate_subjonctif_plusqueparfait_with_etre(ccg): +def test_conjugate_subjonctif_plusqueparfait_with_etre(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense( "aller", Moods.fr.Subjonctif, Tenses.fr.PlusQueParfait ) @@ -274,7 +275,7 @@ def test_conjugate_subjonctif_plusqueparfait_with_etre(ccg): ] -def test_conjugate_futur_anterieur_with_avoir(ccg): +def test_conjugate_futur_anterieur_with_avoir(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense( "manger", Moods.fr.Indicatif, Tenses.fr.FuturAntérieur ) @@ -291,7 +292,7 @@ def test_conjugate_futur_anterieur_with_avoir(ccg): ] -def test_conjugate_futur_anterieur_with_etre(ccg): +def test_conjugate_futur_anterieur_with_etre(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense("aller", Moods.fr.Indicatif, Tenses.fr.FuturAntérieur) assert [c[0] for c in tc] == [ "je serai allée", @@ -311,7 +312,7 @@ def test_conjugate_futur_anterieur_with_etre(ccg): ] -def test_conjugate_passe_anterieur_with_avoir(ccg): +def test_conjugate_passe_anterieur_with_avoir(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense( "manger", Moods.fr.Indicatif, Tenses.fr.PasséAntérieur ) @@ -328,7 +329,7 @@ def test_conjugate_passe_anterieur_with_avoir(ccg): ] -def test_conjugate_passe_anterieur_with_être(ccg): +def test_conjugate_passe_anterieur_with_être(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense("aller", Moods.fr.Indicatif, Tenses.fr.PasséAntérieur) assert [c[0] for c in tc] == [ "je fus allée", @@ -348,7 +349,7 @@ def test_conjugate_passe_anterieur_with_être(ccg): ] -def test_conjugate_imperatif_passe_with_avoir(ccg): +def test_conjugate_imperatif_passe_with_avoir(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense( "manger", Moods.fr.Imperatif, Tenses.fr.ImperatifPassé ) @@ -359,7 +360,7 @@ def test_conjugate_imperatif_passe_with_avoir(ccg): ] -def test_conjugate_imperatif_passe_with_etre(ccg): +def test_conjugate_imperatif_passe_with_etre(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense("aller", Moods.fr.Imperatif, Tenses.fr.ImperatifPassé) assert [c[0] for c in tc] == [ "sois allée", @@ -371,17 +372,17 @@ def test_conjugate_imperatif_passe_with_etre(ccg): ] -def test_conjugate_invalid_mood(ccg): +def test_conjugate_invalid_mood(ccg: CompleteConjugator): with pytest.raises(InvalidMoodError): ccg.conjugate_mood("manger", "oops") -def test_conjugate_invalid_tense(ccg): +def test_conjugate_invalid_tense(ccg: CompleteConjugator): with pytest.raises(InvalidTenseError): ccg.conjugate_mood_tense("manger", Moods.fr.Indicatif, "oops") -def test_conjugator_find_template_template_not_found(ccg): +def test_conjugator_find_template_template_not_found(ccg: CompleteConjugator): with pytest.raises(TemplateNotFoundError): ccg.find_template("oops") @@ -394,11 +395,13 @@ def test_conjugator_find_template_template_not_found(ccg): ("s'aim", ["s'aimanter", "s'aimer"]), ], ) -def test_conjugator_get_verbs_that_start_with(ccg, query, expected_value): +def test_conjugator_get_verbs_that_start_with( + ccg: CompleteConjugator, query: str, expected_value: list[str] +): assert set(ccg.get_verbs_that_start_with(query, max_results=10)) == set( expected_value ) def test_conjugator_construct(): - ccg = CompleteConjugator(lang=Lang.fr) + CompleteConjugator(lang=Lang.fr) diff --git a/tests/test_inflectors/test_inflector_ca.py b/tests/test_inflectors/test_inflector_ca.py index e947d5b..9986cdb 100755 --- a/tests/test_inflectors/test_inflector_ca.py +++ b/tests/test_inflectors/test_inflector_ca.py @@ -1,22 +1,27 @@ +# pyright: reportPrivateUsage=false + import pytest from lxml import etree +from typing import Any, Generator -from verbecc.src.conjugator.complete_conjugator import CompleteConjugator -from verbecc.src.conjugator.mood_conjugator import MoodConjugator -from verbecc.src.conjugator.tense_conjugator import TenseConjugator -from verbecc.src.defs.types.conjugation import Conjugation, TenseConjugation -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang -from verbecc.src.defs.types.mood import Moods -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.tense import Tenses -from verbecc.src.parsers.tense_template_parser import TenseTemplateParser -from verbecc.src.defs.types.pronoun import Pronoun, Pronouns +from verbecc.core.conjugator.complete_conjugator import CompleteConjugator +from verbecc.core.conjugator.mood_conjugator import MoodConjugator +from verbecc.core.conjugator.tense_conjugator import TenseConjugator +from verbecc.core.defs.types.conjugation import Conjugation, TenseConjugation +from verbecc.core.defs.types.gender import Gender +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.defs.types.mood import Moods +from verbecc.core.defs.types.mood import Mood +from verbecc.core.defs.types.number import Number +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.tense import Tenses +from verbecc.core.defs.types.tense import Tense +from verbecc.core.parsers.tense_template_parser import TenseTemplateParser +from verbecc.core.defs.types.pronoun import Pronouns @pytest.fixture(scope="module") -def ccg(): +def ccg() -> Generator[CompleteConjugator, None, None]: ccg = CompleteConjugator(lang=Lang.ca) yield ccg @@ -28,18 +33,18 @@ def mcg(): @pytest.fixture(scope="module") -def tcg(): +def tcg() -> Generator[TenseConjugator, None, None]: tcg = TenseConjugator(lang=Lang.ca) yield tcg -def test_all_verbs_have_templates(ccg): - verbs = ccg.get_verbs() +def test_all_verbs_have_templates(ccg: CompleteConjugator): + verbs: list[Any] = ccg.get_verbs() assert len(verbs) == 8616 template_names = ccg.get_template_names() assert len(template_names) == 66 - missing_verbs = [] - missing_templates = set() + missing_verbs: list[str] = [] + missing_templates: set[str] = set() for verb in verbs: if verb.template not in template_names: missing_templates.add(verb.template) @@ -47,7 +52,7 @@ def test_all_verbs_have_templates(ccg): assert len(missing_templates) == len(missing_verbs) == 0 -def test_find_verb_by_infinitive(ccg): +def test_find_verb_by_infinitive(ccg: CompleteConjugator): v = ccg.find_verb_by_infinitive("abandonar") assert v.infinitive == "abandonar" assert v.template == "cant:ar" @@ -2836,13 +2841,17 @@ def test_find_verb_by_infinitive(ccg): ], ) def test_inflector_ca_conjugate_mood_tense( - ccg, infinitive, mood, tense, expected_result + ccg: CompleteConjugator, + infinitive: str, + mood: Mood | str, + tense: Tense | str, + expected_result: list[list[str]], ): tc = ccg.conjugate_mood_tense(infinitive, mood, tense) assert [list(c) for c in tc] == expected_result -def test_inflector_ca_conjugate_simple_past(ccg): +def test_inflector_ca_conjugate_simple_past(ccg: CompleteConjugator): cc = ccg.conjugate_mood_tense( "pertànyer", Moods.ca.Indicatiu, Tenses.ca.PassatSimple ) @@ -2909,39 +2918,41 @@ def test_inflector_ca_conjugate_simple_past(ccg): ) -def test_inflector_ca_get_conj_obs(ccg): - co = ccg._get_conj_obs("parlar") +def test_inflector_ca_get_conj_objs(ccg: CompleteConjugator): + co = ccg._get_conj_objs("parlar") assert co.verb.infinitive == "parlar" assert co.verb_stem == "parl" assert co.template.name == "cant:ar" -def test_inflector_ca_get_conj_obs_2(ccg): - co = ccg._get_conj_obs("abandonar") +def test_inflector_caget_co_2(ccg: CompleteConjugator): + co = ccg._get_conj_objs("abandonar") assert co.verb.infinitive == "abandonar" assert co.verb_stem == "abandon" assert co.template.name == "cant:ar" -def test_inflector_ca_get_conj_obs_3(ccg): - co = ccg._get_conj_obs("pertànyer") +def test_inflector_caget_co_3(ccg: CompleteConjugator): + co = ccg._get_conj_objs("pertànyer") assert co.verb.infinitive == "pertànyer" assert co.verb_stem == "pertàny" assert co.template.name == "pertàny:er" assert co.template.modify_stem == "strip-accents" -def test_inflector_ca_get_verb_stem_from_template_name(ccg): - verb_stem = ccg._inflector.get_verb_stem_from_template_name("parlar", "cant:ar") +def test_inflector_ca_get_verb_stem_from_template_name(ccg: CompleteConjugator): + inflector = ccg._get_inflector() + verb_stem = inflector.get_verb_stem_from_template_name("parlar", "cant:ar") assert verb_stem == "parl" -def test_inflector_ca_get_verb_stem_from_template_name_2(ccg): - verb_stem = ccg._inflector.get_verb_stem_from_template_name("abandonar", "cant:ar") +def test_inflector_ca_get_verb_stem_from_template_name_2(ccg: CompleteConjugator): + inflector = ccg._get_inflector() + verb_stem = inflector.get_verb_stem_from_template_name("abandonar", "cant:ar") assert verb_stem == "abandon" -def test_inflector_ca_conjugate_simple_mood_tense(tcg): +def test_inflector_ca_conjugate_simple_mood_tense(tcg: TenseConjugator): mood = Moods.ca.Indicatiu tense = Tenses.ca.Present verb_stem = "parl" @@ -2957,7 +2968,7 @@ def test_inflector_ca_conjugate_simple_mood_tense(tcg): parser=None, ) tense_template = TenseTemplateParser(Lang.ca, mood).parse(tense_elem) - tc = tcg._tense_conjugator_simple._conjugate_simple_mood_tense( + tc: TenseConjugation = tcg._get_tense_conjugator_simple().conjugate_simple_mood_tense( verb_stem, mood, tense, tense_template ) assert [c.get_conjugations() for c in tc] == [ @@ -2994,14 +3005,15 @@ def test_inflector_ca_conjugate_simple_mood_tense(tcg): ], ) def test_inflector_ca_get_pronouns( - ccg, + ccg: CompleteConjugator, person: Person, number: Number, gender: Gender, is_reflexive: bool, expected_result: str, ): - pronoun = ccg._inflector.get_pronouns(person, number, gender)[0] + inflector = ccg._get_inflector() + pronoun = inflector.get_pronouns(person, number, gender)[0] if is_reflexive: - pronoun = ccg._inflector.make_pronoun_reflexive(pronoun) + pronoun = inflector.make_pronoun_reflexive(pronoun) assert pronoun == expected_result diff --git a/tests/test_inflectors/test_inflector_ca_to_json.py b/tests/test_inflectors/test_inflector_ca_to_json.py index 067ed89..feb5eb4 100644 --- a/tests/test_inflectors/test_inflector_ca_to_json.py +++ b/tests/test_inflectors/test_inflector_ca_to_json.py @@ -3,8 +3,8 @@ from tests.common import assert_json_str_equal -from verbecc.src.conjugator.complete_conjugator import CompleteConjugator -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.conjugator.complete_conjugator import CompleteConjugator +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang @pytest.fixture(scope="module") @@ -13,7 +13,7 @@ def ccg(): yield ccg -expected_value_conj_ser = { +expected_value_conj_ser: dict[str, dict[str, object]] = { "moods": { "condicional": { "present": [ @@ -221,7 +221,7 @@ def ccg(): }, } -expected_value_conj_ser_nopronouns = { +expected_value_conj_ser_nopronouns: dict[str, dict[str, object]] = { "moods": { "condicional": { "present": [ @@ -342,11 +342,11 @@ def ccg(): } -def test_inflector_ca_conjugate_ser_to_json(ccg): +def test_inflector_ca_conjugate_ser_to_json(ccg: CompleteConjugator): cc = ccg.conjugate("ser") assert_json_str_equal(str(cc), json.dumps(expected_value_conj_ser)) -def test_inflector_conjugate_ser_noconjpronouns_to_json(ccg): +def test_inflector_conjugate_ser_noconjpronouns_to_json(ccg: CompleteConjugator): cc = ccg.conjugate("ser", conjugate_pronouns=False) assert_json_str_equal(str(cc), json.dumps(expected_value_conj_ser_nopronouns)) diff --git a/tests/test_inflectors/test_inflector_es.py b/tests/test_inflectors/test_inflector_es.py index 9c0263c..4f23675 100755 --- a/tests/test_inflectors/test_inflector_es.py +++ b/tests/test_inflectors/test_inflector_es.py @@ -1,18 +1,22 @@ +# pyright: reportPrivateUsage=false + import pytest from lxml import etree -from verbecc.src.conjugator.complete_conjugator import CompleteConjugator -from verbecc.src.conjugator.mood_conjugator import MoodConjugator -from verbecc.src.conjugator.tense_conjugator import TenseConjugator -from verbecc.src.defs.types.conjugation import Conjugation, TenseConjugation -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang -from verbecc.src.defs.types.mood import Moods -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.tense import Tenses -from verbecc.src.parsers.tense_template_parser import TenseTemplateParser -from verbecc.src.defs.types.pronoun import Pronouns +from verbecc.core.conjugator.complete_conjugator import CompleteConjugator +from verbecc.core.conjugator.mood_conjugator import MoodConjugator +from verbecc.core.conjugator.tense_conjugator import TenseConjugator +from verbecc.core.defs.types.conjugation import Conjugation, TenseConjugation +from verbecc.core.defs.types.gender import Gender +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.defs.types.mood import Moods +from verbecc.core.defs.types.mood import Mood +from verbecc.core.defs.types.number import Number +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.tense import Tenses +from verbecc.core.defs.types.tense import Tense +from verbecc.core.parsers.tense_template_parser import TenseTemplateParser +from verbecc.core.defs.types.pronoun import Pronouns @pytest.fixture(scope="module") @@ -33,10 +37,10 @@ def tcg(): yield tcg -def test_all_verbs_have_templates(ccg): +def test_all_verbs_have_templates(ccg: CompleteConjugator): verbs = ccg.get_verbs() template_names = ccg.get_template_names() - missing_templates = set() + missing_templates: set[str] = set() for verb in verbs: if verb.template not in template_names: missing_templates.add(verb.template) @@ -845,13 +849,17 @@ def test_all_verbs_have_templates(ccg): ], ) def test_inflector_es_conjugate_mood_tense( - ccg, infinitive, mood, tense, expected_result + ccg: CompleteConjugator, + infinitive: str, + mood: Mood | str, + tense: Tense | str, + expected_result: list[str], ): tc = ccg.conjugate_mood_tense(infinitive, mood, tense) assert [c[0] for c in tc] == expected_result -def test_abolir(ccg): +def test_abolir(ccg: CompleteConjugator): """ Reproduce error: @@ -866,7 +874,7 @@ def test_abolir(ccg): assert cc is not None -def test_abolir_imperativo_afirmativo(ccg): +def test_abolir_imperativo_afirmativo(ccg: CompleteConjugator): """ Reproduce another error with this verb # step one for imperativo: remove the trailing 'd' @@ -895,7 +903,7 @@ def test_abolir_imperativo_afirmativo(ccg): ] -def test_soler_imperativo_afirmativo(ccg): +def test_soler_imperativo_afirmativo(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense("soler", Moods.es.Imperativo, Tenses.es.Afirmativo) assert [c[0] for c in tc] == [ "suele", @@ -911,7 +919,7 @@ def test_soler_imperativo_afirmativo(ccg): ] -def test_soler_imperativo_negativo(ccg): +def test_soler_imperativo_negativo(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense("soler", Moods.es.Imperativo, Tenses.es.Negativo) assert [c[0] for c in tc] == [ "no suelas", @@ -927,18 +935,18 @@ def test_soler_imperativo_negativo(ccg): ] -def test_inflector_es_get_conj_obs(ccg): - co = ccg._get_conj_obs("abañar") +def test_inflector_es_get_conj_objs(ccg: CompleteConjugator): + co = ccg._get_conj_objs("abañar") assert co.verb.infinitive == "abañar" assert co.verb_stem == "abañ" -def test_inflector_es_get_verb_stem_from_template_name(ccg): - verb_stem = ccg._inflector.get_verb_stem_from_template_name("abañar", "cort:ar") +def test_inflector_es_get_verb_stem_from_template_name(ccg: CompleteConjugator): + verb_stem = ccg._get_inflector().get_verb_stem_from_template_name("abañar", "cort:ar") assert verb_stem == "abañ" -def test_inflector_es_conjugate_simple_mood_tense(tcg): +def test_inflector_es_conjugate_simple_mood_tense(tcg: TenseConjugator): mood = Moods.es.Indicativo tense = Tenses.es.Presente verb_stem = "abañ" @@ -954,8 +962,14 @@ def test_inflector_es_conjugate_simple_mood_tense(tcg): parser=None, ) tense_template = TenseTemplateParser(Lang.es, mood).parse(tense_elem) - tc = tcg._tense_conjugator_simple._conjugate_simple_mood_tense( - verb_stem, mood, tense, tense_template + tc = tcg._get_tense_conjugator_simple().conjugate_simple_mood_tense( + verb_stem, + mood, + tense, + tense_template, + is_reflexive=False, + conjugate_pronouns=True, + modify_stem_strip_accents=False, ) assert [c[0] for c in tc] == [ "yo abaño", @@ -994,20 +1008,20 @@ def test_inflector_es_conjugate_simple_mood_tense(tcg): ], ) def test_inflector_es_get_pronouns( - ccg, + ccg: CompleteConjugator, person: Person, number: Number, gender: Gender, is_reflexive: bool, expected_result: str, ): - pronoun = ccg._inflector.get_pronouns(person, number, gender)[0] + pronoun = ccg._get_inflector().get_pronouns(person, number, gender)[0] if is_reflexive: - pronoun = ccg._inflector.make_pronoun_reflexive(pronoun) + pronoun = ccg._get_inflector().make_pronoun_reflexive(pronoun) assert pronoun == expected_result -def test_inflector_es_conjugate_mood_indicativo_tense_presente_ar_voseo_tipo_3(ccg): +def test_inflector_es_conjugate_mood_indicativo_tense_presente_ar_voseo_tipo_3(ccg: CompleteConjugator): assert ccg.conjugate_mood_tense( "hablar", Moods.es.Indicativo, @@ -1076,7 +1090,7 @@ def test_inflector_es_conjugate_mood_indicativo_tense_presente_ar_voseo_tipo_3(c ) -def test_inflector_es_conjugate_mood_indicativo_tense_presente_er_voseo_tipo_3(ccg): +def test_inflector_es_conjugate_mood_indicativo_tense_presente_er_voseo_tipo_3(ccg: CompleteConjugator): assert ccg.conjugate_mood_tense( "beber", Moods.es.Indicativo, @@ -1141,7 +1155,7 @@ def test_inflector_es_conjugate_mood_indicativo_tense_presente_er_voseo_tipo_3(c ) -def test_inflector_es_conjugate_mood_indicativo_tense_presente_ir_voseo_tipo_3(ccg): +def test_inflector_es_conjugate_mood_indicativo_tense_presente_ir_voseo_tipo_3(ccg: CompleteConjugator): assert ccg.conjugate_mood_tense( "dormir", Moods.es.Indicativo, @@ -1210,7 +1224,7 @@ def test_inflector_es_conjugate_mood_indicativo_tense_presente_ir_voseo_tipo_3(c ) -def test_inflector_es_conjugate_mood_indicativo_tense_presente_ser_voseo_tipo_3(ccg): +def test_inflector_es_conjugate_mood_indicativo_tense_presente_ser_voseo_tipo_3(ccg: CompleteConjugator): assert ccg.conjugate_mood_tense( "ser", Moods.es.Indicativo, @@ -1263,7 +1277,7 @@ def test_inflector_es_conjugate_mood_indicativo_tense_presente_ser_voseo_tipo_3( ) -def test_inflector_es_conjugate_mood_subjuntivo_tense_presente_ser_voseo_tipo_3(ccg): +def test_inflector_es_conjugate_mood_subjuntivo_tense_presente_ser_voseo_tipo_3(ccg: CompleteConjugator): assert ccg.conjugate_mood_tense( "ser", Moods.es.Subjuntivo, @@ -1316,7 +1330,7 @@ def test_inflector_es_conjugate_mood_subjuntivo_tense_presente_ser_voseo_tipo_3( ) -def test_inflector_es_conjugate_mood_imperativo_tense_afirmativo_ar_voseo_tipo_3(ccg): +def test_inflector_es_conjugate_mood_imperativo_tense_afirmativo_ar_voseo_tipo_3(ccg: CompleteConjugator): assert ccg.conjugate_mood_tense( "hablar", Moods.es.Imperativo, @@ -1358,7 +1372,7 @@ def test_inflector_es_conjugate_mood_imperativo_tense_afirmativo_ar_voseo_tipo_3 ) -def test_inflector_es_conjugate_mood_imperativo_tense_negativo_ar_voseo_tipo_3(ccg): +def test_inflector_es_conjugate_mood_imperativo_tense_negativo_ar_voseo_tipo_3(ccg: CompleteConjugator): assert ccg.conjugate_mood_tense( "hablar", Moods.es.Imperativo, @@ -1400,7 +1414,7 @@ def test_inflector_es_conjugate_mood_imperativo_tense_negativo_ar_voseo_tipo_3(c ) -def test_inflector_es_conjugate_mood_imperativo_tense_afirmativo_ir_voseo_tipo_3(ccg): +def test_inflector_es_conjugate_mood_imperativo_tense_afirmativo_ir_voseo_tipo_3(ccg: CompleteConjugator): assert ccg.conjugate_mood_tense( "vivir", Moods.es.Imperativo, @@ -1440,7 +1454,7 @@ def test_inflector_es_conjugate_mood_imperativo_tense_afirmativo_ir_voseo_tipo_3 ) -def test_inflector_es_conjugate_mood_imperativo_tense_negativo_ir_voseo_tipo_3(ccg): +def test_inflector_es_conjugate_mood_imperativo_tense_negativo_ir_voseo_tipo_3(ccg: CompleteConjugator): assert ccg.conjugate_mood_tense( "vivir", Moods.es.Imperativo, @@ -1482,7 +1496,7 @@ def test_inflector_es_conjugate_mood_imperativo_tense_negativo_ir_voseo_tipo_3(c ) -def test_inflector_es_conjugate_mood_imperativo_tense_afirmativo_er_voseo_tipo_3(ccg): +def test_inflector_es_conjugate_mood_imperativo_tense_afirmativo_er_voseo_tipo_3(ccg: CompleteConjugator): assert ccg.conjugate_mood_tense( "beber", Moods.es.Imperativo, @@ -1522,7 +1536,7 @@ def test_inflector_es_conjugate_mood_imperativo_tense_afirmativo_er_voseo_tipo_3 ) -def test_inflector_es_conjugate_mood_imperativo_tense_negativo_er_voseo_tipo_3(ccg): +def test_inflector_es_conjugate_mood_imperativo_tense_negativo_er_voseo_tipo_3(ccg: CompleteConjugator): assert ccg.conjugate_mood_tense( "beber", Moods.es.Imperativo, @@ -1564,7 +1578,7 @@ def test_inflector_es_conjugate_mood_imperativo_tense_negativo_er_voseo_tipo_3(c ) -def test_inflector_es_conjugate_mood_imperativo_tense_afirmativo_ser_voseo_tipo_3(ccg): +def test_inflector_es_conjugate_mood_imperativo_tense_afirmativo_ser_voseo_tipo_3(ccg: CompleteConjugator): assert ccg.conjugate_mood_tense( "ser", Moods.es.Imperativo, @@ -1602,7 +1616,7 @@ def test_inflector_es_conjugate_mood_imperativo_tense_afirmativo_ser_voseo_tipo_ ) -def test_inflector_es_conjugate_mood_imperativo_tense_negativo_ser_voseo_tipo_3(ccg): +def test_inflector_es_conjugate_mood_imperativo_tense_negativo_ser_voseo_tipo_3(ccg: CompleteConjugator): assert ccg.conjugate_mood_tense( "ser", Moods.es.Imperativo, @@ -1644,13 +1658,13 @@ def test_inflector_es_conjugate_mood_imperativo_tense_negativo_ser_voseo_tipo_3( ) -def test_inflector_es_conjugate_simple_ser_infinititivo(tcg): +def test_inflector_es_conjugate_simple_ser_infinititivo(tcg: TenseConjugator): """ Test infinitivo because it has neither person, number, gender nor pronoun. """ infinitive = "ser" - co = tcg._get_conj_obs(infinitive) + co = tcg._get_conj_objs(infinitive) assert co.verb_stem == "" mood = Moods.fr.Infinitif tense = Tenses.fr.InfinitifPrésent @@ -1663,7 +1677,7 @@ def test_inflector_es_conjugate_simple_ser_infinititivo(tcg): parser=None, ) tense_template = TenseTemplateParser(Lang.es, mood).parse(tense_elem) - tc = tcg._tense_conjugator_simple._conjugate_simple_mood_tense( + tc = tcg._get_tense_conjugator_simple().conjugate_simple_mood_tense( co.verb_stem, mood, tense, diff --git a/tests/test_inflectors/test_inflector_fr.py b/tests/test_inflectors/test_inflector_fr.py index 1f8e975..224108b 100755 --- a/tests/test_inflectors/test_inflector_fr.py +++ b/tests/test_inflectors/test_inflector_fr.py @@ -1,20 +1,24 @@ +# pyright: reportPrivateUsage=false + import pytest from lxml import etree -from verbecc.src.conjugator.complete_conjugator import CompleteConjugator -from verbecc.src.conjugator.mood_conjugator import MoodConjugator -from verbecc.src.conjugator.tense_conjugator import TenseConjugator -from verbecc.src.defs.types.conjugation import Conjugation -from verbecc.src.defs.types.conjugation import TenseConjugation -from verbecc.src.defs.types.exceptions import ConjugatorError -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang -from verbecc.src.defs.types.mood import Moods -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.tense import Tenses -from verbecc.src.parsers.tense_template_parser import TenseTemplateParser -from verbecc.src.defs.types.pronoun import Pronoun, Pronouns +from verbecc.core.conjugator.complete_conjugator import CompleteConjugator +from verbecc.core.conjugator.mood_conjugator import MoodConjugator +from verbecc.core.conjugator.tense_conjugator import TenseConjugator +from verbecc.core.defs.types.conjugation import Conjugation +from verbecc.core.defs.types.conjugation import TenseConjugation +from verbecc.core.defs.types.conjugation.complete_conjugation import CompleteConjugation +from verbecc.core.defs.types.data.verb import Verb +from verbecc.core.defs.types.exceptions import ConjugatorError +from verbecc.core.defs.types.gender import Gender +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.defs.types.mood import Moods +from verbecc.core.defs.types.number import Number +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.tense import Tenses +from verbecc.core.parsers.tense_template_parser import TenseTemplateParser +from verbecc.core.defs.types.pronoun import Pronouns @pytest.fixture(scope="module") @@ -35,39 +39,39 @@ def tcg(): yield tcg -def test_all_verbs_have_templates(ccg): +def test_all_verbs_have_templates(ccg: CompleteConjugator): verbs = ccg.get_verbs() template_names = ccg.get_template_names() - missing_templates = set() + missing_templates: set[str] = set() for verb in verbs: if verb.template not in template_names: missing_templates.add(verb.template) assert len(missing_templates) == 0 -def test_add_subjunctive_relative_prounoun(ccg): +def test_add_subjunctive_relative_prounoun(ccg: CompleteConjugator): assert ( - ccg._inflector.add_subjunctive_relative_pronoun("tu manges", "") + ccg._get_inflector().add_subjunctive_relative_pronoun("tu manges", "") == "que tu manges" ) assert ( - ccg._inflector.add_subjunctive_relative_pronoun("il mange", "") == "qu'il mange" + ccg._get_inflector().add_subjunctive_relative_pronoun("il mange", "") == "qu'il mange" ) assert ( - ccg._inflector.add_subjunctive_relative_pronoun("elles mangent", "") + ccg._get_inflector().add_subjunctive_relative_pronoun("elles mangent", "") == "qu'elles mangent" ) -def testadd_reflexive_pronoun(ccg): - assert ccg._inflector.add_reflexive_pronoun("lever") == "se lever" - assert ccg._inflector.add_reflexive_pronoun("écrouler") == "s'écrouler" +def testadd_reflexive_pronoun(ccg: CompleteConjugator): + assert ccg._get_inflector().add_reflexive_pronoun("lever") == "se lever" + assert ccg._get_inflector().add_reflexive_pronoun("écrouler") == "s'écrouler" -def testsplit_reflexive(ccg): - assert ccg._inflector.split_reflexive("se lever") == (True, "lever") - assert ccg._inflector.split_reflexive("s'écrouler") == (True, "écrouler") - assert ccg._inflector.split_reflexive("secouer") == (False, "secouer") +def testsplit_reflexive(ccg: CompleteConjugator): + assert ccg._get_inflector().split_reflexive("se lever") == (True, "lever") + assert ccg._get_inflector().split_reflexive("s'écrouler") == (True, "écrouler") + assert ccg._get_inflector().split_reflexive("secouer") == (False, "secouer") @pytest.mark.parametrize( @@ -79,15 +83,17 @@ def testsplit_reflexive(ccg): ("manger", True), ], ) -def test_inflector_fr_verb_can_be_reflexive(ccg, infinitive, expected_result): - assert ccg._inflector.verb_can_be_reflexive(infinitive) == expected_result +def test_inflector_fr_verb_can_be_reflexive( + ccg: CompleteConjugator, infinitive: str, expected_result: bool +): + assert ccg._get_inflector().verb_can_be_reflexive(infinitive) == expected_result -def test_inflector_fr_impersonal_verbs(ccg): +def test_inflector_fr_impersonal_verbs(ccg: CompleteConjugator): impersonal_verbs = [ v.infinitive - for v in ccg._inflector._verbs - if ccg._inflector._is_impersonal_verb(v.infinitive) + for v in ccg._get_inflector()._get_verbs() + if ccg._get_inflector().private_is_impersonal_verb(v.infinitive) ] assert set(impersonal_verbs) == set( [ @@ -114,11 +120,11 @@ def test_inflector_fr_impersonal_verbs(ccg): ) -def test_inflector_fr_conjugate_simple_mood_tense(tcg): +def test_inflector_fr_conjugate_simple_mood_tense(tcg: TenseConjugator): infinitive = "manger" mood = Moods.fr.Indicatif tense = Tenses.fr.Présent - co = tcg._get_conj_obs(infinitive) + co = tcg._get_conj_objs(infinitive) assert co.verb_stem == "man" tense_elem = etree.fromstring( """ @@ -132,7 +138,7 @@ def test_inflector_fr_conjugate_simple_mood_tense(tcg): parser=None, ) tense_template = TenseTemplateParser(Lang.fr, mood).parse(tense_elem) - tc = tcg._tense_conjugator_simple._conjugate_simple_mood_tense( + tc = tcg._get_tense_conjugator_simple().conjugate_simple_mood_tense( co.verb_stem, mood, tense, tense_template ) assert tc == TenseConjugation( @@ -177,19 +183,19 @@ def test_inflector_fr_conjugate_simple_mood_tense(tcg): ) -def test_inflector_fr_get_verb_stem_from_template_name(ccg): - verb_stem = ccg._inflector.get_verb_stem_from_template_name("manger", "man:ger") +def test_inflector_fr_get_verb_stem_from_template_name(ccg: CompleteConjugator): + verb_stem = ccg._get_inflector().get_verb_stem_from_template_name("manger", "man:ger") assert verb_stem == "man" - verb_stem = ccg._inflector.get_verb_stem_from_template_name("téléphoner", "aim:er") + verb_stem = ccg._get_inflector().get_verb_stem_from_template_name("téléphoner", "aim:er") assert verb_stem == "téléphon" - verb_stem = ccg._inflector.get_verb_stem_from_template_name("vendre", "ten:dre") + verb_stem = ccg._get_inflector().get_verb_stem_from_template_name("vendre", "ten:dre") assert verb_stem == "ven" # In the case of irregular verbs, the verb stem is empty string - verb_stem = ccg._inflector.get_verb_stem_from_template_name("aller", ":aller") + verb_stem = ccg._get_inflector().get_verb_stem_from_template_name("aller", ":aller") assert verb_stem == "" # The infinitive ending must match the template ending with pytest.raises(ConjugatorError): - verb_stem = ccg._inflector.get_verb_stem_from_template_name("vendre", "man:ger") + verb_stem = ccg._get_inflector().get_verb_stem_from_template_name("vendre", "man:ger") @pytest.mark.parametrize( @@ -214,16 +220,16 @@ def test_inflector_fr_get_verb_stem_from_template_name(ccg): ], ) def test_inflector_fr_get_pronouns( - ccg, + ccg: CompleteConjugator, person: Person, number: Number, gender: Gender, is_reflexive: bool, expected_result: str, ): - pronoun = ccg._inflector.get_pronouns(person, number, gender)[0] + pronoun = ccg._get_inflector().get_pronouns(person, number, gender)[0] if is_reflexive: - pronoun = ccg._inflector.make_pronoun_reflexive(pronoun) + pronoun = ccg._get_inflector().make_pronoun_reflexive(pronoun) assert pronoun == expected_result @@ -274,7 +280,7 @@ def test_inflector_fr_get_pronouns( ), ], ) -def test_pronoun_combined_vowel_h_non_aspiré(ccg, infinitive, expected_result): +def test_pronoun_combined_vowel_h_non_aspiré(ccg: CompleteConjugator, infinitive: str, expected_result: list[str]): cc = ccg.conjugate(infinitive) moods_conj = cc.get_moods() mood_conj = moods_conj[Moods.fr.Indicatif] @@ -301,7 +307,7 @@ def test_pronoun_combined_vowel_h_non_aspiré(ccg, infinitive, expected_result): ) ], ) -def test_subjonctif_vowel_h_non_aspiré(ccg, infinitive, expected_result): +def test_subjonctif_vowel_h_non_aspiré(ccg: CompleteConjugator, infinitive: str, expected_result: list[str]): cc = ccg.conjugate(infinitive) moods_conj = cc.get_moods() mood_conj = moods_conj[Moods.fr.Subjonctif] @@ -309,7 +315,7 @@ def test_subjonctif_vowel_h_non_aspiré(ccg, infinitive, expected_result): assert [c[0] for c in tense_conj] == expected_result -def test_fr_get_str_id(ccg): +def test_fr_get_str_id(ccg: CompleteConjugator): cc = ccg.conjugate("parler") assert cc.get_str_id() == "fr:parler" mc = cc[Moods.fr.Indicatif] @@ -320,19 +326,19 @@ def test_fr_get_str_id(ccg): assert tc[1].get_str_id() == "fr:parler:indicatif:présent:2:s::tu" -def test_can_conjugate_all_verbs(ccg): - verbs = ccg.get_verbs() - all_conjugations = {} +def test_can_conjugate_all_verbs(ccg: CompleteConjugator): + verbs: list[Verb] = ccg.get_verbs() + all_conjugations: dict[Verb, CompleteConjugation] = {} for verb in verbs: conjugation = ccg.conjugate(verb.infinitive) all_conjugations[verb] = conjugation assert len(all_conjugations) == len(verbs) -def test_inflector_fr_conjugate_compound_raser(tcg): +def test_inflector_fr_conjugate_compound_raser(tcg: TenseConjugator): infinitive = "raser" - co = tcg._get_conj_obs(infinitive) - tc = tcg._tense_conjugator_compound._conjugate_compound_mood_tense( + co = tcg._get_conj_objs(infinitive) + tc = tcg._get_tense_conjugator_compound()._conjugate_compound_mood_tense( co, Moods.fr.Subjonctif, Tenses.fr.Passé, @@ -403,7 +409,7 @@ def test_inflector_fr_conjugate_compound_raser(tcg): ) -def test_inflector_fr_conjugate_compound_se_raser(tcg): +def test_inflector_fr_conjugate_compound_se_raser(tcg: TenseConjugator): """ test targeting: - reflexive verb conjugation @@ -411,8 +417,8 @@ def test_inflector_fr_conjugate_compound_se_raser(tcg): - Note: In French, all reflexive verbs are conjugated with être """ infinitive = "se raser" - co = tcg._get_conj_obs(infinitive) - tc = tcg._tense_conjugator_compound._conjugate_compound_mood_tense( + co = tcg._get_conj_objs(infinitive) + tc = tcg._get_tense_conjugator_compound()._conjugate_compound_mood_tense( co, Moods.fr.Subjonctif, Tenses.fr.Passé, @@ -526,14 +532,14 @@ def test_inflector_fr_conjugate_compound_se_raser(tcg): ) -def test_inflector_fr_conjugate_compound_parler_indicative_passé_composé(tcg): +def test_inflector_fr_conjugate_compound_parler_indicative_passé_composé(tcg: TenseConjugator): """ test targeting: - compound verb conjugation with a verb not conjugated with être (non-inflected participle) """ infinitive = "parler" - co = tcg._get_conj_obs(infinitive) - tc = tcg._tense_conjugator_compound._conjugate_compound_mood_tense( + co = tcg._get_conj_objs(infinitive) + tc = tcg._get_tense_conjugator_compound()._conjugate_compound_mood_tense( co, Moods.fr.Indicatif, Tenses.fr.PasséComposé, @@ -596,7 +602,7 @@ def test_inflector_fr_conjugate_compound_parler_indicative_passé_composé(tcg): ) -def test_inflector_fr_conjugate_simple_avoir_indicatif_présent_nopronouns(tcg): +def test_inflector_fr_conjugate_simple_avoir_indicatif_présent_nopronouns(tcg: TenseConjugator): """ Given: a verb in infinitive form @@ -606,23 +612,23 @@ def test_inflector_fr_conjugate_simple_avoir_indicatif_présent_nopronouns(tcg): The result is Pronouns.fr.je, ["ai"] etc. instead of Pronouns.fr.je, ["j'ai"] etc. """ infinitive = "avoir" - co = tcg._get_conj_obs(infinitive) + co = tcg._get_conj_objs(infinitive) assert co.verb_stem == "" mood = Moods.fr.Indicatif tense = Tenses.fr.Présent tense_elem = etree.fromstring( """ -

ai

-

as

-

a

-

avons

-

avez

-

ont

- """, +

ai

+

as

+

a

+

avons

+

avez

+

ont

+ """, parser=None, ) tense_template = TenseTemplateParser(Lang.fr, mood).parse(tense_elem) - tc = tcg._tense_conjugator_simple._conjugate_simple_mood_tense( + tc = tcg._get_tense_conjugator_simple().conjugate_simple_mood_tense( co.verb_stem, mood, tense, @@ -653,23 +659,23 @@ def test_inflector_fr_conjugate_simple_avoir_indicatif_présent_nopronouns(tcg): ) -def test_inflector_fr_conjugate_simple_avoir_participe_participe_passé(tcg): +def test_inflector_fr_conjugate_simple_avoir_participe_participe_passé(tcg: TenseConjugator): infinitive = "avoir" - co = tcg._get_conj_obs(infinitive) + co = tcg._get_conj_objs(infinitive) assert co.verb_stem == "" mood = Moods.fr.Participe tense = Tenses.fr.ParticipePassé tense_elem = etree.fromstring( """ -

eu

-

eus

-

eue

-

eues

-
""", +

eu

+

eus

+

eue

+

eues

+ """, parser=None, ) tense_template = TenseTemplateParser(Lang.fr, mood).parse(tense_elem) - tc = tcg._tense_conjugator_simple._conjugate_simple_mood_tense( + tc = tcg._get_tense_conjugator_simple().conjugate_simple_mood_tense( co.verb_stem, mood, tense, @@ -690,20 +696,20 @@ def test_inflector_fr_conjugate_simple_avoir_participe_participe_passé(tcg): ) -def test_inflector_fr_conjugate_simple_avoir_particpe_participe_présent(tcg): +def test_inflector_fr_conjugate_simple_avoir_particpe_participe_présent(tcg: TenseConjugator): infinitive = "avoir" - co = tcg._get_conj_obs(infinitive) + co = tcg._get_conj_objs(infinitive) assert co.verb_stem == "" mood = Moods.fr.Participe tense = Tenses.fr.ParticipePresent tense_elem = etree.fromstring( """ -

ayant

-
""", +

ayant

+ """, parser=None, ) tense_template = TenseTemplateParser(Lang.fr, mood).parse(tense_elem) - tc = tcg._tense_conjugator_simple._conjugate_simple_mood_tense( + tc = tcg._get_tense_conjugator_simple().conjugate_simple_mood_tense( co.verb_stem, mood, tense, @@ -720,24 +726,24 @@ def test_inflector_fr_conjugate_simple_avoir_particpe_participe_présent(tcg): ) -def test_inflector_fr_conjugate_simple_avoir_infinitif_présent(tcg): +def test_inflector_fr_conjugate_simple_avoir_infinitif_présent(tcg: TenseConjugator): """ Test infinitif because it's the only one with neither person, number, gender nor pronoun. """ infinitive = "avoir" - co = tcg._get_conj_obs(infinitive) + co = tcg._get_conj_objs(infinitive) assert co.verb_stem == "" mood = Moods.fr.Infinitif tense = Tenses.fr.InfinitifPrésent tense_elem = etree.fromstring( """ -

avoir

-
""", +

avoir

+ """, parser=None, ) tense_template = TenseTemplateParser(Lang.fr, mood).parse(tense_elem) - tc = tcg._tense_conjugator_simple._conjugate_simple_mood_tense( + tc = tcg._get_tense_conjugator_simple().conjugate_simple_mood_tense( co.verb_stem, mood, tense, @@ -754,22 +760,22 @@ def test_inflector_fr_conjugate_simple_avoir_infinitif_présent(tcg): ) -def test_inflector_fr_conjugate_simple_avoir_imperatif_présent(tcg): +def test_inflector_fr_conjugate_simple_avoir_imperatif_présent(tcg: TenseConjugator): infinitive = "avoir" - co = tcg._get_conj_obs(infinitive) + co = tcg._get_conj_objs(infinitive) assert co.verb_stem == "" mood = Moods.fr.Imperatif tense = Tenses.fr.ImperatifPrésent tense_elem = etree.fromstring( """ -

aie

-

ayons

-

ayez

-
""", +

aie

+

ayons

+

ayez

+ """, parser=None, ) tense_template = TenseTemplateParser(Lang.fr, mood).parse(tense_elem) - tc = tcg._tense_conjugator_simple._conjugate_simple_mood_tense( + tc = tcg._get_tense_conjugator_simple().conjugate_simple_mood_tense( co.verb_stem, mood, tense, @@ -790,7 +796,7 @@ def test_inflector_fr_conjugate_simple_avoir_imperatif_présent(tcg): assert tc == expected_value -def test_combine_pronoun_and_conj(ccg): +def test_combine_pronoun_and_conj(ccg: CompleteConjugator): """ E.g. "je" + "mange" = "je mange" E.g. "elle" + "a " = "elle a" @@ -801,31 +807,31 @@ def test_combine_pronoun_and_conj(ccg): E.g. "ils se" = "habillent" = "ils s'habillent" E.g. "ils se" + "étaient" = "ils s'étaient """ - assert ccg._inflector.combine_pronoun_and_conj("je", "mange") == "je mange" - assert ccg._inflector.combine_pronoun_and_conj("elle", "a") == "elle a" - assert ccg._inflector.combine_pronoun_and_conj("je", "ai") == "j'ai" - assert ccg._inflector.combine_pronoun_and_conj("je me", "habille") == "je m'habille" + assert ccg._get_inflector().combine_pronoun_and_conj("je", "mange") == "je mange" + assert ccg._get_inflector().combine_pronoun_and_conj("elle", "a") == "elle a" + assert ccg._get_inflector().combine_pronoun_and_conj("je", "ai") == "j'ai" + assert ccg._get_inflector().combine_pronoun_and_conj("je me", "habille") == "je m'habille" assert ( - ccg._inflector.combine_pronoun_and_conj("tu te", "habilles") == "tu t'habilles" + ccg._get_inflector().combine_pronoun_and_conj("tu te", "habilles") == "tu t'habilles" ) - assert ccg._inflector.combine_pronoun_and_conj("il se", "habille") == "il s'habille" + assert ccg._get_inflector().combine_pronoun_and_conj("il se", "habille") == "il s'habille" assert ( - ccg._inflector.combine_pronoun_and_conj("elle se", "habille") + ccg._get_inflector().combine_pronoun_and_conj("elle se", "habille") == "elle s'habille" ) assert ( - ccg._inflector.combine_pronoun_and_conj("ils se", "habille") == "ils s'habille" + ccg._get_inflector().combine_pronoun_and_conj("ils se", "habille") == "ils s'habille" ) assert ( - ccg._inflector.combine_pronoun_and_conj("elles se", "habille") + ccg._get_inflector().combine_pronoun_and_conj("elles se", "habille") == "elles s'habille" ) - assert ccg._inflector.combine_pronoun_and_conj("je", "étais") == "j'étais" - assert ccg._inflector.combine_pronoun_and_conj("il", "était") == "il était" - assert ccg._inflector.combine_pronoun_and_conj("elle", "était") == "elle était" - assert ccg._inflector.combine_pronoun_and_conj("elle se", "était") == "elle s'était" + assert ccg._get_inflector().combine_pronoun_and_conj("je", "étais") == "j'étais" + assert ccg._get_inflector().combine_pronoun_and_conj("il", "était") == "il était" + assert ccg._get_inflector().combine_pronoun_and_conj("elle", "était") == "elle était" + assert ccg._get_inflector().combine_pronoun_and_conj("elle se", "était") == "elle s'était" assert ( - ccg._inflector.combine_pronoun_and_conj("ils se", "étaient") == "ils s'étaient" + ccg._get_inflector().combine_pronoun_and_conj("ils se", "étaient") == "ils s'étaient" ) diff --git a/tests/test_inflectors/test_inflector_fr_to_json.py b/tests/test_inflectors/test_inflector_fr_to_json.py index 85f56cd..8e6b3f6 100644 --- a/tests/test_inflectors/test_inflector_fr_to_json.py +++ b/tests/test_inflectors/test_inflector_fr_to_json.py @@ -1,12 +1,19 @@ import pytest import json +import sys +from pathlib import Path +from typing import Any -from tests.common import assert_json_str_equal +try: + from tests.common import assert_json_str_equal +except ModuleNotFoundError: + sys.path.insert(0, str(Path(__file__).resolve().parents[1])) + from common import assert_json_str_equal -from verbecc.src.conjugator.complete_conjugator import CompleteConjugator -from verbecc.src.conjugator.mood_conjugator import MoodConjugator -from verbecc.src.conjugator.tense_conjugator import TenseConjugator -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.conjugator.complete_conjugator import CompleteConjugator +from verbecc.core.conjugator.mood_conjugator import MoodConjugator +from verbecc.core.conjugator.tense_conjugator import TenseConjugator +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang @pytest.fixture(scope="module") @@ -27,7 +34,7 @@ def tcg(): yield tcg -expected_value_conj_manger = { +expected_value_conj_manger: dict[str, dict[str, Any]] = { "moods": { "conditionnel": { "passé": [ @@ -414,7 +421,7 @@ def tcg(): }, } -expected_value_conj_pouvoir = { +expected_value_conj_pouvoir: dict[str, dict[str, Any]] = { "moods": { "conditionnel": { "passé": [ @@ -686,7 +693,7 @@ def tcg(): } -expected_value_conj_pleuvoir = { +expected_value_conj_pleuvoir: dict[str, dict[str, Any]] = { "moods": { "conditionnel": { "passé": [ @@ -881,7 +888,7 @@ def tcg(): } -expected_value_conj_se_lever = { +expected_value_conj_se_lever: dict[str, dict[str, Any]] = { "moods": { "conditionnel": { "passé": [ @@ -1565,8 +1572,8 @@ def tcg(): python -m pytest . -vv -k test_conjugate[Se lever-expected_value4] I think this pattern of -many "def test__(fixtures)" to one "def run_test_(value)" -as exhibited bleow is better than @pytest.mark.parametrize in many cases. +many "def test__(fixtures)" to one "def run_test_(value)" +as exhibited bleow is better than @pytest.mark.parametrize in many cases. Another benefit is being able to jump directly to the specific test by clicking on it in VSCode. You can't do that with parametrize. @@ -1574,27 +1581,31 @@ def tcg(): """ -def run_test_conjugate_to_json_(ccg, infinitive, expected_value): +def run_test_conjugate_to_json_( + ccg: CompleteConjugator, + infinitive: str, + expected_value: dict[str, dict[str, Any]], +): cc = ccg.conjugate(infinitive) conj_json = cc.to_json(beautify=False) assert_json_str_equal(conj_json, json.dumps(expected_value)) -def test_conjugate_to_json_manger(ccg): +def test_conjugate_to_json_manger(ccg: CompleteConjugator): run_test_conjugate_to_json_(ccg, "manger", expected_value_conj_manger) -def test_conjugate_to_json_pouvoir(ccg): +def test_conjugate_to_json_pouvoir(ccg: CompleteConjugator): run_test_conjugate_to_json_(ccg, "pouvoir", expected_value_conj_pouvoir) -def test_conjugate_to_json_Pouvoir(ccg): +def test_conjugate_to_json_Pouvoir(ccg: CompleteConjugator): run_test_conjugate_to_json_(ccg, "Pouvoir", expected_value_conj_pouvoir) -def test_conjugate_to_json_pleuvoir(ccg): +def test_conjugate_to_json_pleuvoir(ccg: CompleteConjugator): run_test_conjugate_to_json_(ccg, "pleuvoir", expected_value_conj_pleuvoir) -def test_conjugate_to_json_Se_lever(ccg): +def test_conjugate_to_json_Se_lever(ccg: CompleteConjugator): run_test_conjugate_to_json_(ccg, "Se lever", expected_value_conj_se_lever) diff --git a/tests/test_inflectors/test_inflector_fr_to_yaml.py b/tests/test_inflectors/test_inflector_fr_to_yaml.py index 5187fdf..20d27a9 100644 --- a/tests/test_inflectors/test_inflector_fr_to_yaml.py +++ b/tests/test_inflectors/test_inflector_fr_to_yaml.py @@ -1,9 +1,7 @@ -# coding: utf-8 import pytest -from verbecc.src.defs.types.conjugation import CompleteConjugation -from verbecc.src.conjugator.complete_conjugator import CompleteConjugator -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.conjugator.complete_conjugator import CompleteConjugator +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang @pytest.fixture(scope="module") @@ -1022,5 +1020,5 @@ def run_test_conjugate_to_yaml( assert conj_yaml == expected_value -def test_conjugate_to_yaml_Se_lever(ccg): +def test_conjugate_to_yaml_Se_lever(ccg: CompleteConjugator) -> None: run_test_conjugate_to_yaml(ccg, "Se lever", expected_value_conj_se_lever) diff --git a/tests/test_inflectors/test_inflector_it.py b/tests/test_inflectors/test_inflector_it.py index defb5ab..e319487 100755 --- a/tests/test_inflectors/test_inflector_it.py +++ b/tests/test_inflectors/test_inflector_it.py @@ -1,15 +1,16 @@ +# pyright: reportPrivateUsage=false + import pytest -from verbecc.src.conjugator.complete_conjugator import CompleteConjugator -from verbecc.src.conjugator.mood_conjugator import MoodConjugator -from verbecc.src.conjugator.tense_conjugator import TenseConjugator -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang -from verbecc.src.defs.types.mood import Moods -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.tense import Tenses -from verbecc.src.defs.types.pronoun import Pronoun, Pronouns +from verbecc.core.conjugator.complete_conjugator import CompleteConjugator +from verbecc.core.conjugator.mood_conjugator import MoodConjugator +from verbecc.core.conjugator.tense_conjugator import TenseConjugator +from verbecc.core.defs.types.gender import Gender +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.defs.types.mood import Moods +from verbecc.core.defs.types.number import Number +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.tense import Tenses @pytest.fixture(scope="module") @@ -30,10 +31,10 @@ def tcg(): yield tcg -def test_all_verbs_have_templates(ccg): +def test_all_verbs_have_templates(ccg: CompleteConjugator): verbs = ccg.get_verbs() template_names = ccg.get_template_names() - missing_templates = set() + missing_templates: set[str] = set() for verb in verbs: if verb.template not in template_names: missing_templates.add(verb.template) @@ -102,19 +103,23 @@ def test_all_verbs_have_templates(ccg): ], ) def test_inflector_it_conjugate_mood_tense( - ccg, infinitive, mood, tense, expected_result + ccg: CompleteConjugator, + infinitive: str, + mood: str, + tense: str, + expected_result: list[str], ): tc = ccg.conjugate_mood_tense(infinitive, mood, tense) assert [c[0] for c in tc] == expected_result -def test_inflector_it_conjugate(ccg): - assert ccg.conjugate("avere") != None +def test_inflector_it_conjugate(ccg: CompleteConjugator): + assert ccg.conjugate("avere") is not None -def test_inflector_itadd_subjunctive_relative_pronoun(ccg): +def test_inflector_itadd_subjunctive_relative_pronoun(ccg: CompleteConjugator): assert ( - ccg._inflector.add_subjunctive_relative_pronoun("io abbia", "") + ccg._get_inflector().add_subjunctive_relative_pronoun("io abbia", "") == "che io abbia" ) @@ -141,16 +146,16 @@ def test_inflector_itadd_subjunctive_relative_pronoun(ccg): ], ) def test_inflector_it_get_pronouns( - ccg, + ccg: CompleteConjugator, person: Person, number: Number, gender: Gender, is_reflexive: bool, expected_result: str, ): - pronoun = ccg._inflector.get_pronouns(person, number, gender)[0] + pronoun = ccg._get_inflector().get_pronouns(person, number, gender)[0] if is_reflexive: - pronoun = ccg._inflector.make_pronoun_reflexive(pronoun) + pronoun = ccg._get_inflector().make_pronoun_reflexive(pronoun) assert pronoun == expected_result @@ -195,7 +200,9 @@ def test_inflector_it_get_pronouns( ), ], ) -def test_indicative_present(ccg, infinitive, expected_result): +def test_indicative_present( + ccg: CompleteConjugator, infinitive: str, expected_result: list[str] +): cc = ccg.conjugate(infinitive) mc = cc[Moods.it.Indicativo] tc = mc[Tenses.it.Presente] @@ -248,7 +255,9 @@ def test_indicative_present(ccg, infinitive, expected_result): ), ], ) -def test_passato_prossimo(ccg, infinitive, expected_result): +def test_passato_prossimo( + ccg: CompleteConjugator, infinitive: str, expected_result: list[str] +): cc = ccg.conjugate(infinitive) mc = cc[Moods.it.Indicativo] tc = mc[Tenses.it.PassatoProssimo] @@ -272,7 +281,9 @@ def test_passato_prossimo(ccg, infinitive, expected_result): ), ], ) -def test_alzarsi_indicative_present(ccg, infinitive, expected_result): +def test_alzarsi_indicative_present( + ccg: CompleteConjugator, infinitive: str, expected_result: list[str] +): cc = ccg.conjugate(infinitive) mc = cc[Moods.it.Indicativo] tc = mc[Tenses.it.Presente] @@ -302,7 +313,7 @@ def test_alzarsi_indicative_present(ccg, infinitive, expected_result): ], ) def test_inflector_it_alzarsi_indicativo_passato_prossimo( - ccg, infinitive, expected_result + ccg: CompleteConjugator, infinitive: str, expected_result: list[str] ): cc = ccg.conjugate(infinitive) mc = cc[Moods.it.Indicativo] @@ -310,10 +321,10 @@ def test_inflector_it_alzarsi_indicativo_passato_prossimo( assert [c[0] for c in tc] == expected_result -def test_inflector_it_conjugate_compound_essere_indicativo_passato_prossimo(tcg): +def test_inflector_it_conjugate_compound_essere_indicativo_passato_prossimo(tcg: TenseConjugator): infinitive = "essere" - co = tcg._get_conj_obs(infinitive) - tc = tcg._tense_conjugator_compound._conjugate_compound_mood_tense( + co = tcg._get_conj_objs(infinitive) + tc = tcg._get_tense_conjugator_compound()._conjugate_compound_mood_tense( co, Moods.it.Indicativo, Tenses.it.PassatoProssimo, diff --git a/tests/test_inflectors/test_inflector_pt.py b/tests/test_inflectors/test_inflector_pt.py index 529ce1a..733a85b 100755 --- a/tests/test_inflectors/test_inflector_pt.py +++ b/tests/test_inflectors/test_inflector_pt.py @@ -1,18 +1,19 @@ +# pyright: reportPrivateUsage=false + import pytest -from typing import List -from verbecc.src.conjugator.complete_conjugator import CompleteConjugator -from verbecc.src.conjugator.mood_conjugator import MoodConjugator -from verbecc.src.conjugator.tense_conjugator import TenseConjugator -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang -from verbecc.src.defs.types.mood import Mood -from verbecc.src.defs.types.mood import Moods -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.tense import Tense -from verbecc.src.defs.types.tense import Tenses -from verbecc.src.defs.types.pronoun import Pronoun, Pronouns +from verbecc.core.conjugator.complete_conjugator import CompleteConjugator +from verbecc.core.conjugator.mood_conjugator import MoodConjugator +from verbecc.core.conjugator.tense_conjugator import TenseConjugator +from verbecc.core.defs.types.conjugation.tense_conjugation import TenseConjugation +from verbecc.core.defs.types.gender import Gender +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.defs.types.mood import Mood +from verbecc.core.defs.types.mood import Moods +from verbecc.core.defs.types.number import Number +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.tense import Tense +from verbecc.core.defs.types.tense import Tenses @pytest.fixture(scope="module") @@ -33,17 +34,17 @@ def tcg(): yield tcg -def test_all_verbs_have_templates(ccg): +def test_all_verbs_have_templates(ccg: CompleteConjugator): verbs = ccg.get_verbs() - template_names = ccg.get_template_names() - missing_templates = set() + template_names: list[str] = ccg.get_template_names() + missing_templates: set[str] = set() for verb in verbs: if verb.template not in template_names: missing_templates.add(verb.template) assert len(missing_templates) == 0 -def test_conjugate_ter_subjuntivo_preterito_perfeito(ccg): +def test_conjugate_ter_subjuntivo_preterito_perfeito(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense( "ter", Moods.pt.Subjuntivo, Tenses.pt.PretéritoPerfeito ) @@ -61,7 +62,7 @@ def test_conjugate_ter_subjuntivo_preterito_perfeito(ccg): ] -def test_conjugate_ter_infinitivo_pessoal_presente(ccg): +def test_conjugate_ter_infinitivo_pessoal_presente(ccg: CompleteConjugator): """ Ref: https://www.conjugacao.com.br/verbo-ter/ @@ -84,11 +85,11 @@ def test_conjugate_ter_infinitivo_pessoal_presente(ccg): ] -def test_conjugate_ter_imperativo_afirmativo(ccg): +def test_conjugate_ter_imperativo_afirmativo(ccg: CompleteConjugator): """ Ref: https://www.conjugacao.com.br/verbo-ter/ """ - tc = ccg.conjugate_mood_tense("ter", Moods.pt.Imperativo, Tenses.pt.Afirmativo) + tc: TenseConjugation = ccg.conjugate_mood_tense("ter", Moods.pt.Imperativo, Tenses.pt.Afirmativo) assert [c[0] for c in tc] == [ "-", "tem tu", @@ -99,11 +100,11 @@ def test_conjugate_ter_imperativo_afirmativo(ccg): ] -def test_conjugate_ter_imperativo_negativo(ccg): +def test_conjugate_ter_imperativo_negativo(ccg: CompleteConjugator): """ Ref: https://www.conjugacao.com.br/verbo-ter/ """ - tc = ccg.conjugate_mood_tense("ter", Moods.pt.Imperativo, Tenses.pt.Negativo) + tc: TenseConjugation = ccg.conjugate_mood_tense("ter", Moods.pt.Imperativo, Tenses.pt.Negativo) assert [c[0] for c in tc] == [ "-", "não tenhas tu", @@ -114,7 +115,7 @@ def test_conjugate_ter_imperativo_negativo(ccg): ] -def test_conjugate_ter_infinitivo_pessoal_composto(ccg): +def test_conjugate_ter_infinitivo_pessoal_composto(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense( "ter", Moods.pt.Infinitivo, Tenses.pt.InfinitivoPessoalComposto ) @@ -721,11 +722,11 @@ def test_conjugate_ter_infinitivo_pessoal_composto(ccg): ], ) def test_inflector_pt_conjugate_mood_tense( - ccg, + ccg: CompleteConjugator, infinitive: str, mood: Mood, tense: Tense, - expected_result: List[str], + expected_result: list[str], ): tc = ccg.conjugate_mood_tense(infinitive, mood, tense) assert [c[0] for c in tc] == expected_result @@ -753,20 +754,20 @@ def test_inflector_pt_conjugate_mood_tense( ], ) def test_inflector_pt_get_pronouns( - ccg, + ccg: CompleteConjugator, person: Person, number: Number, gender: Gender, is_reflexive: bool, expected_result: str, ): - pronoun = ccg._inflector.get_pronouns(person, number, gender)[0] + pronoun = ccg._get_inflector().get_pronouns(person, number, gender)[0] if is_reflexive: - pronoun = ccg._inflector.make_pronoun_reflexive(pronoun) + pronoun = ccg._get_inflector().make_pronoun_reflexive(pronoun) assert pronoun == expected_result -def test_reflexive_indicativo_presente(ccg): +def test_reflexive_indicativo_presente(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense("vestir-se", Moods.pt.Indicativo, Tenses.pt.Presente) assert [c[0] for c in tc] == [ "eu visto-me", @@ -782,7 +783,7 @@ def test_reflexive_indicativo_presente(ccg): ] -def test_reflexive_indicativo_pretérito_perfeito_composto(ccg): +def test_reflexive_indicativo_pretérito_perfeito_composto(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense( "vestir-se", Moods.pt.Indicativo, Tenses.pt.PretéritoPerfeitoComposto ) diff --git a/tests/test_inflectors/test_inflector_ro.py b/tests/test_inflectors/test_inflector_ro.py index aacfd7e..e731ff6 100755 --- a/tests/test_inflectors/test_inflector_ro.py +++ b/tests/test_inflectors/test_inflector_ro.py @@ -1,22 +1,31 @@ +# pyright: reportPrivateUsage=false + import pytest -from verbecc.src.conjugator.complete_conjugator import CompleteConjugator -from verbecc.src.conjugator.mood_conjugator import MoodConjugator -from verbecc.src.conjugator.tense_conjugator import TenseConjugator -from verbecc.src.defs.types.conjugation import Conjugation -from verbecc.src.defs.types.conjugation import TenseConjugation -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang -from verbecc.src.defs.types.mood import Moods -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.tense import Tenses +from typing import Generator + +from verbecc.core.conjugator.complete_conjugator import CompleteConjugator +from verbecc.core.conjugator.mood_conjugator import MoodConjugator +from verbecc.core.conjugator.tense_conjugator import TenseConjugator +from verbecc.core.defs.types.conjugation import Conjugation +from verbecc.core.defs.types.conjugation import TenseConjugation +from verbecc.core.defs.types.gender import Gender +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.defs.types.mood import Moods +from verbecc.core.defs.types.mood import Mood +from verbecc.core.defs.types.number import Number +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.tense import Tenses +from verbecc.core.defs.types.tense import Tense +from verbecc.core.defs.types.pronoun import Pronouns @pytest.fixture(scope="module") -def ccg(): +def ccg() -> Generator[CompleteConjugator, None, None]: + # Setup ccg = CompleteConjugator(lang=Lang.ro) yield ccg + # Teardown @pytest.fixture(scope="module") @@ -31,10 +40,10 @@ def tcg(): yield tcg -def test_all_verbs_have_templates(ccg): +def test_all_verbs_have_templates(ccg: CompleteConjugator): verbs = ccg.get_verbs() template_names = ccg.get_template_names() - missing_templates = set() + missing_templates: set[str] = set() for verb in verbs: if verb.template not in template_names: missing_templates.add(verb.template) @@ -287,7 +296,7 @@ def test_all_verbs_have_templates(ccg): ], ) def test_inflector_ro_conjugate_mood_tense_str_only( - ccg, infinitive, mood, tense, expected_result + ccg: CompleteConjugator, infinitive: str, mood: Mood, tense: Tense, expected_result: list[str] ): tc = ccg.conjugate_mood_tense(infinitive, mood, tense) assert [c[0] for c in tc] == expected_result @@ -320,28 +329,28 @@ def test_inflector_ro_conjugate_mood_tense_str_only( Tenses.ro.PerfectCompus, [ Conjugation( - Person.First, Number.Singular, None, "eu", ["eu am făcut"] + Person.First, Number.Singular, None, Pronouns.ro.eu, ["eu am făcut"] ), Conjugation( - Person.Second, Number.Singular, None, "tu", ["tu ai făcut"] + Person.Second, Number.Singular, None, Pronouns.ro.tu, ["tu ai făcut"] ), Conjugation( - Person.Third, Number.Singular, Gender.m, "el", ["el a făcut"] + Person.Third, Number.Singular, Gender.m, Pronouns.ro.el, ["el a făcut"] ), Conjugation( - Person.Third, Number.Singular, Gender.f, "ea", ["ea a făcut"] + Person.Third, Number.Singular, Gender.f, Pronouns.ro.ea, ["ea a făcut"] ), Conjugation( - Person.First, Number.Plural, None, "noi", ["noi am făcut"] + Person.First, Number.Plural, None, Pronouns.ro.noi, ["noi am făcut"] ), Conjugation( - Person.Second, Number.Plural, None, "voi", ["voi aţi făcut"] + Person.Second, Number.Plural, None, Pronouns.ro.voi, ["voi aţi făcut"] ), Conjugation( - Person.Third, Number.Plural, Gender.m, "ei", ["ei au făcut"] + Person.Third, Number.Plural, Gender.m, Pronouns.ro.ei, ["ei au făcut"] ), Conjugation( - Person.Third, Number.Plural, Gender.f, "ele", ["ele au făcut"] + Person.Third, Number.Plural, Gender.f, Pronouns.ro.ele, ["ele au făcut"] ), ], ), @@ -354,42 +363,42 @@ def test_inflector_ro_conjugate_mood_tense_str_only( Tenses.ro.MaiMultCaPerfect, [ Conjugation( - Person.First, Number.Singular, None, "eu", ["eu făcusem"] + Person.First, Number.Singular, None, Pronouns.ro.eu, ["eu făcusem"] ), Conjugation( - Person.Second, Number.Singular, None, "tu", ["tu făcuseși"] + Person.Second, Number.Singular, None, Pronouns.ro.tu, ["tu făcuseși"] ), Conjugation( - Person.Third, Number.Singular, Gender.m, "el", ["el făcuse"] + Person.Third, Number.Singular, Gender.m, Pronouns.ro.el, ["el făcuse"] ), Conjugation( - Person.Third, Number.Singular, Gender.f, "ea", ["ea făcuse"] + Person.Third, Number.Singular, Gender.f, Pronouns.ro.ea, ["ea făcuse"] ), Conjugation( Person.First, Number.Plural, None, - "noi", + Pronouns.ro.noi, ["noi făcuserăm"], ), Conjugation( Person.Second, Number.Plural, None, - "voi", + Pronouns.ro.voi, ["voi făcuserăţi"], ), Conjugation( - Person.Third, Number.Plural, Gender.m, "ei", ["ei făcuseră"] + Person.Third, Number.Plural, Gender.m, Pronouns.ro.ei, ["ei făcuseră"] ), Conjugation( - Person.Third, Number.Plural, Gender.f, "ele", ["ele făcuseră"] + Person.Third, Number.Plural, Gender.f, Pronouns.ro.ele, ["ele făcuseră"] ), ], ), ), ( - "voi", + Pronouns.ro.voi, Moods.ro.Indicativ, Tenses.ro.Prezent, TenseConjugation( @@ -399,56 +408,56 @@ def test_inflector_ro_conjugate_mood_tense_str_only( Person.First, Number.Singular, None, - "eu", + Pronouns.ro.eu, ["eu voiesc", "eu voi"], ), Conjugation( Person.Second, Number.Singular, None, - "tu", + Pronouns.ro.tu, ["tu voiești", "tu vei"], ), Conjugation( Person.Third, Number.Singular, Gender.m, - "el", + Pronouns.ro.el, ["el voiește", "el va"], ), Conjugation( Person.Third, Number.Singular, Gender.f, - "ea", + Pronouns.ro.ea, ["ea voiește", "ea va"], ), Conjugation( Person.First, Number.Plural, None, - "noi", + Pronouns.ro.noi, ["noi voim", "noi vom"], ), Conjugation( Person.Second, Number.Plural, None, - "voi", + Pronouns.ro.voi, ["voi voiţi", "voi veţi"], ), Conjugation( Person.Third, Number.Plural, Gender.m, - "ei", + Pronouns.ro.ei, ["ei voiesc", "ei vor"], ), Conjugation( Person.Third, Number.Plural, Gender.f, - "ele", + Pronouns.ro.ele, ["ele voiesc", "ele vor"], ), ], @@ -461,39 +470,39 @@ def test_inflector_ro_conjugate_mood_tense_str_only( TenseConjugation( Tenses.ro.Prezent, [ - Conjugation(Person.First, Number.Singular, None, "eu", ["eu am"]), - Conjugation(Person.Second, Number.Singular, None, "tu", ["tu ai"]), + Conjugation(Person.First, Number.Singular, None, Pronouns.ro.eu, ["eu am"]), + Conjugation(Person.Second, Number.Singular, None, Pronouns.ro.tu, ["tu ai"]), Conjugation( Person.Third, Number.Singular, Gender.m, - "el", + Pronouns.ro.el, ["el a", "el are"], ), Conjugation( Person.Third, Number.Singular, Gender.f, - "ea", + Pronouns.ro.ea, ["ea a", "ea are"], ), Conjugation( Person.First, Number.Plural, None, - "noi", + Pronouns.ro.noi, ["noi am", "noi avem"], ), Conjugation( Person.Second, Number.Plural, None, - "voi", + Pronouns.ro.voi, ["voi aţi", "voi aveţi"], ), - Conjugation(Person.Third, Number.Plural, Gender.m, "ei", ["ei au"]), + Conjugation(Person.Third, Number.Plural, Gender.m, Pronouns.ro.ei, ["ei au"]), Conjugation( - Person.Third, Number.Plural, Gender.f, "ele", ["ele au"] + Person.Third, Number.Plural, Gender.f, Pronouns.ro.ele, ["ele au"] ), ], ), @@ -506,28 +515,28 @@ def test_inflector_ro_conjugate_mood_tense_str_only( Tenses.ro.Imperfect, [ Conjugation( - Person.First, Number.Singular, None, "eu", ["eu aveam"] + Person.First, Number.Singular, None, Pronouns.ro.eu, ["eu aveam"] ), Conjugation( - Person.Second, Number.Singular, None, "tu", ["tu aveai"] + Person.Second, Number.Singular, None, Pronouns.ro.tu, ["tu aveai"] ), Conjugation( - Person.Third, Number.Singular, Gender.m, "el", ["el avea"] + Person.Third, Number.Singular, Gender.m, Pronouns.ro.el, ["el avea"] ), Conjugation( - Person.Third, Number.Singular, Gender.f, "ea", ["ea avea"] + Person.Third, Number.Singular, Gender.f, Pronouns.ro.ea, ["ea avea"] ), Conjugation( - Person.First, Number.Plural, None, "noi", ["noi aveam"] + Person.First, Number.Plural, None, Pronouns.ro.noi, ["noi aveam"] ), Conjugation( - Person.Second, Number.Plural, None, "voi", ["voi aveaţi"] + Person.Second, Number.Plural, None, Pronouns.ro.voi, ["voi aveaţi"] ), Conjugation( - Person.Third, Number.Plural, Gender.m, "ei", ["ei aveau"] + Person.Third, Number.Plural, Gender.m, Pronouns.ro.ei, ["ei aveau"] ), Conjugation( - Person.Third, Number.Plural, Gender.f, "ele", ["ele aveau"] + Person.Third, Number.Plural, Gender.f, Pronouns.ro.ele, ["ele aveau"] ), ], ), @@ -535,13 +544,13 @@ def test_inflector_ro_conjugate_mood_tense_str_only( ], ) def test_inflector_ro_conjugate_mood_tense_tc( - ccg, infinitive, mood, tense, expected_result + ccg: CompleteConjugator, infinitive: str, mood: Mood, tense: Tense, expected_result: TenseConjugation ): tc = ccg.conjugate_mood_tense(infinitive, mood, tense) assert tc == expected_result -def test_inflector_ro_conjugate_mood_tense_viitor_1(ccg): +def test_inflector_ro_conjugate_mood_tense_viitor_1(ccg: CompleteConjugator): assert ccg.conjugate_mood_tense( "face", Moods.ro.Indicativ, @@ -549,19 +558,19 @@ def test_inflector_ro_conjugate_mood_tense_viitor_1(ccg): ) == TenseConjugation( Tenses.ro.Viitor1, [ - Conjugation(Person.First, Number.Singular, None, "eu", ["eu voi face"]), - Conjugation(Person.Second, Number.Singular, None, "tu", ["tu vei face"]), - Conjugation(Person.Third, Number.Singular, Gender.m, "el", ["el va face"]), - Conjugation(Person.Third, Number.Singular, Gender.f, "ea", ["ea va face"]), - Conjugation(Person.First, Number.Plural, None, "noi", ["noi vom face"]), - Conjugation(Person.Second, Number.Plural, None, "voi", ["voi veţi face"]), - Conjugation(Person.Third, Number.Plural, Gender.m, "ei", ["ei vor face"]), - Conjugation(Person.Third, Number.Plural, Gender.f, "ele", ["ele vor face"]), + Conjugation(Person.First, Number.Singular, None, Pronouns.ro.eu, ["eu voi face"]), + Conjugation(Person.Second, Number.Singular, None, Pronouns.ro.tu, ["tu vei face"]), + Conjugation(Person.Third, Number.Singular, Gender.m, Pronouns.ro.el, ["el va face"]), + Conjugation(Person.Third, Number.Singular, Gender.f, Pronouns.ro.ea, ["ea va face"]), + Conjugation(Person.First, Number.Plural, None, Pronouns.ro.noi, ["noi vom face"]), + Conjugation(Person.Second, Number.Plural, None, Pronouns.ro.voi, ["voi veţi face"]), + Conjugation(Person.Third, Number.Plural, Gender.m, Pronouns.ro.ei, ["ei vor face"]), + Conjugation(Person.Third, Number.Plural, Gender.f, Pronouns.ro.ele, ["ele vor face"]), ], ) -def test_inflector_ro_conjugate_mood_tense_viitor_1_popular(ccg): +def test_inflector_ro_conjugate_mood_tense_viitor_1_popular(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense( "face", Moods.ro.Indicativ, @@ -570,25 +579,25 @@ def test_inflector_ro_conjugate_mood_tense_viitor_1_popular(ccg): assert tc == TenseConjugation( Tenses.ro.Viitor1Popular, [ - Conjugation(Person.First, Number.Singular, None, "eu", ["eu o să fac"]), - Conjugation(Person.Second, Number.Singular, None, "tu", ["tu o să faci"]), + Conjugation(Person.First, Number.Singular, None, Pronouns.ro.eu, ["eu o să fac"]), + Conjugation(Person.Second, Number.Singular, None, Pronouns.ro.tu, ["tu o să faci"]), Conjugation( - Person.Third, Number.Singular, Gender.m, "el", ["el o să facă"] + Person.Third, Number.Singular, Gender.m, Pronouns.ro.el, ["el o să facă"] ), Conjugation( - Person.Third, Number.Singular, Gender.f, "ea", ["ea o să facă"] + Person.Third, Number.Singular, Gender.f, Pronouns.ro.ea, ["ea o să facă"] ), - Conjugation(Person.First, Number.Plural, None, "noi", ["noi o să facem"]), - Conjugation(Person.Second, Number.Plural, None, "voi", ["voi o să faceţi"]), - Conjugation(Person.Third, Number.Plural, Gender.m, "ei", ["ei o să facă"]), + Conjugation(Person.First, Number.Plural, None, Pronouns.ro.noi, ["noi o să facem"]), + Conjugation(Person.Second, Number.Plural, None, Pronouns.ro.voi, ["voi o să faceţi"]), + Conjugation(Person.Third, Number.Plural, Gender.m, Pronouns.ro.ei, ["ei o să facă"]), Conjugation( - Person.Third, Number.Plural, Gender.f, "ele", ["ele o să facă"] + Person.Third, Number.Plural, Gender.f, Pronouns.ro.ele, ["ele o să facă"] ), ], ) -def test_inflector_ro_conjugate_mood_tense_condițional_perfect(ccg): +def test_inflector_ro_conjugate_mood_tense_condițional_perfect(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense("avea", Moods.ro.Condițional, Tenses.ro.Perfect) assert [c[0] for c in tc] == [ "eu aş fi avut", @@ -602,7 +611,7 @@ def test_inflector_ro_conjugate_mood_tense_condițional_perfect(ccg): ] -def test_inflector_ro_conjugate_mood_tense_conjunctiv_perfect(ccg): +def test_inflector_ro_conjugate_mood_tense_conjunctiv_perfect(ccg: CompleteConjugator): tc = ccg.conjugate_mood_tense( "face", Moods.ro.Conjunctiv, @@ -623,11 +632,11 @@ def test_inflector_ro_conjugate_mood_tense_conjunctiv_perfect(ccg): @pytest.mark.parametrize( "person,number,gender,is_reflexive,expected_result", [ - (Person.First, Number.Singular, Gender.m, False, "eu"), + (Person.First, Number.Singular, Gender.m, False, Pronouns.ro.eu), (Person.First, Number.Singular, Gender.m, True, "eu mă"), - (Person.Second, Number.Singular, Gender.m, False, "tu"), + (Person.Second, Number.Singular, Gender.m, False, Pronouns.ro.tu), (Person.Second, Number.Singular, Gender.m, True, "tu te"), - (Person.Third, Number.Singular, Gender.m, False, "el"), + (Person.Third, Number.Singular, Gender.m, False, Pronouns.ro.el), (Person.Third, Number.Singular, Gender.m, True, "el se"), (Person.Third, Number.Singular, Gender.f, False, "ea"), (Person.Third, Number.Singular, Gender.f, True, "ea se"), @@ -635,21 +644,21 @@ def test_inflector_ro_conjugate_mood_tense_conjunctiv_perfect(ccg): (Person.First, Number.Plural, Gender.m, True, "noi ne"), (Person.Second, Number.Plural, Gender.m, False, "voi"), (Person.Second, Number.Plural, Gender.m, True, "voi vă"), - (Person.Third, Number.Plural, Gender.m, False, "ei"), + (Person.Third, Number.Plural, Gender.m, False, Pronouns.ro.ei), (Person.Third, Number.Plural, Gender.m, True, "ei se"), - (Person.Third, Number.Plural, Gender.f, False, "ele"), + (Person.Third, Number.Plural, Gender.f, False, Pronouns.ro.ele), (Person.Third, Number.Plural, Gender.f, True, "ele se"), ], ) def test_inflector_ro_get_pronouns( - ccg, + ccg: CompleteConjugator, person: Person, number: Number, gender: Gender, is_reflexive: bool, expected_result: str, ): - pronoun = ccg._inflector.get_pronouns(person, number, gender)[0] + pronoun = ccg._get_inflector().get_pronouns(person, number, gender)[0] if is_reflexive: - pronoun = ccg._inflector.make_pronoun_reflexive(pronoun) + pronoun = ccg._get_inflector().make_pronoun_reflexive(pronoun) assert pronoun == expected_result diff --git a/tests/test_localization.py b/tests/test_localization.py index 98c6260..dbf307e 100644 --- a/tests/test_localization.py +++ b/tests/test_localization.py @@ -1,6 +1,6 @@ import pytest -from verbecc.src.defs.constants.localization import xmood, xtense +from verbecc.core.defs.constants.localization import xmood, xtense @pytest.mark.parametrize( @@ -11,7 +11,7 @@ ("ca", "indicative", "indicatiu"), ], ) -def test_xmood(lang, mood, expected): +def test_xmood(lang: str, mood: str, expected: str): assert xmood(lang, mood) == expected @@ -24,5 +24,5 @@ def test_xmood(lang, mood, expected): ("fr", "present", "présent"), ], ) -def test_xtense(lang, tense, expected): +def test_xtense(lang: str, tense: str, expected: str): assert xtense(lang, tense) == expected diff --git a/tests/test_mlconjug.py b/tests/test_mlconjug.py index 8571e21..3df2e24 100755 --- a/tests/test_mlconjug.py +++ b/tests/test_mlconjug.py @@ -1,11 +1,11 @@ import pytest -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang -from verbecc.src.mlconjug.data_set import DataSet -from verbecc.src.mlconjug.predictor import TemplatePredictor -from verbecc.src.mlconjug.feature_extract import extract_verb_features -from verbecc.src.inflectors.lang.inflector_fr import InflectorFr -from verbecc.src.utils.config.verbecc_config_util import VerbeccConfigUtil +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.mlconjug.data_set import DataSet +from verbecc.core.mlconjug.predictor import TemplatePredictor +from verbecc.core.mlconjug.feature_extract import extract_verb_features +from verbecc.core.inflectors.lang.fr import InflectorFr +from verbecc.core.utils.config.verbecc_config_util import VerbeccConfigUtil config = VerbeccConfigUtil().load_config() @@ -13,7 +13,7 @@ @pytest.fixture(scope="module") def verb_template_pairs(): inf = InflectorFr() - yield [(v.infinitive, v.template) for v in inf._verbs] + yield [(v.infinitive, v.template) for v in inf.get_verbs()] def test_extract_verb_features(): @@ -36,13 +36,17 @@ def test_extract_verb_features(): ] -def test_DataSet_construct_dict_conjug(verb_template_pairs): +def test_DataSet_construct_dict_conjug( + verb_template_pairs: list[tuple[str, str]], +): if config.ENABLE_ML_PREDICTION: dict_conjug = DataSet(verb_template_pairs).dict_conjug assert "abaisser" in dict_conjug["aim:er"] -def test_DataSet_split_test_train(verb_template_pairs): +def test_DataSet_split_test_train( + verb_template_pairs: list[tuple[str, str]], +): if config.ENABLE_ML_PREDICTION: ds = DataSet(verb_template_pairs) assert ds.min_threshold == 8 @@ -65,7 +69,9 @@ def test_DataSet_split_test_train(verb_template_pairs): ) -def test_mlconjug_template_predictor(verb_template_pairs): +def test_mlconjug_template_predictor( + verb_template_pairs: list[tuple[str, str]], +): if config.ENABLE_ML_PREDICTION: predictor = TemplatePredictor(verb_template_pairs, lang=Lang.fr) template, prediction_score = predictor.predict("parler") diff --git a/tests/test_parser/test_conjugation_template.py b/tests/test_parser/test_conjugation_template.py index 0d84cda..23cd190 100755 --- a/tests/test_parser/test_conjugation_template.py +++ b/tests/test_parser/test_conjugation_template.py @@ -1,21 +1,21 @@ -from unittest.mock import patch +from unittest.mock import MagicMock, patch import pytest -from verbecc.src.parsers.conjugation_template_parser import ConjugationTemplateParser -from verbecc.src.defs.types.exceptions import ConjugationTemplateError -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.parsers.conjugation_template_parser import ConjugationTemplateParser +from verbecc.core.defs.types.exceptions import ConjugationTemplateError +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang @patch("lxml.etree._Element") -def test_template_invalid_tag_template(mock_template_elem): +def test_template_invalid_tag_template(mock_template_elem: MagicMock): mock_template_elem.tag.return_value = "not-template" with pytest.raises(ConjugationTemplateError): - template = ConjugationTemplateParser(Lang.fr).parse(mock_template_elem) + ConjugationTemplateParser(Lang.fr).parse(mock_template_elem) @patch("lxml.etree._Element") -def test_template_invalid_tag_name(mock_template_elem): +def test_template_invalid_tag_name(mock_template_elem: MagicMock): mock_template_elem.get.return_value = "not-name" with pytest.raises(ConjugationTemplateError): - template = ConjugationTemplateParser(Lang.ca).parse(mock_template_elem) + ConjugationTemplateParser(Lang.ca).parse(mock_template_elem) diff --git a/tests/test_parser/test_tense_and_person.py b/tests/test_parser/test_tense_and_person.py index b1d75a3..a852236 100755 --- a/tests/test_parser/test_tense_and_person.py +++ b/tests/test_parser/test_tense_and_person.py @@ -1,11 +1,12 @@ from lxml import etree -from verbecc.src.parsers.tense_template_parser import TenseTemplateParser -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang -from verbecc.src.defs.types.mood import Moods -from verbecc.src.defs.types.tense import Tenses -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.number import Number +from verbecc.core.defs.types.data.xml_types import XmlElement +from verbecc.core.parsers.tense_template_parser import TenseTemplateParser +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.defs.types.mood import Moods +from verbecc.core.defs.types.tense import Tenses +from verbecc.core.defs.types.person import Person +from verbecc.core.defs.types.number import Number def test_tense_and_person(): @@ -19,7 +20,7 @@ def test_tense_and_person():

yez

ientyent

""" - tense_elem: etree._Element = etree.fromstring(tense_elem_str) + tense_elem: XmlElement = etree.fromstring(tense_elem_str) tense_template = TenseTemplateParser(Lang.fr, mood).parse(tense_elem) assert tense_template.mood == mood assert tense_template.tense == tense diff --git a/tests/test_parser/test_verb_parser.py b/tests/test_parser/test_verb_parser.py index 680a09c..36f17ad 100644 --- a/tests/test_parser/test_verb_parser.py +++ b/tests/test_parser/test_verb_parser.py @@ -1,14 +1,13 @@ -from unittest.mock import patch +from unittest.mock import MagicMock, patch import pytest -from verbecc.src.parsers.verb_parser import VerbParser -from verbecc.src.defs.types.exceptions import VerbsParserError -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.parsers.verb_parser import VerbParser +from verbecc.core.defs.types.exceptions import VerbsParserError @patch("lxml.etree._Element") -def test_verb_invalid_xml(mock_v_elem): +def test_verb_invalid_xml(mock_v_elem: MagicMock): v = VerbParser() mock_v_elem.tag.return_value = "not-v" with pytest.raises(VerbsParserError): diff --git a/tests/test_parser/test_verbs_parser.py b/tests/test_parser/test_verbs_parser.py index df44eab..c8aee90 100755 --- a/tests/test_parser/test_verbs_parser.py +++ b/tests/test_parser/test_verbs_parser.py @@ -1,5 +1,5 @@ -from verbecc.src.parsers.verbs_parser import VerbsParser -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.parsers.verbs_parser import VerbsParser +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang def test_verbs_parser(): diff --git a/tests/test_public_api.py b/tests/test_public_api.py new file mode 100644 index 0000000..9c6917b --- /dev/null +++ b/tests/test_public_api.py @@ -0,0 +1,16 @@ +from verbecc import CompleteConjugator +from verbecc.core import CompleteConjugator as CoreCompleteConjugator +from verbecc.core.conjugator import CompleteConjugator as PackageCompleteConjugator +from verbecc.core.conjugator.complete_conjugator import CompleteConjugator as ImplementationCompleteConjugator +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang + + +def test_complete_conjugator_re_exports() -> None: + assert CompleteConjugator is CoreCompleteConjugator + assert CompleteConjugator is PackageCompleteConjugator + assert CompleteConjugator is ImplementationCompleteConjugator + + +def test_public_complete_conjugator_conjugates() -> None: + conjugator = CompleteConjugator(lang=Lang.fr) + assert conjugator.conjugate("parler") diff --git a/tests/test_string_utils.py b/tests/test_string_utils.py index 157c7df..eb4ec9d 100755 --- a/tests/test_string_utils.py +++ b/tests/test_string_utils.py @@ -1,9 +1,8 @@ -from verbecc.src.utils.string_utils import ( +from verbecc.core.utils.string_utils import ( get_common_letters, get_common_letter_count, starts_with_vowel, strip_accents, - unicodefix, ) @@ -30,7 +29,3 @@ def test_get_common_letters_count(): assert get_common_letter_count("brett", "tolbert") == 5 assert get_common_letter_count("çar", "cer") == 1 assert get_common_letter_count(strip_accents("çar"), "cer") == 2 - - -def test_unicodefix(): - assert unicodefix("éparpiller") == "éparpiller" diff --git a/tests/test_to_json.py b/tests/test_to_json.py index 4d18819..adf5663 100644 --- a/tests/test_to_json.py +++ b/tests/test_to_json.py @@ -1,9 +1,9 @@ import pytest -from verbecc.src.conjugator.tense_conjugator import TenseConjugator -from verbecc.src.defs.types.tense import Tenses -from verbecc.src.defs.types.mood import Moods -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.conjugator.tense_conjugator import TenseConjugator +from verbecc.core.defs.types.tense import Tenses +from verbecc.core.defs.types.mood import Moods +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang @pytest.fixture(scope="module") @@ -12,7 +12,7 @@ def tcg(): yield tcg -def test_ensure_ascii(tcg): +def test_ensure_ascii(tcg: TenseConjugator) -> None: """ Test to ensure json.dumps is being called with ensure_ascii=True diff --git a/tests/test_types/test_verbs.py b/tests/test_types/test_verbs.py index a35118f..41de5f2 100644 --- a/tests/test_types/test_verbs.py +++ b/tests/test_types/test_verbs.py @@ -1,27 +1,27 @@ -from unittest.mock import patch +from typing import Generator import pytest -from verbecc.src.parsers.verbs_parser import VerbsParser -from verbecc.src.defs.types.data.verbs import Verbs -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang +from verbecc.core.defs.types.data.verbs import Verbs +from verbecc.core.parsers.verbs_parser import VerbsParser +from verbecc.core.defs.types.lang_code import LangCodeISO639_1 as Lang @pytest.fixture(scope="module") -def verbs(): +def verbs() -> Generator[Verbs, None, None]: vp = VerbsParser(Lang.fr) verbs = vp.parse() yield verbs -def test_verb(verbs): +def test_verb(verbs: Verbs) -> None: verb = verbs.find_verb_by_infinitive("manger") assert verb.infinitive == "manger" assert verb.template == "man:ger" assert verb.translation_en == "eat" -def test_verb_two(verbs): +def test_verb_two(verbs: Verbs) -> None: verb = verbs.find_verb_by_infinitive("abattre") assert verb.infinitive == "abattre" assert verb.template == "bat:tre" @@ -35,6 +35,6 @@ def test_verb_two(verbs): ("Mang", ["mangeotter", "manger"]), ], ) -def test_get_verbs_that_start_with(query, expected_matches, verbs): +def test_get_verbs_that_start_with(query: str, expected_matches: list[str], verbs: Verbs) -> None: matches = verbs.get_verbs_that_start_with(query) assert matches == expected_matches diff --git a/tox.ini b/tox.ini deleted file mode 100755 index 8714106..0000000 --- a/tox.ini +++ /dev/null @@ -1,11 +0,0 @@ -[tox] -envlist = py312 - -[testenv] -deps = - -r{toxinidir}/requirements.txt - pytest-cov==4.1.0 - setuptools==69.0.3 -commands = - pip install -e . - python -m pytest --cov-report xml:coverage.xml --cov=verbecc . diff --git a/typings/jsbeautifier/__init__.pyi b/typings/jsbeautifier/__init__.pyi new file mode 100644 index 0000000..67f7eb5 --- /dev/null +++ b/typings/jsbeautifier/__init__.pyi @@ -0,0 +1,20 @@ +from typing import Any, TextIO + + +class BeautifierOptions: + def __init__(self, options: Any = ...) -> None: ... + + +def default_options() -> BeautifierOptions: ... + + +def beautify(string: str, opts: BeautifierOptions = ...) -> str: ... + + +def beautify_file(file_name: str, opts: BeautifierOptions = ...) -> str: ... + + +def usage(stream: TextIO = ...) -> int: ... + + +def main() -> None: ... \ No newline at end of file diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..0858358 --- /dev/null +++ b/uv.lock @@ -0,0 +1,1153 @@ +version = 1 +revision = 5 +requires-python = ">=3.10" +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version < '3.11'", +] + +[[package]] +name = "backports-strenum" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/c7/2ed54c32fed313591ffb21edbd48db71e68827d43a61938e5a0bc2b6ec91/backports_strenum-1.3.1.tar.gz", hash = "sha256:77c52407342898497714f0596e86188bb7084f89063226f4ba66863482f42414", size = 7257, upload-time = "2023-12-09T14:36:40.937Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/50/56cf20e2ee5127b603b81d5a69580a1a325083e2b921aa8f067da83927c0/backports_strenum-1.3.1-py3-none-any.whl", hash = "sha256:cdcfe36dc897e2615dc793b7d3097f54d359918fc448754a517e6f23044ccf83", size = 8304, upload-time = "2023-12-09T14:36:39.905Z" }, +] + +[[package]] +name = "cloudpickle" +version = "3.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "cython" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/99/ca/18e324972e5cac914816fdd7bb03e19a455b90039c277a538f7781b50c85/cython-3.3.1.tar.gz", hash = "sha256:e0e45989e821bef6aa9ae15c7d583ab5e055f7406c4b3624e49e06d24e90375a", size = 3743354, upload-time = "2026-11-01T20:12:04.773Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/c6/52279cc4cea73ba4ee0a8076514bac97abd8b9ed0276f34e1d19cd3f4c2a/cython-3.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7c1f05344b32ecbb83e3bbd3586282ac1f4aedeb8025cc6e13f22a6d4e2b4222", size = 3142271, upload-time = "2026-11-01T20:12:21.147Z" }, + { url = "https://files.pythonhosted.org/packages/db/8c/79770fcd3d80f40d0ef2b38cbeb66686f19c8a9b23061c05cd3f4b43b1ff/cython-3.3.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08ef3dcd3070959983a15e82bbfd56edb13cbe75b14043665c76ee4425140eaf", size = 3350594, upload-time = "2026-11-01T20:12:23.088Z" }, + { url = "https://files.pythonhosted.org/packages/82/33/e8d7d8d8e6165416689763d8675872d01b6c1c08fd2312db7b8e68ed0edb/cython-3.3.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14bd1521a14d98bed4ae791ace5e830055ffc45a8cbb09f22adf90ee7dca3409", size = 3482473, upload-time = "2026-11-01T20:12:25.212Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1d/b86524bee9f664b61608757456fe47e0d4cc5f4bf02802ca03fcbcf7d72f/cython-3.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:747dade0373011034645ace4e7eb3a54102c52524fc0675dea76e655e9c1dcff", size = 2861943, upload-time = "2026-11-01T20:12:26.931Z" }, + { url = "https://files.pythonhosted.org/packages/14/e1/bad59c9ce7931f5c2b7cd9d5228d16926373267bb841aa8a4b29a1ab3c3a/cython-3.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0ce9efd76bfbf7b67be0da80d450231e1c1bdd5e755d7a8dc10ffc2669fc5edf", size = 3135399, upload-time = "2026-11-01T20:12:28.657Z" }, + { url = "https://files.pythonhosted.org/packages/21/33/cb2588a238d862ebc5e973acc3504ec28764bcc4273828f8e0915354415f/cython-3.3.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:43b16388665cb829c265060705900db8e26e9c3c04cc91f28af38c905d12293f", size = 3351071, upload-time = "2026-11-01T20:12:31.078Z" }, + { url = "https://files.pythonhosted.org/packages/5f/78/1e8562e6dc57f6b9398bc754dcaf574f71762a5a637bb6525ae06cdc7f91/cython-3.3.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:26a28e0ca5dffe9c7d69d1e3a0f78cb74f41b2b6b40771f620262a5d04862a00", size = 3483723, upload-time = "2026-11-01T20:12:33.044Z" }, + { url = "https://files.pythonhosted.org/packages/de/44/55cfaaba499dcd06fb998bf64d3839f7208e80b2acb1dabcdec515d26bf9/cython-3.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:298d3e46d1d4e2f4405d2c934b51899482101a0b0dc43b1b13b2e38f87addf52", size = 2866366, upload-time = "2026-11-01T20:12:35.09Z" }, + { url = "https://files.pythonhosted.org/packages/40/3f/362d57b505de56a3a24f84a94b2e09772ebbc9d05402e9c3bbe7b1b780a5/cython-3.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:744bb804252cf204e6bc9b0fac06b3bad7bb14258cefb9f321c59c4c59e9bc17", size = 3145239, upload-time = "2026-11-01T20:12:37.11Z" }, + { url = "https://files.pythonhosted.org/packages/4f/9d/016dfaefc1a10e182fd770e6c331ebf79df88a527883b4ecbdd74abb9969/cython-3.3.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f08b3eaa639005bea6ab119ebbd6e30656e448981e7a124d80b409760d06c15", size = 3262357, upload-time = "2026-11-01T20:12:38.861Z" }, + { url = "https://files.pythonhosted.org/packages/4a/c6/51ffdd996a1e6836792061ebe7ecf40480f1bd5693a721cea0ad4a689a81/cython-3.3.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:215669fd0ce792080cd4d0748db7e1e31f424b6706a6220d08339895e6a3a03a", size = 3418124, upload-time = "2026-11-01T20:12:41.076Z" }, + { url = "https://files.pythonhosted.org/packages/4c/6b/de4b5b3f43bccd82ef48071a33c48315098867eafe6ae3a39f1064f8d331/cython-3.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:fd81cfc6e94c43c5c0fe50439c11b90dc89cc6b323f92b37edc6a951d0a619d7", size = 2875470, upload-time = "2026-11-01T20:12:43.169Z" }, + { url = "https://files.pythonhosted.org/packages/4c/c1/15881737c54e90c83ff314998b363a2069ec184bf642f2cce9222dc631c8/cython-3.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6156be528b46e820ba834449716690d5065049ff355f261a8600f7da6b436d96", size = 3136530, upload-time = "2026-11-01T20:12:44.807Z" }, + { url = "https://files.pythonhosted.org/packages/64/db/73a6f5f560b7f77fa5f2b5dca15f761f0598776f47951356449e162a4514/cython-3.3.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d0f2610ce850d264e29a87b3d9b8ec4f0f2dec5bc1dce0398294251f09b149df", size = 3257359, upload-time = "2026-11-01T20:12:46.783Z" }, + { url = "https://files.pythonhosted.org/packages/a3/04/5445c845d08948b4006c72b5bc202b63f108ff0b87161a993dc5922106a0/cython-3.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85be5442e8f465c7c5e80837ac2a85a2ce04034e616445113092e85b65d0ab94", size = 3413336, upload-time = "2026-11-01T20:12:48.701Z" }, + { url = "https://files.pythonhosted.org/packages/e2/2d/8afe74009f266c43033dc54d851f31edadc414842cc061bd2762ffc9737f/cython-3.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:0f7d52979bf7730351cf9ca2e9257d4b80f729053b521159bab8666e6be7e3bd", size = 2869329, upload-time = "2026-11-01T20:12:50.577Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f4/2892e59c7e75e75766021c40c165a30edbbfdae4efb6e10cb3fd23486b66/cython-3.3.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3370326802b82cc7cb7df65e605e1acfebdce193077e27ed5734b9bc3f7c6ad0", size = 3188251, upload-time = "2026-11-01T20:12:52.507Z" }, + { url = "https://files.pythonhosted.org/packages/8d/94/dad3d60dcbf22616ee6564456ffc3b6594d5cac9b91735f34ab90633326c/cython-3.3.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0eac1184dd57591a8f3d25691c7a6bf7db030f515b210691bd251772a7e70a4", size = 3335776, upload-time = "2026-11-01T20:12:54.507Z" }, + { url = "https://files.pythonhosted.org/packages/07/d4/6cb0c23f8f3e7081c3af03ccf685a73cfa86af85725b9f4c44040e5f8858/cython-3.3.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7a6c191617853f2e0d5936c73f5d5cd096548f4d9e0fa37612ef721591c42cb3", size = 3459005, upload-time = "2026-11-01T20:12:56.224Z" }, + { url = "https://files.pythonhosted.org/packages/82/69/8efe9179f9f139d4fbe367e5e76e40673c87450f3f4ed1a5c1350be365bf/cython-3.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:7bcc6de1603d8180667b40f4014be142d7d62cfc10af0184ad8d6a83847a2ced", size = 2928989, upload-time = "2026-11-01T20:12:58.247Z" }, + { url = "https://files.pythonhosted.org/packages/cb/37/231f00ec8f6a56519aea9cef921ae9c5d4c39edd8b5557d0de9048211004/cython-3.3.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:a1bfd7511fe8d83f5e681876df22878efac5cd5b2c174f272e6bf018c8fd4a91", size = 3179367, upload-time = "2026-11-01T20:12:59.906Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b1/a0ed5165b268fe2d52929e08e9e89278b6f2eeee1c49c9976ba64ef194b7/cython-3.3.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bce8f37f50add0432ac802cfc34e5bd16395f2918ad1eaeb50b26d02c9001014", size = 3331494, upload-time = "2026-11-01T20:13:01.774Z" }, + { url = "https://files.pythonhosted.org/packages/63/e4/4ef74af67d86067258187a24764df2d9d9bf9adb0b78712862dea051eaff/cython-3.3.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcb6c86dddaedd1da5d77ba2469c1a4bea52442e02d8f0653ff78667fbaaa8d9", size = 3456176, upload-time = "2026-11-01T20:13:03.833Z" }, + { url = "https://files.pythonhosted.org/packages/a2/0e/0458355eb10a33315ede98d91a40b5848f3302cf4e9b44c70211a592ae3a/cython-3.3.1-cp315-cp315-win_amd64.whl", hash = "sha256:45dd533a87c9629d02d77c9a5b48cf399907a99e4bb556aebafd2ac4b68580d2", size = 2927687, upload-time = "2026-11-01T20:13:05.712Z" }, + { url = "https://files.pythonhosted.org/packages/d7/4e/548df758dbbea481ab054e062933252753b8db86757894bbb1fa84bd9292/cython-3.3.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c4e4132b03a6c6c11541f7498441224f55a9e101c615d49494983d2b10dd7669", size = 3066885, upload-time = "2026-11-01T20:13:07.719Z" }, + { url = "https://files.pythonhosted.org/packages/87/55/61840ec209f861887ca547c75c401a493be6c034c1d9ad8f9b628b607054/cython-3.3.1-cp39-abi3-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:226d791808ce95f20ab1b3589082be604b5b5b31e670bf773ef9c312d2f0a8c2", size = 3361449, upload-time = "2026-11-01T20:13:09.398Z" }, + { url = "https://files.pythonhosted.org/packages/96/b1/16e743c3fbfa6424d9c94430934b5bde3a696e7b72c1476e284fdcd1210f/cython-3.3.1-cp39-abi3-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4d1f9d6fad7a8d9750b641a941cd841ec43740a375cb2fdb934329a17fa88b36", size = 3044439, upload-time = "2026-11-01T20:13:11.239Z" }, + { url = "https://files.pythonhosted.org/packages/59/4d/5e19712c074564e696731df56242c7626001b5695d09798115d788cbd771/cython-3.3.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:73f6f40f9308e77cbdb3259155d793ac189ead6687b9a432f1b4503170cb9a13", size = 3157847, upload-time = "2026-11-01T20:13:12.87Z" }, + { url = "https://files.pythonhosted.org/packages/39/16/d6517f078e69f0e5b224b0f1109851408f240af70982d1ef61c53f3a2df9/cython-3.3.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:801730e5714a40dbab8a373af36110de8ff161ec056c855a9627cf05fbc8a6d1", size = 3049217, upload-time = "2026-11-01T20:13:14.506Z" }, + { url = "https://files.pythonhosted.org/packages/78/5c/539b55fa787a427b0dd0e2ba805c04acb108fa65c69ff76e901d44e4a2d2/cython-3.3.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:a2bdb50e9fe8974447b68edc5b0201573b06d2c5683f472a5fc08e1792051277", size = 3376191, upload-time = "2026-11-01T20:13:16.383Z" }, + { url = "https://files.pythonhosted.org/packages/8d/dc/ca2ea10c22499e00d366a9252177ec9039b8d786c85fd28260d41ec2dda5/cython-3.3.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5febfc8c52aced0cf9360e6f2304428a5fa0e66fd9aa7338d5c41f0ce26dd104", size = 3267528, upload-time = "2026-11-01T20:13:18.074Z" }, + { url = "https://files.pythonhosted.org/packages/66/89/b2fdb92a7ccedff602f362bdf3fe38d67016de8c6655bc60bffce98b923a/cython-3.3.1-cp39-abi3-win32.whl", hash = "sha256:b0265a0e186af3c38339c561507e98e2e3a0d377cdb2b0427f980138ceddd4af", size = 2590014, upload-time = "2026-11-01T20:13:19.925Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d8/3d620697d89110b58937b1297dfdafbbfec4415b11b845b46ec9a04b52b5/cython-3.3.1-cp39-abi3-win_arm64.whl", hash = "sha256:8b4391d39260efedeff2e3914e958b9ed75cb2b40f10b62bb0e9ca002658563a", size = 2693762, upload-time = "2026-11-01T20:13:21.506Z" }, + { url = "https://files.pythonhosted.org/packages/9d/55/088578d75bc8dbcbc535af04b1d596ff227ca756dd74f9d3c1c526f955d0/cython-3.3.1-py3-none-any.whl", hash = "sha256:61abc7feedd20dac6c4ff7ab3a30e17136a067b242ab9253e2b4a6d9a72c2b61", size = 1352000, upload-time = "2026-11-01T20:12:01.888Z" }, +] + +[[package]] +name = "dataclass-wizard" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/9d/465deff762411b97cc37fdd475402484724f695af042fa89ff221f725172/dataclass_wizard-1.0.0.tar.gz", hash = "sha256:d458e6a97585377337f1633e5738ee1c051e84ca7726276ecde78d6534e485b5", size = 449143, upload-time = "2026-07-03T03:21:14.302Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/8b/d4db2e85e6f0a5dd2e590eb646aea1cabbf3c4629aec2bc5a5540c7a4250/dataclass_wizard-1.0.0-py3-none-any.whl", hash = "sha256:0dc53acbfed4ca5fe6bbdc517003959afbd50c0794dec1f8405d3c4d8ae6690c", size = 305849, upload-time = "2026-07-03T03:21:12.252Z" }, +] + +[package.optional-dependencies] +yaml = [ + { name = "pyyaml" }, +] + +[[package]] +name = "editorconfig" +version = "0.17.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/88/3a/a61d9a1f319a186b05d14df17daea42fcddea63c213bcd61a929fb3a6796/editorconfig-0.17.1.tar.gz", hash = "sha256:23c08b00e8e08cc3adcddb825251c497478df1dada6aefeb01e626ad37303745", size = 14695, upload-time = "2025-06-09T08:21:37.097Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/fd/a40c621ff207f3ce8e484aa0fc8ba4eb6e3ecf52e15b42ba764b457a9550/editorconfig-0.17.1-py3-none-any.whl", hash = "sha256:1eda9c2c0db8c16dbd50111b710572a5e6de934e39772de1959d41f64fc17c82", size = 16360, upload-time = "2025-06-09T08:21:35.654Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, +] + +[[package]] +name = "importlib-resources" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/06/b56dfa750b44e86157093bc8fca0ab81dccbf5260510de4eaf1cb69b5b99/importlib_resources-7.1.0.tar.gz", hash = "sha256:0722d4c6212489c530f2a145a34c0a7a3b4721bc96a15fada5930e2a0b760708", size = 44985, upload-time = "2026-04-12T16:36:09.232Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/db/55a262f3606bebcae07cc14095338471ad7c0bbcaa37707e6f0ee49725b7/importlib_resources-7.1.0-py3-none-any.whl", hash = "sha256:1bd7b48b4088eddb2cd16382150bb515af0bd2c70128194392725f82ad2c96a1", size = 37232, upload-time = "2026-04-12T16:36:08.219Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/e1/2069291243c926a2ff1cd706c7f3eeb9b62144bf60f77c9fb9ff2fb26bd3/iniconfig-2.3.1.tar.gz", hash = "sha256:67f4b9c50da0dedf52af349e7749a80a9057a5031199791b906c3bb3ae878960", size = 21209, upload-time = "2026-10-06T22:48:38.076Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/43/4ca9e49d27a1fcf6bece6f6aec0ea46bb9112489b93d4b688fb415457bdb/iniconfig-2.3.1-py3-none-any.whl", hash = "sha256:9121e2c1fdb355232495be3194c8dfe87ccc2d5dee45947b78e68f499790d7a7", size = 7552, upload-time = "2026-10-06T22:48:36.959Z" }, +] + +[[package]] +name = "joblib" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cloudpickle" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d5/1d/537ab090f302b838943a1b56497dd53059b9a9b46a074936470173a2e207/joblib-1.6.0.tar.gz", hash = "sha256:2ccc96785b12046c08fd6d55839c12857831b54a3c1673ffadd2f04bfc4eda03", size = 327903, upload-time = "2026-08-31T09:39:04.122Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/53/84099323c2ec4be98d935f63c033ac4151ee83836ca1050ede3b3aadf155/joblib-1.6.0-py3-none-any.whl", hash = "sha256:3dbbf9f6e4b592a2357b854608e980fe6390d131d7a82f011a377ef2ebef7aba", size = 306115, upload-time = "2026-08-31T09:39:02.298Z" }, +] + +[[package]] +name = "jsbeautifier" +version = "1.15.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "editorconfig" }, + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ea/98/d6cadf4d5a1c03b2136837a435682418c29fdeb66be137128544cecc5b7a/jsbeautifier-1.15.4.tar.gz", hash = "sha256:5bb18d9efb9331d825735fbc5360ee8f1aac5e52780042803943aa7f854f7592", size = 75257, upload-time = "2025-02-27T17:53:53.252Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/14/1c65fccf8413d5f5c6e8425f84675169654395098000d8bddc4e9d3390e1/jsbeautifier-1.15.4-py3-none-any.whl", hash = "sha256:72f65de312a3f10900d7685557f84cb61a9733c50dcc27271a39f5b0051bf528", size = 94707, upload-time = "2025-02-27T17:53:46.152Z" }, +] + +[[package]] +name = "lxml" +version = "6.1.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/23/ad/28ecd7cb894d172f3c9c80a075eeeb2017ac62e3632cee05a5f9493547eb/lxml-6.1.3.tar.gz", hash = "sha256:45222d94ddd511536f3b2f7d9deae3b2339b4ce0f075f1ca25703b07cad9dd21", size = 4211198, upload-time = "2026-09-02T14:48:02.287Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/6b/a7d5c08e19a8e69887ed722fffaefdbaffc8959d5ef5c370a65e52c895ac/lxml-6.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:40bcbd9f94166ffe925811e730607385cec959f42fb1bb7dad83748680465221", size = 8575497, upload-time = "2026-09-02T14:46:05.131Z" }, + { url = "https://files.pythonhosted.org/packages/96/dd/c25a32f9f6039a96cfd52296a4630075868aa16e71858b3076699a059201/lxml-6.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:05f5bce9af14fd1506997594bd81cee6d9c6b58ea80a39c058327aa6371ed9e9", size = 4619233, upload-time = "2026-09-02T14:46:08.898Z" }, + { url = "https://files.pythonhosted.org/packages/3e/f0/d49375a47644369d84f90a9fe4ff1924faad58d4f95563831eca84ca29ae/lxml-6.1.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ff88a92cafde90888511242d1c54afcc1a8adbb6dc0a88fa7f87e29e92400d4a", size = 5015387, upload-time = "2026-09-02T14:46:10.797Z" }, + { url = "https://files.pythonhosted.org/packages/76/0f/d1b1f52925442f7b4b1abd81a41905987322f6df6a5dd42fab8579415828/lxml-6.1.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c00e26288784460885fe76e4d4b293573e0f791f52e6d60e27b42edf005922eb", size = 5168571, upload-time = "2026-09-02T14:46:12.989Z" }, + { url = "https://files.pythonhosted.org/packages/b2/13/e5d8291a68a27e564e4e1eefba08c3844c6800bcb43f3e72a32b20971132/lxml-6.1.3-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:773062aec2f2e56b2b22d37054123f0de8a22a4688a0c3376c3fe42685f975cf", size = 5068024, upload-time = "2026-09-02T14:46:15.325Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ce/dbea34cd115ae9b8ef53816daa912563615adf4daed42531878a2fb29c77/lxml-6.1.3-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f6449672f9c93316deb5e2839e18931f468670e44d5bd9b1301a5a9655d45c07", size = 5296830, upload-time = "2026-09-02T14:46:17.52Z" }, + { url = "https://files.pythonhosted.org/packages/20/f6/12a2ab6e8c8afecb82a3f0e9a518952b6a1cddf405ad8542883bd71e6096/lxml-6.1.3-cp310-cp310-manylinux_2_28_i686.whl", hash = "sha256:ec295280f4b37769256da025acf5890370355ac589c27e89caae0b5e9eedc702", size = 5424696, upload-time = "2026-09-02T14:46:19.706Z" }, + { url = "https://files.pythonhosted.org/packages/02/3f/5670e198266c764595687a234fdaed33837f487b95a596262b2548e48933/lxml-6.1.3-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:5929d9df5e7e3379183be0e21f7d559618a5b61cb63280df6164019242e337ed", size = 4783635, upload-time = "2026-09-02T14:46:21.63Z" }, + { url = "https://files.pythonhosted.org/packages/70/24/007ce6b7bffb61a6ca88c3a8f21b26f3f0aa3b3f6bb648a56e328c994a14/lxml-6.1.3-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6e1eb8a4cbffd5553680ad96be6680e364710656eced73d1dc90ec489df599a3", size = 5373212, upload-time = "2026-09-02T14:46:23.572Z" }, + { url = "https://files.pythonhosted.org/packages/4e/00/cf09f38cf9005bd5cfa4fd452b03b290b1c48c403fe0319a8013f4b3cae0/lxml-6.1.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:16148acd77ed1d8836a56db883af2f5eed720f9723088110b16a0d08582130a6", size = 5116476, upload-time = "2026-09-02T14:46:26.262Z" }, + { url = "https://files.pythonhosted.org/packages/15/83/eb021e5db4336f0bb1438cba6f053ea135aa00b9f4ef0439473d6b986308/lxml-6.1.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:23c366231259cd75ad06495174701afb3fcb36a92917fa47de2d1f1bd9d95739", size = 4814172, upload-time = "2026-09-02T14:46:28.3Z" }, + { url = "https://files.pythonhosted.org/packages/c8/4e/147b6f9088cc191713249ac547b0af2fece489c8cdff1f2801ab47dda8a9/lxml-6.1.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:da85db328e507da922d586c3c7416ec360ec22e9cd9e0700691afacde0c81f53", size = 5361711, upload-time = "2026-09-02T14:46:31.035Z" }, + { url = "https://files.pythonhosted.org/packages/b7/d9/8cfdac0d7d771e25af2c1f4bc874032f025a4b59e0b6917c3c7858070795/lxml-6.1.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0f17d83c48ee9dfd96abae3ac3e2108c76d2fc86ce96355e37b8da9f7f4ecc08", size = 5321598, upload-time = "2026-09-02T14:46:33.165Z" }, + { url = "https://files.pythonhosted.org/packages/f3/5b/d2413c71f312dccdd07ed985be356657fc624d822ba7e2c87e8722646156/lxml-6.1.3-cp310-cp310-win32.whl", hash = "sha256:7dd624c1eaa629ad44b59a1a0145fdf2d67895592dce94c9358b938b3d075e65", size = 3604471, upload-time = "2026-09-02T14:46:35.245Z" }, + { url = "https://files.pythonhosted.org/packages/7a/bf/74b6785beac6488fd395e78796339bc197fbad6fd6103b41b15a4009dc4b/lxml-6.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:18a4db52b5a7b53a3540b0b0f4123319334621ee8083d496de314d0bf06ff59a", size = 4029086, upload-time = "2026-09-02T14:46:37.744Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a5/ddf6e1744cd76fc9f0ce11cb16b117d6eaac46ebaeca01968e9014e8770c/lxml-6.1.3-cp310-cp310-win_arm64.whl", hash = "sha256:0feebef8d0521188d0157f758356072e840173aa61ca45b8b3f87959ac283dd5", size = 3674608, upload-time = "2026-09-02T14:46:39.802Z" }, + { url = "https://files.pythonhosted.org/packages/96/f1/95133bde7af7afb1f5ba6090b674d826b7a518318bba54bbbb633b27865a/lxml-6.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c66f858b82497173f73366795fc6ee8171620e75a338506d6b2e7bc16f5fca11", size = 8563141, upload-time = "2026-09-02T14:46:42.334Z" }, + { url = "https://files.pythonhosted.org/packages/80/54/5a79ee2181ac773ee13e48205411845feec69e1c3d097e985c1343171712/lxml-6.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:032a0a97eed428bd143c75a11118238546424ceb2fa311cca5f073aa44658dc4", size = 4613690, upload-time = "2026-09-02T14:46:45.253Z" }, + { url = "https://files.pythonhosted.org/packages/ab/29/8c24672f56807f119312f073f24204368574bd16b384ede861b5104b3a2b/lxml-6.1.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4a579dfb9c835f8ab47f4b8ed33440cbc75b806b73297208e6ec2a33e903740b", size = 4935630, upload-time = "2026-09-02T14:46:48.071Z" }, + { url = "https://files.pythonhosted.org/packages/71/69/ce2436d854c848c19fc9287143991f3fc76b8b4e9a0dbba8452e51dff264/lxml-6.1.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:49fbc2682a9306135b7ec49e93f97f9c26689b9b7f96ed2742d8d6497e994d13", size = 5079033, upload-time = "2026-09-02T14:46:50.483Z" }, + { url = "https://files.pythonhosted.org/packages/91/ec/b66f66f6499ad800265d57540b51e6632e3232d3526f42f2f8fd4b14e0ea/lxml-6.1.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea2c01cdb16dc12156e455007c406dfaaece0c89aa4ba0e3b47586779f951d41", size = 5012298, upload-time = "2026-09-02T14:46:52.603Z" }, + { url = "https://files.pythonhosted.org/packages/94/2a/25d128872f4d51753542bfc3feb482c2ea7c8a2d6d81a0bc5c6a00779ed4/lxml-6.1.3-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:527195c188d7d0af748cd48d220ab8cdc5cb99be3d49ac4d9be7324d8abf9bc0", size = 5211431, upload-time = "2026-09-02T14:46:54.722Z" }, + { url = "https://files.pythonhosted.org/packages/75/b2/0a41bbef074a556110f84fafb6d8c2998293c7d3bfbe1ce74515bc65393b/lxml-6.1.3-cp311-cp311-manylinux_2_28_i686.whl", hash = "sha256:20384c2bbcbf87180c8c61eb60869699c1ec0cd09b62cfd13804022d860b0867", size = 5343417, upload-time = "2026-09-02T14:46:57.46Z" }, + { url = "https://files.pythonhosted.org/packages/7b/cd/16116c3f91791aeeeab1cbe6e7eb6e646f127be7b0158b262eb526a21a0c/lxml-6.1.3-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:424aa5657141d306ba9ad1baab4b2c0a0719040075ee6c66aee9bb2dea2b5054", size = 4673219, upload-time = "2026-09-02T14:46:59.604Z" }, + { url = "https://files.pythonhosted.org/packages/dd/bb/4dff849f443ef70221676aec938bc41e8bae6430aa2ca13b041319e14b98/lxml-6.1.3-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4736e6c87e603146d8949d8501da621ad20c31015060d3fcf95ace2859f3e3e6", size = 5281246, upload-time = "2026-09-02T14:47:02.375Z" }, + { url = "https://files.pythonhosted.org/packages/9f/ac/4aa7dd059420bfd35278c7fe819e9d319ee36a0453b7bbde1907a7832d91/lxml-6.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6374e9e382e5a98c9c5e66d41b357b470da1c54bce30f17f9dc4bcc58436cc1c", size = 5055451, upload-time = "2026-09-02T14:47:05.883Z" }, + { url = "https://files.pythonhosted.org/packages/de/44/20d90cf6f4234de9cd9eeb4f519419885fdb087fa80d073c7b57be342021/lxml-6.1.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:22eec57e26c418cde02c051ce9914a365e52a7f135a565c6f0480242aeebab48", size = 4722694, upload-time = "2026-09-02T14:47:08.461Z" }, + { url = "https://files.pythonhosted.org/packages/f0/0e/6bee12325e53dd6613fe1e107def07583b6182ade03e94bfef8976622e44/lxml-6.1.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8753b8d51dbc86fd335ee31fcf7f3658e9f5c016d4edfb23f76ad295f4b8c9d0", size = 5269179, upload-time = "2026-09-02T14:47:10.647Z" }, + { url = "https://files.pythonhosted.org/packages/e4/5d/54d269ce5cd0787c0424d9cef449ee794d4097725d13dd2acd6181c44e9c/lxml-6.1.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:207dfc3d47cf0e575e643bbc140dacc8863b39abaa1e5307cd64c7f2365b8a12", size = 5235559, upload-time = "2026-09-02T14:47:13.932Z" }, + { url = "https://files.pythonhosted.org/packages/e4/f7/5a3095f187f1bec293591616a1677781acc265c5b313c009f8a19c471a09/lxml-6.1.3-cp311-cp311-win32.whl", hash = "sha256:18293f8a8d8b6a8e71ef37706b659e3846a4261232158167b1ddf35f6994f633", size = 3600377, upload-time = "2026-09-02T14:47:15.957Z" }, + { url = "https://files.pythonhosted.org/packages/45/5a/15531a0d307c96282fe8b639b3d74e8bd783e4ab4cb2b0781146ac4161b8/lxml-6.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:7ae4949f212a53b007dbc355884fda122545c5764a54256c9217e419a62a6559", size = 4032700, upload-time = "2026-09-02T14:47:18.566Z" }, + { url = "https://files.pythonhosted.org/packages/12/f9/8de76314955545ceaaa7c0305017b8aaa217905dee59c62c0e2c1e44a68f/lxml-6.1.3-cp311-cp311-win_arm64.whl", hash = "sha256:2123e5aa075ac20d23c7af489255efd129cbfe190dbe88fd42598cc9df3199b6", size = 3674431, upload-time = "2026-09-02T14:47:22.186Z" }, + { url = "https://files.pythonhosted.org/packages/dd/1f/a180b57d9eeabaab77f9d5aa30356898ea749c4795596a8f66d1eb6bef2e/lxml-6.1.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c0710ac085a157b593c38fbcacd950f15c4afa8e2057527185875ab302752bc", size = 8602094, upload-time = "2026-09-02T14:47:26.054Z" }, + { url = "https://files.pythonhosted.org/packages/a8/25/070c92013a1c029a602b03560d68772313d918268667fa993da7961759c9/lxml-6.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:623c8799c17128753c65699f1c3aa32402657393a9ad6db09ed8b98ddf76611d", size = 4638308, upload-time = "2026-09-02T14:47:29.587Z" }, + { url = "https://files.pythonhosted.org/packages/1e/1c/722e88883173097a1a375153e3c2447eba3060d0231522cf6596e99f4195/lxml-6.1.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f683dc6300317700025e41d89a43e0276692ded16113a3c43eab704d605c58e5", size = 4939696, upload-time = "2026-09-02T14:47:32.997Z" }, + { url = "https://files.pythonhosted.org/packages/db/36/aa413bc214dc4f785ad2b2ddd8cc99aae7062d49ab155e91e6011af00daf/lxml-6.1.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:379f8a75cf6eb7eef0af074b55f49ab73b868388a98de14646abcdfa4564bb11", size = 5105247, upload-time = "2026-09-02T14:47:36.734Z" }, + { url = "https://files.pythonhosted.org/packages/a3/a0/a1f7f1313795bfec67b77f01ef3b1128d49f2d7f66a8413fa55d47f4e25f/lxml-6.1.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b37772102d44bb6628186accca3a121b1fa3a6b3d97518a8c29a5229ca4c0d0a", size = 5011915, upload-time = "2026-09-02T14:47:39.846Z" }, + { url = "https://files.pythonhosted.org/packages/b9/78/840e7e3f1d0cc7a5cfac5d8505b97e25b6427fd774ac4bae672aaebfb4b5/lxml-6.1.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ddcf547bea2aee967d6a77779376a45e77e610e8465147a1f3d7e20d539d6e32", size = 5638175, upload-time = "2026-09-02T14:47:43.644Z" }, + { url = "https://files.pythonhosted.org/packages/0a/20/e022dbc6b4753a9bc9fc5fb28a27163430c1731b9913997f6544c1b2518c/lxml-6.1.3-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:909f4e927bb051f7740d6367285fc60cdcfdaf0258c2dba4ff5ba7eadadc250c", size = 5244675, upload-time = "2026-09-02T14:47:47.635Z" }, + { url = "https://files.pythonhosted.org/packages/99/83/82cde81d2b5eb38d1539fdfdf318abdd014a7e604f4df01c9cd3deb18f2a/lxml-6.1.3-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:a5c18810318303ce9afb3f95e2ddb54834f96fa699a8600433fd5a93dcf44c56", size = 5358205, upload-time = "2026-09-02T14:47:50.306Z" }, + { url = "https://files.pythonhosted.org/packages/d2/a1/f3b057371c8cb29f2a9c9c44ea320592446e40b74a4b0af68c3d8e65bc73/lxml-6.1.3-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:3e42265103fb385d8642a78672edf376c6f7e1d3598a7a4f9cb1278f2f6b5f6f", size = 4704495, upload-time = "2026-09-02T14:47:53.251Z" }, + { url = "https://files.pythonhosted.org/packages/1a/a4/230eb28be5d412152ffc3c679b51fe1aeede5a53f3a8eb6e9748f2f4754f/lxml-6.1.3-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:21402998e4b78e7cce237d2788841aaa21ac9a4d1574d04dc2d12ee41ae807b5", size = 5255117, upload-time = "2026-09-02T14:47:55.963Z" }, + { url = "https://files.pythonhosted.org/packages/a3/18/1969f56763af24ce42ea156007b0b2d73fddea552e283b2010416394f0f4/lxml-6.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:38fc4e4e4e084e0bd491949482527d406788045c546d4f8789e93fc527b91385", size = 5054424, upload-time = "2026-09-02T14:47:58.131Z" }, + { url = "https://files.pythonhosted.org/packages/f4/d4/2a90acc1f6fabaa3a8db9340437822bd8d041b205d626a4b3e8621aaa390/lxml-6.1.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5609efdb0d3c95499c00046bc53648b3482ec2175b5503d6e611b3f0555dc71d", size = 4785572, upload-time = "2026-09-02T14:48:01.029Z" }, + { url = "https://files.pythonhosted.org/packages/a5/1e/b90e845b1dcd0f2f3f26b98283d857f25909223aacd265eee032c34ab8b1/lxml-6.1.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:97ce49699d87ebf8aad631b55d65b33219a4f1bfefbbf5bff19dc9af160aeaf9", size = 5656516, upload-time = "2026-09-02T14:48:03.419Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ab/0a1b802c57f3fba5c4efd77d5c6b78adaa8f7b681f0c90456b140fe8bf6c/lxml-6.1.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:48542c9acba9ff9450bd18d871d2c2c8787fdb283572b623d206f1b927cd7d9e", size = 5245982, upload-time = "2026-09-02T14:48:06.109Z" }, + { url = "https://files.pythonhosted.org/packages/da/ee/2c016fbceb3778137459292538d9dfa7e3ad9070fe409c15254ddd90d2cc/lxml-6.1.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c55e71a9b1db1f107efb60da49c093689b74c5c31a708e5379e2fd9439d4fbb5", size = 5267340, upload-time = "2026-09-02T14:48:08.374Z" }, + { url = "https://files.pythonhosted.org/packages/9c/b1/736d18fd6f0835761923b7bac1f0c27d60c1200384e9093f05d8c5100525/lxml-6.1.3-cp312-cp312-win32.whl", hash = "sha256:b3ff39654f0ce6ebd4db154211136dbe7e8157bcc3bed2344c87f32c7c6ecb6c", size = 3602606, upload-time = "2026-09-02T14:48:10.384Z" }, + { url = "https://files.pythonhosted.org/packages/3a/5b/6ed903e4e6278a020c8a6f0dbbe78030d041840a6b4a64ea441a1e414077/lxml-6.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:3e9a00d1c2c30936f7add097c41afc5da6556c580909104aafd382cac92a855c", size = 4005999, upload-time = "2026-09-02T14:48:12.51Z" }, + { url = "https://files.pythonhosted.org/packages/e4/1b/7bcebb7b6332cb3ae85e9c13b139adb6f23f75c71d84041c56a5005d9a29/lxml-6.1.3-cp312-cp312-win_arm64.whl", hash = "sha256:1aeca87830c4fe649dcf93fe2b059525b71c72587f21be4ae4af7103082a79fa", size = 3666631, upload-time = "2026-09-02T14:48:14.567Z" }, + { url = "https://files.pythonhosted.org/packages/52/05/3ef45db776baea068044c799bbba68f3ca00a440c0e930a17c572f3d9639/lxml-6.1.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3a48093cdb058a93af842ede9703520e810b05dcd0fc6d7190a06376c3bfb6bd", size = 8590357, upload-time = "2026-09-02T14:48:17.413Z" }, + { url = "https://files.pythonhosted.org/packages/8c/a5/eee2fc77eee5ea68e4a4334b1def1781a3beaeefd3d98e81b4a38dc447b7/lxml-6.1.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:887c021d9a977cff89cb273047c1352997b772a8908a25c21836861f69b92be1", size = 4632616, upload-time = "2026-09-02T14:48:20.745Z" }, + { url = "https://files.pythonhosted.org/packages/35/42/df27b56848acd29d8a720acc28977911aab36f2a09df4208d5502e887415/lxml-6.1.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:611a51e61c92f62345a50b0035df6fc0d678f9299f33728826d831598862f59d", size = 4936186, upload-time = "2026-09-02T14:48:22.94Z" }, + { url = "https://files.pythonhosted.org/packages/ab/8d/8a7b91df0b54d09d25f5f44885d6b3e0a6d6643a8c070191580318d20c42/lxml-6.1.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b477912f42c5c33405a10c759d22f80cf5af043ae02d95b9d8e5e5bc555739ed", size = 5093324, upload-time = "2026-09-02T14:48:25.132Z" }, + { url = "https://files.pythonhosted.org/packages/c6/7e/8f340ddcd43790332fb0de8a26628d571a492da3300cd191821698407c96/lxml-6.1.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cffe18571ccc51d742cd08cbb3f8b756de9311d18c7ea98f5d92f37b8fb60c2", size = 4998850, upload-time = "2026-09-02T14:48:27.394Z" }, + { url = "https://files.pythonhosted.org/packages/c5/c1/9c5bb572f1f09ec9e4322bd4a4e9f4ad48347fc56ef94cf4df58a5279dc8/lxml-6.1.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:75cc6569e86be5785b6188ef1642670c6adbc984e81ec35e224842ecd9eefcc8", size = 5626813, upload-time = "2026-09-02T14:48:29.61Z" }, + { url = "https://files.pythonhosted.org/packages/ac/7d/8bf1fd8bae8247743968bb76d027a1ac5bd2c4b44495fba6a71b30d10706/lxml-6.1.3-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d85dfab42dd672f87a7f76e9de7172962aee69fa12044f0d6e1a23cbd53fb80e", size = 5232385, upload-time = "2026-09-02T14:48:31.969Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2e/6cef69ed81cb7df0d03b0dd09d08e6e2cf5061a743ff6f42f0b741548e9b/lxml-6.1.3-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:42632b4024ab24a6b488f559ac851312509888b6b80ae2aa11cf29a646a0d245", size = 5347088, upload-time = "2026-09-02T14:48:34.13Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e1/8e5fd8ddc8c7d685badb0f2db149e3c9da84eefc2827c01c658df2c4e3cb/lxml-6.1.3-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:febd35ef45f603c2d74b74655efdbf45e14f55fc0aef4ac82b663ca829b283e0", size = 4707227, upload-time = "2026-09-02T14:48:36.62Z" }, + { url = "https://files.pythonhosted.org/packages/7a/7e/00041382a11be40a88bf405ebff11c8efabd3de79f2691e1638b1c47a8a0/lxml-6.1.3-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a43b3bdf11e477dc7770609d3477316f974354dfc8425d596f64f471cc8daf6e", size = 5240208, upload-time = "2026-09-02T14:48:38.893Z" }, + { url = "https://files.pythonhosted.org/packages/fd/fe/316538b5cff0936fa63d45d421c655730fcbb5a28dcac728c175083002bc/lxml-6.1.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d582042c69857c364e8153de6e18e0da9b7b515a6a8113caf69a6ec8e0520f2", size = 5050271, upload-time = "2026-09-02T14:48:41.213Z" }, + { url = "https://files.pythonhosted.org/packages/c9/91/455bcccb3ac725373007344d351151810cd19762d1673b64b811f4359a42/lxml-6.1.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8e49a646acfab83c68974f4aa1d0a2acca9e88d7d627ae0fc13201b14b76d310", size = 4780433, upload-time = "2026-09-02T14:48:43.779Z" }, + { url = "https://files.pythonhosted.org/packages/cb/f6/580440e2f52cf00bba5c5e1080bfa88cdfcde73be71a11d95170ddbb663f/lxml-6.1.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0dee106e9aa97fb00541b1ed7827070564d0549c3d3fba8920e6b20fd980f748", size = 5645928, upload-time = "2026-09-02T14:48:46.187Z" }, + { url = "https://files.pythonhosted.org/packages/f6/dc/d123c1f244306543d545f62443f794959e4f1ea709fe100f8740d514e74a/lxml-6.1.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:dd5e90f34cffcfed97f36cf066325773d2b6021c60c29942e53a18b028501b1d", size = 5231184, upload-time = "2026-09-02T14:48:48.691Z" }, + { url = "https://files.pythonhosted.org/packages/c3/3c/fe55b2bd5c6113c906511cd88f6a470195c5fbff1124f19970ab706c3477/lxml-6.1.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d9b3e7d71bf6acff341233417abbdface29c647e3113892d9aaedc02eb4aa2bc", size = 5255814, upload-time = "2026-09-02T14:48:50.948Z" }, + { url = "https://files.pythonhosted.org/packages/e7/a7/485df55acf55dc35e4ca89d2f48f03889e5a3241826b18b85102b32ce9d8/lxml-6.1.3-cp313-cp313-win32.whl", hash = "sha256:160fcf381f76c3aeac28a756bec44f48942a8f7245a87aa28e3a523b4d90cd87", size = 3602214, upload-time = "2026-09-02T14:48:53.236Z" }, + { url = "https://files.pythonhosted.org/packages/c0/28/e46a7702bd95e9043291f7c3539b6184cba66f96cea9936f20939b284eeb/lxml-6.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:e477aca0bc0d19f3b4ae9e4f2a1cfd687c31bf772d78734910658186b40b2477", size = 4004091, upload-time = "2026-09-02T14:48:55.699Z" }, + { url = "https://files.pythonhosted.org/packages/8a/1d/154c78e20479a43916e63f19cb720d83f44f024b03228be44c92d9a97b24/lxml-6.1.3-cp313-cp313-win_arm64.whl", hash = "sha256:b1cc980905221a5d8b3c476330730b3adb40ff80add71ffbdb6215ba055656f1", size = 3665468, upload-time = "2026-09-02T14:48:57.703Z" }, + { url = "https://files.pythonhosted.org/packages/0c/15/fc75a70b0af6021d0ea16811f1fc71cc42cd06ce90fe10f007a69b2eed84/lxml-6.1.3-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:2bec13085dc8ef48a3fe62f7dfcacfeda2c785cdf19cc8eeda2bb9ed081da165", size = 8609725, upload-time = "2026-09-02T14:49:00.156Z" }, + { url = "https://files.pythonhosted.org/packages/84/ef/398fcf9018f881ec9aeaafae1ddd6586dfb13314a35d35e899de373dcae0/lxml-6.1.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:4f4db7c7e954d289d71878938348b3d91b904a3e8210a11939359fb758a58e7d", size = 4639629, upload-time = "2026-09-02T14:49:02.81Z" }, + { url = "https://files.pythonhosted.org/packages/a7/2d/49b6a6ad7ce8f64b07b9fe852ff0c6d3fcbb26db61bee4f63d4120180a1c/lxml-6.1.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2cae5d5c90a62d9139c512a0cb1aad1d182b022b5740daea2617eb5bf7fc658e", size = 4965074, upload-time = "2026-09-02T14:49:05.133Z" }, + { url = "https://files.pythonhosted.org/packages/66/bc/6230cf80e4331c33383b0b6b73dc31a393dd76edd4cb73d761de5123034d/lxml-6.1.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c6c0c13128a32eb04a51357e56a094e13aa8e6d3d1884de2e9ae923f6915e1a8", size = 5099355, upload-time = "2026-09-02T14:49:07.343Z" }, + { url = "https://files.pythonhosted.org/packages/ac/cf/d1143d9b7717e07a82f158a1fc9ce6e581fdad1226734950af869e3ffde4/lxml-6.1.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2221e88679d1351e9a40aaee54bc65679b9795bbd0160bc3d5e36b163344eb75", size = 5036795, upload-time = "2026-09-02T14:49:09.65Z" }, + { url = "https://files.pythonhosted.org/packages/31/6f/194bb00ffb89712c30f5a7e1b8e685590e140fad6c8261fec172c09a3dc0/lxml-6.1.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cfb398886a7eb4c719161c3efcff2a1248febc53a4d8e5072d2d8a87fed84ac9", size = 5658740, upload-time = "2026-09-02T14:49:11.9Z" }, + { url = "https://files.pythonhosted.org/packages/e9/44/27e3cee3dcdb3b7bc09727b642bdbfcd098490ea77df04611db9060d7722/lxml-6.1.3-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7eb78ba28b187e1e9203a55c60fcf70df2d22cb205fe6d51b9383d6097419f0", size = 5245991, upload-time = "2026-09-02T14:49:14.154Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e9/8312560579fc980bbd2233a8a673cc46f7d613d3633f2bf08a21e8f4ad13/lxml-6.1.3-cp314-cp314-manylinux_2_28_i686.whl", hash = "sha256:ea6b1e9105b4b24a34c722432d9fb578f9ed83af21fa1abda639011e0f22bbb6", size = 5354136, upload-time = "2026-09-02T14:49:16.459Z" }, + { url = "https://files.pythonhosted.org/packages/74/d8/eda60f4f73a9c780b5d6e1175484f66e6c81a2c93346e2906a1fec9c7a02/lxml-6.1.3-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:e8b17e23df3e827a69d25af70990ca2420e92668aaffaeeb3cd2351d7916a023", size = 4704379, upload-time = "2026-09-02T14:49:19.032Z" }, + { url = "https://files.pythonhosted.org/packages/ba/c8/c9cc60057be78ac34bd2b842e45e6e88edbfe5e532e82c3b82381b7aab49/lxml-6.1.3-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1b7c37339d7e75cab9a123a04248e243cefefb302ad6db566ea0c77cbcde421e", size = 5258676, upload-time = "2026-09-02T14:49:21.306Z" }, + { url = "https://files.pythonhosted.org/packages/41/7b/66894008fee8d1785b8db129747ae963fd427b68f456918df7f2f24a8b98/lxml-6.1.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:83e3a51e7933db700a0da0db31849db3a24022d9970da9bb73001e1d0326fd92", size = 5090069, upload-time = "2026-09-02T14:49:23.562Z" }, + { url = "https://files.pythonhosted.org/packages/8b/31/c1b60404859f4c3cd1f41f29c65a24e25cea78fde822d9574a21f66810be/lxml-6.1.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:9bde9ae026a55b9a192078dfa6e27dd0ca4a050171ab6272e92f97b757dfdf48", size = 4741958, upload-time = "2026-09-02T14:49:26.037Z" }, + { url = "https://files.pythonhosted.org/packages/23/b8/6285f0cf546f14da2554cabdeaf7c2c2ff3190c74807f0de2e8810a786f9/lxml-6.1.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:1a635e837b50a1819bebfedaac5916498ea024120969da8790500148fb0a894d", size = 5683245, upload-time = "2026-09-02T14:49:28.438Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f6/2168cab44336dcb15fed0f0b78577225b83297cdf0dee349c95420c3dcb0/lxml-6.1.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:d0c5c362bc94f1929dc7e96e715bbe7bd17037f802e6d8f0d1545df9133c0559", size = 5246087, upload-time = "2026-09-02T14:49:30.955Z" }, + { url = "https://files.pythonhosted.org/packages/f5/89/32f5de69a0a31f30e6164981851f87b37ecb2c4ee838e504b88d49d4818e/lxml-6.1.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c59e4265608da6a041f54646ecc0c9ecdbb19aaf14c4c684bb6c2114998cc415", size = 5269352, upload-time = "2026-09-02T14:49:33.502Z" }, + { url = "https://files.pythonhosted.org/packages/a2/a1/741d952ed3a7ef7a50055c6415aec3f067015e97f72f4389ce77b09657ba/lxml-6.1.3-cp314-cp314-win32.whl", hash = "sha256:2e62c569ec7531b679b184cbfe335c501c1d13c4b363560013019962eb630e6d", size = 3662783, upload-time = "2026-09-02T14:50:23.751Z" }, + { url = "https://files.pythonhosted.org/packages/0f/bc/5811cc73cac05e324e05ba9b0924e1a163a317a167ede8a9c748b11db30a/lxml-6.1.3-cp314-cp314-win_amd64.whl", hash = "sha256:66299564c046bc7e0cc5de5106601eae907e9fa5904cd68a323380a8502f7861", size = 4073951, upload-time = "2026-09-02T14:50:26.348Z" }, + { url = "https://files.pythonhosted.org/packages/92/18/3768c8b01ac3a9bed1914715e6011711b00e2a11628ffa6f7fa37f8e0269/lxml-6.1.3-cp314-cp314-win_arm64.whl", hash = "sha256:ebd054ad1737a68fb7c5c073d405cef2b88bb824e294de3b4a4e995b47f0e376", size = 3749279, upload-time = "2026-09-02T14:50:28.749Z" }, + { url = "https://files.pythonhosted.org/packages/72/38/84684784738d9451db2b330de2483f496690c3a5c642071df24135739b37/lxml-6.1.3-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:5a143e6207579de8baeded4eaac9134413200359f1969d636f0bfb98ee8c3c8f", size = 8860296, upload-time = "2026-09-02T14:49:36.346Z" }, + { url = "https://files.pythonhosted.org/packages/24/b7/fc4c50bb1b38e864010ea396046cabe85129bf9e65b11edcfbc37d356241/lxml-6.1.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:a1cec0f99b9b914d39176347a93b7610dc09324491aee1cbc57cd291a41a1d55", size = 4755190, upload-time = "2026-09-02T14:49:39.872Z" }, + { url = "https://files.pythonhosted.org/packages/94/e2/ee9aa6ed2b666b2db1f6f7fd48964ff9da39ebe827ef5eac0ab881f639d9/lxml-6.1.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f6b9d2aad499c769ee8287609ab0e6de99d8bcea99c6e6c2e64945259fd52fb2", size = 4979517, upload-time = "2026-09-02T14:49:42.153Z" }, + { url = "https://files.pythonhosted.org/packages/29/e3/e7763d1661b283ddd4fa36f91b9a497db6b8d2aff55028b16c7f642e0755/lxml-6.1.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a23fefdb345b2d4d0ff2860571b5ff9a89a28b6a120f720e8fb0324d346626", size = 5115270, upload-time = "2026-09-02T14:49:44.493Z" }, + { url = "https://files.pythonhosted.org/packages/2d/cd/22205d5b4d177e3f4156f780412426ee7c7f8107809f119f0dcc40fa51e3/lxml-6.1.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:545ccc14fb05485f48b4439ec35beb16d5b5280eb6c81c658bd4707a2a119414", size = 5032449, upload-time = "2026-09-02T14:49:46.841Z" }, + { url = "https://files.pythonhosted.org/packages/da/43/06a4626c3bb79ef8c501b674afab8100d64e798665bb2a97d1c960636a49/lxml-6.1.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:93476b6514b373fc6ca67d26c442784f7807c86f00635bfe79f935c3eab2af17", size = 5603325, upload-time = "2026-09-02T14:49:49.664Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9c/733682a0c2de9f5779ba207bbb3f3f6be8c6bda863fc01739b186b38783a/lxml-6.1.3-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8db38ff3fb7aee7d6a82ae4da2eef1178656fe1216841fbd24870062a9d60473", size = 5229023, upload-time = "2026-09-02T14:49:52.447Z" }, + { url = "https://files.pythonhosted.org/packages/c6/8a/e69cdaca3fd33a647942925664f01b20908d41a6968c182305be9c38fb11/lxml-6.1.3-cp314-cp314t-manylinux_2_28_i686.whl", hash = "sha256:25f4118c438f96bb466e83108506d03d5c31b1bd2387e83e5b070bda6ded9c37", size = 5317811, upload-time = "2026-09-02T14:49:55.25Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b2/0c397588174403c2ab68fc464abf97e03e7324f9c6cb6a99023104707195/lxml-6.1.3-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:1beb0f9909b26cee938df9ba56b15252a84429b1fc30ce6fca161390b9789a70", size = 4646516, upload-time = "2026-09-02T14:49:57.761Z" }, + { url = "https://files.pythonhosted.org/packages/56/7e/cfea25afafbe49db8b225764f7f74bb37c2a7f5e717d917d3d4a5e098ed4/lxml-6.1.3-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3a27ac6c780c8b8a1cd231b58407634cafc1c4cc28cd6c7141362df0f36351e7", size = 5240626, upload-time = "2026-09-02T14:50:00.279Z" }, + { url = "https://files.pythonhosted.org/packages/a1/75/7a587771bb52ebb0e2c57b6dbe9fd96a70fbb54d72ddd97d54c5f8ec18d5/lxml-6.1.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a1932d7ce78a561367512c594fe66eac2b2ec9b9264cfd9b5f950622f4a116e2", size = 5086619, upload-time = "2026-09-02T14:50:03.245Z" }, + { url = "https://files.pythonhosted.org/packages/1e/01/94c0ebe6d831861542d251e038052e52bf6d33f1d18f1cfffdc82851065a/lxml-6.1.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:7d0f5976aa2701996f759b30172925829867547bb073af0ae67d1307a0f0262c", size = 4758828, upload-time = "2026-09-02T14:50:05.873Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f1/938d67bd0e5b1fdfa52be28aefdffbad57e1f6b8e921c2aab88542c75f40/lxml-6.1.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:c5e7ce578aa8a80910a72a8ca0bbea3baae10100827249001999726a788456d8", size = 5627083, upload-time = "2026-09-02T14:50:08.555Z" }, + { url = "https://files.pythonhosted.org/packages/d8/65/4e51522f6c214650db0abb7b16ccd11b1238b8a05a8d59aa4ebed59c9f67/lxml-6.1.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d97c5227621af74b111882a290b10f371780a38eef9d9e730408fba2259b52fb", size = 5235170, upload-time = "2026-09-02T14:50:11.255Z" }, + { url = "https://files.pythonhosted.org/packages/92/c2/e73d19365665f6b16ef84df21199befc3b06e4c539046ad2d9595f6fb9ea/lxml-6.1.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:da707f14ea3c35ee463d50acd596d6488e4b2b4ae7cf77a5bf93f55c023d63e8", size = 5252273, upload-time = "2026-09-02T14:50:13.782Z" }, + { url = "https://files.pythonhosted.org/packages/48/a9/7f386c84c9fe2854e1ca6e231c285e1c8f392971ac353c6865e6ec49faff/lxml-6.1.3-cp314-cp314t-win32.whl", hash = "sha256:9efe56a68179f3adc4de41861c9358931db03837c48dd5e1c78077b84dd07f3a", size = 3902712, upload-time = "2026-09-02T14:50:16.171Z" }, + { url = "https://files.pythonhosted.org/packages/82/a6/8a3eb793f7900ef01c7f99e6f5fcbcfbdff35251cfaef66b32a4c16352d6/lxml-6.1.3-cp314-cp314t-win_amd64.whl", hash = "sha256:c9389b3784b56c58d933b5e0aecdf28f901b073ff385358d8a7d40907f6e14b2", size = 4400979, upload-time = "2026-09-02T14:50:18.621Z" }, + { url = "https://files.pythonhosted.org/packages/cc/c4/3807bea283b4fe9e9d9f5dde46a73df91178472b335d2778e10b2a37aa22/lxml-6.1.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32a409be3190b088f960ac92bfedfbef2f86c49ff940765e1548177592d20026", size = 3823401, upload-time = "2026-09-02T14:50:21.119Z" }, + { url = "https://files.pythonhosted.org/packages/e1/8e/4614fcd65496054cfb7172662f3576a59200278739506433b8c241ea422a/lxml-6.1.3-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:6ea2f13dce778ca072ccee598bca46a092ce192e8fd907b6c1f0e52c800529a0", size = 8609378, upload-time = "2026-09-02T14:50:31.772Z" }, + { url = "https://files.pythonhosted.org/packages/f2/51/2cdce3c65fa99a6195dd8fbd512d33407c1000ad99f63e0a285b63d7a8eb/lxml-6.1.3-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:c581b1d68b3845fb86c6b2983e755b29bf001461c59fa411d2c26a911b6559a9", size = 4640022, upload-time = "2026-09-02T14:50:34.41Z" }, + { url = "https://files.pythonhosted.org/packages/52/09/0b30084e9eb1c546a4be3d9c56df70058d116b1a320400a59b0f7da87bf0/lxml-6.1.3-cp315-cp315-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e01125896585139453cab8cb235893644d8815d7509520da95ae3ee8d1c1f79", size = 5037928, upload-time = "2026-09-02T14:50:37.007Z" }, + { url = "https://files.pythonhosted.org/packages/b8/0e/5c37275a3e361f6138dc06db748ea565c1fe8a5f4ee5e2ddd80047c81a89/lxml-6.1.3-cp315-cp315-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:290f66b97ede0e552e1cb44a0fd8a74f9753ee635b50830a0b122fb72788d015", size = 5661932, upload-time = "2026-09-02T14:50:39.777Z" }, + { url = "https://files.pythonhosted.org/packages/70/c5/b71ffb289b15e2642e2a3cf6d468c44da39ea119061a99e5b05e3d10f217/lxml-6.1.3-cp315-cp315-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73fc05988ed20809450474ba760a87c8ad4e455fc09783c02195e56ec634b41a", size = 5249209, upload-time = "2026-09-02T14:50:42.141Z" }, + { url = "https://files.pythonhosted.org/packages/81/ea/9910da149a23932f9301652e57661cd9e42b0df18f12be21159b7255f92b/lxml-6.1.3-cp315-cp315-manylinux_2_31_armv7l.whl", hash = "sha256:dc3a44689eea43eab836e5c98a8ab015dc2419987d1ea6eafc7c590cdff86bed", size = 4704543, upload-time = "2026-09-02T14:50:44.634Z" }, + { url = "https://files.pythonhosted.org/packages/76/07/9290329cd188c62e22021f79df04ee0cc33d9a93b0d38bd65ccd452ad9d0/lxml-6.1.3-cp315-cp315-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:209c3ccbfe35a04ac6d24f0611f9d1cbf8025d49991b14acd935236234d6c156", size = 5261298, upload-time = "2026-09-02T14:50:47.301Z" }, + { url = "https://files.pythonhosted.org/packages/c9/0c/aba78bd3401cd99b73a0aed8e2b9b43e14be94fab3603d4bbc8a62365f2a/lxml-6.1.3-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:2f5b2a2b9811b853b39bfa41367c6d78747b8e3e80e07fc5a24aae295c1a4d7d", size = 5090453, upload-time = "2026-09-02T14:50:49.952Z" }, + { url = "https://files.pythonhosted.org/packages/8d/dc/fa4426c3355aa0216cbeb3911495b5f65a26e0df85859a89928fe28f0396/lxml-6.1.3-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:6a406d0b3cb207b0fa460ed4dc93e866f44f105da0169361cb18ff998a44c7f0", size = 4744709, upload-time = "2026-09-02T14:50:52.394Z" }, + { url = "https://files.pythonhosted.org/packages/be/2b/224fe7918658ab7c532ac2412f3c1eb28f71e6364fb07566262d0cc6a7b6/lxml-6.1.3-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:53258656846f5c48996b882fb4b135885e088a3ad3d96b4bc0530f95124d1f69", size = 5685802, upload-time = "2026-09-02T14:50:55.043Z" }, + { url = "https://files.pythonhosted.org/packages/21/44/7d480819b9adcae5f84dd8ac529132c6b7a578544398225cd20321adcd91/lxml-6.1.3-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:aa633613ff907ea91b9b0489a1f0da1b8725d8c6ccec6b77e8a1c9c235044bb0", size = 5249019, upload-time = "2026-09-02T14:50:57.985Z" }, + { url = "https://files.pythonhosted.org/packages/72/83/385a267ea1b6b283f2249dd827ef360a295e9db14e13ef4665a120c60d64/lxml-6.1.3-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:90f709b9accab6b2e4d14f5c8718203877a0486bcb3afd74d8b539ecd1e961d4", size = 5271886, upload-time = "2026-09-02T14:51:01.667Z" }, + { url = "https://files.pythonhosted.org/packages/d8/0d/f967b0eb172ae876855a402d6d9b11fa86e3e0c89ca9bbfeadf7ffbfa719/lxml-6.1.3-cp315-cp315-win32.whl", hash = "sha256:b4fc6b03b9d9d90557274f571ab30e7fbbfc527955536935d96f98b6817a86e4", size = 3662894, upload-time = "2026-09-02T14:51:45.173Z" }, + { url = "https://files.pythonhosted.org/packages/f4/48/d8a8c4160a29e663109ad520bac2deb37fcd014756d024561e8bc3e611ec/lxml-6.1.3-cp315-cp315-win_amd64.whl", hash = "sha256:33cadd956b667997e4de1635fce9541f2e8ede2038fcde8cf55aa14d571d1bad", size = 4074626, upload-time = "2026-09-02T14:51:47.77Z" }, + { url = "https://files.pythonhosted.org/packages/25/20/3e1395d34d19f9254625d0b567b81cf70d37d3417be074f4d63b94a2be3c/lxml-6.1.3-cp315-cp315-win_arm64.whl", hash = "sha256:8a330c0ee5fa318c7b5cbbaad882baeca3f570357e7eb25ab34bf31008150758", size = 3749495, upload-time = "2026-09-02T14:51:50.663Z" }, + { url = "https://files.pythonhosted.org/packages/8f/c6/7465ffd9c43883526a382df6fa4846c9d8d419214f7effbf65270e795471/lxml-6.1.3-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:0bf5a3e397df2ec4258eb5eea4c1ac6cf013ca1abd04a176903bff20a70021fe", size = 8857677, upload-time = "2026-09-02T14:51:05.109Z" }, + { url = "https://files.pythonhosted.org/packages/ed/eb/1f3a917e299df43c8162c3e6f64fc2cea3bcf277910f35bff5b8e5d39901/lxml-6.1.3-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:13d22c0d57355366b393936acf6b98a5e0edeadddd3fccbc6a846c50a76b8741", size = 4754522, upload-time = "2026-09-02T14:51:08.137Z" }, + { url = "https://files.pythonhosted.org/packages/d7/f9/f81b4bdb6efb7a596be29603d8758154d00a5f545db9f3cef9d9041c8f64/lxml-6.1.3-cp315-cp315t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cad7617727a96d189bd6f979d0fadf765198c7934e85f4edaba9bf3ad919a300", size = 5033744, upload-time = "2026-09-02T14:51:10.633Z" }, + { url = "https://files.pythonhosted.org/packages/c8/0f/26d9bfaacb319c86e0eca8a1a0bf1130d36a7afbd318883e23caea63763d/lxml-6.1.3-cp315-cp315t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cae82b5ca24b0c2beedb269f6e2a96f466acd926879ab00ae19f1a65cbf9ffb0", size = 5615269, upload-time = "2026-09-02T14:51:13.357Z" }, + { url = "https://files.pythonhosted.org/packages/5d/90/73675f3f4141350ed65d6fec533b107d4e802c5caa340cf111771edd86e0/lxml-6.1.3-cp315-cp315t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:69cafd61aea04ebb3502c93c2aaa568b12931ca0802231e0b5de76bf8b6e74bd", size = 5236280, upload-time = "2026-09-02T14:51:16.051Z" }, + { url = "https://files.pythonhosted.org/packages/fd/be/ed260767e7977de463a0f91f3f4fffcab85c0a2a024a21ffe1fa442c2c79/lxml-6.1.3-cp315-cp315t-manylinux_2_31_armv7l.whl", hash = "sha256:dc205732d593118cf701d986f40e9de7801bb2e371cb189ddbda9b7348f4d97e", size = 4650718, upload-time = "2026-09-02T14:51:19.102Z" }, + { url = "https://files.pythonhosted.org/packages/d0/fd/e9839d03b1e767f2725cf7d7d81b80d5f3f9fdc10ad8827e2479311b046e/lxml-6.1.3-cp315-cp315t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:88e719b9437f148f7e1465df845c758dd1598618cbea3a2fd1e61a715542f2b2", size = 5243376, upload-time = "2026-09-02T14:51:21.606Z" }, + { url = "https://files.pythonhosted.org/packages/34/a5/4606e347e2788c301f677004aa83e28d24da9fe663a24380122af57be6fc/lxml-6.1.3-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:40983eabefd13da003e68170928c7acc011f0d095eefce5871a3c71c9385fb9a", size = 5092340, upload-time = "2026-09-02T14:51:24.21Z" }, + { url = "https://files.pythonhosted.org/packages/ea/99/3314a8661cdf30f493c55a87db283961dfaae08451976a2ca418958e1804/lxml-6.1.3-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:fad67b12ffe0f71e02b4932b04883cbc76a9072bbd30731409d3523cf058b011", size = 4758768, upload-time = "2026-09-02T14:51:26.813Z" }, + { url = "https://files.pythonhosted.org/packages/30/58/3bdc577f78ea8b7d72d39a84506f7001d5b28728f43e5b84891e3b7d9a4a/lxml-6.1.3-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:6cd11e7550d89e551a87dcec30f04b1fca32e86b68708aa01a4daa455d8605e5", size = 5649546, upload-time = "2026-09-02T14:51:29.453Z" }, + { url = "https://files.pythonhosted.org/packages/6a/e4/652633de1a2395949ebb7a8fc7d089aba12a2b45f0fefbc9d29e3e3ab3cf/lxml-6.1.3-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:ca0ec532ad2f5ba1e5ec120ac157769c57f01855b3d8bf37213f5d88abd9ba0a", size = 5234874, upload-time = "2026-09-02T14:51:32.262Z" }, + { url = "https://files.pythonhosted.org/packages/65/a6/c4581d171de30449304b4859bbd3607e9b40da13c0f88b68e6097c8d785e/lxml-6.1.3-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:e99e09ab7741f1281e2677f4c0058c7f5267d182530b09c87e4f6aa26adf3887", size = 5260043, upload-time = "2026-09-02T14:51:34.841Z" }, + { url = "https://files.pythonhosted.org/packages/b8/d7/ed6ee6186a89e69ca4ea9658b2a278f46a5efe8b5d4db56c7197f18653fe/lxml-6.1.3-cp315-cp315t-win32.whl", hash = "sha256:ace1d2c83b2bd24db5940600541140e87a325e119cb32d5fa9ad720d7e76648e", size = 3901093, upload-time = "2026-09-02T14:51:37.234Z" }, + { url = "https://files.pythonhosted.org/packages/67/9d/11d10257a4a048d04195d638bb61f0246ce2448eb05f682bcbab25a257a8/lxml-6.1.3-cp315-cp315t-win_amd64.whl", hash = "sha256:b49638355ea3bebba70da783ccbc630fd72afa16bc46c54474bfa1f9a915bbc6", size = 4395446, upload-time = "2026-09-02T14:51:39.884Z" }, + { url = "https://files.pythonhosted.org/packages/f8/b7/44edd7de434181c582892e68d1ffe6775ca403ce14aea07cb5a218a936cf/lxml-6.1.3-cp315-cp315t-win_arm64.whl", hash = "sha256:5a721a98c649855963811b59b55755b30566e7f7fc40bdc9803d66dee9f811cf", size = 3822836, upload-time = "2026-09-02T14:51:42.471Z" }, + { url = "https://files.pythonhosted.org/packages/ad/23/dc1fdf3a53f84ca88b6e942277ddb47954844a0ececea8cc5fa3c1324831/lxml-6.1.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4b061064b4a2fe8598a466d723d43dbcd5a610a5d5cfe02fb6226f5c17349f75", size = 3947704, upload-time = "2026-09-02T14:46:22.27Z" }, + { url = "https://files.pythonhosted.org/packages/f0/ed/e36d547d6c958b5693b873504735cb4d0388d545945d66a7aed8983a720b/lxml-6.1.3-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8499d464de86fab0f102313cce32a9bed9ab1f06ec813cf025cb790964fbb765", size = 4220149, upload-time = "2026-09-02T14:46:24.907Z" }, + { url = "https://files.pythonhosted.org/packages/98/54/7f51e6b6cc0755f9b5fc6637748279e9f48289d917b3a47ac9fedf3318d3/lxml-6.1.3-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9e67324961ac9bbe616cce5100514d2e34d88665aeb07071e8b16eac55d06d94", size = 4329391, upload-time = "2026-09-02T14:46:27.111Z" }, + { url = "https://files.pythonhosted.org/packages/eb/9e/840b0d2e25c10c491b010d555b46e6e5264d3ad73a91557405fceb738c35/lxml-6.1.3-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d12669a2c419b0e8dc423d23dea24bb82f6f9cb829f32e04674b0ba40322a7c", size = 4262125, upload-time = "2026-09-02T14:46:29.199Z" }, + { url = "https://files.pythonhosted.org/packages/69/8f/42a41571dfc772c12628747f883d24c978053856825b99d7a187117b8079/lxml-6.1.3-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:97acecb11cbc411473f15b8d780df06d7a9f3a2aad9aca78364f56640c8fb70e", size = 4410104, upload-time = "2026-09-02T14:46:32.102Z" }, + { url = "https://files.pythonhosted.org/packages/f3/aa/27d93812be916f1f674b2035edd86d41c77745ff2ad84f58c25a7445a397/lxml-6.1.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f8b9c8ceebae6387d0dc77f7f4dbbfbfc962dba2efbfe6877486075a480726b4", size = 3510724, upload-time = "2026-09-02T14:46:34.122Z" }, + { url = "https://files.pythonhosted.org/packages/ec/c1/2433176de263cc3f51fd2c303f993d5bb7f1da3139a0f7d168116c0bfa7a/lxml-6.1.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d2765c18ce303149ee804b1f3dad11232726dd0a702d73a15cf19179ac8cc962", size = 3942969, upload-time = "2026-09-02T14:46:36.55Z" }, + { url = "https://files.pythonhosted.org/packages/7c/71/de7759096f480180fd9e43ff7c017860e2d2a9a43741ab093cbdf1820f07/lxml-6.1.3-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d5a748d12dd9b535e0a130f60dae9ddf0adafbabe61e7864f55c7436c84547a", size = 4213008, upload-time = "2026-09-02T14:46:38.784Z" }, + { url = "https://files.pythonhosted.org/packages/b8/9b/c2d09af47a34fa6c0c27473083812b449a411680bd04bbe609cde291ddc8/lxml-6.1.3-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:41096ec0740a58dad03d3ae0c7486d306d20becefb13ceb1649835ab3eb64167", size = 4322012, upload-time = "2026-09-02T14:46:41.031Z" }, + { url = "https://files.pythonhosted.org/packages/68/f3/bf56fee0403ebd995be8e78ec9aca566016487d1b3cbf755ebea8ccffbdb/lxml-6.1.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:415e3a115c0d510e329020012834d1c0aa1c581ee53a218603e38abbc1dea70a", size = 4257402, upload-time = "2026-09-02T14:46:43.134Z" }, + { url = "https://files.pythonhosted.org/packages/1c/1d/6da9cc086a20d9dd6bcbf7c5d9575f0331cca9a05e67dab02d15e828170b/lxml-6.1.3-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20428910dae17a1a93152a3ff2c0441d2f4932992c0797d65651dd0561f1792f", size = 4410889, upload-time = "2026-09-02T14:46:46.975Z" }, + { url = "https://files.pythonhosted.org/packages/03/5c/91fe48856f9f8089be3096fa4dbe4b3fb5526f3bf3e852ea9497f399cb9f/lxml-6.1.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bc8dd3d9c93e70c3df974a201ac2958b6d77b465d813c51d1f15fa8e645763ae", size = 3511258, upload-time = "2026-09-02T14:46:49.046Z" }, +] + +[[package]] +name = "lxml-stubs" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/99/da/1a3a3e5d159b249fc2970d73437496b908de8e4716a089c69591b4ffa6fd/lxml-stubs-0.5.1.tar.gz", hash = "sha256:e0ec2aa1ce92d91278b719091ce4515c12adc1d564359dfaf81efa7d4feab79d", size = 14778, upload-time = "2024-01-10T09:37:46.521Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/c9/e0f8e4e6e8a69e5959b06499582dca6349db6769cc7fdfb8a02a7c75a9ae/lxml_stubs-0.5.1-py3-none-any.whl", hash = "sha256:1f689e5dbc4b9247cb09ae820c7d34daeb1fdbd1db06123814b856dae7787272", size = 13584, upload-time = "2024-01-10T09:37:44.931Z" }, +] + +[[package]] +name = "narwhals" +version = "2.28.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/67/43/e11d75a7fb635c6f63dd6e176cb3e050ec2fee35f2f2267b332a6ad351ec/narwhals-2.28.0.tar.gz", hash = "sha256:2d9860c80d6610820826e94c6e42ce4b1d92d89cdd479586e652a8d211dab345", size = 754977, upload-time = "2026-10-27T12:19:33.777Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/82/8ef9c7e0f50493307a49c09cfa7f00c5a778b6d1921f7b2dabe1d3a5809e/narwhals-2.28.0-py3-none-any.whl", hash = "sha256:1f69d6385ec30e2fa6e1fdeb37c562ecabd0f713f6f3e3b58a668c5c9df9567f", size = 490591, upload-time = "2026-10-27T12:19:31.649Z" }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, + { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, + { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, + { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, + { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, + { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, + { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, + { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, + { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" }, + { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" }, + { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, + { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, + { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, + { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, + { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, + { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, + { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, + { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, + { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, + { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, + { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, + { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, + { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, + { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, + { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, + { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, + { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, + { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, + { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, + { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, + { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, + { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, + { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, + { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.11.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/49/ec46835a70be8fa6446c495126ac84fdb28cb2558e1620ffb87a10c8b64c/numpy-2.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0280e0356c0829a18d9de1cb7eee50ec22ca639878d7240307ca0943d73cd2c4", size = 16969194, upload-time = "2026-05-18T23:33:13.503Z" }, + { url = "https://files.pythonhosted.org/packages/0e/0d/f5957185c0ee2f3e12f78715aa9e3b353fd83633316c8532b38faa37e3f6/numpy-2.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110f8b71aacb688ec69062bb7f6938a0f8acb01b7c1c4beb453c65b6d234584d", size = 14964111, upload-time = "2026-05-18T23:33:17.795Z" }, + { url = "https://files.pythonhosted.org/packages/ad/40/40a40ee0ddf7ceb782c49af278894b686e586d65d8c1889c8b5da01a3d7d/numpy-2.4.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4cfe66903cc32a9921a6733d96b19bb6abf310397581bbad89c228f5abaf0ee8", size = 5469159, upload-time = "2026-05-18T23:33:20.654Z" }, + { url = "https://files.pythonhosted.org/packages/63/13/f9a8046535cb21deae82f8d03de9617e08882d274fad2539630761888228/numpy-2.4.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8155154c7c691289fe18f510b5d4657c68c67989f293f0535a91360392ff6538", size = 6798936, upload-time = "2026-05-18T23:33:22.987Z" }, + { url = "https://files.pythonhosted.org/packages/33/a8/6fa8c1a345a8c85dbb21932c447bee07c30a2c2a3f31e369c0a84b300147/numpy-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab0a9c4ffb1a6d95ef519fe4247dba8eb6b18ad93999f76b7f657039acabd47", size = 15966692, upload-time = "2026-05-18T23:33:26.62Z" }, + { url = "https://files.pythonhosted.org/packages/02/03/74fe2a4cb3817d94d86402f2506554130a2f01414e299b5a843e5a8a957f/numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cd468399cfd2504718f0ba50e410dca55a170b61a02ad92bb18c8a65186e93", size = 16918164, upload-time = "2026-05-18T23:33:29.955Z" }, + { url = "https://files.pythonhosted.org/packages/c5/80/3615be3313f7e7696609bc194b9f0101da809df79e859bdb84e0cd043f46/numpy-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2d37ab77531417474168eb79d6d80b14f821a966818505d03013d0833edb7a8", size = 17322877, upload-time = "2026-05-18T23:33:34.724Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ac/a691e0fe2675e370d0e08ff905adc49a1c8830e8cae03efe4477e92cd55d/numpy-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f407cb6b8e9d6d8c626bc73c945db1706035af8fd632295547bf1c9e46d092d6", size = 18651487, upload-time = "2026-05-18T23:33:38.217Z" }, + { url = "https://files.pythonhosted.org/packages/15/a7/9bc1cd626d7bf6869bfedf27b91b6ab5dd607758bf8e959d6fa80c6a59cb/numpy-2.4.6-cp311-cp311-win32.whl", hash = "sha256:ddea102b48f9e339f3948bf22040944184627a30fdf7f858667673b9c5f033c8", size = 6233945, upload-time = "2026-05-18T23:33:41.331Z" }, + { url = "https://files.pythonhosted.org/packages/c5/31/7fc6239c12bce7e931463251cca4426c465e1876ba3cc785402ef4dd8f4e/numpy-2.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:1e254a00cdf42b1e4d5b3d68d33af63268d41340d8885df2ab6470f2e1500147", size = 12608406, upload-time = "2026-05-18T23:33:44.131Z" }, + { url = "https://files.pythonhosted.org/packages/27/83/140f85a466595a16382996a1bf06b2b54bcd597488921b0c9daaeeda72af/numpy-2.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:ed9749eef4cbd126da3dc1d6bcb3a57f5eb7ac6a6484146bdbf743f552dfc577", size = 10479528, upload-time = "2026-05-18T23:33:50.725Z" }, + { url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119, upload-time = "2026-05-18T23:33:54.065Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246, upload-time = "2026-05-18T23:33:57.621Z" }, + { url = "https://files.pythonhosted.org/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410, upload-time = "2026-05-18T23:34:00.302Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240, upload-time = "2026-05-18T23:34:02.852Z" }, + { url = "https://files.pythonhosted.org/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012, upload-time = "2026-05-18T23:34:05.485Z" }, + { url = "https://files.pythonhosted.org/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538, upload-time = "2026-05-18T23:34:09.265Z" }, + { url = "https://files.pythonhosted.org/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706, upload-time = "2026-05-18T23:34:13.053Z" }, + { url = "https://files.pythonhosted.org/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541, upload-time = "2026-05-18T23:34:17.024Z" }, + { url = "https://files.pythonhosted.org/packages/f6/81/e1b27545deedce7f4a0b348618c6b62d74e36a4dc9ccd42f3eb2f85eee32/numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45", size = 5962825, upload-time = "2026-05-18T23:34:20.3Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751", size = 12321687, upload-time = "2026-05-18T23:34:23.095Z" }, + { url = "https://files.pythonhosted.org/packages/63/cf/5a6d34850a39d1093558564f77ee8e8e0bee5061151b8f05a55711001ec7/numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8", size = 10221482, upload-time = "2026-05-18T23:34:25.876Z" }, + { url = "https://files.pythonhosted.org/packages/fb/82/bdab26d7438c6791ca31b7c024ca37c1eab8b726ba236129005cd4a06e45/numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0", size = 16684648, upload-time = "2026-05-18T23:34:29.41Z" }, + { url = "https://files.pythonhosted.org/packages/1b/30/a80189bcc7f5e4258b3fbc3968d909d1756f54d023299ecc39ad6fdb9ef8/numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb", size = 14693902, upload-time = "2026-05-18T23:34:33.013Z" }, + { url = "https://files.pythonhosted.org/packages/97/12/70b5d0d7c15e1ebb8a6a84a8caa1d19e181d84fb58bb6d70aca29099dec1/numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f", size = 5198992, upload-time = "2026-05-18T23:34:36.132Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/ebd2a8f8a83541f8d38cc5667e8c2b69cecfd30da6e45693e8158857d44b/numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3", size = 6546944, upload-time = "2026-05-18T23:34:38.484Z" }, + { url = "https://files.pythonhosted.org/packages/bb/c5/7b863a97a91671a0338f4253bd3b5a3d3852f0692dae91711c9f4a10e787/numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b", size = 15669392, upload-time = "2026-05-18T23:34:41.257Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9d/3584b9984ca4c047aea75214ce1a4c4c73d849bd71b604264b7f5653f8a8/numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089", size = 16633220, upload-time = "2026-05-18T23:34:45.075Z" }, + { url = "https://files.pythonhosted.org/packages/05/ae/7c67fba23bd98caec7c99261f3a16072ade14813486b0282cb29846de832/numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a", size = 17020800, upload-time = "2026-05-18T23:34:49.065Z" }, + { url = "https://files.pythonhosted.org/packages/d9/5d/3b6725cb31d983c5e66916f5d36f6d7e5521129e4c4404d64f918292a5b6/numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605", size = 18357600, upload-time = "2026-05-18T23:34:52.709Z" }, + { url = "https://files.pythonhosted.org/packages/f7/da/2ccc6c2fe8898dee01d90c75c5f5f914a23daf99e3e0f59516a08760c8b5/numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91", size = 5961134, upload-time = "2026-05-18T23:34:55.618Z" }, + { url = "https://files.pythonhosted.org/packages/b5/cd/9cc4dc876fb065d5c220aae4d5e14826b2715331bb7618ce1fb07a679d99/numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359", size = 12318598, upload-time = "2026-05-18T23:34:58.928Z" }, + { url = "https://files.pythonhosted.org/packages/39/1e/c0bcba1f8694116485fe28fd1be698c278fcda4141c5b0e53a2aed8b12a8/numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778", size = 10222272, upload-time = "2026-05-18T23:35:02.167Z" }, + { url = "https://files.pythonhosted.org/packages/63/6d/cc5619247c8f4204e507f5883528372e4ac4bb189e579fb859a12e480b1f/numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1", size = 14821197, upload-time = "2026-05-18T23:35:05.468Z" }, + { url = "https://files.pythonhosted.org/packages/00/58/f1c39161c87d9e9bed660f1ed4bafc0e403d5ec9650b6dd77aead07d489b/numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe", size = 5326287, upload-time = "2026-05-18T23:35:08.693Z" }, + { url = "https://files.pythonhosted.org/packages/af/57/3917ab0fd97f271a8694513581b8a36c655f111c446852c302f04ccdb6fc/numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997", size = 6646763, upload-time = "2026-05-18T23:35:11.459Z" }, + { url = "https://files.pythonhosted.org/packages/eb/0f/037e64c494b67581ae18193d770adef354c41f3f2c8ebf865602d949bf8f/numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20", size = 15728070, upload-time = "2026-05-18T23:35:14.79Z" }, + { url = "https://files.pythonhosted.org/packages/21/a6/5d2bae9c9542eb4df16dc9c46dc79c186e9bad53805dfa5399a6023c6db0/numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d", size = 16681752, upload-time = "2026-05-18T23:35:18.836Z" }, + { url = "https://files.pythonhosted.org/packages/92/14/23d1dfb410ae362cd59ce53e936b1513d545eb40db3949ced632e19a459e/numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67", size = 17086024, upload-time = "2026-05-18T23:35:22.52Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6e/23595a2c642cdf3bc567877064bdd7f91c8b0038a4453cf2daf7248eafe9/numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd", size = 18403398, upload-time = "2026-05-18T23:35:26.398Z" }, + { url = "https://files.pythonhosted.org/packages/8a/90/0ac3bc947217e66dec77e7cbc6a1979d1af70b6461b82f620d3bccd5e4c8/numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab", size = 6084971, upload-time = "2026-05-18T23:35:29.387Z" }, + { url = "https://files.pythonhosted.org/packages/77/71/5673e351671a1d2bd6063b91b44f70c0affea7d1516fa7a6572941ba4aa1/numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75", size = 12458532, upload-time = "2026-05-18T23:35:32.175Z" }, + { url = "https://files.pythonhosted.org/packages/3f/88/19d3503c5046e688f049274b27a3ef3d771152fa80d3ba3d01a3dff61abe/numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd", size = 10291881, upload-time = "2026-05-18T23:35:35.465Z" }, + { url = "https://files.pythonhosted.org/packages/f8/91/3ab2044d05fd16d343c5ac2e69b127f1b2854040dd20b193257c78028bd3/numpy-2.4.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06ca2f61ec4385a07a6977c55ba998a4466c123642b4a32694d3128fce18c079", size = 16683458, upload-time = "2026-05-18T23:35:38.353Z" }, + { url = "https://files.pythonhosted.org/packages/8e/62/764ce66fa4147ae6d73071a3abf804ffe606f174618697c571acdf26a7c9/numpy-2.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38efbc8de75c7a0fc1ac190162d892787f3f47b57cc291231aafee36b80982b7", size = 14704559, upload-time = "2026-05-18T23:35:42.14Z" }, + { url = "https://files.pythonhosted.org/packages/60/61/23f27c172f022e04025b7dc2367f4d63c1a398120607ec896228649a6f48/numpy-2.4.6-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d581b735e177fdcdce6fed8e7e8880a3fb6ee4e3653a3ac6af01c6f4c03effc5", size = 5209716, upload-time = "2026-05-18T23:35:45.377Z" }, + { url = "https://files.pythonhosted.org/packages/03/71/21cf70dc6ea3e3acb95fc53a265b2fc248b981f0194ceb5b475271b8809d/numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:0a041d3d761dc3c35cc56ce0351506a02bcbc25f7b169f652435141a17db9096", size = 6543947, upload-time = "2026-05-18T23:35:47.926Z" }, + { url = "https://files.pythonhosted.org/packages/d5/91/64288395ee1799bd2e0b04a305dce9666da90c961e1f3fe982a05ee1c036/numpy-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40fdc1ae7125e518ea98e53e69a4ebc27e1fd50510c47b7ea130cf21e5e1d42b", size = 15685197, upload-time = "2026-05-18T23:35:50.863Z" }, + { url = "https://files.pythonhosted.org/packages/f3/eb/ebffaa97dc55502df69584a8f0dcf07f69a3e0b3e2323670a2722db9aa39/numpy-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c306dea656c12c68f51f4cea133cbe78ca7435eb28c735eac1d3ebe73be6e8", size = 16638245, upload-time = "2026-05-18T23:35:54.752Z" }, + { url = "https://files.pythonhosted.org/packages/b8/0b/54f9da33128d7e350fab89c7455902eeae70349ee52bddb448dc4a576f45/numpy-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:33111801a01c12a8a1e3721f0a9232f8cfc8ae2c6b7098167e6f623c6073f402", size = 17036587, upload-time = "2026-05-18T23:35:58.355Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f0/fdebc1052db1cc37c64beb22072d67cd6d1c71adca1299f53dec2b5e20d3/numpy-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae506e6902902557576a26ff33eda8695e7ecb3cb36c3b573a0765dee114ebdb", size = 18363226, upload-time = "2026-05-18T23:36:02.845Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b4/298628d98c72b57e57f7165ae6a481a1deaf6f3c28262a6e4c739c275930/numpy-2.4.6-cp314-cp314-win32.whl", hash = "sha256:aaf159caa35993cb1f56fb9b8e4610d35758e7ca005412eb1daa856a78c9c4b1", size = 6010196, upload-time = "2026-05-18T23:36:05.92Z" }, + { url = "https://files.pythonhosted.org/packages/df/ac/46de6dda46478f7942f839e094970be2d4a861e005c4b3bf07c92e291a09/numpy-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:b507f5c4c1d508876d1819b6bf9a49d365b96320b5d4993426b33a23ca4b8261", size = 12450334, upload-time = "2026-05-18T23:36:09.107Z" }, + { url = "https://files.pythonhosted.org/packages/78/92/b8b798ac784102c0da830d2257d59358e3d3d90d1e2b3f2575dad976c5cf/numpy-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:6f41ae150c4e32db4f3310cdaf64b1593a03dbabe29eec77fc9b50fe64061df6", size = 10495678, upload-time = "2026-05-18T23:36:12.766Z" }, + { url = "https://files.pythonhosted.org/packages/30/34/ec28d1aa8115971537c01469ab2011ee96827930f0a124de1000cc2a7ed7/numpy-2.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ece3d2cfe132e7d51f44a832b303895e6f2d499c5e74dfbdb06ee246147a304a", size = 14823672, upload-time = "2026-05-18T23:36:16.473Z" }, + { url = "https://files.pythonhosted.org/packages/16/bd/f6d1fede4e54e8042a7ff97bb495510f3c220f94bcd9e8b228e87c92cc0d/numpy-2.4.6-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e3e5193ef5a3dc73bceee50f7fdc2c90dbb76c42df8d8fae3d1067a583df579e", size = 5328731, upload-time = "2026-05-18T23:36:19.767Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f0/e105b9e2fd728a9910103884decd6951d9dd73896b914a98d9a231de02ee/numpy-2.4.6-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:17f9ade344e7d9b464a084d69bcf18fc691cb1db67c62ed80820bf4926d78f0e", size = 6649805, upload-time = "2026-05-18T23:36:22.266Z" }, + { url = "https://files.pythonhosted.org/packages/82/dd/1206a7ca6ab15e3f02069707ca96222e202af681bb73756da7527f3cb837/numpy-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd5ffd25db4e7ba6a375693b3fc0fc1791ec636c17db3720da19bde7180ec43", size = 15730496, upload-time = "2026-05-18T23:36:25.713Z" }, + { url = "https://files.pythonhosted.org/packages/51/e7/38d3ea825dcab85a591734decb2f6c67caa7c8367d374df1a1c3842f9b07/numpy-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d92c3819208a60205a12a245c91ad70cb0a85336659b19b834205573ac8456e", size = 16679616, upload-time = "2026-05-18T23:36:29.652Z" }, + { url = "https://files.pythonhosted.org/packages/93/b7/caabfdf53edf663e0b4eb74d7d405d83baef09eb5e83bcd32d601d72b93e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e85b752a1e912b70eaad4fafbd4d1238007ab221de2009b9a2f5ae7461239895", size = 17085145, upload-time = "2026-05-18T23:36:33.449Z" }, + { url = "https://files.pythonhosted.org/packages/f9/45/68d7c33a6bcf3e5aa3bdbd57a367e6f615286dfd6482f97e8ffeb734306e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:29cb7f67d10b479ff07c17d33e39f78c07f71c40ef30d63c153d340e96cd3fb4", size = 18403813, upload-time = "2026-05-18T23:36:37.369Z" }, + { url = "https://files.pythonhosted.org/packages/9c/50/0753655aa844c99cd9e018aacf76f130f1bd81d881bb74bc0aef5d73a8ba/numpy-2.4.6-cp314-cp314t-win32.whl", hash = "sha256:260a5d70215b61ab4fadf5c7baacd64821842975eea312125ed3c39a6391b063", size = 6156982, upload-time = "2026-05-18T23:36:40.817Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d4/7c67becf668f973cb490cec3e98dfd799d866f9c989a54d355672cfa0db6/numpy-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:81a1cca95ed5bb92aa8b10dd2cdc9a0d3853a50fad926c28b5d7e8ea54389627", size = 12638908, upload-time = "2026-05-18T23:36:43.996Z" }, + { url = "https://files.pythonhosted.org/packages/43/bb/e1c71a4295b1b1d1393d50dbb4f2a36283c6859d9d3892e84f00ec5a91d5/numpy-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:0c9136e14ed34a9e343a31c533d78a9813a69a3148332bce5e9821cb2f996e66", size = 10565867, upload-time = "2026-05-18T23:36:47.114Z" }, + { url = "https://files.pythonhosted.org/packages/de/12/b422cc84439adc0d00de605bf4a308890ae5c26f2c71fbd73e5d08fbb0dd/numpy-2.4.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:55cced7c52e981362f708ad635198e97a752dfba412cc03c23bbf3bd8d5cd662", size = 16847511, upload-time = "2026-05-18T23:36:50.673Z" }, + { url = "https://files.pythonhosted.org/packages/44/53/f481bef68011740f8849418d82db07230e825013f31f4eef5ba5b805316a/numpy-2.4.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6da64deb6b8ed903e7560180a92f2d804ee1ba5eeb849ac2748b8c1aba1f6d7", size = 14889064, upload-time = "2026-05-18T23:36:53.879Z" }, + { url = "https://files.pythonhosted.org/packages/7f/57/42ed575c10ced8af951d426bc4e1f8aff16fd851db33f067036215a7f860/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:68a5124b13fa6cc2086764a20005d30bc0548146f7f5322f02fce212ca14317f", size = 5394157, upload-time = "2026-05-18T23:36:57.194Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ef/f66cc724fcc36c1e364c67f51ae9146090b8b584f27d58b97fdae3edd737/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:948424b06129ce883307e8cff868c31396d8dc7630a59c61d70d98dbe70f222c", size = 6708728, upload-time = "2026-05-18T23:36:59.575Z" }, + { url = "https://files.pythonhosted.org/packages/1a/9c/c531f2293b91265d8b48e9b329f54fdd7ffae73cb4134ea10cca4237e9cc/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbbdb29840ca3d91ee0fece42fc29278886d908280bfec0a5846c6f901a3eb0", size = 15798374, upload-time = "2026-05-18T23:37:02.674Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b0/413077f6b1153ed3cba361401c6783bbad6114804a000cc22eb71c13e190/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ad03c0965fb3c692200e74d458ca28c1dbb4ce96f9a479a8aa041ad5fabca02", size = 16747286, upload-time = "2026-05-18T23:37:06.327Z" }, + { url = "https://files.pythonhosted.org/packages/15/ce/e5ec180bc41812edcd8daeb8639d205622c0e8c02259d8ab25a0201b3c2a/numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73", size = 12504263, upload-time = "2026-05-18T23:37:09.715Z" }, +] + +[[package]] +name = "numpy" +version = "2.5.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", +] +sdist = { url = "https://files.pythonhosted.org/packages/95/b0/c7453d0b6e2073c3264468b106ee1563750cecc910965e67357e3698c83e/numpy-2.5.4.tar.gz", hash = "sha256:9a94cf751c9ad8ebaa835bcd3d40dacf8534ad086b88c38029b65123c7999d2a", size = 20866315, upload-time = "2026-10-10T20:05:31.422Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/97/ba2074e92b7befea137e77ea8471e768bbd87c339b7e8c9f5a931949f977/numpy-2.5.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c6342f54c67093cae5c0227eb0eb772fdb79f2a2c37a6eb278b9909ee06aa356", size = 17001609, upload-time = "2026-10-10T20:02:40.843Z" }, + { url = "https://files.pythonhosted.org/packages/ff/a9/bac826765e971d8e16e2064e9ac7525fd69b40ac17c905033a7f5442023f/numpy-2.5.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b11e8fda06a7d69f15ebf542660b74466c2e51094800c1fb794f47ad4faeef17", size = 12015718, upload-time = "2026-10-10T20:02:43.45Z" }, + { url = "https://files.pythonhosted.org/packages/31/2f/5ea3570fcb8ccd0882bea99436a513b2c85dad8f774a2057849130a8fb99/numpy-2.5.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9cb18a327b49c5c337f972b03682f6a49855525faaf3c0d3e9c96cd0fd8880a8", size = 5451717, upload-time = "2026-10-10T20:02:46.169Z" }, + { url = "https://files.pythonhosted.org/packages/34/f2/b4fc1bafca03868220b5eaf729d2f21ebd7d7b151c0f9e144fe212bbca35/numpy-2.5.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:aec3fc4b32ff82421274f5d205c559c51c840c8df66a78efd7f3612dd005a26a", size = 6789926, upload-time = "2026-10-10T20:02:48.139Z" }, + { url = "https://files.pythonhosted.org/packages/dc/96/8319e2457ae4333c62c815c7006b869a4f60985c1e01024c2f8c6c040fe5/numpy-2.5.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe4d21ab149f15e4e6043dfb0de87e6e5f34ac176cde83060e9802981fca2ac2", size = 15695312, upload-time = "2026-10-10T20:02:50.115Z" }, + { url = "https://files.pythonhosted.org/packages/43/a3/c799c62e19c337e6d3770b08e475887fb30ce8477d3c09efca6b2f0228a6/numpy-2.5.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fbde6962867ee75b48b0ee29b2b9372ec5d617799dbaf38e82dc0596f2f7738a", size = 16727283, upload-time = "2026-10-10T20:02:53.186Z" }, + { url = "https://files.pythonhosted.org/packages/39/6b/3604e53fb00314d0dc1b94ec9125a1484f649c0a17480b1f0f0c7a9d6250/numpy-2.5.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:381a7a3d2e65e64c0ec302795ab9dc12bb1e73f150904699c153716177eebdaf", size = 17047890, upload-time = "2026-10-10T20:02:56.038Z" }, + { url = "https://files.pythonhosted.org/packages/4a/7a/e8b58a5289a0d464c52885de47c35a935cdd70c03a4c3ab94a5126416dd0/numpy-2.5.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b89d0aaae2fe498c648f4c4795c084db535af5bd98ef942b2a3681fb74ce8645", size = 18485839, upload-time = "2026-10-10T20:02:59.018Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c9/47094f597015009f310b8c900def59065ef1ff5a6fe7b51fc65ec58ec2c6/numpy-2.5.4-cp312-cp312-win32.whl", hash = "sha256:9968ab7e49b93ac6e1c3b2239732183152c9150f16308d30b66a372cffe3483c", size = 6138936, upload-time = "2026-10-10T20:03:01.626Z" }, + { url = "https://files.pythonhosted.org/packages/12/33/fefe62073dc8acfd0f2b9ed7c003af2f50aa61555e113e6db02b8f79f145/numpy-2.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7b1b6353e36a7e50de2973a38d705c88ee93adcf120673cee7f45a4a3fa223a", size = 12573091, upload-time = "2026-10-10T20:03:04.349Z" }, + { url = "https://files.pythonhosted.org/packages/1a/07/161270b0c2eec56e4c905f6d6d22e1b836887b2cb189d3f5820aa588e9dd/numpy-2.5.4-cp312-cp312-win_arm64.whl", hash = "sha256:aa1cce2ff3f8d953de38b76bf44602caeb69f101430208f64a10067f7cb4b1d3", size = 10521630, upload-time = "2026-10-10T20:03:06.767Z" }, + { url = "https://files.pythonhosted.org/packages/67/14/1c3ee0118a8fce08565a5d8482631608426a33af10a01077fada5dc7c119/numpy-2.5.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2377da2dd3ba2c1200956acbab2a358c83b8e1f8531191672d1cd6ad83250d53", size = 16997729, upload-time = "2026-10-10T20:03:09.291Z" }, + { url = "https://files.pythonhosted.org/packages/83/8c/b0ea9477fb1f0d4484bbc5cba21678cc9969704d8d7f3f158d1db35f8e14/numpy-2.5.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7415db95818b39ec475a5eea54d9e3b6bc83e3912158e46da3438cdce399804d", size = 12009826, upload-time = "2026-10-10T20:03:11.946Z" }, + { url = "https://files.pythonhosted.org/packages/e2/84/6a3d75b3ba3dfe84ac0053450753d1e6d250a8bf80f66474cc46d1fb643f/numpy-2.5.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:6d6a71b9d9a97c03633aa12565ef2825ffa036cc1d99cfd50dacf0f128af4fe2", size = 5445803, upload-time = "2026-10-10T20:03:14.329Z" }, + { url = "https://files.pythonhosted.org/packages/61/18/bb993f267ca20b376e07092a16793a5b31ed3138751e9ba480011a14d742/numpy-2.5.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d8200f16437b289a5bb927c6e184eccc3e8389bc0070fea4cd5b9e13c1757959", size = 6786220, upload-time = "2026-10-10T20:03:16.602Z" }, + { url = "https://files.pythonhosted.org/packages/db/b6/135bb0953b61dc21c6cafa14b424ae666944e4899cf140e00c2b322a1a45/numpy-2.5.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2e71b04c6cad90026e544501bbe0ab9290fa8a4d845e7e8c0d124fb429c988", size = 15689178, upload-time = "2026-10-10T20:03:18.721Z" }, + { url = "https://files.pythonhosted.org/packages/da/24/3bd070f3269dc609d8f26b2643f62ef91bb415841c0b294805aaf7fe06da/numpy-2.5.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6ffa07666f8da0eef81d149934a626d0d95fbd6838432a33e66245423a9062c0", size = 16718044, upload-time = "2026-10-10T20:03:21.386Z" }, + { url = "https://files.pythonhosted.org/packages/c7/8e/9d15bd356b0a019c965312b1a3c6a727cac4cae5bc40045fbc12ce4cff9c/numpy-2.5.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2fa3328f784fc8277fc48026f6cad516f5c561c5d8e2e39b3c9e0c8f23223b34", size = 17048364, upload-time = "2026-10-10T20:03:24.468Z" }, + { url = "https://files.pythonhosted.org/packages/dc/fe/9d5b560db964f15871885f2250795d15945f8699e17ef90c0c2ff4c875b2/numpy-2.5.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b86966fbe4ad7de710422175572bcdc75fdedadfb54bc6fab7deabccddd7780b", size = 18474904, upload-time = "2026-10-10T20:03:27.895Z" }, + { url = "https://files.pythonhosted.org/packages/e9/98/d27552990f1bd611ef3e7466adadc78312ea2df63b83aad47fdc3d3ca8df/numpy-2.5.4-cp313-cp313-win32.whl", hash = "sha256:5258bc06526964be5face2fc6f756857a3f24f21ec3e72ca131337a75b165d6c", size = 6134537, upload-time = "2026-10-10T20:03:30.511Z" }, + { url = "https://files.pythonhosted.org/packages/90/8c/140a40398a66b4471211be1affdb6ed24c486d581bd28d07b7f2fcb69540/numpy-2.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:8b4d2fd2d34e5f8c9235ee787de5631a37a28402b15cb80814df973d2be54129", size = 12566113, upload-time = "2026-10-10T20:03:32.612Z" }, + { url = "https://files.pythonhosted.org/packages/34/52/01d205e5e8ccb27b2b0b141e801f22b830198c979111b0fa44771438d9a9/numpy-2.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:bc39ac66a7a9a3fbd6134fda43136b60ffde99c8f4501e64e0d2b24da137babf", size = 10519523, upload-time = "2026-10-10T20:03:35.163Z" }, + { url = "https://files.pythonhosted.org/packages/99/ba/005cb5edd580d2f84d7ca3206b92dc17d4388e56e6f87ffe8f2762f83139/numpy-2.5.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c668b2f0d651605b58892644b0e302c7157f7159544227758c896982ef384b18", size = 17005499, upload-time = "2026-10-10T20:03:37.961Z" }, + { url = "https://files.pythonhosted.org/packages/f3/49/fee7587c33ee35f7977f9051d7f2023d4e7246d62710c80f20c2361ea232/numpy-2.5.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ffa6ce09a1c6a08e9667dd9c97aa0b14184e8d18f2a14b78b2a2328c9147f076", size = 12019666, upload-time = "2026-10-10T20:03:40.606Z" }, + { url = "https://files.pythonhosted.org/packages/d5/b2/c6ce165acffceb15a82c07b9cc77d391f86b3f379ba62911908ae5d34b91/numpy-2.5.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:956555e0603a4d38019ae6925711cb9dc43195c076a928accf7ea5d50bddfe53", size = 5455617, upload-time = "2026-10-10T20:03:43.138Z" }, + { url = "https://files.pythonhosted.org/packages/77/7f/dd85ce260a669a89be06842cf355d7353a33e6cfbc590fb8ebb947d88dc9/numpy-2.5.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:2c2c4afffdeb7920e445028dd71eb932cac3e704792e964bc2a232426d4f1255", size = 6791932, upload-time = "2026-10-10T20:03:44.874Z" }, + { url = "https://files.pythonhosted.org/packages/63/d6/34b0a2b0741386a63025a65a2c09caaaaaad6d0ca95b66cd65c30dd7fcb5/numpy-2.5.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4054173604cd8658796053f1f3bc0befb68ec1c0762c57fdad61e199256a8617", size = 15710899, upload-time = "2026-10-10T20:03:46.839Z" }, + { url = "https://files.pythonhosted.org/packages/16/d5/928078d2b28f26829b138b4a6c3980045022fb409f570657a224ae60ef4e/numpy-2.5.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d549420b8858885cea8838a727842249218b9c1da24dd517e25c9c7a948310a3", size = 16721710, upload-time = "2026-10-10T20:03:49.489Z" }, + { url = "https://files.pythonhosted.org/packages/f9/cf/673fd1b8f4cd78eb6320e87ec4c90ac19c095644259e3749853a405c70f4/numpy-2.5.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:823874a507a84af050493b622affde94b6f7c3a0dc22cb2801381bc03b871c00", size = 17066182, upload-time = "2026-10-10T20:03:52.25Z" }, + { url = "https://files.pythonhosted.org/packages/f3/92/a77b5061b1b3e2643928c37976d79ee173e1b171ed158b7a3c61056b41bc/numpy-2.5.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4e263278bfb5ee6409db8aedbc4cc32973b1b82bc1e8d3c668551d04d83a7e37", size = 18480315, upload-time = "2026-10-10T20:03:55.39Z" }, + { url = "https://files.pythonhosted.org/packages/bb/1d/1486ef3d3fb2279fd93c4c43c1bbbf1ca389a19816696684409f71babaab/numpy-2.5.4-cp314-cp314-win32.whl", hash = "sha256:cfd73180400042a7c532d30c5e287bdd03c59ff9ee1b4c0316af0539e29dfe23", size = 6185739, upload-time = "2026-10-10T20:03:58.186Z" }, + { url = "https://files.pythonhosted.org/packages/52/9a/e1e512ebc948d5b9dd33b08736760f0ebbed2848fd4eda1f553088a6dcee/numpy-2.5.4-cp314-cp314-win_amd64.whl", hash = "sha256:2ca144f15135b6212a5c47b1e2aeca6e412f102f95a2d5d88d8aec77eb255de3", size = 12703552, upload-time = "2026-10-10T20:04:00.28Z" }, + { url = "https://files.pythonhosted.org/packages/2c/05/de709a982d7bbcd688a3fad71f002e9ff80c2db39e03ee726609b610f1d1/numpy-2.5.4-cp314-cp314-win_arm64.whl", hash = "sha256:468397ba3c64427474706e5c9123fe266395496714dc684294eac75cd4930d1e", size = 10803901, upload-time = "2026-10-10T20:04:02.659Z" }, + { url = "https://files.pythonhosted.org/packages/13/34/083570ada3bb2a30fbe5d77c8c6fef9141144a15d33e6f793a67e9749ab8/numpy-2.5.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1ef3aa6d7e29bb13677323114280b05acc57607fa2300e66432d665d5418a162", size = 12138695, upload-time = "2026-10-10T20:04:05.012Z" }, + { url = "https://files.pythonhosted.org/packages/94/06/1f9c24db48eef0c2d1207e3b11fffb0478e39dfd8c1e1be7476936885eed/numpy-2.5.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:98b053943e5a0474ec0da309d2cb9d3f18ea57f8a2067c2ab7b5f763d1068380", size = 5574615, upload-time = "2026-10-10T20:04:07.316Z" }, + { url = "https://files.pythonhosted.org/packages/da/0f/593fba2e1560e949123bc7d2fc48b5893d56e58cd4bd5a273d2fbf60b220/numpy-2.5.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:b64a85f40e154983960a4167d4c1d57a50c7f109b3d3264a3a984154e90a8454", size = 6889383, upload-time = "2026-10-10T20:04:09.918Z" }, + { url = "https://files.pythonhosted.org/packages/eb/9f/b799dfdce4e05e80ed4bc815c71ff343a11533b2c0ffc221cae8538cda63/numpy-2.5.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a813ed7719bf45463c51779e6a98d0385fe905e48447526938a4b8337333d551", size = 15753763, upload-time = "2026-10-10T20:04:12.278Z" }, + { url = "https://files.pythonhosted.org/packages/34/88/16c5f12f86f5ad2817c4d103205131fc6c8acb3d1878af05a1a4f23ec859/numpy-2.5.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9b80cdf5cedba0e90d93fa5f9a333c4d65bd545cd669b71bb97ce2b703c9d73", size = 16757212, upload-time = "2026-10-10T20:04:14.799Z" }, + { url = "https://files.pythonhosted.org/packages/ff/4f/a1fe40e18a898e6a5089f4f0d891f0a493eb0574d5b34458f0fbe5aa3e5c/numpy-2.5.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2199ed071f460487c8db2c0e5c0b564494190edb4772fe80f9aad88b2604def5", size = 17116471, upload-time = "2026-10-10T20:04:17.58Z" }, + { url = "https://files.pythonhosted.org/packages/aa/46/e923a11c78e65c1722e7aaad817c06bd591324174b9d28ce5d31eee4d432/numpy-2.5.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:64f9c9878c1938476365e11ccfb6b770f3b9e5f045ccddc514235041e6959365", size = 18524063, upload-time = "2026-10-10T20:04:20.365Z" }, + { url = "https://files.pythonhosted.org/packages/5a/fa/84ab064514440c1f64a1b21088f2c82756defdd05e07c75ab233899565b2/numpy-2.5.4-cp314-cp314t-win32.whl", hash = "sha256:64d1c8ac28a4077cf987e0a71a7a0ef7e2df70722f07f0baa42dbb7eb6938647", size = 6340926, upload-time = "2026-10-10T20:04:22.865Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7e/6cd886876f435b10685db9b9f7eeb70356f99e052116f4e5f11c5792c714/numpy-2.5.4-cp314-cp314t-win_amd64.whl", hash = "sha256:067374eb538c34c745436365cf7b0112595c1d326f21ce4ff340f61230239fbb", size = 12901584, upload-time = "2026-10-10T20:04:24.99Z" }, + { url = "https://files.pythonhosted.org/packages/38/1b/3c1684f6a06f7307f2335fca6e486cb162847fb97e91d65f8eb5cabad213/numpy-2.5.4-cp314-cp314t-win_arm64.whl", hash = "sha256:e94aef2c639da4a960ad0db8e06471208d8589974953d78b61d345b4eb99e394", size = 10891152, upload-time = "2026-10-10T20:04:27.52Z" }, + { url = "https://files.pythonhosted.org/packages/08/f4/3224deff3af2bef6bc0b175369698d8cb348f3d91d9bb0286cd5c9eae9e0/numpy-2.5.4-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:8dddfbee2e68d26d0d7d7d9cb247b1fd4409241cce32d815a11d97ec2cfde179", size = 17003231, upload-time = "2026-10-10T20:04:30.021Z" }, + { url = "https://files.pythonhosted.org/packages/be/75/fee0b8c6d94b44b2fdfae74f6a4ad5a138739589a8aebaec28ce4e713ed5/numpy-2.5.4-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:81e3420b27048b65eb14c3acf0c174a8cb0e023277716110347d2dcb26026dad", size = 12018300, upload-time = "2026-10-10T20:04:32.519Z" }, + { url = "https://files.pythonhosted.org/packages/47/c0/d0b335a499a04b65f532c3f034346ef390f81299060f928492dabc1e0272/numpy-2.5.4-cp315-cp315-macosx_14_0_arm64.whl", hash = "sha256:0b4724a19de67bea8cfc4970798efa78bcbbe2ac2613cfac16721a42d44de2a5", size = 5454250, upload-time = "2026-10-10T20:04:34.943Z" }, + { url = "https://files.pythonhosted.org/packages/5a/0e/461b3783c03d668052e6a21b01b673db6ffcb7831fd32d9aa5368c1cd426/numpy-2.5.4-cp315-cp315-macosx_14_0_x86_64.whl", hash = "sha256:2132418bf8dd124a427ca9e6a1daf9ee1a87185344c95119ceae868b99466da1", size = 6789644, upload-time = "2026-10-10T20:04:37.258Z" }, + { url = "https://files.pythonhosted.org/packages/b3/02/5dad269b02166965a7b4ca14adaddd75dbee0de42435bfecf561b84ba5a6/numpy-2.5.4-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:325518d4245b9e331387702aa58c2ce1dc4cdcbb41dfb4ccd5dcbc7e08db1266", size = 15704353, upload-time = "2026-10-10T20:04:39.616Z" }, + { url = "https://files.pythonhosted.org/packages/93/3a/01360c8036822ed9f7aa32189a77d1476567ec1e8e1383522389e4faac45/numpy-2.5.4-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56733449d2544178beaa4545cee357370440cf056c197f9c7bfb19dbfdd0e86d", size = 16718648, upload-time = "2026-10-10T20:04:42.383Z" }, + { url = "https://files.pythonhosted.org/packages/7d/5c/b863a2c093c4d6f21a597fcaf24ead0835c09ab16a8312d5a5a8868af683/numpy-2.5.4-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:5ec3753760c1a6d8bb91200666e545c3a9728e6269dfb5d6ce02340996698aa3", size = 17059053, upload-time = "2026-10-10T20:04:44.976Z" }, + { url = "https://files.pythonhosted.org/packages/0a/60/ced4f57f9a1258a0af74f17cb0b0c2700b5c67cd6678823c803b263e4df3/numpy-2.5.4-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:b1185012870173de7ae33d370bd45b1cf5baee747ea4b97036b65f4e93016877", size = 18477406, upload-time = "2026-10-10T20:04:47.863Z" }, + { url = "https://files.pythonhosted.org/packages/f9/bd/0ef22dafaafcc7d4bb3ca26b8d2afbd55dedad8eaba99a8c864e1997456f/numpy-2.5.4-cp315-cp315-win32.whl", hash = "sha256:298eca75243f2cbbfdb460560b9fb2a1792a33cf2ab4286efd43d92e8d3df508", size = 6185133, upload-time = "2026-10-10T20:04:50.467Z" }, + { url = "https://files.pythonhosted.org/packages/50/bc/d2651b155ecc608a77e6f4d15495c11f14f19bb98f8bf0c5b0d38f86dda1/numpy-2.5.4-cp315-cp315-win_amd64.whl", hash = "sha256:332f3378fe077dd850e677ec01bdcc4f22368fb5d50ef10b2c79230b1bf5a592", size = 12703085, upload-time = "2026-10-10T20:04:52.63Z" }, + { url = "https://files.pythonhosted.org/packages/dc/d2/45e404f8abb26fb9eda12b94012936873e827b1be76f2ee7890be128312e/numpy-2.5.4-cp315-cp315-win_arm64.whl", hash = "sha256:d4cccbbc78717966f764cd3af4fb70276fa01fc7a2688af11c78901fa5c04f05", size = 10801451, upload-time = "2026-10-10T20:04:55.677Z" }, + { url = "https://files.pythonhosted.org/packages/c6/c3/2ae14e09cfdb67dc187a342e15308a21c15bf4d2071f8079e6aee5fe56dc/numpy-2.5.4-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:950ea81d57ef070665581b6e1b5f6a029306423cd1739c5b95fe78aa30db6b9d", size = 17097121, upload-time = "2026-10-10T20:04:58.403Z" }, + { url = "https://files.pythonhosted.org/packages/f5/cf/305ae624ef8a039414317224abe9ec9c2fe7ea3c2e1cf204d43ff6b2ffb9/numpy-2.5.4-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:c05ede731b03fb1b7591faca9389ade3267d2bddf1ad8882bb3f2cc5e101694f", size = 12135439, upload-time = "2026-10-10T20:05:01.65Z" }, + { url = "https://files.pythonhosted.org/packages/a9/a8/f75c63813aef95827bb2c0d13b12803016853056e8792c280058cdbfe783/numpy-2.5.4-cp315-cp315t-macosx_14_0_arm64.whl", hash = "sha256:5fbf7141bbfd63aea22f435c9062a032b9ea0082fe9845dad7f021d3f1234e71", size = 5571451, upload-time = "2026-10-10T20:05:04.135Z" }, + { url = "https://files.pythonhosted.org/packages/6f/0f/f17763f983868b5c49b4101ebd7e00760bd1769478a6bb6a8de6e085bbac/numpy-2.5.4-cp315-cp315t-macosx_14_0_x86_64.whl", hash = "sha256:3573cd22564692a5b899ec344e5d5b9cc4576f2985b96f22af3564ed54f2710f", size = 6883356, upload-time = "2026-10-10T20:05:06.249Z" }, + { url = "https://files.pythonhosted.org/packages/67/a7/8af04c5a79e047996cfa38854dcfbececdd0343a7c933a46fdd03ef6f5da/numpy-2.5.4-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c109eac9cd439193678f69d70733c1108487546ca8eafc107b510ae10c1aecd", size = 15750991, upload-time = "2026-10-10T20:05:08.376Z" }, + { url = "https://files.pythonhosted.org/packages/57/7a/648254290d0c504faa8f2d07aa206660c728802c781a6f3fc68ab7cb5d71/numpy-2.5.4-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:80d6ef6e8620eb2c2b4c4caad50b5935d6db3cde2d51581b55dcc79e14016d1d", size = 16757675, upload-time = "2026-10-10T20:05:11.393Z" }, + { url = "https://files.pythonhosted.org/packages/b8/fe/4a8c3cdb0c70400cfe4c5bec42d3099a5673802a95064614b33e07b82aa1/numpy-2.5.4-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:77045a4b175bbf5316ec08003880804336c78f92281a1b72222b274ea85ec5ac", size = 17113846, upload-time = "2026-10-10T20:05:14.49Z" }, + { url = "https://files.pythonhosted.org/packages/1b/7e/619692bb67778702c0e9eb2d468568a7573f4e269386ea61aed01ee4e557/numpy-2.5.4-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:0f02a46e49cfb6c73bdb7aea1c0d3461dbae9aba613542b65f657cd3d17b9fab", size = 18522915, upload-time = "2026-10-10T20:05:17.33Z" }, + { url = "https://files.pythonhosted.org/packages/b7/b5/4da41c328788f575838f97a098fe8ca691ebc6f6fd73ad4a262ee40b184d/numpy-2.5.4-cp315-cp315t-win32.whl", hash = "sha256:ad62a416ddcf863bf44bba76fbf6b53366ab0692e294f51cae4b5fbe0d246788", size = 6335804, upload-time = "2026-10-10T20:05:19.921Z" }, + { url = "https://files.pythonhosted.org/packages/98/94/6482ddfa3d312490cb9358f375bf2ad56427dbea8769187158e94d653753/numpy-2.5.4-cp315-cp315t-win_amd64.whl", hash = "sha256:38f47be9f74ab870d2633b5456ae519c43758a8d1fd05342f0ce4ecc034396ee", size = 12890095, upload-time = "2026-10-10T20:05:21.875Z" }, + { url = "https://files.pythonhosted.org/packages/48/7f/c2d1b436b6e7cfebac140c2579a298344b85f2991a2ce5c3615cefb29400/numpy-2.5.4-cp315-cp315t-win_arm64.whl", hash = "sha256:7a14a461d9340f1b46b8648578aed9cdb8b3b018a8fac6c1dde2c9192a01a87f", size = 10883718, upload-time = "2026-10-10T20:05:28.547Z" }, +] + +[[package]] +name = "packaging" +version = "26.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, +] + +[[package]] +name = "pluggy" +version = "1.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/db/7fc19e6f2dc92a966727031389fc2e08b558f0f25eb7403c1119ad4713cd/pluggy-1.7.0.tar.gz", hash = "sha256:d1eaa46ebb595891b860ab086b4d09c8588af65ebd4361b8e8f4bb8920b90ba8", size = 123304, upload-time = "2026-10-15T09:50:58.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/9e/2b38731e0fc536806f16490e1a12d7f0dc2a1235aa8cc07bcc75416a7daa/pluggy-1.7.0-py3-none-any.whl", hash = "sha256:7dd7b0d8832ba3cb632c306926ded123429211b83641b35dc5c41ad2d34f9bec", size = 27082, upload-time = "2026-10-15T09:50:56.808Z" }, +] + +[[package]] +name = "pyaml" +version = "26.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/15/6a/acfdf17de0d6947b419da8696e02b781b18de2cf49e0472298b50e1f0711/pyaml-26.7.0.tar.gz", hash = "sha256:11cda3a796efc6dbce0d56836be56cfd26289dad07bcd78e9904086729929c93", size = 30669, upload-time = "2026-07-04T00:34:38.532Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/26/3f05f9e3692853dff663913d5d62fb8df3f5640c8d70b06588c0b51ed953/pyaml-26.7.0-py3-none-any.whl", hash = "sha256:cfa382780c43ae660669b87d394d550a41856ef175f5749f51f753e12d7077ac", size = 27226, upload-time = "2026-07-04T00:34:37.198Z" }, +] + +[[package]] +name = "pygments" +version = "2.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" }, +] + +[[package]] +name = "pytest" +version = "9.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/42/bb4641652e58dedeac2ec51abc1f8cae4fee4ec63899a0999016229a0f2a/pytest-9.2.0.tar.gz", hash = "sha256:97c4f3b1f3c7414aa36465e09b8220f1d03627993bd51b1282e193753c80557b", size = 1797555, upload-time = "2026-10-27T21:30:20.728Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/39/1f5d8c8877a0fa327db959c19341d4be61660de1ab6b7391f9eb805bcd67/pytest-9.2.0-py3-none-any.whl", hash = "sha256:07acdcf88ec174f907dc01af70bf52d49b014bbab03faeeade31c96b18c2e588", size = 411776, upload-time = "2026-10-27T21:30:19.131Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "ruff" +version = "0.17.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/59/73/81f140c4edd9cc323de5478e6bbd3bbcfae9a82692df1a258de8cec6a6d2/ruff-0.17.3.tar.gz", hash = "sha256:a57049c8addb3662073859bb55a7ad35aa54f8a7c9ed268911d1333c5aadaaa9", size = 5339444, upload-time = "2026-10-27T00:34:05.914Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/8c/083365310c4e79c842f25bf5a842facbd9b0c0ec336f96dffa5074fe723d/ruff-0.17.3-py3-none-linux_armv6l.whl", hash = "sha256:ab86b84cd9d05e56d329742c0cabdcbe2d1d56c98f4a04aab9b5b66196febfb4", size = 10216522, upload-time = "2026-10-27T00:33:29.822Z" }, + { url = "https://files.pythonhosted.org/packages/72/26/4b2edf31cfebe9605d8f6ed8f1d6f376d3a53d4c9a123e62661c64c440f0/ruff-0.17.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e407b4a6852853a9a2cdce96963e94ad9775578bb44e7561a0f2829f3cc04b11", size = 10217786, upload-time = "2026-10-27T00:33:33.949Z" }, + { url = "https://files.pythonhosted.org/packages/a2/7e/537aba4b7f54397a374d2f6a6fc93e9c8015e18553f31a4a7b7dbced48b5/ruff-0.17.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b75cc113b3aca388933d8296b667b1ac401a80317ea3074c9b0a66541199da29", size = 9440362, upload-time = "2026-10-27T00:33:35.784Z" }, + { url = "https://files.pythonhosted.org/packages/3d/66/283e39734f66c8a5ebaa93c22eb7b7515779a0b1d5a5c0ad20e05c0edb41/ruff-0.17.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab5fa7ed5ce18554ea69d850ad162cbaa0c85fc47c08d83a569f806681c9a6ad", size = 10094281, upload-time = "2026-10-27T00:33:38.216Z" }, + { url = "https://files.pythonhosted.org/packages/21/4c/1ffc768497bd025a32c72152771e40c0b99bddc5cc6e4f50c7949a395346/ruff-0.17.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c673c170e27834d1262e3a9c1e97bdc9b9c3b180c63d89173ce69747232474c1", size = 10022543, upload-time = "2026-10-27T00:33:40.181Z" }, + { url = "https://files.pythonhosted.org/packages/73/c3/f90db547301080fcb82c92abf914c8bd57b89911d9b65a6c4cc6b52d0635/ruff-0.17.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a754c0341bc9ca151b7b335988b3263921c08859f84ca219b70cee825571b6f", size = 10683921, upload-time = "2026-10-27T00:33:42.182Z" }, + { url = "https://files.pythonhosted.org/packages/e5/98/92d4a8a26c6c96cf1a5b34dd59036d8a71e52d650aab065c58867e964e63/ruff-0.17.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4590b9d86c0b1543ebb09094254e191cd43fe8edaf330baa035aab03da251532", size = 11373408, upload-time = "2026-10-27T00:33:44.19Z" }, + { url = "https://files.pythonhosted.org/packages/43/02/1f24d2e3064c9753926deeb901afa57777b37ee46799b09ddd7aee6e5145/ruff-0.17.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bbde18beccd98c29a227c63c99022e3443b99258e08326df6425dfe0443705ef", size = 10977469, upload-time = "2026-10-27T00:33:46.179Z" }, + { url = "https://files.pythonhosted.org/packages/f0/20/fecedea2209f574ab92033b344e01a7f0c2dc76c6d6d19a14c9f8141173f/ruff-0.17.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fa4b0f931cdbcb8a52044f370a3eb429fb42eb88807b0e800fcaa98dd42b24e", size = 10577491, upload-time = "2026-10-27T00:33:48.233Z" }, + { url = "https://files.pythonhosted.org/packages/33/36/b93fdf0bc6f84f0cd487aadfb0354deb40013669ea35ac3d26855de1e959/ruff-0.17.3-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:39405f341deec934b4289634324ad695f72df2970f9121582872eaf5b722f0f5", size = 10608748, upload-time = "2026-10-27T00:33:50.284Z" }, + { url = "https://files.pythonhosted.org/packages/5d/b7/7d3f5f14145daead1efb0b9a8289ed1daa100f419d60774375951723ce00/ruff-0.17.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:68074094f55ca3d29ab19491f640282f7a84061c4c7977aa5094b968ca457e9a", size = 10075057, upload-time = "2026-10-27T00:33:52.074Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ed/93ff7a5909ee56e2bd4b982deae9e0efb2724417874a90c9e41ad66c6dd7/ruff-0.17.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:27bb571ac78fef178fe5118d2827df95c2aae74a48fff2b15f6acd03af5ff5bb", size = 10022431, upload-time = "2026-10-27T00:33:54.061Z" }, + { url = "https://files.pythonhosted.org/packages/9b/07/a83b9eeec8c1c3c0e4eda15f06c3a91001bfd2687ea2f07b750719bf9712/ruff-0.17.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:029c37cbd3de48ca37aa12117bead2d065f15da17d66bf84e0d4274013705db2", size = 10394903, upload-time = "2026-10-27T00:33:56.158Z" }, + { url = "https://files.pythonhosted.org/packages/af/b2/05c39857c2526fcbebe46ad93a8dc97424656dfef14d3ef933abe1ffd331/ruff-0.17.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:692fdb46d0fd9c71e380947dc5fc72f2c3f881073bc1f739e4579376231affbe", size = 10799780, upload-time = "2026-10-27T00:33:58.204Z" }, + { url = "https://files.pythonhosted.org/packages/ec/d9/47171a16739eb7a353936b31c20cf54340531e68c9af7d72fd7a784e235d/ruff-0.17.3-py3-none-win32.whl", hash = "sha256:46539cb031b01fd29ae0a02142483c46640b233ee12bb1c1b690bffa1555d5ca", size = 9908481, upload-time = "2026-10-27T00:34:00.087Z" }, + { url = "https://files.pythonhosted.org/packages/d0/a0/f08c1631bfd81faa6a90eaf70c37c786c11bed2046302b56bb506c25e1a0/ruff-0.17.3-py3-none-win_amd64.whl", hash = "sha256:56b8ffb3f3b3cbccb8b76b71b38261b7ed982ef5d20f5b447b1b61a7f4949b24", size = 10341333, upload-time = "2026-10-27T00:34:02.099Z" }, + { url = "https://files.pythonhosted.org/packages/99/d5/4ad4ed31413c301430918e70d3d38ae6f097af0766cb9ab8c9d502b2a4d6/ruff-0.17.3-py3-none-win_arm64.whl", hash = "sha256:81fce74bcd6be0e9da7c67c7553fd0f1f12964a2052cd728ef917b937a5ca1d9", size = 9854332, upload-time = "2026-10-27T00:34:03.847Z" }, +] + +[[package]] +name = "scikit-learn" +version = "1.7.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "joblib" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" } }, + { name = "threadpoolctl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/3e/daed796fd69cce768b8788401cc464ea90b306fb196ae1ffed0b98182859/scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f", size = 9336221, upload-time = "2025-09-09T08:20:19.328Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ce/af9d99533b24c55ff4e18d9b7b4d9919bbc6cd8f22fe7a7be01519a347d5/scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c", size = 8653834, upload-time = "2025-09-09T08:20:22.073Z" }, + { url = "https://files.pythonhosted.org/packages/58/0e/8c2a03d518fb6bd0b6b0d4b114c63d5f1db01ff0f9925d8eb10960d01c01/scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8", size = 9660938, upload-time = "2025-09-09T08:20:24.327Z" }, + { url = "https://files.pythonhosted.org/packages/2b/75/4311605069b5d220e7cf5adabb38535bd96f0079313cdbb04b291479b22a/scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18", size = 9477818, upload-time = "2025-09-09T08:20:26.845Z" }, + { url = "https://files.pythonhosted.org/packages/7f/9b/87961813c34adbca21a6b3f6b2bea344c43b30217a6d24cc437c6147f3e8/scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5", size = 8886969, upload-time = "2025-09-09T08:20:29.329Z" }, + { url = "https://files.pythonhosted.org/packages/43/83/564e141eef908a5863a54da8ca342a137f45a0bfb71d1d79704c9894c9d1/scikit_learn-1.7.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7509693451651cd7361d30ce4e86a1347493554f172b1c72a39300fa2aea79e", size = 9331967, upload-time = "2025-09-09T08:20:32.421Z" }, + { url = "https://files.pythonhosted.org/packages/18/d6/ba863a4171ac9d7314c4d3fc251f015704a2caeee41ced89f321c049ed83/scikit_learn-1.7.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:0486c8f827c2e7b64837c731c8feff72c0bd2b998067a8a9cbc10643c31f0fe1", size = 8648645, upload-time = "2025-09-09T08:20:34.436Z" }, + { url = "https://files.pythonhosted.org/packages/ef/0e/97dbca66347b8cf0ea8b529e6bb9367e337ba2e8be0ef5c1a545232abfde/scikit_learn-1.7.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89877e19a80c7b11a2891a27c21c4894fb18e2c2e077815bcade10d34287b20d", size = 9715424, upload-time = "2025-09-09T08:20:36.776Z" }, + { url = "https://files.pythonhosted.org/packages/f7/32/1f3b22e3207e1d2c883a7e09abb956362e7d1bd2f14458c7de258a26ac15/scikit_learn-1.7.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8da8bf89d4d79aaec192d2bda62f9b56ae4e5b4ef93b6a56b5de4977e375c1f1", size = 9509234, upload-time = "2025-09-09T08:20:38.957Z" }, + { url = "https://files.pythonhosted.org/packages/9f/71/34ddbd21f1da67c7a768146968b4d0220ee6831e4bcbad3e03dd3eae88b6/scikit_learn-1.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:9b7ed8d58725030568523e937c43e56bc01cadb478fc43c042a9aca1dacb3ba1", size = 8894244, upload-time = "2025-09-09T08:20:41.166Z" }, + { url = "https://files.pythonhosted.org/packages/a7/aa/3996e2196075689afb9fce0410ebdb4a09099d7964d061d7213700204409/scikit_learn-1.7.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8d91a97fa2b706943822398ab943cde71858a50245e31bc71dba62aab1d60a96", size = 9259818, upload-time = "2025-09-09T08:20:43.19Z" }, + { url = "https://files.pythonhosted.org/packages/43/5d/779320063e88af9c4a7c2cf463ff11c21ac9c8bd730c4a294b0000b666c9/scikit_learn-1.7.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:acbc0f5fd2edd3432a22c69bed78e837c70cf896cd7993d71d51ba6708507476", size = 8636997, upload-time = "2025-09-09T08:20:45.468Z" }, + { url = "https://files.pythonhosted.org/packages/5c/d0/0c577d9325b05594fdd33aa970bf53fb673f051a45496842caee13cfd7fe/scikit_learn-1.7.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e5bf3d930aee75a65478df91ac1225ff89cd28e9ac7bd1196853a9229b6adb0b", size = 9478381, upload-time = "2025-09-09T08:20:47.982Z" }, + { url = "https://files.pythonhosted.org/packages/82/70/8bf44b933837ba8494ca0fc9a9ab60f1c13b062ad0197f60a56e2fc4c43e/scikit_learn-1.7.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4d6e9deed1a47aca9fe2f267ab8e8fe82ee20b4526b2c0cd9e135cea10feb44", size = 9300296, upload-time = "2025-09-09T08:20:50.366Z" }, + { url = "https://files.pythonhosted.org/packages/c6/99/ed35197a158f1fdc2fe7c3680e9c70d0128f662e1fee4ed495f4b5e13db0/scikit_learn-1.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:6088aa475f0785e01bcf8529f55280a3d7d298679f50c0bb70a2364a82d0b290", size = 8731256, upload-time = "2025-09-09T08:20:52.627Z" }, + { url = "https://files.pythonhosted.org/packages/ae/93/a3038cb0293037fd335f77f31fe053b89c72f17b1c8908c576c29d953e84/scikit_learn-1.7.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b7dacaa05e5d76759fb071558a8b5130f4845166d88654a0f9bdf3eb57851b7", size = 9212382, upload-time = "2025-09-09T08:20:54.731Z" }, + { url = "https://files.pythonhosted.org/packages/40/dd/9a88879b0c1104259136146e4742026b52df8540c39fec21a6383f8292c7/scikit_learn-1.7.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:abebbd61ad9e1deed54cca45caea8ad5f79e1b93173dece40bb8e0c658dbe6fe", size = 8592042, upload-time = "2025-09-09T08:20:57.313Z" }, + { url = "https://files.pythonhosted.org/packages/46/af/c5e286471b7d10871b811b72ae794ac5fe2989c0a2df07f0ec723030f5f5/scikit_learn-1.7.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:502c18e39849c0ea1a5d681af1dbcf15f6cce601aebb657aabbfe84133c1907f", size = 9434180, upload-time = "2025-09-09T08:20:59.671Z" }, + { url = "https://files.pythonhosted.org/packages/f1/fd/df59faa53312d585023b2da27e866524ffb8faf87a68516c23896c718320/scikit_learn-1.7.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a4c328a71785382fe3fe676a9ecf2c86189249beff90bf85e22bdb7efaf9ae0", size = 9283660, upload-time = "2025-09-09T08:21:01.71Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c7/03000262759d7b6f38c836ff9d512f438a70d8a8ddae68ee80de72dcfb63/scikit_learn-1.7.2-cp313-cp313-win_amd64.whl", hash = "sha256:63a9afd6f7b229aad94618c01c252ce9e6fa97918c5ca19c9a17a087d819440c", size = 8702057, upload-time = "2025-09-09T08:21:04.234Z" }, + { url = "https://files.pythonhosted.org/packages/55/87/ef5eb1f267084532c8e4aef98a28b6ffe7425acbfd64b5e2f2e066bc29b3/scikit_learn-1.7.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9acb6c5e867447b4e1390930e3944a005e2cb115922e693c08a323421a6966e8", size = 9558731, upload-time = "2025-09-09T08:21:06.381Z" }, + { url = "https://files.pythonhosted.org/packages/93/f8/6c1e3fc14b10118068d7938878a9f3f4e6d7b74a8ddb1e5bed65159ccda8/scikit_learn-1.7.2-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:2a41e2a0ef45063e654152ec9d8bcfc39f7afce35b08902bfe290c2498a67a6a", size = 9038852, upload-time = "2025-09-09T08:21:08.628Z" }, + { url = "https://files.pythonhosted.org/packages/83/87/066cafc896ee540c34becf95d30375fe5cbe93c3b75a0ee9aa852cd60021/scikit_learn-1.7.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98335fb98509b73385b3ab2bd0639b1f610541d3988ee675c670371d6a87aa7c", size = 9527094, upload-time = "2025-09-09T08:21:11.486Z" }, + { url = "https://files.pythonhosted.org/packages/9c/2b/4903e1ccafa1f6453b1ab78413938c8800633988c838aa0be386cbb33072/scikit_learn-1.7.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:191e5550980d45449126e23ed1d5e9e24b2c68329ee1f691a3987476e115e09c", size = 9367436, upload-time = "2025-09-09T08:21:13.602Z" }, + { url = "https://files.pythonhosted.org/packages/b5/aa/8444be3cfb10451617ff9d177b3c190288f4563e6c50ff02728be67ad094/scikit_learn-1.7.2-cp313-cp313t-win_amd64.whl", hash = "sha256:57dc4deb1d3762c75d685507fbd0bc17160144b2f2ba4ccea5dc285ab0d0e973", size = 9275749, upload-time = "2025-09-09T08:21:15.96Z" }, + { url = "https://files.pythonhosted.org/packages/d9/82/dee5acf66837852e8e68df6d8d3a6cb22d3df997b733b032f513d95205b7/scikit_learn-1.7.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fa8f63940e29c82d1e67a45d5297bdebbcb585f5a5a50c4914cc2e852ab77f33", size = 9208906, upload-time = "2025-09-09T08:21:18.557Z" }, + { url = "https://files.pythonhosted.org/packages/3c/30/9029e54e17b87cb7d50d51a5926429c683d5b4c1732f0507a6c3bed9bf65/scikit_learn-1.7.2-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:f95dc55b7902b91331fa4e5845dd5bde0580c9cd9612b1b2791b7e80c3d32615", size = 8627836, upload-time = "2025-09-09T08:21:20.695Z" }, + { url = "https://files.pythonhosted.org/packages/60/18/4a52c635c71b536879f4b971c2cedf32c35ee78f48367885ed8025d1f7ee/scikit_learn-1.7.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9656e4a53e54578ad10a434dc1f993330568cfee176dff07112b8785fb413106", size = 9426236, upload-time = "2025-09-09T08:21:22.645Z" }, + { url = "https://files.pythonhosted.org/packages/99/7e/290362f6ab582128c53445458a5befd471ed1ea37953d5bcf80604619250/scikit_learn-1.7.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96dc05a854add0e50d3f47a1ef21a10a595016da5b007c7d9cd9d0bffd1fcc61", size = 9312593, upload-time = "2025-09-09T08:21:24.65Z" }, + { url = "https://files.pythonhosted.org/packages/8e/87/24f541b6d62b1794939ae6422f8023703bbf6900378b2b34e0b4384dfefd/scikit_learn-1.7.2-cp314-cp314-win_amd64.whl", hash = "sha256:bb24510ed3f9f61476181e4db51ce801e2ba37541def12dc9333b946fc7a9cf8", size = 8820007, upload-time = "2025-09-09T08:21:26.713Z" }, +] + +[[package]] +name = "scikit-learn" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", +] +dependencies = [ + { name = "joblib" }, + { name = "narwhals" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "scipy", version = "1.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "threadpoolctl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fa/6f/37092bdb25f712817231799fc5674d8e704066a8a70c1d2d40517e18b4ab/scikit_learn-1.9.0.tar.gz", hash = "sha256:8833266989d3a5110178a9fae30783675460724d0e1efb13b14901d2c660c557", size = 7750767, upload-time = "2026-06-02T11:54:32.706Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/be/e844fd9586e66540a15b71924d17a6cbc1bb749e81ddd0a796bcdba4c055/scikit_learn-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9db6f4d34e68c8899e4cab27fdf8eafe6ed21f2ba52ceb25ea250cd237f8e47b", size = 8789686, upload-time = "2026-06-02T11:53:05.439Z" }, + { url = "https://files.pythonhosted.org/packages/42/e2/ff880f62677a17d035817d543cb0fc8727d01eccbee81c5f7fc733a9d856/scikit_learn-1.9.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f401448645a3e7bc115aa3c094097865155b34bff1cba8101857d9104e99074c", size = 8256782, upload-time = "2026-06-02T11:53:08.904Z" }, + { url = "https://files.pythonhosted.org/packages/25/64/eb40435e1a508ab1b4e284ce43ae80f6a162e5be5e38ed5a6fab467a9ea4/scikit_learn-1.9.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fd3a8ef0c758555a3b23c03adaa858af32f7736785ded50ad5991f59c4ed03fa", size = 8992419, upload-time = "2026-06-02T11:53:11.551Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/4810a28e473185429e45a57eebcc91fc991b33d889cc0676063e671db03d/scikit_learn-1.9.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7e254636164090da847715a27f8e5478feb98c40a9e0ee90cbd277de9e5ceb8", size = 9281411, upload-time = "2026-06-02T11:53:15.063Z" }, + { url = "https://files.pythonhosted.org/packages/3b/67/be3d369f40d8178ba3bd86635d132e08cb5329b023e4669d9426d84bc007/scikit_learn-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:5dc1818c77575d149e25fce9ef82dd7b7263ae372f03494158668ad632a69759", size = 8272736, upload-time = "2026-06-02T11:53:18.108Z" }, + { url = "https://files.pythonhosted.org/packages/37/79/a733f02dc2118da7e77a134b34f39f40201a353311b011d20859d2db3556/scikit_learn-1.9.0-cp311-cp311-win_arm64.whl", hash = "sha256:366652351f092b219c248f1e72821e841960a63d8f358f1dcfd54dc1cbdbbc28", size = 7919564, upload-time = "2026-06-02T11:53:21.2Z" }, + { url = "https://files.pythonhosted.org/packages/ac/20/75f915ff375d6249e6550ac740fdbbd66159a068fd3af1400ff62036b07a/scikit_learn-1.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2bd41b0d201bc81575531b96b713d3eb5e5f50fb0b82101ff0f92294fdc236ac", size = 8741122, upload-time = "2026-06-02T11:53:24.08Z" }, + { url = "https://files.pythonhosted.org/packages/cc/d5/2b5148f2279196775e1db2aeb85d14b70ac80e7e32b3b28e7ebeafb0901d/scikit_learn-1.9.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5be45aa4a42a68a533913a6ed736cf309de2226411c79ef8d609a5456f1939b1", size = 8261512, upload-time = "2026-06-02T11:53:27.183Z" }, + { url = "https://files.pythonhosted.org/packages/a0/ee/5adbc77656b71f9456a2f5a7a9fdb4bcf9207a6b962889f1c2f9323afa4e/scikit_learn-1.9.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e50ed4da51974e86e940690e9a3d82e729b62b5a49f7c9bac534d515d39d86f", size = 8837603, upload-time = "2026-06-02T11:53:30.328Z" }, + { url = "https://files.pythonhosted.org/packages/6c/c2/63fdda36c56437eeb44aaf9493c8bcd62ce230ab1598924fc626ffbfa943/scikit_learn-1.9.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:056c92bb67ad4c28463c2f2653d9701449201e7e7a9e94e321be0f71c4fef2b8", size = 9132097, upload-time = "2026-06-02T11:53:33.456Z" }, + { url = "https://files.pythonhosted.org/packages/83/a4/c8e67227c680e2259c8864ae72ff48b06e16a6f51253a22167aa02a8aa4e/scikit_learn-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:4306775fad04cc4b472a1b15af1ae9cede1540fbfcc17fbce3767cd8dc7ae283", size = 8211173, upload-time = "2026-06-02T11:53:36.602Z" }, + { url = "https://files.pythonhosted.org/packages/cf/fd/3c0863792e98e67e9184aa4029288a175935eb65443afcd30d4f143450cf/scikit_learn-1.9.0-cp312-cp312-win_arm64.whl", hash = "sha256:26e22435f63bcdcf396b574273f29f13dd531f5ea035801f5be10ba1540a4e60", size = 7867451, upload-time = "2026-06-02T11:53:39.075Z" }, + { url = "https://files.pythonhosted.org/packages/3c/01/cf3310626b6d48d3e9be69a1223f9180360b5e6edb045f50fade723ce494/scikit_learn-1.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:80746d63bd4b6eaca54d36fe5feaf4d28bb38dc6f9470f81c7cad7c40155f119", size = 8705188, upload-time = "2026-06-02T11:53:41.964Z" }, + { url = "https://files.pythonhosted.org/packages/3e/04/5acd7ae280c5f93b6ac5ef6cdec14eef4c8d1cd91d85b3292989c94d96b1/scikit_learn-1.9.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:5b934c45c252844a91d69fda3a34cff5e7307e1db10d77cb10a3980312c74713", size = 8228299, upload-time = "2026-06-02T11:53:44.817Z" }, + { url = "https://files.pythonhosted.org/packages/0c/39/ffe829a5b8ecb40a518724a997794657fdc354ada5e8fe8e64d998c0bac9/scikit_learn-1.9.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:38c3dcb9a1ffb85505ec53d54c7b4aea0cff70050425a7760c2af661ac85df05", size = 8789690, upload-time = "2026-06-02T11:53:47.461Z" }, + { url = "https://files.pythonhosted.org/packages/1f/88/8dab5de10c638c083772a6be83a3d8106ced492f74a928c8693638e5bb50/scikit_learn-1.9.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da76d09304a4706db7cc1e3ebaa3b6b98a67365cc11d2996c4f1e58ba47df714", size = 9087723, upload-time = "2026-06-02T11:53:50.702Z" }, + { url = "https://files.pythonhosted.org/packages/20/3f/7917ca72464038f6240ec70c29f94862d08a34a74291ae4d4ec5eb8186a0/scikit_learn-1.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:5808d98f15c6bf6d9d96d2348c1997392a5888ce7097e664105f930c4bca1277", size = 8184330, upload-time = "2026-06-02T11:53:53.396Z" }, + { url = "https://files.pythonhosted.org/packages/78/c7/15739eb2f61fda3c54639e9942414e5a19ad8a8d1f5a3266afad7cb7df80/scikit_learn-1.9.0-cp313-cp313-win_arm64.whl", hash = "sha256:d77f54c017633791bc0225a43e2f8d03745fdcfe4880268fcc4df15f505dec2e", size = 7840653, upload-time = "2026-06-02T11:53:56.035Z" }, + { url = "https://files.pythonhosted.org/packages/f4/7d/c9a35cf59b20a86fec24d306f1547b78dec194b08d367ce2a3e4854169d9/scikit_learn-1.9.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9656acd4e93f74e0b66c8a36c88830a99252dfa900044d36bc2212ae89a47162", size = 8713289, upload-time = "2026-06-02T11:53:58.788Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a7/552a7821597c632b907f7bfe8f36f9f572777af8ef8a48353041cf8e091a/scikit_learn-1.9.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:24360002ae845e7866522b0a5bbf690802e7bc388cac8663502e78aa98598aa2", size = 8245141, upload-time = "2026-06-02T11:54:01.694Z" }, + { url = "https://files.pythonhosted.org/packages/7d/79/f4a0c4fe9711154cddabf913471153af79056382ddc612cfe5ee0ff4b72e/scikit_learn-1.9.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5162ad10a418c8a282dde04c9aa06965de3e9a65f33c1440c0ae69bb1a09d913", size = 8847671, upload-time = "2026-06-02T11:54:04.448Z" }, + { url = "https://files.pythonhosted.org/packages/f0/af/4d72d9e475ac83719160c662619e4bf7b95c19507cd582e7d0167a3c3dae/scikit_learn-1.9.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fea2cc5677ab49d6f5bade978c866da44957b712d92e9635e8b4f723013c3cb", size = 9118104, upload-time = "2026-06-02T11:54:07.205Z" }, + { url = "https://files.pythonhosted.org/packages/a2/d5/6a58eea2cb9abbb9b3f2bb8b2cfb3243d1152d69f442d256c7af71304769/scikit_learn-1.9.0-cp314-cp314-win_amd64.whl", hash = "sha256:64fa347efc1c839c487433e40c5144d38c336e8a2b59c81aa8660373945c2673", size = 8290674, upload-time = "2026-06-02T11:54:10.087Z" }, + { url = "https://files.pythonhosted.org/packages/65/5b/d4c879cf358f1187141cf90ced473f087183489090244f50c124a2ee478b/scikit_learn-1.9.0-cp314-cp314-win_arm64.whl", hash = "sha256:1b944b6db288f6b926e3650026ddafb988929de95d11fc2cc5fa117773c9ba42", size = 7978807, upload-time = "2026-06-02T11:54:12.769Z" }, + { url = "https://files.pythonhosted.org/packages/8a/43/bfae3121ec67ae09150d453c442c7c1cc166e9aefe056e6ab3b7728a5cfc/scikit_learn-1.9.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:4ccacf04ca5f4b492158a5f28afe0ace43f81b2571e4b9a66d34848b46128949", size = 9031941, upload-time = "2026-06-02T11:54:15.436Z" }, + { url = "https://files.pythonhosted.org/packages/75/b0/20a4546eb17f3b25d3c66df15810411c14ed5065bcfab50b53c96fb627b2/scikit_learn-1.9.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:ee1a8db2c18c08e34c7412d4b10be1cac214cd4ea7dc9715a6a327eb49a37c96", size = 8613528, upload-time = "2026-06-02T11:54:18.842Z" }, + { url = "https://files.pythonhosted.org/packages/18/3c/e440e039bb82cd19004edaaad00acbde0fb9b461083c3ecf37941c557312/scikit_learn-1.9.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:147e9329ef0e39f75d4cffa02b2aa48d827832684926cd5210d9a2cb5c57246b", size = 8855050, upload-time = "2026-06-02T11:54:21.699Z" }, + { url = "https://files.pythonhosted.org/packages/43/26/b341b8dab5998da6270a3a42c2152c578501354d36f944b5856757035ef8/scikit_learn-1.9.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bad8f8b9950321b54c965fdcbac6c6c55e79e16646b49977bcf3668d3870a1a", size = 9097190, upload-time = "2026-06-02T11:54:24.454Z" }, + { url = "https://files.pythonhosted.org/packages/fb/de/b650b4d69b84468cfa2e28a3ff7b8103743029e6446ce1a97fe060ef688c/scikit_learn-1.9.0-cp314-cp314t-win_amd64.whl", hash = "sha256:78fc56eafd4edb9575d2d8950d1dd152061abb573341a1cb7e099fc40f6c6666", size = 8963204, upload-time = "2026-06-02T11:54:27.428Z" }, + { url = "https://files.pythonhosted.org/packages/ee/f3/ff83d76d7418112e5a61326443cdda87be3545dd8d6599c95b2481a4419e/scikit_learn-1.9.0-cp314-cp314t-win_arm64.whl", hash = "sha256:051075bda8b7aab87b1906ab3d4740a1e1224a19d7b3781a576736edc94e76aa", size = 8222661, upload-time = "2026-06-02T11:54:30.192Z" }, +] + +[[package]] +name = "scipy" +version = "1.15.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/2f/4966032c5f8cc7e6a60f1b2e0ad686293b9474b65246b0c642e3ef3badd0/scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c", size = 38702770, upload-time = "2025-05-08T16:04:20.849Z" }, + { url = "https://files.pythonhosted.org/packages/a0/6e/0c3bf90fae0e910c274db43304ebe25a6b391327f3f10b5dcc638c090795/scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253", size = 30094511, upload-time = "2025-05-08T16:04:27.103Z" }, + { url = "https://files.pythonhosted.org/packages/ea/b1/4deb37252311c1acff7f101f6453f0440794f51b6eacb1aad4459a134081/scipy-1.15.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f", size = 22368151, upload-time = "2025-05-08T16:04:31.731Z" }, + { url = "https://files.pythonhosted.org/packages/38/7d/f457626e3cd3c29b3a49ca115a304cebb8cc6f31b04678f03b216899d3c6/scipy-1.15.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92", size = 25121732, upload-time = "2025-05-08T16:04:36.596Z" }, + { url = "https://files.pythonhosted.org/packages/db/0a/92b1de4a7adc7a15dcf5bddc6e191f6f29ee663b30511ce20467ef9b82e4/scipy-1.15.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82", size = 35547617, upload-time = "2025-05-08T16:04:43.546Z" }, + { url = "https://files.pythonhosted.org/packages/8e/6d/41991e503e51fc1134502694c5fa7a1671501a17ffa12716a4a9151af3df/scipy-1.15.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40", size = 37662964, upload-time = "2025-05-08T16:04:49.431Z" }, + { url = "https://files.pythonhosted.org/packages/25/e1/3df8f83cb15f3500478c889be8fb18700813b95e9e087328230b98d547ff/scipy-1.15.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e", size = 37238749, upload-time = "2025-05-08T16:04:55.215Z" }, + { url = "https://files.pythonhosted.org/packages/93/3e/b3257cf446f2a3533ed7809757039016b74cd6f38271de91682aa844cfc5/scipy-1.15.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c", size = 40022383, upload-time = "2025-05-08T16:05:01.914Z" }, + { url = "https://files.pythonhosted.org/packages/d1/84/55bc4881973d3f79b479a5a2e2df61c8c9a04fcb986a213ac9c02cfb659b/scipy-1.15.3-cp310-cp310-win_amd64.whl", hash = "sha256:9d61e97b186a57350f6d6fd72640f9e99d5a4a2b8fbf4b9ee9a841eab327dc13", size = 41259201, upload-time = "2025-05-08T16:05:08.166Z" }, + { url = "https://files.pythonhosted.org/packages/96/ab/5cc9f80f28f6a7dff646c5756e559823614a42b1939d86dd0ed550470210/scipy-1.15.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b", size = 38714255, upload-time = "2025-05-08T16:05:14.596Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4a/66ba30abe5ad1a3ad15bfb0b59d22174012e8056ff448cb1644deccbfed2/scipy-1.15.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba", size = 30111035, upload-time = "2025-05-08T16:05:20.152Z" }, + { url = "https://files.pythonhosted.org/packages/4b/fa/a7e5b95afd80d24313307f03624acc65801846fa75599034f8ceb9e2cbf6/scipy-1.15.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65", size = 22384499, upload-time = "2025-05-08T16:05:24.494Z" }, + { url = "https://files.pythonhosted.org/packages/17/99/f3aaddccf3588bb4aea70ba35328c204cadd89517a1612ecfda5b2dd9d7a/scipy-1.15.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1", size = 25152602, upload-time = "2025-05-08T16:05:29.313Z" }, + { url = "https://files.pythonhosted.org/packages/56/c5/1032cdb565f146109212153339f9cb8b993701e9fe56b1c97699eee12586/scipy-1.15.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889", size = 35503415, upload-time = "2025-05-08T16:05:34.699Z" }, + { url = "https://files.pythonhosted.org/packages/bd/37/89f19c8c05505d0601ed5650156e50eb881ae3918786c8fd7262b4ee66d3/scipy-1.15.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982", size = 37652622, upload-time = "2025-05-08T16:05:40.762Z" }, + { url = "https://files.pythonhosted.org/packages/7e/31/be59513aa9695519b18e1851bb9e487de66f2d31f835201f1b42f5d4d475/scipy-1.15.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9", size = 37244796, upload-time = "2025-05-08T16:05:48.119Z" }, + { url = "https://files.pythonhosted.org/packages/10/c0/4f5f3eeccc235632aab79b27a74a9130c6c35df358129f7ac8b29f562ac7/scipy-1.15.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594", size = 40047684, upload-time = "2025-05-08T16:05:54.22Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a7/0ddaf514ce8a8714f6ed243a2b391b41dbb65251affe21ee3077ec45ea9a/scipy-1.15.3-cp311-cp311-win_amd64.whl", hash = "sha256:ae48a786a28412d744c62fd7816a4118ef97e5be0bee968ce8f0a2fba7acf3bb", size = 41246504, upload-time = "2025-05-08T16:06:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/37/4b/683aa044c4162e10ed7a7ea30527f2cbd92e6999c10a8ed8edb253836e9c/scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019", size = 38766735, upload-time = "2025-05-08T16:06:06.471Z" }, + { url = "https://files.pythonhosted.org/packages/7b/7e/f30be3d03de07f25dc0ec926d1681fed5c732d759ac8f51079708c79e680/scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6", size = 30173284, upload-time = "2025-05-08T16:06:11.686Z" }, + { url = "https://files.pythonhosted.org/packages/07/9c/0ddb0d0abdabe0d181c1793db51f02cd59e4901da6f9f7848e1f96759f0d/scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477", size = 22446958, upload-time = "2025-05-08T16:06:15.97Z" }, + { url = "https://files.pythonhosted.org/packages/af/43/0bce905a965f36c58ff80d8bea33f1f9351b05fad4beaad4eae34699b7a1/scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c", size = 25242454, upload-time = "2025-05-08T16:06:20.394Z" }, + { url = "https://files.pythonhosted.org/packages/56/30/a6f08f84ee5b7b28b4c597aca4cbe545535c39fe911845a96414700b64ba/scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45", size = 35210199, upload-time = "2025-05-08T16:06:26.159Z" }, + { url = "https://files.pythonhosted.org/packages/0b/1f/03f52c282437a168ee2c7c14a1a0d0781a9a4a8962d84ac05c06b4c5b555/scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49", size = 37309455, upload-time = "2025-05-08T16:06:32.778Z" }, + { url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140, upload-time = "2025-05-08T16:06:39.249Z" }, + { url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549, upload-time = "2025-05-08T16:06:45.729Z" }, + { url = "https://files.pythonhosted.org/packages/e6/eb/3bf6ea8ab7f1503dca3a10df2e4b9c3f6b3316df07f6c0ded94b281c7101/scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed", size = 40966184, upload-time = "2025-05-08T16:06:52.623Z" }, + { url = "https://files.pythonhosted.org/packages/73/18/ec27848c9baae6e0d6573eda6e01a602e5649ee72c27c3a8aad673ebecfd/scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759", size = 38728256, upload-time = "2025-05-08T16:06:58.696Z" }, + { url = "https://files.pythonhosted.org/packages/74/cd/1aef2184948728b4b6e21267d53b3339762c285a46a274ebb7863c9e4742/scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62", size = 30109540, upload-time = "2025-05-08T16:07:04.209Z" }, + { url = "https://files.pythonhosted.org/packages/5b/d8/59e452c0a255ec352bd0a833537a3bc1bfb679944c4938ab375b0a6b3a3e/scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb", size = 22383115, upload-time = "2025-05-08T16:07:08.998Z" }, + { url = "https://files.pythonhosted.org/packages/08/f5/456f56bbbfccf696263b47095291040655e3cbaf05d063bdc7c7517f32ac/scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730", size = 25163884, upload-time = "2025-05-08T16:07:14.091Z" }, + { url = "https://files.pythonhosted.org/packages/a2/66/a9618b6a435a0f0c0b8a6d0a2efb32d4ec5a85f023c2b79d39512040355b/scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825", size = 35174018, upload-time = "2025-05-08T16:07:19.427Z" }, + { url = "https://files.pythonhosted.org/packages/b5/09/c5b6734a50ad4882432b6bb7c02baf757f5b2f256041da5df242e2d7e6b6/scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7", size = 37269716, upload-time = "2025-05-08T16:07:25.712Z" }, + { url = "https://files.pythonhosted.org/packages/77/0a/eac00ff741f23bcabd352731ed9b8995a0a60ef57f5fd788d611d43d69a1/scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11", size = 36872342, upload-time = "2025-05-08T16:07:31.468Z" }, + { url = "https://files.pythonhosted.org/packages/fe/54/4379be86dd74b6ad81551689107360d9a3e18f24d20767a2d5b9253a3f0a/scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126", size = 39670869, upload-time = "2025-05-08T16:07:38.002Z" }, + { url = "https://files.pythonhosted.org/packages/87/2e/892ad2862ba54f084ffe8cc4a22667eaf9c2bcec6d2bff1d15713c6c0703/scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163", size = 40988851, upload-time = "2025-05-08T16:08:33.671Z" }, + { url = "https://files.pythonhosted.org/packages/1b/e9/7a879c137f7e55b30d75d90ce3eb468197646bc7b443ac036ae3fe109055/scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8", size = 38863011, upload-time = "2025-05-08T16:07:44.039Z" }, + { url = "https://files.pythonhosted.org/packages/51/d1/226a806bbd69f62ce5ef5f3ffadc35286e9fbc802f606a07eb83bf2359de/scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5", size = 30266407, upload-time = "2025-05-08T16:07:49.891Z" }, + { url = "https://files.pythonhosted.org/packages/e5/9b/f32d1d6093ab9eeabbd839b0f7619c62e46cc4b7b6dbf05b6e615bbd4400/scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e", size = 22540030, upload-time = "2025-05-08T16:07:54.121Z" }, + { url = "https://files.pythonhosted.org/packages/e7/29/c278f699b095c1a884f29fda126340fcc201461ee8bfea5c8bdb1c7c958b/scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb", size = 25218709, upload-time = "2025-05-08T16:07:58.506Z" }, + { url = "https://files.pythonhosted.org/packages/24/18/9e5374b617aba742a990581373cd6b68a2945d65cc588482749ef2e64467/scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723", size = 34809045, upload-time = "2025-05-08T16:08:03.929Z" }, + { url = "https://files.pythonhosted.org/packages/e1/fe/9c4361e7ba2927074360856db6135ef4904d505e9b3afbbcb073c4008328/scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb", size = 36703062, upload-time = "2025-05-08T16:08:09.558Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8e/038ccfe29d272b30086b25a4960f757f97122cb2ec42e62b460d02fe98e9/scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4", size = 36393132, upload-time = "2025-05-08T16:08:15.34Z" }, + { url = "https://files.pythonhosted.org/packages/10/7e/5c12285452970be5bdbe8352c619250b97ebf7917d7a9a9e96b8a8140f17/scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5", size = 38979503, upload-time = "2025-05-08T16:08:21.513Z" }, + { url = "https://files.pythonhosted.org/packages/81/06/0a5e5349474e1cbc5757975b21bd4fad0e72ebf138c5592f191646154e06/scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca", size = 40308097, upload-time = "2025-05-08T16:08:27.627Z" }, +] + +[[package]] +name = "scipy" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.11.*'", +] +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/75/b4ce781849931fef6fd529afa6b63711d5a733065722d0c3e2724af9e40a/scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec", size = 31613675, upload-time = "2026-02-23T00:16:00.13Z" }, + { url = "https://files.pythonhosted.org/packages/f7/58/bccc2861b305abdd1b8663d6130c0b3d7cc22e8d86663edbc8401bfd40d4/scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696", size = 28162057, upload-time = "2026-02-23T00:16:09.456Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee", size = 20334032, upload-time = "2026-02-23T00:16:17.358Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd", size = 22709533, upload-time = "2026-02-23T00:16:25.791Z" }, + { url = "https://files.pythonhosted.org/packages/4d/60/8804678875fc59362b0fb759ab3ecce1f09c10a735680318ac30da8cd76b/scipy-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:744b2bf3640d907b79f3fd7874efe432d1cf171ee721243e350f55234b4cec4c", size = 33062057, upload-time = "2026-02-23T00:16:36.931Z" }, + { url = "https://files.pythonhosted.org/packages/09/7d/af933f0f6e0767995b4e2d705a0665e454d1c19402aa7e895de3951ebb04/scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4", size = 35349300, upload-time = "2026-02-23T00:16:49.108Z" }, + { url = "https://files.pythonhosted.org/packages/b4/3d/7ccbbdcbb54c8fdc20d3b6930137c782a163fa626f0aef920349873421ba/scipy-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd96a1898c0a47be4520327e01f874acfd61fb48a9420f8aa9f6483412ffa444", size = 35127333, upload-time = "2026-02-23T00:17:01.293Z" }, + { url = "https://files.pythonhosted.org/packages/e8/19/f926cb11c42b15ba08e3a71e376d816ac08614f769b4f47e06c3580c836a/scipy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4eb6c25dd62ee8d5edf68a8e1c171dd71c292fdae95d8aeb3dd7d7de4c364082", size = 37741314, upload-time = "2026-02-23T00:17:12.576Z" }, + { url = "https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff", size = 36607512, upload-time = "2026-02-23T00:17:23.424Z" }, + { url = "https://files.pythonhosted.org/packages/68/7f/bdd79ceaad24b671543ffe0ef61ed8e659440eb683b66f033454dcee90eb/scipy-1.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:9ecb4efb1cd6e8c4afea0daa91a87fbddbce1b99d2895d151596716c0b2e859d", size = 24599248, upload-time = "2026-02-23T00:17:34.561Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/b992b488d6f299dbe3f11a20b24d3dda3d46f1a635ede1c46b5b17a7b163/scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8", size = 31610954, upload-time = "2026-02-23T00:17:49.855Z" }, + { url = "https://files.pythonhosted.org/packages/b2/02/cf107b01494c19dc100f1d0b7ac3cc08666e96ba2d64db7626066cee895e/scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76", size = 28172662, upload-time = "2026-02-23T00:18:01.64Z" }, + { url = "https://files.pythonhosted.org/packages/cf/a9/599c28631bad314d219cf9ffd40e985b24d603fc8a2f4ccc5ae8419a535b/scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086", size = 20344366, upload-time = "2026-02-23T00:18:12.015Z" }, + { url = "https://files.pythonhosted.org/packages/35/f5/906eda513271c8deb5af284e5ef0206d17a96239af79f9fa0aebfe0e36b4/scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b", size = 22704017, upload-time = "2026-02-23T00:18:21.502Z" }, + { url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842, upload-time = "2026-02-23T00:18:35.367Z" }, + { url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890, upload-time = "2026-02-23T00:18:49.188Z" }, + { url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557, upload-time = "2026-02-23T00:18:54.74Z" }, + { url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856, upload-time = "2026-02-23T00:19:00.307Z" }, + { url = "https://files.pythonhosted.org/packages/a2/84/dc08d77fbf3d87d3ee27f6a0c6dcce1de5829a64f2eae85a0ecc1f0daa73/scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87", size = 36549682, upload-time = "2026-02-23T00:19:07.67Z" }, + { url = "https://files.pythonhosted.org/packages/bc/98/fe9ae9ffb3b54b62559f52dedaebe204b408db8109a8c66fdd04869e6424/scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3", size = 24547340, upload-time = "2026-02-23T00:19:12.024Z" }, + { url = "https://files.pythonhosted.org/packages/76/27/07ee1b57b65e92645f219b37148a7e7928b82e2b5dbeccecb4dff7c64f0b/scipy-1.17.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5e3c5c011904115f88a39308379c17f91546f77c1667cea98739fe0fccea804c", size = 31590199, upload-time = "2026-02-23T00:19:17.192Z" }, + { url = "https://files.pythonhosted.org/packages/ec/ae/db19f8ab842e9b724bf5dbb7db29302a91f1e55bc4d04b1025d6d605a2c5/scipy-1.17.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6fac755ca3d2c3edcb22f479fceaa241704111414831ddd3bc6056e18516892f", size = 28154001, upload-time = "2026-02-23T00:19:22.241Z" }, + { url = "https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d", size = 20325719, upload-time = "2026-02-23T00:19:26.329Z" }, + { url = "https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b", size = 22683595, upload-time = "2026-02-23T00:19:30.304Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e0/e58fbde4a1a594c8be8114eb4aac1a55bcd6587047efc18a61eb1f5c0d30/scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b64ca7d4aee0102a97f3ba22124052b4bd2152522355073580bf4845e2550b6", size = 32896429, upload-time = "2026-02-23T00:19:35.536Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464", size = 35203952, upload-time = "2026-02-23T00:19:42.259Z" }, + { url = "https://files.pythonhosted.org/packages/8d/a5/9afd17de24f657fdfe4df9a3f1ea049b39aef7c06000c13db1530d81ccca/scipy-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:beeda3d4ae615106d7094f7e7cef6218392e4465cc95d25f900bebabfded0950", size = 34979063, upload-time = "2026-02-23T00:19:47.547Z" }, + { url = "https://files.pythonhosted.org/packages/8b/13/88b1d2384b424bf7c924f2038c1c409f8d88bb2a8d49d097861dd64a57b2/scipy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6609bc224e9568f65064cfa72edc0f24ee6655b47575954ec6339534b2798369", size = 37598449, upload-time = "2026-02-23T00:19:53.238Z" }, + { url = "https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448", size = 36510943, upload-time = "2026-02-23T00:20:50.89Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fd/3be73c564e2a01e690e19cc618811540ba5354c67c8680dce3281123fb79/scipy-1.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:5cf36e801231b6a2059bf354720274b7558746f3b1a4efb43fcf557ccd484a87", size = 24545621, upload-time = "2026-02-23T00:20:55.871Z" }, + { url = "https://files.pythonhosted.org/packages/6f/6b/17787db8b8114933a66f9dcc479a8272e4b4da75fe03b0c282f7b0ade8cd/scipy-1.17.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:d59c30000a16d8edc7e64152e30220bfbd724c9bbb08368c054e24c651314f0a", size = 31936708, upload-time = "2026-02-23T00:19:58.694Z" }, + { url = "https://files.pythonhosted.org/packages/38/2e/524405c2b6392765ab1e2b722a41d5da33dc5c7b7278184a8ad29b6cb206/scipy-1.17.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:010f4333c96c9bb1a4516269e33cb5917b08ef2166d5556ca2fd9f082a9e6ea0", size = 28570135, upload-time = "2026-02-23T00:20:03.934Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c3/5bd7199f4ea8556c0c8e39f04ccb014ac37d1468e6cfa6a95c6b3562b76e/scipy-1.17.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2ceb2d3e01c5f1d83c4189737a42d9cb2fc38a6eeed225e7515eef71ad301dce", size = 20741977, upload-time = "2026-02-23T00:20:07.935Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b8/8ccd9b766ad14c78386599708eb745f6b44f08400a5fd0ade7cf89b6fc93/scipy-1.17.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:844e165636711ef41f80b4103ed234181646b98a53c8f05da12ca5ca289134f6", size = 23029601, upload-time = "2026-02-23T00:20:12.161Z" }, + { url = "https://files.pythonhosted.org/packages/6d/a0/3cb6f4d2fb3e17428ad2880333cac878909ad1a89f678527b5328b93c1d4/scipy-1.17.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:158dd96d2207e21c966063e1635b1063cd7787b627b6f07305315dd73d9c679e", size = 33019667, upload-time = "2026-02-23T00:20:17.208Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c3/2d834a5ac7bf3a0c806ad1508efc02dda3c8c61472a56132d7894c312dea/scipy-1.17.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cbb80d93260fe2ffa334efa24cb8f2f0f622a9b9febf8b483c0b865bfb3475", size = 35264159, upload-time = "2026-02-23T00:20:23.087Z" }, + { url = "https://files.pythonhosted.org/packages/4d/77/d3ed4becfdbd217c52062fafe35a72388d1bd82c2d0ba5ca19d6fcc93e11/scipy-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dbc12c9f3d185f5c737d801da555fb74b3dcfa1a50b66a1a93e09190f41fab50", size = 35102771, upload-time = "2026-02-23T00:20:28.636Z" }, + { url = "https://files.pythonhosted.org/packages/bd/12/d19da97efde68ca1ee5538bb261d5d2c062f0c055575128f11a2730e3ac1/scipy-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94055a11dfebe37c656e70317e1996dc197e1a15bbcc351bcdd4610e128fe1ca", size = 37665910, upload-time = "2026-02-23T00:20:34.743Z" }, + { url = "https://files.pythonhosted.org/packages/06/1c/1172a88d507a4baaf72c5a09bb6c018fe2ae0ab622e5830b703a46cc9e44/scipy-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e30bdeaa5deed6bc27b4cc490823cd0347d7dae09119b8803ae576ea0ce52e4c", size = 36562980, upload-time = "2026-02-23T00:20:40.575Z" }, + { url = "https://files.pythonhosted.org/packages/70/b0/eb757336e5a76dfa7911f63252e3b7d1de00935d7705cf772db5b45ec238/scipy-1.17.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a720477885a9d2411f94a93d16f9d89bad0f28ca23c3f8daa521e2dcc3f44d49", size = 24856543, upload-time = "2026-02-23T00:20:45.313Z" }, + { url = "https://files.pythonhosted.org/packages/cf/83/333afb452af6f0fd70414dc04f898647ee1423979ce02efa75c3b0f2c28e/scipy-1.17.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:a48a72c77a310327f6a3a920092fa2b8fd03d7deaa60f093038f22d98e096717", size = 31584510, upload-time = "2026-02-23T00:21:01.015Z" }, + { url = "https://files.pythonhosted.org/packages/ed/a6/d05a85fd51daeb2e4ea71d102f15b34fedca8e931af02594193ae4fd25f7/scipy-1.17.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:45abad819184f07240d8a696117a7aacd39787af9e0b719d00285549ed19a1e9", size = 28170131, upload-time = "2026-02-23T00:21:05.888Z" }, + { url = "https://files.pythonhosted.org/packages/db/7b/8624a203326675d7746a254083a187398090a179335b2e4a20e2ddc46e83/scipy-1.17.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3fd1fcdab3ea951b610dc4cef356d416d5802991e7e32b5254828d342f7b7e0b", size = 20342032, upload-time = "2026-02-23T00:21:09.904Z" }, + { url = "https://files.pythonhosted.org/packages/c9/35/2c342897c00775d688d8ff3987aced3426858fd89d5a0e26e020b660b301/scipy-1.17.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7bdf2da170b67fdf10bca777614b1c7d96ae3ca5794fd9587dce41eb2966e866", size = 22678766, upload-time = "2026-02-23T00:21:14.313Z" }, + { url = "https://files.pythonhosted.org/packages/ef/f2/7cdb8eb308a1a6ae1e19f945913c82c23c0c442a462a46480ce487fdc0ac/scipy-1.17.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adb2642e060a6549c343603a3851ba76ef0b74cc8c079a9a58121c7ec9fe2350", size = 32957007, upload-time = "2026-02-23T00:21:19.663Z" }, + { url = "https://files.pythonhosted.org/packages/0b/2e/7eea398450457ecb54e18e9d10110993fa65561c4f3add5e8eccd2b9cd41/scipy-1.17.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eee2cfda04c00a857206a4330f0c5e3e56535494e30ca445eb19ec624ae75118", size = 35221333, upload-time = "2026-02-23T00:21:25.278Z" }, + { url = "https://files.pythonhosted.org/packages/d9/77/5b8509d03b77f093a0d52e606d3c4f79e8b06d1d38c441dacb1e26cacf46/scipy-1.17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2650c1fb97e184d12d8ba010493ee7b322864f7d3d00d3f9bb97d9c21de4068", size = 35042066, upload-time = "2026-02-23T00:21:31.358Z" }, + { url = "https://files.pythonhosted.org/packages/f9/df/18f80fb99df40b4070328d5ae5c596f2f00fffb50167e31439e932f29e7d/scipy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08b900519463543aa604a06bec02461558a6e1cef8fdbb8098f77a48a83c8118", size = 37612763, upload-time = "2026-02-23T00:21:37.247Z" }, + { url = "https://files.pythonhosted.org/packages/4b/39/f0e8ea762a764a9dc52aa7dabcfad51a354819de1f0d4652b6a1122424d6/scipy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:3877ac408e14da24a6196de0ddcace62092bfc12a83823e92e49e40747e52c19", size = 37290984, upload-time = "2026-02-23T00:22:35.023Z" }, + { url = "https://files.pythonhosted.org/packages/7c/56/fe201e3b0f93d1a8bcf75d3379affd228a63d7e2d80ab45467a74b494947/scipy-1.17.1-cp314-cp314-win_arm64.whl", hash = "sha256:f8885db0bc2bffa59d5c1b72fad7a6a92d3e80e7257f967dd81abb553a90d293", size = 25192877, upload-time = "2026-02-23T00:22:39.798Z" }, + { url = "https://files.pythonhosted.org/packages/96/ad/f8c414e121f82e02d76f310f16db9899c4fcde36710329502a6b2a3c0392/scipy-1.17.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:1cc682cea2ae55524432f3cdff9e9a3be743d52a7443d0cba9017c23c87ae2f6", size = 31949750, upload-time = "2026-02-23T00:21:42.289Z" }, + { url = "https://files.pythonhosted.org/packages/7c/b0/c741e8865d61b67c81e255f4f0a832846c064e426636cd7de84e74d209be/scipy-1.17.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:2040ad4d1795a0ae89bfc7e8429677f365d45aa9fd5e4587cf1ea737f927b4a1", size = 28585858, upload-time = "2026-02-23T00:21:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1b/3985219c6177866628fa7c2595bfd23f193ceebbe472c98a08824b9466ff/scipy-1.17.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:131f5aaea57602008f9822e2115029b55d4b5f7c070287699fe45c661d051e39", size = 20757723, upload-time = "2026-02-23T00:21:52.039Z" }, + { url = "https://files.pythonhosted.org/packages/c0/19/2a04aa25050d656d6f7b9e7b685cc83d6957fb101665bfd9369ca6534563/scipy-1.17.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9cdc1a2fcfd5c52cfb3045feb399f7b3ce822abdde3a193a6b9a60b3cb5854ca", size = 23043098, upload-time = "2026-02-23T00:21:56.185Z" }, + { url = "https://files.pythonhosted.org/packages/86/f1/3383beb9b5d0dbddd030335bf8a8b32d4317185efe495374f134d8be6cce/scipy-1.17.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e3dcd57ab780c741fde8dc68619de988b966db759a3c3152e8e9142c26295ad", size = 33030397, upload-time = "2026-02-23T00:22:01.404Z" }, + { url = "https://files.pythonhosted.org/packages/41/68/8f21e8a65a5a03f25a79165ec9d2b28c00e66dc80546cf5eb803aeeff35b/scipy-1.17.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9956e4d4f4a301ebf6cde39850333a6b6110799d470dbbb1e25326ac447f52a", size = 35281163, upload-time = "2026-02-23T00:22:07.024Z" }, + { url = "https://files.pythonhosted.org/packages/84/8d/c8a5e19479554007a5632ed7529e665c315ae7492b4f946b0deb39870e39/scipy-1.17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a4328d245944d09fd639771de275701ccadf5f781ba0ff092ad141e017eccda4", size = 35116291, upload-time = "2026-02-23T00:22:12.585Z" }, + { url = "https://files.pythonhosted.org/packages/52/52/e57eceff0e342a1f50e274264ed47497b59e6a4e3118808ee58ddda7b74a/scipy-1.17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a77cbd07b940d326d39a1d1b37817e2ee4d79cb30e7338f3d0cddffae70fcaa2", size = 37682317, upload-time = "2026-02-23T00:22:18.513Z" }, + { url = "https://files.pythonhosted.org/packages/11/2f/b29eafe4a3fbc3d6de9662b36e028d5f039e72d345e05c250e121a230dd4/scipy-1.17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:eb092099205ef62cd1782b006658db09e2fed75bffcae7cc0d44052d8aa0f484", size = 37345327, upload-time = "2026-02-23T00:22:24.442Z" }, + { url = "https://files.pythonhosted.org/packages/07/39/338d9219c4e87f3e708f18857ecd24d22a0c3094752393319553096b98af/scipy-1.17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:200e1050faffacc162be6a486a984a0497866ec54149a01270adc8a59b7c7d21", size = 25489165, upload-time = "2026-02-23T00:22:29.563Z" }, +] + +[[package]] +name = "scipy" +version = "1.18.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", +] +dependencies = [ + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7e/74/66de6258867beb2ef08f35f9f2ac017a52cacd5081714d239ff1a442d458/scipy-1.18.1.tar.gz", hash = "sha256:52c4b7422442aba924d03ad4019852b08a92e64ea187b933135687bfe2747307", size = 30781235, upload-time = "2026-08-21T23:28:50.599Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/f7/240c110c08693826b4513a52f5717d62ec7c7af72f2920821247c03b17b3/scipy-1.18.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:457fd7a2a8edeb044ab6ffbc0aa03ff6cd18491356e5e0c834d76ce621b916d1", size = 31111061, upload-time = "2026-08-21T23:23:44.522Z" }, + { url = "https://files.pythonhosted.org/packages/05/4a/78c6285577c375e7cf27277ea8ee6961224327f1e1a0c44af5f17f23635c/scipy-1.18.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:e708533e8b2ae2497d65346538a7dcc92814410b25b81432eac66de0f2af8265", size = 28733332, upload-time = "2026-08-21T23:23:50.015Z" }, + { url = "https://files.pythonhosted.org/packages/a5/f6/a5b82f8abbe14d134691b8b903696f701d25a081353a29dc655c364d9e62/scipy-1.18.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:7bbf207c4453ce1ad2e00b17313852b33310b83090c2311bdaf97f93c0380d12", size = 20475078, upload-time = "2026-08-21T23:23:54.138Z" }, + { url = "https://files.pythonhosted.org/packages/23/22/0858a0bbd6b3e825ceb8cd9baf9eaf3b2f2b1d77727eb6be40500bcdc92f/scipy-1.18.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:78c0665edead396b1abb4897c41a5c1d9bf090c8a637a4c20a61678e0a264e66", size = 23108904, upload-time = "2026-08-21T23:23:57.824Z" }, + { url = "https://files.pythonhosted.org/packages/75/9a/2e71719f31eaefe0e3a1706c4a1ded94e664bfd95ffca2b219a671faee01/scipy-1.18.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c085faa2cfa879c5141df483f836f4d691045a078224a670fa570fa01612d89", size = 34025113, upload-time = "2026-08-21T23:24:02.209Z" }, + { url = "https://files.pythonhosted.org/packages/df/64/ff35eb9e54894cf471ff4716abd3c81eb0a0626869217ce3e6ba4ccf17d7/scipy-1.18.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f55fa87b6c612ecd6b058f167c53231b1d14e412efe361d3d6e38b3631c73218", size = 35344199, upload-time = "2026-08-21T23:24:07.844Z" }, + { url = "https://files.pythonhosted.org/packages/d3/af/c5538be1792f7034c12c7db6ee67cace58253c7b87b122d68253eaf5de89/scipy-1.18.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c35d74ce0e193ff740c2f2be2ac913ddc232fe6c1ff40b26cfecb9c670c63314", size = 35639587, upload-time = "2026-08-21T23:24:13.05Z" }, + { url = "https://files.pythonhosted.org/packages/91/4c/075e4f66471bac101141ac739e9e135549be1bae584571bd03a530c056e1/scipy-1.18.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d2924a03db38dc2e848bca2fe9f077dafb891480b91a00a0963a8cf86dfc31c1", size = 37480330, upload-time = "2026-08-21T23:24:19.608Z" }, + { url = "https://files.pythonhosted.org/packages/39/e7/979fd14e75008623df31ba70d6bb144700f68feadcea042021c06a05bf82/scipy-1.18.1-cp312-cp312-win_amd64.whl", hash = "sha256:5e4d44984abc0020154ea81b247adeddcc3ac5527b975ff798bd1ba0adc513c2", size = 36658278, upload-time = "2026-08-21T23:24:25.463Z" }, + { url = "https://files.pythonhosted.org/packages/c7/0b/e1525354ff9d7d5feb6d1b31af6d14072e5c91e9607b421fa1ec889660b3/scipy-1.18.1-cp312-cp312-win_arm64.whl", hash = "sha256:d65d448389b8436493abcf629cc94ad0cf32aecaf06e1acca1de53cc795f2f12", size = 24400588, upload-time = "2026-08-21T23:24:30.579Z" }, + { url = "https://files.pythonhosted.org/packages/b6/55/4540ee0f9c42a9ad7109d0d1a8cc70de54c3572b01c6693a2b1c70e90ceb/scipy-1.18.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:3ab3523da44749156e1f68b464dc56af11ae4cbc5c739a49d05f32b982eca9f3", size = 31089958, upload-time = "2026-08-21T23:24:35.8Z" }, + { url = "https://files.pythonhosted.org/packages/2a/f5/769f36d14922b8071a43e95d24d18b6bdafad10d7f5cf647867e1ac052bc/scipy-1.18.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e6fb6a55cc0ba97b59a1f288fb86dc6fce8bdfc0fffcbfd015e3a954bf2a2d93", size = 28715106, upload-time = "2026-08-21T23:24:40.775Z" }, + { url = "https://files.pythonhosted.org/packages/9a/d7/21d890274f75ea37a8209d5519e72da3da90302e3b9fb8397a0918386a62/scipy-1.18.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:ea324d9dd34c38bfb9bec8ca4d1b407db97dbb74029f566b8e322b1b6fe56fe6", size = 20456846, upload-time = "2026-08-21T23:24:45.066Z" }, + { url = "https://files.pythonhosted.org/packages/ec/01/798430ecea2e78ec7c02663d5f71c007bb6abeca931080debd40d7fa55ea/scipy-1.18.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:75b00eb8fb802090aa903f4ea1c7f5a584779f967361e68b7e98e531cc2d7174", size = 23087986, upload-time = "2026-08-21T23:24:49.539Z" }, + { url = "https://files.pythonhosted.org/packages/e6/5f/4634e9d35c68496e4e34cb6946eafab044458e6cedab42b40b6588e475b6/scipy-1.18.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d416b16cccfd70fbf62400e84d0bb2f4e6af519a45557f1692c749b37f14b315", size = 33998146, upload-time = "2026-08-21T23:24:54.714Z" }, + { url = "https://files.pythonhosted.org/packages/41/48/6450ed9243315322bbc19ac57b9b70d66a20bf1d38d124c96bc4bf6af9ea/scipy-1.18.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fdaf5ea890a6183d0565f51a61799d67081bd5b1cf03c5f4b3fd3732108625c9", size = 35312578, upload-time = "2026-08-21T23:25:00.44Z" }, + { url = "https://files.pythonhosted.org/packages/00/bd/bf5a4be6a3525676499f6dff307991739ff6fdcad1481b1aeb6745339f58/scipy-1.18.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c825cef2f49e46753726a7181a8e199804a912b29519ada542c6ebc654951899", size = 35612621, upload-time = "2026-08-21T23:25:06.144Z" }, + { url = "https://files.pythonhosted.org/packages/bd/4e/3c45c33e00a77996c4b1cb707929f833ba7b1d522ee29f882512c330676d/scipy-1.18.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e3b417bf8c2c7c16e8f58ad91db17783ec911ac16e7b50eb6eab6e809b4f5b07", size = 37457323, upload-time = "2026-08-21T23:25:12.483Z" }, + { url = "https://files.pythonhosted.org/packages/93/0e/e0348fbc0dbab65c114cf78957e7dfeb49f8e8b556b4d930cc12ff195e18/scipy-1.18.1-cp313-cp313-win_amd64.whl", hash = "sha256:559ed65f60c1af5a03f3912605a1b5114f522c7c32fb23c3376ae8f03219fe28", size = 36622841, upload-time = "2026-08-21T23:25:18.722Z" }, + { url = "https://files.pythonhosted.org/packages/50/a8/6a77f5f267c555108f0a864b6db714363dab567a8266422a79a385f9232b/scipy-1.18.1-cp313-cp313-win_arm64.whl", hash = "sha256:cd479fc04dd9401e3b4f49e76518768ef99c4f517a98c284eb091fd725719adf", size = 24399315, upload-time = "2026-08-21T23:25:23.458Z" }, + { url = "https://files.pythonhosted.org/packages/06/d5/d8eb4e280ddb56a4ab2c6f02ee49b56b23f6e977cf0802fd6d68dbef14f5/scipy-1.18.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:83de5453a7799afc9048b4616bd085cef126e36412f0ea2f6370c36a2a3a51e7", size = 31090936, upload-time = "2026-08-21T23:25:28.686Z" }, + { url = "https://files.pythonhosted.org/packages/2a/49/59ea385dc3a62ff498ddf3cfff7c2b41b0f9f9d3c4122b3f1dcb6d6327fe/scipy-1.18.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:9554bcc6d715ee87a633a3cc8e7703c6628b100dd29cb8a2efc4c0533c7ff729", size = 28725221, upload-time = "2026-08-21T23:25:33.244Z" }, + { url = "https://files.pythonhosted.org/packages/70/e8/6b0c288c50942d78193696c9f15f9a0874f5178aa0ddf40f83d9924b3e8d/scipy-1.18.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:011413b7426b75012840e35649e00fe0a2c3bae89fed433876e3a99251572efc", size = 20466839, upload-time = "2026-08-21T23:25:37.516Z" }, + { url = "https://files.pythonhosted.org/packages/4b/e0/54fd3793c729e3b936782f181b59cbb1205bf250ab605a16cb1ba61cdd5e/scipy-1.18.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:88f0e784020649f88ea48c9f5ddfa403bf9205820667c0914740b392035afb82", size = 23089121, upload-time = "2026-08-21T23:25:42.019Z" }, + { url = "https://files.pythonhosted.org/packages/0b/56/030af62bea3cf878e0028515dff78c123b01633606a879b63f42d2db99cc/scipy-1.18.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d3ab0e8c69a17dd3559eab8cbb88f258e285c94d572c2719033f90f83290c89", size = 34053851, upload-time = "2026-08-21T23:25:47.998Z" }, + { url = "https://files.pythonhosted.org/packages/6b/89/2a844506d49651e9aa1af6ef95b6bd8031cb1d5a4375edec6155037e04cf/scipy-1.18.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ac0333bdf38309aa3dcbe7e3fa7ea29e7a2c37c6ea306a757b700ded8e4596ad", size = 35329183, upload-time = "2026-08-21T23:25:53.522Z" }, + { url = "https://files.pythonhosted.org/packages/eb/56/c7370c3640e92ac9613cbf26cb3f729f9b12ddf1727b55b94b53b24d6f48/scipy-1.18.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:911de823097db8b63f034299d12662db93344e6ffa0b881cbb57748974b70168", size = 35672551, upload-time = "2026-08-21T23:25:59.387Z" }, + { url = "https://files.pythonhosted.org/packages/24/16/ec8536f351421f8bf60a1120930638f83790f4710b8230446aca3d6159d4/scipy-1.18.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:95298364e251be3e60249facbeeca03631d3bb7584f85879516ec55ac717b81f", size = 37469416, upload-time = "2026-08-21T23:26:05.432Z" }, + { url = "https://files.pythonhosted.org/packages/52/94/d73da0d28f16c45bb9b0a5691b91610b0275c5ef0eb5e43c87cf2dc1bf31/scipy-1.18.1-cp314-cp314-win_amd64.whl", hash = "sha256:78a0d7c918e74a232394117160e7e3db503377572a45bcef8826e4ab8a35feba", size = 37362755, upload-time = "2026-08-21T23:26:11.366Z" }, + { url = "https://files.pythonhosted.org/packages/89/25/e996e4dc74e10e227b1e14db5eaf6608bb6dd33884a64851c38f18dd4249/scipy-1.18.1-cp314-cp314-win_arm64.whl", hash = "sha256:cbf38d043c1aa4ab306e1ada6ab6eddacc3322a20b7af1b30bc93254b366fe09", size = 25036090, upload-time = "2026-08-21T23:26:15.887Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c9/c00213f92309d753b48903e6a451b87eb52ff5b7a16e789d1568bbf221c4/scipy-1.18.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:0fcb3c93519f27bb4f0c4b0f7802cdcaca7fcf93267b75edda2e9f4e8a55cbd7", size = 31485550, upload-time = "2026-08-21T23:26:20.776Z" }, + { url = "https://files.pythonhosted.org/packages/74/b2/e3067c487982d4eeab2938928529410370c06fea84a4d3f4925e7d96647d/scipy-1.18.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:ddef79fb382df40104a19bb7151b3b23e57c1778fcf857c71ceecd9bd264513f", size = 29174642, upload-time = "2026-08-21T23:26:25.395Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ab/374c9fe2d1ec014e576c781a4b5d8e1ba340e8f6b4638c16f711d2b194f0/scipy-1.18.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:0e82073ecc7acc6436fac4b31674109c7e1d3e596789767eda01258a8c9e8123", size = 20916357, upload-time = "2026-08-21T23:26:30.112Z" }, + { url = "https://files.pythonhosted.org/packages/90/38/223915c88a17317cafbf8ca2a42b11c265a9fb1e804aa665544132b5fe8a/scipy-1.18.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:8bcf3c1ba5d6456e2effd30fcbd3459b044d683fcdac79a2e6830f0bdf7de487", size = 23482611, upload-time = "2026-08-21T23:26:34.846Z" }, + { url = "https://files.pythonhosted.org/packages/c4/d1/db0948da8ca57a80b36520ef0a768b967d99f3af65f4b6f1bf6362ad4dd4/scipy-1.18.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cfbf154f2ba187f2ed6cce2639efff7d105f1140573642c0161615b6d91d6a87", size = 34143202, upload-time = "2026-08-21T23:26:40.4Z" }, + { url = "https://files.pythonhosted.org/packages/87/53/39d046cc7574ed6acacb6bd5723e220107ece80bff12faaf3efc4ddeede4/scipy-1.18.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a1d33a7836f7ddc1993427966a0823468ec41bcbdb1a9f9942d1d7e57f803ba3", size = 35380876, upload-time = "2026-08-21T23:26:46.1Z" }, + { url = "https://files.pythonhosted.org/packages/f9/da/32e0e799d875a85ca57d9bde6c78148afcc0e38276df683d95854eadc8c3/scipy-1.18.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f4b8bc363b6d65ee2152bec57568e3c52639bb34c46057b09857a307ed5e21d", size = 35770885, upload-time = "2026-08-21T23:26:51.533Z" }, + { url = "https://files.pythonhosted.org/packages/88/2e/f97a666d362fee68b18f41c9c30ed502ca5c98b549749bfcb52a8b74d1eb/scipy-1.18.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:11c423f1049c5755ad4409af52a9ada1cff96fe9b50795d4af3619f292901239", size = 37525424, upload-time = "2026-08-21T23:26:56.751Z" }, + { url = "https://files.pythonhosted.org/packages/ca/d5/a9e765a84654ebba8479a1fd1b059ced1af72b168a3b2a3a46540ea38d20/scipy-1.18.1-cp314-cp314t-win_amd64.whl", hash = "sha256:c24acac1e18912761c4700239bbc1fd32f615af690f1584d49b35859be51324d", size = 37416961, upload-time = "2026-08-21T23:27:01.546Z" }, + { url = "https://files.pythonhosted.org/packages/ee/16/e79e0d1c63ef698879d85439d37e9fb434e3b804e506a6991038d086ebd9/scipy-1.18.1-cp314-cp314t-win_arm64.whl", hash = "sha256:9f2897bf7737392ad0d5213ea7b6add72a4edf5679b3153106aeb88b6507b3b9", size = 25331848, upload-time = "2026-08-21T23:27:05.884Z" }, + { url = "https://files.pythonhosted.org/packages/be/4f/1bd37c883b67163e2ca1f60977a399500e6879c15defecac62831c8d078d/scipy-1.18.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:eb0dfcf4e28a99c12c999744a2ff67c9b06200e20401c7c88186e33552a46331", size = 31091484, upload-time = "2026-08-21T23:27:11.051Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c5/ba929d7feb9b2332f96827c12e0e924b61973b59b4dea383b603372c65ce/scipy-1.18.1-cp315-cp315-macosx_12_0_arm64.whl", hash = "sha256:30f464bee641fa8e282577c7dce027308403213c6ca8270bba73285c91024bc5", size = 28725057, upload-time = "2026-08-21T23:27:15.9Z" }, + { url = "https://files.pythonhosted.org/packages/a4/19/68f1c50f609d955d230e66d25d02bd3e1e167ec540232135354fb9a4b9e3/scipy-1.18.1-cp315-cp315-macosx_14_0_arm64.whl", hash = "sha256:1bca3b943fc2567ea49cd02c99abde49da4d5178ec46f624bd8255cda8755beb", size = 20466734, upload-time = "2026-08-21T23:27:20.044Z" }, + { url = "https://files.pythonhosted.org/packages/ef/6d/319fa29b73d1802fa80b32a6eaf3f5be456ef81526da2716a9493bcb5501/scipy-1.18.1-cp315-cp315-macosx_14_0_x86_64.whl", hash = "sha256:c9d18a33309122074ea483dd92dd444189166b8b2ec429fe9ed5ac73c7a0aa23", size = 23089664, upload-time = "2026-08-21T23:27:24.345Z" }, + { url = "https://files.pythonhosted.org/packages/b7/db/30992f9b51a63de671daf3888ffd18378b6cb9ec9f2c972264238ffa7fd6/scipy-1.18.1-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82f201b4c878551d48558337aab270d3c6cca5507b8737c8d8a608d234cccde0", size = 34054035, upload-time = "2026-08-21T23:27:29.409Z" }, + { url = "https://files.pythonhosted.org/packages/91/d4/bf3e735dc0b9d5a8ff45079d2540e17d3aff7a2f0048dd8f552ffd031d2b/scipy-1.18.1-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ac49ea97594532dd44b7136094d35f5440fa06e6d9c6384a74c01764df388c5", size = 35333883, upload-time = "2026-08-21T23:27:34.293Z" }, + { url = "https://files.pythonhosted.org/packages/19/93/12d78ce9f871fe945fca588d32644e6e63f553c2a35c564d73f3b22a3313/scipy-1.18.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:ceb30a00ce7c92d459819443d29ca486d882b83fb6738bdcbb2a1cce94ac5daa", size = 35673124, upload-time = "2026-08-21T23:27:39.059Z" }, + { url = "https://files.pythonhosted.org/packages/70/cd/886219313a1012a48e6ae0ec4f302c837151beb92e1ff0d709ef8fdfc488/scipy-1.18.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:f29633129f9fa7e88a3f0fca835de2d030bfc9643f7799e1a0c46cee24d38fc7", size = 37470753, upload-time = "2026-08-21T23:27:44.435Z" }, + { url = "https://files.pythonhosted.org/packages/17/6c/a776888ce618bee54fbde26172f0f46ac1da70d27b63861797fe78e1904b/scipy-1.18.1-cp315-cp315-win_amd64.whl", hash = "sha256:92c14f5bdbfb6216315ce33e78080474082de8b3830122ba97809bfbe65f75c0", size = 37361483, upload-time = "2026-08-21T23:27:49.334Z" }, + { url = "https://files.pythonhosted.org/packages/ab/09/97b651691322ebee97999b017ffc18a15a0b815103844c97e8da9d469731/scipy-1.18.1-cp315-cp315-win_arm64.whl", hash = "sha256:e402cf31eb68f453dbb2d36fc6d722b33f24a55d68b2ae1d92fa6305ca71c298", size = 25035883, upload-time = "2026-08-21T23:27:53.596Z" }, + { url = "https://files.pythonhosted.org/packages/ed/0f/9ec20467bbabd0d44e2a77d0fd3d124f884b4d67df92af82c91d2d6a486f/scipy-1.18.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:2a0b02f9fc46f8520330c23d45e6560db7e3a0d927232139427637f98943e11d", size = 31474926, upload-time = "2026-08-21T23:27:57.993Z" }, + { url = "https://files.pythonhosted.org/packages/8a/58/dcb79161e56efbedc50079fcd2f5fe427a0ebb53022eb476aa73c015ad8f/scipy-1.18.1-cp315-cp315t-macosx_12_0_arm64.whl", hash = "sha256:1d73131e358976663dd969e1fb4ed1404b815cd977eaaedc3b3a133ba2d81c35", size = 29164940, upload-time = "2026-08-21T23:28:03.062Z" }, + { url = "https://files.pythonhosted.org/packages/71/d3/1eeea80c817fcb8ef7bd4a05a58824977a0e57a375cfc3d7ea7c911c01ad/scipy-1.18.1-cp315-cp315t-macosx_14_0_arm64.whl", hash = "sha256:bff0b729edd992766136b34e39cc76bc2fad905aa58897ee72a9cd000a6d8443", size = 20906742, upload-time = "2026-08-21T23:28:07.642Z" }, + { url = "https://files.pythonhosted.org/packages/54/46/e59350428b6099301a20128108c995e2eb175a43f383af9a346e38824f9b/scipy-1.18.1-cp315-cp315t-macosx_14_0_x86_64.whl", hash = "sha256:10ac20c69d880f77f375db44c22e3e6a644f9fefa291d4cd2fb9790a89fc99fd", size = 23472183, upload-time = "2026-08-21T23:28:12.109Z" }, + { url = "https://files.pythonhosted.org/packages/89/31/cc91623fa98f0621766a0f0aaaadb2c66de74a7ea7e3837164f6e4354260/scipy-1.18.1-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33a834464fdabc0f26a45508df31b3cc5d028e04dbf6c5ed398541418e0a12fe", size = 34130796, upload-time = "2026-08-21T23:28:17.906Z" }, + { url = "https://files.pythonhosted.org/packages/fc/3e/8572ef536957ddb8aa81bb4090d9e25f257e3b4e05d97deb54319deb8a3a/scipy-1.18.1-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49023963c193dacee096301452f223ee24d86ec5807f8df93c0f7221d119e305", size = 35374253, upload-time = "2026-08-21T23:28:23.732Z" }, + { url = "https://files.pythonhosted.org/packages/b5/c6/59fdeffb4f1435299f93d9dc8140b43ad2916e6cfc944be6c3041fcec86d/scipy-1.18.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:d84a09d0dad90ba6525d8ac1c2334b33e64bf3ccfe9e841f02feb867a22681e4", size = 35758543, upload-time = "2026-08-21T23:28:29.431Z" }, + { url = "https://files.pythonhosted.org/packages/cf/d9/135be205d9de8783193aff9cc3bf483a03a38e4b29432c954e8cb66ac14e/scipy-1.18.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:179ce34a8d0fe273d8883ba59e17e052247d08973dfcb743ca52bb1cce2d60b0", size = 37521946, upload-time = "2026-08-21T23:28:35.245Z" }, + { url = "https://files.pythonhosted.org/packages/5c/a2/5b7d5270621ab7cfa3f7766067bf95dc360b5efb6394694e8143b4156e2b/scipy-1.18.1-cp315-cp315t-win_amd64.whl", hash = "sha256:5632e3ae3d09197c446310cd5187de63e28448ce22f0f67b2b93d97503c0c230", size = 37408295, upload-time = "2026-08-21T23:28:40.724Z" }, + { url = "https://files.pythonhosted.org/packages/63/ad/741c19fcb66755ff953daf9243af8480e4bf3d7fbe57583c178c7d2b6b51/scipy-1.18.1-cp315-cp315t-win_arm64.whl", hash = "sha256:eda632a7981f69730d6281f451db9c1c370993a2c0d7ddb43e2a809a2862b83a", size = 25319710, upload-time = "2026-08-21T23:28:45.713Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "threadpoolctl" +version = "3.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/56/97/b4748951a683e369513691c8eea33d100298d556308dcb0ab66b132a67f0/threadpoolctl-3.8.0.tar.gz", hash = "sha256:01080674ad7ac6182429c918a79de92da789bb448acc02fc0b620b1abd6b6137", size = 39424, upload-time = "2026-11-05T11:08:51.112Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/1b/831f2de4840a25db1dd7b1afea433ccafe6bb10aca07e13264f2079462d0/threadpoolctl-3.8.0-py3-none-any.whl", hash = "sha256:f1b49c82704d50f13ffae019c727b0fa4f6791a58f48f06a09c45face1be66da", size = 32091, upload-time = "2026-11-05T11:08:49.773Z" }, +] + +[[package]] +name = "tomli" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/78/9ad63712633ed3ab5cc1a648d863d7e7da371e9425e209555a0fe711b695/tomli-2.5.0.tar.gz", hash = "sha256:264507556cd8b8c8e7c6ee037cdf443a463f03f4c958e57195e3d369711b8ff6", size = 17662, upload-time = "2026-10-07T12:23:37.892Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/ab99b60ee52acd949684febabc3005d0045d0f66bebd9cdebd67372d26dd/tomli-2.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c4dc1c1781f2f716de763d1e9a7b34c6a894e167e291c7c5d16c72f7a9538545", size = 163901, upload-time = "2026-10-07T12:22:15.601Z" }, + { url = "https://files.pythonhosted.org/packages/bc/00/ee01b7ed4579180fff07142d290257f25ba786f23f3ec6005f620933c2f5/tomli-2.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eff8babca5a7999bc137acbc7482a8b7e17ffca5075ab41f5d770ab408c7bfef", size = 163756, upload-time = "2026-10-07T12:22:16.957Z" }, + { url = "https://files.pythonhosted.org/packages/72/c2/4efebf65372f6583185f79799312109dddb61102d47e5c33dcfd1a297aca/tomli-2.5.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:86665cee9c4835b7a7f1e8ec2c719b5258d4dc782887aded5a8ae7352a96843b", size = 268038, upload-time = "2026-10-07T12:22:18.135Z" }, + { url = "https://files.pythonhosted.org/packages/53/07/5850468e925d898abb36038666f9c333a94d2a223e802a8ba5b6d319d23f/tomli-2.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7e369fd63331746182360977b1892bfc215476a30d61612d732425311639f56", size = 276422, upload-time = "2026-10-07T12:22:19.567Z" }, + { url = "https://files.pythonhosted.org/packages/b4/87/f293984cdcf83c054196d4fd3dad44fc68ae55b4b8c44bc76cef360c3150/tomli-2.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7ad1ea345759240d6463efa0ed1c704402752e49aa21476620738d74d72d8aa1", size = 272616, upload-time = "2026-10-07T12:22:20.794Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ce/db582886b3c1219d3fec93ebd669332482e5aee7a91e0f7838d84f2d1759/tomli-2.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:96243987194634bd411066ce40c952e108f86af04db533ecd8ac3ff2a85b1885", size = 276593, upload-time = "2026-10-07T12:22:22.12Z" }, + { url = "https://files.pythonhosted.org/packages/bf/72/7619b87dea4261fc27dd7b54c4461c129c1f7d9bb7ba3aec89c797a431b8/tomli-2.5.0-cp311-cp311-win32.whl", hash = "sha256:610b27d99f28ec5f191c7064a48f3ddb179a1fe6ca73d571483ae859f57b605e", size = 101830, upload-time = "2026-10-07T12:22:23.651Z" }, + { url = "https://files.pythonhosted.org/packages/1e/74/220106da34502304b6751a2a9b8a9fbca6c3fd47e737a2e2e3da7c61c9db/tomli-2.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:c804ae44fe7b4bab5da295e4f980a1ff04670bca9d23fe0a4e887e08ebd741a8", size = 112742, upload-time = "2026-10-07T12:22:24.972Z" }, + { url = "https://files.pythonhosted.org/packages/27/99/7d9c8b41837a7773613e169504147375c157a290167aa59ad74a085f521f/tomli-2.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:cfac177ebd6236003846ea339981f71457cb6eb748f23381eb257e45092e3980", size = 109332, upload-time = "2026-10-07T12:22:26.117Z" }, + { url = "https://files.pythonhosted.org/packages/52/ed/7baa86f87493646a594de388c7c1c40a39dd0461f7e9c0359cbeefc91fe8/tomli-2.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f4a40d03fb9f63424f0979855bdeaf44dd7696b8d59501822c10ed30ba532df", size = 164854, upload-time = "2026-10-07T12:22:27.444Z" }, + { url = "https://files.pythonhosted.org/packages/a5/b1/44c0341f2224397855723c7a8a39f718ea6fcbcc3dacc66e5aeca0f334e3/tomli-2.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9ebf8d19b17bd0daeb7b7dec81a946a439b753942fd0210d6e96c532249eea6b", size = 164074, upload-time = "2026-10-07T12:22:28.679Z" }, + { url = "https://files.pythonhosted.org/packages/23/04/e2d5b7d3fba47adedb23de616c16d428ea076c79a3d8e1d95d649ffe197e/tomli-2.5.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf0b5e8e0f68ebb494356e577c06c139161efd8d3b9050f93b39b7c26cc54ff0", size = 274274, upload-time = "2026-10-07T12:22:29.804Z" }, + { url = "https://files.pythonhosted.org/packages/43/90/6090e706ff27a6f89f4a40578e3324b95c3cd8c4150868aabf33a8f414c3/tomli-2.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cf74416bdc94ae458b14e37286c1073081850ac8459a00d0c5efef5d44294c6", size = 286435, upload-time = "2026-10-07T12:22:31.297Z" }, + { url = "https://files.pythonhosted.org/packages/0a/9e/a2c40768df16c408f22430afb0a73e9d7e5f79c950884954649d1146b74d/tomli-2.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:61ea1ebe1e55a34ea8199cc8dbff398d35027b82271c8ac4802fd3a1fd5b1bcc", size = 278119, upload-time = "2026-10-07T12:22:32.601Z" }, + { url = "https://files.pythonhosted.org/packages/12/25/3c0cb485b98e9cfac495629b1c93c87ccf0b72fbe9d2689fd8fe62c6d5a3/tomli-2.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ed53f7e89bb04f6d9e8e7799112360b0c4d5cbff067de0814c98c37c39b920f7", size = 286177, upload-time = "2026-10-07T12:22:33.745Z" }, + { url = "https://files.pythonhosted.org/packages/77/8b/0144c65f0e37e51c18d04ae15c21b19431c165002d0131fe9aa8b0b8b1e8/tomli-2.5.0-cp312-cp312-win32.whl", hash = "sha256:e7ad033e27a516a233bea839cdb77b80146facb3b4f40bf02cd0cac165cdd5c2", size = 102760, upload-time = "2026-10-07T12:22:34.887Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/5d6d8f42fc9a05fce69354e00ff256484192f5f2fc9a2165718fa0de61ec/tomli-2.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:bd05de8c1698f8413dd7d869492693a0bf2211543b787ac78cd5e7536af1a6d7", size = 112722, upload-time = "2026-10-07T12:22:36.162Z" }, + { url = "https://files.pythonhosted.org/packages/30/65/df18032218db0fb9b769fb23c8039a051f15c811993995ea04c350273a32/tomli-2.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:069435bd5480429b98c5e5afb02ab21c219b6f0064680671c6dc0d46817346ea", size = 109534, upload-time = "2026-10-07T12:22:37.296Z" }, + { url = "https://files.pythonhosted.org/packages/42/e5/51736d70da209350969e15aca5c5ab6e2ce1ea87a0a892a6c13aec172a86/tomli-2.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:943276cf269e0071948d9ff697159c1735e623c1151d88abb09b74659ef0cbea", size = 163328, upload-time = "2026-10-07T12:22:38.373Z" }, + { url = "https://files.pythonhosted.org/packages/ec/55/086f80dab4ab497602644274e6dea7ec5dd0b4e262e443a8ad3bb7edee2d/tomli-2.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:463b16086865b97facd8d0b3fb4cb7c544e3f58d2a69dc3113d6db9653fdb043", size = 162246, upload-time = "2026-10-07T12:22:39.673Z" }, + { url = "https://files.pythonhosted.org/packages/aa/eb/3ecc94459f3635c92321f4e7bde571323fdb2267c50e19e3188a281eae3b/tomli-2.5.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1245a6638fc4bb0a60af38a7d45413db34a13842027c77597c712c998c62fdf0", size = 272655, upload-time = "2026-10-07T12:22:41.08Z" }, + { url = "https://files.pythonhosted.org/packages/c0/d7/494fd1f0c37a621f1ad9975c2efadb523e8101f144ed6edb2e7fe64738f2/tomli-2.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d8bac3d603c97e6854424e5b2b5b741bdbde387e09f162fb0446812b4a8362b", size = 283595, upload-time = "2026-10-07T12:22:42.222Z" }, + { url = "https://files.pythonhosted.org/packages/70/51/bb8d62b1317e6640866f6949b2d5855e5300f2c99d46de1cd245570bba65/tomli-2.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:21e4cae4114aba25aa0d4f85cdf486d290fb35c0954d7bba536248da64d43066", size = 276253, upload-time = "2026-10-07T12:22:43.625Z" }, + { url = "https://files.pythonhosted.org/packages/66/f4/f46bd7f0763cd47de2db697dca9257c6a4adfd1a93b018cc75c8190ed5a8/tomli-2.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bbaefc84548d754be821bba7c4141c4787dda182f9e77f2f87b71213529efa7b", size = 283582, upload-time = "2026-10-07T12:22:44.983Z" }, + { url = "https://files.pythonhosted.org/packages/ac/03/70f2bcb2923a6db37818d917e124270a7f4cfd38ea576f5aa753a91c0ef5/tomli-2.5.0-cp313-cp313-win32.whl", hash = "sha256:abdbf6313b8d9efe157edeb7ab6eae4de064b1300ad31abf73755154b30abe68", size = 102628, upload-time = "2026-10-07T12:22:46.508Z" }, + { url = "https://files.pythonhosted.org/packages/dc/98/d52024bb5b0ff68b4f0d276d867f634c84a67319a7e9f6b7708a37742333/tomli-2.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd4dc129784e0c5335bd4e61dfcc4487499a013419e655cf2da1d091b7e0efdc", size = 113301, upload-time = "2026-10-07T12:22:47.647Z" }, + { url = "https://files.pythonhosted.org/packages/6f/f2/540db3a70572a8c23a28aba3e9c358ce0ffffbafc990905c1343aa265b31/tomli-2.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:69491c143d2fe063046e0301e62a810bed338fa4d1ce0fd870c27dc1e09b0d84", size = 109744, upload-time = "2026-10-07T12:22:48.925Z" }, + { url = "https://files.pythonhosted.org/packages/e4/49/caf6b307766eb9567664a8707e9d6be5fcc0e8903f18781c6677a60d80c7/tomli-2.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d3182ee2d887e507bd67319a0a61105d1dd33facc111329559a233b772c1a105", size = 162899, upload-time = "2026-10-07T12:22:50.088Z" }, + { url = "https://files.pythonhosted.org/packages/d3/c8/68cfce773a2733a49c74f99d627fb461bd990756860099eac25617889585/tomli-2.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:521345fd1f19d45b8df87657aaa38b6f2ca3800059fadf428e7ebf479a383646", size = 162080, upload-time = "2026-10-07T12:22:51.558Z" }, + { url = "https://files.pythonhosted.org/packages/7e/b2/e5bb8651fdad593f670501a7d718b1a7f73f064d44dea15e04c04dfef45d/tomli-2.5.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e95c7614e705bfe2b04b27aa124adec59752d15813df37e2156747cab3a006b", size = 273380, upload-time = "2026-10-07T12:22:52.918Z" }, + { url = "https://files.pythonhosted.org/packages/8d/d2/9e2d7f8b1dfe0e2b34c245986ebd55c4c553ea4ce6c47c443b332673253f/tomli-2.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ac2027d37c3afbdf4bdd377f2676f6f1d2122a5be1f1137b49dced590b37e75", size = 283228, upload-time = "2026-10-07T12:22:54.173Z" }, + { url = "https://files.pythonhosted.org/packages/ba/df/ec7b876b7b1a2718bd74a3743c076fff565b04029ba33e8f61fac262739f/tomli-2.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c414be4ed9d3cac80c42e348fa5a956117d1a48227f48026e31f59cb4a7671eb", size = 277189, upload-time = "2026-10-07T12:22:55.342Z" }, + { url = "https://files.pythonhosted.org/packages/7d/7b/e192d9eed0b9cb80da799f4d77052297fb9a2c3cc9b19f571f56ea88add6/tomli-2.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9b03d7dc168353b4132965bde20feceabaa470e570c6f59660dfae59b1f9eeb3", size = 283632, upload-time = "2026-10-07T12:22:56.735Z" }, + { url = "https://files.pythonhosted.org/packages/84/50/ff94454e75461d75623e47401ed323d65c10aab8fe9033242c20cd2fdf32/tomli-2.5.0-cp314-cp314-win32.whl", hash = "sha256:6f041843c4d3a37245c0c056fd955b186bf8b1fb85690cbe40b81230891dc34b", size = 103535, upload-time = "2026-10-07T12:22:58.084Z" }, + { url = "https://files.pythonhosted.org/packages/54/0b/bdacf05f963bd6026ebf6eeb0beda847d1d60e03e440725c64a4e08a0afd/tomli-2.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:f4b653094e18f9031102d3a1da5c729c8f222d85225b18037dac621695e46e1a", size = 114621, upload-time = "2026-10-07T12:22:59.2Z" }, + { url = "https://files.pythonhosted.org/packages/61/99/53f438fa6ae4f9d4ed0ddde3e7242b3bdc34b48c8f9948b72b9e9b127676/tomli-2.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:3f89d10c1ff6a38d992c27fc8a4816af71a909e08a40ec66934240b1e74347c3", size = 111572, upload-time = "2026-10-07T12:23:00.479Z" }, + { url = "https://files.pythonhosted.org/packages/b9/20/1f88f19427d380a40e90a770e087489eaafe4aeee070ae88ed2bbec00acd/tomli-2.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:e9e15b4a6c7dd6b85b5fbab29488a73f1f70de516942308daa266bf0e0aeb0d4", size = 171814, upload-time = "2026-10-07T12:23:01.914Z" }, + { url = "https://files.pythonhosted.org/packages/d0/56/cbe5079c9f9a54b9b3e27fc82f08f3cb36edee75561679f53d2380c801d6/tomli-2.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e12bbcd32897272fb05929110362ae9ff4c1b9bb26bd9e971e71dcd3275b4c3d", size = 171324, upload-time = "2026-10-07T12:23:03.18Z" }, + { url = "https://files.pythonhosted.org/packages/2b/30/1d53fd3b0f1cb3ba542e345ec32c26aefdddc4e829e4f3429af8a4f27782/tomli-2.5.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20aa36de8f2cf87237143bc1fa1aae8d6612c09118f4da21c6a684db5dd1f6f9", size = 297441, upload-time = "2026-10-07T12:23:04.345Z" }, + { url = "https://files.pythonhosted.org/packages/66/d9/0800acb6a111686f764c1b91ef15cc42a20a66a46013bb42220f1d2c61c1/tomli-2.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22185fad8a1e622f064e78008018a0dd3323550dcb479cb7a1d296888d74024f", size = 307476, upload-time = "2026-10-07T12:23:05.671Z" }, + { url = "https://files.pythonhosted.org/packages/e8/63/30a8f3cd51b5bec37f04744bad0b0dc6160df84aad4f27b0e9283d66f221/tomli-2.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:984012f71908165449a951de2050d52f276bfe3aa5d5f570f63ddad814370374", size = 296113, upload-time = "2026-10-07T12:23:07.202Z" }, + { url = "https://files.pythonhosted.org/packages/ab/18/0b9ffc597e69c5a1e20a7823cb60d54b39a9f54e91edcb8574f022186758/tomli-2.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f79203b3965b4000e91808aaa7c040206093f2b8bf86f455982f2274c9ccf442", size = 307725, upload-time = "2026-10-07T12:23:08.508Z" }, + { url = "https://files.pythonhosted.org/packages/ab/c7/18f8baae0b5607a60e8e19b4a7fedee43a8ff6458e3896dcbbadeeac9c22/tomli-2.5.0-cp314-cp314t-win32.whl", hash = "sha256:91294a9fb94a75542f6e46e4a2ae709bd8d9b51134098cae5cf3bea5478b6d03", size = 108546, upload-time = "2026-10-07T12:23:09.956Z" }, + { url = "https://files.pythonhosted.org/packages/72/34/4cca9739254130627bde87500b3f2b512154fe2f278efa7e2a5e10ad4bcb/tomli-2.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f15e3e0b835a6d68b10c86bf80a3149780498d6911c93c3ffd1861d19f9200f1", size = 117814, upload-time = "2026-10-07T12:23:11.486Z" }, + { url = "https://files.pythonhosted.org/packages/7d/fb/afa530d47dd80a78fce43beac6bc6e00f84558eafcffbc6f37b21e80d056/tomli-2.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:6664b7ae7af7294256c53960a6103077f4914cec8ff98479c352f622c6f6b2f0", size = 115188, upload-time = "2026-10-07T12:23:12.728Z" }, + { url = "https://files.pythonhosted.org/packages/66/98/316fdc00f8c0939e6fe50461dd343c162d3ad51d1286eb25b7db54361d50/tomli-2.5.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:a525685c2f97da40762b8695eb7aa0af4c8344ca1905c73e4e29cb04d34607dc", size = 162775, upload-time = "2026-10-07T12:23:13.941Z" }, + { url = "https://files.pythonhosted.org/packages/c5/22/7b10fa5bb01c9539f53f69b619361b19350acc73657772ea7ac70ba309a8/tomli-2.5.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:9dbb18c1cfb2f6517942fc9314437f66aa06d94436ffb1f06102ef3572f35276", size = 161406, upload-time = "2026-10-07T12:23:15.215Z" }, + { url = "https://files.pythonhosted.org/packages/9c/e7/1a069d86dfd20f1f84f71c63faed9f83c1d890bc06c27d82dc7d888fb573/tomli-2.5.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:752e8b1aa6a4367ef8bf6a1a1e005540f7ed055ba36d7193796812ca5404eb52", size = 273855, upload-time = "2026-10-07T12:23:16.471Z" }, + { url = "https://files.pythonhosted.org/packages/ae/83/d1ef43d1687d092ab9c235455c76e6e709483b346b056f086095c7c263a5/tomli-2.5.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c47300f9bf791808f77d82747691c4bb09cb14bdf3060cca99b42cdc4361d5a7", size = 284910, upload-time = "2026-10-07T12:23:18.166Z" }, + { url = "https://files.pythonhosted.org/packages/cc/05/f4d9cf7de61822ece0c3873f30d291e324911c71a378b8bfe5ced13fd9f5/tomli-2.5.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:19b0dd8749f4ea2f112c5fcfb3c5248390c899d7e2e173f1d91abee1fa0ff391", size = 277723, upload-time = "2026-10-07T12:23:19.355Z" }, + { url = "https://files.pythonhosted.org/packages/42/28/78262493141fa543151cf005760c3cb01d09fc28a11f993c05109902cb8c/tomli-2.5.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:57b1c3b01fab802e2899bc3d168dca320e14165e2fd9fd584760fb4ca5826859", size = 285115, upload-time = "2026-10-07T12:23:20.698Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b9/e1dab9a30bcb677b5cc5cee810609cfd64f24306a3055767dd3fda00b1e0/tomli-2.5.0-cp315-cp315-win32.whl", hash = "sha256:667e521b37a6c5ccaa044202c235b530f90177ffe2cd4a64ecc213c7dd535feb", size = 103475, upload-time = "2026-10-07T12:23:21.941Z" }, + { url = "https://files.pythonhosted.org/packages/4c/bd/31a3790c11d6ea95fcf5e6022ac0f8d0543c9b61120b730fc481bd43d3b4/tomli-2.5.0-cp315-cp315-win_amd64.whl", hash = "sha256:d747252933c8a65ef6bd8da0fbb7ce28a90eb6119d8cd00772cd528aa07b68d5", size = 114589, upload-time = "2026-10-07T12:23:23.098Z" }, + { url = "https://files.pythonhosted.org/packages/47/a2/4f6310fa699364f0e3af7ee3af88dddd9af066d33e716a0265bbe2b3ea84/tomli-2.5.0-cp315-cp315-win_arm64.whl", hash = "sha256:75dbcde8751b0a960aa3de173aa5e894d590755c6d7758b7e774c06f1dc3cbdd", size = 111493, upload-time = "2026-10-07T12:23:24.233Z" }, + { url = "https://files.pythonhosted.org/packages/68/14/00853f0b396d8971107ae1921bb5b322fdee1650d2f16bf06c20adb532e5/tomli-2.5.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:2419c2a189551987b59d80e63ec355671283336f41c6b9b89462df679c7d0c57", size = 171380, upload-time = "2026-10-07T12:23:25.512Z" }, + { url = "https://files.pythonhosted.org/packages/89/ad/fa6949321dadee46b27363974fb197b94c911c3b0f7a5fd26d7dc18fc2a0/tomli-2.5.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:0dc598040da8d42cf20f0be588ed7004f46db12a0ac6c32e03a59dccedaaadcd", size = 170553, upload-time = "2026-10-07T12:23:26.855Z" }, + { url = "https://files.pythonhosted.org/packages/53/aa/3056c919eb3e084df3752b2cf5f865dcc04af0b27dba2f66d7b28af4633a/tomli-2.5.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49096930c8d886c9bbdab62d2d0d17ce823ddeea522309a190b36245d5b49e01", size = 294428, upload-time = "2026-10-07T12:23:28.132Z" }, + { url = "https://files.pythonhosted.org/packages/96/b2/faeeb5d8769ea3832021d73e892c8391eae7b4b4f8b55a789127bd8b18a9/tomli-2.5.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8ade5023067f99fe72b88accd30d0ea05a158e9e32a11f124e731ea9695313f", size = 304909, upload-time = "2026-10-07T12:23:29.381Z" }, + { url = "https://files.pythonhosted.org/packages/f6/52/f094c09e73fb654b621716d019acb5d29bdfd1be01df80c281d552bda48d/tomli-2.5.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:b69564772b5c8f22ea5f498dff08cfa825045b4d4c4400529000bdf818aa3b2a", size = 293220, upload-time = "2026-10-07T12:23:30.608Z" }, + { url = "https://files.pythonhosted.org/packages/86/f5/0c30541078ca4b505ce3bd76ed931facbfec524dd018535d691d1af0a6d2/tomli-2.5.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:8ff3a2ca028c7eee0c777f9a092038d0a594a9fa04e215f929a22c329e2cb142", size = 305705, upload-time = "2026-10-07T12:23:32.181Z" }, + { url = "https://files.pythonhosted.org/packages/05/74/590e7d19d6a118fc5cc5704ff358e21d95b8573f6b9443b1519f29ca8825/tomli-2.5.0-cp315-cp315t-win32.whl", hash = "sha256:62fc1bc8eb03e3a9cadfca713d65614ed8e09d974a283295ffe3a831976b4dc5", size = 108432, upload-time = "2026-10-07T12:23:33.496Z" }, + { url = "https://files.pythonhosted.org/packages/1c/b8/63a75cfb27a17c38550e44025d3a6e7be64516fd8608a3b75703bf37d81b/tomli-2.5.0-cp315-cp315t-win_amd64.whl", hash = "sha256:f3fcbc57b1791fa6cbe5d8434179d51de12be1a4811469529f47f6e7487a2571", size = 117281, upload-time = "2026-10-07T12:23:34.648Z" }, + { url = "https://files.pythonhosted.org/packages/72/01/e8c1debb2173973372934c68fc8e46170ab60ef23ed4592dff4dec6e8993/tomli-2.5.0-cp315-cp315t-win_arm64.whl", hash = "sha256:d2ba24db8a9376921b5e87b4762b9adb0f3f1deaea68f2b8b0bb2c11efb9c3e7", size = 115069, upload-time = "2026-10-07T12:23:35.77Z" }, + { url = "https://files.pythonhosted.org/packages/60/3f/3e3f8fd0919249b0200c80fbc4f9a1e70be19f9883da71dfb7f8b9ab8aca/tomli-2.5.0-py3-none-any.whl", hash = "sha256:32a7b79ac57a2e83670ce329ccf675798bc5a2094783a63676866b70503f2e2b", size = 14765, upload-time = "2026-10-07T12:23:36.875Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, +] + +[[package]] +name = "verbecc" +version = "2.1.0" +source = { editable = "." } +dependencies = [ + { name = "backports-strenum", marker = "python_full_version < '3.11'" }, + { name = "cython" }, + { name = "dataclass-wizard", extra = ["yaml"] }, + { name = "importlib-resources", marker = "python_full_version < '3.13'" }, + { name = "jsbeautifier" }, + { name = "lxml" }, + { name = "lxml-stubs" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "pyaml" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scikit-learn", version = "1.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "scipy", version = "1.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] + +[package.optional-dependencies] +dev = [ + { name = "pytest" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "backports-strenum", marker = "python_full_version < '3.11'", specifier = ">=1.3.1,<2.0" }, + { name = "cython", specifier = ">=3.0.11" }, + { name = "dataclass-wizard", extras = ["yaml"], specifier = ">=0.39.1" }, + { name = "importlib-resources", marker = "python_full_version < '3.13'", specifier = ">=6.4.5" }, + { name = "jsbeautifier", specifier = ">=1.15.4,<2.0" }, + { name = "lxml", specifier = ">=6.0.2" }, + { name = "lxml-stubs", specifier = ">=0.5.1" }, + { name = "numpy", marker = "python_full_version < '3.10'", specifier = ">=1.26.4" }, + { name = "numpy", marker = "python_full_version >= '3.10'", specifier = ">=2.0.2" }, + { name = "pyaml", specifier = ">=25.7.0" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=9.1.1" }, + { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1.0" }, + { name = "scikit-learn", marker = "python_full_version < '3.11'", specifier = ">=1.6.1" }, + { name = "scikit-learn", marker = "python_full_version >= '3.11'", specifier = ">=1.7.2" }, + { name = "scipy", marker = "python_full_version < '3.10'", specifier = ">=1.13.1" }, + { name = "scipy", marker = "python_full_version >= '3.10'", specifier = ">=1.15.3" }, +] +provides-extras = ["dev"] diff --git a/verbecc/__init__.py b/verbecc/__init__.py deleted file mode 100755 index e7ff8d4..0000000 --- a/verbecc/__init__.py +++ /dev/null @@ -1,88 +0,0 @@ -from verbecc.src.conjugator.complete_conjugator import CompleteConjugator -from verbecc.src.conjugator.mood_conjugator import MoodConjugator -from verbecc.src.conjugator.tense_conjugator import TenseConjugator -from verbecc.src.defs.constants import grammar_defines -from verbecc.src.defs.constants.grammar_defines import SUPPORTED_LANGUAGES -from verbecc.src.defs.constants import localization -from verbecc.src.defs.types.config.verbecc_config import VerbeccConfig -from verbecc.src.defs.types.config.jsbeautifier_opts import JSBeautifierOpts -from verbecc.src.utils.config.verbecc_config_util import VerbeccConfigUtil -from verbecc.src.utils.logging_utils import LoggingUtils -from verbecc.src.defs.types.conjugation import CompleteConjugation -from verbecc.src.defs.types.conjugation.complete_conjugation import ( - CompleteConjugationData, -) -from verbecc.src.defs.types.conjugation.conjugation import Conjugation -from verbecc.src.defs.types.conjugation.conjugation_data import ConjugationData -from verbecc.src.defs.types.exceptions.conjugations_parser_error import ( - ConjugationsParserError, -) -from verbecc.src.defs.types.data.conjugation_template import ConjugationTemplate -from verbecc.src.defs.types.exceptions.conjugation_template_error import ( - ConjugationTemplateError, -) -from verbecc.src.defs.types.exceptions.conjugator_error import ConjugatorError -from verbecc.src.defs.types.data.element import Element -from verbecc.src.defs.types.gender import Gender -from verbecc.src.defs.types.exceptions.invalid_lang_error import InvalidLangError -from verbecc.src.defs.types.exceptions.invalid_mood_error import InvalidMoodError -from verbecc.src.defs.types.exceptions.invalid_tense_error import InvalidTenseError -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.defs.types.lang_specific_options import LangSpecificOptions -from verbecc.src.defs.types.lang_specific_options.lang.es.lang_specific_options_es import ( - LangSpecificOptionsEs, -) -from verbecc.src.defs.types.mood.mood import Mood -from verbecc.src.defs.types.mood.lang.mood_ca import MoodCa -from verbecc.src.defs.types.conjugation.mood_conjugation import MoodConjugation -from verbecc.src.defs.types.conjugation.mood_conjugation_data import MoodConjugationData -from verbecc.src.defs.types.mood.lang.mood_es import MoodEs -from verbecc.src.defs.types.mood.lang.mood_fr import MoodFr -from verbecc.src.defs.types.mood.lang.mood_it import MoodIt -from verbecc.src.defs.types.mood.lang.mood_pt import MoodPt -from verbecc.src.defs.types.mood.lang.mood_ro import MoodRo -from verbecc.src.defs.types.mood.moods import Moods -from verbecc.src.defs.types.mood.mood_factory import MoodFactory -from verbecc.src.defs.types.conjugation.moods_conjugation import MoodsConjugation -from verbecc.src.defs.types.conjugation.moods_conjugation_data import ( - MoodsConjugationData, -) -from verbecc.src.defs.types.data.mood_template import MoodTemplate -from verbecc.src.defs.types.number import Number -from verbecc.src.defs.types.person import Person -from verbecc.src.defs.types.data.person_ending import PersonEnding -from verbecc.src.defs.types.exceptions.template_not_found_error import ( - TemplateNotFoundError, -) -from verbecc.src.defs.types.tense.tense import Tense -from verbecc.src.defs.types.tense.lang.tense_ca import TenseCa -from verbecc.src.defs.types.conjugation.tense_conjugation import TenseConjugation -from verbecc.src.defs.types.conjugation.tense_conjugation_data import ( - TenseConjugationData, -) -from verbecc.src.defs.types.tense.lang.tense_es import TenseEs -from verbecc.src.defs.types.tense.lang.tense_fr import TenseFr -from verbecc.src.defs.types.tense.lang.tense_it import TenseIt -from verbecc.src.defs.types.tense.lang.tense_pt import TensePt -from verbecc.src.defs.types.tense.lang.tense_ro import TenseRo -from verbecc.src.defs.types.tense.tenses import Tenses -from verbecc.src.defs.types.tense.tense_factory import TenseFactory -from verbecc.src.defs.types.data.tense_template import TenseTemplate -from verbecc.src.defs.types.data.verb import Verb -from verbecc.src.defs.types.conjugation.verb_info import VerbInfo -from verbecc.src.defs.types.conjugation.verb_info_data import VerbInfoData -from verbecc.src.defs.types.exceptions.verb_not_found_error import VerbNotFoundError -from verbecc.src.defs.types.data.verbs import Verbs -from verbecc.src.defs.types.lang_specific_options.lang.es.voseo_options import ( - VoseoOptions, -) -from verbecc.src.defs.types.pronoun.pronoun import Pronoun -from verbecc.src.defs.types.pronoun.pronouns import Pronouns -from verbecc.src.defs.types.pronoun.pronoun_factory import PronounFactory -from verbecc.src.defs.types.pronoun.lang.pronoun_ca import PronounCa -from verbecc.src.defs.types.pronoun.lang.pronoun_en import PronounEn -from verbecc.src.defs.types.pronoun.lang.pronoun_es import PronounEs -from verbecc.src.defs.types.pronoun.lang.pronoun_fr import PronounFr -from verbecc.src.defs.types.pronoun.lang.pronoun_it import PronounIt -from verbecc.src.defs.types.pronoun.lang.pronoun_pt import PronounPt -from verbecc.src.defs.types.pronoun.lang.pronoun_ro import PronounRo diff --git a/verbecc/src/defs/types/config/logging/__init__.py b/verbecc/src/defs/types/config/logging/__init__.py deleted file mode 100644 index e27ca25..0000000 --- a/verbecc/src/defs/types/config/logging/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from verbecc.src.defs.types.config.logging.logging_config import LoggingConfig -from verbecc.src.defs.types.config.logging.formatter_config import FormatterConfig -from verbecc.src.defs.types.config.logging.stream_handler_config import ( - StreamHandlerConfig, -) -from verbecc.src.defs.types.config.logging.file_handler_config import FileHandlerConfig -from verbecc.src.defs.types.config.logging.handler_config import HandlerConfig -from verbecc.src.defs.types.config.logging.logger_config import LoggerConfig diff --git a/verbecc/src/defs/types/config/logging/logging_config.py b/verbecc/src/defs/types/config/logging/logging_config.py deleted file mode 100644 index 1c878c1..0000000 --- a/verbecc/src/defs/types/config/logging/logging_config.py +++ /dev/null @@ -1,25 +0,0 @@ -from dataclasses import dataclass, field -from typing import Dict, Optional - -from verbecc.src.defs.types.config.logging.formatter_config import FormatterConfig -from verbecc.src.defs.types.config.logging.stream_handler_config import ( - StreamHandlerConfig, -) -from verbecc.src.defs.types.config.logging.file_handler_config import FileHandlerConfig -from verbecc.src.defs.types.config.logging.handler_config import HandlerConfig -from verbecc.src.defs.types.config.logging.logger_config import LoggerConfig - - -@dataclass -class LoggingConfig: - version: int = field(default=1) - disable_existing_loggers: bool = field(default=False) - formatters: Dict[str, FormatterConfig] = field(default_factory=dict) - handlers: Dict[str, HandlerConfig] = field( - default_factory=lambda: { - "consoleHandler": StreamHandlerConfig(), - "fileHandler": FileHandlerConfig(), - } - ) - loggers: Dict[str, LoggerConfig] = field(default_factory=dict) - root: Optional[LoggerConfig] = field(default_factory=LoggerConfig) diff --git a/verbecc/src/defs/types/conjugation/__init__.py b/verbecc/src/defs/types/conjugation/__init__.py deleted file mode 100644 index 4bd63d1..0000000 --- a/verbecc/src/defs/types/conjugation/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -from verbecc.src.defs.types.conjugation.conjugation import Conjugation -from verbecc.src.defs.types.conjugation.conjugation_data import ConjugationData -from verbecc.src.defs.types.conjugation.abstract_conjugation import AbstractConjugation -from verbecc.src.defs.types.conjugation.verb_info import VerbInfo -from verbecc.src.defs.types.conjugation.verb_info_data import VerbInfoData -from verbecc.src.defs.types.conjugation.tense_conjugation import TenseConjugation -from verbecc.src.defs.types.conjugation.tense_conjugation_data import ( - TenseConjugationData, -) -from verbecc.src.defs.types.conjugation.mood_conjugation import MoodConjugation -from verbecc.src.defs.types.conjugation.mood_conjugation_data import MoodConjugationData -from verbecc.src.defs.types.conjugation.mood_conjugation_util import MoodConjugationUtil -from verbecc.src.defs.types.conjugation.moods_conjugation import MoodsConjugation -from verbecc.src.defs.types.conjugation.moods_conjugation_data import ( - MoodsConjugationData, -) -from verbecc.src.defs.types.conjugation.complete_conjugation import CompleteConjugation -from verbecc.src.defs.types.conjugation.complete_conjugation_data import ( - CompleteConjugationData, -) diff --git a/verbecc/src/defs/types/conjugation/complete_conjugation_data.py b/verbecc/src/defs/types/conjugation/complete_conjugation_data.py deleted file mode 100644 index 0fdc2c1..0000000 --- a/verbecc/src/defs/types/conjugation/complete_conjugation_data.py +++ /dev/null @@ -1,8 +0,0 @@ -from typing import Dict, Union - -from verbecc.src.defs.types.conjugation.verb_info_data import VerbInfoData -from verbecc.src.defs.types.conjugation.moods_conjugation_data import ( - MoodsConjugationData, -) - -CompleteConjugationData = Dict[str, Union[VerbInfoData, MoodsConjugationData]] diff --git a/verbecc/src/defs/types/conjugation/mood_conjugation_data.py b/verbecc/src/defs/types/conjugation/mood_conjugation_data.py deleted file mode 100644 index 538fe60..0000000 --- a/verbecc/src/defs/types/conjugation/mood_conjugation_data.py +++ /dev/null @@ -1,8 +0,0 @@ -from typing import Dict - -from verbecc.src.defs.types.tense import Tense -from verbecc.src.defs.types.conjugation.tense_conjugation_data import ( - TenseConjugationData, -) - -MoodConjugationData = Dict[Tense, TenseConjugationData] diff --git a/verbecc/src/defs/types/conjugation/moods_conjugation_data.py b/verbecc/src/defs/types/conjugation/moods_conjugation_data.py deleted file mode 100644 index 27b55de..0000000 --- a/verbecc/src/defs/types/conjugation/moods_conjugation_data.py +++ /dev/null @@ -1,6 +0,0 @@ -from typing import Dict - -from verbecc.src.defs.types.mood import Mood -from verbecc.src.defs.types.conjugation.mood_conjugation_data import MoodConjugationData - -MoodsConjugationData = Dict[Mood, MoodConjugationData] # the "moods" section diff --git a/verbecc/src/defs/types/conjugation/tense_conjugation_data.py b/verbecc/src/defs/types/conjugation/tense_conjugation_data.py deleted file mode 100644 index cbb6bd5..0000000 --- a/verbecc/src/defs/types/conjugation/tense_conjugation_data.py +++ /dev/null @@ -1,5 +0,0 @@ -from typing import List - -from verbecc.src.defs.types.conjugation.conjugation_data import ConjugationData - -TenseConjugationData = List[ConjugationData] diff --git a/verbecc/src/defs/types/conjugation/verb_info_data.py b/verbecc/src/defs/types/conjugation/verb_info_data.py deleted file mode 100644 index 0e70dbe..0000000 --- a/verbecc/src/defs/types/conjugation/verb_info_data.py +++ /dev/null @@ -1,3 +0,0 @@ -from typing import Dict, Union - -VerbInfoData = Dict[str, Union[str, bool, float]] # the "verb" section diff --git a/verbecc/src/defs/types/data/mood_template.py b/verbecc/src/defs/types/data/mood_template.py deleted file mode 100644 index 86c7e9b..0000000 --- a/verbecc/src/defs/types/data/mood_template.py +++ /dev/null @@ -1,21 +0,0 @@ -from typing import Dict - -from verbecc.src.defs.types.data.element import Element -from verbecc.src.defs.types.data.tense_template import TenseTemplate -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang -from verbecc.src.defs.types.mood import Mood -from verbecc.src.defs.types.tense import Tense - - -class MoodTemplate(Element): - def __init__( - self, lang: Lang, mood: Mood, tense_templates: Dict[Tense, TenseTemplate] - ) -> None: - self.lang = lang - self.mood = mood - self.tense_templates = tense_templates - - def __repr__(self) -> str: - return ( - f"lang={self.lang} mood={self.mood} tense_templates={self.tense_templates}" - ) diff --git a/verbecc/src/defs/types/exceptions/__init__.py b/verbecc/src/defs/types/exceptions/__init__.py deleted file mode 100644 index 92865ca..0000000 --- a/verbecc/src/defs/types/exceptions/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -from verbecc.src.defs.types.exceptions.conjugator_error import ConjugatorError -from verbecc.src.defs.types.exceptions.conjugations_parser_error import ( - ConjugationsParserError, -) -from verbecc.src.defs.types.exceptions.conjugation_template_error import ( - ConjugationTemplateError, -) -from verbecc.src.defs.types.exceptions.invalid_lang_error import InvalidLangError -from verbecc.src.defs.types.exceptions.invalid_mood_error import InvalidMoodError -from verbecc.src.defs.types.exceptions.invalid_tense_error import InvalidTenseError -from verbecc.src.defs.types.exceptions.template_not_found_error import ( - TemplateNotFoundError, -) -from verbecc.src.defs.types.exceptions.verb_not_found_error import VerbNotFoundError -from verbecc.src.defs.types.exceptions.verbs_parser_error import VerbsParserError diff --git a/verbecc/src/defs/types/lang_specific_options/__init__.py b/verbecc/src/defs/types/lang_specific_options/__init__.py deleted file mode 100644 index a4513b0..0000000 --- a/verbecc/src/defs/types/lang_specific_options/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from verbecc.src.defs.types.lang_specific_options.lang_specific_options import ( - LangSpecificOptions, -) -from verbecc.src.defs.types.lang_specific_options.lang.es.lang_specific_options_es import ( - LangSpecificOptionsEs, -) -from verbecc.src.defs.types.lang_specific_options.lang_specific_options_factory import ( - LangSpecificOptionsFactory, -) -from verbecc.src.defs.types.lang_specific_options.lang.es.voseo_options import ( - VoseoOptions, -) diff --git a/verbecc/src/defs/types/mood/__init__.py b/verbecc/src/defs/types/mood/__init__.py deleted file mode 100644 index 031ee74..0000000 --- a/verbecc/src/defs/types/mood/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -from verbecc.src.defs.types.mood.mood import Mood -from verbecc.src.defs.types.mood.moods import Moods -from verbecc.src.defs.types.mood.mood_factory import MoodFactory -from verbecc.src.defs.types.mood.lang.mood_ca import MoodCa -from verbecc.src.defs.types.mood.lang.mood_en import MoodEn -from verbecc.src.defs.types.mood.lang.mood_es import MoodEs -from verbecc.src.defs.types.mood.lang.mood_fr import MoodFr -from verbecc.src.defs.types.mood.lang.mood_it import MoodIt -from verbecc.src.defs.types.mood.lang.mood_pt import MoodPt -from verbecc.src.defs.types.mood.lang.mood_ro import MoodRo diff --git a/verbecc/src/defs/types/mood/moods.py b/verbecc/src/defs/types/mood/moods.py deleted file mode 100644 index 02591be..0000000 --- a/verbecc/src/defs/types/mood/moods.py +++ /dev/null @@ -1,17 +0,0 @@ -from verbecc.src.defs.types.mood.lang.mood_ca import MoodCa -from verbecc.src.defs.types.mood.lang.mood_en import MoodEn -from verbecc.src.defs.types.mood.lang.mood_es import MoodEs -from verbecc.src.defs.types.mood.lang.mood_fr import MoodFr -from verbecc.src.defs.types.mood.lang.mood_it import MoodIt -from verbecc.src.defs.types.mood.lang.mood_pt import MoodPt -from verbecc.src.defs.types.mood.lang.mood_ro import MoodRo - - -class Moods: - ca = MoodCa - en = MoodEn - es = MoodEs - fr = MoodFr - it = MoodIt - pt = MoodPt - ro = MoodRo diff --git a/verbecc/src/defs/types/pronoun/__init__.py b/verbecc/src/defs/types/pronoun/__init__.py deleted file mode 100644 index ffba431..0000000 --- a/verbecc/src/defs/types/pronoun/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -from verbecc.src.defs.types.pronoun.pronoun import Pronoun -from verbecc.src.defs.types.pronoun.pronouns import Pronouns -from verbecc.src.defs.types.pronoun.pronoun_factory import PronounFactory -from verbecc.src.defs.types.pronoun.lang.pronoun_ca import PronounCa -from verbecc.src.defs.types.pronoun.lang.pronoun_en import PronounEn -from verbecc.src.defs.types.pronoun.lang.pronoun_es import PronounEs -from verbecc.src.defs.types.pronoun.lang.pronoun_fr import PronounFr -from verbecc.src.defs.types.pronoun.lang.pronoun_it import PronounIt -from verbecc.src.defs.types.pronoun.lang.pronoun_pt import PronounPt -from verbecc.src.defs.types.pronoun.lang.pronoun_ro import PronounRo diff --git a/verbecc/src/defs/types/pronoun/pronouns.py b/verbecc/src/defs/types/pronoun/pronouns.py deleted file mode 100644 index f0da025..0000000 --- a/verbecc/src/defs/types/pronoun/pronouns.py +++ /dev/null @@ -1,17 +0,0 @@ -from verbecc.src.defs.types.pronoun.lang.pronoun_ca import PronounCa -from verbecc.src.defs.types.pronoun.lang.pronoun_en import PronounEn -from verbecc.src.defs.types.pronoun.lang.pronoun_es import PronounEs -from verbecc.src.defs.types.pronoun.lang.pronoun_fr import PronounFr -from verbecc.src.defs.types.pronoun.lang.pronoun_it import PronounIt -from verbecc.src.defs.types.pronoun.lang.pronoun_pt import PronounPt -from verbecc.src.defs.types.pronoun.lang.pronoun_ro import PronounRo - - -class Pronouns: - ca = PronounCa - en = PronounEn - es = PronounEs - fr = PronounFr - it = PronounIt - pt = PronounPt - ro = PronounRo diff --git a/verbecc/src/defs/types/tense/__init__.py b/verbecc/src/defs/types/tense/__init__.py deleted file mode 100644 index 0a2b8ed..0000000 --- a/verbecc/src/defs/types/tense/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -from verbecc.src.defs.types.tense.tense import Tense -from verbecc.src.defs.types.tense.tenses import Tenses -from verbecc.src.defs.types.tense.tense_factory import TenseFactory -from verbecc.src.defs.types.tense.lang.tense_ca import TenseCa -from verbecc.src.defs.types.tense.lang.tense_en import TenseEn -from verbecc.src.defs.types.tense.lang.tense_es import TenseEs -from verbecc.src.defs.types.tense.lang.tense_fr import TenseFr -from verbecc.src.defs.types.tense.lang.tense_it import TenseIt -from verbecc.src.defs.types.tense.lang.tense_pt import TensePt -from verbecc.src.defs.types.tense.lang.tense_ro import TenseRo diff --git a/verbecc/src/defs/types/tense/tenses.py b/verbecc/src/defs/types/tense/tenses.py deleted file mode 100644 index 1aade1a..0000000 --- a/verbecc/src/defs/types/tense/tenses.py +++ /dev/null @@ -1,17 +0,0 @@ -from verbecc.src.defs.types.tense.lang.tense_ca import TenseCa -from verbecc.src.defs.types.tense.lang.tense_en import TenseEn -from verbecc.src.defs.types.tense.lang.tense_es import TenseEs -from verbecc.src.defs.types.tense.lang.tense_fr import TenseFr -from verbecc.src.defs.types.tense.lang.tense_it import TenseIt -from verbecc.src.defs.types.tense.lang.tense_pt import TensePt -from verbecc.src.defs.types.tense.lang.tense_ro import TenseRo - - -class Tenses: - ca = TenseCa - en = TenseEn - es = TenseEs - fr = TenseFr - it = TenseIt - pt = TensePt - ro = TenseRo diff --git a/verbecc/src/parsers/conjugation_template_parser.py b/verbecc/src/parsers/conjugation_template_parser.py deleted file mode 100755 index 360c312..0000000 --- a/verbecc/src/parsers/conjugation_template_parser.py +++ /dev/null @@ -1,45 +0,0 @@ -from lxml import etree -from typing import Dict, Optional - - -from verbecc.src.defs.types.data.conjugation_template import ConjugationTemplate -from verbecc.src.defs.types.data.mood_template import MoodTemplate -from verbecc.src.defs.types.exceptions import ConjugationTemplateError -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang -from verbecc.src.defs.types.mood import Mood, MoodFactory -from verbecc.src.parsers.mood_template_parser import MoodTemplateParser -from verbecc.src.parsers.parser import Parser - - -class ConjugationTemplateParser(Parser): - def __init__(self, lang: Lang) -> None: - self.lang = lang - - def parse(self, elem: Optional[etree._Element] = None) -> ConjugationTemplate: - if elem is None: - raise ValueError("elem must not be None") - if elem.tag != "template": - raise ConjugationTemplateError("Unexpected element") - try: - name_attrib = elem.get("name", default=None) - name = str(name_attrib) - mood_templates: Dict[Mood, MoodTemplate] = {} - for mood_elem in elem: - mood_template = MoodTemplateParser(lang=self.lang).parse(mood_elem) - mood = MoodFactory.from_string(self.lang, mood_elem.tag.lower()) - mood_templates[mood] = mood_template - modify_stem = "" - modify_stem_attrib = elem.get("modify-stem", default=None) - if modify_stem_attrib is not None: - modify_stem = str(modify_stem_attrib) - if modify_stem not in ("strip-accents"): - raise ConjugationTemplateError( - "Invalid 'modify-stem' attribute value '{self.modify_stem}'" - ) - else: - modify_stem = "" - return ConjugationTemplate(self.lang, name, mood_templates, modify_stem) - except AttributeError as e: - raise ConjugationTemplateError( - "Error parsing {}: {}".format(etree.tostring(elem), str(e)) - ) diff --git a/verbecc/src/parsers/mood_template_parser.py b/verbecc/src/parsers/mood_template_parser.py deleted file mode 100755 index 82a40cb..0000000 --- a/verbecc/src/parsers/mood_template_parser.py +++ /dev/null @@ -1,28 +0,0 @@ -from lxml import etree -from typing import Dict, Optional - -from verbecc.src.defs.types.data.mood_template import MoodTemplate -from verbecc.src.defs.types.data.tense_template import TenseTemplate -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 as Lang -from verbecc.src.defs.types.mood import MoodFactory -from verbecc.src.defs.types.tense import TenseFactory -from verbecc.src.defs.types.tense import Tense -from verbecc.src.parsers.parser import Parser -from verbecc.src.parsers.tense_template_parser import TenseTemplateParser - - -class MoodTemplateParser(Parser): - def __init__(self, lang: Lang) -> None: - self.lang = lang - - def parse(self, elem: Optional[etree._Element] = None) -> MoodTemplate: - if elem is None: - raise ValueError - mood = MoodFactory.from_string(self.lang, elem.tag.lower()) - tense_templates: Dict[Tense, TenseTemplate] = {} - for tense_elem in elem: - tense = TenseFactory.from_string(self.lang, tense_elem.tag.lower()) - tense_templates[tense] = TenseTemplateParser( - lang=self.lang, mood=mood - ).parse(tense_elem) - return MoodTemplate(self.lang, mood, tense_templates) diff --git a/verbecc/src/parsers/parser.py b/verbecc/src/parsers/parser.py deleted file mode 100644 index 19b651f..0000000 --- a/verbecc/src/parsers/parser.py +++ /dev/null @@ -1,12 +0,0 @@ -from abc import ABC, abstractmethod -from lxml import etree -from typing import Optional - -from verbecc.src.defs.types.data.element import Element - - -class Parser(ABC): - - @abstractmethod - def parse(self, elem: Optional[etree._Element] = None) -> Element: - pass diff --git a/verbecc/src/parsers/verb_parser.py b/verbecc/src/parsers/verb_parser.py deleted file mode 100755 index 24eeda1..0000000 --- a/verbecc/src/parsers/verb_parser.py +++ /dev/null @@ -1,38 +0,0 @@ -from lxml import etree -from typing import Optional - -from verbecc.src.defs.types.data.verb import Verb -from verbecc.src.defs.types.exceptions import VerbsParserError -from verbecc.src.parsers.parser import Parser - - -class VerbParser(Parser): - def __init__(self) -> None: - pass - - def parse(self, elem: Optional[etree._Element] = None) -> Verb: - if elem is None: - raise ValueError("elem must not be None") - infinitive = "" - template = "" - translation_en = "" - if elem.tag != "v": - raise VerbsParserError("Unexpected element") - try: - infinitive = "" - e = elem.find("i", None) - if e is not None: - infinitive = e.text if e.text is not None else "" - template = "" - e = elem.find("t", None) - if e is not None: - template = e.text if e.text is not None else "" - translation_en = "" - e = elem.find("en", None) - if e is not None: - translation_en = e.text if e.text is not None else "" - except AttributeError as e: - raise VerbsParserError( - "Error parsing {}: {}".format(etree.tostring(elem), str(e)) - ) - return Verb(infinitive, template, translation_en) diff --git a/verbecc/src/parsers/verbs_parser.py b/verbecc/src/parsers/verbs_parser.py deleted file mode 100755 index 352295c..0000000 --- a/verbecc/src/parsers/verbs_parser.py +++ /dev/null @@ -1,59 +0,0 @@ -from __future__ import print_function -from lxml import etree -from importlib_resources import as_file, files - -# import gzip -import os - -# import tempfile -from typing import List - -from verbecc.src.defs.types.data.verb import Verb -from verbecc.src.defs.types.data.verbs import Verbs -from verbecc.src.defs.types.exceptions import VerbsParserError -from verbecc.src.defs.types.lang_code import LangCodeISO639_1 -from verbecc.src.parsers.verb_parser import VerbParser - - -class VerbsParser: - def __init__(self, lang: LangCodeISO639_1 = LangCodeISO639_1.fr) -> None: - self.lang = lang - - def parse(self) -> Verbs: - ret: List[Verb] = [] - parser = etree.XMLParser(encoding="utf-8", remove_blank_text=True, remove_comments=True) # type: ignore - source = files("verbecc.data.xml.verbs").joinpath( - "verbs-{}.xml".format(self.lang) - ) - with as_file(source) as fp: - """ - with gzip.open(fp, "rt") as zf: - with tempfile.NamedTemporaryFile( - prefix=f"/tmp/verbs-{lang}.xml.out.", - suffix=".xml", - mode="wt+", - encoding="utf-8", - delete=True, - ) as tf: - next(zf) # Skips the first line (gzip header plus xml header) - # Regenerate xml header - tf.write('' + os.linesep) - for line in zf: - # there are some null bytes at the end that must be stripped - for byte in line: - if not byte.endswith("\x00"): - tf.write(byte) - tf.flush() - tree = etree.parse(tf.name, parser) # type: ignore - """ - tree = etree.parse(fp, parser) # type: ignore - root = tree.getroot() - root_tag = "verbs-{}".format(self.lang) - if root.tag != root_tag: - raise VerbsParserError("Root XML Tag {} Not Found".format(root_tag)) - for child in root: - if child.tag == "v": - ret.append(VerbParser().parse(child)) # type: ignore - - ret = sorted(ret, key=lambda v: v.infinitive) - return Verbs(self.lang, ret) diff --git a/verbecc/src/utils/config/__init__.py b/verbecc/src/utils/config/__init__.py deleted file mode 100644 index 3a050db..0000000 --- a/verbecc/src/utils/config/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from verbecc.src.utils.config.base_config_util import BaseConfigUtil -from verbecc.src.utils.config.logging_config_util import LoggingConfigUtil -from verbecc.src.utils.config.verbecc_config_util import VerbeccConfigUtil