]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-128911: Add PyImport_ImportModuleAttr() function (#128912)
authorVictor Stinner <vstinner@python.org>
Thu, 30 Jan 2025 11:17:29 +0000 (12:17 +0100)
committerGitHub <noreply@github.com>
Thu, 30 Jan 2025 11:17:29 +0000 (11:17 +0000)
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.

40 files changed:
Doc/c-api/import.rst
Doc/data/refcounts.dat
Doc/whatsnew/3.14.rst
Include/cpython/import.h
Include/internal/pycore_import.h
Lib/test/test_capi/test_import.py
Misc/NEWS.d/next/C_API/2025-01-16-12-47-01.gh-issue-128911.mHVJ4x.rst [new file with mode: 0644]
Modules/Setup.stdlib.in
Modules/_ctypes/callbacks.c
Modules/_ctypes/stgdict.c
Modules/_cursesmodule.c
Modules/_datetimemodule.c
Modules/_decimal/_decimal.c
Modules/_elementtree.c
Modules/_json.c
Modules/_lsprof.c
Modules/_operator.c
Modules/_pickle.c
Modules/_sqlite/connection.c
Modules/_sqlite/module.c
Modules/_sre/sre.c
Modules/_testcapi/import.c [new file with mode: 0644]
Modules/_testcapi/parts.h
Modules/_testcapimodule.c
Modules/_zoneinfo.c
Modules/arraymodule.c
Modules/cjkcodecs/cjkcodecs.h
Modules/faulthandler.c
Modules/posixmodule.c
Modules/selectmodule.c
Modules/timemodule.c
Objects/abstract.c
Objects/fileobject.c
Objects/memoryobject.c
PCbuild/_testcapi.vcxproj
PCbuild/_testcapi.vcxproj.filters
Parser/pegen.c
Parser/tokenizer/file_tokenizer.c
Python/import.c
Python/pylifecycle.c

index 6e48644c8fef8bbd9c41be86cba6b63a6ed065eb..1cab3ce3061ec90bae07fb336920413954aff408 100644 (file)
@@ -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
index e78754e24e23d8fbbf99f6a0776ccd3abb81b55b..d709d2d91b0eb00b226448f8e8978e90113eb437 100644 (file)
@@ -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::
index 2c10d7fefd44ab598a46363925e1e28337d256a9..8d4cb94ae2d805a3a4fe54beb668f5976105a60f 100644 (file)
@@ -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
 ---------------------
index 0fd61c28cafa0efaafbdc94dd06d7eee4667f925..0ce0b1ee6cce2a2c373e3cffb4f4ffcf1d0893fb 100644 (file)
@@ -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);
index 318c712bdfa17494fbacab5bbf599a998238ee69..5fe60df0a92fbc0f48fe3ad7727521b6f445ee52 100644 (file)
@@ -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). */
index 94f96728d9174b123b7807ce08862a5d34699d56..25136624ca4ed9b9a5b725efad9621cc3e5900bd 100644 (file)
@@ -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 (file)
index 0000000..d32cd00
--- /dev/null
@@ -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.
index 6b6a8ae57a511956940328027e973ac84334ed87..563bbc1bda6223cc352030051a65e6b716ebb10b 100644 (file)
 @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
index 89c0749a093765028bdec92fa9b9eca01555e285..591206a78035c5aa0e826c29598448f5b77d4305 100644 (file)
@@ -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 */
index 5ca5b62427600dae55d3af85d6d67f6d41793b8a..d63a46a3bc23d262117f1003456e9181be6c0444 100644 (file)
@@ -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;
index c6835738348ff9169a8e633b3d5ef2340ad3b2f0..7213a5be07de4b404897abcc596b659b8b6d2386 100644 (file)
@@ -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);
index ff2e6d6a098ad9ba8c712193b9b0c461b0d3289c..a486af7833c2c66ec1caca06a3e8f2c296df9255 100644 (file)
@@ -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;
     }
index 78cf6b1426493b9c0369075bfe7fa658a98fd890..3dcb3e9870c8a44ac6db0671311c7c192163d4b3 100644 (file)
@@ -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;
         }
index 355f322d304c2fdea02cd878f64633278492c871..b5b0b82571f882caa52a782928af5c65565f1932 100644 (file)
@@ -16,7 +16,6 @@
 #endif
 
 #include "Python.h"
-#include "pycore_import.h"        // _PyImport_GetModuleAttrString()
 #include "pycore_pyhash.h"        // _Py_HashSecret
 
 #include <stddef.h>               // 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;
index 31a5e935e13ad94c40bfa43f96ad58507ddce576..5532e252819bbdaf8772b8af1967f0a9ad4222fa 100644 (file)
@@ -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;
     }
index 51ad9fc7da84924982c7738040538113114231be..29d9e5b6ef2cbe94308d0e4ef8507413e3e1acc8 100644 (file)
@@ -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;
     }
index ce3ef015710223a0874723b6ec82c962ef8b07cb..59987b8f143da228d347c51ac0e3a474cad4fd68 100644 (file)
@@ -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;
 
index a6cfb2deeb23c196983ad57d70bc1d36023042d8..5641f93391c551ce38b49c16ed724cb432ad0fed 100644 (file)
@@ -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;
 
index 62598ecc86412005df4ac845dd39f95601676c04..0c98f5065ee303064bbcb2724bcdd2021a78b48e 100644 (file)
@@ -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,
index 698e81d9b897d038b2acdf1a5fa148c467733456..73d55fb44e2e15634437123acc593b23a1c4beb5 100644 (file)
@@ -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;
     }
index d0025dd21e045bac1229a2f9f3b6d021547ff6f5..0d8d4843d33c1bc002c936e8c2bbfe8d26e7bc25 100644 (file)
@@ -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 (file)
index 0000000..27d3749
--- /dev/null
@@ -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);
+}
+
index 65ba77596c760eaee478ac687925147f7049036b..792552d80973122fde638a95326e4791b6fa06fb 100644 (file)
@@ -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
index c405a352ed74a19ae080d7a4a2c4e9ab1f1d785a..4da23ba82d575602860d9d1f0a03ef76cdffa6de 100644 (file)
@@ -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;
index c5292575c22f2337b87eab70e66d40fc93fdba3b..1fcea9ce8b126176b7b4610e706b1c13f8605b20 100644 (file)
@@ -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;
     }
index 28c6a0ae05c5988bdaf72f5888bab25e757b6790..dc1729a7a3a5580d6bb1aeca7047843097c8573b 100644 (file)
@@ -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);
index 2b446ba5226ac03b142b186cfc32dbc6f839bc4d..737a7a042753a9f9f22f408d7a0601264a9113bd 100644 (file)
@@ -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
index b44b964b29484be2929fb841c06c6421d1eb2acb..a15ced22677ab7a98e7ec9d52f6b4830ac21c659 100644 (file)
@@ -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;
     }
index a35a848a7ca4b882546fcda7fb524742a286a039..6d9b365ea9ceb2273c2b7bff75cd7f53e1256cf8 100644 (file)
@@ -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;
 
index e14e114a6dafd0a1c6bb96e427f78f92f8e66ec1..c75e2ba28c5b4eab0ccd6f4ba0c4aa3df98abd36 100644 (file)
@@ -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 <stdbool.h>
@@ -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;
index 5d0cd52a93a2d3c23d1ab0557de303750ab01196..8d2cbff662b9a3a772a5d26836f97524e9440064 100644 (file)
@@ -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;
     }
index c92ef10aa796487b3cec26d811e27d8fb8f8d257..db7b9263711f684fedc0430b95fb1eb792396b9e 100644 (file)
@@ -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;
     }
index c377d1bb28b56feadfc75783b424880830b2e84a..7025b5bcffc1c8f080d35184920980a49994f961 100644 (file)
@@ -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);
index ea4d24dc69076877f33f35023e7c8469ab90018c..331363b2babbef62db6d020d421675d42d47ae40 100644 (file)
@@ -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;
 
index c41235eac356af2aff1c1f70634b77e942957eb8..733bb69acb16e2d7ae3198b03b3183aa6417307c 100644 (file)
     <ClCompile Include="..\Modules\_testcapi\run.c" />
     <ClCompile Include="..\Modules\_testcapi\monitoring.c" />
     <ClCompile Include="..\Modules\_testcapi\config.c" />
+    <ClCompile Include="..\Modules\_testcapi\import.c" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\PC\python_nt.rc" />
index 0a00df655deefcdd3073798facf5af880d301bb1..e8ddd537674a9c7b0b01bd4f5474662ba88ec3e5 100644 (file)
     <ClCompile Include="..\Modules\_testcapi\config.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\Modules\_testcapi\import.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\PC\python_nt.rc">
index bb98e7b184a4dca612de6e50694afda0bfb34829..83b0022e47d619615009548894c7a3998d816468 100644 (file)
@@ -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;
index 2750527da484aa59d932cbb1594a083a68e25600..efe9fb9b56abaf48c1329967ac9c623cecd47507 100644 (file)
@@ -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;
     }
index b3648e24d0e064761be892ab632e0baff6389616..dd7a0b4b1ed8dea9daa3e3e07aa19baa699eecfe 100644 (file)
@@ -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;
index 00a98af998cfce7b4bac6effa6b017f7f85dff0b..7031d743174650f4c5f24aff770a5a8520f98a3a 100644 (file)
@@ -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;
     }