Python¶
Siehe auch
- Verwandte Artikel
- Offizielle Dokumentation
- Linuxfabrik
Python ist eine interpretierte, dynamisch typisierte Hochsprache und gehört auf RHEL-Systemen
zur Basisausstattung - viele Systemwerkzeuge wie dnf, firewalld oder
subscription-manager sind in Python geschrieben. Python 2 wurde am 2020-01-01
abgekündigt und ist weder in RHEL 8 noch in neueren Versionen standardmässig enthalten. Dieser
Artikel bezieht sich ausschliesslich auf Python 3 und als untere Grenze auf RHEL 8.
Eigene Software der Linuxfabrik (firewallfabrik, linuxfabrik-lib, Monitoring-Plugins)
verlangt häufig eine deutlich neuere Python-Version als die RHEL-Standardinstallation
mitbringt. Beispiel firewallfabrik: requires-python = ">=3.14". Eine parallele
Python-Installation über DNF Module- oder Application-Streams ist der normale Weg
(siehe Abschnitt installation).
- Begriffe
bandit: Security-Scanner für typische Python-Sicherheitsprobleme.
PEP 621: definiert die Projekt-Metadaten im
[project]-Abschnitt vonpyproject.toml.PEP 668: „Externally Managed Environments“ - ab Python 3.11 markieren Distributionen ihr System-Python als geschützt, sodass
pip installohne venv mit einer Fehlermeldung abbricht.PEP 735: definiert
[dependency-groups]für Dev-Abhängigkeiten, die nicht im gebauten Wheel landen.pip: Referenz-Paketmanager der Python Packaging Authority (PyPA). Installiert Pakete aus PyPI in die aktive Umgebung.
pipx: installiert eigenständige Python-CLI-Tools (z.B.
ruff,httpie,ansible) jeweils in eine eigene venv und legt Wrapper nach~/.local/bin/.PyPI: Python Package Index, gesprochen „Pie Pea Eye“, der zentrale Paket-Hub für Python.
pyright: statischer Type-Checker. Bei der Linuxfabrik (z.B. FirewallFabrik) der bevorzugte Type-Checker, nicht
mypy.pytest: De-facto-Standard-Test-Framework.
ruff: Linter und Formatter in einem, ebenfalls in Rust; ersetzt Kombinationen aus
pylint,flake8,isortundblack.System-Python: die mit dem Betriebssystem gelieferte Python-Installation. Wird von
dnfund anderen RHEL-Tools benutzt. Nicht anfassen, keine Pakete perpipglobal installieren.TOML / pyproject.toml: Tom’s Obvious Minimal Language ist ein schlankes Konfigurationsformat mit Sections (
[section]) undkey = value-Einträgen, ähnlich INI, aber mit echten Datentypen (Strings, Zahlen, Listen, verschachtelte Tables).pyproject.tomlim Projekt-Root ist seit PEP 518 die zentrale Konfigurationsdatei eines Python-Projekts und löstsetup.py,setup.cfg,requirements*.txtsowie verstreute Tool-Configs (.flake8,.pylintrc,pytest.ini, …) ab. Sie enthält Build-System, Projekt-Metadaten (PEP 621) und Tool-Konfigurationen ([tool.ruff],[tool.bandit], usw.). YAML wurde bei der Wahl des Formats bewusst verworfen. Die Gründe, nachzulesen in der PEP-518-Diskussion:YAML-Parser sind gross und komplex. Ein minimaler TOML-Parser lässt sich in wenigen hundert Zeilen bauen und im Bootstrap von
pipmitliefern. Seit Python 3.11 gehörttomllibzur Standardbibliothek.YAML ist whitespace-sensitiv. Inkonsistente Einrückung führt still zu anderen Strukturen, was für Konfigurationsdateien unnötig riskant ist.
YAML ist kontextabhängig typisiert. Berühmt ist das sogenannte „Norway-Problem“:
NOwird als Booleanfalsegeparst statt als String.yaml.load()konnte historisch beliebigen Python-Code ausführen; nuryaml.safe_load()ist sicher. In TOML gibt es diese Fussangel nicht.
uv: sehr schneller, in Rust geschriebener Ersatz für
pipundvenv(Astral). Abwärtskompatibel, primär für CI-Pipelines und Entwicklerworkstations interessant.venv: virtuelle Umgebung, ein isolierter Python-Interpreter samt eigenem
site-packages-Verzeichnis. Wird pro Projekt erstellt und ersetzt früher übliche Tools wievirtualenvodervirtualenvwrapper.vulture: findet nicht verwendete Funktionen, Variablen und Imports („toter Code“).
Wheel: das Standard-Binärformat für Python-Pakete (
.whl), ersetzt das ältere Egg-Format.
Installation¶
System-Python ist auf allen unterstützten RHEL-Versionen bereits installiert oder wird als Abhängigkeit von Basispaketen mitgezogen. Die folgende Tabelle zeigt die standardmässig verfügbaren Python-Versionen:
Distribution |
System-Python |
Alternative Versionen via |
|---|---|---|
RHEL 8 |
3.6 |
|
RHEL 9 |
3.9 |
|
RHEL 10 |
3.12 |
|
Bemerkung
RHEL 8 verwendet das Module-System für parallele Python-Versionen. Ab RHEL 9 werden die neueren Interpreter als reguläre Pakete geliefert und lassen sich parallel installieren.
System-Python explizit installieren (ist aber praktisch immer schon vorhanden):
# RHEL 8+
dnf --assumeyes install python3 python3-pip
Neuere Python-Version parallel installieren. Beispiel Python 3.12 auf RHEL 9:
dnf --assumeyes install python3.12 python3.12-pip
Auf RHEL 8 läuft das über dnf module:
dnf --assumeyes module install python3.12/common
Die Alternative python3.12 ist danach als eigener Interpreter unter /usr/bin/python3.12
verfügbar. python3 bleibt weiterhin auf die System-Version zeigen.
Warnung
Niemals pip install ohne venv als root ausführen. Das würde Dateien in das
System-Python-Verzeichnis schreiben, die dnf dann nicht mehr verwaltet - der
klassische Weg ins Chaos bei Updates. Ab RHEL 10 bzw. Python 3.11 wird das per PEP 668
sogar aktiv blockiert.
Virtuelle Umgebungen¶
Jedes Python-Projekt (auch das kleinste Skript mit externer Abhängigkeit) gehört in eine eigene venv. Nur so bleibt das System-Python sauber und Projekte können unterschiedliche Bibliotheks-Versionen nutzen, ohne sich gegenseitig zu beschädigen.
# create the venv (unprivileged user, home directory)
python3 -m venv ~/venvs/myproject
# activate the venv
source ~/venvs/myproject/bin/activate
# upgrade pip, setuptools and wheel
python3 -m pip install --upgrade pip setuptools wheel
# install the required packages
python3 -m pip install requests pyyaml
# leave the venv
deactivate
Nach dem Aktivieren zeigt der Prompt das aktuelle venv an (z.B. (myproject)) und
python, python3 und pip verweisen auf den venv-Interpreter. Zum endgültigen
Entfernen reicht ein rm --recursive --force ~/venvs/myproject.
Eine spezifische Python-Version für die venv wählen:
python3.12 -m venv ~/venvs/myproject
requirements.txt¶
Abhängigkeiten eines Projekts werden in einer requirements.txt festgehalten und
versionsgenau gepinnt:
pyyaml==6.0.1
requests==2.31.0
Installation:
python3 -m pip install --requirement requirements.txt
Für reproduzierbare Builds empfiehlt sich pip-tools (pip-compile) oder das
wesentlich schnellere uv, das aus einer requirements.in einen vollständig aufgelösten
Lockfile mit Hashes erzeugt.
Moderne Projekte verzichten auf requirements.txt komplett und deklarieren alle
Abhängigkeiten in pyproject.toml (siehe Packaging und Veröffentlichung auf PyPI).
pipx¶
Für eigenständige Python-CLI-Tools (ansible, ruff, httpie, twine, …) ist
pipx die richtige Wahl. Jedes Tool bekommt eine eigene, isolierte venv, die Executables
landen in ~/.local/bin/:
dnf --assumeyes install pipx
pipx ensurepath
pipx install ruff
pipx install --python python3.12 ansible
pipx list
pipx upgrade ruff
pipx uninstall ruff
Code-Qualität¶
Linuxfabrik-Konvention: erst die deterministischen Static-Analysis-Tools (ruff,
pyright, bandit, vulture) sauber laufen lassen, danach erst LLM-Agents. Alle
Toolkonfigurationen liegen in pyproject.toml, damit das Projekt ohne Zusatzdateien
portabel bleibt. pre-commit run --all-files deckt die komplette Codebasis ab, nicht nur
die staged Files.
Syntax-Check¶
Schneller Offline-Check, ohne Tools zu installieren:
python3 -m py_compile script.py
Lint und Format mit ruff¶
ruff ist Linter und Formatter in einem. Ein Bruchteil der Laufzeit von pylint +
black + isort, vollständig in pyproject.toml konfigurierbar.
pipx install ruff
ruff check . # lint
ruff format . # format (black-compatible)
ruff check --fix . # auto-fix
Linuxfabrik-Standard-Konfiguration (orientiert an firewallfabrik):
[tool.ruff]
target-version = "py312"
[tool.ruff.lint]
select = [
"B", # flake8-bugbear: potential bugs
"C4", # flake8-comprehensions
"E", # pycodestyle errors
"F", # pyflakes: logic errors
"I", # isort: import sorting
"PTH", # flake8-use-pathlib: os.path -> pathlib
"RUF", # Ruff-specific rules
"SIM", # flake8-simplify
"TID", # flake8-tidy-imports
"UP", # pyupgrade: modernize syntax
"W", # pycodestyle warnings
]
ignore = [
"E501", # line too long - prose comments and URLs
]
[tool.ruff.format]
quote-style = "single"
Bemerkung
Single Quotes als Standard-Quote-Style. Double Quotes nur innerhalb eines
f-String-Ausdrucks oder wenn der String selbst Single Quotes enthält. """ für alle
Triple-Quoted-Strings (Docstrings, SQL, DESCRIPTION). Die Regel wird durch ruff
format erzwungen.
Type-Checking mit pyright¶
Bei der Linuxfabrik ist pyright der bevorzugte statische Type-Checker, nicht mypy.
Pyright läuft unabhängig von ruff.
pipx install pyright
pyright src/
[tool.pyright]
exclude = ["src/mypackage/gui", "venv"]
Security mit bandit¶
bandit findet typische Sicherheitsprobleme wie hardcodierte Passwörter, unsichere
subprocess-Calls oder unsicheres Deserialize.
pipx install bandit
bandit --configfile pyproject.toml --recursive src/
[tool.bandit]
# Global skips only with a justification as a comment right above the keys.
# B110 (try/except/pass) and B112 (try/except/continue): intentional patterns
# for graceful degradation in GUI/CLI tools.
# B311 (pseudo-random): not used for cryptographic purposes.
skips = ["B110", "B112", "B311"]
[tool.bandit.assert_used]
# pytest-style asserts are idiomatic in tests/.
skips = ["*/tests/*.py", "tests/*.py"]
Einzelne Befunde lassen sich per # nosec BXXX hinter der betreffenden Codezeile
unterdrücken. Die Begründung immer in eine eigene Kommentarzeile direkt darüber, nie in
dieselbe Zeile - bandit parst alles nach # nosec als weitere Test-IDs und verschluckt
Freitext stillschweigend:
# Short justification on its own line above the call site.
offending_call(...) # nosec B603
Dead Code mit vulture¶
vulture findet nicht verwendete Funktionen, Variablen und Imports.
pipx install vulture
vulture src/
[tool.vulture]
min_confidence = 80
paths = ["src/mypackage"]
# Framework hooks invoked dynamically, therefore flagged as false-positive.
ignore_names = [
"dialect",
"indentless",
]
Tests mit pytest¶
pytest ist das De-facto-Test-Framework. Testdateien liegen nach Konvention unter
tests/ und heissen test_*.py.
pipx install pytest
pytest -v
pytest --cov=src # with pytest-cov for coverage
pytest tests/test_foo.py::test_bar # run a single test
[tool.pytest.ini_options]
testpaths = ["tests"]
pylint als Alternative¶
ruff deckt inzwischen den grössten Teil dessen ab, was pylint historisch geprüft hat,
ist aber um Grössenordnungen schneller. Für tiefergehende Struktur-Checks (Zyklen,
Klassenhierarchie, Code-Smells) bleibt pylint weiterhin nützlich.
pipx install pylint
pylint src/
pre-commit¶
Zentrale Konfiguration in .pre-commit-config.yaml im Repo-Root:
pipx install pre-commit
pre-commit install # register the Git hook
pre-commit run --all-files # check the whole codebase
Ein Linuxfabrik-typisches Setup mit Ruff, Bandit und Vulture:
repos:
- repo: 'https://github.com/astral-sh/ruff-pre-commit'
rev: 'v0.15.10'
hooks:
- id: 'ruff-check'
args: ['--fix']
- id: 'ruff-format'
- repo: 'https://github.com/PyCQA/bandit'
rev: '1.9.4'
hooks:
- id: 'bandit'
args: ['-c', 'pyproject.toml']
additional_dependencies: ['bandit[toml]']
types_or: ['python']
- repo: 'https://github.com/jendrikseipp/vulture'
rev: 'v2.16'
hooks:
- id: 'vulture'
args: ['--min-confidence=80']
types_or: ['python']
Python Cheat Sheet¶
Typ-Prüfung:
if isinstance(var, dict):
...
Pfade zusammensetzen (pathlib statt os.path.join):
from pathlib import Path
config = Path('/etc') / 'app' / 'config.yml'
if config.exists():
data = config.read_text(encoding='utf-8')
Optionalen Import kapseln:
try:
import psutil
HAVE_PSUTIL = True
except ImportError:
HAVE_PSUTIL = False
Liste als einzelne Argumente übergeben:
fruits = ['lemon', 'pear', 'watermelon']
myfunc(*fruits)
Dictionary als Keyword-Arguments übergeben:
connection = {
'database': 'mydb',
'host': 'db.example.com',
'password': 'linuxfabrik',
'user': 'admin',
}
cnx = mysql.connector.connect(**connection)
Schleifen über Listen und Dicts:
# list
for value in data:
...
for index, value in enumerate(data):
...
# dict
for key, value in data.items():
...
Exceptions gezielt fangen:
try:
...
except (AttributeError, KeyError) as err:
...
Ausführungszeit messen:
import time
start = time.time()
# ... do the work ...
print(f'duration: {time.time() - start:.3f} s')
Shell-Pipe cmd1 | cmd2 | cmd3 nachbauen:
import subprocess
p1 = subprocess.Popen(['dmesg'], stdout=subprocess.PIPE)
p2 = subprocess.Popen(['grep', 'error'], stdin=p1.stdout, stdout=subprocess.PIPE)
p1.stdout.close()
output = p2.communicate()[0]
Prozess-Informationen:
import platform
platform.python_version() # '3.12.1'
platform.python_implementation() # 'CPython'
platform.system() # 'Linux'
platform.machine() # 'x86_64'
Schneller HTTP-Server¶
Zum Ausliefern einer einzelnen Datei oder eines Verzeichnisses per HTTP (z.B. die
/root/anaconda-ks.cfg):
# serve the current directory on port 8080
python3 -m http.server --bind 0.0.0.0 8080
# localhost only
python3 -m http.server --bind 127.0.0.1 8080
index.html-Dateien werden automatisch als DirectoryIndex verwendet.
Packaging und Veröffentlichung auf PyPI¶
Beispiel anhand der Linuxfabrik libs.
Moderne Python-Pakete nutzen:
ein src-Layout (
src/mypackage/) - schützt davor, versehentlich den Projekt-Root statt die installierte Version zu importieren.pyproject.tomlals einzige Konfigurationsdatei (PEP 621, PEP 735), stattsetup.pyundsetup.cfg.~=als Versions-Operator für Dependencies („compatible release“, erlaubt Patch-Updates, blockt Major-Wechsel) - z.B.jinja2~=3.1erlaubt 3.1.x, aber nicht 4.x.
Minimalvorlage:
[build-system]
build-backend = "setuptools.build_meta"
requires = ["setuptools >= 77.0.3"]
[project]
authors = [
{ name = "Linuxfabrik GmbH, Zurich, Switzerland", email = "info@linuxfabrik.ch" },
]
dependencies = [
"jinja2~=3.1",
"pyyaml~=6.0.3",
]
description = "Kurze Beschreibung des Pakets."
dynamic = ["version"]
license-files = ["LICENSE"]
name = "mypackage"
readme = "README.md"
requires-python = ">=3.12"
[project.optional-dependencies]
# install via: pip install mypackage[gui]
gui = ["PySide6~=6.8"]
[dependency-groups]
# PEP 735: dev dependencies, not part of the built wheel.
dev = ["pytest~=9.0.2"]
[project.scripts]
mycli = "mypackage.cli:main"
[project.urls]
Homepage = "https://github.com/linuxfabrik/mypackage/"
Tracker = "https://github.com/linuxfabrik/mypackage/issues/"
[tool.setuptools.dynamic]
version = { attr = "mypackage.__version__" }
[tool.setuptools.packages.find]
where = ["src"]
venv vorbereiten und Build-Werkzeuge installieren:
python3 -m venv ~/venvs/mypackage
source ~/venvs/mypackage/bin/activate
python3 -m pip install --upgrade pip setuptools wheel
python3 -m pip install --upgrade build twine
Package bauen und Distribution-Metadaten prüfen:
cd /path/to/mypackage
python3 -m build
# check that the package description will render correctly on PyPI
twine check dist/*
Twine gegen die PyPI-Test-Instanz konfigurieren (API-Token zuvor unter https://test.pypi.org/manage/account/ erstellen):
[testpypi]
password = pypi-204513be14d74574a3bef240699e7117
username = __token__
Hochladen auf die Test-Instanz:
twine upload --repository testpypi dist/*
Installation des hochgeladenen Pakets aus Test-PyPI testen. Die regulären Abhängigkeiten kommen weiterhin aus dem produktiven PyPI:
python3 -m pip install --index-url https://test.pypi.org/simple/ --upgrade mypackage
Wenn alles passt, API-Token unter https://pypi.org/manage/account/ erstellen und für das produktive Repository konfigurieren:
[pypi]
password = pypi-<...>
username = __token__
Release hochladen:
twine upload dist/*
Anschliessend unter https://pypi.org/project/<name>/ prüfen.
Executables erzeugen¶
pyinstaller¶
pyinstaller packt Python-Skripte samt Interpreter und Abhängigkeiten in eine einzelne
ausführbare Datei. Damit die gebauten Executables möglichst überall laufen, sollte der Build
auf einer alten, aber noch gepflegten Distribution passieren (Faustregel: die älteste
Ziel-Distribution). Bauen auf RHEL 10 und ausliefern auf RHEL 8 scheitert an zu neuen
Systembibliotheken (z.B. dlopen: /lib64/libm.so.6: version 'GLIBC_2.29' not found).
dnf --assumeyes install binutils glibc
dnf --assumeyes install python3.12 python3.12-devel
python3.12 -m venv ~/venvs/pyinstaller
source ~/venvs/pyinstaller/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install pyinstaller
# add project-specific libraries:
python3 -m pip install BeautifulSoup4 lxml psutil PyMySQL smbprotocol
pyinstaller \
--clean \
--distpath /tmp/dist \
--noconfirm \
--noupx \
--onedir \
--specpath /tmp/spec \
--workpath /tmp/build \
/path/to/my/script.py
Nuitka¶
Nuitka kompiliert Python nach C und erzeugt daraus eigenständige
Binaries oder importierbare Module. Gegenüber pyinstaller entsteht echter Maschinencode,
der Build dauert aber länger.
python3 -m pip install nuitka
python3 -m nuitka --standalone --onefile script.py
Die vollständige Optionsliste zeigt python3 -m nuitka --help.
GUI und TUI¶
- GTK / Gtk
PyGObject hat PyGTK abgelöst. Doku: https://pygobject.readthedocs.io/en/latest/
API-Referenz: https://lazka.github.io/pgi-docs
- Tk
tkinterist das Standard-GUI-Toolkit der Python-Standardbibliothek. Nur für ganz einfache Dialoge verwenden.
- TUI
newt/snackist eine alte, auf RHEL vorhandene Library (python3-newt). Für moderne TUIs ehertextualoderrichverwenden.
Qt 6 mit PySide6¶
Die FirewallFabrik ist eine voll ausgebaute Qt-6-Anwendung in Python. Als Binding kommt
PySide6 (offizielles Qt-Binding der Qt Group, LGPL) zum Einsatz, nicht PyQt6 (GPL/
kommerziell). Beide APIs sind weitgehend identisch.
Installation:
# im venv
python3 -m pip install PySide6
Minimales Programm, das die Linuxfabrik-Konventionen abbildet (SPDX-Header, Single Quotes,
argparse mit --version, Organisationsname, Fusion-Style, sauberer Ctrl+C-Handler,
Wayland-Defaults wie in FirewallFabrik):
# Copyright (C) 2026 Linuxfabrik <info@linuxfabrik.ch>
#
# SPDX-License-Identifier: GPL-2.0-or-later
"""Minimal PySide6 example following Linuxfabrik conventions."""
import argparse
import os
import signal
import sys
try:
from PySide6.QtCore import QTimer
from PySide6.QtWidgets import (
QApplication,
QLabel,
QMainWindow,
QStyleFactory,
QVBoxLayout,
QWidget,
)
except ImportError:
print(
'Python module "PySide6" is not installed.',
file=sys.stderr,
)
sys.exit(1)
__version__ = '1.0.0'
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Hello Qt')
central = QWidget()
layout = QVBoxLayout(central)
layout.addWidget(QLabel('Hello, world.'))
self.setCentralWidget(central)
def main():
parser = argparse.ArgumentParser(
prog='hello-qt',
description='Minimal PySide6 example.',
)
parser.add_argument(
'-v',
'--version',
action='version',
version=f'hello-qt {__version__}',
)
# parse_known_args lets Qt-specific flags (e.g. -platform) pass through.
_args, remaining = parser.parse_known_args()
# prefer Wayland if available and nothing was set explicitly
if 'QT_QPA_PLATFORM' not in os.environ and os.environ.get('WAYLAND_DISPLAY'):
os.environ['QT_QPA_PLATFORM'] = 'wayland;wayland-egl'
# set the desktop file name before QApplication construction
QApplication.setDesktopFileName('ch.linuxfabrik.hello-qt')
app = QApplication(remaining)
app.setStyle(QStyleFactory.create('Fusion'))
app.setOrganizationName('Linuxfabrik')
app.setApplicationName('hello-qt')
# Qt's event loop blocks Python's signal handling. A periodic no-op
# timer lets Python handle SIGINT between Qt events (Ctrl+C in the terminal).
signal.signal(signal.SIGINT, lambda *_args: app.quit())
_sigint_timer = QTimer()
_sigint_timer.start(200)
_sigint_timer.timeout.connect(lambda: None)
window = MainWindow()
window.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()
Start:
python3 hello_qt.py
# with Qt-specific flags
python3 hello_qt.py -platform xcb
Wichtige Punkte, die sich im Projekt FirewallFabrik bewährt haben:
PySide6stattPyQt6LGPL-Lizenz, offiziell von der Qt Group, keine Dual-Licensing-Stolpersteine.
Fusion-Style erzwingenQt-Default-Styles unterscheiden sich je nach Desktop (Breeze unter KDE, Adwaita unter GNOME) und produzieren inkonsistente Layouts.
- Wayland-Präferenz explizit setzen
Sobald
WAYLAND_DISPLAYexistiert, sonst fällt Qt unter manchen Desktops auf XWayland zurück.QApplication.setDesktopFileName(...)vor derQApplication-Konstruktion aufrufenDamit der Wayland-Plugin-Init den Namen sieht und keine Doppelregistrierung versucht.
argparse.parse_known_args()verwendenDamit Qt-eigene Flags (
-platform,-style, …) an Qt durchgereicht werden.- Signal-Handler für SIGINT mit Hilfstimer registrieren
Sonst lässt sich die Anwendung aus dem Terminal nicht sauber per
Ctrl+Cbeenden.
Qt-spezifische Imports kommen in den Namenskonventionen von Qt (CamelCase: QMainWindow,
setCentralWidget). Eigener Code folgt PEP 8 (snake_case). Das ist kein Widerspruch -
ruff erkennt die Qt-API und beschwert sich nicht.
Troubleshooting¶
- bandit meldet
Test in comment: ... is not a test name or id, ignoring Der Kommentar hinter
# nosecenthält Freitext. Bandit parst alles nach# nosecals Test-IDs. Freitext in eine eigene Kommentarzeile direkt oberhalb der Call-Site verschieben, hinter# nosecnur die IDs stehen lassen (# nosec B603 B607).- error: externally-managed-environment
Tritt auf, wenn
pip installgegen das System-Python läuft. Korrekt: venv erstellen und darin installieren. Quick-and-dirty und nur für die eigene Workstation geeignet:python3 -m pip install --break-system-packages .... Das Flag umgeht den PEP-668-Schutz und ist auf Servern nicht angebracht.- ModuleNotFoundError nach venv-Wechsel
Die venv wurde mit einer anderen Python-Minor-Version erstellt, als nun aktiv ist. venv neu erstellen:
rm --recursive --force /path/to/venv; python3 -m venv /path/to/venv.- pyinstaller-Executable meldet
PackageNotFoundError: python-keystoneclient pyinstallerzusätzlich--copy-metadata python-keystoneclientmitgeben.- pyinstaller-Executable meldet
FileNotFoundError: .../os_service_types/data/service-types.json pyinstallerzusätzlich--collect-data os_service_typesmitgeben.- pip-Cache liefert
AssertionError: msgpack .dist-info directory not found Pip-Cache ist korrupt.
rm --recursive --force ~/.cache/pip/undpip installmit--no-cache-direrneut ausführen.