[Python-checkins] bpo-34170, test_embed: write Py_Initialize() tests (GH-8484)

Victor Stinner webhook-mailer at python.org
Thu Jul 26 12:58:02 EDT 2018


https://github.com/python/cpython/commit/56b29b6d6fa3eb32bb1533ee3f21b1e7135648a0
commit: 56b29b6d6fa3eb32bb1533ee3f21b1e7135648a0
branch: master
author: Victor Stinner <vstinner at redhat.com>
committer: GitHub <noreply at github.com>
date: 2018-07-26T18:57:56+02:00
summary:

bpo-34170, test_embed: write Py_Initialize() tests (GH-8484)

files:
M Include/pystate.h
M Lib/test/test_embed.py
M Programs/_testembed.c

diff --git a/Include/pystate.h b/Include/pystate.h
index a35fc89e0522..612e7de99bef 100644
--- a/Include/pystate.h
+++ b/Include/pystate.h
@@ -55,7 +55,12 @@ typedef struct {
     int malloc_stats;       /* PYTHONMALLOCSTATS */
     int coerce_c_locale;    /* PYTHONCOERCECLOCALE, -1 means unknown */
     int coerce_c_locale_warn; /* PYTHONCOERCECLOCALE=warn */
-    int utf8_mode;          /* PYTHONUTF8, -X utf8; -1 means unknown */
+
+    /* Enable UTF-8 mode?
+       Set by -X utf8 command line option and PYTHONUTF8 environment variable.
+       If set to -1 (default), inherit Py_UTF8Mode value. */
+    int utf8_mode;
+
     wchar_t *pycache_prefix; /* PYTHONPYCACHEPREFIX, -X pycache_prefix=PATH */
 
     wchar_t *program_name;  /* Program name, see also Py_GetProgramName() */
diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py
index 024c3f99a85d..e00b1d8f481c 100644
--- a/Lib/test/test_embed.py
+++ b/Lib/test/test_embed.py
@@ -9,7 +9,7 @@
 import sys
 
 
-class EmbeddingTests(unittest.TestCase):
+class EmbeddingTestsMixin:
     def setUp(self):
         here = os.path.abspath(__file__)
         basepath = os.path.dirname(os.path.dirname(os.path.dirname(here)))
@@ -110,6 +110,8 @@ def run_repeated_init_and_subinterpreters(self):
                 yield current_run
                 current_run = []
 
+
+class EmbeddingTests(EmbeddingTestsMixin, unittest.TestCase):
     def test_subinterps_main(self):
         for run in self.run_repeated_init_and_subinterpreters():
             main = run[0]
@@ -247,5 +249,160 @@ def test_initialize_pymain(self):
         self.assertEqual(err, '')
 
 
+class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
+    maxDiff = 4096
+    DEFAULT_CONFIG = {
+        'install_signal_handlers': 1,
+        'use_environment': 1,
+        'use_hash_seed': 0,
+        'hash_seed': 0,
+        'allocator': '(null)',
+        'dev_mode': 0,
+        'faulthandler': 0,
+        'tracemalloc': 0,
+        'import_time': 0,
+        'show_ref_count': 0,
+        'show_alloc_count': 0,
+        'dump_refs': 0,
+        'malloc_stats': 0,
+        'utf8_mode': 0,
+
+        'coerce_c_locale': 0,
+        'coerce_c_locale_warn': 0,
+
+        'pycache_prefix': '(null)',
+        'program_name': './_testembed',
+        'program': '(null)',
+
+        'isolated': 0,
+        'site_import': 1,
+        'bytes_warning': 0,
+        'inspect': 0,
+        'interactive': 0,
+        'optimization_level': 0,
+        'debug': 0,
+        'write_bytecode': 1,
+        'verbose': 0,
+        'quiet': 0,
+        'user_site_directory': 1,
+        'unbuffered_stdio': 0,
+
+        '_install_importlib': 1,
+        '_check_hash_pycs_mode': 'default',
+    }
+
+    def check_config(self, testname, expected):
+        env = dict(os.environ)
+        for key in list(env):
+            if key.startswith('PYTHON'):
+                del env[key]
+        # Disable C locale coercion and UTF-8 mode to not depend
+        # on the current locale
+        env['PYTHONCOERCECLOCALE'] = '0'
+        env['PYTHONUTF8'] = '0'
+        out, err = self.run_embedded_interpreter(testname, env=env)
+        # Ignore err
+
+        expected = dict(self.DEFAULT_CONFIG, **expected)
+        for key, value in expected.items():
+            expected[key] = str(value)
+
+        config = {}
+        for line in out.splitlines():
+            key, value = line.split(' = ', 1)
+            config[key] = value
+        self.assertEqual(config, expected)
+
+    def test_init_default_config(self):
+        self.check_config("init_default_config", {})
+
+    def test_init_global_config(self):
+        config = {
+            'program_name': './globalvar',
+            'site_import': 0,
+            'bytes_warning': 1,
+            'inspect': 1,
+            'interactive': 1,
+            'optimization_level': 2,
+            'write_bytecode': 0,
+            'verbose': 1,
+            'quiet': 1,
+            'unbuffered_stdio': 1,
+            'utf8_mode': 1,
+            'user_site_directory': 0,
+        }
+        self.check_config("init_global_config", config)
+
+    def test_init_from_config(self):
+        config = {
+            'install_signal_handlers': 0,
+            'use_hash_seed': 1,
+            'hash_seed': 123,
+            'allocator': 'malloc_debug',
+            'tracemalloc': 2,
+            'import_time': 1,
+            'show_ref_count': 1,
+            'show_alloc_count': 1,
+            'malloc_stats': 1,
+
+            'utf8_mode': 1,
+
+            'pycache_prefix': 'conf_pycache_prefix',
+            'program_name': './conf_program_name',
+            'program': 'conf_program',
+
+            'site_import': 0,
+            'bytes_warning': 1,
+            'inspect': 1,
+            'interactive': 1,
+            'optimization_level': 2,
+            'write_bytecode': 0,
+            'verbose': 1,
+            'quiet': 1,
+            'unbuffered_stdio': 1,
+            'user_site_directory': 0,
+            'faulthandler': 1,
+            '_check_hash_pycs_mode': 'always',
+        }
+        self.check_config("init_from_config", config)
+
+    def test_init_env(self):
+        config = {
+            'use_hash_seed': 1,
+            'hash_seed': 42,
+            'allocator': 'malloc_debug',
+            'tracemalloc': 2,
+            'import_time': 1,
+            'malloc_stats': 1,
+            'utf8_mode': 1,
+            'inspect': 1,
+            'optimization_level': 2,
+            'pycache_prefix': 'env_pycache_prefix',
+            'write_bytecode': 0,
+            'verbose': 1,
+            'unbuffered_stdio': 1,
+            'user_site_directory': 0,
+            'faulthandler': 1,
+            'dev_mode': 1,
+        }
+        self.check_config("init_env", config)
+
+    def test_init_dev_mode(self):
+        config = {
+            'dev_mode': 1,
+            'faulthandler': 1,
+            'allocator': 'debug',
+        }
+        self.check_config("init_dev_mode", config)
+
+    def test_init_isolated(self):
+        config = {
+            'isolated': 1,
+            'use_environment': 0,
+            'user_site_directory': 0,
+        }
+        self.check_config("init_isolated", config)
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/Programs/_testembed.c b/Programs/_testembed.c
index b1be682f7adc..f7e774975f36 100644
--- a/Programs/_testembed.c
+++ b/Programs/_testembed.c
@@ -292,6 +292,331 @@ static int test_initialize_pymain(void)
 }
 
 
+static void
+dump_config(void)
+{
+#define ASSERT_EQUAL(a, b) \
+    if ((a) != (b)) { \
+        printf("ERROR: %s != %s (%i != %i)\n", #a, #b, (a), (b)); \
+        exit(1); \
+    }
+#define ASSERT_STR_EQUAL(a, b) \
+    if ((a) == NULL || (b == NULL) || wcscmp((a), (b)) != 0) { \
+        printf("ERROR: %s != %s ('%ls' != '%ls')\n", #a, #b, (a), (b)); \
+        exit(1); \
+    }
+
+    PyInterpreterState *interp = PyThreadState_Get()->interp;
+    _PyCoreConfig *config = &interp->core_config;
+
+    printf("install_signal_handlers = %i\n", config->install_signal_handlers);
+
+    printf("use_environment = %i\n", config->use_environment);
+    ASSERT_EQUAL(config->use_environment, !Py_IgnoreEnvironmentFlag);
+
+    printf("use_hash_seed = %i\n", config->use_hash_seed);
+    printf("hash_seed = %lu\n", config->hash_seed);
+
+    printf("allocator = %s\n", config->allocator);
+
+    printf("dev_mode = %i\n", config->dev_mode);
+    printf("faulthandler = %i\n", config->faulthandler);
+    printf("tracemalloc = %i\n", config->tracemalloc);
+    printf("import_time = %i\n", config->import_time);
+    printf("show_ref_count = %i\n", config->show_ref_count);
+    printf("show_alloc_count = %i\n", config->show_alloc_count);
+    printf("dump_refs = %i\n", config->dump_refs);
+    printf("malloc_stats = %i\n", config->malloc_stats);
+
+    printf("coerce_c_locale = %i\n", config->coerce_c_locale);
+    printf("coerce_c_locale_warn = %i\n", config->coerce_c_locale_warn);
+    printf("utf8_mode = %i\n", config->utf8_mode);
+
+    printf("pycache_prefix = %ls\n", config->pycache_prefix);
+    printf("program_name = %ls\n", config->program_name);
+    ASSERT_STR_EQUAL(config->program_name, Py_GetProgramName());
+    /* FIXME: test argc/argv */
+    printf("program = %ls\n", config->program);
+    /* FIXME: test xoptions */
+    /* FIXME: test warnoptions */
+    /* FIXME: test module_search_path_env */
+    /* FIXME: test home */
+    /* FIXME: test module_search_paths */
+    /* FIXME: test executable */
+    /* FIXME: test prefix */
+    /* FIXME: test base_prefix */
+    /* FIXME: test exec_prefix */
+    /* FIXME: test base_exec_prefix */
+    /* FIXME: test dll_path */
+
+    printf("isolated = %i\n", config->isolated);
+    ASSERT_EQUAL(config->isolated, Py_IsolatedFlag);
+    printf("site_import = %i\n", config->site_import);
+    printf("bytes_warning = %i\n", config->bytes_warning);
+    printf("inspect = %i\n", config->inspect);
+    printf("interactive = %i\n", config->interactive);
+    printf("optimization_level = %i\n", config->optimization_level);
+    printf("debug = %i\n", config->debug);
+    printf("write_bytecode = %i\n", config->write_bytecode);
+    printf("verbose = %i\n", config->verbose);
+    ASSERT_EQUAL(config->verbose, Py_VerboseFlag);
+    printf("quiet = %i\n", config->quiet);
+    printf("user_site_directory = %i\n", config->user_site_directory);
+    printf("unbuffered_stdio = %i\n", config->unbuffered_stdio);
+    /* FIXME: test legacy_windows_fs_encoding */
+    /* FIXME: test legacy_windows_stdio */
+
+    printf("_install_importlib = %i\n", config->_install_importlib);
+    printf("_check_hash_pycs_mode = %s\n", config->_check_hash_pycs_mode);
+
+#undef ASSERT_EQUAL
+#undef ASSERT_STR_EQUAL
+}
+
+
+static int test_init_default_config(void)
+{
+    _testembed_Py_Initialize();
+    dump_config();
+    Py_Finalize();
+    return 0;
+}
+
+
+static int test_init_global_config(void)
+{
+    /* FIXME: test Py_IgnoreEnvironmentFlag */
+
+    putenv("PYTHONUTF8=0");
+    Py_UTF8Mode = 1;
+
+    /* Test initialization from global configuration variables (Py_xxx) */
+    Py_SetProgramName(L"./globalvar");
+
+    /* Py_IsolatedFlag is not tested */
+    Py_NoSiteFlag = 1;
+    Py_BytesWarningFlag = 1;
+
+    putenv("PYTHONINSPECT=");
+    Py_InspectFlag = 1;
+
+    putenv("PYTHONOPTIMIZE=0");
+    Py_InteractiveFlag = 1;
+
+    putenv("PYTHONDEBUG=0");
+    Py_OptimizeFlag = 2;
+
+    /* Py_DebugFlag is not tested */
+
+    putenv("PYTHONDONTWRITEBYTECODE=");
+    Py_DontWriteBytecodeFlag = 1;
+
+    putenv("PYTHONVERBOSE=0");
+    Py_VerboseFlag = 1;
+
+    Py_QuietFlag = 1;
+    Py_NoUserSiteDirectory = 1;
+
+    putenv("PYTHONUNBUFFERED=");
+    Py_UnbufferedStdioFlag = 1;
+
+    /* FIXME: test Py_LegacyWindowsFSEncodingFlag */
+    /* FIXME: test Py_LegacyWindowsStdioFlag */
+
+    /* _Py_CheckHashBasedPycsMode is not public, and so not tested */
+
+    Py_Initialize();
+    dump_config();
+    Py_Finalize();
+    return 0;
+}
+
+
+static int test_init_from_config(void)
+{
+    /* Test _Py_InitializeFromConfig() */
+    _PyCoreConfig config = _PyCoreConfig_INIT;
+    config.install_signal_handlers = 0;
+
+    /* FIXME: test use_environment */
+
+    putenv("PYTHONHASHSEED=42");
+    config.use_hash_seed = 1;
+    config.hash_seed = 123;
+
+    putenv("PYTHONMALLOC=malloc");
+    config.allocator = "malloc_debug";
+
+    /* dev_mode=1 is tested in test_init_dev_mode() */
+
+    putenv("PYTHONFAULTHANDLER=");
+    config.faulthandler = 1;
+
+    putenv("PYTHONTRACEMALLOC=0");
+    config.tracemalloc = 2;
+
+    putenv("PYTHONPROFILEIMPORTTIME=0");
+    config.import_time = 1;
+
+    config.show_ref_count = 1;
+    config.show_alloc_count = 1;
+    /* FIXME: test dump_refs: bpo-34223 */
+
+    putenv("PYTHONMALLOCSTATS=0");
+    config.malloc_stats = 1;
+
+    /* FIXME: test coerce_c_locale and coerce_c_locale_warn */
+
+    putenv("PYTHONUTF8=0");
+    Py_UTF8Mode = 0;
+    config.utf8_mode = 1;
+
+    putenv("PYTHONPYCACHEPREFIX=env_pycache_prefix");
+    config.pycache_prefix = L"conf_pycache_prefix";
+
+    Py_SetProgramName(L"./globalvar");
+    config.program_name = L"./conf_program_name";
+
+    /* FIXME: test argc/argv */
+    config.program = L"conf_program";
+    /* FIXME: test xoptions */
+    /* FIXME: test warnoptions */
+    /* FIXME: test module_search_path_env */
+    /* FIXME: test home */
+    /* FIXME: test path config: module_search_path .. dll_path */
+
+    putenv("PYTHONVERBOSE=0");
+    Py_VerboseFlag = 0;
+    config.verbose = 1;
+
+    Py_NoSiteFlag = 0;
+    config.site_import = 0;
+
+    Py_BytesWarningFlag = 0;
+    config.bytes_warning = 1;
+
+    putenv("PYTHONINSPECT=");
+    Py_InspectFlag = 0;
+    config.inspect = 1;
+
+    Py_InteractiveFlag = 0;
+    config.interactive = 1;
+
+    putenv("PYTHONOPTIMIZE=0");
+    Py_OptimizeFlag = 1;
+    config.optimization_level = 2;
+
+    /* FIXME: test debug */
+
+    putenv("PYTHONDONTWRITEBYTECODE=");
+    Py_DontWriteBytecodeFlag = 0;
+    config.write_bytecode = 0;
+
+    Py_QuietFlag = 0;
+    config.quiet = 1;
+
+    putenv("PYTHONUNBUFFERED=");
+    Py_UnbufferedStdioFlag = 0;
+    config.unbuffered_stdio = 1;
+
+    putenv("PYTHONNOUSERSITE=");
+    Py_NoUserSiteDirectory = 0;
+    config.user_site_directory = 0;
+
+    config._check_hash_pycs_mode = "always";
+
+    _PyInitError err = _Py_InitializeFromConfig(&config);
+    /* Don't call _PyCoreConfig_Clear() since all strings are static */
+    if (_Py_INIT_FAILED(err)) {
+        _Py_FatalInitError(err);
+    }
+    dump_config();
+    Py_Finalize();
+    return 0;
+}
+
+
+static void test_init_env_putenvs(void)
+{
+    putenv("PYTHONHASHSEED=42");
+    putenv("PYTHONMALLOC=malloc_debug");
+    putenv("PYTHONTRACEMALLOC=2");
+    putenv("PYTHONPROFILEIMPORTTIME=1");
+    putenv("PYTHONMALLOCSTATS=1");
+    putenv("PYTHONUTF8=1");
+    putenv("PYTHONVERBOSE=1");
+    putenv("PYTHONINSPECT=1");
+    putenv("PYTHONOPTIMIZE=2");
+    putenv("PYTHONDONTWRITEBYTECODE=1");
+    putenv("PYTHONUNBUFFERED=1");
+    putenv("PYTHONPYCACHEPREFIX=env_pycache_prefix");
+    putenv("PYTHONNOUSERSITE=1");
+    putenv("PYTHONFAULTHANDLER=1");
+    putenv("PYTHONDEVMODE=1");
+    /* FIXME: test PYTHONWARNINGS */
+    /* FIXME: test PYTHONEXECUTABLE */
+    /* FIXME: test PYTHONHOME */
+    /* FIXME: test PYTHONDEBUG */
+    /* FIXME: test PYTHONDUMPREFS */
+    /* FIXME: test PYTHONCOERCECLOCALE */
+    /* FIXME: test PYTHONPATH */
+}
+
+
+static int test_init_env(void)
+{
+    /* Test initialization from environment variables */
+    Py_IgnoreEnvironmentFlag = 0;
+    test_init_env_putenvs();
+    _testembed_Py_Initialize();
+    dump_config();
+    Py_Finalize();
+    return 0;
+}
+
+
+static int test_init_isolated(void)
+{
+    /* Test _PyCoreConfig.isolated=1 */
+    _PyCoreConfig config = _PyCoreConfig_INIT;
+
+    /* Set coerce_c_locale and utf8_mode to not depend on the locale */
+    config.coerce_c_locale = 0;
+    config.utf8_mode = 0;
+    /* Use path starting with "./" avoids a search along the PATH */
+    config.program_name = L"./_testembed";
+
+    Py_IsolatedFlag = 0;
+    config.isolated = 1;
+
+    test_init_env_putenvs();
+    _PyInitError err = _Py_InitializeFromConfig(&config);
+    if (_Py_INIT_FAILED(err)) {
+        _Py_FatalInitError(err);
+    }
+    dump_config();
+    Py_Finalize();
+    return 0;
+}
+
+
+static int test_init_dev_mode(void)
+{
+    _PyCoreConfig config = _PyCoreConfig_INIT;
+    putenv("PYTHONFAULTHANDLER=");
+    putenv("PYTHONMALLOC=");
+    config.dev_mode = 1;
+    config.program_name = L"./_testembed";
+    _PyInitError err = _Py_InitializeFromConfig(&config);
+    if (_Py_INIT_FAILED(err)) {
+        _Py_FatalInitError(err);
+    }
+    dump_config();
+    Py_Finalize();
+    return 0;
+}
+
+
 /* *********************************************************
  * List of test cases and the function that implements it.
  *
@@ -318,6 +643,12 @@ static struct TestCase TestCases[] = {
     { "bpo20891", test_bpo20891 },
     { "initialize_twice", test_initialize_twice },
     { "initialize_pymain", test_initialize_pymain },
+    { "init_default_config", test_init_default_config },
+    { "init_global_config", test_init_global_config },
+    { "init_from_config", test_init_from_config },
+    { "init_env", test_init_env },
+    { "init_dev_mode", test_init_dev_mode },
+    { "init_isolated", test_init_isolated },
     { NULL, NULL }
 };
 



More information about the Python-checkins mailing list