-
Notifications
You must be signed in to change notification settings - Fork 412
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Better error messages #462
Conversation
src/Hakyll/Web/Template/Context.hs
Outdated
@@ -157,7 +160,7 @@ snippetField :: Context String | |||
snippetField = functionField "snippet" f | |||
where | |||
f [contentsPath] _ = loadBody (fromFilePath contentsPath) | |||
f _ i = error $ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose using error
here was not on purpose?
Hm, with every Context field now creating a message when it does not apply that does create an awful lot of debugging messages from the Compiler Alternative. Is |
Force templates to be consumed completely, propagate error appropriately
This is great work, but I'm not sure about the change to
If you know run in verbose mode ( |
Yes, that's why I initially thought to drop the What I also considered is to change the
The last one would be a breaking change, but possibly a good one; if not, we still could log these errors. |
src/Hakyll/Web/Template/Internal.hs
Outdated
handler _ = case mf of | ||
Nothing -> return "" | ||
Just f -> go f | ||
handler Error es = compilerThrow es |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you do not want to make a breaking change here,
to match the old behaviour, this should need to be
handler Error es = do
logger <- compilerLogger <$> compilerAsk
forM_ es $ \e -> compilerUnsafeIO $ Logger.debug logger $
"Hakyll.Web.Template.applyTemplate: Error in 'if': " ++ e
return ""
But I really like distinguishing exceptions (fail
) from plain empty
fields.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've had a quick google search for field
and fail
usage scenarios, and found none that were offending. Or actually, this one.
While working on this, I found another bug: When the body of an |
Please see the update. I've changed |
Btw, when do you plan the next release? I'm close to shipping and need to decide whether to wait for an official version bump or just put my branches in as a beta. |
src/Hakyll/Core/Compiler/Internal.hs
Outdated
case vx `compare` vy of | ||
LT -> compilerError vx . const es | ||
EQ -> compilerError vx . (es++) | ||
GT -> compilerError vy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose that when dropping a list of messagse because there are more important ones one could log them to the debug screen. Not sure whether necessary.
Another question I'd like to get some feedback on is the order/formatting of the error messages. I like to use things like -- helper to amend error messages
withErrorContext :: MonadError [String] m => String -> m a -> m a
withErrorContext msg = flip catchError (throwError . (msg:)) which basically prepends a string to the list of error messages if an error happens. This does give us some kind of call stack. |
Hey, this PR is really starting to look god, I appreciate all the effort! Unfortunately I'm traveling to the states this weekend so I won't be able to look into it in detail until monday. I hope to merge it it then. |
Thanks, I don't want to put any pressure on you :-) |
I would prefer to keep the We can probably do a release later this week. If you're having trouble using custom versions of libraries, you can always consider using stack. I need to use patched libraries all the time at work and I've never had any trouble with it. |
Yeah, I'm right in the process of switching from a plain I'm going to change the |
...makes better error messages :-) Notice the breaking change in 'applyElem', where $if(...)$ conditions in templates now will throw errors if their field 'fail'ed (instead of just being 'empty')!
...when a more important error prevails. Also not throwing from 'if' conditions any more, only logging those errors to the debug screen
* boolFields used outside of 'if'-conditions now get a "stack trace" using a new 'NoField' they don't have to rely on 'error' any more * templates applied to their own file get proper description (did use incompatible paths/identifiers before) * renamed 'compilerFail' to more descriptive name
Hi, have you had a chance to look at this? |
Sorry for the delayed response on my part. I'm still very interested in getting a form of this merged in, but I'm not sure when I'll have time to look at it. :-( It currently seems that we could solve this by putting something like |
OK, I'll try to get this working again after resolving the conflicts, and then see what I can do. |
@@ -140,14 +140,13 @@ | |||
-- | |||
module Hakyll.Web.Template | |||
( Template | |||
, template | |||
, readTemplateElems |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This essentially reverts 526cd35. Instead of exposing those primitives, readTemplate
should have been used.
To benefit from error messages, I used the compileTemplateItem
compiler in Feed.hs now, even though it requires an Item
with a name. Not sure whether that is necessary, or whether we should simply assume that the embedded templates contain no errors.
Btw, could unsafeReadTemplateFile
be deprecated? It seems it was originally created for Feed.hs, and is no longer used anywhere now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, we should be good to rip out unsafeReadTemplateFile
Closes jaspervdj#507 (actually was fixed by 9ec43a6 already, this just adds the test)
OK, I've reviewed this All this hassle arises from the design of
If you want to throw the backcompat concerns over board and release it as Hakyll 5.0, I'm all in :-) My current approach resembles the first idea. Instead of using a separate constructor, I added a level to the For backcompat, even when the first alternative fails with an exception, the second one is still tried. In a proper solution, an exception would not be ignored - at least for the purposes of templates, not sure whether an error-suppressing alternative is useful elsewhere or whether
"at the end" sounds like "globally" to me? And I'm not sure whether this will work, filtering for level/verbosity in the end - we would want to locally decide which messages to keep. The template evaluation forms a tree, and if we just accumulate we would need to somehow represent that structure in the monoid?
Not sure whether I grasp the scope of that change. Why use I will try to remove the |
OK I tried data CompilerResult a
= CompilerDone a CompilerWrite
| CompilerSnapshot Snapshot (Compiler a)
| CompilerRequire (Identifier, Snapshot) (Compiler a)
| CompilerNothing [String]
| CompilerError [String] but it's still really ugly: compilerResult x = Compiler $ \_ -> return x
compilerThrow = compilerResult . CompilerError
compilerNothing = compilerResult . CompilerNothing
compilerHandle :: ([String] -> Compiler a) -> ([String] -> Compiler a) -> Compiler a -> Compiler a
compilerHandle f g (Compiler x) = Compiler $ \r -> do
res <- x r
case res of
CompilerDone res' w -> return (CompilerDone res' w)
CompilerSnapshot s c -> return (CompilerSnapshot s (compilerHandle f g c))
CompilerRequire i c -> return (CompilerRequire i (compilerHandle f g c))
CompilerNothing e -> unCompiler (g e) r
CompilerError e -> unCompiler (f e) r
instance Alternative Compiler where
empty = compilerNothing []
x <|> y = compilerHandle (\rxs ->
compilerHandle (\rys ->
compilerNothing $ rxs ++ rys
) (\eys ->
log rxs >> compilerThrow eys
) y
) (\exs ->
compilerHandle (\rys ->
log rys >> compilerThrow exs
) (\eys ->
compilerNothing $ exs ++ eys
) y
) x
where
log = compilerDebugLog . map
("Hakyll.Core.Compiler.Internal: Alternative fail suppressed: " ++) |
I would also favor the first approach you presented, and the code doesn't look too bad IMO. But I'm a bit unsure about the I also think As far as backwards compatibility goes, I care most about keeping everything working. If users get a slightly less good error message because they are using e.g. |
See jaspervdj#462 (comment) and below for detailed explanation Also abstracted out `testCompilerError` in the test suite, and added a `compilerTry` that is much easier to use (and specifically, to branch on) than `compilerCatch`
I did like the
The idea is that
That name I wasn't happy about either :-) Good suggestion - I had the same idea for renaming the |
lib/Hakyll/Core/Compiler/Internal.hs
Outdated
| NoCompilationResult a | ||
|
||
|
||
-- | Unwrap a `Reason` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess unReason
might have followed the naming conventions, but it seemed kinda unreasonable :-D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😁
lib/Hakyll/Core/Compiler/Internal.hs
Outdated
catchError = (. matchErr) . compilerCatch | ||
where | ||
matchErr f Logger.Error es = f es | ||
matchErr f _ _ = f [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't remember why I did pass no messages to the the handler here - to make it distinguishable?
Now catchError
will also catch NoCompilationResult
s, not just CompilationFailure
s, which seems reasonable - they are errors, at least outside of template applications.
However, c `catchError` throwError
will change the error reason type, which I am not so happy about. Maybe we should export a helper function that allows altering (adding to) the messages without changing the type. Bifunctor
comes to my mind, but our messages are string lists not arbitrary types.
lib/Hakyll/Core/Compiler/Internal.hs
Outdated
(CompilationFailure xs, CompilationFailure ys) -> compilerThrow $ xs ++ ys | ||
(CompilationFailure xs, NoCompilationResult ys) -> debug ys >> compilerThrow xs | ||
(NoCompilationResult xs, CompilationFailure ys) -> debug xs >> compilerThrow ys | ||
(NoCompilationResult xs, NoCompilationResult ys) -> compilerMissing $ xs ++ ys |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could have made Reason
a Semigroup
or Monoid
instance for this to make the code even cleaner, but that doesn't allow to put the discarded messages in the debug
.
lib/Hakyll/Core/Compiler/Internal.hs
Outdated
@@ -18,7 +18,7 @@ module Hakyll.Core.Compiler.Internal | |||
, compilerTell | |||
, compilerAsk | |||
, compilerThrow | |||
, compilerFailMessage | |||
, compilerNoResult |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this one should also be re-exported from Core.Compiler
, as it's useful for people writing custom Context
s with error messages.
The separate datatype makes the code much more obvious, nice job! I think Other than that, I think all the code looks good now! :-) |
No,
Me neither. |
This was merged in #722 |
Closes #461, closes #507, closes #504 (via 9ec43a6)
many
just dropped out in the middle of a template when it encountered invalid syntax)