-
Notifications
You must be signed in to change notification settings - Fork 177
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
Abstract away op error handling #327
Abstract away op error handling #327
Conversation
This lets sync ops use the stacktrace viewer, just like how async ops currently do. Requires some changes to the CIDER-nREPL code as well which were submitted as CIDER-nREPL PR 327 clojure-emacs/cider-nrepl#327.
each type is discussed above in the `selector` method." | ||
[pass-through & pairings] | ||
`(fn [{op# :op transport# :transport :as msg#}] | ||
(try |
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.
While I understand the intent behind error-handling the error-handler itself, this will cause a stack of dozens of nested try
blocks. Maybe that's ok. But then, maybe we don't need this outer try
block either.
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.
Oh wow I didn't even think about how ugly this would look once macroexpanded. The outer try
is needed so you can do the inline responses safely (say for something that has a constant reply), but we'll probably never use this and could remove it entirely.
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, let's do this.
Thanks, I like this implementation. The
The only thing we lose from this implementation is the ability to specify a different error status (currently I see |
I agree with @Malabarba about the middleware for pretty much the same reasons he outlined. This would also protect us from internal errors in nREPL itself.
What exactly is different there? The code looks exactly the same as in other middleware to me. |
|
||
(defn pop-reply [msg] | ||
(try (success msg (swap-inspector! msg inspect/up)) | ||
(catch Exception e (failure msg e :inspect-pop-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 wonder if the Elisp code cares about those different error codes. Seems to me there's still room to cleanup/simplify all those ops.
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.
You're right, the Elisp code has absolutely no idea about these error codes. They could be removed entirely, but I think they're good to have so that we can "tune" the error reporting UI based on the type of error returned (which is something I've been experimenting with). Eval/big errors should certainly get the stacktrace viewer, some other types should just get a minibuffer message, and other types of errors might just get swallowed. The user would also be able to customize this by fooling around with some variables.
Thanks Artur...yeah, I might have taken some liberties with mission creep as I went through the code base. Probably time to figure out how to selectively commit hunks in magit 😆.
These are really good points. A couple questions regarding how to implement this: Is there a way to ensure that the error-middleware is the outermost middleware, or will it have to be part of convention that the error-middleware gets preferential ordering in the stack? What about when we're dynamically injecting middleware (such as when refactor-nrepl gets injected on jack-in), can we guarantee that will be injected within the error-middleware?
RIght now it looks the same because virtually none of the middleware is doing anything unique with regards to error handling, but having the error handlers defined at the level of the "op -> action" mapping affords a simple mechanism for op-specific custom error handling. This would still be possible but would be a bit more cumbersome if all exception handling was done within a catch-all middleware (ie, some lookup table that the error-middleware could use to resolve a custom error handler given the information in the In my mind, there should be a 3 tiered system:
Thoughts on this layered approach? Certainly would be simpler to only use a middleware, but I don't have a great feeling about dealing with ops and their errors in 2 very different places. |
That's a good point as well. There's something else to consider, though - you can have several different error codes per one op if several different things can go wrong. In such cases (not sure if we have them in the current codebase or not) you'd still have to roll out some custom error handling code. Anyways, I guess I'd be fine with both approaches - it's obviously that they are both superior to what we're doing right now... |
@@ -56,7 +56,8 @@ | |||
:dependencies [[org.clojure/clojure "1.8.0-master-SNAPSHOT"]]} | |||
|
|||
:test-clj {:test-paths ["test/clj"] | |||
:java-source-paths ["test/java"]} | |||
:java-source-paths ["test/java"] | |||
:resource-paths ["test/resources"]} |
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.
Why is this needed? I don't see anything related in the PR (guess I might be missing something).
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.
Sorry, not related to the PR. When I was going through the code base, I saw that resouce-test
wasn't actually testing anything -- it was in effect doing something like asserting (= nil nil)
-- because the test.txt
resource didn't have its enclosing directory added to the resource path.
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 see. Guess this should be fixed in a separate PR.
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.
OK, just submitted a PR for this, will rebase this branch off master if it gets merged in
Hmmm, interesting. Do you mean that a single op would travel down the middleware stack and throw errors in multiple middlewares? |
I meant one op could potentially throw different errors in different situations. Right now we have just one error per op, but that mostly because we don't really process the errors on the client-side. E.g. there can be 5 different macroexpansion errors. Not a big deal right, just some food for thought. |
Guess you'll have to do that rebase. :-) |
A new util ns in `cider.nrepl.middleware.util.error-handling` which provides the `with-safe-transport` macro, abstracting away transport, error handling, and stacktrace analysis. Most middlewares were updated to use this system, and the tests were updated to reflect these changes. In order to utilize the nicely formatted stacktraces stuffed into these responses, concurrent changes have been made to CIDER and will be submitted as a PR as well.
40d9aa5
to
9a81e27
Compare
Done! Took out the "outer try" from the macro and rebased after the |
I don't know. I know that we could add
I think Cider's dynamic injection should not interfere with this in any way. |
Oops, long night. I meant to ping @cemerick |
I've decided to merge the PR in its current form. We can always do the middleware change later if we decide that'd be best. Thanks for the huge amount of your you did here @sanjayl! It's much appreciated! |
No problem! I'll be taking a stab at #313, the first part ("mute all middleware errors") definitely sounds straightforward. The "mute this specific middleware error" button sounds like a very interesting UI idea and I'll let you know if it's doable. |
This lets sync ops use the stacktrace viewer, just like how async ops currently do. Requires some changes to the CIDER-nREPL code as well which were submitted as CIDER-nREPL PR 327 clojure-emacs/cider-nrepl#327.
This lets sync ops use the stacktrace viewer, just like how async ops currently do. Requires some changes to the CIDER-nREPL code as well which were submitted as CIDER-nREPL PR 327 clojure-emacs/cider-nrepl#327.
Before submitting a PR make sure the following things have been done:
Thanks!
A new util ns in
cider.nrepl.middleware.util.error-handling
whichprovides the
with-safe-transport
macro, abstracting away transport,error handling, and stacktrace analysis. Most middlewares were updated
to use this system, and the tests were updated to reflect these changes.
In order to utilize the nicely formatted stacktraces stuffed into these
responses, concurrent changes have been made to CIDER and will be
submitted as a PR as well.