From dac27d6d9140158963e39646252cb33cdb48efcb Mon Sep 17 00:00:00 2001 From: Alexey Khudyakov Date: Tue, 28 Jul 2026 00:09:05 +0300 Subject: [PATCH 1/9] Typo --- src/Python/Internal/Eval.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Python/Internal/Eval.hs b/src/Python/Internal/Eval.hs index bbed590..7a24a64 100644 --- a/src/Python/Internal/Eval.hs +++ b/src/Python/Internal/Eval.hs @@ -518,7 +518,7 @@ runPyInMain py pure ( atomically (releaseLock tid_main) , evalInOtherThread tid_main eval_lock ) - -- If we513 can grab lock and main thread taken lock we're + -- If we can grab lock and main thread taken lock we're -- already executing on main thread. We can simply execute code Locked t ts | t /= tid From afdb4e3f136759ccf8dc3d1e97360eea7afcc6a8 Mon Sep 17 00:00:00 2001 From: Alexey Khudyakov Date: Sun, 7 Jun 2026 01:40:31 +0300 Subject: [PATCH 2/9] Use python3-config too find necessary flags This allows to build inline-python with python provided by conda/uv/etc since they provide python3-config but not pkg-config Idea is to hide it behind cabal flag but so far it works unconditionally Sublibrary has ot be dropped. Custom build doesn't work with it --- Setup.hs | 58 ++++++++++++++++++++++++++++++++++++++++ inline-python.cabal | 64 +++++++++++++++++++++++++-------------------- 2 files changed, 93 insertions(+), 29 deletions(-) create mode 100644 Setup.hs diff --git a/Setup.hs b/Setup.hs new file mode 100644 index 0000000..e89352d --- /dev/null +++ b/Setup.hs @@ -0,0 +1,58 @@ +{-# LANGUAGE OverloadedRecordDot #-} +import Data.Either + +import Distribution.Simple +import Distribution.Simple.Setup +import Distribution.Simple.LocalBuildInfo +import Distribution.PackageDescription +import Distribution.Types.BuildInfo +import Distribution.Types.PackageDescription +import Distribution.Types.CondTree +import Distribution.Types.ForeignLib +import Distribution.Types.UnqualComponentName +import Distribution.Utils.Path +import System.Process +import Data.List (intercalate, nub) + +import Distribution.Compat.Lens +import qualified Distribution.Types.BuildInfo.Lens as L + +main :: IO () +main = defaultMainWithHooks $ simpleUserHooks + { confHook = patchedConfHook } + +patchedConfHook + :: (GenericPackageDescription, HookedBuildInfo) + -> ConfigFlags + -> IO LocalBuildInfo +patchedConfHook (gpd, hbi) flags = do + cflags_raw <- readProcess "python3-config" ["--cflags"] "" + ldflags_raw <- readProcess "python3-config" ["--embed", "--ldflags"] "" + -- Split flags + let (inc_dirs,cflags) = partitionEithers + [ case flag of + '-':'I':path -> Left path + _ -> Right flag + | flag <- tokenizeArguments cflags_raw + ] + (ldflags, (libs, lib_dirs)) = fmap partitionEithers $ partitionEithers + [ case flag of + '-':'l':path -> Right (Left path) + '-':'L':path -> Right (Right path) + _ -> Left flag + | flag <- tokenizeArguments ldflags_raw + ] + let tweakLib lib = case lib.libName of + LMainLibName -> lib & L.ccOptions %~ (++ cflags) + & L.includeDirs %~ (++ inc_dirs) + & L.ldOptions %~ (++ ldflags) + & L.extraLibs %~ (++ libs) + & L.extraLibDirs %~ (++ lib_dirs) + _ -> lib + let lib' = (fmap . fmap) tweakLib (condLibrary gpd) + let gpd' = gpd { condLibrary = lib' } + confHook simpleUserHooks (gpd', hbi) flags + + +tokenizeArguments :: String -> [String] +tokenizeArguments = words diff --git a/inline-python.cabal b/inline-python.cabal index b98cad0..7a2d204 100644 --- a/inline-python.cabal +++ b/inline-python.cabal @@ -1,6 +1,8 @@ Cabal-Version: 3.0 -Build-Type: Simple - +--Build-Type: Hooks +Build-Type: Custom +--Build-Type: Simple + Name: inline-python Version: 0.2.1.0 Synopsis: Python interpreter embedded into haskell. @@ -31,6 +33,14 @@ Tested-With: GHC == 9.10.2 GHC == 9.12.2 + +custom-setup + setup-depends: base +-- , Cabal-hooks + , Cabal + , Cabal-syntax + , process + source-repository head type: git location: http://github.com/Shimuuar/inline-python @@ -71,8 +81,8 @@ Library hs-source-dirs: src include-dirs: include c-sources: cbits/python.c - cc-options: -g -Wall - pkgconfig-depends: python3-embed >= 3.10 +-- cc-options: -g -Wall +-- pkgconfig-depends: python3-embed >= 3.10 -- Exposed-modules: Python.Inline @@ -89,11 +99,10 @@ Library Python.Internal.Types Python.Internal.Util ----------------------------------------------------------------- -library test - import: language +common tests Default-Extensions: QuasiQuotes + hs-source-dirs: test build-depends: base , inline-python , tasty >=1.2 @@ -106,38 +115,35 @@ library test , vector , bytestring , text - hs-source-dirs: test - Exposed-modules: - TST.Run - TST.ToPy - TST.FromPy - TST.Callbacks - TST.Roundtrip - TST.Util + -- Running tests using several threads does very good job at finding threading -- bugs. Especially deadlocks test-suite inline-python-tests - import: language + import: language, tests type: exitcode-stdio-1.0 Ghc-options: -threaded -rtsopts -with-rtsopts=-N2 - hs-source-dirs: test/exe - main-is: main.hs - build-depends: base - , inline-python - , inline-python:test - , tasty + main-is: exe/main.hs + Other-modules: + TST.Run + TST.ToPy + TST.FromPy + TST.Callbacks + TST.Roundtrip + TST.Util test-suite inline-python-tests1 - import: language + import: language, tests type: exitcode-stdio-1.0 Ghc-options: -rtsopts - hs-source-dirs: test/exe - main-is: main.hs - build-depends: base - , inline-python - , inline-python:test - , tasty + main-is: exe/main.hs + Other-modules: + TST.Run + TST.ToPy + TST.FromPy + TST.Callbacks + TST.Roundtrip + TST.Util benchmark pysmall import: language From a82811df7672aeda603aadb12f3bd36e7b0daf64 Mon Sep 17 00:00:00 2001 From: Alexey Khudyakov Date: Tue, 28 Jul 2026 00:21:02 +0300 Subject: [PATCH 3/9] Only use python3-config if cabal flag is set By default use pkg-config --- Setup.hs | 59 ++++++++++++++++++++++++--------------------- inline-python.cabal | 11 +++++++-- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/Setup.hs b/Setup.hs index e89352d..9cd39e2 100644 --- a/Setup.hs +++ b/Setup.hs @@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedRecordDot #-} +{-# LANGUAGE OverloadedStrings #-} import Data.Either import Distribution.Simple @@ -25,34 +26,36 @@ patchedConfHook :: (GenericPackageDescription, HookedBuildInfo) -> ConfigFlags -> IO LocalBuildInfo -patchedConfHook (gpd, hbi) flags = do - cflags_raw <- readProcess "python3-config" ["--cflags"] "" - ldflags_raw <- readProcess "python3-config" ["--embed", "--ldflags"] "" - -- Split flags - let (inc_dirs,cflags) = partitionEithers - [ case flag of - '-':'I':path -> Left path - _ -> Right flag - | flag <- tokenizeArguments cflags_raw - ] - (ldflags, (libs, lib_dirs)) = fmap partitionEithers $ partitionEithers - [ case flag of - '-':'l':path -> Right (Left path) - '-':'L':path -> Right (Right path) - _ -> Left flag - | flag <- tokenizeArguments ldflags_raw - ] - let tweakLib lib = case lib.libName of - LMainLibName -> lib & L.ccOptions %~ (++ cflags) - & L.includeDirs %~ (++ inc_dirs) - & L.ldOptions %~ (++ ldflags) - & L.extraLibs %~ (++ libs) - & L.extraLibDirs %~ (++ lib_dirs) - _ -> lib - let lib' = (fmap . fmap) tweakLib (condLibrary gpd) - let gpd' = gpd { condLibrary = lib' } - confHook simpleUserHooks (gpd', hbi) flags - +patchedConfHook (gpd, hbi) flags + | Just True <- lookupFlagAssignment ("python3-config") $ configConfigurationsFlags flags + = do cflags_raw <- readProcess "python3-config" ["--cflags"] "" + ldflags_raw <- readProcess "python3-config" ["--embed", "--ldflags"] "" + -- Split flags + let (inc_dirs,cflags) = partitionEithers + [ case flag of + '-':'I':path -> Left path + _ -> Right flag + | flag <- tokenizeArguments cflags_raw + ] + (ldflags, (libs, lib_dirs)) = fmap partitionEithers $ partitionEithers + [ case flag of + '-':'l':path -> Right (Left path) + '-':'L':path -> Right (Right path) + _ -> Left flag + | flag <- tokenizeArguments ldflags_raw + ] + let tweakLib lib = case lib.libName of + LMainLibName -> lib & L.ccOptions %~ (++ cflags) + & L.includeDirs %~ (++ inc_dirs) + & L.ldOptions %~ (++ ldflags) + & L.extraLibs %~ (++ libs) + & L.extraLibDirs %~ (++ lib_dirs) + _ -> lib + let lib' = (fmap . fmap) tweakLib (condLibrary gpd) + let gpd' = gpd { condLibrary = lib' } + confHook simpleUserHooks (gpd', hbi) flags + | otherwise = do + confHook simpleUserHooks (gpd, hbi) flags tokenizeArguments :: String -> [String] tokenizeArguments = words diff --git a/inline-python.cabal b/inline-python.cabal index 7a2d204..ee1f03a 100644 --- a/inline-python.cabal +++ b/inline-python.cabal @@ -41,6 +41,11 @@ custom-setup , Cabal-syntax , process +flag python3-config + Description: Use python3-config instead of pkg-config + Manual: True + Default: False + source-repository head type: git location: http://github.com/Shimuuar/inline-python @@ -81,8 +86,10 @@ Library hs-source-dirs: src include-dirs: include c-sources: cbits/python.c --- cc-options: -g -Wall --- pkgconfig-depends: python3-embed >= 3.10 + cc-options: -g -Wall + -- Else we use python3-config in Setup.hs + if !flag(python3-config) + pkgconfig-depends: python3-embed >= 3.10 -- Exposed-modules: Python.Inline From 9c6a8b8b56110adb3812d264a30ea109f5d30454 Mon Sep 17 00:00:00 2001 From: Alexey Khudyakov Date: Tue, 28 Jul 2026 00:29:41 +0300 Subject: [PATCH 4/9] Formatting --- Setup.hs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Setup.hs b/Setup.hs index 9cd39e2..ae95b87 100644 --- a/Setup.hs +++ b/Setup.hs @@ -51,9 +51,10 @@ patchedConfHook (gpd, hbi) flags & L.extraLibs %~ (++ libs) & L.extraLibDirs %~ (++ lib_dirs) _ -> lib - let lib' = (fmap . fmap) tweakLib (condLibrary gpd) - let gpd' = gpd { condLibrary = lib' } - confHook simpleUserHooks (gpd', hbi) flags + confHook simpleUserHooks + ( gpd { condLibrary = (fmap . fmap) tweakLib (condLibrary gpd) } + , hbi + ) flags | otherwise = do confHook simpleUserHooks (gpd, hbi) flags From ffaf0964eb3fe9a12cf70069908594447cf29d74 Mon Sep 17 00:00:00 2001 From: Alexey Khudyakov Date: Sun, 23 Aug 2026 15:34:09 +0300 Subject: [PATCH 5/9] Add CI configuration which builds library with -fpython3-config --- .github/workflows/ci.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4510eea..f4cba9e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ defaults: jobs: cabal: - name: ${{ matrix.os }} / GHC ${{ matrix.ghc }} / ${{ matrix.python-version }} + name: ${{ matrix.os }} / GHC ${{ matrix.ghc }} / ${{ matrix.python-version }} / ${{ matrix.cabal-flags }} runs-on: ${{ matrix.os }} strategy: matrix: @@ -29,6 +29,8 @@ jobs: - { cabal: "3.14", os: ubuntu-latest, ghc: "9.10.2", python-version: "3.12" } - { cabal: "3.14", os: ubuntu-latest, ghc: "9.10.2", python-version: "3.13" } - { cabal: "3.14", os: ubuntu-latest, ghc: "9.10.2", python-version: "3.14" } + # Build with python3-config instead of pkg-config + - { cabal: "3.14", os: ubuntu-latest, ghc: "9.10.2", python-version: "3.12", cabal-flags: "-fpython3-config" } fail-fast: false steps: # ---------------- @@ -58,6 +60,8 @@ jobs: python -V pkg-config python3-embed --cflags pkg-config python3-embed --libs + python3-config --cflags + python3-config --libs # ---------------- - name: Make sdist run: | @@ -77,7 +81,7 @@ jobs: run: | if [ "${{ matrix.skip-test }}" == "" ]; then FLAG_TEST=--enable-test; fi if [ "${{ matrix.skip-bench }}" == "" ]; then FLAG_BENCH=--enable-benchmarks; fi - cabal configure $FLAG_TEST $FLAG_BENCH + cabal configure $FLAG_TEST $FLAG_BENCH ${{ matrix.cabal-flags }} cabal build all --write-ghc-environment-files=always # ---------------- - name: Test From 75bec6771e1314d2563478d2cbe0b43541367dcb Mon Sep 17 00:00:00 2001 From: Alexey Khudyakov Date: Sun, 23 Aug 2026 15:43:32 +0300 Subject: [PATCH 6/9] Make cabal check happy by using noncimmital upper bounds --- inline-python.cabal | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/inline-python.cabal b/inline-python.cabal index ee1f03a..476f9e2 100644 --- a/inline-python.cabal +++ b/inline-python.cabal @@ -1,7 +1,5 @@ Cabal-Version: 3.0 ---Build-Type: Hooks Build-Type: Custom ---Build-Type: Simple Name: inline-python Version: 0.2.1.0 @@ -35,11 +33,10 @@ Tested-With: custom-setup - setup-depends: base --- , Cabal-hooks - , Cabal - , Cabal-syntax - , process + setup-depends: base >=4.14 && <5 + , Cabal >=3.0 && <4 + , Cabal-syntax >=3.6 && <4 + , process >=1.6 && <2 flag python3-config Description: Use python3-config instead of pkg-config From 394f0131e7707da88fc177858682ca083a91551f Mon Sep 17 00:00:00 2001 From: Alexey Khudyakov Date: Sun, 23 Aug 2026 16:26:22 +0300 Subject: [PATCH 7/9] Fix build with GHC 9.12 and drop 9.2 Latter is just too painful to support with custom setup. Types used by Cabal are not reexports from Cabal-syntax yet --- .github/workflows/ci.yml | 1 - Setup.hs | 29 +++++++++++++++++++++++------ inline-python.cabal | 5 ++--- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f4cba9e..bdef29b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,6 @@ jobs: strategy: matrix: include: - - { cabal: "3.14", os: ubuntu-latest, ghc: "9.2.8", python-version: "3.12" } - { cabal: "3.14", os: ubuntu-latest, ghc: "9.4.8", python-version: "3.12" } - { cabal: "3.14", os: ubuntu-latest, ghc: "9.6.6", python-version: "3.12" } - { cabal: "3.14", os: ubuntu-latest, ghc: "9.8.4", python-version: "3.12" } diff --git a/Setup.hs b/Setup.hs index ae95b87..3b355fa 100644 --- a/Setup.hs +++ b/Setup.hs @@ -1,3 +1,6 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-} import Data.Either @@ -12,16 +15,19 @@ import Distribution.Types.CondTree import Distribution.Types.ForeignLib import Distribution.Types.UnqualComponentName import Distribution.Utils.Path -import System.Process +import Distribution.Compat.Lens +import Distribution.Types.BuildInfo.Lens qualified as L + import Data.List (intercalate, nub) +import System.Process -import Distribution.Compat.Lens -import qualified Distribution.Types.BuildInfo.Lens as L main :: IO () main = defaultMainWithHooks $ simpleUserHooks { confHook = patchedConfHook } + + patchedConfHook :: (GenericPackageDescription, HookedBuildInfo) -> ConfigFlags @@ -29,7 +35,7 @@ patchedConfHook patchedConfHook (gpd, hbi) flags | Just True <- lookupFlagAssignment ("python3-config") $ configConfigurationsFlags flags = do cflags_raw <- readProcess "python3-config" ["--cflags"] "" - ldflags_raw <- readProcess "python3-config" ["--embed", "--ldflags"] "" + ldflags_raw <- readProcess "python3-config" ["--embed", "--ldflags"] "" -- Split flags let (inc_dirs,cflags) = partitionEithers [ case flag of @@ -46,10 +52,10 @@ patchedConfHook (gpd, hbi) flags ] let tweakLib lib = case lib.libName of LMainLibName -> lib & L.ccOptions %~ (++ cflags) - & L.includeDirs %~ (++ inc_dirs) + & L.includeDirs %~ (++ (toSymb <$> inc_dirs)) & L.ldOptions %~ (++ ldflags) & L.extraLibs %~ (++ libs) - & L.extraLibDirs %~ (++ lib_dirs) + & L.extraLibDirs %~ (++ (toSymb <$> lib_dirs)) _ -> lib confHook simpleUserHooks ( gpd { condLibrary = (fmap . fmap) tweakLib (condLibrary gpd) } @@ -60,3 +66,14 @@ patchedConfHook (gpd, hbi) flags tokenizeArguments :: String -> [String] tokenizeArguments = words + +---------------------------------------------------------------- +-- Compatibility + +#if MIN_VERSION_Cabal_syntax(3,14,0) +toSymb :: String -> SymbolicPathX 'AllowAbsolute from to +toSymb = makeSymbolicPath +#else +toSymb :: String -> String +toSymb = id +#endif diff --git a/inline-python.cabal b/inline-python.cabal index 476f9e2..04a7704 100644 --- a/inline-python.cabal +++ b/inline-python.cabal @@ -24,7 +24,6 @@ extra-source-files: py/bound-vars.py Tested-With: - GHC == 9.2.8 GHC == 9.4.8 GHC == 9.6.7 GHC == 9.8.4 @@ -33,7 +32,7 @@ Tested-With: custom-setup - setup-depends: base >=4.14 && <5 + setup-depends: base >=4.15 && <5 , Cabal >=3.0 && <4 , Cabal-syntax >=3.6 && <4 , process >=1.6 && <2 @@ -67,7 +66,7 @@ common language ---------------------------------------------------------------- Library import: language - Build-Depends: base >=4.14 && <5 + Build-Depends: base >=4.15 && <5 , primitive >=0.6.2 , vector >=0.13.2 , containers >=0.5 From 965aee396051655d4773424682e8e9c5937a9ab6 Mon Sep 17 00:00:00 2001 From: Alexey Khudyakov Date: Sun, 23 Aug 2026 17:29:22 +0300 Subject: [PATCH 8/9] Add GHC 9.14.1 to CI matrix --- .github/workflows/ci.yml | 1 + inline-python.cabal | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bdef29b..9a7d927 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,7 @@ jobs: - { cabal: "3.14", os: ubuntu-latest, ghc: "9.8.4", python-version: "3.12" } - { cabal: "3.14", os: ubuntu-latest, ghc: "9.10.2", python-version: "3.12" } - { cabal: "3.14", os: ubuntu-latest, ghc: "9.12.2", python-version: "3.12" } + - { cabal: "3.14", os: ubuntu-latest, ghc: "9.14.1", python-version: "3.12" } # Scan over supported python versions - { cabal: "3.14", os: ubuntu-latest, ghc: "9.10.2", python-version: "3.10" } - { cabal: "3.14", os: ubuntu-latest, ghc: "9.10.2", python-version: "3.11" } diff --git a/inline-python.cabal b/inline-python.cabal index 04a7704..bd72c08 100644 --- a/inline-python.cabal +++ b/inline-python.cabal @@ -29,6 +29,7 @@ Tested-With: GHC == 9.8.4 GHC == 9.10.2 GHC == 9.12.2 + GHC == 9.14.1 custom-setup From 002a729d564e68c4814aab1e8c2f9af97fed3ad4 Mon Sep 17 00:00:00 2001 From: Alexey Khudyakov Date: Sun, 23 Aug 2026 18:13:34 +0300 Subject: [PATCH 9/9] Add note on -new cabal flag --- src/Python/Inline.hs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Python/Inline.hs b/src/Python/Inline.hs index 82624bf..d387930 100644 --- a/src/Python/Inline.hs +++ b/src/Python/Inline.hs @@ -105,6 +105,7 @@ import Python.Internal.Eval -- -- Here's list of common problems and solutions and workarounds. -- +-- -- 1. __@inline-python@ cannot find libraries__ -- -- @inline-python@ may look for modules in wrong place. Set @@ -112,7 +113,23 @@ import Python.Internal.Eval -- right way. -- -- --- 2. __Linker error in GHCi__ +-- 2. __Picking correct python interpreter__ +-- +-- Python's version @inline-python@ uses is determined by @libpython3@ +-- it's linked with. This is decided when package is build. Normally +-- @pkg-config@ is used and this means using whatever distribution is +-- shipping. It's also possible to use @python3-config@ program by +-- specifying manual cabal flag @-fpython3-config@. +-- +-- If it's desired that @inline-python@ should use python installed by +-- conda\/uv\/etc @inline-python@ should be built in environment where +-- desired python version is active and use @-fpython3-config@ flag. +-- This could be done by adding following to cabal.project: +-- +-- > constraints: inline-python -fpython3-config +-- +-- +-- 3. __Linker error in GHCi__ -- -- Attempting to import library using C extensions from ghci may -- result in linker failing to find symbols from @libpython@ like