Closed ak-coram closed 4 years ago
I see this breaks most of the tests, so this may not be a viable approach at all. Could someone give a bit of feedback please?
I was under the impression the new middleware in nREPL was supposed to handle the responses from piggieback automatically. @cichli Can you enlighten us? :-)
A bit of background information:
I started encountering this issue with the figwheel-main repl. I've seen that :nrepl.middleware.print/keys
was not set when using the CLJS repl, even though the nREPL docs state that it should be set by default for the eval and load-file ops. I've added it to piggieback just by following what nrepl.middleware.interruptible-eval
does. Now the print middleware received every result as a string value and "pretty printed" it (effectively printing it twice), so I wrapped the piggieback eval result in clojure.tools.reader.edn/read-string
so that the print middleware can print it correctly. That made all of it work as expected while also using all the other print configuration from CIDER.
I've also seen the comment about :printed-value
being removed from nREPL, so I thought it would be fine to return a form instead of a string (while also removing :printed-value
).
I hope this helps!
I've noticed another issue: user defined tagged literals don't work with this change. Since the mechanism for defining them is different for Clojure and ClojureScript, I imagine that this would only work if both the Clojure and the ClojureScript environments had compatible reader and printer functions defined. I'm not sure how this was handled previously either.
You're right, the edn reader is not appropriate for use in this context. edn is a strict subset of clojure, so can't parse a lot of things that will potentially be in that value (like functions)
I decided to check what clojurescript's browser env returns from evaluations, it looks like it does this: https://github.com/clojure/clojurescript/blob/r1.10.520-1-g230e46ae/src/main/clojure/cljs/repl/browser.clj#L297-L313 note the read-string
call.
The delegating repl env is, well, delegating. So that should be fine!
I had a poke, and found this in repl: https://github.com/clojure/clojurescript/blob/b38ded99dc0967a48824d55ea644bee86b4eae5b/src/main/clojure/cljs/repl.cljc#L500-L502
This string may or may not be readable by the Clojure reader.
So we could attempt a read and then set it to the string otherwise.
@SevereOverfl0w: thank you for your feedback, I've implemented falling back on strings when the reader fails. Sadly this happens whenever there is a value anywhere in the result which the reader can't handle. At least it's much better than not having pretty printing at all or getting errors for unsupported values.
Could we maybe leverage cljs.pprint
and do the pretty printing directly in ClojureScript instead? Or maybe implement a parser and pretty printer which don't rely on actually reading the values? Neither of these seems straightforward.
I don't think you should catch that specific exception, and rethrow the rest. In older versions of clojure, the exception did not look like that. I think it's fine just to fallback whenever there's an exception and never rethrow.
reworking things to have a clojurescript version of the pprint is beyond my knowledge for how the pprint was intended to work.
This is just an idea, but what if we always returned a string from the ClojureScript repl? We would be guaranteed to be able to read it in Clojure and we could return the pretty-printed results through it when pretty printing is enabled. For example:
(defn eval-cljs [repl-env env form opts]
(read-cljs-string
(cljs.repl/evaluate-form repl-env
env
"<cljs repl>"
`(cljs.core/with-out-str
(cljs.pprint/pprint ~form))
((:wrap opts #'cljs.repl/wrap-fn) form)
opts)))
With this my CIDER repl is handling every value I've thrown at it:
c.user> (repeat 10 {:a (fn [])})
({:a #object[Function]}
{:a #object[Function]}
{:a #object[Function]}
{:a #object[Function]}
{:a #object[Function]}
{:a #object[Function]}
{:a #object[Function]}
{:a #object[Function]}
{:a #object[Function]}
{:a #object[Function]})
I'm not sure how much of the nREPL printing configuration cljs.pprint
supports, but the options that are available could be included in the pprint
call as well. As far as I can tell cljs.pprint
is already included via cljs.repl
, so we don't need to worry about loading it either.
One thing to potentially worry about is the wrapping code affecting stack traces. I can already see a lot of tooling-related namespaces there, so I guess it doesn't make it that much worse than it already is.
Also I'm not sure if we need to do
`(let [form# ~form]
(cljs.core/with-out-str (cljs.pprint/pprint form#)))
instead to avoid capturing output while evaluating the form via with-out-str
.
Forgot to mention that the above also breaks repl history (1, e, etc.) since it saves the printed strings instead of the original values. I'm guessing this could be fixed by first allowing the cljs.repl/wrap-fn
functions run before printing.
I suspect a println in the code you send will create broken output
@SevereOverfl0w: yes, that's why I suggested
`(let [form# ~form]
(cljs.core/with-out-str (cljs.pprint/pprint form#)))
This evaluates the form before wrapping the results in with-out-str
making sure that any output is printed. The only problem with this is that if you do printing inside lazy sequences which are only realized by the pprint call, then you still end up with the printed output inside of the result. Guess we could avoid it by not relying on with-out-str
and passing a writer to pprint
:
`(let [sb# (goog.string.StringBuffer.)
sbw# (cljs.core/StringBufferWriter. sb#)
form# ~form]
(cljs.pprint/pprint form# sbw#)
(cljs.core/str sb#))
An example with a lazy seq:
(this is using a writer with pprint)
The history only contains the printed list (and not the outputs from println
):
We're getting closer to a solution. I'll try to find some time for this soon, as I'd really love for us to fix it.
@bbatsov: please let me know if I can help with anything.
Will do. I've been extremely busy the past couple of weeks, but hopefully my schedule will be back to normal after the middle of next week.
Hi, I've made another attempt at fixing this and rebased onto the
current master branch. Compared to the earlier attempts this version
also supports *1
and friends in the repl. One issue I've found is
that the expression false
somehow evaluates to nil
(it looks like
it isn't evaluated at all). I'm not sure how much of an issue this is,
but I assume it's not related to the pretty-printing anyway.
I'll look into why the tests are failing, but could you please take a look at this approach? Thanks!
c.user> (for [x (range 10)]
(do (println "Testing")
(list x x (fn [x] x))))
Testing
Testing
Testing
Testing
Testing
Testing
Testing
Testing
Testing
Testing
((0 0 #object[Function])
(1 1 #object[Function])
(2 2 #object[Function])
(3 3 #object[Function])
(4 4 #object[Function])
(5 5 #object[Function])
(6 6 #object[Function])
(7 7 #object[Function])
(8 8 #object[Function])
(9 9 #object[Function]))
c.user> *1
((0 0 #object[Function])
(1 1 #object[Function])
(2 2 #object[Function])
(3 3 #object[Function])
(4 4 #object[Function])
(5 5 #object[Function])
(6 6 #object[Function])
(7 7 #object[Function])
(8 8 #object[Function])
(9 9 #object[Function]))
c.user> (throw (js/Error. "Test"))
#object[Error Error: Test]
c.user> *e
#object[Error Error: Test]
c.user> nil
nil
c.user> false
nil
I think false->nil is probably indicative of something going on.
Also, why is there special handling of ns, import, etc? Is that because they must be top level forms?
@SevereOverfl0w: thank you for your feedback.
I think false->nil is probably indicative of something going on.
I've tested it with the upstream version of piggieback (0.4.1) and it's also the case there:
c.user> false
nil
Also, why is there special handling of ns, import, etc? Is that because they must be top level forms?
I'm not sure myself: I've based this directly on cljs.repl/wrap-fn
(the default wrapper which is currently used) and wanted it to be as
similar as possible (but pretty printing return values).
The last force-push is for rebasing on top of the latest master version.
Hey there, @ak-coram! Sorry for the radio silence - I was super busy most of September and I finally got to looking into this. In general any solution that we adopt should respect how nREPL when it comes to pretty printing, otherwise that'd be super weird.
It seems to me that something's wrong with the middleware ordering for piggieback if the print-keys don't get properly set for the evaluations performed by it.
I'm also wondering if piggieback shouldn't leverage the print middleware infrastructure directly the same way interruptible-eval
does (although obviously here we'd printing something after it's already evaluated in the ClojureScript REPL and we have the string representation of the result).
Unfortunately I'm not quite sure how @cichli planned for this to work with ClojureScript and he hasn't been around lately so we can't really ask him.
Hi @bbatsov! Thank you for the feedback.
I agree that pretty-printing should be configurable, but I also think
it should be done in the CLJS environment in order to be able to print
simple expressions such as (fn [x] x)
or user-defined tagged
literals (which understandably may be only defined for ClojureScript
in a ClojureScript-only project). I've tried integrating with nREPL
pretty-printing initially to fix this issue too, but discovered that
reading values in Clojure which are printed by cljs.core/pr-str
in
ClojureScript doesn't even cover basic common use-cases (it basically
only works on built-in data structures and primitive values).
As far as I can tell the current nREPL pretty-printing infrastructure completely runs on the CLJ-side, even if the given printer (such as fipp) supports CLJS. Other printers do not support CLJS at all (such as puget), but as I've mentioned I see more of a problem with reading values in CLJ than with printing them.
There's already some control you have over the current version,
because you can provide your own wrapping function via
*cljs-repl-options*
(the one I added is just replacing the default
one, which was already printing expressions via cljs.core/pr-str
). I
think we could support setting your own printing fn too (akin to
cider-pprint-fn
), but it would have to support a different set of
values for CLJS compared to CLJ.
EDIT: I just realized that reading built-in data structures doesn't
work in all cases either (#queue []
for example)
reading values in Clojure which are printed by cljs.core/pr-str in ClojureScript doesn't even cover basic common use-cases (it basically only works on built-in data structures and primitive values).
Well, that certainly comes as a surprise to me, but then again - I don't do much about ClojureScript. I always assumed that reading values was something cross-platform, but I guess it's not. Can you give me some concrete examples so I have a better idea about the nature of the problem?
As far as I can tell the current nREPL pretty-printing infrastructure completely runs on the CLJ-side, even if the given printer (such as fipp) supports CLJS. Other printers do not support CLJS at all (such as puget), but as I've mentioned I see more of a problem with reading values in CLJ than with printing them.
Yeah, the the printing logic is running completely on the Clojure side, which seemed reasonable given the expectation for compatibility that I stated earlier.
There's already some control you have over the current version, because you can provide your own wrapping function via cljs-repl-options (the one I added is just replacing the default one, which was already printing expressions via cljs.core/pr-str). I think we could support setting your own printing fn too (akin to cider-pprint-fn), but it would have to support a different set of values for CLJS compared to CLJ.
Yeah, I get this, but I really hate having to provide different interfaces for Clojure and ClojureScript everywhere. You've got no idea how much I hate ClojureScript already because almost everything is slightly different there and half the code has to be duplicated because of this. I assume there's not chance that the ClojureScript REPL would accept a printing function the way the Clojure REPL does, right? This might have been a reasonable solution.
It's not just ClojureScript, some Clojure values can be pretty-printed just fine, but can understandably not be read back:
Functions
user> (fn [x] x)
#function[user/eval23287/fn--23288]
user> (read-string "#function[user/eval23287/fn--23288]")
Execution error at user/eval23291 (REPL:27).
No reader function for tag function
Objects
user> (java.time.LocalDate/now)
#object[java.time.LocalDate 0x2647cbb8 "2019-10-05"]
user> (read-string "#object[java.time.LocalDate 0x2647cbb8 \"2019-10-05\"]")
Execution error at user/eval23295 (REPL:35).
No reader function for tag object
user> (deftype MyType []) user.MyType user> (->MyType)
user> (read-string "#object[user.MyType 0x3e15eb62 \"user.MyType@3e15eb62\"]") Execution error at user/eval23324 (REPL:48). No reader function for tag object
For ClojureScript there's also the aforementioned `#queue` literal:
https://cljs.github.io/api/syntax/queue-literal
* CLJS Objects
```clojure
c.user> (deftype Cheese [])
cljs.user/Cheese
c.user> (Cheese.)
#object[cljs.user.Cheese]
user> (read-string "#object[cljs.user.Cheese]")
Execution error at user/eval23285 (REPL:22).
No reader function for tag object
CLJS Functions
c.user> (fn [x] x)
#object[Function]
user> (read-string "#object[Function]")
Execution error at user/eval23346 (REPL:62).
No reader function for tag object
And of course it's up to the users to define any number of custom tagged literals, that's why EDN (extensible data notation) is extensible. In a project only using ClojureScript they wouldn't bother defining the same literals for Clojure, which would cause reading them to fail too.
I assume there's not chance that the ClojureScript REPL would accept a printing function the way the Clojure REPL does, right? This might have been a reasonable solution.
I don't think so: the interface looks different from the Clojure REPL, but I'm not familiar with the details either. Even if it does, it probably has to run in JavaScript.
Essentially this is similar to what my workaround in this PR does: it
wraps the evaluated CLJS form with the pretty-printing code. I don't
think there's much we can do here except introduce an option so the
users can override the var used for printing in CLJS (with
#'cljs.pprint/pprint
being the default).
Or writing a pretty-printer which works on printed CLJS values without reading them, but that would defeat the point of being able to use a custom printer anyway :)
We also can't be sure that the types implementing IPrintWithWriter
can be read in Clojure.
I understand your frustration with having to support CLJS in this way, guess one day we could have an independent nREPL implementation in it and not "piggieback" on the current nREPL. I'm guessing that would alleviate at least some of these issues.
I understand your frustration with having to support CLJS in this way, guess one day we could have an independent nREPL implementation in it and not "piggieback" on the current nREPL. I'm guessing that would alleviate at least some of these issues.
That's the dream. :-) Unfortunately so far no one has been willing to invest time in doing it.
Btw, why are you concerned reading back the printed values?
For ClojureScript there's also the aforementioned #queue literal:
I guess for it and other common cases we can just provide some data readers with cider-nrepl
(for instance).
That's the dream. :-) Unfortunately so far no one has been willing to invest time in doing it.
Sounds like a fun project, but also like a lot of work :)
Btw, why are you concerned reading back the printed values?
I personally like to use a few of my own data readers for my projects,
so it would be a shame if using them would break pretty-printing
(especially since this seemed to work fine in the past). I also
defined IPrintWithWriter
for several stateful types for which it
makes no practical sense to define readers (but it's still useful to
readably print them).
I'm thinking that maybe we can try to read and if it succeeds we can
run everything through the nREPL printing middleware just as with
Clojure forms. In case it fails it would nice to have a better
fallback than cljs.core/pr-str
though. Since the output of
cljs.pprint/pprint
should be just as readable to the reader as
cljs.core/pr-str
, why not use it as the default instead? This is
also something decided internally in piggieback, so Cider and nREPL
wouldn't need to be adjusted in any way.
I think this matters a lot to new users, because I doubt they're
interested in customizing pretty-printing, but very quickly notice
when something is not pretty-printed. We could also detect if
cider-print-fn
is set to pr
(or nil
), disable pretty-printing
and use cljs.core/pr-str
instead.
I guess for it and other common cases we can just provide some data readers with cider-nrepl (for instance).
I thought about this as well, but since users can define their own
printers and readers this doesn't seem like a clean solution. Forcing
them to define readers for both Clojure and ClojureScript also seems
hard to justify without explaining the internals of the current
tooling. I've also noticed that most of the current alternatives for
pretty-printing don't work with custom writers (print-method
,
print-dup
) anyway.
Of course there's also the overhead of "printing, reading and then printing again" to consider. I think the greatest advantage of the current printer alternatives is speed, which might be completely negated by doing it twice.
Any thoughts on this?
I guess for it and other common cases we can just provide some data readers with cider-nrepl (for instance).
Those using a namespace-qualified var for cider-print-fn
would also
need to handle the dummy types (for example for #js
) we pass to them
for these values.
I'll need some time to process all of this, but in the mean time I'll ping @thheller, as I'm curious to hear his take on the issue. Lately we've been discussing improving/replacing piggieback and perhaps he has some thoughts/ideas in the area of pretty-printing.
I'll need some time to process all of this, but in the mean time I'll ping @thheller, as I'm curious to hear his take on the issue. Lately we've been discussing improving/replacing piggieback and perhaps he has some thoughts/ideas in the area of pretty-printing.
@bbatsov: sounds great, let me know if I can help in any way!
I don't have anything useful to contribute really.
Printing (pretty or not) can always produce strings that cannot be read back properly. Configuring printing is hard since it happens in another runtime and you can't just pass a function or modify a binding to control it.
:wrap-fn
is sort of your only way to customize printing but in practice the results should be pretty much the same as pr-str
and trying to read/pprint the resulting string value in CLJ. There will just be cases that you can't read but adding a default reader fixes most of those.
;; this fails
(clojure.edn/read-string "#object[cljs.user.Cheese]")
;; this is fine
(clojure.edn/read-string
{:default (fn [tag data] [::lit tag data])}
"#object[cljs.user.Cheese]")
;; [:user/lit object [cljs.user.Cheese]]
If you supply a :default
function that returns some TaggedLiteral wrapper with actual print-method/pprint support you can probably get something that is better than what cljs.pprint
would support without having to configure anything in the JS runtime.
Hi @thheller,
thanks, I didn't know about :default
. If I understand correctly
you're proposing something like this:
user> (deftype UnknownTaggedLiteral [tag data])
user.UnknownTaggedLiteral
user> (defmethod print-method UnknownTaggedLiteral
[^UnknownTaggedLiteral this ^java.io.Writer w]
(.write w (str "#" (.tag this) (.data this))))
#multifn[print-method 0x22c93dd0]
user> (clojure.pprint/pprint
(clojure.edn/read-string
{:default ->UnknownTaggedLiteral}
"#object[cljs.user.Cheese]"))
#object[cljs.user.Cheese]
nil
I think this might work as a reasonable default, I don't think it
works with any printers other than pr
and pprint
though (at least
without further configuration). Also those using their own function
for cider-print-fn
would need to handle this type (unless they're
relying on print-method
themselves).
I know this is a bit pedantic, but as you mentioned technically nobody
is forced to emit readable outputs via IPrintWithWriter
, so we might
have a reader failure even with :default
:
user> (deftype Point [x y])
user.Point
user> (defmethod print-method Point
[^Point this ^java.io.Writer w]
(.write w (str "#<Point[" (.x this) "," (.y this) "]>")))
#multifn[print-method 0x22c93dd0]
user> (->Point 33 44)
#<Point[33,44]>
user> (clojure.pprint/pprint
(clojure.edn/read-string
{:default ->UnknownTaggedLiteral}
"#<Point[33,44]>"))
Execution error at user/eval23243 (REPL:95).
Unreadable form
Therefore I would still suggest to default to cljs.pprint/pprint
instead of cljs.core/pr-str
when we can't read the results so we
have a reasonable fallback.
I think we should also consider the performance implications, since this relies on "printing, reading and printing again" instead of just printing once.
Yes, it is easy to break the default prn
but for the most part people don't. Or this might at least give them a reason not to break stuff.
I'm not a big fan of print,read,print either but performance wise it shouldn't be an issue. A human can't read the output of 1000s of evals per second anyways. If you use a faster pprint like fipp
it may actually be faster to print,read,fipp than cljs.pprint alone but for structures where this becomes an issue pprint itself probably isn't usable anyways. Large values are problematic in their own right regardless of pretty printing or not.
The benefit of doing more in CLJ is that you have more control over the printing than you do if printing is all done in CLJS.
I'm not a big fan of print,read,print either but performance wise it shouldn't be an issue. A human can't read the output of 1000s of evals per second anyways. If you use a faster pprint like fipp it may actually be faster to print,read,fipp than cljs.pprint alone
To my knowledge fipp also works in CLJS, it would be nice to be able to leverage that in the future. I have no idea how the implementation compares to the one for CLJ, but I would be surprised if cljs-fipp is not faster than "cljs-print,clj-read,clj-fipp".
A human can't read the output of 1000s of evals per second anyways.
I agree, but I think it's more of an issue that you have to wait for the first print and read steps to fully complete on a large value (single eval) before you even get to see anything (even when the end result is then truncated to a dozen lines by the second print).
The benefit of doing more in CLJ is that you have more control over the printing than you do if printing is all done in CLJS.
I'm not sure what you mean, I imagine that at least from the perspective of fipp we should have a similar level of configurability for both CLJ and CLJS.
Even cljs.pprint
seems to support a very similar configuration to
clojure.pprint
(such as length
, level
, right-margin
, etc.):
https://cljs.github.io/api/cljs.pprint/
My point is, that out of the 5 supported (built-in) options in Cider (pr, pprint, fipp, puget, zprint) we have 3 (pr, pprint, fipp) which also have implementations in CLJS with nearly identical APIs. For at least these three we could pass the same configuration to the CLJS print call in piggieback to avoid the read and second print steps in CLJ.
For nil
(nrepl.middleware.print/*print-fn*
) and the other two
(puget, zprint) we could still implement what you've suggested. For
those who use their own qualified var in cider-print-fn
, we can
either do the same or we can expect them to also provide an
implementation in CLJS (if they want it to work in the CLJS repl).
Not to mention that fipp and puget don't seem to rely on
print-method
, so we'd have to figure out how to configure them to
emit our UnknownTaggedLiteral
wrapper correctly (same goes for the
custom var users):
user> (deftype UnknownTaggedLiteral [tag data])
user.UnknownTaggedLiteral
user> (defmethod print-method UnknownTaggedLiteral
[^UnknownTaggedLiteral this ^java.io.Writer w]
(.write w (str "#" (.tag this) (.data this))))
#multifn[print-method 0x4a6c1f6b]
user> (def x (clojure.edn/read-string
{:default ->UnknownTaggedLiteral}
"#object[cljs.user.Cheese]"))
#'user/x
user> (clojure.pprint/pprint x)
#object[cljs.user.Cheese]
nil
user> (require 'fipp.clojure)
nil
user> (fipp.clojure/pprint x)
#object[user.UnknownTaggedLiteral
"0x1726b743"
"user.UnknownTaggedLiteral@1726b743"]
nil
user> (require 'puget.printer)
nil
user> (puget.printer/pprint x)
#<user.UnknownTaggedLiteral@6556d7db>
nil
user> (require 'zprint.core)
nil
user> (zprint.core/zprint x)
#object[cljs.user.Cheese]
nil
Is there anything that I'm missing here?
Hi, I'd just like to add that I think we've mostly covered what is possible to do here right now and I would be keen to tackle either of the alternatives (read back everything in CLJ / try to offload to CLJS whenever possible) once a decision has been made on what approach to adopt. If anyone has any different suggestions I'd love if you'd chip in.
Anyway, I'd like to thank everyone for even reading this far into this thread :)
OK, so I thought about this a bit more and I think we should aim to do as much in Clojure and just ignore the cases where something can't be read back (potentially falling back to some cljs printing in those situations and shipping a few "essential" extra data readers with piggieback itself). My main concern about offloading the printing to piggieback is that it seems to me this will breaking the streaming of values to clients, as at least the current implementation just does the pretty-printing in a single step fully realizing the result in the process.
Thank you for you response.
My main concern about offloading the printing to piggieback is that it seems to me this will breaking the streaming of values to clients, as at least the current implementation just does the pretty-printing in a single step fully realizing the result in the process.
As far as I understand the status quo is the following: CLJS results
are first fully realized by cljs.core/pr-str
(used in the default
CLJS wrapper function) and then passed back to CLJ in a repl-specific
manner, but always ending up as single string (see
cljs.repl/evaluate-form
). This string is then sent as is to the
client via nREPL.
Now I've looked at some of the implementations of IJavaScriptEnv
and
they mostly seem to be working directly with strings (and not streams
or something alike). Sidenote: cljs.repl.browser
is actually using
clojure.core/read-string
to parse (a wrapped) evaluation result, so
I guess you could even execute your own code on the JVM from your
connected browser. That's a feature I was unaware of... :)
So before we even start doing anything with the evaluation result in piggieback, it's neatly wrapped up into a (single-line) string. This would mean that we have no way of sending parts of the response to the client before evaluation (and printing) fully completes (however or wherever we do the pretty-printing). At least this seems to the case with the existing CLJS repl environments.
Of course it would be much nicer if the CLJS repl implementations didn't rely on passing strings, but I think as long as they do we don't have an effective way of streaming the results while the evaluation is going on (it definitely doesn't work now: no matter how big the result, I always get the results in a single message).
Even if all we have is a large string, maybe it would make sense to
deliver it through several nREPL responses (so that individual
responses are kept relatively small). In this case it could be even an
advantage if the result is already pretty-printed: for example for a
pretty-printed result (instead of a single-line one) we could send N
lines at a time to the client (by wrapping the string in a
java.io.BufferedReader
or something similar).
I think at least the browser repl is using sockets internally, so maybe one day streaming the results will be possible without keeping everything in memory on the JVM. As for streaming from the nREPL server to the nREPL client, I suspect we can do it either way: whether we do the pretty-printing in Clojure or ClojureScript should be of no consequence.
What do you think?
Hi, I've pushed a bit of further work on this issue. Any feedback and testing would be appreciated.
The tests still fail, but I'm afraid I don't know how to handle the older nREPL versions (which seem to make them fail). Could someone give me a few pointers? Thanks!
The printing was changed once in 0.5 and then in 0.6, so supporting both would be pretty hard. Generally these days almost everyone is using nREPL 0.6, so I guess we can just drop support for 0.4 and 0.5 and simplify our lives.
Ok, that's fine with me. Should I go ahead and update project.clj
accordingly or do you want to handle the deprecation in a separate PR?
Guess the CI config might need to be updated as well.
I'm still not sure why the Java 8 / Clojure 1.10 tests are already succeeding.
I can't reproduce the remaining test failures on my own machine, but
on the surface it looks like the result is sometimes not printed by
the nREPL middleware (proper-ns-tracking
). This doesn't seem to
depend on only the Java version (we have failures and successes for
both Java 8 and Java 11) or only the Clojure version (failures and
successes for both 1.9 and 1.10). Any ideas? Thanks!
Damn! I totally dropped the ball on you! Sorry about that!
The failures seem kind of random to me, as I can't imagine they can really be Clojure/JDK version specific. Probably the real problem is that some operations are not happening in a reliable order or something along those lines. Generally @shen-tian is our resident expert when it comes to debugging hard to reproduce issues. Perhaps he can help with some insight?
@ak-coram One more thing - it'd be nice to document the pretty-printing and its limitations in the README. Our conversation here is pure gold and we should preserve its essence in the README. :-)
Oh dear. Our favourite java.lang.ClassCastException: clojure.lang.PersistentArrayMap cannot be cast to [B
This exception was popping up sporadically in the nREPL test suite for a while. That turned out to be related to having a eval that printed a long message interrupted. The bencode transport is quite sensitive to that, so it would choke and throw that message.
There was some discussion here https://github.com/nrepl/nrepl/issues/132 but think the fix was around how the interrupt worked.
I'm not familiar with the tests in piggieback, so not sure what could be causing that. But does that ring a bell?
Actually, that failure mode is just on Clojure 1.11/master. We have a different (and intermittent) failure mode on the other tests.
Given we are also not running tests on nREPL 0.7/Java 13 etc. either, it's probably worth updating the test suite first, and see what shape that's in first. For all we know, Clojure master actually broke the tests...
I can do that in the next few days. That should give us a bit more information to work with
I'm not familiar with the tests in piggieback, so not sure what could be causing that. But does that ring a bell?
I don't recall such failures in the past. @bhauman and @cichli did most of the piggieback work and they are way more familiar with the test suite, but unfortunately they've not been around recently.
Given we are also not running tests on nREPL 0.7/Java 13 etc. either, it's probably worth updating the test suite first, and see what shape that's in first. For all we know, Clojure master actually broke the tests...
Good point!
For all we know, Clojure master actually broke the tests...
Well, that'd be a relief. :-) Thanks for looking into this so quickly!
Just create a PR for adding nREPL 0.7 and Java 13, and the tests look healthy, so it's some changes from the PR that's causing the issues. I'll have a poke later to see if I can spot anything.
A good theory of what's broken:
wrap-print
in the stack, but we didn't declare that dependency.https://github.com/shen-tian/piggieback/commit/3750bf4828ba1e08f8892dff36cd65a0860973bc <- this commit made all the tests go green in my fork.
Hi,
I'm trying to fix CIDER issue #2667 and believe it could be resolved by also relying on the pretty-printing support of the newer nREPL versions in piggieback. These changes work for me, so please let me know if there are issues with this approach. Thanks!