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 +8/−1
- PyF.cabal +9/−9
- Readme.md +13/−0
- src/PyF/Internal/PythonSyntax.hs +1/−1
- test/Spec.hs +16/−0
- test/SpecFail.hs +177/−0
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"