PEP 741 – Python Konfigurations-C-API
- Autor:
- Victor Stinner <vstinner at python.org>
- Discussions-To:
- Discourse thread
- Status:
- Final
- Typ:
- Standards Track
- Erstellt:
- 18-Jan-2024
- Python-Version:
- 3.14
- Post-History:
- 19-Jan-2024, 08-Feb-2024
- Resolution:
- Discourse-Nachricht
Inhaltsverzeichnis
- Zusammenfassung
- Begründung
- Spezifikation
- Beispiele
- Implementierung
- Abwärtskompatibilität
- Abgelehnte Ideen
- Diskussionen
- Urheberrecht
Zusammenfassung
Eine C-API hinzufügen, um die Python-Initialisierung zu konfigurieren, ohne sich auf C-Strukturen zu verlassen, und die Möglichkeit, ABI-kompatible Änderungen in Zukunft vorzunehmen.
Vervollständigen Sie die PEP 587 API durch Hinzufügen von PyInitConfig_AddModule(), die verwendet werden kann, um ein integriertes Erweiterungsmodul hinzuzufügen; Feature, das zuvor als "inittab" bezeichnet wurde.
Fügen Sie die Funktionen PyConfig_Get() und PyConfig_Set() hinzu, um die aktuelle Laufzeitkonfiguration abzurufen und festzulegen.
PEP 587 "Python Initialization Configuration" hat alle Wege zur Konfiguration der Python-Initialisierung vereinheitlicht. Diese PEP vereinheitlicht auch die Konfiguration der Python-Vorinitialisierung und der Python-Initialisierung in einer einzigen API. Darüber hinaus bietet diese PEP nur eine einzige Wahl für die Einbettung von Python anstelle von zwei "Python"- und "Isolated"-Optionen (PEP 587), um die API weiter zu vereinfachen.
Die Low-Level PEP 587 PyConfig API bleibt für Anwendungsfälle mit einer bewusst höheren Kopplung an CPython-Implementierungsdetails (wie die Emulation der vollen Funktionalität von CPython's CLI, einschließlich seiner Konfigurationsmechanismen) verfügbar.
Begründung
Die Laufzeitkonfiguration abrufen
PEP 587 bietet keine API, um die aktuelle Laufzeitkonfiguration zu ermitteln, sondern nur, um die Python-Initialisierung zu konfigurieren.
Zum Beispiel wurde die globale Konfigurationsvariable Py_UnbufferedStdioFlag in Python 3.12 als veraltet markiert und die Verwendung von PyConfig.buffered_stdio wird stattdessen empfohlen. Sie funktioniert nur zur Konfiguration von Python, es gibt keine öffentliche API, um PyConfig.buffered_stdio abzurufen.
Benutzer der begrenzten C-API fordern eine öffentliche API zum Abrufen der aktuellen Laufzeitkonfiguration an.
Cython muss die Konfigurationsoption optimization_level abrufen: issue.
Als globale Konfigurationsvariablen im Jahr 2022 als veraltet markiert wurden, forderte Marc-André Lemburg eine C-API an, um auf diese Konfigurationsvariablen zur Laufzeit (nicht nur während der Python-Initialisierung) zugreifen zu können.
Sicherheitskorrektur
Um CVE-2020-10735, eine Denial-of-Service-Attacke bei der Konvertierung einer sehr großen Zeichenkette in eine Ganzzahl (in Basis 10), zu beheben, wurde diskutiert, einen neuen PyConfig Member zu stabilen Zweigen hinzuzufügen, was die ABI beeinträchtigt.
Gregory P. Smith schlug eine andere API vor, die eine textbasierte Konfigurationsdatei verwendet, um nicht durch PyConfig Member eingeschränkt zu sein: FR: Allow private runtime config to enable extending without breaking the PyConfig ABI (August 2022).
Schließlich wurde entschieden, keinen neuen PyConfig Member zu stabilen Zweigen hinzuzufügen, sondern nur einen neuen PyConfig.int_max_str_digits Member zum Entwicklungszweig (der zu Python 3.12 wurde). Eine dedizierte private globale Variable (unabhängig von PyConfig) wird in stabilen Zweigen verwendet.
Redundanz zwischen PyPreConfig und PyConfig
Die Python-Vorinitialisierung verwendet die PyPreConfig Struktur und die Python-Initialisierung verwendet die PyConfig Struktur. Beide Strukturen haben vier doppelte Member: dev_mode, parse_argv, isolated und use_environment.
Die Redundanz entsteht dadurch, dass die beiden Strukturen getrennt sind, während einige PyConfig Member für die Vorinitialisierung benötigt werden.
Python einbetten
Anwendungen, die Python einbetten
Beispiele
- Blender 3D-Grafik.
- fontforge Schriftarteneditor.
- Gimp.
- LibreOffice.
- OBS Studio.
- Tiled.
- vim Texteditor.
Unter Linux, FreeBSD und macOS sind Anwendungen normalerweise entweder statisch mit einer libpython verknüpft oder laden dynamisch eine libpython. Die libpython Shared Library ist versioniert, Beispiel: libpython3.12.so für Python 3.12 unter Linux.
Das Vim-Projekt kann die stabile ABI ansprechen. Normalerweise wird die Version des "System-Pythons" verwendet. Es ist derzeit nicht möglich, zu wählen, welche Python-Version verwendet werden soll. Benutzer wünschen sich die Möglichkeit, bei Bedarf eine neuere Python-Version auszuwählen.
Unter Linux ist ein weiterer Ansatz zur Bereitstellung einer Python-einbettenden Anwendung wie GIMP, Python in einem Flatpack-, AppImage- oder Snap-"Container" zu integrieren. In diesem Fall bringt die Anwendung ihre eigene Kopie der Python-Version mit dem Container.
Bibliotheken, die Python einbetten
Beispiele
- Apache mod_wsgi (source).
- nimpy: Nim - Python-Bridge.
- PyO3: Rust-Bindings für den Python-Interpreter.
Dienstprogramme zur Erstellung eigenständiger Anwendungen
- py2app für macOS.
- py2exe für Windows.
- pyinstaller.
- PyOxidizer: Es verwendet die PEP 587 PyConfig API.
Diese Dienstprogramme erstellen eigenständige Anwendungen, sie sind nicht mit libpython verknüpft.
Die Laufzeitkonfiguration festlegen
Marc-André Lemburg forderte eine C-API an, um den Wert einiger Konfigurationsoptionen zur Laufzeit zu setzen.
optimization_levelverboseparser_debuginspectwrite_bytecode
Zuvor war es möglich, globale Konfigurationsvariablen direkt zu setzen
Py_OptimizeFlagPy_VerboseFlagPy_DebugFlagPy_InspectFlagPy_DontWriteBytecodeFlag
Diese Konfigurationsflags wurden jedoch in Python 3.12 als veraltet markiert und sind für die Entfernung in Python 3.14 vorgesehen.
Spezifikation
Hinzufügen von C-API-Funktionen und -Strukturen zur Konfiguration der Python-Initialisierung
- Config erstellen
- Undurchsichtige Struktur
PyInitConfig. PyInitConfig_Create().PyInitConfig_Free(config).
- Undurchsichtige Struktur
- Optionen abrufen
.PyInitConfig_HasOption(config, name).PyInitConfig_GetInt(config, name, &value).PyInitConfig_GetStr(config, name, &value).PyInitConfig_GetStrList(config, name, &length, &items)PyInitConfig_FreeStrList().
- Optionen festlegen
.PyInitConfig_SetInt(config, name, value).PyInitConfig_SetStr(config, name, value).PyInitConfig_SetStrList(config, name, length, items)PyInitConfig_AddModule(config, name, initfunc)
- Initialisieren
Py_InitializeFromInitConfig(config).
- Fehlerbehandlung
.PyInitConfig_GetError(config, &err_msg).PyInitConfig_GetExitcode(config, &exitcode)
Hinzufügen von C-API-Funktionen zum Abrufen und Festlegen der aktuellen Laufzeitkonfiguration
PyConfig_Get(name)..PyConfig_GetInt(name, &value)PyConfig_Set(name).PyConfig_Names().
Die C-API verwendet null-terminierte UTF-8-kodierte Strings, um auf einen Konfigurationsoptionsnamen zu verweisen.
Diese C-API-Funktionen sind von der begrenzten C-API ausgeschlossen.
PyInitConfig Struktur
Die Struktur PyInitConfig wird durch die Kombination der drei Strukturen der PyConfig API implementiert und enthält auch ein inittab-Mitglied
PyPreConfig preconfigPyConfig configPyStatus statusstruct _inittab *inittabfürPyInitConfig_AddModule()
Der PyStatus Status ist nicht mehr getrennt, sondern Teil der vereinheitlichten PyInitConfig Struktur, was die API einfacher zu bedienen macht.
Konfigurationsoptionen
Konfigurationsoptionen werden nach den Membern der PyPreConfig und PyConfig Strukturen benannt. Siehe die PyPreConfig Dokumentation und die PyConfig Dokumentation.
Das Veralten und Entfernen von Konfigurationsoptionen liegt außerhalb des Umfangs der PEP und sollte von Fall zu Fall diskutiert werden.
Öffentliche Konfigurationsoptionen
Folgende Optionen können mit PyConfig_Get() abgerufen und mit PyConfig_Set() gesetzt werden.
| Option | Typ | Kommentar |
|---|---|---|
argv |
list[str] |
API: sys.argv. |
base_exec_prefix |
str |
API: sys.base_exec_prefix. |
base_executable |
str |
API: sys._base_executable. |
base_prefix |
str |
API: sys.base_prefix. |
bytes_warning |
int |
API: sys.flags.bytes_warning. |
exec_prefix |
str |
API: sys.exec_prefix. |
executable |
str |
API: sys.executable. |
inspect |
bool |
API: sys.flags.inspect (int). |
int_max_str_digits |
int |
API: sys.flags.int_max_str_digits, sys.get_int_max_str_digits() und sys.set_int_max_str_digits(). |
interactive |
bool |
API: sys.flags.interactive. |
module_search_paths |
list[str] |
API: sys.path. |
optimization_level |
int |
API: sys.flags.optimize. |
parser_debug |
bool |
API: sys.flags.debug (int). |
platlibdir |
str |
API: sys.platlibdir. |
prefix |
str |
API: sys.base_prefix. |
pycache_prefix |
str |
API: sys.pycache_prefix. |
quiet |
bool |
API: sys.flags.quiet (int). |
stdlib_dir |
str |
API: sys._stdlib_dir. |
use_environment |
bool |
API: sys.flags.ignore_environment (int). |
verbose |
int |
API: sys.flags.verbose. |
warnoptions |
list[str] |
API: sys.warnoptions. |
write_bytecode |
bool |
API: sys.flags.dont_write_bytecode (int) und sys.dont_write_bytecode (bool). |
xoptions |
dict[str, str] |
API: sys._xoptions. |
Einige Optionsnamen unterscheiden sich von sys Attributen, wie die Option optimization_level und das Attribut sys.flags.optimize. PyConfig_Set() setzt das entsprechende sys Attribut.
Die xoptions sind eine Liste von Strings in PyInitConfig, wobei jeder String das Format key (value ist implizit True) oder key=value hat. In der aktuellen Laufzeitkonfiguration wird daraus ein Dictionary (key: str → value: str | True).
Schreibgeschützte Konfigurationsoptionen
Folgende Optionen können mit PyConfig_Get() abgerufen, aber nicht mit PyConfig_Set() gesetzt werden.
| Option | Typ | Kommentar |
|---|---|---|
allocator |
int |
|
buffered_stdio |
bool |
|
check_hash_pycs_mode |
str |
|
code_debug_ranges |
bool |
|
coerce_c_locale |
bool |
|
coerce_c_locale_warn |
bool |
|
configure_c_stdio |
bool |
|
configure_locale |
bool |
|
cpu_count |
int |
API: os.cpu_count() (int | None). |
dev_mode |
bool |
API: sys.flags.dev_mode. |
dump_refs |
bool |
|
dump_refs_file |
str |
|
faulthandler |
bool |
API: faulthandler.is_enabled(). |
filesystem_encoding |
str |
API: sys.getfilesystemencoding(). |
filesystem_errors |
str |
API: sys.getfilesystemencodeerrors(). |
hash_seed |
int |
|
home |
str |
|
import_time |
bool |
|
install_signal_handlers |
bool |
|
isolated |
bool |
API: sys.flags.isolated (int). |
legacy_windows_fs_encoding |
bool |
Nur Windows. |
legacy_windows_stdio |
bool |
Nur Windows. |
malloc_stats |
bool |
|
orig_argv |
list[str] |
API: sys.orig_argv. |
parse_argv |
bool |
|
pathconfig_warnings |
bool |
|
perf_profiling |
bool |
API: sys.is_stack_trampoline_active(). |
program_name |
str |
|
run_command |
str |
|
run_filename |
str |
|
run_module |
str |
|
run_presite |
str |
benötigt einen Debug-Build. |
safe_path |
bool |
|
show_ref_count |
bool |
|
site_import |
bool |
API: sys.flags.no_site (int). |
skip_source_first_line |
bool |
|
stdio_encoding |
str |
API: sys.stdin.encoding, sys.stdout.encoding und sys.stderr.encoding. |
stdio_errors |
str |
API: sys.stdin.errors, sys.stdout.errors und sys.stderr.errors. |
tracemalloc |
int |
API: tracemalloc.is_tracing() (bool). |
use_frozen_modules |
bool |
|
use_hash_seed |
bool |
|
user_site_directory |
bool |
API: sys.flags.no_user_site (int). |
utf8_mode |
bool |
|
warn_default_encoding |
bool |
|
_pystats |
bool |
API: sys._stats_on(), sys._stats_off(). Benötigt einen Py_STATS Build. |
Config erstellen
- Struktur
PyInitConfig - Undurchsichtige Struktur zur Konfiguration der Python-Vorinitialisierung und der Python-Initialisierung.
PyInitConfig* PyInitConfig_Create(void):- Erzeugt eine neue Initialisierungskonfiguration mit Standardwerten der isolierten Konfiguration.
Sie muss mit
PyInitConfig_Free()freigegeben werden.Gibt bei Speicherzuordnungsfehlern
NULLzurück. void PyInitConfig_Free(PyInitConfig *config):- Gibt den Speicher einer Initialisierungskonfiguration frei.
Optionen abrufen
Der Parameter name der Konfigurationsoption muss ein nicht-NULL null-terminierter UTF-8-kodierter String sein.
int PyInitConfig_HasOption(PyInitConfig *config, const char *name):- Prüft, ob die Konfiguration eine Option namens name hat.
Gibt
1zurück, wenn die Option existiert, andernfalls0. int PyInitConfig_GetInt(PyInitConfig *config, const char *name, int64_t *value):- Ruft eine Ganzzahl-Konfigurationsoption ab.
- Setzt *value und gibt bei Erfolg
0zurück. - Setzt einen Fehler in config und gibt bei einem Fehler
-1zurück.
- Setzt *value und gibt bei Erfolg
int PyInitConfig_GetStr(PyInitConfig *config, const char *name, char **value):- Ruft eine String-Konfigurationsoption als null-terminierten UTF-8-kodierten String ab.
- Setzt *value und gibt bei Erfolg
0zurück. - Setzt einen Fehler in config und gibt bei einem Fehler
-1zurück.
Bei Erfolg muss der String mit
free(value)freigegeben werden. - Setzt *value und gibt bei Erfolg
int PyInitConfig_GetStrList(PyInitConfig *config, const char *name, size_t *length, char ***items):- Ruft eine String-Listen-Konfigurationsoption als Array von null-terminierten UTF-8-kodierten Strings ab.
- Setzt *length und *value und gibt bei Erfolg
0zurück. - Setzt einen Fehler in config und gibt bei einem Fehler
-1zurück.
Bei Erfolg muss die String-Liste mit
PyInitConfig_FreeStrList(length, items)freigegeben werden. - Setzt *length und *value und gibt bei Erfolg
void PyInitConfig_FreeStrList(size_t length, char **items):- Gibt den Speicher einer String-Liste frei, die von
PyInitConfig_GetStrList()erstellt wurde.
Optionen festlegen
Der Parameter name der Konfigurationsoption muss ein nicht-NULL null-terminierter UTF-8-kodierter String sein.
Einige Konfigurationsoptionen haben Nebenwirkungen auf andere Optionen. Diese Logik wird nur implementiert, wenn Py_InitializeFromInitConfig() aufgerufen wird, nicht durch die unten stehenden "Set"-Funktionen. Zum Beispiel setzt das Setzen von dev_mode auf 1 nicht faulthandler auf 1.
int PyInitConfig_SetInt(PyInitConfig *config, const char *name, int64_t value):- Setzt eine Ganzzahl-Konfigurationsoption.
- Gibt bei Erfolg
0zurück. - Setzt einen Fehler in config und gibt bei einem Fehler
-1zurück.
- Gibt bei Erfolg
int PyInitConfig_SetStr(PyInitConfig *config, const char *name, const char *value):- Setzt eine String-Konfigurationsoption aus einem null-terminierten UTF-8-kodierten String. Der String wird kopiert.
- Gibt bei Erfolg
0zurück. - Setzt einen Fehler in config und gibt bei einem Fehler
-1zurück.
- Gibt bei Erfolg
int PyInitConfig_SetStrList(PyInitConfig *config, const char *name, size_t length, char * const *items):- Setzt eine String-Listen-Konfigurationsoption aus einem Array von null-terminierten UTF-8-kodierten Strings. Die String-Liste wird kopiert.
- Gibt bei Erfolg
0zurück. - Setzt einen Fehler in config und gibt bei einem Fehler
-1zurück.
- Gibt bei Erfolg
int PyInitConfig_AddModule(PyInitConfig *config, const char *name, PyObject* (*initfunc)(void)):- Fügt ein integriertes Erweiterungsmodul zur Tabelle der integrierten Module hinzu.
Das neue Modul kann unter dem Namen name importiert werden und verwendet die Funktion initfunc als Initialisierungsfunktion, die beim ersten Importversuch aufgerufen wird.
- Gibt bei Erfolg
0zurück. - Setzt einen Fehler in config und gibt bei einem Fehler
-1zurück.
Wenn Python mehrmals initialisiert wird, muss
PyInitConfig_AddModule()bei jeder Python-Initialisierung aufgerufen werden.Ähnlich wie die Funktion
PyImport_AppendInittab(). - Gibt bei Erfolg
Python initialisieren
int Py_InitializeFromInitConfig(PyInitConfig *config):- Initialisiert Python aus der Initialisierungskonfiguration.
- Gibt bei Erfolg
0zurück. - Setzt einen Fehler in config und gibt bei einem Fehler
-1zurück. - Setzt einen Exit-Code in config und gibt
-1zurück, wenn Python beendet werden soll.
Siehe
PyInitConfig_GetExitcode()für den Exitcode-Fall. - Gibt bei Erfolg
Fehlerbehandlung
int PyInitConfig_GetError(PyInitConfig* config, const char **err_msg):- Ruft die Fehlermeldung von config ab.
- Setzt *err_msg und gibt bei gesetzt sein
1zurück. - Setzt *err_msg auf
NULLund gibt andernfalls0zurück.
Eine Fehlermeldung ist ein UTF-8-kodierter String.
Wenn config einen Exit-Code hat, wird der Exit-Code als Fehlermeldung formatiert.
Die Fehlermeldung bleibt gültig, bis eine andere
PyInitConfigFunktion mit config aufgerufen wird. Der Aufrufer muss die Fehlermeldung nicht freigeben. - Setzt *err_msg und gibt bei gesetzt sein
int PyInitConfig_GetExitcode(PyInitConfig* config, int *exitcode):- Ruft den Exit-Code von config ab.
- Setzt *exitcode und gibt
1zurück, wenn Python beendet werden soll. - Gibt
0zurück, wenn config keinen Exit-Code gesetzt hat.
Nur die Funktion
Py_InitializeFromInitConfig()kann einen Exit-Code setzen, wenn die Optionparse_argvungleich Null ist.Ein Exit-Code kann gesetzt werden, wenn die Kommandozeilenanalyse fehlgeschlagen ist (Exit-Code 2) oder wenn eine Kommandozeilenoption die Anzeige der Kommandozeilenhilfe verlangt (Exit-Code 0).
- Setzt *exitcode und gibt
Die Laufzeitkonfiguration abrufen und festlegen
Der Parameter name der Konfigurationsoption muss ein nicht-NULL null-terminierter UTF-8-kodierter String sein.
PyObject* PyConfig_Get(const char *name):- Ruft den aktuellen Laufzeitwert einer Konfigurationsoption als Python-Objekt ab.
- Gibt bei Erfolg eine neue Referenz zurück.
- Setzt eine Ausnahme und gibt bei Fehler
NULLzurück.
Der Objekttyp hängt von der Option ab: siehe die Tabellen unter Konfigurationsoptionen.
Andere Optionen werden aus internen
PyPreConfigundPyConfigStrukturen abgerufen.Der Aufrufer muss den GIL halten. Die Funktion kann nicht vor der Python-Initialisierung oder nach der Python-Finalisierung aufgerufen werden.
int PyConfig_GetInt(const char *name, int *value):- Ähnlich wie
PyConfig_Get(), ruft aber den Wert als Ganzzahl ab.- Setzt
*valueund gibt bei Erfolg0zurück. - Setzt eine Ausnahme und gibt bei Fehler
-1zurück.
- Setzt
PyObject* PyConfig_Names(void):- Ruft alle Namen von Konfigurationsoptionen als
frozensetab.Setzt eine Ausnahme und gibt bei Fehler
NULLzurück.Der Aufrufer muss den GIL halten.
PyObject* PyConfig_Set(const char *name, PyObject *value):- Setzt den aktuellen Laufzeitwert einer Konfigurationsoption.
- Löst einen
ValueErroraus, wenn keine Option name vorhanden ist. - Löst einen
ValueErroraus, wenn value ein ungültiger Wert ist. - Löst einen
ValueErroraus, wenn die Option schreibgeschützt ist und nicht gesetzt werden kann. - Löst einen
TypeErroraus, wenn value nicht den korrekten Typ hat.
Schreibgeschützte Konfigurationsoptionen können nicht gesetzt werden.
Der Aufrufer muss den GIL halten. Die Funktion kann nicht vor der Python-Initialisierung oder nach der Python-Finalisierung aufgerufen werden.
- Löst einen
Stabilität
Das Verhalten von Optionen, die Standardwerte von Optionen und das Verhalten von Python können sich mit jeder Python-Version ändern: sie sind nicht „stabil“.
Darüber hinaus können Konfigurationsoptionen gemäß dem üblichen PEP 387 Deprecation-Prozess hinzugefügt, veraltet und entfernt werden.
Interaktion mit den PyPreConfig und PyConfig APIs
Die Low-Level-APIs PEP 587 PyPreConfig und PyConfig bleiben verfügbar und voll unterstützt. Wie in der Zusammenfassung erwähnt, bleiben sie der bevorzugte Ansatz für Einbettungsfälle, die darauf abzielen, das Verhalten der vollständigen CPython CLI genau nachzuahmen, anstatt lediglich eine Python-Laufzeit als Teil einer größeren Anwendung bereitzustellen.
Die PyPreConfig APIs können in Kombination mit der Initialisierungs-API in dieser PEP verwendet werden. In solchen Fällen gelten die Lese-/Schreibbeschränkungen für vorkonfigurierte Einstellungen für PyInitConfig_SetInt zusätzlich zu PyConfig_Set, nachdem der Interpreter vorkonfiguriert wurde (insbesondere darf nur use_environment aktualisiert werden; ein Versuch, andere vorkonfigurierte Variablen zu aktualisieren, führt zu einer Fehlermeldung).
Beispiele
Python initialisieren
Beispiel für die Initialisierung von Python, Setzen von Konfigurationsoptionen verschiedener Typen, Rückgabe von -1 bei Fehler
int init_python(void)
{
PyInitConfig *config = PyInitConfig_Create();
if (config == NULL) {
printf("PYTHON INIT ERROR: memory allocation failed\n");
return -1;
}
// Set an integer (dev mode)
if (PyInitConfig_SetInt(config, "dev_mode", 1) < 0) {
goto error;
}
// Set a list of UTF-8 strings (argv)
char *argv[] = {"my_program", "-c", "pass"};
if (PyInitConfig_SetStrList(config, "argv",
Py_ARRAY_LENGTH(argv), argv) < 0) {
goto error;
}
// Set a UTF-8 string (program name)
if (PyInitConfig_SetStr(config, "program_name", L"my_program") < 0) {
goto error;
}
// Initialize Python with the configuration
if (Py_InitializeFromInitConfig(config) < 0) {
goto error;
}
PyInitConfig_Free(config);
return 0;
error:
// Display the error message
const char *err_msg;
(void)PyInitConfig_GetError(config, &err_msg);
printf("PYTHON INIT ERROR: %s\n", err_msg);
PyInitConfig_Free(config);
return -1;
}
bytes_warning Option bei der Initialisierung erhöhen
Beispiel für die Erhöhung der Option bytes_warning einer Initialisierungskonfiguration
int config_bytes_warning(PyInitConfig *config)
{
int64_t bytes_warning;
if (PyInitConfig_GetInt(config, "bytes_warning", &bytes_warning)) {
return -1;
}
bytes_warning += 1;
if (PyInitConfig_SetInt(config, "bytes_warning", bytes_warning)) {
return -1;
}
return 0;
}
Die Laufzeit-Verbose-Option abrufen
Beispiel für das Abrufen des aktuellen Laufzeitwerts der Konfigurationsoption verbose
int get_verbose(void)
{
int verbose;
if (PyConfig_GetInt("verbose", &verbose) < 0) {
// Silently ignore the error
PyErr_Clear();
return -1;
}
return verbose;
}
Im Fehlerfall ignoriert die Funktion den Fehler stillschweigend und gibt -1 zurück. In der Praxis kann das Abrufen der Option verbose nicht fehlschlagen, es sei denn, eine zukünftige Python-Version entfernt die Option.
Implementierung
Abwärtskompatibilität
Änderungen sind vollständig abwärtskompatibel. Es werden nur neue APIs hinzugefügt.
Bestehende APIs wie die PyConfig C API (PEP 587) bleiben unverändert.
Abgelehnte Ideen
Konfiguration als Text
Es wurde vorgeschlagen, die Konfiguration als Text bereitzustellen, um die API mit der stabilen ABI kompatibel zu machen und benutzerdefinierte Optionen zu ermöglichen.
Beispiel
# integer
bytes_warning = 2
# string
filesystem_encoding = "utf8" # comment
# list of strings
argv = ['python', '-c', 'code']
Die API würde die Konfiguration als String und nicht als Datei entgegennehmen. Beispiel mit einer hypothetischen Funktion PyInit_SetConfig()
void stable_abi_init_demo(int set_path)
{
PyInit_SetConfig(
"isolated = 1\n"
"argv = ['python', '-c', 'code']\n"
"filesystem_encoding = 'utf-8'\n"
);
if (set_path) {
PyInit_SetConfig("pythonpath = '/my/path'");
}
}
Das Beispiel ignoriert die Fehlerbehandlung, um die Lesbarkeit zu verbessern.
Das Problem ist, dass die Generierung eines solchen Konfigurationstexts das Hinzufügen von Anführungszeichen zu Strings und das Escapen von Anführungszeichen in Strings erfordert. Das Formatieren eines Arrays von Strings wird dadurch nicht trivial.
Bereitstellung einer API zum Formatieren eines Strings oder eines Arrays von Strings lohnt sich nicht wirklich, während Python direkt eine API bereitstellen kann, um eine Konfigurationsoption zu setzen, bei der der Wert direkt als String oder Array von Strings übergeben wird. Dies vermeidet die Sonderbedeutung bestimmter Zeichen, wie z. B. Zeilenumbruchzeichen, die escaped werden müssten.
Auf eine Option mit einer Ganzzahl verweisen
Die Verwendung von Strings zur Referenzierung einer Konfigurationsoption erfordert den Vergleich von Strings, was langsamer sein kann als der Vergleich von Ganzzahlen.
Verwenden Sie Ganzzahlen, ähnlich wie bei Typen wie „Slots“, z. B. Py_tp_doc, um auf eine Konfigurationsoption zu verweisen. Der Parameter const char *name wird durch int option ersetzt.
Das Akzeptieren benutzerdefinierter Optionen führt eher zu Konflikten bei der Verwendung von Ganzzahlen, da es schwieriger ist, „Namensräume“ (Bereiche) für Ganzzahloptionen aufrechtzuerhalten. Bei der Verwendung von Strings kann ein einfaches Präfix mit einem Doppelpunkt als Trennzeichen verwendet werden.
Ganzzahlen erfordern auch die Pflege einer Liste von Ganzzahlkonstanten und machen somit die C-API und die Python-API größer.
Python 3.13 hat nur etwa 62 Konfigurationsoptionen, daher ist die Leistung kein wirkliches Hindernis. Wenn später eine bessere Leistung benötigt wird, kann eine Hashtabelle verwendet werden, um eine Option anhand ihres Namens zu erhalten.
Wenn das Abrufen einer Konfigurationsoption in Hot Code verwendet wird, kann der Wert einmal gelesen und zwischengespeichert werden. Übrigens können die meisten Konfigurationsoptionen zur Laufzeit nicht geändert werden.
Mehrphasige Initialisierung (ähnlich PEP 432)
Eric Snow äußerte Bedenken, dass dieser Vorschlag bei Einbettungen die Vorstellung verstärken könnte, dass die Initialisierung ein einziger monolithischer Schritt ist. Er argumentierte, dass die Initialisierung 5 verschiedene Phasen umfasst und schlug sogar vor, dass die API dies explizit widerspiegeln sollte. Eric schlug vor, dass zumindest die Implementierung der Initialisierung die Phasen widerspiegeln sollte, teilweise zur Verbesserung der Code-Gesundheit. Insgesamt ähneln seine Erklärungen PEP 432 und PEP 587.
Ein weiterer wichtiger Punkt von Eric, der für diese PEP relevant war, war, dass idealerweise die an Py_InitializeFromConfig() übergebene Konfiguration vollständig sein sollte, bevor diese Funktion aufgerufen wird, während die Initialisierung derzeit die Konfiguration modifiziert.
Obwohl Eric nicht unbedingt eine Alternative zu PEP 741 vorschlug, ist jeder Vorschlag, eine granulare Initialisierungs-API rund um Phasen hinzuzufügen, im Wesentlichen das Gegenteil von dem, was diese PEP zu erreichen versucht. Eine solche API ist komplizierter, sie erfordert das Hinzufügen neuer öffentlicher Strukturen und neuer öffentlicher Funktionen. Sie macht die Python-Initialisierung komplizierter, anstatt dass diese PEP versucht, bestehende APIs zu vereinheitlichen und zu vereinfachen (das Gegenteil). Das Vorhandensein mehrerer Strukturen für ähnliche Zwecke kann zu doppelten Mitgliedern führen, ein ähnliches Problem wie doppelte Mitglieder zwischen den bestehenden PyPreConfig und PyConfig Strukturen.
Locale-Kodierung und breite Zeichenketten
Das Akzeptieren von Strings, die in die Locale-Codierung kodiert sind, und das Akzeptieren von Wide-Strings (wchar_t*) in der PyInitConfig API wurde aufgeschoben, um die PyInitConfig API einfach zu halten und die Komplexität der Python-Vorinitialisierung zu vermeiden. Diese Funktionen werden auch meist benötigt, wenn das Verhalten der vollständigen CPython CLI emuliert wird, und werden daher besser von der Low-Level-API PEP 587 abgedeckt.
Diskussionen
Urheberrecht
Dieses Dokument wird in die Public Domain oder unter die CC0-1.0-Universal-Lizenz gestellt, je nachdem, welche Lizenz permissiver ist.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0741.rst
Zuletzt geändert: 2024-09-03 13:37:25 GMT