generated from realAscot/template-python
Merge commit '811a53a19d4528a89293562c589b6ce888cc5681' as 'base'
This commit is contained in:
commit
698be84d29
10
base/.env.example
Normal file
10
base/.env.example
Normal file
@ -0,0 +1,10 @@
|
||||
# APP_MODE ungenutzt im Template (production/ development)
|
||||
APP_MODE=development
|
||||
|
||||
# LOGLEVEL: "CRITICAL"- "ERROR" - "WARNING" - "INFO" - "DEBUG"
|
||||
LOGLEVEL=INFO
|
||||
|
||||
# Pfad zum log z.B log/template.log (relativ und absolut beachten!)
|
||||
# Pfad ist ausgehend vom Ort der run.py und Verzeichnisse werden automatisch erstellt.
|
||||
# DEFAULT: log/template.log
|
||||
LOGFILE=log/template.log
|
184
base/.gitignore
vendored
Normal file
184
base/.gitignore
vendored
Normal file
@ -0,0 +1,184 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# UV
|
||||
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
#uv.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
||||
.pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
# Ruff stuff:
|
||||
.ruff_cache/
|
||||
|
||||
# PyPI configuration file
|
||||
.pypirc
|
||||
|
||||
# Custom:
|
||||
logs/
|
||||
log/
|
||||
release/
|
||||
#media/
|
||||
*.zip
|
||||
|
7
base/.vscode/extensions.json
vendored
Normal file
7
base/.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"ms-python.python",
|
||||
"ms-python.vscode-pylance",
|
||||
"ms-toolsai.jupyter"
|
||||
]
|
||||
}
|
30
base/.vscode/settings.jsonc
vendored
Normal file
30
base/.vscode/settings.jsonc
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
{ // Bitte daran denken das Kommentare eigentlich nicht von json unterstützt werden :-)
|
||||
// Das funktioniert hier nur in Microsofts jsonc im VS-Code!
|
||||
"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,
|
||||
"editor.wordWrap": "off"
|
||||
},
|
||||
|
||||
// Für Pythonfiles Tababstand definieren und Tabs durch Leerzeichen ersetzen
|
||||
"[python]": {
|
||||
"editor.tabSize": 4,
|
||||
"editor.insertSpaces": true
|
||||
},
|
||||
|
||||
|
||||
}
|
17
base/.vscode/tasks.json
vendored
Normal file
17
base/.vscode/tasks.json
vendored
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
18
base/CHANGELOG.md
Normal file
18
base/CHANGELOG.md
Normal file
@ -0,0 +1,18 @@
|
||||
# CHANGELOG - Python Template
|
||||
|
||||
- [CHANGELOG - Python Template](#changelog---python-template)
|
||||
- [2025-04-24 - Commit v1.0.1](#2025-04-24---commit-v101)
|
||||
- [2025-04-22 - Release v1.0.0](#2025-04-22---release-v100)
|
||||
|
||||
## 2025-04-24 - Commit v1.0.1
|
||||
|
||||
- **Geändert:**
|
||||
- [X] `README.md` stilistisch überarbeitet.
|
||||
- [x] `.json` in `.jsonc` umbenannt wenn sie Kommentare enthalten. Betrifft nur den .vscode teil und hat mit dem Template nicht direkt etwas zutun.
|
||||
- [x] Im zentralen Logging-Modul `logging_utils.py` wird bei aktiviertem `DEBUG`-Level nun der Pfad zur Logdatei (`LOGFILE`) im Log ausgegeben. Die Ausgabe erfolgt nur, wenn `logger.isEnabledFor(logging.DEBUG)` zutrifft. Fehler bei der Pfadauflösung (`resolve()`) werden dabei abgefangen und blockieren das Logging nicht.
|
||||
- [x] Standardpfad für Logdatei eingeführt: Wird `LOGFILE` nicht über die `.env` gesetzt, wird nun automatisch `"log/app.log"` verwendet. Damit funktioniert das Logging-Modul auch ohne `.env`-Konfiguration direkt nach dem Klonen oder bei lokalen Tests.
|
||||
- [x] `CHANGELOG.md` erstellt.
|
||||
|
||||
## 2025-04-22 - Release v1.0.0
|
||||
|
||||
- **Release 1.0.0** 🚀
|
252
base/README.md
Normal file
252
base/README.md
Normal file
@ -0,0 +1,252 @@
|
||||
# Python Bootstrap-Template mit `.venv`- und `.env` Support
|
||||
|
||||

|
||||
|
||||
Dieses Template nutzt PEP 8, Type Hints, Docstrings und einen vordefinierten Workspace für sauberen Python-Code.
|
||||
Außerdem 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`
|
||||
- Automatischer Neustart in der virtuellen Umgebung
|
||||
- Unterstützung von Umgebungsvariablen über eine `.env`-Datei
|
||||
- Sauberer Einstiegspunkt über `run.py`
|
||||
- Keine systemweiten Python-Pakete notwendig
|
||||
- Logging-Utils bereits integriert
|
||||
|
||||
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.
|
||||
|
||||
**Was dieses template __nicht__ ist:**
|
||||
|
||||
- [ ] [pep-518](https://peps.python.org/pep-0518/)-konform 🚫
|
||||
|
||||
> ⚠️ Dieses Template verfolgt kein komplexes Build-System.
|
||||
Es ist dafür gedacht, dir in Sekunden eine saubere, gekapselte Python-Umgebung bereitzustellen – perfekt zum schnellen Testen, Debuggen oder Projektstart.
|
||||
Einfach deinen Code in main.py werfen, bei Bedarf requirements.txt anpassen, run.py starten – fertig. Kein Setup-Wahnsinn, kein Overhead.
|
||||
|
||||
---
|
||||
|
||||
## 🔜 Inhalt der Readme
|
||||
|
||||
- [Python Bootstrap-Template mit `.venv`- und `.env` Support](#python-bootstrap-template-mit-venv--und-env-support)
|
||||
- [🔜 Inhalt der Readme](#-inhalt-der-readme)
|
||||
- [🔧 Projektstruktur](#-projektstruktur)
|
||||
- [🚀 Erste Schritte](#-erste-schritte)
|
||||
- [Beim ersten Start passiert:](#beim-ersten-start-passiert)
|
||||
- [📦 Abhängigkeiten](#-abhängigkeiten)
|
||||
- [⚙️ .env-Datei (optional)](#️-env-datei-optional)
|
||||
- [📜 Beispielausgabe](#-beispielausgabe)
|
||||
- [🪵 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 von `Linter` (`pylint`)](#-einsatz-von-linter-pylint)
|
||||
- [📁 Lizenz](#-lizenz)
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Projektstruktur
|
||||
|
||||
```plaintext
|
||||
|
||||
📁 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
|
||||
├── 📄 CHANGELOG.md #
|
||||
├── 📄 VERSION # Versionsinfo zum Paket
|
||||
├── 📄 run.py # Einstiegspunkt für die Anwendung
|
||||
├── 📁 media/
|
||||
│ └── 📄 logo.png # Logo für GitHub
|
||||
└── 📁 app/
|
||||
├── 📄 __init__.py #
|
||||
├── 📄 main.py # Hauptlogik der Anwendung
|
||||
└── 📄 bootstrap.py # Setup- und Relaunch-Logik
|
||||
```
|
||||
|
||||
> Release-Pakete als `.zip` sind bereits von unötigem Balast bereinigt. Die dargestellte Struktur entspricht einem `git clone`.
|
||||
|
||||
[🔝](#-inhalt-der-readme)
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Erste Schritte
|
||||
|
||||
- [ ] `.env.example` in `.env` umbenennen und individuell befüllen.
|
||||
- [ ] `.vscode`-Verzeichnis löschen, wenn du eigene Einstellungen nutzt. Ich habe es versehentlich committet und aus Bequemlichkeit drin gelassen, weil es meinem Standard entspicht.
|
||||
- [ ] `requirements.txt` auf deine Bedürfnisse anpassen.
|
||||
- [ ] `media/`Verzeichnis Löschen falls vorhanden.
|
||||
|
||||
**Erster Start des Templates:**
|
||||
|
||||
```bash
|
||||
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 🚀**
|
||||
|
||||
> Es erfolgen einige Ausgaben, die alle aus der `main.py` stammen, außer du `DEBUG` in der `.env` aktiviert hast.
|
||||
|
||||
[🔝](#-inhalt-der-readme)
|
||||
|
||||
---
|
||||
|
||||
## 📦 Abhängigkeiten
|
||||
|
||||
Alle Abhängigkeiten werden aus `requirements.txt` installiert.
|
||||
**Beispiel:**
|
||||
|
||||
```text
|
||||
python-dotenv
|
||||
```
|
||||
|
||||
[🔝](#-inhalt-der-readme)
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ .env-Datei (optional)
|
||||
|
||||
Wenn vorhanden, wird `.env` automatisch geladen.
|
||||
**Beispiel:**
|
||||
|
||||
```dotenv
|
||||
APP_MODE=development
|
||||
LOGLEVEL=debug
|
||||
PORT=8080
|
||||
```
|
||||
|
||||
Diese Werte sind im Code über `os.getenv("APP_MODE")` verfügbar.
|
||||
|
||||
[🔝](#-inhalt-der-readme)
|
||||
|
||||
---
|
||||
|
||||
## 📜 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!
|
||||
```
|
||||
|
||||
[🔝](#-inhalt-der-readme)
|
||||
|
||||
---
|
||||
|
||||
## 🪵 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 1 MB automatisch rotiert (max. 3 Backups), z. B.:
|
||||
|
||||
```bash
|
||||
logs/app.log
|
||||
logs/app.log.1
|
||||
logs/app.log.2
|
||||
```
|
||||
|
||||
[🔝](#-inhalt-der-readme)
|
||||
|
||||
---
|
||||
|
||||
## 🛠 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`.
|
||||
|
||||
[🔝](#-inhalt-der-readme)
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Getestet mit
|
||||
|
||||
- Python 3.11, 3.12, 3.13
|
||||
- Windows & Linux
|
||||
- VS Code, Terminal, PowerShell
|
||||
|
||||
[🔝](#-inhalt-der-readme)
|
||||
|
||||
---
|
||||
|
||||
## 🛠 Einsatz von `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.
|
||||
|
||||
[🔝](#-inhalt-der-readme)
|
||||
|
||||
---
|
||||
|
||||
## 📁 Lizenz
|
||||
|
||||
MIT – frei verwendbar in eigenen Projekten.
|
1
base/VERSION
Normal file
1
base/VERSION
Normal file
@ -0,0 +1 @@
|
||||
1.0.1
|
8
base/app/__init__.py
Normal file
8
base/app/__init__.py
Normal 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
|
||||
"""
|
71
base/app/bootstrap.py
Normal file
71
base/app/bootstrap.py
Normal file
@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Bootstrap-Modul für automatische Einrichtung und Start der App.
|
||||
Dieses Modul stellt sicher, dass:
|
||||
- eine .venv angelegt ist
|
||||
- python -m pip install -r requirements.txt ausgeführt wurde
|
||||
- das Skript in der .venv neu gestartet wird, falls nötig
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Pfad zur virtuellen Umgebung im Projektverzeichnis
|
||||
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
|
||||
- requirements.txt installiert
|
||||
- das Skript in der .venv neu gestartet
|
||||
"""
|
||||
if os.environ.get("BOOTSTRAPPED") == "1":
|
||||
return # Bereits innerhalb der .venv → nichts tun
|
||||
|
||||
if not VENV_DIR.exists():
|
||||
_create_venv()
|
||||
|
||||
if Path(sys.executable).resolve() != PYTHON_EXE.resolve():
|
||||
_relaunch()
|
||||
|
||||
|
||||
def _create_venv():
|
||||
"""
|
||||
Legt eine virtuelle Umgebung im Projektverzeichnis an
|
||||
und installiert alle Pakete aus requirements.txt.
|
||||
"""
|
||||
print("[BOOTSTRAP] Erstelle virtuelle Umgebung...")
|
||||
subprocess.check_call([sys.executable, "-m", "venv", str(VENV_DIR)])
|
||||
|
||||
print("[BOOTSTRAP] Installiere pip + requirements.txt...")
|
||||
subprocess.check_call([str(PYTHON_EXE), "-m", "pip", "install", "--upgrade", "pip"])
|
||||
|
||||
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)]
|
||||
)
|
||||
else:
|
||||
print(
|
||||
"[BOOTSTRAP] ⚠️ Keine requirements.txt gefunden – Installation übersprungen."
|
||||
)
|
||||
|
||||
|
||||
def _relaunch():
|
||||
"""
|
||||
Startet das Skript innerhalb der .venv neu.
|
||||
Verwendet os.execv(), um den Prozess vollständig zu ersetzen.
|
||||
"""
|
||||
print("\n[BOOTSTRAP] Starte in virtueller Umgebung neu...")
|
||||
os.environ["BOOTSTRAPPED"] = "1"
|
||||
os.execv(str(PYTHON_EXE), [str(PYTHON_EXE)] + sys.argv)
|
70
base/app/logging_utils.py
Normal file
70
base/app/logging_utils.py
Normal file
@ -0,0 +1,70 @@
|
||||
#!/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", "log/app.log") # 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)
|
||||
|
||||
# Debug: Logpfad anzeigen (nur bei DEBUG)
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
try:
|
||||
logger.debug(f"Logdatei: {logfile_path.resolve()}")
|
||||
except Exception:
|
||||
pass # Debug-Ausgabe darf nicht blockieren
|
||||
|
||||
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
|
41
base/app/main.py
Normal file
41
base/app/main.py
Normal file
@ -0,0 +1,41 @@
|
||||
#!/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():
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
print(f"\n[IFO] 📰 Loglevel: aus .env: {os.getenv('LOGFILE')}\n")
|
||||
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.")
|
BIN
base/media/logo.png
Normal file
BIN
base/media/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 918 KiB |
16
base/requirements.txt
Normal file
16
base/requirements.txt
Normal file
@ -0,0 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# falls man .env verwenden möchte:
|
||||
python-dotenv
|
||||
|
||||
# Projektbezogen ab hier:
|
||||
|
||||
|
||||
# --- ALLES AB HIER IST OPTIONAL ---
|
||||
|
||||
# Pytest, Linter und Typ-Prüfung (entfernen bei fertigem Code):
|
||||
pytest
|
||||
pytest-cov
|
||||
pytest-mock
|
||||
pylint
|
||||
mypy
|
39
base/run.py
Normal file
39
base/run.py
Normal file
@ -0,0 +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.
|
||||
"""
|
||||
|
||||
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 find_dotenv, load_dotenv
|
||||
|
||||
dotenv_path = find_dotenv()
|
||||
if dotenv_path:
|
||||
print(f"\n[RUN] 🚀 Lade .env aus: {dotenv_path}")
|
||||
load_dotenv(dotenv_path=dotenv_path, override=True)
|
||||
else:
|
||||
print("\n[RUN] ⚠️ Keine .env-Datei gefunden")
|
||||
|
||||
# Ab hier deine Funktionen aufrufen:
|
||||
|
||||
from app.main import main
|
||||
|
||||
main()
|
||||
|
||||
from app.main import logtest
|
||||
|
||||
logtest()
|
Loading…
x
Reference in New Issue
Block a user