@@ -40,6 +40,7 @@ import Control.Monad
40
40
import Control.Monad.Catch
41
41
import Control.Monad.IO.Class
42
42
import Control.Monad.Trans.Cont
43
+ import Data.Maybe
43
44
import Foreign.Concurrent qualified as GHC
44
45
import Foreign.Ptr
45
46
import Foreign.ForeignPtr
@@ -53,6 +54,7 @@ import System.IO.Unsafe
53
54
import Language.C.Inline qualified as C
54
55
import Language.C.Inline.Unsafe qualified as CU
55
56
57
+ import Python.Internal.CAPI
56
58
import Python.Internal.Types
57
59
import Python.Internal.Util
58
60
import Python.Internal.Program
@@ -231,8 +233,8 @@ callbackEnsurePyLock action = do
231
233
232
234
acquireLock :: ThreadId -> STM ()
233
235
acquireLock tid = readTVar globalPyLock >>= \ case
234
- LockUninialized -> error " Python is not started "
235
- LockFinalized -> error " Python is already stopped "
236
+ LockUninialized -> throwSTM PythonNotInitialized
237
+ LockFinalized -> throwSTM PythonIsFinalized
236
238
LockedByGC -> retry
237
239
LockUnlocked -> writeTVar globalPyLock $ Locked tid []
238
240
Locked t xs
@@ -241,20 +243,20 @@ acquireLock tid = readTVar globalPyLock >>= \case
241
243
242
244
grabLock :: ThreadId -> STM ()
243
245
grabLock tid = readTVar globalPyLock >>= \ case
244
- LockUninialized -> error " Python is not started "
245
- LockFinalized -> error " Python is already stopped "
246
+ LockUninialized -> throwSTM PythonNotInitialized
247
+ LockFinalized -> throwSTM PythonIsFinalized
246
248
LockedByGC -> retry
247
249
LockUnlocked -> writeTVar globalPyLock $ Locked tid []
248
250
Locked t xs -> writeTVar globalPyLock $ Locked tid (t : xs)
249
251
250
252
releaseLock :: ThreadId -> STM ()
251
253
releaseLock tid = readTVar globalPyLock >>= \ case
252
- LockUninialized -> error " Python is not started "
253
- LockFinalized -> error " Python is already stopped "
254
- LockUnlocked -> error " INTERNAL ERROR releasing unlocked "
255
- LockedByGC -> error " INTERNAL ERROR lock held by GC "
254
+ LockUninialized -> throwSTM PythonNotInitialized
255
+ LockFinalized -> throwSTM PythonIsFinalized
256
+ LockUnlocked -> throwSTM $ PyInternalError " releaseLock: releasing LockUnlocked "
257
+ LockedByGC -> throwSTM $ PyInternalError " releaseLock: releasing LockedByGC "
256
258
Locked t xs
257
- | t /= tid -> error " INTERNAL ERROR releasing wrong lock"
259
+ | t /= tid -> throwSTM $ PyInternalError " releaseLock: releasing wrong lock"
258
260
| otherwise -> writeTVar globalPyLock $! case xs of
259
261
[] -> LockUnlocked
260
262
t': ts -> Locked t' ts
@@ -290,8 +292,8 @@ doInializePython = do
290
292
-- First we need to grab global python lock on haskell side
291
293
join $ atomically $ do
292
294
readTVar globalPyState >>= \ case
293
- Finalized -> error " Python was already finalized "
294
- InitFailed -> error " Python was unable to initialize "
295
+ Finalized -> throwSTM PythonNotInitialized
296
+ InitFailed -> throwSTM PythonIsFinalized
295
297
InInitialization -> retry
296
298
InFinalization -> retry
297
299
Running1 {} -> pure $ pure ()
@@ -401,8 +403,8 @@ doInializePythonIO = do
401
403
402
404
doFinalizePython :: IO ()
403
405
doFinalizePython = join $ atomically $ readTVar globalPyState >>= \ case
404
- NotInitialized -> error " Python is not initialized "
405
- InitFailed -> error " Python failed to initialize "
406
+ NotInitialized -> throwSTM PythonNotInitialized
407
+ InitFailed -> throwSTM PythonIsFinalized
406
408
Finalized -> pure $ pure ()
407
409
InInitialization -> retry
408
410
InFinalization -> retry
@@ -419,8 +421,8 @@ doFinalizePython = join $ atomically $ readTVar globalPyState >>= \case
419
421
takeMVar resp
420
422
where
421
423
checkLock action = readTVar globalPyLock >>= \ case
422
- LockUninialized -> error " Internal error: Lock not initialized "
423
- LockFinalized -> error " Internal error: Lock is already finalized "
424
+ LockUninialized -> throwSTM $ PyInternalError " doFinalizePython LockUninialized "
425
+ LockFinalized -> throwSTM $ PyInternalError " doFinalizePython LockFinalized "
424
426
Locked {} -> retry
425
427
LockedByGC -> retry
426
428
LockUnlocked -> do
@@ -459,12 +461,12 @@ runPyInMain :: Py a -> IO a
459
461
runPyInMain py
460
462
-- Multithreaded RTS
461
463
| rtsSupportsBoundThreads = join $ atomically $ readTVar globalPyState >>= \ case
462
- NotInitialized -> error " Python is not initialized "
463
- InitFailed -> error " Python failed to initialize "
464
- Finalized -> error " Python is already finalized "
464
+ NotInitialized -> throwSTM PythonNotInitialized
465
+ InitFailed -> throwSTM PyInitializationFailed
466
+ Finalized -> throwSTM PythonIsFinalized
465
467
InInitialization -> retry
466
468
InFinalization -> retry
467
- Running1 -> error " INTERNAL ERROR "
469
+ Running1 -> throwSTM $ PyInternalError " runPyInMain: Running1 "
468
470
RunningN _ eval tid_main _ -> do
469
471
acquireLock tid_main
470
472
pure
@@ -572,10 +574,9 @@ convertHaskell2Py err = Py $ do
572
574
573
575
-- | Convert python exception to haskell exception. Should only be
574
576
-- called if there's unhandled python exception. Clears exception.
575
- convertPy2Haskell :: Py PyError
577
+ convertPy2Haskell :: Py PyException
576
578
convertPy2Haskell = runProgram $ do
577
579
p_errors <- withPyAllocaArray @ (Ptr PyObject ) 3
578
- p_len <- withPyAlloca @ CLong
579
580
-- Fetch error indicator
580
581
(p_type, p_value) <- progIO $ do
581
582
[CU. block | void {
@@ -587,48 +588,32 @@ convertPy2Haskell = runProgram $ do
587
588
-- Traceback is not used ATM
588
589
pure (p_type,p_value)
589
590
-- Convert exception type and value to strings.
590
- let pythonStr p = do
591
- p_str <- progIO [CU. block | PyObject* {
592
- PyObject *s = PyObject_Str($(PyObject *p));
593
- if( PyErr_Occurred() ) {
594
- PyErr_Clear();
595
- }
596
- return s;
597
- } |]
598
- case p_str of
599
- NULL -> abort UncovertablePyError
600
- _ -> pure p_str
601
- s_type <- takeOwnership =<< pythonStr p_type
602
- s_value <- takeOwnership =<< pythonStr p_value
603
- -- Convert to haskell strings
604
- let toString p = do
605
- c_str <- [CU. block | const char* {
606
- const char* s = PyUnicode_AsUTF8AndSize($(PyObject *p), $(long *p_len));
607
- if( PyErr_Occurred() ) {
608
- PyErr_Clear();
609
- }
610
- return s;
611
- } |]
612
- case c_str of
613
- NULL -> pure " "
614
- _ -> peekCString c_str
615
- progIO $ PyError <$> toString s_type <*> toString s_value
616
-
591
+ progPy $ do
592
+ s_type <- pyobjectStrAsHask p_type
593
+ s_value <- pyobjectStrAsHask p_value
594
+ incref p_value
595
+ exc <- newPyObject p_value
596
+ let bad_str = " __str__ call failed"
597
+ pure $ PyException
598
+ { ty = fromMaybe bad_str s_type
599
+ , str = fromMaybe bad_str s_value
600
+ , exception = exc
601
+ }
617
602
618
603
-- | Throw python error as haskell exception if it's raised.
619
604
checkThrowPyError :: Py ()
620
605
checkThrowPyError =
621
606
Py [CU. exp | PyObject* { PyErr_Occurred() } |] >>= \ case
622
607
NULL -> pure ()
623
- _ -> throwM =<< convertPy2Haskell
608
+ _ -> throwM . PyError =<< convertPy2Haskell
624
609
625
610
-- | Throw python error as haskell exception if it's raised. If it's
626
611
-- not that internal error. Another exception will be raised
627
612
mustThrowPyError :: Py a
628
613
mustThrowPyError =
629
614
Py [CU. exp | PyObject* { PyErr_Occurred() } |] >>= \ case
630
615
NULL -> error $ " mustThrowPyError: no python exception raised."
631
- _ -> throwM =<< convertPy2Haskell
616
+ _ -> throwM . PyError =<< convertPy2Haskell
632
617
633
618
-- | Calls mustThrowPyError if pointer is null or returns it unchanged
634
619
throwOnNULL :: Ptr PyObject -> Py (Ptr PyObject )
0 commit comments