packages feed

PyF 0.7.0.0 → 0.7.1.0

raw patch · 6 files changed

+224/−11 lines, 6 filesdep +HUnitdep +deepseqdep +directorydep ~basedep ~template-haskelldep ~textPVP ok

version bump matches the API change (PVP)

Dependencies added: HUnit, deepseq, directory, filepath, hashable, temporary

Dependency ranges changed: base, template-haskell, text

API changes (from Hackage documentation)

Files

ChangeLog.md view
@@ -1,11 +1,18 @@ # Revision history for PyF +## 0.7.1.0 -- 2019-02-11++- Fixed: PyF was wrongly ignoring everything located after a non-doubled closing delimiter.+- New Feature: line break can be escaped with \, thus allowing string to start on a new line ignoring the initial backspace++## 0.7.0.0 -- 2019-02-04+ - Bump dependencies to megaparsec 7 - Error message are now tested - Name in template haskell splices are stable. This increases readability of error messages - Better error message for badly formated expression -# Formatting removal+### Formatting removal  - All monomorphic quasiquoters (`f`, `fString`, `fText`, `fIO`, `fLazyText`) are removed - Polymophic quasiquoter `f'` is renamed `f` and is the only entry point. Monomorphic users are encouraged to use the polymorphic quasiquoter with type annotation.
PyF.cabal view
@@ -1,5 +1,5 @@ name:                PyF-version:             0.7.0.0+version:             0.7.1.0 synopsis: Quasiquotations for a python like interpolated string formater description: Quasiquotations for a python like interpolated string formater. license:             BSD3@@ -43,14 +43,14 @@   ghc-options:         -Wall -threaded -rtsopts -with-rtsopts=-N   default-language:    Haskell2010 --- test-suite pyf-failure---   type:                exitcode-stdio-1.0---   hs-source-dirs:      test---   main-is:             SpecFail.hs---   build-tools:  python3---   build-depends:       base, hspec, text, template-haskell, process, hspec-golden, hspec, temporary, hashable---   ghc-options:         -Wall -threaded -rtsopts -with-rtsopts=-N---   default-language:    Haskell2010+test-suite pyf-failure+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             SpecFail.hs+  build-tools:  python3+  build-depends:       base, hspec, text, template-haskell, process, hspec, temporary, hashable, filepath, deepseq, directory, HUnit+  ghc-options:         -Wall -threaded -rtsopts -with-rtsopts=-N+  default-language:    Haskell2010  source-repository head   type:     git
Readme.md view
@@ -126,6 +126,19 @@ "~~3.14" ``` +## Multi-line strings++You can ignore a line break with `\` if needed. For example:++```haskell+[f|\+- a+- b\+|]+```++Will returns `-a\n-b`. Note how the first and last line breaks are ignored.+ # Output type  *PyF* main entry point `f` is polymorphic and can represents `Text`, lazy `Text`, `String`, lazy text `Builder` or `IO` operations. Most of the time, type inference will do the right thing for you, but you may need to add type annotations.
src/PyF/Internal/PythonSyntax.hs view
@@ -84,7 +84,7 @@ parsePythonFormatString = parseGenericFormatString ('{', '}')  parseGenericFormatString :: (Char, Char) -> Parser [Item]-parseGenericFormatString delimiters = many (rawString delimiters <|> escapedParenthesis delimiters <|> replacementField delimiters)+parseGenericFormatString delimiters = many ((Raw "\\" <$ "\\\\") <|> (Raw "" <$ "\\\n") <|> rawString delimiters <|> escapedParenthesis delimiters <|> replacementField delimiters) <* eof  rawString :: (Char, Char) -> Parser Item rawString (openingChar,closingChar) = Raw . escapeChars <$> some (noneOf ([openingChar, closingChar]))
test/Spec.hs view
@@ -198,3 +198,19 @@       [myCustomFormatter|2 * pi = @2*pi:.2f!|] `shouldBe` "2 * pi = 6.28"     it "escape chars" $ do        [myCustomFormatter|@@!!@@!!|] `shouldBe` "@!@!"+++  describe "multi line escape" $ do+    it "works" $ do+      [f|\+- a+- b+\+|] `shouldBe` "- a\n- b\n"++    it "escapes" $ do+      [f|\\+- a+- b+\+|] `shouldBe` "\\\n- a\n- b\n"
+ test/SpecFail.hs view
@@ -0,0 +1,177 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE ExtendedDefaultRules #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE DataKinds #-}++import Test.Hspec+import Test.HUnit.Lang++import System.Process (readProcessWithExitCode)+import System.Exit++import qualified Data.Text as Text+ +import System.IO.Temp+import qualified System.IO as IO++import Data.Hashable+import System.FilePath+import Control.Exception+import Control.DeepSeq+import System.Directory++-- * Check compilation with external GHC (this is usefull to test compilation failure)++data CompilationStatus+  = CompileError String -- ^ Fails during compilation (with error)+  | RuntimeError String+  | Ok String+  deriving (Show, Eq)++{- | Compile a formatting string++>>> checkCompile "pi:x"+CompileError "Bla bla bla, Floating cannot be formatted as hexa (`x`)+-}+checkCompile :: String -> IO CompilationStatus+checkCompile s = withSystemTempFile "PyFTest.hs" $ \path fd -> do+  IO.hPutStr fd $ "{-# LANGUAGE QuasiQuotes, ExtendedDefaultRules, TypeApplications #-}\nimport PyF\ntruncate' = truncate @Float @Int\nhello = \"hello\"\nnumber = 3.14 :: Float\nmain :: IO ()\nmain = [f|" ++ s ++ "|]\n"+  IO.hFlush fd++  (ecode, _stdout, stderr) <- readProcessWithExitCode "ghc" [path, "-isrc", "-package-env", "-"] ""+  case ecode of+    ExitFailure _ -> pure (CompileError (sanitize path stderr))+    ExitSuccess -> do+      (ecode', stdout', stderr') <- readProcessWithExitCode (take (length path - 3) path) [] ""++      case ecode' of+        ExitFailure _ -> pure (RuntimeError stderr')+        ExitSuccess -> pure (Ok stdout')++-- sanitize a compilation result by removing variables strings such as+-- temporary files name+sanitize :: FilePath -> String -> String+sanitize path s =+  -- strip the filename+  let+    t = Text.pack s+  in Text.unpack (Text.replace (Text.pack path) (Text.pack "INITIALPATH") t) ++golden :: HasCallStack => String -> String -> IO ()+golden name output = do+  let+    goldenFile = ".golden" </> name </> "golden"+    actualFile = ".golden" </> name </> "actual"++  createDirectoryIfMissing True (".golden" </> name)++  -- It can fail if the golden file does not exists+  goldenContentE :: Either SomeException String <- try $ readFile goldenFile++  let+    -- if no golden file, the golden file is the content+    goldenContent = case goldenContentE of+      Right e -> e+      Left _ -> output++  -- Flush lazy IO+  _ <- evaluate (force goldenContent)++  if output /= goldenContent+    then do+      writeFile actualFile output+      (_, diffOutput, _) <- readProcessWithExitCode "diff" [goldenFile, actualFile] ""++      putStrLn diffOutput++      -- Update golden file+      writeFile goldenFile output++      assertFailure diffOutput+    else do+      writeFile goldenFile output+++-- if the compilation fails, runs a golden test on compilation output+-- else, fails the test+failCompile :: HasCallStack => String -> Spec+failCompile s = do+  before (checkCompile s) $ it (s ++ " " ++ show (hash s)) $ \res -> case res of+   CompileError output -> golden (show $ hash s) output+   _ -> assertFailure (show res)++main :: IO ()+main = hspec spec++spec :: Spec+spec = do+  describe "error reporting" $ do+    describe "string" $ do+      describe "integral / fractional qualifiers" $ do+        failCompile "{hello:f}"+        failCompile "{hello:d}"+        failCompile "{hello:e}"+        failCompile "{hello:b}"+        failCompile "{hello:E}"+        failCompile "{hello:G}"+        failCompile "{hello:g}"+        failCompile "{hello:%}"+        failCompile "{hello:x}"+        failCompile "{hello:X}"+        failCompile "{hello:o}"++      describe "padding center" $ do+        failCompile "{hello:=100s}"+        failCompile "{hello:=100}"++      -- XXX: this are not failing for now, it should be fixed+      xdescribe "grouping" $ do+        failCompile "{hello:_s}"+        failCompile "{hello:,s}"++      describe "sign" $ do+        failCompile "{hello:+s}"+        failCompile "{hello: s}"+        failCompile "{hello:-s}"++    describe "number" $ do+      failCompile "{truncate' number:f}"+      failCompile "{truncate' number:g}"+      failCompile "{truncate' number:G}"+      failCompile "{truncate' number:e}"+      failCompile "{truncate' number:E}"+      failCompile "{truncate' number:%}"+      failCompile "{truncate number:s}"++    describe "number with precision" $ do+      failCompile "{truncate number:.3d}"+      failCompile "{truncate number:.3o}"+      failCompile "{truncate number:.3b}"+      failCompile "{truncate number:.3x}"++    describe "floats" $ do+      failCompile "{number:o}"+      failCompile "{number:b}"+      failCompile "{number:x}"+      failCompile "{number:X}"+      failCompile "{number:d}"+      failCompile "{number:s}"++    -- XXX: this are not failing for now, it should be fixed+    xdescribe "not specified" $ do+      failCompile "{truncate number:.3}"+      failCompile "{hello:#}"++    describe "multiples lines" $ do+      failCompile "hello\n\n\n{pi:l}"++    describe "on haskell expression parsing" $ do+      describe "single line" $ do+        failCompile "{1 + - / lalalal}"+      describe "multiples lines" $ do+        failCompile "hello\n    {\nlet a = 5\n    b = 10\nin 1 + - / lalalal}"++    describe "non-doubled delimiters" $ do+      failCompile "hello } world"+      failCompile "hello { world"