@@ -29,6 +29,14 @@ module Python.Internal.Eval
2929 , mustThrowPyError
3030 , checkThrowBadPyType
3131 , throwOnNULL
32+ -- * Exec & eval
33+ , Namespace (.. )
34+ , Main (.. )
35+ , Temp (.. )
36+ , PtrNamespace (.. )
37+ , unsafeWithCode
38+ , eval
39+ , exec
3240 -- * Debugging
3341 , debugPrintPy
3442 ) where
@@ -42,6 +50,8 @@ import Control.Monad.IO.Class
4250import Control.Monad.Trans.Cont
4351import Data.Maybe
4452import Data.Function
53+ import Data.ByteString qualified as BS
54+ import Data.ByteString.Unsafe qualified as BS
4555import Foreign.Concurrent qualified as GHC
4656import Foreign.Ptr
4757import Foreign.ForeignPtr
@@ -662,6 +672,114 @@ checkThrowBadPyType = do
662672 _ -> throwM BadPyType
663673
664674
675+ ----------------------------------------------------------------
676+ -- Eval/exec
677+ ----------------------------------------------------------------
678+
679+ -- | Type class for values representing python dictionaries containing
680+ -- global or local variables.
681+ class Namespace a where
682+ -- | Returns dictionary object. Caller takes ownership of returned
683+ -- object.
684+ basicNamespaceDict :: a -> Py (Ptr PyObject )
685+
686+
687+ -- | Namespace for the top level code execution.
688+ data Main = Main
689+
690+
691+ instance Namespace Main where
692+ -- NOTE: almost dupe of basicMainDict
693+ basicNamespaceDict _ =
694+ throwOnNULL =<< Py [CU. block | PyObject* {
695+ PyObject* main_module = PyImport_AddModule("__main__");
696+ if( PyErr_Occurred() )
697+ return NULL;
698+ PyObject* dict = PyModule_GetDict(main_module);
699+ Py_XINCREF(dict);
700+ return dict;
701+ }|]
702+
703+ -- | Temporary namespace which get destroyed after execution
704+ data Temp = Temp
705+
706+ instance Namespace Temp where
707+ basicNamespaceDict _ = basicNewDict
708+
709+ -- | Newtype wrapper for bare python object. It's assumed to be a
710+ -- dictionary. This is not checked.
711+ newtype PtrNamespace = PtrNamespace (Ptr PyObject )
712+
713+ instance Namespace PtrNamespace where
714+ basicNamespaceDict (PtrNamespace p) = do
715+ Py [CU. block | void { Py_XINCREF($(PyObject* p)); } |]
716+ return p
717+
718+
719+ -- | Evaluate python expression
720+ eval :: (Namespace global , Namespace local )
721+ => global -- ^ Data type providing global variables dictionary
722+ -> local -- ^ Data type providing local variables dictionary
723+ -> PyQuote -- ^ Source code
724+ -> Py PyObject
725+ eval globals locals q = runProgram $ do
726+ p_py <- unsafeWithCode q. code
727+ p_globals <- takeOwnership =<< progPy (basicNamespaceDict globals)
728+ p_locals <- takeOwnership =<< progPy (basicNamespaceDict locals)
729+ progPy $ do
730+ q. binder. bind p_locals
731+ p_res <- Py [C. block | PyObject* {
732+ PyObject* globals = $(PyObject* p_globals);
733+ PyObject* locals = $(PyObject* p_locals);
734+ // Compile code
735+ PyObject *code = Py_CompileString($(char* p_py), "<interactive>", Py_eval_input);
736+ if( PyErr_Occurred() ) {
737+ return NULL;
738+ }
739+ // Evaluate expression
740+ PyObject* r = PyEval_EvalCode(code, globals, locals);
741+ Py_DECREF(code);
742+ return r;
743+ }|]
744+ checkThrowPyError
745+ newPyObject p_res
746+ {-# SPECIALIZE eval :: Main -> Temp -> PyQuote -> Py PyObject #-}
747+
748+ -- | Evaluate sequence of python statements
749+ exec :: (Namespace global , Namespace local )
750+ => global -- ^ Data type providing global variables dictionary
751+ -> local -- ^ Data type providing local variables dictionary
752+ -> PyQuote -- ^ Source code
753+ -> Py ()
754+ exec globals locals q = runProgram $ do
755+ p_py <- unsafeWithCode q. code
756+ p_globals <- takeOwnership =<< progPy (basicNamespaceDict globals)
757+ p_locals <- takeOwnership =<< progPy (basicNamespaceDict locals)
758+ progPy $ do
759+ q. binder. bind p_locals
760+ Py [C. block | void {
761+ PyObject* globals = $(PyObject* p_globals);
762+ PyObject* locals = $(PyObject* p_locals);
763+ // Compile code
764+ PyObject *code = Py_CompileString($(char* p_py), "<interactive>", Py_file_input);
765+ if( PyErr_Occurred() ){
766+ return;
767+ }
768+ // Execute statements
769+ PyObject* res = PyEval_EvalCode(code, globals, locals);
770+ Py_XDECREF(res);
771+ Py_DECREF(code);
772+ } |]
773+ checkThrowPyError
774+ {-# SPECIALIZE exec :: Main -> Main -> PyQuote -> Py () #-}
775+ {-# SPECIALIZE exec :: Main -> Temp -> PyQuote -> Py () #-}
776+
777+ -- | Obtain pointer to code
778+ unsafeWithCode :: Code -> Program r (Ptr CChar )
779+ unsafeWithCode (Code bs) = Program $ ContT $ \ fun ->
780+ Py (BS. unsafeUseAsCString bs $ unsafeRunPy . fun)
781+
782+
665783----------------------------------------------------------------
666784-- Debugging
667785----------------------------------------------------------------
0 commit comments