[3.7] bpo-34247: Fix Python 3.7 initialization (#8659)

* -X dev: it is now possible to override the memory allocator using
  PYTHONMALLOC even if the developer mode is enabled.
* Add _Py_InitializeFromConfig()
* Add _Py_Initialize_ReadEnvVars() to set global configuration
  variables from environment variables
* Fix the code to initialize Python: Py_Initialize() now also reads
  environment variables
* _Py_InitializeCore() can now be called twice: the second call
  only replaces the configuration.
* Write unit tests on Py_Initialize() and the different ways to
  configure Python
* The isolated mode now always sets Py_IgnoreEnvironmentFlag and
  Py_NoUserSiteDirectory to 1.
* pymain_read_conf() now saves/restores the configuration
  if the encoding changed
diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py
index d8a96c4..95cdc8d 100644
--- a/Lib/test/test_cmd_line.py
+++ b/Lib/test/test_cmd_line.py
@@ -523,9 +523,7 @@
         env = dict(os.environ)
         env.pop('PYTHONWARNINGS', None)
         env.pop('PYTHONDEVMODE', None)
-        # Force malloc() to disable the debug hooks which are enabled
-        # by default for Python compiled in debug mode
-        env['PYTHONMALLOC'] = 'malloc'
+        env.pop('PYTHONMALLOC', None)
 
         if xdev:
             args = (sys.executable, '-X', 'dev', *args)
diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py
index 024c3f9..2927458 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 @@
                 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,149 @@
         self.assertEqual(err, '')
 
 
+class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
+    maxDiff = 4096
+    DEFAULT_CONFIG = {
+        'install_signal_handlers': 1,
+        'Py_IgnoreEnvironmentFlag': 0,
+        '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,
+
+        'program_name': './_testembed',
+        'argc': 0,
+        'argv': '[]',
+        'program': '(null)',
+
+        'Py_IsolatedFlag': 0,
+        'Py_NoSiteFlag': 0,
+        'Py_BytesWarningFlag': 0,
+        'Py_InspectFlag': 0,
+        'Py_InteractiveFlag': 0,
+        'Py_OptimizeFlag': 0,
+        'Py_DebugFlag': 0,
+        'Py_DontWriteBytecodeFlag': 0,
+        'Py_VerboseFlag': 0,
+        'Py_QuietFlag': 0,
+        'Py_NoUserSiteDirectory': 0,
+        'Py_UnbufferedStdioFlag': 0,
+
+        '_disable_importlib': 0,
+        'Py_FrozenFlag': 0,
+    }
+
+    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',
+            'Py_NoSiteFlag': 1,
+            'Py_BytesWarningFlag': 1,
+            'Py_InspectFlag': 1,
+            'Py_InteractiveFlag': 1,
+            'Py_OptimizeFlag': 2,
+            'Py_DontWriteBytecodeFlag': 1,
+            'Py_VerboseFlag': 1,
+            'Py_QuietFlag': 1,
+            'Py_UnbufferedStdioFlag': 1,
+            'utf8_mode': 1,
+            'Py_NoUserSiteDirectory': 1,
+            'Py_FrozenFlag': 1,
+        }
+        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,
+
+            'program_name': './conf_program_name',
+            'program': 'conf_program',
+
+            'faulthandler': 1,
+        }
+        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,
+            'Py_InspectFlag': 1,
+            'Py_OptimizeFlag': 2,
+            'Py_DontWriteBytecodeFlag': 1,
+            'Py_VerboseFlag': 1,
+            'Py_UnbufferedStdioFlag': 1,
+            'Py_NoUserSiteDirectory': 1,
+            '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 = {
+            'Py_IsolatedFlag': 1,
+            'Py_IgnoreEnvironmentFlag': 1,
+            'Py_NoUserSiteDirectory': 1,
+        }
+        self.check_config("init_isolated", config)
+
+
 if __name__ == "__main__":
     unittest.main()