Skip to content

Commit 144dc24

Browse files
authored
*flush-on-newline* support (#866)
Hi, could you please consider patch to support the Clojure's `*flush-on-newlne*` dynamic var. It fixes #865. Although the corresponding documentation string in Clojure describes this variable as used [for flushing the stream everytime a newline is printed](https://github.com/clojure/clojure/blob/e3520c07e8b21dbf5a91fa14b7114b90dc1e89c6/src/clj/clojure/core.clj#L6540-L6544), it is only referenced by `prn` to [flush its output after the last newline has been written](https://github.com/clojure/clojure/blob/e3520c07e8b21dbf5a91fa14b7114b90dc1e89c6/src/clj/clojure/core.clj#L3729-L3737). This patch follows the same logic. Thanks Co-authored-by: ikappaki <[email protected]>
1 parent d90249c commit 144dc24

File tree

3 files changed

+94
-3
lines changed

3 files changed

+94
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
* Added a compile-time warning for attempting to call a function with an unsupported number of arguments (#671)
1111
* Added support for explicit cause exception chaining to the `throw` special form (#862)
1212
* Added `basilisp.stacktrace` namespace (#721)
13+
* Added support for `*flush-on-newline*` to flush the `prn` and `println` output stream after the last newline (#865)
1314

1415
### Changed
1516
* Cause exceptions arising from compilation issues during macroexpansion will no longer be nested for each level of macroexpansion (#852)

src/basilisp/core.lpy

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4098,6 +4098,13 @@
40984098

40994099
(def ^:dynamic *print-sep* " ")
41004100

4101+
(def ^:dynamic *flush-on-newline*
4102+
"Indicates whether the `:lpy:fn:prn` and `:lpy:fn:println` functions
4103+
should flush the output stream after the last newline is written.
4104+
4105+
Defaults to true."
4106+
true)
4107+
41014108
(defn ^:inline repr
41024109
"Return the reader representation of an object."
41034110
[x]
@@ -4153,14 +4160,20 @@
41534160
nil)))
41544161

41554162
(defn prn
4156-
"Same as :lpy:fn:`pr`, but appending a newline afterwards."
4163+
"Same as :lpy:fn:`pr`, but appending a newline afterwards.
4164+
4165+
Observes :lpy:var:`*flush-on-newline*`."
41574166
([]
41584167
(.write *out* os/linesep)
4168+
(when *flush-on-newline*
4169+
(.flush *out*))
41594170
nil)
41604171
([x]
41614172
(let [stdout *out*]
41624173
(.write stdout (repr x))
41634174
(.write stdout os/linesep)
4175+
(when *flush-on-newline*
4176+
(.flush stdout))
41644177
nil))
41654178
([x & args]
41664179
(let [stdout *out*
@@ -4170,6 +4183,8 @@
41704183
(.write stdout sep)
41714184
(.write stdout (apply str repr-args))
41724185
(.write stdout os/linesep)
4186+
(when *flush-on-newline*
4187+
(.flush *out*))
41734188
nil)))
41744189

41754190
(defn pr-str
@@ -4205,12 +4220,16 @@
42054220
"Print the arguments to the stream bound to :lpy:var:`*out*` in a format which is
42064221
readable by humans. ``println`` always prints a trailing newline. Multiple arguments
42074222
will be separated by the string value bound to :lpy:var:`*print-sep*` (default is an
4208-
ASCII space)."
4223+
ASCII space).
4224+
4225+
Observes :lpy:var:`*flush-on-newline*`."
42094226
([] (println ""))
42104227
([x]
42114228
(let [stdout *out*]
42124229
(.write stdout (basilisp.lang.runtime/lstr x))
42134230
(.write stdout os/linesep)
4231+
(when *flush-on-newline*
4232+
(.flush stdout))
42144233
nil))
42154234
([x & args]
42164235
(let [stdout *out*
@@ -4220,6 +4239,8 @@
42204239
(.write stdout sep)
42214240
(.write stdout (apply str repr-args))
42224241
(.write stdout os/linesep)
4242+
(when *flush-on-newline*
4243+
(.flush stdout))
42234244
nil)))
42244245

42254246
(defn print-str

tests/basilisp/test_core_fns.lpy

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
(ns tests.basilisp.test-core-fns
2-
(:import shutil
2+
(:import os
3+
shutil
34
time)
45
(:require
56
[basilisp.io :as bio]
@@ -2142,3 +2143,71 @@
21422143
(deftest pr-test
21432144
(testing "is dynamic"
21442145
(is (= '(1) (binding [pr (fn [& more] more)] (pr 1))))))
2146+
2147+
(defn- bio-write
2148+
"Helper fn to write the ``strings`` to the ByteIO buffer ``bio``
2149+
and return the contents of the buffer."
2150+
[bio & strings]
2151+
(.write bio (python/bytes (apply str strings) "UTF-8"))
2152+
(.getvalue bio))
2153+
2154+
(deftest flush-on-newline-test
2155+
;; :line-buffering false :newline ""
2156+
;;
2157+
;; the above options instruct the TextIOWrapper to internally
2158+
;; disable (1) flushing on newlines and (2) universal newlines
2159+
;; mode.
2160+
2161+
(testing "prn newline flushing"
2162+
(let [bio (io/BytesIO)
2163+
expected (io/BytesIO)]
2164+
(binding [*out* (io/TextIOWrapper bio ** :line-buffering false :newline "")
2165+
*flush-on-newline* true]
2166+
(prn)
2167+
(is (= (bio-write expected os/linesep) (.getvalue bio)))
2168+
(prn "ab")
2169+
(is (= (bio-write expected "\"ab\"" os/linesep) (.getvalue bio)))
2170+
(prn "ab" 1)
2171+
(is (= (bio-write expected "\"ab\"" " " 1 os/linesep) (.getvalue bio))))))
2172+
2173+
(testing "prn newline not flushing"
2174+
(let [bio (io/BytesIO)
2175+
expected (io/BytesIO)]
2176+
(binding [*out* (io/TextIOWrapper bio ** :line-buffering false :newline "")
2177+
*flush-on-newline* false]
2178+
(prn)
2179+
(is (= #b "" (.getvalue bio)))
2180+
(prn "ab")
2181+
(is (= #b "" (.getvalue bio)))
2182+
(prn "ab" 1)
2183+
(is (= #b "" (.getvalue bio)))
2184+
(.flush *out*)
2185+
(is (= (bio-write expected os/linesep "\"ab\"" os/linesep "\"ab\"" " " 1 os/linesep)
2186+
(.getvalue bio))))))
2187+
2188+
(testing "println newline flushing"
2189+
(let [bio (io/BytesIO)
2190+
expected (io/BytesIO)]
2191+
(binding [*out* (io/TextIOWrapper bio ** :line-buffering false :newline "")
2192+
*flush-on-newline* true]
2193+
(println)
2194+
(is (= (bio-write expected os/linesep) (.getvalue bio)))
2195+
(println "ab")
2196+
(is (= (bio-write expected "ab" os/linesep) (.getvalue bio)))
2197+
(println "ab" 1)
2198+
(is (= (bio-write expected "ab" " " 1 os/linesep) (.getvalue bio))))))
2199+
2200+
(testing "println newline not flushing"
2201+
(let [bio (io/BytesIO)
2202+
expected (io/BytesIO)]
2203+
(binding [*out* (io/TextIOWrapper bio ** :line-buffering false :newline "")
2204+
*flush-on-newline* false]
2205+
(println)
2206+
(is (= #b "" (.getvalue bio)))
2207+
(println "ab")
2208+
(is (= #b "" (.getvalue bio)))
2209+
(println "ab" 1)
2210+
(is (= #b "" (.getvalue bio)))
2211+
(.flush *out*)
2212+
(is (= (bio-write expected os/linesep "ab" os/linesep "ab" " " 1 os/linesep)
2213+
(.getvalue bio)))))))

0 commit comments

Comments
 (0)