From 9ee1d42f019ac827f73479ce241e95733d050e67 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 14 Nov 2018 18:58:01 +0100 Subject: [PATCH] bpo-35233: InitConfigTests tests more config vars (GH-10541) (GH-10546) test_embed.InitConfigTests tests more configuration variables. Changes: * InitConfigTests tests more core configuration variables: * base_exec_prefix * base_prefix * exec_prefix * home * module_search_path_env * prefix * "_testembed init_from_config" tests more variables: * argv * warnoptions * xoptions * Py_HasFileSystemDefaultEncoding value is no longer tested since it depends on the LC_CTYPE locale and the platform. * InitConfigTests: add check_global_config(), check_core_config() and check_main_config() subfunctions to cleanup the code. Move also constants at the class level (ex: COPY_MAIN_CONFIG). * Use more macros in _PyCoreConfig_AsDict() and _PyMainInterpreterConfig_AsDict() to reduce code duplication. * Other minor cleanups. (cherry picked from commit 01de89cb59107d4f889aa503a1c0350dae4aebaf) --- Include/pylifecycle.h | 1 + Lib/test/test_embed.py | 226 +++++++++++++++++++++++++---------------- Modules/main.c | 193 +++++++++++++++++------------------ Programs/_testembed.c | 26 ++++- 4 files changed, 255 insertions(+), 191 deletions(-) diff --git a/Include/pylifecycle.h b/Include/pylifecycle.h index 41f7d14af9e..68a88275004 100644 --- a/Include/pylifecycle.h +++ b/Include/pylifecycle.h @@ -80,6 +80,7 @@ PyAPI_FUNC(void) _PyMainInterpreterConfig_Clear(_PyMainInterpreterConfig *); PyAPI_FUNC(int) _PyMainInterpreterConfig_Copy( _PyMainInterpreterConfig *config, const _PyMainInterpreterConfig *config2); +/* Used by _testcapi.get_main_config() */ PyAPI_FUNC(PyObject*) _PyMainInterpreterConfig_AsDict( const _PyMainInterpreterConfig *config); diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 6e2e212105f..81f64f5048a 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -10,12 +10,15 @@ import subprocess import sys +MS_WINDOWS = (os.name == 'nt') + + class EmbeddingTestsMixin: def setUp(self): here = os.path.abspath(__file__) basepath = os.path.dirname(os.path.dirname(os.path.dirname(here))) exename = "_testembed" - if sys.platform.startswith("win"): + if MS_WINDOWS: ext = ("_d" if "_d" in sys.executable else "") + ".exe" exename += ext exepath = os.path.dirname(sys.executable) @@ -37,7 +40,7 @@ class EmbeddingTestsMixin: """Runs a test in the embedded interpreter""" cmd = [self.test_exe] cmd.extend(args) - if env is not None and sys.platform == 'win32': + if env is not None and MS_WINDOWS: # Windows requires at least the SYSTEMROOT environment variable to # start Python. env = env.copy() @@ -198,7 +201,7 @@ class EmbeddingTests(EmbeddingTestsMixin, unittest.TestCase): """ env = dict(os.environ, PYTHONPATH=os.pathsep.join(sys.path)) out, err = self.run_embedded_interpreter("pre_initialization_api", env=env) - if sys.platform == "win32": + if MS_WINDOWS: expected_path = self.test_exe else: expected_path = os.path.join(os.getcwd(), "spam") @@ -252,46 +255,15 @@ class EmbeddingTests(EmbeddingTestsMixin, unittest.TestCase): class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): maxDiff = 4096 - UTF8_MODE_ERRORS = ('surrogatepass' if sys.platform == 'win32' - else 'surrogateescape') - # FIXME: untested core configuration variables - UNTESTED_CORE_CONFIG = ( - 'base_exec_prefix', - 'base_prefix', - 'exec_prefix', - 'executable', - 'home', - 'module_search_path_env', - 'module_search_paths', - 'prefix', - ) - # FIXME: untested main configuration variables - UNTESTED_MAIN_CONFIG = ( - 'module_search_path', - ) - DEFAULT_GLOBAL_CONFIG = { - 'Py_BytesWarningFlag': 0, - 'Py_DebugFlag': 0, - 'Py_DontWriteBytecodeFlag': 0, - 'Py_FrozenFlag': 0, - 'Py_HasFileSystemDefaultEncoding': 0, - 'Py_HashRandomizationFlag': 1, - 'Py_InspectFlag': 0, - 'Py_InteractiveFlag': 0, - 'Py_IsolatedFlag': 0, - 'Py_NoSiteFlag': 0, - 'Py_NoUserSiteDirectory': 0, - 'Py_OptimizeFlag': 0, - 'Py_QuietFlag': 0, - 'Py_UnbufferedStdioFlag': 0, - 'Py_VerboseFlag': 0, - } - if os.name == 'nt': - DEFAULT_GLOBAL_CONFIG['Py_LegacyWindowsFSEncodingFlag'] = 0 - DEFAULT_GLOBAL_CONFIG['Py_LegacyWindowsStdioFlag'] = 0 - if sys.platform in ('win32', 'darwin'): - DEFAULT_GLOBAL_CONFIG['Py_HasFileSystemDefaultEncoding'] = 1 + UTF8_MODE_ERRORS = ('surrogatepass' if MS_WINDOWS else 'surrogateescape') + # core config + UNTESTED_CORE_CONFIG = ( + # FIXME: untested core configuration variables + 'dll_path', + 'executable', + 'module_search_paths', + ) DEFAULT_CORE_CONFIG = { 'install_signal_handlers': 1, 'ignore_environment': 0, @@ -318,9 +290,72 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): 'xoptions': [], 'warnoptions': [], + 'module_search_path_env': None, + 'home': None, + 'prefix': sys.prefix, + 'base_prefix': sys.base_prefix, + 'exec_prefix': sys.exec_prefix, + 'base_exec_prefix': sys.base_exec_prefix, + '_disable_importlib': 0, } + # main config + UNTESTED_MAIN_CONFIG = ( + # FIXME: untested main configuration variables + 'module_search_path', + ) + COPY_MAIN_CONFIG = ( + # Copy core config to main config for expected values + 'argv', + 'base_exec_prefix', + 'base_prefix', + 'exec_prefix', + 'executable', + 'install_signal_handlers', + 'prefix', + 'warnoptions', + # xoptions is created from core_config in check_main_config() + ) + + # global config + UNTESTED_GLOBAL_CONFIG = ( + # Py_HasFileSystemDefaultEncoding value depends on the LC_CTYPE locale + # and the platform. It is complex to test it, and it's value doesn't + # really matter. + 'Py_HasFileSystemDefaultEncoding', + ) + DEFAULT_GLOBAL_CONFIG = { + 'Py_BytesWarningFlag': 0, + 'Py_DebugFlag': 0, + 'Py_DontWriteBytecodeFlag': 0, + # None means that the value is get by get_filesystem_encoding() + 'Py_FileSystemDefaultEncodeErrors': None, + 'Py_FileSystemDefaultEncoding': None, + 'Py_FrozenFlag': 0, + 'Py_HashRandomizationFlag': 1, + 'Py_InspectFlag': 0, + 'Py_InteractiveFlag': 0, + 'Py_IsolatedFlag': 0, + 'Py_NoSiteFlag': 0, + 'Py_NoUserSiteDirectory': 0, + 'Py_OptimizeFlag': 0, + 'Py_QuietFlag': 0, + 'Py_UnbufferedStdioFlag': 0, + 'Py_VerboseFlag': 0, + } + if MS_WINDOWS: + DEFAULT_GLOBAL_CONFIG.update({ + 'Py_LegacyWindowsFSEncodingFlag': 0, + 'Py_LegacyWindowsStdioFlag': 0, + }) + COPY_GLOBAL_CONFIG = [ + # Copy core config to global config for expected values + # True means that the core config value is inverted (0 => 1 and 1 => 0) + ('Py_IgnoreEnvironmentFlag', 'ignore_environment'), + ('Py_UTF8Mode', 'utf8_mode'), + ] + def get_filesystem_encoding(self, isolated, env): code = ('import codecs, locale, sys; ' 'print(sys.getfilesystemencoding(), ' @@ -339,10 +374,65 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): out = proc.stdout.rstrip() return out.split() - def check_config(self, testname, expected, expected_global): - expected = dict(self.DEFAULT_CORE_CONFIG, **expected) + def main_xoptions(self, xoptions_list): + xoptions = {} + for opt in xoptions_list: + if '=' in opt: + key, value = opt.split('=', 1) + xoptions[key] = value + else: + xoptions[opt] = True + return xoptions + def check_main_config(self, config): + core_config = config['core_config'] + main_config = config['main_config'] + + # main config + for key in self.UNTESTED_MAIN_CONFIG: + del main_config[key] + + expected_main = {} + for key in self.COPY_MAIN_CONFIG: + expected_main[key] = core_config[key] + expected_main['xoptions'] = self.main_xoptions(core_config['xoptions']) + self.assertEqual(main_config, expected_main) + + def check_core_config(self, config, expected): + expected = dict(self.DEFAULT_CORE_CONFIG, **expected) + core_config = dict(config['core_config']) + for key in self.UNTESTED_CORE_CONFIG: + core_config.pop(key, None) + self.assertEqual(core_config, expected) + + def check_global_config(self, config, expected, env): + expected = dict(self.DEFAULT_GLOBAL_CONFIG, **expected) + + if expected['Py_FileSystemDefaultEncoding'] is None or expected['Py_FileSystemDefaultEncodeErrors'] is None: + res = self.get_filesystem_encoding(expected['Py_IsolatedFlag'], env) + if expected['Py_FileSystemDefaultEncoding'] is None: + expected['Py_FileSystemDefaultEncoding'] = res[0] + if expected['Py_FileSystemDefaultEncodeErrors'] is None: + expected['Py_FileSystemDefaultEncodeErrors'] = res[1] + + core_config = config['core_config'] + + for item in self.COPY_GLOBAL_CONFIG: + if len(item) == 3: + global_key, core_key, opposite = item + expected[global_key] = 0 if core_config[core_key] else 1 + else: + global_key, core_key = item + expected[global_key] = core_config[core_key] + + global_config = dict(config['global_config']) + for key in self.UNTESTED_GLOBAL_CONFIG: + del global_config[key] + self.assertEqual(global_config, expected) + + def check_config(self, testname, expected_core, expected_global): env = dict(os.environ) + # Remove PYTHON* environment variables to get deterministic environment for key in list(env): if key.startswith('PYTHON'): del env[key] @@ -353,47 +443,11 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): out, err = self.run_embedded_interpreter(testname, env=env) # Ignore err - config = json.loads(out) - core_config = config['core_config'] - executable = core_config['executable'] - main_config = config['main_config'] - for key in self.UNTESTED_MAIN_CONFIG: - del main_config[key] - - expected_main = { - 'install_signal_handlers': core_config['install_signal_handlers'], - 'argv': [], - 'prefix': sys.prefix, - 'executable': core_config['executable'], - 'base_prefix': sys.base_prefix, - 'base_exec_prefix': sys.base_exec_prefix, - 'warnoptions': core_config['warnoptions'], - 'xoptions': {}, - 'exec_prefix': core_config['exec_prefix'], - } - self.assertEqual(main_config, expected_main) - - expected_global = dict(self.DEFAULT_GLOBAL_CONFIG, **expected_global) - - if 'Py_FileSystemDefaultEncoding' not in expected_global: - isolated = expected_global['Py_IsolatedFlag'] - fs_encoding, fs_errors = self.get_filesystem_encoding(isolated, env) - expected_global['Py_FileSystemDefaultEncodeErrors'] = fs_errors - expected_global['Py_FileSystemDefaultEncoding'] = fs_encoding - - for global_key, core_key in ( - ('Py_UTF8Mode', 'utf8_mode'), - ('Py_IgnoreEnvironmentFlag', 'ignore_environment'), - ): - expected_global[global_key] = core_config[core_key] - - self.assertEqual(config['global_config'], expected_global) - - for key in self.UNTESTED_CORE_CONFIG: - core_config.pop(key, None) - self.assertEqual(core_config, expected) + self.check_core_config(config, expected_core) + self.check_main_config(config) + self.check_global_config(config, expected_global, env) def test_init_default_config(self): self.check_config("init_default_config", {}, {}) @@ -406,7 +460,6 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): global_config = { 'Py_BytesWarningFlag': 1, 'Py_DontWriteBytecodeFlag': 1, - 'Py_HasFileSystemDefaultEncoding': 1, 'Py_FileSystemDefaultEncodeErrors': self.UTF8_MODE_ERRORS, 'Py_FileSystemDefaultEncoding': 'utf-8', 'Py_InspectFlag': 1, @@ -436,12 +489,14 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): 'utf8_mode': 1, 'program_name': './conf_program_name', + 'argv': ['-c', 'pass'], 'program': 'conf_program', + 'xoptions': ['core_xoption1=3', 'core_xoption2=', 'core_xoption3'], + 'warnoptions': ['default', 'error::ResourceWarning'], 'faulthandler': 1, } global_config = { - 'Py_HasFileSystemDefaultEncoding': 1, 'Py_NoUserSiteDirectory': 0, } self.check_config("init_from_config", core_config, global_config) @@ -460,7 +515,6 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): } global_config = { 'Py_DontWriteBytecodeFlag': 1, - 'Py_HasFileSystemDefaultEncoding': 1, 'Py_InspectFlag': 1, 'Py_NoUserSiteDirectory': 1, 'Py_OptimizeFlag': 2, diff --git a/Modules/main.c b/Modules/main.c index c0a9c262dfb..f0f7fe55a41 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -2513,14 +2513,6 @@ _PyCoreConfig_AsDict(const _PyCoreConfig *config) return NULL; } -#define FROM_STRING(STR) \ - ((STR != NULL) ? \ - PyUnicode_FromString(STR) \ - : (Py_INCREF(Py_None), Py_None)) -#define FROM_WSTRING(STR) \ - ((STR != NULL) ? \ - PyUnicode_FromWideChar(STR, -1) \ - : (Py_INCREF(Py_None), Py_None)) #define SET_ITEM(KEY, EXPR) \ do { \ obj = (EXPR); \ @@ -2533,67 +2525,55 @@ _PyCoreConfig_AsDict(const _PyCoreConfig *config) goto fail; \ } \ } while (0) +#define FROM_STRING(STR) \ + ((STR != NULL) ? \ + PyUnicode_FromString(STR) \ + : (Py_INCREF(Py_None), Py_None)) +#define SET_ITEM_INT(ATTR) \ + SET_ITEM(#ATTR, PyLong_FromLong(config->ATTR)) +#define SET_ITEM_UINT(ATTR) \ + SET_ITEM(#ATTR, PyLong_FromUnsignedLong(config->ATTR)) +#define SET_ITEM_STR(ATTR) \ + SET_ITEM(#ATTR, FROM_STRING(config->ATTR)) +#define FROM_WSTRING(STR) \ + ((STR != NULL) ? \ + PyUnicode_FromWideChar(STR, -1) \ + : (Py_INCREF(Py_None), Py_None)) +#define SET_ITEM_WSTR(ATTR) \ + SET_ITEM(#ATTR, FROM_WSTRING(config->ATTR)) +#define SET_ITEM_WSTRLIST(NOPTION, OPTIONS) \ + SET_ITEM(#OPTIONS, _Py_wstrlist_as_pylist(config->NOPTION, config->OPTIONS)) - SET_ITEM("install_signal_handlers", - PyLong_FromLong(config->install_signal_handlers)); - SET_ITEM("ignore_environment", - PyLong_FromLong(config->ignore_environment)); - SET_ITEM("use_hash_seed", - PyLong_FromLong(config->use_hash_seed)); - SET_ITEM("hash_seed", - PyLong_FromUnsignedLong(config->hash_seed)); - SET_ITEM("allocator", - FROM_STRING(config->allocator)); - SET_ITEM("dev_mode", - PyLong_FromLong(config->dev_mode)); - SET_ITEM("faulthandler", - PyLong_FromLong(config->faulthandler)); - SET_ITEM("tracemalloc", - PyLong_FromLong(config->tracemalloc)); - SET_ITEM("import_time", - PyLong_FromLong(config->import_time)); - SET_ITEM("show_ref_count", - PyLong_FromLong(config->show_ref_count)); - SET_ITEM("show_alloc_count", - PyLong_FromLong(config->show_alloc_count)); - SET_ITEM("dump_refs", - PyLong_FromLong(config->dump_refs)); - SET_ITEM("malloc_stats", - PyLong_FromLong(config->malloc_stats)); - SET_ITEM("coerce_c_locale", - PyLong_FromLong(config->coerce_c_locale)); - SET_ITEM("coerce_c_locale_warn", - PyLong_FromLong(config->coerce_c_locale_warn)); - SET_ITEM("utf8_mode", - PyLong_FromLong(config->utf8_mode)); - SET_ITEM("program_name", - FROM_WSTRING(config->program_name)); - SET_ITEM("argv", - _Py_wstrlist_as_pylist(config->argc, config->argv)); - SET_ITEM("program", - FROM_WSTRING(config->program)); - SET_ITEM("xoptions", - _Py_wstrlist_as_pylist(config->nxoption, config->xoptions)); - SET_ITEM("warnoptions", - _Py_wstrlist_as_pylist(config->nwarnoption, config->warnoptions)); - SET_ITEM("module_search_path_env", - FROM_WSTRING(config->module_search_path_env)); - SET_ITEM("home", - FROM_WSTRING(config->home)); - SET_ITEM("module_search_paths", - _Py_wstrlist_as_pylist(config->nmodule_search_path, config->module_search_paths)); - SET_ITEM("executable", - FROM_WSTRING(config->executable)); - SET_ITEM("prefix", - FROM_WSTRING(config->prefix)); - SET_ITEM("base_prefix", - FROM_WSTRING(config->base_prefix)); - SET_ITEM("exec_prefix", - FROM_WSTRING(config->exec_prefix)); - SET_ITEM("base_exec_prefix", - FROM_WSTRING(config->base_exec_prefix)); - SET_ITEM("_disable_importlib", - PyLong_FromLong(config->_disable_importlib)); + SET_ITEM_INT(install_signal_handlers); + SET_ITEM_INT(ignore_environment); + SET_ITEM_INT(use_hash_seed); + SET_ITEM_UINT(hash_seed); + SET_ITEM_STR(allocator); + SET_ITEM_INT(dev_mode); + SET_ITEM_INT(faulthandler); + SET_ITEM_INT(tracemalloc); + SET_ITEM_INT(import_time); + SET_ITEM_INT(show_ref_count); + SET_ITEM_INT(show_alloc_count); + SET_ITEM_INT(dump_refs); + SET_ITEM_INT(malloc_stats); + SET_ITEM_INT(coerce_c_locale); + SET_ITEM_INT(coerce_c_locale_warn); + SET_ITEM_INT(utf8_mode); + SET_ITEM_WSTR(program_name); + SET_ITEM_WSTRLIST(argc, argv); + SET_ITEM_WSTR(program); + SET_ITEM_WSTRLIST(nxoption, xoptions); + SET_ITEM_WSTRLIST(nwarnoption, warnoptions); + SET_ITEM_WSTR(module_search_path_env); + SET_ITEM_WSTR(home); + SET_ITEM_WSTRLIST(nmodule_search_path, module_search_paths); + SET_ITEM_WSTR(executable); + SET_ITEM_WSTR(prefix); + SET_ITEM_WSTR(base_prefix); + SET_ITEM_WSTR(exec_prefix); + SET_ITEM_WSTR(base_exec_prefix); + SET_ITEM_INT(_disable_importlib); return dict; @@ -2604,6 +2584,11 @@ fail: #undef FROM_STRING #undef FROM_WSTRING #undef SET_ITEM +#undef SET_ITEM_INT +#undef SET_ITEM_UINT +#undef SET_ITEM_STR +#undef SET_ITEM_WSTR +#undef SET_ITEM_WSTRLIST } @@ -2653,11 +2638,11 @@ _PyMainInterpreterConfig_Copy(_PyMainInterpreterConfig *config, _PyMainInterpreterConfig_Clear(config); #define COPY_ATTR(ATTR) config->ATTR = config2->ATTR -#define COPY_OBJ_ATTR(OBJ_ATTR) \ +#define COPY_OBJ_ATTR(ATTR) \ do { \ - if (config2->OBJ_ATTR != NULL) { \ - config->OBJ_ATTR = config_copy_attr(config2->OBJ_ATTR); \ - if (config->OBJ_ATTR == NULL) { \ + if (config2->ATTR != NULL) { \ + config->ATTR = config_copy_attr(config2->ATTR); \ + if (config->ATTR == NULL) { \ return -1; \ } \ } \ @@ -2690,37 +2675,41 @@ _PyMainInterpreterConfig_AsDict(const _PyMainInterpreterConfig *config) return NULL; } -#define SET_ITEM(KEY, ATTR) \ - do { \ - obj = config->ATTR; \ - if (obj == NULL) { \ - obj = Py_None; \ - } \ - res = PyDict_SetItemString(dict, (KEY), obj); \ - if (res < 0) { \ - goto fail; \ - } \ - } while (0) +#define SET_ITEM_INT(ATTR) \ + do { \ + obj = PyLong_FromLong(config->ATTR); \ + if (obj == NULL) { \ + goto fail; \ + } \ + res = PyDict_SetItemString(dict, #ATTR, obj); \ + Py_DECREF(obj); \ + if (res < 0) { \ + goto fail; \ + } \ + } while (0) - obj = PyLong_FromLong(config->install_signal_handlers); - if (obj == NULL) { - goto fail; - } - res = PyDict_SetItemString(dict, "install_signal_handlers", obj); - Py_DECREF(obj); - if (res < 0) { - goto fail; - } +#define SET_ITEM_OBJ(ATTR) \ + do { \ + obj = config->ATTR; \ + if (obj == NULL) { \ + obj = Py_None; \ + } \ + res = PyDict_SetItemString(dict, #ATTR, obj); \ + if (res < 0) { \ + goto fail; \ + } \ + } while (0) - SET_ITEM("argv", argv); - SET_ITEM("executable", executable); - SET_ITEM("prefix", prefix); - SET_ITEM("base_prefix", base_prefix); - SET_ITEM("exec_prefix", exec_prefix); - SET_ITEM("base_exec_prefix", base_exec_prefix); - SET_ITEM("warnoptions", warnoptions); - SET_ITEM("xoptions", xoptions); - SET_ITEM("module_search_path", module_search_path); + SET_ITEM_INT(install_signal_handlers); + SET_ITEM_OBJ(argv); + SET_ITEM_OBJ(executable); + SET_ITEM_OBJ(prefix); + SET_ITEM_OBJ(base_prefix); + SET_ITEM_OBJ(exec_prefix); + SET_ITEM_OBJ(base_exec_prefix); + SET_ITEM_OBJ(warnoptions); + SET_ITEM_OBJ(xoptions); + SET_ITEM_OBJ(module_search_path); return dict; @@ -2728,7 +2717,7 @@ fail: Py_DECREF(dict); return NULL; -#undef SET_ITEM +#undef SET_ITEM_OBJ } diff --git a/Programs/_testembed.c b/Programs/_testembed.c index b198962db3a..20b853e98d4 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -467,10 +467,30 @@ static int test_init_from_config(void) Py_SetProgramName(L"./globalvar"); config.program_name = L"./conf_program_name"; - /* FIXME: test argc/argv */ + static wchar_t* argv[2] = { + L"-c", + L"pass", + }; + config.argc = Py_ARRAY_LENGTH(argv); + config.argv = argv; + config.program = L"conf_program"; - /* FIXME: test xoptions */ - /* FIXME: test warnoptions */ + + static wchar_t* xoptions[3] = { + L"core_xoption1=3", + L"core_xoption2=", + L"core_xoption3", + }; + config.nxoption = Py_ARRAY_LENGTH(xoptions); + config.xoptions = xoptions; + + static wchar_t* warnoptions[2] = { + L"default", + L"error::ResourceWarning", + }; + config.nwarnoption = Py_ARRAY_LENGTH(warnoptions); + config.warnoptions = warnoptions; + /* FIXME: test module_search_path_env */ /* FIXME: test home */ /* FIXME: test path config: module_search_path .. dll_path */