This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-04-24
Channels
- # announcements (3)
- # babashka (23)
- # beginners (35)
- # cider (3)
- # clara (3)
- # clj-kondo (14)
- # cljdoc (1)
- # cljs-dev (1)
- # clojure (82)
- # clojure-austin (9)
- # clojure-europe (5)
- # clojurescript (23)
- # conjure (62)
- # cursive (73)
- # defnpodcast (1)
- # emacs (3)
- # ethereum (1)
- # gratitude (1)
- # hyperfiddle (12)
- # introduce-yourself (1)
- # leiningen (2)
- # lsp (44)
- # malli (7)
- # polylith (2)
- # portal (17)
- # re-frame (5)
- # reitit (3)
- # sci (8)
- # shadow-cljs (5)
- # tools-build (11)
regarding the io-prepl
... does anyone have a way to make sure that it emits :err
tags (see https://clojure.atlassian.net/browse/CLJ-2645 for prepl
which shows that flush
is needed on *err*
)
I mean you could do something awful like this after connecting:
(defn ^java.io.PrintWriter ^:private auto-flushing-print-writer
"Like clojure.core.PrintWriter-on, but with auto-flushing enabled."
[flush-fn close-fn]
(let [sb (StringBuilder.)]
(-> (proxy [java.io.Writer] []
(flush []
(when (pos? (.length sb))
(flush-fn (.toString sb)))
(.setLength sb 0))
(close []
(.flush ^java.io.Writer this)
(when close-fn (close-fn))
nil)
(write [str-cbuf off len]
(when (pos? len)
(if (instance? String str-cbuf)
(.append sb ^String str-cbuf ^int off ^int len)
(.append sb ^chars str-cbuf ^int off ^int len)))))
java.io.BufferedWriter.
(java.io.PrintWriter. true))))
(def original-err *err*)
(def my-err (auto-flushing-print-writer #(doto original-err (.write ^java.io.Writer %) .flush) nil))
(set! *err* my-err)
(set! *warn-on-reflection* true)
(.toString (identity "foo"))
looking at io-prepl
I'm struggling to see how to affect the *err*
flush operations in the prepl
. What am I missing?
and I have tried setting *err*
after starting the socket server as you suggest and I'm still not getting back any :err
tags
Sorry, yeah, you'd actually need your own version of clojure.core.server/prepl
, not io-prepl
.
I didn't clean that up at all, but given that and then clojure -X clojure.core.server/start-server :name prepl :port 5556 :accept foo.core/io-prepl :server-daemon false
, then nc localhost 5556
, I can get the reflection warning to show up.
Sure thing, not sure how useful any of it was, though. π I just happened to have wrestled with the same thing (flushing err to make reflection warnings show up) in my own work recently.
It's useful: we arrived at the agreement that it's a simple correction but it needs to be fixed in core or we have to fork / patch it which is π’
Given that the fix is a one liner, is it even worth making a patch? @U064X3EF3??
Can you put all this on the ask Clojure question for it? I get mentioned on dozens of threads a day, in multiple slacks. Ask Clojure is the place to create public durable conversations about requests for Clojure in a votable, trackable form
Of particular interest are impact and need to fork/copy to work around
I don't understand the question you asked of me, are you asking if it's worth fixing or worth doing the work of making the patch? (In both cases, yes)
but I'd want to make sure we have isolated what the problem is here. is it that the error stream is flushed on delay? or that the delay is too long? or that warnings should flush? or something else.
ok thanks - I'll add some comments to the Ask Clojure posting and maybe @U4ZDX466T can weigh in too
I have posted a summary on https://ask.clojure.org/index.php/10735/prepl-warnings-remain-buffered-until-explicit-flushing-client?show=10735#q10735
do you want to add them to the jira?
invite sent
Thanks, not going to look at it imminently, but I've added it to our 1.12 consideration list.
Is there a way to invoke a java method without going through Method. invoke()? What's the overhead, besides allocating the array and paying the price of invokevirtual?
I think I'm doing something wrong. If I want to invoke a method I should lookup in an object with a method type which includes only the arguments?
I actually don't need signature polymorphism for my specific use case, just trying to pull teeth working with gRPC
Okay, I managed something like:
(let [lu (.in (MethodHandles/lookup) String)] (.invokeWithArguments (.findVirtual lu String "toUpperCase" (MethodType/methoType String)) ["abc"]))
Now the challenge is doing it without allocating the j.u.List (or array)i'm not super familiar with java, but i'm trying to figure out how to convert a async/chan
to a InputStreamReader.
. The reason is that the cheshire/core
library I'm using to parse json takes an InputStreamReader. Any ideas on how to do this? The only way I've come up with is:
(->> body-chan
chan-to-lazy-seq!!
(map (fn [chunk] (-> chunk .getBytes ByteArrayInputStream.)))
Collections/enumeration
SequenceInputStream.
InputStreamReader.
cheshire/parsed-seq)
this feels wrong though as i'm converting a async/chan to a lazy sequence. Ideally i want to park on the chan and write to the input stream reader some howif there a clean way to parse json from a channel that would be amazing. The channel is made up of many raw strings that may be made up of json fragments (e.g. messages 1-3 can make up 1 json blob)
so channel messages can look like:
temp (async/chan 10)
(async/>! temp "{ \"a\":")
(async/>! temp "5 }")
and the resulting chan should provide the json blobs
result-ch (async/chan 10)
(println (async/<! result-ch))
; prints {:a 5}
I have a utility that does this that I can scrounge up @U01BK5E5ESY . Can I ask what the application is?
@U050ECB92 that would be super useful
the specific application is for reading streamed responses from an api thats sending json events
@U01BK5E5ESY this differs from what you want in one way: it is adapting a channel of byte[]s to an InputStream, whereas you are looking for a channel of Strings or char[] adapted into a Reader
interesting:
{:inputstream (proxy [InputStream] []
(close [] (error! nil))
(read
([] (read1))
([b] (readN b 0 (count b)))
([b off len] (readN b off len))))
so you are overwriting the methods here for the InputStreamWhat is the downside of this approach here:
body-chan
chan-to-lazy-seq!!
(map (fn [chunk] (-> chunk .getBytes ByteArrayInputStream.)))
Collections/enumeration
SequenceInputStream.
InputStreamReader.
(defn chan-to-seq!!
"Takes a channel and returns a lazy sequence of channel messages"
[c]
(lazy-seq
(when-some [v (async/<!! c)]
(cons v (chan-to-seq!! c)))))
Can't you extend https://docs.oracle.com/javase/8/docs/api/java/io/Reader.html instead?
(proxy [java.io.Reader] []
(read [chars offset length] ...) ;; Here you take from channel and put into chars
(close [] ...) ;; Here you close the channel
chars is an array, offset is the array index you need to start at for inserting the chars into, and length is the max number of chars you can insert for a call to read
So basically, take length number of char from the channel and aset them into the chars array starting at index offset
You can now use this proxy with parse-seq on cheschire I believe. Or if not, wrap it in a BufferedReader and that should work I'm pretty sure.
> chars is an array, offset is the array index you need to start at for inserting the chars into, and length is the max number of chars you can insert for a call to read does that mean i need to store the entire total string in memory as I pull from the chan (async/<! ch) (i assume so because we need to support the various offset values)?
No I don't believe so.
I think what happens is:
The reader provides an array, and ask for say 10 char to be read at offset 0, so it calls read with that.
The reader then read the array from 0 to 10, if the reader feels they don't have enough... say the JSON is not complete like you only got: {"hey" : "
and this can't be parsed yet. So the reader will call read again, with the same array, but will ask for another 10 char to be read from offset 10. Now maybe the reader gets: {"hey" : "bob"}
and this is enough to parse, so the reader can parse that and be done, now it will call close.
But, the reader could also choose to continue reading... and the next time it calls read, it can choose to pass a new char array and set the offset back to 0. Allowing the prior char array to be garbage collected.
I think this is what say a LineReader will do. The LineReader will read from another Reader say 10 char at a time into a first array for the first line. When it sees a line ending, it will call read with a new array for the second line, Or it can even choose the same array but overwrite it, by just having the offset be 0 again.
So its up to the user of the Reader to decide like if it cares to remember everything read till now or not, by choosing how to call read
If read is called and the channel closes in the read, you can return -1 to indicate to the reader that the stream ended.
I couldn't resist:
(defn chan->reader
[c]
(proxy [java.io.Reader] []
(read [chars offset length]
(let [first-char (async/<!! c)]
(if (nil? first-char)
-1
(do
(aset chars offset first-char)
(reduce
(fn[acc e]
(if-let [char (async/poll! c)]
(do (aset chars (+ 1 offset e) char)
(inc acc))
(reduced acc)))
1
(range (dec length)))))))
(close [] (async/close! c))))
(def char-chan (doto (async/chan)
(async/onto-chan! [\a \b \c \d \e \f \g \h \i \j])))
(def char-reader (chan->reader char-chan))
(slurp char-reader)
"abcdefghij"
(slurp char-reader)
""
I think that should work. Can try it with cheshire, and maybe need to wrap it in a BufferedReader as well, since I saw cheshire parse-seq has a BufferedReader type hint
(def char-reader-buff (java.io.BufferedReader. (chan->reader char-chan)))
(slurp char-reader-buff)
"abcdefghij"
(slurp char-reader-buff)
java.io.IOException: Stream closed
Does anyone know if it's possible to dynamically update CORS policy?
Unless you mean some specific server, I don't think browsers cache CORS-related headers.
I mean server side.
I'm wanting to initially be open to requests but then lock down those requests after a period of time once I know the origins
You're in full control of the server side. Maybe some particular sever would not have an easy way of implementing what you want, but in general it should be possible.
Write some custom ring middleware that conditionally applies the logic of https://cljdoc.org/d/ring-cors/ring-cors/0.1.13/api/ring.middleware.cors#wrap-cors
Yea I've tried that. I think Reitit might be doing something special. I even tried doing a namespace reload but it doesn't work when I package it up.