Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
1028535
2.1.0 modernization wip
bretttolbert Sep 4, 2026
6db47e0
rm model zip files committed by mistake and updated .gitignore
bretttolbert Sep 4, 2026
10f4964
added script __init__.py
bretttolbert Sep 5, 2026
4c12ca5
fix github actions ci install verbecc
bretttolbert Sep 5, 2026
645e7cc
fix tests
bretttolbert Sep 5, 2026
e854ef7
linter fixes wip (incomplete)
bretttolbert Sep 5, 2026
eeaff74
fixed linter errors
bretttolbert Sep 5, 2026
9b3e3fe
migrated from flake8 to ruff
bretttolbert Sep 5, 2026
2561f38
fixed YAMLWizard import issue
bretttolbert Sep 5, 2026
d5c5b28
enabled type stubs, fixed importlib.resources import for py313, py314
bretttolbert Sep 5, 2026
d088b3b
py310 compat
bretttolbert Sep 5, 2026
6873cfd
update changelog
bretttolbert Sep 5, 2026
2fb1684
resolved all type problems in workspace
bretttolbert Sep 5, 2026
859f21c
consistent lang directories structure
bretttolbert Sep 6, 2026
a783b27
removed new NonApiWarn due to performance concerns, corrected nonstan…
bretttolbert Sep 6, 2026
8360917
consistent private method naming (standard)
bretttolbert Sep 6, 2026
fc2f5d1
revert rename of _get_conj_objs
bretttolbert Sep 6, 2026
0cf87f9
finished adding str as valid type for applicable api methods, removed…
bretttolbert Sep 6, 2026
9a758a1
fix typo
bretttolbert Sep 6, 2026
f434444
moving back to orig dir to fix diff
bretttolbert Sep 6, 2026
c6290aa
git renamed files to fix diff
bretttolbert Sep 6, 2026
60cd100
rearranged pyproject.toml deps
bretttolbert Sep 6, 2026
2516106
updated readme (removed python 3.9)
bretttolbert Sep 7, 2026
fc9b96c
updated readme
bretttolbert Sep 12, 2026
14b4476
transitioned from pip to uv, added logo
bretttolbert Nov 7, 2026
2095ff0
wip
bretttolbert Nov 7, 2026
412f3f8
incorporated feedback from PR
bretttolbert Nov 7, 2026
c24cf59
fixed icon font
bretttolbert Nov 8, 2026
0581709
Update XML file paths for testing
bretttolbert Nov 8, 2026
6db3b68
Fix XML serialization and development paths
bretttolbert Nov 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 45 additions & 16 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -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.*
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.14
74 changes: 74 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
71 changes: 71 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -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
92 changes: 83 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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 |
| --- | --- |
Expand All @@ -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 | |

Expand Down
Loading
Loading