Skip to content

Commit 6bf992a

Browse files
authored
bpo-32030: Add pymain_get_global_config() (#4735)
* Py_Main() now starts by reading Py_xxx configuration variables to only work on its own private structure, and then later writes back the configuration into these variables. * Replace Py_GETENV() with pymain_get_env_var() which ignores empty variables. * Add _PyCoreConfig.dump_refs * Add _PyCoreConfig.malloc_stats * _PyObject_DebugMallocStats() is now responsible to check if debug hooks are installed. The function returns 1 if stats were written, or 0 if the hooks are disabled. Mark _PyMem_PymallocEnabled() as static.
1 parent 672b6ba commit 6bf992a

File tree

7 files changed

+142
-77
lines changed

7 files changed

+142
-77
lines changed

Include/objimpl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ PyAPI_FUNC(Py_ssize_t) _Py_GetAllocatedBlocks(void);
109109
/* Macros */
110110
#ifdef WITH_PYMALLOC
111111
#ifndef Py_LIMITED_API
112-
PyAPI_FUNC(void) _PyObject_DebugMallocStats(FILE *out);
112+
PyAPI_FUNC(int) _PyObject_DebugMallocStats(FILE *out);
113113
#endif /* #ifndef Py_LIMITED_API */
114114
#endif
115115

Include/pymem.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,6 @@ PyAPI_FUNC(int) _PyMem_SetupAllocators(const char *opt);
2424
/* Try to get the allocators name set by _PyMem_SetupAllocators(). */
2525
PyAPI_FUNC(const char*) _PyMem_GetAllocatorsName(void);
2626

27-
#ifdef WITH_PYMALLOC
28-
PyAPI_FUNC(int) _PyMem_PymallocEnabled(void);
29-
#endif
30-
3127
/* Track an allocated memory block in the tracemalloc module.
3228
Return 0 on success, return -1 on error (failed to allocate memory to store
3329
the trace).

Include/pystate.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ typedef struct {
3636
int import_time; /* -X importtime */
3737
int show_ref_count; /* -X showrefcount */
3838
int show_alloc_count; /* -X showalloccount */
39+
int dump_refs; /* PYTHONDUMPREFS */
40+
int malloc_stats; /* PYTHONMALLOCSTATS */
3941
} _PyCoreConfig;
4042

4143
#define _PyCoreConfig_INIT (_PyCoreConfig){.use_hash_seed = -1}
@@ -111,7 +113,7 @@ typedef struct _is {
111113
PyObject *after_forkers_child;
112114
#endif
113115
} PyInterpreterState;
114-
#endif
116+
#endif /* !Py_LIMITED_API */
115117

116118

117119
/* State unique per thread */
@@ -133,7 +135,7 @@ typedef int (*Py_tracefunc)(PyObject *, struct _frame *, int, PyObject *);
133135
#define PyTrace_C_EXCEPTION 5
134136
#define PyTrace_C_RETURN 6
135137
#define PyTrace_OPCODE 7
136-
#endif
138+
#endif /* Py_LIMITED_API */
137139

138140
#ifdef Py_LIMITED_API
139141
typedef struct _ts PyThreadState;
@@ -238,7 +240,7 @@ typedef struct _ts {
238240
/* XXX signal handlers should also be here */
239241

240242
} PyThreadState;
241-
#endif
243+
#endif /* !Py_LIMITED_API */
242244

243245

244246
PyAPI_FUNC(PyInterpreterState *) PyInterpreterState_New(void);
@@ -363,7 +365,7 @@ PyAPI_FUNC(int) PyGILState_Check(void);
363365
Return NULL before _PyGILState_Init() is called and after _PyGILState_Fini()
364366
is called. */
365367
PyAPI_FUNC(PyInterpreterState *) _PyGILState_GetInterpreterStateUnsafe(void);
366-
#endif
368+
#endif /* !Py_LIMITED_API */
367369

368370

369371
/* The implementation of sys._current_frames() Returns a dict mapping

Modules/main.c

Lines changed: 113 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,8 @@ pymain_get_env_var(const char *name)
162162
static void
163163
pymain_run_startup(PyCompilerFlags *cf)
164164
{
165-
char *startup = Py_GETENV("PYTHONSTARTUP");
166-
if (startup == NULL || startup[0] == '\0') {
165+
char *startup = pymain_get_env_var("PYTHONSTARTUP");
166+
if (startup == NULL) {
167167
return;
168168
}
169169

@@ -377,23 +377,28 @@ typedef struct {
377377
wchar_t *command; /* -c argument */
378378
wchar_t *module; /* -m argument */
379379
_Py_OptList warning_options; /* -W options */
380-
PyObject *extra_options; /* -X options */
381380
int print_help; /* -h, -? options */
382381
int print_version; /* -V option */
383-
int bytes_warning; /* Py_BytesWarningFlag */
384-
int debug; /* Py_DebugFlag */
385-
int inspect; /* Py_InspectFlag */
386-
int interactive; /* Py_InteractiveFlag */
387-
int isolated; /* Py_IsolatedFlag */
388-
int optimization_level; /* Py_OptimizeFlag */
389-
int dont_write_bytecode; /* Py_DontWriteBytecodeFlag */
390-
int no_user_site_directory; /* Py_NoUserSiteDirectory */
391-
int no_site_import; /* Py_NoSiteFlag */
392-
int use_unbuffered_io; /* Py_UnbufferedStdioFlag */
393-
int verbosity; /* Py_VerboseFlag */
394-
int quiet_flag; /* Py_QuietFlag */
382+
int bytes_warning; /* Py_BytesWarningFlag, -b */
383+
int debug; /* Py_DebugFlag, -b, PYTHONDEBUG */
384+
int inspect; /* Py_InspectFlag, -i, PYTHONINSPECT */
385+
int interactive; /* Py_InteractiveFlag, -i */
386+
int isolated; /* Py_IsolatedFlag, -I */
387+
int optimization_level; /* Py_OptimizeFlag, -O, PYTHONOPTIMIZE */
388+
int dont_write_bytecode; /* Py_DontWriteBytecodeFlag, -B, PYTHONDONTWRITEBYTECODE */
389+
int no_user_site_directory; /* Py_NoUserSiteDirectory, -I, -s, PYTHONNOUSERSITE */
390+
int no_site_import; /* Py_NoSiteFlag, -S */
391+
int use_unbuffered_io; /* Py_UnbufferedStdioFlag, -u, PYTHONUNBUFFERED */
392+
int verbosity; /* Py_VerboseFlag, -v, PYTHONVERBOSE */
393+
int quiet_flag; /* Py_QuietFlag, -q */
395394
int skip_first_line; /* -x option */
396395
_Py_OptList xoptions; /* -X options */
396+
#ifdef MS_WINDOWS
397+
int legacy_windows_fs_encoding; /* Py_LegacyWindowsFSEncodingFlag,
398+
PYTHONLEGACYWINDOWSFSENCODING */
399+
int legacy_windows_stdio; /* Py_LegacyWindowsStdioFlag,
400+
PYTHONLEGACYWINDOWSSTDIO */
401+
#endif
397402
} _Py_CommandLineDetails;
398403

399404
/* Structure used by Py_Main() to pass data to subfunctions */
@@ -695,19 +700,6 @@ pymain_parse_cmdline_impl(_PyMain *pymain)
695700
}
696701

697702

698-
static void
699-
maybe_set_flag(int *flag, int value)
700-
{
701-
/* Helper to set flag variables from command line options
702-
* - uses the higher of the two values if they're both set
703-
* - otherwise leaves the flag unset
704-
*/
705-
if (*flag < value) {
706-
*flag = value;
707-
}
708-
}
709-
710-
711703
static int
712704
pymain_add_xoptions(_PyMain *pymain)
713705
{
@@ -790,9 +782,8 @@ pymain_warnings_envvar(_PyMain *pymain)
790782
PyMem_RawFree(buf);
791783
}
792784
#else
793-
char *p;
794-
795-
if ((p = Py_GETENV("PYTHONWARNINGS")) && *p != '\0') {
785+
char *p = pymain_get_env_var("PYTHONWARNINGS");
786+
if (p != NULL) {
796787
char *buf, *oldloc;
797788

798789
/* settle for strtok here as there's no one standard
@@ -885,7 +876,6 @@ config_get_program_name(_PyMainInterpreterConfig *config)
885876
}
886877

887878
#ifdef __APPLE__
888-
char *p;
889879
/* On MacOS X, when the Python interpreter is embedded in an
890880
application bundle, it gets executed by a bootstrapping script
891881
that does os.execve() with an argv[0] that's different from the
@@ -895,7 +885,8 @@ config_get_program_name(_PyMainInterpreterConfig *config)
895885
so the actual executable path is passed in an environment variable.
896886
See Lib/plat-mac/bundlebuiler.py for details about the bootstrap
897887
script. */
898-
if ((p = Py_GETENV("PYTHONEXECUTABLE")) && *p != '\0') {
888+
char *p = pymain_get_env_var("PYTHONEXECUTABLE");
889+
if (p != NULL) {
899890
size_t len;
900891
wchar_t* program_name = Py_DecodeLocale(p, &len);
901892
if (program_name == NULL) {
@@ -1015,25 +1006,76 @@ pymain_set_argv(_PyMain *pymain)
10151006
}
10161007

10171008

1009+
static void
1010+
pymain_get_flag(int flag, int *value)
1011+
{
1012+
if (flag) {
1013+
*value = flag;
1014+
}
1015+
}
1016+
1017+
static void
1018+
pymain_set_flag(int *flag, int value)
1019+
{
1020+
/* Helper to set flag variables from command line options
1021+
* - uses the higher of the two values if they're both set
1022+
* - otherwise leaves the flag unset
1023+
*/
1024+
if (*flag < value) {
1025+
*flag = value;
1026+
}
1027+
}
1028+
1029+
1030+
/* Get Py_xxx global configuration variables */
1031+
static void
1032+
pymain_get_global_config(_PyMain *pymain)
1033+
{
1034+
_Py_CommandLineDetails *cmdline = &pymain->cmdline;
1035+
pymain_get_flag(Py_BytesWarningFlag, &cmdline->bytes_warning);
1036+
pymain_get_flag(Py_DebugFlag, &cmdline->debug);
1037+
pymain_get_flag(Py_InspectFlag, &cmdline->inspect);
1038+
pymain_get_flag(Py_InteractiveFlag, &cmdline->interactive);
1039+
pymain_get_flag(Py_IsolatedFlag, &cmdline->isolated);
1040+
pymain_get_flag(Py_OptimizeFlag, &cmdline->optimization_level);
1041+
pymain_get_flag(Py_DontWriteBytecodeFlag, &cmdline->dont_write_bytecode);
1042+
pymain_get_flag(Py_NoUserSiteDirectory, &cmdline->no_user_site_directory);
1043+
pymain_get_flag(Py_NoSiteFlag, &cmdline->no_site_import);
1044+
pymain_get_flag(Py_UnbufferedStdioFlag, &cmdline->use_unbuffered_io);
1045+
pymain_get_flag(Py_VerboseFlag, &cmdline->verbosity);
1046+
pymain_get_flag(Py_QuietFlag, &cmdline->quiet_flag);
1047+
#ifdef MS_WINDOWS
1048+
pymain_get_flag(Py_LegacyWindowsFSEncodingFlag, &cmdline->legacy_windows_fs_encoding);
1049+
pymain_get_flag(Py_LegacyWindowsStdioFlag, &cmdline->legacy_windows_stdio);
1050+
#endif
1051+
1052+
pymain_get_flag(Py_IgnoreEnvironmentFlag, &pymain->core_config.ignore_environment);
1053+
}
1054+
1055+
10181056
/* Set Py_XXX global configuration variables */
10191057
static void
10201058
pymain_set_global_config(_PyMain *pymain)
10211059
{
10221060
_Py_CommandLineDetails *cmdline = &pymain->cmdline;
1023-
maybe_set_flag(&Py_BytesWarningFlag, cmdline->bytes_warning);
1024-
maybe_set_flag(&Py_DebugFlag, cmdline->debug);
1025-
maybe_set_flag(&Py_InspectFlag, cmdline->inspect);
1026-
maybe_set_flag(&Py_InteractiveFlag, cmdline->interactive);
1027-
maybe_set_flag(&Py_IsolatedFlag, cmdline->isolated);
1028-
maybe_set_flag(&Py_OptimizeFlag, cmdline->optimization_level);
1029-
maybe_set_flag(&Py_DontWriteBytecodeFlag, cmdline->dont_write_bytecode);
1030-
maybe_set_flag(&Py_NoUserSiteDirectory, cmdline->no_user_site_directory);
1031-
maybe_set_flag(&Py_NoSiteFlag, cmdline->no_site_import);
1032-
maybe_set_flag(&Py_UnbufferedStdioFlag, cmdline->use_unbuffered_io);
1033-
maybe_set_flag(&Py_VerboseFlag, cmdline->verbosity);
1034-
maybe_set_flag(&Py_QuietFlag, cmdline->quiet_flag);
1035-
1036-
maybe_set_flag(&Py_IgnoreEnvironmentFlag, pymain->core_config.ignore_environment);
1061+
pymain_set_flag(&Py_BytesWarningFlag, cmdline->bytes_warning);
1062+
pymain_set_flag(&Py_DebugFlag, cmdline->debug);
1063+
pymain_set_flag(&Py_InspectFlag, cmdline->inspect);
1064+
pymain_set_flag(&Py_InteractiveFlag, cmdline->interactive);
1065+
pymain_set_flag(&Py_IsolatedFlag, cmdline->isolated);
1066+
pymain_set_flag(&Py_OptimizeFlag, cmdline->optimization_level);
1067+
pymain_set_flag(&Py_DontWriteBytecodeFlag, cmdline->dont_write_bytecode);
1068+
pymain_set_flag(&Py_NoUserSiteDirectory, cmdline->no_user_site_directory);
1069+
pymain_set_flag(&Py_NoSiteFlag, cmdline->no_site_import);
1070+
pymain_set_flag(&Py_UnbufferedStdioFlag, cmdline->use_unbuffered_io);
1071+
pymain_set_flag(&Py_VerboseFlag, cmdline->verbosity);
1072+
pymain_set_flag(&Py_QuietFlag, cmdline->quiet_flag);
1073+
#ifdef MS_WINDOWS
1074+
pymain_set_flag(&Py_LegacyWindowsFSEncodingFlag, cmdline->legacy_windows_fs_encoding);
1075+
pymain_set_flag(&Py_LegacyWindowsStdioFlag, cmdline->legacy_windows_stdio);
1076+
#endif
1077+
1078+
pymain_set_flag(&Py_IgnoreEnvironmentFlag, pymain->core_config.ignore_environment);
10371079
}
10381080

10391081

@@ -1330,24 +1372,25 @@ pymain_set_flag_from_env(int *flag, const char *name)
13301372
static void
13311373
pymain_set_flags_from_env(_PyMain *pymain)
13321374
{
1333-
pymain_set_flag_from_env(&Py_DebugFlag,
1375+
_Py_CommandLineDetails *cmdline = &pymain->cmdline;
1376+
pymain_set_flag_from_env(&cmdline->debug,
13341377
"PYTHONDEBUG");
1335-
pymain_set_flag_from_env(&Py_VerboseFlag,
1378+
pymain_set_flag_from_env(&cmdline->verbosity,
13361379
"PYTHONVERBOSE");
1337-
pymain_set_flag_from_env(&Py_OptimizeFlag,
1380+
pymain_set_flag_from_env(&cmdline->optimization_level,
13381381
"PYTHONOPTIMIZE");
1339-
pymain_set_flag_from_env(&Py_InspectFlag,
1382+
pymain_set_flag_from_env(&cmdline->inspect,
13401383
"PYTHONINSPECT");
1341-
pymain_set_flag_from_env(&Py_DontWriteBytecodeFlag,
1384+
pymain_set_flag_from_env(&cmdline->dont_write_bytecode,
13421385
"PYTHONDONTWRITEBYTECODE");
1343-
pymain_set_flag_from_env(&Py_NoUserSiteDirectory,
1386+
pymain_set_flag_from_env(&cmdline->no_user_site_directory,
13441387
"PYTHONNOUSERSITE");
1345-
pymain_set_flag_from_env(&Py_UnbufferedStdioFlag,
1388+
pymain_set_flag_from_env(&cmdline->use_unbuffered_io,
13461389
"PYTHONUNBUFFERED");
13471390
#ifdef MS_WINDOWS
1348-
pymain_set_flag_from_env(&Py_LegacyWindowsFSEncodingFlag,
1391+
pymain_set_flag_from_env(&cmdline->legacy_windows_fs_encoding,
13491392
"PYTHONLEGACYWINDOWSFSENCODING");
1350-
pymain_set_flag_from_env(&Py_LegacyWindowsStdioFlag,
1393+
pymain_set_flag_from_env(&cmdline->legacy_windows_stdio,
13511394
"PYTHONLEGACYWINDOWSSTDIO");
13521395
#endif
13531396
}
@@ -1485,7 +1528,7 @@ pymain_parse_envvars(_PyMain *pymain)
14851528
return -1;
14861529
}
14871530

1488-
core_config->allocator = Py_GETENV("PYTHONMALLOC");
1531+
core_config->allocator = pymain_get_env_var("PYTHONMALLOC");
14891532

14901533
/* -X options */
14911534
if (pymain_get_xoption(pymain, L"showrefcount")) {
@@ -1514,6 +1557,14 @@ pymain_parse_envvars(_PyMain *pymain)
15141557
core_config->faulthandler = 1;
15151558
core_config->allocator = "debug";
15161559
}
1560+
if (pymain_get_env_var("PYTHONDUMPREFS")) {
1561+
pymain->core_config.dump_refs = 1;
1562+
}
1563+
if (pymain_get_env_var("PYTHONMALLOCSTATS")) {
1564+
pymain->core_config.malloc_stats = 1;
1565+
}
1566+
1567+
15171568
return 0;
15181569
}
15191570

@@ -1535,6 +1586,7 @@ pymain_parse_cmdline_envvars_impl(_PyMain *pymain)
15351586
return 1;
15361587
}
15371588

1589+
/* Set Py_IgnoreEnvironmentFlag needed by Py_GETENV() */
15381590
pymain_set_global_config(pymain);
15391591

15401592
if (pymain_parse_envvars(pymain) < 0) {
@@ -1568,6 +1620,8 @@ pymain_parse_cmdline_envvars(_PyMain *pymain)
15681620
static int
15691621
pymain_init_python(_PyMain *pymain)
15701622
{
1623+
pymain_set_global_config(pymain);
1624+
15711625
pymain_init_stdio(pymain);
15721626

15731627
pymain->err = _Py_InitializeCore(&pymain->core_config);
@@ -1641,6 +1695,8 @@ pymain_impl(_PyMain *pymain)
16411695
return -1;
16421696
}
16431697

1698+
pymain_get_global_config(pymain);
1699+
16441700
res = pymain_parse_cmdline_envvars(pymain);
16451701
if (res < 0) {
16461702
return -1;

Objects/obmalloc.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ _PyMem_DebugEnabled(void)
379379
return (_PyObject.malloc == _PyMem_DebugMalloc);
380380
}
381381

382-
int
382+
static int
383383
_PyMem_PymallocEnabled(void)
384384
{
385385
if (_PyMem_DebugEnabled()) {
@@ -2467,10 +2467,17 @@ pool_is_in_list(const poolp target, poolp list)
24672467
/* Print summary info to "out" about the state of pymalloc's structures.
24682468
* In Py_DEBUG mode, also perform some expensive internal consistency
24692469
* checks.
2470+
*
2471+
* Return 0 if the memory debug hooks are not installed or no statistics was
2472+
* writen into out, return 1 otherwise.
24702473
*/
2471-
void
2474+
int
24722475
_PyObject_DebugMallocStats(FILE *out)
24732476
{
2477+
if (!_PyMem_PymallocEnabled()) {
2478+
return 0;
2479+
}
2480+
24742481
uint i;
24752482
const uint numclasses = SMALL_REQUEST_THRESHOLD >> ALIGNMENT_SHIFT;
24762483
/* # of pools, allocated blocks, and free blocks per class index */
@@ -2603,6 +2610,7 @@ _PyObject_DebugMallocStats(FILE *out)
26032610
total += printone(out, "# bytes lost to quantization", quantization);
26042611
total += printone(out, "# bytes lost to arena alignment", arena_alignment);
26052612
(void)printone(out, "Total", total);
2613+
return 1;
26062614
}
26072615

26082616
#endif /* #ifdef WITH_PYMALLOC */

0 commit comments

Comments
 (0)