---
{
  "title": "@atomic-ehr/codegen: Python FHIR Types",
  "description": "Générez des modèles Pydantic fortement typés à partir de n'importe quel paquet FHIR avec @atomic-ehr/codegen — validation, prise en charge IDE, faisceaux polymorphiques, extensions de types primitifs et intégration fhirpy incluses.",
  "date": "2026-04-20",
  "author": "Aleksandr Penskoi",
  "reading-time": "6 minutes",
  "tags": [
    "FHIR Tools",
    "FHIR Standard",
    "Code Generation",
    "Python"
  ]
}
---
[Le développement FHIR](/articles/using-fhir-to-simplify-healthcare-application-development) en Python signifie généralement se débattre avec des dictionnaires bruts, deviner les noms de champs à partir d'une spécification volumineuse et découvrir des fautes de frappe seulement à l'exécution. Nous avons conçu un générateur qui élimine tout cela.

[`@atomic-ehr/codegen`](https://github.com/atomic-ehr/codegen) produit des modèles [Pydantic](https://docs.pydantic.dev/latest/) fortement typés à partir de n'importe quel paquet FHIR — avec validation, autocomplétion IDE et intégration optionnelle de [fhirpy](https://github.com/beda-software/fhir-py). Il fonctionne avec n'importe quel serveur FHIR, pas seulement [Aidbox](https://www.health-samurai.io/aidbox). Le projet est à code source ouvert (MIT) et nous accueillons les contributions avec plaisir.

Cet article met à jour notre [article précédent sur le SDK Python](https://www.health-samurai.io/articles/type-schema-python-sdk-for-fhir). Depuis lors, le générateur a été réécrit de zéro dans une pile TypeScript unifiée sous le nom `@atomic-ehr/codegen`.

Ce que vous obtenez :

- **Modèles Pydantic v2** pour chaque ressource et type de données
- **Ensembles de valeurs** — énumérations `Literal` et `CodeableConcept[T]` générique avec liaisons dans le type
- **Extensions de types primitifs** — champs `_birthDate`, `_family` typés
- **[Client asynchrone fhirpy](https://github.com/beda-software/fhir-py)** — branchement direct sur `AsyncFHIRClient`
- **[N'importe quel paquet FHIR](https://github.com/atomic-ehr/fhir-canonical-manager)** — R4, US Core et profils personnalisés en une seule passe ; application de correctifs aux paquets à la volée
- **[Type Schema](https://www.health-samurai.io/articles/type-schema-a-pragmatic-approach-to-build-fhir-sdk)** — élagage d'arbre, promotion de modèles logiques, résolution de collisions

## Générez vos types FHIR

Installez le générateur (`bun add` et `pnpm add` fonctionnent aussi) :

```bash
npm install @atomic-ehr/codegen
```

Créez un script `generate.ts` :

```typescript
import { APIBuilder } from "@atomic-ehr/codegen";

const main = async () => {
  const builder = new APIBuilder()
    .fromPackage("hl7.fhir.r4.core", "4.0.1")
    .python({
      fieldFormat: "snake_case",
      allowExtraFields: false,
      primitiveTypeExtension: true,
    })
    .typeSchema({
      treeShake: {
        "hl7.fhir.r4.core": {
          "http://hl7.org/fhir/StructureDefinition/Patient": {},
          "http://hl7.org/fhir/StructureDefinition/Observation": {},
          "http://hl7.org/fhir/StructureDefinition/Bundle": {},
        },
      },
    })
    .outputTo("./fhir_types")
    .cleanOutput(true);

  const report = await builder.generate();
  if (!report.success) process.exit(1);
};

main();
```

Exécutez-le :

```bash
npx tsx generate.ts
```

Vous disposez maintenant d'un paquet Python prêt à l'emploi dans `./fhir_types/`. L'élagage d'arbre n'inclut que les ressources que vous avez listées ainsi que leurs dépendances (`DomainResource`, `Element`, `OperationOutcome`, etc.) automatiquement.

## Ce qui est généré

```
fhir_types/
├── __init__.py                  # Exports + model_rebuild() for forward refs
├── requirements.txt             # pydantic, requests, fhirpy, mypy, pytest
├── README.md
└── hl7_fhir_r4_core/
    ├── __init__.py
    ├── base.py                  # Complex types: CodeableConcept, HumanName, ...
    ├── patient.py               # Patient + nested types (PatientContact, ...)
    ├── observation.py
    ├── operation_outcome.py
    ├── bundle.py
    ├── domain_resource.py
    ├── resource.py
    └── resource_families.py     # Polymorphic validators for Bundle.entry etc.
```

Chaque ressource est un modèle Pydantic avec des types de champs appropriés, des alias et une validation :

```python
# Simplified -- other fields also carry serialization_alias, omitted for brevity
class Patient(DomainResource):
    model_config = ConfigDict(
        validate_by_name=True,
        serialize_by_alias=True,
        extra="forbid"
    )
    resource_type: Literal['Patient'] = Field(
        default='Patient', alias='resourceType',
        serialization_alias='resourceType', frozen=True
    )
    birth_date: str | None = Field(None, alias="birthDate")
    birth_date_extension: Element | None = Field(None, alias="_birthDate")
    gender: Literal["male", "female", "other", "unknown"] | None = Field(None)
    marital_status: CodeableConcept[
        Literal["A", "D", "I", "L", "M", "P", "S", "T", "U", "W", "UNK"] | str
    ] | None = Field(None, alias="maritalStatus")
    name: PyList[HumanName] | None = Field(None)  # PyList = typing.List
    # ...
```

Consultez le [fichier généré complet](https://github.com/atomic-ehr/codegen/blob/main/examples/python-r4-us-core/fhir_types/hl7_fhir_r4_core/patient.py) pour voir le modèle Patient dans son intégralité.

## Créer et manipuler des ressources

<details>
<summary>Configurer l'environnement Python (Python 3.12+)</summary>

```bash
python3.12 -m venv .venv
source .venv/bin/activate
pip install -r fhir_types/requirements.txt
```

</details>

Le générateur produit des **types et une validation** — aucun client HTTP n'est intégré. Pour la communication avec le serveur, utilisez [fhirpy](https://github.com/beda-software/fhir-py) ou n'importe quelle bibliothèque HTTP. Ici, nous travaillons directement avec les modèles :

```python
from fhir_types.hl7_fhir_r4_core import Element, Extension, HumanName, Patient

patient = Patient(
    name=[HumanName(given=["John"], family="Doe")],
    gender="male",
    birth_date="1980-01-01",
    birth_date_extension=Element(
        extension=[
            Extension(
                url="http://hl7.org/fhir/StructureDefinition/patient-birthTime",
                value_date_time="1980-01-01T08:30:00-05:00",
            ),
        ],
    ),
)

# Serialize to FHIR JSON (camelCase keys, nulls excluded)
print(patient.to_json(indent=2))
```

```json
{
  "resourceType": "Patient",
  "birthDate": "1980-01-01",
  "_birthDate": {
    "extension": [{
      "url": "http://hl7.org/fhir/StructureDefinition/patient-birthTime",
      "valueDateTime": "1980-01-01T08:30:00-05:00"
    }]
  },
  "gender": "male",
  "name": [{ "family": "Doe", "given": ["John"] }]
}
```

```python
# Round-trip: JSON -> Patient
restored = Patient.from_json(patient.to_json())
assert restored == patient

# Typed field access
patient.gender                                              # 'male'
assert patient.name is not None
patient.name[0].family                                      # 'Doe'
patient.birth_date                                          # '1980-01-01'
assert patient.birth_date_extension is not None
assert patient.birth_date_extension.extension is not None
patient.birth_date_extension.extension[0].value_date_time   # '1980-01-01T08:30:00-05:00'
```

## Vérification de types et validation

Que se passe-t-il lorsque nous faisons des erreurs ?

```python
from fhir_types.hl7_fhir_r4_core import HumanName, Patient

Patient(
    name=[HumanName(family="Doe")],
    gender="FOO",            # wrong value
    some_data="1990-01-01",  # wrong field
)
```

**Analyse statique avec mypy.** Ajoutez le greffon Pydantic à `mypy.ini` :

```ini
[mypy]
python_version = 3.12
plugins = pydantic.mypy

[pydantic-mypy]
init_typed = True
```

Puis exécutez :

```bash
$ mypy . --strict
main.py:10: error: Unexpected keyword argument "some_data" for "Patient"  [call-arg]
main.py:12: error: Argument "gender" to "Patient" has incompatible type "Literal['FOO']"; expected "Literal['male', 'female', 'other', 'unknown'] | None"  [arg-type]
```

**Validation à l'exécution** — exécutez le script et Pydantic intercepte les deux erreurs à l'instanciation :

```bash
$ python main.py
pydantic_core._pydantic_core.ValidationError: 2 validation errors for Patient
gender
  Input should be 'male', 'female', 'other' or 'unknown'
some_data
  Extra inputs are not permitted
```

Nul besoin d'exécuter un validateur séparément — les modèles appliquent les contraintes à la construction. Si vous devez accepter des champs supplémentaires, générez avec `allowExtraFields: true`.

## Options de personnalisation

| Option | Type | Défaut | Effet |
|---|---|---|---|
| `fieldFormat` | `"snake_case"` \| `"camelCase"` | `"camelCase"` | Nommage des champs : `birth_date` ou `birthDate` |
| `allowExtraFields` | `boolean` | `false` | Accepter les champs inconnus dans les données JSON en entrée |
| `primitiveTypeExtension` | `boolean` | `false` | Générer des champs complémentaires typés `_birthDate`, `_family` |
| `client` | `fhirpy`, `none` | `fhirpy` | Classe de base compatible [FhirpyBaseModel](https://github.com/atomic-ehr/codegen/blob/main/examples/python-r4-us-core/fhir_types/fhirpy_base_model.py) |

Ces options se combinent avec les fonctionnalités au niveau de l'`APIBuilder` — [élagage d'arbre](#générez-vos-types-fhir) pour n'inclure que les ressources dont vous avez besoin, [chargement multi-paquets](https://github.com/atomic-ehr/fhir-canonical-manager) pour combiner le noyau R4 avec des IG et des profils personnalisés, et [prétraitement des paquets](https://github.com/atomic-ehr/codegen#architecture) pour corriger les paquets amont défectueux avant la génération.

## Prochainement : prise en charge des profils pour Python

Notre générateur TypeScript prend déjà en charge les [classes de profils FHIR](https://github.com/atomic-ehr/codegen/blob/main/docs/posts/2026-03-09-typescript-profiles-quick.md) — des classes générées qui peuplent automatiquement les valeurs fixes, fournissent des accesseurs typés pour les tranches et les extensions, et incluent une validation côté client. Nous travaillons à apporter les mêmes capacités à Python :

- Accesseurs de tranches avec valeurs de discriminateur appliquées automatiquement
- Accesseurs/modificateurs d'extensions
- Validation retournant des erreurs et avertissements structurés

Si cela vous intéresse — vos commentaires sur les schémas de profils les plus importants pour vos flux de travail Python sont les bienvenus sur [GitHub](https://github.com/atomic-ehr/codegen).

## Au-delà de Python : ce que fait d'autre @atomic-ehr/codegen

Cet article s'est concentré sur le générateur Python, mais `@atomic-ehr/codegen` est une plateforme de génération de code multilangage. Voici ce qu'il offre également :

- **Quatre générateurs intégrés** — Python, TypeScript, C# et un moteur de gabarits Mustache pour tout langage
- **Prétraitement des paquets** — les paquets FHIR du monde réel sont souvent livrés avec des bogues : URL canoniques malformées, concepts CodeSystem manquants, ValueSets externes indisponibles. Le point d'ancrage `preprocessPackage` vous permet de les corriger avant la génération, sans avoir à créer une bifurcation du paquet ni à contourner les erreurs en aval
- **Promotion de modèles logiques** — convertir les StructureDefinitions logiques en ressources de première classe pour la génération de code
- **Résolution des collisions de schémas** — lorsque plusieurs paquets définissent des liaisons qui se chevauchent, vous pouvez spécifier quelle source l'emporte
- **Chargement multi-sources** — combiner des paquets NPM, des archives TGZ locales et des fichiers JSON StructureDefinition isolés en une seule passe de génération

Exemples Python fonctionnels :

- [Python + fhirpy](https://github.com/atomic-ehr/codegen/tree/main/examples/python-r4-us-core) — camelCase, client fhirpy asynchrone
- [Python + Pydantic + Client personnalisé](https://github.com/atomic-ehr/codegen/tree/main/examples/python-r4) — snake_case, extensions de types primitifs, client synchrone personnalisé

`@atomic-ehr/codegen` est à code source ouvert sous licence MIT. Que vous travailliez avec FHIR en Python pour créer des pipelines de données, des fonctionnalités d'apprentissage automatique ou des applications cliniques — essayez-le et dites-nous ce que vous en pensez. Les signalements de bogues, demandes de fonctionnalités et requêtes de fusion sont tous les bienvenus.

[GitHub](https://github.com/atomic-ehr/codegen) | [NPM](https://www.npmjs.com/package/@atomic-ehr/codegen) | [Aleksandr Penskoi sur LinkedIn](https://www.linkedin.com/in/aleksandr-penskoi/)