Update sys module to conform with the PEP, add matching C API

This commit is contained in:
Dino Viehland 2025-10-09 13:27:14 -07:00
parent fe526b4e54
commit 06b9110569
18 changed files with 260 additions and 177 deletions

View file

@ -3072,14 +3072,14 @@ _PyEval_LazyImportName(PyThreadState *tstate, PyObject *builtins, PyObject *glob
{
PyObject *res = NULL;
// Check if global policy overrides the local syntax
switch (PyImport_LazyImportsEnabled()) {
case PyLazyImportsMode_ForcedOff:
switch (PyImport_GetLazyImportsMode()) {
case PyImport_LAZY_NONE:
lazy = 0;
break;
case PyLazyImportsMode_ForcedOn:
case PyImport_LAZY_ALL:
lazy = 1;
break;
case PyLazyImportsMode_Default:
case PyImport_LAZY_NORMAL:
break;
}

View file

@ -622,88 +622,6 @@ exit:
return return_value;
}
PyDoc_STRVAR(_imp_set_lazy_imports__doc__,
"set_lazy_imports($module, enabled=None, /, filter=<unrepresentable>)\n"
"--\n"
"\n"
"Programmatic API for enabling lazy imports at runtime.\n"
"\n"
"enabled can be:\n"
" None (lazy imports always respect keyword)\n"
" False (forced lazy imports off)\n"
" True (forced lazy imports on)\n"
"\n"
"filter is an optional callable which further disables lazy imports when they\n"
"would otherwise be enabled. Returns True if the the import is still enabled\n"
"or False to disable it. The callable is called with:\n"
"\n"
"(importing_module_name, imported_module_name, [fromlist])");
#define _IMP_SET_LAZY_IMPORTS_METHODDEF \
{"set_lazy_imports", _PyCFunction_CAST(_imp_set_lazy_imports), METH_FASTCALL|METH_KEYWORDS, _imp_set_lazy_imports__doc__},
static PyObject *
_imp_set_lazy_imports_impl(PyObject *module, PyObject *enabled,
PyObject *filter);
static PyObject *
_imp_set_lazy_imports(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
#define NUM_KEYWORDS 1
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
Py_hash_t ob_hash;
PyObject *ob_item[NUM_KEYWORDS];
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
.ob_hash = -1,
.ob_item = { &_Py_ID(filter), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
#else // !Py_BUILD_CORE
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
static const char * const _keywords[] = {"", "filter", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.fname = "set_lazy_imports",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
PyObject *argsbuf[2];
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0;
PyObject *enabled = Py_None;
PyObject *filter = NULL;
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
/*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
if (!args) {
goto exit;
}
if (nargs < 1) {
goto skip_optional_posonly;
}
noptargs--;
enabled = args[0];
skip_optional_posonly:
if (!noptargs) {
goto skip_optional_pos;
}
filter = args[1];
skip_optional_pos:
return_value = _imp_set_lazy_imports_impl(module, enabled, filter);
exit:
return return_value;
}
PyDoc_STRVAR(_imp__set_lazy_attributes__doc__,
"_set_lazy_attributes($module, child_module, name, /)\n"
"--\n"
@ -746,4 +664,4 @@ exit:
#ifndef _IMP_EXEC_DYNAMIC_METHODDEF
#define _IMP_EXEC_DYNAMIC_METHODDEF
#endif /* !defined(_IMP_EXEC_DYNAMIC_METHODDEF) */
/*[clinic end generated code: output=0b2b116cf3e431ca input=a9049054013a1b77]*/
/*[clinic end generated code: output=0fe31ade5e29e8d6 input=a9049054013a1b77]*/

View file

@ -1907,6 +1907,93 @@ sys_get_lazy_imports_filter(PyObject *module, PyObject *Py_UNUSED(ignored))
return sys_get_lazy_imports_filter_impl(module);
}
PyDoc_STRVAR(sys_set_lazy_imports__doc__,
"set_lazy_imports($module, /, enabled)\n"
"--\n"
"\n"
"Sets the global lazy imports flag.\n"
"\n"
"True sets all imports at the top level as potentially lazy.\n"
"False disables lazy imports for any explicitly marked imports.\n"
"None causes only explicitly marked imports as lazy.\n"
"\n"
"In addition to the mode lazy imports can be controlled via the filter\n"
"provided to sys.set_lazy_imports_filter");
#define SYS_SET_LAZY_IMPORTS_METHODDEF \
{"set_lazy_imports", _PyCFunction_CAST(sys_set_lazy_imports), METH_FASTCALL|METH_KEYWORDS, sys_set_lazy_imports__doc__},
static PyObject *
sys_set_lazy_imports_impl(PyObject *module, PyObject *enabled);
static PyObject *
sys_set_lazy_imports(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
#define NUM_KEYWORDS 1
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
Py_hash_t ob_hash;
PyObject *ob_item[NUM_KEYWORDS];
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
.ob_hash = -1,
.ob_item = { &_Py_ID(enabled), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
#else // !Py_BUILD_CORE
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
static const char * const _keywords[] = {"enabled", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.fname = "set_lazy_imports",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
PyObject *argsbuf[1];
PyObject *enabled;
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
/*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
if (!args) {
goto exit;
}
enabled = args[0];
return_value = sys_set_lazy_imports_impl(module, enabled);
exit:
return return_value;
}
PyDoc_STRVAR(sys_get_lazy_imports__doc__,
"get_lazy_imports($module, /)\n"
"--\n"
"\n"
"Gets the global lazy imports flag.\n"
"\n"
"Returns True if all top level imports are potentially lazy.\n"
"Returns False if all explicilty marked lazy imports are suppressed.\n"
"Returns None if only explicitly marked imports are lazy.");
#define SYS_GET_LAZY_IMPORTS_METHODDEF \
{"get_lazy_imports", (PyCFunction)sys_get_lazy_imports, METH_NOARGS, sys_get_lazy_imports__doc__},
static PyObject *
sys_get_lazy_imports_impl(PyObject *module);
static PyObject *
sys_get_lazy_imports(PyObject *module, PyObject *Py_UNUSED(ignored))
{
return sys_get_lazy_imports_impl(module);
}
PyDoc_STRVAR(_jit_is_available__doc__,
"is_available($module, /)\n"
"--\n"
@ -2034,4 +2121,4 @@ exit:
#ifndef SYS_GETANDROIDAPILEVEL_METHODDEF
#define SYS_GETANDROIDAPILEVEL_METHODDEF
#endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */
/*[clinic end generated code: output=64c56362026c8fcf input=a9049054013a1b77]*/
/*[clinic end generated code: output=dd304e713c0d089f input=a9049054013a1b77]*/

View file

@ -15,6 +15,7 @@
#include "pycore_moduleobject.h" // _PyModule_GetDef()
#include "pycore_namespace.h" // _PyNamespace_Type
#include "pycore_object.h" // _Py_SetImmortal()
#include "pycore_pyatomic_ft_wrappers.h"
#include "pycore_pyerrors.h" // _PyErr_SetString()
#include "pycore_pyhash.h" // _Py_KeyedHash()
#include "pycore_pylifecycle.h"
@ -4228,7 +4229,7 @@ _PyImport_LazyImportModuleLevelObject(PyThreadState *tstate,
assert(_PyEval_GetFrame()->f_globals == _PyEval_GetFrame()->f_locals); // should only be called in global scope
// Check if the filter disables the lazy import
PyObject *filter = LAZY_IMPORTS_FILTER(interp);
PyObject *filter = FT_ATOMIC_LOAD_PTR_RELAXED(LAZY_IMPORTS_FILTER(interp));
if (filter != NULL) {
PyObject *modname;
if (PyDict_GetItemRef(globals, &_Py_ID(__name__), &modname) < 0) {
@ -4608,9 +4609,8 @@ PyImport_ImportModuleAttrString(const char *modname, const char *attrname)
return result;
}
int
PyImport_SetLazyImports(PyImport_LazyImportsMode mode, PyObject *filter)
PyImport_SetLazyImportsFilter(PyObject *filter)
{
if (filter == Py_None) {
filter = NULL;
@ -4621,17 +4621,39 @@ PyImport_SetLazyImports(PyImport_LazyImportsMode mode, PyObject *filter)
}
PyInterpreterState *interp = _PyInterpreterState_GET();
LAZY_IMPORTS_MODE(interp) = mode;
#ifdef Py_GIL_DISABLED
// exchange just in case another thread did same thing at same time
PyObject *old = _Py_atomic_exchange_ptr(&LAZY_IMPORTS_FILTER(interp), Py_XNewRef(filter));
Py_XDECREF(old);
#else
Py_XSETREF(LAZY_IMPORTS_FILTER(interp), Py_XNewRef(filter));
#endif
return 0;
}
/* Gets the lazy imports filter. Returns a new reference. */
PyObject *
PyImport_GetLazyImportsFilter()
{
PyInterpreterState *interp = _PyInterpreterState_GET();
return Py_XNewRef(FT_ATOMIC_LOAD_PTR_RELAXED(LAZY_IMPORTS_FILTER(interp)));
}
int
PyImport_SetLazyImportsMode(PyImport_LazyImportsMode mode)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
FT_ATOMIC_STORE_INT_RELAXED(LAZY_IMPORTS_MODE(interp), mode);
return 0;
}
/* Checks if lazy imports is globally enabled or disabled. Return 1 when globally
* forced on, 0 when globally forced off, or -1 when */
PyImport_LazyImportsMode PyImport_LazyImportsEnabled(void)
PyImport_LazyImportsMode
PyImport_GetLazyImportsMode()
{
PyInterpreterState *interp = _PyInterpreterState_GET();
return LAZY_IMPORTS_MODE(interp);
return FT_ATOMIC_LOAD_INT_RELAXED(LAZY_IMPORTS_MODE(interp));
}
/**************/
@ -5223,51 +5245,6 @@ _imp_source_hash_impl(PyObject *module, long key, Py_buffer *source)
return PyBytes_FromStringAndSize(hash.data, sizeof(hash.data));
}
/*[clinic input]
_imp.set_lazy_imports
enabled: object = None
/
filter: object = NULL
Programmatic API for enabling lazy imports at runtime.
enabled can be:
None (lazy imports always respect keyword)
False (forced lazy imports off)
True (forced lazy imports on)
filter is an optional callable which further disables lazy imports when they
would otherwise be enabled. Returns True if the the import is still enabled
or False to disable it. The callable is called with:
(importing_module_name, imported_module_name, [fromlist])
[clinic start generated code]*/
static PyObject *
_imp_set_lazy_imports_impl(PyObject *module, PyObject *enabled,
PyObject *filter)
/*[clinic end generated code: output=d8d5a848c041edc5 input=00b2334fae4345a3]*/
{
PyImport_LazyImportsMode mode;
if (enabled == Py_None) {
mode = PyLazyImportsMode_Default;
} else if (enabled == Py_False) {
mode = PyLazyImportsMode_ForcedOff;
} else if (enabled == Py_True) {
mode = PyLazyImportsMode_ForcedOn;
} else {
PyErr_SetString(PyExc_ValueError, "expected None, True or False for enabled mode");
return NULL;
}
if (PyImport_SetLazyImports(mode, filter) < 0) {
return NULL;
}
Py_RETURN_NONE;
}
/*[clinic input]
_imp._set_lazy_attributes
child_module: object
@ -5352,7 +5329,6 @@ static PyMethodDef imp_methods[] = {
_IMP_EXEC_BUILTIN_METHODDEF
_IMP__FIX_CO_FILENAME_METHODDEF
_IMP_SOURCE_HASH_METHODDEF
_IMP_SET_LAZY_IMPORTS_METHODDEF
_IMP__SET_LAZY_ATTRIBUTES_METHODDEF
{NULL, NULL} /* sentinel */
};

View file

@ -1321,11 +1321,11 @@ init_interp_main(PyThreadState *tstate)
if (config->lazy_imports != -1) {
PyImport_LazyImportsMode lazy_mode;
if (config->lazy_imports == 1) {
lazy_mode = PyLazyImportsMode_ForcedOn;
lazy_mode = PyImport_LAZY_ALL;
} else {
lazy_mode = PyLazyImportsMode_ForcedOff;
lazy_mode = PyImport_LAZY_NONE;
}
if (PyImport_SetLazyImports(lazy_mode, NULL) < 0) {
if (PyImport_SetLazyImportsMode(lazy_mode) < 0) {
return _PyStatus_ERR("failed to set lazy imports mode");
}
}

View file

@ -2792,20 +2792,10 @@ static PyObject *
sys_set_lazy_imports_filter_impl(PyObject *module, PyObject *filter)
/*[clinic end generated code: output=10251d49469c278c input=2eb48786bdd4ee42]*/
{
PyObject *current_filter = NULL;
if (filter == Py_None) {
current_filter = NULL;
}
else if (!PyCallable_Check(filter)) {
PyErr_SetString(PyExc_TypeError, "filter must be callable or None");
if (PyImport_SetLazyImportsFilter(filter) < 0) {
return NULL;
}
else {
current_filter = filter;
}
PyInterpreterState *interp = _PyInterpreterState_GET();
Py_XSETREF(interp->imports.lazy_imports_filter, Py_XNewRef(current_filter));
Py_RETURN_NONE;
}
@ -2821,14 +2811,78 @@ static PyObject *
sys_get_lazy_imports_filter_impl(PyObject *module)
/*[clinic end generated code: output=3bf73022892165af input=cf1e07cb8e203c94]*/
{
PyInterpreterState *interp = _PyInterpreterState_GET();
PyObject *filter = interp->imports.lazy_imports_filter;
PyObject *filter = PyImport_GetLazyImportsFilter();
if (filter == NULL) {
Py_RETURN_NONE;
}
return Py_NewRef(filter);
return filter;
}
/*[clinic input]
sys.set_lazy_imports
enabled: object
Sets the global lazy imports flag.
True sets all imports at the top level as potentially lazy.
False disables lazy imports for any explicitly marked imports.
None causes only explicitly marked imports as lazy.
In addition to the mode lazy imports can be controlled via the filter
provided to sys.set_lazy_imports_filter
[clinic start generated code]*/
static PyObject *
sys_set_lazy_imports_impl(PyObject *module, PyObject *enabled)
/*[clinic end generated code: output=d601640d3e2d70fb input=d351054b5884eae5]*/
{
PyImport_LazyImportsMode mode;
if (enabled == Py_None) {
mode = PyImport_LAZY_NORMAL;
} else if (enabled == Py_False) {
mode = PyImport_LAZY_NONE;
} else if (enabled == Py_True) {
mode = PyImport_LAZY_ALL;
} else {
PyErr_SetString(PyExc_ValueError, "expected None, True or False for enabled mode");
return NULL;
}
if (PyImport_SetLazyImportsMode(mode)) {
return NULL;
}
Py_RETURN_NONE;
}
/*[clinic input]
sys.get_lazy_imports
Gets the global lazy imports flag.
Returns True if all top level imports are potentially lazy.
Returns False if all explicilty marked lazy imports are suppressed.
Returns None if only explicitly marked imports are lazy.
[clinic start generated code]*/
static PyObject *
sys_get_lazy_imports_impl(PyObject *module)
/*[clinic end generated code: output=4147dec48c51ae99 input=d7b25d814165c8ce]*/
{
switch (PyImport_GetLazyImportsMode()) {
case PyImport_LAZY_NORMAL:
Py_RETURN_NONE;
case PyImport_LAZY_ALL:
Py_RETURN_TRUE;
case PyImport_LAZY_NONE:
Py_RETURN_FALSE;
default:
PyErr_SetString(PyExc_RuntimeError, "unknown lazy imports mode");
return NULL;
}
}
static PyMethodDef sys_methods[] = {
/* Might as well keep this in alphabetic order */
@ -2894,6 +2948,8 @@ static PyMethodDef sys_methods[] = {
SYS_UNRAISABLEHOOK_METHODDEF
SYS_GET_INT_MAX_STR_DIGITS_METHODDEF
SYS_SET_INT_MAX_STR_DIGITS_METHODDEF
SYS_GET_LAZY_IMPORTS_METHODDEF
SYS_SET_LAZY_IMPORTS_METHODDEF
SYS_GET_LAZY_IMPORTS_FILTER_METHODDEF
SYS_SET_LAZY_IMPORTS_FILTER_METHODDEF
SYS__BASEREPL_METHODDEF