8000 Implement sys._set_exception() instead of using ctypes · python/cpython@79766c8 · GitHub
[go: up one dir, main page]

Skip to content

Commit 79766c8

Browse files
committed
Implement sys._set_exception() instead of using ctypes
1 parent c80e42b commit 79766c8

File tree

4 files changed

+154
-10
lines changed

4 files changed

+154
-10
lines changed

Lib/contextlib.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,6 @@
1515
"chdir"]
1616

1717

18-
_PyErr_SetHandledException = ctypes.pythonapi.PyErr_SetHandledException
19-
_PyErr_SetHandledException.argtypes = [ctypes.py_object]
20-
def _set_current_exception(exc):
21-
if exc is not None and not isinstance(exc, BaseException):
22-
raise TypeError(f"{exc!r} is not an exception")
23-
_PyErr_SetHandledException(exc)
24-
25-
2618
class AbstractContextManager(abc.ABC):
2719

2820
"""An abstract base class for context managers."""
@@ -178,7 +170,7 @@ def __exit__(self, typ, value, traceback):
178170
# context here. In order to make the context manager behave
179171
# like a normal function we set the current exception context
180172
# to what it was during the context manager's __enter__
181-
_set_current_exception(exc_context)
173+
sys._set_exception(exc_context)
182174
self.gen.throw(value)
183175
except StopIteration as exc:
184176
# Suppress StopIteration *unless* it's the same exception that

Lib/test/test_sys.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,71 @@ def f():
142142
self.assertIsInstance(e, ValueError)
143143
self.assertIs(exc, e)
144144

145+
class SetExceptionTests(unittest.TestCase):
146+
147+
def tearDown(self):
148+
# make sure we don't leave the global exception set
149+
sys._set_exception(None);
150+
151+
def test_set_exc_invalid_values(self):
152+
for x in (0, "1", b"2"):
153+
with self.assertRaises(TypeError):
154+
sys._set_exception(x);
155+
156+
def test_clear_exc(self):
157+
try:
158+
raise ValueError()
159+
except ValueError:
160+
self.assertIsInstance(sys.exception(), ValueError)
161+
sys._set_exception(None)
162+
self.assertIsNone(sys.exception())
163+
164+
def test_set_exc(self):
165+
exc = ValueError()
166+
self.assertIsNone(sys.exception())
167+
sys._set_exception(exc)
168+
self.assertIs(sys.exception(), exc)
169+
170+
def test_set_exc_replaced_by_new_exception_and_restored(self):
171+
exc = ValueError()
172+
sys._set_exception(exc)
173+
self.assertIs(sys.exception(), exc)
174+
try:
175+
raise TypeError()
176+
except TypeError:
177+
self.assertIsInstance(sys.exception(), TypeError)
178+
self.assertIs(sys.exception().__context__, exc)
179+
180+
self.assertIs(sys.exception(), exc)
181+
182+
def test_set_exc_popped_on_exit_except(self):
183+
exc = ValueError()
184+
try:
185+
raise TypeError()
186+
except TypeError:
187+
self.assertIsInstance(sys.exception(), TypeError)
188+
sys._set_exception(exc)
189+
self.assertIs(sys.exception(), exc)
190+
self.assertIsNone(sys.exception())
191+
192+
def test_cleared_exc_overridden_and_restored(self):
193+
try:
194+
raise ValueError()
195+
except ValueError:
196+
try:
197+
raise TypeError()
198+
except TypeError:
199+
self.assertIsInstance(sys.exception(), TypeError)
200+
sys._set_exception(None)
201+
self.assertIsNone(sys.exception())
202+
try:
203+
raise IndexError()
204+
except IndexError:
205+
self.assertIsInstance(sys.exception(), IndexError)
206+
self.assertIsNone(sys.exception().__context__)
207+
self.assertIsNone(sys.exception())
208+
self.assertIsInstance(sys.exception(), ValueError)
209+
self.assertIsNone(sys.exception())
145210

146211
class ExceptHookTest(unittest.TestCase):
147212

Python/clinic/sysmodule.c.h

Lines changed: 61 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/sysmodule.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,32 @@ sys_exception_impl(PyObject *module)
824824
Py_RETURN_NONE;
825825
}
826826

827+
/*[clinic input]
828+
sys._set_exception
829+
exception: object
830+
831+
Set the current exception.
832+
833+
Subsequent calls to sys.exception()/sys.exc_info() will return
834+
the provided exception until another exception is raised in the
835+
current thread or the execution stack returns to a frame where
836+
another exception is being handled.
837+
[clinic start generated code]*/
838+
839+
static PyObject *
840+
sys__set_exception_impl(PyObject *module, PyObject *exception)
841+
/*[clinic end generated code: output=39e119ee6b747085 input=46da3b45313a1cfa]*/
842+
{
843+
if (!Py_IsNone(exception) && !PyExceptionInstance_Check(exception)){
844+
PyErr_SetString(
845+
PyExc_TypeError,
846+
"must be an exception/None"
847+
);
848+
return NULL;
849+
}
850+
PyErr_SetHandledException(exception);
851+
Py_RETURN_NONE;
852+
}
827853

828854
/*[clinic input]
829855
sys.exc_info
@@ -2400,6 +2426,7 @@ static PyMethodDef sys_methods[] = {
24002426
SYS__CURRENT_EXCEPTIONS_METHODDEF
24012427
SYS_DISPLAYHOOK_METHODDEF
24022428
SYS_EXCEPTION_METHODDEF
2429+
SYS__SET_EXCEPTION_METHODDEF
24032430
SYS_EXC_INFO_METHODDEF
24042431
SYS_EXCEPTHOOK_METHODDEF
24052432
SYS_EXIT_METHODDEF

0 commit comments

Comments
 (0)
0