From ab8a3c66353b8338ceed09a262c4af964eb54fd6 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 26 Jan 2022 20:00:55 -0800 Subject: [PATCH 1/8] make it exist --- Doc/library/sys.rst | 20 ++++++++ .../2022-01-26-20-00-45.bpo-46543.oIulvu.rst | 1 + Python/clinic/sysmodule.c.h | 48 ++++++++++++++++++- Python/sysmodule.c | 44 +++++++++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-01-26-20-00-45.bpo-46543.oIulvu.rst diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 5e47201f88eae13..c6fafc15605ef4b 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -758,6 +758,26 @@ always available. It is not guaranteed to exist in all implementations of Python. +.. function:: _getfunc(depth=1) + + Return a function object from the call stack. If optional integer *depth* is + positive, return the function object that many calls below the top of the stack. If + that is deeper than the call stack, :exc:`ValueError` is raised. If + *depth* is zero, the function returns the currently executing function. + + Unlike the similar :func:`_getframe`, this does not create a frame object + when called, making it less expensive to call. + + .. versionadded:: 3.11 + + .. audit-event:: sys._getfunc "" sys._getfunc + + .. 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:: diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-01-26-20-00-45.bpo-46543.oIulvu.rst b/Misc/NEWS.d/next/Core and Builtins/2022-01-26-20-00-45.bpo-46543.oIulvu.rst new file mode 100644 index 000000000000000..b41c09824d6fbbc --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-01-26-20-00-45.bpo-46543.oIulvu.rst @@ -0,0 +1 @@ +Add :func:`sys._getfunc`. Patch by Jelle Zijlstra. diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index ce5390c8a1e5880..127087424bbb935 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -787,6 +787,52 @@ sys_getallocatedblocks(PyObject *module, PyObject *Py_UNUSED(ignored)) return return_value; } +PyDoc_STRVAR(sys__getfunc__doc__, +"_getfunc($module, depth=1, /)\n" +"--\n" +"\n" +"Return a function object from the call stack.\n" +"\n" +"If optional integer depth is 1 or more, return the function object that many\n" +"calls below the top of the stack. If that is deeper than the call\n" +"stack, ValueError is raised. If depth is 0, return the current function\n" +"object.\n" +"\n" +"This is similar to sys._getframe() but cheaper because it does not\n" +"create a full frame object.\n" +"\n" +"This function should be used for internal and specialized purposes\n" +"only."); + +#define SYS__GETFUNC_METHODDEF \ + {"_getfunc", (PyCFunction)(void(*)(void))sys__getfunc, METH_FASTCALL, sys__getfunc__doc__}, + +static PyObject * +sys__getfunc_impl(PyObject *module, int depth); + +static PyObject * +sys__getfunc(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + int depth = 1; + + if (!_PyArg_CheckPositional("_getfunc", nargs, 0, 1)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + depth = _PyLong_AsInt(args[0]); + if (depth == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional: + return_value = sys__getfunc_impl(module, depth); + +exit: + return return_value; +} + PyDoc_STRVAR(sys__getframe__doc__, "_getframe($module, depth=0, /)\n" "--\n" @@ -1014,4 +1060,4 @@ sys_getandroidapilevel(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF #define SYS_GETANDROIDAPILEVEL_METHODDEF #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */ -/*[clinic end generated code: output=60756bc6f683e0c8 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=357e73ba2ac2bcc7 input=a9049054013a1b77]*/ diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 7597ea2ea9e495a..01c0aa1d34e7358 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1799,6 +1799,49 @@ sys_getallocatedblocks_impl(PyObject *module) } +/*[clinic input] +sys._getfunc + + depth: int = 1 + / + +Return a function object from the call stack. + +If optional integer depth is 1 or more, return the function object that many +calls below the top of the stack. If that is deeper than the call +stack, ValueError is raised. If depth is 0, return the current function +object. + +This is similar to sys._getframe() but cheaper because it does not +create a full frame object. + +This function should be used for internal and specialized purposes +only. +[clinic start generated code]*/ + +static PyObject * +sys__getfunc_impl(PyObject *module, int depth) +/*[clinic end generated code: output=6a4f9e993f6c96d1 input=3c79a6f4642a5ba5]*/ +{ + PyThreadState *tstate = _PyThreadState_GET(); + InterpreterFrame *frame = tstate->cframe->current_frame; + + if (_PySys_Audit(tstate, "sys._getfunc", NULL) < 0) { + return NULL; + } + + while (depth > 0 && frame != NULL) { + frame = frame->previous; + --depth; + } + if (frame == NULL) { + _PyErr_SetString(tstate, PyExc_ValueError, + "call stack is not deep enough"); + return NULL; + } + return _Py_XNewRef(frame->f_func); +} + /*[clinic input] sys._getframe @@ -2007,6 +2050,7 @@ static PyMethodDef sys_methods[] = { {"getsizeof", (PyCFunction)(void(*)(void))sys_getsizeof, METH_VARARGS | METH_KEYWORDS, getsizeof_doc}, SYS__GETFRAME_METHODDEF + SYS__GETFUNC_METHODDEF SYS_GETWINDOWSVERSION_METHODDEF SYS__ENABLELEGACYWINDOWSFSENCODING_METHODDEF SYS_INTERN_METHODDEF From adb735f8874ec344c6fbd4bb2925ebe67fffb6f9 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 26 Jan 2022 20:02:58 -0800 Subject: [PATCH 2/8] basic test --- Lib/test/test_sys.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 41c4618ad10d408..a7deb4794ad5be2 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -399,6 +399,11 @@ def test_getframe(self): is sys._getframe().f_code ) + def test_getfunc(self): + self.assertRaises(TypeError, sys._getfunc, 42, 42) + self.assertRaises(ValueError, sys._getfunc, 2000000000) + self.assertIs(SysModuleTest.test_getfunc, sys._getfunc(0)) + # sys._current_frames() is a CPython-only gimmick. @threading_helper.reap_threads def test_current_frames(self): From a79fe75f9e57777191d200a48c0d5103649a9ae2 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 26 Jan 2022 20:11:07 -0800 Subject: [PATCH 3/8] more tests --- Lib/test/sys_getfunc.py | 3 +++ Lib/test/test_sys.py | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 Lib/test/sys_getfunc.py diff --git a/Lib/test/sys_getfunc.py b/Lib/test/sys_getfunc.py new file mode 100644 index 000000000000000..7d4b02b23c4962e --- /dev/null +++ b/Lib/test/sys_getfunc.py @@ -0,0 +1,3 @@ +import sys + +func = sys._getfunc(0) diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index a7deb4794ad5be2..007c54cfa3907d4 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -404,6 +404,22 @@ def test_getfunc(self): self.assertRaises(ValueError, sys._getfunc, 2000000000) self.assertIs(SysModuleTest.test_getfunc, sys._getfunc(0)) + def save_parent(): + return sys._getfunc() + + self.assertIs(SysModuleTest.test_getfunc, save_parent()) + + class X: + func = sys._getfunc(0) + func2 = save_parent() + + self.assertEqual(X.func.__name__, "X") + self.assertIs(X.func, X.func2) + + from test import sys_getfunc + + self.assertEqual(sys_getfunc.func.__name__, "") + # sys._current_frames() is a CPython-only gimmick. @threading_helper.reap_threads def test_current_frames(self): From 1cf14c5c7cb9734b5b6278aa645e6ac8f9926ad9 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 26 Jan 2022 20:16:34 -0800 Subject: [PATCH 4/8] replace some callsites --- Lib/collections/__init__.py | 6 +++--- Lib/doctest.py | 2 +- Lib/enum.py | 2 +- Lib/typing.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index fa8b30985a43502..9408f8edc6f1c39 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -490,12 +490,12 @@ def __getnewargs__(self): # For pickling to work, the __module__ variable needs to be set to the frame # where the named tuple is created. Bypass this step in environments where - # sys._getframe is not defined (Jython for example) or sys._getframe is not - # defined for arguments greater than 0 (IronPython), or where the user has + # sys._getfunc is not defined or sys._getfunc is not + # defined for arguments greater than 0, or where the user has # specified a particular module. if module is None: try: - module = _sys._getframe(1).f_globals.get('__name__', '__main__') + module = _sys._getfunc(1).__globals__.get('__name__', '__main__') except (AttributeError, ValueError): pass if module is not None: diff --git a/Lib/doctest.py b/Lib/doctest.py index 4735b59852685c9..02f35be057b3e3a 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -207,7 +207,7 @@ def _normalize_module(module, depth=2): elif isinstance(module, str): return __import__(module, globals(), locals(), ["*"]) elif module is None: - return sys.modules[sys._getframe(depth).f_globals['__name__']] + return sys.modules[sys._getfunc(depth).__globals__['__name__']] else: raise TypeError("Expected a module, string, or None") diff --git a/Lib/enum.py b/Lib/enum.py index 85245c95f9a9c71..460d4a86b3c8a2d 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -904,7 +904,7 @@ def _create_(cls, class_name, names, *, module=None, qualname=None, type=None, s # module is ever developed if module is None: try: - module = sys._getframe(2).f_globals['__name__'] + module = sys._getfunc(2).__globals__['__name__'] except (AttributeError, ValueError, KeyError): pass if module is None: diff --git a/Lib/typing.py b/Lib/typing.py index 450cd7b51184efc..441c2ea5e43e4fc 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1436,8 +1436,8 @@ def _no_init_or_replace_init(self, *args, **kwargs): def _caller(depth=1, default='__main__'): try: - return sys._getframe(depth + 1).f_globals.get('__name__', default) - except (AttributeError, ValueError): # For platforms without _getframe() + return sys._getfunc(depth + 1).__globals__.get('__name__', default) + except (AttributeError, ValueError): # For platforms without _getfunc() return None From b7245ae2d2dfec9febd086dd5b2c904def20c67c Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 26 Jan 2022 20:41:33 -0800 Subject: [PATCH 5/8] cast --- Python/sysmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 01c0aa1d34e7358..6ccb26df4adaa69 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1839,7 +1839,7 @@ sys__getfunc_impl(PyObject *module, int depth) "call stack is not deep enough"); return NULL; } - return _Py_XNewRef(frame->f_func); + return _Py_XNewRef((PyObject *)frame->f_func); } /*[clinic input] From 0fa97c2006bdd0f3e5bcfb74540078cebfa76674 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 7 Feb 2022 22:23:17 -0800 Subject: [PATCH 6/8] rename it --- Doc/library/sys.rst | 4 ++-- Lib/collections/__init__.py | 4 ++-- Lib/doctest.py | 2 +- Lib/enum.py | 2 +- Lib/test/sys_getcaller.py | 3 +++ Lib/test/sys_getfunc.py | 3 --- Lib/test/test_sys.py | 18 +++++++++--------- Lib/typing.py | 4 ++-- .../2022-01-26-20-00-45.bpo-46543.oIulvu.rst | 2 +- Python/clinic/sysmodule.c.h | 8 ++++---- Python/sysmodule.c | 4 ++-- 11 files changed, 27 insertions(+), 27 deletions(-) create mode 100644 Lib/test/sys_getcaller.py delete mode 100644 Lib/test/sys_getfunc.py diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index c6fafc15605ef4b..2ec846b2d8a0ab5 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -758,7 +758,7 @@ always available. It is not guaranteed to exist in all implementations of Python. -.. function:: _getfunc(depth=1) +.. function:: _getcaller(depth=1) Return a function object from the call stack. If optional integer *depth* is positive, return the function object that many calls below the top of the stack. If @@ -770,7 +770,7 @@ always available. .. versionadded:: 3.11 - .. audit-event:: sys._getfunc "" sys._getfunc + .. audit-event:: sys._getcaller "" sys._getcaller .. impl-detail:: diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index 9408f8edc6f1c39..2911ac00de0f055 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -490,12 +490,12 @@ def __getnewargs__(self): # For pickling to work, the __module__ variable needs to be set to the frame # where the named tuple is created. Bypass this step in environments where - # sys._getfunc is not defined or sys._getfunc is not + # sys._getcaller is not defined or sys._getcaller is not # defined for arguments greater than 0, or where the user has # specified a particular module. if module is None: try: - module = _sys._getfunc(1).__globals__.get('__name__', '__main__') + module = _sys._getcaller(1).__globals__.get('__name__', '__main__') except (AttributeError, ValueError): pass if module is not None: diff --git a/Lib/doctest.py b/Lib/doctest.py index 02f35be057b3e3a..5df59333887d155 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -207,7 +207,7 @@ def _normalize_module(module, depth=2): elif isinstance(module, str): return __import__(module, globals(), locals(), ["*"]) elif module is None: - return sys.modules[sys._getfunc(depth).__globals__['__name__']] + return sys.modules[sys._getcaller(depth).__globals__['__name__']] else: raise TypeError("Expected a module, string, or None") diff --git a/Lib/enum.py b/Lib/enum.py index 460d4a86b3c8a2d..94eed268440f121 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -904,7 +904,7 @@ def _create_(cls, class_name, names, *, module=None, qualname=None, type=None, s # module is ever developed if module is None: try: - module = sys._getfunc(2).__globals__['__name__'] + module = sys._getcaller(2).__globals__['__name__'] except (AttributeError, ValueError, KeyError): pass if module is None: diff --git a/Lib/test/sys_getcaller.py b/Lib/test/sys_getcaller.py new file mode 100644 index 000000000000000..b8962180c2e59e5 --- /dev/null +++ b/Lib/test/sys_getcaller.py @@ -0,0 +1,3 @@ +import sys + +func = sys._getcaller(0) diff --git a/Lib/test/sys_getfunc.py b/Lib/test/sys_getfunc.py deleted file mode 100644 index 7d4b02b23c4962e..000000000000000 --- a/Lib/test/sys_getfunc.py +++ /dev/null @@ -1,3 +0,0 @@ -import sys - -func = sys._getfunc(0) diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 007c54cfa3907d4..8a143746496f1b0 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -399,26 +399,26 @@ def test_getframe(self): is sys._getframe().f_code ) - def test_getfunc(self): - self.assertRaises(TypeError, sys._getfunc, 42, 42) - self.assertRaises(ValueError, sys._getfunc, 2000000000) - self.assertIs(SysModuleTest.test_getfunc, sys._getfunc(0)) + def test_getcaller(self): + self.assertRaises(TypeError, sys._getcaller, 42, 42) + self.assertRaises(ValueError, sys._getcaller, 2000000000) + self.assertIs(SysModuleTest.test_getcaller, sys._getcaller(0)) def save_parent(): - return sys._getfunc() + return sys._getcaller(1) - self.assertIs(SysModuleTest.test_getfunc, save_parent()) + self.assertIs(SysModuleTest.test_getcaller, save_parent()) class X: - func = sys._getfunc(0) + func = sys._getcaller(0) func2 = save_parent() self.assertEqual(X.func.__name__, "X") self.assertIs(X.func, X.func2) - from test import sys_getfunc + from test import sys_getcaller - self.assertEqual(sys_getfunc.func.__name__, "") + self.assertEqual(sys_getcaller.func.__name__, "") # sys._current_frames() is a CPython-only gimmick. @threading_helper.reap_threads diff --git a/Lib/typing.py b/Lib/typing.py index 441c2ea5e43e4fc..7022206522933d7 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1436,8 +1436,8 @@ def _no_init_or_replace_init(self, *args, **kwargs): def _caller(depth=1, default='__main__'): try: - return sys._getfunc(depth + 1).__globals__.get('__name__', default) - except (AttributeError, ValueError): # For platforms without _getfunc() + return sys._getcaller(depth + 1).__globals__.get('__name__', default) + except (AttributeError, ValueError): # For platforms without _getcaller() return None diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-01-26-20-00-45.bpo-46543.oIulvu.rst b/Misc/NEWS.d/next/Core and Builtins/2022-01-26-20-00-45.bpo-46543.oIulvu.rst index b41c09824d6fbbc..136f2a7ccba342e 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2022-01-26-20-00-45.bpo-46543.oIulvu.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2022-01-26-20-00-45.bpo-46543.oIulvu.rst @@ -1 +1 @@ -Add :func:`sys._getfunc`. Patch by Jelle Zijlstra. +Add :func:`sys._getcaller`. Patch by Jelle Zijlstra. diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index 127087424bbb935..5ed900b4b29d80b 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -788,7 +788,7 @@ sys_getallocatedblocks(PyObject *module, PyObject *Py_UNUSED(ignored)) } PyDoc_STRVAR(sys__getfunc__doc__, -"_getfunc($module, depth=1, /)\n" +"_getcaller($module, depth=1, /)\n" "--\n" "\n" "Return a function object from the call stack.\n" @@ -805,18 +805,18 @@ PyDoc_STRVAR(sys__getfunc__doc__, "only."); #define SYS__GETFUNC_METHODDEF \ - {"_getfunc", (PyCFunction)(void(*)(void))sys__getfunc, METH_FASTCALL, sys__getfunc__doc__}, + {"_getcaller", (PyCFunction)(void(*)(void))sys__getcaller, METH_FASTCALL, sys__getfunc__doc__}, static PyObject * sys__getfunc_impl(PyObject *module, int depth); static PyObject * -sys__getfunc(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +sys__getcaller(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; int depth = 1; - if (!_PyArg_CheckPositional("_getfunc", nargs, 0, 1)) { + if (!_PyArg_CheckPositional("_getcaller", nargs, 0, 1)) { goto exit; } if (nargs < 1) { diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 6ccb26df4adaa69..b7180960b807f48 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1800,7 +1800,7 @@ sys_getallocatedblocks_impl(PyObject *module) /*[clinic input] -sys._getfunc +sys._getcaller depth: int = 1 / @@ -1826,7 +1826,7 @@ sys__getfunc_impl(PyObject *module, int depth) PyThreadState *tstate = _PyThreadState_GET(); InterpreterFrame *frame = tstate->cframe->current_frame; - if (_PySys_Audit(tstate, "sys._getfunc", NULL) < 0) { + if (_PySys_Audit(tstate, "sys._getcaller", NULL) < 0) { return NULL; } From 6b060f1b4be40ac77901eb1129b26745da391c36 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 8 Feb 2022 06:44:09 -0800 Subject: [PATCH 7/8] adjust docs and default --- Doc/library/sys.rst | 13 +++++++------ Python/sysmodule.c | 9 +++++---- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 2ec846b2d8a0ab5..1c08e5b18ed1033 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -750,6 +750,10 @@ always available. that is deeper than the call stack, :exc:`ValueError` is raised. The default for *depth* is zero, returning the frame at the top of the call stack. + Unlike the similar :func:`_getcaller` (new in Python 3.11), this returns a + full frame object, making it more expensive to execute and harder to port + to alternative implementations of Python. + .. audit-event:: sys._getframe "" sys._getframe .. impl-detail:: @@ -758,15 +762,12 @@ always available. It is not guaranteed to exist in all implementations of Python. -.. function:: _getcaller(depth=1) +.. function:: _getcaller([depth]) Return a function object from the call stack. If optional integer *depth* is positive, return the function object that many calls below the top of the stack. If - that is deeper than the call stack, :exc:`ValueError` is raised. If - *depth* is zero, the function returns the currently executing function. - - Unlike the similar :func:`_getframe`, this does not create a frame object - when called, making it less expensive to call. + that is deeper than the call stack, :exc:`ValueError` is raised. The default + for *depth* is zero, returning the currently executing function. .. versionadded:: 3.11 diff --git a/Python/sysmodule.c b/Python/sysmodule.c index b7180960b807f48..77eed729482ee20 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1802,7 +1802,7 @@ sys_getallocatedblocks_impl(PyObject *module) /*[clinic input] sys._getcaller - depth: int = 1 + depth: int = 0 / Return a function object from the call stack. @@ -1812,9 +1812,6 @@ calls below the top of the stack. If that is deeper than the call stack, ValueError is raised. If depth is 0, return the current function object. -This is similar to sys._getframe() but cheaper because it does not -create a full frame object. - This function should be used for internal and specialized purposes only. [clinic start generated code]*/ @@ -1855,6 +1852,10 @@ calls below the top of the stack. If that is deeper than the call stack, ValueError is raised. The default for depth is zero, returning the frame at the top of the call stack. +Unlike the similar sys._getcaller, this returns a full frame object, +making it more expensive to execute and harder to port to alternative +implementations of Python. + This function should be used for internal and specialized purposes only. [clinic start generated code]*/ From b7985d538e7c697335680c3beb95da611e1c1dd1 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 8 Feb 2022 06:57:08 -0800 Subject: [PATCH 8/8] fix argument clinic --- Python/clinic/sysmodule.c.h | 23 ++++++++++++----------- Python/sysmodule.c | 8 ++++---- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index 5ed900b4b29d80b..7f8e2d570b3373b 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -787,8 +787,8 @@ sys_getallocatedblocks(PyObject *module, PyObject *Py_UNUSED(ignored)) return return_value; } -PyDoc_STRVAR(sys__getfunc__doc__, -"_getcaller($module, depth=1, /)\n" +PyDoc_STRVAR(sys__getcaller__doc__, +"_getcaller($module, depth=0, /)\n" "--\n" "\n" "Return a function object from the call stack.\n" @@ -798,23 +798,20 @@ PyDoc_STRVAR(sys__getfunc__doc__, "stack, ValueError is raised. If depth is 0, return the current function\n" "object.\n" "\n" -"This is similar to sys._getframe() but cheaper because it does not\n" -"create a full frame object.\n" -"\n" "This function should be used for internal and specialized purposes\n" "only."); -#define SYS__GETFUNC_METHODDEF \ - {"_getcaller", (PyCFunction)(void(*)(void))sys__getcaller, METH_FASTCALL, sys__getfunc__doc__}, +#define SYS__GETCALLER_METHODDEF \ + {"_getcaller", (PyCFunction)(void(*)(void))sys__getcaller, METH_FASTCALL, sys__getcaller__doc__}, static PyObject * -sys__getfunc_impl(PyObject *module, int depth); +sys__getcaller_impl(PyObject *module, int depth); static PyObject * sys__getcaller(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - int depth = 1; + int depth = 0; if (!_PyArg_CheckPositional("_getcaller", nargs, 0, 1)) { goto exit; @@ -827,7 +824,7 @@ sys__getcaller(PyObject *module, PyObject *const *args, Py_ssize_t nargs) goto exit; } skip_optional: - return_value = sys__getfunc_impl(module, depth); + return_value = sys__getcaller_impl(module, depth); exit: return return_value; @@ -844,6 +841,10 @@ PyDoc_STRVAR(sys__getframe__doc__, "stack, ValueError is raised. The default for depth is zero, returning\n" "the frame at the top of the call stack.\n" "\n" +"Unlike the similar sys._getcaller, this returns a full frame object,\n" +"making it more expensive to execute and harder to port to alternative\n" +"implementations of Python.\n" +"\n" "This function should be used for internal and specialized purposes\n" "only."); @@ -1060,4 +1061,4 @@ sys_getandroidapilevel(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF #define SYS_GETANDROIDAPILEVEL_METHODDEF #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */ -/*[clinic end generated code: output=357e73ba2ac2bcc7 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=32a5bed6e0473b5d input=a9049054013a1b77]*/ diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 917754facb99617..b656369d4710cc0 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1817,8 +1817,8 @@ only. [clinic start generated code]*/ static PyObject * -sys__getfunc_impl(PyObject *module, int depth) -/*[clinic end generated code: output=6a4f9e993f6c96d1 input=3c79a6f4642a5ba5]*/ +sys__getcaller_impl(PyObject *module, int depth) +/*[clinic end generated code: output=250f47adb2372e4a input=242cb11f8e6d7a60]*/ { PyThreadState *tstate = _PyThreadState_GET(); InterpreterFrame *frame = tstate->cframe->current_frame; @@ -1862,7 +1862,7 @@ only. static PyObject * sys__getframe_impl(PyObject *module, int depth) -/*[clinic end generated code: output=d438776c04d59804 input=c1be8a6464b11ee5]*/ +/*[clinic end generated code: output=d438776c04d59804 input=a57ecc9db9b721ad]*/ { PyThreadState *tstate = _PyThreadState_GET(); InterpreterFrame *frame = tstate->cframe->current_frame; @@ -2051,7 +2051,7 @@ static PyMethodDef sys_methods[] = { {"getsizeof", (PyCFunction)(void(*)(void))sys_getsizeof, METH_VARARGS | METH_KEYWORDS, getsizeof_doc}, SYS__GETFRAME_METHODDEF - SYS__GETFUNC_METHODDEF + SYS__GETCALLER_METHODDEF SYS_GETWINDOWSVERSION_METHODDEF SYS__ENABLELEGACYWINDOWSFSENCODING_METHODDEF SYS_INTERN_METHODDEF