From ee80f056ffb1b9917e5e0d1626a9c0a0e1a9e33d Mon Sep 17 00:00:00 2001 From: Adam Skotarczak Date: Tue, 22 Apr 2025 15:00:06 +0200 Subject: [PATCH] initial commit --- .gitignore | 174 +++++++++++++++++++++++++++++++++++++++++++++++ README.md | 127 ++++++++++++++++++++++++++++++++++ VERSION | 1 + app/__init__.py | 0 app/bootstrap.py | 62 +++++++++++++++++ app/main.py | 12 ++++ requirements.txt | 1 + run.py | 28 ++++++++ 8 files changed, 405 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 VERSION create mode 100644 app/__init__.py create mode 100644 app/bootstrap.py create mode 100644 app/main.py create mode 100644 requirements.txt create mode 100644 run.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0a19790 --- /dev/null +++ b/.gitignore @@ -0,0 +1,174 @@ +# 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 +.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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..31a49fc --- /dev/null +++ b/README.md @@ -0,0 +1,127 @@ +# Python Bootstrap-Template mit `.venv` und `.env` Support + +Dieses Projekt bietet 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 + +--- + +- [Python Bootstrap-Template mit `.venv` und `.env` Support](#python-bootstrap-template-mit-venv-und-env-support) + - [🔧 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) + - [🧼 Optional: `.env.example`](#-optional-envexample) + - [🛠 Hinweise](#-hinweise) + - [🧪 Getestet mit](#-getestet-mit) + - [📁 Lizenz](#-lizenz) + +--- + +## 🔧 Projektstruktur + +```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 +``` + +--- + +## 🚀 Erste Schritte + +```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 + +--- + +## 📦 Abhängigkeiten + +Alle Abhängigkeiten werden aus `requirements.txt` installiert. Beispiel: + +```text +python-dotenv +``` + +--- + +## ⚙️ .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. + +--- + +## 📜 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! +``` + +--- + +## 🧼 Optional: `.env.example` + +Erstelle eine `.env.example`, um deine Konfiguration zu dokumentieren: + +```dotenv +APP_MODE=production +PORT=8000 +LOGLEVEL=info +``` + +--- + +## 🛠 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`. + +--- + +## 🧪 Getestet mit + +- Python 3.11, 3.12, 3.13 +- Windows & Linux +- VS Code, Terminal, PowerShell + +--- + +## 📁 Lizenz + +MIT – frei verwendbar in eigenen Projekten. \ No newline at end of file diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..6c6aa7c --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1.0 \ No newline at end of file diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/bootstrap.py b/app/bootstrap.py new file mode 100644 index 0000000..8aeec20 --- /dev/null +++ b/app/bootstrap.py @@ -0,0 +1,62 @@ + +""" +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 sys +import subprocess +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) diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..38a0609 --- /dev/null +++ b/app/main.py @@ -0,0 +1,12 @@ + +""" +Hier beginnt deine eigentliche Anwendung. +Alle Konfigurationen aus .env sind jetzt über os.getenv() verfügbar. +""" + +import os + +def main(): + mode = os.getenv("APP_MODE", "development") + print(f"[APP] Starte Anwendung im Modus: {mode}") + print("[APP] Hello, world!") diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..566cccb --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +python-dotenv diff --git a/run.py b/run.py new file mode 100644 index 0000000..e810cc3 --- /dev/null +++ b/run.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 + +""" + 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 + 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("[RUN] ⚠️ Keine .env-Datei gefunden") + + from app.main import main + main()