SQL-first data pipeline framework
Jinja2 templating · multi-base de données · monitoring intégré
"Noria" — la roue hydraulique qui fait circuler l'eau avec précision et régularité.
Ici, vos données circulent avec la même fiabilité.
Pourquoi Noria ?
| Besoin | Solution Noria |
|---|---|
| Écrire du SQL, pas du Python ORM | Templates Jinja2 directement dans vos fichiers .sql |
| Changer de BDD sans tout réécrire | Macros cross-DB compilées selon le dialecte cible |
| Orchestrer des dépendances entre requêtes | DAG déclaratif en YAML, exécution parallèle |
| Voir ce qui se passe | Dashboard de monitoring intégré, historique SQLite |
| Démarrer en 2 minutes | pip install noria && noria init mon_projet && noria run |
Installation
Noria s'installe dans un environnement Python : un dossier de travail qui contiendra Noria et vos projets.
# Installer uv (une seule fois sur votre machine)
curl -LsSf https://astral.sh/uv/install.sh | sh
# Créer votre espace de travail et y installer Noria
mkdir data_projets && cd data_projets
uv venv # crée l'environnement Python (.venv)
source .venv/bin/activate # l'active (à refaire à chaque nouveau terminal)
uv pip install noria
noria version # vérification : affiche la version installée
Sans uv :
python3 -m venv .venv && source .venv/bin/activate && pip install noriafait exactement la même chose.
« noria : commande introuvable » ? L'environnement n'est pas activé : relancez
source .venv/bin/activatedepuis le dossierdata_projets.
Démarrage rapide
noria init mon_pipeline # Noria pose quelques questions (base de données…)
# Entrée pour garder les valeurs par défaut
cd mon_pipeline
noria run # exécute le pipeline d'exemple
Organisation générée :
data_projets/ ← espace de travail (environnement Python)
├── .venv/ ← Noria installé ici
└── mon_pipeline/ ← votre projet Noria
├── noria.yml
├── envs/
└── sql/
Pour un deuxième projet : revenez dans data_projets, puis noria init autre_projet.
Structure d'un projet Noria
mon_pipeline/
├── noria.yml # Configuration du pipeline
├── sql/
│ ├── macros/ # Macros Jinja2 réutilisables
│ │ └── utils.sql.j2
│ ├── 01_clients.sql # Requêtes SQL avec templates Jinja2
│ └── 02_rapport.sql
├── params/
│ └── prod.yml # Paramètres par environnement
└── .noria/
├── data.db # Base DuckDB locale (développement)
└── tracking.db # Historique des exécutions
Configuration — noria.yml
name: mon_pipeline
description: "Pipeline de données clients"
database:
dialect: duckdb # duckdb | postgresql | bigquery | snowflake
database: .noria/data.db
params:
date_min: "2024-01-01"
schema: main
monitoring:
enabled: true
port: 7070
jobs:
clients_actifs:
sql: sql/01_clients.sql
target: "{{ params.schema }}.clients_actifs"
write_mode: replace # voir tableau des modes d'écriture
active: true
description: "Clients avec au moins une commande"
rapport:
sql: sql/02_rapport.sql
target: "{{ params.schema }}.rapport"
write_mode: replace
after:
- clients_actifs # exécuté après clients_actifs
pre_hook:
file: sql/hooks/pre_rapport.sql
post_hook:
file: sql/hooks/post_rapport.sql
Écrire du SQL avec Jinja2
-- sql/01_clients.sql
SELECT
{{ cast('id', 'VARCHAR') }} AS id_client, -- adapté au dialecte
first_name || ' ' || last_name AS nom,
{{ date_trunc('month', 'created_at') }} AS mois_inscription,
{{ coalesce('phone', "'N/A'") }} AS telephone
FROM source_schema.customers
WHERE created_at >= '{{ params.date_min }}'
{% if params.actif_only %}
AND actif = TRUE
{% endif %}
Macros cross-base de données
| Macro Jinja2 | PostgreSQL compilé | DuckDB compilé |
|---|---|---|
{{ cast('id', 'TEXT') }} |
id::TEXT |
CAST(id AS TEXT) |
{{ date_trunc('month', 'ts') }} |
DATE_TRUNC('month', ts) |
DATE_TRUNC('month', ts) |
{{ current_timestamp() }} |
NOW() |
CURRENT_TIMESTAMP |
{{ concat('a', 'b') }} |
CONCAT(a, b) |
CONCAT(a, b) |
{{ coalesce('a', '0') }} |
COALESCE(a, 0) |
COALESCE(a, 0) |
{{ safe_divide('num', 'den') }} |
CASE WHEN den=0 THEN NULL... |
CASE WHEN den=0 THEN NULL... |
{{ to_char('ts', 'YYYY-MM') }} |
TO_CHAR(ts, 'YYYY-MM') |
STRFTIME(ts, '%Y-%m') |
{{ limit(10) }} |
LIMIT 10 |
LIMIT 10 |
{{ schema_table('s', 't') }} |
s.t |
s.t |
Macros personnalisées
Créez vos propres macros dans sql/macros/*.sql.j2 :
{# sql/macros/utils.sql.j2 #}
{% macro panier_moyen(montant, quantite) %}
{{ safe_divide(montant, quantite) }}
{% endmacro %}
Puis utilisez-les dans n'importe quel fichier SQL :
{% from 'utils.sql.j2' import panier_moyen %}
SELECT {{ panier_moyen('SUM(amount)', 'COUNT(id)') }} AS panier_moyen
FROM orders
Modes d'écriture (write_mode)
| Mode | Comportement |
|---|---|
replace |
DROP TABLE + CREATE TABLE AS SELECT |
append |
INSERT INTO … SELECT |
truncate |
TRUNCATE + INSERT |
merge |
DELETE sur les clés + INSERT (nécessite merge_key) |
create_if_not_exists |
Crée seulement si absente |
safe_replace |
Comme replace, annulé si la requête retourne trop peu de lignes |
incremental |
DELETE sur une partition (incremental_key) + INSERT |
scd2 |
Historisation SCD2 avec _valid_from, _valid_to, _is_current |
update |
UPDATE des colonnes ciblées sur les clés (update_key) |
CLI — Commandes
noria init <nom> # Initialiser un projet
noria validate # Valider config + DAG (sans connexion BDD)
noria compile # Prévisualiser le SQL compilé
noria compile --job mon_job # Compiler un seul job
noria run # Lancer tout le pipeline
noria run --job mon_job # Lancer un job spécifique
noria run --dry-run # Compiler sans exécuter
noria run --workers 8 # Parallélisme (défaut: 4)
noria run --param date_min=2024-06-01 # Surcharger un paramètre
noria run --log-file noria.log # Écrire les logs dans un fichier
noria run --log-format json # Format JSON (avec run_id et job)
noria jobs # Lister les jobs et dépendances
noria status # Voir l'historique des exécutions
noria monitor # Ouvrir le dashboard web
Paramétrage par environnement
# Développement (défaut)
noria run
# Production via surcharge CLI
noria run --param schema=prod --param date_min=2023-01-01
# Variables d'environnement dans noria.yml
# database.password: ${DB_PASSWORD}
Hooks pre/post
jobs:
ma_tache:
sql: sql/ma_tache.sql
pre_hook:
file: sql/hooks/pre.sql # Exécuté avant le job
ignore_on_error: false
post_hook:
file: sql/hooks/post.sql # Exécuté après
ignore_on_error: true # Erreur non bloquante
on_failure_hook:
file: sql/hooks/alerte.sql # Exécuté une fois après un échec définitif
Monitoring
Le dashboard web est inclus par défaut :
noria monitor # Ouvre http://localhost:7070
- Historique de toutes les exécutions
- Détail par job : statut, durée, lignes écrites, erreurs
- Rafraîchissement automatique (HTMX)
L'historique est stocké localement dans .noria/tracking.db (SQLite, zéro config).
API Python
from noria.core.pipeline import Pipeline
# Lancer le pipeline
pipeline = Pipeline("./mon_projet", params_override={"schema": "staging"})
results = pipeline.run()
# Compilation uniquement
compiled_sql = pipeline.compile(task_name="ma_tache")
print(compiled_sql["ma_tache"])
# Historique
history = pipeline.history(limit=5)
Ajouter un dialecte personnalisé
from noria.adapters import register_adapter
from noria.adapters.base import BaseAdapter
from sqlalchemy import create_engine
class MyAdapter(BaseAdapter):
dialect_name = "mydb"
def create_engine(self, db_config):
return create_engine(f"mydb://...")
# ... implémenter les méthodes abstraites
register_adapter("mydb", MyAdapter)
Tests
make install-dev
make test
make test-cov # avec rapport de couverture HTML
Contribuer
- Fork le dépôt
make install-dev- Créer une branche feature
make test+make lintavant de soumettre
Licence
MIT © Noria Contributors
Metadata
Release files for noria 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| noria-0.3.0.tar.gz | 2.8 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| noria-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 3.9 MB
Release files / noria-0.3.0.tar.gz
| Download URL | noria-0.3.0.tar.gz |
|---|---|
| Size | 2.8 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
78b6f9bc34ed75bcd1f55a09a7619cbf68bc8b674a0aff3db187dd4c3cfd0b27
|
|
BLAKE2b-256 checksum How to use checksums |
bdc39549d04c94c6dd3ec2bdb336122e5a0bc505de5f3bbbd3c8739171052a5c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.
Transparency logRelease files / noria-0.3.0-py3-none-any.whl
| Download URL | noria-0.3.0-py3-none-any.whl |
|---|---|
| Size | 1.1 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
70799f4d7e34e40a252a6cdb54dfbd492906e50face474377feff644f325a6c7
|
|
BLAKE2b-256 checksum How to use checksums |
5185cd7deac44bb460cd9f6491b3381b4a541ce3f18a322c610f5b4803d95be4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.
Transparency log