Also updates calls in collections, doctest, enum, and typing modules to use _getframemodulename first when available.
It is not guaranteed to exist in all implementations of Python.
+.. function:: _getframemodulename([depth])
+
+ Return the name of a module from the call stack. If optional integer *depth*
+ is given, return the module that many calls below the top of the stack. If
+ that is deeper than the call stack, or if the module is unidentifiable,
+ ``None`` is returned. The default for *depth* is zero, returning the
+ module at the top of the call stack.
+
+ .. audit-event:: sys._getframemodulename depth sys._getframemodulename
+
+ .. impl-detail::
+
+ This function should be used for internal and specialized purposes only.
+ It is not guaranteed to exist in all implementations of Python.
+
+
.. function:: getprofile()
.. index::
try:
from collections import namedtuple as _namedtuple
- DecimalTuple = _namedtuple('DecimalTuple', 'sign digits exponent')
+ DecimalTuple = _namedtuple('DecimalTuple', 'sign digits exponent', module='decimal')
except ImportError:
DecimalTuple = lambda *args: args
# specified a particular module.
if module is None:
try:
- module = _sys._getframe(1).f_globals.get('__name__', '__main__')
- except (AttributeError, ValueError):
- pass
+ module = _sys._getframemodulename(1) or '__main__'
+ except AttributeError:
+ try:
+ module = _sys._getframe(1).f_globals.get('__name__', '__main__')
+ except (AttributeError, ValueError):
+ pass
if module is not None:
result.__module__ = module
elif isinstance(module, str):
return __import__(module, globals(), locals(), ["*"])
elif module is None:
- return sys.modules[sys._getframe(depth).f_globals['__name__']]
+ try:
+ try:
+ return sys.modules[sys._getframemodulename(depth)]
+ except AttributeError:
+ return sys.modules[sys._getframe(depth).f_globals['__name__']]
+ except KeyError:
+ pass
else:
raise TypeError("Expected a module, string, or None")
member_name, member_value = item
classdict[member_name] = member_value
- # TODO: replace the frame hack if a blessed way to know the calling
- # module is ever developed
if module is None:
try:
- module = sys._getframe(2).f_globals['__name__']
- except (AttributeError, ValueError, KeyError):
- pass
+ module = sys._getframemodulename(2)
+ except AttributeError:
+ # Fall back on _getframe if _getframemodulename is missing
+ try:
+ module = sys._getframe(2).f_globals['__name__']
+ except (AttributeError, ValueError, KeyError):
+ pass
if module is None:
_make_class_unpicklable(classdict)
else:
sys._getframe()
+def test_sys_getframemodulename():
+ import sys
+
+ def hook(event, args):
+ if event.startswith("sys."):
+ print(event, *args)
+
+ sys.addaudithook(hook)
+ sys._getframemodulename()
+
+
def test_threading():
import _thread
self.assertEqual(actual, expected)
+ def test_sys_getframemodulename(self):
+ returncode, events, stderr = self.run_python("test_sys_getframemodulename")
+ if returncode:
+ self.fail(stderr)
+
+ if support.verbose:
+ print(*events, sep='\n')
+ actual = [(ev[0], ev[2]) for ev in events]
+ expected = [("sys._getframemodulename", "0")]
+
+ self.assertEqual(actual, expected)
+
def test_threading(self):
returncode, events, stderr = self.run_python("test_threading")
is sys._getframe().f_code
)
+ def test_getframemodulename(self):
+ # Default depth gets ourselves
+ self.assertEqual(__name__, sys._getframemodulename())
+ self.assertEqual("unittest.case", sys._getframemodulename(1))
+ i = 0
+ f = sys._getframe(i)
+ while f:
+ self.assertEqual(
+ f.f_globals['__name__'],
+ sys._getframemodulename(i) or '__main__'
+ )
+ i += 1
+ f2 = f.f_back
+ try:
+ f = sys._getframe(i)
+ except ValueError:
+ break
+ self.assertIs(f, f2)
+ self.assertIsNone(sys._getframemodulename(i))
+
# sys._current_frames() is a CPython-only gimmick.
@threading_helper.reap_threads
@threading_helper.requires_working_threading()
def _caller(depth=1, default='__main__'):
+ try:
+ return sys._getframemodulename(depth + 1) or default
+ except AttributeError: # For platforms without _getframemodulename()
+ pass
try:
return sys._getframe(depth + 1).f_globals.get('__name__', default)
except (AttributeError, ValueError): # For platforms without _getframe()
- return None
-
+ pass
+ return None
def _allow_reckless_class_checks(depth=3):
"""Allow instance and class checks for special stdlib modules.
--- /dev/null
+Ensure runtime-created collections have the correct module name using
+the newly added (internal) :func:`sys._getframemodulename`.
op->func_doc = Py_NewRef(Py_None);
op->func_dict = NULL;
op->func_weakreflist = NULL;
- op->func_module = NULL;
+ op->func_module = Py_XNewRef(PyDict_GetItem(op->func_globals, &_Py_ID(__name__)));
+ if (!op->func_module) {
+ PyErr_Clear();
+ }
op->func_annotations = NULL;
op->vectorcall = _PyFunction_Vectorcall;
op->func_version = 0;
return sys_is_stack_trampoline_active_impl(module);
}
+PyDoc_STRVAR(sys__getframemodulename__doc__,
+"_getframemodulename($module, /, depth=0)\n"
+"--\n"
+"\n"
+"Return the name of the module for a calling frame.\n"
+"\n"
+"The default depth returns the module containing the call to this API.\n"
+"A more typical use in a library will pass a depth of 1 to get the user\'s\n"
+"module rather than the library module.\n"
+"\n"
+"If no frame, module, or name can be found, returns None.");
+
+#define SYS__GETFRAMEMODULENAME_METHODDEF \
+ {"_getframemodulename", _PyCFunction_CAST(sys__getframemodulename), METH_FASTCALL|METH_KEYWORDS, sys__getframemodulename__doc__},
+
+static PyObject *
+sys__getframemodulename_impl(PyObject *module, int depth);
+
+static PyObject *
+sys__getframemodulename(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
+ PyObject *ob_item[NUM_KEYWORDS];
+ } _kwtuple = {
+ .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
+ .ob_item = { &_Py_ID(depth), },
+ };
+ #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[] = {"depth", NULL};
+ static _PyArg_Parser _parser = {
+ .keywords = _keywords,
+ .fname = "_getframemodulename",
+ .kwtuple = KWTUPLE,
+ };
+ #undef KWTUPLE
+ PyObject *argsbuf[1];
+ Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0;
+ int depth = 0;
+
+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf);
+ if (!args) {
+ goto exit;
+ }
+ if (!noptargs) {
+ goto skip_optional_pos;
+ }
+ depth = _PyLong_AsInt(args[0]);
+ if (depth == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+skip_optional_pos:
+ return_value = sys__getframemodulename_impl(module, depth);
+
+exit:
+ return return_value;
+}
+
#ifndef SYS_GETWINDOWSVERSION_METHODDEF
#define SYS_GETWINDOWSVERSION_METHODDEF
#endif /* !defined(SYS_GETWINDOWSVERSION_METHODDEF) */
#ifndef SYS_GETANDROIDAPILEVEL_METHODDEF
#define SYS_GETANDROIDAPILEVEL_METHODDEF
#endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */
-/*[clinic end generated code: output=b32b444538dfd354 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=5c761f14326ced54 input=a9049054013a1b77]*/
}
+/*[clinic input]
+sys._getframemodulename
+
+ depth: int = 0
+
+Return the name of the module for a calling frame.
+
+The default depth returns the module containing the call to this API.
+A more typical use in a library will pass a depth of 1 to get the user's
+module rather than the library module.
+
+If no frame, module, or name can be found, returns None.
+[clinic start generated code]*/
+
+static PyObject *
+sys__getframemodulename_impl(PyObject *module, int depth)
+/*[clinic end generated code: output=1d70ef691f09d2db input=d4f1a8ed43b8fb46]*/
+{
+ if (PySys_Audit("sys._getframemodulename", "i", depth) < 0) {
+ return NULL;
+ }
+ _PyInterpreterFrame *f = _PyThreadState_GET()->cframe->current_frame;
+ while (f && (_PyFrame_IsIncomplete(f) || depth-- > 0)) {
+ f = f->previous;
+ }
+ if (f == NULL || f->f_funcobj == NULL) {
+ Py_RETURN_NONE;
+ }
+ PyObject *r = PyFunction_GetModule(f->f_funcobj);
+ if (!r) {
+ PyErr_Clear();
+ r = Py_None;
+ }
+ return Py_NewRef(r);
+}
+
+
static PyMethodDef sys_methods[] = {
/* Might as well keep this in alphabetic order */
SYS_ADDAUDITHOOK_METHODDEF
{"getsizeof", _PyCFunction_CAST(sys_getsizeof),
METH_VARARGS | METH_KEYWORDS, getsizeof_doc},
SYS__GETFRAME_METHODDEF
+ SYS__GETFRAMEMODULENAME_METHODDEF
SYS_GETWINDOWSVERSION_METHODDEF
SYS__ENABLELEGACYWINDOWSFSENCODING_METHODDEF
SYS_INTERN_METHODDEF