Dieser Beitrag beschreibt die Entwicklung eines Python-Mikrodienstes namens „MarkItLikeItsHot“, der die Inhaltskonvertierung in Markdown innerhalb von Willow CMS optimieren soll. Dieser Mikrodienst verwendet einen FastAPI- Wrapper um die leistungsstarke MarkItDown -Bibliothek von Microsoft und ermöglicht so die nahtlose Konvertierung von Dateien, URLs und Rohtext in sauberes, formatiertes Markdown. Wir werden die zugrunde liegenden Technologien untersuchen, uns mit der Codestruktur befassen und wichtige Aspekte wie Docker-Bereitstellung, -Tests und -Konfiguration hervorheben.
Die Kerntechnologien verstehen
Bevor wir uns mit der Implementierung befassen, klären wir die Rollen der wichtigsten Technologien:
Was ist MarkItDown?
MarkItDown wurde von Microsoft entwickelt und ist eine robuste Bibliothek zum Konvertieren verschiedener Dokumentformate (wie DOCX, PDF, HTML) in Markdown. Es bewältigt die Komplexität des Parsens verschiedener Dateistrukturen und Extrahierens von Inhalten und sorgt für eine konsistente Markdown-Ausgabe.
Was ist FastAPI?
FastAPI ist ein modernes, leistungsstarkes Python-Webframework, das sich ideal zum Erstellen von APIs eignet. Es bietet Geschwindigkeit, Benutzerfreundlichkeit und automatische interaktive API-Dokumentation.
Definieren von Endpunkten mit FastAPI
FastAPI verwendet Dekoratoren , um API-Endpunkte zu definieren. Beispiel:
@app.post("/convert/file")
async def convert_file(file: UploadFile = File(...)):
# ... conversion logic ...
Dies definiert einen POST-Endpunkt bei/convert/file
das einen Dateiupload akzeptiert. Auf ähnliche Weise verarbeiten andere Endpunkte Text- und URL-Konvertierungen:
/convert/text
: /konvertieren/text/convert/url
: /konvertieren/url
Dockerisierung des Microservice: Bereitstellung leicht gemacht
Docker vereinfacht die Bereitstellung und sorgt für Konsistenz zwischen Umgebungen. Das Setup umfasst einenDockerfile
Unddocker-compose.yml
.
Das Dockerfile: Erstellen des Images
Der Dockerfile
definiert die Umgebung für unseren 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"]
DerDockerfile
richtet eine leichte und effiziente Umgebung für den Microservice ein. Wir beginnen mit der Basis unseres Images aufpython:3.13.1-slim
, eine schlanke Version des offiziellen Python 3.13.1-Images, die die Gesamtgröße und Angriffsfläche des Images minimiert. Anschließend setzen wir das Arbeitsverzeichnis innerhalb des Containers auf/app
.
Der nächste Schritt beinhaltet die Installation von Systemabhängigkeiten.apt-get
, aktualisieren wir die Paketliste und installieren Tools wiegit
,ffmpeg
,tesseract-ocr
und andere. Diese werden für verschiedene Funktionen innerhalb der MarkItDown-Bibliothek benötigt, wie z. B. die Handhabung verschiedener Dateiformate und die Verarbeitung von Bildern. Dierm -rf /var/lib/apt/lists/*
Der Befehl bereinigt den Apt-Cache, um die Größe des Docker-Images weiter zu reduzieren.
Um die Abhängigkeiten unseres Projekts zu isolieren und Konflikte zu vermeiden, erstellen und aktivieren wir eine virtuelle Python-Umgebung. Wir setzen dieVIRTUAL_ENV
Umgebungsvariable auf/opt/venv
und Verwendungpython -m venv
um es zu erstellen. Ändern derPATH
stellt sicher, dass Befehle aus der virtuellen Umgebung priorisiert werden. Für ein besseres Docker-Build-Caching kopieren wirrequirements.txt
bevor die Python-Abhängigkeiten installiert werden. Dadurch kann Docker die zwischengespeicherten Ebenen wiederverwenden, wenn sich die Anforderungen nicht geändert haben. Anschließend aktualisieren wirpip
und installieren Sie die Python-Pakete des Projekts mitpip install
mit dem--no-cache-dir
Flag, um das Caching innerhalb des Containers zu vermeiden (das Caching wird vom Schichtensystem von Docker übernommen).
Zum Schluss kopieren wir den Anwendungscode in den Container./app/app
Verzeichnis. DasCMD
Anweisung gibt den Befehl an, der beim Starten des Containers ausgeführt werden soll. Er verwendetuvicorn
um unsere FastAPI-Anwendung zu bedienen und auf allen Schnittstellen zu lauschen (0.0.0.0
) auf Port 8000. Dieser Port wird dann beim Ausführen des Containers einem Port auf dem Hostcomputer zugeordnet, wodurch ein externer Zugriff auf den Microservice ermöglicht wird.
Docker Compose: Den Dienst orchestrieren
docker-compose.yml
vereinfacht die Ausführung des Dienstes mit definierten Ports und Volumes:
services:
markitdown:
build:
context: ./markitdown-service
dockerfile: Dockerfile
ports:
- "8000:8000"
volumes:
- ./markitdown-service/app:/app/app
environment:
- ENVIRONMENT=development
Erstellen und Ausführen werden unkompliziert:
docker compose build
docker compose up -d
Tauchen Sie ein inmain.py
: Die Kernlogik
Dermain.py
Datei enthält die Kernlogik des Microservice: Link zu main.py
- Datenhandhabung und Validierung: Pydantic-Modelle (
TextInput
,UrlInput
) gewährleisten die Datenintegrität.
Dieses TextInput-Modell validiert die Struktur des eingehenden JSON für den Endpunkt /convert/text und erzwingt das Vorhandensein eines Inhaltsfelds und eines optionalen Optionsfelds, das entweder ein Wörterbuch (dict) oder None sein kann.
class TextInput(BaseModel):
content: str
options: Optional[dict] = None
So funktioniert es in der Praxis:
Gültige Anfrage (mit Optionen):
{
"content": "Some text here",
"options": {
"some_option": "some_value"
}
}
Gültige Anfrage (ohne Optionen):
{
"content": "Some text here"
}
Im zweiten Beispiel ist die Anfrage auch dann gültig, wenn keine Optionen angegeben sind, und die Optionen werden in der Anwendungslogik auf „Keine“ gesetzt. Diese Flexibilität ermöglicht es Benutzern, bei Bedarf zusätzliche Optionen anzugeben, macht diese aber nicht zwingend. Derzeit werden die Optionen nicht an die MarkItDown-Bibliothek übergeben – ich schließe sie für die zukünftige Erweiterbarkeit des Microservice ein, z. B. Konvertierungsoptionen bei der Verarbeitung eines Dokuments oder einer Webseite.
- Temporäre Dateiverwaltung: Die
save_temp_file
Die Funktion übernimmt die temporäre Speicherung hochgeladener Dateien und abgerufener Webinhalte und gewährleistet so eine sichere und effiziente Verarbeitung. - MarkItDown Integration: Die
process_conversion
Die Funktion kapselt die Konvertierungslogik mithilfe der MarkItDown-Bibliothek. Diese Funktion enthält auch eine spezielle Logik für die Verarbeitung von Wikipedia-URLs und optimiert die Inhaltsextraktion für diese Seiten. - Fehlerbehandlung: Fehlerbehandlung mit benutzerdefinierten Ausnahmen (
FileProcessingError
,ConversionError
,URLFetchError
) und HTTP-Ausnahmen bieten informatives Benutzerfeedback. Diese können erweitert werden und sollten in Zukunft wahrscheinlich in eine separate Datei verschoben werden.
Implementierung der Ratenbegrenzung
Die Ratenbegrenzung ist unerlässlich, um den Dienst vor Missbrauch zu schützen, wenn ich ihn dem Internet aussetze. Wir verwendenslowapi
für diesen Zweck und einen Dekorateur:
@limiter.limit(f"{settings.RATE_LIMIT_REQUESTS}/hour")
@app.post("/convert/text")
# ...
Dieses Beispiel beschränkt die Anfragen auf/convert/text
Endpunkt auf 10 pro Stunde (gemäß Einstellungen ) pro Client-IP. Link zum Rate-Limiting-Decorator für diesen Endpunkt
Codestruktur und -konfiguration
MarkItLikeItsHot verwaltet durch Hilfsfunktionen und eine zentrale Konfiguration eine saubere, DRY-Codebasis (Don't Repeat Yourself).
- Hilfsfunktionen: Funktionen wie
save_temp_file
Undprocess_conversion
Reduzieren Sie Redundanz und verbessern Sie die Lesbarkeit. - Zentralisierte Konfiguration: Die
config.py
Datei, mitpydantic_settings
, verwaltet anwendungsweite Einstellungen und ermöglicht die einfache Anpassung von Parametern wieMAX_FILE_SIZE
,SUPPORTED_EXTENSIONS
,REQUEST_TIMEOUT
und Ratenbegrenzungen. Link zu config.py
Testen mit Pytest und Docker
MarkItLikeItsHot pytest zum Testen, und Docker vereinfacht das Ausführen dieser Tests in einer konsistenten Umgebung. Die Datei docker-compose.yml definiert einen separaten Testdienst speziell zum Ausführen der 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
So funktioniert dastest
Dienst ist konfiguriert:
build
: Es verwendet die gleicheDockerfile
als Hauptanwendung, um sicherzustellen, dass die Testumgebung die Produktionsumgebung widerspiegelt.volumes
: Mountet das Projektverzeichnis (./markitdown-service
) in den Behälter bei/app
, wodurch der Testcode zugänglich wird.environment
: Legt dieENVIRONMENT
variabel auftest
. Dies kann innerhalb des Anwendungscodes verwendet werden (z. B. inconfig.py
), um Einstellungen speziell für die Testumgebung anzupassen, wie z. B. Protokollierungsebenen oder Datenbankverbindungen. DiePYTHONPATH=/app
Einstellung stellt sicher, dass der Testläufer den Anwendungscode finden kann.entrypoint
: Definiert den Eintrittspunkt für den Container alspython -m pytest
. Dies führt denpytest
Testläufer.command
: Gibt die Befehlszeilenargumente fürpytest
.-v
ermöglicht eine ausführliche Ausgabe und--capture=no
Deaktiviert die Ausgabeerfassung, sodass Druckanweisungen während der Testausführung leichter zu sehen sind./app/tests/test_api.py
gibt die auszuführende Testdatei an. Sie können dies ändern, um bestimmte Tests oder Verzeichnisse innerhalb dertests
Ordner.depends_on
: Stellt sicher, dass diemarkitdown
Der Dienst (die Hauptanwendung) wird ausgeführt, bevor die Tests beginnen. Dies ist wichtig für Integrationstests, die mit den API-Endpunkten interagieren.profiles
: Verwendet Docker Compose-Profile, um diesen Dienst nur beim Ausführen von Tests einzuschließen. Dies verhindert, dass der Testcontainer gestartet wird, wenn einfach ausgeführt wirddocker compose up
Um die Tests durchzuführen, verwenden Siesudo docker-compose --profile test run --rm test
wobei die Option „rm“ den Container entfernt, nachdem die Tests ausgeführt wurden.
Die Tests decken ein breites Spektrum an Szenarien ab:
- Dateikonvertierungstests: Überprüfen Sie die korrekte Konvertierung verschiedener Dateitypen. Lesen Sie den Test zum Konvertieren eines Word-Dokuments
- URL-Konvertierungstests: Testen Sie das Abrufen und Konvertieren von Webseiten, einschließlich spezieller Tests für Wikipedia-URLs. Lesen Sie den Test zum Konvertieren eines BBC-News-Artikels
- Fehlerbehandlungstests: Stellen Sie eine robuste Behandlung ungültiger Eingaben und Randfälle sicher. Lesen Sie den Test zur Fehlerbehandlung
- Rate Limiting Tests: Überprüfen Sie die korrekte Durchsetzung von Rate Limits. Lesen Sie den Rest zur Rate Limiting
Der Testaufbau verwendet Vorrichtungen, die definiert sind inconftest.py
für einen effizienten Testaufbau und -abbau. Lesen Sie den Quellcode für conftest.py .
Testen mit GitHub Actions
GitHub Actions automatisiert die Ausführung dieser Tests bei jeder Push- und Pull-Anfrage und sorgt so für eine kontinuierliche Integration. Link zum GitHub Actions-Workflow
Nächste Schritte
Der unmittelbar nächste Schritt ist die Integration von MarkItLikeItsHot in Willow CMS. Dadurch wird eine benutzerfreundliche Oberfläche bereitgestellt, mit der Uploads, Links und Text direkt in Markdown-Entwürfe für Blogbeiträge und Seiten umgewandelt werden können. Weitere Funktionen sind:
- API-Schlüsselunterstützung zur Sicherung des Dienstes
- Entdecken und Bereitstellen zusätzlicher MarkItDown-Funktionen
- Hinzufügen von Unterstützung für benutzerdefinierte Konvertierungsoptionen (über die derzeit nicht verwendete
options
Feld) - Implementierung einer ausgefeilteren Fehlerbehandlung
- Erkunden der asynchronen Verarbeitung zur Verbesserung der Leistung bei sehr großen Dateien
Probieren Sie es aus. Der Code befindet sich auf GitHub mit einer guten Readme-Datei, die Ihnen dabei hilft, ihn für Ihre eigenen Projekte zu verwenden.