Skip to main content

Alchimie Data Solutions — adsToolBox

adsToolBox est une librairie Python interne d'Alchimie Data Solutions, qui regroupe les fonctions génériques réutilisées dans les développements liés à Onyx. Elle fournit des briques homogènes pour accéder aux bases de données, industrialiser des pipelines, capturer du changement (CDC), manipuler des fichiers sur différents protocoles, et gérer les tâches transverses (logs, chrono, environnement, mails, Git, Odoo, Google Calendar).

Dépôt privé — ce repository est réservé aux employés d'Alchimie Data Solutions. Le package est toutefois publié publiquement sur PyPI sous le nom adstoolbox, et un dépôt d'exemples publics est disponible : AlchimieDataSolutions/DemoPy.

  • Nom du package : adstoolbox
  • Module Python : adsToolBox
  • Versioning : calendaire (YYYY.MM.DD) — voir pyproject.toml
  • Python : >= 3.10, < 4.0
  • Licence : MIT

Sommaire

Fonctionnalités

Tous les symboles ci-dessous sont exposés directement depuis adsToolBox (voir adsToolBox/__init__.py). La colonne Extra indique la dépendance optionnelle à installer — voir Modules et extras.

Bases de données et pipelines

Symbole Extra Rôle
DataFactory dataframe Classe abstraite commune : connect, sql_query, sql_exec, sql_scalaire, insert, insert_many, insert_bulk, upsert, upsert_many, upsert_bulk, find_text_anywhere
DbMssql mssql Implémentation SQL Server (driver pymssql).
DbMysql mysql Implémentation MySQL (driver pymysql).
DbPgsql pgsql Implémentation PostgreSQL (driver psycopg2).
Pipeline dataframe Orchestration d'un transfert source → destination avec batch, déduplication par hash, inférence de schéma Polars, création automatique de la table cible.
DataComparator dataframe Compare deux sources batch par batch et produit un rapport de différences.
ChangeDataCapture cdc CDC déclarative (modes append, scd1, scd2, scd4) validée par JSON Schema, avec gestion staging/persistent et synchronisation côté métier.

Fichiers, infrastructure, intégrations externes

Symbole Extra Rôle
FileHandler files Accès fichiers multi-backends via fsspec (local, SMB, SFTP, Azure Blob) avec transfert atomique et checksum optionnel.
GitHandler git Clonage / mise à jour de dépôts Git via token (GitPython + API GitHub). Exige aussi le binaire git dans le PATH.
GoogleCalendarConnector google Lecture/écriture d'événements Google Calendar (OAuth2).
MailReader Lecture IMAP avec décodage robuste des en-têtes et du corps (multipart).
OdooConnector Accès XML-RPC à Odoo (get, put, …).

Utilitaires transverses

Aucun extra requis : ces symboles fonctionnent avec l'installation de base.

Symbole Rôle
Logger Logger unifié console / fichier / base, avec niveaux, contexte disabled() et insertion dans une table de détails.
timer, get_timer, set_timer, now, set_timezone Décorateur de chronométrage et helpers de temps (timezone-aware).
retry_on_failure Décorateur de retry avec backoff et méthode de reconnexion optionnelle.
get_public_ip Récupération de l'IP publique (utile pour pare-feux).
Env Chargement d'un .env trouvé automatiquement dans l'arborescence parente.

Installation

Utilisation du package (public)

Le package est publié sur PyPI et installable par n'importe qui :

pip install adstoolbox

Cette installation de base est volontairement légère (environ 20 Mo) et couvre Logger, Env, timer, MailReader et OdooConnector. Les autres modules demandent un extra, à choisir selon les besoins :

pip install "adstoolbox[pgsql]"              # PostgreSQL + Polars
pip install "adstoolbox[mssql,mysql,pgsql]"  # les trois bases
pip install "adstoolbox[files]"              # FileHandler (fsspec + backends)
pip install "adstoolbox[all]"                # tout (environ 480 Mo)

Les guillemets sont nécessaires sous zsh, qui interprète les crochets.

Migration depuis les versions ≤ 2026.05.19 Ces versions installaient toutes les dépendances d'office. Depuis, elles sont optionnelles. Pour retrouver le comportement précédent en une commande : pip install "adstoolbox[all]". Les imports étant paresseux, une dépendance manquante ne casse plus import adsToolBox : l'erreur survient à l'utilisation du module concerné, et nomme l'extra à installer.

Développement (interne ADS uniquement)

L'accès aux sources est restreint aux employés d'Alchimie Data Solutions. Une fois le dépôt cloné via les accès internes, le projet est géré avec Poetry (voir pyproject.toml et poetry.lock) :

poetry install --all-extras

--all-extras est indispensable : sans lui, ni Polars, ni fsspec, ni les drivers SQL ne sont installés, et la suite de tests échoue dès la collecte. Le groupe dev (pytest, testcontainers, ruff) est inclus par défaut.

Modules et extras

Les modules sont chargés paresseusement (PEP 562) : import adsToolBox n'importe aucune dépendance tierce, chaque module n'est chargé qu'au premier accès à l'un de ses symboles. Une dépendance absente produit un message explicite :

ImportError: DbPgsql requiert une dépendance non installée (No module named 'psycopg2').
Installez-la avec : pip install "adstoolbox[pgsql]".
Extra Paquets installés Modules débloqués
(aucun) requests, chardet, python-dotenv, tzdata (Windows) logger, timer, global_config, load_env, mail_reader, odoo, dml_generator
dataframe polars data_factory, pipeline, data_comparator
mssql polars, pymssql db_mssql
mysql polars, pymysql db_mysql
pgsql polars, psycopg2-binary db_pgsql
files fsspec, adlfs, smbprotocol, paramiko file_handler
git GitPython, PyGithub git_handler
google polars, google-api-python-client, google-auth, google-auth-oauthlib google_calendar
cdc SQLAlchemy, jsonschema cdc, ddl_operations
all tous les précédents tous

Les extras des bases incluent polars parce que db_* en dépend via data_factory : adstoolbox[pgsql] est donc autosuffisant.

Note sur Polars et les anciens processeurs

Le package dépend de polars, le build standard (AVX2). Sur un processeur sans AVX2, il faut basculer sur polars-lts-cpu après l'installation :

pip install "adstoolbox[pgsql]"
pip install --force-reinstall --no-deps polars-lts-cpu

--force-reinstall est nécessaire : les deux distributions fournissent le même module polars sans se déclarer incompatibles, et pip considère la contrainte satisfaite sans remplacer les fichiers. Ne déclarez jamais les deux dans un même fichier de dépendances — elles s'écrasent mutuellement et rendent import polars inutilisable.

Démarrage rapide

Connexion à une base de données

Nécessite pip install "adstoolbox[pgsql]".

from adsToolBox import DbPgsql, Logger, Env

logger = Logger(log_level=Logger.INFO, logger_name="adsLogger")
env = Env(logger)

db = DbPgsql({
    'database': env.PG_DWH_DB,
    'user': env.PG_DWH_USER,
    'password': env.PG_DWH_PWD,
    'port': env.PG_DWH_PORT,
    'host': env.PG_DWH_HOST
}, logger)
db.connect()

generator = db.sql_query("SELECT * FROM table_test;")

for batch in generator:
    for row in batch:
        data = row

Pipeline source → destination

Nécessite pip install "adstoolbox[dataframe]", plus l'extra de chaque base utilisée.

from adsToolBox import Pipeline

pipeline = Pipeline(
    {
        "db_source": db_src,
        "query_source": "SELECT * FROM source_table",
        "db_destination": {
            "name": "demo",
            "db": db_dst,
            "table": "destination_table",
            "cols": ["col1", "col2"],
            "cols_def": ["INT", "VARCHAR(50)"],
        },
        "operation_type": "insert",
        "insert_method": "bulk",
        "batch_size": 10_000,
    },
    logger,
)
results = pipeline.run()

print(results)

Logger et chronomètre

Aucun extra nécessaire.

from adsToolBox import Logger, set_timer, timer

set_timer(state=True)

class MyJob:
    def __init__(self) -> None:
        self.logger = Logger(log_level=Logger.DEBUG)

    @timer
    def run(self) -> None:
        self.logger.info("traitement en cours")

D'autres exemples sont disponibles dans le dépôt de démo : AlchimieDataSolutions/DemoPy.

Structure du dépôt

adsGenericFunctions/
├── .github/workflows/       # CI : tests, matrice d'extras, contrôle du paquet publié
├── adsToolBox/              # Package publié
│   ├── __init__.py          # Exports publics et chargement paresseux (PEP 562)
│   ├── cdc.py               # ChangeDataCapture + modes SCD
│   ├── data_comparator.py   # DataComparator
│   ├── data_factory.py      # DataFactory (classe abstraite)
│   ├── db_mssql.py          # DbMssql
│   ├── db_mysql.py          # DbMysql
│   ├── db_pgsql.py          # DbPgsql
│   ├── ddl_operations.py    # Génération DDL multi-dialecte
│   ├── dml_generator.py     # Génération DML multi-dialecte
│   ├── file_handler.py      # FileHandler (fsspec)
│   ├── git_handler.py       # GitHandler
│   ├── global_config.py     # retry_on_failure, set_timer, get_public_ip
│   ├── google_calendar.py   # GoogleCalendarConnector
│   ├── load_env.py          # Env
│   ├── logger.py            # Logger
│   ├── mail_reader.py       # MailReader
│   ├── odoo.py              # OdooConnector
│   ├── pipeline.py          # Pipeline
│   └── timer.py             # timer, now, set_timezone, get_timer
├── scripts/
│   └── check_extras.py      # Valide le contrat des extras (utilisé par la CI)
├── tests/                   # Tests unitaires (mocks)
├── integration_tests/       # Tests fonctionnels (testcontainers → Docker)
├── adsGenericFunctions.py   # Point d'entrée historique
├── pyproject.toml           # Métadonnées PEP 621 + configuration Ruff
├── poetry.lock              # Versions figées (source de vérité pour la CI)
└── pytest.ini               # Configuration pytest

Tests

Les tests sont répartis en deux suites, déclarées dans pytest.ini :

  • tests/ — tests unitaires avec mocks (unittest.mock), sans dépendance externe.
  • integration_tests/ — tests fonctionnels avec Testcontainers (SQL Server, MySQL, PostgreSQL, Samba, Azurite) ; Docker doit être disponible.

Les deux suites supposent une installation --all-extras : plusieurs fichiers de test importent polars et fsspec directement, donc une installation partielle échoue à la collecte.

Exécuter toutes les suites

poetry run pytest

Cibler une suite

poetry run pytest tests/                 # unitaires uniquement
poetry run pytest integration_tests/     # fonctionnels uniquement
poetry run pytest tests/test_logger.py   # un fichier précis

Tests de garde sur le chargement paresseux

tests/test_init_lazy.py protège trois propriétés faciles à casser en silence :

  • import adsToolBox ne charge aucune dépendance tierce ;
  • timer reste la fonction et n'est pas masqué par le sous-module homonyme ;
  • aucun symbole exporté n'est masqué par un sous-module de même nom.

Ces tests s'ignorent d'eux-mêmes pour les cas dont l'extra est absent, et sont donc exécutables sur une installation partielle.

Contrat des extras

python scripts/check_extras.py            # aucun extra installé
python scripts/check_extras.py pgsql      # extras installés
python scripts/check_extras.py all

Le script vérifie que le cœur reste importable et que chaque symbole indisponible nomme un extra qui n'est effectivement pas installé. La CI l'exécute sur une matrice de configurations : c'est le seul endroit capable de détecter une régression de couplage, le job principal installant tout.

Tests fonctionnels — prérequis

  • Docker Desktop (ou équivalent) lancé et accessible.
  • Les containers sont démarrés automatiquement par les fixtures (pas de setup manuel).
  • Sous Windows, la variable TESTCONTAINERS_RYUK_DISABLED=true est positionnée par les tests pour éviter les problèmes de cleanup.

Développement

Linter / formatter

Le projet utilise Ruff (configuration dans pyproject.toml, select = ["ALL"] avec quelques exceptions documentées) :

poetry run ruff check .
poetry run ruff format .

Cibles configurées : adsToolBox, tests, integration_tests, scripts. Longueur de ligne : 100.

Conventions

  • Python ≥ 3.10, typage explicite et from __future__ import annotations quand utile.
  • Docstrings en français, style concis.
  • Noms en snake_case pour les méthodes publiques.
  • Tests unitaires obligatoires pour toute nouvelle méthode publique.

Ajouter une dépendance

Toute nouvelle dépendance tierce doit être optionnelle et rattachée à un extra, sauf si elle est pure Python et de taille négligeable. Le critère : un paquet binaire (donc susceptible d'échouer à l'installation), lourd, ou exigeant un binaire système va en extra.

Trois endroits à mettre à jour de façon cohérente :

  1. [project.optional-dependencies] dans pyproject.toml, extra dédié et liste all ;
  2. _EXTRA_OF_MODULE dans adsToolBox/__init__.py, pour le message d'erreur ;
  3. la matrice du workflow CI, si l'extra est nouveau.

Le module concerné doit importer sa dépendance au niveau module (jamais depuis __init__.py), afin que le chargement paresseux isole la panne.

Publication

Le package est publié sur PyPI sous le nom adstoolbox. La version suit un schéma calendaire YYYY.MM.DD défini dans pyproject.toml.

poetry build
poetry publish

Dépendances

Les dépendances sont déclarées dans pyproject.toml, sections [project.dependencies] (cœur) et [project.optional-dependencies] (extras). Les versions sont exprimées en bornes ouvertes ; poetry.lock fixe les versions exactes pour la CI.

Cœur — toujours installé

Pur Python, taille négligeable, aucun risque d'échec d'installation.

  • requests, chardetglobal_config, mail_reader
  • python-dotenvEnv
  • tzdata — base IANA pour zoneinfo (Windows uniquement, via marqueur d'environnement)

Extras

  • Données : polars
  • Bases de données : pymssql (MSSQL), pymysql (MySQL), psycopg2-binary (PostgreSQL)
  • CDC : SQLAlchemy, jsonschema
  • Fichiers : fsspec, plus les backends qu'il charge dynamiquement — adlfs (Azure abfs://), paramiko (SFTP sftp://), smbprotocol (SMB smb://). Ces trois paquets ne sont jamais importés directement mais sont requis à l'exécution.
  • Intégrations : GitPython, PyGithub, google-api-python-client, google-auth, google-auth-oauthlib

Développement

Groupe dev du pyproject.toml, jamais installé chez les utilisateurs : pytest, testcontainers, ruff.

Auteurs

Licence

Distribué sous licence MIT.

Release files for adstoolbox 2026.9.11

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for adstoolbox 2026.9.11
File Size Uploaded
adstoolbox-2026.9.11.tar.gz 57.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for adstoolbox 2026.9.11
File Interpreter ABI Platform
adstoolbox-2026.9.11-py3-none-any.whl Python 3 none any Details

Total release size:127.4 kB

Release files / adstoolbox-2026.9.11.tar.gz

Download URL adstoolbox-2026.9.11.tar.gz
Size 57.4 kB
Tags Source
SHA-256 checksum
How to use checksums
105e2cb343ae565e0b6499fa4c79f40c6713751f297308dc5f51fc1085529336
BLAKE2b-256 checksum
How to use checksums
0c3d9507ad0fb1ad068aefa11419924079599255acfc5cd5f48736d7a19af9c3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.4.3 CPython/3.10.11 Windows/10

Release files / adstoolbox-2026.9.11-py3-none-any.whl

Download URL adstoolbox-2026.9.11-py3-none-any.whl
Size 70.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
68d70453fb8ad46eade1351be776ca3ad8b0ef9a5e289b2df200b14fa050183a
BLAKE2b-256 checksum
How to use checksums
de3d1ef3894d8d1f196a750da9ff960090dd52e495579c79ac96d9778230e6da
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.4.3 CPython/3.10.11 Windows/10

Release history Release notifications | RSS feed

This release

2026.9.11 This release

2 release files

1.0.61

2 release files

1.0.60

2 release files

1.0.59

2 release files

1.0.58

2 release files

1.0.57

2 release files

1.0.56

2 release files

1.0.55

2 release files

1.0.54

2 release files

1.0.53

2 release files

1.0.51

2 release files

1.0.50

2 release files

1.0.49

2 release files

1.0.48

2 release files

1.0.47

2 release files

1.0.46

2 release files

1.0.45

2 release files

1.0.44

2 release files

1.0.41

2 release files

1.0.40

2 release files

1.0.39

2 release files

1.0.38

2 release files

1.0.37

2 release files

1.0.36

2 release files

1.0.31

2 release files

1.0.30

2 release files

1.0.29

2 release files

1.0.28

2 release files

1.0.22

2 release files

1.0.21

2 release files

1.0.20

2 release files

1.0.19

2 release files

1.0.18

2 release files

1.0.17

2 release files

1.0.16

2 release files

1.0.15

2 release files

1.0.14

2 release files

1.0.13

2 release files

1.0.12

2 release files

1.0.11

2 release files

1.0.9

2 release files

1.0.8

2 release files

1.0.7

2 release files

1.0.6

2 release files

1.0.5

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page