Repository: python/cpython
Pull request: gh-106789: avoid importing pprint from sysconfig
Fixes #106789.

<!-- gh-issue-number: gh-106789 -->
* Issue: gh-106789
<!-- /gh-issue-number -->


Problem statement:
sysconfig imports pprint which imports the world
sysconfig imports pprint in order to print the config dict to a file. But this dict contains only strings and ints, so pprint is overkill.

The problem with importing pprint is that it imports dataclasses, which imports inspect, which imports dis which imports opcode which tries to import _opcode which does not exist in early stages of the build when sysconfig is used. So opcode imports _opcode in a try-except and ignores import errors. Since sysconfig doesn't need anything from _opcode, the partially constructed opcode module suffices. But this is flaky and hard to reason about (it works because it works).   It would be better if sysconfig didn't import so much.



<!-- gh-linked-prs -->
### Linked PRs
* gh-106790
<!-- /gh-linked-prs -->

Hints from issue discussion:

Commit message: gh-106789: avoid importing pprint from sysconfig

Patch to review:
diff --git a/Lib/opcode.py b/Lib/opcode.py
index bc885051c6454e..db9b7dbee3b2c9 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -8,24 +8,10 @@
            "haslocal", "hascompare", "hasfree", "hasexc", "opname", "opmap",
            "HAVE_ARGUMENT", "EXTENDED_ARG"]
 
-# It's a chicken-and-egg I'm afraid:
-# We're imported before _opcode's made.
-# With exception unheeded
-# (stack_effect is not needed)
-# Both our chickens and eggs are allayed.
-#     --Larry Hastings, 2013/11/23
-
-try:
-    from _opcode import stack_effect
-    __all__.append('stack_effect')
-except ImportError:
-    pass
-
-# _opcode_metadata may not be ready during early stages of the build
-try:
-    from _opcode_metadata import _specializations, _specialized_instructions
-except ModuleNotFoundError:
-    pass
+from _opcode import stack_effect
+__all__.append('stack_effect')
+
+from _opcode_metadata import _specializations, _specialized_instructions
 
 cmp_op = ('<', '<=', '==', '!=', '>', '>=')
 
diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py
index 122d441bd19f5e..eba6535b78866d 100644
--- a/Lib/sysconfig.py
+++ b/Lib/sysconfig.py
@@ -465,10 +465,14 @@ def _get_sysconfigdata_name():
         f'_sysconfigdata_{sys.abiflags}_{sys.platform}_{multiarch}',
     )
 
+def _print_config_dict(d, stream):
+    print (" {", file=stream)
+    for k, v in sorted(d.items()):
+        print(f"    {k!r}: {v!r},", file=stream)
+    print ("}", file=stream)
 
 def _generate_posix_vars():
     """Generate the Python module containing build-time variables."""
-    import pprint
     vars = {}
     # load the installed Makefile:
     makefile = get_makefile_filename()
@@ -523,7 +527,7 @@ def _generate_posix_vars():
         f.write('# system configuration generated and used by'
                 ' the sysconfig module\n')
         f.write('build_time_vars = ')
-        pprint.pprint(vars, stream=f)
+        _print_config_dict(vars, stream=f)
 
     # Create file used for sys.path fixup -- see Modules/getpath.c
     with open('pybuilddir.txt', 'w', encoding='utf8') as f:


Code review comments:
diff_hunk: @@ -465,10 +465,14 @@ def _get_sysconfigdata_name():
         f'_sysconfigdata_{sys.abiflags}_{sys.platform}_{multiarch}',
     )
 
+def _print_config_dict(d, stream):
+    print (" {", file=stream)
path: Lib/sysconfig.py
text: @user1:
```suggestion
    print ("{", file=stream)
```

One space is written already via `f.write('build_time_vars = ')`.