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 # Environments
.env .env
.env.*
!.env.example
.venv .venv
env/ env/
venv/ venv/
@ -172,3 +174,9 @@ cython_debug/
# PyPI configuration file # PyPI configuration file
.pypirc .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"
}
]
}

117
README.md
View File

@ -1,6 +1,7 @@
# Python Bootstrap-Template mit `.venv` und `.env` Support # 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 Erstellung einer virtuellen Umgebung (`.venv`)
- Automatische Installation von Abhängigkeiten aus `requirements.txt` - 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` - Sauberer Einstiegspunkt über `run.py`
- Keine systemweiten Python-Pakete notwendig - 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) - [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) - [⚙️ .env-Datei (optional)](#-env-datei-optional)
- [📜 Beispielausgabe](#-beispielausgabe) - [📜 Beispielausgabe](#-beispielausgabe)
- [🧼 Optional: `.env.example`](#-optional-envexample) - [🧼 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) - [🛠 Hinweise](#-hinweise)
- [🧪 Getestet mit](#-getestet-mit) - [🧪 Getestet mit](#-getestet-mit)
- [🛠 Einsatz Linter (`pylint`)](#-einsatz-linter-pylint)
- [📁 Lizenz](#-lizenz) - [📁 Lizenz](#-lizenz)
--- ---
@ -29,14 +40,20 @@ Dieses Projekt bietet ein portables Start-Template für Python-Anwendungen mit f
```plaintext ```plaintext
template-root/ 📁 template-root/
├── .env # Projektkonfiguration (optional, wird automatisch geladen) ├── 📁 .vscode/ # Projekteinstellungen VS-Code
├── requirements.txt # Abhängigkeiten (z.B. python-dotenv) │ ├── 📄 settings.json # Einstellungen
├── run.py # Einstiegspunkt für die Anwendung │ └── 📄 extensions.json # Erweiterungen
└── app/ ├── 📄 .env # Projektkonfiguration (optional, nicht im git)
├── __init__.py ├── 📄 .env.example # Vorlage der .env
├── main.py # Hauptlogik der Anwendung ├── 📄 requirements.txt # Abhängigkeiten (z.B. python-dotenv)
└── bootstrap.py # Setup- und Relaunch-Logik ├── 📄 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
``` ```
--- ---
@ -59,7 +76,8 @@ python run.py
## 📦 Abhängigkeiten ## 📦 Abhängigkeiten
Alle Abhängigkeiten werden aus `requirements.txt` installiert. Beispiel: Alle Abhängigkeiten werden aus `requirements.txt` installiert.
**Beispiel:**
```text ```text
python-dotenv python-dotenv
@ -69,7 +87,8 @@ python-dotenv
## ⚙️ .env-Datei (optional) ## ⚙️ .env-Datei (optional)
Wenn vorhanden, wird `.env` automatisch geladen. Beispiel: Wenn vorhanden, wird `.env` automatisch geladen.
**Beispiel:**
```dotenv ```dotenv
APP_MODE=development APP_MODE=development
@ -106,6 +125,56 @@ 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 ## 🛠 Hinweise
- Das Template ist portabel und benötigt keine global installierten Pakete. - Das Template ist portabel und benötigt keine global installierten Pakete.
@ -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 ## 📁 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. Bootstrap-Modul für automatische Einrichtung und Start der App.
@ -8,8 +10,8 @@ Dieses Modul stellt sicher, dass:
""" """
import os import os
import sys
import subprocess import subprocess
import sys
from pathlib import Path from pathlib import Path
# Pfad zur virtuellen Umgebung im Projektverzeichnis # 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 # Pfad zum Python-Interpreter in der venv
PYTHON_EXE = VENV_DIR / ("Scripts" if os.name == "nt" else "bin") / "python" PYTHON_EXE = VENV_DIR / ("Scripts" if os.name == "nt" else "bin") / "python"
def ensure_venv(): def ensure_venv():
""" """
Prüft, ob die .venv existiert und ob das aktuelle Skript Prüft, ob die .venv existiert und ob das aktuelle Skript
bereits innerhalb der venv ausgeführt wird. bereits innerhalb der venv ausgeführt wird.
Falls nicht, wird: Falls nicht, wird:
- die venv erstellt - die .venv erstellt
- requirements.txt installiert - requirements.txt installiert
- das Skript in der venv neu gestartet - das Skript in der .venv neu gestartet
""" """
if os.environ.get("BOOTSTRAPPED") == "1": if os.environ.get("BOOTSTRAPPED") == "1":
return # Bereits innerhalb der .venv → nichts tun return # Bereits innerhalb der .venv → nichts tun
@ -35,6 +38,7 @@ def ensure_venv():
if Path(sys.executable).resolve() != PYTHON_EXE.resolve(): if Path(sys.executable).resolve() != PYTHON_EXE.resolve():
_relaunch() _relaunch()
def _create_venv(): def _create_venv():
""" """
Legt eine virtuelle Umgebung im Projektverzeichnis an Legt eine virtuelle Umgebung im Projektverzeichnis an
@ -48,9 +52,14 @@ def _create_venv():
req_file = Path(__file__).resolve().parent.parent / "requirements.txt" req_file = Path(__file__).resolve().parent.parent / "requirements.txt"
if req_file.exists(): 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: else:
print("[BOOTSTRAP] ⚠️ Keine requirements.txt gefunden Installation übersprungen.") print(
"[BOOTSTRAP] ⚠️ Keine requirements.txt gefunden Installation übersprungen."
)
def _relaunch(): 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. Hier beginnt deine eigentliche Anwendung.
Alle Konfigurationen aus .env sind jetzt über os.getenv() verfügbar. Alle Konfigurationen aus .env sind jetzt über os.getenv() verfügbar.
""" """
import os import os
from app.logging_utils import get_logger
log = get_logger(__name__)
def main(): def main():
mode = os.getenv("APP_MODE", "development") """
print(f"[APP] Starte Anwendung im Modus: {mode}") Hier liegen die Dateien für die primäre Logik der Anwendung
print("[APP] Hello, world!") 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 python-dotenv
# Projektbezogen ab hier:
# --- ALLES AB HIER IST OPTIONAL ---
# Pytest, Linter und Typprüfung:
pytest
pytest-cov
pytest-mock
pylint
mypy

17
run.py
View File

@ -1,7 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*-
""" """
Einstiegspunkt der Anwendung. Einstiegspunkt der Anwendung.
Sorgt dafür, dass beim ersten Start automatisch: Sorgt dafür, dass beim ersten Start automatisch:
- eine virtuelle Umgebung (.venv) angelegt wird - eine virtuelle Umgebung (.venv) angelegt wird
- alle Abhängigkeiten installiert werden - alle Abhängigkeiten installiert werden
@ -10,19 +12,28 @@
""" """
import os import os
from app.bootstrap import ensure_venv from app.bootstrap import ensure_venv
if __name__ == "__main__": if __name__ == "__main__":
ensure_venv() ensure_venv()
# .env laden jetzt ist python-dotenv installiert (innerhalb der 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() dotenv_path = find_dotenv()
if dotenv_path: 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) load_dotenv(dotenv_path=dotenv_path, override=True)
else: else:
print("[RUN] ⚠️ Keine .env-Datei gefunden") print("\n[RUN] ⚠️ Keine .env-Datei gefunden")
# Ab hier deine Funktionen aufrufen:
from app.main import main from app.main import main
main() main()
from app.main import logtest
logtest()