hint 0.9.0.5 → 0.9.0.6
raw patch · 6 files changed
+103/−29 lines, 6 filesdep +bytestringdep +textdep +typed-processPVP ok
version bump matches the API change (PVP)
Dependencies added: bytestring, text, typed-process
API changes (from Hackage documentation)
Files
- CHANGELOG.md +8/−1
- hint.cabal +5/−2
- src/Hint/InterpreterT.hs +1/−2
- src/Hint/Typecheck.hs +2/−2
- src/Language/Haskell/Interpreter.hs +0/−11
- unit-tests/run-unit-tests.hs +87/−11
CHANGELOG.md view
@@ -1,6 +1,13 @@-### 0.9.0.5+### 0.9.0.6 +* Fixes the 0.9.0.5 regression+* Small fix in documentation (thanks to Ed Behn)++### 0.9.0.5 (deprecated)+ * Support GHC 9.2.1+* Deprecated because it breaks the common pattern of using+ 'unsafeRunInterpreterWithArgs' to load a custom package database. ### 0.9.0.4
hint.cabal view
@@ -1,5 +1,5 @@ name: hint-version: 0.9.0.5+version: 0.9.0.6 description: This library defines an Interpreter monad. It allows to load Haskell modules, browse them, type-check and evaluate strings with Haskell@@ -9,7 +9,7 @@ It is, essentially, a huge subset of the GHC API wrapped in a simpler API. -synopsis: Runtime Haskell interpreter (GHC API wrapper)+synopsis: A Haskell interpreter built on top of the GHC API category: Language, Compilers/Interpreters license: BSD3 license-file: LICENSE@@ -37,12 +37,15 @@ default-language: Haskell2010 build-depends: base == 4.*,+ bytestring, hint, HUnit, directory, filepath, exceptions >= 0.10.0, stm,+ text,+ typed-process, -- packages used by setImports calls containers
src/Hint/InterpreterT.hs view
@@ -77,8 +77,6 @@ -> InterpreterT m () initialize args = do logger <- fromSession ghcLogger- runGhc $ GHC.modifyLogger (const logger)- -- Set a custom log handler, to intercept error messages :S df0 <- runGhc GHC.getSessionDynFlags @@ -91,6 +89,7 @@ -- Observe that, setSessionDynFlags loads info on packages -- available; calling this function once is mandatory!+ runGhc $ GHC.modifyLogger (const logger) _ <- runGhc $ GHC.setSessionDynFlags df2 let extMap = [ (GHC.flagSpecName flagSpec, GHC.flagSpecFlag flagSpec)
src/Hint/Typecheck.hs view
@@ -23,8 +23,8 @@ -- | Tests if the expression type checks. ----- NB. Be careful if there is `-fdefer-type-errors` involved.--- Perhaps unsurprisingly, that can falsely make @typeChecks@ and @getType@+-- NB. Be careful if @unsafeSetGhcOption "-fdefer-type-errors"@ is used.+-- Perhaps unsurprisingly, that can falsely make @typeChecks@ and @typeChecksWithDetails@ -- return @True@ and @Right _@ respectively. typeChecks :: MonadInterpreter m => String -> m Bool typeChecks expr = (True <$ typeOf expr)
src/Language/Haskell/Interpreter.hs view
@@ -1,14 +1,3 @@--------------------------------------------------------------------------------- |--- Module : Language.Haskell.Interpreter--- License : BSD-style------ Maintainer : mvdan@mvdan.cc--- Stability : experimental--- Portability : non-portable (GHC API)------ A Haskell interpreter built on top of the GHC API------------------------------------------------------------------------------ module Language.Haskell.Interpreter( -- * The interpreter monad transformer MonadInterpreter(..), InterpreterT, Interpreter,
unit-tests/run-unit-tests.hs view
@@ -11,12 +11,18 @@ import Control.Concurrent.MVar import Control.Concurrent.STM +import qualified Data.ByteString.Lazy as ByteString+import qualified Data.Text as Text+import qualified Data.Text.Encoding as Text+import Data.Function ((&)) import Data.IORef import System.IO import System.FilePath import System.Directory+import System.Environment (getEnvironment, unsetEnv) import System.Exit+import System.Process.Typed #if defined(mingw32_HOST_OS) || defined(__MINGW32__) #else import System.Posix.Signals@@ -26,6 +32,7 @@ import qualified Test.HUnit as HUnit import Language.Haskell.Interpreter+import Language.Haskell.Interpreter.Unsafe test_reload_modified :: TestCase test_reload_modified = TestCase "reload_modified" [mod_file] $ do@@ -291,6 +298,63 @@ ,"type instance Foo x = ()"] mod_file = "TEST_NormalizeType.hs" +test_package_db :: IOTestCase+test_package_db = IOTestCase "package_db" [dir] $ \wrapInterp -> do+ setup+ ghcVersionOutput <- readProcessStdout_ $ proc "ghc" ["--version"]+ let ghcVersion+ :: String+ ghcVersion+ = ghcVersionOutput+ -- "The Glorious Glasgow Haskell Compilation System, version 8.8.4" :: ByteString.Lazy+ & ByteString.toStrict+ -- "The Glorious Glasgow Haskell Compilation System, version 8.8.4" :: ByteString+ & Text.decodeUtf8+ -- "The Glorious Glasgow Haskell Compilation System, version 8.8.4" :: Text+ & Text.unpack+ -- "The Glorious Glasgow Haskell Compilation System, version 8.8.4" :: String+ & words+ -- ["The","Glorious","Glasgow","Haskell","Compilation","System,","version","8.8.4"]+ & last+ -- "8.8.4"+ let pkgdb = dir </> "dist-newstyle" </> "packagedb" </> ("ghc-" ++ ghcVersion)+ ghc_args = ["-package-db=" ++ pkgdb]++ -- stack sets GHC_ENVIRONMENT to a file which pins down the versions of+ -- all the packages we can load, and since it does not list my-package,+ -- we cannot load it.+ unsetEnv "GHC_ENVIRONMENT"++ wrapInterp (unsafeRunInterpreterWithArgs ghc_args) $ do+ --succeeds (setImports [mod]) @@? "module from package-db must be visible"+ setImports [mod]+ --+ where pkg = "my-package"+ dir = pkg+ mod_file = dir </> mod <.> "hs"+ mod = "MyModule"+ cabal_file = dir </> pkg <.> "cabal"+ setup = do createDirectory dir+ writeFile cabal_file $ unlines+ [ "cabal-version: 2.4"+ , "name: " ++ pkg+ , "version: 0.1.0.0"+ , ""+ , "library"+ , " exposed-modules: " ++ mod+ ]+ writeFile mod_file $ unlines+ [ "{-# LANGUAGE NoImplicitPrelude #-}"+ , "module " ++ mod ++ " where"+ ]+ env <- getEnvironment+ runProcess_+ $ setWorkingDir dir+ $ -- stack sets GHC_PACKAGE_PATH, but cabal complains+ -- that it cannot run if that variable is set.+ setEnv (filter ((/= "GHC_PACKAGE_PATH") . fst) env)+ $ proc "cabal" ["build"]+ -- earlier versions of hint were accidentally overwriting the signal handlers -- for ^C and others. --@@ -299,9 +363,9 @@ -- succeeds when executed from ghci and ghcid, regardless of whether the problematic -- behaviour has been fixed or not. test_signal_handlers :: IOTestCase-test_signal_handlers = IOTestCase "signal_handlers" [] $ \runInterp -> do+test_signal_handlers = IOTestCase "signal_handlers" [] $ \wrapInterp -> do #if defined(mingw32_HOST_OS) || defined(__MINGW32__)- runInterp $ do+ wrapInterp runInterpreter $ do pure () #else signalDetectedRef <- newIORef False@@ -311,7 +375,7 @@ acquire = installHandler sigINT (Catch detectSignal) Nothing release handler = installHandler sigINT handler Nothing r <- bracket acquire release $ \_ -> do- runInterp $ do+ wrapInterp runInterpreter $ do liftIO $ do r <- try $ do raiseSignal sigINT@@ -357,6 +421,7 @@ ioTests :: [IOTestCase] ioTests = [test_signal_handlers+ ,test_package_db ] main :: IO ()@@ -406,16 +471,23 @@ noInterpreterError (Left e) = assertFailure (show e) noInterpreterError (Right a) = pure a -data IOTestCase = IOTestCase String [FilePath] ((Interpreter () -> IO (Either InterpreterError ())) -> IO (Either InterpreterError ()))+data IOTestCase = IOTestCase+ String -- test name+ [FilePath] -- temporary files and folders to delete after the test+ ( ( (Interpreter () -> IO (Either InterpreterError ()))+ -> (Interpreter () -> IO (Either InterpreterError ()))+ ) -- please wrap your 'runInterpreter' calls with this+ -> IO (Either InterpreterError ()) -- create temporary files and run the test+ ) runIOTests :: Bool -> [IOTestCase] -> IO HUnit.Counts runIOTests sandboxed = HUnit.runTestTT . HUnit.TestList . map build- where build (IOTestCase title tmps test) = HUnit.TestLabel title $- HUnit.TestCase test_case+ where build (IOTestCase title tmps test)+ = HUnit.TestLabel title $ HUnit.TestCase test_case where test_case = go `finally` clean_up clean_up = mapM_ removeIfExists tmps- go = do r <- test (\body -> runInterpreter- (when sandboxed setSandbox >> body))+ wrapInterp runInterp body = runInterp (when sandboxed setSandbox >> body)+ go = do r <- test wrapInterp noInterpreterError r removeIfExists f = do existsF <- doesFileExist f if existsF@@ -423,12 +495,16 @@ else do existsD <- doesDirectoryExist f when existsD $- removeDirectory f+ removeDirectoryRecursive f -data TestCase = TestCase String [FilePath] (Interpreter ())+data TestCase = TestCase+ String -- test name+ [FilePath] -- temporary files and folders to delete after the test+ (Interpreter ()) -- create temporary files and run the test runTests :: Bool -> [TestCase] -> IO HUnit.Counts runTests sandboxed = runIOTests sandboxed . map toIOTestCase where toIOTestCase :: TestCase -> IOTestCase- toIOTestCase (TestCase title tmps test) = IOTestCase title tmps ($ test)+ toIOTestCase (TestCase title tmps test) = IOTestCase title tmps $ \wrapInterp -> do+ wrapInterp runInterpreter test