diff options
author | Victor Stinner <vstinner@python.org> | 2024-09-03 12:33:49 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-03 12:33:49 (GMT) |
commit | ef9d54703f1f21d2fde2fe3ef49d4cf7c8b38f06 (patch) | |
tree | f4955ada605c8562ba07183bb47ca88e800a1d29 /Python | |
parent | 9e079c220b7f64d78a1aa36a23b513d7f377a694 (diff) | |
download | cpython-ef9d54703f1f21d2fde2fe3ef49d4cf7c8b38f06.zip cpython-ef9d54703f1f21d2fde2fe3ef49d4cf7c8b38f06.tar.gz cpython-ef9d54703f1f21d2fde2fe3ef49d4cf7c8b38f06.tar.bz2 |
gh-107954, PEP 741: Add PyInitConfig C API (#123502)
Add Doc/c-api/config.rst documentation.
Diffstat (limited to 'Python')
-rw-r--r-- | Python/initconfig.c | 493 |
1 files changed, 485 insertions, 8 deletions
diff --git a/Python/initconfig.c b/Python/initconfig.c index 1c21486..d2bb460 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -1114,6 +1114,20 @@ PyConfig_SetBytesString(PyConfig *config, wchar_t **config_str, } +static inline void* +config_get_spec_member(const PyConfig *config, const PyConfigSpec *spec) +{ + return (char *)config + spec->offset; +} + + +static inline void* +preconfig_get_spec_member(const PyPreConfig *preconfig, const PyConfigSpec *spec) +{ + return (char *)preconfig + spec->offset; +} + + PyStatus _PyConfig_Copy(PyConfig *config, const PyConfig *config2) { @@ -1122,8 +1136,8 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2) PyStatus status; const PyConfigSpec *spec = PYCONFIG_SPEC; for (; spec->name != NULL; spec++) { - char *member = (char *)config + spec->offset; - char *member2 = (char *)config2 + spec->offset; + void *member = config_get_spec_member(config, spec); + const void *member2 = config_get_spec_member((PyConfig*)config2, spec); switch (spec->type) { case PyConfig_MEMBER_INT: case PyConfig_MEMBER_UINT: @@ -3405,15 +3419,478 @@ _Py_DumpPathConfig(PyThreadState *tstate) } -// --- PyConfig_Get() ------------------------------------------------------- +// --- PyInitConfig API --------------------------------------------------- -static void* -config_spec_get_member(const PyConfigSpec *spec, const PyConfig *config) +struct PyInitConfig { + PyPreConfig preconfig; + PyConfig config; + PyStatus status; + char *err_msg; +}; + +static PyInitConfig* +initconfig_alloc(void) { - return (char *)config + spec->offset; + return calloc(1, sizeof(PyInitConfig)); +} + + +PyInitConfig* +PyInitConfig_Create(void) +{ + PyInitConfig *config = initconfig_alloc(); + if (config == NULL) { + return NULL; + } + PyPreConfig_InitIsolatedConfig(&config->preconfig); + PyConfig_InitIsolatedConfig(&config->config); + config->status = _PyStatus_OK(); + return config; +} + + +void +PyInitConfig_Free(PyInitConfig *config) +{ + free(config->err_msg); + free(config); +} + + +int +PyInitConfig_GetError(PyInitConfig* config, const char **perr_msg) +{ + if (_PyStatus_IS_EXIT(config->status)) { + char buffer[22]; // len("exit code -2147483648\0") + PyOS_snprintf(buffer, sizeof(buffer), + "exit code %i", + config->status.exitcode); + + if (config->err_msg != NULL) { + free(config->err_msg); + } + config->err_msg = strdup(buffer); + if (config->err_msg != NULL) { + *perr_msg = config->err_msg; + return 1; + } + config->status = _PyStatus_NO_MEMORY(); + } + + if (_PyStatus_IS_ERROR(config->status) && config->status.err_msg != NULL) { + *perr_msg = config->status.err_msg; + return 1; + } + else { + *perr_msg = NULL; + return 0; + } +} + + +int +PyInitConfig_GetExitCode(PyInitConfig* config, int *exitcode) +{ + if (_PyStatus_IS_EXIT(config->status)) { + *exitcode = config->status.exitcode; + return 1; + } + else { + return 0; + } +} + + +static void +initconfig_set_error(PyInitConfig *config, const char *err_msg) +{ + config->status = _PyStatus_ERR(err_msg); +} + + +static const PyConfigSpec* +initconfig_find_spec(const PyConfigSpec *spec, const char *name) +{ + for (; spec->name != NULL; spec++) { + if (strcmp(name, spec->name) == 0) { + return spec; + } + } + return NULL; +} + + +int +PyInitConfig_HasOption(PyInitConfig *config, const char *name) +{ + const PyConfigSpec *spec = initconfig_find_spec(PYCONFIG_SPEC, name); + if (spec == NULL) { + spec = initconfig_find_spec(PYPRECONFIG_SPEC, name); + } + return (spec != NULL); +} + + +static const PyConfigSpec* +initconfig_prepare(PyInitConfig *config, const char *name, void **raw_member) +{ + const PyConfigSpec *spec = initconfig_find_spec(PYCONFIG_SPEC, name); + if (spec != NULL) { + *raw_member = config_get_spec_member(&config->config, spec); + return spec; + } + + spec = initconfig_find_spec(PYPRECONFIG_SPEC, name); + if (spec != NULL) { + *raw_member = preconfig_get_spec_member(&config->preconfig, spec); + return spec; + } + + initconfig_set_error(config, "unknown config option name"); + return NULL; +} + + +int +PyInitConfig_GetInt(PyInitConfig *config, const char *name, int64_t *value) +{ + void *raw_member; + const PyConfigSpec *spec = initconfig_prepare(config, name, &raw_member); + if (spec == NULL) { + return -1; + } + + switch (spec->type) { + case PyConfig_MEMBER_INT: + case PyConfig_MEMBER_UINT: + case PyConfig_MEMBER_BOOL: + { + int *member = raw_member; + *value = *member; + break; + } + + case PyConfig_MEMBER_ULONG: + { + unsigned long *member = raw_member; +#if SIZEOF_LONG >= 8 + if ((unsigned long)INT64_MAX < *member) { + initconfig_set_error(config, + "config option value doesn't fit into int64_t"); + return -1; + } +#endif + *value = *member; + break; + } + + default: + initconfig_set_error(config, "config option type is not int"); + return -1; + } + return 0; +} + + +static char* +wstr_to_utf8(PyInitConfig *config, wchar_t *wstr) +{ + char *utf8; + int res = _Py_EncodeUTF8Ex(wstr, &utf8, NULL, NULL, 1, _Py_ERROR_STRICT); + if (res == -2) { + initconfig_set_error(config, "encoding error"); + return NULL; + } + if (res < 0) { + config->status = _PyStatus_NO_MEMORY(); + return NULL; + } + + // Copy to use the malloc() memory allocator + size_t size = strlen(utf8) + 1; + char *str = malloc(size); + if (str == NULL) { + PyMem_RawFree(utf8); + config->status = _PyStatus_NO_MEMORY(); + return NULL; + } + + memcpy(str, utf8, size); + PyMem_RawFree(utf8); + return str; +} + + +int +PyInitConfig_GetStr(PyInitConfig *config, const char *name, char **value) +{ + void *raw_member; + const PyConfigSpec *spec = initconfig_prepare(config, name, &raw_member); + if (spec == NULL) { + return -1; + } + + if (spec->type != PyConfig_MEMBER_WSTR + && spec->type != PyConfig_MEMBER_WSTR_OPT) + { + initconfig_set_error(config, "config option type is not string"); + return -1; + } + + wchar_t **member = raw_member; + if (*member == NULL) { + *value = NULL; + return 0; + } + + *value = wstr_to_utf8(config, *member); + if (*value == NULL) { + return -1; + } + return 0; +} + + +int +PyInitConfig_GetStrList(PyInitConfig *config, const char *name, size_t *length, char ***items) +{ + void *raw_member; + const PyConfigSpec *spec = initconfig_prepare(config, name, &raw_member); + if (spec == NULL) { + return -1; + } + + if (spec->type != PyConfig_MEMBER_WSTR_LIST) { + initconfig_set_error(config, "config option type is not string list"); + return -1; + } + + PyWideStringList *list = raw_member; + *length = list->length; + + *items = malloc(list->length * sizeof(char*)); + if (*items == NULL) { + config->status = _PyStatus_NO_MEMORY(); + return -1; + } + + for (Py_ssize_t i=0; i < list->length; i++) { + (*items)[i] = wstr_to_utf8(config, list->items[i]); + if ((*items)[i] == NULL) { + PyInitConfig_FreeStrList(i, *items); + return -1; + } + } + return 0; +} + + +void +PyInitConfig_FreeStrList(size_t length, char **items) +{ + for (size_t i=0; i < length; i++) { + free(items[i]); + } + free(items); +} + + +int +PyInitConfig_SetInt(PyInitConfig *config, const char *name, int64_t value) +{ + void *raw_member; + const PyConfigSpec *spec = initconfig_prepare(config, name, &raw_member); + if (spec == NULL) { + return -1; + } + + switch (spec->type) { + case PyConfig_MEMBER_INT: + { + if (value < (int64_t)INT_MIN || (int64_t)INT_MAX < value) { + initconfig_set_error(config, + "config option value is out of int range"); + return -1; + } + int int_value = (int)value; + + int *member = raw_member; + *member = int_value; + break; + } + + case PyConfig_MEMBER_UINT: + case PyConfig_MEMBER_BOOL: + { + if (value < 0 || (uint64_t)UINT_MAX < (uint64_t)value) { + initconfig_set_error(config, + "config option value is out of unsigned int range"); + return -1; + } + int int_value = (int)value; + + int *member = raw_member; + *member = int_value; + break; + } + + case PyConfig_MEMBER_ULONG: + { + if (value < 0 || (uint64_t)ULONG_MAX < (uint64_t)value) { + initconfig_set_error(config, + "config option value is out of unsigned long range"); + return -1; + } + unsigned long ulong_value = (unsigned long)value; + + unsigned long *member = raw_member; + *member = ulong_value; + break; + } + + default: + initconfig_set_error(config, "config option type is not int"); + return -1; + } + + if (strcmp(name, "hash_seed")) { + config->config.use_hash_seed = 1; + } + + return 0; +} + + +static wchar_t* +utf8_to_wstr(PyInitConfig *config, const char *str) +{ + wchar_t *wstr; + size_t wlen; + int res = _Py_DecodeUTF8Ex(str, strlen(str), &wstr, &wlen, NULL, _Py_ERROR_STRICT); + if (res == -2) { + initconfig_set_error(config, "decoding error"); + return NULL; + } + if (res < 0) { + config->status = _PyStatus_NO_MEMORY(); + return NULL; + } + + // Copy to use the malloc() memory allocator + size_t size = (wlen + 1) * sizeof(wchar_t); + wchar_t *wstr2 = malloc(size); + if (wstr2 == NULL) { + PyMem_RawFree(wstr); + config->status = _PyStatus_NO_MEMORY(); + return NULL; + } + + memcpy(wstr2, wstr, size); + PyMem_RawFree(wstr); + return wstr2; +} + + +int +PyInitConfig_SetStr(PyInitConfig *config, const char *name, const char* value) +{ + void *raw_member; + const PyConfigSpec *spec = initconfig_prepare(config, name, &raw_member); + if (spec == NULL) { + return -1; + } + + if (spec->type != PyConfig_MEMBER_WSTR + && spec->type != PyConfig_MEMBER_WSTR_OPT) { + initconfig_set_error(config, "config option type is not string"); + return -1; + } + + if (value == NULL && spec->type != PyConfig_MEMBER_WSTR_OPT) { + initconfig_set_error(config, "config option string cannot be NULL"); + } + + wchar_t **member = raw_member; + + *member = utf8_to_wstr(config, value); + if (*member == NULL) { + return -1; + } + return 0; } +static int +_PyWideStringList_FromUTF8(PyInitConfig *config, PyWideStringList *list, + Py_ssize_t length, char * const *items) +{ + PyWideStringList wlist = _PyWideStringList_INIT; + size_t size = sizeof(wchar_t*) * length; + wlist.items = (wchar_t **)PyMem_RawMalloc(size); + if (wlist.items == NULL) { + config->status = _PyStatus_NO_MEMORY(); + return -1; + } + + for (Py_ssize_t i = 0; i < length; i++) { + wchar_t *arg = utf8_to_wstr(config, items[i]); + if (arg == NULL) { + _PyWideStringList_Clear(&wlist); + return -1; + } + wlist.items[i] = arg; + wlist.length++; + } + + _PyWideStringList_Clear(list); + *list = wlist; + return 0; +} + + +int +PyInitConfig_SetStrList(PyInitConfig *config, const char *name, + size_t length, char * const *items) +{ + void *raw_member; + const PyConfigSpec *spec = initconfig_prepare(config, name, &raw_member); + if (spec == NULL) { + return -1; + } + + if (spec->type != PyConfig_MEMBER_WSTR_LIST) { + initconfig_set_error(config, "config option type is not strings list"); + return -1; + } + PyWideStringList *list = raw_member; + return _PyWideStringList_FromUTF8(config, list, length, items); +} + + +int +Py_InitializeFromInitConfig(PyInitConfig *config) +{ + _PyPreConfig_GetConfig(&config->preconfig, &config->config); + + config->status = Py_PreInitializeFromArgs( + &config->preconfig, + config->config.argv.length, + config->config.argv.items); + if (_PyStatus_EXCEPTION(config->status)) { + return -1; + } + + config->status = Py_InitializeFromConfig(&config->config); + if (_PyStatus_EXCEPTION(config->status)) { + return -1; + } + + return 0; +} + + +// --- PyConfig_Get() ------------------------------------------------------- + static const PyConfigSpec* config_generic_find_spec(const PyConfigSpec *spec, const char *name) { @@ -3554,7 +4031,7 @@ config_get(const PyConfig *config, const PyConfigSpec *spec, } } - void *member = config_spec_get_member(spec, config); + void *member = config_get_spec_member(config, spec); switch (spec->type) { case PyConfig_MEMBER_INT: case PyConfig_MEMBER_UINT: @@ -3761,7 +4238,7 @@ config_set_sys_flag(const PyConfigSpec *spec, int int_value) assert(spec->type == PyConfig_MEMBER_INT || spec->type == PyConfig_MEMBER_UINT || spec->type == PyConfig_MEMBER_BOOL); - int *member = config_spec_get_member(spec, config); + int *member = config_get_spec_member(config, spec); *member = int_value; // Set sys.dont_write_bytecode attribute |