From 3bebe46d3413195ee18c5c9ada83a35d4fd1d0e7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 30 Jan 2025 12:17:29 +0100 Subject: gh-128911: Add PyImport_ImportModuleAttr() function (#128912) Add PyImport_ImportModuleAttr() and PyImport_ImportModuleAttrString() functions. * Add unit tests. * Replace _PyImport_GetModuleAttr() with PyImport_ImportModuleAttr(). * Replace _PyImport_GetModuleAttrString() with PyImport_ImportModuleAttrString(). * Remove "pycore_import.h" includes, no longer needed. --- Doc/c-api/import.rst | 21 ++++++++ Doc/data/refcounts.dat | 8 ++++ Doc/whatsnew/3.14.rst | 5 ++ Include/cpython/import.h | 7 +++ Include/internal/pycore_import.h | 6 --- Lib/test/test_capi/test_import.py | 56 +++++++++++++++++++++- .../2025-01-16-12-47-01.gh-issue-128911.mHVJ4x.rst | 3 ++ Modules/Setup.stdlib.in | 2 +- Modules/_ctypes/callbacks.c | 2 +- Modules/_ctypes/stgdict.c | 2 +- Modules/_cursesmodule.c | 2 +- Modules/_datetimemodule.c | 6 +-- Modules/_decimal/_decimal.c | 2 +- Modules/_elementtree.c | 5 +- Modules/_json.c | 2 +- Modules/_lsprof.c | 6 +-- Modules/_operator.c | 2 +- Modules/_pickle.c | 4 +- Modules/_sqlite/connection.c | 3 +- Modules/_sqlite/module.c | 4 +- Modules/_sre/sre.c | 2 +- Modules/_testcapi/import.c | 44 +++++++++++++++++ Modules/_testcapi/parts.h | 1 + Modules/_testcapimodule.c | 3 ++ Modules/_zoneinfo.c | 8 ++-- Modules/arraymodule.c | 4 +- Modules/cjkcodecs/cjkcodecs.h | 3 +- Modules/faulthandler.c | 2 +- Modules/posixmodule.c | 2 +- Modules/selectmodule.c | 3 +- Modules/timemodule.c | 2 +- Objects/abstract.c | 2 +- Objects/fileobject.c | 4 +- Objects/memoryobject.c | 2 +- PCbuild/_testcapi.vcxproj | 1 + PCbuild/_testcapi.vcxproj.filters | 3 ++ Parser/pegen.c | 2 +- Parser/tokenizer/file_tokenizer.c | 2 +- Python/import.c | 8 ++-- Python/pylifecycle.c | 4 +- 40 files changed, 194 insertions(+), 56 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2025-01-16-12-47-01.gh-issue-128911.mHVJ4x.rst create mode 100644 Modules/_testcapi/import.c diff --git a/Doc/c-api/import.rst b/Doc/c-api/import.rst index 6e48644..1cab3ce 100644 --- a/Doc/c-api/import.rst +++ b/Doc/c-api/import.rst @@ -325,3 +325,24 @@ Importing Modules If Python is initialized multiple times, :c:func:`PyImport_AppendInittab` or :c:func:`PyImport_ExtendInittab` must be called before each Python initialization. + + +.. c:function:: PyObject* PyImport_ImportModuleAttr(PyObject *mod_name, PyObject *attr_name) + + Import the module *mod_name* and get its attribute *attr_name*. + + Names must be Python :class:`str` objects. + + Helper function combining :c:func:`PyImport_Import` and + :c:func:`PyObject_GetAttr`. For example, it can raise :exc:`ImportError` if + the module is not found, and :exc:`AttributeError` if the attribute doesn't + exist. + + .. versionadded:: 3.14 + +.. c:function:: PyObject* PyImport_ImportModuleAttrString(const char *mod_name, const char *attr_name) + + Similar to :c:func:`PyImport_ImportModuleAttr`, but names are UTF-8 encoded + strings instead of Python :class:`str` objects. + + .. versionadded:: 3.14 diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index e78754e..d709d2d 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -3052,3 +3052,11 @@ _Py_c_quot:Py_complex:divisor:: _Py_c_sum:Py_complex::: _Py_c_sum:Py_complex:left:: _Py_c_sum:Py_complex:right:: + +PyImport_ImportModuleAttr:PyObject*::+1: +PyImport_ImportModuleAttr:PyObject*:mod_name:0: +PyImport_ImportModuleAttr:PyObject*:attr_name:0: + +PyImport_ImportModuleAttrString:PyObject*::+1: +PyImport_ImportModuleAttrString:const char *:mod_name:: +PyImport_ImportModuleAttrString:const char *:attr_name:: diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 2c10d7f..8d4cb94 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1322,6 +1322,11 @@ New features * Add :c:func:`PyUnstable_IsImmortal` for determining whether an object is :term:`immortal`, for debugging purposes. +* Add :c:func:`PyImport_ImportModuleAttr` and + :c:func:`PyImport_ImportModuleAttrString` helper functions to import a module + and get an attribute of the module. + (Contributed by Victor Stinner in :gh:`128911`.) + Limited C API changes --------------------- diff --git a/Include/cpython/import.h b/Include/cpython/import.h index 0fd61c2..0ce0b1e 100644 --- a/Include/cpython/import.h +++ b/Include/cpython/import.h @@ -21,3 +21,10 @@ struct _frozen { collection of frozen modules: */ PyAPI_DATA(const struct _frozen *) PyImport_FrozenModules; + +PyAPI_FUNC(PyObject*) PyImport_ImportModuleAttr( + PyObject *mod_name, + PyObject *attr_name); +PyAPI_FUNC(PyObject*) PyImport_ImportModuleAttrString( + const char *mod_name, + const char *attr_name); diff --git a/Include/internal/pycore_import.h b/Include/internal/pycore_import.h index 318c712..5fe60df 100644 --- a/Include/internal/pycore_import.h +++ b/Include/internal/pycore_import.h @@ -31,12 +31,6 @@ extern int _PyImport_FixupBuiltin( PyObject *modules ); -// Export for many shared extensions, like '_json' -PyAPI_FUNC(PyObject*) _PyImport_GetModuleAttr(PyObject *, PyObject *); - -// Export for many shared extensions, like '_datetime' -PyAPI_FUNC(PyObject*) _PyImport_GetModuleAttrString(const char *, const char *); - struct _import_runtime_state { /* The builtin modules (defined in config.c). */ diff --git a/Lib/test/test_capi/test_import.py b/Lib/test/test_capi/test_import.py index 94f9672..2513662 100644 --- a/Lib/test/test_capi/test_import.py +++ b/Lib/test/test_capi/test_import.py @@ -7,6 +7,7 @@ from test.support import os_helper from test.support import import_helper from test.support.warnings_helper import check_warnings +_testcapi = import_helper.import_module('_testcapi') _testlimitedcapi = import_helper.import_module('_testlimitedcapi') NULL = None @@ -148,7 +149,7 @@ class ImportTests(unittest.TestCase): try: self.assertEqual(import_frozen_module('zipimport'), 1) - # import zipimport again + # import zipimport again self.assertEqual(import_frozen_module('zipimport'), 1) finally: sys.modules['zipimport'] = old_zipimport @@ -317,6 +318,59 @@ class ImportTests(unittest.TestCase): # CRASHES execute_code_func(NULL, code, NULL, NULL) # CRASHES execute_code_func(name, NULL, NULL, NULL) + def check_importmoduleattr(self, importmoduleattr): + self.assertIs(importmoduleattr('sys', 'argv'), sys.argv) + self.assertIs(importmoduleattr('types', 'ModuleType'), types.ModuleType) + + # module name containing a dot + attr = importmoduleattr('email.message', 'Message') + from email.message import Message + self.assertIs(attr, Message) + + with self.assertRaises(ImportError): + # nonexistent module + importmoduleattr('nonexistentmodule', 'attr') + with self.assertRaises(AttributeError): + # nonexistent attribute + importmoduleattr('sys', 'nonexistentattr') + with self.assertRaises(AttributeError): + # attribute name containing a dot + importmoduleattr('sys', 'implementation.name') + + def test_importmoduleattr(self): + # Test PyImport_ImportModuleAttr() + importmoduleattr = _testcapi.PyImport_ImportModuleAttr + self.check_importmoduleattr(importmoduleattr) + + # Invalid module name type + for mod_name in (object(), 123, b'bytes'): + with self.subTest(mod_name=mod_name): + with self.assertRaises(TypeError): + importmoduleattr(mod_name, "attr") + + # Invalid attribute name type + for attr_name in (object(), 123, b'bytes'): + with self.subTest(attr_name=attr_name): + with self.assertRaises(TypeError): + importmoduleattr("sys", attr_name) + + with self.assertRaises(SystemError): + importmoduleattr(NULL, "argv") + # CRASHES importmoduleattr("sys", NULL) + + def test_importmoduleattrstring(self): + # Test PyImport_ImportModuleAttrString() + importmoduleattr = _testcapi.PyImport_ImportModuleAttrString + self.check_importmoduleattr(importmoduleattr) + + with self.assertRaises(UnicodeDecodeError): + importmoduleattr(b"sys\xff", "argv") + with self.assertRaises(UnicodeDecodeError): + importmoduleattr("sys", b"argv\xff") + + # CRASHES importmoduleattr(NULL, "argv") + # CRASHES importmoduleattr("sys", NULL) + # TODO: test PyImport_GetImporter() # TODO: test PyImport_ReloadModule() # TODO: test PyImport_ExtendInittab() diff --git a/Misc/NEWS.d/next/C_API/2025-01-16-12-47-01.gh-issue-128911.mHVJ4x.rst b/Misc/NEWS.d/next/C_API/2025-01-16-12-47-01.gh-issue-128911.mHVJ4x.rst new file mode 100644 index 0000000..d32cd00 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2025-01-16-12-47-01.gh-issue-128911.mHVJ4x.rst @@ -0,0 +1,3 @@ +Add :c:func:`PyImport_ImportModuleAttr` and :c:func:`PyImport_ImportModuleAttrString` +helper functions to import a module and get an attribute of the module. Patch +by Victor Stinner. diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 6b6a8ae..563bbc1 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -162,7 +162,7 @@ @MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c -@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c +@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c @MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c @MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index 89c0749..591206a 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -492,7 +492,7 @@ long Call_GetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) if (context == NULL) context = PyUnicode_InternFromString("_ctypes.DllGetClassObject"); - func = _PyImport_GetModuleAttrString("ctypes", "DllGetClassObject"); + func = PyImport_ImportModuleAttrString("ctypes", "DllGetClassObject"); if (!func) { PyErr_WriteUnraisable(context ? context : Py_None); /* There has been a warning before about this already */ diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 5ca5b62..d63a46a 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -257,7 +257,7 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct goto error; } - PyObject *layout_func = _PyImport_GetModuleAttrString("ctypes._layout", + PyObject *layout_func = PyImport_ImportModuleAttrString("ctypes._layout", "get_layout"); if (!layout_func) { goto error; diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index c683573..7213a5b 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -226,7 +226,7 @@ _PyCursesCheckFunction(int called, const char *funcname) if (called == TRUE) { return 1; } - PyObject *exc = _PyImport_GetModuleAttrString("_curses", "error"); + PyObject *exc = PyImport_ImportModuleAttrString("_curses", "error"); if (exc != NULL) { PyErr_Format(exc, "must call %s() first", funcname); Py_DECREF(exc); diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index ff2e6d6..a486af7 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1839,7 +1839,7 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, assert(object && format && timetuple); assert(PyUnicode_Check(format)); - PyObject *strftime = _PyImport_GetModuleAttrString("time", "strftime"); + PyObject *strftime = PyImport_ImportModuleAttrString("time", "strftime"); if (strftime == NULL) { return NULL; } @@ -2022,7 +2022,7 @@ static PyObject * time_time(void) { PyObject *result = NULL; - PyObject *time = _PyImport_GetModuleAttrString("time", "time"); + PyObject *time = PyImport_ImportModuleAttrString("time", "time"); if (time != NULL) { result = PyObject_CallNoArgs(time); @@ -2040,7 +2040,7 @@ build_struct_time(int y, int m, int d, int hh, int mm, int ss, int dstflag) PyObject *struct_time; PyObject *result; - struct_time = _PyImport_GetModuleAttrString("time", "struct_time"); + struct_time = PyImport_ImportModuleAttrString("time", "struct_time"); if (struct_time == NULL) { return NULL; } diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 78cf6b1..3dcb3e9 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -3474,7 +3474,7 @@ pydec_format(PyObject *dec, PyObject *context, PyObject *fmt, decimal_state *sta PyObject *u; if (state->PyDecimal == NULL) { - state->PyDecimal = _PyImport_GetModuleAttrString("_pydecimal", "Decimal"); + state->PyDecimal = PyImport_ImportModuleAttrString("_pydecimal", "Decimal"); if (state->PyDecimal == NULL) { return NULL; } diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 355f322..b5b0b82 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -16,7 +16,6 @@ #endif #include "Python.h" -#include "pycore_import.h" // _PyImport_GetModuleAttrString() #include "pycore_pyhash.h" // _Py_HashSecret #include // offsetof() @@ -4393,7 +4392,7 @@ module_exec(PyObject *m) CREATE_TYPE(m, st->Element_Type, &element_spec); CREATE_TYPE(m, st->XMLParser_Type, &xmlparser_spec); - st->deepcopy_obj = _PyImport_GetModuleAttrString("copy", "deepcopy"); + st->deepcopy_obj = PyImport_ImportModuleAttrString("copy", "deepcopy"); if (st->deepcopy_obj == NULL) { goto error; } @@ -4403,7 +4402,7 @@ module_exec(PyObject *m) goto error; /* link against pyexpat */ - if (!(st->expat_capsule = _PyImport_GetModuleAttrString("pyexpat", "expat_CAPI"))) + if (!(st->expat_capsule = PyImport_ImportModuleAttrString("pyexpat", "expat_CAPI"))) goto error; if (!(st->expat_capi = PyCapsule_GetPointer(st->expat_capsule, PyExpat_CAPSULE_NAME))) goto error; diff --git a/Modules/_json.c b/Modules/_json.c index 31a5e93..5532e25 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -302,7 +302,7 @@ raise_errmsg(const char *msg, PyObject *s, Py_ssize_t end) /* Use JSONDecodeError exception to raise a nice looking ValueError subclass */ _Py_DECLARE_STR(json_decoder, "json.decoder"); PyObject *JSONDecodeError = - _PyImport_GetModuleAttr(&_Py_STR(json_decoder), &_Py_ID(JSONDecodeError)); + PyImport_ImportModuleAttr(&_Py_STR(json_decoder), &_Py_ID(JSONDecodeError)); if (JSONDecodeError == NULL) { return; } diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c index 51ad9fc..29d9e5b 100644 --- a/Modules/_lsprof.c +++ b/Modules/_lsprof.c @@ -775,7 +775,7 @@ _lsprof_Profiler_enable_impl(ProfilerObject *self, int subcalls, return NULL; } - PyObject* monitoring = _PyImport_GetModuleAttrString("sys", "monitoring"); + PyObject* monitoring = PyImport_ImportModuleAttrString("sys", "monitoring"); if (!monitoring) { return NULL; } @@ -857,7 +857,7 @@ _lsprof_Profiler_disable_impl(ProfilerObject *self) } if (self->flags & POF_ENABLED) { PyObject* result = NULL; - PyObject* monitoring = _PyImport_GetModuleAttrString("sys", "monitoring"); + PyObject* monitoring = PyImport_ImportModuleAttrString("sys", "monitoring"); if (!monitoring) { return NULL; @@ -973,7 +973,7 @@ profiler_init_impl(ProfilerObject *self, PyObject *timer, double timeunit, Py_XSETREF(self->externalTimer, Py_XNewRef(timer)); self->tool_id = PY_MONITORING_PROFILER_ID; - PyObject* monitoring = _PyImport_GetModuleAttrString("sys", "monitoring"); + PyObject* monitoring = PyImport_ImportModuleAttrString("sys", "monitoring"); if (!monitoring) { return -1; } diff --git a/Modules/_operator.c b/Modules/_operator.c index ce3ef01..59987b8 100644 --- a/Modules/_operator.c +++ b/Modules/_operator.c @@ -1868,7 +1868,7 @@ methodcaller_reduce(methodcallerobject *mc, PyObject *Py_UNUSED(ignored)) PyObject *constructor; PyObject *newargs[2]; - partial = _PyImport_GetModuleAttrString("functools", "partial"); + partial = PyImport_ImportModuleAttrString("functools", "partial"); if (!partial) return NULL; diff --git a/Modules/_pickle.c b/Modules/_pickle.c index a6cfb2d..5641f93 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -362,7 +362,7 @@ _Pickle_InitState(PickleState *st) } Py_CLEAR(compat_pickle); - st->codecs_encode = _PyImport_GetModuleAttrString("codecs", "encode"); + st->codecs_encode = PyImport_ImportModuleAttrString("codecs", "encode"); if (st->codecs_encode == NULL) { goto error; } @@ -373,7 +373,7 @@ _Pickle_InitState(PickleState *st) goto error; } - st->partial = _PyImport_GetModuleAttrString("functools", "partial"); + st->partial = PyImport_ImportModuleAttrString("functools", "partial"); if (!st->partial) goto error; diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 62598ec..0c98f50 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -34,7 +34,6 @@ #include "prepare_protocol.h" #include "util.h" -#include "pycore_import.h" // _PyImport_GetModuleAttrString() #include "pycore_modsupport.h" // _PyArg_NoKeywords() #include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() #include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing() @@ -2000,7 +1999,7 @@ pysqlite_connection_iterdump_impl(pysqlite_Connection *self, return NULL; } - PyObject *iterdump = _PyImport_GetModuleAttrString(MODULE_NAME ".dump", "_iterdump"); + PyObject *iterdump = PyImport_ImportModuleAttrString(MODULE_NAME ".dump", "_iterdump"); if (!iterdump) { if (!PyErr_Occurred()) { PyErr_SetString(self->OperationalError, diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 698e81d..73d55fb 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -33,8 +33,6 @@ #include "row.h" #include "blob.h" -#include "pycore_import.h" // _PyImport_GetModuleAttrString() - #if SQLITE_VERSION_NUMBER < 3015002 #error "SQLite 3.15.2 or higher required" #endif @@ -234,7 +232,7 @@ static int load_functools_lru_cache(PyObject *module) { pysqlite_state *state = pysqlite_get_state(module); - state->lru_cache = _PyImport_GetModuleAttrString("functools", "lru_cache"); + state->lru_cache = PyImport_ImportModuleAttrString("functools", "lru_cache"); if (state->lru_cache == NULL) { return -1; } diff --git a/Modules/_sre/sre.c b/Modules/_sre/sre.c index d0025dd..0d8d484 100644 --- a/Modules/_sre/sre.c +++ b/Modules/_sre/sre.c @@ -1169,7 +1169,7 @@ compile_template(_sremodulestate *module_state, /* delegate to Python code */ PyObject *func = module_state->compile_template; if (func == NULL) { - func = _PyImport_GetModuleAttrString("re", "_compile_template"); + func = PyImport_ImportModuleAttrString("re", "_compile_template"); if (func == NULL) { return NULL; } diff --git a/Modules/_testcapi/import.c b/Modules/_testcapi/import.c new file mode 100644 index 0000000..27d3749 --- /dev/null +++ b/Modules/_testcapi/import.c @@ -0,0 +1,44 @@ +#include "parts.h" +#include "util.h" + +// Test PyImport_ImportModuleAttr() +static PyObject * +pyimport_importmoduleattr(PyObject *self, PyObject *args) +{ + PyObject *mod_name, *attr_name; + if (!PyArg_ParseTuple(args, "OO", &mod_name, &attr_name)) { + return NULL; + } + NULLABLE(mod_name); + NULLABLE(attr_name); + + return PyImport_ImportModuleAttr(mod_name, attr_name); +} + + +// Test PyImport_ImportModuleAttrString() +static PyObject * +pyimport_importmoduleattrstring(PyObject *self, PyObject *args) +{ + const char *mod_name, *attr_name; + Py_ssize_t len; + if (!PyArg_ParseTuple(args, "z#z#", &mod_name, &len, &attr_name, &len)) { + return NULL; + } + + return PyImport_ImportModuleAttrString(mod_name, attr_name); +} + + +static PyMethodDef test_methods[] = { + {"PyImport_ImportModuleAttr", pyimport_importmoduleattr, METH_VARARGS}, + {"PyImport_ImportModuleAttrString", pyimport_importmoduleattrstring, METH_VARARGS}, + {NULL}, +}; + +int +_PyTestCapi_Init_Import(PyObject *m) +{ + return PyModule_AddFunctions(m, test_methods); +} + diff --git a/Modules/_testcapi/parts.h b/Modules/_testcapi/parts.h index 65ba775..792552d 100644 --- a/Modules/_testcapi/parts.h +++ b/Modules/_testcapi/parts.h @@ -61,5 +61,6 @@ int _PyTestCapi_Init_Time(PyObject *module); int _PyTestCapi_Init_Monitoring(PyObject *module); int _PyTestCapi_Init_Object(PyObject *module); int _PyTestCapi_Init_Config(PyObject *mod); +int _PyTestCapi_Init_Import(PyObject *mod); #endif // Py_TESTCAPI_PARTS_H diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index c405a35..4da23ba 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -4401,6 +4401,9 @@ PyInit__testcapi(void) if (_PyTestCapi_Init_Config(m) < 0) { return NULL; } + if (_PyTestCapi_Init_Import(m) < 0) { + return NULL; + } PyState_AddModule(m, &_testcapimodule); return m; diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index c529257..1fcea9c 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -782,7 +782,7 @@ zoneinfo_reduce(PyObject *obj_self, PyObject *unused) if (self->source == SOURCE_FILE) { // Objects constructed from files cannot be pickled. PyObject *pickle_error = - _PyImport_GetModuleAttrString("pickle", "PicklingError"); + PyImport_ImportModuleAttrString("pickle", "PicklingError"); if (pickle_error == NULL) { return NULL; } @@ -2554,7 +2554,7 @@ static PyObject * new_weak_cache(void) { PyObject *WeakValueDictionary = - _PyImport_GetModuleAttrString("weakref", "WeakValueDictionary"); + PyImport_ImportModuleAttrString("weakref", "WeakValueDictionary"); if (WeakValueDictionary == NULL) { return NULL; } @@ -2732,12 +2732,12 @@ zoneinfomodule_exec(PyObject *m) /* Populate imports */ state->_tzpath_find_tzfile = - _PyImport_GetModuleAttrString("zoneinfo._tzpath", "find_tzfile"); + PyImport_ImportModuleAttrString("zoneinfo._tzpath", "find_tzfile"); if (state->_tzpath_find_tzfile == NULL) { goto error; } - state->io_open = _PyImport_GetModuleAttrString("io", "open"); + state->io_open = PyImport_ImportModuleAttrString("io", "open"); if (state->io_open == NULL) { goto error; } diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 28c6a0a..dc1729a 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -2285,7 +2285,7 @@ array_array___reduce_ex___impl(arrayobject *self, PyTypeObject *cls, assert(state != NULL); if (state->array_reconstructor == NULL) { - state->array_reconstructor = _PyImport_GetModuleAttrString( + state->array_reconstructor = PyImport_ImportModuleAttrString( "array", "_array_reconstructor"); if (state->array_reconstructor == NULL) { return NULL; @@ -3206,7 +3206,7 @@ array_modexec(PyObject *m) return -1; } - PyObject *mutablesequence = _PyImport_GetModuleAttrString( + PyObject *mutablesequence = PyImport_ImportModuleAttrString( "collections.abc", "MutableSequence"); if (!mutablesequence) { Py_DECREF((PyObject *)state->ArrayType); diff --git a/Modules/cjkcodecs/cjkcodecs.h b/Modules/cjkcodecs/cjkcodecs.h index 2b446ba..737a7a0 100644 --- a/Modules/cjkcodecs/cjkcodecs.h +++ b/Modules/cjkcodecs/cjkcodecs.h @@ -13,7 +13,6 @@ #include "Python.h" #include "multibytecodec.h" -#include "pycore_import.h" // _PyImport_GetModuleAttrString() /* a unicode "undefined" code point */ @@ -299,7 +298,7 @@ add_codecs(cjkcodecs_module_state *st) \ static PyObject * getmultibytecodec(void) { - return _PyImport_GetModuleAttrString("_multibytecodec", "__create_codec"); + return PyImport_ImportModuleAttrString("_multibytecodec", "__create_codec"); } static void diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c index b44b964..a15ced2 100644 --- a/Modules/faulthandler.c +++ b/Modules/faulthandler.c @@ -1346,7 +1346,7 @@ PyInit_faulthandler(void) static int faulthandler_init_enable(void) { - PyObject *enable = _PyImport_GetModuleAttrString("faulthandler", "enable"); + PyObject *enable = PyImport_ImportModuleAttrString("faulthandler", "enable"); if (enable == NULL) { return -1; } diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index a35a848..6d9b365 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9877,7 +9877,7 @@ wait_helper(PyObject *module, pid_t pid, int status, struct rusage *ru) memset(ru, 0, sizeof(*ru)); } - struct_rusage = _PyImport_GetModuleAttrString("resource", "struct_rusage"); + struct_rusage = PyImport_ImportModuleAttrString("resource", "struct_rusage"); if (struct_rusage == NULL) return NULL; diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index e14e114..c75e2ba 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -14,7 +14,6 @@ #include "Python.h" #include "pycore_fileutils.h" // _Py_set_inheritable() -#include "pycore_import.h" // _PyImport_GetModuleAttrString() #include "pycore_time.h" // _PyTime_FromSecondsObject() #include @@ -1996,7 +1995,7 @@ kqueue_tracking_init(PyObject *module) { // Register a callback to invalidate kqueues with open fds after fork. PyObject *register_at_fork = NULL, *cb = NULL, *args = NULL, *kwargs = NULL, *result = NULL; - register_at_fork = _PyImport_GetModuleAttrString("posix", + register_at_fork = PyImport_ImportModuleAttrString("posix", "register_at_fork"); if (register_at_fork == NULL) { goto finally; diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 5d0cd52..8d2cbff 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -979,7 +979,7 @@ time_strptime(PyObject *self, PyObject *args) { PyObject *func, *result; - func = _PyImport_GetModuleAttrString("_strptime", "_strptime_time"); + func = PyImport_ImportModuleAttrString("_strptime", "_strptime_time"); if (!func) { return NULL; } diff --git a/Objects/abstract.c b/Objects/abstract.c index c92ef10..db7b926 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -583,7 +583,7 @@ PyBuffer_SizeFromFormat(const char *format) PyObject *fmt = NULL; Py_ssize_t itemsize = -1; - calcsize = _PyImport_GetModuleAttrString("struct", "calcsize"); + calcsize = PyImport_ImportModuleAttrString("struct", "calcsize"); if (calcsize == NULL) { goto done; } diff --git a/Objects/fileobject.c b/Objects/fileobject.c index c377d1b..7025b5b 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -34,7 +34,7 @@ PyFile_FromFd(int fd, const char *name, const char *mode, int buffering, const c PyObject *open, *stream; /* import _io in case we are being used to open io.py */ - open = _PyImport_GetModuleAttrString("_io", "open"); + open = PyImport_ImportModuleAttrString("_io", "open"); if (open == NULL) return NULL; stream = PyObject_CallFunction(open, "isisssO", fd, mode, @@ -506,7 +506,7 @@ PyFile_OpenCodeObject(PyObject *path) if (hook) { f = hook(path, _PyRuntime.open_code_userdata); } else { - PyObject *open = _PyImport_GetModuleAttrString("_io", "open"); + PyObject *open = PyImport_ImportModuleAttrString("_io", "open"); if (open) { f = PyObject_CallFunction(open, "Os", path, "rb"); Py_DECREF(open); diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c index ea4d24d..331363b 100644 --- a/Objects/memoryobject.c +++ b/Objects/memoryobject.c @@ -2083,7 +2083,7 @@ struct_get_unpacker(const char *fmt, Py_ssize_t itemsize) PyObject *format = NULL; struct unpacker *x = NULL; - Struct = _PyImport_GetModuleAttrString("struct", "Struct"); + Struct = PyImport_ImportModuleAttrString("struct", "Struct"); if (Struct == NULL) return NULL; diff --git a/PCbuild/_testcapi.vcxproj b/PCbuild/_testcapi.vcxproj index c41235e..733bb69 100644 --- a/PCbuild/_testcapi.vcxproj +++ b/PCbuild/_testcapi.vcxproj @@ -127,6 +127,7 @@ + diff --git a/PCbuild/_testcapi.vcxproj.filters b/PCbuild/_testcapi.vcxproj.filters index 0a00df6..e8ddd53 100644 --- a/PCbuild/_testcapi.vcxproj.filters +++ b/PCbuild/_testcapi.vcxproj.filters @@ -114,6 +114,9 @@ Source Files + + Source Files + diff --git a/Parser/pegen.c b/Parser/pegen.c index bb98e7b..83b0022 100644 --- a/Parser/pegen.c +++ b/Parser/pegen.c @@ -111,7 +111,7 @@ init_normalization(Parser *p) if (p->normalize) { return 1; } - p->normalize = _PyImport_GetModuleAttrString("unicodedata", "normalize"); + p->normalize = PyImport_ImportModuleAttrString("unicodedata", "normalize"); if (!p->normalize) { return 0; diff --git a/Parser/tokenizer/file_tokenizer.c b/Parser/tokenizer/file_tokenizer.c index 2750527..efe9fb9 100644 --- a/Parser/tokenizer/file_tokenizer.c +++ b/Parser/tokenizer/file_tokenizer.c @@ -158,7 +158,7 @@ fp_setreadl(struct tok_state *tok, const char* enc) return 0; } - open = _PyImport_GetModuleAttrString("io", "open"); + open = PyImport_ImportModuleAttrString("io", "open"); if (open == NULL) { return 0; } diff --git a/Python/import.c b/Python/import.c index b3648e2..dd7a0b4 100644 --- a/Python/import.c +++ b/Python/import.c @@ -4111,7 +4111,7 @@ init_zipimport(PyThreadState *tstate, int verbose) PySys_WriteStderr("# installing zipimport hook\n"); } - PyObject *zipimporter = _PyImport_GetModuleAttrString("zipimport", "zipimporter"); + PyObject *zipimporter = PyImport_ImportModuleAttrString("zipimport", "zipimporter"); if (zipimporter == NULL) { _PyErr_Clear(tstate); /* No zipimporter object -- okay */ if (verbose) { @@ -4174,7 +4174,7 @@ _PyImport_FiniExternal(PyInterpreterState *interp) /******************/ PyObject * -_PyImport_GetModuleAttr(PyObject *modname, PyObject *attrname) +PyImport_ImportModuleAttr(PyObject *modname, PyObject *attrname) { PyObject *mod = PyImport_Import(modname); if (mod == NULL) { @@ -4186,7 +4186,7 @@ _PyImport_GetModuleAttr(PyObject *modname, PyObject *attrname) } PyObject * -_PyImport_GetModuleAttrString(const char *modname, const char *attrname) +PyImport_ImportModuleAttrString(const char *modname, const char *attrname) { PyObject *pmodname = PyUnicode_FromString(modname); if (pmodname == NULL) { @@ -4197,7 +4197,7 @@ _PyImport_GetModuleAttrString(const char *modname, const char *attrname) Py_DECREF(pmodname); return NULL; } - PyObject *result = _PyImport_GetModuleAttr(pmodname, pattrname); + PyObject *result = PyImport_ImportModuleAttr(pmodname, pattrname); Py_DECREF(pattrname); Py_DECREF(pmodname); return result; diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 00a98af..7031d74 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -2609,7 +2609,7 @@ create_stdio(const PyConfig *config, PyObject* io, #ifdef HAVE_WINDOWS_CONSOLE_IO /* Windows console IO is always UTF-8 encoded */ - PyTypeObject *winconsoleio_type = (PyTypeObject *)_PyImport_GetModuleAttr( + PyTypeObject *winconsoleio_type = (PyTypeObject *)PyImport_ImportModuleAttr( &_Py_ID(_io), &_Py_ID(_WindowsConsoleIO)); if (winconsoleio_type == NULL) { goto error; @@ -2714,7 +2714,7 @@ init_set_builtins_open(void) goto error; } - if (!(wrapper = _PyImport_GetModuleAttrString("io", "open"))) { + if (!(wrapper = PyImport_ImportModuleAttrString("io", "open"))) { goto error; } -- cgit v0.12