summaryrefslogtreecommitdiffstats
path: root/Python
diff options
context:
space:
mode:
authorDonghee Na <donghee.na@python.org>2023-10-10 10:00:09 (GMT)
committerGitHub <noreply@github.com>2023-10-10 10:00:09 (GMT)
commit0362cbf908aff2b87298f8a9422e7b368f890071 (patch)
tree7d5ae0e1f44801fe2102cb783938c60ef99b7ae4 /Python
parent5aa62a8de15212577a13966710b3aede46e93824 (diff)
downloadcpython-0362cbf908aff2b87298f8a9422e7b368f890071.zip
cpython-0362cbf908aff2b87298f8a9422e7b368f890071.tar.gz
cpython-0362cbf908aff2b87298f8a9422e7b368f890071.tar.bz2
gh-109595: Add -Xcpu_count=<n> cmdline for container users (#109667)
--------- Co-authored-by: Victor Stinner <vstinner@python.org> Co-authored-by: Gregory P. Smith [Google LLC] <greg@krypto.org>
Diffstat (limited to 'Python')
-rw-r--r--Python/clinic/sysmodule.c.h30
-rw-r--r--Python/initconfig.c58
-rw-r--r--Python/sysmodule.c15
3 files changed, 101 insertions, 2 deletions
diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h
index 30691c3..06105e2 100644
--- a/Python/clinic/sysmodule.c.h
+++ b/Python/clinic/sysmodule.c.h
@@ -1380,6 +1380,34 @@ exit:
return return_value;
}
+PyDoc_STRVAR(sys__get_cpu_count_config__doc__,
+"_get_cpu_count_config($module, /)\n"
+"--\n"
+"\n"
+"Private function for getting PyConfig.cpu_count");
+
+#define SYS__GET_CPU_COUNT_CONFIG_METHODDEF \
+ {"_get_cpu_count_config", (PyCFunction)sys__get_cpu_count_config, METH_NOARGS, sys__get_cpu_count_config__doc__},
+
+static int
+sys__get_cpu_count_config_impl(PyObject *module);
+
+static PyObject *
+sys__get_cpu_count_config(PyObject *module, PyObject *Py_UNUSED(ignored))
+{
+ PyObject *return_value = NULL;
+ int _return_value;
+
+ _return_value = sys__get_cpu_count_config_impl(module);
+ if ((_return_value == -1) && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = PyLong_FromLong((long)_return_value);
+
+exit:
+ return return_value;
+}
+
#ifndef SYS_GETWINDOWSVERSION_METHODDEF
#define SYS_GETWINDOWSVERSION_METHODDEF
#endif /* !defined(SYS_GETWINDOWSVERSION_METHODDEF) */
@@ -1423,4 +1451,4 @@ exit:
#ifndef SYS_GETANDROIDAPILEVEL_METHODDEF
#define SYS_GETANDROIDAPILEVEL_METHODDEF
#endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */
-/*[clinic end generated code: output=549bb1f92a15f916 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=3a7d3fbbcb281c22 input=a9049054013a1b77]*/
diff --git a/Python/initconfig.c b/Python/initconfig.c
index 6b76b4d..f7eb853 100644
--- a/Python/initconfig.c
+++ b/Python/initconfig.c
@@ -92,6 +92,7 @@ static const PyConfigSpec PYCONFIG_SPEC[] = {
SPEC(use_frozen_modules, UINT),
SPEC(safe_path, UINT),
SPEC(int_max_str_digits, INT),
+ SPEC(cpu_count, INT),
SPEC(pathconfig_warnings, UINT),
SPEC(program_name, WSTR),
SPEC(pythonpath_env, WSTR_OPT),
@@ -229,7 +230,11 @@ The following implementation-specific options are available:\n\
\n\
-X int_max_str_digits=number: limit the size of int<->str conversions.\n\
This helps avoid denial of service attacks when parsing untrusted data.\n\
- The default is sys.int_info.default_max_str_digits. 0 disables."
+ The default is sys.int_info.default_max_str_digits. 0 disables.\n\
+\n\
+-X cpu_count=[n|default]: Override the return value of os.cpu_count(),\n\
+ os.process_cpu_count(), and multiprocessing.cpu_count(). This can help users who need\n\
+ to limit resources in a container."
#ifdef Py_STATS
"\n\
@@ -267,6 +272,8 @@ static const char usage_envvars[] =
" locale coercion and locale compatibility warnings on stderr.\n"
"PYTHONBREAKPOINT: if this variable is set to 0, it disables the default\n"
" debugger. It can be set to the callable of your debugger of choice.\n"
+"PYTHON_CPU_COUNT: Overrides the return value of os.process_cpu_count(),\n"
+" os.cpu_count(), and multiprocessing.cpu_count() if set to a positive integer.\n"
"PYTHONDEVMODE: enable the development mode.\n"
"PYTHONPYCACHEPREFIX: root directory for bytecode cache (pyc) files.\n"
"PYTHONWARNDEFAULTENCODING: enable opt-in EncodingWarning for 'encoding=None'.\n"
@@ -732,6 +739,8 @@ config_check_consistency(const PyConfig *config)
assert(config->_is_python_build >= 0);
assert(config->safe_path >= 0);
assert(config->int_max_str_digits >= 0);
+ // cpu_count can be -1 if the user doesn't override it.
+ assert(config->cpu_count != 0);
// config->use_frozen_modules is initialized later
// by _PyConfig_InitImportConfig().
#ifdef Py_STATS
@@ -832,6 +841,7 @@ _PyConfig_InitCompatConfig(PyConfig *config)
config->int_max_str_digits = -1;
config->_is_python_build = 0;
config->code_debug_ranges = 1;
+ config->cpu_count = -1;
}
@@ -1618,6 +1628,45 @@ config_read_env_vars(PyConfig *config)
}
static PyStatus
+config_init_cpu_count(PyConfig *config)
+{
+ const char *env = config_get_env(config, "PYTHON_CPU_COUNT");
+ if (env) {
+ int cpu_count = -1;
+ if (strcmp(env, "default") == 0) {
+ cpu_count = -1;
+ }
+ else if (_Py_str_to_int(env, &cpu_count) < 0 || cpu_count < 1) {
+ goto error;
+ }
+ config->cpu_count = cpu_count;
+ }
+
+ const wchar_t *xoption = config_get_xoption(config, L"cpu_count");
+ if (xoption) {
+ int cpu_count = -1;
+ const wchar_t *sep = wcschr(xoption, L'=');
+ if (sep) {
+ if (wcscmp(sep + 1, L"default") == 0) {
+ cpu_count = -1;
+ }
+ else if (config_wstr_to_int(sep + 1, &cpu_count) < 0 || cpu_count < 1) {
+ goto error;
+ }
+ }
+ else {
+ goto error;
+ }
+ config->cpu_count = cpu_count;
+ }
+ return _PyStatus_OK();
+
+error:
+ return _PyStatus_ERR("-X cpu_count=n option: n is missing or an invalid number, "
+ "n must be greater than 0");
+}
+
+static PyStatus
config_init_perf_profiling(PyConfig *config)
{
int active = 0;
@@ -1799,6 +1848,13 @@ config_read_complex_options(PyConfig *config)
}
}
+ if (config->cpu_count < 0) {
+ status = config_init_cpu_count(config);
+ if (_PyStatus_EXCEPTION(status)) {
+ return status;
+ }
+ }
+
if (config->pycache_prefix == NULL) {
status = config_init_pycache_prefix(config);
if (_PyStatus_EXCEPTION(status)) {
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index a7ce07d..3debe7f 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -2306,6 +2306,20 @@ sys__getframemodulename_impl(PyObject *module, int depth)
return Py_NewRef(r);
}
+/*[clinic input]
+sys._get_cpu_count_config -> int
+
+Private function for getting PyConfig.cpu_count
+[clinic start generated code]*/
+
+static int
+sys__get_cpu_count_config_impl(PyObject *module)
+/*[clinic end generated code: output=36611bb5efad16dc input=523e1ade2204084e]*/
+{
+ const PyConfig *config = _Py_GetConfig();
+ return config->cpu_count;
+}
+
static PerfMapState perf_map_state;
PyAPI_FUNC(int) PyUnstable_PerfMapState_Init(void) {
@@ -2440,6 +2454,7 @@ static PyMethodDef sys_methods[] = {
SYS__STATS_CLEAR_METHODDEF
SYS__STATS_DUMP_METHODDEF
#endif
+ SYS__GET_CPU_COUNT_CONFIG_METHODDEF
{NULL, NULL} // sentinel
};