From 3842f2997fbd4dc840986aad2bb94656815e243b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 23 Aug 2019 16:57:54 +0100 Subject: [PATCH] bpo-36763: Implement PyWideStringList_Insert() of PEP 587 (GH-15423) --- Doc/c-api/init_config.rst | 8 ++++++-- Include/cpython/initconfig.h | 3 +++ Lib/test/test_embed.py | 16 ++++++++++------ Programs/_testembed.c | 8 +++++++- Python/initconfig.c | 29 +++++++++++++++++++++++++---- 5 files changed, 51 insertions(+), 13 deletions(-) diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst index d2c1f9a2f3e..5fd836d77b8 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst @@ -72,8 +72,12 @@ PyWideStringList .. c:function:: PyStatus PyWideStringList_Insert(PyWideStringList *list, Py_ssize_t index, const wchar_t *item) - Insert *item* into *list* at *index*. If *index* is greater than *list* - length, just append *item* to *list*. + Insert *item* into *list* at *index*. + + If *index* is greater than or equal to *list* length, append *item* to + *list*. + + *index* must be greater than or equal to 0. Python must be preinitialized to call this function. diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h index bd07a4829b4..047f511f310 100644 --- a/Include/cpython/initconfig.h +++ b/Include/cpython/initconfig.h @@ -37,6 +37,9 @@ typedef struct { PyAPI_FUNC(PyStatus) PyWideStringList_Append(PyWideStringList *list, const wchar_t *item); +PyAPI_FUNC(PyStatus) PyWideStringList_Insert(PyWideStringList *list, + Py_ssize_t index, + const wchar_t *item); /* --- PyPreConfig ----------------------------------------------- */ diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 37f542b2954..6fb40120038 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -500,7 +500,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): self.fail(f"fail to decode stdout: {stdout!r}") def get_expected_config(self, expected_preconfig, expected, env, api, - add_path=None): + modify_path_cb=None): cls = self.__class__ if cls.EXPECTED_CONFIG is None: cls.EXPECTED_CONFIG = self._get_expected_config(env) @@ -556,8 +556,9 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): prepend_path = expected['pythonpath_env'] if prepend_path is not None: expected['module_search_paths'] = [prepend_path, *expected['module_search_paths']] - if add_path is not None: - expected['module_search_paths'] = [*expected['module_search_paths'], add_path] + if modify_path_cb is not None: + expected['module_search_paths'] = expected['module_search_paths'].copy() + modify_path_cb(expected['module_search_paths']) for key in self.COPY_PRE_CONFIG: if key not in expected_preconfig: @@ -602,7 +603,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): self.assertEqual(configs['global_config'], expected) def check_all_configs(self, testname, expected_config=None, - expected_preconfig=None, add_path=None, stderr=None, + expected_preconfig=None, modify_path_cb=None, stderr=None, *, api): env = remove_python_envvars() @@ -628,7 +629,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): self.get_expected_config(expected_preconfig, expected_config, env, - api, add_path) + api, modify_path_cb) out, err = self.run_embedded_interpreter(testname, env=env) if stderr is None and not expected_config['verbose']: @@ -894,9 +895,12 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): 'program_name': './init_read_set', 'executable': 'my_executable', } + def modify_path(path): + path.insert(1, "test_path_insert1") + path.append("test_path_append") self.check_all_configs("test_init_read_set", config, api=API_PYTHON, - add_path="init_read_set_path") + modify_path_cb=modify_path) def test_init_run_main(self): code = ('import _testinternalcapi, json; ' diff --git a/Programs/_testembed.c b/Programs/_testembed.c index 3d27ed2a400..38730095dab 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -1350,8 +1350,14 @@ static int test_init_read_set(void) goto fail; } + status = PyWideStringList_Insert(&config.module_search_paths, + 1, L"test_path_insert1"); + if (PyStatus_Exception(status)) { + goto fail; + } + status = PyWideStringList_Append(&config.module_search_paths, - L"init_read_set_path"); + L"test_path_append"); if (PyStatus_Exception(status)) { goto fail; } diff --git a/Python/initconfig.c b/Python/initconfig.c index 5bd7d4fcf88..b706f4cb851 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -297,32 +297,53 @@ _PyWideStringList_Copy(PyWideStringList *list, const PyWideStringList *list2) PyStatus -PyWideStringList_Append(PyWideStringList *list, const wchar_t *item) +PyWideStringList_Insert(PyWideStringList *list, + Py_ssize_t index, const wchar_t *item) { - if (list->length == PY_SSIZE_T_MAX) { + Py_ssize_t len = list->length; + if (len == PY_SSIZE_T_MAX) { /* length+1 would overflow */ return _PyStatus_NO_MEMORY(); } + if (index < 0) { + return _PyStatus_ERR("PyWideStringList_Insert index must be >= 0"); + } + if (index > len) { + index = len; + } wchar_t *item2 = _PyMem_RawWcsdup(item); if (item2 == NULL) { return _PyStatus_NO_MEMORY(); } - size_t size = (list->length + 1) * sizeof(list->items[0]); + size_t size = (len + 1) * sizeof(list->items[0]); wchar_t **items2 = (wchar_t **)PyMem_RawRealloc(list->items, size); if (items2 == NULL) { PyMem_RawFree(item2); return _PyStatus_NO_MEMORY(); } - items2[list->length] = item2; + if (index < len) { + memmove(&items2[index + 1], + &items2[index], + (len - index) * sizeof(items2[0])); + } + + items2[index] = item2; list->items = items2; list->length++; return _PyStatus_OK(); } +PyStatus +PyWideStringList_Append(PyWideStringList *list, const wchar_t *item) +{ + return PyWideStringList_Insert(list, list->length, item); +} + + PyStatus _PyWideStringList_Extend(PyWideStringList *list, const PyWideStringList *list2) {