]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-132775: Add _PyModule_GetFilenameObject() and _PyModule_GetFilenameUTF8() (gh...
authorEric Snow <ericsnowcurrently@gmail.com>
Mon, 28 Apr 2025 18:41:32 +0000 (12:41 -0600)
committerGitHub <noreply@github.com>
Mon, 28 Apr 2025 18:41:32 +0000 (12:41 -0600)
They are derived from the existing `PyModule_GetFilenameObject().

They are used by a later change related to pickle and handling __main__.

Include/internal/pycore_moduleobject.h
Objects/moduleobject.c

index 9bb282a13a9659c1991319496cf4920b2780bbc4..b170d7bce702c62f53b5677c1cfb975e4919e4df 100644 (file)
@@ -47,6 +47,12 @@ static inline PyObject* _PyModule_GetDict(PyObject *mod) {
     return dict;  // borrowed reference
 }
 
+extern PyObject * _PyModule_GetFilenameObject(PyObject *);
+extern Py_ssize_t _PyModule_GetFilenameUTF8(
+        PyObject *module,
+        char *buffer,
+        Py_ssize_t maxlen);
+
 PyObject* _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress);
 PyObject* _Py_module_getattro(PyObject *m, PyObject *name);
 
index 16f1b2cacd4f83ce0086e6c01fe26aa3a8817397..f363ef173cbd469ceb75a026bf1558d884f15f1b 100644 (file)
@@ -607,32 +607,51 @@ PyModule_GetName(PyObject *m)
 }
 
 PyObject*
-PyModule_GetFilenameObject(PyObject *mod)
+_PyModule_GetFilenameObject(PyObject *mod)
 {
+    // We return None to indicate "not found" or "bogus".
     if (!PyModule_Check(mod)) {
         PyErr_BadArgument();
         return NULL;
     }
     PyObject *dict = ((PyModuleObject *)mod)->md_dict;  // borrowed reference
     if (dict == NULL) {
-        goto error;
+        // The module has been tampered with.
+        Py_RETURN_NONE;
     }
     PyObject *fileobj;
-    if (PyDict_GetItemRef(dict, &_Py_ID(__file__), &fileobj) <= 0) {
-        // error or not found
-        goto error;
+    int res = PyDict_GetItemRef(dict, &_Py_ID(__file__), &fileobj);
+    if (res < 0) {
+        return NULL;
+    }
+    if (res == 0) {
+        // __file__ isn't set.  There are several reasons why this might
+        // be so, most of them valid reasons.  If it's the __main__
+        // module then we're running the REPL or with -c.  Otherwise
+        // it's a namespace package or other module with a loader that
+        // isn't disk-based.  It could also be that a user created
+        // a module manually but without manually setting __file__.
+        Py_RETURN_NONE;
     }
     if (!PyUnicode_Check(fileobj)) {
         Py_DECREF(fileobj);
-        goto error;
+        Py_RETURN_NONE;
     }
     return fileobj;
+}
 
-error:
-    if (!PyErr_Occurred()) {
+PyObject*
+PyModule_GetFilenameObject(PyObject *mod)
+{
+    PyObject *fileobj = _PyModule_GetFilenameObject(mod);
+    if (fileobj == NULL) {
+        return NULL;
+    }
+    if (fileobj == Py_None) {
         PyErr_SetString(PyExc_SystemError, "module filename missing");
+        return NULL;
     }
-    return NULL;
+    return fileobj;
 }
 
 const char *
@@ -648,6 +667,37 @@ PyModule_GetFilename(PyObject *m)
     return utf8;
 }
 
+Py_ssize_t
+_PyModule_GetFilenameUTF8(PyObject *mod, char *buffer, Py_ssize_t maxlen)
+{
+    // We "return" an empty string for an invalid module
+    // and for a missing, empty, or invalid filename.
+    assert(maxlen >= 0);
+    Py_ssize_t size = -1;
+    PyObject *filenameobj = _PyModule_GetFilenameObject(mod);
+    if (filenameobj == NULL) {
+        return -1;
+    }
+    if (filenameobj == Py_None) {
+        // It is missing or invalid.
+        buffer[0] = '\0';
+        size = 0;
+    }
+    else {
+        const char *filename = PyUnicode_AsUTF8AndSize(filenameobj, &size);
+        assert(size >= 0);
+        if (size > maxlen) {
+            size = -1;
+            PyErr_SetString(PyExc_ValueError, "__file__ too long");
+        }
+        else {
+            (void)strcpy(buffer, filename);
+        }
+    }
+    Py_DECREF(filenameobj);
+    return size;
+}
+
 PyModuleDef*
 PyModule_GetDef(PyObject* m)
 {