Cet article détaille le développement d'un microservice Python nommé « MarkItLikeItsHot », conçu pour rationaliser la conversion de contenu en Markdown dans Willow CMS. Ce microservice utilise un wrapper FastAPI autour de la puissante bibliothèque MarkItDown de Microsoft, permettant une conversion transparente des fichiers, des URL et du texte brut en Markdown propre et formaté. Nous explorerons les technologies sous-jacentes, aborderons la structure du code et mettrons en évidence des aspects clés tels que le déploiement, les tests et la configuration de Docker.
Comprendre les technologies de base
Avant de plonger dans la mise en œuvre, clarifions les rôles des technologies clés :
Qu'est-ce que MarkItDown ?
MarkItDown , développé par Microsoft, est une bibliothèque robuste pour convertir divers formats de documents (tels que DOCX, PDF, HTML) en Markdown. Il gère les complexités de l'analyse de différentes structures de fichiers et de l'extraction de contenu, fournissant une sortie Markdown cohérente.
Qu'est-ce que FastAPI ?
FastAPI est un framework Web Python moderne et performant, idéal pour la création d'API. Il offre rapidité, simplicité d'utilisation et documentation API interactive automatique.
Définition des points de terminaison avec FastAPI
FastAPI utilise des décorateurs pour définir les points de terminaison de l'API. Par exemple :
@app.post("/convert/file")
async def convert_file(file: UploadFile = File(...)):
# ... conversion logic ...
Ceci définit un point de terminaison POST à/convert/file
qui accepte le téléchargement d'un fichier. De même, d'autres points de terminaison gèrent les conversions de texte et d'URL :
/convert/text
: /convertir/texte/convert/url
: /convertir/url
Dockerisation du microservice : le déploiement simplifié
Docker simplifie le déploiement et assure la cohérence entre les environnements. La configuration implique unDockerfile
etdocker-compose.yml
.
Le Dockerfile : construire l'image
Le Dockerfile
définit l'environnement de notre microservice :
FROM python:3.13.1-slim
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
ffmpeg \
libsm6 \
libxext6 \
libmagic1 \
tesseract-ocr \
python3-venv \
&& rm -rf /var/lib/apt/lists/*
# Create and activate virtual environment
ENV VIRTUAL_ENV=/opt/venv
RUN python -m venv $VIRTUAL_ENV
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
# Copy requirements first for better caching
COPY requirements.txt .
# Update pip and install requirements in the virtual environment
RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r requirements.txt
# Copy the rest of the application
COPY ./app /app/app
# Command to run the application
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
LeDockerfile
met en place un environnement léger et efficace pour le microservice. Nous commençons par baser notre image surpython:3.13.1-slim
, une version allégée de l'image officielle Python 3.13.1, minimisant la taille globale de l'image et la surface d'attaque. Nous définissons ensuite le répertoire de travail à l'intérieur du conteneur sur/app
.
L'étape suivante consiste à installer les dépendances du système.apt-get
, nous mettons à jour la liste des packages et installons des outils tels quegit
,ffmpeg
,tesseract-ocr
, et d'autres. Ceux-ci sont nécessaires pour diverses fonctionnalités de la bibliothèque MarkItDown, telles que la gestion de différents formats de fichiers et le traitement d'images.rm -rf /var/lib/apt/lists/*
la commande nettoie le cache apt pour réduire davantage la taille de l'image Docker.
Pour isoler les dépendances de notre projet et éviter les conflits, nous créons et activons un environnement virtuel Python. Nous définissons lesVIRTUAL_ENV
variable d'environnement à/opt/venv
et utiliserpython -m venv
pour le créer. Modification duPATH
garantit que les commandes de l'environnement virtuel sont prioritaires. Pour une meilleure mise en cache de build Docker, nous copionsrequirements.txt
avant d'installer les dépendances Python. Cela permet à Docker de réutiliser les couches mises en cache si les exigences n'ont pas changé. Nous mettons ensuite à niveaupip
et installez les packages Python du projet en utilisantpip install
avec le--no-cache-dir
indicateur pour éviter la mise en cache dans le conteneur (la mise en cache est gérée par le système en couches de Docker).
Enfin, nous copions le code de l'application dans le conteneur/app/app
répertoire. LeCMD
L'instruction spécifie la commande à exécuter au démarrage du conteneur. Elle utiliseuvicorn
pour servir notre application FastAPI, en écoutant sur toutes les interfaces (0.0.0.0
) sur le port 8000. Ce port est ensuite mappé à un port sur la machine hôte lors de l'exécution du conteneur, permettant un accès externe au microservice.
Docker Compose : orchestration du service
docker-compose.yml
simplifie l'exécution du service avec des ports et des volumes définis :
services:
markitdown:
build:
context: ./markitdown-service
dockerfile: Dockerfile
ports:
- "8000:8000"
volumes:
- ./markitdown-service/app:/app/app
environment:
- ENVIRONMENT=development
Construire et exécuter devient simple :
docker compose build
docker compose up -d
Plongée en profondeur dansmain.py
:La logique de base
Lemain.py
le fichier héberge la logique principale du microservice : lien vers main.py
- Traitement et validation des données : modèles pydantiques (
TextInput
,UrlInput
) garantir l’intégrité des données.
Ce modèle TextInput valide la structure du JSON entrant pour le point de terminaison /convert/text, en imposant la présence d'un champ de contenu et d'un champ d'options facultatif qui peut être un dictionnaire (dict) ou Aucun.
class TextInput(BaseModel):
content: str
options: Optional[dict] = None
Voici comment cela fonctionne en pratique :
Requête valide (avec options) :
{
"content": "Some text here",
"options": {
"some_option": "some_value"
}
}
Requête valide (sans options) :
{
"content": "Some text here"
}
Dans le deuxième exemple, même si les options ne sont pas fournies, la demande est toujours valide et les options seront définies sur None dans la logique de l'application. Cette flexibilité permet aux utilisateurs de fournir des options supplémentaires si nécessaire, mais ne les rend pas obligatoires. À l'heure actuelle, les options ne sont pas transmises à la bibliothèque MarkItDown. Je les inclue pour une extensibilité future du microservice, comme les options de conversion lors du traitement d'un document ou d'une page Web.
- Gestion des fichiers temporaires : La
save_temp_file
La fonction gère le stockage temporaire des fichiers téléchargés et du contenu Web récupéré, garantissant un traitement sécurisé et efficace. - Intégration MarkItDown : Le
process_conversion
La fonction encapsule la logique de conversion à l'aide de la bibliothèque MarkItDown. Cette fonction inclut également une logique spécialisée pour la gestion des URL de Wikipédia, optimisant ainsi l'extraction de contenu pour ces pages. - Gestion des erreurs : gestion des erreurs avec des exceptions personnalisées (
FileProcessingError
,ConversionError
,URLFetchError
) et les exceptions HTTP fournissent des commentaires informatifs aux utilisateurs. Ceux-ci peuvent être étendus et devraient probablement être déplacés vers un fichier séparé à l'avenir.
Mise en œuvre de la limitation de débit
La limitation du débit est essentielle pour protéger le service contre les abus si je l'expose à Internet. Nous utilisonsslowapi
à cet effet et un décorateur :
@limiter.limit(f"{settings.RATE_LIMIT_REQUESTS}/hour")
@app.post("/convert/text")
# ...
Cet exemple limite les requêtes à la/convert/text
point de terminaison à 10 par heure (conformément aux paramètres ) par IP client. Lien vers le décorateur de limitation de débit pour ce point de terminaison
Structure et configuration du code
MarkItLikeItsHot maintient une base de code propre et SÈCHE (Ne vous répétez pas) grâce à des fonctions d'assistance et une configuration centralisée.
- Fonctions d'assistance : fonctions telles que
save_temp_file
etprocess_conversion
réduire la redondance et améliorer la lisibilité. - Configuration centralisée : La
config.py
fichier, en utilisantpydantic_settings
, gère les paramètres à l'échelle de l'application, permettant une personnalisation facile des paramètres tels queMAX_FILE_SIZE
,SUPPORTED_EXTENSIONS
,REQUEST_TIMEOUT
, et les limites de débit. Lien vers config.py
Tests avec Pytest et Docker
MarkItLikeItsHot pytest pour les tests, et Docker simplifie l'exécution de ces tests dans un environnement cohérent. Le fichier docker-compose.yml définit un service de test distinct spécifiquement pour l'exécution des tests :
test:
build:
context: ./markitdown-service
dockerfile: Dockerfile
volumes:
- ./markitdown-service:/app
environment:
- ENVIRONMENT=test
- PYTHONPATH=/app
entrypoint: python -m pytest
command: /app/tests/test_api.py -v --capture=no
depends_on:
- markitdown
profiles:
- test
Voici comment letest
le service est configuré :
build
:Il utilise le mêmeDockerfile
en tant qu'application principale, garantissant que l'environnement de test reflète l'environnement de production.volumes
: Monte le répertoire du projet (./markitdown-service
) dans le récipient à/app
, rendant le code de test accessible.environment
: Définit leENVIRONMENT
variable àtest
. Cela peut être utilisé dans le code de l'application (par exemple, dansconfig.py
) pour ajuster les paramètres spécifiques à l'environnement de test, tels que les niveaux de journalisation ou les connexions à la base de données.PYTHONPATH=/app
le paramètre garantit que le testeur peut trouver le code de l'application.entrypoint
: Définit le point d'entrée du conteneur commepython -m pytest
. Ceci exécute lepytest
coureur de tests.command
:Spécifie les arguments de ligne de commande pourpytest
.-v
permet une sortie détaillée et--capture=no
désactive la capture de sortie, ce qui facilite la visualisation des instructions d'impression pendant l'exécution du test./app/tests/test_api.py
spécifie le fichier de test à exécuter. Vous pouvez le modifier pour exécuter des tests ou des répertoires spécifiques dans letests
dossier.depends_on
: Assure que lemarkitdown
Le service (l'application principale) est en cours d'exécution avant le début des tests. Ceci est crucial pour les tests d'intégration qui interagissent avec les points de terminaison de l'API.profiles
: Utilise les profils Docker Compose pour inclure ce service uniquement lors de l'exécution de tests. Cela empêche le conteneur de test de démarrer lors de la simple exécutiondocker compose up
Pour exécuter les tests, vous utiliseriezsudo docker-compose --profile test run --rm test
où l'option rm supprimera le conteneur après avoir exécuté les tests.
Les tests couvrent un large éventail de scénarios :
- Tests de conversion de fichiers : vérifiez la conversion correcte de différents types de fichiers. Lisez le test pour convertir un document Word
- Tests de conversion d'URL : testez la récupération et la conversion de pages Web, y compris des tests spécialisés pour les URL de Wikipédia. Lisez le test pour convertir un article de BBC News
- Tests de gestion des erreurs : assurez une gestion robuste des entrées non valides et des cas limites. Lisez le test de gestion des erreurs
- Tests de limitation de débit : validez l'application correcte des limites de débit. Lisez la suite pour la limitation de débit
La configuration de test utilise des montages définis dansconftest.py
pour une configuration et un démontage efficaces des tests. Lisez la source de conftest.py .
Tester avec GitHub Actions
GitHub Actions automatise l'exécution de ces tests sur chaque requête push et pull, en maintenant une intégration continue. Lien vers le flux de travail GitHub Actions
Prochaines étapes
L'étape suivante consiste à intégrer MarkItLikeItsHot dans Willow CMS, en fournissant une interface conviviale pour convertir les téléchargements, les liens et le texte directement en brouillons Markdown pour les articles et les pages de blog. Les autres fonctionnalités incluront :
- Prise en charge des clés API pour sécuriser le service
- Exploration et exposition de fonctionnalités MarkItDown supplémentaires
- ajout de la prise en charge des options de conversion personnalisées (via le actuellement inutilisé
options
champ) - mettre en œuvre une gestion des erreurs plus sophistiquée
- exploration du traitement asynchrone pour améliorer les performances avec des fichiers très volumineux
Alors, jetez-y un œil. Le code est sur GitHub avec un bon fichier readme pour vous aider à commencer à l'utiliser pour vos propres projets.