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
6 changes: 2 additions & 4 deletions inline-python.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ extra-doc-files:
ChangeLog.md
extra-source-files:
include/inline-python.h
data-files:
py/bound-vars.py

source-repository head
Expand Down Expand Up @@ -55,6 +54,8 @@ Library
, transformers >=0.4
, inline-c >=0.9.1
, template-haskell -any
, text >=2
, bytestring
hs-source-dirs: src
include-dirs: include
c-sources: cbits/python.c
Expand All @@ -72,9 +73,6 @@ Library
Python.Internal.Program
Python.Internal.Types
Python.Internal.Util
Paths_inline_python
Autogen-modules:
Paths_inline_python

----------------------------------------------------------------
library test
Expand Down
11 changes: 8 additions & 3 deletions py/bound-vars.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,20 @@
import ast
import sys
import re
import base64

mode = sys.argv[1]
is_hs = re.compile('.*_hs$')
code = ast.parse(sys.stdin.read(), '<interactive>', mode)

def extract_hs_vars(code):
for node in ast.walk(code):
if isinstance(node, ast.Name) and is_hs.match(node.id):
yield node.id

for nm in set(extract_hs_vars(code)):
print(nm)
def print_hs_vars(src):
code = ast.parse(src, '<interactive>', mode)
for nm in set(extract_hs_vars(code)):
print(nm)

def decode_and_print(codeB64):
print_hs_vars(base64.b16decode(codeB64, casefold=True).decode('utf8'))
17 changes: 12 additions & 5 deletions src/Python/Internal/Eval.hs
Original file line number Diff line number Diff line change
Expand Up @@ -123,17 +123,24 @@ C.include "<inline-python.h>"
runPy :: Py a -> IO a
-- See NOTE: [Python and threading]
runPy py
-- Multithreaded RTS
| rtsSupportsBoundThreads = runInBoundThread $ mask_ $ unPy $ ensureGIL py
-- Single-threaded RTS
| otherwise = mask_ $ unPy $ ensureGIL py

| rtsSupportsBoundThreads = runInBoundThread go -- Multithreaded RTS
| otherwise = go -- Single-threaded RTS
where
-- We check whether interpreter is initialized. Throw exception if
-- it wasn't. Better than segfault isn't it?
go = mask_ $ checkInitialized >> unPy (ensureGIL py)

-- | Execute python action. This function is unsafe and should be only
-- called in thread of interpreter.
unPy :: Py a -> IO a
unPy (Py io) = io

checkInitialized :: IO ()
checkInitialized =
[CU.exp| int { Py_IsInitialized() } |] >>= \case
0 -> error "Python is not initialized"
_ -> pure ()



----------------------------------------------------------------
Expand Down
26 changes: 23 additions & 3 deletions src/Python/Internal/EvalQQ.hs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ module Python.Internal.EvalQQ
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
import Control.Monad.Trans.Cont
import Data.Bits
import Data.Char
import Data.ByteString qualified as BS
import Data.Text qualified as T
import Data.Text.Encoding qualified as T
import Foreign.C.Types
import Foreign.Ptr
import Foreign.Storable
Expand All @@ -34,7 +38,7 @@ import Python.Internal.Types
import Python.Internal.Program
import Python.Internal.Eval
import Python.Inline.Literal
import Paths_inline_python (getDataFileName)


----------------------------------------------------------------
C.context (C.baseCtx <> pyCtx)
Expand Down Expand Up @@ -148,16 +152,32 @@ basicDecref o = Py [CU.exp| void { Py_DECREF($(PyObject* o)) } |]
-- TH generator
----------------------------------------------------------------

script :: String
script = $( do let path = "py/bound-vars.py"
TH.addDependentFile path
TH.lift =<< TH.runIO (readFile path)
)

-- | Generate TH splice which updates python environment dictionary
-- and returns python source code.
expQQ :: String -- ^ Python evaluation mode: @exec@/@eval@
-> String -- ^ Python source code
-> TH.Q TH.Exp
expQQ mode src = do
script <- liftIO $ getDataFileName "py/bound-vars.py"
antis <- liftIO $ do
(code, stdout, stderr) <- readProcessWithExitCode "python" [script, mode] src
-- We've embedded script into library and we need to pass source
-- code of QQ to a script. It can contain whatever symbols so to
-- be safe it's base16 encode. This encoding is very simple and we
-- don't care much about efficiency here
(code, stdout, stderr) <- readProcessWithExitCode "python" ["-", mode]
$ unlines [ script
, "decode_and_print('" <>
concat [ [ intToDigit $ fromIntegral (w `shiftR` 4)
, intToDigit $ fromIntegral (w .&. 15) ]
| w <- BS.unpack $ T.encodeUtf8 $ T.pack src
]
<> "')"
]
case code of
ExitSuccess -> pure $ words stdout
ExitFailure{} -> error stderr
Expand Down
Loading