02.01 — Identité technique d’Ocarina#

pyproject.toml#

[project]
name = "ocarina"
version = "1.1.14"
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.14" : 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-pytest / allure-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.
twine / buildPublication 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 clean / make 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.