Initialer stabiler Stand

- Bootstrap mit .venv-Autoinstall und Relaunch
- Logging-Modul mit Loglevel-Fallback und optionaler Rotation
- .env-Support via python-dotenv
- Beispielstruktur für portable Python-Apps
- Umfangreiche README mit Anleitung und Logging-Kapitel
- .env.example enthalten
- VS Code-Tasks integriert
This commit is contained in:
Adam Skotarczak 2025-04-23 12:05:52 +02:00
parent ee80f056ff
commit 39aacfa931
13 changed files with 337 additions and 46 deletions

8
.env.example Normal file
View File

@ -0,0 +1,8 @@
# APP_MODE ungenutzt im Template
APP_MODE=
# LOGLEVEL: "CRITICAL"- "ERROR" - "WARNING" - "INFO" - "DEBUG"
LOGLEVEL=INFO
# Pfad zum log z.B log/template.log
LOGFILE=log/template.log

8
.gitignore vendored
View File

@ -129,6 +129,8 @@ celerybeat.pid
# Environments
.env
.env.*
!.env.example
.venv
env/
venv/
@ -172,3 +174,9 @@ cython_debug/
# PyPI configuration file
.pypirc
# Custom:
logs/
log/
*.zip

7
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"recommendations": [
"ms-python.python",
"ms-python.vscode-pylance",
"ms-toolsai.jupyter"
]
}

24
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,24 @@
{
"python.linting.enabled": true,
"python.linting.pylintEnabled": true,
"python.linting.mypyEnabled": true,
"python.linting.pylintArgs": ["--disable=C0114,C0115,C0116"],
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
},
"python.analysis.typeCheckingMode": "basic",
// Abschliessende Leerzeichen entfernen:
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true,
// Markdown für das Entfernen von abschliessenden Leerzeichen rausnehmen:
"[markdown]": {
"files.trimTrailingWhitespace": false
},
"[python]": {
"editor.tabSize": 4,
"editor.insertSpaces": true
}
}

17
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,17 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Linter (pylint)",
"type": "shell",
"command": "pylint beispiel.py",
"group": "build"
},
{
"label": "Typprüfung (mypy)",
"type": "shell",
"command": "mypy beispiel.py",
"group": "build"
}
]
}

151
README.md
View File

@ -1,6 +1,7 @@
# Python Bootstrap-Template mit `.venv` und `.env` Support
Dieses Projekt bietet ein portables Start-Template für Python-Anwendungen mit folgenden Features:
Dieses Template nutzt PEP 8, Type Hints, Docstrings und einen vordefinierten Workspace für sauberen Python-Code.
Ausserdem bietet es ein portables Start-Template für Python-Anwendungen mit folgenden Features:
- Automatische Erstellung einer virtuellen Umgebung (`.venv`)
- Automatische Installation von Abhängigkeiten aus `requirements.txt`
@ -9,6 +10,10 @@ Dieses Projekt bietet ein portables Start-Template für Python-Anwendungen mit f
- Sauberer Einstiegspunkt über `run.py`
- Keine systemweiten Python-Pakete notwendig
Das Template ist durchdacht, pragmatisch und stark auf Entwicklerkomfort ausgelegt.
Es bietet eine sehr gute Grundlage für Projekte aller Art insbesondere CLI-Tools, kleine Services und lokale Anwendungen.
Die automatische Einrichtung der virtuellen Umgebung hebt es funktional deutlich von Standard-Vorlagen ab.
---
- [Python Bootstrap-Template mit `.venv` und `.env` Support](#python-bootstrap-template-mit-venv-und-env-support)
@ -19,8 +24,14 @@ Dieses Projekt bietet ein portables Start-Template für Python-Anwendungen mit f
- [⚙️ .env-Datei (optional)](#-env-datei-optional)
- [📜 Beispielausgabe](#-beispielausgabe)
- [🧼 Optional: `.env.example`](#-optional-envexample)
- [🪵 Logging](#-logging)
- [🔧 Konfiguration (in `.env`)](#-konfiguration-in-env)
- [📥 Beispielausgabe](#-beispielausgabe-1)
- [📌 Logik im Code](#-logik-im-code)
- [📁 Logrotation](#-logrotation)
- [🛠 Hinweise](#-hinweise)
- [🧪 Getestet mit](#-getestet-mit)
- [🛠 Einsatz Linter (`pylint`)](#-einsatz-linter-pylint)
- [📁 Lizenz](#-lizenz)
---
@ -29,14 +40,20 @@ Dieses Projekt bietet ein portables Start-Template für Python-Anwendungen mit f
```plaintext
template-root/
├── .env # Projektkonfiguration (optional, wird automatisch geladen)
├── requirements.txt # Abhängigkeiten (z.B. python-dotenv)
├── run.py # Einstiegspunkt für die Anwendung
└── app/
├── __init__.py
├── main.py # Hauptlogik der Anwendung
└── bootstrap.py # Setup- und Relaunch-Logik
📁 template-root/
├── 📁 .vscode/ # Projekteinstellungen VS-Code
│ ├── 📄 settings.json # Einstellungen
│ └── 📄 extensions.json # Erweiterungen
├── 📄 .env # Projektkonfiguration (optional, nicht im git)
├── 📄 .env.example # Vorlage der .env
├── 📄 requirements.txt # Abhängigkeiten (z.B. python-dotenv)
├── 📄 README.md # diese Datei
├── 📄 VERSION # Versionsinfo zum Paket
├── 📄 run.py # Einstiegspunkt für die Anwendung
└── 📁 app/ #
├── 📄 __init__.py #
├── 📄 main.py # Hauptlogik der Anwendung
└── 📄 bootstrap.py # Setup- und Relaunch-Logik
```
---
@ -49,17 +66,18 @@ python run.py
### Beim ersten Start passiert:
1. `.venv` wird erstellt (wenn noch nicht vorhanden)
2. `requirements.txt` wird installiert
3. Das Skript wird automatisch innerhalb der venv neu gestartet
4. `.env` wird geladen (falls vorhanden)
5. Die App startet
1. `.venv` wird erstellt (wenn noch nicht vorhanden)
2. `requirements.txt` wird installiert
3. Das Skript wird automatisch innerhalb der venv neu gestartet
4. `.env` wird geladen (falls vorhanden)
5. Die App startet
---
## 📦 Abhängigkeiten
Alle Abhängigkeiten werden aus `requirements.txt` installiert. Beispiel:
Alle Abhängigkeiten werden aus `requirements.txt` installiert.
**Beispiel:**
```text
python-dotenv
@ -69,7 +87,8 @@ python-dotenv
## ⚙️ .env-Datei (optional)
Wenn vorhanden, wird `.env` automatisch geladen. Beispiel:
Wenn vorhanden, wird `.env` automatisch geladen.
**Beispiel:**
```dotenv
APP_MODE=development
@ -77,26 +96,26 @@ LOGLEVEL=debug
PORT=8080
```
Diese Werte sind im Code über `os.getenv("APP_MODE")` verfügbar.
Diese Werte sind im Code über `os.getenv("APP_MODE")` verfügbar.
---
## 📜 Beispielausgabe
```text
[BOOTSTRAP] Erstelle virtuelle Umgebung...
[BOOTSTRAP] Installiere pip + requirements.txt...
[BOOTSTRAP] Starte in virtueller Umgebung neu...
[RUN] Lade .env aus: ./cliqrcode/.env
[APP] Starte Anwendung im Modus: development
[APP] Hello, world!
[BOOTSTRAP] Erstelle virtuelle Umgebung...
[BOOTSTRAP] Installiere pip + requirements.txt...
[BOOTSTRAP] Starte in virtueller Umgebung neu...
[RUN] Lade .env aus: ./cliqrcode/.env
[APP] Starte Anwendung im Modus: development
[APP] Hello, world!
```
---
## 🧼 Optional: `.env.example`
Erstelle eine `.env.example`, um deine Konfiguration zu dokumentieren:
Erstelle eine `.env.example`, um deine Konfiguration zu dokumentieren:
```dotenv
APP_MODE=production
@ -106,11 +125,61 @@ LOGLEVEL=info
---
## 🪵 Logging
Dieses Template verwendet ein integriertes Logging-Modul mit folgenden Eigenschaften:
- Ausgabe in die Konsole (immer aktiv)
- Optionale Ausgabe in eine Logdatei (`LOGFILE`)
- Unterstützung für rotierende Logdateien
- Loglevel konfigurierbar über `.env`
- Plattformunabhängig (Windows, Linux, macOS)
- Keine externen Abhängigkeiten
### 🔧 Konfiguration (in `.env`)
```dotenv
LOGLEVEL=INFO # Möglich: DEBUG, INFO, WARNING, ERROR, CRITICAL
LOGFILE=logs/app.log # Optionaler Pfad zur Logdatei (relativ oder absolut)
```
> Wenn `LOGFILE` nicht gesetzt ist, wird nur in die Konsole geloggt.
### 📥 Beispielausgabe
```bash
[2025-04-23 14:10:00] INFO app.main: Template ready.
[2025-04-23 14:10:00] DEBUG app.main: Dies ist eine Debug-Meldung.
```
### 📌 Logik im Code
In beliebigen Modulen kannst du so einen Logger verwenden:
```python
from app.logging_utils import get_logger
log = get_logger(__name__)
log.info("Template ready.")
```
### 📁 Logrotation
Die Logdatei wird bei 1MB automatisch rotiert (max. 3 Backups), z.B.:
```bash
logs/app.log
logs/app.log.1
logs/app.log.2
```
---
## 🛠 Hinweise
- Das Template ist portabel und benötigt keine global installierten Pakete.
- Du kannst es für jede neue App wiederverwenden.
- `run.py` ist der einzige Einstiegspunkt keine direkten Aufrufe von `main.py`.
- Das Template ist portabel und benötigt keine global installierten Pakete.
- Du kannst es für jede neue App wiederverwenden.
- `run.py` ist der einzige Einstiegspunkt keine direkten Aufrufe von `main.py`.
---
@ -122,6 +191,32 @@ LOGLEVEL=info
---
## 🛠 Einsatz Linter (`pylint`)
```cmd
PS C:\Users\adams\Documents\template> .\.venv\Scripts\activate
```
```cmd
(.venv) PS C:\Users\adams\Documents\template> pylint.exe run.py
```
```cmd
************* Module run
run.py:27:4: C0412: Imports from package app are not grouped (ungrouped-imports)
run.py:12:0: W0611: Unused import os (unused-import)
-----------------------------------
Your code has been rated at 8.33/10
```
**Bonus:**
Durch den Einsatz der <.vscode/task.json> für VS-Code, kannst du in VS-Code mit `Strg + Umschalt + P``Tasks: Run Task``Linter (pylint)` oder `Typprüfung (mypy)` aufrufen.
---
## 📁 Lizenz
MIT frei verwendbar in eigenen Projekten.
MIT frei verwendbar in eigenen Projekten.

View File

@ -1 +1 @@
0.1.0
1.0.0

View File

@ -0,0 +1,8 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Hier liegen die Dateien für die primäre Logik der Anwendung
Diese Information hier stammt aus der datei ./app/__init__.py
"""

View File

@ -1,3 +1,5 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Bootstrap-Modul für automatische Einrichtung und Start der App.
@ -8,8 +10,8 @@ Dieses Modul stellt sicher, dass:
"""
import os
import sys
import subprocess
import sys
from pathlib import Path
# Pfad zur virtuellen Umgebung im Projektverzeichnis
@ -17,14 +19,15 @@ VENV_DIR = Path(__file__).resolve().parent.parent / ".venv"
# Pfad zum Python-Interpreter in der venv
PYTHON_EXE = VENV_DIR / ("Scripts" if os.name == "nt" else "bin") / "python"
def ensure_venv():
"""
Prüft, ob die .venv existiert und ob das aktuelle Skript
bereits innerhalb der venv ausgeführt wird.
Falls nicht, wird:
- die venv erstellt
- die .venv erstellt
- requirements.txt installiert
- das Skript in der venv neu gestartet
- das Skript in der .venv neu gestartet
"""
if os.environ.get("BOOTSTRAPPED") == "1":
return # Bereits innerhalb der .venv → nichts tun
@ -35,6 +38,7 @@ def ensure_venv():
if Path(sys.executable).resolve() != PYTHON_EXE.resolve():
_relaunch()
def _create_venv():
"""
Legt eine virtuelle Umgebung im Projektverzeichnis an
@ -48,9 +52,14 @@ def _create_venv():
req_file = Path(__file__).resolve().parent.parent / "requirements.txt"
if req_file.exists():
subprocess.check_call([str(PYTHON_EXE), "-m", "pip", "install", "-r", str(req_file)])
subprocess.check_call(
[str(PYTHON_EXE), "-m", "pip", "install", "-r", str(req_file)]
)
else:
print("[BOOTSTRAP] ⚠️ Keine requirements.txt gefunden Installation übersprungen.")
print(
"[BOOTSTRAP] ⚠️ Keine requirements.txt gefunden Installation übersprungen."
)
def _relaunch():
"""

62
app/logging_utils.py Normal file
View File

@ -0,0 +1,62 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Zentrales Logging-Modul
Unterstützt LOGLEVEL und LOGFILE aus der .env
Plattformunabhängig (Windows/Linux)
Erstellt automatisch das Log-Verzeichnis bei Bedarf
Fällt bei ungültigem Log-Level sicher auf INFO zurück
"""
import logging
import os
from logging.handlers import RotatingFileHandler
from pathlib import Path
def safe_log_level(level_str: str) -> int:
"""Wandelt einen Level-String in einen gültigen Logging-Level um."""
levels = {
"CRITICAL": logging.CRITICAL,
"ERROR": logging.ERROR,
"WARNING": logging.WARNING,
"INFO": logging.INFO,
"DEBUG": logging.DEBUG,
"NOTSET": logging.NOTSET,
}
return levels.get(level_str.upper(), logging.INFO)
LOGLEVEL = safe_log_level(os.getenv("LOGLEVEL", "INFO"))
LOGFILE = os.getenv("LOGFILE") # z.B. logs/app.log
def get_logger(name: str) -> logging.Logger:
logger = logging.getLogger(name)
if logger.handlers:
return logger # Logger bereits konfiguriert
logger.setLevel(LOGLEVEL)
formatter = logging.Formatter("[%(asctime)s] %(levelname)s %(name)s: %(message)s")
# Konsole
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
# Datei (optional)
if LOGFILE:
logfile_path = Path(LOGFILE)
try:
logfile_path.parent.mkdir(parents=True, exist_ok=True)
file_handler = RotatingFileHandler(
logfile_path, maxBytes=1_000_000, backupCount=3, encoding="utf-8"
)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
except Exception as e:
logger.warning(f"Konnte Logdatei nicht schreiben: {e}")
return logger

View File

@ -1,12 +1,39 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
./app/main.py
Hier beginnt deine eigentliche Anwendung.
Alle Konfigurationen aus .env sind jetzt über os.getenv() verfügbar.
"""
import os
from app.logging_utils import get_logger
log = get_logger(__name__)
def main():
mode = os.getenv("APP_MODE", "development")
print(f"[APP] Starte Anwendung im Modus: {mode}")
print("[APP] Hello, world!")
"""
Hier liegen die Dateien für die primäre Logik der Anwendung
Diese Information hier stammt aus der datei ./app/__init__.py
"""
# Hole APP_MODE aus der .env
mode = os.getenv("APP_MODE", "DEVEL")
# Testausgabe:
print(f"[APP] 🚀 Starte Anwendung im Modus: {mode}")
print("[APP] 📦 -= Hello, world! =-")
def logtest():
"""
wirft testweise alle Logvarianten aus.
"""
log.info("Template ready.")
log.debug("Dies ist eine Debug-Meldung.")
log.warning("Dies ist eine Warnung.")
log.error("Dies ist eine Fehlermeldung.")
log.critical("Dies ist eine kritische Meldung.")

View File

@ -1 +1,16 @@
# -*- coding: utf-8 -*-
# falls man .env verwenden möchte:
python-dotenv
# Projektbezogen ab hier:
# --- ALLES AB HIER IST OPTIONAL ---
# Pytest, Linter und Typprüfung:
pytest
pytest-cov
pytest-mock
pylint
mypy

29
run.py
View File

@ -1,28 +1,39 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Einstiegspunkt der Anwendung.
Sorgt dafür, dass beim ersten Start automatisch:
- eine virtuelle Umgebung (.venv) angelegt wird
- alle Abhängigkeiten installiert werden
- das Skript in der .venv neu gestartet wird
Erst danach wird die .env geladen und die App gestartet.
Einstiegspunkt der Anwendung.
Sorgt dafür, dass beim ersten Start automatisch:
- eine virtuelle Umgebung (.venv) angelegt wird
- alle Abhängigkeiten installiert werden
- das Skript in der .venv neu gestartet wird
Erst danach wird die .env geladen und die App gestartet.
"""
import os
from app.bootstrap import ensure_venv
if __name__ == "__main__":
ensure_venv()
# .env laden jetzt ist python-dotenv installiert (innerhalb der venv)
from dotenv import load_dotenv, find_dotenv
from dotenv import find_dotenv, load_dotenv
dotenv_path = find_dotenv()
if dotenv_path:
print(f"\n[RUN] Lade .env aus: {dotenv_path}")
print(f"\n[RUN] 🚀 Lade .env aus: {dotenv_path}")
load_dotenv(dotenv_path=dotenv_path, override=True)
else:
print("[RUN] ⚠️ Keine .env-Datei gefunden")
print("\n[RUN] ⚠️ Keine .env-Datei gefunden")
# Ab hier deine Funktionen aufrufen:
from app.main import main
main()
from app.main import logtest
logtest()