diff --git a/src/Python/Inline/Eval.hs b/src/Python/Inline/Eval.hs index 759fc83..a9ee307 100644 --- a/src/Python/Inline/Eval.hs +++ b/src/Python/Inline/Eval.hs @@ -24,7 +24,7 @@ module Python.Inline.Eval , Module(..) ) where -import Data.ByteString.Unsafe qualified as BS +import Data.ByteString qualified as BS import Data.Text (Text) import Data.Text.Encoding qualified as T import Language.C.Inline qualified as C @@ -48,7 +48,7 @@ bindVar -> a -- ^ Variable value -> DictBinder bindVar name a = DictBinder $ \p_dict -> runProgram $ do - p_key <- progIOBracket $ BS.unsafeUseAsCString (T.encodeUtf8 name) + p_key <- progIOBracket $ BS.useAsCString (T.encodeUtf8 name) p_obj <- takeOwnership =<< progPy (throwOnNULL =<< basicToPy a) progPy $ do r <- Py [CU.block| int { diff --git a/src/Python/Internal/EvalQQ.hs b/src/Python/Internal/EvalQQ.hs index 8297309..cbdf467 100644 --- a/src/Python/Internal/EvalQQ.hs +++ b/src/Python/Internal/EvalQQ.hs @@ -8,7 +8,6 @@ module Python.Internal.EvalQQ ) where import Control.Monad.IO.Class -import Control.Monad.Catch import Data.Bits import Data.Char import Data.List (intercalate) @@ -29,7 +28,6 @@ import Language.Haskell.TH.Syntax qualified as TH import Python.Internal.Types import Python.Internal.Program import Python.Internal.Eval -import Python.Internal.CAPI import Python.Inline.Literal @@ -39,12 +37,16 @@ C.include "" ---------------------------------------------------------------- -- | Python's variable name encoded using UTF-8. It exists in order to --- avoid working with @String@ at runtime. +-- avoid working with @String@ at runtime. Bytestring should be NULL +-- terminated manually newtype PyVarName = PyVarName BS.ByteString deriving stock (Show, TH.Lift) +-- | PyVarName is being consumed using 'BS.unsafeUseAsCString' which +-- doesn't NULL terminate underlying buffer and python's C API +-- require it. We have to add terminator ourselves varName :: String -> PyVarName -varName = PyVarName . T.encodeUtf8 . T.pack +varName nm = PyVarName $ flip BS.snoc 0 $ T.encodeUtf8 (T.pack nm) unsafeWithPyVarName :: PyVarName -> Program r (Ptr CChar) unsafeWithPyVarName (PyVarName bs) diff --git a/src/Python/Internal/Types.hs b/src/Python/Internal/Types.hs index 91a6730..b918309 100644 --- a/src/Python/Internal/Types.hs +++ b/src/Python/Internal/Types.hs @@ -42,6 +42,8 @@ import Data.Coerce import Data.Int import Data.Word import Data.ByteString qualified as BS +import Data.ByteString.Lazy qualified as BL +import Data.ByteString.Builder qualified as BB import Data.Map.Strict qualified as Map import Data.Text qualified as T import Data.Text.Encoding qualified as T @@ -171,7 +173,13 @@ newtype Code = Code BS.ByteString -- -- @since 0.2@ codeFromText :: T.Text -> Code -codeFromText = Code . T.encodeUtf8 +-- NOTE: We consume code with BS.useAsCString and python C API expect +-- NULL-terminated string. We have to add terminator manually +codeFromText code + = Code + $ BL.toStrict + $ BB.toLazyByteString + $ T.encodeUtf8Builder code <> BB.word8 0 -- | Create properly encoded @Code@. This function doesn't check -- syntactic validity.