{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TemplateHaskell #-}
module Python.Internal.Eval
(
ensurePyLock
, callbackEnsurePyLock
, initializePython
, finalizePython
, withPython
, runPy
, runPyInMain
, unsafeRunPy
, newPyObject
, takeOwnership
, ensureGIL
, dropGIL
, convertHaskell2Py
, convertPy2Haskell
, checkThrowPyError
, mustThrowPyError
, checkThrowBadPyType
, throwOnNULL
, Namespace(..)
, Main(..)
, Temp(..)
, Dict(..)
, DictPtr(..)
, Module(..)
, ModulePtr(..)
, unsafeWithCode
, eval
, exec
, debugPrintPy
) where
import Control.Concurrent
import Control.Concurrent.STM
import Control.Exception (interruptible)
import Control.Monad
import Control.Monad.Catch
import Control.Monad.IO.Class
import Control.Monad.Trans.Cont
import Data.Maybe
import Data.Function
import Data.ByteString.Unsafe qualified as BS
import Foreign.Concurrent qualified as GHC
import Foreign.Ptr
import Foreign.ForeignPtr
import Foreign.C.Types
import Foreign.C.String
import Foreign.Marshal.Array
import Foreign.Storable
import System.Environment
import System.IO.Unsafe
import Language.C.Inline qualified as C
import Language.C.Inline.Unsafe qualified as CU
import Python.Internal.CAPI
import Python.Internal.Types
import Python.Internal.Util
import Python.Internal.Program
C.context (C.baseCtx <> pyCtx)
C.include "<inline-python.h>"
globalPyState :: TVar PyState
globalPyState :: TVar PyState
globalPyState = IO (TVar PyState) -> TVar PyState
forall a. IO a -> a
unsafePerformIO (IO (TVar PyState) -> TVar PyState)
-> IO (TVar PyState) -> TVar PyState
forall a b. (a -> b) -> a -> b
$ PyState -> IO (TVar PyState)
forall a. a -> IO (TVar a)
newTVarIO PyState
NotInitialized
{-# NOINLINE globalPyState #-}
globalPyLock :: TVar PyLock
globalPyLock :: TVar PyLock
globalPyLock = IO (TVar PyLock) -> TVar PyLock
forall a. IO a -> a
unsafePerformIO (IO (TVar PyLock) -> TVar PyLock)
-> IO (TVar PyLock) -> TVar PyLock
forall a b. (a -> b) -> a -> b
$ PyLock -> IO (TVar PyLock)
forall a. a -> IO (TVar a)
newTVarIO PyLock
LockUninialized
{-# NOINLINE globalPyLock #-}
data PyState
= NotInitialized
| InInitialization
| InitFailed
| Running1
| RunningN !(Chan (Ptr PyObject))
!(MVar EvalReq)
!ThreadId
!ThreadId
| InFinalization
| Finalized
data PyLock
= LockUninialized
| LockUnlocked
| Locked !ThreadId [ThreadId]
| LockedByGC
| LockFinalized
deriving Int -> PyLock -> ShowS
[PyLock] -> ShowS
PyLock -> String
(Int -> PyLock -> ShowS)
-> (PyLock -> String) -> ([PyLock] -> ShowS) -> Show PyLock
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> PyLock -> ShowS
showsPrec :: Int -> PyLock -> ShowS
$cshow :: PyLock -> String
show :: PyLock -> String
$cshowList :: [PyLock] -> ShowS
showList :: [PyLock] -> ShowS
Show
ensurePyLock :: IO a -> IO a
ensurePyLock :: forall a. IO a -> IO a
ensurePyLock IO a
action = do
tid <- IO ThreadId
myThreadId
bracket_ (atomically $ acquireLock tid)
(atomically $ releaseLock tid)
action
callbackEnsurePyLock :: IO a -> IO a
callbackEnsurePyLock :: forall a. IO a -> IO a
callbackEnsurePyLock IO a
action = do
tid <- IO ThreadId
myThreadId
bracket_ (atomically $ grabLock tid)
(atomically $ releaseLock tid)
action
acquireLock :: ThreadId -> STM ()
acquireLock :: ThreadId -> STM ()
acquireLock ThreadId
tid = TVar PyLock -> STM PyLock
forall a. TVar a -> STM a
readTVar TVar PyLock
globalPyLock STM PyLock -> (PyLock -> STM ()) -> STM ()
forall a b. STM a -> (a -> STM b) -> STM b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
PyLock
LockUninialized -> PyError -> STM ()
forall e a. Exception e => e -> STM a
throwSTM PyError
PythonNotInitialized
PyLock
LockFinalized -> PyError -> STM ()
forall e a. Exception e => e -> STM a
throwSTM PyError
PythonIsFinalized
PyLock
LockedByGC -> STM ()
forall a. STM a
retry
PyLock
LockUnlocked -> TVar PyLock -> PyLock -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar PyLock
globalPyLock (PyLock -> STM ()) -> PyLock -> STM ()
forall a b. (a -> b) -> a -> b
$ ThreadId -> [ThreadId] -> PyLock
Locked ThreadId
tid []
Locked ThreadId
t [ThreadId]
xs
| ThreadId
t ThreadId -> ThreadId -> Bool
forall a. Eq a => a -> a -> Bool
== ThreadId
tid -> TVar PyLock -> PyLock -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar PyLock
globalPyLock (PyLock -> STM ()) -> PyLock -> STM ()
forall a b. (a -> b) -> a -> b
$ ThreadId -> [ThreadId] -> PyLock
Locked ThreadId
t (ThreadId
t ThreadId -> [ThreadId] -> [ThreadId]
forall a. a -> [a] -> [a]
: [ThreadId]
xs)
| Bool
otherwise -> STM ()
forall a. STM a
retry
grabLock :: ThreadId -> STM ()
grabLock :: ThreadId -> STM ()
grabLock ThreadId
tid = TVar PyLock -> STM PyLock
forall a. TVar a -> STM a
readTVar TVar PyLock
globalPyLock STM PyLock -> (PyLock -> STM ()) -> STM ()
forall a b. STM a -> (a -> STM b) -> STM b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
PyLock
LockUninialized -> PyError -> STM ()
forall e a. Exception e => e -> STM a
throwSTM PyError
PythonNotInitialized
PyLock
LockFinalized -> PyError -> STM ()
forall e a. Exception e => e -> STM a
throwSTM PyError
PythonIsFinalized
PyLock
LockedByGC -> STM ()
forall a. STM a
retry
PyLock
LockUnlocked -> TVar PyLock -> PyLock -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar PyLock
globalPyLock (PyLock -> STM ()) -> PyLock -> STM ()
forall a b. (a -> b) -> a -> b
$ ThreadId -> [ThreadId] -> PyLock
Locked ThreadId
tid []
Locked ThreadId
t [ThreadId]
xs -> TVar PyLock -> PyLock -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar PyLock
globalPyLock (PyLock -> STM ()) -> PyLock -> STM ()
forall a b. (a -> b) -> a -> b
$ ThreadId -> [ThreadId] -> PyLock
Locked ThreadId
tid (ThreadId
t ThreadId -> [ThreadId] -> [ThreadId]
forall a. a -> [a] -> [a]
: [ThreadId]
xs)
releaseLock :: ThreadId -> STM ()
releaseLock :: ThreadId -> STM ()
releaseLock ThreadId
tid = TVar PyLock -> STM PyLock
forall a. TVar a -> STM a
readTVar TVar PyLock
globalPyLock STM PyLock -> (PyLock -> STM ()) -> STM ()
forall a b. STM a -> (a -> STM b) -> STM b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
PyLock
LockUninialized -> PyError -> STM ()
forall e a. Exception e => e -> STM a
throwSTM PyError
PythonNotInitialized
PyLock
LockFinalized -> PyError -> STM ()
forall e a. Exception e => e -> STM a
throwSTM PyError
PythonIsFinalized
PyLock
LockUnlocked -> PyInternalError -> STM ()
forall e a. Exception e => e -> STM a
throwSTM (PyInternalError -> STM ()) -> PyInternalError -> STM ()
forall a b. (a -> b) -> a -> b
$ String -> PyInternalError
PyInternalError String
"releaseLock: releasing LockUnlocked"
PyLock
LockedByGC -> PyInternalError -> STM ()
forall e a. Exception e => e -> STM a
throwSTM (PyInternalError -> STM ()) -> PyInternalError -> STM ()
forall a b. (a -> b) -> a -> b
$ String -> PyInternalError
PyInternalError String
"releaseLock: releasing LockedByGC"
Locked ThreadId
t [ThreadId]
xs
| ThreadId
t ThreadId -> ThreadId -> Bool
forall a. Eq a => a -> a -> Bool
/= ThreadId
tid -> PyInternalError -> STM ()
forall e a. Exception e => e -> STM a
throwSTM (PyInternalError -> STM ()) -> PyInternalError -> STM ()
forall a b. (a -> b) -> a -> b
$ String -> PyInternalError
PyInternalError String
"releaseLock: releasing wrong lock"
| Bool
otherwise -> TVar PyLock -> PyLock -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar PyLock
globalPyLock (PyLock -> STM ()) -> PyLock -> STM ()
forall a b. (a -> b) -> a -> b
$! case [ThreadId]
xs of
[] -> PyLock
LockUnlocked
ThreadId
t':[ThreadId]
ts -> ThreadId -> [ThreadId] -> PyLock
Locked ThreadId
t' [ThreadId]
ts
initializePython :: IO ()
initializePython :: IO ()
initializePython = IO CInt
[CU.exp| int { Py_IsInitialized() } |] IO CInt -> (CInt -> IO ()) -> IO ()
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
CInt
0 | Bool
rtsSupportsBoundThreads -> IO () -> IO ()
forall a. IO a -> IO a
runInBoundThread (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ IO ()
doInializePython
| Bool
otherwise -> IO ()
doInializePython
CInt
_ -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
finalizePython :: IO ()
finalizePython :: IO ()
finalizePython = IO (IO ()) -> IO ()
forall (m :: * -> *) a. Monad m => m (m a) -> m a
join (IO (IO ()) -> IO ()) -> IO (IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ STM (IO ()) -> IO (IO ())
forall a. STM a -> IO a
atomically (STM (IO ()) -> IO (IO ())) -> STM (IO ()) -> IO (IO ())
forall a b. (a -> b) -> a -> b
$ TVar PyState -> STM PyState
forall a. TVar a -> STM a
readTVar TVar PyState
globalPyState STM PyState -> (PyState -> STM (IO ())) -> STM (IO ())
forall a b. STM a -> (a -> STM b) -> STM b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
PyState
NotInitialized -> PyError -> STM (IO ())
forall e a. Exception e => e -> STM a
throwSTM PyError
PythonNotInitialized
PyState
InitFailed -> PyError -> STM (IO ())
forall e a. Exception e => e -> STM a
throwSTM PyError
PythonIsFinalized
PyState
Finalized -> IO () -> STM (IO ())
forall a. a -> STM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (IO () -> STM (IO ())) -> IO () -> STM (IO ())
forall a b. (a -> b) -> a -> b
$ () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
PyState
InInitialization -> STM (IO ())
forall a. STM a
retry
PyState
InFinalization -> STM (IO ())
forall a. STM a
retry
PyState
Running1 -> IO () -> STM (IO ())
forall a. a -> STM a
checkLock (IO () -> STM (IO ())) -> IO () -> STM (IO ())
forall a b. (a -> b) -> a -> b
$ IO ()
[C.block| void {
PyGILState_Ensure();
Py_Finalize();
} |]
RunningN Chan (Ptr PyObject)
_ MVar EvalReq
lock_eval ThreadId
_ ThreadId
tid_gc -> IO () -> STM (IO ())
forall a. a -> STM a
checkLock (IO () -> STM (IO ())) -> IO () -> STM (IO ())
forall a b. (a -> b) -> a -> b
$ do
ThreadId -> IO ()
killThread ThreadId
tid_gc
resp <- IO (MVar ())
forall a. IO (MVar a)
newEmptyMVar
putMVar lock_eval $ StopReq resp
takeMVar resp
where
checkLock :: b -> STM b
checkLock b
action = TVar PyLock -> STM PyLock
forall a. TVar a -> STM a
readTVar TVar PyLock
globalPyLock STM PyLock -> (PyLock -> STM b) -> STM b
forall a b. STM a -> (a -> STM b) -> STM b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
PyLock
LockUninialized -> PyInternalError -> STM b
forall e a. Exception e => e -> STM a
throwSTM (PyInternalError -> STM b) -> PyInternalError -> STM b
forall a b. (a -> b) -> a -> b
$ String -> PyInternalError
PyInternalError String
"finalizePython LockUninialized"
PyLock
LockFinalized -> PyInternalError -> STM b
forall e a. Exception e => e -> STM a
throwSTM (PyInternalError -> STM b) -> PyInternalError -> STM b
forall a b. (a -> b) -> a -> b
$ String -> PyInternalError
PyInternalError String
"finalizePython LockFinalized"
Locked{} -> STM b
forall a. STM a
retry
PyLock
LockedByGC -> STM b
forall a. STM a
retry
PyLock
LockUnlocked -> do
TVar PyLock -> PyLock -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar PyLock
globalPyLock PyLock
LockFinalized
TVar PyState -> PyState -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar PyState
globalPyState PyState
Finalized
b -> STM b
forall a. a -> STM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure b
action
withPython :: IO a -> IO a
withPython :: forall a. IO a -> IO a
withPython = IO () -> IO () -> IO a -> IO a
forall (m :: * -> *) a c b.
(HasCallStack, MonadMask m) =>
m a -> m c -> m b -> m b
bracket_ IO ()
initializePython IO ()
finalizePython
doInializePython :: IO ()
doInializePython :: IO ()
doInializePython = do
IO (IO ()) -> IO ()
forall (m :: * -> *) a. Monad m => m (m a) -> m a
join (IO (IO ()) -> IO ()) -> IO (IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ STM (IO ()) -> IO (IO ())
forall a. STM a -> IO a
atomically (STM (IO ()) -> IO (IO ())) -> STM (IO ()) -> IO (IO ())
forall a b. (a -> b) -> a -> b
$ do
TVar PyState -> STM PyState
forall a. TVar a -> STM a
readTVar TVar PyState
globalPyState STM PyState -> (PyState -> STM (IO ())) -> STM (IO ())
forall a b. STM a -> (a -> STM b) -> STM b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
PyState
Finalized -> PyError -> STM (IO ())
forall e a. Exception e => e -> STM a
throwSTM PyError
PythonNotInitialized
PyState
InitFailed -> PyError -> STM (IO ())
forall e a. Exception e => e -> STM a
throwSTM PyError
PythonIsFinalized
PyState
InInitialization -> STM (IO ())
forall a. STM a
retry
PyState
InFinalization -> STM (IO ())
forall a. STM a
retry
Running1{} -> IO () -> STM (IO ())
forall a. a -> STM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (IO () -> STM (IO ())) -> IO () -> STM (IO ())
forall a b. (a -> b) -> a -> b
$ () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
RunningN{} -> IO () -> STM (IO ())
forall a. a -> STM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (IO () -> STM (IO ())) -> IO () -> STM (IO ())
forall a b. (a -> b) -> a -> b
$ () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
PyState
NotInitialized -> do
TVar PyState -> PyState -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar PyState
globalPyState PyState
InInitialization
let fini :: PyState -> IO ()
fini PyState
st = STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
TVar PyState -> PyState -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar PyState
globalPyState (PyState -> STM ()) -> PyState -> STM ()
forall a b. (a -> b) -> a -> b
$ PyState
st
TVar PyLock -> PyLock -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar PyLock
globalPyLock (PyLock -> STM ()) -> PyLock -> STM ()
forall a b. (a -> b) -> a -> b
$ PyLock
LockUnlocked
IO () -> STM (IO ())
forall a. a -> STM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (IO () -> STM (IO ())) -> IO () -> STM (IO ())
forall a b. (a -> b) -> a -> b
$
(IO () -> IO ()
forall (m :: * -> *) a. (HasCallStack, MonadMask m) => m a -> m a
mask_ (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ if
| Bool
rtsSupportsBoundThreads -> do
lock_init <- IO (MVar Bool)
forall a. IO (MVar a)
newEmptyMVar
lock_eval <- newEmptyMVar
tid_main <- forkOS $ mainThread lock_init lock_eval
takeMVar lock_init >>= \case
Bool
True -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
Bool
False -> PyError -> IO ()
forall e a. (HasCallStack, Exception e) => e -> IO a
forall (m :: * -> *) e a.
(MonadThrow m, HasCallStack, Exception e) =>
e -> m a
throwM PyError
PyInitializationFailed
gc_chan <- newChan
tid_gc <- forkOS $ gcThread gc_chan
fini $ RunningN gc_chan lock_eval tid_main tid_gc
| Bool
otherwise -> do
IO Bool
doInializePythonIO IO Bool -> (Bool -> IO ()) -> IO ()
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Bool
True -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
Bool
False -> PyError -> IO ()
forall e a. (HasCallStack, Exception e) => e -> IO a
forall (m :: * -> *) e a.
(MonadThrow m, HasCallStack, Exception e) =>
e -> m a
throwM PyError
PyInitializationFailed
PyState -> IO ()
fini PyState
Running1
) IO () -> IO () -> IO ()
forall (m :: * -> *) a b.
(HasCallStack, MonadCatch m) =>
m a -> m b -> m a
`onException` STM () -> IO ()
forall a. STM a -> IO a
atomically (TVar PyState -> PyState -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar PyState
globalPyState PyState
InitFailed)
mainThread :: MVar Bool -> MVar EvalReq -> IO ()
mainThread :: MVar Bool -> MVar EvalReq -> IO ()
mainThread MVar Bool
lock_init MVar EvalReq
lock_eval = do
r_init <- IO Bool
doInializePythonIO
putMVar lock_init r_init
case r_init of
Bool
False -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
Bool
True -> IO () -> IO ()
forall (m :: * -> *) a. (HasCallStack, MonadMask m) => m a -> m a
mask_ (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ (IO () -> IO ()) -> IO ()
forall a. (a -> a) -> a
fix ((IO () -> IO ()) -> IO ()) -> (IO () -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \IO ()
loop ->
(MVar EvalReq -> IO EvalReq
forall a. MVar a -> IO a
takeMVar MVar EvalReq
lock_eval IO EvalReq -> (InterruptMain -> IO EvalReq) -> IO EvalReq
forall e a.
(HasCallStack, Exception e) =>
IO a -> (e -> IO a) -> IO a
forall (m :: * -> *) e a.
(MonadCatch m, HasCallStack, Exception e) =>
m a -> (e -> m a) -> m a
`catch` (\InterruptMain
InterruptMain -> EvalReq -> IO EvalReq
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure EvalReq
HereWeGoAgain)) IO EvalReq -> (EvalReq -> IO ()) -> IO ()
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
EvalReq Py a
py MVar (Either SomeException a)
resp -> do
res <- (a -> Either SomeException a
forall a b. b -> Either a b
Right (a -> Either SomeException a)
-> IO a -> IO (Either SomeException a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Py a -> IO a
forall a. Py a -> IO a
runPy Py a
py) IO (Either SomeException a)
-> (SomeException -> IO (Either SomeException a))
-> IO (Either SomeException a)
forall e a.
(HasCallStack, Exception e) =>
IO a -> (e -> IO a) -> IO a
forall (m :: * -> *) e a.
(MonadCatch m, HasCallStack, Exception e) =>
m a -> (e -> m a) -> m a
`catch` (Either SomeException a -> IO (Either SomeException a)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either SomeException a -> IO (Either SomeException a))
-> (SomeException -> Either SomeException a)
-> SomeException
-> IO (Either SomeException a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SomeException -> Either SomeException a
forall a b. a -> Either a b
Left)
putMVar resp res
loop
StopReq MVar ()
resp -> do
IO ()
[C.block| void {
PyGILState_Ensure();
Py_Finalize();
} |]
MVar () -> () -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar ()
resp ()
EvalReq
HereWeGoAgain -> IO ()
loop
doInializePythonIO :: IO Bool
doInializePythonIO :: IO Bool
doInializePythonIO = do
argv0 <- IO String
getProgName
argv <- getArgs
let n_argv = Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> CInt) -> Int -> CInt
forall a b. (a -> b) -> a -> b
$ [String] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [String]
argv Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1
r <- evalContT $ do
p_argv0 <- ContT $ withWCString argv0
p_argv <- traverse (ContT . withWCString) argv
ptr_argv <- ContT $ withArray (p_argv0 : p_argv)
liftIO [C.block| int {
// Now fill config
PyStatus status;
PyConfig cfg;
PyConfig_InitPythonConfig( &cfg );
cfg.parse_argv = 0;
cfg.install_signal_handlers = 0;
//----------------
status = PyConfig_SetBytesString(&cfg, &cfg.program_name, "XX");
if( PyStatus_Exception(status) ) {
goto error;
}
//----------------
status = PyConfig_SetArgv(&cfg,
$(int n_argv),
$(wchar_t** ptr_argv)
);
if( PyStatus_Exception(status) ) {
goto error;
};
// Initialize interpreter
status = Py_InitializeFromConfig(&cfg);
if( PyStatus_Exception(status) ) {
goto error;
};
PyConfig_Clear(&cfg);
// Release GIL so other threads may take it
PyEval_SaveThread();
return 0;
// Error case
error:
PyConfig_Clear(&cfg);
return 1;
} |]
return $! r == 0
data EvalReq
= forall a. EvalReq (Py a) (MVar (Either SomeException a))
| StopReq (MVar ())
| HereWeGoAgain
data InterruptMain = InterruptMain
deriving stock Int -> InterruptMain -> ShowS
[InterruptMain] -> ShowS
InterruptMain -> String
(Int -> InterruptMain -> ShowS)
-> (InterruptMain -> String)
-> ([InterruptMain] -> ShowS)
-> Show InterruptMain
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> InterruptMain -> ShowS
showsPrec :: Int -> InterruptMain -> ShowS
$cshow :: InterruptMain -> String
show :: InterruptMain -> String
$cshowList :: [InterruptMain] -> ShowS
showList :: [InterruptMain] -> ShowS
Show
deriving anyclass Show InterruptMain
Typeable InterruptMain
(Typeable InterruptMain, Show InterruptMain) =>
(InterruptMain -> SomeException)
-> (SomeException -> Maybe InterruptMain)
-> (InterruptMain -> String)
-> (InterruptMain -> Bool)
-> Exception InterruptMain
SomeException -> Maybe InterruptMain
InterruptMain -> Bool
InterruptMain -> String
InterruptMain -> SomeException
forall e.
(Typeable e, Show e) =>
(e -> SomeException)
-> (SomeException -> Maybe e)
-> (e -> String)
-> (e -> Bool)
-> Exception e
$ctoException :: InterruptMain -> SomeException
toException :: InterruptMain -> SomeException
$cfromException :: SomeException -> Maybe InterruptMain
fromException :: SomeException -> Maybe InterruptMain
$cdisplayException :: InterruptMain -> String
displayException :: InterruptMain -> String
$cbacktraceDesired :: InterruptMain -> Bool
backtraceDesired :: InterruptMain -> Bool
Exception
runPy :: Py a -> IO a
runPy :: forall a. Py a -> IO a
runPy Py a
py
| Bool
rtsSupportsBoundThreads = IO a -> IO a
forall a. IO a -> IO a
runInBoundThread IO a
go
| Bool
otherwise = IO a
go
where
go :: IO a
go = IO a -> IO a
forall a. IO a -> IO a
ensurePyLock (IO a -> IO a) -> IO a -> IO a
forall a b. (a -> b) -> a -> b
$ IO a -> IO a
forall (m :: * -> *) a. (HasCallStack, MonadMask m) => m a -> m a
mask_ (IO a -> IO a) -> IO a -> IO a
forall a b. (a -> b) -> a -> b
$ Py a -> IO a
forall a. Py a -> IO a
unsafeRunPy (Py a -> Py a
forall a. Py a -> Py a
ensureGIL Py a
py)
runPyInMain :: Py a -> IO a
runPyInMain :: forall a. Py a -> IO a
runPyInMain Py a
py
| Bool
rtsSupportsBoundThreads = do
tid <- IO ThreadId
myThreadId
bracket (acquireMain tid) fst snd
| Bool
otherwise = Py a -> IO a
forall a. Py a -> IO a
runPy Py a
py
where
acquireMain :: ThreadId -> IO (IO (), IO a)
acquireMain ThreadId
tid = STM (IO (), IO a) -> IO (IO (), IO a)
forall a. STM a -> IO a
atomically (STM (IO (), IO a) -> IO (IO (), IO a))
-> STM (IO (), IO a) -> IO (IO (), IO a)
forall a b. (a -> b) -> a -> b
$ TVar PyState -> STM PyState
forall a. TVar a -> STM a
readTVar TVar PyState
globalPyState STM PyState -> (PyState -> STM (IO (), IO a)) -> STM (IO (), IO a)
forall a b. STM a -> (a -> STM b) -> STM b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
PyState
NotInitialized -> PyError -> STM (IO (), IO a)
forall e a. Exception e => e -> STM a
throwSTM PyError
PythonNotInitialized
PyState
InitFailed -> PyError -> STM (IO (), IO a)
forall e a. Exception e => e -> STM a
throwSTM PyError
PyInitializationFailed
PyState
Finalized -> PyError -> STM (IO (), IO a)
forall e a. Exception e => e -> STM a
throwSTM PyError
PythonIsFinalized
PyState
InInitialization -> STM (IO (), IO a)
forall a. STM a
retry
PyState
InFinalization -> STM (IO (), IO a)
forall a. STM a
retry
PyState
Running1 -> PyInternalError -> STM (IO (), IO a)
forall e a. Exception e => e -> STM a
throwSTM (PyInternalError -> STM (IO (), IO a))
-> PyInternalError -> STM (IO (), IO a)
forall a b. (a -> b) -> a -> b
$ String -> PyInternalError
PyInternalError String
"runPyInMain: Running1"
RunningN Chan (Ptr PyObject)
_ MVar EvalReq
eval_lock ThreadId
tid_main ThreadId
_ -> TVar PyLock -> STM PyLock
forall a. TVar a -> STM a
readTVar TVar PyLock
globalPyLock STM PyLock -> (PyLock -> STM (IO (), IO a)) -> STM (IO (), IO a)
forall a b. STM a -> (a -> STM b) -> STM b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
PyLock
LockUninialized -> PyError -> STM (IO (), IO a)
forall e a. Exception e => e -> STM a
throwSTM PyError
PythonNotInitialized
PyLock
LockFinalized -> PyError -> STM (IO (), IO a)
forall e a. Exception e => e -> STM a
throwSTM PyError
PythonIsFinalized
PyLock
LockedByGC -> STM (IO (), IO a)
forall a. STM a
retry
PyLock
LockUnlocked -> do
TVar PyLock -> PyLock -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar PyLock
globalPyLock (PyLock -> STM ()) -> PyLock -> STM ()
forall a b. (a -> b) -> a -> b
$ ThreadId -> [ThreadId] -> PyLock
Locked ThreadId
tid_main []
(IO (), IO a) -> STM (IO (), IO a)
forall a. a -> STM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ( STM () -> IO ()
forall a. STM a -> IO a
atomically (ThreadId -> STM ()
releaseLock ThreadId
tid_main)
, ThreadId -> MVar EvalReq -> IO a
evalInOtherThread ThreadId
tid_main MVar EvalReq
eval_lock
)
Locked ThreadId
t [ThreadId]
ts
| ThreadId
t ThreadId -> ThreadId -> Bool
forall a. Eq a => a -> a -> Bool
/= ThreadId
tid
-> STM (IO (), IO a)
forall a. STM a
retry
| ThreadId
t ThreadId -> ThreadId -> Bool
forall a. Eq a => a -> a -> Bool
== ThreadId
tid_main Bool -> Bool -> Bool
|| (ThreadId
tid_main ThreadId -> [ThreadId] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [ThreadId]
ts) -> do
TVar PyLock -> PyLock -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar PyLock
globalPyLock (PyLock -> STM ()) -> PyLock -> STM ()
forall a b. (a -> b) -> a -> b
$ ThreadId -> [ThreadId] -> PyLock
Locked ThreadId
t (ThreadId
t ThreadId -> [ThreadId] -> [ThreadId]
forall a. a -> [a] -> [a]
: [ThreadId]
ts)
(IO (), IO a) -> STM (IO (), IO a)
forall a. a -> STM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ( STM () -> IO ()
forall a. STM a -> IO a
atomically (ThreadId -> STM ()
releaseLock ThreadId
t)
, Py a -> IO a
forall a. Py a -> IO a
unsafeRunPy (Py a -> IO a) -> Py a -> IO a
forall a b. (a -> b) -> a -> b
$ Py a -> Py a
forall a. Py a -> Py a
ensureGIL Py a
py
)
| Bool
otherwise -> do
TVar PyLock -> PyLock -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar PyLock
globalPyLock (PyLock -> STM ()) -> PyLock -> STM ()
forall a b. (a -> b) -> a -> b
$ ThreadId -> [ThreadId] -> PyLock
Locked ThreadId
tid_main (ThreadId
t ThreadId -> [ThreadId] -> [ThreadId]
forall a. a -> [a] -> [a]
: [ThreadId]
ts)
(IO (), IO a) -> STM (IO (), IO a)
forall a. a -> STM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ( STM () -> IO ()
forall a. STM a -> IO a
atomically (ThreadId -> STM ()
releaseLock ThreadId
tid_main)
, ThreadId -> MVar EvalReq -> IO a
evalInOtherThread ThreadId
tid_main MVar EvalReq
eval_lock
)
evalInOtherThread :: ThreadId -> MVar EvalReq -> IO a
evalInOtherThread ThreadId
tid_main MVar EvalReq
eval_lock = do
r <- IO (Either SomeException a) -> IO (Either SomeException a)
forall (m :: * -> *) a. (HasCallStack, MonadMask m) => m a -> m a
mask_ (IO (Either SomeException a) -> IO (Either SomeException a))
-> IO (Either SomeException a) -> IO (Either SomeException a)
forall a b. (a -> b) -> a -> b
$ do resp <- IO (MVar (Either SomeException a))
forall a. IO (MVar a)
newEmptyMVar
putMVar eval_lock $ EvalReq py resp
takeMVar resp `onException` throwTo tid_main InterruptMain
either throwM pure r
unsafeRunPy :: Py a -> IO a
unsafeRunPy :: forall a. Py a -> IO a
unsafeRunPy (Py IO a
io) = IO a
io
newPyObject :: Ptr PyObject -> Py PyObject
newPyObject :: Ptr PyObject -> Py PyObject
newPyObject Ptr PyObject
p = IO PyObject -> Py PyObject
forall a. IO a -> Py a
Py (IO PyObject -> Py PyObject) -> IO PyObject -> Py PyObject
forall a b. (a -> b) -> a -> b
$ do
fptr <- Ptr PyObject -> IO (ForeignPtr PyObject)
forall a. Ptr a -> IO (ForeignPtr a)
newForeignPtr_ Ptr PyObject
p
GHC.addForeignPtrFinalizer fptr $
readTVarIO globalPyState >>= \case
RunningN Chan (Ptr PyObject)
ch MVar EvalReq
_ ThreadId
_ ThreadId
_ -> Chan (Ptr PyObject) -> Ptr PyObject -> IO ()
forall a. Chan a -> a -> IO ()
writeChan Chan (Ptr PyObject)
ch Ptr PyObject
p
PyState
Running1 -> Ptr PyObject -> IO ()
singleThreadedDecrefCG Ptr PyObject
p
PyState
_ -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
pure $ PyObject fptr
gcThread :: Chan (Ptr PyObject) -> IO ()
gcThread :: Chan (Ptr PyObject) -> IO ()
gcThread Chan (Ptr PyObject)
ch = IO () -> IO ()
forall (f :: * -> *) a b. Applicative f => f a -> f b
forever (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
Ptr PyObject -> IO ()
decrefGC (Ptr PyObject -> IO ()) -> IO (Ptr PyObject) -> IO ()
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Chan (Ptr PyObject) -> IO (Ptr PyObject)
forall a. Chan a -> IO a
readChan Chan (Ptr PyObject)
ch
decrefGC :: Ptr PyObject -> IO ()
decrefGC :: Ptr PyObject -> IO ()
decrefGC Ptr PyObject
p = IO (IO ()) -> IO ()
forall (m :: * -> *) a. Monad m => m (m a) -> m a
join (IO (IO ()) -> IO ()) -> IO (IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ STM (IO ()) -> IO (IO ())
forall a. STM a -> IO a
atomically (STM (IO ()) -> IO (IO ())) -> STM (IO ()) -> IO (IO ())
forall a b. (a -> b) -> a -> b
$ TVar PyLock -> STM PyLock
forall a. TVar a -> STM a
readTVar TVar PyLock
globalPyLock STM PyLock -> (PyLock -> STM (IO ())) -> STM (IO ())
forall a b. STM a -> (a -> STM b) -> STM b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
PyLock
LockUninialized -> IO () -> STM (IO ())
forall a. a -> STM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (IO () -> STM (IO ())) -> IO () -> STM (IO ())
forall a b. (a -> b) -> a -> b
$ () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
PyLock
LockFinalized -> IO () -> STM (IO ())
forall a. a -> STM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (IO () -> STM (IO ())) -> IO () -> STM (IO ())
forall a b. (a -> b) -> a -> b
$ () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
PyLock
LockedByGC -> IO () -> STM (IO ())
forall a. a -> STM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (IO () -> STM (IO ())) -> IO () -> STM (IO ())
forall a b. (a -> b) -> a -> b
$ () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
Locked{} -> STM (IO ())
forall a. STM a
retry
PyLock
LockUnlocked -> do
TVar PyLock -> PyLock -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar PyLock
globalPyLock PyLock
LockedByGC
IO () -> STM (IO ())
forall a. a -> STM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (IO () -> STM (IO ())) -> IO () -> STM (IO ())
forall a b. (a -> b) -> a -> b
$ do
Ptr PyObject -> IO ()
gcDecref Ptr PyObject
p IO () -> IO () -> IO ()
forall (m :: * -> *) a b.
(HasCallStack, MonadMask m) =>
m a -> m b -> m a
`finally` STM () -> IO ()
forall a. STM a -> IO a
atomically (TVar PyLock -> PyLock -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar PyLock
globalPyLock PyLock
LockUnlocked)
singleThreadedDecrefCG :: Ptr PyObject -> IO ()
singleThreadedDecrefCG :: Ptr PyObject -> IO ()
singleThreadedDecrefCG Ptr PyObject
p = TVar PyLock -> IO PyLock
forall a. TVar a -> IO a
readTVarIO TVar PyLock
globalPyLock IO PyLock -> (PyLock -> IO ()) -> IO ()
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
PyLock
LockUninialized -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
PyLock
LockFinalized -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
PyLock
LockedByGC -> Ptr PyObject -> IO ()
gcDecref Ptr PyObject
p
Locked{} -> Ptr PyObject -> IO ()
gcDecref Ptr PyObject
p
PyLock
LockUnlocked -> Ptr PyObject -> IO ()
gcDecref Ptr PyObject
p
gcDecref :: Ptr PyObject -> IO ()
gcDecref :: Ptr PyObject -> IO ()
gcDecref Ptr PyObject
p = [CU.block| void {
PyGILState_STATE st = PyGILState_Ensure();
Py_XDECREF( $(PyObject* p) );
PyGILState_Release(st);
} |]
ensureGIL :: Py a -> Py a
ensureGIL :: forall a. Py a -> Py a
ensureGIL Py a
action = do
gil_state <- IO CInt -> Py CInt
forall a. IO a -> Py a
Py IO CInt
[CU.exp| int { PyGILState_Ensure() } |]
action `finally` Py [CU.exp| void { PyGILState_Release($(int gil_state)) } |]
dropGIL :: IO a -> Py a
dropGIL :: forall a. IO a -> Py a
dropGIL IO a
action = do
st <- IO (Ptr PyThreadState) -> Py (Ptr PyThreadState)
forall a. IO a -> Py a
Py IO (Ptr PyThreadState)
[CU.exp| PyThreadState* { PyEval_SaveThread() } |]
Py $ interruptible action
`finally` [CU.exp| void { PyEval_RestoreThread($(PyThreadState *st)) } |]
convertHaskell2Py :: SomeException -> Py (Ptr PyObject)
convertHaskell2Py :: SomeException -> Py (Ptr PyObject)
convertHaskell2Py SomeException
err = IO (Ptr PyObject) -> Py (Ptr PyObject)
forall a. IO a -> Py a
Py (IO (Ptr PyObject) -> Py (Ptr PyObject))
-> IO (Ptr PyObject) -> Py (Ptr PyObject)
forall a b. (a -> b) -> a -> b
$ do
String -> (CString -> IO (Ptr PyObject)) -> IO (Ptr PyObject)
forall a. String -> (CString -> IO a) -> IO a
withCString (String
"Haskell exception: "String -> ShowS
forall a. [a] -> [a] -> [a]
++SomeException -> String
forall a. Show a => a -> String
show SomeException
err) ((CString -> IO (Ptr PyObject)) -> IO (Ptr PyObject))
-> (CString -> IO (Ptr PyObject)) -> IO (Ptr PyObject)
forall a b. (a -> b) -> a -> b
$ \CString
p_err -> do
[CU.block| PyObject* {
PyErr_SetString(PyExc_RuntimeError, $(char *p_err));
return NULL;
} |]
convertPy2Haskell :: Py PyException
convertPy2Haskell :: Py PyException
convertPy2Haskell = Program PyException PyException -> Py PyException
forall a. Program a a -> Py a
runProgram (Program PyException PyException -> Py PyException)
-> Program PyException PyException -> Py PyException
forall a b. (a -> b) -> a -> b
$ do
p_errors <- forall a r. Storable a => Int -> Program r (Ptr a)
withPyAllocaArray @(Ptr PyObject) Int
3
(p_type, p_value) <- progIO $ do
[CU.block| void {
PyObject **p = $(PyObject** p_errors);
PyErr_Fetch(p, p+1, p+2);
}|]
p_type <- peekElemOff p_errors 0
p_value <- peekElemOff p_errors 1
pure (p_type,p_value)
progPy $ do
s_type <- pyobjectStrAsHask p_type
s_value <- pyobjectStrAsHask p_value
incref p_value
exc <- newPyObject p_value
let bad_str = String
"__str__ call failed"
pure $ PyException
{ ty = fromMaybe bad_str s_type
, str = fromMaybe bad_str s_value
, exception = exc
}
checkThrowPyError :: Py ()
checkThrowPyError :: Py ()
checkThrowPyError =
IO (Ptr PyObject) -> Py (Ptr PyObject)
forall a. IO a -> Py a
Py IO (Ptr PyObject)
[CU.exp| PyObject* { PyErr_Occurred() } |] Py (Ptr PyObject) -> (Ptr PyObject -> Py ()) -> Py ()
forall a b. Py a -> (a -> Py b) -> Py b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Ptr PyObject
NULL -> () -> Py ()
forall a. a -> Py a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
Ptr PyObject
_ -> PyError -> Py ()
forall e a. (HasCallStack, Exception e) => e -> Py a
forall (m :: * -> *) e a.
(MonadThrow m, HasCallStack, Exception e) =>
e -> m a
throwM (PyError -> Py ())
-> (PyException -> PyError) -> PyException -> Py ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PyException -> PyError
PyError (PyException -> Py ()) -> Py PyException -> Py ()
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Py PyException
convertPy2Haskell
mustThrowPyError :: Py a
mustThrowPyError :: forall a. Py a
mustThrowPyError =
IO (Ptr PyObject) -> Py (Ptr PyObject)
forall a. IO a -> Py a
Py IO (Ptr PyObject)
[CU.exp| PyObject* { PyErr_Occurred() } |] Py (Ptr PyObject) -> (Ptr PyObject -> Py a) -> Py a
forall a b. Py a -> (a -> Py b) -> Py b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Ptr PyObject
NULL -> String -> Py a
forall a. HasCallStack => String -> a
error (String -> Py a) -> String -> Py a
forall a b. (a -> b) -> a -> b
$ String
"mustThrowPyError: no python exception raised."
Ptr PyObject
_ -> PyError -> Py a
forall e a. (HasCallStack, Exception e) => e -> Py a
forall (m :: * -> *) e a.
(MonadThrow m, HasCallStack, Exception e) =>
e -> m a
throwM (PyError -> Py a)
-> (PyException -> PyError) -> PyException -> Py a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PyException -> PyError
PyError (PyException -> Py a) -> Py PyException -> Py a
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Py PyException
convertPy2Haskell
throwOnNULL :: Ptr PyObject -> Py (Ptr PyObject)
throwOnNULL :: Ptr PyObject -> Py (Ptr PyObject)
throwOnNULL = \case
Ptr PyObject
NULL -> Py (Ptr PyObject)
forall a. Py a
mustThrowPyError
Ptr PyObject
p -> Ptr PyObject -> Py (Ptr PyObject)
forall a. a -> Py a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Ptr PyObject
p
checkThrowBadPyType :: Py ()
checkThrowBadPyType :: Py ()
checkThrowBadPyType = do
r <- IO CInt -> Py CInt
forall a. IO a -> Py a
Py IO CInt
[CU.block| int {
if( PyErr_Occurred() ) {
PyErr_Clear();
return 1;
}
return 0;
} |]
case r of
CInt
0 -> () -> Py ()
forall a. a -> Py a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
CInt
_ -> PyError -> Py ()
forall e a. (HasCallStack, Exception e) => e -> Py a
forall (m :: * -> *) e a.
(MonadThrow m, HasCallStack, Exception e) =>
e -> m a
throwM PyError
BadPyType
class Namespace a where
basicNamespaceDict :: a -> Py (Ptr PyObject)
data Main = Main
instance Namespace Main where
basicNamespaceDict :: Main -> Py (Ptr PyObject)
basicNamespaceDict Main
_ =
Ptr PyObject -> Py (Ptr PyObject)
throwOnNULL (Ptr PyObject -> Py (Ptr PyObject))
-> Py (Ptr PyObject) -> Py (Ptr PyObject)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< IO (Ptr PyObject) -> Py (Ptr PyObject)
forall a. IO a -> Py a
Py IO (Ptr PyObject)
[CU.block| PyObject* {
PyObject* main_module = PyImport_AddModule("__main__");
if( PyErr_Occurred() )
return NULL;
PyObject* dict = PyModule_GetDict(main_module);
Py_XINCREF(dict);
return dict;
}|]
data Temp = Temp
instance Namespace Temp where
basicNamespaceDict :: Temp -> Py (Ptr PyObject)
basicNamespaceDict Temp
_ = Py (Ptr PyObject)
basicNewDict
newtype DictPtr = DictPtr (Ptr PyObject)
instance Namespace DictPtr where
basicNamespaceDict :: DictPtr -> Py (Ptr PyObject)
basicNamespaceDict (DictPtr Ptr PyObject
p) = Ptr PyObject
p Ptr PyObject -> Py () -> Py (Ptr PyObject)
forall a b. a -> Py b -> Py a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ Ptr PyObject -> Py ()
incref Ptr PyObject
p
newtype Dict = Dict PyObject
instance Namespace Dict where
basicNamespaceDict :: Dict -> Py (Ptr PyObject)
basicNamespaceDict (Dict PyObject
d)
= PyObject
-> (Ptr PyObject -> Py (Ptr PyObject)) -> Py (Ptr PyObject)
forall a. PyObject -> (Ptr PyObject -> Py a) -> Py a
unsafeWithPyObject PyObject
d (DictPtr -> Py (Ptr PyObject)
forall a. Namespace a => a -> Py (Ptr PyObject)
basicNamespaceDict (DictPtr -> Py (Ptr PyObject))
-> (Ptr PyObject -> DictPtr) -> Ptr PyObject -> Py (Ptr PyObject)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Ptr PyObject -> DictPtr
DictPtr)
newtype ModulePtr = ModulePtr (Ptr PyObject)
instance Namespace ModulePtr where
basicNamespaceDict :: ModulePtr -> Py (Ptr PyObject)
basicNamespaceDict (ModulePtr Ptr PyObject
p) = do
Ptr PyObject -> Py (Ptr PyObject)
throwOnNULL (Ptr PyObject -> Py (Ptr PyObject))
-> Py (Ptr PyObject) -> Py (Ptr PyObject)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< IO (Ptr PyObject) -> Py (Ptr PyObject)
forall a. IO a -> Py a
Py [CU.block| PyObject* {
PyObject* dict = PyModule_GetDict($(PyObject* p));
Py_XINCREF(dict);
return dict;
}|]
newtype Module = Module PyObject
instance Namespace Module where
basicNamespaceDict :: Module -> Py (Ptr PyObject)
basicNamespaceDict (Module PyObject
d)
= PyObject
-> (Ptr PyObject -> Py (Ptr PyObject)) -> Py (Ptr PyObject)
forall a. PyObject -> (Ptr PyObject -> Py a) -> Py a
unsafeWithPyObject PyObject
d (ModulePtr -> Py (Ptr PyObject)
forall a. Namespace a => a -> Py (Ptr PyObject)
basicNamespaceDict (ModulePtr -> Py (Ptr PyObject))
-> (Ptr PyObject -> ModulePtr) -> Ptr PyObject -> Py (Ptr PyObject)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Ptr PyObject -> ModulePtr
ModulePtr)
eval :: (Namespace global, Namespace local)
=> global
-> local
-> PyQuote
-> Py PyObject
eval :: forall global local.
(Namespace global, Namespace local) =>
global -> local -> PyQuote -> Py PyObject
eval global
globals local
locals PyQuote
q = Program PyObject PyObject -> Py PyObject
forall a. Program a a -> Py a
runProgram (Program PyObject PyObject -> Py PyObject)
-> Program PyObject PyObject -> Py PyObject
forall a b. (a -> b) -> a -> b
$ do
p_py <- Code -> Program PyObject CString
forall r. Code -> Program r CString
unsafeWithCode PyQuote
q.code
p_globals <- takeOwnership =<< progPy (basicNamespaceDict globals)
p_locals <- takeOwnership =<< progPy (basicNamespaceDict locals)
progPy $ do
q.binder.bind p_locals
p_res <- Py [C.block| PyObject* {
PyObject* globals = $(PyObject* p_globals);
PyObject* locals = $(PyObject* p_locals);
// Compile code
PyObject *code = Py_CompileString($(char* p_py), "<interactive>", Py_eval_input);
if( PyErr_Occurred() ) {
return NULL;
}
// Evaluate expression
PyObject* r = PyEval_EvalCode(code, globals, locals);
Py_DECREF(code);
return r;
}|]
checkThrowPyError
newPyObject p_res
{-# SPECIALIZE eval :: Main -> Temp -> PyQuote -> Py PyObject #-}
exec :: (Namespace global, Namespace local)
=> global
-> local
-> PyQuote
-> Py ()
exec :: forall global local.
(Namespace global, Namespace local) =>
global -> local -> PyQuote -> Py ()
exec global
globals local
locals PyQuote
q = Program () () -> Py ()
forall a. Program a a -> Py a
runProgram (Program () () -> Py ()) -> Program () () -> Py ()
forall a b. (a -> b) -> a -> b
$ do
p_py <- Code -> Program () CString
forall r. Code -> Program r CString
unsafeWithCode PyQuote
q.code
p_globals <- takeOwnership =<< progPy (basicNamespaceDict globals)
p_locals <- takeOwnership =<< progPy (basicNamespaceDict locals)
progPy $ do
q.binder.bind p_locals
Py[C.block| void {
PyObject* globals = $(PyObject* p_globals);
PyObject* locals = $(PyObject* p_locals);
// Compile code
PyObject *code = Py_CompileString($(char* p_py), "<interactive>", Py_file_input);
if( PyErr_Occurred() ){
return;
}
// Execute statements
PyObject* res = PyEval_EvalCode(code, globals, locals);
Py_XDECREF(res);
Py_DECREF(code);
} |]
checkThrowPyError
{-# SPECIALIZE exec :: Main -> Main -> PyQuote -> Py () #-}
{-# SPECIALIZE exec :: Main -> Temp -> PyQuote -> Py () #-}
unsafeWithCode :: Code -> Program r (Ptr CChar)
unsafeWithCode :: forall r. Code -> Program r CString
unsafeWithCode (Code ByteString
bs) = ContT r Py CString -> Program r CString
forall r a. ContT r Py a -> Program r a
Program (ContT r Py CString -> Program r CString)
-> ContT r Py CString -> Program r CString
forall a b. (a -> b) -> a -> b
$ ((CString -> Py r) -> Py r) -> ContT r Py CString
forall {k} (r :: k) (m :: k -> *) a.
((a -> m r) -> m r) -> ContT r m a
ContT (((CString -> Py r) -> Py r) -> ContT r Py CString)
-> ((CString -> Py r) -> Py r) -> ContT r Py CString
forall a b. (a -> b) -> a -> b
$ \CString -> Py r
fun ->
IO r -> Py r
forall a. IO a -> Py a
Py (ByteString -> (CString -> IO r) -> IO r
forall a. ByteString -> (CString -> IO a) -> IO a
BS.unsafeUseAsCString ByteString
bs ((CString -> IO r) -> IO r) -> (CString -> IO r) -> IO r
forall a b. (a -> b) -> a -> b
$ Py r -> IO r
forall a. Py a -> IO a
unsafeRunPy (Py r -> IO r) -> (CString -> Py r) -> CString -> IO r
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CString -> Py r
fun)
debugPrintPy :: Ptr PyObject -> Py ()
debugPrintPy :: Ptr PyObject -> Py ()
debugPrintPy Ptr PyObject
p = IO () -> Py ()
forall a. IO a -> Py a
Py [CU.block| void {
PyObject_Print($(PyObject *p), stdout, 0);
printf(" [REF=%li]\n", Py_REFCNT($(PyObject *p)) );
} |]