Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Added a compile-time warning for attempting to call a function with an unsupported number of arguments (#671)
* Added support for explicit cause exception chaining to the `throw` special form (#862)
* Added `basilisp.stacktrace` namespace (#721)
* Added support for `*flush-on-newline*` to flush the `prn` and `println` output stream after the last newline (#865)

### Changed
* Cause exceptions arising from compilation issues during macroexpansion will no longer be nested for each level of macroexpansion (#852)
Expand Down
25 changes: 23 additions & 2 deletions src/basilisp/core.lpy
Original file line number Diff line number Diff line change
Expand Up @@ -4098,6 +4098,13 @@

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

(def ^:dynamic *flush-on-newline*
"Indicates whether the `:lpy:fn:prn` and `:lpy:fn:println` functions
should flush the output stream after the last newline is written.

Defaults to true."
true)

(defn ^:inline repr
"Return the reader representation of an object."
[x]
Expand Down Expand Up @@ -4153,14 +4160,20 @@
nil)))

(defn prn
"Same as :lpy:fn:`pr`, but appending a newline afterwards."
"Same as :lpy:fn:`pr`, but appending a newline afterwards.

Observes :lpy:var:`*flush-on-newline*`."
([]
(.write *out* os/linesep)
(when *flush-on-newline*
(.flush *out*))
nil)
([x]
(let [stdout *out*]
(.write stdout (repr x))
(.write stdout os/linesep)
(when *flush-on-newline*
(.flush stdout))
nil))
([x & args]
(let [stdout *out*
Expand All @@ -4170,6 +4183,8 @@
(.write stdout sep)
(.write stdout (apply str repr-args))
(.write stdout os/linesep)
(when *flush-on-newline*
(.flush *out*))
nil)))

(defn pr-str
Expand Down Expand Up @@ -4205,12 +4220,16 @@
"Print the arguments to the stream bound to :lpy:var:`*out*` in a format which is
readable by humans. ``println`` always prints a trailing newline. Multiple arguments
will be separated by the string value bound to :lpy:var:`*print-sep*` (default is an
ASCII space)."
ASCII space).

Observes :lpy:var:`*flush-on-newline*`."
([] (println ""))
([x]
(let [stdout *out*]
(.write stdout (basilisp.lang.runtime/lstr x))
(.write stdout os/linesep)
(when *flush-on-newline*
(.flush stdout))
nil))
([x & args]
(let [stdout *out*
Expand All @@ -4220,6 +4239,8 @@
(.write stdout sep)
(.write stdout (apply str repr-args))
(.write stdout os/linesep)
(when *flush-on-newline*
(.flush stdout))
nil)))

(defn print-str
Expand Down
71 changes: 70 additions & 1 deletion tests/basilisp/test_core_fns.lpy
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
(ns tests.basilisp.test-core-fns
(:import shutil
(:import os
shutil
time)
(:require
[basilisp.io :as bio]
Expand Down Expand Up @@ -2142,3 +2143,71 @@
(deftest pr-test
(testing "is dynamic"
(is (= '(1) (binding [pr (fn [& more] more)] (pr 1))))))

(defn- bio-write
"Helper fn to write the ``strings`` to the ByteIO buffer ``bio``
and return the contents of the buffer."
[bio & strings]
(.write bio (python/bytes (apply str strings) "UTF-8"))
(.getvalue bio))

(deftest flush-on-newline-test
;; :line-buffering false :newline ""
;;
;; the above options instruct the TextIOWrapper to internally
;; disable (1) flushing on newlines and (2) universal newlines
;; mode.

(testing "prn newline flushing"
(let [bio (io/BytesIO)
expected (io/BytesIO)]
(binding [*out* (io/TextIOWrapper bio ** :line-buffering false :newline "")
*flush-on-newline* true]
(prn)
(is (= (bio-write expected os/linesep) (.getvalue bio)))
(prn "ab")
(is (= (bio-write expected "\"ab\"" os/linesep) (.getvalue bio)))
(prn "ab" 1)
(is (= (bio-write expected "\"ab\"" " " 1 os/linesep) (.getvalue bio))))))

(testing "prn newline not flushing"
(let [bio (io/BytesIO)
expected (io/BytesIO)]
(binding [*out* (io/TextIOWrapper bio ** :line-buffering false :newline "")
*flush-on-newline* false]
(prn)
(is (= #b "" (.getvalue bio)))
(prn "ab")
(is (= #b "" (.getvalue bio)))
(prn "ab" 1)
(is (= #b "" (.getvalue bio)))
(.flush *out*)
(is (= (bio-write expected os/linesep "\"ab\"" os/linesep "\"ab\"" " " 1 os/linesep)
(.getvalue bio))))))

(testing "println newline flushing"
(let [bio (io/BytesIO)
expected (io/BytesIO)]
(binding [*out* (io/TextIOWrapper bio ** :line-buffering false :newline "")
*flush-on-newline* true]
(println)
(is (= (bio-write expected os/linesep) (.getvalue bio)))
(println "ab")
(is (= (bio-write expected "ab" os/linesep) (.getvalue bio)))
(println "ab" 1)
(is (= (bio-write expected "ab" " " 1 os/linesep) (.getvalue bio))))))

(testing "println newline not flushing"
(let [bio (io/BytesIO)
expected (io/BytesIO)]
(binding [*out* (io/TextIOWrapper bio ** :line-buffering false :newline "")
*flush-on-newline* false]
(println)
(is (= #b "" (.getvalue bio)))
(println "ab")
(is (= #b "" (.getvalue bio)))
(println "ab" 1)
(is (= #b "" (.getvalue bio)))
(.flush *out*)
(is (= (bio-write expected os/linesep "ab" os/linesep "ab" " " 1 os/linesep)
(.getvalue bio)))))))