Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 47 additions & 11 deletions cbits/python.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,17 +212,6 @@ void inline_py_Integer_FromPy(
}



static PyObject* AsyncError = 0;

PyObject* inline_py_AsyncError() {
if( AsyncError == 0 ) {
AsyncError = PyErr_NewException("inline_py.AsyncError", PyExc_BaseException, 0);
}
return AsyncError;
}


static Py_tss_t key_py_async = Py_tss_NEEDS_INIT;

void inline_py_init_state(void *stack) {
Expand Down Expand Up @@ -253,3 +242,50 @@ void inline_py_initialize(void) {
exit(1);
}
}

// ================================================================
// inline_python module

PyObject* inline_py_AsyncCancelled() {
static PyObject* AsyncCancelled = 0;
if( AsyncCancelled == 0 ) {
AsyncCancelled = PyErr_NewException("inline_python.AsyncCancelled", PyExc_BaseException, 0);
}
return AsyncCancelled;
}

static PyMethodDef inline_python_methods[] = {
{NULL, NULL, 0, NULL}
};

static int inline_python_module_exec(PyObject *m) {
// Initialize module only once
static int initialized = 0;
if( initialized ) {
return 0;
}
initialized = 1;
//
if (PyModule_AddObjectRef(m, "AsyncCancelled", inline_py_AsyncCancelled()) < 0) {
return -1;
}
return 0;
}

static PyModuleDef_Slot inline_python_module_slots[] = {
{Py_mod_exec, inline_python_module_exec},
{0, NULL}
};

static struct PyModuleDef inline_python_module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "inline_python",
.m_size = 0,
.m_slots = inline_python_module_slots,
.m_methods = inline_python_methods,
};

PyMODINIT_FUNC PyInit_inline_python(void)
{
return PyModuleDef_Init(&inline_python_module);
}
8 changes: 7 additions & 1 deletion include/inline-python.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,5 +101,11 @@ void* inline_py_get_state(void);



// ================================================================
// inline_python module
// ================================================================

PyMODINIT_FUNC PyInit_inline_python(void);

// Obtain class for async exception
PyObject* inline_py_AsyncError();
PyObject* inline_py_AsyncCancelled();
2 changes: 2 additions & 0 deletions inline-python.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ test-suite inline-python-tests
TST.Callbacks
TST.Roundtrip
TST.Util
TST.Module

test-suite inline-python-tests1
import: language, tests
Expand All @@ -148,6 +149,7 @@ test-suite inline-python-tests1
TST.Callbacks
TST.Roundtrip
TST.Util
TST.Module

benchmark pysmall
import: language
Expand Down
8 changes: 5 additions & 3 deletions src/Python/Internal/Eval.hs
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,8 @@ doInitializePythonIO = do
if( PyStatus_Exception(status) ) {
goto error;
};
// Make builting module visible to python
PyImport_AppendInittab("inline_python", PyInit_inline_python);
// Initialize interpreter
status = Py_InitializeFromConfig(&cfg);
if( PyStatus_Exception(status) ) {
Expand Down Expand Up @@ -651,7 +653,7 @@ cancelPy PyAsync{asyncTID=tid, asyncTidStack, asyncPyTID, asyncAlive} = do
False -> return 1
True -> [C.block| int {
int gil = PyGILState_Ensure();
int n = PyThreadState_SetAsyncExc($(uint64_t py_tid), inline_py_AsyncError());
int n = PyThreadState_SetAsyncExc($(uint64_t py_tid), inline_py_AsyncCancelled());
PyGILState_Release(gil);
return n;
}|]
Expand Down Expand Up @@ -787,10 +789,10 @@ convertPy2Haskell = runProgram $ do
p_type <- peekElemOff p_errors 0
-- NOTE: When we set exception using PyThreadState_SetAsyncExc
-- this field remains NULL on python<=3.11. In this case we
-- assume it's our AsyncError:
-- assume it's our AsyncCancelled:
p_value <- peekElemOff p_errors 1 >>= \case
NULL -> [CU.block| PyObject* {
PyObject *err_class = inline_py_AsyncError();
PyObject *err_class = inline_py_AsyncCancelled();
PyObject *tuple = PyTuple_New(0);
PyObject *err = PyObject_Call(err_class, tuple, NULL);
Py_DECREF(tuple);
Expand Down
29 changes: 29 additions & 0 deletions test/TST/Module.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
-- |
module TST.Module where

import Test.Tasty
import Test.Tasty.HUnit
import Python.Inline
import Python.Inline.QQ


tests :: TestTree
tests = testGroup "Builtin module"
[ testCase "Module exists" $ runPy [py_| import inline_python |]
, testCase "AsyncCancelled" $ runPy [py_|
import inline_python
assert issubclass(inline_python.AsyncCancelled, BaseException)
assert not issubclass(inline_python.AsyncCancelled, Exception)
|]
-- We want to check that inline_python types are stable under
-- reload using importlib.
, testCase "importlib.reload stable" $ runPy [py_|
import inline_python
import importlib
ty = inline_python.AsyncCancelled
err = ty()
importlib.reload(inline_python)
assert ty is inline_python.AsyncCancelled
assert isinstance(err, inline_python.AsyncCancelled)
|]
]
2 changes: 2 additions & 0 deletions test/exe/main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import TST.FromPy
import TST.ToPy
import TST.Callbacks
import TST.Roundtrip
import TST.Module
import Python.Inline

main :: IO ()
Expand All @@ -16,4 +17,5 @@ main = withPython $ defaultMain $ testGroup "PY"
, TST.ToPy.tests
, TST.Roundtrip.tests
, TST.Callbacks.tests
, TST.Module.tests
]
Loading