02.01 — Identité technique d’Ocarina#

pyproject.toml#

[project]
name = "ocarina"
version = "1.1.10"
description = "Websites test framework for Igor"
requires-python = ">=3.14"
authors = [{ name="Igor Casanova", email="[REDACTED]" }]
license = "MIT"
license-files = ["LICEN[CS]E*"]
readme = "README.md"

dependencies = ["python-docx>=1.2.0"]
  1. version = "1.1.10" : le projet est en stable 1.x, pas en pré-version.
  2. requires-python = ">=3.14" : le typage générique PEP 695 est utilisé partout.
  3. dependencies = ["python-docx>=1.2.0"] : une seule dépendance d’exécution. Tout le reste est dans dev.
  4. license = "MIT".
  5. description = "Websites test framework for Igor" : pas « for everyone », pas « for humans », explicitement « for Igor ». Cohérent avec ../01-philosophy/05-political-stance.md : « C’est ma voiture. »

Dépendances dev#

[dependency-groups]
dev = [
    "ruff>=0.15.0",
    "mypy>=1.17.0",
    "mypy-extensions>=1.1.0",
    "typing-extensions>=4.14.0",
    "pytest>=9.0.0",
    "pytest-cov>=7.0.0",
    "hypothesis>=6.151.0",
    "pytest-mypy-plugins>=3.1.0",
    "allure-pytest>=2.15.3",
    "allure-python-commons>=2.15.3",
    "pre-commit>=4.5.1",
    "syrupy>=5.1.0",
    "selenium>=4.40.0",
    "playwright>=1.60.0",
    "twine>=6.2.0",
    "build>=1.4.2",
    "prysk>=0.20.0",
]
OutilRôle dans Ocarina
ruffLinter + formatter (remplace flake8 + isort + black). select = ["ALL"].
mypy (+ extensions)Type-checker. strict = true (cf. mypy.ini).
pytestRunner des tests unitaires (le framework est lui-même testé).
pytest-covCouverture de tests (du framework lui-même). Configurée dans pyproject.toml#tool.coverage.
hypothesisProperty-based testing (PBT) → test_invariants_properties.py.
pytest-mypy-pluginsTests statiques de typage (vérifie les erreurs mypy attendues et tests d’inférence de types avec reveal_type).
allure-pytestallure-python-commonsRapport Allure (déployé sur GH Pages).
pre-commitHooks Git locaux (ruff-format).
syrupySnapshot testing (sortie de pretty_print_results, results_to_json).
seleniumPrésent en dev parce qu’Ocarina ne dépend pas de Selenium pour fonctionner, livre juste un adapter Selenium pour être immédiatement opérationnel.
playwrightMême logique : un second adapter livré (depuis la 1.1.3). Ocarina pilote désormais Selenium et Playwright out of the box, derrière les mêmes ports.
twinebuildPublication PyPI.
pryskTests CLI au format cram (.t). Successeur de cram.

mypy.ini#

[mypy]
python_version = 3.14
strict = true

# * ... Allow missing annotations (type inference is cool)
disallow_incomplete_defs = false

# * ... Allow missing annotations (type inference is cool)
disallow_untyped_defs = false
  • strict = true : active --warn-redundant-casts, --warn-unused-ignores, --no-implicit-optional, --check-untyped-defs, etc.
  • Deux exceptions : disallow_incomplete_defs et disallow_untyped_defs désactivés, car l’auteur estime que l’inférence de type de mypy est suffisante quand on n’a pas besoin d’expliciter. Toutes les annotations explicites sont là où elles comptent.

pyproject.toml#tool.ruff#

[tool.ruff.lint]
select = ["ALL"]
ignore = [
    "ANN002", "ANN003", "ANN201",
    "TRY003",
    "C901",
    "D203",    # conflit avec D211
    "D213",    # conflit avec D212
    "COM812",  # conflit avec le formatter ruff (auto-fix)
]

[tool.ruff]
exclude = ["**/.venv/**", "**/bin/**", "**/__init__.py", "**/__bypass_linter__"]
  • select = ["ALL"] : toutes les règles de ruff sont activées (~800).
  • Ignore list courte : six règles seulement, toutes justifiées.
  • B008 NE doit JAMAIS être ignoré (« # "B008", # * ... NEVER ignore this rule without knowing very well what you are doing: https://docs.astral.sh/ruff/rules/function-call-in-default-argument/ »). C’est une note pour ne pas répéter une erreur passée.
  • ****/**bypass_linter**** : convention de répertoire pour héberger du code explicitement non-linté (utile pour des modules expérimentaux internes).

pyproject.toml#tool.pytest.ini_options#

testpaths = ["tests"]
python_files = ["test_*.py"]
norecursedirs = [".*", "__pycache__"]
log_cli = true
log_cli_level = "DEBUG"
log_cli_format = "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
log_cli_date_format = "%Y-%m-%d %H:%M:%S"
addopts = """
--cov=src
--cov-branch
--cov-report=term-missing
--cov-report=html
--cov-report=xml:coverage.xml
"""
RéglageEffet
log_cli = trueTous les logs des tests apparaissent en CLI (utile pour les scénarios qui passent par ILogger).
--cov=src + --cov-branchCouverture par branche, pas seulement par ligne.
--cov-report=html + xmlTrois sorties : terminal, HTML local, XML pour ingestion CI.

pyproject.toml#tool.coverage#

Voir le détail ici : ../04-internal-tests/07-coverage-policy.md. En bref : tout ce qui est shape (custom_types, ports, errors), ce qui requiert un navigateur réel (adapters Selenium), et ce qui est traversé par les cram tests (cli/store, cli/builder), est explicitement omis. Pour ne pas fausser les métriques.

Makefile#

RecetteEffet
make installCrée .venv si absent, pip install -e . --group dev, pre-commit install. Cross-OS (Windows / autres).
make install-on-ciVariante CI : pip install -r requirements-dev.txt puis pip install -e . --no-deps.
make testmake cram-test puis pytest --alluredir=allure-results -vv --hypothesis-show-statistics.
make cram-testprysk tests/cram/ (sur Windows : ne fait rien).
make check-coding-stylemypy + ruff
make mypy-checkmypy src/ tests/
make ruff-checkruff check .
make ruff-formatruff format .
make generate-allureallure generate allure-results -o allure-report
make serve-allureOuvre le rapport Allure dans le navigateur.
make serve-htmlcovIdem pour htmlcov/
make test-uimake test + make generate-allure + make serve-htmlcov + make serve-allure
make update-snapshotspytest --snapshot-update
make cleanmake clean-allureCleanup cross-OS.

allurerc.mjs (taxonomie Allure)#

Depuis la migration vers Allure 3, la taxonomie des failures réside dans allurerc.mjs (le fichier categories.json dédié a disparu). Les catégories sont inchangées :

categories: {
  rules: [
    { name: "Test defects", matchers: { statuses: ["broken"] } },
    {
      name: "Invariant violations",
      matchers: { statuses: ["failed"], message: /.*InvariantViolationError.*/ },
    },
    {
      name: "Assertion errors",
      matchers: { statuses: ["failed"], message: /.*AssertionError.*/ },
    },
    { name: "Skipped", matchers: { statuses: ["skipped"] } },
  ],
}

Quatre catégories, dont par exemple Invariant violations : échecs des tests impliquant validate(...).execute().raise_if_invalid(). Voir ../../04-internal-tests/08-allure-history.md pour la config complète.

.pre-commit-config.yaml#

repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.15.11
    hooks:
      - id: ruff-format

Minimal : juste ruff-format en pre-commit. Les autres outils (ruff check, mypy) sont en CI uniquement, choix qui réduit la friction locale (= éviter de « casser les couilles » des développeurs).

URLs#

[project.urls]
Documentation = "https://mojo-molotov.github.io/ocarina-holy-book"
Homepage      = "https://github.com/mojo-molotov/ocarina"
Issues        = "https://github.com/mojo-molotov/ocarina/issues"

L’URL Documentation pointe vers le Holy Book. Voir ../09-holy-book/

Build backend#

[build-system]
requires = ["hatchling >= 1.26"]
build-backend = "hatchling.build"

hatchling : choix moderne (PEP 517/518), léger, sans config ad hoc.