gh-132775: Add _PyModule_GetFilenameObject() and _PyModule_GetFilenameUTF8() (gh-132979)

They are derived from the existing `PyModule_GetFilenameObject().

They are used by a later change related to pickle and handling __main__.
This commit is contained in:
Eric Snow 2025-04-28 12:41:32 -06:00 committed by GitHub
parent 25186c2472
commit c17238251f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 66 additions and 10 deletions

View file

@ -47,6 +47,12 @@ static inline PyObject* _PyModule_GetDict(PyObject *mod) {
return dict; // borrowed reference 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_impl(PyModuleObject *m, PyObject *name, int suppress);
PyObject* _Py_module_getattro(PyObject *m, PyObject *name); PyObject* _Py_module_getattro(PyObject *m, PyObject *name);

View file

@ -607,32 +607,51 @@ PyModule_GetName(PyObject *m)
} }
PyObject* PyObject*
PyModule_GetFilenameObject(PyObject *mod) _PyModule_GetFilenameObject(PyObject *mod)
{ {
// We return None to indicate "not found" or "bogus".
if (!PyModule_Check(mod)) { if (!PyModule_Check(mod)) {
PyErr_BadArgument(); PyErr_BadArgument();
return NULL; return NULL;
} }
PyObject *dict = ((PyModuleObject *)mod)->md_dict; // borrowed reference PyObject *dict = ((PyModuleObject *)mod)->md_dict; // borrowed reference
if (dict == NULL) { if (dict == NULL) {
goto error; // The module has been tampered with.
Py_RETURN_NONE;
} }
PyObject *fileobj; PyObject *fileobj;
if (PyDict_GetItemRef(dict, &_Py_ID(__file__), &fileobj) <= 0) { int res = PyDict_GetItemRef(dict, &_Py_ID(__file__), &fileobj);
// error or not found if (res < 0) {
goto error; 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)) { if (!PyUnicode_Check(fileobj)) {
Py_DECREF(fileobj); Py_DECREF(fileobj);
goto error; Py_RETURN_NONE;
} }
return fileobj; return fileobj;
}
error: PyObject*
if (!PyErr_Occurred()) { PyModule_GetFilenameObject(PyObject *mod)
PyErr_SetString(PyExc_SystemError, "module filename missing"); {
PyObject *fileobj = _PyModule_GetFilenameObject(mod);
if (fileobj == NULL) {
return NULL;
} }
return NULL; if (fileobj == Py_None) {
PyErr_SetString(PyExc_SystemError, "module filename missing");
return NULL;
}
return fileobj;
} }
const char * const char *
@ -648,6 +667,37 @@ PyModule_GetFilename(PyObject *m)
return utf8; 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* PyModuleDef*
PyModule_GetDef(PyObject* m) PyModule_GetDef(PyObject* m)
{ {