mapfarm

entity-core (0.2.3)

Published 2026-09-14 03:31:21 +00:00 by xavier

Installation

pip install --index-url https://git.it.iaos.cc/api/packages/mapfarm/pypi/simple/ --extra-index-url https://pypi.org/simple entity-core

About this package

Brique de résolution d'entités (CDC-ENTITY-CORE-v1, refondue ADR-074) — un résolveur fail-closed sur un canon REÇU (référentiel de tenants, instantané JSON), jamais embarqué. Niveau plateforme / stateless.

entity-core

Brique de résolution d'entités — la sémantique de l'identité d'entité (normalisation fail-closed, alias, marque → propriétaire, garde de cohérence), appliquée à un canon reçu. Toutes les briques de la plateforme (iaos · corpus · harvest · factory · cockpit · fin-core · client-core · catalog-core) l'appellent pour résoudre une désignation d'entité, au lieu de re-coder leur propre liste.

Spécifiée par CDC-ENTITY-CORE-v1 (docs/), refondue par ADR-074 (dépôt iaos, 2026-09-12) : l'entité est une donnée du tenant, jamais une constante du produit.

v0.1 → v0.2, rupture voulue. La v0.1 embarquait le canon d'un tenant particulier (six entités, leurs alias, harvest → hold) : toute instance du produit — y compris celle vendue à un tiers — portait cet organigramme et refusait le sien. La v0.2 ne connaît aucun nom. Les symboles de module CANON, normalize, is_entity, resolve_marque_owner n'existent plus : un consommateur qui ne reçoit pas de canon ne compile pas — c'est le cliquet.

Niveau plateforme / stateless. Aucune base, aucun réseau, aucune dépendance. Ce qui reste de la v0.1 : stateless (le canon est un argument) et offline (un instantané JSON remplace le canon embarqué). Ce qui tombe : global — le canon de la holding n'est pas celui du produit.

Principes (non négociables — CDC §5/§6)

# Règle
R1 / P4 Une seule sémantique de résolution : entity-core. Aucune liste re-codée par dépôt — et aucune liste dans la brique.
R2 Toute frontière d'entrée d'entité normalise via un Canon (route, RAG, INSERT, allow-list).
R3 / G2 Fail-closed : forme inconnue → REFUS ; jamais de devinette ni de pass-through. Un canon vide refuse tout.
G1 Un canon reçu incohérent (alias vers une non-entité, marque qui est aussi une entité, snapshot d'une autre version) est refusé à la construction.
G3 Typo non bénie : toute faute → refus, jamais d'alias ; correction à la source (le référentiel).
R4 / P2 Stateless / plateforme : pas de données par app, pas de cache caché — le consommateur décide d'où vient le canon et quand il le rafraîchit.
R7 Une forme connue mais hors axe entité (marque, société, socle) est refusée par normalize avec sa raison ; une marque se résout par resolve_marque_owner.

Le canon — d'où il vient

from entity_core import Canon

# 1. Depuis le référentiel de tenants (iaos : tables `tenants` + `entity_aliases`).
#    L'appelant filtre sur SON tenant ; la brique ne connaît pas les tenants.
canon = Canon.from_rows(
    tenants=[  # company_id|slug, kind, is_entity, display_name?
        {"company_id": "siege", "kind": "internal", "is_entity": True},
        {"company_id": "ops",   "kind": "internal", "is_entity": True},
        {"company_id": "socle", "kind": "internal", "is_entity": False, "display_name": "socle doc"},
        {"company_id": "client-a", "kind": "external"},   # ignoré : pas dans le canon d'un autre
    ],
    aliases=[  # alias, slug, kind? ∈ {alias (défaut), marque}
        {"alias": "operations", "slug": "ops"},
        {"alias": "brand-x", "slug": "siege", "kind": "marque"},
    ],
)

# 2. Depuis un instantané JSON (consommateurs offline : snapshot versionné dans le dépôt,
#    régénéré depuis GET /api/iaos/v1/tenants).
canon = Canon.from_json(open("canon.json").read())
open("canon.json", "w").write(canon.to_json())   # déterministe, clés triées

# 3. En clair (tests, fixtures).
canon = Canon(entities={"siege", "ops"}, aliases={"operations": "ops"},
              marques={"brand-x": "siege"}, non_entities={"acme": "société"})

# 4. Le canon d'une instance qui ne s'est pas encore déclarée : tout est refusé.
canon = Canon.empty()

Usage

from entity_core import assert_canon
from entity_core.errors import NotAnEntity, UnknownEntity

canon.normalize("Operations")          # → "ops"
canon.normalize("ops")                 # → "ops" (idempotent)
canon.is_entity("brand-x")             # → False (marque : hors axe entité)
canon.resolve_marque_owner("brand-x")  # → "siege"

canon.normalize("opss")                # UnknownEntity  [R3] — typo : refus, jamais d'alias
canon.normalize("acme")                # NotAnEntity    [R7] — société, avec sa raison
canon.normalize("brand-x")             # NotAnEntity    [R7] — « utiliser resolve_marque_owner »

assert_canon(canon, ["siege", "operations"])   # True | CanonDivergence [G4] (CI rouge)

Canon est immuable (__slots__, mappings figés), comparable et hachable par contenu.

Installation (registre PyPI Gitea)

pip install --index-url https://git.it.iaos.cc/api/packages/mapfarm/pypi/simple/ entity-core==0.2.0
# ou, build OFFLINE souverain : pip install --no-deps ./vendor/entity-core

Développement

make install   # pip install -e ".[test]"
make lint      # ruff check + ruff format --check
make test      # pytest — unitaires purs, aucune base
make build     # sdist + wheel

Publication : workflow publish-entity-core.yml (workflow_dispatch) après bump de version.

Requirements

Requires Python: >=3.11
Details
PyPI
2026-09-14 03:31:21 +00:00
6462
Holding MAPFARM
Proprietary
29 KiB
Assets (2)
Versions (5) View all
0.2.3 2026-09-14
0.2.2 2026-09-12
0.2.1 2026-09-12
0.2.0 2026-09-12
0.1.0 2026-06-21